diff options
author | Prashant Patil <patilprashant@google.com> | 2022-04-04 17:03:06 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2022-04-04 17:03:06 +0000 |
commit | f370f7911b2f90fc299db116b6e3a623dca91398 (patch) | |
tree | 171da275a686c68bd5a5347e43ffd8d12156b42f /keystore-cts/java/com/google/security/wycheproof/testcases/EcdhTest.java | |
parent | 79f88d6cbdbce9979c2a5f9ef8614a421000087f (diff) | |
parent | 0650b8071d04f0f34112a6d78c2a06795e361245 (diff) | |
download | wycheproof-android13-qpr3-s2-release.tar.gz |
KeyStore: Wycheproof github files am: 0978999584 am: 3883de5832 am: d341490638 am: cc03cf106b am: 0650b8071dandroid-13.0.0_r83android-13.0.0_r82android-13.0.0_r81android-13.0.0_r80android-13.0.0_r79android-13.0.0_r78android-13.0.0_r77android-13.0.0_r76android-13.0.0_r75android-13.0.0_r74android-13.0.0_r73android-13.0.0_r72android-13.0.0_r71android-13.0.0_r70android-13.0.0_r69android-13.0.0_r68android-13.0.0_r67android-13.0.0_r66android-13.0.0_r65android-13.0.0_r64android-13.0.0_r63android-13.0.0_r62android-13.0.0_r61android-13.0.0_r60android-13.0.0_r59android-13.0.0_r58android-13.0.0_r56android-13.0.0_r54android-13.0.0_r53android-13.0.0_r52android-13.0.0_r51android-13.0.0_r50android-13.0.0_r49android-13.0.0_r48android-13.0.0_r47android-13.0.0_r46android-13.0.0_r45android-13.0.0_r44android-13.0.0_r43android-13.0.0_r42android-13.0.0_r41android-13.0.0_r40android-13.0.0_r39android-13.0.0_r38android-13.0.0_r37android-13.0.0_r36android-13.0.0_r35android-13.0.0_r34android-13.0.0_r33android-13.0.0_r32android13-qpr3-s9-releaseandroid13-qpr3-s8-releaseandroid13-qpr3-s7-releaseandroid13-qpr3-s6-releaseandroid13-qpr3-s5-releaseandroid13-qpr3-s4-releaseandroid13-qpr3-s3-releaseandroid13-qpr3-s2-releaseandroid13-qpr3-s14-releaseandroid13-qpr3-s13-releaseandroid13-qpr3-s12-releaseandroid13-qpr3-s11-releaseandroid13-qpr3-s10-releaseandroid13-qpr3-s1-releaseandroid13-qpr3-releaseandroid13-qpr3-c-s8-releaseandroid13-qpr3-c-s7-releaseandroid13-qpr3-c-s6-releaseandroid13-qpr3-c-s5-releaseandroid13-qpr3-c-s4-releaseandroid13-qpr3-c-s3-releaseandroid13-qpr3-c-s2-releaseandroid13-qpr3-c-s12-releaseandroid13-qpr3-c-s11-releaseandroid13-qpr3-c-s10-releaseandroid13-qpr3-c-s1-releaseandroid13-qpr2-s9-releaseandroid13-qpr2-s8-releaseandroid13-qpr2-s7-releaseandroid13-qpr2-s6-releaseandroid13-qpr2-s5-releaseandroid13-qpr2-s3-releaseandroid13-qpr2-s2-releaseandroid13-qpr2-s12-releaseandroid13-qpr2-s11-releaseandroid13-qpr2-s10-releaseandroid13-qpr2-s1-releaseandroid13-qpr2-releaseandroid13-qpr2-b-s1-releaseandroid13-d4-s2-releaseandroid13-d4-s1-releaseandroid13-d4-release
Original change: https://android-review.googlesource.com/c/platform/external/wycheproof/+/2051321
Change-Id: I2fb46e3b9a23541b0416dcd21b61a47118203ea4
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
Diffstat (limited to 'keystore-cts/java/com/google/security/wycheproof/testcases/EcdhTest.java')
-rw-r--r-- | keystore-cts/java/com/google/security/wycheproof/testcases/EcdhTest.java | 1072 |
1 files changed, 1072 insertions, 0 deletions
diff --git a/keystore-cts/java/com/google/security/wycheproof/testcases/EcdhTest.java b/keystore-cts/java/com/google/security/wycheproof/testcases/EcdhTest.java new file mode 100644 index 0000000..517d23b --- /dev/null +++ b/keystore-cts/java/com/google/security/wycheproof/testcases/EcdhTest.java @@ -0,0 +1,1072 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.security.wycheproof; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import com.google.security.wycheproof.WycheproofRunner.NoPresubmitTest; +import com.google.security.wycheproof.WycheproofRunner.ProviderType; +import com.google.security.wycheproof.WycheproofRunner.SlowTest; +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadMXBean; +import java.math.BigInteger; +import java.security.GeneralSecurityException; +import java.security.InvalidKeyException; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.ECFieldFp; +import java.security.spec.ECGenParameterSpec; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPoint; +import java.security.spec.ECPrivateKeySpec; +import java.security.spec.ECPublicKeySpec; +import java.security.spec.EllipticCurve; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.X509EncodedKeySpec; +import javax.crypto.KeyAgreement; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** + * Testing ECDH. + * + * <p><b>Defense in depth</b>: The tests for ECDH assume that a attacker has control over all + * aspects of the public key in an exchange. That means that the attacker can potentially send weak + * or invalid public keys. For example, invalid public keys can contain points not on the curve, + * curves that have been deliberately chosen so that DLs are easy to compute as well as orders or + * cofactors that are wrong. It is expected that implementations validate the inputs of a key + * agreement and that in no case information about the private key is leaked. + * + * <p><b>References:</b> Ingrid Biehl, Bernd Meyer, Volker Müller, "Differential Fault Attacks on + * Elliptic Curve Cryptosystems", Crypto '00, pp. 131-164 + * + * <p>Adrian Antipa, Daniel Brown, Alfred Menezes, Rene Struik, and Scott Vanstone, "Validation of + * Elliptic Curve Public Keys", PKC 2003, https://www.iacr.org/archive/pkc2003/25670211/25670211.pdf + * + * <p><b>Bugs:</b> CVE-2015-7940: BouncyCastle before 1.51 does not validate a point is on the + * curve. BouncyCastle v.1.52 checks that the public key point is on the public key curve but does + * not check whether public key and private key use the same curve. BouncyCastle v.1.53 is still + * vulnerable to attacks with modified public keys. An attacker can change the order of the curve + * used by the public key. ECDHC would then reduce the private key modulo this order, which can be + * used to find the private key. + * + * <p>CVE-2015-6924: Utimaco HSMs vulnerable to invalid curve attacks, which made the private key + * extraction possible. + * + * <p>CVE-2015-7940: Issue with elliptic curve addition in mixed Jacobian-affine coordinates + * + * @author bleichen@google.com (Daniel Bleichenbacher) + */ +// TODO(bleichen): Stuff we haven't implemented: +// - timing attacks +// Stuff we are delaying because there are more important bugs: +// - testWrongOrder using BouncyCastle with ECDHWithSHA1Kdf throws +// java.lang.UnsupportedOperationException: KDF can only be used when algorithm is known +// Not sure if that is expected or another bug. +// CVEs for ECDH we haven't used anywhere. +// - CVE-2014-3470: OpenSSL anonymous ECDH denial of service: triggered by NULL value in +// certificate. +// - CVE-2014-3572: OpenSSL downgrades ECDHE to ECDH +// - CVE-2011-3210: OpenSSL was not thread safe +@RunWith(JUnit4.class) +public class EcdhTest { + + static final String[] ECDH_VARIANTS = { + // Raw ECDH. The shared secret is the x-coordinate of the ECDH computation. + // The tests below assume that this variant is implemenented. + "ECDH", + // ECDHC is a variant described in P1363 7.2.2 ECSVDP-DHC. + // BouncyCastle implements this variant. + "ECDHC", + // A variant with an explicit key derivation function. + // This is implemented by BouncyCastle. + "ECDHWITHSHA1KDF", + }; + + /** Test vectors */ + public static class EcPublicKeyTestVector { + final String comment; + final String encoded; // hexadecimal representation of the X509 encoding + final BigInteger p; // characteristic of the field + final BigInteger n; // order of the subgroup + final BigInteger a; // parameter a of the Weierstrass representation + final BigInteger b; // parameter b of the Weierstrass represnetation + final BigInteger gx; // x-coordinate of the generator + final BigInteger gy; // y-coordainat of the generator + final Integer h; // cofactor: may be null + final BigInteger pubx; // x-coordinate of the public point + final BigInteger puby; // y-coordinate of the public point + + public EcPublicKeyTestVector( + String comment, + String encoded, + BigInteger p, + BigInteger n, + BigInteger a, + BigInteger b, + BigInteger gx, + BigInteger gy, + Integer h, + BigInteger pubx, + BigInteger puby) { + this.comment = comment; + this.encoded = encoded; + this.p = p; + this.n = n; + this.a = a; + this.b = b; + this.gx = gx; + this.gy = gy; + this.h = h; + this.pubx = pubx; + this.puby = puby; + } + + /** + * Returns this key as ECPublicKeySpec or null if the key cannot be represented as + * ECPublicKeySpec. The later happens for example if the order of cofactor are not positive. + */ + public ECPublicKeySpec getSpec() { + try { + ECFieldFp fp = new ECFieldFp(p); + EllipticCurve curve = new EllipticCurve(fp, a, b); + ECPoint g = new ECPoint(gx, gy); + // ECParameterSpec requires that the cofactor h is specified. + if (h == null) { + return null; + } + ECParameterSpec params = new ECParameterSpec(curve, g, n, h); + ECPoint pubPoint = new ECPoint(pubx, puby); + ECPublicKeySpec pub = new ECPublicKeySpec(pubPoint, params); + return pub; + } catch (Exception ex) { + System.out.println(comment + " throws " + ex.toString()); + return null; + } + } + + public X509EncodedKeySpec getX509EncodedKeySpec() { + return new X509EncodedKeySpec(TestUtil.hexToBytes(encoded)); + } + } + +public static final EcPublicKeyTestVector EC_VALID_PUBLIC_KEY = + new EcPublicKeyTestVector( + "unmodified", + "3059301306072a8648ce3d020106082a8648ce3d03010703420004cdeb39edd0" + + "3e2b1a11a5e134ec99d5f25f21673d403f3ecb47bd1fa676638958ea58493b84" + + "29598c0b49bbb85c3303ddb1553c3b761c2caacca71606ba9ebac8", + new BigInteger("ffffffff00000001000000000000000000000000ffffffffffffffffffffffff", 16), + new BigInteger("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", 16), + new BigInteger("ffffffff00000001000000000000000000000000fffffffffffffffffffffffc", 16), + new BigInteger("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", 16), + new BigInteger("6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", 16), + new BigInteger("4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5", 16), + 1, + new BigInteger("cdeb39edd03e2b1a11a5e134ec99d5f25f21673d403f3ecb47bd1fa676638958", 16), + new BigInteger("ea58493b8429598c0b49bbb85c3303ddb1553c3b761c2caacca71606ba9ebac8", 16)); + + public static final EcPublicKeyTestVector[] EC_MODIFIED_PUBLIC_KEYS = { + // Modified keys + new EcPublicKeyTestVector( + "public point not on curve", + "3059301306072a8648ce3d020106082a8648ce3d03010703420004cdeb39edd0" + + "3e2b1a11a5e134ec99d5f25f21673d403f3ecb47bd1fa676638958ea58493b84" + + "29598c0b49bbb85c3303ddb1553c3b761c2caacca71606ba9ebaca", + new BigInteger("ffffffff00000001000000000000000000000000ffffffffffffffffffffffff", 16), + new BigInteger("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", 16), + new BigInteger("ffffffff00000001000000000000000000000000fffffffffffffffffffffffc", 16), + new BigInteger("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", 16), + new BigInteger("6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", 16), + new BigInteger("4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5", 16), + 1, + new BigInteger("cdeb39edd03e2b1a11a5e134ec99d5f25f21673d403f3ecb47bd1fa676638958", 16), + new BigInteger("ea58493b8429598c0b49bbb85c3303ddb1553c3b761c2caacca71606ba9ebaca", 16)), + new EcPublicKeyTestVector( + "public point = (0,0)", + "3059301306072a8648ce3d020106082a8648ce3d030107034200040000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000", + new BigInteger("ffffffff00000001000000000000000000000000ffffffffffffffffffffffff", 16), + new BigInteger("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", 16), + new BigInteger("ffffffff00000001000000000000000000000000fffffffffffffffffffffffc", 16), + new BigInteger("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", 16), + new BigInteger("6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", 16), + new BigInteger("4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5", 16), + 1, + new BigInteger("0"), + new BigInteger("0")), + new EcPublicKeyTestVector( + "order = 1", + "308201133081cc06072a8648ce3d02013081c0020101302c06072a8648ce3d01" + + "01022100ffffffff00000001000000000000000000000000ffffffffffffffff" + + "ffffffff30440420ffffffff00000001000000000000000000000000ffffffff" + + "fffffffffffffffc04205ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53" + + "b0f63bce3c3e27d2604b0441046b17d1f2e12c4247f8bce6e563a440f277037d" + + "812deb33a0f4a13945d898c2964fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33" + + "576b315ececbb6406837bf51f502010102010103420004cdeb39edd03e2b1a11" + + "a5e134ec99d5f25f21673d403f3ecb47bd1fa676638958ea58493b8429598c0b" + + "49bbb85c3303ddb1553c3b761c2caacca71606ba9ebac8", + new BigInteger("ffffffff00000001000000000000000000000000ffffffffffffffffffffffff", 16), + new BigInteger("01", 16), + new BigInteger("ffffffff00000001000000000000000000000000fffffffffffffffffffffffc", 16), + new BigInteger("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", 16), + new BigInteger("6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", 16), + new BigInteger("4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5", 16), + 1, + new BigInteger("cdeb39edd03e2b1a11a5e134ec99d5f25f21673d403f3ecb47bd1fa676638958", 16), + new BigInteger("ea58493b8429598c0b49bbb85c3303ddb1553c3b761c2caacca71606ba9ebac8", 16)), + new EcPublicKeyTestVector( + "order = 26959946660873538060741835960514744168612397095220107664918121663170", + "3082012f3081e806072a8648ce3d02013081dc020101302c06072a8648ce3d01" + + "01022100ffffffff00000001000000000000000000000000ffffffffffffffff" + + "ffffffff30440420ffffffff00000001000000000000000000000000ffffffff" + + "fffffffffffffffc04205ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53" + + "b0f63bce3c3e27d2604b0441046b17d1f2e12c4247f8bce6e563a440f277037d" + + "812deb33a0f4a13945d898c2964fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33" + + "576b315ececbb6406837bf51f5021d00ffffffff00000000ffffffffffffffff" + + "bce6faada7179e84f3b9cac202010103420004cdeb39edd03e2b1a11a5e134ec" + + "99d5f25f21673d403f3ecb47bd1fa676638958ea58493b8429598c0b49bbb85c" + + "3303ddb1553c3b761c2caacca71606ba9ebac8", + new BigInteger("ffffffff00000001000000000000000000000000ffffffffffffffffffffffff", 16), + new BigInteger("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2", 16), + new BigInteger("ffffffff00000001000000000000000000000000fffffffffffffffffffffffc", 16), + new BigInteger("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", 16), + new BigInteger("6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", 16), + new BigInteger("4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5", 16), + 1, + new BigInteger("cdeb39edd03e2b1a11a5e134ec99d5f25f21673d403f3ecb47bd1fa676638958", 16), + new BigInteger("ea58493b8429598c0b49bbb85c3303ddb1553c3b761c2caacca71606ba9ebac8", 16)), + new EcPublicKeyTestVector( + "generator = (0,0)", + "308201333081ec06072a8648ce3d02013081e0020101302c06072a8648ce3d01" + + "01022100ffffffff00000001000000000000000000000000ffffffffffffffff" + + "ffffffff30440420ffffffff00000001000000000000000000000000ffffffff" + + "fffffffffffffffc04205ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53" + + "b0f63bce3c3e27d2604b04410400000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "00000000000000000000000000022100ffffffff00000000ffffffffffffffff" + + "bce6faada7179e84f3b9cac2fc63255102010103420004cdeb39edd03e2b1a11" + + "a5e134ec99d5f25f21673d403f3ecb47bd1fa676638958ea58493b8429598c0b" + + "49bbb85c3303ddb1553c3b761c2caacca71606ba9ebac8", + new BigInteger("ffffffff00000001000000000000000000000000ffffffffffffffffffffffff", 16), + new BigInteger("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", 16), + new BigInteger("ffffffff00000001000000000000000000000000fffffffffffffffffffffffc", 16), + new BigInteger("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", 16), + new BigInteger("0"), + new BigInteger("0"), + 1, + new BigInteger("cdeb39edd03e2b1a11a5e134ec99d5f25f21673d403f3ecb47bd1fa676638958", 16), + new BigInteger("ea58493b8429598c0b49bbb85c3303ddb1553c3b761c2caacca71606ba9ebac8", 16)), + new EcPublicKeyTestVector( + "generator not on curve", + "308201333081ec06072a8648ce3d02013081e0020101302c06072a8648ce3d01" + + "01022100ffffffff00000001000000000000000000000000ffffffffffffffff" + + "ffffffff30440420ffffffff00000001000000000000000000000000ffffffff" + + "fffffffffffffffc04205ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53" + + "b0f63bce3c3e27d2604b0441046b17d1f2e12c4247f8bce6e563a440f277037d" + + "812deb33a0f4a13945d898c2964fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33" + + "576b315ececbb6406837bf51f7022100ffffffff00000000ffffffffffffffff" + + "bce6faada7179e84f3b9cac2fc63255102010103420004cdeb39edd03e2b1a11" + + "a5e134ec99d5f25f21673d403f3ecb47bd1fa676638958ea58493b8429598c0b" + + "49bbb85c3303ddb1553c3b761c2caacca71606ba9ebac8", + new BigInteger("ffffffff00000001000000000000000000000000ffffffffffffffffffffffff", 16), + new BigInteger("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", 16), + new BigInteger("ffffffff00000001000000000000000000000000fffffffffffffffffffffffc", 16), + new BigInteger("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", 16), + new BigInteger("6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", 16), + new BigInteger("4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f7", 16), + 1, + new BigInteger("cdeb39edd03e2b1a11a5e134ec99d5f25f21673d403f3ecb47bd1fa676638958", 16), + new BigInteger("ea58493b8429598c0b49bbb85c3303ddb1553c3b761c2caacca71606ba9ebac8", 16)), + new EcPublicKeyTestVector( + "cofactor = 2", + "308201333081ec06072a8648ce3d02013081e0020101302c06072a8648ce3d01" + + "01022100ffffffff00000001000000000000000000000000ffffffffffffffff" + + "ffffffff30440420ffffffff00000001000000000000000000000000ffffffff" + + "fffffffffffffffc04205ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53" + + "b0f63bce3c3e27d2604b0441046b17d1f2e12c4247f8bce6e563a440f277037d" + + "812deb33a0f4a13945d898c2964fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33" + + "576b315ececbb6406837bf51f5022100ffffffff00000000ffffffffffffffff" + + "bce6faada7179e84f3b9cac2fc63255102010203420004cdeb39edd03e2b1a11" + + "a5e134ec99d5f25f21673d403f3ecb47bd1fa676638958ea58493b8429598c0b" + + "49bbb85c3303ddb1553c3b761c2caacca71606ba9ebac8", + new BigInteger("ffffffff00000001000000000000000000000000ffffffffffffffffffffffff", 16), + new BigInteger("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", 16), + new BigInteger("ffffffff00000001000000000000000000000000fffffffffffffffffffffffc", 16), + new BigInteger("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", 16), + new BigInteger("6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", 16), + new BigInteger("4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5", 16), + 2, + new BigInteger("cdeb39edd03e2b1a11a5e134ec99d5f25f21673d403f3ecb47bd1fa676638958", 16), + new BigInteger("ea58493b8429598c0b49bbb85c3303ddb1553c3b761c2caacca71606ba9ebac8", 16)), + new EcPublicKeyTestVector( + "cofactor = None", + "308201303081e906072a8648ce3d02013081dd020101302c06072a8648ce3d01" + + "01022100ffffffff00000001000000000000000000000000ffffffffffffffff" + + "ffffffff30440420ffffffff00000001000000000000000000000000ffffffff" + + "fffffffffffffffc04205ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53" + + "b0f63bce3c3e27d2604b0441046b17d1f2e12c4247f8bce6e563a440f277037d" + + "812deb33a0f4a13945d898c2964fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33" + + "576b315ececbb6406837bf51f5022100ffffffff00000000ffffffffffffffff" + + "bce6faada7179e84f3b9cac2fc63255103420004cdeb39edd03e2b1a11a5e134" + + "ec99d5f25f21673d403f3ecb47bd1fa676638958ea58493b8429598c0b49bbb8" + + "5c3303ddb1553c3b761c2caacca71606ba9ebac8", + new BigInteger("ffffffff00000001000000000000000000000000ffffffffffffffffffffffff", 16), + new BigInteger("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", 16), + new BigInteger("ffffffff00000001000000000000000000000000fffffffffffffffffffffffc", 16), + new BigInteger("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", 16), + new BigInteger("6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", 16), + new BigInteger("4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5", 16), + null, + new BigInteger("cdeb39edd03e2b1a11a5e134ec99d5f25f21673d403f3ecb47bd1fa676638958", 16), + new BigInteger("ea58493b8429598c0b49bbb85c3303ddb1553c3b761c2caacca71606ba9ebac8", 16)), + new EcPublicKeyTestVector( + "modified prime", + "308201333081ec06072a8648ce3d02013081e0020101302c06072a8648ce3d01" + + "01022100fd091059a6893635f900e9449d63f572b2aebc4cff7b4e5e33f1b200" + + "e8bbc1453044042002f6efa55976c9cb06ff16bb629c0a8d4d5143b40084b1a1" + + "cc0e4dff17443eb704205ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53" + + "b0f63bce3c3e27d2604b0441040000000000000000000006597fa94b1fd90000" + + "000000000000000000000000021b8c7dd77f9a95627922eceefea73f028f1ec9" + + "5ba9b8fa95a3ad24bdf9fff414022100ffffffff00000000ffffffffffffffff" + + "bce6faada7179e84f3b9cac2fc63255102010103420004000000000000000000" + + "0006597fa94b1fd90000000000000000000000000000021b8c7dd77f9a956279" + + "22eceefea73f028f1ec95ba9b8fa95a3ad24bdf9fff414", + new BigInteger("fd091059a6893635f900e9449d63f572b2aebc4cff7b4e5e33f1b200e8bbc145", 16), + new BigInteger("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", 16), + new BigInteger("ffffffff00000001000000000000000000000000fffffffffffffffffffffffc", 16), + new BigInteger("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", 16), + new BigInteger("06597fa94b1fd9000000000000000000000000000002", 16), + new BigInteger("1b8c7dd77f9a95627922eceefea73f028f1ec95ba9b8fa95a3ad24bdf9fff414", 16), + 1, + new BigInteger("06597fa94b1fd9000000000000000000000000000002", 16), + new BigInteger("1b8c7dd77f9a95627922eceefea73f028f1ec95ba9b8fa95a3ad24bdf9fff414", 16)), + new EcPublicKeyTestVector( + "using secp224r1", + "304e301006072a8648ce3d020106052b81040021033a0004074f56dc2ea648ef" + + "89c3b72e23bbd2da36f60243e4d2067b70604af1c2165cec2f86603d60c8a611" + + "d5b84ba3d91dfe1a480825bcc4af3bcf", + new BigInteger("ffffffffffffffffffffffffffffffff000000000000000000000001", 16), + new BigInteger("ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3d", 16), + new BigInteger("fffffffffffffffffffffffffffffffefffffffffffffffffffffffe", 16), + new BigInteger("b4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4", 16), + new BigInteger("b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21", 16), + new BigInteger("bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34", 16), + 1, + new BigInteger("074f56dc2ea648ef89c3b72e23bbd2da36f60243e4d2067b70604af1", 16), + new BigInteger("c2165cec2f86603d60c8a611d5b84ba3d91dfe1a480825bcc4af3bcf", 16)), + new EcPublicKeyTestVector( + "a = 0", + "308201143081cd06072a8648ce3d02013081c1020101302c06072a8648ce3d01" + + "01022100ffffffff00000001000000000000000000000000ffffffffffffffff" + + "ffffffff30250401000420f104880c3980129c7efa19b6b0cb04e547b8d0fc0b" + + "95f4946496dd4ac4a7c440044104cdeb39edd03e2b1a11a5e134ec99d5f25f21" + + "673d403f3ecb47bd1fa676638958ea58493b8429598c0b49bbb85c3303ddb155" + + "3c3b761c2caacca71606ba9ebac8022100ffffffff00000000ffffffffffffff" + + "ffbce6faada7179e84f3b9cac2fc63255102010103420004cdeb39edd03e2b1a" + + "11a5e134ec99d5f25f21673d403f3ecb47bd1fa676638958ea58493b8429598c" + + "0b49bbb85c3303ddb1553c3b761c2caacca71606ba9ebac8", + new BigInteger("ffffffff00000001000000000000000000000000ffffffffffffffffffffffff", 16), + new BigInteger("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", 16), + new BigInteger("0"), + new BigInteger("f104880c3980129c7efa19b6b0cb04e547b8d0fc0b95f4946496dd4ac4a7c440", 16), + new BigInteger("cdeb39edd03e2b1a11a5e134ec99d5f25f21673d403f3ecb47bd1fa676638958", 16), + new BigInteger("ea58493b8429598c0b49bbb85c3303ddb1553c3b761c2caacca71606ba9ebac8", 16), + 1, + new BigInteger("cdeb39edd03e2b1a11a5e134ec99d5f25f21673d403f3ecb47bd1fa676638958", 16), + new BigInteger("ea58493b8429598c0b49bbb85c3303ddb1553c3b761c2caacca71606ba9ebac8", 16)), + new EcPublicKeyTestVector( + "new curve with generator of order 3 that is also on secp256r1", + "308201333081ec06072a8648ce3d02013081e0020101302c06072a8648ce3d01" + + "01022100ffffffff00000001000000000000000000000000ffffffffffffffff" + + "ffffffff3044042046dc879a5c2995d0e6f682468ea95791b7bbd0225cfdb251" + + "3fb10a737afece170420bea6c109251bfe4acf2eeda7c24c4ab70a1473335dec" + + "28b244d4d823d15935e2044104701c05255026aa4630b78fc6b769e388059ab1" + + "443cbdd1f8348bedc3be589dc34cfdab998ad27738ae382aa013986ade0f4859" + + "2a9a1ae37ca61d25ec5356f1bd022100ffffffff00000000ffffffffffffffff" + + "bce6faada7179e84f3b9cac2fc63255102010103420004701c05255026aa4630" + + "b78fc6b769e388059ab1443cbdd1f8348bedc3be589dc3b3025465752d88c851" + + "c7d55fec679521f0b7a6d665e51c8359e2da13aca90e42", + new BigInteger("ffffffff00000001000000000000000000000000ffffffffffffffffffffffff", 16), + new BigInteger("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", 16), + new BigInteger("46dc879a5c2995d0e6f682468ea95791b7bbd0225cfdb2513fb10a737afece17", 16), + new BigInteger("bea6c109251bfe4acf2eeda7c24c4ab70a1473335dec28b244d4d823d15935e2", 16), + new BigInteger("701c05255026aa4630b78fc6b769e388059ab1443cbdd1f8348bedc3be589dc3", 16), + new BigInteger("4cfdab998ad27738ae382aa013986ade0f48592a9a1ae37ca61d25ec5356f1bd", 16), + 1, + new BigInteger("701c05255026aa4630b78fc6b769e388059ab1443cbdd1f8348bedc3be589dc3", 16), + new BigInteger("b3025465752d88c851c7d55fec679521f0b7a6d665e51c8359e2da13aca90e42", 16)), + // Invalid keys + new EcPublicKeyTestVector( + "order = -1157920892103562487626974469494075735299969552241357603" + + "42422259061068512044369", + "308201333081ec06072a8648ce3d02013081e0020101302c06072a8648ce3d01" + + "01022100ffffffff00000001000000000000000000000000ffffffffffffffff" + + "ffffffff30440420ffffffff00000001000000000000000000000000ffffffff" + + "fffffffffffffffc04205ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53" + + "b0f63bce3c3e27d2604b0441046b17d1f2e12c4247f8bce6e563a440f277037d" + + "812deb33a0f4a13945d898c2964fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33" + + "576b315ececbb6406837bf51f50221ff00000000ffffffff0000000000000000" + + "4319055258e8617b0c46353d039cdaaf02010103420004cdeb39edd03e2b1a11" + + "a5e134ec99d5f25f21673d403f3ecb47bd1fa676638958ea58493b8429598c0b" + + "49bbb85c3303ddb1553c3b761c2caacca71606ba9ebac8", + new BigInteger("ffffffff00000001000000000000000000000000ffffffffffffffffffffffff", 16), + new BigInteger( + "-115792089210356248762697446949407573529996955224135760342422259061068512044369"), + new BigInteger("ffffffff00000001000000000000000000000000fffffffffffffffffffffffc", 16), + new BigInteger("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", 16), + new BigInteger("6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", 16), + new BigInteger("4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5", 16), + 1, + new BigInteger("cdeb39edd03e2b1a11a5e134ec99d5f25f21673d403f3ecb47bd1fa676638958", 16), + new BigInteger("ea58493b8429598c0b49bbb85c3303ddb1553c3b761c2caacca71606ba9ebac8", 16)), + new EcPublicKeyTestVector( + "order = 0", + "308201133081cc06072a8648ce3d02013081c0020101302c06072a8648ce3d01" + + "01022100ffffffff00000001000000000000000000000000ffffffffffffffff" + + "ffffffff30440420ffffffff00000001000000000000000000000000ffffffff" + + "fffffffffffffffc04205ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53" + + "b0f63bce3c3e27d2604b0441046b17d1f2e12c4247f8bce6e563a440f277037d" + + "812deb33a0f4a13945d898c2964fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33" + + "576b315ececbb6406837bf51f502010002010103420004cdeb39edd03e2b1a11" + + "a5e134ec99d5f25f21673d403f3ecb47bd1fa676638958ea58493b8429598c0b" + + "49bbb85c3303ddb1553c3b761c2caacca71606ba9ebac8", + new BigInteger("ffffffff00000001000000000000000000000000ffffffffffffffffffffffff", 16), + new BigInteger("0"), + new BigInteger("ffffffff00000001000000000000000000000000fffffffffffffffffffffffc", 16), + new BigInteger("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", 16), + new BigInteger("6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", 16), + new BigInteger("4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5", 16), + 1, + new BigInteger("cdeb39edd03e2b1a11a5e134ec99d5f25f21673d403f3ecb47bd1fa676638958", 16), + new BigInteger("ea58493b8429598c0b49bbb85c3303ddb1553c3b761c2caacca71606ba9ebac8", 16)), + new EcPublicKeyTestVector( + "cofactor = -1", + "308201333081ec06072a8648ce3d02013081e0020101302c06072a8648ce3d01" + + "01022100ffffffff00000001000000000000000000000000ffffffffffffffff" + + "ffffffff30440420ffffffff00000001000000000000000000000000ffffffff" + + "fffffffffffffffc04205ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53" + + "b0f63bce3c3e27d2604b0441046b17d1f2e12c4247f8bce6e563a440f277037d" + + "812deb33a0f4a13945d898c2964fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33" + + "576b315ececbb6406837bf51f5022100ffffffff00000000ffffffffffffffff" + + "bce6faada7179e84f3b9cac2fc6325510201ff03420004cdeb39edd03e2b1a11" + + "a5e134ec99d5f25f21673d403f3ecb47bd1fa676638958ea58493b8429598c0b" + + "49bbb85c3303ddb1553c3b761c2caacca71606ba9ebac8", + new BigInteger("ffffffff00000001000000000000000000000000ffffffffffffffffffffffff", 16), + new BigInteger("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", 16), + new BigInteger("ffffffff00000001000000000000000000000000fffffffffffffffffffffffc", 16), + new BigInteger("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", 16), + new BigInteger("6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", 16), + new BigInteger("4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5", 16), + -1, + new BigInteger("cdeb39edd03e2b1a11a5e134ec99d5f25f21673d403f3ecb47bd1fa676638958", 16), + new BigInteger("ea58493b8429598c0b49bbb85c3303ddb1553c3b761c2caacca71606ba9ebac8", 16)), + new EcPublicKeyTestVector( + "cofactor = 0", + "308201333081ec06072a8648ce3d02013081e0020101302c06072a8648ce3d01" + + "01022100ffffffff00000001000000000000000000000000ffffffffffffffff" + + "ffffffff30440420ffffffff00000001000000000000000000000000ffffffff" + + "fffffffffffffffc04205ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53" + + "b0f63bce3c3e27d2604b0441046b17d1f2e12c4247f8bce6e563a440f277037d" + + "812deb33a0f4a13945d898c2964fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33" + + "576b315ececbb6406837bf51f5022100ffffffff00000000ffffffffffffffff" + + "bce6faada7179e84f3b9cac2fc63255102010003420004cdeb39edd03e2b1a11" + + "a5e134ec99d5f25f21673d403f3ecb47bd1fa676638958ea58493b8429598c0b" + + "49bbb85c3303ddb1553c3b761c2caacca71606ba9ebac8", + new BigInteger("ffffffff00000001000000000000000000000000ffffffffffffffffffffffff", 16), + new BigInteger("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", 16), + new BigInteger("ffffffff00000001000000000000000000000000fffffffffffffffffffffffc", 16), + new BigInteger("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", 16), + new BigInteger("6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", 16), + new BigInteger("4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5", 16), + 0, + new BigInteger("cdeb39edd03e2b1a11a5e134ec99d5f25f21673d403f3ecb47bd1fa676638958", 16), + new BigInteger("ea58493b8429598c0b49bbb85c3303ddb1553c3b761c2caacca71606ba9ebac8", 16)), + }; + + /** Checks that key agreement using ECDH works. */ + @Test + public void testBasic() throws Exception { + KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC"); + ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp256r1"); + keyGen.initialize(ecSpec); + KeyPair keyPairA = keyGen.generateKeyPair(); + KeyPair keyPairB = keyGen.generateKeyPair(); + + KeyAgreement kaA = KeyAgreement.getInstance("ECDH"); + KeyAgreement kaB = KeyAgreement.getInstance("ECDH"); + kaA.init(keyPairA.getPrivate()); + kaB.init(keyPairB.getPrivate()); + kaA.doPhase(keyPairB.getPublic(), true); + kaB.doPhase(keyPairA.getPublic(), true); + byte[] kAB = kaA.generateSecret(); + byte[] kBA = kaB.generateSecret(); + assertEquals(TestUtil.bytesToHex(kAB), TestUtil.bytesToHex(kBA)); + } + + @NoPresubmitTest( + providers = {ProviderType.BOUNCY_CASTLE}, + bugs = {"BouncyCastle uses long encoding. Is this a bug?"} + ) + @Test + public void testEncode() throws Exception { + KeyFactory kf = KeyFactory.getInstance("EC"); + ECPublicKey valid = (ECPublicKey) kf.generatePublic(EC_VALID_PUBLIC_KEY.getSpec()); + assertEquals(TestUtil.bytesToHex(valid.getEncoded()), EC_VALID_PUBLIC_KEY.encoded); + } + + @Test + public void testDecode() throws Exception { + KeyFactory kf = KeyFactory.getInstance("EC"); + ECPublicKey key1 = (ECPublicKey) kf.generatePublic(EC_VALID_PUBLIC_KEY.getSpec()); + ECPublicKey key2 = (ECPublicKey) kf.generatePublic(EC_VALID_PUBLIC_KEY.getX509EncodedKeySpec()); + ECParameterSpec params1 = key1.getParams(); + ECParameterSpec params2 = key2.getParams(); + assertEquals(params1.getCofactor(), params2.getCofactor()); + assertEquals(params1.getCurve(), params2.getCurve()); + assertEquals(params1.getGenerator(), params2.getGenerator()); + assertEquals(params1.getOrder(), params2.getOrder()); + assertEquals(key1.getW(), key2.getW()); + } + + /** + * This test modifies the order of group in the public key. A severe bug would be an + * implementation that leaks information whether the private key is larger than the order given in + * the public key. Also a severe bug would be to reduce the private key modulo the order given in + * the public key parameters. + */ + @SuppressWarnings("InsecureCryptoUsage") + public void testModifiedPublic(String algorithm) throws Exception { + KeyAgreement ka; + try { + ka = KeyAgreement.getInstance(algorithm); + } catch (NoSuchAlgorithmException ex) { + System.out.println("testWrongOrder: " + algorithm + " not supported"); + return; + } + KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC"); + keyGen.initialize(EcUtil.getNistP256Params()); + ECPrivateKey priv = (ECPrivateKey) keyGen.generateKeyPair().getPrivate(); + KeyFactory kf = KeyFactory.getInstance("EC"); + ECPublicKey validKey = (ECPublicKey) kf.generatePublic(EC_VALID_PUBLIC_KEY.getSpec()); + ka.init(priv); + ka.doPhase(validKey, true); + String expected = TestUtil.bytesToHex(ka.generateSecret()); + for (EcPublicKeyTestVector test : EC_MODIFIED_PUBLIC_KEYS) { + try { + X509EncodedKeySpec spec = test.getX509EncodedKeySpec(); + ECPublicKey modifiedKey = (ECPublicKey) kf.generatePublic(spec); + ka.init(priv); + ka.doPhase(modifiedKey, true); + String shared = TestUtil.bytesToHex(ka.generateSecret()); + // The implementation did not notice that the public key was modified. + // This is not nice, but at the moment we only fail the test if the + // modification was essential for computing the shared secret. + // + // BouncyCastle v.1.53 fails this test, for ECDHC with modified order. + // This implementation reduces the product s*h modulo the order given + // in the public key. An attacker who can modify the order of the public key + // and who can learn whether such a modification changes the shared secret is + // able to learn the private key with a simple binary search. + assertEquals("algorithm:" + algorithm + " test:" + test.comment, expected, shared); + } catch (GeneralSecurityException ex) { + // OK, since the public keys have been modified. + System.out.println("testModifiedPublic:" + test.comment + " throws " + ex.toString()); + } + } + } + + /** + * This is a similar test as testModifiedPublic. However, this test uses test vectors + * ECPublicKeySpec + */ + @SuppressWarnings("InsecureCryptoUsage") + public void testModifiedPublicSpec(String algorithm) throws Exception { + KeyAgreement ka; + try { + ka = KeyAgreement.getInstance(algorithm); + } catch (NoSuchAlgorithmException ex) { + System.out.println("testWrongOrder: " + algorithm + " not supported"); + return; + } + KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC"); + keyGen.initialize(EcUtil.getNistP256Params()); + ECPrivateKey priv = (ECPrivateKey) keyGen.generateKeyPair().getPrivate(); + KeyFactory kf = KeyFactory.getInstance("EC"); + ECPublicKey validKey = (ECPublicKey) kf.generatePublic(EC_VALID_PUBLIC_KEY.getSpec()); + ka.init(priv); + ka.doPhase(validKey, true); + String expected = TestUtil.bytesToHex(ka.generateSecret()); + for (EcPublicKeyTestVector test : EC_MODIFIED_PUBLIC_KEYS) { + ECPublicKeySpec spec = test.getSpec(); + if (spec == null) { + // The constructor of EcPublicKeySpec performs some very minor validity checks. + // spec == null if one of these validity checks fails. Of course such a failure is OK. + continue; + } + try { + ECPublicKey modifiedKey = (ECPublicKey) kf.generatePublic(spec); + ka.init(priv); + ka.doPhase(modifiedKey, true); + String shared = TestUtil.bytesToHex(ka.generateSecret()); + // The implementation did not notice that the public key was modified. + // This is not nice, but at the moment we only fail the test if the + // modification was essential for computing the shared secret. + // + // BouncyCastle v.1.53 fails this test, for ECDHC with modified order. + // This implementation reduces the product s*h modulo the order given + // in the public key. An attacker who can modify the order of the public key + // and who can learn whether such a modification changes the shared secret is + // able to learn the private key with a simple binary search. + assertEquals("algorithm:" + algorithm + " test:" + test.comment, expected, shared); + } catch (GeneralSecurityException ex) { + // OK, since the public keys have been modified. + System.out.println("testModifiedPublic:" + test.comment + " throws " + ex.toString()); + } + } + } + + @Test + public void testModifiedPublic() throws Exception { + testModifiedPublic("ECDH"); + testModifiedPublic("ECDHC"); + } + + @Test + public void testModifiedPublicSpec() throws Exception { + testModifiedPublicSpec("ECDH"); + testModifiedPublicSpec("ECDHC"); + } + + @SuppressWarnings("InsecureCryptoUsage") + public void testDistinctCurves(String algorithm, ECPrivateKey priv, ECPublicKey pub) + throws Exception { + KeyAgreement kaA; + try { + kaA = KeyAgreement.getInstance(algorithm); + } catch (NoSuchAlgorithmException ex) { + System.out.println("Algorithm not supported: " + algorithm); + return; + } + byte[] shared; + try { + kaA.init(priv); + kaA.doPhase(pub, true); + shared = kaA.generateSecret(); + } catch (InvalidKeyException ex) { + // This is expected. + return; + } + // Printing some information to determine what might have gone wrong: + // E.g., if the generated secret is the same as the x-coordinate of the public key + // then it is likely that the ECDH computation was using a fake group with small order. + // Such a situation is probably exploitable. + // This probably is exploitable. If the curve of the private key was used for the ECDH + // then the generated secret and the x-coordinate of the public key are likely + // distinct. + EllipticCurve pubCurve = pub.getParams().getCurve(); + EllipticCurve privCurve = priv.getParams().getCurve(); + ECPoint pubW = pub.getW(); + System.out.println("testDistinctCurves: algorithm=" + algorithm); + System.out.println( + "Private key: a=" + + privCurve.getA() + + " b=" + + privCurve.getB() + + " p" + + EcUtil.getModulus(privCurve)); + System.out.println(" s =" + priv.getS()); + System.out.println( + "Public key: a=" + + pubCurve.getA() + + " b=" + + pubCurve.getB() + + " p" + + EcUtil.getModulus(pubCurve)); + System.out.println(" w = (" + pubW.getAffineX() + ", " + pubW.getAffineY() + ")"); + System.out.println( + " = (" + + pubW.getAffineX().toString(16) + + ", " + + pubW.getAffineY().toString(16) + + ")"); + System.out.println("generated shared secret:" + TestUtil.bytesToHex(shared)); + fail("Generated secret with distinct Curves using " + algorithm); + } + + /** + * This test modifies the order of group in the public key. A severe bug would be an + * implementation that leaks information whether the private key is larger than the order given in + * the public key. Also a severe bug would be to reduce the private key modulo the order given in + * the public key parameters. + */ + // TODO(bleichen): This can be merged with testModifiedPublic once this is fixed. + @SuppressWarnings("InsecureCryptoUsage") + public void testWrongOrder(String algorithm, ECParameterSpec spec) throws Exception { + KeyAgreement ka; + try { + ka = KeyAgreement.getInstance(algorithm); + } catch (NoSuchAlgorithmException ex) { + System.out.println("testWrongOrder: " + algorithm + " not supported"); + return; + } + KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC"); + ECPrivateKey priv; + ECPublicKey pub; + try { + keyGen.initialize(spec); + priv = (ECPrivateKey) keyGen.generateKeyPair().getPrivate(); + pub = (ECPublicKey) keyGen.generateKeyPair().getPublic(); + } catch (GeneralSecurityException ex) { + // This is OK, since not all provider support Brainpool curves + System.out.println("testWrongOrder: could not generate keys for curve"); + return; + } + // Get the shared secret for the unmodified keys. + ka.init(priv); + ka.doPhase(pub, true); + byte[] shared = ka.generateSecret(); + // Generate a modified public key. + ECParameterSpec modifiedParams = + new ECParameterSpec( + spec.getCurve(), spec.getGenerator(), spec.getOrder().shiftRight(16), 1); + ECPublicKeySpec modifiedPubSpec = new ECPublicKeySpec(pub.getW(), modifiedParams); + KeyFactory kf = KeyFactory.getInstance("EC"); + ECPublicKey modifiedPub; + try { + modifiedPub = (ECPublicKey) kf.generatePublic(modifiedPubSpec); + } catch (GeneralSecurityException ex) { + // The provider does not support non-standard curves or did a validity check. + // Both would be correct. + System.out.println("testWrongOrder: can't modify order."); + return; + } + byte[] shared2; + try { + ka.init(priv); + ka.doPhase(modifiedPub, true); + shared2 = ka.generateSecret(); + } catch (GeneralSecurityException ex) { + // This is the expected behavior + System.out.println("testWrongOrder:" + ex.toString()); + return; + } + // TODO(bleichen): Getting here is already a bug and we might flag this later. + // At the moment we are only interested in really bad behavior of a library, that potentially + // leaks the secret key. This is the case when the shared secrets are different, since this + // suggests that the implementation reduces the multiplier modulo the given order of the curve + // or some other behaviour that is dependent on the private key. + // An attacker who can check whether a DH computation was done correctly or incorrectly because + // of modular reduction, can determine the private key, either by a binary search or by trying + // to guess the private key modulo some small "order". + // BouncyCastle v.1.53 fails this test, and leaks the private key. + System.out.println( + "Generated shared secret with a modified order:" + + algorithm + + "\n" + + "expected:" + + TestUtil.bytesToHex(shared) + + " computed:" + + TestUtil.bytesToHex(shared2)); + assertEquals( + "Algorithm:" + algorithm, TestUtil.bytesToHex(shared), TestUtil.bytesToHex(shared2)); + } + + @Test + public void testWrongOrderEcdh() throws Exception { + testWrongOrder("ECDH", EcUtil.getNistP256Params()); + testWrongOrder("ECDH", EcUtil.getBrainpoolP256r1Params()); + } + + @Test + public void testWrongOrderEcdhc() throws Exception { + testWrongOrder("ECDHC", EcUtil.getNistP256Params()); + testWrongOrder("ECDHC", EcUtil.getBrainpoolP256r1Params()); + } + + /** + * Tests for the problem detected by CVE-2017-10176. + * + * <p>Some libraries do not compute P + (-P) correctly and return 2 * P or throw exceptions. When + * the library uses addition-subtraction chains for the point multiplication then such cases can + * occur for example when the private key is close to the order of the curve. + */ + private void testLargePrivateKey(ECParameterSpec spec) throws Exception { + BigInteger order = spec.getOrder(); + KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC"); + ECPublicKey pub; + try { + keyGen.initialize(spec); + pub = (ECPublicKey) keyGen.generateKeyPair().getPublic(); + } catch (GeneralSecurityException ex) { + // curve is not supported + return; + } + KeyFactory kf = KeyFactory.getInstance("EC"); + KeyAgreement ka = KeyAgreement.getInstance("ECDH"); + for (int i = 1; i <= 64; i++) { + BigInteger p1 = BigInteger.valueOf(i); + ECPrivateKeySpec spec1 = new ECPrivateKeySpec(p1, spec); + ECPrivateKeySpec spec2 = new ECPrivateKeySpec(order.subtract(p1), spec); + ka.init(kf.generatePrivate(spec1)); + ka.doPhase(pub, true); + byte[] shared1 = ka.generateSecret(); + ka.init(kf.generatePrivate(spec2)); + ka.doPhase(pub, true); + byte[] shared2 = ka.generateSecret(); + // The private keys p1 and p2 are equivalent, since only the x-coordinate of the + // shared point is used to generate the shared secret. + assertEquals(TestUtil.bytesToHex(shared1), TestUtil.bytesToHex(shared2)); + } + } + + @Test + public void testLargePrivateKey() throws Exception { + testLargePrivateKey(EcUtil.getNistP224Params()); + testLargePrivateKey(EcUtil.getNistP256Params()); + testLargePrivateKey(EcUtil.getNistP384Params()); + // This test failed before CVE-2017-10176 was fixed. + testLargePrivateKey(EcUtil.getNistP521Params()); + testLargePrivateKey(EcUtil.getBrainpoolP256r1Params()); + } + + /** + * This test tries to determine whether point multipliplication using two distinct + * points leads to distinguishable timings. + * + * The main goal here is to determine if the attack by Toru Akishita and Tsuyoshi Takagi + * in https://www-old.cdc.informatik.tu-darmstadt.de/reports/TR/TI-03-01.zvp.pdf + * might be applicable. I.e. one of the points contains a zero value when multiplied + * by mul, the other one does not. + * + * In its current form the test here is quite weak for a number of reasons: + * (1) The timing is often noisy, because the test is run as a unit test. + * (2) The test is executed with only a small number of input points. + * (3) The number of samples is rather low. Running this test with a larger sample + * size would detect more timing differences. Unfortunately + * (4) The test does not determine if a variable run time is exploitable. For example + * if the tested provider uses windowed exponentiation and the special point is + * in the precomputation table then timing differences are easy to spot, but more + * difficult to exploit and hence additional experiments would be necessary. + * + * @param spec the specification of the curve + * @param p0 This is a special point. I.e. multiplying this point by mul + * may lead to a zero value that may be observable. + * @param p1 a random point on the curve + * @param mul an integer, such that multiplying p0 with this value may lead to a timing + * difference + * @param privKeySize the size of the private key in bits + * @param comment describes the test case + */ + private void testTiming(ECParameterSpec spec, ECPoint p0, ECPoint p1, + BigInteger mul, int privKeySize, String comment) throws Exception { + ThreadMXBean bean = ManagementFactory.getThreadMXBean(); + if (!bean.isCurrentThreadCpuTimeSupported()) { + System.out.println("getCurrentThreadCpuTime is not supported. Skipping"); + return; + } + SecureRandom random = new SecureRandom(); + int fixedSize = mul.bitLength(); + int missingBits = privKeySize - 2 * fixedSize; + assertTrue(missingBits > 0); + // possible values for tests, minCount: + // 1024, 410 + // 2048, 880 + // 4096, 1845 + // 10000, 4682 + // I.e. these are values, such that doing 'tests' coin flips results in <= minCount heads or + // tails with a probability smaller than 2^-32. + // + // def min_count(n, b=33): + // res, sum, k = 1,1,0 + // bnd = 2**(n-b) + // while sum < bnd: + // res *= n - k + // res //= 1 + k + // k += 1 + // sum += res + // return k - 1 + final int tests = 2048; + final int minCount = 880; + // the number of measurements done with each point + final int repetitions = 8; + // the number of warmup experiments that are ignored + final int warmup = 8; + final int sampleSize = warmup + tests; + KeyFactory kf = KeyFactory.getInstance("EC"); + PublicKey[] publicKeys = new PublicKey[2]; + try { + publicKeys[0] = kf.generatePublic(new ECPublicKeySpec(p0, spec)); + publicKeys[1] = kf.generatePublic(new ECPublicKeySpec(p1, spec)); + } catch (InvalidKeySpecException ex) { + // unsupported curve + return; + } + PrivateKey[] privKeys = new PrivateKey[sampleSize]; + for (int i = 0; i < sampleSize; i++) { + BigInteger m = new BigInteger(missingBits, random); + m = mul.shiftLeft(missingBits).add(m); + m = m.shiftLeft(fixedSize).add(mul); + ECPrivateKeySpec privSpec = new ECPrivateKeySpec(m, spec); + privKeys[i] = kf.generatePrivate(privSpec); + } + KeyAgreement ka = KeyAgreement.getInstance("ECDH"); + long[][] timings = new long[2][sampleSize]; + for (int i = 0; i < sampleSize; i++) { + for (int j = 0; j < 2 * repetitions; j++) { + // idx determines which key to use. + int idx = (j ^ i) & 1; + ka.init(privKeys[i]); + long start = bean.getCurrentThreadCpuTime(); + ka.doPhase(publicKeys[idx], true); + byte[] unused = ka.generateSecret(); + long time = bean.getCurrentThreadCpuTime() - start; + timings[idx][i] += time; + } + } + for (int i = 0; i < sampleSize; i++) { + for (int j = 0; j < 2; j++) { + timings[j][i] /= repetitions; + } + } + + // Performs some statistics. + boolean noisy = false; // Set to true, if the timings have a large variance. + System.out.println("ECDH timing test:" + comment); + double[] avg = new double[2]; + double[] var = new double[2]; + for (int i = 0; i < 2; i++) { + double sum = 0.0; + double sumSqr = 0.0; + for (int j = warmup; j < sampleSize; j++) { + double val = (double) timings[i][j]; + sum += val; + sumSqr += val * val; + } + avg[i] = sum / tests; + var[i] = (sumSqr - avg[i] * sum) / (tests - 1); + double stdDev = Math.sqrt(var[i]); + double cv = stdDev / avg[i]; + System.out.println("Timing for point " + i + " avg: " + avg[i] + " std dev: " + stdDev + + " cv:" + cv); + // The ratio 0.05 below is a somewhat arbitrary value that tries to determine if the noise + // is too big to detect even larger timing differences. + if (cv > 0.05) { + noisy = true; + } + } + // Paired Z-test: + // The outcome of this value can be significantly influenced by extreme outliers, such + // as slow timings because of things like a garbage collection. + double sigmas = Math.abs(avg[0] - avg[1]) / Math.sqrt((var[0] + var[1]) / tests); + System.out.println("Sigmas: " + sigmas); + + // Pairwise comparison: + // this comparison has the property that it compares timings done with the same + // private key, hence timing differences from using different addition chain sizes + // are ignored. Extreme outliers should not influence the result a lot, as long as the + // number of outliers is small. + int point0Faster = 0; + int equal = 0; + for (int i = 0; i < sampleSize; i++) { + if (timings[0][i] < timings[1][i]) { + point0Faster += 1; + } else if (timings[0][i] < timings[1][i]) { + equal += 1; + } + } + point0Faster += equal / 2; + System.out.println("Point 0 multiplication is faster: " + point0Faster); + if (point0Faster < minCount || point0Faster > sampleSize - minCount) { + fail("Timing differences in ECDH computation detected"); + } else if (noisy) { + System.out.println("Timing was too noisy to expect results."); + } + } + + @SlowTest(providers = + {ProviderType.BOUNCY_CASTLE, ProviderType.SPONGY_CASTLE, ProviderType.OPENJDK}) + @Test + public void testTimingSecp256r1() throws Exception { + // edge case for projective coordinates + BigInteger x1 = + new BigInteger("81bfb55b010b1bdf08b8d9d8590087aa278e28febff3b05632eeff09011c5579", 16); + BigInteger y1 = + new BigInteger("732d0e65267ea28b7af8cfcb148936c2af8664cbb4f04e188148a1457400c2a7", 16); + ECPoint p1 = new ECPoint(x1, y1); + // random point + BigInteger x2 = + new BigInteger("8608e36a91f1fba12e4074972af446176b5608c9c58dc318bd0742754c3dcee7", 16); + BigInteger y2 = + new BigInteger("bc2c9ecd44af916ca58d9e3ef1257f698d350ef486eb86137fe69a7375bcc191", 16); + ECPoint p2 = new ECPoint(x2, y2); + testTiming(EcUtil.getNistP256Params(), p1, p2, new BigInteger("2"), 256, "secp256r1"); + } + + @SlowTest(providers = + {ProviderType.BOUNCY_CASTLE, ProviderType.SPONGY_CASTLE, ProviderType.OPENJDK}) + @Test + public void testTimingSecp384r1() throws Exception { + // edge case for projective coordinates + BigInteger x1 = + new BigInteger("7a6fadfee03eb09554f2a04fe08300aca88bb3a46e8f6347bace672cfe427698" + + "8541cef8dc10536a84580215f5f90a3b", 16); + BigInteger y1 = + new BigInteger("6d243d5d9de1cdddd04cbeabdc7a0f6c244391f7cb2d5738fe13c334add4b458" + + "5fef61ffd446db33b39402278713ae78", 16); + ECPoint p1 = new ECPoint(x1, y1); + // random point + BigInteger x2 = + new BigInteger("71f3c57d6a879889e582af2c7c5444b0eb6ba95d88365b21ca9549475273ecdd" + + "3930aa0bebbd1cf084e4049667278602", 16); + BigInteger y2 = + new BigInteger("9dcbc4d843af8944eb4ba018d369b351a9ea0f7b9e3561df2ee218d54e198f7c" + + "837a3abaa41dffd2d2cb771a7599ed9e", 16); + ECPoint p2 = new ECPoint(x2, y2); + testTiming(EcUtil.getNistP384Params(), p1, p2, new BigInteger("2"), 384, "secp384r1"); + } + + @SlowTest(providers = + {ProviderType.BOUNCY_CASTLE, ProviderType.SPONGY_CASTLE, ProviderType.OPENJDK}) + @Test + public void testTimingBrainpoolP256r1() throws Exception { + // edge case for Jacobian and projective coordinates + BigInteger x1 = + new BigInteger("79838c22d2b8dc9af2e6cf56f8826dc3dfe10fcb17b6aaaf551ee52bef12f826", 16); + BigInteger y1 = + new BigInteger("1e2ed3d453088c8552c6feecf898667bc1e15905002edec6b269feb7bea09d5b", 16); + ECPoint p1 = new ECPoint(x1, y1); + + // random point + BigInteger x2 = + new BigInteger("2720b2e821b2ac8209b573bca755a68821e1e09deb580666702570dd527dd4c1", 16); + BigInteger y2 = + new BigInteger("25cdd610243c7e693fad7bd69b43ae3e63e94317c4c6b717d9c8bc3be8c996fb", 16); + ECPoint p2 = new ECPoint(x2, y2); + testTiming(EcUtil.getBrainpoolP256r1Params(), p1, p2, new BigInteger("2"), 255, + "brainpoolP256r1"); + } +} + |