passkey_authenticator/
u2f.rs1use crate::{
4 Authenticator, CoseKeyPair, CredentialStore, UserValidationMethod, passkey::PasskeyAccessor,
5};
6use coset::iana;
7use p256::{
8 SecretKey,
9 ecdsa::{SigningKey, signature::Signer},
10};
11use passkey_types::{
12 Bytes, Passkey,
13 ctap2::{Flags, U2FError},
14 u2f::{
15 AuthenticationRequest, AuthenticationResponse, PublicKey, RegisterRequest, RegisterResponse,
16 },
17};
18mod sealed {
19 use crate::{Authenticator, CredentialStore, UserValidationMethod};
20
21 pub trait Sealed {}
22 impl<S: CredentialStore, U: UserValidationMethod> Sealed for Authenticator<S, U> {}
23}
24
25#[async_trait::async_trait]
27pub trait U2fApi: sealed::Sealed {
28 async fn register(
30 &mut self,
31 request: RegisterRequest,
32 handle: &[u8],
33 ) -> Result<RegisterResponse, U2FError>;
34
35 async fn authenticate(
37 &self,
38 request: AuthenticationRequest,
39 counter: u32,
40 user_presence: Flags,
41 ) -> Result<AuthenticationResponse, U2FError>;
42}
43
44#[async_trait::async_trait]
45impl<S: CredentialStore + Sync + Send, U: UserValidationMethod + Sync + Send> U2fApi
46 for Authenticator<S, U>
47{
48 async fn register(
50 &mut self,
51 request: RegisterRequest,
52 handle: &[u8],
53 ) -> Result<RegisterResponse, U2FError> {
54 let private_key = {
56 let mut rng = rand::thread_rng();
57 SecretKey::random(&mut rng)
58 };
59
60 let CoseKeyPair { public: _, private } =
62 CoseKeyPair::from_secret_key(&private_key, iana::Algorithm::ES256);
63 let signing_key = SigningKey::from(private_key);
64 let public_key = signing_key.verifying_key();
65 let pub_key_encoded = public_key.to_encoded_point(false);
66
67 let public_key = PublicKey {
71 x: pub_key_encoded.x().unwrap().as_slice().try_into().unwrap(),
72 y: pub_key_encoded.y().unwrap().as_slice().try_into().unwrap(),
73 };
74
75 let signature_target = [0x00] .into_iter()
78 .chain(request.application) .chain(request.challenge) .chain(handle.iter().copied()) .chain(public_key.encode()) .collect::<Vec<u8>>();
83 let signature_singleton: p256::ecdsa::Signature = signing_key.sign(&signature_target);
84 let signature = signature_singleton.to_vec();
85
86 let attestation_certificate = Vec::new();
87
88 let response = RegisterResponse {
89 public_key,
90 key_handle: handle.into(),
91 attestation_certificate,
92 signature,
93 };
94
95 let (passkey, user, rp) =
96 Passkey::wrap_u2f_registration_request(&request, &response, handle, &private);
97
98 let options = passkey_types::ctap2::get_assertion::Options {
100 rk: false,
101 uv: false,
102 up: false,
103 };
104 let result = self
105 .store_mut()
106 .save_credential(passkey, user, rp, options)
107 .await;
108
109 match result {
110 Ok(_) => Ok(response),
111 _ => Err(U2FError::Other),
112 }
113 }
114
115 async fn authenticate(
117 &self,
118 request: AuthenticationRequest,
119 counter: u32,
120 user_presence: Flags,
121 ) -> Result<AuthenticationResponse, U2FError> {
122 let pk_descriptor = passkey_types::webauthn::PublicKeyCredentialDescriptor {
126 ty: passkey_types::webauthn::PublicKeyCredentialType::PublicKey,
127 id: request.key_handle.into(),
128 transports: None,
129 };
130 let id_bytes: Bytes = request.application.to_vec().into();
131 let maybe_credential = self
132 .store()
133 .find_credentials(
134 Some(&[pk_descriptor]),
135 String::from(id_bytes).as_str(),
136 None,
137 )
138 .await
139 .map_err(|_| U2FError::Other);
140
141 let credential = maybe_credential?
142 .into_iter()
143 .next()
144 .ok_or(U2FError::Other)?;
145
146 let secret_key =
147 super::private_key_from_cose_key(&credential.key()).map_err(|_| U2FError::Other)?;
148 let signing_key = SigningKey::from(secret_key);
149
150 let signature_target = request
154 .application .into_iter()
156 .chain(std::iter::once(user_presence.into())) .chain(counter.to_be_bytes()) .chain(request.challenge) .collect::<Vec<u8>>();
160
161 let signature: p256::ecdsa::Signature = signing_key.sign(&signature_target);
162 let signature_bytes = signature.to_der().as_bytes().to_vec();
163
164 Ok(AuthenticationResponse {
165 user_presence,
166 counter,
167 signature: signature_bytes,
168 })
169 }
170}
171
172#[cfg(test)]
173mod tests;