diff --git a/src/ctap/ctap1.rs b/src/ctap/ctap1.rs index 56bec45..788d0c9 100644 --- a/src/ctap/ctap1.rs +++ b/src/ctap/ctap1.rs @@ -286,7 +286,9 @@ impl Ctap1Command { { let sk = crypto::ecdsa::SecKey::gensk(ctap_state.rng); let pk = sk.genpk(); - let key_handle = ctap_state.encrypt_key_handle(sk, &application); + let key_handle = ctap_state + .encrypt_key_handle(sk, &application) + .map_err(|_| Ctap1StatusCode::SW_VENDOR_KEY_HANDLE_TOO_LONG)?; if key_handle.len() > 0xFF { // This is just being defensive with unreachable code. return Err(Ctap1StatusCode::SW_VENDOR_KEY_HANDLE_TOO_LONG); @@ -341,14 +343,19 @@ impl Ctap1Command { R: Rng256, CheckUserPresence: Fn(ChannelID) -> Result<(), Ctap2StatusCode>, { - let credential_source = ctap_state.decrypt_credential_source(key_handle, &application); + let credential_source = ctap_state + .decrypt_credential_source(key_handle, &application) + .map_err(|_| Ctap1StatusCode::SW_WRONG_DATA)?; if let Some(credential_source) = credential_source { if flags == Ctap1Flags::CheckOnly { return Err(Ctap1StatusCode::SW_CONDITIONS_NOT_SATISFIED); } - ctap_state.increment_global_signature_counter(); + ctap_state + .increment_global_signature_counter() + .map_err(|_| Ctap1StatusCode::SW_WRONG_DATA)?; let mut signature_data = ctap_state - .generate_auth_data(&application, Ctap1Command::USER_PRESENCE_INDICATOR_BYTE); + .generate_auth_data(&application, Ctap1Command::USER_PRESENCE_INDICATOR_BYTE) + .map_err(|_| Ctap1StatusCode::SW_WRONG_DATA)?; signature_data.extend(&challenge); let signature = credential_source .private_key @@ -435,6 +442,7 @@ mod test { response[67..67 + ENCRYPTED_CREDENTIAL_ID_SIZE].to_vec(), &application ) + .unwrap() .is_some()); const CERT_START: usize = 67 + ENCRYPTED_CREDENTIAL_ID_SIZE; assert_eq!( @@ -485,7 +493,7 @@ mod test { let rp_id = "example.com"; let application = crypto::sha256::Sha256::hash(rp_id.as_bytes()); - let key_handle = ctap_state.encrypt_key_handle(sk, &application); + let key_handle = ctap_state.encrypt_key_handle(sk, &application).unwrap(); let message = create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle); let response = Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE); @@ -501,7 +509,7 @@ mod test { let rp_id = "example.com"; let application = crypto::sha256::Sha256::hash(rp_id.as_bytes()); - let key_handle = ctap_state.encrypt_key_handle(sk, &application); + let key_handle = ctap_state.encrypt_key_handle(sk, &application).unwrap(); let application = [0x55; 32]; let message = create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle); @@ -518,7 +526,7 @@ mod test { let rp_id = "example.com"; let application = crypto::sha256::Sha256::hash(rp_id.as_bytes()); - let key_handle = ctap_state.encrypt_key_handle(sk, &application); + let key_handle = ctap_state.encrypt_key_handle(sk, &application).unwrap(); let mut message = create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle); @@ -542,7 +550,7 @@ mod test { let rp_id = "example.com"; let application = crypto::sha256::Sha256::hash(rp_id.as_bytes()); - let key_handle = ctap_state.encrypt_key_handle(sk, &application); + let key_handle = ctap_state.encrypt_key_handle(sk, &application).unwrap(); let mut message = create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle); message[0] = 0xEE; @@ -560,7 +568,7 @@ mod test { let rp_id = "example.com"; let application = crypto::sha256::Sha256::hash(rp_id.as_bytes()); - let key_handle = ctap_state.encrypt_key_handle(sk, &application); + let key_handle = ctap_state.encrypt_key_handle(sk, &application).unwrap(); let mut message = create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle); message[1] = 0xEE; @@ -578,7 +586,7 @@ mod test { let rp_id = "example.com"; let application = crypto::sha256::Sha256::hash(rp_id.as_bytes()); - let key_handle = ctap_state.encrypt_key_handle(sk, &application); + let key_handle = ctap_state.encrypt_key_handle(sk, &application).unwrap(); let mut message = create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle); message[2] = 0xEE; @@ -596,7 +604,7 @@ mod test { let rp_id = "example.com"; let application = crypto::sha256::Sha256::hash(rp_id.as_bytes()); - let key_handle = ctap_state.encrypt_key_handle(sk, &application); + let key_handle = ctap_state.encrypt_key_handle(sk, &application).unwrap(); let message = create_authenticate_message(&application, Ctap1Flags::EnforceUpAndSign, &key_handle); @@ -621,7 +629,7 @@ mod test { let rp_id = "example.com"; let application = crypto::sha256::Sha256::hash(rp_id.as_bytes()); - let key_handle = ctap_state.encrypt_key_handle(sk, &application); + let key_handle = ctap_state.encrypt_key_handle(sk, &application).unwrap(); let message = create_authenticate_message( &application, Ctap1Flags::DontEnforceUpAndSign, diff --git a/src/ctap/mod.rs b/src/ctap/mod.rs index a26ad92..e7793fc 100644 --- a/src/ctap/mod.rs +++ b/src/ctap/mod.rs @@ -174,10 +174,11 @@ where } } - pub fn increment_global_signature_counter(&mut self) { + pub fn increment_global_signature_counter(&mut self) -> Result<(), Ctap2StatusCode> { if USE_SIGNATURE_COUNTER { - self.persistent_store.incr_global_signature_counter(); + self.persistent_store.incr_global_signature_counter()?; } + Ok(()) } // Encrypts the private key and relying party ID hash into a credential ID. Other @@ -188,9 +189,9 @@ where &mut self, private_key: crypto::ecdsa::SecKey, application: &[u8; 32], - ) -> Vec { - let master_keys = self.persistent_store.master_keys(); - let aes_enc_key = crypto::aes256::EncryptionKey::new(master_keys.encryption); + ) -> Result, Ctap2StatusCode> { + let master_keys = self.persistent_store.master_keys()?; + let aes_enc_key = crypto::aes256::EncryptionKey::new(master_keys.encryption()); let mut sk_bytes = [0; 32]; private_key.to_bytes(&mut sk_bytes); let mut iv = [0; 16]; @@ -208,9 +209,9 @@ where for b in &blocks { encrypted_id.extend(b); } - let id_hmac = hmac_256::(master_keys.hmac, &encrypted_id[..]); + let id_hmac = hmac_256::(master_keys.hmac(), &encrypted_id[..]); encrypted_id.extend(&id_hmac); - encrypted_id + Ok(encrypted_id) } // Decrypts a credential ID and writes the private key into a PublicKeyCredentialSource. @@ -220,20 +221,20 @@ where &self, credential_id: Vec, rp_id_hash: &[u8], - ) -> Option { + ) -> Result, Ctap2StatusCode> { if credential_id.len() != ENCRYPTED_CREDENTIAL_ID_SIZE { - return None; + return Ok(None); } - let master_keys = self.persistent_store.master_keys(); + let master_keys = self.persistent_store.master_keys()?; let payload_size = ENCRYPTED_CREDENTIAL_ID_SIZE - 32; if !verify_hmac_256::( - master_keys.hmac, + master_keys.hmac(), &credential_id[..payload_size], array_ref![credential_id, payload_size, 32], ) { - return None; + return Ok(None); } - let aes_enc_key = crypto::aes256::EncryptionKey::new(master_keys.encryption); + let aes_enc_key = crypto::aes256::EncryptionKey::new(master_keys.encryption()); let aes_dec_key = crypto::aes256::DecryptionKey::new(&aes_enc_key); let mut iv = [0; 16]; iv.copy_from_slice(&credential_id[..16]); @@ -251,11 +252,11 @@ where decrypted_rp_id_hash[16..].clone_from_slice(&blocks[3]); if rp_id_hash != decrypted_rp_id_hash { - return None; + return Ok(None); } let sk_option = crypto::ecdsa::SecKey::from_bytes(&decrypted_sk); - sk_option.map(|sk| PublicKeyCredentialSource { + Ok(sk_option.map(|sk| PublicKeyCredentialSource { key_type: PublicKeyCredentialType::PublicKey, credential_id, private_key: sk, @@ -264,7 +265,7 @@ where other_ui: None, cred_random: None, cred_protect_policy: None, - }) + })) } pub fn process_command(&mut self, command_cbor: &[u8], cid: ChannelID) -> Vec { @@ -338,7 +339,7 @@ where if let Some(auth_param) = &pin_uv_auth_param { // This case was added in FIDO 2.1. if auth_param.is_empty() { - if self.persistent_store.pin_hash().is_none() { + if self.persistent_store.pin_hash()?.is_none() { return Err(Ctap2StatusCode::CTAP2_ERR_PIN_NOT_SET); } else { return Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID); @@ -389,7 +390,7 @@ where for cred_desc in exclude_list { if self .persistent_store - .find_credential(&rp_id, &cred_desc.key_id, pin_uv_auth_param.is_none()) + .find_credential(&rp_id, &cred_desc.key_id, pin_uv_auth_param.is_none())? .is_some() { // Perform this check, so bad actors can't brute force exclude_list @@ -405,7 +406,7 @@ where let ed_flag = if has_extension_output { ED_FLAG } else { 0 }; let flags = match pin_uv_auth_param { Some(pin_auth) => { - if self.persistent_store.pin_hash().is_none() { + if self.persistent_store.pin_hash()?.is_none() { // Specification is unclear, could be CTAP2_ERR_INVALID_OPTION. return Err(Ctap2StatusCode::CTAP2_ERR_PIN_NOT_SET); } @@ -424,7 +425,7 @@ where UP_FLAG | UV_FLAG | AT_FLAG | ed_flag } None => { - if self.persistent_store.pin_hash().is_some() { + if self.persistent_store.pin_hash()?.is_some() { return Err(Ctap2StatusCode::CTAP2_ERR_PIN_REQUIRED); } if options.uv { @@ -459,11 +460,11 @@ where self.persistent_store.store_credential(credential_source)?; random_id } else { - self.encrypt_key_handle(sk.clone(), &rp_id_hash) + self.encrypt_key_handle(sk.clone(), &rp_id_hash)? }; - let mut auth_data = self.generate_auth_data(&rp_id_hash, flags); - auth_data.extend(self.persistent_store.aaguid()?); + let mut auth_data = self.generate_auth_data(&rp_id_hash, flags)?; + auth_data.append(&mut self.persistent_store.aaguid()?); // The length is fixed to 0x20 or 0x70 and fits one byte. if credential_id.len() > 0xFF { return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_RESPONSE_TOO_LONG); @@ -492,6 +493,7 @@ where // decide whether batch attestation is needed. let (signature, x5c) = match self.persistent_store.attestation_private_key()? { Some(attestation_private_key) => { + let attestation_private_key = array_ref![attestation_private_key, 0, 32]; let attestation_key = crypto::ecdsa::SecKey::from_bytes(attestation_private_key).unwrap(); let attestation_certificate = self @@ -541,7 +543,7 @@ where if let Some(auth_param) = &pin_uv_auth_param { // This case was added in FIDO 2.1. if auth_param.is_empty() { - if self.persistent_store.pin_hash().is_none() { + if self.persistent_store.pin_hash()?.is_none() { return Err(Ctap2StatusCode::CTAP2_ERR_PIN_NOT_SET); } else { return Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID); @@ -560,7 +562,7 @@ where // This case was added in FIDO 2.1. if pin_uv_auth_param == Some(vec![]) { - if self.persistent_store.pin_hash().is_none() { + if self.persistent_store.pin_hash()?.is_none() { return Err(Ctap2StatusCode::CTAP2_ERR_PIN_NOT_SET); } else { return Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID); @@ -589,7 +591,7 @@ where let has_uv = pin_uv_auth_param.is_some(); let mut flags = match pin_uv_auth_param { Some(pin_auth) => { - if self.persistent_store.pin_hash().is_none() { + if self.persistent_store.pin_hash()?.is_none() { // Specification is unclear, could be CTAP2_ERR_UNSUPPORTED_OPTION. return Err(Ctap2StatusCode::CTAP2_ERR_PIN_NOT_SET); } @@ -631,14 +633,16 @@ where &rp_id, &allowed_credential.key_id, !has_uv, - ) { + )? { Some(credential) => { found_credentials.push(credential); } None => { if decrypted_credential.is_none() { - decrypted_credential = self - .decrypt_credential_source(allowed_credential.key_id, &rp_id_hash); + decrypted_credential = self.decrypt_credential_source( + allowed_credential.key_id, + &rp_id_hash, + )?; } } } @@ -646,7 +650,7 @@ where found_credentials } else { // TODO(kaczmarczyck) use GetNextAssertion - self.persistent_store.filter_credential(&rp_id, !has_uv) + self.persistent_store.filter_credential(&rp_id, !has_uv)? }; let credential = if let Some(credential) = credentials.first() { @@ -661,9 +665,9 @@ where (self.check_user_presence)(cid)?; } - self.increment_global_signature_counter(); + self.increment_global_signature_counter()?; - let mut auth_data = self.generate_auth_data(&rp_id_hash, flags); + let mut auth_data = self.generate_auth_data(&rp_id_hash, flags)?; // Process extensions. if let Some(hmac_secret_input) = hmac_secret_input { let encrypted_output = self @@ -716,8 +720,9 @@ where options_map.insert(String::from("up"), true); options_map.insert( String::from("clientPin"), - self.persistent_store.pin_hash().is_some(), + self.persistent_store.pin_hash()?.is_some(), ); + let aaguid = self.persistent_store.aaguid()?; Ok(ResponseData::AuthenticatorGetInfo( AuthenticatorGetInfoResponse { versions: vec![ @@ -726,7 +731,7 @@ where String::from(FIDO2_VERSION_STRING), ], extensions: Some(vec![String::from("hmac-secret")]), - aaguid: *self.persistent_store.aaguid()?, + aaguid: *array_ref![aaguid, 0, 16], options: Some(options_map), max_msg_size: Some(1024), pin_protocols: Some(vec![ @@ -744,7 +749,7 @@ where algorithms: Some(vec![ES256_CRED_PARAM]), default_cred_protect: DEFAULT_CRED_PROTECT, #[cfg(feature = "with_ctap2_1")] - min_pin_length: self.persistent_store.min_pin_length(), + min_pin_length: self.persistent_store.min_pin_length()?, #[cfg(feature = "with_ctap2_1")] firmware_version: None, }, @@ -769,7 +774,7 @@ where } (self.check_user_presence)(cid)?; - self.persistent_store.reset(self.rng); + self.persistent_store.reset(self.rng)?; self.pin_protocol_v1.reset(self.rng); #[cfg(feature = "with_ctap1")] { @@ -791,7 +796,11 @@ where Err(Ctap2StatusCode::CTAP1_ERR_INVALID_COMMAND) } - pub fn generate_auth_data(&self, rp_id_hash: &[u8], flag_byte: u8) -> Vec { + pub fn generate_auth_data( + &self, + rp_id_hash: &[u8], + flag_byte: u8, + ) -> Result, Ctap2StatusCode> { let mut auth_data = vec![]; auth_data.extend(rp_id_hash); auth_data.push(flag_byte); @@ -800,10 +809,10 @@ where let mut signature_counter = [0u8; 4]; BigEndian::write_u32( &mut signature_counter, - self.persistent_store.global_signature_counter(), + self.persistent_store.global_signature_counter()?, ); auth_data.extend(&signature_counter); - auth_data + Ok(auth_data) } } @@ -1060,6 +1069,7 @@ mod test { let stored_credential = ctap_state .persistent_store .filter_credential("example.com", false) + .unwrap() .pop() .unwrap(); let credential_id = stored_credential.credential_id; @@ -1084,6 +1094,7 @@ mod test { let stored_credential = ctap_state .persistent_store .filter_credential("example.com", false) + .unwrap() .pop() .unwrap(); let credential_id = stored_credential.credential_id; @@ -1378,12 +1389,12 @@ mod test { .persistent_store .store_credential(credential_source) .is_ok()); - assert!(ctap_state.persistent_store.count_credentials() > 0); + assert!(ctap_state.persistent_store.count_credentials().unwrap() > 0); let reset_reponse = ctap_state.process_command(&[0x07], DUMMY_CHANNEL_ID); let expected_response = vec![0x00]; assert_eq!(reset_reponse, expected_response); - assert!(ctap_state.persistent_store.count_credentials() == 0); + assert!(ctap_state.persistent_store.count_credentials().unwrap() == 0); } #[test] @@ -1422,9 +1433,12 @@ mod test { // Usually, the relying party ID or its hash is provided by the client. // We are not testing the correctness of our SHA256 here, only if it is checked. let rp_id_hash = [0x55; 32]; - let encrypted_id = ctap_state.encrypt_key_handle(private_key.clone(), &rp_id_hash); + let encrypted_id = ctap_state + .encrypt_key_handle(private_key.clone(), &rp_id_hash) + .unwrap(); let decrypted_source = ctap_state .decrypt_credential_source(encrypted_id, &rp_id_hash) + .unwrap() .unwrap(); assert_eq!(private_key, decrypted_source.private_key); @@ -1439,12 +1453,15 @@ mod test { // Same as above. let rp_id_hash = [0x55; 32]; - let encrypted_id = ctap_state.encrypt_key_handle(private_key, &rp_id_hash); + let encrypted_id = ctap_state + .encrypt_key_handle(private_key, &rp_id_hash) + .unwrap(); for i in 0..encrypted_id.len() { let mut modified_id = encrypted_id.clone(); modified_id[i] ^= 0x01; assert!(ctap_state .decrypt_credential_source(modified_id, &rp_id_hash) + .unwrap() .is_none()); } } diff --git a/src/ctap/pin_protocol_v1.rs b/src/ctap/pin_protocol_v1.rs index b2c49c3..bfdb817 100644 --- a/src/ctap/pin_protocol_v1.rs +++ b/src/ctap/pin_protocol_v1.rs @@ -148,7 +148,7 @@ fn check_and_store_new_pin( .ok_or(Ctap2StatusCode::CTAP2_ERR_PIN_POLICY_VIOLATION)?; #[cfg(feature = "with_ctap2_1")] - let min_pin_length = persistent_store.min_pin_length() as usize; + let min_pin_length = persistent_store.min_pin_length()? as usize; #[cfg(not(feature = "with_ctap2_1"))] let min_pin_length = 4; if pin.len() < min_pin_length || pin.len() == PIN_PADDED_LENGTH { @@ -157,7 +157,7 @@ fn check_and_store_new_pin( } let mut pin_hash = [0u8; 16]; pin_hash.copy_from_slice(&Sha256::hash(&pin[..])[..16]); - persistent_store.set_pin_hash(&pin_hash); + persistent_store.set_pin_hash(&pin_hash)?; Ok(()) } @@ -210,16 +210,12 @@ impl PinProtocolV1 { aes_dec_key: &crypto::aes256::DecryptionKey, pin_hash_enc: Vec, ) -> Result<(), Ctap2StatusCode> { - match persistent_store.pin_hash() { + match persistent_store.pin_hash()? { Some(pin_hash) => { if self.consecutive_pin_mismatches >= 3 { return Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_BLOCKED); } - // We need to copy the pin hash, because decrementing the pin retries below mutably - // borrows from the same persistent_store reference, and therefore invalidates the - // pin_hash reference. - let pin_hash = pin_hash.clone(); - persistent_store.decr_pin_retries(); + persistent_store.decr_pin_retries()?; if pin_hash_enc.len() != PIN_AUTH_LENGTH { return Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID); } @@ -231,7 +227,7 @@ impl PinProtocolV1 { if !bool::from(pin_hash.ct_eq(&blocks[0])) { self.key_agreement_key = crypto::ecdh::SecKey::gensk(rng); - if persistent_store.pin_retries() == 0 { + if persistent_store.pin_retries()? == 0 { return Err(Ctap2StatusCode::CTAP2_ERR_PIN_BLOCKED); } self.consecutive_pin_mismatches += 1; @@ -244,7 +240,7 @@ impl PinProtocolV1 { // This status code is not explicitly mentioned in the specification. None => return Err(Ctap2StatusCode::CTAP2_ERR_PIN_REQUIRED), } - persistent_store.reset_pin_retries(); + persistent_store.reset_pin_retries()?; self.consecutive_pin_mismatches = 0; Ok(()) } @@ -276,7 +272,7 @@ impl PinProtocolV1 { Ok(AuthenticatorClientPinResponse { key_agreement: None, pin_token: None, - retries: Some(persistent_store.pin_retries() as u64), + retries: Some(persistent_store.pin_retries()? as u64), }) } @@ -296,13 +292,13 @@ impl PinProtocolV1 { pin_auth: Vec, new_pin_enc: Vec, ) -> Result<(), Ctap2StatusCode> { - if persistent_store.pin_hash().is_some() { + if persistent_store.pin_hash()?.is_some() { return Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID); } let pin_decryption_key = self.exchange_decryption_key(key_agreement, &pin_auth, &new_pin_enc)?; check_and_store_new_pin(persistent_store, &pin_decryption_key, new_pin_enc)?; - persistent_store.reset_pin_retries(); + persistent_store.reset_pin_retries()?; Ok(()) } @@ -315,7 +311,7 @@ impl PinProtocolV1 { new_pin_enc: Vec, pin_hash_enc: Vec, ) -> Result<(), Ctap2StatusCode> { - if persistent_store.pin_retries() == 0 { + if persistent_store.pin_retries()? == 0 { return Err(Ctap2StatusCode::CTAP2_ERR_PIN_BLOCKED); } let mut auth_param_data = new_pin_enc.clone(); @@ -336,7 +332,7 @@ impl PinProtocolV1 { key_agreement: CoseKey, pin_hash_enc: Vec, ) -> Result { - if persistent_store.pin_retries() == 0 { + if persistent_store.pin_retries()? == 0 { return Err(Ctap2StatusCode::CTAP2_ERR_PIN_BLOCKED); } let pk: crypto::ecdh::PubKey = CoseKey::try_into(key_agreement)?; @@ -412,7 +408,7 @@ impl PinProtocolV1 { if min_pin_length_rp_ids.is_some() { return Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_EXTENSION); } - if persistent_store.pin_hash().is_some() { + if persistent_store.pin_hash()?.is_some() { match pin_auth { Some(pin_auth) => { if self.consecutive_pin_mismatches >= 3 { @@ -438,10 +434,10 @@ impl PinProtocolV1 { None => return Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID), }; } - if min_pin_length < persistent_store.min_pin_length() { + if min_pin_length < persistent_store.min_pin_length()? { return Err(Ctap2StatusCode::CTAP2_ERR_PIN_POLICY_VIOLATION); } - persistent_store.set_min_pin_length(min_pin_length); + persistent_store.set_min_pin_length(min_pin_length)?; // TODO(kaczmarczyck) commented code is useful for the extension // https://github.com/google/OpenSK/issues/129 // if let Some(min_pin_length_rp_ids) = min_pin_length_rp_ids { @@ -650,7 +646,7 @@ mod test { pin[3] = 0x34; let mut pin_hash = [0u8; 16]; pin_hash.copy_from_slice(&Sha256::hash(&pin[..])[..16]); - persistent_store.set_pin_hash(&pin_hash); + persistent_store.set_pin_hash(&pin_hash).unwrap(); } // Fails on PINs bigger than 64 bytes. @@ -704,7 +700,7 @@ mod test { 0x01, 0xD9, 0x88, 0x40, 0x50, 0xBB, 0xD0, 0x7A, 0x23, 0x1A, 0xEB, 0x69, 0xD8, 0x36, 0xC4, 0x12, ]; - persistent_store.set_pin_hash(&pin_hash); + persistent_store.set_pin_hash(&pin_hash).unwrap(); let shared_secret = [0x88; 32]; let aes_enc_key = crypto::aes256::EncryptionKey::new(&shared_secret); let aes_dec_key = crypto::aes256::DecryptionKey::new(&aes_enc_key); @@ -782,7 +778,7 @@ mod test { let expected_response = Ok(AuthenticatorClientPinResponse { key_agreement: None, pin_token: None, - retries: Some(persistent_store.pin_retries() as u64), + retries: Some(persistent_store.pin_retries().unwrap() as u64), }); assert_eq!( pin_protocol_v1.process_get_pin_retries(&mut persistent_store), @@ -866,8 +862,8 @@ mod test { Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID) ); - while persistent_store.pin_retries() > 0 { - persistent_store.decr_pin_retries(); + while persistent_store.pin_retries().unwrap() > 0 { + persistent_store.decr_pin_retries().unwrap(); } assert_eq!( pin_protocol_v1.process_change_pin( @@ -999,7 +995,7 @@ mod test { Some(pin_auth.clone()), ); assert_eq!(response, Ok(())); - assert_eq!(persistent_store.min_pin_length(), min_pin_length); + assert_eq!(persistent_store.min_pin_length().unwrap(), min_pin_length); let response = pin_protocol_v1.process_set_min_pin_length( &mut persistent_store, 7, @@ -1010,7 +1006,7 @@ mod test { response, Err(Ctap2StatusCode::CTAP2_ERR_PIN_POLICY_VIOLATION) ); - assert_eq!(persistent_store.min_pin_length(), min_pin_length); + assert_eq!(persistent_store.min_pin_length().unwrap(), min_pin_length); } #[test] @@ -1133,16 +1129,16 @@ mod test { ), ]; for (pin, result) in test_cases { - let old_pin_hash = persistent_store.pin_hash().cloned(); + let old_pin_hash = persistent_store.pin_hash().unwrap(); let new_pin_enc = encrypt_pin(&shared_secret, pin); assert_eq!( check_and_store_new_pin(&mut persistent_store, &aes_dec_key, new_pin_enc), result ); if result.is_ok() { - assert_ne!(old_pin_hash.as_ref(), persistent_store.pin_hash()); + assert_ne!(old_pin_hash, persistent_store.pin_hash().unwrap()); } else { - assert_eq!(old_pin_hash.as_ref(), persistent_store.pin_hash()); + assert_eq!(old_pin_hash, persistent_store.pin_hash().unwrap()); } } } diff --git a/src/ctap/storage.rs b/src/ctap/storage.rs index 24456c8..03770ee 100644 --- a/src/ctap/storage.rs +++ b/src/ctap/storage.rs @@ -105,9 +105,16 @@ enum Key { MinPinLengthRpIds, } -pub struct MasterKeys<'a> { - pub encryption: &'a [u8; 32], - pub hmac: &'a [u8; 32], +pub struct MasterKeys(Vec); + +impl MasterKeys { + pub fn encryption(&self) -> &[u8; 32] { + array_ref!(&self.0, 0, 32) + } + + pub fn hmac(&self) -> &[u8; 32] { + array_ref!(&self.0, 32, 32) + } } struct Config; @@ -246,13 +253,16 @@ impl PersistentStore { rp_id: &str, credential_id: &[u8], check_cred_protect: bool, - ) -> Option { + ) -> Result, Ctap2StatusCode> { let key = Key::Credential { rp_id: Some(rp_id.into()), credential_id: Some(credential_id.into()), user_handle: None, }; - let (_, entry) = self.store.find_one(&key)?; + let entry = match self.store.find_one(&key) { + None => return Ok(None), + Some((_, entry)) => entry, + }; debug_assert_eq!(entry.tag, TAG_CREDENTIAL); let result = deserialize_credential(entry.data); debug_assert!(result.is_some()); @@ -262,9 +272,9 @@ impl PersistentStore { == Some(CredentialProtectionPolicy::UserVerificationRequired) }) { - None + Ok(None) } else { - result + Ok(result) } } @@ -278,7 +288,7 @@ impl PersistentStore { user_handle: Some(credential.user_handle.clone()), }; let old_entry = self.store.find_one(&key); - if old_entry.is_none() && self.count_credentials() >= MAX_SUPPORTED_RESIDENTIAL_KEYS { + if old_entry.is_none() && self.count_credentials()? >= MAX_SUPPORTED_RESIDENTIAL_KEYS { return Err(Ctap2StatusCode::CTAP2_ERR_KEY_STORE_FULL); } let credential = serialize_credential(credential)?; @@ -301,8 +311,9 @@ impl PersistentStore { &self, rp_id: &str, check_cred_protect: bool, - ) -> Vec { - self.store + ) -> Result, Ctap2StatusCode> { + Ok(self + .store .find_all(&Key::Credential { rp_id: Some(rp_id.into()), credential_id: None, @@ -315,163 +326,162 @@ impl PersistentStore { credential }) .filter(|cred| !check_cred_protect || cred.is_discoverable()) - .collect() + .collect()) } - pub fn count_credentials(&self) -> usize { - self.store + pub fn count_credentials(&self) -> Result { + Ok(self + .store .find_all(&Key::Credential { rp_id: None, credential_id: None, user_handle: None, }) - .count() + .count()) } - pub fn global_signature_counter(&self) -> u32 { - self.store + pub fn global_signature_counter(&self) -> Result { + Ok(self + .store .find_one(&Key::GlobalSignatureCounter) .map_or(0, |(_, entry)| { u32::from_ne_bytes(*array_ref!(entry.data, 0, 4)) - }) + })) } - pub fn incr_global_signature_counter(&mut self) { + pub fn incr_global_signature_counter(&mut self) -> Result<(), Ctap2StatusCode> { let mut buffer = [0; core::mem::size_of::()]; match self.store.find_one(&Key::GlobalSignatureCounter) { None => { buffer.copy_from_slice(&1u32.to_ne_bytes()); - self.store - .insert(StoreEntry { - tag: GLOBAL_SIGNATURE_COUNTER, - data: &buffer, - sensitive: false, - }) - .unwrap(); + self.store.insert(StoreEntry { + tag: GLOBAL_SIGNATURE_COUNTER, + data: &buffer, + sensitive: false, + })?; } Some((index, entry)) => { let value = u32::from_ne_bytes(*array_ref!(entry.data, 0, 4)); // In hopes that servers handle the wrapping gracefully. buffer.copy_from_slice(&value.wrapping_add(1).to_ne_bytes()); - self.store - .replace( - index, - StoreEntry { - tag: GLOBAL_SIGNATURE_COUNTER, - data: &buffer, - sensitive: false, - }, - ) - .unwrap(); + self.store.replace( + index, + StoreEntry { + tag: GLOBAL_SIGNATURE_COUNTER, + data: &buffer, + sensitive: false, + }, + )?; } } + Ok(()) } - pub fn master_keys(&self) -> MasterKeys { - // We have as invariant that there is always exactly one MasterKeys entry in the store. + pub fn master_keys(&self) -> Result { let (_, entry) = self.store.find_one(&Key::MasterKeys).unwrap(); - let data = entry.data; - // And this entry is well formed: the encryption key followed by the hmac key. - let encryption = array_ref!(data, 0, 32); - let hmac = array_ref!(data, 32, 32); - MasterKeys { encryption, hmac } + if entry.data.len() != 64 { + return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR); + } + Ok(MasterKeys(entry.data.to_vec())) } - pub fn pin_hash(&self) -> Option<&[u8; PIN_AUTH_LENGTH]> { - self.store - .find_one(&Key::PinHash) - .map(|(_, entry)| array_ref!(entry.data, 0, PIN_AUTH_LENGTH)) + pub fn pin_hash(&self) -> Result>, Ctap2StatusCode> { + let data = match self.store.find_one(&Key::PinHash) { + None => return Ok(None), + Some((_, entry)) => entry.data, + }; + if data.len() != PIN_AUTH_LENGTH { + return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR); + } + Ok(Some(data.to_vec())) } - pub fn set_pin_hash(&mut self, pin_hash: &[u8; PIN_AUTH_LENGTH]) { + pub fn set_pin_hash( + &mut self, + pin_hash: &[u8; PIN_AUTH_LENGTH], + ) -> Result<(), Ctap2StatusCode> { let entry = StoreEntry { tag: PIN_HASH, data: pin_hash, sensitive: true, }; - match self.store.find_one(&Key::PinHash) { - None => self.store.insert(entry).unwrap(), - Some((index, _)) => { - self.store.replace(index, entry).unwrap(); - } - } + Ok(match self.store.find_one(&Key::PinHash) { + None => self.store.insert(entry)?, + Some((index, _)) => self.store.replace(index, entry)?, + }) } - pub fn pin_retries(&self) -> u8 { - self.store + pub fn pin_retries(&self) -> Result { + Ok(self + .store .find_one(&Key::PinRetries) .map_or(MAX_PIN_RETRIES, |(_, entry)| { debug_assert_eq!(entry.data.len(), 1); entry.data[0] - }) + })) } - pub fn decr_pin_retries(&mut self) { + pub fn decr_pin_retries(&mut self) -> Result<(), Ctap2StatusCode> { match self.store.find_one(&Key::PinRetries) { None => { - self.store - .insert(StoreEntry { - tag: PIN_RETRIES, - data: &[MAX_PIN_RETRIES.saturating_sub(1)], - sensitive: false, - }) - .unwrap(); + self.store.insert(StoreEntry { + tag: PIN_RETRIES, + data: &[MAX_PIN_RETRIES.saturating_sub(1)], + sensitive: false, + })?; } Some((index, entry)) => { debug_assert_eq!(entry.data.len(), 1); if entry.data[0] == 0 { - return; + return Ok(()); } let new_value = entry.data[0].saturating_sub(1); - self.store - .replace( - index, - StoreEntry { - tag: PIN_RETRIES, - data: &[new_value], - sensitive: false, - }, - ) - .unwrap(); + self.store.replace( + index, + StoreEntry { + tag: PIN_RETRIES, + data: &[new_value], + sensitive: false, + }, + )?; } } + Ok(()) } - pub fn reset_pin_retries(&mut self) { + pub fn reset_pin_retries(&mut self) -> Result<(), Ctap2StatusCode> { if let Some((index, _)) = self.store.find_one(&Key::PinRetries) { - self.store.delete(index).unwrap(); + self.store.delete(index)?; } + Ok(()) } #[cfg(feature = "with_ctap2_1")] - pub fn min_pin_length(&self) -> u8 { - self.store + pub fn min_pin_length(&self) -> Result { + Ok(self + .store .find_one(&Key::MinPinLength) .map_or(DEFAULT_MIN_PIN_LENGTH, |(_, entry)| { debug_assert_eq!(entry.data.len(), 1); entry.data[0] - }) + })) } #[cfg(feature = "with_ctap2_1")] - pub fn set_min_pin_length(&mut self, min_pin_length: u8) { + pub fn set_min_pin_length(&mut self, min_pin_length: u8) -> Result<(), Ctap2StatusCode> { let entry = StoreEntry { tag: MIN_PIN_LENGTH, data: &[min_pin_length], sensitive: false, }; - match self.store.find_one(&Key::MinPinLength) { - None => { - self.store.insert(entry).unwrap(); - } - Some((index, _)) => { - self.store.replace(index, entry).unwrap(); - } - } + Ok(match self.store.find_one(&Key::MinPinLength) { + None => self.store.insert(entry)?, + Some((index, _)) => self.store.replace(index, entry)?, + }) } #[cfg(feature = "with_ctap2_1")] - pub fn _min_pin_length_rp_ids(&self) -> Vec { + pub fn _min_pin_length_rp_ids(&self) -> Result, Ctap2StatusCode> { let rp_ids = self .store .find_one(&Key::MinPinLengthRpIds) @@ -479,7 +489,7 @@ impl PersistentStore { _deserialize_min_pin_length_rp_ids(entry.data) }); debug_assert!(rp_ids.is_some()); - rp_ids.unwrap_or(vec![]) + Ok(rp_ids.unwrap_or(vec![])) } #[cfg(feature = "with_ctap2_1")] @@ -565,7 +575,7 @@ impl PersistentStore { Ok(()) } - pub fn aaguid(&self) -> Result<&[u8; AAGUID_LENGTH], Ctap2StatusCode> { + pub fn aaguid(&self) -> Result, Ctap2StatusCode> { let (_, entry) = self .store .find_one(&Key::Aaguid) @@ -574,7 +584,7 @@ impl PersistentStore { if data.len() != AAGUID_LENGTH { return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR); } - Ok(array_ref!(data, 0, AAGUID_LENGTH)) + Ok(data.to_vec()) } pub fn set_aaguid(&mut self, aaguid: &[u8; AAGUID_LENGTH]) -> Result<(), Ctap2StatusCode> { @@ -590,7 +600,7 @@ impl PersistentStore { Ok(()) } - pub fn reset(&mut self, rng: &mut impl Rng256) { + pub fn reset(&mut self, rng: &mut impl Rng256) -> Result<(), Ctap2StatusCode> { loop { let index = { let mut iter = self.store.iter().filter(|(_, entry)| should_reset(entry)); @@ -599,9 +609,10 @@ impl PersistentStore { Some((index, _)) => index, } }; - self.store.delete(index).unwrap(); + self.store.delete(index)?; } self.init(rng); + Ok(()) } } @@ -708,10 +719,10 @@ mod test { fn test_store() { let mut rng = ThreadRng256 {}; let mut persistent_store = PersistentStore::new(&mut rng); - assert_eq!(persistent_store.count_credentials(), 0); + assert_eq!(persistent_store.count_credentials().unwrap(), 0); let credential_source = create_credential_source(&mut rng, "example.com", vec![]); assert!(persistent_store.store_credential(credential_source).is_ok()); - assert!(persistent_store.count_credentials() > 0); + assert!(persistent_store.count_credentials().unwrap() > 0); } #[test] @@ -719,7 +730,7 @@ mod test { fn test_fill_store() { let mut rng = ThreadRng256 {}; let mut persistent_store = PersistentStore::new(&mut rng); - assert_eq!(persistent_store.count_credentials(), 0); + assert_eq!(persistent_store.count_credentials().unwrap(), 0); // To make this test work for bigger storages, implement better int -> Vec conversion. assert!(MAX_SUPPORTED_RESIDENTIAL_KEYS < 256); @@ -727,7 +738,7 @@ mod test { let credential_source = create_credential_source(&mut rng, "example.com", vec![i as u8]); assert!(persistent_store.store_credential(credential_source).is_ok()); - assert_eq!(persistent_store.count_credentials(), i + 1); + assert_eq!(persistent_store.count_credentials().unwrap(), i + 1); } let credential_source = create_credential_source( &mut rng, @@ -739,7 +750,7 @@ mod test { Err(Ctap2StatusCode::CTAP2_ERR_KEY_STORE_FULL) ); assert_eq!( - persistent_store.count_credentials(), + persistent_store.count_credentials().unwrap(), MAX_SUPPORTED_RESIDENTIAL_KEYS ); } @@ -749,7 +760,7 @@ mod test { fn test_overwrite() { let mut rng = ThreadRng256 {}; let mut persistent_store = PersistentStore::new(&mut rng); - assert_eq!(persistent_store.count_credentials(), 0); + assert_eq!(persistent_store.count_credentials().unwrap(), 0); // These should have different IDs. let credential_source0 = create_credential_source(&mut rng, "example.com", vec![0x00]); let credential_source1 = create_credential_source(&mut rng, "example.com", vec![0x00]); @@ -761,9 +772,11 @@ mod test { assert!(persistent_store .store_credential(credential_source1) .is_ok()); - assert_eq!(persistent_store.count_credentials(), 1); + assert_eq!(persistent_store.count_credentials().unwrap(), 1); assert_eq!( - &persistent_store.filter_credential("example.com", false), + &persistent_store + .filter_credential("example.com", false) + .unwrap(), &[expected_credential] ); @@ -773,7 +786,7 @@ mod test { let credential_source = create_credential_source(&mut rng, "example.com", vec![i as u8]); assert!(persistent_store.store_credential(credential_source).is_ok()); - assert_eq!(persistent_store.count_credentials(), i + 1); + assert_eq!(persistent_store.count_credentials().unwrap(), i + 1); } let credential_source = create_credential_source( &mut rng, @@ -785,7 +798,7 @@ mod test { Err(Ctap2StatusCode::CTAP2_ERR_KEY_STORE_FULL) ); assert_eq!( - persistent_store.count_credentials(), + persistent_store.count_credentials().unwrap(), MAX_SUPPORTED_RESIDENTIAL_KEYS ); } @@ -794,7 +807,7 @@ mod test { fn test_filter() { let mut rng = ThreadRng256 {}; let mut persistent_store = PersistentStore::new(&mut rng); - assert_eq!(persistent_store.count_credentials(), 0); + assert_eq!(persistent_store.count_credentials().unwrap(), 0); let credential_source0 = create_credential_source(&mut rng, "example.com", vec![0x00]); let credential_source1 = create_credential_source(&mut rng, "example.com", vec![0x01]); let credential_source2 = @@ -811,7 +824,9 @@ mod test { .store_credential(credential_source2) .is_ok()); - let filtered_credentials = persistent_store.filter_credential("example.com", false); + let filtered_credentials = persistent_store + .filter_credential("example.com", false) + .unwrap(); assert_eq!(filtered_credentials.len(), 2); assert!( (filtered_credentials[0].credential_id == id0 @@ -825,7 +840,7 @@ mod test { fn test_filter_with_cred_protect() { let mut rng = ThreadRng256 {}; let mut persistent_store = PersistentStore::new(&mut rng); - assert_eq!(persistent_store.count_credentials(), 0); + assert_eq!(persistent_store.count_credentials().unwrap(), 0); let private_key = crypto::ecdsa::SecKey::gensk(&mut rng); let credential = PublicKeyCredentialSource { key_type: PublicKeyCredentialType::PublicKey, @@ -841,7 +856,9 @@ mod test { }; assert!(persistent_store.store_credential(credential).is_ok()); - let no_credential = persistent_store.filter_credential("example.com", true); + let no_credential = persistent_store + .filter_credential("example.com", true) + .unwrap(); assert_eq!(no_credential, vec![]); } @@ -849,7 +866,7 @@ mod test { fn test_find() { let mut rng = ThreadRng256 {}; let mut persistent_store = PersistentStore::new(&mut rng); - assert_eq!(persistent_store.count_credentials(), 0); + assert_eq!(persistent_store.count_credentials().unwrap(), 0); let credential_source0 = create_credential_source(&mut rng, "example.com", vec![0x00]); let credential_source1 = create_credential_source(&mut rng, "example.com", vec![0x01]); let id0 = credential_source0.credential_id.clone(); @@ -861,9 +878,13 @@ mod test { .store_credential(credential_source1) .is_ok()); - let no_credential = persistent_store.find_credential("another.example.com", &id0, false); + let no_credential = persistent_store + .find_credential("another.example.com", &id0, false) + .unwrap(); assert_eq!(no_credential, None); - let found_credential = persistent_store.find_credential("example.com", &id0, false); + let found_credential = persistent_store + .find_credential("example.com", &id0, false) + .unwrap(); let expected_credential = PublicKeyCredentialSource { key_type: PublicKeyCredentialType::PublicKey, credential_id: id0, @@ -881,7 +902,7 @@ mod test { fn test_find_with_cred_protect() { let mut rng = ThreadRng256 {}; let mut persistent_store = PersistentStore::new(&mut rng); - assert_eq!(persistent_store.count_credentials(), 0); + assert_eq!(persistent_store.count_credentials().unwrap(), 0); let private_key = crypto::ecdsa::SecKey::gensk(&mut rng); let credential = PublicKeyCredentialSource { key_type: PublicKeyCredentialType::PublicKey, @@ -895,7 +916,9 @@ mod test { }; assert!(persistent_store.store_credential(credential).is_ok()); - let no_credential = persistent_store.find_credential("example.com", &vec![0x00], true); + let no_credential = persistent_store + .find_credential("example.com", &vec![0x00], true) + .unwrap(); assert_eq!(no_credential, None); } @@ -905,19 +928,19 @@ mod test { let mut persistent_store = PersistentStore::new(&mut rng); // Master keys stay the same between resets. - let master_keys_1 = persistent_store.master_keys(); - let master_keys_2 = persistent_store.master_keys(); - assert_eq!(master_keys_2.encryption, master_keys_1.encryption); - assert_eq!(master_keys_2.hmac, master_keys_1.hmac); + let master_keys_1 = persistent_store.master_keys().unwrap(); + let master_keys_2 = persistent_store.master_keys().unwrap(); + assert_eq!(master_keys_2.encryption(), master_keys_1.encryption()); + assert_eq!(master_keys_2.hmac(), master_keys_1.hmac()); // Master keys change after reset. This test may fail if the random generator produces the // same keys. - let master_encryption_key = master_keys_1.encryption.to_vec(); - let master_hmac_key = master_keys_1.hmac.to_vec(); - persistent_store.reset(&mut rng); - let master_keys_3 = persistent_store.master_keys(); - assert!(master_keys_3.encryption as &[u8] != &master_encryption_key[..]); - assert!(master_keys_3.hmac as &[u8] != &master_hmac_key[..]); + let master_encryption_key = master_keys_1.encryption().to_vec(); + let master_hmac_key = master_keys_1.hmac().to_vec(); + persistent_store.reset(&mut rng).unwrap(); + let master_keys_3 = persistent_store.master_keys().unwrap(); + assert!(master_keys_3.encryption() != &master_encryption_key[..]); + assert!(master_keys_3.hmac() != &master_hmac_key[..]); } #[test] @@ -926,23 +949,23 @@ mod test { let mut persistent_store = PersistentStore::new(&mut rng); // Pin hash is initially not set. - assert!(persistent_store.pin_hash().is_none()); + assert!(persistent_store.pin_hash().unwrap().is_none()); // Setting the pin hash sets the pin hash. let random_data = rng.gen_uniform_u8x32(); assert_eq!(random_data.len(), 2 * PIN_AUTH_LENGTH); let pin_hash_1 = array_ref!(random_data, 0, PIN_AUTH_LENGTH); let pin_hash_2 = array_ref!(random_data, PIN_AUTH_LENGTH, PIN_AUTH_LENGTH); - persistent_store.set_pin_hash(&pin_hash_1); - assert_eq!(persistent_store.pin_hash(), Some(pin_hash_1)); - assert_eq!(persistent_store.pin_hash(), Some(pin_hash_1)); - persistent_store.set_pin_hash(&pin_hash_2); - assert_eq!(persistent_store.pin_hash(), Some(pin_hash_2)); - assert_eq!(persistent_store.pin_hash(), Some(pin_hash_2)); + persistent_store.set_pin_hash(&pin_hash_1).unwrap(); + assert_eq!(persistent_store.pin_hash().unwrap().unwrap(), pin_hash_1); + assert_eq!(persistent_store.pin_hash().unwrap().unwrap(), pin_hash_1); + persistent_store.set_pin_hash(&pin_hash_2).unwrap(); + assert_eq!(persistent_store.pin_hash().unwrap().unwrap(), pin_hash_2); + assert_eq!(persistent_store.pin_hash().unwrap().unwrap(), pin_hash_2); // Resetting the storage resets the pin hash. - persistent_store.reset(&mut rng); - assert!(persistent_store.pin_hash().is_none()); + persistent_store.reset(&mut rng).unwrap(); + assert!(persistent_store.pin_hash().unwrap().is_none()); } #[test] @@ -951,21 +974,21 @@ mod test { let mut persistent_store = PersistentStore::new(&mut rng); // The pin retries is initially at the maximum. - assert_eq!(persistent_store.pin_retries(), MAX_PIN_RETRIES); + assert_eq!(persistent_store.pin_retries().unwrap(), MAX_PIN_RETRIES); // Decrementing the pin retries decrements the pin retries. for pin_retries in (0..MAX_PIN_RETRIES).rev() { - persistent_store.decr_pin_retries(); - assert_eq!(persistent_store.pin_retries(), pin_retries); + persistent_store.decr_pin_retries().unwrap(); + assert_eq!(persistent_store.pin_retries().unwrap(), pin_retries); } // Decrementing the pin retries after zero does not modify the pin retries. - persistent_store.decr_pin_retries(); - assert_eq!(persistent_store.pin_retries(), 0); + persistent_store.decr_pin_retries().unwrap(); + assert_eq!(persistent_store.pin_retries().unwrap(), 0); // Resetting the pin retries resets the pin retries. - persistent_store.reset_pin_retries(); - assert_eq!(persistent_store.pin_retries(), MAX_PIN_RETRIES); + persistent_store.reset_pin_retries().unwrap(); + assert_eq!(persistent_store.pin_retries().unwrap(), MAX_PIN_RETRIES); } #[test] @@ -993,7 +1016,7 @@ mod test { assert_eq!(persistent_store.aaguid().unwrap(), key_material::AAGUID); // The persistent keys stay initialized and preserve their value after a reset. - persistent_store.reset(&mut rng); + persistent_store.reset(&mut rng).unwrap(); assert_eq!( persistent_store.attestation_private_key().unwrap().unwrap(), key_material::ATTESTATION_PRIVATE_KEY @@ -1012,12 +1035,20 @@ mod test { let mut persistent_store = PersistentStore::new(&mut rng); // The minimum PIN length is initially at the default. - assert_eq!(persistent_store.min_pin_length(), DEFAULT_MIN_PIN_LENGTH); + assert_eq!( + persistent_store.min_pin_length().unwrap(), + DEFAULT_MIN_PIN_LENGTH + ); // Changes by the setter are reflected by the getter.. let new_min_pin_length = 8; - persistent_store.set_min_pin_length(new_min_pin_length); - assert_eq!(persistent_store.min_pin_length(), new_min_pin_length); + persistent_store + .set_min_pin_length(new_min_pin_length) + .unwrap(); + assert_eq!( + persistent_store.min_pin_length().unwrap(), + new_min_pin_length + ); } #[cfg(feature = "with_ctap2_1")] @@ -1028,7 +1059,7 @@ mod test { // The minimum PIN length RP IDs are initially at the default. assert_eq!( - persistent_store._min_pin_length_rp_ids(), + persistent_store._min_pin_length_rp_ids().unwrap(), _DEFAULT_MIN_PIN_LENGTH_RP_IDS ); @@ -1043,7 +1074,7 @@ mod test { rp_ids.push(rp_id); } } - assert_eq!(persistent_store._min_pin_length_rp_ids(), rp_ids); + assert_eq!(persistent_store._min_pin_length_rp_ids().unwrap(), rp_ids); } #[test]