From 80a6b82ed70f31322f9d68501197b6c6c1f16b9d Mon Sep 17 00:00:00 2001 From: Julien Cretin Date: Tue, 5 Jul 2022 16:11:56 +0200 Subject: [PATCH 1/6] Abstract attestation support --- src/api/attestation_store.rs | 110 +++++++++++++++++++++++++++++++++++ src/api/mod.rs | 1 + src/ctap/ctap1.rs | 40 ++++++------- src/ctap/mod.rs | 106 ++++++++++++++++----------------- src/ctap/status_code.rs | 13 ++++- src/ctap/storage.rs | 101 ++++++++------------------------ src/ctap/storage/key.rs | 7 +-- src/env/mod.rs | 3 + src/env/test/mod.rs | 8 ++- src/env/tock/mod.rs | 8 ++- 10 files changed, 232 insertions(+), 165 deletions(-) create mode 100644 src/api/attestation_store.rs diff --git a/src/api/attestation_store.rs b/src/api/attestation_store.rs new file mode 100644 index 0000000..7e6900e --- /dev/null +++ b/src/api/attestation_store.rs @@ -0,0 +1,110 @@ +use alloc::string::String; +use alloc::vec::Vec; +use persistent_store::{StoreError, StoreUpdate}; + +use crate::env::Env; + +/// Identifies an attestation. +pub enum Id { + Batch, + Enterprise { rp_id: String }, +} + +#[cfg_attr(feature = "std", derive(Debug, PartialEq, Eq))] +pub struct Attestation { + /// ECDSA private key (big-endian). + pub private_key: [u8; 32], + pub certificate: Vec, +} + +/// Stores enterprise or batch attestations. +/// +/// Implementations don't need to distinguish different attestations. In particular, setting one +/// attestation may set other ones. +pub trait AttestationStore { + /// Returns an attestation given its id, if it exists. + /// + /// This should always return the attestation. Checking whether it is ok to use the attestation + /// is done in the CTAP library. + fn get(&mut self, id: &Id) -> Result, Error>; + + /// Sets the attestation for a given id. + /// + /// This function may not be supported. + fn set(&mut self, id: &Id, attestation: Option<&Attestation>) -> Result<(), Error>; +} + +/// Attestation store errors. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[non_exhaustive] +pub enum Error { + Storage, + Internal, + NoSupport, +} + +/// Keys of the environment store reserved for the attestation store. +pub const STORAGE_KEYS: &[usize] = &[1, 2]; + +/// Implements a default attestation store using the environment store. +/// +/// The same attestation is used for batch and enterprise. +pub trait Helper: Env {} + +impl AttestationStore for T { + fn get(&mut self, _: &Id) -> Result, Error> { + let private_key = self.store().find(PRIVATE_KEY_STORAGE_KEY)?; + let certificate = self.store().find(CERTIFICATE_STORAGE_KEY)?; + let (private_key, certificate) = match (private_key, certificate) { + (Some(x), Some(y)) => (x, y), + (None, None) => return Ok(None), + _ => return Err(Error::Internal), + }; + if private_key.len() != 32 { + return Err(Error::Internal); + } + Ok(Some(Attestation { + private_key: *array_ref![private_key, 0, 32], + certificate, + })) + } + + fn set(&mut self, _: &Id, attestation: Option<&Attestation>) -> Result<(), Error> { + let updates = match attestation { + None => [ + StoreUpdate::Remove { + key: PRIVATE_KEY_STORAGE_KEY, + }, + StoreUpdate::Remove { + key: CERTIFICATE_STORAGE_KEY, + }, + ], + Some(attestation) => [ + StoreUpdate::Insert { + key: PRIVATE_KEY_STORAGE_KEY, + value: &attestation.private_key[..], + }, + StoreUpdate::Insert { + key: CERTIFICATE_STORAGE_KEY, + value: &attestation.certificate[..], + }, + ], + }; + Ok(self.store().transaction(&updates)?) + } +} + +const PRIVATE_KEY_STORAGE_KEY: usize = STORAGE_KEYS[0]; +const CERTIFICATE_STORAGE_KEY: usize = STORAGE_KEYS[1]; + +impl From for Error { + fn from(error: StoreError) -> Self { + match error { + StoreError::InvalidArgument + | StoreError::NoCapacity + | StoreError::NoLifetime + | StoreError::InvalidStorage => Error::Internal, + StoreError::StorageError => Error::Storage, + } + } +} diff --git a/src/api/mod.rs b/src/api/mod.rs index 27f2cdb..1239c11 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -17,6 +17,7 @@ //! The [environment](crate::env::Env) is split into components. Each component has an API described //! by a trait. This module gathers the API of those components. +pub mod attestation_store; pub mod connection; pub mod customization; pub mod firmware_protection; diff --git a/src/ctap/ctap1.rs b/src/ctap/ctap1.rs index 00def87..60a8248 100644 --- a/src/ctap/ctap1.rs +++ b/src/ctap/ctap1.rs @@ -16,7 +16,7 @@ use super::super::clock::CtapInstant; use super::apdu::{Apdu, ApduStatusCode}; use super::crypto_wrapper::{decrypt_credential_source, encrypt_key_handle, PrivateKey}; use super::CtapState; -use crate::ctap::storage; +use crate::api::attestation_store::{self, Attestation, AttestationStore}; use crate::env::Env; use alloc::vec::Vec; use arrayref::array_ref; @@ -257,12 +257,14 @@ impl Ctap1Command { return Err(Ctap1StatusCode::SW_INTERNAL_EXCEPTION); } - let certificate = storage::attestation_certificate(env) + let Attestation { + private_key, + certificate, + } = env + .attestation_store() + .get(&attestation_store::Id::Batch) .map_err(|_| Ctap1StatusCode::SW_MEMERR)? .ok_or(Ctap1StatusCode::SW_INTERNAL_EXCEPTION)?; - let private_key = storage::attestation_private_key(env) - .map_err(|_| Ctap1StatusCode::SW_INTERNAL_EXCEPTION)? - .ok_or(Ctap1StatusCode::SW_INTERNAL_EXCEPTION)?; let mut response = Vec::with_capacity(105 + key_handle.len() + certificate.len()); response.push(Ctap1Command::LEGACY_BYTE); @@ -345,10 +347,10 @@ impl Ctap1Command { mod test { use super::super::crypto_wrapper::ECDSA_CREDENTIAL_ID_SIZE; use super::super::data_formats::SignatureAlgorithm; - use super::super::key_material; use super::*; use crate::api::customization::Customization; use crate::clock::TEST_CLOCK_FREQUENCY_HZ; + use crate::ctap::storage; use crate::env::test::TestEnv; use crypto::Hash256; @@ -423,21 +425,13 @@ mod test { // Certificate and private key are missing assert_eq!(response, Err(Ctap1StatusCode::SW_INTERNAL_EXCEPTION)); - let fake_key = [0x41u8; key_material::ATTESTATION_PRIVATE_KEY_LENGTH]; - assert!(storage::set_attestation_private_key(&mut env, &fake_key).is_ok()); - ctap_state.u2f_up_state.consume_up(CtapInstant::new(0)); - ctap_state.u2f_up_state.grant_up(CtapInstant::new(0)); - let response = Ctap1Command::process_command( - &mut env, - &message, - &mut ctap_state, - CtapInstant::new(0_u64), - ); - // Certificate is still missing - assert_eq!(response, Err(Ctap1StatusCode::SW_INTERNAL_EXCEPTION)); - - let fake_cert = [0x99u8; 100]; // Arbitrary length - assert!(storage::set_attestation_certificate(&mut env, &fake_cert[..]).is_ok()); + let attestation = Attestation { + private_key: [0x41; 32], + certificate: vec![0x99; 100], + }; + env.attestation_store() + .set(&attestation_store::Id::Batch, Some(&attestation)) + .unwrap(); ctap_state.u2f_up_state.consume_up(CtapInstant::new(0)); ctap_state.u2f_up_state.grant_up(CtapInstant::new(0)); let response = @@ -454,8 +448,8 @@ mod test { .is_some()); const CERT_START: usize = 67 + ECDSA_CREDENTIAL_ID_SIZE; assert_eq!( - &response[CERT_START..CERT_START + fake_cert.len()], - &fake_cert[..] + &response[CERT_START..][..attestation.certificate.len()], + &attestation.certificate ); } diff --git a/src/ctap/mod.rs b/src/ctap/mod.rs index 52a410f..3d94a8e 100644 --- a/src/ctap/mod.rs +++ b/src/ctap/mod.rs @@ -62,6 +62,7 @@ use self::status_code::Ctap2StatusCode; use self::timed_permission::TimedPermission; #[cfg(feature = "with_ctap1")] use self::timed_permission::U2fUserPresenceState; +use crate::api::attestation_store::{self, Attestation, AttestationStore}; use crate::api::connection::{HidConnection, SendOrRecvStatus}; use crate::api::customization::Customization; use crate::api::firmware_protection::FirmwareProtection; @@ -860,7 +861,7 @@ impl CtapState { key_type: PublicKeyCredentialType::PublicKey, credential_id: random_id.clone(), private_key: private_key.clone(), - rp_id, + rp_id: rp_id.clone(), user_handle: user.user_id, // This input is user provided, so we crop it to 64 byte for storage. // The UTF8 encoding is always preserved, so the string might end up shorter. @@ -918,21 +919,31 @@ impl CtapState { let mut signature_data = auth_data.clone(); signature_data.extend(client_data_hash); - let (signature, x5c) = if env.customization().use_batch_attestation() || ep_att { - let attestation_private_key = storage::attestation_private_key(env)? - .ok_or(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR)?; - let attestation_key = - crypto::ecdsa::SecKey::from_bytes(&attestation_private_key).unwrap(); - let attestation_certificate = storage::attestation_certificate(env)? - .ok_or(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR)?; - ( - attestation_key - .sign_rfc6979::(&signature_data) - .to_asn1_der(), - Some(vec![attestation_certificate]), - ) + let attestation_id = if env.customization().use_batch_attestation() { + Some(attestation_store::Id::Batch) + } else if ep_att { + Some(attestation_store::Id::Enterprise { rp_id }) } else { - (private_key.sign_and_encode(env, &signature_data)?, None) + None + }; + let (signature, x5c) = match attestation_id { + Some(id) => { + let Attestation { + private_key, + certificate, + } = env + .attestation_store() + .get(&id)? + .ok_or(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR)?; + let attestation_key = crypto::ecdsa::SecKey::from_bytes(&private_key).unwrap(); + ( + attestation_key + .sign_rfc6979::(&signature_data) + .to_asn1_der(), + Some(vec![certificate]), + ) + } + None => (private_key.sign_and_encode(env, &signature_data)?, None), }; let attestation_statement = PackedAttestationStatement { alg: SignatureAlgorithm::ES256 as i64, @@ -1338,41 +1349,26 @@ impl CtapState { if params.attestation_material.is_some() || params.lockdown { check_user_presence(env, channel)?; } + // This command is for U2F support and we use the batch attestation there. + let attestation_id = attestation_store::Id::Batch; // Sanity checks - let current_priv_key = storage::attestation_private_key(env)?; - let current_cert = storage::attestation_certificate(env)?; - + let current_attestation = env.attestation_store().get(&attestation_id)?; let response = match params.attestation_material { - // Only reading values. None => AuthenticatorVendorConfigureResponse { - cert_programmed: current_cert.is_some(), - pkey_programmed: current_priv_key.is_some(), + cert_programmed: current_attestation.is_some(), + pkey_programmed: current_attestation.is_some(), }, - // Device is already fully programmed. We don't leak information. - Some(_) if current_cert.is_some() && current_priv_key.is_some() => { - AuthenticatorVendorConfigureResponse { - cert_programmed: true, - pkey_programmed: true, - } - } - // Device is partially or not programmed. We complete the process. Some(data) => { - if let Some(current_cert) = ¤t_cert { - if current_cert != &data.certificate { - return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER); - } - } - if let Some(current_priv_key) = ¤t_priv_key { - if current_priv_key != &data.private_key { - return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER); - } - } - if current_cert.is_none() { - storage::set_attestation_certificate(env, &data.certificate)?; - } - if current_priv_key.is_none() { - storage::set_attestation_private_key(env, &data.private_key)?; + // We don't overwrite the attestation if it's already set. We don't return any error + // to not leak information. + if current_attestation.is_none() { + let attestation = Attestation { + private_key: data.private_key, + certificate: data.certificate, + }; + env.attestation_store() + .set(&attestation_id, Some(&attestation))?; } AuthenticatorVendorConfigureResponse { cert_programmed: true, @@ -3145,12 +3141,11 @@ mod test { )) ); assert_eq!( - storage::attestation_certificate(&mut env).unwrap().unwrap(), - dummy_cert - ); - assert_eq!( - storage::attestation_private_key(&mut env).unwrap().unwrap(), - dummy_key + env.attestation_store().get(&attestation_store::Id::Batch), + Ok(Some(Attestation { + private_key: dummy_key, + certificate: dummy_cert.to_vec(), + })) ); // Try to inject other dummy values and check that initial values are retained. @@ -3176,12 +3171,11 @@ mod test { )) ); assert_eq!( - storage::attestation_certificate(&mut env).unwrap().unwrap(), - dummy_cert - ); - assert_eq!( - storage::attestation_private_key(&mut env).unwrap().unwrap(), - dummy_key + env.attestation_store().get(&attestation_store::Id::Batch), + Ok(Some(Attestation { + private_key: dummy_key, + certificate: dummy_cert.to_vec(), + })) ); // Now try to lock the device diff --git a/src/ctap/status_code.rs b/src/ctap/status_code.rs index c6c93c0..58d02a4 100644 --- a/src/ctap/status_code.rs +++ b/src/ctap/status_code.rs @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::api::key_store; use crate::api::user_presence::UserPresenceError; +use crate::api::{attestation_store, key_store}; // CTAP specification (version 20190130) section 6.3 // For now, only the CTAP2 codes are here, the CTAP1 are not included. @@ -100,3 +100,14 @@ impl From for Ctap2StatusCode { Self::CTAP2_ERR_VENDOR_INTERNAL_ERROR } } + +impl From for Ctap2StatusCode { + fn from(error: attestation_store::Error) -> Self { + use attestation_store::Error; + match error { + Error::Storage => Self::CTAP2_ERR_VENDOR_HARDWARE_FAILURE, + Error::Internal => Self::CTAP2_ERR_VENDOR_INTERNAL_ERROR, + Error::NoSupport => Self::CTAP2_ERR_VENDOR_INTERNAL_ERROR, + } + } +} diff --git a/src/ctap/storage.rs b/src/ctap/storage.rs index 4141003..029f0b2 100644 --- a/src/ctap/storage.rs +++ b/src/ctap/storage.rs @@ -437,58 +437,6 @@ pub fn commit_large_blob_array( )?) } -/// Returns the attestation private key if defined. -pub fn attestation_private_key( - env: &mut impl Env, -) -> Result, Ctap2StatusCode> { - match env.store().find(key::ATTESTATION_PRIVATE_KEY)? { - None => Ok(None), - Some(key) if key.len() == key_material::ATTESTATION_PRIVATE_KEY_LENGTH => { - Ok(Some(*array_ref![ - key, - 0, - key_material::ATTESTATION_PRIVATE_KEY_LENGTH - ])) - } - Some(_) => Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR), - } -} - -/// Sets the attestation private key. -/// -/// If it is already defined, it is overwritten. -pub fn set_attestation_private_key( - env: &mut impl Env, - attestation_private_key: &[u8; key_material::ATTESTATION_PRIVATE_KEY_LENGTH], -) -> Result<(), Ctap2StatusCode> { - match env.store().find(key::ATTESTATION_PRIVATE_KEY)? { - None => Ok(env - .store() - .insert(key::ATTESTATION_PRIVATE_KEY, attestation_private_key)?), - Some(_) => Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR), - } -} - -/// Returns the attestation certificate if defined. -pub fn attestation_certificate(env: &mut impl Env) -> Result>, Ctap2StatusCode> { - Ok(env.store().find(key::ATTESTATION_CERTIFICATE)?) -} - -/// Sets the attestation certificate. -/// -/// If it is already defined, it is overwritten. -pub fn set_attestation_certificate( - env: &mut impl Env, - attestation_certificate: &[u8], -) -> Result<(), Ctap2StatusCode> { - match env.store().find(key::ATTESTATION_CERTIFICATE)? { - None => Ok(env - .store() - .insert(key::ATTESTATION_CERTIFICATE, attestation_certificate)?), - Some(_) => Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR), - } -} - /// Returns the AAGUID. pub fn aaguid(env: &mut impl Env) -> Result<[u8; key_material::AAGUID_LENGTH], Ctap2StatusCode> { let aaguid = env @@ -545,10 +493,9 @@ pub fn enterprise_attestation(env: &mut impl Env) -> Result Result<(), Ctap2StatusCode> { - if attestation_private_key(env)?.is_none() || attestation_certificate(env)?.is_none() { - return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR); - } if !enterprise_attestation(env)? { env.store().insert(key::ENTERPRISE_ATTESTATION, &[])?; } @@ -696,6 +643,7 @@ fn serialize_min_pin_length_rp_ids(rp_ids: Vec) -> Result, Ctap2 #[cfg(test)] mod test { use super::*; + use crate::api::attestation_store::{self, Attestation, AttestationStore}; use crate::ctap::crypto_wrapper::PrivateKey; use crate::ctap::data_formats::{PublicKeyCredentialSource, PublicKeyCredentialType}; use crate::env::test::TestEnv; @@ -1033,25 +981,26 @@ mod test { init(&mut env).unwrap(); // Make sure the attestation are absent. There is no batch attestation in tests. - assert!(attestation_private_key(&mut env).unwrap().is_none()); - assert!(attestation_certificate(&mut env).unwrap().is_none()); + assert_eq!( + env.attestation_store().get(&attestation_store::Id::Batch), + Ok(None) + ); // Make sure the persistent keys are initialized to dummy values. - let dummy_key = [0x41u8; key_material::ATTESTATION_PRIVATE_KEY_LENGTH]; - let dummy_cert = [0xddu8; 20]; - set_attestation_private_key(&mut env, &dummy_key).unwrap(); - set_attestation_certificate(&mut env, &dummy_cert).unwrap(); + let dummy_attestation = Attestation { + private_key: [0x41; key_material::ATTESTATION_PRIVATE_KEY_LENGTH], + certificate: vec![0xdd; 20], + }; + env.attestation_store() + .set(&attestation_store::Id::Batch, Some(&dummy_attestation)) + .unwrap(); assert_eq!(&aaguid(&mut env).unwrap(), key_material::AAGUID); // The persistent keys stay initialized and preserve their value after a reset. reset(&mut env).unwrap(); assert_eq!( - &attestation_private_key(&mut env).unwrap().unwrap(), - &dummy_key - ); - assert_eq!( - attestation_certificate(&mut env).unwrap().unwrap(), - &dummy_cert + env.attestation_store().get(&attestation_store::Id::Batch), + Ok(Some(dummy_attestation)) ); assert_eq!(&aaguid(&mut env).unwrap(), key_material::AAGUID); } @@ -1187,17 +1136,13 @@ mod test { fn test_enterprise_attestation() { let mut env = TestEnv::new(); - assert!(!enterprise_attestation(&mut env).unwrap()); - assert_eq!( - enable_enterprise_attestation(&mut env), - Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR) - ); - assert!(!enterprise_attestation(&mut env).unwrap()); - - let dummy_key = [0x41u8; key_material::ATTESTATION_PRIVATE_KEY_LENGTH]; - let dummy_cert = [0xddu8; 20]; - set_attestation_private_key(&mut env, &dummy_key).unwrap(); - set_attestation_certificate(&mut env, &dummy_cert).unwrap(); + let dummy_attestation = Attestation { + private_key: [0x41; key_material::ATTESTATION_PRIVATE_KEY_LENGTH], + certificate: vec![0xdd; 20], + }; + env.attestation_store() + .set(&attestation_store::Id::Batch, Some(&dummy_attestation)) + .unwrap(); assert!(!enterprise_attestation(&mut env).unwrap()); assert_eq!(enable_enterprise_attestation(&mut env), Ok(())); diff --git a/src/ctap/storage/key.rs b/src/ctap/storage/key.rs index 45fef2d..6b6f28a 100644 --- a/src/ctap/storage/key.rs +++ b/src/ctap/storage/key.rs @@ -61,11 +61,8 @@ make_partition! { // WARNING: Keys should not be deleted but prefixed with `_` to avoid accidentally reusing them. - /// The attestation private key. - ATTESTATION_PRIVATE_KEY = 1; - - /// The attestation certificate. - ATTESTATION_CERTIFICATE = 2; + /// Reserved for the attestation store implementation of the environment. + _RESERVED_ATTESTATION_STORE = 1..3; /// The aaguid. AAGUID = 3; diff --git a/src/env/mod.rs b/src/env/mod.rs index 08bb9f3..eb1ad5c 100644 --- a/src/env/mod.rs +++ b/src/env/mod.rs @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +use crate::api::attestation_store::AttestationStore; use crate::api::connection::HidConnection; use crate::api::customization::Customization; use crate::api::firmware_protection::FirmwareProtection; @@ -36,11 +37,13 @@ pub trait Env { type Write: core::fmt::Write; type Customization: Customization; type HidConnection: HidConnection; + type AttestationStore: AttestationStore; fn rng(&mut self) -> &mut Self::Rng; fn user_presence(&mut self) -> &mut Self::UserPresence; fn store(&mut self) -> &mut Store; fn key_store(&mut self) -> &mut Self::KeyStore; + fn attestation_store(&mut self) -> &mut Self::AttestationStore; /// Returns the upgrade storage instance. /// diff --git a/src/env/test/mod.rs b/src/env/test/mod.rs index 1cd6d24..073061d 100644 --- a/src/env/test/mod.rs +++ b/src/env/test/mod.rs @@ -16,8 +16,8 @@ use self::upgrade_storage::BufferUpgradeStorage; use crate::api::connection::{HidConnection, SendOrRecvResult, SendOrRecvStatus}; use crate::api::customization::DEFAULT_CUSTOMIZATION; use crate::api::firmware_protection::FirmwareProtection; -use crate::api::key_store; use crate::api::user_presence::{UserPresence, UserPresenceResult}; +use crate::api::{attestation_store, key_store}; use crate::clock::ClockInt; use crate::env::Env; use customization::TestCustomization; @@ -149,12 +149,14 @@ impl FirmwareProtection for TestEnv { } impl key_store::Helper for TestEnv {} +impl attestation_store::Helper for TestEnv {} impl Env for TestEnv { type Rng = TestRng256; type UserPresence = TestUserPresence; type Storage = BufferStorage; type KeyStore = Self; + type AttestationStore = Self; type UpgradeStorage = BufferUpgradeStorage; type FirmwareProtection = Self; type Write = TestWrite; @@ -177,6 +179,10 @@ impl Env for TestEnv { self } + fn attestation_store(&mut self) -> &mut Self { + self + } + fn upgrade_storage(&mut self) -> Option<&mut Self::UpgradeStorage> { self.upgrade_storage.as_mut() } diff --git a/src/env/tock/mod.rs b/src/env/tock/mod.rs index 8cb6274..58574ba 100644 --- a/src/env/tock/mod.rs +++ b/src/env/tock/mod.rs @@ -16,8 +16,8 @@ pub use self::storage::{TockStorage, TockUpgradeStorage}; use crate::api::connection::{HidConnection, SendOrRecvError, SendOrRecvResult, SendOrRecvStatus}; use crate::api::customization::{CustomizationImpl, DEFAULT_CUSTOMIZATION}; use crate::api::firmware_protection::FirmwareProtection; -use crate::api::key_store; use crate::api::user_presence::{UserPresence, UserPresenceError, UserPresenceResult}; +use crate::api::{attestation_store, key_store}; use crate::clock::{ClockInt, KEEPALIVE_DELAY_MS}; use crate::env::Env; use core::cell::Cell; @@ -195,12 +195,14 @@ impl FirmwareProtection for TockEnv { } impl key_store::Helper for TockEnv {} +impl attestation_store::Helper for TockEnv {} impl Env for TockEnv { type Rng = TockRng256; type UserPresence = Self; type Storage = TockStorage; type KeyStore = Self; + type AttestationStore = Self; type UpgradeStorage = TockUpgradeStorage; type FirmwareProtection = Self; type Write = Console; @@ -223,6 +225,10 @@ impl Env for TockEnv { self } + fn attestation_store(&mut self) -> &mut Self { + self + } + fn upgrade_storage(&mut self) -> Option<&mut Self::UpgradeStorage> { self.upgrade_storage.as_mut() } From 25c884c07090097e408ba09cb05fed7b752a8823 Mon Sep 17 00:00:00 2001 From: Julien Cretin Date: Thu, 7 Jul 2022 17:23:24 +0200 Subject: [PATCH 2/6] Address comments --- src/api/attestation_store.rs | 24 +++++++++++++++--------- src/ctap/apdu.rs | 13 +++++++++++++ src/ctap/ctap1.rs | 3 +-- src/ctap/mod.rs | 7 +++++-- src/ctap/storage.rs | 13 ++++++++++--- src/env/test/mod.rs | 14 +++++++++++++- src/env/tock/mod.rs | 7 ++++++- src/test_helpers/mod.rs | 25 ++++++++++--------------- 8 files changed, 73 insertions(+), 33 deletions(-) diff --git a/src/api/attestation_store.rs b/src/api/attestation_store.rs index 7e6900e..17cb82f 100644 --- a/src/api/attestation_store.rs +++ b/src/api/attestation_store.rs @@ -1,13 +1,13 @@ -use alloc::string::String; use alloc::vec::Vec; use persistent_store::{StoreError, StoreUpdate}; use crate::env::Env; /// Identifies an attestation. +#[derive(Clone, PartialEq, Eq)] pub enum Id { Batch, - Enterprise { rp_id: String }, + Enterprise, } #[cfg_attr(feature = "std", derive(Debug, PartialEq, Eq))] @@ -18,9 +18,6 @@ pub struct Attestation { } /// Stores enterprise or batch attestations. -/// -/// Implementations don't need to distinguish different attestations. In particular, setting one -/// attestation may set other ones. pub trait AttestationStore { /// Returns an attestation given its id, if it exists. /// @@ -48,11 +45,17 @@ pub const STORAGE_KEYS: &[usize] = &[1, 2]; /// Implements a default attestation store using the environment store. /// -/// The same attestation is used for batch and enterprise. -pub trait Helper: Env {} +/// Supports only one attestation at a time. +pub trait Helper: Env { + /// Returns the current attestation id. + fn attestation_id(&self) -> Id; +} impl AttestationStore for T { - fn get(&mut self, _: &Id) -> Result, Error> { + fn get(&mut self, id: &Id) -> Result, Error> { + if id != &self.attestation_id() { + return Err(Error::NoSupport); + } let private_key = self.store().find(PRIVATE_KEY_STORAGE_KEY)?; let certificate = self.store().find(CERTIFICATE_STORAGE_KEY)?; let (private_key, certificate) = match (private_key, certificate) { @@ -69,7 +72,10 @@ impl AttestationStore for T { })) } - fn set(&mut self, _: &Id, attestation: Option<&Attestation>) -> Result<(), Error> { + fn set(&mut self, id: &Id, attestation: Option<&Attestation>) -> Result<(), Error> { + if id != &self.attestation_id() { + return Err(Error::NoSupport); + } let updates = match attestation { None => [ StoreUpdate::Remove { diff --git a/src/ctap/apdu.rs b/src/ctap/apdu.rs index 145b3fe..fc1c240 100644 --- a/src/ctap/apdu.rs +++ b/src/ctap/apdu.rs @@ -16,6 +16,8 @@ use alloc::vec::Vec; use byteorder::{BigEndian, ByteOrder}; use core::convert::TryFrom; +use crate::api::attestation_store; + const APDU_HEADER_LEN: usize = 4; #[derive(Clone, Debug, PartialEq, Eq)] @@ -44,6 +46,17 @@ impl From for u16 { } } +impl From for ApduStatusCode { + fn from(error: attestation_store::Error) -> Self { + use attestation_store::Error; + match error { + Error::Storage => ApduStatusCode::SW_MEMERR, + Error::Internal => ApduStatusCode::SW_INTERNAL_EXCEPTION, + Error::NoSupport => ApduStatusCode::SW_INTERNAL_EXCEPTION, + } + } +} + #[allow(dead_code)] pub enum ApduInstructions { Select = 0xA4, diff --git a/src/ctap/ctap1.rs b/src/ctap/ctap1.rs index 60a8248..62cc82d 100644 --- a/src/ctap/ctap1.rs +++ b/src/ctap/ctap1.rs @@ -262,8 +262,7 @@ impl Ctap1Command { certificate, } = env .attestation_store() - .get(&attestation_store::Id::Batch) - .map_err(|_| Ctap1StatusCode::SW_MEMERR)? + .get(&attestation_store::Id::Batch)? .ok_or(Ctap1StatusCode::SW_INTERNAL_EXCEPTION)?; let mut response = Vec::with_capacity(105 + key_handle.len() + certificate.len()); diff --git a/src/ctap/mod.rs b/src/ctap/mod.rs index 3d94a8e..4e452e1 100644 --- a/src/ctap/mod.rs +++ b/src/ctap/mod.rs @@ -861,7 +861,7 @@ impl CtapState { key_type: PublicKeyCredentialType::PublicKey, credential_id: random_id.clone(), private_key: private_key.clone(), - rp_id: rp_id.clone(), + rp_id, user_handle: user.user_id, // This input is user provided, so we crop it to 64 byte for storage. // The UTF8 encoding is always preserved, so the string might end up shorter. @@ -922,7 +922,7 @@ impl CtapState { let attestation_id = if env.customization().use_batch_attestation() { Some(attestation_store::Id::Batch) } else if ep_att { - Some(attestation_store::Id::Enterprise { rp_id }) + Some(attestation_store::Id::Enterprise) } else { None }; @@ -2123,6 +2123,7 @@ mod test { #[test] fn test_process_make_credential_with_enterprise_attestation_vendor_facilitated() { let mut env = TestEnv::new(); + env.set_attestation_id(attestation_store::Id::Enterprise); env.customization_mut().setup_enterprise_attestation( Some(EnterpriseAttestationMode::VendorFacilitated), Some(vec!["example.com".to_string()]), @@ -2169,6 +2170,7 @@ mod test { #[test] fn test_process_make_credential_with_enterprise_attestation_platform_managed() { let mut env = TestEnv::new(); + env.set_attestation_id(attestation_store::Id::Enterprise); env.customization_mut().setup_enterprise_attestation( Some(EnterpriseAttestationMode::PlatformManaged), Some(vec!["example.com".to_string()]), @@ -2205,6 +2207,7 @@ mod test { #[test] fn test_process_make_credential_with_enterprise_attestation_invalid() { let mut env = TestEnv::new(); + env.set_attestation_id(attestation_store::Id::Enterprise); env.customization_mut() .setup_enterprise_attestation(Some(EnterpriseAttestationMode::PlatformManaged), None); diff --git a/src/ctap/storage.rs b/src/ctap/storage.rs index 029f0b2..59db1d5 100644 --- a/src/ctap/storage.rs +++ b/src/ctap/storage.rs @@ -14,6 +14,7 @@ mod key; +use crate::api::attestation_store::{self, AttestationStore}; use crate::api::customization::Customization; use crate::api::key_store::KeyStore; use crate::ctap::client_pin::PIN_AUTH_LENGTH; @@ -493,9 +494,14 @@ pub fn enterprise_attestation(env: &mut impl Env) -> Result Result<(), Ctap2StatusCode> { + if env + .attestation_store() + .get(&attestation_store::Id::Enterprise)? + .is_none() + { + return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR); + } if !enterprise_attestation(env)? { env.store().insert(key::ENTERPRISE_ATTESTATION, &[])?; } @@ -1135,13 +1141,14 @@ mod test { #[test] fn test_enterprise_attestation() { let mut env = TestEnv::new(); + env.set_attestation_id(attestation_store::Id::Enterprise); let dummy_attestation = Attestation { private_key: [0x41; key_material::ATTESTATION_PRIVATE_KEY_LENGTH], certificate: vec![0xdd; 20], }; env.attestation_store() - .set(&attestation_store::Id::Batch, Some(&dummy_attestation)) + .set(&attestation_store::Id::Enterprise, Some(&dummy_attestation)) .unwrap(); assert!(!enterprise_attestation(&mut env).unwrap()); diff --git a/src/env/test/mod.rs b/src/env/test/mod.rs index 073061d..4fe25e2 100644 --- a/src/env/test/mod.rs +++ b/src/env/test/mod.rs @@ -36,6 +36,7 @@ pub struct TestEnv { store: Store, upgrade_storage: Option, customization: TestCustomization, + attestation_id: attestation_store::Id, } pub struct TestRng256 { @@ -106,12 +107,14 @@ impl TestEnv { let store = Store::new(storage).ok().unwrap(); let upgrade_storage = Some(BufferUpgradeStorage::new().unwrap()); let customization = DEFAULT_CUSTOMIZATION.into(); + let attestation_id = attestation_store::Id::Batch; TestEnv { rng, user_presence, store, upgrade_storage, customization, + attestation_id, } } @@ -126,6 +129,10 @@ impl TestEnv { pub fn rng(&mut self) -> &mut TestRng256 { &mut self.rng } + + pub fn set_attestation_id(&mut self, id: attestation_store::Id) { + self.attestation_id = id; + } } impl TestUserPresence { @@ -149,7 +156,12 @@ impl FirmwareProtection for TestEnv { } impl key_store::Helper for TestEnv {} -impl attestation_store::Helper for TestEnv {} + +impl attestation_store::Helper for TestEnv { + fn attestation_id(&self) -> attestation_store::Id { + self.attestation_id.clone() + } +} impl Env for TestEnv { type Rng = TestRng256; diff --git a/src/env/tock/mod.rs b/src/env/tock/mod.rs index 58574ba..7c7f987 100644 --- a/src/env/tock/mod.rs +++ b/src/env/tock/mod.rs @@ -195,7 +195,12 @@ impl FirmwareProtection for TockEnv { } impl key_store::Helper for TockEnv {} -impl attestation_store::Helper for TockEnv {} + +impl attestation_store::Helper for TockEnv { + fn attestation_id(&self) -> attestation_store::Id { + attestation_store::Id::Batch + } +} impl Env for TockEnv { type Rng = TockRng256; diff --git a/src/test_helpers/mod.rs b/src/test_helpers/mod.rs index 968e25a..adcc791 100644 --- a/src/test_helpers/mod.rs +++ b/src/test_helpers/mod.rs @@ -12,10 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. +use crate::api::attestation_store::{self, Attestation, AttestationStore}; use crate::clock::CtapInstant; use crate::ctap::command::{ - AuthenticatorAttestationMaterial, AuthenticatorConfigParameters, - AuthenticatorVendorConfigureParameters, Command, + AuthenticatorAttestationMaterial, AuthenticatorConfigParameters, Command, }; use crate::ctap::data_formats::ConfigSubCommand; use crate::ctap::status_code::Ctap2StatusCode; @@ -32,22 +32,17 @@ pub fn enable_enterprise_attestation( state: &mut CtapState, env: &mut impl Env, ) -> Result { - let dummy_key = [0x41; key_material::ATTESTATION_PRIVATE_KEY_LENGTH]; - let dummy_cert = vec![0xdd; 20]; let attestation_material = AuthenticatorAttestationMaterial { - certificate: dummy_cert, - private_key: dummy_key, + certificate: vec![0xdd; 20], + private_key: [0x41; key_material::ATTESTATION_PRIVATE_KEY_LENGTH], }; - let configure_params = AuthenticatorVendorConfigureParameters { - lockdown: false, - attestation_material: Some(attestation_material.clone()), + + let attestation = Attestation { + private_key: attestation_material.private_key, + certificate: attestation_material.certificate.clone(), }; - #[cfg(feature = "vendor_hid")] - let vendor_channel = VENDOR_CHANNEL; - #[cfg(not(feature = "vendor_hid"))] - let vendor_channel = DUMMY_CHANNEL; - let vendor_command = Command::AuthenticatorVendorConfigure(configure_params); - state.process_parsed_command(env, vendor_command, vendor_channel, CtapInstant::new(0))?; + env.attestation_store() + .set(&attestation_store::Id::Enterprise, Some(&attestation))?; let config_params = AuthenticatorConfigParameters { sub_command: ConfigSubCommand::EnableEnterpriseAttestation, From 0bbc8663c23306ab0a12459ecff5084c3f6d5e62 Mon Sep 17 00:00:00 2001 From: Julien Cretin Date: Thu, 7 Jul 2022 17:28:33 +0200 Subject: [PATCH 3/6] Remove unused variable --- src/test_helpers/mod.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/test_helpers/mod.rs b/src/test_helpers/mod.rs index adcc791..4ed02b0 100644 --- a/src/test_helpers/mod.rs +++ b/src/test_helpers/mod.rs @@ -25,8 +25,6 @@ use crate::env::Env; // In tests where we define a dummy user-presence check that immediately returns, the channel // ID is irrelevant, so we pass this (dummy but valid) value. const DUMMY_CHANNEL: Channel = Channel::MainHid([0x12, 0x34, 0x56, 0x78]); -#[cfg(feature = "vendor_hid")] -const VENDOR_CHANNEL: Channel = Channel::VendorHid([0x12, 0x34, 0x56, 0x78]); pub fn enable_enterprise_attestation( state: &mut CtapState, From bc354d8abb4b0b319ad86ae0e4e4df0d8c36eee8 Mon Sep 17 00:00:00 2001 From: Julien Cretin Date: Mon, 11 Jul 2022 11:16:58 +0200 Subject: [PATCH 4/6] Fix attestation priority --- src/ctap/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ctap/mod.rs b/src/ctap/mod.rs index 4e452e1..a9decab 100644 --- a/src/ctap/mod.rs +++ b/src/ctap/mod.rs @@ -919,10 +919,10 @@ impl CtapState { let mut signature_data = auth_data.clone(); signature_data.extend(client_data_hash); - let attestation_id = if env.customization().use_batch_attestation() { - Some(attestation_store::Id::Batch) - } else if ep_att { + let attestation_id = if ep_att { Some(attestation_store::Id::Enterprise) + } else if env.customization().use_batch_attestation() { + Some(attestation_store::Id::Batch) } else { None }; From f528567ce1b186de27d2ff22c2379afabd9993fe Mon Sep 17 00:00:00 2001 From: Julien Cretin Date: Tue, 12 Jul 2022 16:57:19 +0200 Subject: [PATCH 5/6] Expose the get and set helper implementation --- src/api/attestation_store.rs | 78 ++++++++++++++++++++---------------- 1 file changed, 43 insertions(+), 35 deletions(-) diff --git a/src/api/attestation_store.rs b/src/api/attestation_store.rs index 17cb82f..111f5ea 100644 --- a/src/api/attestation_store.rs +++ b/src/api/attestation_store.rs @@ -56,50 +56,58 @@ impl AttestationStore for T { if id != &self.attestation_id() { return Err(Error::NoSupport); } - let private_key = self.store().find(PRIVATE_KEY_STORAGE_KEY)?; - let certificate = self.store().find(CERTIFICATE_STORAGE_KEY)?; - let (private_key, certificate) = match (private_key, certificate) { - (Some(x), Some(y)) => (x, y), - (None, None) => return Ok(None), - _ => return Err(Error::Internal), - }; - if private_key.len() != 32 { - return Err(Error::Internal); - } - Ok(Some(Attestation { - private_key: *array_ref![private_key, 0, 32], - certificate, - })) + helper_get(self) } fn set(&mut self, id: &Id, attestation: Option<&Attestation>) -> Result<(), Error> { if id != &self.attestation_id() { return Err(Error::NoSupport); } - let updates = match attestation { - None => [ - StoreUpdate::Remove { - key: PRIVATE_KEY_STORAGE_KEY, - }, - StoreUpdate::Remove { - key: CERTIFICATE_STORAGE_KEY, - }, - ], - Some(attestation) => [ - StoreUpdate::Insert { - key: PRIVATE_KEY_STORAGE_KEY, - value: &attestation.private_key[..], - }, - StoreUpdate::Insert { - key: CERTIFICATE_STORAGE_KEY, - value: &attestation.certificate[..], - }, - ], - }; - Ok(self.store().transaction(&updates)?) + helper_set(self, attestation) } } +pub fn helper_get(env: &mut impl Env) -> Result, Error> { + let private_key = env.store().find(PRIVATE_KEY_STORAGE_KEY)?; + let certificate = env.store().find(CERTIFICATE_STORAGE_KEY)?; + let (private_key, certificate) = match (private_key, certificate) { + (Some(x), Some(y)) => (x, y), + (None, None) => return Ok(None), + _ => return Err(Error::Internal), + }; + if private_key.len() != 32 { + return Err(Error::Internal); + } + Ok(Some(Attestation { + private_key: *array_ref![private_key, 0, 32], + certificate, + })) +} + +pub fn helper_set(env: &mut impl Env, attestation: Option<&Attestation>) -> Result<(), Error> { + let updates = match attestation { + None => [ + StoreUpdate::Remove { + key: PRIVATE_KEY_STORAGE_KEY, + }, + StoreUpdate::Remove { + key: CERTIFICATE_STORAGE_KEY, + }, + ], + Some(attestation) => [ + StoreUpdate::Insert { + key: PRIVATE_KEY_STORAGE_KEY, + value: &attestation.private_key[..], + }, + StoreUpdate::Insert { + key: CERTIFICATE_STORAGE_KEY, + value: &attestation.certificate[..], + }, + ], + }; + Ok(env.store().transaction(&updates)?) +} + const PRIVATE_KEY_STORAGE_KEY: usize = STORAGE_KEYS[0]; const CERTIFICATE_STORAGE_KEY: usize = STORAGE_KEYS[1]; From 07a28fe6112e7fbec3bb0fb76730990919aa0594 Mon Sep 17 00:00:00 2001 From: Julien Cretin Date: Thu, 14 Jul 2022 12:46:01 +0200 Subject: [PATCH 6/6] Inline Helper --- src/api/attestation_store.rs | 24 ------------------------ src/ctap/mod.rs | 3 --- src/ctap/storage.rs | 1 - src/env/test/mod.rs | 25 +++++++++++++++---------- src/env/tock/mod.rs | 24 +++++++++++++++++++++--- src/test_helpers/mod.rs | 27 +++++++++++++++++---------- 6 files changed, 53 insertions(+), 51 deletions(-) diff --git a/src/api/attestation_store.rs b/src/api/attestation_store.rs index 111f5ea..74024a4 100644 --- a/src/api/attestation_store.rs +++ b/src/api/attestation_store.rs @@ -43,30 +43,6 @@ pub enum Error { /// Keys of the environment store reserved for the attestation store. pub const STORAGE_KEYS: &[usize] = &[1, 2]; -/// Implements a default attestation store using the environment store. -/// -/// Supports only one attestation at a time. -pub trait Helper: Env { - /// Returns the current attestation id. - fn attestation_id(&self) -> Id; -} - -impl AttestationStore for T { - fn get(&mut self, id: &Id) -> Result, Error> { - if id != &self.attestation_id() { - return Err(Error::NoSupport); - } - helper_get(self) - } - - fn set(&mut self, id: &Id, attestation: Option<&Attestation>) -> Result<(), Error> { - if id != &self.attestation_id() { - return Err(Error::NoSupport); - } - helper_set(self, attestation) - } -} - pub fn helper_get(env: &mut impl Env) -> Result, Error> { let private_key = env.store().find(PRIVATE_KEY_STORAGE_KEY)?; let certificate = env.store().find(CERTIFICATE_STORAGE_KEY)?; diff --git a/src/ctap/mod.rs b/src/ctap/mod.rs index 9ee17de..c8e8925 100644 --- a/src/ctap/mod.rs +++ b/src/ctap/mod.rs @@ -2126,7 +2126,6 @@ mod test { #[test] fn test_process_make_credential_with_enterprise_attestation_vendor_facilitated() { let mut env = TestEnv::new(); - env.set_attestation_id(attestation_store::Id::Enterprise); env.customization_mut().setup_enterprise_attestation( Some(EnterpriseAttestationMode::VendorFacilitated), Some(vec!["example.com".to_string()]), @@ -2173,7 +2172,6 @@ mod test { #[test] fn test_process_make_credential_with_enterprise_attestation_platform_managed() { let mut env = TestEnv::new(); - env.set_attestation_id(attestation_store::Id::Enterprise); env.customization_mut().setup_enterprise_attestation( Some(EnterpriseAttestationMode::PlatformManaged), Some(vec!["example.com".to_string()]), @@ -2210,7 +2208,6 @@ mod test { #[test] fn test_process_make_credential_with_enterprise_attestation_invalid() { let mut env = TestEnv::new(); - env.set_attestation_id(attestation_store::Id::Enterprise); env.customization_mut() .setup_enterprise_attestation(Some(EnterpriseAttestationMode::PlatformManaged), None); diff --git a/src/ctap/storage.rs b/src/ctap/storage.rs index 59db1d5..ff932d0 100644 --- a/src/ctap/storage.rs +++ b/src/ctap/storage.rs @@ -1141,7 +1141,6 @@ mod test { #[test] fn test_enterprise_attestation() { let mut env = TestEnv::new(); - env.set_attestation_id(attestation_store::Id::Enterprise); let dummy_attestation = Attestation { private_key: [0x41; key_material::ATTESTATION_PRIVATE_KEY_LENGTH], diff --git a/src/env/test/mod.rs b/src/env/test/mod.rs index 4fe25e2..e5bac84 100644 --- a/src/env/test/mod.rs +++ b/src/env/test/mod.rs @@ -13,6 +13,7 @@ // limitations under the License. use self::upgrade_storage::BufferUpgradeStorage; +use crate::api::attestation_store::AttestationStore; use crate::api::connection::{HidConnection, SendOrRecvResult, SendOrRecvStatus}; use crate::api::customization::DEFAULT_CUSTOMIZATION; use crate::api::firmware_protection::FirmwareProtection; @@ -36,7 +37,6 @@ pub struct TestEnv { store: Store, upgrade_storage: Option, customization: TestCustomization, - attestation_id: attestation_store::Id, } pub struct TestRng256 { @@ -107,14 +107,12 @@ impl TestEnv { let store = Store::new(storage).ok().unwrap(); let upgrade_storage = Some(BufferUpgradeStorage::new().unwrap()); let customization = DEFAULT_CUSTOMIZATION.into(); - let attestation_id = attestation_store::Id::Batch; TestEnv { rng, user_presence, store, upgrade_storage, customization, - attestation_id, } } @@ -129,10 +127,6 @@ impl TestEnv { pub fn rng(&mut self) -> &mut TestRng256 { &mut self.rng } - - pub fn set_attestation_id(&mut self, id: attestation_store::Id) { - self.attestation_id = id; - } } impl TestUserPresence { @@ -157,9 +151,20 @@ impl FirmwareProtection for TestEnv { impl key_store::Helper for TestEnv {} -impl attestation_store::Helper for TestEnv { - fn attestation_id(&self) -> attestation_store::Id { - self.attestation_id.clone() +impl AttestationStore for TestEnv { + fn get( + &mut self, + _id: &attestation_store::Id, + ) -> Result, attestation_store::Error> { + attestation_store::helper_get(self) + } + + fn set( + &mut self, + _id: &attestation_store::Id, + attestation: Option<&attestation_store::Attestation>, + ) -> Result<(), attestation_store::Error> { + attestation_store::helper_set(self, attestation) } } diff --git a/src/env/tock/mod.rs b/src/env/tock/mod.rs index 7c7f987..891883d 100644 --- a/src/env/tock/mod.rs +++ b/src/env/tock/mod.rs @@ -13,6 +13,7 @@ // limitations under the License. pub use self::storage::{TockStorage, TockUpgradeStorage}; +use crate::api::attestation_store::AttestationStore; use crate::api::connection::{HidConnection, SendOrRecvError, SendOrRecvResult, SendOrRecvStatus}; use crate::api::customization::{CustomizationImpl, DEFAULT_CUSTOMIZATION}; use crate::api::firmware_protection::FirmwareProtection; @@ -196,9 +197,26 @@ impl FirmwareProtection for TockEnv { impl key_store::Helper for TockEnv {} -impl attestation_store::Helper for TockEnv { - fn attestation_id(&self) -> attestation_store::Id { - attestation_store::Id::Batch +impl AttestationStore for TockEnv { + fn get( + &mut self, + id: &attestation_store::Id, + ) -> Result, attestation_store::Error> { + if !matches!(id, attestation_store::Id::Batch) { + return Err(attestation_store::Error::NoSupport); + } + attestation_store::helper_get(self) + } + + fn set( + &mut self, + id: &attestation_store::Id, + attestation: Option<&attestation_store::Attestation>, + ) -> Result<(), attestation_store::Error> { + if !matches!(id, attestation_store::Id::Batch) { + return Err(attestation_store::Error::NoSupport); + } + attestation_store::helper_set(self, attestation) } } diff --git a/src/test_helpers/mod.rs b/src/test_helpers/mod.rs index 4ed02b0..968e25a 100644 --- a/src/test_helpers/mod.rs +++ b/src/test_helpers/mod.rs @@ -12,10 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::api::attestation_store::{self, Attestation, AttestationStore}; use crate::clock::CtapInstant; use crate::ctap::command::{ - AuthenticatorAttestationMaterial, AuthenticatorConfigParameters, Command, + AuthenticatorAttestationMaterial, AuthenticatorConfigParameters, + AuthenticatorVendorConfigureParameters, Command, }; use crate::ctap::data_formats::ConfigSubCommand; use crate::ctap::status_code::Ctap2StatusCode; @@ -25,22 +25,29 @@ use crate::env::Env; // In tests where we define a dummy user-presence check that immediately returns, the channel // ID is irrelevant, so we pass this (dummy but valid) value. const DUMMY_CHANNEL: Channel = Channel::MainHid([0x12, 0x34, 0x56, 0x78]); +#[cfg(feature = "vendor_hid")] +const VENDOR_CHANNEL: Channel = Channel::VendorHid([0x12, 0x34, 0x56, 0x78]); pub fn enable_enterprise_attestation( state: &mut CtapState, env: &mut impl Env, ) -> Result { + let dummy_key = [0x41; key_material::ATTESTATION_PRIVATE_KEY_LENGTH]; + let dummy_cert = vec![0xdd; 20]; let attestation_material = AuthenticatorAttestationMaterial { - certificate: vec![0xdd; 20], - private_key: [0x41; key_material::ATTESTATION_PRIVATE_KEY_LENGTH], + certificate: dummy_cert, + private_key: dummy_key, }; - - let attestation = Attestation { - private_key: attestation_material.private_key, - certificate: attestation_material.certificate.clone(), + let configure_params = AuthenticatorVendorConfigureParameters { + lockdown: false, + attestation_material: Some(attestation_material.clone()), }; - env.attestation_store() - .set(&attestation_store::Id::Enterprise, Some(&attestation))?; + #[cfg(feature = "vendor_hid")] + let vendor_channel = VENDOR_CHANNEL; + #[cfg(not(feature = "vendor_hid"))] + let vendor_channel = DUMMY_CHANNEL; + let vendor_command = Command::AuthenticatorVendorConfigure(configure_params); + state.process_parsed_command(env, vendor_command, vendor_channel, CtapInstant::new(0))?; let config_params = AuthenticatorConfigParameters { sub_command: ConfigSubCommand::EnableEnterpriseAttestation,