1use bitflags::bitflags;
6use cbor_smol::cbor_deserialize;
7use heapless::{Vec, VecView};
8use serde::{Deserialize, Serialize};
9
10use crate::{sizes::*, Bytes, TryFromStrError};
11
12pub use crate::operation::{Operation, VendorOperation};
13
14pub mod client_pin;
15pub mod credential_management;
16pub mod get_assertion;
17pub mod get_info;
18pub mod large_blobs;
19pub mod make_credential;
20
21pub type Result<T> = core::result::Result<T, Error>;
22
23#[derive(Clone, Debug, PartialEq)]
24#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
25#[non_exhaustive]
26#[allow(clippy::large_enum_variant)]
27pub enum Request<'a> {
30 MakeCredential(make_credential::Request<'a>),
32 GetAssertion(get_assertion::Request<'a>),
34 GetNextAssertion,
36 GetInfo,
38 ClientPin(client_pin::Request<'a>),
40 Reset,
42 CredentialManagement(credential_management::Request<'a>),
44 Selection,
46 LargeBlobs(large_blobs::Request<'a>),
48 Vendor(crate::operation::VendorOperation),
51}
52
53pub enum CtapMappingError {
54 InvalidCommand(u8),
55 ParsingError(cbor_smol::Error),
56}
57
58impl From<CtapMappingError> for Error {
59 fn from(mapping_error: CtapMappingError) -> Error {
60 match mapping_error {
61 CtapMappingError::InvalidCommand(_cmd) => Error::InvalidCommand,
62 CtapMappingError::ParsingError(cbor_error) => match cbor_error {
63 cbor_smol::Error::SerdeMissingField => Error::MissingParameter,
64 _ => Error::InvalidCbor,
65 },
66 }
67 }
68}
69
70impl<'a> Request<'a> {
71 #[inline(never)]
73 pub fn deserialize(data: &'a [u8]) -> Result<Self> {
74 if data.is_empty() {
75 return Err(
76 CtapMappingError::ParsingError(cbor_smol::Error::DeserializeUnexpectedEnd).into(),
77 );
78 }
79
80 let (&op, data) = data.split_first().ok_or(CtapMappingError::ParsingError(
81 cbor_smol::Error::DeserializeUnexpectedEnd,
82 ))?;
83
84 let operation = Operation::try_from(op).map_err(|_| {
85 debug_now!("invalid operation {}", op);
86 CtapMappingError::InvalidCommand(op)
87 })?;
88
89 info!("deser {:?}", operation);
90 Ok(match operation {
91 Operation::MakeCredential => Request::MakeCredential(
92 cbor_deserialize(data).map_err(CtapMappingError::ParsingError)?,
93 ),
94
95 Operation::GetAssertion => Request::GetAssertion(
96 cbor_deserialize(data).map_err(CtapMappingError::ParsingError)?,
97 ),
98
99 Operation::GetNextAssertion => Request::GetNextAssertion,
100
101 Operation::CredentialManagement | Operation::PreviewCredentialManagement => {
102 Request::CredentialManagement(
103 cbor_deserialize(data).map_err(CtapMappingError::ParsingError)?,
104 )
105 }
106
107 Operation::Reset => Request::Reset,
108
109 Operation::Selection => Request::Selection,
110
111 Operation::GetInfo => Request::GetInfo,
112
113 Operation::ClientPin => {
114 Request::ClientPin(cbor_deserialize(data).map_err(CtapMappingError::ParsingError)?)
115 }
116
117 Operation::LargeBlobs => {
118 Request::LargeBlobs(cbor_deserialize(data).map_err(CtapMappingError::ParsingError)?)
119 }
120
121 Operation::Vendor(vendor_operation) => Request::Vendor(vendor_operation),
123
124 Operation::BioEnrollment | Operation::PreviewBioEnrollment | Operation::Config => {
125 debug_now!("unhandled CBOR operation {:?}", operation);
126 return Err(CtapMappingError::InvalidCommand(op).into());
127 }
128 })
129 }
130}
131
132#[derive(Clone, Debug, PartialEq)]
133#[non_exhaustive]
134#[allow(clippy::large_enum_variant)]
136pub enum Response {
137 MakeCredential(make_credential::Response),
138 GetAssertion(get_assertion::Response),
139 GetNextAssertion(get_assertion::Response),
140 GetInfo(get_info::Response),
141 ClientPin(client_pin::Response),
142 Reset,
143 Selection,
144 CredentialManagement(credential_management::Response),
145 LargeBlobs(large_blobs::Response),
146 Vendor,
148}
149
150impl Response {
151 #[inline(never)]
152 pub fn serialize(&self, buffer: &mut VecView<u8>) {
153 buffer.resize_default(buffer.capacity()).ok();
154 let (status, data) = buffer.split_first_mut().unwrap();
155 use cbor_smol::cbor_serialize;
156 use Response::*;
157 let outcome = match self {
158 GetInfo(response) => cbor_serialize(response, data),
159 MakeCredential(response) => cbor_serialize(response, data),
160 ClientPin(response) => cbor_serialize(response, data),
161 GetAssertion(response) | GetNextAssertion(response) => cbor_serialize(response, data),
162 CredentialManagement(response) => cbor_serialize(response, data),
163 LargeBlobs(response) => cbor_serialize(response, data),
164 Reset | Selection | Vendor => Ok([].as_slice()),
165 };
166 if let Ok(slice) = outcome {
167 *status = 0;
168 if slice == [0xA0] {
170 buffer.resize_default(1).ok();
171 } else {
172 let l = slice.len();
173 buffer.resize_default(l + 1).ok();
174 }
175 } else {
176 *status = Error::Other as u8;
177 buffer.resize_default(1).ok();
178 }
179 }
180}
181
182#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
183#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
184#[non_exhaustive]
185pub struct AuthenticatorOptions {
186 #[serde(skip_serializing_if = "Option::is_none")]
187 pub rk: Option<bool>,
188 #[serde(skip_serializing_if = "Option::is_none")]
189 pub up: Option<bool>,
190 #[serde(skip_serializing_if = "Option::is_none")]
191 pub uv: Option<bool>,
195}
196
197bitflags! {
198 pub struct AuthenticatorDataFlags: u8 {
199 const USER_PRESENCE = 1 << 0;
200 const USER_VERIFIED = 1 << 2;
201 const ATTESTED_CREDENTIAL_DATA = 1 << 6;
202 const EXTENSION_DATA = 1 << 7;
203 }
204}
205
206pub trait SerializeAttestedCredentialData {
207 fn serialize(&self, buffer: &mut SerializedAuthenticatorData) -> Result<()>;
208}
209
210#[derive(Clone, Debug, Eq, PartialEq)]
211pub struct AuthenticatorData<'a, A, E> {
212 pub rp_id_hash: &'a [u8; 32],
213 pub flags: AuthenticatorDataFlags,
214 pub sign_count: u32,
215 pub attested_credential_data: Option<A>,
216 pub extensions: Option<E>,
217}
218
219pub type SerializedAuthenticatorData = Bytes<AUTHENTICATOR_DATA_LENGTH>;
220
221impl<A: SerializeAttestedCredentialData, E: serde::Serialize> AuthenticatorData<'_, A, E> {
224 #[inline(never)]
225 pub fn serialize(&self) -> Result<SerializedAuthenticatorData> {
226 let mut bytes = SerializedAuthenticatorData::new();
227
228 bytes
230 .extend_from_slice(self.rp_id_hash)
231 .map_err(|_| Error::Other)?;
232 bytes.push(self.flags.bits()).map_err(|_| Error::Other)?;
234 bytes
236 .extend_from_slice(&self.sign_count.to_be_bytes())
237 .map_err(|_| Error::Other)?;
238
239 if let Some(attested_credential_data) = &self.attested_credential_data {
241 attested_credential_data.serialize(&mut bytes)?;
242 }
243
244 if let Some(extensions) = self.extensions.as_ref() {
246 cbor_smol::cbor_serialize_to(extensions, &mut bytes).map_err(|_| Error::Other)?;
247 }
248
249 Ok(bytes)
250 }
251}
252
253#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
254#[non_exhaustive]
255#[serde(untagged)]
256#[allow(clippy::large_enum_variant)]
257pub enum AttestationStatement {
258 None(NoneAttestationStatement),
259 Packed(PackedAttestationStatement),
260}
261
262#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
263#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
264#[non_exhaustive]
265#[serde(into = "&str", try_from = "&str")]
266pub enum AttestationStatementFormat {
267 None,
268 Packed,
269}
270
271impl AttestationStatementFormat {
272 const NONE: &'static str = "none";
273 const PACKED: &'static str = "packed";
274}
275
276impl From<AttestationStatementFormat> for &str {
277 fn from(format: AttestationStatementFormat) -> Self {
278 match format {
279 AttestationStatementFormat::None => AttestationStatementFormat::NONE,
280 AttestationStatementFormat::Packed => AttestationStatementFormat::PACKED,
281 }
282 }
283}
284
285impl TryFrom<&str> for AttestationStatementFormat {
286 type Error = TryFromStrError;
287
288 fn try_from(s: &str) -> core::result::Result<Self, Self::Error> {
289 match s {
290 Self::NONE => Ok(Self::None),
291 Self::PACKED => Ok(Self::Packed),
292 _ => Err(TryFromStrError),
293 }
294 }
295}
296
297#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
298pub struct NoneAttestationStatement {}
299
300#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
301pub struct PackedAttestationStatement {
302 pub alg: i32,
303 pub sig: Bytes<ASN1_SIGNATURE_LENGTH>,
304 #[serde(skip_serializing_if = "Option::is_none")]
305 pub x5c: Option<Vec<Bytes<1024>, 1>>,
306}
307
308#[derive(Clone, Debug, Default, Eq, PartialEq)]
309pub struct AttestationFormatsPreference {
310 pub(crate) known_formats: Vec<AttestationStatementFormat, 2>,
311 pub(crate) unknown: bool,
312}
313
314impl AttestationFormatsPreference {
315 pub fn known_formats(&self) -> &[AttestationStatementFormat] {
316 &self.known_formats
317 }
318
319 pub fn includes_unknown_formats(&self) -> bool {
320 self.unknown
321 }
322}
323
324impl<'de> Deserialize<'de> for AttestationFormatsPreference {
325 fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
326 where
327 D: serde::Deserializer<'de>,
328 {
329 struct ValueVisitor;
330
331 impl<'de> serde::de::Visitor<'de> for ValueVisitor {
332 type Value = AttestationFormatsPreference;
333
334 fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
335 formatter.write_str("a sequence")
336 }
337
338 fn visit_seq<A>(self, mut seq: A) -> core::result::Result<Self::Value, A::Error>
339 where
340 A: serde::de::SeqAccess<'de>,
341 {
342 let mut preference = AttestationFormatsPreference::default();
343 while let Some(value) = seq.next_element::<&str>()? {
344 if let Ok(format) = AttestationStatementFormat::try_from(value) {
345 preference.known_formats.push(format).ok();
346 } else {
347 preference.unknown = true;
348 }
349 }
350 Ok(preference)
351 }
352 }
353
354 deserializer.deserialize_seq(ValueVisitor)
355 }
356}
357
358#[derive(Clone, Copy, Debug, Eq, PartialEq)]
359#[non_exhaustive]
360pub enum Error {
361 Success = 0x00,
362 InvalidCommand = 0x01,
363 InvalidParameter = 0x02,
364 InvalidLength = 0x03,
365 InvalidSeq = 0x04,
366 Timeout = 0x05,
367 ChannelBusy = 0x06,
368 LockRequired = 0x0A,
369 InvalidChannel = 0x0B,
370 CborUnexpectedType = 0x11,
371 InvalidCbor = 0x12,
372 MissingParameter = 0x14,
373 LimitExceeded = 0x15,
374 UnsupportedExtension = 0x16,
375 FingerprintDatabaseFull = 0x17,
376 LargeBlobStorageFull = 0x18,
377 CredentialExcluded = 0x19,
378 Processing = 0x21,
379 InvalidCredential = 0x22,
380 UserActionPending = 0x23,
381 OperationPending = 0x24,
382 NoOperations = 0x25,
383 UnsupportedAlgorithm = 0x26,
384 OperationDenied = 0x27,
385 KeyStoreFull = 0x28,
386 NotBusy = 0x29,
387 NoOperationPending = 0x2A,
388 UnsupportedOption = 0x2B,
389 InvalidOption = 0x2C,
390 KeepaliveCancel = 0x2D,
391 NoCredentials = 0x2E,
392 UserActionTimeout = 0x2F,
393 NotAllowed = 0x30,
394 PinInvalid = 0x31,
395 PinBlocked = 0x32,
396 PinAuthInvalid = 0x33,
397 PinAuthBlocked = 0x34,
398 PinNotSet = 0x35,
399 PinRequired = 0x36,
400 PinPolicyViolation = 0x37,
401 PinTokenExpired = 0x38,
402 RequestTooLarge = 0x39,
403 ActionTimeout = 0x3A,
404 UpRequired = 0x3B,
405 UvBlocked = 0x3C,
406 IntegrityFailure = 0x3D,
407 InvalidSubcommand = 0x3E,
408 UvInvalid = 0x3F,
409 UnauthorizedPermission = 0x40,
410 Other = 0x7F,
411 SpecLast = 0xDF,
412 ExtensionFirst = 0xE0,
413 ExtensionLast = 0xEF,
414 VendorFirst = 0xF0,
415 VendorLast = 0xFF,
416}
417
418pub trait Authenticator {
423 fn get_info(&mut self) -> get_info::Response;
424 fn make_credential(
425 &mut self,
426 request: &make_credential::Request,
427 ) -> Result<make_credential::Response>;
428 fn get_assertion(
429 &mut self,
430 request: &get_assertion::Request,
431 ) -> Result<get_assertion::Response>;
432 fn get_next_assertion(&mut self) -> Result<get_assertion::Response>;
433 fn reset(&mut self) -> Result<()>;
434 fn client_pin(&mut self, request: &client_pin::Request) -> Result<client_pin::Response>;
435 fn credential_management(
436 &mut self,
437 request: &credential_management::Request,
438 ) -> Result<credential_management::Response>;
439 fn selection(&mut self) -> Result<()>;
440 fn vendor(&mut self, op: VendorOperation) -> Result<()>;
441
442 fn large_blobs(&mut self, request: &large_blobs::Request) -> Result<large_blobs::Response> {
444 let _ = request;
445 Err(Error::InvalidCommand)
446 }
447
448 #[inline(never)]
450 fn call_ctap2(&mut self, request: &Request) -> Result<Response> {
451 match request {
452 Request::GetInfo => {
454 debug_now!("CTAP2.GI");
455 Ok(Response::GetInfo(self.get_info()))
456 }
457
458 Request::MakeCredential(request) => {
460 debug_now!("CTAP2.MC");
461 Ok(Response::MakeCredential(
462 self.make_credential(request).inspect_err(|_e| {
463 debug!("error: {:?}", _e);
464 })?,
465 ))
466 }
467
468 Request::GetAssertion(request) => {
470 debug_now!("CTAP2.GA");
471 Ok(Response::GetAssertion(
472 self.get_assertion(request).inspect_err(|_e| {
473 debug!("error: {:?}", _e);
474 })?,
475 ))
476 }
477
478 Request::GetNextAssertion => {
480 debug_now!("CTAP2.GNA");
481 Ok(Response::GetNextAssertion(
482 self.get_next_assertion().inspect_err(|_e| {
483 debug!("error: {:?}", _e);
484 })?,
485 ))
486 }
487
488 Request::Reset => {
490 debug_now!("CTAP2.RST");
491 self.reset().inspect_err(|_e| {
492 debug!("error: {:?}", _e);
493 })?;
494 Ok(Response::Reset)
495 }
496
497 Request::ClientPin(request) => {
499 debug_now!("CTAP2.PIN");
500 Ok(Response::ClientPin(self.client_pin(request).inspect_err(
501 |_e| {
502 debug!("error: {:?}", _e);
503 },
504 )?))
505 }
506
507 Request::CredentialManagement(request) => {
509 debug_now!("CTAP2.CM");
510 Ok(Response::CredentialManagement(
511 self.credential_management(request).inspect_err(|_e| {
512 debug!("error: {:?}", _e);
513 })?,
514 ))
515 }
516
517 Request::Selection => {
519 debug_now!("CTAP2.SEL");
520 self.selection().inspect_err(|_e| {
521 debug!("error: {:?}", _e);
522 })?;
523 Ok(Response::Selection)
524 }
525
526 Request::LargeBlobs(request) => {
528 debug_now!("CTAP2.LB");
529 Ok(Response::LargeBlobs(
530 self.large_blobs(request).inspect_err(|_e| {
531 debug!("error: {:?}", _e);
532 })?,
533 ))
534 }
535
536 Request::Vendor(op) => {
538 debug_now!("CTAP2.V");
539 self.vendor(*op).inspect_err(|_e| {
540 debug!("error: {:?}", _e);
541 })?;
542 Ok(Response::Vendor)
543 }
544 }
545 }
546}
547
548impl<A: Authenticator> crate::Rpc<Error, Request<'_>, Response> for A {
549 #[inline(never)]
551 fn call(&mut self, request: &Request) -> Result<Response> {
552 self.call_ctap2(request)
553 }
554}