Skip to content

fix: verify that BC works with approved_only mode set#375

Open
mkleene wants to merge 61 commits into
mainfrom
enhance-fips-tests
Open

fix: verify that BC works with approved_only mode set#375
mkleene wants to merge 61 commits into
mainfrom
enhance-fips-tests

Conversation

@mkleene

@mkleene mkleene commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

The bouncycastle FIPS provider happily does things that are not FIPS-compliant when the
org.bouncycastle.fips.approved_only system property is not set to true. The following problems were observed:

  1. using OAEP to ENCRYPT rather than WRAP. We are indeed using OEAP for key wrapping so we just had
    to change some cipher parameters. WRAP and ENCRYPT perform the same operation; the only difference
    is in the semantics and the JCA API associated with each operation. This should not affect compatibility with other parts of the platform in any way.
  2. Without approved_only BC would be fine using a non-FIPS truststore. Now we add an empty truststore
    (with an empty password) of a type that can be loaded by the BC FIPS truststore.
  3. Our home grown implementation of HKDF didn't work because our usage of SHA-265/HMAC within the HKDF
    implementation triggers the error: ECKeyPairTest.createSymmetricKeysWithOtherCurves:129 » IllegalKey Key size for HMAC must be at least 112 bits in approved mode: SHA-256/HMAC. By using a FIPS compliant provider that implements HKDF directly we bypass this error.

We include the FIPS implementation of HKDF in a separate JAR due to its dependency on the bouncycastle FIPS provider. If we included it in the same JAR we would have a dependency on bc-fips, which would conflict with usage of the regular bouncycastle provider. Since bouncycastle is popular it seems best to allow non-FIPS usage.

The major change from a customer POV is that customers that want to use EC crypto in a FIPS context will now
need to pull in the sdk-fips-bouncycastle jar and depend on bc-fips since HKDF only got a JCA name
in JDK 24
and bc-fips does not expose HKDF through a provider interface. If using bc-fips is not appropriate
in a particular application it would be quite easy to implement an alternative JAR that implements a similar
provider pattern and uses whichever FIPS-compliant library one wishes to implement HKDF.

Also:

  • pull the test scripts in checks.yaml in to bash files so they can be run locally more easily
  • add a FIPS mode cmdline platform integration phase
  • publish the new FIPS provider JAR to maven central
  • remove double addition of generated sources that was generating a warning
  • overwrite the java.security file in its entirety so that we know exactly which providers we are using in FIPS tests

@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR adds FIPS-140 compliance by introducing a pluggable HKDF service provider interface (SPI), a BouncyCastle FIPS implementation, asymmetric key-wrapping semantics, and supporting build/test infrastructure for FIPS-enforced test runs, CLI verification, and matrix-based CI/CD workflows.

Changes

FIPS-Approved Encryption and Pluggable HKDF Provider

