passkey_types/ctap2/get_assertion.rs
1//! <https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#authenticatorGetAssertion>
2use serde::{Deserialize, Serialize};
3
4use crate::{
5 Bytes,
6 ctap2::AuthenticatorData,
7 utils::serde::{ignore_unknown, ignore_unknown_opt_vec},
8 webauthn::{PublicKeyCredentialDescriptor, PublicKeyCredentialUserEntity},
9};
10
11pub use crate::ctap2::make_credential::Options;
12
13#[cfg(doc)]
14use {
15 crate::webauthn::{CollectedClientData, PublicKeyCredentialRequestOptions},
16 ciborium::value::Value,
17};
18
19use super::extensions::{AuthenticatorPrfGetOutputs, AuthenticatorPrfInputs, HmacGetSecretInput};
20
21serde_workaround! {
22 /// While similar in structure to [`PublicKeyCredentialRequestOptions`],
23 /// it is not completely identical, namely the presence of the `options` key.
24 #[derive(Debug)]
25 pub struct Request {
26 /// Relying Party Identifier
27 #[serde(rename = 0x01)]
28 pub rp_id: String,
29
30 /// Hash of the serialized client data collected by the host.
31 /// See [`CollectedClientData`]
32 #[serde(rename = 0x02)]
33 pub client_data_hash: Bytes,
34
35 /// A sequence of PublicKeyCredentialDescriptor structures, each denoting a credential. If
36 /// this parameter is present and has 1 or more entries, the authenticator MUST only
37 /// generate an assertion using one of the denoted credentials.
38 #[serde(
39 rename = 0x03;
40 default,
41 skip_serializing_if = Option::is_none,
42 deserialize_with = ignore_unknown_opt_vec
43 )]
44 pub allow_list: Option<Vec<PublicKeyCredentialDescriptor>>,
45
46 /// Parameters to influence authenticator operation. These parameters might be authenticator
47 /// specific.
48 #[serde(
49 rename = 0x04;
50 default,
51 skip_serializing_if = Option::is_none,
52 deserialize_with = ignore_unknown
53 )]
54 pub extensions: Option<ExtensionInputs>,
55
56 /// Parameters to influence authenticator operation, see [`Options`] for more details.
57 #[serde(rename = 0x05; default)]
58 pub options: Options,
59
60 /// First 16 bytes of HMAC-SHA-256 of clientDataHash using pinToken which platform got from
61 /// the authenticator: HMAC-SHA-256(pinToken, clientDataHash). (NOT YET SUPPORTED)
62 #[serde(rename = 0x06; default, skip_serializing_if = Option::is_none)]
63 pub pin_auth: Option<Bytes>,
64
65 /// PIN protocol version chosen by the client
66 #[serde(rename = 0x07; default, skip_serializing_if = Option::is_none)]
67 pub pin_protocol: Option<u8>,
68 }
69}
70
71/// All supported Authenticator extensions inputs during credential assertion
72#[derive(Debug, Serialize, Deserialize, Default)]
73pub struct ExtensionInputs {
74 /// The input salts for fetching and deriving a symmetric secret.
75 ///
76 /// <https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-errata-20220621.html#sctn-hmac-secret-extension>
77 #[serde(
78 rename = "hmac-secret",
79 default,
80 skip_serializing_if = "Option::is_none",
81 deserialize_with = "ignore_unknown"
82 )]
83 pub hmac_secret: Option<HmacGetSecretInput>,
84
85 /// The direct input from a on-system client for the prf extension.
86 ///
87 /// The output from a request using the `prf` extension will not be signed
88 /// and will be un-encrypted.
89 /// This input should already be hashed by the client.
90 #[serde(
91 default,
92 skip_serializing_if = "Option::is_none",
93 deserialize_with = "ignore_unknown"
94 )]
95 pub prf: Option<AuthenticatorPrfInputs>,
96}
97
98impl ExtensionInputs {
99 /// Validates that there is at least one extension field that is `Some`.
100 /// If all fields are `None` then this returns `None` as well.
101 pub fn zip_contents(self) -> Option<Self> {
102 let Self { hmac_secret, prf } = &self;
103
104 let has_hmac_secret = hmac_secret.is_some();
105 let has_prf = prf.is_some();
106
107 (has_hmac_secret || has_prf).then_some(self)
108 }
109}
110
111serde_workaround! {
112 /// Type returned from `Authenticator::get_assertion` on success.
113 #[derive(Debug)]
114 pub struct Response {
115 /// PublicKeyCredentialDescriptor structure containing the credential identifier whose
116 /// private key was used to generate the assertion. May be omitted if the allowList has
117 /// exactly one Credential.
118 #[serde(rename = 0x01; default, skip_serializing_if = Option::is_none)]
119 pub credential: Option<PublicKeyCredentialDescriptor>,
120
121 /// The signed-over contextual bindings made by the authenticator
122 #[serde(rename = 0x02)]
123 pub auth_data: AuthenticatorData,
124
125 /// The assertion signature produced by the authenticator
126 #[serde(rename = 0x03)]
127 pub signature: Bytes,
128
129 /// [`PublicKeyCredentialUserEntity`] structure containing the user account information.
130 /// User identifiable information (name, DisplayName, icon) MUST not be returned if user
131 /// verification is not done by the authenticator.
132 ///
133 /// ## U2F Devices:
134 /// For U2F devices, this parameter is not returned as this user information is not present
135 /// for U2F credentials.
136 ///
137 /// ## FIDO Devices - server resident credentials:
138 /// For server resident credentials on FIDO devices, this parameter is optional as server
139 /// resident credentials behave same as U2F credentials where they are discovered given the
140 /// user information on the RP. Authenticators optionally MAY store user information inside
141 /// the credential ID.
142 ///
143 /// ## FIDO devices - device resident credentials:
144 /// For device resident keys on FIDO devices, at least user "id" is mandatory.
145 ///
146 /// For single account per RP case, authenticator returns "id" field to the platform which
147 /// will be returned to the WebAuthn layer.
148 ///
149 /// For multiple accounts per RP case, where the authenticator does not have a display,
150 /// authenticator returns "id" as well as other fields to the platform. Platform will use
151 /// this information to show the account selection UX to the user and for the user selected
152 /// account, it will ONLY return "id" back to the WebAuthn layer and discard other user details.
153 #[serde(rename = 0x04; default, skip_serializing_if = Option::is_none)]
154 pub user: Option<PublicKeyCredentialUserEntity>,
155
156 /// Total number of account credentials for the RP. This member is required when more than
157 /// one account for the RP and the authenticator does not have a display. Omitted when
158 /// returned for the authenticatorGetNextAssertion method.
159 ///
160 /// It seems unlikely that more than 256 credentials would be needed for any given RP. Please
161 /// file an enhancement request if this limit impacts your application.
162 #[serde(rename = 0x05; default, skip_serializing_if = Option::is_none)]
163 pub number_of_credentials: Option<u8>,
164
165 /// Indicates that a credential was selected by the user via interaction directly with the authenticator,
166 /// and thus the platform does not need to confirm the credential.
167 /// Optional; defaults to false.
168 /// MUST NOT be present in response to a request where an [`Request::allow_list`] was given,
169 /// where [`Self::number_of_credentials`] is greater than one,
170 /// nor in response to an `authenticatorGetNextAssertion` request.
171 #[serde(rename = 0x06; default, skip_serializing_if = Option::is_none)]
172 pub user_selected: Option<bool>,
173
174 /// The contents of the associated `largeBlobKey` if present for the asserted credential,
175 /// and if [largeBlobKey[] was true in the extensions input.
176 ///
177 /// This extension is currently un-supported by this library.
178 #[serde(rename = 0x07; default, skip_serializing_if = Option::is_none)]
179 pub large_blob_key: Option<Bytes>,
180
181 /// A map, keyed by extension identifiers, to unsigned outputs of extensions, if any.
182 /// Authenticators SHOULD omit this field if no processed extensions define unsigned outputs.
183 /// Clients MUST treat an empty map the same as an omitted field.
184 #[serde(rename = 0x08; default, skip_serializing_if = Option::is_none)]
185 pub unsigned_extension_outputs: Option<UnsignedExtensionOutputs>,
186 }
187}
188
189/// All supported Authenticator extensions outputs during credential assertion
190///
191/// This is to be serialized to [`Value`] in [`AuthenticatorData::extensions`]
192#[derive(Debug, Serialize, Deserialize)]
193pub struct SignedExtensionOutputs {
194 /// Outputs the symmetric secrets after successful processing. The output MUST be encrypted.
195 ///
196 /// <https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-errata-20220621.html#sctn-hmac-secret-extension>
197 #[serde(
198 rename = "hmac-secret",
199 default,
200 skip_serializing_if = "Option::is_none"
201 )]
202 pub hmac_secret: Option<Bytes>,
203}
204
205impl SignedExtensionOutputs {
206 /// Validates that there is at least one extension field that is `Some`.
207 /// If all fields are `None` then this returns `None` as well.
208 pub fn zip_contents(self) -> Option<Self> {
209 let Self { hmac_secret } = &self;
210 hmac_secret.is_some().then_some(self)
211 }
212}
213
214/// A map, keyed by extension identifiers, to unsigned outputs of extensions, if any.
215/// Authenticators SHOULD omit this field if no processed extensions define unsigned outputs.
216/// Clients MUST treat an empty map the same as an omitted field.
217#[derive(Debug, Serialize, Deserialize, Default)]
218#[serde(rename_all = "camelCase")]
219pub struct UnsignedExtensionOutputs {
220 /// This output is supported in the Webauthn specification and will be used when the authenticator
221 /// and the client are in memory or communicating through an internal channel.
222 ///
223 /// If you are using transports where this needs to pass through a wire, use hmac-secret instead.
224 pub prf: Option<AuthenticatorPrfGetOutputs>,
225}
226
227impl UnsignedExtensionOutputs {
228 /// Validates that there is at least one extension field that is `Some`.
229 /// If all fields are `None` then this returns `None` as well.
230 pub fn zip_contents(self) -> Option<Self> {
231 let Self { prf } = &self;
232 prf.is_some().then_some(self)
233 }
234}