Skip to content

GH-3934: Encrypt DataPageV2 repetition and definition levels together with the values - #3935

Draft
RussellSpitzer wants to merge 6 commits into
apache:masterfrom
RussellSpitzer:fix/pme-datapagev2-encrypt-levels
Draft

RussellSpitzer wants to merge 6 commits into
apache:masterfrom
RussellSpitzer:fix/pme-datapagev2-encrypt-levels

Conversation

@RussellSpitzer

Copy link
Copy Markdown
Member

Rationale for this change

Parquet-java seems to be unique amongst several other implementations in that it encrypts only the values of DataPageV2 structures separate from repetition and definition levels. All the other implementations (rs, c++, go, duckdb) assume that a DataPageV2 is encrypted as a single module with the the information together.

What changes are included in this PR?

The main change is that all new files will be written encrypting the entire DataPageV2 as a single entity rather than storing levels in plaintext.

Readers are changed to support a fully encrypted DataPageV2 as well as falling back to reading encrypted pages with the previous behavior.

Are these changes tested?

Yes.

Files were generated with both whole module encryption and only encrypting the values. Both are included as test cases

Are there any user-facing changes?

Yes. This is a breaking change for users of Parquet-Java.

Encrypted Files written by the next version of Parquet Java (currently 1.9.0) will be unable to be read by Parquet 1.8 and earlier. They will though be readable by all the other Parquet implementations. Encrypted files written by 1.8 and earlier will remain readable by 1.9.

Closes #3934

This pr was written with the assistance of Claude Opus 5.5 and I haven't fully finished my review which is why I'm leaving this as a draft.

…gether with the values

The data page module of an encrypted DataPageV2 page is the whole page body.
Arrow C++ and arrow-rs encrypt [repetition levels | definition levels | values]
as one module, but parquet-java left the levels in plaintext and encrypted only
the values, so the two could not read each other's files whenever a page had
repetition or definition levels, and the levels leaked the null and list
structure of encrypted columns.

parquet-java now writes the single module layout, in ColumnChunkPageWriteStore
and in ParquetRewriter, through the new ParquetFileWriter.writeEncryptedDataPageV2.

The reader recognizes both layouts, so files written by earlier versions can
still be read. An encrypted module starts with its own length, so a page body
that starts with the body length minus four is a single module; otherwise the
levels are in plaintext in front of the module of the values.

Tests read files written by Arrow and by the previous parquet-java layout, check
what parquet-java and ParquetRewriter write with a reference decoder written
from the specification, and check that tampering with levels is detected.
- detect the page layout with both markers, resolve ambiguous GCM pages by
  the tag check and prefer the legacy layout for unauthenticated CTR pages
- validate the level lengths of the page header
- replace the public writeEncryptedDataPageV2 by a writeDataPageV2 overload
  that takes the page block encryptor, so the writer owns the layout
- tests: checksums on reads, several row groups, page skipping, a crafted
  ambiguous legacy page, leak tracking, KMS cache isolation
- document the compatibility change in the parquet-hadoop README
…2 levels in plaintext

The writeDataPageV2 overloads that took a page header encryptor but no page
block encryptor let callers encrypt only the values, which is the layout of
apache#3934. They are removed (with japicmp excludes); the
remaining overload rejects a header encryptor without a page block encryptor.
The hand-built ambiguous legacy pages are now fixture files.
BytesInput dlLevels = readBlockAllocate(dlLength, reader);
int payLoadLength = pageHeader.getCompressed_page_size() - rlLength - dlLength;
int rawDataLength = pageHeader.getUncompressed_page_size() - rlLength - dlLength;
// Not encrypted here: the writer encrypts the levels and the payload together

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This is set up so that if you happen to use the rewriter, we will fix the encryption from the old version to the new one.

…ds instead of removing them

Restores the four writeDataPageV2 overloads that take only a page header
encryptor, deprecated with a warning that they leave the levels of an
encrypted column in plaintext, and drops their japicmp excludes. Reads
DataPageV2 pages with a switch on the detected layout, notes that the
plaintext-levels read path exists for backward compatibility only, and
passes the CRC to the fallback page in FullBodyEncryptedDataPageV2.
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.

PME DataPageV2: parquet-java leaves rep/def levels in plaintext (Interop failure with Arrow)

1 participant