generic_ecies/
curve25519xsalsa20hmac.rs

1//! Instantiation of ECIES with the following parameters:
2//!
3//! * Curve25519 as the elliptic curve
4//! * XSalsa20 as the symmetric cipher
5//! * HMAC-SHA256 as the message authentication code
6//!
7//! ## Example of usage
8//! ```rust
9//! # let mut rng = rand_dev::DevRng::new();
10//! use generic_ecies::curve25519xsalsa20hmac as ecies;
11//! // Use EdDSA key as openssl generates it instead of Curve25519 private scalar
12//! let eddsa_private_key_bytes = b"eddsa priv key is any 32 bytes^^";
13//! let private_key = ecies::PrivateKey::from_eddsa_pkey_bytes(eddsa_private_key_bytes).unwrap();
14//! let public_key = private_key.public_key();
15//!
16//! // Encrypt
17//! let message = b"Putin is an agent of kremlin";
18//! let mut encrypted_message = public_key.encrypt(message, &mut rng).unwrap();
19//!
20//! // Decrypt
21//! let parsed_message = ecies::EncryptedMessage::from_bytes(&mut encrypted_message).unwrap();
22//! let decrypted_message = private_key.decrypt_in_place(parsed_message).unwrap();
23//! assert_eq!(decrypted_message, message);
24//! ```
25
26/// The ciphersuite for curve25519+xsalsa20+hmacsha256
27#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
28pub struct Curve25519Xsalsa20Hmacsha256;
29
30type S = Curve25519Xsalsa20Hmacsha256;
31
32impl super::Suite for S {
33    type E = generic_ec::curves::Ed25519;
34    type Mac = hmac::Hmac<sha2::Sha256>;
35    type Enc = salsa20::XSalsa20;
36    type Dec = salsa20::XSalsa20;
37}
38
39/// Private key of this suite, a scalar of Curve25519
40pub type PrivateKey = crate::PrivateKey<S>;
41/// Public key of this suite, a point on Curve25519
42pub type PublicKey = crate::PublicKey<S>;
43/// Message encrypted with this ciphersuite
44pub type EncryptedMessage<'m> = crate::EncryptedMessage<'m, S>;
45
46impl PublicKey {
47    /// Encrypt the message bytes in place; specialization for
48    /// `curve25519xsalsa20hmac`
49    ///
50    /// You can interact with the encrypted bytes through the returned
51    /// [`EncryptedMessage`], but be careful that changing them will invalidate
52    /// the mac.
53    ///
54    /// Convenient alias for [`PublicKey::stream_encrypt_in_place`]
55    pub fn encrypt_in_place<'m>(
56        &self,
57        message: &'m mut [u8],
58        rng: &mut (impl rand_core::RngCore + rand_core::CryptoRng),
59    ) -> Result<EncryptedMessage<'m>, crate::EncError> {
60        self.stream_encrypt_in_place(message, rng)
61    }
62
63    /// Encrypt the message bytes into a new buffer. Returnes the encoded bytes
64    /// of [`EncryptedMessage`]. Specialization for `curve25519xsalsa20hmac`
65    ///
66    /// Convenient alias for [`PublicKey::stream_encrypt`]
67    pub fn encrypt(
68        &self,
69        message: &[u8],
70        rng: &mut (impl rand_core::RngCore + rand_core::CryptoRng),
71    ) -> Result<Vec<u8>, crate::EncError> {
72        self.stream_encrypt(message, rng)
73    }
74}
75
76impl PrivateKey {
77    /// Decrypt the message bytes in place; specialization for
78    /// `curve25519xsalsa20hmac`
79    ///
80    /// When you have a buffer of bytes to decrypt, you first need to parse it
81    /// with `EncryptedMessage::from_bytes`, and then decrypt the structure
82    /// using this funciton. It will modify the bytes in the buffer and return a
83    /// slice to them.
84    ///
85    /// Convenient alias to [`PrivateKey::stream_decrypt_in_place`]
86    pub fn decrypt_in_place<'m>(
87        &self,
88        message: EncryptedMessage<'m>,
89    ) -> Result<&'m mut [u8], crate::DecError> {
90        self.stream_decrypt_in_place(message)
91    }
92
93    /// Decrypt the message bytes into a new buffer; specialization for
94    /// `curve25519xsalsa20hmac`
95    ///
96    /// When you have a buffer of bytes to decrypt, you first need to parse it
97    /// with `EncryptedMessage::from_bytes`, and then decrypt the structure
98    /// using this funciton. It will copy the message bytes into a new buffer
99    /// and return a [`Vec`] containing them.
100    ///
101    /// Convenient alias to [`PrivateKey::decrypt_in_place`]
102    pub fn decrypt(&self, message: &EncryptedMessage<'_>) -> Result<Vec<u8>, crate::DecError> {
103        self.stream_decrypt(message)
104    }
105}
106
107#[cfg(test)]
108crate::common::make_tests!("stream");
109#[cfg(test)]
110mod test {
111    #[test]
112    fn openssl_key() {
113        // This key is obtained from openssl by
114        // `openssl genpkey -algorithm ed25519 -out ed25519key.pem`
115        // And converted from pem to hex by
116        // `openssl pkey -in backup_key.pem -text -noout | grep -E '^priv:$' -A3 | tail -n +2 | tr -d ' :\n'`
117        let key_bytes =
118            hex::decode("eaec3fecf6d988cd8a51bbfba5d5a310d1887459f8433fa0a17fc09f34ee77a4")
119                .unwrap();
120        // Public key is obtained by `openssl pkey -in ed25519key.pem -pubout`
121        // And then converted to hex in a similar way
122        // `openssl pkey -pubin -in ed25519.pub -text -noout | grep -E '^pub:$' -A3 | tail -n +2 | tr -d ' :\n'`
123        let pubkey_bytes =
124            hex::decode("d24f3652e2100524d31ae794e781c4cd0b4f53e2bb02665b85f9c71d5e80ab69")
125                .unwrap();
126        // Would be a good idea to generate them on the fly with openssl, but it's
127        // not available in all distributions by default, for example on macos
128
129        let pubkey = super::PublicKey::from_bytes(pubkey_bytes).unwrap();
130        let key =
131            super::PrivateKey::from_eddsa_pkey_bytes(key_bytes[0..32].try_into().unwrap()).unwrap();
132        assert_eq!(key.public_key(), pubkey);
133    }
134
135    #[test]
136    fn compat() {
137        // Sadly we already use this in production to encrypt data for long-term
138        // storage, and unless something breaking is discovered, we need to be
139        // able to still decrypt the data we encrypted at the old revision of
140        // this library. (If something is discovered though, we'll have to make
141        // a breaking change and use an old compat version for decrypting, which
142        // would be extremely inconvenient)
143
144        let key_bytes =
145            hex::decode("eaec3fecf6d988cd8a51bbfba5d5a310d1887459f8433fa0a17fc09f34ee77a4")
146                .unwrap();
147        let key =
148            super::PrivateKey::from_eddsa_pkey_bytes(key_bytes[0..32].try_into().unwrap()).unwrap();
149
150        let plaintext = b"Look at this very important data, I hope nothing happens to it.";
151        let mut ciphertext = [
152            92, 192, 226, 249, 116, 180, 153, 152, 175, 76, 239, 137, 146, 108, 44, 37, 235, 153,
153            93, 214, 162, 184, 139, 81, 116, 9, 222, 126, 28, 56, 67, 115, 240, 125, 224, 21, 125,
154            74, 130, 14, 122, 74, 148, 102, 17, 64, 196, 16, 37, 118, 97, 118, 106, 77, 244, 187,
155            167, 116, 82, 230, 180, 88, 165, 33, 235, 106, 231, 254, 195, 15, 223, 88, 182, 9, 47,
156            197, 182, 25, 47, 55, 238, 79, 132, 141, 236, 27, 95, 59, 91, 226, 208, 222, 159, 8,
157            53, 105, 82, 6, 253, 206, 83, 2, 191, 72, 8, 194, 112, 92, 18, 54, 131, 20, 34, 228,
158            195, 79, 169, 25, 246, 176, 181, 143, 5, 172, 230, 96, 53,
159        ];
160        let message = super::EncryptedMessage::from_bytes(&mut ciphertext).unwrap();
161        let decrypted = key.decrypt_in_place(message).unwrap();
162        assert_eq!(plaintext, decrypted);
163    }
164}