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 }