Skip to content

feat(encryption): [11/N] Read encrypted Parquet data files - #4309

Open
xanderbailey wants to merge 1 commit into
apache:mainfrom
xanderbailey:xb/encryption-read-parquet
Open

xanderbailey wants to merge 1 commit into
apache:mainfrom
xanderbailey:xb/encryption-read-parquet

Conversation

@xanderbailey

Copy link
Copy Markdown
Contributor

Working towards #3222

Rationale for this change

We need to be able to read data files encrypted with Parquet Modular Encryption.

  • A data file's key_metadata in its manifest entry holds the plaintext key and AAD prefix for that file. The manifest holding it is itself encrypted, so this doesn't need the EncryptionManager or the KMS.
  • When key_metadata is set, scans build the Parquet file format with decryption properties from PyArrow's direct key API, pyarrow.parquet.encryption.create_decryption_properties (GH-47435: [Python][Parquet] Add direct key encryption/decryption API arrow#49667). This matches iceberg-rust, which builds arrow-rs FileDecryptionProperties from the key and AAD prefix ([9/N] feat(encryption) [9/N] Read encrypted parquet data-files iceberg-rust#2584).
  • This covers data files and Parquet position delete files. Encrypted ORC files raise, and deletion vectors come in a follow-up.
  • The AAD prefix always comes from key metadata, because Java doesn't store it in the file.

The direct key API was added in PyArrow 25, so reading an encrypted file on an older PyArrow raises an ImportError asking for 25.0.0 or later. The minimum PyArrow version for everything else is unchanged.

Are these changes tested?

Yes. A Parquet data file encrypted by iceberg-rust is checked in under tests/table/encryption, and reading it with its key metadata returns its rows. There's also a round trip of an encrypted data file with an encrypted position delete file. Reads fail with a wrong key, a wrong AAD prefix, missing key metadata, or key metadata on a plaintext file.

Are there any user-facing changes?

No. Nothing changes for unencrypted tables.

A data file's key_metadata holds its Parquet encryption key and AAD prefix,
so scans decrypt with pyarrow's direct key API when it is set, as iceberg-rust
does. That API needs pyarrow 25.
@xanderbailey
xanderbailey marked this pull request as ready for review November 8, 2026 18:24
Comment thread pyiceberg/io/pyarrow.py
from pyarrow.parquet import encryption

MIN_PYARROW_VERSION_SUPPORTING_PARQUET_DECRYPTION = "25.0.0"
if version.parse(pyarrow.__version__) < version.parse(MIN_PYARROW_VERSION_SUPPORTING_PARQUET_DECRYPTION):

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Not 100% sure what the blessed way to do this is...

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant