package sun.security.ec;
import java.math.*;
import java.security.*;
import java.security.interfaces.*;
import java.security.spec.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import sun.security.util.ArrayUtil;
import sun.security.util.ECUtil;
import sun.security.util.Optional;
import sun.security.util.Supplier;
import sun.security.util.math.*;
import sun.security.ec.point.*;
public final class ECDHKeyAgreement extends KeyAgreementSpi {
private ECPrivateKey privateKey;
private ECPublicKey publicKey;
private int secretLen;
public ECDHKeyAgreement() {
}
@Override
protected void engineInit(Key key, SecureRandom random)
throws InvalidKeyException {
if (!(key instanceof PrivateKey)) {
throw new InvalidKeyException
("Key must be instance of PrivateKey");
}
privateKey = (ECPrivateKey) ECKeyFactory.toECKey(key);
publicKey = null;
}
@Override
protected void engineInit(Key key, AlgorithmParameterSpec params,
SecureRandom random) throws InvalidKeyException,
InvalidAlgorithmParameterException {
if (params != null) {
throw new InvalidAlgorithmParameterException
("Parameters not supported");
}
engineInit(key, random);
}
@Override
protected Key engineDoPhase(Key key, boolean lastPhase)
throws InvalidKeyException, IllegalStateException {
if (privateKey == null) {
throw new IllegalStateException("Not initialized");
}
if (publicKey != null) {
throw new IllegalStateException("Phase already executed");
}
if (!lastPhase) {
throw new IllegalStateException
("Only two party agreement supported, lastPhase must be true");
}
if (!(key instanceof ECPublicKey)) {
throw new InvalidKeyException
("Key must be a PublicKey with algorithm EC");
}
this.publicKey = (ECPublicKey) key;
ECParameterSpec params = publicKey.getParams();
int keyLenBits = params.getCurve().getField().getFieldSize();
secretLen = (keyLenBits + 7) >> 3;
return null;
}
private static void validateCoordinate(BigInteger c, BigInteger mod) {
if (c.compareTo(BigInteger.ZERO) < 0) {
throw new ProviderException("invalid coordinate");
}
if (c.compareTo(mod) >= 0) {
throw new ProviderException("invalid coordinate");
}
}
private static void validate(ECOperations ops, ECPublicKey key) {
BigInteger x = key.getW().getAffineX();
BigInteger y = key.getW().getAffineY();
BigInteger p = ops.getField().getSize();
validateCoordinate(x, p);
validateCoordinate(y, p);
EllipticCurve curve = key.getParams().getCurve();
BigInteger rhs = x.modPow(BigInteger.valueOf(3), p).add(curve.getA()
.multiply(x)).add(curve.getB()).mod(p);
BigInteger lhs = y.modPow(BigInteger.valueOf(2), p).mod(p);
if (!rhs.equals(lhs)) {
throw new ProviderException("point is not on curve");
}
ImmutableIntegerModuloP xElem = ops.getField().getElement(x);
ImmutableIntegerModuloP yElem = ops.getField().getElement(y);
AffinePoint affP = new AffinePoint(xElem, yElem);
byte[] order = key.getParams().getOrder().toByteArray();
ArrayUtil.reverse(order);
Point product = ops.multiply(affP, order);
if (!ops.isNeutral(product)) {
throw new ProviderException("point has incorrect order");
}
}
@Override
protected byte[] engineGenerateSecret() throws IllegalStateException {
if ((privateKey == null) || (publicKey == null)) {
throw new IllegalStateException("Not initialized correctly");
}
Optional<byte[]> resultOpt = deriveKeyImpl(privateKey, publicKey);
return resultOpt.orElseGet(new Supplier<byte[]>() {
@Override
public byte[] get() { return deriveKeyNative(privateKey, publicKey); }
});
}
@Override
protected int engineGenerateSecret(byte[] sharedSecret, int
offset) throws IllegalStateException, ShortBufferException {
if (offset + secretLen > sharedSecret.length) {
throw new ShortBufferException("Need " + secretLen
+ " bytes, only " + (sharedSecret.length - offset)
+ " available");
}
byte[] secret = engineGenerateSecret();
System.arraycopy(secret, 0, sharedSecret, offset, secret.length);
return secret.length;
}
@Override
protected SecretKey engineGenerateSecret(String algorithm)
throws IllegalStateException, NoSuchAlgorithmException,
InvalidKeyException {
if (algorithm == null) {
throw new NoSuchAlgorithmException("Algorithm must not be null");
}
if (!(algorithm.equals("TlsPremasterSecret"))) {
throw new NoSuchAlgorithmException
("Only supported for algorithm TlsPremasterSecret");
}
return new SecretKeySpec(engineGenerateSecret(), "TlsPremasterSecret");
}
private static
Optional<byte[]> deriveKeyImpl(ECPrivateKey priv, ECPublicKey pubKey) {
ECParameterSpec ecSpec = priv.getParams();
EllipticCurve curve = ecSpec.getCurve();
Optional<ECOperations> opsOpt = ECOperations.forParameters(ecSpec);
if (opsOpt.isEmpty()) {
return Optional.empty();
}
ECOperations ops = opsOpt.get();
if (! (priv instanceof ECPrivateKeyImpl)) {
return Optional.empty();
}
ECPrivateKeyImpl privImpl = (ECPrivateKeyImpl) priv;
byte[] sArr = privImpl.getArrayS();
validate(ops, pubKey);
IntegerFieldModuloP field = ops.getField();
MutableIntegerModuloP scalar = field.getElement(sArr).mutable();
SmallValue cofactor =
field.getSmallValue(priv.getParams().getCofactor());
scalar.setProduct(cofactor);
int keySize = (curve.getField().getFieldSize() + 7) / 8;
byte[] privArr = scalar.asByteArray(keySize);
ImmutableIntegerModuloP x =
field.getElement(pubKey.getW().getAffineX());
ImmutableIntegerModuloP y =
field.getElement(pubKey.getW().getAffineY());
AffinePoint affPub = new AffinePoint(x, y);
Point product = ops.multiply(affPub, privArr);
if (ops.isNeutral(product)) {
throw new ProviderException("Product is zero");
}
AffinePoint affProduct = product.asAffine();
byte[] result = affProduct.getX().asByteArray(keySize);
ArrayUtil.reverse(result);
return Optional.of(result);
}
private static
byte[] deriveKeyNative(ECPrivateKey privateKey, ECPublicKey publicKey) {
ECParameterSpec params = privateKey.getParams();
byte[] s = privateKey.getS().toByteArray();
byte[] encodedParams =
ECUtil.encodeECParameterSpec(null, params);
byte[] publicValue;
if (publicKey instanceof ECPublicKeyImpl) {
ECPublicKeyImpl ecPub = (ECPublicKeyImpl) publicKey;
publicValue = ecPub.getEncodedPublicValue();
} else {
publicValue =
ECUtil.encodePoint(publicKey.getW(), params.getCurve());
}
try {
return deriveKey(s, publicValue, encodedParams);
} catch (GeneralSecurityException e) {
throw new ProviderException("Could not derive key", e);
}
}
private static native byte[] deriveKey(byte[] s, byte[] w,
byte[] encodedParams) throws GeneralSecurityException;
}