Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions cmdline/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,26 @@
<version>${project.version}</version>
</dependency>
</dependencies>

<profiles>
<!-- Pulls the BouncyCastle KemProvider onto the classpath at runtime so
cmdline supports hybrid PQC (X-Wing, ECDH+ML-KEM). Gated behind
non-fips to mirror the reactor gating in the root pom: a fips
build does NOT include sdk-pqc-bc, so cmdline must not declare
a hard dep on it under fips either. -->
<profile>
<id>non-fips</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<dependencies>
<dependency>
<groupId>io.opentdf.platform</groupId>
<artifactId>sdk-pqc-bc</artifactId>
<version>${project.version}</version>
<scope>runtime</scope>
</dependency>
</dependencies>
</profile>
</profiles>
</project>
14 changes: 14 additions & 0 deletions pom.xml
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.

I think it's ok for this to work this way but it might be simpler to do dependency management inside the commandline module in order to keep things out. That said, I think that this is fine and will work.

Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,20 @@
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<profile>
<!-- sdk-pqc-bc is gated behind the non-fips profile because its
BC compile dependency (bcprov-jdk18on) collides with bc-fips
on the same package namespace. A FIPS build (mvn -P fips,!non-fips)
must omit sdk-pqc-bc from the reactor entirely; consumers who
want hybrid PQC opt in via the non-fips profile. -->
<id>non-fips</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<modules>
<module>sdk-pqc-bc</module>
</modules>
</profile>
<profile>
<id>stage</id>
<modules>
Expand Down
114 changes: 114 additions & 0 deletions scripts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# scripts/

Developer scripts for the OpenTDF Java SDK. Not bundled with the published
artifacts.

## `test-hybrid-pqc.sh`

End-to-end test of the Java SDK's hybrid post-quantum key wrapping
(`hpqt:xwing`, `hpqt:secp256r1-mlkem768`, `hpqt:secp384r1-mlkem1024`) against
a locally running OpenTDF platform. Per algorithm it:

1. Confirms the KAS publishes a hybrid PEM for that algorithm (`grpcurl`
pre-flight, optional).
2. Encrypts a small payload via the `cmdline` jar using
`--encap-key-type=<Hybrid…Key>`.
3. Asserts the resulting TDF manifest has:
- `keyAccess[0].type == "hybrid-wrapped"`
- `keyAccess[0].ephemeralPublicKey` empty (the ephemeral material is
carried inside the ASN.1 envelope in `wrappedKey`)
- `keyAccess[0].wrappedKey` starts with the ASN.1 SEQUENCE byte `0x30`
4. Decrypts the TDF (this is the step that actually exercises hybrid
decapsulation on the KAS rewrap path).
5. Diffs the decrypted payload against the original.

On success the script also prints the plaintext, the full `keyAccess[0]`
(KAO), and the decrypted output for each algorithm so you can eyeball the
artifacts.

### Prerequisites

| Requirement | Notes |
|---|---|
| **JDK 17** | The project's Kotlin compiler can't parse newer JDK version strings. Use Corretto/Temurin/etc. 17. On macOS: `export JAVA_HOME=$(/usr/libexec/java_home -v 17)`. |
| **Maven 3.9+** | Project uses standard `mvn clean install`. |
| **Buf token** | Proto generation requires auth. Either `buf registry login` once, or export `BUF_INPUT_HTTPS_USERNAME` / `BUF_INPUT_HTTPS_PASSWORD`. |
| **`sdk-pqc-bc` module on the classpath** | The BC-backed hybrid PQC implementation lives in the optional `sdk-pqc-bc` sibling module. `cmdline` declares it at runtime scope, so the test script picks it up automatically through `ServiceLoader`. FIPS deployments should omit `sdk-pqc-bc` and accept that hybrid PQC is unavailable in that mode — `TDF.createKeyAccess` throws a clean `SDKException` directing the user to add it. |
| **Local platform with PQC support** | `opentdf/platform` checked out on a branch that implements `hpqt:*` KAS keys + the `hybrid-wrapped` rewrap path. See the platform repo for bring-up (`docker compose` / `make start`). |
| **Hybrid KAS keys registered** | The local platform must have a KAS key registered for each `hpqt:*` algorithm you intend to test. Use `otdfctl` (or platform tooling) to register them. |
| **CLI tools** | `java`, `mvn`, `unzip`, `jq` on `PATH`. `grpcurl` optional but recommended (drives the pre-flight check). |

### Run it

From the repo root:

```bash
# Full run — builds cmdline, pre-flight check, all 3 algorithms
PLATFORM_ENDPOINT=http://localhost:8080 scripts/test-hybrid-pqc.sh

# Reuse an already-built cmdline jar (much faster on iterative runs)
scripts/test-hybrid-pqc.sh --skip-build

# One algorithm only
scripts/test-hybrid-pqc.sh --algorithms HybridXWingKey

# Multiple specific algorithms (comma-separated)
scripts/test-hybrid-pqc.sh --algorithms HybridXWingKey,HybridSecp256r1MLKEM768Key

# Skip the grpcurl pre-flight (use when grpcurl isn't installed)
scripts/test-hybrid-pqc.sh --skip-kas-check
```

