ctap_types/ctap2/
make_credential.rs1use crate::Vec;
2
3use serde::{Deserialize, Serialize};
4use serde_bytes::ByteArray;
5use serde_indexed::{DeserializeIndexed, SerializeIndexed};
6
7use super::{
8 AttestationFormatsPreference, AttestationStatement, AttestationStatementFormat,
9 AuthenticatorOptions, Error,
10};
11use crate::ctap2::credential_management::CredentialProtectionPolicy;
12use crate::webauthn::*;
13
14impl TryFrom<u8> for CredentialProtectionPolicy {
15 type Error = super::Error;
16
17 fn try_from(value: u8) -> Result<Self, Self::Error> {
18 Ok(match value {
19 1 => CredentialProtectionPolicy::Optional,
20 2 => CredentialProtectionPolicy::OptionalWithCredentialIdList,
21 3 => CredentialProtectionPolicy::Required,
22 _ => return Err(Self::Error::InvalidParameter),
23 })
24 }
25}
26
27#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
28#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
29#[non_exhaustive]
30pub struct Extensions {
31 #[serde(rename = "credProtect")]
32 #[serde(skip_serializing_if = "Option::is_none")]
33 pub cred_protect: Option<u8>,
34
35 #[serde(rename = "hmac-secret")]
36 #[serde(skip_serializing_if = "Option::is_none")]
37 pub hmac_secret: Option<bool>,
38
39 #[serde(rename = "largeBlobKey")]
41 #[serde(skip_serializing_if = "Option::is_none")]
42 pub large_blob_key: Option<bool>,
43
44 #[cfg(feature = "third-party-payment")]
45 #[serde(rename = "thirdPartyPayment")]
46 #[serde(skip_serializing_if = "Option::is_none")]
47 pub third_party_payment: Option<bool>,
48}
49
50#[derive(Clone, Debug, Eq, PartialEq, DeserializeIndexed)]
51#[non_exhaustive]
52#[serde_indexed(offset = 1)]
53pub struct Request<'a> {
54 pub client_data_hash: &'a serde_bytes::Bytes,
55 pub rp: PublicKeyCredentialRpEntity,
56 pub user: PublicKeyCredentialUserEntity,
57 pub pub_key_cred_params: FilteredPublicKeyCredentialParameters,
58 #[serde(skip_serializing_if = "Option::is_none")]
59 pub exclude_list: Option<Vec<PublicKeyCredentialDescriptorRef<'a>, 16>>,
60 #[serde(skip_serializing_if = "Option::is_none")]
61 pub extensions: Option<Extensions>,
62 #[serde(skip_serializing_if = "Option::is_none")]
63 pub options: Option<AuthenticatorOptions>,
64 #[serde(skip_serializing_if = "Option::is_none")]
65 pub pin_auth: Option<&'a serde_bytes::Bytes>,
66 #[serde(skip_serializing_if = "Option::is_none")]
67 pub pin_protocol: Option<u32>,
68 #[serde(skip_serializing_if = "Option::is_none")]
69 pub enterprise_attestation: Option<u32>,
70 #[serde(skip_serializing_if = "Option::is_none")]
71 pub attestation_formats_preference: Option<AttestationFormatsPreference>,
72}
73
74pub type AttestationObject = Response;
75
76pub type AuthenticatorData<'a> =
77 super::AuthenticatorData<'a, AttestedCredentialData<'a>, Extensions>;
78
79#[derive(Clone, Debug, Eq, PartialEq)]
82pub struct AttestedCredentialData<'a> {
83 pub aaguid: &'a [u8],
84 pub credential_id: &'a [u8],
87 pub credential_public_key: &'a [u8],
88}
89
90impl<'a> super::SerializeAttestedCredentialData for AttestedCredentialData<'a> {
91 fn serialize(&self, buffer: &mut super::SerializedAuthenticatorData) -> Result<(), Error> {
92 buffer
95 .extend_from_slice(self.aaguid)
96 .map_err(|_| Error::Other)?;
97 let credential_id_len =
99 u16::try_from(self.credential_id.len()).map_err(|_| Error::Other)?;
100 buffer
101 .extend_from_slice(&credential_id_len.to_be_bytes())
102 .map_err(|_| Error::Other)?;
103 buffer
105 .extend_from_slice(self.credential_id)
106 .map_err(|_| Error::Other)?;
107 buffer
108 .extend_from_slice(self.credential_public_key)
109 .map_err(|_| Error::Other)?;
110 Ok(())
111 }
112}
113
114#[derive(Clone, Debug, Eq, PartialEq, SerializeIndexed)]
115#[non_exhaustive]
116#[serde_indexed(offset = 1)]
117pub struct Response {
118 pub fmt: AttestationStatementFormat,
119 pub auth_data: super::SerializedAuthenticatorData,
120 #[serde(skip_serializing_if = "Option::is_none")]
121 pub att_stmt: Option<AttestationStatement>,
122 #[serde(skip_serializing_if = "Option::is_none")]
123 pub ep_att: Option<bool>,
124 #[serde(skip_serializing_if = "Option::is_none")]
125 pub large_blob_key: Option<ByteArray<32>>,
126 #[serde(skip_serializing_if = "Option::is_none")]
127 pub unsigned_extension_outputs: Option<UnsignedExtensionOutputs>,
128}
129
130#[derive(Debug)]
131pub struct ResponseBuilder {
132 pub fmt: AttestationStatementFormat,
133 pub auth_data: super::SerializedAuthenticatorData,
134}
135
136impl ResponseBuilder {
137 #[inline(always)]
138 pub fn build(self) -> Response {
139 Response {
140 fmt: self.fmt,
141 auth_data: self.auth_data,
142 att_stmt: None,
143 ep_att: None,
144 large_blob_key: None,
145 unsigned_extension_outputs: None,
146 }
147 }
148}
149
150#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
151#[non_exhaustive]
152pub struct UnsignedExtensionOutputs {}
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157 use serde_test::{assert_ser_tokens, Token};
158
159 #[test]
160 fn rp_entity_icon() {
161 let cbor = b"\xa4\x01X \xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\x02\xa2bidx0make_credential_relying_party_entity.example.comdiconohttp://icon.png\x03\xa2bidX \x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1ddnamedAdam\x04\x81\xa2calg&dtypejpublic-key";
163 let _request: Request = cbor_smol::cbor_deserialize(cbor.as_slice()).unwrap();
164
165 let cbor = b"\xa4\x01X \xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\x02\xa2bidx0make_credential_relying_party_entity.example.comcurlohttp://icon.png\x03\xa2bidX \x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1d\x1ddnamedAdam\x04\x81\xa2calg&dtypejpublic-key";
167 let _request: Request = cbor_smol::cbor_deserialize(cbor.as_slice()).unwrap();
168 }
169
170 #[test]
171 fn test_serde_attestation_statement_format() {
172 let formats = [
173 (AttestationStatementFormat::None, "none"),
174 (AttestationStatementFormat::Packed, "packed"),
175 ];
176 for (format, s) in formats {
177 assert_ser_tokens(&format, &[Token::BorrowedStr(s)]);
178 }
179 }
180}