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