summaryrefslogtreecommitdiff
path: root/src/cz/crcs/ectester/common/ec
diff options
context:
space:
mode:
Diffstat (limited to 'src/cz/crcs/ectester/common/ec')
-rw-r--r--src/cz/crcs/ectester/common/ec/EC_Category.java142
-rw-r--r--src/cz/crcs/ectester/common/ec/EC_Curve.java132
-rw-r--r--src/cz/crcs/ectester/common/ec/EC_Data.java217
-rw-r--r--src/cz/crcs/ectester/common/ec/EC_KAResult.java65
-rw-r--r--src/cz/crcs/ectester/common/ec/EC_Key.java83
-rw-r--r--src/cz/crcs/ectester/common/ec/EC_Keypair.java41
-rw-r--r--src/cz/crcs/ectester/common/ec/EC_Params.java194
7 files changed, 874 insertions, 0 deletions
diff --git a/src/cz/crcs/ectester/common/ec/EC_Category.java b/src/cz/crcs/ectester/common/ec/EC_Category.java
new file mode 100644
index 0000000..32a788d
--- /dev/null
+++ b/src/cz/crcs/ectester/common/ec/EC_Category.java
@@ -0,0 +1,142 @@
+package cz.crcs.ectester.common.ec;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Objects;
+import java.util.TreeMap;
+
+/**
+ * A category of EC_Data objects, has a name, description and represents a directory in
+ * the cz.crcs.ectester.data package.
+ * @author Jan Jancar johny@neuromancer.sk
+ */
+public class EC_Category {
+
+ private String name;
+ private String directory;
+ private String desc;
+
+ private Map<String, EC_Data> objects;
+
+
+ public EC_Category(String name, String directory) {
+ this.name = name;
+ this.directory = directory;
+ }
+
+ public EC_Category(String name, String directory, String desc) {
+ this(name, directory);
+ this.desc = desc;
+ }
+
+ public EC_Category(String name, String directory, String desc, Map<String, EC_Data> objects) {
+ this(name, directory, desc);
+ this.objects = objects;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getDirectory() {
+ return directory;
+ }
+
+ public String getDesc() {
+ return desc;
+ }
+
+ public Map<String, EC_Data> getObjects() {
+ return Collections.unmodifiableMap(objects);
+ }
+
+ public <T extends EC_Data> Map<String, T> getObjects(Class<T> cls) {
+ Map<String, T> objs = new TreeMap<>();
+ for (Map.Entry<String, EC_Data> entry : objects.entrySet()) {
+ if (cls.isInstance(entry.getValue())) {
+ objs.put(entry.getKey(), cls.cast(entry.getValue()));
+ }
+ }
+ return Collections.unmodifiableMap(objs);
+ }
+
+ public <T extends EC_Data> T getObject(Class<T> cls, String id) {
+ EC_Data obj = objects.get(id);
+ if (cls.isInstance(obj)) {
+ return cls.cast(obj);
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder out = new StringBuilder();
+ out.append("\t- ").append(name).append((desc == null || desc.equals("")) ? "" : ": " + desc);
+ out.append(System.lineSeparator());
+
+ Map<String, EC_Curve> curves = getObjects(EC_Curve.class);
+ int size = curves.size();
+ if (size > 0) {
+ out.append("\t\tCurves: ");
+ for (Map.Entry<String, EC_Curve> curve : curves.entrySet()) {
+ out.append(curve.getKey());
+ size--;
+ if (size > 0)
+ out.append(", ");
+ }
+ out.append(System.lineSeparator());
+ }
+
+ Map<String, EC_Key> keys = getObjects(EC_Key.class);
+ size = keys.size();
+ if (size > 0) {
+ out.append("\t\tKeys: ");
+ for (Map.Entry<String, EC_Key> key : keys.entrySet()) {
+ out.append(key.getKey());
+ size--;
+ if (size > 0)
+ out.append(", ");
+ }
+ out.append(System.lineSeparator());
+ }
+
+ Map<String, EC_Keypair> keypairs = getObjects(EC_Keypair.class);
+ size = keypairs.size();
+ if (size > 0) {
+ out.append("\t\tKeypairs: ");
+ for (Map.Entry<String, EC_Keypair> key : keypairs.entrySet()) {
+ out.append(key.getKey());
+ size--;
+ if (size > 0)
+ out.append(", ");
+ }
+ out.append(System.lineSeparator());
+ }
+
+ Map<String, EC_KAResult> results = getObjects(EC_KAResult.class);
+ size = results.size();
+ if (size > 0) {
+ out.append("\t\tResults: ");
+ for (Map.Entry<String, EC_KAResult> result : results.entrySet()) {
+ out.append(result.getKey());
+ size--;
+ if (size > 0)
+ out.append(", ");
+ }
+ out.append(System.lineSeparator());
+ }
+ return out.toString();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof EC_Category && Objects.equals(this.name, ((EC_Category) obj).name);
+ }
+
+ @Override
+ public int hashCode() {
+ return this.name.hashCode() ^ this.directory.hashCode();
+ }
+
+}
diff --git a/src/cz/crcs/ectester/common/ec/EC_Curve.java b/src/cz/crcs/ectester/common/ec/EC_Curve.java
new file mode 100644
index 0000000..173fd29
--- /dev/null
+++ b/src/cz/crcs/ectester/common/ec/EC_Curve.java
@@ -0,0 +1,132 @@
+package cz.crcs.ectester.common.ec;
+
+import cz.crcs.ectester.applet.EC_Consts;
+import cz.crcs.ectester.common.util.ByteUtil;
+import javacard.security.KeyPair;
+
+import java.math.BigInteger;
+import java.security.spec.*;
+
+/**
+ * An Elliptic curve, contains parameters Fp/F2M, A, B, G, R, (K)?.
+ *
+ * @author Jan Jancar johny@neuromancer.sk
+ */
+public class EC_Curve extends EC_Params {
+ private short bits;
+ private byte field;
+ private String desc;
+
+ /**
+ * @param bits
+ * @param field KeyPair.ALG_EC_FP or KeyPair.ALG_EC_F2M
+ */
+ public EC_Curve(short bits, byte field) {
+ super(field == KeyPair.ALG_EC_FP ? EC_Consts.PARAMETERS_DOMAIN_FP : EC_Consts.PARAMETERS_DOMAIN_F2M);
+ this.bits = bits;
+ this.field = field;
+ }
+
+ public EC_Curve(String id, short bits, byte field) {
+ this(bits, field);
+ this.id = id;
+ }
+
+ public EC_Curve(String id, short bits, byte field, String desc) {
+ this(id, bits, field);
+ this.desc = desc;
+ }
+
+ public short getBits() {
+ return bits;
+ }
+
+ public byte getField() {
+ return field;
+ }
+
+ public String getDesc() {
+ return desc;
+ }
+
+ @Override
+ public String toString() {
+ return "<" + getId() + "> " + (field == KeyPair.ALG_EC_FP ? "Prime" : "Binary") + " field Elliptic curve (" + String.valueOf(bits) + "b)" + (desc == null ? "" : ": " + desc);
+ }
+
+ public ECParameterSpec toSpec() {
+ ECField field;
+ if (this.field == KeyPair.ALG_EC_FP) {
+ field = new ECFieldFp(new BigInteger(1, getData(0)));
+ } else {
+ byte[][] fieldData = getParam(EC_Consts.PARAMETER_F2M);
+ int m = ByteUtil.getShort(fieldData[0], 0);
+ int e1 = ByteUtil.getShort(fieldData[1], 0);
+ int e2 = ByteUtil.getShort(fieldData[2], 0);
+ int e3 = ByteUtil.getShort(fieldData[3], 0);
+ int[] powers = new int[]{e1, e2, e3};
+ field = new ECFieldF2m(m, powers);
+ }
+
+ BigInteger a = new BigInteger(1, getParam(EC_Consts.PARAMETER_A)[0]);
+ BigInteger b = new BigInteger(1, getParam(EC_Consts.PARAMETER_B)[0]);
+
+ EllipticCurve curve = new EllipticCurve(field, a, b);
+
+ byte[][] G = getParam(EC_Consts.PARAMETER_G);
+ BigInteger gx = new BigInteger(1, G[0]);
+ BigInteger gy = new BigInteger(1, G[1]);
+ ECPoint generator = new ECPoint(gx, gy);
+
+ BigInteger n = new BigInteger(1, getParam(EC_Consts.PARAMETER_R)[0]);
+
+ int h = ByteUtil.getShort(getParam(EC_Consts.PARAMETER_K)[0], 0);
+
+ return new ECParameterSpec(curve, generator, n, h);
+ }
+
+ public static EC_Curve fromSpec(ECParameterSpec spec) {
+ EllipticCurve curve = spec.getCurve();
+ ECField field = curve.getField();
+
+ short bits = (short) field.getFieldSize();
+ byte[][] params;
+ int paramIndex = 0;
+ byte fieldType;
+ if (field instanceof ECFieldFp) {
+ ECFieldFp primeField = (ECFieldFp) field;
+ params = new byte[7][];
+ params[paramIndex++] = primeField.getP().toByteArray();
+ fieldType = KeyPair.ALG_EC_FP;
+ } else if (field instanceof ECFieldF2m) {
+ ECFieldF2m binaryField = (ECFieldF2m) field;
+ params = new byte[10][];
+ params[paramIndex] = new byte[2];
+ ByteUtil.setShort(params[paramIndex++], 0, (short) binaryField.getM());
+ int[] powers = binaryField.getMidTermsOfReductionPolynomial();
+ for (int i = 0; i < 3; ++i) {
+ params[paramIndex] = new byte[2];
+ ByteUtil.setShort(params[paramIndex++], 0, (short) powers[i]);
+ }
+ fieldType = KeyPair.ALG_EC_F2M;
+ } else {
+ throw new IllegalArgumentException("ECParameterSpec with an unknown field.");
+ }
+
+ ECPoint generator = spec.getGenerator();
+
+ params[paramIndex++] = curve.getA().toByteArray();
+ params[paramIndex++] = curve.getB().toByteArray();
+
+ params[paramIndex++] = generator.getAffineX().toByteArray();
+ params[paramIndex++] = generator.getAffineY().toByteArray();
+
+ params[paramIndex++] = spec.getOrder().toByteArray();
+ params[paramIndex] = new byte[2];
+ ByteUtil.setShort(params[paramIndex], 0, (short) spec.getCofactor());
+
+ EC_Curve result = new EC_Curve(bits, fieldType);
+ result.readByteArray(params);
+ return result;
+ }
+}
diff --git a/src/cz/crcs/ectester/common/ec/EC_Data.java b/src/cz/crcs/ectester/common/ec/EC_Data.java
new file mode 100644
index 0000000..c048ef7
--- /dev/null
+++ b/src/cz/crcs/ectester/common/ec/EC_Data.java
@@ -0,0 +1,217 @@
+package cz.crcs.ectester.common.ec;
+
+import cz.crcs.ectester.common.util.ByteUtil;
+
+import java.io.*;
+import java.util.*;
+import java.util.regex.Pattern;
+
+/**
+ * A list of byte arrays for holding EC data.
+ * <p>
+ * The data can be read from a byte array via <code>readBytes()</code>, from a CSV via <code>readCSV()</code>.
+ * The data can be exported to a byte array via <code>flatten()</code> or to a string array via <code>expand()</code>.
+ *
+ * @author Jan Jancar johny@neuromancer.sk
+ */
+public abstract class EC_Data {
+ String id;
+ int count;
+ byte[][] data;
+
+ private static final Pattern HEX = Pattern.compile("(0x|0X)?[a-fA-F\\d]+");
+
+ EC_Data() {
+ }
+
+ EC_Data(int count) {
+ this.count = count;
+ this.data = new byte[count][];
+ }
+
+ EC_Data(byte[][] data) {
+ this.count = data.length;
+ this.data = data;
+ }
+
+ EC_Data(String id, int count) {
+ this(count);
+ this.id = id;
+ }
+
+ EC_Data(String id, byte[][] data) {
+ this(data);
+ this.id = id;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public int getCount() {
+ return count;
+ }
+
+ public byte[][] getData() {
+ return data;
+ }
+
+ public byte[] getData(int index) {
+ return data[index];
+ }
+
+ public boolean hasData() {
+ return data != null;
+ }
+
+ public byte[] flatten() {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ for (byte[] param : data) {
+ byte[] length = new byte[2];
+ ByteUtil.setShort(length, 0, (short) param.length);
+
+ out.write(length, 0, 2);
+ out.write(param, 0, param.length);
+ }
+
+ return out.toByteArray();
+ }
+
+ public String[] expand() {
+ List<String> out = new ArrayList<>(count);
+ for (byte[] param : data) {
+ out.add(ByteUtil.bytesToHex(param, false));
+ }
+
+ return out.toArray(new String[out.size()]);
+ }
+
+ private static byte[] pad(byte[] data) {
+ if (data.length == 1) {
+ return new byte[]{(byte) 0, data[0]};
+ } else if (data.length == 0 || data.length > 2) {
+ return data;
+ }
+ return null;
+ }
+
+ private static byte[] parse(String param) {
+ byte[] data;
+ if (param.startsWith("0x") || param.startsWith("0X")) {
+ data = ByteUtil.hexToBytes(param.substring(2));
+ } else {
+ data = ByteUtil.hexToBytes(param);
+ }
+ if (data == null)
+ return new byte[0];
+ if (data.length < 2)
+ return pad(data);
+ return data;
+ }
+
+ private boolean readHex(String[] hex) {
+ if (hex.length != count) {
+ return false;
+ }
+
+ for (int i = 0; i < count; ++i) {
+ this.data[i] = parse(hex[i]);
+ }
+ return true;
+ }
+
+ public boolean readCSV(InputStream in) {
+ Scanner s = new Scanner(in);
+
+ s.useDelimiter(",|;");
+ List<String> data = new LinkedList<>();
+ while (s.hasNext()) {
+ String field = s.next();
+ data.add(field.replaceAll("\\s+", ""));
+ }
+
+ if (data.isEmpty()) {
+ return false;
+ }
+ for (String param : data) {
+ if (!HEX.matcher(param).matches()) {
+ return false;
+ }
+ }
+ return readHex(data.toArray(new String[data.size()]));
+ }
+
+ public boolean readBytes(byte[] bytes) {
+ if (bytes == null) {
+ return false;
+ }
+
+ int offset = 0;
+ for (int i = 0; i < count; i++) {
+ if (bytes.length - offset < 2) {
+ return false;
+ }
+ short paramLength = ByteUtil.getShort(bytes, offset);
+ offset += 2;
+ if (bytes.length < offset + paramLength) {
+ return false;
+ }
+ data[i] = new byte[paramLength];
+ System.arraycopy(bytes, offset, data[i], 0, paramLength);
+ offset += paramLength;
+ }
+ return true;
+ }
+
+ public boolean readByteArray(byte[][] bytes) {
+ if (bytes == null || count != bytes.length) {
+ return false;
+ }
+
+ for (int i = 0; i < count; ++i) {
+ data[i] = new byte[bytes[i].length];
+ System.arraycopy(bytes[i], 0, data[i], 0, bytes[i].length);
+ }
+ return true;
+ }
+
+ public void writeCSV(OutputStream out) throws IOException {
+ Writer w = new OutputStreamWriter(out);
+ w.write(String.join(",", expand()));
+ w.flush();
+ }
+
+ @Override
+ public String toString() {
+ return String.join(",", expand());
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof EC_Data) {
+ EC_Data other = (EC_Data) obj;
+ if (this.id != null || other.id != null) {
+ return Objects.equals(this.id, other.id);
+ }
+
+ if (this.count != other.count)
+ return false;
+ for (int i = 0; i < this.count; ++i) {
+ if (!Arrays.equals(this.data[i], other.data[i])) {
+ return false;
+ }
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ if (this.id != null) {
+ return this.id.hashCode();
+ }
+ return Arrays.deepHashCode(this.data);
+ }
+}
diff --git a/src/cz/crcs/ectester/common/ec/EC_KAResult.java b/src/cz/crcs/ectester/common/ec/EC_KAResult.java
new file mode 100644
index 0000000..8a5fcb4
--- /dev/null
+++ b/src/cz/crcs/ectester/common/ec/EC_KAResult.java
@@ -0,0 +1,65 @@
+package cz.crcs.ectester.common.ec;
+
+import cz.crcs.ectester.common.util.CardUtil;
+
+/**
+ * A result of EC based Key agreement operation.
+ *
+ * @author Jan Jancar johny@neuromancer.sk
+ */
+public class EC_KAResult extends EC_Data {
+ private String ka;
+ private String curve;
+ private String oneKey;
+ private String otherKey;
+
+ private String desc;
+
+ public EC_KAResult(String ka, String curve, String oneKey, String otherKey) {
+ super(1);
+ this.ka = ka;
+ this.curve = curve;
+ this.oneKey = oneKey;
+ this.otherKey = otherKey;
+ }
+
+ public EC_KAResult(String id, String ka, String curve, String oneKey, String otherKey) {
+ this(ka, curve, oneKey, otherKey);
+ this.id = id;
+ }
+
+ public EC_KAResult(String id, String ka, String curve, String oneKey, String otherKey, String desc) {
+ this(id, ka, curve, oneKey, otherKey);
+ this.desc = desc;
+ }
+
+ public String getKA() {
+ return ka;
+ }
+
+ public byte getJavaCardKA() {
+ return CardUtil.getKA(ka);
+ }
+
+ public String getCurve() {
+ return curve;
+ }
+
+ public String getOneKey() {
+ return oneKey;
+ }
+
+ public String getOtherKey() {
+ return otherKey;
+ }
+
+ public String getDesc() {
+ return desc;
+ }
+
+ @Override
+ public String toString() {
+ return "<" + getId() + "> " + ka + " result over " + curve + ", " + oneKey + " + " + otherKey + (desc == null ? "" : ": " + desc);
+ }
+
+}
diff --git a/src/cz/crcs/ectester/common/ec/EC_Key.java b/src/cz/crcs/ectester/common/ec/EC_Key.java
new file mode 100644
index 0000000..a34b0e7
--- /dev/null
+++ b/src/cz/crcs/ectester/common/ec/EC_Key.java
@@ -0,0 +1,83 @@
+package cz.crcs.ectester.common.ec;
+
+import cz.crcs.ectester.applet.EC_Consts;
+
+/**
+ * An abstract-like EC key. Concrete implementations create a public and private keys.
+ *
+ * @author Jan Jancar johny@neuromancer.sk
+ */
+public class EC_Key extends EC_Params {
+
+ private String curve;
+ private String desc;
+
+ private EC_Key(short mask, String curve) {
+ super(mask);
+ this.curve = curve;
+ }
+
+ private EC_Key(short mask, String curve, String desc) {
+ this(mask, curve);
+ this.desc = desc;
+ }
+
+ private EC_Key(String id, short mask, String curve, String desc) {
+ this(mask, curve, desc);
+ this.id = id;
+ }
+
+ public String getCurve() {
+ return curve;
+ }
+
+ public String getDesc() {
+ return desc;
+ }
+
+ /**
+ * An EC public key, contains the W parameter.
+ */
+ public static class Public extends EC_Key {
+
+ public Public(String curve) {
+ super(EC_Consts.PARAMETER_W, curve);
+ }
+
+ public Public(String curve, String desc) {
+ super(EC_Consts.PARAMETER_W, curve, desc);
+ }
+
+ public Public(String id, String curve, String desc) {
+ super(id, EC_Consts.PARAMETER_W, curve, desc);
+ }
+
+ @Override
+ public String toString() {
+ return "<" + getId() + "> EC Public key, over " + getCurve() + (getDesc() == null ? "" : ": " + getDesc());
+ }
+ }
+
+ /**
+ * An EC private key, contains the S parameter.
+ */
+ public static class Private extends EC_Key {
+
+ public Private(String curve) {
+ super(EC_Consts.PARAMETER_S, curve);
+ }
+
+ public Private(String curve, String desc) {
+ super(EC_Consts.PARAMETER_S, curve, desc);
+ }
+
+ public Private(String id, String curve, String desc) {
+ super(id, EC_Consts.PARAMETER_S, curve, desc);
+ }
+
+ @Override
+ public String toString() {
+ return "<" + getId() + "> EC Private key, over " + getCurve() + (getDesc() == null ? "" : ": " + getDesc());
+ }
+ }
+}
diff --git a/src/cz/crcs/ectester/common/ec/EC_Keypair.java b/src/cz/crcs/ectester/common/ec/EC_Keypair.java
new file mode 100644
index 0000000..53632cd
--- /dev/null
+++ b/src/cz/crcs/ectester/common/ec/EC_Keypair.java
@@ -0,0 +1,41 @@
+package cz.crcs.ectester.common.ec;
+
+import cz.crcs.ectester.applet.EC_Consts;
+
+/**
+ * An EC keypair, contains both the W and S parameters.
+ *
+ * @author Jan Jancar johny@neuromancer.sk
+ */
+public class EC_Keypair extends EC_Params {
+ private String curve;
+ private String desc;
+
+ public EC_Keypair(String curve) {
+ super(EC_Consts.PARAMETERS_KEYPAIR);
+ this.curve = curve;
+ }
+
+ public EC_Keypair(String curve, String desc) {
+ this(curve);
+ this.desc = desc;
+ }
+
+ public EC_Keypair(String id, String curve, String desc) {
+ this(curve, desc);
+ this.id = id;
+ }
+
+ public String getCurve() {
+ return curve;
+ }
+
+ public String getDesc() {
+ return desc;
+ }
+
+ @Override
+ public String toString() {
+ return "<" + getId() + "> EC Keypair, over " + curve + (desc == null ? "" : ": " + desc);
+ }
+}
diff --git a/src/cz/crcs/ectester/common/ec/EC_Params.java b/src/cz/crcs/ectester/common/ec/EC_Params.java
new file mode 100644
index 0000000..1c066e7
--- /dev/null
+++ b/src/cz/crcs/ectester/common/ec/EC_Params.java
@@ -0,0 +1,194 @@
+package cz.crcs.ectester.common.ec;
+
+import cz.crcs.ectester.applet.EC_Consts;
+import cz.crcs.ectester.common.util.ByteUtil;
+
+import java.io.ByteArrayOutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A list of EC parameters, can contain a subset of the Fp/F2M, A, B, G, R, K, W, S parameters.
+ *
+ * The set of parameters is uniquely identified by a short bit string.
+ * The parameters can be exported to a byte array via <code>flatten()</code> or to a comma delimited
+ * string via <code>expand()</code>.
+ * @author Jan Jancar johny@neuromancer.sk
+ */
+public class EC_Params extends EC_Data {
+ private short params;
+
+ public EC_Params(short params) {
+ this.params = params;
+ this.count = numParams();
+ this.data = new byte[this.count][];
+ }
+
+ public EC_Params(short params, byte[][] data) {
+ this.params = params;
+ this.count = data.length;
+ this.data = data;
+ }
+
+ public EC_Params(String id, short params) {
+ this(params);
+ this.id = id;
+ }
+
+ public EC_Params(String id, short params, byte[][] data) {
+ this(params, data);
+ this.id = id;
+ }
+
+ public short getParams() {
+ return params;
+ }
+
+ public byte[][] getParam(short param) {
+ if (!hasParam(param)) {
+ return null;
+ }
+ if (Integer.bitCount(param) != 1) {
+ return null;
+ }
+ short paramMask = EC_Consts.PARAMETER_FP;
+ byte[][] result = null;
+ int i = 0;
+ while (paramMask <= EC_Consts.PARAMETER_S) {
+ short masked = (short) (this.params & param & paramMask);
+ short shallow = (short) (this.params & paramMask);
+ if (masked != 0) {
+ if (masked == EC_Consts.PARAMETER_F2M) {
+ result = new byte[4][];
+ result[0] = data[i].clone();
+ result[1] = data[i+1].clone();
+ result[2] = data[i+2].clone();
+ result[3] = data[i+3].clone();
+ break;
+ }
+ if (masked == EC_Consts.PARAMETER_G || masked == EC_Consts.PARAMETER_W) {
+ result = new byte[2][];
+ result[0] = data[i].clone();
+ result[1] = data[i+1].clone();
+ break;
+ }
+ result = new byte[1][];
+ result[0] = data[i].clone();
+ }
+ if (shallow == EC_Consts.PARAMETER_F2M) {
+ i += 4;
+ } else if (shallow == EC_Consts.PARAMETER_G || shallow == EC_Consts.PARAMETER_W) {
+ i += 2;
+ } else if (shallow != 0) {
+ i++;
+ }
+ paramMask = (short) (paramMask << 1);
+ }
+ return result;
+ }
+
+ public boolean hasParam(short param) {
+ return (params & param) != 0;
+ }
+
+ public int numParams() {
+ short paramMask = EC_Consts.PARAMETER_FP;
+ int num = 0;
+ while (paramMask <= EC_Consts.PARAMETER_S) {
+ if ((paramMask & params) != 0) {
+ if (paramMask == EC_Consts.PARAMETER_F2M) {
+ num += 3;
+ }
+ if (paramMask == EC_Consts.PARAMETER_W || paramMask == EC_Consts.PARAMETER_G) {
+ num += 1;
+ }
+ ++num;
+ }
+ paramMask = (short) (paramMask << 1);
+ }
+ return num;
+ }
+
+ @Override
+ public byte[] flatten() {
+ return flatten(params);
+ }
+
+ public byte[] flatten(short params) {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ short paramMask = EC_Consts.PARAMETER_FP;
+ int i = 0;
+ while (paramMask <= EC_Consts.PARAMETER_S) {
+ short masked = (short) (this.params & params & paramMask);
+ short shallow = (short) (this.params & paramMask);
+ if (masked != 0) {
+ byte[] param = data[i];
+ if (masked == EC_Consts.PARAMETER_F2M) {
+ //add m, e_1, e_2, e_3
+ param = ByteUtil.concatenate(param, data[i + 1]);
+ if (!ByteUtil.allValue(data[i + 2], (byte) 0)) {
+ param = ByteUtil.concatenate(param, data[i + 2]);
+ }
+ if (!ByteUtil.allValue(data[i + 3], (byte) 0)) {
+ param = ByteUtil.concatenate(param, data[i + 3]);
+ }
+ if (!(param.length == 4 || param.length == 8))
+ throw new RuntimeException("PARAMETER_F2M length is not 8.(should be)");
+ }
+ if (masked == EC_Consts.PARAMETER_G || masked == EC_Consts.PARAMETER_W) {
+ //read another param (the y coord) and put into X962 format.
+ byte[] y = data[i + 1];
+ param = ByteUtil.concatenate(new byte[]{4}, param, y); //<- ugly but works!
+ }
+ if (param.length == 0)
+ throw new RuntimeException("Empty parameter read?");
+
+ //write length
+ byte[] length = new byte[2];
+ ByteUtil.setShort(length, 0, (short) param.length);
+ out.write(length, 0, 2);
+ //write data
+ out.write(param, 0, param.length);
+ }
+ if (shallow == EC_Consts.PARAMETER_F2M) {
+ i += 4;
+ } else if (shallow == EC_Consts.PARAMETER_G || shallow == EC_Consts.PARAMETER_W) {
+ i += 2;
+ } else if (shallow != 0) {
+ i++;
+ }
+ paramMask = (short) (paramMask << 1);
+ }
+
+ return (out.size() == 0) ? null : out.toByteArray();
+ }
+
+ @Override
+ public String[] expand() {
+ List<String> out = new ArrayList<>();
+
+ short paramMask = EC_Consts.PARAMETER_FP;
+ int index = 0;
+ while (paramMask <= EC_Consts.PARAMETER_S) {
+ short masked = (short) (params & paramMask);
+ if (masked != 0) {
+ byte[] param = data[index];
+ if (masked == EC_Consts.PARAMETER_F2M) {
+ for (int i = 0; i < 4; ++i) {
+ out.add(ByteUtil.bytesToHex(data[index + i], false));
+ }
+ index += 4;
+ } else if (masked == EC_Consts.PARAMETER_G || masked == EC_Consts.PARAMETER_W) {
+ out.add(ByteUtil.bytesToHex(param, false));
+ out.add(ByteUtil.bytesToHex(data[index + 1], false));
+ index += 2;
+ } else {
+ out.add(ByteUtil.bytesToHex(param, false));
+ index++;
+ }
+ }
+ paramMask = (short) (paramMask << 1);
+ }
+ return out.toArray(new String[out.size()]);
+ }
+}