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
src/jdk.crypto.ec/share/classes/sun/security/ec
@ -25,14 +25,19 @@
|
|||||||
|
|
||||||
package sun.security.ec;
|
package sun.security.ec;
|
||||||
|
|
||||||
|
import java.math.*;
|
||||||
import java.security.*;
|
import java.security.*;
|
||||||
import java.security.interfaces.*;
|
import java.security.interfaces.*;
|
||||||
import java.security.spec.*;
|
import java.security.spec.*;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import javax.crypto.*;
|
import javax.crypto.*;
|
||||||
import javax.crypto.spec.*;
|
import javax.crypto.spec.*;
|
||||||
|
|
||||||
|
import sun.security.util.ArrayUtil;
|
||||||
import sun.security.util.ECUtil;
|
import sun.security.util.ECUtil;
|
||||||
|
import sun.security.util.math.*;
|
||||||
|
import sun.security.ec.point.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* KeyAgreement implementation for ECDH.
|
* KeyAgreement implementation for ECDH.
|
||||||
@ -44,8 +49,8 @@ public final class ECDHKeyAgreement extends KeyAgreementSpi {
|
|||||||
// private key, if initialized
|
// private key, if initialized
|
||||||
private ECPrivateKey privateKey;
|
private ECPrivateKey privateKey;
|
||||||
|
|
||||||
// encoded public point, non-null between doPhase() & generateSecret() only
|
// public key, non-null between doPhase() & generateSecret() only
|
||||||
private byte[] publicValue;
|
private ECPublicKey publicKey;
|
||||||
|
|
||||||
// length of the secret to be derived
|
// length of the secret to be derived
|
||||||
private int secretLen;
|
private int secretLen;
|
||||||
@ -65,7 +70,7 @@ public final class ECDHKeyAgreement extends KeyAgreementSpi {
|
|||||||
("Key must be instance of PrivateKey");
|
("Key must be instance of PrivateKey");
|
||||||
}
|
}
|
||||||
privateKey = (ECPrivateKey) ECKeyFactory.toECKey(key);
|
privateKey = (ECPrivateKey) ECKeyFactory.toECKey(key);
|
||||||
publicValue = null;
|
publicKey = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// see JCE spec
|
// see JCE spec
|
||||||
@ -87,7 +92,7 @@ public final class ECDHKeyAgreement extends KeyAgreementSpi {
|
|||||||
if (privateKey == null) {
|
if (privateKey == null) {
|
||||||
throw new IllegalStateException("Not initialized");
|
throw new IllegalStateException("Not initialized");
|
||||||
}
|
}
|
||||||
if (publicValue != null) {
|
if (publicKey != null) {
|
||||||
throw new IllegalStateException("Phase already executed");
|
throw new IllegalStateException("Phase already executed");
|
||||||
}
|
}
|
||||||
if (!lastPhase) {
|
if (!lastPhase) {
|
||||||
@ -99,42 +104,74 @@ public final class ECDHKeyAgreement extends KeyAgreementSpi {
|
|||||||
("Key must be a PublicKey with algorithm EC");
|
("Key must be a PublicKey with algorithm EC");
|
||||||
}
|
}
|
||||||
|
|
||||||
ECPublicKey ecKey = (ECPublicKey)key;
|
this.publicKey = (ECPublicKey) key;
|
||||||
ECParameterSpec params = ecKey.getParams();
|
|
||||||
|
|
||||||
if (ecKey instanceof ECPublicKeyImpl) {
|
ECParameterSpec params = publicKey.getParams();
|
||||||
publicValue = ((ECPublicKeyImpl)ecKey).getEncodedPublicValue();
|
|
||||||
} else { // instanceof ECPublicKey
|
|
||||||
publicValue =
|
|
||||||
ECUtil.encodePoint(ecKey.getW(), params.getCurve());
|
|
||||||
}
|
|
||||||
int keyLenBits = params.getCurve().getField().getFieldSize();
|
int keyLenBits = params.getCurve().getField().getFieldSize();
|
||||||
secretLen = (keyLenBits + 7) >> 3;
|
secretLen = (keyLenBits + 7) >> 3;
|
||||||
|
|
||||||
return null;
|
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
|
// see JCE spec
|
||||||
@Override
|
@Override
|
||||||
protected byte[] engineGenerateSecret() throws IllegalStateException {
|
protected byte[] engineGenerateSecret() throws IllegalStateException {
|
||||||
if ((privateKey == null) || (publicValue == null)) {
|
if ((privateKey == null) || (publicKey == null)) {
|
||||||
throw new IllegalStateException("Not initialized correctly");
|
throw new IllegalStateException("Not initialized correctly");
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] s = privateKey.getS().toByteArray();
|
Optional<byte[]> resultOpt = deriveKeyImpl(privateKey, publicKey);
|
||||||
byte[] encodedParams = // DER OID
|
byte[] result = resultOpt.orElseGet(
|
||||||
ECUtil.encodeECParameterSpec(null, privateKey.getParams());
|
() -> deriveKeyNative(privateKey, publicKey)
|
||||||
|
);
|
||||||
try {
|
publicKey = null;
|
||||||
|
return result;
|
||||||
byte[] result = deriveKey(s, publicValue, encodedParams);
|
|
||||||
publicValue = null;
|
|
||||||
return result;
|
|
||||||
|
|
||||||
} catch (GeneralSecurityException e) {
|
|
||||||
throw new ProviderException("Could not derive key", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// see JCE spec
|
// see JCE spec
|
||||||
@ -143,7 +180,8 @@ public final class ECDHKeyAgreement extends KeyAgreementSpi {
|
|||||||
offset) throws IllegalStateException, ShortBufferException {
|
offset) throws IllegalStateException, ShortBufferException {
|
||||||
if (offset + secretLen > sharedSecret.length) {
|
if (offset + secretLen > sharedSecret.length) {
|
||||||
throw new ShortBufferException("Need " + secretLen
|
throw new ShortBufferException("Need " + secretLen
|
||||||
+ " bytes, only " + (sharedSecret.length - offset) + " available");
|
+ " bytes, only " + (sharedSecret.length - offset)
|
||||||
|
+ " available");
|
||||||
}
|
}
|
||||||
byte[] secret = engineGenerateSecret();
|
byte[] secret = engineGenerateSecret();
|
||||||
System.arraycopy(secret, 0, sharedSecret, offset, secret.length);
|
System.arraycopy(secret, 0, sharedSecret, offset, secret.length);
|
||||||
@ -165,6 +203,78 @@ public final class ECDHKeyAgreement extends KeyAgreementSpi {
|
|||||||
return new SecretKeySpec(engineGenerateSecret(), "TlsPremasterSecret");
|
return new SecretKeySpec(engineGenerateSecret(), "TlsPremasterSecret");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static
|
||||||
|
Optional<byte[]> deriveKeyImpl(ECPrivateKey priv, ECPublicKey pubKey) {
|
||||||
|
|
||||||
|
ECParameterSpec ecSpec = priv.getParams();
|
||||||
|
EllipticCurve curve = ecSpec.getCurve();
|
||||||
|
Optional<ECOperations> opsOpt = ECOperations.forParameters(ecSpec);
|
||||||
|
if (opsOpt.isEmpty()) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
ECOperations ops = opsOpt.get();
|
||||||
|
if (! (priv instanceof ECPrivateKeyImpl)) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
ECPrivateKeyImpl privImpl = (ECPrivateKeyImpl) priv;
|
||||||
|
byte[] sArr = privImpl.getArrayS();
|
||||||
|
|
||||||
|
// 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.
|
* 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.*;
|
||||||
import java.security.interfaces.*;
|
import java.security.interfaces.*;
|
||||||
import java.security.spec.*;
|
import java.security.spec.*;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import sun.security.jca.JCAUtil;
|
import sun.security.jca.JCAUtil;
|
||||||
import sun.security.util.*;
|
import sun.security.util.*;
|
||||||
|
import static sun.security.ec.ECOperations.IntermediateValueException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ECDSA signature implementation. This class currently supports the
|
* ECDSA signature implementation. This class currently supports the
|
||||||
@ -147,7 +149,7 @@ abstract class ECDSASignature extends SignatureSpi {
|
|||||||
// Stores the precomputed message digest value.
|
// Stores the precomputed message digest value.
|
||||||
@Override
|
@Override
|
||||||
protected void engineUpdate(byte[] b, int off, int len)
|
protected void engineUpdate(byte[] b, int off, int len)
|
||||||
throws SignatureException {
|
throws SignatureException {
|
||||||
if (offset >= precomputedDigest.length) {
|
if (offset >= precomputedDigest.length) {
|
||||||
offset = RAW_ECDSA_MAX + 1;
|
offset = RAW_ECDSA_MAX + 1;
|
||||||
return;
|
return;
|
||||||
@ -172,7 +174,7 @@ abstract class ECDSASignature extends SignatureSpi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void resetDigest(){
|
protected void resetDigest() {
|
||||||
offset = 0;
|
offset = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -222,14 +224,14 @@ abstract class ECDSASignature extends SignatureSpi {
|
|||||||
// Nested class for SHA224withECDSA signatures
|
// Nested class for SHA224withECDSA signatures
|
||||||
public static final class SHA224 extends ECDSASignature {
|
public static final class SHA224 extends ECDSASignature {
|
||||||
public SHA224() {
|
public SHA224() {
|
||||||
super("SHA-224");
|
super("SHA-224");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nested class for SHA224withECDSAinP1363Format signatures
|
// Nested class for SHA224withECDSAinP1363Format signatures
|
||||||
public static final class SHA224inP1363Format extends ECDSASignature {
|
public static final class SHA224inP1363Format extends ECDSASignature {
|
||||||
public SHA224inP1363Format() {
|
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
|
// initialize for verification. See JCA doc
|
||||||
@Override
|
@Override
|
||||||
protected void engineInitVerify(PublicKey publicKey)
|
protected void engineInitVerify(PublicKey publicKey)
|
||||||
throws InvalidKeyException {
|
throws InvalidKeyException {
|
||||||
this.publicKey = (ECPublicKey) ECKeyFactory.toECKey(publicKey);
|
this.publicKey = (ECPublicKey) ECKeyFactory.toECKey(publicKey);
|
||||||
|
|
||||||
// Should check that the supplied key is appropriate for signature
|
// 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
|
// initialize for signing. See JCA doc
|
||||||
@Override
|
@Override
|
||||||
protected void engineInitSign(PrivateKey privateKey)
|
protected void engineInitSign(PrivateKey privateKey)
|
||||||
throws InvalidKeyException {
|
throws InvalidKeyException {
|
||||||
engineInitSign(privateKey, null);
|
engineInitSign(privateKey, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// initialize for signing. See JCA doc
|
// initialize for signing. See JCA doc
|
||||||
@Override
|
@Override
|
||||||
protected void engineInitSign(PrivateKey privateKey, SecureRandom random)
|
protected void engineInitSign(PrivateKey privateKey, SecureRandom random)
|
||||||
throws InvalidKeyException {
|
throws InvalidKeyException {
|
||||||
this.privateKey = (ECPrivateKey) ECKeyFactory.toECKey(privateKey);
|
this.privateKey = (ECPrivateKey) ECKeyFactory.toECKey(privateKey);
|
||||||
|
|
||||||
// Should check that the supplied key is appropriate for signature
|
// 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
|
// update the signature with the plaintext data. See JCA doc
|
||||||
@Override
|
@Override
|
||||||
protected void engineUpdate(byte[] b, int off, int len)
|
protected void engineUpdate(byte[] b, int off, int len)
|
||||||
throws SignatureException {
|
throws SignatureException {
|
||||||
messageDigest.update(b, off, len);
|
messageDigest.update(b, off, len);
|
||||||
needsReset = true;
|
needsReset = true;
|
||||||
}
|
}
|
||||||
@ -354,20 +356,67 @@ abstract class ECDSASignature extends SignatureSpi {
|
|||||||
needsReset = true;
|
needsReset = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// sign the data and return the signature. See JCA doc
|
private byte[] signDigestImpl(ECDSAOperations ops, int seedBits,
|
||||||
@Override
|
byte[] digest, ECPrivateKeyImpl privImpl, SecureRandom random)
|
||||||
protected byte[] engineSign() throws SignatureException {
|
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();
|
byte[] s = privateKey.getS().toByteArray();
|
||||||
ECParameterSpec params = privateKey.getParams();
|
ECParameterSpec params = privateKey.getParams();
|
||||||
|
|
||||||
// DER OID
|
// DER OID
|
||||||
byte[] encodedParams = ECUtil.encodeECParameterSpec(null, params);
|
byte[] encodedParams = ECUtil.encodeECParameterSpec(null, params);
|
||||||
int keySize = params.getCurve().getField().getFieldSize();
|
int keySize = params.getCurve().getField().getFieldSize();
|
||||||
|
|
||||||
// seed is twice the key size (in bytes) plus 1
|
// seed is twice the key size (in bytes) plus 1
|
||||||
byte[] seed = new byte[(((keySize + 7) >> 3) + 1) * 2];
|
byte[] seed = new byte[(((keySize + 7) >> 3) + 1) * 2];
|
||||||
if (random == null) {
|
|
||||||
random = JCAUtil.getSecureRandom();
|
|
||||||
}
|
|
||||||
random.nextBytes(seed);
|
random.nextBytes(seed);
|
||||||
|
|
||||||
// random bits needed for timing countermeasures
|
// random bits needed for timing countermeasures
|
||||||
@ -375,14 +424,32 @@ abstract class ECDSASignature extends SignatureSpi {
|
|||||||
// values must be non-zero to enable countermeasures
|
// values must be non-zero to enable countermeasures
|
||||||
timingArgument |= 1;
|
timingArgument |= 1;
|
||||||
|
|
||||||
byte[] sig;
|
|
||||||
try {
|
try {
|
||||||
sig = signDigest(getDigestValue(), s, encodedParams, seed,
|
return signDigest(digest, s, encodedParams, seed,
|
||||||
timingArgument);
|
timingArgument);
|
||||||
} catch (GeneralSecurityException e) {
|
} catch (GeneralSecurityException e) {
|
||||||
throw new SignatureException("Could not sign data", 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) {
|
if (p1363Format) {
|
||||||
return sig;
|
return sig;
|
||||||
} else {
|
} else {
|
||||||
@ -400,7 +467,7 @@ abstract class ECDSASignature extends SignatureSpi {
|
|||||||
byte[] encodedParams = ECUtil.encodeECParameterSpec(null, params);
|
byte[] encodedParams = ECUtil.encodeECParameterSpec(null, params);
|
||||||
|
|
||||||
if (publicKey instanceof ECPublicKeyImpl) {
|
if (publicKey instanceof ECPublicKeyImpl) {
|
||||||
w = ((ECPublicKeyImpl)publicKey).getEncodedPublicValue();
|
w = ((ECPublicKeyImpl) publicKey).getEncodedPublicValue();
|
||||||
} else { // instanceof ECPublicKey
|
} else { // instanceof ECPublicKey
|
||||||
w = ECUtil.encodePoint(publicKey.getW(), params.getCurve());
|
w = ECUtil.encodePoint(publicKey.getW(), params.getCurve());
|
||||||
}
|
}
|
||||||
@ -423,13 +490,13 @@ abstract class ECDSASignature extends SignatureSpi {
|
|||||||
@Override
|
@Override
|
||||||
@Deprecated
|
@Deprecated
|
||||||
protected void engineSetParameter(String param, Object value)
|
protected void engineSetParameter(String param, Object value)
|
||||||
throws InvalidParameterException {
|
throws InvalidParameterException {
|
||||||
throw new UnsupportedOperationException("setParameter() not supported");
|
throw new UnsupportedOperationException("setParameter() not supported");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void engineSetParameter(AlgorithmParameterSpec params)
|
protected void engineSetParameter(AlgorithmParameterSpec params)
|
||||||
throws InvalidAlgorithmParameterException {
|
throws InvalidAlgorithmParameterException {
|
||||||
if (params != null) {
|
if (params != null) {
|
||||||
throw new InvalidAlgorithmParameterException("No parameter accepted");
|
throw new InvalidAlgorithmParameterException("No parameter accepted");
|
||||||
}
|
}
|
||||||
@ -439,7 +506,7 @@ abstract class ECDSASignature extends SignatureSpi {
|
|||||||
@Override
|
@Override
|
||||||
@Deprecated
|
@Deprecated
|
||||||
protected Object engineGetParameter(String param)
|
protected Object engineGetParameter(String param)
|
||||||
throws InvalidParameterException {
|
throws InvalidParameterException {
|
||||||
throw new UnsupportedOperationException("getParameter() not supported");
|
throw new UnsupportedOperationException("getParameter() not supported");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -464,7 +531,7 @@ abstract class ECDSASignature extends SignatureSpi {
|
|||||||
out.putInteger(r);
|
out.putInteger(r);
|
||||||
out.putInteger(s);
|
out.putInteger(s);
|
||||||
DerValue result =
|
DerValue result =
|
||||||
new DerValue(DerValue.tag_Sequence, out.toByteArray());
|
new DerValue(DerValue.tag_Sequence, out.toByteArray());
|
||||||
|
|
||||||
return result.toByteArray();
|
return result.toByteArray();
|
||||||
|
|
||||||
@ -497,9 +564,9 @@ abstract class ECDSASignature extends SignatureSpi {
|
|||||||
// r and s each occupy half the array
|
// r and s each occupy half the array
|
||||||
byte[] result = new byte[k << 1];
|
byte[] result = new byte[k << 1];
|
||||||
System.arraycopy(rBytes, 0, result, k - rBytes.length,
|
System.arraycopy(rBytes, 0, result, k - rBytes.length,
|
||||||
rBytes.length);
|
rBytes.length);
|
||||||
System.arraycopy(sBytes, 0, result, result.length - sBytes.length,
|
System.arraycopy(sBytes, 0, result, result.length - sBytes.length,
|
||||||
sBytes.length);
|
sBytes.length);
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@ -539,13 +606,13 @@ abstract class ECDSASignature extends SignatureSpi {
|
|||||||
* @return byte[] the signature.
|
* @return byte[] the signature.
|
||||||
*/
|
*/
|
||||||
private static native byte[] signDigest(byte[] digest, byte[] s,
|
private static native byte[] signDigest(byte[] digest, byte[] s,
|
||||||
byte[] encodedParams, byte[] seed, int timing)
|
byte[] encodedParams, byte[] seed, int timing)
|
||||||
throws GeneralSecurityException;
|
throws GeneralSecurityException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verifies the signed digest using the public key.
|
* 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.
|
* as a concatenation of the key's R and S values.
|
||||||
* @param digest the digest to be used.
|
* @param digest the digest to be used.
|
||||||
* @param w the public key's W point (in uncompressed form).
|
* @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.
|
* @return boolean true if the signature is successfully verified.
|
||||||
*/
|
*/
|
||||||
private static native boolean verifySignedDigest(byte[] signature,
|
private static native boolean verifySignedDigest(byte[] signature,
|
||||||
byte[] digest, byte[] w, byte[] encodedParams)
|
byte[] digest, byte[] w, byte[] encodedParams)
|
||||||
throws GeneralSecurityException;
|
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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* 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.ECParameterSpec;
|
||||||
import java.security.spec.ECPoint;
|
import java.security.spec.ECPoint;
|
||||||
import java.security.spec.InvalidParameterSpecException;
|
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.jca.JCAUtil;
|
||||||
import sun.security.util.ECParameters;
|
|
||||||
import sun.security.util.ECUtil;
|
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.util.SecurityProviderConstants.DEF_EC_KEY_SIZE;
|
||||||
|
import static sun.security.ec.ECOperations.IntermediateValueException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* EC keypair generator.
|
* EC keypair generator.
|
||||||
@ -90,14 +92,14 @@ public final class ECKeyPairGenerator extends KeyPairGeneratorSpi {
|
|||||||
ECParameterSpec ecSpec = null;
|
ECParameterSpec ecSpec = null;
|
||||||
|
|
||||||
if (params instanceof ECParameterSpec) {
|
if (params instanceof ECParameterSpec) {
|
||||||
ecSpec = ECUtil.getECParameterSpec(null,
|
ECParameterSpec ecParams = (ECParameterSpec) params;
|
||||||
(ECParameterSpec)params);
|
ecSpec = ECUtil.getECParameterSpec(null, ecParams);
|
||||||
if (ecSpec == null) {
|
if (ecSpec == null) {
|
||||||
throw new InvalidAlgorithmParameterException(
|
throw new InvalidAlgorithmParameterException(
|
||||||
"Unsupported curve: " + params);
|
"Unsupported curve: " + params);
|
||||||
}
|
}
|
||||||
} else if (params instanceof ECGenParameterSpec) {
|
} else if (params instanceof ECGenParameterSpec) {
|
||||||
String name = ((ECGenParameterSpec)params).getName();
|
String name = ((ECGenParameterSpec) params).getName();
|
||||||
ecSpec = ECUtil.getECParameterSpec(null, name);
|
ecSpec = ECUtil.getECParameterSpec(null, name);
|
||||||
if (ecSpec == null) {
|
if (ecSpec == null) {
|
||||||
throw new InvalidAlgorithmParameterException(
|
throw new InvalidAlgorithmParameterException(
|
||||||
@ -112,8 +114,7 @@ public final class ECKeyPairGenerator extends KeyPairGeneratorSpi {
|
|||||||
ensureCurveIsSupported(ecSpec);
|
ensureCurveIsSupported(ecSpec);
|
||||||
this.params = ecSpec;
|
this.params = ecSpec;
|
||||||
|
|
||||||
this.keySize =
|
this.keySize = ecSpec.getCurve().getField().getFieldSize();
|
||||||
((ECParameterSpec)this.params).getCurve().getField().getFieldSize();
|
|
||||||
this.random = random;
|
this.random = random;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,41 +142,99 @@ public final class ECKeyPairGenerator extends KeyPairGeneratorSpi {
|
|||||||
@Override
|
@Override
|
||||||
public KeyPair generateKeyPair() {
|
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) {
|
if (random == null) {
|
||||||
random = JCAUtil.getSecureRandom();
|
random = JCAUtil.getSecureRandom();
|
||||||
}
|
}
|
||||||
random.nextBytes(seed);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
Optional<KeyPair> kp = generateKeyPairImpl(random);
|
||||||
Object[] keyBytes = generateECKeyPair(keySize, encodedParams, seed);
|
if (kp.isPresent()) {
|
||||||
|
return kp.get();
|
||||||
// The 'params' object supplied above is equivalent to the native
|
}
|
||||||
// one so there is no need to fetch it.
|
return generateKeyPairNative(random);
|
||||||
// keyBytes[0] is the encoding of the native private key
|
} catch (Exception ex) {
|
||||||
BigInteger s = new BigInteger(1, (byte[])keyBytes[0]);
|
throw new ProviderException(ex);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
private void checkKeySize(int keySize) throws InvalidParameterException {
|
||||||
if (keySize < KEY_SIZE_MIN) {
|
if (keySize < KEY_SIZE_MIN) {
|
||||||
throw new InvalidParameterException
|
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
|
* 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
|
* @param encodedParams encoded parameters in the same form accepted
|
||||||
* by generateECKeyPair
|
* 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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* 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.interfaces.*;
|
||||||
import java.security.spec.*;
|
import java.security.spec.*;
|
||||||
|
|
||||||
import sun.security.util.DerInputStream;
|
import sun.security.util.*;
|
||||||
import sun.security.util.DerOutputStream;
|
|
||||||
import sun.security.util.DerValue;
|
|
||||||
import sun.security.util.ECParameters;
|
|
||||||
import sun.security.util.ECUtil;
|
|
||||||
import sun.security.x509.AlgorithmId;
|
import sun.security.x509.AlgorithmId;
|
||||||
import sun.security.pkcs.PKCS8Key;
|
import sun.security.pkcs.PKCS8Key;
|
||||||
|
|
||||||
@ -68,6 +64,7 @@ public final class ECPrivateKeyImpl extends PKCS8Key implements ECPrivateKey {
|
|||||||
private static final long serialVersionUID = 88695385615075129L;
|
private static final long serialVersionUID = 88695385615075129L;
|
||||||
|
|
||||||
private BigInteger s; // private value
|
private BigInteger s; // private value
|
||||||
|
private byte[] arrayS; // private value as a little-endian array
|
||||||
private ECParameterSpec params;
|
private ECParameterSpec params;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -85,13 +82,25 @@ public final class ECPrivateKeyImpl extends PKCS8Key implements ECPrivateKey {
|
|||||||
throws InvalidKeyException {
|
throws InvalidKeyException {
|
||||||
this.s = s;
|
this.s = s;
|
||||||
this.params = params;
|
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
|
algid = new AlgorithmId
|
||||||
(AlgorithmId.EC_oid, ECParameters.getAlgorithmParameters(params));
|
(AlgorithmId.EC_oid, ECParameters.getAlgorithmParameters(params));
|
||||||
try {
|
try {
|
||||||
DerOutputStream out = new DerOutputStream();
|
DerOutputStream out = new DerOutputStream();
|
||||||
out.putInteger(1); // version 1
|
out.putInteger(1); // version 1
|
||||||
byte[] privBytes = ECUtil.trimZeroes(s.toByteArray());
|
byte[] privBytes = s.clone();
|
||||||
|
ArrayUtil.reverse(privBytes);
|
||||||
out.putOctetString(privBytes);
|
out.putOctetString(privBytes);
|
||||||
DerValue val =
|
DerValue val =
|
||||||
new DerValue(DerValue.tag_Sequence, out.toByteArray());
|
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
|
// see JCA doc
|
||||||
public String getAlgorithm() {
|
public String getAlgorithm() {
|
||||||
return "EC";
|
return "EC";
|
||||||
@ -109,9 +143,26 @@ public final class ECPrivateKeyImpl extends PKCS8Key implements ECPrivateKey {
|
|||||||
|
|
||||||
// see JCA doc
|
// see JCA doc
|
||||||
public BigInteger getS() {
|
public BigInteger getS() {
|
||||||
|
if (s == null) {
|
||||||
|
byte[] arrCopy = arrayS.clone();
|
||||||
|
ArrayUtil.reverse(arrCopy);
|
||||||
|
s = new BigInteger(1, arrCopy);
|
||||||
|
}
|
||||||
return s;
|
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
|
// see JCA doc
|
||||||
public ECParameterSpec getParams() {
|
public ECParameterSpec getParams() {
|
||||||
return params;
|
return params;
|
||||||
@ -133,12 +184,13 @@ public final class ECPrivateKeyImpl extends PKCS8Key implements ECPrivateKey {
|
|||||||
throw new IOException("Version must be 1");
|
throw new IOException("Version must be 1");
|
||||||
}
|
}
|
||||||
byte[] privData = data.getOctetString();
|
byte[] privData = data.getOctetString();
|
||||||
s = new BigInteger(1, privData);
|
ArrayUtil.reverse(privData);
|
||||||
|
arrayS = privData;
|
||||||
while (data.available() != 0) {
|
while (data.available() != 0) {
|
||||||
DerValue value = data.getDerValue();
|
DerValue value = data.getDerValue();
|
||||||
if (value.isContextSpecific((byte)0)) {
|
if (value.isContextSpecific((byte) 0)) {
|
||||||
// ignore for now
|
// ignore for now
|
||||||
} else if (value.isContextSpecific((byte)1)) {
|
} else if (value.isContextSpecific((byte) 1)) {
|
||||||
// ignore for now
|
// ignore for now
|
||||||
} else {
|
} else {
|
||||||
throw new InvalidKeyException("Unexpected value: " + value);
|
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