passkey_types/utils/
bytes.rs

1use std::ops::{Deref, DerefMut};
2
3use serde::{Deserialize, Deserializer, Serialize, de::Visitor};
4#[cfg(feature = "typeshare")]
5use typeshare::typeshare;
6
7use super::encoding;
8
9/// A newtype around `Vec<u8>` which serializes using the transport format's byte representation.
10///
11/// When feature `serialize_bytes_as_base64_string` is set, this type will be serialized into a
12/// `base64url` representation instead. Note that this type should not be used externally when this
13/// feature is set, such as in Kotlin, to avoid a serialization errors. In the future, this feature
14/// flag can be removed when typeshare supports target/language specific serialization:
15/// <https://github.com/1Password/typeshare/issues/63>
16///
17/// This will use an array of numbers for JSON, and a byte string in CBOR for example.
18///
19/// It also supports deserializing from `base64` and `base64url` formatted strings.
20#[cfg_attr(feature = "typeshare", typeshare(transparent))]
21#[derive(Debug, Default, PartialEq, Eq, Clone, Hash)]
22#[repr(transparent)]
23pub struct Bytes(Vec<u8>);
24
25impl Deref for Bytes {
26    type Target = Vec<u8>;
27
28    fn deref(&self) -> &Self::Target {
29        &self.0
30    }
31}
32
33impl DerefMut for Bytes {
34    fn deref_mut(&mut self) -> &mut Self::Target {
35        &mut self.0
36    }
37}
38
39impl From<Vec<u8>> for Bytes {
40    fn from(inner: Vec<u8>) -> Self {
41        Bytes(inner)
42    }
43}
44
45impl From<&[u8]> for Bytes {
46    fn from(value: &[u8]) -> Self {
47        Bytes(value.to_vec())
48    }
49}
50
51impl From<Bytes> for Vec<u8> {
52    fn from(src: Bytes) -> Self {
53        src.0
54    }
55}
56
57impl From<Bytes> for String {
58    fn from(src: Bytes) -> Self {
59        encoding::base64url(&src)
60    }
61}
62
63/// The string given for decoding is not `base64url` nor `base64` encoded data.
64#[derive(Debug)]
65pub struct NotBase64Encoded;
66
67impl TryFrom<&str> for Bytes {
68    type Error = NotBase64Encoded;
69
70    fn try_from(value: &str) -> Result<Self, Self::Error> {
71        encoding::try_from_base64url(value)
72            .or_else(|| encoding::try_from_base64(value))
73            .ok_or(NotBase64Encoded)
74            .map(Self)
75    }
76}
77
78impl FromIterator<u8> for Bytes {
79    fn from_iter<T: IntoIterator<Item = u8>>(iter: T) -> Self {
80        Bytes(iter.into_iter().collect())
81    }
82}
83
84impl IntoIterator for Bytes {
85    type Item = u8;
86
87    type IntoIter = std::vec::IntoIter<u8>;
88
89    fn into_iter(self) -> Self::IntoIter {
90        self.0.into_iter()
91    }
92}
93
94impl<'a> IntoIterator for &'a Bytes {
95    type Item = &'a u8;
96
97    type IntoIter = std::slice::Iter<'a, u8>;
98
99    fn into_iter(self) -> Self::IntoIter {
100        self.0.iter()
101    }
102}
103
104impl Serialize for Bytes {
105    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
106    where
107        S: serde::Serializer,
108    {
109        if cfg!(feature = "serialize_bytes_as_base64_string") {
110            serializer.serialize_str(&encoding::base64url(&self.0))
111        } else {
112            serializer.serialize_bytes(&self.0)
113        }
114    }
115}
116
117impl<'de> Deserialize<'de> for Bytes {
118    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
119    where
120        D: Deserializer<'de>,
121    {
122        struct Base64Visitor;
123
124        impl<'de> Visitor<'de> for Base64Visitor {
125            type Value = Bytes;
126
127            fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
128                write!(f, "A vector of bytes or a base46(url) encoded string")
129            }
130            fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
131            where
132                E: serde::de::Error,
133            {
134                self.visit_str(v)
135            }
136            fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
137            where
138                E: serde::de::Error,
139            {
140                self.visit_str(&v)
141            }
142            fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
143            where
144                E: serde::de::Error,
145            {
146                // There have been some whitespace seen in incoming base64 encodings.
147                let v = v.trim();
148                v.try_into().map_err(|_| {
149                    E::invalid_value(
150                        serde::de::Unexpected::Str(v),
151                        &"A base64(url) encoded string",
152                    )
153                })
154            }
155            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
156            where
157                A: serde::de::SeqAccess<'de>,
158            {
159                let mut buf = Vec::with_capacity(seq.size_hint().unwrap_or_default());
160                while let Some(byte) = seq.next_element()? {
161                    buf.push(byte);
162                }
163                Ok(Bytes(buf))
164            }
165            fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
166            where
167                E: serde::de::Error,
168            {
169                Ok(Bytes(v.to_vec()))
170            }
171        }
172        deserializer.deserialize_any(Base64Visitor)
173    }
174}
175
176#[cfg(test)]
177mod tests;