1 /*
2  * Copyright (c) 2009, 2021, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package sun.security.ec;
27 
28 import java.io.IOException;
29 import java.math.BigInteger;
30 import java.security.*;
31 import java.security.spec.AlgorithmParameterSpec;
32 import java.security.spec.ECGenParameterSpec;
33 import java.security.spec.ECParameterSpec;
34 import java.security.spec.ECPoint;
35 import java.security.spec.InvalidParameterSpecException;
36 import java.util.Arrays;
37 import java.util.Optional;
38 
39 import sun.security.jca.JCAUtil;
40 import sun.security.util.ECUtil;
41 import sun.security.util.math.*;
42 import sun.security.ec.point.*;
43 import static sun.security.util.SecurityProviderConstants.DEF_EC_KEY_SIZE;
44 import static sun.security.ec.ECOperations.IntermediateValueException;
45 
46 /**
47  * EC keypair generator.
48  * Standard algorithm, minimum key length is 112 bits, maximum is 571 bits.
49  *
50  * @since 1.7
51  */
52 public final class ECKeyPairGenerator extends KeyPairGeneratorSpi {
53 
54     private static final int KEY_SIZE_MIN = 112; // min bits (see ecc_impl.h)
55     private static final int KEY_SIZE_MAX = 571; // max bits (see ecc_impl.h)
56 
57     // used to seed the keypair generator
58     private SecureRandom random;
59 
60     // size of the key to generate, KEY_SIZE_MIN <= keySize <= KEY_SIZE_MAX
61     private int keySize;
62 
63     // parameters specified via init, if any
64     private AlgorithmParameterSpec params = null;
65 
66     /**
67      * Constructs a new ECKeyPairGenerator.
68      */
ECKeyPairGenerator()69     public ECKeyPairGenerator() {
70         // initialize to default in case the app does not call initialize()
71         initialize(DEF_EC_KEY_SIZE, null);
72     }
73 
74     // initialize the generator. See JCA doc
75     @Override
initialize(int keySize, SecureRandom random)76     public void initialize(int keySize, SecureRandom random) {
77 
78         checkKeySize(keySize);
79         this.params = ECUtil.getECParameterSpec(null, keySize);
80         if (params == null) {
81             throw new InvalidParameterException(
82                 "No EC parameters available for key size " + keySize + " bits");
83         }
84         this.random = random;
85     }
86 
87     // second initialize method. See JCA doc
88     @Override
initialize(AlgorithmParameterSpec params, SecureRandom random)89     public void initialize(AlgorithmParameterSpec params, SecureRandom random)
90             throws InvalidAlgorithmParameterException {
91 
92         ECParameterSpec ecSpec = null;
93 
94         if (params instanceof ECParameterSpec) {
95             ECParameterSpec ecParams = (ECParameterSpec) params;
96             ecSpec = ECUtil.getECParameterSpec(null, ecParams);
97             if (ecSpec == null) {
98                 throw new InvalidAlgorithmParameterException(
99                     "Curve not supported: " + params);
100             }
101         } else if (params instanceof ECGenParameterSpec) {
102             String name = ((ECGenParameterSpec) params).getName();
103             ecSpec = ECUtil.getECParameterSpec(null, name);
104             if (ecSpec == null) {
105                 throw new InvalidAlgorithmParameterException(
106                     "Unknown curve name: " + name);
107             }
108         } else {
109             throw new InvalidAlgorithmParameterException(
110                 "ECParameterSpec or ECGenParameterSpec required for EC");
111         }
112 
113         // Not all known curves are supported by the native implementation
114         ensureCurveIsSupported(ecSpec);
115         this.params = ecSpec;
116 
117         this.keySize = ecSpec.getCurve().getField().getFieldSize();
118         this.random = random;
119     }
120 
ensureCurveIsSupported(ECParameterSpec ecSpec)121     private static void ensureCurveIsSupported(ECParameterSpec ecSpec)
122         throws InvalidAlgorithmParameterException {
123 
124         // Check if ecSpec is a valid curve
125         AlgorithmParameters ecParams = ECUtil.getECParameters(null);
126         try {
127             ecParams.init(ecSpec);
128         } catch (InvalidParameterSpecException ex) {
129             throw new InvalidAlgorithmParameterException(
130                 "Curve not supported: " + ecSpec.toString());
131         }
132 
133         // Check if the java implementation supports this curve
134         if (ECOperations.forParameters(ecSpec).isEmpty()) {
135             throw new InvalidAlgorithmParameterException(
136                 "Curve not supported: " + ecSpec.toString());
137         }
138     }
139 
140     // generate the keypair. See JCA doc
141     @Override
generateKeyPair()142     public KeyPair generateKeyPair() {
143 
144         if (random == null) {
145             random = JCAUtil.getSecureRandom();
146         }
147 
148         try {
149             Optional<KeyPair> kp = generateKeyPairImpl(random);
150             if (kp.isPresent()) {
151                 return kp.get();
152             }
153         } catch (Exception ex) {
154             throw new ProviderException(ex);
155         }
156         throw new ProviderException("Curve not supported:  " +
157             params.toString());
158     }
159 
generatePrivateScalar(SecureRandom random, ECOperations ecOps, int seedSize)160     private byte[] generatePrivateScalar(SecureRandom random,
161         ECOperations ecOps, int seedSize) {
162         // Attempt to create the private scalar in a loop that uses new random
163         // input each time. The chance of failure is very small assuming the
164         // implementation derives the nonce using extra bits
165         int numAttempts = 128;
166         byte[] seedArr = new byte[seedSize];
167         for (int i = 0; i < numAttempts; i++) {
168             random.nextBytes(seedArr);
169             try {
170                 return ecOps.seedToScalar(seedArr);
171             } catch (IntermediateValueException ex) {
172                 // try again in the next iteration
173             }
174         }
175 
176         throw new ProviderException("Unable to produce private key after "
177                                          + numAttempts + " attempts");
178     }
179 
generateKeyPairImpl(SecureRandom random)180     private Optional<KeyPair> generateKeyPairImpl(SecureRandom random)
181         throws InvalidKeyException {
182 
183         ECParameterSpec ecParams = (ECParameterSpec) params;
184 
185         Optional<ECOperations> opsOpt = ECOperations.forParameters(ecParams);
186         if (opsOpt.isEmpty()) {
187             return Optional.empty();
188         }
189         ECOperations ops = opsOpt.get();
190         IntegerFieldModuloP field = ops.getField();
191         int numBits = ecParams.getOrder().bitLength();
192         int seedBits = numBits + 64;
193         int seedSize = (seedBits + 7) / 8;
194         byte[] privArr = generatePrivateScalar(random, ops, seedSize);
195 
196         ECPoint genPoint = ecParams.getGenerator();
197         ImmutableIntegerModuloP x = field.getElement(genPoint.getAffineX());
198         ImmutableIntegerModuloP y = field.getElement(genPoint.getAffineY());
199         AffinePoint affGen = new AffinePoint(x, y);
200         Point pub = ops.multiply(affGen, privArr);
201         AffinePoint affPub = pub.asAffine();
202 
203         PrivateKey privateKey = new ECPrivateKeyImpl(privArr, ecParams);
204         Arrays.fill(privArr, (byte)0);
205 
206         ECPoint w = new ECPoint(affPub.getX().asBigInteger(),
207             affPub.getY().asBigInteger());
208         PublicKey publicKey = new ECPublicKeyImpl(w, ecParams);
209 
210         return Optional.of(new KeyPair(publicKey, privateKey));
211     }
212 
checkKeySize(int keySize)213     private void checkKeySize(int keySize) throws InvalidParameterException {
214         if (keySize < KEY_SIZE_MIN) {
215             throw new InvalidParameterException
216                 ("Key size must be at least " + KEY_SIZE_MIN + " bits");
217         }
218         if (keySize > KEY_SIZE_MAX) {
219             throw new InvalidParameterException
220                 ("Key size must be at most " + KEY_SIZE_MAX + " bits");
221         }
222         this.keySize = keySize;
223     }
224 }
225