1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18package java.security.spec;
19
20import java.math.BigInteger;
21import java.util.Arrays;
22
23/**
24 * An Elliptic Curve with its necessary values.
25 */
26public class EllipticCurve {
27
28    // Underlying finite field
29    private final ECField field;
30
31    // The first coefficient of the equation defining this elliptic curve
32    private final BigInteger a;
33
34    // The second coefficient of the equation defining this elliptic curve
35    private final BigInteger b;
36
37    // Bytes used during this elliptic curve generation,
38    // if it was generated randomly
39    private final byte[] seed;
40
41    // Hash code
42    private volatile int hash;
43
44    /**
45     * Creates a new {@code EllipticCurve} with the specified field,
46     * coefficients and seed.
47     *
48     * @param field
49     *            the finite field of this elliptic curve.
50     * @param a
51     *            the coefficient {@code a}.
52     * @param b
53     *            the coefficient {@code b}.
54     * @param seed
55     *            the seed used for the generation of the curve.
56     * @throws IllegalArgumentException
57     *             if the specified coefficients are not in the specified field.
58     */
59    public EllipticCurve(ECField field, BigInteger a, BigInteger b, byte[] seed) {
60        this.field = field;
61        if (this.field == null) {
62            throw new NullPointerException("field == null");
63        }
64        this.a = a;
65        if (this.a == null) {
66            throw new NullPointerException("a == null");
67        }
68        this.b = b;
69        if (this.b == null) {
70            throw new NullPointerException("b == null");
71        }
72        // make defensive copy
73        if (seed == null) {
74            this.seed = null;
75        } else {
76            this.seed = new byte[seed.length];
77            System.arraycopy(seed, 0, this.seed, 0, this.seed.length);
78        }
79        // check parameters for ECFieldFp and ECFieldF2m.
80        // Check invariant: a and b must be in the field.
81        // Check conditions for custom ECField are not specified.
82        if (this.field instanceof ECFieldFp) {
83            BigInteger p = ((ECFieldFp) this.field).getP();
84            if (this.a.signum() < 0 || this.a.compareTo(p) >= 0) {
85                throw new IllegalArgumentException("the a is not in the field");
86            }
87            if (this.b.signum() < 0 || this.b.compareTo(p) >= 0) {
88                throw new IllegalArgumentException("the b is not in the field");
89            }
90        } else if (this.field instanceof ECFieldF2m) {
91            int fieldSizeInBits = this.field.getFieldSize();
92            if (!(this.a.bitLength() <= fieldSizeInBits)) {
93                throw new IllegalArgumentException("the a is not in the field");
94            }
95            if (!(this.b.bitLength() <= fieldSizeInBits)) {
96                throw new IllegalArgumentException("the b is not in the field");
97            }
98        }
99    }
100
101    /**
102     * Creates a new {@code EllipticCurve} with the specified field and
103     * coefficients.
104     *
105     * @param field
106     *            the finite field of this elliptic curve.
107     * @param a
108     *            the coefficient {@code a}.
109     * @param b
110     *            the coefficient {@code b}.
111     * @throws IllegalArgumentException
112     *             if the specified coefficients are not in the specified field.
113     */
114    public EllipticCurve(ECField field, BigInteger a, BigInteger b) {
115        this(field, a, b, null);
116    }
117
118    /**
119     * Returns the coefficient {@code a} of this elliptic curve.
120     *
121     * @return the coefficient {@code a} of this elliptic curve.
122     */
123    public BigInteger getA() {
124        return a;
125    }
126
127    /**
128     * Returns the coefficient {@code b} of this elliptic curve.
129     *
130     * @return the coefficient {@code b} of this elliptic curve.
131     */
132    public BigInteger getB() {
133        return b;
134    }
135
136    /**
137     * Returns the finite field of this elliptic curve.
138     *
139     * @return the finite field of this elliptic curve.
140     */
141    public ECField getField() {
142        return field;
143    }
144
145    /**
146     * Returns a copy of the seed that was used to generate this elliptic curve.
147     *
148     * @return a copy of the seed that was used to generate this elliptic curve,
149     *         or {@code null} if none specified.
150     */
151    public byte[] getSeed() {
152        if (seed == null) {
153            return null;
154        } else {
155            // return copy
156            byte[] ret = new byte[seed.length];
157            System.arraycopy(seed, 0, ret, 0, ret.length);
158            return ret;
159        }
160    }
161
162    /**
163     * Returns whether the specified object equals to this elliptic curve.
164     *
165     * @param other
166     *            the object to compare.
167     * @return {@code true} if the specified object is equal to this elliptic
168     *         curve, otherwise {@code false}.
169     */
170    public boolean equals(Object other) {
171        if (this == other) {
172            return true;
173        }
174        if (!(other instanceof EllipticCurve)) {
175            return false;
176        }
177        EllipticCurve otherEc = (EllipticCurve) other;
178        return this.field.equals(otherEc.field) && this.a.equals(otherEc.a)
179                && this.b.equals(otherEc.b)
180                && Arrays.equals(this.seed, otherEc.seed);
181    }
182
183    /**
184     * Returns the hashcode of this elliptic curve.
185     *
186     * @return the hashcode of this elliptic curve.
187     */
188    public int hashCode() {
189        // hash init is delayed
190        if (hash == 0) {
191            int hash0 = 11;
192            hash0 = hash0 * 31 + field.hashCode();
193            hash0 = hash0 * 31 + a.hashCode();
194            hash0 = hash0 * 31 + b.hashCode();
195            if (seed != null) {
196                for (int i = 0; i < seed.length; i++) {
197                    hash0 = hash0 * 31 + seed[i];
198                }
199            } else {
200                hash0 = hash0 * 31;
201            }
202            hash = hash0;
203        }
204        return hash;
205    }
206}
207