/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package java.security.spec;
import java.math.BigInteger;
import java.util.Arrays;
/**
* The parameters specifying a <i>characteristic 2 finite field</i> of an
* elliptic curve.
*/
public class ECFieldF2m implements ECField {
// Mid terms array length for trinomial basis
private static final int TPB_MID_LEN = 1;
// Mid terms array length for pentanomial basis
private static final int PPB_MID_LEN = 3;
// All terms number for trinomial basis
private static final int TPB_LEN = TPB_MID_LEN + 2;
// All terms number for pentanomial basis
private static final int PPB_LEN = PPB_MID_LEN + 2;
// m value
private final int m;
// Reduction polynomial
private final BigInteger rp;
// Mid term(s) of reduction polynomial
private final int[] ks;
/**
* Creates a new {@code ECFieldF2m} with {@code 2^m} elements with a normal
* basis.
*
* @param m
* the exponent {@code m} for the number of elements.
* @throws IllegalArgumentException
* if {@code m <= zero}.
*/
public ECFieldF2m(int m) {
this.m = m;
if (this.m <= 0) {
throw new IllegalArgumentException("m <= 0");
}
this.rp = null;
this.ks = null;
}
/**
* Creates a new {@code ECFieldF2m} with {@code 2^m} elements with a polynomial
* basis and the reduction polynomial based on {@code rp}.
* <p>
* The reduction polynomial must be either <i>trinomial</i> or
* <i>pentanomial</i>.
*
* @param m
* the exponent {@code m} for the number of elements.
* @param rp
* the base of the reduction polynomial with the n-th bit
* corresponding to the n-th coefficient of the reduction
* polynomial.
* @throws IllegalArgumentException
* if {@code m <= zero} or the {@code rp} is invalid.
*/
public ECFieldF2m(int m, BigInteger rp) {
this.m = m;
if (this.m <= 0) {
throw new IllegalArgumentException("m <= 0");
}
this.rp = rp;
if (this.rp == null) {
throw new NullPointerException("rp == null");
}
// the leftmost bit must be (m+1)-th one,
// set bits count must be 3 or 5,
// bits 0 and m must be set
int rp_bc = this.rp.bitCount();
if ((this.rp.bitLength() != (m+1)) ||
(rp_bc != TPB_LEN && rp_bc != PPB_LEN) ||
(!this.rp.testBit(0) || !this.rp.testBit(m)) ) {
throw new IllegalArgumentException("rp is invalid");
}
// setup ks using rp:
// allocate for mid terms only
ks = new int[rp_bc-2];
// find midterm orders and set ks accordingly
BigInteger rpTmp = rp.clearBit(0);
for (int i=ks.length-1; i>=0; i-- ) {
ks[i] = rpTmp.getLowestSetBit();
rpTmp = rpTmp.clearBit(ks[i]);
}
}
/**
* Creates a new {@code ECFieldF2m} with {@code 2^m} elements with
* a polynomial basis and the reduction polynomial based on {@code ks}.
* <p>
* The reduction polynomial must be either <i>trinomial</i> or
* <i>pentanomial</i>.
*
* @param m
* the exponent {@code m} for the number of elements.
* @param ks
* the base of the reduction polynomial with coefficients
* given in descending order.
* @throws IllegalArgumentException
* if {@code m <= zero} or the reduction polynomial is not
* valid.
*/
public ECFieldF2m(int m, int[] ks) {
this.m = m;
if (this.m <= 0) {
throw new IllegalArgumentException("m <= 0");
}
// Defensively copies array parameter
// to prevent subsequent modification.
// NPE as specified if ks is null
this.ks = new int[ks.length];
System.arraycopy(ks, 0, this.ks, 0, this.ks.length);
// no need to check for null already
if (this.ks.length != TPB_MID_LEN && this.ks.length != PPB_MID_LEN) {
// must be either trinomial or pentanomial basis
throw new IllegalArgumentException("the length of ks is invalid");
}
// trinomial basis:
// check that m > k >= 1, where k is ks[0]
// pentanomial basis:
// check that m > k3 > k2 > k1 >= 1
// and kx in descending order, where
// k3 is ks[0], k2 is ks[1], k1 is ks[2]
boolean checkFailed = false;
int prev = this.m;
for (int i=0; i<this.ks.length; i++) {
if (this.ks[i] < prev) {
prev = this.ks[i];
continue;
}
checkFailed = true;
break;
}
if (checkFailed || prev < 1) {
throw new IllegalArgumentException("ks is invalid");
}
// Setup rp using ks:
// bits 0 and m always set
BigInteger rpTmp = BigInteger.ONE.setBit(this.m);
// set remaining bits according to ks
for (int i=0; i<this.ks.length; i++) {
rpTmp = rpTmp.setBit(this.ks[i]);
}
rp = rpTmp;
}
/**
* Returns whether the specified object equals to this finite field.
*
* @param obj
* the object to compare to this finite field.
* @return {@code true} if the specified object is equal to this finite field,
* otherwise {@code false}.
*/
public boolean equals(Object obj) {
// object equals to itself
if (this == obj) {
return true;
}
if (obj instanceof ECFieldF2m) {
ECFieldF2m o = (ECFieldF2m)obj;
// check m
if (this.m == o.m) {
// check rp
if (this.rp == null) {
if (o.rp == null) {
// fields both with normal basis
return true;
}
} else {
// at least this field with polynomial basis
// check that rp match
// return this.rp.equals(o.rp);
return Arrays.equals(this.ks, o.ks);
}
}
}
return false;
}
/**
* Returns the size of this finite field (in bits).
*
* @return the size of this finite field (in bits).
*/
public int getFieldSize() {
return m;
}
/**
* Returns the exponent {@code m} for this finite field, with {@code 2^m} as
* the number of elements.
*
* @return the exponent {@code m} for this finite field
*/
public int getM() {
return m;
}
/**
* Returns a copy of the integer array containing the order of the middle
* term(s) of the reduction polynomial for a polynomial basis.
*
* @return a copy of the integer array containing the order of the middle
* term(s) of the reduction polynomial for a polynomial basis or
* {@code null} for a normal basis.
*/
public int[] getMidTermsOfReductionPolynomial() {
// Defensively copies private array
// to prevent subsequent modification
// was: return ks == null ? null : (int[])ks.clone();
if (ks == null) {
return null;
} else {
int[] ret = new int[ks.length];
System.arraycopy(ks, 0, ret, 0, ret.length);
return ret;
}
}
/**
* Returns the base of the reduction polynomial with the n-th bit
* corresponding to the n-th coefficient of the reduction polynomial for a
* polynomial basis.
*
* @return the base of the reduction polynomial with the n-th bit
* corresponding to the n-th coefficient of the reduction polynomial
* for a polynomial basis or {@code null} for a normal basis.
*/
public BigInteger getReductionPolynomial() {
return rp;
}
/**
* Returns the hashcode value for this finite field.
*
* @return the hashcode value for this finite field.
*/
public int hashCode() {
return rp == null ? m : m + rp.hashCode();
}
}