aboutsummaryrefslogtreecommitdiff
path: root/java/com/google/security/wycheproof/testcases/EcKeyTest.java
blob: a18a8ffe20465cd130906dbea2c5b88bcda908a2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
/**
 * @license
 * Copyright 2016 Google Inc. All rights reserved.
 * 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.
 */

// TODO(bleichen): RFC 3279 allows ECKeys with a number of different parameters.
//   E.g. public keys can specify the order, base points etc.
//   We might want to check how well these parameters are verified when parsing
//   a public key.

package com.google.security.wycheproof;

import com.google.security.wycheproof.WycheproofRunner.ExcludedTest;
import com.google.security.wycheproof.WycheproofRunner.ProviderType;
import java.math.BigInteger;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint;
import java.security.spec.ECPublicKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import junit.framework.TestCase;

/** EC tests */
public class EcKeyTest extends TestCase {
  /**
   * Encodings of public keys with invalid parameters. There are multiple places where a provider
   * can validate a public key: some parameters are typically validated by the KeyFactory, more
   * validation can be done by the cryptographic primitive. Unused parameters are sometimes not
   * validated at all.
   *
   * <p>This following test vectors are public key encodings with invalid parameters where we expect
   * that KeyFactory.generatePublic recognizes the problem. The documentation simply claims that an
   * InvalidKeySpecException is thrown if the given key specification is inappropriate but does not
   * specify what an appropriate key exactly is. Nonetheless we expect that the following minimal
   * validations are performed: order is a positive integer, cofactor is a small positive integer.
   * Some modifications may not be detected and must be caught by the primitives using them. E.g.,
   * it is expensive to verify the order of the group generated by the generator and hence the key
   * factory may not verify the correctness of this parameter. Thus an implementation of ECDH must
   * not trust an order claimed in the public key.
   *
   * <p>TODO(bleichen): The encoding is defined in https://tools.ietf.org/html/rfc3279 Section
   * 2.3.5. This document defines a few additional requirements and options which are not yet
   * checked: - OID for id-public-key_type must be ansi-X9.62 2 - OID for id-ecPublicKey must be
   * id-publicKeyType 1 - The intended application for the key may be indicated in the key usage
   * field (RFC 3280). - EcpkParameters can be implicitlyCA (not sure how we would specify the curve
   * in this case) - the version is always 1 - the points on the curves can be either compressed or
   * uncompressed (so far all points are uncompressed) - the seed value is optional (so far no test
   * vector specifies the seed) - the cofactor is optional but must be included for ECDH keys. (so
   * far all test vectors have a cofactor)
   *
   * <p>RFC 3279 also specifies curves over binary fields. Because of attacks against such curves,
   * i.e. "New algorithm for the discrete logarithm problem on elliptic curves" by I.Semaev
   * https://eprint.iacr.org/2015/310 such curves should no longer be used and hence testing them
   * has low priority.
   */
  public static final String[] EC_INVALID_PUBLIC_KEYS = {
    // order = -115792089210356248762697446949407573529996955224135760342422259061068512044369
    "308201333081ec06072a8648ce3d02013081e0020101302c06072a8648ce3d01"
        + "01022100ffffffff00000001000000000000000000000000ffffffffffffffff"
        + "ffffffff30440420ffffffff00000001000000000000000000000000ffffffff"
        + "fffffffffffffffc04205ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53"
        + "b0f63bce3c3e27d2604b0441046b17d1f2e12c4247f8bce6e563a440f277037d"
        + "812deb33a0f4a13945d898c2964fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33"
        + "576b315ececbb6406837bf51f50221ff00000000ffffffff0000000000000000"
        + "4319055258e8617b0c46353d039cdaaf02010103420004cdeb39edd03e2b1a11"
        + "a5e134ec99d5f25f21673d403f3ecb47bd1fa676638958ea58493b8429598c0b"
        + "49bbb85c3303ddb1553c3b761c2caacca71606ba9ebac8",
    // order = 0
    "308201123081cb06072a8648ce3d02013081bf020101302c06072a8648ce3d01"
        + "01022100ffffffff00000001000000000000000000000000ffffffffffffffff"
        + "ffffffff30440420ffffffff00000001000000000000000000000000ffffffff"
        + "fffffffffffffffc04205ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53"
        + "b0f63bce3c3e27d2604b0441046b17d1f2e12c4247f8bce6e563a440f277037d"
        + "812deb33a0f4a13945d898c2964fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33"
        + "576b315ececbb6406837bf51f5020002010103420004cdeb39edd03e2b1a11a5"
        + "e134ec99d5f25f21673d403f3ecb47bd1fa676638958ea58493b8429598c0b49"
        + "bbb85c3303ddb1553c3b761c2caacca71606ba9ebac8",
    // cofactor = -1
    "308201333081ec06072a8648ce3d02013081e0020101302c06072a8648ce3d01"
        + "01022100ffffffff00000001000000000000000000000000ffffffffffffffff"
        + "ffffffff30440420ffffffff00000001000000000000000000000000ffffffff"
        + "fffffffffffffffc04205ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53"
        + "b0f63bce3c3e27d2604b0441046b17d1f2e12c4247f8bce6e563a440f277037d"
        + "812deb33a0f4a13945d898c2964fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33"
        + "576b315ececbb6406837bf51f5022100ffffffff00000000ffffffffffffffff"
        + "bce6faada7179e84f3b9cac2fc6325510201ff03420004cdeb39edd03e2b1a11"
        + "a5e134ec99d5f25f21673d403f3ecb47bd1fa676638958ea58493b8429598c0b"
        + "49bbb85c3303ddb1553c3b761c2caacca71606ba9ebac8",
    // cofactor = 0
    "308201323081eb06072a8648ce3d02013081df020101302c06072a8648ce3d01"
        + "01022100ffffffff00000001000000000000000000000000ffffffffffffffff"
        + "ffffffff30440420ffffffff00000001000000000000000000000000ffffffff"
        + "fffffffffffffffc04205ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53"
        + "b0f63bce3c3e27d2604b0441046b17d1f2e12c4247f8bce6e563a440f277037d"
        + "812deb33a0f4a13945d898c2964fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33"
        + "576b315ececbb6406837bf51f5022100ffffffff00000000ffffffffffffffff"
        + "bce6faada7179e84f3b9cac2fc632551020003420004cdeb39edd03e2b1a11a5"
        + "e134ec99d5f25f21673d403f3ecb47bd1fa676638958ea58493b8429598c0b49"
        + "bbb85c3303ddb1553c3b761c2caacca71606ba9ebac8",
    // cofactor = 115792089210356248762697446949407573529996955224135760342422259061068512044369
    "308201553082010d06072a8648ce3d020130820100020101302c06072a8648ce"
        + "3d0101022100ffffffff00000001000000000000000000000000ffffffffffff"
        + "ffffffffffff30440420ffffffff00000001000000000000000000000000ffff"
        + "fffffffffffffffffffc04205ac635d8aa3a93e7b3ebbd55769886bc651d06b0"
        + "cc53b0f63bce3c3e27d2604b0441046b17d1f2e12c4247f8bce6e563a440f277"
        + "037d812deb33a0f4a13945d898c2964fe342e2fe1a7f9b8ee7eb4a7c0f9e162b"
        + "ce33576b315ececbb6406837bf51f5022100ffffffff00000000ffffffffffff"
        + "ffffbce6faada7179e84f3b9cac2fc632551022100ffffffff00000000ffffff"
        + "ffffffffffbce6faada7179e84f3b9cac2fc63255103420004cdeb39edd03e2b"
        + "1a11a5e134ec99d5f25f21673d403f3ecb47bd1fa676638958ea58493b842959"
        + "8c0b49bbb85c3303ddb1553c3b761c2caacca71606ba9ebac8",
  };

