1use std::fmt;
6
7use serde::{
8 Deserialize, Deserializer, Serialize,
9 de::{self, MapAccess, SeqAccess, Visitor},
10};
11use serde_json::{Map, Value};
12
13#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
15pub enum CanonicalError {
16 #[error("number {0} is not a safe integer")]
18 Number(String),
19 #[error("not representable as JSON: {0}")]
22 Json(String),
23}
24
25pub const MAX_SAFE_INTEGER: u64 = (1 << 53) - 1;
27
28pub fn to_canonical_vec<T: Serialize + ?Sized>(value: &T) -> Result<Vec<u8>, CanonicalError> {
31 let value = serde_json::to_value(value).map_err(|e| CanonicalError::Json(e.to_string()))?;
32 let mut out = String::new();
33 write_value(&value, &mut out)?;
34 Ok(out.into_bytes())
35}
36
37pub fn canonicalize(json: &[u8]) -> Result<Vec<u8>, CanonicalError> {
40 let Strict(value) = serde_json::from_slice(json).map_err(|e| CanonicalError::Json(e.to_string()))?;
41 let mut out = String::new();
42 write_value(&value, &mut out)?;
43 Ok(out.into_bytes())
44}
45
46struct Strict(Value);
48
49impl<'de> Deserialize<'de> for Strict {
50 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
51 deserializer.deserialize_any(StrictVisitor).map(Strict)
52 }
53}
54
55struct StrictVisitor;
56
57impl<'de> Visitor<'de> for StrictVisitor {
58 type Value = Value;
59
60 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61 f.write_str("a JSON value")
62 }
63
64 fn visit_unit<E>(self) -> Result<Value, E> {
65 Ok(Value::Null)
66 }
67
68 fn visit_bool<E>(self, v: bool) -> Result<Value, E> {
69 Ok(Value::Bool(v))
70 }
71
72 fn visit_i64<E>(self, v: i64) -> Result<Value, E> {
73 Ok(Value::from(v))
74 }
75
76 fn visit_u64<E>(self, v: u64) -> Result<Value, E> {
77 Ok(Value::from(v))
78 }
79
80 fn visit_f64<E>(self, v: f64) -> Result<Value, E> {
81 Ok(Value::from(v))
83 }
84
85 fn visit_str<E>(self, v: &str) -> Result<Value, E> {
86 Ok(Value::String(v.to_owned()))
87 }
88
89 fn visit_string<E>(self, v: String) -> Result<Value, E> {
90 Ok(Value::String(v))
91 }
92
93 fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Value, A::Error> {
94 let mut items = Vec::new();
95 while let Some(Strict(item)) = seq.next_element()? {
96 items.push(item);
97 }
98 Ok(Value::Array(items))
99 }
100
101 fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<Value, A::Error> {
102 let mut members = Map::new();
103 while let Some(name) = map.next_key::<String>()? {
104 let Strict(value) = map.next_value()?;
105 if members.contains_key(&name) {
106 return Err(de::Error::custom(format!("duplicate member {name:?}")));
107 }
108 members.insert(name, value);
109 }
110 Ok(Value::Object(members))
111 }
112}
113
114fn write_value(value: &Value, out: &mut String) -> Result<(), CanonicalError> {
115 match value {
116 Value::Null => out.push_str("null"),
117 Value::Bool(b) => out.push_str(if *b { "true" } else { "false" }),
118 Value::Number(n) => {
119 let safe = n.as_u64().is_some_and(|v| v <= MAX_SAFE_INTEGER)
120 || n.as_i64().is_some_and(|v| v.unsigned_abs() <= MAX_SAFE_INTEGER);
121 if !safe {
122 return Err(CanonicalError::Number(n.to_string()));
123 }
124 out.push_str(&n.to_string());
125 }
126 Value::String(s) => write_string(s, out),
127 Value::Array(items) => {
128 out.push('[');
129 for (i, item) in items.iter().enumerate() {
130 if i > 0 {
131 out.push(',');
132 }
133 write_value(item, out)?;
134 }
135 out.push(']');
136 }
137 Value::Object(members) => {
138 let mut sorted: Vec<_> = members.iter().collect();
139 sorted.sort_by(|(a, _), (b, _)| a.encode_utf16().cmp(b.encode_utf16()));
140 out.push('{');
141 for (i, (name, item)) in sorted.into_iter().enumerate() {
142 if i > 0 {
143 out.push(',');
144 }
145 write_string(name, out);
146 out.push(':');
147 write_value(item, out)?;
148 }
149 out.push('}');
150 }
151 }
152 Ok(())
153}
154
155fn write_string(s: &str, out: &mut String) {
158 out.push('"');
159 for c in s.chars() {
160 match c {
161 '"' => out.push_str("\\\""),
162 '\\' => out.push_str("\\\\"),
163 '\u{8}' => out.push_str("\\b"),
164 '\u{c}' => out.push_str("\\f"),
165 '\n' => out.push_str("\\n"),
166 '\r' => out.push_str("\\r"),
167 '\t' => out.push_str("\\t"),
168 c if (c as u32) < 0x20 => out.push_str(&format!("\\u{:04x}", c as u32)),
169 c => out.push(c),
170 }
171 }
172 out.push('"');
173}
174
175#[cfg(test)]
176mod tests {
177 use serde_json::json;
178
179 use super::*;
180
181 fn canon(value: Value) -> String {
182 String::from_utf8(to_canonical_vec(&value).unwrap()).unwrap()
183 }
184
185 #[test]
186 fn sorts_members_and_drops_whitespace() {
187 assert_eq!(
188 canon(json!({"b": 1, "a": [true, null, {"d": "x", "c": 2}]})),
189 r#"{"a":[true,null,{"c":2,"d":"x"}],"b":1}"#
190 );
191 }
192
193 #[test]
194 fn sorts_by_utf16_code_units() {
195 assert_eq!(canon(json!({"\u{FB33}": 1, "\u{10000}": 2})), "{\"\u{10000}\":2,\"\u{FB33}\":1}");
197 }
198
199 #[test]
200 fn escapes_only_what_json_requires() {
201 assert_eq!(canon(json!("a\"b\\c/€\u{1}\n")), "\"a\\\"b\\\\c/€\\u0001\\n\"");
202 assert_eq!(canon(json!("\u{1f}")), "\"\\u001f\"");
203 }
204
205 #[test]
206 fn canonicalizes_received_text() {
207 let text = br#"{ "b" : [1, "\u00e9"], "a" : {"unknown": null} }"#;
208 assert_eq!(canonicalize(text).unwrap(), "{\"a\":{\"unknown\":null},\"b\":[1,\"\u{e9}\"]}".as_bytes());
209 let duplicate = canonicalize(br#"{"a":1,"a":2}"#).unwrap_err();
210 assert!(duplicate.to_string().contains("duplicate member \"a\""), "{duplicate}");
211 for bad in [&b"{"[..], b"1.5", b"9007199254740992", b"{\"a\":1e3}"] {
212 assert!(canonicalize(bad).is_err(), "{}", String::from_utf8_lossy(bad));
213 }
214 }
215
216 #[test]
217 fn allows_only_safe_integers() {
218 assert_eq!(canon(json!(9_007_199_254_740_991u64)), "9007199254740991");
219 assert_eq!(canon(json!(-42)), "-42");
220 assert!(to_canonical_vec(&json!(9_007_199_254_740_992u64)).is_err());
221 assert!(to_canonical_vec(&json!(1.5)).is_err());
222 }
223}