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
use aes_gcm::aead::{consts::U12, generic_array::GenericArray, Aead, NewAead, Payload};
use tink_core::{utils::wrap_err, TinkError};
pub const AES_GCM_IV_SIZE: usize = 12;
pub const AES_GCM_TAG_SIZE: usize = 16;
const MAX_AES_GCM_PLAINTEXT_SIZE: usize = (1 << 36) - 32;
#[derive(Clone)]
enum AesGcmVariant {
Aes128(Box<aes_gcm::Aes128Gcm>),
Aes256(Box<aes_gcm::Aes256Gcm>),
}
#[derive(Clone)]
pub struct AesGcm {
key: AesGcmVariant,
}
impl AesGcm {
pub fn new(key: &[u8]) -> Result<AesGcm, TinkError> {
let key = match key.len() {
16 => AesGcmVariant::Aes128(Box::new(aes_gcm::Aes128Gcm::new(
GenericArray::from_slice(key),
))),
32 => AesGcmVariant::Aes256(Box::new(aes_gcm::Aes256Gcm::new(
GenericArray::from_slice(key),
))),
l => return Err(format!("AesGcm: invalid AES key size {} (want 16, 32)", l).into()),
};
Ok(AesGcm { key })
}
}
impl tink_core::Aead for AesGcm {
fn encrypt(&self, pt: &[u8], aad: &[u8]) -> Result<Vec<u8>, TinkError> {
if pt.len() > max_pt_size() {
return Err("AesGcm: plaintext too long".into());
}
let iv = new_iv();
let payload = Payload { msg: pt, aad };
let ct = match &self.key {
AesGcmVariant::Aes128(key) => key.encrypt(&iv, payload),
AesGcmVariant::Aes256(key) => key.encrypt(&iv, payload),
}
.map_err(|e| wrap_err("AesGcm", e))?;
let mut ret = Vec::with_capacity(iv.len() + ct.len());
ret.extend_from_slice(&iv);
ret.extend_from_slice(&ct);
Ok(ret)
}
fn decrypt(&self, ct: &[u8], aad: &[u8]) -> Result<Vec<u8>, TinkError> {
if ct.len() < AES_GCM_IV_SIZE + AES_GCM_TAG_SIZE {
return Err("AesGcm: ciphertext too short".into());
}
let iv = GenericArray::from_slice(&ct[..AES_GCM_IV_SIZE]);
let payload = Payload {
msg: &ct[AES_GCM_IV_SIZE..],
aad,
};
let pt = match &self.key {
AesGcmVariant::Aes128(key) => key.decrypt(iv, payload),
AesGcmVariant::Aes256(key) => key.decrypt(iv, payload),
}
.map_err(|e| wrap_err("AesGcm", e))?;
Ok(pt)
}
}
fn new_iv() -> GenericArray<u8, U12> {
let iv = tink_core::subtle::random::get_random_bytes(AES_GCM_IV_SIZE);
*GenericArray::<u8, U12>::from_slice(&iv)
}
fn max_pt_size() -> usize {
let x = (isize::MAX as usize) - AES_GCM_IV_SIZE - AES_GCM_TAG_SIZE;
std::cmp::min(x, MAX_AES_GCM_PLAINTEXT_SIZE)
}