Merge branch 'master' into v2_lib
This commit is contained in:
@@ -17,9 +17,9 @@ mod key;
|
||||
#[cfg(feature = "with_ctap2_1")]
|
||||
use crate::ctap::data_formats::{extract_array, extract_text_string};
|
||||
use crate::ctap::data_formats::{CredentialProtectionPolicy, PublicKeyCredentialSource};
|
||||
use crate::ctap::key_material;
|
||||
use crate::ctap::pin_protocol_v1::PIN_AUTH_LENGTH;
|
||||
use crate::ctap::status_code::Ctap2StatusCode;
|
||||
use crate::ctap::{key_material, USE_BATCH_ATTESTATION};
|
||||
#[cfg(feature = "with_ctap2_1")]
|
||||
use alloc::string::String;
|
||||
#[cfg(any(test, feature = "ram_storage", feature = "with_ctap2_1"))]
|
||||
@@ -61,8 +61,6 @@ const NUM_PAGES: usize = 20;
|
||||
const MAX_SUPPORTED_RESIDENTIAL_KEYS: usize = 150;
|
||||
|
||||
const MAX_PIN_RETRIES: u8 = 8;
|
||||
const ATTESTATION_PRIVATE_KEY_LENGTH: usize = 32;
|
||||
const AAGUID_LENGTH: usize = 16;
|
||||
#[cfg(feature = "with_ctap2_1")]
|
||||
const DEFAULT_MIN_PIN_LENGTH: u8 = 4;
|
||||
// TODO(kaczmarczyck) use this for the minPinLength extension
|
||||
@@ -140,29 +138,36 @@ impl PersistentStore {
|
||||
master_keys.extend_from_slice(&master_hmac_key);
|
||||
self.store.insert(key::MASTER_KEYS, &master_keys)?;
|
||||
}
|
||||
// The following 3 entries are meant to be written by vendor-specific commands.
|
||||
if USE_BATCH_ATTESTATION {
|
||||
if self
|
||||
.store
|
||||
.find_handle(key::ATTESTATION_PRIVATE_KEY)?
|
||||
.is_none()
|
||||
{
|
||||
self.set_attestation_private_key(key_material::ATTESTATION_PRIVATE_KEY)?;
|
||||
}
|
||||
if self
|
||||
.store
|
||||
.find_handle(key::ATTESTATION_CERTIFICATE)?
|
||||
.is_none()
|
||||
{
|
||||
self.set_attestation_certificate(key_material::ATTESTATION_CERTIFICATE)?;
|
||||
}
|
||||
}
|
||||
// TODO(jmichel): remove this when vendor command is in place
|
||||
#[cfg(not(test))]
|
||||
self.load_attestation_data_from_firmware()?;
|
||||
if self.store.find_handle(key::AAGUID)?.is_none() {
|
||||
self.set_aaguid(key_material::AAGUID)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// TODO(jmichel): remove this function when vendor command is in place.
|
||||
#[cfg(not(test))]
|
||||
fn load_attestation_data_from_firmware(&mut self) -> Result<(), Ctap2StatusCode> {
|
||||
// The following 2 entries are meant to be written by vendor-specific commands.
|
||||
if self
|
||||
.store
|
||||
.find_handle(key::ATTESTATION_PRIVATE_KEY)?
|
||||
.is_none()
|
||||
{
|
||||
self.set_attestation_private_key(key_material::ATTESTATION_PRIVATE_KEY)?;
|
||||
}
|
||||
if self
|
||||
.store
|
||||
.find_handle(key::ATTESTATION_CERTIFICATE)?
|
||||
.is_none()
|
||||
{
|
||||
self.set_attestation_certificate(key_material::ATTESTATION_CERTIFICATE)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the first matching credential.
|
||||
///
|
||||
/// Returns `None` if no credentials are matched or if `check_cred_protect` is set and the first
|
||||
@@ -279,6 +284,15 @@ impl PersistentStore {
|
||||
IterCredentials::new(&self.store, result)
|
||||
}
|
||||
|
||||
/// Returns the next creation order.
|
||||
pub fn new_creation_order(&self) -> Result<u64, Ctap2StatusCode> {
|
||||
let mut iter_result = Ok(());
|
||||
let iter = self.iter_credentials(&mut iter_result)?;
|
||||
let max = iter.map(|(_, credential)| credential.creation_order).max();
|
||||
iter_result?;
|
||||
Ok(max.unwrap_or(0).wrapping_add(1))
|
||||
}
|
||||
|
||||
/// Returns the global signature counter.
|
||||
pub fn global_signature_counter(&self) -> Result<u32, Ctap2StatusCode> {
|
||||
Ok(match self.store.find(key::GLOBAL_SIGNATURE_COUNTER)? {
|
||||
@@ -411,13 +425,17 @@ impl PersistentStore {
|
||||
/// Returns the attestation private key if defined.
|
||||
pub fn attestation_private_key(
|
||||
&self,
|
||||
) -> Result<Option<[u8; ATTESTATION_PRIVATE_KEY_LENGTH]>, Ctap2StatusCode> {
|
||||
) -> Result<Option<[u8; key_material::ATTESTATION_PRIVATE_KEY_LENGTH]>, Ctap2StatusCode> {
|
||||
match self.store.find(key::ATTESTATION_PRIVATE_KEY)? {
|
||||
None => Ok(None),
|
||||
Some(key) if key.len() != ATTESTATION_PRIVATE_KEY_LENGTH => {
|
||||
Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INVALID_PERSISTENT_STORAGE)
|
||||
Some(key) if key.len() == key_material::ATTESTATION_PRIVATE_KEY_LENGTH => {
|
||||
Ok(Some(*array_ref![
|
||||
key,
|
||||
0,
|
||||
key_material::ATTESTATION_PRIVATE_KEY_LENGTH
|
||||
]))
|
||||
}
|
||||
Some(key) => Ok(Some(*array_ref![key, 0, ATTESTATION_PRIVATE_KEY_LENGTH])),
|
||||
Some(_) => Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INVALID_PERSISTENT_STORAGE),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -426,11 +444,14 @@ impl PersistentStore {
|
||||
/// If it is already defined, it is overwritten.
|
||||
pub fn set_attestation_private_key(
|
||||
&mut self,
|
||||
attestation_private_key: &[u8; ATTESTATION_PRIVATE_KEY_LENGTH],
|
||||
attestation_private_key: &[u8; key_material::ATTESTATION_PRIVATE_KEY_LENGTH],
|
||||
) -> Result<(), Ctap2StatusCode> {
|
||||
Ok(self
|
||||
.store
|
||||
.insert(key::ATTESTATION_PRIVATE_KEY, attestation_private_key)?)
|
||||
match self.store.find(key::ATTESTATION_PRIVATE_KEY)? {
|
||||
None => Ok(self
|
||||
.store
|
||||
.insert(key::ATTESTATION_PRIVATE_KEY, attestation_private_key)?),
|
||||
Some(_) => Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the attestation certificate if defined.
|
||||
@@ -445,27 +466,33 @@ impl PersistentStore {
|
||||
&mut self,
|
||||
attestation_certificate: &[u8],
|
||||
) -> Result<(), Ctap2StatusCode> {
|
||||
Ok(self
|
||||
.store
|
||||
.insert(key::ATTESTATION_CERTIFICATE, attestation_certificate)?)
|
||||
match self.store.find(key::ATTESTATION_CERTIFICATE)? {
|
||||
None => Ok(self
|
||||
.store
|
||||
.insert(key::ATTESTATION_CERTIFICATE, attestation_certificate)?),
|
||||
Some(_) => Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the AAGUID.
|
||||
pub fn aaguid(&self) -> Result<[u8; AAGUID_LENGTH], Ctap2StatusCode> {
|
||||
pub fn aaguid(&self) -> Result<[u8; key_material::AAGUID_LENGTH], Ctap2StatusCode> {
|
||||
let aaguid = self
|
||||
.store
|
||||
.find(key::AAGUID)?
|
||||
.ok_or(Ctap2StatusCode::CTAP2_ERR_VENDOR_INVALID_PERSISTENT_STORAGE)?;
|
||||
if aaguid.len() != AAGUID_LENGTH {
|
||||
if aaguid.len() != key_material::AAGUID_LENGTH {
|
||||
return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INVALID_PERSISTENT_STORAGE);
|
||||
}
|
||||
Ok(*array_ref![aaguid, 0, AAGUID_LENGTH])
|
||||
Ok(*array_ref![aaguid, 0, key_material::AAGUID_LENGTH])
|
||||
}
|
||||
|
||||
/// Sets the AAGUID.
|
||||
///
|
||||
/// If it is already defined, it is overwritten.
|
||||
pub fn set_aaguid(&mut self, aaguid: &[u8; AAGUID_LENGTH]) -> Result<(), Ctap2StatusCode> {
|
||||
pub fn set_aaguid(
|
||||
&mut self,
|
||||
aaguid: &[u8; key_material::AAGUID_LENGTH],
|
||||
) -> Result<(), Ctap2StatusCode> {
|
||||
Ok(self.store.insert(key::AAGUID, aaguid)?)
|
||||
}
|
||||
|
||||
@@ -622,6 +649,7 @@ mod test {
|
||||
other_ui: None,
|
||||
cred_random: None,
|
||||
cred_protect_policy: None,
|
||||
creation_order: 0,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -635,6 +663,21 @@ mod test {
|
||||
assert!(persistent_store.count_credentials().unwrap() > 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_credential_order() {
|
||||
let mut rng = ThreadRng256 {};
|
||||
let mut persistent_store = PersistentStore::new(&mut rng);
|
||||
let credential_source = create_credential_source(&mut rng, "example.com", vec![]);
|
||||
let current_latest_creation = credential_source.creation_order;
|
||||
assert!(persistent_store.store_credential(credential_source).is_ok());
|
||||
let mut credential_source = create_credential_source(&mut rng, "example.com", vec![]);
|
||||
credential_source.creation_order = persistent_store.new_creation_order().unwrap();
|
||||
assert!(credential_source.creation_order > current_latest_creation);
|
||||
let current_latest_creation = credential_source.creation_order;
|
||||
assert!(persistent_store.store_credential(credential_source).is_ok());
|
||||
assert!(persistent_store.new_creation_order().unwrap() > current_latest_creation);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(clippy::assertions_on_constants)]
|
||||
fn test_fill_store() {
|
||||
@@ -763,6 +806,7 @@ mod test {
|
||||
cred_protect_policy: Some(
|
||||
CredentialProtectionPolicy::UserVerificationOptionalWithCredentialIdList,
|
||||
),
|
||||
creation_order: 0,
|
||||
};
|
||||
assert!(persistent_store.store_credential(credential).is_ok());
|
||||
|
||||
@@ -804,6 +848,7 @@ mod test {
|
||||
other_ui: None,
|
||||
cred_random: None,
|
||||
cred_protect_policy: None,
|
||||
creation_order: 0,
|
||||
};
|
||||
assert_eq!(found_credential, Some(expected_credential));
|
||||
}
|
||||
@@ -823,6 +868,7 @@ mod test {
|
||||
other_ui: None,
|
||||
cred_random: None,
|
||||
cred_protect_policy: Some(CredentialProtectionPolicy::UserVerificationRequired),
|
||||
creation_order: 0,
|
||||
};
|
||||
assert!(persistent_store.store_credential(credential).is_ok());
|
||||
|
||||
@@ -1000,6 +1046,7 @@ mod test {
|
||||
other_ui: None,
|
||||
cred_random: None,
|
||||
cred_protect_policy: None,
|
||||
creation_order: 0,
|
||||
};
|
||||
let serialized = serialize_credential(credential.clone()).unwrap();
|
||||
let reconstructed = deserialize_credential(&serialized).unwrap();
|
||||
|
||||
Reference in New Issue
Block a user