8288508: Enhance ECDSA usage

Reviewed-by: abakhtin, bae
Backport-of: efd603063e60ca6861b41309445d7b8e20768d9b
This commit is contained in:
Aleksei Voitylov 2022-09-06 20:45:59 +00:00 committed by Yuri Nesterenko
parent 4b2dd7650b
commit 49f3d639ad
4 changed files with 88 additions and 27 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2006, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -305,5 +305,44 @@ public final class ECUtil {
}
}
// Partial Public key validation as described in NIST SP 800-186 Appendix D.1.1.1.
// The extra step in the full validation (described in Appendix D.1.1.2) is implemented
// as sun.security.ec.ECOperations#checkOrder inside the jdk.crypto.ec module.
public static void validatePublicKey(ECPoint point, ECParameterSpec spec)
throws InvalidKeyException {
BigInteger p;
if (spec.getCurve().getField() instanceof ECFieldFp) {
ECFieldFp f = (ECFieldFp) spec.getCurve().getField();
p = f.getP();
} else if (spec.getCurve().getField() instanceof ECFieldF2m) {
// curves over binary fields are validated in SunEC code
return;
} else {
throw new InvalidKeyException("Curve field is not supported");
}
// 1. If Q is the point at infinity, output REJECT
if (point.equals(ECPoint.POINT_INFINITY)) {
throw new InvalidKeyException("Public point is at infinity");
}
// 2. Verify that x and y are integers in the interval [0, p-1]. Output REJECT if verification fails.
BigInteger x = point.getAffineX();
if (x.signum() < 0 || x.compareTo(p) >= 0) {
throw new InvalidKeyException("Public point x is not in the interval [0, p-1]");
}
BigInteger y = point.getAffineY();
if (y.signum() < 0 || y.compareTo(p) >= 0) {
throw new InvalidKeyException("Public point y is not in the interval [0, p-1]");
}
// 3. Verify that (x, y) is a point on the W_a,b by checking that (x, y) satisfies the defining
// equation y^2 = x^3 + a x + b where computations are carried out in GF(p). Output REJECT
// if verification fails.
BigInteger left = y.modPow(BigInteger.TWO, p);
BigInteger right = x.pow(3).add(spec.getCurve().getA().multiply(x)).add(spec.getCurve().getB()).mod(p);
if (!left.equals(right)) {
throw new InvalidKeyException("Public point is not on the curve");
}
}
private ECUtil() {}
}

View File

@ -485,6 +485,9 @@ abstract class ECDSASignature extends SignatureSpi {
@Override
protected boolean engineVerify(byte[] signature) throws SignatureException {
ECPoint w = publicKey.getW();
ECParameterSpec params = publicKey.getParams();
byte[] sig;
if (p1363Format) {
sig = signature;
@ -493,11 +496,25 @@ abstract class ECDSASignature extends SignatureSpi {
}
byte[] digest = getDigestValue();
Optional<Boolean> verifyOpt
= verifySignedDigestAvailable(publicKey, sig, digest);
if (verifyOpt.isPresent()) {
return verifyOpt.get();
Optional<ECDSAOperations> ops = ECDSAOperations.forParameters(params);
if (ops.isPresent()) {
// Partial public key validation
try {
ECUtil.validatePublicKey(w, params);
} catch (InvalidKeyException e) {
return false;
}
// Full public key validation, only necessary when h != 1.
if (params.getCofactor() != 1) {
if (!ops.get().getEcOperations().checkOrder(w)) {
return false;
}
}
return verifySignedDigestImpl(ops.get(), digest, publicKey, sig);
} else {
if (SunEC.isNativeDisabled()) {
NamedCurve nc = CurveDB.lookup(publicKey.getParams());
@ -508,41 +525,24 @@ abstract class ECDSASignature extends SignatureSpi {
: "unknown")));
}
byte[] w;
ECParameterSpec params = publicKey.getParams();
// DER OID
byte[] encodedParams = ECUtil.encodeECParameterSpec(null, params);
byte[] encodedW;
if (publicKey instanceof ECPublicKeyImpl) {
w = ((ECPublicKeyImpl) publicKey).getEncodedPublicValue();
encodedW = ((ECPublicKeyImpl) publicKey).getEncodedPublicValue();
} else { // instanceof ECPublicKey
w = ECUtil.encodePoint(publicKey.getW(), params.getCurve());
encodedW = ECUtil.encodePoint(w, params.getCurve());
}
try {
return verifySignedDigest(sig, digest, w, encodedParams);
return verifySignedDigest(sig, digest, encodedW, encodedParams);
} catch (GeneralSecurityException e) {
throw new SignatureException("Could not verify signature", e);
}
}
}
private Optional<Boolean> verifySignedDigestAvailable(
ECPublicKey publicKey, byte[] sig, byte[] digestValue) {
ECParameterSpec params = publicKey.getParams();
Optional<ECDSAOperations> opsOpt =
ECDSAOperations.forParameters(params);
if (opsOpt.isEmpty()) {
return Optional.empty();
} else {
boolean result = verifySignedDigestImpl(opsOpt.get(), digestValue,
publicKey, sig);
return Optional.of(result);
}
}
private boolean verifySignedDigestImpl(ECDSAOperations ops,
byte[] digest, ECPublicKey pub, byte[] sig) {
return ops.verifySignedDigest(digest, sig, pub.getW());

View File

@ -26,6 +26,7 @@
package sun.security.ec;
import sun.security.ec.point.*;
import sun.security.util.ArrayUtil;
import sun.security.util.math.*;
import sun.security.util.math.intpoly.*;
@ -33,6 +34,7 @@ import java.math.BigInteger;
import java.security.ProviderException;
import java.security.spec.ECFieldFp;
import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint;
import java.security.spec.EllipticCurve;
import java.util.Map;
import java.util.Optional;
@ -489,5 +491,19 @@ public class ECOperations {
p.getZ().setSum(t1);
}
// The extra step in the Full Public key validation as described in
// NIST SP 800-186 Appendix D.1.1.2
public boolean checkOrder(ECPoint point) {
BigInteger x = point.getAffineX();
BigInteger y = point.getAffineY();
// Verify that n Q = INFINITY. Output REJECT if verification fails.
IntegerFieldModuloP field = this.getField();
AffinePoint ap = new AffinePoint(field.getElement(x), field.getElement(y));
byte[] scalar = this.orderField.getSize().toByteArray();
ArrayUtil.reverse(scalar);
return isNeutral(this.multiply(ap, scalar));
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2007, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2007, 2022, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
* This library is free software; you can redistribute it and/or
@ -925,6 +925,12 @@ ECDSA_VerifyDigest(ECPublicKey *key, const SECItem *signature,
}
ecParams = &(key->ecParams);
if (EC_ValidatePublicKey(ecParams, &key->publicValue, kmflag) != SECSuccess) {
PORT_SetError(SEC_ERROR_BAD_KEY);
goto cleanup;
}
flen = (ecParams->fieldID.size + 7) >> 3;
olen = ecParams->order.len;
if (signature->len == 0 || signature->len%2 != 0 ||