Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
c017804
Allow more encryption algorithms
hsiang-c Jan 6, 2026
1d12eb4
Merge branch 'main' into allow_more_enc_algo
hsiang-c Jan 23, 2026
e8da4b9
Expose new API
hsiang-c Jan 27, 2026
3e1b8a7
Fix API
hsiang-c Jan 27, 2026
1f1f0da
Fix format and clippy
hsiang-c Feb 5, 2026
92b4094
Document new api
hsiang-c Feb 5, 2026
12c1888
Pick algorithm by key size
hsiang-c Feb 14, 2026
3ec6b70
Remove unnecessary tests
hsiang-c Feb 14, 2026
deec81e
Round-trip tests w/ AES-256 encrypted parquet
hsiang-c Feb 19, 2026
35b482f
Merge branch 'main' into allow_more_enc_algo
hsiang-c Feb 19, 2026
f55fe3d
Fix comment
hsiang-c Feb 19, 2026
d92014b
More test cases
hsiang-c Mar 1, 2026
0fa4888
Merge branch 'main' into allow_more_enc_algo
hsiang-c Mar 1, 2026
bd79c92
Align field names
hsiang-c Mar 2, 2026
e95eb4d
Refactor async encryption tests
hsiang-c Mar 4, 2026
e8999d3
Refactor sync encryption tests
hsiang-c Mar 5, 2026
928ec10
Merge branch 'main' into allow_more_enc_algo
hsiang-c Mar 6, 2026
0abe16d
Comment & clean up
hsiang-c Mar 6, 2026
206c6c6
Update comment
hsiang-c Apr 22, 2026
36fd995
Merge branch 'main' into allow_more_enc_algo
hsiang-c Apr 22, 2026
efe04f6
Format
hsiang-c Apr 22, 2026
d26f41b
Revert tests
hsiang-c Apr 30, 2026
1b3f2f7
Revert tests
hsiang-c May 1, 2026
5b8b244
Fix comments
hsiang-c May 1, 2026
96ad92d
Merge branch 'apache:main' into allow_more_enc_algo
hsiang-c May 4, 2026
f414b3e
Extract hardcoded strings to constants
hsiang-c May 4, 2026
4336eed
Clean up
hsiang-c May 4, 2026
44caecc
Revert uncessary changes
hsiang-c May 4, 2026
12d768d
Extract common data
hsiang-c May 4, 2026
06c6b73
Restore one test
hsiang-c May 4, 2026
a91f2f9
Remove the workaround
hsiang-c May 11, 2026
ec912ab
Update test data
hsiang-c May 14, 2026
28f3ab2
Merge branch 'main' into allow_more_enc_algo
hsiang-c May 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 36 additions & 8 deletions parquet/src/encryption/ciphers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use crate::errors::ParquetError;
use crate::errors::ParquetError::General;
use crate::errors::Result;
use crate::file::metadata::HeapSize;
use ring::aead::{AES_128_GCM, Aad, LessSafeKey, NonceSequence, UnboundKey};
use ring::aead::{AES_128_GCM, Aad, LessSafeKey, NonceSequence, UnboundKey, Algorithm};
Comment thread
hsiang-c marked this conversation as resolved.
Outdated
use ring::rand::{SecureRandom, SystemRandom};
use std::fmt::Debug;

