1package org.bouncycastle.jcajce.provider.asymmetric.util;
2
3import java.math.BigInteger;
4import java.security.InvalidKeyException;
5import java.security.PrivateKey;
6import java.security.PublicKey;
7import java.util.Enumeration;
8import java.util.Map;
9
10import org.bouncycastle.asn1.ASN1ObjectIdentifier;
11// BEGIN android-removed
12// import org.bouncycastle.asn1.anssi.ANSSINamedCurves;
13// import org.bouncycastle.asn1.cryptopro.ECGOST3410NamedCurves;
14// END android-removed
15import org.bouncycastle.asn1.nist.NISTNamedCurves;
16import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
17import org.bouncycastle.asn1.sec.SECNamedCurves;
18// BEGIN android-removed
19// import org.bouncycastle.asn1.teletrust.TeleTrusTNamedCurves;
20// END android-removed
21import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
22import org.bouncycastle.asn1.x9.ECNamedCurveTable;
23import org.bouncycastle.asn1.x9.X962NamedCurves;
24import org.bouncycastle.asn1.x9.X962Parameters;
25import org.bouncycastle.asn1.x9.X9ECParameters;
26import org.bouncycastle.crypto.ec.CustomNamedCurves;
27import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
28import org.bouncycastle.crypto.params.ECDomainParameters;
29import org.bouncycastle.crypto.params.ECNamedDomainParameters;
30import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
31import org.bouncycastle.crypto.params.ECPublicKeyParameters;
32import org.bouncycastle.jcajce.provider.config.ProviderConfiguration;
33import org.bouncycastle.jce.interfaces.ECPrivateKey;
34import org.bouncycastle.jce.interfaces.ECPublicKey;
35import org.bouncycastle.jce.provider.BouncyCastleProvider;
36import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
37import org.bouncycastle.jce.spec.ECParameterSpec;
38
39/**
40 * utility class for converting jce/jca ECDSA, ECDH, and ECDHC
41 * objects into their org.bouncycastle.crypto counterparts.
42 */
43public class ECUtil
44{
45    /**
46     * Returns a sorted array of middle terms of the reduction polynomial.
47     * @param k The unsorted array of middle terms of the reduction polynomial
48     * of length 1 or 3.
49     * @return the sorted array of middle terms of the reduction polynomial.
50     * This array always has length 3.
51     */
52    static int[] convertMidTerms(
53        int[] k)
54    {
55        int[] res = new int[3];
56
57        if (k.length == 1)
58        {
59            res[0] = k[0];
60        }
61        else
62        {
63            if (k.length != 3)
64            {
65                throw new IllegalArgumentException("Only Trinomials and pentanomials supported");
66            }
67
68            if (k[0] < k[1] && k[0] < k[2])
69            {
70                res[0] = k[0];
71                if (k[1] < k[2])
72                {
73                    res[1] = k[1];
74                    res[2] = k[2];
75                }
76                else
77                {
78                    res[1] = k[2];
79                    res[2] = k[1];
80                }
81            }
82            else if (k[1] < k[2])
83            {
84                res[0] = k[1];
85                if (k[0] < k[2])
86                {
87                    res[1] = k[0];
88                    res[2] = k[2];
89                }
90                else
91                {
92                    res[1] = k[2];
93                    res[2] = k[0];
94                }
95            }
96            else
97            {
98                res[0] = k[2];
99                if (k[0] < k[1])
100                {
101                    res[1] = k[0];
102                    res[2] = k[1];
103                }
104                else
105                {
106                    res[1] = k[1];
107                    res[2] = k[0];
108                }
109            }
110        }
111
112        return res;
113    }
114
115    public static ECDomainParameters getDomainParameters(
116        ProviderConfiguration configuration,
117        org.bouncycastle.jce.spec.ECParameterSpec params)
118    {
119        ECDomainParameters domainParameters;
120
121        if (params instanceof ECNamedCurveParameterSpec)
122        {
123            ECNamedCurveParameterSpec nParams = (ECNamedCurveParameterSpec)params;
124            ASN1ObjectIdentifier nameOid = ECUtil.getNamedCurveOid(nParams.getName());
125
126            domainParameters = new ECNamedDomainParameters(nameOid, nParams.getCurve(), nParams.getG(), nParams.getN(), nParams.getH(), nParams.getSeed());
127        }
128        else if (params == null)
129        {
130            org.bouncycastle.jce.spec.ECParameterSpec iSpec = configuration.getEcImplicitlyCa();
131
132            domainParameters = new ECDomainParameters(iSpec.getCurve(), iSpec.getG(), iSpec.getN(), iSpec.getH(), iSpec.getSeed());
133        }
134        else
135        {
136            domainParameters = new ECDomainParameters(params.getCurve(), params.getG(), params.getN(), params.getH(), params.getSeed());
137        }
138
139        return domainParameters;
140    }
141
142    public static ECDomainParameters getDomainParameters(
143        ProviderConfiguration configuration,
144        X962Parameters params)
145    {
146        ECDomainParameters domainParameters;
147
148        if (params.isNamedCurve())
149        {
150            ASN1ObjectIdentifier oid = ASN1ObjectIdentifier.getInstance(params.getParameters());
151            X9ECParameters ecP = ECUtil.getNamedCurveByOid(oid);
152            if (ecP == null)
153            {
154                Map extraCurves = configuration.getAdditionalECParameters();
155
156                ecP = (X9ECParameters)extraCurves.get(oid);
157            }
158            domainParameters = new ECNamedDomainParameters(oid, ecP.getCurve(), ecP.getG(), ecP.getN(), ecP.getH(), ecP.getSeed());
159        }
160        else if (params.isImplicitlyCA())
161        {
162            org.bouncycastle.jce.spec.ECParameterSpec iSpec = configuration.getEcImplicitlyCa();
163
164            domainParameters = new ECDomainParameters(iSpec.getCurve(), iSpec.getG(), iSpec.getN(), iSpec.getH(), iSpec.getSeed());
165        }
166        else
167        {
168            X9ECParameters ecP = X9ECParameters.getInstance(params.getParameters());
169
170            domainParameters = new ECDomainParameters(ecP.getCurve(), ecP.getG(), ecP.getN(), ecP.getH(), ecP.getSeed());
171        }
172
173        return domainParameters;
174    }
175
176    public static AsymmetricKeyParameter generatePublicKeyParameter(
177        PublicKey    key)
178        throws InvalidKeyException
179    {
180        if (key instanceof ECPublicKey)
181        {
182            ECPublicKey    k = (ECPublicKey)key;
183            ECParameterSpec s = k.getParameters();
184
185            return new ECPublicKeyParameters(
186                            k.getQ(),
187                            new ECDomainParameters(s.getCurve(), s.getG(), s.getN(), s.getH(), s.getSeed()));
188        }
189        else if (key instanceof java.security.interfaces.ECPublicKey)
190        {
191            java.security.interfaces.ECPublicKey pubKey = (java.security.interfaces.ECPublicKey)key;
192            ECParameterSpec s = EC5Util.convertSpec(pubKey.getParams(), false);
193            return new ECPublicKeyParameters(
194                EC5Util.convertPoint(pubKey.getParams(), pubKey.getW(), false),
195                            new ECDomainParameters(s.getCurve(), s.getG(), s.getN(), s.getH(), s.getSeed()));
196        }
197        else
198        {
199            // see if we can build a key from key.getEncoded()
200            try
201            {
202                byte[] bytes = key.getEncoded();
203
204                if (bytes == null)
205                {
206                    throw new InvalidKeyException("no encoding for EC public key");
207                }
208
209                PublicKey publicKey = BouncyCastleProvider.getPublicKey(SubjectPublicKeyInfo.getInstance(bytes));
210
211                if (publicKey instanceof java.security.interfaces.ECPublicKey)
212                {
213                    return ECUtil.generatePublicKeyParameter(publicKey);
214                }
215            }
216            catch (Exception e)
217            {
218                throw new InvalidKeyException("cannot identify EC public key: " + e.toString());
219            }
220        }
221
222        throw new InvalidKeyException("cannot identify EC public key.");
223    }
224
225    public static AsymmetricKeyParameter generatePrivateKeyParameter(
226        PrivateKey    key)
227        throws InvalidKeyException
228    {
229        if (key instanceof ECPrivateKey)
230        {
231            ECPrivateKey  k = (ECPrivateKey)key;
232            ECParameterSpec s = k.getParameters();
233
234            if (s == null)
235            {
236                s = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa();
237            }
238
239            return new ECPrivateKeyParameters(
240                            k.getD(),
241                            new ECDomainParameters(s.getCurve(), s.getG(), s.getN(), s.getH(), s.getSeed()));
242        }
243        else if (key instanceof java.security.interfaces.ECPrivateKey)
244        {
245            java.security.interfaces.ECPrivateKey privKey = (java.security.interfaces.ECPrivateKey)key;
246            ECParameterSpec s = EC5Util.convertSpec(privKey.getParams(), false);
247            return new ECPrivateKeyParameters(
248                            privKey.getS(),
249                            new ECDomainParameters(s.getCurve(), s.getG(), s.getN(), s.getH(), s.getSeed()));
250        }
251        else
252        {
253            // see if we can build a key from key.getEncoded()
254            try
255            {
256                byte[] bytes = key.getEncoded();
257
258                if (bytes == null)
259                {
260                    throw new InvalidKeyException("no encoding for EC private key");
261                }
262
263                PrivateKey privateKey = BouncyCastleProvider.getPrivateKey(PrivateKeyInfo.getInstance(bytes));
264
265                if (privateKey instanceof java.security.interfaces.ECPrivateKey)
266                {
267                    return ECUtil.generatePrivateKeyParameter(privateKey);
268                }
269            }
270            catch (Exception e)
271            {
272                throw new InvalidKeyException("cannot identify EC private key: " + e.toString());
273            }
274        }
275
276        throw new InvalidKeyException("can't identify EC private key.");
277    }
278
279    public static int getOrderBitLength(ProviderConfiguration configuration, BigInteger order, BigInteger privateValue)
280    {
281        if (order == null)     // implicitly CA
282        {
283            ECParameterSpec implicitCA = configuration.getEcImplicitlyCa();
284
285            if (implicitCA == null)
286            {
287                return privateValue.bitLength();   // a guess but better than an exception!
288            }
289
290            return implicitCA.getN().bitLength();
291        }
292        else
293        {
294            return order.bitLength();
295        }
296    }
297
298    public static ASN1ObjectIdentifier getNamedCurveOid(
299        String curveName)
300    {
301        String name;
302
303        if (curveName.indexOf(' ') > 0)
304        {
305            name = curveName.substring(curveName.indexOf(' ') + 1);
306        }
307        else
308        {
309            name = curveName;
310        }
311
312        try
313        {
314            if (name.charAt(0) >= '0' && name.charAt(0) <= '2')
315            {
316                return new ASN1ObjectIdentifier(name);
317            }
318            else
319            {
320                return lookupOidByName(name);
321            }
322        }
323        catch (IllegalArgumentException ex)
324        {
325            return lookupOidByName(name);
326        }
327    }
328
329    private static ASN1ObjectIdentifier lookupOidByName(String name)
330    {
331        ASN1ObjectIdentifier oid = X962NamedCurves.getOID(name);
332
333        if (oid == null)
334        {
335            oid = SECNamedCurves.getOID(name);
336            if (oid == null)
337            {
338                oid = NISTNamedCurves.getOID(name);
339            }
340            // BEGIN android-removed
341            // if (oid == null)
342            // {
343            //     oid = TeleTrusTNamedCurves.getOID(name);
344            // }
345            // if (oid == null)
346            // {
347            //     oid = ECGOST3410NamedCurves.getOID(name);
348            // }
349            // if (oid == null)
350            // {
351            //     oid = ANSSINamedCurves.getOID(name);
352            // }
353            // END android-removed
354        }
355
356        return oid;
357    }
358
359    public static ASN1ObjectIdentifier getNamedCurveOid(
360        ECParameterSpec ecParameterSpec)
361    {
362        for (Enumeration names = ECNamedCurveTable.getNames(); names.hasMoreElements();)
363        {
364            String name = (String)names.nextElement();
365
366            X9ECParameters params = ECNamedCurveTable.getByName(name);
367
368            if (params.getN().equals(ecParameterSpec.getN())
369                && params.getH().equals(ecParameterSpec.getH())
370                && params.getCurve().equals(ecParameterSpec.getCurve())
371                && params.getG().equals(ecParameterSpec.getG()))
372            {
373                return org.bouncycastle.asn1.x9.ECNamedCurveTable.getOID(name);
374            }
375        }
376
377        return null;
378    }
379
380    public static X9ECParameters getNamedCurveByOid(
381        ASN1ObjectIdentifier oid)
382    {
383        X9ECParameters params = CustomNamedCurves.getByOID(oid);
384
385        if (params == null)
386        {
387            params = X962NamedCurves.getByOID(oid);
388            if (params == null)
389            {
390                params = SECNamedCurves.getByOID(oid);
391            }
392            if (params == null)
393            {
394                params = NISTNamedCurves.getByOID(oid);
395            }
396            // BEGIN android-removed
397            // if (params == null)
398            // {
399            //     params = TeleTrusTNamedCurves.getByOID(oid);
400            // }
401            // END android-removed
402        }
403
404        return params;
405    }
406
407    public static X9ECParameters getNamedCurveByName(
408        String curveName)
409    {
410        X9ECParameters params = CustomNamedCurves.getByName(curveName);
411
412        if (params == null)
413        {
414            params = X962NamedCurves.getByName(curveName);
415            if (params == null)
416            {
417                params = SECNamedCurves.getByName(curveName);
418            }
419            if (params == null)
420            {
421                params = NISTNamedCurves.getByName(curveName);
422            }
423            // BEGIN android-removed
424            // if (params == null)
425            // {
426            //     params = TeleTrusTNamedCurves.getByName(curveName);
427            // }
428            // END android-removed
429        }
430
431        return params;
432    }
433
434    public static String getCurveName(
435        ASN1ObjectIdentifier oid)
436    {
437        String name = X962NamedCurves.getName(oid);
438
439        if (name == null)
440        {
441            name = SECNamedCurves.getName(oid);
442            if (name == null)
443            {
444                name = NISTNamedCurves.getName(oid);
445            }
446            // BEGIN android-removed
447            // if (name == null)
448            // {
449            //     name = TeleTrusTNamedCurves.getName(oid);
450            // }
451            // if (name == null)
452            // {
453            //     name = ECGOST3410NamedCurves.getName(oid);
454            // }
455            // END android-removed
456        }
457
458        return name;
459    }
460}
461