Skip to content

Commit a89b76c

Browse files
authored
Restrict EC curves in computing shared secret (#826)
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 0809300 commit a89b76c

File tree

3 files changed

+148
-42
lines changed

3 files changed

+148
-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: 72 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,24 @@ 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+
} else {
291+
ise.printStackTrace();
292+
throw ise;
293+
}
294+
} else {
295+
ise.printStackTrace();
296+
throw ise;
297+
}
298+
}
282299
}
283300

284301
/*
@@ -333,9 +350,9 @@ void compute_ecdh_key_with_global_key(String idString, AlgorithmParameterSpec al
333350
final String methodName = "compute_ecdh_key_with_global_key" + "_" + idString;
334351
KeyPair keyPairA = null, keyPairB = null;
335352
switch (idString) {
336-
case "secp192k1":
337-
keyPairA = keyPairA_192k1;
338-
keyPairB = keyPairB_192k1;
353+
case "secp192r1":
354+
keyPairA = keyPairA_192r1;
355+
keyPairB = keyPairB_192r1;
339356
break;
340357

341358
case "secp256r1":
@@ -399,11 +416,25 @@ void compute_ecdh_key_with_global_key(String idString, AlgorithmParameterSpec al
399416
throw e;
400417
}
401418

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));
419+
try {
420+
// Generate the key bytes
421+
byte[] sharedSecretA = keyAgreeA.generateSecret();
422+
byte[] sharedSecretB = keyAgreeB.generateSecret();
423+
System.out.println(methodName + " sharedSecretB = " + BaseUtils.bytesToHex(sharedSecretB));
424+
assertTrue(Arrays.equals(sharedSecretA, sharedSecretB));
425+
} catch (IllegalStateException ise) {
426+
if (getProviderName().equals(("OpenJCEPlusFIPS"))) {
427+
if (idString.equals("secp192r1")) {
428+
assertTrue(ise.getMessage().equals("NIST P-192 curve is not supported in FIPS for calculating the shared secret"));
429+
} else {
430+
ise.printStackTrace();
431+
throw ise;
432+
}
433+
} else {
434+
ise.printStackTrace();
435+
throw ise;
436+
}
437+
}
407438
}
408439

409440
@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)