diff --git a/src/constants.rs b/src/constants.rs index 6271354..43e88da 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -3,7 +3,7 @@ use trussed::types::{CertId, KeyId}; pub const FIDO2_UP_TIMEOUT: u32 = 30_000; -pub const U2F_UP_TIMEOUT: u32 = 250; +pub const U2F_UP_TIMEOUT: u32 = 250; pub const ATTESTATION_CERT_ID: CertId = CertId::from_special(0); pub const ATTESTATION_KEY_ID: KeyId = KeyId::from_special(0); diff --git a/src/credential.rs b/src/credential.rs index 4ea6413..c0d677a 100644 --- a/src/credential.rs +++ b/src/credential.rs @@ -2,26 +2,18 @@ use core::cmp::Ordering; -use trussed::{ - client, syscall, try_syscall, - types::KeyId, -}; +use trussed::{client, syscall, try_syscall, types::KeyId}; pub(crate) use ctap_types::{ - Bytes, String, // authenticator::{ctap1, ctap2, Error, Request, Response}, ctap2::credential_management::CredentialProtectionPolicy, sizes::*, webauthn::PublicKeyCredentialDescriptor, + Bytes, + String, }; -use crate::{ - Authenticator, - Error, - Result, - UserPresence, -}; - +use crate::{Authenticator, Error, Result, UserPresence}; /// As signaled in `get_info`. /// @@ -49,7 +41,9 @@ impl TryFrom for CredentialId { type Error = Error; fn try_from(esc: EncryptedSerializedCredential) -> Result { - Ok(CredentialId(trussed::cbor_serialize_bytes(&esc.0).map_err(|_| Error::Other)?)) + Ok(CredentialId( + trussed::cbor_serialize_bytes(&esc.0).map_err(|_| Error::Other)?, + )) } } @@ -60,7 +54,7 @@ impl TryFrom for EncryptedSerializedCredential { fn try_from(cid: CredentialId) -> Result { let encrypted_serialized_credential = EncryptedSerializedCredential( - ctap_types::serde::cbor_deserialize(&cid.0).map_err(|_| Error::InvalidCredential)? + ctap_types::serde::cbor_deserialize(&cid.0).map_err(|_| Error::InvalidCredential)?, ); Ok(encrypted_serialized_credential) } @@ -78,7 +72,9 @@ pub enum Key { } /// The main content of a `Credential`. -#[derive(Clone, Debug, PartialEq, serde_indexed::DeserializeIndexed, serde_indexed::SerializeIndexed)] +#[derive( + Clone, Debug, PartialEq, serde_indexed::DeserializeIndexed, serde_indexed::SerializeIndexed, +)] pub struct CredentialData { // id, name, url pub rp: ctap_types::webauthn::PublicKeyCredentialRpEntity, @@ -103,7 +99,6 @@ pub struct CredentialData { pub hmac_secret: Option, #[serde(skip_serializing_if = "Option::is_none")] pub cred_protect: Option, - // TODO: add `sig_counter: Option`, // and grant RKs a per-credential sig-counter. } @@ -139,9 +134,7 @@ impl core::ops::Deref for Credential { /// Likely comparison based on timestamp would be good enough? impl PartialEq for Credential { fn eq(&self, other: &Self) -> bool { - (self.creation_time == other.creation_time) - && - (self.key == other.key) + (self.creation_time == other.creation_time) && (self.key == other.key) } } @@ -183,7 +176,7 @@ impl From for PublicKeyCredentialDescriptor { let mut key_type = String::new(); key_type.push_str("public-key").unwrap(); key_type - } + }, } } } @@ -201,9 +194,7 @@ impl Credential { hmac_secret: Option, cred_protect: Option, nonce: [u8; 12], - ) - -> Self - { + ) -> Self { info!("credential for algorithm {}", algorithm); let data = CredentialData { rp: rp.clone(), @@ -243,9 +234,7 @@ impl Credential { trussed: &mut T, key_encryption_key: KeyId, rp_id_hash: Option<&Bytes<32>>, - ) - -> Result - { + ) -> Result { let serialized_credential = self.strip().serialize()?; let message = &serialized_credential; // info!("serialized cred = {:?}", message).ok(); @@ -255,14 +244,14 @@ impl Credential { } else { syscall!(trussed.hash_sha256(self.rp.id.as_ref())) .hash - .to_bytes().map_err(|_| Error::Other)? + .to_bytes() + .map_err(|_| Error::Other)? }; let associated_data = &rp_id_hash[..]; let nonce: [u8; 12] = self.nonce.as_slice().try_into().unwrap(); - let encrypted_serialized_credential = EncryptedSerializedCredential( - syscall!(trussed.encrypt_chacha8poly1305( - key_encryption_key, message, associated_data, Some(&nonce)))); + let encrypted_serialized_credential = EncryptedSerializedCredential(syscall!(trussed + .encrypt_chacha8poly1305(key_encryption_key, message, associated_data, Some(&nonce)))); let credential_id: CredentialId = encrypted_serialized_credential.try_into().unwrap(); Ok(credential_id) @@ -283,12 +272,10 @@ impl Credential { } pub fn try_from( - authnr: &mut Authenticator, + authnr: &mut Authenticator, rp_id_hash: &Bytes<32>, descriptor: &PublicKeyCredentialDescriptor, - ) - -> Result - { + ) -> Result { Self::try_from_bytes(authnr, rp_id_hash, &descriptor.id) } @@ -296,18 +283,17 @@ impl Credential { authnr: &mut Authenticator, rp_id_hash: &Bytes<32>, id: &[u8], - ) - -> Result - { - + ) -> Result { let mut cred: Bytes = Bytes::new(); - cred.extend_from_slice(id).map_err(|_| Error::InvalidCredential)?; + cred.extend_from_slice(id) + .map_err(|_| Error::InvalidCredential)?; - let encrypted_serialized = EncryptedSerializedCredential::try_from( - CredentialId(cred) - )?; + let encrypted_serialized = EncryptedSerializedCredential::try_from(CredentialId(cred))?; - let kek = authnr.state.persistent.key_encryption_key(&mut authnr.trussed)?; + let kek = authnr + .state + .persistent + .key_encryption_key(&mut authnr.trussed)?; let serialized = try_syscall!(authnr.trussed.decrypt_chacha8poly1305( // TODO: use RpId as associated data here? @@ -317,11 +303,12 @@ impl Credential { &encrypted_serialized.0.nonce, &encrypted_serialized.0.tag, )) - .map_err(|_| Error::InvalidCredential)?.plaintext - .ok_or(Error::InvalidCredential)?; + .map_err(|_| Error::InvalidCredential)? + .plaintext + .ok_or(Error::InvalidCredential)?; - let credential = Credential::deserialize(&serialized) - .map_err(|_| Error::InvalidCredential)?; + let credential = + Credential::deserialize(&serialized).map_err(|_| Error::InvalidCredential)?; Ok(credential) } @@ -362,7 +349,7 @@ mod test { url: None, }, user: PublicKeyCredentialUserEntity { - id: Bytes::from_slice(&[1,2,3]).unwrap(), + id: Bytes::from_slice(&[1, 2, 3]).unwrap(), icon: None, name: None, display_name: None, @@ -370,7 +357,7 @@ mod test { creation_time: 123, use_counter: false, algorithm: -7, - key: Key::WrappedKey(Bytes::from_slice(&[1,2,3]).unwrap()), + key: Key::WrappedKey(Bytes::from_slice(&[1, 2, 3]).unwrap()), hmac_secret: Some(false), cred_protect: None, }; @@ -378,7 +365,11 @@ mod test { } fn random_bytes() -> Bytes { - use rand::{RngCore, distributions::{Distribution, Uniform}, rngs::OsRng}; + use rand::{ + distributions::{Distribution, Uniform}, + rngs::OsRng, + RngCore, + }; let mut bytes = Bytes::default(); let between = Uniform::from(0..(N + 1)); @@ -392,7 +383,7 @@ mod test { #[allow(dead_code)] fn maybe_random_bytes() -> Option> { - use rand::{RngCore, rngs::OsRng}; + use rand::{rngs::OsRng, RngCore}; if OsRng.next_u32() & 1 != 0 { Some(random_bytes()) } else { @@ -401,18 +392,26 @@ mod test { } fn random_string() -> String { + use rand::{ + distributions::{Alphanumeric, Distribution, Uniform}, + rngs::OsRng, + Rng, + }; use std::str::FromStr; - use rand::{Rng, distributions::{Alphanumeric, Distribution, Uniform}, rngs::OsRng}; let between = Uniform::from(0..(N + 1)); let n = between.sample(&mut OsRng); - let std_string: std::string::String = OsRng.sample_iter(&Alphanumeric).take(n).map(char::from).collect(); + let std_string: std::string::String = OsRng + .sample_iter(&Alphanumeric) + .take(n) + .map(char::from) + .collect(); String::from_str(&std_string).unwrap() } fn maybe_random_string() -> Option> { - use rand::{RngCore, rngs::OsRng}; + use rand::{rngs::OsRng, RngCore}; if OsRng.next_u32() & 1 != 0 { Some(random_string()) } else { @@ -430,7 +429,7 @@ mod test { url: maybe_random_string(), }, user: PublicKeyCredentialUserEntity { - id: random_bytes(),//Bytes::from_slice(&[1,2,3]).unwrap(), + id: random_bytes(), //Bytes::from_slice(&[1,2,3]).unwrap(), icon: maybe_random_string(), name: maybe_random_string(), display_name: maybe_random_string(), @@ -518,5 +517,5 @@ mod test { // TestResult::from_bool(credential_data == deserialized) // } - // } + // } } diff --git a/src/ctap1.rs b/src/ctap1.rs index bab737d..5be6414 100644 --- a/src/ctap1.rs +++ b/src/ctap1.rs @@ -1,39 +1,22 @@ //! The `ctap_types::ctap1::Authenticator` implementation. use ctap_types::{ - ctap1::{ - Authenticator, - ControlByte, - register, authenticate, - Result, - Error, - }, + ctap1::{authenticate, register, Authenticator, ControlByte, Error, Result}, heapless_bytes::Bytes, }; use trussed::{ syscall, - types::{ - KeySerialization, - Mechanism, - SignatureSerialization, - Location, - }, + types::{KeySerialization, Location, Mechanism, SignatureSerialization}, }; use crate::{ - credential::{ - self, - Credential, - Key, - }, constants, - SigningAlgorithm, - TrussedRequirements, - UserPresence, + credential::{self, Credential, Key}, + SigningAlgorithm, TrussedRequirements, UserPresence, }; -type Commitment = Bytes::<324>; +type Commitment = Bytes<324>; /// Implement `ctap1::Authenticator` for our Authenticator. /// @@ -41,8 +24,7 @@ type Commitment = Bytes::<324>; /// The "proposed standard" of U2F V1.2 applies to CTAP1. /// - [Message formats](https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-raw-message-formats-v1.2-ps-20170411.html) /// - [App ID](https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-appid-and-facets-v1.2-ps-20170411.html) -impl Authenticator for crate::Authenticator -{ +impl Authenticator for crate::Authenticator { /// Register a new credential, this always uses P-256 keys. /// /// Note that attestation is mandatory in CTAP1/U2F, so if the state @@ -52,35 +34,51 @@ impl Authenticator for crate::Authenti /// Also note that CTAP1 credentials should be assertable over CTAP2. I believe this is /// currently not the case. fn register(&mut self, reg: ®ister::Request) -> Result { - self.up.user_present(&mut self.trussed, constants::U2F_UP_TIMEOUT) + self.up + .user_present(&mut self.trussed, constants::U2F_UP_TIMEOUT) .map_err(|_| Error::ConditionsOfUseNotSatisfied)?; // Generate a new P256 key pair. let private_key = syscall!(self.trussed.generate_p256_private_key(Location::Volatile)).key; - let public_key = syscall!(self.trussed.derive_p256_public_key(private_key, Location::Volatile)).key; + let public_key = syscall!(self + .trussed + .derive_p256_public_key(private_key, Location::Volatile)) + .key; - let serialized_cose_public_key = syscall!(self.trussed.serialize_p256_key( - public_key, KeySerialization::EcdhEsHkdf256 - )).serialized_key; + let serialized_cose_public_key = syscall!(self + .trussed + .serialize_p256_key(public_key, KeySerialization::EcdhEsHkdf256)) + .serialized_key; syscall!(self.trussed.delete(public_key)); - let cose_key: ctap_types::cose::EcdhEsHkdf256PublicKey - = trussed::cbor_deserialize(&serialized_cose_public_key).unwrap(); + let cose_key: ctap_types::cose::EcdhEsHkdf256PublicKey = + trussed::cbor_deserialize(&serialized_cose_public_key).unwrap(); - let wrapping_key = self.state.persistent.key_wrapping_key(&mut self.trussed) + let wrapping_key = self + .state + .persistent + .key_wrapping_key(&mut self.trussed) .map_err(|_| Error::UnspecifiedCheckingError)?; // debug!("wrapping u2f private key"); - let wrapped_key = syscall!(self.trussed.wrap_key_chacha8poly1305( - wrapping_key, - private_key, - ®.app_id, - )).wrapped_key; + let wrapped_key = + syscall!(self + .trussed + .wrap_key_chacha8poly1305(wrapping_key, private_key, ®.app_id,)) + .wrapped_key; // debug!("wrapped_key = {:?}", &wrapped_key); syscall!(self.trussed.delete(private_key)); - let key = Key::WrappedKey(wrapped_key.to_bytes().map_err(|_| Error::UnspecifiedCheckingError)?); - let nonce = syscall!(self.trussed.random_bytes(12)).bytes.as_slice().try_into().unwrap(); + let key = Key::WrappedKey( + wrapped_key + .to_bytes() + .map_err(|_| Error::UnspecifiedCheckingError)?, + ); + let nonce = syscall!(self.trussed.random_bytes(12)) + .bytes + .as_slice() + .try_into() + .unwrap(); let mut rp_id = heapless::String::new(); @@ -88,7 +86,7 @@ impl Authenticator for crate::Authenti // TODO: Is this true? // rp_id.push_str("u2f").ok(); - let rp = ctap_types::webauthn::PublicKeyCredentialRpEntity{ + let rp = ctap_types::webauthn::PublicKeyCredentialRpEntity { id: rp_id, name: None, url: None, @@ -105,10 +103,12 @@ impl Authenticator for crate::Authenti credential::CtapVersion::U2fV2, &rp, &user, - SigningAlgorithm::P256 as i32, key, - self.state.persistent.timestamp(&mut self.trussed).map_err(|_| Error::NotEnoughMemory)?, + self.state + .persistent + .timestamp(&mut self.trussed) + .map_err(|_| Error::NotEnoughMemory)?, None, None, nonce, @@ -117,18 +117,24 @@ impl Authenticator for crate::Authenti // info!("made credential {:?}", &credential); // 12.b generate credential ID { = AEAD(Serialize(Credential)) } - let kek = self.state.persistent.key_encryption_key(&mut self.trussed).map_err(|_| Error::NotEnoughMemory)?; - let credential_id = credential.id(&mut self.trussed, kek, Some(®.app_id)).map_err(|_| Error::NotEnoughMemory)?; + let kek = self + .state + .persistent + .key_encryption_key(&mut self.trussed) + .map_err(|_| Error::NotEnoughMemory)?; + let credential_id = credential + .id(&mut self.trussed, kek, Some(®.app_id)) + .map_err(|_| Error::NotEnoughMemory)?; let mut commitment = Commitment::new(); - commitment.push(0).unwrap(); // reserve byte + commitment.push(0).unwrap(); // reserve byte commitment.extend_from_slice(®.app_id).unwrap(); commitment.extend_from_slice(®.challenge).unwrap(); commitment.extend_from_slice(&credential_id.0).unwrap(); - commitment.push(0x04).unwrap(); // public key uncompressed byte + commitment.push(0x04).unwrap(); // public key uncompressed byte commitment.extend_from_slice(&cose_key.x).unwrap(); commitment.extend_from_slice(&cose_key.y).unwrap(); @@ -138,22 +144,24 @@ impl Authenticator for crate::Authenti (Some((key, cert)), _aaguid) => { info!("aaguid: {}", hex_str!(&_aaguid)); ( - syscall!( - self.trussed.sign(Mechanism::P256, + syscall!(self.trussed.sign( + Mechanism::P256, key, &commitment, SignatureSerialization::Asn1Der - )).signature.to_bytes().unwrap(), - cert + )) + .signature + .to_bytes() + .unwrap(), + cert, ) - }, + } _ => { info!("Not provisioned with attestation key!"); return Err(Error::KeyReferenceNotFound); } }; - Ok(register::Response::new( 0x05, &cose_key, @@ -176,12 +184,13 @@ impl Authenticator for crate::Authenti } else { Err(Error::IncorrectDataParameter) }; - }, + } ControlByte::EnforceUserPresenceAndSign => { - self.up.user_present(&mut self.trussed, constants::U2F_UP_TIMEOUT) + self.up + .user_present(&mut self.trussed, constants::U2F_UP_TIMEOUT) .map_err(|_| Error::ConditionsOfUseNotSatisfied)?; 0x01 - }, + } ControlByte::DontEnforceUserPresenceAndSign => 0x00, }; @@ -189,14 +198,18 @@ impl Authenticator for crate::Authenti let key = match &cred.key { Key::WrappedKey(bytes) => { - let wrapping_key = self.state.persistent.key_wrapping_key(&mut self.trussed) + let wrapping_key = self + .state + .persistent + .key_wrapping_key(&mut self.trussed) .map_err(|_| Error::IncorrectDataParameter)?; let key_result = syscall!(self.trussed.unwrap_key_chacha8poly1305( wrapping_key, bytes, b"", Location::Volatile, - )).key; + )) + .key; match key_result { Some(key) => { info!("loaded u2f key!"); @@ -216,22 +229,30 @@ impl Authenticator for crate::Authenti return Err(Error::IncorrectDataParameter); } - let sig_count = self.state.persistent.timestamp(&mut self.trussed). - map_err(|_| Error::UnspecifiedNonpersistentExecutionError)?; + let sig_count = self + .state + .persistent + .timestamp(&mut self.trussed) + .map_err(|_| Error::UnspecifiedNonpersistentExecutionError)?; let mut commitment = Commitment::new(); commitment.extend_from_slice(&auth.app_id).unwrap(); commitment.push(user_presence_byte).unwrap(); - commitment.extend_from_slice(&sig_count.to_be_bytes()).unwrap(); + commitment + .extend_from_slice(&sig_count.to_be_bytes()) + .unwrap(); commitment.extend_from_slice(&auth.challenge).unwrap(); - let signature = syscall!( - self.trussed.sign(Mechanism::P256, + let signature = syscall!(self.trussed.sign( + Mechanism::P256, key, &commitment, SignatureSerialization::Asn1Der - )).signature.to_bytes().unwrap(); + )) + .signature + .to_bytes() + .unwrap(); Ok(authenticate::Response { user_presence: user_presence_byte, @@ -239,6 +260,4 @@ impl Authenticator for crate::Authenti signature, }) } - } - diff --git a/src/ctap2.rs b/src/ctap2.rs index 1a4c663..71d0a93 100644 --- a/src/ctap2.rs +++ b/src/ctap2.rs @@ -1,15 +1,10 @@ //! The `ctap_types::ctap2::Authenticator` implementation. use ctap_types::{ + ctap2::{self, Authenticator, VendorOperation}, heapless::{String, Vec}, - Error, - ctap2::{ - self, - Authenticator, - VendorOperation, - }, heapless_bytes::Bytes, - sizes, + sizes, Error, }; use littlefs2::path::Path; @@ -17,36 +12,26 @@ use littlefs2::path::Path; use trussed::{ syscall, try_syscall, types::{ - KeyId, - KeySerialization, - Mechanism, - MediumData, - Message, + KeyId, KeySerialization, Location, Mechanism, MediumData, Message, PathBuf, SignatureSerialization, - Location, - PathBuf, }, }; use crate::{ + constants, credential::{ self, Credential, // CredentialList, Key, }, - constants, format_hex, state::{ self, // // (2022-02-27): 9288 bytes // MinCredentialHeap, }, - Result, - - SigningAlgorithm, - UserPresence, - TrussedRequirements, + Result, SigningAlgorithm, TrussedRequirements, UserPresence, }; #[allow(unused_imports)] @@ -59,20 +44,27 @@ pub mod credential_management; impl Authenticator for crate::Authenticator { #[inline(never)] fn get_info(&mut self) -> ctap2::get_info::Response { - debug_now!("remaining stack size: {} bytes", msp() - 0x2000_0000); use core::str::FromStr; let mut versions = Vec::, 4>::new(); versions.push(String::from_str("U2F_V2").unwrap()).unwrap(); - versions.push(String::from_str("FIDO_2_0").unwrap()).unwrap(); + versions + .push(String::from_str("FIDO_2_0").unwrap()) + .unwrap(); // #[cfg(feature = "enable-fido-pre")] - versions.push(String::from_str("FIDO_2_1_PRE").unwrap()).unwrap(); + versions + .push(String::from_str("FIDO_2_1_PRE").unwrap()) + .unwrap(); let mut extensions = Vec::, 4>::new(); // extensions.push(String::from_str("credProtect").unwrap()).unwrap(); - extensions.push(String::from_str("credProtect").unwrap()).unwrap(); - extensions.push(String::from_str("hmac-secret").unwrap()).unwrap(); + extensions + .push(String::from_str("credProtect").unwrap()) + .unwrap(); + extensions + .push(String::from_str("hmac-secret").unwrap()) + .unwrap(); let mut pin_protocols = Vec::::new(); pin_protocols.push(1).unwrap(); @@ -84,7 +76,7 @@ impl Authenticator for crate::Authenti uv: None, plat: Some(false), cred_mgmt: Some(true), - client_pin: match self.state.persistent.pin_is_set() { + client_pin: match self.state.persistent.pin_is_set() { true => Some(true), false => Some(false), }, @@ -107,7 +99,7 @@ impl Authenticator for crate::Authenti transports.push(String::from("nfc")).unwrap(); transports.push(String::from("usb")).unwrap(); - let (_, aaguid)= self.state.identity.attestation(&mut self.trussed); + let (_, aaguid) = self.state.identity.attestation(&mut self.trussed); ctap2::get_info::Response { versions, @@ -143,7 +135,10 @@ impl Authenticator for crate::Authenti if self.state.runtime.active_get_assertion.is_none() { return Err(Error::NotAllowed); } - let credential = self.state.runtime.pop_credential(&mut self.trussed) + let credential = self + .state + .runtime + .pop_credential(&mut self.trussed) .ok_or(Error::NotAllowed)?; // 5. suppress PII if no UV was performed in original GA @@ -156,8 +151,10 @@ impl Authenticator for crate::Authenti } #[inline(never)] - fn make_credential(&mut self, parameters: &ctap2::make_credential::Request) -> Result { - + fn make_credential( + &mut self, + parameters: &ctap2::make_credential::Request, + ) -> Result { let rp_id_hash = self.hash(parameters.rp.id.as_ref()); // 1-4. @@ -168,7 +165,9 @@ impl Authenticator for crate::Authenti } } let uv_performed = self.pin_prechecks( - ¶meters.options, ¶meters.pin_auth, ¶meters.pin_protocol, + ¶meters.options, + ¶meters.pin_auth, + ¶meters.pin_protocol, parameters.client_data_hash.as_ref(), )?; @@ -183,9 +182,12 @@ impl Authenticator for crate::Authenti if let Ok(excluded_cred) = result { use credential::CredentialProtectionPolicy; // If UV is not performed, than CredProtectRequired credentials should not be visibile. - if !(excluded_cred.cred_protect == Some(CredentialProtectionPolicy::Required)) || uv_performed { + if !(excluded_cred.cred_protect == Some(CredentialProtectionPolicy::Required)) + || uv_performed + { info_now!("Excluded!"); - self.up.user_present(&mut self.trussed, constants::FIDO2_UP_TIMEOUT)?; + self.up + .user_present(&mut self.trussed, constants::FIDO2_UP_TIMEOUT)?; return Err(Error::CredentialExcluded); } } @@ -197,8 +199,14 @@ impl Authenticator for crate::Authenti let mut algorithm: Option = None; for param in parameters.pub_key_cred_params.iter() { match param.alg { - -7 => { if algorithm.is_none() { algorithm = Some(SigningAlgorithm::P256); }} - -8 => { algorithm = Some(SigningAlgorithm::Ed25519); } + -7 => { + if algorithm.is_none() { + algorithm = Some(SigningAlgorithm::P256); + } + } + -8 => { + algorithm = Some(SigningAlgorithm::Ed25519); + } // -9 => { algorithm = Some(SigningAlgorithm::Totp); } _ => {} } @@ -207,11 +215,12 @@ impl Authenticator for crate::Authenti Some(algorithm) => { info_now!("algo: {:?}", algorithm as i32); algorithm - }, - None => { return Err(Error::UnsupportedAlgorithm); } + } + None => { + return Err(Error::UnsupportedAlgorithm); + } }; - // 8. process options; on known but unsupported error UnsupportedOption let mut rk_requested = false; @@ -234,18 +243,19 @@ impl Authenticator for crate::Authenti // let mut cred_protect_requested = CredentialProtectionPolicy::Optional; let mut cred_protect_requested = None; if let Some(extensions) = ¶meters.extensions { - hmac_secret_requested = extensions.hmac_secret; if let Some(policy) = &extensions.cred_protect { - cred_protect_requested = Some(credential::CredentialProtectionPolicy::try_from(*policy)?); + cred_protect_requested = + Some(credential::CredentialProtectionPolicy::try_from(*policy)?); } } // debug_now!("hmac-secret = {:?}, credProtect = {:?}", hmac_secret_requested, cred_protect_requested); // 10. get UP, if denied error OperationDenied - self.up.user_present(&mut self.trussed, constants::FIDO2_UP_TIMEOUT)?; + self.up + .user_present(&mut self.trussed, constants::FIDO2_UP_TIMEOUT)?; // 11. generate credential keypair let location = match rk_requested { @@ -259,36 +269,47 @@ impl Authenticator for crate::Authenti match algorithm { SigningAlgorithm::P256 => { private_key = syscall!(self.trussed.generate_p256_private_key(location)).key; - public_key = syscall!(self.trussed.derive_p256_public_key(private_key, Location::Volatile)).key; + public_key = syscall!(self + .trussed + .derive_p256_public_key(private_key, Location::Volatile)) + .key; cose_public_key = syscall!(self.trussed.serialize_key( - Mechanism::P256, public_key, KeySerialization::Cose - )).serialized_key; + Mechanism::P256, + public_key, + KeySerialization::Cose + )) + .serialized_key; let _success = syscall!(self.trussed.delete(public_key)).success; info_now!("deleted public P256 key: {}", _success); } SigningAlgorithm::Ed25519 => { private_key = syscall!(self.trussed.generate_ed255_private_key(location)).key; - public_key = syscall!(self.trussed.derive_ed255_public_key(private_key, Location::Volatile)).key; + public_key = syscall!(self + .trussed + .derive_ed255_public_key(private_key, Location::Volatile)) + .key; cose_public_key = syscall!(self.trussed.serialize_key( - Mechanism::Ed255, public_key, KeySerialization::Cose - )).serialized_key; + Mechanism::Ed255, + public_key, + KeySerialization::Cose + )) + .serialized_key; let _success = syscall!(self.trussed.delete(public_key)).success; info_now!("deleted public Ed25519 key: {}", _success); - } - // SigningAlgorithm::Totp => { - // if parameters.client_data_hash.len() != 32 { - // return Err(Error::InvalidParameter); - // } - // // b'TOTP---W\x0e\xf1\xe0\xd7\x83\xfe\t\xd1\xc1U\xbf\x08T_\x07v\xb2\xc6--TOTP' - // let totp_secret: [u8; 20] = parameters.client_data_hash[6..26].try_into().unwrap(); - // private_key = syscall!(self.trussed.unsafe_inject_shared_key( - // &totp_secret, Location::Internal)).key; - // // info_now!("totes injected"); - // let fake_cose_pk = ctap_types::cose::TotpPublicKey {}; - // let fake_serialized_cose_pk = trussed::cbor_serialize_bytes(&fake_cose_pk) - // .map_err(|_| Error::NotAllowed)?; - // cose_public_key = fake_serialized_cose_pk; // Bytes::from_slice(&[0u8; 20]).unwrap(); - // } + } // SigningAlgorithm::Totp => { + // if parameters.client_data_hash.len() != 32 { + // return Err(Error::InvalidParameter); + // } + // // b'TOTP---W\x0e\xf1\xe0\xd7\x83\xfe\t\xd1\xc1U\xbf\x08T_\x07v\xb2\xc6--TOTP' + // let totp_secret: [u8; 20] = parameters.client_data_hash[6..26].try_into().unwrap(); + // private_key = syscall!(self.trussed.unsafe_inject_shared_key( + // &totp_secret, Location::Internal)).key; + // // info_now!("totes injected"); + // let fake_cose_pk = ctap_types::cose::TotpPublicKey {}; + // let fake_serialized_cose_pk = trussed::cbor_serialize_bytes(&fake_cose_pk) + // .map_err(|_| Error::NotAllowed)?; + // cose_public_key = fake_serialized_cose_pk; // Bytes::from_slice(&[0u8; 20]).unwrap(); + // } } // 12. if `rk` is set, store or overwrite key pair, if full error KeyStoreFull @@ -303,7 +324,8 @@ impl Authenticator for crate::Authenti wrapping_key, private_key, &rp_id_hash, - )).wrapped_key; + )) + .wrapped_key; // 32B key, 12B nonce, 16B tag + some info on algorithm (P256/Ed25519) // Turns out it's size 92 (enum serialization not optimized yet...) @@ -314,11 +336,18 @@ impl Authenticator for crate::Authenti }; // injecting this is a bit mehhh.. - let nonce = syscall!(self.trussed.random_bytes(12)).bytes.as_slice().try_into().unwrap(); + let nonce = syscall!(self.trussed.random_bytes(12)) + .bytes + .as_slice() + .try_into() + .unwrap(); info_now!("nonce = {:?}", &nonce); // 12.b generate credential ID { = AEAD(Serialize(Credential)) } - let kek = self.state.persistent.key_encryption_key(&mut self.trussed)?; + let kek = self + .state + .persistent + .key_encryption_key(&mut self.trussed)?; // store it. // TODO: overwrite, error handling with KeyStoreFull @@ -343,7 +372,8 @@ impl Authenticator for crate::Authenti let serialized_credential = credential.serialize()?; // first delete any other RK cred with same RP + UserId if there is one. - self.delete_resident_key_by_user_id(&rp_id_hash, &credential.user.id).ok(); + self.delete_resident_key_by_user_id(&rp_id_hash, &credential.user.id) + .ok(); // then store key, making it resident let credential_id_hash = self.hash(credential_id.0.as_ref()); @@ -354,7 +384,8 @@ impl Authenticator for crate::Authenti // user attribute for later easy lookup // Some(rp_id_hash.clone()), None, - )).map_err(|_| Error::KeyStoreFull)?; + )) + .map_err(|_| Error::KeyStoreFull)?; } // 13. generate and return attestation statement using clientDataHash @@ -401,7 +432,6 @@ impl Authenticator for crate::Authenti cred_protect: parameters.extensions.as_ref().unwrap().cred_protect, hmac_secret: parameters.extensions.as_ref().unwrap().hmac_secret, }) - } else { None } @@ -416,9 +446,13 @@ impl Authenticator for crate::Authenti // can we write Sum somehow? // debug_now!("seeking commitment, {} + {}", serialized_auth_data.len(), parameters.client_data_hash.len()); let mut commitment = Bytes::<1024>::new(); - commitment.extend_from_slice(&serialized_auth_data).map_err(|_| Error::Other)?; + commitment + .extend_from_slice(&serialized_auth_data) + .map_err(|_| Error::Other)?; // debug_now!("serialized_auth_data ={:?}", &serialized_auth_data); - commitment.extend_from_slice(¶meters.client_data_hash).map_err(|_| Error::Other)?; + commitment + .extend_from_slice(¶meters.client_data_hash) + .map_err(|_| Error::Other)?; // debug_now!("client_data_hash = {:?}", ¶meters.client_data_hash); // debug_now!("commitment = {:?}", &commitment); @@ -434,39 +468,44 @@ impl Authenticator for crate::Authenti if attestation_maybe.is_none() { match algorithm { SigningAlgorithm::Ed25519 => { - let signature = syscall!(self.trussed.sign_ed255(private_key, &commitment)).signature; + let signature = + syscall!(self.trussed.sign_ed255(private_key, &commitment)).signature; (signature.to_bytes().map_err(|_| Error::Other)?, -8) } SigningAlgorithm::P256 => { // DO NOT prehash here, `trussed` does that - let der_signature = syscall!(self.trussed.sign_p256(private_key, &commitment, SignatureSerialization::Asn1Der)).signature; + let der_signature = syscall!(self.trussed.sign_p256( + private_key, + &commitment, + SignatureSerialization::Asn1Der + )) + .signature; (der_signature.to_bytes().map_err(|_| Error::Other)?, -7) - } - // SigningAlgorithm::Totp => { - // // maybe we can fake it here too, but seems kinda weird - // // return Err(Error::UnsupportedAlgorithm); - // // micro-ecc is borked. let's self-sign anyway - // let hash = syscall!(self.trussed.hash_sha256(&commitment.as_ref())).hash; - // let tmp_key = syscall!(self.trussed - // .generate_p256_private_key(Location::Volatile)) - // .key; + } // SigningAlgorithm::Totp => { + // // maybe we can fake it here too, but seems kinda weird + // // return Err(Error::UnsupportedAlgorithm); + // // micro-ecc is borked. let's self-sign anyway + // let hash = syscall!(self.trussed.hash_sha256(&commitment.as_ref())).hash; + // let tmp_key = syscall!(self.trussed + // .generate_p256_private_key(Location::Volatile)) + // .key; - // let signature = syscall!(self.trussed.sign_p256( - // tmp_key, - // &hash, - // SignatureSerialization::Asn1Der, - // )).signature; - // (signature.to_bytes().map_err(|_| Error::Other)?, -7) - // } + // let signature = syscall!(self.trussed.sign_p256( + // tmp_key, + // &hash, + // SignatureSerialization::Asn1Der, + // )).signature; + // (signature.to_bytes().map_err(|_| Error::Other)?, -7) + // } } } else { - let signature = syscall!(self.trussed.sign_p256( attestation_maybe.as_ref().unwrap().0, &commitment, SignatureSerialization::Asn1Der, - )).signature; + )) + .signature; (signature.to_bytes().map_err(|_| Error::Other)?, -7) } }; @@ -516,14 +555,14 @@ impl Authenticator for crate::Authenti // 2. check for user presence // denied -> OperationDenied // timeout -> UserActionTimeout - self.up.user_present(&mut self.trussed, constants::FIDO2_UP_TIMEOUT)?; + self.up + .user_present(&mut self.trussed, constants::FIDO2_UP_TIMEOUT)?; // Delete resident keys syscall!(self.trussed.delete_all(Location::Internal)); - syscall!(self.trussed.remove_dir_all( - Location::Internal, - PathBuf::from("rk"), - )); + syscall!(self + .trussed + .remove_dir_all(Location::Internal, PathBuf::from("rk"),)); // b. delete persistent state self.state.persistent.reset(&mut self.trussed)?; @@ -535,22 +574,25 @@ impl Authenticator for crate::Authenti } fn selection(&mut self) -> Result<()> { - self.up.user_present(&mut self.trussed, constants::FIDO2_UP_TIMEOUT) + self.up + .user_present(&mut self.trussed, constants::FIDO2_UP_TIMEOUT) } #[inline(never)] - fn client_pin(&mut self, parameters: &ctap2::client_pin::Request) -> Result { + fn client_pin( + &mut self, + parameters: &ctap2::client_pin::Request, + ) -> Result { use ctap2::client_pin::PinV1Subcommand as Subcommand; debug_now!("CTAP2.PIN..."); // info_now!("{:?}", parameters); // TODO: Handle pin protocol V2 - if parameters.pin_protocol != 1{ + if parameters.pin_protocol != 1 { return Err(Error::InvalidParameter); } Ok(match parameters.sub_command { - Subcommand::GetRetries => { debug_now!("CTAP2.Pin.GetRetries"); @@ -565,9 +607,16 @@ impl Authenticator for crate::Authenti debug_now!("CTAP2.Pin.GetKeyAgreement"); let private_key = self.state.runtime.key_agreement_key(&mut self.trussed); - let public_key = syscall!(self.trussed.derive_p256_public_key(private_key, Location::Volatile)).key; + let public_key = syscall!(self + .trussed + .derive_p256_public_key(private_key, Location::Volatile)) + .key; let serialized_cose_key = syscall!(self.trussed.serialize_key( - Mechanism::P256, public_key, KeySerialization::EcdhEsHkdf256)).serialized_key; + Mechanism::P256, + public_key, + KeySerialization::EcdhEsHkdf256 + )) + .serialized_key; let cose_key = trussed::cbor_deserialize(&serialized_cose_key).unwrap(); syscall!(self.trussed.delete(public_key)); @@ -584,15 +633,21 @@ impl Authenticator for crate::Authenti // 1. check mandatory parameters let platform_kek = match parameters.key_agreement.as_ref() { Some(key) => key, - None => { return Err(Error::MissingParameter); } + None => { + return Err(Error::MissingParameter); + } }; let new_pin_enc = match parameters.new_pin_enc.as_ref() { Some(pin) => pin, - None => { return Err(Error::MissingParameter); } + None => { + return Err(Error::MissingParameter); + } }; let pin_auth = match parameters.pin_auth.as_ref() { Some(auth) => auth, - None => { return Err(Error::MissingParameter); } + None => { + return Err(Error::MissingParameter); + } }; // 2. is pin already set @@ -601,7 +656,10 @@ impl Authenticator for crate::Authenti } // 3. generate shared secret - let shared_secret = self.state.runtime.generate_shared_secret(&mut self.trussed, platform_kek)?; + let shared_secret = self + .state + .runtime + .generate_shared_secret(&mut self.trussed, platform_kek)?; // TODO: there are moar early returns!! // - implement Drop? @@ -617,7 +675,9 @@ impl Authenticator for crate::Authenti // 6. store LEFT(SHA-256(newPin), 16), set retries to 8 self.hash_store_pin(&new_pin)?; - self.state.reset_retries(&mut self.trussed).map_err(|_| Error::Other)?; + self.state + .reset_retries(&mut self.trussed) + .map_err(|_| Error::Other)?; ctap2::client_pin::Response { key_agreement: None, @@ -632,31 +692,44 @@ impl Authenticator for crate::Authenti // 1. check mandatory parameters let platform_kek = match parameters.key_agreement.as_ref() { Some(key) => key, - None => { return Err(Error::MissingParameter); } + None => { + return Err(Error::MissingParameter); + } }; let pin_hash_enc = match parameters.pin_hash_enc.as_ref() { Some(hash) => hash, - None => { return Err(Error::MissingParameter); } + None => { + return Err(Error::MissingParameter); + } }; let new_pin_enc = match parameters.new_pin_enc.as_ref() { Some(pin) => pin, - None => { return Err(Error::MissingParameter); } + None => { + return Err(Error::MissingParameter); + } }; let pin_auth = match parameters.pin_auth.as_ref() { Some(auth) => auth, - None => { return Err(Error::MissingParameter); } + None => { + return Err(Error::MissingParameter); + } }; // 2. fail if no retries left self.state.pin_blocked()?; // 3. generate shared secret - let shared_secret = self.state.runtime.generate_shared_secret(&mut self.trussed, platform_kek)?; + let shared_secret = self + .state + .runtime + .generate_shared_secret(&mut self.trussed, platform_kek)?; // 4. verify pinAuth let mut data = MediumData::new(); - data.extend_from_slice(new_pin_enc).map_err(|_| Error::InvalidParameter)?; - data.extend_from_slice(pin_hash_enc).map_err(|_| Error::InvalidParameter)?; + data.extend_from_slice(new_pin_enc) + .map_err(|_| Error::InvalidParameter)?; + data.extend_from_slice(pin_hash_enc) + .map_err(|_| Error::InvalidParameter)?; self.verify_pin_auth(shared_secret, &data, pin_auth)?; // 5. decrement retries @@ -689,18 +762,25 @@ impl Authenticator for crate::Authenti // 1. check mandatory parameters let platform_kek = match parameters.key_agreement.as_ref() { Some(key) => key, - None => { return Err(Error::MissingParameter); } + None => { + return Err(Error::MissingParameter); + } }; let pin_hash_enc = match parameters.pin_hash_enc.as_ref() { Some(hash) => hash, - None => { return Err(Error::MissingParameter); } + None => { + return Err(Error::MissingParameter); + } }; // 2. fail if no retries left self.state.pin_blocked()?; // 3. generate shared secret - let shared_secret = self.state.runtime.generate_shared_secret(&mut self.trussed, platform_kek)?; + let shared_secret = self + .state + .runtime + .generate_shared_secret(&mut self.trussed, platform_kek)?; // 4. decrement retires self.state.decrement_retries(&mut self.trussed)?; @@ -715,7 +795,8 @@ impl Authenticator for crate::Authenti let pin_token = self.state.runtime.pin_token(&mut self.trussed); debug_now!("wrapping pin token"); // info_now!("exists? {}", syscall!(self.trussed.exists(shared_secret)).exists); - let pin_token_enc = syscall!(self.trussed.wrap_key_aes256cbc(shared_secret, pin_token)).wrapped_key; + let pin_token_enc = + syscall!(self.trussed.wrap_key_aes256cbc(shared_secret, pin_token)).wrapped_key; syscall!(self.trussed.delete(shared_secret)); @@ -732,9 +813,9 @@ impl Authenticator for crate::Authenti } } - Subcommand::GetPinUvAuthTokenUsingUvWithPermissions | - Subcommand::GetUVRetries | - Subcommand::GetPinUvAuthTokenUsingPinWithPermissions => { + Subcommand::GetPinUvAuthTokenUsingUvWithPermissions + | Subcommand::GetUVRetries + | Subcommand::GetPinUvAuthTokenUsingPinWithPermissions => { // todo!("not implemented yet") return Err(Error::InvalidParameter); } @@ -742,11 +823,12 @@ impl Authenticator for crate::Authenti } #[inline(never)] - fn credential_management(&mut self, parameters: &ctap2::credential_management::Request) - -> Result { - - use ctap2::credential_management::Subcommand; + fn credential_management( + &mut self, + parameters: &ctap2::credential_management::Request, + ) -> Result { use credential_management as cm; + use ctap2::credential_management::Subcommand; // TODO: I see "failed pinauth" output, but then still continuation... self.verify_pin_auth_using_token(parameters)?; @@ -754,44 +836,40 @@ impl Authenticator for crate::Authenti let mut cred_mgmt = cm::CredentialManagement::new(self); let sub_parameters = ¶meters.sub_command_params; match parameters.sub_command { - // 0x1 - Subcommand::GetCredsMetadata => - cred_mgmt.get_creds_metadata(), + Subcommand::GetCredsMetadata => cred_mgmt.get_creds_metadata(), // 0x2 - Subcommand::EnumerateRpsBegin => - cred_mgmt.first_relying_party(), + Subcommand::EnumerateRpsBegin => cred_mgmt.first_relying_party(), // 0x3 - Subcommand::EnumerateRpsGetNextRp => - cred_mgmt.next_relying_party(), + Subcommand::EnumerateRpsGetNextRp => cred_mgmt.next_relying_party(), // 0x4 Subcommand::EnumerateCredentialsBegin => { - let sub_parameters = sub_parameters.as_ref() - .ok_or(Error::MissingParameter)?; + let sub_parameters = sub_parameters.as_ref().ok_or(Error::MissingParameter)?; cred_mgmt.first_credential( sub_parameters - .rp_id_hash.as_ref() + .rp_id_hash + .as_ref() .ok_or(Error::MissingParameter)?, ) } // 0x5 - Subcommand::EnumerateCredentialsGetNextCredential => - cred_mgmt.next_credential(), + Subcommand::EnumerateCredentialsGetNextCredential => cred_mgmt.next_credential(), // 0x6 Subcommand::DeleteCredential => { - let sub_parameters = sub_parameters.as_ref() - .ok_or(Error::MissingParameter)?; + let sub_parameters = sub_parameters.as_ref().ok_or(Error::MissingParameter)?; - cred_mgmt.delete_credential(sub_parameters - .credential_id.as_ref() + cred_mgmt.delete_credential( + sub_parameters + .credential_id + .as_ref() .ok_or(Error::MissingParameter)?, - ) + ) } } } @@ -807,18 +885,21 @@ impl Authenticator for crate::Authenti Ok(()) } - #[inline(never)] - fn get_assertion(&mut self, parameters: &ctap2::get_assertion::Request) -> Result { - + fn get_assertion( + &mut self, + parameters: &ctap2::get_assertion::Request, + ) -> Result { debug_now!("remaining stack size: {} bytes", msp() - 0x2000_0000); let rp_id_hash = self.hash(parameters.rp_id.as_ref()); // 1-4. let uv_performed = match self.pin_prechecks( - ¶meters.options, ¶meters.pin_auth, ¶meters.pin_protocol, - parameters.client_data_hash.as_ref(), + ¶meters.options, + ¶meters.pin_auth, + ¶meters.pin_protocol, + parameters.client_data_hash.as_ref(), ) { Ok(b) => b, Err(Error::PinRequired) => { @@ -833,9 +914,9 @@ impl Authenticator for crate::Authenti // Note: If allowList is passed, credential is Some(credential) // If no allowList is passed, credential is None and the retrieved credentials // are stored in state.runtime.credential_heap - let (credential, num_credentials) = self.prepare_credentials( - &rp_id_hash, ¶meters.allow_list, uv_performed - ).ok_or(Error::NoCredentials)?; + let (credential, num_credentials) = self + .prepare_credentials(&rp_id_hash, ¶meters.allow_list, uv_performed) + .ok_or(Error::NoCredentials)?; info_now!("found {:?} applicable credentials", num_credentials); info_now!("{:?}", &credential); @@ -852,7 +933,8 @@ impl Authenticator for crate::Authenti // 7. collect user presence let up_performed = if do_up { info_now!("asking for up"); - self.up.user_present(&mut self.trussed, constants::FIDO2_UP_TIMEOUT)?; + self.up + .user_present(&mut self.trussed, constants::FIDO2_UP_TIMEOUT)?; true } else { info_now!("not asking for up"); @@ -884,41 +966,42 @@ impl Authenticator for crate::Authenti self.assert_with_credential(num_credentials, credential) } - } // impl Authenticator for crate::Authenticator -impl crate::Authenticator -{ +impl crate::Authenticator { #[inline(never)] - fn check_credential_applicable(&mut self, credential: &Credential, allowlist_passed: bool, uv_performed: bool) -> bool { - + fn check_credential_applicable( + &mut self, + credential: &Credential, + allowlist_passed: bool, + uv_performed: bool, + ) -> bool { if !self.check_key_exists(credential.algorithm, &credential.key) { return false; } - if !{ - use credential::CredentialProtectionPolicy as Policy; - debug_now!("CredentialProtectionPolicy {:?}", &credential.cred_protect); - match credential.cred_protect { - None | Some(Policy::Optional) => true, - Some(Policy::OptionalWithCredentialIdList) => allowlist_passed || uv_performed, - Some(Policy::Required) => uv_performed, - - } - } { - return false; - } - true + if !{ + use credential::CredentialProtectionPolicy as Policy; + debug_now!("CredentialProtectionPolicy {:?}", &credential.cred_protect); + match credential.cred_protect { + None | Some(Policy::Optional) => true, + Some(Policy::OptionalWithCredentialIdList) => allowlist_passed || uv_performed, + Some(Policy::Required) => uv_performed, + } + } { + return false; + } + true } #[inline(never)] fn prepare_credentials( - &mut self, rp_id_hash: &Bytes<32>, + &mut self, + rp_id_hash: &Bytes<32>, allow_list: &Option, uv_performed: bool, - ) -> Option<(Credential, u32)> - { + ) -> Option<(Credential, u32)> { debug_now!("remaining stack size: {} bytes", msp() - 0x2000_0000); self.state.runtime.clear_credential_cache(); @@ -963,20 +1046,19 @@ impl crate::Authenticator // we are only dealing with discoverable credentials. debug_now!("Allowlist not passed, fetching RKs"); - let mut maybe_path = syscall!(self.trussed.read_dir_first( - Location::Internal, - rp_rk_dir(rp_id_hash), - None, - )).entry.map(|entry| PathBuf::try_from(entry.path()).unwrap()); + let mut maybe_path = + syscall!(self + .trussed + .read_dir_first(Location::Internal, rp_rk_dir(rp_id_hash), None,)) + .entry + .map(|entry| PathBuf::try_from(entry.path()).unwrap()); - use core::str::FromStr; use crate::state::CachedCredential; + use core::str::FromStr; while let Some(path) = maybe_path { - let credential_data = syscall!(self.trussed.read_file( - Location::Internal, - path.clone(), - )).data; + let credential_data = + syscall!(self.trussed.read_file(Location::Internal, path.clone(),)).data; let credential = Credential::deserialize(&credential_data).ok()?; @@ -988,7 +1070,8 @@ impl crate::Authenticator } maybe_path = syscall!(self.trussed.read_dir_next()) - .entry.map(|entry| PathBuf::try_from(entry.path()).unwrap()); + .entry + .map(|entry| PathBuf::try_from(entry.path()).unwrap()); } let num_credentials = self.state.runtime.remaining_credentials(); @@ -996,20 +1079,27 @@ impl crate::Authenticator credential.map(|credential| (credential, num_credentials)) } - fn decrypt_pin_hash_and_maybe_escalate(&mut self, shared_secret: KeyId, pin_hash_enc: &Bytes<64>) - -> Result<()> - { - let pin_hash = syscall!(self.trussed.decrypt_aes256cbc( - shared_secret, pin_hash_enc)).plaintext.ok_or(Error::Other)?; + fn decrypt_pin_hash_and_maybe_escalate( + &mut self, + shared_secret: KeyId, + pin_hash_enc: &Bytes<64>, + ) -> Result<()> { + let pin_hash = syscall!(self.trussed.decrypt_aes256cbc(shared_secret, pin_hash_enc)) + .plaintext + .ok_or(Error::Other)?; let stored_pin_hash = match self.state.persistent.pin_hash() { Some(hash) => hash, - None => { return Err(Error::PinNotSet); } + None => { + return Err(Error::PinNotSet); + } }; if pin_hash != stored_pin_hash { // I) generate new KEK - self.state.runtime.rotate_key_agreement_key(&mut self.trussed); + self.state + .runtime + .rotate_key_agreement_key(&mut self.trussed); if self.state.persistent.retries() == 0 { return Err(Error::PinBlocked); } @@ -1025,20 +1115,28 @@ impl crate::Authenticator fn hash_store_pin(&mut self, pin: &Message) -> Result<()> { let pin_hash_32 = syscall!(self.trussed.hash_sha256(pin)).hash; let pin_hash: [u8; 16] = pin_hash_32[..16].try_into().unwrap(); - self.state.persistent.set_pin_hash(&mut self.trussed, pin_hash).unwrap(); + self.state + .persistent + .set_pin_hash(&mut self.trussed, pin_hash) + .unwrap(); Ok(()) } - fn decrypt_pin_check_length(&mut self, shared_secret: KeyId, pin_enc: &[u8]) -> Result { + fn decrypt_pin_check_length( + &mut self, + shared_secret: KeyId, + pin_enc: &[u8], + ) -> Result { // pin is expected to be filled with null bytes to length at least 64 if pin_enc.len() < 64 { // correct error? return Err(Error::PinPolicyViolation); } - let mut pin = syscall!(self.trussed.decrypt_aes256cbc( - shared_secret, pin_enc)).plaintext.ok_or(Error::Other)?; + let mut pin = syscall!(self.trussed.decrypt_aes256cbc(shared_secret, pin_enc)) + .plaintext + .ok_or(Error::Other)?; // // temp // let pin_length = pin.iter().position(|&b| b == b'\0').unwrap_or(pin.len()); @@ -1055,7 +1153,6 @@ impl crate::Authenticator Ok(pin) } - // fn verify_pin(&mut self, pin_auth: &Bytes<16>, client_data_hash: &Bytes<32>) -> bool { fn verify_pin(&mut self, pin_auth: &[u8; 16], data: &[u8]) -> Result<()> { let key = self.state.runtime.pin_token(&mut self.trussed); @@ -1067,10 +1164,14 @@ impl crate::Authenticator } } - fn verify_pin_auth(&mut self, shared_secret: KeyId, data: &[u8], pin_auth: &Bytes<16>) - -> Result<()> - { - let expected_pin_auth = syscall!(self.trussed.sign_hmacsha256(shared_secret, data)).signature; + fn verify_pin_auth( + &mut self, + shared_secret: KeyId, + data: &[u8], + pin_auth: &Bytes<16>, + ) -> Result<()> { + let expected_pin_auth = + syscall!(self.trussed.sign_hmacsha256(shared_secret, data)).signature; if expected_pin_auth[..16] == pin_auth[..] { Ok(()) @@ -1082,53 +1183,54 @@ impl crate::Authenticator // fn verify_pin_auth_using_token(&mut self, data: &[u8], pin_auth: &Bytes<16>) fn verify_pin_auth_using_token( &mut self, - parameters: &ctap2::credential_management::Request + parameters: &ctap2::credential_management::Request, ) -> Result<()> { - // info_now!("CM params: {:?}", parameters); use ctap2::credential_management::Subcommand; match parameters.sub_command { // are we Haskell yet lol - sub_command @ Subcommand::GetCredsMetadata | - sub_command @ Subcommand::EnumerateRpsBegin | - sub_command @ Subcommand::EnumerateCredentialsBegin | - sub_command @ Subcommand::DeleteCredential => { - + sub_command @ Subcommand::GetCredsMetadata + | sub_command @ Subcommand::EnumerateRpsBegin + | sub_command @ Subcommand::EnumerateCredentialsBegin + | sub_command @ Subcommand::DeleteCredential => { // check pinProtocol let pin_protocol = parameters // .sub_command_params.as_ref().ok_or(Error::MissingParameter)? - .pin_protocol.ok_or(Error::MissingParameter)?; + .pin_protocol + .ok_or(Error::MissingParameter)?; if pin_protocol != 1 { return Err(Error::InvalidParameter); } // check pinAuth let pin_token = self.state.runtime.pin_token(&mut self.trussed); - let mut data: Bytes<{sizes::MAX_CREDENTIAL_ID_LENGTH_PLUS_256}> = + let mut data: Bytes<{ sizes::MAX_CREDENTIAL_ID_LENGTH_PLUS_256 }> = Bytes::from_slice(&[sub_command as u8]).unwrap(); let len = 1 + match sub_command { - Subcommand::EnumerateCredentialsBegin | - Subcommand::DeleteCredential => { + Subcommand::EnumerateCredentialsBegin | Subcommand::DeleteCredential => { data.resize_to_capacity(); // ble, need to reserialize ctap_types::serde::cbor_serialize( - ¶meters.sub_command_params - .as_ref() - .ok_or(Error::MissingParameter)?, + ¶meters + .sub_command_params + .as_ref() + .ok_or(Error::MissingParameter)?, &mut data[1..], - ).map_err(|_| Error::LimitExceeded)?.len() + ) + .map_err(|_| Error::LimitExceeded)? + .len() } _ => 0, }; // info_now!("input to hmacsha256: {:?}", &data[..len]); - let expected_pin_auth = syscall!(self.trussed.sign_hmacsha256( - pin_token, - &data[..len], - )).signature; + let expected_pin_auth = + syscall!(self.trussed.sign_hmacsha256(pin_token, &data[..len],)).signature; let pin_auth = parameters - .pin_auth.as_ref().ok_or(Error::MissingParameter)?; + .pin_auth + .as_ref() + .ok_or(Error::MissingParameter)?; if expected_pin_auth[..16] == pin_auth[..] { info_now!("passed pinauth"); @@ -1144,27 +1246,24 @@ impl crate::Authenticator info_now!("pinAuthInvalid"); Err(Error::PinAuthInvalid) } - } } // don't need the PIN auth, they're continuations // of already checked CredMgmt subcommands - Subcommand::EnumerateRpsGetNextRp | - Subcommand::EnumerateCredentialsGetNextCredential - => Ok(()), + Subcommand::EnumerateRpsGetNextRp + | Subcommand::EnumerateCredentialsGetNextCredential => Ok(()), } } /// Returns whether UV was performed. - fn pin_prechecks(&mut self, + fn pin_prechecks( + &mut self, options: &Option, pin_auth: &Option, pin_protocol: &Option, data: &[u8], - ) - -> Result - { + ) -> Result { // 1. pinAuth zero length -> wait for user touch, then // return PinNotSet if not set, PinInvalid if set // @@ -1172,7 +1271,8 @@ impl crate::Authenticator // wants to enforce PIN and needs to figure out which authnrs support PIN if let Some(pin_auth) = pin_auth.as_ref() { if pin_auth.len() == 0 { - self.up.user_present(&mut self.trussed, constants::FIDO2_UP_TIMEOUT)?; + self.up + .user_present(&mut self.trussed, constants::FIDO2_UP_TIMEOUT)?; if !self.state.persistent.pin_is_set() { return Err(Error::PinNotSet); } else { @@ -1207,7 +1307,6 @@ impl crate::Authenticator // TODO: Should we should fail if `uv` is passed? // Current thinking: no if self.state.persistent.pin_is_set() { - // let mut uv_performed = false; if let Some(ref pin_auth) = pin_auth { if pin_auth.len() != 16 { @@ -1226,12 +1325,10 @@ impl crate::Authenticator )?; return Ok(true); - } else { // 7. pinAuth present + pinProtocol != 1 --> error PinAuthInvalid return Err(Error::PinAuthInvalid); } - } else { // 6. pinAuth not present + clientPin set --> error PinRequired if self.state.persistent.pin_is_set() { @@ -1266,7 +1363,8 @@ impl crate::Authenticator } #[inline(never)] - fn process_assertion_extensions(&mut self, + fn process_assertion_extensions( + &mut self, get_assertion_state: &state::ActiveGetAssertionData, extensions: &ctap2::get_assertion::ExtensionsInput, _credential: &Credential, @@ -1287,11 +1385,16 @@ impl crate::Authenticator credential_key, Some(Bytes::from_slice(&[get_assertion_state.uv_performed as u8]).unwrap()), trussed::types::StorageAttributes::new().set_persistence(Location::Volatile) - )).key; + )) + .key; // Verify the auth tag, which uses the same process as the pinAuth - let kek = self.state.runtime.generate_shared_secret(&mut self.trussed, &hmac_secret.key_agreement)?; - self.verify_pin_auth(kek, &hmac_secret.salt_enc, &hmac_secret.salt_auth).map_err(|_| Error::ExtensionFirst)?; + let kek = self + .state + .runtime + .generate_shared_secret(&mut self.trussed, &hmac_secret.key_agreement)?; + self.verify_pin_auth(kek, &hmac_secret.salt_enc, &hmac_secret.salt_auth) + .map_err(|_| Error::ExtensionFirst)?; if hmac_secret.salt_enc.len() != 32 && hmac_secret.salt_enc.len() != 64 { debug_now!("invalid hmac-secret length"); @@ -1299,24 +1402,29 @@ impl crate::Authenticator } // decrypt input salt_enc to get salt1 or (salt1 || salt2) - let salts = syscall!( - self.trussed.decrypt(Mechanism::Aes256Cbc, kek, &hmac_secret.salt_enc, b"", b"", b"") - ).plaintext.ok_or(Error::InvalidOption)?; + let salts = syscall!(self.trussed.decrypt( + Mechanism::Aes256Cbc, + kek, + &hmac_secret.salt_enc, + b"", + b"", + b"" + )) + .plaintext + .ok_or(Error::InvalidOption)?; let mut salt_output: Bytes<64> = Bytes::new(); // output1 = hmac_sha256(credRandom, salt1) - let output1 = syscall!( - self.trussed.sign_hmacsha256(cred_random, &salts[0..32]) - ).signature; + let output1 = + syscall!(self.trussed.sign_hmacsha256(cred_random, &salts[0..32])).signature; salt_output.extend_from_slice(&output1).unwrap(); if salts.len() == 64 { // output2 = hmac_sha256(credRandom, salt2) - let output2 = syscall!( - self.trussed.sign_hmacsha256(cred_random, &salts[32..64]) - ).signature; + let output2 = + syscall!(self.trussed.sign_hmacsha256(cred_random, &salts[32..64])).signature; salt_output.extend_from_slice(&output2).unwrap(); } @@ -1324,25 +1432,26 @@ impl crate::Authenticator syscall!(self.trussed.delete(cred_random)); // output_enc = aes256-cbc(sharedSecret, IV=0, output1 || output2) - let output_enc = syscall!( - self.trussed.encrypt(Mechanism::Aes256Cbc, kek, &salt_output, b"", None) - ).ciphertext; + let output_enc = + syscall!(self + .trussed + .encrypt(Mechanism::Aes256Cbc, kek, &salt_output, b"", None)) + .ciphertext; Ok(Some(ctap2::get_assertion::ExtensionsOutput { - hmac_secret: Some(Bytes::from_slice(&output_enc).unwrap()) + hmac_secret: Some(Bytes::from_slice(&output_enc).unwrap()), })) - } else { Ok(None) } - } - #[inline(never)] - fn assert_with_credential(&mut self, num_credentials: Option, credential: Credential) - -> Result - { + fn assert_with_credential( + &mut self, + num_credentials: Option, + credential: Credential, + ) -> Result { let data = self.state.runtime.active_get_assertion.clone().unwrap(); let rp_id_hash = Bytes::from_slice(&data.rp_id_hash).unwrap(); @@ -1357,12 +1466,15 @@ impl crate::Authenticator b"", // &rp_id_hash, Location::Volatile, - )).key; + )) + .key; // debug_now!("key result: {:?}", &key_result); info_now!("key result"); match key_result { Some(key) => (key, false), - None => { return Err(Error::Other); } + None => { + return Err(Error::Other); + } } } }; @@ -1377,7 +1489,10 @@ impl crate::Authenticator // 9./10. sign clientDataHash || authData with "first" credential // info_now!("signing with credential {:?}", &credential); - let kek = self.state.persistent.key_encryption_key(&mut self.trussed)?; + let kek = self + .state + .persistent + .key_encryption_key(&mut self.trussed)?; let credential_id = credential.id(&mut self.trussed, kek, Some(&rp_id_hash))?; use ctap2::AuthenticatorDataFlags as Flags; @@ -1403,20 +1518,26 @@ impl crate::Authenticator sign_count: sig_count, attested_credential_data: None, - extensions: extensions_output + extensions: extensions_output, }; let serialized_auth_data = authenticator_data.serialize(); let mut commitment = Bytes::<1024>::new(); - commitment.extend_from_slice(&serialized_auth_data).map_err(|_| Error::Other)?; - commitment.extend_from_slice(&data.client_data_hash).map_err(|_| Error::Other)?; + commitment + .extend_from_slice(&serialized_auth_data) + .map_err(|_| Error::Other)?; + commitment + .extend_from_slice(&data.client_data_hash) + .map_err(|_| Error::Other)?; let (mechanism, serialization) = match credential.algorithm { -7 => (Mechanism::P256, SignatureSerialization::Asn1Der), -8 => (Mechanism::Ed255, SignatureSerialization::Raw), // -9 => (Mechanism::Totp, SignatureSerialization::Raw), - _ => { return Err(Error::Other); } + _ => { + return Err(Error::Other); + } }; debug_now!("signing with {:?}, {:?}", &mechanism, &serialization); @@ -1426,8 +1547,12 @@ impl crate::Authenticator // info_now!("TOTP with timestamp {:?}", ×tamp); // syscall!(self.trussed.sign_totp(key, timestamp)).signature.to_bytes().unwrap() // } - _ => syscall!(self.trussed.sign(mechanism, key, &commitment, serialization)).signature - .to_bytes().unwrap(), + _ => syscall!(self + .trussed + .sign(mechanism, key, &commitment, serialization)) + .signature + .to_bytes() + .unwrap(), }; if !is_rk { @@ -1464,14 +1589,12 @@ impl crate::Authenticator rp_id_hash: &Bytes<32>, user_id: &Bytes<64>, ) -> Result<()> { - // Prepare to iterate over all credentials associated to RP. let rp_path = rp_rk_dir(rp_id_hash); - let mut entry = syscall!(self.trussed.read_dir_first( - Location::Internal, - rp_path, - None, - )).entry; + let mut entry = syscall!(self + .trussed + .read_dir_first(Location::Internal, rp_path, None,)) + .entry; loop { info_now!("this may be an RK: {:?}", &entry); @@ -1483,10 +1606,8 @@ impl crate::Authenticator }; info_now!("checking RK {:?} for userId ", &rk_path); - let credential_data = syscall!(self.trussed.read_file( - Location::Internal, - rk_path.clone(), - )).data; + let credential_data = + syscall!(self.trussed.read_file(Location::Internal, rk_path.clone(),)).data; let credential_maybe = Credential::deserialize(&credential_data); if let Ok(old_credential) = credential_maybe { @@ -1500,10 +1621,7 @@ impl crate::Authenticator warn_now!(":: WARNING: unexpected server credential in rk."); } } - syscall!(self.trussed.remove_file( - Location::Internal, - rk_path, - )); + syscall!(self.trussed.remove_file(Location::Internal, rk_path,)); info_now!("Overwriting previous rk tied to this userId."); break; @@ -1517,27 +1635,19 @@ impl crate::Authenticator } Ok(()) - } #[inline(never)] - pub(crate) fn delete_resident_key_by_path( - &mut self, - rk_path: &Path, - ) - -> Result<()> - { + pub(crate) fn delete_resident_key_by_path(&mut self, rk_path: &Path) -> Result<()> { info_now!("deleting RK {:?}", &rk_path); - let credential_data = syscall!(self.trussed.read_file( - Location::Internal, - PathBuf::from(rk_path), - )).data; + let credential_data = syscall!(self + .trussed + .read_file(Location::Internal, PathBuf::from(rk_path),)) + .data; let credential_maybe = Credential::deserialize(&credential_data); // info_now!("deleting credential {:?}", &credential); - if let Ok(credential) = credential_maybe { - match credential.key { credential::Key::ResidentKey(key) => { info_now!(":: deleting resident key"); @@ -1552,15 +1662,12 @@ impl crate::Authenticator } info_now!(":: deleting RK file {:?} itself", &rk_path); - syscall!(self.trussed.remove_file( - Location::Internal, - PathBuf::from(rk_path), - )); - + syscall!(self + .trussed + .remove_file(Location::Internal, PathBuf::from(rk_path),)); Ok(()) } - } fn rp_rk_dir(rp_id_hash: &Bytes<32>) -> PathBuf { @@ -1583,4 +1690,3 @@ fn rk_path(rp_id_hash: &Bytes<32>, credential_id_hash: &Bytes<32>) -> PathBuf { path } - diff --git a/src/ctap2/credential_management.rs b/src/ctap2/credential_management.rs index 9f2557d..d8bc26f 100644 --- a/src/ctap2/credential_management.rs +++ b/src/ctap2/credential_management.rs @@ -4,45 +4,35 @@ use core::convert::TryFrom; use trussed::{ syscall, - types::{ - DirEntry, - Location, - }, + types::{DirEntry, Location}, }; use ctap_types::{ - heapless_bytes::Bytes, - Error, - ctap2::credential_management::{ - CredentialProtectionPolicy, - Response, - }, cose::PublicKey, + ctap2::credential_management::{CredentialProtectionPolicy, Response}, + heapless_bytes::Bytes, webauthn::PublicKeyCredentialDescriptor, + Error, }; use littlefs2::path::{Path, PathBuf}; use crate::{ - Authenticator, - Result, - UserPresence, credential::Credential, - state::{ - CredentialManagementEnumerateRps, - CredentialManagementEnumerateCredentials, - }, - TrussedRequirements, + state::{CredentialManagementEnumerateCredentials, CredentialManagementEnumerateRps}, + Authenticator, Result, TrussedRequirements, UserPresence, }; pub(crate) struct CredentialManagement<'a, UP, T> -where UP: UserPresence, +where + UP: UserPresence, { authnr: &'a mut Authenticator, } impl core::ops::Deref for CredentialManagement<'_, UP, T> -where UP: UserPresence, +where + UP: UserPresence, { type Target = Authenticator; fn deref(&self) -> &Self::Target { @@ -51,7 +41,8 @@ where UP: UserPresence, } impl core::ops::DerefMut for CredentialManagement<'_, UP, T> -where UP: UserPresence, +where + UP: UserPresence, { fn deref_mut(&mut self) -> &mut Self::Target { self.authnr @@ -59,7 +50,8 @@ where UP: UserPresence, } impl<'a, UP, T> CredentialManagement<'a, UP, T> -where UP: UserPresence, +where + UP: UserPresence, { pub fn new(authnr: &'a mut Authenticator) -> Self { Self { authnr } @@ -67,25 +59,26 @@ where UP: UserPresence, } impl CredentialManagement<'_, UP, T> -where UP: UserPresence, - T: TrussedRequirements, +where + UP: UserPresence, + T: TrussedRequirements, { pub fn get_creds_metadata(&mut self) -> Result { info!("get metadata"); - let mut response: Response = - Default::default(); + let mut response: Response = Default::default(); - let guesstimate = self.state.persistent - .max_resident_credentials_guesstimate(); + let guesstimate = self.state.persistent.max_resident_credentials_guesstimate(); response.existing_resident_credentials_count = Some(0); - response.max_possible_remaining_residential_credentials_count = - Some(guesstimate); + response.max_possible_remaining_residential_credentials_count = Some(guesstimate); let dir = PathBuf::from(b"rk"); - let maybe_first_rp = syscall!(self.trussed.read_dir_first( - Location::Internal, dir.clone(), None)).entry; + let maybe_first_rp = + syscall!(self + .trussed + .read_dir_first(Location::Internal, dir.clone(), None)) + .entry; - let first_rp = match maybe_first_rp{ + let first_rp = match maybe_first_rp { None => return Ok(response), Some(rp) => rp, }; @@ -94,17 +87,16 @@ where UP: UserPresence, let mut last_rp = PathBuf::from(first_rp.file_name()); loop { - syscall!(self.trussed.read_dir_first( - Location::Internal, - dir.clone(), - Some(last_rp), - )).entry.unwrap(); + syscall!(self + .trussed + .read_dir_first(Location::Internal, dir.clone(), Some(last_rp),)) + .entry + .unwrap(); let maybe_next_rp = syscall!(self.trussed.read_dir_next()).entry; match maybe_next_rp { None => { - response.existing_resident_credentials_count = - Some(num_rks); + response.existing_resident_credentials_count = Some(num_rks); response.max_possible_remaining_residential_credentials_count = Some(if num_rks >= guesstimate { 0 @@ -116,8 +108,7 @@ where UP: UserPresence, Some(rp) => { last_rp = PathBuf::from(rp.file_name()); info!("counting.."); - let (this_rp_rk_count, _) = - self.count_rp_rks(PathBuf::from(rp.path()))?; + let (this_rp_rk_count, _) = self.count_rp_rks(PathBuf::from(rp.path()))?; info!("{:?}", this_rp_rk_count); num_rks += this_rp_rk_count; } @@ -136,16 +127,15 @@ where UP: UserPresence, let dir = PathBuf::from(b"rk"); - let maybe_first_rp = syscall!(self.trussed.read_dir_first( - Location::Internal, dir, None)).entry; + let maybe_first_rp = + syscall!(self.trussed.read_dir_first(Location::Internal, dir, None)).entry; response.total_rps = Some(match maybe_first_rp { None => 0, _ => { let mut num_rps = 1; loop { - let maybe_next_rp = syscall!(self.trussed.read_dir_next()) - .entry; + let maybe_next_rp = syscall!(self.trussed.read_dir_next()).entry; match maybe_next_rp { None => break, _ => num_rps += 1, @@ -156,21 +146,21 @@ where UP: UserPresence, }); if let Some(rp) = maybe_first_rp { - // load credential and extract rp and rpIdHash let maybe_first_credential = syscall!(self.trussed.read_dir_first( Location::Internal, PathBuf::from(rp.path()), None - )).entry; + )) + .entry; match maybe_first_credential { None => panic!("chaos! disorder!"), Some(rk_entry) => { - let serialized = syscall!(self.trussed.read_file( - Location::Internal, - rk_entry.path().into(), - )).data; + let serialized = syscall!(self + .trussed + .read_file(Location::Internal, rk_entry.path().into(),)) + .data; let credential = Credential::deserialize(&serialized) // this may be a confusing error message @@ -180,7 +170,6 @@ where UP: UserPresence, response.rp_id_hash = Some(self.hash(rp.id.as_ref())); response.rp = Some(rp); - } } @@ -188,11 +177,10 @@ where UP: UserPresence, if let Some(total_rps) = response.total_rps { if total_rps > 1 { let rp_id_hash = response.rp_id_hash.as_ref().unwrap().clone(); - self.state.runtime.cached_rp = Some( - CredentialManagementEnumerateRps { - remaining: total_rps - 1, - rp_id_hash, - }); + self.state.runtime.cached_rp = Some(CredentialManagementEnumerateRps { + remaining: total_rps - 1, + rp_id_hash, + }); } } } @@ -206,7 +194,12 @@ where UP: UserPresence, let CredentialManagementEnumerateRps { remaining, rp_id_hash: last_rp_id_hash, - } = self.state.runtime.cached_rp.clone().ok_or(Error::NotAllowed)?; + } = self + .state + .runtime + .cached_rp + .clone() + .ok_or(Error::NotAllowed)?; let dir = PathBuf::from(b"rk"); @@ -214,11 +207,11 @@ where UP: UserPresence, super::format_hex(&last_rp_id_hash[..8], &mut hex); let filename = PathBuf::from(&hex); - let mut maybe_next_rp = syscall!(self.trussed.read_dir_first( - Location::Internal, - dir, - Some(filename), - )).entry; + let mut maybe_next_rp = + syscall!(self + .trussed + .read_dir_first(Location::Internal, dir, Some(filename),)) + .entry; // Advance to the next if maybe_next_rp.is_some() { @@ -235,15 +228,16 @@ where UP: UserPresence, Location::Internal, PathBuf::from(rp.path()), None - )).entry; + )) + .entry; match maybe_first_credential { None => panic!("chaos! disorder!"), Some(rk_entry) => { - let serialized = syscall!(self.trussed.read_file( - Location::Internal, - rk_entry.path().into(), - )).data; + let serialized = syscall!(self + .trussed + .read_file(Location::Internal, rk_entry.path().into(),)) + .data; let credential = Credential::deserialize(&serialized) // this may be a confusing error message @@ -274,11 +268,11 @@ where UP: UserPresence, } fn count_rp_rks(&mut self, rp_dir: PathBuf) -> Result<(u32, DirEntry)> { - let maybe_first_rk = syscall!(self.trussed.read_dir_first( - Location::Internal, - rp_dir, - None - )).entry; + let maybe_first_rk = + syscall!(self + .trussed + .read_dir_first(Location::Internal, rp_dir, None)) + .entry; let first_rk = maybe_first_rk.ok_or(Error::NoCredentials)?; @@ -302,8 +296,7 @@ where UP: UserPresence, let (num_rks, first_rk) = self.count_rp_rks(rp_dir)?; // extract data required into response - let mut response = self.extract_response_from_credential_file( - first_rk.path())?; + let mut response = self.extract_response_from_credential_file(first_rk.path())?; response.total_credentials = Some(num_rks); // cache state for next call @@ -328,7 +321,12 @@ where UP: UserPresence, remaining, rp_dir, prev_filename, - } = self.state.runtime.cached_rk.clone().ok_or(Error::NotAllowed)?; + } = self + .state + .runtime + .cached_rk + .clone() + .ok_or(Error::NotAllowed)?; // let (remaining, rp_dir, prev_filename) = match self.state.runtime.cached_rk { // Some(CredentialManagementEnumerateCredentials( // x, ref y, ref z)) @@ -342,11 +340,11 @@ where UP: UserPresence, // super::format_hex(&rp_id_hash[..8], &mut hex); // let rp_dir = PathBuf::from(b"rk").join(&PathBuf::from(&hex)); - let mut maybe_next_rk = syscall!(self.trussed.read_dir_first( - Location::Internal, - rp_dir, - Some(prev_filename) - )).entry; + let mut maybe_next_rk = + syscall!(self + .trussed + .read_dir_first(Location::Internal, rp_dir, Some(prev_filename))) + .entry; // Advance to the next if maybe_next_rk.is_some() { @@ -358,8 +356,7 @@ where UP: UserPresence, match maybe_next_rk { Some(rk) => { // extract data required into response - let response = self.extract_response_from_credential_file( - rk.path())?; + let response = self.extract_response_from_credential_file(rk.path())?; // cache state for next call if remaining > 1 { @@ -376,19 +373,14 @@ where UP: UserPresence, } } - fn extract_response_from_credential_file(&mut self, rk_path: &Path) -> Result { - // user (0x06) // credentialID (0x07): PublicKeyCredentialDescriptor // publicKey (0x08): public key of the credential in COSE_Key format // totalCredentials (0x09): total number of credentials for this RP // credProtect (0x0A): credential protection policy - let serialized = syscall!(self.trussed.read_file( - Location::Internal, - rk_path.into(), - )).data; + let serialized = syscall!(self.trussed.read_file(Location::Internal, rk_path.into(),)).data; let credential = Credential::deserialize(&serialized) // this may be a confusing error message @@ -398,7 +390,10 @@ where UP: UserPresence, // why these contortions to get kek. sheesh let authnr = &mut self.authnr; - let kek = authnr.state.persistent.key_encryption_key(&mut authnr.trussed)?; + let kek = authnr + .state + .persistent + .key_encryption_key(&mut authnr.trussed)?; let credential_id = credential.id(&mut self.trussed, kek, None)?; @@ -412,33 +407,38 @@ where UP: UserPresence, use trussed::types::{KeySerialization, Mechanism}; let algorithm = SigningAlgorithm::try_from(credential.algorithm)?; - let cose_public_key = match algorithm { + let cose_public_key = match algorithm { SigningAlgorithm::P256 => { - let public_key = syscall!(self.trussed.derive_p256_public_key(private_key, Location::Volatile)).key; + let public_key = syscall!(self + .trussed + .derive_p256_public_key(private_key, Location::Volatile)) + .key; let cose_public_key = syscall!(self.trussed.serialize_key( - Mechanism::P256, public_key, + Mechanism::P256, + public_key, // KeySerialization::EcdhEsHkdf256 KeySerialization::Cose, - )).serialized_key; + )) + .serialized_key; syscall!(self.trussed.delete(public_key)); - PublicKey::P256Key( - ctap_types::serde::cbor_deserialize(&cose_public_key) - .unwrap()) + PublicKey::P256Key(ctap_types::serde::cbor_deserialize(&cose_public_key).unwrap()) } SigningAlgorithm::Ed25519 => { - let public_key = syscall!(self.trussed.derive_ed255_public_key( - private_key, Location::Volatile)).key; - let cose_public_key = syscall!(self.trussed.serialize_ed255_key( - public_key, KeySerialization::Cose - )).serialized_key; + let public_key = syscall!(self + .trussed + .derive_ed255_public_key(private_key, Location::Volatile)) + .key; + let cose_public_key = syscall!(self + .trussed + .serialize_ed255_key(public_key, KeySerialization::Cose)) + .serialized_key; syscall!(self.trussed.delete(public_key)); PublicKey::Ed25519Key( - ctap_types::serde::cbor_deserialize(&cose_public_key) - .unwrap()) - } - // SigningAlgorithm::Totp => { - // PublicKey::TotpKey(Default::default()) - // } + ctap_types::serde::cbor_deserialize(&cose_public_key).unwrap(), + ) + } // SigningAlgorithm::Totp => { + // PublicKey::TotpKey(Default::default()) + // } }; let cred_protect = match credential.cred_protect { Some(x) => Some(x), @@ -456,11 +456,10 @@ where UP: UserPresence, Ok(response) } - pub fn delete_credential(&mut self, + pub fn delete_credential( + &mut self, credential_descriptor: &PublicKeyCredentialDescriptor, - ) - -> Result - { + ) -> Result { info!("delete credential"); let credential_id_hash = self.hash(&credential_descriptor.id[..]); let mut hex = [b'0'; 16]; @@ -468,43 +467,39 @@ where UP: UserPresence, let dir = PathBuf::from(b"rk"); let filename = PathBuf::from(&hex); - let rk_path = syscall!(self.trussed.locate_file( - Location::Internal, - Some(dir), - filename, - )).path.ok_or(Error::InvalidCredential)?; - + let rk_path = syscall!(self + .trussed + .locate_file(Location::Internal, Some(dir), filename,)) + .path + .ok_or(Error::InvalidCredential)?; // DELETE self.delete_resident_key_by_path(&rk_path)?; // get rid of directory if it's now empty - let rp_path = rk_path.parent() + let rp_path = rk_path + .parent() // by construction, RK has a parent, its RP .unwrap(); - let maybe_first_remaining_rk = syscall!(self.trussed.read_dir_first( - Location::Internal, - rp_path.clone(), - None, - )).entry; + let maybe_first_remaining_rk = + syscall!(self + .trussed + .read_dir_first(Location::Internal, rp_path.clone(), None,)) + .entry; if maybe_first_remaining_rk.is_none() { - info!("deleting parent {:?} as this was its last RK", - &rp_path); - syscall!(self.trussed.remove_dir( - Location::Internal, - rp_path, - )); + info!("deleting parent {:?} as this was its last RK", &rp_path); + syscall!(self.trussed.remove_dir(Location::Internal, rp_path,)); } else { - info!("not deleting deleting parent {:?} as there is {:?}", - &rp_path, - &maybe_first_remaining_rk.unwrap().path(), - ); + info!( + "not deleting deleting parent {:?} as there is {:?}", + &rp_path, + &maybe_first_remaining_rk.unwrap().path(), + ); } // just return OK let response = Default::default(); Ok(response) } } - diff --git a/src/dispatch.rs b/src/dispatch.rs index fe09e3e..e1da024 100644 --- a/src/dispatch.rs +++ b/src/dispatch.rs @@ -3,30 +3,36 @@ mod apdu; mod ctaphid; -use crate::{Authenticator, TrussedRequirements, UserPresence}; #[allow(unused_imports)] use crate::msp; +use crate::{Authenticator, TrussedRequirements, UserPresence}; use ctap_types::{ctap1, ctap2}; use iso7816::Status; impl iso7816::App for Authenticator -where UP: UserPresence, +where + UP: UserPresence, { fn aid(&self) -> iso7816::Aid { - iso7816::Aid::new(&[ 0xA0, 0x00, 0x00, 0x06, 0x47, 0x2F, 0x00, 0x01]) + iso7816::Aid::new(&[0xA0, 0x00, 0x00, 0x06, 0x47, 0x2F, 0x00, 0x01]) } } - #[inline(never)] /// Deserialize U2F, call authenticator, serialize response *Result*. -fn handle_ctap1(authenticator: &mut Authenticator, data: &[u8], response: &mut apdu_dispatch::response::Data) -where +fn handle_ctap1( + authenticator: &mut Authenticator, + data: &[u8], + response: &mut apdu_dispatch::response::Data, +) where T: TrussedRequirements, UP: UserPresence, { - debug_now!("handle CTAP1: remaining stack: {} bytes", msp() - 0x2000_0000); + debug_now!( + "handle CTAP1: remaining stack: {} bytes", + msp() - 0x2000_0000 + ); // debug_now!("1A SP: {:X}", msp()); match try_handle_ctap1(authenticator, data, response) { Ok(()) => { @@ -34,12 +40,12 @@ where // Need to add x9000 success code (normally the apdu-dispatch does this, but // since u2f uses apdus over ctaphid, we must do it here.) response.extend_from_slice(&[0x90, 0x00]).ok(); - }, + } Err(status) => { let code: [u8; 2] = status.into(); debug_now!("CTAP1 error: {:?} ({})", status, hex_str!(&code)); response.extend_from_slice(&code).ok(); - }, + } } // debug_now!("1B SP: {:X}", msp()); debug_now!("end handle CTAP1"); @@ -47,12 +53,18 @@ where #[inline(never)] /// Deserialize CBOR, call authenticator, serialize response *Result*. -fn handle_ctap2(authenticator: &mut Authenticator, data: &[u8], response: &mut apdu_dispatch::response::Data) -where +fn handle_ctap2( + authenticator: &mut Authenticator, + data: &[u8], + response: &mut apdu_dispatch::response::Data, +) where T: TrussedRequirements, UP: UserPresence, { - debug_now!("handle CTAP2: remaining stack: {} bytes", msp() - 0x2000_0000); + debug_now!( + "handle CTAP2: remaining stack: {} bytes", + msp() - 0x2000_0000 + ); // debug_now!("2A SP: {:X}", msp()); if let Err(error) = try_handle_ctap2(authenticator, data, response) { debug_now!("CTAP2 error: {:02X}", error); @@ -63,14 +75,20 @@ where } #[inline(never)] -fn try_handle_ctap1(authenticator: &mut Authenticator, data: &[u8], response: &mut apdu_dispatch::response::Data) - -> Result<(), Status> +fn try_handle_ctap1( + authenticator: &mut Authenticator, + data: &[u8], + response: &mut apdu_dispatch::response::Data, +) -> Result<(), Status> where T: TrussedRequirements, UP: UserPresence, { // Annoyance: We can't load in fido-authenticator constructor. - authenticator.state.persistent.load_if_not_initialised(&mut authenticator.trussed); + authenticator + .state + .persistent + .load_if_not_initialised(&mut authenticator.trussed); // let command = apdu_dispatch::Command::try_from(data) // .map_err(|_| Status::IncorrectDataParameter)?; @@ -97,16 +115,25 @@ where } #[inline(never)] -fn try_handle_ctap2(authenticator: &mut Authenticator, data: &[u8], response: &mut apdu_dispatch::response::Data) - -> Result<(), u8> +fn try_handle_ctap2( + authenticator: &mut Authenticator, + data: &[u8], + response: &mut apdu_dispatch::response::Data, +) -> Result<(), u8> where T: TrussedRequirements, UP: UserPresence, { // Annoyance: We can't load in fido-authenticator constructor. - authenticator.state.persistent.load_if_not_initialised(&mut authenticator.trussed); + authenticator + .state + .persistent + .load_if_not_initialised(&mut authenticator.trussed); - debug_now!("try_handle CTAP2: remaining stack: {} bytes", msp() - 0x2000_0000); + debug_now!( + "try_handle CTAP2: remaining stack: {} bytes", + msp() - 0x2000_0000 + ); // let ctap_request = ctap2::Request::deserialize(data) // .map_err(|error| error as u8)?; @@ -120,22 +147,30 @@ where } #[inline(never)] -fn try_get_ctap2_response(authenticator: &mut Authenticator, data: &[u8]) - -> Result +fn try_get_ctap2_response( + authenticator: &mut Authenticator, + data: &[u8], +) -> Result where T: TrussedRequirements, UP: UserPresence, { // Annoyance: We can't load in fido-authenticator constructor. - authenticator.state.persistent.load_if_not_initialised(&mut authenticator.trussed); + authenticator + .state + .persistent + .load_if_not_initialised(&mut authenticator.trussed); - debug_now!("try_get CTAP2: remaining stack: {} bytes", msp() - 0x2000_0000); + debug_now!( + "try_get CTAP2: remaining stack: {} bytes", + msp() - 0x2000_0000 + ); // Goal of these nested scopes is to keep stack small. - let ctap_request = ctap2::Request::deserialize(data) - .map_err(|error| error as u8)?; + let ctap_request = ctap2::Request::deserialize(data).map_err(|error| error as u8)?; debug_now!("2a SP: {:X}", msp()); use ctap2::Authenticator; - authenticator.call_ctap2(&ctap_request) + authenticator + .call_ctap2(&ctap_request) .map_err(|error| error as u8) } diff --git a/src/dispatch/apdu.rs b/src/dispatch/apdu.rs index 9e66fc0..7d4e834 100644 --- a/src/dispatch/apdu.rs +++ b/src/dispatch/apdu.rs @@ -1,9 +1,6 @@ -use apdu_dispatch::{Command, response::Data, app as apdu}; +use apdu_dispatch::{app as apdu, response::Data, Command}; +use ctap_types::{serde::error::Error as SerdeError, Error}; use ctaphid_dispatch::app as ctaphid; -use ctap_types::{ - Error, - serde::error::Error as SerdeError, -}; use iso7816::Status; use crate::{Authenticator, TrussedRequirements, UserPresence}; @@ -16,26 +13,21 @@ pub enum CtapMappingError { impl From for Error { fn from(mapping_error: CtapMappingError) -> Error { match mapping_error { - CtapMappingError::InvalidCommand(_cmd) => { - Error::InvalidCommand - } - CtapMappingError::ParsingError(cbor_error) => { - match cbor_error { - SerdeError::SerdeMissingField => Error::MissingParameter, - _ => Error::InvalidCbor - } - } + CtapMappingError::InvalidCommand(_cmd) => Error::InvalidCommand, + CtapMappingError::ParsingError(cbor_error) => match cbor_error { + SerdeError::SerdeMissingField => Error::MissingParameter, + _ => Error::InvalidCbor, + }, } - } } -impl apdu::App<{apdu_dispatch::command::SIZE}, {apdu_dispatch::response::SIZE} > -for Authenticator - where UP: UserPresence, - T: TrussedRequirements, +impl apdu::App<{ apdu_dispatch::command::SIZE }, { apdu_dispatch::response::SIZE }> + for Authenticator +where + UP: UserPresence, + T: TrussedRequirements, { - fn select(&mut self, _: &Command, reply: &mut Data) -> apdu::Result { reply.extend_from_slice(b"U2F_V2").unwrap(); Ok(()) @@ -43,7 +35,12 @@ for Authenticator fn deselect(&mut self) {} - fn call(&mut self, interface: apdu::Interface, apdu: &Command, response: &mut Data) -> apdu::Result { + fn call( + &mut self, + interface: apdu::Interface, + apdu: &Command, + response: &mut Data, + ) -> apdu::Result { // FIDO-over-CCID does not seem to officially be a thing; we don't support it. // If we would, need to review the following cases catering to semi-documented U2F legacy. if interface != apdu::Interface::Contactless { @@ -62,7 +59,7 @@ for Authenticator Ok(match instruction { // U2F instruction codes // NB(nickray): I don't think 0x00 is a valid case. - 0x00 | 0x01 | 0x02 => super::handle_ctap1(self, apdu.data(), response),//self.call_authenticator_u2f(apdu, response), + 0x00 | 0x01 | 0x02 => super::handle_ctap1(self, apdu.data(), response), //self.call_authenticator_u2f(apdu, response), _ => { match ctaphid::Command::try_from(instruction) { @@ -79,4 +76,3 @@ for Authenticator }) } } - diff --git a/src/dispatch/ctaphid.rs b/src/dispatch/ctaphid.rs index e6943a5..fac824c 100644 --- a/src/dispatch/ctaphid.rs +++ b/src/dispatch/ctaphid.rs @@ -1,22 +1,29 @@ use ctaphid_dispatch::app as ctaphid; -use crate::{Authenticator, TrussedRequirements, UserPresence}; #[allow(unused_imports)] use crate::msp; +use crate::{Authenticator, TrussedRequirements, UserPresence}; impl ctaphid::App for Authenticator -where UP: UserPresence, - T: TrussedRequirements, +where + UP: UserPresence, + T: TrussedRequirements, { - - fn commands(&self,) -> &'static [ctaphid::Command] { - &[ ctaphid::Command::Cbor, ctaphid::Command::Msg ] + fn commands(&self) -> &'static [ctaphid::Command] { + &[ctaphid::Command::Cbor, ctaphid::Command::Msg] } #[inline(never)] - fn call(&mut self, command: ctaphid::Command, request: &ctaphid::Message, response: &mut ctaphid::Message) -> ctaphid::AppResult { - - debug_now!("ctaphid-dispatch: remaining stack: {} bytes", msp() - 0x2000_0000); + fn call( + &mut self, + command: ctaphid::Command, + request: &ctaphid::Message, + response: &mut ctaphid::Message, + ) -> ctaphid::AppResult { + debug_now!( + "ctaphid-dispatch: remaining stack: {} bytes", + msp() - 0x2000_0000 + ); if request.len() < 1 { debug_now!("invalid request length in ctaphid.call"); @@ -26,7 +33,6 @@ where UP: UserPresence, // info_now!("request: "); // blocking::dump_hex(request, request.len()); Ok(match command { - ctaphid::Command::Cbor => super::handle_ctap2(self, request, response), ctaphid::Command::Msg => super::handle_ctap1(self, request, response), _ => { diff --git a/src/lib.rs b/src/lib.rs index d549f70..8cd6ab3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,17 +15,11 @@ extern crate delog; generate_macros!(); -use trussed::{ - client, syscall, - Client as TrussedClient, - types::{ - Message, - }, -}; +use trussed::{client, syscall, types::Message, Client as TrussedClient}; use ctap_types::{ - heapless_bytes::Bytes, authenticator::{Request, Response}, + heapless_bytes::Bytes, }; /// Re-export of `ctap-types` authenticator errors. @@ -34,7 +28,7 @@ pub use ctap_types::Error; mod ctap1; mod ctap2; -#[cfg(feature="dispatch")] +#[cfg(feature = "dispatch")] mod dispatch; pub mod constants; @@ -44,7 +38,6 @@ pub mod state; /// Results with our [`Error`]. pub type Result = core::result::Result; - /// Trait bound on our implementation's requirements from a Trussed client. /// /// - Client is core Trussed client functionality. @@ -58,21 +51,20 @@ pub trait TrussedRequirements: + client::Aes256Cbc + client::Sha256 + client::HmacSha256 - + client::Ed255 - // + client::Totp -{} + + client::Ed255 // + client::Totp +{ +} -impl TrussedRequirements for T -where T: - client::Client - + client::P256 - + client::Chacha8Poly1305 - + client::Aes256Cbc - + client::Sha256 - + client::HmacSha256 - + client::Ed255 - // + client::Totp -{} +impl TrussedRequirements for T where + T: client::Client + + client::P256 + + client::Chacha8Poly1305 + + client::Aes256Cbc + + client::Sha256 + + client::HmacSha256 + + client::Ed255 // + client::Totp +{ +} #[derive(Copy, Clone, Debug, Eq, PartialEq)] /// Externally defined configuration. @@ -140,7 +132,9 @@ fn format_hex(data: &[u8], mut buffer: &mut [u8]) { #[inline] #[allow(dead_code)] -pub(crate) fn msp() -> u32 { 0x2000_0000 } +pub(crate) fn msp() -> u32 { + 0x2000_0000 +} /// Currently Ed25519 and P256. #[derive(Copy, Clone, Debug, Eq, PartialEq)] @@ -169,10 +163,14 @@ impl core::convert::TryFrom for SigningAlgorithm { /// Method to check for user presence. pub trait UserPresence: Copy { - fn user_present(self, trussed: &mut T, timeout_milliseconds: u32) -> Result<()>; + fn user_present( + self, + trussed: &mut T, + timeout_milliseconds: u32, + ) -> Result<()>; } -#[deprecated(note="use `Silent` directly`")] +#[deprecated(note = "use `Silent` directly`")] #[doc(hidden)] pub type SilentAuthenticator = Silent; @@ -181,12 +179,12 @@ pub type SilentAuthenticator = Silent; pub struct Silent {} impl UserPresence for Silent { - fn user_present(self, _: &mut T, _:u32) -> Result<()> { + fn user_present(self, _: &mut T, _: u32) -> Result<()> { Ok(()) } } -#[deprecated(note="use `Conforming` directly")] +#[deprecated(note = "use `Conforming` directly")] #[doc(hidden)] pub type NonSilentAuthenticator = Conforming; @@ -195,7 +193,11 @@ pub type NonSilentAuthenticator = Conforming; pub struct Conforming {} impl UserPresence for Conforming { - fn user_present(self, trussed: &mut T, timeout_milliseconds: u32) -> Result<()> { + fn user_present( + self, + trussed: &mut T, + timeout_milliseconds: u32, + ) -> Result<()> { let result = syscall!(trussed.confirm_user_present(timeout_milliseconds)).result; result.map_err(|err| match err { trussed::types::consent::Error::TimedOut => Error::UserActionTimeout, @@ -205,22 +207,31 @@ impl UserPresence for Conforming { } } -fn cbor_serialize_message(object: &T) -> core::result::Result { +fn cbor_serialize_message( + object: &T, +) -> core::result::Result { trussed::cbor_serialize_bytes(object) } impl Authenticator -where UP: UserPresence, - T: TrussedRequirements, +where + UP: UserPresence, + T: TrussedRequirements, { pub fn new(trussed: T, up: UP, config: Config) -> Self { - let state = state::State::new(); - Self { trussed, state, up, config } + Self { + trussed, + state, + up, + config, + } } pub fn call(&mut self, request: &Request) -> Result { - self.state.persistent.load_if_not_initialised(&mut self.trussed); + self.state + .persistent + .load_if_not_initialised(&mut self.trussed); match request { Request::Ctap2(request) => { @@ -245,5 +256,4 @@ where UP: UserPresence, } #[cfg(test)] -mod test { -} +mod test {} diff --git a/src/state.rs b/src/state.rs index e921937..69d608f 100644 --- a/src/state.rs +++ b/src/state.rs @@ -2,32 +2,24 @@ //! //! Needs cleanup. -use trussed::{ - client, syscall, try_syscall, - Client as TrussedClient, - types::{ - self, - KeyId, - Location, - Mechanism, - }, -}; use ctap_types::{ - Bytes, String, - Error, cose::EcdhEsHkdf256PublicKey as CoseEcdhEsHkdf256PublicKey, // 2022-02-27: 10 credentials sizes::MAX_CREDENTIAL_COUNT_IN_LIST, // U8 currently + Bytes, + Error, + String, +}; +use trussed::{ + client, syscall, try_syscall, + types::{self, KeyId, Location, Mechanism}, + Client as TrussedClient, }; use heapless::binary_heap::{BinaryHeap, Max}; use littlefs2::path::PathBuf; -use crate::{ - cbor_serialize_message, - credential::Credential, - Result, -}; +use crate::{cbor_serialize_message, credential::Credential, Result}; #[derive(Clone, Debug, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct CachedCredential { @@ -81,7 +73,6 @@ pub type CredentialCache = CredentialCacheGeneric; #[derive(Clone, Debug, /*uDebug, Eq, PartialEq,*/ serde::Deserialize, serde::Serialize)] pub struct State { - /// Batch device identity (aaguid, certificate, key). pub identity: Identity, pub persistent: PersistentState, @@ -95,7 +86,6 @@ impl Default for State { } impl State { - // pub fn new(trussed: &mut TrussedClient) -> Self { pub fn new() -> Self { // let identity = Identity::get(trussed); @@ -104,7 +94,11 @@ impl State { // let persistent = PersistentState::load_or_reset(trussed); let persistent = Default::default(); - Self { identity, persistent, runtime } + Self { + identity, + persistent, + runtime, + } } pub fn decrement_retries(&mut self, trussed: &mut T) -> Result<()> { @@ -119,9 +113,7 @@ impl State { Ok(()) } - pub fn pin_blocked(&self) -> Result<()> { - if self.persistent.pin_blocked() { return Err(Error::PinBlocked); } @@ -131,7 +123,6 @@ impl State { Ok(()) } - } /// Batch device identity (aaguid, certificate, key). @@ -146,16 +137,13 @@ pub type Aaguid = [u8; 16]; pub type Certificate = trussed::types::Message; impl Identity { - // Attempt to yank out the aaguid of a certificate. fn yank_aaguid(&mut self, der: &[u8]) -> Option<[u8; 16]> { - let aaguid_start_sequence = [ // OBJECT IDENTIFIER 1.3.6.1.4.1.45724.1.1.4 (AAGUID) 0x06u8, 0x0B, 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0xE5, 0x1C, 0x01, 0x01, 0x04, - // Sequence, 16 bytes - 0x04, 0x12, 0x04, 0x10 + 0x04, 0x12, 0x04, 0x10, ]; // Scan for the beginning sequence for AAGUID. @@ -180,16 +168,16 @@ impl Identity { } /// Lookup batch key and certificate, together with AAUGID. - pub fn attestation(&mut self, trussed: &mut T) -> (Option<(KeyId, Certificate)>, Aaguid) - { + pub fn attestation( + &mut self, + trussed: &mut T, + ) -> (Option<(KeyId, Certificate)>, Aaguid) { let key = crate::constants::ATTESTATION_KEY_ID; let attestation_key_exists = syscall!(trussed.exists(Mechanism::P256, key)).exists; if attestation_key_exists { - // Will panic if certificate does not exist. - let cert = syscall!(trussed.read_certificate( - crate::constants::ATTESTATION_CERT_ID - )).der; + let cert = + syscall!(trussed.read_certificate(crate::constants::ATTESTATION_CERT_ID)).der; let mut aaguid = self.yank_aaguid(cert.as_slice()); @@ -204,7 +192,6 @@ impl Identity { (None, *b"AAGUID0123456789") } } - } #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] @@ -220,7 +207,9 @@ pub struct CredentialManagementEnumerateCredentials { pub prev_filename: PathBuf, } -#[derive(Clone, Debug, /*uDebug,*/ Default, /*PartialEq,*/ serde::Deserialize, serde::Serialize)] +#[derive( + Clone, Debug, /*uDebug,*/ Default, /*PartialEq,*/ serde::Deserialize, serde::Serialize, +)] pub struct ActiveGetAssertionData { pub rp_id_hash: [u8; 32], pub client_data_hash: [u8; 32], @@ -230,7 +219,9 @@ pub struct ActiveGetAssertionData { pub extensions: Option, } -#[derive(Clone, Debug, /*uDebug,*/ Default, /*PartialEq,*/ serde::Deserialize, serde::Serialize)] +#[derive( + Clone, Debug, /*uDebug,*/ Default, /*PartialEq,*/ serde::Deserialize, serde::Serialize, +)] pub struct RuntimeState { key_agreement_key: Option, pin_token: Option, @@ -280,7 +271,6 @@ pub struct PersistentState { } impl PersistentState { - const RESET_RETRIES: u8 = 8; const FILENAME: &'static [u8] = b"persistent-state.cbor"; const MAX_RESIDENT_CREDENTIALS_GUESSTIMATE: u32 = 100; @@ -290,12 +280,10 @@ impl PersistentState { } pub fn load(trussed: &mut T) -> Result { - // TODO: add "exists_file" method instead? - let result = try_syscall!(trussed.read_file( - Location::Internal, - PathBuf::from(Self::FILENAME), - )).map_err(|_| Error::Other); + let result = + try_syscall!(trussed.read_file(Location::Internal, PathBuf::from(Self::FILENAME),)) + .map_err(|_| Error::Other); if result.is_err() { info!("err loading: {:?}", result.err().unwrap()); @@ -342,13 +330,16 @@ impl PersistentState { self.save(trussed) } - pub fn load_if_not_initialised(&mut self, trussed: &mut T) { + pub fn load_if_not_initialised( + &mut self, + trussed: &mut T, + ) { if !self.initialised { match Self::load(trussed) { Ok(previous_self) => { info!("loaded previous state!"); *self = previous_self - }, + } Err(_err) => { info!("error with previous state! {:?}", _err); } @@ -364,33 +355,47 @@ impl PersistentState { Ok(now) } - pub fn key_encryption_key(&mut self, trussed: &mut T) -> Result - { + pub fn key_encryption_key( + &mut self, + trussed: &mut T, + ) -> Result { match self.key_encryption_key { Some(key) => Ok(key), None => self.rotate_key_encryption_key(trussed), } } - pub fn rotate_key_encryption_key(&mut self, trussed: &mut T) -> Result { - if let Some(key) = self.key_encryption_key { syscall!(trussed.delete(key)); } + pub fn rotate_key_encryption_key( + &mut self, + trussed: &mut T, + ) -> Result { + if let Some(key) = self.key_encryption_key { + syscall!(trussed.delete(key)); + } let key = syscall!(trussed.generate_chacha8poly1305_key(Location::Internal)).key; self.key_encryption_key = Some(key); self.save(trussed)?; Ok(key) } - pub fn key_wrapping_key(&mut self, trussed: &mut T) -> Result - { + pub fn key_wrapping_key( + &mut self, + trussed: &mut T, + ) -> Result { match self.key_wrapping_key { Some(key) => Ok(key), None => self.rotate_key_wrapping_key(trussed), } } - pub fn rotate_key_wrapping_key(&mut self, trussed: &mut T) -> Result { + pub fn rotate_key_wrapping_key( + &mut self, + trussed: &mut T, + ) -> Result { self.load_if_not_initialised(trussed); - if let Some(key) = self.key_wrapping_key { syscall!(trussed.delete(key)); } + if let Some(key) = self.key_wrapping_key { + syscall!(trussed.delete(key)); + } let key = syscall!(trussed.generate_chacha8poly1305_key(Location::Internal)).key; self.key_wrapping_key = Some(key); self.save(trussed)?; @@ -409,7 +414,7 @@ impl PersistentState { self.consecutive_pin_mismatches >= Self::RESET_RETRIES } - fn reset_retries(&mut self, trussed: &mut T) -> Result<()> { + fn reset_retries(&mut self, trussed: &mut T) -> Result<()> { if self.consecutive_pin_mismatches > 0 { self.consecutive_pin_mismatches = 0; self.save(trussed)?; @@ -433,17 +438,18 @@ impl PersistentState { self.pin_hash } - pub fn set_pin_hash(&mut self, trussed: &mut T, pin_hash: [u8; 16]) -> Result<()> { + pub fn set_pin_hash( + &mut self, + trussed: &mut T, + pin_hash: [u8; 16], + ) -> Result<()> { self.pin_hash = Some(pin_hash); self.save(trussed)?; Ok(()) } - - } impl RuntimeState { - const POWERCYCLE_RETRIES: u8 = 3; fn decrement_retries(&mut self) -> Result<()> { @@ -461,7 +467,6 @@ impl RuntimeState { self.consecutive_pin_mismatches = 0; } - pub fn pin_blocked(&self) -> bool { self.consecutive_pin_mismatches >= Self::POWERCYCLE_RETRIES } @@ -482,13 +487,17 @@ impl RuntimeState { self.cached_credentials.push(credential); } - pub fn pop_credential(&mut self, trussed: &mut T) -> Option { + pub fn pop_credential( + &mut self, + trussed: &mut T, + ) -> Option { let cached_credential = self.cached_credentials.pop()?; let credential_data = syscall!(trussed.read_file( Location::Internal, PathBuf::from(cached_credential.path.as_str()), - )).data; + )) + .data; Credential::deserialize(&credential_data).ok() } @@ -528,13 +537,18 @@ impl RuntimeState { pub fn rotate_pin_token(&mut self, trussed: &mut T) -> KeyId { // TODO: need to rotate key agreement key? - if let Some(token) = self.pin_token { syscall!(trussed.delete(token)); } + if let Some(token) = self.pin_token { + syscall!(trussed.delete(token)); + } let token = syscall!(trussed.generate_secret_key(16, Location::Volatile)).key; self.pin_token = Some(token); token } - pub fn reset(&mut self, trussed: &mut T) { + pub fn reset( + &mut self, + trussed: &mut T, + ) { // Could use `free_credential_heap`, but since we're deleting everything here, this is quicker. syscall!(trussed.delete_all(Location::Volatile)); self.clear_credential_cache(); @@ -542,22 +556,32 @@ impl RuntimeState { self.rotate_pin_token(trussed); self.rotate_key_agreement_key(trussed); - } - pub fn generate_shared_secret(&mut self, trussed: &mut T, platform_key_agreement_key: &CoseEcdhEsHkdf256PublicKey) -> Result { + pub fn generate_shared_secret( + &mut self, + trussed: &mut T, + platform_key_agreement_key: &CoseEcdhEsHkdf256PublicKey, + ) -> Result { let private_key = self.key_agreement_key(trussed); - let serialized_pkak = cbor_serialize_message(platform_key_agreement_key).map_err(|_| Error::InvalidParameter)?; + let serialized_pkak = cbor_serialize_message(platform_key_agreement_key) + .map_err(|_| Error::InvalidParameter)?; let platform_kak = try_syscall!(trussed.deserialize_p256_key( - &serialized_pkak, types::KeySerialization::EcdhEsHkdf256, + &serialized_pkak, + types::KeySerialization::EcdhEsHkdf256, types::StorageAttributes::new().set_persistence(types::Location::Volatile) - )).map_err(|_| Error::InvalidParameter)?.key; + )) + .map_err(|_| Error::InvalidParameter)? + .key; let pre_shared_secret = syscall!(trussed.agree( - types::Mechanism::P256, private_key, platform_kak, + types::Mechanism::P256, + private_key, + platform_kak, types::StorageAttributes::new().set_persistence(types::Location::Volatile), - )).shared_secret; + )) + .shared_secret; syscall!(trussed.delete(platform_kak)); if let Some(previous_shared_secret) = self.shared_secret { @@ -565,13 +589,16 @@ impl RuntimeState { } let shared_secret = syscall!(trussed.derive_key( - types::Mechanism::Sha256, pre_shared_secret, None, types::StorageAttributes::new().set_persistence(types::Location::Volatile) - )).key; + types::Mechanism::Sha256, + pre_shared_secret, + None, + types::StorageAttributes::new().set_persistence(types::Location::Volatile) + )) + .key; self.shared_secret = Some(shared_secret); syscall!(trussed.delete(pre_shared_secret)); Ok(shared_secret) } - }