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
use aes_gcm_siv::aead::{consts::U12, generic_array::GenericArray, Aead, NewAead, Payload};
use tink_core::{utils::wrap_err, TinkError};
pub const AES_GCM_SIV_NONCE_SIZE: usize = 12;
pub const AES_GCM_SIV_TAG_SIZE: usize = 16;
#[derive(Clone)]
enum AesGcmSivVariant {
Aes128(Box<aes_gcm_siv::Aes128GcmSiv>),
Aes256(Box<aes_gcm_siv::Aes256GcmSiv>),
}
#[derive(Clone)]
pub struct AesGcmSiv {
key: AesGcmSivVariant,
}
impl AesGcmSiv {
pub fn new(key: &[u8]) -> Result<AesGcmSiv, TinkError> {
let key = match key.len() {
16 => AesGcmSivVariant::Aes128(Box::new(aes_gcm_siv::Aes128GcmSiv::new(
GenericArray::from_slice(key),
))),
32 => AesGcmSivVariant::Aes256(Box::new(aes_gcm_siv::Aes256GcmSiv::new(
GenericArray::from_slice(key),
))),
l => return Err(format!("AesGcmSiv: invalid AES key size {} (want 16, 32)", l).into()),
};
Ok(AesGcmSiv { key })
}
}
impl tink_core::Aead for AesGcmSiv {
fn encrypt(&self, pt: &[u8], aad: &[u8]) -> Result<Vec<u8>, TinkError> {
if pt.len() > ((isize::MAX as usize) - AES_GCM_SIV_NONCE_SIZE - AES_GCM_SIV_TAG_SIZE) {
return Err("AesGcmSiv: plaintext too long".into());
}
if aad.len() > (isize::MAX as usize) {
return Err("AesGcmSiv: additional-data too long".into());
}
let iv = new_iv();
let payload = Payload { msg: pt, aad };
let ct = match &self.key {
AesGcmSivVariant::Aes128(key) => key.encrypt(&iv, payload),
AesGcmSivVariant::Aes256(key) => key.encrypt(&iv, payload),
}
.map_err(|e| wrap_err("AesGcmSiv", 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_SIV_NONCE_SIZE + AES_GCM_SIV_TAG_SIZE {
return Err("AesGcmSiv: ciphertext too short".into());
}
if ct.len() > (isize::MAX as usize) {
return Err("AesGcmSiv: ciphertext too long".into());
}
if aad.len() > (isize::MAX as usize) {
return Err("AesGcmSiv: additional-data too long".into());
}
let iv = GenericArray::from_slice(&ct[..AES_GCM_SIV_NONCE_SIZE]);
let payload = Payload {
msg: &ct[AES_GCM_SIV_NONCE_SIZE..],
aad,
};
let pt = match &self.key {
AesGcmSivVariant::Aes128(key) => key.decrypt(iv, payload),
AesGcmSivVariant::Aes256(key) => key.decrypt(iv, payload),
}
.map_err(|e| wrap_err("AesGcmSiv", e))?;
Ok(pt)
}
}
fn new_iv() -> GenericArray<u8, U12> {
let iv = tink_core::subtle::random::get_random_bytes(AES_GCM_SIV_NONCE_SIZE);
*GenericArray::<u8, U12>::from_slice(&iv)
}