1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
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
17package org.conscrypt;
18
19import java.math.BigInteger;
20import java.security.InvalidAlgorithmParameterException;
21import java.security.InvalidParameterException;
22import java.security.spec.ECField;
23import java.security.spec.ECFieldF2m;
24import java.security.spec.ECFieldFp;
25import java.security.spec.ECParameterSpec;
26import java.security.spec.ECPoint;
27import java.security.spec.EllipticCurve;
28
29public final class OpenSSLECGroupContext {
30    private final long groupCtx;
31
32    public OpenSSLECGroupContext(long groupCtx) {
33        this.groupCtx = groupCtx;
34    }
35
36    public static OpenSSLECGroupContext getCurveByName(String curveName) {
37        // Workaround for OpenSSL not supporting SECG names for NIST P-192 and P-256
38        // (aka ANSI X9.62 prime192v1 and prime256v1) curve names.
39        if ("secp256r1".equals(curveName)) {
40            curveName = "prime256v1";
41        } else if ("secp192r1".equals(curveName)) {
42            curveName = "prime192v1";
43        }
44
45        final long ctx = NativeCrypto.EC_GROUP_new_by_curve_name(curveName);
46        if (ctx == 0) {
47            return null;
48        }
49
50        NativeCrypto.EC_GROUP_set_point_conversion_form(ctx,
51                NativeCrypto.POINT_CONVERSION_UNCOMPRESSED);
52        NativeCrypto.EC_GROUP_set_asn1_flag(ctx, NativeCrypto.OPENSSL_EC_NAMED_CURVE);
53
54        return new OpenSSLECGroupContext(ctx);
55    }
56
57    public static OpenSSLECGroupContext getInstance(int type, BigInteger p, BigInteger a,
58            BigInteger b, BigInteger x, BigInteger y, BigInteger n, BigInteger h) {
59        final long ctx = NativeCrypto.EC_GROUP_new_curve(type, p.toByteArray(), a.toByteArray(),
60                b.toByteArray());
61        if (ctx == 0) {
62            return null;
63        }
64
65        NativeCrypto.EC_GROUP_set_point_conversion_form(ctx,
66                NativeCrypto.POINT_CONVERSION_UNCOMPRESSED);
67
68        OpenSSLECGroupContext group = new OpenSSLECGroupContext(ctx);
69
70        OpenSSLECPointContext generator = new OpenSSLECPointContext(group,
71                NativeCrypto.EC_POINT_new(ctx));
72
73        NativeCrypto.EC_POINT_set_affine_coordinates(ctx, generator.getContext(),
74                x.toByteArray(), y.toByteArray());
75
76        NativeCrypto.EC_GROUP_set_generator(ctx, generator.getContext(), n.toByteArray(),
77                h.toByteArray());
78
79        return group;
80    }
81
82    @Override
83    protected void finalize() throws Throwable {
84        try {
85            if (groupCtx != 0) {
86                NativeCrypto.EC_GROUP_clear_free(groupCtx);
87            }
88        } finally {
89            super.finalize();
90        }
91    }
92
93    @Override
94    public boolean equals(Object o) {
95        if (!(o instanceof OpenSSLECGroupContext)) {
96            return false;
97        }
98
99        final OpenSSLECGroupContext other = (OpenSSLECGroupContext) o;
100        return NativeCrypto.EC_GROUP_cmp(groupCtx, other.groupCtx);
101    }
102
103    @Override
104    public int hashCode() {
105        // TODO Auto-generated method stub
106        return super.hashCode();
107    }
108
109    public long getContext() {
110        return groupCtx;
111    }
112
113    public static OpenSSLECGroupContext getInstance(ECParameterSpec params)
114            throws InvalidAlgorithmParameterException {
115        final String curveName = Platform.getCurveName(params);
116        if (curveName != null) {
117            return OpenSSLECGroupContext.getCurveByName(curveName);
118        }
119
120        final EllipticCurve curve = params.getCurve();
121        final ECField field = curve.getField();
122
123        final int type;
124        final BigInteger p;
125        if (field instanceof ECFieldFp) {
126            type = NativeCrypto.EC_CURVE_GFP;
127            p = ((ECFieldFp) field).getP();
128        } else if (field instanceof ECFieldF2m) {
129            type = NativeCrypto.EC_CURVE_GF2M;
130            p = ((ECFieldF2m) field).getReductionPolynomial();
131        } else {
132            throw new InvalidParameterException("unhandled field class "
133                    + field.getClass().getName());
134        }
135
136        final ECPoint generator = params.getGenerator();
137        return OpenSSLECGroupContext.getInstance(type, p, curve.getA(), curve.getB(),
138                generator.getAffineX(), generator.getAffineY(), params.getOrder(),
139                BigInteger.valueOf(params.getCofactor()));
140    }
141
142    public ECParameterSpec getECParameterSpec() {
143        final String curveName = NativeCrypto.EC_GROUP_get_curve_name(groupCtx);
144
145        final byte[][] curveParams = NativeCrypto.EC_GROUP_get_curve(groupCtx);
146        final BigInteger p = new BigInteger(curveParams[0]);
147        final BigInteger a = new BigInteger(curveParams[1]);
148        final BigInteger b = new BigInteger(curveParams[2]);
149
150        final ECField field;
151        final int type = NativeCrypto.get_EC_GROUP_type(groupCtx);
152        if (type == NativeCrypto.EC_CURVE_GFP) {
153            field = new ECFieldFp(p);
154        } else if (type == NativeCrypto.EC_CURVE_GF2M) {
155            field = new ECFieldF2m(p.bitLength() - 1, p);
156        } else {
157            throw new RuntimeException("unknown curve type " + type);
158        }
159
160        final EllipticCurve curve = new EllipticCurve(field, a, b);
161
162        final OpenSSLECPointContext generatorCtx = new OpenSSLECPointContext(this,
163                NativeCrypto.EC_GROUP_get_generator(groupCtx));
164        final ECPoint generator = generatorCtx.getECPoint();
165
166        final BigInteger order = new BigInteger(NativeCrypto.EC_GROUP_get_order(groupCtx));
167        final BigInteger cofactor = new BigInteger(NativeCrypto.EC_GROUP_get_cofactor(groupCtx));
168
169        ECParameterSpec spec = new ECParameterSpec(curve, generator, order, cofactor.intValue());
170        Platform.setCurveName(spec, curveName);
171        return spec;
172    }
173}
174