Enterprise attestation testing (#465)
* fix enterprise attestation check * returns storage errors
This commit is contained in:
@@ -118,12 +118,11 @@ pub trait Customization {
|
|||||||
///
|
///
|
||||||
/// - If the mode is VendorFacilitated, enterprise_attestation_mode() must be non-empty.
|
/// - If the mode is VendorFacilitated, enterprise_attestation_mode() must be non-empty.
|
||||||
///
|
///
|
||||||
/// This list is only considered if the enterprise attestation mode is
|
/// This list is only considered if enterprise attestation is used.
|
||||||
/// VendorFacilitated.
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
fn enterprise_rp_id_list(&self) -> Vec<String>;
|
fn enterprise_rp_id_list(&self) -> Vec<String>;
|
||||||
|
|
||||||
// Returns whether the rp_id is contained in enterprise_rp_id_list().
|
/// Returns whether the rp_id is contained in enterprise_rp_id_list().
|
||||||
fn is_enterprise_rp_id(&self, rp_id: &str) -> bool;
|
fn is_enterprise_rp_id(&self, rp_id: &str) -> bool;
|
||||||
|
|
||||||
/// Maximum message size send for CTAP commands.
|
/// Maximum message size send for CTAP commands.
|
||||||
@@ -303,11 +302,18 @@ pub fn is_valid(customization: &impl Customization) -> bool {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// enterprise_rp_id_list() should be non-empty in vendor facilitated mode, and empty otherwise.
|
// enterprise_rp_id_list() should be non-empty in vendor facilitated mode.
|
||||||
if matches!(
|
if matches!(
|
||||||
customization.enterprise_attestation_mode(),
|
customization.enterprise_attestation_mode(),
|
||||||
Some(EnterpriseAttestationMode::VendorFacilitated)
|
Some(EnterpriseAttestationMode::VendorFacilitated)
|
||||||
) == customization.enterprise_rp_id_list().is_empty()
|
) && customization.enterprise_rp_id_list().is_empty()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// enterprise_rp_id_list() should be empty without an enterprise attestation mode.
|
||||||
|
if customization.enterprise_attestation_mode().is_none()
|
||||||
|
&& !customization.enterprise_rp_id_list().is_empty()
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -230,6 +230,7 @@ impl TryFrom<cbor::Value> for AuthenticatorMakeCredentialParameters {
|
|||||||
let pin_uv_auth_protocol = pin_uv_auth_protocol
|
let pin_uv_auth_protocol = pin_uv_auth_protocol
|
||||||
.map(PinUvAuthProtocol::try_from)
|
.map(PinUvAuthProtocol::try_from)
|
||||||
.transpose()?;
|
.transpose()?;
|
||||||
|
// We don't convert into EnterpriseAttestationMode to maintain the correct order of errors.
|
||||||
let enterprise_attestation = enterprise_attestation.map(extract_unsigned).transpose()?;
|
let enterprise_attestation = enterprise_attestation.map(extract_unsigned).transpose()?;
|
||||||
|
|
||||||
Ok(AuthenticatorMakeCredentialParameters {
|
Ok(AuthenticatorMakeCredentialParameters {
|
||||||
|
|||||||
136
src/ctap/mod.rs
136
src/ctap/mod.rs
@@ -697,8 +697,8 @@ impl CtapState {
|
|||||||
(
|
(
|
||||||
EnterpriseAttestationMode::PlatformManaged,
|
EnterpriseAttestationMode::PlatformManaged,
|
||||||
EnterpriseAttestationMode::PlatformManaged,
|
EnterpriseAttestationMode::PlatformManaged,
|
||||||
) => env.customization().is_enterprise_rp_id(&rp_id),
|
) => true,
|
||||||
_ => true,
|
_ => env.customization().is_enterprise_rp_id(&rp_id),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
@@ -2048,6 +2048,138 @@ mod test {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_ep(make_credential_response: Result<ResponseData, Ctap2StatusCode>, has_ep: bool) {
|
||||||
|
let ep_att = if has_ep { Some(true) } else { None };
|
||||||
|
match make_credential_response.unwrap() {
|
||||||
|
ResponseData::AuthenticatorMakeCredential(make_credential_response) => {
|
||||||
|
assert_eq!(make_credential_response.ep_att, ep_att);
|
||||||
|
}
|
||||||
|
_ => panic!("Invalid response type"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_process_make_credential_with_enterprise_attestation_vendor_facilitated() {
|
||||||
|
let mut env = TestEnv::new();
|
||||||
|
env.customization_mut().enterprise_attestation_mode =
|
||||||
|
Some(EnterpriseAttestationMode::VendorFacilitated);
|
||||||
|
env.customization_mut().enterprise_rp_id_list = vec!["example.com".to_string()];
|
||||||
|
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
|
||||||
|
|
||||||
|
let mut key_bytes = [0; 32];
|
||||||
|
let private_key = crypto::ecdsa::SecKey::gensk(env.rng());
|
||||||
|
private_key.to_bytes(array_mut_ref!(key_bytes, 0, 32));
|
||||||
|
storage::set_attestation_certificate(&mut env, &[0xCC]).unwrap();
|
||||||
|
storage::set_attestation_private_key(&mut env, &key_bytes).unwrap();
|
||||||
|
storage::enable_enterprise_attestation(&mut env).unwrap();
|
||||||
|
|
||||||
|
let mut make_credential_params = create_minimal_make_credential_parameters();
|
||||||
|
make_credential_params.enterprise_attestation = Some(1);
|
||||||
|
make_credential_params.rp = PublicKeyCredentialRpEntity {
|
||||||
|
rp_id: "counter-example.com".to_string(),
|
||||||
|
rp_name: None,
|
||||||
|
rp_icon: None,
|
||||||
|
};
|
||||||
|
let make_credential_response =
|
||||||
|
ctap_state.process_make_credential(&mut env, make_credential_params, DUMMY_CHANNEL);
|
||||||
|
check_ep(make_credential_response, false);
|
||||||
|
|
||||||
|
let mut make_credential_params = create_minimal_make_credential_parameters();
|
||||||
|
make_credential_params.enterprise_attestation = Some(2);
|
||||||
|
make_credential_params.rp = PublicKeyCredentialRpEntity {
|
||||||
|
rp_id: "counter-example.com".to_string(),
|
||||||
|
rp_name: None,
|
||||||
|
rp_icon: None,
|
||||||
|
};
|
||||||
|
let make_credential_response =
|
||||||
|
ctap_state.process_make_credential(&mut env, make_credential_params, DUMMY_CHANNEL);
|
||||||
|
check_ep(make_credential_response, false);
|
||||||
|
|
||||||
|
let mut make_credential_params = create_minimal_make_credential_parameters();
|
||||||
|
make_credential_params.enterprise_attestation = Some(1);
|
||||||
|
let make_credential_response =
|
||||||
|
ctap_state.process_make_credential(&mut env, make_credential_params, DUMMY_CHANNEL);
|
||||||
|
check_ep(make_credential_response, true);
|
||||||
|
|
||||||
|
let mut make_credential_params = create_minimal_make_credential_parameters();
|
||||||
|
make_credential_params.enterprise_attestation = Some(2);
|
||||||
|
let make_credential_response =
|
||||||
|
ctap_state.process_make_credential(&mut env, make_credential_params, DUMMY_CHANNEL);
|
||||||
|
check_ep(make_credential_response, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_process_make_credential_with_enterprise_attestation_platform_managed() {
|
||||||
|
let mut env = TestEnv::new();
|
||||||
|
env.customization_mut().enterprise_attestation_mode =
|
||||||
|
Some(EnterpriseAttestationMode::PlatformManaged);
|
||||||
|
env.customization_mut().enterprise_rp_id_list = vec!["example.com".to_string()];
|
||||||
|
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
|
||||||
|
|
||||||
|
let mut key_bytes = [0; 32];
|
||||||
|
let private_key = crypto::ecdsa::SecKey::gensk(env.rng());
|
||||||
|
private_key.to_bytes(array_mut_ref!(key_bytes, 0, 32));
|
||||||
|
storage::set_attestation_certificate(&mut env, &[0xCC]).unwrap();
|
||||||
|
storage::set_attestation_private_key(&mut env, &key_bytes).unwrap();
|
||||||
|
storage::enable_enterprise_attestation(&mut env).unwrap();
|
||||||
|
|
||||||
|
let mut make_credential_params = create_minimal_make_credential_parameters();
|
||||||
|
make_credential_params.enterprise_attestation = Some(1);
|
||||||
|
make_credential_params.rp = PublicKeyCredentialRpEntity {
|
||||||
|
rp_id: "counter-example.com".to_string(),
|
||||||
|
rp_name: None,
|
||||||
|
rp_icon: None,
|
||||||
|
};
|
||||||
|
let make_credential_response =
|
||||||
|
ctap_state.process_make_credential(&mut env, make_credential_params, DUMMY_CHANNEL);
|
||||||
|
check_ep(make_credential_response, false);
|
||||||
|
|
||||||
|
let mut make_credential_params = create_minimal_make_credential_parameters();
|
||||||
|
make_credential_params.enterprise_attestation = Some(1);
|
||||||
|
let make_credential_response =
|
||||||
|
ctap_state.process_make_credential(&mut env, make_credential_params, DUMMY_CHANNEL);
|
||||||
|
check_ep(make_credential_response, true);
|
||||||
|
|
||||||
|
let mut make_credential_params = create_minimal_make_credential_parameters();
|
||||||
|
make_credential_params.enterprise_attestation = Some(2);
|
||||||
|
let make_credential_response =
|
||||||
|
ctap_state.process_make_credential(&mut env, make_credential_params, DUMMY_CHANNEL);
|
||||||
|
check_ep(make_credential_response, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_process_make_credential_with_enterprise_attestation_invalid() {
|
||||||
|
let mut env = TestEnv::new();
|
||||||
|
env.customization_mut().enterprise_attestation_mode =
|
||||||
|
Some(EnterpriseAttestationMode::PlatformManaged);
|
||||||
|
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
|
||||||
|
|
||||||
|
let mut make_credential_params = create_minimal_make_credential_parameters();
|
||||||
|
make_credential_params.enterprise_attestation = Some(2);
|
||||||
|
let make_credential_response =
|
||||||
|
ctap_state.process_make_credential(&mut env, make_credential_params, DUMMY_CHANNEL);
|
||||||
|
assert_eq!(
|
||||||
|
make_credential_response,
|
||||||
|
Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut key_bytes = [0; 32];
|
||||||
|
let private_key = crypto::ecdsa::SecKey::gensk(env.rng());
|
||||||
|
private_key.to_bytes(array_mut_ref!(key_bytes, 0, 32));
|
||||||
|
storage::set_attestation_certificate(&mut env, &[0xCC]).unwrap();
|
||||||
|
storage::set_attestation_private_key(&mut env, &key_bytes).unwrap();
|
||||||
|
storage::enable_enterprise_attestation(&mut env).unwrap();
|
||||||
|
|
||||||
|
let mut make_credential_params = create_minimal_make_credential_parameters();
|
||||||
|
make_credential_params.enterprise_attestation = Some(3);
|
||||||
|
let make_credential_response =
|
||||||
|
ctap_state.process_make_credential(&mut env, make_credential_params, DUMMY_CHANNEL);
|
||||||
|
assert_eq!(
|
||||||
|
make_credential_response,
|
||||||
|
Err(Ctap2StatusCode::CTAP2_ERR_INVALID_OPTION)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_process_make_credential_cancelled() {
|
fn test_process_make_credential_cancelled() {
|
||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
|
|||||||
@@ -577,6 +577,9 @@ pub fn enterprise_attestation(env: &mut impl Env) -> Result<bool, Ctap2StatusCod
|
|||||||
|
|
||||||
/// Marks enterprise attestation as enabled.
|
/// Marks enterprise attestation as enabled.
|
||||||
pub fn enable_enterprise_attestation(env: &mut impl Env) -> Result<(), Ctap2StatusCode> {
|
pub fn enable_enterprise_attestation(env: &mut impl Env) -> 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)? {
|
if !enterprise_attestation(env)? {
|
||||||
env.store().insert(key::ENTERPRISE_ATTESTATION, &[])?;
|
env.store().insert(key::ENTERPRISE_ATTESTATION, &[])?;
|
||||||
}
|
}
|
||||||
@@ -1079,8 +1082,8 @@ mod test {
|
|||||||
init(&mut env).unwrap();
|
init(&mut env).unwrap();
|
||||||
|
|
||||||
// Make sure the attestation are absent. There is no batch attestation in tests.
|
// Make sure the attestation are absent. There is no batch attestation in tests.
|
||||||
assert!(attestation_private_key(&mut env,).unwrap().is_none());
|
assert!(attestation_private_key(&mut env).unwrap().is_none());
|
||||||
assert!(attestation_certificate(&mut env,).unwrap().is_none());
|
assert!(attestation_certificate(&mut env).unwrap().is_none());
|
||||||
|
|
||||||
// Make sure the persistent keys are initialized to dummy values.
|
// Make sure the persistent keys are initialized to dummy values.
|
||||||
let dummy_key = [0x41u8; key_material::ATTESTATION_PRIVATE_KEY_LENGTH];
|
let dummy_key = [0x41u8; key_material::ATTESTATION_PRIVATE_KEY_LENGTH];
|
||||||
@@ -1233,6 +1236,18 @@ mod test {
|
|||||||
fn test_enterprise_attestation() {
|
fn test_enterprise_attestation() {
|
||||||
let mut env = TestEnv::new();
|
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();
|
||||||
|
|
||||||
assert!(!enterprise_attestation(&mut env).unwrap());
|
assert!(!enterprise_attestation(&mut env).unwrap());
|
||||||
assert_eq!(enable_enterprise_attestation(&mut env), Ok(()));
|
assert_eq!(enable_enterprise_attestation(&mut env), Ok(()));
|
||||||
assert!(enterprise_attestation(&mut env).unwrap());
|
assert!(enterprise_attestation(&mut env).unwrap());
|
||||||
|
|||||||
Reference in New Issue
Block a user