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 * The parameters specifying a <i>characteristic 2 finite field</i> of an
25 * elliptic curve.
26 */
27public class ECFieldF2m implements ECField {
28    // Mid terms array length for trinomial basis
29    private static final int TPB_MID_LEN = 1;
30    // Mid terms array length for pentanomial basis
31    private static final int PPB_MID_LEN = 3;
32    // All terms number for trinomial basis
33    private static final int TPB_LEN = TPB_MID_LEN + 2;
34    // All terms number for pentanomial basis
35    private static final int PPB_LEN = PPB_MID_LEN + 2;
36    // m value
37    private final int m;
38    // Reduction polynomial
39    private final BigInteger rp;
40    // Mid term(s) of reduction polynomial
41    private final int[] ks;
42
43    /**
44     * Creates a new {@code ECFieldF2m} with {@code 2^m} elements with a normal
45     * basis.
46     *
47     * @param m
48     *            the exponent {@code m} for the number of elements.
49     * @throws IllegalArgumentException
50     *             if {@code m <= zero}.
51     */
52    public ECFieldF2m(int m) {
53        this.m = m;
54        if (this.m <= 0) {
55            throw new IllegalArgumentException("m <= 0");
56        }
57        this.rp = null;
58        this.ks = null;
59    }
60
61    /**
62     * Creates a new {@code ECFieldF2m} with {@code 2^m} elements with a polynomial
63     * basis and the reduction polynomial based on {@code rp}.
64     * <p>
65     * The reduction polynomial must be either <i>trinomial</i> or
66     * <i>pentanomial</i>.
67     *
68     * @param m
69     *            the exponent {@code m} for the number of elements.
70     * @param rp
71     *            the base of the reduction polynomial with the n-th bit
72     *            corresponding to the n-th coefficient of the reduction
73     *            polynomial.
74     * @throws IllegalArgumentException
75     *             if {@code m <= zero} or the {@code rp} is invalid.
76     */
77    public ECFieldF2m(int m, BigInteger rp) {
78        this.m = m;
79        if (this.m <= 0) {
80            throw new IllegalArgumentException("m <= 0");
81        }
82        this.rp = rp;
83        if (this.rp == null) {
84            throw new NullPointerException("rp == null");
85        }
86        // the leftmost bit must be (m+1)-th one,
87        // set bits count must be 3 or 5,
88        // bits 0 and m must be set
89        int rp_bc = this.rp.bitCount();
90        if ((this.rp.bitLength() != (m+1)) ||
91            (rp_bc != TPB_LEN && rp_bc != PPB_LEN) ||
92            (!this.rp.testBit(0) || !this.rp.testBit(m)) ) {
93            throw new IllegalArgumentException("rp is invalid");
94        }
95
96        // setup ks using rp:
97        // allocate for mid terms only
98        ks = new int[rp_bc-2];
99        // find midterm orders and set ks accordingly
100        BigInteger rpTmp = rp.clearBit(0);
101        for (int i=ks.length-1; i>=0; i-- ) {
102            ks[i] = rpTmp.getLowestSetBit();
103            rpTmp = rpTmp.clearBit(ks[i]);
104        }
105    }
106
107    /**
108     * Creates a new {@code ECFieldF2m} with {@code 2^m} elements with
109     * a polynomial basis and the reduction polynomial based on {@code ks}.
110     * <p>
111     * The reduction polynomial must be either <i>trinomial</i> or
112     * <i>pentanomial</i>.
113     *
114     * @param m
115     *            the exponent {@code m} for the number of elements.
116     * @param ks
117     *            the base of the reduction polynomial with coefficients
118     *            given in descending order.
119     * @throws IllegalArgumentException
120     *             if {@code m <= zero} or the reduction polynomial is not
121     *             valid.
122     */
123    public ECFieldF2m(int m, int[] ks) {
124        this.m = m;
125        if (this.m <= 0) {
126            throw new IllegalArgumentException("m <= 0");
127        }
128        // Defensively copies array parameter
129        // to prevent subsequent modification.
130        // NPE as specified if ks is null
131        this.ks = new int[ks.length];
132        System.arraycopy(ks, 0, this.ks, 0, this.ks.length);
133
134        // no need to check for null already
135        if (this.ks.length != TPB_MID_LEN && this.ks.length != PPB_MID_LEN) {
136            // must be either trinomial or pentanomial basis
137            throw new IllegalArgumentException("the length of ks is invalid");
138        }
139        // trinomial basis:
140        // check that m > k >= 1, where k is ks[0]
141        // pentanomial basis:
142        // check that m > k3 > k2 > k1 >= 1
143        // and kx in descending order, where
144        // k3 is ks[0], k2 is ks[1], k1 is ks[2]
145        boolean checkFailed = false;
146        int prev = this.m;
147        for (int i=0; i<this.ks.length; i++) {
148            if (this.ks[i] < prev) {
149                prev = this.ks[i];
150                continue;
151            }
152            checkFailed = true;
153            break;
154        }
155        if (checkFailed || prev < 1) {
156            throw new IllegalArgumentException("ks is invalid");
157        }
158
159        // Setup rp using ks:
160        // bits 0 and m always set
161        BigInteger rpTmp = BigInteger.ONE.setBit(this.m);
162        // set remaining bits according to ks
163        for (int i=0; i<this.ks.length; i++) {
164            rpTmp = rpTmp.setBit(this.ks[i]);
165        }
166        rp = rpTmp;
167    }
168
169    /**
170     * Returns whether the specified object equals to this finite field.
171     *
172     * @param obj
173     *            the object to compare to this finite field.
174     * @return {@code true} if the specified object is equal to this finite field,
175     *         otherwise {@code false}.
176     */
177    public boolean equals(Object obj) {
178        // object equals to itself
179        if (this == obj) {
180            return true;
181        }
182        if (obj instanceof ECFieldF2m) {
183            ECFieldF2m o = (ECFieldF2m)obj;
184            // check m
185            if (this.m == o.m) {
186                // check rp
187                if (this.rp == null) {
188                    if (o.rp == null) {
189                        // fields both with normal basis
190                        return true;
191                    }
192                } else {
193                    // at least this field with polynomial basis
194                    // check that rp match
195                    // return this.rp.equals(o.rp);
196                    return Arrays.equals(this.ks, o.ks);
197                }
198            }
199        }
200        return false;
201    }
202
203    /**
204     * Returns the size of this finite field (in bits).
205     *
206     * @return the size of this finite field (in bits).
207     */
208    public int getFieldSize() {
209        return m;
210    }
211
212    /**
213     * Returns the exponent {@code m} for this finite field, with {@code 2^m} as
214     * the number of elements.
215     *
216     * @return the exponent {@code m} for this finite field
217     */
218    public int getM() {
219        return m;
220    }
221
222    /**
223     * Returns a copy of the integer array containing the order of the middle
224     * term(s) of the reduction polynomial for a polynomial basis.
225     *
226     * @return a copy of the integer array containing the order of the middle
227     *         term(s) of the reduction polynomial for a polynomial basis or
228     *         {@code null} for a normal basis.
229     */
230    public int[] getMidTermsOfReductionPolynomial() {
231        // Defensively copies private array
232        // to prevent subsequent modification
233        // was: return ks == null ? null : (int[])ks.clone();
234        if (ks == null) {
235            return null;
236        } else {
237            int[] ret = new int[ks.length];
238            System.arraycopy(ks, 0, ret, 0, ret.length);
239            return ret;
240        }
241    }
242
243    /**
244     * Returns the base of the reduction polynomial with the n-th bit
245     * corresponding to the n-th coefficient of the reduction polynomial for a
246     * polynomial basis.
247     *
248     * @return the base of the reduction polynomial with the n-th bit
249     *         corresponding to the n-th coefficient of the reduction polynomial
250     *         for a polynomial basis or {@code null} for a normal basis.
251     */
252    public BigInteger getReductionPolynomial() {
253        return rp;
254    }
255
256    /**
257     * Returns the hashcode value for this finite field.
258     *
259     * @return the hashcode value for this finite field.
260     */
261    public int hashCode() {
262        return rp == null ? m : m + rp.hashCode();
263    }
264}
265