1use std::{
2 marker::PhantomData,
3 sync::{
4 Arc,
5 atomic::{AtomicU32, Ordering},
6 },
7};
8
9use defuse_crypto::{Curve, Signer};
10use hex_literal::hex;
11use impl_tools::autoimpl;
12
13use crate::{Algorithm, ClientDataType, CollectedClientData, UserVerification, WebauthnAssertion};
14
15const ORIGIN: &str = "http://localhost";
17
18const RP_ID_HASH: [u8; 32] =
22 hex!("49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d9763");
23
24#[autoimpl(Debug, Clone where S: trait)]
26pub struct MockWebauthnSigner<A: Algorithm, UV: UserVerification, S: Signer<A::Curve>> {
27 sign_count: Arc<AtomicU32>,
28 signer: S,
29 _algorithm: PhantomData<fn() -> A>,
30 _user_verification: PhantomData<fn() -> UV>,
31}
32
33impl<A, UV, S> MockWebauthnSigner<A, UV, S>
34where
35 A: Algorithm,
36 UV: UserVerification,
37 S: Signer<A::Curve>,
38{
39 #[inline]
40 pub fn new(signer: S) -> Self {
41 Self {
42 sign_count: Arc::new(AtomicU32::new(0)),
43 signer,
44 _algorithm: PhantomData,
45 _user_verification: PhantomData,
46 }
47 }
48
49 #[inline]
50 pub const fn signer(&self) -> &S {
51 &self.signer
52 }
53
54 pub async fn sign(
55 &self,
56 challenge: impl Into<Vec<u8>>,
57 ) -> Result<(WebauthnAssertion, <A::Curve as Curve>::Signature), S::Error> {
58 let assertion = WebauthnAssertion {
59 authenticator_data: [
61 RP_ID_HASH.as_slice(),
63 &[{
65 let mut flags = 0b00000001u8; if UV::REQUIRED {
67 flags |= 0b00000100u8; }
69 flags
70 }],
71 &self.sign_count.fetch_add(1, Ordering::SeqCst).to_be_bytes(),
73 ]
74 .concat(),
75
76 client_data_json: serde_json::to_string(&CollectedClientData {
78 typ: ClientDataType::Get,
79 challenge: challenge.into(),
80 origin: ORIGIN.to_string(),
81 })
82 .expect("JSON: failed to serialize"),
83 };
84
85 let data = A::maybe_prehash(assertion.effective_msg())
86 .as_ref()
87 .to_vec();
88
89 let signature = self.signer.sign(&data).await?;
90
91 Ok((assertion, signature))
92 }
93}