blob: 8f9e88fbd85c75accdea32a893526f928ddee4b8 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2006 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package sun.security.ec;
27
28import java.io.IOException;
29import java.math.BigInteger;
30
31import java.security.*;
32import java.security.spec.*;
33
34import sun.security.util.*;
35
36/**
37 * This class implements encoding and decoding of Elliptic Curve parameters
38 * as specified in RFC 3279.
39 *
40 * However, only named curves are currently supported.
41 *
42 * ASN.1 from RFC 3279 follows. Note that X9.62 (2005) has added some additional
43 * options.
44 *
45 * <pre>
46 * EcpkParameters ::= CHOICE {
47 * ecParameters ECParameters,
48 * namedCurve OBJECT IDENTIFIER,
49 * implicitlyCA NULL }
50 *
51 * ECParameters ::= SEQUENCE {
52 * version ECPVer, -- version is always 1
53 * fieldID FieldID, -- identifies the finite field over
54 * -- which the curve is defined
55 * curve Curve, -- coefficients a and b of the
56 * -- elliptic curve
57 * base ECPoint, -- specifies the base point P
58 * -- on the elliptic curve
59 * order INTEGER, -- the order n of the base point
60 * cofactor INTEGER OPTIONAL -- The integer h = #E(Fq)/n
61 * }
62 *
63 * ECPVer ::= INTEGER {ecpVer1(1)}
64 *
65 * Curve ::= SEQUENCE {
66 * a FieldElement,
67 * b FieldElement,
68 * seed BIT STRING OPTIONAL }
69 *
70 * FieldElement ::= OCTET STRING
71 *
72 * ECPoint ::= OCTET STRING
73 * </pre>
74 *
75 * @since 1.6
76 * @author Andreas Sterbenz
77 */
78public final class ECParameters extends AlgorithmParametersSpi {
79
80 public ECParameters() {
81 // empty
82 }
83
84 // Used by SunPKCS11 and SunJSSE.
85 public static ECPoint decodePoint(byte[] data, EllipticCurve curve)
86 throws IOException {
87 if ((data.length == 0) || (data[0] != 4)) {
88 throw new IOException("Only uncompressed point format supported");
89 }
90 int n = (curve.getField().getFieldSize() + 7 ) >> 3;
91 if (data.length != (n * 2) + 1) {
92 throw new IOException("Point does not match field size");
93 }
94 byte[] xb = new byte[n];
95 byte[] yb = new byte[n];
96 System.arraycopy(data, 1, xb, 0, n);
97 System.arraycopy(data, n + 1, yb, 0, n);
98 return new ECPoint(new BigInteger(1, xb), new BigInteger(1, yb));
99 }
100
101 // Used by SunPKCS11 and SunJSSE.
102 public static byte[] encodePoint(ECPoint point, EllipticCurve curve) {
103 // get field size in bytes (rounding up)
104 int n = (curve.getField().getFieldSize() + 7) >> 3;
105 byte[] xb = trimZeroes(point.getAffineX().toByteArray());
106 byte[] yb = trimZeroes(point.getAffineY().toByteArray());
107 if ((xb.length > n) || (yb.length > n)) {
108 throw new RuntimeException
109 ("Point coordinates do not match field size");
110 }
111 byte[] b = new byte[1 + (n << 1)];
112 b[0] = 4; // uncompressed
113 System.arraycopy(xb, 0, b, n - xb.length + 1, xb.length);
114 System.arraycopy(yb, 0, b, b.length - yb.length, yb.length);
115 return b;
116 }
117
118 // Copied from the SunPKCS11 code - should be moved to a common location.
119 // trim leading (most significant) zeroes from the result
120 static byte[] trimZeroes(byte[] b) {
121 int i = 0;
122 while ((i < b.length - 1) && (b[i] == 0)) {
123 i++;
124 }
125 if (i == 0) {
126 return b;
127 }
128 byte[] t = new byte[b.length - i];
129 System.arraycopy(b, i, t, 0, t.length);
130 return t;
131 }
132
133 // Convert the given ECParameterSpec object to a NamedCurve object.
134 // If params does not represent a known named curve, return null.
135 // Used by SunPKCS11.
136 public static NamedCurve getNamedCurve(ECParameterSpec params) {
137 if ((params instanceof NamedCurve) || (params == null)) {
138 return (NamedCurve)params;
139 }
140 // This is a hack to allow SunJSSE to work with 3rd party crypto
141 // providers for ECC and not just SunPKCS11.
142 // This can go away once we decide how to expose curve names in the
143 // public API.
144 // Note that it assumes that the 3rd party provider encodes named
145 // curves using the short form, not explicitly. If it did that, then
146 // the SunJSSE TLS ECC extensions are wrong, which could lead to
147 // interoperability problems.
148 int fieldSize = params.getCurve().getField().getFieldSize();
149 for (ECParameterSpec namedCurve : NamedCurve.knownECParameterSpecs()) {
150 // ECParameterSpec does not define equals, so check all the
151 // components ourselves.
152 // Quick field size check first
153 if (namedCurve.getCurve().getField().getFieldSize() != fieldSize) {
154 continue;
155 }
156 if (namedCurve.getCurve().equals(params.getCurve()) == false) {
157 continue;
158 }
159 if (namedCurve.getGenerator().equals(params.getGenerator()) == false) {
160 continue;
161 }
162 if (namedCurve.getOrder().equals(params.getOrder()) == false) {
163 continue;
164 }
165 if (namedCurve.getCofactor() != params.getCofactor()) {
166 continue;
167 }
168 // everything matches our named curve, return it
169 return (NamedCurve)namedCurve;
170 }
171 // no match found
172 return null;
173 }
174
175 // Used by SunJSSE.
176 public static String getCurveName(ECParameterSpec params) {
177 NamedCurve curve = getNamedCurve(params);
178 return (curve == null) ? null : curve.getObjectIdentifier().toString();
179 }
180
181 // Used by SunPKCS11.
182 public static byte[] encodeParameters(ECParameterSpec params) {
183 NamedCurve curve = getNamedCurve(params);
184 if (curve == null) {
185 throw new RuntimeException("Not a known named curve: " + params);
186 }
187 return curve.getEncoded();
188 }
189
190 // Used by SunPKCS11.
191 public static ECParameterSpec decodeParameters(byte[] params) throws IOException {
192 DerValue encodedParams = new DerValue(params);
193 if (encodedParams.tag == DerValue.tag_ObjectId) {
194 ObjectIdentifier oid = encodedParams.getOID();
195 ECParameterSpec spec = NamedCurve.getECParameterSpec(oid);
196 if (spec == null) {
197 throw new IOException("Unknown named curve: " + oid);
198 }
199 return spec;
200 }
201
202 throw new IOException("Only named ECParameters supported");
203
204 // The code below is incomplete.
205 // It is left as a starting point for a complete parsing implementation.
206
207/*
208 if (encodedParams.tag != DerValue.tag_Sequence) {
209 throw new IOException("Unsupported EC parameters, tag: " + encodedParams.tag);
210 }
211
212 encodedParams.data.reset();
213
214 DerInputStream in = encodedParams.data;
215
216 int version = in.getInteger();
217 if (version != 1) {
218 throw new IOException("Unsupported EC parameters version: " + version);
219 }
220 ECField field = parseField(in);
221 EllipticCurve curve = parseCurve(in, field);
222 ECPoint point = parsePoint(in, curve);
223
224 BigInteger order = in.getBigInteger();
225 int cofactor = 0;
226
227 if (in.available() != 0) {
228 cofactor = in.getInteger();
229 }
230
231 // XXX HashAlgorithm optional
232
233 if (encodedParams.data.available() != 0) {
234 throw new IOException("encoded params have " +
235 encodedParams.data.available() +
236 " extra bytes");
237 }
238
239 return new ECParameterSpec(curve, point, order, cofactor);
240*/
241 }
242
243/*
244 private static final ObjectIdentifier fieldTypePrime =
245 ObjectIdentifier.newInternal(new int[] {1, 2, 840, 10045, 1, 1});
246
247 private static final ObjectIdentifier fieldTypeChar2 =
248 ObjectIdentifier.newInternal(new int[] {1, 2, 840, 10045, 1, 2});
249
250 private static ECField parseField(DerInputStream in) throws IOException {
251 DerValue v = in.getDerValue();
252 ObjectIdentifier oid = v.data.getOID();
253 if (oid.equals(fieldTypePrime) == false) {
254 throw new IOException("Only prime fields supported: " + oid);
255 }
256 BigInteger fieldSize = v.data.getBigInteger();
257 return new ECFieldFp(fieldSize);
258 }
259
260 private static EllipticCurve parseCurve(DerInputStream in, ECField field)
261 throws IOException {
262 DerValue v = in.getDerValue();
263 byte[] ab = v.data.getOctetString();
264 byte[] bb = v.data.getOctetString();
265 return new EllipticCurve(field, new BigInteger(1, ab), new BigInteger(1, bb));
266 }
267
268 private static ECPoint parsePoint(DerInputStream in, EllipticCurve curve)
269 throws IOException {
270 byte[] data = in.getOctetString();
271 return decodePoint(data, curve);
272 }
273*/
274
275 // used by ECPublicKeyImpl and ECPrivateKeyImpl
276 static AlgorithmParameters getAlgorithmParameters(ECParameterSpec spec)
277 throws InvalidKeyException {
278 try {
279 AlgorithmParameters params = AlgorithmParameters.getInstance
280 ("EC", ECKeyFactory.ecInternalProvider);
281 params.init(spec);
282 return params;
283 } catch (GeneralSecurityException e) {
284 throw new InvalidKeyException("EC parameters error", e);
285 }
286 }
287
288 // AlgorithmParameterSpi methods
289
290 // The parameters these AlgorithmParameters object represents.
291 // Currently, it is always an instance of NamedCurve.
292 private ECParameterSpec paramSpec;
293
294 protected void engineInit(AlgorithmParameterSpec paramSpec)
295 throws InvalidParameterSpecException {
296 if (paramSpec instanceof ECParameterSpec) {
297 this.paramSpec = getNamedCurve((ECParameterSpec)paramSpec);
298 if (this.paramSpec == null) {
299 throw new InvalidParameterSpecException
300 ("Not a supported named curve: " + paramSpec);
301 }
302 } else if (paramSpec instanceof ECGenParameterSpec) {
303 String name = ((ECGenParameterSpec)paramSpec).getName();
304 ECParameterSpec spec = NamedCurve.getECParameterSpec(name);
305 if (spec == null) {
306 throw new InvalidParameterSpecException("Unknown curve: " + name);
307 }
308 this.paramSpec = spec;
309 } else if (paramSpec == null) {
310 throw new InvalidParameterSpecException
311 ("paramSpec must not be null");
312 } else {
313 throw new InvalidParameterSpecException
314 ("Only ECParameterSpec and ECGenParameterSpec supported");
315 }
316 }
317
318 protected void engineInit(byte[] params) throws IOException {
319 paramSpec = decodeParameters(params);
320 }
321
322 protected void engineInit(byte[] params, String decodingMethod) throws IOException {
323 engineInit(params);
324 }
325
326 protected <T extends AlgorithmParameterSpec> T engineGetParameterSpec(Class<T> spec)
327 throws InvalidParameterSpecException {
328 if (spec.isAssignableFrom(ECParameterSpec.class)) {
329 return (T)paramSpec;
330 } else if (spec.isAssignableFrom(ECGenParameterSpec.class)) {
331 return (T)new ECGenParameterSpec(getCurveName(paramSpec));
332 } else {
333 throw new InvalidParameterSpecException
334 ("Only ECParameterSpec and ECGenParameterSpec supported");
335 }
336 }
337
338 protected byte[] engineGetEncoded() throws IOException {
339 return encodeParameters(paramSpec);
340 }
341
342 protected byte[] engineGetEncoded(String encodingMethod) throws IOException {
343 return engineGetEncoded();
344 }
345
346 protected String engineToString() {
347 return paramSpec.toString();
348 }
349}