### Configuration

All defaults match the existing CI workflow (`.github/workflows/checks.yaml`).
Override via flag or env var:

| Flag / Env | Default | Description |
|---|---|---|
| `--platform-endpoint` / `PLATFORM_ENDPOINT` | `http://localhost:8080` | Platform base URL |
| `--kas-url` / `KAS_URL` | same as platform endpoint | KAS URL passed to cmdline `encrypt` |
| `--client-id` / `CLIENT_ID` | `opentdf-sdk` | OIDC client id |
| `--client-secret` / `CLIENT_SECRET` | `secret` | OIDC client secret |
| `--attr` / `DATA_ATTR` | `https://example.com/attr/attr1/value/value1` | Attribute FQN attached to encrypt |
| `--algorithms` | all three | Comma-separated subset of `KeyType` enum names |
| `--skip-build` | (off) | Reuse `cmdline/target/cmdline.jar` |
| `--skip-kas-check` | (off) | Skip the `grpcurl` pre-flight |

### Expected output

```
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

Add a language tag to the fenced code block.

This avoids markdownlint MD040 and improves rendering consistency.

Proposed fix
-```
+```text
 [OK]   hpqt:xwing: KAS returns hybrid PEM (-----BEGIN XWING PUBLIC KEY-----)
 [OK]   hpqt:secp256r1-mlkem768: KAS returns hybrid PEM (-----BEGIN SECP256R1 MLKEM768 PUBLIC KEY-----)
 [OK]   hpqt:secp384r1-mlkem1024: KAS returns hybrid PEM (-----BEGIN SECP384R1 MLKEM1024 PUBLIC KEY-----)
 ...
 [OK]   HybridXWingKey: manifest OK (hybrid-wrapped, ASN.1 envelope, no ephemeralPublicKey)
 [OK]   HybridXWingKey: round-trip OK
 ...
 All 3 hybrid algorithm(s) passed round-trip.
</details>

<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **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.

```suggestion

🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 79-79: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 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 `@scripts/README.md` at line 79, The fenced code block missing a language tag
should be updated to use a language identifier (e.g., add "text") so
markdownlint MD040 is satisfied; change the opening fence from ``` to ```text
for the block that contains the "[OK]   hpqt:..." lines and ensure the closing
fence remains ``` so rendering and linting are correct.

[OK] hpqt:xwing: KAS returns hybrid PEM (-----BEGIN XWING PUBLIC KEY-----)
[OK] hpqt:secp256r1-mlkem768: KAS returns hybrid PEM (-----BEGIN SECP256R1 MLKEM768 PUBLIC KEY-----)
[OK] hpqt:secp384r1-mlkem1024: KAS returns hybrid PEM (-----BEGIN SECP384R1 MLKEM1024 PUBLIC KEY-----)
...
[OK] HybridXWingKey: manifest OK (hybrid-wrapped, ASN.1 envelope, no ephemeralPublicKey)
[OK] HybridXWingKey: round-trip OK
...
All 3 hybrid algorithm(s) passed round-trip.
```

Exit code is 0 on success, 1 on any algorithm failure (other algorithms still
attempted), 2 on misuse.

### Troubleshooting

| Symptom | Likely cause / fix |
|---|---|
| `Maven build failed ... Buf API token` | Run `buf registry login`, or export `BUF_INPUT_HTTPS_USERNAME` and `BUF_INPUT_HTTPS_PASSWORD`. |
| `Maven build failed ... Kotlin ... isAtLeastJava9` (stack trace) | JDK too new. `export JAVA_HOME=$(/usr/libexec/java_home -v 17)` and rerun. |
| `KAS returned no publicKey` | Platform isn't running, or isn't reachable at `$PLATFORM_ENDPOINT`. |
| `KAS returned a non-hybrid PEM` | The platform is up but no hybrid KAS key is registered for that algorithm. Register one and rerun. |
| `keyType='null'` (manifest assertion) | You're on an old branch where `TDF.java` doesn't yet route hybrid algorithms. Pull the latest branch HEAD. |
| `decrypt failed` after manifest passes | KAS-side rewrap doesn't yet support the `hybrid-wrapped` keyType. Check the platform branch has the matching server change. |

### Known SDK gap

`KeyType.fromAlgorithm` and `KeyType.fromPublicKeyAlgorithm`
(`sdk/src/main/java/io/opentdf/platform/sdk/KeyType.java`) don't yet map the
hybrid algorithm protobuf enums. Auto-discovery via the KAS registry
(`Config.KASInfo.fromKeyAccessServer`) will throw `IllegalArgumentException`
once the platform's proto definitions include `KAS_PUBLIC_KEY_ALG_ENUM_HPQT_*`
values. This script bypasses that path by using `--encap-key-type` explicitly;
extending the script to also exercise registry-discovery should wait until the
mapping is added.
Loading
Loading