parquet/encryption/
ciphers.rs1use crate::errors::ParquetError;
19use crate::errors::ParquetError::General;
20use crate::errors::Result;
21use crate::file::metadata::HeapSize;
22use ring::aead::{AES_128_GCM, Aad, LessSafeKey, NonceSequence, UnboundKey};
23use ring::rand::{SecureRandom, SystemRandom};
24use std::fmt::Debug;
25
26const RIGHT_TWELVE: u128 = 0x0000_0000_ffff_ffff_ffff_ffff_ffff_ffff;
27pub(crate) const NONCE_LEN: usize = 12;
28pub(crate) const TAG_LEN: usize = 16;
29pub(crate) const SIZE_LEN: usize = 4;
30
31pub(crate) trait BlockDecryptor: Debug + Send + Sync + HeapSize {
32 fn decrypt(&self, length_and_ciphertext: &[u8], aad: &[u8]) -> Result<Vec<u8>>;
33
34 fn compute_plaintext_tag(&self, aad: &[u8], plaintext: &[u8]) -> Result<Vec<u8>>;
35}
36
37#[derive(Debug, Clone)]
38pub(crate) struct RingGcmBlockDecryptor {
39 key: LessSafeKey,
40}
41
42impl RingGcmBlockDecryptor {
43 pub(crate) fn new(key_bytes: &[u8]) -> Result<Self> {
44 let key = UnboundKey::new(&AES_128_GCM, key_bytes)
46 .map_err(|_| General("Failed to create AES key".to_string()))?;
47
48 Ok(Self {
49 key: LessSafeKey::new(key),
50 })
51 }
52}
53
54impl HeapSize for RingGcmBlockDecryptor {
55 fn heap_size(&self) -> usize {
56 0
58 }
59}
60
61impl BlockDecryptor for RingGcmBlockDecryptor {
62 fn decrypt(&self, length_and_ciphertext: &[u8], aad: &[u8]) -> Result<Vec<u8>> {
63 let mut result = Vec::with_capacity(length_and_ciphertext.len() - SIZE_LEN - NONCE_LEN);
64 result.extend_from_slice(&length_and_ciphertext[SIZE_LEN + NONCE_LEN..]);
65
66 let nonce = ring::aead::Nonce::try_assume_unique_for_key(
67 &length_and_ciphertext[SIZE_LEN..SIZE_LEN + NONCE_LEN],
68 )?;
69
70 self.key.open_in_place(nonce, Aad::from(aad), &mut result)?;
71
72 result.resize(result.len() - TAG_LEN, 0u8);
74 Ok(result)
75 }
76
77 fn compute_plaintext_tag(&self, aad: &[u8], plaintext: &[u8]) -> Result<Vec<u8>> {
78 let mut plaintext = plaintext.to_vec();
79 let nonce = &plaintext[plaintext.len() - NONCE_LEN - TAG_LEN..plaintext.len() - TAG_LEN];
80 let nonce = ring::aead::Nonce::try_assume_unique_for_key(nonce)?;
81 let plaintext_end = plaintext.len() - NONCE_LEN - TAG_LEN;
82 let tag = self.key.seal_in_place_separate_tag(
83 nonce,
84 Aad::from(aad),
85 &mut plaintext[..plaintext_end],
86 )?;
87 Ok(tag.as_ref().to_vec())
88 }
89}
90
91pub(crate) trait BlockEncryptor: Debug + Send + Sync {
92 fn encrypt(&mut self, plaintext: &[u8], aad: &[u8]) -> Result<Vec<u8>>;
93}
94
95#[derive(Debug, Clone)]
96struct CounterNonce {
97 start: u128,
98 counter: u128,
99}
100
101impl CounterNonce {
102 pub fn new(rng: &SystemRandom) -> Result<Self> {
103 let mut buf = [0; 16];
104 rng.fill(&mut buf)?;
105
106 let start = u128::from_ne_bytes(buf) & RIGHT_TWELVE;
109 let counter = start.wrapping_add(1);
110
111 Ok(Self { start, counter })
112 }
113
114 #[inline]
116 pub fn get_bytes(&self) -> [u8; NONCE_LEN] {
117 self.counter.to_le_bytes()[0..NONCE_LEN].try_into().unwrap()
118 }
119}
120
121impl NonceSequence for CounterNonce {
122 fn advance(&mut self) -> Result<ring::aead::Nonce, ring::error::Unspecified> {
123 if (self.counter & RIGHT_TWELVE) == (self.start & RIGHT_TWELVE) {
125 Err(ring::error::Unspecified)
126 } else {
127 let buf: [u8; NONCE_LEN] = self.get_bytes();
129 self.counter = self.counter.wrapping_add(1);
130 Ok(ring::aead::Nonce::assume_unique_for_key(buf))
131 }
132 }
133}
134
135#[derive(Debug, Clone)]
136pub(crate) struct RingGcmBlockEncryptor {
137 key: LessSafeKey,
138 nonce_sequence: CounterNonce,
139}
140
141impl RingGcmBlockEncryptor {
142 pub(crate) fn new(key_bytes: &[u8]) -> Result<Self> {
146 let rng = SystemRandom::new();
147
148 let key = UnboundKey::new(&AES_128_GCM, key_bytes)
150 .map_err(|e| general_err!("Error creating AES key: {}", e))?;
151 let nonce = CounterNonce::new(&rng)?;
152
153 Ok(Self {
154 key: LessSafeKey::new(key),
155 nonce_sequence: nonce,
156 })
157 }
158}
159
160impl BlockEncryptor for RingGcmBlockEncryptor {
161 fn encrypt(&mut self, plaintext: &[u8], aad: &[u8]) -> Result<Vec<u8>> {
162 let ciphertext_length: u32 = (NONCE_LEN + plaintext.len() + TAG_LEN)
165 .try_into()
166 .map_err(|err| General(format!("Plaintext data too long. {err:?}")))?;
167 let mut ciphertext = Vec::with_capacity(SIZE_LEN + ciphertext_length as usize);
169 ciphertext.extend((ciphertext_length).to_le_bytes());
170
171 let nonce = self.nonce_sequence.advance()?;
172 ciphertext.extend(nonce.as_ref());
173 ciphertext.extend(plaintext);
174
175 let tag = self.key.seal_in_place_separate_tag(
176 nonce,
177 Aad::from(aad),
178 &mut ciphertext[SIZE_LEN + NONCE_LEN..],
179 )?;
180
181 ciphertext.extend(tag.as_ref());
182
183 debug_assert_eq!(SIZE_LEN + ciphertext_length as usize, ciphertext.len());
184
185 Ok(ciphertext)
186 }
187}
188
189#[cfg(test)]
190mod tests {
191 use super::*;
192
193 #[test]
194 fn test_round_trip() {
195 let key = [0u8; 16];
196 let mut encryptor = RingGcmBlockEncryptor::new(&key).unwrap();
197 let decryptor = RingGcmBlockDecryptor::new(&key).unwrap();
198
199 let plaintext = b"hello, world!";
200 let aad = b"some aad";
201
202 let ciphertext = encryptor.encrypt(plaintext, aad).unwrap();
203 let decrypted = decryptor.decrypt(&ciphertext, aad).unwrap();
204
205 assert_eq!(plaintext, decrypted.as_slice());
206 }
207}