ctap_types/ctap2/
get_assertion.rs

1use crate::{Bytes, Vec};
2use cosey::EcdhEsHkdf256PublicKey;
3use serde::{Deserialize, Serialize};
4use serde_bytes::ByteArray;
5use serde_indexed::{DeserializeIndexed, SerializeIndexed};
6
7use super::{AttestationFormatsPreference, AttestationStatement, AuthenticatorOptions, Result};
8use crate::sizes::*;
9use crate::webauthn::*;
10
11#[derive(Clone, Debug, Eq, PartialEq, SerializeIndexed, DeserializeIndexed)]
12#[non_exhaustive]
13#[serde_indexed(offset = 1)]
14pub struct HmacSecretInput {
15    pub key_agreement: EcdhEsHkdf256PublicKey,
16    // *either* enc(salt1) *or* enc(salt1 || salt2)
17    pub salt_enc: Bytes<80>,
18    pub salt_auth: Bytes<32>,
19    #[serde(skip_serializing_if = "Option::is_none")]
20    pub pin_protocol: Option<u32>,
21}
22
23#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
24#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
25#[non_exhaustive]
26pub struct ExtensionsInput {
27    #[serde(rename = "hmac-secret")]
28    #[serde(skip_serializing_if = "Option::is_none")]
29    pub hmac_secret: Option<HmacSecretInput>,
30
31    /// Whether a large blob key is requested.
32    #[serde(rename = "largeBlobKey")]
33    #[serde(skip_serializing_if = "Option::is_none")]
34    pub large_blob_key: Option<bool>,
35
36    #[cfg(feature = "third-party-payment")]
37    #[serde(rename = "thirdPartyPayment")]
38    #[serde(skip_serializing_if = "Option::is_none")]
39    pub third_party_payment: Option<bool>,
40}
41
42#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
43#[non_exhaustive]
44pub struct ExtensionsOutput {
45    #[serde(rename = "hmac-secret")]
46    #[serde(skip_serializing_if = "Option::is_none")]
47    // *either* enc(output1) *or* enc(output1 || output2)
48    pub hmac_secret: Option<Bytes<80>>,
49
50    #[cfg(feature = "third-party-payment")]
51    #[serde(rename = "thirdPartyPayment")]
52    #[serde(skip_serializing_if = "Option::is_none")]
53    pub third_party_payment: Option<bool>,
54}
55
56impl ExtensionsOutput {
57    #[inline]
58    pub fn is_set(&self) -> bool {
59        let Self {
60            hmac_secret,
61            #[cfg(feature = "third-party-payment")]
62            third_party_payment,
63        } = self;
64        if hmac_secret.is_some() {
65            return true;
66        }
67        #[cfg(feature = "third-party-payment")]
68        if third_party_payment.is_some() {
69            return true;
70        }
71        false
72    }
73}
74
75pub struct NoAttestedCredentialData;
76
77impl super::SerializeAttestedCredentialData for NoAttestedCredentialData {
78    fn serialize(&self, _buffer: &mut super::SerializedAuthenticatorData) -> Result<()> {
79        Ok(())
80    }
81}
82
83pub type AuthenticatorData<'a> =
84    super::AuthenticatorData<'a, NoAttestedCredentialData, ExtensionsOutput>;
85
86pub type AllowList<'a> = Vec<PublicKeyCredentialDescriptorRef<'a>, MAX_CREDENTIAL_COUNT_IN_LIST>;
87
88#[derive(Clone, Debug, Eq, PartialEq, DeserializeIndexed)]
89#[non_exhaustive]
90#[serde_indexed(offset = 1)]
91pub struct Request<'a> {
92    pub rp_id: &'a str,
93    pub client_data_hash: &'a serde_bytes::Bytes,
94    #[serde(skip_serializing_if = "Option::is_none")]
95    pub allow_list: Option<AllowList<'a>>,
96    #[serde(skip_serializing_if = "Option::is_none")]
97    pub extensions: Option<ExtensionsInput>,
98    #[serde(skip_serializing_if = "Option::is_none")]
99    pub options: Option<AuthenticatorOptions>,
100    #[serde(skip_serializing_if = "Option::is_none")]
101    pub pin_auth: Option<&'a serde_bytes::Bytes>,
102    #[serde(skip_serializing_if = "Option::is_none")]
103    pub pin_protocol: Option<u32>,
104    #[serde(skip_serializing_if = "Option::is_none")]
105    pub enterprise_attestation: Option<u32>,
106    #[serde(skip_serializing_if = "Option::is_none")]
107    pub attestation_formats_preference: Option<AttestationFormatsPreference>,
108}
109
110// NB: attn object definition / order at end of
111// https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#authenticatorMakeCredential
112// does not coincide with what python-fido2 expects in AttestationObject.__init__ *at all* :'-)
113#[derive(Clone, Debug, Eq, PartialEq, SerializeIndexed)]
114#[non_exhaustive]
115#[serde_indexed(offset = 1)]
116pub struct Response {
117    pub credential: PublicKeyCredentialDescriptor,
118    pub auth_data: Bytes<AUTHENTICATOR_DATA_LENGTH>,
119    pub signature: Bytes<ASN1_SIGNATURE_LENGTH>,
120    #[serde(skip_serializing_if = "Option::is_none")]
121    pub user: Option<PublicKeyCredentialUserEntity>,
122    #[serde(skip_serializing_if = "Option::is_none")]
123    pub number_of_credentials: Option<u32>,
124    #[serde(skip_serializing_if = "Option::is_none")]
125    pub user_selected: Option<bool>,
126    /// A key that can be used to encrypt and decrypt large blob data.
127    /// See https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#sctn-getAssert-authnr-alg
128    #[serde(skip_serializing_if = "Option::is_none")]
129    pub large_blob_key: Option<ByteArray<32>>,
130    #[serde(skip_serializing_if = "Option::is_none")]
131    pub unsigned_extension_outputs: Option<UnsignedExtensionOutputs>,
132    #[serde(skip_serializing_if = "Option::is_none")]
133    pub ep_att: Option<bool>,
134    #[serde(skip_serializing_if = "Option::is_none")]
135    pub att_stmt: Option<AttestationStatement>,
136}
137
138#[derive(Debug)]
139pub struct ResponseBuilder {
140    pub credential: PublicKeyCredentialDescriptor,
141    pub auth_data: Bytes<AUTHENTICATOR_DATA_LENGTH>,
142    pub signature: Bytes<ASN1_SIGNATURE_LENGTH>,
143}
144
145impl ResponseBuilder {
146    #[inline(always)]
147    pub fn build(self) -> Response {
148        Response {
149            credential: self.credential,
150            auth_data: self.auth_data,
151            signature: self.signature,
152            user: None,
153            number_of_credentials: None,
154            user_selected: None,
155            large_blob_key: None,
156            unsigned_extension_outputs: None,
157            ep_att: None,
158            att_stmt: None,
159        }
160    }
161}
162
163#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
164#[non_exhaustive]
165pub struct UnsignedExtensionOutputs {}