diff options
| author | Ján Jančár | 2024-03-25 20:13:02 +0100 |
|---|---|---|
| committer | GitHub | 2024-03-25 20:13:02 +0100 |
| commit | dc7bb975e1c230f1f0a1937c454b2b90817d18e9 (patch) | |
| tree | 8b4e3437e489894afcead9a6466050f3611cb38d /common/src/main/java | |
| parent | 64b95fa059295e1dc23371c849f2302c1c18f5b4 (diff) | |
| parent | 591deaece718b7821385ff0069b44adac4e1baf3 (diff) | |
| download | ECTester-dc7bb975e1c230f1f0a1937c454b2b90817d18e9.tar.gz ECTester-dc7bb975e1c230f1f0a1937c454b2b90817d18e9.tar.zst ECTester-dc7bb975e1c230f1f0a1937c454b2b90817d18e9.zip | |
Merge pull request #20 from crocs-muni/feat/gradle
Move to gradle build
Diffstat (limited to 'common/src/main/java')
45 files changed, 6454 insertions, 0 deletions
diff --git a/common/src/main/java/cz/crcs/ectester/common/cli/Argument.java b/common/src/main/java/cz/crcs/ectester/common/cli/Argument.java new file mode 100644 index 0000000..e9b6688 --- /dev/null +++ b/common/src/main/java/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/common/src/main/java/cz/crcs/ectester/common/cli/CLITools.java b/common/src/main/java/cz/crcs/ectester/common/cli/CLITools.java new file mode 100644 index 0000000..82ab530 --- /dev/null +++ b/common/src/main/java/cz/crcs/ectester/common/cli/CLITools.java @@ -0,0 +1,161 @@ +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(Colors.bold(prog), header, options, footer, usage); + } + + private static void help(HelpFormatter help, PrintWriter pw, String cmd, ParserOptions parser, int depth) { + String description = parser.getDescription() == null ? "" : " | " + parser.getDescription() + " |"; + help.printWrapped(pw, HelpFormatter.DEFAULT_WIDTH, String.format("%" + depth + "s" + cmd + ":" + description, " ")); + CLITools.help(help, pw, parser.getParser(), parser.getOptions(), depth + 1); + } + + 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(help, pw, key, value, depth); + }); + } + } + + 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: " + Colors.bold(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()); + } + + public static void help(String header, TreeParser baseParser, String footer, String command) { + ParserOptions opts = baseParser.getParsers().get(command); + if (opts == null) { + System.err.println("Command not found: " + command); + return; + } + HelpFormatter help = new HelpFormatter(); + help.setOptionComparator(null); + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + help.printWrapped(pw, HelpFormatter.DEFAULT_WIDTH, header); + help(help, pw, command, opts, 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/common/src/main/java/cz/crcs/ectester/common/cli/Colors.java b/common/src/main/java/cz/crcs/ectester/common/cli/Colors.java new file mode 100644 index 0000000..7601088 --- /dev/null +++ b/common/src/main/java/cz/crcs/ectester/common/cli/Colors.java @@ -0,0 +1,97 @@ +package cz.crcs.ectester.common.cli; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * @author Diogo Nunes + * @author Jan Jancar johny@neuromancer.sk + * Adapted from https://github.com/dialex/JCDP/ + */ +public class Colors { + public static boolean enabled = false; + + public interface ANSIParam { + } + + public enum Foreground implements ANSIParam { + BLACK("30"), RED("31"), GREEN("32"), YELLOW("33"), BLUE("34"), MAGENTA("35"), CYAN("36"), WHITE("37"), NONE(""); + private final String code; + + Foreground(String code) { + this.code = code; + } + + @Override + public String toString() { + return code; + } + } + + public enum Background implements ANSIParam { + BLACK("40"), RED("41"), GREEN("42"), YELLOW("43"), BLUE("44"), MAGENTA("45"), CYAN("46"), WHITE("47"), NONE(""); + private final String code; + + Background(String code) { + this.code = code; + } + + @Override + public String toString() { + return code; + } + } + + public enum Attribute implements ANSIParam { + CLEAR("0"), BOLD("1"), LIGHT("1"), DARK("2"), UNDERLINE("4"), REVERSE("7"), HIDDEN("8"), NONE(""); + private final String code; + + Attribute(String code) { + this.code = code; + } + + @Override + public String toString() { + return code; + } + } + + private static final String PREFIX = "\033["; + private static final String SEPARATOR = ";"; + private static final String POSTFIX = "m"; + + public static String colored(String text, ANSIParam... params) { + if (!enabled) { + return text; + } + Optional<Foreground> fg = Arrays.stream(params).filter(Foreground.class::isInstance).map(Foreground.class::cast).findFirst(); + Optional<Background> bg = Arrays.stream(params).filter(Background.class::isInstance).map(Background.class::cast).findFirst(); + List<Attribute> attr = Arrays.stream(params).filter(Attribute.class::isInstance).distinct().map(Attribute.class::cast).collect(Collectors.toList()); + + List<ANSIParam> apply = new LinkedList<>(); + apply.addAll(attr); + fg.ifPresent(apply::add); + bg.ifPresent(apply::add); + List<String> codes = apply.stream().map(Object::toString).collect(Collectors.toList()); + return PREFIX + String.join(SEPARATOR, codes) + POSTFIX + text + PREFIX + Attribute.CLEAR + POSTFIX; + } + + public static String error(String text) { + return colored(text, Foreground.RED, Attribute.BOLD); + } + + public static String ok(String text) { + return colored(text, Foreground.GREEN, Attribute.BOLD); + } + + public static String bold(String text) { + return colored(text, Attribute.BOLD); + } + + public static String underline(String text) { + return colored(text, Attribute.UNDERLINE); + } +}
\ No newline at end of file diff --git a/common/src/main/java/cz/crcs/ectester/common/cli/ParserOptions.java b/common/src/main/java/cz/crcs/ectester/common/cli/ParserOptions.java new file mode 100644 index 0000000..7300cbb --- /dev/null +++ b/common/src/main/java/cz/crcs/ectester/common/cli/ParserOptions.java @@ -0,0 +1,35 @@ +package cz.crcs.ectester.common.cli; + +import org.apache.commons.cli.CommandLineParser; +import org.apache.commons.cli.Options; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class ParserOptions { + private CommandLineParser parser; + private Options options; + private String description; + + public ParserOptions(CommandLineParser parser, Options options) { + this.parser = parser; + this.options = options; + } + + public ParserOptions(CommandLineParser parser, Options options, String description) { + this(parser, options); + this.description = description; + } + + public CommandLineParser getParser() { + return parser; + } + + public Options getOptions() { + return options; + } + + public String getDescription() { + return description; + } +} diff --git a/common/src/main/java/cz/crcs/ectester/common/cli/TreeCommandLine.java b/common/src/main/java/cz/crcs/ectester/common/cli/TreeCommandLine.java new file mode 100644 index 0000000..d758b78 --- /dev/null +++ b/common/src/main/java/cz/crcs/ectester/common/cli/TreeCommandLine.java @@ -0,0 +1,179 @@ +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 + */ +@SuppressWarnings("serial") +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/common/src/main/java/cz/crcs/ectester/common/cli/TreeParser.java b/common/src/main/java/cz/crcs/ectester/common/cli/TreeParser.java new file mode 100644 index 0000000..657318d --- /dev/null +++ b/common/src/main/java/cz/crcs/ectester/common/cli/TreeParser.java @@ -0,0 +1,130 @@ +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())); + } + } + + int maxArgs = args.size(); + 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); + } + //else if (lastCli.getArgs().length > maxArgs) { + // throw new MissingArgumentException("Too many arguments."); + //} + + subTreeCli.setName(sub); + return new TreeCommandLine(cli, subTreeCli); + } else if (subCli != null) { + if (subCli.getArgs().length < requiredArgs) { + throw new MissingArgumentException("Not enough arguments: " + reqArgs); + } else if (subCli.getArgs().length > maxArgs) { + throw new MissingArgumentException("Too many arguments."); + } + + TreeCommandLine subTreeCli = new TreeCommandLine(sub, subCli, null); + return new TreeCommandLine(cli, subTreeCli); + } else { + if (cliArgs.length < requiredArgs) { + throw new MissingArgumentException("Not enough arguments: " + reqArgs); + } + //else if (cliArgs.length > maxArgs) { + // throw new MissingArgumentException("Too many arguments."); + //} + + return new TreeCommandLine(cli, null); + } + } +} diff --git a/common/src/main/java/cz/crcs/ectester/common/ec/CustomECFieldF2m.java b/common/src/main/java/cz/crcs/ectester/common/ec/CustomECFieldF2m.java new file mode 100644 index 0000000..24ea5aa --- /dev/null +++ b/common/src/main/java/cz/crcs/ectester/common/ec/CustomECFieldF2m.java @@ -0,0 +1,67 @@ +package cz.crcs.ectester.common.ec; + +import java.math.BigInteger; +import java.security.spec.ECFieldF2m; +import java.util.Arrays; + +/** + * @author David Hofman + */ +public class CustomECFieldF2m extends ECFieldF2m { + private int m; + private int[] ks; + private BigInteger rp; + + public CustomECFieldF2m(int m, int[] ks) { + //feed the constructor of the superclass some default, valid data + //getters will return custom parameters instead + super(163, new int[] {3, 2, 1}); + this.m = m; + this.ks = ks.clone(); + + //causes ArithmeticException if m < 0 or any element of ks < 0 + this.rp = BigInteger.ONE; + this.rp = this.rp.setBit(m); + for(int i = 0; i < this.ks.length; ++i) { + this.rp = this.rp.setBit(this.ks[i]); + } + } + + @Override + public int getFieldSize() { + return m; + } + + @Override + public int getM() { + return m; + } + + @Override + public int[] getMidTermsOfReductionPolynomial() { + return ks.clone(); + } + + @Override + public BigInteger getReductionPolynomial() { + return rp; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } else if (!(o instanceof CustomECFieldF2m)) { + return false; + } else { + return m == ((CustomECFieldF2m) o).m && Arrays.equals(ks, ((CustomECFieldF2m) o).ks); + } + } + + @Override + public int hashCode() { + int hash = m << 5; + hash += rp == null ? 0 : rp.hashCode(); + return hash; + } +} diff --git a/common/src/main/java/cz/crcs/ectester/common/ec/CustomECFieldFp.java b/common/src/main/java/cz/crcs/ectester/common/ec/CustomECFieldFp.java new file mode 100644 index 0000000..eafcb72 --- /dev/null +++ b/common/src/main/java/cz/crcs/ectester/common/ec/CustomECFieldFp.java @@ -0,0 +1,43 @@ +package cz.crcs.ectester.common.ec; + +import java.math.BigInteger; +import java.security.spec.ECFieldFp; + +/** + * @author David Hofman + */ +public class CustomECFieldFp extends ECFieldFp { + private BigInteger p; + + public CustomECFieldFp(BigInteger p) { + //feed the constructor of the superclass some default, valid parameter p + //getters will return custom (and possibly invalid) data + super(BigInteger.ONE); + this.p = p; + } + + + @Override + public int getFieldSize() { + return p.bitCount(); + } + + @Override + public BigInteger getP() { + return p; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } else { + return o instanceof CustomECFieldFp && p.equals(((CustomECFieldFp) o).p); + } + } + + @Override + public int hashCode() { + return p.hashCode(); + } +} diff --git a/common/src/main/java/cz/crcs/ectester/common/ec/CustomECParameterSpec.java b/common/src/main/java/cz/crcs/ectester/common/ec/CustomECParameterSpec.java new file mode 100644 index 0000000..cbc15e7 --- /dev/null +++ b/common/src/main/java/cz/crcs/ectester/common/ec/CustomECParameterSpec.java @@ -0,0 +1,47 @@ +package cz.crcs.ectester.common.ec; + +import java.math.BigInteger; +import java.security.spec.ECFieldFp; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPoint; +import java.security.spec.EllipticCurve; + +/** + * @author David Hofman + */ +public class CustomECParameterSpec extends ECParameterSpec { + private EllipticCurve curve; + private ECPoint g; + private BigInteger n; + private int h; + + public CustomECParameterSpec(EllipticCurve curve, ECPoint g, BigInteger n, int h) { + //feed the constructor of the superclass some default, valid data + //getters will return custom (and possibly invalid) parameters instead + super(new EllipticCurve(new ECFieldFp(BigInteger.ONE),BigInteger.ZERO,BigInteger.ZERO), new ECPoint(BigInteger.ZERO, BigInteger.ZERO), BigInteger.ONE, 1); + this.curve = curve; + this.g = g; + this.n = n; + this.h = h; + } + + @Override + public EllipticCurve getCurve() { + return curve; + } + + @Override + public ECPoint getGenerator() { + return g; + } + + @Override + public BigInteger getOrder() { + return n; + } + + @Override + public int getCofactor() { + return h; + } +} diff --git a/common/src/main/java/cz/crcs/ectester/common/ec/CustomEllipticCurve.java b/common/src/main/java/cz/crcs/ectester/common/ec/CustomEllipticCurve.java new file mode 100644 index 0000000..489861c --- /dev/null +++ b/common/src/main/java/cz/crcs/ectester/common/ec/CustomEllipticCurve.java @@ -0,0 +1,60 @@ +package cz.crcs.ectester.common.ec; + +import java.math.BigInteger; +import java.security.spec.ECField; +import java.security.spec.ECFieldFp; +import java.security.spec.EllipticCurve; + +/** + * @author David Hofman + */ +public class CustomEllipticCurve extends EllipticCurve { + private ECField field; + private BigInteger a; + private BigInteger b; + + public CustomEllipticCurve(ECField field, BigInteger a, BigInteger b) { + //feed the constructor of the superclass some default, valid EC parameters + //getters will return custom (and possibly invalid) data instead + super(new ECFieldFp(BigInteger.ONE), BigInteger.ZERO, BigInteger.ZERO); + this.field = field; + this.a = a; + this.b = b; + + } + + @Override + public BigInteger getA() { + return a; + } + + @Override + public BigInteger getB() { + return b; + } + + @Override + public ECField getField() { + return field; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } else { + if (o instanceof CustomEllipticCurve) { + CustomEllipticCurve otherCurve = (CustomEllipticCurve) o; + if (field.equals(otherCurve.field) && a.equals(otherCurve.a) && b.equals(otherCurve.b)) { + return true; + } + } + return false; + } + } + + @Override + public int hashCode() { + return field.hashCode() << 6 + (a.hashCode() << 4) + (b.hashCode() << 2); + } +} diff --git a/common/src/main/java/cz/crcs/ectester/common/ec/EC_Category.java b/common/src/main/java/cz/crcs/ectester/common/ec/EC_Category.java new file mode 100644 index 0000000..154403e --- /dev/null +++ b/common/src/main/java/cz/crcs/ectester/common/ec/EC_Category.java @@ -0,0 +1,110 @@ +package cz.crcs.ectester.common.ec; + +import cz.crcs.ectester.common.cli.Colors; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * A category of EC_Data objects, has a name, description and represents a directory in + * the cz.crcs.ectester.data package. + * + * @author Jan Jancar johny@neuromancer.sk + */ +public class EC_Category { + + private String name; + private String directory; + private String desc; + + private Map<String, EC_Data> objects; + + + public EC_Category(String name, String directory) { + this.name = name; + this.directory = directory; + } + + public EC_Category(String name, String directory, String desc) { + this(name, directory); + this.desc = desc; + } + + public EC_Category(String name, String directory, String desc, Map<String, EC_Data> objects) { + this(name, directory, desc); + this.objects = objects; + } + + public String getName() { + return name; + } + + public String getDirectory() { + return directory; + } + + public String getDesc() { + return desc; + } + + public Map<String, EC_Data> getObjects() { + return Collections.unmodifiableMap(objects); + } + + public <T extends EC_Data> Map<String, T> getObjects(Class<T> cls) { + Map<String, T> objs = new TreeMap<>(); + for (Map.Entry<String, EC_Data> entry : objects.entrySet()) { + if (cls.isInstance(entry.getValue())) { + objs.put(entry.getKey(), cls.cast(entry.getValue())); + } + } + return Collections.unmodifiableMap(objs); + } + + public <T extends EC_Data> T getObject(Class<T> cls, String id) { + EC_Data obj = objects.get(id); + if (cls.isInstance(obj)) { + return cls.cast(obj); + } else { + return null; + } + } + + @Override + public String toString() { + StringBuilder out = new StringBuilder(); + out.append("\t- ").append(Colors.bold(name)).append((desc == null || desc.equals("")) ? "" : ": " + desc); + out.append(System.lineSeparator()); + + String[] headers = new String[]{"Curves", "Public keys", "Private keys", "KeyPairs", "Results(KA)", "Results(SIG)"}; + Class<EC_Data>[] classes = new Class[]{EC_Curve.class, EC_Key.Public.class, EC_Key.Private.class, EC_Keypair.class, EC_KAResult.class, EC_SigResult.class}; + for (int i = 0; i < headers.length; ++i) { + Map<String, EC_Data> data = getObjects(classes[i]); + int size = data.size(); + if (size > 0) { + out.append(Colors.bold(String.format("\t\t%s: ", headers[i]))); + List<EC_Data> sorted = new ArrayList<>(data.values()); + Collections.sort(sorted); + for (EC_Data element : sorted) { + out.append(element.getId()); + size--; + if (size > 0) + out.append(", "); + } + out.append(System.lineSeparator()); + } + } + return out.toString(); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof EC_Category && Objects.equals(this.name, ((EC_Category) obj).name); + } + + @Override + public int hashCode() { + return this.name.hashCode() ^ this.directory.hashCode(); + } + +} diff --git a/common/src/main/java/cz/crcs/ectester/common/ec/EC_Consts.java b/common/src/main/java/cz/crcs/ectester/common/ec/EC_Consts.java new file mode 100644 index 0000000..86c30fa --- /dev/null +++ b/common/src/main/java/cz/crcs/ectester/common/ec/EC_Consts.java @@ -0,0 +1,1403 @@ +package cz.crcs.ectester.common.ec; + +import cz.crcs.ectester.common.util.ByteUtil; + +import java.nio.ByteBuffer; + +/** + * @author Petr Svenda petr@svenda.com + * @author Jan Jancar johny@neuromancer.sk + */ +public class EC_Consts { + private static byte[] EC_FP_P = null; //p + private static byte[] EC_A = null; //a + private static byte[] EC_B = null; //b + private static byte[] EC_G_X = null; //G[x,y] + private static byte[] EC_G_Y = null; // + private static byte[] EC_R = null; //n + private static short EC_K = 1; //h + + private static byte[] EC_W_X = null; //Pubkey[x,y] + private static byte[] EC_W_Y = null; + private static byte[] EC_S = null; //Private + + private static byte[] EC_F2M_F2M = null; //[short i1, short i2, short i3], f = x^m + x^i1 + x^i2 + x^i3 + 1 + + // EC domain parameter identifiers (bit flags) + public static final short PARAMETER_FP = 0x0001; + public static final short PARAMETER_F2M = 0x0002; + + public static final short PARAMETER_A = 0x0004; + public static final short PARAMETER_B = 0x0008; + public static final short PARAMETER_G = 0x0010; + public static final short PARAMETER_R = 0x0020; + public static final short PARAMETER_K = 0x0040; + public static final short PARAMETER_W = 0x0080; + public static final short PARAMETER_S = 0x0100; + + public static final short PARAMETERS_NONE = 0x0000; + /** + * FP,A,B,G,R,K + */ + public static final short PARAMETERS_DOMAIN_FP = 0x007d; + /** + * F2M,A,B,G,R,K + */ + public static final short PARAMETERS_DOMAIN_F2M = 0x007e; + /** + * W,S + */ + public static final short PARAMETERS_KEYPAIR = 0x0180; + public static final short PARAMETERS_ALL = 0x01ff; + + // EC key identifiers + public static final byte KEY_PUBLIC = 0x01; + public static final byte KEY_PRIVATE = 0x02; + public static final byte KEY_BOTH = KEY_PUBLIC | KEY_PRIVATE; + + // secp112r1 + public static final byte[] EC112_FP_P = new byte[]{ + (byte) 0xdb, (byte) 0x7c, (byte) 0x2a, (byte) 0xbf, + (byte) 0x62, (byte) 0xe3, (byte) 0x5e, (byte) 0x66, + (byte) 0x80, (byte) 0x76, (byte) 0xbe, (byte) 0xad, + (byte) 0x20, (byte) 0x8b + }; + + public static final byte[] EC112_FP_A = new byte[]{ + (byte) 0xdb, (byte) 0x7c, (byte) 0x2a, (byte) 0xbf, + (byte) 0x62, (byte) 0xe3, (byte) 0x5e, (byte) 0x66, + (byte) 0x80, (byte) 0x76, (byte) 0xbe, (byte) 0xad, + (byte) 0x20, (byte) 0x88 + }; + + public static final byte[] EC112_FP_B = new byte[]{ + (byte) 0x65, (byte) 0x9e, (byte) 0xf8, (byte) 0xba, + (byte) 0x04, (byte) 0x39, (byte) 0x16, (byte) 0xee, + (byte) 0xde, (byte) 0x89, (byte) 0x11, (byte) 0x70, + (byte) 0x2b, (byte) 0x22 + }; + + public static final byte[] EC112_FP_G_X = new byte[]{ + (byte) 0x09, (byte) 0x48, (byte) 0x72, (byte) 0x39, + (byte) 0x99, (byte) 0x5a, (byte) 0x5e, (byte) 0xe7, + (byte) 0x6b, (byte) 0x55, (byte) 0xf9, (byte) 0xc2, + (byte) 0xf0, (byte) 0x98 + }; + + public static final byte[] EC112_FP_G_Y = new byte[]{ + (byte) 0xa8, (byte) 0x9c, (byte) 0xe5, (byte) 0xaf, + (byte) 0x87, (byte) 0x24, (byte) 0xc0, (byte) 0xa2, + (byte) 0x3e, (byte) 0x0e, (byte) 0x0f, (byte) 0xf7, + (byte) 0x75, (byte) 0x00 + }; + + public static final byte[] EC112_FP_R = new byte[]{ + (byte) 0xdb, (byte) 0x7c, (byte) 0x2a, (byte) 0xbf, + (byte) 0x62, (byte) 0xe3, (byte) 0x5e, (byte) 0x76, + (byte) 0x28, (byte) 0xdf, (byte) 0xac, (byte) 0x65, + (byte) 0x61, (byte) 0xc5 + }; + + public static final short EC112_FP_K = 1; + + + // secp128r1 from http://www.secg.org/sec2-v2.pdf + public static final byte[] EC128_FP_P = new byte[]{ + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFD, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF + }; + + public static final byte[] EC128_FP_A = new byte[]{ + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFD, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFC + }; + + public static final byte[] EC128_FP_B = new byte[]{ + (byte) 0xE8, (byte) 0x75, (byte) 0x79, (byte) 0xC1, + (byte) 0x10, (byte) 0x79, (byte) 0xF4, (byte) 0x3D, + (byte) 0xD8, (byte) 0x24, (byte) 0x99, (byte) 0x3C, + (byte) 0x2C, (byte) 0xEE, (byte) 0x5E, (byte) 0xD3 + }; + + // G in compressed form / first part of ucompressed + public static final byte[] EC128_FP_G_X = new byte[]{ + (byte) 0x16, (byte) 0x1F, (byte) 0xF7, (byte) 0x52, + (byte) 0x8B, (byte) 0x89, (byte) 0x9B, (byte) 0x2D, + (byte) 0x0C, (byte) 0x28, (byte) 0x60, (byte) 0x7C, + (byte) 0xA5, (byte) 0x2C, (byte) 0x5B, (byte) 0x86 + }; + + // second part of G uncompressed + public static final byte[] EC128_FP_G_Y = new byte[]{ + (byte) 0xCF, (byte) 0x5A, (byte) 0xC8, (byte) 0x39, + (byte) 0x5B, (byte) 0xAF, (byte) 0xEB, (byte) 0x13, + (byte) 0xC0, (byte) 0x2D, (byte) 0xA2, (byte) 0x92, + (byte) 0xDD, (byte) 0xED, (byte) 0x7A, (byte) 0x83 + }; + // Order of G + public static final byte[] EC128_FP_R = new byte[]{ + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFE, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x75, (byte) 0xA3, (byte) 0x0D, (byte) 0x1B, + (byte) 0x90, (byte) 0x38, (byte) 0xA1, (byte) 0x15 + }; + // cofactor of G + public static final short EC128_FP_K = 1; + + // secp160r1 from http://www.secg.org/sec2-v2.pdf + public static final byte[] EC160_FP_P = new byte[]{ + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0x7F, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF + }; + + public static final byte[] EC160_FP_A = new byte[]{ + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0x7F, (byte) 0xFF, (byte) 0xFF, (byte) 0xFC + }; + + public static final byte[] EC160_FP_B = new byte[]{ + (byte) 0x1C, (byte) 0x97, (byte) 0xBE, (byte) 0xFC, + (byte) 0x54, (byte) 0xBD, (byte) 0x7A, (byte) 0x8B, + (byte) 0x65, (byte) 0xAC, (byte) 0xF8, (byte) 0x9F, + (byte) 0x81, (byte) 0xD4, (byte) 0xD4, (byte) 0xAD, + (byte) 0xC5, (byte) 0x65, (byte) 0xFA, (byte) 0x45 + }; + + // G in compressed form / first part of ucompressed + public static final byte[] EC160_FP_G_X = new byte[]{ + (byte) 0x4A, (byte) 0x96, (byte) 0xB5, (byte) 0x68, + (byte) 0x8E, (byte) 0xF5, (byte) 0x73, (byte) 0x28, + (byte) 0x46, (byte) 0x64, (byte) 0x69, (byte) 0x89, + (byte) 0x68, (byte) 0xC3, (byte) 0x8B, (byte) 0xB9, + (byte) 0x13, (byte) 0xCB, (byte) 0xFC, (byte) 0x82 + }; + + // second part of G uncompressed + public static final byte[] EC160_FP_G_Y = new byte[]{ + (byte) 0x23, (byte) 0xA6, (byte) 0x28, (byte) 0x55, + (byte) 0x31, (byte) 0x68, (byte) 0x94, (byte) 0x7D, + (byte) 0x59, (byte) 0xDC, (byte) 0xC9, (byte) 0x12, + (byte) 0x04, (byte) 0x23, (byte) 0x51, (byte) 0x37, + (byte) 0x7A, (byte) 0xC5, (byte) 0xFB, (byte) 0x32 + }; + // Order of G + public static final byte[] EC160_FP_R = new byte[]{ + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x01, (byte) 0xF4, (byte) 0xC8, + (byte) 0xF9, (byte) 0x27, (byte) 0xAE, (byte) 0xD3, + (byte) 0xCA, (byte) 0x75, (byte) 0x22, (byte) 0x57 + }; + // cofactor of G + public static final short EC160_FP_K = 1; + + + // secp192r1 from http://www.secg.org/sec2-v2.pdf + public static final byte[] EC192_FP_P = new byte[]{ + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFE, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF + }; + public static final byte[] EC192_FP_A = new byte[]{ + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFE, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFC + }; + public static final byte[] EC192_FP_B = new byte[]{ + (byte) 0x64, (byte) 0x21, (byte) 0x05, (byte) 0x19, + (byte) 0xE5, (byte) 0x9C, (byte) 0x80, (byte) 0xE7, + (byte) 0x0F, (byte) 0xA7, (byte) 0xE9, (byte) 0xAB, + (byte) 0x72, (byte) 0x24, (byte) 0x30, (byte) 0x49, + (byte) 0xFE, (byte) 0xB8, (byte) 0xDE, (byte) 0xEC, + (byte) 0xC1, (byte) 0x46, (byte) 0xB9, (byte) 0xB1 + }; + // G in compressed form / first part of ucompressed + public static final byte[] EC192_FP_G_X = new byte[]{ + (byte) 0x18, (byte) 0x8D, (byte) 0xA8, (byte) 0x0E, + (byte) 0xB0, (byte) 0x30, (byte) 0x90, (byte) 0xF6, + (byte) 0x7C, (byte) 0xBF, (byte) 0x20, (byte) 0xEB, + (byte) 0x43, (byte) 0xA1, (byte) 0x88, (byte) 0x00, + (byte) 0xF4, (byte) 0xFF, (byte) 0x0A, (byte) 0xFD, + (byte) 0x82, (byte) 0xFF, (byte) 0x10, (byte) 0x12 + }; + // second part of G uncompressed + public static final byte[] EC192_FP_G_Y = new byte[]{ + (byte) 0x07, (byte) 0x19, (byte) 0x2B, (byte) 0x95, + (byte) 0xFF, (byte) 0xC8, (byte) 0xDA, (byte) 0x78, + (byte) 0x63, (byte) 0x10, (byte) 0x11, (byte) 0xED, + (byte) 0x6B, (byte) 0x24, (byte) 0xCD, (byte) 0xD5, + (byte) 0x73, (byte) 0xF9, (byte) 0x77, (byte) 0xA1, + (byte) 0x1E, (byte) 0x79, (byte) 0x48, (byte) 0x11 + }; + // Order of G + public static final byte[] EC192_FP_R = new byte[]{ + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0x99, (byte) 0xDE, (byte) 0xF8, (byte) 0x36, + (byte) 0x14, (byte) 0x6B, (byte) 0xC9, (byte) 0xB1, + (byte) 0xB4, (byte) 0xD2, (byte) 0x28, (byte) 0x31 + }; + // cofactor of G + public static final short EC192_FP_K = 1; + + // secp224r1 from http://www.secg.org/sec2-v2.pdf + public static final byte[] EC224_FP_P = new byte[]{ + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01 + }; + + public static final byte[] EC224_FP_A = new byte[]{ + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFE, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFE + }; + + public static final byte[] EC224_FP_B = new byte[]{ + (byte) 0xB4, (byte) 0x05, (byte) 0x0A, (byte) 0x85, + (byte) 0x0C, (byte) 0x04, (byte) 0xB3, (byte) 0xAB, + (byte) 0xF5, (byte) 0x41, (byte) 0x32, (byte) 0x56, + (byte) 0x50, (byte) 0x44, (byte) 0xB0, (byte) 0xB7, + (byte) 0xD7, (byte) 0xBF, (byte) 0xD8, (byte) 0xBA, + (byte) 0x27, (byte) 0x0B, (byte) 0x39, (byte) 0x43, + (byte) 0x23, (byte) 0x55, (byte) 0xFF, (byte) 0xB4 + }; + + // G in compressed form / first part of ucompressed + public static final byte[] EC224_FP_G_X = new byte[]{ + (byte) 0xB7, (byte) 0x0E, (byte) 0x0C, (byte) 0xBD, + (byte) 0x6B, (byte) 0xB4, (byte) 0xBF, (byte) 0x7F, + (byte) 0x32, (byte) 0x13, (byte) 0x90, (byte) 0xB9, + (byte) 0x4A, (byte) 0x03, (byte) 0xC1, (byte) 0xD3, + (byte) 0x56, (byte) 0xC2, (byte) 0x11, (byte) 0x22, + (byte) 0x34, (byte) 0x32, (byte) 0x80, (byte) 0xD6, + (byte) 0x11, (byte) 0x5C, (byte) 0x1D, (byte) 0x21 + }; + // second part of G uncompressed + public static final byte[] EC224_FP_G_Y = new byte[]{ + (byte) 0xBD, (byte) 0x37, (byte) 0x63, (byte) 0x88, + (byte) 0xB5, (byte) 0xF7, (byte) 0x23, (byte) 0xFB, + (byte) 0x4C, (byte) 0x22, (byte) 0xDF, (byte) 0xE6, + (byte) 0xCD, (byte) 0x43, (byte) 0x75, (byte) 0xA0, + (byte) 0x5A, (byte) 0x07, (byte) 0x47, (byte) 0x64, + (byte) 0x44, (byte) 0xD5, (byte) 0x81, (byte) 0x99, + (byte) 0x85, (byte) 0x00, (byte) 0x7E, (byte) 0x34 + }; + // Order of G + public static final byte[] EC224_FP_R = new byte[]{ + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0x16, (byte) 0xA2, + (byte) 0xE0, (byte) 0xB8, (byte) 0xF0, (byte) 0x3E, + (byte) 0x13, (byte) 0xDD, (byte) 0x29, (byte) 0x45, + (byte) 0x5C, (byte) 0x5C, (byte) 0x2A, (byte) 0x3D + }; + // cofactor of G + public static final short EC224_FP_K = 1; + + // secp256r1 from http://www.secg.org/sec2-v2.pdf + public static final byte[] EC256_FP_P = new byte[]{ + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF + }; + public static final byte[] EC256_FP_A = new byte[]{ + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFC + }; + public static final byte[] EC256_FP_B = new byte[]{ + (byte) 0x5A, (byte) 0xC6, (byte) 0x35, (byte) 0xD8, + (byte) 0xAA, (byte) 0x3A, (byte) 0x93, (byte) 0xE7, + (byte) 0xB3, (byte) 0xEB, (byte) 0xBD, (byte) 0x55, + (byte) 0x76, (byte) 0x98, (byte) 0x86, (byte) 0xBC, + (byte) 0x65, (byte) 0x1D, (byte) 0x06, (byte) 0xB0, + (byte) 0xCC, (byte) 0x53, (byte) 0xB0, (byte) 0xF6, + (byte) 0x3B, (byte) 0xCE, (byte) 0x3C, (byte) 0x3E, + (byte) 0x27, (byte) 0xD2, (byte) 0x60, (byte) 0x4B + }; + // G in compressed form / first part of ucompressed + public static final byte[] EC256_FP_G_X = new byte[]{ + (byte) 0x6B, (byte) 0x17, (byte) 0xD1, (byte) 0xF2, + (byte) 0xE1, (byte) 0x2C, (byte) 0x42, (byte) 0x47, + (byte) 0xF8, (byte) 0xBC, (byte) 0xE6, (byte) 0xE5, + (byte) 0x63, (byte) 0xA4, (byte) 0x40, (byte) 0xF2, + (byte) 0x77, (byte) 0x03, (byte) 0x7D, (byte) 0x81, + (byte) 0x2D, (byte) 0xEB, (byte) 0x33, (byte) 0xA0, + (byte) 0xF4, (byte) 0xA1, (byte) 0x39, (byte) 0x45, + (byte) 0xD8, (byte) 0x98, (byte) 0xC2, (byte) 0x96 + }; + // second part of G uncompressed + public static final byte[] EC256_FP_G_Y = new byte[]{ + (byte) 0x4F, (byte) 0xE3, (byte) 0x42, (byte) 0xE2, + (byte) 0xFE, (byte) 0x1A, (byte) 0x7F, (byte) 0x9B, + (byte) 0x8E, (byte) 0xE7, (byte) 0xEB, (byte) 0x4A, + (byte) 0x7C, (byte) 0x0F, (byte) 0x9E, (byte) 0x16, + (byte) 0x2B, (byte) 0xCE, (byte) 0x33, (byte) 0x57, + (byte) 0x6B, (byte) 0x31, (byte) 0x5E, (byte) 0xCE, + (byte) 0xCB, (byte) 0xB6, (byte) 0x40, (byte) 0x68, + (byte) 0x37, (byte) 0xBF, (byte) 0x51, (byte) 0xF5 + }; + // Order of G + public static final byte[] EC256_FP_R = new byte[]{ + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xBC, (byte) 0xE6, (byte) 0xFA, (byte) 0xAD, + (byte) 0xA7, (byte) 0x17, (byte) 0x9E, (byte) 0x84, + (byte) 0xF3, (byte) 0xB9, (byte) 0xCA, (byte) 0xC2, + (byte) 0xFC, (byte) 0x63, (byte) 0x25, (byte) 0x51 + }; + // cofactor of G + public static final short EC256_FP_K = 1; + + // secp384r1 from http://www.secg.org/sec2-v2.pdf + public static final byte[] EC384_FP_P = new byte[]{ + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFE, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF + }; + + public static final byte[] EC384_FP_A = new byte[]{ + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFE, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFC + }; + + public static final byte[] EC384_FP_B = new byte[]{ + (byte) 0xB3, (byte) 0x31, (byte) 0x2F, (byte) 0xA7, + (byte) 0xE2, (byte) 0x3E, (byte) 0xE7, (byte) 0xE4, + (byte) 0x98, (byte) 0x8E, (byte) 0x05, (byte) 0x6B, + (byte) 0xE3, (byte) 0xF8, (byte) 0x2D, (byte) 0x19, + (byte) 0x18, (byte) 0x1D, (byte) 0x9C, (byte) 0x6E, + (byte) 0xFE, (byte) 0x81, (byte) 0x41, (byte) 0x12, + (byte) 0x03, (byte) 0x14, (byte) 0x08, (byte) 0x8F, + (byte) 0x50, (byte) 0x13, (byte) 0x87, (byte) 0x5A, + (byte) 0xC6, (byte) 0x56, (byte) 0x39, (byte) 0x8D, + (byte) 0x8A, (byte) 0x2E, (byte) 0xD1, (byte) 0x9D, + (byte) 0x2A, (byte) 0x85, (byte) 0xC8, (byte) 0xED, + (byte) 0xD3, (byte) 0xEC, (byte) 0x2A, (byte) 0xEF + }; + + // G in compressed form / first part of ucompressed + public static final byte[] EC384_FP_G_X = new byte[]{ + (byte) 0xAA, (byte) 0x87, (byte) 0xCA, (byte) 0x22, + (byte) 0xBE, (byte) 0x8B, (byte) 0x05, (byte) 0x37, + (byte) 0x8E, (byte) 0xB1, (byte) 0xC7, (byte) 0x1E, + (byte) 0xF3, (byte) 0x20, (byte) 0xAD, (byte) 0x74, + (byte) 0x6E, (byte) 0x1D, (byte) 0x3B, (byte) 0x62, + (byte) 0x8B, (byte) 0xA7, (byte) 0x9B, (byte) 0x98, + (byte) 0x59, (byte) 0xF7, (byte) 0x41, (byte) 0xE0, + (byte) 0x82, (byte) 0x54, (byte) 0x2A, (byte) 0x38, + (byte) 0x55, (byte) 0x02, (byte) 0xF2, (byte) 0x5D, + (byte) 0xBF, (byte) 0x55, (byte) 0x29, (byte) 0x6C, + (byte) 0x3A, (byte) 0x54, (byte) 0x5E, (byte) 0x38, + (byte) 0x72, (byte) 0x76, (byte) 0x0A, (byte) 0xB7 + }; + // second part of G uncompressed + public static final byte[] EC384_FP_G_Y = new byte[]{ + (byte) 0x36, (byte) 0x17, (byte) 0xDE, (byte) 0x4A, + (byte) 0x96, (byte) 0x26, (byte) 0x2C, (byte) 0x6F, + (byte) 0x5D, (byte) 0x9E, (byte) 0x98, (byte) 0xBF, + (byte) 0x92, (byte) 0x92, (byte) 0xDC, (byte) 0x29, + (byte) 0xF8, (byte) 0xF4, (byte) 0x1D, (byte) 0xBD, + (byte) 0x28, (byte) 0x9A, (byte) 0x14, (byte) 0x7C, + (byte) 0xE9, (byte) 0xDA, (byte) 0x31, (byte) 0x13, + (byte) 0xB5, (byte) 0xF0, (byte) 0xB8, (byte) 0xC0, + (byte) 0x0A, (byte) 0x60, (byte) 0xB1, (byte) 0xCE, + (byte) 0x1D, (byte) 0x7E, (byte) 0x81, (byte) 0x9D, + (byte) 0x7A, (byte) 0x43, (byte) 0x1D, (byte) 0x7C, + (byte) 0x90, (byte) 0xEA, (byte) 0x0E, (byte) 0x5F + }; + + // Order of G + public static final byte[] EC384_FP_R = new byte[]{ + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xC7, (byte) 0x63, (byte) 0x4D, (byte) 0x81, + (byte) 0xF4, (byte) 0x37, (byte) 0x2D, (byte) 0xDF, + (byte) 0x58, (byte) 0x1A, (byte) 0x0D, (byte) 0xB2, + (byte) 0x48, (byte) 0xB0, (byte) 0xA7, (byte) 0x7A, + (byte) 0xEC, (byte) 0xEC, (byte) 0x19, (byte) 0x6A, + (byte) 0xCC, (byte) 0xC5, (byte) 0x29, (byte) 0x73 + }; + // cofactor of G + public static final short EC384_FP_K = 1; + + + // secp521r1 from http://www.secg.org/sec2-v2.pdf + public static final byte[] EC521_FP_P = new byte[]{ + (byte) 0x01, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF + }; + + public static final byte[] EC521_FP_A = new byte[]{ + (byte) 0x01, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFC + }; + + public static final byte[] EC521_FP_B = new byte[]{ + (byte) 0x00, (byte) 0x51, (byte) 0x95, (byte) 0x3E, + (byte) 0xB9, (byte) 0x61, (byte) 0x8E, (byte) 0x1C, + (byte) 0x9A, (byte) 0x1F, (byte) 0x92, (byte) 0x9A, + (byte) 0x21, (byte) 0xA0, (byte) 0xB6, (byte) 0x85, + (byte) 0x40, (byte) 0xEE, (byte) 0xA2, (byte) 0xDA, + (byte) 0x72, (byte) 0x5B, (byte) 0x99, (byte) 0xB3, + (byte) 0x15, (byte) 0xF3, (byte) 0xB8, (byte) 0xB4, + (byte) 0x89, (byte) 0x91, (byte) 0x8E, (byte) 0xF1, + (byte) 0x09, (byte) 0xE1, (byte) 0x56, (byte) 0x19, + (byte) 0x39, (byte) 0x51, (byte) 0xEC, (byte) 0x7E, + (byte) 0x93, (byte) 0x7B, (byte) 0x16, (byte) 0x52, + (byte) 0xC0, (byte) 0xBD, (byte) 0x3B, (byte) 0xB1, + (byte) 0xBF, (byte) 0x07, (byte) 0x35, (byte) 0x73, + (byte) 0xDF, (byte) 0x88, (byte) 0x3D, (byte) 0x2C, + (byte) 0x34, (byte) 0xF1, (byte) 0xEF, (byte) 0x45, + (byte) 0x1F, (byte) 0xD4, (byte) 0x6B, (byte) 0x50, + (byte) 0x3F, (byte) 0x00 + }; + + // G in compressed form / first part of ucompressed + public static final byte[] EC521_FP_G_X = new byte[]{ + (byte) 0x00, (byte) 0xC6, (byte) 0x85, (byte) 0x8E, + (byte) 0x06, (byte) 0xB7, (byte) 0x04, (byte) 0x04, + (byte) 0xE9, (byte) 0xCD, (byte) 0x9E, (byte) 0x3E, + (byte) 0xCB, (byte) 0x66, (byte) 0x23, (byte) 0x95, + (byte) 0xB4, (byte) 0x42, (byte) 0x9C, (byte) 0x64, + (byte) 0x81, (byte) 0x39, (byte) 0x05, (byte) 0x3F, + (byte) 0xB5, (byte) 0x21, (byte) 0xF8, (byte) 0x28, + (byte) 0xAF, (byte) 0x60, (byte) 0x6B, (byte) 0x4D, + (byte) 0x3D, (byte) 0xBA, (byte) 0xA1, (byte) 0x4B, + (byte) 0x5E, (byte) 0x77, (byte) 0xEF, (byte) 0xE7, + (byte) 0x59, (byte) 0x28, (byte) 0xFE, (byte) 0x1D, + (byte) 0xC1, (byte) 0x27, (byte) 0xA2, (byte) 0xFF, + (byte) 0xA8, (byte) 0xDE, (byte) 0x33, (byte) 0x48, + (byte) 0xB3, (byte) 0xC1, (byte) 0x85, (byte) 0x6A, + (byte) 0x42, (byte) 0x9B, (byte) 0xF9, (byte) 0x7E, + (byte) 0x7E, (byte) 0x31, (byte) 0xC2, (byte) 0xE5, + (byte) 0xBD, (byte) 0x66 + }; + + // second part of G uncompressed + public static final byte[] EC521_FP_G_Y = new byte[]{ + (byte) 0x01, (byte) 0x18, (byte) 0x39, (byte) 0x29, + (byte) 0x6A, (byte) 0x78, (byte) 0x9A, (byte) 0x3B, + (byte) 0xC0, (byte) 0x04, (byte) 0x5C, (byte) 0x8A, + (byte) 0x5F, (byte) 0xB4, (byte) 0x2C, (byte) 0x7D, + (byte) 0x1B, (byte) 0xD9, (byte) 0x98, (byte) 0xF5, + (byte) 0x44, (byte) 0x49, (byte) 0x57, (byte) 0x9B, + (byte) 0x44, (byte) 0x68, (byte) 0x17, (byte) 0xAF, + (byte) 0xBD, (byte) 0x17, (byte) 0x27, (byte) 0x3E, + (byte) 0x66, (byte) 0x2C, (byte) 0x97, (byte) 0xEE, + (byte) 0x72, (byte) 0x99, (byte) 0x5E, (byte) 0xF4, + (byte) 0x26, (byte) 0x40, (byte) 0xC5, (byte) 0x50, + (byte) 0xB9, (byte) 0x01, (byte) 0x3F, (byte) 0xAD, + (byte) 0x07, (byte) 0x61, (byte) 0x35, (byte) 0x3C, + (byte) 0x70, (byte) 0x86, (byte) 0xA2, (byte) 0x72, + (byte) 0xC2, (byte) 0x40, (byte) 0x88, (byte) 0xBE, + (byte) 0x94, (byte) 0x76, (byte) 0x9F, (byte) 0xD1, + (byte) 0x66, (byte) 0x50 + }; + + // Order of G + public static final byte[] EC521_FP_R = new byte[]{ + (byte) 0x01, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFA, + (byte) 0x51, (byte) 0x86, (byte) 0x87, (byte) 0x83, + (byte) 0xBF, (byte) 0x2F, (byte) 0x96, (byte) 0x6B, + (byte) 0x7F, (byte) 0xCC, (byte) 0x01, (byte) 0x48, + (byte) 0xF7, (byte) 0x09, (byte) 0xA5, (byte) 0xD0, + (byte) 0x3B, (byte) 0xB5, (byte) 0xC9, (byte) 0xB8, + (byte) 0x89, (byte) 0x9C, (byte) 0x47, (byte) 0xAE, + (byte) 0xBB, (byte) 0x6F, (byte) 0xB7, (byte) 0x1E, + (byte) 0x91, (byte) 0x38, (byte) 0x64, (byte) 0x09 + }; + + // cofactor of G + public static final short EC521_FP_K = 1; + + //sect163r1 from http://www.secg.org/sec2-v2.pdf + // [short i1, short i2, short i3] f = x^163 + x^i1 + x^i2 + x^i3 + 1 + public static final byte[] EC163_F2M_F = new byte[]{ + (byte) 0x00, (byte) 0x07, + (byte) 0x00, (byte) 0x06, + (byte) 0x00, (byte) 0x03 + }; + + public static final byte[] EC163_F2M_A = new byte[]{ + (byte) 0x07, (byte) 0xB6, (byte) 0x88, (byte) 0x2C, + (byte) 0xAA, (byte) 0xEF, (byte) 0xA8, (byte) 0x4F, + (byte) 0x95, (byte) 0x54, (byte) 0xFF, (byte) 0x84, + (byte) 0x28, (byte) 0xBD, (byte) 0x88, (byte) 0xE2, + (byte) 0x46, (byte) 0xD2, (byte) 0x78, (byte) 0x2A, + (byte) 0xE2 + }; + + public static final byte[] EC163_F2M_B = new byte[]{ + (byte) 0x07, (byte) 0x13, (byte) 0x61, (byte) 0x2D, + (byte) 0xCD, (byte) 0xDC, (byte) 0xB4, (byte) 0x0A, + (byte) 0xAB, (byte) 0x94, (byte) 0x6B, (byte) 0xDA, + (byte) 0x29, (byte) 0xCA, (byte) 0x91, (byte) 0xF7, + (byte) 0x3A, (byte) 0xF9, (byte) 0x58, (byte) 0xAF, + (byte) 0xD9 + }; + + // G in compressed form / first part of ucompressed + public static final byte[] EC163_F2M_G_X = new byte[]{ + (byte) 0x03, (byte) 0x69, (byte) 0x97, (byte) 0x96, + (byte) 0x97, (byte) 0xAB, (byte) 0x43, (byte) 0x89, + (byte) 0x77, (byte) 0x89, (byte) 0x56, (byte) 0x67, + (byte) 0x89, (byte) 0x56, (byte) 0x7F, (byte) 0x78, + (byte) 0x7A, (byte) 0x78, (byte) 0x76, (byte) 0xA6, + (byte) 0x54 + }; + + // second part of G uncompressed + public static final byte[] EC163_F2M_G_Y = new byte[]{ + (byte) 0x00, (byte) 0x43, (byte) 0x5E, (byte) 0xDB, + (byte) 0x42, (byte) 0xEF, (byte) 0xAF, (byte) 0xB2, + (byte) 0x98, (byte) 0x9D, (byte) 0x51, (byte) 0xFE, + (byte) 0xFC, (byte) 0xE3, (byte) 0xC8, (byte) 0x09, + (byte) 0x88, (byte) 0xF4, (byte) 0x1F, (byte) 0xF8, + (byte) 0x83 + }; + + // order of G + public static final byte[] EC163_F2M_R = new byte[]{ + (byte) 0x03, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x48, + (byte) 0xAA, (byte) 0xB6, (byte) 0x89, (byte) 0xC2, + (byte) 0x9C, (byte) 0xA7, (byte) 0x10, (byte) 0x27, + (byte) 0x9B + }; + + // cofactor of G + public static final short EC163_F2M_K = 2; + + //sect233r1 from http://www.secg.org/sec2-v2.pdf + // [short i1, short i2, short i3] f = x^233 + x^i1 + 1 + public static final byte[] EC233_F2M_F = new byte[]{ + (byte) 0x00, (byte) 0x4a + }; + + public static final byte[] EC233_F2M_A = new byte[]{ + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x01 + }; + + public static final byte[] EC233_F2M_B = new byte[]{ + (byte) 0x00, (byte) 0x66, (byte) 0x64, (byte) 0x7E, + (byte) 0xDE, (byte) 0x6C, (byte) 0x33, (byte) 0x2C, + (byte) 0x7F, (byte) 0x8C, (byte) 0x09, (byte) 0x23, + (byte) 0xBB, (byte) 0x58, (byte) 0x21, (byte) 0x3B, + (byte) 0x33, (byte) 0x3B, (byte) 0x20, (byte) 0xE9, + (byte) 0xCE, (byte) 0x42, (byte) 0x81, (byte) 0xFE, + (byte) 0x11, (byte) 0x5F, (byte) 0x7D, (byte) 0x8F, + (byte) 0x90, (byte) 0xAD + }; + + // G in compressed form / first part of ucompressed + public static final byte[] EC233_F2M_G_X = new byte[]{ + (byte) 0x00, (byte) 0xFA, (byte) 0xC9, (byte) 0xDF, + (byte) 0xCB, (byte) 0xAC, (byte) 0x83, (byte) 0x13, + (byte) 0xBB, (byte) 0x21, (byte) 0x39, (byte) 0xF1, + (byte) 0xBB, (byte) 0x75, (byte) 0x5F, (byte) 0xEF, + (byte) 0x65, (byte) 0xBC, (byte) 0x39, (byte) 0x1F, + (byte) 0x8B, (byte) 0x36, (byte) 0xF8, (byte) 0xF8, + (byte) 0xEB, (byte) 0x73, (byte) 0x71, (byte) 0xFD, + (byte) 0x55, (byte) 0x8B + }; + + // second part of G uncompressed + public static final byte[] EC233_F2M_G_Y = new byte[]{ + (byte) 0x01, (byte) 0x00, (byte) 0x6A, (byte) 0x08, + (byte) 0xA4, (byte) 0x19, (byte) 0x03, (byte) 0x35, + (byte) 0x06, (byte) 0x78, (byte) 0xE5, (byte) 0x85, + (byte) 0x28, (byte) 0xBE, (byte) 0xBF, (byte) 0x8A, + (byte) 0x0B, (byte) 0xEF, (byte) 0xF8, (byte) 0x67, + (byte) 0xA7, (byte) 0xCA, (byte) 0x36, (byte) 0x71, + (byte) 0x6F, (byte) 0x7E, (byte) 0x01, (byte) 0xF8, + (byte) 0x10, (byte) 0x52 + }; + + // order of G + public static final byte[] EC233_F2M_R = new byte[]{ + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x13, + (byte) 0xE9, (byte) 0x74, (byte) 0xE7, (byte) 0x2F, + (byte) 0x8A, (byte) 0x69, (byte) 0x22, (byte) 0x03, + (byte) 0x1D, (byte) 0x26, (byte) 0x03, (byte) 0xCF, + (byte) 0xE0, (byte) 0xD7 + }; + + // cofactor of G + public static final short EC233_F2M_K = 2; + + //sect283r1 from http://www.secg.org/sec2-v2.pdf + // [short i1, short i2, short i3] f = x^283 + x^i1 + x^i2 + x^i3 + 1 + public static final byte[] EC283_F2M_F = new byte[]{ + (byte) 0x00, (byte) 0x0c, + (byte) 0x00, (byte) 0x07, + (byte) 0x00, (byte) 0x05 + }; + + public static final byte[] EC283_F2M_A = new byte[]{ + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01 + }; + + public static final byte[] EC283_F2M_B = new byte[]{ + (byte) 0x02, (byte) 0x7B, (byte) 0x68, (byte) 0x0A, + (byte) 0xC8, (byte) 0xB8, (byte) 0x59, (byte) 0x6D, + (byte) 0xA5, (byte) 0xA4, (byte) 0xAF, (byte) 0x8A, + (byte) 0x19, (byte) 0xA0, (byte) 0x30, (byte) 0x3F, + (byte) 0xCA, (byte) 0x97, (byte) 0xFD, (byte) 0x76, + (byte) 0x45, (byte) 0x30, (byte) 0x9F, (byte) 0xA2, + (byte) 0xA5, (byte) 0x81, (byte) 0x48, (byte) 0x5A, + (byte) 0xF6, (byte) 0x26, (byte) 0x3E, (byte) 0x31, + (byte) 0x3B, (byte) 0x79, (byte) 0xA2, (byte) 0xF5 + }; + + // G in compressed form / first part of ucompressed + public static final byte[] EC283_F2M_G_X = new byte[]{ + (byte) 0x05, (byte) 0xF9, (byte) 0x39, (byte) 0x25, + (byte) 0x8D, (byte) 0xB7, (byte) 0xDD, (byte) 0x90, + (byte) 0xE1, (byte) 0x93, (byte) 0x4F, (byte) 0x8C, + (byte) 0x70, (byte) 0xB0, (byte) 0xDF, (byte) 0xEC, + (byte) 0x2E, (byte) 0xED, (byte) 0x25, (byte) 0xB8, + (byte) 0x55, (byte) 0x7E, (byte) 0xAC, (byte) 0x9C, + (byte) 0x80, (byte) 0xE2, (byte) 0xE1, (byte) 0x98, + (byte) 0xF8, (byte) 0xCD, (byte) 0xBE, (byte) 0xCD, + (byte) 0x86, (byte) 0xB1, (byte) 0x20, (byte) 0x53 + }; + + // second part of G uncompressed + public static final byte[] EC283_F2M_G_Y = new byte[]{ + (byte) 0x03, (byte) 0x67, (byte) 0x68, (byte) 0x54, + (byte) 0xFE, (byte) 0x24, (byte) 0x14, (byte) 0x1C, + (byte) 0xB9, (byte) 0x8F, (byte) 0xE6, (byte) 0xD4, + (byte) 0xB2, (byte) 0x0D, (byte) 0x02, (byte) 0xB4, + (byte) 0x51, (byte) 0x6F, (byte) 0xF7, (byte) 0x02, + (byte) 0x35, (byte) 0x0E, (byte) 0xDD, (byte) 0xB0, + (byte) 0x82, (byte) 0x67, (byte) 0x79, (byte) 0xC8, + (byte) 0x13, (byte) 0xF0, (byte) 0xDF, (byte) 0x45, + (byte) 0xBE, (byte) 0x81, (byte) 0x12, (byte) 0xF4 + }; + + // order of G + public static final byte[] EC283_F2M_R = new byte[]{ + (byte) 0x03, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xEF, (byte) 0x90, + (byte) 0x39, (byte) 0x96, (byte) 0x60, (byte) 0xFC, + (byte) 0x93, (byte) 0x8A, (byte) 0x90, (byte) 0x16, + (byte) 0x5B, (byte) 0x04, (byte) 0x2A, (byte) 0x7C, + (byte) 0xEF, (byte) 0xAD, (byte) 0xB3, (byte) 0x07 + }; + + // cofactor of G + public static final short EC283_F2M_K = 2; + + //sect409r1 from http://www.secg.org/sec2-v2.pdf + // [short i1, short i2, short i3] f = x^409 + x^i1 + 1 + public static final byte[] EC409_F2M_F = new byte[]{ + (byte) 0x00, (byte) 0x57 + }; + + public static final byte[] EC409_F2M_A = new byte[]{ + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01 + }; + + public static final byte[] EC409_F2M_B = new byte[]{ + (byte) 0x00, (byte) 0x21, (byte) 0xA5, (byte) 0xC2, + (byte) 0xC8, (byte) 0xEE, (byte) 0x9F, (byte) 0xEB, + (byte) 0x5C, (byte) 0x4B, (byte) 0x9A, (byte) 0x75, + (byte) 0x3B, (byte) 0x7B, (byte) 0x47, (byte) 0x6B, + (byte) 0x7F, (byte) 0xD6, (byte) 0x42, (byte) 0x2E, + (byte) 0xF1, (byte) 0xF3, (byte) 0xDD, (byte) 0x67, + (byte) 0x47, (byte) 0x61, (byte) 0xFA, (byte) 0x99, + (byte) 0xD6, (byte) 0xAC, (byte) 0x27, (byte) 0xC8, + (byte) 0xA9, (byte) 0xA1, (byte) 0x97, (byte) 0xB2, + (byte) 0x72, (byte) 0x82, (byte) 0x2F, (byte) 0x6C, + (byte) 0xD5, (byte) 0x7A, (byte) 0x55, (byte) 0xAA, + (byte) 0x4F, (byte) 0x50, (byte) 0xAE, (byte) 0x31, + (byte) 0x7B, (byte) 0x13, (byte) 0x54, (byte) 0x5F + }; + + // G in compressed form / first part of ucompressed + public static final byte[] EC409_F2M_G_X = new byte[]{ + (byte) 0x01, (byte) 0x5D, (byte) 0x48, (byte) 0x60, + (byte) 0xD0, (byte) 0x88, (byte) 0xDD, (byte) 0xB3, + (byte) 0x49, (byte) 0x6B, (byte) 0x0C, (byte) 0x60, + (byte) 0x64, (byte) 0x75, (byte) 0x62, (byte) 0x60, + (byte) 0x44, (byte) 0x1C, (byte) 0xDE, (byte) 0x4A, + (byte) 0xF1, (byte) 0x77, (byte) 0x1D, (byte) 0x4D, + (byte) 0xB0, (byte) 0x1F, (byte) 0xFE, (byte) 0x5B, + (byte) 0x34, (byte) 0xE5, (byte) 0x97, (byte) 0x03, + (byte) 0xDC, (byte) 0x25, (byte) 0x5A, (byte) 0x86, + (byte) 0x8A, (byte) 0x11, (byte) 0x80, (byte) 0x51, + (byte) 0x56, (byte) 0x03, (byte) 0xAE, (byte) 0xAB, + (byte) 0x60, (byte) 0x79, (byte) 0x4E, (byte) 0x54, + (byte) 0xBB, (byte) 0x79, (byte) 0x96, (byte) 0xA7 + }; + + // second part of G uncompressed + public static final byte[] EC409_F2M_G_Y = new byte[]{ + (byte) 0x00, (byte) 0x61, (byte) 0xB1, (byte) 0xCF, + (byte) 0xAB, (byte) 0x6B, (byte) 0xE5, (byte) 0xF3, + (byte) 0x2B, (byte) 0xBF, (byte) 0xA7, (byte) 0x83, + (byte) 0x24, (byte) 0xED, (byte) 0x10, (byte) 0x6A, + (byte) 0x76, (byte) 0x36, (byte) 0xB9, (byte) 0xC5, + (byte) 0xA7, (byte) 0xBD, (byte) 0x19, (byte) 0x8D, + (byte) 0x01, (byte) 0x58, (byte) 0xAA, (byte) 0x4F, + (byte) 0x54, (byte) 0x88, (byte) 0xD0, (byte) 0x8F, + (byte) 0x38, (byte) 0x51, (byte) 0x4F, (byte) 0x1F, + (byte) 0xDF, (byte) 0x4B, (byte) 0x4F, (byte) 0x40, + (byte) 0xD2, (byte) 0x18, (byte) 0x1B, (byte) 0x36, + (byte) 0x81, (byte) 0xC3, (byte) 0x64, (byte) 0xBA, + (byte) 0x02, (byte) 0x73, (byte) 0xC7, (byte) 0x06 + }; + + // order of G + public static final byte[] EC409_F2M_R = new byte[]{ + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0xE2, + (byte) 0xAA, (byte) 0xD6, (byte) 0xA6, (byte) 0x12, + (byte) 0xF3, (byte) 0x33, (byte) 0x07, (byte) 0xBE, + (byte) 0x5F, (byte) 0xA4, (byte) 0x7C, (byte) 0x3C, + (byte) 0x9E, (byte) 0x05, (byte) 0x2F, (byte) 0x83, + (byte) 0x81, (byte) 0x64, (byte) 0xCD, (byte) 0x37, + (byte) 0xD9, (byte) 0xA2, (byte) 0x11, (byte) 0x73 + }; + + // cofactor of G + public static final short EC409_F2M_K = 2; + + //sect571r1 from http://www.secg.org/sec2-v2.pdf + // [short i1, short i2, short i3] f = x^571 + x^i1 + x^i2 + x^i3 + 1 + public static final byte[] EC571_F2M_F = new byte[]{ + (byte) 0x00, (byte) 0x0a, + (byte) 0x00, (byte) 0x05, + (byte) 0x00, (byte) 0x02, + }; + + public static final byte[] EC571_F2M_A = new byte[]{ + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01 + }; + + public static final byte[] EC571_F2M_B = new byte[]{ + (byte) 0x02, (byte) 0xF4, (byte) 0x0E, (byte) 0x7E, + (byte) 0x22, (byte) 0x21, (byte) 0xF2, (byte) 0x95, + (byte) 0xDE, (byte) 0x29, (byte) 0x71, (byte) 0x17, + (byte) 0xB7, (byte) 0xF3, (byte) 0xD6, (byte) 0x2F, + (byte) 0x5C, (byte) 0x6A, (byte) 0x97, (byte) 0xFF, + (byte) 0xCB, (byte) 0x8C, (byte) 0xEF, (byte) 0xF1, + (byte) 0xCD, (byte) 0x6B, (byte) 0xA8, (byte) 0xCE, + (byte) 0x4A, (byte) 0x9A, (byte) 0x18, (byte) 0xAD, + (byte) 0x84, (byte) 0xFF, (byte) 0xAB, (byte) 0xBD, + (byte) 0x8E, (byte) 0xFA, (byte) 0x59, (byte) 0x33, + (byte) 0x2B, (byte) 0xE7, (byte) 0xAD, (byte) 0x67, + (byte) 0x56, (byte) 0xA6, (byte) 0x6E, (byte) 0x29, + (byte) 0x4A, (byte) 0xFD, (byte) 0x18, (byte) 0x5A, + (byte) 0x78, (byte) 0xFF, (byte) 0x12, (byte) 0xAA, + (byte) 0x52, (byte) 0x0E, (byte) 0x4D, (byte) 0xE7, + (byte) 0x39, (byte) 0xBA, (byte) 0xCA, (byte) 0x0C, + (byte) 0x7F, (byte) 0xFE, (byte) 0xFF, (byte) 0x7F, + (byte) 0x29, (byte) 0x55, (byte) 0x72, (byte) 0x7A + }; + + // G in compressed form / first part of ucompressed + public static final byte[] EC571_F2M_G_X = new byte[]{ + (byte) 0x03, (byte) 0x03, (byte) 0x00, (byte) 0x1D, + (byte) 0x34, (byte) 0xB8, (byte) 0x56, (byte) 0x29, + (byte) 0x6C, (byte) 0x16, (byte) 0xC0, (byte) 0xD4, + (byte) 0x0D, (byte) 0x3C, (byte) 0xD7, (byte) 0x75, + (byte) 0x0A, (byte) 0x93, (byte) 0xD1, (byte) 0xD2, + (byte) 0x95, (byte) 0x5F, (byte) 0xA8, (byte) 0x0A, + (byte) 0xA5, (byte) 0xF4, (byte) 0x0F, (byte) 0xC8, + (byte) 0xDB, (byte) 0x7B, (byte) 0x2A, (byte) 0xBD, + (byte) 0xBD, (byte) 0xE5, (byte) 0x39, (byte) 0x50, + (byte) 0xF4, (byte) 0xC0, (byte) 0xD2, (byte) 0x93, + (byte) 0xCD, (byte) 0xD7, (byte) 0x11, (byte) 0xA3, + (byte) 0x5B, (byte) 0x67, (byte) 0xFB, (byte) 0x14, + (byte) 0x99, (byte) 0xAE, (byte) 0x60, (byte) 0x03, + (byte) 0x86, (byte) 0x14, (byte) 0xF1, (byte) 0x39, + (byte) 0x4A, (byte) 0xBF, (byte) 0xA3, (byte) 0xB4, + (byte) 0xC8, (byte) 0x50, (byte) 0xD9, (byte) 0x27, + (byte) 0xE1, (byte) 0xE7, (byte) 0x76, (byte) 0x9C, + (byte) 0x8E, (byte) 0xEC, (byte) 0x2D, (byte) 0x19 + }; + + // second part of G uncompressed + public static final byte[] EC571_F2M_G_Y = new byte[]{ + (byte) 0x03, (byte) 0x7B, (byte) 0xF2, (byte) 0x73, + (byte) 0x42, (byte) 0xDA, (byte) 0x63, (byte) 0x9B, + (byte) 0x6D, (byte) 0xCC, (byte) 0xFF, (byte) 0xFE, + (byte) 0xB7, (byte) 0x3D, (byte) 0x69, (byte) 0xD7, + (byte) 0x8C, (byte) 0x6C, (byte) 0x27, (byte) 0xA6, + (byte) 0x00, (byte) 0x9C, (byte) 0xBB, (byte) 0xCA, + (byte) 0x19, (byte) 0x80, (byte) 0xF8, (byte) 0x53, + (byte) 0x39, (byte) 0x21, (byte) 0xE8, (byte) 0xA6, + (byte) 0x84, (byte) 0x42, (byte) 0x3E, (byte) 0x43, + (byte) 0xBA, (byte) 0xB0, (byte) 0x8A, (byte) 0x57, + (byte) 0x62, (byte) 0x91, (byte) 0xAF, (byte) 0x8F, + (byte) 0x46, (byte) 0x1B, (byte) 0xB2, (byte) 0xA8, + (byte) 0xB3, (byte) 0x53, (byte) 0x1D, (byte) 0x2F, + (byte) 0x04, (byte) 0x85, (byte) 0xC1, (byte) 0x9B, + (byte) 0x16, (byte) 0xE2, (byte) 0xF1, (byte) 0x51, + (byte) 0x6E, (byte) 0x23, (byte) 0xDD, (byte) 0x3C, + (byte) 0x1A, (byte) 0x48, (byte) 0x27, (byte) 0xAF, + (byte) 0x1B, (byte) 0x8A, (byte) 0xC1, (byte) 0x5B + }; + + // order of G + public static final byte[] EC571_F2M_R = new byte[]{ + (byte) 0x03, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xE6, (byte) 0x61, (byte) 0xCE, (byte) 0x18, + (byte) 0xFF, (byte) 0x55, (byte) 0x98, (byte) 0x73, + (byte) 0x08, (byte) 0x05, (byte) 0x9B, (byte) 0x18, + (byte) 0x68, (byte) 0x23, (byte) 0x85, (byte) 0x1E, + (byte) 0xC7, (byte) 0xDD, (byte) 0x9C, (byte) 0xA1, + (byte) 0x16, (byte) 0x1D, (byte) 0xE9, (byte) 0x3D, + (byte) 0x51, (byte) 0x74, (byte) 0xD6, (byte) 0x6E, + (byte) 0x83, (byte) 0x82, (byte) 0xE9, (byte) 0xBB, + (byte) 0x2F, (byte) 0xE8, (byte) 0x4E, (byte) 0x47 + }; + + // cofactor of G + public static final short EC571_F2M_K = 2; + + + // transformParameter TRANSFORMATION types + public static final short TRANSFORMATION_NONE = (short) 0x00; + public static final short TRANSFORMATION_FIXED = (short) 0x01; + public static final short TRANSFORMATION_FULLRANDOM = (short) 0x02; + public static final short TRANSFORMATION_ONEBYTERANDOM = (short) 0x04; + public static final short TRANSFORMATION_ZERO = (short) 0x08; + public static final short TRANSFORMATION_ONE = (short) 0x10; + public static final short TRANSFORMATION_MAX = (short) 0x20; + public static final short TRANSFORMATION_INCREMENT = (short) 0x40; + public static final short TRANSFORMATION_INFINITY = (short) 0x80; + public static final short TRANSFORMATION_COMPRESS = (short) 0x0100; + public static final short TRANSFORMATION_COMPRESS_HYBRID = (short) 0x0200; + public static final short TRANSFORMATION_04_MASK = (short) 0x0400; + + // toX962 FORM types + public static final byte X962_UNCOMPRESSED = (byte) 0x00; + public static final byte X962_COMPRESSED = (byte) 0x01; + public static final byte X962_HYBRID = (byte) 0x02; + + // Supported embedded curves, getCurveParameter + public static final byte CURVE_default = (byte) 0; + public static final byte CURVE_external = (byte) 0xff; + + // SECG recommended curves over FP + public static final byte CURVE_secp112r1 = (byte) 1; + public static final byte CURVE_secp128r1 = (byte) 2; + public static final byte CURVE_secp160r1 = (byte) 3; + public static final byte CURVE_secp192r1 = (byte) 4; + public static final byte CURVE_secp224r1 = (byte) 5; + public static final byte CURVE_secp256r1 = (byte) 6; + public static final byte CURVE_secp384r1 = (byte) 7; + public static final byte CURVE_secp521r1 = (byte) 8; + + public static final byte FP_CURVES = (byte) 8; + + // SECG recommended curves over F2M + public static final byte CURVE_sect163r1 = (byte) 9; + public static final byte CURVE_sect233r1 = (byte) 10; + public static final byte CURVE_sect283r1 = (byte) 11; + public static final byte CURVE_sect409r1 = (byte) 12; + public static final byte CURVE_sect571r1 = (byte) 13; + + public static final byte F2M_CURVES = (byte) 13; + + public static final short[] FP_SIZES = new short[]{112, 128, 160, 192, 224, 256, 384, 521}; + public static final short[] F2M_SIZES = new short[]{163, 233, 283, 409, 571}; + + public static final byte ALG_EC_F2M = 4; + public static final byte ALG_EC_FP = 5; + + // Class javacard.security.KeyAgreement + // javacard.security.KeyAgreement Fields: + public static final byte KeyAgreement_ALG_EC_SVDP_DH = 1; + public static final byte KeyAgreement_ALG_EC_SVDP_DH_KDF = 1; + public static final byte KeyAgreement_ALG_EC_SVDP_DHC = 2; + public static final byte KeyAgreement_ALG_EC_SVDP_DHC_KDF = 2; + public static final byte KeyAgreement_ALG_EC_SVDP_DH_PLAIN = 3; + public static final byte KeyAgreement_ALG_EC_SVDP_DHC_PLAIN = 4; + public static final byte KeyAgreement_ALG_EC_PACE_GM = 5; + public static final byte KeyAgreement_ALG_EC_SVDP_DH_PLAIN_XY = 6; + + public static final byte[] KA_TYPES = new byte[]{ + KeyAgreement_ALG_EC_SVDP_DH, + //KeyAgreement_ALG_EC_SVDP_DH_KDF, //duplicate + KeyAgreement_ALG_EC_SVDP_DHC, + //KeyAgreement_ALG_EC_SVDP_DHC_KDF, //duplicate + KeyAgreement_ALG_EC_SVDP_DH_PLAIN, + KeyAgreement_ALG_EC_SVDP_DHC_PLAIN, + KeyAgreement_ALG_EC_PACE_GM, + KeyAgreement_ALG_EC_SVDP_DH_PLAIN_XY + }; + + // Class javacard.security.Signature + // javacard.security.Signature Fields: + public static final byte Signature_ALG_ECDSA_SHA = 17; + public static final byte Signature_ALG_ECDSA_SHA_224 = 37; + public static final byte Signature_ALG_ECDSA_SHA_256 = 33; + public static final byte Signature_ALG_ECDSA_SHA_384 = 34; + public static final byte Signature_ALG_ECDSA_SHA_512 = 38; + + public static final byte[] SIG_TYPES = new byte[]{ + Signature_ALG_ECDSA_SHA, + Signature_ALG_ECDSA_SHA_224, + Signature_ALG_ECDSA_SHA_256, + Signature_ALG_ECDSA_SHA_384, + Signature_ALG_ECDSA_SHA_512 + }; + + public static byte getCurve(short keyLength, byte keyClass) { + if (keyClass == ALG_EC_FP) { + switch (keyLength) { + case (short) 112: + return CURVE_secp112r1; + case (short) 128: + return CURVE_secp128r1; + case (short) 160: + return CURVE_secp160r1; + case (short) 192: + return CURVE_secp192r1; + case (short) 224: + return CURVE_secp224r1; + case (short) 256: + return CURVE_secp256r1; + case (short) 384: + return CURVE_secp384r1; + case (short) 521: + return CURVE_secp521r1; + default: + throw new IllegalArgumentException("Unsupported keyLength and keyClass."); + } + } else if (keyClass == ALG_EC_F2M) { + switch (keyLength) { + case (short) 163: + return CURVE_sect163r1; + case (short) 233: + return CURVE_sect233r1; + case (short) 283: + return CURVE_sect283r1; + case (short) 409: + return CURVE_sect409r1; + case (short) 571: + return CURVE_sect571r1; + default: + throw new IllegalArgumentException("Unsupported keyLength and keyClass."); + } + } else { + throw new IllegalArgumentException("Unsupported keyClass."); + } + } + + public static byte[] getCurveParameter(byte curve, short param) { + byte alg = getCurveType(curve); + switch (curve) { + case CURVE_secp112r1: { + EC_FP_P = EC112_FP_P; + EC_A = EC112_FP_A; + EC_B = EC112_FP_B; + EC_G_X = EC112_FP_G_X; + EC_G_Y = EC112_FP_G_Y; + EC_R = EC112_FP_R; + EC_K = EC112_FP_K; + EC_W_X = null; + EC_W_Y = null; + EC_S = null; + break; + } + case CURVE_secp128r1: { + EC_FP_P = EC128_FP_P; + EC_A = EC128_FP_A; + EC_B = EC128_FP_B; + EC_G_X = EC128_FP_G_X; + EC_G_Y = EC128_FP_G_Y; + EC_R = EC128_FP_R; + EC_K = EC128_FP_K; + EC_W_X = null; + EC_W_Y = null; + EC_S = null; + break; + } + case CURVE_secp160r1: { + EC_FP_P = EC160_FP_P; + EC_A = EC160_FP_A; + EC_B = EC160_FP_B; + EC_G_X = EC160_FP_G_X; + EC_G_Y = EC160_FP_G_Y; + EC_R = EC160_FP_R; + EC_K = EC160_FP_K; + EC_W_X = null; + EC_W_Y = null; + EC_S = null; + break; + } + case CURVE_secp192r1: { + EC_FP_P = EC192_FP_P; + EC_A = EC192_FP_A; + EC_B = EC192_FP_B; + EC_G_X = EC192_FP_G_X; + EC_G_Y = EC192_FP_G_Y; + EC_R = EC192_FP_R; + EC_K = EC192_FP_K; + EC_W_X = null; + EC_W_Y = null; + EC_S = null; + break; + } + case CURVE_secp224r1: { + EC_FP_P = EC224_FP_P; + EC_A = EC224_FP_A; + EC_B = EC224_FP_B; + EC_G_X = EC224_FP_G_X; + EC_G_Y = EC224_FP_G_Y; + EC_R = EC224_FP_R; + EC_K = EC224_FP_K; + EC_S = null; + break; + } + case CURVE_secp256r1: { + EC_FP_P = EC256_FP_P; + EC_A = EC256_FP_A; + EC_B = EC256_FP_B; + EC_G_X = EC256_FP_G_X; + EC_G_Y = EC256_FP_G_Y; + EC_R = EC256_FP_R; + EC_K = EC256_FP_K; + EC_W_X = null; + EC_W_Y = null; + EC_S = null; + break; + } + case CURVE_secp384r1: { + EC_FP_P = EC384_FP_P; + EC_A = EC384_FP_A; + EC_B = EC384_FP_B; + EC_G_X = EC384_FP_G_X; + EC_G_Y = EC384_FP_G_Y; + EC_R = EC384_FP_R; + EC_K = EC384_FP_K; + EC_W_X = null; + EC_W_Y = null; + EC_S = null; + break; + } + case CURVE_secp521r1: { + EC_FP_P = EC521_FP_P; + EC_A = EC521_FP_A; + EC_B = EC521_FP_B; + EC_G_X = EC521_FP_G_X; + EC_G_Y = EC521_FP_G_Y; + EC_R = EC521_FP_R; + EC_K = EC521_FP_K; + EC_W_X = null; + EC_W_Y = null; + EC_S = null; + break; + } + case CURVE_sect163r1: { + EC_F2M_F2M = EC163_F2M_F; + EC_A = EC163_F2M_A; + EC_B = EC163_F2M_B; + EC_G_X = EC163_F2M_G_X; + EC_G_Y = EC163_F2M_G_Y; + EC_R = EC163_F2M_R; + EC_K = EC163_F2M_K; + EC_W_X = null; + EC_W_Y = null; + EC_S = null; + break; + } + case CURVE_sect233r1: { + EC_F2M_F2M = EC233_F2M_F; + EC_A = EC233_F2M_A; + EC_B = EC233_F2M_B; + EC_G_X = EC233_F2M_G_X; + EC_G_Y = EC233_F2M_G_Y; + EC_R = EC233_F2M_R; + EC_K = EC233_F2M_K; + EC_W_X = null; + EC_W_Y = null; + EC_S = null; + break; + } + case CURVE_sect283r1: { + EC_F2M_F2M = EC283_F2M_F; + EC_A = EC283_F2M_A; + EC_B = EC283_F2M_B; + EC_G_X = EC283_F2M_G_X; + EC_G_Y = EC283_F2M_G_Y; + EC_R = EC283_F2M_R; + EC_K = EC283_F2M_K; + EC_W_X = null; + EC_W_Y = null; + EC_S = null; + break; + } + case CURVE_sect409r1: { + EC_F2M_F2M = EC409_F2M_F; + EC_A = EC409_F2M_A; + EC_B = EC409_F2M_B; + EC_G_X = EC409_F2M_G_X; + EC_G_Y = EC409_F2M_G_Y; + EC_R = EC409_F2M_R; + EC_K = EC409_F2M_K; + EC_W_X = null; + EC_W_Y = null; + EC_S = null; + break; + } + case CURVE_sect571r1: { + EC_F2M_F2M = EC571_F2M_F; + EC_A = EC571_F2M_A; + EC_B = EC571_F2M_B; + EC_G_X = EC571_F2M_G_X; + EC_G_Y = EC571_F2M_G_Y; + EC_R = EC571_F2M_R; + EC_K = EC571_F2M_K; + EC_W_X = null; + EC_W_Y = null; + EC_S = null; + break; + } + default: + throw new IllegalArgumentException("Unknown curve."); + } + switch (param) { + case PARAMETER_FP: + if (alg == ALG_EC_FP) { + return EC_FP_P.clone(); + } + break; + case PARAMETER_F2M: + if (alg == ALG_EC_F2M) { + return EC_F2M_F2M.clone(); + } + break; + case PARAMETER_A: + return EC_A.clone(); + case PARAMETER_B: + return EC_B.clone(); + case PARAMETER_G: + return toX962(X962_UNCOMPRESSED, EC_G_X, EC_G_Y); + case PARAMETER_R: + return EC_R.clone(); + case PARAMETER_K: + return ByteUtil.shortToBytes(EC_K); + case PARAMETER_W: + if (EC_W_X == null || EC_W_Y == null) { + return null; + } + return toX962(X962_UNCOMPRESSED, EC_W_X, EC_W_Y); + case PARAMETER_S: + if (EC_S == null) { + return null; + } + return EC_S.clone(); + default: + throw new IllegalArgumentException("Unknown parameter"); + } + return null; + } + + public static byte getCurveType(byte curve) { + return curve <= FP_CURVES ? ALG_EC_FP : ALG_EC_F2M; + } + + public static byte[] toX962(byte form, byte[] xBuffer, byte[] yBuffer) { + ByteBuffer bb = ByteBuffer.allocate(xBuffer.length + yBuffer.length + 1); + byte yLSB = yBuffer[yBuffer.length - 1]; + byte yBit = (byte) (yLSB & 0x01); + + switch (form) { + case X962_UNCOMPRESSED: + bb.put((byte) 4); + break; + case X962_HYBRID: + if (yBit == 1) { + bb.put((byte) 7); + } else { + bb.put((byte) 6); + } + break; + case X962_COMPRESSED: + if (yBit == 1) { + bb.put((byte) 3); + } else { + bb.put((byte) 2); + } + break; + default: + throw new IllegalArgumentException("Unsupported form."); + } + bb.put(xBuffer); + if (form == X962_HYBRID || form == X962_UNCOMPRESSED) { + bb.put(yBuffer); + } + return bb.array(); + } + +} diff --git a/common/src/main/java/cz/crcs/ectester/common/ec/EC_Curve.java b/common/src/main/java/cz/crcs/ectester/common/ec/EC_Curve.java new file mode 100644 index 0000000..e26fc44 --- /dev/null +++ b/common/src/main/java/cz/crcs/ectester/common/ec/EC_Curve.java @@ -0,0 +1,162 @@ +package cz.crcs.ectester.common.ec; + +import cz.crcs.ectester.common.util.ByteUtil; +import org.bouncycastle.math.ec.ECCurve; + +import java.math.BigInteger; +import java.security.spec.*; + +/** + * An Elliptic curve, contains parameters Fp/F2M, A, B, G, R, (K)?. + * + * @author Jan Jancar johny@neuromancer.sk + */ +public class EC_Curve extends EC_Params { + private short bits; + private byte field; + private String desc; + + /** + * @param bits + * @param field EC_Consts.ALG_EC_FP or EC_Consts.ALG_EC_F2M + */ + public EC_Curve(short bits, byte field) { + super(field == EC_Consts.ALG_EC_FP ? EC_Consts.PARAMETERS_DOMAIN_FP : EC_Consts.PARAMETERS_DOMAIN_F2M); + this.bits = bits; + this.field = field; + } + + public EC_Curve(String id, short bits, byte field) { + this(bits, field); + this.id = id; + } + + public EC_Curve(String id, short bits, byte field, String desc) { + this(id, bits, field); + this.desc = desc; + } + + public short getBits() { + return bits; + } + + public byte getField() { + return field; + } + + public String getDesc() { + return desc; + } + + @Override + public String toString() { + return "<" + getId() + "> " + (field == EC_Consts.ALG_EC_FP ? "Prime" : "Binary") + " field Elliptic curve (" + String.valueOf(bits) + "b)" + (desc == null ? "" : ": " + desc) + System.lineSeparator() + super.toString(); + } + + public EllipticCurve toCurve() { + ECField field; + if (this.field == EC_Consts.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; + if (e2 == 0 && e3 == 0) { + powers = new int[]{e1}; + } else { + 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]); + + return new EllipticCurve(field, a, b); + } + + public ECCurve toBCCurve() { + if (this.field == EC_Consts.ALG_EC_FP) { + BigInteger p = new BigInteger(1, getParam(EC_Consts.PARAMETER_FP)[0]); + BigInteger a = new BigInteger(1, getParam(EC_Consts.PARAMETER_A)[0]); + BigInteger b = new BigInteger(1, getParam(EC_Consts.PARAMETER_B)[0]); + BigInteger r = new BigInteger(1, getParam(EC_Consts.PARAMETER_R)[0]); + BigInteger k = new BigInteger(1, getParam(EC_Consts.PARAMETER_K)[0]); + return new ECCurve.Fp(p, a, b, r, k); + } 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); + BigInteger a = new BigInteger(1, getParam(EC_Consts.PARAMETER_A)[0]); + BigInteger b = new BigInteger(1, getParam(EC_Consts.PARAMETER_B)[0]); + BigInteger r = new BigInteger(1, getParam(EC_Consts.PARAMETER_R)[0]); + BigInteger k = new BigInteger(1, getParam(EC_Consts.PARAMETER_K)[0]); + return new ECCurve.F2m(m, e1, e2, e3, a, b, r, k); + } + } + + public ECParameterSpec toSpec() { + EllipticCurve curve = toCurve(); + + 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.getShortInt(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 = EC_Consts.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]; + short power = (i < powers.length) ? (short) powers[i] : 0; + ByteUtil.setShort(params[paramIndex++], 0, power); + } + fieldType = EC_Consts.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/common/src/main/java/cz/crcs/ectester/common/ec/EC_Data.java b/common/src/main/java/cz/crcs/ectester/common/ec/EC_Data.java new file mode 100644 index 0000000..14ae1c5 --- /dev/null +++ b/common/src/main/java/cz/crcs/ectester/common/ec/EC_Data.java @@ -0,0 +1,265 @@ +package cz.crcs.ectester.common.ec; + +import cz.crcs.ectester.common.util.ByteUtil; + +import java.io.*; +import java.util.*; +import java.util.regex.Pattern; + +/** + * A list of byte arrays for holding EC data. + * <p> + * The data can be read from a byte array via <code>readBytes()</code>, from a CSV via <code>readCSV()</code>. + * The data can be exported to a byte array via <code>flatten()</code> or to a string array via <code>expand()</code>. + * + * @author Jan Jancar johny@neuromancer.sk + */ +public abstract class EC_Data implements Comparable<EC_Data> { + String id; + int count; + byte[][] data; + + private static final Pattern HEX = Pattern.compile("(0x|0X)?[a-fA-F\\d]+"); + + EC_Data() { + } + + EC_Data(int count) { + this.count = count; + this.data = new byte[count][]; + } + + EC_Data(byte[][] data) { + this.count = data.length; + this.data = data; + } + + EC_Data(String id, int count) { + this(count); + this.id = id; + } + + EC_Data(String id, byte[][] data) { + this(data); + this.id = id; + } + + public String getId() { + return id; + } + + public int getCount() { + return count; + } + + public byte[][] getData() { + return data; + } + + public byte[] getData(int index) { + return data[index]; + } + + public boolean hasData() { + return data != null; + } + + public byte[] flatten() { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + for (byte[] param : data) { + byte[] length = new byte[2]; + ByteUtil.setShort(length, 0, (short) param.length); + + out.write(length, 0, 2); + out.write(param, 0, param.length); + } + + return out.toByteArray(); + } + + public String[] expand() { + List<String> out = new ArrayList<>(count); + for (byte[] param : data) { + out.add(ByteUtil.bytesToHex(param, false)); + } + + return out.toArray(new String[out.size()]); + } + + private static byte[] pad(byte[] data) { + if (data.length == 1) { + return new byte[]{(byte) 0, data[0]}; + } else if (data.length == 0 || data.length > 2) { + return data; + } + return null; + } + + protected static byte[] parse(String param) { + byte[] data; + if (param.startsWith("0x") || param.startsWith("0X")) { + data = ByteUtil.hexToBytes(param.substring(2)); + } else { + data = ByteUtil.hexToBytes(param); + } + if (data == null) + return new byte[0]; + if (data.length < 2) + return pad(data); + return data; + } + + private boolean readHex(String[] hex) { + if (hex.length != count) { + return false; + } + + for (int i = 0; i < count; ++i) { + this.data[i] = parse(hex[i]); + } + return true; + } + + public boolean readCSV(InputStream in) { + Scanner s = new Scanner(in); + + s.useDelimiter("[,;]"); + List<String> data = new LinkedList<>(); + while (s.hasNext()) { + String field = s.next(); + data.add(field.replaceAll("\\s+", "")); + } + + if (data.isEmpty()) { + return false; + } + for (String param : data) { + if (!HEX.matcher(param).matches()) { + return false; + } + } + return readHex(data.toArray(new String[data.size()])); + } + + public boolean readBytes(byte[] bytes) { + if (bytes == null) { + return false; + } + + int offset = 0; + for (int i = 0; i < count; i++) { + if (bytes.length - offset < 2) { + return false; + } + short paramLength = ByteUtil.getShort(bytes, offset); + offset += 2; + if (bytes.length < offset + paramLength) { + return false; + } + data[i] = new byte[paramLength]; + System.arraycopy(bytes, offset, data[i], 0, paramLength); + offset += paramLength; + } + return true; + } + + public boolean readByteArray(byte[][] bytes) { + if (bytes == null || count != bytes.length) { + return false; + } + + for (int i = 0; i < count; ++i) { + data[i] = new byte[bytes[i].length]; + System.arraycopy(bytes[i], 0, data[i], 0, bytes[i].length); + } + return true; + } + + public void writeCSV(OutputStream out) throws IOException { + Writer w = new OutputStreamWriter(out); + w.write(String.join(",", expand())); + w.flush(); + } + + @Override + public String toString() { + return String.join(",", expand()); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof EC_Data) { + EC_Data other = (EC_Data) obj; + if (this.id != null || other.id != null) { + return Objects.equals(this.id, other.id); + } + + if (this.count != other.count) + return false; + for (int i = 0; i < this.count; ++i) { + if (!Arrays.equals(this.data[i], other.data[i])) { + return false; + } + } + return true; + } else { + return false; + } + } + + @Override + public int hashCode() { + if (this.id != null) { + return this.id.hashCode(); + } + return Arrays.deepHashCode(this.data); + } + + @Override + public int compareTo(EC_Data o) { + if (o == this) return 0; + if (this.id != null && o.id != null) { + + int minLength = Math.min(this.id.length(), o.id.length()); + for (int i = 0; i < minLength; i++) { + if (this.id.charAt(i) != o.id.charAt(i)) { + String thisEnd = this.id.substring(i); + String oEnd = o.id.substring(i); + try { + int thisIndex = Integer.parseInt(thisEnd); + int oIndex = Integer.parseInt(oEnd); + return Integer.compare(thisIndex, oIndex); + } catch (NumberFormatException ignored) { + break; + } + } + } + return this.id.compareTo(o.id); + } else if (this.id == null && o.id == null) { + if (Arrays.equals(this.data, o.data)) { + return 0; + } else { + int minCount = (this.count < o.count) ? this.count : o.count; + for (int i = 0; i < minCount; ++i) { + byte[] thisData = this.data[i]; + byte[] oData = o.data[i]; + int innerMinCount = (thisData.length < oData.length) ? thisData.length : oData.length; + for (int j = 0; j < innerMinCount; ++j) { + if (thisData[j] < oData[j]) { + return -1; + } else if (thisData[j] > oData[j]) { + return 1; + } + } + } + } + } else { + if (this.id == null) { + return -1; + } else { + return 1; + } + } + return 0; + } +} diff --git a/common/src/main/java/cz/crcs/ectester/common/ec/EC_KAResult.java b/common/src/main/java/cz/crcs/ectester/common/ec/EC_KAResult.java new file mode 100644 index 0000000..4e97950 --- /dev/null +++ b/common/src/main/java/cz/crcs/ectester/common/ec/EC_KAResult.java @@ -0,0 +1,65 @@ +package cz.crcs.ectester.common.ec; + +import cz.crcs.ectester.common.util.CardUtil; + +/** + * A result of EC based Key agreement operation. + * + * @author Jan Jancar johny@neuromancer.sk + */ +public class EC_KAResult extends EC_Data { + private String ka; + private String curve; + private String oneKey; + private String otherKey; + + private String desc; + + public EC_KAResult(String ka, String curve, String oneKey, String otherKey) { + super(1); + this.ka = ka; + this.curve = curve; + this.oneKey = oneKey; + this.otherKey = otherKey; + } + + public EC_KAResult(String id, String ka, String curve, String oneKey, String otherKey) { + this(ka, curve, oneKey, otherKey); + this.id = id; + } + + public EC_KAResult(String id, String ka, String curve, String oneKey, String otherKey, String desc) { + this(id, ka, curve, oneKey, otherKey); + this.desc = desc; + } + + public String getKA() { + return ka; + } + + public byte getJavaCardKA() { + return CardUtil.getKA(ka); + } + + public String getCurve() { + return curve; + } + + public String getOneKey() { + return oneKey; + } + + public String getOtherKey() { + return otherKey; + } + + public String getDesc() { + return desc; + } + + @Override + public String toString() { + return "<" + getId() + "> " + ka + " result over " + curve + ", " + oneKey + " + " + otherKey + (desc == null ? "" : ": " + desc) + System.lineSeparator() + super.toString(); + } + +} diff --git a/common/src/main/java/cz/crcs/ectester/common/ec/EC_Key.java b/common/src/main/java/cz/crcs/ectester/common/ec/EC_Key.java new file mode 100644 index 0000000..a9f0c40 --- /dev/null +++ b/common/src/main/java/cz/crcs/ectester/common/ec/EC_Key.java @@ -0,0 +1,81 @@ +package cz.crcs.ectester.common.ec; + +/** + * An abstract-like EC key. Concrete implementations create a public and private keys. + * + * @author Jan Jancar johny@neuromancer.sk + */ +public class EC_Key extends EC_Params { + + private String curve; + private String desc; + + private EC_Key(short mask, String curve) { + super(mask); + this.curve = curve; + } + + private EC_Key(short mask, String curve, String desc) { + this(mask, curve); + this.desc = desc; + } + + private EC_Key(String id, short mask, String curve, String desc) { + this(mask, curve, desc); + this.id = id; + } + + public String getCurve() { + return curve; + } + + public String getDesc() { + return desc; + } + + /** + * An EC public key, contains the W parameter. + */ + public static class Public extends EC_Key { + + public Public(String curve) { + super(EC_Consts.PARAMETER_W, curve); + } + + public Public(String curve, String desc) { + super(EC_Consts.PARAMETER_W, curve, desc); + } + + public Public(String id, String curve, String desc) { + super(id, EC_Consts.PARAMETER_W, curve, desc); + } + + @Override + public String toString() { + return "<" + getId() + "> EC Public key, over " + getCurve() + (getDesc() == null ? "" : ": " + getDesc()) + System.lineSeparator() + super.toString(); + } + } + + /** + * An EC private key, contains the S parameter. + */ + public static class Private extends EC_Key { + + public Private(String curve) { + super(EC_Consts.PARAMETER_S, curve); + } + + public Private(String curve, String desc) { + super(EC_Consts.PARAMETER_S, curve, desc); + } + + public Private(String id, String curve, String desc) { + super(id, EC_Consts.PARAMETER_S, curve, desc); + } + + @Override + public String toString() { + return "<" + getId() + "> EC Private key, over " + getCurve() + (getDesc() == null ? "" : ": " + getDesc()) + System.lineSeparator() + super.toString(); + } + } +} diff --git a/common/src/main/java/cz/crcs/ectester/common/ec/EC_Keypair.java b/common/src/main/java/cz/crcs/ectester/common/ec/EC_Keypair.java new file mode 100644 index 0000000..b1a0cbc --- /dev/null +++ b/common/src/main/java/cz/crcs/ectester/common/ec/EC_Keypair.java @@ -0,0 +1,39 @@ +package cz.crcs.ectester.common.ec; + +/** + * An EC keypair, contains both the W and S parameters. + * + * @author Jan Jancar johny@neuromancer.sk + */ +public class EC_Keypair extends EC_Params { + private String curve; + private String desc; + + public EC_Keypair(String curve) { + super(EC_Consts.PARAMETERS_KEYPAIR); + this.curve = curve; + } + + public EC_Keypair(String curve, String desc) { + this(curve); + this.desc = desc; + } + + public EC_Keypair(String id, String curve, String desc) { + this(curve, desc); + this.id = id; + } + + public String getCurve() { + return curve; + } + + public String getDesc() { + return desc; + } + + @Override + public String toString() { + return "<" + getId() + "> EC Keypair, over " + curve + (desc == null ? "" : ": " + desc) + System.lineSeparator() + super.toString(); + } +} diff --git a/common/src/main/java/cz/crcs/ectester/common/ec/EC_Params.java b/common/src/main/java/cz/crcs/ectester/common/ec/EC_Params.java new file mode 100644 index 0000000..146c8d6 --- /dev/null +++ b/common/src/main/java/cz/crcs/ectester/common/ec/EC_Params.java @@ -0,0 +1,233 @@ +package cz.crcs.ectester.common.ec; + +import cz.crcs.ectester.common.util.ByteUtil; + +import java.io.ByteArrayOutputStream; +import java.util.ArrayList; +import java.util.List; + +/** + * A list of EC parameters, can contain a subset of the Fp/F2M, A, B, G, R, K, W, S parameters. + * <p> + * The set of parameters is uniquely identified by a short bit string. + * The parameters can be exported to a byte array via <code>flatten()</code> or to a comma delimited + * string via <code>expand()</code>. + * + * @author Jan Jancar johny@neuromancer.sk + */ +public class EC_Params extends EC_Data { + private short params; + + public EC_Params(short params) { + this.params = params; + this.count = numParams(); + this.data = new byte[this.count][]; + } + + public EC_Params(short params, byte[][] data) { + this.params = params; + this.count = data.length; + this.data = data; + } + + public EC_Params(String id, short params) { + this(params); + this.id = id; + } + + public EC_Params(String id, short params, byte[][] data) { + this(params, data); + this.id = id; + } + + public short getParams() { + return params; + } + + public byte[][] getParam(short param) { + if (!hasParam(param)) { + return null; + } + if (Integer.bitCount(param) != 1) { + return null; + } + short paramMask = EC_Consts.PARAMETER_FP; + byte[][] result = null; + int i = 0; + while (paramMask <= EC_Consts.PARAMETER_S) { + short masked = (short) (this.params & param & paramMask); + short shallow = (short) (this.params & paramMask); + if (masked != 0) { + if (masked == EC_Consts.PARAMETER_F2M) { + result = new byte[4][]; + result[0] = data[i].clone(); + result[1] = data[i + 1].clone(); + result[2] = data[i + 2].clone(); + result[3] = data[i + 3].clone(); + break; + } + if (masked == EC_Consts.PARAMETER_G || masked == EC_Consts.PARAMETER_W) { + result = new byte[2][]; + result[0] = data[i].clone(); + result[1] = data[i + 1].clone(); + break; + } + result = new byte[1][]; + result[0] = data[i].clone(); + } + if (shallow == EC_Consts.PARAMETER_F2M) { + i += 4; + } else if (shallow == EC_Consts.PARAMETER_G || shallow == EC_Consts.PARAMETER_W) { + i += 2; + } else if (shallow != 0) { + i++; + } + paramMask = (short) (paramMask << 1); + } + return result; + } + + public boolean setParam(short param, byte[][] value) { + if (!hasParam(param)) { + return false; + } + if (Integer.bitCount(param) != 1) { + return false; + } + short paramMask = EC_Consts.PARAMETER_FP; + int i = 0; + while (paramMask <= EC_Consts.PARAMETER_S) { + short masked = (short) (this.params & param & paramMask); + short shallow = (short) (this.params & paramMask); + if (masked != 0) { + if (masked == EC_Consts.PARAMETER_F2M) { + data[i] = value[0]; + data[i + 1] = value[1]; + data[i + 2] = value[2]; + data[i + 3] = value[3]; + break; + } + if (masked == EC_Consts.PARAMETER_G || masked == EC_Consts.PARAMETER_W) { + data[i] = value[0]; + data[i + 1] = value[1]; + break; + } + data[i] = value[0]; + } + if (shallow == EC_Consts.PARAMETER_F2M) { + i += 4; + } else if (shallow == EC_Consts.PARAMETER_G || shallow == EC_Consts.PARAMETER_W) { + i += 2; + } else if (shallow != 0) { + i++; + } + paramMask = (short) (paramMask << 1); + } + return true; + } + + public boolean hasParam(short param) { + return (params & param) != 0; + } + + public int numParams() { + short paramMask = EC_Consts.PARAMETER_FP; + int num = 0; + while (paramMask <= EC_Consts.PARAMETER_S) { + if ((paramMask & params) != 0) { + if (paramMask == EC_Consts.PARAMETER_F2M) { + num += 3; + } + if (paramMask == EC_Consts.PARAMETER_W || paramMask == EC_Consts.PARAMETER_G) { + num += 1; + } + ++num; + } + paramMask = (short) (paramMask << 1); + } + return num; + } + + @Override + public byte[] flatten() { + return flatten(params); + } + + public byte[] flatten(short params) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + short paramMask = EC_Consts.PARAMETER_FP; + int i = 0; + while (paramMask <= EC_Consts.PARAMETER_S) { + short masked = (short) (this.params & params & paramMask); + short shallow = (short) (this.params & paramMask); + if (masked != 0) { + byte[] param = data[i]; + if (masked == EC_Consts.PARAMETER_F2M) { + //add m, e_1, e_2, e_3 + param = ByteUtil.concatenate(param, data[i + 1]); + if (!ByteUtil.allValue(data[i + 2], (byte) 0)) { + param = ByteUtil.concatenate(param, data[i + 2]); + } + if (!ByteUtil.allValue(data[i + 3], (byte) 0)) { + param = ByteUtil.concatenate(param, data[i + 3]); + } + if (!(param.length == 4 || param.length == 8)) + throw new RuntimeException("PARAMETER_F2M length is not 8.(should be)"); + } + if (masked == EC_Consts.PARAMETER_G || masked == EC_Consts.PARAMETER_W) { + //read another param (the y coord) and put into X962 format. + byte[] y = data[i + 1]; + param = ByteUtil.concatenate(new byte[]{4}, param, y); //<- ugly but works! + } + if (param.length == 0) + throw new RuntimeException("Empty parameter read?"); + + //write length + byte[] length = new byte[2]; + ByteUtil.setShort(length, 0, (short) param.length); + out.write(length, 0, 2); + //write data + out.write(param, 0, param.length); + } + if (shallow == EC_Consts.PARAMETER_F2M) { + i += 4; + } else if (shallow == EC_Consts.PARAMETER_G || shallow == EC_Consts.PARAMETER_W) { + i += 2; + } else if (shallow != 0) { + i++; + } + paramMask = (short) (paramMask << 1); + } + + return (out.size() == 0) ? null : out.toByteArray(); + } + + @Override + public String[] expand() { + List<String> out = new ArrayList<>(); + + short paramMask = EC_Consts.PARAMETER_FP; + int index = 0; + while (paramMask <= EC_Consts.PARAMETER_S) { + short masked = (short) (params & paramMask); + if (masked != 0) { + byte[] param = data[index]; + if (masked == EC_Consts.PARAMETER_F2M) { + for (int i = 0; i < 4; ++i) { + out.add(ByteUtil.bytesToHex(data[index + i], false)); + } + index += 4; + } else if (masked == EC_Consts.PARAMETER_G || masked == EC_Consts.PARAMETER_W) { + out.add(ByteUtil.bytesToHex(param, false)); + out.add(ByteUtil.bytesToHex(data[index + 1], false)); + index += 2; + } else { + out.add(ByteUtil.bytesToHex(param, false)); + index++; + } + } + paramMask = (short) (paramMask << 1); + } + return out.toArray(new String[0]); + } +} diff --git a/common/src/main/java/cz/crcs/ectester/common/ec/EC_SigResult.java b/common/src/main/java/cz/crcs/ectester/common/ec/EC_SigResult.java new file mode 100644 index 0000000..d97ced1 --- /dev/null +++ b/common/src/main/java/cz/crcs/ectester/common/ec/EC_SigResult.java @@ -0,0 +1,75 @@ +package cz.crcs.ectester.common.ec; + +import cz.crcs.ectester.common.util.CardUtil; + +/** + * A result of EC based Signature operation. + * + * @author Jan Jancar johny@neuromancer.sk + */ +public class EC_SigResult extends EC_Data { + private String sig; + private String curve; + private String signKey; + private String verifyKey; + + private String data; + private String desc; + + public EC_SigResult(String sig, String curve, String signKey, String verifyKey, String raw) { + super(1); + this.sig = sig; + this.curve = curve; + this.signKey = signKey; + this.verifyKey = verifyKey; + this.data = raw; + } + + public EC_SigResult(String id, String sig, String curve, String signKey, String verifyKey, String data) { + this(sig, curve, signKey, verifyKey, data); + this.id = id; + } + + public EC_SigResult(String id, String sig, String curve, String signKey, String verifyKey, String data, String desc) { + this(id, sig, curve, signKey, verifyKey, data); + this.desc = desc; + } + + public String getSig() { + return sig; + } + + public byte getJavaCardSig() { + return CardUtil.getSig(sig); + } + + public String getCurve() { + return curve; + } + + public String getSignKey() { + return signKey; + } + + public String getVerifyKey() { + return verifyKey; + } + + public byte[] getSigData() { + if (data == null) { + return null; + } else { + return parse(data); + } + } + + public String getDesc() { + return desc; + } + + @Override + public String toString() { + return "<" + getId() + "> " + sig + " result over " + curve + ", " + signKey + " + " + verifyKey + (data == null ? "" : " of data \"" + data + "\"") + (desc == null ? "" : ": " + desc) + System.lineSeparator() + super.toString(); + } + +} diff --git a/common/src/main/java/cz/crcs/ectester/common/ec/RawECPrivateKey.java b/common/src/main/java/cz/crcs/ectester/common/ec/RawECPrivateKey.java new file mode 100644 index 0000000..479118f --- /dev/null +++ b/common/src/main/java/cz/crcs/ectester/common/ec/RawECPrivateKey.java @@ -0,0 +1,46 @@ +package cz.crcs.ectester.common.ec; + +import cz.crcs.ectester.common.util.ECUtil; + +import java.math.BigInteger; +import java.security.interfaces.ECPrivateKey; +import java.security.spec.ECParameterSpec; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +@SuppressWarnings("serial") +public class RawECPrivateKey implements ECPrivateKey { + private BigInteger scalar; + private ECParameterSpec params; + + public RawECPrivateKey(BigInteger scalar, ECParameterSpec params) { + this.scalar = scalar; + this.params = params; + } + + @Override + public BigInteger getS() { + return scalar; + } + + @Override + public String getAlgorithm() { + return "EC"; + } + + @Override + public String getFormat() { + return "Raw"; + } + + @Override + public byte[] getEncoded() { + return ECUtil.toByteArray(scalar, params.getOrder().bitLength()); + } + + @Override + public ECParameterSpec getParams() { + return params; + } +} diff --git a/common/src/main/java/cz/crcs/ectester/common/ec/RawECPublicKey.java b/common/src/main/java/cz/crcs/ectester/common/ec/RawECPublicKey.java new file mode 100644 index 0000000..7888854 --- /dev/null +++ b/common/src/main/java/cz/crcs/ectester/common/ec/RawECPublicKey.java @@ -0,0 +1,46 @@ +package cz.crcs.ectester.common.ec; + +import cz.crcs.ectester.common.util.ECUtil; + +import java.security.interfaces.ECPublicKey; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPoint; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +@SuppressWarnings("serial") +public class RawECPublicKey implements ECPublicKey { + private ECPoint point; + private ECParameterSpec params; + + public RawECPublicKey(ECPoint point, ECParameterSpec params) { + this.point = point; + this.params = params; + } + + @Override + public ECPoint getW() { + return point; + } + + @Override + public String getAlgorithm() { + return "EC"; + } + + @Override + public String getFormat() { + return "Raw"; + } + + @Override + public byte[] getEncoded() { + return ECUtil.toX962Uncompressed(point, params); + } + + @Override + public ECParameterSpec getParams() { + return params; + } +} diff --git a/common/src/main/java/cz/crcs/ectester/common/output/BaseTextTestWriter.java b/common/src/main/java/cz/crcs/ectester/common/output/BaseTextTestWriter.java new file mode 100644 index 0000000..5c449db --- /dev/null +++ b/common/src/main/java/cz/crcs/ectester/common/output/BaseTextTestWriter.java @@ -0,0 +1,149 @@ +package cz.crcs.ectester.common.output; + +import cz.crcs.ectester.common.cli.Colors; +import cz.crcs.ectester.common.test.*; + +import java.io.PrintStream; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * An absctract basis of a TextTestWriter, which outputs in a human readable format, into console. + * Requires the implementation of: + * <code>String testableString(Testable t)</code> + * <code>String deviceString(TestSuite t)</code> + * + * @author Jan Jancar johny@neuromancer.sk + */ +public abstract class BaseTextTestWriter implements TestWriter { + private PrintStream output; + + public static int BASE_WIDTH = 105; + + public BaseTextTestWriter(PrintStream output) { + this.output = output; + } + + @Override + public void begin(TestSuite suite) { + output.println("═══ " + Colors.underline("Running test suite:") + " " + Colors.bold(suite.getName()) + " ═══"); + for (String d : suite.getDescription()) { + output.println("═══ " + d); + } + DateFormat dateFormat = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss"); + Date date = new Date(); + output.println("═══ " + Colors.underline("Date:") + " " + dateFormat.format(date)); + output.print(deviceString(suite)); + } + + /** + * @param t + * @return + */ + protected abstract String testableString(Testable t); + + /** + * @param suite + * @return + */ + protected abstract String deviceString(TestSuite suite); + + private String testString(Test t, String prefix, int index) { + boolean compound = t instanceof CompoundTest; + + Result result = t.getResult(); + + String line = ""; + if (prefix.equals("")) { + char[] charLine = new char[BASE_WIDTH + 24]; + new String(new char[BASE_WIDTH + 24]).replace("\0", "━").getChars(0, charLine.length - 1, charLine, 0); + charLine[0] = '■'; + charLine[4] = '┳'; + charLine[BASE_WIDTH + 1] = '┳'; + charLine[BASE_WIDTH + 13] = '┳'; + charLine[BASE_WIDTH + 23] = '┓'; + line = new String(charLine) + System.lineSeparator(); + } + + StringBuilder out = new StringBuilder(); + out.append(t.ok() ? Colors.ok(" OK ") : Colors.error("NOK ")); + out.append(compound ? (prefix.equals("") ? "╋ " : "┳ ") : "━ "); + int width = BASE_WIDTH - (prefix.length() + 6); + String widthSpec = "%-" + width + "s"; + String desc = ((prefix.equals("")) ? "(" + index + ") " : "") + t.getDescription(); + out.append(String.format(widthSpec, desc)); + out.append(" ┃ "); + Colors.Foreground valueColor; + if (result.getValue().ok()) { + valueColor = Colors.Foreground.GREEN; + } else if (result.getValue().equals(Result.Value.ERROR)) { + valueColor = Colors.Foreground.RED; + } else { + valueColor = Colors.Foreground.YELLOW; + } + out.append(Colors.colored(String.format("%-9s", result.getValue().name()), Colors.Attribute.BOLD, valueColor)); + out.append(" ┃ "); + + if (compound) { + CompoundTest test = (CompoundTest) t; + out.append(result.getCause()); + out.append(System.lineSeparator()); + Test[] tests = test.getStartedTests(); + for (int i = 0; i < tests.length; ++i) { + if (i == tests.length - 1) { + out.append(prefix).append(" ┗ "); + out.append(testString(tests[i], prefix + " ", index)); + } else { + out.append(prefix).append(" ┣ "); + out.append(testString(tests[i], prefix + " ┃ ", index)); + } + + if (i != tests.length - 1) { + out.append(System.lineSeparator()); + } + } + } else { + SimpleTest<? extends BaseTestable> test = (SimpleTest<? extends BaseTestable>) t; + out.append(testableString(test.getTestable())); + if (t.getResult().getCause() != null) { + out.append(" ┃ ").append(t.getResult().getCause().toString()); + } + } + return line + out.toString(); + } + + @Override + public void outputTest(Test t, int index) { + if (!t.hasRun()) + return; + output.println(testString(t, "", index)); + output.flush(); + } + + private String errorString(Throwable error) { + StringBuilder sb = new StringBuilder(); + sb.append("═══ Exception: ═══").append(System.lineSeparator()); + for (Throwable t = error; t != null; t = t.getCause()) { + sb.append("═══ ").append(t.toString()).append(" ═══"); + sb.append(System.lineSeparator()); + } + sb.append("═══ Stack trace: ═══").append(System.lineSeparator()); + for (StackTraceElement s : error.getStackTrace()) { + sb.append("═══ ").append(s.toString()).append(" ═══"); + sb.append(System.lineSeparator()); + } + return sb.toString(); + } + + @Override + public void outputError(Test t, Throwable cause, int index) { + output.println(testString(t, "", index)); + output.print(errorString(cause)); + output.flush(); + } + + @Override + public void end() { + } +} diff --git a/common/src/main/java/cz/crcs/ectester/common/output/BaseXMLTestWriter.java b/common/src/main/java/cz/crcs/ectester/common/output/BaseXMLTestWriter.java new file mode 100644 index 0000000..53970dd --- /dev/null +++ b/common/src/main/java/cz/crcs/ectester/common/output/BaseXMLTestWriter.java @@ -0,0 +1,144 @@ +package cz.crcs.ectester.common.output; + +import cz.crcs.ectester.common.test.*; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import java.io.OutputStream; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public abstract class BaseXMLTestWriter implements TestWriter { + private OutputStream output; + private DocumentBuilder db; + protected Document doc; + private Node root; + private Node tests; + + public BaseXMLTestWriter(OutputStream output) throws ParserConfigurationException { + this.output = output; + this.db = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + } + + @Override + public void begin(TestSuite suite) { + doc = db.newDocument(); + Element rootElem = doc.createElement("testSuite"); + rootElem.setAttribute("name", suite.getName()); + rootElem.setAttribute("desc", suite.getTextDescription()); + DateFormat dateFormat = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss"); + Date date = new Date(); + rootElem.setAttribute("date", dateFormat.format(date)); + + root = rootElem; + doc.appendChild(root); + root.appendChild(deviceElement(suite)); + tests = doc.createElement("tests"); + root.appendChild(tests); + } + + protected abstract Element testableElement(Testable t); + + protected abstract Element deviceElement(TestSuite suite); + + private String causeString(Object cause) { + if (cause == null) { + return "null"; + } else if (cause instanceof Throwable) { + StringBuilder sb = new StringBuilder(); + for (Throwable t = (Throwable) cause; t != null; t = t.getCause()) { + sb.append(t.toString()); + sb.append(System.lineSeparator()); + } + return sb.toString(); + } else { + return cause.toString(); + } + } + + private Element resultElement(Result result) { + Element resultElem = doc.createElement("result"); + + Element ok = doc.createElement("ok"); + ok.setTextContent(String.valueOf(result.ok())); + Element value = doc.createElement("value"); + value.setTextContent(result.getValue().name()); + Element cause = doc.createElement("cause"); + cause.setTextContent(causeString(result.getCause())); + + resultElem.appendChild(ok); + resultElem.appendChild(value); + resultElem.appendChild(cause); + + return resultElem; + } + + private Element testElement(Test t, int index) { + Element testElem; + if (t instanceof CompoundTest) { + CompoundTest test = (CompoundTest) t; + testElem = doc.createElement("test"); + testElem.setAttribute("type", "compound"); + for (Test innerTest : test.getStartedTests()) { + testElem.appendChild(testElement(innerTest, -1)); + } + } else { + SimpleTest<? extends BaseTestable> test = (SimpleTest<? extends BaseTestable>) t; + testElem = testableElement(test.getTestable()); + } + + Element description = doc.createElement("desc"); + description.setTextContent(t.getDescription()); + testElem.appendChild(description); + + Element result = resultElement(t.getResult()); + testElem.appendChild(result); + + if (index != -1) { + testElem.setAttribute("index", String.valueOf(index)); + } + + return testElem; + } + + @Override + public void outputTest(Test t, int index) { + if (!t.hasRun()) + return; + tests.appendChild(testElement(t, index)); + } + + @Override + public void outputError(Test t, Throwable cause, int index) { + tests.appendChild(testElement(t, index)); + } + + @Override + public void end() { + try { + DOMSource domSource = new DOMSource(doc); + StreamResult result = new StreamResult(output); + TransformerFactory tf = TransformerFactory.newInstance(); + Transformer transformer = tf.newTransformer(); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); + transformer.transform(domSource, result); + } catch (TransformerException e) { + e.printStackTrace(); + } + } +} diff --git a/common/src/main/java/cz/crcs/ectester/common/output/BaseYAMLTestWriter.java b/common/src/main/java/cz/crcs/ectester/common/output/BaseYAMLTestWriter.java new file mode 100644 index 0000000..e054563 --- /dev/null +++ b/common/src/main/java/cz/crcs/ectester/common/output/BaseYAMLTestWriter.java @@ -0,0 +1,120 @@ +package cz.crcs.ectester.common.output; + +import cz.crcs.ectester.common.test.*; +import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.Yaml; + +import java.io.PrintStream; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.*; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public abstract class BaseYAMLTestWriter implements TestWriter { + private PrintStream output; + private Map<String, Object> testRun; + private Map<String, String> testSuite; + protected List<Object> tests; + + public BaseYAMLTestWriter(PrintStream output) { + this.output = output; + } + + @Override + public void begin(TestSuite suite) { + output.println("---"); + testRun = new LinkedHashMap<>(); + testSuite = new LinkedHashMap<>(); + tests = new LinkedList<>(); + testSuite.put("name", suite.getName()); + testSuite.put("desc", suite.getTextDescription()); + + DateFormat dateFormat = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss"); + Date date = new Date(); + testRun.put("date", dateFormat.format(date)); + testRun.put("suite", testSuite); + testRun.put("device", deviceObject(suite)); + testRun.put("tests", tests); + } + + abstract protected Map<String, Object> testableObject(Testable t); + + abstract protected Map<String, Object> deviceObject(TestSuite suite); + + private Object causeObject(Object cause) { + if (cause == null) { + return null; + } else if (cause instanceof Throwable) { + StringBuilder sb = new StringBuilder(); + for (Throwable t = (Throwable) cause; t != null; t = t.getCause()) { + sb.append(t.toString()); + sb.append(System.lineSeparator()); + } + return sb.toString(); + } else { + return cause.toString(); + } + } + + private Map<String, Object> resultObject(Result result) { + Map<String, Object> resultObject = new LinkedHashMap<>(); + resultObject.put("ok", result.ok()); + resultObject.put("value", result.getValue().name()); + resultObject.put("cause", causeObject(result.getCause())); + return resultObject; + } + + private Map<String, Object> testObject(Test t, int index) { + Map<String, Object> testObj; + if (t instanceof CompoundTest) { + CompoundTest test = (CompoundTest) t; + testObj = new HashMap<>(); + testObj.put("type", "compound"); + List<Map<String, Object>> innerTests = new LinkedList<>(); + for (Test innerTest : test.getStartedTests()) { + innerTests.add(testObject(innerTest, -1)); + } + testObj.put("tests", innerTests); + } else { + SimpleTest<? extends BaseTestable> test = (SimpleTest<? extends BaseTestable>) t; + testObj = testableObject(test.getTestable()); + } + + testObj.put("desc", t.getDescription()); + testObj.put("result", resultObject(t.getResult())); + if (index != -1) { + testObj.put("index", index); + } + + return testObj; + } + + @Override + public void outputTest(Test t, int index) { + if (!t.hasRun()) + return; + tests.add(testObject(t, index)); + } + + @Override + public void outputError(Test t, Throwable cause, int index) { + tests.add(testObject(t, index)); + } + + @Override + public void end() { + DumperOptions options = new DumperOptions(); + options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); + options.setPrettyFlow(true); + Yaml yaml = new Yaml(options); + + Map<String, Object> result = new LinkedHashMap<>(); + result.put("testRun", testRun); + String out = yaml.dump(result); + + output.println(out); + output.println("---"); + } +} diff --git a/common/src/main/java/cz/crcs/ectester/common/output/OutputLogger.java b/common/src/main/java/cz/crcs/ectester/common/output/OutputLogger.java new file mode 100644 index 0000000..effd1fd --- /dev/null +++ b/common/src/main/java/cz/crcs/ectester/common/output/OutputLogger.java @@ -0,0 +1,63 @@ +package cz.crcs.ectester.common.output; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; +import java.util.LinkedList; +import java.util.List; + +/** + * @author Petr Svenda petr@svenda.com + * @author Jan Jancar johny@neuromancer.sk + */ +public class OutputLogger { + private OutputStream out; + private PrintStream print; + + public OutputLogger(boolean systemOut, String... filePaths) throws IOException { + List<OutputStream> streams = new LinkedList<>(); + for (String filePath : filePaths) { + if (filePath != null) { + streams.add(new FileOutputStream(filePath)); + } + } + if (systemOut) { + streams.add(System.out); + } + this.out = new TeeOutputStream(streams.toArray(new OutputStream[0])); + this.print = new PrintStream(this.out); + } + + public OutputLogger(String filePath) throws IOException { + this(true, filePath); + } + + public OutputStream getOutputStream() { + return this.out; + } + + public PrintStream getPrintStream() { + return this.print; + } + + public void println() { + print.println(); + } + + public void println(String logLine) { + print.println(logLine); + } + + public void print(String logLine) { + print.print(logLine); + } + + public void flush() { + print.flush(); + } + + public void close() { + print.close(); + } +} diff --git a/common/src/main/java/cz/crcs/ectester/common/output/TeeOutputStream.java b/common/src/main/java/cz/crcs/ectester/common/output/TeeOutputStream.java new file mode 100644 index 0000000..2401fce --- /dev/null +++ b/common/src/main/java/cz/crcs/ectester/common/output/TeeOutputStream.java @@ -0,0 +1,36 @@ +package cz.crcs.ectester.common.output; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class TeeOutputStream extends OutputStream { + private OutputStream[] outputs; + + public TeeOutputStream(OutputStream... outputs) { + this.outputs = outputs; + } + + @Override + public void write(int b) throws IOException { + for (OutputStream out : outputs) { + out.write(b); + } + } + + @Override + public void flush() throws IOException { + for (OutputStream out : outputs) { + out.flush(); + } + } + + @Override + public void close() throws IOException { + for (OutputStream out : outputs) { + out.close(); + } + } +} diff --git a/common/src/main/java/cz/crcs/ectester/common/output/TeeTestWriter.java b/common/src/main/java/cz/crcs/ectester/common/output/TeeTestWriter.java new file mode 100644 index 0000000..58a0a15 --- /dev/null +++ b/common/src/main/java/cz/crcs/ectester/common/output/TeeTestWriter.java @@ -0,0 +1,43 @@ +package cz.crcs.ectester.common.output; + +import cz.crcs.ectester.common.test.Test; +import cz.crcs.ectester.common.test.TestSuite; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class TeeTestWriter implements TestWriter { + protected TestWriter[] writers; + + public TeeTestWriter(TestWriter... writers) { + this.writers = writers; + } + + @Override + public void begin(TestSuite suite) { + for (TestWriter writer : writers) { + writer.begin(suite); + } + } + + @Override + public void outputTest(Test t, int index) { + for (TestWriter writer : writers) { + writer.outputTest(t, index); + } + } + + @Override + public void outputError(Test t, Throwable cause, int index) { + for (TestWriter writer : writers) { + writer.outputError(t, cause, index); + } + } + + @Override + public void end() { + for (TestWriter writer : writers) { + writer.end(); + } + } +} diff --git a/common/src/main/java/cz/crcs/ectester/common/output/TestWriter.java b/common/src/main/java/cz/crcs/ectester/common/output/TestWriter.java new file mode 100644 index 0000000..67aeccb --- /dev/null +++ b/common/src/main/java/cz/crcs/ectester/common/output/TestWriter.java @@ -0,0 +1,38 @@ +package cz.crcs.ectester.common.output; + +import cz.crcs.ectester.common.test.Test; +import cz.crcs.ectester.common.test.TestSuite; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public interface TestWriter { + /** + * Begin writing the <code>TestSuite suite</code>. + * This should reset all the internal state of the writer + * and prepare it to output tests from <code>suite</code>. + * It may also write any header part of the output of the + * writer but doesn't have to. + * + * @param suite The <code>TestSuite</code> to start writing. + */ + void begin(TestSuite suite); + + /** + * @param t + * @param index + */ + void outputTest(Test t, int index); + + /** + * @param t + * @param cause + * @param index + */ + void outputError(Test t, Throwable cause, int index); + + /** + * + */ + void end(); +} diff --git a/common/src/main/java/cz/crcs/ectester/common/test/BaseTestable.java b/common/src/main/java/cz/crcs/ectester/common/test/BaseTestable.java new file mode 100644 index 0000000..3c304d9 --- /dev/null +++ b/common/src/main/java/cz/crcs/ectester/common/test/BaseTestable.java @@ -0,0 +1,44 @@ +package cz.crcs.ectester.common.test; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public abstract class BaseTestable implements Testable, Cloneable { + protected boolean hasRun; + protected boolean ok; + protected boolean error; + protected Object errorCause; + + @Override + public boolean hasRun() { + return hasRun; + } + + @Override + public boolean ok() { + return ok; + } + + @Override + public boolean error() { + return error; + } + + @Override + public Object errorCause() { + return errorCause; + } + + @Override + public void reset() { + hasRun = false; + ok = false; + error = false; + errorCause = null; + } + + @Override + protected BaseTestable clone() throws CloneNotSupportedException { + return (BaseTestable) super.clone(); + } +} diff --git a/common/src/main/java/cz/crcs/ectester/common/test/CompoundTest.java b/common/src/main/java/cz/crcs/ectester/common/test/CompoundTest.java new file mode 100644 index 0000000..ba4ad4f --- /dev/null +++ b/common/src/main/java/cz/crcs/ectester/common/test/CompoundTest.java @@ -0,0 +1,214 @@ +package cz.crcs.ectester.common.test; + +import java.util.Arrays; +import java.util.Objects; +import java.util.function.Consumer; +import java.util.function.Function; + +/** + * A compound test that runs many Tests and has a Result dependent on all/some of their Results. + * + * @author Jan Jancar johny@neuromancer.sk + */ +public class CompoundTest extends Test implements Cloneable { + private Function<Test[], Result> resultCallback; + private Consumer<Test[]> runCallback; + private Test[] tests; + private String description = ""; + + private final static Consumer<Test[]> RUN_ALL = tests -> { + for (Test t : tests) { + t.run(); + } + }; + + private final static Consumer<Test[]> RUN_GREEDY_ALL = tests -> { + for (Test t : tests) { + t.run(); + if (!t.ok()) { + break; + } + } + }; + + private final static Consumer<Test[]> RUN_GREEDY_ANY = tests -> { + for (Test t : tests) { + t.run(); + if (t.ok()) { + break; + } + } + }; + + private CompoundTest(Function<Test[], Result> resultCallback, Consumer<Test[]> runCallback, Test... tests) { + this.resultCallback = resultCallback; + this.runCallback = runCallback; + this.tests = Arrays.stream(tests).filter(Objects::nonNull).toArray(Test[]::new); + } + + private CompoundTest(Function<Test[], Result> callback, Consumer<Test[]> runCallback, String descripiton, Test... tests) { + this(callback, runCallback, tests); + this.description = descripiton; + } + + public static CompoundTest function(Function<Test[], Result> callback, Test... tests) { + return new CompoundTest(callback, RUN_ALL, tests); + } + + public static CompoundTest function(Function<Test[], Result> callback, Consumer<Test[]> runCallback, Test... tests) { + return new CompoundTest(callback, runCallback, tests); + } + + public static CompoundTest function(Function<Test[], Result> callback, String description, Test... tests) { + return new CompoundTest(callback, RUN_ALL, description, tests); + } + + public static CompoundTest function(Function<Test[], Result> callback, Consumer<Test[]> runCallback, String description, Test... tests) { + return new CompoundTest(callback, runCallback, description, tests); + } + + private static CompoundTest expectAll(Result.ExpectedValue what, Consumer<Test[]> runCallback, Test[] all) { + return new CompoundTest((tests) -> { + for (Test test : tests) { + if (!Result.Value.fromExpected(what, test.ok()).ok()) { + return new Result(Result.Value.FAILURE, "Some sub-tests did not have the expected result."); + } + } + return new Result(Result.Value.SUCCESS, "All sub-tests had the expected result."); + }, runCallback, all); + } + + public static CompoundTest all(Result.ExpectedValue what, Test... all) { + return expectAll(what, RUN_ALL, all); + } + + public static CompoundTest all(Result.ExpectedValue what, String description, Test... all) { + CompoundTest result = CompoundTest.all(what, all); + result.setDescription(description); + return result; + } + + public static CompoundTest greedyAll(Result.ExpectedValue what, Test... all) { + return expectAll(what, RUN_GREEDY_ALL, all); + } + + public static CompoundTest greedyAll(Result.ExpectedValue what, String description, Test... all) { + CompoundTest result = CompoundTest.greedyAll(what, all); + result.setDescription(description); + return result; + } + + public static CompoundTest greedyAllTry(Result.ExpectedValue what, Test... all) { + return new CompoundTest((tests) -> { + int run = 0; + int ok = 0; + for (Test test : tests) { + if (test.hasRun()) { + run++; + if (Result.Value.fromExpected(what, test.ok()).ok()) { + ok++; + } + } + } + if (run == tests.length) { + if (ok == run) { + return new Result(Result.Value.SUCCESS, "All sub-tests had the expected result."); + } else { + return new Result(Result.Value.FAILURE, "Some sub-tests did not have the expected result."); + } + } else { + return new Result(Result.Value.SUCCESS, "All considered sub-tests had the expected result."); + } + }, RUN_GREEDY_ALL, all); + } + + public static CompoundTest greedyAllTry(Result.ExpectedValue what, String description, Test... all) { + CompoundTest result = CompoundTest.greedyAllTry(what, all); + result.setDescription(description); + return result; + } + + private static CompoundTest expectAny(Result.ExpectedValue what, Consumer<Test[]> runCallback, Test[] any) { + return new CompoundTest((tests) -> { + for (Test test : tests) { + if (Result.Value.fromExpected(what, test.ok()).ok()) { + return new Result(Result.Value.SUCCESS, "Some sub-tests did have the expected result."); + } + } + return new Result(Result.Value.FAILURE, "None of the sub-tests had the expected result."); + }, runCallback, any); + } + + public static CompoundTest greedyAny(Result.ExpectedValue what, Test... any) { + return expectAny(what, RUN_GREEDY_ANY, any); + } + + public static CompoundTest greedyAny(Result.ExpectedValue what, String description, Test... any) { + CompoundTest result = CompoundTest.greedyAny(what, any); + result.setDescription(description); + return result; + } + + public static CompoundTest any(Result.ExpectedValue what, Test... any) { + return expectAny(what, RUN_ALL, any); + } + + public static CompoundTest any(Result.ExpectedValue what, String description, Test... any) { + CompoundTest result = CompoundTest.any(what, any); + result.setDescription(description); + return result; + } + + public static CompoundTest mask(Result.ExpectedValue[] results, Test... masked) { + return new CompoundTest((tests) -> { + for (int i = 0; i < results.length; ++i) { + if (!Result.Value.fromExpected(results[i], tests[i].ok()).ok()) { + return new Result(Result.Value.FAILURE, "Some sub-tests did not match the result mask."); + } + } + return new Result(Result.Value.SUCCESS, "All sub-tests matched the expected mask."); + }, RUN_ALL, masked); + } + + public static CompoundTest mask(Result.ExpectedValue[] results, String description, Test... masked) { + CompoundTest result = CompoundTest.mask(results, masked); + result.setDescription(description); + return result; + } + + public Test[] getTests() { + return tests.clone(); + } + + public Test[] getRunTests() { + return Arrays.stream(tests).filter(Test::hasRun).toArray(Test[]::new); + } + + public Test[] getStartedTests() { + return Arrays.stream(tests).filter(Test::hasStarted).toArray(Test[]::new); + } + + public Test[] getSkippedTests() { + return Arrays.stream(tests).filter((test) -> !test.hasRun()).toArray(Test[]::new); + } + + @Override + protected void runSelf() { + runCallback.accept(tests); + result = resultCallback.apply(tests); + } + + public void setDescription(String description) { + this.description = description; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public CompoundTest clone() throws CloneNotSupportedException { + return (CompoundTest) super.clone(); + } +} diff --git a/common/src/main/java/cz/crcs/ectester/common/test/Result.java b/common/src/main/java/cz/crcs/ectester/common/test/Result.java new file mode 100644 index 0000000..f065f9c --- /dev/null +++ b/common/src/main/java/cz/crcs/ectester/common/test/Result.java @@ -0,0 +1,106 @@ +package cz.crcs.ectester.common.test; + +/** + * A Result of a Test. Has a Value and an optional String cause. + * + * @author Jan Jancar johny@neuromancer.sk + */ +public class Result { + + private Value value; + private Object cause; + + public Result(Value value) { + this.value = value; + } + + public Result(Value value, Object cause) { + this(value); + this.cause = cause; + } + + public Value getValue() { + return value; + } + + public Object getCause() { + return cause; + } + + public boolean ok() { + return value.ok(); + } + + public boolean compareTo(Result other) { + if (other == null) { + return false; + } + return value == other.value; + } + + public boolean compareTo(Value other) { + if (other == null) { + return false; + } + return value == other; + } + + /** + * A result value of a Test. + */ + public enum Value { + SUCCESS(true, "Expected success."), + FAILURE(false, "Unexpected failure."), + UXSUCCESS(false, "Unexpected success."), + XFAILURE(true, "Expected failure."), + ERROR(false, "Error."); + + private boolean ok; + private String desc; + + Value(boolean ok) { + this.ok = ok; + } + + Value(boolean ok, String desc) { + this(ok); + this.desc = desc; + } + + public static Value fromExpected(ExpectedValue expected, boolean successful) { + switch (expected) { + case SUCCESS: + return successful ? SUCCESS : FAILURE; + case FAILURE: + return successful ? UXSUCCESS : XFAILURE; + case ANY: + return successful ? SUCCESS : XFAILURE; + } + return SUCCESS; + } + + public static Value fromExpected(ExpectedValue expected, boolean successful, boolean error) { + if (error) { + return ERROR; + } + return fromExpected(expected, successful); + } + + public boolean ok() { + return ok; + } + + public String description() { + return desc; + } + } + + /** + * A possible expected value result of a Test. + */ + public enum ExpectedValue { + SUCCESS, + FAILURE, + ANY + } +} diff --git a/common/src/main/java/cz/crcs/ectester/common/test/SimpleTest.java b/common/src/main/java/cz/crcs/ectester/common/test/SimpleTest.java new file mode 100644 index 0000000..d2b3e94 --- /dev/null +++ b/common/src/main/java/cz/crcs/ectester/common/test/SimpleTest.java @@ -0,0 +1,38 @@ +package cz.crcs.ectester.common.test; + +/** + * @param <T> + * @author Jan Jancar johny@neuromancer.sk + */ +public abstract class SimpleTest<T extends BaseTestable> extends Test implements Testable { + protected T testable; + protected TestCallback<T> callback; + + public SimpleTest(T testable, TestCallback<T> callback) { + if (testable == null) { + throw new IllegalArgumentException("testable is null."); + } + if (callback == null) { + throw new IllegalArgumentException("callback is null."); + } + this.testable = testable; + this.callback = callback; + } + + public T getTestable() { + return testable; + } + + @Override + protected void runSelf() { + testable.run(); + result = callback.apply(testable); + } + + @Override + public SimpleTest clone() throws CloneNotSupportedException { + SimpleTest clone = (SimpleTest) super.clone(); + clone.testable = testable.clone(); + return clone; + } +} diff --git a/common/src/main/java/cz/crcs/ectester/common/test/Test.java b/common/src/main/java/cz/crcs/ectester/common/test/Test.java new file mode 100644 index 0000000..8bf9502 --- /dev/null +++ b/common/src/main/java/cz/crcs/ectester/common/test/Test.java @@ -0,0 +1,83 @@ +package cz.crcs.ectester.common.test; + +import static cz.crcs.ectester.common.test.Result.Value; + +/** + * An abstract test that can be run and has a Result. + * + * @author Jan Jancar johny@neuromancer.sk + */ +public abstract class Test implements Testable, Cloneable { + protected boolean hasRun; + protected boolean hasStarted; + protected Result result; + + public Result getResult() { + return result; + } + + public boolean ok() { + if (result == null) { + return true; + } + return result.ok(); + } + + @Override + public boolean error() { + if (result == null) { + return false; + } + return result.compareTo(Value.ERROR); + } + + @Override + public Object errorCause() { + if (result == null || !result.compareTo(Value.ERROR)) { + return null; + } + return result.getCause(); + } + + @Override + public boolean hasRun() { + return hasRun; + } + + public boolean hasStarted() { + return hasStarted; + } + + @Override + public void reset() { + hasRun = false; + hasStarted = false; + result = null; + } + + public abstract String getDescription(); + + @Override + public Test clone() throws CloneNotSupportedException { + return (Test) super.clone(); + } + + @Override + public void run() { + if (hasRun) + return; + try { + hasStarted = true; + runSelf(); + hasRun = true; + } catch (TestException e) { + result = new Result(Value.ERROR, e); + throw e; + } catch (Exception e) { + result = new Result(Value.ERROR, e); + throw new TestException(e); + } + } + + protected abstract void runSelf(); +} diff --git a/common/src/main/java/cz/crcs/ectester/common/test/TestCallback.java b/common/src/main/java/cz/crcs/ectester/common/test/TestCallback.java new file mode 100644 index 0000000..c5a49f3 --- /dev/null +++ b/common/src/main/java/cz/crcs/ectester/common/test/TestCallback.java @@ -0,0 +1,11 @@ +package cz.crcs.ectester.common.test; + +import java.util.function.Function; + +/** + * @param <T> + * @author Jan Jancar johny@neuromancer.sk + */ +public abstract class TestCallback<T extends Testable> implements Function<T, Result> { + +} diff --git a/common/src/main/java/cz/crcs/ectester/common/test/TestException.java b/common/src/main/java/cz/crcs/ectester/common/test/TestException.java new file mode 100644 index 0000000..0b605eb --- /dev/null +++ b/common/src/main/java/cz/crcs/ectester/common/test/TestException.java @@ -0,0 +1,15 @@ +package cz.crcs.ectester.common.test; + +/** + * A TestException is an Exception that can be thrown during the running of a Testable, + * or a Test. It means that the Testable/TestSuite encountered an unexpected error + * and has to terminate. + * + * @author Jan Jancar johny@neuromancer.sk + */ +@SuppressWarnings("serial") +public class TestException extends RuntimeException { + public TestException(Throwable e) { + super(e); + } +} diff --git a/common/src/main/java/cz/crcs/ectester/common/test/TestSuite.java b/common/src/main/java/cz/crcs/ectester/common/test/TestSuite.java new file mode 100644 index 0000000..b12680a --- /dev/null +++ b/common/src/main/java/cz/crcs/ectester/common/test/TestSuite.java @@ -0,0 +1,100 @@ +package cz.crcs.ectester.common.test; + +import cz.crcs.ectester.common.output.TestWriter; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public abstract class TestSuite { + protected String name; + protected String[] description; + private TestWriter writer; + private Test running; + private int ran = 0; + private int runFrom = 0; + private int runTo = -1; + + public TestSuite(TestWriter writer, String name, String... description) { + this.writer = writer; + this.name = name; + this.description = description; + } + + /** + * Run the <code>TestSuite</code>. + */ + public void run() { + run(0); + } + + public void run(int from) { + run(from, -1); + } + + public void run(int from, int to) { + this.runFrom = from; + this.runTo = to; + writer.begin(this); + try { + runTests(); + } catch (TestException e) { + writer.outputError(running, e, ran); + } catch (Exception e) { + writer.end(); + throw new TestSuiteException(e); + } + writer.end(); + } + + /** + * Run the given test and return it back. + * + * @param t The test to run. + * @return The test that was run. + * @throws TestException + */ + protected <T extends Test> T runTest(T t) { + running = t; + t.run(); + running = null; + return t; + } + + /** + * Run the given test, output it and return it back. + * + * @param t The test to run. + * @return The test that was run. + * @throws TestException + */ + protected <T extends Test> T doTest(T t) { + if (ran >= runFrom && (runTo < 0 || ran <= runTo)) { + runTest(t); + writer.outputTest(t, ran); + } + ran++; + return t; + } + + /** + * + */ + protected abstract void runTests() throws Exception; + + public String getName() { + return name; + } + + public String[] getDescription() { + return description; + } + + public String getTextDescription() { + return String.join(System.lineSeparator(), description); + } + + public String toString() { + return null; + } + +} diff --git a/common/src/main/java/cz/crcs/ectester/common/test/TestSuiteException.java b/common/src/main/java/cz/crcs/ectester/common/test/TestSuiteException.java new file mode 100644 index 0000000..2d1ea09 --- /dev/null +++ b/common/src/main/java/cz/crcs/ectester/common/test/TestSuiteException.java @@ -0,0 +1,14 @@ +package cz.crcs.ectester.common.test; + +/** + * An unexpected exception was thrown while running a TestSuite, outside Test + * or a Testable. + * + * @author Jan Jancar johny@neuromancer.sk + */ +@SuppressWarnings("serial") +public class TestSuiteException extends RuntimeException { + public TestSuiteException(Throwable e) { + super(e); + } +} diff --git a/common/src/main/java/cz/crcs/ectester/common/test/Testable.java b/common/src/main/java/cz/crcs/ectester/common/test/Testable.java new file mode 100644 index 0000000..7b4545c --- /dev/null +++ b/common/src/main/java/cz/crcs/ectester/common/test/Testable.java @@ -0,0 +1,38 @@ +package cz.crcs.ectester.common.test; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public interface Testable { + /** + * @return Whether this Testable was OK. + */ + boolean ok(); + + /** + * @return Whether an error happened. + */ + boolean error(); + + /** + * @return The cause of an error, if it happened, otherwise null. + */ + Object errorCause(); + + /** + * @return Whether this runnable was run. + */ + boolean hasRun(); + + /** + * + */ + void reset(); + + /** + * Run this Runnable. + * + * @throws TestException If an unexpected exception/error is encountered. + */ + void run(); +} diff --git a/common/src/main/java/cz/crcs/ectester/common/util/ByteUtil.java b/common/src/main/java/cz/crcs/ectester/common/util/ByteUtil.java new file mode 100644 index 0000000..442824a --- /dev/null +++ b/common/src/main/java/cz/crcs/ectester/common/util/ByteUtil.java @@ -0,0 +1,193 @@ +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 { + + /** + * Get a short from a byte array at <code>offset</code>, big-endian. + * + * @return the short value + */ + public static short getShort(byte[] array, int offset) { + return (short) (((array[offset] & 0xFF) << 8) | (array[offset + 1] & 0xFF)); + } + + /** + * Get a short from a byte array at <code>offset</code>, return it as an int, big-endian. + * + * @return the short value (as an int) + */ + public static int getShortInt(byte[] array, int offset) { + return (((array[offset] & 0xFF) << 8) | (array[offset + 1] & 0xFF)); + } + + /** + * Set a short in a byte array at <code>offset</code>, big-endian. + */ + public static void setShort(byte[] array, int offset, short value) { + array[offset + 1] = (byte) (value & 0xFF); + array[offset] = (byte) ((value >> 8) & 0xFF); + } + + /** + * Compare two byte arrays upto <code>length</code> and get first difference. + * + * @return the position of the first difference in the two byte arrays, or <code>length</code> if they are equal. + */ + 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; + } + + /** + * Compare two byte arrays, upto <code>length</code>. + * + * @return whether the arrays are equal upto <code>length</code> + */ + public static boolean compareBytes(byte[] one, int oneOffset, byte[] other, int otherOffset, int length) { + return diffBytes(one, oneOffset, other, otherOffset, length) == length; + } + + /** + * Test if the byte array has all values equal to <code>value</code>. + */ + public static boolean allValue(byte[] array, byte value) { + for (byte a : array) { + if (a != value) + return false; + } + return true; + } + + public static byte[] shortToBytes(short value) { + byte[] result = new byte[2]; + setShort(result, 0, value); + return result; + } + + public static byte[] shortToBytes(short[] shorts) { + if (shorts == null) { + return null; + } + byte[] result = new byte[shorts.length * 2]; + for (int i = 0; i < shorts.length; ++i) { + setShort(result, 2 * i, shorts[i]); + } + return result; + } + + /** + * Parse a hex string into a byte array, big-endian. + * + * @param hex The String to parse. + * @return the byte array from the hex string. + */ + public static byte[] hexToBytes(String hex) { + return hexToBytes(hex, true); + } + + /** + * Parse a hex string into a byte-array, specify endianity. + * + * @param hex The String to parse. + * @param bigEndian Whether to parse as big-endian. + * @return the byte array from the hex string. + */ + 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) { + if (data == null) { + return ""; + } + 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) { + if (data == null) { + return ""; + } + 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 static byte[] prependLength(byte[] data) { + return concatenate(ByteUtil.shortToBytes((short) data.length), data); + } +} diff --git a/common/src/main/java/cz/crcs/ectester/common/util/CardConsts.java b/common/src/main/java/cz/crcs/ectester/common/util/CardConsts.java new file mode 100644 index 0000000..1483346 --- /dev/null +++ b/common/src/main/java/cz/crcs/ectester/common/util/CardConsts.java @@ -0,0 +1,65 @@ +package cz.crcs.ectester.common.util; + +public class CardConsts { + // MAIN INSTRUCTION CLASS + public static final byte CLA_ECTESTERAPPLET = (byte) 0xB0; + + // INSTRUCTIONS + public static final byte INS_ALLOCATE = (byte) 0x5a; + public static final byte INS_CLEAR = (byte) 0x5b; + public static final byte INS_SET = (byte) 0x5c; + public static final byte INS_TRANSFORM = (byte) 0x5d; + public static final byte INS_GENERATE = (byte) 0x5e; + public static final byte INS_EXPORT = (byte) 0x5f; + public static final byte INS_ECDH = (byte) 0x70; + public static final byte INS_ECDH_DIRECT = (byte) 0x71; + public static final byte INS_ECDSA = (byte) 0x72; + public static final byte INS_ECDSA_SIGN = (byte) 0x73; + public static final byte INS_ECDSA_VERIFY = (byte) 0x74; + public static final byte INS_CLEANUP = (byte) 0x75; + public static final byte INS_ALLOCATE_KA = (byte) 0x76; + public static final byte INS_ALLOCATE_SIG = (byte) 0x77; + public static final byte INS_GET_INFO = (byte) 0x78; + public static final byte INS_SET_DRY_RUN_MODE = (byte) 0x79; + public static final byte INS_BUFFER = (byte) 0x7a; + public static final byte INS_PERFORM = (byte) 0x7b; + + // PARAMETERS for P1 and P2 + public static final byte KEYPAIR_LOCAL = (byte) 0x01; + public static final byte KEYPAIR_REMOTE = (byte) 0x02; + public static final byte KEYPAIR_BOTH = KEYPAIR_LOCAL | KEYPAIR_REMOTE; + public static final byte BUILD_KEYPAIR = (byte) 0x01; + public static final byte BUILD_KEYBUILDER = (byte) 0x02; + public static final byte EXPORT_TRUE = (byte) 0xff; + public static final byte EXPORT_FALSE = (byte) 0x00; + public static final byte MODE_NORMAL = (byte) 0xaa; + public static final byte MODE_DRY_RUN = (byte) 0xbb; + + // STATUS WORDS + public static final short SW_SIG_VERIFY_FAIL = (short) 0x0ee1; + public static final short SW_DH_DHC_MISMATCH = (short) 0x0ee2; + public static final short SW_KEYPAIR_NULL = (short) 0x0ee3; + public static final short SW_KA_NULL = (short) 0x0ee4; + public static final short SW_SIGNATURE_NULL = (short) 0x0ee5; + public static final short SW_OBJECT_NULL = (short) 0x0ee6; + public static final short SW_CANNOT_FIT = (short) 0x0ee7; + public static final short SW_Exception = (short) 0xff01; + public static final short SW_ArrayIndexOutOfBoundsException = (short) 0xff02; + public static final short SW_ArithmeticException = (short) 0xff03; + public static final short SW_ArrayStoreException = (short) 0xff04; + public static final short SW_NullPointerException = (short) 0xff05; + public static final short SW_NegativeArraySizeException = (short) 0xff06; + public static final short SW_CryptoException_prefix = (short) 0xf100; + public static final short SW_SystemException_prefix = (short) 0xf200; + public static final short SW_PINException_prefix = (short) 0xf300; + public static final short SW_TransactionException_prefix = (short) 0xf400; + public static final short SW_CardRuntimeException_prefix = (short) 0xf500; + + // + public static final short BASE_221 = (short) 0x0221; + public static final short BASE_222 = (short) 0x0222; + + // + public static final short CDATA_BASIC = (short) 5; + public static final short CDATA_EXTENDED = (short) 7; +} diff --git a/common/src/main/java/cz/crcs/ectester/common/util/CardUtil.java b/common/src/main/java/cz/crcs/ectester/common/util/CardUtil.java new file mode 100644 index 0000000..eeb2159 --- /dev/null +++ b/common/src/main/java/cz/crcs/ectester/common/util/CardUtil.java @@ -0,0 +1,569 @@ +package cz.crcs.ectester.common.util; + +import cz.crcs.ectester.common.ec.EC_Consts; + +import java.util.LinkedList; +import java.util.List; + +/** + * @author Petr Svenda petr@svenda.com + * @author Jan Jancar johny@neuromancer.sk + */ +public class CardUtil { + public class ISO7816 { + public static final short SW_APPLET_SELECT_FAILED = 27033; + public static final short SW_BYTES_REMAINING_00 = 24832; + public static final short SW_CLA_NOT_SUPPORTED = 28160; + public static final short SW_COMMAND_CHAINING_NOT_SUPPORTED = 26756; + public static final short SW_COMMAND_NOT_ALLOWED = 27014; + public static final short SW_CONDITIONS_NOT_SATISFIED = 27013; + public static final short SW_CORRECT_LENGTH_00 = 27648; + public static final short SW_DATA_INVALID = 27012; + public static final short SW_FILE_FULL = 27268; + public static final short SW_FILE_INVALID = 27011; + public static final short SW_FILE_NOT_FOUND = 27266; + public static final short SW_FUNC_NOT_SUPPORTED = 27265; + public static final short SW_INCORRECT_P1P2 = 27270; + public static final short SW_INS_NOT_SUPPORTED = 27904; + public static final short SW_LAST_COMMAND_EXPECTED = 26755; + public static final short SW_LOGICAL_CHANNEL_NOT_SUPPORTED = 26753; + public static final short SW_NO_ERROR = -28672; + public static final short SW_RECORD_NOT_FOUND = 27267; + public static final short SW_SECURE_MESSAGING_NOT_SUPPORTED = 26754; + public static final short SW_SECURITY_STATUS_NOT_SATISFIED = 27010; + public static final short SW_UNKNOWN = 28416; + public static final short SW_WARNING_STATE_UNCHANGED = 25088; + public static final short SW_WRONG_DATA = 27264; + public static final short SW_WRONG_LENGTH = 26368; + public static final short SW_WRONG_P1P2 = 27392; + } + + public class CryptoException { + public static final short ILLEGAL_VALUE = 1; + public static final short UNINITIALIZED_KEY = 2; + public static final short NO_SUCH_ALGORITHM = 3; + public static final short INVALID_INIT = 4; + public static final short ILLEGAL_USE = 5; + } + + public static byte getSig(String name) { + switch (name) { + case "SHA1": + return EC_Consts.Signature_ALG_ECDSA_SHA; + case "SHA224": + return EC_Consts.Signature_ALG_ECDSA_SHA_224; + case "SHA256": + return EC_Consts.Signature_ALG_ECDSA_SHA_256; + case "SHA384": + return EC_Consts.Signature_ALG_ECDSA_SHA_384; + case "SHA512": + return EC_Consts.Signature_ALG_ECDSA_SHA_512; + default: + return EC_Consts.Signature_ALG_ECDSA_SHA; + } + } + + public static String getSigHashAlgo(byte sigType) { + switch (sigType) { + case EC_Consts.Signature_ALG_ECDSA_SHA: + return "SHA1"; + case EC_Consts.Signature_ALG_ECDSA_SHA_224: + return "SHA224"; + case EC_Consts.Signature_ALG_ECDSA_SHA_256: + return "SHA256"; + case EC_Consts.Signature_ALG_ECDSA_SHA_384: + return "SHA384"; + case EC_Consts.Signature_ALG_ECDSA_SHA_512: + return "SHA512"; + default: + return null; + } + } + + public static String getSigHashName(byte sigType) { + switch (sigType) { + case EC_Consts.Signature_ALG_ECDSA_SHA: + return "SHA1"; + case EC_Consts.Signature_ALG_ECDSA_SHA_224: + return "SHA224"; + case EC_Consts.Signature_ALG_ECDSA_SHA_256: + return "SHA256"; + case EC_Consts.Signature_ALG_ECDSA_SHA_384: + return "SHA384"; + case EC_Consts.Signature_ALG_ECDSA_SHA_512: + return "SHA512"; + default: + return null; + } + } + + public static byte getKA(String name) { + switch (name) { + case "DH": + return EC_Consts.KeyAgreement_ALG_EC_SVDP_DH; + case "DHC": + return EC_Consts.KeyAgreement_ALG_EC_SVDP_DHC; + case "DH_PLAIN": + return EC_Consts.KeyAgreement_ALG_EC_SVDP_DH_PLAIN; + case "DHC_PLAIN": + return EC_Consts.KeyAgreement_ALG_EC_SVDP_DHC_PLAIN; + case "PACE_GM": + return EC_Consts.KeyAgreement_ALG_EC_PACE_GM; + case "DH_PLAIN_XY": + return EC_Consts.KeyAgreement_ALG_EC_SVDP_DH_PLAIN_XY; + default: + return EC_Consts.KeyAgreement_ALG_EC_SVDP_DH; + } + } + + public static String getKexHashName(byte kexType) { + switch (kexType) { + case EC_Consts.KeyAgreement_ALG_EC_SVDP_DH: + case EC_Consts.KeyAgreement_ALG_EC_SVDP_DHC: + return "SHA1"; + default: + return "NONE"; + } + } + + public static String getSWSource(short sw) { + switch (sw) { + case ISO7816.SW_NO_ERROR: + case ISO7816.SW_APPLET_SELECT_FAILED: + case ISO7816.SW_BYTES_REMAINING_00: + case ISO7816.SW_CLA_NOT_SUPPORTED: + case ISO7816.SW_COMMAND_NOT_ALLOWED: + case ISO7816.SW_COMMAND_CHAINING_NOT_SUPPORTED: + case ISO7816.SW_CONDITIONS_NOT_SATISFIED: + case ISO7816.SW_CORRECT_LENGTH_00: + case ISO7816.SW_DATA_INVALID: + case ISO7816.SW_FILE_FULL: + case ISO7816.SW_FILE_INVALID: + case ISO7816.SW_FILE_NOT_FOUND: + case ISO7816.SW_FUNC_NOT_SUPPORTED: + case ISO7816.SW_INCORRECT_P1P2: + case ISO7816.SW_INS_NOT_SUPPORTED: + case ISO7816.SW_LOGICAL_CHANNEL_NOT_SUPPORTED: + case ISO7816.SW_RECORD_NOT_FOUND: + case ISO7816.SW_SECURE_MESSAGING_NOT_SUPPORTED: + case ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED: + case ISO7816.SW_UNKNOWN: + case ISO7816.SW_WARNING_STATE_UNCHANGED: + case ISO7816.SW_WRONG_DATA: + case ISO7816.SW_WRONG_LENGTH: + case ISO7816.SW_WRONG_P1P2: + return "ISO"; + case CryptoException.ILLEGAL_VALUE: + case CryptoException.UNINITIALIZED_KEY: + case CryptoException.NO_SUCH_ALGORITHM: + case CryptoException.INVALID_INIT: + case CryptoException.ILLEGAL_USE: + return "CryptoException"; + case CardConsts.SW_SIG_VERIFY_FAIL: + case CardConsts.SW_DH_DHC_MISMATCH: + case CardConsts.SW_KEYPAIR_NULL: + case CardConsts.SW_KA_NULL: + case CardConsts.SW_SIGNATURE_NULL: + case CardConsts.SW_OBJECT_NULL: + return "ECTesterApplet"; + default: + return "?"; + } + } + + public static String getSW(short sw) { + int upper = (sw & 0xff00) >> 8; + int lower = (sw & 0xff); + switch (upper) { + case 0xf1: + return String.format("CryptoException(%d)", lower); + case 0xf2: + return String.format("SystemException(%d)", lower); + case 0xf3: + return String.format("PINException(%d)", lower); + case 0xf4: + return String.format("TransactionException(%d)", lower); + case 0xf5: + return String.format("CardRuntimeException(%d)", lower); + default: + switch (sw) { + case ISO7816.SW_APPLET_SELECT_FAILED: + return "APPLET_SELECT_FAILED"; + case ISO7816.SW_BYTES_REMAINING_00: + return "BYTES_REMAINING"; + case ISO7816.SW_CLA_NOT_SUPPORTED: + return "CLA_NOT_SUPPORTED"; + case ISO7816.SW_COMMAND_NOT_ALLOWED: + return "COMMAND_NOT_ALLOWED"; + case ISO7816.SW_COMMAND_CHAINING_NOT_SUPPORTED: + return "COMMAND_CHAINING_NOT_SUPPORTED"; + case ISO7816.SW_CONDITIONS_NOT_SATISFIED: + return "CONDITIONS_NOT_SATISFIED"; + case ISO7816.SW_CORRECT_LENGTH_00: + return "CORRECT_LENGTH"; + case ISO7816.SW_DATA_INVALID: + return "DATA_INVALID"; + case ISO7816.SW_FILE_FULL: + return "FILE_FULL"; + case ISO7816.SW_FILE_INVALID: + return "FILE_INVALID"; + case ISO7816.SW_FILE_NOT_FOUND: + return "FILE_NOT_FOUND"; + case ISO7816.SW_FUNC_NOT_SUPPORTED: + return "FUNC_NOT_SUPPORTED"; + case ISO7816.SW_INCORRECT_P1P2: + return "INCORRECT_P1P2"; + case ISO7816.SW_INS_NOT_SUPPORTED: + return "INS_NOT_SUPPORTED"; + case ISO7816.SW_LOGICAL_CHANNEL_NOT_SUPPORTED: + return "LOGICAL_CHANNEL_NOT_SUPPORTED"; + case ISO7816.SW_RECORD_NOT_FOUND: + return "RECORD_NOT_FOUND"; + case ISO7816.SW_SECURE_MESSAGING_NOT_SUPPORTED: + return "SECURE_MESSAGING_NOT_SUPPORTED"; + case ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED: + return "SECURITY_STATUS_NOT_SATISFIED"; + case ISO7816.SW_UNKNOWN: + return "UNKNOWN"; + case ISO7816.SW_WARNING_STATE_UNCHANGED: + return "WARNING_STATE_UNCHANGED"; + case ISO7816.SW_WRONG_DATA: + return "WRONG_DATA"; + case ISO7816.SW_WRONG_LENGTH: + return "WRONG_LENGTH"; + case ISO7816.SW_WRONG_P1P2: + return "WRONG_P1P2"; + case CryptoException.ILLEGAL_VALUE: + return "ILLEGAL_VALUE"; + case CryptoException.UNINITIALIZED_KEY: + return "UNINITIALIZED_KEY"; + case CryptoException.NO_SUCH_ALGORITHM: + return "NO_SUCH_ALG"; + case CryptoException.INVALID_INIT: + return "INVALID_INIT"; + case CryptoException.ILLEGAL_USE: + return "ILLEGAL_USE"; + case CardConsts.SW_SIG_VERIFY_FAIL: + return "SIG_VERIFY_FAIL"; + case CardConsts.SW_DH_DHC_MISMATCH: + return "DH_DHC_MISMATCH"; + case CardConsts.SW_KEYPAIR_NULL: + return "KEYPAIR_NULL"; + case CardConsts.SW_KA_NULL: + return "KA_NULL"; + case CardConsts.SW_SIGNATURE_NULL: + return "SIGNATURE_NULL"; + case CardConsts.SW_OBJECT_NULL: + return "OBJECT_NULL"; + case CardConsts.SW_Exception: + return "Exception"; + case CardConsts.SW_ArrayIndexOutOfBoundsException: + return "ArrayIndexOutOfBoundsException"; + case CardConsts.SW_ArithmeticException: + return "ArithmeticException"; + case CardConsts.SW_ArrayStoreException: + return "ArrayStoreException"; + case CardConsts.SW_NullPointerException: + return "NullPointerException"; + case CardConsts.SW_NegativeArraySizeException: + return "NegativeArraySizeException"; + default: + return "unknown"; + } + } + } + + public static String getSWString(short sw) { + if (sw == ISO7816.SW_NO_ERROR) { + return "OK (0x9000)"; + } else { + String str = getSW(sw); + return String.format("fail (%s, 0x%04x)", str, sw); + } + } + + public static String getParams(short params) { + if (params == 0) { + return ""; + } + List<String> ps = new LinkedList<>(); + short paramMask = EC_Consts.PARAMETER_FP; + while (paramMask <= EC_Consts.PARAMETER_S) { + short paramValue = (short) (paramMask & params); + if (paramValue != 0) { + switch (paramValue) { + case EC_Consts.PARAMETER_FP: + ps.add("P"); + break; + case EC_Consts.PARAMETER_F2M: + ps.add("2^M"); + break; + case EC_Consts.PARAMETER_A: + ps.add("A"); + break; + case EC_Consts.PARAMETER_B: + ps.add("B"); + break; + case EC_Consts.PARAMETER_G: + ps.add("G"); + break; + case EC_Consts.PARAMETER_R: + ps.add("R"); + break; + case EC_Consts.PARAMETER_K: + ps.add("K"); + break; + case EC_Consts.PARAMETER_W: + ps.add("W"); + break; + case EC_Consts.PARAMETER_S: + ps.add("S"); + break; + } + } + paramMask = (short) (paramMask << 1); + } + + if (ps.size() != 0) { + return "[" + String.join(",", ps) + "]"; + } else { + return "unknown"; + } + } + + public static String getTransformation(short transformationType) { + if (transformationType == 0) { + return "NONE"; + } + List<String> names = new LinkedList<>(); + short transformationMask = 1; + while (transformationMask <= EC_Consts.TRANSFORMATION_04_MASK) { + short transformationValue = (short) (transformationMask & transformationType); + if (transformationValue != 0) { + switch (transformationValue) { + case EC_Consts.TRANSFORMATION_FIXED: + names.add("FIXED"); + break; + case EC_Consts.TRANSFORMATION_ONE: + names.add("ONE"); + break; + case EC_Consts.TRANSFORMATION_ZERO: + names.add("ZERO"); + break; + case EC_Consts.TRANSFORMATION_ONEBYTERANDOM: + names.add("ONE_BYTE_RANDOM"); + break; + case EC_Consts.TRANSFORMATION_FULLRANDOM: + names.add("FULL_RANDOM"); + break; + case EC_Consts.TRANSFORMATION_INCREMENT: + names.add("INCREMENT"); + break; + case EC_Consts.TRANSFORMATION_INFINITY: + names.add("INFINITY"); + break; + case EC_Consts.TRANSFORMATION_COMPRESS: + names.add("COMPRESSED"); + break; + case EC_Consts.TRANSFORMATION_COMPRESS_HYBRID: + names.add("HYBRID"); + break; + case EC_Consts.TRANSFORMATION_04_MASK: + names.add("MASK(O4)"); + break; + case EC_Consts.TRANSFORMATION_MAX: + names.add("MAX"); + break; + } + } + transformationMask = (short) ((transformationMask) << 1); + } + if (names.size() != 0) { + return String.join(" + ", names); + } else { + return "unknown"; + } + } + + public static String getKATypeString(byte kaType) { + switch (kaType) { + case EC_Consts.KeyAgreement_ALG_EC_SVDP_DH: + return "ALG_EC_SVDP_DH"; + case EC_Consts.KeyAgreement_ALG_EC_SVDP_DH_PLAIN: + return "ALG_EC_SVDP_DH_PLAIN"; + case EC_Consts.KeyAgreement_ALG_EC_PACE_GM: + return "ALG_EC_PACE_GM"; + case EC_Consts.KeyAgreement_ALG_EC_SVDP_DH_PLAIN_XY: + return "ALG_EC_SVDP_DH_PLAIN_XY"; + case EC_Consts.KeyAgreement_ALG_EC_SVDP_DHC: + return "ALG_EC_SVDP_DHC"; + case EC_Consts.KeyAgreement_ALG_EC_SVDP_DHC_PLAIN: + return "ALG_EC_SVDP_DHC_PLAIN"; + default: + return "unknown"; + } + } + + public static byte getKAType(String kaTypeString) { + switch (kaTypeString) { + case "DH": + case "ALG_EC_SVDP_DH": + return EC_Consts.KeyAgreement_ALG_EC_SVDP_DH; + case "DH_PLAIN": + case "ALG_EC_SVDP_DH_PLAIN": + return EC_Consts.KeyAgreement_ALG_EC_SVDP_DH_PLAIN; + case "PACE_GM": + case "ALG_EC_PACE_GM": + return EC_Consts.KeyAgreement_ALG_EC_PACE_GM; + case "DH_PLAIN_XY": + case "ALG_EC_SVDP_DH_PLAIN_XY": + return EC_Consts.KeyAgreement_ALG_EC_SVDP_DH_PLAIN_XY; + case "DHC": + case "ALG_EC_SVDP_DHC": + return EC_Consts.KeyAgreement_ALG_EC_SVDP_DHC; + case "DHC_PLAIN": + case "ALG_EC_SVDP_DHC_PLAIN": + return EC_Consts.KeyAgreement_ALG_EC_SVDP_DHC_PLAIN; + default: + return 0; + } + } + + public static byte parseKAType(String kaTypeString) { + byte kaType; + try { + kaType = Byte.parseByte(kaTypeString); + } catch (NumberFormatException nfex) { + kaType = getKAType(kaTypeString); + } + return kaType; + } + + public static String getSigTypeString(byte sigType) { + switch (sigType) { + case EC_Consts.Signature_ALG_ECDSA_SHA: + return "ALG_ECDSA_SHA"; + case EC_Consts.Signature_ALG_ECDSA_SHA_224: + return "ALG_ECDSA_SHA_224"; + case EC_Consts.Signature_ALG_ECDSA_SHA_256: + return "ALG_ECDSA_SHA_256"; + case EC_Consts.Signature_ALG_ECDSA_SHA_384: + return "ALG_ECDSA_SHA_384"; + case EC_Consts.Signature_ALG_ECDSA_SHA_512: + return "ALG_ECDSA_SHA_512"; + default: + return "unknown"; + } + } + + public static byte getSigType(String sigTypeString) { + switch (sigTypeString) { + case "ECDSA_SHA": + case "ALG_ECDSA_SHA": + return EC_Consts.Signature_ALG_ECDSA_SHA; + case "ECDSA_SHA_224": + case "ALG_ECDSA_SHA_224": + return EC_Consts.Signature_ALG_ECDSA_SHA_224; + case "ECDSA_SHA_256": + case "ALG_ECDSA_SHA_256": + return EC_Consts.Signature_ALG_ECDSA_SHA_256; + case "ECDSA_SHA_384": + case "ALG_ECDSA_SHA_384": + return EC_Consts.Signature_ALG_ECDSA_SHA_384; + case "ECDSA_SHA_512": + case "ALG_ECDSA_SHA_512": + return EC_Consts.Signature_ALG_ECDSA_SHA_512; + default: + return 0; + } + } + + public static byte parseSigType(String sigTypeString) { + byte sigType; + try { + sigType = Byte.parseByte(sigTypeString); + } catch (NumberFormatException nfex) { + sigType = getSigType(sigTypeString); + } + return sigType; + } + + public static String getKeyTypeString(byte keyClass) { + switch (keyClass) { + case EC_Consts.ALG_EC_FP: + return "ALG_EC_FP"; + case EC_Consts.ALG_EC_F2M: + return "ALG_EC_F2M"; + default: + return ""; + } + } + + public static String getCurveName(byte curve) { + String result = ""; + switch (curve) { + case EC_Consts.CURVE_default: + result = "default"; + break; + case EC_Consts.CURVE_external: + result = "external"; + break; + case EC_Consts.CURVE_secp112r1: + result = "secp112r1"; + break; + case EC_Consts.CURVE_secp128r1: + result = "secp128r1"; + break; + case EC_Consts.CURVE_secp160r1: + result = "secp160r1"; + break; + case EC_Consts.CURVE_secp192r1: + result = "secp192r1"; + break; + case EC_Consts.CURVE_secp224r1: + result = "secp224r1"; + break; + case EC_Consts.CURVE_secp256r1: + result = "secp256r1"; + break; + case EC_Consts.CURVE_secp384r1: + result = "secp384r1"; + break; + case EC_Consts.CURVE_secp521r1: + result = "secp521r1"; + break; + case EC_Consts.CURVE_sect163r1: + result = "sect163r1"; + break; + case EC_Consts.CURVE_sect233r1: + result = "sect233r1"; + break; + case EC_Consts.CURVE_sect283r1: + result = "sect283r1"; + break; + case EC_Consts.CURVE_sect409r1: + result = "sect409r1"; + break; + case EC_Consts.CURVE_sect571r1: + result = "sect571r1"; + break; + } + return result; + } + + public static String getParameterString(short params) { + String what = ""; + if (params == EC_Consts.PARAMETERS_DOMAIN_F2M || params == EC_Consts.PARAMETERS_DOMAIN_FP) { + what = "curve"; + } else if (params == EC_Consts.PARAMETER_W) { + what = "pubkey"; + } else if (params == EC_Consts.PARAMETER_S) { + what = "privkey"; + } else if (params == EC_Consts.PARAMETERS_KEYPAIR) { + what = "keypair"; + } else { + what = getParams(params); + } + return what; + } +} diff --git a/common/src/main/java/cz/crcs/ectester/common/util/ECUtil.java b/common/src/main/java/cz/crcs/ectester/common/util/ECUtil.java new file mode 100644 index 0000000..773644b --- /dev/null +++ b/common/src/main/java/cz/crcs/ectester/common/util/ECUtil.java @@ -0,0 +1,467 @@ +package cz.crcs.ectester.common.util; + +import cz.crcs.ectester.common.ec.*; +import cz.crcs.ectester.data.EC_Store; +import org.bouncycastle.crypto.digests.SHA1Digest; +import org.bouncycastle.crypto.signers.PlainDSAEncoding; +import org.bouncycastle.crypto.signers.StandardDSAEncoding; + +import java.io.FileInputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.security.KeyPair; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.interfaces.ECKey; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.*; +import java.util.LinkedList; +import java.util.List; +import java.util.Random; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class ECUtil { + private static Random rand = new Random(); + + 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(byte[][] point) { + if (point.length != 2) { + return null; + } + byte ybit = (byte) (point[1][point[1].length - 1] % 2); + return ByteUtil.concatenate(new byte[]{(byte) (0x02 | ybit)}, point[0]); + } + + 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, ECParameterSpec spec) { + return toX962Compressed(point, spec.getOrder().bitLength()); + } + + 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, ECParameterSpec spec) { + return toX962Uncompressed(point, spec.getOrder().bitLength()); + } + + 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.equals(BigInteger.ONE); + } + + 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); + + if (!isResidue(alpha, p)) { + throw new IllegalArgumentException(); + } + + BigInteger beta = modSqrt(alpha, p); + if (beta.getLowestSetBit() == 0) { + // rightmost bit is one + if (data[0] == 0x02) { + // yp is 0 + beta = p.subtract(beta); + } + } else { + // rightmost bit is zero + if (data[0] == 0x03) { + // yp is 1 + beta = p.subtract(beta); + } + } + + return new ECPoint(x, beta); + } else if (field instanceof ECFieldF2m) { + //TODO + throw new UnsupportedOperationException(); + } + return null; + } else { + throw new IllegalArgumentException(); + } + } + + private static byte[] hashCurve(EC_Curve curve) { + int bytes = (curve.getBits() + 7) / 8; + byte[] result = new byte[bytes]; + SHA1Digest digest = new SHA1Digest(); + byte[] curveName = curve.getId().getBytes(StandardCharsets.US_ASCII); + digest.update(curveName, 0, curveName.length); + int written = 0; + while (written < bytes) { + byte[] dig = new byte[digest.getDigestSize()]; + digest.doFinal(dig, 0); + int toWrite = Math.min(digest.getDigestSize(), bytes - written); + System.arraycopy(dig, 0, result, written, toWrite); + written += toWrite; + digest.update(dig, 0, dig.length); + } + return result; + } + + public static EC_Params fullRandomKey(EC_Curve curve) { + int bytes = (curve.getBits() + 7) / 8; + byte[] result = new byte[bytes]; + rand.nextBytes(result); + BigInteger priv = new BigInteger(1, result); + BigInteger order = new BigInteger(1, curve.getParam(EC_Consts.PARAMETER_R)[0]); + priv = priv.mod(order); + return new EC_Params(EC_Consts.PARAMETER_S, new byte[][]{toByteArray(priv, curve.getBits())}); + } + + public static EC_Params fixedRandomKey(EC_Curve curve) { + byte[] hash = hashCurve(curve); + BigInteger priv = new BigInteger(1, hash); + BigInteger order = new BigInteger(1, curve.getParam(EC_Consts.PARAMETER_R)[0]); + priv = priv.mod(order); + return new EC_Params(EC_Consts.PARAMETER_S, new byte[][]{toByteArray(priv, curve.getBits())}); + } + + private static BigInteger computeRHS(BigInteger x, BigInteger a, BigInteger b, BigInteger p) { + BigInteger rhs = x.modPow(BigInteger.valueOf(3), p); + rhs = rhs.add(a.multiply(x)).mod(p); + rhs = rhs.add(b).mod(p); + return rhs; + } + + public static EC_Params fullRandomPoint(EC_Curve curve) { + EllipticCurve ecCurve = curve.toCurve(); + + BigInteger p; + if (ecCurve.getField() instanceof ECFieldFp) { + ECFieldFp fp = (ECFieldFp) ecCurve.getField(); + p = fp.getP(); + if (!p.isProbablePrime(20)) { + return null; + } + } else { + //TODO + return null; + } + BigInteger x; + BigInteger rhs; + do { + x = new BigInteger(ecCurve.getField().getFieldSize(), rand).mod(p); + rhs = computeRHS(x, ecCurve.getA(), ecCurve.getB(), p); + } while (!isResidue(rhs, p)); + BigInteger y = modSqrt(rhs, p); + if (rand.nextBoolean()) { + y = p.subtract(y); + } + + byte[] xArr = toByteArray(x, ecCurve.getField().getFieldSize()); + byte[] yArr = toByteArray(y, ecCurve.getField().getFieldSize()); + return new EC_Params(EC_Consts.PARAMETER_W, new byte[][]{xArr, yArr}); + } + + public static EC_Params fixedRandomPoint(EC_Curve curve) { + EllipticCurve ecCurve = curve.toCurve(); + + BigInteger p; + if (ecCurve.getField() instanceof ECFieldFp) { + ECFieldFp fp = (ECFieldFp) ecCurve.getField(); + p = fp.getP(); + if (!p.isProbablePrime(20)) { + return null; + } + } else { + //TODO + return null; + } + + BigInteger x = new BigInteger(1, hashCurve(curve)).mod(p); + BigInteger rhs = computeRHS(x, ecCurve.getA(), ecCurve.getB(), p); + while (!isResidue(rhs, p)) { + x = x.add(BigInteger.ONE).mod(p); + rhs = computeRHS(x, ecCurve.getA(), ecCurve.getB(), p); + } + BigInteger y = modSqrt(rhs, p); + if (y.bitCount() % 2 == 0) { + y = p.subtract(y); + } + + byte[] xArr = toByteArray(x, ecCurve.getField().getFieldSize()); + byte[] yArr = toByteArray(y, ecCurve.getField().getFieldSize()); + return new EC_Params(EC_Consts.PARAMETER_W, new byte[][]{xArr, yArr}); + } + + public static ECPoint toPoint(EC_Params params) { + return new ECPoint( + new BigInteger(1, params.getParam(EC_Consts.PARAMETER_W)[0]), + new BigInteger(1, params.getParam(EC_Consts.PARAMETER_W)[1])); + } + + public static BigInteger toScalar(EC_Params params) { + return new BigInteger(1, params.getParam(EC_Consts.PARAMETER_S)[0]); + } + + public static ECPublicKey toPublicKey(EC_Key.Public pubkey) { + if (pubkey == null) { + return null; + } + EC_Curve curve = EC_Store.getInstance().getObject(EC_Curve.class, pubkey.getCurve()); + if (curve == null) { + throw new IllegalArgumentException("pubkey curve not found: " + pubkey.getCurve()); + } + return new RawECPublicKey(toPoint(pubkey), curve.toSpec()); + } + + public static ECPrivateKey toPrivateKey(EC_Key.Private privkey) { + if (privkey == null) { + return null; + } + EC_Curve curve = EC_Store.getInstance().getObject(EC_Curve.class, privkey.getCurve()); + if (curve == null) { + throw new IllegalArgumentException("privkey curve not found: " + privkey.getCurve()); + } + return new RawECPrivateKey(toScalar(privkey), curve.toSpec()); + } + + public static KeyPair toKeyPair(EC_Keypair kp) { + if (kp == null) { + return null; + } + EC_Curve curve = EC_Store.getInstance().getObject(EC_Curve.class, kp.getCurve()); + if (curve == null) { + throw new IllegalArgumentException("keypair curve not found: " + kp.getCurve()); + } + ECPublicKey pubkey = new RawECPublicKey(toPoint(kp), curve.toSpec()); + ECPrivateKey privkey = new RawECPrivateKey(toScalar(kp), curve.toSpec()); + return new KeyPair(pubkey, privkey); + } + + public static BigInteger recoverSignatureNonce(byte[] signature, byte[] data, BigInteger privkey, ECParameterSpec params, String hashAlgo, String sigType) { + // We do not know how to reconstruct those nonces so far. + // sigType.contains("ECKCDSA") || sigType.contains("ECNR") || sigType.contains("SM2") + if (!sigType.contains("ECDSA")) { + return null; + } + try { + int bitSize = params.getOrder().bitLength(); + // Hash the data. + byte[] hash; + if (hashAlgo == null || hashAlgo.equals("NONE")) { + hash = data; + } else { + MessageDigest md = MessageDigest.getInstance(hashAlgo); + hash = md.digest(data); + } + // Trim bitSize of rightmost bits. + BigInteger hashInt = new BigInteger(1, hash); + int hashBits = hashInt.bitLength(); + if (hashBits > bitSize) { + hashInt = hashInt.shiftRight(hashBits - bitSize); + } + + // Parse signature + BigInteger[] sigPair; + if (sigType.contains("CVC") || sigType.contains("PLAIN")) { + sigPair = PlainDSAEncoding.INSTANCE.decode(params.getOrder(), signature); + } else { + sigPair = StandardDSAEncoding.INSTANCE.decode(params.getOrder(), signature); + } + BigInteger r = sigPair[0]; + BigInteger s = sigPair[1]; + + BigInteger rd = privkey.multiply(r).mod(params.getOrder()); + BigInteger hrd = hashInt.add(rd).mod(params.getOrder()); + return s.modInverse(params.getOrder()).multiply(hrd).mod(params.getOrder()); + } catch (NoSuchAlgorithmException | IOException | ArithmeticException ex) { + ex.printStackTrace(); + return null; + } + } + + public static EC_Params joinParams(EC_Params... params) { + List<EC_Params> paramList = new LinkedList<>(); + short paramMask = 0; + int len = 0; + for (EC_Params param : params) { + if (param == null) { + continue; + } + int i = 0; + for (; i + 1 < paramList.size(); ++i) { + if (paramList.get(i + 1).getParams() == param.getParams()) { + throw new IllegalArgumentException(); + } + if (paramList.get(i + 1).getParams() < param.getParams()) { + break; + } + } + paramList.add(i, param); + paramMask |= param.getParams(); + len += param.numParams(); + } + + byte[][] res = new byte[len][]; + int i = 0; + for (EC_Params param : params) { + for (byte[] data : param.getData()) { + res[i++] = data.clone(); + } + } + return new EC_Params(paramMask, res); + } + + public static EC_Params loadParams(short params, String named, String file) throws IOException { + EC_Params result = null; + if (file != null) { + result = new EC_Params(params); + + FileInputStream in = new FileInputStream(file); + result.readCSV(in); + in.close(); + } else if (named != null) { + if (params == EC_Consts.PARAMETER_W) { + result = EC_Store.getInstance().getObject(EC_Key.Public.class, named); + } else if (params == EC_Consts.PARAMETER_S) { + result = EC_Store.getInstance().getObject(EC_Key.Private.class, named); + } + + if (result == null) { + result = EC_Store.getInstance().getObject(EC_Keypair.class, named); + } + } + return result; + } + + public static ECKey loadKey(short params, String named, String file, AlgorithmParameterSpec spec) throws IOException { + if (params == EC_Consts.PARAMETERS_KEYPAIR) { + throw new IllegalArgumentException(); + } + EC_Params param = loadParams(params, named, file); + if (param != null) { + if (params == EC_Consts.PARAMETER_W) { + return new RawECPublicKey(toPoint(param), (ECParameterSpec) spec); + } else if (params == EC_Consts.PARAMETER_S) { + return new RawECPrivateKey(toScalar(param), (ECParameterSpec) spec); + } + } + return null; + } + + public static boolean equalKeyPairParameters(ECPrivateKey priv, ECPublicKey pub) { + if(priv == null || pub == null) { + return false; + } + return priv.getParams().getCurve().equals(pub.getParams().getCurve()) && + priv.getParams().getCofactor() == pub.getParams().getCofactor() && + priv.getParams().getGenerator().equals(pub.getParams().getGenerator()) && + priv.getParams().getOrder().equals(pub.getParams().getOrder()); + } +} diff --git a/common/src/main/java/cz/crcs/ectester/common/util/FileUtil.java b/common/src/main/java/cz/crcs/ectester/common/util/FileUtil.java new file mode 100644 index 0000000..e6e319b --- /dev/null +++ b/common/src/main/java/cz/crcs/ectester/common/util/FileUtil.java @@ -0,0 +1,99 @@ +package cz.crcs.ectester.common.util; + +import cz.crcs.ectester.common.output.TeeOutputStream; + +import java.io.*; +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.util.LinkedList; +import java.util.List; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class FileUtil { + private static Path appData = null; + public static String LIB_RESOURCE_DIR = "/cz/crcs/ectester/standalone/libs/jni/"; + + public static OutputStream openStream(String[] files) throws FileNotFoundException { + if (files == null) { + return null; + } + List<OutputStream> outs = new LinkedList<>(); + for (String fileOut : files) { + outs.add(new FileOutputStream(fileOut)); + } + return new TeeOutputStream(outs.toArray(new OutputStream[0])); + } + + public static OutputStreamWriter openFiles(String[] files) throws FileNotFoundException { + if (files == null) { + return null; + } + return new OutputStreamWriter(openStream(files)); + } + + public static Path getAppData() { + if (appData != null) { + return appData; + } + + if (System.getProperty("os.name").startsWith("Windows")) { + appData = Paths.get(System.getenv("AppData")); + } else { + 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"); + } + } + return appData; + } + + public static boolean isNewer(URLConnection jarConn, Path realPath) throws IOException { + if (realPath.toFile().isFile()) { + long jarModified = jarConn.getLastModified(); + long realModified = Files.getLastModifiedTime(realPath).toMillis(); + return jarModified > realModified; + } + return true; + } + + public static boolean writeNewer(String resourcePath, Path outPath) throws IOException { + URL reqURL = FileUtil.class.getResource(resourcePath); + if (reqURL == null) { + return false; + } + URLConnection reqConn = reqURL.openConnection(); + if (isNewer(reqConn, outPath)) { + Files.copy(reqConn.getInputStream(), outPath, StandardCopyOption.REPLACE_EXISTING); + } + reqConn.getInputStream().close(); + return true; + } + + public static Path getLibDir() { + return getAppData().resolve("ECTesterStandalone"); + } + + public static Path getRequirementsDir() { + return getLibDir().resolve("lib"); + } + + public static String getLibSuffix() { + if (System.getProperty("os.name").startsWith("Windows")) { + return "dll"; + } else { + return "so"; + } + } +} diff --git a/common/src/main/java/cz/crcs/ectester/common/util/Util.java b/common/src/main/java/cz/crcs/ectester/common/util/Util.java new file mode 100644 index 0000000..5b0cd79 --- /dev/null +++ b/common/src/main/java/cz/crcs/ectester/common/util/Util.java @@ -0,0 +1,28 @@ +package cz.crcs.ectester.common.util; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class Util { + public static long convertTime(long nanos, String timeUnit) { + switch (timeUnit) { + default: + case "nano": + return nanos; + case "micro": + return nanos / 1000; + case "milli": + return nanos / 1000000; + } + } + + public static int getVersion() { + String version = System.getProperty("java.version"); + if(version.startsWith("1.")) { + version = version.substring(2, 3); + } else { + int dot = version.indexOf("."); + if(dot != -1) { version = version.substring(0, dot); } + } return Integer.parseInt(version); + } +} diff --git a/common/src/main/java/cz/crcs/ectester/data/EC_Store.java b/common/src/main/java/cz/crcs/ectester/data/EC_Store.java new file mode 100644 index 0000000..ad25b1d --- /dev/null +++ b/common/src/main/java/cz/crcs/ectester/data/EC_Store.java @@ -0,0 +1,404 @@ +package cz.crcs.ectester.data; + +import cz.crcs.ectester.common.ec.*; +import cz.crcs.ectester.common.util.Util; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.ErrorHandler; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; +import org.xml.sax.ext.EntityResolver2; + +import javax.xml.XMLConstants; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.validation.Schema; +import javax.xml.validation.SchemaFactory; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.*; +import java.util.function.Function; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class EC_Store { + private DocumentBuilder db; + private Map<String, EC_Category> categories; + private static EC_Store instance; + + private EC_Store() { + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + + try { + SchemaFactory scf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); + Schema sch = scf.newSchema(this.getClass().getResource("/cz/crcs/ectester/data/schema.xsd")); + dbf.setSchema(sch); + dbf.setNamespaceAware(true); + dbf.setIgnoringComments(true); + dbf.setXIncludeAware(true); + dbf.setIgnoringElementContentWhitespace(true); + db = dbf.newDocumentBuilder(); + db.setErrorHandler(new ErrorHandler() { + @Override + public void warning(SAXParseException exception) throws SAXException { + System.err.println("EC_Store | Warning : " + exception); + } + + @Override + public void error(SAXParseException exception) throws SAXException { + System.err.println("EC_Store | Error : " + exception); + } + + @Override + public void fatalError(SAXParseException exception) throws SAXException { + System.err.println("EC_Store | Fatal : " + exception); + throw new SAXException(exception); + } + }); + db.setEntityResolver(new EntityResolver2() { + @Override + public InputSource getExternalSubset(String name, String baseURI) throws SAXException, IOException { + return null; + } + + @Override + public InputSource resolveEntity(String name, String publicId, String baseURI, String systemId) throws SAXException, IOException { + InputSource is = new InputSource(); + is.setSystemId(systemId); + + InputStream bs; + // TODO: Figure out if this is correct for the older Java versions or also wrong. + if (Util.getVersion() <= 8) { + bs = getClass().getClass().getResourceAsStream("/cz/crcs/ectester/data/" + systemId); + } else { + bs = getClass().getResourceAsStream("/cz/crcs/ectester/data/" + systemId); + } + is.setByteStream(bs); + return is; + } + + @Override + public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException { + return null; + } + }); + + parse(); + } catch (ParserConfigurationException | SAXException | IOException e) { + e.printStackTrace(); + } + } + + private void parse() throws SAXException, ParserConfigurationException, IOException { + + InputStream categories = this.getClass().getResourceAsStream("/cz/crcs/ectester/data/categories.xml"); + if (categories == null) { + throw new IOException(); + } + Document categoriesDoc = db.parse(categories); + categories.close(); + categoriesDoc.normalize(); + + NodeList catList = categoriesDoc.getElementsByTagName("category"); + + this.categories = new TreeMap<>(); + for (int i = 0; i < catList.getLength(); ++i) { + Node catNode = catList.item(i); + if (catNode instanceof Element) { + Element catElem = (Element) catNode; + Node name = catElem.getElementsByTagName("name").item(0); + Node dir = catElem.getElementsByTagName("directory").item(0); + Node desc = catElem.getElementsByTagName("desc").item(0); + + EC_Category category = parseCategory(name.getTextContent(), dir.getTextContent(), desc.getTextContent()); + this.categories.put(name.getTextContent(), category); + } else { + throw new SAXException("?"); + } + } + } + + private EC_Category parseCategory(String name, String dir, String desc) throws ParserConfigurationException, IOException, SAXException { + + Map<String, EC_Data> objMap = new TreeMap<>(); + + InputStream curves = this.getClass().getResourceAsStream("/cz/crcs/ectester/data/" + dir + "/curves.xml"); + if (curves != null) { + Document curvesDoc = db.parse(curves); + curvesDoc.normalize(); + + NodeList curveList = curvesDoc.getElementsByTagName("curve"); + + for (int i = 0; i < curveList.getLength(); ++i) { + Node curveNode = curveList.item(i); + if (curveNode instanceof Element) { + Element curveElem = (Element) curveNode; + Node id = curveElem.getElementsByTagName("id").item(0); + Node bits = curveElem.getElementsByTagName("bits").item(0); + Node field = curveElem.getElementsByTagName("field").item(0); + + NodeList descc = curveElem.getElementsByTagName("desc"); + String descs = null; + if (descc.getLength() != 0) { + descs = descc.item(0).getTextContent(); + } + + byte alg; + if (field.getTextContent().equalsIgnoreCase("prime")) { + alg = EC_Consts.ALG_EC_FP; + } else { + alg = EC_Consts.ALG_EC_F2M; + } + short bitsize = Short.parseShort(bits.getTextContent()); + + EC_Curve curve = new EC_Curve(id.getTextContent(), bitsize, alg, descs); + + InputStream csv = parseDataElement(dir, curveElem); + if (!curve.readCSV(csv)) { + throw new IOException("Invalid csv data." + id.getTextContent()); + } + csv.close(); + + objMap.put(id.getTextContent(), curve); + } else { + throw new SAXException("?"); + } + } + curves.close(); + } + + InputStream keys = this.getClass().getResourceAsStream("/cz/crcs/ectester/data/" + dir + "/keys.xml"); + if (keys != null) { + Document keysDoc = db.parse(keys); + keysDoc.normalize(); + + NodeList directs = keysDoc.getDocumentElement().getChildNodes(); + for (int i = 0; i < directs.getLength(); ++i) { + Node direct = directs.item(i); + if (direct instanceof Element) { + Element elem = (Element) direct; + + NodeList ids = elem.getElementsByTagName("id"); + if (ids.getLength() != 1) { + throw new SAXException("key no id?"); + } + String id = ids.item(0).getTextContent(); + + EC_Params result = parseKeylike(dir, elem); + + objMap.put(id, result); + } else { + throw new SAXException("?"); + } + } + keys.close(); + } + + InputStream results = this.getClass().getResourceAsStream("/cz/crcs/ectester/data/" + dir + "/results.xml"); + if (results != null) { + Document resultsDoc = db.parse(results); + resultsDoc.normalize(); + + NodeList directs = resultsDoc.getDocumentElement().getChildNodes(); + for (int i = 0; i < directs.getLength(); ++i) { + Node direct = directs.item(i); + if (direct instanceof Element) { + Element elem = (Element) direct; + + NodeList ids = elem.getElementsByTagName("id"); + if (ids.getLength() != 1) { + throw new SAXException("result no id?"); + } + String id = ids.item(0).getTextContent(); + + EC_Data result = parseResultlike(dir, elem); + + objMap.put(id, result); + } else { + throw new SAXException("?"); + } + } + results.close(); + } + + return new EC_Category(name, dir, desc, objMap); + } + + private EC_Data parseResultlike(String dir, Element elem) throws SAXException, IOException { + String tag = elem.getTagName(); + Node id = elem.getElementsByTagName("id").item(0); + + NodeList descc = elem.getElementsByTagName("desc"); + String descs = null; + if (descc.getLength() != 0) { + descs = descc.item(0).getTextContent(); + } + + Node curve = elem.getElementsByTagName("curve").item(0); + + EC_Data result; + if (tag.equals("kaResult")) { + Node ka = elem.getElementsByTagName("ka").item(0); + Node onekey = elem.getElementsByTagName("onekey").item(0); + Node otherkey = elem.getElementsByTagName("otherkey").item(0); + + result = new EC_KAResult(id.getTextContent(), ka.getTextContent(), curve.getTextContent(), onekey.getTextContent(), otherkey.getTextContent(), descs); + } else if (tag.equals("sigResult")) { + Node sig = elem.getElementsByTagName("sig").item(0); + Node signkey = elem.getElementsByTagName("signkey").item(0); + Node verifykey = elem.getElementsByTagName("verifykey").item(0); + NodeList datas = elem.getElementsByTagName("raw"); + String data = null; + if (datas.getLength() != 0) { + data = datas.item(0).getTextContent(); + } + + result = new EC_SigResult(id.getTextContent(), sig.getTextContent(), curve.getTextContent(), signkey.getTextContent(), verifykey.getTextContent(), data, descs); + } else { + throw new SAXException("?"); + } + + InputStream csv = parseDataElement(dir, elem); + if (!result.readCSV(csv)) { + throw new IOException("Invalid csv data. " + id.getTextContent()); + } + csv.close(); + + return result; + } + + private EC_Params parseKeylike(String dir, Element elem) throws SAXException, IOException { + Node id = elem.getElementsByTagName("id").item(0); + Node curve = elem.getElementsByTagName("curve").item(0); + + NodeList desc = elem.getElementsByTagName("desc"); + String descs = null; + if (desc.getLength() != 0) { + descs = desc.item(0).getTextContent(); + } + + EC_Params result; + if (elem.getTagName().equals("pubkey")) { + result = new EC_Key.Public(id.getTextContent(), curve.getTextContent(), descs); + } else if (elem.getTagName().equals("privkey")) { + result = new EC_Key.Private(id.getTextContent(), curve.getTextContent(), descs); + } else if (elem.getTagName().equals("keypair")) { + result = new EC_Keypair(id.getTextContent(), curve.getTextContent(), descs); + } else { + throw new SAXException("?"); + } + + InputStream csv = parseDataElement(dir, elem); + if (!result.readCSV(csv)) { + throw new IOException("Invalid CSV data. " + id.getTextContent()); + } + csv.close(); + + return result; + } + + private InputStream parseDataElement(String dir, Element elem) throws SAXException { + NodeList file = elem.getElementsByTagName("file"); + NodeList inline = elem.getElementsByTagName("inline"); + + InputStream csv; + if (file.getLength() == 1) { + csv = this.getClass().getResourceAsStream("/cz/crcs/ectester/data/" + dir + "/" + file.item(0).getTextContent()); + } else if (inline.getLength() == 1) { + csv = new ByteArrayInputStream(inline.item(0).getTextContent().getBytes()); + } else { + throw new SAXException("?"); + } + return csv; + } + + public Map<String, EC_Category> getCategories() { + return Collections.unmodifiableMap(categories); + } + + public EC_Category getCategory(String category) { + return categories.get(category); + } + + public Map<String, EC_Data> getObjects(String category) { + EC_Category cat = categories.get(category); + if (cat != null) { + return cat.getObjects(); + } + return null; + } + + public <T extends EC_Data> Map<String, T> getObjects(Class<T> objClass, String category) { + EC_Category cat = categories.get(category); + if (cat != null) { + return cat.getObjects(objClass); + } + return null; + } + + public <T extends EC_Data> T getObject(Class<T> objClass, String category, String id) { + EC_Category cat = categories.get(category); + if (cat != null) { + return cat.getObject(objClass, id); + } + return null; + } + + public <T extends EC_Data> T getObject(Class<T> objClass, String query) { + int split = query.indexOf("/"); + if (split < 0) { + return null; + } + return getObject(objClass, query.substring(0, split), query.substring(split + 1)); + } + + private static <T extends EC_Data> Map<EC_Curve, List<T>> mapKeyToCurve(Collection<T> data, Function<T, String> getter) { + Map<EC_Curve, List<T>> curves = new TreeMap<>(); + for (T item : data) { + EC_Curve curve = EC_Store.getInstance().getObject(EC_Curve.class, getter.apply(item)); + List<T> curveKeys = curves.getOrDefault(curve, new LinkedList<>()); + curveKeys.add(item); + curves.putIfAbsent(curve, curveKeys); + } + for (List<T> keyList : curves.values()) { + Collections.sort(keyList); + } + return curves; + } + + public static <T extends EC_Key> Map<EC_Curve, List<T>> mapKeyToCurve(Collection<T> keys) { + return mapKeyToCurve(keys, EC_Key::getCurve); + } + + public static Map<EC_Curve, List<EC_KAResult>> mapResultToCurve(Collection<EC_KAResult> results) { + return mapKeyToCurve(results, EC_KAResult::getCurve); + } + + public static <T extends EC_Data> Map<String, List<T>> mapToPrefix(Collection<T> data) { + Map<String, List<T>> groups = new TreeMap<>(); + for (T item : data) { + String prefix = item.getId().split("/")[0]; + List<T> group = groups.getOrDefault(prefix, new LinkedList<>()); + group.add(item); + groups.putIfAbsent(prefix, group); + } + for (List<T> itemList : groups.values()) { + Collections.sort(itemList); + } + return groups; + } + + public static EC_Store getInstance() { + if (instance == null) { + instance = new EC_Store(); + } + return instance; + } + +} |
