Add vendor command to load certificate and priv key
This commit is contained in:
190
src/ctap/mod.rs
190
src/ctap/mod.rs
@@ -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
|
||||
}
|
||||
))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user