Skip to content

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

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

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

Conversation

@mkleene

@mkleene mkleene commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

The bouncycastle FIPS provider can do things that do not strictly conform to FIPS when the
org.bouncycastle.fips.approved_only system property is not set to true.

Two things that failed under this stricter regime were

  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.
  2. Our home grown implementation of HKDF didn't work because our usage of SHA-265/HMAC 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.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added FIPS-140 compliance support via BouncyCastle FIPS module
    • Asymmetric encryption now uses key-wrapping semantics for enhanced security
    • Introduced pluggable HKDF provider interface for FIPS-approved cryptographic implementations
  • Bug Fixes

    • Fixed Log4j2 configuration to use correct case-sensitive element tags
  • Tests

    • Added FIPS provider verification test suite
    • Added comprehensive end-to-end encryption roundtrip and assertions verification test scripts

@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 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, allowing 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/.../*
New module providing FIPS-140 approved HKDF via BouncyCastle FIPS KDF primitives; registered via ServiceLoader; tested for 32-byte output size and determinism.
ECKeyPair HKDF Provider Delegation
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; test imports updated for conditional FIPS execution.
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, cmdline/pom.xml, sdk/pom.xml, sdk/src/test/resources/java.security.fips.test
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; updates Java security test resource with explicit provider ordering and FIPS keystore type.
FIPS Provider Verification and Test Updates
sdk/src/test/java/io/opentdf/platform/sdk/FipsProviderVerificationTest.java
Introduces FIPS-gated test suite verifying provider registration order, KeyManagerFactory algorithm, and HkdfResolver availability when FIPS mode is active.
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, and RS256 signed assertions with generated key material.
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; replaces inline shell test logic with parameterized invocations of verification scripts.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 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, and fallback behavior.

Possibly related PRs

  • opentdf/java-sdk#367: Both PRs directly touch HKDF derivation in ECKeyPair.java (this PR adds SPI-based delegation; retrieved PR rewrites HKDF away from BouncyCastle).

Suggested reviewers

  • marythought
  • jentfoo

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 22.22% 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
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.
Title check ✅ Passed The title 'fix: verify that bc works with approved_only mode set' directly aligns with the PR's primary objective to ensure BouncyCastle operates correctly in FIPS approved-only mode, as evidenced by the new FipsProviderVerificationTest and related configuration changes.
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

@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

mkleene added 2 commits June 12, 2026 14:09
Removed static logger initialization from TDFReader class.
@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

Removed unnecessary blank lines for cleaner code.
@github-actions

Copy link
Copy Markdown
Contributor

X-Test Failure Report

@mkleene mkleene marked this pull request as ready for review June 12, 2026 18:16
@mkleene mkleene requested review from a team as code owners June 12, 2026 18:16

@coderabbitai coderabbitai 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.

Actionable comments posted: 3

🧹 Nitpick comments (4)
sdk-fips-bouncycastle/pom.xml (1)

35-40: ⚡ Quick win

Centralize AssertJ version in parent dependencyManagement.

The AssertJ version 3.27.7 is hardcoded here and also appears hardcoded in sdk/pom.xml:180. For consistency and easier maintenance, define it in the parent POM's dependencyManagement section (similar to how bc-fips, JUnit, and other dependencies are managed).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sdk-fips-bouncycastle/pom.xml` around lines 35 - 40, The AssertJ version is
hardcoded for org.assertj:assertj-core in this module; remove the
<version>3.27.7</version> here and instead add a managed version entry in the
parent POM's dependencyManagement (e.g., add an
<dependencyManagement><dependencies><dependency> with groupId org.assertj,
artifactId assertj-core and the version 3.27.7). Then update any other POMs that
currently hardcode assertj-core (such as the sdk/pom.xml entry) to rely on the
parent-managed version by omitting the <version> element and keeping only the
dependency coordinates.
sdk-fips-bouncycastle/src/test/java/io/opentdf/platform/sdk/fips/bouncycastle/BouncyCastleFipsHkdfProviderTest.java (1)

9-33: ⚡ Quick win

Consider adding edge case tests for robustness.

The current tests cover the happy path well, but consider adding tests for edge cases to improve robustness:

  • Null salt (RFC 5869 specifies zero-filled buffer substitution)
  • Empty salt
  • Null secret (expected behavior: throw or handle gracefully?)
  • Empty secret
  • Known test vectors from RFC 5869 for cross-validation
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@sdk-fips-bouncycastle/src/test/java/io/opentdf/platform/sdk/fips/bouncycastle/BouncyCastleFipsHkdfProviderTest.java`
around lines 9 - 33, Add unit tests in BouncyCastleFipsHkdfProviderTest that
exercise edge cases for BouncyCastleFipsHkdfProvider.computeHKDF: verify
behavior when salt is null (expect provider to treat it as an RFC-5869
zero-filled salt), when salt is empty, when secret is null (assert expected
exception or documented handling), when secret is empty, and add at least one
known RFC-5869 test vector to validate output against a canonical value; use the
existing test structure/method names (computeHKDF_returns32Bytes,
computeHKDF_isDeterministic) as examples for naming and assertions.
.github/scripts/verify_assertions_rs256.sh (1)

23-23: 💤 Low value

Inconsistent argument quoting style across verification scripts.

This script uses --with-assertions "$SIGNED_ASSERTIONS_RS256" (space-separated, line 23) while verify_assertions_basic.sh and verify_assertions_hs256.sh use --with-assertions="$ASSERTIONS" (equals-separated, no space). Both forms work in Bash, but consistency would improve maintainability.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/scripts/verify_assertions_rs256.sh at line 23, Change the
inconsistent argument quoting in the RS256 script: replace the space-separated
option form --with-assertions "$SIGNED_ASSERTIONS_RS256" with the equals form
--with-assertions="$SIGNED_ASSERTIONS_RS256" to match verify_assertions_basic.sh
and verify_assertions_hs256.sh; update the line in
.github/scripts/verify_assertions_rs256.sh so the option uses the equals sign
with the quoted variable.
sdk/src/main/java/io/opentdf/platform/sdk/ECKeyPair.java (1)

119-125: 💤 Low value

Consider the overhead of debug logging on every HKDF call.

The provider resolution and debug logging occur on every invocation of calculateHKDF. While HkdfResolver.get() returns a cached singleton, the null check and debug log at lines 121-123 execute on every call. If HKDF derivation is performance-sensitive or called frequently, consider whether debug-level logging here adds measurable overhead.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sdk/src/main/java/io/opentdf/platform/sdk/ECKeyPair.java` around lines 119 -
125, The debug check and provider resolution are executed on every calculateHKDF
call; to avoid per-call overhead, resolve and cache the HkdfProvider once (via
HkdfResolver.get()) and perform the log.debug(...) only when the provider is
initially obtained or changes rather than inside every calculateHKDF invocation;
update calculateHKDF to use the cached provider (HkdfProvider) and remove the
repeated log.isDebugEnabled() check on the hot path, or cache
log.isDebugEnabled() in a static/instance boolean so only
provider.computeHKDF(salt, secret) runs on each call.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/checks.yaml:
- Line 96: Fix the duplicated word in the comment string "install the the
sdk-fips-bouncycastle jar so that FIPS mode tests work" by changing it to
"install the sdk-fips-bouncycastle jar so that FIPS mode tests work"; locate and
update the comment in .github/workflows/checks.yaml (the comment containing
"install the the sdk-fips-bouncycastle jar") so the duplicated "the" is removed.

In
`@sdk-fips-bouncycastle/src/main/java/io/opentdf/platform/sdk/fips/bouncycastle/BouncyCastleFipsHkdfProvider.java`:
- Around line 14-18: BouncyCastleFipsHkdfProvider.computeHKDF currently forwards
salt/secret directly to FipsKDF; make its behavior match the JDK fallback by
normalizing salt==null or salt.length==0 to a 32-byte zero array before calling
.withSalt(...), and explicitly validate secret (e.g., throw a clear
NPE/IllegalArgumentException or document acceptance) so
HkdfProvider.computeHKDF, BouncyCastleFipsHkdfProvider.computeHKDF and
ECKeyPair.calculateHKDF have consistent null/empty handling; update computeHKDF
to perform the salt normalization and secret null-check prior to calling
.build(secret).

In `@sdk/src/test/java/io/opentdf/platform/sdk/ECKeyPairTest.java`:
- Line 7: Remove the unused import of DisabledIfSystemProperty from the
ECKeyPairTest class: locate the import statement referencing
DisabledIfSystemProperty and delete it so the class ECKeyPairTest no longer
contains an unused import; run the test compile to verify there are no remaining
unused-import warnings.

---

Nitpick comments:
In @.github/scripts/verify_assertions_rs256.sh:
- Line 23: Change the inconsistent argument quoting in the RS256 script: replace
the space-separated option form --with-assertions "$SIGNED_ASSERTIONS_RS256"
with the equals form --with-assertions="$SIGNED_ASSERTIONS_RS256" to match
verify_assertions_basic.sh and verify_assertions_hs256.sh; update the line in
.github/scripts/verify_assertions_rs256.sh so the option uses the equals sign
with the quoted variable.

In `@sdk-fips-bouncycastle/pom.xml`:
- Around line 35-40: The AssertJ version is hardcoded for
org.assertj:assertj-core in this module; remove the <version>3.27.7</version>
here and instead add a managed version entry in the parent POM's
dependencyManagement (e.g., add an
<dependencyManagement><dependencies><dependency> with groupId org.assertj,
artifactId assertj-core and the version 3.27.7). Then update any other POMs that
currently hardcode assertj-core (such as the sdk/pom.xml entry) to rely on the
parent-managed version by omitting the <version> element and keeping only the
dependency coordinates.

In
`@sdk-fips-bouncycastle/src/test/java/io/opentdf/platform/sdk/fips/bouncycastle/BouncyCastleFipsHkdfProviderTest.java`:
- Around line 9-33: Add unit tests in BouncyCastleFipsHkdfProviderTest that
exercise edge cases for BouncyCastleFipsHkdfProvider.computeHKDF: verify
behavior when salt is null (expect provider to treat it as an RFC-5869
zero-filled salt), when salt is empty, when secret is null (assert expected
exception or documented handling), when secret is empty, and add at least one
known RFC-5869 test vector to validate output against a canonical value; use the
existing test structure/method names (computeHKDF_returns32Bytes,
computeHKDF_isDeterministic) as examples for naming and assertions.

In `@sdk/src/main/java/io/opentdf/platform/sdk/ECKeyPair.java`:
- Around line 119-125: The debug check and provider resolution are executed on
every calculateHKDF call; to avoid per-call overhead, resolve and cache the
HkdfProvider once (via HkdfResolver.get()) and perform the log.debug(...) only
when the provider is initially obtained or changes rather than inside every
calculateHKDF invocation; update calculateHKDF to use the cached provider
(HkdfProvider) and remove the repeated log.isDebugEnabled() check on the hot
path, or cache log.isDebugEnabled() in a static/instance boolean so only
provider.computeHKDF(salt, secret) runs on each call.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c41c71a4-8315-4746-ae6d-c490b8931c6f

📥 Commits

Reviewing files that changed from the base of the PR and between 63b49af and a8c39b4.

📒 Files selected for processing (24)
  • .github/scripts/verify_assertions_basic.sh
  • .github/scripts/verify_assertions_hs256.sh
  • .github/scripts/verify_assertions_rs256.sh
  • .github/scripts/verify_cmdline_roundtrip.sh
  • .github/workflows/checks.yaml
  • cmdline/pom.xml
  • cmdline/src/main/java/io/opentdf/platform/Command.java
  • cmdline/src/main/resources/log4j2.xml
  • pom.xml
  • 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/io.opentdf.platform.sdk.HkdfProvider
  • sdk-fips-bouncycastle/src/test/java/io/opentdf/platform/sdk/fips/bouncycastle/BouncyCastleFipsHkdfProviderTest.java
  • sdk/pom.xml
  • sdk/src/main/java/io/opentdf/platform/sdk/AsymDecryption.java
  • sdk/src/main/java/io/opentdf/platform/sdk/AsymEncryption.java
  • sdk/src/main/java/io/opentdf/platform/sdk/ECKeyPair.java
  • sdk/src/main/java/io/opentdf/platform/sdk/HkdfProvider.java
  • sdk/src/main/java/io/opentdf/platform/sdk/HkdfResolver.java
  • sdk/src/test/java/io/opentdf/platform/sdk/ECKeyPairTest.java
  • sdk/src/test/java/io/opentdf/platform/sdk/FipsProviderVerificationTest.java
  • sdk/src/test/resources/empty-fips-truststore.bcfks
  • sdk/src/test/resources/empty-fips-truststore.p12
  • sdk/src/test/resources/java.security.fips.test

- name: Tests and enforcer (fips)
run: mvn --batch-mode test enforcer:enforce -P 'fips,!non-fips' -Dmaven.antrun.skip
run: |
# install the the sdk-fips-bouncycastle jar so that FIPS mode tests work

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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix typo in comment.

The comment contains a duplicated word: "install the the sdk-fips-bouncycastle jar" should be "install the sdk-fips-bouncycastle jar".

📝 Proposed fix
-          # install the the sdk-fips-bouncycastle jar so that FIPS mode tests work
+          # install the sdk-fips-bouncycastle jar so that FIPS mode tests work
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# install the the sdk-fips-bouncycastle jar so that FIPS mode tests work
# install the sdk-fips-bouncycastle jar so that FIPS mode tests work
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/checks.yaml at line 96, Fix the duplicated word in the
comment string "install the the sdk-fips-bouncycastle jar so that FIPS mode
tests work" by changing it to "install the sdk-fips-bouncycastle jar so that
FIPS mode tests work"; locate and update the comment in
.github/workflows/checks.yaml (the comment containing "install the the
sdk-fips-bouncycastle jar") so the duplicated "the" is removed.

Comment on lines +14 to +18
public byte[] computeHKDF(byte[] salt, byte[] secret) {
var key = FipsKDF.HKDF_KEY_BUILDER
.withPrf(FipsKDF.AgreementKDFPRF.SHA256_HMAC)
.withSalt(salt)
.build(secret);

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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check HkdfProvider interface for null-handling documentation or validation patterns

# Search for HkdfProvider interface definition and javadoc
rg -A 10 -B 2 'interface HkdfProvider' sdk/src/main/java/

# Check if other HKDF call sites in ECKeyPair handle null
rg -A 3 -B 3 'calculateHKDF' sdk/src/main/java/io/opentdf/platform/sdk/ECKeyPair.java

Repository: opentdf/java-sdk

Length of output: 1202


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show HkdfProvider javadoc + method signature
sed -n '1,120p' sdk/src/main/java/io/opentdf/platform/sdk/HkdfProvider.java

# Show ECKeyPair.calculateHKDF (and any null checks around it)
rg -n "calculateHKDF\\(" sdk/src/main/java/io/opentdf/platform/sdk/ECKeyPair.java
sed -n '1,220p' sdk/src/main/java/io/opentdf/platform/sdk/ECKeyPair.java

# Show computeHKDF implementation in the FIPS provider
sed -n '1,120p' sdk-fips-bouncycastle/src/main/java/io/opentdf/platform/sdk/fips/bouncycastle/BouncyCastleFipsHkdfProvider.java

Repository: opentdf/java-sdk

Length of output: 9432


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find all call sites of calculateHKDF
rg -n "calculateHKDF\\(" -S .

# Look for any explicit null checks or defaulting for salt/secret at call sites
rg -n "calculateHKDF\\(.*null|null.*\\)" -S .

# Check HkdfResolver (in case it wraps/validates inputs)
rg -n "class HkdfResolver|interface HkdfResolver|HkdfResolver" sdk/src/main/java/io/opentdf/platform/sdk -S

Repository: opentdf/java-sdk

Length of output: 41640


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show where GLOBAL_KEY_SALT is defined (and its value)
rg -n "GLOBAL_KEY_SALT" sdk/src/main/java/io/opentdf/platform/sdk -S
rg -n "GLOBAL_KEY_SALT" sdk/src/test/java/io/opentdf/platform/sdk -S

# Inspect TDF/KASClient around HKDF usage and the constant definition context
sed -n '200,320p' sdk/src/main/java/io/opentdf/platform/sdk/TDF.java
sed -n '140,240p' sdk/src/main/java/io/opentdf/platform/sdk/KASClient.java

# Check tests for null salt/secret behavior
rg -n "calculateHKDF\\(null|computeHKDF\\(null|salt.*null|secret.*null" sdk/src/test/java/io/opentdf/platform/sdk -S
sed -n '1,220p' sdk/src/test/java/io/opentdf/platform/sdk/ECKeyPairTest.java

Repository: opentdf/java-sdk

Length of output: 10374


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect HkdfResolver
sed -n '1,160p' sdk/src/main/java/io/opentdf/platform/sdk/HkdfResolver.java

# Check whether there are any tests for null salt/secret in HKDF
rg -n "calculateHKDF\\(.*null|computeHKDF\\(.*null" sdk/src/test/java/io/opentdf/platform/sdk -S

# Inspect ECKeyPairTest around HKDF
sed -n '1,240p' sdk/src/test/java/io/opentdf/platform/sdk/ECKeyPairTest.java

Repository: opentdf/java-sdk

Length of output: 770


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect ECKeyPairTest around HKDF
sed -n '1,240p' sdk/src/test/java/io/opentdf/platform/sdk/ECKeyPairTest.java

# Search for any HKDF null-input tests (allow empty result)
rg -n "computeHKDF\\(|calculateHKDF\\(" sdk/src/test/java/io/opentdf/platform/sdk/ECKeyPairTest.java sdk/src/test/java/io/opentdf/platform/sdk/TDFTest.java sdk/src/test/java/io/opentdf/platform/sdk/KASClientTest.java || true
rg -n "calculateHKDF\\(\\s*(null|\\{)\\s*|computeHKDF\\(\\s*(null|\\{)\\s*" sdk/src/test/java/io/opentdf/platform/sdk -S || true

# Also inspect the provider tests (if any)
fd -e java sdk/src/test/java/io/opentdf/platform/sdk | rg -n "Fips|Bouncy|Hkdf|Provider" || true

Repository: opentdf/java-sdk

Length of output: 9597


Align HKDF null-handling contract across JDK fallback and FIPS provider

ECKeyPair.calculateHKDF supports salt == null (it replaces null/empty salt with 32 zero bytes) when using the JDK fallback, but when an HkdfProvider is resolved it forwards salt/secret directly to HkdfProvider.computeHKDF(...). BouncyCastleFipsHkdfProvider.computeHKDF (lines 14-18) passes salt into .withSalt(salt) and secret into .build(secret) without any null checks, and HkdfProvider’s interface Javadoc doesn’t specify null expectations. Update the FIPS provider (or the resolver/wrapper) to match the fallback behavior for salt, and validate/document secret null-handling so FIPS and non-FIPS modes behave consistently.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@sdk-fips-bouncycastle/src/main/java/io/opentdf/platform/sdk/fips/bouncycastle/BouncyCastleFipsHkdfProvider.java`
around lines 14 - 18, BouncyCastleFipsHkdfProvider.computeHKDF currently
forwards salt/secret directly to FipsKDF; make its behavior match the JDK
fallback by normalizing salt==null or salt.length==0 to a 32-byte zero array
before calling .withSalt(...), and explicitly validate secret (e.g., throw a
clear NPE/IllegalArgumentException or document acceptance) so
HkdfProvider.computeHKDF, BouncyCastleFipsHkdfProvider.computeHKDF and
ECKeyPair.calculateHKDF have consistent null/empty handling; update computeHKDF
to perform the salt normalization and secret null-check prior to calling
.build(secret).

import java.security.interfaces.ECPublicKey;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledIfSystemProperty;

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.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check if DisabledIfSystemProperty is actually used in the test file

rg -n 'DisabledIfSystemProperty' sdk/src/test/java/io/opentdf/platform/sdk/ECKeyPairTest.java -C 3

Repository: opentdf/java-sdk

Length of output: 288


Remove unused DisabledIfSystemProperty import in ECKeyPairTest

DisabledIfSystemProperty only appears as an import (sdk/src/test/java/io/opentdf/platform/sdk/ECKeyPairTest.java:7) and is not referenced anywhere else in the test class.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sdk/src/test/java/io/opentdf/platform/sdk/ECKeyPairTest.java` at line 7,
Remove the unused import of DisabledIfSystemProperty from the ECKeyPairTest
class: locate the import statement referencing DisabledIfSystemProperty and
delete it so the class ECKeyPairTest no longer contains an unused import; run
the test compile to verify there are no remaining unused-import warnings.

@github-actions

Copy link
Copy Markdown
Contributor

@sonarqubecloud

Copy link
Copy Markdown

@github-actions

Copy link
Copy Markdown
Contributor

@mkleene mkleene changed the title fix: set bc in approved only fix: verify that bc works with approved_only mode set Jun 12, 2026
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