1 /** 2 * @license 3 * Copyright 2016 Google Inc. All rights reserved. 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 // TODO(bleichen): RFC 3279 allows ECKeys with a number of different parameters. 18 // E.g. public keys can specify the order, base points etc. 19 // We might want to check how well these parameters are verified when parsing 20 // a public key. 21 22 package com.google.security.wycheproof; 23 24 import com.google.security.wycheproof.WycheproofRunner.ExcludedTest; 25 import com.google.security.wycheproof.WycheproofRunner.ProviderType; 26 import java.math.BigInteger; 27 import java.security.InvalidAlgorithmParameterException; 28 import java.security.KeyFactory; 29 import java.security.KeyPair; 30 import java.security.KeyPairGenerator; 31 import java.security.interfaces.ECPrivateKey; 32 import java.security.interfaces.ECPublicKey; 33 import java.security.spec.ECParameterSpec; 34 import java.security.spec.ECPoint; 35 import java.security.spec.ECPublicKeySpec; 36 import java.security.spec.InvalidKeySpecException; 37 import java.security.spec.PKCS8EncodedKeySpec; 38 import java.security.spec.X509EncodedKeySpec; 39 import junit.framework.TestCase; 40 41 /** EC tests */ 42 public class EcKeyTest extends TestCase { 43 /** 44 * Encodings of public keys with invalid parameters. There are multiple places where a provider 45 * can validate a public key: some parameters are typically validated by the KeyFactory, more 46 * validation can be done by the cryptographic primitive. Unused parameters are sometimes not 47 * validated at all. 48 * 49 * <p>This following test vectors are public key encodings with invalid parameters where we expect 50 * that KeyFactory.generatePublic recognizes the problem. The documentation simply claims that an 51 * InvalidKeySpecException is thrown if the given key specification is inappropriate but does not 52 * specify what an appropriate key exactly is. Nonetheless we expect that the following minimal 53 * validations are performed: order is a positive integer, cofactor is a small positive integer. 54 * Some modifications may not be detected and must be caught by the primitives using them. E.g., 55 * it is expensive to verify the order of the group generated by the generator and hence the key 56 * factory may not verify the correctness of this parameter. Thus an implementation of ECDH must 57 * not trust an order claimed in the public key. 58 * 59 * <p>TODO(bleichen): The encoding is defined in https://tools.ietf.org/html/rfc3279 Section 60 * 2.3.5. This document defines a few additional requirements and options which are not yet 61 * checked: - OID for id-public-key_type must be ansi-X9.62 2 - OID for id-ecPublicKey must be 62 * id-publicKeyType 1 - The intended application for the key may be indicated in the key usage 63 * field (RFC 3280). - EcpkParameters can be implicitlyCA (not sure how we would specify the curve 64 * in this case) - the version is always 1 - the points on the curves can be either compressed or 65 * uncompressed (so far all points are uncompressed) - the seed value is optional (so far no test 66 * vector specifies the seed) - the cofactor is optional but must be included for ECDH keys. (so 67 * far all test vectors have a cofactor) 68 * 69 * <p>RFC 3279 also specifies curves over binary fields. Because of attacks against such curves, 70 * i.e. "New algorithm for the discrete logarithm problem on elliptic curves" by I.Semaev 71 * https://eprint.iacr.org/2015/310 such curves should no longer be used and hence testing them 72 * has low priority. 73 */ 74 public static final String[] EC_INVALID_PUBLIC_KEYS = { 75 // order = -115792089210356248762697446949407573529996955224135760342422259061068512044369 76 "308201333081ec06072a8648ce3d02013081e0020101302c06072a8648ce3d01" 77 + "01022100ffffffff00000001000000000000000000000000ffffffffffffffff" 78 + "ffffffff30440420ffffffff00000001000000000000000000000000ffffffff" 79 + "fffffffffffffffc04205ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53" 80 + "b0f63bce3c3e27d2604b0441046b17d1f2e12c4247f8bce6e563a440f277037d" 81 + "812deb33a0f4a13945d898c2964fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33" 82 + "576b315ececbb6406837bf51f50221ff00000000ffffffff0000000000000000" 83 + "4319055258e8617b0c46353d039cdaaf02010103420004cdeb39edd03e2b1a11" 84 + "a5e134ec99d5f25f21673d403f3ecb47bd1fa676638958ea58493b8429598c0b" 85 + "49bbb85c3303ddb1553c3b761c2caacca71606ba9ebac8", 86 // order = 0 87 "308201123081cb06072a8648ce3d02013081bf020101302c06072a8648ce3d01" 88 + "01022100ffffffff00000001000000000000000000000000ffffffffffffffff" 89 + "ffffffff30440420ffffffff00000001000000000000000000000000ffffffff" 90 + "fffffffffffffffc04205ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53" 91 + "b0f63bce3c3e27d2604b0441046b17d1f2e12c4247f8bce6e563a440f277037d" 92 + "812deb33a0f4a13945d898c2964fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33" 93 + "576b315ececbb6406837bf51f5020002010103420004cdeb39edd03e2b1a11a5" 94 + "e134ec99d5f25f21673d403f3ecb47bd1fa676638958ea58493b8429598c0b49" 95 + "bbb85c3303ddb1553c3b761c2caacca71606ba9ebac8", 96 // cofactor = -1 97 "308201333081ec06072a8648ce3d02013081e0020101302c06072a8648ce3d01" 98 + "01022100ffffffff00000001000000000000000000000000ffffffffffffffff" 99 + "ffffffff30440420ffffffff00000001000000000000000000000000ffffffff" 100 + "fffffffffffffffc04205ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53" 101 + "b0f63bce3c3e27d2604b0441046b17d1f2e12c4247f8bce6e563a440f277037d" 102 + "812deb33a0f4a13945d898c2964fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33" 103 + "576b315ececbb6406837bf51f5022100ffffffff00000000ffffffffffffffff" 104 + "bce6faada7179e84f3b9cac2fc6325510201ff03420004cdeb39edd03e2b1a11" 105 + "a5e134ec99d5f25f21673d403f3ecb47bd1fa676638958ea58493b8429598c0b" 106 + "49bbb85c3303ddb1553c3b761c2caacca71606ba9ebac8", 107 // cofactor = 0 108 "308201323081eb06072a8648ce3d02013081df020101302c06072a8648ce3d01" 109 + "01022100ffffffff00000001000000000000000000000000ffffffffffffffff" 110 + "ffffffff30440420ffffffff00000001000000000000000000000000ffffffff" 111 + "fffffffffffffffc04205ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53" 112 + "b0f63bce3c3e27d2604b0441046b17d1f2e12c4247f8bce6e563a440f277037d" 113 + "812deb33a0f4a13945d898c2964fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33" 114 + "576b315ececbb6406837bf51f5022100ffffffff00000000ffffffffffffffff" 115 + "bce6faada7179e84f3b9cac2fc632551020003420004cdeb39edd03e2b1a11a5" 116 + "e134ec99d5f25f21673d403f3ecb47bd1fa676638958ea58493b8429598c0b49" 117 + "bbb85c3303ddb1553c3b761c2caacca71606ba9ebac8", 118 // cofactor = 115792089210356248762697446949407573529996955224135760342422259061068512044369 119 "308201553082010d06072a8648ce3d020130820100020101302c06072a8648ce" 120 + "3d0101022100ffffffff00000001000000000000000000000000ffffffffffff" 121 + "ffffffffffff30440420ffffffff00000001000000000000000000000000ffff" 122 + "fffffffffffffffffffc04205ac635d8aa3a93e7b3ebbd55769886bc651d06b0" 123 + "cc53b0f63bce3c3e27d2604b0441046b17d1f2e12c4247f8bce6e563a440f277" 124 + "037d812deb33a0f4a13945d898c2964fe342e2fe1a7f9b8ee7eb4a7c0f9e162b" 125 + "ce33576b315ececbb6406837bf51f5022100ffffffff00000000ffffffffffff" 126 + "ffffbce6faada7179e84f3b9cac2fc632551022100ffffffff00000000ffffff" 127 + "ffffffffffbce6faada7179e84f3b9cac2fc63255103420004cdeb39edd03e2b" 128 + "1a11a5e134ec99d5f25f21673d403f3ecb47bd1fa676638958ea58493b842959" 129 + "8c0b49bbb85c3303ddb1553c3b761c2caacca71606ba9ebac8", 130 }; 131 132 @ExcludedTest( 133 providers = {ProviderType.BOUNCY_CASTLE}, 134 comment = "KeyFactory.EC is removed") testEncodedPublicKey()135 public void testEncodedPublicKey() throws Exception { 136 KeyFactory kf = KeyFactory.getInstance("EC"); 137 for (String encodedHex : EC_INVALID_PUBLIC_KEYS) { 138 byte[] encoded = TestUtil.hexToBytes(encodedHex); 139 X509EncodedKeySpec x509keySpec = new X509EncodedKeySpec(encoded); 140 try { 141 ECPublicKey unused = (ECPublicKey) kf.generatePublic(x509keySpec); 142 fail("Constructed invalid public key from:" + encodedHex); 143 } catch (InvalidKeySpecException ex) { 144 // OK, since the public keys have been modified. 145 System.out.println(ex.toString()); 146 } 147 } 148 } 149 150 @ExcludedTest( 151 providers = {ProviderType.BOUNCY_CASTLE}, 152 comment = "KeyPairGenerator.EC is removed") testEncodedPrivateKey()153 public void testEncodedPrivateKey() throws Exception { 154 KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC"); 155 keyGen.initialize(EcUtil.getNistP256Params()); 156 KeyPair keyPair = keyGen.generateKeyPair(); 157 ECPrivateKey priv = (ECPrivateKey) keyPair.getPrivate(); 158 byte[] encoded = priv.getEncoded(); 159 System.out.println("Encoded ECPrivateKey:" + TestUtil.bytesToHex(encoded)); 160 PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(encoded); 161 KeyFactory kf = KeyFactory.getInstance("EC"); 162 ECPrivateKey decoded = (ECPrivateKey) kf.generatePrivate(spec); 163 assertEquals(priv.getS(), decoded.getS()); 164 assertEquals(priv.getParams().getCofactor(), decoded.getParams().getCofactor()); 165 assertEquals(priv.getParams().getCurve(), decoded.getParams().getCurve()); 166 assertEquals(priv.getParams().getGenerator(), decoded.getParams().getGenerator()); 167 assertEquals(priv.getParams().getOrder(), decoded.getParams().getOrder()); 168 } 169 170 /** 171 * Tests key generation for given parameters. The test can be skipped if the curve is not a 172 * standard curve. 173 */ testKeyGeneration(ECParameterSpec ecParams, boolean isStandard)174 void testKeyGeneration(ECParameterSpec ecParams, boolean isStandard) throws Exception { 175 KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC"); 176 KeyPair keyPair; 177 try { 178 keyGen.initialize(ecParams); 179 keyPair = keyGen.generateKeyPair(); 180 } catch (InvalidAlgorithmParameterException ex) { 181 if (!isStandard) { 182 return; 183 } 184 throw ex; 185 } 186 ECPublicKey pub = (ECPublicKey) keyPair.getPublic(); 187 ECPrivateKey priv = (ECPrivateKey) keyPair.getPrivate(); 188 EcUtil.checkPublicKey(pub); 189 BigInteger s = priv.getS(); 190 // Check the length of s. Could fail with probability 2^{-32}. 191 assertTrue(s.bitLength() >= EcUtil.fieldSizeInBits(ecParams.getCurve()) - 32); 192 // TODO(bleichen): correct curve? 193 // TODO(bleichen): use RandomUtil 194 } 195 196 @ExcludedTest( 197 providers = {ProviderType.BOUNCY_CASTLE}, 198 comment = "KeyPairGenerator.EC is removed") testKeyGenerationAll()199 public void testKeyGenerationAll() throws Exception { 200 testKeyGeneration(EcUtil.getNistP224Params(), true); 201 testKeyGeneration(EcUtil.getNistP256Params(), true); 202 testKeyGeneration(EcUtil.getNistP384Params(), true); 203 testKeyGeneration(EcUtil.getNistP521Params(), true); 204 // Curves that are sometimes not supported. 205 testKeyGeneration(EcUtil.getBrainpoolP256r1Params(), false); 206 } 207 208 /** 209 * Checks that the default key size for ECDSA is up to date. 210 * The test uses NIST SP 800-57 part1 revision 4, Table 2, page 53 211 * http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-57pt1r4.pdf 212 * for the minimal key size of EC keys. 213 * Nist recommends a minimal security strength of 112 bits for the time until 2030. 214 * To achieve this security strength EC keys of at least 224 bits are required. 215 */ 216 @ExcludedTest( 217 providers = {ProviderType.BOUNCY_CASTLE}, 218 comment = "KeyPairGenerator.EC is removed") testDefaultKeyGeneration()219 public void testDefaultKeyGeneration() throws Exception { 220 KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC"); 221 KeyPair keyPair = keyGen.generateKeyPair(); 222 ECPublicKey pub = (ECPublicKey) keyPair.getPublic(); 223 int keySize = EcUtil.fieldSizeInBits(pub.getParams().getCurve()); 224 if (keySize < 224) { 225 fail("Expected a default key size of at least 224 bits. Size of generate key is " + keySize); 226 } 227 } 228 229 /** 230 * Tries to generate a public key with a point at infinity. Public keys with a point at infinity 231 * should be rejected to prevent subgroup confinement attacks. 232 */ testPublicKeyAtInfinity()233 public void testPublicKeyAtInfinity() throws Exception { 234 ECParameterSpec ecSpec = EcUtil.getNistP256Params(); 235 try { 236 ECPublicKeySpec pubSpec = new ECPublicKeySpec(ECPoint.POINT_INFINITY, ecSpec); 237 fail( 238 "Point at infinity is not a valid public key. " 239 + pubSpec.getW().equals(ECPoint.POINT_INFINITY)); 240 } catch (java.lang.IllegalArgumentException ex) { 241 // This is expected 242 } 243 } 244 } 245