-
Notifications
You must be signed in to change notification settings - Fork 110
/
Copy pathprimitives.rs
68 lines (61 loc) · 2.08 KB
/
primitives.rs
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
//! Primitive cryptographic operations used across various `age` components.
use chacha20poly1305::{
aead::{self, generic_array::typenum::Unsigned, Aead, AeadCore, KeyInit},
ChaCha20Poly1305,
};
use hkdf::Hkdf;
use sha2::Sha256;
/// `encrypt[key](plaintext)` - encrypts a message with a one-time key.
///
/// ChaCha20-Poly1305 from [RFC 7539] with a zero nonce.
///
/// [RFC 7539]: https://tools.ietf.org/html/rfc7539
pub fn aead_encrypt(key: &[u8; 32], plaintext: &[u8]) -> Vec<u8> {
let c = ChaCha20Poly1305::new(key.into());
c.encrypt(&[0; 12].into(), plaintext)
.expect("we won't overflow the ChaCha20 block counter")
}
/// `decrypt[key](ciphertext)` - decrypts a message of an expected fixed size.
///
/// ChaCha20-Poly1305 from [RFC 7539] with a zero nonce.
///
/// The message size is limited to mitigate multi-key attacks, where a ciphertext can be
/// crafted that decrypts successfully under multiple keys. Short ciphertexts can only
/// target two keys, which has limited impact.
///
/// [RFC 7539]: https://tools.ietf.org/html/rfc7539
pub fn aead_decrypt(
key: &[u8; 32],
size: usize,
ciphertext: &[u8],
) -> Result<Vec<u8>, aead::Error> {
if ciphertext.len() != size + <ChaCha20Poly1305 as AeadCore>::TagSize::to_usize() {
return Err(aead::Error);
}
let c = ChaCha20Poly1305::new(key.into());
c.decrypt(&[0; 12].into(), ciphertext)
}
/// `HKDF[salt, label](key, 32)`
///
/// HKDF from [RFC 5869] with SHA-256.
///
/// [RFC 5869]: https://tools.ietf.org/html/rfc5869
pub fn hkdf(salt: &[u8], label: &[u8], ikm: &[u8]) -> [u8; 32] {
let mut okm = [0; 32];
Hkdf::<Sha256>::new(Some(salt), ikm)
.expand(label, &mut okm)
.expect("okm is the correct length");
okm
}
#[cfg(test)]
mod tests {
use super::{aead_decrypt, aead_encrypt};
#[test]
fn aead_round_trip() {
let key = [14; 32];
let plaintext = b"12345678";
let encrypted = aead_encrypt(&key, plaintext);
let decrypted = aead_decrypt(&key, plaintext.len(), &encrypted).unwrap();
assert_eq!(decrypted, plaintext);
}
}