aboutsummaryrefslogtreecommitdiff
path: root/src/cz/crcs/ectester/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/cz/crcs/ectester/common')
-rw-r--r--src/cz/crcs/ectester/common/cli/CLITools.java7
-rw-r--r--src/cz/crcs/ectester/common/cli/Colors.java97
-rw-r--r--src/cz/crcs/ectester/common/cli/ParserOptions.java13
-rw-r--r--src/cz/crcs/ectester/common/cli/TreeParser.java2
-rw-r--r--src/cz/crcs/ectester/common/ec/EC_Category.java12
-rw-r--r--src/cz/crcs/ectester/common/ec/EC_Curve.java10
-rw-r--r--src/cz/crcs/ectester/common/ec/EC_Data.java52
-rw-r--r--src/cz/crcs/ectester/common/ec/EC_KAResult.java2
-rw-r--r--src/cz/crcs/ectester/common/ec/EC_Key.java4
-rw-r--r--src/cz/crcs/ectester/common/ec/EC_Keypair.java2
-rw-r--r--src/cz/crcs/ectester/common/ec/EC_Params.java13
-rw-r--r--src/cz/crcs/ectester/common/output/BaseTextTestWriter.java103
-rw-r--r--src/cz/crcs/ectester/common/output/BaseXMLTestWriter.java75
-rw-r--r--src/cz/crcs/ectester/common/output/BaseYAMLTestWriter.java67
-rw-r--r--src/cz/crcs/ectester/common/output/TeeTestWriter.java43
-rw-r--r--src/cz/crcs/ectester/common/output/TestWriter.java26
-rw-r--r--src/cz/crcs/ectester/common/test/BaseTestable.java14
-rw-r--r--src/cz/crcs/ectester/common/test/CompoundTest.java151
-rw-r--r--src/cz/crcs/ectester/common/test/Result.java28
-rw-r--r--src/cz/crcs/ectester/common/test/SimpleTest.java21
-rw-r--r--src/cz/crcs/ectester/common/test/Test.java59
-rw-r--r--src/cz/crcs/ectester/common/test/TestException.java7
-rw-r--r--src/cz/crcs/ectester/common/test/TestSuite.java76
-rw-r--r--src/cz/crcs/ectester/common/test/TestSuiteException.java13
-rw-r--r--src/cz/crcs/ectester/common/test/Testable.java7
-rw-r--r--src/cz/crcs/ectester/common/util/ByteUtil.java51
-rw-r--r--src/cz/crcs/ectester/common/util/CardUtil.java378
-rw-r--r--src/cz/crcs/ectester/common/util/ECUtil.java10
-rw-r--r--src/cz/crcs/ectester/common/util/FileUtil.java33
29 files changed, 1089 insertions, 287 deletions
diff --git a/src/cz/crcs/ectester/common/cli/CLITools.java b/src/cz/crcs/ectester/common/cli/CLITools.java
index 91f121f..a9d036e 100644
--- a/src/cz/crcs/ectester/common/cli/CLITools.java
+++ b/src/cz/crcs/ectester/common/cli/CLITools.java
@@ -22,7 +22,7 @@ public class CLITools {
public static void help(String prog, String header, Options options, String footer, boolean usage) {
HelpFormatter help = new HelpFormatter();
help.setOptionComparator(null);
- help.printHelp(prog, header, options, footer, usage);
+ help.printHelp(Colors.bold(prog), header, options, footer, usage);
}
private static void help(HelpFormatter help, PrintWriter pw, CommandLineParser cli, Options opts, int depth) {
@@ -37,7 +37,8 @@ public class CLITools {
}
tp.getParsers().forEach((key, value) -> {
pw.println();
- help.printWrapped(pw, HelpFormatter.DEFAULT_WIDTH, String.format("%" + depth + "s" + key + ":", " "));
+ String description = value.getDescription() == null ? "" : " | " + value.getDescription() + " |";
+ help.printWrapped(pw, HelpFormatter.DEFAULT_WIDTH, String.format("%" + depth + "s" + key + ":" + description, " "));
CLITools.help(help, pw, value.getParser(), value.getOptions(), depth + 1);
});
}
@@ -96,7 +97,7 @@ public class CLITools {
StringWriter uw = new StringWriter();
PrintWriter upw = new PrintWriter(uw);
usage(help, upw, baseParser, baseOpts);
- pw.print("usage: " + prog);
+ pw.print("usage: " + Colors.bold(prog));
help.printWrapped(pw, HelpFormatter.DEFAULT_WIDTH, uw.toString());
upw.close();
pw.println();
diff --git a/src/cz/crcs/ectester/common/cli/Colors.java b/src/cz/crcs/ectester/common/cli/Colors.java
new file mode 100644
index 0000000..7601088
--- /dev/null
+++ b/src/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/src/cz/crcs/ectester/common/cli/ParserOptions.java b/src/cz/crcs/ectester/common/cli/ParserOptions.java
index ee2097e..7300cbb 100644
--- a/src/cz/crcs/ectester/common/cli/ParserOptions.java
+++ b/src/cz/crcs/ectester/common/cli/ParserOptions.java
@@ -3,25 +3,22 @@ package cz.crcs.ectester.common.cli;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.Options;
-import java.util.Collections;
-import java.util.List;
-
/**
* @author Jan Jancar johny@neuromancer.sk
*/
public class ParserOptions {
private CommandLineParser parser;
private Options options;
- private List<Argument> arguments;
+ private String description;
public ParserOptions(CommandLineParser parser, Options options) {
this.parser = parser;
this.options = options;
}
- public ParserOptions(CommandLineParser parser, Options options, List<Argument> arguments) {
+ public ParserOptions(CommandLineParser parser, Options options, String description) {
this(parser, options);
- this.arguments = arguments;
+ this.description = description;
}
public CommandLineParser getParser() {
@@ -32,7 +29,7 @@ public class ParserOptions {
return options;
}
- public List<Argument> getArguments() {
- return Collections.unmodifiableList(arguments);
+ public String getDescription() {
+ return description;
}
}
diff --git a/src/cz/crcs/ectester/common/cli/TreeParser.java b/src/cz/crcs/ectester/common/cli/TreeParser.java
index f1a1980..23f59b1 100644
--- a/src/cz/crcs/ectester/common/cli/TreeParser.java
+++ b/src/cz/crcs/ectester/common/cli/TreeParser.java
@@ -82,7 +82,7 @@ public class TreeParser implements CommandLineParser {
}
} else {
if (required) {
- throw new MissingOptionException(new ArrayList(parsers.keySet()));
+ throw new MissingOptionException(new ArrayList<>(parsers.keySet()));
}
}
diff --git a/src/cz/crcs/ectester/common/ec/EC_Category.java b/src/cz/crcs/ectester/common/ec/EC_Category.java
index 32a788d..9c65f3b 100644
--- a/src/cz/crcs/ectester/common/ec/EC_Category.java
+++ b/src/cz/crcs/ectester/common/ec/EC_Category.java
@@ -1,5 +1,7 @@
package cz.crcs.ectester.common.ec;
+import cz.crcs.ectester.common.cli.Colors;
+
import java.util.Collections;
import java.util.Map;
import java.util.Objects;
@@ -72,13 +74,13 @@ public class EC_Category {
@Override
public String toString() {
StringBuilder out = new StringBuilder();
- out.append("\t- ").append(name).append((desc == null || desc.equals("")) ? "" : ": " + desc);
+ out.append("\t- ").append(Colors.bold(name)).append((desc == null || desc.equals("")) ? "" : ": " + desc);
out.append(System.lineSeparator());
Map<String, EC_Curve> curves = getObjects(EC_Curve.class);
int size = curves.size();
if (size > 0) {
- out.append("\t\tCurves: ");
+ out.append(Colors.bold("\t\tCurves: "));
for (Map.Entry<String, EC_Curve> curve : curves.entrySet()) {
out.append(curve.getKey());
size--;
@@ -91,7 +93,7 @@ public class EC_Category {
Map<String, EC_Key> keys = getObjects(EC_Key.class);
size = keys.size();
if (size > 0) {
- out.append("\t\tKeys: ");
+ out.append(Colors.bold("\t\tKeys: "));
for (Map.Entry<String, EC_Key> key : keys.entrySet()) {
out.append(key.getKey());
size--;
@@ -104,7 +106,7 @@ public class EC_Category {
Map<String, EC_Keypair> keypairs = getObjects(EC_Keypair.class);
size = keypairs.size();
if (size > 0) {
- out.append("\t\tKeypairs: ");
+ out.append(Colors.bold("\t\tKeypairs: "));
for (Map.Entry<String, EC_Keypair> key : keypairs.entrySet()) {
out.append(key.getKey());
size--;
@@ -117,7 +119,7 @@ public class EC_Category {
Map<String, EC_KAResult> results = getObjects(EC_KAResult.class);
size = results.size();
if (size > 0) {
- out.append("\t\tResults: ");
+ out.append(Colors.bold("\t\tResults: "));
for (Map.Entry<String, EC_KAResult> result : results.entrySet()) {
out.append(result.getKey());
size--;
diff --git a/src/cz/crcs/ectester/common/ec/EC_Curve.java b/src/cz/crcs/ectester/common/ec/EC_Curve.java
index 173fd29..6c0d060 100644
--- a/src/cz/crcs/ectester/common/ec/EC_Curve.java
+++ b/src/cz/crcs/ectester/common/ec/EC_Curve.java
@@ -51,10 +51,10 @@ public class EC_Curve extends EC_Params {
@Override
public String toString() {
- return "<" + getId() + "> " + (field == KeyPair.ALG_EC_FP ? "Prime" : "Binary") + " field Elliptic curve (" + String.valueOf(bits) + "b)" + (desc == null ? "" : ": " + desc);
+ return "<" + getId() + "> " + (field == KeyPair.ALG_EC_FP ? "Prime" : "Binary") + " field Elliptic curve (" + String.valueOf(bits) + "b)" + (desc == null ? "" : ": " + desc) + System.lineSeparator() + super.toString();
}
- public ECParameterSpec toSpec() {
+ public EllipticCurve toCurve() {
ECField field;
if (this.field == KeyPair.ALG_EC_FP) {
field = new ECFieldFp(new BigInteger(1, getData(0)));
@@ -71,7 +71,11 @@ public class EC_Curve extends EC_Params {
BigInteger a = new BigInteger(1, getParam(EC_Consts.PARAMETER_A)[0]);
BigInteger b = new BigInteger(1, getParam(EC_Consts.PARAMETER_B)[0]);
- EllipticCurve curve = new EllipticCurve(field, a, b);
+ return new EllipticCurve(field, a, b);
+ }
+
+ public ECParameterSpec toSpec() {
+ EllipticCurve curve = toCurve();
byte[][] G = getParam(EC_Consts.PARAMETER_G);
BigInteger gx = new BigInteger(1, G[0]);
diff --git a/src/cz/crcs/ectester/common/ec/EC_Data.java b/src/cz/crcs/ectester/common/ec/EC_Data.java
index c048ef7..abe6e93 100644
--- a/src/cz/crcs/ectester/common/ec/EC_Data.java
+++ b/src/cz/crcs/ectester/common/ec/EC_Data.java
@@ -14,7 +14,7 @@ import java.util.regex.Pattern;
*
* @author Jan Jancar johny@neuromancer.sk
*/
-public abstract class EC_Data {
+public abstract class EC_Data implements Comparable<EC_Data> {
String id;
int count;
byte[][] data;
@@ -123,7 +123,7 @@ public abstract class EC_Data {
public boolean readCSV(InputStream in) {
Scanner s = new Scanner(in);
- s.useDelimiter(",|;");
+ s.useDelimiter("[,;]");
List<String> data = new LinkedList<>();
while (s.hasNext()) {
String field = s.next();
@@ -214,4 +214,52 @@ public abstract class EC_Data {
}
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/src/cz/crcs/ectester/common/ec/EC_KAResult.java b/src/cz/crcs/ectester/common/ec/EC_KAResult.java
index 8a5fcb4..4e97950 100644
--- a/src/cz/crcs/ectester/common/ec/EC_KAResult.java
+++ b/src/cz/crcs/ectester/common/ec/EC_KAResult.java
@@ -59,7 +59,7 @@ public class EC_KAResult extends EC_Data {
@Override
public String toString() {
- return "<" + getId() + "> " + ka + " result over " + curve + ", " + oneKey + " + " + otherKey + (desc == null ? "" : ": " + desc);
+ return "<" + getId() + "> " + ka + " result over " + curve + ", " + oneKey + " + " + otherKey + (desc == null ? "" : ": " + desc) + System.lineSeparator() + super.toString();
}
}
diff --git a/src/cz/crcs/ectester/common/ec/EC_Key.java b/src/cz/crcs/ectester/common/ec/EC_Key.java
index a34b0e7..754775d 100644
--- a/src/cz/crcs/ectester/common/ec/EC_Key.java
+++ b/src/cz/crcs/ectester/common/ec/EC_Key.java
@@ -54,7 +54,7 @@ public class EC_Key extends EC_Params {
@Override
public String toString() {
- return "<" + getId() + "> EC Public key, over " + getCurve() + (getDesc() == null ? "" : ": " + getDesc());
+ return "<" + getId() + "> EC Public key, over " + getCurve() + (getDesc() == null ? "" : ": " + getDesc()) + System.lineSeparator() + super.toString();
}
}
@@ -77,7 +77,7 @@ public class EC_Key extends EC_Params {
@Override
public String toString() {
- return "<" + getId() + "> EC Private key, over " + getCurve() + (getDesc() == null ? "" : ": " + getDesc());
+ return "<" + getId() + "> EC Private key, over " + getCurve() + (getDesc() == null ? "" : ": " + getDesc()) + System.lineSeparator() + super.toString();
}
}
}
diff --git a/src/cz/crcs/ectester/common/ec/EC_Keypair.java b/src/cz/crcs/ectester/common/ec/EC_Keypair.java
index 53632cd..24ddba7 100644
--- a/src/cz/crcs/ectester/common/ec/EC_Keypair.java
+++ b/src/cz/crcs/ectester/common/ec/EC_Keypair.java
@@ -36,6 +36,6 @@ public class EC_Keypair extends EC_Params {
@Override
public String toString() {
- return "<" + getId() + "> EC Keypair, over " + curve + (desc == null ? "" : ": " + desc);
+ return "<" + getId() + "> EC Keypair, over " + curve + (desc == null ? "" : ": " + desc) + System.lineSeparator() + super.toString();
}
}
diff --git a/src/cz/crcs/ectester/common/ec/EC_Params.java b/src/cz/crcs/ectester/common/ec/EC_Params.java
index 1c066e7..b08fdfd 100644
--- a/src/cz/crcs/ectester/common/ec/EC_Params.java
+++ b/src/cz/crcs/ectester/common/ec/EC_Params.java
@@ -9,10 +9,11 @@ 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 {
@@ -61,15 +62,15 @@ public class EC_Params extends EC_Data {
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();
+ 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();
+ result[1] = data[i + 1].clone();
break;
}
result = new byte[1][];
@@ -189,6 +190,6 @@ public class EC_Params extends EC_Data {
}
paramMask = (short) (paramMask << 1);
}
- return out.toArray(new String[out.size()]);
+ return out.toArray(new String[0]);
}
}
diff --git a/src/cz/crcs/ectester/common/output/BaseTextTestWriter.java b/src/cz/crcs/ectester/common/output/BaseTextTestWriter.java
index 29eb671..ee55069 100644
--- a/src/cz/crcs/ectester/common/output/BaseTextTestWriter.java
+++ b/src/cz/crcs/ectester/common/output/BaseTextTestWriter.java
@@ -1,16 +1,25 @@
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 = 90;
+ public static int BASE_WIDTH = 105;
public BaseTextTestWriter(PrintStream output) {
this.output = output;
@@ -18,43 +27,76 @@ public abstract class BaseTextTestWriter implements TestWriter {
@Override
public void begin(TestSuite suite) {
- output.println("═══ Running test suite: " + suite.getName() + " ═══");
- output.println("═══ " + suite.getDescription());
+ 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) {
- if (!t.hasRun()) {
- return null;
- }
+ 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() ? " OK " : "NOK ");
- out.append(compound ? "┳ " : "━ ");
- int width = BASE_WIDTH - (prefix.length() + out.length());
+ out.append(t.ok() ? Colors.ok(" OK ") : Colors.error("NOK "));
+ out.append(compound ? (prefix.equals("") ? "╋ " : "┳ ") : "━ ");
+ int width = BASE_WIDTH - (prefix.length() + 6);
String widthSpec = "%-" + String.valueOf(width) + "s";
- out.append(String.format(widthSpec, t.getDescription()));
+ String desc = ((prefix.equals("")) ? "(" + index + ") " : "") + t.getDescription();
+ out.append(String.format(widthSpec, desc));
out.append(" ┃ ");
- out.append(String.format("%-9s", t.getResultValue().name()));
+ 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(test.getResultCause());
+ out.append(String.valueOf(result.getCause()));
out.append(System.lineSeparator());
- Test[] tests = test.getTests();
+ 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 + " "));
+ out.append(testString(tests[i], prefix + " ", index));
} else {
out.append(prefix).append(" ┣ ");
- out.append(testString(tests[i], prefix + " ┃ "));
+ out.append(testString(tests[i], prefix + " ┃ ", index));
}
if (i != tests.length - 1) {
@@ -62,17 +104,38 @@ public abstract class BaseTextTestWriter implements TestWriter {
}
}
} else {
- SimpleTest test = (SimpleTest) t;
+ SimpleTest<? extends BaseTestable> test = (SimpleTest<? extends BaseTestable>) t;
out.append(testableString(test.getTestable()));
}
- return out.toString();
+ return line + out.toString();
}
@Override
- public void outputTest(Test t) {
+ public void outputTest(Test t, int index) {
if (!t.hasRun())
return;
- output.println(testString(t, ""));
+ output.println(testString(t, "", index));
+ output.flush();
+ }
+
+ private String errorString(Throwable error) {
+ StringBuilder sb = new StringBuilder();
+ for (Throwable t = error; t != null; t = t.getCause()) {
+ sb.append("═══ ").append(t.toString()).append(" ═══");
+ sb.append(System.lineSeparator());
+ }
+ sb.append("═══ ═══").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();
}
diff --git a/src/cz/crcs/ectester/common/output/BaseXMLTestWriter.java b/src/cz/crcs/ectester/common/output/BaseXMLTestWriter.java
index f3e9411..53970dd 100644
--- a/src/cz/crcs/ectester/common/output/BaseXMLTestWriter.java
+++ b/src/cz/crcs/ectester/common/output/BaseXMLTestWriter.java
@@ -15,6 +15,9 @@ 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
@@ -24,6 +27,7 @@ public abstract class BaseXMLTestWriter implements TestWriter {
private DocumentBuilder db;
protected Document doc;
private Node root;
+ private Node tests;
public BaseXMLTestWriter(OutputStream output) throws ParserConfigurationException {
this.output = output;
@@ -35,28 +39,65 @@ public abstract class BaseXMLTestWriter implements TestWriter {
doc = db.newDocument();
Element rootElem = doc.createElement("testSuite");
rootElem.setAttribute("name", suite.getName());
- rootElem.setAttribute("desc", suite.getDescription());
+ 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 Element testElement(Test t) {
+ 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.getTests()) {
- testElem.appendChild(testElement(innerTest));
+ for (Test innerTest : test.getStartedTests()) {
+ testElem.appendChild(testElement(innerTest, -1));
}
} else {
- SimpleTest test = (SimpleTest) t;
+ SimpleTest<? extends BaseTestable> test = (SimpleTest<? extends BaseTestable>) t;
testElem = testableElement(test.getTestable());
}
@@ -64,26 +105,26 @@ public abstract class BaseXMLTestWriter implements TestWriter {
description.setTextContent(t.getDescription());
testElem.appendChild(description);
- Element result = doc.createElement("result");
- Element ok = doc.createElement("ok");
- ok.setTextContent(String.valueOf(t.ok()));
- Element value = doc.createElement("value");
- value.setTextContent(t.getResultValue().name());
- Element cause = doc.createElement("cause");
- cause.setTextContent(t.getResultCause());
- result.appendChild(ok);
- result.appendChild(value);
- result.appendChild(cause);
+ 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) {
+ public void outputTest(Test t, int index) {
if (!t.hasRun())
return;
- root.appendChild(testElement(t));
+ tests.appendChild(testElement(t, index));
+ }
+
+ @Override
+ public void outputError(Test t, Throwable cause, int index) {
+ tests.appendChild(testElement(t, index));
}
@Override
diff --git a/src/cz/crcs/ectester/common/output/BaseYAMLTestWriter.java b/src/cz/crcs/ectester/common/output/BaseYAMLTestWriter.java
index 0769e83..e054563 100644
--- a/src/cz/crcs/ectester/common/output/BaseYAMLTestWriter.java
+++ b/src/cz/crcs/ectester/common/output/BaseYAMLTestWriter.java
@@ -5,10 +5,9 @@ import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;
import java.io.PrintStream;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.*;
/**
* @author Jan Jancar johny@neuromancer.sk
@@ -26,12 +25,15 @@ public abstract class BaseYAMLTestWriter implements TestWriter {
@Override
public void begin(TestSuite suite) {
output.println("---");
- testRun = new HashMap<>();
- testSuite = new HashMap<>();
+ testRun = new LinkedHashMap<>();
+ testSuite = new LinkedHashMap<>();
tests = new LinkedList<>();
testSuite.put("name", suite.getName());
- testSuite.put("desc", suite.getDescription());
+ 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);
@@ -41,37 +43,64 @@ public abstract class BaseYAMLTestWriter implements TestWriter {
abstract protected Map<String, Object> deviceObject(TestSuite suite);
- private Map<String, Object> testObject(Test t) {
+ 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.getTests()) {
- innerTests.add(testObject(innerTest));
+ for (Test innerTest : test.getStartedTests()) {
+ innerTests.add(testObject(innerTest, -1));
}
testObj.put("tests", innerTests);
} else {
- SimpleTest test = (SimpleTest) t;
+ SimpleTest<? extends BaseTestable> test = (SimpleTest<? extends BaseTestable>) t;
testObj = testableObject(test.getTestable());
}
testObj.put("desc", t.getDescription());
- Map<String, Object> result = new HashMap<>();
- result.put("ok", t.ok());
- result.put("value", t.getResultValue().name());
- result.put("cause", t.getResultCause());
- testObj.put("result", result);
+ testObj.put("result", resultObject(t.getResult()));
+ if (index != -1) {
+ testObj.put("index", index);
+ }
return testObj;
}
@Override
- public void outputTest(Test t) {
+ public void outputTest(Test t, int index) {
if (!t.hasRun())
return;
- tests.add(testObject(t));
+ tests.add(testObject(t, index));
+ }
+
+ @Override
+ public void outputError(Test t, Throwable cause, int index) {
+ tests.add(testObject(t, index));
}
@Override
@@ -81,7 +110,7 @@ public abstract class BaseYAMLTestWriter implements TestWriter {
options.setPrettyFlow(true);
Yaml yaml = new Yaml(options);
- Map<String, Object> result = new HashMap<>();
+ Map<String, Object> result = new LinkedHashMap<>();
result.put("testRun", testRun);
String out = yaml.dump(result);
diff --git a/src/cz/crcs/ectester/common/output/TeeTestWriter.java b/src/cz/crcs/ectester/common/output/TeeTestWriter.java
new file mode 100644
index 0000000..58a0a15
--- /dev/null
+++ b/src/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/src/cz/crcs/ectester/common/output/TestWriter.java b/src/cz/crcs/ectester/common/output/TestWriter.java
index 0ecfd5a..eb95804 100644
--- a/src/cz/crcs/ectester/common/output/TestWriter.java
+++ b/src/cz/crcs/ectester/common/output/TestWriter.java
@@ -7,9 +7,33 @@ 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);
- void outputTest(Test t);
+ /**
+ *
+ * @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/src/cz/crcs/ectester/common/test/BaseTestable.java b/src/cz/crcs/ectester/common/test/BaseTestable.java
index a4b9a00..3c304d9 100644
--- a/src/cz/crcs/ectester/common/test/BaseTestable.java
+++ b/src/cz/crcs/ectester/common/test/BaseTestable.java
@@ -3,10 +3,11 @@ package cz.crcs.ectester.common.test;
/**
* @author Jan Jancar johny@neuromancer.sk
*/
-public abstract class BaseTestable implements Testable {
+public abstract class BaseTestable implements Testable, Cloneable {
protected boolean hasRun;
protected boolean ok;
protected boolean error;
+ protected Object errorCause;
@Override
public boolean hasRun() {
@@ -24,9 +25,20 @@ public abstract class BaseTestable implements Testable {
}
@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/src/cz/crcs/ectester/common/test/CompoundTest.java b/src/cz/crcs/ectester/common/test/CompoundTest.java
index 10ecf9c..ba4ad4f 100644
--- a/src/cz/crcs/ectester/common/test/CompoundTest.java
+++ b/src/cz/crcs/ectester/common/test/CompoundTest.java
@@ -2,6 +2,7 @@ package cz.crcs.ectester.common.test;
import java.util.Arrays;
import java.util.Objects;
+import java.util.function.Consumer;
import java.util.function.Function;
/**
@@ -9,30 +10,64 @@ import java.util.function.Function;
*
* @author Jan Jancar johny@neuromancer.sk
*/
-public class CompoundTest extends Test {
- private Function<Test[], Result> callback;
+public class CompoundTest extends Test implements Cloneable {
+ private Function<Test[], Result> resultCallback;
+ private Consumer<Test[]> runCallback;
private Test[] tests;
- private String description;
+ private String description = "";
- private CompoundTest(Function<Test[], Result> callback, Test... tests) {
- this.callback = callback;
+ 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, String descripiton, Test... tests) {
- this(callback, tests);
+ 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, 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, description, tests);
+ return new CompoundTest(callback, RUN_ALL, description, tests);
}
- public static CompoundTest all(Result.ExpectedValue what, Test... all) {
+ 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()) {
@@ -40,7 +75,11 @@ public class CompoundTest extends Test {
}
}
return new Result(Result.Value.SUCCESS, "All sub-tests had the expected result.");
- }, all);
+ }, 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) {
@@ -49,7 +88,47 @@ public class CompoundTest extends Test {
return result;
}
- public static CompoundTest any(Result.ExpectedValue what, Test... any) {
+ 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()) {
@@ -57,7 +136,21 @@ public class CompoundTest extends Test {
}
}
return new Result(Result.Value.FAILURE, "None of the sub-tests had the expected result.");
- }, any);
+ }, 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) {
@@ -74,7 +167,7 @@ public class CompoundTest extends Test {
}
}
return new Result(Result.Value.SUCCESS, "All sub-tests matched the expected mask.");
- }, masked);
+ }, RUN_ALL, masked);
}
public static CompoundTest mask(Result.ExpectedValue[] results, String description, Test... masked) {
@@ -84,20 +177,25 @@ public class CompoundTest extends Test {
}
public Test[] getTests() {
- return tests;
+ return tests.clone();
}
- @Override
- public void run() throws TestException {
- if (hasRun)
- return;
+ public Test[] getRunTests() {
+ return Arrays.stream(tests).filter(Test::hasRun).toArray(Test[]::new);
+ }
- for (Test test : tests) {
- test.run();
- }
+ public Test[] getStartedTests() {
+ return Arrays.stream(tests).filter(Test::hasStarted).toArray(Test[]::new);
+ }
- result = callback.apply(tests);
- this.hasRun = true;
+ 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) {
@@ -108,4 +206,9 @@ public class CompoundTest extends Test {
public String getDescription() {
return description;
}
+
+ @Override
+ public CompoundTest clone() throws CloneNotSupportedException {
+ return (CompoundTest) super.clone();
+ }
}
diff --git a/src/cz/crcs/ectester/common/test/Result.java b/src/cz/crcs/ectester/common/test/Result.java
index 11fcb4d..f065f9c 100644
--- a/src/cz/crcs/ectester/common/test/Result.java
+++ b/src/cz/crcs/ectester/common/test/Result.java
@@ -8,13 +8,13 @@ package cz.crcs.ectester.common.test;
public class Result {
private Value value;
- private String cause;
+ private Object cause;
public Result(Value value) {
this.value = value;
}
- public Result(Value value, String cause) {
+ public Result(Value value, Object cause) {
this(value);
this.cause = cause;
}
@@ -23,7 +23,7 @@ public class Result {
return value;
}
- public String getCause() {
+ public Object getCause() {
return cause;
}
@@ -49,18 +49,24 @@ public class Result {
* A result value of a Test.
*/
public enum Value {
- SUCCESS(true),
- FAILURE(false),
- UXSUCCESS(false),
- XFAILURE(true),
- ERROR(false);
+ 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:
@@ -68,7 +74,7 @@ public class Result {
case FAILURE:
return successful ? UXSUCCESS : XFAILURE;
case ANY:
- return SUCCESS;
+ return successful ? SUCCESS : XFAILURE;
}
return SUCCESS;
}
@@ -83,6 +89,10 @@ public class Result {
public boolean ok() {
return ok;
}
+
+ public String description() {
+ return desc;
+ }
}
/**
diff --git a/src/cz/crcs/ectester/common/test/SimpleTest.java b/src/cz/crcs/ectester/common/test/SimpleTest.java
index f68320a..d2b3e94 100644
--- a/src/cz/crcs/ectester/common/test/SimpleTest.java
+++ b/src/cz/crcs/ectester/common/test/SimpleTest.java
@@ -4,11 +4,17 @@ package cz.crcs.ectester.common.test;
* @param <T>
* @author Jan Jancar johny@neuromancer.sk
*/
-public abstract class SimpleTest<T extends BaseTestable> extends Test {
+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;
}
@@ -16,4 +22,17 @@ public abstract class SimpleTest<T extends BaseTestable> extends Test {
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/src/cz/crcs/ectester/common/test/Test.java b/src/cz/crcs/ectester/common/test/Test.java
index 3d0baf6..8bf9502 100644
--- a/src/cz/crcs/ectester/common/test/Test.java
+++ b/src/cz/crcs/ectester/common/test/Test.java
@@ -7,33 +7,17 @@ import static cz.crcs.ectester.common.test.Result.Value;
*
* @author Jan Jancar johny@neuromancer.sk
*/
-public abstract class Test implements Testable {
+public abstract class Test implements Testable, Cloneable {
protected boolean hasRun;
+ protected boolean hasStarted;
protected Result result;
public Result getResult() {
- if (!hasRun) {
- return null;
- }
return result;
}
- public Value getResultValue() {
- if (!hasRun) {
- return null;
- }
- return result.getValue();
- }
-
- public String getResultCause() {
- if (!hasRun) {
- return null;
- }
- return result.getCause();
- }
-
public boolean ok() {
- if (!hasRun) {
+ if (result == null) {
return true;
}
return result.ok();
@@ -41,26 +25,59 @@ public abstract class Test implements Testable {
@Override
public boolean error() {
- if (!hasRun) {
+ 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 abstract void run() throws TestException;
+ 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/src/cz/crcs/ectester/common/test/TestException.java b/src/cz/crcs/ectester/common/test/TestException.java
index 008e9f6..291a073 100644
--- a/src/cz/crcs/ectester/common/test/TestException.java
+++ b/src/cz/crcs/ectester/common/test/TestException.java
@@ -2,11 +2,12 @@ package cz.crcs.ectester.common.test;
/**
* A TestException is an Exception that can be thrown during the running of a Testable,
- * or a TestSuite. It means that the Testable/TestSuite encountered an unexpected error
- * during it's run which points to an error in ECTester or it's runtime environment.cd
+ * or a Test. It means that the Testable/TestSuite encountered an unexpected error
+ * and has to terminate.
+ *
* @author Jan Jancar johny@neuromancer.sk
*/
-public class TestException extends Exception {
+public class TestException extends RuntimeException {
public TestException(Throwable e) {
super(e);
}
diff --git a/src/cz/crcs/ectester/common/test/TestSuite.java b/src/cz/crcs/ectester/common/test/TestSuite.java
index f4f30ee..b12680a 100644
--- a/src/cz/crcs/ectester/common/test/TestSuite.java
+++ b/src/cz/crcs/ectester/common/test/TestSuite.java
@@ -1,56 +1,100 @@
package cz.crcs.ectester.common.test;
import cz.crcs.ectester.common.output.TestWriter;
-import cz.crcs.ectester.data.EC_Store;
-
-import java.util.Collections;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.stream.Collectors;
/**
* @author Jan Jancar johny@neuromancer.sk
*/
public abstract class TestSuite {
protected String name;
- protected String description;
- protected TestWriter writer;
+ 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) {
+ public TestSuite(TestWriter writer, String name, String... description) {
this.writer = writer;
this.name = name;
this.description = description;
}
- public void run() throws TestException {
+ /**
+ * 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) {
- throw new TestException(e);
+ writer.end();
+ throw new TestSuiteException(e);
}
writer.end();
}
- protected Test runTest(Test t) throws TestException {
+ /**
+ * 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;
}
- protected Test doTest(Test t) throws TestException {
- t.run();
- writer.outputTest(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() {
+ public String[] getDescription() {
return description;
}
+ public String getTextDescription() {
+ return String.join(System.lineSeparator(), description);
+ }
+
+ public String toString() {
+ return null;
+ }
+
}
diff --git a/src/cz/crcs/ectester/common/test/TestSuiteException.java b/src/cz/crcs/ectester/common/test/TestSuiteException.java
new file mode 100644
index 0000000..cc3cfda
--- /dev/null
+++ b/src/cz/crcs/ectester/common/test/TestSuiteException.java
@@ -0,0 +1,13 @@
+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
+ */
+public class TestSuiteException extends RuntimeException {
+ public TestSuiteException(Throwable e) {
+ super(e);
+ }
+}
diff --git a/src/cz/crcs/ectester/common/test/Testable.java b/src/cz/crcs/ectester/common/test/Testable.java
index 33c9485..7b4545c 100644
--- a/src/cz/crcs/ectester/common/test/Testable.java
+++ b/src/cz/crcs/ectester/common/test/Testable.java
@@ -15,6 +15,11 @@ public interface Testable {
boolean error();
/**
+ * @return The cause of an error, if it happened, otherwise null.
+ */
+ Object errorCause();
+
+ /**
* @return Whether this runnable was run.
*/
boolean hasRun();
@@ -29,5 +34,5 @@ public interface Testable {
*
* @throws TestException If an unexpected exception/error is encountered.
*/
- void run() throws TestException;
+ void run();
}
diff --git a/src/cz/crcs/ectester/common/util/ByteUtil.java b/src/cz/crcs/ectester/common/util/ByteUtil.java
index 90c6eaa..daacabb 100644
--- a/src/cz/crcs/ectester/common/util/ByteUtil.java
+++ b/src/cz/crcs/ectester/common/util/ByteUtil.java
@@ -7,15 +7,27 @@ package cz.crcs.ectester.common.util;
* @author Jan Jancar johny@neuromancer.sk
*/
public class ByteUtil {
+
+ /**
+ * Gen 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));
}
+ /**
+ * 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];
@@ -27,10 +39,17 @@ public class ByteUtil {
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)
@@ -39,10 +58,38 @@ public class ByteUtil {
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();
@@ -125,4 +172,8 @@ public class ByteUtil {
}
return out;
}
+
+ public static byte[] prependLength(byte[] data) {
+ return concatenate(ByteUtil.shortToBytes((short) data.length), data);
+ }
}
diff --git a/src/cz/crcs/ectester/common/util/CardUtil.java b/src/cz/crcs/ectester/common/util/CardUtil.java
index 8285d8b..a628d5b 100644
--- a/src/cz/crcs/ectester/common/util/CardUtil.java
+++ b/src/cz/crcs/ectester/common/util/CardUtil.java
@@ -4,8 +4,10 @@ import cz.crcs.ectester.applet.ECTesterApplet;
import cz.crcs.ectester.applet.EC_Consts;
import javacard.framework.ISO7816;
import javacard.security.CryptoException;
+import javacard.security.KeyPair;
-import static cz.crcs.ectester.applet.ECTesterApplet.*;
+import java.util.LinkedList;
+import java.util.List;
/**
* @author Petr Svenda petr@svenda.com
@@ -15,13 +17,19 @@ public class CardUtil {
public static byte getKA(String name) {
switch (name) {
case "DH":
- case "ECDH":
- return ECTesterApplet.KeyAgreement_ALG_EC_SVDP_DH;
+ return EC_Consts.KeyAgreement_ALG_EC_SVDP_DH;
case "DHC":
- case "ECDHC":
- return ECTesterApplet.KeyAgreement_ALG_EC_SVDP_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 ECTesterApplet.KeyAgreement_ALG_EC_SVDP_DH;
+ return EC_Consts.KeyAgreement_ALG_EC_SVDP_DH;
}
}
@@ -70,75 +78,102 @@ public class CardUtil {
}
public static String getSW(short sw) {
- 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_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 ECTesterApplet.SW_SIG_VERIFY_FAIL:
- return "SIG_VERIFY_FAIL";
- case ECTesterApplet.SW_DH_DHC_MISMATCH:
- return "DH_DHC_MISMATCH";
- case ECTesterApplet.SW_KEYPAIR_NULL:
- return "KEYPAIR_NULL";
- case ECTesterApplet.SW_KA_NULL:
- return "KA_NULL";
- case ECTesterApplet.SW_SIGNATURE_NULL:
- return "SIGNATURE_NULL";
- case ECTesterApplet.SW_OBJECT_NULL:
- return "OBJECT_NULL";
+ 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:
- return "unknown";
+ 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_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 ECTesterApplet.SW_SIG_VERIFY_FAIL:
+ return "SIG_VERIFY_FAIL";
+ case ECTesterApplet.SW_DH_DHC_MISMATCH:
+ return "DH_DHC_MISMATCH";
+ case ECTesterApplet.SW_KEYPAIR_NULL:
+ return "KEYPAIR_NULL";
+ case ECTesterApplet.SW_KA_NULL:
+ return "KA_NULL";
+ case ECTesterApplet.SW_SIGNATURE_NULL:
+ return "SIGNATURE_NULL";
+ case ECTesterApplet.SW_OBJECT_NULL:
+ return "OBJECT_NULL";
+ case ECTesterApplet.SW_Exception:
+ return "Exception";
+ case ECTesterApplet.SW_ArrayIndexOutOfBoundsException:
+ return "ArrayIndexOutOfBoundsException";
+ case ECTesterApplet.SW_ArithmeticException:
+ return "ArithmeticException";
+ case ECTesterApplet.SW_ArrayStoreException:
+ return "ArrayStoreException";
+ case ECTesterApplet.SW_NullPointerException:
+ return "NullPointerException";
+ case ECTesterApplet.SW_NegativeArraySizeException:
+ return "NegativeArraySizeException";
+ default:
+ return "unknown";
+ }
}
}
@@ -151,46 +186,122 @@ public class CardUtil {
}
}
- public static String getCorruption(short corruptionType) {
- switch (corruptionType) {
- case EC_Consts.CORRUPTION_NONE:
- return "NONE";
- case EC_Consts.CORRUPTION_FIXED:
- return "FIXED";
- case EC_Consts.CORRUPTION_ONE:
- return "ONE";
- case EC_Consts.CORRUPTION_ZERO:
- return "ZERO";
- case EC_Consts.CORRUPTION_ONEBYTERANDOM:
- return "ONE_BYTE_RANDOM";
- case EC_Consts.CORRUPTION_FULLRANDOM:
- return "FULL_RANDOM";
- case EC_Consts.CORRUPTION_INCREMENT:
- return "INCREMENT";
- case EC_Consts.CORRUPTION_INFINITY:
- return "INFINITY";
- case EC_Consts.CORRUPTION_COMPRESS:
- return "COMPRESSED";
- case EC_Consts.CORRUPTION_MAX:
- return "MAX";
- default:
- return "unknown";
+ 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 KeyAgreement_ALG_EC_SVDP_DH:
+ case EC_Consts.KeyAgreement_ALG_EC_SVDP_DH:
return "ALG_EC_SVDP_DH";
- case KeyAgreement_ALG_EC_SVDP_DH_PLAIN:
+ case EC_Consts.KeyAgreement_ALG_EC_SVDP_DH_PLAIN:
return "ALG_EC_SVDP_DH_PLAIN";
- case KeyAgreement_ALG_EC_PACE_GM:
+ case EC_Consts.KeyAgreement_ALG_EC_PACE_GM:
return "ALG_EC_PACE_GM";
- case KeyAgreement_ALG_EC_SVDP_DH_PLAIN_XY:
+ case EC_Consts.KeyAgreement_ALG_EC_SVDP_DH_PLAIN_XY:
return "ALG_EC_SVDP_DH_PLAIN_XY";
- case KeyAgreement_ALG_EC_SVDP_DHC:
+ case EC_Consts.KeyAgreement_ALG_EC_SVDP_DHC:
return "ALG_EC_SVDP_DHC";
- case KeyAgreement_ALG_EC_SVDP_DHC_PLAIN:
+ case EC_Consts.KeyAgreement_ALG_EC_SVDP_DHC_PLAIN:
return "ALG_EC_SVDP_DHC_PLAIN";
default:
return "unknown";
@@ -200,17 +311,17 @@ public class CardUtil {
public static byte getKAType(String kaTypeString) {
switch (kaTypeString) {
case "ALG_EC_SVDP_DH":
- return KeyAgreement_ALG_EC_SVDP_DH;
+ return EC_Consts.KeyAgreement_ALG_EC_SVDP_DH;
case "ALG_EC_SVDP_DH_PLAIN":
- return KeyAgreement_ALG_EC_SVDP_DH_PLAIN;
+ return EC_Consts.KeyAgreement_ALG_EC_SVDP_DH_PLAIN;
case "ALG_EC_PACE_GM":
- return KeyAgreement_ALG_EC_PACE_GM;
+ return EC_Consts.KeyAgreement_ALG_EC_PACE_GM;
case "ALG_EC_SVDP_DH_PLAIN_XY":
- return KeyAgreement_ALG_EC_SVDP_DH_PLAIN_XY;
+ return EC_Consts.KeyAgreement_ALG_EC_SVDP_DH_PLAIN_XY;
case "ALG_EC_SVDP_DHC":
- return KeyAgreement_ALG_EC_SVDP_DHC;
+ return EC_Consts.KeyAgreement_ALG_EC_SVDP_DHC;
case "ALG_EC_SVDP_DHC_PLAIN":
- return KeyAgreement_ALG_EC_SVDP_DHC_PLAIN;
+ return EC_Consts.KeyAgreement_ALG_EC_SVDP_DHC_PLAIN;
default:
return 0;
}
@@ -228,15 +339,15 @@ public class CardUtil {
public static String getSigTypeString(byte sigType) {
switch (sigType) {
- case Signature_ALG_ECDSA_SHA:
+ case EC_Consts.Signature_ALG_ECDSA_SHA:
return "ALG_ECDSA_SHA";
- case Signature_ALG_ECDSA_SHA_224:
+ case EC_Consts.Signature_ALG_ECDSA_SHA_224:
return "ALG_ECDSA_SHA_224";
- case Signature_ALG_ECDSA_SHA_256:
+ case EC_Consts.Signature_ALG_ECDSA_SHA_256:
return "ALG_ECDSA_SHA_256";
- case Signature_ALG_ECDSA_SHA_384:
+ case EC_Consts.Signature_ALG_ECDSA_SHA_384:
return "ALG_ECDSA_SHA_384";
- case Signature_ALG_ECDSA_SHA_512:
+ case EC_Consts.Signature_ALG_ECDSA_SHA_512:
return "ALG_ECDSA_SHA_512";
default:
return "unknown";
@@ -246,15 +357,15 @@ public class CardUtil {
public static byte getSigType(String sigTypeString) {
switch (sigTypeString) {
case "ALG_ECDSA_SHA":
- return Signature_ALG_ECDSA_SHA;
+ return EC_Consts.Signature_ALG_ECDSA_SHA;
case "ALG_ECDSA_SHA_224":
- return Signature_ALG_ECDSA_SHA_224;
+ return EC_Consts.Signature_ALG_ECDSA_SHA_224;
case "ALG_ECDSA_SHA_256":
- return Signature_ALG_ECDSA_SHA_256;
+ return EC_Consts.Signature_ALG_ECDSA_SHA_256;
case "ALG_ECDSA_SHA_384":
- return Signature_ALG_ECDSA_SHA_384;
+ return EC_Consts.Signature_ALG_ECDSA_SHA_384;
case "ALG_ECDSA_SHA_512":
- return Signature_ALG_ECDSA_SHA_512;
+ return EC_Consts.Signature_ALG_ECDSA_SHA_512;
default:
return 0;
}
@@ -269,4 +380,31 @@ public class CardUtil {
}
return sigType;
}
+
+ public static String getKeyTypeString(byte keyClass) {
+ switch (keyClass) {
+ case KeyPair.ALG_EC_FP:
+ return "ALG_EC_FP";
+ case KeyPair.ALG_EC_F2M:
+ return "ALG_EC_F2M";
+ default:
+ return "";
+ }
+ }
+
+ 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/src/cz/crcs/ectester/common/util/ECUtil.java b/src/cz/crcs/ectester/common/util/ECUtil.java
index 973b813..0979d91 100644
--- a/src/cz/crcs/ectester/common/util/ECUtil.java
+++ b/src/cz/crcs/ectester/common/util/ECUtil.java
@@ -146,16 +146,22 @@ public class ECUtil {
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) {
- beta = beta.negate();
+ // yp is 0
+ beta = p.subtract(beta);
}
} else {
// rightmost bit is zero
if (data[0] == 0x03) {
- beta = beta.negate();
+ // yp is 1
+ beta = p.subtract(beta);
}
}
diff --git a/src/cz/crcs/ectester/common/util/FileUtil.java b/src/cz/crcs/ectester/common/util/FileUtil.java
new file mode 100644
index 0000000..790596b
--- /dev/null
+++ b/src/cz/crcs/ectester/common/util/FileUtil.java
@@ -0,0 +1,33 @@
+package cz.crcs.ectester.common.util;
+
+import cz.crcs.ectester.common.output.TeeOutputStream;
+
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author Jan Jancar johny@neuromancer.sk
+ */
+public class FileUtil {
+ 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));
+ }
+}