Path: blob/master/src/jdk.crypto.ec/share/classes/sun/security/ec/ECDHKeyAgreement.java
41161 views
/*1* Copyright (c) 2009, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.security.ec;2627import sun.security.ec.point.AffinePoint;28import sun.security.ec.point.Point;29import sun.security.util.ArrayUtil;30import sun.security.util.CurveDB;31import sun.security.util.NamedCurve;32import sun.security.util.math.ImmutableIntegerModuloP;33import sun.security.util.math.IntegerFieldModuloP;34import sun.security.util.math.MutableIntegerModuloP;35import sun.security.util.math.SmallValue;3637import javax.crypto.KeyAgreementSpi;38import javax.crypto.SecretKey;39import javax.crypto.ShortBufferException;40import javax.crypto.spec.SecretKeySpec;41import java.math.BigInteger;42import java.security.InvalidAlgorithmParameterException;43import java.security.InvalidKeyException;44import java.security.Key;45import java.security.NoSuchAlgorithmException;46import java.security.PrivateKey;47import java.security.SecureRandom;48import java.security.interfaces.ECPrivateKey;49import java.security.interfaces.ECPublicKey;50import java.security.spec.AlgorithmParameterSpec;51import java.security.spec.ECParameterSpec;52import java.security.spec.EllipticCurve;53import java.util.Optional;5455/**56* KeyAgreement implementation for ECDH.57*58* @since 1.759*/60public final class ECDHKeyAgreement extends KeyAgreementSpi {6162// private key, if initialized63private ECPrivateKey privateKey;64ECOperations privateKeyOps;6566// public key, non-null between doPhase() & generateSecret() only67private ECPublicKey publicKey;6869// length of the secret to be derived70private int secretLen;7172/**73* Constructs a new ECDHKeyAgreement.74*/75public ECDHKeyAgreement() {76}7778// Generic init79private void init(Key key) throws80InvalidKeyException, InvalidAlgorithmParameterException {81if (!(key instanceof PrivateKey)) {82throw new InvalidKeyException("Key must be instance of PrivateKey");83}84privateKey = (ECPrivateKey)ECKeyFactory.toECKey(key);85publicKey = null;86Optional<ECOperations> opsOpt =87ECOperations.forParameters(privateKey.getParams());88if (opsOpt.isEmpty()) {89NamedCurve nc = CurveDB.lookup(privateKey.getParams());90throw new InvalidAlgorithmParameterException(91"Curve not supported: " + (nc != null ? nc.toString() :92"unknown"));93}94privateKeyOps = opsOpt.get();95}9697// see JCE spec98@Override99protected void engineInit(Key key, SecureRandom random)100throws InvalidKeyException {101try {102init(key);103} catch (InvalidAlgorithmParameterException e) {104throw new InvalidKeyException(e);105}106}107108// see JCE spec109@Override110protected void engineInit(Key key, AlgorithmParameterSpec params,111SecureRandom random) throws InvalidKeyException,112InvalidAlgorithmParameterException {113if (params != null) {114throw new InvalidAlgorithmParameterException115("Parameters not supported");116}117init(key);118}119120// see JCE spec121@Override122protected Key engineDoPhase(Key key, boolean lastPhase)123throws InvalidKeyException, IllegalStateException {124if (privateKey == null) {125throw new IllegalStateException("Not initialized");126}127if (publicKey != null) {128throw new IllegalStateException("Phase already executed");129}130if (!lastPhase) {131throw new IllegalStateException132("Only two party agreement supported, lastPhase must be true");133}134if (!(key instanceof ECPublicKey)) {135throw new InvalidKeyException136("Key must be a PublicKey with algorithm EC");137}138139this.publicKey = (ECPublicKey) key;140141int keyLenBits =142publicKey.getParams().getCurve().getField().getFieldSize();143secretLen = (keyLenBits + 7) >> 3;144145// Validate public key146validate(privateKeyOps, publicKey);147148return null;149}150151// Verify that x and y are integers in the interval [0, p - 1].152private static void validateCoordinate(BigInteger c, BigInteger mod)153throws InvalidKeyException{154if (c.compareTo(BigInteger.ZERO) < 0) {155throw new InvalidKeyException("Invalid coordinate");156}157158if (c.compareTo(mod) >= 0) {159throw new InvalidKeyException("Invalid coordinate");160}161}162163// Check whether a public key is valid, following the ECC164// Full Public-key Validation Routine (See section 5.6.2.3.3,165// NIST SP 800-56A Revision 3).166private static void validate(ECOperations ops, ECPublicKey key)167throws InvalidKeyException {168169ECParameterSpec spec = key.getParams();170171// Note: Per the NIST 800-56A specification, it is required172// to verify that the public key is not the identity element173// (point of infinity). However, the point of infinity has no174// affine coordinates, although the point of infinity could175// be encoded. Per IEEE 1363.3-2013 (see section A.6.4.1),176// the point of inifinity is represented by a pair of177// coordinates (x, y) not on the curve. For EC prime finite178// field (q = p^m), the point of infinity is (0, 0) unless179// b = 0; in which case it is (0, 1).180//181// It means that this verification could be covered by the182// validation that the public key is on the curve. As will be183// verified in the following steps.184185// Ensure that integers are in proper range.186BigInteger x = key.getW().getAffineX();187BigInteger y = key.getW().getAffineY();188189BigInteger p = ops.getField().getSize();190validateCoordinate(x, p);191validateCoordinate(y, p);192193// Ensure the point is on the curve.194EllipticCurve curve = spec.getCurve();195BigInteger rhs = x.modPow(BigInteger.valueOf(3), p).add(curve.getA()196.multiply(x)).add(curve.getB()).mod(p);197BigInteger lhs = y.modPow(BigInteger.valueOf(2), p).mod(p);198if (!rhs.equals(lhs)) {199throw new InvalidKeyException("Point is not on curve");200}201202// Check the order of the point.203//204// Compute nQ (using elliptic curve arithmetic), and verify that205// nQ is the the identity element.206ImmutableIntegerModuloP xElem = ops.getField().getElement(x);207ImmutableIntegerModuloP yElem = ops.getField().getElement(y);208AffinePoint affP = new AffinePoint(xElem, yElem);209byte[] order = spec.getOrder().toByteArray();210ArrayUtil.reverse(order);211Point product = ops.multiply(affP, order);212if (!ops.isNeutral(product)) {213throw new InvalidKeyException("Point has incorrect order");214}215}216217// see JCE spec218@Override219protected byte[] engineGenerateSecret() throws IllegalStateException {220if ((privateKey == null) || (publicKey == null)) {221throw new IllegalStateException("Not initialized correctly");222}223224byte[] result;225try {226result = deriveKeyImpl(privateKey, privateKeyOps, publicKey);227} catch (Exception e) {228throw new IllegalStateException(e);229}230publicKey = null;231return result;232}233234// see JCE spec235@Override236protected int engineGenerateSecret(byte[] sharedSecret, int237offset) throws IllegalStateException, ShortBufferException {238if (secretLen > sharedSecret.length - offset) {239throw new ShortBufferException("Need " + secretLen240+ " bytes, only " + (sharedSecret.length - offset)241+ " available");242}243byte[] secret = engineGenerateSecret();244System.arraycopy(secret, 0, sharedSecret, offset, secret.length);245return secret.length;246}247248// see JCE spec249@Override250protected SecretKey engineGenerateSecret(String algorithm)251throws IllegalStateException, NoSuchAlgorithmException,252InvalidKeyException {253if (algorithm == null) {254throw new NoSuchAlgorithmException("Algorithm must not be null");255}256if (!(algorithm.equals("TlsPremasterSecret"))) {257throw new NoSuchAlgorithmException258("Only supported for algorithm TlsPremasterSecret");259}260return new SecretKeySpec(engineGenerateSecret(), "TlsPremasterSecret");261}262263private static264byte[] deriveKeyImpl(ECPrivateKey priv, ECOperations ops,265ECPublicKey pubKey) throws InvalidKeyException {266267IntegerFieldModuloP field = ops.getField();268// convert s array into field element and multiply by the cofactor269MutableIntegerModuloP scalar = field.getElement(priv.getS()).mutable();270SmallValue cofactor =271field.getSmallValue(priv.getParams().getCofactor());272scalar.setProduct(cofactor);273int keySize =274(priv.getParams().getCurve().getField().getFieldSize() + 7) / 8;275ImmutableIntegerModuloP x =276field.getElement(pubKey.getW().getAffineX());277ImmutableIntegerModuloP y =278field.getElement(pubKey.getW().getAffineY());279Point product = ops.multiply(new AffinePoint(x, y),280scalar.asByteArray(keySize));281if (ops.isNeutral(product)) {282throw new InvalidKeyException("Product is zero");283}284285byte[] result = product.asAffine().getX().asByteArray(keySize);286ArrayUtil.reverse(result);287288return result;289}290}291292293