1 /*
   2  * Copyright (c) 2009, 2018, Oracle and/or its affiliates. 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.security.ec;
  27 
  28 import java.math.*;
  29 import java.security.*;
  30 import java.security.interfaces.*;
  31 import java.security.spec.*;
  32 import java.util.Optional;
  33 
  34 import javax.crypto.*;
  35 import javax.crypto.spec.*;
  36 
  37 import sun.security.util.ArrayUtil;
  38 import sun.security.util.ECUtil;
  39 import sun.security.util.math.*;
  40 import sun.security.ec.point.*;
  41 
  42 /**
  43  * KeyAgreement implementation for ECDH.
  44  *
  45  * @since   1.7
  46  */
  47 public final class ECDHKeyAgreement extends KeyAgreementSpi {
  48 
  49     // private key, if initialized
  50     private ECPrivateKey privateKey;
  51 
  52     // public key, non-null between doPhase() & generateSecret() only
  53     private ECPublicKey publicKey;
  54 
  55     // length of the secret to be derived
  56     private int secretLen;
  57 
  58     /**
  59      * Constructs a new ECDHKeyAgreement.
  60      */
  61     public ECDHKeyAgreement() {
  62     }
  63 
  64     // see JCE spec
  65     @Override
  66     protected void engineInit(Key key, SecureRandom random)
  67             throws InvalidKeyException {
  68         if (!(key instanceof PrivateKey)) {
  69             throw new InvalidKeyException
  70                         ("Key must be instance of PrivateKey");
  71         }
  72         privateKey = (ECPrivateKey) ECKeyFactory.toECKey(key);
  73         publicKey = null;
  74     }
  75 
  76     // see JCE spec
  77     @Override
  78     protected void engineInit(Key key, AlgorithmParameterSpec params,
  79             SecureRandom random) throws InvalidKeyException,
  80             InvalidAlgorithmParameterException {
  81         if (params != null) {
  82             throw new InvalidAlgorithmParameterException
  83                         ("Parameters not supported");
  84         }
  85         engineInit(key, random);
  86     }
  87 
  88     // see JCE spec
  89     @Override
  90     protected Key engineDoPhase(Key key, boolean lastPhase)
  91             throws InvalidKeyException, IllegalStateException {
  92         if (privateKey == null) {
  93             throw new IllegalStateException("Not initialized");
  94         }
  95         if (publicKey != null) {
  96             throw new IllegalStateException("Phase already executed");
  97         }
  98         if (!lastPhase) {
  99             throw new IllegalStateException
 100                 ("Only two party agreement supported, lastPhase must be true");
 101         }
 102         if (!(key instanceof ECPublicKey)) {
 103             throw new InvalidKeyException
 104                 ("Key must be a PublicKey with algorithm EC");
 105         }
 106 
 107         this.publicKey = (ECPublicKey) key;
 108 
 109         ECParameterSpec params = publicKey.getParams();
 110         int keyLenBits = params.getCurve().getField().getFieldSize();
 111         secretLen = (keyLenBits + 7) >> 3;
 112 
 113         return null;
 114     }
 115 
 116     private static void validateCoordinate(BigInteger c, BigInteger mod) {
 117         if (c.compareTo(BigInteger.ZERO) < 0) {
 118             throw new ProviderException("invalid coordinate");
 119         }
 120 
 121         if (c.compareTo(mod) >= 0) {
 122             throw new ProviderException("invalid coordinate");
 123         }
 124     }
 125 
 126     /*
 127      * Check whether a public key is valid. Throw ProviderException
 128      * if it is not valid or could not be validated.
 129      */
 130     private static void validate(ECOperations ops, ECPublicKey key) {
 131 
 132         // ensure that integers are in proper range
 133         BigInteger x = key.getW().getAffineX();
 134         BigInteger y = key.getW().getAffineY();
 135 
 136         BigInteger p = ops.getField().getSize();
 137         validateCoordinate(x, p);
 138         validateCoordinate(y, p);
 139 
 140         // ensure the point is on the curve
 141         EllipticCurve curve = key.getParams().getCurve();
 142         BigInteger rhs = x.modPow(BigInteger.valueOf(3), p).add(curve.getA()
 143             .multiply(x)).add(curve.getB()).mod(p);
 144         BigInteger lhs = y.modPow(BigInteger.valueOf(2), p).mod(p);
 145         if (!rhs.equals(lhs)) {
 146             throw new ProviderException("point is not on curve");
 147         }
 148 
 149         // check the order of the point
 150         ImmutableIntegerModuloP xElem = ops.getField().getElement(x);
 151         ImmutableIntegerModuloP yElem = ops.getField().getElement(y);
 152         AffinePoint affP = new AffinePoint(xElem, yElem);
 153         byte[] order = key.getParams().getOrder().toByteArray();
 154         ArrayUtil.reverse(order);
 155         Point product = ops.multiply(affP, order);
 156         if (!ops.isNeutral(product)) {
 157             throw new ProviderException("point has incorrect order");
 158         }
 159 
 160     }
 161 
 162     // see JCE spec
 163     @Override
 164     protected byte[] engineGenerateSecret() throws IllegalStateException {
 165         if ((privateKey == null) || (publicKey == null)) {
 166             throw new IllegalStateException("Not initialized correctly");
 167         }
 168 
 169         Optional<byte[]> resultOpt = deriveKeyImpl(privateKey, publicKey);
 170         byte[] result = resultOpt.orElseGet(
 171             () -> deriveKeyNative(privateKey, publicKey)
 172         );
 173         publicKey = null;
 174         return result;
 175     }
 176 
 177     // see JCE spec
 178     @Override
 179     protected int engineGenerateSecret(byte[] sharedSecret, int
 180             offset) throws IllegalStateException, ShortBufferException {
 181         if (offset + secretLen > sharedSecret.length) {
 182             throw new ShortBufferException("Need " + secretLen
 183                 + " bytes, only " + (sharedSecret.length - offset)
 184                 + " available");
 185         }
 186         byte[] secret = engineGenerateSecret();
 187         System.arraycopy(secret, 0, sharedSecret, offset, secret.length);
 188         return secret.length;
 189     }
 190 
 191     // see JCE spec
 192     @Override
 193     protected SecretKey engineGenerateSecret(String algorithm)
 194             throws IllegalStateException, NoSuchAlgorithmException,
 195             InvalidKeyException {
 196         if (algorithm == null) {
 197             throw new NoSuchAlgorithmException("Algorithm must not be null");
 198         }
 199         if (!(algorithm.equals("TlsPremasterSecret"))) {
 200             throw new NoSuchAlgorithmException
 201                 ("Only supported for algorithm TlsPremasterSecret");
 202         }
 203         return new SecretKeySpec(engineGenerateSecret(), "TlsPremasterSecret");
 204     }
 205 
 206     private static
 207     Optional<byte[]> deriveKeyImpl(ECPrivateKey priv, ECPublicKey pubKey) {
 208 
 209         ECParameterSpec ecSpec = priv.getParams();
 210         EllipticCurve curve = ecSpec.getCurve();
 211         Optional<ECOperations> opsOpt = ECOperations.forParameters(ecSpec);
 212         if (opsOpt.isEmpty()) {
 213             return Optional.empty();
 214         }
 215         ECOperations ops = opsOpt.get();
 216         if (! (priv instanceof ECPrivateKeyImpl)) {
 217             return Optional.empty();
 218         }
 219         ECPrivateKeyImpl privImpl = (ECPrivateKeyImpl) priv;
 220         byte[] sArr = privImpl.getArrayS();
 221 
 222         // to match the native implementation, validate the public key here
 223         // and throw ProviderException if it is invalid
 224         validate(ops, pubKey);
 225 
 226         IntegerFieldModuloP field = ops.getField();
 227         // convert s array into field element and multiply by the cofactor
 228         MutableIntegerModuloP scalar = field.getElement(sArr).mutable();
 229         SmallValue cofactor =
 230             field.getSmallValue(priv.getParams().getCofactor());
 231         scalar.setProduct(cofactor);
 232         int keySize = (curve.getField().getFieldSize() + 7) / 8;
 233         byte[] privArr = scalar.asByteArray(keySize);
 234 
 235         ImmutableIntegerModuloP x =
 236             field.getElement(pubKey.getW().getAffineX());
 237         ImmutableIntegerModuloP y =
 238             field.getElement(pubKey.getW().getAffineY());
 239         AffinePoint affPub = new AffinePoint(x, y);
 240         Point product = ops.multiply(affPub, privArr);
 241         if (ops.isNeutral(product)) {
 242             throw new ProviderException("Product is zero");
 243         }
 244         AffinePoint affProduct = product.asAffine();
 245 
 246         byte[] result = affProduct.getX().asByteArray(keySize);
 247         ArrayUtil.reverse(result);
 248 
 249         return Optional.of(result);
 250     }
 251 
 252     private static
 253     byte[] deriveKeyNative(ECPrivateKey privateKey, ECPublicKey publicKey) {
 254 
 255         ECParameterSpec params = privateKey.getParams();
 256         byte[] s = privateKey.getS().toByteArray();
 257         byte[] encodedParams =                   // DER OID
 258             ECUtil.encodeECParameterSpec(null, params);
 259 
 260         byte[] publicValue;
 261         if (publicKey instanceof ECPublicKeyImpl) {
 262             ECPublicKeyImpl ecPub = (ECPublicKeyImpl) publicKey;
 263             publicValue = ecPub.getEncodedPublicValue();
 264         } else { // instanceof ECPublicKey
 265             publicValue =
 266                 ECUtil.encodePoint(publicKey.getW(), params.getCurve());
 267         }
 268 
 269         try {
 270             return deriveKey(s, publicValue, encodedParams);
 271 
 272         } catch (GeneralSecurityException e) {
 273             throw new ProviderException("Could not derive key", e);
 274         }
 275     }
 276 
 277 
 278     /**
 279      * Generates a secret key using the public and private keys.
 280      *
 281      * @param s the private key's S value.
 282      * @param w the public key's W point (in uncompressed form).
 283      * @param encodedParams the curve's DER encoded object identifier.
 284      *
 285      * @return byte[] the secret key.
 286      */
 287     private static native byte[] deriveKey(byte[] s, byte[] w,
 288         byte[] encodedParams) throws GeneralSecurityException;
 289 }