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.io.IOException;
20import java.io.ObjectInputStream;
21import java.io.ObjectOutputStream;
22import java.security.InvalidKeyException;
23import java.security.interfaces.ECPublicKey;
24import java.security.spec.ECParameterSpec;
25import java.security.spec.ECPoint;
26import java.security.spec.ECPublicKeySpec;
27import java.security.spec.InvalidKeySpecException;
28import java.util.Arrays;
29
30/**
31 * An implementation of a {@link java.security.PublicKey} for EC keys based on BoringSSL.
32 *
33 * @hide
34 */
35@Internal
36public final class OpenSSLECPublicKey implements ECPublicKey, OpenSSLKeyHolder {
37    private static final long serialVersionUID = 3215842926808298020L;
38
39    private static final String ALGORITHM = "EC";
40
41    protected transient OpenSSLKey key;
42
43    protected transient OpenSSLECGroupContext group;
44
45    public OpenSSLECPublicKey(OpenSSLECGroupContext group, OpenSSLKey key) {
46        this.group = group;
47        this.key = key;
48    }
49
50    public OpenSSLECPublicKey(OpenSSLKey key) {
51        this.group = new OpenSSLECGroupContext(new NativeRef.EC_GROUP(
52                NativeCrypto.EC_KEY_get1_group(key.getNativeRef())));
53        this.key = key;
54    }
55
56    public OpenSSLECPublicKey(ECPublicKeySpec ecKeySpec) throws InvalidKeySpecException {
57        try {
58            group = OpenSSLECGroupContext.getInstance(ecKeySpec.getParams());
59            OpenSSLECPointContext pubKey = OpenSSLECPointContext.getInstance(group,
60                    ecKeySpec.getW());
61            key = new OpenSSLKey(NativeCrypto.EVP_PKEY_new_EC_KEY(group.getNativeRef(),
62                    pubKey.getNativeRef(), null));
63        } catch (Exception e) {
64            throw new InvalidKeySpecException(e);
65        }
66    }
67
68    public static OpenSSLKey getInstance(ECPublicKey ecPublicKey) throws InvalidKeyException {
69        try {
70            OpenSSLECGroupContext group = OpenSSLECGroupContext
71                    .getInstance(ecPublicKey.getParams());
72            OpenSSLECPointContext pubKey = OpenSSLECPointContext.getInstance(group,
73                    ecPublicKey.getW());
74            return new OpenSSLKey(NativeCrypto.EVP_PKEY_new_EC_KEY(group.getNativeRef(),
75                    pubKey.getNativeRef(), null));
76        } catch (Exception e) {
77            throw new InvalidKeyException(e);
78        }
79    }
80
81    @Override
82    public String getAlgorithm() {
83        return ALGORITHM;
84    }
85
86    @Override
87    public String getFormat() {
88        return "X.509";
89    }
90
91    @Override
92    public byte[] getEncoded() {
93        return NativeCrypto.i2d_PUBKEY(key.getNativeRef());
94    }
95
96    @Override
97    public ECParameterSpec getParams() {
98        return group.getECParameterSpec();
99    }
100
101    private ECPoint getPublicKey() {
102        final OpenSSLECPointContext pubKey = new OpenSSLECPointContext(group,
103                new NativeRef.EC_POINT(NativeCrypto.EC_KEY_get_public_key(key.getNativeRef())));
104
105        return pubKey.getECPoint();
106    }
107
108    @Override
109    public ECPoint getW() {
110        return getPublicKey();
111    }
112
113    @Override
114    public OpenSSLKey getOpenSSLKey() {
115        return key;
116    }
117
118    @Override
119    public boolean equals(Object o) {
120        if (o == this) {
121            return true;
122        }
123
124        if (o instanceof OpenSSLECPublicKey) {
125            OpenSSLECPublicKey other = (OpenSSLECPublicKey) o;
126            return key.equals(other.key);
127        }
128
129        if (!(o instanceof ECPublicKey)) {
130            return false;
131        }
132
133        final ECPublicKey other = (ECPublicKey) o;
134        if (!getPublicKey().equals(other.getW())) {
135            return false;
136        }
137
138        final ECParameterSpec spec = getParams();
139        final ECParameterSpec otherSpec = other.getParams();
140
141        return spec.getCurve().equals(otherSpec.getCurve())
142                && spec.getGenerator().equals(otherSpec.getGenerator())
143                && spec.getOrder().equals(otherSpec.getOrder())
144                && spec.getCofactor() == otherSpec.getCofactor();
145    }
146
147    @Override
148    public int hashCode() {
149        return Arrays.hashCode(NativeCrypto.i2d_PUBKEY(key.getNativeRef()));
150    }
151
152    @Override
153    public String toString() {
154        return NativeCrypto.EVP_PKEY_print_public(key.getNativeRef());
155    }
156
157    private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
158        stream.defaultReadObject();
159
160        byte[] encoded = (byte[]) stream.readObject();
161
162        key = new OpenSSLKey(NativeCrypto.d2i_PUBKEY(encoded));
163        group = new OpenSSLECGroupContext(new NativeRef.EC_GROUP(
164                NativeCrypto.EC_KEY_get1_group(key.getNativeRef())));
165    }
166
167    private void writeObject(ObjectOutputStream stream) throws IOException {
168        stream.defaultWriteObject();
169        stream.writeObject(getEncoded());
170    }
171}
172