webauthn_authenticator_rs/
authenticator_hashed.rs1#[cfg(feature = "ctap2")]
3use std::collections::BTreeMap;
4
5#[cfg(any(feature = "ctap2", feature = "crypto"))]
6use base64urlsafedata::Base64UrlSafeData;
7
8#[cfg(feature = "ctap2")]
9use serde_cbor_2::{ser::to_vec_packed, Value};
10#[cfg(any(all(doc, not(doctest)), feature = "crypto"))]
11use url::Url;
12#[cfg(feature = "ctap2")]
13use webauthn_rs_proto::PublicKeyCredentialDescriptor;
14use webauthn_rs_proto::{
15 PublicKeyCredential, PublicKeyCredentialCreationOptions, PublicKeyCredentialRequestOptions,
16 RegisterPublicKeyCredential,
17};
18
19#[cfg(any(all(doc, not(doctest)), feature = "ctap2"))]
20use crate::ctap2::commands::{
21 GetAssertionRequest, GetAssertionResponse, MakeCredentialRequest, MakeCredentialResponse,
22};
23use crate::error::WebauthnCError;
24#[cfg(any(all(doc, not(doctest)), feature = "crypto"))]
25use crate::{
26 crypto::compute_sha256,
27 util::{creation_to_clientdata, get_to_clientdata},
28 AuthenticatorBackend,
29};
30
31pub trait AuthenticatorBackendHashedClientData {
75 fn perform_register(
76 &mut self,
77 client_data_hash: Vec<u8>,
78 options: PublicKeyCredentialCreationOptions,
79 timeout_ms: u32,
80 ) -> Result<RegisterPublicKeyCredential, WebauthnCError>;
81
82 fn perform_auth(
83 &mut self,
84 client_data_hash: Vec<u8>,
85 options: PublicKeyCredentialRequestOptions,
86 timeout_ms: u32,
87 ) -> Result<PublicKeyCredential, WebauthnCError>;
88}
89
90#[cfg(any(all(doc, not(doctest)), feature = "crypto"))]
91impl<T: AuthenticatorBackendHashedClientData> AuthenticatorBackend for T {
97 fn perform_register(
98 &mut self,
99 origin: Url,
100 options: PublicKeyCredentialCreationOptions,
101 timeout_ms: u32,
102 ) -> Result<RegisterPublicKeyCredential, WebauthnCError> {
103 let client_data = creation_to_clientdata(origin, options.challenge.clone());
104 let client_data: Vec<u8> = serde_json::to_string(&client_data)
105 .map_err(|_| WebauthnCError::Json)?
106 .into();
107 let client_data_hash = compute_sha256(&client_data).to_vec();
108 let mut cred = self.perform_register(client_data_hash, options, timeout_ms)?;
109 cred.response.client_data_json = Base64UrlSafeData::from(client_data);
110
111 Ok(cred)
112 }
113
114 fn perform_auth(
115 &mut self,
116 origin: Url,
117 options: PublicKeyCredentialRequestOptions,
118 timeout_ms: u32,
119 ) -> Result<PublicKeyCredential, WebauthnCError> {
120 let client_data = get_to_clientdata(origin, options.challenge.clone());
121 let client_data: Vec<u8> = serde_json::to_string(&client_data)
122 .map_err(|_| WebauthnCError::Json)?
123 .into();
124 let client_data_hash = compute_sha256(&client_data).to_vec();
125 let mut cred = self.perform_auth(client_data_hash, options, timeout_ms)?;
126 cred.response.client_data_json = Base64UrlSafeData::from(client_data);
127 Ok(cred)
128 }
129}
130
131#[cfg(any(all(doc, not(doctest)), feature = "ctap2"))]
132pub fn perform_register_with_request(
142 backend: &mut impl AuthenticatorBackendHashedClientData,
143 request: MakeCredentialRequest,
144 timeout_ms: u32,
145) -> Result<Vec<u8>, WebauthnCError> {
146 let options = PublicKeyCredentialCreationOptions {
147 rp: request.rp,
148 user: request.user,
149 challenge: Base64UrlSafeData::new(),
150 pub_key_cred_params: request.pub_key_cred_params,
151 timeout: Some(timeout_ms),
152 exclude_credentials: Some(request.exclude_list),
153 hints: None,
155 attestation: None,
156 attestation_formats: None,
157 authenticator_selection: None,
158 extensions: None,
159 };
160 let client_data_hash = request.client_data_hash;
161
162 let cred: RegisterPublicKeyCredential =
163 backend.perform_register(client_data_hash, options, timeout_ms)?;
164
165 let resp: MakeCredentialResponse =
168 serde_cbor_2::de::from_slice(cred.response.attestation_object.as_slice())
169 .map_err(|_| WebauthnCError::Cbor)?;
170
171 let resp: BTreeMap<u32, Value> = resp.into();
173 to_vec_packed(&resp).map_err(|_| WebauthnCError::Cbor)
174}
175
176#[cfg(any(all(doc, not(doctest)), feature = "ctap2"))]
177pub fn perform_auth_with_request(
187 backend: &mut impl AuthenticatorBackendHashedClientData,
188 request: GetAssertionRequest,
189 timeout_ms: u32,
190) -> Result<Vec<u8>, WebauthnCError> {
191 let options = PublicKeyCredentialRequestOptions {
192 challenge: Base64UrlSafeData::new(),
193 timeout: Some(timeout_ms),
194 rp_id: request.rp_id,
195 allow_credentials: request.allow_list,
196 hints: None,
198 user_verification: webauthn_rs_proto::UserVerificationPolicy::Preferred,
199 extensions: None,
200 };
201
202 let cred = backend.perform_auth(request.client_data_hash, options, timeout_ms)?;
203 let resp = GetAssertionResponse {
204 credential: Some(PublicKeyCredentialDescriptor {
205 type_: cred.type_,
206 id: cred.raw_id,
207 transports: None,
208 }),
209 auth_data: Some(cred.response.authenticator_data.into()),
210 signature: Some(cred.response.signature.into()),
211 number_of_credentials: None,
212 user_selected: None,
213 large_blob_key: None,
214 };
215
216 let resp: BTreeMap<u32, Value> = resp.into();
218 to_vec_packed(&resp).map_err(|_| WebauthnCError::Cbor)
219}