Skip to content

MINOR: Use constant-time comparison for signed footer GCM tag#3655

Open
anxkhn wants to merge 1 commit into
apache:masterfrom
anxkhn:patch-4
Open

MINOR: Use constant-time comparison for signed footer GCM tag#3655
anxkhn wants to merge 1 commit into
apache:masterfrom
anxkhn:patch-4

Conversation

@anxkhn

@anxkhn anxkhn commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Rationale for this change

When an encrypted file is written with a plaintext (signed) footer, the footer
carries an AES-GCM authentication tag. On read,
ParquetMetadataConverter.verifyFooterIntegrity recomputes the tag and compares
it against the stored one to detect tampering.

That comparison used java.util.Arrays.equals, which returns as soon as it hits
a mismatching byte, so its running time depends on how many leading bytes match.
Comparing a secret authentication tag this way is a timing side channel
(CWE-208): an attacker who can measure verification time over many crafted files
could, in principle, recover the correct tag byte by byte. Constant-time
comparison is the standard practice for MAC/authentication-tag checks.

What changes are included in this PR?

  • In ParquetMetadataConverter.verifyFooterIntegrity, replace
    Arrays.equals(gcmTag, calculatedTag) with
    MessageDigest.isEqual(gcmTag, calculatedTag) and add
    import java.security.MessageDigest;. A short comment notes the CWE-208
    rationale. MessageDigest.isEqual has the same boolean contract (true iff both
    arrays are non-null, of equal length, and byte-for-byte equal) but is
    implemented to run in time independent of the number of matching bytes, so the
    change is behavior-preserving: tampered footers are still rejected with a
    TagVerificationException. (The Arrays import stays; it is used elsewhere in
    the file.)
  • Add TestFooterIntegrity in parquet-hadoop covering the signed
    plaintext-footer path end to end: a valid signature is accepted, and a footer
    whose GCM tag has one byte flipped is rejected with TagVerificationException.

The other Arrays.equals calls in the crypto module compare non-secret
identifiers (AAD prefix, key-metadata reuse checks), not a secret MAC, so they
are intentionally left unchanged.

Are these changes tested?

Yes.

  • mvn -pl parquet-hadoop -Dtest=TestFooterIntegrity test -> both cases pass.
  • The existing encryption suites still pass with the change:
    mvn -pl parquet-hadoop -Dtest='ColumnEncryptorTest,TestPropertiesDrivenEncryption,TestFooterIntegrity' test
    -> 43 tests, 0 failures, 0 errors.
  • The negative test was confirmed to be meaningful: temporarily neutralizing the
    tag check makes tamperedFooterSignatureIsRejected fail (nothing thrown),
    which shows the tampered bytes actually reach the reader and the tag check is
    what rejects them.

A true timing measurement is impractical to assert deterministically in a unit
test, so this PR verifies functional equivalence (accept valid, reject tampered)
rather than the timing property itself.

Are there any user-facing changes?

No. This is an internal hardening of the footer-signature check with identical
observable behavior (same acceptance and rejection outcomes, same exception on
mismatch). No API, format, or configuration changes.

The signature of an encrypted file with a plaintext (signed) footer is
verified in ParquetMetadataConverter.verifyFooterIntegrity by comparing
the stored GCM authentication tag against a recomputed tag. It used
java.util.Arrays.equals, which returns as soon as it finds a mismatching
byte, so the comparison time depends on how many leading bytes match. For
a MAC/authentication-tag check that is a timing side channel (CWE-208).

Replace it with java.security.MessageDigest.isEqual, which has the same
boolean contract (true iff both arrays are non-null, of equal length, and
byte-for-byte equal) but runs in time that does not depend on the number
of matching bytes. The change is behavior-preserving; tampered footers are
still rejected with a TagVerificationException.

Add TestFooterIntegrity covering both the accepted valid signature and a
tampered GCM tag that must be rejected.

Signed-off-by: Anas Khan <83116240+anxkhn@users.noreply.github.com>
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