diff --git a/src/api/attestation_store.rs b/src/api/attestation_store.rs index 7e6900e3..17cb82fc 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 145b3fe2..fc1c2402 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 60a82480..62cc82d2 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 3d94a8ea..4e452e14 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 029f0b26..59db1d51 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 073061d8..4fe25e2b 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 58574baa..7c7f9874 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 968e25a2..adcc7912 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,