Skip to main content

co_didcomm/crypto/
encryptor.rs

1use std::convert::TryFrom;
2
3use super::*;
4
5/// Pluggable closure generator enum, which creates instance of crypto function
6///     based on selected algorithm types.
7/// # Attention:
8/// Immutable by design and should be instance per invocation to make sure no
9///     sensitive data is been stored in memory longer than necessary.
10/// Underlying algorithms are implemented by Rust-crypto crate family.
11///
12/// Allowed (and implemented) cryptographical algorithms (JWA).
13/// According to [spec](https://identity.foundation/didcomm-messaging/spec/#sender-authenticated-encryption)
14#[derive(Copy, Clone)]
15pub enum CryptoAlgorithm {
16    XC20P,
17    A256GCM,
18    A256CBC,
19}
20
21impl Cypher for CryptoAlgorithm {
22    /// Generates + invokes crypto of `SymmetricCypherMethod` which performs encryption.
23    /// Algorithm selected is based on struct's `CryptoAlgorithm` property.
24    fn encryptor(&self) -> SymmetricCypherMethod {
25        match self {
26            CryptoAlgorithm::XC20P => Box::new(
27                |nonce: &[u8], key: &[u8], message: &[u8], aad: &[u8]| -> Result<Vec<u8>, Error> {
28                    check_nonce(nonce, 24)?;
29                    use chacha20poly1305::{
30                        aead::{Aead, KeyInit, Payload},
31                        XChaCha20Poly1305, XNonce,
32                    };
33                    let nonce = XNonce::from_slice(nonce);
34                    let aead = XChaCha20Poly1305::new(key.into());
35                    aead.encrypt(nonce, Payload { msg: message, aad })
36                        .map_err(|e| Error::Generic(e.to_string()))
37                },
38            ),
39            CryptoAlgorithm::A256GCM => Box::new(
40                |nonce: &[u8], key: &[u8], message: &[u8], aad: &[u8]| -> Result<Vec<u8>, Error> {
41                    check_nonce(nonce, 12)?;
42                    use aes_gcm::{
43                        aead::{generic_array::GenericArray, Aead, KeyInit, Payload},
44                        Aes256Gcm,
45                    };
46                    let nonce = GenericArray::from_slice(&nonce[..12]);
47                    let aead = Aes256Gcm::new(GenericArray::from_slice(key));
48                    aead.encrypt(nonce, Payload { msg: message, aad })
49                        .map_err(|e| Error::Generic(e.to_string()))
50                },
51            ),
52            CryptoAlgorithm::A256CBC => Box::new(
53                |nonce: &[u8], key: &[u8], message: &[u8], _aad: &[u8]| -> Result<Vec<u8>, Error> {
54                    if key.len() != 32 {
55                        return Err(Error::InvalidKeySize(
56                            "expected 256 bit (32 byte) key".into(),
57                        ));
58                    }
59                    if nonce.len() != 16 {
60                        return Err(Error::InvalidKeySize("expected 16 bytes nonce".into()));
61                    }
62                    use arrayref::array_ref;
63                    use libaes::Cipher;
64                    let aead = Cipher::new_256(array_ref!(key, 0, 32));
65                    Ok(aead.cbc_encrypt(nonce, message))
66                },
67            ),
68        }
69    }
70
71    /// Generates + invokes crypto of `SymmetricCypherMethod` which performs decryption.
72    /// Algorithm selected is based on struct's `CryptoAlgorithm` property.
73    fn decrypter(&self) -> SymmetricCypherMethod {
74        match self {
75            CryptoAlgorithm::XC20P => Box::new(
76                |nonce: &[u8], key: &[u8], message: &[u8], aad: &[u8]| -> Result<Vec<u8>, Error> {
77                    check_nonce(nonce, 24)?;
78                    use chacha20poly1305::{
79                        aead::{Aead, KeyInit, Payload},
80                        XChaCha20Poly1305, XNonce,
81                    };
82                    let aead = XChaCha20Poly1305::new(key.into());
83                    let nonce = XNonce::from_slice(nonce);
84                    aead.decrypt(nonce, Payload { msg: message, aad })
85                        .map_err(|e| Error::Generic(e.to_string()))
86                },
87            ),
88            CryptoAlgorithm::A256GCM => Box::new(
89                |nonce: &[u8], key: &[u8], message: &[u8], aad: &[u8]| -> Result<Vec<u8>, Error> {
90                    check_nonce(nonce, 12)?;
91                    use aes_gcm::{
92                        aead::{generic_array::GenericArray, Aead, KeyInit, Payload},
93                        Aes256Gcm,
94                    };
95                    let nonce = GenericArray::from_slice(&nonce[..12]);
96                    let aead = Aes256Gcm::new(GenericArray::from_slice(key));
97                    aead.decrypt(nonce, Payload { msg: message, aad })
98                        .map_err(|e| Error::Generic(e.to_string()))
99                },
100            ),
101            CryptoAlgorithm::A256CBC => {
102                todo!()
103            }
104        }
105    }
106
107    /// Not implemented - no use case atm...
108    fn asymmetric_encryptor(&self) -> AsymmetricCypherMethod {
109        match self {
110            CryptoAlgorithm::XC20P => {
111                todo!()
112            }
113            CryptoAlgorithm::A256GCM => {
114                todo!()
115            }
116            CryptoAlgorithm::A256CBC => {
117                todo!()
118            }
119        }
120    }
121}
122
123impl TryFrom<&String> for CryptoAlgorithm {
124    type Error = Error;
125    fn try_from(incoming: &String) -> Result<Self, Error> {
126        match &incoming[..] {
127            "ECDH-1PU+A256KW" => Ok(Self::A256GCM),
128            "ECDH-1PU+XC20PKW" => Ok(Self::XC20P),
129            _ => Err(Error::JweParseError),
130        }
131    }
132}
133
134// inner helper function
135fn check_nonce(nonce: &[u8], expected_len: usize) -> Result<(), Error> {
136    if nonce.len() < expected_len {
137        return Err(Error::PlugCryptoFailure);
138    }
139    Ok(())
140}
141
142#[cfg(test)]
143mod batteries_tests {
144    use super::*;
145    use crate::{Jwe, Message};
146
147    #[test]
148    fn xc20p_test() -> Result<(), Error> {
149        // Arrange
150        let payload = r#"{"test":"message's body - can be anything..."}"#;
151        let m = Message::new()
152            .as_jwe(&CryptoAlgorithm::XC20P, None) // Set jwe header manually - should be preceded by key properties
153            .body(payload)?;
154        let original_header = m.jwm_header.clone();
155        let key = b"super duper key 32 bytes long!!!";
156        // Act
157        let jwe_string_result = m.encrypt(CryptoAlgorithm::XC20P.encryptor(), key);
158        assert!(&jwe_string_result.is_ok());
159        let jwe_string = jwe_string_result?;
160        let jwe: Jwe = serde_json::from_str(&jwe_string)?;
161        assert!(&jwe.tag.is_some());
162        let s = Message::decrypt(
163            jwe_string.as_bytes(),
164            CryptoAlgorithm::XC20P.decrypter(),
165            key,
166        )?;
167        let received_payload = &s.get_body()?;
168        // Assert
169        assert_eq!(s.jwm_header, original_header);
170        assert_eq!(payload, received_payload);
171        Ok(())
172    }
173
174    #[test]
175    fn a256gcm_test() -> Result<(), Error> {
176        // Arrange
177        let payload = r#"{"example":"message's body - can be anything..."}"#;
178        let m = Message::new()
179            .as_jwe(&CryptoAlgorithm::A256GCM, None) // Set jwe header manually - should be preceded by key properties
180            .body(payload)?;
181        let original_header = m.jwm_header.clone();
182        let key = b"super duper key 32 bytes long!!!";
183        // Act
184        let jwe = m.encrypt(CryptoAlgorithm::A256GCM.encryptor(), key);
185        assert!(&jwe.is_ok());
186        let s = Message::decrypt(
187            jwe.expect("failed to get JWE").as_bytes(),
188            CryptoAlgorithm::A256GCM.decrypter(),
189            key,
190        )?;
191        let received_payload = &s.get_body()?;
192        // Assert
193        assert_eq!(s.jwm_header, original_header);
194        assert_eq!(payload, received_payload);
195        Ok(())
196    }
197}