  @ExcludedTest(
      providers = {ProviderType.BOUNCY_CASTLE},
      comment = "KeyFactory.EC is removed")
  public void testEncodedPublicKey() throws Exception {
    KeyFactory kf = KeyFactory.getInstance("EC");
    for (String encodedHex : EC_INVALID_PUBLIC_KEYS) {
      byte[] encoded = TestUtil.hexToBytes(encodedHex);
      X509EncodedKeySpec x509keySpec = new X509EncodedKeySpec(encoded);
      try {
        ECPublicKey unused = (ECPublicKey) kf.generatePublic(x509keySpec);
        fail("Constructed invalid public key from:" + encodedHex);
      } catch (InvalidKeySpecException ex) {
        // OK, since the public keys have been modified.
        System.out.println(ex.toString());
      }
    }
  }

  @ExcludedTest(
      providers = {ProviderType.BOUNCY_CASTLE},
      comment = "KeyPairGenerator.EC is removed")
  public void testEncodedPrivateKey() throws Exception {
    KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");
    keyGen.initialize(EcUtil.getNistP256Params());
    KeyPair keyPair = keyGen.generateKeyPair();
    ECPrivateKey priv = (ECPrivateKey) keyPair.getPrivate();
    byte[] encoded = priv.getEncoded();
    System.out.println("Encoded ECPrivateKey:" + TestUtil.bytesToHex(encoded));
    PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(encoded);
    KeyFactory kf = KeyFactory.getInstance("EC");
    ECPrivateKey decoded = (ECPrivateKey) kf.generatePrivate(spec);
    assertEquals(priv.getS(), decoded.getS());
    assertEquals(priv.getParams().getCofactor(), decoded.getParams().getCofactor());
    assertEquals(priv.getParams().getCurve(), decoded.getParams().getCurve());
    assertEquals(priv.getParams().getGenerator(), decoded.getParams().getGenerator());
    assertEquals(priv.getParams().getOrder(), decoded.getParams().getOrder());
  }

