-
Notifications
You must be signed in to change notification settings - Fork 3
move things to an SPI pattern #372
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
| <modelVersion>4.0.0</modelVersion> | ||
| <parent> | ||
| <groupId>io.opentdf.platform</groupId> | ||
| <artifactId>sdk-pom</artifactId> | ||
| <version>0.15.0</version> | ||
| </parent> | ||
| <artifactId>sdk-hybrid-bouncycastle</artifactId> | ||
| <name>io.opentdf.platform:sdk-hybrid-bouncycastle</name> | ||
| <description>BouncyCastle-backed HybridKeyWrapProvider SPI implementation | ||
| (X-Wing and NIST EC + ML-KEM hybrid post-quantum key wrapping).</description> | ||
| <packaging>jar</packaging> | ||
| <properties> | ||
| <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||
| </properties> | ||
| <dependencies> | ||
| <dependency> | ||
| <groupId>io.opentdf.platform</groupId> | ||
| <artifactId>sdk</artifactId> | ||
| <version>${project.version}</version> | ||
| </dependency> | ||
| <!-- Hybrid PQ key wrapping requires BC's low-level X-Wing and ML-KEM APIs | ||
| (no JDK 11 stdlib equivalent; provider-agnostic JCA APIs for KEM | ||
| are JDK 21+). See adr/0001 for context. --> | ||
| <dependency> | ||
| <groupId>org.bouncycastle</groupId> | ||
| <artifactId>bcprov-jdk18on</artifactId> | ||
| </dependency> | ||
| <!-- Testing --> | ||
| <dependency> | ||
| <groupId>org.junit.jupiter</groupId> | ||
| <artifactId>junit-jupiter</artifactId> | ||
| <scope>test</scope> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.assertj</groupId> | ||
| <artifactId>assertj-core</artifactId> | ||
| <version>3.27.7</version> | ||
| <scope>test</scope> | ||
| </dependency> | ||
| <!-- Test-jar from sdk exposes FakeServicesBuilder and other package-private | ||
| test helpers needed by TDFHybridTest (which lives in package | ||
| io.opentdf.platform.sdk to keep access to the package-private TDF class). --> | ||
| <dependency> | ||
| <groupId>io.opentdf.platform</groupId> | ||
| <artifactId>sdk</artifactId> | ||
| <version>${project.version}</version> | ||
| <type>test-jar</type> | ||
| <scope>test</scope> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.mockito</groupId> | ||
| <artifactId>mockito-core</artifactId> | ||
| <version>5.2.0</version> | ||
| <scope>test</scope> | ||
| </dependency> | ||
| </dependencies> | ||
| </project> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| package io.opentdf.platform.sdk.hybrid.bouncycastle; | ||
|
|
||
| import io.opentdf.platform.sdk.HybridKeyWrapProvider; | ||
| import io.opentdf.platform.sdk.KeyType; | ||
| import io.opentdf.platform.sdk.SDKException; | ||
|
|
||
| /** | ||
| * BouncyCastle-backed {@link HybridKeyWrapProvider} covering X-Wing, | ||
| * P-256 + ML-KEM-768, and P-384 + ML-KEM-1024. Discovered at runtime via | ||
| * {@code META-INF/services/io.opentdf.platform.sdk.HybridKeyWrapProvider}. | ||
| */ | ||
| public final class BouncyCastleHybridKeyWrapProvider implements HybridKeyWrapProvider { | ||
|
|
||
| @Override | ||
| public boolean supports(KeyType keyType) { | ||
| if (keyType == null) { | ||
| return false; | ||
| } | ||
| switch (keyType) { | ||
| case HybridXWingKey: | ||
| case HybridSecp256r1MLKEM768Key: | ||
| case HybridSecp384r1MLKEM1024Key: | ||
| return true; | ||
| default: | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| public byte[] wrapDEK(KeyType keyType, String publicKeyPem, byte[] dek) { | ||
| switch (keyType) { | ||
| case HybridXWingKey: | ||
| return XWingKeyPair.wrapDEK(XWingKeyPair.pubKeyFromPem(publicKeyPem), dek); | ||
| case HybridSecp256r1MLKEM768Key: | ||
| return HybridNISTKeyPair.P256_MLKEM768.wrapDEK( | ||
| HybridNISTKeyPair.P256_MLKEM768.pubKeyFromPem(publicKeyPem), dek); | ||
| case HybridSecp384r1MLKEM1024Key: | ||
| return HybridNISTKeyPair.P384_MLKEM1024.wrapDEK( | ||
| HybridNISTKeyPair.P384_MLKEM1024.pubKeyFromPem(publicKeyPem), dek); | ||
| default: | ||
| throw new SDKException("unsupported hybrid key type: " + keyType); | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| public byte[] unwrapDEK(KeyType keyType, String privateKeyPem, byte[] wrappedDek) { | ||
| switch (keyType) { | ||
| case HybridXWingKey: | ||
| return XWingKeyPair.unwrapDEK(XWingKeyPair.privateKeyFromPem(privateKeyPem), wrappedDek); | ||
| case HybridSecp256r1MLKEM768Key: | ||
| return HybridNISTKeyPair.P256_MLKEM768.unwrapDEK( | ||
| HybridNISTKeyPair.P256_MLKEM768.privateKeyFromPem(privateKeyPem), wrappedDek); | ||
| case HybridSecp384r1MLKEM1024Key: | ||
| return HybridNISTKeyPair.P384_MLKEM1024.unwrapDEK( | ||
| HybridNISTKeyPair.P384_MLKEM1024.privateKeyFromPem(privateKeyPem), wrappedDek); | ||
| default: | ||
| throw new SDKException("unsupported hybrid key type: " + keyType); | ||
| } | ||
| } | ||
|
Comment on lines
+46
to
+59
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In Java, switching on a public byte[] unwrapDEK(KeyType keyType, String privateKeyPem, byte[] wrappedDek) {
if (keyType == null) {
throw new SDKException("keyType cannot be null");
}
if (privateKeyPem == null) {
throw new SDKException("privateKeyPem cannot be null");
}
if (wrappedDek == null) {
throw new SDKException("wrappedDek cannot be null");
}
switch (keyType) {
case HybridXWingKey:
return XWingKeyPair.unwrapDEK(XWingKeyPair.privateKeyFromPem(privateKeyPem), wrappedDek);
case HybridSecp256r1MLKEM768Key:
return HybridNISTKeyPair.P256_MLKEM768.unwrapDEK(
HybridNISTKeyPair.P256_MLKEM768.privateKeyFromPem(privateKeyPem), wrappedDek);
case HybridSecp384r1MLKEM1024Key:
return HybridNISTKeyPair.P384_MLKEM1024.unwrapDEK(
HybridNISTKeyPair.P384_MLKEM1024.privateKeyFromPem(privateKeyPem), wrappedDek);
default:
throw new SDKException("unsupported hybrid key type: " + keyType);
}
} |
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| package io.opentdf.platform.sdk.hybrid.bouncycastle; | ||
|
|
||
| import io.opentdf.platform.sdk.KeyType; | ||
| import io.opentdf.platform.sdk.SDKException; | ||
|
|
||
| /** | ||
| * Test-support helper for generating hybrid post-quantum keypairs as PEM-encoded | ||
| * pairs. Provided as a public utility on the published jar so the {@code sdk} | ||
| * module's tests (and downstream consumers writing their own round-trip tests) | ||
| * can exercise the SPI without importing BouncyCastle directly. | ||
| */ | ||
| public final class HybridTestKeys { | ||
|
|
||
| /** Holder for a PEM-encoded hybrid keypair. */ | ||
| public static final class PemPair { | ||
| public final String publicKeyPem; | ||
| public final String privateKeyPem; | ||
|
|
||
| PemPair(String publicKeyPem, String privateKeyPem) { | ||
| this.publicKeyPem = publicKeyPem; | ||
| this.privateKeyPem = privateKeyPem; | ||
| } | ||
| } | ||
|
|
||
| private HybridTestKeys() {} | ||
|
|
||
| /** | ||
| * Generate a fresh hybrid keypair for the given type and return both halves | ||
| * as PEM blocks suitable for use with {@link io.opentdf.platform.sdk.HybridKeyWrapProvider}. | ||
| */ | ||
| public static PemPair generate(KeyType keyType) { | ||
| switch (keyType) { | ||
|
Comment on lines
+31
to
+32
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| case HybridXWingKey: { | ||
| XWingKeyPair kp = XWingKeyPair.generate(); | ||
| return new PemPair(kp.publicKeyInPemFormat(), kp.privateKeyInPemFormat()); | ||
| } | ||
| case HybridSecp256r1MLKEM768Key: { | ||
| HybridNISTKeyPair kp = HybridNISTKeyPair.P256_MLKEM768.generate(); | ||
| return new PemPair(kp.publicKeyInPemFormat(), kp.privateKeyInPemFormat()); | ||
| } | ||
| case HybridSecp384r1MLKEM1024Key: { | ||
| HybridNISTKeyPair kp = HybridNISTKeyPair.P384_MLKEM1024.generate(); | ||
| return new PemPair(kp.publicKeyInPemFormat(), kp.privateKeyInPemFormat()); | ||
| } | ||
| default: | ||
| throw new SDKException("not a hybrid key type: " + keyType); | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In Java, switching on a
nullreference throws aNullPointerException. To prevent runtime failures, add defensive null checks forkeyType,publicKeyPem, anddekbefore executing the switch statement.