Skip to content

Commit 1d82816

Browse files
Aleksei VoitylovRealCLanger
authored andcommitted
8288508: Enhance ECDSA usage
Reviewed-by: abakhtin, mbalao Backport-of: efd603063e60ca6861b41309445d7b8e20768d9b
1 parent e19abce commit 1d82816

File tree

3 files changed

+74
-13
lines changed

3 files changed

+74
-13
lines changed

src/java.base/share/classes/sun/security/util/ECUtil.java

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2006, 2021, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2006, 2022, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -310,5 +310,40 @@ public static byte[] decodeSignature(byte[] sig) throws SignatureException {
310310
}
311311
}
312312

313+
// Partial Public key validation as described in NIST SP 800-186 Appendix D.1.1.1.
314+
// The extra step in the full validation (described in Appendix D.1.1.2) is implemented
315+
// as sun.security.ec.ECOperations#checkOrder inside the jdk.crypto.ec module.
316+
public static void validatePublicKey(ECPoint point, ECParameterSpec spec)
317+
throws InvalidKeyException {
318+
BigInteger p;
319+
if (spec.getCurve().getField() instanceof ECFieldFp f) {
320+
p = f.getP();
321+
} else {
322+
throw new InvalidKeyException("Only curves over prime fields are supported");
323+
}
324+
325+
// 1. If Q is the point at infinity, output REJECT
326+
if (point.equals(ECPoint.POINT_INFINITY)) {
327+
throw new InvalidKeyException("Public point is at infinity");
328+
}
329+
// 2. Verify that x and y are integers in the interval [0, p-1]. Output REJECT if verification fails.
330+
BigInteger x = point.getAffineX();
331+
if (x.signum() < 0 || x.compareTo(p) >= 0) {
332+
throw new InvalidKeyException("Public point x is not in the interval [0, p-1]");
333+
}
334+
BigInteger y = point.getAffineY();
335+
if (y.signum() < 0 || y.compareTo(p) >= 0) {
336+
throw new InvalidKeyException("Public point y is not in the interval [0, p-1]");
337+
}
338+
// 3. Verify that (x, y) is a point on the W_a,b by checking that (x, y) satisfies the defining
339+
// equation y^2 = x^3 + a x + b where computations are carried out in GF(p). Output REJECT
340+
// if verification fails.
341+
BigInteger left = y.modPow(BigInteger.TWO, p);
342+
BigInteger right = x.pow(3).add(spec.getCurve().getA().multiply(x)).add(spec.getCurve().getB()).mod(p);
343+
if (!left.equals(right)) {
344+
throw new InvalidKeyException("Public point is not on the curve");
345+
}
346+
}
347+
313348
private ECUtil() {}
314349
}

src/jdk.crypto.ec/share/classes/sun/security/ec/ECDSASignature.java

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -482,8 +482,7 @@ protected byte[] engineSign() throws SignatureException {
482482
Optional<ECDSAOperations> opsOpt =
483483
ECDSAOperations.forParameters(params);
484484
if (opsOpt.isEmpty()) {
485-
throw new SignatureException("Curve not supported: " +
486-
params.toString());
485+
throw new SignatureException("Curve not supported: " + params);
487486
}
488487
byte[] sig = signDigestImpl(opsOpt.get(), seedBits, digest, privateKey,
489488
random);
@@ -499,22 +498,33 @@ protected byte[] engineSign() throws SignatureException {
499498
@Override
500499
protected boolean engineVerify(byte[] signature) throws SignatureException {
501500

501+
ECPoint w = publicKey.getW();
502+
ECParameterSpec params = publicKey.getParams();
503+
504+
// Partial public key validation
505+
try {
506+
ECUtil.validatePublicKey(w, params);
507+
} catch (InvalidKeyException e) {
508+
return false;
509+
}
510+
511+
ECDSAOperations ops = ECDSAOperations.forParameters(params)
512+
.orElseThrow(() -> new SignatureException("Curve not supported: " + params));
513+
514+
// Full public key validation, only necessary when h != 1.
515+
if (params.getCofactor() != 1) {
516+
if (!ops.getEcOperations().checkOrder(w)) {
517+
return false;
518+
}
519+
}
520+
502521
byte[] sig;
503522
if (p1363Format) {
504523
sig = signature;
505524
} else {
506525
sig = ECUtil.decodeSignature(signature);
507526
}
508-
509-
byte[] digest = getDigestValue();
510-
511-
Optional<ECDSAOperations> opsOpt =
512-
ECDSAOperations.forParameters(publicKey.getParams());
513-
if (opsOpt.isEmpty()) {
514-
throw new SignatureException("Curve not supported: " +
515-
publicKey.getParams().toString());
516-
}
517-
return opsOpt.get().verifySignedDigest(digest, sig, publicKey.getW());
527+
return ops.verifySignedDigest(getDigestValue(), sig, w);
518528
}
519529

520530
// set parameter, not supported. See JCA doc

src/jdk.crypto.ec/share/classes/sun/security/ec/ECOperations.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,15 @@
2626
package sun.security.ec;
2727

2828
import sun.security.ec.point.*;
29+
import sun.security.util.ArrayUtil;
2930
import sun.security.util.math.*;
3031
import sun.security.util.math.intpoly.*;
3132

3233
import java.math.BigInteger;
3334
import java.security.ProviderException;
3435
import java.security.spec.ECFieldFp;
3536
import java.security.spec.ECParameterSpec;
37+
import java.security.spec.ECPoint;
3638
import java.security.spec.EllipticCurve;
3739
import java.util.Map;
3840
import java.util.Optional;
@@ -489,5 +491,19 @@ private void setSum(ProjectivePoint.Mutable p, ProjectivePoint.Mutable p2,
489491
p.getZ().setSum(t1);
490492

491493
}
494+
495+
// The extra step in the Full Public key validation as described in
496+
// NIST SP 800-186 Appendix D.1.1.2
497+
public boolean checkOrder(ECPoint point) {
498+
BigInteger x = point.getAffineX();
499+
BigInteger y = point.getAffineY();
500+
501+
// Verify that n Q = INFINITY. Output REJECT if verification fails.
502+
IntegerFieldModuloP field = this.getField();
503+
AffinePoint ap = new AffinePoint(field.getElement(x), field.getElement(y));
504+
byte[] scalar = this.orderField.getSize().toByteArray();
505+
ArrayUtil.reverse(scalar);
506+
return isNeutral(this.multiply(ap, scalar));
507+
}
492508
}
493509

0 commit comments

Comments
 (0)