  /**
   * Tests key generation for given parameters. The test can be skipped if the curve is not a
   * standard curve.
   */
  void testKeyGeneration(ECParameterSpec ecParams, boolean isStandard) throws Exception {
    KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");
    KeyPair keyPair;
    try {
      keyGen.initialize(ecParams);
      keyPair = keyGen.generateKeyPair();
    } catch (InvalidAlgorithmParameterException ex) {
      if (!isStandard) {
        return;
      }
      throw ex;
    }
    ECPublicKey pub = (ECPublicKey) keyPair.getPublic();
    ECPrivateKey priv = (ECPrivateKey) keyPair.getPrivate();
    EcUtil.checkPublicKey(pub);
    BigInteger s = priv.getS();
    // Check the length of s. Could fail with probability 2^{-32}.
    assertTrue(s.bitLength() >= EcUtil.fieldSizeInBits(ecParams.getCurve()) - 32);
    // TODO(bleichen): correct curve?
    // TODO(bleichen): use RandomUtil
  }

  @ExcludedTest(
      providers = {ProviderType.BOUNCY_CASTLE},
      comment = "KeyPairGenerator.EC is removed")
  public void testKeyGenerationAll() throws Exception {
    testKeyGeneration(EcUtil.getNistP224Params(), true);
    testKeyGeneration(EcUtil.getNistP256Params(), true);
    testKeyGeneration(EcUtil.getNistP384Params(), true);
    testKeyGeneration(EcUtil.getNistP521Params(), true);
    // Curves that are sometimes not supported.
    testKeyGeneration(EcUtil.getBrainpoolP256r1Params(), false);
  }

  /**
   * Checks that the default key size for ECDSA is up to date.
   * The test uses NIST SP 800-57 part1 revision 4, Table 2, page 53
   * http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-57pt1r4.pdf
   * for the minimal key size of EC keys.
   * Nist recommends a minimal security strength of 112 bits for the time until 2030.
   * To achieve this security strength EC keys of at least 224 bits are required.
   */
  @ExcludedTest(
      providers = {ProviderType.BOUNCY_CASTLE},
      comment = "KeyPairGenerator.EC is removed")
  public void testDefaultKeyGeneration() throws Exception {
    KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");
    KeyPair keyPair = keyGen.generateKeyPair();
    ECPublicKey pub = (ECPublicKey) keyPair.getPublic();
    int keySize = EcUtil.fieldSizeInBits(pub.getParams().getCurve());
    if (keySize < 224) {
      fail("Expected a default key size of at least 224 bits. Size of generate key is " + keySize); 
    }
  }

  /**
   * Tries to generate a public key with a point at infinity. Public keys with a point at infinity
   * should be rejected to prevent subgroup confinement attacks.
   */
  public void testPublicKeyAtInfinity() throws Exception {
    ECParameterSpec ecSpec = EcUtil.getNistP256Params();
    try {
      ECPublicKeySpec pubSpec = new ECPublicKeySpec(ECPoint.POINT_INFINITY, ecSpec);
      fail(
          "Point at infinity is not a valid public key. "
              + pubSpec.getW().equals(ECPoint.POINT_INFINITY));
    } catch (java.lang.IllegalArgumentException ex) {
      // This is expected
    }
  }
}