new extension entry for largeBlobKey
This commit is contained in:
@@ -81,6 +81,7 @@ fn enumerate_credentials_response(
|
|||||||
user_name,
|
user_name,
|
||||||
user_icon,
|
user_icon,
|
||||||
cred_blob: _,
|
cred_blob: _,
|
||||||
|
large_blob_key,
|
||||||
} = credential;
|
} = credential;
|
||||||
let user = PublicKeyCredentialUserEntity {
|
let user = PublicKeyCredentialUserEntity {
|
||||||
user_id: user_handle,
|
user_id: user_handle,
|
||||||
@@ -100,8 +101,7 @@ fn enumerate_credentials_response(
|
|||||||
public_key: Some(public_key),
|
public_key: Some(public_key),
|
||||||
total_credentials,
|
total_credentials,
|
||||||
cred_protect: cred_protect_policy,
|
cred_protect: cred_protect_policy,
|
||||||
// TODO(kaczmarczyck) add when largeBlobKey extension is implemented
|
large_blob_key,
|
||||||
large_blob_key: None,
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -348,6 +348,7 @@ mod test {
|
|||||||
user_name: Some("name".to_string()),
|
user_name: Some("name".to_string()),
|
||||||
user_icon: Some("icon".to_string()),
|
user_icon: Some("icon".to_string()),
|
||||||
cred_blob: None,
|
cred_blob: None,
|
||||||
|
large_blob_key: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -282,6 +282,7 @@ pub struct MakeCredentialExtensions {
|
|||||||
pub cred_protect: Option<CredentialProtectionPolicy>,
|
pub cred_protect: Option<CredentialProtectionPolicy>,
|
||||||
pub min_pin_length: bool,
|
pub min_pin_length: bool,
|
||||||
pub cred_blob: Option<Vec<u8>>,
|
pub cred_blob: Option<Vec<u8>>,
|
||||||
|
pub large_blob_key: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<cbor::Value> for MakeCredentialExtensions {
|
impl TryFrom<cbor::Value> for MakeCredentialExtensions {
|
||||||
@@ -293,6 +294,7 @@ impl TryFrom<cbor::Value> for MakeCredentialExtensions {
|
|||||||
"credBlob" => cred_blob,
|
"credBlob" => cred_blob,
|
||||||
"credProtect" => cred_protect,
|
"credProtect" => cred_protect,
|
||||||
"hmac-secret" => hmac_secret,
|
"hmac-secret" => hmac_secret,
|
||||||
|
"largeBlobKey" => large_blob_key,
|
||||||
"minPinLength" => min_pin_length,
|
"minPinLength" => min_pin_length,
|
||||||
} = extract_map(cbor_value)?;
|
} = extract_map(cbor_value)?;
|
||||||
}
|
}
|
||||||
@@ -303,11 +305,18 @@ impl TryFrom<cbor::Value> for MakeCredentialExtensions {
|
|||||||
.transpose()?;
|
.transpose()?;
|
||||||
let min_pin_length = min_pin_length.map_or(Ok(false), extract_bool)?;
|
let min_pin_length = min_pin_length.map_or(Ok(false), extract_bool)?;
|
||||||
let cred_blob = cred_blob.map(extract_byte_string).transpose()?;
|
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 {
|
Ok(Self {
|
||||||
hmac_secret,
|
hmac_secret,
|
||||||
cred_protect,
|
cred_protect,
|
||||||
min_pin_length,
|
min_pin_length,
|
||||||
cred_blob,
|
cred_blob,
|
||||||
|
large_blob_key,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -317,6 +326,7 @@ impl TryFrom<cbor::Value> for MakeCredentialExtensions {
|
|||||||
pub struct GetAssertionExtensions {
|
pub struct GetAssertionExtensions {
|
||||||
pub hmac_secret: Option<GetAssertionHmacSecretInput>,
|
pub hmac_secret: Option<GetAssertionHmacSecretInput>,
|
||||||
pub cred_blob: bool,
|
pub cred_blob: bool,
|
||||||
|
pub large_blob_key: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<cbor::Value> for GetAssertionExtensions {
|
impl TryFrom<cbor::Value> for GetAssertionExtensions {
|
||||||
@@ -327,6 +337,7 @@ impl TryFrom<cbor::Value> for GetAssertionExtensions {
|
|||||||
let {
|
let {
|
||||||
"credBlob" => cred_blob,
|
"credBlob" => cred_blob,
|
||||||
"hmac-secret" => hmac_secret,
|
"hmac-secret" => hmac_secret,
|
||||||
|
"largeBlobKey" => large_blob_key,
|
||||||
} = extract_map(cbor_value)?;
|
} = extract_map(cbor_value)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -334,9 +345,16 @@ impl TryFrom<cbor::Value> for GetAssertionExtensions {
|
|||||||
.map(GetAssertionHmacSecretInput::try_from)
|
.map(GetAssertionHmacSecretInput::try_from)
|
||||||
.transpose()?;
|
.transpose()?;
|
||||||
let cred_blob = cred_blob.map_or(Ok(false), extract_bool)?;
|
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 {
|
Ok(Self {
|
||||||
hmac_secret,
|
hmac_secret,
|
||||||
cred_blob,
|
cred_blob,
|
||||||
|
large_blob_key,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -546,6 +564,7 @@ pub struct PublicKeyCredentialSource {
|
|||||||
pub user_name: Option<String>,
|
pub user_name: Option<String>,
|
||||||
pub user_icon: Option<String>,
|
pub user_icon: Option<String>,
|
||||||
pub cred_blob: Option<Vec<u8>>,
|
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
|
// We serialize credentials for the persistent storage using CBOR maps. Each field of a credential
|
||||||
@@ -561,6 +580,7 @@ enum PublicKeyCredentialSourceField {
|
|||||||
UserName = 8,
|
UserName = 8,
|
||||||
UserIcon = 9,
|
UserIcon = 9,
|
||||||
CredBlob = 10,
|
CredBlob = 10,
|
||||||
|
LargeBlobKey = 11,
|
||||||
// When a field is removed, its tag should be reserved and not used for new fields. We document
|
// When a field is removed, its tag should be reserved and not used for new fields. We document
|
||||||
// those reserved tags below.
|
// those reserved tags below.
|
||||||
// Reserved tags:
|
// Reserved tags:
|
||||||
@@ -588,6 +608,7 @@ impl From<PublicKeyCredentialSource> for cbor::Value {
|
|||||||
PublicKeyCredentialSourceField::UserName => credential.user_name,
|
PublicKeyCredentialSourceField::UserName => credential.user_name,
|
||||||
PublicKeyCredentialSourceField::UserIcon => credential.user_icon,
|
PublicKeyCredentialSourceField::UserIcon => credential.user_icon,
|
||||||
PublicKeyCredentialSourceField::CredBlob => credential.cred_blob,
|
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::UserName => user_name,
|
||||||
PublicKeyCredentialSourceField::UserIcon => user_icon,
|
PublicKeyCredentialSourceField::UserIcon => user_icon,
|
||||||
PublicKeyCredentialSourceField::CredBlob => cred_blob,
|
PublicKeyCredentialSourceField::CredBlob => cred_blob,
|
||||||
|
PublicKeyCredentialSourceField::LargeBlobKey => large_blob_key,
|
||||||
} = extract_map(cbor_value)?;
|
} = 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_name = user_name.map(extract_text_string).transpose()?;
|
||||||
let user_icon = user_icon.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 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
|
// 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
|
// 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:
|
// 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_name,
|
||||||
user_icon,
|
user_icon,
|
||||||
cred_blob,
|
cred_blob,
|
||||||
|
large_blob_key,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1522,6 +1546,7 @@ mod test {
|
|||||||
"credProtect" => CredentialProtectionPolicy::UserVerificationRequired,
|
"credProtect" => CredentialProtectionPolicy::UserVerificationRequired,
|
||||||
"minPinLength" => true,
|
"minPinLength" => true,
|
||||||
"credBlob" => vec![0xCB],
|
"credBlob" => vec![0xCB],
|
||||||
|
"largeBlobKey" => true,
|
||||||
};
|
};
|
||||||
let extensions = MakeCredentialExtensions::try_from(cbor_extensions);
|
let extensions = MakeCredentialExtensions::try_from(cbor_extensions);
|
||||||
let expected_extensions = MakeCredentialExtensions {
|
let expected_extensions = MakeCredentialExtensions {
|
||||||
@@ -1529,6 +1554,7 @@ mod test {
|
|||||||
cred_protect: Some(CredentialProtectionPolicy::UserVerificationRequired),
|
cred_protect: Some(CredentialProtectionPolicy::UserVerificationRequired),
|
||||||
min_pin_length: true,
|
min_pin_length: true,
|
||||||
cred_blob: Some(vec![0xCB]),
|
cred_blob: Some(vec![0xCB]),
|
||||||
|
large_blob_key: Some(true),
|
||||||
};
|
};
|
||||||
assert_eq!(extensions, Ok(expected_extensions));
|
assert_eq!(extensions, Ok(expected_extensions));
|
||||||
}
|
}
|
||||||
@@ -1546,6 +1572,7 @@ mod test {
|
|||||||
3 => vec![0x03; 16],
|
3 => vec![0x03; 16],
|
||||||
},
|
},
|
||||||
"credBlob" => true,
|
"credBlob" => true,
|
||||||
|
"largeBlobKey" => true,
|
||||||
};
|
};
|
||||||
let extensions = GetAssertionExtensions::try_from(cbor_extensions);
|
let extensions = GetAssertionExtensions::try_from(cbor_extensions);
|
||||||
let expected_input = GetAssertionHmacSecretInput {
|
let expected_input = GetAssertionHmacSecretInput {
|
||||||
@@ -1556,6 +1583,7 @@ mod test {
|
|||||||
let expected_extensions = GetAssertionExtensions {
|
let expected_extensions = GetAssertionExtensions {
|
||||||
hmac_secret: Some(expected_input),
|
hmac_secret: Some(expected_input),
|
||||||
cred_blob: true,
|
cred_blob: true,
|
||||||
|
large_blob_key: Some(true),
|
||||||
};
|
};
|
||||||
assert_eq!(extensions, Ok(expected_extensions));
|
assert_eq!(extensions, Ok(expected_extensions));
|
||||||
}
|
}
|
||||||
@@ -1849,6 +1877,7 @@ mod test {
|
|||||||
user_name: None,
|
user_name: None,
|
||||||
user_icon: None,
|
user_icon: None,
|
||||||
cred_blob: None,
|
cred_blob: None,
|
||||||
|
large_blob_key: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -1901,6 +1930,16 @@ mod test {
|
|||||||
..credential
|
..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!(
|
assert_eq!(
|
||||||
PublicKeyCredentialSource::try_from(cbor::Value::from(credential.clone())),
|
PublicKeyCredentialSource::try_from(cbor::Value::from(credential.clone())),
|
||||||
Ok(credential)
|
Ok(credential)
|
||||||
|
|||||||
179
src/ctap/mod.rs
179
src/ctap/mod.rs
@@ -49,7 +49,7 @@ use self::response::{
|
|||||||
AuthenticatorMakeCredentialResponse, AuthenticatorVendorResponse, ResponseData,
|
AuthenticatorMakeCredentialResponse, AuthenticatorVendorResponse, ResponseData,
|
||||||
};
|
};
|
||||||
use self::status_code::Ctap2StatusCode;
|
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;
|
use self::timed_permission::TimedPermission;
|
||||||
#[cfg(feature = "with_ctap1")]
|
#[cfg(feature = "with_ctap1")]
|
||||||
use self::timed_permission::U2fUserPresenceState;
|
use self::timed_permission::U2fUserPresenceState;
|
||||||
@@ -427,6 +427,7 @@ where
|
|||||||
user_name: None,
|
user_name: None,
|
||||||
user_icon: None,
|
user_icon: None,
|
||||||
cred_blob: None,
|
cred_blob: None,
|
||||||
|
large_blob_key: None,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -596,6 +597,10 @@ where
|
|||||||
|| cred_protect_policy.is_some()
|
|| cred_protect_policy.is_some()
|
||||||
|| min_pin_length
|
|| min_pin_length
|
||||||
|| has_cred_blob_output;
|
|| 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());
|
let rp_id_hash = Sha256::hash(rp_id.as_bytes());
|
||||||
if let Some(exclude_list) = exclude_list {
|
if let Some(exclude_list) = exclude_list {
|
||||||
@@ -674,6 +679,7 @@ where
|
|||||||
.user_icon
|
.user_icon
|
||||||
.map(|s| truncate_to_char_boundary(&s, 64).to_string()),
|
.map(|s| truncate_to_char_boundary(&s, 64).to_string()),
|
||||||
cred_blob,
|
cred_blob,
|
||||||
|
large_blob_key: large_blob_key.clone(),
|
||||||
};
|
};
|
||||||
self.persistent_store.store_credential(credential_source)?;
|
self.persistent_store.store_credential(credential_source)?;
|
||||||
random_id
|
random_id
|
||||||
@@ -749,6 +755,7 @@ where
|
|||||||
fmt: String::from("packed"),
|
fmt: String::from("packed"),
|
||||||
auth_data,
|
auth_data,
|
||||||
att_stmt: attestation_statement,
|
att_stmt: attestation_statement,
|
||||||
|
large_blob_key,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -806,6 +813,10 @@ where
|
|||||||
return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR);
|
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();
|
let mut signature_data = auth_data.clone();
|
||||||
signature_data.extend(client_data_hash);
|
signature_data.extend(client_data_hash);
|
||||||
@@ -841,6 +852,7 @@ where
|
|||||||
signature: signature.to_asn1_der(),
|
signature: signature.to_asn1_der(),
|
||||||
user,
|
user,
|
||||||
number_of_credentials: number_of_credentials.map(|n| n as u64),
|
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> {
|
fn process_get_info(&self) -> Result<ResponseData, Ctap2StatusCode> {
|
||||||
let mut options_map = BTreeMap::new();
|
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("rk"), true);
|
||||||
options_map.insert(String::from("up"), true);
|
|
||||||
options_map.insert(
|
options_map.insert(
|
||||||
String::from("clientPin"),
|
String::from("clientPin"),
|
||||||
self.persistent_store.pin_hash()?.is_some(),
|
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("credMgmt"), true);
|
||||||
options_map.insert(String::from("setMinPINLength"), true);
|
options_map.insert(String::from("setMinPINLength"), true);
|
||||||
options_map.insert(
|
options_map.insert(String::from("makeCredUvNotRqd"), true);
|
||||||
String::from("forcePINChange"),
|
|
||||||
self.persistent_store.has_force_pin_change()?,
|
|
||||||
);
|
|
||||||
Ok(ResponseData::AuthenticatorGetInfo(
|
Ok(ResponseData::AuthenticatorGetInfo(
|
||||||
AuthenticatorGetInfoResponse {
|
AuthenticatorGetInfoResponse {
|
||||||
versions: vec![
|
versions: vec![
|
||||||
@@ -1032,6 +1043,7 @@ where
|
|||||||
String::from("credProtect"),
|
String::from("credProtect"),
|
||||||
String::from("minPinLength"),
|
String::from("minPinLength"),
|
||||||
String::from("credBlob"),
|
String::from("credBlob"),
|
||||||
|
String::from("largeBlobKey"),
|
||||||
]),
|
]),
|
||||||
aaguid: self.persistent_store.aaguid()?,
|
aaguid: self.persistent_store.aaguid()?,
|
||||||
options: Some(options_map),
|
options: Some(options_map),
|
||||||
@@ -1041,7 +1053,8 @@ where
|
|||||||
max_credential_id_length: Some(CREDENTIAL_ID_SIZE as u64),
|
max_credential_id_length: Some(CREDENTIAL_ID_SIZE as u64),
|
||||||
transports: Some(vec![AuthenticatorTransport::Usb]),
|
transports: Some(vec![AuthenticatorTransport::Usb]),
|
||||||
algorithms: Some(vec![ES256_CRED_PARAM]),
|
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()?,
|
min_pin_length: self.persistent_store.min_pin_length()?,
|
||||||
firmware_version: None,
|
firmware_version: None,
|
||||||
max_cred_blob_length: Some(MAX_CRED_BLOB_LENGTH as u64),
|
max_cred_blob_length: Some(MAX_CRED_BLOB_LENGTH as u64),
|
||||||
@@ -1214,6 +1227,7 @@ mod test {
|
|||||||
fmt,
|
fmt,
|
||||||
auth_data,
|
auth_data,
|
||||||
att_stmt,
|
att_stmt,
|
||||||
|
large_blob_key,
|
||||||
} = make_credential_response;
|
} = make_credential_response;
|
||||||
// The expected response is split to only assert the non-random parts.
|
// The expected response is split to only assert the non-random parts.
|
||||||
assert_eq!(fmt, "packed");
|
assert_eq!(fmt, "packed");
|
||||||
@@ -1234,6 +1248,7 @@ mod test {
|
|||||||
expected_extension_cbor
|
expected_extension_cbor
|
||||||
);
|
);
|
||||||
assert_eq!(att_stmt.alg, SignatureAlgorithm::ES256 as i64);
|
assert_eq!(att_stmt.alg, SignatureAlgorithm::ES256 as i64);
|
||||||
|
assert_eq!(large_blob_key, None);
|
||||||
}
|
}
|
||||||
_ => panic!("Invalid response type"),
|
_ => panic!("Invalid response type"),
|
||||||
}
|
}
|
||||||
@@ -1258,15 +1273,19 @@ mod test {
|
|||||||
String::from("credProtect"),
|
String::from("credProtect"),
|
||||||
String::from("minPinLength"),
|
String::from("minPinLength"),
|
||||||
String::from("credBlob"),
|
String::from("credBlob"),
|
||||||
|
String::from("largeBlobKey"),
|
||||||
]],
|
]],
|
||||||
0x03 => ctap_state.persistent_store.aaguid().unwrap(),
|
0x03 => ctap_state.persistent_store.aaguid().unwrap(),
|
||||||
0x04 => cbor_map! {
|
0x04 => cbor_map! {
|
||||||
"rk" => true,
|
"rk" => true,
|
||||||
"up" => true,
|
|
||||||
"clientPin" => false,
|
"clientPin" => false,
|
||||||
|
"up" => true,
|
||||||
|
"pinUvAuthToken" => true,
|
||||||
|
"largeBlobs" => true,
|
||||||
|
"authnrCfg" => true,
|
||||||
"credMgmt" => true,
|
"credMgmt" => true,
|
||||||
"setMinPINLength" => true,
|
"setMinPINLength" => true,
|
||||||
"forcePINChange" => false,
|
"makeCredUvNotRqd" => true,
|
||||||
},
|
},
|
||||||
0x05 => MAX_MSG_SIZE as u64,
|
0x05 => MAX_MSG_SIZE as u64,
|
||||||
0x06 => cbor_array_vec![vec![1]],
|
0x06 => cbor_array_vec![vec![1]],
|
||||||
@@ -1274,7 +1293,8 @@ mod test {
|
|||||||
0x08 => CREDENTIAL_ID_SIZE as u64,
|
0x08 => CREDENTIAL_ID_SIZE as u64,
|
||||||
0x09 => cbor_array_vec![vec!["usb"]],
|
0x09 => cbor_array_vec![vec!["usb"]],
|
||||||
0x0A => cbor_array_vec![vec![ES256_CRED_PARAM]],
|
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,
|
0x0D => ctap_state.persistent_store.min_pin_length().unwrap() as u64,
|
||||||
0x0F => MAX_CRED_BLOB_LENGTH as u64,
|
0x0F => MAX_CRED_BLOB_LENGTH as u64,
|
||||||
0x10 => MAX_RP_IDS_LENGTH as u64,
|
0x10 => MAX_RP_IDS_LENGTH as u64,
|
||||||
@@ -1336,10 +1356,8 @@ mod test {
|
|||||||
policy: CredentialProtectionPolicy,
|
policy: CredentialProtectionPolicy,
|
||||||
) -> AuthenticatorMakeCredentialParameters {
|
) -> AuthenticatorMakeCredentialParameters {
|
||||||
let extensions = MakeCredentialExtensions {
|
let extensions = MakeCredentialExtensions {
|
||||||
hmac_secret: false,
|
|
||||||
cred_protect: Some(policy),
|
cred_protect: Some(policy),
|
||||||
min_pin_length: false,
|
..Default::default()
|
||||||
cred_blob: None,
|
|
||||||
};
|
};
|
||||||
let mut make_credential_params = create_minimal_make_credential_parameters();
|
let mut make_credential_params = create_minimal_make_credential_parameters();
|
||||||
make_credential_params.extensions = extensions;
|
make_credential_params.extensions = extensions;
|
||||||
@@ -1424,6 +1442,7 @@ mod test {
|
|||||||
user_name: None,
|
user_name: None,
|
||||||
user_icon: None,
|
user_icon: None,
|
||||||
cred_blob: None,
|
cred_blob: None,
|
||||||
|
large_blob_key: None,
|
||||||
};
|
};
|
||||||
assert!(ctap_state
|
assert!(ctap_state
|
||||||
.persistent_store
|
.persistent_store
|
||||||
@@ -1504,9 +1523,7 @@ mod test {
|
|||||||
|
|
||||||
let extensions = MakeCredentialExtensions {
|
let extensions = MakeCredentialExtensions {
|
||||||
hmac_secret: true,
|
hmac_secret: true,
|
||||||
cred_protect: None,
|
..Default::default()
|
||||||
min_pin_length: false,
|
|
||||||
cred_blob: None,
|
|
||||||
};
|
};
|
||||||
let mut make_credential_params = create_minimal_make_credential_parameters();
|
let mut make_credential_params = create_minimal_make_credential_parameters();
|
||||||
make_credential_params.options.rk = false;
|
make_credential_params.options.rk = false;
|
||||||
@@ -1534,9 +1551,7 @@ mod test {
|
|||||||
|
|
||||||
let extensions = MakeCredentialExtensions {
|
let extensions = MakeCredentialExtensions {
|
||||||
hmac_secret: true,
|
hmac_secret: true,
|
||||||
cred_protect: None,
|
..Default::default()
|
||||||
min_pin_length: false,
|
|
||||||
cred_blob: None,
|
|
||||||
};
|
};
|
||||||
let mut make_credential_params = create_minimal_make_credential_parameters();
|
let mut make_credential_params = create_minimal_make_credential_parameters();
|
||||||
make_credential_params.extensions = extensions;
|
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.
|
// First part: The extension is ignored, since the RP ID is not on the list.
|
||||||
let extensions = MakeCredentialExtensions {
|
let extensions = MakeCredentialExtensions {
|
||||||
hmac_secret: false,
|
|
||||||
cred_protect: None,
|
|
||||||
min_pin_length: true,
|
min_pin_length: true,
|
||||||
cred_blob: None,
|
..Default::default()
|
||||||
};
|
};
|
||||||
let mut make_credential_params = create_minimal_make_credential_parameters();
|
let mut make_credential_params = create_minimal_make_credential_parameters();
|
||||||
make_credential_params.extensions = extensions;
|
make_credential_params.extensions = extensions;
|
||||||
@@ -1589,10 +1602,8 @@ mod test {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let extensions = MakeCredentialExtensions {
|
let extensions = MakeCredentialExtensions {
|
||||||
hmac_secret: false,
|
|
||||||
cred_protect: None,
|
|
||||||
min_pin_length: true,
|
min_pin_length: true,
|
||||||
cred_blob: None,
|
..Default::default()
|
||||||
};
|
};
|
||||||
let mut make_credential_params = create_minimal_make_credential_parameters();
|
let mut make_credential_params = create_minimal_make_credential_parameters();
|
||||||
make_credential_params.extensions = extensions;
|
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 mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
|
||||||
|
|
||||||
let extensions = MakeCredentialExtensions {
|
let extensions = MakeCredentialExtensions {
|
||||||
hmac_secret: false,
|
|
||||||
cred_protect: None,
|
|
||||||
min_pin_length: false,
|
|
||||||
cred_blob: Some(vec![0xCB]),
|
cred_blob: Some(vec![0xCB]),
|
||||||
|
..Default::default()
|
||||||
};
|
};
|
||||||
let mut make_credential_params = create_minimal_make_credential_parameters();
|
let mut make_credential_params = create_minimal_make_credential_parameters();
|
||||||
make_credential_params.extensions = extensions;
|
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 mut ctap_state = CtapState::new(&mut rng, user_immediately_present, DUMMY_CLOCK_VALUE);
|
||||||
|
|
||||||
let extensions = MakeCredentialExtensions {
|
let extensions = MakeCredentialExtensions {
|
||||||
hmac_secret: false,
|
|
||||||
cred_protect: None,
|
|
||||||
min_pin_length: false,
|
|
||||||
cred_blob: Some(vec![0xCB; MAX_CRED_BLOB_LENGTH + 1]),
|
cred_blob: Some(vec![0xCB; MAX_CRED_BLOB_LENGTH + 1]),
|
||||||
|
..Default::default()
|
||||||
};
|
};
|
||||||
let mut make_credential_params = create_minimal_make_credential_parameters();
|
let mut make_credential_params = create_minimal_make_credential_parameters();
|
||||||
make_credential_params.extensions = extensions;
|
make_credential_params.extensions = extensions;
|
||||||
@@ -1687,6 +1694,39 @@ mod test {
|
|||||||
assert_eq!(stored_credential.cred_blob, None);
|
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]
|
#[test]
|
||||||
fn test_process_make_credential_cancelled() {
|
fn test_process_make_credential_cancelled() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
@@ -1828,9 +1868,7 @@ mod test {
|
|||||||
|
|
||||||
let make_extensions = MakeCredentialExtensions {
|
let make_extensions = MakeCredentialExtensions {
|
||||||
hmac_secret: true,
|
hmac_secret: true,
|
||||||
cred_protect: None,
|
..Default::default()
|
||||||
min_pin_length: false,
|
|
||||||
cred_blob: None,
|
|
||||||
};
|
};
|
||||||
let mut make_credential_params = create_minimal_make_credential_parameters();
|
let mut make_credential_params = create_minimal_make_credential_parameters();
|
||||||
make_credential_params.options.rk = false;
|
make_credential_params.options.rk = false;
|
||||||
@@ -1857,7 +1895,7 @@ mod test {
|
|||||||
};
|
};
|
||||||
let get_extensions = GetAssertionExtensions {
|
let get_extensions = GetAssertionExtensions {
|
||||||
hmac_secret: Some(hmac_secret_input),
|
hmac_secret: Some(hmac_secret_input),
|
||||||
cred_blob: false,
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let cred_desc = PublicKeyCredentialDescriptor {
|
let cred_desc = PublicKeyCredentialDescriptor {
|
||||||
@@ -1898,9 +1936,7 @@ mod test {
|
|||||||
|
|
||||||
let make_extensions = MakeCredentialExtensions {
|
let make_extensions = MakeCredentialExtensions {
|
||||||
hmac_secret: true,
|
hmac_secret: true,
|
||||||
cred_protect: None,
|
..Default::default()
|
||||||
min_pin_length: false,
|
|
||||||
cred_blob: None,
|
|
||||||
};
|
};
|
||||||
let mut make_credential_params = create_minimal_make_credential_parameters();
|
let mut make_credential_params = create_minimal_make_credential_parameters();
|
||||||
make_credential_params.extensions = make_extensions;
|
make_credential_params.extensions = make_extensions;
|
||||||
@@ -1916,7 +1952,7 @@ mod test {
|
|||||||
};
|
};
|
||||||
let get_extensions = GetAssertionExtensions {
|
let get_extensions = GetAssertionExtensions {
|
||||||
hmac_secret: Some(hmac_secret_input),
|
hmac_secret: Some(hmac_secret_input),
|
||||||
cred_blob: false,
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let get_assertion_params = AuthenticatorGetAssertionParameters {
|
let get_assertion_params = AuthenticatorGetAssertionParameters {
|
||||||
@@ -1970,6 +2006,7 @@ mod test {
|
|||||||
user_name: None,
|
user_name: None,
|
||||||
user_icon: None,
|
user_icon: None,
|
||||||
cred_blob: None,
|
cred_blob: None,
|
||||||
|
large_blob_key: None,
|
||||||
};
|
};
|
||||||
assert!(ctap_state
|
assert!(ctap_state
|
||||||
.persistent_store
|
.persistent_store
|
||||||
@@ -2033,6 +2070,7 @@ mod test {
|
|||||||
user_name: None,
|
user_name: None,
|
||||||
user_icon: None,
|
user_icon: None,
|
||||||
cred_blob: None,
|
cred_blob: None,
|
||||||
|
large_blob_key: None,
|
||||||
};
|
};
|
||||||
assert!(ctap_state
|
assert!(ctap_state
|
||||||
.persistent_store
|
.persistent_store
|
||||||
@@ -2082,6 +2120,7 @@ mod test {
|
|||||||
user_name: None,
|
user_name: None,
|
||||||
user_icon: None,
|
user_icon: None,
|
||||||
cred_blob: Some(vec![0xCB]),
|
cred_blob: Some(vec![0xCB]),
|
||||||
|
large_blob_key: None,
|
||||||
};
|
};
|
||||||
assert!(ctap_state
|
assert!(ctap_state
|
||||||
.persistent_store
|
.persistent_store
|
||||||
@@ -2089,8 +2128,8 @@ mod test {
|
|||||||
.is_ok());
|
.is_ok());
|
||||||
|
|
||||||
let extensions = GetAssertionExtensions {
|
let extensions = GetAssertionExtensions {
|
||||||
hmac_secret: None,
|
|
||||||
cred_blob: true,
|
cred_blob: true,
|
||||||
|
..Default::default()
|
||||||
};
|
};
|
||||||
let get_assertion_params = AuthenticatorGetAssertionParameters {
|
let get_assertion_params = AuthenticatorGetAssertionParameters {
|
||||||
rp_id: String::from("example.com"),
|
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]
|
#[test]
|
||||||
fn test_process_get_next_assertion_two_credentials_with_uv() {
|
fn test_process_get_next_assertion_two_credentials_with_uv() {
|
||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
@@ -2369,6 +2465,7 @@ mod test {
|
|||||||
user_name: None,
|
user_name: None,
|
||||||
user_icon: None,
|
user_icon: None,
|
||||||
cred_blob: None,
|
cred_blob: None,
|
||||||
|
large_blob_key: None,
|
||||||
};
|
};
|
||||||
assert!(ctap_state
|
assert!(ctap_state
|
||||||
.persistent_store
|
.persistent_store
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ pub struct AuthenticatorMakeCredentialResponse {
|
|||||||
pub fmt: String,
|
pub fmt: String,
|
||||||
pub auth_data: Vec<u8>,
|
pub auth_data: Vec<u8>,
|
||||||
pub att_stmt: PackedAttestationStatement,
|
pub att_stmt: PackedAttestationStatement,
|
||||||
|
pub large_blob_key: Option<Vec<u8>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<AuthenticatorMakeCredentialResponse> for cbor::Value {
|
impl From<AuthenticatorMakeCredentialResponse> for cbor::Value {
|
||||||
@@ -71,12 +72,14 @@ impl From<AuthenticatorMakeCredentialResponse> for cbor::Value {
|
|||||||
fmt,
|
fmt,
|
||||||
auth_data,
|
auth_data,
|
||||||
att_stmt,
|
att_stmt,
|
||||||
|
large_blob_key,
|
||||||
} = make_credential_response;
|
} = make_credential_response;
|
||||||
|
|
||||||
cbor_map_options! {
|
cbor_map_options! {
|
||||||
0x01 => fmt,
|
0x01 => fmt,
|
||||||
0x02 => auth_data,
|
0x02 => auth_data,
|
||||||
0x03 => att_stmt,
|
0x03 => att_stmt,
|
||||||
|
0x05 => large_blob_key,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -89,6 +92,7 @@ pub struct AuthenticatorGetAssertionResponse {
|
|||||||
pub signature: Vec<u8>,
|
pub signature: Vec<u8>,
|
||||||
pub user: Option<PublicKeyCredentialUserEntity>,
|
pub user: Option<PublicKeyCredentialUserEntity>,
|
||||||
pub number_of_credentials: Option<u64>,
|
pub number_of_credentials: Option<u64>,
|
||||||
|
pub large_blob_key: Option<Vec<u8>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<AuthenticatorGetAssertionResponse> for cbor::Value {
|
impl From<AuthenticatorGetAssertionResponse> for cbor::Value {
|
||||||
@@ -99,6 +103,7 @@ impl From<AuthenticatorGetAssertionResponse> for cbor::Value {
|
|||||||
signature,
|
signature,
|
||||||
user,
|
user,
|
||||||
number_of_credentials,
|
number_of_credentials,
|
||||||
|
large_blob_key,
|
||||||
} = get_assertion_response;
|
} = get_assertion_response;
|
||||||
|
|
||||||
cbor_map_options! {
|
cbor_map_options! {
|
||||||
@@ -107,6 +112,7 @@ impl From<AuthenticatorGetAssertionResponse> for cbor::Value {
|
|||||||
0x03 => signature,
|
0x03 => signature,
|
||||||
0x04 => user,
|
0x04 => user,
|
||||||
0x05 => number_of_credentials,
|
0x05 => number_of_credentials,
|
||||||
|
0x07 => large_blob_key,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -124,7 +130,8 @@ pub struct AuthenticatorGetInfoResponse {
|
|||||||
pub max_credential_id_length: Option<u64>,
|
pub max_credential_id_length: Option<u64>,
|
||||||
pub transports: Option<Vec<AuthenticatorTransport>>,
|
pub transports: Option<Vec<AuthenticatorTransport>>,
|
||||||
pub algorithms: Option<Vec<PublicKeyCredentialParameter>>,
|
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 min_pin_length: u8,
|
||||||
pub firmware_version: Option<u64>,
|
pub firmware_version: Option<u64>,
|
||||||
pub max_cred_blob_length: Option<u64>,
|
pub max_cred_blob_length: Option<u64>,
|
||||||
@@ -145,7 +152,8 @@ impl From<AuthenticatorGetInfoResponse> for cbor::Value {
|
|||||||
max_credential_id_length,
|
max_credential_id_length,
|
||||||
transports,
|
transports,
|
||||||
algorithms,
|
algorithms,
|
||||||
default_cred_protect,
|
max_serialized_large_blob_array,
|
||||||
|
force_pin_change,
|
||||||
min_pin_length,
|
min_pin_length,
|
||||||
firmware_version,
|
firmware_version,
|
||||||
max_cred_blob_length,
|
max_cred_blob_length,
|
||||||
@@ -172,7 +180,8 @@ impl From<AuthenticatorGetInfoResponse> for cbor::Value {
|
|||||||
0x08 => max_credential_id_length,
|
0x08 => max_credential_id_length,
|
||||||
0x09 => transports.map(|vec| cbor_array_vec!(vec)),
|
0x09 => transports.map(|vec| cbor_array_vec!(vec)),
|
||||||
0x0A => algorithms.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,
|
0x0D => min_pin_length as u64,
|
||||||
0x0E => firmware_version,
|
0x0E => firmware_version,
|
||||||
0x0F => max_cred_blob_length,
|
0x0F => max_cred_blob_length,
|
||||||
@@ -297,7 +306,7 @@ mod test {
|
|||||||
use super::super::data_formats::{PackedAttestationStatement, PublicKeyCredentialType};
|
use super::super::data_formats::{PackedAttestationStatement, PublicKeyCredentialType};
|
||||||
use super::super::ES256_CRED_PARAM;
|
use super::super::ES256_CRED_PARAM;
|
||||||
use super::*;
|
use super::*;
|
||||||
use cbor::{cbor_bytes, cbor_map};
|
use cbor::{cbor_array, cbor_bytes, cbor_map};
|
||||||
use crypto::rng256::ThreadRng256;
|
use crypto::rng256::ThreadRng256;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -320,6 +329,7 @@ mod test {
|
|||||||
fmt: "packed".to_string(),
|
fmt: "packed".to_string(),
|
||||||
auth_data: vec![0xAD],
|
auth_data: vec![0xAD],
|
||||||
att_stmt,
|
att_stmt,
|
||||||
|
large_blob_key: Some(vec![0x1B]),
|
||||||
};
|
};
|
||||||
let response_cbor: Option<cbor::Value> =
|
let response_cbor: Option<cbor::Value> =
|
||||||
ResponseData::AuthenticatorMakeCredential(make_credential_response).into();
|
ResponseData::AuthenticatorMakeCredential(make_credential_response).into();
|
||||||
@@ -327,24 +337,50 @@ mod test {
|
|||||||
0x01 => "packed",
|
0x01 => "packed",
|
||||||
0x02 => vec![0xAD],
|
0x02 => vec![0xAD],
|
||||||
0x03 => cbor_packed_attestation_statement,
|
0x03 => cbor_packed_attestation_statement,
|
||||||
|
0x05 => vec![0x1B],
|
||||||
};
|
};
|
||||||
assert_eq!(response_cbor, Some(expected_cbor));
|
assert_eq!(response_cbor, Some(expected_cbor));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_assertion_into_cbor() {
|
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 {
|
let get_assertion_response = AuthenticatorGetAssertionResponse {
|
||||||
credential: None,
|
credential: Some(pub_key_cred_descriptor),
|
||||||
auth_data: vec![0xAD],
|
auth_data: vec![0xAD],
|
||||||
signature: vec![0x51],
|
signature: vec![0x51],
|
||||||
user: None,
|
user: Some(user),
|
||||||
number_of_credentials: None,
|
number_of_credentials: Some(2),
|
||||||
|
large_blob_key: Some(vec![0x1B]),
|
||||||
};
|
};
|
||||||
let response_cbor: Option<cbor::Value> =
|
let response_cbor: Option<cbor::Value> =
|
||||||
ResponseData::AuthenticatorGetAssertion(get_assertion_response).into();
|
ResponseData::AuthenticatorGetAssertion(get_assertion_response).into();
|
||||||
let expected_cbor = cbor_map_options! {
|
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],
|
0x02 => vec![0xAD],
|
||||||
0x03 => vec![0x51],
|
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));
|
assert_eq!(response_cbor, Some(expected_cbor));
|
||||||
}
|
}
|
||||||
@@ -363,7 +399,8 @@ mod test {
|
|||||||
max_credential_id_length: None,
|
max_credential_id_length: None,
|
||||||
transports: None,
|
transports: None,
|
||||||
algorithms: None,
|
algorithms: None,
|
||||||
default_cred_protect: None,
|
max_serialized_large_blob_array: None,
|
||||||
|
force_pin_change: None,
|
||||||
min_pin_length: 4,
|
min_pin_length: 4,
|
||||||
firmware_version: None,
|
firmware_version: None,
|
||||||
max_cred_blob_length: None,
|
max_cred_blob_length: None,
|
||||||
@@ -395,7 +432,8 @@ mod test {
|
|||||||
max_credential_id_length: Some(256),
|
max_credential_id_length: Some(256),
|
||||||
transports: Some(vec![AuthenticatorTransport::Usb]),
|
transports: Some(vec![AuthenticatorTransport::Usb]),
|
||||||
algorithms: Some(vec![ES256_CRED_PARAM]),
|
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,
|
min_pin_length: 4,
|
||||||
firmware_version: Some(0),
|
firmware_version: Some(0),
|
||||||
max_cred_blob_length: Some(1024),
|
max_cred_blob_length: Some(1024),
|
||||||
@@ -415,7 +453,8 @@ mod test {
|
|||||||
0x08 => 256,
|
0x08 => 256,
|
||||||
0x09 => cbor_array_vec![vec!["usb"]],
|
0x09 => cbor_array_vec![vec!["usb"]],
|
||||||
0x0A => cbor_array_vec![vec![ES256_CRED_PARAM]],
|
0x0A => cbor_array_vec![vec![ES256_CRED_PARAM]],
|
||||||
0x0C => CredentialProtectionPolicy::UserVerificationRequired as u64,
|
0x0B => 1024,
|
||||||
|
0x0C => false,
|
||||||
0x0D => 4,
|
0x0D => 4,
|
||||||
0x0E => 0,
|
0x0E => 0,
|
||||||
0x0F => 1024,
|
0x0F => 1024,
|
||||||
|
|||||||
@@ -756,6 +756,7 @@ mod test {
|
|||||||
user_name: None,
|
user_name: None,
|
||||||
user_icon: None,
|
user_icon: None,
|
||||||
cred_blob: None,
|
cred_blob: None,
|
||||||
|
large_blob_key: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -973,6 +974,7 @@ mod test {
|
|||||||
user_name: None,
|
user_name: None,
|
||||||
user_icon: None,
|
user_icon: None,
|
||||||
cred_blob: None,
|
cred_blob: None,
|
||||||
|
large_blob_key: None,
|
||||||
};
|
};
|
||||||
assert_eq!(found_credential, Some(expected_credential));
|
assert_eq!(found_credential, Some(expected_credential));
|
||||||
}
|
}
|
||||||
@@ -995,6 +997,7 @@ mod test {
|
|||||||
user_name: None,
|
user_name: None,
|
||||||
user_icon: None,
|
user_icon: None,
|
||||||
cred_blob: None,
|
cred_blob: None,
|
||||||
|
large_blob_key: None,
|
||||||
};
|
};
|
||||||
assert!(persistent_store.store_credential(credential).is_ok());
|
assert!(persistent_store.store_credential(credential).is_ok());
|
||||||
|
|
||||||
@@ -1321,6 +1324,7 @@ mod test {
|
|||||||
user_name: None,
|
user_name: None,
|
||||||
user_icon: None,
|
user_icon: None,
|
||||||
cred_blob: None,
|
cred_blob: None,
|
||||||
|
large_blob_key: None,
|
||||||
};
|
};
|
||||||
let serialized = serialize_credential(credential.clone()).unwrap();
|
let serialized = serialize_credential(credential.clone()).unwrap();
|
||||||
let reconstructed = deserialize_credential(&serialized).unwrap();
|
let reconstructed = deserialize_credential(&serialized).unwrap();
|
||||||
|
|||||||
Reference in New Issue
Block a user