1use std::fmt;
17
18use serde::de::{SeqAccess, Visitor};
19use subtle::ConstantTimeEq;
20pub use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
21
22#[cfg(all(feature = "test-utils", unix))]
23pub mod heap_canary;
24
25const DESERIALIZE_CAPACITY: usize = 64;
30
31#[derive(Clone)]
41pub struct Secret(Zeroizing<Vec<u8>>);
42
43impl Secret {
44 pub fn new(bytes: Vec<u8>) -> Self {
46 Self(Zeroizing::new(bytes))
47 }
48
49 pub fn as_slice(&self) -> &[u8] {
51 self.0.as_slice()
52 }
53}
54
55impl From<Vec<u8>> for Secret {
56 fn from(bytes: Vec<u8>) -> Self {
57 Self::new(bytes)
58 }
59}
60
61impl From<Secret> for Zeroizing<Vec<u8>> {
62 fn from(secret: Secret) -> Self {
63 secret.0
64 }
65}
66
67impl fmt::Debug for Secret {
68 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69 write!(f, "Secret(<{} bytes>)", self.0.len())
70 }
71}
72
73impl PartialEq for Secret {
74 fn eq(&self, other: &Self) -> bool {
75 self.as_slice().ct_eq(other.as_slice()).into()
76 }
77}
78
79impl Eq for Secret {}
80
81impl ZeroizeOnDrop for Secret {}
82
83impl openmls_traits::storage::Entity<{ openmls_traits::storage::CURRENT_VERSION }> for Secret {}
86
87impl serde::Serialize for Secret {
88 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
89 serializer.serialize_bytes(self.as_slice())
90 }
91}
92
93impl<'de> serde::Deserialize<'de> for Secret {
94 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
95 if deserializer.is_human_readable() {
96 deserializer.deserialize_seq(SecretVisitor)
97 } else {
98 deserializer.deserialize_bytes(SecretVisitor)
99 }
100 }
101}
102
103pub fn deserialize_seq<'de, D: serde::Deserializer<'de>>(
107 deserializer: D,
108) -> Result<Secret, D::Error> {
109 deserializer.deserialize_seq(SecretVisitor)
110}
111
112struct SecretVisitor;
124
125impl<'de> Visitor<'de> for SecretVisitor {
126 type Value = Secret;
127
128 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129 f.write_str("secret bytes")
130 }
131
132 fn visit_bytes<E: serde::de::Error>(self, bytes: &[u8]) -> Result<Secret, E> {
133 let mut secret = Zeroizing::new(Vec::with_capacity(bytes.len()));
134 secret.extend_from_slice(bytes);
135 Ok(Secret(secret))
136 }
137
138 fn visit_byte_buf<E: serde::de::Error>(self, bytes: Vec<u8>) -> Result<Secret, E> {
139 Ok(Secret(Zeroizing::new(bytes)))
140 }
141
142 fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Secret, A::Error> {
143 let mut bytes = Zeroizing::new(Vec::with_capacity(DESERIALIZE_CAPACITY));
144 while let Some(byte) = seq.next_element::<u8>()? {
145 push_zeroizing(&mut bytes, byte);
146 }
147 Ok(Secret(bytes))
148 }
149}
150
151fn push_zeroizing(bytes: &mut Zeroizing<Vec<u8>>, byte: u8) {
155 if bytes.len() == bytes.capacity() {
156 let mut grown = Zeroizing::new(Vec::with_capacity(bytes.capacity().max(1) * 2));
157 grown.extend_from_slice(bytes);
158 *bytes = grown;
160 }
161 bytes.push(byte);
162}
163
164#[cfg(test)]
165mod tests {
166 use super::*;
167
168 const KEY: [u8; 32] = [
169 0x8f, 0x13, 0x2a, 0x55, 0xc0, 0x01, 0x7e, 0x9d, 0x42, 0xbb, 0x06, 0xe1, 0x3c, 0x77, 0xa4,
170 0x18, 0xd2, 0x5b, 0x90, 0x6f, 0x24, 0xe8, 0x31, 0xcd, 0x0a, 0x99, 0x4e, 0xf7, 0x63, 0xb5,
171 0x2c, 0x81,
172 ];
173
174 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
175 #[cfg_attr(not(target_arch = "wasm32"), test)]
176 fn debug_shows_only_the_length() {
177 let debug = format!("{:?}", Secret::new(KEY.to_vec()));
178 assert_eq!(debug, "Secret(<32 bytes>)");
179 assert!(!debug.contains(&hex::encode(KEY)));
180 assert!(!debug.contains(&format!("{:?}", KEY.as_slice())));
181 }
182
183 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
184 #[cfg_attr(not(target_arch = "wasm32"), test)]
185 fn serde_is_a_byte_string() {
186 for len in [0, 1, 23, 24, 32, 64, 65, 200] {
187 let bytes: Vec<u8> = (0..len).map(|i| i as u8).collect();
188 let secret = Secret::new(bytes.clone());
189
190 let cbor_secret = cbor4ii::serde::to_vec(Vec::new(), &secret).unwrap();
193 let header = match u8::try_from(len).unwrap() {
194 short @ 0..=23 => vec![0x40 | short],
195 long => vec![0x58, long],
196 };
197 assert_eq!(cbor_secret, [header.as_slice(), &bytes].concat());
198 let decoded: Secret = cbor4ii::serde::from_slice(&cbor_secret).unwrap();
199 assert_eq!(decoded.as_slice(), bytes.as_slice());
200
201 let cbor_vec = cbor4ii::serde::to_vec(Vec::new(), &bytes).unwrap();
204 let mut reader = cbor4ii::core::utils::SliceReader::new(&cbor_vec);
205 let decoded =
206 deserialize_seq(&mut cbor4ii::serde::Deserializer::new(&mut reader)).unwrap();
207 assert_eq!(decoded.as_slice(), bytes.as_slice());
208
209 let json_secret = serde_json::to_vec(&secret).unwrap();
211 assert_eq!(json_secret, serde_json::to_vec(&bytes).unwrap());
212 let decoded: Secret = serde_json::from_slice(&json_secret).unwrap();
213 assert_eq!(decoded.as_slice(), bytes.as_slice());
214 }
215 }
216
217 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
218 #[cfg_attr(not(target_arch = "wasm32"), test)]
219 fn serde_rejects_what_is_not_secret_bytes() {
220 for json in [
223 "\"0x01\"", "\"AQI=\"", "[256]", "[-1]", "[1,", "{}", "null", "7",
224 ] {
225 assert!(serde_json::from_str::<Secret>(json).is_err(), "{json}");
226 }
227 let mut truncated = cbor4ii::serde::to_vec(Vec::new(), &Secret::new(KEY.to_vec())).unwrap();
230 truncated.truncate(truncated.len() - 1);
231 let integers = cbor4ii::serde::to_vec(Vec::new(), &KEY.to_vec()).unwrap();
232 for cbor in [&truncated[..], &integers, &[0x62, b'a', b'b']] {
233 assert!(
234 cbor4ii::serde::from_slice::<Secret>(cbor).is_err(),
235 "{cbor:02x?}"
236 );
237 }
238 }
239
240 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
241 #[cfg_attr(not(target_arch = "wasm32"), test)]
242 fn equality_compares_bytes() {
243 assert_eq!(Secret::new(KEY.to_vec()), Secret::from(KEY.to_vec()));
244 assert_ne!(Secret::new(KEY.to_vec()), Secret::new(KEY[..31].to_vec()));
245 let mut other = KEY;
246 other[31] ^= 1;
247 assert_ne!(Secret::new(KEY.to_vec()), Secret::new(other.to_vec()));
248 }
249}