trustchain_http/
attestation_encryption_utils.rs

1use std::collections::HashMap;
2
3use josekit::jwe::ECDH_ES;
4use josekit::jwk::Jwk;
5use josekit::jws::{JwsHeader, ES256K};
6use josekit::jwt::{self, JwtPayload};
7use serde_json::Value;
8use ssi::did::{Document, VerificationMethod};
9use ssi::jwk::JWK;
10
11use crate::attestation_utils::TrustchainCRError;
12
13pub struct Entity {}
14
15impl SignEncrypt for Entity {}
16
17impl DecryptVerify for Entity {}
18
19/// Interface for signing and then encrypting data.
20pub trait SignEncrypt {
21    /// Cryptographically signs a payload with a secret key.
22    fn sign(&self, payload: &JwtPayload, secret_key: &Jwk) -> Result<String, TrustchainCRError> {
23        let mut header = JwsHeader::new();
24        header.set_token_type("JWT");
25        let signer = ES256K.signer_from_jwk(secret_key)?;
26        let signed_jwt = jwt::encode_with_signer(payload, &header, &signer)?;
27        Ok(signed_jwt)
28    }
29    /// `JWTPayload` is a wrapped [`Map`](https://docs.rs/serde_json/1.0.79/serde_json/struct.Map.html)
30    /// of claims.
31    /// Cryptographically encrypts a payload with a public key.
32    fn encrypt(&self, payload: &JwtPayload, public_key: &Jwk) -> Result<String, TrustchainCRError> {
33        let mut header = josekit::jwe::JweHeader::new();
34        header.set_token_type("JWT");
35        header.set_content_encryption("A128CBC-HS256");
36        header.set_content_encryption("A256GCM");
37
38        let encrypter = ECDH_ES.encrypter_from_jwk(public_key)?;
39        let encrypted_jwt = jwt::encode_with_encrypter(payload, &header, &encrypter)?;
40        Ok(encrypted_jwt)
41    }
42    /// Wrapper function for signing and encrypting a payload.
43    fn sign_and_encrypt_claim(
44        &self,
45        payload: &JwtPayload,
46        secret_key: &Jwk,
47        public_key: &Jwk,
48    ) -> Result<String, TrustchainCRError> {
49        let signed_payload = self.sign(payload, secret_key)?;
50        let mut claims = JwtPayload::new();
51        claims.set_claim("claim", Some(Value::from(signed_payload)))?;
52        self.encrypt(&claims, public_key)
53    }
54}
55/// Interface for decrypting and then verifying data.
56pub trait DecryptVerify {
57    /// Decrypts a payload with a secret key.
58    fn decrypt(&self, value: &Value, secret_key: &Jwk) -> Result<JwtPayload, TrustchainCRError> {
59        let decrypter = ECDH_ES.decrypter_from_jwk(secret_key)?;
60        let (payload, _) = jwt::decode_with_decrypter(
61            value
62                .as_str()
63                .ok_or(TrustchainCRError::FailedToConvertToStr(value.clone()))?,
64            &decrypter,
65        )?;
66        Ok(payload)
67    }
68    /// Wrapper function that combines decrypting a payload with a secret key and then verifying it with a public key.
69    fn decrypt_and_verify(
70        &self,
71        input: String,
72        secret_key: &Jwk,
73        public_key: &Jwk,
74    ) -> Result<JwtPayload, TrustchainCRError> {
75        let decrypter = ECDH_ES.decrypter_from_jwk(secret_key)?;
76        let (payload, _) = jwt::decode_with_decrypter(input, &decrypter)?;
77
78        let verifier = ES256K.verifier_from_jwk(public_key)?;
79        let claim = payload
80            .claim("claim")
81            .ok_or(TrustchainCRError::ClaimNotFound)?;
82        let (payload, _) = jwt::decode_with_verifier(
83            claim
84                .as_str()
85                .ok_or(TrustchainCRError::FailedToConvertToStr(claim.clone()))?,
86            &verifier,
87        )?;
88        Ok(payload)
89    }
90}
91
92/// Converts key from josekit Jwk into ssi JWK
93pub fn josekit_to_ssi_jwk(key: &Jwk) -> Result<JWK, serde_json::Error> {
94    let key_as_str: &str = &serde_json::to_string(&key)?;
95    let ssi_key: JWK = serde_json::from_str(key_as_str)?;
96    Ok(ssi_key)
97}
98/// Converts key from ssi JWK into josekit Jwk
99pub fn ssi_to_josekit_jwk(key: &JWK) -> Result<Jwk, serde_json::Error> {
100    let key_as_str: &str = &serde_json::to_string(&key)?;
101    let ssi_key: Jwk = serde_json::from_str(key_as_str)?;
102    Ok(ssi_key)
103}
104
105/// Extracts public keys contained in DID document
106pub fn extract_key_ids_and_jwk(
107    document: &Document,
108) -> Result<HashMap<String, Jwk>, TrustchainCRError> {
109    let mut my_map = HashMap::<String, Jwk>::new();
110    if let Some(vms) = &document.verification_method {
111        // TODO: leave the commented code
112        // vms.iter().for_each(|vm| match vm {
113        //     VerificationMethod::Map(vm_map) => {
114        //         let id = vm_map.id;
115        //         let key = vm_map.get_jwk().unwrap();
116        //         let key_jose = ssi_to_josekit_jwk(&key).unwrap();
117        //         my_map.insert(id, key_jose);
118        //     }
119        //     _ => (),
120        // });
121        // TODO: consider rewriting functional with filter, partition, fold over returned error
122        // variants.
123        for vm in vms {
124            if let VerificationMethod::Map(vm_map) = vm {
125                let key = vm_map
126                    .get_jwk()
127                    .map_err(|_| TrustchainCRError::MissingJWK)?;
128                let id = key
129                    .thumbprint()
130                    .map_err(|_| TrustchainCRError::MissingJWK)?;
131                let key_jose = ssi_to_josekit_jwk(&key).map_err(TrustchainCRError::Serde)?;
132                my_map.insert(id, key_jose);
133            }
134        }
135    }
136    Ok(my_map)
137}
138
139#[cfg(test)]
140mod tests {
141    use super::*;
142    use crate::data::{TEST_CANDIDATE_DDID_DOCUMENT, TEST_SIGNING_KEY_1, TEST_SIGNING_KEY_2};
143    #[test]
144    fn test_sign_encrypt_and_decrypt_verify() {
145        let entity = Entity {};
146        let mut payload = JwtPayload::new();
147        payload
148            .set_claim("test", Some(Value::from("This is a test claim.")))
149            .unwrap();
150        // encrypt and sign payload
151        let secret_key_1: Jwk = serde_json::from_str(TEST_SIGNING_KEY_1).unwrap();
152        let secret_key_2: Jwk = serde_json::from_str(TEST_SIGNING_KEY_2).unwrap();
153        let public_key_1 = secret_key_1.to_public_key().unwrap();
154        let public_key_2 = secret_key_2.to_public_key().unwrap();
155        let signed_encrypted_payload = entity
156            .sign_and_encrypt_claim(&payload, &secret_key_1, &public_key_2)
157            .unwrap();
158        // decrypt and verify payload
159        let decrypted_verified_payload = entity
160            .decrypt_and_verify(signed_encrypted_payload, &secret_key_2, &public_key_1)
161            .unwrap();
162        assert_eq!(
163            decrypted_verified_payload
164                .claim("test")
165                .unwrap()
166                .as_str()
167                .unwrap(),
168            "This is a test claim."
169        );
170    }
171
172    #[test]
173    fn test_extract_key_ids_and_jwk() {
174        let document: Document = serde_json::from_str(TEST_CANDIDATE_DDID_DOCUMENT).unwrap();
175        let key_ids_and_jwk = extract_key_ids_and_jwk(&document).unwrap();
176        assert_eq!(key_ids_and_jwk.len(), 2);
177    }
178}