feat: Iceberg table format V3: native table decryption, fall back for other V3 features#4991
Merged
Merged
Conversation
…ey data through proto
…Java's encryption suite locally
mbutrovich
marked this pull request as ready for review
July 21, 2026 19:18
hsiang-c
reviewed
Jul 21, 2026
hsiang-c
reviewed
Jul 21, 2026
hsiang-c
reviewed
Jul 21, 2026
| // The native Parquet reader (arrow-rs 58.3) decrypts only 128-bit AES-GCM keys, so a table with a | ||
| // larger data key must fall back to Spark and still return correct results. Flip this to a native | ||
| // assertion when arrow-rs 58.4 (256-bit support, arrow-rs#10349) is picked up. | ||
| test("encrypted V3 table with a 256-bit data key falls back to Spark") { |
hsiang-c
reviewed
Jul 21, 2026
Contributor
Author
Thanks for the review @hsiang-c! |
# Conflicts: # native/Cargo.lock # native/Cargo.toml # native/core/src/execution/planner.rs
mbutrovich
force-pushed
the
iceberg_decryption
branch
from
July 22, 2026 12:07
b7ae106 to
15f71f8
Compare
mbutrovich
commented
Jul 22, 2026
| # (cache-save) phase runs, and by then the test run has left trees under the | ||
| # workspace (e.g. the Hive-catalog warehouse from CometIcebergEncryptionSuite) | ||
| # that make a fresh '**/pom.xml' walk throw "Fail to hash files under | ||
| # directory". A precomputed string keeps the save phase from re-walking them. |
Contributor
Author
There was a problem hiding this comment.
I also added some cleanup logic for the Hive-catalog, but just in case I'm also addressing the footgun here. It fixed a failure in the Post Java test steps:
Post job cleanup.
Error: The template is not valid. /home/runner/work/datafusion-comet/datafusion-comet/./.github/actions/java-test/action.yaml (Line: 63, Col: 14): hashFiles('**/pom.xml') failed. Fail to hash files under directory '/home/runner/work/datafusion-comet/datafusion-comet'
that I saw after adding the Hive catalog https://github.com/apache/datafusion-comet/actions/runs/29912607672/job/88905543943
andygrove
reviewed
Jul 23, 2026
|
|
||
| // Serialized StandardKeyMetadata for an encrypted delete file, from DeleteFile.keyMetadata(). | ||
| // Absent for unencrypted tables. Decoded directly by iceberg-rust; no KMS unwrap needed. | ||
| // Field 8: fields 5-7 are reserved for the deletion-vector coordinates on the dv-read branch |
Member
There was a problem hiding this comment.
Should we add an explicit reserved 5, 6, 7 ?
andygrove
approved these changes
Jul 23, 2026
andygrove
left a comment
Member
There was a problem hiding this comment.
LGTM with one suggested change. Thanks @mbutrovich
hsiang-c
reviewed
Jul 23, 2026
|
|
||
| // A 256-bit (32-byte) AES-GCM data key is supported natively, so the read must run through Comet | ||
| // and match Spark. | ||
| test("encrypted V3 table with a 256-bit data key is read natively and matches Spark") { |
# Conflicts: # native/core/src/execution/planner.rs # spark/src/main/scala/org/apache/comet/serde/operator/CometIcebergNativeScan.scala
mbutrovich
added a commit
that referenced
this pull request
Jul 24, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Which issue does this PR close?
Part of #3756. Closes #.
Rationale for this change
Comet's native Iceberg scan rejected V3 tables. This adds native reads of encrypted Iceberg tables (a V3 feature).
Iceberg-Java unwraps the key envelope on the driver during planning and stores each data file's plaintext data encryption key in the manifest's
key_metadatafield. SoDataFile.keyMetadata()already holds the plaintext key when Comet reads the scan tasks. Comet forwards those bytes to iceberg-rust, which decodesStandardKeyMetadataand uses the key directly for Parquet decryption. No native KMS or JNI is needed (unlike plain-Parquet Modular Encryption in #2447, where the reader unwraps at read time).What changes are included in this PR?
58.4.0and iceberg-rust to4532da8for the encryption read path (feat(encryption) [9/N] Read encrypted parquet data-files iceberg-rust#2584 adds encrypted-data-file reads andFileScanTask::key_metadata; feat(encryption): support AES-256 keys for reading Parquet data files iceberg-rust#2878 adds AES-256 data keys).optional bytes key_metadataonIcebergFileScanTask(field 6) andIcebergDeleteFile(field 8; 5 to 7 reserved for a separate deletion-vector branch).CometIcebergNativeScan): forwardContentFile.keyMetadata()for data and delete files. Unset when null.FileScanTask/FileScanTaskDeleteFilekey_metadatafrom the proto.CometScanRule/IcebergReflection: allow format V3 (reject V4+); fall back for V3 default values, for unsupported column types (allow-list over the full table schema), and for 192-bit data keys (no AES-192-GCM in the underlying crypto; 128- and 256-bit are supported); keep the delete gate Parquet-only so deletion-vector tables still fall back. Table encryption itself is not a fallback reason.CometTestKMS(in-memoryKeyManagementClient, spark-4.1 source set sinceKeyManagementClientis public only from Iceberg 1.10 and encryption needs 1.11);spark-hiveadded as a test dependency to stand up an embedded Hive metastore.dev/diffs/iceberg/1.11.0.diff:testMissingRequiredWithoutDefaulttolerates Comet's exception wrapping.How are these changes tested?
standard_key_metadata_roundtrips:StandardKeyMetadataencode/decode round-trips (API-stability guard after the bump).decodes_java_produced_key_metadata: decodes a real Iceberg-Java blob and recovers the identical 16-byte DEK and AAD prefix (cross-language wire-format guard).CometIcebergEncryptionSuite.data file key_metadata carries a plaintext DEK that round-trips:StandardEncryptionManagerstores a plaintext (not wrapped) DEK inkey_metadataand the blob round-trips.CometIcebergEncryptionSuite.encrypted V3 table is read natively and matches Spark: end-to-end. Atype=hiveIceberg catalog (embedded Derby metastore) withCometTestKMScreates an encrypted V3 table; the test assertskey_metadatais populated, that results match Spark, and that the read runs throughCometIcebergNativeScanExec.CometIcebergEncryptionSuite.encrypted V3 table with a 256-bit data key is read natively and matches Spark: same, withencryption.data-key-length=32.CometIcebergEncryptionSuite.encrypted V3 table with a 192-bit data key falls back to Spark: a table withencryption.data-key-length=24falls back and still returns correct results.TestTableEncryptionalso runs under Comet in the iceberg-spark CI (viadev/diffs/iceberg/1.11.0.diff, which enables native Iceberg scan), reading a real encrypted V3 table.org.apache.iceberg.spark.sql.TestTableEncryptionlocally against Iceberg 1.11 with the diff applied (13 tests, all passing) and inspected the captured logs: theINFO org.apache.comet.serde.operator.CometIcebergNativeScan - IcebergScan: ...lines confirm the encrypted data reads (testSelectandtestInsertAndDelete) engageCometIcebergNativeScanrather than falling back. Those tests assert exact result sets, so the reads are decrypted correctly through the native path. The remaining fallbacks are metadata-table scans (ALL_DATA_FILES,MANIFESTS,all_manifests), which are unrelated to the data path.