Skip to content

Commit 43d2d68

Browse files
author
John Jiang
committedJan 16, 2024
8320449: ECDHKeyAgreement should validate parameters before using them
Reviewed-by: mullan
1 parent b058063 commit 43d2d68

File tree

2 files changed

+137
-17
lines changed

2 files changed

+137
-17
lines changed
 

‎src/java.base/share/classes/sun/security/ec/ECDHKeyAgreement.java

+17-17
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2009, 2022, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2009, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -25,13 +25,11 @@
2525

2626
package sun.security.ec;
2727

28-
import sun.security.ec.point.AffinePoint;
2928
import sun.security.ec.point.Point;
3029
import sun.security.util.ArrayUtil;
3130
import sun.security.util.CurveDB;
3231
import sun.security.util.ECUtil;
3332
import sun.security.util.NamedCurve;
34-
import sun.security.util.math.ImmutableIntegerModuloP;
3533
import sun.security.util.math.IntegerFieldModuloP;
3634
import sun.security.util.math.MutableIntegerModuloP;
3735
import sun.security.util.math.SmallValue;
@@ -63,7 +61,7 @@ public final class ECDHKeyAgreement extends KeyAgreementSpi {
6361

6462
// private key, if initialized
6563
private ECPrivateKey privateKey;
66-
ECOperations privateKeyOps;
64+
private ECOperations privateKeyOps;
6765

6866
// public key, non-null between doPhase() & generateSecret() only
6967
private ECPublicKey publicKey;
@@ -80,20 +78,26 @@ public ECDHKeyAgreement() {
8078
// Generic init
8179
private void init(Key key) throws
8280
InvalidKeyException, InvalidAlgorithmParameterException {
81+
privateKey = null;
82+
privateKeyOps = null;
83+
publicKey = null;
84+
8385
if (!(key instanceof PrivateKey)) {
8486
throw new InvalidKeyException("Key must be instance of PrivateKey");
8587
}
86-
privateKey = (ECPrivateKey)ECKeyFactory.toECKey(key);
87-
publicKey = null;
88+
89+
ECPrivateKey ecPrivateKey = (ECPrivateKey)ECKeyFactory.toECKey(key);
8890
Optional<ECOperations> opsOpt =
89-
ECOperations.forParameters(privateKey.getParams());
91+
ECOperations.forParameters(ecPrivateKey.getParams());
9092
if (opsOpt.isEmpty()) {
91-
NamedCurve nc = CurveDB.lookup(privateKey.getParams());
93+
NamedCurve nc = CurveDB.lookup(ecPrivateKey.getParams());
9294
throw new InvalidAlgorithmParameterException(
9395
"Curve not supported: " + (nc != null ? nc.toString() :
9496
"unknown"));
9597
}
96-
ECUtil.checkPrivateKey(privateKey);
98+
ECUtil.checkPrivateKey(ecPrivateKey);
99+
100+
privateKey = ecPrivateKey;
97101
privateKeyOps = opsOpt.get();
98102
}
99103

@@ -139,26 +143,22 @@ protected Key engineDoPhase(Key key, boolean lastPhase)
139143
("Key must be a PublicKey with algorithm EC");
140144
}
141145

146+
// Validate public key
147+
validate(privateKeyOps, (ECPublicKey) key);
148+
142149
this.publicKey = (ECPublicKey) key;
143150

144151
int keyLenBits =
145152
publicKey.getParams().getCurve().getField().getFieldSize();
146153
secretLen = (keyLenBits + 7) >> 3;
147154

148-
// Validate public key
149-
validate(privateKeyOps, publicKey);
150-
151155
return null;
152156
}
153157

