monetize_embed/
signing.rs1use ed25519_dalek::{Signature, Verifier, VerifyingKey};
13use monetize_product::EntitlementFact;
14use serde_json::Value;
15
16#[derive(Clone, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
19pub struct Snapshot {
20 pub issued_unix_ms: u64,
22 pub facts: Vec<EntitlementFact>,
23 pub signature: Vec<u8>,
25}
26
27#[derive(Debug, thiserror::Error, PartialEq, Eq)]
28pub enum SignatureError {
29 #[error("signature is not 64 bytes")]
30 Malformed,
31 #[error("signature does not verify for tenant {0}")]
32 Fact(String),
33 #[error("snapshot envelope signature does not verify")]
34 Envelope,
35}
36
37pub fn canonical_json(value: &Value, out: &mut String) {
39 match value {
40 Value::Object(map) => {
41 let mut keys: Vec<&String> = map.keys().collect();
42 keys.sort();
43 out.push('{');
44 for (i, k) in keys.iter().enumerate() {
45 if i > 0 {
46 out.push(',');
47 }
48 out.push_str(&serde_json::to_string(k).expect("string"));
49 out.push(':');
50 canonical_json(&map[*k], out);
51 }
52 out.push('}');
53 }
54 Value::Array(items) => {
55 out.push('[');
56 for (i, v) in items.iter().enumerate() {
57 if i > 0 {
58 out.push(',');
59 }
60 canonical_json(v, out);
61 }
62 out.push(']');
63 }
64 other => out.push_str(&other.to_string()),
65 }
66}
67
68pub fn fact_message(fact: &EntitlementFact) -> Vec<u8> {
70 let mut v = serde_json::to_value(fact).expect("fact serializes");
71 v.as_object_mut().expect("fact is an object").remove("signature");
72 let mut s = String::new();
73 canonical_json(&v, &mut s);
74 s.into_bytes()
75}
76
77pub fn snapshot_message(issued_unix_ms: u64, facts: &[EntitlementFact]) -> Vec<u8> {
80 let v = serde_json::json!({ "issued_unix_ms": issued_unix_ms, "facts": facts });
81 let mut s = String::new();
82 canonical_json(&v, &mut s);
83 s.into_bytes()
84}
85
86fn check(key: &VerifyingKey, msg: &[u8], sig: &[u8]) -> Result<bool, SignatureError> {
87 let sig = Signature::from_slice(sig).map_err(|_| SignatureError::Malformed)?;
88 Ok(key.verify(msg, &sig).is_ok())
89}
90
91pub fn verify_fact(fact: &EntitlementFact, key: &VerifyingKey) -> Result<(), SignatureError> {
92 if check(key, &fact_message(fact), &fact.signature)? {
93 Ok(())
94 } else {
95 Err(SignatureError::Fact(fact.tenant.0.clone()))
96 }
97}
98
99pub fn verify_snapshot(snap: &Snapshot, key: &VerifyingKey) -> Result<(), SignatureError> {
101 if !check(key, &snapshot_message(snap.issued_unix_ms, &snap.facts), &snap.signature)? {
102 return Err(SignatureError::Envelope);
103 }
104 snap.facts.iter().try_for_each(|f| verify_fact(f, key))
105}