use serde::ser::{
Error as SerError, SerializeMap, SerializeSeq, SerializeStruct, SerializeStructVariant,
SerializeTuple, SerializeTupleStruct, SerializeTupleVariant,
};
use serde::{Serialize, Serializer};
use sha2::{Digest, Sha256};
use std::fmt;
pub fn canonical_json<T>(value: &T) -> Result<String, String>
where
T: Serialize + ?Sized,
{
value
.serialize(InteroperableNumberValidator)
.map_err(|error| format!("RFC 8785 canonicalization failed: {error}"))?;
serde_jcs::to_string(value)
.map_err(|error| format!("RFC 8785 canonicalization failed: {error}"))
}
pub fn canonical_sha256<T>(value: &T) -> Result<String, String>
where
T: Serialize + ?Sized,
{
let canonical = canonical_json(value)?;
Ok(format!("{:x}", Sha256::digest(canonical.as_bytes())))
}
#[derive(Debug)]
struct CanonicalizationError(String);
impl fmt::Display for CanonicalizationError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(&self.0)
}
}
impl std::error::Error for CanonicalizationError {}
impl SerError for CanonicalizationError {
fn custom<T>(message: T) -> Self
where
T: fmt::Display,
{
Self(message.to_string())
}
}
#[derive(Clone, Copy)]
struct InteroperableNumberValidator;
struct InteroperableCompound;
const MAX_INTEROPERABLE_SAFE_INTEGER: u64 = 9_007_199_254_740_991;
fn unsafe_integer_error() -> CanonicalizationError {
CanonicalizationError(format!(
"integer outside the interoperable safe integer range -{MAX_INTEROPERABLE_SAFE_INTEGER}..={MAX_INTEROPERABLE_SAFE_INTEGER}"
))
}
macro_rules! interoperable_scalar {
($($method:ident($type:ty)),+ $(,)?) => {
$(
fn $method(self, _value: $type) -> Result<Self::Ok, Self::Error> {
Ok(())
}
)+
};
}
impl Serializer for InteroperableNumberValidator {
type Ok = ();
type Error = CanonicalizationError;
type SerializeSeq = InteroperableCompound;
type SerializeTuple = InteroperableCompound;
type SerializeTupleStruct = InteroperableCompound;
type SerializeTupleVariant = InteroperableCompound;
type SerializeMap = InteroperableCompound;
type SerializeStruct = InteroperableCompound;
type SerializeStructVariant = InteroperableCompound;
interoperable_scalar!(
serialize_bool(bool),
serialize_i8(i8),
serialize_i16(i16),
serialize_i32(i32),
serialize_u8(u8),
serialize_u16(u16),
serialize_u32(u32),
serialize_char(char)
);
fn serialize_i64(self, value: i64) -> Result<Self::Ok, Self::Error> {
if value.unsigned_abs() <= MAX_INTEROPERABLE_SAFE_INTEGER {
Ok(())
} else {
Err(unsafe_integer_error())
}
}
fn serialize_u64(self, value: u64) -> Result<Self::Ok, Self::Error> {
if value <= MAX_INTEROPERABLE_SAFE_INTEGER {
Ok(())
} else {
Err(unsafe_integer_error())
}
}
fn serialize_i128(self, value: i128) -> Result<Self::Ok, Self::Error> {
if value.unsigned_abs() <= u128::from(MAX_INTEROPERABLE_SAFE_INTEGER) {
Ok(())
} else {
Err(unsafe_integer_error())
}
}
fn serialize_u128(self, value: u128) -> Result<Self::Ok, Self::Error> {
if value <= u128::from(MAX_INTEROPERABLE_SAFE_INTEGER) {
Ok(())
} else {
Err(unsafe_integer_error())
}
}
fn serialize_f32(self, value: f32) -> Result<Self::Ok, Self::Error> {
if value.is_finite() {
Ok(())
} else {
Err(CanonicalizationError("invalid float value".into()))
}
}
fn serialize_f64(self, value: f64) -> Result<Self::Ok, Self::Error> {
if value.is_finite() {
Ok(())
} else {
Err(CanonicalizationError("invalid float value".into()))
}
}
fn serialize_str(self, _value: &str) -> Result<Self::Ok, Self::Error> {
Ok(())
}
fn serialize_bytes(self, _value: &[u8]) -> Result<Self::Ok, Self::Error> {
Ok(())
}
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
Ok(())
}
fn serialize_some<T>(self, value: &T) -> Result<Self::Ok, Self::Error>
where
T: Serialize + ?Sized,
{
value.serialize(self)
}
fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
Ok(())
}
fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok, Self::Error> {
Ok(())
}
fn serialize_unit_variant(
self,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
) -> Result<Self::Ok, Self::Error> {
Ok(())
}
fn serialize_newtype_struct<T>(
self,
_name: &'static str,
value: &T,
) -> Result<Self::Ok, Self::Error>
where
T: Serialize + ?Sized,
{
value.serialize(self)
}
fn serialize_newtype_variant<T>(
self,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
value: &T,
) -> Result<Self::Ok, Self::Error>
where
T: Serialize + ?Sized,
{
value.serialize(self)
}
fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
Ok(InteroperableCompound)
}
fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, Self::Error> {
Ok(InteroperableCompound)
}
fn serialize_tuple_struct(
self,
_name: &'static str,
_len: usize,
) -> Result<Self::SerializeTupleStruct, Self::Error> {
Ok(InteroperableCompound)
}
fn serialize_tuple_variant(
self,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
_len: usize,
) -> Result<Self::SerializeTupleVariant, Self::Error> {
Ok(InteroperableCompound)
}
fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
Ok(InteroperableCompound)
}
fn serialize_struct(
self,
_name: &'static str,
_len: usize,
) -> Result<Self::SerializeStruct, Self::Error> {
Ok(InteroperableCompound)
}
fn serialize_struct_variant(
self,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
_len: usize,
) -> Result<Self::SerializeStructVariant, Self::Error> {
Ok(InteroperableCompound)
}
}
macro_rules! interoperable_element {
($trait:ident, $method:ident) => {
impl $trait for InteroperableCompound {
type Ok = ();
type Error = CanonicalizationError;
fn $method<T>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: Serialize + ?Sized,
{
value.serialize(InteroperableNumberValidator)
}
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(())
}
}
};
}
interoperable_element!(SerializeSeq, serialize_element);
interoperable_element!(SerializeTuple, serialize_element);
interoperable_element!(SerializeTupleStruct, serialize_field);
interoperable_element!(SerializeTupleVariant, serialize_field);
impl SerializeMap for InteroperableCompound {
type Ok = ();
type Error = CanonicalizationError;
fn serialize_key<T>(&mut self, key: &T) -> Result<(), Self::Error>
where
T: Serialize + ?Sized,
{
key.serialize(InteroperableNumberValidator)
}
fn serialize_value<T>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: Serialize + ?Sized,
{
value.serialize(InteroperableNumberValidator)
}
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(())
}
}
impl SerializeStruct for InteroperableCompound {
type Ok = ();
type Error = CanonicalizationError;
fn serialize_field<T>(&mut self, _key: &'static str, value: &T) -> Result<(), Self::Error>
where
T: Serialize + ?Sized,
{
value.serialize(InteroperableNumberValidator)
}
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(())
}
}
impl SerializeStructVariant for InteroperableCompound {
type Ok = ();
type Error = CanonicalizationError;
fn serialize_field<T>(&mut self, _key: &'static str, value: &T) -> Result<(), Self::Error>
where
T: Serialize + ?Sized,
{
value.serialize(InteroperableNumberValidator)
}
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn canonical_json_rejects_unsafe_nested_integer() {
let error = canonical_json(&json!({
"safe": 1,
"nested": { "array": [0, 9_007_199_254_740_992_u64] }
}))
.unwrap_err();
assert!(error.contains("interoperable safe integer range"));
}
#[test]
fn canonical_sha256_is_order_independent() {
let left = json!({"z": 1, "a": {"y": 2, "x": 3}});
let right = json!({"a": {"x": 3, "y": 2}, "z": 1});
assert_eq!(
canonical_sha256(&left).unwrap(),
canonical_sha256(&right).unwrap()
);
}
}