1package org.bouncycastle.jcajce.provider.asymmetric.ec;
2
3import java.math.BigInteger;
4import java.security.InvalidAlgorithmParameterException;
5import java.security.InvalidParameterException;
6import java.security.KeyPair;
7import java.security.SecureRandom;
8import java.security.spec.AlgorithmParameterSpec;
9import java.security.spec.ECGenParameterSpec;
10import java.util.Hashtable;
11import java.util.Map;
12
13import org.bouncycastle.asn1.ASN1ObjectIdentifier;
14import org.bouncycastle.asn1.x9.ECNamedCurveTable;
15import org.bouncycastle.asn1.x9.X9ECParameters;
16import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
17import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
18import org.bouncycastle.crypto.params.ECDomainParameters;
19import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
20import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
21import org.bouncycastle.crypto.params.ECPublicKeyParameters;
22import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
23import org.bouncycastle.jcajce.provider.config.ProviderConfiguration;
24import org.bouncycastle.jce.provider.BouncyCastleProvider;
25import org.bouncycastle.jce.spec.ECNamedCurveGenParameterSpec;
26import org.bouncycastle.jce.spec.ECNamedCurveSpec;
27import org.bouncycastle.jce.spec.ECParameterSpec;
28import org.bouncycastle.math.ec.ECCurve;
29import org.bouncycastle.math.ec.ECPoint;
30import org.bouncycastle.util.Integers;
31
32public abstract class KeyPairGeneratorSpi
33    extends java.security.KeyPairGenerator
34{
35    public KeyPairGeneratorSpi(String algorithmName)
36    {
37        super(algorithmName);
38    }
39
40    public static class EC
41        extends KeyPairGeneratorSpi
42    {
43        ECKeyGenerationParameters   param;
44        ECKeyPairGenerator          engine = new ECKeyPairGenerator();
45        Object                      ecParams = null;
46        // BEGIN android-changed
47        int                         strength = 256;
48        // BEGIN android-changed
49        int                         certainty = 50;
50        SecureRandom                random = new SecureRandom();
51        boolean                     initialised = false;
52        String                      algorithm;
53        ProviderConfiguration       configuration;
54
55        static private Hashtable    ecParameters;
56
57        static {
58            ecParameters = new Hashtable();
59
60            ecParameters.put(Integers.valueOf(192), new ECGenParameterSpec("prime192v1")); // a.k.a P-192
61            ecParameters.put(Integers.valueOf(239), new ECGenParameterSpec("prime239v1"));
62            ecParameters.put(Integers.valueOf(256), new ECGenParameterSpec("prime256v1")); // a.k.a P-256
63
64            ecParameters.put(Integers.valueOf(224), new ECGenParameterSpec("P-224"));
65            ecParameters.put(Integers.valueOf(384), new ECGenParameterSpec("P-384"));
66            ecParameters.put(Integers.valueOf(521), new ECGenParameterSpec("P-521"));
67        }
68
69        public EC()
70        {
71            super("EC");
72            this.algorithm = "EC";
73            this.configuration = BouncyCastleProvider.CONFIGURATION;
74        }
75
76        public EC(
77            String  algorithm,
78            ProviderConfiguration configuration)
79        {
80            super(algorithm);
81            this.algorithm = algorithm;
82            this.configuration = configuration;
83        }
84
85        public void initialize(
86            int             strength,
87            SecureRandom    random)
88        {
89            this.strength = strength;
90            // BEGIN android-added
91            if (random != null) {
92            // END android-added
93            this.random = random;
94            // BEGIN android-added
95            }
96            // END android-added
97
98            ECGenParameterSpec ecParams = (ECGenParameterSpec)ecParameters.get(Integers.valueOf(strength));
99            if (ecParams == null)
100            {
101                throw new InvalidParameterException("unknown key size.");
102            }
103
104            try
105            {
106                initialize(ecParams, random);
107            }
108            catch (InvalidAlgorithmParameterException e)
109            {
110                throw new InvalidParameterException("key size not configurable.");
111            }
112        }
113
114        public void initialize(
115            AlgorithmParameterSpec  params,
116            SecureRandom            random)
117            throws InvalidAlgorithmParameterException
118        {
119            // BEGIN android-added
120            if (random == null) {
121                random = this.random;
122            }
123            // END android-added
124            if (params == null)
125            {
126                ECParameterSpec implicitCA = configuration.getEcImplicitlyCa();
127                if (implicitCA == null)
128                {
129                    throw new InvalidAlgorithmParameterException("null parameter passed but no implicitCA set");
130                }
131
132                this.ecParams = null;
133                this.param = createKeyGenParamsBC(implicitCA, random);
134            }
135            else if (params instanceof ECParameterSpec)
136            {
137                this.ecParams = params;
138                this.param = createKeyGenParamsBC((ECParameterSpec)params, random);
139            }
140            else if (params instanceof java.security.spec.ECParameterSpec)
141            {
142                this.ecParams = params;
143                this.param = createKeyGenParamsJCE((java.security.spec.ECParameterSpec)params, random);
144            }
145            else if (params instanceof ECGenParameterSpec)
146            {
147                initializeNamedCurve(((ECGenParameterSpec)params).getName(), random);
148            }
149            else if (params instanceof ECNamedCurveGenParameterSpec)
150            {
151                initializeNamedCurve(((ECNamedCurveGenParameterSpec)params).getName(), random);
152            }
153            else
154            {
155                throw new InvalidAlgorithmParameterException("parameter object not a ECParameterSpec");
156            }
157
158            engine.init(param);
159            initialised = true;
160        }
161
162        public KeyPair generateKeyPair()
163        {
164            if (!initialised)
165            {
166                initialize(strength, new SecureRandom());
167            }
168
169            AsymmetricCipherKeyPair     pair = engine.generateKeyPair();
170            ECPublicKeyParameters       pub = (ECPublicKeyParameters)pair.getPublic();
171            ECPrivateKeyParameters      priv = (ECPrivateKeyParameters)pair.getPrivate();
172
173            if (ecParams instanceof ECParameterSpec)
174            {
175                ECParameterSpec p = (ECParameterSpec)ecParams;
176
177                BCECPublicKey pubKey = new BCECPublicKey(algorithm, pub, p, configuration);
178                return new KeyPair(pubKey,
179                                   new BCECPrivateKey(algorithm, priv, pubKey, p, configuration));
180            }
181            else if (ecParams == null)
182            {
183               return new KeyPair(new BCECPublicKey(algorithm, pub, configuration),
184                                   new BCECPrivateKey(algorithm, priv, configuration));
185            }
186            else
187            {
188                java.security.spec.ECParameterSpec p = (java.security.spec.ECParameterSpec)ecParams;
189
190                BCECPublicKey pubKey = new BCECPublicKey(algorithm, pub, p, configuration);
191
192                return new KeyPair(pubKey, new BCECPrivateKey(algorithm, priv, pubKey, p, configuration));
193            }
194        }
195
196        protected ECKeyGenerationParameters createKeyGenParamsBC(ECParameterSpec p, SecureRandom r)
197        {
198            return new ECKeyGenerationParameters(new ECDomainParameters(p.getCurve(), p.getG(), p.getN(), p.getH()), r);
199        }
200
201        protected ECKeyGenerationParameters createKeyGenParamsJCE(java.security.spec.ECParameterSpec p, SecureRandom r)
202        {
203            ECCurve curve = EC5Util.convertCurve(p.getCurve());
204            ECPoint g = EC5Util.convertPoint(curve, p.getGenerator(), false);
205            BigInteger n = p.getOrder();
206            BigInteger h = BigInteger.valueOf(p.getCofactor());
207            ECDomainParameters dp = new ECDomainParameters(curve, g, n, h);
208            return new ECKeyGenerationParameters(dp, r);
209        }
210
211        protected ECNamedCurveSpec createNamedCurveSpec(String curveName)
212            throws InvalidAlgorithmParameterException
213        {
214            // NOTE: Don't bother with custom curves here as the curve will be converted to JCE type shortly
215
216            X9ECParameters p = ECUtils.getDomainParametersFromName(curveName);
217            if (p == null)
218            {
219                try
220                {
221                    // Check whether it's actually an OID string (SunJSSE ServerHandshaker setupEphemeralECDHKeys bug)
222                    p = ECNamedCurveTable.getByOID(new ASN1ObjectIdentifier(curveName));
223                    if (p == null)
224                    {
225                        Map extraCurves = configuration.getAdditionalECParameters();
226
227                        p = (X9ECParameters)extraCurves.get(new ASN1ObjectIdentifier(curveName));
228
229                        if (p == null)
230                        {
231                            throw new InvalidAlgorithmParameterException("unknown curve OID: " + curveName);
232                        }
233                    }
234                }
235                catch (IllegalArgumentException ex)
236                {
237                    throw new InvalidAlgorithmParameterException("unknown curve name: " + curveName);
238                }
239            }
240
241            // Work-around for JDK bug -- it won't look up named curves properly if seed is present
242            byte[] seed = null; //p.getSeed();
243
244            return new ECNamedCurveSpec(curveName, p.getCurve(), p.getG(), p.getN(), p.getH(), seed);
245        }
246
247        protected void initializeNamedCurve(String curveName, SecureRandom random)
248            throws InvalidAlgorithmParameterException
249        {
250            ECNamedCurveSpec namedCurve = createNamedCurveSpec(curveName);
251            this.ecParams = namedCurve;
252            this.param = createKeyGenParamsJCE(namedCurve, random);
253        }
254    }
255
256    public static class ECDSA
257        extends EC
258    {
259        public ECDSA()
260        {
261            super("ECDSA", BouncyCastleProvider.CONFIGURATION);
262        }
263    }
264
265    public static class ECDH
266        extends EC
267    {
268        public ECDH()
269        {
270            super("ECDH", BouncyCastleProvider.CONFIGURATION);
271        }
272    }
273
274    public static class ECDHC
275        extends EC
276    {
277        public ECDHC()
278        {
279            super("ECDHC", BouncyCastleProvider.CONFIGURATION);
280        }
281    }
282
283    public static class ECMQV
284        extends EC
285    {
286        public ECMQV()
287        {
288            super("ECMQV", BouncyCastleProvider.CONFIGURATION);
289        }
290    }
291}
292