guardian_shared/
auth_request_payload.rs1use miden_protocol::crypto::hash::rpo::Rpo256;
2use miden_protocol::{Felt, Word};
3use serde::Serialize;
4use serde_json::Value;
5
6#[derive(Clone, Debug, PartialEq, Eq)]
7pub struct AuthRequestPayload {
8 digest: Word,
9}
10
11impl AuthRequestPayload {
12 pub fn empty() -> Self {
13 Self {
14 digest: Word::from([Felt::ZERO; 4]),
15 }
16 }
17
18 pub fn from_bytes(bytes: &[u8]) -> Self {
19 if bytes.is_empty() {
20 return Self::empty();
21 }
22
23 let mut payload_elements = Vec::with_capacity(bytes.len().div_ceil(8));
24 for chunk in bytes.chunks(8) {
25 let mut chunk_bytes = [0u8; 8];
26 chunk_bytes[..chunk.len()].copy_from_slice(chunk);
27 payload_elements.push(Felt::new(u64::from_le_bytes(chunk_bytes)));
28 }
29
30 Self {
31 digest: Rpo256::hash_elements(&payload_elements),
32 }
33 }
34
35 pub fn from_json_bytes(bytes: &[u8]) -> Result<Self, String> {
36 let value: Value =
37 serde_json::from_slice(bytes).map_err(|e| format!("Invalid JSON payload: {e}"))?;
38 Self::from_json_value(&value)
39 }
40
41 pub fn from_json_value(value: &Value) -> Result<Self, String> {
42 let canonical = canonicalize_json(value);
43 let bytes =
44 serde_json::to_vec(&canonical).map_err(|e| format!("Failed to serialize JSON: {e}"))?;
45 Ok(Self::from_bytes(&bytes))
46 }
47
48 pub fn from_json_serializable<T: Serialize>(value: &T) -> Result<Self, String> {
49 let json = serde_json::to_value(value)
50 .map_err(|e| format!("Failed to convert payload to JSON value: {e}"))?;
51 Self::from_json_value(&json)
52 }
53
54 pub fn from_protobuf_message<T: prost::Message>(value: &T) -> Self {
55 Self::from_bytes(&value.encode_to_vec())
56 }
57
58 pub fn as_elements(&self) -> [Felt; 4] {
59 let elements = self.digest.as_elements();
60 [elements[0], elements[1], elements[2], elements[3]]
61 }
62
63 pub fn to_word(&self) -> Word {
64 self.digest
65 }
66}
67
68fn canonicalize_json(value: &Value) -> Value {
69 match value {
70 Value::Object(map) => {
71 let mut keys = map.keys().cloned().collect::<Vec<_>>();
72 keys.sort();
73
74 let mut sorted = serde_json::Map::with_capacity(map.len());
75 for key in keys {
76 let item = map
77 .get(&key)
78 .expect("key collected from map must always exist");
79 sorted.insert(key, canonicalize_json(item));
80 }
81 Value::Object(sorted)
82 }
83 Value::Array(items) => Value::Array(items.iter().map(canonicalize_json).collect()),
84 _ => value.clone(),
85 }
86}
87
88#[cfg(test)]
89mod tests {
90 use super::AuthRequestPayload;
91
92 #[test]
93 fn json_payload_hash_is_order_insensitive() {
94 let left = br#"{"b":2,"a":1}"#;
95 let right = br#"{"a":1,"b":2}"#;
96
97 let left_payload = AuthRequestPayload::from_json_bytes(left).expect("left json");
98 let right_payload = AuthRequestPayload::from_json_bytes(right).expect("right json");
99
100 assert_eq!(left_payload, right_payload);
101 }
102
103 #[test]
104 fn empty_payload_hash_is_zero_word() {
105 let payload = AuthRequestPayload::from_bytes(b"");
106 assert_eq!(payload, AuthRequestPayload::empty());
107 }
108}