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