Address comments

This commit is contained in:
Julien Cretin
2022-07-07 17:23:24 +02:00
parent 80a6b82ed7
commit 25c884c070
8 changed files with 73 additions and 33 deletions

View File

@@ -1,13 +1,13 @@
use alloc::string::String;
use alloc::vec::Vec; use alloc::vec::Vec;
use persistent_store::{StoreError, StoreUpdate}; use persistent_store::{StoreError, StoreUpdate};
use crate::env::Env; use crate::env::Env;
/// Identifies an attestation. /// Identifies an attestation.
#[derive(Clone, PartialEq, Eq)]
pub enum Id { pub enum Id {
Batch, Batch,
Enterprise { rp_id: String }, Enterprise,
} }
#[cfg_attr(feature = "std", derive(Debug, PartialEq, Eq))] #[cfg_attr(feature = "std", derive(Debug, PartialEq, Eq))]
@@ -18,9 +18,6 @@ pub struct Attestation {
} }
/// Stores enterprise or batch attestations. /// 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 { pub trait AttestationStore {
/// Returns an attestation given its id, if it exists. /// 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. /// Implements a default attestation store using the environment store.
/// ///
/// The same attestation is used for batch and enterprise. /// Supports only one attestation at a time.
pub trait Helper: Env {} pub trait Helper: Env {
/// Returns the current attestation id.
fn attestation_id(&self) -> Id;
}
impl<T: Helper> AttestationStore for T { impl<T: Helper> AttestationStore for T {
fn get(&mut self, _: &Id) -> Result<Option<Attestation>, Error> { fn get(&mut self, id: &Id) -> Result<Option<Attestation>, Error> {
if id != &self.attestation_id() {
return Err(Error::NoSupport);
}
let private_key = self.store().find(PRIVATE_KEY_STORAGE_KEY)?; let private_key = self.store().find(PRIVATE_KEY_STORAGE_KEY)?;
let certificate = self.store().find(CERTIFICATE_STORAGE_KEY)?; let certificate = self.store().find(CERTIFICATE_STORAGE_KEY)?;
let (private_key, certificate) = match (private_key, certificate) { let (private_key, certificate) = match (private_key, certificate) {
@@ -69,7 +72,10 @@ impl<T: Helper> 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 { let updates = match attestation {
None => [ None => [
StoreUpdate::Remove { StoreUpdate::Remove {

View File

@@ -16,6 +16,8 @@ use alloc::vec::Vec;
use byteorder::{BigEndian, ByteOrder}; use byteorder::{BigEndian, ByteOrder};
use core::convert::TryFrom; use core::convert::TryFrom;
use crate::api::attestation_store;
const APDU_HEADER_LEN: usize = 4; const APDU_HEADER_LEN: usize = 4;
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
@@ -44,6 +46,17 @@ impl From<ApduStatusCode> for u16 {
} }
} }
impl From<attestation_store::Error> 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)] #[allow(dead_code)]
pub enum ApduInstructions { pub enum ApduInstructions {
Select = 0xA4, Select = 0xA4,

View File

@@ -262,8 +262,7 @@ impl Ctap1Command {
certificate, certificate,
} = env } = env
.attestation_store() .attestation_store()
.get(&attestation_store::Id::Batch) .get(&attestation_store::Id::Batch)?
.map_err(|_| Ctap1StatusCode::SW_MEMERR)?
.ok_or(Ctap1StatusCode::SW_INTERNAL_EXCEPTION)?; .ok_or(Ctap1StatusCode::SW_INTERNAL_EXCEPTION)?;
let mut response = Vec::with_capacity(105 + key_handle.len() + certificate.len()); let mut response = Vec::with_capacity(105 + key_handle.len() + certificate.len());

View File

@@ -861,7 +861,7 @@ impl CtapState {
key_type: PublicKeyCredentialType::PublicKey, key_type: PublicKeyCredentialType::PublicKey,
credential_id: random_id.clone(), credential_id: random_id.clone(),
private_key: private_key.clone(), private_key: private_key.clone(),
rp_id: rp_id.clone(), rp_id,
user_handle: user.user_id, user_handle: user.user_id,
// This input is user provided, so we crop it to 64 byte for storage. // 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. // 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() { let attestation_id = if env.customization().use_batch_attestation() {
Some(attestation_store::Id::Batch) Some(attestation_store::Id::Batch)
} else if ep_att { } else if ep_att {
Some(attestation_store::Id::Enterprise { rp_id }) Some(attestation_store::Id::Enterprise)
} else { } else {
None None
}; };
@@ -2123,6 +2123,7 @@ mod test {
#[test] #[test]
fn test_process_make_credential_with_enterprise_attestation_vendor_facilitated() { fn test_process_make_credential_with_enterprise_attestation_vendor_facilitated() {
let mut env = TestEnv::new(); let mut env = TestEnv::new();
env.set_attestation_id(attestation_store::Id::Enterprise);
env.customization_mut().setup_enterprise_attestation( env.customization_mut().setup_enterprise_attestation(
Some(EnterpriseAttestationMode::VendorFacilitated), Some(EnterpriseAttestationMode::VendorFacilitated),
Some(vec!["example.com".to_string()]), Some(vec!["example.com".to_string()]),
@@ -2169,6 +2170,7 @@ mod test {
#[test] #[test]
fn test_process_make_credential_with_enterprise_attestation_platform_managed() { fn test_process_make_credential_with_enterprise_attestation_platform_managed() {
let mut env = TestEnv::new(); let mut env = TestEnv::new();
env.set_attestation_id(attestation_store::Id::Enterprise);
env.customization_mut().setup_enterprise_attestation( env.customization_mut().setup_enterprise_attestation(
Some(EnterpriseAttestationMode::PlatformManaged), Some(EnterpriseAttestationMode::PlatformManaged),
Some(vec!["example.com".to_string()]), Some(vec!["example.com".to_string()]),
@@ -2205,6 +2207,7 @@ mod test {
#[test] #[test]
fn test_process_make_credential_with_enterprise_attestation_invalid() { fn test_process_make_credential_with_enterprise_attestation_invalid() {
let mut env = TestEnv::new(); let mut env = TestEnv::new();
env.set_attestation_id(attestation_store::Id::Enterprise);
env.customization_mut() env.customization_mut()
.setup_enterprise_attestation(Some(EnterpriseAttestationMode::PlatformManaged), None); .setup_enterprise_attestation(Some(EnterpriseAttestationMode::PlatformManaged), None);

View File

@@ -14,6 +14,7 @@
mod key; mod key;
use crate::api::attestation_store::{self, AttestationStore};
use crate::api::customization::Customization; use crate::api::customization::Customization;
use crate::api::key_store::KeyStore; use crate::api::key_store::KeyStore;
use crate::ctap::client_pin::PIN_AUTH_LENGTH; use crate::ctap::client_pin::PIN_AUTH_LENGTH;
@@ -493,9 +494,14 @@ pub fn enterprise_attestation(env: &mut impl Env) -> Result<bool, Ctap2StatusCod
} }
/// Marks enterprise attestation as enabled. /// Marks enterprise attestation as enabled.
///
/// Doesn't check whether an attestation is setup because it depends on the RP id.
pub fn enable_enterprise_attestation(env: &mut impl Env) -> Result<(), Ctap2StatusCode> { pub fn enable_enterprise_attestation(env: &mut impl Env) -> 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)? { if !enterprise_attestation(env)? {
env.store().insert(key::ENTERPRISE_ATTESTATION, &[])?; env.store().insert(key::ENTERPRISE_ATTESTATION, &[])?;
} }
@@ -1135,13 +1141,14 @@ mod test {
#[test] #[test]
fn test_enterprise_attestation() { fn test_enterprise_attestation() {
let mut env = TestEnv::new(); let mut env = TestEnv::new();
env.set_attestation_id(attestation_store::Id::Enterprise);
let dummy_attestation = Attestation { let dummy_attestation = Attestation {
private_key: [0x41; key_material::ATTESTATION_PRIVATE_KEY_LENGTH], private_key: [0x41; key_material::ATTESTATION_PRIVATE_KEY_LENGTH],
certificate: vec![0xdd; 20], certificate: vec![0xdd; 20],
}; };
env.attestation_store() env.attestation_store()
.set(&attestation_store::Id::Batch, Some(&dummy_attestation)) .set(&attestation_store::Id::Enterprise, Some(&dummy_attestation))
.unwrap(); .unwrap();
assert!(!enterprise_attestation(&mut env).unwrap()); assert!(!enterprise_attestation(&mut env).unwrap());

14
src/env/test/mod.rs vendored
View File

@@ -36,6 +36,7 @@ pub struct TestEnv {
store: Store<BufferStorage>, store: Store<BufferStorage>,
upgrade_storage: Option<BufferUpgradeStorage>, upgrade_storage: Option<BufferUpgradeStorage>,
customization: TestCustomization, customization: TestCustomization,
attestation_id: attestation_store::Id,
} }
pub struct TestRng256 { pub struct TestRng256 {
@@ -106,12 +107,14 @@ impl TestEnv {
let store = Store::new(storage).ok().unwrap(); let store = Store::new(storage).ok().unwrap();
let upgrade_storage = Some(BufferUpgradeStorage::new().unwrap()); let upgrade_storage = Some(BufferUpgradeStorage::new().unwrap());
let customization = DEFAULT_CUSTOMIZATION.into(); let customization = DEFAULT_CUSTOMIZATION.into();
let attestation_id = attestation_store::Id::Batch;
TestEnv { TestEnv {
rng, rng,
user_presence, user_presence,
store, store,
upgrade_storage, upgrade_storage,
customization, customization,
attestation_id,
} }
} }
@@ -126,6 +129,10 @@ impl TestEnv {
pub fn rng(&mut self) -> &mut TestRng256 { pub fn rng(&mut self) -> &mut TestRng256 {
&mut self.rng &mut self.rng
} }
pub fn set_attestation_id(&mut self, id: attestation_store::Id) {
self.attestation_id = id;
}
} }
impl TestUserPresence { impl TestUserPresence {
@@ -149,7 +156,12 @@ impl FirmwareProtection for TestEnv {
} }
impl key_store::Helper 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 { impl Env for TestEnv {
type Rng = TestRng256; type Rng = TestRng256;

7
src/env/tock/mod.rs vendored
View File

@@ -195,7 +195,12 @@ impl FirmwareProtection for TockEnv {
} }
impl key_store::Helper 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 { impl Env for TockEnv {
type Rng = TockRng256; type Rng = TockRng256;

View File

@@ -12,10 +12,10 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
use crate::api::attestation_store::{self, Attestation, AttestationStore};
use crate::clock::CtapInstant; use crate::clock::CtapInstant;
use crate::ctap::command::{ use crate::ctap::command::{
AuthenticatorAttestationMaterial, AuthenticatorConfigParameters, AuthenticatorAttestationMaterial, AuthenticatorConfigParameters, Command,
AuthenticatorVendorConfigureParameters, Command,
}; };
use crate::ctap::data_formats::ConfigSubCommand; use crate::ctap::data_formats::ConfigSubCommand;
use crate::ctap::status_code::Ctap2StatusCode; use crate::ctap::status_code::Ctap2StatusCode;
@@ -32,22 +32,17 @@ pub fn enable_enterprise_attestation(
state: &mut CtapState, state: &mut CtapState,
env: &mut impl Env, env: &mut impl Env,
) -> Result<AuthenticatorAttestationMaterial, Ctap2StatusCode> { ) -> Result<AuthenticatorAttestationMaterial, Ctap2StatusCode> {
let dummy_key = [0x41; key_material::ATTESTATION_PRIVATE_KEY_LENGTH];
let dummy_cert = vec![0xdd; 20];
let attestation_material = AuthenticatorAttestationMaterial { let attestation_material = AuthenticatorAttestationMaterial {
certificate: dummy_cert, certificate: vec![0xdd; 20],
private_key: dummy_key, private_key: [0x41; key_material::ATTESTATION_PRIVATE_KEY_LENGTH],
}; };
let configure_params = AuthenticatorVendorConfigureParameters {
lockdown: false, let attestation = Attestation {
attestation_material: Some(attestation_material.clone()), private_key: attestation_material.private_key,
certificate: attestation_material.certificate.clone(),
}; };
#[cfg(feature = "vendor_hid")] env.attestation_store()
let vendor_channel = VENDOR_CHANNEL; .set(&attestation_store::Id::Enterprise, Some(&attestation))?;
#[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 { let config_params = AuthenticatorConfigParameters {
sub_command: ConfigSubCommand::EnableEnterpriseAttestation, sub_command: ConfigSubCommand::EnableEnterpriseAttestation,