1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
use super::{ecdh, Error, Generator, Public, Random, Secret};
use crate::{aes, digest, hmac, is_equal};
use ethereum_types::H128;
const ENC_VERSION: u8 = 0x04;
pub fn encrypt(public: &Public, auth_data: &[u8], plain: &[u8]) -> Result<Vec<u8>, Error> {
let r = Random.generate();
let z = ecdh::agree(r.secret(), public)?;
let mut key = [0u8; 32];
kdf(&z, &[0u8; 0], &mut key);
let ekey = &key[0..16];
let mkey = hmac::SigKey::sha256(&digest::sha256(&key[16..32]));
let mut msg = vec![0u8; 1 + 64 + 16 + plain.len() + 32];
msg[0] = ENC_VERSION;
{
let result_msg = &mut msg[1..];
result_msg[0..64].copy_from_slice(r.public().as_bytes());
let iv = H128::random();
result_msg[64..80].copy_from_slice(iv.as_bytes());
{
let cipher = &mut result_msg[(64 + 16)..(64 + 16 + plain.len())];
aes::encrypt_128_ctr(ekey, iv.as_bytes(), plain, cipher)?;
}
let mut hmac = hmac::Signer::with(&mkey);
{
let cipher_iv = &result_msg[64..(64 + 16 + plain.len())];
hmac.update(cipher_iv);
}
hmac.update(auth_data);
let sig = hmac.sign();
result_msg[(64 + 16 + plain.len())..].copy_from_slice(&sig);
}
Ok(msg)
}
pub fn decrypt(secret: &Secret, auth_data: &[u8], encrypted: &[u8]) -> Result<Vec<u8>, Error> {
const META_LEN: usize = 1 + 64 + 16 + 32;
let enc_version = encrypted[0];
if encrypted.len() < META_LEN || enc_version < 2 || enc_version > 4 {
return Err(Error::InvalidMessage);
}
let e = &encrypted[1..];
let p = Public::from_slice(&e[0..64]);
let z = ecdh::agree(secret, &p)?;
let mut key = [0u8; 32];
kdf(&z, &[0u8; 0], &mut key);
let ekey = &key[0..16];
let mkey = hmac::SigKey::sha256(&digest::sha256(&key[16..32]));
let cipher_text_len = encrypted.len() - META_LEN;
let cipher_with_iv = &e[64..(64 + 16 + cipher_text_len)];
let cipher_iv = &cipher_with_iv[0..16];
let cipher_no_iv = &cipher_with_iv[16..];
let msg_mac = &e[(64 + 16 + cipher_text_len)..];
let mut hmac = hmac::Signer::with(&mkey);
hmac.update(cipher_with_iv);
hmac.update(auth_data);
let mac = hmac.sign();
if !is_equal(&mac.as_ref()[..], msg_mac) {
return Err(Error::InvalidMessage);
}
let mut msg = vec![0u8; cipher_text_len];
aes::decrypt_128_ctr(ekey, cipher_iv, cipher_no_iv, &mut msg[..])?;
Ok(msg)
}
fn kdf(secret: &Secret, s1: &[u8], dest: &mut [u8]) {
let mut ctr = 1u32;
let mut written = 0usize;
while written < dest.len() {
let mut hasher = digest::Hasher::sha256();
let ctrs = [(ctr >> 24) as u8, (ctr >> 16) as u8, (ctr >> 8) as u8, ctr as u8];
hasher.update(&ctrs);
hasher.update(secret.as_bytes());
hasher.update(s1);
let d = hasher.finish();
&mut dest[written..(written + 32)].copy_from_slice(&d);
written += 32;
ctr += 1;
}
}
#[cfg(test)]
mod tests {
use super::super::{ecies, Generator, Random};
#[test]
fn ecies_shared() {
let kp = Random.generate();
let message = b"So many books, so little time";
let shared = b"shared";
let wrong_shared = b"incorrect";
let encrypted = ecies::encrypt(kp.public(), shared, message).unwrap();
assert!(encrypted[..] != message[..]);
assert_eq!(encrypted[0], 0x04);
assert!(ecies::decrypt(kp.secret(), wrong_shared, &encrypted).is_err());
let decrypted = ecies::decrypt(kp.secret(), shared, &encrypted).unwrap();
assert_eq!(decrypted[..message.len()], message[..]);
}
}