Home | History | Annotate | Download | only in ec
      1 /*
      2  * Copyright (c) 2006, Oracle and/or its affiliates. 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.  Oracle designates this
      8  * particular file as subject to the "Classpath" exception as provided
      9  * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
     22  * or visit www.oracle.com if you need additional information or have any
     23  * questions.
     24  */
     25 
     26 package sun.security.ec;
     27 
     28 import java.io.IOException;
     29 import java.math.BigInteger;
     30 
     31 import java.security.*;
     32 import java.security.spec.*;
     33 
     34 import 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  */
     78 public 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 }
    350