8208698: Improved ECC Implementation
New implementation of ECDH and ECDSA forsome prime-order curves Reviewed-by: ascarpino
This commit is contained in:
parent
b564c0c1a9
commit
3c12c4b0f3
@ -25,14 +25,19 @@
|
||||
|
||||
package sun.security.ec;
|
||||
|
||||
import java.math.*;
|
||||
import java.security.*;
|
||||
import java.security.interfaces.*;
|
||||
import java.security.spec.*;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.crypto.*;
|
||||
import javax.crypto.spec.*;
|
||||
|
||||
import sun.security.util.ArrayUtil;
|
||||
import sun.security.util.ECUtil;
|
||||
import sun.security.util.math.*;
|
||||
import sun.security.ec.point.*;
|
||||
|
||||
/**
|
||||
* KeyAgreement implementation for ECDH.
|
||||
@ -44,8 +49,8 @@ public final class ECDHKeyAgreement extends KeyAgreementSpi {
|
||||
// private key, if initialized
|
||||
private ECPrivateKey privateKey;
|
||||
|
||||
// encoded public point, non-null between doPhase() & generateSecret() only
|
||||
private byte[] publicValue;
|
||||
// public key, non-null between doPhase() & generateSecret() only
|
||||
private ECPublicKey publicKey;
|
||||
|
||||
// length of the secret to be derived
|
||||
private int secretLen;
|
||||
@ -65,7 +70,7 @@ public final class ECDHKeyAgreement extends KeyAgreementSpi {
|
||||
("Key must be instance of PrivateKey");
|
||||
}
|
||||
privateKey = (ECPrivateKey) ECKeyFactory.toECKey(key);
|
||||
publicValue = null;
|
||||
publicKey = null;
|
||||
}
|
||||
|
||||
// see JCE spec
|
||||
@ -87,7 +92,7 @@ public final class ECDHKeyAgreement extends KeyAgreementSpi {
|
||||
if (privateKey == null) {
|
||||
throw new IllegalStateException("Not initialized");
|
||||
}
|
||||
if (publicValue != null) {
|
||||
if (publicKey != null) {
|
||||
throw new IllegalStateException("Phase already executed");
|
||||
}
|
||||
if (!lastPhase) {
|
||||
@ -99,42 +104,74 @@ public final class ECDHKeyAgreement extends KeyAgreementSpi {
|
||||
("Key must be a PublicKey with algorithm EC");
|
||||
}
|
||||
|
||||
ECPublicKey ecKey = (ECPublicKey)key;
|
||||
ECParameterSpec params = ecKey.getParams();
|
||||
this.publicKey = (ECPublicKey) key;
|
||||
|
||||
if (ecKey instanceof ECPublicKeyImpl) {
|
||||
publicValue = ((ECPublicKeyImpl)ecKey).getEncodedPublicValue();
|
||||
} else { // instanceof ECPublicKey
|
||||
publicValue =
|
||||
ECUtil.encodePoint(ecKey.getW(), params.getCurve());
|
||||
}
|
||||
ECParameterSpec params = publicKey.getParams();
|
||||
int keyLenBits = params.getCurve().getField().getFieldSize();
|
||||
secretLen = (keyLenBits + 7) >> 3;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void validateCoordinate(BigInteger c, BigInteger mod) {
|
||||
if (c.compareTo(BigInteger.ZERO) < 0) {
|
||||
throw new ProviderException("invalid coordinate");
|
||||
}
|
||||
|
||||
if (c.compareTo(mod) >= 0) {
|
||||
throw new ProviderException("invalid coordinate");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether a public key is valid. Throw ProviderException
|
||||
* if it is not valid or could not be validated.
|
||||
*/
|
||||
private static void validate(ECOperations ops, ECPublicKey key) {
|
||||
|
||||
// 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 = key.getParams().getCurve();
|
||||
BigInteger rhs = x.modPow(BigInteger.valueOf(3), p).add(curve.getA()
|
||||
.multiply(x)).add(curve.getB()).mod(p);
|
||||
BigInteger lhs = y.modPow(BigInteger.valueOf(2), p).mod(p);
|
||||
if (!rhs.equals(lhs)) {
|
||||
throw new ProviderException("point is not on curve");
|
||||
}
|
||||
|
||||
// check the order of the point
|
||||
ImmutableIntegerModuloP xElem = ops.getField().getElement(x);
|
||||
ImmutableIntegerModuloP yElem = ops.getField().getElement(y);
|
||||
AffinePoint affP = new AffinePoint(xElem, yElem);
|
||||
byte[] order = key.getParams().getOrder().toByteArray();
|
||||
ArrayUtil.reverse(order);
|
||||
Point product = ops.multiply(affP, order);
|
||||
if (!ops.isNeutral(product)) {
|
||||
throw new ProviderException("point has incorrect order");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// see JCE spec
|
||||
@Override
|
||||
protected byte[] engineGenerateSecret() throws IllegalStateException {
|
||||
if ((privateKey == null) || (publicValue == null)) {
|
||||
if ((privateKey == null) || (publicKey == null)) {
|
||||
throw new IllegalStateException("Not initialized correctly");
|
||||
}
|
||||
|
||||
byte[] s = privateKey.getS().toByteArray();
|
||||
byte[] encodedParams = // DER OID
|
||||
ECUtil.encodeECParameterSpec(null, privateKey.getParams());
|
||||
|
||||
try {
|
||||
|
||||
byte[] result = deriveKey(s, publicValue, encodedParams);
|
||||
publicValue = null;
|
||||
return result;
|
||||
|
||||
} catch (GeneralSecurityException e) {
|
||||
throw new ProviderException("Could not derive key", e);
|
||||
}
|
||||
|
||||
Optional<byte[]> resultOpt = deriveKeyImpl(privateKey, publicKey);
|
||||
byte[] result = resultOpt.orElseGet(
|
||||
() -> deriveKeyNative(privateKey, publicKey)
|
||||
);
|
||||
publicKey = null;
|
||||
return result;
|
||||
}
|
||||
|
||||
// see JCE spec
|
||||
@ -143,7 +180,8 @@ public final class ECDHKeyAgreement extends KeyAgreementSpi {
|
||||
offset) throws IllegalStateException, ShortBufferException {
|
||||
if (offset + secretLen > sharedSecret.length) {
|
||||
throw new ShortBufferException("Need " + secretLen
|
||||
+ " bytes, only " + (sharedSecret.length - offset) + " available");
|
||||
+ " bytes, only " + (sharedSecret.length - offset)
|
||||
+ " available");
|
||||
}
|
||||
byte[] secret = engineGenerateSecret();
|
||||
System.arraycopy(secret, 0, sharedSecret, offset, secret.length);
|
||||
@ -165,6 +203,78 @@ public final class ECDHKeyAgreement extends KeyAgreementSpi {
|
||||
return new SecretKeySpec(engineGenerateSecret(), "TlsPremasterSecret");
|
||||
}
|
||||
|
||||
private static
|
||||
Optional<byte[]> deriveKeyImpl(ECPrivateKey priv, ECPublicKey pubKey) {
|
||||
|
||||
ECParameterSpec ecSpec = priv.getParams();
|
||||
EllipticCurve curve = ecSpec.getCurve();
|
||||
Optional<ECOperations> opsOpt = ECOperations.forParameters(ecSpec);
|
||||
if (opsOpt.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
ECOperations ops = opsOpt.get();
|
||||
if (! (priv instanceof ECPrivateKeyImpl)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
ECPrivateKeyImpl privImpl = (ECPrivateKeyImpl) priv;
|
||||
byte[] sArr = privImpl.getArrayS();
|
||||
|
||||
// to match the native implementation, validate the public key here
|
||||
// and throw ProviderException if it is invalid
|
||||
validate(ops, pubKey);
|
||||
|
||||
IntegerFieldModuloP field = ops.getField();
|
||||
// convert s array into field element and multiply by the cofactor
|
||||
MutableIntegerModuloP scalar = field.getElement(sArr).mutable();
|
||||
SmallValue cofactor =
|
||||
field.getSmallValue(priv.getParams().getCofactor());
|
||||
scalar.setProduct(cofactor);
|
||||
int keySize = (curve.getField().getFieldSize() + 7) / 8;
|
||||
byte[] privArr = scalar.asByteArray(keySize);
|
||||
|
||||
ImmutableIntegerModuloP x =
|
||||
field.getElement(pubKey.getW().getAffineX());
|
||||
ImmutableIntegerModuloP y =
|
||||
field.getElement(pubKey.getW().getAffineY());
|
||||
AffinePoint affPub = new AffinePoint(x, y);
|
||||
Point product = ops.multiply(affPub, privArr);
|
||||
if (ops.isNeutral(product)) {
|
||||
throw new ProviderException("Product is zero");
|
||||
}
|
||||
AffinePoint affProduct = product.asAffine();
|
||||
|
||||
byte[] result = affProduct.getX().asByteArray(keySize);
|
||||
ArrayUtil.reverse(result);
|
||||
|
||||
return Optional.of(result);
|
||||
}
|
||||
|
||||
private static
|
||||
byte[] deriveKeyNative(ECPrivateKey privateKey, ECPublicKey publicKey) {
|
||||
|
||||
ECParameterSpec params = privateKey.getParams();
|
||||
byte[] s = privateKey.getS().toByteArray();
|
||||
byte[] encodedParams = // DER OID
|
||||
ECUtil.encodeECParameterSpec(null, params);
|
||||
|
||||
byte[] publicValue;
|
||||
if (publicKey instanceof ECPublicKeyImpl) {
|
||||
ECPublicKeyImpl ecPub = (ECPublicKeyImpl) publicKey;
|
||||
publicValue = ecPub.getEncodedPublicValue();
|
||||
} else { // instanceof ECPublicKey
|
||||
publicValue =
|
||||
ECUtil.encodePoint(publicKey.getW(), params.getCurve());
|
||||
}
|
||||
|
||||
try {
|
||||
return deriveKey(s, publicValue, encodedParams);
|
||||
|
||||
} catch (GeneralSecurityException e) {
|
||||
throw new ProviderException("Could not derive key", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates a secret key using the public and private keys.
|
||||
*
|
||||
|
@ -0,0 +1,202 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import sun.security.ec.point.*;
|
||||
import sun.security.util.ArrayUtil;
|
||||
import sun.security.util.math.*;
|
||||
import static sun.security.ec.ECOperations.IntermediateValueException;
|
||||
|
||||
import java.security.ProviderException;
|
||||
import java.security.spec.*;
|
||||
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 nonnce 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 nonnce 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.asByteArray(temp);
|
||||
r = orderField.getElement(temp);
|
||||
// store r in result
|
||||
r.asByteArray(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;
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -32,9 +32,11 @@ import java.math.BigInteger;
|
||||
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
|
||||
@ -147,7 +149,7 @@ abstract class ECDSASignature extends SignatureSpi {
|
||||
// Stores the precomputed message digest value.
|
||||
@Override
|
||||
protected void engineUpdate(byte[] b, int off, int len)
|
||||
throws SignatureException {
|
||||
throws SignatureException {
|
||||
if (offset >= precomputedDigest.length) {
|
||||
offset = RAW_ECDSA_MAX + 1;
|
||||
return;
|
||||
@ -172,7 +174,7 @@ abstract class ECDSASignature extends SignatureSpi {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void resetDigest(){
|
||||
protected void resetDigest() {
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
@ -222,14 +224,14 @@ abstract class ECDSASignature extends SignatureSpi {
|
||||
// Nested class for SHA224withECDSA signatures
|
||||
public static final class SHA224 extends ECDSASignature {
|
||||
public SHA224() {
|
||||
super("SHA-224");
|
||||
super("SHA-224");
|
||||
}
|
||||
}
|
||||
|
||||
// Nested class for SHA224withECDSAinP1363Format signatures
|
||||
public static final class SHA224inP1363Format extends ECDSASignature {
|
||||
public SHA224inP1363Format() {
|
||||
super("SHA-224", true);
|
||||
super("SHA-224", true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -278,7 +280,7 @@ abstract class ECDSASignature extends SignatureSpi {
|
||||
// initialize for verification. See JCA doc
|
||||
@Override
|
||||
protected void engineInitVerify(PublicKey publicKey)
|
||||
throws InvalidKeyException {
|
||||
throws InvalidKeyException {
|
||||
this.publicKey = (ECPublicKey) ECKeyFactory.toECKey(publicKey);
|
||||
|
||||
// Should check that the supplied key is appropriate for signature
|
||||
@ -290,14 +292,14 @@ abstract class ECDSASignature extends SignatureSpi {
|
||||
// initialize for signing. See JCA doc
|
||||
@Override
|
||||
protected void engineInitSign(PrivateKey privateKey)
|
||||
throws InvalidKeyException {
|
||||
throws InvalidKeyException {
|
||||
engineInitSign(privateKey, null);
|
||||
}
|
||||
|
||||
// initialize for signing. See JCA doc
|
||||
@Override
|
||||
protected void engineInitSign(PrivateKey privateKey, SecureRandom random)
|
||||
throws InvalidKeyException {
|
||||
throws InvalidKeyException {
|
||||
this.privateKey = (ECPrivateKey) ECKeyFactory.toECKey(privateKey);
|
||||
|
||||
// Should check that the supplied key is appropriate for signature
|
||||
@ -337,7 +339,7 @@ abstract class ECDSASignature extends SignatureSpi {
|
||||
// update the signature with the plaintext data. See JCA doc
|
||||
@Override
|
||||
protected void engineUpdate(byte[] b, int off, int len)
|
||||
throws SignatureException {
|
||||
throws SignatureException {
|
||||
messageDigest.update(b, off, len);
|
||||
needsReset = true;
|
||||
}
|
||||
@ -354,20 +356,67 @@ abstract class ECDSASignature extends SignatureSpi {
|
||||
needsReset = true;
|
||||
}
|
||||
|
||||
// sign the data and return the signature. See JCA doc
|
||||
@Override
|
||||
protected byte[] engineSign() throws SignatureException {
|
||||
private byte[] signDigestImpl(ECDSAOperations ops, int seedBits,
|
||||
byte[] digest, ECPrivateKeyImpl privImpl, SecureRandom random)
|
||||
throws SignatureException {
|
||||
|
||||
byte[] seedBytes = new byte[(seedBits + 7) / 8];
|
||||
byte[] s = privImpl.getArrayS();
|
||||
|
||||
// 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");
|
||||
}
|
||||
|
||||
|
||||
private Optional<byte[]> signDigestImpl(ECPrivateKey privateKey,
|
||||
byte[] digest, SecureRandom random) throws SignatureException {
|
||||
|
||||
if (! (privateKey instanceof ECPrivateKeyImpl)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
ECPrivateKeyImpl privImpl = (ECPrivateKeyImpl) privateKey;
|
||||
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()) {
|
||||
return Optional.empty();
|
||||
} else {
|
||||
byte[] sig = signDigestImpl(opsOpt.get(), seedBits, digest,
|
||||
privImpl, random);
|
||||
return Optional.of(sig);
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] signDigestNative(ECPrivateKey privateKey, byte[] digest,
|
||||
SecureRandom random) throws SignatureException {
|
||||
|
||||
byte[] s = privateKey.getS().toByteArray();
|
||||
ECParameterSpec params = privateKey.getParams();
|
||||
|
||||
// DER OID
|
||||
byte[] encodedParams = ECUtil.encodeECParameterSpec(null, params);
|
||||
int keySize = params.getCurve().getField().getFieldSize();
|
||||
|
||||
// seed is twice the key size (in bytes) plus 1
|
||||
byte[] seed = new byte[(((keySize + 7) >> 3) + 1) * 2];
|
||||
if (random == null) {
|
||||
random = JCAUtil.getSecureRandom();
|
||||
}
|
||||
|
||||
random.nextBytes(seed);
|
||||
|
||||
// random bits needed for timing countermeasures
|
||||
@ -375,14 +424,32 @@ abstract class ECDSASignature extends SignatureSpi {
|
||||
// values must be non-zero to enable countermeasures
|
||||
timingArgument |= 1;
|
||||
|
||||
byte[] sig;
|
||||
try {
|
||||
sig = signDigest(getDigestValue(), s, encodedParams, seed,
|
||||
return signDigest(digest, s, encodedParams, seed,
|
||||
timingArgument);
|
||||
} catch (GeneralSecurityException e) {
|
||||
throw new SignatureException("Could not sign data", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 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();
|
||||
Optional<byte[]> sigOpt = signDigestImpl(privateKey, digest, random);
|
||||
byte[] sig;
|
||||
if (sigOpt.isPresent()) {
|
||||
sig = sigOpt.get();
|
||||
} else {
|
||||
sig = signDigestNative(privateKey, digest, random);
|
||||
}
|
||||
|
||||
if (p1363Format) {
|
||||
return sig;
|
||||
} else {
|
||||
@ -400,7 +467,7 @@ abstract class ECDSASignature extends SignatureSpi {
|
||||
byte[] encodedParams = ECUtil.encodeECParameterSpec(null, params);
|
||||
|
||||
if (publicKey instanceof ECPublicKeyImpl) {
|
||||
w = ((ECPublicKeyImpl)publicKey).getEncodedPublicValue();
|
||||
w = ((ECPublicKeyImpl) publicKey).getEncodedPublicValue();
|
||||
} else { // instanceof ECPublicKey
|
||||
w = ECUtil.encodePoint(publicKey.getW(), params.getCurve());
|
||||
}
|
||||
@ -423,13 +490,13 @@ abstract class ECDSASignature extends SignatureSpi {
|
||||
@Override
|
||||
@Deprecated
|
||||
protected void engineSetParameter(String param, Object value)
|
||||
throws InvalidParameterException {
|
||||
throws InvalidParameterException {
|
||||
throw new UnsupportedOperationException("setParameter() not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void engineSetParameter(AlgorithmParameterSpec params)
|
||||
throws InvalidAlgorithmParameterException {
|
||||
throws InvalidAlgorithmParameterException {
|
||||
if (params != null) {
|
||||
throw new InvalidAlgorithmParameterException("No parameter accepted");
|
||||
}
|
||||
@ -439,7 +506,7 @@ abstract class ECDSASignature extends SignatureSpi {
|
||||
@Override
|
||||
@Deprecated
|
||||
protected Object engineGetParameter(String param)
|
||||
throws InvalidParameterException {
|
||||
throws InvalidParameterException {
|
||||
throw new UnsupportedOperationException("getParameter() not supported");
|
||||
}
|
||||
|
||||
@ -464,7 +531,7 @@ abstract class ECDSASignature extends SignatureSpi {
|
||||
out.putInteger(r);
|
||||
out.putInteger(s);
|
||||
DerValue result =
|
||||
new DerValue(DerValue.tag_Sequence, out.toByteArray());
|
||||
new DerValue(DerValue.tag_Sequence, out.toByteArray());
|
||||
|
||||
return result.toByteArray();
|
||||
|
||||
@ -497,9 +564,9 @@ abstract class ECDSASignature extends SignatureSpi {
|
||||
// r and s each occupy half the array
|
||||
byte[] result = new byte[k << 1];
|
||||
System.arraycopy(rBytes, 0, result, k - rBytes.length,
|
||||
rBytes.length);
|
||||
rBytes.length);
|
||||
System.arraycopy(sBytes, 0, result, result.length - sBytes.length,
|
||||
sBytes.length);
|
||||
sBytes.length);
|
||||
return result;
|
||||
|
||||
} catch (Exception e) {
|
||||
@ -539,13 +606,13 @@ abstract class ECDSASignature extends SignatureSpi {
|
||||
* @return byte[] the signature.
|
||||
*/
|
||||
private static native byte[] signDigest(byte[] digest, byte[] s,
|
||||
byte[] encodedParams, byte[] seed, int timing)
|
||||
throws GeneralSecurityException;
|
||||
byte[] encodedParams, byte[] seed, int timing)
|
||||
throws GeneralSecurityException;
|
||||
|
||||
/**
|
||||
* Verifies the signed digest using the public key.
|
||||
*
|
||||
* @param signedDigest the signature to be verified. It is encoded
|
||||
* @param signature the signature to be verified. It is encoded
|
||||
* as a concatenation of the key's R and S values.
|
||||
* @param digest the digest to be used.
|
||||
* @param w the public key's W point (in uncompressed form).
|
||||
@ -554,6 +621,6 @@ abstract class ECDSASignature extends SignatureSpi {
|
||||
* @return boolean true if the signature is successfully verified.
|
||||
*/
|
||||
private static native boolean verifySignedDigest(byte[] signature,
|
||||
byte[] digest, byte[] w, byte[] encodedParams)
|
||||
throws GeneralSecurityException;
|
||||
byte[] digest, byte[] w, byte[] encodedParams)
|
||||
throws GeneralSecurityException;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2009, 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
|
||||
@ -33,13 +33,15 @@ import java.security.spec.ECGenParameterSpec;
|
||||
import java.security.spec.ECParameterSpec;
|
||||
import java.security.spec.ECPoint;
|
||||
import java.security.spec.InvalidParameterSpecException;
|
||||
import java.security.spec.*;
|
||||
import java.util.Optional;
|
||||
|
||||
import sun.security.ec.ECPrivateKeyImpl;
|
||||
import sun.security.ec.ECPublicKeyImpl;
|
||||
import sun.security.jca.JCAUtil;
|
||||
import sun.security.util.ECParameters;
|
||||
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.
|
||||
@ -90,14 +92,14 @@ public final class ECKeyPairGenerator extends KeyPairGeneratorSpi {
|
||||
ECParameterSpec ecSpec = null;
|
||||
|
||||
if (params instanceof ECParameterSpec) {
|
||||
ecSpec = ECUtil.getECParameterSpec(null,
|
||||
(ECParameterSpec)params);
|
||||
ECParameterSpec ecParams = (ECParameterSpec) params;
|
||||
ecSpec = ECUtil.getECParameterSpec(null, ecParams);
|
||||
if (ecSpec == null) {
|
||||
throw new InvalidAlgorithmParameterException(
|
||||
"Unsupported curve: " + params);
|
||||
}
|
||||
} else if (params instanceof ECGenParameterSpec) {
|
||||
String name = ((ECGenParameterSpec)params).getName();
|
||||
String name = ((ECGenParameterSpec) params).getName();
|
||||
ecSpec = ECUtil.getECParameterSpec(null, name);
|
||||
if (ecSpec == null) {
|
||||
throw new InvalidAlgorithmParameterException(
|
||||
@ -112,8 +114,7 @@ public final class ECKeyPairGenerator extends KeyPairGeneratorSpi {
|
||||
ensureCurveIsSupported(ecSpec);
|
||||
this.params = ecSpec;
|
||||
|
||||
this.keySize =
|
||||
((ECParameterSpec)this.params).getCurve().getField().getFieldSize();
|
||||
this.keySize = ecSpec.getCurve().getField().getFieldSize();
|
||||
this.random = random;
|
||||
}
|
||||
|
||||
@ -141,41 +142,99 @@ public final class ECKeyPairGenerator extends KeyPairGeneratorSpi {
|
||||
@Override
|
||||
public KeyPair generateKeyPair() {
|
||||
|
||||
byte[] encodedParams =
|
||||
ECUtil.encodeECParameterSpec(null, (ECParameterSpec)params);
|
||||
|
||||
// seed is twice the key size (in bytes) plus 1
|
||||
byte[] seed = new byte[(((keySize + 7) >> 3) + 1) * 2];
|
||||
if (random == null) {
|
||||
random = JCAUtil.getSecureRandom();
|
||||
}
|
||||
random.nextBytes(seed);
|
||||
|
||||
try {
|
||||
|
||||
Object[] keyBytes = generateECKeyPair(keySize, encodedParams, seed);
|
||||
|
||||
// The 'params' object supplied above is equivalent to the native
|
||||
// one so there is no need to fetch it.
|
||||
// keyBytes[0] is the encoding of the native private key
|
||||
BigInteger s = new BigInteger(1, (byte[])keyBytes[0]);
|
||||
|
||||
PrivateKey privateKey =
|
||||
new ECPrivateKeyImpl(s, (ECParameterSpec)params);
|
||||
|
||||
// keyBytes[1] is the encoding of the native public key
|
||||
ECPoint w = ECUtil.decodePoint((byte[])keyBytes[1],
|
||||
((ECParameterSpec)params).getCurve());
|
||||
PublicKey publicKey =
|
||||
new ECPublicKeyImpl(w, (ECParameterSpec)params);
|
||||
|
||||
return new KeyPair(publicKey, privateKey);
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new ProviderException(e);
|
||||
Optional<KeyPair> kp = generateKeyPairImpl(random);
|
||||
if (kp.isPresent()) {
|
||||
return kp.get();
|
||||
}
|
||||
return generateKeyPairNative(random);
|
||||
} catch (Exception ex) {
|
||||
throw new ProviderException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
ECPoint genPoint = ecParams.getGenerator();
|
||||
ImmutableIntegerModuloP x = field.getElement(genPoint.getAffineX());
|
||||
ImmutableIntegerModuloP y = field.getElement(genPoint.getAffineY());
|
||||
AffinePoint affGen = new AffinePoint(x, y);
|
||||
Point pub = ops.multiply(affGen, privArr);
|
||||
AffinePoint affPub = pub.asAffine();
|
||||
|
||||
PrivateKey privateKey = new ECPrivateKeyImpl(privArr, ecParams);
|
||||
|
||||
ECPoint w = new ECPoint(affPub.getX().asBigInteger(),
|
||||
affPub.getY().asBigInteger());
|
||||
PublicKey publicKey = new ECPublicKeyImpl(w, ecParams);
|
||||
|
||||
return Optional.of(new KeyPair(publicKey, privateKey));
|
||||
}
|
||||
|
||||
private KeyPair generateKeyPairNative(SecureRandom random)
|
||||
throws Exception {
|
||||
|
||||
ECParameterSpec ecParams = (ECParameterSpec) params;
|
||||
byte[] encodedParams = ECUtil.encodeECParameterSpec(null, ecParams);
|
||||
|
||||
// seed is twice the key size (in bytes) plus 1
|
||||
byte[] seed = new byte[(((keySize + 7) >> 3) + 1) * 2];
|
||||
random.nextBytes(seed);
|
||||
Object[] keyBytes = generateECKeyPair(keySize, encodedParams, seed);
|
||||
|
||||
// The 'params' object supplied above is equivalent to the native
|
||||
// one so there is no need to fetch it.
|
||||
// keyBytes[0] is the encoding of the native private key
|
||||
BigInteger s = new BigInteger(1, (byte[]) keyBytes[0]);
|
||||
|
||||
PrivateKey privateKey = new ECPrivateKeyImpl(s, ecParams);
|
||||
|
||||
// keyBytes[1] is the encoding of the native public key
|
||||
byte[] pubKey = (byte[]) keyBytes[1];
|
||||
ECPoint w = ECUtil.decodePoint(pubKey, ecParams.getCurve());
|
||||
PublicKey publicKey = new ECPublicKeyImpl(w, ecParams);
|
||||
|
||||
return new KeyPair(publicKey, privateKey);
|
||||
}
|
||||
|
||||
private void checkKeySize(int keySize) throws InvalidParameterException {
|
||||
if (keySize < KEY_SIZE_MIN) {
|
||||
throw new InvalidParameterException
|
||||
@ -190,7 +249,9 @@ public final class ECKeyPairGenerator extends KeyPairGeneratorSpi {
|
||||
|
||||
/**
|
||||
* Checks whether the curve in the encoded parameters is supported by the
|
||||
* native implementation.
|
||||
* native implementation. Some curve operations will be performed by the
|
||||
* Java implementation, but not all of them. So native support is still
|
||||
* required for all curves.
|
||||
*
|
||||
* @param encodedParams encoded parameters in the same form accepted
|
||||
* by generateECKeyPair
|
||||
|
@ -0,0 +1,493 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import sun.security.ec.point.*;
|
||||
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.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 {
|
||||
|
||||
/*
|
||||
* 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, new IntegerPolynomialP256(),
|
||||
IntegerPolynomialP384.MODULUS, new IntegerPolynomialP384(),
|
||||
IntegerPolynomialP521.MODULUS, new IntegerPolynomialP521()
|
||||
);
|
||||
|
||||
static final Map<BigInteger, IntegerFieldModuloP> orderFields = Map.of(
|
||||
P256OrderField.MODULUS, new P256OrderField(),
|
||||
P384OrderField.MODULUS, new P384OrderField(),
|
||||
P521OrderField.MODULUS, new P521OrderField()
|
||||
);
|
||||
|
||||
public static Optional<ECOperations> forParameters(ECParameterSpec params) {
|
||||
|
||||
EllipticCurve curve = params.getCurve();
|
||||
if (!(curve.getField() instanceof ECFieldFp)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
ECFieldFp primeField = (ECFieldFp) curve.getField();
|
||||
|
||||
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 in to 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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 4-bit branchless array lookup for projective points.
|
||||
*/
|
||||
private void lookup4(ProjectivePoint.Immutable[] arr, int index,
|
||||
ProjectivePoint.Mutable result, IntegerModuloP zero) {
|
||||
|
||||
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 = arr[i];
|
||||
result.conditionalSet(pi, set);
|
||||
}
|
||||
}
|
||||
|
||||
private void double4(ProjectivePoint.Mutable p, MutableIntegerModuloP t0,
|
||||
MutableIntegerModuloP t1, MutableIntegerModuloP t2,
|
||||
MutableIntegerModuloP t3, MutableIntegerModuloP t4) {
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
setDouble(p, t0, t1, t2, t3, t4);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
|
||||
// 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++) {
|
||||
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;
|
||||
lookup4(pointMultiples, high, lookupResult, zero);
|
||||
setSum(result, lookupResult, t0, t1, t2, t3, t4);
|
||||
|
||||
double4(result, t0, t1, t2, t3, t4);
|
||||
|
||||
int low = 0xF & s[i];
|
||||
lookup4(pointMultiples, low, lookupResult, zero);
|
||||
setSum(result, lookupResult, t0, t1, t2, t3, t4);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.getX().setValue(p.getY()).setProduct(two);
|
||||
p.getY().setSum(p.getX());
|
||||
p.getY().setReduced();
|
||||
p.getX().setValue(t1).setDifference(p.getY());
|
||||
|
||||
p.getY().setSum(t1);
|
||||
p.getY().setProduct(p.getX());
|
||||
p.getX().setProduct(t3);
|
||||
|
||||
t3.setValue(t2).setProduct(two);
|
||||
t2.setSum(t3);
|
||||
p.getZ().setProduct(b);
|
||||
|
||||
t2.setReduced();
|
||||
p.getZ().setDifference(t2);
|
||||
p.getZ().setDifference(t0);
|
||||
t3.setValue(p.getZ()).setProduct(two);
|
||||
p.getZ().setReduced();
|
||||
p.getZ().setSum(t3);
|
||||
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());
|
||||
p.getX().setReduced();
|
||||
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().setReduced();
|
||||
p.getZ().setValue(p.getX()).setProduct(two);
|
||||
p.getX().setSum(p.getZ());
|
||||
|
||||
p.getZ().setValue(t1).setDifference(p.getX());
|
||||
p.getX().setSum(t1);
|
||||
p.getY().setProduct(b);
|
||||
|
||||
t1.setValue(t2).setProduct(two);
|
||||
t2.setSum(t1);
|
||||
t2.setReduced();
|
||||
p.getY().setDifference(t2);
|
||||
|
||||
p.getY().setDifference(t0);
|
||||
p.getY().setReduced();
|
||||
t1.setValue(p.getY()).setProduct(two);
|
||||
p.getY().setSum(t1);
|
||||
|
||||
t1.setValue(t0).setProduct(two);
|
||||
t0.setSum(t1);
|
||||
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);
|
||||
t1.setValue(t3).setProduct(t0);
|
||||
p.getZ().setSum(t1);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.getY().setReduced();
|
||||
|
||||
p.getZ().setValue(t2).setProduct(b);
|
||||
p.getX().setValue(p.getY()).setDifference(p.getZ());
|
||||
p.getZ().setValue(p.getX()).setProduct(two);
|
||||
|
||||
p.getX().setSum(p.getZ());
|
||||
p.getX().setReduced();
|
||||
p.getZ().setValue(t1).setDifference(p.getX());
|
||||
p.getX().setSum(t1);
|
||||
|
||||
p.getY().setProduct(b);
|
||||
t1.setValue(t2).setSum(t2);
|
||||
t2.setSum(t1);
|
||||
t2.setReduced();
|
||||
|
||||
p.getY().setDifference(t2);
|
||||
p.getY().setDifference(t0);
|
||||
p.getY().setReduced();
|
||||
t1.setValue(p.getY()).setSum(p.getY());
|
||||
|
||||
p.getY().setSum(t1);
|
||||
t1.setValue(t0).setProduct(two);
|
||||
t0.setSum(t1);
|
||||
|
||||
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);
|
||||
t1.setValue(t3).setProduct(t0);
|
||||
|
||||
p.getZ().setSum(t1);
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2006, 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
|
||||
@ -32,11 +32,7 @@ import java.security.*;
|
||||
import java.security.interfaces.*;
|
||||
import java.security.spec.*;
|
||||
|
||||
import sun.security.util.DerInputStream;
|
||||
import sun.security.util.DerOutputStream;
|
||||
import sun.security.util.DerValue;
|
||||
import sun.security.util.ECParameters;
|
||||
import sun.security.util.ECUtil;
|
||||
import sun.security.util.*;
|
||||
import sun.security.x509.AlgorithmId;
|
||||
import sun.security.pkcs.PKCS8Key;
|
||||
|
||||
@ -68,6 +64,7 @@ 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
|
||||
private ECParameterSpec params;
|
||||
|
||||
/**
|
||||
@ -85,13 +82,25 @@ public final class ECPrivateKeyImpl extends PKCS8Key implements ECPrivateKey {
|
||||
throws InvalidKeyException {
|
||||
this.s = s;
|
||||
this.params = params;
|
||||
// generate the encoding
|
||||
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));
|
||||
(AlgorithmId.EC_oid, ECParameters.getAlgorithmParameters(params));
|
||||
try {
|
||||
DerOutputStream out = new DerOutputStream();
|
||||
out.putInteger(1); // version 1
|
||||
byte[] privBytes = ECUtil.trimZeroes(s.toByteArray());
|
||||
byte[] privBytes = s.clone();
|
||||
ArrayUtil.reverse(privBytes);
|
||||
out.putOctetString(privBytes);
|
||||
DerValue val =
|
||||
new DerValue(DerValue.tag_Sequence, out.toByteArray());
|
||||
@ -102,6 +111,31 @@ public final class ECPrivateKeyImpl extends PKCS8Key implements ECPrivateKey {
|
||||
}
|
||||
}
|
||||
|
||||
private void makeEncoding(BigInteger s) throws InvalidKeyException {
|
||||
algid = new AlgorithmId
|
||||
(AlgorithmId.EC_oid, ECParameters.getAlgorithmParameters(params));
|
||||
try {
|
||||
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);
|
||||
|
||||
DerOutputStream out = new DerOutputStream();
|
||||
out.putInteger(1); // version 1
|
||||
out.putOctetString(sOctets);
|
||||
DerValue val =
|
||||
new DerValue(DerValue.tag_Sequence, out.toByteArray());
|
||||
key = val.toByteArray();
|
||||
} catch (IOException exc) {
|
||||
// should never occur
|
||||
throw new InvalidKeyException(exc);
|
||||
}
|
||||
}
|
||||
|
||||
// see JCA doc
|
||||
public String getAlgorithm() {
|
||||
return "EC";
|
||||
@ -109,9 +143,26 @@ public final class ECPrivateKeyImpl extends PKCS8Key implements ECPrivateKey {
|
||||
|
||||
// see JCA doc
|
||||
public BigInteger getS() {
|
||||
if (s == null) {
|
||||
byte[] arrCopy = arrayS.clone();
|
||||
ArrayUtil.reverse(arrCopy);
|
||||
s = new BigInteger(1, arrCopy);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
public byte[] getArrayS() {
|
||||
if (arrayS == null) {
|
||||
byte[] arr = getS().toByteArray();
|
||||
ArrayUtil.reverse(arr);
|
||||
int byteLength = (params.getOrder().bitLength() + 7) / 8;
|
||||
arrayS = new byte[byteLength];
|
||||
int length = Math.min(byteLength, arr.length);
|
||||
System.arraycopy(arr, 0, arrayS, 0, length);
|
||||
}
|
||||
return arrayS.clone();
|
||||
}
|
||||
|
||||
// see JCA doc
|
||||
public ECParameterSpec getParams() {
|
||||
return params;
|
||||
@ -133,12 +184,13 @@ public final class ECPrivateKeyImpl extends PKCS8Key implements ECPrivateKey {
|
||||
throw new IOException("Version must be 1");
|
||||
}
|
||||
byte[] privData = data.getOctetString();
|
||||
s = new BigInteger(1, privData);
|
||||
ArrayUtil.reverse(privData);
|
||||
arrayS = privData;
|
||||
while (data.available() != 0) {
|
||||
DerValue value = data.getDerValue();
|
||||
if (value.isContextSpecific((byte)0)) {
|
||||
if (value.isContextSpecific((byte) 0)) {
|
||||
// ignore for now
|
||||
} else if (value.isContextSpecific((byte)1)) {
|
||||
} else if (value.isContextSpecific((byte) 1)) {
|
||||
// ignore for now
|
||||
} else {
|
||||
throw new InvalidKeyException("Unexpected value: " + value);
|
||||
|
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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 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 ImmutableIntegerModuloP getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public ImmutableIntegerModuloP getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof AffinePoint)) {
|
||||
return false;
|
||||
}
|
||||
AffinePoint p = (AffinePoint) obj;
|
||||
boolean xEquals = x.asBigInteger().equals(p.x.asBigInteger());
|
||||
boolean yEquals = y.asBigInteger().equals(p.y.asBigInteger());
|
||||
return xEquals && yEquals;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "(" + x.asBigInteger().toString() + "," +
|
||||
y.asBigInteger().toString() + ")";
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ec.point;
|
||||
|
||||
/**
|
||||
* An interface for immutable points on an elliptic curve over a finite field.
|
||||
*/
|
||||
public interface ImmutablePoint extends Point {
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ec.point;
|
||||
|
||||
/**
|
||||
* An interface for mutable points on an elliptic curve over a finite field.
|
||||
*/
|
||||
public interface MutablePoint extends Point {
|
||||
|
||||
MutablePoint setValue(AffinePoint p);
|
||||
MutablePoint setValue(Point p);
|
||||
MutablePoint conditionalSet(Point p, int set);
|
||||
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.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();
|
||||
|
||||
ImmutablePoint fixed();
|
||||
MutablePoint mutable();
|
||||
|
||||
}
|
@ -0,0 +1,160 @@
|
||||
/*
|
||||
* 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.*;
|
||||
|
||||
/**
|
||||
* 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));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user