Skip to content

Commit a9e6df6

Browse files
committed
Restrict EC curves in computing shared secret
According to the policy, curve size less than 192 should not be allowed in computing shared secret in ECDH keyagreement in FIPS 140-3 mode. This PR proposes an option to enable/disable this behaviour. Signed-off-by: JinhangZhang <[email protected]>
1 parent 6e34a1e commit a9e6df6

File tree

3 files changed

+136
-42
lines changed

3 files changed

+136
-42
lines changed

src/main/java/com/ibm/crypto/plus/provider/ECDHKeyAgreement.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import javax.crypto.SecretKey;
2727
import javax.crypto.ShortBufferException;
2828
import javax.crypto.spec.SecretKeySpec;
29+
import sun.security.util.ObjectIdentifier;
2930

3031
public final class ECDHKeyAgreement extends KeyAgreementSpi { // implements
3132
// AlgorithmStatus
@@ -44,6 +45,7 @@ public final class ECDHKeyAgreement extends KeyAgreementSpi { // implements
4445
private ECPublicKey ecPublicKey = null;
4546
private ECPrivateKey ecPrivateKey = null;
4647
private int secretLen;
48+
private static boolean disableSmallCurve = Boolean.parseBoolean(System.getProperty("openjceplus.disableSmallerECKeySizeForSharedKeyComputing", "true"));
4749

4850
public ECDHKeyAgreement(OpenJCEPlusProvider provider) {
4951
// System.out.println ("In ECDHKeyAgreement");
@@ -164,6 +166,29 @@ protected byte[] engineGenerateSecret() throws IllegalStateException {
164166
throw new IllegalStateException("Wrong state");
165167
}
166168

169+
if (provider.isFIPS() && disableSmallCurve) {
170+
ECNamedCurve ecPubKeyNamedCurve = ECParameters.getNamedCurve(this.ecPublicKey.getParams());
171+
ECNamedCurve ecPriKeyNamedCurve = ECParameters.getNamedCurve(this.ecPrivateKey.getParams());
172+
if (ecPubKeyNamedCurve == null || ecPriKeyNamedCurve == null) {
173+
throw new IllegalStateException("Curve name is not recognized or not supported");
174+
}
175+
ObjectIdentifier oidPubKey = ECNamedCurve.getOIDFromName(ecPubKeyNamedCurve.getName());
176+
ObjectIdentifier oidPriKey = ECNamedCurve.getOIDFromName(ecPriKeyNamedCurve.getName());
177+
178+
// check if curve is FIPS allowance and if it is allowed for ECDH shared secret computation
179+
if (!((oidPubKey.toString()).equals("1.2.840.10045.3.1.7") // secp256r1 [NIST P-256, X9.62 prime256v1]
180+
|| (oidPubKey.toString()).equals("1.3.132.0.33") // secp224r1 [NIST P-224]
181+
|| (oidPubKey.toString()).equals("1.3.132.0.34") // secp384r1 [NIST P-384]
182+
|| (oidPubKey.toString()).equals("1.3.132.0.35")) ||
183+
!((oidPriKey.toString()).equals("1.2.840.10045.3.1.7") // secp256r1 [NIST P-256, X9.62 prime256v1]
184+
|| (oidPriKey.toString()).equals("1.3.132.0.33") // secp224r1 [NIST P-224]
185+
|| (oidPriKey.toString()).equals("1.3.132.0.34") // secp384r1 [NIST P-384]
186+
|| (oidPriKey.toString()).equals("1.3.132.0.35")) // secp521r1 [NIST P-521]
187+
) {
188+
throw new IllegalStateException(ecPubKeyNamedCurve.getName() + " curve is not supported in FIPS for calculating the shared secret");
189+
}
190+
}
191+
167192
// Reset the key agreement here (in case anything goes wrong)
168193
generateSecret = false;
169194
byte[] secret = null;

src/test/java/ibm/jceplus/junit/base/BaseTestECDH.java

Lines changed: 60 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,13 @@
4444
public class BaseTestECDH extends BaseTestJunit5 {
4545

4646
static final byte[] origMsg = "this is the original message to be signed".getBytes();
47-
static ECGenParameterSpec algParameterSpec_192k1, algParameterSpec_256r1;
47+
static ECGenParameterSpec algParameterSpec_192r1, algParameterSpec_256r1;
4848

4949
static KeyPairGenerator kpgA = null;
5050
static KeyPairGenerator kpgB = null;
5151

52-
static KeyPair keyPairA_192k1, keyPairA_256r1;
53-
static KeyPair keyPairB_192k1, keyPairB_256r1;
52+
static KeyPair keyPairA_192r1, keyPairA_256r1;
53+
static KeyPair keyPairB_192r1, keyPairB_256r1;
5454

5555
private boolean isMulti = false;
5656

@@ -70,21 +70,21 @@ synchronized static void generateParams(String provider_name) {
7070
try {
7171

7272
//String provider_name = "OpenJCEPlus";
73-
String curveName_192k1 = "secp192k1";
73+
String curveName_192r1 = "secp192r1";
7474
String curveName_256r1 = "secp256r1";
7575

76-
algParameterSpec_192k1 = new ECGenParameterSpec(curveName_192k1);
76+
algParameterSpec_192r1 = new ECGenParameterSpec(curveName_192r1);
7777
algParameterSpec_256r1 = new ECGenParameterSpec(curveName_256r1);
7878

7979
kpgA = KeyPairGenerator.getInstance("EC", provider_name);
80-
kpgA.initialize(algParameterSpec_192k1);
81-
keyPairA_192k1 = kpgA.generateKeyPair();
80+
kpgA.initialize(algParameterSpec_192r1);
81+
keyPairA_192r1 = kpgA.generateKeyPair();
8282
kpgA.initialize(algParameterSpec_256r1);
8383
keyPairA_256r1 = kpgA.generateKeyPair();
8484

8585
kpgB = KeyPairGenerator.getInstance("EC", provider_name);
86-
kpgB.initialize(algParameterSpec_192k1);
87-
keyPairB_192k1 = kpgB.generateKeyPair();
86+
kpgB.initialize(algParameterSpec_192r1);
87+
keyPairB_192r1 = kpgB.generateKeyPair();
8888
kpgB.initialize(algParameterSpec_256r1);
8989
keyPairB_256r1 = kpgB.generateKeyPair();
9090

@@ -112,15 +112,15 @@ public void setUp() {
112112
* @throws Exception
113113
*/
114114
@Test
115-
public void testECDH_secp192k1() throws Exception {
115+
public void testECDH_secp192r1() throws Exception {
116116

117-
String curveName = "secp192k1";
117+
String curveName = "secp192r1";
118118

119119
ECGenParameterSpec ecgn = new ECGenParameterSpec(curveName);
120120

121121
compute_ecdh_key(curveName, ecgn);
122122
if (isMulti)
123-
compute_ecdh_key_with_global_key(curveName, algParameterSpec_192k1);
123+
compute_ecdh_key_with_global_key(curveName, algParameterSpec_192r1);
124124

125125
}
126126

@@ -142,27 +142,34 @@ public void testECDH_ECSpec() throws Exception {
142142

143143
String methodId = "ECDHECParamSpec";
144144

145-
// These values were copied from ECNamedCurve.java in IBMJCEFIPS
146-
String sfield_p256 = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F";
147-
String sa_p256 = "0000000000000000000000000000000000000000000000000000000000000000";
148-
String sb_p256 = "0000000000000000000000000000000000000000000000000000000000000007";
149-
String sx_p256 = "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798";
150-
String sy_p256 = "483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8";
151-
String sorder_p256 = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141";
145+
// secp256r1 / prime256v1 (NIST P-256)
146+
String sfield_p256r1 = "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF";
147+
String sa_p256r1 = "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC";
148+
String sb_p256r1 = "5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B";
149+
String sx_p256r1 = "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296";
150+
String sy_p256r1 = "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5";
151+
String sorder_p256r1 = "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551";
152152

153-
BigInteger p = new BigInteger(sfield_p256, 16);
153+
BigInteger p = new BigInteger(sfield_p256r1, 16);
154154
ECField field = new ECFieldFp(p);
155155

156-
EllipticCurve curve = new EllipticCurve(field, new BigInteger(sa_p256, 16),
157-
new BigInteger(sb_p256, 16));
158-
ECPoint g = new ECPoint(new BigInteger(sx_p256, 16), new BigInteger(sy_p256, 16));
159-
BigInteger order = new BigInteger(sorder_p256, 16);
156+
EllipticCurve curve = new EllipticCurve(
157+
field,
158+
new BigInteger(sa_p256r1, 16),
159+
new BigInteger(sb_p256r1, 16)
160+
);
160161

162+
ECPoint g = new ECPoint(
163+
new BigInteger(sx_p256r1, 16),
164+
new BigInteger(sy_p256r1, 16)
165+
);
166+
167+
BigInteger order = new BigInteger(sorder_p256r1, 16);
161168
int cofactor = 1;
169+
162170
ECParameterSpec ecParamSpec = new ECParameterSpec(curve, g, order, cofactor);
163171

164172
compute_ecdh_key(methodId, ecParamSpec);
165-
166173
}
167174

168175
void compute_ecdh_key(String idString, AlgorithmParameterSpec algParameterSpec)
@@ -271,14 +278,18 @@ void compute_ecdh_key(String idString, AlgorithmParameterSpec algParameterSpec)
271278
throw e;
272279
}
273280

274-
// Generate the key bytes
275-
byte[] sharedSecretA = keyAgreeA.generateSecret();
276-
byte[] sharedSecretB = keyAgreeB.generateSecret();
277-
// System.out.println(methodName + " sharedSecretA = " + BaseUtils.bytesToHex(sharedSecretA));
278-
// System.out.println(methodName + " sharedSecretB = " + BaseUtils.bytesToHex(sharedSecretB));
279-
280-
assertTrue(Arrays.equals(sharedSecretA, sharedSecretB));
281-
281+
try {
282+
// Generate the key bytes
283+
byte[] sharedSecretA = keyAgreeA.generateSecret();
284+
byte[] sharedSecretB = keyAgreeB.generateSecret();
285+
assertTrue(Arrays.equals(sharedSecretA, sharedSecretB));
286+
} catch (IllegalStateException ise) {
287+
if (getProviderName().equals(("OpenJCEPlusFIPS"))) {
288+
if (idString.equals("secp192r1")) {
289+
assertTrue(ise.getMessage().equals("NIST P-192 curve is not supported in FIPS for calculating the shared secret"));
290+
}
291+
}
292+
}
282293
}
283294

284295
/*
@@ -333,9 +344,9 @@ void compute_ecdh_key_with_global_key(String idString, AlgorithmParameterSpec al
333344
final String methodName = "compute_ecdh_key_with_global_key" + "_" + idString;
334345
KeyPair keyPairA = null, keyPairB = null;
335346
switch (idString) {
336-
case "secp192k1":
337-
keyPairA = keyPairA_192k1;
338-
keyPairB = keyPairB_192k1;
347+
case "secp192r1":
348+
keyPairA = keyPairA_192r1;
349+
keyPairB = keyPairB_192r1;
339350
break;
340351

341352
case "secp256r1":
@@ -399,11 +410,19 @@ void compute_ecdh_key_with_global_key(String idString, AlgorithmParameterSpec al
399410
throw e;
400411
}
401412

402-
// Generate the key bytes
403-
byte[] sharedSecretA = keyAgreeA.generateSecret();
404-
byte[] sharedSecretB = keyAgreeB.generateSecret();
405-
System.out.println(methodName + " sharedSecretB = " + BaseUtils.bytesToHex(sharedSecretB));
406-
assertTrue(Arrays.equals(sharedSecretA, sharedSecretB));
413+
try {
414+
// Generate the key bytes
415+
byte[] sharedSecretA = keyAgreeA.generateSecret();
416+
byte[] sharedSecretB = keyAgreeB.generateSecret();
417+
System.out.println(methodName + " sharedSecretB = " + BaseUtils.bytesToHex(sharedSecretB));
418+
assertTrue(Arrays.equals(sharedSecretA, sharedSecretB));
419+
} catch (IllegalStateException ise) {
420+
if (getProviderName().equals(("OpenJCEPlusFIPS"))) {
421+
if (idString.equals("secp192r1")) {
422+
assertTrue(ise.getMessage().equals("NIST P-192 curve is not supported in FIPS for calculating the shared secret"));
423+
}
424+
}
425+
}
407426
}
408427

409428
@Test

src/test/java/ibm/jceplus/junit/openjceplusfips/TestECDHKeyAgreementParamValidation.java

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright IBM Corp. 2024
2+
* Copyright IBM Corp. 2024, 2025
33
*
44
* This code is free software; you can redistribute it and/or modify it
55
* under the terms provided by IBM in the LICENSE file that accompanied
@@ -8,9 +8,20 @@
88
package ibm.jceplus.junit.openjceplusfips;
99

1010
import ibm.jceplus.junit.base.BaseTestECDHKeyAgreementParamValidation;
11+
import java.security.KeyPair;
12+
import java.security.KeyPairGenerator;
13+
import java.security.PrivateKey;
14+
import java.security.PublicKey;
15+
import java.security.spec.ECGenParameterSpec;
16+
import javax.crypto.KeyAgreement;
1117
import org.junit.jupiter.api.BeforeAll;
1218
import org.junit.jupiter.api.TestInstance;
1319
import org.junit.jupiter.api.TestInstance.Lifecycle;
20+
import org.junit.jupiter.params.ParameterizedTest;
21+
import org.junit.jupiter.params.provider.ValueSource;
22+
import static org.junit.jupiter.api.Assertions.assertTrue;
23+
import static org.junit.jupiter.api.Assertions.fail;
24+
import static org.junit.jupiter.api.Assumptions.assumeTrue;
1425

1526
@TestInstance(Lifecycle.PER_CLASS)
1627
public class TestECDHKeyAgreementParamValidation extends BaseTestECDHKeyAgreementParamValidation {
@@ -21,4 +32,43 @@ public void beforeAll() throws Exception {
2132
setProviderName(Utils.TEST_SUITE_PROVIDER_NAME);
2233
}
2334

35+
@ParameterizedTest
36+
@ValueSource(strings = {"secp192r1", "secp224r1"})
37+
public void testECDHKeyAgreementSharedSecretComputation(String curveName) {
38+
assumeTrue(
39+
Boolean.getBoolean("openjceplus.disableSmallerECKeySizeForSharedKeyComputing"),
40+
"Property not true; skipping"
41+
);
42+
43+
try {
44+
KeyPair alice = genECKeyPair(curveName);
45+
KeyPair bob = genECKeyPair(curveName);
46+
47+
ecdhSharedSecretComputation(alice.getPrivate(), bob.getPublic());
48+
ecdhSharedSecretComputation(bob.getPrivate(), alice.getPublic());
49+
50+
fail("Curve " + curveName + " worked unexpectedly");
51+
} catch (Exception e) {
52+
if (curveName.equals("secp192r1")) {
53+
assertTrue(e.getMessage().equals("NIST P-192 curve is not supported in FIPS for calculating the shared secret"));
54+
} else {
55+
assertTrue(e.getMessage().equals("NIST P-224 curve is not supported in FIPS for calculating the shared secret"));
56+
}
57+
}
58+
}
59+
60+
private KeyPair genECKeyPair(String curveName) throws Exception {
61+
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", getProviderName());
62+
kpg.initialize(new ECGenParameterSpec(curveName));
63+
KeyPair kp = kpg.generateKeyPair();
64+
return kp;
65+
}
66+
67+
private byte[] ecdhSharedSecretComputation(PrivateKey priv, PublicKey peerPub) throws Exception {
68+
KeyAgreement ka = KeyAgreement.getInstance("ECDH", getProviderName());
69+
ka.init(priv);
70+
ka.doPhase(peerPub, true);
71+
return ka.generateSecret();
72+
}
73+
2474
}

0 commit comments

Comments
 (0)