dpp/serialization/
serde_bytes.rs1use base64::prelude::BASE64_STANDARD;
20use base64::Engine;
21use serde::de::{self, SeqAccess, Visitor};
22use serde::{Deserialize, Deserializer, Serializer};
23use std::fmt;
24
25pub fn serialize<S: Serializer, const N: usize>(
26 bytes: &[u8; N],
27 serializer: S,
28) -> Result<S::Ok, S::Error> {
29 if serializer.is_human_readable() {
30 serializer.serialize_str(&BASE64_STANDARD.encode(bytes))
31 } else {
32 serializer.serialize_bytes(bytes)
33 }
34}
35
36pub fn deserialize<'de, D: Deserializer<'de>, const N: usize>(
37 deserializer: D,
38) -> Result<[u8; N], D::Error> {
39 if deserializer.is_human_readable() {
40 let s = <String>::deserialize(deserializer)?;
41 let vec = BASE64_STANDARD
42 .decode(&s)
43 .map_err(serde::de::Error::custom)?;
44 vec.try_into().map_err(|v: Vec<u8>| {
45 serde::de::Error::custom(format!("expected {} bytes, got {}", N, v.len()))
46 })
47 } else {
48 struct BytesOrSeqVisitor<const N: usize>;
53
54 impl<'de, const N: usize> Visitor<'de> for BytesOrSeqVisitor<N> {
55 type Value = [u8; N];
56
57 fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
58 write!(f, "{} bytes (as a byte buffer or sequence of u8)", N)
59 }
60
61 fn visit_bytes<E: de::Error>(self, v: &[u8]) -> Result<Self::Value, E> {
62 v.try_into()
63 .map_err(|_| E::custom(format!("expected {} bytes, got {}", N, v.len())))
64 }
65
66 fn visit_byte_buf<E: de::Error>(self, v: Vec<u8>) -> Result<Self::Value, E> {
67 let len = v.len();
68 v.try_into()
69 .map_err(|_| E::custom(format!("expected {} bytes, got {}", N, len)))
70 }
71
72 fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
73 let mut buf = Vec::with_capacity(N);
74 while let Some(b) = seq.next_element::<u8>()? {
75 buf.push(b);
76 }
77 let len = buf.len();
78 buf.try_into()
79 .map_err(|_| de::Error::custom(format!("expected {} bytes, got {}", N, len)))
80 }
81 }
82
83 deserializer.deserialize_byte_buf(BytesOrSeqVisitor::<N>)
84 }
85}
86
87#[cfg(test)]
88mod tests {
89 use base64::prelude::BASE64_STANDARD;
90 use base64::Engine;
91 use serde::{Deserialize, Serialize};
92
93 #[derive(Serialize, Deserialize, PartialEq, Debug)]
94 struct Wrap32(#[serde(with = "super")] [u8; 32]);
95
96 #[derive(Serialize, Deserialize, PartialEq, Debug)]
97 struct Wrap64(#[serde(with = "super")] [u8; 64]);
98
99 #[derive(Serialize, Deserialize, PartialEq, Debug)]
100 struct Wrap20(#[serde(with = "super")] [u8; 20]);
101
102 #[test]
103 fn json_round_trip_32_bytes_uses_base64_string() {
104 let original = Wrap32([0xab; 32]);
105 let value = serde_json::to_value(&original).expect("serialize");
106 assert_eq!(value, serde_json::json!(BASE64_STANDARD.encode([0xab; 32])));
107 let restored: Wrap32 = serde_json::from_value(value).expect("deserialize");
108 assert_eq!(original, restored);
109 }
110
111 #[test]
112 fn json_round_trip_64_bytes_uses_base64_string() {
113 let original = Wrap64([0xcd; 64]);
114 let value = serde_json::to_value(&original).expect("serialize");
115 assert_eq!(value, serde_json::json!(BASE64_STANDARD.encode([0xcd; 64])));
116 let restored: Wrap64 = serde_json::from_value(value).expect("deserialize");
117 assert_eq!(original, restored);
118 }
119
120 #[test]
121 fn json_round_trip_20_bytes_works_with_const_generic() {
122 let original = Wrap20([0x12; 20]);
123 let value = serde_json::to_value(&original).expect("serialize");
124 assert_eq!(value, serde_json::json!(BASE64_STANDARD.encode([0x12; 20])));
125 let restored: Wrap20 = serde_json::from_value(value).expect("deserialize");
126 assert_eq!(original, restored);
127 }
128
129 #[test]
130 fn rejects_wrong_length_base64() {
131 let result: Result<Wrap32, _> =
132 serde_json::from_value(serde_json::json!(BASE64_STANDARD.encode([0u8; 8])));
133 assert!(result.is_err());
134 }
135
136 #[test]
137 fn binary_round_trip_uses_raw_bytes() {
138 let original = Wrap32([0x55; 32]);
139 let bytes = bincode::serde::encode_to_vec(&original, bincode::config::standard())
140 .expect("bincode encode");
141 let (restored, _): (Wrap32, usize) =
142 bincode::serde::decode_from_slice(&bytes, bincode::config::standard())
143 .expect("bincode decode");
144 assert_eq!(original, restored);
145 }
146}