Merge pull request #260 from kaczmarczyck/resident-naming
Resident naming
This commit is contained in:
@@ -106,8 +106,7 @@ a few things you can personalize:
|
|||||||
check [WebAuthn](https://www.w3.org/TR/webauthn/#signature-counter) for
|
check [WebAuthn](https://www.w3.org/TR/webauthn/#signature-counter) for
|
||||||
documentation.
|
documentation.
|
||||||
1. Depending on your available flash storage, choose an appropriate maximum
|
1. Depending on your available flash storage, choose an appropriate maximum
|
||||||
number of supported residential keys and number of pages in
|
number of supported resident keys and number of pages in `ctap/storage.rs`.
|
||||||
`ctap/storage.rs`.
|
|
||||||
1. Change the default level for the credProtect extension in `ctap/mod.rs`.
|
1. Change the default level for the credProtect extension in `ctap/mod.rs`.
|
||||||
When changing the default, resident credentials become undiscoverable without
|
When changing the default, resident credentials become undiscoverable without
|
||||||
user verification. This helps privacy, but can make usage less comfortable
|
user verification. This helps privacy, but can make usage less comfortable
|
||||||
|
|||||||
@@ -1195,7 +1195,7 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_residential_process_make_credential() {
|
fn test_resident_process_make_credential() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
let user_immediately_present = |_| Ok(());
|
let user_immediately_present = |_| Ok(());
|
||||||
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
|
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
|
||||||
@@ -1214,7 +1214,7 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_non_residential_process_make_credential() {
|
fn test_non_resident_process_make_credential() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
let user_immediately_present = |_| Ok(());
|
let user_immediately_present = |_| Ok(());
|
||||||
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
|
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
|
||||||
@@ -1526,7 +1526,7 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_residential_process_get_assertion() {
|
fn test_resident_process_get_assertion() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
let user_immediately_present = |_| Ok(());
|
let user_immediately_present = |_| Ok(());
|
||||||
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
|
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
|
||||||
@@ -1629,7 +1629,7 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_residential_process_get_assertion_hmac_secret() {
|
fn test_resident_process_get_assertion_hmac_secret() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
let sk = crypto::ecdh::SecKey::gensk(&mut rng);
|
let sk = crypto::ecdh::SecKey::gensk(&mut rng);
|
||||||
let user_immediately_present = |_| Ok(());
|
let user_immediately_present = |_| Ok(());
|
||||||
@@ -1681,7 +1681,7 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_residential_process_get_assertion_with_cred_protect() {
|
fn test_resident_process_get_assertion_with_cred_protect() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
let private_key = crypto::ecdsa::SecKey::gensk(&mut rng);
|
let private_key = crypto::ecdsa::SecKey::gensk(&mut rng);
|
||||||
let credential_id = rng.gen_uniform_u8x32().to_vec();
|
let credential_id = rng.gen_uniform_u8x32().to_vec();
|
||||||
|
|||||||
@@ -38,11 +38,11 @@ use crypto::rng256::Rng256;
|
|||||||
// number of pages. This may improve in the future. Currently, using 20 pages gives between 20ms and
|
// number of pages. This may improve in the future. Currently, using 20 pages gives between 20ms and
|
||||||
// 240ms per operation. The rule of thumb is between 1ms and 12ms per additional page.
|
// 240ms per operation. The rule of thumb is between 1ms and 12ms per additional page.
|
||||||
//
|
//
|
||||||
// Limiting the number of residential keys permits to ensure a minimum number of counter increments.
|
// Limiting the number of resident keys permits to ensure a minimum number of counter increments.
|
||||||
// Let:
|
// Let:
|
||||||
// - P the number of pages (NUM_PAGES)
|
// - P the number of pages (NUM_PAGES)
|
||||||
// - K the maximum number of residential keys (MAX_SUPPORTED_RESIDENTIAL_KEYS)
|
// - K the maximum number of resident keys (MAX_SUPPORTED_RESIDENT_KEYS)
|
||||||
// - S the maximum size of a residential key (about 500)
|
// - S the maximum size of a resident key (about 500)
|
||||||
// - C the number of erase cycles (10000)
|
// - C the number of erase cycles (10000)
|
||||||
// - I the minimum number of counter increments
|
// - I the minimum number of counter increments
|
||||||
//
|
//
|
||||||
@@ -50,7 +50,7 @@ use crypto::rng256::Rng256;
|
|||||||
//
|
//
|
||||||
// With P=20 and K=150, we have I=2M which is enough for 500 increments per day for 10 years.
|
// With P=20 and K=150, we have I=2M which is enough for 500 increments per day for 10 years.
|
||||||
const NUM_PAGES: usize = 20;
|
const NUM_PAGES: usize = 20;
|
||||||
const MAX_SUPPORTED_RESIDENTIAL_KEYS: usize = 150;
|
const MAX_SUPPORTED_RESIDENT_KEYS: usize = 150;
|
||||||
|
|
||||||
const MAX_PIN_RETRIES: u8 = 8;
|
const MAX_PIN_RETRIES: u8 = 8;
|
||||||
const DEFAULT_MIN_PIN_LENGTH: u8 = 4;
|
const DEFAULT_MIN_PIN_LENGTH: u8 = 4;
|
||||||
@@ -132,7 +132,7 @@ impl PersistentStore {
|
|||||||
/// Returns `CTAP2_ERR_VENDOR_INTERNAL_ERROR` if the key does not hold a valid credential.
|
/// Returns `CTAP2_ERR_VENDOR_INTERNAL_ERROR` if the key does not hold a valid credential.
|
||||||
pub fn get_credential(&self, key: usize) -> Result<PublicKeyCredentialSource, Ctap2StatusCode> {
|
pub fn get_credential(&self, key: usize) -> Result<PublicKeyCredentialSource, Ctap2StatusCode> {
|
||||||
let min_key = key::CREDENTIALS.start;
|
let min_key = key::CREDENTIALS.start;
|
||||||
if key < min_key || key >= min_key + MAX_SUPPORTED_RESIDENTIAL_KEYS {
|
if key < min_key || key >= min_key + MAX_SUPPORTED_RESIDENT_KEYS {
|
||||||
return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR);
|
return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR);
|
||||||
}
|
}
|
||||||
let credential_entry = self
|
let credential_entry = self
|
||||||
@@ -200,13 +200,11 @@ impl PersistentStore {
|
|||||||
let mut old_key = None;
|
let mut old_key = None;
|
||||||
let min_key = key::CREDENTIALS.start;
|
let min_key = key::CREDENTIALS.start;
|
||||||
// Holds whether a key is used (indices are shifted by min_key).
|
// Holds whether a key is used (indices are shifted by min_key).
|
||||||
let mut keys = vec![false; MAX_SUPPORTED_RESIDENTIAL_KEYS];
|
let mut keys = vec![false; MAX_SUPPORTED_RESIDENT_KEYS];
|
||||||
let mut iter_result = Ok(());
|
let mut iter_result = Ok(());
|
||||||
let iter = self.iter_credentials(&mut iter_result)?;
|
let iter = self.iter_credentials(&mut iter_result)?;
|
||||||
for (key, credential) in iter {
|
for (key, credential) in iter {
|
||||||
if key < min_key
|
if key < min_key || key - min_key >= MAX_SUPPORTED_RESIDENT_KEYS || keys[key - min_key]
|
||||||
|| key - min_key >= MAX_SUPPORTED_RESIDENTIAL_KEYS
|
|
||||||
|| keys[key - min_key]
|
|
||||||
{
|
{
|
||||||
return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR);
|
return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR);
|
||||||
}
|
}
|
||||||
@@ -221,16 +219,14 @@ impl PersistentStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
iter_result?;
|
iter_result?;
|
||||||
if old_key.is_none()
|
if old_key.is_none() && keys.iter().filter(|&&x| x).count() >= MAX_SUPPORTED_RESIDENT_KEYS {
|
||||||
&& keys.iter().filter(|&&x| x).count() >= MAX_SUPPORTED_RESIDENTIAL_KEYS
|
|
||||||
{
|
|
||||||
return Err(Ctap2StatusCode::CTAP2_ERR_KEY_STORE_FULL);
|
return Err(Ctap2StatusCode::CTAP2_ERR_KEY_STORE_FULL);
|
||||||
}
|
}
|
||||||
let key = match old_key {
|
let key = match old_key {
|
||||||
// This is a new credential being added, we need to allocate a free key. We choose the
|
// This is a new credential being added, we need to allocate a free key. We choose the
|
||||||
// first available key.
|
// first available key.
|
||||||
None => key::CREDENTIALS
|
None => key::CREDENTIALS
|
||||||
.take(MAX_SUPPORTED_RESIDENTIAL_KEYS)
|
.take(MAX_SUPPORTED_RESIDENT_KEYS)
|
||||||
.find(|key| !keys[key - min_key])
|
.find(|key| !keys[key - min_key])
|
||||||
.ok_or(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR)?,
|
.ok_or(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR)?,
|
||||||
// This is an existing credential being updated, we reuse its key.
|
// This is an existing credential being updated, we reuse its key.
|
||||||
@@ -280,7 +276,7 @@ impl PersistentStore {
|
|||||||
|
|
||||||
/// Returns the estimated number of credentials that can still be stored.
|
/// Returns the estimated number of credentials that can still be stored.
|
||||||
pub fn remaining_credentials(&self) -> Result<usize, Ctap2StatusCode> {
|
pub fn remaining_credentials(&self) -> Result<usize, Ctap2StatusCode> {
|
||||||
MAX_SUPPORTED_RESIDENTIAL_KEYS
|
MAX_SUPPORTED_RESIDENT_KEYS
|
||||||
.checked_sub(self.count_credentials()?)
|
.checked_sub(self.count_credentials()?)
|
||||||
.ok_or(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR)
|
.ok_or(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR)
|
||||||
}
|
}
|
||||||
@@ -714,7 +710,7 @@ mod test {
|
|||||||
assert_eq!(persistent_store.count_credentials().unwrap(), 0);
|
assert_eq!(persistent_store.count_credentials().unwrap(), 0);
|
||||||
|
|
||||||
let mut credential_ids = vec![];
|
let mut credential_ids = vec![];
|
||||||
for i in 0..MAX_SUPPORTED_RESIDENTIAL_KEYS {
|
for i in 0..MAX_SUPPORTED_RESIDENT_KEYS {
|
||||||
let user_handle = i.to_ne_bytes().to_vec();
|
let user_handle = i.to_ne_bytes().to_vec();
|
||||||
let credential_source = create_credential_source(&mut rng, "example.com", user_handle);
|
let credential_source = create_credential_source(&mut rng, "example.com", user_handle);
|
||||||
credential_ids.push(credential_source.credential_id.clone());
|
credential_ids.push(credential_source.credential_id.clone());
|
||||||
@@ -788,7 +784,7 @@ mod test {
|
|||||||
let mut persistent_store = PersistentStore::new(&mut rng);
|
let mut persistent_store = PersistentStore::new(&mut rng);
|
||||||
assert_eq!(persistent_store.count_credentials().unwrap(), 0);
|
assert_eq!(persistent_store.count_credentials().unwrap(), 0);
|
||||||
|
|
||||||
for i in 0..MAX_SUPPORTED_RESIDENTIAL_KEYS {
|
for i in 0..MAX_SUPPORTED_RESIDENT_KEYS {
|
||||||
let user_handle = i.to_ne_bytes().to_vec();
|
let user_handle = i.to_ne_bytes().to_vec();
|
||||||
let credential_source = create_credential_source(&mut rng, "example.com", user_handle);
|
let credential_source = create_credential_source(&mut rng, "example.com", user_handle);
|
||||||
assert!(persistent_store.store_credential(credential_source).is_ok());
|
assert!(persistent_store.store_credential(credential_source).is_ok());
|
||||||
@@ -797,7 +793,7 @@ mod test {
|
|||||||
let credential_source = create_credential_source(
|
let credential_source = create_credential_source(
|
||||||
&mut rng,
|
&mut rng,
|
||||||
"example.com",
|
"example.com",
|
||||||
vec![MAX_SUPPORTED_RESIDENTIAL_KEYS as u8],
|
vec![MAX_SUPPORTED_RESIDENT_KEYS as u8],
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
persistent_store.store_credential(credential_source),
|
persistent_store.store_credential(credential_source),
|
||||||
@@ -805,7 +801,7 @@ mod test {
|
|||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
persistent_store.count_credentials().unwrap(),
|
persistent_store.count_credentials().unwrap(),
|
||||||
MAX_SUPPORTED_RESIDENTIAL_KEYS
|
MAX_SUPPORTED_RESIDENT_KEYS
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -837,7 +833,7 @@ mod test {
|
|||||||
.is_some());
|
.is_some());
|
||||||
|
|
||||||
let mut persistent_store = PersistentStore::new(&mut rng);
|
let mut persistent_store = PersistentStore::new(&mut rng);
|
||||||
for i in 0..MAX_SUPPORTED_RESIDENTIAL_KEYS {
|
for i in 0..MAX_SUPPORTED_RESIDENT_KEYS {
|
||||||
let user_handle = i.to_ne_bytes().to_vec();
|
let user_handle = i.to_ne_bytes().to_vec();
|
||||||
let credential_source = create_credential_source(&mut rng, "example.com", user_handle);
|
let credential_source = create_credential_source(&mut rng, "example.com", user_handle);
|
||||||
assert!(persistent_store.store_credential(credential_source).is_ok());
|
assert!(persistent_store.store_credential(credential_source).is_ok());
|
||||||
@@ -846,7 +842,7 @@ mod test {
|
|||||||
let credential_source = create_credential_source(
|
let credential_source = create_credential_source(
|
||||||
&mut rng,
|
&mut rng,
|
||||||
"example.com",
|
"example.com",
|
||||||
vec![MAX_SUPPORTED_RESIDENTIAL_KEYS as u8],
|
vec![MAX_SUPPORTED_RESIDENT_KEYS as u8],
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
persistent_store.store_credential(credential_source),
|
persistent_store.store_credential(credential_source),
|
||||||
@@ -854,7 +850,7 @@ mod test {
|
|||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
persistent_store.count_credentials().unwrap(),
|
persistent_store.count_credentials().unwrap(),
|
||||||
MAX_SUPPORTED_RESIDENTIAL_KEYS
|
MAX_SUPPORTED_RESIDENT_KEYS
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -84,8 +84,8 @@ make_partition! {
|
|||||||
|
|
||||||
/// The credentials.
|
/// The credentials.
|
||||||
///
|
///
|
||||||
/// Depending on `MAX_SUPPORTED_RESIDENTIAL_KEYS`, only a prefix of those keys is used. Each
|
/// Depending on `MAX_SUPPORTED_RESIDENT_KEYS`, only a prefix of those keys is used. Each
|
||||||
/// board may configure `MAX_SUPPORTED_RESIDENTIAL_KEYS` depending on the storage size.
|
/// board may configure `MAX_SUPPORTED_RESIDENT_KEYS` depending on the storage size.
|
||||||
CREDENTIALS = 1700..2000;
|
CREDENTIALS = 1700..2000;
|
||||||
|
|
||||||
/// The secret of the CredRandom feature.
|
/// The secret of the CredRandom feature.
|
||||||
@@ -127,8 +127,8 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn enough_credentials() {
|
fn enough_credentials() {
|
||||||
use super::super::MAX_SUPPORTED_RESIDENTIAL_KEYS;
|
use super::super::MAX_SUPPORTED_RESIDENT_KEYS;
|
||||||
assert!(MAX_SUPPORTED_RESIDENTIAL_KEYS <= CREDENTIALS.end - CREDENTIALS.start);
|
assert!(MAX_SUPPORTED_RESIDENT_KEYS <= CREDENTIALS.end - CREDENTIALS.start);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
Reference in New Issue
Block a user