diff --git a/src/java.base/share/classes/sun/security/ec/ECDHKeyAgreement.java b/src/java.base/share/classes/sun/security/ec/ECDHKeyAgreement.java index fbdd2bdff37..1fdbd94786a 100644 --- a/src/java.base/share/classes/sun/security/ec/ECDHKeyAgreement.java +++ b/src/java.base/share/classes/sun/security/ec/ECDHKeyAgreement.java @@ -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 @@ -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; @@ -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; @@ -80,20 +78,26 @@ public final class ECDHKeyAgreement extends KeyAgreementSpi { // 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 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(); } @@ -139,26 +143,22 @@ public final class ECDHKeyAgreement extends KeyAgreementSpi { ("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"); } } diff --git a/test/jdk/sun/security/ec/ECDHKeyAgreementParamValidation.java b/test/jdk/sun/security/ec/ECDHKeyAgreementParamValidation.java new file mode 100644 index 00000000000..0864d650162 --- /dev/null +++ b/test/jdk/sun/security/ec/ECDHKeyAgreementParamValidation.java @@ -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"); + } + } +}