adds the command logic for credential management

This commit is contained in:
Fabian Kaczmarczyck
2021-01-13 11:22:41 +01:00
parent 46b9a0262c
commit c6726660ac
7 changed files with 1366 additions and 42 deletions

View File

@@ -14,7 +14,8 @@
use super::data_formats::{
AuthenticatorTransport, CoseKey, CredentialProtectionPolicy, PackedAttestationStatement,
PublicKeyCredentialDescriptor, PublicKeyCredentialParameter, PublicKeyCredentialUserEntity,
PublicKeyCredentialDescriptor, PublicKeyCredentialParameter, PublicKeyCredentialRpEntity,
PublicKeyCredentialUserEntity,
};
use alloc::collections::BTreeMap;
use alloc::string::String;
@@ -30,6 +31,7 @@ pub enum ResponseData {
AuthenticatorGetInfo(AuthenticatorGetInfoResponse),
AuthenticatorClientPin(Option<AuthenticatorClientPinResponse>),
AuthenticatorReset,
AuthenticatorCredentialManagement(Option<AuthenticatorCredentialManagementResponse>),
AuthenticatorSelection,
AuthenticatorVendor(AuthenticatorVendorResponse),
}
@@ -41,9 +43,9 @@ impl From<ResponseData> for Option<cbor::Value> {
ResponseData::AuthenticatorGetAssertion(data) => Some(data.into()),
ResponseData::AuthenticatorGetNextAssertion(data) => Some(data.into()),
ResponseData::AuthenticatorGetInfo(data) => Some(data.into()),
ResponseData::AuthenticatorClientPin(Some(data)) => Some(data.into()),
ResponseData::AuthenticatorClientPin(None) => None,
ResponseData::AuthenticatorClientPin(data) => data.map(|d| d.into()),
ResponseData::AuthenticatorReset => None,
ResponseData::AuthenticatorCredentialManagement(data) => data.map(|d| d.into()),
ResponseData::AuthenticatorSelection => None,
ResponseData::AuthenticatorVendor(data) => Some(data.into()),
}
@@ -199,6 +201,54 @@ impl From<AuthenticatorClientPinResponse> for cbor::Value {
}
}
#[cfg_attr(test, derive(PartialEq))]
#[cfg_attr(any(test, feature = "debug_ctap"), derive(Debug))]
pub struct AuthenticatorCredentialManagementResponse {
pub existing_resident_credentials_count: Option<u64>,
pub max_possible_remaining_resident_credentials_count: Option<u64>,
pub rp: Option<PublicKeyCredentialRpEntity>,
pub rp_id_hash: Option<Vec<u8>>,
pub total_rps: Option<u64>,
pub user: Option<PublicKeyCredentialUserEntity>,
pub credential_id: Option<PublicKeyCredentialDescriptor>,
pub public_key: Option<CoseKey>,
pub total_credentials: Option<u64>,
pub cred_protect: Option<CredentialProtectionPolicy>,
pub large_blob_key: Option<Vec<u8>>,
}
impl From<AuthenticatorCredentialManagementResponse> for cbor::Value {
fn from(cred_management_response: AuthenticatorCredentialManagementResponse) -> Self {
let AuthenticatorCredentialManagementResponse {
existing_resident_credentials_count,
max_possible_remaining_resident_credentials_count,
rp,
rp_id_hash,
total_rps,
user,
credential_id,
public_key,
total_credentials,
cred_protect,
large_blob_key,
} = cred_management_response;
cbor_map_options! {
0x01 => existing_resident_credentials_count,
0x02 => max_possible_remaining_resident_credentials_count,
0x03 => rp,
0x04 => rp_id_hash,
0x05 => total_rps,
0x06 => user,
0x07 => credential_id,
0x08 => public_key.map(cbor::Value::from),
0x09 => total_credentials,
0x0A => cred_protect,
0x0B => large_blob_key,
}
}
}
#[cfg_attr(test, derive(PartialEq))]
#[cfg_attr(any(test, feature = "debug_ctap"), derive(Debug))]
pub struct AuthenticatorVendorResponse {
@@ -222,10 +272,11 @@ impl From<AuthenticatorVendorResponse> for cbor::Value {
#[cfg(test)]
mod test {
use super::super::data_formats::PackedAttestationStatement;
use super::super::data_formats::{PackedAttestationStatement, PublicKeyCredentialType};
use super::super::ES256_CRED_PARAM;
use super::*;
use cbor::{cbor_bytes, cbor_map};
use crypto::rng256::ThreadRng256;
#[test]
fn test_make_credential_into_cbor() {
@@ -379,6 +430,88 @@ mod test {
assert_eq!(response_cbor, None);
}
#[test]
fn test_used_credential_management_into_cbor() {
let cred_management_response = AuthenticatorCredentialManagementResponse {
existing_resident_credentials_count: None,
max_possible_remaining_resident_credentials_count: None,
rp: None,
rp_id_hash: None,
total_rps: None,
user: None,
credential_id: None,
public_key: None,
total_credentials: None,
cred_protect: None,
large_blob_key: None,
};
let response_cbor: Option<cbor::Value> =
ResponseData::AuthenticatorCredentialManagement(Some(cred_management_response)).into();
let expected_cbor = cbor_map_options! {};
assert_eq!(response_cbor, Some(expected_cbor));
}
#[test]
fn test_used_credential_management_optionals_into_cbor() {
let mut rng = ThreadRng256 {};
let sk = crypto::ecdh::SecKey::gensk(&mut rng);
let rp = PublicKeyCredentialRpEntity {
rp_id: String::from("example.com"),
rp_name: None,
rp_icon: None,
};
let user = PublicKeyCredentialUserEntity {
user_id: vec![0xFA, 0xB1, 0xA2],
user_name: None,
user_display_name: None,
user_icon: None,
};
let cred_descriptor = PublicKeyCredentialDescriptor {
key_type: PublicKeyCredentialType::PublicKey,
key_id: vec![0x1D; 32],
transports: None,
};
let pk = sk.genpk();
let cose_key = CoseKey::from(pk);
let cred_management_response = AuthenticatorCredentialManagementResponse {
existing_resident_credentials_count: Some(100),
max_possible_remaining_resident_credentials_count: Some(96),
rp: Some(rp.clone()),
rp_id_hash: Some(vec![0x1D; 32]),
total_rps: Some(3),
user: Some(user.clone()),
credential_id: Some(cred_descriptor.clone()),
public_key: Some(cose_key.clone()),
total_credentials: Some(2),
cred_protect: Some(CredentialProtectionPolicy::UserVerificationOptional),
large_blob_key: Some(vec![0xBB; 64]),
};
let response_cbor: Option<cbor::Value> =
ResponseData::AuthenticatorCredentialManagement(Some(cred_management_response)).into();
let expected_cbor = cbor_map_options! {
0x01 => 100,
0x02 => 96,
0x03 => rp,
0x04 => vec![0x1D; 32],
0x05 => 3,
0x06 => user,
0x07 => cred_descriptor,
0x08 => cbor::Value::from(cose_key),
0x09 => 2,
0x0A => 0x01,
0x0B => vec![0xBB; 64],
};
assert_eq!(response_cbor, Some(expected_cbor));
}
#[test]
fn test_empty_credential_management_into_cbor() {
let response_cbor: Option<cbor::Value> =
ResponseData::AuthenticatorCredentialManagement(None).into();
assert_eq!(response_cbor, None);
}
#[test]
fn test_selection_into_cbor() {
let response_cbor: Option<cbor::Value> = ResponseData::AuthenticatorSelection.into();