From b12d3e6f5916f67aae14b71b8f22542360f9632d Mon Sep 17 00:00:00 2001 From: Morgan Kleene Date: Tue, 8 Jul 2025 14:03:07 +0200 Subject: [PATCH 01/15] refactor --- .../java/io/opentdf/platform/sdk/ECCMode.java | 57 +------------------ .../io/opentdf/platform/sdk/ECKeyPair.java | 37 ------------ .../java/io/opentdf/platform/sdk/Header.java | 6 +- .../io/opentdf/platform/sdk/KASClient.java | 4 +- .../java/io/opentdf/platform/sdk/KeyType.java | 38 +++++++------ .../java/io/opentdf/platform/sdk/NanoTDF.java | 2 +- .../io/opentdf/platform/sdk/NanoTDFType.java | 39 +++++++++---- .../sdk/SymmetricAndPayloadConfig.java | 31 ++++------ .../java/io/opentdf/platform/sdk/TDF.java | 5 +- .../opentdf/platform/sdk/ECKeyPairTest.java | 11 ++-- .../io/opentdf/platform/sdk/HeaderTest.java | 2 +- .../platform/sdk/NanoTDFHeaderTest.java | 12 ++-- .../io/opentdf/platform/sdk/NanoTDFTest.java | 2 +- .../java/io/opentdf/platform/sdk/TDFTest.java | 2 +- 14 files changed, 84 insertions(+), 164 deletions(-) diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/ECCMode.java b/sdk/src/main/java/io/opentdf/platform/sdk/ECCMode.java index ce000d54..59a9121e 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/ECCMode.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/ECCMode.java @@ -13,7 +13,7 @@ public ECCMode() { public ECCMode(byte value) { data = new ECCModeStruct(); int curveMode = value & 0x07; // first 3 bits - setEllipticCurve(NanoTDFType.ECCurve.values()[curveMode]); + setEllipticCurve(NanoTDFType.ECCurve.fromCurveMode(curveMode)); int useECDSABinding = (value >> 7) & 0x01; // most significant bit data.useECDSABinding = useECDSABinding; } @@ -52,69 +52,16 @@ public boolean isECDSABindingEnabled() { return data.useECDSABinding == 1; } - public String getCurveName() { - return getEllipticCurveName(NanoTDFType.ECCurve.values()[data.curveMode]); - } - public byte getECCModeAsByte() { int value = (data.useECDSABinding << 7) | data.curveMode; return (byte) value; } - public static String getEllipticCurveName(NanoTDFType.ECCurve curve) { - switch (curve) { - case SECP256R1: - return "secp256r1"; - case SECP384R1: - return "secp384r1"; - case SECP521R1: - return "secp521r1"; - case SECP256K1: - throw new RuntimeException("SDK doesn't support 'secp256k1' curve"); - default: - throw new RuntimeException("Unsupported ECC algorithm."); - } - } - - public static int getECKeySize(NanoTDFType.ECCurve curve) { - switch (curve) { - case SECP256K1: - throw new RuntimeException("SDK doesn't support 'secp256k1' curve"); - case SECP256R1: - return 32; - case SECP384R1: - return 48; - case SECP521R1: - return 66; - default: - throw new RuntimeException("Unsupported ECC algorithm."); - } - } - public static int getECDSASignatureStructSize(NanoTDFType.ECCurve curve) { - int keySize = getECKeySize(curve); + int keySize = curve.compressedPubKeySize; return (1 + keySize + 1 + keySize); } - public static int getECKeySize(String curveName) { - return ECKeyPair.getECKeySize(curveName); - } - - public static int getECCompressedPubKeySize(NanoTDFType.ECCurve curve) { - switch (curve) { - case SECP256K1: - throw new RuntimeException("SDK doesn't support 'secp256k1' curve"); - case SECP256R1: - return 33; - case SECP384R1: - return 49; - case SECP521R1: - return 67; - default: - throw new RuntimeException("Unsupported ECC algorithm."); - } - } - private class ECCModeStruct { int curveMode; int unused; diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/ECKeyPair.java b/sdk/src/main/java/io/opentdf/platform/sdk/ECKeyPair.java index 53095bd7..de79670b 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/ECKeyPair.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/ECKeyPair.java @@ -39,30 +39,6 @@ public enum ECAlgorithm { private static final BouncyCastleProvider BOUNCY_CASTLE_PROVIDER = new BouncyCastleProvider(); - public enum NanoTDFECCurve { - SECP256R1("secp256r1", KeyType.EC256Key), - PRIME256V1("prime256v1", KeyType.EC256Key), - SECP384R1("secp384r1", KeyType.EC384Key), - SECP521R1("secp521r1", KeyType.EC521Key); - - private String name; - private KeyType keyType; - - NanoTDFECCurve(String curveName, KeyType keyType) { - this.name = curveName; - this.keyType = keyType; - } - - @Override - public String toString() { - return name; - } - - public KeyType getKeyType() { - return keyType; - } - } - private KeyPair keyPair; private String curveName; @@ -108,19 +84,6 @@ public ECPrivateKey getPrivateKey() { return (ECPrivateKey) this.keyPair.getPrivate(); } - public static int getECKeySize(String curveName) { - if (curveName.equalsIgnoreCase(NanoTDFECCurve.SECP256R1.toString()) || - curveName.equalsIgnoreCase(NanoTDFECCurve.PRIME256V1.toString())) { - return 32; - } else if (curveName.equalsIgnoreCase(NanoTDFECCurve.SECP384R1.toString())) { - return 48; - } else if (curveName.equalsIgnoreCase(NanoTDFECCurve.SECP521R1.toString())) { - return 66; - } else { - throw new IllegalArgumentException("Unsupported ECC algorithm."); - } - } - public String publicKeyInPEMFormat() { StringWriter writer = new StringWriter(); PemWriter pemWriter = new PemWriter(writer); diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/Header.java b/sdk/src/main/java/io/opentdf/platform/sdk/Header.java index 2d8fda7b..fb0acc1b 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/Header.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/Header.java @@ -26,7 +26,7 @@ public Header(ByteBuffer buffer) { this.payloadConfig = new SymmetricAndPayloadConfig(buffer.get()); this.policyInfo = new PolicyInfo(buffer, this.eccMode); - int compressedPubKeySize = ECCMode.getECCompressedPubKeySize(this.eccMode.getEllipticCurveType()); + int compressedPubKeySize = this.eccMode.getEllipticCurveType().keySize; this.ephemeralKey = new byte[compressedPubKeySize]; buffer.get(this.ephemeralKey); } @@ -79,10 +79,10 @@ public PolicyInfo getPolicyInfo() { } public void setEphemeralKey(byte[] bytes) { - if (bytes.length < eccMode.getECCompressedPubKeySize(eccMode.getEllipticCurveType())) { + if (bytes.length < eccMode.getEllipticCurveType().keySize) { throw new IllegalArgumentException("Failed to read ephemeral key - invalid buffer size."); } - ephemeralKey = Arrays.copyOf(bytes, eccMode.getECCompressedPubKeySize(eccMode.getEllipticCurveType())); + ephemeralKey = Arrays.copyOf(bytes, eccMode.getEllipticCurveType().keySize); } public byte[] getEphemeralKey() { diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/KASClient.java b/sdk/src/main/java/io/opentdf/platform/sdk/KASClient.java index bafc3f2c..43ecb760 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/KASClient.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/KASClient.java @@ -149,8 +149,8 @@ public byte[] unwrap(Manifest.KeyAccess keyAccess, String policy, KeyType sessi ECKeyPair ecKeyPair = null; if (sessionKeyType.isEc()) { - var curveName = sessionKeyType.getCurveName(); - ecKeyPair = new ECKeyPair(curveName, ECKeyPair.ECAlgorithm.ECDH); + var curve = sessionKeyType.getECEcurve().get(); + ecKeyPair = new ECKeyPair(curve.curveName, ECKeyPair.ECAlgorithm.ECDH); clientPublicKey = ecKeyPair.publicKeyInPEMFormat(); } else { // Initialize the RSA key pair only once and reuse it for future unwrap operations diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/KeyType.java b/sdk/src/main/java/io/opentdf/platform/sdk/KeyType.java index 9c5cf010..3a4cfb7f 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/KeyType.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/KeyType.java @@ -1,15 +1,31 @@ package io.opentdf.platform.sdk; +import java.util.Optional; + +import static io.opentdf.platform.sdk.NanoTDFType.ECCurve.SECP256R1; +import static io.opentdf.platform.sdk.NanoTDFType.ECCurve.SECP384R1; +import static io.opentdf.platform.sdk.NanoTDFType.ECCurve.SECP521R1; + public enum KeyType { RSA2048Key("rsa:2048"), - EC256Key("ec:secp256r1"), - EC384Key("ec:secp384r1"), - EC521Key("ec:secp521r1"); + EC256Key("ec:secp256r1", SECP256R1), + EC384Key("ec:secp384r1", SECP384R1), + EC521Key("ec:secp521r1", SECP521R1); private final String keyType; + private final NanoTDFType.ECCurve curve; - KeyType(String keyType) { + KeyType(String keyType, NanoTDFType.ECCurve ecCurve) { this.keyType = keyType; + this.curve = ecCurve; + } + + KeyType(String keyType) { + this(keyType, null); + } + + public Optional getECEcurve() { + return Optional.ofNullable(curve); } @Override @@ -17,18 +33,6 @@ public String toString() { return keyType; } - public String getCurveName() { - switch (this) { - case EC256Key: - return "secp256r1"; - case EC384Key: - return "secp384r1"; - case EC521Key: - return "secp521r1"; - default: - throw new IllegalArgumentException("Unsupported key type: " + this); - } - } public static KeyType fromString(String keyType) { for (KeyType type : KeyType.values()) { @@ -40,6 +44,6 @@ public static KeyType fromString(String keyType) { } public boolean isEc() { - return this != RSA2048Key; + return this.curve != null; } } \ No newline at end of file diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDF.java b/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDF.java index e0b41549..37add6e9 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDF.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDF.java @@ -89,7 +89,7 @@ private Config.HeaderInfo getHeaderInfo(Config.NanoTDFConfig nanoTDFConfig) thro ResourceLocator kasURL = new ResourceLocator(nanoTDFConfig.kasInfoList.get(0).URL, kasInfo.KID); assert kasURL.getIdentifier() != null : "Identifier in ResourceLocator cannot be null"; - ECKeyPair keyPair = new ECKeyPair(nanoTDFConfig.eccMode.getCurveName(), ECKeyPair.ECAlgorithm.ECDSA); + ECKeyPair keyPair = new ECKeyPair(nanoTDFConfig.eccMode.getEllipticCurveType().curveName, ECKeyPair.ECAlgorithm.ECDSA); // Generate symmetric key ECPublicKey kasPublicKey = ECKeyPair.publicKeyFromPem(kasInfo.PublicKey); diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDFType.java b/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDFType.java index 38102794..04e8c393 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDFType.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDFType.java @@ -1,21 +1,38 @@ package io.opentdf.platform.sdk; public class NanoTDFType { - public enum ECCurve { - SECP256R1("secp256r1"), - SECP384R1("secp384r1"), - SECP521R1("secp384r1"), - SECP256K1("secp256k1"); + enum ECCurve { + SECP256R1("secp256r1", 32, 33, 0x00), + SECP384R1("secp384r1", 48, 49, 0x01), + SECP521R1("secp512r1", 66, 67, 0x02), + SECP256K1("secp256k1",-1, -1, -1, false); // Note: SECP256K1 is not supported by the SDK - private final String name; + final int curveMode; + final int keySize; + final int compressedPubKeySize; + final String curveName; + final boolean isSupported; - ECCurve(String curveName) { - this.name = curveName; + + ECCurve(String curveName, int compressedPubKeySize, int keySize, int curveMode) { + this(curveName, compressedPubKeySize, keySize, curveMode, true); + } + + ECCurve(String curveName, int compressedPubKeySize, int keySize, int curveMode, boolean isSupported) { + this.compressedPubKeySize = compressedPubKeySize; + this.keySize = keySize; + this.curveMode = curveMode; + this.curveName = curveName ; + this.isSupported = isSupported; } - @Override - public String toString() { - return name; + static ECCurve fromCurveMode(int curveMode) { + for (ECCurve curve : ECCurve.values()) { + if (curve.curveMode == curveMode) { + return curve; + } + } + throw new IllegalArgumentException("No enum constant for curve mode: " + curveMode); } } // ResourceLocator Protocol diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/SymmetricAndPayloadConfig.java b/sdk/src/main/java/io/opentdf/platform/sdk/SymmetricAndPayloadConfig.java index 5e78b03a..a5c46e72 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/SymmetricAndPayloadConfig.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/SymmetricAndPayloadConfig.java @@ -1,5 +1,7 @@ package io.opentdf.platform.sdk; +import io.opentdf.platform.policy.Key; + public class SymmetricAndPayloadConfig { private Data data; @@ -17,7 +19,7 @@ public SymmetricAndPayloadConfig(byte value) { setSymmetricCipherType(NanoTDFType.Cipher.values()[cipherType]); int signatureECCMode = (value >> 4) & 0x07; - setSignatureECCMode(NanoTDFType.ECCurve.values()[signatureECCMode]); + data.signatureECCMode = signatureECCMode; int hasSignature = (value >> 7) & 0x01; // most significant bit data.hasSignature = hasSignature; @@ -27,22 +29,11 @@ public void setHasSignature(boolean flag) { data.hasSignature = flag ? 1 : 0; } - public void setSignatureECCMode(NanoTDFType.ECCurve curve) { - switch (curve) { - case SECP256R1: - data.signatureECCMode = 0x00; - break; - case SECP384R1: - data.signatureECCMode = 0x01; - break; - case SECP521R1: - data.signatureECCMode = 0x02; - break; - case SECP256K1: - throw new RuntimeException("SDK doesn't support 'secp256k1' curve"); - default: - throw new RuntimeException("Unsupported ECC algorithm."); + public void setSignatureECCMode(NanoTDFType.ECCurve eccCurve) { + if (!eccCurve.isSupported) { + throw new RuntimeException(String.format("Unsupported ECC algorithm: %s", eccCurve.curveName)); } + data.signatureECCMode = eccCurve.curveMode; } public void setSymmetricCipherType(NanoTDFType.Cipher cipherType) { @@ -77,10 +68,6 @@ public boolean hasSignature() { return data.hasSignature == 1; } - public NanoTDFType.ECCurve getSignatureECCMode() { - return NanoTDFType.ECCurve.values()[data.signatureECCMode]; - } - public NanoTDFType.Cipher getCipherType() { return NanoTDFType.Cipher.values()[data.symmetricCipherEnum]; } @@ -111,6 +98,10 @@ static public int sizeOfAuthTagForCipher(NanoTDFType.Cipher cipherType) { } } + public NanoTDFType.ECCurve getSignatureECCMode() { + return NanoTDFType.ECCurve.fromCurveMode(this.data.signatureECCMode); + } + private static class Data { int symmetricCipherEnum; int signatureECCMode; diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java b/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java index fe547caa..2ccfabd9 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java @@ -277,8 +277,9 @@ private Manifest.KeyAccess createKeyAccess(Config.TDFConfig tdfConfig, Config.KA } private ECKeyWrappedKeyInfo createECWrappedKey(Config.TDFConfig tdfConfig, Config.KASInfo kasInfo, byte[] symKey) { - var curveName = tdfConfig.wrappingKeyType.getCurveName(); - var keyPair = new ECKeyPair(curveName, ECKeyPair.ECAlgorithm.ECDH); + var curve = tdfConfig.wrappingKeyType.getECEcurve().get(); + assert curve != null : "Wrapping key type must be an EC key type"; + var keyPair = new ECKeyPair(curve.curveName, ECKeyPair.ECAlgorithm.ECDH); ECPublicKey kasPubKey = ECKeyPair.publicKeyFromPem(kasInfo.PublicKey); byte[] symmetricKey = ECKeyPair.computeECDHKey(kasPubKey, keyPair.getPrivateKey()); diff --git a/sdk/src/test/java/io/opentdf/platform/sdk/ECKeyPairTest.java b/sdk/src/test/java/io/opentdf/platform/sdk/ECKeyPairTest.java index 1ebe2e75..14127655 100644 --- a/sdk/src/test/java/io/opentdf/platform/sdk/ECKeyPairTest.java +++ b/sdk/src/test/java/io/opentdf/platform/sdk/ECKeyPairTest.java @@ -12,6 +12,7 @@ import java.util.Arrays; import java.util.Base64; +import static io.opentdf.platform.sdk.NanoTDFType.ECCurve.SECP256R1; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -63,8 +64,7 @@ void ecPublicKeyInPemformat() throws InvalidAlgorithmParameterException, NoSuchA byte[] compressedKey2 = ECKeyPair.compressECPublickey(keyPairA.publicKeyInPEMFormat()); assertArrayEquals(compressedKey1, compressedKey2); - String publicKey = ECKeyPair.publicKeyFromECPoint(compressedKey1, - ECKeyPair.NanoTDFECCurve.SECP256R1.toString()); + String publicKey = ECKeyPair.publicKeyFromECPoint(compressedKey1, SECP256R1.curveName); assertEquals(keyPairA.publicKeyInPEMFormat(), publicKey); ECKeyPair keyPairB = new ECKeyPair(); @@ -147,7 +147,7 @@ void testECDH() { assertEquals(encodeECPoint, "Al3vx59pBnP8tRxuUFw18aK9ym6rFrxZRhpVQytUQ+Kg"); String publicKey = ECKeyPair.publicKeyFromECPoint(ecPoint, - ECKeyPair.NanoTDFECCurve.SECP256R1.toString()); + SECP256R1.name()); assertArrayEquals(ECKeys.sdkPublicKey.toCharArray(), publicKey.toCharArray()); } @@ -155,9 +155,8 @@ void testECDH() { void testECDSA() { String plainText = "Virtru!"; - for (ECKeyPair.NanoTDFECCurve curve: ECKeyPair.NanoTDFECCurve.values()) { - - ECKeyPair keyPair = new ECKeyPair(curve.toString(), ECKeyPair.ECAlgorithm.ECDSA); + for (var curve: NanoTDFType.ECCurve.values()) { + ECKeyPair keyPair = new ECKeyPair(curve.name(), ECKeyPair.ECAlgorithm.ECDSA); byte[] signature = ECKeyPair.computeECDSASig(plainText.getBytes(), keyPair.getPrivateKey()); boolean verify = ECKeyPair.verifyECDSAig(plainText.getBytes(), signature, keyPair.getPublicKey()); assertEquals(verify, true); diff --git a/sdk/src/test/java/io/opentdf/platform/sdk/HeaderTest.java b/sdk/src/test/java/io/opentdf/platform/sdk/HeaderTest.java index 7d39836b..d30b2f73 100644 --- a/sdk/src/test/java/io/opentdf/platform/sdk/HeaderTest.java +++ b/sdk/src/test/java/io/opentdf/platform/sdk/HeaderTest.java @@ -47,7 +47,7 @@ void settingAndGettingEphemeralKey() { ECCMode mode = new ECCMode((byte) 1); // Initialize the ECCMode object header.setECCMode(mode); // Set the ECCMode object - int keySize = ECCMode.getECCompressedPubKeySize(mode.getEllipticCurveType()); + int keySize = mode.getEllipticCurveType().keySize; byte[] key = new byte[keySize]; // Ensure the key size is correct header.setEphemeralKey(key); assertArrayEquals(key, header.getEphemeralKey()); diff --git a/sdk/src/test/java/io/opentdf/platform/sdk/NanoTDFHeaderTest.java b/sdk/src/test/java/io/opentdf/platform/sdk/NanoTDFHeaderTest.java index 1e0bbd2a..957479d8 100644 --- a/sdk/src/test/java/io/opentdf/platform/sdk/NanoTDFHeaderTest.java +++ b/sdk/src/test/java/io/opentdf/platform/sdk/NanoTDFHeaderTest.java @@ -104,7 +104,7 @@ public void testNanoTDFHeaderRemotePolicy() throws IOException { header.setECCMode(eccMode); SymmetricAndPayloadConfig payloadConfig = new SymmetricAndPayloadConfig((byte) 0x0); // no signature and - // AES_256_GCM_64_TAG + // AES_256_GCM_64_TAG header.setPayloadConfig(payloadConfig); PolicyInfo policyInfo = new PolicyInfo(); @@ -114,8 +114,7 @@ public void testNanoTDFHeaderRemotePolicy() throws IOException { header.setPolicyInfo(policyInfo); header.setEphemeralKey(compressedPubKey); - int headerSize = header.getTotalSize(); - headerSize = header.writeIntoBuffer(ByteBuffer.wrap(headerData)); + int headerSize = header.writeIntoBuffer(ByteBuffer.wrap(headerData)); assertEquals(headerData.length, headerSize); assertTrue(Arrays.equals(headerData, expectedHeader)); } @@ -170,8 +169,7 @@ public void testNanoTDFReader() header2.setPolicyInfo(policyInfo); int sizeToRead = policyInfo.getTotalSize(); - int compressedPubKeySize = ECCMode - .getECCompressedPubKeySize(header2.getECCMode().getEllipticCurveType()); + int compressedPubKeySize = header2.getECCMode().getEllipticCurveType().keySize; byte[] ephemeralKey = new byte[compressedPubKeySize]; // size of compressed public key System.arraycopy(remainingBytesArray, sizeToRead, ephemeralKey, 0, ephemeralKey.length); header2.setEphemeralKey(ephemeralKey); @@ -203,11 +201,11 @@ public void testNanoTDFEncryption() byte[] tag = new byte[tagSize]; ECCMode eccMode = new ECCMode((byte) 0x0); // no ecdsa binding and 'secp256r1' - ECKeyPair sdkECKeyPair = new ECKeyPair(eccMode.getCurveName(), ECKeyPair.ECAlgorithm.ECDH); + ECKeyPair sdkECKeyPair = new ECKeyPair(eccMode.getEllipticCurveType().curveName, ECKeyPair.ECAlgorithm.ECDH); String sdkPrivateKeyForEncrypt = sdkECKeyPair.privateKeyInPEMFormat(); String sdkPublicKeyForEncrypt = sdkECKeyPair.publicKeyInPEMFormat(); - ECKeyPair kasECKeyPair = new ECKeyPair(eccMode.getCurveName(), ECKeyPair.ECAlgorithm.ECDH); + ECKeyPair kasECKeyPair = new ECKeyPair(eccMode.getEllipticCurveType().curveName, ECKeyPair.ECAlgorithm.ECDH); String kasPublicKey = kasECKeyPair.publicKeyInPEMFormat(); // Encrypt Header header = new Header(); diff --git a/sdk/src/test/java/io/opentdf/platform/sdk/NanoTDFTest.java b/sdk/src/test/java/io/opentdf/platform/sdk/NanoTDFTest.java index 5e428c79..6516ad3a 100644 --- a/sdk/src/test/java/io/opentdf/platform/sdk/NanoTDFTest.java +++ b/sdk/src/test/java/io/opentdf/platform/sdk/NanoTDFTest.java @@ -98,7 +98,7 @@ public byte[] unwrapNanoTDF(NanoTDFType.ECCurve curve, String header, String kas Header nTDFHeader = new Header(ByteBuffer.wrap(headerAsBytes)); byte[] ephemeralKey = nTDFHeader.getEphemeralKey(); - String publicKeyAsPem = ECKeyPair.publicKeyFromECPoint(ephemeralKey, nTDFHeader.getECCMode().getCurveName()); + String publicKeyAsPem = ECKeyPair.publicKeyFromECPoint(ephemeralKey, nTDFHeader.getECCMode().getEllipticCurveType().curveName); // Generate symmetric key byte[] symmetricKey = ECKeyPair.computeECDHKey(ECKeyPair.publicKeyFromPem(publicKeyAsPem), diff --git a/sdk/src/test/java/io/opentdf/platform/sdk/TDFTest.java b/sdk/src/test/java/io/opentdf/platform/sdk/TDFTest.java index e41f54ea..170b2c70 100644 --- a/sdk/src/test/java/io/opentdf/platform/sdk/TDFTest.java +++ b/sdk/src/test/java/io/opentdf/platform/sdk/TDFTest.java @@ -123,7 +123,7 @@ static void setupKeyPairsAndMocks() { if (i % 2 == 0) { keypairs.add(CryptoUtils.generateRSAKeypair()); } else { - keypairs.add(CryptoUtils.generateECKeypair(KeyType.EC256Key.getCurveName())); + keypairs.add(CryptoUtils.generateECKeypair(NanoTDFType.ECCurve.SECP256R1.curveName)); } } From 3b7d30f0fc02d3d0cc692397774fbe64473dc8a5 Mon Sep 17 00:00:00 2001 From: Morgan Kleene Date: Tue, 8 Jul 2025 14:31:59 +0200 Subject: [PATCH 02/15] just saving --- .../io/opentdf/platform/sdk/ECKeyPair.java | 20 ++++++------------- .../io/opentdf/platform/sdk/KASClient.java | 4 ++-- .../java/io/opentdf/platform/sdk/NanoTDF.java | 2 +- .../java/io/opentdf/platform/sdk/TDF.java | 2 +- .../opentdf/platform/sdk/ECKeyPairTest.java | 5 ++--- .../platform/sdk/NanoTDFHeaderTest.java | 4 ++-- 6 files changed, 14 insertions(+), 23 deletions(-) diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/ECKeyPair.java b/sdk/src/main/java/io/opentdf/platform/sdk/ECKeyPair.java index de79670b..eb6335da 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/ECKeyPair.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/ECKeyPair.java @@ -32,6 +32,8 @@ public class ECKeyPair { Security.addProvider(new BouncyCastleProvider()); } + private final NanoTDFType.ECCurve curve; + public enum ECAlgorithm { ECDH, ECDSA @@ -40,13 +42,13 @@ public enum ECAlgorithm { private static final BouncyCastleProvider BOUNCY_CASTLE_PROVIDER = new BouncyCastleProvider(); private KeyPair keyPair; - private String curveName; public ECKeyPair() { - this("secp256r1", ECAlgorithm.ECDH); + this(NanoTDFType.ECCurve.SECP256R1, ECAlgorithm.ECDH); } - public ECKeyPair(String curveName, ECAlgorithm algorithm) { + public ECKeyPair(NanoTDFType.ECCurve curve, ECAlgorithm algorithm) { + this.curve = curve; KeyPairGenerator generator; try { @@ -61,19 +63,13 @@ public ECKeyPair(String curveName, ECAlgorithm algorithm) { throw new RuntimeException(e); } - ECGenParameterSpec spec = new ECGenParameterSpec(curveName); + ECGenParameterSpec spec = new ECGenParameterSpec(this.curve.curveName); try { generator.initialize(spec); } catch (InvalidAlgorithmParameterException e) { throw new RuntimeException(e); } this.keyPair = generator.generateKeyPair(); - this.curveName = curveName; - } - - public ECKeyPair(ECPublicKey publicKey, ECPrivateKey privateKey, String curveName) { - this.keyPair = new KeyPair(publicKey, privateKey); - this.curveName = curveName; } public ECPublicKey getPublicKey() { @@ -118,10 +114,6 @@ public int keySize() { return this.keyPair.getPrivate().getEncoded().length * 8; } - public String curveName() { - return this.curveName; - } - public byte[] compressECPublickey() { return ((ECPublicKey) this.keyPair.getPublic()).getQ().getEncoded(true); } diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/KASClient.java b/sdk/src/main/java/io/opentdf/platform/sdk/KASClient.java index 43ecb760..44b6fe3b 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/KASClient.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/KASClient.java @@ -150,7 +150,7 @@ public byte[] unwrap(Manifest.KeyAccess keyAccess, String policy, KeyType sessi if (sessionKeyType.isEc()) { var curve = sessionKeyType.getECEcurve().get(); - ecKeyPair = new ECKeyPair(curve.curveName, ECKeyPair.ECAlgorithm.ECDH); + ecKeyPair = new ECKeyPair(curve, ECKeyPair.ECAlgorithm.ECDH); clientPublicKey = ecKeyPair.publicKeyInPEMFormat(); } else { // Initialize the RSA key pair only once and reuse it for future unwrap operations @@ -219,7 +219,7 @@ public byte[] unwrap(Manifest.KeyAccess keyAccess, String policy, KeyType sessi } public byte[] unwrapNanoTDF(NanoTDFType.ECCurve curve, String header, String kasURL) { - ECKeyPair keyPair = new ECKeyPair(curve.toString(), ECKeyPair.ECAlgorithm.ECDH); + ECKeyPair keyPair = new ECKeyPair(curve, ECKeyPair.ECAlgorithm.ECDH); NanoTDFKeyAccess keyAccess = new NanoTDFKeyAccess(); keyAccess.header = header; diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDF.java b/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDF.java index 37add6e9..b24b6a6f 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDF.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDF.java @@ -89,7 +89,7 @@ private Config.HeaderInfo getHeaderInfo(Config.NanoTDFConfig nanoTDFConfig) thro ResourceLocator kasURL = new ResourceLocator(nanoTDFConfig.kasInfoList.get(0).URL, kasInfo.KID); assert kasURL.getIdentifier() != null : "Identifier in ResourceLocator cannot be null"; - ECKeyPair keyPair = new ECKeyPair(nanoTDFConfig.eccMode.getEllipticCurveType().curveName, ECKeyPair.ECAlgorithm.ECDSA); + ECKeyPair keyPair = new ECKeyPair(nanoTDFConfig.eccMode.getEllipticCurveType(), ECKeyPair.ECAlgorithm.ECDSA); // Generate symmetric key ECPublicKey kasPublicKey = ECKeyPair.publicKeyFromPem(kasInfo.PublicKey); diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java b/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java index 2ccfabd9..fd50a584 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java @@ -279,7 +279,7 @@ private Manifest.KeyAccess createKeyAccess(Config.TDFConfig tdfConfig, Config.KA private ECKeyWrappedKeyInfo createECWrappedKey(Config.TDFConfig tdfConfig, Config.KASInfo kasInfo, byte[] symKey) { var curve = tdfConfig.wrappingKeyType.getECEcurve().get(); assert curve != null : "Wrapping key type must be an EC key type"; - var keyPair = new ECKeyPair(curve.curveName, ECKeyPair.ECAlgorithm.ECDH); + var keyPair = new ECKeyPair(curve, ECKeyPair.ECAlgorithm.ECDH); ECPublicKey kasPubKey = ECKeyPair.publicKeyFromPem(kasInfo.PublicKey); byte[] symmetricKey = ECKeyPair.computeECDHKey(kasPubKey, keyPair.getPrivateKey()); diff --git a/sdk/src/test/java/io/opentdf/platform/sdk/ECKeyPairTest.java b/sdk/src/test/java/io/opentdf/platform/sdk/ECKeyPairTest.java index 14127655..d2c649fb 100644 --- a/sdk/src/test/java/io/opentdf/platform/sdk/ECKeyPairTest.java +++ b/sdk/src/test/java/io/opentdf/platform/sdk/ECKeyPairTest.java @@ -47,8 +47,7 @@ public class ECKeys { public static final String salt = "L1L"; } @Test - void ecPublicKeyInPemformat() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, - IOException, NoSuchProviderException, InvalidKeySpecException, CertificateException, InvalidKeyException { + void ecPublicKeyInPemformat() { ECKeyPair keyPairA = new ECKeyPair(); String keypairAPubicKey = keyPairA.publicKeyInPEMFormat(); @@ -156,7 +155,7 @@ void testECDSA() { String plainText = "Virtru!"; for (var curve: NanoTDFType.ECCurve.values()) { - ECKeyPair keyPair = new ECKeyPair(curve.name(), ECKeyPair.ECAlgorithm.ECDSA); + ECKeyPair keyPair = new ECKeyPair(curve, ECKeyPair.ECAlgorithm.ECDSA); byte[] signature = ECKeyPair.computeECDSASig(plainText.getBytes(), keyPair.getPrivateKey()); boolean verify = ECKeyPair.verifyECDSAig(plainText.getBytes(), signature, keyPair.getPublicKey()); assertEquals(verify, true); diff --git a/sdk/src/test/java/io/opentdf/platform/sdk/NanoTDFHeaderTest.java b/sdk/src/test/java/io/opentdf/platform/sdk/NanoTDFHeaderTest.java index 957479d8..6bef6f18 100644 --- a/sdk/src/test/java/io/opentdf/platform/sdk/NanoTDFHeaderTest.java +++ b/sdk/src/test/java/io/opentdf/platform/sdk/NanoTDFHeaderTest.java @@ -201,11 +201,11 @@ public void testNanoTDFEncryption() byte[] tag = new byte[tagSize]; ECCMode eccMode = new ECCMode((byte) 0x0); // no ecdsa binding and 'secp256r1' - ECKeyPair sdkECKeyPair = new ECKeyPair(eccMode.getEllipticCurveType().curveName, ECKeyPair.ECAlgorithm.ECDH); + ECKeyPair sdkECKeyPair = new ECKeyPair(eccMode.getEllipticCurveType(), ECKeyPair.ECAlgorithm.ECDH); String sdkPrivateKeyForEncrypt = sdkECKeyPair.privateKeyInPEMFormat(); String sdkPublicKeyForEncrypt = sdkECKeyPair.publicKeyInPEMFormat(); - ECKeyPair kasECKeyPair = new ECKeyPair(eccMode.getEllipticCurveType().curveName, ECKeyPair.ECAlgorithm.ECDH); + ECKeyPair kasECKeyPair = new ECKeyPair(eccMode.getEllipticCurveType(), ECKeyPair.ECAlgorithm.ECDH); String kasPublicKey = kasECKeyPair.publicKeyInPEMFormat(); // Encrypt Header header = new Header(); From 5895e4608879bd13682e2bdca6c37187b5011213 Mon Sep 17 00:00:00 2001 From: Morgan Kleene Date: Tue, 8 Jul 2025 14:37:14 +0200 Subject: [PATCH 03/15] get this passing --- sdk/src/main/java/io/opentdf/platform/sdk/NanoTDFType.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDFType.java b/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDFType.java index 04e8c393..f05f651e 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDFType.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDFType.java @@ -4,7 +4,7 @@ public class NanoTDFType { enum ECCurve { SECP256R1("secp256r1", 32, 33, 0x00), SECP384R1("secp384r1", 48, 49, 0x01), - SECP521R1("secp512r1", 66, 67, 0x02), + SECP521R1("secp521r1", 66, 67, 0x02), SECP256K1("secp256k1",-1, -1, -1, false); // Note: SECP256K1 is not supported by the SDK final int curveMode; From 976b986d2bd92b3377b70bde5b93a8e834f82438 Mon Sep 17 00:00:00 2001 From: Morgan Kleene Date: Tue, 8 Jul 2025 16:58:23 +0200 Subject: [PATCH 04/15] add a test --- .../java/io/opentdf/platform/sdk/ECCMode.java | 2 +- .../io/opentdf/platform/sdk/ECKeyPair.java | 7 ++- .../java/io/opentdf/platform/sdk/Header.java | 6 +-- .../java/io/opentdf/platform/sdk/NanoTDF.java | 21 +++++++-- .../io/opentdf/platform/sdk/NanoTDFType.java | 20 ++++++++ .../io/opentdf/platform/sdk/PolicyInfo.java | 2 +- .../io/opentdf/platform/sdk/HeaderTest.java | 2 +- .../platform/sdk/NanoTDFHeaderTest.java | 6 +-- .../io/opentdf/platform/sdk/NanoTDFTest.java | 46 +++++++++++++------ 9 files changed, 84 insertions(+), 28 deletions(-) diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/ECCMode.java b/sdk/src/main/java/io/opentdf/platform/sdk/ECCMode.java index 59a9121e..688be171 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/ECCMode.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/ECCMode.java @@ -44,7 +44,7 @@ public void setEllipticCurve(NanoTDFType.ECCurve curve) { } } - public NanoTDFType.ECCurve getEllipticCurveType() { + public NanoTDFType.ECCurve getCurve() { return NanoTDFType.ECCurve.values()[data.curveMode]; } diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/ECKeyPair.java b/sdk/src/main/java/io/opentdf/platform/sdk/ECKeyPair.java index eb6335da..294ec7da 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/ECKeyPair.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/ECKeyPair.java @@ -24,6 +24,7 @@ import java.io.*; import java.security.*; import java.security.spec.*; +import java.util.Objects; // https://www.bouncycastle.org/latest_releases.html public class ECKeyPair { @@ -48,7 +49,7 @@ public ECKeyPair() { } public ECKeyPair(NanoTDFType.ECCurve curve, ECAlgorithm algorithm) { - this.curve = curve; + this.curve = Objects.requireNonNull(curve); KeyPairGenerator generator; try { @@ -80,6 +81,10 @@ public ECPrivateKey getPrivateKey() { return (ECPrivateKey) this.keyPair.getPrivate(); } + NanoTDFType.ECCurve getCurve() { + return this.curve; + } + public String publicKeyInPEMFormat() { StringWriter writer = new StringWriter(); PemWriter pemWriter = new PemWriter(writer); diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/Header.java b/sdk/src/main/java/io/opentdf/platform/sdk/Header.java index fb0acc1b..d189a289 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/Header.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/Header.java @@ -26,7 +26,7 @@ public Header(ByteBuffer buffer) { this.payloadConfig = new SymmetricAndPayloadConfig(buffer.get()); this.policyInfo = new PolicyInfo(buffer, this.eccMode); - int compressedPubKeySize = this.eccMode.getEllipticCurveType().keySize; + int compressedPubKeySize = this.eccMode.getCurve().keySize; this.ephemeralKey = new byte[compressedPubKeySize]; buffer.get(this.ephemeralKey); } @@ -79,10 +79,10 @@ public PolicyInfo getPolicyInfo() { } public void setEphemeralKey(byte[] bytes) { - if (bytes.length < eccMode.getEllipticCurveType().keySize) { + if (bytes.length < eccMode.getCurve().keySize) { throw new IllegalArgumentException("Failed to read ephemeral key - invalid buffer size."); } - ephemeralKey = Arrays.copyOf(bytes, eccMode.getEllipticCurveType().keySize); + ephemeralKey = Arrays.copyOf(bytes, eccMode.getCurve().keySize); } public byte[] getEphemeralKey() { diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDF.java b/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDF.java index b24b6a6f..0abc0bb7 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDF.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDF.java @@ -82,14 +82,20 @@ private Config.HeaderInfo getHeaderInfo(Config.NanoTDFConfig nanoTDFConfig) thro String url = kasInfo.URL; if (kasInfo.PublicKey == null || kasInfo.PublicKey.isEmpty()) { logger.info("no public key provided for KAS at {}, retrieving", url); - kasInfo = services.kas().getECPublicKey(kasInfo, nanoTDFConfig.eccMode.getEllipticCurveType()); + kasInfo = services.kas().getECPublicKey(kasInfo, nanoTDFConfig.eccMode.getCurve()); } // Kas url resource locator ResourceLocator kasURL = new ResourceLocator(nanoTDFConfig.kasInfoList.get(0).URL, kasInfo.KID); assert kasURL.getIdentifier() != null : "Identifier in ResourceLocator cannot be null"; - ECKeyPair keyPair = new ECKeyPair(nanoTDFConfig.eccMode.getEllipticCurveType(), ECKeyPair.ECAlgorithm.ECDSA); + // it might be better to pull the curve from the OIDC in the PEM but it looks like we + // are just taking the Algorithm as correct + var ecCurve = NanoTDFType.ECCurve.fromAlgorithm(kasInfo.Algorithm); + if (ecCurve != nanoTDFConfig.eccMode.getCurve()) { + logger.warn("ECCurve in NanoTDFConfig [{}] does not match the curve in KASInfo, using KASInfo curve [{}]", nanoTDFConfig.eccMode.getCurve(), ecCurve); + } + ECKeyPair keyPair = new ECKeyPair(ecCurve, ECKeyPair.ECAlgorithm.ECDSA); // Generate symmetric key ECPublicKey kasPublicKey = ECKeyPair.publicKeyFromPem(kasInfo.PublicKey); @@ -138,7 +144,14 @@ private Config.HeaderInfo getHeaderInfo(Config.NanoTDFConfig nanoTDFConfig) thro // Create header byte[] compressedPubKey = keyPair.compressECPublickey(); Header header = new Header(); - header.setECCMode(nanoTDFConfig.eccMode); + ECCMode mode; + if (nanoTDFConfig.eccMode.getCurve() != keyPair.getCurve()) { + mode = new ECCMode(nanoTDFConfig.eccMode.getECCModeAsByte()); + mode.setEllipticCurve(keyPair.getCurve()); + } else { + mode = nanoTDFConfig.eccMode; + } + header.setECCMode(mode); header.setPayloadConfig(nanoTDFConfig.config); header.setEphemeralKey(compressedPubKey); header.setKasLocator(kasURL); @@ -276,7 +289,7 @@ public void readNanoTDF(ByteBuffer nanoTDF, OutputStream outputStream, } - key = services.kas().unwrapNanoTDF(header.getECCMode().getEllipticCurveType(), + key = services.kas().unwrapNanoTDF(header.getECCMode().getCurve(), base64HeaderData, kasUrl); collectionStore.store(header, new CollectionKey(key)); diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDFType.java b/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDFType.java index f05f651e..9a16e103 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDFType.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDFType.java @@ -1,6 +1,12 @@ package io.opentdf.platform.sdk; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Arrays; + public class NanoTDFType { + private static final Logger log = LoggerFactory.getLogger(NanoTDFType.class); enum ECCurve { SECP256R1("secp256r1", 32, 33, 0x00), SECP384R1("secp384r1", 48, 49, 0x01), @@ -34,6 +40,20 @@ static ECCurve fromCurveMode(int curveMode) { } throw new IllegalArgumentException("No enum constant for curve mode: " + curveMode); } + + public static ECCurve fromAlgorithm(String algorithm) { + if (algorithm == null) { + log.warn("got a null algorithm, returning SECP256R1 as default"); + return SECP256R1; + } + + assert algorithm.startsWith("ec:"); + var searchKey = algorithm.substring("ec:".length()); + return Arrays.stream(ECCurve.values()) + .filter(v -> v.curveName.equalsIgnoreCase(searchKey)) + .findAny() + .orElseThrow(() -> new IllegalArgumentException(String.format("No enum constant for algorithm: %s", algorithm))); + } } // ResourceLocator Protocol public enum Protocol { diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/PolicyInfo.java b/sdk/src/main/java/io/opentdf/platform/sdk/PolicyInfo.java index 66898215..f181a817 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/PolicyInfo.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/PolicyInfo.java @@ -47,7 +47,7 @@ public PolicyInfo(ByteBuffer buffer, ECCMode eccMode) { int bindingBytesSize = 8; // GMAC length if (this.hasECDSABinding) { // ECDSA - The size of binding depends on the curve. - bindingBytesSize = ECCMode.getECDSASignatureStructSize(eccMode.getEllipticCurveType()); + bindingBytesSize = ECCMode.getECDSASignatureStructSize(eccMode.getCurve()); } this.binding = new byte[bindingBytesSize]; diff --git a/sdk/src/test/java/io/opentdf/platform/sdk/HeaderTest.java b/sdk/src/test/java/io/opentdf/platform/sdk/HeaderTest.java index d30b2f73..cfc36e61 100644 --- a/sdk/src/test/java/io/opentdf/platform/sdk/HeaderTest.java +++ b/sdk/src/test/java/io/opentdf/platform/sdk/HeaderTest.java @@ -47,7 +47,7 @@ void settingAndGettingEphemeralKey() { ECCMode mode = new ECCMode((byte) 1); // Initialize the ECCMode object header.setECCMode(mode); // Set the ECCMode object - int keySize = mode.getEllipticCurveType().keySize; + int keySize = mode.getCurve().keySize; byte[] key = new byte[keySize]; // Ensure the key size is correct header.setEphemeralKey(key); assertArrayEquals(key, header.getEphemeralKey()); diff --git a/sdk/src/test/java/io/opentdf/platform/sdk/NanoTDFHeaderTest.java b/sdk/src/test/java/io/opentdf/platform/sdk/NanoTDFHeaderTest.java index 6bef6f18..eead2988 100644 --- a/sdk/src/test/java/io/opentdf/platform/sdk/NanoTDFHeaderTest.java +++ b/sdk/src/test/java/io/opentdf/platform/sdk/NanoTDFHeaderTest.java @@ -169,7 +169,7 @@ public void testNanoTDFReader() header2.setPolicyInfo(policyInfo); int sizeToRead = policyInfo.getTotalSize(); - int compressedPubKeySize = header2.getECCMode().getEllipticCurveType().keySize; + int compressedPubKeySize = header2.getECCMode().getCurve().keySize; byte[] ephemeralKey = new byte[compressedPubKeySize]; // size of compressed public key System.arraycopy(remainingBytesArray, sizeToRead, ephemeralKey, 0, ephemeralKey.length); header2.setEphemeralKey(ephemeralKey); @@ -201,11 +201,11 @@ public void testNanoTDFEncryption() byte[] tag = new byte[tagSize]; ECCMode eccMode = new ECCMode((byte) 0x0); // no ecdsa binding and 'secp256r1' - ECKeyPair sdkECKeyPair = new ECKeyPair(eccMode.getEllipticCurveType(), ECKeyPair.ECAlgorithm.ECDH); + ECKeyPair sdkECKeyPair = new ECKeyPair(eccMode.getCurve(), ECKeyPair.ECAlgorithm.ECDH); String sdkPrivateKeyForEncrypt = sdkECKeyPair.privateKeyInPEMFormat(); String sdkPublicKeyForEncrypt = sdkECKeyPair.publicKeyInPEMFormat(); - ECKeyPair kasECKeyPair = new ECKeyPair(eccMode.getEllipticCurveType(), ECKeyPair.ECAlgorithm.ECDH); + ECKeyPair kasECKeyPair = new ECKeyPair(eccMode.getCurve(), ECKeyPair.ECAlgorithm.ECDH); String kasPublicKey = kasECKeyPair.publicKeyInPEMFormat(); // Encrypt Header header = new Header(); diff --git a/sdk/src/test/java/io/opentdf/platform/sdk/NanoTDFTest.java b/sdk/src/test/java/io/opentdf/platform/sdk/NanoTDFTest.java index 6516ad3a..635c129b 100644 --- a/sdk/src/test/java/io/opentdf/platform/sdk/NanoTDFTest.java +++ b/sdk/src/test/java/io/opentdf/platform/sdk/NanoTDFTest.java @@ -76,6 +76,7 @@ public KASInfo getECPublicKey(Config.KASInfo kasInfo, NanoTDFType.ECCurve curve) var k2 = kasInfo.clone(); k2.KID = KID; k2.PublicKey = kasPublicKey; + k2.Algorithm = "ec:secp256r1"; return k2; } @@ -98,7 +99,7 @@ public byte[] unwrapNanoTDF(NanoTDFType.ECCurve curve, String header, String kas Header nTDFHeader = new Header(ByteBuffer.wrap(headerAsBytes)); byte[] ephemeralKey = nTDFHeader.getEphemeralKey(); - String publicKeyAsPem = ECKeyPair.publicKeyFromECPoint(ephemeralKey, nTDFHeader.getECCMode().getEllipticCurveType().curveName); + String publicKeyAsPem = ECKeyPair.publicKeyFromECPoint(ephemeralKey, nTDFHeader.getECCMode().getCurve().curveName); // Generate symmetric key byte[] symmetricKey = ECKeyPair.computeECDHKey(ECKeyPair.publicKeyFromPem(publicKeyAsPem), @@ -162,6 +163,7 @@ void encryptionAndDecryptionWithValidKey() throws Exception { Config.NanoTDFConfig config = Config.newNanoTDFConfig( Config.withNanoKasInformation(kasInfos.toArray(new Config.KASInfo[0])), + Config.withEllipticCurve("secp384r1"), Config.witDataAttributes("https://example.com/attr/Classification/value/S", "https://example.com/attr/Classification/value/X") ); @@ -201,18 +203,36 @@ void encryptionAndDecryptionWithValidKey() throws Exception { } } - void runBasicTest(String kasUrl, boolean allowed, KeyAccessServerRegistryServiceClient kasReg, NanoTDFReaderConfig decryptConfig) throws Exception { + @Test + void testWithDifferentConfigAndKeyValues() throws Exception { var kasInfos = new ArrayList<>(); var kasInfo = new Config.KASInfo(); - kasInfo.URL = kasUrl; + kasInfo.URL = "https://api.example.com/kas"; kasInfo.PublicKey = null; kasInfos.add(kasInfo); - - Config.NanoTDFConfig config = Config.newNanoTDFConfig( + var config = Config.newNanoTDFConfig( Config.withNanoKasInformation(kasInfos.toArray(new Config.KASInfo[0])), - Config.witDataAttributes("https://example.com/attr/Classification/value/S", - "https://example.com/attr/Classification/value/X") + Config.withEllipticCurve("secp384r1"), + Config.witDataAttributes("https://example.com/attr/Classification/value/S", "https://example.com/attr/Classification/value/X") ); + runBasicTest(null, true, kasRegistryService, null, config); + } + + void runBasicTest(String kasUrl, boolean allowed, KeyAccessServerRegistryServiceClient kasReg, NanoTDFReaderConfig decryptConfig, Config.NanoTDFConfig writerConfig) throws Exception { + Config.NanoTDFConfig config; + if (writerConfig == null) { + var kasInfos = new ArrayList<>(); + var kasInfo = new Config.KASInfo(); + kasInfo.URL = kasUrl; + kasInfo.PublicKey = null; + kasInfos.add(kasInfo); + config = Config.newNanoTDFConfig( + Config.withNanoKasInformation(kasInfos.toArray(new Config.KASInfo[0])), + Config.witDataAttributes("https://example.com/attr/Classification/value/S", "https://example.com/attr/Classification/value/X") + ); + } else { + config = writerConfig; + } String plainText = "Virtru!!"; ByteBuffer byteBuffer = ByteBuffer.wrap(plainText.getBytes()); @@ -244,8 +264,6 @@ void runBasicTest(String kasUrl, boolean allowed, KeyAccessServerRegistryService assertThat(e.getMessage()).contains("KasAllowlist"); } } - - } @Test @@ -265,18 +283,18 @@ void kasAllowlistTests() throws Exception { "https://localhost:8080/kas" ); for (String kasUrl : kasUrlsSuccess) { - runBasicTest(kasUrl, true, kasRegistryService, null); + runBasicTest(kasUrl, true, kasRegistryService, null, null); } for (String kasUrl : kasUrlsFail) { - runBasicTest(kasUrl, false, kasRegistryService, null); + runBasicTest(kasUrl, false, kasRegistryService, null, null); } // test with kasAllowlist - runBasicTest("http://api.example.com/kas", true, null, Config.newNanoTDFReaderConfig(Config.WithNanoKasAllowlist("http://api.example.com/kas"))); - runBasicTest(platformUrl+"/kas", false, null, Config.newNanoTDFReaderConfig(Config.WithNanoKasAllowlist("http://api.example.com/kas"))); + runBasicTest("http://api.example.com/kas", true, null, Config.newNanoTDFReaderConfig(Config.WithNanoKasAllowlist("http://api.example.com/kas")), null); + runBasicTest(platformUrl+"/kas", false, null, Config.newNanoTDFReaderConfig(Config.WithNanoKasAllowlist("http://api.example.com/kas")), null); // test ignore kasAllowlist - runBasicTest(platformUrl+"/kas", true, null, Config.newNanoTDFReaderConfig(Config.WithNanoKasAllowlist("http://api.example.com/kas"), Config.WithNanoIgnoreKasAllowlist(true))); + runBasicTest(platformUrl+"/kas", true, null, Config.newNanoTDFReaderConfig(Config.WithNanoKasAllowlist("http://api.example.com/kas"), Config.WithNanoIgnoreKasAllowlist(true)), null); } @Test From 1bc45053f55e32a2128ac4ca8fd1149984f6b956 Mon Sep 17 00:00:00 2001 From: Morgan Kleene Date: Tue, 8 Jul 2025 17:56:09 +0200 Subject: [PATCH 05/15] sonarcloud --- .../main/java/io/opentdf/platform/sdk/KASClient.java | 3 +-- .../main/java/io/opentdf/platform/sdk/KeyType.java | 11 +++++++---- .../java/io/opentdf/platform/sdk/NanoTDFType.java | 7 +++---- .../platform/sdk/SymmetricAndPayloadConfig.java | 2 -- sdk/src/main/java/io/opentdf/platform/sdk/TDF.java | 3 +-- 5 files changed, 12 insertions(+), 14 deletions(-) diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/KASClient.java b/sdk/src/main/java/io/opentdf/platform/sdk/KASClient.java index 44b6fe3b..09cffbf7 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/KASClient.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/KASClient.java @@ -147,9 +147,8 @@ static class NanoTDFRewrapRequestBody { @Override public byte[] unwrap(Manifest.KeyAccess keyAccess, String policy, KeyType sessionKeyType) { ECKeyPair ecKeyPair = null; - if (sessionKeyType.isEc()) { - var curve = sessionKeyType.getECEcurve().get(); + var curve = sessionKeyType.getECCurve(); ecKeyPair = new ECKeyPair(curve, ECKeyPair.ECAlgorithm.ECDH); clientPublicKey = ecKeyPair.publicKeyInPEMFormat(); } else { diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/KeyType.java b/sdk/src/main/java/io/opentdf/platform/sdk/KeyType.java index 3a4cfb7f..0f9cbd3d 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/KeyType.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/KeyType.java @@ -1,6 +1,6 @@ package io.opentdf.platform.sdk; -import java.util.Optional; +import javax.annotation.Nonnull; import static io.opentdf.platform.sdk.NanoTDFType.ECCurve.SECP256R1; import static io.opentdf.platform.sdk.NanoTDFType.ECCurve.SECP384R1; @@ -24,8 +24,12 @@ public enum KeyType { this(keyType, null); } - public Optional getECEcurve() { - return Optional.ofNullable(curve); + @Nonnull + NanoTDFType.ECCurve getECCurve() { + if (!isEc()) { + throw new IllegalStateException("This key type does not have an ECCurve associated with it: " + keyType); + } + return curve; } @Override @@ -33,7 +37,6 @@ public String toString() { return keyType; } - public static KeyType fromString(String keyType) { for (KeyType type : KeyType.values()) { if (type.keyType.equalsIgnoreCase(keyType)) { diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDFType.java b/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDFType.java index 9a16e103..b8bba287 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDFType.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDFType.java @@ -41,14 +41,13 @@ static ECCurve fromCurveMode(int curveMode) { throw new IllegalArgumentException("No enum constant for curve mode: " + curveMode); } - public static ECCurve fromAlgorithm(String algorithm) { + static ECCurve fromAlgorithm(String algorithm) { if (algorithm == null) { log.warn("got a null algorithm, returning SECP256R1 as default"); return SECP256R1; } - - assert algorithm.startsWith("ec:"); - var searchKey = algorithm.substring("ec:".length()); + var searchKey = algorithm.startsWith("ec:") ? algorithm.substring("ec:".length()) : algorithm; + log.debug("looking for algorithm [{}]", searchKey); return Arrays.stream(ECCurve.values()) .filter(v -> v.curveName.equalsIgnoreCase(searchKey)) .findAny() diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/SymmetricAndPayloadConfig.java b/sdk/src/main/java/io/opentdf/platform/sdk/SymmetricAndPayloadConfig.java index a5c46e72..fbca3007 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/SymmetricAndPayloadConfig.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/SymmetricAndPayloadConfig.java @@ -1,7 +1,5 @@ package io.opentdf.platform.sdk; -import io.opentdf.platform.policy.Key; - public class SymmetricAndPayloadConfig { private Data data; diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java b/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java index fd50a584..67f743ca 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java @@ -277,8 +277,7 @@ private Manifest.KeyAccess createKeyAccess(Config.TDFConfig tdfConfig, Config.KA } private ECKeyWrappedKeyInfo createECWrappedKey(Config.TDFConfig tdfConfig, Config.KASInfo kasInfo, byte[] symKey) { - var curve = tdfConfig.wrappingKeyType.getECEcurve().get(); - assert curve != null : "Wrapping key type must be an EC key type"; + var curve = tdfConfig.wrappingKeyType.getECCurve(); var keyPair = new ECKeyPair(curve, ECKeyPair.ECAlgorithm.ECDH); ECPublicKey kasPubKey = ECKeyPair.publicKeyFromPem(kasInfo.PublicKey); From 2814aa38465840f09cc29870797ef9d168818263 Mon Sep 17 00:00:00 2001 From: Morgan Kleene Date: Tue, 8 Jul 2025 18:16:30 +0200 Subject: [PATCH 06/15] get the enum value the right way --- sdk/src/main/java/io/opentdf/platform/sdk/ECCMode.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/ECCMode.java b/sdk/src/main/java/io/opentdf/platform/sdk/ECCMode.java index 688be171..484223d7 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/ECCMode.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/ECCMode.java @@ -44,10 +44,6 @@ public void setEllipticCurve(NanoTDFType.ECCurve curve) { } } - public NanoTDFType.ECCurve getCurve() { - return NanoTDFType.ECCurve.values()[data.curveMode]; - } - public boolean isECDSABindingEnabled() { return data.useECDSABinding == 1; } @@ -62,6 +58,10 @@ public static int getECDSASignatureStructSize(NanoTDFType.ECCurve curve) { return (1 + keySize + 1 + keySize); } + public NanoTDFType.ECCurve getCurve() { + return NanoTDFType.ECCurve.fromCurveMode(data.curveMode); + } + private class ECCModeStruct { int curveMode; int unused; From 52f0626a077939d1986f752ca7b20d1973136dcc Mon Sep 17 00:00:00 2001 From: Morgan Kleene Date: Tue, 8 Jul 2025 18:28:19 +0200 Subject: [PATCH 07/15] use the curveName --- sdk/src/main/java/io/opentdf/platform/sdk/KASClient.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/KASClient.java b/sdk/src/main/java/io/opentdf/platform/sdk/KASClient.java index 09cffbf7..ea0fa009 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/KASClient.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/KASClient.java @@ -21,6 +21,8 @@ import io.opentdf.platform.sdk.SDK.KasBadRequestException; import okhttp3.OkHttpClient; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -49,6 +51,8 @@ class KASClient implements SDK.KAS { private String clientPublicKey; private KASKeyCache kasKeyCache; + private static final Logger log = LoggerFactory.getLogger(KASClient.class); + /*** * A client that communicates with KAS * @@ -69,7 +73,9 @@ class KASClient implements SDK.KAS { @Override public KASInfo getECPublicKey(Config.KASInfo kasInfo, NanoTDFType.ECCurve curve) { - var req = PublicKeyRequest.newBuilder().setAlgorithm(format("ec:%s", curve.toString())).build(); + log.debug("retrieving public key with kasinfo = [{}]", kasInfo); + + var req = PublicKeyRequest.newBuilder().setAlgorithm(format("ec:%s", curve.curveName)).build(); var r = getStub(kasInfo.URL).publicKeyBlocking(req, Collections.emptyMap()).execute(); PublicKeyResponse res; try { From cd5506568bb21ddbd40afd370d10113029360361 Mon Sep 17 00:00:00 2001 From: Morgan Kleene Date: Tue, 8 Jul 2025 18:44:50 +0200 Subject: [PATCH 08/15] missed it here --- sdk/src/main/java/io/opentdf/platform/sdk/KASClient.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/KASClient.java b/sdk/src/main/java/io/opentdf/platform/sdk/KASClient.java index ea0fa009..e0cb3aaf 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/KASClient.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/KASClient.java @@ -203,7 +203,7 @@ public byte[] unwrap(Manifest.KeyAccess keyAccess, String policy, KeyType sessi } var wrappedKey = response.getEntityWrappedKey().toByteArray(); - if (sessionKeyType != KeyType.RSA2048Key) { + if (sessionKeyType.isEc()) { if (ecKeyPair == null) { throw new SDKException("ECKeyPair is null. Unable to proceed with the unwrap operation."); @@ -233,7 +233,7 @@ public byte[] unwrapNanoTDF(NanoTDFType.ECCurve curve, String header, String kas keyAccess.protocol = "kas"; NanoTDFRewrapRequestBody body = new NanoTDFRewrapRequestBody(); - body.algorithm = format("ec:%s", curve.toString()); + body.algorithm = format("ec:%s", curve.curveName); body.clientPublicKey = keyPair.publicKeyInPEMFormat(); body.keyAccess = keyAccess; From d8cb8e9d8ebeec9f57d11ea6547593d8c9cfe740 Mon Sep 17 00:00:00 2001 From: Morgan Kleene Date: Tue, 8 Jul 2025 22:38:05 +0200 Subject: [PATCH 09/15] switch these back --- sdk/src/main/java/io/opentdf/platform/sdk/ECCMode.java | 2 +- sdk/src/main/java/io/opentdf/platform/sdk/Header.java | 6 +++--- sdk/src/main/java/io/opentdf/platform/sdk/NanoTDFType.java | 4 ++-- sdk/src/test/java/io/opentdf/platform/sdk/HeaderTest.java | 2 +- .../java/io/opentdf/platform/sdk/NanoTDFHeaderTest.java | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/ECCMode.java b/sdk/src/main/java/io/opentdf/platform/sdk/ECCMode.java index 484223d7..621b6333 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/ECCMode.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/ECCMode.java @@ -54,7 +54,7 @@ public byte getECCModeAsByte() { } public static int getECDSASignatureStructSize(NanoTDFType.ECCurve curve) { - int keySize = curve.compressedPubKeySize; + int keySize = curve.keySize; return (1 + keySize + 1 + keySize); } diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/Header.java b/sdk/src/main/java/io/opentdf/platform/sdk/Header.java index d189a289..b76dccb5 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/Header.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/Header.java @@ -26,7 +26,7 @@ public Header(ByteBuffer buffer) { this.payloadConfig = new SymmetricAndPayloadConfig(buffer.get()); this.policyInfo = new PolicyInfo(buffer, this.eccMode); - int compressedPubKeySize = this.eccMode.getCurve().keySize; + int compressedPubKeySize = this.eccMode.getCurve().compressedPubKeySize; this.ephemeralKey = new byte[compressedPubKeySize]; buffer.get(this.ephemeralKey); } @@ -79,10 +79,10 @@ public PolicyInfo getPolicyInfo() { } public void setEphemeralKey(byte[] bytes) { - if (bytes.length < eccMode.getCurve().keySize) { + if (bytes.length < eccMode.getCurve().compressedPubKeySize) { throw new IllegalArgumentException("Failed to read ephemeral key - invalid buffer size."); } - ephemeralKey = Arrays.copyOf(bytes, eccMode.getCurve().keySize); + ephemeralKey = Arrays.copyOf(bytes, eccMode.getCurve().compressedPubKeySize); } public byte[] getEphemeralKey() { diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDFType.java b/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDFType.java index b8bba287..b7adbe63 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDFType.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDFType.java @@ -20,11 +20,11 @@ enum ECCurve { final boolean isSupported; - ECCurve(String curveName, int compressedPubKeySize, int keySize, int curveMode) { + ECCurve(String curveName, int keySize, int compressedPubKeySize, int curveMode) { this(curveName, compressedPubKeySize, keySize, curveMode, true); } - ECCurve(String curveName, int compressedPubKeySize, int keySize, int curveMode, boolean isSupported) { + ECCurve(String curveName, int keySize, int compressedPubKeySize, int curveMode, boolean isSupported) { this.compressedPubKeySize = compressedPubKeySize; this.keySize = keySize; this.curveMode = curveMode; diff --git a/sdk/src/test/java/io/opentdf/platform/sdk/HeaderTest.java b/sdk/src/test/java/io/opentdf/platform/sdk/HeaderTest.java index cfc36e61..7a8f0ac2 100644 --- a/sdk/src/test/java/io/opentdf/platform/sdk/HeaderTest.java +++ b/sdk/src/test/java/io/opentdf/platform/sdk/HeaderTest.java @@ -47,7 +47,7 @@ void settingAndGettingEphemeralKey() { ECCMode mode = new ECCMode((byte) 1); // Initialize the ECCMode object header.setECCMode(mode); // Set the ECCMode object - int keySize = mode.getCurve().keySize; + int keySize = mode.getCurve().compressedPubKeySize; byte[] key = new byte[keySize]; // Ensure the key size is correct header.setEphemeralKey(key); assertArrayEquals(key, header.getEphemeralKey()); diff --git a/sdk/src/test/java/io/opentdf/platform/sdk/NanoTDFHeaderTest.java b/sdk/src/test/java/io/opentdf/platform/sdk/NanoTDFHeaderTest.java index eead2988..67a2ab2b 100644 --- a/sdk/src/test/java/io/opentdf/platform/sdk/NanoTDFHeaderTest.java +++ b/sdk/src/test/java/io/opentdf/platform/sdk/NanoTDFHeaderTest.java @@ -169,7 +169,7 @@ public void testNanoTDFReader() header2.setPolicyInfo(policyInfo); int sizeToRead = policyInfo.getTotalSize(); - int compressedPubKeySize = header2.getECCMode().getCurve().keySize; + int compressedPubKeySize = header2.getECCMode().getCurve().compressedPubKeySize; byte[] ephemeralKey = new byte[compressedPubKeySize]; // size of compressed public key System.arraycopy(remainingBytesArray, sizeToRead, ephemeralKey, 0, ephemeralKey.length); header2.setEphemeralKey(ephemeralKey); From 41a112070deeabd2e3657b49f81bf5c0f33e0351 Mon Sep 17 00:00:00 2001 From: Morgan Kleene Date: Tue, 8 Jul 2025 22:52:25 +0200 Subject: [PATCH 10/15] oops --- sdk/src/main/java/io/opentdf/platform/sdk/NanoTDFType.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDFType.java b/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDFType.java index b7adbe63..7c2f290d 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDFType.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDFType.java @@ -21,14 +21,14 @@ enum ECCurve { ECCurve(String curveName, int keySize, int compressedPubKeySize, int curveMode) { - this(curveName, compressedPubKeySize, keySize, curveMode, true); + this(curveName, keySize, compressedPubKeySize, curveMode, true); } ECCurve(String curveName, int keySize, int compressedPubKeySize, int curveMode, boolean isSupported) { - this.compressedPubKeySize = compressedPubKeySize; + this.curveName = curveName ; this.keySize = keySize; + this.compressedPubKeySize = compressedPubKeySize; this.curveMode = curveMode; - this.curveName = curveName ; this.isSupported = isSupported; } From 0c96358e583d053e26f22bb202987261a0aa1337 Mon Sep 17 00:00:00 2001 From: Morgan Kleene Date: Wed, 9 Jul 2025 08:59:32 +0200 Subject: [PATCH 11/15] throw an error instead --- sdk/src/main/java/io/opentdf/platform/sdk/NanoTDFType.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDFType.java b/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDFType.java index 7c2f290d..f9d59d35 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDFType.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDFType.java @@ -4,6 +4,7 @@ import org.slf4j.LoggerFactory; import java.util.Arrays; +import java.util.Objects; public class NanoTDFType { private static final Logger log = LoggerFactory.getLogger(NanoTDFType.class); @@ -42,10 +43,8 @@ static ECCurve fromCurveMode(int curveMode) { } static ECCurve fromAlgorithm(String algorithm) { - if (algorithm == null) { - log.warn("got a null algorithm, returning SECP256R1 as default"); - return SECP256R1; - } + Objects.requireNonNull(algorithm, "Algorithm cannot be null"); + var searchKey = algorithm.startsWith("ec:") ? algorithm.substring("ec:".length()) : algorithm; log.debug("looking for algorithm [{}]", searchKey); return Arrays.stream(ECCurve.values()) From 0475afc3470525a350b211044d86a5108b56ee81 Mon Sep 17 00:00:00 2001 From: Morgan Kleene Date: Wed, 9 Jul 2025 09:12:34 +0200 Subject: [PATCH 12/15] encapsulate fields and be more pedantic --- .../java/io/opentdf/platform/sdk/ECCMode.java | 2 +- .../io/opentdf/platform/sdk/ECKeyPair.java | 2 +- .../java/io/opentdf/platform/sdk/Header.java | 6 +-- .../io/opentdf/platform/sdk/KASClient.java | 4 +- .../io/opentdf/platform/sdk/NanoTDFType.java | 47 ++++++++++++++----- .../sdk/SymmetricAndPayloadConfig.java | 6 +-- .../opentdf/platform/sdk/ECKeyPairTest.java | 2 +- .../io/opentdf/platform/sdk/HeaderTest.java | 2 +- .../platform/sdk/NanoTDFHeaderTest.java | 2 +- .../io/opentdf/platform/sdk/NanoTDFTest.java | 3 +- .../java/io/opentdf/platform/sdk/TDFTest.java | 2 +- 11 files changed, 51 insertions(+), 27 deletions(-) diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/ECCMode.java b/sdk/src/main/java/io/opentdf/platform/sdk/ECCMode.java index 621b6333..46570e9b 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/ECCMode.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/ECCMode.java @@ -54,7 +54,7 @@ public byte getECCModeAsByte() { } public static int getECDSASignatureStructSize(NanoTDFType.ECCurve curve) { - int keySize = curve.keySize; + int keySize = curve.getKeySize(); return (1 + keySize + 1 + keySize); } diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/ECKeyPair.java b/sdk/src/main/java/io/opentdf/platform/sdk/ECKeyPair.java index 294ec7da..93b09557 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/ECKeyPair.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/ECKeyPair.java @@ -64,7 +64,7 @@ public ECKeyPair(NanoTDFType.ECCurve curve, ECAlgorithm algorithm) { throw new RuntimeException(e); } - ECGenParameterSpec spec = new ECGenParameterSpec(this.curve.curveName); + ECGenParameterSpec spec = new ECGenParameterSpec(this.curve.getCurveName()); try { generator.initialize(spec); } catch (InvalidAlgorithmParameterException e) { diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/Header.java b/sdk/src/main/java/io/opentdf/platform/sdk/Header.java index b76dccb5..69a93a60 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/Header.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/Header.java @@ -26,7 +26,7 @@ public Header(ByteBuffer buffer) { this.payloadConfig = new SymmetricAndPayloadConfig(buffer.get()); this.policyInfo = new PolicyInfo(buffer, this.eccMode); - int compressedPubKeySize = this.eccMode.getCurve().compressedPubKeySize; + int compressedPubKeySize = this.eccMode.getCurve().getCompressedPubKeySize(); this.ephemeralKey = new byte[compressedPubKeySize]; buffer.get(this.ephemeralKey); } @@ -79,10 +79,10 @@ public PolicyInfo getPolicyInfo() { } public void setEphemeralKey(byte[] bytes) { - if (bytes.length < eccMode.getCurve().compressedPubKeySize) { + if (bytes.length < eccMode.getCurve().getCompressedPubKeySize()) { throw new IllegalArgumentException("Failed to read ephemeral key - invalid buffer size."); } - ephemeralKey = Arrays.copyOf(bytes, eccMode.getCurve().compressedPubKeySize); + ephemeralKey = Arrays.copyOf(bytes, eccMode.getCurve().getCompressedPubKeySize()); } public byte[] getEphemeralKey() { diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/KASClient.java b/sdk/src/main/java/io/opentdf/platform/sdk/KASClient.java index e0cb3aaf..beff0e5b 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/KASClient.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/KASClient.java @@ -75,7 +75,7 @@ class KASClient implements SDK.KAS { public KASInfo getECPublicKey(Config.KASInfo kasInfo, NanoTDFType.ECCurve curve) { log.debug("retrieving public key with kasinfo = [{}]", kasInfo); - var req = PublicKeyRequest.newBuilder().setAlgorithm(format("ec:%s", curve.curveName)).build(); + var req = PublicKeyRequest.newBuilder().setAlgorithm(curve.getPlatformCurveName()).build(); var r = getStub(kasInfo.URL).publicKeyBlocking(req, Collections.emptyMap()).execute(); PublicKeyResponse res; try { @@ -233,7 +233,7 @@ public byte[] unwrapNanoTDF(NanoTDFType.ECCurve curve, String header, String kas keyAccess.protocol = "kas"; NanoTDFRewrapRequestBody body = new NanoTDFRewrapRequestBody(); - body.algorithm = format("ec:%s", curve.curveName); + body.algorithm = format("ec:%s", curve.getCurveName()); body.clientPublicKey = keyPair.publicKeyInPEMFormat(); body.keyAccess = keyAccess; diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDFType.java b/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDFType.java index f9d59d35..5a7c8d07 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDFType.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDFType.java @@ -14,11 +14,11 @@ enum ECCurve { SECP521R1("secp521r1", 66, 67, 0x02), SECP256K1("secp256k1",-1, -1, -1, false); // Note: SECP256K1 is not supported by the SDK - final int curveMode; - final int keySize; - final int compressedPubKeySize; - final String curveName; - final boolean isSupported; + private final int curveMode; + private final int keySize; + private final int compressedPubKeySize; + private final String curveName; + private final boolean isSupported; ECCurve(String curveName, int keySize, int compressedPubKeySize, int curveMode) { @@ -35,22 +35,45 @@ enum ECCurve { static ECCurve fromCurveMode(int curveMode) { for (ECCurve curve : ECCurve.values()) { - if (curve.curveMode == curveMode) { + if (curve.getCurveMode() == curveMode) { return curve; } } throw new IllegalArgumentException("No enum constant for curve mode: " + curveMode); } - static ECCurve fromAlgorithm(String algorithm) { - Objects.requireNonNull(algorithm, "Algorithm cannot be null"); + static ECCurve fromAlgorithm(String platformAlgorithm) { + Objects.requireNonNull(platformAlgorithm, "Algorithm cannot be null"); - var searchKey = algorithm.startsWith("ec:") ? algorithm.substring("ec:".length()) : algorithm; - log.debug("looking for algorithm [{}]", searchKey); + log.debug("looking for platformAlgorithm [{}]", platformAlgorithm); return Arrays.stream(ECCurve.values()) - .filter(v -> v.curveName.equalsIgnoreCase(searchKey)) + .filter(v -> v.getPlatformCurveName().equals(platformAlgorithm)) .findAny() - .orElseThrow(() -> new IllegalArgumentException(String.format("No enum constant for algorithm: %s", algorithm))); + .orElseThrow(() -> new IllegalArgumentException(String.format("No enum constant for platformAlgorithm: %s", platformAlgorithm))); + } + + int getCurveMode() { + return curveMode; + } + + int getKeySize() { + return keySize; + } + + int getCompressedPubKeySize() { + return compressedPubKeySize; + } + + String getCurveName() { + return curveName; + } + + String getPlatformCurveName() { + return String.format("ec:%s", curveName); + } + + boolean isSupported() { + return isSupported; } } // ResourceLocator Protocol diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/SymmetricAndPayloadConfig.java b/sdk/src/main/java/io/opentdf/platform/sdk/SymmetricAndPayloadConfig.java index fbca3007..8d4a8b3d 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/SymmetricAndPayloadConfig.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/SymmetricAndPayloadConfig.java @@ -28,10 +28,10 @@ public void setHasSignature(boolean flag) { } public void setSignatureECCMode(NanoTDFType.ECCurve eccCurve) { - if (!eccCurve.isSupported) { - throw new RuntimeException(String.format("Unsupported ECC algorithm: %s", eccCurve.curveName)); + if (!eccCurve.isSupported()) { + throw new SDKException(String.format("Unsupported ECC algorithm: %s", eccCurve.getCurveName())); } - data.signatureECCMode = eccCurve.curveMode; + data.signatureECCMode = eccCurve.getCurveMode(); } public void setSymmetricCipherType(NanoTDFType.Cipher cipherType) { diff --git a/sdk/src/test/java/io/opentdf/platform/sdk/ECKeyPairTest.java b/sdk/src/test/java/io/opentdf/platform/sdk/ECKeyPairTest.java index d2c649fb..f6f3125c 100644 --- a/sdk/src/test/java/io/opentdf/platform/sdk/ECKeyPairTest.java +++ b/sdk/src/test/java/io/opentdf/platform/sdk/ECKeyPairTest.java @@ -63,7 +63,7 @@ void ecPublicKeyInPemformat() { byte[] compressedKey2 = ECKeyPair.compressECPublickey(keyPairA.publicKeyInPEMFormat()); assertArrayEquals(compressedKey1, compressedKey2); - String publicKey = ECKeyPair.publicKeyFromECPoint(compressedKey1, SECP256R1.curveName); + String publicKey = ECKeyPair.publicKeyFromECPoint(compressedKey1, SECP256R1.getCurveName()); assertEquals(keyPairA.publicKeyInPEMFormat(), publicKey); ECKeyPair keyPairB = new ECKeyPair(); diff --git a/sdk/src/test/java/io/opentdf/platform/sdk/HeaderTest.java b/sdk/src/test/java/io/opentdf/platform/sdk/HeaderTest.java index 7a8f0ac2..8fd231a3 100644 --- a/sdk/src/test/java/io/opentdf/platform/sdk/HeaderTest.java +++ b/sdk/src/test/java/io/opentdf/platform/sdk/HeaderTest.java @@ -47,7 +47,7 @@ void settingAndGettingEphemeralKey() { ECCMode mode = new ECCMode((byte) 1); // Initialize the ECCMode object header.setECCMode(mode); // Set the ECCMode object - int keySize = mode.getCurve().compressedPubKeySize; + int keySize = mode.getCurve().getCompressedPubKeySize(); byte[] key = new byte[keySize]; // Ensure the key size is correct header.setEphemeralKey(key); assertArrayEquals(key, header.getEphemeralKey()); diff --git a/sdk/src/test/java/io/opentdf/platform/sdk/NanoTDFHeaderTest.java b/sdk/src/test/java/io/opentdf/platform/sdk/NanoTDFHeaderTest.java index 67a2ab2b..5019c198 100644 --- a/sdk/src/test/java/io/opentdf/platform/sdk/NanoTDFHeaderTest.java +++ b/sdk/src/test/java/io/opentdf/platform/sdk/NanoTDFHeaderTest.java @@ -169,7 +169,7 @@ public void testNanoTDFReader() header2.setPolicyInfo(policyInfo); int sizeToRead = policyInfo.getTotalSize(); - int compressedPubKeySize = header2.getECCMode().getCurve().compressedPubKeySize; + int compressedPubKeySize = header2.getECCMode().getCurve().getCompressedPubKeySize(); byte[] ephemeralKey = new byte[compressedPubKeySize]; // size of compressed public key System.arraycopy(remainingBytesArray, sizeToRead, ephemeralKey, 0, ephemeralKey.length); header2.setEphemeralKey(ephemeralKey); diff --git a/sdk/src/test/java/io/opentdf/platform/sdk/NanoTDFTest.java b/sdk/src/test/java/io/opentdf/platform/sdk/NanoTDFTest.java index 635c129b..73c38087 100644 --- a/sdk/src/test/java/io/opentdf/platform/sdk/NanoTDFTest.java +++ b/sdk/src/test/java/io/opentdf/platform/sdk/NanoTDFTest.java @@ -99,7 +99,7 @@ public byte[] unwrapNanoTDF(NanoTDFType.ECCurve curve, String header, String kas Header nTDFHeader = new Header(ByteBuffer.wrap(headerAsBytes)); byte[] ephemeralKey = nTDFHeader.getEphemeralKey(); - String publicKeyAsPem = ECKeyPair.publicKeyFromECPoint(ephemeralKey, nTDFHeader.getECCMode().getCurve().curveName); + String publicKeyAsPem = ECKeyPair.publicKeyFromECPoint(ephemeralKey, nTDFHeader.getECCMode().getCurve().getCurveName()); // Generate symmetric key byte[] symmetricKey = ECKeyPair.computeECDHKey(ECKeyPair.publicKeyFromPem(publicKeyAsPem), @@ -341,6 +341,7 @@ public void testNanoTDFWithPlainTextPolicy() throws Exception { kasInfo.URL = sampleKasUrl; kasInfo.PublicKey = kasPublicKey; kasInfo.KID = KID; + kasInfo.Algorithm = "ec:secp256r1"; kasInfos.add(kasInfo); Config.NanoTDFConfig config = Config.newNanoTDFConfig( diff --git a/sdk/src/test/java/io/opentdf/platform/sdk/TDFTest.java b/sdk/src/test/java/io/opentdf/platform/sdk/TDFTest.java index 170b2c70..517ca8f9 100644 --- a/sdk/src/test/java/io/opentdf/platform/sdk/TDFTest.java +++ b/sdk/src/test/java/io/opentdf/platform/sdk/TDFTest.java @@ -123,7 +123,7 @@ static void setupKeyPairsAndMocks() { if (i % 2 == 0) { keypairs.add(CryptoUtils.generateRSAKeypair()); } else { - keypairs.add(CryptoUtils.generateECKeypair(NanoTDFType.ECCurve.SECP256R1.curveName)); + keypairs.add(CryptoUtils.generateECKeypair(NanoTDFType.ECCurve.SECP256R1.getCurveName())); } } From 04cac764423d5bba0d70d992ca29eaeda540a461 Mon Sep 17 00:00:00 2001 From: Morgan Kleene Date: Wed, 9 Jul 2025 09:33:50 +0200 Subject: [PATCH 13/15] sometimes the algorithm is not specified --- .../java/io/opentdf/platform/sdk/ECCMode.java | 4 ++++ .../java/io/opentdf/platform/sdk/NanoTDF.java | 15 ++++++++++++--- .../java/io/opentdf/platform/sdk/NanoTDFType.java | 13 ++++++++----- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/ECCMode.java b/sdk/src/main/java/io/opentdf/platform/sdk/ECCMode.java index 46570e9b..98d89feb 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/ECCMode.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/ECCMode.java @@ -1,5 +1,7 @@ package io.opentdf.platform.sdk; +import javax.annotation.Nonnull; + public class ECCMode { private ECCModeStruct data; @@ -58,6 +60,8 @@ public static int getECDSASignatureStructSize(NanoTDFType.ECCurve curve) { return (1 + keySize + 1 + keySize); } + + @Nonnull public NanoTDFType.ECCurve getCurve() { return NanoTDFType.ECCurve.fromCurveMode(data.curveMode); } diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDF.java b/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDF.java index 0abc0bb7..b3adc098 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDF.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDF.java @@ -20,6 +20,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static io.opentdf.platform.sdk.NanoTDFType.ECCurve.SECP256R1; + /** * The NanoTDF class provides methods to create and read NanoTDF (Tiny Data Format) files. * The NanoTDF format is intended for securely encrypting small data payloads using elliptic-curve cryptography @@ -91,9 +93,16 @@ private Config.HeaderInfo getHeaderInfo(Config.NanoTDFConfig nanoTDFConfig) thro // it might be better to pull the curve from the OIDC in the PEM but it looks like we // are just taking the Algorithm as correct - var ecCurve = NanoTDFType.ECCurve.fromAlgorithm(kasInfo.Algorithm); - if (ecCurve != nanoTDFConfig.eccMode.getCurve()) { - logger.warn("ECCurve in NanoTDFConfig [{}] does not match the curve in KASInfo, using KASInfo curve [{}]", nanoTDFConfig.eccMode.getCurve(), ecCurve); + Optional specifiedCurve = NanoTDFType.ECCurve.fromAlgorithm(kasInfo.Algorithm); + NanoTDFType.ECCurve ecCurve; + if (specifiedCurve.isEmpty()) { + logger.info("no curve specified in KASInfo, using the curve from config", nanoTDFConfig.eccMode.getCurve()); + ecCurve = nanoTDFConfig.eccMode.getCurve(); + } else { + if (specifiedCurve.get() != nanoTDFConfig.eccMode.getCurve()) { + logger.warn("ECCurve in NanoTDFConfig [{}] does not match the curve in KASInfo, using KASInfo curve [{}]", nanoTDFConfig.eccMode.getCurve(), specifiedCurve); + } + ecCurve = specifiedCurve.get(); } ECKeyPair keyPair = new ECKeyPair(ecCurve, ECKeyPair.ECAlgorithm.ECDSA); diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDFType.java b/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDFType.java index 5a7c8d07..16351a01 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDFType.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDFType.java @@ -3,8 +3,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.annotation.Nonnull; import java.util.Arrays; import java.util.Objects; +import java.util.Optional; public class NanoTDFType { private static final Logger log = LoggerFactory.getLogger(NanoTDFType.class); @@ -33,6 +35,7 @@ enum ECCurve { this.isSupported = isSupported; } + @Nonnull static ECCurve fromCurveMode(int curveMode) { for (ECCurve curve : ECCurve.values()) { if (curve.getCurveMode() == curveMode) { @@ -42,14 +45,14 @@ static ECCurve fromCurveMode(int curveMode) { throw new IllegalArgumentException("No enum constant for curve mode: " + curveMode); } - static ECCurve fromAlgorithm(String platformAlgorithm) { - Objects.requireNonNull(platformAlgorithm, "Algorithm cannot be null"); - + static Optional fromAlgorithm(String platformAlgorithm) { log.debug("looking for platformAlgorithm [{}]", platformAlgorithm); + if (platformAlgorithm == null) { + return Optional.empty(); + } return Arrays.stream(ECCurve.values()) .filter(v -> v.getPlatformCurveName().equals(platformAlgorithm)) - .findAny() - .orElseThrow(() -> new IllegalArgumentException(String.format("No enum constant for platformAlgorithm: %s", platformAlgorithm))); + .findAny(); } int getCurveMode() { From 75bde9d4afde9094fafa458063a619f4f138155d Mon Sep 17 00:00:00 2001 From: Morgan Kleene Date: Wed, 9 Jul 2025 09:41:39 +0200 Subject: [PATCH 14/15] more sonar --- .../java/io/opentdf/platform/sdk/NanoTDF.java | 33 ++++++++++--------- .../io/opentdf/platform/sdk/NanoTDFType.java | 1 - 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDF.java b/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDF.java index b3adc098..2ea97801 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDF.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDF.java @@ -20,8 +20,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static io.opentdf.platform.sdk.NanoTDFType.ECCurve.SECP256R1; - /** * The NanoTDF class provides methods to create and read NanoTDF (Tiny Data Format) files. * The NanoTDF format is intended for securely encrypting small data payloads using elliptic-curve cryptography @@ -91,19 +89,7 @@ private Config.HeaderInfo getHeaderInfo(Config.NanoTDFConfig nanoTDFConfig) thro ResourceLocator kasURL = new ResourceLocator(nanoTDFConfig.kasInfoList.get(0).URL, kasInfo.KID); assert kasURL.getIdentifier() != null : "Identifier in ResourceLocator cannot be null"; - // it might be better to pull the curve from the OIDC in the PEM but it looks like we - // are just taking the Algorithm as correct - Optional specifiedCurve = NanoTDFType.ECCurve.fromAlgorithm(kasInfo.Algorithm); - NanoTDFType.ECCurve ecCurve; - if (specifiedCurve.isEmpty()) { - logger.info("no curve specified in KASInfo, using the curve from config", nanoTDFConfig.eccMode.getCurve()); - ecCurve = nanoTDFConfig.eccMode.getCurve(); - } else { - if (specifiedCurve.get() != nanoTDFConfig.eccMode.getCurve()) { - logger.warn("ECCurve in NanoTDFConfig [{}] does not match the curve in KASInfo, using KASInfo curve [{}]", nanoTDFConfig.eccMode.getCurve(), specifiedCurve); - } - ecCurve = specifiedCurve.get(); - } + NanoTDFType.ECCurve ecCurve = getEcCurve(nanoTDFConfig, kasInfo); ECKeyPair keyPair = new ECKeyPair(ecCurve, ECKeyPair.ECAlgorithm.ECDSA); // Generate symmetric key @@ -174,6 +160,23 @@ private Config.HeaderInfo getHeaderInfo(Config.NanoTDFConfig nanoTDFConfig) thro return headerInfo; } + private static NanoTDFType.ECCurve getEcCurve(Config.NanoTDFConfig nanoTDFConfig, Config.KASInfo kasInfo) { + // it might be better to pull the curve from the OIDC in the PEM but it looks like we + // are just taking the Algorithm as correct + Optional specifiedCurve = NanoTDFType.ECCurve.fromAlgorithm(kasInfo.Algorithm); + NanoTDFType.ECCurve ecCurve; + if (specifiedCurve.isEmpty()) { + logger.info("no curve specified in KASInfo, using the curve from config [{}]", nanoTDFConfig.eccMode.getCurve()); + ecCurve = nanoTDFConfig.eccMode.getCurve(); + } else { + if (specifiedCurve.get() != nanoTDFConfig.eccMode.getCurve()) { + logger.warn("ECCurve in NanoTDFConfig [{}] does not match the curve in KASInfo, using KASInfo curve [{}]", nanoTDFConfig.eccMode.getCurve(), specifiedCurve); + } + ecCurve = specifiedCurve.get(); + } + return ecCurve; + } + public int createNanoTDF(ByteBuffer data, OutputStream outputStream, Config.NanoTDFConfig nanoTDFConfig) throws SDKException, IOException { int nanoTDFSize = 0; diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDFType.java b/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDFType.java index 16351a01..69967f28 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDFType.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDFType.java @@ -5,7 +5,6 @@ import javax.annotation.Nonnull; import java.util.Arrays; -import java.util.Objects; import java.util.Optional; public class NanoTDFType { From 5c0e3094d335248b5d13412834d5abc5dabe84d7 Mon Sep 17 00:00:00 2001 From: Morgan Kleene Date: Wed, 9 Jul 2025 16:08:36 +0200 Subject: [PATCH 15/15] Update NanoTDFType.java --- sdk/src/main/java/io/opentdf/platform/sdk/NanoTDFType.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDFType.java b/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDFType.java index 69967f28..46743383 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDFType.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDFType.java @@ -17,6 +17,8 @@ enum ECCurve { private final int curveMode; private final int keySize; + // compressedPubKeySize is a byte bigger since it encodes the X coordinate plus a byte that tells + // if the Y coordinate is positive or negative private final int compressedPubKeySize; private final String curveName; private final boolean isSupported;