Repository navigation
Conversation
|
|
|
|
|
@autoCAT6 This is AI-generated, right? Have you read our PR template at least? |
|
|
|
|
|
Thanks for pointing this out, @pitrou. Yes, AI was used for the implementation, regression tests, and PR description. I have updated the description to include the AI disclosure and completed the template checklist. |
|
Ok, can you please make the PR description terser so that it only keeps the salient points? |
|
I'm not an expert on this and the Parquet encryption code is convoluted, but I think this PR is wrong (and so was the previous attempt in #51693). Basically:
So a proper fix looks IMHO like this: diff --git a/cpp/src/parquet/encryption/internal_file_encryptor.cc b/cpp/src/parquet/encryption/internal_file_encryptor.cc
index 23dbfe34d3..4ab221fd55 100644
--- a/cpp/src/parquet/encryption/internal_file_encryptor.cc
+++ b/cpp/src/parquet/encryption/internal_file_encryptor.cc
@@ -68,7 +68,9 @@ std::shared_ptr<Encryptor> InternalFileEncryptor::GetFooterSigningEncryptor() {
return footer_signing_encryptor_;
}
- ParquetCipher::type algorithm = properties_->algorithm().algorithm;
+ // A plaintext footer is always signed with AES-GCM, regardless of the algorithm
+ // configured for encryption.
+ const auto algorithm = ParquetCipher::AES_GCM_V1;
std::string footer_aad = encryption::CreateFooterAad(properties_->file_aad());
const SecureString& footer_signing_key = properties_->footer_key();
auto aes_encryptor = GetMetaAesEncryptor(algorithm, footer_signing_key.size());
diff --git a/cpp/src/parquet/metadata.cc b/cpp/src/parquet/metadata.cc
index 61a111fc0c..fa0c80442a 100644
--- a/cpp/src/parquet/metadata.cc
+++ b/cpp/src/parquet/metadata.cc
@@ -1202,8 +1202,10 @@ bool FileMetaData::VerifySignature(std::span<const uint8_t> serialized_metadata,
const SecureString& key = file_decryptor->GetFooterKey();
const std::string& aad = encryption::CreateFooterAad(file_decryptor->file_aad());
+ // A plaintext footer is always signed with AES-GCM, regardless of the algorithm
+ // configured for encryption.
auto aes_encryptor = encryption::AesEncryptor::Make(
- file_decryptor->algorithm(), static_cast<int>(key.size()), /*metadata=*/true,
+ ParquetCipher::AES_GCM_V1, static_cast<int>(key.size()), /*metadata=*/true,
/*write_length=*/false);
std::shared_ptr<Buffer> encrypted_buffer =
@@ -2136,16 +2138,8 @@ class FileMetaDataBuilder::FileMetaDataBuilderImpl {
// if plaintext footer, set footer signing algorithm
auto file_encryption_properties = properties_->file_encryption_properties();
if (file_encryption_properties && !file_encryption_properties->encrypted_footer()) {
- EncryptionAlgorithm signing_algorithm;
- EncryptionAlgorithm algo = file_encryption_properties->algorithm();
- signing_algorithm.aad.aad_file_unique = algo.aad.aad_file_unique;
- signing_algorithm.aad.supply_aad_prefix = algo.aad.supply_aad_prefix;
- if (!algo.aad.supply_aad_prefix) {
- signing_algorithm.aad.aad_prefix = algo.aad.aad_prefix;
- }
- signing_algorithm.algorithm = ParquetCipher::AES_GCM_V1;
-
- metadata_->__set_encryption_algorithm(ToThrift(signing_algorithm));
+ metadata_->__set_encryption_algorithm(
+ ToThrift(file_encryption_properties->algorithm()));
const std::string& footer_signing_key_metadata =
file_encryption_properties->footer_key_metadata();
if (footer_signing_key_metadata.size() > 0) {cc @adamreeve @EnricoMi who are more knowledgeable than I am. |
|
I opened apache/parquet-testing#129 to add a file under parquet-testing. |
Yes, agreed. It looks like this PR is a partial fix, it correctly sets the file metadata's I also agree with dropping any modifications to arrow/cpp/src/parquet/encryption/encryption.cc Lines 328 to 332 in 1413260 |
|
I was also a bit confused about how signing and verifying the footer with AES-CTR would even work, because AES-CTR doesn't write a tag. It looks like we just assume the last 16 bytes of the encrypted data are the GCM tag when writing: arrow/cpp/src/parquet/metadata.cc Lines 885 to 886 in 1413260 And then do the same when reading: arrow/cpp/src/parquet/metadata.cc Lines 1217 to 1218 in 1413260 So the "tag" is actually just the last bytes of the AES-CTR encrypted data and the round trip succeeds |
Simplify file algorithm serialization and verify generated plaintext footer tags directly against AES-GCM. Apply the footer signing and metadata simplification suggested in PR review.
|
|
|
|
|
Thanks, updated. I shortened the description and simplified the algorithm copy. Signing and both verification paths now explicitly use GCM. I checked the old code too: C++ tests and the Java 1.18.1 checks pass. |
It took me a while to parse this AI speak, but I think this is saying that changing Passing the AES_GCM_V1 algorithm explicitly makes things clearer. But this is more evidence for a tidy up being necessary (#45525). I'm happy to create a new parquet-testing file for this. I think we should generate it from this PR branch, then verify it can be read in arrow-go and parquet-java (which are the only other implementations that support AES-CTR I believe). |
Rationale for this change
Closes #51669. CTR files with plaintext footers incorrectly declare GCM, causing encrypted-column reads to fail.
What changes are included in this PR?
Original report by dotnwat in #51669. The follow-up incorporates pitrou's review and adamreeve's clarification.
Are these changes tested?
git diff --checkpass.Are there any user-facing changes?
New CTR/plaintext-footer files declare the correct algorithm. No public API changes.
This PR contains a "Critical Fix": it corrects invalid encryption algorithm metadata emitted by the writer.
Was AI used for this PR?
AI was used to generate the implementation, regression tests, and PR description.
PR code and description written by:
Reviewed before submission by:
Human
AI
Not reviewed
GitHub Issue: [C++][Parquet] Plaintext-footer files written with AES_GCM_CTR_V1 record AES_GCM_V1 as the encryption algorithm and cannot be read #51669