Skip to content
Snippets Groups Projects
Commit 718514d2 authored by Julien Cretin's avatar Julien Cretin
Browse files

Merge branch 'master' into no_wfr

parents 5c2b72ce bd687084
No related branches found
No related tags found
No related merge requests found
...@@ -4,18 +4,16 @@ ...@@ -4,18 +4,16 @@
"owner": "yapf-diff", "owner": "yapf-diff",
"pattern": [ "pattern": [
{ {
"regexp": "^[+-]{3}\\s*([^\\s]*)\\s*\\((original|reformatted)\\)$", "regexp": "^---\\s*([^\\s]*)\\s*\\(original\\)$",
"file": 1 "file": 1
}, },
{ {
"regexp": "^@@\\s*-(\\d+),(\\d+)\\s*\\+(\\d+),(\\d+)\\s*@@$", "regexp": "^\\+\\+\\+\\s*([^\\s]*)\\s*\\((.*)\\)$",
"line": 1, "message": 2
"column": 2
}, },
{ {
"regexp": "^(\\s|\\+[^+]|\\-[^-]).*$", "regexp": "^@@\\s*-(\\d+),(\\d+)\\s*\\+(\\d+),(\\d+)\\s*@@$",
"loop": true, "line": 1
"message": 1
} }
] ]
}, },
......
...@@ -40,6 +40,12 @@ jobs: ...@@ -40,6 +40,12 @@ jobs:
command: check command: check
args: --target thumbv7em-none-eabi --release --features with_ctap1 args: --target thumbv7em-none-eabi --release --features with_ctap1
- name: Check OpenSK with_ctap2_1
uses: actions-rs/cargo@v1
with:
command: check
args: --target thumbv7em-none-eabi --release --features with_ctap2_1
- name: Check OpenSK debug_ctap - name: Check OpenSK debug_ctap
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
...@@ -76,11 +82,17 @@ jobs: ...@@ -76,11 +82,17 @@ jobs:
command: check command: check
args: --target thumbv7em-none-eabi --release --features debug_ctap,with_ctap1 args: --target thumbv7em-none-eabi --release --features debug_ctap,with_ctap1
- name: Check OpenSK debug_ctap,with_ctap1,panic_console,debug_allocations,verbose - name: Check OpenSK debug_ctap,with_ctap2_1
uses: actions-rs/cargo@v1
with:
command: check
args: --target thumbv7em-none-eabi --release --features debug_ctap,with_ctap2_1
- name: Check OpenSK debug_ctap,with_ctap1,with_ctap2_1,panic_console,debug_allocations,verbose
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: check command: check
args: --target thumbv7em-none-eabi --release --features debug_ctap,with_ctap1,panic_console,debug_allocations,verbose args: --target thumbv7em-none-eabi --release --features debug_ctap,with_ctap1,with_ctap2_1,panic_console,debug_allocations,verbose
- name: Check examples - name: Check examples
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
......
...@@ -49,3 +49,27 @@ jobs: ...@@ -49,3 +49,27 @@ jobs:
command: test command: test
args: --features std,with_ctap1 args: --features std,with_ctap1
- name: Unit testing of CTAP2 (release mode + CTAP2.1)
uses: actions-rs/cargo@v1
with:
command: test
args: --release --features std,with_ctap2_1
- name: Unit testing of CTAP2 (debug mode + CTAP2.1)
uses: actions-rs/cargo@v1
with:
command: test
args: --features std,with_ctap2_1
- name: Unit testing of CTAP2 (release mode + CTAP1 + CTAP2.1)
uses: actions-rs/cargo@v1
with:
command: test
args: --release --features std,with_ctap1,with_ctap2_1
- name: Unit testing of CTAP2 (debug mode + CTAP1 + CTAP2.1)
uses: actions-rs/cargo@v1
with:
command: test
args: --features std,with_ctap1,with_ctap2_1
...@@ -25,6 +25,7 @@ std = ["cbor/std", "crypto/std", "crypto/derive_debug"] ...@@ -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"
......
...@@ -29,6 +29,10 @@ Although we tested and implemented our firmware based on the published ...@@ -29,6 +29,10 @@ 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. be FIDO Certified.
We started adding features of the upcoming next version of the
[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 `--ctap2.1` to the deploy command to include them.
### Cryptography ### Cryptography
......
...@@ -251,6 +251,7 @@ class OpenSKInstaller: ...@@ -251,6 +251,7 @@ class OpenSKInstaller:
self.tockloader_default_args = argparse.Namespace( self.tockloader_default_args = argparse.Namespace(
arch=board.arch, arch=board.arch,
board=self.args.board, board=self.args.board,
bundle_apps=False,
debug=False, debug=False,
force=False, force=False,
jlink_cmd="JLinkExe", jlink_cmd="JLinkExe",
...@@ -785,6 +786,14 @@ if __name__ == "__main__": ...@@ -785,6 +786,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 ...@@ -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,16 @@ then ...@@ -86,4 +87,16 @@ 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
echo "Running unit tests on the desktop (release mode + CTAP1 + CTAP2.1)..."
cargo test --release --features std,with_ctap1,with_ctap2_1
echo "Running unit tests on the desktop (debug mode + CTAP1 + CTAP2.1)..."
cargo test --features std,with_ctap1,with_ctap2_1
fi fi
...@@ -13,16 +13,21 @@ ...@@ -13,16 +13,21 @@
// limitations under the License. // limitations under the License.
use super::data_formats::{ use super::data_formats::{
ok_or_missing, read_array, read_byte_string, read_integer, read_map, read_text_string, ok_or_missing, read_array, read_byte_string, read_map, read_text_string, read_unsigned,
read_unsigned, ClientPinSubCommand, CoseKey, Extensions, GetAssertionOptions, ClientPinSubCommand, CoseKey, Extensions, GetAssertionOptions, MakeCredentialOptions,
MakeCredentialOptions, PublicKeyCredentialDescriptor, PublicKeyCredentialRpEntity, PublicKeyCredentialDescriptor, PublicKeyCredentialParameter, PublicKeyCredentialRpEntity,
PublicKeyCredentialType, PublicKeyCredentialUserEntity, PublicKeyCredentialUserEntity,
}; };
use super::status_code::Ctap2StatusCode; use super::status_code::Ctap2StatusCode;
use alloc::string::String; 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 in
// MakeCredential and GetAssertion. This affects allowList and excludeList.
// You might also want to set the max credential size in process_get_info then.
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))]
pub enum Command { pub enum Command {
...@@ -106,7 +111,7 @@ pub struct AuthenticatorMakeCredentialParameters { ...@@ -106,7 +111,7 @@ pub struct AuthenticatorMakeCredentialParameters {
pub client_data_hash: Vec<u8>, pub client_data_hash: Vec<u8>,
pub rp: PublicKeyCredentialRpEntity, pub rp: PublicKeyCredentialRpEntity,
pub user: PublicKeyCredentialUserEntity, pub user: PublicKeyCredentialUserEntity,
pub pub_key_cred_params: Vec<(PublicKeyCredentialType, i64)>, pub pub_key_cred_params: Vec<PublicKeyCredentialParameter>,
pub exclude_list: Option<Vec<PublicKeyCredentialDescriptor>>, pub exclude_list: Option<Vec<PublicKeyCredentialDescriptor>>,
pub extensions: Option<Extensions>, pub extensions: Option<Extensions>,
// Even though options are optional, we can use the default if not present. // Even though options are optional, we can use the default if not present.
...@@ -132,23 +137,19 @@ impl TryFrom<cbor::Value> for AuthenticatorMakeCredentialParameters { ...@@ -132,23 +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()
let cred_param_map = read_map(cred_param_map_value)?; .map(PublicKeyCredentialParameter::try_from)
let cred_type = PublicKeyCredentialType::try_from(ok_or_missing( .collect::<Result<Vec<PublicKeyCredentialParameter>, Ctap2StatusCode>>()?;
cred_param_map.get(&cbor_text!("type")),
)?)?;
let alg = read_integer(ok_or_missing(cred_param_map.get(&cbor_text!("alg")))?)?;
pub_key_cred_params.push((cred_type, alg));
}
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()
exclude_list.push(PublicKeyCredentialDescriptor::try_from(exclude_list_value)?); .take(MAX_CREDENTIAL_COUNT_IN_LIST.unwrap_or(exclude_list_vec.len()))
} .map(PublicKeyCredentialDescriptor::try_from)
.collect::<Result<Vec<PublicKeyCredentialDescriptor>, Ctap2StatusCode>>()?;
Some(exclude_list) Some(exclude_list)
} }
None => None, None => None,
...@@ -216,10 +217,11 @@ impl TryFrom<cbor::Value> for AuthenticatorGetAssertionParameters { ...@@ -216,10 +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()
allow_list.push(PublicKeyCredentialDescriptor::try_from(allow_list_value)?); .take(MAX_CREDENTIAL_COUNT_IN_LIST.unwrap_or(allow_list_vec.len()))
} .map(PublicKeyCredentialDescriptor::try_from)
.collect::<Result<Vec<PublicKeyCredentialDescriptor>, Ctap2StatusCode>>()?;
Some(allow_list) Some(allow_list)
} }
None => None, None => None,
...@@ -316,8 +318,10 @@ impl TryFrom<cbor::Value> for AuthenticatorClientPinParameters { ...@@ -316,8 +318,10 @@ impl TryFrom<cbor::Value> for AuthenticatorClientPinParameters {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::super::data_formats::{ use super::super::data_formats::{
AuthenticatorTransport, PublicKeyCredentialRpEntity, PublicKeyCredentialUserEntity, AuthenticatorTransport, PublicKeyCredentialRpEntity, PublicKeyCredentialType,
PublicKeyCredentialUserEntity,
}; };
use super::super::ES256_CRED_PARAM;
use super::*; use super::*;
use alloc::collections::BTreeMap; use alloc::collections::BTreeMap;
...@@ -336,10 +340,7 @@ mod test { ...@@ -336,10 +340,7 @@ mod test {
"displayName" => "bar", "displayName" => "bar",
"icon" => "example.com/foo/icon.png", "icon" => "example.com/foo/icon.png",
}, },
4 => cbor_array![ cbor_map! { 4 => cbor_array![ES256_CRED_PARAM],
"type" => "public-key",
"alg" => -7
} ],
5 => cbor_array![], 5 => cbor_array![],
8 => vec![0x12, 0x34], 8 => vec![0x12, 0x34],
9 => 1, 9 => 1,
...@@ -362,7 +363,6 @@ mod test { ...@@ -362,7 +363,6 @@ mod test {
user_display_name: Some("bar".to_string()), user_display_name: Some("bar".to_string()),
user_icon: Some("example.com/foo/icon.png".to_string()), user_icon: Some("example.com/foo/icon.png".to_string()),
}; };
let pub_key_cred_param = (PublicKeyCredentialType::PublicKey, -7);
let options = MakeCredentialOptions { let options = MakeCredentialOptions {
rk: false, rk: false,
uv: false, uv: false,
...@@ -371,7 +371,7 @@ mod test { ...@@ -371,7 +371,7 @@ mod test {
client_data_hash, client_data_hash,
rp, rp,
user, user,
pub_key_cred_params: vec![pub_key_cred_param], 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; ...@@ -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 { ...@@ -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 { ...@@ -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 => "unknown",
} }
.into() .into()
} }
...@@ -116,11 +124,43 @@ impl TryFrom<&cbor::Value> for PublicKeyCredentialType { ...@@ -116,11 +124,43 @@ 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)]
#[cfg_attr(any(test, feature = "debug_ctap"), derive(Debug))]
pub struct PublicKeyCredentialParameter {
pub cred_type: PublicKeyCredentialType,
pub alg: SignatureAlgorithm,
}
impl TryFrom<&cbor::Value> for PublicKeyCredentialParameter {
type Error = Ctap2StatusCode;
fn try_from(cbor_value: &cbor::Value) -> Result<Self, Ctap2StatusCode> {
let cred_param_map = read_map(cbor_value)?;
let cred_type = PublicKeyCredentialType::try_from(ok_or_missing(
cred_param_map.get(&cbor_text!("type")),
)?)?;
let alg =
SignatureAlgorithm::try_from(ok_or_missing(cred_param_map.get(&cbor_text!("alg")))?)?;
Ok(Self { cred_type, alg })
}
}
impl From<PublicKeyCredentialParameter> for cbor::Value {
fn from(cred_param: PublicKeyCredentialParameter) -> Self {
cbor_map_options! {
"type" => cred_param.cred_type,
"alg" => cred_param.alg as i64,
}
}
}
// 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,
...@@ -156,6 +196,7 @@ impl TryFrom<&cbor::Value> for AuthenticatorTransport { ...@@ -156,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,
...@@ -175,10 +216,11 @@ impl TryFrom<&cbor::Value> for PublicKeyCredentialDescriptor { ...@@ -175,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,
...@@ -349,6 +391,7 @@ impl TryFrom<&cbor::Value> for GetAssertionOptions { ...@@ -349,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 {
...@@ -369,12 +412,27 @@ impl From<PackedAttestationStatement> for cbor::Value { ...@@ -369,12 +412,27 @@ impl From<PackedAttestationStatement> for cbor::Value {
} }
} }
#[cfg_attr(test, derive(PartialEq))] #[derive(PartialEq)]
#[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 {
type Error = Ctap2StatusCode;
fn try_from(cbor_value: &cbor::Value) -> Result<Self, Ctap2StatusCode> {
match read_integer(cbor_value)? {
ecdsa::PubKey::ES256_ALGORITHM => Ok(SignatureAlgorithm::ES256),
_ => 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))]
...@@ -638,6 +696,7 @@ mod test { ...@@ -638,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() {
...@@ -949,6 +1008,26 @@ mod test { ...@@ -949,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]
...@@ -965,6 +1044,23 @@ mod test { ...@@ -965,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! {
...@@ -985,7 +1081,7 @@ mod test { ...@@ -985,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,
}; };
...@@ -995,6 +1091,53 @@ mod test { ...@@ -995,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]
...@@ -1046,8 +1189,6 @@ mod test { ...@@ -1046,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();
...@@ -1068,8 +1209,6 @@ mod test { ...@@ -1068,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,14 +23,18 @@ pub mod status_code; ...@@ -23,14 +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, AuthenticatorMakeCredentialParameters, Command,
}; };
#[cfg(feature = "with_ctap2_1")]
use self::data_formats::AuthenticatorTransport;
use self::data_formats::{ use self::data_formats::{
ClientPinSubCommand, CoseKey, GetAssertionHmacSecretInput, PackedAttestationStatement, ClientPinSubCommand, CoseKey, GetAssertionHmacSecretInput, PackedAttestationStatement,
PublicKeyCredentialDescriptor, PublicKeyCredentialSource, PublicKeyCredentialType, PublicKeyCredentialDescriptor, PublicKeyCredentialParameter, PublicKeyCredentialSource,
PublicKeyCredentialUserEntity, SignatureAlgorithm, PublicKeyCredentialType, PublicKeyCredentialUserEntity, 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};
...@@ -99,6 +103,13 @@ pub const FIDO2_VERSION_STRING: &str = "FIDO_2_0"; ...@@ -99,6 +103,13 @@ 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";
// 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,
alg: SignatureAlgorithm::ES256,
};
fn check_pin_auth(hmac_key: &[u8], hmac_contents: &[u8], pin_auth: &[u8]) -> bool { fn check_pin_auth(hmac_key: &[u8], hmac_contents: &[u8], pin_auth: &[u8]) -> bool {
if pin_auth.len() != PIN_AUTH_LENGTH { if pin_auth.len() != PIN_AUTH_LENGTH {
return false; return false;
...@@ -413,15 +424,7 @@ where ...@@ -413,15 +424,7 @@ where
} }
} }
let has_es_256 = pub_key_cred_params if !pub_key_cred_params.contains(&ES256_CRED_PARAM) {
.iter()
.any(|(credential_type, algorithm)| {
// Even though there is only one type now, checking seems safer in
// case of extension so you can't forget to update here.
*credential_type == PublicKeyCredentialType::PublicKey
&& *algorithm == SignatureAlgorithm::ES256 as i64
});
if !has_es_256 {
return Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_ALGORITHM); return Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_ALGORITHM);
} }
...@@ -751,7 +754,7 @@ where ...@@ -751,7 +754,7 @@ 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 FIDO 2.1 options // 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(String::from("up"), true);
options_map.insert( options_map.insert(
...@@ -772,6 +775,18 @@ where ...@@ -772,6 +775,18 @@ where
pin_protocols: Some(vec![ pin_protocols: Some(vec![
CtapState::<R, CheckUserPresence>::PIN_PROTOCOL_VERSION, CtapState::<R, CheckUserPresence>::PIN_PROTOCOL_VERSION,
]), ]),
#[cfg(feature = "with_ctap2_1")]
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
// browser passes that value, it might be used to fingerprint.
#[cfg(feature = "with_ctap2_1")]
max_credential_id_length: None,
#[cfg(feature = "with_ctap2_1")]
transports: Some(vec![AuthenticatorTransport::Usb]),
#[cfg(feature = "with_ctap2_1")]
algorithms: Some(vec![ES256_CRED_PARAM]),
#[cfg(feature = "with_ctap2_1")]
firmware_version: None,
}, },
)) ))
} }
...@@ -1093,6 +1108,9 @@ mod test { ...@@ -1093,6 +1108,9 @@ 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];
#[cfg(not(feature = "with_ctap2_1"))]
let mut expected_response = vec![0x00, 0xA6, 0x01]; 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"))]
...@@ -1111,6 +1129,15 @@ mod test { ...@@ -1111,6 +1129,15 @@ mod test {
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,
0x64, 0x74, 0x79, 0x70, 0x65, 0x6A, 0x70, 0x75, 0x62, 0x6C, 0x69, 0x63, 0x2D, 0x6B,
0x65, 0x79,
]
.iter(),
);
assert_eq!(info_reponse, expected_response); assert_eq!(info_reponse, expected_response);
} }
...@@ -1128,10 +1155,7 @@ mod test { ...@@ -1128,10 +1155,7 @@ mod test {
user_display_name: None, user_display_name: None,
user_icon: None, user_icon: None,
}; };
let pub_key_cred_params = vec![( let pub_key_cred_params = vec![ES256_CRED_PARAM];
PublicKeyCredentialType::PublicKey,
SignatureAlgorithm::ES256 as i64,
)];
let options = MakeCredentialOptions { let options = MakeCredentialOptions {
rk: true, rk: true,
uv: false, uv: false,
...@@ -1228,12 +1252,8 @@ mod test { ...@@ -1228,12 +1252,8 @@ mod test {
let user_immediately_present = |_| Ok(()); let user_immediately_present = |_| Ok(());
let mut ctap_state = CtapState::new(&mut rng, user_immediately_present); let mut ctap_state = CtapState::new(&mut rng, user_immediately_present);
let pub_key_cred_params = vec![(
PublicKeyCredentialType::PublicKey,
SignatureAlgorithm::ES256 as i64 + 1, // any different number works
)];
let mut make_credential_params = create_minimal_make_credential_parameters(); let mut make_credential_params = create_minimal_make_credential_parameters();
make_credential_params.pub_key_cred_params = pub_key_cred_params; make_credential_params.pub_key_cred_params = vec![];
let make_credential_response = let make_credential_response =
ctap_state.process_make_credential(make_credential_params, DUMMY_CHANNEL_ID); ctap_state.process_make_credential(make_credential_params, DUMMY_CHANNEL_ID);
......
...@@ -12,6 +12,8 @@ ...@@ -12,6 +12,8 @@
// 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::{
CoseKey, PackedAttestationStatement, PublicKeyCredentialDescriptor, CoseKey, PackedAttestationStatement, PublicKeyCredentialDescriptor,
PublicKeyCredentialUserEntity, PublicKeyCredentialUserEntity,
...@@ -102,16 +104,27 @@ impl From<AuthenticatorGetAssertionResponse> for cbor::Value { ...@@ -102,16 +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>,
#[cfg(feature = "with_ctap2_1")]
pub max_credential_id_length: Option<u64>,
#[cfg(feature = "with_ctap2_1")]
pub transports: Option<Vec<AuthenticatorTransport>>,
#[cfg(feature = "with_ctap2_1")]
pub algorithms: Option<Vec<PublicKeyCredentialParameter>>,
#[cfg(feature = "with_ctap2_1")]
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,
...@@ -120,6 +133,11 @@ impl From<AuthenticatorGetInfoResponse> for cbor::Value { ...@@ -120,6 +133,11 @@ impl From<AuthenticatorGetInfoResponse> for cbor::Value {
options, options,
max_msg_size, max_msg_size,
pin_protocols, pin_protocols,
max_credential_count_in_list,
max_credential_id_length,
transports,
algorithms,
firmware_version,
} = get_info_response; } = get_info_response;
let options_cbor: Option<cbor::Value> = options.map(|options| { let options_cbor: Option<cbor::Value> = options.map(|options| {
...@@ -131,12 +149,46 @@ impl From<AuthenticatorGetInfoResponse> for cbor::Value { ...@@ -131,12 +149,46 @@ impl From<AuthenticatorGetInfoResponse> for cbor::Value {
}); });
cbor_map_options! { cbor_map_options! {
1 => cbor_array_vec!(versions), 0x01 => cbor_array_vec!(versions),
2 => extensions.map(|vec| cbor_array_vec!(vec)), 0x02 => extensions.map(|vec| cbor_array_vec!(vec)),
3 => &aaguid, 0x03 => &aaguid,
4 => options_cbor, 0x04 => options_cbor,
5 => max_msg_size, 0x05 => max_msg_size,
6 => pin_protocols.map(|vec| cbor_array_vec!(vec)), 0x06 => pin_protocols.map(|vec| cbor_array_vec!(vec)),
0x07 => max_credential_count_in_list,
0x08 => max_credential_id_length,
0x09 => transports.map(|vec| cbor_array_vec!(vec)),
0x0A => algorithms.map(|vec| cbor_array_vec!(vec)),
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)),
} }
} }
} }
...@@ -168,6 +220,8 @@ impl From<AuthenticatorClientPinResponse> for cbor::Value { ...@@ -168,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]
...@@ -228,12 +282,58 @@ mod test { ...@@ -228,12 +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,
#[cfg(feature = "with_ctap2_1")]
max_credential_id_length: None,
#[cfg(feature = "with_ctap2_1")]
transports: None,
#[cfg(feature = "with_ctap2_1")]
algorithms: None,
#[cfg(feature = "with_ctap2_1")]
firmware_version: None,
};
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"]],
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> = 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], 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));
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment