Skip to content

Commit

Permalink
8320449: ECDHKeyAgreement should validate parameters before using them
Browse files Browse the repository at this point in the history
Reviewed-by: mullan
  • Loading branch information
John Jiang committed Jan 16, 2024
1 parent b058063 commit 43d2d68
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 17 deletions.
34 changes: 17 additions & 17 deletions src/java.base/share/classes/sun/security/ec/ECDHKeyAgreement.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2009, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand All @@ -25,13 +25,11 @@

package sun.security.ec;

import sun.security.ec.point.AffinePoint;
import sun.security.ec.point.Point;
import sun.security.util.ArrayUtil;
import sun.security.util.CurveDB;
import sun.security.util.ECUtil;
import sun.security.util.NamedCurve;
import sun.security.util.math.ImmutableIntegerModuloP;
import sun.security.util.math.IntegerFieldModuloP;
import sun.security.util.math.MutableIntegerModuloP;
import sun.security.util.math.SmallValue;
Expand Down Expand Up @@ -63,7 +61,7 @@ public final class ECDHKeyAgreement extends KeyAgreementSpi {

// private key, if initialized
private ECPrivateKey privateKey;
ECOperations privateKeyOps;
private ECOperations privateKeyOps;

// public key, non-null between doPhase() & generateSecret() only
private ECPublicKey publicKey;
Expand All @@ -80,20 +78,26 @@ public ECDHKeyAgreement() {
// Generic init
private void init(Key key) throws
InvalidKeyException, InvalidAlgorithmParameterException {
privateKey = null;
privateKeyOps = null;
publicKey = null;

if (!(key instanceof PrivateKey)) {
throw new InvalidKeyException("Key must be instance of PrivateKey");
}
privateKey = (ECPrivateKey)ECKeyFactory.toECKey(key);
publicKey = null;

ECPrivateKey ecPrivateKey = (ECPrivateKey)ECKeyFactory.toECKey(key);
Optional<ECOperations> opsOpt =
ECOperations.forParameters(privateKey.getParams());
ECOperations.forParameters(ecPrivateKey.getParams());
if (opsOpt.isEmpty()) {
NamedCurve nc = CurveDB.lookup(privateKey.getParams());
NamedCurve nc = CurveDB.lookup(ecPrivateKey.getParams());
throw new InvalidAlgorithmParameterException(
"Curve not supported: " + (nc != null ? nc.toString() :
"unknown"));
}
ECUtil.checkPrivateKey(privateKey);
ECUtil.checkPrivateKey(ecPrivateKey);

privateKey = ecPrivateKey;
privateKeyOps = opsOpt.get();
}

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

// Validate public key
validate(privateKeyOps, (ECPublicKey) key);

this.publicKey = (ECPublicKey) key;

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

// Validate public key
validate(privateKeyOps, publicKey);

return null;
}

// Verify that x and y are integers in the interval [0, p - 1].
private static void validateCoordinate(BigInteger c, BigInteger mod)
throws InvalidKeyException{
if (c.compareTo(BigInteger.ZERO) < 0) {
throw new InvalidKeyException("Invalid coordinate");
}

if (c.compareTo(mod) >= 0) {
if (c.compareTo(BigInteger.ZERO) < 0 || c.compareTo(mod) >= 0) {
throw new InvalidKeyException("Invalid coordinate");
}
}
Expand Down
120 changes: 120 additions & 0 deletions test/jdk/sun/security/ec/ECDHKeyAgreementParamValidation.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/*
* Copyright (C) 2024, THL A29 Limited, a Tencent company. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

/*
* @test
* @bug 8320449
* @summary ECDHKeyAgreement should validate parameters before assigning them to fields.
* @library /test/lib
* @run main ECDHKeyAgreementParamValidation
*/

import javax.crypto.KeyAgreement;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.ECPrivateKey;
import java.security.spec.ECPrivateKeySpec;

import jdk.test.lib.Asserts;

public class ECDHKeyAgreementParamValidation {

private static void testInitWithInvalidKey() throws Exception {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC");
kpg.initialize(256);
KeyPair kp = kpg.generateKeyPair();
ECPrivateKey privateKey = (ECPrivateKey) kp.getPrivate();

KeyFactory keyFactory = KeyFactory.getInstance("EC");
ECPrivateKey invalidPrivateKey
= (ECPrivateKey) keyFactory.generatePrivate(
new ECPrivateKeySpec(BigInteger.ZERO,
privateKey.getParams()));

KeyAgreement ka = KeyAgreement.getInstance("ECDH");

// The first initiation should succeed.
ka.init(privateKey);

// The second initiation should fail with invalid private key,
// and the private key assigned by the first initiation should be cleared.
Asserts.assertThrows(
InvalidKeyException.class,
() -> ka.init(invalidPrivateKey));

// Cannot doPhase due to no private key.
Asserts.assertThrows(
IllegalStateException.class,
() -> ka.doPhase(kp.getPublic(), true));

// Cannot generate shared key due to no key
Asserts.assertThrows(IllegalStateException.class, ka::generateSecret);
}

private static void testDoPhaseWithInvalidKey() throws Exception {
// SECP256R1 key pair
KeyPairGenerator kpgP256 = KeyPairGenerator.getInstance("EC");
kpgP256.initialize(256);
KeyPair kpP256 = kpgP256.generateKeyPair();

// SECP384R1 key pair
KeyPairGenerator kpgP384 = KeyPairGenerator.getInstance("EC");
kpgP384.initialize(384);
KeyPair kpP384 = kpgP384.generateKeyPair();

KeyAgreement ka = KeyAgreement.getInstance("ECDH");
ka.init(kpP256.getPrivate());

Asserts.assertThrows(
InvalidKeyException.class,
() -> ka.doPhase(kpP384.getPublic(), true));

// Should not generate shared key with SECP256R1 private key and SECP384R1 public key
Asserts.assertThrows(IllegalStateException.class, ka::generateSecret);
}

public static void main(String[] args) {
boolean failed = false;

try {
testInitWithInvalidKey();
} catch (Exception e) {
failed = true;
e.printStackTrace();
}

try {
testDoPhaseWithInvalidKey();
} catch (Exception e) {
failed = true;
e.printStackTrace();
}

if (failed) {
throw new RuntimeException("Test failed");
}
}
}

1 comment on commit 43d2d68

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.