mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 07:14:30 +02:00
8308398: Move SunEC crypto provider into java.base
Reviewed-by: valeriep, alanb
This commit is contained in:
parent
69a46c25cc
commit
e737968792
58 changed files with 146 additions and 114 deletions
|
@ -0,0 +1,286 @@
|
|||
/*
|
||||
* Copyright (c) 2009, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ec;
|
||||
|
||||
import sun.security.ec.point.AffinePoint;
|
||||
import sun.security.ec.point.Point;
|
||||
import sun.security.util.ArrayUtil;
|
||||
import sun.security.util.CurveDB;
|
||||
import sun.security.util.ECUtil;
|
||||
import sun.security.util.NamedCurve;
|
||||
import sun.security.util.math.ImmutableIntegerModuloP;
|
||||
import sun.security.util.math.IntegerFieldModuloP;
|
||||
import sun.security.util.math.MutableIntegerModuloP;
|
||||
import sun.security.util.math.SmallValue;
|
||||
|
||||
import javax.crypto.KeyAgreementSpi;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.ShortBufferException;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.math.BigInteger;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.Key;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.interfaces.ECPrivateKey;
|
||||
import java.security.interfaces.ECPublicKey;
|
||||
import java.security.spec.AlgorithmParameterSpec;
|
||||
import java.security.spec.ECParameterSpec;
|
||||
import java.security.spec.EllipticCurve;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* KeyAgreement implementation for ECDH.
|
||||
*
|
||||
* @since 1.7
|
||||
*/
|
||||
public final class ECDHKeyAgreement extends KeyAgreementSpi {
|
||||
|
||||
// private key, if initialized
|
||||
private ECPrivateKey privateKey;
|
||||
ECOperations privateKeyOps;
|
||||
|
||||
// public key, non-null between doPhase() & generateSecret() only
|
||||
private ECPublicKey publicKey;
|
||||
|
||||
// length of the secret to be derived
|
||||
private int secretLen;
|
||||
|
||||
/**
|
||||
* Constructs a new ECDHKeyAgreement.
|
||||
*/
|
||||
public ECDHKeyAgreement() {
|
||||
}
|
||||
|
||||
// Generic init
|
||||
private void init(Key key) throws
|
||||
InvalidKeyException, InvalidAlgorithmParameterException {
|
||||
if (!(key instanceof PrivateKey)) {
|
||||
throw new InvalidKeyException("Key must be instance of PrivateKey");
|
||||
}
|
||||
privateKey = (ECPrivateKey)ECKeyFactory.toECKey(key);
|
||||
publicKey = null;
|
||||
Optional<ECOperations> opsOpt =
|
||||
ECOperations.forParameters(privateKey.getParams());
|
||||
if (opsOpt.isEmpty()) {
|
||||
NamedCurve nc = CurveDB.lookup(privateKey.getParams());
|
||||
throw new InvalidAlgorithmParameterException(
|
||||
"Curve not supported: " + (nc != null ? nc.toString() :
|
||||
"unknown"));
|
||||
}
|
||||
ECUtil.checkPrivateKey(privateKey);
|
||||
privateKeyOps = opsOpt.get();
|
||||
}
|
||||
|
||||
// see JCE spec
|
||||
@Override
|
||||
protected void engineInit(Key key, SecureRandom random)
|
||||
throws InvalidKeyException {
|
||||
try {
|
||||
init(key);
|
||||
} catch (InvalidAlgorithmParameterException e) {
|
||||
throw new InvalidKeyException(e);
|
||||
}
|
||||
}
|
||||
|
||||
// see JCE spec
|
||||
@Override
|
||||
protected void engineInit(Key key, AlgorithmParameterSpec params,
|
||||
SecureRandom random) throws InvalidKeyException,
|
||||
InvalidAlgorithmParameterException {
|
||||
if (params != null) {
|
||||
throw new InvalidAlgorithmParameterException
|
||||
("Parameters not supported");
|
||||
}
|
||||
init(key);
|
||||
}
|
||||
|
||||
// see JCE spec
|
||||
@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;
|
||||
|
||||
int keyLenBits =
|
||||
publicKey.getParams().getCurve().getField().getFieldSize();
|
||||
secretLen = (keyLenBits + 7) >> 3;
|
||||
|
||||
// Validate public key
|
||||
validate(privateKeyOps, publicKey);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Verify that x and y are integers in the interval [0, p - 1].
|
||||
private static void validateCoordinate(BigInteger c, BigInteger mod)
|
||||
throws InvalidKeyException{
|
||||
if (c.compareTo(BigInteger.ZERO) < 0) {
|
||||
throw new InvalidKeyException("Invalid coordinate");
|
||||
}
|
||||
|
||||
if (c.compareTo(mod) >= 0) {
|
||||
throw new InvalidKeyException("Invalid coordinate");
|
||||
}
|
||||
}
|
||||
|
||||
// Check whether a public key is valid, following the ECC
|
||||
// Full Public-key Validation Routine (See section 5.6.2.3.3,
|
||||
// NIST SP 800-56A Revision 3).
|
||||
private static void validate(ECOperations ops, ECPublicKey key)
|
||||
throws InvalidKeyException {
|
||||
|
||||
ECParameterSpec spec = key.getParams();
|
||||
|
||||
// Note: Per the NIST 800-56A specification, it is required
|
||||
// to verify that the public key is not the identity element
|
||||
// (point of infinity). However, the point of infinity has no
|
||||
// affine coordinates, although the point of infinity could
|
||||
// be encoded. Per IEEE 1363.3-2013 (see section A.6.4.1),
|
||||
// the point of infinity is represented by a pair of
|
||||
// coordinates (x, y) not on the curve. For EC prime finite
|
||||
// field (q = p^m), the point of infinity is (0, 0) unless
|
||||
// b = 0; in which case it is (0, 1).
|
||||
//
|
||||
// It means that this verification could be covered by the
|
||||
// validation that the public key is on the curve. As will be
|
||||
// verified in the following steps.
|
||||
|
||||
// Ensure that integers are in proper range.
|
||||
BigInteger x = key.getW().getAffineX();
|
||||
BigInteger y = key.getW().getAffineY();
|
||||
|
||||
BigInteger p = ops.getField().getSize();
|
||||
validateCoordinate(x, p);
|
||||
validateCoordinate(y, p);
|
||||
|
||||
// Ensure the point is on the curve.
|
||||
EllipticCurve curve = spec.getCurve();
|
||||
BigInteger rhs = x.modPow(BigInteger.valueOf(3), p).add(curve.getA()
|
||||
.multiply(x)).add(curve.getB()).mod(p);
|
||||
BigInteger lhs = y.modPow(BigInteger.TWO, p);
|
||||
if (!rhs.equals(lhs)) {
|
||||
throw new InvalidKeyException("Point is not on curve");
|
||||
}
|
||||
|
||||
// Check the order of the point.
|
||||
//
|
||||
// Compute nQ (using elliptic curve arithmetic), and verify that
|
||||
// nQ is the identity element.
|
||||
byte[] order = spec.getOrder().toByteArray();
|
||||
ArrayUtil.reverse(order);
|
||||
Point product = ops.multiply(key.getW(), order);
|
||||
if (!ops.isNeutral(product)) {
|
||||
throw new InvalidKeyException("Point has incorrect order");
|
||||
}
|
||||
}
|
||||
|
||||
// see JCE spec
|
||||
@Override
|
||||
protected byte[] engineGenerateSecret() throws IllegalStateException {
|
||||
if ((privateKey == null) || (publicKey == null)) {
|
||||
throw new IllegalStateException("Not initialized correctly");
|
||||
}
|
||||
|
||||
byte[] result;
|
||||
try {
|
||||
result = deriveKeyImpl(privateKey, privateKeyOps, publicKey);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
publicKey = null;
|
||||
return result;
|
||||
}
|
||||
|
||||
// see JCE spec
|
||||
@Override
|
||||
protected int engineGenerateSecret(byte[] sharedSecret, int
|
||||
offset) throws IllegalStateException, ShortBufferException {
|
||||
if (secretLen > sharedSecret.length - offset) {
|
||||
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;
|
||||
}
|
||||
|
||||
// see JCE spec
|
||||
@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
|
||||
byte[] deriveKeyImpl(ECPrivateKey priv, ECOperations ops,
|
||||
ECPublicKey pubKey) throws InvalidKeyException {
|
||||
|
||||
IntegerFieldModuloP field = ops.getField();
|
||||
// convert s array into field element and multiply by the cofactor
|
||||
MutableIntegerModuloP scalar = field.getElement(priv.getS()).mutable();
|
||||
SmallValue cofactor =
|
||||
field.getSmallValue(priv.getParams().getCofactor());
|
||||
scalar.setProduct(cofactor);
|
||||
int keySize =
|
||||
(priv.getParams().getCurve().getField().getFieldSize() + 7) / 8;
|
||||
Point product =
|
||||
ops.multiply(pubKey.getW(), scalar.asByteArray(keySize));
|
||||
if (ops.isNeutral(product)) {
|
||||
throw new InvalidKeyException("Product is zero");
|
||||
}
|
||||
|
||||
byte[] result = product.asAffine().getX().asByteArray(keySize);
|
||||
ArrayUtil.reverse(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
268
src/java.base/share/classes/sun/security/ec/ECDSAOperations.java
Normal file
268
src/java.base/share/classes/sun/security/ec/ECDSAOperations.java
Normal file
|
@ -0,0 +1,268 @@
|
|||
/*
|
||||
* Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ec;
|
||||
|
||||
import sun.security.ec.point.*;
|
||||
import sun.security.util.ArrayUtil;
|
||||
import sun.security.util.math.*;
|
||||
import static sun.security.ec.ECOperations.IntermediateValueException;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.ProviderException;
|
||||
import java.security.spec.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
|
||||
public class ECDSAOperations {
|
||||
|
||||
public static class Seed {
|
||||
private final byte[] seedValue;
|
||||
|
||||
public Seed(byte[] seedValue) {
|
||||
this.seedValue = seedValue;
|
||||
}
|
||||
|
||||
public byte[] getSeedValue() {
|
||||
return seedValue;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Nonce {
|
||||
private final byte[] nonceValue;
|
||||
|
||||
public Nonce(byte[] nonceValue) {
|
||||
this.nonceValue = nonceValue;
|
||||
}
|
||||
|
||||
public byte[] getNonceValue() {
|
||||
return nonceValue;
|
||||
}
|
||||
}
|
||||
|
||||
private final ECOperations ecOps;
|
||||
private final AffinePoint basePoint;
|
||||
|
||||
public ECDSAOperations(ECOperations ecOps, ECPoint basePoint) {
|
||||
this.ecOps = ecOps;
|
||||
this.basePoint = toAffinePoint(basePoint, ecOps.getField());
|
||||
}
|
||||
|
||||
public ECOperations getEcOperations() {
|
||||
return ecOps;
|
||||
}
|
||||
|
||||
public AffinePoint basePointMultiply(byte[] scalar) {
|
||||
return ecOps.multiply(basePoint, scalar).asAffine();
|
||||
}
|
||||
|
||||
public static AffinePoint toAffinePoint(ECPoint point,
|
||||
IntegerFieldModuloP field) {
|
||||
|
||||
ImmutableIntegerModuloP affineX = field.getElement(point.getAffineX());
|
||||
ImmutableIntegerModuloP affineY = field.getElement(point.getAffineY());
|
||||
return new AffinePoint(affineX, affineY);
|
||||
}
|
||||
|
||||
public static
|
||||
Optional<ECDSAOperations> forParameters(ECParameterSpec ecParams) {
|
||||
Optional<ECOperations> curveOps =
|
||||
ECOperations.forParameters(ecParams);
|
||||
return curveOps.map(
|
||||
ops -> new ECDSAOperations(ops, ecParams.getGenerator())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Sign a digest using the provided private key and seed.
|
||||
* IMPORTANT: The private key is a scalar represented using a
|
||||
* little-endian byte array. This is backwards from the conventional
|
||||
* representation in ECDSA. The routines that produce and consume this
|
||||
* value uses little-endian, so this deviation from convention removes
|
||||
* the requirement to swap the byte order. The returned signature is in
|
||||
* the conventional byte order.
|
||||
*
|
||||
* @param privateKey the private key scalar as a little-endian byte array
|
||||
* @param digest the digest to be signed
|
||||
* @param seed the seed that will be used to produce the nonce. This object
|
||||
* should contain an array that is at least 64 bits longer than
|
||||
* the number of bits required to represent the group order.
|
||||
* @return the ECDSA signature value
|
||||
* @throws IntermediateValueException if the signature cannot be produced
|
||||
* due to an unacceptable intermediate or final value. If this
|
||||
* exception is thrown, then the caller should discard the nonce and
|
||||
* try again with an entirely new nonce value.
|
||||
*/
|
||||
public byte[] signDigest(byte[] privateKey, byte[] digest, Seed seed)
|
||||
throws IntermediateValueException {
|
||||
|
||||
byte[] nonceArr = ecOps.seedToScalar(seed.getSeedValue());
|
||||
|
||||
Nonce nonce = new Nonce(nonceArr);
|
||||
return signDigest(privateKey, digest, nonce);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Sign a digest using the provided private key and nonce.
|
||||
* IMPORTANT: The private key and nonce are scalars represented by a
|
||||
* little-endian byte array. This is backwards from the conventional
|
||||
* representation in ECDSA. The routines that produce and consume these
|
||||
* values use little-endian, so this deviation from convention removes
|
||||
* the requirement to swap the byte order. The returned signature is in
|
||||
* the conventional byte order.
|
||||
*
|
||||
* @param privateKey the private key scalar as a little-endian byte array
|
||||
* @param digest the digest to be signed
|
||||
* @param nonce the nonce object containing a little-endian scalar value.
|
||||
* @return the ECDSA signature value
|
||||
* @throws IntermediateValueException if the signature cannot be produced
|
||||
* due to an unacceptable intermediate or final value. If this
|
||||
* exception is thrown, then the caller should discard the nonce and
|
||||
* try again with an entirely new nonce value.
|
||||
*/
|
||||
public byte[] signDigest(byte[] privateKey, byte[] digest, Nonce nonce)
|
||||
throws IntermediateValueException {
|
||||
|
||||
IntegerFieldModuloP orderField = ecOps.getOrderField();
|
||||
int orderBits = orderField.getSize().bitLength();
|
||||
if (orderBits % 8 != 0 && orderBits < digest.length * 8) {
|
||||
// This implementation does not support truncating digests to
|
||||
// a length that is not a multiple of 8.
|
||||
throw new ProviderException("Invalid digest length");
|
||||
}
|
||||
|
||||
byte[] k = nonce.getNonceValue();
|
||||
// check nonce length
|
||||
int length = (orderField.getSize().bitLength() + 7) / 8;
|
||||
if (k.length != length) {
|
||||
throw new ProviderException("Incorrect nonce length");
|
||||
}
|
||||
|
||||
MutablePoint R = ecOps.multiply(basePoint, k);
|
||||
IntegerModuloP r = R.asAffine().getX();
|
||||
// put r into the correct field by fully reducing to an array
|
||||
byte[] temp = new byte[length];
|
||||
r = b2a(r, orderField, temp);
|
||||
byte[] result = new byte[2 * length];
|
||||
ArrayUtil.reverse(temp);
|
||||
System.arraycopy(temp, 0, result, 0, length);
|
||||
// compare r to 0
|
||||
if (ECOperations.allZero(temp)) {
|
||||
throw new IntermediateValueException();
|
||||
}
|
||||
|
||||
IntegerModuloP dU = orderField.getElement(privateKey);
|
||||
int lengthE = Math.min(length, digest.length);
|
||||
byte[] E = new byte[lengthE];
|
||||
System.arraycopy(digest, 0, E, 0, lengthE);
|
||||
ArrayUtil.reverse(E);
|
||||
IntegerModuloP e = orderField.getElement(E);
|
||||
IntegerModuloP kElem = orderField.getElement(k);
|
||||
IntegerModuloP kInv = kElem.multiplicativeInverse();
|
||||
MutableIntegerModuloP s = r.mutable();
|
||||
s.setProduct(dU).setSum(e).setProduct(kInv);
|
||||
// store s in result
|
||||
s.asByteArray(temp);
|
||||
ArrayUtil.reverse(temp);
|
||||
System.arraycopy(temp, 0, result, length, length);
|
||||
// compare s to 0
|
||||
if (ECOperations.allZero(temp)) {
|
||||
throw new IntermediateValueException();
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
public boolean verifySignedDigest(byte[] digest, byte[] sig, ECPoint pp) {
|
||||
|
||||
IntegerFieldModuloP field = ecOps.getField();
|
||||
IntegerFieldModuloP orderField = ecOps.getOrderField();
|
||||
BigInteger mod = orderField.getSize();
|
||||
int length = (mod.bitLength() + 7) / 8;
|
||||
|
||||
byte[] r;
|
||||
byte[] s;
|
||||
|
||||
int encodeLength = sig.length / 2;
|
||||
if (sig.length %2 != 0 || encodeLength > length) {
|
||||
return false;
|
||||
} else if (encodeLength == length) {
|
||||
r = Arrays.copyOf(sig, length);
|
||||
s = Arrays.copyOfRange(sig, length, length * 2);
|
||||
} else {
|
||||
r = new byte[length];
|
||||
s = new byte[length];
|
||||
System.arraycopy(sig, 0, r, length - encodeLength, encodeLength);
|
||||
System.arraycopy(sig, encodeLength, s, length - encodeLength, encodeLength);
|
||||
}
|
||||
|
||||
BigInteger rb = new BigInteger(1, r);
|
||||
BigInteger sb = new BigInteger(1, s);
|
||||
if (rb.signum() == 0 || sb.signum() == 0
|
||||
|| rb.compareTo(mod) >= 0 || sb.compareTo(mod) >= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ArrayUtil.reverse(r);
|
||||
ArrayUtil.reverse(s);
|
||||
IntegerModuloP ri = orderField.getElement(r);
|
||||
IntegerModuloP si = orderField.getElement(s);
|
||||
// z
|
||||
int lengthE = Math.min(length, digest.length);
|
||||
byte[] E = new byte[lengthE];
|
||||
System.arraycopy(digest, 0, E, 0, lengthE);
|
||||
ArrayUtil.reverse(E);
|
||||
IntegerModuloP e = orderField.getElement(E);
|
||||
|
||||
IntegerModuloP sInv = si.multiplicativeInverse();
|
||||
ImmutableIntegerModuloP u1 = e.multiply(sInv);
|
||||
ImmutableIntegerModuloP u2 = ri.multiply(sInv);
|
||||
|
||||
byte[] temp1 = new byte[length];
|
||||
b2a(u1, orderField, temp1);
|
||||
|
||||
byte[] temp2 = new byte[length];
|
||||
b2a(u2, orderField, temp2);
|
||||
|
||||
MutablePoint p1 = ecOps.multiply(basePoint, temp1);
|
||||
MutablePoint p2 = ecOps.multiply(pp, temp2);
|
||||
|
||||
ecOps.setSum(p1, p2.asAffine());
|
||||
IntegerModuloP result = p1.asAffine().getX();
|
||||
b2a(result, orderField, temp1);
|
||||
return MessageDigest.isEqual(temp1, r);
|
||||
}
|
||||
|
||||
public static ImmutableIntegerModuloP b2a(IntegerModuloP b,
|
||||
IntegerFieldModuloP orderField, byte[] temp1) {
|
||||
b.asByteArray(temp1);
|
||||
ImmutableIntegerModuloP b2 = orderField.getElement(temp1);
|
||||
b2.asByteArray(temp1);
|
||||
return b2;
|
||||
}
|
||||
}
|
553
src/java.base/share/classes/sun/security/ec/ECDSASignature.java
Normal file
553
src/java.base/share/classes/sun/security/ec/ECDSASignature.java
Normal file
|
@ -0,0 +1,553 @@
|
|||
/*
|
||||
* Copyright (c) 2009, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ec;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import java.security.*;
|
||||
import java.security.interfaces.*;
|
||||
import java.security.spec.*;
|
||||
import java.util.Optional;
|
||||
|
||||
import sun.security.jca.JCAUtil;
|
||||
import sun.security.util.*;
|
||||
import static sun.security.ec.ECOperations.IntermediateValueException;
|
||||
|
||||
/**
|
||||
* ECDSA signature implementation. This class currently supports the
|
||||
* following algorithm names:
|
||||
*
|
||||
* . "NONEwithECDSA"
|
||||
* . "SHA1withECDSA"
|
||||
* . "SHA224withECDSA"
|
||||
* . "SHA256withECDSA"
|
||||
* . "SHA384withECDSA"
|
||||
* . "SHA512withECDSA"
|
||||
* . "SHA3-224withECDSA"
|
||||
* . "SHA3-256withECDSA"
|
||||
* . "SHA3-384withECDSA"
|
||||
* . "SHA3-512withECDSA"
|
||||
* . "NONEwithECDSAinP1363Format"
|
||||
* . "SHA1withECDSAinP1363Format"
|
||||
* . "SHA224withECDSAinP1363Format"
|
||||
* . "SHA256withECDSAinP1363Format"
|
||||
* . "SHA384withECDSAinP1363Format"
|
||||
* . "SHA512withECDSAinP1363Format"
|
||||
* . "SHA3-224withECDSAinP1363Format"
|
||||
* . "SHA3-256withECDSAinP1363Format"
|
||||
* . "SHA3-384withECDSAinP1363Format"
|
||||
* . "SHA3-512withECDSAinP1363Format"
|
||||
*
|
||||
* @since 1.7
|
||||
*/
|
||||
abstract class ECDSASignature extends SignatureSpi {
|
||||
|
||||
// message digest implementation we use
|
||||
private final MessageDigest messageDigest;
|
||||
|
||||
// supplied entropy
|
||||
private SecureRandom random;
|
||||
|
||||
// flag indicating whether the digest has been reset
|
||||
private boolean needsReset;
|
||||
|
||||
// private key, if initialized for signing
|
||||
private ECPrivateKey privateKey;
|
||||
|
||||
// public key, if initialized for verifying
|
||||
private ECPublicKey publicKey;
|
||||
|
||||
// The format. true for the IEEE P1363 format. false (default) for ASN.1
|
||||
private final boolean p1363Format;
|
||||
|
||||
/**
|
||||
* Constructs a new ECDSASignature.
|
||||
*
|
||||
* @exception ProviderException if the native ECC library is unavailable.
|
||||
*/
|
||||
ECDSASignature() {
|
||||
this(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new ECDSASignature that will use the specified
|
||||
* signature format. {@code p1363Format} should be {@code true} to
|
||||
* use the IEEE P1363 format. If {@code p1363Format} is {@code false},
|
||||
* the DER-encoded ASN.1 format will be used. This constructor is
|
||||
* used by the RawECDSA subclasses.
|
||||
*/
|
||||
ECDSASignature(boolean p1363Format) {
|
||||
this.messageDigest = null;
|
||||
this.p1363Format = p1363Format;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new ECDSASignature. Used by subclasses.
|
||||
*/
|
||||
ECDSASignature(String digestName) {
|
||||
this(digestName, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new ECDSASignature that will use the specified
|
||||
* digest and signature format. {@code p1363Format} should be
|
||||
* {@code true} to use the IEEE P1363 format. If {@code p1363Format}
|
||||
* is {@code false}, the DER-encoded ASN.1 format will be used. This
|
||||
* constructor is used by subclasses.
|
||||
*/
|
||||
ECDSASignature(String digestName, boolean p1363Format) {
|
||||
try {
|
||||
messageDigest = MessageDigest.getInstance(digestName);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new ProviderException(e);
|
||||
}
|
||||
this.needsReset = false;
|
||||
this.p1363Format = p1363Format;
|
||||
}
|
||||
|
||||
// Class for Raw ECDSA signatures.
|
||||
static class RawECDSA extends ECDSASignature {
|
||||
|
||||
// the longest supported digest is 512 bits (SHA-512)
|
||||
private static final int RAW_ECDSA_MAX = 64;
|
||||
|
||||
private final byte[] precomputedDigest;
|
||||
private int offset = 0;
|
||||
|
||||
RawECDSA(boolean p1363Format) {
|
||||
super(p1363Format);
|
||||
precomputedDigest = new byte[RAW_ECDSA_MAX];
|
||||
}
|
||||
|
||||
// Stores the precomputed message digest value.
|
||||
@Override
|
||||
protected void engineUpdate(byte b) throws SignatureException {
|
||||
if (offset >= precomputedDigest.length) {
|
||||
offset = RAW_ECDSA_MAX + 1;
|
||||
return;
|
||||
}
|
||||
precomputedDigest[offset++] = b;
|
||||
}
|
||||
|
||||
// Stores the precomputed message digest value.
|
||||
@Override
|
||||
protected void engineUpdate(byte[] b, int off, int len)
|
||||
throws SignatureException {
|
||||
if (offset >= precomputedDigest.length) {
|
||||
offset = RAW_ECDSA_MAX + 1;
|
||||
return;
|
||||
}
|
||||
System.arraycopy(b, off, precomputedDigest, offset, len);
|
||||
offset += len;
|
||||
}
|
||||
|
||||
// Stores the precomputed message digest value.
|
||||
@Override
|
||||
protected void engineUpdate(ByteBuffer byteBuffer) {
|
||||
int len = byteBuffer.remaining();
|
||||
if (len <= 0) {
|
||||
return;
|
||||
}
|
||||
if (len >= precomputedDigest.length - offset) {
|
||||
offset = RAW_ECDSA_MAX + 1;
|
||||
return;
|
||||
}
|
||||
byteBuffer.get(precomputedDigest, offset, len);
|
||||
offset += len;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void resetDigest() {
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
// Returns the precomputed message digest value.
|
||||
@Override
|
||||
protected byte[] getDigestValue() throws SignatureException {
|
||||
if (offset > RAW_ECDSA_MAX) {
|
||||
throw new SignatureException("Message digest is too long");
|
||||
|
||||
}
|
||||
byte[] result = new byte[offset];
|
||||
System.arraycopy(precomputedDigest, 0, result, 0, offset);
|
||||
offset = 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// Nested class for NONEwithECDSA signatures
|
||||
public static final class Raw extends RawECDSA {
|
||||
public Raw() {
|
||||
super(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Nested class for NONEwithECDSAinP1363Format signatures
|
||||
public static final class RawinP1363Format extends RawECDSA {
|
||||
public RawinP1363Format() {
|
||||
super(true);
|
||||
}
|
||||
}
|
||||
|
||||
// Nested class for SHA1withECDSA signatures
|
||||
public static final class SHA1 extends ECDSASignature {
|
||||
public SHA1() {
|
||||
super("SHA1");
|
||||
}
|
||||
}
|
||||
|
||||
// Nested class for SHA1withECDSAinP1363Format signatures
|
||||
public static final class SHA1inP1363Format extends ECDSASignature {
|
||||
public SHA1inP1363Format() {
|
||||
super("SHA1", true);
|
||||
}
|
||||
}
|
||||
|
||||
// Nested class for SHA224withECDSA signatures
|
||||
public static final class SHA224 extends ECDSASignature {
|
||||
public SHA224() {
|
||||
super("SHA-224");
|
||||
}
|
||||
}
|
||||
|
||||
// Nested class for SHA224withECDSAinP1363Format signatures
|
||||
public static final class SHA224inP1363Format extends ECDSASignature {
|
||||
public SHA224inP1363Format() {
|
||||
super("SHA-224", true);
|
||||
}
|
||||
}
|
||||
|
||||
// Nested class for SHA256withECDSA signatures
|
||||
public static final class SHA256 extends ECDSASignature {
|
||||
public SHA256() {
|
||||
super("SHA-256");
|
||||
}
|
||||
}
|
||||
|
||||
// Nested class for SHA256withECDSAinP1363Format signatures
|
||||
public static final class SHA256inP1363Format extends ECDSASignature {
|
||||
public SHA256inP1363Format() {
|
||||
super("SHA-256", true);
|
||||
}
|
||||
}
|
||||
|
||||
// Nested class for SHA384withECDSA signatures
|
||||
public static final class SHA384 extends ECDSASignature {
|
||||
public SHA384() {
|
||||
super("SHA-384");
|
||||
}
|
||||
}
|
||||
|
||||
// Nested class for SHA384withECDSAinP1363Format signatures
|
||||
public static final class SHA384inP1363Format extends ECDSASignature {
|
||||
public SHA384inP1363Format() {
|
||||
super("SHA-384", true);
|
||||
}
|
||||
}
|
||||
|
||||
// Nested class for SHA512withECDSA signatures
|
||||
public static final class SHA512 extends ECDSASignature {
|
||||
public SHA512() {
|
||||
super("SHA-512");
|
||||
}
|
||||
}
|
||||
|
||||
// Nested class for SHA512withECDSAinP1363Format signatures
|
||||
public static final class SHA512inP1363Format extends ECDSASignature {
|
||||
public SHA512inP1363Format() {
|
||||
super("SHA-512", true);
|
||||
}
|
||||
}
|
||||
|
||||
// Nested class for SHA3_224withECDSA signatures
|
||||
public static final class SHA3_224 extends ECDSASignature {
|
||||
public SHA3_224() {
|
||||
super("SHA3-224");
|
||||
}
|
||||
}
|
||||
|
||||
// Nested class for SHA3_224withECDSAinP1363Format signatures
|
||||
public static final class SHA3_224inP1363Format extends ECDSASignature {
|
||||
public SHA3_224inP1363Format() {
|
||||
super("SHA3-224", true);
|
||||
}
|
||||
}
|
||||
|
||||
// Nested class for SHA3_256withECDSA signatures
|
||||
public static final class SHA3_256 extends ECDSASignature {
|
||||
public SHA3_256() {
|
||||
super("SHA3-256");
|
||||
}
|
||||
}
|
||||
|
||||
// Nested class for SHA3_256withECDSAinP1363Format signatures
|
||||
public static final class SHA3_256inP1363Format extends ECDSASignature {
|
||||
public SHA3_256inP1363Format() {
|
||||
super("SHA3-256", true);
|
||||
}
|
||||
}
|
||||
|
||||
// Nested class for SHA3_384withECDSA signatures
|
||||
public static final class SHA3_384 extends ECDSASignature {
|
||||
public SHA3_384() {
|
||||
super("SHA3-384");
|
||||
}
|
||||
}
|
||||
|
||||
// Nested class for SHA3_384withECDSAinP1363Format signatures
|
||||
public static final class SHA3_384inP1363Format extends ECDSASignature {
|
||||
public SHA3_384inP1363Format() {
|
||||
super("SHA3-384", true);
|
||||
}
|
||||
}
|
||||
|
||||
// Nested class for SHA3_512withECDSA signatures
|
||||
public static final class SHA3_512 extends ECDSASignature {
|
||||
public SHA3_512() {
|
||||
super("SHA3-512");
|
||||
}
|
||||
}
|
||||
|
||||
// Nested class for SHA3_512withECDSAinP1363Format signatures
|
||||
public static final class SHA3_512inP1363Format extends ECDSASignature {
|
||||
public SHA3_512inP1363Format() {
|
||||
super("SHA3-512", true);
|
||||
}
|
||||
}
|
||||
|
||||
// initialize for verification. See JCA doc
|
||||
@Override
|
||||
protected void engineInitVerify(PublicKey publicKey)
|
||||
throws InvalidKeyException {
|
||||
ECPublicKey key = (ECPublicKey) ECKeyFactory.toECKey(publicKey);
|
||||
// Should check that the supplied key is appropriate for signature
|
||||
// algorithm (e.g. P-256 for SHA256withECDSA)
|
||||
this.publicKey = key;
|
||||
this.privateKey = null;
|
||||
resetDigest();
|
||||
}
|
||||
|
||||
// initialize for signing. See JCA doc
|
||||
@Override
|
||||
protected void engineInitSign(PrivateKey privateKey)
|
||||
throws InvalidKeyException {
|
||||
engineInitSign(privateKey, null);
|
||||
}
|
||||
|
||||
// initialize for signing. See JCA doc
|
||||
@Override
|
||||
protected void engineInitSign(PrivateKey privateKey, SecureRandom random)
|
||||
throws InvalidKeyException {
|
||||
ECPrivateKey key = (ECPrivateKey) ECKeyFactory.toECKey(privateKey);
|
||||
ECUtil.checkPrivateKey(key);
|
||||
// Should check that the supplied key is appropriate for signature
|
||||
// algorithm (e.g. P-256 for SHA256withECDSA)
|
||||
this.privateKey = key;
|
||||
this.publicKey = null;
|
||||
this.random = random;
|
||||
resetDigest();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the message digest if needed.
|
||||
*/
|
||||
protected void resetDigest() {
|
||||
if (needsReset) {
|
||||
if (messageDigest != null) {
|
||||
messageDigest.reset();
|
||||
}
|
||||
needsReset = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the message digest value.
|
||||
*/
|
||||
protected byte[] getDigestValue() throws SignatureException {
|
||||
needsReset = false;
|
||||
return messageDigest.digest();
|
||||
}
|
||||
|
||||
// update the signature with the plaintext data. See JCA doc
|
||||
@Override
|
||||
protected void engineUpdate(byte b) throws SignatureException {
|
||||
messageDigest.update(b);
|
||||
needsReset = true;
|
||||
}
|
||||
|
||||
// update the signature with the plaintext data. See JCA doc
|
||||
@Override
|
||||
protected void engineUpdate(byte[] b, int off, int len)
|
||||
throws SignatureException {
|
||||
messageDigest.update(b, off, len);
|
||||
needsReset = true;
|
||||
}
|
||||
|
||||
// update the signature with the plaintext data. See JCA doc
|
||||
@Override
|
||||
protected void engineUpdate(ByteBuffer byteBuffer) {
|
||||
int len = byteBuffer.remaining();
|
||||
if (len <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
messageDigest.update(byteBuffer);
|
||||
needsReset = true;
|
||||
}
|
||||
|
||||
private byte[] signDigestImpl(ECDSAOperations ops, int seedBits,
|
||||
byte[] digest, ECPrivateKey priv, SecureRandom random)
|
||||
throws SignatureException {
|
||||
|
||||
byte[] seedBytes = new byte[(seedBits + 7) / 8];
|
||||
byte[] s = priv instanceof ECPrivateKeyImpl
|
||||
? ((ECPrivateKeyImpl)priv).getArrayS()
|
||||
: ECUtil.sArray(priv.getS(), priv.getParams());
|
||||
|
||||
// Attempt to create the signature in a loop that uses new random input
|
||||
// each time. The chance of failure is very small assuming the
|
||||
// implementation derives the nonce using extra bits
|
||||
int numAttempts = 128;
|
||||
for (int i = 0; i < numAttempts; i++) {
|
||||
random.nextBytes(seedBytes);
|
||||
ECDSAOperations.Seed seed = new ECDSAOperations.Seed(seedBytes);
|
||||
try {
|
||||
return ops.signDigest(s, digest, seed);
|
||||
} catch (IntermediateValueException ex) {
|
||||
// try again in the next iteration
|
||||
}
|
||||
}
|
||||
|
||||
throw new SignatureException("Unable to produce signature after "
|
||||
+ numAttempts + " attempts");
|
||||
}
|
||||
|
||||
|
||||
// sign the data and return the signature. See JCA doc
|
||||
@Override
|
||||
protected byte[] engineSign() throws SignatureException {
|
||||
|
||||
if (random == null) {
|
||||
random = JCAUtil.getSecureRandom();
|
||||
}
|
||||
|
||||
byte[] digest = getDigestValue();
|
||||
ECParameterSpec params = privateKey.getParams();
|
||||
|
||||
// seed is the key size + 64 bits
|
||||
int seedBits = params.getOrder().bitLength() + 64;
|
||||
Optional<ECDSAOperations> opsOpt =
|
||||
ECDSAOperations.forParameters(params);
|
||||
if (opsOpt.isEmpty()) {
|
||||
throw new SignatureException("Curve not supported: " + params);
|
||||
}
|
||||
byte[] sig = signDigestImpl(opsOpt.get(), seedBits, digest, privateKey,
|
||||
random);
|
||||
|
||||
if (p1363Format) {
|
||||
return sig;
|
||||
} else {
|
||||
return ECUtil.encodeSignature(sig);
|
||||
}
|
||||
}
|
||||
|
||||
// verify the data and return the result. See JCA doc
|
||||
@Override
|
||||
protected boolean engineVerify(byte[] signature) throws SignatureException {
|
||||
|
||||
ECPoint w = publicKey.getW();
|
||||
ECParameterSpec params = publicKey.getParams();
|
||||
|
||||
// Partial public key validation
|
||||
try {
|
||||
ECUtil.validatePublicKey(w, params);
|
||||
} catch (InvalidKeyException e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ECDSAOperations ops = ECDSAOperations.forParameters(params)
|
||||
.orElseThrow(() -> new SignatureException("Curve not supported: " + params));
|
||||
|
||||
// Full public key validation, only necessary when h != 1.
|
||||
if (params.getCofactor() != 1) {
|
||||
if (!ops.getEcOperations().checkOrder(w)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
byte[] sig;
|
||||
if (p1363Format) {
|
||||
sig = signature;
|
||||
} else {
|
||||
sig = ECUtil.decodeSignature(signature);
|
||||
}
|
||||
return ops.verifySignedDigest(getDigestValue(), sig, w);
|
||||
}
|
||||
|
||||
// set parameter, not supported. See JCA doc
|
||||
@Override
|
||||
@Deprecated
|
||||
protected void engineSetParameter(String param, Object value)
|
||||
throws InvalidParameterException {
|
||||
throw new UnsupportedOperationException("setParameter() not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void engineSetParameter(AlgorithmParameterSpec params)
|
||||
throws InvalidAlgorithmParameterException {
|
||||
// Interop: some certificates include parameters in an ECDSA
|
||||
// algorithm identifier. We only accept one matching the key.
|
||||
if (params == null) {
|
||||
return;
|
||||
}
|
||||
if (!(params instanceof ECParameterSpec ecparams)) {
|
||||
throw new InvalidAlgorithmParameterException(
|
||||
"Parameters must be of type ECParameterSpec");
|
||||
}
|
||||
ECKey key = (this.privateKey == null? this.publicKey : this.privateKey);
|
||||
if ((key != null) && !ECUtil.equals(ecparams, key.getParams())) {
|
||||
throw new InvalidAlgorithmParameterException
|
||||
("Signature params does not match key params");
|
||||
}
|
||||
}
|
||||
|
||||
// get parameter, not supported. See JCA doc
|
||||
@Override
|
||||
@Deprecated
|
||||
protected Object engineGetParameter(String param)
|
||||
throws InvalidParameterException {
|
||||
throw new UnsupportedOperationException("getParameter() not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AlgorithmParameters engineGetParameters() {
|
||||
// Always return null even if setParameter is called before.
|
||||
// According to RFC 3279 2.2.3 and RFC 5758 3.2, no parameters is
|
||||
// defined for ECDSA AlgorithmIdentifiers.
|
||||
return null;
|
||||
}
|
||||
}
|
304
src/java.base/share/classes/sun/security/ec/ECKeyFactory.java
Normal file
304
src/java.base/share/classes/sun/security/ec/ECKeyFactory.java
Normal file
|
@ -0,0 +1,304 @@
|
|||
/*
|
||||
* Copyright (c) 2006, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ec;
|
||||
|
||||
import java.security.*;
|
||||
import java.security.interfaces.*;
|
||||
import java.security.spec.*;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* KeyFactory for EC keys. Keys must be instances of PublicKey or PrivateKey
|
||||
* and getAlgorithm() must return "EC". For such keys, it supports conversion
|
||||
* between the following:
|
||||
*
|
||||
* For public keys:
|
||||
* . PublicKey with an X.509 encoding
|
||||
* . ECPublicKey
|
||||
* . ECPublicKeySpec
|
||||
* . X509EncodedKeySpec
|
||||
*
|
||||
* For private keys:
|
||||
* . PrivateKey with a PKCS#8 encoding
|
||||
* . ECPrivateKey
|
||||
* . ECPrivateKeySpec
|
||||
* . PKCS8EncodedKeySpec
|
||||
*
|
||||
* @since 1.6
|
||||
* @author Andreas Sterbenz
|
||||
*/
|
||||
public final class ECKeyFactory extends KeyFactorySpi {
|
||||
|
||||
// Used by translateKey()
|
||||
private static KeyFactory instance;
|
||||
|
||||
private static KeyFactory getInstance() {
|
||||
if (instance == null) {
|
||||
try {
|
||||
instance = KeyFactory.getInstance("EC", "SunEC");
|
||||
} catch (NoSuchProviderException | NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
public ECKeyFactory() {
|
||||
// empty
|
||||
}
|
||||
|
||||
/**
|
||||
* Static method to convert Key into a usable instance of
|
||||
* ECPublicKey or ECPrivateKey. Check the key and convert it
|
||||
* to a Sun key if necessary. If the key is not an EC key
|
||||
* or cannot be used, throw an InvalidKeyException.
|
||||
*
|
||||
* The difference between this method and engineTranslateKey() is that
|
||||
* we do not convert keys of other providers that are already an
|
||||
* instance of ECPublicKey or ECPrivateKey.
|
||||
*
|
||||
* To be used by future Java ECDSA and ECDH implementations.
|
||||
*/
|
||||
public static ECKey toECKey(Key key) throws InvalidKeyException {
|
||||
if (key instanceof ECKey) {
|
||||
ECKey ecKey = (ECKey)key;
|
||||
checkKey(ecKey);
|
||||
return ecKey;
|
||||
} else {
|
||||
/*
|
||||
* We don't call the engineTranslateKey method directly
|
||||
* because KeyFactory.translateKey adds code to loop through
|
||||
* all key factories.
|
||||
*/
|
||||
return (ECKey)getInstance().translateKey(key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the given EC key is valid.
|
||||
*/
|
||||
private static void checkKey(ECKey key) throws InvalidKeyException {
|
||||
// check for subinterfaces, omit additional checks for our keys
|
||||
if (key instanceof ECPublicKey) {
|
||||
if (key instanceof ECPublicKeyImpl) {
|
||||
return;
|
||||
}
|
||||
} else if (key instanceof ECPrivateKey) {
|
||||
if (key instanceof ECPrivateKeyImpl) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
throw new InvalidKeyException("Neither a public nor a private key");
|
||||
}
|
||||
// ECKey does not extend Key, so we need to do a cast
|
||||
String keyAlg = ((Key)key).getAlgorithm();
|
||||
if (keyAlg.equals("EC") == false) {
|
||||
throw new InvalidKeyException("Not an EC key: " + keyAlg);
|
||||
}
|
||||
// XXX further sanity checks about whether this key uses supported
|
||||
// fields, point formats, etc. would go here
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate an EC key into a Sun EC key. If conversion is
|
||||
* not possible, throw an InvalidKeyException.
|
||||
* See also JCA doc.
|
||||
*/
|
||||
protected Key engineTranslateKey(Key key) throws InvalidKeyException {
|
||||
if (key == null) {
|
||||
throw new InvalidKeyException("Key must not be null");
|
||||
}
|
||||
String keyAlg = key.getAlgorithm();
|
||||
if (keyAlg.equals("EC") == false) {
|
||||
throw new InvalidKeyException("Not an EC key: " + keyAlg);
|
||||
}
|
||||
if (key instanceof PublicKey) {
|
||||
return implTranslatePublicKey((PublicKey)key);
|
||||
} else if (key instanceof PrivateKey) {
|
||||
return implTranslatePrivateKey((PrivateKey)key);
|
||||
} else {
|
||||
throw new InvalidKeyException("Neither a public nor a private key");
|
||||
}
|
||||
}
|
||||
|
||||
// see JCA doc
|
||||
protected PublicKey engineGeneratePublic(KeySpec keySpec)
|
||||
throws InvalidKeySpecException {
|
||||
try {
|
||||
return implGeneratePublic(keySpec);
|
||||
} catch (InvalidKeySpecException e) {
|
||||
throw e;
|
||||
} catch (GeneralSecurityException e) {
|
||||
throw new InvalidKeySpecException(e);
|
||||
}
|
||||
}
|
||||
|
||||
// see JCA doc
|
||||
protected PrivateKey engineGeneratePrivate(KeySpec keySpec)
|
||||
throws InvalidKeySpecException {
|
||||
try {
|
||||
return implGeneratePrivate(keySpec);
|
||||
} catch (InvalidKeySpecException e) {
|
||||
throw e;
|
||||
} catch (GeneralSecurityException e) {
|
||||
throw new InvalidKeySpecException(e);
|
||||
}
|
||||
}
|
||||
|
||||
// internal implementation of translateKey() for public keys. See JCA doc
|
||||
private PublicKey implTranslatePublicKey(PublicKey key)
|
||||
throws InvalidKeyException {
|
||||
if (key instanceof ECPublicKey) {
|
||||
if (key instanceof ECPublicKeyImpl) {
|
||||
return key;
|
||||
}
|
||||
ECPublicKey ecKey = (ECPublicKey)key;
|
||||
return new ECPublicKeyImpl(
|
||||
ecKey.getW(),
|
||||
ecKey.getParams()
|
||||
);
|
||||
} else if ("X.509".equals(key.getFormat())) {
|
||||
byte[] encoded = key.getEncoded();
|
||||
return new ECPublicKeyImpl(encoded);
|
||||
} else {
|
||||
throw new InvalidKeyException("Public keys must be instance "
|
||||
+ "of ECPublicKey or have X.509 encoding");
|
||||
}
|
||||
}
|
||||
|
||||
// internal implementation of translateKey() for private keys. See JCA doc
|
||||
private PrivateKey implTranslatePrivateKey(PrivateKey key)
|
||||
throws InvalidKeyException {
|
||||
if (key instanceof ECPrivateKey) {
|
||||
if (key instanceof ECPrivateKeyImpl) {
|
||||
return key;
|
||||
}
|
||||
ECPrivateKey ecKey = (ECPrivateKey)key;
|
||||
return new ECPrivateKeyImpl(
|
||||
ecKey.getS(),
|
||||
ecKey.getParams()
|
||||
);
|
||||
} else if ("PKCS#8".equals(key.getFormat())) {
|
||||
byte[] encoded = key.getEncoded();
|
||||
try {
|
||||
return new ECPrivateKeyImpl(encoded);
|
||||
} finally {
|
||||
Arrays.fill(encoded, (byte)0);
|
||||
}
|
||||
} else {
|
||||
throw new InvalidKeyException("Private keys must be instance "
|
||||
+ "of ECPrivateKey or have PKCS#8 encoding");
|
||||
}
|
||||
}
|
||||
|
||||
// internal implementation of generatePublic. See JCA doc
|
||||
private PublicKey implGeneratePublic(KeySpec keySpec)
|
||||
throws GeneralSecurityException {
|
||||
if (keySpec instanceof X509EncodedKeySpec) {
|
||||
X509EncodedKeySpec x509Spec = (X509EncodedKeySpec)keySpec;
|
||||
return new ECPublicKeyImpl(x509Spec.getEncoded());
|
||||
} else if (keySpec instanceof ECPublicKeySpec) {
|
||||
ECPublicKeySpec ecSpec = (ECPublicKeySpec)keySpec;
|
||||
return new ECPublicKeyImpl(
|
||||
ecSpec.getW(),
|
||||
ecSpec.getParams()
|
||||
);
|
||||
} else {
|
||||
throw new InvalidKeySpecException("Only ECPublicKeySpec "
|
||||
+ "and X509EncodedKeySpec supported for EC public keys");
|
||||
}
|
||||
}
|
||||
|
||||
// internal implementation of generatePrivate. See JCA doc
|
||||
private PrivateKey implGeneratePrivate(KeySpec keySpec)
|
||||
throws GeneralSecurityException {
|
||||
if (keySpec instanceof PKCS8EncodedKeySpec) {
|
||||
PKCS8EncodedKeySpec pkcsSpec = (PKCS8EncodedKeySpec)keySpec;
|
||||
byte[] encoded = pkcsSpec.getEncoded();
|
||||
try {
|
||||
return new ECPrivateKeyImpl(encoded);
|
||||
} finally {
|
||||
Arrays.fill(encoded, (byte) 0);
|
||||
}
|
||||
} else if (keySpec instanceof ECPrivateKeySpec) {
|
||||
ECPrivateKeySpec ecSpec = (ECPrivateKeySpec)keySpec;
|
||||
return new ECPrivateKeyImpl(ecSpec.getS(), ecSpec.getParams());
|
||||
} else {
|
||||
throw new InvalidKeySpecException("Only ECPrivateKeySpec "
|
||||
+ "and PKCS8EncodedKeySpec supported for EC private keys");
|
||||
}
|
||||
}
|
||||
|
||||
protected <T extends KeySpec> T engineGetKeySpec(Key key, Class<T> keySpec)
|
||||
throws InvalidKeySpecException {
|
||||
try {
|
||||
// convert key to one of our keys
|
||||
// this also verifies that the key is a valid EC key and ensures
|
||||
// that the encoding is X.509/PKCS#8 for public/private keys
|
||||
key = engineTranslateKey(key);
|
||||
} catch (InvalidKeyException e) {
|
||||
throw new InvalidKeySpecException(e);
|
||||
}
|
||||
if (key instanceof ECPublicKey) {
|
||||
ECPublicKey ecKey = (ECPublicKey)key;
|
||||
if (keySpec.isAssignableFrom(ECPublicKeySpec.class)) {
|
||||
return keySpec.cast(new ECPublicKeySpec(
|
||||
ecKey.getW(),
|
||||
ecKey.getParams()
|
||||
));
|
||||
} else if (keySpec.isAssignableFrom(X509EncodedKeySpec.class)) {
|
||||
return keySpec.cast(new X509EncodedKeySpec(key.getEncoded()));
|
||||
} else {
|
||||
throw new InvalidKeySpecException
|
||||
("KeySpec must be ECPublicKeySpec or "
|
||||
+ "X509EncodedKeySpec for EC public keys");
|
||||
}
|
||||
} else if (key instanceof ECPrivateKey) {
|
||||
if (keySpec.isAssignableFrom(PKCS8EncodedKeySpec.class)) {
|
||||
byte[] encoded = key.getEncoded();
|
||||
try {
|
||||
return keySpec.cast(new PKCS8EncodedKeySpec(encoded));
|
||||
} finally {
|
||||
Arrays.fill(encoded, (byte)0);
|
||||
}
|
||||
} else if (keySpec.isAssignableFrom(ECPrivateKeySpec.class)) {
|
||||
ECPrivateKey ecKey = (ECPrivateKey)key;
|
||||
return keySpec.cast(new ECPrivateKeySpec(
|
||||
ecKey.getS(),
|
||||
ecKey.getParams()
|
||||
));
|
||||
} else {
|
||||
throw new InvalidKeySpecException
|
||||
("KeySpec must be ECPrivateKeySpec or "
|
||||
+ "PKCS8EncodedKeySpec for EC private keys");
|
||||
}
|
||||
} else {
|
||||
// should not occur, caught in engineTranslateKey()
|
||||
throw new InvalidKeySpecException("Neither public nor private key");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,213 @@
|
|||
/*
|
||||
* Copyright (c) 2009, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ec;
|
||||
|
||||
import java.security.*;
|
||||
import java.security.spec.AlgorithmParameterSpec;
|
||||
import java.security.spec.ECGenParameterSpec;
|
||||
import java.security.spec.ECParameterSpec;
|
||||
import java.security.spec.ECPoint;
|
||||
import java.security.spec.InvalidParameterSpecException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
|
||||
import sun.security.jca.JCAUtil;
|
||||
import sun.security.util.ECUtil;
|
||||
import sun.security.util.math.*;
|
||||
import sun.security.ec.point.*;
|
||||
import static sun.security.util.SecurityProviderConstants.DEF_EC_KEY_SIZE;
|
||||
import static sun.security.ec.ECOperations.IntermediateValueException;
|
||||
|
||||
/**
|
||||
* EC keypair generator.
|
||||
* Standard algorithm, minimum key length is 112 bits, maximum is 571 bits.
|
||||
*
|
||||
* @since 1.7
|
||||
*/
|
||||
public final class ECKeyPairGenerator extends KeyPairGeneratorSpi {
|
||||
|
||||
private static final int KEY_SIZE_MIN = 112; // min bits (see ecc_impl.h)
|
||||
private static final int KEY_SIZE_MAX = 571; // max bits (see ecc_impl.h)
|
||||
|
||||
// used to seed the keypair generator
|
||||
private SecureRandom random;
|
||||
|
||||
// size of the key to generate, KEY_SIZE_MIN <= keySize <= KEY_SIZE_MAX
|
||||
private int keySize;
|
||||
|
||||
// parameters specified via init, if any
|
||||
private AlgorithmParameterSpec params = null;
|
||||
|
||||
/**
|
||||
* Constructs a new ECKeyPairGenerator.
|
||||
*/
|
||||
public ECKeyPairGenerator() {
|
||||
// initialize to default in case the app does not call initialize()
|
||||
initialize(DEF_EC_KEY_SIZE, null);
|
||||
}
|
||||
|
||||
// initialize the generator. See JCA doc
|
||||
@Override
|
||||
public void initialize(int keySize, SecureRandom random) {
|
||||
|
||||
checkKeySize(keySize);
|
||||
this.params = ECUtil.getECParameterSpec(null, keySize);
|
||||
if (params == null) {
|
||||
throw new InvalidParameterException(
|
||||
"No EC parameters available for key size " + keySize + " bits");
|
||||
}
|
||||
this.random = random;
|
||||
}
|
||||
|
||||
// second initialize method. See JCA doc
|
||||
@Override
|
||||
public void initialize(AlgorithmParameterSpec params, SecureRandom random)
|
||||
throws InvalidAlgorithmParameterException {
|
||||
|
||||
ECParameterSpec ecSpec = null;
|
||||
|
||||
if (params instanceof ECParameterSpec) {
|
||||
ECParameterSpec ecParams = (ECParameterSpec) params;
|
||||
ecSpec = ECUtil.getECParameterSpec(null, ecParams);
|
||||
if (ecSpec == null) {
|
||||
throw new InvalidAlgorithmParameterException(
|
||||
"Curve not supported: " + params);
|
||||
}
|
||||
} else if (params instanceof ECGenParameterSpec) {
|
||||
String name = ((ECGenParameterSpec) params).getName();
|
||||
ecSpec = ECUtil.getECParameterSpec(null, name);
|
||||
if (ecSpec == null) {
|
||||
throw new InvalidAlgorithmParameterException(
|
||||
"Unknown curve name: " + name);
|
||||
}
|
||||
} else {
|
||||
throw new InvalidAlgorithmParameterException(
|
||||
"ECParameterSpec or ECGenParameterSpec required for EC");
|
||||
}
|
||||
|
||||
// Not all known curves are supported by the native implementation
|
||||
ensureCurveIsSupported(ecSpec);
|
||||
this.params = ecSpec;
|
||||
|
||||
this.keySize = ecSpec.getCurve().getField().getFieldSize();
|
||||
this.random = random;
|
||||
}
|
||||
|
||||
private static void ensureCurveIsSupported(ECParameterSpec ecSpec)
|
||||
throws InvalidAlgorithmParameterException {
|
||||
|
||||
// Check if ecSpec is a valid curve
|
||||
AlgorithmParameters ecParams = ECUtil.getECParameters(null);
|
||||
try {
|
||||
ecParams.init(ecSpec);
|
||||
} catch (InvalidParameterSpecException ex) {
|
||||
throw new InvalidAlgorithmParameterException(
|
||||
"Curve not supported: " + ecSpec.toString());
|
||||
}
|
||||
|
||||
// Check if the java implementation supports this curve
|
||||
if (ECOperations.forParameters(ecSpec).isEmpty()) {
|
||||
throw new InvalidAlgorithmParameterException(
|
||||
"Curve not supported: " + ecSpec.toString());
|
||||
}
|
||||
}
|
||||
|
||||
// generate the keypair. See JCA doc
|
||||
@Override
|
||||
public KeyPair generateKeyPair() {
|
||||
|
||||
if (random == null) {
|
||||
random = JCAUtil.getSecureRandom();
|
||||
}
|
||||
|
||||
try {
|
||||
Optional<KeyPair> kp = generateKeyPairImpl(random);
|
||||
if (kp.isPresent()) {
|
||||
return kp.get();
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
throw new ProviderException(ex);
|
||||
}
|
||||
throw new ProviderException("Curve not supported: " +
|
||||
params.toString());
|
||||
}
|
||||
|
||||
private byte[] generatePrivateScalar(SecureRandom random,
|
||||
ECOperations ecOps, int seedSize) {
|
||||
// Attempt to create the private scalar in a loop that uses new random
|
||||
// input each time. The chance of failure is very small assuming the
|
||||
// implementation derives the nonce using extra bits
|
||||
int numAttempts = 128;
|
||||
byte[] seedArr = new byte[seedSize];
|
||||
for (int i = 0; i < numAttempts; i++) {
|
||||
random.nextBytes(seedArr);
|
||||
try {
|
||||
return ecOps.seedToScalar(seedArr);
|
||||
} catch (IntermediateValueException ex) {
|
||||
// try again in the next iteration
|
||||
}
|
||||
}
|
||||
|
||||
throw new ProviderException("Unable to produce private key after "
|
||||
+ numAttempts + " attempts");
|
||||
}
|
||||
|
||||
private Optional<KeyPair> generateKeyPairImpl(SecureRandom random)
|
||||
throws InvalidKeyException {
|
||||
|
||||
ECParameterSpec ecParams = (ECParameterSpec) params;
|
||||
|
||||
Optional<ECOperations> opsOpt = ECOperations.forParameters(ecParams);
|
||||
if (opsOpt.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
ECOperations ops = opsOpt.get();
|
||||
IntegerFieldModuloP field = ops.getField();
|
||||
int numBits = ecParams.getOrder().bitLength();
|
||||
int seedBits = numBits + 64;
|
||||
int seedSize = (seedBits + 7) / 8;
|
||||
byte[] privArr = generatePrivateScalar(random, ops, seedSize);
|
||||
|
||||
ECPrivateKeyImpl privateKey = new ECPrivateKeyImpl(privArr, ecParams);
|
||||
Arrays.fill(privArr, (byte)0);
|
||||
|
||||
PublicKey publicKey = privateKey.calculatePublicKey();
|
||||
|
||||
return Optional.of(new KeyPair(publicKey, privateKey));
|
||||
}
|
||||
|
||||
private void checkKeySize(int keySize) throws InvalidParameterException {
|
||||
if (keySize < KEY_SIZE_MIN) {
|
||||
throw new InvalidParameterException
|
||||
("Key size must be at least " + KEY_SIZE_MIN + " bits");
|
||||
}
|
||||
if (keySize > KEY_SIZE_MAX) {
|
||||
throw new InvalidParameterException
|
||||
("Key size must be at most " + KEY_SIZE_MAX + " bits");
|
||||
}
|
||||
this.keySize = keySize;
|
||||
}
|
||||
}
|
700
src/java.base/share/classes/sun/security/ec/ECOperations.java
Normal file
700
src/java.base/share/classes/sun/security/ec/ECOperations.java
Normal file
|
@ -0,0 +1,700 @@
|
|||
/*
|
||||
* Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ec;
|
||||
|
||||
import sun.security.ec.point.*;
|
||||
import sun.security.util.CurveDB;
|
||||
import sun.security.util.KnownOIDs;
|
||||
import sun.security.util.ArrayUtil;
|
||||
import sun.security.util.math.*;
|
||||
import sun.security.util.math.intpoly.*;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.ProviderException;
|
||||
import java.security.spec.ECFieldFp;
|
||||
import java.security.spec.ECParameterSpec;
|
||||
import java.security.spec.ECPoint;
|
||||
import java.security.spec.EllipticCurve;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
/*
|
||||
* Elliptic curve point arithmetic for prime-order curves where a=-3.
|
||||
* Formulas are derived from "Complete addition formulas for prime order
|
||||
* elliptic curves" by Renes, Costello, and Batina.
|
||||
*/
|
||||
|
||||
public class ECOperations {
|
||||
private static final ECOperations secp256r1Ops =
|
||||
new ECOperations(IntegerPolynomialP256.ONE.getElement(
|
||||
CurveDB.lookup(KnownOIDs.secp256r1.value()).getCurve().getB()),
|
||||
P256OrderField.ONE);
|
||||
|
||||
/*
|
||||
* An exception indicating a problem with an intermediate value produced
|
||||
* by some part of the computation. For example, the signing operation
|
||||
* will throw this exception to indicate that the r or s value is 0, and
|
||||
* that the signing operation should be tried again with a different nonce.
|
||||
*/
|
||||
static class IntermediateValueException extends Exception {
|
||||
private static final long serialVersionUID = 1;
|
||||
}
|
||||
|
||||
static final Map<BigInteger, IntegerFieldModuloP> fields = Map.of(
|
||||
IntegerPolynomialP256.MODULUS, IntegerPolynomialP256.ONE,
|
||||
IntegerPolynomialP384.MODULUS, IntegerPolynomialP384.ONE,
|
||||
IntegerPolynomialP521.MODULUS, IntegerPolynomialP521.ONE
|
||||
);
|
||||
|
||||
static final Map<BigInteger, IntegerFieldModuloP> orderFields = Map.of(
|
||||
P256OrderField.MODULUS, P256OrderField.ONE,
|
||||
P384OrderField.MODULUS, P384OrderField.ONE,
|
||||
P521OrderField.MODULUS, P521OrderField.ONE
|
||||
);
|
||||
|
||||
public static Optional<ECOperations> forParameters(ECParameterSpec params) {
|
||||
|
||||
EllipticCurve curve = params.getCurve();
|
||||
if (!(curve.getField() instanceof ECFieldFp primeField)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
BigInteger three = BigInteger.valueOf(3);
|
||||
if (!primeField.getP().subtract(curve.getA()).equals(three)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
IntegerFieldModuloP field = fields.get(primeField.getP());
|
||||
if (field == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
IntegerFieldModuloP orderField = orderFields.get(params.getOrder());
|
||||
if (orderField == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
ImmutableIntegerModuloP b = field.getElement(curve.getB());
|
||||
ECOperations ecOps = new ECOperations(b, orderField);
|
||||
return Optional.of(ecOps);
|
||||
}
|
||||
|
||||
final ImmutableIntegerModuloP b;
|
||||
final SmallValue one;
|
||||
final SmallValue two;
|
||||
final SmallValue three;
|
||||
final SmallValue four;
|
||||
final ProjectivePoint.Immutable neutral;
|
||||
private final IntegerFieldModuloP orderField;
|
||||
|
||||
public ECOperations(IntegerModuloP b, IntegerFieldModuloP orderField) {
|
||||
this.b = b.fixed();
|
||||
this.orderField = orderField;
|
||||
|
||||
this.one = b.getField().getSmallValue(1);
|
||||
this.two = b.getField().getSmallValue(2);
|
||||
this.three = b.getField().getSmallValue(3);
|
||||
this.four = b.getField().getSmallValue(4);
|
||||
|
||||
IntegerFieldModuloP field = b.getField();
|
||||
this.neutral = new ProjectivePoint.Immutable(field.get0(),
|
||||
field.get1(), field.get0());
|
||||
}
|
||||
|
||||
public IntegerFieldModuloP getField() {
|
||||
return b.getField();
|
||||
}
|
||||
public IntegerFieldModuloP getOrderField() {
|
||||
return orderField;
|
||||
}
|
||||
|
||||
protected ProjectivePoint.Immutable getNeutral() {
|
||||
return neutral;
|
||||
}
|
||||
|
||||
public boolean isNeutral(Point p) {
|
||||
ProjectivePoint<?> pp = (ProjectivePoint<?>) p;
|
||||
|
||||
IntegerModuloP z = pp.getZ();
|
||||
|
||||
IntegerFieldModuloP field = z.getField();
|
||||
int byteLength = (field.getSize().bitLength() + 7) / 8;
|
||||
byte[] zBytes = z.asByteArray(byteLength);
|
||||
return allZero(zBytes);
|
||||
}
|
||||
|
||||
byte[] seedToScalar(byte[] seedBytes)
|
||||
throws IntermediateValueException {
|
||||
|
||||
// Produce a nonce from the seed using FIPS 186-4,section B.5.1:
|
||||
// Per-Message Secret Number Generation Using Extra Random Bits
|
||||
// or
|
||||
// Produce a scalar from the seed using FIPS 186-4, section B.4.1:
|
||||
// Key Pair Generation Using Extra Random Bits
|
||||
|
||||
// To keep the implementation simple, sample in the range [0,n)
|
||||
// and throw IntermediateValueException in the (unlikely) event
|
||||
// that the result is 0.
|
||||
|
||||
// Get 64 extra bits and reduce into the nonce
|
||||
int seedBits = orderField.getSize().bitLength() + 64;
|
||||
if (seedBytes.length * 8 < seedBits) {
|
||||
throw new ProviderException("Incorrect seed length: " +
|
||||
seedBytes.length * 8 + " < " + seedBits);
|
||||
}
|
||||
|
||||
// input conversion only works on byte boundaries
|
||||
// clear high-order bits of last byte so they don't influence nonce
|
||||
int lastByteBits = seedBits % 8;
|
||||
if (lastByteBits != 0) {
|
||||
int lastByteIndex = seedBits / 8;
|
||||
byte mask = (byte) (0xFF >>> (8 - lastByteBits));
|
||||
seedBytes[lastByteIndex] &= mask;
|
||||
}
|
||||
|
||||
int seedLength = (seedBits + 7) / 8;
|
||||
IntegerModuloP scalarElem =
|
||||
orderField.getElement(seedBytes, 0, seedLength, (byte) 0);
|
||||
int scalarLength = (orderField.getSize().bitLength() + 7) / 8;
|
||||
byte[] scalarArr = new byte[scalarLength];
|
||||
scalarElem.asByteArray(scalarArr);
|
||||
if (ECOperations.allZero(scalarArr)) {
|
||||
throw new IntermediateValueException();
|
||||
}
|
||||
return scalarArr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compare all values in the array to 0 without branching on any value
|
||||
*
|
||||
*/
|
||||
public static boolean allZero(byte[] arr) {
|
||||
byte acc = 0;
|
||||
for (int i = 0; i < arr.length; i++) {
|
||||
acc |= arr[i];
|
||||
}
|
||||
return acc == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiply an affine point by a scalar and return the result as a mutable
|
||||
* point.
|
||||
*
|
||||
* @param affineP the point
|
||||
* @param s the scalar as a little-endian array
|
||||
* @return the product
|
||||
*/
|
||||
public MutablePoint multiply(AffinePoint affineP, byte[] s) {
|
||||
return PointMultiplier.of(this, affineP).pointMultiply(s);
|
||||
}
|
||||
|
||||
public MutablePoint multiply(ECPoint ecPoint, byte[] s) {
|
||||
return PointMultiplier.of(this, ecPoint).pointMultiply(s);
|
||||
}
|
||||
|
||||
/*
|
||||
* Point double
|
||||
*/
|
||||
private void setDouble(ProjectivePoint.Mutable p, MutableIntegerModuloP t0,
|
||||
MutableIntegerModuloP t1, MutableIntegerModuloP t2,
|
||||
MutableIntegerModuloP t3, MutableIntegerModuloP t4) {
|
||||
|
||||
t0.setValue(p.getX()).setSquare();
|
||||
t1.setValue(p.getY()).setSquare();
|
||||
t2.setValue(p.getZ()).setSquare();
|
||||
t3.setValue(p.getX()).setProduct(p.getY());
|
||||
t4.setValue(p.getY()).setProduct(p.getZ());
|
||||
|
||||
t3.setSum(t3);
|
||||
p.getZ().setProduct(p.getX());
|
||||
|
||||
p.getZ().setProduct(two);
|
||||
|
||||
p.getY().setValue(t2).setProduct(b);
|
||||
p.getY().setDifference(p.getZ());
|
||||
|
||||
p.getY().setProduct(three);
|
||||
p.getX().setValue(t1).setDifference(p.getY());
|
||||
|
||||
p.getY().setSum(t1);
|
||||
p.getY().setProduct(p.getX());
|
||||
p.getX().setProduct(t3);
|
||||
|
||||
t2.setProduct(three);
|
||||
p.getZ().setProduct(b);
|
||||
|
||||
p.getZ().setDifference(t2);
|
||||
p.getZ().setDifference(t0);
|
||||
p.getZ().setProduct(three);
|
||||
t0.setProduct(three);
|
||||
|
||||
t0.setDifference(t2);
|
||||
t0.setProduct(p.getZ());
|
||||
p.getY().setSum(t0);
|
||||
|
||||
t4.setSum(t4);
|
||||
p.getZ().setProduct(t4);
|
||||
|
||||
p.getX().setDifference(p.getZ());
|
||||
p.getZ().setValue(t4).setProduct(t1);
|
||||
|
||||
p.getZ().setProduct(four);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Mixed point addition. This method constructs new temporaries each time
|
||||
* it is called. For better efficiency, the method that reuses temporaries
|
||||
* should be used if more than one sum will be computed.
|
||||
*/
|
||||
public void setSum(MutablePoint p, AffinePoint p2) {
|
||||
|
||||
IntegerModuloP zero = p.getField().get0();
|
||||
MutableIntegerModuloP t0 = zero.mutable();
|
||||
MutableIntegerModuloP t1 = zero.mutable();
|
||||
MutableIntegerModuloP t2 = zero.mutable();
|
||||
MutableIntegerModuloP t3 = zero.mutable();
|
||||
MutableIntegerModuloP t4 = zero.mutable();
|
||||
setSum((ProjectivePoint.Mutable) p, p2, t0, t1, t2, t3, t4);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Mixed point addition
|
||||
*/
|
||||
private void setSum(ProjectivePoint.Mutable p, AffinePoint p2,
|
||||
MutableIntegerModuloP t0, MutableIntegerModuloP t1,
|
||||
MutableIntegerModuloP t2, MutableIntegerModuloP t3,
|
||||
MutableIntegerModuloP t4) {
|
||||
|
||||
t0.setValue(p.getX()).setProduct(p2.getX());
|
||||
t1.setValue(p.getY()).setProduct(p2.getY());
|
||||
t3.setValue(p2.getX()).setSum(p2.getY());
|
||||
t4.setValue(p.getX()).setSum(p.getY());
|
||||
t3.setProduct(t4);
|
||||
t4.setValue(t0).setSum(t1);
|
||||
|
||||
t3.setDifference(t4);
|
||||
t4.setValue(p2.getY()).setProduct(p.getZ());
|
||||
t4.setSum(p.getY());
|
||||
|
||||
p.getY().setValue(p2.getX()).setProduct(p.getZ());
|
||||
p.getY().setSum(p.getX());
|
||||
t2.setValue(p.getZ());
|
||||
p.getZ().setProduct(b);
|
||||
|
||||
p.getX().setValue(p.getY()).setDifference(p.getZ());
|
||||
p.getX().setProduct(three);
|
||||
|
||||
p.getZ().setValue(t1).setDifference(p.getX());
|
||||
p.getX().setSum(t1);
|
||||
p.getY().setProduct(b);
|
||||
|
||||
t2.setProduct(three);
|
||||
p.getY().setDifference(t2);
|
||||
|
||||
p.getY().setDifference(t0);
|
||||
p.getY().setProduct(three);
|
||||
|
||||
t0.setProduct(three);
|
||||
t0.setDifference(t2);
|
||||
|
||||
t1.setValue(t4).setProduct(p.getY());
|
||||
t2.setValue(t0).setProduct(p.getY());
|
||||
p.getY().setValue(p.getX()).setProduct(p.getZ());
|
||||
|
||||
p.getY().setSum(t2);
|
||||
p.getX().setProduct(t3);
|
||||
p.getX().setDifference(t1);
|
||||
|
||||
p.getZ().setProduct(t4);
|
||||
t3.setProduct(t0);
|
||||
p.getZ().setSum(t3);
|
||||
}
|
||||
|
||||
/*
|
||||
* Projective point addition
|
||||
*/
|
||||
private void setSum(ProjectivePoint.Mutable p, ProjectivePoint.Mutable p2,
|
||||
MutableIntegerModuloP t0, MutableIntegerModuloP t1,
|
||||
MutableIntegerModuloP t2, MutableIntegerModuloP t3,
|
||||
MutableIntegerModuloP t4) {
|
||||
|
||||
t0.setValue(p.getX()).setProduct(p2.getX());
|
||||
t1.setValue(p.getY()).setProduct(p2.getY());
|
||||
t2.setValue(p.getZ()).setProduct(p2.getZ());
|
||||
|
||||
t3.setValue(p.getX()).setSum(p.getY());
|
||||
t4.setValue(p2.getX()).setSum(p2.getY());
|
||||
t3.setProduct(t4);
|
||||
|
||||
t4.setValue(t0).setSum(t1);
|
||||
t3.setDifference(t4);
|
||||
t4.setValue(p.getY()).setSum(p.getZ());
|
||||
|
||||
p.getY().setValue(p2.getY()).setSum(p2.getZ());
|
||||
t4.setProduct(p.getY());
|
||||
p.getY().setValue(t1).setSum(t2);
|
||||
|
||||
t4.setDifference(p.getY());
|
||||
p.getX().setSum(p.getZ());
|
||||
p.getY().setValue(p2.getX()).setSum(p2.getZ());
|
||||
|
||||
p.getX().setProduct(p.getY());
|
||||
p.getY().setValue(t0).setSum(t2);
|
||||
p.getY().setAdditiveInverse().setSum(p.getX());
|
||||
|
||||
p.getZ().setValue(t2).setProduct(b);
|
||||
p.getX().setValue(p.getY()).setDifference(p.getZ());
|
||||
|
||||
p.getX().setProduct(three);
|
||||
|
||||
p.getZ().setValue(t1).setDifference(p.getX());
|
||||
p.getX().setSum(t1);
|
||||
|
||||
p.getY().setProduct(b);
|
||||
t2.setProduct(three);
|
||||
|
||||
p.getY().setDifference(t2);
|
||||
p.getY().setDifference(t0);
|
||||
p.getY().setProduct(three);
|
||||
|
||||
t0.setProduct(three);
|
||||
|
||||
t0.setDifference(t2);
|
||||
t1.setValue(t4).setProduct(p.getY());
|
||||
t2.setValue(t0).setProduct(p.getY());
|
||||
|
||||
p.getY().setValue(p.getX()).setProduct(p.getZ());
|
||||
p.getY().setSum(t2);
|
||||
p.getX().setProduct(t3);
|
||||
|
||||
p.getX().setDifference(t1);
|
||||
p.getZ().setProduct(t4);
|
||||
|
||||
t3.setProduct(t0);
|
||||
p.getZ().setSum(t3);
|
||||
}
|
||||
|
||||
// The extra step in the Full Public key validation as described in
|
||||
// NIST SP 800-186 Appendix D.1.1.2
|
||||
public boolean checkOrder(ECPoint point) {
|
||||
BigInteger x = point.getAffineX();
|
||||
BigInteger y = point.getAffineY();
|
||||
|
||||
// Verify that n Q = INFINITY. Output REJECT if verification fails.
|
||||
IntegerFieldModuloP field = this.getField();
|
||||
AffinePoint ap = new AffinePoint(field.getElement(x), field.getElement(y));
|
||||
byte[] scalar = this.orderField.getSize().toByteArray();
|
||||
ArrayUtil.reverse(scalar);
|
||||
return isNeutral(this.multiply(ap, scalar));
|
||||
}
|
||||
|
||||
sealed interface PointMultiplier {
|
||||
Map<ECPoint, PointMultiplier> multipliers = Map.of(
|
||||
Secp256R1GeneratorMultiplier.generator,
|
||||
Secp256R1GeneratorMultiplier.multiplier);
|
||||
|
||||
// Multiply the point by a scalar and return the result as a mutable
|
||||
// point. The multiplier point is specified by the implementation of
|
||||
// this interface, which could be a general EC point or EC generator
|
||||
// point.
|
||||
//
|
||||
// Multiply the ECPoint (that is specified in the implementation) by
|
||||
// a scalar and return the result as a ProjectivePoint.Mutable point.
|
||||
// The point to be multiplied can be a general EC point or the
|
||||
// generator of a named EC group. The scalar multiplier is an integer
|
||||
// in little endian byte array representation.
|
||||
ProjectivePoint.Mutable pointMultiply(byte[] scalar);
|
||||
|
||||
static PointMultiplier of(ECOperations ecOps, AffinePoint affPoint) {
|
||||
PointMultiplier multiplier = multipliers.get(affPoint.toECPoint());
|
||||
if (multiplier == null) {
|
||||
multiplier = new Default(ecOps, affPoint);
|
||||
}
|
||||
|
||||
return multiplier;
|
||||
}
|
||||
|
||||
static PointMultiplier of(ECOperations ecOps, ECPoint ecPoint) {
|
||||
PointMultiplier multiplier = multipliers.get(ecPoint);
|
||||
if (multiplier == null) {
|
||||
AffinePoint affPoint =
|
||||
AffinePoint.fromECPoint(ecPoint, ecOps.getField());
|
||||
multiplier = new Default(ecOps, affPoint);
|
||||
}
|
||||
|
||||
return multiplier;
|
||||
}
|
||||
|
||||
private static void lookup(
|
||||
ProjectivePoint.Immutable[] ips, int index,
|
||||
ProjectivePoint.Mutable result) {
|
||||
for (int i = 0; i < 16; i++) {
|
||||
int xor = index ^ i;
|
||||
int bit3 = (xor & 0x8) >>> 3;
|
||||
int bit2 = (xor & 0x4) >>> 2;
|
||||
int bit1 = (xor & 0x2) >>> 1;
|
||||
int bit0 = (xor & 0x1);
|
||||
int inverse = bit0 | bit1 | bit2 | bit3;
|
||||
int set = 1 - inverse;
|
||||
|
||||
ProjectivePoint.Immutable pi = ips[i];
|
||||
result.conditionalSet(pi, set);
|
||||
}
|
||||
}
|
||||
|
||||
final class Default implements PointMultiplier {
|
||||
private final AffinePoint affineP;
|
||||
private final ECOperations ecOps;
|
||||
|
||||
private Default(ECOperations ecOps, AffinePoint affineP) {
|
||||
this.ecOps = ecOps;
|
||||
this.affineP = affineP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProjectivePoint.Mutable pointMultiply(byte[] s) {
|
||||
// 4-bit windowed multiply with branchless lookup.
|
||||
// The mixed addition is faster, so it is used to construct
|
||||
// the array at the beginning of the operation.
|
||||
|
||||
IntegerFieldModuloP field = affineP.getX().getField();
|
||||
ImmutableIntegerModuloP zero = field.get0();
|
||||
// temporaries
|
||||
MutableIntegerModuloP t0 = zero.mutable();
|
||||
MutableIntegerModuloP t1 = zero.mutable();
|
||||
MutableIntegerModuloP t2 = zero.mutable();
|
||||
MutableIntegerModuloP t3 = zero.mutable();
|
||||
MutableIntegerModuloP t4 = zero.mutable();
|
||||
|
||||
ProjectivePoint.Mutable result =
|
||||
new ProjectivePoint.Mutable(field);
|
||||
result.getY().setValue(field.get1().mutable());
|
||||
|
||||
ProjectivePoint.Immutable[] pointMultiples =
|
||||
new ProjectivePoint.Immutable[16];
|
||||
// 0P is neutral---same as initial result value
|
||||
pointMultiples[0] = result.fixed();
|
||||
|
||||
ProjectivePoint.Mutable ps = new ProjectivePoint.Mutable(field);
|
||||
ps.setValue(affineP);
|
||||
// 1P = P
|
||||
pointMultiples[1] = ps.fixed();
|
||||
|
||||
// the rest are calculated using mixed point addition
|
||||
for (int i = 2; i < 16; i++) {
|
||||
ecOps.setSum(ps, affineP, t0, t1, t2, t3, t4);
|
||||
pointMultiples[i] = ps.fixed();
|
||||
}
|
||||
|
||||
ProjectivePoint.Mutable lookupResult = ps.mutable();
|
||||
|
||||
for (int i = s.length - 1; i >= 0; i--) {
|
||||
double4(result, t0, t1, t2, t3, t4);
|
||||
|
||||
int high = (0xFF & s[i]) >>> 4;
|
||||
lookup(pointMultiples, high, lookupResult);
|
||||
ecOps.setSum(result, lookupResult, t0, t1, t2, t3, t4);
|
||||
|
||||
double4(result, t0, t1, t2, t3, t4);
|
||||
|
||||
int low = 0xF & s[i];
|
||||
lookup(pointMultiples, low, lookupResult);
|
||||
ecOps.setSum(result, lookupResult, t0, t1, t2, t3, t4);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void double4(ProjectivePoint.Mutable p,
|
||||
MutableIntegerModuloP t0, MutableIntegerModuloP t1,
|
||||
MutableIntegerModuloP t2, MutableIntegerModuloP t3,
|
||||
MutableIntegerModuloP t4) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
ecOps.setDouble(p, t0, t1, t2, t3, t4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final class Secp256R1GeneratorMultiplier implements PointMultiplier {
|
||||
private static final ECPoint generator =
|
||||
CurveDB.P_256.getGenerator();
|
||||
private static final PointMultiplier multiplier =
|
||||
new Secp256R1GeneratorMultiplier();
|
||||
|
||||
private static final ImmutableIntegerModuloP zero =
|
||||
IntegerPolynomialP256.ONE.get0();
|
||||
private static final ImmutableIntegerModuloP one =
|
||||
IntegerPolynomialP256.ONE.get1();
|
||||
|
||||
@Override
|
||||
public ProjectivePoint.Mutable pointMultiply(byte[] s) {
|
||||
MutableIntegerModuloP t0 = zero.mutable();
|
||||
MutableIntegerModuloP t1 = zero.mutable();
|
||||
MutableIntegerModuloP t2 = zero.mutable();
|
||||
MutableIntegerModuloP t3 = zero.mutable();
|
||||
MutableIntegerModuloP t4 = zero.mutable();
|
||||
|
||||
ProjectivePoint.Mutable d = new ProjectivePoint.Mutable(
|
||||
zero.mutable(),
|
||||
one.mutable(),
|
||||
zero.mutable());
|
||||
ProjectivePoint.Mutable r = d.mutable();
|
||||
for (int i = 15; i >= 0; i--) {
|
||||
secp256r1Ops.setDouble(d, t0, t1, t2, t3, t4);
|
||||
for (int j = 3; j >= 0; j--) {
|
||||
int pos = i + j * 16;
|
||||
int index = (bit(s, pos + 192) << 3) |
|
||||
(bit(s, pos + 128) << 2) |
|
||||
(bit(s, pos + 64) << 1) |
|
||||
bit(s, pos);
|
||||
|
||||
lookup(P256.points[j], index, r);
|
||||
secp256r1Ops.setSum(d, r, t0, t1, t2, t3, t4);
|
||||
}
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
private static int bit(byte[] k, int i) {
|
||||
return (k[i >> 3] >> (i & 0x07)) & 0x01;
|
||||
}
|
||||
|
||||
// Lazy loading of the tables.
|
||||
private static final class P256 {
|
||||
// Pre-computed table to speed up the point multiplication.
|
||||
//
|
||||
// This is a 4x16 array of ProjectivePoint.Immutable elements.
|
||||
// The first row contains the following multiples of the
|
||||
// generator.
|
||||
//
|
||||
// index | point
|
||||
// --------+----------------
|
||||
// 0x0000 | 0G
|
||||
// 0x0001 | 1G
|
||||
// 0x0002 | (2^64)G
|
||||
// 0x0003 | (2^64 + 1)G
|
||||
// 0x0004 | 2^128G
|
||||
// 0x0005 | (2^128 + 1)G
|
||||
// 0x0006 | (2^128 + 2^64)G
|
||||
// 0x0007 | (2^128 + 2^64 + 1)G
|
||||
// 0x0008 | 2^192G
|
||||
// 0x0009 | (2^192 + 1)G
|
||||
// 0x000A | (2^192 + 2^64)G
|
||||
// 0x000B | (2^192 + 2^64 + 1)G
|
||||
// 0x000C | (2^192 + 2^128)G
|
||||
// 0x000D | (2^192 + 2^128 + 1)G
|
||||
// 0x000E | (2^192 + 2^128 + 2^64)G
|
||||
// 0x000F | (2^192 + 2^128 + 2^64 + 1)G
|
||||
//
|
||||
// For the other 3 rows, points[i][j] = 2^16 * (points[i-1][j].
|
||||
private static final ProjectivePoint.Immutable[][] points;
|
||||
|
||||
// Generate the pre-computed tables. This block may be
|
||||
// replaced with hard-coded tables in order to speed up
|
||||
// the class loading.
|
||||
static {
|
||||
points = new ProjectivePoint.Immutable[4][16];
|
||||
BigInteger[] factors = new BigInteger[] {
|
||||
BigInteger.ONE,
|
||||
BigInteger.TWO.pow(64),
|
||||
BigInteger.TWO.pow(128),
|
||||
BigInteger.TWO.pow(192)
|
||||
};
|
||||
|
||||
BigInteger[] base = new BigInteger[16];
|
||||
base[0] = BigInteger.ZERO;
|
||||
base[1] = BigInteger.ONE;
|
||||
base[2] = factors[1];
|
||||
for (int i = 3; i < 16; i++) {
|
||||
base[i] = BigInteger.ZERO;
|
||||
for (int k = 0; k < 4; k++) {
|
||||
if (((i >>> k) & 0x01) != 0) {
|
||||
base[i] = base[i].add(factors[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int d = 0; d < 4; d++) {
|
||||
for (int w = 0; w < 16; w++) {
|
||||
BigInteger bi = base[w];
|
||||
if (d != 0) {
|
||||
bi = bi.multiply(BigInteger.TWO.pow(d * 16));
|
||||
}
|
||||
if (w == 0) {
|
||||
points[d][0] = new ProjectivePoint.Immutable(
|
||||
zero.fixed(), one.fixed(), zero.fixed());
|
||||
} else {
|
||||
PointMultiplier multiplier = new Default(
|
||||
secp256r1Ops, AffinePoint.fromECPoint(
|
||||
generator, zero.getField()));
|
||||
byte[] s = bi.toByteArray();
|
||||
ArrayUtil.reverse(s);
|
||||
ProjectivePoint.Mutable m =
|
||||
multiplier.pointMultiply(s);
|
||||
points[d][w] = m.setValue(m.asAffine()).fixed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check that the tables are correctly generated.
|
||||
if (ECOperations.class.desiredAssertionStatus()) {
|
||||
verifyTables(base);
|
||||
}
|
||||
}
|
||||
|
||||
private static void verifyTables(BigInteger[] base) {
|
||||
for (int d = 0; d < 4; d++) {
|
||||
for (int w = 0; w < 16; w++) {
|
||||
BigInteger bi = base[w];
|
||||
if (d != 0) {
|
||||
bi = bi.multiply(BigInteger.TWO.pow(d * 16));
|
||||
}
|
||||
if (w != 0) {
|
||||
byte[] s = new byte[32];
|
||||
byte[] b = bi.toByteArray();
|
||||
ArrayUtil.reverse(b);
|
||||
System.arraycopy(b, 0, s, 0, b.length);
|
||||
|
||||
ProjectivePoint.Mutable m =
|
||||
multiplier.pointMultiply(s);
|
||||
ProjectivePoint.Immutable v =
|
||||
m.setValue(m.asAffine()).fixed();
|
||||
if (!v.getX().asBigInteger().equals(
|
||||
points[d][w].getX().asBigInteger()) ||
|
||||
!v.getY().asBigInteger().equals(
|
||||
points[d][w].getY().asBigInteger())) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,221 @@
|
|||
/*
|
||||
* Copyright (c) 2006, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ec;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
|
||||
import java.security.*;
|
||||
import java.security.interfaces.*;
|
||||
import java.security.spec.*;
|
||||
import java.util.Arrays;
|
||||
|
||||
import sun.security.ec.point.AffinePoint;
|
||||
import sun.security.ec.point.MutablePoint;
|
||||
import sun.security.util.*;
|
||||
import sun.security.x509.AlgorithmId;
|
||||
import sun.security.pkcs.PKCS8Key;
|
||||
|
||||
/**
|
||||
* Key implementation for EC private keys.
|
||||
*
|
||||
* ASN.1 syntax for EC private keys from SEC 1 v1.5 (draft):
|
||||
*
|
||||
* <pre>
|
||||
* EXPLICIT TAGS
|
||||
*
|
||||
* ECPrivateKey ::= SEQUENCE {
|
||||
* version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
|
||||
* privateKey OCTET STRING,
|
||||
* parameters [0] ECDomainParameters {{ SECGCurveNames }} OPTIONAL,
|
||||
* publicKey [1] BIT STRING OPTIONAL
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* We currently ignore the optional parameters and publicKey fields. We
|
||||
* require that the parameters are encoded as part of the AlgorithmIdentifier,
|
||||
* not in the private key structure.
|
||||
*
|
||||
* @since 1.6
|
||||
* @author Andreas Sterbenz
|
||||
*/
|
||||
public final class ECPrivateKeyImpl extends PKCS8Key implements ECPrivateKey {
|
||||
|
||||
private static final long serialVersionUID = 88695385615075129L;
|
||||
|
||||
private BigInteger s; // private value
|
||||
private byte[] arrayS; // private value as a little-endian array
|
||||
@SuppressWarnings("serial") // Type of field is not Serializable
|
||||
private ECParameterSpec params;
|
||||
|
||||
/**
|
||||
* Construct a key from its encoding. Called by the ECKeyFactory.
|
||||
*/
|
||||
ECPrivateKeyImpl(byte[] encoded) throws InvalidKeyException {
|
||||
super(encoded);
|
||||
parseKeyBits();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a key from its components. Used by the
|
||||
* KeyFactory.
|
||||
*/
|
||||
ECPrivateKeyImpl(BigInteger s, ECParameterSpec params)
|
||||
throws InvalidKeyException {
|
||||
this.s = s;
|
||||
this.params = params;
|
||||
makeEncoding(s);
|
||||
|
||||
}
|
||||
|
||||
ECPrivateKeyImpl(byte[] s, ECParameterSpec params)
|
||||
throws InvalidKeyException {
|
||||
this.arrayS = s.clone();
|
||||
this.params = params;
|
||||
makeEncoding(s);
|
||||
}
|
||||
|
||||
private void makeEncoding(byte[] s) throws InvalidKeyException {
|
||||
algid = new AlgorithmId
|
||||
(AlgorithmId.EC_oid, ECParameters.getAlgorithmParameters(params));
|
||||
DerOutputStream out = new DerOutputStream();
|
||||
out.putInteger(1); // version 1
|
||||
byte[] privBytes = s.clone();
|
||||
ArrayUtil.reverse(privBytes);
|
||||
out.putOctetString(privBytes);
|
||||
Arrays.fill(privBytes, (byte) 0);
|
||||
DerValue val = DerValue.wrap(DerValue.tag_Sequence, out);
|
||||
key = val.toByteArray();
|
||||
val.clear();
|
||||
}
|
||||
|
||||
private void makeEncoding(BigInteger s) throws InvalidKeyException {
|
||||
algid = new AlgorithmId(AlgorithmId.EC_oid,
|
||||
ECParameters.getAlgorithmParameters(params));
|
||||
byte[] sArr = s.toByteArray();
|
||||
// convert to fixed-length array
|
||||
int numOctets = (params.getOrder().bitLength() + 7) / 8;
|
||||
byte[] sOctets = new byte[numOctets];
|
||||
int inPos = Math.max(sArr.length - sOctets.length, 0);
|
||||
int outPos = Math.max(sOctets.length - sArr.length, 0);
|
||||
int length = Math.min(sArr.length, sOctets.length);
|
||||
System.arraycopy(sArr, inPos, sOctets, outPos, length);
|
||||
Arrays.fill(sArr, (byte) 0);
|
||||
|
||||
DerOutputStream out = new DerOutputStream();
|
||||
out.putInteger(1); // version 1
|
||||
out.putOctetString(sOctets);
|
||||
Arrays.fill(sOctets, (byte) 0);
|
||||
DerValue val = DerValue.wrap(DerValue.tag_Sequence, out);
|
||||
key = val.toByteArray();
|
||||
val.clear();
|
||||
}
|
||||
|
||||
// see JCA doc
|
||||
public String getAlgorithm() {
|
||||
return "EC";
|
||||
}
|
||||
|
||||
// see JCA doc
|
||||
public BigInteger getS() {
|
||||
if (s == null) {
|
||||
byte[] arrCopy = arrayS.clone();
|
||||
ArrayUtil.reverse(arrCopy);
|
||||
s = new BigInteger(1, arrCopy);
|
||||
Arrays.fill(arrCopy, (byte)0);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
private byte[] getArrayS0() {
|
||||
if (arrayS == null) {
|
||||
arrayS = ECUtil.sArray(getS(), params);
|
||||
}
|
||||
return arrayS;
|
||||
}
|
||||
|
||||
public byte[] getArrayS() {
|
||||
return getArrayS0().clone();
|
||||
}
|
||||
|
||||
// see JCA doc
|
||||
public ECParameterSpec getParams() {
|
||||
return params;
|
||||
}
|
||||
|
||||
private void parseKeyBits() throws InvalidKeyException {
|
||||
try {
|
||||
DerInputStream in = new DerInputStream(key);
|
||||
DerValue derValue = in.getDerValue();
|
||||
if (derValue.tag != DerValue.tag_Sequence) {
|
||||
throw new IOException("Not a SEQUENCE");
|
||||
}
|
||||
DerInputStream data = derValue.data;
|
||||
int version = data.getInteger();
|
||||
if (version != 1) {
|
||||
throw new IOException("Version must be 1");
|
||||
}
|
||||
byte[] privData = data.getOctetString();
|
||||
ArrayUtil.reverse(privData);
|
||||
arrayS = privData;
|
||||
while (data.available() != 0) {
|
||||
DerValue value = data.getDerValue();
|
||||
if (value.isContextSpecific((byte) 0)) {
|
||||
// ignore for now
|
||||
} else if (value.isContextSpecific((byte) 1)) {
|
||||
// ignore for now
|
||||
} else {
|
||||
throw new InvalidKeyException("Unexpected value: " + value);
|
||||
}
|
||||
}
|
||||
AlgorithmParameters algParams = this.algid.getParameters();
|
||||
if (algParams == null) {
|
||||
throw new InvalidKeyException("EC domain parameters must be "
|
||||
+ "encoded in the algorithm identifier");
|
||||
}
|
||||
params = algParams.getParameterSpec(ECParameterSpec.class);
|
||||
} catch (IOException | InvalidParameterSpecException e) {
|
||||
throw new InvalidKeyException("Invalid EC private key", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PublicKey calculatePublicKey() {
|
||||
ECParameterSpec ecParams = getParams();
|
||||
ECOperations ops = ECOperations.forParameters(ecParams)
|
||||
.orElseThrow(ProviderException::new);
|
||||
MutablePoint pub = ops.multiply(ecParams.getGenerator(), getArrayS0());
|
||||
AffinePoint affPub = pub.asAffine();
|
||||
ECPoint w = new ECPoint(affPub.getX().asBigInteger(),
|
||||
affPub.getY().asBigInteger());
|
||||
try {
|
||||
return new ECPublicKeyImpl(w, ecParams);
|
||||
} catch (InvalidKeyException e) {
|
||||
throw new ProviderException(
|
||||
"Unexpected error calculating public key", e);
|
||||
}
|
||||
}
|
||||
}
|
132
src/java.base/share/classes/sun/security/ec/ECPublicKeyImpl.java
Normal file
132
src/java.base/share/classes/sun/security/ec/ECPublicKeyImpl.java
Normal file
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* Copyright (c) 2006, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ec;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import java.security.*;
|
||||
import java.security.interfaces.*;
|
||||
import java.security.spec.*;
|
||||
|
||||
import sun.security.util.ECParameters;
|
||||
import sun.security.util.ECUtil;
|
||||
|
||||
import sun.security.x509.*;
|
||||
|
||||
/**
|
||||
* Key implementation for EC public keys.
|
||||
*
|
||||
* @since 1.6
|
||||
* @author Andreas Sterbenz
|
||||
*/
|
||||
public final class ECPublicKeyImpl extends X509Key implements ECPublicKey {
|
||||
|
||||
private static final long serialVersionUID = -2462037275160462289L;
|
||||
|
||||
@SuppressWarnings("serial") // Type of field is not
|
||||
// Serializable;see writeReplace
|
||||
private ECPoint w;
|
||||
@SuppressWarnings("serial") // Type of field is not Serializable
|
||||
private ECParameterSpec params;
|
||||
|
||||
/**
|
||||
* Construct a key from its components. Used by the
|
||||
* ECKeyFactory.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
ECPublicKeyImpl(ECPoint w, ECParameterSpec params)
|
||||
throws InvalidKeyException {
|
||||
this.w = w;
|
||||
this.params = params;
|
||||
// generate the encoding
|
||||
algid = new AlgorithmId
|
||||
(AlgorithmId.EC_oid, ECParameters.getAlgorithmParameters(params));
|
||||
key = ECUtil.encodePoint(w, params.getCurve());
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a key from its encoding.
|
||||
*/
|
||||
ECPublicKeyImpl(byte[] encoded) throws InvalidKeyException {
|
||||
decode(encoded);
|
||||
}
|
||||
|
||||
// see JCA doc
|
||||
public String getAlgorithm() {
|
||||
return "EC";
|
||||
}
|
||||
|
||||
// see JCA doc
|
||||
public ECPoint getW() {
|
||||
return w;
|
||||
}
|
||||
|
||||
// see JCA doc
|
||||
public ECParameterSpec getParams() {
|
||||
return params;
|
||||
}
|
||||
|
||||
// Internal API to get the encoded point. Currently used by SunPKCS11.
|
||||
// This may change/go away depending on what we do with the public API.
|
||||
@SuppressWarnings("deprecation")
|
||||
public byte[] getEncodedPublicValue() {
|
||||
return key.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the key. Called by X509Key.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
protected void parseKeyBits() throws InvalidKeyException {
|
||||
AlgorithmParameters algParams = this.algid.getParameters();
|
||||
if (algParams == null) {
|
||||
throw new InvalidKeyException("EC domain parameters must be " +
|
||||
"encoded in the algorithm identifier");
|
||||
}
|
||||
|
||||
try {
|
||||
params = algParams.getParameterSpec(ECParameterSpec.class);
|
||||
w = ECUtil.decodePoint(key, params.getCurve());
|
||||
} catch (IOException | InvalidParameterSpecException e) {
|
||||
throw new InvalidKeyException("Invalid EC key", e);
|
||||
}
|
||||
}
|
||||
|
||||
// return a string representation of this key for debugging
|
||||
public String toString() {
|
||||
return "Sun EC public key, " + params.getCurve().getField().getFieldSize()
|
||||
+ " bits\n public x coord: " + w.getAffineX()
|
||||
+ "\n public y coord: " + w.getAffineY()
|
||||
+ "\n parameters: " + params;
|
||||
}
|
||||
|
||||
protected Object writeReplace() throws java.io.ObjectStreamException {
|
||||
return new KeyRep(KeyRep.Type.PUBLIC,
|
||||
getAlgorithm(),
|
||||
getFormat(),
|
||||
getEncoded());
|
||||
}
|
||||
}
|
156
src/java.base/share/classes/sun/security/ec/ParametersMap.java
Normal file
156
src/java.base/share/classes/sun/security/ec/ParametersMap.java
Normal file
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ec;
|
||||
|
||||
import sun.security.util.ObjectIdentifier;
|
||||
import sun.security.x509.AlgorithmId;
|
||||
|
||||
import java.security.spec.AlgorithmParameterSpec;
|
||||
import java.security.spec.NamedParameterSpec;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class ParametersMap<T> {
|
||||
|
||||
private Map<Integer, T> sizeMap = new HashMap<Integer, T>();
|
||||
private Map<ObjectIdentifier, T> oidMap =
|
||||
new HashMap<ObjectIdentifier, T>();
|
||||
private Map<String, T> nameMap = new HashMap<String, T>();
|
||||
|
||||
|
||||
public void fix() {
|
||||
|
||||
sizeMap = Collections.unmodifiableMap(sizeMap);
|
||||
oidMap = Collections.unmodifiableMap(oidMap);
|
||||
nameMap = Collections.unmodifiableMap(nameMap);
|
||||
}
|
||||
|
||||
public void put(String name, ObjectIdentifier oid, int size, T params) {
|
||||
nameMap.put(name.toLowerCase(), params);
|
||||
oidMap.put(oid, params);
|
||||
sizeMap.put(size, params);
|
||||
}
|
||||
|
||||
public Optional<T> getByOid(ObjectIdentifier id) {
|
||||
return Optional.ofNullable(oidMap.get(id));
|
||||
}
|
||||
public Optional<T> getBySize(int size) {
|
||||
return Optional.ofNullable(sizeMap.get(size));
|
||||
}
|
||||
public Optional<T> getByName(String name) {
|
||||
return Optional.ofNullable(nameMap.get(name.toLowerCase()));
|
||||
}
|
||||
|
||||
// Utility method that is used by the methods below to handle exception
|
||||
// suppliers
|
||||
private static
|
||||
<A, B> Supplier<B> apply(final Function<A, B> func, final A a) {
|
||||
return new Supplier<B>() {
|
||||
@Override
|
||||
public B get() {
|
||||
return func.apply(a);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get parameters by key size, or throw an exception if no parameters are
|
||||
* defined for the specified key size. This method is used in several
|
||||
* contexts that should throw different exceptions when the parameters
|
||||
* are not found. The first argument is a function that produces the
|
||||
* desired exception.
|
||||
*
|
||||
* @param exception a function that produces an exception from a string
|
||||
* @param size the desired key size
|
||||
* @param <E> the type of exception that is thrown
|
||||
* @return the parameters for the specified key size
|
||||
* @throws T when suitable parameters do not exist
|
||||
*/
|
||||
public
|
||||
<E extends Throwable>
|
||||
T getBySize(Function<String, E> exception,
|
||||
int size) throws E {
|
||||
|
||||
Optional<T> paramsOpt = getBySize(size);
|
||||
return paramsOpt.orElseThrow(
|
||||
apply(exception, "Unsupported size: " + size));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get parameters by algorithm ID, or throw an exception if no
|
||||
* parameters are defined for the specified ID. This method is used in
|
||||
* several contexts that should throw different exceptions when the
|
||||
* parameters are not found. The first argument is a function that produces
|
||||
* the desired exception.
|
||||
*
|
||||
* @param exception a function that produces an exception from a string
|
||||
* @param algId the algorithm ID
|
||||
* @param <E> the type of exception that is thrown
|
||||
* @return the parameters for the specified algorithm ID
|
||||
* @throws E when suitable parameters do not exist
|
||||
*/
|
||||
public
|
||||
<E extends Throwable>
|
||||
T get(Function<String, E> exception,
|
||||
AlgorithmId algId) throws E {
|
||||
|
||||
Optional<T> paramsOpt = getByOid(algId.getOID());
|
||||
return paramsOpt.orElseThrow(
|
||||
apply(exception, "Unsupported OID: " + algId.getOID()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get parameters by algorithm parameter spec, or throw an exception if no
|
||||
* parameters are defined for the spec. This method is used in
|
||||
* several contexts that should throw different exceptions when the
|
||||
* parameters are not found. The first argument is a function that produces
|
||||
* the desired exception.
|
||||
*
|
||||
* @param exception a function that produces an exception from a string
|
||||
* @param params the algorithm parameters spec
|
||||
* @param <E> the type of exception that is thrown
|
||||
* @return the parameters for the spec
|
||||
* @throws E when suitable parameters do not exist
|
||||
*/
|
||||
public
|
||||
<E extends Throwable>
|
||||
T get(Function<String, E> exception,
|
||||
AlgorithmParameterSpec params) throws E {
|
||||
|
||||
if (params instanceof NamedParameterSpec) {
|
||||
NamedParameterSpec namedParams = (NamedParameterSpec) params;
|
||||
Optional<T> paramsOpt = getByName(namedParams.getName());
|
||||
return paramsOpt.orElseThrow(
|
||||
apply(exception, "Unsupported name: " + namedParams.getName()));
|
||||
} else {
|
||||
throw exception.apply("Only NamedParameterSpec is supported.");
|
||||
}
|
||||
}
|
||||
}
|
384
src/java.base/share/classes/sun/security/ec/SunEC.java
Normal file
384
src/java.base/share/classes/sun/security/ec/SunEC.java
Normal file
|
@ -0,0 +1,384 @@
|
|||
/*
|
||||
* Copyright (c) 2009, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ec;
|
||||
|
||||
import java.security.AccessController;
|
||||
import java.security.InvalidParameterException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.security.Provider;
|
||||
import java.security.ProviderException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import sun.security.ec.ed.EdDSAKeyFactory;
|
||||
import sun.security.ec.ed.EdDSAKeyPairGenerator;
|
||||
import sun.security.ec.ed.EdDSASignature;
|
||||
import sun.security.util.CurveDB;
|
||||
import sun.security.util.NamedCurve;
|
||||
|
||||
import static sun.security.util.SecurityConstants.PROVIDER_VER;
|
||||
import static sun.security.util.SecurityProviderConstants.*;
|
||||
|
||||
/**
|
||||
* Provider class for the Elliptic Curve provider.
|
||||
*/
|
||||
public final class SunEC extends Provider {
|
||||
|
||||
private static final long serialVersionUID = -2279741672933606418L;
|
||||
|
||||
private static class ProviderServiceA extends ProviderService {
|
||||
ProviderServiceA(Provider p, String type, String algo, String cn,
|
||||
HashMap<String, String> attrs) {
|
||||
super(p, type, algo, cn, getAliases(algo), attrs);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ProviderService extends Provider.Service {
|
||||
|
||||
ProviderService(Provider p, String type, String algo, String cn) {
|
||||
super(p, type, algo, cn, null, null);
|
||||
}
|
||||
|
||||
ProviderService(Provider p, String type, String algo, String cn,
|
||||
List<String> aliases, HashMap<String, String> attrs) {
|
||||
super(p, type, algo, cn, aliases, attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object newInstance(Object ctrParamObj)
|
||||
throws NoSuchAlgorithmException {
|
||||
String type = getType();
|
||||
if (ctrParamObj != null) {
|
||||
throw new InvalidParameterException
|
||||
("constructorParameter not used with " + type + " engines");
|
||||
}
|
||||
|
||||
String algo = getAlgorithm();
|
||||
try {
|
||||
if (type.equals("Signature")) {
|
||||
|
||||
if (algo.equalsIgnoreCase("EdDSA")) {
|
||||
return new EdDSASignature();
|
||||
} else if (algo.equalsIgnoreCase("Ed25519")) {
|
||||
return new EdDSASignature.Ed25519();
|
||||
} else if (algo.equalsIgnoreCase("Ed448")) {
|
||||
return new EdDSASignature.Ed448();
|
||||
}
|
||||
|
||||
boolean inP1363 = algo.endsWith("inP1363Format");
|
||||
if (inP1363) {
|
||||
algo = algo.substring(0, algo.length() - 13);
|
||||
}
|
||||
if (algo.equals("SHA1withECDSA")) {
|
||||
return (inP1363? new ECDSASignature.SHA1inP1363Format() :
|
||||
new ECDSASignature.SHA1());
|
||||
} else if (algo.equals("SHA224withECDSA")) {
|
||||
return (inP1363? new ECDSASignature.SHA224inP1363Format() :
|
||||
new ECDSASignature.SHA224());
|
||||
} else if (algo.equals("SHA256withECDSA")) {
|
||||
return (inP1363? new ECDSASignature.SHA256inP1363Format() :
|
||||
new ECDSASignature.SHA256());
|
||||
} else if (algo.equals("SHA384withECDSA")) {
|
||||
return (inP1363? new ECDSASignature.SHA384inP1363Format() :
|
||||
new ECDSASignature.SHA384());
|
||||
} else if (algo.equals("SHA512withECDSA")) {
|
||||
return (inP1363? new ECDSASignature.SHA512inP1363Format() :
|
||||
new ECDSASignature.SHA512());
|
||||
} else if (algo.equals("NONEwithECDSA")) {
|
||||
return (inP1363? new ECDSASignature.RawinP1363Format() :
|
||||
new ECDSASignature.Raw());
|
||||
} else if (algo.equals("SHA3-224withECDSA")) {
|
||||
return (inP1363? new ECDSASignature.SHA3_224inP1363Format() :
|
||||
new ECDSASignature.SHA3_224());
|
||||
} else if (algo.equals("SHA3-256withECDSA")) {
|
||||
return (inP1363? new ECDSASignature.SHA3_256inP1363Format() :
|
||||
new ECDSASignature.SHA3_256());
|
||||
} else if (algo.equals("SHA3-384withECDSA")) {
|
||||
return (inP1363? new ECDSASignature.SHA3_384inP1363Format() :
|
||||
new ECDSASignature.SHA3_384());
|
||||
} else if (algo.equals("SHA3-512withECDSA")) {
|
||||
return (inP1363? new ECDSASignature.SHA3_512inP1363Format() :
|
||||
new ECDSASignature.SHA3_512());
|
||||
}
|
||||
} else if (type.equals("KeyFactory")) {
|
||||
if (algo.equals("EC")) {
|
||||
return new ECKeyFactory();
|
||||
} else if (algo.equals("XDH")) {
|
||||
return new XDHKeyFactory();
|
||||
} else if (algo.equals("X25519")) {
|
||||
return new XDHKeyFactory.X25519();
|
||||
} else if (algo.equals("X448")) {
|
||||
return new XDHKeyFactory.X448();
|
||||
} else if (algo.equalsIgnoreCase("EdDSA")) {
|
||||
return new EdDSAKeyFactory();
|
||||
} else if (algo.equalsIgnoreCase("Ed25519")) {
|
||||
return new EdDSAKeyFactory.Ed25519();
|
||||
} else if (algo.equalsIgnoreCase("Ed448")) {
|
||||
return new EdDSAKeyFactory.Ed448();
|
||||
}
|
||||
} else if (type.equals("AlgorithmParameters")) {
|
||||
if (algo.equals("EC")) {
|
||||
return new sun.security.util.ECParameters();
|
||||
}
|
||||
} else if (type.equals("KeyPairGenerator")) {
|
||||
if (algo.equals("EC")) {
|
||||
return new ECKeyPairGenerator();
|
||||
} else if (algo.equals("XDH")) {
|
||||
return new XDHKeyPairGenerator();
|
||||
} else if (algo.equals("X25519")) {
|
||||
return new XDHKeyPairGenerator.X25519();
|
||||
} else if (algo.equals("X448")) {
|
||||
return new XDHKeyPairGenerator.X448();
|
||||
} else if (algo.equalsIgnoreCase("EdDSA")) {
|
||||
return new EdDSAKeyPairGenerator();
|
||||
} else if (algo.equalsIgnoreCase("Ed25519")) {
|
||||
return new EdDSAKeyPairGenerator.Ed25519();
|
||||
} else if (algo.equalsIgnoreCase("Ed448")) {
|
||||
return new EdDSAKeyPairGenerator.Ed448();
|
||||
}
|
||||
} else if (type.equals("KeyAgreement")) {
|
||||
if (algo.equals("ECDH")) {
|
||||
return new ECDHKeyAgreement();
|
||||
} else if (algo.equals("XDH")) {
|
||||
return new XDHKeyAgreement();
|
||||
} else if (algo.equals("X25519")) {
|
||||
return new XDHKeyAgreement.X25519();
|
||||
} else if (algo.equals("X448")) {
|
||||
return new XDHKeyAgreement.X448();
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
throw new NoSuchAlgorithmException("Error constructing " +
|
||||
type + " for " + algo + " using SunEC", ex);
|
||||
}
|
||||
throw new ProviderException("No impl for " + algo +
|
||||
" " + type);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
public SunEC() {
|
||||
super("SunEC", PROVIDER_VER, "Sun Elliptic Curve provider");
|
||||
AccessController.doPrivileged(new PrivilegedAction<Void>() {
|
||||
public Void run() {
|
||||
putEntries();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void putEntries() {
|
||||
HashMap<String, String> ATTRS = new HashMap<>(3);
|
||||
ATTRS.put("ImplementedIn", "Software");
|
||||
String ecKeyClasses = "java.security.interfaces.ECPublicKey" +
|
||||
"|java.security.interfaces.ECPrivateKey";
|
||||
ATTRS.put("SupportedKeyClasses", ecKeyClasses);
|
||||
ATTRS.put("KeySize", "256");
|
||||
|
||||
/*
|
||||
* Key Factory engine
|
||||
*/
|
||||
putService(new ProviderServiceA(this, "KeyFactory",
|
||||
"EC", "sun.security.ec.ECKeyFactory", ATTRS));
|
||||
|
||||
/*
|
||||
* Algorithm Parameter engine
|
||||
*/
|
||||
// "AlgorithmParameters.EC SupportedCurves" prop used by unit test
|
||||
boolean firstCurve = true;
|
||||
StringBuilder names = new StringBuilder();
|
||||
|
||||
for (NamedCurve namedCurve : List.of(
|
||||
CurveDB.P_256, CurveDB.P_384, CurveDB.P_521)) {
|
||||
if (!firstCurve) {
|
||||
names.append("|");
|
||||
} else {
|
||||
firstCurve = false;
|
||||
}
|
||||
|
||||
names.append("[");
|
||||
String[] commonNames = namedCurve.getNameAndAliases();
|
||||
for (String commonName : commonNames) {
|
||||
names.append(commonName);
|
||||
names.append(",");
|
||||
}
|
||||
|
||||
names.append(namedCurve.getObjectId());
|
||||
names.append("]");
|
||||
}
|
||||
|
||||
HashMap<String, String> apAttrs = new HashMap<>(ATTRS);
|
||||
apAttrs.put("SupportedCurves", names.toString());
|
||||
|
||||
putService(new ProviderServiceA(this, "AlgorithmParameters",
|
||||
"EC", "sun.security.util.ECParameters", apAttrs));
|
||||
|
||||
putXDHEntries();
|
||||
putEdDSAEntries();
|
||||
|
||||
/*
|
||||
* Signature engines
|
||||
*/
|
||||
putService(new ProviderService(this, "Signature",
|
||||
"NONEwithECDSA", "sun.security.ec.ECDSASignature$Raw",
|
||||
null, ATTRS));
|
||||
putService(new ProviderServiceA(this, "Signature",
|
||||
"SHA1withECDSA", "sun.security.ec.ECDSASignature$SHA1",
|
||||
ATTRS));
|
||||
putService(new ProviderServiceA(this, "Signature",
|
||||
"SHA224withECDSA", "sun.security.ec.ECDSASignature$SHA224",
|
||||
ATTRS));
|
||||
putService(new ProviderServiceA(this, "Signature",
|
||||
"SHA256withECDSA", "sun.security.ec.ECDSASignature$SHA256",
|
||||
ATTRS));
|
||||
putService(new ProviderServiceA(this, "Signature",
|
||||
"SHA384withECDSA", "sun.security.ec.ECDSASignature$SHA384",
|
||||
ATTRS));
|
||||
putService(new ProviderServiceA(this, "Signature",
|
||||
"SHA512withECDSA", "sun.security.ec.ECDSASignature$SHA512",
|
||||
ATTRS));
|
||||
putService(new ProviderServiceA(this, "Signature",
|
||||
"SHA3-224withECDSA", "sun.security.ec.ECDSASignature$SHA3_224",
|
||||
ATTRS));
|
||||
putService(new ProviderServiceA(this, "Signature",
|
||||
"SHA3-256withECDSA", "sun.security.ec.ECDSASignature$SHA3_256",
|
||||
ATTRS));
|
||||
putService(new ProviderServiceA(this, "Signature",
|
||||
"SHA3-384withECDSA", "sun.security.ec.ECDSASignature$SHA3_384",
|
||||
ATTRS));
|
||||
putService(new ProviderServiceA(this, "Signature",
|
||||
"SHA3-512withECDSA", "sun.security.ec.ECDSASignature$SHA3_512",
|
||||
ATTRS));
|
||||
|
||||
putService(new ProviderService(this, "Signature",
|
||||
"NONEwithECDSAinP1363Format",
|
||||
"sun.security.ec.ECDSASignature$RawinP1363Format"));
|
||||
putService(new ProviderService(this, "Signature",
|
||||
"SHA1withECDSAinP1363Format",
|
||||
"sun.security.ec.ECDSASignature$SHA1inP1363Format"));
|
||||
putService(new ProviderService(this, "Signature",
|
||||
"SHA224withECDSAinP1363Format",
|
||||
"sun.security.ec.ECDSASignature$SHA224inP1363Format"));
|
||||
putService(new ProviderService(this, "Signature",
|
||||
"SHA256withECDSAinP1363Format",
|
||||
"sun.security.ec.ECDSASignature$SHA256inP1363Format"));
|
||||
putService(new ProviderService(this, "Signature",
|
||||
"SHA384withECDSAinP1363Format",
|
||||
"sun.security.ec.ECDSASignature$SHA384inP1363Format"));
|
||||
putService(new ProviderService(this, "Signature",
|
||||
"SHA512withECDSAinP1363Format",
|
||||
"sun.security.ec.ECDSASignature$SHA512inP1363Format"));
|
||||
|
||||
putService(new ProviderService(this, "Signature",
|
||||
"SHA3-224withECDSAinP1363Format",
|
||||
"sun.security.ec.ECDSASignature$SHA3_224inP1363Format"));
|
||||
putService(new ProviderService(this, "Signature",
|
||||
"SHA3-256withECDSAinP1363Format",
|
||||
"sun.security.ec.ECDSASignature$SHA3_256inP1363Format"));
|
||||
putService(new ProviderService(this, "Signature",
|
||||
"SHA3-384withECDSAinP1363Format",
|
||||
"sun.security.ec.ECDSASignature$SHA3_384inP1363Format"));
|
||||
putService(new ProviderService(this, "Signature",
|
||||
"SHA3-512withECDSAinP1363Format",
|
||||
"sun.security.ec.ECDSASignature$SHA3_512inP1363Format"));
|
||||
|
||||
/*
|
||||
* Key Pair Generator engine
|
||||
*/
|
||||
putService(new ProviderServiceA(this, "KeyPairGenerator",
|
||||
"EC", "sun.security.ec.ECKeyPairGenerator", ATTRS));
|
||||
|
||||
/*
|
||||
* Key Agreement engine
|
||||
*/
|
||||
putService(new ProviderService(this, "KeyAgreement",
|
||||
"ECDH", "sun.security.ec.ECDHKeyAgreement", null, ATTRS));
|
||||
}
|
||||
|
||||
private void putXDHEntries() {
|
||||
|
||||
HashMap<String, String> ATTRS = new HashMap<>(1);
|
||||
ATTRS.put("ImplementedIn", "Software");
|
||||
|
||||
putService(new ProviderService(this, "KeyFactory",
|
||||
"XDH", "sun.security.ec.XDHKeyFactory", null, ATTRS));
|
||||
putService(new ProviderServiceA(this, "KeyFactory",
|
||||
"X25519", "sun.security.ec.XDHKeyFactory.X25519",
|
||||
ATTRS));
|
||||
putService(new ProviderServiceA(this, "KeyFactory",
|
||||
"X448", "sun.security.ec.XDHKeyFactory.X448",
|
||||
ATTRS));
|
||||
|
||||
putService(new ProviderService(this, "KeyPairGenerator",
|
||||
"XDH", "sun.security.ec.XDHKeyPairGenerator", null, ATTRS));
|
||||
putService(new ProviderServiceA(this, "KeyPairGenerator",
|
||||
"X25519", "sun.security.ec.XDHKeyPairGenerator.X25519",
|
||||
ATTRS));
|
||||
putService(new ProviderServiceA(this, "KeyPairGenerator",
|
||||
"X448", "sun.security.ec.XDHKeyPairGenerator.X448",
|
||||
ATTRS));
|
||||
|
||||
putService(new ProviderService(this, "KeyAgreement",
|
||||
"XDH", "sun.security.ec.XDHKeyAgreement", null, ATTRS));
|
||||
putService(new ProviderServiceA(this, "KeyAgreement",
|
||||
"X25519", "sun.security.ec.XDHKeyAgreement.X25519",
|
||||
ATTRS));
|
||||
putService(new ProviderServiceA(this, "KeyAgreement",
|
||||
"X448", "sun.security.ec.XDHKeyAgreement.X448",
|
||||
ATTRS));
|
||||
}
|
||||
|
||||
private void putEdDSAEntries() {
|
||||
|
||||
HashMap<String, String> ATTRS = new HashMap<>(1);
|
||||
ATTRS.put("ImplementedIn", "Software");
|
||||
|
||||
putService(new ProviderService(this, "KeyFactory",
|
||||
"EdDSA", "sun.security.ec.ed.EdDSAKeyFactory", null, ATTRS));
|
||||
putService(new ProviderServiceA(this, "KeyFactory",
|
||||
"Ed25519", "sun.security.ec.ed.EdDSAKeyFactory.Ed25519", ATTRS));
|
||||
putService(new ProviderServiceA(this, "KeyFactory",
|
||||
"Ed448", "sun.security.ec.ed.EdDSAKeyFactory.Ed448", ATTRS));
|
||||
|
||||
putService(new ProviderService(this, "KeyPairGenerator",
|
||||
"EdDSA", "sun.security.ec.ed.EdDSAKeyPairGenerator", null, ATTRS));
|
||||
putService(new ProviderServiceA(this, "KeyPairGenerator",
|
||||
"Ed25519", "sun.security.ec.ed.EdDSAKeyPairGenerator.Ed25519",
|
||||
ATTRS));
|
||||
putService(new ProviderServiceA(this, "KeyPairGenerator",
|
||||
"Ed448", "sun.security.ec.ed.EdDSAKeyPairGenerator.Ed448",
|
||||
ATTRS));
|
||||
|
||||
putService(new ProviderService(this, "Signature",
|
||||
"EdDSA", "sun.security.ec.ed.EdDSASignature", null, ATTRS));
|
||||
putService(new ProviderServiceA(this, "Signature",
|
||||
"Ed25519", "sun.security.ec.ed.EdDSASignature.Ed25519", ATTRS));
|
||||
putService(new ProviderServiceA(this, "Signature",
|
||||
"Ed448", "sun.security.ec.ed.EdDSASignature.Ed448", ATTRS));
|
||||
|
||||
}
|
||||
}
|
230
src/java.base/share/classes/sun/security/ec/XDHKeyAgreement.java
Normal file
230
src/java.base/share/classes/sun/security/ec/XDHKeyAgreement.java
Normal file
|
@ -0,0 +1,230 @@
|
|||
/*
|
||||
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
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() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// the private key parameters must match params, if present
|
||||
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;
|
||||
|
||||
// Ensure public key parameters are compatible with private 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.");
|
||||
}
|
||||
|
||||
// The privateKey may be modified to a value that is equivalent for
|
||||
// the purposes of this algorithm.
|
||||
byte[] computedSecret = ops.encodedPointMultiply(
|
||||
this.privateKey,
|
||||
publicKey.getU());
|
||||
|
||||
// test for contributory behavior
|
||||
if (allZero(computedSecret)) {
|
||||
throw new InvalidKeyException("Point has small order");
|
||||
}
|
||||
|
||||
this.secret = computedSecret;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* Constant-time check for an all-zero array
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
267
src/java.base/share/classes/sun/security/ec/XDHKeyFactory.java
Normal file
267
src/java.base/share/classes/sun/security/ec/XDHKeyFactory.java
Normal file
|
@ -0,0 +1,267 @@
|
|||
/*
|
||||
* Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ec;
|
||||
|
||||
import java.security.KeyFactorySpi;
|
||||
import java.security.Key;
|
||||
import java.security.PublicKey;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.ProviderException;
|
||||
import java.security.interfaces.XECKey;
|
||||
import java.security.interfaces.XECPrivateKey;
|
||||
import java.security.interfaces.XECPublicKey;
|
||||
import java.security.spec.AlgorithmParameterSpec;
|
||||
import java.security.spec.NamedParameterSpec;
|
||||
import java.security.spec.KeySpec;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import java.security.spec.XECPublicKeySpec;
|
||||
import java.security.spec.XECPrivateKeySpec;
|
||||
import java.util.Arrays;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class XDHKeyFactory extends KeyFactorySpi {
|
||||
|
||||
private XECParameters lockedParams = null;
|
||||
|
||||
XDHKeyFactory() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
protected XDHKeyFactory(AlgorithmParameterSpec paramSpec) {
|
||||
lockedParams = XECParameters.get(ProviderException::new, paramSpec);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Key engineTranslateKey(Key key) throws InvalidKeyException {
|
||||
|
||||
if (key == null) {
|
||||
throw new InvalidKeyException("Key must not be null");
|
||||
}
|
||||
|
||||
if (key instanceof XECKey) {
|
||||
XECKey xecKey = (XECKey) key;
|
||||
XECParameters params = XECParameters.get(InvalidKeyException::new,
|
||||
xecKey.getParams());
|
||||
checkLockedParams(InvalidKeyException::new, params);
|
||||
|
||||
if (xecKey instanceof XECPublicKey) {
|
||||
XECPublicKey publicKey = (XECPublicKey) xecKey;
|
||||
return new XDHPublicKeyImpl(params, publicKey.getU());
|
||||
} else if (xecKey instanceof XECPrivateKey) {
|
||||
XECPrivateKey privateKey = (XECPrivateKey) xecKey;
|
||||
byte[] scalar = privateKey.getScalar().orElseThrow(
|
||||
() -> new InvalidKeyException("No private key data"));
|
||||
return new XDHPrivateKeyImpl(params, scalar);
|
||||
} else {
|
||||
throw new InvalidKeyException("Unsupported XECKey subclass");
|
||||
}
|
||||
} else if (key instanceof PublicKey &&
|
||||
key.getFormat().equals("X.509")) {
|
||||
XDHPublicKeyImpl result = new XDHPublicKeyImpl(key.getEncoded());
|
||||
checkLockedParams(InvalidKeyException::new, result.getParams());
|
||||
return result;
|
||||
} else if (key instanceof PrivateKey &&
|
||||
key.getFormat().equals("PKCS#8")) {
|
||||
byte[] encoded = key.getEncoded();
|
||||
try {
|
||||
XDHPrivateKeyImpl result = new XDHPrivateKeyImpl(encoded);
|
||||
checkLockedParams(InvalidKeyException::new, result.getParams());
|
||||
return result;
|
||||
} finally {
|
||||
Arrays.fill(encoded, (byte)0);
|
||||
}
|
||||
} else {
|
||||
throw new InvalidKeyException("Unsupported key type or format");
|
||||
}
|
||||
}
|
||||
|
||||
private
|
||||
<T extends Throwable>
|
||||
void checkLockedParams(Function<String, T> exception,
|
||||
AlgorithmParameterSpec spec) throws T {
|
||||
|
||||
XECParameters params = XECParameters.get(exception, spec);
|
||||
checkLockedParams(exception, params);
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PublicKey engineGeneratePublic(KeySpec keySpec)
|
||||
throws InvalidKeySpecException {
|
||||
|
||||
try {
|
||||
return generatePublicImpl(keySpec);
|
||||
} catch (InvalidKeyException ex) {
|
||||
throw new InvalidKeySpecException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PrivateKey engineGeneratePrivate(KeySpec keySpec)
|
||||
throws InvalidKeySpecException {
|
||||
|
||||
try {
|
||||
return generatePrivateImpl(keySpec);
|
||||
} catch (InvalidKeyException ex) {
|
||||
throw new InvalidKeySpecException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private PublicKey generatePublicImpl(KeySpec keySpec)
|
||||
throws InvalidKeyException, InvalidKeySpecException {
|
||||
|
||||
if (keySpec instanceof X509EncodedKeySpec) {
|
||||
X509EncodedKeySpec x509Spec = (X509EncodedKeySpec) keySpec;
|
||||
XDHPublicKeyImpl result =
|
||||
new XDHPublicKeyImpl(x509Spec.getEncoded());
|
||||
checkLockedParams(InvalidKeySpecException::new,
|
||||
result.getParams());
|
||||
return result;
|
||||
} else if (keySpec instanceof XECPublicKeySpec) {
|
||||
XECPublicKeySpec publicKeySpec = (XECPublicKeySpec) keySpec;
|
||||
XECParameters params = XECParameters.get(
|
||||
InvalidKeySpecException::new, publicKeySpec.getParams());
|
||||
checkLockedParams(InvalidKeySpecException::new, params);
|
||||
return new XDHPublicKeyImpl(params, publicKeySpec.getU());
|
||||
} else {
|
||||
throw new InvalidKeySpecException(
|
||||
"Only X509EncodedKeySpec and XECPublicKeySpec are supported");
|
||||
}
|
||||
}
|
||||
|
||||
private PrivateKey generatePrivateImpl(KeySpec keySpec)
|
||||
throws InvalidKeyException, InvalidKeySpecException {
|
||||
|
||||
if (keySpec instanceof PKCS8EncodedKeySpec) {
|
||||
PKCS8EncodedKeySpec pkcsSpec = (PKCS8EncodedKeySpec) keySpec;
|
||||
byte[] encoded = pkcsSpec.getEncoded();
|
||||
try {
|
||||
XDHPrivateKeyImpl result = new XDHPrivateKeyImpl(encoded);
|
||||
checkLockedParams(InvalidKeySpecException::new,
|
||||
result.getParams());
|
||||
return result;
|
||||
} finally {
|
||||
Arrays.fill(encoded, (byte) 0);
|
||||
}
|
||||
} else if (keySpec instanceof XECPrivateKeySpec) {
|
||||
XECPrivateKeySpec privateKeySpec = (XECPrivateKeySpec) keySpec;
|
||||
XECParameters params = XECParameters.get(
|
||||
InvalidKeySpecException::new, privateKeySpec.getParams());
|
||||
checkLockedParams(InvalidKeySpecException::new, params);
|
||||
byte[] scalar = privateKeySpec.getScalar();
|
||||
try {
|
||||
return new XDHPrivateKeyImpl(params, scalar);
|
||||
} finally {
|
||||
Arrays.fill(scalar, (byte)0);
|
||||
}
|
||||
} else {
|
||||
throw new InvalidKeySpecException(
|
||||
"Only PKCS8EncodedKeySpec and XECPrivateKeySpec supported");
|
||||
}
|
||||
}
|
||||
|
||||
protected <T extends KeySpec> T engineGetKeySpec(Key key, Class<T> keySpec)
|
||||
throws InvalidKeySpecException {
|
||||
|
||||
if (key instanceof XECPublicKey) {
|
||||
checkLockedParams(InvalidKeySpecException::new,
|
||||
((XECPublicKey) key).getParams());
|
||||
|
||||
if (keySpec.isAssignableFrom(X509EncodedKeySpec.class)) {
|
||||
if (!key.getFormat().equals("X.509")) {
|
||||
throw new InvalidKeySpecException("Format is not X.509");
|
||||
}
|
||||
return keySpec.cast(new X509EncodedKeySpec(key.getEncoded()));
|
||||
} else if (keySpec.isAssignableFrom(XECPublicKeySpec.class)) {
|
||||
XECPublicKey xecKey = (XECPublicKey) key;
|
||||
return keySpec.cast(
|
||||
new XECPublicKeySpec(xecKey.getParams(), xecKey.getU()));
|
||||
} else {
|
||||
throw new InvalidKeySpecException(
|
||||
"KeySpec must be X509EncodedKeySpec or XECPublicKeySpec");
|
||||
}
|
||||
} else if (key instanceof XECPrivateKey) {
|
||||
checkLockedParams(InvalidKeySpecException::new,
|
||||
((XECPrivateKey) key).getParams());
|
||||
|
||||
if (keySpec.isAssignableFrom(PKCS8EncodedKeySpec.class)) {
|
||||
if (!key.getFormat().equals("PKCS#8")) {
|
||||
throw new InvalidKeySpecException("Format is not PKCS#8");
|
||||
}
|
||||
byte[] encoded = key.getEncoded();
|
||||
try {
|
||||
return keySpec.cast(new PKCS8EncodedKeySpec(encoded));
|
||||
} finally {
|
||||
Arrays.fill(encoded, (byte)0);
|
||||
}
|
||||
} else if (keySpec.isAssignableFrom(XECPrivateKeySpec.class)) {
|
||||
XECPrivateKey xecKey = (XECPrivateKey) key;
|
||||
byte[] scalar = xecKey.getScalar().orElseThrow(
|
||||
() -> new InvalidKeySpecException("No private key value")
|
||||
);
|
||||
try {
|
||||
return keySpec.cast(
|
||||
new XECPrivateKeySpec(xecKey.getParams(), scalar));
|
||||
} finally {
|
||||
Arrays.fill(scalar, (byte)0);
|
||||
}
|
||||
} else {
|
||||
throw new InvalidKeySpecException
|
||||
("KeySpec must be PKCS8EncodedKeySpec or XECPrivateKeySpec");
|
||||
}
|
||||
} else {
|
||||
throw new InvalidKeySpecException("Unsupported key type");
|
||||
}
|
||||
}
|
||||
|
||||
static class X25519 extends XDHKeyFactory {
|
||||
|
||||
public X25519() {
|
||||
super(NamedParameterSpec.X25519);
|
||||
}
|
||||
}
|
||||
|
||||
static class X448 extends XDHKeyFactory {
|
||||
|
||||
public X448() {
|
||||
super(NamedParameterSpec.X448);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ec;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.KeyPairGeneratorSpi;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.InvalidParameterException;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.ProviderException;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.spec.AlgorithmParameterSpec;
|
||||
import java.security.spec.NamedParameterSpec;
|
||||
import java.util.Arrays;
|
||||
|
||||
import sun.security.jca.JCAUtil;
|
||||
|
||||
/**
|
||||
* Key pair generator for the XDH key agreement algorithm.
|
||||
*/
|
||||
public class XDHKeyPairGenerator extends KeyPairGeneratorSpi {
|
||||
|
||||
private static final NamedParameterSpec DEFAULT_PARAM_SPEC
|
||||
= NamedParameterSpec.X25519;
|
||||
|
||||
private SecureRandom random = null;
|
||||
private XECOperations ops = null;
|
||||
private XECParameters lockedParams = null;
|
||||
|
||||
XDHKeyPairGenerator() {
|
||||
tryInitialize(DEFAULT_PARAM_SPEC);
|
||||
}
|
||||
|
||||
private XDHKeyPairGenerator(NamedParameterSpec paramSpec) {
|
||||
tryInitialize(paramSpec);
|
||||
lockedParams = ops.getParameters();
|
||||
}
|
||||
|
||||
private void tryInitialize(NamedParameterSpec paramSpec) {
|
||||
try {
|
||||
initialize(paramSpec, null);
|
||||
} catch (InvalidAlgorithmParameterException ex) {
|
||||
String name = paramSpec.getName();
|
||||
throw new ProviderException(name + " not supported");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(int keySize, SecureRandom random) {
|
||||
|
||||
XECParameters params = XECParameters.getBySize(
|
||||
InvalidParameterException::new, keySize);
|
||||
|
||||
initializeImpl(params, random);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(AlgorithmParameterSpec params, SecureRandom random)
|
||||
throws InvalidAlgorithmParameterException {
|
||||
|
||||
XECParameters xecParams = XECParameters.get(
|
||||
InvalidAlgorithmParameterException::new, params);
|
||||
|
||||
initializeImpl(xecParams, random);
|
||||
}
|
||||
|
||||
private void initializeImpl(XECParameters params, SecureRandom random) {
|
||||
|
||||
if (lockedParams != null && lockedParams != params) {
|
||||
throw new InvalidParameterException("Parameters must be " +
|
||||
lockedParams.getName());
|
||||
}
|
||||
|
||||
this.ops = new XECOperations(params);
|
||||
this.random = random == null ? JCAUtil.getSecureRandom() : random;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public KeyPair generateKeyPair() {
|
||||
|
||||
byte[] privateKey = ops.generatePrivate(random);
|
||||
// computePublic may modify the private key, so clone it first
|
||||
byte[] cloned = privateKey.clone();
|
||||
BigInteger publicKey = ops.computePublic(cloned);
|
||||
Arrays.fill(cloned, (byte)0);
|
||||
|
||||
try {
|
||||
return new KeyPair(
|
||||
new XDHPublicKeyImpl(ops.getParameters(), publicKey),
|
||||
new XDHPrivateKeyImpl(ops.getParameters(), privateKey)
|
||||
);
|
||||
} catch (InvalidKeyException ex) {
|
||||
throw new ProviderException(ex);
|
||||
} finally {
|
||||
Arrays.fill(privateKey, (byte)0);
|
||||
}
|
||||
}
|
||||
|
||||
static class X25519 extends XDHKeyPairGenerator {
|
||||
|
||||
public X25519() {
|
||||
super(NamedParameterSpec.X25519);
|
||||
}
|
||||
}
|
||||
|
||||
static class X448 extends XDHKeyPairGenerator {
|
||||
|
||||
public X448() {
|
||||
super(NamedParameterSpec.X448);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ec;
|
||||
|
||||
import java.io.*;
|
||||
import java.security.interfaces.XECPrivateKey;
|
||||
import java.util.Optional;
|
||||
import java.security.*;
|
||||
import java.security.spec.*;
|
||||
|
||||
import sun.security.pkcs.PKCS8Key;
|
||||
import sun.security.x509.AlgorithmId;
|
||||
import sun.security.util.*;
|
||||
|
||||
public final class XDHPrivateKeyImpl extends PKCS8Key implements XECPrivateKey {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@SuppressWarnings("serial") // Type of field is not Serializable
|
||||
private final NamedParameterSpec paramSpec;
|
||||
private byte[] k;
|
||||
|
||||
XDHPrivateKeyImpl(XECParameters params, byte[] k)
|
||||
throws InvalidKeyException {
|
||||
|
||||
this.paramSpec = new NamedParameterSpec(params.getName());
|
||||
this.k = k.clone();
|
||||
|
||||
this.algid = new AlgorithmId(params.getOid());
|
||||
|
||||
DerValue val = new DerValue(DerValue.tag_OctetString, k);
|
||||
try {
|
||||
this.key = val.toByteArray();
|
||||
} finally {
|
||||
val.clear();
|
||||
}
|
||||
checkLength(params);
|
||||
}
|
||||
|
||||
XDHPrivateKeyImpl(byte[] encoded) throws InvalidKeyException {
|
||||
super(encoded);
|
||||
XECParameters params = XECParameters.get(
|
||||
InvalidKeyException::new, algid);
|
||||
paramSpec = new NamedParameterSpec(params.getName());
|
||||
try {
|
||||
DerInputStream derStream = new DerInputStream(key);
|
||||
k = derStream.getOctetString();
|
||||
} catch (IOException ex) {
|
||||
throw new InvalidKeyException(ex);
|
||||
}
|
||||
checkLength(params);
|
||||
}
|
||||
|
||||
void checkLength(XECParameters params) throws InvalidKeyException {
|
||||
|
||||
if (params.getBytes() != this.k.length) {
|
||||
throw new InvalidKeyException(
|
||||
"key length must be " + params.getBytes());
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] getK() {
|
||||
return k.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlgorithm() {
|
||||
return "XDH";
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlgorithmParameterSpec getParams() {
|
||||
return paramSpec;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<byte[]> getScalar() {
|
||||
return Optional.of(getK());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PublicKey calculatePublicKey() {
|
||||
XECParameters params = paramSpec.getName().equals("X25519")
|
||||
? XECParameters.X25519
|
||||
: XECParameters.X448;
|
||||
try {
|
||||
return new XDHPublicKeyImpl(params,
|
||||
new XECOperations(params).computePublic(k.clone()));
|
||||
} catch (InvalidKeyException e) {
|
||||
throw new ProviderException(
|
||||
"Unexpected error calculating public key", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ec;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.KeyRep;
|
||||
import java.security.interfaces.XECPublicKey;
|
||||
import java.security.spec.AlgorithmParameterSpec;
|
||||
import java.security.spec.NamedParameterSpec;
|
||||
import java.util.Arrays;
|
||||
|
||||
import sun.security.util.BitArray;
|
||||
import sun.security.x509.AlgorithmId;
|
||||
import sun.security.x509.X509Key;
|
||||
|
||||
public final class XDHPublicKeyImpl extends X509Key implements XECPublicKey {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final BigInteger u;
|
||||
@SuppressWarnings("serial") // Type of field is not Serializable
|
||||
private final NamedParameterSpec paramSpec;
|
||||
|
||||
XDHPublicKeyImpl(XECParameters params, BigInteger u)
|
||||
throws InvalidKeyException {
|
||||
|
||||
this.paramSpec = new NamedParameterSpec(params.getName());
|
||||
this.algid = new AlgorithmId(params.getOid());
|
||||
this.u = u.mod(params.getP());
|
||||
|
||||
byte[] u_arr = this.u.toByteArray();
|
||||
reverse(u_arr);
|
||||
// u_arr may be too large or too small, depending on the value of u
|
||||
u_arr = Arrays.copyOf(u_arr, params.getBytes());
|
||||
|
||||
setKey(new BitArray(u_arr.length * 8, u_arr));
|
||||
|
||||
checkLength(params);
|
||||
}
|
||||
|
||||
XDHPublicKeyImpl(byte[] encoded) throws InvalidKeyException {
|
||||
decode(encoded);
|
||||
|
||||
XECParameters params =
|
||||
XECParameters.get(InvalidKeyException::new, algid);
|
||||
this.paramSpec = new NamedParameterSpec(params.getName());
|
||||
// construct the BigInteger representation
|
||||
byte[] u_arr = getKey().toByteArray();
|
||||
reverse(u_arr);
|
||||
|
||||
// clear the extra bits
|
||||
int bitsMod8 = params.getBits() % 8;
|
||||
if (bitsMod8 != 0) {
|
||||
byte mask = (byte) ((1 << bitsMod8) - 1);
|
||||
u_arr[0] &= mask;
|
||||
}
|
||||
|
||||
this.u = new BigInteger(1, u_arr);
|
||||
|
||||
checkLength(params);
|
||||
}
|
||||
|
||||
void checkLength(XECParameters params) throws InvalidKeyException {
|
||||
|
||||
if (params.getBytes() * 8 != getKey().length()) {
|
||||
throw new InvalidKeyException(
|
||||
"key length must be " + params.getBytes());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigInteger getU() {
|
||||
return u;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlgorithmParameterSpec getParams() {
|
||||
return paramSpec;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlgorithm() {
|
||||
return "XDH";
|
||||
}
|
||||
|
||||
protected Object writeReplace() throws java.io.ObjectStreamException {
|
||||
return new KeyRep(KeyRep.Type.PUBLIC,
|
||||
getAlgorithm(),
|
||||
getFormat(),
|
||||
getEncoded());
|
||||
}
|
||||
|
||||
private static void swap(byte[] arr, int i, int j) {
|
||||
byte tmp = arr[i];
|
||||
arr[i] = arr[j];
|
||||
arr[j] = tmp;
|
||||
}
|
||||
|
||||
private static void reverse(byte [] arr) {
|
||||
int i = 0;
|
||||
int j = arr.length - 1;
|
||||
|
||||
while (i < j) {
|
||||
swap(arr, i, j);
|
||||
i++;
|
||||
j--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
271
src/java.base/share/classes/sun/security/ec/XECOperations.java
Normal file
271
src/java.base/share/classes/sun/security/ec/XECOperations.java
Normal file
|
@ -0,0 +1,271 @@
|
|||
/*
|
||||
* Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ec;
|
||||
|
||||
import sun.security.util.math.IntegerFieldModuloP;
|
||||
import sun.security.util.math.ImmutableIntegerModuloP;
|
||||
import sun.security.util.math.IntegerModuloP;
|
||||
import sun.security.util.math.MutableIntegerModuloP;
|
||||
import sun.security.util.math.SmallValue;
|
||||
import sun.security.util.math.intpoly.IntegerPolynomial25519;
|
||||
import sun.security.util.math.intpoly.IntegerPolynomial448;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.ProviderException;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
public class XECOperations {
|
||||
|
||||
private final XECParameters params;
|
||||
private final IntegerFieldModuloP field;
|
||||
private final ImmutableIntegerModuloP zero;
|
||||
private final ImmutableIntegerModuloP one;
|
||||
private final SmallValue a24;
|
||||
private final ImmutableIntegerModuloP basePoint;
|
||||
|
||||
public XECOperations(XECParameters c) {
|
||||
this.params = c;
|
||||
|
||||
BigInteger p = params.getP();
|
||||
this.field = getIntegerFieldModulo(p);
|
||||
this.zero = field.getElement(BigInteger.ZERO).fixed();
|
||||
this.one = field.get1().fixed();
|
||||
this.a24 = field.getSmallValue(params.getA24());
|
||||
this.basePoint = field.getElement(
|
||||
BigInteger.valueOf(c.getBasePoint()));
|
||||
}
|
||||
|
||||
public XECParameters getParameters() {
|
||||
return params;
|
||||
}
|
||||
|
||||
public byte[] generatePrivate(SecureRandom random) {
|
||||
byte[] result = new byte[this.params.getBytes()];
|
||||
random.nextBytes(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute a public key from an encoded private key. This method will
|
||||
* modify the supplied array in order to prune it.
|
||||
*/
|
||||
public BigInteger computePublic(byte[] k) {
|
||||
pruneK(k);
|
||||
return pointMultiply(k, this.basePoint).asBigInteger();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Multiply an encoded scalar with a point as a BigInteger and return an
|
||||
* encoded point. The array k holding the scalar will be pruned by
|
||||
* modifying it in place.
|
||||
*
|
||||
* @param k an encoded scalar
|
||||
* @param u the u-coordinate of a point as a BigInteger
|
||||
* @return the encoded product
|
||||
*/
|
||||
public byte[] encodedPointMultiply(byte[] k, BigInteger u) {
|
||||
pruneK(k);
|
||||
ImmutableIntegerModuloP elemU = field.getElement(u);
|
||||
return pointMultiply(k, elemU).asByteArray(params.getBytes());
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Multiply an encoded scalar with an encoded point and return an encoded
|
||||
* point. The array k holding the scalar will be pruned by
|
||||
* modifying it in place.
|
||||
*
|
||||
* @param k an encoded scalar
|
||||
* @param u an encoded point
|
||||
* @return the encoded product
|
||||
*/
|
||||
public byte[] encodedPointMultiply(byte[] k, byte[] u) {
|
||||
pruneK(k);
|
||||
ImmutableIntegerModuloP elemU = decodeU(u);
|
||||
return pointMultiply(k, elemU).asByteArray(params.getBytes());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the field element corresponding to an encoded u-coordinate.
|
||||
* This method prunes u by modifying it in place.
|
||||
*
|
||||
* @param u
|
||||
* @param bits
|
||||
* @return
|
||||
*/
|
||||
private ImmutableIntegerModuloP decodeU(byte[] u, int bits) {
|
||||
|
||||
maskHighOrder(u, bits);
|
||||
|
||||
return field.getElement(u);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mask off the high order bits of an encoded integer in an array. The
|
||||
* array is modified in place.
|
||||
*
|
||||
* @param arr an array containing an encoded integer
|
||||
* @param bits the number of bits to keep
|
||||
* @return the number, in range [1,8], of bits kept in the highest byte
|
||||
*/
|
||||
private static byte maskHighOrder(byte[] arr, int bits) {
|
||||
|
||||
int lastByteIndex = arr.length - 1;
|
||||
byte bitsMod8 = (byte) (bits % 8);
|
||||
byte highBits = bitsMod8 == 0 ? 8 : bitsMod8;
|
||||
byte msbMaskOff = (byte) ((1 << highBits) - 1);
|
||||
arr[lastByteIndex] &= msbMaskOff;
|
||||
|
||||
return highBits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prune an encoded scalar value by modifying it in place. The extra
|
||||
* high-order bits are masked off, the highest valid bit is set, and the
|
||||
* number is rounded down to a multiple of the cofactor.
|
||||
*
|
||||
* @param k an encoded scalar value
|
||||
* @param bits the number of bits in the scalar
|
||||
* @param logCofactor the base-2 logarithm of the cofactor
|
||||
*/
|
||||
private static void pruneK(byte[] k, int bits, int logCofactor) {
|
||||
|
||||
int lastByteIndex = k.length - 1;
|
||||
|
||||
// mask off unused high-order bits
|
||||
byte highBits = maskHighOrder(k, bits);
|
||||
|
||||
// set the highest bit
|
||||
byte msbMaskOn = (byte) (1 << (highBits - 1));
|
||||
k[lastByteIndex] |= msbMaskOn;
|
||||
|
||||
// round down to a multiple of the cofactor
|
||||
byte lsbMaskOff = (byte) (0xFF << logCofactor);
|
||||
k[0] &= lsbMaskOff;
|
||||
}
|
||||
|
||||
private void pruneK(byte[] k) {
|
||||
pruneK(k, params.getBits(), params.getLogCofactor());
|
||||
}
|
||||
|
||||
private ImmutableIntegerModuloP decodeU(byte [] u) {
|
||||
return decodeU(u, params.getBits());
|
||||
}
|
||||
|
||||
// Constant-time conditional swap
|
||||
private static void cswap(int swap, MutableIntegerModuloP x1,
|
||||
MutableIntegerModuloP x2) {
|
||||
|
||||
x1.conditionalSwapWith(x2, swap);
|
||||
}
|
||||
|
||||
private static IntegerFieldModuloP getIntegerFieldModulo(BigInteger p) {
|
||||
|
||||
if (p.equals(IntegerPolynomial25519.MODULUS)) {
|
||||
return IntegerPolynomial25519.ONE;
|
||||
}
|
||||
else if (p.equals(IntegerPolynomial448.MODULUS)) {
|
||||
return IntegerPolynomial448.ONE;
|
||||
}
|
||||
|
||||
throw new ProviderException("Unsupported prime: " + p.toString());
|
||||
}
|
||||
|
||||
private int bitAt(byte[] arr, int index) {
|
||||
int byteIndex = index / 8;
|
||||
int bitIndex = index % 8;
|
||||
return (arr[byteIndex] & (1 << bitIndex)) >> bitIndex;
|
||||
}
|
||||
|
||||
/*
|
||||
* Constant-time Montgomery ladder that computes k*u and returns the
|
||||
* result as a field element.
|
||||
*/
|
||||
private IntegerModuloP pointMultiply(byte[] k,
|
||||
ImmutableIntegerModuloP u) {
|
||||
|
||||
ImmutableIntegerModuloP x_1 = u;
|
||||
MutableIntegerModuloP x_2 = this.one.mutable();
|
||||
MutableIntegerModuloP z_2 = this.zero.mutable();
|
||||
MutableIntegerModuloP x_3 = u.mutable();
|
||||
MutableIntegerModuloP z_3 = this.one.mutable();
|
||||
int swap = 0;
|
||||
|
||||
// Variables below are reused to avoid unnecessary allocation
|
||||
// They will be assigned in the loop, so initial value doesn't matter
|
||||
MutableIntegerModuloP m1 = this.zero.mutable();
|
||||
MutableIntegerModuloP DA = this.zero.mutable();
|
||||
MutableIntegerModuloP E = this.zero.mutable();
|
||||
MutableIntegerModuloP a24_times_E = this.zero.mutable();
|
||||
|
||||
// Comments describe the equivalent operations from RFC 7748
|
||||
// In comments, A(m1) means the variable m1 holds the value A
|
||||
for (int t = params.getBits() - 1; t >= 0; t--) {
|
||||
int k_t = bitAt(k, t);
|
||||
swap = swap ^ k_t;
|
||||
cswap(swap, x_2, x_3);
|
||||
cswap(swap, z_2, z_3);
|
||||
swap = k_t;
|
||||
|
||||
// A(m1) = x_2 + z_2
|
||||
m1.setValue(x_2).setSum(z_2);
|
||||
// D = x_3 - z_3
|
||||
// DA = D * A(m1)
|
||||
DA.setValue(x_3).setDifference(z_3).setProduct(m1);
|
||||
// AA(m1) = A(m1)^2
|
||||
m1.setSquare();
|
||||
// B(x_2) = x_2 - z_2
|
||||
x_2.setDifference(z_2);
|
||||
// C = x_3 + z_3
|
||||
// CB(x_3) = C * B(x_2)
|
||||
x_3.setSum(z_3).setProduct(x_2);
|
||||
// BB(x_2) = B^2
|
||||
x_2.setSquare();
|
||||
// E = AA(m1) - BB(x_2)
|
||||
E.setValue(m1).setDifference(x_2);
|
||||
// compute a24 * E using SmallValue
|
||||
a24_times_E.setValue(E);
|
||||
a24_times_E.setProduct(this.a24);
|
||||
|
||||
// assign results to x_3, z_3, x_2, z_2
|
||||
// x_2 = AA(m1) * BB
|
||||
x_2.setProduct(m1);
|
||||
// z_2 = E * (AA(m1) + a24 * E)
|
||||
z_2.setValue(m1).setSum(a24_times_E).setProduct(E);
|
||||
// z_3 = x_1*(DA - CB(x_3))^2
|
||||
z_3.setValue(DA).setDifference(x_3).setSquare().setProduct(x_1);
|
||||
// x_3 = (CB(x_3) + DA)^2
|
||||
x_3.setSum(DA).setSquare();
|
||||
}
|
||||
|
||||
cswap(swap, x_2, x_3);
|
||||
cswap(swap, z_2, z_3);
|
||||
|
||||
// return (x_2 * z_2^(p - 2))
|
||||
return x_2.setProduct(z_2.multiplicativeInverse());
|
||||
}
|
||||
}
|
173
src/java.base/share/classes/sun/security/ec/XECParameters.java
Normal file
173
src/java.base/share/classes/sun/security/ec/XECParameters.java
Normal file
|
@ -0,0 +1,173 @@
|
|||
/*
|
||||
* Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ec;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.security.spec.AlgorithmParameterSpec;
|
||||
import java.security.spec.NamedParameterSpec;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import java.util.function.Function;
|
||||
|
||||
import sun.security.util.KnownOIDs;
|
||||
import sun.security.util.ObjectIdentifier;
|
||||
import sun.security.x509.AlgorithmId;
|
||||
|
||||
public class XECParameters {
|
||||
|
||||
static final XECParameters X25519;
|
||||
static final XECParameters X448;
|
||||
|
||||
private static final ParametersMap<XECParameters> namedParams =
|
||||
new ParametersMap<>();
|
||||
|
||||
// Naming/identification parameters
|
||||
private final ObjectIdentifier oid;
|
||||
private final String name;
|
||||
|
||||
// Curve/field parameters
|
||||
private final int bits;
|
||||
private final BigInteger p;
|
||||
private final int logCofactor;
|
||||
private final int a24;
|
||||
private final byte basePoint;
|
||||
|
||||
/**
|
||||
*
|
||||
* Construct an object holding the supplied parameters. No parameters are
|
||||
* checked, so this method always succeeds. This method supports
|
||||
* Montgomery curves of the form y^2 = x^3 + ax^2 + x.
|
||||
*
|
||||
* @param bits The number of relevant bits in a public/private key.
|
||||
* @param p The prime that defines the finite field.
|
||||
* @param a24 The value of (a - 2) / 4, where a is the second-degree curve
|
||||
* coefficient.
|
||||
* @param basePoint The point that generates the desired group
|
||||
* @param logCofactor The base-2 logarithm of the cofactor of the curve
|
||||
* @param oid
|
||||
* @param name
|
||||
*/
|
||||
public XECParameters(int bits, BigInteger p, int a24,
|
||||
byte basePoint, int logCofactor,
|
||||
ObjectIdentifier oid, String name) {
|
||||
|
||||
this.bits = bits;
|
||||
this.logCofactor = logCofactor;
|
||||
this.p = p;
|
||||
this.a24 = a24;
|
||||
this.basePoint = basePoint;
|
||||
this.oid = oid;
|
||||
this.name = name;
|
||||
|
||||
}
|
||||
|
||||
public int getBits() {
|
||||
return bits;
|
||||
}
|
||||
public int getBytes() {
|
||||
return (bits + 7) / 8;
|
||||
}
|
||||
public int getLogCofactor() {
|
||||
return logCofactor;
|
||||
}
|
||||
public BigInteger getP() {
|
||||
return p;
|
||||
}
|
||||
public int getA24() {
|
||||
return a24;
|
||||
}
|
||||
public byte getBasePoint() {
|
||||
return basePoint;
|
||||
}
|
||||
public ObjectIdentifier getOid() {
|
||||
return oid;
|
||||
}
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
static {
|
||||
final BigInteger TWO = BigInteger.valueOf(2);
|
||||
|
||||
Map<Integer, XECParameters> bySize = new HashMap<>();
|
||||
Map<ObjectIdentifier, XECParameters> byOid = new HashMap<>();
|
||||
Map<String, XECParameters> byName = new HashMap<>();
|
||||
|
||||
// set up X25519
|
||||
BigInteger p2 = TWO.pow(255).subtract(BigInteger.valueOf(19));
|
||||
X25519 = addParameters(255, p2, 121665, (byte)0x09, 3,
|
||||
KnownOIDs.X25519, NamedParameterSpec.X25519.getName());
|
||||
|
||||
// set up X448
|
||||
BigInteger p4 = TWO.pow(448).subtract(TWO.pow(224))
|
||||
.subtract(BigInteger.ONE);
|
||||
X448 = addParameters(448, p4, 39081, (byte)0x05, 2,
|
||||
KnownOIDs.X448, NamedParameterSpec.X448.getName());
|
||||
|
||||
namedParams.fix();
|
||||
}
|
||||
|
||||
private static XECParameters addParameters(int bits, BigInteger p, int a24,
|
||||
byte basePoint, int logCofactor, KnownOIDs koid, String name) {
|
||||
|
||||
ObjectIdentifier oid = ObjectIdentifier.of(koid);
|
||||
XECParameters params =
|
||||
new XECParameters(bits, p, a24, basePoint, logCofactor, oid, name);
|
||||
namedParams.put(name.toLowerCase(), oid, bits, params);
|
||||
return params;
|
||||
}
|
||||
|
||||
boolean oidEquals(XECParameters other) {
|
||||
return oid.equals(other.getOid());
|
||||
}
|
||||
|
||||
|
||||
public static
|
||||
<T extends Throwable>
|
||||
XECParameters getBySize(Function<String, T> exception,
|
||||
int size) throws T {
|
||||
|
||||
return namedParams.getBySize(exception, size);
|
||||
}
|
||||
|
||||
public static
|
||||
<T extends Throwable>
|
||||
XECParameters get(Function<String, T> exception,
|
||||
AlgorithmId algId) throws T {
|
||||
|
||||
return namedParams.get(exception, algId);
|
||||
}
|
||||
|
||||
public static
|
||||
<T extends Throwable>
|
||||
XECParameters get(Function<String, T> exception,
|
||||
AlgorithmParameterSpec params) throws T {
|
||||
|
||||
return namedParams.get(exception, params);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,214 @@
|
|||
/*
|
||||
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package sun.security.ec.ed;
|
||||
|
||||
import sun.security.ec.point.*;
|
||||
import sun.security.util.math.*;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.function.Function;
|
||||
|
||||
/*
|
||||
* Elliptic curve point arithmetic, decoding, and other operations for the
|
||||
* family of curves including edwards25519 and its related group. Though the
|
||||
* operations in this class are optimized for edwards25519, they are correct
|
||||
* for any twisted Edwards curve ax^2 + y^2 = 1 + dx^2y^2 (mod p) with the
|
||||
* following properties:
|
||||
* 1) a = -1 (mod p)
|
||||
* 2) a is square (mod p)
|
||||
* 3) d is not square (mod p)
|
||||
*/
|
||||
public class Ed25519Operations extends EdECOperations {
|
||||
|
||||
private final SmallValue two;
|
||||
private final ImmutableIntegerModuloP d;
|
||||
private final ExtendedHomogeneousPoint.Immutable basePoint;
|
||||
|
||||
private static final BigInteger TWO = BigInteger.valueOf(2);
|
||||
private static final BigInteger SEVEN = BigInteger.valueOf(7);
|
||||
private final BigInteger sizeMinus5;
|
||||
|
||||
public Ed25519Operations(ImmutableIntegerModuloP d, BigInteger baseX,
|
||||
BigInteger baseY) {
|
||||
|
||||
this.two = d.getField().getSmallValue(2);
|
||||
this.d = d;
|
||||
this.basePoint = of(new AffinePoint(
|
||||
d.getField().getElement(baseX), d.getField().getElement(baseY)
|
||||
));
|
||||
this.sizeMinus5 =
|
||||
d.getField().getSize().subtract(BigInteger.valueOf(5));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Point basePointMultiply(byte[] scalar) {
|
||||
return setProduct(basePoint.mutable(), scalar);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ExtendedHomogeneousPoint.Immutable getNeutral() {
|
||||
IntegerFieldModuloP field = d.getField();
|
||||
return new ExtendedHomogeneousPoint.Immutable(field.get0(),
|
||||
field.get1(), field.get0(), field.get1());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MutablePoint setSum(MutablePoint p1, MutablePoint p2,
|
||||
MutableIntegerModuloP t1,
|
||||
MutableIntegerModuloP t2,
|
||||
MutableIntegerModuloP t3) {
|
||||
|
||||
ExtendedHomogeneousPoint.Mutable ehp1 =
|
||||
(ExtendedHomogeneousPoint.Mutable) p1;
|
||||
ExtendedHomogeneousPoint.Mutable ehp2 =
|
||||
(ExtendedHomogeneousPoint.Mutable) p2;
|
||||
return setSum(ehp1, ehp2, t1, t2, t3);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MutablePoint setDouble(MutablePoint p, MutableIntegerModuloP t1,
|
||||
MutableIntegerModuloP t2) {
|
||||
|
||||
ExtendedHomogeneousPoint.Mutable ehp =
|
||||
(ExtendedHomogeneousPoint.Mutable) p;
|
||||
return setDouble(ehp, t1, t2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExtendedHomogeneousPoint.Immutable of(AffinePoint p) {
|
||||
return new ExtendedHomogeneousPoint.Immutable(p.getX(), p.getY(),
|
||||
p.getX().multiply(p.getY()), p.getX().getField().get1());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Throwable>
|
||||
AffinePoint decodeAffinePoint(Function<String, T> exception,
|
||||
int xLSB, IntegerModuloP y) throws T {
|
||||
|
||||
IntegerFieldModuloP field = d.getField();
|
||||
BigInteger p = field.getSize();
|
||||
ImmutableIntegerModuloP y2 = y.square();
|
||||
ImmutableIntegerModuloP u = y2.subtract(field.get1());
|
||||
MutableIntegerModuloP v = d.mutable().setProduct(y2)
|
||||
.setSum(field.get1());
|
||||
|
||||
MutableIntegerModuloP x =
|
||||
u.mutable().setProduct(v.pow(BigInteger.valueOf(3)));
|
||||
ImmutableIntegerModuloP uv7pow =
|
||||
u.multiply(v.pow(SEVEN)).pow(sizeMinus5.shiftRight(3));
|
||||
x.setProduct(uv7pow);
|
||||
|
||||
v.setProduct(x).setProduct(x);
|
||||
// v now holds vx^2
|
||||
BigInteger bigVX2 = v.asBigInteger();
|
||||
if (bigVX2.equals(u.asBigInteger())) {
|
||||
// do nothing---x is correct
|
||||
} else if (bigVX2.equals(u.additiveInverse().asBigInteger())) {
|
||||
BigInteger exp = p.subtract(BigInteger.ONE).shiftRight(2);
|
||||
IntegerModuloP twoPow = field.getElement(TWO.modPow(exp, p));
|
||||
x.setProduct(twoPow);
|
||||
} else {
|
||||
throw exception.apply("Invalid point");
|
||||
}
|
||||
|
||||
if (x.asBigInteger().equals(BigInteger.ZERO) && xLSB == 1) {
|
||||
throw exception.apply("Invalid point");
|
||||
}
|
||||
|
||||
if (xLSB != (x.asBigInteger().intValue() & 1)) {
|
||||
x.setAdditiveInverse();
|
||||
}
|
||||
|
||||
return new AffinePoint(x.fixed(), y.fixed());
|
||||
}
|
||||
|
||||
ExtendedHomogeneousPoint.Mutable setSum(
|
||||
ExtendedHomogeneousPoint.Mutable p1,
|
||||
ExtendedHomogeneousPoint.Mutable p2,
|
||||
MutableIntegerModuloP t1,
|
||||
MutableIntegerModuloP t2,
|
||||
MutableIntegerModuloP t3) {
|
||||
|
||||
t1.setValue(p2.getY()).setDifference(p2.getX());
|
||||
// t1 holds y2 - x2
|
||||
t2.setValue(p1.getY()).setDifference(p1.getX()).setProduct(t1);
|
||||
// t2 holds A = (y1 - x1) * (y2 - x2)
|
||||
t1.setValue(p2.getY()).setSum(p2.getX());
|
||||
// t1 holds y2 + x2
|
||||
t3.setValue(p1.getY()).setSum(p1.getX()).setProduct(t1);
|
||||
// t3 holds B = (y1 + x1) * (y2 + x2)
|
||||
p1.getX().setValue(t3).setDifference(t2);
|
||||
// x holds E = B - A
|
||||
t3.setSum(t2);
|
||||
// t3 holds H = B + A, t2 is unused
|
||||
t2.setValue(d).setSum(d).setProduct(p1.getT()).setProduct(p2.getT());
|
||||
// t2 holds C
|
||||
t1.setValue(p1.getZ()).setProduct(p2.getZ()).setProduct(two);
|
||||
// t1 holds D
|
||||
p1.getY().setValue(t1).setSum(t2);
|
||||
// y holds G
|
||||
p1.getZ().setValue(t1).setDifference(t2);
|
||||
// z holds F
|
||||
|
||||
p1.getT().setValue(p1.getX()).setProduct(t3);
|
||||
p1.getX().setProduct(p1.getZ());
|
||||
p1.getZ().setProduct(p1.getY());
|
||||
p1.getY().setProduct(t3);
|
||||
|
||||
return p1;
|
||||
|
||||
}
|
||||
|
||||
protected ExtendedHomogeneousPoint.Mutable setDouble(
|
||||
ExtendedHomogeneousPoint.Mutable p,
|
||||
MutableIntegerModuloP t1, MutableIntegerModuloP t2) {
|
||||
|
||||
t1.setValue(p.getX()).setSum(p.getY()).setSquare();
|
||||
// t1 holds (x + y)^2
|
||||
p.getX().setSquare();
|
||||
// x = A = x^2
|
||||
p.getY().setSquare();
|
||||
// y = B = y^2
|
||||
t2.setValue(p.getX()).setSum(p.getY());
|
||||
// t2 holds H
|
||||
p.getZ().setSquare().setProduct(two);
|
||||
// z holds C
|
||||
|
||||
p.getT().setValue(t2).setDifference(t1);
|
||||
// t holds E
|
||||
t1.setValue(p.getX()).setDifference(p.getY());
|
||||
// t1 holds G
|
||||
|
||||
p.getZ().setSum(t1);
|
||||
// z holds F
|
||||
|
||||
p.getX().setValue(p.getT()).setProduct(p.getZ());
|
||||
p.getY().setValue(t1).setProduct(t2);
|
||||
p.getT().setProduct(t2);
|
||||
p.getZ().setProduct(t1);
|
||||
|
||||
return p;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,201 @@
|
|||
/*
|
||||
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package sun.security.ec.ed;
|
||||
|
||||
import sun.security.ec.point.*;
|
||||
import sun.security.util.math.*;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.function.Function;
|
||||
|
||||
// Arithmetic works for a=1 and non-square d
|
||||
/*
|
||||
* Elliptic curve point arithmetic, decoding, and other operations for the
|
||||
* family of curves including edwards448 and its related group. Though the
|
||||
* operations in this class are optimized for edwards448, they are correct
|
||||
* for any untwisted Edwards curve x^2 + y^2 = 1 + dx^2y^2 (mod p) where
|
||||
* d is not square (mod p).
|
||||
*/
|
||||
public class Ed448Operations extends EdECOperations {
|
||||
|
||||
private final SmallValue two;
|
||||
private final ImmutableIntegerModuloP d;
|
||||
private final ProjectivePoint.Immutable basePoint;
|
||||
|
||||
private static final BigInteger TWO = BigInteger.valueOf(2);
|
||||
private static final BigInteger THREE = BigInteger.valueOf(3);
|
||||
private static final BigInteger FIVE = BigInteger.valueOf(5);
|
||||
private final BigInteger sizeMinus3;
|
||||
|
||||
public Ed448Operations(ImmutableIntegerModuloP d, BigInteger baseX,
|
||||
BigInteger baseY) {
|
||||
|
||||
this.two = d.getField().getSmallValue(2);
|
||||
this.d = d;
|
||||
this.basePoint = of(new AffinePoint(
|
||||
d.getField().getElement(baseX),
|
||||
d.getField().getElement(baseY)
|
||||
));
|
||||
|
||||
this.sizeMinus3 = d.getField().getSize().subtract(THREE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Point basePointMultiply(byte[] scalar) {
|
||||
return setProduct(basePoint.mutable(), scalar);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ProjectivePoint.Immutable getNeutral() {
|
||||
IntegerFieldModuloP field = d.getField();
|
||||
return new ProjectivePoint.Immutable(field.get0(), field.get1(),
|
||||
field.get1());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MutablePoint setSum(MutablePoint p1, MutablePoint p2,
|
||||
MutableIntegerModuloP t1,
|
||||
MutableIntegerModuloP t2,
|
||||
MutableIntegerModuloP t3) {
|
||||
|
||||
ProjectivePoint.Mutable ehp1 = (ProjectivePoint.Mutable) p1;
|
||||
ProjectivePoint.Mutable ehp2 = (ProjectivePoint.Mutable) p2;
|
||||
return setSum(ehp1, ehp2, t1, t2, t3);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MutablePoint setDouble(MutablePoint p, MutableIntegerModuloP t1,
|
||||
MutableIntegerModuloP t2) {
|
||||
|
||||
ProjectivePoint.Mutable ehp = (ProjectivePoint.Mutable) p;
|
||||
return setDouble(ehp, t1, t2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProjectivePoint.Immutable of(AffinePoint p) {
|
||||
return new ProjectivePoint.Immutable(p.getX(), p.getY(),
|
||||
p.getX().getField().get1());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Throwable>
|
||||
AffinePoint decodeAffinePoint(Function<String, T> exception, int xLSB,
|
||||
IntegerModuloP y) throws T {
|
||||
|
||||
ImmutableIntegerModuloP y2 = y.square();
|
||||
ImmutableIntegerModuloP u = y2.subtract(d.getField().get1());
|
||||
MutableIntegerModuloP v = d.mutable().setProduct(y2)
|
||||
.setDifference(d.getField().get1());
|
||||
|
||||
IntegerModuloP u5v3pow = u.pow(FIVE).multiply(v.pow(THREE))
|
||||
.pow(sizeMinus3.shiftRight(2));
|
||||
|
||||
MutableIntegerModuloP x = v.mutable().setProduct(u.pow(THREE))
|
||||
.setProduct(u5v3pow);
|
||||
|
||||
v.setProduct(x).setProduct(x);
|
||||
// v now holds vx^2
|
||||
if (v.asBigInteger().equals(u.asBigInteger())) {
|
||||
// x is correct
|
||||
} else {
|
||||
throw exception.apply("Invalid point");
|
||||
}
|
||||
|
||||
if (x.asBigInteger().equals(BigInteger.ZERO) && xLSB == 1) {
|
||||
throw exception.apply("Invalid point");
|
||||
}
|
||||
|
||||
if (xLSB != (x.asBigInteger().intValue() & 1)) {
|
||||
x.setAdditiveInverse();
|
||||
}
|
||||
|
||||
return new AffinePoint(x.fixed(), y.fixed());
|
||||
}
|
||||
|
||||
ProjectivePoint.Mutable setSum(
|
||||
ProjectivePoint.Mutable p1,
|
||||
ProjectivePoint.Mutable p2,
|
||||
MutableIntegerModuloP t1,
|
||||
MutableIntegerModuloP t2,
|
||||
MutableIntegerModuloP t3) {
|
||||
|
||||
t1.setValue(p1.getX()).setProduct(p2.getX());
|
||||
// t1 holds C
|
||||
t2.setValue(p2.getX()).setSum(p2.getY());
|
||||
p1.getX().setSum(p1.getY()).setProduct(t2);
|
||||
// x holds H
|
||||
p1.getZ().setProduct(p2.getZ());
|
||||
// z holds A
|
||||
p1.getY().setProduct(p2.getY());
|
||||
// y holds D
|
||||
|
||||
t3.setValue(d).setProduct(t1).setProduct(p1.getY());
|
||||
// t3 holds E
|
||||
// do part of the final calculation of x and y to free up t1
|
||||
p1.getX().setDifference(t1).setDifference(p1.getY());
|
||||
p1.getY().setDifference(t1);
|
||||
t1.setValue(p1.getZ()).setSquare();
|
||||
// t2 holds B
|
||||
|
||||
t2.setValue(t1).setDifference(t3);
|
||||
// t2 holds F
|
||||
t1.setSum(t3);
|
||||
// t1 holds G
|
||||
|
||||
p1.getX().setProduct(t2).setProduct(p1.getZ());
|
||||
p1.getY().setProduct(t1).setProduct(p1.getZ());
|
||||
p1.getZ().setValue(t2.multiply(t1));
|
||||
|
||||
return p1;
|
||||
|
||||
}
|
||||
|
||||
protected ProjectivePoint.Mutable setDouble(ProjectivePoint.Mutable p,
|
||||
MutableIntegerModuloP t1,
|
||||
MutableIntegerModuloP t2) {
|
||||
|
||||
t2.setValue(p.getX()).setSquare();
|
||||
// t2 holds C
|
||||
p.getX().setSum(p.getY()).setSquare();
|
||||
// x holds B
|
||||
p.getY().setSquare();
|
||||
// y holds D
|
||||
p.getZ().setSquare();
|
||||
// z holds H
|
||||
|
||||
t1.setValue(t2).setSum(p.getY());
|
||||
// t1 holds E
|
||||
t2.setDifference(p.getY());
|
||||
p.getY().setValue(t1).setProduct(t2);
|
||||
|
||||
p.getZ().setProduct(two);
|
||||
p.getZ().setAdditiveInverse().setSum(t1);
|
||||
// z holds J
|
||||
p.getX().setDifference(t1).setProduct(p.getZ());
|
||||
p.getZ().setProduct(t1);
|
||||
|
||||
return p;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ec.ed;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.AlgorithmParametersSpi;
|
||||
import java.security.spec.AlgorithmParameterSpec;
|
||||
import java.security.spec.ECParameterSpec;
|
||||
import java.security.spec.EdDSAParameterSpec;
|
||||
import java.security.spec.InvalidParameterSpecException;
|
||||
|
||||
/**
|
||||
* This AlgorithmParametersSpi only supports NamedParameterSpec.
|
||||
* EdDSAParameterSpec is not support because there is not ASN.1 format
|
||||
*/
|
||||
|
||||
public class EdDSAAlgorithmParameters extends AlgorithmParametersSpi {
|
||||
|
||||
EdDSAParameterSpec edspec;
|
||||
|
||||
// If no curve is provide, wait engineInit() to provide one.
|
||||
public EdDSAAlgorithmParameters() {
|
||||
}
|
||||
|
||||
/**
|
||||
* NamedParameterSpec can only be used if curve was not specified
|
||||
* as part of getInstance(EdDSA). If the curve was used, engineInit will
|
||||
* throws an exception for being already initialized.
|
||||
* EdDSAParameterSpec is not support because there is not ASN.1 format
|
||||
*
|
||||
* @param paramSpec NamedParameterSpec curve.
|
||||
*
|
||||
* @throws InvalidParameterSpecException
|
||||
*/
|
||||
@Override
|
||||
protected void engineInit(AlgorithmParameterSpec paramSpec)
|
||||
throws InvalidParameterSpecException {
|
||||
if (paramSpec instanceof EdDSAParameterSpec) {
|
||||
edspec = (EdDSAParameterSpec)paramSpec;
|
||||
return;
|
||||
}
|
||||
throw new InvalidParameterSpecException(
|
||||
"Unknown AlgorithmParameterSpec");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void engineInit(byte[] params) throws IOException {
|
||||
throw new IOException(
|
||||
"EdDSA does not support parameters as a byte array.");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void engineInit(byte[] params, String format) throws IOException {
|
||||
engineInit(params);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T extends AlgorithmParameterSpec> T engineGetParameterSpec(
|
||||
Class<T> paramSpec) throws InvalidParameterSpecException {
|
||||
|
||||
if (paramSpec.isAssignableFrom(ECParameterSpec.class)) {
|
||||
return paramSpec.cast(edspec);
|
||||
}
|
||||
throw new InvalidParameterSpecException(
|
||||
"Only EDDSAParameterSpec supported.");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] engineGetEncoded() throws IOException {
|
||||
throw new IOException(
|
||||
"EdDSA does not support parameters as a byte array.");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] engineGetEncoded(String format) throws IOException {
|
||||
throw new IOException(
|
||||
"EdDSA does not support parameters as a byte array.");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String engineToString() {
|
||||
return edspec.toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,261 @@
|
|||
/*
|
||||
* Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ec.ed;
|
||||
|
||||
import java.security.KeyFactorySpi;
|
||||
import java.security.Key;
|
||||
import java.security.PublicKey;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.ProviderException;
|
||||
import java.security.interfaces.*;
|
||||
import java.security.spec.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class EdDSAKeyFactory extends KeyFactorySpi {
|
||||
|
||||
private EdDSAParameters lockedParams = null;
|
||||
|
||||
public EdDSAKeyFactory() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
protected EdDSAKeyFactory(NamedParameterSpec paramSpec) {
|
||||
lockedParams = EdDSAParameters.get(ProviderException::new, paramSpec);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Key engineTranslateKey(Key key) throws InvalidKeyException {
|
||||
|
||||
if (key == null) {
|
||||
throw new InvalidKeyException("Key must not be null");
|
||||
}
|
||||
|
||||
if (key instanceof EdECKey) {
|
||||
EdECKey edKey = (EdECKey) key;
|
||||
EdDSAParameters params = EdDSAParameters.get(
|
||||
InvalidKeyException::new, edKey.getParams());
|
||||
checkLockedParams(InvalidKeyException::new, params);
|
||||
|
||||
if (edKey instanceof EdECPublicKey) {
|
||||
EdECPublicKey publicKey = (EdECPublicKey) edKey;
|
||||
return new EdDSAPublicKeyImpl(params, publicKey.getPoint());
|
||||
} else if (edKey instanceof EdECPrivateKey) {
|
||||
EdECPrivateKey privateKey = (EdECPrivateKey) edKey;
|
||||
byte[] privateKeyBytes = privateKey.getBytes().orElseThrow(
|
||||
() -> new InvalidKeyException("No private key data"));
|
||||
return new EdDSAPrivateKeyImpl(params, privateKeyBytes);
|
||||
} else {
|
||||
throw new InvalidKeyException("Unsupported EdECKey subclass");
|
||||
}
|
||||
} else if (key instanceof PublicKey &&
|
||||
key.getFormat().equals("X.509")) {
|
||||
EdDSAPublicKeyImpl result =
|
||||
new EdDSAPublicKeyImpl(key.getEncoded());
|
||||
checkLockedParams(InvalidKeyException::new, result.getParams());
|
||||
return result;
|
||||
} else if (key instanceof PrivateKey &&
|
||||
key.getFormat().equals("PKCS#8")) {
|
||||
byte[] encoded = key.getEncoded();
|
||||
try {
|
||||
EdDSAPrivateKeyImpl result =
|
||||
new EdDSAPrivateKeyImpl(encoded);
|
||||
checkLockedParams(InvalidKeyException::new, result.getParams());
|
||||
return result;
|
||||
} finally {
|
||||
Arrays.fill(encoded, (byte)0);
|
||||
}
|
||||
} else {
|
||||
throw new InvalidKeyException("Unsupported key type or format");
|
||||
}
|
||||
}
|
||||
|
||||
private
|
||||
<T extends Throwable>
|
||||
void checkLockedParams(Function<String, T> exception,
|
||||
NamedParameterSpec spec) throws T {
|
||||
|
||||
EdDSAParameters params = EdDSAParameters.get(exception, spec);
|
||||
checkLockedParams(exception, params);
|
||||
}
|
||||
|
||||
private
|
||||
<T extends Throwable>
|
||||
void checkLockedParams(Function<String, T> exception,
|
||||
EdDSAParameters params) throws T {
|
||||
|
||||
if (lockedParams != null && lockedParams != params) {
|
||||
throw exception.apply("Parameters must be " +
|
||||
lockedParams.getName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PublicKey engineGeneratePublic(KeySpec keySpec)
|
||||
throws InvalidKeySpecException {
|
||||
|
||||
try {
|
||||
return generatePublicImpl(keySpec);
|
||||
} catch (InvalidKeyException ex) {
|
||||
throw new InvalidKeySpecException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PrivateKey engineGeneratePrivate(KeySpec keySpec)
|
||||
throws InvalidKeySpecException {
|
||||
|
||||
try {
|
||||
return generatePrivateImpl(keySpec);
|
||||
} catch (InvalidKeyException ex) {
|
||||
throw new InvalidKeySpecException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private PublicKey generatePublicImpl(KeySpec keySpec)
|
||||
throws InvalidKeyException, InvalidKeySpecException {
|
||||
|
||||
if (keySpec instanceof X509EncodedKeySpec) {
|
||||
X509EncodedKeySpec x509Spec = (X509EncodedKeySpec) keySpec;
|
||||
EdDSAPublicKeyImpl result =
|
||||
new EdDSAPublicKeyImpl(x509Spec.getEncoded());
|
||||
checkLockedParams(InvalidKeySpecException::new,
|
||||
result.getParams());
|
||||
return result;
|
||||
} else if (keySpec instanceof EdECPublicKeySpec) {
|
||||
EdECPublicKeySpec publicKeySpec = (EdECPublicKeySpec) keySpec;
|
||||
EdDSAParameters params = EdDSAParameters.get(
|
||||
InvalidKeySpecException::new, publicKeySpec.getParams());
|
||||
checkLockedParams(InvalidKeySpecException::new, params);
|
||||
return new EdDSAPublicKeyImpl(params, publicKeySpec.getPoint());
|
||||
} else {
|
||||
throw new InvalidKeySpecException(
|
||||
"Only X509EncodedKeySpec and EdECPublicKeySpec are supported");
|
||||
}
|
||||
}
|
||||
|
||||
private PrivateKey generatePrivateImpl(KeySpec keySpec)
|
||||
throws InvalidKeyException, InvalidKeySpecException {
|
||||
|
||||
if (keySpec instanceof PKCS8EncodedKeySpec) {
|
||||
PKCS8EncodedKeySpec pkcsSpec = (PKCS8EncodedKeySpec) keySpec;
|
||||
byte[] encoded = pkcsSpec.getEncoded();
|
||||
try {
|
||||
EdDSAPrivateKeyImpl result =
|
||||
new EdDSAPrivateKeyImpl(encoded);
|
||||
checkLockedParams(InvalidKeySpecException::new,
|
||||
result.getParams());
|
||||
return result;
|
||||
} finally {
|
||||
Arrays.fill(encoded, (byte) 0);
|
||||
}
|
||||
} else if (keySpec instanceof EdECPrivateKeySpec) {
|
||||
EdECPrivateKeySpec privateKeySpec = (EdECPrivateKeySpec) keySpec;
|
||||
EdDSAParameters params = EdDSAParameters.get(
|
||||
InvalidKeySpecException::new, privateKeySpec.getParams());
|
||||
checkLockedParams(InvalidKeySpecException::new, params);
|
||||
byte[] bytes = privateKeySpec.getBytes();
|
||||
try {
|
||||
return new EdDSAPrivateKeyImpl(params, bytes);
|
||||
} finally {
|
||||
Arrays.fill(bytes, (byte)0);
|
||||
}
|
||||
} else {
|
||||
throw new InvalidKeySpecException(
|
||||
"Only PKCS8EncodedKeySpec and EdECPrivateKeySpec supported");
|
||||
}
|
||||
}
|
||||
|
||||
protected <T extends KeySpec> T engineGetKeySpec(Key key, Class<T> keySpec)
|
||||
throws InvalidKeySpecException {
|
||||
|
||||
if (key instanceof EdECPublicKey) {
|
||||
checkLockedParams(InvalidKeySpecException::new,
|
||||
((EdECPublicKey) key).getParams());
|
||||
|
||||
if (keySpec.isAssignableFrom(X509EncodedKeySpec.class)) {
|
||||
if (!key.getFormat().equals("X.509")) {
|
||||
throw new InvalidKeySpecException("Format is not X.509");
|
||||
}
|
||||
return keySpec.cast(new X509EncodedKeySpec(key.getEncoded()));
|
||||
} else if (keySpec.isAssignableFrom(EdECPublicKeySpec.class)) {
|
||||
EdECPublicKey edKey = (EdECPublicKey) key;
|
||||
return keySpec.cast(
|
||||
new EdECPublicKeySpec(edKey.getParams(), edKey.getPoint()));
|
||||
} else {
|
||||
throw new InvalidKeySpecException(
|
||||
"KeySpec must be X509EncodedKeySpec or EdECPublicKeySpec");
|
||||
}
|
||||
} else if (key instanceof EdECPrivateKey) {
|
||||
checkLockedParams(InvalidKeySpecException::new,
|
||||
((EdECPrivateKey) key).getParams());
|
||||
|
||||
if (keySpec.isAssignableFrom(PKCS8EncodedKeySpec.class)) {
|
||||
if (!key.getFormat().equals("PKCS#8")) {
|
||||
throw new InvalidKeySpecException("Format is not PKCS#8");
|
||||
}
|
||||
byte[] encoded = key.getEncoded();
|
||||
try {
|
||||
return keySpec.cast(new PKCS8EncodedKeySpec(encoded));
|
||||
} finally {
|
||||
Arrays.fill(encoded, (byte)0);
|
||||
}
|
||||
} else if (keySpec.isAssignableFrom(EdECPrivateKeySpec.class)) {
|
||||
EdECPrivateKey edKey = (EdECPrivateKey) key;
|
||||
byte[] scalar = edKey.getBytes().orElseThrow(
|
||||
() -> new InvalidKeySpecException("No private key value")
|
||||
);
|
||||
try {
|
||||
return keySpec.cast(
|
||||
new EdECPrivateKeySpec(edKey.getParams(), scalar));
|
||||
} finally {
|
||||
Arrays.fill(scalar, (byte)0);
|
||||
}
|
||||
} else {
|
||||
throw new InvalidKeySpecException
|
||||
("KeySpec must be PKCS8EncodedKeySpec or EdECPrivateKeySpec");
|
||||
}
|
||||
} else {
|
||||
throw new InvalidKeySpecException("Unsupported key type");
|
||||
}
|
||||
}
|
||||
|
||||
public static class Ed25519 extends EdDSAKeyFactory {
|
||||
|
||||
public Ed25519() {
|
||||
super(NamedParameterSpec.ED25519);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Ed448 extends EdDSAKeyFactory {
|
||||
|
||||
public Ed448() {
|
||||
super(NamedParameterSpec.ED448);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
* Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ec.ed;
|
||||
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.InvalidParameterException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGeneratorSpi;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.ProviderException;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.spec.AlgorithmParameterSpec;
|
||||
import java.security.spec.EdECPoint;
|
||||
import java.security.spec.NamedParameterSpec;
|
||||
import java.util.Arrays;
|
||||
|
||||
import sun.security.jca.JCAUtil;
|
||||
import sun.security.util.SecurityProviderConstants;
|
||||
|
||||
/**
|
||||
* Key pair generator for the EdDSA signature algorithm.
|
||||
*/
|
||||
public class EdDSAKeyPairGenerator extends KeyPairGeneratorSpi {
|
||||
|
||||
private SecureRandom random = null;
|
||||
private EdDSAOperations ops = null;
|
||||
private EdDSAParameters lockedParams = null;
|
||||
|
||||
public EdDSAKeyPairGenerator() {
|
||||
initialize(SecurityProviderConstants.DEF_ED_KEY_SIZE, null);
|
||||
}
|
||||
|
||||
private EdDSAKeyPairGenerator(NamedParameterSpec paramSpec) {
|
||||
tryInitialize(paramSpec);
|
||||
lockedParams = ops.getParameters();
|
||||
}
|
||||
|
||||
private void tryInitialize(NamedParameterSpec paramSpec) {
|
||||
try {
|
||||
initialize(paramSpec, null);
|
||||
} catch (InvalidAlgorithmParameterException ex) {
|
||||
String name = paramSpec.getName();
|
||||
throw new ProviderException(name + " not supported");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(int keySize, SecureRandom random) {
|
||||
|
||||
EdDSAParameters params = EdDSAParameters.getBySize(
|
||||
InvalidParameterException::new, keySize);
|
||||
|
||||
initializeImpl(params, random);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(AlgorithmParameterSpec params, SecureRandom random)
|
||||
throws InvalidAlgorithmParameterException {
|
||||
|
||||
EdDSAParameters edParams = EdDSAParameters.get(
|
||||
InvalidAlgorithmParameterException::new, params);
|
||||
|
||||
try {
|
||||
initializeImpl(edParams, random);
|
||||
} catch (InvalidParameterException e) {
|
||||
throw new InvalidAlgorithmParameterException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeImpl(EdDSAParameters params, SecureRandom random) {
|
||||
|
||||
if (lockedParams != null && lockedParams != params) {
|
||||
throw new InvalidParameterException("Parameters must be " +
|
||||
lockedParams.getName());
|
||||
}
|
||||
|
||||
try {
|
||||
this.ops = new EdDSAOperations(params);
|
||||
} catch (NoSuchAlgorithmException ex) {
|
||||
throw new ProviderException(ex);
|
||||
}
|
||||
this.random = random == null ? JCAUtil.getSecureRandom() : random;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public KeyPair generateKeyPair() {
|
||||
|
||||
byte[] privateKey = ops.generatePrivate(random);
|
||||
EdECPoint publicKey = ops.computePublic(privateKey);
|
||||
|
||||
try {
|
||||
return new KeyPair(
|
||||
new EdDSAPublicKeyImpl(ops.getParameters(), publicKey),
|
||||
new EdDSAPrivateKeyImpl(ops.getParameters(), privateKey)
|
||||
);
|
||||
} catch (InvalidKeyException ex) {
|
||||
throw new ProviderException(ex);
|
||||
} finally {
|
||||
Arrays.fill(privateKey, (byte)0);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Ed25519 extends EdDSAKeyPairGenerator {
|
||||
|
||||
public Ed25519() {
|
||||
super(NamedParameterSpec.ED25519);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Ed448 extends EdDSAKeyPairGenerator {
|
||||
|
||||
public Ed448() {
|
||||
super(NamedParameterSpec.ED448);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,282 @@
|
|||
/*
|
||||
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package sun.security.ec.ed;
|
||||
|
||||
import sun.security.ec.point.AffinePoint;
|
||||
import sun.security.ec.point.Point;
|
||||
import sun.security.util.ArrayUtil;
|
||||
import sun.security.util.math.IntegerFieldModuloP;
|
||||
import sun.security.util.math.IntegerModuloP;
|
||||
import sun.security.util.math.MutableIntegerModuloP;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.SignatureException;
|
||||
import java.security.spec.EdDSAParameterSpec;
|
||||
import java.security.spec.EdECPoint;
|
||||
import java.util.Arrays;
|
||||
import java.util.function.Function;
|
||||
|
||||
/*
|
||||
* A class containing the operations of the EdDSA signature scheme. The
|
||||
* parameters include an object that performs the elliptic curve point
|
||||
* arithmetic, and EdDSAOperations uses this object to construct the signing
|
||||
* and verification operations.
|
||||
*/
|
||||
public class EdDSAOperations {
|
||||
|
||||
private final EdDSAParameters params;
|
||||
|
||||
public EdDSAOperations(EdDSAParameters params)
|
||||
throws NoSuchAlgorithmException {
|
||||
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
public EdDSAParameters getParameters() {
|
||||
return params;
|
||||
}
|
||||
|
||||
public byte[] generatePrivate(SecureRandom random) {
|
||||
byte[] result = new byte[params.getKeyLength()];
|
||||
random.nextBytes(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public EdECPoint computePublic(byte[] privateKey) {
|
||||
byte[] privateKeyHash = params.digest(privateKey);
|
||||
int byteLength = privateKeyHash.length / 2;
|
||||
byte[] s = Arrays.copyOf(privateKeyHash, byteLength);
|
||||
prune(s);
|
||||
IntegerModuloP fieldS = params.getOrderField().getElement(s);
|
||||
fieldS.asByteArray(s);
|
||||
Point A = params.getEdOperations().basePointMultiply(s);
|
||||
return asEdECPoint(A.asAffine());
|
||||
}
|
||||
|
||||
private static EdECPoint asEdECPoint(AffinePoint p) {
|
||||
return new EdECPoint(p.getX().asBigInteger().testBit(0),
|
||||
p.getY().asBigInteger());
|
||||
}
|
||||
|
||||
public byte[] sign(EdDSAParameterSpec sigParams, byte[] privateKey,
|
||||
byte[] message) {
|
||||
|
||||
byte[] privateKeyHash = params.digest(privateKey);
|
||||
|
||||
int byteLength = privateKeyHash.length / 2;
|
||||
byte[] s = Arrays.copyOf(privateKeyHash, byteLength);
|
||||
prune(s);
|
||||
IntegerModuloP sElem = params.getOrderField().getElement(s);
|
||||
sElem.asByteArray(s);
|
||||
Point A = params.getEdOperations().basePointMultiply(s);
|
||||
byte[] prefix = Arrays.copyOfRange(privateKeyHash,
|
||||
privateKeyHash.length / 2, privateKeyHash.length);
|
||||
byte[] dom = params.dom(sigParams);
|
||||
byte[] r = params.digest(dom, prefix, message);
|
||||
|
||||
// reduce r modulo the order
|
||||
IntegerModuloP fieldR = params.getOrderField().getElement(r);
|
||||
r = new byte[params.getKeyLength()];
|
||||
fieldR.asByteArray(r);
|
||||
|
||||
Point R = params.getEdOperations().basePointMultiply(r);
|
||||
|
||||
byte[] encodedR = encode(byteLength, R);
|
||||
byte[] encodedA = encode(byteLength, A);
|
||||
byte[] k = params.digest(dom, encodedR, encodedA, message);
|
||||
|
||||
// S computation is in group-order field
|
||||
IntegerFieldModuloP subField = params.getOrderField();
|
||||
IntegerModuloP kElem = subField.getElement(k);
|
||||
IntegerModuloP rElem = subField.getElement(r);
|
||||
MutableIntegerModuloP S = kElem.mutable().setProduct(sElem);
|
||||
S.setSum(rElem);
|
||||
byte[] sArr = S.asByteArray(byteLength);
|
||||
byte[] rArr = encode(byteLength, R);
|
||||
|
||||
byte[] result = new byte[byteLength * 2];
|
||||
System.arraycopy(rArr, 0, result, 0, byteLength);
|
||||
System.arraycopy(sArr, 0, result, byteLength, byteLength);
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean verify(EdDSAParameterSpec sigParams, AffinePoint affineA,
|
||||
byte[] publicKey, byte[] message, byte[] signature)
|
||||
throws SignatureException {
|
||||
|
||||
if (signature == null) {
|
||||
throw new SignatureException("signature was null");
|
||||
}
|
||||
|
||||
if (params.getKeyLength() * 2 != signature.length) {
|
||||
throw new SignatureException("signature length invalid");
|
||||
}
|
||||
|
||||
byte[] encR = Arrays.copyOf(signature, signature.length / 2);
|
||||
byte[] encS = Arrays.copyOfRange(signature, signature.length / 2,
|
||||
signature.length);
|
||||
|
||||
// reject s if it is too large
|
||||
ArrayUtil.reverse(encS);
|
||||
BigInteger bigS = new BigInteger(1, encS);
|
||||
if (bigS.compareTo(params.getOrderField().getSize()) >= 0) {
|
||||
throw new SignatureException("s is too large");
|
||||
}
|
||||
ArrayUtil.reverse(encS);
|
||||
|
||||
byte[] dom = params.dom(sigParams);
|
||||
AffinePoint affineR = decodeAffinePoint(SignatureException::new, encR);
|
||||
byte[] k = params.digest(dom, encR, publicKey, message);
|
||||
// reduce k to improve performance of multiply
|
||||
IntegerFieldModuloP subField = params.getOrderField();
|
||||
IntegerModuloP kElem = subField.getElement(k);
|
||||
k = kElem.asByteArray(k.length / 2);
|
||||
|
||||
Point pointR = params.getEdOperations().of(affineR);
|
||||
Point pointA = params.getEdOperations().of(affineA);
|
||||
|
||||
EdECOperations edOps = params.getEdOperations();
|
||||
Point lhs = edOps.basePointMultiply(encS);
|
||||
Point rhs = edOps.setSum(edOps.setProduct(pointA.mutable(), k),
|
||||
pointR.mutable());
|
||||
|
||||
return lhs.affineEquals(rhs);
|
||||
}
|
||||
|
||||
public boolean verify(EdDSAParameterSpec sigParams, byte[] publicKey,
|
||||
byte[] message, byte[] signature)
|
||||
throws InvalidKeyException, SignatureException {
|
||||
|
||||
AffinePoint affineA = decodeAffinePoint(InvalidKeyException::new,
|
||||
publicKey);
|
||||
return verify(sigParams, affineA, publicKey, message, signature);
|
||||
}
|
||||
|
||||
public
|
||||
<T extends Throwable>
|
||||
AffinePoint decodeAffinePoint(Function<String, T> exception, byte[] arr)
|
||||
throws T {
|
||||
|
||||
if (arr.length != params.getKeyLength()) {
|
||||
throw exception.apply("incorrect length");
|
||||
}
|
||||
|
||||
arr = arr.clone();
|
||||
int xLSB = (0xFF & arr[arr.length - 1]) >>> 7;
|
||||
arr[arr.length - 1] &= 0x7F;
|
||||
int yLength = (params.getBits() + 7) >> 3;
|
||||
IntegerModuloP y =
|
||||
params.getField().getElement(arr, 0, yLength, (byte) 0);
|
||||
// reject non-canonical y values
|
||||
ArrayUtil.reverse(arr);
|
||||
BigInteger bigY = new BigInteger(1, arr);
|
||||
if (bigY.compareTo(params.getField().getSize()) >= 0) {
|
||||
throw exception.apply("y value is too large");
|
||||
}
|
||||
return params.getEdOperations().decodeAffinePoint(exception, xLSB, y);
|
||||
}
|
||||
|
||||
public
|
||||
<T extends Throwable>
|
||||
AffinePoint decodeAffinePoint(Function<String, T> exception,
|
||||
EdECPoint point)
|
||||
throws T {
|
||||
|
||||
// reject non-canonical y values
|
||||
if (point.getY().compareTo(params.getField().getSize()) >= 0) {
|
||||
throw exception.apply("y value is too large");
|
||||
}
|
||||
|
||||
int xLSB = point.isXOdd() ? 1 : 0;
|
||||
IntegerModuloP y = params.getField().getElement(point.getY());
|
||||
return params.getEdOperations().decodeAffinePoint(exception, xLSB, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mask off the high order bits of an encoded integer in an array. The
|
||||
* array is modified in place.
|
||||
*
|
||||
* @param arr an array containing an encoded integer
|
||||
* @param bits the number of bits to keep
|
||||
* @return the number, in range [0,8], of bits kept in the highest byte
|
||||
*/
|
||||
private static int maskHighOrder(byte[] arr, int bits) {
|
||||
|
||||
int lastByteIndex = arr.length - 1;
|
||||
int bitsDiff = arr.length * 8 - bits;
|
||||
int highBits = 8 - bitsDiff;
|
||||
byte msbMaskOff = (byte) ((1 << highBits) - 1);
|
||||
arr[lastByteIndex] &= msbMaskOff;
|
||||
|
||||
return highBits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prune an encoded scalar value by modifying it in place. The extra
|
||||
* high-order bits are masked off, the highest valid bit it set, and the
|
||||
* number is rounded down to a multiple of the co-factor.
|
||||
*
|
||||
* @param k an encoded scalar value
|
||||
* @param bits the number of bits in the scalar
|
||||
* @param logCofactor the base-2 logarithm of the co-factor
|
||||
*/
|
||||
private static void prune(byte[] k, int bits, int logCofactor) {
|
||||
|
||||
int lastByteIndex = k.length - 1;
|
||||
|
||||
// mask off unused high-order bits
|
||||
int highBits = maskHighOrder(k, bits);
|
||||
|
||||
// set the highest bit
|
||||
if (highBits == 0) {
|
||||
k[lastByteIndex - 1] |= (byte) 0x80;
|
||||
} else {
|
||||
byte msbMaskOn = (byte) (1 << (highBits - 1));
|
||||
k[lastByteIndex] |= msbMaskOn;
|
||||
}
|
||||
|
||||
// round down to a multiple of the co-factor
|
||||
byte lsbMaskOff = (byte) (0xFF << logCofactor);
|
||||
k[0] &= lsbMaskOff;
|
||||
}
|
||||
|
||||
void prune(byte[] arr) {
|
||||
prune(arr, params.getBits(), params.getLogCofactor());
|
||||
}
|
||||
|
||||
private static byte[] encode(int length, Point p) {
|
||||
return encode(length, p.asAffine());
|
||||
}
|
||||
|
||||
private static byte[] encode(int length, AffinePoint p) {
|
||||
byte[] result = p.getY().asByteArray(length);
|
||||
int xLSB = p.getX().asByteArray(1)[0] & 0x01;
|
||||
result[result.length - 1] |= (byte) (xLSB << 7);
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,331 @@
|
|||
/*
|
||||
* Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package sun.security.ec.ed;
|
||||
|
||||
import sun.security.ec.ParametersMap;
|
||||
import sun.security.provider.SHAKE256;
|
||||
import sun.security.util.ObjectIdentifier;
|
||||
import sun.security.util.KnownOIDs;
|
||||
import sun.security.util.math.*;
|
||||
import sun.security.util.math.intpoly.*;
|
||||
import sun.security.x509.AlgorithmId;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.*;
|
||||
import java.security.spec.*;
|
||||
import java.util.function.Function;
|
||||
|
||||
/*
|
||||
* The set of parameters that defines an instance of the EdDSA signature
|
||||
* scheme.
|
||||
*/
|
||||
public class EdDSAParameters {
|
||||
|
||||
public interface DigesterFactory {
|
||||
// Default digest creator
|
||||
Digester createDigester();
|
||||
|
||||
// Override this method if multiple key lengths are needed
|
||||
default Digester createDigester(int len) {
|
||||
return createDigester();
|
||||
}
|
||||
|
||||
// Return a digest over all the provided byte arrays
|
||||
default byte[] digest(byte[]... data) {
|
||||
Digester d = createDigester();
|
||||
for (byte[] curData : data) {
|
||||
d.update(curData, 0, curData.length);
|
||||
}
|
||||
return d.digest();
|
||||
}
|
||||
}
|
||||
|
||||
// Hash for Ed25519
|
||||
private static class SHA512DigesterFactory implements DigesterFactory {
|
||||
@Override
|
||||
public Digester createDigester() {
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-512");
|
||||
return new MessageDigester(md);
|
||||
} catch (NoSuchAlgorithmException ex) {
|
||||
throw new ProviderException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Hash for Ed448
|
||||
private static class SHAKE256DigesterFactory implements DigesterFactory {
|
||||
@Override
|
||||
// Most usage for Ed448 is 114bytes long
|
||||
public Digester createDigester() {
|
||||
return new SHAKE256Digester(114);
|
||||
}
|
||||
|
||||
// Ed448 uses 64bytes long hash for the signature message
|
||||
@Override
|
||||
public Digester createDigester(int len) {
|
||||
return new SHAKE256Digester(len);
|
||||
}
|
||||
}
|
||||
|
||||
public interface Digester {
|
||||
void update(byte data);
|
||||
void update(byte[] data, int off, int len);
|
||||
byte[] digest();
|
||||
}
|
||||
|
||||
private static class MessageDigester implements Digester {
|
||||
private final MessageDigest md;
|
||||
|
||||
private MessageDigester(MessageDigest md) {
|
||||
this.md = md;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(byte data) {
|
||||
md.update(data);
|
||||
}
|
||||
@Override
|
||||
public void update(byte[] data, int off, int len) {
|
||||
md.update(data, off, len);
|
||||
}
|
||||
@Override
|
||||
public byte[] digest() {
|
||||
try {
|
||||
return md.digest();
|
||||
} finally {
|
||||
md.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class SHAKE256Digester implements Digester {
|
||||
SHAKE256 md;
|
||||
|
||||
SHAKE256Digester(int len) {
|
||||
md = new SHAKE256(len);
|
||||
}
|
||||
@Override
|
||||
public void update(byte data) {
|
||||
md.update(data);
|
||||
}
|
||||
@Override
|
||||
public void update(byte[] data, int off, int len) {
|
||||
md.update(data, off, len);
|
||||
}
|
||||
@Override
|
||||
public byte[] digest() {
|
||||
try {
|
||||
return md.digest();
|
||||
} finally {
|
||||
md.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final ParametersMap<EdDSAParameters> namedParams =
|
||||
new ParametersMap<>();
|
||||
|
||||
private final String name;
|
||||
private final ObjectIdentifier oid;
|
||||
private final IntegerFieldModuloP field;
|
||||
private final IntegerFieldModuloP orderField;
|
||||
private final ImmutableIntegerModuloP d;
|
||||
private final EdECOperations edOperations;
|
||||
private final DigesterFactory digester;
|
||||
private final int keyLength;
|
||||
private final int bits;
|
||||
private final int logCofactor;
|
||||
private final Function<EdDSAParameterSpec, byte[]> dom;
|
||||
|
||||
public EdDSAParameters(String name, ObjectIdentifier oid,
|
||||
IntegerFieldModuloP field,
|
||||
IntegerFieldModuloP orderField,
|
||||
ImmutableIntegerModuloP d,
|
||||
EdECOperations edOps,
|
||||
DigesterFactory digester,
|
||||
Function<EdDSAParameterSpec, byte[]> dom,
|
||||
int keyLength, int bits, int logCofactor) {
|
||||
this.oid = oid;
|
||||
this.name = name;
|
||||
this.field = field;
|
||||
this.orderField = orderField;
|
||||
this.d = d;
|
||||
this.edOperations = edOps;
|
||||
this.digester = digester;
|
||||
this.keyLength = keyLength;
|
||||
this.bits = bits;
|
||||
this.logCofactor = logCofactor;
|
||||
this.dom = dom;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
public ObjectIdentifier getOid() {
|
||||
return oid;
|
||||
}
|
||||
public IntegerFieldModuloP getField() {
|
||||
return field;
|
||||
}
|
||||
public IntegerFieldModuloP getOrderField() {
|
||||
return orderField;
|
||||
}
|
||||
public ImmutableIntegerModuloP getD() {
|
||||
return d;
|
||||
}
|
||||
public EdECOperations getEdOperations() {
|
||||
return edOperations;
|
||||
}
|
||||
public int getKeyLength() {
|
||||
return keyLength;
|
||||
}
|
||||
public int getBits() {
|
||||
return bits;
|
||||
}
|
||||
public int getLogCofactor() {
|
||||
return logCofactor;
|
||||
}
|
||||
|
||||
public Digester createDigester() {
|
||||
return digester.createDigester();
|
||||
}
|
||||
|
||||
public Digester createDigester(int len) {
|
||||
return digester.createDigester(len);
|
||||
}
|
||||
|
||||
public byte[] digest(byte[]... data) {
|
||||
return digester.digest(data);
|
||||
}
|
||||
|
||||
public byte[] dom(EdDSAParameterSpec sigParams) {
|
||||
return dom.apply(sigParams);
|
||||
}
|
||||
|
||||
private static final String prefixStr25519 =
|
||||
"SigEd25519 no Ed25519 collisions";
|
||||
private static final String prefixStr448 = "SigEd448";
|
||||
|
||||
// Used for Ed25519
|
||||
static byte[] dom2(EdDSAParameterSpec sigParams) {
|
||||
if (!sigParams.isPrehash() && !sigParams.getContext().isPresent()) {
|
||||
return new byte[0];
|
||||
}
|
||||
return domImpl(prefixStr25519, sigParams);
|
||||
}
|
||||
|
||||
// Used for Ed488
|
||||
static byte[] dom4(EdDSAParameterSpec sigParams) {
|
||||
return domImpl(prefixStr448, sigParams);
|
||||
}
|
||||
|
||||
static byte[] domImpl(String prefixStr, EdDSAParameterSpec sigParams) {
|
||||
byte[] prefix = prefixStr.getBytes(StandardCharsets.US_ASCII);
|
||||
byte[] context = sigParams.getContext().orElse(new byte[0]);
|
||||
int length = prefix.length + 2 + context.length;
|
||||
byte[] result = new byte[length];
|
||||
System.arraycopy(prefix, 0, result, 0, prefix.length);
|
||||
byte x = (byte) (sigParams.isPrehash() ? 1 : 0);
|
||||
result[prefix.length] = x;
|
||||
result[prefix.length + 1] = (byte) context.length;
|
||||
System.arraycopy(context, 0, result, prefix.length + 2,
|
||||
context.length);
|
||||
return result;
|
||||
}
|
||||
|
||||
static {
|
||||
// set up Ed25519
|
||||
IntegerFieldModuloP ed25519Field = IntegerPolynomial25519.ONE;
|
||||
IntegerFieldModuloP ed25519OrderField = Curve25519OrderField.ONE;
|
||||
BigInteger biD = new BigInteger("3709570593466943934313808350875" +
|
||||
"4565189542113879843219016388785533085940283555");
|
||||
ImmutableIntegerModuloP d = ed25519Field.getElement(biD);
|
||||
BigInteger baseX = new BigInteger("15112221349535400772501151409" +
|
||||
"588531511454012693041857206046113283949847762202");
|
||||
BigInteger baseY = new BigInteger("46316835694926478169428394003" +
|
||||
"475163141307993866256225615783033603165251855960");
|
||||
EdECOperations edOps = new Ed25519Operations(d, baseX, baseY);
|
||||
String name = NamedParameterSpec.ED25519.getName();
|
||||
ObjectIdentifier oid = ObjectIdentifier.of(KnownOIDs.Ed25519);
|
||||
int bits = 255;
|
||||
DigesterFactory digester = new SHA512DigesterFactory();
|
||||
EdDSAParameters params = new EdDSAParameters(name, oid,
|
||||
ed25519Field, ed25519OrderField, d, edOps,
|
||||
digester, EdDSAParameters::dom2, 32, bits, 3);
|
||||
|
||||
namedParams.put(name, oid, bits, params);
|
||||
|
||||
// set up Ed448
|
||||
IntegerFieldModuloP ed448Field = IntegerPolynomial448.ONE;
|
||||
IntegerFieldModuloP ed448OrderField = Curve448OrderField.ONE;
|
||||
biD = ed448Field.getSize().subtract(new BigInteger("39081"));
|
||||
d = ed448Field.getElement(biD);
|
||||
baseX = new BigInteger("224580040295924300187604334" +
|
||||
"099896036246789641632564134246125461686950415467406032909" +
|
||||
"029192869357953282578032075146446173674602635247710");
|
||||
baseY = new BigInteger("298819210078481492676017930" +
|
||||
"443930673437544040154080242095928241372331506189835876003" +
|
||||
"536878655418784733982303233503462500531545062832660");
|
||||
edOps = new Ed448Operations(d, baseX, baseY);
|
||||
name = NamedParameterSpec.ED448.getName();
|
||||
oid = ObjectIdentifier.of(KnownOIDs.Ed448);
|
||||
bits = 448;
|
||||
digester = new SHAKE256DigesterFactory();
|
||||
params = new EdDSAParameters(name, oid,
|
||||
ed448Field, ed448OrderField, d, edOps,
|
||||
digester, EdDSAParameters::dom4, 57, bits, 2);
|
||||
|
||||
namedParams.put(name, oid, bits, params);
|
||||
|
||||
namedParams.fix();
|
||||
}
|
||||
|
||||
public static
|
||||
<T extends Throwable>
|
||||
EdDSAParameters getBySize(Function<String, T> exception,
|
||||
int size) throws T {
|
||||
|
||||
return namedParams.getBySize(exception, size);
|
||||
}
|
||||
|
||||
public static
|
||||
<T extends Throwable>
|
||||
EdDSAParameters get(Function<String, T> exception,
|
||||
AlgorithmId algId) throws T {
|
||||
|
||||
return namedParams.get(exception, algId);
|
||||
}
|
||||
|
||||
public static
|
||||
<T extends Throwable>
|
||||
EdDSAParameters get(Function<String, T> exception,
|
||||
AlgorithmParameterSpec params) throws T {
|
||||
|
||||
return namedParams.get(exception, params);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ec.ed;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.interfaces.EdECPrivateKey;
|
||||
import java.util.Optional;
|
||||
import java.security.spec.NamedParameterSpec;
|
||||
|
||||
import sun.security.pkcs.PKCS8Key;
|
||||
import sun.security.x509.AlgorithmId;
|
||||
import sun.security.util.*;
|
||||
|
||||
public final class EdDSAPrivateKeyImpl
|
||||
extends PKCS8Key implements EdECPrivateKey {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@SuppressWarnings("serial") // Type of field is not Serializable
|
||||
private final NamedParameterSpec paramSpec;
|
||||
private byte[] h;
|
||||
|
||||
EdDSAPrivateKeyImpl(EdDSAParameters params, byte[] h)
|
||||
throws InvalidKeyException {
|
||||
|
||||
this.paramSpec = new NamedParameterSpec(params.getName());
|
||||
this.algid = new AlgorithmId(params.getOid());
|
||||
this.h = h.clone();
|
||||
|
||||
DerValue val = new DerValue(DerValue.tag_OctetString, h);
|
||||
try {
|
||||
this.key = val.toByteArray();
|
||||
} finally {
|
||||
val.clear();
|
||||
}
|
||||
checkLength(params);
|
||||
}
|
||||
|
||||
EdDSAPrivateKeyImpl(byte[] encoded) throws InvalidKeyException {
|
||||
|
||||
super(encoded);
|
||||
EdDSAParameters params = EdDSAParameters.get(
|
||||
InvalidKeyException::new, algid);
|
||||
paramSpec = new NamedParameterSpec(params.getName());
|
||||
|
||||
try {
|
||||
DerInputStream derStream = new DerInputStream(key);
|
||||
h = derStream.getOctetString();
|
||||
} catch (IOException ex) {
|
||||
throw new InvalidKeyException(ex);
|
||||
}
|
||||
checkLength(params);
|
||||
}
|
||||
|
||||
void checkLength(EdDSAParameters params) throws InvalidKeyException {
|
||||
|
||||
if (params.getKeyLength() != this.h.length) {
|
||||
throw new InvalidKeyException("key length is " + this.h.length +
|
||||
", key length must be " + params.getKeyLength());
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] getKey() {
|
||||
return h.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlgorithm() {
|
||||
return "EdDSA";
|
||||
}
|
||||
|
||||
@Override
|
||||
public NamedParameterSpec getParams() {
|
||||
return paramSpec;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<byte[]> getBytes() {
|
||||
return Optional.of(getKey());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ec.ed;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.KeyRep;
|
||||
import java.security.interfaces.EdECPublicKey;
|
||||
import java.security.spec.EdECPoint;
|
||||
import java.security.spec.NamedParameterSpec;
|
||||
import java.util.Arrays;
|
||||
|
||||
import sun.security.util.BitArray;
|
||||
import sun.security.x509.AlgorithmId;
|
||||
import sun.security.x509.X509Key;
|
||||
|
||||
public final class EdDSAPublicKeyImpl extends X509Key implements EdECPublicKey {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@SuppressWarnings("serial") // Type of field is not Serializable
|
||||
private final EdECPoint point;
|
||||
@SuppressWarnings("serial") // Type of field is not Serializable
|
||||
private final NamedParameterSpec paramSpec;
|
||||
|
||||
public EdDSAPublicKeyImpl(EdDSAParameters params, EdECPoint point)
|
||||
throws InvalidKeyException {
|
||||
this.paramSpec = new NamedParameterSpec(params.getName());
|
||||
this.algid = new AlgorithmId(params.getOid());
|
||||
this.point = point;
|
||||
|
||||
byte[] encodedPoint = point.getY().toByteArray();
|
||||
reverse(encodedPoint);
|
||||
// array may be too large or too small, depending on the value
|
||||
encodedPoint = Arrays.copyOf(encodedPoint, params.getKeyLength());
|
||||
// set the high-order bit of the encoded point
|
||||
byte msb = (byte) (point.isXOdd() ? 0x80 : 0);
|
||||
encodedPoint[encodedPoint.length - 1] |= msb;
|
||||
setKey(new BitArray(encodedPoint.length * 8, encodedPoint));
|
||||
|
||||
checkLength(params);
|
||||
}
|
||||
|
||||
public EdDSAPublicKeyImpl(byte[] encoded) throws InvalidKeyException {
|
||||
decode(encoded);
|
||||
|
||||
EdDSAParameters params =
|
||||
EdDSAParameters.get(InvalidKeyException::new, algid);
|
||||
this.paramSpec = new NamedParameterSpec(params.getName());
|
||||
// construct the EdECPoint representation
|
||||
byte[] encodedPoint = getKey().toByteArray();
|
||||
byte msb = encodedPoint[encodedPoint.length - 1];
|
||||
encodedPoint[encodedPoint.length - 1] &= (byte) 0x7F;
|
||||
boolean xOdd = (msb & 0x80) != 0;
|
||||
reverse(encodedPoint);
|
||||
BigInteger y = new BigInteger(1, encodedPoint);
|
||||
this.point = new EdECPoint(xOdd, y);
|
||||
|
||||
checkLength(params);
|
||||
}
|
||||
|
||||
void checkLength(EdDSAParameters params) throws InvalidKeyException {
|
||||
if (params.getKeyLength() * 8 != getKey().length()) {
|
||||
throw new InvalidKeyException(
|
||||
"key length must be " + params.getKeyLength());
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] getEncodedPoint() {
|
||||
return getKey().toByteArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EdECPoint getPoint() {
|
||||
return point;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NamedParameterSpec getParams() {
|
||||
return paramSpec;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlgorithm() {
|
||||
return "EdDSA";
|
||||
}
|
||||
|
||||
protected Object writeReplace() throws java.io.ObjectStreamException {
|
||||
return new KeyRep(KeyRep.Type.PUBLIC, getAlgorithm(), getFormat(),
|
||||
getEncoded());
|
||||
}
|
||||
|
||||
private static void swap(byte[] arr, int i, int j) {
|
||||
byte tmp = arr[i];
|
||||
arr[i] = arr[j];
|
||||
arr[j] = tmp;
|
||||
}
|
||||
|
||||
private static void reverse(byte [] arr) {
|
||||
int i = 0;
|
||||
int j = arr.length - 1;
|
||||
|
||||
while (i < j) {
|
||||
swap(arr, i, j);
|
||||
i++;
|
||||
j--;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,312 @@
|
|||
/*
|
||||
* Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ec.ed;
|
||||
|
||||
import sun.security.ec.point.AffinePoint;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.security.AlgorithmParameters;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.InvalidParameterException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.ProviderException;
|
||||
import java.security.PublicKey;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.SignatureException;
|
||||
import java.security.SignatureSpi;
|
||||
import java.security.interfaces.EdECPrivateKey;
|
||||
import java.security.interfaces.EdECPublicKey;
|
||||
import java.security.spec.AlgorithmParameterSpec;
|
||||
import java.security.spec.EdDSAParameterSpec;
|
||||
import java.security.spec.NamedParameterSpec;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class EdDSASignature extends SignatureSpi {
|
||||
|
||||
private interface MessageAccumulator {
|
||||
void add(byte b);
|
||||
void add(byte[] data, int off, int len);
|
||||
byte[] getMessage();
|
||||
}
|
||||
|
||||
private static class DigestAccumulator implements MessageAccumulator {
|
||||
private final EdDSAParameters.Digester digester;
|
||||
|
||||
DigestAccumulator(EdDSAParameters.Digester digester) {
|
||||
this.digester = digester;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(byte b) {
|
||||
digester.update(b);
|
||||
}
|
||||
@Override
|
||||
public void add(byte[] data, int off, int len) {
|
||||
digester.update(data, off, len);
|
||||
}
|
||||
@Override
|
||||
public byte[] getMessage() {
|
||||
return digester.digest();
|
||||
}
|
||||
}
|
||||
|
||||
private static class MemoryAccumulator implements MessageAccumulator {
|
||||
ByteArrayOutputStream message = new ByteArrayOutputStream();
|
||||
|
||||
@Override
|
||||
public void add(byte b) {
|
||||
message.write(b);
|
||||
}
|
||||
@Override
|
||||
public void add(byte[] data, int off, int len) {
|
||||
message.write(data, off, len);
|
||||
}
|
||||
@Override
|
||||
public byte[] getMessage() {
|
||||
return message.toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] privateKey;
|
||||
private AffinePoint publicKeyPoint;
|
||||
private byte[] publicKeyBytes;
|
||||
private EdDSAOperations ops;
|
||||
private EdDSAParameters lockedParams = null;
|
||||
private MessageAccumulator message = null;
|
||||
private EdDSAParameterSpec sigParams = new EdDSAParameterSpec(false);
|
||||
|
||||
public EdDSASignature() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
EdDSASignature(NamedParameterSpec paramSpec) {
|
||||
lockedParams = EdDSAParameters.get(ProviderException::new, paramSpec);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void engineInitVerify(PublicKey publicKey)
|
||||
throws InvalidKeyException {
|
||||
|
||||
if (!(publicKey instanceof EdECPublicKey)) {
|
||||
throw new InvalidKeyException("Unsupported key type");
|
||||
}
|
||||
EdECPublicKey edKey = (EdECPublicKey) publicKey;
|
||||
EdDSAParameters params = EdDSAParameters.get(
|
||||
InvalidKeyException::new, edKey.getParams());
|
||||
|
||||
initImpl(params);
|
||||
this.privateKey = null;
|
||||
this.publicKeyPoint = ops.decodeAffinePoint(InvalidKeyException::new,
|
||||
edKey.getPoint());
|
||||
EdDSAPublicKeyImpl pubKeyImpl = new EdDSAPublicKeyImpl(params,
|
||||
edKey.getPoint());
|
||||
this.publicKeyBytes = pubKeyImpl.getEncodedPoint();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void engineInitSign(PrivateKey privateKey)
|
||||
throws InvalidKeyException {
|
||||
engineInitSign(privateKey, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void engineInitSign(PrivateKey privateKey, SecureRandom random)
|
||||
throws InvalidKeyException {
|
||||
|
||||
if (!(privateKey instanceof EdECPrivateKey)) {
|
||||
throw new InvalidKeyException("Unsupported key type");
|
||||
}
|
||||
EdECPrivateKey edKey = (EdECPrivateKey) privateKey;
|
||||
|
||||
initImpl(edKey.getParams());
|
||||
this.privateKey = edKey.getBytes().orElseThrow(
|
||||
() -> new InvalidKeyException("No private key value"));
|
||||
this.publicKeyPoint = null;
|
||||
this.publicKeyBytes = null;
|
||||
}
|
||||
|
||||
private
|
||||
<T extends Throwable>
|
||||
void checkLockedParams(Function<String, T> exception,
|
||||
EdDSAParameters params) throws T {
|
||||
if (lockedParams != null && lockedParams != params) {
|
||||
throw exception.apply("Parameters must be " +
|
||||
lockedParams.getName());
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureMessageInit() throws SignatureException {
|
||||
if (message == null) {
|
||||
initMessage();
|
||||
}
|
||||
}
|
||||
|
||||
private void initMessage() throws SignatureException {
|
||||
if (this.ops == null) {
|
||||
throw new SignatureException("not initialized");
|
||||
}
|
||||
EdDSAParameters params = ops.getParameters();
|
||||
|
||||
if (sigParams.isPrehash()) {
|
||||
this.message = new DigestAccumulator(params.createDigester(64));
|
||||
} else {
|
||||
this.message = new MemoryAccumulator();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void engineUpdate(byte b) throws SignatureException {
|
||||
ensureMessageInit();
|
||||
this.message.add(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void engineUpdate(byte[] b, int off, int len)
|
||||
throws SignatureException {
|
||||
|
||||
ensureMessageInit();
|
||||
this.message.add(b, off, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] engineSign() throws SignatureException {
|
||||
if (privateKey == null) {
|
||||
throw new SignatureException("Missing private key");
|
||||
}
|
||||
ensureMessageInit();
|
||||
byte[] result = ops.sign(this.sigParams, this.privateKey,
|
||||
message.getMessage());
|
||||
message = null;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean engineVerify(byte[] sigBytes) throws SignatureException {
|
||||
if (publicKeyBytes == null) {
|
||||
throw new SignatureException("Missing publicKey");
|
||||
}
|
||||
ensureMessageInit();
|
||||
boolean result = ops.verify(this.sigParams, this.publicKeyPoint,
|
||||
this.publicKeyBytes, message.getMessage(), sigBytes);
|
||||
message = null;
|
||||
return result;
|
||||
}
|
||||
|
||||
private void initImpl(EdDSAParameters params) throws InvalidKeyException {
|
||||
checkLockedParams(InvalidKeyException::new, params);
|
||||
|
||||
try {
|
||||
this.ops = new EdDSAOperations(params);
|
||||
} catch (NoSuchAlgorithmException ex) {
|
||||
throw new ProviderException(ex);
|
||||
}
|
||||
// message is (re)set to null
|
||||
// it will be initialized on first update
|
||||
this.message = null;
|
||||
}
|
||||
|
||||
private void initImpl(NamedParameterSpec paramSpec)
|
||||
throws InvalidKeyException {
|
||||
|
||||
EdDSAParameters params = EdDSAParameters.get(
|
||||
InvalidKeyException::new, paramSpec);
|
||||
initImpl(params);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
protected Object engineGetParameter(String param)
|
||||
throws InvalidParameterException {
|
||||
throw new UnsupportedOperationException("getParameter() not supported");
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
protected void engineSetParameter(String param, Object value)
|
||||
throws InvalidParameterException {
|
||||
|
||||
throw new UnsupportedOperationException("setParameter() not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void engineSetParameter(AlgorithmParameterSpec params)
|
||||
throws InvalidAlgorithmParameterException {
|
||||
|
||||
// by convention, ignore null parameters
|
||||
if (params == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (params instanceof EdDSAParameterSpec) {
|
||||
if (message != null) {
|
||||
// sign/verify in progress
|
||||
throw new InvalidParameterException("Cannot change signature " +
|
||||
"parameters during operation");
|
||||
}
|
||||
EdDSAParameterSpec edDsaParams = (EdDSAParameterSpec) params;
|
||||
checkContextLength(edDsaParams);
|
||||
|
||||
this.sigParams = edDsaParams;
|
||||
} else {
|
||||
throw new InvalidAlgorithmParameterException(
|
||||
"Only EdDSAParameterSpec supported");
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkContextLength(EdDSAParameterSpec edDsaParams)
|
||||
throws InvalidAlgorithmParameterException {
|
||||
|
||||
if (edDsaParams.getContext().isPresent()) {
|
||||
byte[] context = edDsaParams.getContext().get();
|
||||
if (context.length > 255) {
|
||||
throw new InvalidAlgorithmParameterException(
|
||||
"Context is longer than 255 bytes");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// There is no RFC-defined ASN.1 for prehash and context (RFC 8410)
|
||||
@Override
|
||||
protected AlgorithmParameters engineGetParameters() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static class Ed25519 extends EdDSASignature {
|
||||
|
||||
public Ed25519() {
|
||||
super(NamedParameterSpec.ED25519);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Ed448 extends EdDSASignature {
|
||||
|
||||
public Ed448() {
|
||||
super(NamedParameterSpec.ED448);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ec.ed;
|
||||
|
||||
import sun.security.ec.point.*;
|
||||
import sun.security.util.math.IntegerModuloP;
|
||||
import sun.security.util.math.MutableIntegerModuloP;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
/*
|
||||
* Base class for Edwards curve ECC implementations.
|
||||
*/
|
||||
public abstract class EdECOperations {
|
||||
|
||||
// Curve-specific base point multiplication.
|
||||
public abstract Point basePointMultiply(byte[] s);
|
||||
|
||||
// Decode curve-specifics to the affinePoint
|
||||
public abstract <T extends Throwable>
|
||||
AffinePoint decodeAffinePoint(Function<String, T> exception,
|
||||
int xLSB, IntegerModuloP y) throws T;
|
||||
|
||||
// Curve specific point from an X,Y point
|
||||
public abstract ImmutablePoint of(AffinePoint p);
|
||||
|
||||
/*
|
||||
* Generic method for taking two classes implementing MutablePoint to be
|
||||
* called by the curve-specific setSum()
|
||||
*/
|
||||
public MutablePoint setSum(MutablePoint p1, MutablePoint p2) {
|
||||
MutableIntegerModuloP t1 = p2.getField().get1().mutable();
|
||||
MutableIntegerModuloP t2 = p2.getField().get1().mutable();
|
||||
MutableIntegerModuloP t3 = p2.getField().get1().mutable();
|
||||
return setSum(p1, p2, t1, t2, t3);
|
||||
}
|
||||
|
||||
/*
|
||||
* Generic method for taking a class implementing MutablePoint with a
|
||||
* scalar to returning the point product using curve-specific methods.
|
||||
*/
|
||||
public MutablePoint setProduct(MutablePoint p1, byte[] s) {
|
||||
MutablePoint p = p1.mutable();
|
||||
p1.setValue(getNeutral());
|
||||
MutablePoint addResult = getNeutral().mutable();
|
||||
MutableIntegerModuloP t1 = p.getField().get0().mutable();
|
||||
MutableIntegerModuloP t2 = p.getField().get0().mutable();
|
||||
MutableIntegerModuloP t3 = p.getField().get0().mutable();
|
||||
|
||||
for (int i = 0; i < s.length * 8; i++) {
|
||||
addResult.setValue(p1);
|
||||
setSum(addResult, p, t1, t2, t3);
|
||||
int swap = bitAt(s, i);
|
||||
p1.conditionalSet(addResult, swap);
|
||||
setDouble(p, t1, t2);
|
||||
}
|
||||
|
||||
return p1;
|
||||
}
|
||||
|
||||
// Abstract method for constructing the neutral point on the curve
|
||||
protected abstract ImmutablePoint getNeutral();
|
||||
|
||||
|
||||
// Abstract method for Curve-specific point addition
|
||||
protected abstract MutablePoint setSum(MutablePoint p1, MutablePoint p2,
|
||||
MutableIntegerModuloP t1,
|
||||
MutableIntegerModuloP t2,
|
||||
MutableIntegerModuloP t3);
|
||||
// Abstract method for Curve-specific point doubling
|
||||
protected abstract MutablePoint setDouble(MutablePoint p,
|
||||
MutableIntegerModuloP t1,
|
||||
MutableIntegerModuloP t2);
|
||||
|
||||
private static int bitAt(byte[] arr, int index) {
|
||||
int byteIndex = index / 8;
|
||||
int bitIndex = index % 8;
|
||||
return (arr[byteIndex] & (1 << bitIndex)) >> bitIndex;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package sun.security.ec.point;
|
||||
|
||||
import sun.security.util.math.ImmutableIntegerModuloP;
|
||||
import sun.security.util.math.IntegerFieldModuloP;
|
||||
|
||||
import java.security.spec.ECPoint;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Elliptic curve point represented using affine coordinates (x, y). This class
|
||||
* is not part of the sun.security.ec.point.Point hierarchy because it is not
|
||||
* used to hold intermediate values during point arithmetic, and so it does not
|
||||
* have a mutable form.
|
||||
*/
|
||||
public class AffinePoint {
|
||||
|
||||
private final ImmutableIntegerModuloP x;
|
||||
private final ImmutableIntegerModuloP y;
|
||||
|
||||
public AffinePoint(ImmutableIntegerModuloP x, ImmutableIntegerModuloP y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
public static AffinePoint fromECPoint(
|
||||
ECPoint ecPoint, IntegerFieldModuloP field) {
|
||||
return new AffinePoint(
|
||||
field.getElement(ecPoint.getAffineX()),
|
||||
field.getElement(ecPoint.getAffineY()));
|
||||
}
|
||||
|
||||
public ECPoint toECPoint() {
|
||||
return new ECPoint(x.asBigInteger(), y.asBigInteger());
|
||||
}
|
||||
|
||||
public ImmutableIntegerModuloP getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public ImmutableIntegerModuloP getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof AffinePoint)) {
|
||||
return false;
|
||||
}
|
||||
AffinePoint p = (AffinePoint) obj;
|
||||
boolean xEquals = x.asBigInteger().equals(p.x.asBigInteger());
|
||||
boolean yEquals = y.asBigInteger().equals(p.y.asBigInteger());
|
||||
return xEquals && yEquals;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "(" + x.asBigInteger().toString() + "," +
|
||||
y.asBigInteger().toString() + ")";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package sun.security.ec.point;
|
||||
|
||||
import sun.security.util.math.*;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* Elliptic curve point in extended homogeneous coordinates (X, Y, T, Z) where
|
||||
* an affine point (x, y) is represented using any (X, Y, T, Z) s.t.
|
||||
* x = X/Z, y = Y/Z, and x*y = T/Z.
|
||||
*/
|
||||
public abstract class ExtendedHomogeneousPoint
|
||||
<T extends IntegerModuloP> implements Point {
|
||||
|
||||
protected final T x;
|
||||
protected final T y;
|
||||
protected final T t;
|
||||
protected final T z;
|
||||
|
||||
protected ExtendedHomogeneousPoint(T x, T y, T t, T z) {
|
||||
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.t = t;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntegerFieldModuloP getField() {
|
||||
return this.x.getField();
|
||||
}
|
||||
@Override
|
||||
public Immutable fixed() {
|
||||
return new Immutable(x.fixed(), y.fixed(), t.fixed(), z.fixed());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mutable mutable() {
|
||||
return new Mutable(x.mutable(), y.mutable(), t.mutable(), z.mutable());
|
||||
}
|
||||
|
||||
public T getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public T getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public T getT() {
|
||||
return t;
|
||||
}
|
||||
|
||||
public T getZ() {
|
||||
return z;
|
||||
}
|
||||
|
||||
public AffinePoint asAffine() {
|
||||
IntegerModuloP zInv = z.multiplicativeInverse();
|
||||
return new AffinePoint(x.multiply(zInv), y.multiply(zInv));
|
||||
}
|
||||
|
||||
private static
|
||||
<T1 extends IntegerModuloP, T2 extends IntegerModuloP>
|
||||
boolean affineEquals(ExtendedHomogeneousPoint<T1> p1,
|
||||
ExtendedHomogeneousPoint<T2> p2) {
|
||||
MutableIntegerModuloP x1 = p1.getX().mutable().setProduct(p2.getZ());
|
||||
MutableIntegerModuloP x2 = p2.getX().mutable().setProduct(p1.getZ());
|
||||
if (!x1.asBigInteger().equals(x2.asBigInteger())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MutableIntegerModuloP y1 = p1.getY().mutable().setProduct(p2.getZ());
|
||||
MutableIntegerModuloP y2 = p2.getY().mutable().setProduct(p1.getZ());
|
||||
if (!y1.asBigInteger().equals(y2.asBigInteger())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean affineEquals(Point p) {
|
||||
if (p instanceof ExtendedHomogeneousPoint) {
|
||||
@SuppressWarnings("unchecked")
|
||||
ExtendedHomogeneousPoint<IntegerModuloP> ehp =
|
||||
(ExtendedHomogeneousPoint<IntegerModuloP>) p;
|
||||
return affineEquals(this, ehp);
|
||||
}
|
||||
|
||||
return asAffine().equals(p.asAffine());
|
||||
}
|
||||
|
||||
public static class Immutable
|
||||
extends ExtendedHomogeneousPoint<ImmutableIntegerModuloP>
|
||||
implements ImmutablePoint {
|
||||
|
||||
public Immutable(ImmutableIntegerModuloP x,
|
||||
ImmutableIntegerModuloP y,
|
||||
ImmutableIntegerModuloP t,
|
||||
ImmutableIntegerModuloP z) {
|
||||
super(x, y, t, z);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Mutable
|
||||
extends ExtendedHomogeneousPoint<MutableIntegerModuloP>
|
||||
implements MutablePoint {
|
||||
|
||||
public Mutable(MutableIntegerModuloP x,
|
||||
MutableIntegerModuloP y,
|
||||
MutableIntegerModuloP t,
|
||||
MutableIntegerModuloP z) {
|
||||
super(x, y, t, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mutable conditionalSet(Point p, int set) {
|
||||
if (!(p instanceof ExtendedHomogeneousPoint)) {
|
||||
throw new RuntimeException("Incompatible point");
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
ExtendedHomogeneousPoint<IntegerModuloP> ehp =
|
||||
(ExtendedHomogeneousPoint<IntegerModuloP>) p;
|
||||
return conditionalSet(ehp, set);
|
||||
}
|
||||
|
||||
private <T extends IntegerModuloP>
|
||||
Mutable conditionalSet(ExtendedHomogeneousPoint<T> ehp, int set) {
|
||||
|
||||
x.conditionalSet(ehp.x, set);
|
||||
y.conditionalSet(ehp.y, set);
|
||||
t.conditionalSet(ehp.t, set);
|
||||
z.conditionalSet(ehp.z, set);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mutable setValue(AffinePoint p) {
|
||||
x.setValue(p.getX());
|
||||
y.setValue(p.getY());
|
||||
t.setValue(p.getX()).setProduct(p.getY());
|
||||
z.setValue(p.getX().getField().get1());
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mutable setValue(Point p) {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
ExtendedHomogeneousPoint<IntegerModuloP> ehp =
|
||||
(ExtendedHomogeneousPoint<IntegerModuloP>) p;
|
||||
|
||||
return setValue(ehp);
|
||||
}
|
||||
|
||||
private <T extends IntegerModuloP>
|
||||
Mutable setValue(ExtendedHomogeneousPoint<T> ehp) {
|
||||
|
||||
x.setValue(ehp.x);
|
||||
y.setValue(ehp.y);
|
||||
t.setValue(ehp.t);
|
||||
z.setValue(ehp.z);
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ec.point;
|
||||
|
||||
/**
|
||||
* An interface for immutable points on an elliptic curve over a finite field.
|
||||
*/
|
||||
public interface ImmutablePoint extends Point {
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ec.point;
|
||||
|
||||
/**
|
||||
* An interface for mutable points on an elliptic curve over a finite field.
|
||||
*/
|
||||
public interface MutablePoint extends Point {
|
||||
|
||||
MutablePoint setValue(AffinePoint p);
|
||||
MutablePoint setValue(Point p);
|
||||
MutablePoint conditionalSet(Point p, int set);
|
||||
|
||||
}
|
46
src/java.base/share/classes/sun/security/ec/point/Point.java
Normal file
46
src/java.base/share/classes/sun/security/ec/point/Point.java
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ec.point;
|
||||
|
||||
import sun.security.util.math.IntegerFieldModuloP;
|
||||
|
||||
/**
|
||||
* A base interface for points on an elliptic curve over a finite field.
|
||||
* Implementations may use different representations for points, and this
|
||||
* interface creates a common API for manipulating points. This API has no
|
||||
* methods for point arithmetic, which depends on group structure and curve
|
||||
* parameters in addition to point representation.
|
||||
*/
|
||||
public interface Point {
|
||||
|
||||
IntegerFieldModuloP getField();
|
||||
AffinePoint asAffine();
|
||||
boolean affineEquals(Point p);
|
||||
|
||||
ImmutablePoint fixed();
|
||||
MutablePoint mutable();
|
||||
|
||||
}
|
|
@ -0,0 +1,190 @@
|
|||
/*
|
||||
* Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package sun.security.ec.point;
|
||||
|
||||
import sun.security.util.math.*;
|
||||
|
||||
/**
|
||||
* Elliptic curve point in projective coordinates (X, Y, Z) where
|
||||
* an affine point (x, y) is represented using any (X, Y, Z) s.t.
|
||||
* x = X/Z and y = Y/Z.
|
||||
*/
|
||||
public abstract class ProjectivePoint
|
||||
<T extends IntegerModuloP> implements Point {
|
||||
|
||||
protected final T x;
|
||||
protected final T y;
|
||||
protected final T z;
|
||||
|
||||
protected ProjectivePoint(T x, T y, T z) {
|
||||
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntegerFieldModuloP getField() {
|
||||
return this.x.getField();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Immutable fixed() {
|
||||
return new Immutable(x.fixed(), y.fixed(), z.fixed());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mutable mutable() {
|
||||
return new Mutable(x.mutable(), y.mutable(), z.mutable());
|
||||
}
|
||||
|
||||
public T getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public T getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public T getZ() {
|
||||
return z;
|
||||
}
|
||||
|
||||
public AffinePoint asAffine() {
|
||||
IntegerModuloP zInv = z.multiplicativeInverse();
|
||||
return new AffinePoint(x.multiply(zInv), y.multiply(zInv));
|
||||
}
|
||||
|
||||
private static
|
||||
<T1 extends IntegerModuloP, T2 extends IntegerModuloP>
|
||||
boolean affineEquals(ProjectivePoint<T1> p1,
|
||||
ProjectivePoint<T2> p2) {
|
||||
MutableIntegerModuloP x1 = p1.getX().mutable().setProduct(p2.getZ());
|
||||
MutableIntegerModuloP x2 = p2.getX().mutable().setProduct(p1.getZ());
|
||||
if (!x1.asBigInteger().equals(x2.asBigInteger())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MutableIntegerModuloP y1 = p1.getY().mutable().setProduct(p2.getZ());
|
||||
MutableIntegerModuloP y2 = p2.getY().mutable().setProduct(p1.getZ());
|
||||
if (!y1.asBigInteger().equals(y2.asBigInteger())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean affineEquals(Point p) {
|
||||
if (p instanceof ProjectivePoint) {
|
||||
@SuppressWarnings("unchecked")
|
||||
ProjectivePoint<IntegerModuloP> pp =
|
||||
(ProjectivePoint<IntegerModuloP>) p;
|
||||
return affineEquals(this, pp);
|
||||
}
|
||||
|
||||
return asAffine().equals(p.asAffine());
|
||||
}
|
||||
|
||||
public static class Immutable
|
||||
extends ProjectivePoint<ImmutableIntegerModuloP>
|
||||
implements ImmutablePoint {
|
||||
|
||||
public Immutable(ImmutableIntegerModuloP x,
|
||||
ImmutableIntegerModuloP y,
|
||||
ImmutableIntegerModuloP z) {
|
||||
super(x, y, z);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Mutable
|
||||
extends ProjectivePoint<MutableIntegerModuloP>
|
||||
implements MutablePoint {
|
||||
|
||||
public Mutable(MutableIntegerModuloP x,
|
||||
MutableIntegerModuloP y,
|
||||
MutableIntegerModuloP z) {
|
||||
super(x, y, z);
|
||||
}
|
||||
|
||||
public Mutable(IntegerFieldModuloP field) {
|
||||
super(field.get0().mutable(),
|
||||
field.get0().mutable(),
|
||||
field.get0().mutable());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mutable conditionalSet(Point p, int set) {
|
||||
if (!(p instanceof ProjectivePoint)) {
|
||||
throw new RuntimeException("Incompatible point");
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
ProjectivePoint<IntegerModuloP> pp =
|
||||
(ProjectivePoint<IntegerModuloP>) p;
|
||||
return conditionalSet(pp, set);
|
||||
}
|
||||
|
||||
private <T extends IntegerModuloP>
|
||||
Mutable conditionalSet(ProjectivePoint<T> pp, int set) {
|
||||
|
||||
x.conditionalSet(pp.x, set);
|
||||
y.conditionalSet(pp.y, set);
|
||||
z.conditionalSet(pp.z, set);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mutable setValue(AffinePoint p) {
|
||||
x.setValue(p.getX());
|
||||
y.setValue(p.getY());
|
||||
z.setValue(p.getX().getField().get1());
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mutable setValue(Point p) {
|
||||
if (!(p instanceof ProjectivePoint)) {
|
||||
throw new RuntimeException("Incompatible point");
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
ProjectivePoint<IntegerModuloP> pp =
|
||||
(ProjectivePoint<IntegerModuloP>) p;
|
||||
return setValue(pp);
|
||||
}
|
||||
|
||||
private <T extends IntegerModuloP>
|
||||
Mutable setValue(ProjectivePoint<T> pp) {
|
||||
|
||||
x.setValue(pp.x);
|
||||
y.setValue(pp.y);
|
||||
z.setValue(pp.z);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -176,57 +176,60 @@ final class ProviderConfig {
|
|||
return null;
|
||||
}
|
||||
|
||||
// Create providers which are in java.base directly
|
||||
if (provName.equals("SUN") || provName.equals("sun.security.provider.Sun")) {
|
||||
p = new sun.security.provider.Sun();
|
||||
} else if (provName.equals("SunRsaSign") || provName.equals("sun.security.rsa.SunRsaSign")) {
|
||||
p = new sun.security.rsa.SunRsaSign();
|
||||
} else if (provName.equals("SunJCE") || provName.equals("com.sun.crypto.provider.SunJCE")) {
|
||||
p = new com.sun.crypto.provider.SunJCE();
|
||||
} else if (provName.equals("SunJSSE")) {
|
||||
p = new sun.security.ssl.SunJSSE();
|
||||
} else if (provName.equals("Apple") || provName.equals("apple.security.AppleProvider")) {
|
||||
// need to use reflection since this class only exists on MacOsx
|
||||
@SuppressWarnings("removal")
|
||||
var tmp = AccessController.doPrivileged(new PrivilegedAction<Provider>() {
|
||||
public Provider run() {
|
||||
try {
|
||||
Class<?> c = Class.forName("apple.security.AppleProvider");
|
||||
if (Provider.class.isAssignableFrom(c)) {
|
||||
@SuppressWarnings("deprecation")
|
||||
Object tmp = c.newInstance();
|
||||
return (Provider) tmp;
|
||||
} else {
|
||||
p = switch (provName) {
|
||||
case "SUN", "sun.security.provider.Sun" ->
|
||||
new sun.security.provider.Sun();
|
||||
case "SunRsaSign", "sun.security.rsa.SunRsaSign" ->
|
||||
new sun.security.rsa.SunRsaSign();
|
||||
case "SunJCE", "com.sun.crypto.provider.SunJCE" ->
|
||||
new com.sun.crypto.provider.SunJCE();
|
||||
case "SunJSSE" -> new sun.security.ssl.SunJSSE();
|
||||
case "SunEC" -> new sun.security.ec.SunEC();
|
||||
case "Apple", "apple.security.AppleProvider" -> {
|
||||
// Reflection is needed for compile time as the class
|
||||
// is not available for non-macosx systems
|
||||
@SuppressWarnings("removal")
|
||||
var tmp = AccessController.doPrivileged(
|
||||
new PrivilegedAction<Provider>() {
|
||||
public Provider run() {
|
||||
try {
|
||||
Class<?> c = Class.forName(
|
||||
"apple.security.AppleProvider");
|
||||
if (Provider.class.isAssignableFrom(c)) {
|
||||
@SuppressWarnings("deprecation")
|
||||
Object tmp = c.newInstance();
|
||||
return (Provider) tmp;
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
if (debug != null) {
|
||||
debug.println("Error loading provider Apple");
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
if (debug != null) {
|
||||
debug.println("Error loading provider Apple");
|
||||
ex.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
});
|
||||
yield tmp;
|
||||
}
|
||||
default -> {
|
||||
if (isLoading) {
|
||||
// because this method is synchronized, this can only
|
||||
// happen if there is recursion.
|
||||
if (debug != null) {
|
||||
debug.println("Recursion loading provider: " + this);
|
||||
new Exception("Call trace").printStackTrace();
|
||||
}
|
||||
yield null;
|
||||
}
|
||||
});
|
||||
p = tmp;
|
||||
} else {
|
||||
if (isLoading) {
|
||||
// because this method is synchronized, this can only
|
||||
// happen if there is recursion.
|
||||
if (debug != null) {
|
||||
debug.println("Recursion loading provider: " + this);
|
||||
new Exception("Call trace").printStackTrace();
|
||||
try {
|
||||
isLoading = true;
|
||||
tries++;
|
||||
yield doLoadProvider();
|
||||
} finally {
|
||||
isLoading = false;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
isLoading = true;
|
||||
tries++;
|
||||
p = doLoadProvider();
|
||||
} finally {
|
||||
isLoading = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
provider = p;
|
||||
}
|
||||
return p;
|
||||
|
|
|
@ -544,8 +544,8 @@ public class AlgorithmId implements Serializable, DerEncoder {
|
|||
if (pn != null && mn != null) {
|
||||
return ((mn.equals("java.base") &&
|
||||
(pn.equals("SUN") || pn.equals("SunRsaSign") ||
|
||||
pn.equals("SunJCE") || pn.equals("SunJSSE"))) ||
|
||||
(mn.equals("jdk.crypto.ec") && pn.equals("SunEC")) ||
|
||||
pn.equals("SunJCE") || pn.equals("SunJSSE") ||
|
||||
pn.equals("SunEC"))) ||
|
||||
(mn.equals("jdk.crypto.mscapi") && pn.equals("SunMSCAPI")) ||
|
||||
(mn.equals("jdk.crypto.cryptoki") &&
|
||||
pn.startsWith("SunPKCS11")));
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue