adds a feature flag for CTAP2.1, addresses comments
This commit is contained in:
@@ -25,6 +25,7 @@ std = ["cbor/std", "crypto/std", "crypto/derive_debug"]
|
|||||||
ram_storage = []
|
ram_storage = []
|
||||||
verbose = ["debug_ctap"]
|
verbose = ["debug_ctap"]
|
||||||
with_ctap1 = ["crypto/with_ctap1"]
|
with_ctap1 = ["crypto/with_ctap1"]
|
||||||
|
with_ctap2_1 = []
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
elf2tab = "0.4.0"
|
elf2tab = "0.4.0"
|
||||||
|
|||||||
@@ -28,9 +28,11 @@ few limitations:
|
|||||||
Although we tested and implemented our firmware based on the published
|
Although we tested and implemented our firmware based on the published
|
||||||
[CTAP2.0 specifications](https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html),
|
[CTAP2.0 specifications](https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html),
|
||||||
our implementation was not reviewed nor officially tested and doesn't claim to
|
our implementation was not reviewed nor officially tested and doesn't claim to
|
||||||
be FIDO Certified. With the upcoming next version of the
|
be FIDO Certified.
|
||||||
[CTAP2.1 specifications](https://fidoalliance.org/specs/fido2/fido-client-to-authenticator-protocol-v2.1-rd-20191217.html),
|
We started adding features of the upcoming next version of the
|
||||||
we started adding features, so master is currently between version 2.0 and 2.1.
|
[CTAP2.1 specifications](https://fidoalliance.org/specs/fido2/fido-client-to-authenticator-protocol-v2.1-rd-20191217.html).
|
||||||
|
The development is currently between 2.0 and 2.1, with updates hidden behind a feature flag.
|
||||||
|
Please add the flag `shell --ctap2-1` to the deploy command to include them.
|
||||||
|
|
||||||
### Cryptography
|
### Cryptography
|
||||||
|
|
||||||
|
|||||||
@@ -741,6 +741,14 @@ if __name__ == "__main__":
|
|||||||
help=("Compiles the OpenSK application without backward compatible "
|
help=("Compiles the OpenSK application without backward compatible "
|
||||||
"support for U2F/CTAP1 protocol."),
|
"support for U2F/CTAP1 protocol."),
|
||||||
)
|
)
|
||||||
|
main_parser.add_argument(
|
||||||
|
"--ctap2-1",
|
||||||
|
action=RemoveConstAction,
|
||||||
|
const="with_ctap2_1",
|
||||||
|
dest="features",
|
||||||
|
help=("Compiles the OpenSK application with backward compatible "
|
||||||
|
"support for CTAP2.1 protocol."),
|
||||||
|
)
|
||||||
main_parser.add_argument(
|
main_parser.add_argument(
|
||||||
"--regen-keys",
|
"--regen-keys",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ cargo build --manifest-path third_party/tock/tools/sha256sum/Cargo.toml
|
|||||||
echo "Checking that CTAP2 builds properly..."
|
echo "Checking that CTAP2 builds properly..."
|
||||||
cargo check --release --target=thumbv7em-none-eabi
|
cargo check --release --target=thumbv7em-none-eabi
|
||||||
cargo check --release --target=thumbv7em-none-eabi --features with_ctap1
|
cargo check --release --target=thumbv7em-none-eabi --features with_ctap1
|
||||||
|
cargo check --release --target=thumbv7em-none-eabi --features with_ctap2_1
|
||||||
cargo check --release --target=thumbv7em-none-eabi --features debug_ctap
|
cargo check --release --target=thumbv7em-none-eabi --features debug_ctap
|
||||||
cargo check --release --target=thumbv7em-none-eabi --features panic_console
|
cargo check --release --target=thumbv7em-none-eabi --features panic_console
|
||||||
cargo check --release --target=thumbv7em-none-eabi --features debug_allocations
|
cargo check --release --target=thumbv7em-none-eabi --features debug_allocations
|
||||||
@@ -86,4 +87,10 @@ then
|
|||||||
|
|
||||||
echo "Running unit tests on the desktop (debug mode + CTAP1)..."
|
echo "Running unit tests on the desktop (debug mode + CTAP1)..."
|
||||||
cargo test --features std,with_ctap1
|
cargo test --features std,with_ctap1
|
||||||
|
|
||||||
|
echo "Running unit tests on the desktop (release mode + CTAP2.1)..."
|
||||||
|
cargo test --release --features std,with_ctap2_1
|
||||||
|
|
||||||
|
echo "Running unit tests on the desktop (debug mode + CTAP2.1)..."
|
||||||
|
cargo test --features std,with_ctap2_1
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -23,9 +23,10 @@ use alloc::string::String;
|
|||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use core::convert::TryFrom;
|
use core::convert::TryFrom;
|
||||||
|
|
||||||
// Depending on your memory, you can use Some(n) to limit request sizes.
|
// Depending on your memory, you can use Some(n) to limit request sizes in
|
||||||
|
// MakeCredential and GetAssertion. This affects allowList and excludeList.
|
||||||
// You might also want to set the max credential size in process_get_info then.
|
// You might also want to set the max credential size in process_get_info then.
|
||||||
pub const MAX_CREDENTIAL_COUNT_IN_LIST: Option<u64> = None;
|
pub const MAX_CREDENTIAL_COUNT_IN_LIST: Option<usize> = None;
|
||||||
|
|
||||||
// CTAP specification (version 20190130) section 6.1
|
// CTAP specification (version 20190130) section 6.1
|
||||||
#[cfg_attr(any(test, feature = "debug_ctap"), derive(Debug, PartialEq))]
|
#[cfg_attr(any(test, feature = "debug_ctap"), derive(Debug, PartialEq))]
|
||||||
@@ -136,25 +137,19 @@ impl TryFrom<cbor::Value> for AuthenticatorMakeCredentialParameters {
|
|||||||
)?)?;
|
)?)?;
|
||||||
|
|
||||||
let cred_param_vec = read_array(ok_or_missing(param_map.get(&cbor_unsigned!(4)))?)?;
|
let cred_param_vec = read_array(ok_or_missing(param_map.get(&cbor_unsigned!(4)))?)?;
|
||||||
let mut pub_key_cred_params = vec![];
|
let pub_key_cred_params = cred_param_vec
|
||||||
for cred_param_map_value in cred_param_vec {
|
.iter()
|
||||||
if let Ok(cred_param) = PublicKeyCredentialParameter::try_from(cred_param_map_value) {
|
.map(PublicKeyCredentialParameter::try_from)
|
||||||
pub_key_cred_params.push(cred_param);
|
.collect::<Result<Vec<PublicKeyCredentialParameter>, Ctap2StatusCode>>()?;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let exclude_list = match param_map.get(&cbor_unsigned!(5)) {
|
let exclude_list = match param_map.get(&cbor_unsigned!(5)) {
|
||||||
Some(entry) => {
|
Some(entry) => {
|
||||||
let exclude_list_vec = read_array(entry)?;
|
let exclude_list_vec = read_array(entry)?;
|
||||||
let mut exclude_list = vec![];
|
let exclude_list = exclude_list_vec
|
||||||
for exclude_list_value in exclude_list_vec {
|
.iter()
|
||||||
if let Some(count) = MAX_CREDENTIAL_COUNT_IN_LIST {
|
.take(MAX_CREDENTIAL_COUNT_IN_LIST.unwrap_or(exclude_list_vec.len()))
|
||||||
if exclude_list.len() as u64 >= count {
|
.map(PublicKeyCredentialDescriptor::try_from)
|
||||||
break;
|
.collect::<Result<Vec<PublicKeyCredentialDescriptor>, Ctap2StatusCode>>()?;
|
||||||
}
|
|
||||||
}
|
|
||||||
exclude_list.push(PublicKeyCredentialDescriptor::try_from(exclude_list_value)?);
|
|
||||||
}
|
|
||||||
Some(exclude_list)
|
Some(exclude_list)
|
||||||
}
|
}
|
||||||
None => None,
|
None => None,
|
||||||
@@ -222,15 +217,11 @@ impl TryFrom<cbor::Value> for AuthenticatorGetAssertionParameters {
|
|||||||
let allow_list = match param_map.get(&cbor_unsigned!(3)) {
|
let allow_list = match param_map.get(&cbor_unsigned!(3)) {
|
||||||
Some(entry) => {
|
Some(entry) => {
|
||||||
let allow_list_vec = read_array(entry)?;
|
let allow_list_vec = read_array(entry)?;
|
||||||
let mut allow_list = vec![];
|
let allow_list = allow_list_vec
|
||||||
for allow_list_value in allow_list_vec {
|
.iter()
|
||||||
if let Some(count) = MAX_CREDENTIAL_COUNT_IN_LIST {
|
.take(MAX_CREDENTIAL_COUNT_IN_LIST.unwrap_or(allow_list_vec.len()))
|
||||||
if allow_list.len() as u64 >= count {
|
.map(PublicKeyCredentialDescriptor::try_from)
|
||||||
break;
|
.collect::<Result<Vec<PublicKeyCredentialDescriptor>, Ctap2StatusCode>>()?;
|
||||||
}
|
|
||||||
}
|
|
||||||
allow_list.push(PublicKeyCredentialDescriptor::try_from(allow_list_value)?);
|
|
||||||
}
|
|
||||||
Some(allow_list)
|
Some(allow_list)
|
||||||
}
|
}
|
||||||
None => None,
|
None => None,
|
||||||
@@ -330,7 +321,7 @@ mod test {
|
|||||||
AuthenticatorTransport, PublicKeyCredentialRpEntity, PublicKeyCredentialType,
|
AuthenticatorTransport, PublicKeyCredentialRpEntity, PublicKeyCredentialType,
|
||||||
PublicKeyCredentialUserEntity,
|
PublicKeyCredentialUserEntity,
|
||||||
};
|
};
|
||||||
use super::super::CREDENTIAL_PARAMETER;
|
use super::super::ES256_CRED_PARAM;
|
||||||
use super::*;
|
use super::*;
|
||||||
use alloc::collections::BTreeMap;
|
use alloc::collections::BTreeMap;
|
||||||
|
|
||||||
@@ -349,7 +340,7 @@ mod test {
|
|||||||
"displayName" => "bar",
|
"displayName" => "bar",
|
||||||
"icon" => "example.com/foo/icon.png",
|
"icon" => "example.com/foo/icon.png",
|
||||||
},
|
},
|
||||||
4 => cbor_array![CREDENTIAL_PARAMETER],
|
4 => cbor_array![ES256_CRED_PARAM],
|
||||||
5 => cbor_array![],
|
5 => cbor_array![],
|
||||||
8 => vec![0x12, 0x34],
|
8 => vec![0x12, 0x34],
|
||||||
9 => 1,
|
9 => 1,
|
||||||
@@ -380,7 +371,7 @@ mod test {
|
|||||||
client_data_hash,
|
client_data_hash,
|
||||||
rp,
|
rp,
|
||||||
user,
|
user,
|
||||||
pub_key_cred_params: vec![CREDENTIAL_PARAMETER],
|
pub_key_cred_params: vec![ES256_CRED_PARAM],
|
||||||
exclude_list: Some(vec![]),
|
exclude_list: Some(vec![]),
|
||||||
extensions: None,
|
extensions: None,
|
||||||
options,
|
options,
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ use alloc::vec::Vec;
|
|||||||
use core::convert::TryFrom;
|
use core::convert::TryFrom;
|
||||||
use crypto::{ecdh, ecdsa};
|
use crypto::{ecdh, ecdsa};
|
||||||
|
|
||||||
|
// https://www.w3.org/TR/webauthn/#dictdef-publickeycredentialrpentity
|
||||||
#[cfg_attr(any(test, feature = "debug_ctap"), derive(Debug, PartialEq))]
|
#[cfg_attr(any(test, feature = "debug_ctap"), derive(Debug, PartialEq))]
|
||||||
pub struct PublicKeyCredentialRpEntity {
|
pub struct PublicKeyCredentialRpEntity {
|
||||||
pub rp_id: String,
|
pub rp_id: String,
|
||||||
@@ -48,6 +49,7 @@ impl TryFrom<&cbor::Value> for PublicKeyCredentialRpEntity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://www.w3.org/TR/webauthn/#dictdef-publickeycredentialuserentity
|
||||||
#[cfg_attr(any(test, feature = "debug_ctap"), derive(Debug, PartialEq))]
|
#[cfg_attr(any(test, feature = "debug_ctap"), derive(Debug, PartialEq))]
|
||||||
pub struct PublicKeyCredentialUserEntity {
|
pub struct PublicKeyCredentialUserEntity {
|
||||||
pub user_id: Vec<u8>,
|
pub user_id: Vec<u8>,
|
||||||
@@ -94,16 +96,22 @@ impl From<PublicKeyCredentialUserEntity> for cbor::Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://www.w3.org/TR/webauthn/#enumdef-publickeycredentialtype
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone, PartialEq)]
|
||||||
#[cfg_attr(any(test, feature = "debug_ctap"), derive(Debug))]
|
#[cfg_attr(any(test, feature = "debug_ctap"), derive(Debug))]
|
||||||
pub enum PublicKeyCredentialType {
|
pub enum PublicKeyCredentialType {
|
||||||
PublicKey,
|
PublicKey,
|
||||||
|
// This is the default for all strings not covered above.
|
||||||
|
// Unknown types should be ignored, instead of returning errors.
|
||||||
|
Unknown,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<PublicKeyCredentialType> for cbor::Value {
|
impl From<PublicKeyCredentialType> for cbor::Value {
|
||||||
fn from(cred_type: PublicKeyCredentialType) -> Self {
|
fn from(cred_type: PublicKeyCredentialType) -> Self {
|
||||||
match cred_type {
|
match cred_type {
|
||||||
PublicKeyCredentialType::PublicKey => "public-key",
|
PublicKeyCredentialType::PublicKey => "public-key",
|
||||||
|
// We should never create this credential type.
|
||||||
|
PublicKeyCredentialType::Unknown => unreachable!(),
|
||||||
}
|
}
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
@@ -116,11 +124,12 @@ impl TryFrom<&cbor::Value> for PublicKeyCredentialType {
|
|||||||
let cred_type_string = read_text_string(cbor_value)?;
|
let cred_type_string = read_text_string(cbor_value)?;
|
||||||
match &cred_type_string[..] {
|
match &cred_type_string[..] {
|
||||||
"public-key" => Ok(PublicKeyCredentialType::PublicKey),
|
"public-key" => Ok(PublicKeyCredentialType::PublicKey),
|
||||||
_ => Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_ALGORITHM),
|
_ => Ok(PublicKeyCredentialType::Unknown),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://www.w3.org/TR/webauthn/#dictdef-publickeycredentialparameters
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
#[cfg_attr(any(test, feature = "debug_ctap"), derive(Debug))]
|
#[cfg_attr(any(test, feature = "debug_ctap"), derive(Debug))]
|
||||||
pub struct PublicKeyCredentialParameter {
|
pub struct PublicKeyCredentialParameter {
|
||||||
@@ -151,6 +160,7 @@ impl From<PublicKeyCredentialParameter> for cbor::Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://www.w3.org/TR/webauthn/#enumdef-authenticatortransport
|
||||||
#[cfg_attr(any(test, feature = "debug_ctap"), derive(Debug, PartialEq))]
|
#[cfg_attr(any(test, feature = "debug_ctap"), derive(Debug, PartialEq))]
|
||||||
pub enum AuthenticatorTransport {
|
pub enum AuthenticatorTransport {
|
||||||
Usb,
|
Usb,
|
||||||
@@ -186,6 +196,7 @@ impl TryFrom<&cbor::Value> for AuthenticatorTransport {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://www.w3.org/TR/webauthn/#dictdef-publickeycredentialdescriptor
|
||||||
#[cfg_attr(any(test, feature = "debug_ctap"), derive(Debug, PartialEq))]
|
#[cfg_attr(any(test, feature = "debug_ctap"), derive(Debug, PartialEq))]
|
||||||
pub struct PublicKeyCredentialDescriptor {
|
pub struct PublicKeyCredentialDescriptor {
|
||||||
pub key_type: PublicKeyCredentialType,
|
pub key_type: PublicKeyCredentialType,
|
||||||
@@ -205,10 +216,11 @@ impl TryFrom<&cbor::Value> for PublicKeyCredentialDescriptor {
|
|||||||
let transports = match cred_desc_map.get(&cbor_text!("transports")) {
|
let transports = match cred_desc_map.get(&cbor_text!("transports")) {
|
||||||
Some(exclude_entry) => {
|
Some(exclude_entry) => {
|
||||||
let transport_vec = read_array(exclude_entry)?;
|
let transport_vec = read_array(exclude_entry)?;
|
||||||
let mut transports = vec![];
|
let transports = transport_vec
|
||||||
for transport_value in transport_vec {
|
.iter()
|
||||||
transports.push(AuthenticatorTransport::try_from(transport_value)?);
|
.map(AuthenticatorTransport::try_from)
|
||||||
}
|
.collect::<Result<Vec<AuthenticatorTransport>, Ctap2StatusCode>>(
|
||||||
|
)?;
|
||||||
Some(transports)
|
Some(transports)
|
||||||
}
|
}
|
||||||
None => None,
|
None => None,
|
||||||
@@ -379,6 +391,7 @@ impl TryFrom<&cbor::Value> for GetAssertionOptions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://www.w3.org/TR/webauthn/#packed-attestation
|
||||||
#[cfg_attr(test, derive(PartialEq))]
|
#[cfg_attr(test, derive(PartialEq))]
|
||||||
#[cfg_attr(any(test, feature = "debug_ctap"), derive(Debug))]
|
#[cfg_attr(any(test, feature = "debug_ctap"), derive(Debug))]
|
||||||
pub struct PackedAttestationStatement {
|
pub struct PackedAttestationStatement {
|
||||||
@@ -403,6 +416,9 @@ impl From<PackedAttestationStatement> for cbor::Value {
|
|||||||
#[cfg_attr(any(test, feature = "debug_ctap"), derive(Debug))]
|
#[cfg_attr(any(test, feature = "debug_ctap"), derive(Debug))]
|
||||||
pub enum SignatureAlgorithm {
|
pub enum SignatureAlgorithm {
|
||||||
ES256 = ecdsa::PubKey::ES256_ALGORITHM as isize,
|
ES256 = ecdsa::PubKey::ES256_ALGORITHM as isize,
|
||||||
|
// This is the default for all numbers not covered above.
|
||||||
|
// Unknown types should be ignored, instead of returning errors.
|
||||||
|
Unknown = 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&cbor::Value> for SignatureAlgorithm {
|
impl TryFrom<&cbor::Value> for SignatureAlgorithm {
|
||||||
@@ -411,11 +427,12 @@ impl TryFrom<&cbor::Value> for SignatureAlgorithm {
|
|||||||
fn try_from(cbor_value: &cbor::Value) -> Result<Self, Ctap2StatusCode> {
|
fn try_from(cbor_value: &cbor::Value) -> Result<Self, Ctap2StatusCode> {
|
||||||
match read_integer(cbor_value)? {
|
match read_integer(cbor_value)? {
|
||||||
ecdsa::PubKey::ES256_ALGORITHM => Ok(SignatureAlgorithm::ES256),
|
ecdsa::PubKey::ES256_ALGORITHM => Ok(SignatureAlgorithm::ES256),
|
||||||
_ => Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_ALGORITHM),
|
_ => Ok(SignatureAlgorithm::Unknown),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://www.w3.org/TR/webauthn/#public-key-credential-source
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
#[cfg_attr(test, derive(PartialEq))]
|
#[cfg_attr(test, derive(PartialEq))]
|
||||||
#[cfg_attr(any(test, feature = "debug_ctap"), derive(Debug))]
|
#[cfg_attr(any(test, feature = "debug_ctap"), derive(Debug))]
|
||||||
@@ -679,6 +696,7 @@ mod test {
|
|||||||
use self::Ctap2StatusCode::CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
|
use self::Ctap2StatusCode::CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
|
||||||
use super::*;
|
use super::*;
|
||||||
use alloc::collections::BTreeMap;
|
use alloc::collections::BTreeMap;
|
||||||
|
use crypto::rng256::{Rng256, ThreadRng256};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_read_unsigned() {
|
fn test_read_unsigned() {
|
||||||
@@ -990,6 +1008,26 @@ mod test {
|
|||||||
assert_eq!(credential_type, Ok(expected_credential_type));
|
assert_eq!(credential_type, Ok(expected_credential_type));
|
||||||
let created_cbor: cbor::Value = credential_type.unwrap().into();
|
let created_cbor: cbor::Value = credential_type.unwrap().into();
|
||||||
assert_eq!(created_cbor, cbor_credential_type);
|
assert_eq!(created_cbor, cbor_credential_type);
|
||||||
|
|
||||||
|
let cbor_unknown_type = cbor_text!("unknown-type");
|
||||||
|
let unknown_type = PublicKeyCredentialType::try_from(&cbor_unknown_type);
|
||||||
|
let expected_unknown_type = PublicKeyCredentialType::Unknown;
|
||||||
|
assert_eq!(unknown_type, Ok(expected_unknown_type));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_from_into_signature_algorithm() {
|
||||||
|
let cbor_signature_algorithm = cbor_int!(ecdsa::PubKey::ES256_ALGORITHM);
|
||||||
|
let signature_algorithm = SignatureAlgorithm::try_from(&cbor_signature_algorithm);
|
||||||
|
let expected_signature_algorithm = SignatureAlgorithm::ES256;
|
||||||
|
assert_eq!(signature_algorithm, Ok(expected_signature_algorithm));
|
||||||
|
let created_cbor: cbor::Value = cbor_int!(signature_algorithm.unwrap() as i64);
|
||||||
|
assert_eq!(created_cbor, cbor_signature_algorithm);
|
||||||
|
|
||||||
|
let cbor_unknown_algorithm = cbor_int!(-1);
|
||||||
|
let unknown_algorithm = SignatureAlgorithm::try_from(&cbor_unknown_algorithm);
|
||||||
|
let expected_unknown_algorithm = SignatureAlgorithm::Unknown;
|
||||||
|
assert_eq!(unknown_algorithm, Ok(expected_unknown_algorithm));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -1006,6 +1044,23 @@ mod test {
|
|||||||
assert_eq!(created_cbor, cbor_authenticator_transport);
|
assert_eq!(created_cbor, cbor_authenticator_transport);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_from_into_public_key_credential_parameter() {
|
||||||
|
let cbor_credential_parameter = cbor_map! {
|
||||||
|
"type" => "public-key",
|
||||||
|
"alg" => ecdsa::PubKey::ES256_ALGORITHM,
|
||||||
|
};
|
||||||
|
let credential_parameter =
|
||||||
|
PublicKeyCredentialParameter::try_from(&cbor_credential_parameter);
|
||||||
|
let expected_credential_parameter = PublicKeyCredentialParameter {
|
||||||
|
cred_type: PublicKeyCredentialType::PublicKey,
|
||||||
|
alg: SignatureAlgorithm::ES256,
|
||||||
|
};
|
||||||
|
assert_eq!(credential_parameter, Ok(expected_credential_parameter));
|
||||||
|
let created_cbor: cbor::Value = credential_parameter.unwrap().into();
|
||||||
|
assert_eq!(created_cbor, cbor_credential_parameter);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_from_into_public_key_credential_descriptor() {
|
fn test_from_into_public_key_credential_descriptor() {
|
||||||
let cbor_credential_descriptor = cbor_map! {
|
let cbor_credential_descriptor = cbor_map! {
|
||||||
@@ -1026,7 +1081,7 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_from_extensions() {
|
fn test_from_into_extensions() {
|
||||||
let cbor_extensions = cbor_map! {
|
let cbor_extensions = cbor_map! {
|
||||||
"the_answer" => 42,
|
"the_answer" => 42,
|
||||||
};
|
};
|
||||||
@@ -1036,6 +1091,53 @@ mod test {
|
|||||||
.0
|
.0
|
||||||
.insert("the_answer".to_string(), cbor_int!(42));
|
.insert("the_answer".to_string(), cbor_int!(42));
|
||||||
assert_eq!(extensions, Ok(expected_extensions));
|
assert_eq!(extensions, Ok(expected_extensions));
|
||||||
|
let created_cbor: cbor::Value = extensions.unwrap().into();
|
||||||
|
assert_eq!(created_cbor, cbor_extensions);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_from_into_get_assertion_hmac_secret_output() {
|
||||||
|
let cbor_output = cbor_bytes![vec![0xC0; 32]];
|
||||||
|
let output = GetAssertionHmacSecretOutput::try_from(&cbor_output);
|
||||||
|
let expected_output = GetAssertionHmacSecretOutput(vec![0xC0; 32]);
|
||||||
|
assert_eq!(output, Ok(expected_output));
|
||||||
|
let created_cbor: cbor::Value = output.unwrap().into();
|
||||||
|
assert_eq!(created_cbor, cbor_output);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hmac_secret_extension() {
|
||||||
|
let cbor_extensions = cbor_map! {
|
||||||
|
"hmac-secret" => true,
|
||||||
|
};
|
||||||
|
let extensions = Extensions::try_from(&cbor_extensions).unwrap();
|
||||||
|
assert!(extensions.has_make_credential_hmac_secret().unwrap());
|
||||||
|
|
||||||
|
let cbor_extensions = cbor_map! {
|
||||||
|
"hmac-secret" => false,
|
||||||
|
};
|
||||||
|
let extensions = Extensions::try_from(&cbor_extensions).unwrap();
|
||||||
|
assert!(!extensions.has_make_credential_hmac_secret().unwrap());
|
||||||
|
|
||||||
|
let mut rng = ThreadRng256 {};
|
||||||
|
let sk = crypto::ecdh::SecKey::gensk(&mut rng);
|
||||||
|
let pk = sk.genpk();
|
||||||
|
let cose_key = CoseKey::from(pk.clone());
|
||||||
|
let cbor_extensions = cbor_map! {
|
||||||
|
"hmac-secret" => cbor_map! {
|
||||||
|
1 => cbor::Value::Map(cose_key.0.clone()),
|
||||||
|
2 => vec![0x02; 32],
|
||||||
|
3 => vec![0x03; 32],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let extensions = Extensions::try_from(&cbor_extensions).unwrap();
|
||||||
|
let get_assertion_input = extensions.get_assertion_hmac_secret();
|
||||||
|
let expected_input = GetAssertionHmacSecretInput {
|
||||||
|
key_agreement: cose_key,
|
||||||
|
salt_enc: vec![0x02; 32],
|
||||||
|
salt_auth: vec![0x03; 32],
|
||||||
|
};
|
||||||
|
assert_eq!(get_assertion_input, Some(Ok(expected_input)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -1087,8 +1189,6 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_from_into_cose_key() {
|
fn test_from_into_cose_key() {
|
||||||
use crypto::rng256::ThreadRng256;
|
|
||||||
|
|
||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
let sk = crypto::ecdh::SecKey::gensk(&mut rng);
|
let sk = crypto::ecdh::SecKey::gensk(&mut rng);
|
||||||
let pk = sk.genpk();
|
let pk = sk.genpk();
|
||||||
@@ -1109,8 +1209,6 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_credential_source_cbor_round_trip() {
|
fn test_credential_source_cbor_round_trip() {
|
||||||
use crypto::rng256::{Rng256, ThreadRng256};
|
|
||||||
|
|
||||||
let mut rng = ThreadRng256 {};
|
let mut rng = ThreadRng256 {};
|
||||||
let credential = PublicKeyCredentialSource {
|
let credential = PublicKeyCredentialSource {
|
||||||
key_type: PublicKeyCredentialType::PublicKey,
|
key_type: PublicKeyCredentialType::PublicKey,
|
||||||
|
|||||||
@@ -23,15 +23,18 @@ pub mod status_code;
|
|||||||
mod storage;
|
mod storage;
|
||||||
mod timed_permission;
|
mod timed_permission;
|
||||||
|
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
|
use self::command::MAX_CREDENTIAL_COUNT_IN_LIST;
|
||||||
use self::command::{
|
use self::command::{
|
||||||
AuthenticatorClientPinParameters, AuthenticatorGetAssertionParameters,
|
AuthenticatorClientPinParameters, AuthenticatorGetAssertionParameters,
|
||||||
AuthenticatorMakeCredentialParameters, Command, MAX_CREDENTIAL_COUNT_IN_LIST,
|
AuthenticatorMakeCredentialParameters, Command,
|
||||||
};
|
};
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
|
use self::data_formats::AuthenticatorTransport;
|
||||||
use self::data_formats::{
|
use self::data_formats::{
|
||||||
AuthenticatorTransport, ClientPinSubCommand, CoseKey, GetAssertionHmacSecretInput,
|
ClientPinSubCommand, CoseKey, GetAssertionHmacSecretInput, PackedAttestationStatement,
|
||||||
PackedAttestationStatement, PublicKeyCredentialDescriptor, PublicKeyCredentialParameter,
|
PublicKeyCredentialDescriptor, PublicKeyCredentialParameter, PublicKeyCredentialSource,
|
||||||
PublicKeyCredentialSource, PublicKeyCredentialType, PublicKeyCredentialUserEntity,
|
PublicKeyCredentialType, PublicKeyCredentialUserEntity, SignatureAlgorithm,
|
||||||
SignatureAlgorithm,
|
|
||||||
};
|
};
|
||||||
use self::hid::ChannelID;
|
use self::hid::ChannelID;
|
||||||
use self::key_material::{AAGUID, ATTESTATION_CERTIFICATE, ATTESTATION_PRIVATE_KEY};
|
use self::key_material::{AAGUID, ATTESTATION_CERTIFICATE, ATTESTATION_PRIVATE_KEY};
|
||||||
@@ -100,7 +103,9 @@ pub const FIDO2_VERSION_STRING: &str = "FIDO_2_0";
|
|||||||
#[cfg(feature = "with_ctap1")]
|
#[cfg(feature = "with_ctap1")]
|
||||||
pub const U2F_VERSION_STRING: &str = "U2F_V2";
|
pub const U2F_VERSION_STRING: &str = "U2F_V2";
|
||||||
|
|
||||||
pub const CREDENTIAL_PARAMETER: PublicKeyCredentialParameter = PublicKeyCredentialParameter {
|
// We currently only support one algorithm for signatures: ES256.
|
||||||
|
// This algorithm is requested in MakeCredential and advertized in GetInfo.
|
||||||
|
pub const ES256_CRED_PARAM: PublicKeyCredentialParameter = PublicKeyCredentialParameter {
|
||||||
cred_type: PublicKeyCredentialType::PublicKey,
|
cred_type: PublicKeyCredentialType::PublicKey,
|
||||||
alg: SignatureAlgorithm::ES256,
|
alg: SignatureAlgorithm::ES256,
|
||||||
};
|
};
|
||||||
@@ -419,7 +424,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !pub_key_cred_params.contains(&CREDENTIAL_PARAMETER) {
|
if !pub_key_cred_params.contains(&ES256_CRED_PARAM) {
|
||||||
return Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_ALGORITHM);
|
return Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_ALGORITHM);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -757,6 +762,7 @@ where
|
|||||||
self.persistent_store.pin_hash().is_some(),
|
self.persistent_store.pin_hash().is_some(),
|
||||||
);
|
);
|
||||||
Ok(ResponseData::AuthenticatorGetInfo(
|
Ok(ResponseData::AuthenticatorGetInfo(
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
AuthenticatorGetInfoResponse {
|
AuthenticatorGetInfoResponse {
|
||||||
versions: vec![
|
versions: vec![
|
||||||
#[cfg(feature = "with_ctap1")]
|
#[cfg(feature = "with_ctap1")]
|
||||||
@@ -770,14 +776,29 @@ where
|
|||||||
pin_protocols: Some(vec![
|
pin_protocols: Some(vec![
|
||||||
CtapState::<R, CheckUserPresence>::PIN_PROTOCOL_VERSION,
|
CtapState::<R, CheckUserPresence>::PIN_PROTOCOL_VERSION,
|
||||||
]),
|
]),
|
||||||
max_credential_count_in_list: MAX_CREDENTIAL_COUNT_IN_LIST,
|
max_credential_count_in_list: MAX_CREDENTIAL_COUNT_IN_LIST.map(|c| c as u64),
|
||||||
// You can use ENCRYPTED_CREDENTIAL_ID_SIZE here, but if your
|
// You can use ENCRYPTED_CREDENTIAL_ID_SIZE here, but if your
|
||||||
// browser passes that value, it might be used to fingerprint.
|
// browser passes that value, it might be used to fingerprint.
|
||||||
max_credential_id_length: None,
|
max_credential_id_length: None,
|
||||||
transports: Some(vec![AuthenticatorTransport::Usb]),
|
transports: Some(vec![AuthenticatorTransport::Usb]),
|
||||||
algorithms: Some(vec![CREDENTIAL_PARAMETER]),
|
algorithms: Some(vec![ES256_CRED_PARAM]),
|
||||||
firmware_version: None,
|
firmware_version: None,
|
||||||
},
|
},
|
||||||
|
#[cfg(not(feature = "with_ctap2_1"))]
|
||||||
|
AuthenticatorGetInfoResponse {
|
||||||
|
versions: vec![
|
||||||
|
#[cfg(feature = "with_ctap1")]
|
||||||
|
String::from(U2F_VERSION_STRING),
|
||||||
|
String::from(FIDO2_VERSION_STRING),
|
||||||
|
],
|
||||||
|
extensions: Some(vec![String::from("hmac-secret")]),
|
||||||
|
aaguid: *AAGUID,
|
||||||
|
options: Some(options_map),
|
||||||
|
max_msg_size: Some(1024),
|
||||||
|
pin_protocols: Some(vec![
|
||||||
|
CtapState::<R, CheckUserPresence>::PIN_PROTOCOL_VERSION,
|
||||||
|
]),
|
||||||
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1098,7 +1119,10 @@ mod test {
|
|||||||
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present);
|
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present);
|
||||||
let info_reponse = ctap_state.process_command(&[0x04], DUMMY_CHANNEL_ID);
|
let info_reponse = ctap_state.process_command(&[0x04], DUMMY_CHANNEL_ID);
|
||||||
|
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
let mut expected_response = vec![0x00, 0xA8, 0x01];
|
let mut expected_response = vec![0x00, 0xA8, 0x01];
|
||||||
|
#[cfg(not(feature = "with_ctap2_1"))]
|
||||||
|
let mut expected_response = vec![0x00, 0xA6, 0x01];
|
||||||
// The difference here is a longer array of supported versions.
|
// The difference here is a longer array of supported versions.
|
||||||
#[cfg(not(feature = "with_ctap1"))]
|
#[cfg(not(feature = "with_ctap1"))]
|
||||||
expected_response.extend(&[0x81, 0x68, 0x46, 0x49, 0x44, 0x4F, 0x5F, 0x32, 0x5F, 0x30]);
|
expected_response.extend(&[0x81, 0x68, 0x46, 0x49, 0x44, 0x4F, 0x5F, 0x32, 0x5F, 0x30]);
|
||||||
@@ -1112,10 +1136,13 @@ mod test {
|
|||||||
0x03, 0x50,
|
0x03, 0x50,
|
||||||
]);
|
]);
|
||||||
expected_response.extend(AAGUID);
|
expected_response.extend(AAGUID);
|
||||||
expected_response.extend(
|
expected_response.extend(&[
|
||||||
[
|
|
||||||
0x04, 0xA3, 0x62, 0x72, 0x6B, 0xF5, 0x62, 0x75, 0x70, 0xF5, 0x69, 0x63, 0x6C, 0x69,
|
0x04, 0xA3, 0x62, 0x72, 0x6B, 0xF5, 0x62, 0x75, 0x70, 0xF5, 0x69, 0x63, 0x6C, 0x69,
|
||||||
0x65, 0x6E, 0x74, 0x50, 0x69, 0x6E, 0xF4, 0x05, 0x19, 0x04, 0x00, 0x06, 0x81, 0x01,
|
0x65, 0x6E, 0x74, 0x50, 0x69, 0x6E, 0xF4, 0x05, 0x19, 0x04, 0x00, 0x06, 0x81, 0x01,
|
||||||
|
]);
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
|
expected_response.extend(
|
||||||
|
[
|
||||||
0x09, 0x81, 0x63, 0x75, 0x73, 0x62, 0x0A, 0x81, 0xA2, 0x63, 0x61, 0x6C, 0x67, 0x26,
|
0x09, 0x81, 0x63, 0x75, 0x73, 0x62, 0x0A, 0x81, 0xA2, 0x63, 0x61, 0x6C, 0x67, 0x26,
|
||||||
0x64, 0x74, 0x79, 0x70, 0x65, 0x6A, 0x70, 0x75, 0x62, 0x6C, 0x69, 0x63, 0x2D, 0x6B,
|
0x64, 0x74, 0x79, 0x70, 0x65, 0x6A, 0x70, 0x75, 0x62, 0x6C, 0x69, 0x63, 0x2D, 0x6B,
|
||||||
0x65, 0x79,
|
0x65, 0x79,
|
||||||
@@ -1139,7 +1166,7 @@ mod test {
|
|||||||
user_display_name: None,
|
user_display_name: None,
|
||||||
user_icon: None,
|
user_icon: None,
|
||||||
};
|
};
|
||||||
let pub_key_cred_params = vec![CREDENTIAL_PARAMETER];
|
let pub_key_cred_params = vec![ES256_CRED_PARAM];
|
||||||
let options = MakeCredentialOptions {
|
let options = MakeCredentialOptions {
|
||||||
rk: true,
|
rk: true,
|
||||||
uv: false,
|
uv: false,
|
||||||
|
|||||||
@@ -12,9 +12,11 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
|
use super::data_formats::{AuthenticatorTransport, PublicKeyCredentialParameter};
|
||||||
use super::data_formats::{
|
use super::data_formats::{
|
||||||
AuthenticatorTransport, CoseKey, PackedAttestationStatement, PublicKeyCredentialDescriptor,
|
CoseKey, PackedAttestationStatement, PublicKeyCredentialDescriptor,
|
||||||
PublicKeyCredentialParameter, PublicKeyCredentialUserEntity,
|
PublicKeyCredentialUserEntity,
|
||||||
};
|
};
|
||||||
use alloc::collections::BTreeMap;
|
use alloc::collections::BTreeMap;
|
||||||
use alloc::string::String;
|
use alloc::string::String;
|
||||||
@@ -102,21 +104,27 @@ impl From<AuthenticatorGetAssertionResponse> for cbor::Value {
|
|||||||
#[cfg_attr(test, derive(PartialEq))]
|
#[cfg_attr(test, derive(PartialEq))]
|
||||||
#[cfg_attr(any(test, feature = "debug_ctap"), derive(Debug))]
|
#[cfg_attr(any(test, feature = "debug_ctap"), derive(Debug))]
|
||||||
pub struct AuthenticatorGetInfoResponse {
|
pub struct AuthenticatorGetInfoResponse {
|
||||||
// TODO(kaczmarczyck) add fields from 2.1
|
// TODO(kaczmarczyck) add maxAuthenticatorConfigLength and defaultCredProtect
|
||||||
pub versions: Vec<String>,
|
pub versions: Vec<String>,
|
||||||
pub extensions: Option<Vec<String>>,
|
pub extensions: Option<Vec<String>>,
|
||||||
pub aaguid: [u8; 16],
|
pub aaguid: [u8; 16],
|
||||||
pub options: Option<BTreeMap<String, bool>>,
|
pub options: Option<BTreeMap<String, bool>>,
|
||||||
pub max_msg_size: Option<u64>,
|
pub max_msg_size: Option<u64>,
|
||||||
pub pin_protocols: Option<Vec<u64>>,
|
pub pin_protocols: Option<Vec<u64>>,
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
pub max_credential_count_in_list: Option<u64>,
|
pub max_credential_count_in_list: Option<u64>,
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
pub max_credential_id_length: Option<u64>,
|
pub max_credential_id_length: Option<u64>,
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
pub transports: Option<Vec<AuthenticatorTransport>>,
|
pub transports: Option<Vec<AuthenticatorTransport>>,
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
pub algorithms: Option<Vec<PublicKeyCredentialParameter>>,
|
pub algorithms: Option<Vec<PublicKeyCredentialParameter>>,
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
pub firmware_version: Option<u64>,
|
pub firmware_version: Option<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<AuthenticatorGetInfoResponse> for cbor::Value {
|
impl From<AuthenticatorGetInfoResponse> for cbor::Value {
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
fn from(get_info_response: AuthenticatorGetInfoResponse) -> Self {
|
fn from(get_info_response: AuthenticatorGetInfoResponse) -> Self {
|
||||||
let AuthenticatorGetInfoResponse {
|
let AuthenticatorGetInfoResponse {
|
||||||
versions,
|
versions,
|
||||||
@@ -154,6 +162,35 @@ impl From<AuthenticatorGetInfoResponse> for cbor::Value {
|
|||||||
0x0E => firmware_version,
|
0x0E => firmware_version,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "with_ctap2_1"))]
|
||||||
|
fn from(get_info_response: AuthenticatorGetInfoResponse) -> Self {
|
||||||
|
let AuthenticatorGetInfoResponse {
|
||||||
|
versions,
|
||||||
|
extensions,
|
||||||
|
aaguid,
|
||||||
|
options,
|
||||||
|
max_msg_size,
|
||||||
|
pin_protocols,
|
||||||
|
} = get_info_response;
|
||||||
|
|
||||||
|
let options_cbor: Option<cbor::Value> = options.map(|options| {
|
||||||
|
let option_map: BTreeMap<_, _> = options
|
||||||
|
.into_iter()
|
||||||
|
.map(|(key, value)| (cbor_text!(key), cbor_bool!(value)))
|
||||||
|
.collect();
|
||||||
|
cbor_map_btree!(option_map)
|
||||||
|
});
|
||||||
|
|
||||||
|
cbor_map_options! {
|
||||||
|
0x01 => cbor_array_vec!(versions),
|
||||||
|
0x02 => extensions.map(|vec| cbor_array_vec!(vec)),
|
||||||
|
0x03 => &aaguid,
|
||||||
|
0x04 => options_cbor,
|
||||||
|
0x05 => max_msg_size,
|
||||||
|
0x06 => pin_protocols.map(|vec| cbor_array_vec!(vec)),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(test, derive(PartialEq))]
|
#[cfg_attr(test, derive(PartialEq))]
|
||||||
@@ -183,6 +220,8 @@ impl From<AuthenticatorClientPinResponse> for cbor::Value {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::super::data_formats::PackedAttestationStatement;
|
use super::super::data_formats::PackedAttestationStatement;
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
|
use super::super::ES256_CRED_PARAM;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -243,17 +282,58 @@ mod test {
|
|||||||
options: None,
|
options: None,
|
||||||
max_msg_size: None,
|
max_msg_size: None,
|
||||||
pin_protocols: None,
|
pin_protocols: None,
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
max_credential_count_in_list: None,
|
max_credential_count_in_list: None,
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
max_credential_id_length: None,
|
max_credential_id_length: None,
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
transports: None,
|
transports: None,
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
algorithms: None,
|
algorithms: None,
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
firmware_version: None,
|
firmware_version: None,
|
||||||
};
|
};
|
||||||
let response_cbor: Option<cbor::Value> =
|
let response_cbor: Option<cbor::Value> =
|
||||||
ResponseData::AuthenticatorGetInfo(get_info_response).into();
|
ResponseData::AuthenticatorGetInfo(get_info_response).into();
|
||||||
let expected_cbor = cbor_map_options! {
|
let expected_cbor = cbor_map_options! {
|
||||||
1 => cbor_array_vec![vec!["FIDO_2_0"]],
|
0x01 => cbor_array_vec![vec!["FIDO_2_0"]],
|
||||||
3 => vec![0x00; 16],
|
0x03 => vec![0x00; 16],
|
||||||
|
};
|
||||||
|
assert_eq!(response_cbor, Some(expected_cbor));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "with_ctap2_1")]
|
||||||
|
fn test_get_info_optionals_into_cbor() {
|
||||||
|
let mut options_map = BTreeMap::new();
|
||||||
|
options_map.insert(String::from("rk"), true);
|
||||||
|
let get_info_response = AuthenticatorGetInfoResponse {
|
||||||
|
versions: vec!["FIDO_2_0".to_string()],
|
||||||
|
extensions: Some(vec!["extension".to_string()]),
|
||||||
|
aaguid: [0x00; 16],
|
||||||
|
options: Some(options_map),
|
||||||
|
max_msg_size: Some(1024),
|
||||||
|
pin_protocols: Some(vec![1]),
|
||||||
|
max_credential_count_in_list: Some(20),
|
||||||
|
max_credential_id_length: Some(256),
|
||||||
|
transports: Some(vec![AuthenticatorTransport::Usb]),
|
||||||
|
algorithms: Some(vec![ES256_CRED_PARAM]),
|
||||||
|
firmware_version: Some(0),
|
||||||
|
};
|
||||||
|
let response_cbor: Option<cbor::Value> =
|
||||||
|
ResponseData::AuthenticatorGetInfo(get_info_response).into();
|
||||||
|
let expected_cbor = cbor_map_options! {
|
||||||
|
0x01 => cbor_array_vec![vec!["FIDO_2_0"]],
|
||||||
|
0x02 => cbor_array_vec![vec!["extension"]],
|
||||||
|
0x03 => vec![0x00; 16],
|
||||||
|
0x04 => cbor_map! {"rk" => true},
|
||||||
|
0x05 => 1024,
|
||||||
|
0x06 => cbor_array_vec![vec![1]],
|
||||||
|
0x07 => 20,
|
||||||
|
0x08 => 256,
|
||||||
|
0x09 => cbor_array_vec![vec!["usb"]],
|
||||||
|
0x0A => cbor_array_vec![vec![ES256_CRED_PARAM]],
|
||||||
|
0x0E => 0,
|
||||||
};
|
};
|
||||||
assert_eq!(response_cbor, Some(expected_cbor));
|
assert_eq!(response_cbor, Some(expected_cbor));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user