mirror of
https://github.com/trussed-dev/ctap-types.git
synced 2026-06-20 04:16:17 -07:00
Apply a round of cargo clippy + fmt
This commit is contained in:
+10
-9
@@ -2,23 +2,24 @@
|
||||
name = "ctap-types"
|
||||
version = "0.1.0"
|
||||
authors = ["Nicolas Stalder <n@stalder.io>"]
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
bitflags = "1.2.1"
|
||||
bitflags = "1.3"
|
||||
cbor-smol = "0.4"
|
||||
cosey = "0.3.0"
|
||||
delog = "0.1.0"
|
||||
cosey = "0.3"
|
||||
delog = "0.1"
|
||||
heapless = { version = "0.7", default-features = false, features = ["serde"] }
|
||||
heapless-bytes = "0.3.0"
|
||||
interchange = "0.2.0"
|
||||
serde = { version = "1.0", default-features = false, features = ["derive"] }
|
||||
serde-indexed = "0.1.0"
|
||||
heapless-bytes = "0.3"
|
||||
interchange = "0.2.1"
|
||||
serde = { version = "1", default-features = false, features = ["derive"] }
|
||||
serde-indexed = "0.1"
|
||||
serde_repr = "0.1"
|
||||
|
||||
iso7816 = { git = "https://github.com/ycrypto/iso7816" }
|
||||
# iso7816 = { git = "https://github.com/ycrypto/iso7816" }
|
||||
iso7816 = "0.1.0-alpha.1"
|
||||
|
||||
[features]
|
||||
log-all = ["cbor-smol/log-all"]
|
||||
|
||||
+11
-5
@@ -7,14 +7,20 @@
|
||||
// }
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
// clippy says (2022-02-26): large size difference
|
||||
// - first is 88 bytes
|
||||
// - second is 10456 bytes
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum Request {
|
||||
Ctap1(ctap1::Request),
|
||||
Ctap2(ctap2::Request),
|
||||
}
|
||||
|
||||
// see below
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
// #[derive(Debug)]
|
||||
// clippy says...large size difference
|
||||
// - first is 0 bytes
|
||||
// - second is 1880 bytes
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum Response {
|
||||
Ctap1(ctap1::Response),
|
||||
Ctap2(ctap2::Response),
|
||||
@@ -36,12 +42,13 @@ pub mod ctap1 {
|
||||
#[allow(non_camel_case_types)]
|
||||
_unused,
|
||||
}
|
||||
|
||||
}
|
||||
pub mod ctap2 {
|
||||
pub use crate::ctap2::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
// clippy says...large size difference
|
||||
pub enum Request {
|
||||
// 0x1
|
||||
MakeCredential(make_credential::Parameters),
|
||||
@@ -74,12 +81,11 @@ pub mod ctap2 {
|
||||
// Q: how to handle the associated CBOR structures
|
||||
Vendor,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// pub type Result<T> = core::result::Result<T, Error>;
|
||||
|
||||
#[derive(Clone,Copy,Debug, Eq,PartialEq)]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum Error {
|
||||
Success = 0x00,
|
||||
InvalidCommand = 0x01,
|
||||
|
||||
+3
-3
@@ -2,18 +2,18 @@
|
||||
//!
|
||||
//! Data types and serde for public COSE_Keys
|
||||
//!
|
||||
//! https://tools.ietf.org/html/rfc8152#section-7
|
||||
//! <https://tools.ietf.org/html/rfc8152#section-7>
|
||||
//!
|
||||
//! A COSE Key structure is built on a CBOR map object. The set of
|
||||
//! common parameters that can appear in a COSE Key can be found in the
|
||||
//! IANA "COSE Key Common Parameters" registry (Section 16.5).
|
||||
//!
|
||||
//! https://www.iana.org/assignments/cose/cose.xhtml#key-common-parameters
|
||||
//! <https://www.iana.org/assignments/cose/cose.xhtml#key-common-parameters>
|
||||
//!
|
||||
//! Additional parameters defined for specific key types can be found in
|
||||
//! the IANA "COSE Key Type Parameters" registry (Section 16.6).
|
||||
//!
|
||||
//! https://www.iana.org/assignments/cose/cose.xhtml#key-type-parameters
|
||||
//! <https://www.iana.org/assignments/cose/cose.xhtml#key-type-parameters>
|
||||
//!
|
||||
//!
|
||||
//! Key Type 1 (OKP)
|
||||
|
||||
+43
-48
@@ -1,7 +1,4 @@
|
||||
use iso7816::{
|
||||
Command as ApduCommand,
|
||||
Instruction,
|
||||
};
|
||||
use iso7816::{Command as ApduCommand, Instruction};
|
||||
|
||||
use crate::Bytes;
|
||||
|
||||
@@ -10,13 +7,13 @@ pub const NO_ERROR: u16 = 0x9000;
|
||||
pub use iso7816::Status as Error;
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Copy,Clone,Debug, Eq,PartialEq)]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum ControlByte {
|
||||
// Conor:
|
||||
// Conor:
|
||||
// I think U2F check-only maps to FIDO2 MakeCredential with the credID in the excludeList,
|
||||
// and pinAuth="" so the request will fail before UP check.
|
||||
// I think this is what the windows hello API does to silently check if a credential is
|
||||
// on an authenticator
|
||||
// on an authenticator
|
||||
CheckOnly = 0x07,
|
||||
EnforceUserPresenceAndSign = 0x03,
|
||||
DontEnforceUserPresenceAndSign = 0x08,
|
||||
@@ -37,13 +34,13 @@ impl core::convert::TryFrom<u8> for ControlByte {
|
||||
|
||||
pub type Result<T> = core::result::Result<T, Error>;
|
||||
|
||||
#[derive(Clone,Debug, Eq,PartialEq)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct Register {
|
||||
pub challenge: Bytes<32>,
|
||||
pub app_id: Bytes<32>,
|
||||
}
|
||||
|
||||
#[derive(Clone,Debug, Eq,PartialEq)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct RegisterResponse {
|
||||
pub header_byte: u8,
|
||||
pub public_key: Bytes<65>,
|
||||
@@ -52,7 +49,7 @@ pub struct RegisterResponse {
|
||||
pub signature: Bytes<72>,
|
||||
}
|
||||
|
||||
#[derive(Clone,Debug, Eq,PartialEq)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct Authenticate {
|
||||
pub control_byte: ControlByte,
|
||||
pub challenge: Bytes<32>,
|
||||
@@ -60,21 +57,23 @@ pub struct Authenticate {
|
||||
pub key_handle: Bytes<255>,
|
||||
}
|
||||
|
||||
#[derive(Clone,Debug, Eq,PartialEq)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct AuthenticateResponse {
|
||||
user_presence: u8,
|
||||
count: u32,
|
||||
signature: Bytes<72>,
|
||||
}
|
||||
|
||||
#[derive(Clone,Debug, Eq,PartialEq)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum Command {
|
||||
Register(Register),
|
||||
Authenticate(Authenticate),
|
||||
Version,
|
||||
}
|
||||
|
||||
#[derive(Clone,Debug, Eq,PartialEq)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum Response {
|
||||
Register(RegisterResponse),
|
||||
Authenticate(AuthenticateResponse),
|
||||
@@ -89,10 +88,9 @@ impl RegisterResponse {
|
||||
signature: Bytes<72>,
|
||||
attestation_certificate: &[u8],
|
||||
) -> Self {
|
||||
|
||||
debug_assert!(key_handle.len()<=255);
|
||||
debug_assert!(attestation_certificate.len()<=1024);
|
||||
debug_assert!(signature.len()<=72);
|
||||
debug_assert!(key_handle.len() <= 255);
|
||||
debug_assert!(attestation_certificate.len() <= 1024);
|
||||
debug_assert!(signature.len() <= 72);
|
||||
|
||||
let mut public_key_bytes = Bytes::new();
|
||||
let mut key_handle_bytes = Bytes::new();
|
||||
@@ -104,52 +102,51 @@ impl RegisterResponse {
|
||||
|
||||
key_handle_bytes.extend_from_slice(key_handle).unwrap();
|
||||
|
||||
cert_bytes.extend_from_slice(attestation_certificate).unwrap();
|
||||
cert_bytes
|
||||
.extend_from_slice(attestation_certificate)
|
||||
.unwrap();
|
||||
|
||||
Self {
|
||||
header_byte: header_byte,
|
||||
header_byte,
|
||||
public_key: public_key_bytes,
|
||||
key_handle: key_handle_bytes,
|
||||
attestation_certificate: cert_bytes,
|
||||
signature: signature,
|
||||
signature,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AuthenticateResponse {
|
||||
pub fn new(
|
||||
user_presence: u8,
|
||||
count: u32,
|
||||
signature: Bytes<72>,
|
||||
) -> Self {
|
||||
pub fn new(user_presence: u8, count: u32, signature: Bytes<72>) -> Self {
|
||||
Self {
|
||||
user_presence: user_presence,
|
||||
count: count,
|
||||
signature: signature,
|
||||
user_presence,
|
||||
count,
|
||||
signature,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Response {
|
||||
pub fn serialize<const S: usize>(&self, buf: &mut iso7816::Data<S>) -> core::result::Result<(),()>
|
||||
{
|
||||
#[allow(clippy::result_unit_err)]
|
||||
pub fn serialize<const S: usize>(
|
||||
&self,
|
||||
buf: &mut iso7816::Data<S>,
|
||||
) -> core::result::Result<(), ()> {
|
||||
match self {
|
||||
Response::Register(reg) => {
|
||||
buf.push(reg.header_byte).ok();
|
||||
buf.extend_from_slice(®.public_key).ok();
|
||||
buf.push(reg.key_handle.len() as u8).ok();
|
||||
buf.extend_from_slice(®.key_handle).ok();
|
||||
buf.extend_from_slice(®.attestation_certificate).ok();
|
||||
buf.push(reg.header_byte).map_err(drop)?;
|
||||
buf.extend_from_slice(®.public_key)?;
|
||||
buf.push(reg.key_handle.len() as u8).map_err(drop)?;
|
||||
buf.extend_from_slice(®.key_handle)?;
|
||||
buf.extend_from_slice(®.attestation_certificate)?;
|
||||
buf.extend_from_slice(®.signature)
|
||||
},
|
||||
Response::Authenticate(auth) => {
|
||||
buf.push(auth.user_presence).ok();
|
||||
buf.extend_from_slice(&auth.count.to_be_bytes()).ok();
|
||||
buf.extend_from_slice(&auth.signature)
|
||||
},
|
||||
Response::Version(version) => {
|
||||
buf.extend_from_slice(version)
|
||||
}
|
||||
Response::Authenticate(auth) => {
|
||||
buf.push(auth.user_presence).map_err(drop)?;
|
||||
buf.extend_from_slice(&auth.count.to_be_bytes())?;
|
||||
buf.extend_from_slice(&auth.signature)
|
||||
}
|
||||
Response::Version(version) => buf.extend_from_slice(version),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -186,7 +183,7 @@ impl<const S: usize> core::convert::TryFrom<&ApduCommand<S>> for Command {
|
||||
challenge: Bytes::from_slice(&request[..32]).unwrap(),
|
||||
app_id: Bytes::from_slice(&request[32..]).unwrap(),
|
||||
}))
|
||||
},
|
||||
}
|
||||
|
||||
// authenticate
|
||||
0x2 => {
|
||||
@@ -204,12 +201,10 @@ impl<const S: usize> core::convert::TryFrom<&ApduCommand<S>> for Command {
|
||||
app_id: Bytes::from_slice(&request[32..64]).unwrap(),
|
||||
key_handle: Bytes::from_slice(&request[65..]).unwrap(),
|
||||
}))
|
||||
},
|
||||
}
|
||||
|
||||
// version
|
||||
0x3 => {
|
||||
Ok(Command::Version)
|
||||
}
|
||||
0x3 => Ok(Command::Version),
|
||||
|
||||
_ => Err(Error::InstructionNotSupportedOrInvalid),
|
||||
}
|
||||
|
||||
+11
-8
@@ -1,14 +1,14 @@
|
||||
use bitflags::bitflags;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::Bytes;
|
||||
use crate::sizes::*;
|
||||
use crate::Bytes;
|
||||
|
||||
pub mod client_pin;
|
||||
pub mod credential_management;
|
||||
pub mod get_assertion;
|
||||
pub mod get_next_assertion;
|
||||
pub mod get_info;
|
||||
pub mod get_next_assertion;
|
||||
pub mod make_credential;
|
||||
|
||||
// TODO: this is a bit weird to model...
|
||||
@@ -27,7 +27,7 @@ pub mod make_credential;
|
||||
// // pub cred_protect:
|
||||
// }
|
||||
|
||||
#[derive(Clone,Debug, Eq,PartialEq,Serialize,Deserialize)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct AuthenticatorOptions {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub rk: Option<bool>,
|
||||
@@ -131,7 +131,7 @@ pub trait SerializeAttestedCredentialData {
|
||||
fn serialize(&self) -> Bytes<ATTESTED_CREDENTIAL_DATA_LENGTH>;
|
||||
}
|
||||
|
||||
#[derive(Clone,Debug,Eq,PartialEq)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
// #[serde(rename_all = "camelCase")]
|
||||
pub struct AuthenticatorData<A, E> {
|
||||
pub rp_id_hash: Bytes<32>,
|
||||
@@ -140,7 +140,7 @@ pub struct AuthenticatorData<A, E> {
|
||||
// this can get pretty long
|
||||
// pub attested_credential_data: Option<Bytes<ATTESTED_CREDENTIAL_DATA_LENGTH>>,
|
||||
pub attested_credential_data: Option<A>,
|
||||
pub extensions: Option<E>
|
||||
pub extensions: Option<E>,
|
||||
}
|
||||
|
||||
pub type SerializedAuthenticatorData = Bytes<AUTHENTICATOR_DATA_LENGTH>;
|
||||
@@ -157,11 +157,15 @@ impl<A: SerializeAttestedCredentialData, E: serde::Serialize> AuthenticatorData<
|
||||
// flags
|
||||
bytes.push(self.flags.bits()).unwrap();
|
||||
// signature counts as 32-bit unsigned big-endian integer.
|
||||
bytes.extend_from_slice(&self.sign_count.to_be_bytes()).unwrap();
|
||||
bytes
|
||||
.extend_from_slice(&self.sign_count.to_be_bytes())
|
||||
.unwrap();
|
||||
|
||||
// the attested credential data
|
||||
if let Some(ref attested_credential_data) = &self.attested_credential_data {
|
||||
bytes.extend_from_slice(&attested_credential_data.serialize()).unwrap();
|
||||
bytes
|
||||
.extend_from_slice(&attested_credential_data.serialize())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// the extensions data
|
||||
@@ -175,7 +179,6 @@ impl<A: SerializeAttestedCredentialData, E: serde::Serialize> AuthenticatorData<
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// // TODO: add Default and builder
|
||||
// #[derive(Clone,Debug,Eq,PartialEq,Serialize)]
|
||||
// pub struct AuthenticatorInfo<'l> {
|
||||
|
||||
@@ -4,7 +4,7 @@ use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||
|
||||
use crate::cose::EcdhEsHkdf256PublicKey;
|
||||
|
||||
#[derive(Clone,Debug, Eq,PartialEq,Serialize_repr,Deserialize_repr)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize_repr, Deserialize_repr)]
|
||||
#[repr(u8)]
|
||||
pub enum PinV1Subcommand {
|
||||
GetRetries = 0x01,
|
||||
@@ -21,7 +21,7 @@ pub enum PinV1Subcommand {
|
||||
// maximum PIN length: UTF-8 represented by <= 63 bytes
|
||||
// maximum consecutive incorrect PIN attempts: 8
|
||||
|
||||
#[derive(Clone,Debug, Eq,PartialEq,SerializeIndexed,DeserializeIndexed)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, SerializeIndexed, DeserializeIndexed)]
|
||||
#[serde_indexed(offset = 1)]
|
||||
pub struct Parameters {
|
||||
// 0x01
|
||||
@@ -55,10 +55,9 @@ pub struct Parameters {
|
||||
// Encrypted first 16 bytes of SHA-256 of PIN using `sharedSecret`.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub pin_hash_enc: Option<Bytes<64>>,
|
||||
|
||||
}
|
||||
|
||||
#[derive(Clone,Debug, Eq,PartialEq,SerializeIndexed,DeserializeIndexed)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, SerializeIndexed, DeserializeIndexed)]
|
||||
#[serde_indexed(offset = 1)]
|
||||
pub struct Response {
|
||||
// 0x01, like ClientPinParameters::key_agreement
|
||||
@@ -72,7 +71,6 @@ pub struct Response {
|
||||
// 0x03, number of PIN attempts remaining before lockout
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub retries: Option<u8>,
|
||||
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -5,13 +5,11 @@ use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||
use crate::{
|
||||
cose::PublicKey,
|
||||
webauthn::{
|
||||
PublicKeyCredentialDescriptor,
|
||||
PublicKeyCredentialRpEntity,
|
||||
PublicKeyCredentialUserEntity,
|
||||
}
|
||||
PublicKeyCredentialDescriptor, PublicKeyCredentialRpEntity, PublicKeyCredentialUserEntity,
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Copy,Clone,Debug, Eq,PartialEq,Serialize_repr,Deserialize_repr)]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize_repr, Deserialize_repr)]
|
||||
// #[derive(Clone,Debug,Eq,PartialEq,Serialize, Deserialize)]
|
||||
// #[serde(tag = "credProtect")]
|
||||
#[repr(u8)]
|
||||
@@ -30,21 +28,18 @@ impl core::default::Default for CredentialProtectionPolicy {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[derive(Clone,Copy,Debug, Eq,PartialEq,Serialize_repr,Deserialize_repr)]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize_repr, Deserialize_repr)]
|
||||
#[repr(u8)]
|
||||
pub enum Subcommand {
|
||||
GetCredsMetadata = 0x01, // 1, 2
|
||||
EnumerateRpsBegin = 0x02, // 3, 4, 5
|
||||
EnumerateRpsGetNextRp = 0x03, // 3, 4
|
||||
EnumerateCredentialsBegin = 0x04, // 6, 7, 8 ,9, A
|
||||
pub enum Subcommand {
|
||||
GetCredsMetadata = 0x01, // 1, 2
|
||||
EnumerateRpsBegin = 0x02, // 3, 4, 5
|
||||
EnumerateRpsGetNextRp = 0x03, // 3, 4
|
||||
EnumerateCredentialsBegin = 0x04, // 6, 7, 8 ,9, A
|
||||
EnumerateCredentialsGetNextCredential = 0x05, // 6, 7, 8, A
|
||||
DeleteCredential = 0x06, // -
|
||||
DeleteCredential = 0x06, // -
|
||||
}
|
||||
|
||||
|
||||
#[derive(Clone,Debug, Eq,PartialEq,SerializeIndexed,DeserializeIndexed)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, SerializeIndexed, DeserializeIndexed)]
|
||||
#[serde_indexed(offset = 1)]
|
||||
pub struct SubcommandParameters {
|
||||
// 0x01
|
||||
@@ -55,7 +50,7 @@ pub struct SubcommandParameters {
|
||||
pub credential_id: Option<PublicKeyCredentialDescriptor>,
|
||||
}
|
||||
|
||||
#[derive(Clone,Debug, Eq,PartialEq,SerializeIndexed,DeserializeIndexed)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, SerializeIndexed, DeserializeIndexed)]
|
||||
#[serde_indexed(offset = 1)]
|
||||
pub struct Parameters {
|
||||
// 0x01
|
||||
@@ -71,11 +66,10 @@ pub struct Parameters {
|
||||
pub pin_auth: Option<Bytes16>,
|
||||
}
|
||||
|
||||
#[derive(Clone,Debug, Default,Eq,PartialEq,SerializeIndexed)]
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, SerializeIndexed)]
|
||||
#[serde_indexed(offset = 1)]
|
||||
// #[derive(Clone,Debug, Default,Eq,PartialEq,Serialize,Deserialize)]
|
||||
pub struct Response {
|
||||
|
||||
// Metadata
|
||||
|
||||
// 0x01
|
||||
|
||||
@@ -14,7 +14,7 @@ use crate::webauthn::*;
|
||||
// pub hmac_secret: Option<bool>,
|
||||
// }
|
||||
|
||||
#[derive(Clone,Debug, Eq,PartialEq,SerializeIndexed,DeserializeIndexed)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, SerializeIndexed, DeserializeIndexed)]
|
||||
#[serde_indexed(offset = 1)]
|
||||
pub struct HmacSecretInput {
|
||||
pub key_agreement: EcdhEsHkdf256PublicKey,
|
||||
@@ -22,17 +22,17 @@ pub struct HmacSecretInput {
|
||||
pub salt_enc: Bytes<64>,
|
||||
pub salt_auth: Bytes<16>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub pin_protocol: Option<u32>
|
||||
pub pin_protocol: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Clone,Debug, Eq,PartialEq,Serialize,Deserialize)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct ExtensionsInput {
|
||||
#[serde(rename = "hmac-secret")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub hmac_secret: Option<HmacSecretInput>,
|
||||
}
|
||||
|
||||
#[derive(Clone,Debug, Eq,PartialEq,Serialize,Deserialize,Default)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Default)]
|
||||
pub struct ExtensionsOutput {
|
||||
#[serde(rename = "hmac-secret")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
@@ -40,7 +40,7 @@ pub struct ExtensionsOutput {
|
||||
pub hmac_secret: Option<Bytes<64>>,
|
||||
}
|
||||
|
||||
pub struct NoAttestedCredentialData (core::marker::PhantomData<()>);
|
||||
pub struct NoAttestedCredentialData(core::marker::PhantomData<()>);
|
||||
|
||||
impl super::SerializeAttestedCredentialData for NoAttestedCredentialData {
|
||||
fn serialize(&self) -> Bytes<ATTESTED_CREDENTIAL_DATA_LENGTH> {
|
||||
@@ -52,7 +52,7 @@ pub type AuthenticatorData = super::AuthenticatorData<NoAttestedCredentialData,
|
||||
|
||||
pub type AllowList = Vec<PublicKeyCredentialDescriptor, MAX_CREDENTIAL_COUNT_IN_LIST>;
|
||||
|
||||
#[derive(Clone,Debug, Eq,PartialEq,SerializeIndexed,DeserializeIndexed)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, SerializeIndexed, DeserializeIndexed)]
|
||||
// #[serde(rename_all = "camelCase")]
|
||||
#[serde_indexed(offset = 1)]
|
||||
pub struct Parameters {
|
||||
@@ -73,7 +73,7 @@ pub struct Parameters {
|
||||
// NB: attn object definition / order at end of
|
||||
// https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#authenticatorMakeCredential
|
||||
// does not coincide with what python-fido2 expects in AttestationObject.__init__ *at all* :'-)
|
||||
#[derive(Clone,Debug, Eq,PartialEq,SerializeIndexed,DeserializeIndexed)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, SerializeIndexed, DeserializeIndexed)]
|
||||
#[serde_indexed(offset = 1)]
|
||||
pub struct Response {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
use crate::webauthn::PublicKeyCredentialParameters;
|
||||
use crate::{Bytes, String, Vec};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_indexed::{DeserializeIndexed, SerializeIndexed};
|
||||
use crate::webauthn::PublicKeyCredentialParameters;
|
||||
|
||||
pub type AuthenticatorInfo = Response;
|
||||
|
||||
#[derive(Clone,Debug, Eq,PartialEq,SerializeIndexed,DeserializeIndexed)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, SerializeIndexed, DeserializeIndexed)]
|
||||
#[serde_indexed(offset = 1)]
|
||||
pub struct Response {
|
||||
|
||||
// 0x01
|
||||
pub versions: Vec<String<12>, 4>,
|
||||
|
||||
@@ -55,7 +54,6 @@ pub struct Response {
|
||||
// FIDO_2_1
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub algorithms: Option<Vec<PublicKeyCredentialParameters, 4>>,
|
||||
|
||||
// #[serde(skip_serializing_if = "Option::is_none")]
|
||||
// pub(crate) algorithms: Option<&'l[u8]>,
|
||||
}
|
||||
@@ -69,8 +67,7 @@ impl Default for Response {
|
||||
Self {
|
||||
versions: Vec::new(),
|
||||
extensions: None,
|
||||
aaguid: aaguid,
|
||||
// options: None,
|
||||
aaguid,
|
||||
options: Some(CtapOptions::default()),
|
||||
max_msg_size: None, //Some(MESSAGE_SIZE),
|
||||
pin_protocols: None,
|
||||
@@ -82,7 +79,7 @@ impl Default for Response {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy,Clone,Debug, Eq,PartialEq,Serialize,Deserialize)]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CtapOptions {
|
||||
pub rk: bool,
|
||||
|
||||
@@ -4,9 +4,9 @@ use serde::{Deserialize, Serialize};
|
||||
use serde_indexed::{DeserializeIndexed, SerializeIndexed};
|
||||
|
||||
use super::{AuthenticatorOptions, PinAuth};
|
||||
use crate::ctap2::credential_management::CredentialProtectionPolicy;
|
||||
use crate::sizes::*;
|
||||
use crate::webauthn::*;
|
||||
use crate::ctap2::credential_management::CredentialProtectionPolicy;
|
||||
|
||||
// // Approach 1:
|
||||
// pub type AuthenticatorExtensions = heapless::LinearMap<String<11>, bool, 2>;
|
||||
@@ -19,7 +19,7 @@ use crate::ctap2::credential_management::CredentialProtectionPolicy;
|
||||
// "userVerificationOptional" => CredentialProtectionPolicy::Optional,
|
||||
// "userVerificationOptionalWithCredentialIDList" => CredentialProtectionPolicy::OptionalWithCredentialIdList,
|
||||
// "userVerificationRequired" => CredentialProtectionPolicy::Required,
|
||||
// _ => { return Err(Self::Error::InvalidParameter); }
|
||||
// _ => return Err(Self::Error::InvalidParameter),
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
@@ -32,13 +32,13 @@ impl core::convert::TryFrom<u8> for CredentialProtectionPolicy {
|
||||
1 => CredentialProtectionPolicy::Optional,
|
||||
2 => CredentialProtectionPolicy::OptionalWithCredentialIdList,
|
||||
3 => CredentialProtectionPolicy::Required,
|
||||
_ => { return Err(Self::Error::InvalidParameter); }
|
||||
_ => return Err(Self::Error::InvalidParameter),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Approach 2:
|
||||
#[derive(Clone,Debug, Eq,PartialEq,Serialize,Deserialize)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Extensions {
|
||||
#[serde(rename = "credProtect")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
@@ -46,11 +46,9 @@ pub struct Extensions {
|
||||
pub cred_protect: Option<u8>,
|
||||
// #[serde(serialize_with = "u8::from")]
|
||||
// pub cred_protect: Option<u8>,
|
||||
|
||||
#[serde(rename = "hmac-secret")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub hmac_secret: Option<bool>,
|
||||
|
||||
}
|
||||
|
||||
// // Approach 3:
|
||||
@@ -68,7 +66,7 @@ pub struct Extensions {
|
||||
// pub extensions: Vec<AuthenticatorExtension, 3>,
|
||||
// }
|
||||
|
||||
#[derive(Clone,Debug, Eq,PartialEq,SerializeIndexed,DeserializeIndexed)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, SerializeIndexed, DeserializeIndexed)]
|
||||
// #[serde(rename_all = "camelCase")]
|
||||
#[serde_indexed(offset = 1)]
|
||||
pub struct Parameters {
|
||||
@@ -151,9 +149,9 @@ pub type AuthenticatorData = super::AuthenticatorData<AttestedCredentialData, Ex
|
||||
|
||||
// NOTE: This is not CBOR, it has a custom encoding...
|
||||
// https://www.w3.org/TR/webauthn/#sec-attested-credential-data
|
||||
#[derive(Clone,Debug,Eq,PartialEq)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct AttestedCredentialData {
|
||||
pub aaguid: Bytes<16>,
|
||||
pub aaguid: Bytes<16>,
|
||||
// this is where "unlimited non-resident keys" get stored
|
||||
// TODO: Model as actual credential ID, with ser/de to bytes (format is up to authenticator)
|
||||
pub credential_id: Bytes<MAX_CREDENTIAL_ID_LENGTH>,
|
||||
@@ -168,9 +166,13 @@ impl super::SerializeAttestedCredentialData for AttestedCredentialData {
|
||||
bytes.extend_from_slice(&self.aaguid).unwrap();
|
||||
|
||||
// byte length of credential ID as 16-bit unsigned big-endian integer.
|
||||
bytes.extend_from_slice(&(self.credential_id.len() as u16).to_be_bytes()).unwrap();
|
||||
bytes
|
||||
.extend_from_slice(&(self.credential_id.len() as u16).to_be_bytes())
|
||||
.unwrap();
|
||||
// raw bytes of credential ID
|
||||
bytes.extend_from_slice(&self.credential_id[..self.credential_id.len()]).unwrap();
|
||||
bytes
|
||||
.extend_from_slice(&self.credential_id[..self.credential_id.len()])
|
||||
.unwrap();
|
||||
|
||||
// use existing `bytes` buffer
|
||||
// let mut cbor_key = [0u8; 128];
|
||||
@@ -178,13 +180,15 @@ impl super::SerializeAttestedCredentialData for AttestedCredentialData {
|
||||
// CHANGE this back if credential_public_key is not serialized again
|
||||
// let l = crate::serde::cbor_serialize(&self.credential_public_key, &mut cbor_key).unwrap();
|
||||
// bytes.extend_from_slice(&cbor_key[..l]).unwrap();
|
||||
bytes.extend_from_slice(&self.credential_public_key).unwrap();
|
||||
bytes
|
||||
.extend_from_slice(&self.credential_public_key)
|
||||
.unwrap();
|
||||
|
||||
Bytes::from(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone,Debug, Eq,PartialEq,SerializeIndexed)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, SerializeIndexed)]
|
||||
#[serde_indexed(offset = 1)]
|
||||
pub struct Response {
|
||||
pub fmt: String<32>,
|
||||
@@ -193,14 +197,15 @@ pub struct Response {
|
||||
pub att_stmt: AttestationStatement,
|
||||
}
|
||||
|
||||
#[derive(Clone,Debug, Eq,PartialEq,Serialize)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
|
||||
#[serde(untagged)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum AttestationStatement {
|
||||
None(NoneAttestationStatement),
|
||||
Packed(PackedAttestationStatement),
|
||||
}
|
||||
|
||||
#[derive(Clone,Debug,Eq,PartialEq,Serialize)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum AttestationStatementFormat {
|
||||
None,
|
||||
@@ -211,10 +216,10 @@ pub enum AttestationStatementFormat {
|
||||
// FidoU2f,
|
||||
}
|
||||
|
||||
#[derive(Clone,Debug, Eq,PartialEq,Serialize)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
|
||||
pub struct NoneAttestationStatement {}
|
||||
|
||||
#[derive(Clone,Debug, Eq,PartialEq,Serialize)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
|
||||
pub struct PackedAttestationStatement {
|
||||
pub alg: i32,
|
||||
pub sig: Bytes<ASN1_SIGNATURE_LENGTH>,
|
||||
|
||||
+3
-4
@@ -19,9 +19,9 @@
|
||||
extern crate delog;
|
||||
generate_macros!();
|
||||
|
||||
pub use heapless::{String, Vec};
|
||||
pub use heapless::spsc::{Consumer, Producer, Queue};
|
||||
pub use heapless_bytes::Bytes as Bytes;
|
||||
pub use heapless::{String, Vec};
|
||||
pub use heapless_bytes::Bytes;
|
||||
pub type Bytes16 = Bytes<16>;
|
||||
pub type Bytes32 = Bytes<32>;
|
||||
|
||||
@@ -37,5 +37,4 @@ pub mod sizes;
|
||||
pub mod webauthn;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
}
|
||||
mod tests {}
|
||||
|
||||
+11
-11
@@ -1,7 +1,7 @@
|
||||
use core::convert::TryFrom;
|
||||
|
||||
/// the authenticator API, consisting of "operations"
|
||||
#[derive(Copy,Clone,Debug, Eq,PartialEq)]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum Operation {
|
||||
MakeCredential,
|
||||
GetAssertion,
|
||||
@@ -21,10 +21,10 @@ pub enum Operation {
|
||||
Vendor(VendorOperation),
|
||||
}
|
||||
|
||||
impl Into<u8> for Operation {
|
||||
fn into(self) -> u8 {
|
||||
impl From<Operation> for u8 {
|
||||
fn from(operation: Operation) -> u8 {
|
||||
use Operation::*;
|
||||
match self {
|
||||
match operation {
|
||||
MakeCredential => 0x01,
|
||||
GetAssertion => 0x02,
|
||||
GetNextAssertion => 0x08,
|
||||
@@ -50,7 +50,7 @@ impl Operation {
|
||||
}
|
||||
|
||||
/// Vendor CTAP2 operations, from 0x40 to 0x7f.
|
||||
#[derive(Copy,Clone,Debug, Eq,PartialEq)]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub struct VendorOperation(u8);
|
||||
|
||||
impl VendorOperation {
|
||||
@@ -70,9 +70,9 @@ impl TryFrom<u8> for VendorOperation {
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for VendorOperation {
|
||||
fn into(self) -> u8 {
|
||||
self.0
|
||||
impl From<VendorOperation> for u8 {
|
||||
fn from(operation: VendorOperation) -> u8 {
|
||||
operation.0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,10 +95,10 @@ impl TryFrom<u8> for Operation {
|
||||
0x0D => Config,
|
||||
0x40 => PreviewBioEnrollment,
|
||||
0x41 => PreviewCredentialManagement,
|
||||
code @ VendorOperation::FIRST..=VendorOperation::LAST
|
||||
=> Vendor(VendorOperation::try_from(code)?),
|
||||
code @ VendorOperation::FIRST..=VendorOperation::LAST => {
|
||||
Vendor(VendorOperation::try_from(code)?)
|
||||
}
|
||||
_ => return Err(()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1,3 +1,4 @@
|
||||
#![allow(clippy::declare_interior_mutable_const)]
|
||||
use crate::authenticator::{Error, Request, Response};
|
||||
|
||||
// PRIOR ART:
|
||||
@@ -7,4 +8,3 @@ use crate::authenticator::{Error, Request, Response};
|
||||
interchange::interchange! {
|
||||
CtapInterchange: (Request, Result<Response, Error>)
|
||||
}
|
||||
|
||||
|
||||
+5
-5
@@ -1,18 +1,18 @@
|
||||
pub const ATTESTED_CREDENTIAL_DATA_LENGTH: usize = 612;
|
||||
pub const ATTESTED_CREDENTIAL_DATA_LENGTH: usize = 612;
|
||||
// // not sure why i can't use `::to_usize()` here?
|
||||
// pub const ATTESTED_CREDENTIAL_DATA_LENGTH_BYTES: usize = 512;
|
||||
|
||||
pub const AUTHENTICATOR_DATA_LENGTH: usize = 676;
|
||||
pub const AUTHENTICATOR_DATA_LENGTH: usize = 676;
|
||||
// pub const AUTHENTICATOR_DATA_LENGTH_BYTES: usize = 512;
|
||||
|
||||
pub const ASN1_SIGNATURE_LENGTH: usize = 77;
|
||||
pub const ASN1_SIGNATURE_LENGTH: usize = 77;
|
||||
// pub const ASN1_SIGNATURE_LENGTH_BYTES: usize = 72;
|
||||
|
||||
pub const COSE_KEY_LENGTH: usize = 256;
|
||||
// pub const COSE_KEY_LENGTH_BYTES: usize = 256;
|
||||
|
||||
pub const MAX_CREDENTIAL_ID_LENGTH : usize = 512;
|
||||
pub const MAX_CREDENTIAL_ID_LENGTH_PLUS_256 : usize = 768;
|
||||
pub const MAX_CREDENTIAL_ID_LENGTH: usize = 512;
|
||||
pub const MAX_CREDENTIAL_ID_LENGTH_PLUS_256: usize = 768;
|
||||
pub const MAX_CREDENTIAL_COUNT_IN_LIST: usize = 10;
|
||||
|
||||
pub const PACKET_SIZE: usize = 64;
|
||||
|
||||
+24
-14
@@ -1,8 +1,8 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use crate::{Bytes, String};
|
||||
use crate::sizes::*;
|
||||
use crate::{Bytes, String};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone,Debug, Eq,PartialEq,Serialize,Deserialize)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct PublicKeyCredentialRpEntity {
|
||||
pub id: String<256>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
@@ -11,11 +11,14 @@ pub struct PublicKeyCredentialRpEntity {
|
||||
pub url: Option<String<64>>,
|
||||
}
|
||||
|
||||
#[derive(Clone,Debug, Eq,PartialEq,Serialize,Deserialize)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PublicKeyCredentialUserEntity {
|
||||
pub id: Bytes<64>,
|
||||
#[serde(default, deserialize_with = "deserialize_from_str_and_skip_if_too_long")]
|
||||
#[serde(
|
||||
default,
|
||||
deserialize_with = "deserialize_from_str_and_skip_if_too_long"
|
||||
)]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub icon: Option<String<128>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
@@ -24,15 +27,15 @@ pub struct PublicKeyCredentialUserEntity {
|
||||
pub display_name: Option<String<64>>,
|
||||
}
|
||||
|
||||
fn deserialize_from_str_and_skip_if_too_long<'de, D, const L: usize>(deserializer: D) -> Result<Option<String<L>>, D::Error>
|
||||
fn deserialize_from_str_and_skip_if_too_long<'de, D, const L: usize>(
|
||||
deserializer: D,
|
||||
) -> Result<Option<String<L>>, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
let result: Result<String<L>, D::Error> = serde::Deserialize::deserialize(deserializer);
|
||||
match result {
|
||||
Ok(string) => {
|
||||
Ok(Some(string))
|
||||
},
|
||||
Ok(string) => Ok(Some(string)),
|
||||
Err(_err) => {
|
||||
info_now!("skipping field: {:?}", _err);
|
||||
Ok(None)
|
||||
@@ -40,14 +43,18 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl PublicKeyCredentialUserEntity {
|
||||
pub fn from(id: Bytes<64>) -> Self {
|
||||
Self { id, icon: None, name: None, display_name: None }
|
||||
Self {
|
||||
id,
|
||||
icon: None,
|
||||
name: None,
|
||||
display_name: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone,Debug, Eq,PartialEq,Serialize,Deserialize)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct PublicKeyCredentialParameters {
|
||||
pub alg: i32,
|
||||
#[serde(rename = "type")]
|
||||
@@ -56,11 +63,14 @@ pub struct PublicKeyCredentialParameters {
|
||||
|
||||
impl PublicKeyCredentialParameters {
|
||||
pub fn public_key_with_alg(alg: i32) -> Self {
|
||||
return Self { alg, key_type: String::from("public-key") }
|
||||
Self {
|
||||
alg,
|
||||
key_type: String::from("public-key"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone,Debug, Eq,PartialEq,Serialize,Deserialize)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PublicKeyCredentialDescriptor {
|
||||
// NB: if this is too small, get a nasty error
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
use serde::{Serialize, Deserialize};
|
||||
use ctap_types::serde::{cbor_serialize, cbor_deserialize};
|
||||
use ctap_types::serde::{cbor_deserialize, cbor_serialize};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||
struct Example {
|
||||
|
||||
@@ -46,13 +46,11 @@ fn test_extensions_hmac_secret_input_key_agreement() {
|
||||
#[test]
|
||||
fn test_extensions_hmac_secret_salt_enc() {
|
||||
test::<ctap_types::Bytes<64>>(
|
||||
b"X \xb9.\xb6\xaa\xdcS6;r'q\x93j\xb5~3\x1eN\xa1\xcc%\x0f\x8dVV\n\x87o\t\xc0\xb1\xcb"
|
||||
b"X \xb9.\xb6\xaa\xdcS6;r'q\x93j\xb5~3\x1eN\xa1\xcc%\x0f\x8dVV\n\x87o\t\xc0\xb1\xcb",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extensions_hmac_secret_salt_auth() {
|
||||
test::<ctap_types::Bytes<16>>(
|
||||
b"PaU/RA\xb9\x1a\x935\x8d<\xfd8\xabXs"
|
||||
);
|
||||
test::<ctap_types::Bytes<16>>(b"PaU/RA\xb9\x1a\x935\x8d<\xfd8\xabXs");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user