diff --git a/src/main/java/com/ibm/crypto/plus/provider/ECDHKeyAgreement.java b/src/main/java/com/ibm/crypto/plus/provider/ECDHKeyAgreement.java index 4cd38a9b5..60d8105c7 100644 --- a/src/main/java/com/ibm/crypto/plus/provider/ECDHKeyAgreement.java +++ b/src/main/java/com/ibm/crypto/plus/provider/ECDHKeyAgreement.java @@ -26,6 +26,7 @@ import javax.crypto.SecretKey; import javax.crypto.ShortBufferException; import javax.crypto.spec.SecretKeySpec; +import sun.security.util.ObjectIdentifier; public final class ECDHKeyAgreement extends KeyAgreementSpi { // implements // AlgorithmStatus @@ -44,6 +45,7 @@ public final class ECDHKeyAgreement extends KeyAgreementSpi { // implements private ECPublicKey ecPublicKey = null; private ECPrivateKey ecPrivateKey = null; private int secretLen; + private static boolean disableSmallCurve = Boolean.parseBoolean(System.getProperty("openjceplus.disableSmallerECKeySizeForSharedKeyComputing", "true")); public ECDHKeyAgreement(OpenJCEPlusProvider provider) { // System.out.println ("In ECDHKeyAgreement"); @@ -164,6 +166,29 @@ protected byte[] engineGenerateSecret() throws IllegalStateException { throw new IllegalStateException("Wrong state"); } + if (provider.isFIPS() && disableSmallCurve) { + ECNamedCurve ecPubKeyNamedCurve = ECParameters.getNamedCurve(this.ecPublicKey.getParams()); + ECNamedCurve ecPriKeyNamedCurve = ECParameters.getNamedCurve(this.ecPrivateKey.getParams()); + if (ecPubKeyNamedCurve == null || ecPriKeyNamedCurve == null) { + throw new IllegalStateException("Curve name is not recognized or not supported"); + } + ObjectIdentifier oidPubKey = ECNamedCurve.getOIDFromName(ecPubKeyNamedCurve.getName()); + ObjectIdentifier oidPriKey = ECNamedCurve.getOIDFromName(ecPriKeyNamedCurve.getName()); + + // check if curve is FIPS allowance and if it is allowed for ECDH shared secret computation + if (!((oidPubKey.toString()).equals("1.2.840.10045.3.1.7") // secp256r1 [NIST P-256, X9.62 prime256v1] + || (oidPubKey.toString()).equals("1.3.132.0.33") // secp224r1 [NIST P-224] + || (oidPubKey.toString()).equals("1.3.132.0.34") // secp384r1 [NIST P-384] + || (oidPubKey.toString()).equals("1.3.132.0.35")) || + !((oidPriKey.toString()).equals("1.2.840.10045.3.1.7") // secp256r1 [NIST P-256, X9.62 prime256v1] + || (oidPriKey.toString()).equals("1.3.132.0.33") // secp224r1 [NIST P-224] + || (oidPriKey.toString()).equals("1.3.132.0.34") // secp384r1 [NIST P-384] + || (oidPriKey.toString()).equals("1.3.132.0.35")) // secp521r1 [NIST P-521] + ) { + throw new IllegalStateException(ecPubKeyNamedCurve.getName() + " curve is not supported in FIPS for calculating the shared secret"); + } + } + // Reset the key agreement here (in case anything goes wrong) generateSecret = false; byte[] secret = null; diff --git a/src/test/java/ibm/jceplus/junit/base/BaseTestECDH.java b/src/test/java/ibm/jceplus/junit/base/BaseTestECDH.java index ad9de76af..4b0dce547 100644 --- a/src/test/java/ibm/jceplus/junit/base/BaseTestECDH.java +++ b/src/test/java/ibm/jceplus/junit/base/BaseTestECDH.java @@ -44,13 +44,13 @@ public class BaseTestECDH extends BaseTestJunit5 { static final byte[] origMsg = "this is the original message to be signed".getBytes(); - static ECGenParameterSpec algParameterSpec_192k1, algParameterSpec_256r1; + static ECGenParameterSpec algParameterSpec_192r1, algParameterSpec_256r1; static KeyPairGenerator kpgA = null; static KeyPairGenerator kpgB = null; - static KeyPair keyPairA_192k1, keyPairA_256r1; - static KeyPair keyPairB_192k1, keyPairB_256r1; + static KeyPair keyPairA_192r1, keyPairA_256r1; + static KeyPair keyPairB_192r1, keyPairB_256r1; private boolean isMulti = false; @@ -70,21 +70,21 @@ synchronized static void generateParams(String provider_name) { try { //String provider_name = "OpenJCEPlus"; - String curveName_192k1 = "secp192k1"; + String curveName_192r1 = "secp192r1"; String curveName_256r1 = "secp256r1"; - algParameterSpec_192k1 = new ECGenParameterSpec(curveName_192k1); + algParameterSpec_192r1 = new ECGenParameterSpec(curveName_192r1); algParameterSpec_256r1 = new ECGenParameterSpec(curveName_256r1); kpgA = KeyPairGenerator.getInstance("EC", provider_name); - kpgA.initialize(algParameterSpec_192k1); - keyPairA_192k1 = kpgA.generateKeyPair(); + kpgA.initialize(algParameterSpec_192r1); + keyPairA_192r1 = kpgA.generateKeyPair(); kpgA.initialize(algParameterSpec_256r1); keyPairA_256r1 = kpgA.generateKeyPair(); kpgB = KeyPairGenerator.getInstance("EC", provider_name); - kpgB.initialize(algParameterSpec_192k1); - keyPairB_192k1 = kpgB.generateKeyPair(); + kpgB.initialize(algParameterSpec_192r1); + keyPairB_192r1 = kpgB.generateKeyPair(); kpgB.initialize(algParameterSpec_256r1); keyPairB_256r1 = kpgB.generateKeyPair(); @@ -112,15 +112,15 @@ public void setUp() { * @throws Exception */ @Test - public void testECDH_secp192k1() throws Exception { + public void testECDH_secp192r1() throws Exception { - String curveName = "secp192k1"; + String curveName = "secp192r1"; ECGenParameterSpec ecgn = new ECGenParameterSpec(curveName); compute_ecdh_key(curveName, ecgn); if (isMulti) - compute_ecdh_key_with_global_key(curveName, algParameterSpec_192k1); + compute_ecdh_key_with_global_key(curveName, algParameterSpec_192r1); } @@ -142,27 +142,34 @@ public void testECDH_ECSpec() throws Exception { String methodId = "ECDHECParamSpec"; - // These values were copied from ECNamedCurve.java in IBMJCEFIPS - String sfield_p256 = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F"; - String sa_p256 = "0000000000000000000000000000000000000000000000000000000000000000"; - String sb_p256 = "0000000000000000000000000000000000000000000000000000000000000007"; - String sx_p256 = "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798"; - String sy_p256 = "483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8"; - String sorder_p256 = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"; + // secp256r1 / prime256v1 (NIST P-256) + String sfield_p256r1 = "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF"; + String sa_p256r1 = "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC"; + String sb_p256r1 = "5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B"; + String sx_p256r1 = "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296"; + String sy_p256r1 = "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5"; + String sorder_p256r1 = "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551"; - BigInteger p = new BigInteger(sfield_p256, 16); + BigInteger p = new BigInteger(sfield_p256r1, 16); ECField field = new ECFieldFp(p); - EllipticCurve curve = new EllipticCurve(field, new BigInteger(sa_p256, 16), - new BigInteger(sb_p256, 16)); - ECPoint g = new ECPoint(new BigInteger(sx_p256, 16), new BigInteger(sy_p256, 16)); - BigInteger order = new BigInteger(sorder_p256, 16); + EllipticCurve curve = new EllipticCurve( + field, + new BigInteger(sa_p256r1, 16), + new BigInteger(sb_p256r1, 16) + ); + ECPoint g = new ECPoint( + new BigInteger(sx_p256r1, 16), + new BigInteger(sy_p256r1, 16) + ); + + BigInteger order = new BigInteger(sorder_p256r1, 16); int cofactor = 1; + ECParameterSpec ecParamSpec = new ECParameterSpec(curve, g, order, cofactor); compute_ecdh_key(methodId, ecParamSpec); - } void compute_ecdh_key(String idString, AlgorithmParameterSpec algParameterSpec) @@ -271,14 +278,24 @@ void compute_ecdh_key(String idString, AlgorithmParameterSpec algParameterSpec) throw e; } - // Generate the key bytes - byte[] sharedSecretA = keyAgreeA.generateSecret(); - byte[] sharedSecretB = keyAgreeB.generateSecret(); - // System.out.println(methodName + " sharedSecretA = " + BaseUtils.bytesToHex(sharedSecretA)); - // System.out.println(methodName + " sharedSecretB = " + BaseUtils.bytesToHex(sharedSecretB)); - - assertTrue(Arrays.equals(sharedSecretA, sharedSecretB)); - + try { + // Generate the key bytes + byte[] sharedSecretA = keyAgreeA.generateSecret(); + byte[] sharedSecretB = keyAgreeB.generateSecret(); + assertTrue(Arrays.equals(sharedSecretA, sharedSecretB)); + } catch (IllegalStateException ise) { + if (getProviderName().equals(("OpenJCEPlusFIPS"))) { + if (idString.equals("secp192r1")) { + assertTrue(ise.getMessage().equals("NIST P-192 curve is not supported in FIPS for calculating the shared secret")); + } else { + ise.printStackTrace(); + throw ise; + } + } else { + ise.printStackTrace(); + throw ise; + } + } } /* @@ -333,9 +350,9 @@ void compute_ecdh_key_with_global_key(String idString, AlgorithmParameterSpec al final String methodName = "compute_ecdh_key_with_global_key" + "_" + idString; KeyPair keyPairA = null, keyPairB = null; switch (idString) { - case "secp192k1": - keyPairA = keyPairA_192k1; - keyPairB = keyPairB_192k1; + case "secp192r1": + keyPairA = keyPairA_192r1; + keyPairB = keyPairB_192r1; break; case "secp256r1": @@ -399,11 +416,25 @@ void compute_ecdh_key_with_global_key(String idString, AlgorithmParameterSpec al throw e; } - // Generate the key bytes - byte[] sharedSecretA = keyAgreeA.generateSecret(); - byte[] sharedSecretB = keyAgreeB.generateSecret(); - System.out.println(methodName + " sharedSecretB = " + BaseUtils.bytesToHex(sharedSecretB)); - assertTrue(Arrays.equals(sharedSecretA, sharedSecretB)); + try { + // Generate the key bytes + byte[] sharedSecretA = keyAgreeA.generateSecret(); + byte[] sharedSecretB = keyAgreeB.generateSecret(); + System.out.println(methodName + " sharedSecretB = " + BaseUtils.bytesToHex(sharedSecretB)); + assertTrue(Arrays.equals(sharedSecretA, sharedSecretB)); + } catch (IllegalStateException ise) { + if (getProviderName().equals(("OpenJCEPlusFIPS"))) { + if (idString.equals("secp192r1")) { + assertTrue(ise.getMessage().equals("NIST P-192 curve is not supported in FIPS for calculating the shared secret")); + } else { + ise.printStackTrace(); + throw ise; + } + } else { + ise.printStackTrace(); + throw ise; + } + } } @Test diff --git a/src/test/java/ibm/jceplus/junit/openjceplusfips/TestECDHKeyAgreementParamValidation.java b/src/test/java/ibm/jceplus/junit/openjceplusfips/TestECDHKeyAgreementParamValidation.java index 8988f6625..97c8e597b 100644 --- a/src/test/java/ibm/jceplus/junit/openjceplusfips/TestECDHKeyAgreementParamValidation.java +++ b/src/test/java/ibm/jceplus/junit/openjceplusfips/TestECDHKeyAgreementParamValidation.java @@ -1,5 +1,5 @@ /* - * Copyright IBM Corp. 2024 + * Copyright IBM Corp. 2024, 2025 * * This code is free software; you can redistribute it and/or modify it * under the terms provided by IBM in the LICENSE file that accompanied @@ -8,9 +8,20 @@ package ibm.jceplus.junit.openjceplusfips; import ibm.jceplus.junit.base.BaseTestECDHKeyAgreementParamValidation; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.spec.ECGenParameterSpec; +import javax.crypto.KeyAgreement; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.api.TestInstance.Lifecycle; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assumptions.assumeTrue; @TestInstance(Lifecycle.PER_CLASS) public class TestECDHKeyAgreementParamValidation extends BaseTestECDHKeyAgreementParamValidation { @@ -21,4 +32,43 @@ public void beforeAll() throws Exception { setProviderName(Utils.TEST_SUITE_PROVIDER_NAME); } + @ParameterizedTest + @ValueSource(strings = {"secp192r1", "secp224r1"}) + public void testECDHKeyAgreementSharedSecretComputation(String curveName) { + assumeTrue( + Boolean.getBoolean("openjceplus.disableSmallerECKeySizeForSharedKeyComputing"), + "Property not true; skipping" + ); + + try { + KeyPair alice = genECKeyPair(curveName); + KeyPair bob = genECKeyPair(curveName); + + ecdhSharedSecretComputation(alice.getPrivate(), bob.getPublic()); + ecdhSharedSecretComputation(bob.getPrivate(), alice.getPublic()); + + fail("Curve " + curveName + " worked unexpectedly"); + } catch (Exception e) { + if (curveName.equals("secp192r1")) { + assertTrue(e.getMessage().equals("NIST P-192 curve is not supported in FIPS for calculating the shared secret")); + } else { + assertTrue(e.getMessage().equals("NIST P-224 curve is not supported in FIPS for calculating the shared secret")); + } + } + } + + private KeyPair genECKeyPair(String curveName) throws Exception { + KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", getProviderName()); + kpg.initialize(new ECGenParameterSpec(curveName)); + KeyPair kp = kpg.generateKeyPair(); + return kp; + } + + private byte[] ecdhSharedSecretComputation(PrivateKey priv, PublicKey peerPub) throws Exception { + KeyAgreement ka = KeyAgreement.getInstance("ECDH", getProviderName()); + ka.init(priv); + ka.doPhase(peerPub, true); + return ka.generateSecret(); + } + }