Add vendor command to load certificate and priv key

This commit is contained in:
Jean-Michel Picod
2020-12-01 15:32:32 +01:00
parent 218188ad49
commit efb6378311
3 changed files with 287 additions and 7 deletions

View File

@@ -29,7 +29,7 @@ mod timed_permission;
use self::command::MAX_CREDENTIAL_COUNT_IN_LIST;
use self::command::{
AuthenticatorClientPinParameters, AuthenticatorGetAssertionParameters,
AuthenticatorMakeCredentialParameters, Command,
AuthenticatorMakeCredentialParameters, AuthenticatorVendorConfigureParameters, Command,
};
#[cfg(feature = "with_ctap2_1")]
use self::data_formats::AuthenticatorTransport;
@@ -44,7 +44,7 @@ use self::pin_protocol_v1::PinPermission;
use self::pin_protocol_v1::PinProtocolV1;
use self::response::{
AuthenticatorGetAssertionResponse, AuthenticatorGetInfoResponse,
AuthenticatorMakeCredentialResponse, ResponseData,
AuthenticatorMakeCredentialResponse, AuthenticatorVendorResponse, ResponseData,
};
use self::status_code::Ctap2StatusCode;
use self::storage::PersistentStore;
@@ -358,6 +358,10 @@ where
#[cfg(feature = "with_ctap2_1")]
Command::AuthenticatorSelection => self.process_selection(cid),
// TODO(kaczmarczyck) implement FIDO 2.1 commands
// Vendor specific commands
Command::AuthenticatorVendorConfigure(params) => {
self.process_vendor_configure(params, cid)
}
};
#[cfg(feature = "debug_ctap")]
writeln!(&mut Console::new(), "Sending response: {:#?}", response).unwrap();
@@ -919,6 +923,63 @@ where
Ok(ResponseData::AuthenticatorSelection)
}
fn process_vendor_configure(
&mut self,
params: AuthenticatorVendorConfigureParameters,
cid: ChannelID,
) -> Result<ResponseData, Ctap2StatusCode> {
(self.check_user_presence)(cid)?;
// Sanity checks
let has_priv_key = self.persistent_store.attestation_private_key()?.is_some();
let has_cert = self.persistent_store.attestation_certificate()?.is_some();
if params.attestation_material.is_some() {
let data = params.attestation_material.unwrap();
if !has_cert {
self.persistent_store
.set_attestation_certificate(&data.certificate)?;
}
if !has_priv_key {
self.persistent_store
.set_attestation_private_key(&data.private_key)?;
}
};
let has_priv_key = self.persistent_store.attestation_private_key()?.is_some();
let has_cert = self.persistent_store.attestation_certificate()?.is_some();
if params.lockdown {
// To avoid bricking the authenticator, we only allow lockdown
// to happen if both values are programmed or if both U2F/CTAP1 and
// batch attestation are disabled.
#[cfg(feature = "with_ctap1")]
let need_certificate = true;
#[cfg(not(feature = "with_ctap1"))]
let need_certificate = USE_BATCH_ATTESTATION;
if (need_certificate && !(has_priv_key && has_cert))
|| libtock_drivers::crp::protect().is_err()
{
Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR)
} else {
Ok(ResponseData::AuthenticatorVendor(
AuthenticatorVendorResponse {
cert_programmed: has_cert,
pkey_programmed: has_priv_key,
lockdown_enabled: true,
},
))
}
} else {
Ok(ResponseData::AuthenticatorVendor(
AuthenticatorVendorResponse {
cert_programmed: has_cert,
pkey_programmed: has_priv_key,
lockdown_enabled: false,
},
))
}
}
pub fn generate_auth_data(
&self,
rp_id_hash: &[u8],
@@ -941,6 +1002,7 @@ where
#[cfg(test)]
mod test {
use super::command::AuthenticatorAttestationMaterial;
use super::data_formats::{
CoseKey, GetAssertionExtensions, GetAssertionOptions, MakeCredentialExtensions,
MakeCredentialOptions, PublicKeyCredentialRpEntity, PublicKeyCredentialUserEntity,
@@ -2052,4 +2114,128 @@ mod test {
last_counter = next_counter;
}
}
#[test]
fn test_vendor_configure() {
let mut rng = ThreadRng256 {};
let user_immediately_present = |_| Ok(());
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
// Nothing should be configured at the beginning
let response = ctap_state.process_vendor_configure(
AuthenticatorVendorConfigureParameters {
lockdown: false,
attestation_material: None,
},
DUMMY_CHANNEL_ID,
);
assert_eq!(
response,
Ok(ResponseData::AuthenticatorVendor(
AuthenticatorVendorResponse {
cert_programmed: false,
pkey_programmed: false,
lockdown_enabled: false
}
))
);
// Inject dummy values
let dummy_key = [0x41u8; key_material::ATTESTATION_PRIVATE_KEY_LENGTH];
let dummy_cert = [0xddu8; 20];
let response = ctap_state.process_vendor_configure(
AuthenticatorVendorConfigureParameters {
lockdown: false,
attestation_material: Some(AuthenticatorAttestationMaterial {
certificate: dummy_cert.to_vec(),
private_key: dummy_key,
}),
},
DUMMY_CHANNEL_ID,
);
assert_eq!(
response,
Ok(ResponseData::AuthenticatorVendor(
AuthenticatorVendorResponse {
cert_programmed: true,
pkey_programmed: true,
lockdown_enabled: false
}
))
);
assert_eq!(
ctap_state
.persistent_store
.attestation_certificate()
.unwrap()
.unwrap(),
dummy_cert
);
assert_eq!(
ctap_state
.persistent_store
.attestation_private_key()
.unwrap()
.unwrap(),
dummy_key
);
// Try to inject other dummy values and check that intial values are retained.
let other_dummy_key = [0x44u8; key_material::ATTESTATION_PRIVATE_KEY_LENGTH];
let response = ctap_state.process_vendor_configure(
AuthenticatorVendorConfigureParameters {
lockdown: false,
attestation_material: Some(AuthenticatorAttestationMaterial {
certificate: dummy_cert.to_vec(),
private_key: other_dummy_key,
}),
},
DUMMY_CHANNEL_ID,
);
assert_eq!(
response,
Ok(ResponseData::AuthenticatorVendor(
AuthenticatorVendorResponse {
cert_programmed: true,
pkey_programmed: true,
lockdown_enabled: false
}
))
);
assert_eq!(
ctap_state
.persistent_store
.attestation_certificate()
.unwrap()
.unwrap(),
dummy_cert
);
assert_eq!(
ctap_state
.persistent_store
.attestation_private_key()
.unwrap()
.unwrap(),
dummy_key
);
// Now try to lock the device
let response = ctap_state.process_vendor_configure(
AuthenticatorVendorConfigureParameters {
lockdown: true,
attestation_material: None,
},
DUMMY_CHANNEL_ID,
);
assert_eq!(
response,
Ok(ResponseData::AuthenticatorVendor(
AuthenticatorVendorResponse {
cert_programmed: true,
pkey_programmed: true,
lockdown_enabled: true
}
))
);
}
}