diff options
Diffstat (limited to 'src')
72 files changed, 5424 insertions, 465 deletions
diff --git a/src/cz/crcs/ectester/common/cli/Argument.java b/src/cz/crcs/ectester/common/cli/Argument.java new file mode 100644 index 0000000..e9b6688 --- /dev/null +++ b/src/cz/crcs/ectester/common/cli/Argument.java @@ -0,0 +1,29 @@ +package cz.crcs.ectester.common.cli; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class Argument { + private String name; + private String desc; + private boolean required; + + public Argument(String name, String desc, boolean isRequired) { + this.name = name; + this.desc = desc; + this.required = isRequired; + } + + + public String getName() { + return name; + } + + public String getDesc() { + return desc; + } + + public boolean isRequired() { + return required; + } +} diff --git a/src/cz/crcs/ectester/common/cli/CLITools.java b/src/cz/crcs/ectester/common/cli/CLITools.java new file mode 100644 index 0000000..91f121f --- /dev/null +++ b/src/cz/crcs/ectester/common/cli/CLITools.java @@ -0,0 +1,140 @@ +package cz.crcs.ectester.common.cli; + +import cz.crcs.ectester.common.ec.EC_Category; +import cz.crcs.ectester.common.ec.EC_Data; +import cz.crcs.ectester.data.EC_Store; +import org.apache.commons.cli.CommandLineParser; +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.Options; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.Map; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class CLITools { + + /** + * Print help. + */ + public static void help(String prog, String header, Options options, String footer, boolean usage) { + HelpFormatter help = new HelpFormatter(); + help.setOptionComparator(null); + help.printHelp(prog, header, options, footer, usage); + } + + private static void help(HelpFormatter help, PrintWriter pw, CommandLineParser cli, Options opts, int depth) { + if (opts.getOptions().size() > 0) { + help.printOptions(pw, HelpFormatter.DEFAULT_WIDTH, opts, HelpFormatter.DEFAULT_LEFT_PAD + depth, HelpFormatter.DEFAULT_DESC_PAD); + } + if (cli instanceof TreeParser) { + TreeParser tp = (TreeParser) cli; + for (Argument arg : tp.getArgs()) { + String argname = arg.isRequired() ? "<" + arg.getName() + ">" : "[" + arg.getName() + "]"; + help.printWrapped(pw, HelpFormatter.DEFAULT_WIDTH, String.format("%" + (depth + 1) + "s" + argname + " " + arg.getDesc(), " ")); + } + tp.getParsers().forEach((key, value) -> { + pw.println(); + help.printWrapped(pw, HelpFormatter.DEFAULT_WIDTH, String.format("%" + depth + "s" + key + ":", " ")); + CLITools.help(help, pw, value.getParser(), value.getOptions(), depth + 1); + }); + } + } + + private static void usage(HelpFormatter help, PrintWriter pw, CommandLineParser cli, Options opts) { + StringWriter sw = new StringWriter(); + PrintWriter upw = new PrintWriter(sw); + help.printUsage(upw, HelpFormatter.DEFAULT_WIDTH, "", opts); + if (cli instanceof TreeParser) { + upw.print(" "); + TreeParser tp = (TreeParser) cli; + String[] keys = tp.getParsers().keySet().toArray(new String[tp.getParsers().size()]); + if (keys.length > 0 && !tp.isRequired()) { + upw.print("[ "); + } + + for (int i = 0; i < keys.length; ++i) { + String key = keys[i]; + ParserOptions value = tp.getParsers().get(key); + upw.print("(" + key); + usage(help, upw, value.getParser(), value.getOptions()); + upw.print(")"); + if (i != keys.length - 1) { + upw.print(" | "); + } + } + + if (keys.length > 0 && !tp.isRequired()) { + upw.print(" ]"); + } + + Argument[] args = tp.getArgs().toArray(new Argument[tp.getArgs().size()]); + if (args.length > 0) { + String[] argss = new String[tp.getArgs().size()]; + for (int i = 0; i < args.length; ++i) { + Argument arg = args[i]; + argss[i] = arg.isRequired() ? "<" + arg.getName() + ">" : "[" + arg.getName() + "]"; + } + upw.print(" " + String.join(" ", argss)); + } + } + pw.println(sw.toString().replaceAll("usage:( )?", "").replace("\n", "")); + } + + /** + * Print tree help. + */ + public static void help(String prog, String header, Options baseOpts, TreeParser baseParser, String footer, boolean printUsage) { + HelpFormatter help = new HelpFormatter(); + help.setOptionComparator(null); + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + help.printWrapped(pw, HelpFormatter.DEFAULT_WIDTH, header); + if (printUsage) { + StringWriter uw = new StringWriter(); + PrintWriter upw = new PrintWriter(uw); + usage(help, upw, baseParser, baseOpts); + pw.print("usage: " + prog); + help.printWrapped(pw, HelpFormatter.DEFAULT_WIDTH, uw.toString()); + upw.close(); + pw.println(); + } + help(help, pw, baseParser, baseOpts, 1); + help.printWrapped(pw, HelpFormatter.DEFAULT_WIDTH, footer); + System.out.println(sw.toString()); + } + + /** + * Print version info. + */ + public static void version(String description, String license) { + System.out.println(description); + System.out.println(license); + } + + /** + * List categories and named curves. + */ + public static void listNamed(EC_Store dataStore, String named) { + Map<String, EC_Category> categories = dataStore.getCategories(); + if (named == null) { + // print all categories, briefly + for (EC_Category cat : categories.values()) { + System.out.println(cat); + } + } else if (categories.containsKey(named)) { + // print given category + System.out.println(categories.get(named)); + } else { + // print given object + EC_Data object = dataStore.getObject(EC_Data.class, named); + if (object != null) { + System.out.println(object); + } else { + System.err.println("Named object " + named + " not found!"); + } + } + } +} diff --git a/src/cz/crcs/ectester/common/cli/ParserOptions.java b/src/cz/crcs/ectester/common/cli/ParserOptions.java new file mode 100644 index 0000000..ee2097e --- /dev/null +++ b/src/cz/crcs/ectester/common/cli/ParserOptions.java @@ -0,0 +1,38 @@ +package cz.crcs.ectester.common.cli; + +import org.apache.commons.cli.CommandLineParser; +import org.apache.commons.cli.Options; + +import java.util.Collections; +import java.util.List; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class ParserOptions { + private CommandLineParser parser; + private Options options; + private List<Argument> arguments; + + public ParserOptions(CommandLineParser parser, Options options) { + this.parser = parser; + this.options = options; + } + + public ParserOptions(CommandLineParser parser, Options options, List<Argument> arguments) { + this(parser, options); + this.arguments = arguments; + } + + public CommandLineParser getParser() { + return parser; + } + + public Options getOptions() { + return options; + } + + public List<Argument> getArguments() { + return Collections.unmodifiableList(arguments); + } +} diff --git a/src/cz/crcs/ectester/common/cli/TreeCommandLine.java b/src/cz/crcs/ectester/common/cli/TreeCommandLine.java new file mode 100644 index 0000000..6a044d2 --- /dev/null +++ b/src/cz/crcs/ectester/common/cli/TreeCommandLine.java @@ -0,0 +1,178 @@ +package cz.crcs.ectester.common.cli; + +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.Option; +import org.apache.commons.cli.ParseException; + +import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import java.util.Properties; +import java.util.function.BiFunction; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class TreeCommandLine extends CommandLine { + private String name = ""; + private TreeCommandLine next; + private CommandLine cli; + + public TreeCommandLine(CommandLine cli, TreeCommandLine next) { + this.cli = cli; + this.next = next; + } + + public TreeCommandLine(String name, CommandLine cli, TreeCommandLine next) { + this(cli, next); + this.name = name; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public String getNextName() { + if (next != null) { + return next.getName(); + } + return null; + } + + public TreeCommandLine getNext() { + return next; + } + + public boolean isNext(String next) { + return Objects.equals(getNextName(), next); + } + + public CommandLine getThis() { + return cli; + } + + public int getDepth() { + if (next == null) { + return 0; + } + return next.getDepth() + 1; + } + + private <T> T getOption(String opt, BiFunction<CommandLine, String, T> getter, T defaultValue) { + if (opt.contains(".")) { + String[] parts = opt.split("\\.", 2); + if (next != null && parts[0].equals(next.getName())) { + T result = getter.apply(next, parts[1]); + if (result != null) + return result; + return defaultValue; + } + return defaultValue; + } + return getter.apply(cli, opt); + } + + @Override + public boolean hasOption(String opt) { + return getOption(opt, CommandLine::hasOption, false); + } + + @Override + public boolean hasOption(char opt) { + return cli.hasOption(opt); + } + + @Override + public Object getParsedOptionValue(String opt) throws ParseException { + if (opt.contains(".")) { + String[] parts = opt.split("\\.", 2); + if (next != null && parts[0].equals(next.getName())) { + return next.getParsedOptionValue(parts[1]); + } + return null; + } + return cli.getParsedOptionValue(opt); + } + + @Override + public Object getOptionObject(char opt) { + return cli.getOptionObject(opt); + } + + @Override + public String getOptionValue(String opt) { + return getOption(opt, CommandLine::getOptionValue, null); + } + + @Override + public String getOptionValue(char opt) { + return cli.getOptionValue(opt); + } + + @Override + public String[] getOptionValues(String opt) { + return getOption(opt, CommandLine::getOptionValues, null); + } + + @Override + public String[] getOptionValues(char opt) { + return cli.getOptionValues(opt); + } + + @Override + public String getOptionValue(String opt, String defaultValue) { + return getOption(opt, CommandLine::getOptionValue, defaultValue); + } + + @Override + public String getOptionValue(char opt, String defaultValue) { + return cli.getOptionValue(opt, defaultValue); + } + + @Override + public Properties getOptionProperties(String opt) { + return getOption(opt, CommandLine::getOptionProperties, new Properties()); + } + + @Override + public Iterator<Option> iterator() { + return cli.iterator(); + } + + @Override + public Option[] getOptions() { + return cli.getOptions(); + } + + public boolean hasArg(int index) { + return getArg(index) != null; + } + + public String getArg(int index) { + if (next != null) { + return next.getArg(index); + } + String[] args = cli.getArgs(); + if (index >= args.length) { + return null; + } + if (index < 0 && -index > args.length) { + return null; + } + return index < 0 ? args[args.length + index] : args[index]; + } + + @Override + public String[] getArgs() { + return cli.getArgs(); + } + + @Override + public List<String> getArgList() { + return cli.getArgList(); + } +} diff --git a/src/cz/crcs/ectester/common/cli/TreeParser.java b/src/cz/crcs/ectester/common/cli/TreeParser.java new file mode 100644 index 0000000..77cce30 --- /dev/null +++ b/src/cz/crcs/ectester/common/cli/TreeParser.java @@ -0,0 +1,120 @@ +package cz.crcs.ectester.common.cli; + +import org.apache.commons.cli.*; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class TreeParser implements CommandLineParser { + private Map<String, ParserOptions> parsers; + private boolean required; + private List<Argument> args = Collections.emptyList(); + + public TreeParser(Map<String, ParserOptions> parsers, boolean required) { + this.parsers = parsers; + this.required = required; + } + + public TreeParser(Map<String, ParserOptions> parsers, boolean required, List<Argument> args) { + this(parsers, required); + this.args = args; + } + + public Map<String, ParserOptions> getParsers() { + return Collections.unmodifiableMap(parsers); + } + + public boolean isRequired() { + return required; + } + + public List<Argument> getArgs() { + return Collections.unmodifiableList(args); + } + + @Override + public TreeCommandLine parse(Options options, String[] arguments) throws ParseException { + return this.parse(options, arguments, null); + } + + public TreeCommandLine parse(Options options, String[] arguments, Properties properties) throws ParseException { + return this.parse(options, arguments, properties, false); + } + + @Override + public TreeCommandLine parse(Options options, String[] arguments, boolean stopAtNonOption) throws ParseException { + return this.parse(options, arguments, null, stopAtNonOption); + } + + public TreeCommandLine parse(Options options, String[] arguments, Properties properties, boolean stopAtNonOption) throws ParseException { + DefaultParser thisParser = new DefaultParser(); + CommandLine cli = thisParser.parse(options, arguments, properties, true); + + CommandLine subCli = null; + String[] cliArgs = cli.getArgs(); + String sub = null; + if (cliArgs.length != 0) { + sub = cliArgs[0]; + + List<String> matches = new LinkedList<>(); + String finalSub = sub; + for (Map.Entry<String, ParserOptions> entry : parsers.entrySet()) { + if (entry.getKey().equalsIgnoreCase(finalSub)) { + matches.clear(); + matches.add(finalSub); + break; + } else if (entry.getKey().startsWith(finalSub)) { + matches.add(entry.getKey()); + } + } + + if (matches.size() == 1) { + sub = matches.get(0); + ParserOptions subparser = parsers.get(sub); + String[] remainingArgs = new String[cliArgs.length - 1]; + System.arraycopy(cliArgs, 1, remainingArgs, 0, cliArgs.length - 1); + subCli = subparser.getParser().parse(subparser.getOptions(), remainingArgs, true); + } else if (matches.size() > 1) { + throw new AmbiguousOptionException(sub, matches); + } + } else { + if (required) { + throw new MissingOptionException(new ArrayList(parsers.keySet())); + } + } + + long requiredArgs = args.stream().filter(Argument::isRequired).count(); + String reqArgs = String.join(" ", args.stream().filter(Argument::isRequired).map(Argument::getName).collect(Collectors.toList())); + + if (subCli instanceof TreeCommandLine) { + TreeCommandLine subTreeCli = (TreeCommandLine) subCli; + + TreeCommandLine lastCli = subTreeCli; + while (lastCli.getNext() != null) { + lastCli = lastCli.getNext(); + } + + if (lastCli.getArgs().length < requiredArgs) { + throw new MissingArgumentException("Not enough arguments: " + reqArgs); + } + + subTreeCli.setName(sub); + return new TreeCommandLine(cli, subTreeCli); + } else if (subCli != null) { + if (subCli.getArgs().length < requiredArgs) { + throw new MissingArgumentException("Not enough arguments: " + reqArgs); + } + + TreeCommandLine subTreeCli = new TreeCommandLine(sub, subCli, null); + return new TreeCommandLine(cli, subTreeCli); + } else { + if (cliArgs.length < requiredArgs) { + throw new MissingArgumentException("Not enough arguments: " + reqArgs); + } + return new TreeCommandLine(cli, null); + } + } +} diff --git a/src/cz/crcs/ectester/common/ec/EC_Curve.java b/src/cz/crcs/ectester/common/ec/EC_Curve.java index 19cfe2d..173fd29 100644 --- a/src/cz/crcs/ectester/common/ec/EC_Curve.java +++ b/src/cz/crcs/ectester/common/ec/EC_Curve.java @@ -1,8 +1,12 @@ 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)?. * @@ -49,4 +53,80 @@ public class EC_Curve extends EC_Params { 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 index acd282a..c048ef7 100644 --- a/src/cz/crcs/ectester/common/ec/EC_Data.java +++ b/src/cz/crcs/ectester/common/ec/EC_Data.java @@ -1,6 +1,6 @@ package cz.crcs.ectester.common.ec; -import cz.crcs.ectester.common.Util; +import cz.crcs.ectester.common.util.ByteUtil; import java.io.*; import java.util.*; @@ -8,9 +8,10 @@ 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 { @@ -67,7 +68,7 @@ public abstract class EC_Data { ByteArrayOutputStream out = new ByteArrayOutputStream(); for (byte[] param : data) { byte[] length = new byte[2]; - Util.setShort(length, 0, (short) param.length); + ByteUtil.setShort(length, 0, (short) param.length); out.write(length, 0, 2); out.write(param, 0, param.length); @@ -79,7 +80,7 @@ public abstract class EC_Data { public String[] expand() { List<String> out = new ArrayList<>(count); for (byte[] param : data) { - out.add(Util.bytesToHex(param, false)); + out.add(ByteUtil.bytesToHex(param, false)); } return out.toArray(new String[out.size()]); @@ -97,9 +98,9 @@ public abstract class EC_Data { private static byte[] parse(String param) { byte[] data; if (param.startsWith("0x") || param.startsWith("0X")) { - data = Util.hexToBytes(param.substring(2)); + data = ByteUtil.hexToBytes(param.substring(2)); } else { - data = Util.hexToBytes(param); + data = ByteUtil.hexToBytes(param); } if (data == null) return new byte[0]; @@ -141,12 +142,16 @@ public abstract class EC_Data { } 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 = Util.getShort(bytes, offset); + short paramLength = ByteUtil.getShort(bytes, offset); offset += 2; if (bytes.length < offset + paramLength) { return false; @@ -158,6 +163,18 @@ public abstract class EC_Data { 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())); diff --git a/src/cz/crcs/ectester/common/ec/EC_KAResult.java b/src/cz/crcs/ectester/common/ec/EC_KAResult.java index 3b74c57..a7b3cd5 100644 --- a/src/cz/crcs/ectester/common/ec/EC_KAResult.java +++ b/src/cz/crcs/ectester/common/ec/EC_KAResult.java @@ -1,6 +1,6 @@ package cz.crcs.ectester.common.ec; -import cz.crcs.ectester.common.Util; +import cz.crcs.ectester.common.util.CardUtil; /** * A result of EC based Key agreement operation. @@ -56,7 +56,7 @@ public class EC_KAResult extends EC_Data { @Override public String toString() { - String algo = Util.getKA(ka); + String algo = CardUtil.getKA(ka); return "<" + getId() + "> " + algo + " result over " + curve + ", " + oneKey + " + " + otherKey + (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 index 3fada93..1c066e7 100644 --- a/src/cz/crcs/ectester/common/ec/EC_Params.java +++ b/src/cz/crcs/ectester/common/ec/EC_Params.java @@ -1,7 +1,7 @@ package cz.crcs.ectester.common.ec; import cz.crcs.ectester.applet.EC_Consts; -import cz.crcs.ectester.common.Util; +import cz.crcs.ectester.common.util.ByteUtil; import java.io.ByteArrayOutputStream; import java.util.ArrayList; @@ -125,12 +125,12 @@ public class EC_Params extends EC_Data { byte[] param = data[i]; if (masked == EC_Consts.PARAMETER_F2M) { //add m, e_1, e_2, e_3 - param = Util.concatenate(param, data[i + 1]); - if (!Util.allValue(data[i + 2], (byte) 0)) { - param = Util.concatenate(param, data[i + 2]); + param = ByteUtil.concatenate(param, data[i + 1]); + if (!ByteUtil.allValue(data[i + 2], (byte) 0)) { + param = ByteUtil.concatenate(param, data[i + 2]); } - if (!Util.allValue(data[i + 3], (byte) 0)) { - param = Util.concatenate(param, data[i + 3]); + 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)"); @@ -138,14 +138,14 @@ public class EC_Params extends EC_Data { 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 = Util.concatenate(new byte[]{4}, param, y); //<- ugly but works! + 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]; - Util.setShort(length, 0, (short) param.length); + ByteUtil.setShort(length, 0, (short) param.length); out.write(length, 0, 2); //write data out.write(param, 0, param.length); @@ -175,15 +175,15 @@ public class EC_Params extends EC_Data { byte[] param = data[index]; if (masked == EC_Consts.PARAMETER_F2M) { for (int i = 0; i < 4; ++i) { - out.add(Util.bytesToHex(data[index + i], false)); + out.add(ByteUtil.bytesToHex(data[index + i], false)); } index += 4; } else if (masked == EC_Consts.PARAMETER_G || masked == EC_Consts.PARAMETER_W) { - out.add(Util.bytesToHex(param, false)); - out.add(Util.bytesToHex(data[index + 1], false)); + out.add(ByteUtil.bytesToHex(param, false)); + out.add(ByteUtil.bytesToHex(data[index + 1], false)); index += 2; } else { - out.add(Util.bytesToHex(param, false)); + out.add(ByteUtil.bytesToHex(param, false)); index++; } } diff --git a/src/cz/crcs/ectester/reader/output/TestWriter.java b/src/cz/crcs/ectester/common/output/TestWriter.java index d79252d..0ecfd5a 100644 --- a/src/cz/crcs/ectester/reader/output/TestWriter.java +++ b/src/cz/crcs/ectester/common/output/TestWriter.java @@ -1,7 +1,7 @@ -package cz.crcs.ectester.reader.output; +package cz.crcs.ectester.common.output; import cz.crcs.ectester.common.test.Test; -import cz.crcs.ectester.reader.test.TestSuite; +import cz.crcs.ectester.common.test.TestSuite; /** * @author Jan Jancar johny@neuromancer.sk diff --git a/src/cz/crcs/ectester/common/output/TestableWriter.java b/src/cz/crcs/ectester/common/output/TestableWriter.java new file mode 100644 index 0000000..9876064 --- /dev/null +++ b/src/cz/crcs/ectester/common/output/TestableWriter.java @@ -0,0 +1,65 @@ +package cz.crcs.ectester.common.output; + +import cz.crcs.ectester.common.test.BaseTestable; +import cz.crcs.ectester.reader.output.ResponseWriter; +import cz.crcs.ectester.reader.response.Response; +import cz.crcs.ectester.reader.test.CommandTestable; +import cz.crcs.ectester.standalone.test.KeyAgreementTestable; +import cz.crcs.ectester.standalone.test.KeyGeneratorTestable; +import cz.crcs.ectester.standalone.test.SignatureTestable; + +import java.io.OutputStream; +import java.io.PrintStream; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class TestableWriter { + private PrintStream output; + private ResponseWriter respWriter; + + public TestableWriter(PrintStream output) { + this.output = output; + this.respWriter = new ResponseWriter(output); + } + + public TestableWriter(OutputStream output) { + this(new PrintStream(output)); + } + + public String outputTestableSuffix(BaseTestable t) { + if (t instanceof CommandTestable) { + Response r = ((CommandTestable) t).getResponse(); + return respWriter.responseSuffix(r); + } else if (t instanceof KeyAgreementTestable) { + + } else if (t instanceof KeyGeneratorTestable) { + + } else if (t instanceof SignatureTestable) { + + } + return null; + } + + public void writeTestableSuffix(BaseTestable t) { + output.println(outputTestableSuffix(t)); + } + + public String outputTestable(BaseTestable t) { + if (t instanceof CommandTestable) { + CommandTestable testable = (CommandTestable) t; + return respWriter.responseString(testable.getResponse()); + } else if (t instanceof KeyAgreementTestable) { + + } else if (t instanceof KeyGeneratorTestable) { + + } else if (t instanceof SignatureTestable) { + + } + return null; + } + + public void writeTestable(BaseTestable t) { + output.println(outputTestable(t)); + } +} diff --git a/src/cz/crcs/ectester/reader/output/TextTestWriter.java b/src/cz/crcs/ectester/common/output/TextTestWriter.java index 07b2a2f..2691ccb 100644 --- a/src/cz/crcs/ectester/reader/output/TextTestWriter.java +++ b/src/cz/crcs/ectester/common/output/TextTestWriter.java @@ -1,9 +1,9 @@ -package cz.crcs.ectester.reader.output; +package cz.crcs.ectester.common.output; import cz.crcs.ectester.common.test.CompoundTest; +import cz.crcs.ectester.common.test.SimpleTest; import cz.crcs.ectester.common.test.Test; -import cz.crcs.ectester.reader.test.SimpleTest; -import cz.crcs.ectester.reader.test.TestSuite; +import cz.crcs.ectester.common.test.TestSuite; import java.io.PrintStream; @@ -12,13 +12,13 @@ import java.io.PrintStream; */ public class TextTestWriter implements TestWriter { private PrintStream output; - private ResponseWriter respWriter; + private TestableWriter testableWriter; public static int BASE_WIDTH = 76; public TextTestWriter(PrintStream output) { this.output = output; - this.respWriter = new ResponseWriter(output); + this.testableWriter = new TestableWriter(output); } @Override @@ -33,27 +33,17 @@ public class TextTestWriter implements TestWriter { } StringBuilder out = new StringBuilder(); - if (t instanceof SimpleTest) { - SimpleTest test = (SimpleTest) t; - out.append(test.ok() ? "OK " : "NOK "); - out.append("━ "); - int width = BASE_WIDTH - (offset + out.length()); - String widthSpec = "%-" + String.valueOf(width) + "s"; - out.append(String.format(widthSpec, t.getDescription())); - out.append(" ┃ "); - out.append(String.format("%-9s", test.getResultValue().name())); - out.append(" ┃ "); - out.append(respWriter.responseSuffix(test.getResponse())); - } else { + out.append(t.ok() ? "OK " : "NOK "); + out.append("━ "); + int width = BASE_WIDTH - (offset + out.length()); + String widthSpec = "%-" + String.valueOf(width) + "s"; + out.append(String.format(widthSpec, t.getDescription())); + out.append(" ┃ "); + out.append(String.format("%-9s", t.getResultValue().name())); + out.append(" ┃ "); + + if (t instanceof CompoundTest) { CompoundTest test = (CompoundTest) t; - out.append(test.ok() ? "OK " : "NOK "); - out.append("┳ "); - int width = BASE_WIDTH - (offset + out.length()); - String widthSpec = "%-" + String.valueOf(width) + "s"; - out.append(String.format(widthSpec, t.getDescription())); - out.append(" ┃ "); - out.append(String.format("%-9s", test.getResultValue().name())); - out.append(" ┃ "); out.append(test.getResultCause()); out.append(System.lineSeparator()); Test[] tests = test.getTests(); @@ -68,8 +58,10 @@ public class TextTestWriter implements TestWriter { out.append(System.lineSeparator()); } } + } else { + SimpleTest test = (SimpleTest) t; + out.append(testableWriter.outputTestableSuffix(test.getTestable())); } - return out.toString(); } diff --git a/src/cz/crcs/ectester/reader/output/XMLTestWriter.java b/src/cz/crcs/ectester/common/output/XMLTestWriter.java index 24a308c..4139330 100644 --- a/src/cz/crcs/ectester/reader/output/XMLTestWriter.java +++ b/src/cz/crcs/ectester/common/output/XMLTestWriter.java @@ -1,12 +1,13 @@ -package cz.crcs.ectester.reader.output; +package cz.crcs.ectester.common.output; import cz.crcs.ectester.common.test.CompoundTest; -import cz.crcs.ectester.common.Util; +import cz.crcs.ectester.common.test.Test; +import cz.crcs.ectester.common.test.TestSuite; +import cz.crcs.ectester.common.util.ByteUtil; import cz.crcs.ectester.reader.command.Command; import cz.crcs.ectester.reader.response.Response; -import cz.crcs.ectester.common.test.Test; -import cz.crcs.ectester.reader.test.SimpleTest; -import cz.crcs.ectester.reader.test.TestSuite; +import cz.crcs.ectester.reader.test.CommandTest; +import cz.crcs.ectester.standalone.test.*; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; @@ -21,6 +22,8 @@ import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import java.io.OutputStream; +import java.security.PrivateKey; +import java.security.PublicKey; /** * @author Jan Jancar johny@neuromancer.sk @@ -51,7 +54,7 @@ public class XMLTestWriter implements TestWriter { Element commandElem = doc.createElement("command"); Element apdu = doc.createElement("apdu"); - apdu.setTextContent(Util.bytesToHex(c.getAPDU().getBytes())); + apdu.setTextContent(ByteUtil.bytesToHex(c.getAPDU().getBytes())); commandElem.appendChild(apdu); return commandElem; @@ -62,7 +65,7 @@ public class XMLTestWriter implements TestWriter { responseElem.setAttribute("successful", r.successful() ? "true" : "false"); Element apdu = doc.createElement("apdu"); - apdu.setTextContent(Util.bytesToHex(r.getAPDU().getBytes())); + apdu.setTextContent(ByteUtil.bytesToHex(r.getAPDU().getBytes())); responseElem.appendChild(apdu); Element naturalSW = doc.createElement("natural-sw"); @@ -88,14 +91,68 @@ public class XMLTestWriter implements TestWriter { return responseElem; } + private Element kaElement(KeyAgreementTestable kat) { + Element katElem = doc.createElement("key-agreement"); + + Element secret = doc.createElement("secret"); + secret.setTextContent(ByteUtil.bytesToHex(kat.getSecret())); + katElem.appendChild(secret); + + return katElem; + } + + private Element kgtElement(KeyGeneratorTestable kgt) { + Element kgtElem = doc.createElement("key-pair-generator"); + + Element keyPair = doc.createElement("key-pair"); + Element pubkey = doc.createElement("pubkey"); + PublicKey pkey = kgt.getKeyPair().getPublic(); + pubkey.setAttribute("algorithm", pkey.getAlgorithm()); + pubkey.setAttribute("format", pkey.getFormat()); + pubkey.setTextContent(ByteUtil.bytesToHex(pkey.getEncoded())); + keyPair.appendChild(pubkey); + + Element privkey = doc.createElement("privkey"); + PrivateKey skey = kgt.getKeyPair().getPrivate(); + privkey.setAttribute("algorithm", skey.getAlgorithm()); + privkey.setAttribute("format", skey.getFormat()); + privkey.setTextContent(ByteUtil.bytesToHex(skey.getEncoded())); + keyPair.appendChild(privkey); + + return kgtElem; + } + + private Element sigElement(SignatureTestable sig) { + Element sigElem = doc.createElement("signature"); + sigElem.setAttribute("verified", sig.getVerified() ? "true" : "false"); + + Element raw = doc.createElement("raw"); + raw.setTextContent(ByteUtil.bytesToHex(sig.getSignature())); + sigElem.appendChild(raw); + + return sigElem; + } + private Element testElement(Test t) { Element testElem = doc.createElement("test"); - if (t instanceof SimpleTest) { - SimpleTest test = (SimpleTest) t; - testElem.setAttribute("type", "simple"); + if (t instanceof CommandTest) { + CommandTest test = (CommandTest) t; + testElem.setAttribute("type", "command"); testElem.appendChild(commandElement(test.getCommand())); testElem.appendChild(responseElement(test.getResponse())); + } else if (t instanceof KeyAgreementTest) { + KeyAgreementTest test = (KeyAgreementTest) t; + testElem.setAttribute("type", "key-agreement"); + testElem.appendChild(kaElement(test.getTestable())); + } else if (t instanceof KeyGeneratorTest) { + KeyGeneratorTest test = (KeyGeneratorTest) t; + testElem.setAttribute("type", "key-pair-generator"); + testElem.appendChild(kgtElement(test.getTestable())); + } else if (t instanceof SignatureTest) { + SignatureTest test = (SignatureTest) t; + testElem.setAttribute("type", "signature"); + testElem.appendChild(sigElement(test.getTestable())); } else if (t instanceof CompoundTest) { CompoundTest test = (CompoundTest) t; testElem.setAttribute("type", "compound"); diff --git a/src/cz/crcs/ectester/reader/output/YAMLTestWriter.java b/src/cz/crcs/ectester/common/output/YAMLTestWriter.java index c637a13..ba9fa43 100644 --- a/src/cz/crcs/ectester/reader/output/YAMLTestWriter.java +++ b/src/cz/crcs/ectester/common/output/YAMLTestWriter.java @@ -1,16 +1,19 @@ -package cz.crcs.ectester.reader.output; +package cz.crcs.ectester.common.output; import cz.crcs.ectester.common.test.CompoundTest; -import cz.crcs.ectester.common.Util; +import cz.crcs.ectester.common.test.Test; +import cz.crcs.ectester.common.test.TestSuite; +import cz.crcs.ectester.common.util.ByteUtil; import cz.crcs.ectester.reader.command.Command; import cz.crcs.ectester.reader.response.Response; -import cz.crcs.ectester.common.test.Test; -import cz.crcs.ectester.reader.test.SimpleTest; -import cz.crcs.ectester.reader.test.TestSuite; +import cz.crcs.ectester.reader.test.CommandTest; +import cz.crcs.ectester.standalone.test.*; import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.Yaml; import java.io.PrintStream; +import java.security.PrivateKey; +import java.security.PublicKey; import java.util.HashMap; import java.util.LinkedList; import java.util.List; @@ -44,14 +47,14 @@ public class YAMLTestWriter implements TestWriter { private Map<String, Object> commandObject(Command c) { Map<String, Object> commandObj = new HashMap<>(); - commandObj.put("apdu", Util.bytesToHex(c.getAPDU().getBytes())); + commandObj.put("apdu", ByteUtil.bytesToHex(c.getAPDU().getBytes())); return commandObj; } private Map<String, Object> responseObject(Response r) { Map<String, Object> responseObj = new HashMap<>(); responseObj.put("successful", r.successful()); - responseObj.put("apdu", Util.bytesToHex(r.getAPDU().getBytes())); + responseObj.put("apdu", ByteUtil.bytesToHex(r.getAPDU().getBytes())); responseObj.put("natural_sw", Short.toUnsignedInt(r.getNaturalSW())); List<Integer> sws = new LinkedList<>(); for (int i = 0; i < r.getNumSW(); ++i) { @@ -63,14 +66,57 @@ public class YAMLTestWriter implements TestWriter { return responseObj; } + private Map<String, Object> kaObject(KeyAgreementTestable kat) { + Map<String, Object> katObject = new HashMap<>(); + katObject.put("secret", ByteUtil.bytesToHex(kat.getSecret())); + return katObject; + } + + private Map<String, Object> kgtObject(KeyGeneratorTestable kgt) { + Map<String, Object> kgtObject = new HashMap<>(); + Map<String, Object> pubObject = new HashMap<>(); + PublicKey pkey = kgt.getKeyPair().getPublic(); + pubObject.put("algorithm", pkey.getAlgorithm()); + pubObject.put("format", pkey.getFormat()); + pubObject.put("raw", ByteUtil.bytesToHex(pkey.getEncoded())); + kgtObject.put("pubkey", pubObject); + + Map<String, Object> privObject = new HashMap<>(); + PrivateKey skey = kgt.getKeyPair().getPrivate(); + privObject.put("algorithm", skey.getAlgorithm()); + privObject.put("format", skey.getFormat()); + privObject.put("raw", ByteUtil.bytesToHex(skey.getEncoded())); + kgtObject.put("privkey", privObject); + return kgtObject; + } + + private Map<String, Object> sigObject(SignatureTestable sig) { + Map<String, Object> sigObject = new HashMap<>(); + sigObject.put("verified", sig.getVerified()); + sigObject.put("raw", ByteUtil.bytesToHex(sig.getSignature())); + return sigObject; + } + private Map<String, Object> testObject(Test t) { Map<String, Object> testObj = new HashMap<>(); - if (t instanceof SimpleTest) { - SimpleTest test = (SimpleTest) t; - testObj.put("type", "simple"); + if (t instanceof CommandTest) { + CommandTest test = (CommandTest) t; + testObj.put("type", "command"); testObj.put("command", commandObject(test.getCommand())); testObj.put("response", responseObject(test.getResponse())); + } else if (t instanceof KeyAgreementTest) { + KeyAgreementTest test = (KeyAgreementTest) t; + testObj.put("type", "key-agreement"); + testObj.put("key-agreement", kaObject(test.getTestable())); + } else if (t instanceof KeyGeneratorTest) { + KeyGeneratorTest test = (KeyGeneratorTest) t; + testObj.put("type", "key-pair-generator"); + testObj.put("key-pair-generator", kgtObject(test.getTestable())); + } else if (t instanceof SignatureTest) { + SignatureTest test = (SignatureTest) t; + testObj.put("type", "signature"); + testObj.put("signature", sigObject(test.getTestable())); } else if (t instanceof CompoundTest) { CompoundTest test = (CompoundTest) t; testObj.put("type", "compound"); diff --git a/src/cz/crcs/ectester/common/test/BaseTestable.java b/src/cz/crcs/ectester/common/test/BaseTestable.java new file mode 100644 index 0000000..f8ebf48 --- /dev/null +++ b/src/cz/crcs/ectester/common/test/BaseTestable.java @@ -0,0 +1,36 @@ +package cz.crcs.ectester.common.test; + +import java.util.Collections; +import java.util.Map; +import java.util.TreeMap; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public abstract class BaseTestable implements Testable { + protected boolean hasRun; + protected boolean ok; + protected boolean error; + + protected Map<String, Object> meta = new TreeMap<>(); + + @Override + public boolean hasRun() { + return hasRun; + } + + @Override + public boolean ok() { + return ok; + } + + @Override + public boolean error() { + return error; + } + + @Override + public Map<String, Object> meta() { + return Collections.unmodifiableMap(meta); + } +} diff --git a/src/cz/crcs/ectester/common/test/SimpleTest.java b/src/cz/crcs/ectester/common/test/SimpleTest.java new file mode 100644 index 0000000..f68320a --- /dev/null +++ b/src/cz/crcs/ectester/common/test/SimpleTest.java @@ -0,0 +1,19 @@ +package cz.crcs.ectester.common.test; + +/** + * @param <T> + * @author Jan Jancar johny@neuromancer.sk + */ +public abstract class SimpleTest<T extends BaseTestable> extends Test { + protected T testable; + protected TestCallback<T> callback; + + public SimpleTest(T testable, TestCallback<T> callback) { + this.testable = testable; + this.callback = callback; + } + + public T getTestable() { + return testable; + } +} diff --git a/src/cz/crcs/ectester/common/test/Test.java b/src/cz/crcs/ectester/common/test/Test.java index 750a410..5f55337 100644 --- a/src/cz/crcs/ectester/common/test/Test.java +++ b/src/cz/crcs/ectester/common/test/Test.java @@ -1,5 +1,8 @@ package cz.crcs.ectester.common.test; +import java.util.Collections; +import java.util.Map; + import static cz.crcs.ectester.common.test.Result.Value; /** @@ -8,8 +11,9 @@ import static cz.crcs.ectester.common.test.Result.Value; * @author Jan Jancar johny@neuromancer.sk */ public abstract class Test implements Testable { - protected boolean hasRun = false; + protected boolean hasRun; protected Result result; + protected Map<String, Object> meta; public Result getResult() { if (!hasRun) { @@ -39,6 +43,7 @@ public abstract class Test implements Testable { return result.ok(); } + @Override public boolean error() { if (!hasRun) { return false; @@ -46,12 +51,19 @@ public abstract class Test implements Testable { return result.compareTo(Value.ERROR); } - public abstract String getDescription(); - + @Override public boolean hasRun() { return hasRun; } + @Override + public Map<String, Object> meta() { + return Collections.unmodifiableMap(meta); + } + + public abstract String getDescription(); + + @Override public abstract void run() throws TestException; } diff --git a/src/cz/crcs/ectester/common/test/TestCallback.java b/src/cz/crcs/ectester/common/test/TestCallback.java new file mode 100644 index 0000000..ce6000b --- /dev/null +++ b/src/cz/crcs/ectester/common/test/TestCallback.java @@ -0,0 +1,12 @@ +package cz.crcs.ectester.common.test; + +import java.util.function.Function; + +/** + * + * @author Jan Jancar johny@neuromancer.sk + * @param <T> + */ +public abstract class TestCallback<T extends Testable> implements Function<T, Result> { + +} diff --git a/src/cz/crcs/ectester/reader/test/TestRunner.java b/src/cz/crcs/ectester/common/test/TestRunner.java index dcc78db..ef448c2 100644 --- a/src/cz/crcs/ectester/reader/test/TestRunner.java +++ b/src/cz/crcs/ectester/common/test/TestRunner.java @@ -1,8 +1,6 @@ -package cz.crcs.ectester.reader.test; +package cz.crcs.ectester.common.test; -import cz.crcs.ectester.common.test.Test; -import cz.crcs.ectester.common.test.TestException; -import cz.crcs.ectester.reader.output.TestWriter; +import cz.crcs.ectester.common.output.TestWriter; /** * @author Jan Jancar johny@neuromancer.sk diff --git a/src/cz/crcs/ectester/common/test/TestSuite.java b/src/cz/crcs/ectester/common/test/TestSuite.java new file mode 100644 index 0000000..74a24af --- /dev/null +++ b/src/cz/crcs/ectester/common/test/TestSuite.java @@ -0,0 +1,36 @@ +package cz.crcs.ectester.common.test; + +import cz.crcs.ectester.data.EC_Store; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public abstract class TestSuite { + protected String name; + protected String description; + protected List<Test> tests = new LinkedList<>(); + protected EC_Store dataStore; + + public TestSuite(EC_Store dataStore, String name, String description) { + this.dataStore = dataStore; + this.name = name; + this.description = description; + } + + public List<Test> getTests() { + return Collections.unmodifiableList(tests); + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + +} diff --git a/src/cz/crcs/ectester/common/test/Testable.java b/src/cz/crcs/ectester/common/test/Testable.java index d05d31e..e8eb321 100644 --- a/src/cz/crcs/ectester/common/test/Testable.java +++ b/src/cz/crcs/ectester/common/test/Testable.java @@ -1,15 +1,38 @@ package cz.crcs.ectester.common.test; +import java.util.Map; + /** * @author Jan Jancar johny@neuromancer.sk */ public interface Testable { + /** + * @return Whether this testable was run. + */ boolean hasRun(); + /** + * Run this Testable. + * + * @throws TestException + */ void run() throws TestException; + /** + * @return Whether this Testable was OK. + */ boolean ok(); + /** + * @return Whether an error happened. + */ boolean error(); + + /** + * Get the metadata of this Testable. + * + * @return The metadata of the testable. + */ + Map<String, Object> meta(); } diff --git a/src/cz/crcs/ectester/common/util/ByteUtil.java b/src/cz/crcs/ectester/common/util/ByteUtil.java new file mode 100644 index 0000000..939e487 --- /dev/null +++ b/src/cz/crcs/ectester/common/util/ByteUtil.java @@ -0,0 +1,122 @@ +package cz.crcs.ectester.common.util; + +/** + * Utility class, some byte/hex manipulation, convenient byte[] methods. + * + * @author Petr Svenda petr@svenda.com + * @author Jan Jancar johny@neuromancer.sk + */ +public class ByteUtil { + public static short getShort(byte[] array, int offset) { + return (short) (((array[offset] & 0xFF) << 8) | (array[offset + 1] & 0xFF)); + } + + public static void setShort(byte[] array, int offset, short value) { + array[offset + 1] = (byte) (value & 0xFF); + array[offset] = (byte) ((value >> 8) & 0xFF); + } + + public static int diffBytes(byte[] one, int oneOffset, byte[] other, int otherOffset, int length) { + for (int i = 0; i < length; ++i) { + byte a = one[i + oneOffset]; + byte b = other[i + otherOffset]; + if (a != b) { + return i; + } + } + return length; + } + + public static boolean compareBytes(byte[] one, int oneOffset, byte[] other, int otherOffset, int length) { + return diffBytes(one, oneOffset, other, otherOffset, length) == length; + } + + public static boolean allValue(byte[] array, byte value) { + for (byte a : array) { + if (a != value) + return false; + } + return true; + } + + public static byte[] hexToBytes(String hex) { + return hexToBytes(hex, true); + } + + public static byte[] hexToBytes(String hex, boolean bigEndian) { + hex = hex.replace(" ", ""); + int len = hex.length(); + StringBuilder sb = new StringBuilder(); + + if (len % 2 == 1) { + sb.append("0"); + ++len; + } + + if (bigEndian) { + sb.append(hex); + } else { + for (int i = 0; i < len / 2; ++i) { + if (sb.length() >= 2) { + sb.insert(sb.length() - 2, hex.substring(2 * i, 2 * i + 2)); + } else { + sb.append(hex.substring(2 * i, 2 * i + 2)); + } + + } + } + + String data = sb.toString(); + byte[] result = new byte[len / 2]; + for (int i = 0; i < len; i += 2) { + result[i / 2] = (byte) ((Character.digit(data.charAt(i), 16) << 4) + + (Character.digit(data.charAt(i + 1), 16))); + } + return result; + } + + public static String byteToHex(byte data) { + return String.format("%02x", data); + } + + public static String bytesToHex(byte[] data) { + return bytesToHex(data, true); + } + + public static String bytesToHex(byte[] data, boolean addSpace) { + return bytesToHex(data, 0, data.length, addSpace); + } + + public static String bytesToHex(byte[] data, int offset, int len) { + return bytesToHex(data, offset, len, true); + } + + public static String bytesToHex(byte[] data, int offset, int len, boolean addSpace) { + StringBuilder buf = new StringBuilder(); + for (int i = offset; i < (offset + len); i++) { + buf.append(byteToHex(data[i])); + if (addSpace && i != (offset + len - 1)) { + buf.append(" "); + } + } + return (buf.toString()); + } + + public static byte[] concatenate(byte[]... arrays) { + int len = 0; + for (byte[] array : arrays) { + if (array == null) + continue; + len += array.length; + } + byte[] out = new byte[len]; + int offset = 0; + for (byte[] array : arrays) { + if (array == null || array.length == 0) + continue; + System.arraycopy(array, 0, out, offset, array.length); + offset += array.length; + } + return out; + } +} diff --git a/src/cz/crcs/ectester/common/Util.java b/src/cz/crcs/ectester/common/util/CardUtil.java index 0136493..edcb510 100644 --- a/src/cz/crcs/ectester/common/Util.java +++ b/src/cz/crcs/ectester/common/util/CardUtil.java @@ -1,4 +1,4 @@ -package cz.crcs.ectester.common; +package cz.crcs.ectester.common.util; import cz.crcs.ectester.applet.ECTesterApplet; import cz.crcs.ectester.applet.EC_Consts; @@ -8,126 +8,10 @@ import javacard.security.CryptoException; import static cz.crcs.ectester.applet.ECTesterApplet.*; /** - * Utility class, some byte/hex manipulation, convenient byte[] methods. - * * @author Petr Svenda petr@svenda.com * @author Jan Jancar johny@neuromancer.sk */ -public class Util { - - public static short getShort(byte[] array, int offset) { - return (short) (((array[offset] & 0xFF) << 8) | (array[offset + 1] & 0xFF)); - } - - public static void setShort(byte[] array, int offset, short value) { - array[offset + 1] = (byte) (value & 0xFF); - array[offset] = (byte) ((value >> 8) & 0xFF); - } - - public static int diffBytes(byte[] one, int oneOffset, byte[] other, int otherOffset, int length) { - for (int i = 0; i < length; ++i) { - byte a = one[i + oneOffset]; - byte b = other[i + otherOffset]; - if (a != b) { - return i; - } - } - return length; - } - - public static boolean compareBytes(byte[] one, int oneOffset, byte[] other, int otherOffset, int length) { - return diffBytes(one, oneOffset, other, otherOffset, length) == length; - } - - public static boolean allValue(byte[] array, byte value) { - for (byte a : array) { - if (a != value) - return false; - } - return true; - } - - public static byte[] hexToBytes(String hex) { - return hexToBytes(hex, true); - } - - public static byte[] hexToBytes(String hex, boolean bigEndian) { - hex = hex.replace(" ", ""); - int len = hex.length(); - StringBuilder sb = new StringBuilder(); - - if (len % 2 == 1) { - sb.append("0"); - ++len; - } - - if (bigEndian) { - sb.append(hex); - } else { - for (int i = 0; i < len / 2; ++i) { - if (sb.length() >= 2) { - sb.insert(sb.length() - 2, hex.substring(2 * i, 2 * i + 2)); - } else { - sb.append(hex.substring(2 * i, 2 * i + 2)); - } - - } - } - - String data = sb.toString(); - byte[] result = new byte[len / 2]; - for (int i = 0; i < len; i += 2) { - result[i / 2] = (byte) ((Character.digit(data.charAt(i), 16) << 4) - + (Character.digit(data.charAt(i + 1), 16))); - } - return result; - } - - public static String byteToHex(byte data) { - return String.format("%02x", data); - } - - public static String bytesToHex(byte[] data) { - return bytesToHex(data, true); - } - - public static String bytesToHex(byte[] data, boolean addSpace) { - return bytesToHex(data, 0, data.length, addSpace); - } - - public static String bytesToHex(byte[] data, int offset, int len) { - return bytesToHex(data, offset, len, true); - } - - public static String bytesToHex(byte[] data, int offset, int len, boolean addSpace) { - StringBuilder buf = new StringBuilder(); - for (int i = offset; i < (offset + len); i++) { - buf.append(byteToHex(data[i])); - if (addSpace && i != (offset + len - 1)) { - buf.append(" "); - } - } - return (buf.toString()); - } - - public static byte[] concatenate(byte[]... arrays) { - int len = 0; - for (byte[] array : arrays) { - if (array == null) - continue; - len += array.length; - } - byte[] out = new byte[len]; - int offset = 0; - for (byte[] array : arrays) { - if (array == null || array.length == 0) - continue; - System.arraycopy(array, 0, out, offset, array.length); - offset += array.length; - } - return out; - } - +public class CardUtil { public static String getSWSource(short sw) { switch (sw) { case ISO7816.SW_NO_ERROR: diff --git a/src/cz/crcs/ectester/common/util/ECUtil.java b/src/cz/crcs/ectester/common/util/ECUtil.java new file mode 100644 index 0000000..973b813 --- /dev/null +++ b/src/cz/crcs/ectester/common/util/ECUtil.java @@ -0,0 +1,172 @@ +package cz.crcs.ectester.common.util; + +import java.math.BigInteger; +import java.security.spec.*; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class ECUtil { + + public static byte[] toByteArray(BigInteger what, int bits) { + byte[] raw = what.toByteArray(); + int bytes = (bits + 7) / 8; + if (raw.length < bytes) { + byte[] result = new byte[bytes]; + System.arraycopy(raw, 0, result, bytes - raw.length, raw.length); + return result; + } + if (bytes < raw.length) { + byte[] result = new byte[bytes]; + System.arraycopy(raw, raw.length - bytes, result, 0, bytes); + return result; + } + return raw; + } + + public static byte[] toX962Compressed(ECPoint point, int bits) { + if (point.equals(ECPoint.POINT_INFINITY)) { + return new byte[]{0}; + } + byte[] x = toByteArray(point.getAffineX(), bits); + byte marker = (byte) (0x02 | point.getAffineY().mod(BigInteger.valueOf(2)).byteValue()); + return ByteUtil.concatenate(new byte[]{marker}, x); + } + + public static byte[] toX962Compressed(ECPoint point, EllipticCurve curve) { + return toX962Compressed(point, curve.getField().getFieldSize()); + } + + public static byte[] toX962Compressed(ECPoint point, ECParameterSpec spec) { + return toX962Compressed(point, spec.getCurve()); + } + + public static byte[] toX962Uncompressed(ECPoint point, int bits) { + if (point.equals(ECPoint.POINT_INFINITY)) { + return new byte[]{0}; + } + byte[] x = toByteArray(point.getAffineX(), bits); + byte[] y = toByteArray(point.getAffineY(), bits); + return ByteUtil.concatenate(new byte[]{0x04}, x, y); + } + + public static byte[] toX962Uncompressed(ECPoint point, EllipticCurve curve) { + return toX962Uncompressed(point, curve.getField().getFieldSize()); + } + + public static byte[] toX962Uncompressed(ECPoint point, ECParameterSpec spec) { + return toX962Uncompressed(point, spec.getCurve()); + } + + public static byte[] toX962Hybrid(ECPoint point, int bits) { + if (point.equals(ECPoint.POINT_INFINITY)) { + return new byte[]{0}; + } + byte[] x = toByteArray(point.getAffineX(), bits); + byte[] y = toByteArray(point.getAffineY(), bits); + byte marker = (byte) (0x06 | point.getAffineY().mod(BigInteger.valueOf(2)).byteValue()); + return ByteUtil.concatenate(new byte[]{marker}, x, y); + } + + public static byte[] toX962Hybrid(ECPoint point, EllipticCurve curve) { + return toX962Hybrid(point, curve.getField().getFieldSize()); + } + + public static byte[] toX962Hybrid(ECPoint point, ECParameterSpec spec) { + return toX962Hybrid(point, spec.getCurve()); + } + + private static boolean isResidue(BigInteger a, BigInteger p) { + BigInteger exponent = p.subtract(BigInteger.ONE).divide(BigInteger.valueOf(2)); + BigInteger result = a.modPow(exponent, p); + return result.intValueExact() == 1; + } + + private static BigInteger modSqrt(BigInteger a, BigInteger p) { + BigInteger q = p.subtract(BigInteger.ONE); + int s = 0; + while (q.mod(BigInteger.valueOf(2)).equals(BigInteger.ZERO)) { + q = q.divide(BigInteger.valueOf(2)); + s++; + } + + BigInteger z = BigInteger.ONE; + do { + z = z.add(BigInteger.ONE); + } while (isResidue(z, p)); + + BigInteger m = BigInteger.valueOf(s); + BigInteger c = z.modPow(q, p); + BigInteger t = a.modPow(q, p); + BigInteger rExponent = q.add(BigInteger.ONE).divide(BigInteger.valueOf(2)); + BigInteger r = a.modPow(rExponent, p); + + while (!t.equals(BigInteger.ONE)) { + int i = 0; + BigInteger exponent; + do { + exponent = BigInteger.valueOf(2).pow(++i); + } while (!t.modPow(exponent, p).equals(BigInteger.ONE)); + + BigInteger twoExponent = m.subtract(BigInteger.valueOf(i + 1)); + BigInteger b = c.modPow(BigInteger.valueOf(2).modPow(twoExponent, p), p); + m = BigInteger.valueOf(i); + c = b.modPow(BigInteger.valueOf(2), p); + t = t.multiply(c).mod(p); + r = r.multiply(b).mod(p); + } + return r; + } + + public static ECPoint fromX962(byte[] data, EllipticCurve curve) { + if (data == null) { + return null; + } + if (data[0] == 0x04 || data[0] == 0x06 || data[0] == 0x07) { + int len = (data.length - 1) / 2; + byte[] xbytes = new byte[len]; + System.arraycopy(data, 1, xbytes, 0, len); + byte[] ybytes = new byte[len]; + System.arraycopy(data, 1 + len, ybytes, 0, len); + return new ECPoint(new BigInteger(1, xbytes), new BigInteger(1, ybytes)); + } else if (data[0] == 0x02 || data[0] == 0x03) { + if (curve == null) { + throw new IllegalArgumentException(); + } + byte[] xbytes = new byte[data.length - 1]; + System.arraycopy(data, 1, xbytes, 0, data.length - 1); + BigInteger x = new BigInteger(1, xbytes); + BigInteger a = curve.getA(); + BigInteger b = curve.getB(); + + ECField field = curve.getField(); + if (field instanceof ECFieldFp) { + BigInteger p = ((ECFieldFp) field).getP(); + BigInteger alpha = x.modPow(BigInteger.valueOf(3), p); + alpha = alpha.add(x.multiply(a)); + alpha = alpha.add(b); + + BigInteger beta = modSqrt(alpha, p); + if (beta.getLowestSetBit() == 0) { + // rightmost bit is one + if (data[0] == 0x02) { + beta = beta.negate(); + } + } else { + // rightmost bit is zero + if (data[0] == 0x03) { + beta = beta.negate(); + } + } + + return new ECPoint(x, beta); + } else if (field instanceof ECFieldF2m) { + //TODO + throw new UnsupportedOperationException(); + } + return null; + } else { + throw new IllegalArgumentException(); + } + } +} diff --git a/src/cz/crcs/ectester/reader/CardMngr.java b/src/cz/crcs/ectester/reader/CardMngr.java index ad5b368..cea46bc 100644 --- a/src/cz/crcs/ectester/reader/CardMngr.java +++ b/src/cz/crcs/ectester/reader/CardMngr.java @@ -2,7 +2,7 @@ package cz.crcs.ectester.reader; import com.licel.jcardsim.io.CAD; import com.licel.jcardsim.io.JavaxSmartCardInterface; -import cz.crcs.ectester.common.Util; +import cz.crcs.ectester.common.util.ByteUtil; import javacard.framework.AID; import javax.smartcardio.*; @@ -80,7 +80,7 @@ public class CardMngr { //reset the card if (verbose) - System.out.println(Util.bytesToHex(card.getATR().getBytes())); + System.out.println(ByteUtil.bytesToHex(card.getATR().getBytes())); cardFound = true; } @@ -109,7 +109,7 @@ public class CardMngr { try { card = terminal.connect("*"); ATR atr = card.getATR(); - System.out.println(terminalIndex + " : " + terminal.getName() + " - " + Util.bytesToHex(atr.getBytes())); + System.out.println(terminalIndex + " : " + terminal.getName() + " - " + ByteUtil.bytesToHex(atr.getBytes())); terminalIndex++; } catch (CardException ex) { ex.printStackTrace(System.out); @@ -227,7 +227,7 @@ public class CardMngr { System.out.println(">>>>"); System.out.println(apdu); - System.out.println(Util.bytesToHex(apdu.getBytes())); + System.out.println(ByteUtil.bytesToHex(apdu.getBytes())); } long elapsed = -System.nanoTime(); @@ -238,7 +238,7 @@ public class CardMngr { if (verbose) { System.out.println(responseAPDU); - System.out.println(Util.bytesToHex(responseAPDU.getBytes())); + System.out.println(ByteUtil.bytesToHex(responseAPDU.getBytes())); } if (responseAPDU.getSW1() == (byte) 0x61) { @@ -248,7 +248,7 @@ public class CardMngr { responseAPDU = channel.transmit(apduToSend); if (verbose) - System.out.println(Util.bytesToHex(responseAPDU.getBytes())); + System.out.println(ByteUtil.bytesToHex(responseAPDU.getBytes())); } if (verbose) { @@ -277,7 +277,7 @@ public class CardMngr { if (verbose) { System.out.println(">>>>"); System.out.println(apdu); - System.out.println(Util.bytesToHex(apdu.getBytes())); + System.out.println(ByteUtil.bytesToHex(apdu.getBytes())); } ResponseAPDU response = simulator.transmitCommand(apdu); @@ -285,7 +285,7 @@ public class CardMngr { if (verbose) { System.out.println(response); - System.out.println(Util.bytesToHex(responseBytes)); + System.out.println(ByteUtil.bytesToHex(responseBytes)); System.out.println("<<<<"); } diff --git a/src/cz/crcs/ectester/reader/ECTesterReader.java b/src/cz/crcs/ectester/reader/ECTesterReader.java index 0bbe8f7..6fa7068 100644 --- a/src/cz/crcs/ectester/reader/ECTesterReader.java +++ b/src/cz/crcs/ectester/reader/ECTesterReader.java @@ -23,12 +23,12 @@ package cz.crcs.ectester.reader; import cz.crcs.ectester.applet.ECTesterApplet; import cz.crcs.ectester.applet.EC_Consts; -import cz.crcs.ectester.common.Util; -import cz.crcs.ectester.common.ec.EC_Category; -import cz.crcs.ectester.common.ec.EC_Data; +import cz.crcs.ectester.common.cli.CLITools; import cz.crcs.ectester.common.ec.EC_Params; -import cz.crcs.ectester.common.output.OutputLogger; +import cz.crcs.ectester.common.output.*; import cz.crcs.ectester.common.test.TestException; +import cz.crcs.ectester.common.test.TestRunner; +import cz.crcs.ectester.common.util.ByteUtil; import cz.crcs.ectester.data.EC_Store; import cz.crcs.ectester.reader.command.Command; import cz.crcs.ectester.reader.output.*; @@ -79,11 +79,10 @@ public class ECTesterReader { //if help, print and quit if (cli.hasOption("help")) { - help(); + CLITools.help("ECTesterReader.jar", CLI_HEADER, opts, CLI_FOOTER, true); return; } else if (cli.hasOption("version")) { - System.out.println(DESCRIPTION); - System.out.println(LICENSE); + CLITools.version(DESCRIPTION, LICENSE); return; } cfg = new Config(); @@ -96,7 +95,7 @@ public class ECTesterReader { dataStore = new EC_Store(); //if list, print and quit if (cli.hasOption("list-named")) { - list(); + CLITools.listNamed(dataStore, cli.getOptionValue("list-named")); return; } @@ -312,39 +311,6 @@ public class ECTesterReader { } /** - * Prints help. - */ - private void help() { - HelpFormatter help = new HelpFormatter(); - help.setOptionComparator(null); - help.printHelp("ECTesterReader.jar", CLI_HEADER, opts, CLI_FOOTER, true); - } - - /** - * List categories and named curves. - */ - private void list() { - Map<String, EC_Category> categories = dataStore.getCategories(); - if (cfg.listNamed == null) { - // print all categories, briefly - for (EC_Category cat : categories.values()) { - System.out.println(cat); - } - } else if (categories.containsKey(cfg.listNamed)) { - // print given category - System.out.println(categories.get(cfg.listNamed)); - } else { - // print given object - EC_Data object = dataStore.getObject(EC_Data.class, cfg.listNamed); - if (object != null) { - System.out.println(object); - } else { - System.err.println("Named object " + cfg.listNamed + " not found!"); - } - } - } - - /** * Exports default card/simulation EC domain parameters to output file. * * @throws CardException if APDU transmission fails @@ -420,8 +386,8 @@ public class ECTesterReader { } respWriter.outputResponse(response); - String pub = Util.bytesToHex(export.getParameter(ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.PARAMETER_W), false); - String priv = Util.bytesToHex(export.getParameter(ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.PARAMETER_S), false); + String pub = ByteUtil.bytesToHex(export.getParameter(ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.PARAMETER_W), false); + String priv = ByteUtil.bytesToHex(export.getParameter(ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.PARAMETER_S), false); String line = String.format("%d;%d;%s;%s\n", generated, elapsed / 1000000, pub, priv); keysFile.write(line); keysFile.flush(); @@ -440,14 +406,14 @@ public class ECTesterReader { * @throws IOException if an IO error occurs when writing to key file. */ private void test() throws IOException, TestException { - TestSuite suite; + CardTestSuite suite; switch (cfg.testSuite) { case "default": - suite = new DefaultSuite(dataStore, cfg); + suite = new CardDefaultSuite(dataStore, cfg); break; case "test-vectors": - suite = new TestVectorSuite(dataStore, cfg); + suite = new CardTestVectorSuite(dataStore, cfg); break; default: // These tests are dangerous, prompt before them. @@ -466,13 +432,13 @@ public class ECTesterReader { switch (cfg.testSuite) { case "wrong": - suite = new WrongCurvesSuite(dataStore, cfg); + suite = new CardWrongCurvesSuite(dataStore, cfg); break; case "composite": - suite = new CompositeCurvesSuite(dataStore, cfg); + suite = new CardCompositeCurvesSuite(dataStore, cfg); break; case "invalid": - suite = new InvalidCurvesSuite(dataStore, cfg); + suite = new CardInvalidCurvesSuite(dataStore, cfg); break; default: System.err.println("Unknown test suite."); @@ -542,7 +508,7 @@ public class ECTesterReader { } if (out != null) { - out.write(String.format("%d;%d;%s\n", done, perform.getDuration() / 1000000, Util.bytesToHex(perform.getSecret(), false))); + out.write(String.format("%d;%d;%s\n", done, perform.getDuration() / 1000000, ByteUtil.bytesToHex(perform.getSecret(), false))); } ++done; @@ -619,7 +585,7 @@ public class ECTesterReader { } if (out != null) { - out.write(String.format("%d;%d;%s\n", done, perform.getDuration() / 1000000, Util.bytesToHex(perform.getSignature(), false))); + out.write(String.format("%d;%d;%s\n", done, perform.getDuration() / 1000000, ByteUtil.bytesToHex(perform.getSignature(), false))); } ++done; diff --git a/src/cz/crcs/ectester/reader/command/Command.java b/src/cz/crcs/ectester/reader/command/Command.java index 9d23322..922a33a 100644 --- a/src/cz/crcs/ectester/reader/command/Command.java +++ b/src/cz/crcs/ectester/reader/command/Command.java @@ -2,11 +2,11 @@ package cz.crcs.ectester.reader.command; import cz.crcs.ectester.applet.ECTesterApplet; import cz.crcs.ectester.applet.EC_Consts; +import cz.crcs.ectester.common.util.ByteUtil; import cz.crcs.ectester.data.EC_Store; import cz.crcs.ectester.reader.CardMngr; import cz.crcs.ectester.reader.ECTesterReader; import cz.crcs.ectester.reader.response.Response; -import cz.crcs.ectester.common.Util; import cz.crcs.ectester.common.ec.EC_Curve; import cz.crcs.ectester.common.ec.EC_Key; import cz.crcs.ectester.common.ec.EC_Keypair; @@ -174,7 +174,7 @@ public abstract class Command { if (privkey == null) { throw new IOException("Couldn't read the private key file correctly."); } - data = Util.concatenate(data, privkey); + data = ByteUtil.concatenate(data, privkey); } return new Command.Set(cardManager, keyPair, EC_Consts.CURVE_external, params, data); } @@ -203,7 +203,7 @@ public abstract class Command { this.keyClass = keyClass; byte[] data = new byte[]{0, 0, keyClass}; - Util.setShort(data, 0, keyLength); + ByteUtil.setShort(data, 0, keyLength); this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_ALLOCATE, keyPair, 0x00, data); } @@ -214,13 +214,19 @@ public abstract class Command { elapsed += System.nanoTime(); return new Response.Allocate(response, elapsed, keyPair, keyLength, keyClass); } + + @Override + public String toString() { + return "Allocate"; + } } - - public static class AllocateKeyAgreement extends Command { + /** + * + */ + public static class AllocateKeyAgreement extends Command { private byte kaType; - /** * Creates the INS_ALLOCATE_KA instruction. * @@ -241,6 +247,11 @@ public abstract class Command { elapsed += System.nanoTime(); return new Response.AllocateKeyAgreement(response, elapsed, kaType); } + + @Override + public String toString() { + return "AllocateKeyAgreement"; + } } /** @@ -267,6 +278,11 @@ public abstract class Command { elapsed += System.nanoTime(); return new Response.Clear(response, elapsed, keyPair); } + + @Override + public String toString() { + return "Clear"; + } } /** @@ -296,7 +312,7 @@ public abstract class Command { int len = external != null ? 2 + external.length : 2; byte[] data = new byte[len]; - Util.setShort(data, 0, params); + ByteUtil.setShort(data, 0, params); if (external != null) { System.arraycopy(external, 0, data, 2, external.length); } @@ -311,6 +327,11 @@ public abstract class Command { elapsed += System.nanoTime(); return new Response.Set(response, elapsed, keyPair, curve, params); } + + @Override + public String toString() { + return "Set"; + } } /** @@ -337,7 +358,7 @@ public abstract class Command { this.corruption = corruption; byte[] data = new byte[3]; - Util.setShort(data, 0, params); + ByteUtil.setShort(data, 0, params); data[2] = corruption; this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_CORRUPT, keyPair, key, data); @@ -350,6 +371,11 @@ public abstract class Command { elapsed += System.nanoTime(); return new Response.Corrupt(response, elapsed, keyPair, key, params, corruption); } + + @Override + public String toString() { + return "Corrupt"; + } } /** @@ -378,6 +404,11 @@ public abstract class Command { elapsed += System.nanoTime(); return new Response.Generate(response, elapsed, keyPair); } + + @Override + public String toString() { + return "Generate"; + } } /** @@ -403,7 +434,7 @@ public abstract class Command { this.params = params; byte[] data = new byte[2]; - Util.setShort(data, 0, params); + ByteUtil.setShort(data, 0, params); this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_EXPORT, keyPair, key, data); } @@ -415,6 +446,11 @@ public abstract class Command { elapsed += System.nanoTime(); return new Response.Export(response, elapsed, keyPair, key, params); } + + @Override + public String toString() { + return "Export"; + } } /** @@ -446,7 +482,7 @@ public abstract class Command { this.type = type; byte[] data = new byte[]{export, 0,0, type}; - Util.setShort(data, 1, corruption); + ByteUtil.setShort(data, 1, corruption); this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_ECDH, pubkey, privkey, data); } @@ -458,6 +494,11 @@ public abstract class Command { elapsed += System.nanoTime(); return new Response.ECDH(response, elapsed, pubkey, privkey, export, corruption, type); } + + @Override + public String toString() { + return "ECDH"; + } } /** @@ -489,7 +530,7 @@ public abstract class Command { this.pubkey = pubkey; byte[] data = new byte[3 + pubkey.length]; - Util.setShort(data, 0, corruption); + ByteUtil.setShort(data, 0, corruption); data[2] = type; System.arraycopy(pubkey, 0, data, 3, pubkey.length); @@ -503,6 +544,11 @@ public abstract class Command { elapsed += System.nanoTime(); return new Response.ECDH(response, elapsed, ECTesterApplet.KEYPAIR_REMOTE, privkey, export, corruption, type); } + + @Override + public String toString() { + return "ECDH_direct"; + } } public static class ECDSA extends Command { @@ -526,7 +572,7 @@ public abstract class Command { int len = raw != null ? raw.length : 0; byte[] data = new byte[2 + len]; - Util.setShort(data, 0, (short) len); + ByteUtil.setShort(data, 0, (short) len); if (raw != null) { System.arraycopy(raw, 0, data, 2, len); } @@ -541,6 +587,11 @@ public abstract class Command { elapsed += System.nanoTime(); return new Response.ECDSA(response, elapsed, keyPair, export, raw); } + + @Override + public String toString() { + return "ECDSA"; + } } /** @@ -564,6 +615,11 @@ public abstract class Command { elapsed += System.nanoTime(); return new Response.Cleanup(response, elapsed); } + + @Override + public String toString() { + return "Cleanup"; + } } /** @@ -587,6 +643,11 @@ public abstract class Command { elapsed += System.nanoTime(); return new Response.Support(response, elapsed); } + + @Override + public String toString() { + return "Support"; + } } } diff --git a/src/cz/crcs/ectester/reader/output/ResponseWriter.java b/src/cz/crcs/ectester/reader/output/ResponseWriter.java index 0f5b6e8..f8837f8 100644 --- a/src/cz/crcs/ectester/reader/output/ResponseWriter.java +++ b/src/cz/crcs/ectester/reader/output/ResponseWriter.java @@ -1,6 +1,6 @@ package cz.crcs.ectester.reader.output; -import cz.crcs.ectester.common.Util; +import cz.crcs.ectester.common.util.CardUtil; import cz.crcs.ectester.reader.response.Response; import java.io.PrintStream; @@ -20,20 +20,24 @@ public class ResponseWriter { for (int j = 0; j < r.getNumSW(); ++j) { short sw = r.getSW(j); if (sw != 0) { - suffix.append(" ").append(Util.getSWString(sw)); + suffix.append(" ").append(CardUtil.getSWString(sw)); } } if (suffix.length() == 0) { - suffix.append(" [").append(Util.getSW(r.getNaturalSW())).append("]"); + suffix.append(" [").append(CardUtil.getSW(r.getNaturalSW())).append("]"); } return String.format("%4d ms ┃ %s", r.getDuration() / 1000000, suffix); } - public void outputResponse(Response r) { + public String responseString(Response r) { String out = ""; out += String.format("%-70s", r.getDescription()) + " ┃ "; out += responseSuffix(r); - output.println(out); + return out; + } + + public void outputResponse(Response r) { + output.println(responseString(r)); output.flush(); } } diff --git a/src/cz/crcs/ectester/reader/response/Response.java b/src/cz/crcs/ectester/reader/response/Response.java index 4158ac3..3b5d98b 100644 --- a/src/cz/crcs/ectester/reader/response/Response.java +++ b/src/cz/crcs/ectester/reader/response/Response.java @@ -2,7 +2,8 @@ package cz.crcs.ectester.reader.response; import cz.crcs.ectester.applet.ECTesterApplet; import cz.crcs.ectester.applet.EC_Consts; -import cz.crcs.ectester.common.Util; +import cz.crcs.ectester.common.util.ByteUtil; +import cz.crcs.ectester.common.util.CardUtil; import javacard.framework.ISO7816; import javacard.security.KeyPair; @@ -12,7 +13,6 @@ import javax.smartcardio.ResponseAPDU; * @author Jan Jancar johny@neuromancer.sk */ public abstract class Response { - private ResponseAPDU resp; private long time; private short[] sws; @@ -36,7 +36,7 @@ public abstract class Response { //parse SWs in response for (int i = 0; i < numSW; ++i) { if (getLength() >= (offset + 2)) { - short sw = Util.getShort(data, offset); + short sw = ByteUtil.getShort(data, offset); offset += 2; sws[i] = sw; if (sw != ISO7816.SW_NO_ERROR) { @@ -62,7 +62,7 @@ public abstract class Response { error = true; break; } - short paramLength = Util.getShort(data, offset); + short paramLength = ByteUtil.getShort(data, offset); offset += 2; if (data.length < offset + paramLength) { error = true; @@ -87,6 +87,10 @@ public abstract class Response { return (short) resp.getSW(); } + public short[] getSWs() { + return sws; + } + public short getSW(int index) { return sws[index]; } @@ -140,7 +144,7 @@ public abstract class Response { @Override public String getDescription() { - return String.format("Allocated KeyAgreement(%s) object", Util.getKATypeString(this.kaType)); + return String.format("Allocated KeyAgreement(%s) object", CardUtil.getKATypeString(this.kaType)); } } @@ -289,7 +293,7 @@ public abstract class Response { @Override public String getDescription() { - String corrupt = Util.getCorruption(corruption); + String corrupt = CardUtil.getCorruption(corruption); String pair; if (keyPair == ECTesterApplet.KEYPAIR_BOTH) { @@ -476,7 +480,7 @@ public abstract class Response { @Override public String getDescription() { - String algo = Util.getKA(type); + String algo = CardUtil.getKA(type); String pub = pubkey == ECTesterApplet.KEYPAIR_LOCAL ? "local" : "remote"; String priv = privkey == ECTesterApplet.KEYPAIR_LOCAL ? "local" : "remote"; @@ -485,7 +489,7 @@ public abstract class Response { if (corruption == EC_Consts.CORRUPTION_NONE) { validity = "unchanged"; } else { - validity = Util.getCorruption(corruption); + validity = CardUtil.getCorruption(corruption); } return String.format("%s of %s pubkey and %s privkey(%s point)", algo, pub, priv, validity); } diff --git a/src/cz/crcs/ectester/reader/test/CompositeCurvesSuite.java b/src/cz/crcs/ectester/reader/test/CardCompositeCurvesSuite.java index 2e711a2..930a0d0 100644 --- a/src/cz/crcs/ectester/reader/test/CompositeCurvesSuite.java +++ b/src/cz/crcs/ectester/reader/test/CardCompositeCurvesSuite.java @@ -17,9 +17,9 @@ import static cz.crcs.ectester.common.test.Result.ExpectedValue; /** * @author Jan Jancar johny@neuromancer.sk */ -public class CompositeCurvesSuite extends TestSuite { +public class CardCompositeCurvesSuite extends CardTestSuite { - public CompositeCurvesSuite(EC_Store dataStore, ECTesterReader.Config cfg) { + public CardCompositeCurvesSuite(EC_Store dataStore, ECTesterReader.Config cfg) { super(dataStore, cfg, "composite", "The composite suite tests ECDH over curves with composite order. This should generally fail, as using such a curve is unsafe."); } @@ -41,12 +41,12 @@ public class CompositeCurvesSuite extends TestSuite { continue; } if ((curve.getBits() == cfg.bits || cfg.all)) { - tests.add(new SimpleTest(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), ExpectedValue.SUCCESS)); - tests.add(new SimpleTest(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), ExpectedValue.ANY)); - tests.add(new SimpleTest(new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_LOCAL), ExpectedValue.ANY)); + tests.add(CommandTest.expect(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), ExpectedValue.SUCCESS)); + tests.add(CommandTest.expect(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), ExpectedValue.ANY)); + tests.add(CommandTest.expect(new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_LOCAL), ExpectedValue.ANY)); Command ecdhCommand = new Command.ECDH_direct(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_NONE, EC_Consts.KA_ECDH, key.flatten()); - tests.add(new SimpleTest(ecdhCommand, ExpectedValue.FAILURE, "Card correctly rejected to do ECDH over a composite order curve.", "Card incorrectly does ECDH over a composite order curve, leaks bits of private key.")); - tests.add(new SimpleTest(new Command.Cleanup(cardManager), ExpectedValue.ANY)); + tests.add(CommandTest.expect(ecdhCommand, ExpectedValue.FAILURE, "Card correctly rejected to do ECDH over a composite order curve.", "Card incorrectly does ECDH over a composite order curve, leaks bits of private key.")); + tests.add(CommandTest.expect(new Command.Cleanup(cardManager), ExpectedValue.ANY)); } } } diff --git a/src/cz/crcs/ectester/reader/test/DefaultSuite.java b/src/cz/crcs/ectester/reader/test/CardDefaultSuite.java index fb8fdab..06818d4 100644 --- a/src/cz/crcs/ectester/reader/test/DefaultSuite.java +++ b/src/cz/crcs/ectester/reader/test/CardDefaultSuite.java @@ -15,15 +15,15 @@ import static cz.crcs.ectester.common.test.Result.ExpectedValue; /** * @author Jan Jancar johny@neuromancer.sk */ -public class DefaultSuite extends TestSuite { +public class CardDefaultSuite extends CardTestSuite { - public DefaultSuite(EC_Store dataStore, ECTesterReader.Config cfg) { + public CardDefaultSuite(EC_Store dataStore, ECTesterReader.Config cfg) { super(dataStore, cfg, "default", "The default test suite tests basic support of ECDH and ECDSA."); } @Override public void setup(CardMngr cardManager) throws IOException { - tests.add(new SimpleTest(new Command.Support(cardManager), ExpectedValue.ANY)); + tests.add(CommandTest.expect(new Command.Support(cardManager), ExpectedValue.ANY)); if (cfg.namedCurve != null) { String desc = "Default tests over the " + cfg.namedCurve + " curve category."; if (cfg.primeField) { @@ -59,11 +59,11 @@ public class DefaultSuite extends TestSuite { } private void defaultTests(CardMngr cardManager, short keyLength, byte keyType) throws IOException { - tests.add(new SimpleTest(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_BOTH, keyLength, keyType), ExpectedValue.SUCCESS)); + tests.add(CommandTest.expect(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_BOTH, keyLength, keyType), ExpectedValue.SUCCESS)); Command curve = Command.prepareCurve(cardManager, dataStore, cfg, ECTesterApplet.KEYPAIR_BOTH, keyLength, keyType); if (curve != null) - tests.add(new SimpleTest(curve, ExpectedValue.SUCCESS)); + tests.add(CommandTest.expect(curve, ExpectedValue.SUCCESS)); tests.add(defaultCurveTests(cardManager, ExpectedValue.SUCCESS, ExpectedValue.SUCCESS, ExpectedValue.ANY, ExpectedValue.SUCCESS, "Default tests.")); - tests.add(new SimpleTest(new Command.Cleanup(cardManager), ExpectedValue.ANY)); + tests.add(CommandTest.expect(new Command.Cleanup(cardManager), ExpectedValue.ANY)); } } diff --git a/src/cz/crcs/ectester/reader/test/InvalidCurvesSuite.java b/src/cz/crcs/ectester/reader/test/CardInvalidCurvesSuite.java index 1f71ad5..e4e55c9 100644 --- a/src/cz/crcs/ectester/reader/test/InvalidCurvesSuite.java +++ b/src/cz/crcs/ectester/reader/test/CardInvalidCurvesSuite.java @@ -23,9 +23,9 @@ import static cz.crcs.ectester.common.test.Result.ExpectedValue; /** * @author Jan Jancar johny@neuromancer.sk */ -public class InvalidCurvesSuite extends TestSuite { +public class CardInvalidCurvesSuite extends CardTestSuite { - public InvalidCurvesSuite(EC_Store dataStore, ECTesterReader.Config cfg) { + public CardInvalidCurvesSuite(EC_Store dataStore, ECTesterReader.Config cfg) { super(dataStore, cfg, "invalid", "The invalid curve suite tests whether the card rejects points outside of the curve during ECDH."); } @@ -55,16 +55,16 @@ public class InvalidCurvesSuite extends TestSuite { EC_Curve curve = e.getKey(); List<EC_Key.Public> keys = e.getValue(); - tests.add(new SimpleTest(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), ExpectedValue.SUCCESS)); - tests.add(new SimpleTest(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), ExpectedValue.SUCCESS)); - tests.add(new SimpleTest(new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_LOCAL), ExpectedValue.SUCCESS)); + tests.add(CommandTest.expect(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), ExpectedValue.SUCCESS)); + tests.add(CommandTest.expect(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), ExpectedValue.SUCCESS)); + tests.add(CommandTest.expect(new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_LOCAL), ExpectedValue.SUCCESS)); List<Test> ecdhTests = new LinkedList<>(); for (EC_Key.Public pub : keys) { Command ecdhCommand = new Command.ECDH_direct(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_NONE, EC_Consts.KA_ANY, pub.flatten()); - ecdhTests.add(new SimpleTest(ecdhCommand, ExpectedValue.FAILURE, "Card correctly rejected point on invalid curve." , "Card incorrectly accepted point on invalid curve.")); + ecdhTests.add(CommandTest.expect(ecdhCommand, ExpectedValue.FAILURE, "Card correctly rejected point on invalid curve." , "Card incorrectly accepted point on invalid curve.")); } tests.add(CompoundTest.all(ExpectedValue.SUCCESS, "Invalid curve test of " + curve.getId(), ecdhTests.toArray(new Test[0]))); - tests.add(new SimpleTest(new Command.Cleanup(cardManager), ExpectedValue.ANY)); + tests.add(CommandTest.expect(new Command.Cleanup(cardManager), ExpectedValue.ANY)); } } } diff --git a/src/cz/crcs/ectester/reader/test/TestSuite.java b/src/cz/crcs/ectester/reader/test/CardTestSuite.java index dc8167b..3da5158 100644 --- a/src/cz/crcs/ectester/reader/test/TestSuite.java +++ b/src/cz/crcs/ectester/reader/test/CardTestSuite.java @@ -6,13 +6,13 @@ import cz.crcs.ectester.common.ec.EC_Curve; import cz.crcs.ectester.common.test.CompoundTest; import cz.crcs.ectester.common.test.Result; import cz.crcs.ectester.common.test.Test; +import cz.crcs.ectester.common.test.TestSuite; import cz.crcs.ectester.data.EC_Store; import cz.crcs.ectester.reader.CardMngr; import cz.crcs.ectester.reader.ECTesterReader; import cz.crcs.ectester.reader.command.Command; import java.io.IOException; -import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -24,34 +24,16 @@ import static cz.crcs.ectester.common.test.Result.Value; /** * @author Jan Jancar johny@neuromancer.sk */ -public abstract class TestSuite { - EC_Store dataStore; +public abstract class CardTestSuite extends TestSuite { ECTesterReader.Config cfg; - String name; - String description; - List<Test> tests = new LinkedList<>(); - TestSuite(EC_Store dataStore, ECTesterReader.Config cfg, String name, String description) { - this.dataStore = dataStore; + CardTestSuite(EC_Store dataStore, ECTesterReader.Config cfg, String name, String description) { + super(dataStore, name, description); this.cfg = cfg; - this.name = name; - this.description = description; } public abstract void setup(CardMngr cardManager) throws IOException; - public List<Test> getTests() { - return Collections.unmodifiableList(tests); - } - - public String getName() { - return name; - } - - public String getDescription() { - return description; - } - /** * @param cardManager cardManager to send APDU through * @param generateExpected expected result of the Generate command @@ -64,14 +46,14 @@ public abstract class TestSuite { Test defaultCurveTests(CardMngr cardManager, ExpectedValue generateExpected, ExpectedValue ecdhExpected, ExpectedValue ecdhCompressExpected, ExpectedValue ecdsaExpected, String description) { List<Test> tests = new LinkedList<>(); - tests.add(new SimpleTest(new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_BOTH), generateExpected)); - tests.add(new SimpleTest(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_NONE, EC_Consts.KA_ECDH), ecdhExpected)); - tests.add(new SimpleTest(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_COMPRESS, EC_Consts.KA_ECDH), ecdhExpected)); - tests.add(new SimpleTest(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_ONE, EC_Consts.KA_ECDH), ExpectedValue.FAILURE)); - tests.add(new SimpleTest(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_ZERO, EC_Consts.KA_ECDH), ExpectedValue.FAILURE)); - tests.add(new SimpleTest(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_MAX, EC_Consts.KA_ECDH), ExpectedValue.FAILURE)); - tests.add(new SimpleTest(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_FULLRANDOM, EC_Consts.KA_ECDH), ExpectedValue.FAILURE)); - tests.add(new SimpleTest(new Command.ECDSA(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, null), ecdsaExpected)); + tests.add(CommandTest.expect(new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_BOTH), generateExpected)); + tests.add(CommandTest.expect(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_NONE, EC_Consts.KA_ECDH), ecdhExpected)); + tests.add(CommandTest.expect(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_COMPRESS, EC_Consts.KA_ECDH), ecdhExpected)); + tests.add(CommandTest.expect(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_ONE, EC_Consts.KA_ECDH), ExpectedValue.FAILURE)); + tests.add(CommandTest.expect(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_ZERO, EC_Consts.KA_ECDH), ExpectedValue.FAILURE)); + tests.add(CommandTest.expect(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_MAX, EC_Consts.KA_ECDH), ExpectedValue.FAILURE)); + tests.add(CommandTest.expect(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_FULLRANDOM, EC_Consts.KA_ECDH), ExpectedValue.FAILURE)); + tests.add(CommandTest.expect(new Command.ECDSA(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, null), ecdsaExpected)); return CompoundTest.function((testArray) -> { Function<ExpectedValue, String> shouldHave = (expected) -> { @@ -126,10 +108,10 @@ public abstract class TestSuite { for (Map.Entry<String, EC_Curve> entry : curves.entrySet()) { EC_Curve curve = entry.getValue(); if (curve.getField() == field && (curve.getBits() == cfg.bits || cfg.all)) { - tests.add(new SimpleTest(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), field), ExpectedValue.SUCCESS)); - tests.add(new SimpleTest(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), setExpected)); + tests.add(CommandTest.expect(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), field), ExpectedValue.SUCCESS)); + tests.add(CommandTest.expect(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), setExpected)); tests.add(defaultCurveTests(cardManager, generateExpected, ecdhExpected, ecdhCompressedExpected, ecdsaExpected, description)); - tests.add(new SimpleTest(new Command.Cleanup(cardManager), ExpectedValue.ANY)); + tests.add(CommandTest.expect(new Command.Cleanup(cardManager), ExpectedValue.ANY)); } } diff --git a/src/cz/crcs/ectester/reader/test/TestVectorSuite.java b/src/cz/crcs/ectester/reader/test/CardTestVectorSuite.java index 3f11a79..72e3cce 100644 --- a/src/cz/crcs/ectester/reader/test/TestVectorSuite.java +++ b/src/cz/crcs/ectester/reader/test/CardTestVectorSuite.java @@ -2,15 +2,16 @@ package cz.crcs.ectester.reader.test; import cz.crcs.ectester.applet.ECTesterApplet; import cz.crcs.ectester.applet.EC_Consts; +import cz.crcs.ectester.common.ec.*; import cz.crcs.ectester.common.test.CompoundTest; import cz.crcs.ectester.common.test.Result; import cz.crcs.ectester.common.test.Test; +import cz.crcs.ectester.common.test.TestCallback; +import cz.crcs.ectester.common.util.ByteUtil; import cz.crcs.ectester.data.EC_Store; import cz.crcs.ectester.reader.CardMngr; import cz.crcs.ectester.reader.ECTesterReader; -import cz.crcs.ectester.common.Util; import cz.crcs.ectester.reader.command.Command; -import cz.crcs.ectester.common.ec.*; import cz.crcs.ectester.reader.response.Response; import javacard.security.KeyPair; @@ -25,9 +26,9 @@ import static cz.crcs.ectester.common.test.Result.Value; /** * @author Jan Jancar johny@neuromancer.sk */ -public class TestVectorSuite extends TestSuite { +public class CardTestVectorSuite extends CardTestSuite { - public TestVectorSuite(EC_Store dataStore, ECTesterReader.Config cfg) { + public CardTestVectorSuite(EC_Store dataStore, ECTesterReader.Config cfg) { super(dataStore, cfg, "test", "The test-vectors suite contains a collection of test vectors which test basic ECDH correctness."); } @@ -61,25 +62,28 @@ public class TestVectorSuite extends TestSuite { } List<Test> testVector = new LinkedList<>(); - testVector.add(new SimpleTest(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), ExpectedValue.SUCCESS)); - testVector.add(new SimpleTest(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), ExpectedValue.SUCCESS)); + testVector.add(CommandTest.expect(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), ExpectedValue.SUCCESS)); + testVector.add(CommandTest.expect(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), ExpectedValue.SUCCESS)); //tests.add(new Test.Simple(new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_BOTH), ExpectedValue.SUCCESS)); - testVector.add(new SimpleTest(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.CURVE_external, EC_Consts.PARAMETER_S, onekey.flatten(EC_Consts.PARAMETER_S)), ExpectedValue.SUCCESS)); - testVector.add(new SimpleTest(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, EC_Consts.PARAMETER_W, otherkey.flatten(EC_Consts.PARAMETER_W)), ExpectedValue.SUCCESS)); - testVector.add(new SimpleTest(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_TRUE, EC_Consts.CORRUPTION_NONE, result.getKA()), (command, response) -> { - Response.ECDH dh = (Response.ECDH) response; - if (!dh.successful()) - return new Result(Value.FAILURE, "ECDH was unsuccessful."); - if (!dh.hasSecret()) - return new Result(Value.FAILURE, "ECDH response did not contain the derived secret."); - if (!Util.compareBytes(dh.getSecret(), 0, result.getData(0), 0, dh.secretLength())) { - int firstDiff = Util.diffBytes(dh.getSecret(), 0, result.getData(0), 0, dh.secretLength()); - return new Result(Value.FAILURE, "ECDH derived secret does not match the test, first difference was at byte " + String.valueOf(firstDiff) + "."); + testVector.add(CommandTest.expect(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.CURVE_external, EC_Consts.PARAMETER_S, onekey.flatten(EC_Consts.PARAMETER_S)), ExpectedValue.SUCCESS)); + testVector.add(CommandTest.expect(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, EC_Consts.PARAMETER_W, otherkey.flatten(EC_Consts.PARAMETER_W)), ExpectedValue.SUCCESS)); + testVector.add(CommandTest.function(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_TRUE, EC_Consts.CORRUPTION_NONE, result.getKA()), new TestCallback<CommandTestable>() { + @Override + public Result apply(CommandTestable testable) { + Response.ECDH dh = (Response.ECDH) testable.getResponse(); + if (!dh.successful()) + return new Result(Value.FAILURE, "ECDH was unsuccessful."); + if (!dh.hasSecret()) + return new Result(Value.FAILURE, "ECDH response did not contain the derived secret."); + if (!ByteUtil.compareBytes(dh.getSecret(), 0, result.getData(0), 0, dh.secretLength())) { + int firstDiff = ByteUtil.diffBytes(dh.getSecret(), 0, result.getData(0), 0, dh.secretLength()); + return new Result(Value.FAILURE, "ECDH derived secret does not match the test, first difference was at byte " + String.valueOf(firstDiff) + "."); + } + return new Result(Value.SUCCESS); } - return new Result(Value.SUCCESS); })); tests.add(CompoundTest.all(ExpectedValue.SUCCESS, "Test vector " + result.getId(), testVector.toArray(new Test[0]))); - tests.add(new SimpleTest(new Command.Cleanup(cardManager), ExpectedValue.ANY)); + tests.add(CommandTest.expect(new Command.Cleanup(cardManager), ExpectedValue.ANY)); } } diff --git a/src/cz/crcs/ectester/reader/test/WrongCurvesSuite.java b/src/cz/crcs/ectester/reader/test/CardWrongCurvesSuite.java index 76da718..4c529da 100644 --- a/src/cz/crcs/ectester/reader/test/WrongCurvesSuite.java +++ b/src/cz/crcs/ectester/reader/test/CardWrongCurvesSuite.java @@ -12,9 +12,9 @@ import static cz.crcs.ectester.common.test.Result.ExpectedValue; /** * @author Jan Jancar johny@neuromancer.sk */ -public class WrongCurvesSuite extends TestSuite { +public class CardWrongCurvesSuite extends CardTestSuite { - public WrongCurvesSuite(EC_Store dataStore, ECTesterReader.Config cfg) { + public CardWrongCurvesSuite(EC_Store dataStore, ECTesterReader.Config cfg) { super(dataStore, cfg, "wrong", "The wrong curve suite tests whether the card rejects domain parameters which are not curves."); } diff --git a/src/cz/crcs/ectester/reader/test/CommandTest.java b/src/cz/crcs/ectester/reader/test/CommandTest.java new file mode 100644 index 0000000..a08d820 --- /dev/null +++ b/src/cz/crcs/ectester/reader/test/CommandTest.java @@ -0,0 +1,76 @@ +package cz.crcs.ectester.reader.test; + +import cz.crcs.ectester.common.test.Result; +import cz.crcs.ectester.common.test.SimpleTest; +import cz.crcs.ectester.common.test.TestCallback; +import cz.crcs.ectester.common.test.TestException; +import cz.crcs.ectester.reader.command.Command; +import cz.crcs.ectester.reader.response.Response; + +/** + * A simple test that runs one Command to get and evaluate one Response + * to get a Result and compare it with the expected one. + */ +public class CommandTest extends SimpleTest<CommandTestable> { + private CommandTest(CommandTestable command, TestCallback<CommandTestable> callback) { + super(command, callback); + } + + public static CommandTest function(CommandTestable command, TestCallback<CommandTestable> callback) { + return new CommandTest(command, callback); + } + + public static CommandTest function(Command command, TestCallback<CommandTestable> callback) { + return function(new CommandTestable(command), callback); + } + + public static CommandTest expect(CommandTestable command, Result.ExpectedValue expected, String ok, String nok) { + return new CommandTest(command, new TestCallback<CommandTestable>() { + @Override + public Result apply(CommandTestable commandTestable) { + Response resp = commandTestable.getResponse(); + Result.Value resultValue = Result.Value.fromExpected(expected, resp.successful(), resp.error()); + return new Result(resultValue, resultValue.ok() ? ok : nok); + } + }); + } + + public static CommandTest expect(Command command, Result.ExpectedValue expectedValue, String ok, String nok) { + return expect(new CommandTestable(command), expectedValue, ok, nok); + } + + public static CommandTest expect(CommandTestable command, Result.ExpectedValue expected) { + return expect(command, expected, null, null); + } + + public static CommandTest expect(Command command, Result.ExpectedValue expectedValue) { + return expect(command, expectedValue, null, null); + } + + public Command getCommand() { + return testable.getCommand(); + } + + public Response getResponse() { + return testable.getResponse(); + } + + @Override + public void run() throws TestException { + if (hasRun) + return; + + testable.run(); + result = callback.apply(testable); + hasRun = true; + } + + @Override + public String getDescription() { + if (hasRun) { + return testable.getResponse().getDescription(); + } else { + return testable.getCommand().toString(); + } + } +} diff --git a/src/cz/crcs/ectester/reader/test/CommandTestable.java b/src/cz/crcs/ectester/reader/test/CommandTestable.java new file mode 100644 index 0000000..4025c61 --- /dev/null +++ b/src/cz/crcs/ectester/reader/test/CommandTestable.java @@ -0,0 +1,47 @@ +package cz.crcs.ectester.reader.test; + +import cz.crcs.ectester.common.test.BaseTestable; +import cz.crcs.ectester.common.test.TestException; +import cz.crcs.ectester.reader.command.Command; +import cz.crcs.ectester.reader.response.Response; + +import javax.smartcardio.CardException; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class CommandTestable extends BaseTestable { + private Command command; + private Response response; + + public CommandTestable(Command command) { + this.command = command; + } + + public Command getCommand() { + return command; + } + + public Response getResponse() { + return response; + } + + @Override + public void run() throws TestException { + try { + response = command.send(); + } catch (CardException e) { + throw new TestException(e); + } + + hasRun = true; + if (response.error()) { + error = true; + } else if (response.successful()) { + ok = true; + } + meta.clear(); + meta.put("natural-sw", response.getNaturalSW()); + meta.put("sws", response.getSWs()); + } +} diff --git a/src/cz/crcs/ectester/reader/test/SimpleTest.java b/src/cz/crcs/ectester/reader/test/SimpleTest.java deleted file mode 100644 index 067f43e..0000000 --- a/src/cz/crcs/ectester/reader/test/SimpleTest.java +++ /dev/null @@ -1,71 +0,0 @@ -package cz.crcs.ectester.reader.test; - -import cz.crcs.ectester.common.test.Result; -import cz.crcs.ectester.common.test.Test; -import cz.crcs.ectester.common.test.TestException; -import cz.crcs.ectester.reader.command.Command; -import cz.crcs.ectester.reader.response.Response; - -import javax.smartcardio.CardException; -import java.util.function.BiFunction; - -/** - * A simple test that runs one Command to get and evaluate one Response - * to get a Result and compare it with the expected one. - */ -public class SimpleTest extends Test { - private BiFunction<Command, Response, Result> callback; - private Command command; - private Response response; - - public SimpleTest(Command command, BiFunction<Command, Response, Result> callback) { - this.command = command; - this.callback = callback; - } - - public SimpleTest(Command command, Result.ExpectedValue expected, String ok, String nok) { - this(command, (cmd, resp) -> { - Result.Value resultValue = Result.Value.fromExpected(expected, resp.successful(), resp.error()); - return new Result(resultValue, resultValue.ok() ? ok : nok); - }); - } - - public SimpleTest(Command command, Result.ExpectedValue expected) { - this(command, expected, null, null); - } - - public Command getCommand() { - return command; - } - - public Response getResponse() { - return response; - } - - @Override - public void run() throws TestException { - if (hasRun) - return; - - try { - response = command.send(); - } catch (CardException e) { - throw new TestException(e); - } - if (callback != null) { - result = callback.apply(command, response); - } else { - if (response.successful()) { - result = new Result(Result.Value.SUCCESS); - } else { - result = new Result(Result.Value.FAILURE); - } - } - hasRun = true; - } - - @Override - public String getDescription() { - return response.getDescription(); - } -} diff --git a/src/cz/crcs/ectester/standalone/ECTesterStandalone.java b/src/cz/crcs/ectester/standalone/ECTesterStandalone.java index d2cbce1..c3dfbbe 100644 --- a/src/cz/crcs/ectester/standalone/ECTesterStandalone.java +++ b/src/cz/crcs/ectester/standalone/ECTesterStandalone.java @@ -1,12 +1,36 @@ package cz.crcs.ectester.standalone; -import cz.crcs.ectester.common.Util; -import cz.crcs.ectester.applet.EC_Consts; +import cz.crcs.ectester.common.cli.*; import cz.crcs.ectester.common.ec.EC_Curve; +import cz.crcs.ectester.common.output.TextTestWriter; +import cz.crcs.ectester.common.test.TestException; +import cz.crcs.ectester.common.test.TestRunner; +import cz.crcs.ectester.common.util.ByteUtil; +import cz.crcs.ectester.common.util.ECUtil; import cz.crcs.ectester.data.EC_Store; -import org.apache.commons.cli.*; +import cz.crcs.ectester.standalone.consts.KeyAgreementIdent; +import cz.crcs.ectester.standalone.consts.KeyPairGeneratorIdent; +import cz.crcs.ectester.standalone.consts.SignatureIdent; +import cz.crcs.ectester.standalone.libs.*; +import cz.crcs.ectester.standalone.test.StandaloneDefaultSuite; +import cz.crcs.ectester.standalone.test.StandaloneTestSuite; +import org.apache.commons.cli.DefaultParser; +import org.apache.commons.cli.Option; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; +import javax.crypto.KeyAgreement; +import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; +import java.nio.file.Files; +import java.security.*; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.ECParameterSpec; +import java.util.*; +import java.util.stream.Collectors; /** * Standalone part of ECTester, a tool for testing Elliptic curve implementations in software libraries. @@ -15,11 +39,13 @@ import java.io.IOException; * @version v0.1.0 */ public class ECTesterStandalone { - + private ProviderECLibrary[] libs = new ProviderECLibrary[]{new SunECLib(), new BouncyCastleLib(), new TomcryptLib(), new BotanLib()}; private EC_Store dataStore; private Config cfg; private Options opts = new Options(); + private TreeParser optParser; + private TreeCommandLine cli; private static final String VERSION = "v0.1.0"; private static final String DESCRIPTION = "ECTesterStandalone " + VERSION + ", an Elliptic Curve Cryptography support tester/utility."; private static final String LICENSE = "MIT Licensed\nCopyright (c) 2016-2017 Petr Svenda <petr@svenda.com>"; @@ -28,64 +54,390 @@ public class ECTesterStandalone { private void run(String[] args) { try { - CommandLine cli = parseArgs(args); + cli = parseArgs(args); - if (cli.hasOption("help")) { - help(); + if (cli.hasOption("version")) { + CLITools.version(DESCRIPTION, LICENSE); return; - } else if (cli.hasOption("version")) { - version(); + } else if (cli.hasOption("help") || cli.getNext() == null) { + CLITools.help("ECTesterStandalone.jar", CLI_HEADER, opts, optParser, CLI_FOOTER, true); return; } - cfg = new Config(); + for (ECLibrary lib : libs) { + lib.initialize(); + } + + cfg = new Config(libs); + if (!cfg.readOptions(cli)) { + return; + } dataStore = new EC_Store(); - if (cli.hasOption("generate")) { + if (cli.isNext("list-libs")) { + listLibraries(); + } else if (cli.isNext("list-data")) { + CLITools.listNamed(dataStore, cli.getNext().getArg(0)); + } else if (cli.isNext("ecdh")) { + ecdh(); + } else if (cli.isNext("ecdsa")) { + ecdsa(); + } else if (cli.isNext("generate")) { generate(); + } else if (cli.isNext("test")) { + test(); + } else if (cli.isNext("export")) { + export(); } } catch (ParseException | IOException ex) { System.err.println(ex.getMessage()); + } catch (InvalidAlgorithmParameterException | InvalidParameterException e) { + System.err.println("Invalid algorithm parameter: " + e.getMessage()); + } catch (NoSuchAlgorithmException nsaex) { + System.err.println("Algorithm not supported by the selected library: " + nsaex.getMessage()); + nsaex.printStackTrace(); + } catch (InvalidKeyException | SignatureException | TestException e) { + e.printStackTrace(); + } + } + + private TreeCommandLine parseArgs(String[] args) throws ParseException { + Map<String, ParserOptions> actions = new TreeMap<>(); + + Options testOpts = new Options(); + ParserOptions test = new ParserOptions(new DefaultParser(), testOpts); + testOpts.addOption(Option.builder("gt").longOpt("kpg-type").desc("Set the KeyPairGenerator object [type].").hasArg().argName("type").optionalArg(false).build()); + testOpts.addOption(Option.builder("kt").longOpt("ka-type").desc("Set the KeyAgreement object [type].").hasArg().argName("type").optionalArg(false).build()); + testOpts.addOption(Option.builder("st").longOpt("sig-type").desc("Set the Signature object [type].").hasArg().argName("type").optionalArg(false).build()); + testOpts.addOption(Option.builder("b").longOpt("bits").hasArg().argName("n").optionalArg(false).desc("What size of curve to use.").build()); + testOpts.addOption(Option.builder("nc").longOpt("named-curve").desc("Use a named curve, from CurveDB: <cat/id>").hasArg().argName("cat/id").build()); + actions.put("test", test); + + Options ecdhOpts = new Options(); + ecdhOpts.addOption(Option.builder("t").longOpt("type").desc("Set KeyAgreement object [type].").hasArg().argName("type").optionalArg(false).build()); + ecdhOpts.addOption(Option.builder("n").longOpt("amount").hasArg().argName("amount").optionalArg(false).desc("Do ECDH [amount] times.").build()); + ecdhOpts.addOption(Option.builder("b").longOpt("bits").hasArg().argName("n").optionalArg(false).desc("What size of curve to use.").build()); + ecdhOpts.addOption(Option.builder("nc").longOpt("named-curve").desc("Use a named curve, from CurveDB: <cat/id>").hasArg().argName("cat/id").build()); + ParserOptions ecdh = new ParserOptions(new DefaultParser(), ecdhOpts); + actions.put("ecdh", ecdh); + + Options ecdsaOpts = new Options(); + ecdsaOpts.addOption(Option.builder("t").longOpt("type").desc("Set Signature object [type].").hasArg().argName("type").optionalArg(false).build()); + ecdsaOpts.addOption(Option.builder("n").longOpt("amount").hasArg().argName("amount").optionalArg(false).desc("Do ECDSA [amount] times.").build()); + ecdsaOpts.addOption(Option.builder("b").longOpt("bits").hasArg().argName("n").optionalArg(false).desc("What size of curve to use.").build()); + ecdsaOpts.addOption(Option.builder("nc").longOpt("named-curve").desc("Use a named curve, from CurveDB: <cat/id>").hasArg().argName("cat/id").build()); + ecdsaOpts.addOption(Option.builder("f").longOpt("file").hasArg().argName("file").optionalArg(false).desc("Input [file] to sign.").build()); + ParserOptions ecdsa = new ParserOptions(new DefaultParser(), ecdsaOpts); + actions.put("ecdsa", ecdsa); + + Options generateOpts = new Options(); + generateOpts.addOption(Option.builder("nc").longOpt("named-curve").desc("Use a named curve, from CurveDB: <cat/id>").hasArg().argName("cat/id").build()); + generateOpts.addOption(Option.builder("n").longOpt("amount").hasArg().argName("amount").optionalArg(false).desc("Generate [amount] of EC keys.").build()); + generateOpts.addOption(Option.builder("t").longOpt("type").hasArg().argName("type").optionalArg(false).desc("Set KeyPairGenerator object [type].").build()); + generateOpts.addOption(Option.builder("b").longOpt("bits").hasArg().argName("n").optionalArg(false).desc("What size of curve to use.").build()); + ParserOptions generate = new ParserOptions(new DefaultParser(), generateOpts); + actions.put("generate", generate); + + Options exportOpts = new Options(); + exportOpts.addOption(Option.builder("t").longOpt("type").hasArg().argName("type").optionalArg(false).desc("Set KeyPair object [type].").build()); + exportOpts.addOption(Option.builder("b").longOpt("bits").hasArg().argName("n").optionalArg(false).desc("What size of curve to use.").build()); + ParserOptions export = new ParserOptions(new DefaultParser(), exportOpts); + actions.put("export", export); + + Options listDataOpts = new Options(); + List<Argument> listDataArgs = new LinkedList<>(); + listDataArgs.add(new Argument("what", "what to list.", false)); + ParserOptions listData = new ParserOptions(new TreeParser(Collections.emptyMap(), false, listDataArgs), listDataOpts); + actions.put("list-data", listData); + + Options listLibsOpts = new Options(); + ParserOptions listLibs = new ParserOptions(new DefaultParser(), listLibsOpts); + actions.put("list-libs", listLibs); + + List<Argument> baseArgs = new LinkedList<>(); + baseArgs.add(new Argument("lib", "What library to use.", false)); + optParser = new TreeParser(actions, false, baseArgs); + + opts.addOption(Option.builder("V").longOpt("version").desc("Print version info.").build()); + opts.addOption(Option.builder("h").longOpt("help").desc("Print help.").build()); + + return optParser.parse(opts, args); + } + + /** + * + */ + private void listLibraries() { + for (ECLibrary lib : libs) { + if (lib.isInitialized() && (cfg.selected == null || lib == cfg.selected)) { + System.out.println("\t- " + lib.name()); + Set<KeyPairGeneratorIdent> kpgs = lib.getKPGs(); + if (!kpgs.isEmpty()) { + System.out.println("\t\t- KeyPairGenerators: " + String.join(",", kpgs.stream().map(KeyPairGeneratorIdent::getName).collect(Collectors.toList()))); + } + Set<KeyAgreementIdent> eckas = lib.getKAs(); + if (!eckas.isEmpty()) { + System.out.println("\t\t- KeyAgreements: " + String.join(",", eckas.stream().map(KeyAgreementIdent::getName).collect(Collectors.toList()))); + } + Set<SignatureIdent> sigs = lib.getSigs(); + if (!sigs.isEmpty()) { + System.out.println("\t\t- Signatures: " + String.join(",", sigs.stream().map(SignatureIdent::getName).collect(Collectors.toList()))); + } + Set<String> curves = lib.getCurves(); + if (!curves.isEmpty()) { + System.out.println("\t\t- Curves: " + String.join(",", curves)); + } + System.out.println(); + } } } - private CommandLine parseArgs(String[] args) throws ParseException { - OptionGroup actions = new OptionGroup(); - actions.setRequired(true); - actions.addOption(Option.builder("V").longOpt("version").desc("Print version info.").build()); - actions.addOption(Option.builder("h").longOpt("help").desc("Print help.").build()); - actions.addOption(Option.builder("g").longOpt("generate").desc("Generate [amount] of EC keys.").hasArg().argName("amount").optionalArg(true).build()); - opts.addOptionGroup(actions); + /** + * + */ + private void ecdh() throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException { + ProviderECLibrary lib = cfg.selected; + + String algo = cli.getOptionValue("ecdh.type", "ECDH"); + KeyAgreementIdent kaIdent = lib.getKAs().stream() + .filter((ident) -> ident.contains(algo)) + .findFirst() + .orElse(null); + + KeyPairGeneratorIdent kpIdent = lib.getKPGs().stream() + .filter((ident) -> ident.contains(algo)) + .findFirst() + .orElse(lib.getKPGs().stream() + .filter((ident) -> ident.contains("ECDH")) + .findFirst() + .orElse(lib.getKPGs().stream() + .filter((ident) -> ident.contains("EC")) + .findFirst() + .orElse(null))); + + + if (kaIdent == null || kpIdent == null) { + throw new NoSuchAlgorithmException(algo); + } else { + KeyAgreement ka = kaIdent.getInstance(lib.getProvider()); + KeyPairGenerator kpg = kpIdent.getInstance(lib.getProvider()); + AlgorithmParameterSpec spec = null; + if (cli.hasOption("ecdh.bits")) { + int bits = Integer.parseInt(cli.getOptionValue("ecdh.bits")); + kpg.initialize(bits); + } else if (cli.hasOption("ecdh.named-curve")) { + String curveName = cli.getOptionValue("ecdh.named-curve"); + EC_Curve curve = dataStore.getObject(EC_Curve.class, curveName); + if (curve == null) { + System.err.println("Curve not found: " + curveName); + return; + } + spec = curve.toSpec(); + kpg.initialize(spec); + }//TODO: allow ECGenNamedSpec + + System.out.println("index;nanotime;pubW;privS;secret"); - CommandLineParser parser = new DefaultParser(); - return parser.parse(opts, args); + int amount = Integer.parseInt(cli.getOptionValue("ecdh.amount", "1")); + for (int i = 0; i < amount; ++i) { + KeyPair one = kpg.genKeyPair(); + KeyPair other = kpg.genKeyPair(); + + ECPrivateKey privkey = (ECPrivateKey) one.getPrivate(); + ECPublicKey pubkey = (ECPublicKey) other.getPublic(); + + long elapsed = -System.nanoTime(); + if (spec != null) { + ka.init(privkey, spec); + } else { + ka.init(privkey); + } + ka.doPhase(pubkey, true); + elapsed += System.nanoTime(); + byte[] result = ka.generateSecret(); + ka = kaIdent.getInstance(lib.getProvider()); + + String pub = ByteUtil.bytesToHex(ECUtil.toX962Uncompressed(pubkey.getW(), pubkey.getParams()), false); + String priv = ByteUtil.bytesToHex(privkey.getS().toByteArray(), false); + String dh = ByteUtil.bytesToHex(result, false); + System.out.println(String.format("%d;%d;%s;%s;%s", i, elapsed, pub, priv, dh)); + } + } } /** - * Prints help. + * */ - private void help() { - HelpFormatter help = new HelpFormatter(); - help.setOptionComparator(null); - help.printHelp("ECTesterStandalone.jar", CLI_HEADER, opts, CLI_FOOTER, true); + private void ecdsa() throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, IOException, SignatureException { + byte[] data; + String dataString; + if (cli.hasOption("ecdsa.file")) { + String fileName = cli.getOptionValue("ecdsa.file"); + File in = new File(fileName); + long len = in.length(); + if (len == 0) { + throw new FileNotFoundException(fileName); + } + data = Files.readAllBytes(in.toPath()); + dataString = ""; + } else { + SecureRandom random = new SecureRandom(); + data = new byte[32]; + random.nextBytes(data); + dataString = ByteUtil.bytesToHex(data, false); + } + + ProviderECLibrary lib = cfg.selected; + + String algo = cli.getOptionValue("ecdsa.type", "ECDSA"); + SignatureIdent sigIdent = lib.getSigs().stream() + .filter((ident) -> ident.contains(algo)) + .findFirst() + .orElse(null); + + KeyPairGeneratorIdent kpIdent = lib.getKPGs().stream() + .filter((ident) -> ident.contains(algo)) + .findFirst() + .orElse(lib.getKPGs().stream() + .filter((ident) -> ident.contains("EC")) + .findFirst() + .orElse(null)); + + if (sigIdent == null || kpIdent == null) { + throw new NoSuchAlgorithmException(algo); + } else { + Signature sig = sigIdent.getInstance(lib.getProvider()); + KeyPairGenerator kpg = kpIdent.getInstance(lib.getProvider()); + if (cli.hasOption("ecdsa.bits")) { + int bits = Integer.parseInt(cli.getOptionValue("ecdsa.bits")); + kpg.initialize(bits); + } else if (cli.hasOption("ecdsa.named-curve")) { + String curveName = cli.getOptionValue("ecdsa.named-curve"); + EC_Curve curve = dataStore.getObject(EC_Curve.class, curveName); + if (curve == null) { + System.err.println("Curve not found: " + curveName); + return; + } + kpg.initialize(curve.toSpec()); + } + + System.out.println("index;data;signtime;verifytime;pubW;privS;signature;verified"); + + int amount = Integer.parseInt(cli.getOptionValue("ecdsa.amount", "1")); + for (int i = 0; i < amount; ++i) { + KeyPair one = kpg.genKeyPair(); + + ECPrivateKey privkey = (ECPrivateKey) one.getPrivate(); + ECPublicKey pubkey = (ECPublicKey) one.getPublic(); + + sig.initSign(privkey); + sig.update(data); + + long signTime = -System.nanoTime(); + byte[] signature = sig.sign(); + signTime += System.nanoTime(); + + sig.initVerify(pubkey); + sig.update(data); + + long verifyTime = -System.nanoTime(); + boolean verified = sig.verify(signature); + verifyTime += System.nanoTime(); + + + String pub = ByteUtil.bytesToHex(ECUtil.toX962Uncompressed(pubkey.getW(), pubkey.getParams()), false); + String priv = ByteUtil.bytesToHex(privkey.getS().toByteArray(), false); + String sign = ByteUtil.bytesToHex(signature, false); + System.out.println(String.format("%d;%s;%d;%d;%s;%s;%s;%d", i, dataString, signTime, verifyTime, pub, priv, sign, verified ? 1 : 0)); + } + } } /** - * Prints version info. + * */ - private void version() { - System.out.println(DESCRIPTION); - System.out.println(LICENSE); + private void generate() throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { + ProviderECLibrary lib = cfg.selected; + KeyPairGeneratorIdent ident = null; + String algo = cli.getOptionValue("generate.type", "EC"); + for (KeyPairGeneratorIdent kpIdent : lib.getKPGs()) { + if (kpIdent.contains(algo)) { + ident = kpIdent; + break; + } + } + if (ident == null) { + throw new NoSuchAlgorithmException(algo); + } else { + KeyPairGenerator kpg = ident.getInstance(lib.getProvider()); + if (cli.hasOption("generate.bits")) { + int bits = Integer.parseInt(cli.getOptionValue("generate.bits")); + kpg.initialize(bits); + } else if (cli.hasOption("generate.named-curve")) { + String curveName = cli.getOptionValue("generate.named-curve"); + EC_Curve curve = dataStore.getObject(EC_Curve.class, curveName); + if (curve == null) { + System.err.println("Curve not found: " + curveName); + return; + } + kpg.initialize(curve.toSpec()); + } + System.out.println("index;nanotime;pubW;privS"); + + int amount = Integer.parseInt(cli.getOptionValue("generate.amount", "1")); + for (int i = 0; i < amount; ++i) { + long elapsed = -System.nanoTime(); + KeyPair kp = kpg.genKeyPair(); + elapsed += System.nanoTime(); + ECPublicKey publicKey = (ECPublicKey) kp.getPublic(); + ECPrivateKey privateKey = (ECPrivateKey) kp.getPrivate(); + + String pub = ByteUtil.bytesToHex(ECUtil.toX962Uncompressed(publicKey.getW(), publicKey.getParams()), false); + String priv = ByteUtil.bytesToHex(privateKey.getS().toByteArray(), false); + System.out.println(String.format("%d;%d;%s;%s", i, elapsed, pub, priv)); + } + } } /** * */ - private void generate() { - EC_Curve curve = dataStore.getObject(EC_Curve.class, "secg/secp192r1"); - byte[] fp = curve.getParam(EC_Consts.PARAMETER_FP)[0]; + private void test() throws NoSuchAlgorithmException, TestException { + StandaloneTestSuite suite = new StandaloneDefaultSuite(dataStore, cfg, cli); + TestRunner runner = new TestRunner(suite, new TextTestWriter(System.out)); + suite.setup(); + runner.run(); + } + /** + * + */ + private void export() throws NoSuchAlgorithmException, IOException { + ProviderECLibrary lib = (ProviderECLibrary) cfg.selected; + KeyPairGeneratorIdent ident = null; + String algo = cli.getOptionValue("export.type", "EC"); + for (KeyPairGeneratorIdent kpIdent : lib.getKPGs()) { + if (kpIdent.contains(algo)) { + ident = kpIdent; + break; + } + } + if (ident == null) { + throw new NoSuchAlgorithmException(algo); + } else { + KeyPairGenerator kpg = ident.getInstance(lib.getProvider()); + if (cli.hasOption("export.bits")) { + int bits = Integer.parseInt(cli.getOptionValue("export.bits")); + kpg.initialize(bits); + } + KeyPair kp = kpg.genKeyPair(); + ECPrivateKey privateKey = (ECPrivateKey) kp.getPrivate(); + ECParameterSpec params = privateKey.getParams(); + System.out.println(params); + EC_Curve curve = EC_Curve.fromSpec(params); + curve.writeCSV(System.out); + } } public static void main(String[] args) { @@ -93,7 +445,52 @@ public class ECTesterStandalone { app.run(args); } + + /** + * + */ public static class Config { + private ProviderECLibrary[] libs; + public ProviderECLibrary selected = null; + + public Config(ProviderECLibrary[] libs) { + this.libs = libs; + } + + boolean readOptions(TreeCommandLine cli) { + if (cli.isNext("generate") || cli.isNext("export") || cli.isNext("ecdh") || cli.isNext("ecdsa") || cli.isNext("test")) { + if (!cli.hasArg(-1)) { + System.err.println("Missing library name argument."); + return false; + } + String next = cli.getNextName(); + if (cli.hasOption(next + ".bits") && cli.hasOption(next + ".named-curve")) { + System.err.println("You can only specify bitsize or a named curve, nor both."); + return false; + } + } + + String libraryName = cli.getArg(-1); + if (libraryName != null) { + List<ProviderECLibrary> matchedLibs = new LinkedList<>(); + for (ProviderECLibrary lib : libs) { + if (lib.name().toLowerCase().contains(libraryName.toLowerCase())) { + matchedLibs.add(lib); + } + } + if (matchedLibs.size() == 0) { + System.err.println("No library " + libraryName + " found."); + return false; + } else if (matchedLibs.size() > 1) { + System.err.println("Multiple matching libraries found: " + String.join(",", matchedLibs.stream().map(ECLibrary::name).collect(Collectors.toList()))); + return false; + } else { + selected = matchedLibs.get(0); + } + } + + return true; + } } } diff --git a/src/cz/crcs/ectester/standalone/consts/Ident.java b/src/cz/crcs/ectester/standalone/consts/Ident.java new file mode 100644 index 0000000..40a44ac --- /dev/null +++ b/src/cz/crcs/ectester/standalone/consts/Ident.java @@ -0,0 +1,80 @@ +package cz.crcs.ectester.standalone.consts; + +import java.security.NoSuchAlgorithmException; +import java.security.Provider; +import java.util.Arrays; +import java.util.Collections; +import java.util.Set; +import java.util.TreeSet; +import java.util.function.BiFunction; + +public abstract class Ident { + Set<String> idents; + String name; + + public Ident(String name, String... aliases) { + this.name = name; + this.idents = new TreeSet<>(String.CASE_INSENSITIVE_ORDER); + this.idents.add(name); + this.idents.addAll(Arrays.asList(aliases)); + } + + public String getName() { + return name; + } + + public Set<String> getIdents() { + return Collections.unmodifiableSet(idents); + } + + public boolean contains(String other) { + return name.equals(other) || idents.contains(other); + } + + <T> T getInstance(BiFunction<String, Provider, T> getter, Provider provider) throws NoSuchAlgorithmException { + T instance = null; + try { + instance = getter.apply(name, provider); + } catch (Exception ignored) { + } + + if (instance == null) { + for (String alias : idents) { + try { + instance = getter.apply(alias, provider); + if (instance != null) { + break; + } + } catch (Exception ignored) { + } + } + } + + if (instance == null) { + throw new NoSuchAlgorithmException(name); + } + return instance; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof Ident)) { + return false; + } + Ident other = (Ident) obj; + return idents.equals(other.getIdents()); + } + + @Override + public int hashCode() { + return idents.hashCode() + 37; + } + + @Override + public String toString() { + return "(" + String.join("|", idents) + ")"; + } +} diff --git a/src/cz/crcs/ectester/standalone/consts/KeyAgreementIdent.java b/src/cz/crcs/ectester/standalone/consts/KeyAgreementIdent.java new file mode 100644 index 0000000..a5d3578 --- /dev/null +++ b/src/cz/crcs/ectester/standalone/consts/KeyAgreementIdent.java @@ -0,0 +1,68 @@ +package cz.crcs.ectester.standalone.consts; + +import javax.crypto.KeyAgreement; +import java.security.NoSuchAlgorithmException; +import java.security.Provider; +import java.util.LinkedList; +import java.util.List; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class KeyAgreementIdent extends Ident { + private static final List<KeyAgreementIdent> ALL = new LinkedList<>(); + + static { + //https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html + // Basic ECDH and ECDHC (plain/raw) + ALL.add(new KeyAgreementIdent("ECDH")); + ALL.add(new KeyAgreementIdent("ECDHC", "ECCDH")); + // ECDH and ECDHC with SHA as KDF, OIDs from RFC 3278 + ALL.add(new KeyAgreementIdent("ECDHwithSHA1KDF", "1.3.133.16.840.63.0.2")); + ALL.add(new KeyAgreementIdent("ECCDHwithSHA1KDF", "1.3.133.16.840.63.0.3")); + ALL.add(new KeyAgreementIdent("ECDHwithSHA224KDF", "1.3.132.1.11.0")); + ALL.add(new KeyAgreementIdent("ECCDHwithSHA224KDF", "1.3.132.1.14.0")); + ALL.add(new KeyAgreementIdent("ECDHwithSHA256KDF", "1.3.132.1.11.1")); + ALL.add(new KeyAgreementIdent("ECCDHwithSHA256KDF", "1.3.132.1.14.1")); + ALL.add(new KeyAgreementIdent("ECDHwithSHA384KDF", "1.3.132.1.11.2")); + ALL.add(new KeyAgreementIdent("ECCDHwithSHA384KDF", "1.3.132.1.14.2")); + ALL.add(new KeyAgreementIdent("ECDHwithSHA512KDF", "1.3.132.1.11.3")); + ALL.add(new KeyAgreementIdent("ECCDHwithSHA512KDF", "1.3.132.1.14.3")); + // ECMQV - Disable for now as it needs diferent params(too different from DH) + //ALL.add(new KeyAgreementIdent("ECMQV")); + //ALL.add(new KeyAgreementIdent("ECMQVwithSHA1CKDF", "1.3.133.16.840.63.0.16")); + //ALL.add(new KeyAgreementIdent("ECMQVwithSHA224CKDF", "1.3.132.1.15.0")); + //ALL.add(new KeyAgreementIdent("ECMQVwithSHA256CKDF", "1.3.132.1.15.1")); + //ALL.add(new KeyAgreementIdent("ECMQVwithSHA384CKDF", "1.3.132.1.15.2")); + //ALL.add(new KeyAgreementIdent("ECMQVwithSHA512CKDF", "1.3.132.1.15.3")); + // ECVKO + ALL.add(new KeyAgreementIdent("ECVKO", "ECGOST3410", "1.2.643.2.2.19", "GOST-3410-2001", "1.2.643.2.2.96")); + ALL.add(new KeyAgreementIdent("ECVKO256", "ECGOST3410-2012-256", "1.2.643.7.1.1.6.1", "1.2.643.7.1.1.1.1")); + ALL.add(new KeyAgreementIdent("ECVKO512", "ECGOST3410-2012-512", "1.2.643.7.1.1.6.2", "1.2.643.7.1.1.1.2")); + } + + public static KeyAgreementIdent get(String ident) { + for (KeyAgreementIdent ka : ALL) { + if (ka.getIdents().contains(ident)) { + return ka; + } + } + return null; + } + + private KeyAgreementIdent(String name, String... aliases) { + super(name, aliases); + } + + public KeyAgreement getInstance(Provider provider) throws NoSuchAlgorithmException { + KeyAgreement instance = getInstance((algorithm, provider1) -> { + try { + return KeyAgreement.getInstance(algorithm, provider1); + } catch (NoSuchAlgorithmException e) { + return null; + } + }, provider); + instance.getProvider(); + return instance; + } +} diff --git a/src/cz/crcs/ectester/standalone/consts/KeyPairGeneratorIdent.java b/src/cz/crcs/ectester/standalone/consts/KeyPairGeneratorIdent.java new file mode 100644 index 0000000..8e67967 --- /dev/null +++ b/src/cz/crcs/ectester/standalone/consts/KeyPairGeneratorIdent.java @@ -0,0 +1,50 @@ +package cz.crcs.ectester.standalone.consts; + +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.Provider; +import java.util.LinkedList; +import java.util.List; + +public class KeyPairGeneratorIdent extends Ident { + private static final List<KeyPairGeneratorIdent> ALL = new LinkedList<>(); + + static { + ALL.add(new KeyPairGeneratorIdent("EC")); + ALL.add(new KeyPairGeneratorIdent("ECDH")); + ALL.add(new KeyPairGeneratorIdent("ECDSA")); + ALL.add(new KeyPairGeneratorIdent("ECDHC")); + ALL.add(new KeyPairGeneratorIdent("ECMQV")); + ALL.add(new KeyPairGeneratorIdent("ECGOST3410")); + ALL.add(new KeyPairGeneratorIdent("ECGOST3410-2012")); + // ECKCDSA? Botan provides. + ALL.add(new KeyPairGeneratorIdent("ECKCDSA")); + // ECGDSA? Botan provides. + ALL.add(new KeyPairGeneratorIdent("ECGDSA")); + } + + public static KeyPairGeneratorIdent get(String ident) { + for (KeyPairGeneratorIdent kg : ALL) { + if (kg.getIdents().contains(ident)) { + return kg; + } + } + return null; + } + + public KeyPairGeneratorIdent(String name, String... aliases) { + super(name, aliases); + } + + public KeyPairGenerator getInstance(Provider provider) throws NoSuchAlgorithmException { + KeyPairGenerator instance = getInstance((algorithm, provider1) -> { + try { + return KeyPairGenerator.getInstance(algorithm, provider1); + } catch (NoSuchAlgorithmException e) { + return null; + } + }, provider); + instance.getProvider(); + return instance; + } +} diff --git a/src/cz/crcs/ectester/standalone/consts/SignatureIdent.java b/src/cz/crcs/ectester/standalone/consts/SignatureIdent.java new file mode 100644 index 0000000..42ff050 --- /dev/null +++ b/src/cz/crcs/ectester/standalone/consts/SignatureIdent.java @@ -0,0 +1,109 @@ +package cz.crcs.ectester.standalone.consts; + +import java.security.NoSuchAlgorithmException; +import java.security.Provider; +import java.security.Signature; +import java.util.LinkedList; +import java.util.List; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class SignatureIdent extends Ident { + private static final List<SignatureIdent> ALL = new LinkedList<>(); + + static { + //https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html + // ECDSA + ALL.add(new SignatureIdent("ECDSA", "SHA1withECDSA", "ECDSAwithSHA1", "1.2.840.10045.4.1", "1.3.36.3.3.2.1")); + ALL.add(new SignatureIdent("NONEwithECDSA")); + ALL.add(new SignatureIdent("SHA224withECDSA", "SHA224/ECDSA", "1.2.840.10045.4.3.1")); + ALL.add(new SignatureIdent("SHA256withECDSA", "SHA256/ECDSA", "1.2.840.10045.4.3.2")); + ALL.add(new SignatureIdent("SHA384withECDSA", "SHA384/ECDSA", "1.2.840.10045.4.3.3")); + ALL.add(new SignatureIdent("SHA512withECDSA", "SHA512/ECDSA", "1.2.840.10045.4.3.4")); + ALL.add(new SignatureIdent("SHA3-224withECDSA", "SHA3-224/ECDSA", "2.16.840.1.101.3.4.3.9")); + ALL.add(new SignatureIdent("SHA3-256withECDSA", "SHA3-256/ECDSA", "2.16.840.1.101.3.4.3.10")); + ALL.add(new SignatureIdent("SHA3-384withECDSA", "SHA3-384/ECDSA", "2.16.840.1.101.3.4.3.11")); + ALL.add(new SignatureIdent("SHA3-512withECDSA", "SHA3-512/ECDSA", "2.16.840.1.101.3.4.3.12")); + ALL.add(new SignatureIdent("RIPEMD160withECDSA", "RIPEMD160/ECDSA", "1.3.36.3.3.2.2")); + // ECNR + ALL.add(new SignatureIdent("SHA1withECNR")); + ALL.add(new SignatureIdent("SHA224withECNR")); + ALL.add(new SignatureIdent("SHA256withECNR")); + ALL.add(new SignatureIdent("SHA512withECNR")); + // CVC-ECDSA + ALL.add(new SignatureIdent("SHA1withCVC-ECDSA", "SHA1/CVC-ECDSA", "0.4.0.127.0.7.2.2.2.2.1")); + ALL.add(new SignatureIdent("SHA224withCVC-ECDSA", "SHA224/CVC-ECDSA", "0.4.0.127.0.7.2.2.2.2.2")); + ALL.add(new SignatureIdent("SHA256withCVC-ECDSA", "SHA256/CVC-ECDSA", "0.4.0.127.0.7.2.2.2.2.3")); + ALL.add(new SignatureIdent("SHA384withCVC-ECDSA", "SHA384/CVC-ECDSA", "0.4.0.127.0.7.2.2.2.2.4")); + ALL.add(new SignatureIdent("SHA512withCVC-ECDSA", "SHA512/CVC-ECDSA", "0.4.0.127.0.7.2.2.2.2.5")); + // PLAIN-ECDSA + ALL.add(new SignatureIdent("SHA1withPLAIN-ECDSA", "SHA1/PLAIN-ECDSA", "0.4.0.127.0.7.1.1.4.1.1")); + ALL.add(new SignatureIdent("SHA224withPLAIN-ECDSA", "SHA224/PLAIN-ECDSA", "0.4.0.127.0.7.1.1.4.1.2")); + ALL.add(new SignatureIdent("SHA256withPLAIN-ECDSA", "SHA256/PLAIN-ECDSA", "0.4.0.127.0.7.1.1.4.1.3")); + ALL.add(new SignatureIdent("SHA384withPLAIN-ECDSA", "SHA384/PLAIN-ECDSA", "0.4.0.127.0.7.1.1.4.1.4")); + ALL.add(new SignatureIdent("SHA512withPLAIN-ECDSA", "SHA512/PLAIN-ECDSA", "0.4.0.127.0.7.1.1.4.1.5")); + ALL.add(new SignatureIdent("RIPEMD160withPLAIN-ECDSA", "RIPEMD160/PLAIN-ECDSA", "0.4.0.127.0.7.1.1.4.1.6")); + // ECGOST + ALL.add(new SignatureIdent("ECGOST3410", "ECGOST-3410", "GOST-3410-2001")); + ALL.add(new SignatureIdent("GOST3411withECGOST3410", "GOST3411/ECGOST3410", "1.2.643.2.2.3")); + ALL.add(new SignatureIdent("ECGOST3410-2012-256", "GOST-3410-2012-256")); + ALL.add(new SignatureIdent("GOST3411-2012-256withECGOST3410-2012-256", "GOST3411-2012-256/ECGOST3410-2012-2560", "1.2.643.7.1.1.3.2")); + ALL.add(new SignatureIdent("ECGOST3410-2012-512", "GOST-3410-2012-512")); + ALL.add(new SignatureIdent("GOST3411-2012-512withECGOST3410-2012-512", "GOST3411-2012-512/ECGOST3410-2012-5120", "1.2.643.7.1.1.3.3")); + ALL.add(new SignatureIdent("SM3withSM2")); + // ECDDSA + ALL.add(new SignatureIdent("ECDDSA", "DETECDSA", "ECDETDSA")); + ALL.add(new SignatureIdent("SHA1withECDDSA", "SHA1withDETECDSA")); + ALL.add(new SignatureIdent("SHA224withECDDSA", "SHA224withDETECDSA")); + ALL.add(new SignatureIdent("SHA256withECDDSA", "SHA256withDETECDSA")); + ALL.add(new SignatureIdent("SHA384withECDDSA", "SHA384withDETECDSA")); + ALL.add(new SignatureIdent("SHA512withECDDSA", "SHA512withDETECDSA")); + ALL.add(new SignatureIdent("SHA3-224withECDDSA", "SHA3-224withDETECDSA")); + ALL.add(new SignatureIdent("SHA3-256withECDDSA", "SHA3-256withDETECDSA")); + ALL.add(new SignatureIdent("SHA3-384withECDDSA", "SHA3-384withDETECDSA")); + ALL.add(new SignatureIdent("SHA3-512withECDDSA", "SHA3-512withDETECDSA")); + // ECKCDSA? Botan provides. + ALL.add(new SignatureIdent("ECKCDSA","SHA1withECKCDSA", "1.2.410.200004.1.100.4.3")); + ALL.add(new SignatureIdent("NONEwithECKCDSA")); + ALL.add(new SignatureIdent("RIPEMD160withECKCDSA")); + ALL.add(new SignatureIdent("SHA224withECKCDSA", "1.2.410.200004.1.100.4.4")); + ALL.add(new SignatureIdent("SHA256withECKCDSA", "1.2.410.200004.1.100.4.5")); + ALL.add(new SignatureIdent("SHA384withECKCDSA")); + ALL.add(new SignatureIdent("SHA512withECKCDSA")); + // ECGDSA? Botan provides. + ALL.add(new SignatureIdent("ECGDSA", "SHA1withECGDSA", "1.3.36.3.3.2.5.4.2")); + ALL.add(new SignatureIdent("NONEwithECGDSA")); + ALL.add(new SignatureIdent("RIPEMD160withECGDSA", "1.3.36.3.3.2.5.4.1")); + ALL.add(new SignatureIdent("SHA224withECGDSA", "1.3.36.3.3.2.5.4.3")); + ALL.add(new SignatureIdent("SHA224withECGDSA", "1.3.36.3.3.2.5.4.4")); + ALL.add(new SignatureIdent("SHA384withECGDSA", "1.3.36.3.3.2.5.4.5")); + ALL.add(new SignatureIdent("SHA512withECGDSA", "1.3.36.3.3.2.5.4.6")); + + } + + public static SignatureIdent get(String ident) { + for (SignatureIdent sig : ALL) { + if (sig.getIdents().contains(ident)) { + return sig; + } + } + return null; + } + + private SignatureIdent(String name, String... aliases) { + super(name, aliases); + } + + public Signature getInstance(Provider provider) throws NoSuchAlgorithmException { + Signature instance = getInstance((algorithm, provider1) -> { + try { + return Signature.getInstance(algorithm, provider1); + } catch (NoSuchAlgorithmException e) { + return null; + } + }, provider); + instance.getProvider(); + return instance; + } +} diff --git a/src/cz/crcs/ectester/standalone/libs/BotanLib.java b/src/cz/crcs/ectester/standalone/libs/BotanLib.java new file mode 100644 index 0000000..cd28791 --- /dev/null +++ b/src/cz/crcs/ectester/standalone/libs/BotanLib.java @@ -0,0 +1,20 @@ +package cz.crcs.ectester.standalone.libs; + +import java.security.Provider; +import java.util.Set; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class BotanLib extends NativeECLibrary { + + public BotanLib() { + super("botan_provider", "botan-2"); + } + + @Override + native Provider createProvider(); + + @Override + public native Set<String> getCurves(); +} diff --git a/src/cz/crcs/ectester/standalone/libs/BouncyCastleLib.java b/src/cz/crcs/ectester/standalone/libs/BouncyCastleLib.java index 78da737..c6600f9 100644 --- a/src/cz/crcs/ectester/standalone/libs/BouncyCastleLib.java +++ b/src/cz/crcs/ectester/standalone/libs/BouncyCastleLib.java @@ -1,21 +1,28 @@ package cz.crcs.ectester.standalone.libs; + +import org.bouncycastle.jce.ECNamedCurveTable; import org.bouncycastle.jce.provider.BouncyCastleProvider; -import java.security.Security; +import java.util.Enumeration; +import java.util.Set; +import java.util.TreeSet; -public class BouncyCastleLib { +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class BouncyCastleLib extends ProviderECLibrary { public BouncyCastleLib() { - + super(new BouncyCastleProvider()); } - public boolean setUp() { - try { - Security.addProvider(new BouncyCastleProvider()); - } catch (NullPointerException | SecurityException ignored) { - return false; + @Override + public Set<String> getCurves() { + Set<String> result = new TreeSet<>(); + Enumeration names = ECNamedCurveTable.getNames(); + while (names.hasMoreElements()) { + result.add((String) names.nextElement()); } - return true; + return result; } - } diff --git a/src/cz/crcs/ectester/standalone/libs/ECLibrary.java b/src/cz/crcs/ectester/standalone/libs/ECLibrary.java new file mode 100644 index 0000000..0f81978 --- /dev/null +++ b/src/cz/crcs/ectester/standalone/libs/ECLibrary.java @@ -0,0 +1,26 @@ +package cz.crcs.ectester.standalone.libs; + +import cz.crcs.ectester.standalone.consts.KeyAgreementIdent; +import cz.crcs.ectester.standalone.consts.KeyPairGeneratorIdent; +import cz.crcs.ectester.standalone.consts.SignatureIdent; + +import java.util.Set; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public interface ECLibrary { + boolean initialize(); + + boolean isInitialized(); + + Set<String> getCurves(); + + Set<KeyAgreementIdent> getKAs(); + + Set<SignatureIdent> getSigs(); + + Set<KeyPairGeneratorIdent> getKPGs(); + + String name(); +} diff --git a/src/cz/crcs/ectester/standalone/libs/NativeECLibrary.java b/src/cz/crcs/ectester/standalone/libs/NativeECLibrary.java new file mode 100644 index 0000000..0a420a1 --- /dev/null +++ b/src/cz/crcs/ectester/standalone/libs/NativeECLibrary.java @@ -0,0 +1,119 @@ +package cz.crcs.ectester.standalone.libs; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Field; +import java.net.URL; +import java.net.URLConnection; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.security.Provider; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public abstract class NativeECLibrary extends ProviderECLibrary { + private String resource; + private String[] requriements; + + public static String LIB_RESOURCE_DIR = "/cz/crcs/ectester/standalone/libs/jni/"; + + public NativeECLibrary(String resource, String... requirements) { + this.resource = resource; + this.requriements = requirements; + } + + @Override + public boolean initialize() { + try { + /* Determine what OS are we running on and use appropriate suffix and path. */ + String suffix; + Path appData; + if (System.getProperty("os.name").startsWith("Windows")) { + suffix = "dll"; + appData = Paths.get(System.getenv("AppData")); + } else { + suffix = "so"; + if (System.getProperty("os.name").startsWith("Linux")) { + String dataHome = System.getenv("XDG_DATA_HOME"); + if (dataHome != null) { + appData = Paths.get(dataHome); + } else { + appData = Paths.get(System.getProperty("user.home"), ".local", "share"); + } + } else { + appData = Paths.get(System.getProperty("user.home"), ".local", "share"); + } + } + Path libDir = appData.resolve("ECTesterStandalone"); + File libDirFile = libDir.toFile(); + Path libPath = libDir.resolve(resource + "." + suffix); + File libFile = libPath.toFile(); + + URL jarURL = NativeECLibrary.class.getResource(LIB_RESOURCE_DIR + resource + "." + suffix); + if (jarURL == null) { + return false; + } + URLConnection jarConnection = jarURL.openConnection(); + + /* Only write the file if it does not exist, + * or if the existing one is older than the + * one in the JAR. + */ + boolean write = false; + if (libDirFile.isDirectory() && libFile.isFile()) { + long jarModified = jarConnection.getLastModified(); + + long libModified = Files.getLastModifiedTime(libPath).toMillis(); + if (jarModified > libModified) { + write = true; + } + } else { + libDir.toFile().mkdirs(); + libFile.createNewFile(); + write = true; + } + + if (write) { + Files.copy(jarConnection.getInputStream(), libPath, StandardCopyOption.REPLACE_EXISTING); + } + jarConnection.getInputStream().close(); + + /* + * Need to hack in /usr/local/lib to path. + * See: https://stackoverflow.com/questions/5419039/is-djava-library-path-equivalent-to-system-setpropertyjava-library-path/24988095#24988095 + */ + String path = System.getProperty("java.library.path"); + if (suffix.equals("so")) { + String newPath = path + ":/usr/local/lib"; + System.setProperty("java.library.path", newPath); + Field fieldSysPath; + try { + fieldSysPath = ClassLoader.class.getDeclaredField( "sys_paths" ); + fieldSysPath.setAccessible( true ); + fieldSysPath.set( null, null ); + } catch (NoSuchFieldException | IllegalAccessException ignored) { + } + } + + for (String requirement : requriements) { + System.loadLibrary(requirement); + } + + if (suffix.equals("so")) { + System.setProperty("java.library.path", path); + } + + System.load(libPath.toString()); + + provider = createProvider(); + return super.initialize(); + } catch (IOException | UnsatisfiedLinkError ignored) { + } + return false; + } + + abstract Provider createProvider(); +} diff --git a/src/cz/crcs/ectester/standalone/libs/ProviderECLibrary.java b/src/cz/crcs/ectester/standalone/libs/ProviderECLibrary.java new file mode 100644 index 0000000..9108eaf --- /dev/null +++ b/src/cz/crcs/ectester/standalone/libs/ProviderECLibrary.java @@ -0,0 +1,93 @@ +package cz.crcs.ectester.standalone.libs; + +import cz.crcs.ectester.standalone.consts.Ident; +import cz.crcs.ectester.standalone.consts.KeyAgreementIdent; +import cz.crcs.ectester.standalone.consts.KeyPairGeneratorIdent; +import cz.crcs.ectester.standalone.consts.SignatureIdent; + +import java.security.Provider; +import java.security.Security; +import java.util.HashSet; +import java.util.Set; +import java.util.function.Function; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public abstract class ProviderECLibrary implements ECLibrary { + Provider provider; + private boolean initialized = false; + + public ProviderECLibrary() { + + } + + public ProviderECLibrary(Provider provider) { + this.provider = provider; + } + + @Override + public boolean initialize() { + try { + int result = Security.addProvider(provider); + if (result == -1) { + provider = Security.getProvider(provider.getName()); + } + initialized = true; + } catch (NullPointerException | SecurityException ignored) { + initialized = false; + } + return initialized; + } + + @Override + public boolean isInitialized() { + return initialized; + } + + private <T extends Ident> Set<T> getIdents(String type, Function<String, T> getter) { + Set<T> results = new HashSet<>(); + if (!initialized) { + return results; + } + + for (Provider.Service service : provider.getServices()) { + if (service.getType().equals(type)) { + T id = getter.apply(service.getAlgorithm()); + if (id != null) { + results.add(id); + } + } + } + return results; + } + + @Override + public Set<KeyAgreementIdent> getKAs() { + return getIdents("KeyAgreement", KeyAgreementIdent::get); + } + + @Override + public Set<SignatureIdent> getSigs() { + return getIdents("Signature", SignatureIdent::get); + } + + @Override + public Set<KeyPairGeneratorIdent> getKPGs() { + return getIdents("KeyPairGenerator", KeyPairGeneratorIdent::get); + } + + @Override + public String name() { + return provider.getInfo(); + } + + public Provider getProvider() { + return provider; + } + + @Override + public String toString() { + return name(); + } +} diff --git a/src/cz/crcs/ectester/standalone/libs/SunECLib.java b/src/cz/crcs/ectester/standalone/libs/SunECLib.java new file mode 100644 index 0000000..3aec842 --- /dev/null +++ b/src/cz/crcs/ectester/standalone/libs/SunECLib.java @@ -0,0 +1,28 @@ +package cz.crcs.ectester.standalone.libs; + +import sun.security.ec.SunEC; + +import java.util.Set; +import java.util.TreeSet; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class SunECLib extends ProviderECLibrary { + + public SunECLib() { + super(new SunEC()); + } + + @Override + public Set<String> getCurves() { + String curves = provider.get("AlgorithmParameters.EC SupportedCurves").toString(); + String[] split = curves.split("\\|"); + Set<String> result = new TreeSet<>(); + for (String curve : split) { + String body = curve.split(",")[0].substring(1); + result.add(body); + } + return result; + } +} diff --git a/src/cz/crcs/ectester/standalone/libs/TomcryptLib.java b/src/cz/crcs/ectester/standalone/libs/TomcryptLib.java new file mode 100644 index 0000000..57b273a --- /dev/null +++ b/src/cz/crcs/ectester/standalone/libs/TomcryptLib.java @@ -0,0 +1,20 @@ +package cz.crcs.ectester.standalone.libs; + +import java.security.Provider; +import java.util.Set; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class TomcryptLib extends NativeECLibrary { + + public TomcryptLib() { + super("tomcrypt_provider", "tommath", "tomcrypt"); + } + + @Override + native Provider createProvider(); + + @Override + public native Set<String> getCurves(); +} diff --git a/src/cz/crcs/ectester/standalone/libs/jni/Makefile b/src/cz/crcs/ectester/standalone/libs/jni/Makefile new file mode 100644 index 0000000..3530499 --- /dev/null +++ b/src/cz/crcs/ectester/standalone/libs/jni/Makefile @@ -0,0 +1,70 @@ +ifeq ($(JAVA_HOME),) +ifeq ($(OS),Windows_NT) +which = $(shell where $1) +else +which = $(shell which $1) +endif +JAVAC ?= $(realpath $(call which,javac)) +JAVA_HOME = $(abspath $(dir $(JAVAC))..) +endif + +ifneq ($(JAVA_HOME),) +JNI_INCLUDEDIR ?= $(JAVA_HOME)/include +endif + +ifeq ($(JNI_INCLUDEDIR),) +$(error could not determine JNI include dir, try specifying either \ + JAVA_HOME or JNI_INCLUDEDIR) +endif + +TARGETTRIPLET := $(shell $(CC) -dumpmachine) +ifeq ($(JNI_PLATFORM),) +ifeq ($(findstring mingw,$(TARGETTRIPLET)),mingw) +JNI_PLATFORM:= win32 +else +ifeq ($(findstring linux,$(TARGETTRIPLET)),linux) +JNI_PLATFORM:= linux +# add more checks here +endif +endif +endif + +JNI_PLATFORMINCLUDEDIR ?= $(JNI_INCLUDEDIR)/$(JNI_PLATFORM) + +LOCAL_INCLUDES = /usr/local/include +LOCAL_LIBS = /usr/local/lib + +CC?=gcc +CXX?=g++ + +CFLAGS+=-fPIC -g -I"$(JNI_INCLUDEDIR)" -I"$(JNI_PLATFORMINCLUDEDIR)" -I. +CXXFLAGS+=-fPIC -g -I"$(JNI_INCLUDEDIR)" -I"$(JNI_PLATFORMINCLUDEDIR)" -I. + + +all: tomcrypt_provider.so botan_provider.so + +c_utils.o: c_utils.c + $(CC) $(CFLAGS) -c $< + +cpp_utils.o: cpp_utils.cpp + $(CXX) $(CXXFLAGS) -c $< + + +tomcrypt_provider.so: tomcrypt.o c_utils.o + $(CC) -fPIC -g -shared -o $@ $^ -L. -ltommath -ltomcrypt + +tomcrypt.o: tomcrypt.c + $(CC) -DLTM_DESC $(CFLAGS) -c $< + + +botan_provider.so: botan.o cpp_utils.o + $(CXX) -fPIC -g -shared -o $@ $^ -L. -L"$(LOCAL_LIBS)" -lbotan-2 -fstack-protector -m64 -pthread + +botan.o: botan.cpp + $(CXX) -I"$(LOCAL_INCLUDES)/botan-2" $(CFLAGS) -c $< + +clean: + rm -rf *.o + rm -rf *.so + +.PHONY: all clean
\ No newline at end of file diff --git a/src/cz/crcs/ectester/standalone/libs/jni/NativeECPrivateKey.java b/src/cz/crcs/ectester/standalone/libs/jni/NativeECPrivateKey.java new file mode 100644 index 0000000..22e5329 --- /dev/null +++ b/src/cz/crcs/ectester/standalone/libs/jni/NativeECPrivateKey.java @@ -0,0 +1,68 @@ +package cz.crcs.ectester.standalone.libs.jni; + +import org.bouncycastle.util.Arrays; + +import java.math.BigInteger; +import java.security.interfaces.ECPrivateKey; +import java.security.spec.ECParameterSpec; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public abstract class NativeECPrivateKey implements ECPrivateKey { + private String algorithm; + private String format; + + public NativeECPrivateKey(String algorithm, String format) { + this.algorithm = algorithm; + this.format = format; + } + + @Override + public String getAlgorithm() { + return algorithm; + } + + @Override + public String getFormat() { + return format; + } + + private static class Raw extends NativeECPrivateKey { + private byte[] keyData; + private ECParameterSpec params; + + public Raw(byte[] keyData, ECParameterSpec params) { + super("EC", "raw"); + this.keyData = keyData; + this.params = params; + } + + @Override + public BigInteger getS() { + return new BigInteger(1, keyData); + } + + @Override + public byte[] getEncoded() { + return Arrays.clone(keyData); + } + + @Override + public ECParameterSpec getParams() { + return params; + } + } + + public static class TomCrypt extends Raw { + public TomCrypt(byte[] keyData, ECParameterSpec params) { + super(keyData, params); + } + } + + public static class Botan extends Raw { + public Botan(byte[] keyData, ECParameterSpec params) { + super(keyData, params); + } + } +} diff --git a/src/cz/crcs/ectester/standalone/libs/jni/NativeECPublicKey.java b/src/cz/crcs/ectester/standalone/libs/jni/NativeECPublicKey.java new file mode 100644 index 0000000..8fc4747 --- /dev/null +++ b/src/cz/crcs/ectester/standalone/libs/jni/NativeECPublicKey.java @@ -0,0 +1,69 @@ +package cz.crcs.ectester.standalone.libs.jni; + +import cz.crcs.ectester.common.util.ECUtil; +import org.bouncycastle.util.Arrays; + +import java.security.interfaces.ECPublicKey; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPoint; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public abstract class NativeECPublicKey implements ECPublicKey { + private String algorithm; + private String format; + + public NativeECPublicKey(String algorithm, String format) { + this.algorithm = algorithm; + this.format = format; + } + + @Override + public String getAlgorithm() { + return algorithm; + } + + @Override + public String getFormat() { + return format; + } + + private static class ANSIX962 extends NativeECPublicKey { + private byte[] keyData; + private ECParameterSpec params; + + public ANSIX962(byte[] keyData, ECParameterSpec params) { + super("EC", "ANSI X9.62"); + this.keyData = keyData; + this.params = params; + } + + @Override + public ECPoint getW() { + return ECUtil.fromX962(keyData, params.getCurve()); + } + + @Override + public byte[] getEncoded() { + return Arrays.clone(keyData); + } + + @Override + public ECParameterSpec getParams() { + return params; + } + } + + public static class TomCrypt extends ANSIX962 { + public TomCrypt(byte[] keyData, ECParameterSpec params) { + super(keyData, params); + } + } + + public static class Botan extends ANSIX962 { + public Botan(byte[] keyData, ECParameterSpec params) { + super(keyData, params); + } + } +} diff --git a/src/cz/crcs/ectester/standalone/libs/jni/NativeKeyAgreementSpi.java b/src/cz/crcs/ectester/standalone/libs/jni/NativeKeyAgreementSpi.java new file mode 100644 index 0000000..37c9add --- /dev/null +++ b/src/cz/crcs/ectester/standalone/libs/jni/NativeKeyAgreementSpi.java @@ -0,0 +1,137 @@ +package cz.crcs.ectester.standalone.libs.jni; + +import cz.crcs.ectester.common.util.ECUtil; + +import javax.crypto.KeyAgreementSpi; +import javax.crypto.SecretKey; +import javax.crypto.ShortBufferException; +import java.security.*; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.ECParameterSpec; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public abstract class NativeKeyAgreementSpi extends KeyAgreementSpi { + private ECPrivateKey privateKey; + private ECPublicKey publicKey; + private ECParameterSpec params; + + @Override + protected void engineInit(Key key, SecureRandom random) throws InvalidKeyException { + if (!(key instanceof ECPrivateKey)) { + throw new InvalidKeyException + ("Key must be instance of ECPrivateKey"); + } + privateKey = (ECPrivateKey) key; + this.params = privateKey.getParams(); + } + + @Override + protected void engineInit(Key key, AlgorithmParameterSpec params, SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { + if (!(params instanceof ECParameterSpec)) { + throw new InvalidAlgorithmParameterException(); + } + engineInit(key, random); + this.params = (ECParameterSpec) params; + } + + @Override + protected Key engineDoPhase(Key key, boolean lastPhase) throws InvalidKeyException, IllegalStateException { + if (privateKey == null) { + throw new IllegalStateException("Not initialized"); + } + if (publicKey != null) { + throw new IllegalStateException("Phase already executed"); + } + if (!lastPhase) { + throw new IllegalStateException + ("Only two party agreement supported, lastPhase must be true"); + } + if (!(key instanceof ECPublicKey)) { + throw new InvalidKeyException + ("Key must be an instance of ECPublicKey"); + } + publicKey = (ECPublicKey) key; + return null; + } + + @Override + protected byte[] engineGenerateSecret() throws IllegalStateException { + byte[] pubkey = ECUtil.toX962Uncompressed(publicKey.getW(), params.getCurve()); + byte[] privkey = ECUtil.toByteArray(privateKey.getS(), params.getCurve().getField().getFieldSize()); + return generateSecret(pubkey, privkey, params); + } + + @Override + protected int engineGenerateSecret(byte[] sharedSecret, int offset) throws IllegalStateException, ShortBufferException { + byte[] secret = engineGenerateSecret(); + if (sharedSecret.length < offset + secret.length) { + throw new ShortBufferException(); + } + System.arraycopy(secret, 0, sharedSecret, offset, secret.length); + return secret.length; + } + + @Override + protected SecretKey engineGenerateSecret(String algorithm) throws IllegalStateException, NoSuchAlgorithmException, InvalidKeyException { + throw new NoSuchAlgorithmException(algorithm); + } + + abstract byte[] generateSecret(byte[] pubkey, byte[] privkey, ECParameterSpec params); + + + public static class TomCrypt extends NativeKeyAgreementSpi { + + @Override + native byte[] generateSecret(byte[] pubkey, byte[] privkey, ECParameterSpec params); + } + + public abstract static class Botan extends NativeKeyAgreementSpi { + private String type; + public Botan(String type) { + this.type = type; + } + + @Override + native byte[] generateSecret(byte[] pubkey, byte[] privkey, ECParameterSpec params); + } + + public static class BotanECDH extends Botan { + public BotanECDH() { + super("ECDH"); + } + } + + public static class BotanECDHwithSHA1KDF extends Botan { + public BotanECDHwithSHA1KDF() { + super("ECDHwithSHA1KDF"); + } + } + + public static class BotanECDHwithSHA224KDF extends Botan { + public BotanECDHwithSHA224KDF() { + super("ECDHwithSHA224KDF"); + } + } + + public static class BotanECDHwithSHA256KDF extends Botan { + public BotanECDHwithSHA256KDF() { + super("ECDHwithSHA256KDF"); + } + } + + public static class BotanECDHwithSHA384KDF extends Botan { + public BotanECDHwithSHA384KDF() { + super("ECDHwithSHA384KDF"); + } + } + + public static class BotanECDHwithSHA512KDF extends Botan { + public BotanECDHwithSHA512KDF() { + super("ECDHwithSHA512KDF"); + } + } +} diff --git a/src/cz/crcs/ectester/standalone/libs/jni/NativeKeyPairGeneratorSpi.java b/src/cz/crcs/ectester/standalone/libs/jni/NativeKeyPairGeneratorSpi.java new file mode 100644 index 0000000..9461251 --- /dev/null +++ b/src/cz/crcs/ectester/standalone/libs/jni/NativeKeyPairGeneratorSpi.java @@ -0,0 +1,123 @@ +package cz.crcs.ectester.standalone.libs.jni; + +import java.security.*; +import java.security.spec.AlgorithmParameterSpec; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public abstract class NativeKeyPairGeneratorSpi extends KeyPairGeneratorSpi { + private int keysize; + private SecureRandom random; + private AlgorithmParameterSpec params; + private boolean useKeysize; + private boolean useParams; + + @Override + public void initialize(int keysize, SecureRandom random) { + if (!keysizeSupported(keysize)) { + throw new InvalidParameterException("Keysize " + keysize + " not supported."); + } + this.keysize = keysize; + this.random = random; + this.useKeysize = true; + this.useParams = false; + } + + @Override + public void initialize(AlgorithmParameterSpec params, SecureRandom random) throws InvalidAlgorithmParameterException { + if (!paramsSupported(params)) { + throw new InvalidAlgorithmParameterException("not supported."); + } + this.params = params; + this.random = random; + this.useParams = true; + this.useKeysize = false; + } + + @Override + public KeyPair generateKeyPair() { + if (useKeysize) { + return generate(keysize, random); + } else if (useParams) { + return generate(params, random); + } + return null; + } + + abstract boolean keysizeSupported(int keysize); + + abstract boolean paramsSupported(AlgorithmParameterSpec params); + + abstract KeyPair generate(int keysize, SecureRandom random); + + abstract KeyPair generate(AlgorithmParameterSpec params, SecureRandom random); + + public static class TomCrypt extends NativeKeyPairGeneratorSpi { + + public TomCrypt() { + initialize(256, new SecureRandom()); + } + + @Override + native boolean keysizeSupported(int keysize); + + @Override + native boolean paramsSupported(AlgorithmParameterSpec params); + + @Override + native KeyPair generate(int keysize, SecureRandom random); + + @Override + native KeyPair generate(AlgorithmParameterSpec params, SecureRandom random); + } + + public static abstract class Botan extends NativeKeyPairGeneratorSpi { + private String type; + + public Botan(String type) { + this.type = type; + initialize(256, new SecureRandom()); + } + + @Override + native boolean keysizeSupported(int keysize); + + @Override + native boolean paramsSupported(AlgorithmParameterSpec params); + + @Override + native KeyPair generate(int keysize, SecureRandom random); + + @Override + native KeyPair generate(AlgorithmParameterSpec params, SecureRandom random); + } + + public static class BotanECDH extends Botan { + + public BotanECDH() { + super("ECDH"); + } + } + + public static class BotanECDSA extends Botan { + + public BotanECDSA() { + super("ECDSA"); + } + } + + public static class BotanECKCDSA extends Botan { + + public BotanECKCDSA() { + super("ECKCDSA"); + } + } + + public static class BotanECGDSA extends Botan { + + public BotanECGDSA() { + super("ECGDSA"); + } + } +} diff --git a/src/cz/crcs/ectester/standalone/libs/jni/NativeProvider.java b/src/cz/crcs/ectester/standalone/libs/jni/NativeProvider.java new file mode 100644 index 0000000..a0689d6 --- /dev/null +++ b/src/cz/crcs/ectester/standalone/libs/jni/NativeProvider.java @@ -0,0 +1,42 @@ +package cz.crcs.ectester.standalone.libs.jni; + +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.security.Provider; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public abstract class NativeProvider extends Provider { + + public NativeProvider(String name, double version, String info) { + super(name, version, info); + + AccessController.doPrivileged((PrivilegedAction<Object>) () -> { + setup(); + return null; + }); + } + + abstract void setup(); + + public static class TomCrypt extends NativeProvider { + + public TomCrypt(String name, double version, String info) { + super(name, version, info); + } + + @Override + native void setup(); + } + + public static class Botan extends NativeProvider { + + public Botan(String name, double version, String info) { + super(name, version, info); + } + + @Override + native void setup(); + } +} diff --git a/src/cz/crcs/ectester/standalone/libs/jni/NativeSignatureSpi.java b/src/cz/crcs/ectester/standalone/libs/jni/NativeSignatureSpi.java new file mode 100644 index 0000000..b212697 --- /dev/null +++ b/src/cz/crcs/ectester/standalone/libs/jni/NativeSignatureSpi.java @@ -0,0 +1,227 @@ +package cz.crcs.ectester.standalone.libs.jni; + +import cz.crcs.ectester.common.util.ECUtil; + +import java.io.ByteArrayOutputStream; +import java.security.*; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.ECParameterSpec; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public abstract class NativeSignatureSpi extends SignatureSpi { + private ECPublicKey verifyKey; + private ECPrivateKey signKey; + private ECParameterSpec params; + + private ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + + @Override + protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException { + if (!(publicKey instanceof ECPublicKey)) { + throw new InvalidKeyException + ("Key must be an instance of ECPublicKey"); + } + verifyKey = (ECPublicKey) publicKey; + params = verifyKey.getParams(); + buffer.reset(); + } + + @Override + protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException { + if (!(privateKey instanceof ECPrivateKey)) { + throw new InvalidKeyException + ("Key must be an instance of ECPrivateKey"); + } + signKey = (ECPrivateKey) privateKey; + params = signKey.getParams(); + buffer.reset(); + } + + @Override + protected void engineUpdate(byte b) throws SignatureException { + buffer.write(b); + } + + @Override + protected void engineUpdate(byte[] b, int off, int len) throws SignatureException { + buffer.write(b, off, len); + } + + @Override + protected byte[] engineSign() throws SignatureException { + return sign(buffer.toByteArray(), ECUtil.toByteArray(signKey.getS(), params.getCurve().getField().getFieldSize()), params); + } + + @Override + protected boolean engineVerify(byte[] sigBytes) throws SignatureException { + return verify(sigBytes, buffer.toByteArray(), ECUtil.toX962Uncompressed(verifyKey.getW(), params), params); + } + + @Override + @Deprecated + protected void engineSetParameter(String param, Object value) throws InvalidParameterException { + throw new UnsupportedOperationException("setParameter() not supported"); + } + + @Override + @Deprecated + protected Object engineGetParameter(String param) throws InvalidParameterException { + throw new UnsupportedOperationException("getParameter() not supported"); + } + + abstract byte[] sign(byte[] data, byte[] privkey, ECParameterSpec params); + + abstract boolean verify(byte[] signature, byte[] data, byte[] pubkey, ECParameterSpec params); + + public static class TomCryptRaw extends NativeSignatureSpi { + + @Override + native byte[] sign(byte[] data, byte[] privkey, ECParameterSpec params); + + @Override + native boolean verify(byte[] signature, byte[] data, byte[] pubkey, ECParameterSpec params); + } + + public abstract static class Botan extends NativeSignatureSpi { + private String type; + + public Botan(String type) { + this.type = type; + } + + @Override + native byte[] sign(byte[] data, byte[] privkey, ECParameterSpec params); + + @Override + native boolean verify(byte[] signature, byte[] data, byte[] pubkey, ECParameterSpec params); + } + + public static class BotanECDSAwithNONE extends Botan { + + public BotanECDSAwithNONE() { + super("NONEwithECDSA"); + } + } + + public static class BotanECDSAwithSHA1 extends Botan { + + public BotanECDSAwithSHA1() { + super("SHA1withECDSA"); + } + } + + public static class BotanECDSAwithSHA224 extends Botan { + + public BotanECDSAwithSHA224() { + super("SHA224withECDSA"); + } + } + + public static class BotanECDSAwithSHA256 extends Botan { + + public BotanECDSAwithSHA256() { + super("SHA256withECDSA"); + } + } + + public static class BotanECDSAwithSHA384 extends Botan { + + public BotanECDSAwithSHA384() { + super("SHA384withECDSA"); + } + } + + public static class BotanECDSAwithSHA512 extends Botan { + + public BotanECDSAwithSHA512() { + super("SHA512withECDSA"); + } + } + + public static class BotanECKCDSAwithNONE extends Botan { + + public BotanECKCDSAwithNONE() { + super("NONEwithECKCDSA"); + } + } + + public static class BotanECKCDSAwithSHA1 extends Botan { + + public BotanECKCDSAwithSHA1() { + super("SHA1withECKCDSA"); + } + } + + public static class BotanECKCDSAwithSHA224 extends Botan { + + public BotanECKCDSAwithSHA224() { + super("SHA224withECKCDSA"); + } + } + + public static class BotanECKCDSAwithSHA256 extends Botan { + + public BotanECKCDSAwithSHA256() { + super("SHA256withECKCDSA"); + } + } + + public static class BotanECKCDSAwithSHA384 extends Botan { + + public BotanECKCDSAwithSHA384() { + super("SHA384withECKCDSA"); + } + } + + public static class BotanECKCDSAwithSHA512 extends Botan { + + public BotanECKCDSAwithSHA512() { + super("SHA512withECKCDSA"); + } + } + + public static class BotanECGDSAwithNONE extends Botan { + + public BotanECGDSAwithNONE() { + super("NONEwithECGDSA"); + } + } + + public static class BotanECGDSAwithSHA1 extends Botan { + + public BotanECGDSAwithSHA1() { + super("SHA1withECGDSA"); + } + } + + public static class BotanECGDSAwithSHA224 extends Botan { + + public BotanECGDSAwithSHA224() { + super("SHA224withECGDSA"); + } + } + + public static class BotanECGDSAwithSHA256 extends Botan { + + public BotanECGDSAwithSHA256() { + super("SHA256withECGDSA"); + } + } + + public static class BotanECGDSAwithSHA384 extends Botan { + + public BotanECGDSAwithSHA384() { + super("SHA384withECGDSA"); + } + } + + public static class BotanECGDSAwithSHA512 extends Botan { + + public BotanECGDSAwithSHA512() { + super("SHA512withECGDSA"); + } + } +} diff --git a/src/cz/crcs/ectester/standalone/libs/jni/botan.cpp b/src/cz/crcs/ectester/standalone/libs/jni/botan.cpp new file mode 100644 index 0000000..f87d68b --- /dev/null +++ b/src/cz/crcs/ectester/standalone/libs/jni/botan.cpp @@ -0,0 +1,606 @@ +#include "native.h" +#include <string> +#include <botan/botan.h> +#include <botan/ec_group.h> +#include <botan/ecc_key.h> +#include <botan/ecdsa.h> +#include <botan/eckcdsa.h> +#include <botan/ecgdsa.h> +#include <botan/ecdh.h> +#include <botan/pubkey.h> +#include "cpp_utils.hpp" + +static jclass provider_class; + +/* + * Class: cz_crcs_ectester_standalone_libs_BotanLib + * Method: createProvider + * Signature: ()Ljava/security/Provider; + */ +JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_BotanLib_createProvider(JNIEnv *env, jobject self) { + /* Create the custom provider. */ + jclass local_provider_class = env->FindClass("cz/crcs/ectester/standalone/libs/jni/NativeProvider$Botan"); + provider_class = (jclass) env->NewGlobalRef(local_provider_class); + + jmethodID init = env->GetMethodID(local_provider_class, "<init>", "(Ljava/lang/String;DLjava/lang/String;)V"); + + const char* info_str = Botan::version_cstr(); + const char* v_str = Botan::short_version_cstr(); + std::string name_str = Botan::short_version_string(); + name_str.insert(0, "Botan "); + + jstring name = env->NewStringUTF(name_str.c_str()); + double version = strtod(v_str, NULL); + jstring info = env->NewStringUTF(info_str); + + return env->NewObject(provider_class, init, name, version, info); +} + +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeProvider_Botan + * Method: setup + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeProvider_00024Botan_setup(JNIEnv *env, jobject self){ + jmethodID provider_put = env->GetMethodID(provider_class, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); + + jstring ecdh = env->NewStringUTF("KeyPairGenerator.ECDH"); + jstring ecdh_value = env->NewStringUTF("cz.crcs.ectester.standalone.libs.jni.NativeKeyPairGeneratorSpi$BotanECDH"); + env->CallObjectMethod(self, provider_put, ecdh, ecdh_value); + + jstring ecdsa = env->NewStringUTF("KeyPairGenerator.ECDSA"); + jstring ecdsa_value = env->NewStringUTF("cz.crcs.ectester.standalone.libs.jni.NativeKeyPairGeneratorSpi$BotanECDSA"); + env->CallObjectMethod(self, provider_put, ecdsa, ecdsa_value); + + jstring eckcdsa = env->NewStringUTF("KeyPairGenerator.ECKCDSA"); + jstring eckcdsa_value = env->NewStringUTF("cz.crcs.ectester.standalone.libs.jni.NativeKeyPairGeneratorSpi$BotanECKCDSA"); + env->CallObjectMethod(self, provider_put, eckcdsa, eckcdsa_value); + + jstring ecgdsa = env->NewStringUTF("KeyPairGenerator.ECGDSA"); + jstring ecgdsa_value = env->NewStringUTF("cz.crcs.ectester.standalone.libs.jni.NativeKeyPairGeneratorSpi$BotanECGDSA"); + env->CallObjectMethod(self, provider_put, ecgdsa, ecgdsa_value); + + jstring ecdh_ka = env->NewStringUTF("KeyAgreement.ECDH"); + jstring ecdh_ka_value = env->NewStringUTF("cz.crcs.ectester.standalone.libs.jni.NativeKeyAgreementSpi$BotanECDH"); + env->CallObjectMethod(self, provider_put, ecdh_ka, ecdh_ka_value); + + jstring ecdh_sha1_ka = env->NewStringUTF("KeyAgreement.ECDHwithSHA1KDF"); + jstring ecdh_sha1_ka_value = env->NewStringUTF("cz.crcs.ectester.standalone.libs.jni.NativeKeyAgreementSpi$BotanECDHwithSHA1KDF"); + env->CallObjectMethod(self, provider_put, ecdh_sha1_ka, ecdh_sha1_ka_value); + + jstring ecdh_sha224_ka = env->NewStringUTF("KeyAgreement.ECDHwithSHA224KDF"); + jstring ecdh_sha224_ka_value = env->NewStringUTF("cz.crcs.ectester.standalone.libs.jni.NativeKeyAgreementSpi$BotanECDHwithSHA224KDF"); + env->CallObjectMethod(self, provider_put, ecdh_sha224_ka, ecdh_sha224_ka_value); + + jstring ecdh_sha256_ka = env->NewStringUTF("KeyAgreement.ECDHwithSHA256KDF"); + jstring ecdh_sha256_ka_value = env->NewStringUTF("cz.crcs.ectester.standalone.libs.jni.NativeKeyAgreementSpi$BotanECDHwithSHA256KDF"); + env->CallObjectMethod(self, provider_put, ecdh_sha256_ka, ecdh_sha256_ka_value); + + jstring ecdh_sha384_ka = env->NewStringUTF("KeyAgreement.ECDHwithSHA384KDF"); + jstring ecdh_sha384_ka_value = env->NewStringUTF("cz.crcs.ectester.standalone.libs.jni.NativeKeyAgreementSpi$BotanECDHwithSHA384KDF"); + env->CallObjectMethod(self, provider_put, ecdh_sha384_ka, ecdh_sha384_ka_value); + + jstring ecdh_sha512_ka = env->NewStringUTF("KeyAgreement.ECDHwithSHA512KDF"); + jstring ecdh_sha512_ka_value = env->NewStringUTF("cz.crcs.ectester.standalone.libs.jni.NativeKeyAgreementSpi$BotanECDHwithSHA512KDF"); + env->CallObjectMethod(self, provider_put, ecdh_sha512_ka, ecdh_sha512_ka_value); + + jstring ecdsa_sig = env->NewStringUTF("Signature.NONEwithECDSA"); + jstring ecdsa_sig_value = env->NewStringUTF("cz.crcs.ectester.standalone.libs.jni.NativeSignatureSpi$BotanECDSAwithNONE"); + env->CallObjectMethod(self, provider_put, ecdsa_sig, ecdsa_sig_value); + + jstring ecdsa_sha1_sig = env->NewStringUTF("Signature.SHA1withECDSA"); + jstring ecdsa_sha1_sig_value = env->NewStringUTF("cz.crcs.ectester.standalone.libs.jni.NativeSignatureSpi$BotanECDSAwithSHA1"); + env->CallObjectMethod(self, provider_put, ecdsa_sha1_sig, ecdsa_sha1_sig_value); + + jstring ecdsa_sha224_sig = env->NewStringUTF("Signature.SHA224withECDSA"); + jstring ecdsa_sha224_sig_value = env->NewStringUTF("cz.crcs.ectester.standalone.libs.jni.NativeSignatureSpi$BotanECDSAwithSHA224"); + env->CallObjectMethod(self, provider_put, ecdsa_sha224_sig, ecdsa_sha224_sig_value); + + jstring ecdsa_sha256_sig = env->NewStringUTF("Signature.SHA256withECDSA"); + jstring ecdsa_sha256_sig_value = env->NewStringUTF("cz.crcs.ectester.standalone.libs.jni.NativeSignatureSpi$BotanECDSAwithSHA256"); + env->CallObjectMethod(self, provider_put, ecdsa_sha256_sig, ecdsa_sha256_sig_value); + + jstring ecdsa_sha384_sig = env->NewStringUTF("Signature.SHA384withECDSA"); + jstring ecdsa_sha384_sig_value = env->NewStringUTF("cz.crcs.ectester.standalone.libs.jni.NativeSignatureSpi$BotanECDSAwithSHA384"); + env->CallObjectMethod(self, provider_put, ecdsa_sha384_sig, ecdsa_sha384_sig_value); + + jstring ecdsa_sha512_sig = env->NewStringUTF("Signature.SHA512withECDSA"); + jstring ecdsa_sha512_sig_value = env->NewStringUTF("cz.crcs.ectester.standalone.libs.jni.NativeSignatureSpi$BotanECDSAwithSHA512"); + env->CallObjectMethod(self, provider_put, ecdsa_sha512_sig, ecdsa_sha512_sig_value); + + jstring eckcdsa_sig = env->NewStringUTF("Signature.NONEwithECKCDSA"); + jstring eckcdsa_sig_value = env->NewStringUTF("cz.crcs.ectester.standalone.libs.jni.NativeSignatureSpi$BotanECKCDSAwithNONE"); + env->CallObjectMethod(self, provider_put, eckcdsa_sig, eckcdsa_sig_value); + + jstring eckcdsa_sha1_sig = env->NewStringUTF("Signature.SHA1withECKCDSA"); + jstring eckcdsa_sha1_sig_value = env->NewStringUTF("cz.crcs.ectester.standalone.libs.jni.NativeSignatureSpi$BotanECKCDSAwithSHA1"); + env->CallObjectMethod(self, provider_put, eckcdsa_sha1_sig, eckcdsa_sha1_sig_value); + + jstring eckcdsa_sha224_sig = env->NewStringUTF("Signature.SHA224withECKCDSA"); + jstring eckcdsa_sha224_sig_value = env->NewStringUTF("cz.crcs.ectester.standalone.libs.jni.NativeSignatureSpi$BotanECKCDSAwithSHA224"); + env->CallObjectMethod(self, provider_put, eckcdsa_sha224_sig, eckcdsa_sha224_sig_value); + + jstring eckcdsa_sha256_sig = env->NewStringUTF("Signature.SHA256withECKCDSA"); + jstring eckcdsa_sha256_sig_value = env->NewStringUTF("cz.crcs.ectester.standalone.libs.jni.NativeSignatureSpi$BotanECKCDSAwithSHA256"); + env->CallObjectMethod(self, provider_put, eckcdsa_sha256_sig, eckcdsa_sha256_sig_value); + + jstring eckcdsa_sha384_sig = env->NewStringUTF("Signature.SHA384withECKCDSA"); + jstring eckcdsa_sha384_sig_value = env->NewStringUTF("cz.crcs.ectester.standalone.libs.jni.NativeSignatureSpi$BotanECKCDSAwithSHA384"); + env->CallObjectMethod(self, provider_put, eckcdsa_sha384_sig, eckcdsa_sha384_sig_value); + + jstring eckcdsa_sha512_sig = env->NewStringUTF("Signature.SHA512withECKCDSA"); + jstring eckcdsa_sha512_sig_value = env->NewStringUTF("cz.crcs.ectester.standalone.libs.jni.NativeSignatureSpi$BotanECKCDSAwithSHA512"); + env->CallObjectMethod(self, provider_put, eckcdsa_sha512_sig, eckcdsa_sha512_sig_value); + + jstring ecgdsa_sig = env->NewStringUTF("Signature.NONEwithECGDSA"); + jstring ecgdsa_sig_value = env->NewStringUTF("cz.crcs.ectester.standalone.libs.jni.NativeSignatureSpi$BotanECGDSAwithNONE"); + env->CallObjectMethod(self, provider_put, ecgdsa_sig, ecgdsa_sig_value); + + jstring ecgdsa_sha1_sig = env->NewStringUTF("Signature.SHA1withECGDSA"); + jstring ecgdsa_sha1_sig_value = env->NewStringUTF("cz.crcs.ectester.standalone.libs.jni.NativeSignatureSpi$BotanECGDSAwithSHA1"); + env->CallObjectMethod(self, provider_put, ecgdsa_sha1_sig, ecgdsa_sha1_sig_value); + + jstring ecgdsa_sha224_sig = env->NewStringUTF("Signature.SHA224withECGDSA"); + jstring ecgdsa_sha224_sig_value = env->NewStringUTF("cz.crcs.ectester.standalone.libs.jni.NativeSignatureSpi$BotanECGDSAwithSHA224"); + env->CallObjectMethod(self, provider_put, ecgdsa_sha224_sig, ecgdsa_sha224_sig_value); + + jstring ecgdsa_sha256_sig = env->NewStringUTF("Signature.SHA256withECGDSA"); + jstring ecgdsa_sha256_sig_value = env->NewStringUTF("cz.crcs.ectester.standalone.libs.jni.NativeSignatureSpi$BotanECGDSAwithSHA256"); + env->CallObjectMethod(self, provider_put, ecgdsa_sha256_sig, ecgdsa_sha256_sig_value); + + jstring ecgdsa_sha384_sig = env->NewStringUTF("Signature.SHA384withECGDSA"); + jstring ecgdsa_sha384_sig_value = env->NewStringUTF("cz.crcs.ectester.standalone.libs.jni.NativeSignatureSpi$BotanECGDSAwithSHA384"); + env->CallObjectMethod(self, provider_put, ecgdsa_sha384_sig, ecgdsa_sha384_sig_value); + + jstring ecgdsa_sha512_sig = env->NewStringUTF("Signature.SHA512withECGDSA"); + jstring ecgdsa_sha512_sig_value = env->NewStringUTF("cz.crcs.ectester.standalone.libs.jni.NativeSignatureSpi$BotanECGDSAwithSHA512"); + env->CallObjectMethod(self, provider_put, ecgdsa_sha512_sig, ecgdsa_sha512_sig_value); + + init_classes(env, "Botan"); +} + +/* + * Class: cz_crcs_ectester_standalone_libs_BotanLib + * Method: getCurves + * Signature: ()Ljava/util/Set; + */ +JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_BotanLib_getCurves(JNIEnv *env, jobject self){ + jclass hash_set_class = env->FindClass("java/util/TreeSet"); + + jmethodID hash_set_ctr = env->GetMethodID(hash_set_class, "<init>", "()V"); + jmethodID hash_set_add = env->GetMethodID(hash_set_class, "add", "(Ljava/lang/Object;)Z"); + + jobject result = env->NewObject(hash_set_class, hash_set_ctr); + + const std::set<std::string>& curves = Botan::EC_Group::known_named_groups(); + for (auto it = curves.begin(); it != curves.end(); ++it) { + std::string curve_name = *it; + jstring name_str = env->NewStringUTF(curve_name.c_str()); + env->CallBooleanMethod(result, hash_set_add, name_str); + } + + return result; +} + +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_Botan + * Method: keysizeSupported + * Signature: (I)Z + */ +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_00024Botan_keysizeSupported(JNIEnv *env, jobject self, jint keysize){ + return JNI_TRUE; +} + +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_Botan + * Method: paramsSupported + * Signature: (Ljava/security/spec/AlgorithmParameterSpec;)Z + */ +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_00024Botan_paramsSupported(JNIEnv *env, jobject self, jobject params){ + if (params == NULL) { + return JNI_FALSE; + } + + if (env->IsInstanceOf(params, ec_parameter_spec_class)) { + jmethodID get_curve = env->GetMethodID(ec_parameter_spec_class, "getCurve", "()Ljava/security/spec/EllipticCurve;"); + jobject curve = env->CallObjectMethod(params, get_curve); + + jmethodID get_field = env->GetMethodID(elliptic_curve_class, "getField", "()Ljava/security/spec/ECField;"); + jobject field = env->CallObjectMethod(curve, get_field); + + if (env->IsInstanceOf(field, fp_field_class)) { + return JNI_TRUE; + } + } else if (env->IsInstanceOf(params, ecgen_parameter_spec_class)) { + const std::set<std::string>& curves = Botan::EC_Group::known_named_groups(); + jmethodID get_name = env->GetMethodID(ecgen_parameter_spec_class, "getName", "()Ljava/lang/String;"); + jstring name = (jstring) env->CallObjectMethod(params, get_name); + const char *utf_name = env->GetStringUTFChars(name, NULL); + std::string str_name(utf_name); + env->ReleaseStringUTFChars(name, utf_name); + if (curves.find(str_name) != curves.end()) { + return JNI_TRUE; + } + } + return JNI_FALSE; +} + +static jobject biginteger_from_bigint(JNIEnv *env, const Botan::BigInt& bigint) { + std::vector<uint8_t> bigint_data = Botan::BigInt::encode(bigint); + jbyteArray bigint_array = env->NewByteArray(bigint_data.size()); + jbyte * bigint_bytes = env->GetByteArrayElements(bigint_array, NULL); + std::copy(bigint_data.begin(), bigint_data.end(), bigint_bytes); + env->ReleaseByteArrayElements(bigint_array, bigint_bytes, JNI_COMMIT); + + jmethodID biginteger_init = env->GetMethodID(biginteger_class, "<init>", "(I[B)V"); + return env->NewObject(biginteger_class, biginteger_init, (jint) 1, bigint_array); +} + +static Botan::BigInt bigint_from_biginteger(JNIEnv *env, jobject biginteger) { + jmethodID to_byte_array = env->GetMethodID(biginteger_class, "toByteArray", "()[B"); + jbyteArray byte_array = (jbyteArray) env->CallObjectMethod(biginteger, to_byte_array); + jsize byte_length = env->GetArrayLength(byte_array); + jbyte *byte_data = env->GetByteArrayElements(byte_array, NULL); + Botan::BigInt result((unsigned uint8_t*) byte_data, byte_length); + env->ReleaseByteArrayElements(byte_array, byte_data, JNI_ABORT); + return result; +} + +static Botan::EC_Group group_from_params(JNIEnv *env, jobject params) { + if (env->IsInstanceOf(params, ec_parameter_spec_class)) { + jmethodID get_curve = env->GetMethodID(ec_parameter_spec_class, "getCurve", "()Ljava/security/spec/EllipticCurve;"); + jobject elliptic_curve = env->CallObjectMethod(params, get_curve); + + jmethodID get_field = env->GetMethodID(elliptic_curve_class, "getField", "()Ljava/security/spec/ECField;"); + jobject field = env->CallObjectMethod(elliptic_curve, get_field); + + jmethodID get_bits = env->GetMethodID(fp_field_class, "getFieldSize", "()I"); + jint bits = env->CallIntMethod(field, get_bits); + jint bytes = (bits + 7) / 8; + + jmethodID get_a = env->GetMethodID(elliptic_curve_class, "getA", "()Ljava/math/BigInteger;"); + jobject a = env->CallObjectMethod(elliptic_curve, get_a); + + jmethodID get_b = env->GetMethodID(elliptic_curve_class, "getB", "()Ljava/math/BigInteger;"); + jobject b = env->CallObjectMethod(elliptic_curve, get_b); + + jmethodID get_p = env->GetMethodID(fp_field_class, "getP", "()Ljava/math/BigInteger;"); + jobject p = env->CallObjectMethod(field, get_p); + + jmethodID get_g = env->GetMethodID(ec_parameter_spec_class, "getGenerator", "()Ljava/security/spec/ECPoint;"); + jobject g = env->CallObjectMethod(params, get_g); + + jmethodID get_x = env->GetMethodID(point_class, "getAffineX", "()Ljava/math/BigInteger;"); + jobject gx = env->CallObjectMethod(g, get_x); + + jmethodID get_y = env->GetMethodID(point_class, "getAffineY", "()Ljava/math/BigInteger;"); + jobject gy = env->CallObjectMethod(g, get_y); + + jmethodID get_n = env->GetMethodID(ec_parameter_spec_class, "getOrder", "()Ljava/math/BigInteger;"); + jobject n = env->CallObjectMethod(params, get_n); + + jmethodID get_h = env->GetMethodID(ec_parameter_spec_class, "getCofactor", "()I"); + jint h = env->CallIntMethod(params, get_h); + + Botan::BigInt pi = bigint_from_biginteger(env, p); + Botan::BigInt ai = bigint_from_biginteger(env, a); + Botan::BigInt bi = bigint_from_biginteger(env, b); + Botan::CurveGFp curve(pi, ai, bi); + + Botan::BigInt gxi = bigint_from_biginteger(env, gx); + Botan::BigInt gyi = bigint_from_biginteger(env, gy); + Botan::PointGFp generator(curve, gxi, gyi); + + Botan::BigInt ni = bigint_from_biginteger(env, n); + Botan::BigInt hi(h); + + return Botan::EC_Group(curve, generator, ni, hi); + } else if (env->IsInstanceOf(params, ecgen_parameter_spec_class)) { + jmethodID get_name = env->GetMethodID(ecgen_parameter_spec_class, "getName", "()Ljava/lang/String;"); + jstring name = (jstring) env->CallObjectMethod(params, get_name); + const char *utf_name = env->GetStringUTFChars(name, NULL); + std::string curve_name(utf_name); + env->ReleaseStringUTFChars(name, utf_name); + return Botan::EC_Group(curve_name); + } + return Botan::EC_Group(); +} + +static jobject params_from_group(JNIEnv *env, Botan::EC_Group group) { + const Botan::CurveGFp& curve = group.get_curve(); + jobject p = biginteger_from_bigint(env, curve.get_p()); + + jmethodID fp_field_init = env->GetMethodID(fp_field_class, "<init>", "(Ljava/math/BigInteger;)V"); + jobject fp_field = env->NewObject(fp_field_class, fp_field_init, p); + + jobject a = biginteger_from_bigint(env, curve.get_a()); + jobject b = biginteger_from_bigint(env, curve.get_b()); + + jmethodID elliptic_curve_init = env->GetMethodID(elliptic_curve_class, "<init>", "(Ljava/security/spec/ECField;Ljava/math/BigInteger;Ljava/math/BigInteger;)V"); + jobject elliptic_curve = env->NewObject(elliptic_curve_class, elliptic_curve_init, fp_field, a, b); + + const Botan::PointGFp& generator = group.get_base_point(); + jobject gx = biginteger_from_bigint(env, generator.get_affine_x()); + jobject gy = biginteger_from_bigint(env, generator.get_affine_y()); + + jmethodID point_init = env->GetMethodID(point_class, "<init>", "(Ljava/math/BigInteger;Ljava/math/BigInteger;)V"); + jobject g = env->NewObject(point_class, point_init, gx, gy); + + const Botan::BigInt& order = group.get_order(); + jobject n = biginteger_from_bigint(env, order); + + const Botan::BigInt& cofactor = group.get_cofactor(); + jint h = (jint) cofactor.to_u32bit(); + + jmethodID ec_parameter_spec_init = env->GetMethodID(ec_parameter_spec_class, "<init>", "(Ljava/security/spec/EllipticCurve;Ljava/security/spec/ECPoint;Ljava/math/BigInteger;I)V"); + return env->NewObject(ec_parameter_spec_class, ec_parameter_spec_init, elliptic_curve, g, n, h); +} + +static jobject generate_from_group(JNIEnv* env, jobject self, Botan::EC_Group group) { + Botan::AutoSeeded_RNG rng; + + jclass botan_kpg_class = env->FindClass("cz/crcs/ectester/standalone/libs/jni/NativeKeyPairGeneratorSpi$Botan"); + jfieldID type_id = env->GetFieldID(botan_kpg_class, "type", "Ljava/lang/String;"); + jstring type = (jstring) env->GetObjectField(self, type_id); + const char* type_data = env->GetStringUTFChars(type, NULL); + std::string type_str(type_data); + env->ReleaseStringUTFChars(type, type_data); + + std::unique_ptr<Botan::EC_PrivateKey> skey; + if (type_str == "ECDH") { + skey = std::make_unique<Botan::ECDH_PrivateKey>(rng, group); + } else if (type_str == "ECDSA") { + skey = std::make_unique<Botan::ECDSA_PrivateKey>(rng, group); + } else if (type_str == "ECKCDSA") { + skey = std::make_unique<Botan::ECKCDSA_PrivateKey>(rng, group); + } else if (type_str == "ECGDSA") { + skey = std::make_unique<Botan::ECGDSA_PrivateKey>(rng, group); + } + + jobject ec_param_spec = params_from_group(env, group); + + const Botan::PointGFp& pub_point = skey->public_point(); + std::vector<uint8_t> pub_data = Botan::unlock(Botan::EC2OSP(pub_point, Botan::PointGFp::UNCOMPRESSED)); + + jbyteArray pub_bytearray = env->NewByteArray(pub_data.size()); + jbyte *pub_bytes = env->GetByteArrayElements(pub_bytearray, NULL); + std::copy(pub_data.begin(), pub_data.end(), pub_bytes); + env->ReleaseByteArrayElements(pub_bytearray, pub_bytes, JNI_COMMIT); + + jobject ec_pub_param_spec = env->NewLocalRef(ec_param_spec); + jmethodID ec_pub_init = env->GetMethodID(pubkey_class, "<init>", "([BLjava/security/spec/ECParameterSpec;)V"); + jobject pubkey = env->NewObject(pubkey_class, ec_pub_init, pub_bytearray, ec_pub_param_spec); + + const Botan::BigInt& priv_scalar = skey->private_value(); + std::vector<uint8_t> priv_data = Botan::BigInt::encode(priv_scalar); + + jbyteArray priv_bytearray = env->NewByteArray(priv_data.size()); + jbyte *priv_bytes = env->GetByteArrayElements(priv_bytearray, NULL); + std::copy(priv_data.begin(), priv_data.end(), priv_bytes); + env->ReleaseByteArrayElements(priv_bytearray, priv_bytes, JNI_COMMIT); + + jobject ec_priv_param_spec = env->NewLocalRef(ec_param_spec); + jmethodID ec_priv_init = env->GetMethodID(privkey_class, "<init>", "([BLjava/security/spec/ECParameterSpec;)V"); + jobject privkey = env->NewObject(privkey_class, ec_priv_init, priv_bytearray, ec_priv_param_spec); + + jmethodID keypair_init = env->GetMethodID(keypair_class, "<init>", "(Ljava/security/PublicKey;Ljava/security/PrivateKey;)V"); + + return env->NewObject(keypair_class, keypair_init, pubkey, privkey); +} + +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_Botan + * Method: generate + * Signature: (ILjava/security/SecureRandom;)Ljava/security/KeyPair; + */ +JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_00024Botan_generate__ILjava_security_SecureRandom_2(JNIEnv *env, jobject self, jint keysize, jobject random){ + const std::set<std::string>& curves = Botan::EC_Group::known_named_groups(); + for (auto it = curves.begin(); it != curves.end(); ++it) { + Botan::EC_Group curve_group = Botan::EC_Group(*it); + size_t curve_size = curve_group.get_curve().get_p().bits(); + if (curve_size == keysize) { + //generate on this group. Even thou no default groups are present... + return generate_from_group(env, self, curve_group); + } + } + //TODO throw an exception here? InvalidAlgorithmParameters one? + return NULL; +} + +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_Botan + * Method: generate + * Signature: (Ljava/security/spec/AlgorithmParameterSpec;Ljava/security/SecureRandom;)Ljava/security/KeyPair; + */ +JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_00024Botan_generate__Ljava_security_spec_AlgorithmParameterSpec_2Ljava_security_SecureRandom_2(JNIEnv *env, jobject self, jobject params, jobject random){ + Botan::EC_Group curve_group = group_from_params(env, params); + return generate_from_group(env, self, curve_group); +} + +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeKeyAgreementSpi_Botan + * Method: generateSecret + * Signature: ([B[BLjava/security/spec/ECParameterSpec;)[B + */ +JNIEXPORT jbyteArray JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyAgreementSpi_00024Botan_generateSecret(JNIEnv *env, jobject self, jbyteArray pubkey, jbyteArray privkey, jobject params){ + Botan::EC_Group curve_group = group_from_params(env, params); + + jsize privkey_length = env->GetArrayLength(privkey); + jbyte *privkey_data = env->GetByteArrayElements(privkey, NULL); + Botan::BigInt privkey_scalar((unsigned uint8_t*) privkey_data, privkey_length); + env->ReleaseByteArrayElements(privkey, privkey_data, JNI_ABORT); + + Botan::AutoSeeded_RNG rng; + + Botan::ECDH_PrivateKey skey(rng, curve_group, privkey_scalar); + + jsize pubkey_length = env->GetArrayLength(pubkey); + jbyte *pubkey_data = env->GetByteArrayElements(pubkey, NULL); + Botan::PointGFp public_point = Botan::OS2ECP((uint8_t*) pubkey_data, pubkey_length, curve_group.get_curve()); + env->ReleaseByteArrayElements(pubkey, pubkey_data, JNI_ABORT); + + Botan::ECDH_PublicKey pkey(curve_group, public_point); + //TODO: do check_key here? + + jclass botan_ka_class = env->FindClass("cz/crcs/ectester/standalone/libs/jni/NativeKeyAgreementSpi$Botan"); + jfieldID type_id = env->GetFieldID(botan_ka_class, "type", "Ljava/lang/String;"); + jstring type = (jstring) env->GetObjectField(self, type_id); + const char *type_data = env->GetStringUTFChars(type, NULL); + std::string type_str(type_data); + env->ReleaseStringUTFChars(type, type_data); + + std::string kdf; + size_t key_len = 0; + if (type_str == "ECDH") { + kdf = "Raw"; + //key len unused + } else if (type_str == "ECDHwithSHA1KDF") { + kdf = "KDF1(SHA-1)"; + key_len = 20; + } else if (type_str == "ECDHwithSHA224KDF") { + kdf = "KDF1(SHA-224)"; + key_len = 28; + } else if (type_str == "ECDHwithSHA256KDF") { + kdf = "KDF1(SHA-256)"; + key_len = 32; + } else if (type_str == "ECDHwithSHA384KDF") { + kdf = "KDF1(SHA-384)"; + key_len = 48; + } else if (type_str == "ECDHwithSHA512KDF") { + kdf = "KDF1(SHA-512)"; + key_len = 64; + } else { + //TODO what? + } + + Botan::PK_Key_Agreement ka(skey, rng, kdf); + + std::vector<uint8_t> derived = Botan::unlock(ka.derive_key(key_len, pkey.public_value()).bits_of()); + jbyteArray result = env->NewByteArray(derived.size()); + jbyte *result_data = env->GetByteArrayElements(result, NULL); + std::copy(derived.begin(), derived.end(), result_data); + env->ReleaseByteArrayElements(result, result_data, JNI_COMMIT); + + return result; +} + +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeSignatureSpi_Botan + * Method: sign + * Signature: ([B[BLjava/security/spec/ECParameterSpec;)[B + */ +JNIEXPORT jbyteArray JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSignatureSpi_00024Botan_sign(JNIEnv *env, jobject self, jbyteArray data, jbyteArray privkey, jobject params){ + Botan::EC_Group curve_group = group_from_params(env, params); + + jclass botan_sig_class = env->FindClass("cz/crcs/ectester/standalone/libs/jni/NativeSignatureSpi$Botan"); + jfieldID type_id = env->GetFieldID(botan_sig_class, "type", "Ljava/lang/String;"); + jstring type = (jstring) env->GetObjectField(self, type_id); + const char *type_data = env->GetStringUTFChars(type, NULL); + std::string type_str(type_data); + env->ReleaseStringUTFChars(type, type_data); + + jsize privkey_length = env->GetArrayLength(privkey); + jbyte *privkey_bytes = env->GetByteArrayElements(privkey, NULL); + Botan::BigInt privkey_scalar((uint8_t*) privkey_bytes, privkey_length); + env->ReleaseByteArrayElements(privkey, privkey_bytes, JNI_ABORT); + + Botan::AutoSeeded_RNG rng; + + std::unique_ptr<Botan::EC_PrivateKey> skey; + if (type_str.find("ECDSA") != std::string::npos) { + skey = std::make_unique<Botan::ECDSA_PrivateKey>(rng, curve_group, privkey_scalar); + } else if (type_str.find("ECKCDSA") != std::string::npos) { + skey = std::make_unique<Botan::ECKCDSA_PrivateKey>(rng, curve_group, privkey_scalar); + } else if (type_str.find("ECGDSA") != std::string::npos) { + skey = std::make_unique<Botan::ECGDSA_PrivateKey>(rng, curve_group, privkey_scalar); + } + + std::string kdf; + if (type_str.find("NONE") != std::string::npos) { + kdf = "Raw"; + } else if (type_str.find("SHA1") != std::string::npos) { + kdf = "EMSA1(SHA-1)"; + } else if (type_str.find("SHA224") != std::string::npos) { + kdf = "EMSA1(SHA-224)"; + } else if (type_str.find("SHA256") != std::string::npos) { + kdf = "EMSA1(SHA-256)"; + } else if (type_str.find("SHA384") != std::string::npos) { + kdf = "EMSA1(SHA-384)"; + } else if (type_str.find("SHA512") != std::string::npos) { + kdf = "EMSA1(SHA-512)"; + } + + Botan::PK_Signer signer(*skey, rng, kdf, Botan::DER_SEQUENCE); + + jsize data_length = env->GetArrayLength(data); + jbyte *data_bytes = env->GetByteArrayElements(data, NULL); + std::vector<uint8_t> sig = signer.sign_message((uint8_t*) data_bytes, data_length, rng); + env->ReleaseByteArrayElements(data, data_bytes, JNI_ABORT); + + jbyteArray result = env->NewByteArray(sig.size()); + jbyte *result_data = env->GetByteArrayElements(result, NULL); + std::copy(sig.begin(), sig.end(), result_data); + env->ReleaseByteArrayElements(result, result_data, JNI_COMMIT); + + return result; +} + +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeSignatureSpi_Botan + * Method: verify + * Signature: ([B[B[BLjava/security/spec/ECParameterSpec;)Z + */ +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSignatureSpi_00024Botan_verify(JNIEnv *env, jobject self, jbyteArray signature, jbyteArray data, jbyteArray pubkey, jobject params){ + Botan::EC_Group curve_group = group_from_params(env, params); + + jclass botan_sig_class = env->FindClass("cz/crcs/ectester/standalone/libs/jni/NativeSignatureSpi$Botan"); + jfieldID type_id = env->GetFieldID(botan_sig_class, "type", "Ljava/lang/String;"); + jstring type = (jstring) env->GetObjectField(self, type_id); + const char *type_data = env->GetStringUTFChars(type, NULL); + std::string type_str(type_data); + env->ReleaseStringUTFChars(type, type_data); + + jsize pubkey_length = env->GetArrayLength(pubkey); + jbyte *pubkey_data = env->GetByteArrayElements(pubkey, NULL); + Botan::PointGFp public_point = Botan::OS2ECP((uint8_t*) pubkey_data, pubkey_length, curve_group.get_curve()); + env->ReleaseByteArrayElements(pubkey, pubkey_data, JNI_ABORT); + + std::unique_ptr<Botan::EC_PublicKey> pkey; + if (type_str.find("ECDSA") != std::string::npos) { + pkey = std::make_unique<Botan::ECDSA_PublicKey>(curve_group, public_point); + } else if (type_str.find("ECKCDSA") != std::string::npos) { + pkey = std::make_unique<Botan::ECKCDSA_PublicKey>(curve_group, public_point); + } else if (type_str.find("ECGDSA") != std::string::npos) { + pkey = std::make_unique<Botan::ECGDSA_PublicKey>(curve_group, public_point); + } + + std::string kdf; + if (type_str.find("NONE") != std::string::npos) { + kdf = "Raw"; + } else if (type_str.find("SHA1") != std::string::npos) { + kdf = "EMSA1(SHA-1)"; + } else if (type_str.find("SHA224") != std::string::npos) { + kdf = "EMSA1(SHA-224)"; + } else if (type_str.find("SHA256") != std::string::npos) { + kdf = "EMSA1(SHA-256)"; + } else if (type_str.find("SHA384") != std::string::npos) { + kdf = "EMSA1(SHA-384)"; + } else if (type_str.find("SHA512") != std::string::npos) { + kdf = "EMSA1(SHA-512)"; + } + + Botan::PK_Verifier verifier(*pkey, kdf, Botan::DER_SEQUENCE); + + jsize data_length = env->GetArrayLength(data); + jsize sig_length = env->GetArrayLength(signature); + jbyte *data_bytes = env->GetByteArrayElements(data, NULL); + jbyte *sig_bytes = env->GetByteArrayElements(signature, NULL); + + bool result = verifier.verify_message((uint8_t*)data_bytes, data_length, (uint8_t*)sig_bytes, sig_length); + env->ReleaseByteArrayElements(data, data_bytes, JNI_ABORT); + env->ReleaseByteArrayElements(signature, sig_bytes, JNI_ABORT); + if (result) { + return JNI_TRUE; + } + return JNI_FALSE; +}
\ No newline at end of file diff --git a/src/cz/crcs/ectester/standalone/libs/jni/c_utils.c b/src/cz/crcs/ectester/standalone/libs/jni/c_utils.c new file mode 100644 index 0000000..230f516 --- /dev/null +++ b/src/cz/crcs/ectester/standalone/libs/jni/c_utils.c @@ -0,0 +1,61 @@ +#include "c_utils.h" +#include <string.h> + +jclass ec_parameter_spec_class; +jclass ecgen_parameter_spec_class; +jclass pubkey_class; +jclass privkey_class; +jclass keypair_class; +jclass elliptic_curve_class; +jclass fp_field_class; +jclass f2m_field_class; +jclass point_class; +jclass biginteger_class; +jclass illegal_state_exception_class; + +void init_classes(JNIEnv *env, const char* lib_name) { + jclass local_ec_parameter_spec_class = (*env)->FindClass(env, "java/security/spec/ECParameterSpec"); + ec_parameter_spec_class = (*env)->NewGlobalRef(env, local_ec_parameter_spec_class); + + jclass local_ecgen_parameter_spec_class = (*env)->FindClass(env, "java/security/spec/ECGenParameterSpec"); + ecgen_parameter_spec_class = (*env)->NewGlobalRef(env, local_ecgen_parameter_spec_class); + + const char *pubkey_base = "cz/crcs/ectester/standalone/libs/jni/NativeECPublicKey$"; + char pubkey_class_name[strlen(pubkey_base) + strlen(lib_name) + 1]; + pubkey_class_name[0] = 0; + strcat(pubkey_class_name, pubkey_base); + strcat(pubkey_class_name, lib_name); + + jclass local_pubkey_class = (*env)->FindClass(env, pubkey_class_name); + pubkey_class = (*env)->NewGlobalRef(env, local_pubkey_class); + + const char *privkey_base = "cz/crcs/ectester/standalone/libs/jni/NativeECPrivateKey$"; + char privkey_class_name[strlen(privkey_base) + strlen(lib_name) + 1]; + privkey_class_name[0] = 0; + strcat(privkey_class_name, privkey_base); + strcat(privkey_class_name, lib_name); + + jclass local_privkey_class = (*env)->FindClass(env, privkey_class_name); + privkey_class = (*env)->NewGlobalRef(env, local_privkey_class); + + jclass local_keypair_class = (*env)->FindClass(env, "java/security/KeyPair"); + keypair_class = (*env)->NewGlobalRef(env, local_keypair_class); + + jclass local_elliptic_curve_class = (*env)->FindClass(env, "java/security/spec/EllipticCurve"); + elliptic_curve_class = (*env)->NewGlobalRef(env, local_elliptic_curve_class); + + jclass local_fp_field_class = (*env)->FindClass(env, "java/security/spec/ECFieldFp"); + fp_field_class = (*env)->NewGlobalRef(env, local_fp_field_class); + + jclass local_f2m_field_class = (*env)->FindClass(env, "java/security/spec/ECFieldF2m"); + f2m_field_class = (*env)->NewGlobalRef(env, local_f2m_field_class); + + jclass local_biginteger_class = (*env)->FindClass(env, "java/math/BigInteger"); + biginteger_class = (*env)->NewGlobalRef(env, local_biginteger_class); + + jclass local_point_class = (*env)->FindClass(env, "java/security/spec/ECPoint"); + point_class = (*env)->NewGlobalRef(env, local_point_class); + + jclass local_illegal_state_exception_class = (*env)->FindClass(env, "java/lang/IllegalStateException"); + illegal_state_exception_class = (*env)->NewGlobalRef(env, local_illegal_state_exception_class); +}
\ No newline at end of file diff --git a/src/cz/crcs/ectester/standalone/libs/jni/c_utils.h b/src/cz/crcs/ectester/standalone/libs/jni/c_utils.h new file mode 100644 index 0000000..edd0bda --- /dev/null +++ b/src/cz/crcs/ectester/standalone/libs/jni/c_utils.h @@ -0,0 +1,15 @@ +#include "native.h" + +extern jclass ec_parameter_spec_class; +extern jclass ecgen_parameter_spec_class; +extern jclass pubkey_class; +extern jclass privkey_class; +extern jclass keypair_class; +extern jclass elliptic_curve_class; +extern jclass fp_field_class; +extern jclass f2m_field_class; +extern jclass point_class; +extern jclass biginteger_class; +extern jclass illegal_state_exception_class; + +void init_classes(JNIEnv *env, const char* lib_name);
\ No newline at end of file diff --git a/src/cz/crcs/ectester/standalone/libs/jni/cpp_utils.cpp b/src/cz/crcs/ectester/standalone/libs/jni/cpp_utils.cpp new file mode 100644 index 0000000..ed59d51 --- /dev/null +++ b/src/cz/crcs/ectester/standalone/libs/jni/cpp_utils.cpp @@ -0,0 +1,54 @@ +#include "cpp_utils.hpp" + +jclass ec_parameter_spec_class; +jclass ecgen_parameter_spec_class; +jclass pubkey_class; +jclass privkey_class; +jclass keypair_class; +jclass elliptic_curve_class; +jclass fp_field_class; +jclass f2m_field_class; +jclass point_class; +jclass biginteger_class; +jclass illegal_state_exception_class; + +void init_classes(JNIEnv *env, std::string lib_name) { + jclass local_ec_parameter_spec_class = env->FindClass("java/security/spec/ECParameterSpec"); + ec_parameter_spec_class = (jclass) env->NewGlobalRef(local_ec_parameter_spec_class); + + jclass local_ecgen_parameter_spec_class = env->FindClass("java/security/spec/ECGenParameterSpec"); + ecgen_parameter_spec_class = (jclass) env->NewGlobalRef(local_ecgen_parameter_spec_class); + + std::string pubkey_class_name("cz/crcs/ectester/standalone/libs/jni/NativeECPublicKey$"); + pubkey_class_name += lib_name; + + jclass local_pubkey_class = env->FindClass(pubkey_class_name.c_str()); + pubkey_class = (jclass) env->NewGlobalRef(local_pubkey_class); + + std::string privkey_class_name("cz/crcs/ectester/standalone/libs/jni/NativeECPrivateKey$"); + privkey_class_name += lib_name; + + jclass local_privkey_class = env->FindClass(privkey_class_name.c_str()); + privkey_class = (jclass) env->NewGlobalRef(local_privkey_class); + + jclass local_keypair_class = env->FindClass("java/security/KeyPair"); + keypair_class = (jclass) env->NewGlobalRef(local_keypair_class); + + jclass local_elliptic_curve_class = env->FindClass("java/security/spec/EllipticCurve"); + elliptic_curve_class = (jclass) env->NewGlobalRef(local_elliptic_curve_class); + + jclass local_fp_field_class = env->FindClass("java/security/spec/ECFieldFp"); + fp_field_class = (jclass) env->NewGlobalRef(local_fp_field_class); + + jclass local_f2m_field_class = env->FindClass("java/security/spec/ECFieldF2m"); + f2m_field_class = (jclass) env->NewGlobalRef(local_f2m_field_class); + + jclass local_biginteger_class = env->FindClass("java/math/BigInteger"); + biginteger_class = (jclass) env->NewGlobalRef(local_biginteger_class); + + jclass local_point_class = env->FindClass("java/security/spec/ECPoint"); + point_class = (jclass) env->NewGlobalRef(local_point_class); + + jclass local_illegal_state_exception_class = env->FindClass("java/lang/IllegalStateException"); + illegal_state_exception_class = (jclass) env->NewGlobalRef(local_illegal_state_exception_class); +}
\ No newline at end of file diff --git a/src/cz/crcs/ectester/standalone/libs/jni/cpp_utils.hpp b/src/cz/crcs/ectester/standalone/libs/jni/cpp_utils.hpp new file mode 100644 index 0000000..d0bf8f2 --- /dev/null +++ b/src/cz/crcs/ectester/standalone/libs/jni/cpp_utils.hpp @@ -0,0 +1,16 @@ +#include "native.h" +#include <string> + +extern jclass ec_parameter_spec_class; +extern jclass ecgen_parameter_spec_class; +extern jclass pubkey_class; +extern jclass privkey_class; +extern jclass keypair_class; +extern jclass elliptic_curve_class; +extern jclass fp_field_class; +extern jclass f2m_field_class; +extern jclass point_class; +extern jclass biginteger_class; +extern jclass illegal_state_exception_class; + +void init_classes(JNIEnv *env, std::string lib_name);
\ No newline at end of file diff --git a/src/cz/crcs/ectester/standalone/libs/jni/native.h b/src/cz/crcs/ectester/standalone/libs/jni/native.h new file mode 100644 index 0000000..d714b39 --- /dev/null +++ b/src/cz/crcs/ectester/standalone/libs/jni/native.h @@ -0,0 +1,344 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include <jni.h> +/* Header for class cz_crcs_ectester_standalone_libs_TomcryptLib */ + +#ifndef _Included_cz_crcs_ectester_standalone_libs_TomcryptLib +#define _Included_cz_crcs_ectester_standalone_libs_TomcryptLib +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: cz_crcs_ectester_standalone_libs_TomcryptLib + * Method: createProvider + * Signature: ()Ljava/security/Provider; + */ +JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_TomcryptLib_createProvider + (JNIEnv *, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_TomcryptLib + * Method: getCurves + * Signature: ()Ljava/util/Set; + */ +JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_TomcryptLib_getCurves + (JNIEnv *, jobject); + +#ifdef __cplusplus +} +#endif +#endif +/* Header for class cz_crcs_ectester_standalone_libs_jni_NativeProvider_TomCrypt */ + +#ifndef _Included_cz_crcs_ectester_standalone_libs_jni_NativeProvider_TomCrypt +#define _Included_cz_crcs_ectester_standalone_libs_jni_NativeProvider_TomCrypt +#ifdef __cplusplus +extern "C" { +#endif +#undef cz_crcs_ectester_standalone_libs_jni_NativeProvider_TomCrypt_serialVersionUID +#define cz_crcs_ectester_standalone_libs_jni_NativeProvider_TomCrypt_serialVersionUID 1421746759512286392LL +#undef cz_crcs_ectester_standalone_libs_jni_NativeProvider_TomCrypt_MAX_ARRAY_SIZE +#define cz_crcs_ectester_standalone_libs_jni_NativeProvider_TomCrypt_MAX_ARRAY_SIZE 2147483639L +#undef cz_crcs_ectester_standalone_libs_jni_NativeProvider_TomCrypt_KEYS +#define cz_crcs_ectester_standalone_libs_jni_NativeProvider_TomCrypt_KEYS 0L +#undef cz_crcs_ectester_standalone_libs_jni_NativeProvider_TomCrypt_VALUES +#define cz_crcs_ectester_standalone_libs_jni_NativeProvider_TomCrypt_VALUES 1L +#undef cz_crcs_ectester_standalone_libs_jni_NativeProvider_TomCrypt_ENTRIES +#define cz_crcs_ectester_standalone_libs_jni_NativeProvider_TomCrypt_ENTRIES 2L +#undef cz_crcs_ectester_standalone_libs_jni_NativeProvider_TomCrypt_serialVersionUID +#define cz_crcs_ectester_standalone_libs_jni_NativeProvider_TomCrypt_serialVersionUID 4112578634029874840LL +#undef cz_crcs_ectester_standalone_libs_jni_NativeProvider_TomCrypt_serialVersionUID +#define cz_crcs_ectester_standalone_libs_jni_NativeProvider_TomCrypt_serialVersionUID -4298000515446427739LL +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeProvider_TomCrypt + * Method: setup + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeProvider_00024TomCrypt_setup + (JNIEnv *, jobject); + +#ifdef __cplusplus +} +#endif +#endif +/* Header for class cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_TomCrypt */ + +#ifndef _Included_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_TomCrypt +#define _Included_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_TomCrypt +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_TomCrypt + * Method: keysizeSupported + * Signature: (I)Z + */ +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_00024TomCrypt_keysizeSupported + (JNIEnv *, jobject, jint); + +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_TomCrypt + * Method: paramsSupported + * Signature: (Ljava/security/spec/AlgorithmParameterSpec;)Z + */ +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_00024TomCrypt_paramsSupported + (JNIEnv *, jobject, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_TomCrypt + * Method: generate + * Signature: (ILjava/security/SecureRandom;)Ljava/security/KeyPair; + */ +JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_00024TomCrypt_generate__ILjava_security_SecureRandom_2 + (JNIEnv *, jobject, jint, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_TomCrypt + * Method: generate + * Signature: (Ljava/security/spec/AlgorithmParameterSpec;Ljava/security/SecureRandom;)Ljava/security/KeyPair; + */ +JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_00024TomCrypt_generate__Ljava_security_spec_AlgorithmParameterSpec_2Ljava_security_SecureRandom_2 + (JNIEnv *, jobject, jobject, jobject); + +#ifdef __cplusplus +} +#endif +#endif +/* Header for class cz_crcs_ectester_standalone_libs_jni_NativeECPublicKey_TomCrypt */ + +#ifndef _Included_cz_crcs_ectester_standalone_libs_jni_NativeECPublicKey_TomCrypt +#define _Included_cz_crcs_ectester_standalone_libs_jni_NativeECPublicKey_TomCrypt +#ifdef __cplusplus +extern "C" { +#endif +#ifdef __cplusplus +} +#endif +#endif +/* Header for class cz_crcs_ectester_standalone_libs_jni_NativeECPrivateKey_TomCrypt */ + +#ifndef _Included_cz_crcs_ectester_standalone_libs_jni_NativeECPrivateKey_TomCrypt +#define _Included_cz_crcs_ectester_standalone_libs_jni_NativeECPrivateKey_TomCrypt +#ifdef __cplusplus +extern "C" { +#endif +#ifdef __cplusplus +} +#endif +#endif +/* Header for class cz_crcs_ectester_standalone_libs_jni_NativeKeyAgreementSpi_TomCrypt */ + +#ifndef _Included_cz_crcs_ectester_standalone_libs_jni_NativeKeyAgreementSpi_TomCrypt +#define _Included_cz_crcs_ectester_standalone_libs_jni_NativeKeyAgreementSpi_TomCrypt +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeKeyAgreementSpi_TomCrypt + * Method: generateSecret + * Signature: ([B[BLjava/security/spec/ECParameterSpec;)[B + */ +JNIEXPORT jbyteArray JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyAgreementSpi_00024TomCrypt_generateSecret + (JNIEnv *, jobject, jbyteArray, jbyteArray, jobject); + +#ifdef __cplusplus +} +#endif +#endif +/* Header for class cz_crcs_ectester_standalone_libs_jni_NativeSignatureSpi_TomCryptRaw */ + +#ifndef _Included_cz_crcs_ectester_standalone_libs_jni_NativeSignatureSpi_TomCryptRaw +#define _Included_cz_crcs_ectester_standalone_libs_jni_NativeSignatureSpi_TomCryptRaw +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeSignatureSpi_TomCryptRaw + * Method: sign + * Signature: ([B[BLjava/security/spec/ECParameterSpec;)[B + */ +JNIEXPORT jbyteArray JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSignatureSpi_00024TomCryptRaw_sign + (JNIEnv *, jobject, jbyteArray, jbyteArray, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeSignatureSpi_TomCryptRaw + * Method: verify + * Signature: ([B[B[BLjava/security/spec/ECParameterSpec;)Z + */ +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSignatureSpi_00024TomCryptRaw_verify + (JNIEnv *, jobject, jbyteArray, jbyteArray, jbyteArray, jobject); + +#ifdef __cplusplus +} +#endif +#endif +/* Header for class cz_crcs_ectester_standalone_libs_BotanLib */ + +#ifndef _Included_cz_crcs_ectester_standalone_libs_BotanLib +#define _Included_cz_crcs_ectester_standalone_libs_BotanLib +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: cz_crcs_ectester_standalone_libs_BotanLib + * Method: createProvider + * Signature: ()Ljava/security/Provider; + */ +JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_BotanLib_createProvider + (JNIEnv *, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_BotanLib + * Method: getCurves + * Signature: ()Ljava/util/Set; + */ +JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_BotanLib_getCurves + (JNIEnv *, jobject); + +#ifdef __cplusplus +} +#endif +#endif +/* Header for class cz_crcs_ectester_standalone_libs_jni_NativeProvider_Botan */ + +#ifndef _Included_cz_crcs_ectester_standalone_libs_jni_NativeProvider_Botan +#define _Included_cz_crcs_ectester_standalone_libs_jni_NativeProvider_Botan +#ifdef __cplusplus +extern "C" { +#endif +#undef cz_crcs_ectester_standalone_libs_jni_NativeProvider_Botan_serialVersionUID +#define cz_crcs_ectester_standalone_libs_jni_NativeProvider_Botan_serialVersionUID 1421746759512286392LL +#undef cz_crcs_ectester_standalone_libs_jni_NativeProvider_Botan_MAX_ARRAY_SIZE +#define cz_crcs_ectester_standalone_libs_jni_NativeProvider_Botan_MAX_ARRAY_SIZE 2147483639L +#undef cz_crcs_ectester_standalone_libs_jni_NativeProvider_Botan_KEYS +#define cz_crcs_ectester_standalone_libs_jni_NativeProvider_Botan_KEYS 0L +#undef cz_crcs_ectester_standalone_libs_jni_NativeProvider_Botan_VALUES +#define cz_crcs_ectester_standalone_libs_jni_NativeProvider_Botan_VALUES 1L +#undef cz_crcs_ectester_standalone_libs_jni_NativeProvider_Botan_ENTRIES +#define cz_crcs_ectester_standalone_libs_jni_NativeProvider_Botan_ENTRIES 2L +#undef cz_crcs_ectester_standalone_libs_jni_NativeProvider_Botan_serialVersionUID +#define cz_crcs_ectester_standalone_libs_jni_NativeProvider_Botan_serialVersionUID 4112578634029874840LL +#undef cz_crcs_ectester_standalone_libs_jni_NativeProvider_Botan_serialVersionUID +#define cz_crcs_ectester_standalone_libs_jni_NativeProvider_Botan_serialVersionUID -4298000515446427739LL +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeProvider_Botan + * Method: setup + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeProvider_00024Botan_setup + (JNIEnv *, jobject); + +#ifdef __cplusplus +} +#endif +#endif +/* Header for class cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_Botan */ + +#ifndef _Included_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_Botan +#define _Included_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_Botan +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_Botan + * Method: keysizeSupported + * Signature: (I)Z + */ +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_00024Botan_keysizeSupported + (JNIEnv *, jobject, jint); + +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_Botan + * Method: paramsSupported + * Signature: (Ljava/security/spec/AlgorithmParameterSpec;)Z + */ +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_00024Botan_paramsSupported + (JNIEnv *, jobject, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_Botan + * Method: generate + * Signature: (ILjava/security/SecureRandom;)Ljava/security/KeyPair; + */ +JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_00024Botan_generate__ILjava_security_SecureRandom_2 + (JNIEnv *, jobject, jint, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_Botan + * Method: generate + * Signature: (Ljava/security/spec/AlgorithmParameterSpec;Ljava/security/SecureRandom;)Ljava/security/KeyPair; + */ +JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_00024Botan_generate__Ljava_security_spec_AlgorithmParameterSpec_2Ljava_security_SecureRandom_2 + (JNIEnv *, jobject, jobject, jobject); + +#ifdef __cplusplus +} +#endif +#endif +/* Header for class cz_crcs_ectester_standalone_libs_jni_NativeECPublicKey_Botan */ + +#ifndef _Included_cz_crcs_ectester_standalone_libs_jni_NativeECPublicKey_Botan +#define _Included_cz_crcs_ectester_standalone_libs_jni_NativeECPublicKey_Botan +#ifdef __cplusplus +extern "C" { +#endif +#ifdef __cplusplus +} +#endif +#endif +/* Header for class cz_crcs_ectester_standalone_libs_jni_NativeECPrivateKey_Botan */ + +#ifndef _Included_cz_crcs_ectester_standalone_libs_jni_NativeECPrivateKey_Botan +#define _Included_cz_crcs_ectester_standalone_libs_jni_NativeECPrivateKey_Botan +#ifdef __cplusplus +extern "C" { +#endif +#ifdef __cplusplus +} +#endif +#endif +/* Header for class cz_crcs_ectester_standalone_libs_jni_NativeKeyAgreementSpi_Botan */ + +#ifndef _Included_cz_crcs_ectester_standalone_libs_jni_NativeKeyAgreementSpi_Botan +#define _Included_cz_crcs_ectester_standalone_libs_jni_NativeKeyAgreementSpi_Botan +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeKeyAgreementSpi_Botan + * Method: generateSecret + * Signature: ([B[BLjava/security/spec/ECParameterSpec;)[B + */ +JNIEXPORT jbyteArray JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyAgreementSpi_00024Botan_generateSecret + (JNIEnv *, jobject, jbyteArray, jbyteArray, jobject); + +#ifdef __cplusplus +} +#endif +#endif +/* Header for class cz_crcs_ectester_standalone_libs_jni_NativeSignatureSpi_Botan */ + +#ifndef _Included_cz_crcs_ectester_standalone_libs_jni_NativeSignatureSpi_Botan +#define _Included_cz_crcs_ectester_standalone_libs_jni_NativeSignatureSpi_Botan +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeSignatureSpi_Botan + * Method: sign + * Signature: ([B[BLjava/security/spec/ECParameterSpec;)[B + */ +JNIEXPORT jbyteArray JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSignatureSpi_00024Botan_sign + (JNIEnv *, jobject, jbyteArray, jbyteArray, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeSignatureSpi_Botan + * Method: verify + * Signature: ([B[B[BLjava/security/spec/ECParameterSpec;)Z + */ +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSignatureSpi_00024Botan_verify + (JNIEnv *, jobject, jbyteArray, jbyteArray, jbyteArray, jobject); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/cz/crcs/ectester/standalone/libs/jni/tomcrypt.c b/src/cz/crcs/ectester/standalone/libs/jni/tomcrypt.c new file mode 100644 index 0000000..0fb69a3 --- /dev/null +++ b/src/cz/crcs/ectester/standalone/libs/jni/tomcrypt.c @@ -0,0 +1,453 @@ +#include "native.h" +#include <stdio.h> +#include <string.h> +#include <tomcrypt.h> +#include "c_utils.h" + +static prng_state ltc_prng; +static jclass provider_class; + +JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_TomcryptLib_createProvider(JNIEnv *env, jobject this) { + /* Create the custom provider. */ + jclass local_provider_class = (*env)->FindClass(env, "cz/crcs/ectester/standalone/libs/jni/NativeProvider$TomCrypt"); + provider_class = (*env)->NewGlobalRef(env, local_provider_class); + + jmethodID init = (*env)->GetMethodID(env, local_provider_class, "<init>", "(Ljava/lang/String;DLjava/lang/String;)V"); + + jstring name = (*env)->NewStringUTF(env, "libtomcrypt " SCRYPT); + double version = strtod(SCRYPT, NULL); + + return (*env)->NewObject(env, provider_class, init, name, version, name); +} + + +JNIEXPORT void JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeProvider_00024TomCrypt_setup(JNIEnv *env, jobject this) { + /* Initialize libtommath as the math lib. */ + ltc_mp = ltm_desc; + + jmethodID provider_put = (*env)->GetMethodID(env, provider_class, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); + + jstring ec = (*env)->NewStringUTF(env, "KeyPairGenerator.EC"); + jstring ec_value = (*env)->NewStringUTF(env, "cz.crcs.ectester.standalone.libs.jni.NativeKeyPairGeneratorSpi$TomCrypt"); + (*env)->CallObjectMethod(env, this, provider_put, ec, ec_value); + + jstring ecdh = (*env)->NewStringUTF(env, "KeyAgreement.ECDH"); + jstring ecdh_value = (*env)->NewStringUTF(env, "cz.crcs.ectester.standalone.libs.jni.NativeKeyAgreementSpi$TomCrypt"); + (*env)->CallObjectMethod(env, this, provider_put, ecdh, ecdh_value); + + jstring ecdsa = (*env)->NewStringUTF(env, "Signature.NONEwithECDSA"); + jstring ecdsa_value = (*env)->NewStringUTF(env, "cz.crcs.ectester.standalone.libs.jni.NativeSignatureSpi$TomCryptRaw"); + (*env)->CallObjectMethod(env, this, provider_put, ecdsa, ecdsa_value); + + int err; + /* register yarrow */ + if (register_prng(&yarrow_desc) == -1) { + fprintf(stderr, "Error registering Yarrow\n"); + return; + } + /* setup the PRNG */ + if ((err = rng_make_prng(128, find_prng("yarrow"), <c_prng, NULL)) != CRYPT_OK) { + fprintf(stderr, "Error setting up PRNG, %s\n", error_to_string(err)); + } + + init_classes(env, "TomCrypt"); +} + +JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_TomcryptLib_getCurves(JNIEnv *env, jobject this) { + jclass hash_set_class = (*env)->FindClass(env, "java/util/TreeSet"); + + jmethodID hash_set_ctr = (*env)->GetMethodID(env, hash_set_class, "<init>", "()V"); + jmethodID hash_set_add = (*env)->GetMethodID(env, hash_set_class, "add", "(Ljava/lang/Object;)Z"); + + jobject result = (*env)->NewObject(env, hash_set_class, hash_set_ctr); + const ltc_ecc_set_type * curve = ltc_ecc_sets; + while (curve->size != 0) { + jstring curve_name = (*env)->NewStringUTF(env, curve->name); + (*env)->CallBooleanMethod(env, result, hash_set_add, curve_name); + curve++; + } + + return result; +} + +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_00024TomCrypt_keysizeSupported(JNIEnv *env, jobject this, jint keysize){ + int key_bytes = (keysize + 7) / 8; + const ltc_ecc_set_type * curve = ltc_ecc_sets; + while (curve->size != 0) { + if (curve->size == key_bytes) { + return JNI_TRUE; + } + curve++; + } + + return JNI_FALSE; +} + +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_00024TomCrypt_paramsSupported(JNIEnv *env, jobject this, jobject params){ + if (params == NULL) { + return JNI_FALSE; + } + + if ((*env)->IsInstanceOf(env, params, ec_parameter_spec_class)) { + jmethodID get_curve = (*env)->GetMethodID(env, ec_parameter_spec_class, "getCurve", "()Ljava/security/spec/EllipticCurve;"); + jobject curve = (*env)->CallObjectMethod(env, params, get_curve); + + jmethodID get_field = (*env)->GetMethodID(env, elliptic_curve_class, "getField", "()Ljava/security/spec/ECField;"); + jobject field = (*env)->CallObjectMethod(env, curve, get_field); + + if ((*env)->IsInstanceOf(env, field, fp_field_class)) { + jmethodID get_p = (*env)->GetMethodID(env, fp_field_class, "getP", "()Ljava/math/BigInteger;"); + jobject p = (*env)->CallObjectMethod(env, field, get_p); + + jmethodID get_a = (*env)->GetMethodID(env, elliptic_curve_class, "getA", "()Ljava/math/BigInteger;"); + jobject a = (*env)->CallObjectMethod(env, curve, get_a); + + jmethodID biginteger_valueof = (*env)->GetStaticMethodID(env, biginteger_class, "valueOf", "(J)Ljava/math/BigInteger;"); + jobject three = (*env)->CallStaticObjectMethod(env, biginteger_class, biginteger_valueof, (jlong)3); + + jmethodID biginteger_add = (*env)->GetMethodID(env, biginteger_class, "add", "(Ljava/math/BigInteger;)Ljava/math/BigInteger;"); + jobject a_3 = (*env)->CallObjectMethod(env, a, biginteger_add, three); + + jmethodID biginteger_equals = (*env)->GetMethodID(env, biginteger_class, "equals", "(Ljava/lang/Object;)Z"); + jboolean eq = (*env)->CallBooleanMethod(env, p, biginteger_equals, a_3); + return eq; + } else if ((*env)->IsInstanceOf(env, field, f2m_field_class)) { + return JNI_FALSE; + } else { + return JNI_FALSE; + } + } else if ((*env)->IsInstanceOf(env, params, ecgen_parameter_spec_class)) { + jmethodID get_name = (*env)->GetMethodID(env, ecgen_parameter_spec_class, "getName", "()Ljava/lang/String;"); + jstring name = (*env)->CallObjectMethod(env, params, get_name); + const char *utf_name = (*env)->GetStringUTFChars(env, name, NULL); + const ltc_ecc_set_type * curve = ltc_ecc_sets; + while (curve->size != 0) { + if (strcasecmp(utf_name, curve->name) == 0) { + (*env)->ReleaseStringUTFChars(env, name, utf_name); + return JNI_TRUE; + } + curve++; + } + return JNI_FALSE; + } else { + return JNI_FALSE; + } +} + +static jobject create_ec_param_spec(JNIEnv *env, const ltc_ecc_set_type *curve) { + jstring p_string = (*env)->NewStringUTF(env, curve->prime); + jmethodID biginteger_init = (*env)->GetMethodID(env, biginteger_class, "<init>", "(Ljava/lang/String;I)V"); + jobject p = (*env)->NewObject(env, biginteger_class, biginteger_init, p_string, (jint) 16); + + jmethodID fp_field_init = (*env)->GetMethodID(env, fp_field_class, "<init>", "(Ljava/math/BigInteger;)V"); + jobject field = (*env)->NewObject(env, fp_field_class, fp_field_init, p); + + jmethodID biginteger_subtract = (*env)->GetMethodID(env, biginteger_class, "subtract", "(Ljava/math/BigInteger;)Ljava/math/BigInteger;"); + jmethodID biginteger_valueof = (*env)->GetStaticMethodID(env, biginteger_class, "valueOf", "(J)Ljava/math/BigInteger;"); + jobject three = (*env)->CallStaticObjectMethod(env, biginteger_class, biginteger_valueof, (jlong) 3); + jobject a = (*env)->CallObjectMethod(env, p, biginteger_subtract, three); + + jstring b_string = (*env)->NewStringUTF(env, curve->B); + jobject b = (*env)->NewObject(env, biginteger_class, biginteger_init, b_string, (jint) 16); + + jmethodID elliptic_curve_init = (*env)->GetMethodID(env, elliptic_curve_class, "<init>", "(Ljava/security/spec/ECField;Ljava/math/BigInteger;Ljava/math/BigInteger;)V"); + jobject elliptic_curve = (*env)->NewObject(env, elliptic_curve_class, elliptic_curve_init, field, a, b); + + jstring gx_string = (*env)->NewStringUTF(env, curve->Gx); + jstring gy_string = (*env)->NewStringUTF(env, curve->Gy); + jobject gx = (*env)->NewObject(env, biginteger_class, biginteger_init, gx_string, (jint) 16); + jobject gy = (*env)->NewObject(env, biginteger_class, biginteger_init, gy_string, (jint) 16); + + jmethodID point_init = (*env)->GetMethodID(env, point_class, "<init>", "(Ljava/math/BigInteger;Ljava/math/BigInteger;)V"); + jobject g = (*env)->NewObject(env, point_class, point_init, gx, gy); + + jstring n_string = (*env)->NewStringUTF(env, curve->order); + jobject n = (*env)->NewObject(env, biginteger_class, biginteger_init, n_string, (jint) 16); + + jmethodID ec_parameter_spec_init = (*env)->GetMethodID(env, ec_parameter_spec_class, "<init>", "(Ljava/security/spec/EllipticCurve;Ljava/security/spec/ECPoint;Ljava/math/BigInteger;I)V"); + return (*env)->NewObject(env, ec_parameter_spec_class, ec_parameter_spec_init, elliptic_curve, g, n, (jint) 1); +} + +static char *biginteger_to_hex(JNIEnv *env, jobject big, jint bytes) { + jmethodID to_string = (*env)->GetMethodID(env, biginteger_class, "toString", "(I)Ljava/lang/String;"); + jstring big_string = (*env)->CallObjectMethod(env, big, to_string, (jint) 16); + + jsize len = (*env)->GetStringUTFLength(env, big_string); + char raw_string[len]; + (*env)->GetStringUTFRegion(env, big_string, 0, len, raw_string); + + char *result = calloc(bytes, 2); + if (len >= bytes) { + return strncpy(result, raw_string, 2*bytes); + } else { + jsize diff = bytes - len; + for (jint i = 0; i < diff*2; ++i) { + result[i] = '0'; + } + return strncpy(result + diff*2, raw_string, 2*bytes); + } +} + +static ltc_ecc_set_type* create_curve(JNIEnv *env, jobject params) { + jmethodID get_curve = (*env)->GetMethodID(env, ec_parameter_spec_class, "getCurve", "()Ljava/security/spec/EllipticCurve;"); + jobject elliptic_curve = (*env)->CallObjectMethod(env, params, get_curve); + + jmethodID get_field = (*env)->GetMethodID(env, elliptic_curve_class, "getField", "()Ljava/security/spec/ECField;"); + jobject field = (*env)->CallObjectMethod(env, elliptic_curve, get_field); + + jmethodID get_bits = (*env)->GetMethodID(env, fp_field_class, "getFieldSize", "()I"); + jint bits = (*env)->CallIntMethod(env, field, get_bits); + jint bytes = (bits + 7) / 8; + + jmethodID get_b = (*env)->GetMethodID(env, elliptic_curve_class, "getB", "()Ljava/math/BigInteger;"); + jobject b = (*env)->CallObjectMethod(env, elliptic_curve, get_b); + + jmethodID get_p = (*env)->GetMethodID(env, fp_field_class, "getP", "()Ljava/math/BigInteger;"); + jobject p = (*env)->CallObjectMethod(env, field, get_p); + + jmethodID get_g = (*env)->GetMethodID(env, ec_parameter_spec_class, "getGenerator", "()Ljava/security/spec/ECPoint;"); + jobject g = (*env)->CallObjectMethod(env, params, get_g); + + jmethodID get_x = (*env)->GetMethodID(env, point_class, "getAffineX", "()Ljava/math/BigInteger;"); + jobject gx = (*env)->CallObjectMethod(env, g, get_x); + + jmethodID get_y = (*env)->GetMethodID(env, point_class, "getAffineY", "()Ljava/math/BigInteger;"); + jobject gy = (*env)->CallObjectMethod(env, g, get_y); + + jmethodID get_n = (*env)->GetMethodID(env, ec_parameter_spec_class, "getOrder", "()Ljava/math/BigInteger;"); + jobject n = (*env)->CallObjectMethod(env, params, get_n); + + ltc_ecc_set_type *curve = calloc(sizeof(ltc_ecc_set_type), 1); + curve->size = bytes; + curve->name = ""; + curve->prime = biginteger_to_hex(env, p, bytes); + curve->B = biginteger_to_hex(env, b, bytes); + curve->order = biginteger_to_hex(env, n, bytes); + curve->Gx = biginteger_to_hex(env, gx, bytes); + curve->Gy = biginteger_to_hex(env, gy, bytes); + + return curve; +} + +static void throw_new(JNIEnv *env, const char *class, const char *message) { + jclass clazz = (*env)->FindClass(env, class); + (*env)->ThrowNew(env, clazz, message); +} + +static jobject generate_from_curve(JNIEnv *env, const ltc_ecc_set_type *curve) { + ecc_key key; + int err; + if ((err = ecc_make_key_ex(<c_prng, find_prng("yarrow"), &key, curve)) != CRYPT_OK) { + throw_new(env, "java/security/GeneralSecurityException", error_to_string(err)); + return NULL; + } + unsigned long key_len = 2*curve->size + 1; + jbyteArray pub_bytes = (*env)->NewByteArray(env, key_len); + jbyte *key_pub = (*env)->GetByteArrayElements(env, pub_bytes, NULL); + ecc_ansi_x963_export(&key, key_pub, &key_len); + (*env)->ReleaseByteArrayElements(env, pub_bytes, key_pub, JNI_COMMIT); + + jobject ec_param_spec = create_ec_param_spec(env, curve); + + jobject ec_pub_param_spec = (*env)->NewLocalRef(env, ec_param_spec); + jmethodID ec_pub_init = (*env)->GetMethodID(env, pubkey_class, "<init>", "([BLjava/security/spec/ECParameterSpec;)V"); + jobject pubkey = (*env)->NewObject(env, pubkey_class, ec_pub_init, pub_bytes, ec_param_spec); + + jbyteArray priv_bytes = (*env)->NewByteArray(env, curve->size); + jbyte *key_priv = (*env)->GetByteArrayElements(env, priv_bytes, NULL); + ltc_mp.unsigned_write(key.k, key_priv); + (*env)->ReleaseByteArrayElements(env, priv_bytes, key_priv, JNI_COMMIT); + + jobject ec_priv_param_spec = (*env)->NewLocalRef(env, ec_param_spec); + jmethodID ec_priv_init = (*env)->GetMethodID(env, privkey_class, "<init>", "([BLjava/security/spec/ECParameterSpec;)V"); + jobject privkey = (*env)->NewObject(env, privkey_class, ec_priv_init, priv_bytes, ec_priv_param_spec); + + jmethodID keypair_init = (*env)->GetMethodID(env, keypair_class, "<init>", "(Ljava/security/PublicKey;Ljava/security/PrivateKey;)V"); + + ecc_free(&key); + return (*env)->NewObject(env, keypair_class, keypair_init, pubkey, privkey); +} + +JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_00024TomCrypt_generate__ILjava_security_SecureRandom_2(JNIEnv *env, jobject this, jint keysize, jobject random){ + int key_bytes = (keysize + 7) / 8; + + const ltc_ecc_set_type *curve = ltc_ecc_sets; + while (curve->size != 0) { + if (curve->size == key_bytes) { + break; + } + curve++; + } + + if (curve->size == 0) { + return NULL; + } + + return generate_from_curve(env, curve); +} + +JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_00024TomCrypt_generate__Ljava_security_spec_AlgorithmParameterSpec_2Ljava_security_SecureRandom_2(JNIEnv *env, jobject this, jobject params, jobject random){ + if ((*env)->IsInstanceOf(env, params, ec_parameter_spec_class)) { + ltc_ecc_set_type *curve = create_curve(env, params); + jobject result = generate_from_curve(env, curve); + free(curve); + return result; + } else if ((*env)->IsInstanceOf(env, params, ecgen_parameter_spec_class)) { + jmethodID get_name = (*env)->GetMethodID(env, ecgen_parameter_spec_class, "getName", "()Ljava/lang/String;"); + jstring name = (*env)->CallObjectMethod(env, params, get_name); + const char* utf_name = (*env)->GetStringUTFChars(env, name, NULL); + const ltc_ecc_set_type* curve = ltc_ecc_sets; + while (curve->size != 0) { + if (strcasecmp(utf_name, curve->name) == 0) { + break; + } + curve++; + } + (*env)->ReleaseStringUTFChars(env, name, utf_name); + + return generate_from_curve(env, curve); + } else { + return NULL; + } +} + +static jboolean privkey_from_bytes(JNIEnv *env, jbyteArray privkey, const ltc_ecc_set_type *curve, ecc_key *out) { + jsize priv_size = (*env)->GetArrayLength(env, privkey); + jbyte *priv_data = (*env)->GetByteArrayElements(env, privkey, NULL); + + if (curve->size != priv_size) { + throw_new(env, "java/lang/IllegalStateException", "Curve size does not match the private key size."); + (*env)->ReleaseByteArrayElements(env, privkey, priv_data, JNI_ABORT); + return JNI_FALSE; + } + + out->type = PK_PRIVATE; + out->idx = -1; + out->dp = curve; + ltc_mp.init(&out->k); + ltc_mp.unsigned_read(out->k, priv_data, (unsigned long) curve->size); + + (*env)->ReleaseByteArrayElements(env, privkey, priv_data, JNI_ABORT); + return JNI_TRUE; +} + +static jboolean pubkey_from_bytes(JNIEnv *env, jbyteArray pubkey, const ltc_ecc_set_type *curve, ecc_key *out) { + jsize pub_size = (*env)->GetArrayLength(env, pubkey); + jbyte *pub_data = (*env)->GetByteArrayElements(env, pubkey, NULL); + + if (curve->size != (pub_size - 1) / 2) { + throw_new(env, "java/lang/IllegalStateException", "Curve size does not match the public key size."); + (*env)->ReleaseByteArrayElements(env, pubkey, pub_data, JNI_ABORT); + return JNI_FALSE; + } + + out->type = PK_PUBLIC; + out->idx = -1; + out->dp = curve; + ltc_init_multi(&out->pubkey.x, &out->pubkey.y, &out->pubkey.z, NULL); + ltc_mp.set_int(out->pubkey.z, 1); + ltc_mp.unsigned_read(out->pubkey.x, pub_data + 1, (unsigned long) curve->size); + ltc_mp.unsigned_read(out->pubkey.y, pub_data + 1 + curve->size, (unsigned long) curve->size); + + (*env)->ReleaseByteArrayElements(env, pubkey, pub_data, JNI_ABORT); + + return JNI_TRUE; +} + +JNIEXPORT jbyteArray JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyAgreementSpi_00024TomCrypt_generateSecret(JNIEnv *env, jobject this, jbyteArray pubkey, jbyteArray privkey, jobject params){ + ltc_ecc_set_type *curve = create_curve(env, params); + + ecc_key pub; + if (!pubkey_from_bytes(env, pubkey, curve, &pub)) { + free(curve); + return NULL; + } + + ecc_key priv; + if (!privkey_from_bytes(env, privkey, curve, &priv)) { + free(curve); + return NULL; + } + + unsigned char result[curve->size]; + unsigned long output_len = curve->size; + int err; + if ((err = ecc_shared_secret(&priv, &pub, result, &output_len)) != CRYPT_OK) { + throw_new(env, "java/security/GeneralSecurityException", error_to_string(err)); + free(curve); + return NULL; + } + + jbyteArray output = (*env)->NewByteArray(env, curve->size); + jbyte *output_data = (*env)->GetByteArrayElements(env, output, NULL); + memcpy(output_data, result, curve->size); + (*env)->ReleaseByteArrayElements(env, output, output_data, JNI_COMMIT); + + ltc_cleanup_multi(&pub.pubkey.x, &pub.pubkey.y, &pub.pubkey.z, &priv.k, NULL); + free(curve); + return output; +} + +JNIEXPORT jbyteArray JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSignatureSpi_00024TomCryptRaw_sign(JNIEnv *env, jobject this, jbyteArray data, jbyteArray privkey, jobject params) { + ltc_ecc_set_type *curve = create_curve(env, params); + + ecc_key priv; + if (!privkey_from_bytes(env, privkey, curve, &priv)) { + free(curve); + return NULL; + } + + jsize data_size = (*env)->GetArrayLength(env, data); + jbyte *data_data = (*env)->GetByteArrayElements(env, data, NULL); + + unsigned char result[curve->size*4]; + unsigned long output_len = curve->size*4; + int err; + if ((err = ecc_sign_hash(data_data, data_size, result, &output_len, <c_prng, find_prng("yarrow"), &priv)) != CRYPT_OK) { + throw_new(env, "java/security/GeneralSecurityException", error_to_string(err)); + free(curve); + (*env)->ReleaseByteArrayElements(env, data, data_data, JNI_ABORT); + return NULL; + } + + (*env)->ReleaseByteArrayElements(env, data, data_data, JNI_ABORT); + + jbyteArray output = (*env)->NewByteArray(env, output_len); + jbyte *output_data = (*env)->GetByteArrayElements(env, output, NULL); + memcpy(output_data, result, output_len); + (*env)->ReleaseByteArrayElements(env, output, output_data, JNI_COMMIT); + + free(curve); + return output; +} + +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSignatureSpi_00024TomCryptRaw_verify(JNIEnv *env, jobject this, jbyteArray signature, jbyteArray data, jbyteArray pubkey, jobject params) { + ltc_ecc_set_type *curve = create_curve(env, params); + + ecc_key pub; + if (!pubkey_from_bytes(env, pubkey, curve, &pub)) { + free(curve); + return JNI_FALSE; + } + + jsize data_size = (*env)->GetArrayLength(env, data); + jbyte *data_data = (*env)->GetByteArrayElements(env, data, NULL); + + jsize sig_size = (*env)->GetArrayLength(env, signature); + jbyte *sig_data = (*env)->GetByteArrayElements(env, signature, NULL); + + int err; + int result; + if ((err = ecc_verify_hash(sig_data, sig_size, data_data, data_size, &result, &pub)) != CRYPT_OK) { + throw_new(env, "java/security/GeneralSecurityException", error_to_string(err)); + free(curve); + (*env)->ReleaseByteArrayElements(env, data, data_data, JNI_ABORT); + (*env)->ReleaseByteArrayElements(env, signature, sig_data, JNI_ABORT); + return JNI_FALSE; + } + + (*env)->ReleaseByteArrayElements(env, data, data_data, JNI_ABORT); + (*env)->ReleaseByteArrayElements(env, signature, sig_data, JNI_ABORT); + free(curve); + return result; +}
\ No newline at end of file diff --git a/src/cz/crcs/ectester/standalone/test/KeyAgreementTest.java b/src/cz/crcs/ectester/standalone/test/KeyAgreementTest.java new file mode 100644 index 0000000..e273a44 --- /dev/null +++ b/src/cz/crcs/ectester/standalone/test/KeyAgreementTest.java @@ -0,0 +1,57 @@ +package cz.crcs.ectester.standalone.test; + +import cz.crcs.ectester.common.test.Result; +import cz.crcs.ectester.common.test.SimpleTest; +import cz.crcs.ectester.common.test.TestCallback; +import cz.crcs.ectester.common.test.TestException; + +import java.util.Arrays; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class KeyAgreementTest extends SimpleTest<KeyAgreementTestable> { + private KeyAgreementTest(KeyAgreementTestable ka, TestCallback<KeyAgreementTestable> callback) { + super(ka, callback); + } + + public static KeyAgreementTest match(KeyAgreementTestable ka, byte[] expectedSecret) { + return new KeyAgreementTest(ka, new TestCallback<KeyAgreementTestable>() { + @Override + public Result apply(KeyAgreementTestable ka) { + if (Arrays.equals(ka.getSecret(), expectedSecret)) { + return new Result(Result.Value.SUCCESS); + } else { + return new Result(Result.Value.FAILURE); + } + } + }); + } + + public static KeyAgreementTest expect(KeyAgreementTestable ka, Result.ExpectedValue expected) { + return new KeyAgreementTest(ka, new TestCallback<KeyAgreementTestable>() { + @Override + public Result apply(KeyAgreementTestable keyAgreementTestable) { + return new Result(Result.Value.fromExpected(expected, keyAgreementTestable.ok(), keyAgreementTestable.error())); + } + }); + } + + public static KeyAgreementTest function(KeyAgreementTestable ka, TestCallback<KeyAgreementTestable> callback) { + return new KeyAgreementTest(ka, callback); + } + + @Override + public String getDescription() { + return "KeyAgreement test"; + } + + @Override + public void run() throws TestException { + if (hasRun) + return; + testable.run(); + result = callback.apply(testable); + hasRun = true; + } +} diff --git a/src/cz/crcs/ectester/standalone/test/KeyAgreementTestable.java b/src/cz/crcs/ectester/standalone/test/KeyAgreementTestable.java new file mode 100644 index 0000000..8e9b0dd --- /dev/null +++ b/src/cz/crcs/ectester/standalone/test/KeyAgreementTestable.java @@ -0,0 +1,112 @@ +package cz.crcs.ectester.standalone.test; + +import cz.crcs.ectester.common.test.BaseTestable; +import cz.crcs.ectester.common.test.TestException; + +import javax.crypto.KeyAgreement; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.ECParameterSpec; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class KeyAgreementTestable extends BaseTestable { + private KeyAgreement ka; + private ECPrivateKey privateKey; + private ECPublicKey publicKey; + private KeyGeneratorTestable kgtPrivate; + private KeyGeneratorTestable kgtPublic; + private AlgorithmParameterSpec spec; + private byte[] secret; + + public KeyAgreementTestable(KeyAgreement ka, ECPrivateKey privateKey, ECPublicKey publicKey) { + this.ka = ka; + this.privateKey = privateKey; + this.publicKey = publicKey; + } + + public KeyAgreementTestable(KeyAgreement ka, ECPrivateKey privateKey, ECPublicKey publicKey, ECParameterSpec spec) { + this(ka, privateKey, publicKey); + this.spec = spec; + } + + public KeyAgreementTestable(KeyAgreement ka, KeyGeneratorTestable kgt, ECPrivateKey privateKey, ECParameterSpec spec) { + this(ka, privateKey, null, spec); + this.kgtPublic = kgt; + } + + public KeyAgreementTestable(KeyAgreement ka, ECPublicKey publicKey, KeyGeneratorTestable kgt, ECParameterSpec spec) { + this(ka, null, publicKey, spec); + this.kgtPrivate = kgt; + } + + public KeyAgreementTestable(KeyAgreement ka, KeyGeneratorTestable privKgt, KeyGeneratorTestable pubKgt, ECParameterSpec spec) { + this(ka, (ECPrivateKey) null, null, spec); + this.kgtPrivate = privKgt; + this.kgtPublic = pubKgt; + } + + public byte[] getSecret() { + if (!hasRun) { + return null; + } + return secret; + } + + @Override + public void run() throws TestException { + if (kgtPrivate != null) { + privateKey = (ECPrivateKey) kgtPrivate.getKeyPair().getPrivate(); + } + + if (kgtPublic != null) { + publicKey = (ECPublicKey) kgtPublic.getKeyPair().getPublic(); + } + + try { + if (spec != null) { + ka.init(privateKey, spec); + } else { + ka.init(privateKey); + } + } catch (InvalidKeyException | InvalidAlgorithmParameterException e) { + ok = false; + error = true; + hasRun = true; + return; + } + + try { + ka.doPhase(publicKey, true); + } catch (IllegalStateException e) { + ok = false; + hasRun = true; + return; + } catch (InvalidKeyException e) { + ok = false; + error = true; + hasRun = true; + return; + } + + try { + secret = ka.generateSecret(); + } catch (IllegalStateException isex) { + ok = false; + hasRun = true; + return; + } catch (UnsupportedOperationException uoe) { + ok = false; + error = true; + hasRun = false; + return; + } + + ok = true; + hasRun = true; + } +} diff --git a/src/cz/crcs/ectester/standalone/test/KeyGeneratorTest.java b/src/cz/crcs/ectester/standalone/test/KeyGeneratorTest.java new file mode 100644 index 0000000..a57e28c --- /dev/null +++ b/src/cz/crcs/ectester/standalone/test/KeyGeneratorTest.java @@ -0,0 +1,42 @@ +package cz.crcs.ectester.standalone.test; + +import cz.crcs.ectester.common.test.Result; +import cz.crcs.ectester.common.test.SimpleTest; +import cz.crcs.ectester.common.test.TestCallback; +import cz.crcs.ectester.common.test.TestException; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class KeyGeneratorTest extends SimpleTest<KeyGeneratorTestable> { + private KeyGeneratorTest(KeyGeneratorTestable kg, TestCallback<KeyGeneratorTestable> callback) { + super(kg, callback); + } + + public static KeyGeneratorTest expect(KeyGeneratorTestable kg, Result.ExpectedValue expected) { + return new KeyGeneratorTest(kg, new TestCallback<KeyGeneratorTestable>() { + @Override + public Result apply(KeyGeneratorTestable keyGenerationTestable) { + return new Result(Result.Value.fromExpected(expected, keyGenerationTestable.ok(), keyGenerationTestable.error())); + } + }); + } + + public static KeyGeneratorTest function(KeyGeneratorTestable ka, TestCallback<KeyGeneratorTestable> callback) { + return new KeyGeneratorTest(ka, callback); + } + + @Override + public String getDescription() { + return "KeyPairGenerator test"; + } + + @Override + public void run() throws TestException { + if (hasRun) + return; + testable.run(); + result = callback.apply(testable); + hasRun = true; + } +} diff --git a/src/cz/crcs/ectester/standalone/test/KeyGeneratorTestable.java b/src/cz/crcs/ectester/standalone/test/KeyGeneratorTestable.java new file mode 100644 index 0000000..ca7f1e7 --- /dev/null +++ b/src/cz/crcs/ectester/standalone/test/KeyGeneratorTestable.java @@ -0,0 +1,52 @@ +package cz.crcs.ectester.standalone.test; + +import cz.crcs.ectester.common.test.BaseTestable; +import cz.crcs.ectester.common.test.TestException; + +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.spec.ECParameterSpec; + +public class KeyGeneratorTestable extends BaseTestable { + private KeyPair kp; + private KeyPairGenerator kpg; + private int keysize = 0; + private ECParameterSpec spec = null; + + public KeyGeneratorTestable(KeyPairGenerator kpg) { + this.kpg = kpg; + } + + public KeyGeneratorTestable(KeyPairGenerator kpg, int keysize) { + this.kpg = kpg; + this.keysize = keysize; + } + + public KeyGeneratorTestable(KeyPairGenerator kpg, ECParameterSpec spec) { + this.kpg = kpg; + this.spec = spec; + } + + public KeyPair getKeyPair() { + return kp; + } + + @Override + public void run() throws TestException { + try { + if (spec != null) { + kpg.initialize(spec); + } else if (keysize != 0) { + kpg.initialize(keysize); + } + } catch (InvalidAlgorithmParameterException e) { + hasRun = true; + ok = false; + return; + } + kp = kpg.genKeyPair(); + hasRun = true; + ok = true; + } +} diff --git a/src/cz/crcs/ectester/standalone/test/SignatureTest.java b/src/cz/crcs/ectester/standalone/test/SignatureTest.java new file mode 100644 index 0000000..97e387c --- /dev/null +++ b/src/cz/crcs/ectester/standalone/test/SignatureTest.java @@ -0,0 +1,42 @@ +package cz.crcs.ectester.standalone.test; + +import cz.crcs.ectester.common.test.Result; +import cz.crcs.ectester.common.test.SimpleTest; +import cz.crcs.ectester.common.test.TestCallback; +import cz.crcs.ectester.common.test.TestException; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class SignatureTest extends SimpleTest<SignatureTestable> { + private SignatureTest(SignatureTestable sig, TestCallback<SignatureTestable> callback) { + super(sig, callback); + } + + public static SignatureTest expect(SignatureTestable kg, Result.ExpectedValue expected) { + return new SignatureTest(kg, new TestCallback<SignatureTestable>() { + @Override + public Result apply(SignatureTestable signatureTestable) { + return new Result(Result.Value.fromExpected(expected, signatureTestable.ok(), signatureTestable.error())); + } + }); + } + + public static SignatureTest function(SignatureTestable ka, TestCallback<SignatureTestable> callback) { + return new SignatureTest(ka, callback); + } + + @Override + public String getDescription() { + return "Signature test"; + } + + @Override + public void run() throws TestException { + if (hasRun) + return; + testable.run(); + result = callback.apply(testable); + hasRun = true; + } +} diff --git a/src/cz/crcs/ectester/standalone/test/SignatureTestable.java b/src/cz/crcs/ectester/standalone/test/SignatureTestable.java new file mode 100644 index 0000000..564a6bf --- /dev/null +++ b/src/cz/crcs/ectester/standalone/test/SignatureTestable.java @@ -0,0 +1,103 @@ +package cz.crcs.ectester.standalone.test; + +import cz.crcs.ectester.common.test.BaseTestable; +import cz.crcs.ectester.common.test.TestException; + +import java.security.InvalidKeyException; +import java.security.SecureRandom; +import java.security.Signature; +import java.security.SignatureException; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; + +public class SignatureTestable extends BaseTestable { + private Signature sig; + private ECPrivateKey signKey; + private ECPublicKey verifyKey; + private KeyGeneratorTestable kgt; + private byte[] data; + private byte[] signature; + private boolean verified; + + public SignatureTestable(Signature sig, ECPrivateKey signKey, ECPublicKey verifyKey, byte[] data) { + this.sig = sig; + this.signKey = signKey; + this.verifyKey = verifyKey; + this.data = data; + if (data == null) { + SecureRandom random = new SecureRandom(); + this.data = new byte[32]; + random.nextBytes(this.data); + } + } + + public SignatureTestable(Signature sig, KeyGeneratorTestable kgt, byte[] data) { + this(sig, null, null, data); + this.kgt = kgt; + } + + public byte[] getData() { + return data; + } + + public byte[] getSignature() { + return signature; + } + + public boolean getVerified() { + return verified; + } + + @Override + public void run() throws TestException { + if (kgt != null) { + signKey = (ECPrivateKey) kgt.getKeyPair().getPrivate(); + verifyKey = (ECPublicKey) kgt.getKeyPair().getPublic(); + } + + try { + sig.initSign(signKey); + } catch (InvalidKeyException e) { + throw new TestException(e); + } + + try { + sig.update(data); + } catch (SignatureException e) { + ok = false; + hasRun = true; + return; + } + + try { + signature = sig.sign(); + } catch (SignatureException e) { + ok = false; + hasRun = true; + return; + } + + try { + sig.initVerify(verifyKey); + } catch (InvalidKeyException e) { + throw new TestException(e); + } + + try { + sig.update(data); + } catch (SignatureException e) { + ok = false; + hasRun = true; + return; + } + + try { + verified = sig.verify(signature); + } catch (SignatureException e) { + ok = false; + hasRun = true; + } + ok = true; + hasRun = true; + } +} diff --git a/src/cz/crcs/ectester/standalone/test/StandaloneDefaultSuite.java b/src/cz/crcs/ectester/standalone/test/StandaloneDefaultSuite.java new file mode 100644 index 0000000..7056d69 --- /dev/null +++ b/src/cz/crcs/ectester/standalone/test/StandaloneDefaultSuite.java @@ -0,0 +1,76 @@ +package cz.crcs.ectester.standalone.test; + +import cz.crcs.ectester.common.cli.TreeCommandLine; +import cz.crcs.ectester.common.ec.EC_Curve; +import cz.crcs.ectester.common.test.Result; +import cz.crcs.ectester.data.EC_Store; +import cz.crcs.ectester.standalone.ECTesterStandalone; +import cz.crcs.ectester.standalone.consts.KeyAgreementIdent; +import cz.crcs.ectester.standalone.consts.KeyPairGeneratorIdent; +import cz.crcs.ectester.standalone.consts.SignatureIdent; + +import javax.crypto.KeyAgreement; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.Signature; +import java.security.spec.ECParameterSpec; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class StandaloneDefaultSuite extends StandaloneTestSuite { + + public StandaloneDefaultSuite(EC_Store dataStore, ECTesterStandalone.Config cfg, TreeCommandLine cli) { + super(dataStore, cfg, cli, "default", "The default test suite tests basic support of ECDH and ECDSA."); + } + + @Override + public void setup() throws NoSuchAlgorithmException { + String kpgAlgo = cli.getOptionValue("test.kpg-type", "EC"); + String kaAlgo = cli.getOptionValue("test.ka-type"); + String sigAlgo = cli.getOptionValue("test.sig-type"); + + KeyPairGeneratorIdent kpgIdent = cfg.selected.getKPGs().stream() + .filter((ident) -> ident.contains(kpgAlgo)) + .findFirst().get(); + KeyPairGenerator kpg = kpgIdent.getInstance(cfg.selected.getProvider()); + + KeyGeneratorTestable kgtOne; + KeyGeneratorTestable kgtOther; + ECParameterSpec spec = null; + if (cli.hasOption("test.bits")) { + int bits = Integer.parseInt(cli.getOptionValue("test.bits")); + kgtOne = new KeyGeneratorTestable(kpg, bits); + kgtOther = new KeyGeneratorTestable(kpg, bits); + } else if (cli.hasOption("test.named-curve")) { + String curveName = cli.getOptionValue("test.named-curve"); + EC_Curve curve = dataStore.getObject(EC_Curve.class, curveName); + if (curve == null) { + System.err.println("Curve not found: " + curveName); + return; + } + spec = curve.toSpec(); + kgtOne = new KeyGeneratorTestable(kpg, spec); + kgtOther = new KeyGeneratorTestable(kpg, spec); + } else { + kgtOne = new KeyGeneratorTestable(kpg); + kgtOther = new KeyGeneratorTestable(kpg); + } + + tests.add(KeyGeneratorTest.expect(kgtOne, Result.ExpectedValue.SUCCESS)); + tests.add(KeyGeneratorTest.expect(kgtOther, Result.ExpectedValue.SUCCESS)); + + for (KeyAgreementIdent kaIdent : cfg.selected.getKAs()) { + if (kaAlgo == null || kaIdent.contains(kaAlgo)) { + KeyAgreement ka = kaIdent.getInstance(cfg.selected.getProvider()); + tests.add(KeyAgreementTest.expect(new KeyAgreementTestable(ka, kgtOne, kgtOther, spec), Result.ExpectedValue.SUCCESS)); + } + } + for (SignatureIdent sigIdent : cfg.selected.getSigs()) { + if (sigAlgo == null || sigIdent.contains(sigAlgo)) { + Signature sig = sigIdent.getInstance(cfg.selected.getProvider()); + tests.add(SignatureTest.expect(new SignatureTestable(sig, kgtOne, null), Result.ExpectedValue.SUCCESS)); + } + } + } +} diff --git a/src/cz/crcs/ectester/standalone/test/StandaloneTestSuite.java b/src/cz/crcs/ectester/standalone/test/StandaloneTestSuite.java new file mode 100644 index 0000000..5682cd5 --- /dev/null +++ b/src/cz/crcs/ectester/standalone/test/StandaloneTestSuite.java @@ -0,0 +1,24 @@ +package cz.crcs.ectester.standalone.test; + +import cz.crcs.ectester.common.cli.TreeCommandLine; +import cz.crcs.ectester.common.test.TestSuite; +import cz.crcs.ectester.data.EC_Store; +import cz.crcs.ectester.standalone.ECTesterStandalone; + +import java.security.NoSuchAlgorithmException; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public abstract class StandaloneTestSuite extends TestSuite { + TreeCommandLine cli; + ECTesterStandalone.Config cfg; + + public StandaloneTestSuite(EC_Store dataStore, ECTesterStandalone.Config cfg, TreeCommandLine cli, String name, String description) { + super(dataStore, name, description); + this.cfg = cfg; + this.cli = cli; + } + + public abstract void setup() throws NoSuchAlgorithmException; +} |
