1use std::cmp::Ordering;
6
7use serde_json::Value;
8use zeroize::Zeroizing;
9
10use crate::error::JcsError;
11use crate::parse_json::{parse_json_text, SensitiveJsonValue, SensitiveNumber};
12
13const MAX_INTEROPERABLE_INTEGER: u64 = 9_007_199_254_740_991;
14const MIN_INTEROPERABLE_INTEGER: i64 = -9_007_199_254_740_991;
15const MAX_INTEROPERABLE_INTEGER_F64: f64 = 9_007_199_254_740_991.0;
16const MIN_INTEROPERABLE_INTEGER_F64: f64 = -9_007_199_254_740_991.0;
17
18pub fn canonicalize_trusted_json_value(value: &Value) -> Result<String, JcsError> {
29 let output_length = canonicalized_len(value, 0)?;
30 let mut output = Zeroizing::new(String::with_capacity(output_length));
31 write_canonical(value, 0, &mut output)?;
32 if output.len() != output_length {
33 return Err(JcsError::SerializationError);
34 }
35 Ok(core::mem::take(&mut *output))
36}
37
38pub fn canonicalize_json_text(input: &str) -> Result<String, JcsError> {
47 let value = parse_json_text(input)?;
48 canonicalize_sensitive_json_value(&value)
49}
50
51fn write_canonical(value: &Value, depth: usize, output: &mut String) -> Result<(), JcsError> {
55 match value {
56 Value::Null => output.push_str("null"),
57 Value::Bool(false) => output.push_str("false"),
58 Value::Bool(true) => output.push_str("true"),
59 Value::Number(value) => write_canonical_number(value, output)?,
60 Value::String(value) => write_escaped_string(value, output),
61 Value::Array(values) => write_canonical_array(values, depth, output)?,
62 Value::Object(values) => {
63 let child_depth = descend(depth)?;
64 let mut keys: Vec<&String> = values.keys().collect();
65 keys.sort_by(|left, right| utf16_cmp(left, right));
72
73 output.push('{');
74 for (index, key) in keys.iter().enumerate() {
75 if index > 0 {
76 output.push(',');
77 }
78 write_escaped_string(key, output);
79 output.push(':');
80 write_canonical(&values[*key], child_depth, output)?;
81 }
82 output.push('}');
83 }
84 }
85 Ok(())
86}
87
88fn descend(depth: usize) -> Result<usize, JcsError> {
91 let next = depth.checked_add(1).ok_or(JcsError::DepthExceeded)?;
92 if next > crate::MAX_NESTING_DEPTH {
93 return Err(JcsError::DepthExceeded);
94 }
95 Ok(next)
96}
97
98fn utf16_cmp(left: &str, right: &str) -> Ordering {
101 left.encode_utf16().cmp(right.encode_utf16())
102}
103
104fn write_canonical_number(value: &serde_json::Number, output: &mut String) -> Result<(), JcsError> {
105 if let Some(unsigned) = value.as_u64() {
106 if unsigned > MAX_INTEROPERABLE_INTEGER {
107 return Err(JcsError::IntegerOutsideInteroperableRange);
108 }
109 let mut buffer = itoa::Buffer::new();
110 output.push_str(buffer.format(unsigned));
111 return Ok(());
112 }
113 if let Some(signed) = value.as_i64() {
114 if signed < MIN_INTEROPERABLE_INTEGER {
115 return Err(JcsError::IntegerOutsideInteroperableRange);
116 }
117 let mut buffer = itoa::Buffer::new();
118 output.push_str(buffer.format(signed));
119 return Ok(());
120 }
121
122 let float = value.as_f64().ok_or(JcsError::SerializationError)?;
123 if !float.is_finite() {
124 return Err(JcsError::NonFiniteNumber);
125 }
126 validate_interoperable_float_integer(float)?;
127
128 let mut buffer = ryu_js::Buffer::new();
134 output.push_str(buffer.format_finite(float));
135 Ok(())
136}
137
138fn write_sensitive_number(value: &SensitiveNumber, output: &mut String) -> Result<(), JcsError> {
139 match value {
140 SensitiveNumber::Unsigned(unsigned) => {
141 if *unsigned > MAX_INTEROPERABLE_INTEGER {
142 return Err(JcsError::IntegerOutsideInteroperableRange);
143 }
144 let mut buffer = itoa::Buffer::new();
145 output.push_str(buffer.format(*unsigned));
146 }
147 SensitiveNumber::Signed(signed) => {
148 if *signed < MIN_INTEROPERABLE_INTEGER {
149 return Err(JcsError::IntegerOutsideInteroperableRange);
150 }
151 let mut buffer = itoa::Buffer::new();
152 output.push_str(buffer.format(*signed));
153 }
154 SensitiveNumber::Float(float) => {
155 if !float.is_finite() {
156 return Err(JcsError::NonFiniteNumber);
157 }
158 validate_interoperable_float_integer(*float)?;
159 let mut buffer = ryu_js::Buffer::new();
160 output.push_str(buffer.format_finite(*float));
161 }
162 }
163 Ok(())
164}
165
166fn write_canonical_array(
167 values: &[Value],
168 depth: usize,
169 output: &mut String,
170) -> Result<(), JcsError> {
171 let child_depth = descend(depth)?;
172 output.push('[');
173 for (index, item) in values.iter().enumerate() {
174 if index > 0 {
175 output.push(',');
176 }
177 write_canonical(item, child_depth, output)?;
178 }
179 output.push(']');
180 Ok(())
181}
182
183fn canonicalized_len(value: &Value, depth: usize) -> Result<usize, JcsError> {
184 match value {
185 Value::Null => Ok("null".len()),
186 Value::Bool(false) => Ok("false".len()),
187 Value::Bool(true) => Ok("true".len()),
188 Value::Number(value) => canonical_number_len(value),
189 Value::String(value) => escaped_string_len(value),
190 Value::Array(values) => canonical_array_len(values, depth),
191 Value::Object(values) => canonical_object_len(values, depth),
192 }
193}
194
195fn canonicalize_sensitive_json_value(value: &SensitiveJsonValue) -> Result<String, JcsError> {
196 let output_length = canonicalized_sensitive_len(value, 0)?;
197 let mut output = Zeroizing::new(String::with_capacity(output_length));
198 write_sensitive_canonical(value, 0, &mut output)?;
199 if output.len() != output_length {
200 return Err(JcsError::SerializationError);
201 }
202 Ok(core::mem::take(&mut *output))
203}
204
205fn write_sensitive_canonical(
206 value: &SensitiveJsonValue,
207 depth: usize,
208 output: &mut String,
209) -> Result<(), JcsError> {
210 match value {
211 SensitiveJsonValue::Null => output.push_str("null"),
212 SensitiveJsonValue::Bool(false) => output.push_str("false"),
213 SensitiveJsonValue::Bool(true) => output.push_str("true"),
214 SensitiveJsonValue::Number(value) => write_sensitive_number(value, output)?,
215 SensitiveJsonValue::String(value) => write_escaped_string(value, output),
216 SensitiveJsonValue::Array(values) => {
217 let child_depth = descend(depth)?;
218 output.push('[');
219 for (index, item) in values.iter().enumerate() {
220 if index > 0 {
221 output.push(',');
222 }
223 write_sensitive_canonical(item, child_depth, output)?;
224 }
225 output.push(']');
226 }
227 SensitiveJsonValue::Object(values) => {
228 let child_depth = descend(depth)?;
229 let mut keys: Vec<&String> = values.keys().collect();
230 keys.sort_by(|left, right| utf16_cmp(left, right));
231
232 output.push('{');
233 for (index, key) in keys.iter().enumerate() {
234 if index > 0 {
235 output.push(',');
236 }
237 write_escaped_string(key, output);
238 output.push(':');
239 let value = values.get(*key).ok_or(JcsError::SerializationError)?;
240 write_sensitive_canonical(value, child_depth, output)?;
241 }
242 output.push('}');
243 }
244 }
245 Ok(())
246}
247
248fn canonicalized_sensitive_len(
249 value: &SensitiveJsonValue,
250 depth: usize,
251) -> Result<usize, JcsError> {
252 match value {
253 SensitiveJsonValue::Null => Ok("null".len()),
254 SensitiveJsonValue::Bool(false) => Ok("false".len()),
255 SensitiveJsonValue::Bool(true) => Ok("true".len()),
256 SensitiveJsonValue::Number(value) => canonical_sensitive_number_len(value),
257 SensitiveJsonValue::String(value) => escaped_string_len(value),
258 SensitiveJsonValue::Array(values) => canonical_sensitive_array_len(values, depth),
259 SensitiveJsonValue::Object(values) => canonical_sensitive_object_len(values, depth),
260 }
261}
262
263fn canonical_sensitive_array_len(
264 values: &[Box<SensitiveJsonValue>],
265 depth: usize,
266) -> Result<usize, JcsError> {
267 let child_depth = descend(depth)?;
268 let mut length = "["
269 .len()
270 .checked_add("]".len())
271 .ok_or(JcsError::SerializationError)?;
272 for (index, item) in values.iter().enumerate() {
273 if index > 0 {
274 length = length
275 .checked_add(",".len())
276 .ok_or(JcsError::SerializationError)?;
277 }
278 length = length
279 .checked_add(canonicalized_sensitive_len(item, child_depth)?)
280 .ok_or(JcsError::SerializationError)?;
281 }
282 Ok(length)
283}
284
285fn canonical_sensitive_object_len(
286 values: &std::collections::BTreeMap<String, Box<SensitiveJsonValue>>,
287 depth: usize,
288) -> Result<usize, JcsError> {
289 let child_depth = descend(depth)?;
290 let mut keys: Vec<&String> = values.keys().collect();
291 keys.sort_by(|left, right| utf16_cmp(left, right));
292
293 let mut length = "{"
294 .len()
295 .checked_add("}".len())
296 .ok_or(JcsError::SerializationError)?;
297 for (index, key) in keys.iter().enumerate() {
298 if index > 0 {
299 length = length
300 .checked_add(",".len())
301 .ok_or(JcsError::SerializationError)?;
302 }
303 let value = values.get(*key).ok_or(JcsError::SerializationError)?;
304 let value_length = canonicalized_sensitive_len(value, child_depth)?;
305 length = length
306 .checked_add(escaped_string_len(key)?)
307 .and_then(|value| value.checked_add(":".len()))
308 .and_then(|value| value.checked_add(value_length))
309 .ok_or(JcsError::SerializationError)?;
310 }
311 Ok(length)
312}
313
314fn canonical_array_len(values: &[Value], depth: usize) -> Result<usize, JcsError> {
315 let child_depth = descend(depth)?;
316 let mut length = "["
317 .len()
318 .checked_add("]".len())
319 .ok_or(JcsError::SerializationError)?;
320 for (index, item) in values.iter().enumerate() {
321 if index > 0 {
322 length = length
323 .checked_add(",".len())
324 .ok_or(JcsError::SerializationError)?;
325 }
326 length = length
327 .checked_add(canonicalized_len(item, child_depth)?)
328 .ok_or(JcsError::SerializationError)?;
329 }
330 Ok(length)
331}
332
333fn canonical_object_len(
334 values: &serde_json::Map<String, Value>,
335 depth: usize,
336) -> Result<usize, JcsError> {
337 let child_depth = descend(depth)?;
338 let mut keys: Vec<&String> = values.keys().collect();
339 keys.sort_by(|left, right| utf16_cmp(left, right));
340
341 let mut length = "{"
342 .len()
343 .checked_add("}".len())
344 .ok_or(JcsError::SerializationError)?;
345 for (index, key) in keys.iter().enumerate() {
346 if index > 0 {
347 length = length
348 .checked_add(",".len())
349 .ok_or(JcsError::SerializationError)?;
350 }
351 let value_length = canonicalized_len(&values[*key], child_depth)?;
352 length = length
353 .checked_add(escaped_string_len(key)?)
354 .and_then(|value| value.checked_add(":".len()))
355 .and_then(|value| value.checked_add(value_length))
356 .ok_or(JcsError::SerializationError)?;
357 }
358 Ok(length)
359}
360
361fn canonical_number_len(value: &serde_json::Number) -> Result<usize, JcsError> {
362 if let Some(unsigned) = value.as_u64() {
363 if unsigned > MAX_INTEROPERABLE_INTEGER {
364 return Err(JcsError::IntegerOutsideInteroperableRange);
365 }
366 let mut buffer = itoa::Buffer::new();
367 return Ok(buffer.format(unsigned).len());
368 }
369 if let Some(signed) = value.as_i64() {
370 if signed < MIN_INTEROPERABLE_INTEGER {
371 return Err(JcsError::IntegerOutsideInteroperableRange);
372 }
373 let mut buffer = itoa::Buffer::new();
374 return Ok(buffer.format(signed).len());
375 }
376
377 let float = value.as_f64().ok_or(JcsError::SerializationError)?;
378 if !float.is_finite() {
379 return Err(JcsError::NonFiniteNumber);
380 }
381 validate_interoperable_float_integer(float)?;
382
383 let mut buffer = ryu_js::Buffer::new();
384 Ok(buffer.format_finite(float).len())
385}
386
387fn canonical_sensitive_number_len(value: &SensitiveNumber) -> Result<usize, JcsError> {
388 match value {
389 SensitiveNumber::Unsigned(unsigned) => {
390 if *unsigned > MAX_INTEROPERABLE_INTEGER {
391 return Err(JcsError::IntegerOutsideInteroperableRange);
392 }
393 let mut buffer = itoa::Buffer::new();
394 Ok(buffer.format(*unsigned).len())
395 }
396 SensitiveNumber::Signed(signed) => {
397 if *signed < MIN_INTEROPERABLE_INTEGER {
398 return Err(JcsError::IntegerOutsideInteroperableRange);
399 }
400 let mut buffer = itoa::Buffer::new();
401 Ok(buffer.format(*signed).len())
402 }
403 SensitiveNumber::Float(float) => {
404 if !float.is_finite() {
405 return Err(JcsError::NonFiniteNumber);
406 }
407 validate_interoperable_float_integer(*float)?;
408 let mut buffer = ryu_js::Buffer::new();
409 Ok(buffer.format_finite(*float).len())
410 }
411 }
412}
413
414fn validate_interoperable_float_integer(value: f64) -> Result<(), JcsError> {
415 if value.fract() == 0.0
416 && !(MIN_INTEROPERABLE_INTEGER_F64..=MAX_INTEROPERABLE_INTEGER_F64).contains(&value)
417 {
418 return Err(JcsError::IntegerOutsideInteroperableRange);
419 }
420 Ok(())
421}
422
423fn escaped_string_len(value: &str) -> Result<usize, JcsError> {
424 let mut length = "\""
425 .len()
426 .checked_add("\"".len())
427 .ok_or(JcsError::SerializationError)?;
428 for character in value.chars() {
429 let scalar = u32::from(character);
430 if (0xfdd0..=0xfdef).contains(&scalar) || scalar & 0xfffe == 0xfffe {
431 return Err(JcsError::Noncharacter);
432 }
433 length = length
434 .checked_add(escaped_character_len(character))
435 .ok_or(JcsError::SerializationError)?;
436 }
437 Ok(length)
438}
439
440fn escaped_character_len(character: char) -> usize {
441 match character {
442 '"' | '\\' | '\u{08}' | '\t' | '\n' | '\u{0c}' | '\r' => 2,
443 '\u{00}'..='\u{1f}' => 6,
444 _ => character.len_utf8(),
445 }
446}
447
448fn write_escaped_string(value: &str, output: &mut String) {
449 const HEX: &[u8; 16] = b"0123456789abcdef";
450
451 output.push('"');
452 for character in value.chars() {
453 match character {
454 '"' => output.push_str("\\\""),
455 '\\' => output.push_str("\\\\"),
456 '\u{08}' => output.push_str("\\b"),
457 '\t' => output.push_str("\\t"),
458 '\n' => output.push_str("\\n"),
459 '\u{0c}' => output.push_str("\\f"),
460 '\r' => output.push_str("\\r"),
461 '\u{00}'..='\u{1f}' => {
462 let byte = character as u8;
463 output.push_str("\\u00");
464 output.push(char::from(HEX[usize::from(byte >> 4)]));
465 output.push(char::from(HEX[usize::from(byte & 0x0f)]));
466 }
467 _ => output.push(character),
468 }
469 }
470 output.push('"');
471}