Expand All @@ -41,9 +41,12 @@ pub(crate) struct RingGcmBlockDecryptor {

impl RingGcmBlockDecryptor {
pub(crate) fn new(key_bytes: &[u8]) -> Result<Self> {
// todo support other key sizes
let key = UnboundKey::new(&AES_128_GCM, key_bytes)
.map_err(|_| General("Failed to create AES key".to_string()))?;
Self::new_with_algorithm(&AES_128_GCM, key_bytes)
}

pub(crate) fn new_with_algorithm(algorithm: &'static Algorithm, key_bytes: &[u8]) -> Result<Self> {
let key = UnboundKey::new(algorithm, key_bytes)
.map_err(|_| general_err!("Failed to create {:?} key", algorithm))?;

Ok(Self {
key: LessSafeKey::new(key),
Expand Down Expand Up @@ -143,11 +146,13 @@ impl RingGcmBlockEncryptor {
/// The nonce will advance appropriately with each block encryption and
/// return an error if it wraps around.
pub(crate) fn new(key_bytes: &[u8]) -> Result<Self> {
let rng = SystemRandom::new();
Self::new_with_algorithm(&AES_128_GCM, key_bytes)
}

// todo support other key sizes
let key = UnboundKey::new(&AES_128_GCM, key_bytes)
.map_err(|e| general_err!("Error creating AES key: {}", e))?;
pub(crate) fn new_with_algorithm(algorithm: &'static Algorithm, key_bytes: &[u8]) -> Result<Self> {
let rng = SystemRandom::new();
let key = UnboundKey::new(&algorithm, key_bytes)
.map_err(|e| general_err!("Error creating {:?} key: {}", algorithm, e))?;
let nonce = CounterNonce::new(&rng)?;

Ok(Self {
Expand Down Expand Up @@ -188,6 +193,7 @@ impl BlockEncryptor for RingGcmBlockEncryptor {

#[cfg(test)]
mod tests {
use ring::aead::{AES_256_GCM, CHACHA20_POLY1305};
use super::*;

#[test]
Expand All @@ -204,4 +210,26 @@ mod tests {

assert_eq!(plaintext, decrypted.as_slice());
}

#[test]
fn test_round_trip_with_algorithm() {
let key = [0u8; 32];
let mut encryptor = RingGcmBlockEncryptor::new_with_algorithm(&AES_256_GCM, &key).unwrap();
let decryptor = RingGcmBlockDecryptor::new_with_algorithm(&AES_256_GCM, &key).unwrap();

let plaintext = b"hello, world!";
let aad = b"some aad";

let ciphertext = encryptor.encrypt(plaintext, aad).unwrap();
let decrypted = decryptor.decrypt(&ciphertext, aad).unwrap();

assert_eq!(plaintext, decrypted.as_slice());
}

#[test]
fn test_round_trip_with_incorrect_key_length() {
let key = [0u8; 16];
assert!(RingGcmBlockEncryptor::new_with_algorithm(&CHACHA20_POLY1305, &key).is_err());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It isn't clear to me that CHACHA is a valid Parquet encryption: https://github.com/apache/parquet-format/blob/master/Encryption.md :

assert!(RingGcmBlockDecryptor::new_with_algorithm(&CHACHA20_POLY1305, &key).is_err());
}
}
18 changes: 18 additions & 0 deletions parquet/src/encryption/decrypt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ use std::collections::HashMap;
use std::fmt::Formatter;
use std::io::Read;
use std::sync::Arc;
use ring::aead::{Algorithm, AES_128_GCM};

/// Trait for retrieving an encryption key using the key's metadata
///
Expand Down Expand Up @@ -352,6 +353,7 @@ pub struct FileDecryptionProperties {
keys: DecryptionKeys,
aad_prefix: Option<Vec<u8>>,
footer_signature_verification: bool,
algorithm: &'static Algorithm
Comment thread
hsiang-c marked this conversation as resolved.
Outdated
}

impl HeapSize for FileDecryptionProperties {
Expand Down Expand Up @@ -448,6 +450,7 @@ pub struct DecryptionPropertiesBuilder {
column_keys: HashMap<String, Vec<u8>>,
aad_prefix: Option<Vec<u8>>,
footer_signature_verification: bool,
algorithm: &'static Algorithm
}

impl DecryptionPropertiesBuilder {
Expand All @@ -459,6 +462,7 @@ impl DecryptionPropertiesBuilder {
column_keys: HashMap::default(),
aad_prefix: None,
footer_signature_verification: true,
algorithm: &AES_128_GCM
}
}

Expand All @@ -472,6 +476,7 @@ impl DecryptionPropertiesBuilder {
keys,
aad_prefix: self.aad_prefix,
footer_signature_verification: self.footer_signature_verification,
algorithm: self.algorithm
}))
}

Expand Down Expand Up @@ -505,6 +510,11 @@ impl DecryptionPropertiesBuilder {
Ok(self)
}

pub fn with_algorithm(mut self, algorithm: &'static Algorithm) -> Result<Self> {
self.algorithm = algorithm;
Ok(self)
}

/// Disable verification of footer tags for files that use plaintext footers.
/// Signature verification is enabled by default.
pub fn disable_footer_signature_verification(mut self) -> Self {
Expand All @@ -520,6 +530,7 @@ pub struct DecryptionPropertiesBuilderWithRetriever {
key_retriever: Arc<dyn KeyRetriever>,
aad_prefix: Option<Vec<u8>>,
footer_signature_verification: bool,
algorithm: &'static Algorithm
}

impl DecryptionPropertiesBuilderWithRetriever {
Expand All @@ -530,6 +541,7 @@ impl DecryptionPropertiesBuilderWithRetriever {
key_retriever,
aad_prefix: None,
footer_signature_verification: true,
algorithm: &AES_128_GCM
}
}

Expand All @@ -540,6 +552,7 @@ impl DecryptionPropertiesBuilderWithRetriever {
keys,
aad_prefix: self.aad_prefix,
footer_signature_verification: self.footer_signature_verification,
algorithm: self.algorithm
}))
}

Expand All @@ -551,6 +564,11 @@ impl DecryptionPropertiesBuilderWithRetriever {
self
}

pub fn with_algorithm(mut self, algorithm: &'static Algorithm) -> Self {
self.algorithm = algorithm;
self
}

/// Disable verification of footer tags for files that use plaintext footers.
/// Signature verification is enabled by default.
pub fn disable_footer_signature_verification(mut self) -> Self {
Expand Down
10 changes: 10 additions & 0 deletions parquet/src/encryption/encrypt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ use ring::rand::{SecureRandom, SystemRandom};
use std::collections::{HashMap, HashSet};
use std::io::Write;
use std::sync::Arc;
use ring::aead::{Algorithm, AES_128_GCM};

#[derive(Debug, Clone, PartialEq)]
struct EncryptionKey {
Expand Down Expand Up @@ -96,6 +97,7 @@ pub struct FileEncryptionProperties {
column_keys: HashMap<String, EncryptionKey>,
aad_prefix: Option<Vec<u8>>,
store_aad_prefix: bool,
algorithm: &'static Algorithm
}

impl FileEncryptionProperties {
Expand Down Expand Up @@ -186,6 +188,7 @@ pub struct EncryptionPropertiesBuilder {
column_keys: HashMap<String, EncryptionKey>,
aad_prefix: Option<Vec<u8>>,
store_aad_prefix: bool,
algorithm: &'static Algorithm
}

impl EncryptionPropertiesBuilder {
Expand All @@ -197,6 +200,7 @@ impl EncryptionPropertiesBuilder {
aad_prefix: None,
encrypt_footer: true,
store_aad_prefix: false,
algorithm: &AES_128_GCM
}
}

Expand Down Expand Up @@ -274,6 +278,11 @@ impl EncryptionPropertiesBuilder {
self
}

pub fn with_algorithm(mut self, algorithm: &'static Algorithm) -> Self {
self.algorithm = algorithm;
self
}

/// Build the encryption properties
pub fn build(self) -> Result<Arc<FileEncryptionProperties>> {
Ok(Arc::new(FileEncryptionProperties {
Expand All @@ -282,6 +291,7 @@ impl EncryptionPropertiesBuilder {
column_keys: self.column_keys,
aad_prefix: self.aad_prefix,
store_aad_prefix: self.store_aad_prefix,
algorithm: self.algorithm
}))
}
}
Expand Down
3 changes: 3 additions & 0 deletions parquet/src/file/metadata/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2045,6 +2045,9 @@ mod tests {
.set_file_decryptor(Some(decryptor))
.build();

#[cfg(feature = "encryption")]
let expected_size_with_decryptor = 3080;
#[cfg(not(feature = "encryption"))]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this needed? The whole test is gated by

    #[cfg(feature = "encryption")]
    fn test_memory_size_with_decryptor() {

it seems like you just need to update the 3072 to 3080 to reflect the additional size?

let expected_size_with_decryptor = 3072;
assert!(expected_size_with_decryptor > base_expected_size);

Expand Down
4 changes: 2 additions & 2 deletions parquet/tests/encryption/encryption_async.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ async fn test_misspecified_encryption_keys() {

// Too short footer key
check_for_error(
Comment thread
hsiang-c marked this conversation as resolved.
"Parquet error: Invalid footer key. Failed to create AES key",
"Parquet error: Invalid footer key. Failed to create AES_128_GCM key",
&path,
"bad_pwd".as_bytes(),
column_1_key,
Expand Down Expand Up @@ -144,7 +144,7 @@ async fn test_misspecified_encryption_keys() {

// Too short column key
check_for_error(
"Parquet error: Failed to create AES key",
"Parquet error: Failed to create AES_128_GCM key",
&path,
footer_key,
"abc".as_bytes(),
Expand Down