Skip to content

Commit d420201

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 086c490 commit d420201

File tree

2 files changed

+76
-1
lines changed

2 files changed

+76
-1
lines changed

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

Lines changed: 29 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,33 @@ 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+
ObjectIdentifier oidPubKey = ECNamedCurve.getOIDFromName(ecPubKeyNamedCurve.getName());
173+
ObjectIdentifier oidPriKey = ECNamedCurve.getOIDFromName(ecPriKeyNamedCurve.getName());
174+
175+
// check if curve is FIPS allowance and if it is allowed for ECDH shared secret computation.
176+
if (!ECNamedCurve.isFIPS(oidPubKey.toString())) {
177+
throw new IllegalStateException(ecPubKeyNamedCurve.getName() + " curve is not supported in FIPS for public key");
178+
}
179+
if (!ECNamedCurve.isFIPS(oidPriKey.toString())) {
180+
throw new IllegalStateException(ecPriKeyNamedCurve.getName() + " curve is not supported in FIPS for private key");
181+
}
182+
183+
if (!((oidPubKey.toString()).equals("1.2.840.10045.3.1.7") // secp256r1 [NIST P-256, X9.62 prime256v1]
184+
|| (oidPubKey.toString()).equals("1.3.132.0.33") // secp224r1 [NIST P-224]
185+
|| (oidPubKey.toString()).equals("1.3.132.0.34") // secp384r1 [NIST P-384]
186+
|| (oidPubKey.toString()).equals("1.3.132.0.35")) ||
187+
!((oidPriKey.toString()).equals("1.2.840.10045.3.1.7") // secp256r1 [NIST P-256, X9.62 prime256v1]
188+
|| (oidPriKey.toString()).equals("1.3.132.0.33") // secp224r1 [NIST P-224]
189+
|| (oidPriKey.toString()).equals("1.3.132.0.34") // secp384r1 [NIST P-384]
190+
|| (oidPriKey.toString()).equals("1.3.132.0.35")) // secp521r1 [NIST P-521]
191+
) {
192+
throw new IllegalStateException(ecPubKeyNamedCurve.getName() + " curve is not supported in FIPS for calculating the shared secret");
193+
}
194+
}
195+
167196
// Reset the key agreement here (in case anything goes wrong)
168197
generateSecret = false;
169198
byte[] secret = null;

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

Lines changed: 47 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,39 @@ public void beforeAll() throws Exception {
2132
setProviderName(Utils.TEST_SUITE_PROVIDER_NAME);
2233
}
2334

35+
@ParameterizedTest
36+
@ValueSource(strings = {"secp112r1", "secp112r2", "secp128r1", "secp128r2", "secp160r1", "secp160r2", "secp160k1", "secp192k1", "secp192r1"})
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+
assertTrue(e.getMessage().equals(curveName + " curve is not supported in FIPS for calculating the shared secret"));
53+
}
54+
}
55+
56+
private KeyPair genECKeyPair(String curveName) throws Exception {
57+
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", getProviderName());
58+
kpg.initialize(new ECGenParameterSpec(curveName));
59+
KeyPair kp = kpg.generateKeyPair();
60+
return kp;
61+
}
62+
63+
private byte[] ecdhSharedSecretComputation(PrivateKey priv, PublicKey peerPub) throws Exception {
64+
KeyAgreement ka = KeyAgreement.getInstance("ECDH", getProviderName());
65+
ka.init(priv);
66+
ka.doPhase(peerPub, true);
67+
return ka.generateSecret();
68+
}
69+
2470
}

0 commit comments

Comments
 (0)