diff --git a/src/java.base/share/classes/sun/security/util/ECUtil.java b/src/java.base/share/classes/sun/security/util/ECUtil.java index 739f3648dfd..f3cfd15a57d 100644 --- a/src/java.base/share/classes/sun/security/util/ECUtil.java +++ b/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 @@ -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() {} } diff --git a/src/jdk.crypto.ec/share/classes/sun/security/ec/ECDSASignature.java b/src/jdk.crypto.ec/share/classes/sun/security/ec/ECDSASignature.java index 5f34d123a4a..31b3136769a 100644 --- a/src/jdk.crypto.ec/share/classes/sun/security/ec/ECDSASignature.java +++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/ECDSASignature.java @@ -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 verifyOpt - = verifySignedDigestAvailable(publicKey, sig, digest); - if (verifyOpt.isPresent()) { - return verifyOpt.get(); + Optional 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 verifySignedDigestAvailable( - ECPublicKey publicKey, byte[] sig, byte[] digestValue) { - - ECParameterSpec params = publicKey.getParams(); - - Optional 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()); diff --git a/src/jdk.crypto.ec/share/classes/sun/security/ec/ECOperations.java b/src/jdk.crypto.ec/share/classes/sun/security/ec/ECOperations.java index 2995ef752c8..6f611eebbcc 100644 --- a/src/jdk.crypto.ec/share/classes/sun/security/ec/ECOperations.java +++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/ECOperations.java @@ -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)); + } } diff --git a/src/jdk.crypto.ec/share/native/libsunec/impl/ec.c b/src/jdk.crypto.ec/share/native/libsunec/impl/ec.c index 8c98bdd74d4..25c062a5889 100644 --- a/src/jdk.crypto.ec/share/native/libsunec/impl/ec.c +++ b/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 @@ -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 ||