webauthn_rs_proto/
attest.rs

1//! Types related to attestation (Registration)
2
3#[cfg(feature = "wasm")]
4use base64::Engine;
5use base64urlsafedata::Base64UrlSafeData;
6use serde::{Deserialize, Serialize};
7
8use crate::extensions::{RegistrationExtensionsClientOutputs, RequestRegistrationExtensions};
9use crate::options::*;
10#[cfg(feature = "wasm")]
11use crate::BASE64_ENGINE;
12
13/// <https://w3c.github.io/webauthn/#dictionary-makecredentialoptions>
14#[derive(Debug, Serialize, Clone, Deserialize)]
15#[serde(rename_all = "camelCase")]
16pub struct PublicKeyCredentialCreationOptions {
17    /// The relying party
18    pub rp: RelyingParty,
19    /// The user.
20    pub user: User,
21    /// The one-time challenge for the credential to sign.
22    pub challenge: Base64UrlSafeData,
23    /// The set of cryptographic types allowed by this server.
24    pub pub_key_cred_params: Vec<PubKeyCredParams>,
25
26    /// The timeout for the authenticator to stop accepting the operation
27    #[serde(skip_serializing_if = "Option::is_none")]
28    pub timeout: Option<u32>,
29
30    /// Credential ID's that are excluded from being able to be registered.
31    #[serde(skip_serializing_if = "Option::is_none")]
32    pub exclude_credentials: Option<Vec<PublicKeyCredentialDescriptor>>,
33
34    /// Criteria defining which authenticators may be used in this operation.
35    #[serde(skip_serializing_if = "Option::is_none")]
36    pub authenticator_selection: Option<AuthenticatorSelectionCriteria>,
37
38    /// Hints defining which credentials may be used in this operation.
39    #[serde(skip_serializing_if = "Option::is_none")]
40    pub hints: Option<Vec<PublicKeyCredentialHints>>,
41
42    /// The requested attestation level from the device.
43    #[serde(skip_serializing_if = "Option::is_none")]
44    pub attestation: Option<AttestationConveyancePreference>,
45
46    /// The list of attestation formats that the RP will accept.
47    #[serde(skip_serializing_if = "Option::is_none")]
48    pub attestation_formats: Option<Vec<AttestationFormat>>,
49
50    /// Non-standard extensions that may be used by the browser/authenticator.
51    #[serde(skip_serializing_if = "Option::is_none")]
52    pub extensions: Option<RequestRegistrationExtensions>,
53}
54
55/// A JSON serializable challenge which is issued to the user's web browser
56/// for handling. This is meant to be opaque, that is, you should not need
57/// to inspect or alter the content of the struct - you should serialise it
58/// and transmit it to the client only.
59#[derive(Debug, Serialize, Clone, Deserialize)]
60#[serde(rename_all = "camelCase")]
61pub struct CreationChallengeResponse {
62    /// The options.
63    pub public_key: PublicKeyCredentialCreationOptions,
64}
65
66#[cfg(feature = "wasm")]
67impl From<CreationChallengeResponse> for web_sys::CredentialCreationOptions {
68    fn from(ccr: CreationChallengeResponse) -> Self {
69        use js_sys::{Array, Object, Uint8Array};
70        use wasm_bindgen::JsValue;
71
72        let chal = Uint8Array::from(ccr.public_key.challenge.as_slice());
73        let userid = Uint8Array::from(ccr.public_key.user.id.as_slice());
74
75        let jsv = serde_wasm_bindgen::to_value(&ccr).unwrap();
76
77        let pkcco = js_sys::Reflect::get(&jsv, &"publicKey".into()).unwrap();
78        js_sys::Reflect::set(&pkcco, &"challenge".into(), &chal).unwrap();
79
80        let user = js_sys::Reflect::get(&pkcco, &"user".into()).unwrap();
81        js_sys::Reflect::set(&user, &"id".into(), &userid).unwrap();
82
83        if let Some(extensions) = ccr.public_key.extensions {
84            let obj: Object = (&extensions).into();
85            js_sys::Reflect::set(&pkcco, &"extensions".into(), &obj).unwrap();
86        }
87
88        if let Some(exclude_credentials) = ccr.public_key.exclude_credentials {
89            // There must be an array of these in the jsv ...
90            let exclude_creds: Array = exclude_credentials
91                .iter()
92                .map(|ac| {
93                    let obj = Object::new();
94                    js_sys::Reflect::set(
95                        &obj,
96                        &"type".into(),
97                        &JsValue::from_str(ac.type_.as_str()),
98                    )
99                    .unwrap();
100
101                    js_sys::Reflect::set(&obj, &"id".into(), &Uint8Array::from(ac.id.as_slice()))
102                        .unwrap();
103
104                    if let Some(transports) = &ac.transports {
105                        let tarray: Array = transports
106                            .iter()
107                            .map(|trs| serde_wasm_bindgen::to_value(trs).unwrap())
108                            .collect();
109
110                        js_sys::Reflect::set(&obj, &"transports".into(), &tarray).unwrap();
111                    }
112
113                    obj
114                })
115                .collect();
116
117            js_sys::Reflect::set(&pkcco, &"excludeCredentials".into(), &exclude_creds).unwrap();
118        }
119
120        web_sys::CredentialCreationOptions::from(jsv)
121    }
122}
123
124/// <https://w3c.github.io/webauthn/#authenticatorattestationresponse>
125#[derive(Debug, Serialize, Clone, Deserialize)]
126pub struct AuthenticatorAttestationResponseRaw {
127    /// <https://w3c.github.io/webauthn/#dom-authenticatorattestationresponse-attestationobject>
128    #[serde(rename = "attestationObject")]
129    pub attestation_object: Base64UrlSafeData,
130
131    /// <https://w3c.github.io/webauthn/#dom-authenticatorresponse-clientdatajson>
132    #[serde(rename = "clientDataJSON")]
133    pub client_data_json: Base64UrlSafeData,
134
135    /// <https://w3c.github.io/webauthn/#dom-authenticatorattestationresponse-gettransports>
136    #[serde(default)]
137    pub transports: Option<Vec<AuthenticatorTransport>>,
138}
139
140/// A client response to a registration challenge. This contains all required
141/// information to assess and assert trust in a credential's legitimacy, followed
142/// by registration to a user.
143///
144/// You should not need to handle the inner content of this structure - you should
145/// provide this to the correctly handling function of Webauthn only.
146/// <https://w3c.github.io/webauthn/#iface-pkcredential>
147#[derive(Debug, Clone, Deserialize, Serialize)]
148pub struct RegisterPublicKeyCredential {
149    /// The id of the PublicKey credential, likely in base64.
150    ///
151    /// This is NEVER actually
152    /// used in a real registration, because the true credential ID is taken from the
153    /// attestation data.
154    pub id: String,
155    /// The id of the credential, as binary.
156    ///
157    /// This is NEVER actually
158    /// used in a real registration, because the true credential ID is taken from the
159    /// attestation data.
160    #[serde(rename = "rawId")]
161    pub raw_id: Base64UrlSafeData,
162    /// <https://w3c.github.io/webauthn/#dom-publickeycredential-response>
163    pub response: AuthenticatorAttestationResponseRaw,
164    /// The type of credential.
165    #[serde(rename = "type")]
166    pub type_: String,
167    /// Unsigned Client processed extensions.
168    #[serde(default, alias = "clientExtensionResults", alias = "extensions")]
169    pub extensions: RegistrationExtensionsClientOutputs,
170}
171
172#[cfg(feature = "wasm")]
173impl From<web_sys::PublicKeyCredential> for RegisterPublicKeyCredential {
174    fn from(data: web_sys::PublicKeyCredential) -> RegisterPublicKeyCredential {
175        use js_sys::Uint8Array;
176
177        // AuthenticatorAttestationResponse has getTransports but web_sys isn't exposing it?
178        let transports = None;
179
180        // First, we have to b64 some data here.
181        // data.raw_id
182        let data_raw_id =
183            Uint8Array::new(&js_sys::Reflect::get(&data, &"rawId".into()).unwrap()).to_vec();
184
185        let data_response = js_sys::Reflect::get(&data, &"response".into()).unwrap();
186        let data_response_attestation_object = Uint8Array::new(
187            &js_sys::Reflect::get(&data_response, &"attestationObject".into()).unwrap(),
188        )
189        .to_vec();
190
191        let data_response_client_data_json = Uint8Array::new(
192            &js_sys::Reflect::get(&data_response, &"clientDataJSON".into()).unwrap(),
193        )
194        .to_vec();
195
196        let data_extensions = data.get_client_extension_results();
197
198        RegisterPublicKeyCredential {
199            id: BASE64_ENGINE.encode(&data_raw_id),
200            raw_id: data_raw_id.into(),
201            response: AuthenticatorAttestationResponseRaw {
202                attestation_object: data_response_attestation_object.into(),
203                client_data_json: data_response_client_data_json.into(),
204                transports,
205            },
206            type_: "public-key".to_string(),
207            extensions: data_extensions.into(),
208        }
209    }
210}