Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
46 changes: 46 additions & 0 deletions cpp/src/parquet/encryption/write_configurations_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@

#include <stdio.h>

#include <fstream>

#include <arrow/io/file.h>

#include "parquet/column_reader.h"
Expand Down Expand Up @@ -195,6 +197,50 @@ TEST_F(TestEncryptionConfiguration, EncryptTwoColumnsAndFooterUseAES_GCM_CTR) {
"tmp_encrypt_columns_and_footer_ctr.parquet.encrypted"));
}

// Cover the file algorithm independently of the footer signing cipher.
TEST_F(TestEncryptionConfiguration, PlaintextFooterAlgorithms) {
for (auto algorithm : {ParquetCipher::AES_GCM_V1, ParquetCipher::AES_GCM_CTR_V1}) {
SCOPED_TRACE(algorithm);
FileEncryptionProperties::Builder encryption_builder(kFooterEncryptionKey_);
auto encryption_properties =
encryption_builder.algorithm(algorithm)->set_plaintext_footer()->build();
const std::string file = temp_dir->path().ToString() + "plaintext_footer.parquet";
encryptor_.EncryptFile(file, encryption_properties);

FileDecryptionProperties::Builder decryption_builder;
auto decryption_properties =
decryption_builder.footer_key(kFooterEncryptionKey_)->build();
ReaderProperties reader_properties;
reader_properties.file_decryption_properties(decryption_properties);
auto reader = ParquetFileReader::OpenFile(file, false, reader_properties);
EXPECT_EQ(reader->metadata()->encryption_algorithm().algorithm, algorithm);
reader->Close();

FileDecryptor decryptor;
EXPECT_NO_THROW(decryptor.DecryptFile(file, decryption_properties));

FileDecryptionProperties::Builder wrong_key_builder;
ReaderProperties wrong_key_properties;
wrong_key_properties.file_decryption_properties(
wrong_key_builder.footer_key(kColumnEncryptionKey1_)->build());
EXPECT_THROW(ParquetFileReader::OpenFile(file, false, wrong_key_properties),
ParquetException);

// The signature precedes the footer length and trailing magic (eight bytes).
std::fstream stream(file, std::ios::in | std::ios::out | std::ios::binary);
stream.seekg(-9, std::ios::end);
char tag_byte;
stream.read(&tag_byte, 1);
ASSERT_TRUE(stream.good());
tag_byte ^= 1;
stream.seekp(-9, std::ios::end);
stream.write(&tag_byte, 1);
stream.close();
EXPECT_THROW(ParquetFileReader::OpenFile(file, false, reader_properties),
ParquetException);
}
}

// Set temp_dir before running the write/read tests. The encrypted files will
// be written/read from this directory.
void TestEncryptionConfiguration::SetUpTestCase() {
Expand Down
4 changes: 2 additions & 2 deletions cpp/src/parquet/metadata.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2133,7 +2133,7 @@ class FileMetaDataBuilder::FileMetaDataBuilderImpl {
metadata_->column_orders.resize(schema_->num_columns(), column_order);
metadata_->__isset.column_orders = true;

// if plaintext footer, set footer signing algorithm
// For a plaintext footer, record the file encryption algorithm.
auto file_encryption_properties = properties_->file_encryption_properties();
if (file_encryption_properties && !file_encryption_properties->encrypted_footer()) {
EncryptionAlgorithm signing_algorithm;
Expand All @@ -2143,7 +2143,7 @@ class FileMetaDataBuilder::FileMetaDataBuilderImpl {
if (!algo.aad.supply_aad_prefix) {
signing_algorithm.aad.aad_prefix = algo.aad.aad_prefix;
}
signing_algorithm.algorithm = ParquetCipher::AES_GCM_V1;
signing_algorithm.algorithm = algo.algorithm;

metadata_->__set_encryption_algorithm(ToThrift(signing_algorithm));
const std::string& footer_signing_key_metadata =
Expand Down
10 changes: 7 additions & 3 deletions python/pyarrow/tests/parquet/test_encryption.py
Original file line number Diff line number Diff line change
Expand Up @@ -949,12 +949,16 @@ def test_encrypted_file_has_pare_magic(self, tempdir, data_table):
magic = f.read(4)
assert magic == b"PARE"

def test_plaintext_footer(self, tempdir, data_table):
path = tempdir / "direct_plaintext_footer.parquet"
@pytest.mark.parametrize("algorithm", ["AES_GCM_V1", "AES_GCM_CTR_V1"])
@pytest.mark.parametrize("plaintext_footer", [False, True])
def test_footer_algorithms(self, tempdir, data_table, algorithm,
plaintext_footer):
path = tempdir / "direct_footer_algorithms.parquet"

enc_props = pe.create_encryption_properties(
footer_key=DIRECT_KEY_128,
plaintext_footer=True,
plaintext_footer=plaintext_footer,
encryption_algorithm=algorithm,
)
pq.write_table(data_table, path, encryption_properties=enc_props)

Expand Down
Loading