diff --git a/reproducible/reference_binaries_macos-10.15.sha256sum b/reproducible/reference_binaries_macos-10.15.sha256sum index 6d3272e..ab2e9a3 100644 --- a/reproducible/reference_binaries_macos-10.15.sha256sum +++ b/reproducible/reference_binaries_macos-10.15.sha256sum @@ -1,9 +1,9 @@ 0b54df6d548849e24d67b9b022ca09cb33c51f078ce85d0c9c4635ffc69902e1 third_party/tock/target/thumbv7em-none-eabi/release/nrf52840dk.bin -9ff63684ca08375e643f14f33dc6dc8131681bb562fb0df18f9c7f637e90cc73 target/nrf52840dk_merged.hex +f49e2205136159671f8291b284fc02300cf659f088a2ca301d74111e0e96849a target/nrf52840dk_merged.hex 052eec0ae526038352b9f7573468d0cf7fb5ec331d4dc1a2df75fdbd514ea5ca third_party/tock/target/thumbv7em-none-eabi/release/nrf52840_dongle.bin -d2373ac9df2ba8feff88f19e67ec87a58e635b94f0a0f759b6fcf4c750b256c9 target/nrf52840_dongle_merged.hex +b35ac62a490c62d4b23dddf1d8e6946badb32b5b35b40bbd75587815530094c9 target/nrf52840_dongle_merged.hex 908d7f4f40936d968b91ab6e19b2406612fe8c2c273d9c0b71ef1f55116780e0 third_party/tock/target/thumbv7em-none-eabi/release/nrf52840_dongle_dfu.bin -3c6f18ad1e1ceedeb622f39cd00ae3328ea5ad1557a9042c1b4bf831d5e1fb0d target/nrf52840_dongle_dfu_merged.hex +1adb9f71697947109020b25ad2b3fb3b03e6a07945dee14351ad67341241205e target/nrf52840_dongle_dfu_merged.hex 34ecbecaebf1188277f2310fe769c8c60310d8576493242712854deb4ba1036e third_party/tock/target/thumbv7em-none-eabi/release/nrf52840_mdk_dfu.bin -d1320adfcec35099ade04988111a947c05d14c43851fc5800d17d7a83bdba033 target/nrf52840_mdk_dfu_merged.hex -c2cbcc28b835934be4c3d3e3c5bdaba642a5811d760c1d2cb73d26b6474e4219 target/tab/ctap2.tab +1661fb4da7cbaf01529e593600f47c4613446a37f400cb0b238249d100a3d9f1 target/nrf52840_mdk_dfu_merged.hex +529ac9aef3941b45e7e480810ae4e821da433985b149028aa6a33f33e0dc1685 target/tab/ctap2.tab diff --git a/reproducible/reference_binaries_ubuntu-18.04.sha256sum b/reproducible/reference_binaries_ubuntu-18.04.sha256sum index ea64676..5e3e757 100644 --- a/reproducible/reference_binaries_ubuntu-18.04.sha256sum +++ b/reproducible/reference_binaries_ubuntu-18.04.sha256sum @@ -1,9 +1,9 @@ 29382e72d0f3c6a72ce9517211952ff29ea270193d7f0ddc48ca69009ee29925 third_party/tock/target/thumbv7em-none-eabi/release/nrf52840dk.bin -bb2fbf0d9dab2b489a49d1dc3db8923086ab109d14f1f1aa8296f086a03b75dd target/nrf52840dk_merged.hex +e446a94d67f77d5346be6e476641f4ff50561f5a77bfa8bc49262f89e7399893 target/nrf52840dk_merged.hex 30f239390ae9bef0825731e4c82d40470fc5e9bded2bf0d942e92dbb5d4faba1 third_party/tock/target/thumbv7em-none-eabi/release/nrf52840_dongle.bin -c9349bd480b30e28214bb8d58d10938889050b92d34fbeb70e3110919b3a2601 target/nrf52840_dongle_merged.hex +1bf5219f7b096b4ade330e9b02544b09d10972ddf253c7fdfbd6241b03e98f31 target/nrf52840_dongle_merged.hex e3acf15d5ae3a22aecff6cc58db5fc311f538f47328d348b7ad7db7f9ab5e72c third_party/tock/target/thumbv7em-none-eabi/release/nrf52840_dongle_dfu.bin -08f3ca1bb79e13e83149324244929b68f8d7583630d9a62a8ffdedb710c95d8b target/nrf52840_dongle_dfu_merged.hex +b83edda1b2588e3eff019fc8b2e16097e159f8a43fa5fc62a6e23497882c8dca target/nrf52840_dongle_dfu_merged.hex cae312a26a513ada6c198fdc59b2bba3860c51726b817a9fd17a4331ee12c882 third_party/tock/target/thumbv7em-none-eabi/release/nrf52840_mdk_dfu.bin -849c67c811da8d359d4e55d81d2587b3efa2f6065d72e4db09c3e571af8fef94 target/nrf52840_mdk_dfu_merged.hex -40b413a8b645b4b47fae62a4311acb12cb0c57faff2757e45c18d9e5d441e52d target/tab/ctap2.tab +d376cb19e672ab80b9dd25e9df40af7ac833d03ede32f4a2ae21fdfd4e31d365 target/nrf52840_mdk_dfu_merged.hex +ba0e11a0036f167a56864de43db3602a8a855b38be8a53afc3a97fcaa40f2201 target/tab/ctap2.tab diff --git a/reproducible/reference_elf2tab_macos-10.15.txt b/reproducible/reference_elf2tab_macos-10.15.txt index 94273d9..e701412 100644 --- a/reproducible/reference_elf2tab_macos-10.15.txt +++ b/reproducible/reference_elf2tab_macos-10.15.txt @@ -5,8 +5,8 @@ Min RAM size from sections in ELF: 16 bytes Number of writeable flash regions: 0 Adding .crt0_header section. Offset: 64 (0x40). Length: 64 (0x40) bytes. Entry point is in .text section - Adding .text section. Offset: 128 (0x80). Length: 175636 (0x2ae14) bytes. - Adding .stack section. Offset: 175764 (0x2ae94). Length: 16384 (0x4000) bytes. + Adding .text section. Offset: 128 (0x80). Length: 171876 (0x29f64) bytes. + Adding .stack section. Offset: 172004 (0x29fe4). Length: 16384 (0x4000) bytes. Searching for .rel.X sections to add. TBF Header: version: 2 0x2 @@ -24,8 +24,8 @@ Min RAM size from sections in ELF: 16 bytes Number of writeable flash regions: 0 Adding .crt0_header section. Offset: 64 (0x40). Length: 64 (0x40) bytes. Entry point is in .text section - Adding .text section. Offset: 128 (0x80). Length: 175636 (0x2ae14) bytes. - Adding .stack section. Offset: 175764 (0x2ae94). Length: 16384 (0x4000) bytes. + Adding .text section. Offset: 128 (0x80). Length: 171876 (0x29f64) bytes. + Adding .stack section. Offset: 172004 (0x29fe4). Length: 16384 (0x4000) bytes. Searching for .rel.X sections to add. TBF Header: version: 2 0x2 @@ -43,8 +43,8 @@ Min RAM size from sections in ELF: 16 bytes Number of writeable flash regions: 0 Adding .crt0_header section. Offset: 64 (0x40). Length: 64 (0x40) bytes. Entry point is in .text section - Adding .text section. Offset: 128 (0x80). Length: 175636 (0x2ae14) bytes. - Adding .stack section. Offset: 175764 (0x2ae94). Length: 16384 (0x4000) bytes. + Adding .text section. Offset: 128 (0x80). Length: 171876 (0x29f64) bytes. + Adding .stack section. Offset: 172004 (0x29fe4). Length: 16384 (0x4000) bytes. Searching for .rel.X sections to add. TBF Header: version: 2 0x2 @@ -62,8 +62,8 @@ Min RAM size from sections in ELF: 16 bytes Number of writeable flash regions: 0 Adding .crt0_header section. Offset: 64 (0x40). Length: 64 (0x40) bytes. Entry point is in .text section - Adding .text section. Offset: 128 (0x80). Length: 175636 (0x2ae14) bytes. - Adding .stack section. Offset: 175764 (0x2ae94). Length: 16384 (0x4000) bytes. + Adding .text section. Offset: 128 (0x80). Length: 171876 (0x29f64) bytes. + Adding .stack section. Offset: 172004 (0x29fe4). Length: 16384 (0x4000) bytes. Searching for .rel.X sections to add. TBF Header: version: 2 0x2 diff --git a/reproducible/reference_elf2tab_ubuntu-18.04.txt b/reproducible/reference_elf2tab_ubuntu-18.04.txt index fd00e16..4810644 100644 --- a/reproducible/reference_elf2tab_ubuntu-18.04.txt +++ b/reproducible/reference_elf2tab_ubuntu-18.04.txt @@ -5,8 +5,8 @@ Min RAM size from sections in ELF: 16 bytes Number of writeable flash regions: 0 Adding .crt0_header section. Offset: 64 (0x40). Length: 64 (0x40) bytes. Entry point is in .text section - Adding .text section. Offset: 128 (0x80). Length: 175252 (0x2ac94) bytes. - Adding .stack section. Offset: 175380 (0x2ad14). Length: 16384 (0x4000) bytes. + Adding .text section. Offset: 128 (0x80). Length: 171316 (0x29d34) bytes. + Adding .stack section. Offset: 171444 (0x29db4). Length: 16384 (0x4000) bytes. Searching for .rel.X sections to add. TBF Header: version: 2 0x2 @@ -24,8 +24,8 @@ Min RAM size from sections in ELF: 16 bytes Number of writeable flash regions: 0 Adding .crt0_header section. Offset: 64 (0x40). Length: 64 (0x40) bytes. Entry point is in .text section - Adding .text section. Offset: 128 (0x80). Length: 175252 (0x2ac94) bytes. - Adding .stack section. Offset: 175380 (0x2ad14). Length: 16384 (0x4000) bytes. + Adding .text section. Offset: 128 (0x80). Length: 171316 (0x29d34) bytes. + Adding .stack section. Offset: 171444 (0x29db4). Length: 16384 (0x4000) bytes. Searching for .rel.X sections to add. TBF Header: version: 2 0x2 @@ -43,8 +43,8 @@ Min RAM size from sections in ELF: 16 bytes Number of writeable flash regions: 0 Adding .crt0_header section. Offset: 64 (0x40). Length: 64 (0x40) bytes. Entry point is in .text section - Adding .text section. Offset: 128 (0x80). Length: 175252 (0x2ac94) bytes. - Adding .stack section. Offset: 175380 (0x2ad14). Length: 16384 (0x4000) bytes. + Adding .text section. Offset: 128 (0x80). Length: 171316 (0x29d34) bytes. + Adding .stack section. Offset: 171444 (0x29db4). Length: 16384 (0x4000) bytes. Searching for .rel.X sections to add. TBF Header: version: 2 0x2 @@ -62,8 +62,8 @@ Min RAM size from sections in ELF: 16 bytes Number of writeable flash regions: 0 Adding .crt0_header section. Offset: 64 (0x40). Length: 64 (0x40) bytes. Entry point is in .text section - Adding .text section. Offset: 128 (0x80). Length: 175252 (0x2ac94) bytes. - Adding .stack section. Offset: 175380 (0x2ad14). Length: 16384 (0x4000) bytes. + Adding .text section. Offset: 128 (0x80). Length: 171316 (0x29d34) bytes. + Adding .stack section. Offset: 171444 (0x29db4). Length: 16384 (0x4000) bytes. Searching for .rel.X sections to add. TBF Header: version: 2 0x2 diff --git a/src/ctap/mod.rs b/src/ctap/mod.rs index a52798b..7edc79b 100644 --- a/src/ctap/mod.rs +++ b/src/ctap/mod.rs @@ -38,7 +38,6 @@ use self::data_formats::{ SignatureAlgorithm, }; use self::hid::ChannelID; -use self::key_material::{AAGUID, ATTESTATION_CERTIFICATE, ATTESTATION_PRIVATE_KEY}; use self::response::{ AuthenticatorClientPinResponse, AuthenticatorGetAssertionResponse, AuthenticatorGetInfoResponse, AuthenticatorMakeCredentialResponse, ResponseData, @@ -529,7 +528,7 @@ where }; let mut auth_data = self.generate_auth_data(&rp_id_hash, flags); - auth_data.extend(AAGUID); + auth_data.extend(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); @@ -554,18 +553,25 @@ where let mut signature_data = auth_data.clone(); signature_data.extend(client_data_hash); - let (signature, x5c) = if USE_BATCH_ATTESTATION { - let attestation_key = - crypto::ecdsa::SecKey::from_bytes(ATTESTATION_PRIVATE_KEY).unwrap(); - ( - attestation_key.sign_rfc6979::(&signature_data), - Some(vec![ATTESTATION_CERTIFICATE.to_vec()]), - ) - } else { - ( + // We currently use the presence of the attestation private key in the persistent storage to + // decide whether batch attestation is needed. + let (signature, x5c) = match self.persistent_store.attestation_private_key()? { + Some(attestation_private_key) => { + let attestation_key = + crypto::ecdsa::SecKey::from_bytes(attestation_private_key).unwrap(); + let attestation_certificate = self + .persistent_store + .attestation_certificate()? + .ok_or(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR)?; + ( + attestation_key.sign_rfc6979::(&signature_data), + Some(vec![attestation_certificate]), + ) + } + None => ( sk.sign_rfc6979::(&signature_data), None, - ) + ), }; let attestation_statement = PackedAttestationStatement { alg: SignatureAlgorithm::ES256 as i64, @@ -792,7 +798,7 @@ where String::from(FIDO2_VERSION_STRING), ], extensions: Some(vec![String::from("hmac-secret")]), - aaguid: *AAGUID, + aaguid: *self.persistent_store.aaguid()?, options: Some(options_map), max_msg_size: Some(1024), pin_protocols: Some(vec![ @@ -1148,7 +1154,7 @@ mod test { 0x02, 0x81, 0x6B, 0x68, 0x6D, 0x61, 0x63, 0x2D, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x03, 0x50, ]); - expected_response.extend(AAGUID); + expected_response.extend(ctap_state.persistent_store.aaguid().unwrap()); expected_response.extend(&[ 0x04, 0xA3, 0x62, 0x72, 0x6B, 0xF5, 0x62, 0x75, 0x70, 0xF5, 0x69, 0x63, 0x6C, 0x69, 0x65, 0x6E, 0x74, 0x50, 0x69, 0x6E, 0xF4, 0x05, 0x19, 0x04, 0x00, 0x06, 0x81, 0x01, @@ -1247,7 +1253,7 @@ mod test { 0x34, 0xE2, 0x75, 0x1E, 0x68, 0x2F, 0xAB, 0x9F, 0x2D, 0x30, 0xAB, 0x13, 0xD2, 0x12, 0x55, 0x86, 0xCE, 0x19, 0x47, 0x41, 0x00, 0x00, 0x00, 0x00, ]; - expected_auth_data.extend(AAGUID); + expected_auth_data.extend(ctap_state.persistent_store.aaguid().unwrap()); expected_auth_data.extend(&[0x00, 0x20]); assert_eq!( auth_data[0..expected_auth_data.len()], @@ -1284,7 +1290,7 @@ mod test { 0x34, 0xE2, 0x75, 0x1E, 0x68, 0x2F, 0xAB, 0x9F, 0x2D, 0x30, 0xAB, 0x13, 0xD2, 0x12, 0x55, 0x86, 0xCE, 0x19, 0x47, 0x41, 0x00, 0x00, 0x00, 0x00, ]; - expected_auth_data.extend(AAGUID); + expected_auth_data.extend(ctap_state.persistent_store.aaguid().unwrap()); expected_auth_data.extend(&[0x00, ENCRYPTED_CREDENTIAL_ID_SIZE as u8]); assert_eq!( auth_data[0..expected_auth_data.len()], @@ -1427,7 +1433,7 @@ mod test { 0x34, 0xE2, 0x75, 0x1E, 0x68, 0x2F, 0xAB, 0x9F, 0x2D, 0x30, 0xAB, 0x13, 0xD2, 0x12, 0x55, 0x86, 0xCE, 0x19, 0x47, 0xC1, 0x00, 0x00, 0x00, 0x00, ]; - expected_auth_data.extend(AAGUID); + expected_auth_data.extend(ctap_state.persistent_store.aaguid().unwrap()); expected_auth_data.extend(&[0x00, 0x20]); assert_eq!( auth_data[0..expected_auth_data.len()], diff --git a/src/ctap/status_code.rs b/src/ctap/status_code.rs index 1e0c2bf..b58b8d0 100644 --- a/src/ctap/status_code.rs +++ b/src/ctap/status_code.rs @@ -67,5 +67,11 @@ pub enum Ctap2StatusCode { // CTAP2_ERR_VENDOR_FIRST = 0xF0, CTAP2_ERR_VENDOR_RESPONSE_TOO_LONG = 0xF0, CTAP2_ERR_VENDOR_RESPONSE_CANNOT_WRITE_CBOR = 0xF1, + + /// An internal invariant is broken. + /// + /// This type of error is unexpected and the current state is undefined. + CTAP2_ERR_VENDOR_INTERNAL_ERROR = 0xF2, + CTAP2_ERR_VENDOR_LAST = 0xFF, } diff --git a/src/ctap/storage.rs b/src/ctap/storage.rs index 4e33ba1..60dd99d 100644 --- a/src/ctap/storage.rs +++ b/src/ctap/storage.rs @@ -15,7 +15,7 @@ use crate::crypto::rng256::Rng256; use crate::ctap::data_formats::{CredentialProtectionPolicy, PublicKeyCredentialSource}; use crate::ctap::status_code::Ctap2StatusCode; -use crate::ctap::PIN_AUTH_LENGTH; +use crate::ctap::{key_material, PIN_AUTH_LENGTH, USE_BATCH_ATTESTATION}; use alloc::string::String; use alloc::vec::Vec; use core::convert::TryInto; @@ -56,9 +56,14 @@ const GLOBAL_SIGNATURE_COUNTER: usize = 1; const MASTER_KEYS: usize = 2; const PIN_HASH: usize = 3; const PIN_RETRIES: usize = 4; -const NUM_TAGS: usize = 5; +const ATTESTATION_PRIVATE_KEY: usize = 5; +const ATTESTATION_CERTIFICATE: usize = 6; +const AAGUID: usize = 7; +const NUM_TAGS: usize = 8; const MAX_PIN_RETRIES: u8 = 6; +const ATTESTATION_PRIVATE_KEY_LENGTH: usize = 32; +const AAGUID_LENGTH: usize = 16; #[derive(PartialEq, Eq, PartialOrd, Ord)] enum Key { @@ -73,6 +78,9 @@ enum Key { MasterKeys, PinHash, PinRetries, + AttestationPrivateKey, + AttestationCertificate, + Aaguid, } pub struct MasterKeys<'a> { @@ -124,6 +132,9 @@ impl StoreConfig for Config { MASTER_KEYS => add(Key::MasterKeys), PIN_HASH => add(Key::PinHash), PIN_RETRIES => add(Key::PinRetries), + ATTESTATION_PRIVATE_KEY => add(Key::AttestationPrivateKey), + ATTESTATION_CERTIFICATE => add(Key::AttestationCertificate), + AAGUID => add(Key::Aaguid), _ => debug_assert!(false), } } @@ -197,6 +208,20 @@ impl PersistentStore { }) .unwrap(); } + // The following 3 entries are meant to be written by vendor-specific commands. + if USE_BATCH_ATTESTATION { + if self.store.find_one(&Key::AttestationPrivateKey).is_none() { + self.set_attestation_private_key(key_material::ATTESTATION_PRIVATE_KEY) + .unwrap(); + } + if self.store.find_one(&Key::AttestationCertificate).is_none() { + self.set_attestation_certificate(key_material::ATTESTATION_CERTIFICATE) + .unwrap(); + } + } + if self.store.find_one(&Key::Aaguid).is_none() { + self.set_aaguid(key_material::AAGUID).unwrap(); + } } pub fn find_credential( @@ -395,10 +420,88 @@ impl PersistentStore { .unwrap(); } + pub fn attestation_private_key( + &self, + ) -> Result, Ctap2StatusCode> { + let data = match self.store.find_one(&Key::AttestationPrivateKey) { + None => return Ok(None), + Some((_, entry)) => entry.data, + }; + if data.len() != ATTESTATION_PRIVATE_KEY_LENGTH { + return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR); + } + Ok(Some(array_ref!(data, 0, ATTESTATION_PRIVATE_KEY_LENGTH))) + } + + pub fn set_attestation_private_key( + &mut self, + attestation_private_key: &[u8; ATTESTATION_PRIVATE_KEY_LENGTH], + ) -> Result<(), Ctap2StatusCode> { + let entry = StoreEntry { + tag: ATTESTATION_PRIVATE_KEY, + data: attestation_private_key, + sensitive: false, + }; + match self.store.find_one(&Key::AttestationPrivateKey) { + None => self.store.insert(entry)?, + Some((index, _)) => self.store.replace(index, entry)?, + } + Ok(()) + } + + pub fn attestation_certificate(&self) -> Result>, Ctap2StatusCode> { + let data = match self.store.find_one(&Key::AttestationCertificate) { + None => return Ok(None), + Some((_, entry)) => entry.data, + }; + Ok(Some(data.to_vec())) + } + + pub fn set_attestation_certificate( + &mut self, + attestation_certificate: &[u8], + ) -> Result<(), Ctap2StatusCode> { + let entry = StoreEntry { + tag: ATTESTATION_CERTIFICATE, + data: attestation_certificate, + sensitive: false, + }; + match self.store.find_one(&Key::AttestationCertificate) { + None => self.store.insert(entry)?, + Some((index, _)) => self.store.replace(index, entry)?, + } + Ok(()) + } + + pub fn aaguid(&self) -> Result<&[u8; AAGUID_LENGTH], Ctap2StatusCode> { + let (_, entry) = self + .store + .find_one(&Key::Aaguid) + .ok_or(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR)?; + let data = entry.data; + if data.len() != AAGUID_LENGTH { + return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR); + } + Ok(array_ref!(data, 0, AAGUID_LENGTH)) + } + + pub fn set_aaguid(&mut self, aaguid: &[u8; AAGUID_LENGTH]) -> Result<(), Ctap2StatusCode> { + let entry = StoreEntry { + tag: AAGUID, + data: aaguid, + sensitive: false, + }; + match self.store.find_one(&Key::Aaguid) { + None => self.store.insert(entry)?, + Some((index, _)) => self.store.replace(index, entry)?, + } + Ok(()) + } + pub fn reset(&mut self, rng: &mut impl Rng256) { loop { let index = { - let mut iter = self.store.iter(); + let mut iter = self.store.iter().filter(|(_, entry)| should_reset(entry)); match iter.next() { None => break, Some((index, _)) => index, @@ -420,6 +523,13 @@ impl From for Ctap2StatusCode { } } +fn should_reset(entry: &StoreEntry<'_>) -> bool { + match entry.tag { + ATTESTATION_PRIVATE_KEY | ATTESTATION_CERTIFICATE | AAGUID => false, + _ => true, + } +} + fn deserialize_credential(data: &[u8]) -> Option { let cbor = cbor::read(data).ok()?; cbor.try_into().ok() @@ -745,4 +855,41 @@ mod test { persistent_store.reset_pin_retries(); assert_eq!(persistent_store.pin_retries(), MAX_PIN_RETRIES); } + + #[test] + fn test_persistent_keys() { + let mut rng = ThreadRng256 {}; + let mut persistent_store = PersistentStore::new(&mut rng); + + // Make sure the attestation are absent. There is no batch attestation in tests. + assert!(persistent_store + .attestation_private_key() + .unwrap() + .is_none()); + assert!(persistent_store + .attestation_certificate() + .unwrap() + .is_none()); + + // Make sure the persistent keys are initialized. + persistent_store + .set_attestation_private_key(key_material::ATTESTATION_PRIVATE_KEY) + .unwrap(); + persistent_store + .set_attestation_certificate(key_material::ATTESTATION_CERTIFICATE) + .unwrap(); + 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); + assert_eq!( + persistent_store.attestation_private_key().unwrap().unwrap(), + key_material::ATTESTATION_PRIVATE_KEY + ); + assert_eq!( + persistent_store.attestation_certificate().unwrap().unwrap(), + key_material::ATTESTATION_CERTIFICATE + ); + assert_eq!(persistent_store.aaguid().unwrap(), key_material::AAGUID); + } }