package sun.security.ec;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.Key;
import java.security.SecureRandom;
import java.security.ProviderException;
import java.security.interfaces.XECPrivateKey;
import java.security.interfaces.XECPublicKey;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.NamedParameterSpec;
import javax.crypto.KeyAgreementSpi;
import javax.crypto.SecretKey;
import javax.crypto.ShortBufferException;
import javax.crypto.spec.SecretKeySpec;
import java.util.function.Function;
public class XDHKeyAgreement extends KeyAgreementSpi {
private byte[] privateKey;
private byte[] secret;
private XECOperations ops;
private XECParameters lockedParams = null;
XDHKeyAgreement() {
}
XDHKeyAgreement(AlgorithmParameterSpec paramSpec) {
lockedParams = XECParameters.get(ProviderException::new, paramSpec);
}
@Override
protected void engineInit(Key key, SecureRandom random)
throws InvalidKeyException {
initImpl(key);
}
@Override
protected void engineInit(Key key, final AlgorithmParameterSpec params,
SecureRandom random) throws InvalidKeyException,
InvalidAlgorithmParameterException {
initImpl(key);
if (params != null) {
XECParameters xecParams = XECParameters.get(
InvalidAlgorithmParameterException::new, params);
if (!xecParams.oidEquals(this.ops.getParameters())) {
throw new InvalidKeyException(
"Incorrect private key parameters"
);
}
}
}
private
<T extends Throwable>
void checkLockedParams(Function<String, T> exception,
XECParameters params) throws T {
if (lockedParams != null && lockedParams != params) {
throw exception.apply("Parameters must be " +
lockedParams.getName());
}
}
private void initImpl(Key key) throws InvalidKeyException {
if (!(key instanceof XECPrivateKey)) {
throw new InvalidKeyException
("Unsupported key type");
}
XECPrivateKey privateKey = (XECPrivateKey) key;
XECParameters xecParams = XECParameters.get(
InvalidKeyException::new, privateKey.getParams());
checkLockedParams(InvalidKeyException::new, xecParams);
this.ops = new XECOperations(xecParams);
this.privateKey = privateKey.getScalar().orElseThrow(
() -> new InvalidKeyException("No private key value")
);
secret = null;
}
@Override
protected Key engineDoPhase(Key key, boolean lastPhase)
throws InvalidKeyException, IllegalStateException {
if (this.privateKey == null) {
throw new IllegalStateException("Not initialized");
}
if (this.secret != null) {
throw new IllegalStateException("Phase already executed");
}
if (!lastPhase) {
throw new IllegalStateException
("Only two party agreement supported, lastPhase must be true");
}
if (!(key instanceof XECPublicKey)) {
throw new InvalidKeyException
("Unsupported key type");
}
XECPublicKey publicKey = (XECPublicKey) key;
XECParameters xecParams = XECParameters.get(InvalidKeyException::new,
publicKey.getParams());
if (!ops.getParameters().oidEquals(xecParams)) {
throw new InvalidKeyException(
"Public key parameters are not compatible with private key.");
}
byte[] computedSecret = ops.encodedPointMultiply(
this.privateKey,
publicKey.getU());
if (allZero(computedSecret)) {
throw new InvalidKeyException("Point has small order");
}
this.secret = computedSecret;
return null;
}
private boolean allZero(byte[] arr) {
byte orValue = (byte) 0;
for (int i = 0; i < arr.length; i++) {
orValue |= arr[i];
}
return orValue == (byte) 0;
}
@Override
protected byte[] engineGenerateSecret() throws IllegalStateException {
if (secret == null) {
throw new IllegalStateException("Not initialized correctly");
}
byte[] result = secret;
secret = null;
return result;
}
@Override
protected int engineGenerateSecret(byte[] sharedSecret, int offset)
throws IllegalStateException, ShortBufferException {
if (secret == null) {
throw new IllegalStateException("Not initialized correctly");
}
int secretLen = this.secret.length;
if (secretLen > sharedSecret.length - offset) {
throw new ShortBufferException("Need " + secretLen
+ " bytes, only " + (sharedSecret.length - offset)
+ " available");
}
System.arraycopy(this.secret, 0, sharedSecret, offset, secretLen);
secret = null;
return secretLen;
}
@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(), algorithm);
}
static class X25519 extends XDHKeyAgreement {
public X25519() {
super(NamedParameterSpec.X25519);
}
}
static class X448 extends XDHKeyAgreement {
public X448() {
super(NamedParameterSpec.X448);
}
}
}