car-proto 0.54.0

JSON-RPC protocol types for Common Agent Runtime client-server communication
Documentation
use serde::ser::{
    Error as SerError, SerializeMap, SerializeSeq, SerializeStruct, SerializeStructVariant,
    SerializeTuple, SerializeTupleStruct, SerializeTupleVariant,
};
use serde::{Serialize, Serializer};
use sha2::{Digest, Sha256};
use std::fmt;

/// Serialize with RFC 8785 JSON Canonicalization Scheme (JCS).
///
/// The preflight traversal rejects every integer outside the I-JSON safe range
/// recursively before `serde_jcs` renders ECMAScript-compatible JSON.
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}"))
}

/// Lowercase SHA-256 of a value's RFC 8785/JCS representation.
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()
        );
    }
}