8308398: Move SunEC crypto provider into java.base

Reviewed-by: valeriep, alanb
This commit is contained in:
Anthony Scarpino 2023-07-17 17:38:54 +00:00
parent 69a46c25cc
commit e737968792
58 changed files with 146 additions and 114 deletions

View file

@ -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;
}
}

View 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;
}
}

View 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;
}
}

View 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");
}
}
}

View file

@ -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;
}
}

View 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();
}
}
}
}
}
}
}
}
}

View file

@ -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);
}
}
}

View 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());
}
}

View 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.");
}
}
}

View 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));
}
}

View 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);
}
}
}

View 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);
}
}
}

View file

@ -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);
}
}
}

View file

@ -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);
}
}
}

View file

@ -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--;
}
}
}

View 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());
}
}

View 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);
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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();
}
}

View file

@ -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);
}
}
}

View file

@ -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);
}
}
}

View file

@ -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;
}
}

View file

@ -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);
}
}

View file

@ -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());
}
}

View file

@ -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--;
}
}
}

View file

@ -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);
}
}
}

View file

@ -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;
}
}

View file

@ -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() + ")";
}
}

View file

@ -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;
}
}
}

View file

@ -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 {
}

View file

@ -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);
}

View 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();
}

View file

@ -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;
}
}
}

View file

@ -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;

View file

@ -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")));