Skip to content
This repository has been archived by the owner on Feb 2, 2023. It is now read-only.

Commit

Permalink
8288508: Enhance ECDSA usage
Browse files Browse the repository at this point in the history
Reviewed-by: abakhtin, bae
Backport-of: efd603063e60ca6861b41309445d7b8e20768d9b
  • Loading branch information
Aleksei Voitylov authored and Yuri Nesterenko committed Oct 18, 2022
1 parent 4b2dd76 commit 49f3d63
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 27 deletions.
41 changes: 40 additions & 1 deletion src/java.base/share/classes/sun/security/util/ECUtil.java
@@ -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
Expand Down Expand Up @@ -305,5 +305,44 @@ public static byte[] decodeSignature(byte[] sig) throws SignatureException {
}
}

// 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() {}
}
50 changes: 25 additions & 25 deletions src/jdk.crypto.ec/share/classes/sun/security/ec/ECDSASignature.java
Expand Up @@ -485,6 +485,9 @@ protected byte[] engineSign() throws SignatureException {
@Override
protected boolean engineVerify(byte[] signature) throws SignatureException {

ECPoint w = publicKey.getW();
ECParameterSpec params = publicKey.getParams();

byte[] sig;
if (p1363Format) {
sig = signature;
Expand All @@ -493,11 +496,25 @@ protected boolean engineVerify(byte[] signature) throws SignatureException {
}

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());
Expand All @@ -508,41 +525,24 @@ protected boolean engineVerify(byte[] signature) throws SignatureException {
: "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());
Expand Down
16 changes: 16 additions & 0 deletions src/jdk.crypto.ec/share/classes/sun/security/ec/ECOperations.java
Expand Up @@ -26,13 +26,15 @@
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.*;

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;
Expand Down Expand Up @@ -489,5 +491,19 @@ private void setSum(ProjectivePoint.Mutable p, ProjectivePoint.Mutable p2,
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));
}
}

8 changes: 7 additions & 1 deletion src/jdk.crypto.ec/share/native/libsunec/impl/ec.c
@@ -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
Expand Down Expand Up @@ -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 ||
Expand Down

0 comments on commit 49f3d63

Please sign in to comment.