cyfs_ecies/
lib.rs

1use secp256k1::{util::FULL_PUBLIC_KEY_SIZE, Error as SecpError, PublicKey, SecretKey};
2
3pub mod utils;
4
5use utils::{aes_decrypt, aes_encrypt, decapsulate, encapsulate, generate_keypair};
6
7pub fn encrypt(receiver_pub: &[u8], msg: &[u8]) -> Result<Vec<u8>, SecpError> {
8    let receiver_pk = PublicKey::parse_slice(receiver_pub, None)?;
9    let (ephemeral_sk, ephemeral_pk) = generate_keypair();
10
11    let aes_key = encapsulate(&ephemeral_sk, &receiver_pk);
12    let encrypted = aes_encrypt(&aes_key, msg).ok_or(SecpError::InvalidMessage)?;
13
14    let mut cipher_text = Vec::with_capacity(FULL_PUBLIC_KEY_SIZE + encrypted.len());
15    cipher_text.extend(ephemeral_pk.serialize().iter());
16    cipher_text.extend(encrypted);
17
18    Ok(cipher_text)
19}
20
21pub fn decrypt(receiver_sec: &[u8], msg: &[u8]) -> Result<Vec<u8>, SecpError> {
22    let receiver_sk = SecretKey::parse_slice(receiver_sec)?;
23
24    if msg.len() < FULL_PUBLIC_KEY_SIZE {
25        return Err(SecpError::InvalidMessage);
26    }
27
28    let ephemeral_pk = PublicKey::parse_slice(&msg[..FULL_PUBLIC_KEY_SIZE], None)?;
29    let encrypted = &msg[FULL_PUBLIC_KEY_SIZE..];
30
31    let aes_key = decapsulate(&ephemeral_pk, &receiver_sk);
32
33    aes_decrypt(&aes_key, encrypted).ok_or(SecpError::InvalidMessage)
34}
35
36#[cfg(test)]
37mod tests {
38    use super::*;
39    use hex::encode;
40    use utils::{decode_hex, generate_keypair};
41
42    const PYTHON_BACKEND: &str = "https://eciespy.herokuapp.com/";
43    const MSG: &str = "helloworld";
44
45    const BIG_MSG_SIZE: usize = 100 * 1024 * 1024;
46    const BIG_MSG: [u8; BIG_MSG_SIZE] = [1u8; BIG_MSG_SIZE]; // 100 MB
47
48    fn test_enc_dec(sk: &[u8], pk: &[u8]) {
49        let msg = MSG.as_bytes();
50        assert_eq!(msg, decrypt(sk, &encrypt(pk, msg).unwrap()).unwrap().as_slice());
51
52        let msg = &BIG_MSG;
53        assert_eq!(msg.to_vec(), decrypt(sk, &encrypt(pk, msg).unwrap()).unwrap());
54    }
55
56    #[test]
57    fn attempts_to_decrypt_with_another_key() {
58        let (_, pk1) = generate_keypair();
59
60        let (sk2, _) = generate_keypair();
61
62        assert_eq!(
63            decrypt(
64                &sk2.serialize(),
65                encrypt(&pk1.serialize_compressed(), b"text").unwrap().as_slice()
66            ),
67            Err(SecpError::InvalidMessage)
68        );
69    }
70
71    #[test]
72    fn attempts_to_decrypt_incorrect_message() {
73        let (sk, _) = generate_keypair();
74
75        assert_eq!(decrypt(&sk.serialize(), &[]), Err(SecpError::InvalidMessage));
76
77        assert_eq!(decrypt(&sk.serialize(), &[0u8; 65]), Err(SecpError::InvalidPublicKey));
78    }
79
80    #[test]
81    fn attempts_to_encrypt_with_invalid_key() {
82        assert_eq!(encrypt(&[0u8; 33], b"text"), Err(SecpError::InvalidPublicKey));
83    }
84
85    #[test]
86    fn test_compressed_public() {
87        let (sk, pk) = generate_keypair();
88        let (sk, pk) = (&sk.serialize(), &pk.serialize_compressed());
89        test_enc_dec(sk, pk);
90    }
91
92    #[test]
93    fn test_uncompressed_public() {
94        let (sk, pk) = generate_keypair();
95        let (sk, pk) = (&sk.serialize(), &pk.serialize());
96        test_enc_dec(sk, pk);
97    }
98
99    #[test]
100    fn test_against_python() {
101        use futures_util::FutureExt;
102        use tokio::runtime::Runtime;
103
104        let mut rt = Runtime::new().unwrap();
105
106        let (sk, pk) = generate_keypair();
107
108        let sk_hex = encode(&sk.serialize().to_vec());
109        let uncompressed_pk = &pk.serialize();
110        let pk_hex = encode(uncompressed_pk.to_vec());
111
112        let client = reqwest::Client::new();
113        let params = [("data", MSG), ("pub", pk_hex.as_str())];
114        let res = rt
115            .block_on(
116                client
117                    .post(PYTHON_BACKEND)
118                    .form(&params)
119                    .send()
120                    .then(|r| r.unwrap().text()),
121            )
122            .unwrap();
123
124        let server_encrypted = decode_hex(&res);
125        let local_decrypted = decrypt(&sk.serialize(), server_encrypted.as_slice()).unwrap();
126        assert_eq!(local_decrypted, MSG.as_bytes());
127
128        let local_encrypted = encrypt(uncompressed_pk, MSG.as_bytes()).unwrap();
129        let params = [("data", encode(local_encrypted)), ("prv", sk_hex)];
130
131        let res = rt
132            .block_on(
133                client
134                    .post(PYTHON_BACKEND)
135                    .form(&params)
136                    .send()
137                    .then(|r| r.unwrap().text()),
138            )
139            .unwrap();
140
141        assert_eq!(res, MSG);
142    }
143}