154158
// Verify that x and y are integers in the interval [0, p - 1].
155159
private static void validateCoordinate(BigInteger c, BigInteger mod)
156160
throws InvalidKeyException{
157-
if (c.compareTo(BigInteger.ZERO) < 0) {
158-
throw new InvalidKeyException("Invalid coordinate");
159-
}
160-
161-
if (c.compareTo(mod) >= 0) {
161+
if (c.compareTo(BigInteger.ZERO) < 0 || c.compareTo(mod) >= 0) {
162162
throw new InvalidKeyException("Invalid coordinate");
163163
}
164164
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/*
2+
* Copyright (C) 2024, THL A29 Limited, a Tencent company. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/*
25+
* @test
26+
* @bug 8320449
27+
* @summary ECDHKeyAgreement should validate parameters before assigning them to fields.
28+
* @library /test/lib
29+
* @run main ECDHKeyAgreementParamValidation
30+
*/
31+
32+
import javax.crypto.KeyAgreement;
33+
import java.math.BigInteger;
34+
import java.security.InvalidKeyException;
35+
import java.security.KeyFactory;
36+
import java.security.KeyPair;
37+
import java.security.KeyPairGenerator;
38+
import java.security.interfaces.ECPrivateKey;
39+
import java.security.spec.ECPrivateKeySpec;
40+
41+
import jdk.test.lib.Asserts;
42+
43+
public class ECDHKeyAgreementParamValidation {
44+
45+
private static void testInitWithInvalidKey() throws Exception {
46+
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC");
47+
kpg.initialize(256);
48+
KeyPair kp = kpg.generateKeyPair();
49+
ECPrivateKey privateKey = (ECPrivateKey) kp.getPrivate();
50+
51+
KeyFactory keyFactory = KeyFactory.getInstance("EC");
52+
ECPrivateKey invalidPrivateKey
53+
= (ECPrivateKey) keyFactory.generatePrivate(
54+
new ECPrivateKeySpec(BigInteger.ZERO,
55+
privateKey.getParams()));
56+
57+
KeyAgreement ka = KeyAgreement.getInstance("ECDH");
58+
59+
// The first initiation should succeed.
60+
ka.init(privateKey);
61+
62+
// The second initiation should fail with invalid private key,
63+
// and the private key assigned by the first initiation should be cleared.
64+
Asserts.assertThrows(
65+
InvalidKeyException.class,
66+
() -> ka.init(invalidPrivateKey));
67+
68+
// Cannot doPhase due to no private key.
69+
Asserts.assertThrows(
70+
IllegalStateException.class,
71+
() -> ka.doPhase(kp.getPublic(), true));
72+
73+
// Cannot generate shared key due to no key
74+
Asserts.assertThrows(IllegalStateException.class, ka::generateSecret);
75+
}
76+
77+
private static void testDoPhaseWithInvalidKey() throws Exception {
78+
// SECP256R1 key pair
79+
KeyPairGenerator kpgP256 = KeyPairGenerator.getInstance("EC");
80+
kpgP256.initialize(256);
81+
KeyPair kpP256 = kpgP256.generateKeyPair();
82+
83+
// SECP384R1 key pair
84+
KeyPairGenerator kpgP384 = KeyPairGenerator.getInstance("EC");
85+
kpgP384.initialize(384);
86+
KeyPair kpP384 = kpgP384.generateKeyPair();
87+
88+
KeyAgreement ka = KeyAgreement.getInstance("ECDH");
89+
ka.init(kpP256.getPrivate());
90+
91+
Asserts.assertThrows(
92+
InvalidKeyException.class,
93+
() -> ka.doPhase(kpP384.getPublic(), true));
94+
95+
// Should not generate shared key with SECP256R1 private key and SECP384R1 public key
96+
Asserts.assertThrows(IllegalStateException.class, ka::generateSecret);
97+
}
98+
99+
public static void main(String[] args) {
100+
boolean failed = false;
101+
102+
try {
103+
testInitWithInvalidKey();
104+
} catch (Exception e) {
105+
failed = true;
106+
e.printStackTrace();
107+
}
108+
109+
try {
110+
testDoPhaseWithInvalidKey();
111+
} catch (Exception e) {
112+
failed = true;
113+
e.printStackTrace();
114+
}
115+
116+
if (failed) {
117+
throw new RuntimeException("Test failed");
118+
}
119+
}
120+
}

1 commit comments

Comments
 (1)

openjdk-notifier[bot] commented on Jan 16, 2024

@openjdk-notifier[bot]
Please sign in to comment.