ecies_ed25519/
lib.rs

1//! ECIES-ed25519: An Integrated Encryption Scheme on Twisted Edwards Curve25519.
2//!
3
4//! ECIES can be used to encrypt data using a public key such that it can only be decrypted
5//! by the holder of the corresponding private key. It is based on [curve25519-dalek](https://docs.rs/curve25519-dalek).
6//!
7//! There are two different backends for HKDF-SHA256 / AES-GCM operations:
8//!
9//!   - The `pure_rust` backend (default). It uses a collection of pure-rust implementations of SHA2, HKDF, AES, and AEAD.
10//!
11//!   - The `ring` backend uses [ring](https://briansmith.org/rustdoc/ring/). It uses rock solid primitives based on BoringSSL,
12//!     but cannot run on all platforms. For example it won't work in web assembly. To enable it add the following to your Cargo.toml:
13//!
14//!     `ecies-ed25519 = { version = "0.3", features = ["ring"] }`
15//!
16//! ## Example Usage
17//! ```rust
18//! let mut csprng = rand::thread_rng();
19//! let (secret, public) = ecies_ed25519::generate_keypair(&mut csprng);
20//!
21//! let message = "I 💖🔒";
22//!
23//! // Encrypt the message with the public key such that only the holder of the secret key can decrypt.
24//! let encrypted = ecies_ed25519::encrypt(&public, message.as_bytes(), &mut csprng).unwrap();
25//!
26//! // Decrypt the message with the secret key
27//! let decrypted = ecies_ed25519::decrypt(&secret, &encrypted);
28//!```
29//!
30//! ## `serde` support
31//!
32//! The `serde` feature is provided for serializing / deserializing private and public keys.
33//!
34
35use curve25519_dalek::scalar::Scalar;
36use rand::{CryptoRng, RngCore};
37
38mod keys;
39pub use keys::*;
40
41#[cfg(feature = "ring")]
42mod ring_backend;
43
44#[cfg(feature = "ring")]
45use ring_backend::*;
46
47#[cfg(feature = "pure_rust")]
48mod pure_rust_backend;
49
50#[cfg(feature = "pure_rust")]
51use pure_rust_backend::*;
52
53#[cfg(not(any(feature = "ring", feature = "pure_rust")))]
54compile_error!(
55    "ecies-rd25519: Either feature 'ring' or 'pure_rust' must be enabled for this crate."
56);
57
58#[cfg(all(feature = "ring", feature = "pure_rust"))]
59compile_error!(
60    "ecies-rd25519: Feature 'ring' and 'pure_rust' cannot both be enabled. Please choose one."
61);
62
63const HKDF_INFO: &[u8; 13] = b"ecies-ed25519";
64
65const AES_IV_LENGTH: usize = 12;
66
67type AesKey = [u8; 32];
68type SharedSecret = [u8; 32];
69
70/// Generate a keypair, ready for use in ECIES
71pub fn generate_keypair<R: CryptoRng + RngCore>(rng: &mut R) -> (SecretKey, PublicKey) {
72    let secret = SecretKey::generate(rng);
73    let public = PublicKey::from_secret(&secret);
74    (secret, public)
75}
76
77/// Encrypt a message using ECIES, it can only be decrypted by the receiver's SecretKey.
78pub fn encrypt<R: CryptoRng + RngCore>(
79    receiver_pub: &PublicKey,
80    msg: &[u8],
81    rng: &mut R,
82) -> Result<Vec<u8>, Error> {
83    let (ephemeral_sk, ephemeral_pk) = generate_keypair(rng);
84
85    let aes_key = encapsulate(&ephemeral_sk, &receiver_pub);
86    let encrypted = aes_encrypt(&aes_key, msg, rng)?;
87
88    let mut cipher_text = Vec::with_capacity(PUBLIC_KEY_LENGTH + encrypted.len());
89    cipher_text.extend(ephemeral_pk.to_bytes().iter());
90    cipher_text.extend(encrypted);
91
92    Ok(cipher_text)
93}
94
95/// Decrypt a ECIES encrypted ciphertext using the receiver's SecretKey.
96pub fn decrypt(receiver_sec: &SecretKey, ciphertext: &[u8]) -> Result<Vec<u8>, Error> {
97    if ciphertext.len() <= PUBLIC_KEY_LENGTH {
98        return Err(Error::DecryptionFailedCiphertextShort);
99    }
100
101    let ephemeral_pk = PublicKey::from_bytes(&ciphertext[..PUBLIC_KEY_LENGTH])?;
102    let encrypted = &ciphertext[PUBLIC_KEY_LENGTH..];
103    let aes_key = decapsulate(&receiver_sec, &ephemeral_pk);
104
105    let decrypted = aes_decrypt(&aes_key, encrypted).map_err(|_| Error::DecryptionFailed)?;
106
107    Ok(decrypted)
108}
109
110fn generate_shared(secret: &SecretKey, public: &PublicKey) -> SharedSecret {
111    let public = public.to_point();
112    let secret = Scalar::from_bits(secret.to_bytes());
113    let shared_point = public * secret;
114    let shared_point_compressed = shared_point.compress();
115
116    let output = shared_point_compressed.as_bytes().to_owned();
117
118    output
119}
120
121fn encapsulate(emphemeral_sk: &SecretKey, peer_pk: &PublicKey) -> AesKey {
122    let shared_point = generate_shared(emphemeral_sk, peer_pk);
123
124    let emphemeral_pk = PublicKey::from_secret(emphemeral_sk);
125
126    let mut master = [0u8; 32 * 2];
127    master[..32].clone_from_slice(emphemeral_pk.0.as_bytes());
128    master[32..].clone_from_slice(&shared_point);
129
130    hkdf_sha256(&master)
131}
132
133fn decapsulate(sk: &SecretKey, emphemeral_pk: &PublicKey) -> AesKey {
134    let shared_point = generate_shared(sk, emphemeral_pk);
135
136    let mut master = [0u8; 32 * 2];
137    master[..32].clone_from_slice(emphemeral_pk.0.as_bytes());
138    master[32..].clone_from_slice(&shared_point);
139
140    hkdf_sha256(&master)
141}
142
143/// Error types
144use thiserror::Error;
145
146#[derive(Debug, Error)]
147pub enum Error {
148    /// Encryption failed
149    #[error("ecies-rd25519: encryption failed")]
150    EncryptionFailed,
151
152    /// Encryption failed - RNG error
153    #[error("ecies-rd25519: encryption failed - RNG error")]
154    EncryptionFailedRng,
155
156    /// Decryption failed
157    #[error("ecies-rd25519: decryption failed")]
158    DecryptionFailed,
159
160    /// Decryption failed - ciphertext too short
161    #[error("ecies-rd25519: decryption failed - ciphertext too short")]
162    DecryptionFailedCiphertextShort,
163
164    /// Invalid public key bytes
165    #[error("ecies-rd25519: invalid public key bytes")]
166    InvalidPublicKeyBytes,
167
168    /// Invalid secret key bytes
169    #[error("ecies-rd25519: invalid secret key bytes")]
170    InvalidSecretKeyBytes,
171}
172
173#[cfg(test)]
174pub mod tests {
175    use super::*;
176
177    use rand::thread_rng;
178    use rand::SeedableRng;
179
180    #[test]
181    fn test_shared() {
182        let (emphemeral_sk, emphemeral_pk) = generate_keypair(&mut thread_rng());
183        let (peer_sk, peer_pk) = generate_keypair(&mut thread_rng());
184
185        assert_eq!(
186            generate_shared(&emphemeral_sk, &peer_pk),
187            generate_shared(&peer_sk, &emphemeral_pk)
188        );
189
190        // Make sure it fails when wrong keys used
191        assert_ne!(
192            generate_shared(&emphemeral_sk, &emphemeral_pk),
193            generate_shared(&peer_sk, &peer_pk)
194        )
195    }
196
197    #[test]
198    fn test_encapsulation() {
199        let (emphemeral_sk, emphemeral_pk) = generate_keypair(&mut thread_rng());
200        let (peer_sk, peer_pk) = generate_keypair(&mut thread_rng());
201
202        assert_eq!(
203            encapsulate(&emphemeral_sk, &peer_pk),
204            decapsulate(&peer_sk, &emphemeral_pk)
205        )
206    }
207
208    #[test]
209    fn test_aes() {
210        let mut test_rng = rand::rngs::StdRng::from_seed([0u8; 32]);
211        let mut key = [0u8; 32];
212        test_rng.fill_bytes(&mut key);
213
214        let plaintext = b"ABC";
215        let encrypted = aes_encrypt(&key, plaintext, &mut test_rng).unwrap();
216        let decrypted = aes_decrypt(&key, &encrypted).unwrap();
217
218        assert_eq!(plaintext, decrypted.as_slice());
219
220        // Test bad ciphertext
221        assert!(aes_decrypt(&key, &[0u8; 16]).is_err());
222
223        // Test bad secret key
224        let bad_secret = SecretKey::generate(&mut thread_rng());
225        assert!(aes_decrypt(&bad_secret.as_bytes(), &encrypted).is_err());
226    }
227
228    #[test]
229    fn test_ecies_ed25519() {
230        let (peer_sk, peer_pk) = generate_keypair(&mut thread_rng());
231
232        let plaintext = b"ABOLISH ICE";
233
234        let encrypted = encrypt(&peer_pk, plaintext, &mut thread_rng()).unwrap();
235        let decrypted = decrypt(&peer_sk, &encrypted).unwrap();
236
237        assert_eq!(plaintext, decrypted.as_slice());
238
239        // Test bad ciphertext
240        assert!(decrypt(&peer_sk, &[0u8; 16]).is_err());
241
242        // Test that it fails when using a bad secret key
243        let bad_secret = SecretKey::generate(&mut thread_rng());
244        assert!(decrypt(&bad_secret, &encrypted).is_err());
245    }
246
247    #[test]
248    fn test_hkdf_sha256_interop() {
249        let known_key: Vec<u8> = vec![
250            204, 68, 78, 7, 8, 70, 53, 136, 56, 115, 129, 183, 226, 82, 147, 253, 62, 59, 170, 188,
251            131, 119, 31, 21, 249, 255, 19, 103, 230, 24, 213, 204,
252        ];
253        let key = hkdf_sha256(b"ABC123");
254
255        assert_eq!(key.to_vec(), known_key);
256    }
257
258    #[test]
259    fn test_aes_interop() {
260        let mut test_rng = rand::rngs::StdRng::from_seed([0u8; 32]);
261
262        let mut key = [0u8; 32];
263        test_rng.fill_bytes(&mut key);
264
265        let plaintext = b"ABC";
266
267        let known_encrypted: Vec<u8> = vec![
268            218, 65, 89, 124, 81, 87, 72, 141, 119, 36, 224, 63, 149, 218, 64, 106, 159, 178, 238,
269            212, 36, 223, 93, 107, 19, 211, 62, 75, 195, 46, 177,
270        ];
271
272        let decrypted = aes_decrypt(&key, &known_encrypted).unwrap();
273        assert_eq!(plaintext, decrypted.as_slice());
274    }
275
276    #[test]
277    fn test_ecies_ed25519_interop() {
278        let mut test_rng = rand::rngs::StdRng::from_seed([0u8; 32]);
279
280        let (peer_sk, _peer_pk) = generate_keypair(&mut test_rng);
281
282        let plaintext = b"ABC";
283        let known_encrypted: Vec<u8> = vec![
284            235, 249, 207, 231, 91, 38, 106, 202, 22, 34, 114, 191, 107, 122, 99, 157, 43, 210, 46,
285            229, 219, 208, 111, 176, 98, 154, 42, 250, 114, 233, 68, 8, 159, 7, 231, 190, 85, 81,
286            56, 122, 152, 186, 151, 124, 246, 147, 163, 153, 29, 85, 248, 238, 194, 15, 180, 98,
287            163, 36, 49, 191, 133, 242, 186,
288        ];
289
290        let decrypted = decrypt(&peer_sk, &known_encrypted).unwrap();
291
292        assert_eq!(plaintext, decrypted.as_slice());
293    }
294
295    #[test]
296    fn test_public_key_extract() {
297        let mut test_rng = rand::rngs::StdRng::from_seed([0u8; 32]);
298
299        let secret = SecretKey::generate(&mut test_rng);
300        let public = PublicKey::from_secret(&secret);
301
302        PublicKey::from_bytes(public.as_bytes()).unwrap();
303
304        // Test bad bytes
305        assert!(PublicKey::from_bytes(&[0u8; 16]).is_err());
306        assert!(SecretKey::from_bytes(&[0u8; 16]).is_err());
307    }
308
309    #[cfg(feature = "serde")]
310    #[test]
311    fn test_hex() {
312        use hex::{FromHex, ToHex};
313
314        let mut test_rng = rand::rngs::StdRng::from_seed([0u8; 32]);
315        let (secret, public) = generate_keypair(&mut test_rng);
316
317        // lower
318        let serialized_secret: String = secret.encode_hex();
319        let serialized_public: String = public.encode_hex();
320
321        let deserialized_secret = SecretKey::from_hex(serialized_secret).unwrap();
322        let deserialized_public = PublicKey::from_hex(&serialized_public).unwrap();
323
324        assert_eq!(secret.to_bytes(), deserialized_secret.to_bytes());
325        assert_eq!(public.as_bytes(), deserialized_public.as_bytes());
326
327        // UPPER
328        let serialized_secret: String = secret.encode_hex_upper();
329        let serialized_public: String = public.encode_hex_upper();
330
331        let deserialized_secret = SecretKey::from_hex(serialized_secret).unwrap();
332        let deserialized_public = PublicKey::from_hex(serialized_public).unwrap();
333
334        assert_eq!(secret.to_bytes(), deserialized_secret.to_bytes());
335        assert_eq!(public.as_bytes(), deserialized_public.as_bytes());
336    }
337
338    #[cfg(feature = "serde")]
339    #[test]
340    fn test_serde_json() {
341        let mut test_rng = rand::rngs::StdRng::from_seed([0u8; 32]);
342        let (secret, public) = generate_keypair(&mut test_rng);
343
344        // String
345        let serialized_secret = serde_json::to_string(&secret).unwrap();
346        let serialized_public = serde_json::to_string(&public).unwrap();
347
348        let deserialized_secret: SecretKey = serde_json::from_str(&serialized_secret).unwrap();
349        let deserialized_public: PublicKey = serde_json::from_str(&serialized_public).unwrap();
350
351        assert_eq!(secret.to_bytes(), deserialized_secret.to_bytes());
352        assert_eq!(public.as_bytes(), deserialized_public.as_bytes());
353
354        // Stringy bytes
355        let deserialized_secret: SecretKey =
356            serde_json::from_slice(serialized_secret.as_bytes()).unwrap();
357        let deserialized_public: PublicKey =
358            serde_json::from_slice(serialized_public.as_bytes()).unwrap();
359
360        assert_eq!(secret.as_bytes(), deserialized_secret.as_bytes());
361        assert_eq!(public.as_bytes(), deserialized_public.as_bytes());
362
363        // Bytes
364        let serialized_secret = serde_json::to_vec(&secret).unwrap();
365        let serialized_public = serde_json::to_vec(&public).unwrap();
366
367        let deserialized_secret: SecretKey = serde_json::from_slice(&serialized_secret).unwrap();
368        let deserialized_public: PublicKey = serde_json::from_slice(&serialized_public).unwrap();
369
370        assert_eq!(secret.as_bytes(), deserialized_secret.as_bytes());
371        assert_eq!(public.as_bytes(), deserialized_public.as_bytes());
372
373        // Test errors - mangle some bits and confirm it doesn't work:
374        let mut serialized_public = serde_json::to_vec(&public).unwrap();
375        serialized_public[0] = 50;
376        assert!(serde_json::from_slice::<PublicKey>(&serialized_public).is_err());
377
378        let mut serialized_public = serde_json::to_vec(&public).unwrap();
379        serialized_public.push(48);
380        serialized_public.push(49);
381        assert!(serde_json::from_slice::<PublicKey>(&serialized_public).is_err());
382    }
383
384    #[cfg(feature = "serde")]
385    #[test]
386    fn test_serde_cbor() {
387        let mut test_rng = rand::rngs::StdRng::from_seed([0u8; 32]);
388        let (secret, public) = generate_keypair(&mut test_rng);
389
390        let serialized_secret = serde_cbor::to_vec(&secret).unwrap();
391        let serialized_public = serde_cbor::to_vec(&public).unwrap();
392
393        let deserialized_secret: SecretKey = serde_cbor::from_slice(&serialized_secret).unwrap();
394        let deserialized_public: PublicKey = serde_cbor::from_slice(&serialized_public).unwrap();
395
396        assert_eq!(secret.as_bytes(), deserialized_secret.as_bytes());
397        assert_eq!(public.as_bytes(), deserialized_public.as_bytes());
398
399        // Test errors - mangle some bits and confirm it doesn't work:
400        let mut serialized_public = serde_cbor::to_vec(&public).unwrap();
401        serialized_public[6] = 120;
402        assert!(serde_cbor::from_slice::<PublicKey>(&serialized_public).is_err());
403    }
404}