Merge pull request #274 from kaczmarczyck/extension-large-blobs

largeBlobKey extension
This commit is contained in:
kaczmarczyck
2021-01-31 11:44:58 +01:00
committed by GitHub
5 changed files with 233 additions and 53 deletions

View File

@@ -81,6 +81,7 @@ fn enumerate_credentials_response(
user_name,
user_icon,
cred_blob: _,
large_blob_key,
} = credential;
let user = PublicKeyCredentialUserEntity {
user_id: user_handle,
@@ -100,8 +101,7 @@ fn enumerate_credentials_response(
public_key: Some(public_key),
total_credentials,
cred_protect: cred_protect_policy,
// TODO(kaczmarczyck) add when largeBlobKey extension is implemented
large_blob_key: None,
large_blob_key,
..Default::default()
})
}
@@ -348,6 +348,7 @@ mod test {
user_name: Some("name".to_string()),
user_icon: Some("icon".to_string()),
cred_blob: None,
large_blob_key: None,
}
}

View File

@@ -282,6 +282,7 @@ pub struct MakeCredentialExtensions {
pub cred_protect: Option<CredentialProtectionPolicy>,
pub min_pin_length: bool,
pub cred_blob: Option<Vec<u8>>,
pub large_blob_key: Option<bool>,
}
impl TryFrom<cbor::Value> for MakeCredentialExtensions {
@@ -293,6 +294,7 @@ impl TryFrom<cbor::Value> for MakeCredentialExtensions {
"credBlob" => cred_blob,
"credProtect" => cred_protect,
"hmac-secret" => hmac_secret,
"largeBlobKey" => large_blob_key,
"minPinLength" => min_pin_length,
} = extract_map(cbor_value)?;
}
@@ -303,11 +305,18 @@ impl TryFrom<cbor::Value> for MakeCredentialExtensions {
.transpose()?;
let min_pin_length = min_pin_length.map_or(Ok(false), extract_bool)?;
let cred_blob = cred_blob.map(extract_byte_string).transpose()?;
let large_blob_key = large_blob_key.map(extract_bool).transpose()?;
if let Some(large_blob_key) = large_blob_key {
if !large_blob_key {
return Err(Ctap2StatusCode::CTAP2_ERR_INVALID_OPTION);
}
}
Ok(Self {
hmac_secret,
cred_protect,
min_pin_length,
cred_blob,
large_blob_key,
})
}
}
@@ -317,6 +326,7 @@ impl TryFrom<cbor::Value> for MakeCredentialExtensions {
pub struct GetAssertionExtensions {
pub hmac_secret: Option<GetAssertionHmacSecretInput>,
pub cred_blob: bool,
pub large_blob_key: Option<bool>,
}
impl TryFrom<cbor::Value> for GetAssertionExtensions {
@@ -327,6 +337,7 @@ impl TryFrom<cbor::Value> for GetAssertionExtensions {
let {
"credBlob" => cred_blob,
"hmac-secret" => hmac_secret,
"largeBlobKey" => large_blob_key,
} = extract_map(cbor_value)?;
}
@@ -334,9 +345,16 @@ impl TryFrom<cbor::Value> for GetAssertionExtensions {
.map(GetAssertionHmacSecretInput::try_from)
.transpose()?;
let cred_blob = cred_blob.map_or(Ok(false), extract_bool)?;
let large_blob_key = large_blob_key.map(extract_bool).transpose()?;
if let Some(large_blob_key) = large_blob_key {
if !large_blob_key {
return Err(Ctap2StatusCode::CTAP2_ERR_INVALID_OPTION);
}
}
Ok(Self {
hmac_secret,
cred_blob,
large_blob_key,
})
}
}
@@ -546,6 +564,7 @@ pub struct PublicKeyCredentialSource {
pub user_name: Option<String>,
pub user_icon: Option<String>,
pub cred_blob: Option<Vec<u8>>,
pub large_blob_key: Option<Vec<u8>>,
}
// We serialize credentials for the persistent storage using CBOR maps. Each field of a credential
@@ -561,6 +580,7 @@ enum PublicKeyCredentialSourceField {
UserName = 8,
UserIcon = 9,
CredBlob = 10,
LargeBlobKey = 11,
// When a field is removed, its tag should be reserved and not used for new fields. We document
// those reserved tags below.
// Reserved tags:
@@ -588,6 +608,7 @@ impl From<PublicKeyCredentialSource> for cbor::Value {
PublicKeyCredentialSourceField::UserName => credential.user_name,
PublicKeyCredentialSourceField::UserIcon => credential.user_icon,
PublicKeyCredentialSourceField::CredBlob => credential.cred_blob,
PublicKeyCredentialSourceField::LargeBlobKey => credential.large_blob_key,
}
}
}
@@ -608,6 +629,7 @@ impl TryFrom<cbor::Value> for PublicKeyCredentialSource {
PublicKeyCredentialSourceField::UserName => user_name,
PublicKeyCredentialSourceField::UserIcon => user_icon,
PublicKeyCredentialSourceField::CredBlob => cred_blob,
PublicKeyCredentialSourceField::LargeBlobKey => large_blob_key,
} = extract_map(cbor_value)?;
}
@@ -628,6 +650,7 @@ impl TryFrom<cbor::Value> for PublicKeyCredentialSource {
let user_name = user_name.map(extract_text_string).transpose()?;
let user_icon = user_icon.map(extract_text_string).transpose()?;
let cred_blob = cred_blob.map(extract_byte_string).transpose()?;
let large_blob_key = large_blob_key.map(extract_byte_string).transpose()?;
// We don't return whether there were unknown fields in the CBOR value. This means that
// deserialization is not injective. In particular deserialization is only an inverse of
// serialization at a given version of OpenSK. This is not a problem because:
@@ -650,6 +673,7 @@ impl TryFrom<cbor::Value> for PublicKeyCredentialSource {
user_name,
user_icon,
cred_blob,
large_blob_key,
})
}
}
@@ -1522,6 +1546,7 @@ mod test {
"credProtect" => CredentialProtectionPolicy::UserVerificationRequired,
"minPinLength" => true,
"credBlob" => vec![0xCB],
"largeBlobKey" => true,
};
let extensions = MakeCredentialExtensions::try_from(cbor_extensions);
let expected_extensions = MakeCredentialExtensions {
@@ -1529,6 +1554,7 @@ mod test {
cred_protect: Some(CredentialProtectionPolicy::UserVerificationRequired),
min_pin_length: true,
cred_blob: Some(vec![0xCB]),
large_blob_key: Some(true),
};
assert_eq!(extensions, Ok(expected_extensions));
}
@@ -1546,6 +1572,7 @@ mod test {
3 => vec![0x03; 16],
},
"credBlob" => true,
"largeBlobKey" => true,
};
let extensions = GetAssertionExtensions::try_from(cbor_extensions);
let expected_input = GetAssertionHmacSecretInput {
@@ -1556,6 +1583,7 @@ mod test {
let expected_extensions = GetAssertionExtensions {
hmac_secret: Some(expected_input),
cred_blob: true,
large_blob_key: Some(true),
};
assert_eq!(extensions, Ok(expected_extensions));
}
@@ -1849,6 +1877,7 @@ mod test {
user_name: None,
user_icon: None,
cred_blob: None,
large_blob_key: None,
};
assert_eq!(
@@ -1901,6 +1930,16 @@ mod test {
..credential
};
assert_eq!(
PublicKeyCredentialSource::try_from(cbor::Value::from(credential.clone())),
Ok(credential.clone())
);
let credential = PublicKeyCredentialSource {
large_blob_key: Some(vec![0x1B]),
..credential
};
assert_eq!(
PublicKeyCredentialSource::try_from(cbor::Value::from(credential.clone())),
Ok(credential)

View File

@@ -49,7 +49,7 @@ use self::response::{
AuthenticatorMakeCredentialResponse, AuthenticatorVendorResponse, ResponseData,
};
use self::status_code::Ctap2StatusCode;
use self::storage::{PersistentStore, MAX_RP_IDS_LENGTH};
use self::storage::{PersistentStore, MAX_LARGE_BLOB_ARRAY_SIZE, MAX_RP_IDS_LENGTH};
use self::timed_permission::TimedPermission;
#[cfg(feature = "with_ctap1")]
use self::timed_permission::U2fUserPresenceState;
@@ -427,6 +427,7 @@ where
user_name: None,
user_icon: None,
cred_blob: None,
large_blob_key: None,
}))
}
@@ -596,6 +597,10 @@ where
|| cred_protect_policy.is_some()
|| min_pin_length
|| has_cred_blob_output;
let large_blob_key = match (options.rk, extensions.large_blob_key) {
(true, Some(true)) => Some(self.rng.gen_uniform_u8x32().to_vec()),
_ => None,
};
let rp_id_hash = Sha256::hash(rp_id.as_bytes());
if let Some(exclude_list) = exclude_list {
@@ -674,6 +679,7 @@ where
.user_icon
.map(|s| truncate_to_char_boundary(&s, 64).to_string()),
cred_blob,
large_blob_key: large_blob_key.clone(),
};
self.persistent_store.store_credential(credential_source)?;
random_id
@@ -749,6 +755,7 @@ where
fmt: String::from("packed"),
auth_data,
att_stmt: attestation_statement,
large_blob_key,
},
))
}
@@ -806,6 +813,10 @@ where
return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR);
}
}
let large_blob_key = match extensions.large_blob_key {
Some(true) => credential.large_blob_key,
_ => None,
};
let mut signature_data = auth_data.clone();
signature_data.extend(client_data_hash);
@@ -841,6 +852,7 @@ where
signature: signature.to_asn1_der(),
user,
number_of_credentials: number_of_credentials.map(|n| n as u64),
large_blob_key,
},
))
}
@@ -1006,19 +1018,18 @@ where
fn process_get_info(&self) -> Result<ResponseData, Ctap2StatusCode> {
let mut options_map = BTreeMap::new();
// TODO(kaczmarczyck) add authenticatorConfig and credProtect options
options_map.insert(String::from("rk"), true);
options_map.insert(String::from("up"), true);
options_map.insert(
String::from("clientPin"),
self.persistent_store.pin_hash()?.is_some(),
);
options_map.insert(String::from("up"), true);
options_map.insert(String::from("pinUvAuthToken"), true);
options_map.insert(String::from("largeBlobs"), true);
options_map.insert(String::from("authnrCfg"), true);
options_map.insert(String::from("credMgmt"), true);
options_map.insert(String::from("setMinPINLength"), true);
options_map.insert(
String::from("forcePINChange"),
self.persistent_store.has_force_pin_change()?,
);
options_map.insert(String::from("makeCredUvNotRqd"), true);
Ok(ResponseData::AuthenticatorGetInfo(
AuthenticatorGetInfoResponse {
versions: vec![
@@ -1032,6 +1043,7 @@ where
String::from("credProtect"),
String::from("minPinLength"),
String::from("credBlob"),
String::from("largeBlobKey"),
]),
aaguid: self.persistent_store.aaguid()?,
options: Some(options_map),
@@ -1041,7 +1053,8 @@ where
max_credential_id_length: Some(CREDENTIAL_ID_SIZE as u64),
transports: Some(vec![AuthenticatorTransport::Usb]),
algorithms: Some(vec![ES256_CRED_PARAM]),
default_cred_protect: DEFAULT_CRED_PROTECT,
max_serialized_large_blob_array: Some(MAX_LARGE_BLOB_ARRAY_SIZE as u64),
force_pin_change: Some(self.persistent_store.has_force_pin_change()?),
min_pin_length: self.persistent_store.min_pin_length()?,
firmware_version: None,
max_cred_blob_length: Some(MAX_CRED_BLOB_LENGTH as u64),
@@ -1214,6 +1227,7 @@ mod test {
fmt,
auth_data,
att_stmt,
large_blob_key,
} = make_credential_response;
// The expected response is split to only assert the non-random parts.
assert_eq!(fmt, "packed");
@@ -1234,6 +1248,7 @@ mod test {
expected_extension_cbor
);
assert_eq!(att_stmt.alg, SignatureAlgorithm::ES256 as i64);
assert_eq!(large_blob_key, None);
}
_ => panic!("Invalid response type"),
}
@@ -1258,15 +1273,19 @@ mod test {
String::from("credProtect"),
String::from("minPinLength"),
String::from("credBlob"),
String::from("largeBlobKey"),
]],
0x03 => ctap_state.persistent_store.aaguid().unwrap(),
0x04 => cbor_map! {
"rk" => true,
"up" => true,
"clientPin" => false,
"up" => true,
"pinUvAuthToken" => true,
"largeBlobs" => true,
"authnrCfg" => true,
"credMgmt" => true,
"setMinPINLength" => true,
"forcePINChange" => false,
"makeCredUvNotRqd" => true,
},
0x05 => MAX_MSG_SIZE as u64,
0x06 => cbor_array_vec![vec![1]],
@@ -1274,7 +1293,8 @@ mod test {
0x08 => CREDENTIAL_ID_SIZE as u64,
0x09 => cbor_array_vec![vec!["usb"]],
0x0A => cbor_array_vec![vec![ES256_CRED_PARAM]],
0x0C => DEFAULT_CRED_PROTECT.map(|c| c as u64),
0x0B => MAX_LARGE_BLOB_ARRAY_SIZE as u64,
0x0C => false,
0x0D => ctap_state.persistent_store.min_pin_length().unwrap() as u64,
0x0F => MAX_CRED_BLOB_LENGTH as u64,
0x10 => MAX_RP_IDS_LENGTH as u64,
@@ -1336,10 +1356,8 @@ mod test {
policy: CredentialProtectionPolicy,
) -> AuthenticatorMakeCredentialParameters {
let extensions = MakeCredentialExtensions {
hmac_secret: false,
cred_protect: Some(policy),
min_pin_length: false,
cred_blob: None,
..Default::default()
};
let mut make_credential_params = create_minimal_make_credential_parameters();
make_credential_params.extensions = extensions;
@@ -1424,6 +1442,7 @@ mod test {
user_name: None,
user_icon: None,
cred_blob: None,
large_blob_key: None,
};
assert!(ctap_state
.persistent_store
@@ -1504,9 +1523,7 @@ mod test {
let extensions = MakeCredentialExtensions {
hmac_secret: true,
cred_protect: None,
min_pin_length: false,
cred_blob: None,
..Default::default()
};
let mut make_credential_params = create_minimal_make_credential_parameters();
make_credential_params.options.rk = false;
@@ -1534,9 +1551,7 @@ mod test {
let extensions = MakeCredentialExtensions {
hmac_secret: true,
cred_protect: None,
min_pin_length: false,
cred_blob: None,
..Default::default()
};
let mut make_credential_params = create_minimal_make_credential_parameters();
make_credential_params.extensions = extensions;
@@ -1563,10 +1578,8 @@ mod test {
// First part: The extension is ignored, since the RP ID is not on the list.
let extensions = MakeCredentialExtensions {
hmac_secret: false,
cred_protect: None,
min_pin_length: true,
cred_blob: None,
..Default::default()
};
let mut make_credential_params = create_minimal_make_credential_parameters();
make_credential_params.extensions = extensions;
@@ -1589,10 +1602,8 @@ mod test {
);
let extensions = MakeCredentialExtensions {
hmac_secret: false,
cred_protect: None,
min_pin_length: true,
cred_blob: None,
..Default::default()
};
let mut make_credential_params = create_minimal_make_credential_parameters();
make_credential_params.extensions = extensions;
@@ -1618,10 +1629,8 @@ mod test {
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
let extensions = MakeCredentialExtensions {
hmac_secret: false,
cred_protect: None,
min_pin_length: false,
cred_blob: Some(vec![0xCB]),
..Default::default()
};
let mut make_credential_params = create_minimal_make_credential_parameters();
make_credential_params.extensions = extensions;
@@ -1656,10 +1665,8 @@ mod test {
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
let extensions = MakeCredentialExtensions {
hmac_secret: false,
cred_protect: None,
min_pin_length: false,
cred_blob: Some(vec![0xCB; MAX_CRED_BLOB_LENGTH + 1]),
..Default::default()
};
let mut make_credential_params = create_minimal_make_credential_parameters();
make_credential_params.extensions = extensions;
@@ -1687,6 +1694,39 @@ mod test {
assert_eq!(stored_credential.cred_blob, None);
}
#[test]
fn test_process_make_credential_large_blob_key() {
let mut rng = ThreadRng256 {};
let user_immediately_present = |_| Ok(());
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
let extensions = MakeCredentialExtensions {
large_blob_key: Some(true),
..Default::default()
};
let mut make_credential_params = create_minimal_make_credential_parameters();
make_credential_params.extensions = extensions;
let make_credential_response =
ctap_state.process_make_credential(make_credential_params, DUMMY_CHANNEL_ID);
let large_blob_key = match make_credential_response.unwrap() {
ResponseData::AuthenticatorMakeCredential(make_credential_response) => {
make_credential_response.large_blob_key.unwrap()
}
_ => panic!("Invalid response type"),
};
assert_eq!(large_blob_key.len(), 32);
let mut iter_result = Ok(());
let iter = ctap_state
.persistent_store
.iter_credentials(&mut iter_result)
.unwrap();
// There is only 1 credential, so last is good enough.
let (_, stored_credential) = iter.last().unwrap();
iter_result.unwrap();
assert_eq!(stored_credential.large_blob_key.unwrap(), large_blob_key);
}
#[test]
fn test_process_make_credential_cancelled() {
let mut rng = ThreadRng256 {};
@@ -1828,9 +1868,7 @@ mod test {
let make_extensions = MakeCredentialExtensions {
hmac_secret: true,
cred_protect: None,
min_pin_length: false,
cred_blob: None,
..Default::default()
};
let mut make_credential_params = create_minimal_make_credential_parameters();
make_credential_params.options.rk = false;
@@ -1857,7 +1895,7 @@ mod test {
};
let get_extensions = GetAssertionExtensions {
hmac_secret: Some(hmac_secret_input),
cred_blob: false,
..Default::default()
};
let cred_desc = PublicKeyCredentialDescriptor {
@@ -1898,9 +1936,7 @@ mod test {
let make_extensions = MakeCredentialExtensions {
hmac_secret: true,
cred_protect: None,
min_pin_length: false,
cred_blob: None,
..Default::default()
};
let mut make_credential_params = create_minimal_make_credential_parameters();
make_credential_params.extensions = make_extensions;
@@ -1916,7 +1952,7 @@ mod test {
};
let get_extensions = GetAssertionExtensions {
hmac_secret: Some(hmac_secret_input),
cred_blob: false,
..Default::default()
};
let get_assertion_params = AuthenticatorGetAssertionParameters {
@@ -1970,6 +2006,7 @@ mod test {
user_name: None,
user_icon: None,
cred_blob: None,
large_blob_key: None,
};
assert!(ctap_state
.persistent_store
@@ -2033,6 +2070,7 @@ mod test {
user_name: None,
user_icon: None,
cred_blob: None,
large_blob_key: None,
};
assert!(ctap_state
.persistent_store
@@ -2082,6 +2120,7 @@ mod test {
user_name: None,
user_icon: None,
cred_blob: Some(vec![0xCB]),
large_blob_key: None,
};
assert!(ctap_state
.persistent_store
@@ -2089,8 +2128,8 @@ mod test {
.is_ok());
let extensions = GetAssertionExtensions {
hmac_secret: None,
cred_blob: true,
..Default::default()
};
let get_assertion_params = AuthenticatorGetAssertionParameters {
rp_id: String::from("example.com"),
@@ -2125,6 +2164,63 @@ mod test {
);
}
#[test]
fn test_process_get_assertion_with_large_blob_key() {
let mut rng = ThreadRng256 {};
let private_key = crypto::ecdsa::SecKey::gensk(&mut rng);
let credential_id = rng.gen_uniform_u8x32().to_vec();
let user_immediately_present = |_| Ok(());
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
let credential = PublicKeyCredentialSource {
key_type: PublicKeyCredentialType::PublicKey,
credential_id,
private_key,
rp_id: String::from("example.com"),
user_handle: vec![0x1D],
user_display_name: None,
cred_protect_policy: None,
creation_order: 0,
user_name: None,
user_icon: None,
cred_blob: None,
large_blob_key: Some(vec![0x1C; 32]),
};
assert!(ctap_state
.persistent_store
.store_credential(credential)
.is_ok());
let extensions = GetAssertionExtensions {
large_blob_key: Some(true),
..Default::default()
};
let get_assertion_params = AuthenticatorGetAssertionParameters {
rp_id: String::from("example.com"),
client_data_hash: vec![0xCD],
allow_list: None,
extensions,
options: GetAssertionOptions {
up: false,
uv: false,
},
pin_uv_auth_param: None,
pin_uv_auth_protocol: None,
};
let get_assertion_response = ctap_state.process_get_assertion(
get_assertion_params,
DUMMY_CHANNEL_ID,
DUMMY_CLOCK_VALUE,
);
let large_blob_key = match get_assertion_response.unwrap() {
ResponseData::AuthenticatorGetAssertion(get_assertion_response) => {
get_assertion_response.large_blob_key.unwrap()
}
_ => panic!("Invalid response type"),
};
assert_eq!(large_blob_key, vec![0x1C; 32]);
}
#[test]
fn test_process_get_next_assertion_two_credentials_with_uv() {
let mut rng = ThreadRng256 {};
@@ -2369,6 +2465,7 @@ mod test {
user_name: None,
user_icon: None,
cred_blob: None,
large_blob_key: None,
};
assert!(ctap_state
.persistent_store

View File

@@ -63,6 +63,7 @@ pub struct AuthenticatorMakeCredentialResponse {
pub fmt: String,
pub auth_data: Vec<u8>,
pub att_stmt: PackedAttestationStatement,
pub large_blob_key: Option<Vec<u8>>,
}
impl From<AuthenticatorMakeCredentialResponse> for cbor::Value {
@@ -71,12 +72,14 @@ impl From<AuthenticatorMakeCredentialResponse> for cbor::Value {
fmt,
auth_data,
att_stmt,
large_blob_key,
} = make_credential_response;
cbor_map_options! {
0x01 => fmt,
0x02 => auth_data,
0x03 => att_stmt,
0x05 => large_blob_key,
}
}
}
@@ -89,6 +92,7 @@ pub struct AuthenticatorGetAssertionResponse {
pub signature: Vec<u8>,
pub user: Option<PublicKeyCredentialUserEntity>,
pub number_of_credentials: Option<u64>,
pub large_blob_key: Option<Vec<u8>>,
}
impl From<AuthenticatorGetAssertionResponse> for cbor::Value {
@@ -99,6 +103,7 @@ impl From<AuthenticatorGetAssertionResponse> for cbor::Value {
signature,
user,
number_of_credentials,
large_blob_key,
} = get_assertion_response;
cbor_map_options! {
@@ -107,6 +112,7 @@ impl From<AuthenticatorGetAssertionResponse> for cbor::Value {
0x03 => signature,
0x04 => user,
0x05 => number_of_credentials,
0x07 => large_blob_key,
}
}
}
@@ -124,7 +130,8 @@ pub struct AuthenticatorGetInfoResponse {
pub max_credential_id_length: Option<u64>,
pub transports: Option<Vec<AuthenticatorTransport>>,
pub algorithms: Option<Vec<PublicKeyCredentialParameter>>,
pub default_cred_protect: Option<CredentialProtectionPolicy>,
pub max_serialized_large_blob_array: Option<u64>,
pub force_pin_change: Option<bool>,
pub min_pin_length: u8,
pub firmware_version: Option<u64>,
pub max_cred_blob_length: Option<u64>,
@@ -145,7 +152,8 @@ impl From<AuthenticatorGetInfoResponse> for cbor::Value {
max_credential_id_length,
transports,
algorithms,
default_cred_protect,
max_serialized_large_blob_array,
force_pin_change,
min_pin_length,
firmware_version,
max_cred_blob_length,
@@ -172,7 +180,8 @@ impl From<AuthenticatorGetInfoResponse> for cbor::Value {
0x08 => max_credential_id_length,
0x09 => transports.map(|vec| cbor_array_vec!(vec)),
0x0A => algorithms.map(|vec| cbor_array_vec!(vec)),
0x0C => default_cred_protect.map(|p| p as u64),
0x0B => max_serialized_large_blob_array,
0x0C => force_pin_change,
0x0D => min_pin_length as u64,
0x0E => firmware_version,
0x0F => max_cred_blob_length,
@@ -297,7 +306,7 @@ mod test {
use super::super::data_formats::{PackedAttestationStatement, PublicKeyCredentialType};
use super::super::ES256_CRED_PARAM;
use super::*;
use cbor::{cbor_bytes, cbor_map};
use cbor::{cbor_array, cbor_bytes, cbor_map};
use crypto::rng256::ThreadRng256;
#[test]
@@ -320,6 +329,7 @@ mod test {
fmt: "packed".to_string(),
auth_data: vec![0xAD],
att_stmt,
large_blob_key: Some(vec![0x1B]),
};
let response_cbor: Option<cbor::Value> =
ResponseData::AuthenticatorMakeCredential(make_credential_response).into();
@@ -327,24 +337,50 @@ mod test {
0x01 => "packed",
0x02 => vec![0xAD],
0x03 => cbor_packed_attestation_statement,
0x05 => vec![0x1B],
};
assert_eq!(response_cbor, Some(expected_cbor));
}
#[test]
fn test_get_assertion_into_cbor() {
let pub_key_cred_descriptor = PublicKeyCredentialDescriptor {
key_type: PublicKeyCredentialType::PublicKey,
key_id: vec![0x2D, 0x2D, 0x2D, 0x2D],
transports: Some(vec![AuthenticatorTransport::Usb]),
};
let user = PublicKeyCredentialUserEntity {
user_id: vec![0x1D, 0x1D, 0x1D, 0x1D],
user_name: Some("foo".to_string()),
user_display_name: Some("bar".to_string()),
user_icon: Some("example.com/foo/icon.png".to_string()),
};
let get_assertion_response = AuthenticatorGetAssertionResponse {
credential: None,
credential: Some(pub_key_cred_descriptor),
auth_data: vec![0xAD],
signature: vec![0x51],
user: None,
number_of_credentials: None,
user: Some(user),
number_of_credentials: Some(2),
large_blob_key: Some(vec![0x1B]),
};
let response_cbor: Option<cbor::Value> =
ResponseData::AuthenticatorGetAssertion(get_assertion_response).into();
let expected_cbor = cbor_map_options! {
0x01 => cbor_map! {
"type" => "public-key",
"id" => vec![0x2D, 0x2D, 0x2D, 0x2D],
"transports" => cbor_array!["usb"],
},
0x02 => vec![0xAD],
0x03 => vec![0x51],
0x04 => cbor_map! {
"id" => vec![0x1D, 0x1D, 0x1D, 0x1D],
"name" => "foo".to_string(),
"displayName" => "bar".to_string(),
"icon" => "example.com/foo/icon.png".to_string(),
},
0x05 => 2,
0x07 => vec![0x1B],
};
assert_eq!(response_cbor, Some(expected_cbor));
}
@@ -363,7 +399,8 @@ mod test {
max_credential_id_length: None,
transports: None,
algorithms: None,
default_cred_protect: None,
max_serialized_large_blob_array: None,
force_pin_change: None,
min_pin_length: 4,
firmware_version: None,
max_cred_blob_length: None,
@@ -395,7 +432,8 @@ mod test {
max_credential_id_length: Some(256),
transports: Some(vec![AuthenticatorTransport::Usb]),
algorithms: Some(vec![ES256_CRED_PARAM]),
default_cred_protect: Some(CredentialProtectionPolicy::UserVerificationRequired),
max_serialized_large_blob_array: Some(1024),
force_pin_change: Some(false),
min_pin_length: 4,
firmware_version: Some(0),
max_cred_blob_length: Some(1024),
@@ -415,7 +453,8 @@ mod test {
0x08 => 256,
0x09 => cbor_array_vec![vec!["usb"]],
0x0A => cbor_array_vec![vec![ES256_CRED_PARAM]],
0x0C => CredentialProtectionPolicy::UserVerificationRequired as u64,
0x0B => 1024,
0x0C => false,
0x0D => 4,
0x0E => 0,
0x0F => 1024,

View File

@@ -756,6 +756,7 @@ mod test {
user_name: None,
user_icon: None,
cred_blob: None,
large_blob_key: None,
}
}
@@ -973,6 +974,7 @@ mod test {
user_name: None,
user_icon: None,
cred_blob: None,
large_blob_key: None,
};
assert_eq!(found_credential, Some(expected_credential));
}
@@ -995,6 +997,7 @@ mod test {
user_name: None,
user_icon: None,
cred_blob: None,
large_blob_key: None,
};
assert!(persistent_store.store_credential(credential).is_ok());
@@ -1321,6 +1324,7 @@ mod test {
user_name: None,
user_icon: None,
cred_blob: None,
large_blob_key: None,
};
let serialized = serialize_credential(credential.clone()).unwrap();
let reconstructed = deserialize_credential(&serialized).unwrap();