Merge branch 'master' into v2_lib

This commit is contained in:
Julien Cretin
2020-11-28 19:20:59 +01:00
24 changed files with 2017 additions and 399 deletions

View File

@@ -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();