Skip to main content

valence_crypto_utils/
request.rs

1use alloc::vec::Vec;
2use serde_json::Value;
3use sha2::{Digest as _, Sha256};
4
5use crate::{Ecdsa, Signer};
6
7impl Signer {
8    /// Signs a JSON using rfc-8785 canonical format.
9    pub fn sign_json(&self, json: &[u8]) -> anyhow::Result<Vec<u8>> {
10        let json: Value = serde_json::from_slice(json)?;
11        let canonical = serde_json_canonicalizer::to_string(&json)?;
12        let digest = Sha256::digest(canonical.as_bytes());
13
14        self.sign(&digest)
15    }
16
17    /// Signs a JSON using rfc-8785 canonical format prepending the provided id.
18    pub fn sign_json_with_id(&self, id: &[u8], json: &[u8]) -> anyhow::Result<Vec<u8>> {
19        let json: Value = serde_json::from_slice(json)?;
20        let canonical = serde_json_canonicalizer::to_string(&json)?;
21
22        let mut hasher = Sha256::new();
23
24        hasher.update(id);
25        hasher.update(canonical);
26
27        let digest = hasher.finalize();
28
29        self.sign(&digest)
30    }
31}
32
33impl Ecdsa {
34    /// Recovers the public identity from a signature for the provided JSON.
35    pub fn recover_from_json(sig: &[u8], json: &[u8]) -> anyhow::Result<Vec<u8>> {
36        let json: Value = serde_json::from_slice(json)?;
37        let canonical = serde_json_canonicalizer::to_string(&json)?;
38        let digest = Sha256::digest(canonical.as_bytes());
39
40        Self::recover(sig, &digest)
41    }
42
43    /// Recovers the public identity from a signature for the provided JSON.
44    pub fn recover_from_json_with_id(
45        sig: &[u8],
46        id: &[u8],
47        json: &[u8],
48    ) -> anyhow::Result<Vec<u8>> {
49        let json: Value = serde_json::from_slice(json)?;
50        let canonical = serde_json_canonicalizer::to_string(&json)?;
51
52        let mut hasher = Sha256::new();
53
54        hasher.update(id);
55        hasher.update(canonical);
56
57        let digest = hasher.finalize();
58
59        Self::recover(sig, &digest)
60    }
61}
62
63#[test]
64fn recover_from_request_works() {
65    let secret = "b0cb16ba60d3b770eb5e001efaa8f5b94270f4e8e858f673f5e896db11d21698";
66    let request = serde_json::json!({
67        "b": "foo",
68        "a": 42,
69    });
70    let requests = [
71        serde_json::to_string(&request).unwrap(),
72        serde_json::to_string_pretty(&request).unwrap(),
73        r#"{"b":          "foo", "a": 42   }"#.into(),
74    ];
75
76    let secret = const_hex::decode(secret).unwrap();
77    let signer = crate::Signer::from_secret(&secret).unwrap();
78    let public = signer.to_public();
79
80    for r in requests {
81        let signature = signer.sign_json(r.as_bytes()).unwrap();
82        let recovered = Ecdsa::recover_from_json(&signature, r.as_bytes()).unwrap();
83
84        assert_eq!(public, recovered);
85    }
86}
87
88#[test]
89fn recover_from_request_with_id_works() {
90    let secret = "b0cb16ba60d3b770eb5e001efaa8f5b94270f4e8e858f673f5e896db11d21698";
91    let request = serde_json::json!({
92        "b": "foo",
93        "a": 42,
94    });
95    let requests = [
96        serde_json::to_string(&request).unwrap(),
97        serde_json::to_string_pretty(&request).unwrap(),
98        r#"{"b":          "foo", "a": 42   }"#.into(),
99    ];
100
101    let secret = const_hex::decode(secret).unwrap();
102    let signer = crate::Signer::from_secret(&secret).unwrap();
103    let public = signer.to_public();
104
105    for r in requests {
106        let id = b"id";
107        let signature = signer.sign_json_with_id(id, r.as_bytes()).unwrap();
108        let recovered = Ecdsa::recover_from_json_with_id(&signature, id, r.as_bytes()).unwrap();
109
110        assert_eq!(public, recovered);
111    }
112}