1use crate::sizes::*;
4use crate::{Bytes, String};
5use serde::{de::Deserializer, Deserialize, Serialize};
6
7#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
8pub struct PublicKeyCredentialRpEntity {
9 pub id: String<256>,
10 #[serde(
11 default,
12 skip_serializing_if = "Option::is_none",
13 deserialize_with = "deserialize_from_str_and_truncate"
14 )]
15 pub name: Option<String<64>>,
16 #[serde(skip_serializing, alias = "url")]
24 pub icon: Option<Icon>,
25}
26
27#[derive(Clone, Debug, Eq, PartialEq)]
32#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
33pub struct Icon;
34
35impl<'de> Deserialize<'de> for Icon {
36 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
37 where
38 D: Deserializer<'de>,
39 {
40 let _s: &'de str = Deserialize::deserialize(deserializer)?;
41 Ok(Self)
42 }
43}
44
45#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
46#[serde(rename_all = "camelCase")]
47pub struct PublicKeyCredentialUserEntity {
48 pub id: Bytes<64>,
49 #[serde(
50 default,
51 deserialize_with = "deserialize_from_str_and_skip_if_too_long"
52 )]
53 #[serde(skip_serializing_if = "Option::is_none")]
54 pub icon: Option<String<128>>,
55 #[serde(
56 default,
57 skip_serializing_if = "Option::is_none",
58 deserialize_with = "deserialize_from_str_and_truncate"
59 )]
60 pub name: Option<String<64>>,
61 #[serde(
62 default,
63 skip_serializing_if = "Option::is_none",
64 deserialize_with = "deserialize_from_str_and_truncate"
65 )]
66 pub display_name: Option<String<64>>,
67}
68
69fn deserialize_from_str_and_skip_if_too_long<'de, D, const L: usize>(
70 deserializer: D,
71) -> Result<Option<String<L>>, D::Error>
72where
73 D: serde::Deserializer<'de>,
74{
75 let s: &'de str = Deserialize::deserialize(deserializer)?;
76 #[allow(clippy::unnecessary_fallible_conversions)]
78 match String::try_from(s) {
79 Ok(string) => Ok(Some(string)),
80 Err(_err) => {
81 info_now!("skipping field: {:?}", _err);
82 Ok(None)
83 }
84 }
85}
86
87fn deserialize_from_str_and_truncate<'de, D, const L: usize>(
88 deserializer: D,
89) -> Result<Option<String<L>>, D::Error>
90where
91 D: serde::Deserializer<'de>,
92{
93 let s: Option<&str> = serde::Deserialize::deserialize(deserializer)?;
94 Ok(s.map(truncate))
95}
96
97fn truncate<const L: usize>(s: &str) -> String<L> {
98 let split = floor_char_boundary(s, L);
99 let mut truncated = String::new();
100 truncated.push_str(&s[..split]).unwrap();
102 truncated
103}
104
105fn floor_char_boundary(s: &str, index: usize) -> usize {
107 if index >= s.len() {
108 s.len()
109 } else {
110 let lower_bound = index.saturating_sub(3);
111 let new_index = s.as_bytes()[lower_bound..=index]
112 .iter()
113 .rposition(|b| is_utf8_char_boundary(*b));
114
115 unsafe { lower_bound + new_index.unwrap_unchecked() }
117 }
118}
119
120#[inline]
122const fn is_utf8_char_boundary(b: u8) -> bool {
123 (b as i8) >= -0x40
125}
126
127impl PublicKeyCredentialUserEntity {
128 pub fn from(id: Bytes<64>) -> Self {
129 Self {
130 id,
131 icon: None,
132 name: None,
133 display_name: None,
134 }
135 }
136}
137
138#[derive(Clone, Debug, Eq, PartialEq)]
139pub struct KnownPublicKeyCredentialParameters {
140 pub alg: i32,
141}
142
143impl From<KnownPublicKeyCredentialParameters> for PublicKeyCredentialParameters {
144 fn from(value: KnownPublicKeyCredentialParameters) -> Self {
145 Self {
146 alg: value.alg,
147 key_type: String::from("public-key"),
148 }
149 }
150}
151
152pub enum UnknownPKCredentialParam {
153 UnknownType,
154 UnknownAlg,
155}
156
157pub const ES256: i32 = -7;
159pub const ED_DSA: i32 = -8;
161
162pub const COUNT_KNOWN_ALGS: usize = 2;
163pub const KNOWN_ALGS: [i32; COUNT_KNOWN_ALGS] = [ES256, ED_DSA];
164
165impl TryFrom<PublicKeyCredentialParameters> for KnownPublicKeyCredentialParameters {
166 type Error = UnknownPKCredentialParam;
167
168 fn try_from(value: PublicKeyCredentialParameters) -> Result<Self, Self::Error> {
169 if value.key_type != "public-key" {
170 Err(UnknownPKCredentialParam::UnknownType)
171 } else if KNOWN_ALGS.contains(&value.alg) {
172 Ok(Self { alg: value.alg })
173 } else {
174 Err(UnknownPKCredentialParam::UnknownAlg)
175 }
176 }
177}
178
179#[derive(Clone, Debug, Eq, PartialEq)]
181pub struct FilteredPublicKeyCredentialParameters(
182 pub heapless::Vec<KnownPublicKeyCredentialParameters, COUNT_KNOWN_ALGS>,
183);
184
185impl Serialize for FilteredPublicKeyCredentialParameters {
186 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
187 where
188 S: serde::Serializer,
189 {
190 use serde::ser::SerializeSeq;
191 let mut seq = serializer.serialize_seq(Some(self.0.len()))?;
192 for element in &self.0 {
193 let el: PublicKeyCredentialParameters = element.clone().into();
194 seq.serialize_element(&el)?
195 }
196 seq.end()
197 }
198}
199
200impl<'de> Deserialize<'de> for FilteredPublicKeyCredentialParameters {
201 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
202 where
203 D: serde::Deserializer<'de>,
204 {
205 struct ValueVisitor;
206 impl<'de> serde::de::Visitor<'de> for ValueVisitor {
207 type Value = FilteredPublicKeyCredentialParameters;
208
209 fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
210 formatter.write_str("a sequence")
211 }
212
213 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
214 where
215 A: serde::de::SeqAccess<'de>,
216 {
217 let mut values = FilteredPublicKeyCredentialParameters(Default::default());
218 while let Some(value) = seq.next_element::<PublicKeyCredentialParameters>()? {
219 let Ok(el) = value.try_into() else {
220 continue;
222 };
223 values.0.push(el).ok();
226 }
227 Ok(values)
228 }
229 }
230
231 deserializer.deserialize_seq(ValueVisitor)
232 }
233}
234
235#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
236pub struct PublicKeyCredentialParameters {
237 pub alg: i32,
238 #[serde(rename = "type")]
239 pub key_type: String<32>,
240}
241
242impl PublicKeyCredentialParameters {
243 pub fn public_key_with_alg(alg: i32) -> Self {
244 Self {
245 alg,
246 key_type: String::from("public-key"),
247 }
248 }
249}
250
251#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
252#[serde(rename_all = "camelCase")]
253pub struct PublicKeyCredentialDescriptor {
254 pub id: Bytes<MAX_CREDENTIAL_ID_LENGTH>,
257 #[serde(rename = "type")]
258 pub key_type: String<32>,
259 }
262
263#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
264#[serde(rename_all = "camelCase")]
265pub struct PublicKeyCredentialDescriptorRef<'a> {
267 pub id: &'a serde_bytes::Bytes,
268 #[serde(rename = "type")]
269 pub key_type: &'a str,
270 }
273
274#[cfg(test)]
275mod tests {
276 use super::*;
277
278 #[test]
279 fn test_truncate() {
280 let v = vec![0x61, 0x67, 0xcc, 0x88];
282 let s = std::str::from_utf8(&v).unwrap();
283
284 assert_eq!(truncate::<1>(s), "a");
285 assert_eq!(truncate::<2>(s), "ag");
286 assert_eq!(truncate::<3>(s), "ag");
287 assert_eq!(truncate::<4>(s), s);
288 assert_eq!(truncate::<5>(s), s);
289 assert_eq!(truncate::<64>(s), s);
290 }
291}