Skip to content

Commit 7883ff9

Browse files
authored
Restrict EC curves in computing shared secret (#936)
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 74941a6 commit 7883ff9

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
@@ -42,13 +42,13 @@
4242
public class BaseTestECDH extends BaseTestJunit5 {
4343

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

4747
static KeyPairGenerator kpgA = null;
4848
static KeyPairGenerator kpgB = null;
4949

50-
static KeyPair keyPairA_192k1, keyPairA_256r1;
51-
static KeyPair keyPairB_192k1, keyPairB_256r1;
50+
static KeyPair keyPairA_192r1, keyPairA_256r1;
51+
static KeyPair keyPairB_192r1, keyPairB_256r1;
5252

5353
private boolean isMulti = false;
5454

@@ -68,21 +68,21 @@ synchronized static void generateParams(String provider_name) {
6868
try {
6969

7070
//String provider_name = "OpenJCEPlus";
71-
String curveName_192k1 = "secp192k1";
71+
String curveName_192r1 = "secp192r1";
7272
String curveName_256r1 = "secp256r1";
7373

74-
algParameterSpec_192k1 = new ECGenParameterSpec(curveName_192k1);
74+
algParameterSpec_192r1 = new ECGenParameterSpec(curveName_192r1);
7575
algParameterSpec_256r1 = new ECGenParameterSpec(curveName_256r1);
7676

7777
kpgA = KeyPairGenerator.getInstance("EC", provider_name);
78-
kpgA.initialize(algParameterSpec_192k1);
79-
keyPairA_192k1 = kpgA.generateKeyPair();
78+
kpgA.initialize(algParameterSpec_192r1);
79+
keyPairA_192r1 = kpgA.generateKeyPair();
8080
kpgA.initialize(algParameterSpec_256r1);
8181
keyPairA_256r1 = kpgA.generateKeyPair();
8282

8383
kpgB = KeyPairGenerator.getInstance("EC", provider_name);
84-
kpgB.initialize(algParameterSpec_192k1);
85-
keyPairB_192k1 = kpgB.generateKeyPair();
84+
kpgB.initialize(algParameterSpec_192r1);
85+
keyPairB_192r1 = kpgB.generateKeyPair();
8686
kpgB.initialize(algParameterSpec_256r1);
8787
keyPairB_256r1 = kpgB.generateKeyPair();
8888

@@ -110,15 +110,15 @@ public void setUp() {
110110
* @throws Exception
111111
*/
112112
@Test
113-
public void testECDH_secp192k1() throws Exception {
113+
public void testECDH_secp192r1() throws Exception {
114114

115-
String curveName = "secp192k1";
115+
String curveName = "secp192r1";
116116

117117
ECGenParameterSpec ecgn = new ECGenParameterSpec(curveName);
118118

119119
compute_ecdh_key(curveName, ecgn);
120120
if (isMulti)
121-
compute_ecdh_key_with_global_key(curveName, algParameterSpec_192k1);
121+
compute_ecdh_key_with_global_key(curveName, algParameterSpec_192r1);
122122

123123
}
124124

@@ -140,27 +140,34 @@ public void testECDH_ECSpec() throws Exception {
140140

141141
String methodId = "ECDHECParamSpec";
142142

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

151-
BigInteger p = new BigInteger(sfield_p256, 16);
151+
BigInteger p = new BigInteger(sfield_p256r1, 16);
152152
ECField field = new ECFieldFp(p);
153153

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

160+
ECPoint g = new ECPoint(
161+
new BigInteger(sx_p256r1, 16),
162+
new BigInteger(sy_p256r1, 16)
163+
);
164+
165+
BigInteger order = new BigInteger(sorder_p256r1, 16);
159166
int cofactor = 1;
167+
160168
ECParameterSpec ecParamSpec = new ECParameterSpec(curve, g, order, cofactor);
161169

162170
compute_ecdh_key(methodId, ecParamSpec);
163-
164171
}
165172

166173
void compute_ecdh_key(String idString, AlgorithmParameterSpec algParameterSpec)
@@ -269,14 +276,24 @@ void compute_ecdh_key(String idString, AlgorithmParameterSpec algParameterSpec)
269276
throw e;
270277
}
271278

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

282299
/*
@@ -314,9 +331,9 @@ void compute_ecdh_key_with_global_key(String idString, AlgorithmParameterSpec al
314331
final String methodName = "compute_ecdh_key_with_global_key" + "_" + idString;
315332
KeyPair keyPairA = null, keyPairB = null;
316333
switch (idString) {
317-
case "secp192k1":
318-
keyPairA = keyPairA_192k1;
319-
keyPairB = keyPairB_192k1;
334+
case "secp192r1":
335+
keyPairA = keyPairA_192r1;
336+
keyPairB = keyPairB_192r1;
320337
break;
321338

322339
case "secp256r1":
@@ -380,11 +397,25 @@ void compute_ecdh_key_with_global_key(String idString, AlgorithmParameterSpec al
380397
throw e;
381398
}
382399

383-
// Generate the key bytes
384-
byte[] sharedSecretA = keyAgreeA.generateSecret();
385-
byte[] sharedSecretB = keyAgreeB.generateSecret();
386-
System.out.println(methodName + " sharedSecretB = " + BaseUtils.bytesToHex(sharedSecretB));
387-
assertTrue(Arrays.equals(sharedSecretA, sharedSecretB));
400+
try {
401+
// Generate the key bytes
402+
byte[] sharedSecretA = keyAgreeA.generateSecret();
403+
byte[] sharedSecretB = keyAgreeB.generateSecret();
404+
System.out.println(methodName + " sharedSecretB = " + BaseUtils.bytesToHex(sharedSecretB));
405+
assertTrue(Arrays.equals(sharedSecretA, sharedSecretB));
406+
} catch (IllegalStateException ise) {
407+
if (getProviderName().equals(("OpenJCEPlusFIPS"))) {
408+
if (idString.equals("secp192r1")) {
409+
assertTrue(ise.getMessage().equals("NIST P-192 curve is not supported in FIPS for calculating the shared secret"));
410+
} else {
411+
ise.printStackTrace();
412+
throw ise;
413+
}
414+
} else {
415+
ise.printStackTrace();
416+
throw ise;
417+
}
418+
}
388419
}
389420

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