Layer / File(s) Summary
HKDF Service Provider Interface
sdk/src/main/java/io/opentdf/platform/sdk/HkdfProvider.java, sdk/src/main/java/io/opentdf/platform/sdk/HkdfResolver.java
Introduces HkdfProvider SPI and HkdfResolver for runtime discovery of HKDF implementations via ServiceLoader, enabling delegation to FIPS-approved or fallback JDK-native implementations.
BouncyCastle FIPS HKDF Provider Module
sdk-fips-bouncycastle/pom.xml, sdk-fips-bouncycastle/src/main/java/io/opentdf/platform/sdk/fips/bouncycastle/BouncyCastleFipsHkdfProvider.java, sdk-fips-bouncycastle/src/main/resources/META-INF/services/*, sdk-fips-bouncycastle/src/test/java/io/opentdf/platform/sdk/fips/bouncycastle/BouncyCastleFipsHkdfProviderTest.java
New module providing FIPS-140 approved HKDF via BouncyCastle FIPS KDF primitives; registered via ServiceLoader; tested for 32-byte output size, determinism, and null-salt handling.
ECKeyPair HKDF Provider Delegation and Test Updates
sdk/src/main/java/io/opentdf/platform/sdk/ECKeyPair.java, sdk/src/test/java/io/opentdf/platform/sdk/ECKeyPairTest.java
Updates calculateHKDF to resolve and delegate to an available HkdfProvider with debug logging; falls back to JDK-native HKDF when no provider is registered; simplifies test imports and removes unused exception declarations.
Asymmetric Key Wrapping Semantics
sdk/src/main/java/io/opentdf/platform/sdk/AsymEncryption.java, sdk/src/main/java/io/opentdf/platform/sdk/AsymDecryption.java
Changes from RSA encrypt/decrypt to key-wrapping semantics: AsymEncryption wraps plaintext as AES SecretKeySpec via WRAP_MODE; AsymDecryption unwraps via UNWRAP_MODE and returns extracted key bytes.
FIPS Build Profiles and Java Security Configuration
pom.xml, sdk/pom.xml, sdk/src/test/resources/java.security.fips.test, sdk/src/test/java/io/opentdf/platform/sdk/FipsProviderVerificationTest.java
Adds FIPS Maven profiles to root and cmdline modules; configures sdk FIPS profile with approved-only enforcement, FIPS keystore, trust store path, and BouncyCastle FIPS test-classpath dependency; bumps maven-surefire-plugin to 3.5.6; updates Java security test resource with explicit BouncyCastle FIPS provider ordering and FIPS keystore type; introduces FIPS-gated test suite verifying provider registration and HkdfResolver availability.
Command-line Module FIPS Configuration and Logging
cmdline/pom.xml, cmdline/src/main/java/io/opentdf/platform/Command.java, cmdline/src/main/resources/log4j2.xml
Adds FIPS profile for conditional FIPS dependency; adds SLF4J logging infrastructure; fixes Log4j2 XML element casing to match expected Log4j2 tag names.
CLI Roundtrip and Assertion Verification Scripts
.github/scripts/verify_cmdline_roundtrip.sh, .github/scripts/verify_assertions_basic.sh, .github/scripts/verify_assertions_hs256.sh, .github/scripts/verify_assertions_rs256.sh
Four new Bash scripts for CLI encrypt/decrypt validation: basic roundtrip with metadata extraction, predefined JSON assertions, HS256 signed assertions with generated HMAC keys, and RS256 signed assertions with generated RSA key pairs.
Workflow Matrix Build and Script Integration
.github/workflows/checks.yaml
Introduces job-level matrix to run FIPS and default build variants in parallel; updates FIPS Maven setup to install sdk-fips-bouncycastle module; removes prior unconditional build step and invokes shared verification scripts from parameterized matrix variants.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

The PR introduces multiple functional layers: a pluggable SPI for HKDF, a new FIPS module with implementation and tests, core asymmetric crypto changes from encrypt/decrypt to key-wrapping semantics, FIPS configuration across multiple POM files and test resources, and CI/CD matrix workflow changes. While individual layers are well-scoped, the interdependencies and crypto semantic changes demand careful review of key wrapping logic, FIPS provider registration, fallback behavior, and verification script correctness.

Possibly related PRs

  • opentdf/java-sdk#367: Both PRs directly modify HKDF derivation in ECKeyPair.java and FIPS test security configuration; this PR adds SPI-based pluggable delegation while the related PR refactors HKDF implementation.

Poem

🐰 A FIPS-approved journey hops in place,
Key wrapping replaces bare encrypt's embrace,
SPI discovery springs at runtime's call,
While BouncyCastle FIPS protects them all! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 19.05% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately describes the main objective: verifying BouncyCastle works with FIPS approved-only mode, which is the core issue this PR addresses throughout multiple files and modules.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch enhance-fips-tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces FipsProviderVerificationTest.java to verify that the FIPS security provider configuration is correctly loaded. A review comment points out that the system property org.bouncycastle.fips.approved_only is not configured in the fips profile in sdk/pom.xml, which would cause the test to be silently skipped, and provides a configuration fix.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

@github-actions

github-actions Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

mkleene and others added 2 commits June 9, 2026 14:54
Switch AsymEncryption from ENCRYPT_MODE+doFinal to WRAP_MODE+wrap,
and AsymDecryption from DECRYPT_MODE+doFinal to UNWRAP_MODE+unwrap,
treating the key material as AES SecretKeySpec for FIPS compatibility.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

@github-actions

Copy link
Copy Markdown
Contributor

@github-actions

Copy link
Copy Markdown
Contributor

@github-actions

Copy link
Copy Markdown
Contributor

@github-actions

Copy link
Copy Markdown
Contributor

X-Test Failure Report

@github-actions

Copy link
Copy Markdown
Contributor

@github-actions

Copy link
Copy Markdown
Contributor

X-Test Failure Report

@github-actions

Copy link
Copy Markdown
Contributor

X-Test Failure Report

@github-actions

Copy link
Copy Markdown
Contributor

X-Test Failure Report

@github-actions

Copy link
Copy Markdown
Contributor

X-Test Failure Report

@github-actions

Copy link
Copy Markdown
Contributor

X-Test Failure Report

@github-actions

Copy link
Copy Markdown
Contributor

X-Test Failure Report

@github-actions

Copy link
Copy Markdown
Contributor

@github-actions

Copy link
Copy Markdown
Contributor

X-Test Failure Report

@github-actions

Copy link
Copy Markdown
Contributor

@github-actions

Copy link
Copy Markdown
Contributor

@mkleene mkleene changed the title fix: verify that bc works with approved_only mode set fix: verify that BC works with approved_only mode set Jun 15, 2026
@github-actions

Copy link
Copy Markdown
Contributor

X-Test Failure Report

@github-actions

Copy link
Copy Markdown
Contributor

@github-actions

Copy link
Copy Markdown
Contributor

@github-actions

Copy link
Copy Markdown
Contributor

pflynn-virtru
pflynn-virtru previously approved these changes Jun 17, 2026

@pflynn-virtru pflynn-virtru left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nice to have an interop test with FIPS and non-FIPS

@marythought marythought left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

DX Review — release / migration impact

The FIPS fixes are correct, but this is a breaking change for existing FIPS + EC deployments and the release process needs to surface that.

1. Migration note via release-please

Since this repo uses release-please, the squash-merge commit message needs a BREAKING CHANGE: footer so it appears in the generated release notes. Something like:

BREAKING CHANGE: FIPS deployments using EC key wrapping must add
`io.opentdf.platform:sdk-fips-bouncycastle` to their classpath.
Without it, HKDF key derivation fails under
`org.bouncycastle.fips.approved_only=true`.

Without this footer, the change ships as a minor fix: and FIPS consumers discover the new dependency requirement at runtime.

2. Failure mode isn't discoverable

If a FIPS consumer doesn't add the jar: HkdfResolver.get() returns null → calculateHKDF falls through to the JDK-native HmacSHA256 → same IllegalKey error as before. The error doesn't mention sdk-fips-bouncycastle or how to fix it. Consider logging a warning when approved_only=true is set but no HkdfProvider resolves — that would point people to the fix instead of leaving them with a raw BC exception.

3. Wire compatibility of ENCRYPT→WRAP cipher mode change

The AsymEncryption/AsymDecryption change from ENCRYPT_MODE/DECRYPT_MODE to WRAP_MODE/UNWRAP_MODE should be wire-compatible (OAEP produces the same bytes either way), but worth confirming in the release notes that existing TDFs encrypted with the old mode can still be decrypted — this is the kind of thing a security-conscious consumer will ask about.

sujankota
sujankota previously approved these changes Jun 17, 2026
@mkleene mkleene dismissed stale reviews from sujankota and pflynn-virtru via 6cfcb9e June 17, 2026 21:24
@mkleene

mkleene commented Jun 17, 2026

Copy link
Copy Markdown
Contributor Author

DX Review — release / migration impact

The FIPS fixes are correct, but this is a breaking change for existing FIPS + EC deployments and the release process needs to surface that.

1. Migration note via release-please

Since this repo uses release-please, the squash-merge commit message needs a BREAKING CHANGE: footer so it appears in the generated release notes. Something like:

BREAKING CHANGE: FIPS deployments using EC key wrapping must add
`io.opentdf.platform:sdk-fips-bouncycastle` to their classpath.
Without it, HKDF key derivation fails under
`org.bouncycastle.fips.approved_only=true`.

Without this footer, the change ships as a minor fix: and FIPS consumers discover the new dependency requirement at runtime.

2. Failure mode isn't discoverable

If a FIPS consumer doesn't add the jar: HkdfResolver.get() returns null → calculateHKDF falls through to the JDK-native HmacSHA256 → same IllegalKey error as before. The error doesn't mention sdk-fips-bouncycastle or how to fix it. Consider logging a warning when approved_only=true is set but no HkdfProvider resolves — that would point people to the fix instead of leaving them with a raw BC exception.

3. Wire compatibility of ENCRYPT→WRAP cipher mode change

The AsymEncryption/AsymDecryption change from ENCRYPT_MODE/DECRYPT_MODE to WRAP_MODE/UNWRAP_MODE should be wire-compatible (OAEP produces the same bytes either way), but worth confirming in the release notes that existing TDFs encrypted with the old mode can still be decrypted — this is the kind of thing a security-conscious consumer will ask about.

  1. I don't think that this is necessary since it never could have worked in approved_only mode. It will continue to work in non-approved mode and now there is a way to get it working in approved_only mode.
  2. added a test. well, a disabled test. I can't think of a way to add test that runs without a whole new profile and that seems a bit much for this.
  3. This should be covered by our existing golden-file tests, at least for decryption. The fact that this works with the unmodified platform verifieds that encryption will also work with older versions of the platform.

@mkleene mkleene requested a review from marythought June 17, 2026 21:28
@github-actions

Copy link
Copy Markdown
Contributor

@sonarqubecloud

Copy link
Copy Markdown

@github-actions

Copy link
Copy Markdown
Contributor

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.

4 participants