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, AES_256_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> {
45 let algorithm = if key_bytes.len() == AES_128_GCM.key_len() {
46 &AES_128_GCM
47 } else if key_bytes.len() == AES_256_GCM.key_len() {
48 &AES_256_GCM
49 } else {
50 return Err(general_err!(
51 "Error creating RingGcmBlockDecryptor with unsupported key length: {}",
52 key_bytes.len()
53 ));
54 };
55 let key = UnboundKey::new(algorithm, key_bytes)
56 .map_err(|_| general_err!("Failed to create {:?} key", algorithm))?;
57
58 Ok(Self {
59 key: LessSafeKey::new(key),
60 })
61 }
62}
63
64impl HeapSize for RingGcmBlockDecryptor {
65 fn heap_size(&self) -> usize {
66 0
68 }
69}
70
71impl BlockDecryptor for RingGcmBlockDecryptor {
72 fn decrypt(&self, length_and_ciphertext: &[u8], aad: &[u8]) -> Result<Vec<u8>> {
73 let mut result = Vec::with_capacity(length_and_ciphertext.len() - SIZE_LEN - NONCE_LEN);
74 result.extend_from_slice(&length_and_ciphertext[SIZE_LEN + NONCE_LEN..]);
75
76 let nonce = ring::aead::Nonce::try_assume_unique_for_key(
77 &length_and_ciphertext[SIZE_LEN..SIZE_LEN + NONCE_LEN],
78 )?;
79
80 self.key.open_in_place(nonce, Aad::from(aad), &mut result)?;
81
82 result.resize(result.len() - TAG_LEN, 0u8);
84 Ok(result)
85 }
86
87 fn compute_plaintext_tag(&self, aad: &[u8], plaintext: &[u8]) -> Result<Vec<u8>> {
88 let mut plaintext = plaintext.to_vec();
89 let nonce = &plaintext[plaintext.len() - NONCE_LEN - TAG_LEN..plaintext.len() - TAG_LEN];
90 let nonce = ring::aead::Nonce::try_assume_unique_for_key(nonce)?;
91 let plaintext_end = plaintext.len() - NONCE_LEN - TAG_LEN;
92 let tag = self.key.seal_in_place_separate_tag(
93 nonce,
94 Aad::from(aad),
95 &mut plaintext[..plaintext_end],
96 )?;
97 Ok(tag.as_ref().to_vec())
98 }
99}
100
101pub(crate) trait BlockEncryptor: Debug + Send + Sync {
102 fn encrypt(&mut self, plaintext: &[u8], aad: &[u8]) -> Result<Vec<u8>>;
103}
104
105#[derive(Debug, Clone)]
106struct CounterNonce {
107 start: u128,
108 counter: u128,
109}
110
111impl CounterNonce {
112 pub fn new(rng: &SystemRandom) -> Result<Self> {
113 let mut buf = [0; 16];
114 rng.fill(&mut buf)?;
115
116 let start = u128::from_ne_bytes(buf) & RIGHT_TWELVE;
119 let counter = start.wrapping_add(1);
120
121 Ok(Self { start, counter })
122 }
123
124 #[inline]
126 pub fn get_bytes(&self) -> [u8; NONCE_LEN] {
127 self.counter.to_le_bytes()[0..NONCE_LEN].try_into().unwrap()
128 }
129}
130
131impl NonceSequence for CounterNonce {
132 fn advance(&mut self) -> Result<ring::aead::Nonce, ring::error::Unspecified> {
133 if (self.counter & RIGHT_TWELVE) == (self.start & RIGHT_TWELVE) {
135 Err(ring::error::Unspecified)
136 } else {
137 let buf: [u8; NONCE_LEN] = self.get_bytes();
139 self.counter = self.counter.wrapping_add(1);
140 Ok(ring::aead::Nonce::assume_unique_for_key(buf))
141 }
142 }
143}
144
145#[derive(Debug, Clone)]
146pub(crate) struct RingGcmBlockEncryptor {
147 key: LessSafeKey,
148 nonce_sequence: CounterNonce,
149}
150
151impl RingGcmBlockEncryptor {
152 pub(crate) fn new(key_bytes: &[u8]) -> Result<Self> {
156 let rng = SystemRandom::new();
157 let algorithm = if key_bytes.len() == AES_128_GCM.key_len() {
158 &AES_128_GCM
159 } else if key_bytes.len() == AES_256_GCM.key_len() {
160 &AES_256_GCM
161 } else {
162 return Err(general_err!(
163 "Error creating RingGcmBlockEncryptor with unsupported key length: {}",
164 key_bytes.len()
165 ));
166 };
167
168 let key = UnboundKey::new(algorithm, key_bytes)
169 .map_err(|e| general_err!("Error creating {:?} key: {}", algorithm, e))?;
170 let nonce = CounterNonce::new(&rng)?;
171
172 Ok(Self {
173 key: LessSafeKey::new(key),
174 nonce_sequence: nonce,
175 })
176 }
177}
178
179impl BlockEncryptor for RingGcmBlockEncryptor {
180 fn encrypt(&mut self, plaintext: &[u8], aad: &[u8]) -> Result<Vec<u8>> {
181 let ciphertext_length: u32 = (NONCE_LEN + plaintext.len() + TAG_LEN)
184 .try_into()
185 .map_err(|err| General(format!("Plaintext data too long. {err:?}")))?;
186 let mut ciphertext = Vec::with_capacity(SIZE_LEN + ciphertext_length as usize);
188 ciphertext.extend((ciphertext_length).to_le_bytes());
189
190 let nonce = self.nonce_sequence.advance()?;
191 ciphertext.extend(nonce.as_ref());
192 ciphertext.extend(plaintext);
193
194 let tag = self.key.seal_in_place_separate_tag(
195 nonce,
196 Aad::from(aad),
197 &mut ciphertext[SIZE_LEN + NONCE_LEN..],
198 )?;
199
200 ciphertext.extend(tag.as_ref());
201
202 debug_assert_eq!(SIZE_LEN + ciphertext_length as usize, ciphertext.len());
203
204 Ok(ciphertext)
205 }
206}
207
208#[cfg(test)]
209mod tests {
210 use super::*;
211
212 #[test]
213 fn test_round_trip() {
214 let key = [0u8; 16];
215 let mut encryptor = RingGcmBlockEncryptor::new(&key).unwrap();
216 let decryptor = RingGcmBlockDecryptor::new(&key).unwrap();
217
218 let plaintext = b"hello, world!";
219 let aad = b"some aad";
220
221 let ciphertext = encryptor.encrypt(plaintext, aad).unwrap();
222 let decrypted = decryptor.decrypt(&ciphertext, aad).unwrap();
223
224 assert_eq!(plaintext, decrypted.as_slice());
225 }
226}