summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--!uploader/ectester.capbin16008 -> 16008 bytes
-rw-r--r--.travis.yml17
-rw-r--r--README.md11
-rw-r--r--build.xml29
-rw-r--r--dist/ECTester.jarbin726928 -> 810137 bytes
-rw-r--r--dist/ectester.bat34
-rwxr-xr-xdist/ectester.sh48
-rw-r--r--dist/lib/snakeyaml-1.19.jarbin0 -> 297518 bytes
-rw-r--r--docs/CURVES.md (renamed from CURVES.md)0
-rw-r--r--docs/FORMAT.md (renamed from FORMAT.md)0
-rw-r--r--docs/TESTS.md (renamed from TESTS.md)8
-rw-r--r--lib/snakeyaml-1.19.jarbin0 -> 297518 bytes
-rw-r--r--manifest.mf3
-rw-r--r--nbproject/project.properties3
-rw-r--r--src/cz/crcs/ectester/data/EC_Store.java6
-rw-r--r--src/cz/crcs/ectester/data/categories.xml6
-rw-r--r--src/cz/crcs/ectester/data/composite/composite128.csv1
-rw-r--r--src/cz/crcs/ectester/data/composite/composite160.csv1
-rw-r--r--src/cz/crcs/ectester/data/composite/composite192.csv1
-rw-r--r--src/cz/crcs/ectester/data/composite/composite224.csv1
-rw-r--r--src/cz/crcs/ectester/data/composite/composite256.csv1
-rw-r--r--src/cz/crcs/ectester/data/composite/composite384.csv1
-rw-r--r--src/cz/crcs/ectester/data/composite/composite521.csv1
-rw-r--r--src/cz/crcs/ectester/data/composite/curves.xml (renamed from src/cz/crcs/ectester/data/nonprime/curves.xml)28
-rw-r--r--src/cz/crcs/ectester/data/composite/keys.xml570
-rw-r--r--src/cz/crcs/ectester/data/nonprime/keys.xml48
-rw-r--r--src/cz/crcs/ectester/data/nonprime/nonprime128.csv1
-rw-r--r--src/cz/crcs/ectester/data/nonprime/nonprime128_pub.csv1
-rw-r--r--src/cz/crcs/ectester/data/nonprime/nonprime160.csv1
-rw-r--r--src/cz/crcs/ectester/data/nonprime/nonprime160_pub.csv1
-rw-r--r--src/cz/crcs/ectester/data/nonprime/nonprime192.csv1
-rw-r--r--src/cz/crcs/ectester/data/nonprime/nonprime192_pub.csv1
-rw-r--r--src/cz/crcs/ectester/data/nonprime/nonprime224.csv1
-rw-r--r--src/cz/crcs/ectester/data/nonprime/nonprime224_pub.csv1
-rw-r--r--src/cz/crcs/ectester/data/nonprime/nonprime256.csv1
-rw-r--r--src/cz/crcs/ectester/data/nonprime/nonprime256_pub.csv1
-rw-r--r--src/cz/crcs/ectester/data/nonprime/nonprime384.csv1
-rw-r--r--src/cz/crcs/ectester/data/nonprime/nonprime384_pub.csv1
-rw-r--r--src/cz/crcs/ectester/data/nonprime/nonprime521.csv1
-rw-r--r--src/cz/crcs/ectester/data/nonprime/nonprime521_pub.csv1
-rw-r--r--src/cz/crcs/ectester/reader/DirtyLogger.java56
-rw-r--r--src/cz/crcs/ectester/reader/ECTester.java119
-rw-r--r--src/cz/crcs/ectester/reader/Test.java82
-rw-r--r--src/cz/crcs/ectester/reader/TestSuite.java314
-rw-r--r--src/cz/crcs/ectester/reader/Util.java14
-rw-r--r--src/cz/crcs/ectester/reader/command/Command.java (renamed from src/cz/crcs/ectester/reader/Command.java)42
-rw-r--r--src/cz/crcs/ectester/reader/ec/EC_Category.java2
-rw-r--r--src/cz/crcs/ectester/reader/ec/EC_Curve.java2
-rw-r--r--src/cz/crcs/ectester/reader/ec/EC_Data.java4
-rw-r--r--src/cz/crcs/ectester/reader/ec/EC_KAResult.java2
-rw-r--r--src/cz/crcs/ectester/reader/ec/EC_Key.java23
-rw-r--r--src/cz/crcs/ectester/reader/ec/EC_Keypair.java7
-rw-r--r--src/cz/crcs/ectester/reader/ec/EC_Params.java5
-rw-r--r--src/cz/crcs/ectester/reader/output/OutputLogger.java60
-rw-r--r--src/cz/crcs/ectester/reader/output/ResponseWriter.java39
-rw-r--r--src/cz/crcs/ectester/reader/output/TeeOutputStream.java36
-rw-r--r--src/cz/crcs/ectester/reader/output/TestWriter.java15
-rw-r--r--src/cz/crcs/ectester/reader/output/TextTestWriter.java85
-rw-r--r--src/cz/crcs/ectester/reader/output/XMLTestWriter.java145
-rw-r--r--src/cz/crcs/ectester/reader/output/YAMLTestWriter.java113
-rw-r--r--src/cz/crcs/ectester/reader/response/Response.java (renamed from src/cz/crcs/ectester/reader/Response.java)130
-rw-r--r--src/cz/crcs/ectester/reader/test/CompositeCurvesSuite.java53
-rw-r--r--src/cz/crcs/ectester/reader/test/DefaultSuite.java69
-rw-r--r--src/cz/crcs/ectester/reader/test/InvalidCurvesSuite.java68
-rw-r--r--src/cz/crcs/ectester/reader/test/Result.java94
-rw-r--r--src/cz/crcs/ectester/reader/test/Test.java217
-rw-r--r--src/cz/crcs/ectester/reader/test/TestRunner.java29
-rw-r--r--src/cz/crcs/ectester/reader/test/TestSuite.java135
-rw-r--r--src/cz/crcs/ectester/reader/test/TestVectorSuite.java83
-rw-r--r--src/cz/crcs/ectester/reader/test/WrongCurvesSuite.java34
-rw-r--r--src/cz/crcs/ectester/scripts/ectester.bat34
-rwxr-xr-xsrc/cz/crcs/ectester/scripts/ectester.sh48
72 files changed, 2306 insertions, 685 deletions
diff --git a/!uploader/ectester.cap b/!uploader/ectester.cap
index 3b9e650..b883965 100644
--- a/!uploader/ectester.cap
+++ b/!uploader/ectester.cap
Binary files differ
diff --git a/.travis.yml b/.travis.yml
index 484c6d9..ecac5d1 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,6 +1,17 @@
language: java
jdk:
- - oraclejdk8
+- oraclejdk8
script:
- - ant -f jcbuild.xml build
- - ant -f build.xml jar \ No newline at end of file
+- ant -f jcbuild.xml build
+- ant -f build.xml package
+deploy:
+ provider: releases
+ api_key:
+ secure: q2aJvu32K+nfbMR60nFCEkn+jYCKprlCRlIoPjuRz1HySX233Ccwpx1CAdNzEjY6FDFcoReKAg6r5vdPjJ4FRPAQ23TxffIYZPkykL5K/pUZJbM5xkazJY0Fp8i6Vyl0JfeanVib1PTyOSugplhCttFk5nb9JUFV36Tre66XntOl5y80Trn94F5aTlRjfW26UH65W7Aa6WZ0N4OX/ZsX+vEOJPAu+RLfOq9oBOx/loB8ntYM/e/6bEwJp6EedRQLDsiS4NavP3svH+GXsPLs5p3soyRXYsvvGKVnVjcjZURxDDdxv5YuCWUUfl9PbNB+Mqmx/HQxl50BKoKFqwap1+TnlbuTAiWaXeh3zdXuGB+TPg8KE8h6ueDneHd3Lpivgq79IvPWIH+N4b3Pa952+rD+JKBZ807efB+97OtWrkQL7/sLZESQUdIszE724HHOiArKpNajIX+kN6NJdul5xFCiQQHG+O7iDFQBavCGM9fk63mZRyGPxZQzS06BV2vIIHg0yx3igN+OKKMFCH+P3hYR1zL6o65OlgbL1ifTZ18GDvmVRNdi53/fxQ2n/mQmI4tQpn4ZB7Ddoxx4GlpjFjzdKk/P9nKwng0M9wrp8row/vb5S+1aPwSxp9/4ASP9dkvLcNjTkWhmGPrWe+82Y9JPK47uesx0YeaVI2C7IR0=
+ file:
+ - "dist/ECTester.jar"
+ - "!uploader/ectester.cap"
+ skip_cleanup: true
+ on:
+ tags: true
+ repo: crocs-muni/ECTester
diff --git a/README.md b/README.md
index 4bae53b..c48426e 100644
--- a/README.md
+++ b/README.md
@@ -55,10 +55,15 @@ See `java -jar ECTester.jar -h` for more.
-o,--output <output_file> Output into file <output_file>.
-l,--log <log_file> Log output into file [log_file].
-v,--verbose Turn on verbose logging.
+ --format <format> Output format to use.
-f,--fresh Generate fresh keys (set domain
parameters before every generation).
-s,--simulate Simulate a card with jcardsim instead
of using a terminal.
+ -y,--yes Accept all warnings and prompts.
+ -ka,--ka-type <type> Set KeyAgreement object [type],
+ corresponds to JC.KeyAgreement
+ constants.
```
### Actions
@@ -68,14 +73,14 @@ See `java -jar ECTester.jar -h` for more.
Exports the default curves (if any) that are preset on the card.
Use with `-o / --output [out_file]` to output the curve parameters to a file.
-For format of this file see [FORMAT](FORMAT.md).
+For format of this file see [FORMAT](docs/FORMAT.md).
#### Test
`-t / --test [test_suite]`
Perform support and performance tests of ECC.
-For more info about the test suites see [TESTS](TESTS.md).
+For more info about the test suites see [TESTS](docs/TESTS.md).
#### Generate
`-g / --generate [amount]`
@@ -108,7 +113,7 @@ With the format: `category/name`.
For example:
`secg/secp192r1` identifies the SECG 192 bit prime field curve known as `secp192r1`.
-For more info about the curves see [CURVES](CURVES.md).
+For more info about the curves see [CURVES](docs/CURVES.md).
### Example
diff --git a/build.xml b/build.xml
index 1fd4fcd..49cc20a 100644
--- a/build.xml
+++ b/build.xml
@@ -92,4 +92,33 @@
</jar>
</target>
-->
+ <target name="-post-jar">
+ <copy file="src/cz/crcs/ectester/scripts/ectester.sh" todir="${dist.dir}"/>
+ <copy file="src/cz/crcs/ectester/scripts/ectester.bat" todir="${dist.dir}"/>
+ </target>
+ <target name="package" depends="jar">
+ <property name="store.jar.name" value="ECTester-dist"/>
+ <property name="store.dir" value="dist"/>
+ <property name="store.jar" value="${store.dir}/${store.jar.name}.jar"/>
+
+ <echo message="Packaging ${application.title} into a single JAR at ${store.jar}"/>
+
+ <tempfile property="temp.file" destDir="${java.io.tmpdir}" suffix=".jar"/>
+
+ <jar destfile="${temp.file}" filesetmanifest="skip">
+ <zipgroupfileset dir="dist" includes="*.jar"/>
+ <zipgroupfileset dir="dist/lib" includes="*.jar"/>
+
+ <manifest>
+ <attribute name="Main-Class" value="${main.class}"/>
+ </manifest>
+ </jar>
+
+ <zip destfile="${store.jar}">
+ <zipfileset src="${temp.file}"
+ excludes="META-INF/*.SF, META-INF/*.DSA, META-INF/*.RSA"/>
+ </zip>
+
+ <delete file="${temp.file}"/>
+ </target>
</project>
diff --git a/dist/ECTester.jar b/dist/ECTester.jar
index 8195c6f..b5d07c5 100644
--- a/dist/ECTester.jar
+++ b/dist/ECTester.jar
Binary files differ
diff --git a/dist/ectester.bat b/dist/ectester.bat
new file mode 100644
index 0000000..e20b855
--- /dev/null
+++ b/dist/ectester.bat
@@ -0,0 +1,34 @@
+@ECHO OFF
+SETLOCAL enabledelayedexpansion
+
+SET n=0
+:loop
+IF NOT "%1"=="" (
+ IF "%1"=="--dangerous" (
+ SET dangerous=1
+ ) ELSE (
+ SET positional[!n!]=%1
+ SET /A n+=1
+ )
+ SHIFT
+ GOTO :loop
+)
+
+IF NOT "%n%"=="1" (
+ ECHO "One argument expected:"
+ ECHO " ./ectester.bar [--dangerous] CARD_NAME"
+)
+
+SET card=!positional[%%0]!
+
+SET tests="default test-vectors"
+java -jar ECTester.jar -t default -a --format yaml -l %card%.default
+java -jar ECTester.jar -t test-vectors -a --format yaml -l %card%.test-vectors
+IF "%dangerous%"=="1" (
+ SET tests=%tests% "invalid wrong composite"
+ java -jar ECTester.jar -t invalid -a --format yaml -l %card%.invalid
+ java -jar ECTester.jar -t wrong -a --format yaml -l %card%.wrong
+ java -jar ECTester.jar -t composite -a --format yaml -l %card%.composite
+)
+
+zip %card%.zip %tests%
diff --git a/dist/ectester.sh b/dist/ectester.sh
new file mode 100755
index 0000000..8040096
--- /dev/null
+++ b/dist/ectester.sh
@@ -0,0 +1,48 @@
+#!/bin/bash
+
+dangerous="0"
+
+positional=()
+while [[ $# -gt 0 ]]
+do
+
+key="$1"
+case $key in
+ --dangerous)
+ dangerous=1
+ shift
+ ;;
+ *)
+ positional+=("$1")
+ shift
+ ;;
+esac
+done
+set -- "${positional[@]}"
+
+if [[ $# -lt 1 ]]; then
+ echo "At least one argument expected:" >&2
+ echo " ./ectester.sh [--dangerous] CARD_NAME [ECTester args]" >&2
+ exit 1
+fi
+
+card="$1"
+shift
+
+declare -a tests=("default" "test-vectors")
+if [[ "$dangerous" == "1" ]]; then
+ tests+=("invalid" "wrong" "composite")
+fi
+
+declare -a files=()
+for i in $(seq 0 $((${#tests[@]} - 1))); do
+ test="${tests[$i]}"
+ java -jar ECTester.jar -t ${test} -a --format yaml -l ${card}.${test} $@
+ files+=(${card}.$test)
+done
+
+if command -v tar 2>&1 >/dev/null; then
+ tar -czvf ${card}.tar.gz ${files[*]}
+elif command -v zip 2>&1 >/dev/null; then
+ zip ${card}.zip ${files[*]}
+fi
diff --git a/dist/lib/snakeyaml-1.19.jar b/dist/lib/snakeyaml-1.19.jar
new file mode 100644
index 0000000..7c73a76
--- /dev/null
+++ b/dist/lib/snakeyaml-1.19.jar
Binary files differ
diff --git a/CURVES.md b/docs/CURVES.md
index d1749df..d1749df 100644
--- a/CURVES.md
+++ b/docs/CURVES.md
diff --git a/FORMAT.md b/docs/FORMAT.md
index b68db39..b68db39 100644
--- a/FORMAT.md
+++ b/docs/FORMAT.md
diff --git a/TESTS.md b/docs/TESTS.md
index 465ca5d..21298dc 100644
--- a/TESTS.md
+++ b/docs/TESTS.md
@@ -3,10 +3,10 @@
- `default`
- `test-vectors`
- `wrong`
- - `nonprime`
+ - `composite`
- `invalid`
-**NOTE: The `wrong`, `nonprime` and `invalid` test suites caused temporary DoS of some cards. These test suites prompt you for
+**NOTE: The `wrong`, `composite` and `invalid` test suites caused temporary DoS of some cards. These test suites prompt you for
confirmation before running, be cautious.**
## Default
@@ -68,14 +68,14 @@ java -jar ECTester.jar -t wrong -b 521 -fp
```
tests a 521 bit(`-b`), prime-field(`-fp`) wrong curve.
-## Nonprime
+## Composite
Tests using curves that don't have a prime order/nearly prime order.
These tests should generally fail, a success here implies the card **WILL** use a non-secure curve if such curve is set
by the applet. Operations over such curves are susceptible to small-subgroup attacks.
For example:
```bash
-java -jar ECTester.jar -t nonprime -b 160 -fp
+java -jar ECTester.jar -t composite -b 160 -fp
```
## Invalid
diff --git a/lib/snakeyaml-1.19.jar b/lib/snakeyaml-1.19.jar
new file mode 100644
index 0000000..7c73a76
--- /dev/null
+++ b/lib/snakeyaml-1.19.jar
Binary files differ
diff --git a/manifest.mf b/manifest.mf
index fdec036..2cb1a50 100644
--- a/manifest.mf
+++ b/manifest.mf
@@ -1,5 +1,4 @@
Manifest-Version: 1.0
-X-COMMENT: Main-Class will be added automatically by build
-Class-Path: lib/jcardsim-3.0.4-SNAPSHOT.jar lib/commons-cli-1.3.1.jar
+Class-Path: lib/jcardsim-3.0.4-SNAPSHOT.jar lib/commons-cli-1.3.1.jar lib/snakeyaml-1.19.jar
Main-Class: cz.crcs.ectester.reader.ECTester
diff --git a/nbproject/project.properties b/nbproject/project.properties
index ad90c49..16be542 100644
--- a/nbproject/project.properties
+++ b/nbproject/project.properties
@@ -33,7 +33,8 @@ includes=**
jar.compress=false
javac.classpath=\
lib/jcardsim-3.0.4-SNAPSHOT.jar:\
- lib/commons-cli-1.3.1.jar
+ lib/commons-cli-1.3.1.jar:\
+ lib/snakeyaml-1.19.jar
# Space-separated list of extra javac options
javac.compilerargs=
javac.deprecation=false
diff --git a/src/cz/crcs/ectester/data/EC_Store.java b/src/cz/crcs/ectester/data/EC_Store.java
index 94eb011..9b1f5bb 100644
--- a/src/cz/crcs/ectester/data/EC_Store.java
+++ b/src/cz/crcs/ectester/data/EC_Store.java
@@ -266,11 +266,11 @@ public class EC_Store {
EC_Params result;
if (elem.getTagName().equals("pubkey")) {
- result = new EC_Key.Public(curve.getTextContent(), descs);
+ result = new EC_Key.Public(id.getTextContent(), curve.getTextContent(), descs);
} else if (elem.getTagName().equals("privkey")) {
- result = new EC_Key.Private(curve.getTextContent(), descs);
+ result = new EC_Key.Private(id.getTextContent(), curve.getTextContent(), descs);
} else if (elem.getTagName().equals("keypair")) {
- result = new EC_Keypair(curve.getTextContent(), descs);
+ result = new EC_Keypair(id.getTextContent(), curve.getTextContent(), descs);
} else {
throw new SAXException("?");
}
diff --git a/src/cz/crcs/ectester/data/categories.xml b/src/cz/crcs/ectester/data/categories.xml
index c53a7c0..750fa8c 100644
--- a/src/cz/crcs/ectester/data/categories.xml
+++ b/src/cz/crcs/ectester/data/categories.xml
@@ -32,9 +32,9 @@
<desc>GOST R 34.10-2001: RFC5832</desc>
</category>
<category>
- <name>nonprime</name>
- <directory>nonprime</directory>
- <desc>Non-prime order curves, with points of very small order pregenerated.</desc>
+ <name>composite</name>
+ <directory>composite</directory>
+ <desc>Composite order curves, with points of very small order pregenerated.</desc>
</category>
<category>
<name>wrong</name>
diff --git a/src/cz/crcs/ectester/data/composite/composite128.csv b/src/cz/crcs/ectester/data/composite/composite128.csv
new file mode 100644
index 0000000..66b7011
--- /dev/null
+++ b/src/cz/crcs/ectester/data/composite/composite128.csv
@@ -0,0 +1 @@
+0xc8bb039faeca04585e1d5864fbc05f6b,0x90af1043a837133b556495fe1e080b7c,0x916d1eaf6cc9eee0f86e5bd6313ba6fc,0xacb0cfa25821e758258ee3c7ddd37cbf,0x1fcb4b9c5c3f9a902f19130755c802b9,0xc8bb039faeca045765097266c32b334f,0x1 \ No newline at end of file
diff --git a/src/cz/crcs/ectester/data/composite/composite160.csv b/src/cz/crcs/ectester/data/composite/composite160.csv
new file mode 100644
index 0000000..a78d77c
--- /dev/null
+++ b/src/cz/crcs/ectester/data/composite/composite160.csv
@@ -0,0 +1 @@
+0x9b0bafb73969e379b49753fa1b3cc65e9ca73adf,0x93091909c640bcdf66f470c1627ec776e9f625cc,0x69c34611c6d4cb088365ae95bf2d7d9c97aaf224,0x34e7895542e4986cd2884d9b6571cf0a955bba12,0x8ae9489212982d059b483d08a9dac6587ad67d8e,0x9b0bafb73969e379b496b7f060d2e76956ba8504,0x1 \ No newline at end of file
diff --git a/src/cz/crcs/ectester/data/composite/composite192.csv b/src/cz/crcs/ectester/data/composite/composite192.csv
new file mode 100644
index 0000000..a9fbe1f
--- /dev/null
+++ b/src/cz/crcs/ectester/data/composite/composite192.csv
@@ -0,0 +1 @@
+0xabe758cde9fc09b714f543b4acac1550c09297279d4d2a1f,0x41e9bf05575d991e1e3393fc1746f2eb49e7f9b34867343e,0x54c5db9d913d1afeabce6e11ea623f8536dd6eef8eb5dfff,0xa8ee9eaafc61a1ae53da6815a644a95ccfa6c9c10de617f8,0x396ebe9cb965a02dd72f7a4f96dfc48c6ac74b49a93216d1,0xabe758cde9fc09b714f543b5567a1cda695faff0d96cbf46,0x1 \ No newline at end of file
diff --git a/src/cz/crcs/ectester/data/composite/composite224.csv b/src/cz/crcs/ectester/data/composite/composite224.csv
new file mode 100644
index 0000000..7902f2b
--- /dev/null
+++ b/src/cz/crcs/ectester/data/composite/composite224.csv
@@ -0,0 +1 @@
+0xe6cacc569ea2a63aea0409959625c8f8238b0ddc2565046db20390ed,0x20484bda40fdcade9ff6f9147076e2824e5f0d057a4a95cfb1b9312d,0x14cacbe6ad4a700a3f6a44cdc0a60d6fd0a4f2052c5b9ae5661fd964,0x92eddfe929f73cd4088dc865bbbc33e7828c7d9c9cd7e978d226aba0,0x944111a52330a4986388b10e709ff9b1408f361cd353bed31c43ad4c,0xe6cacc569ea2a63aea04099596266ee5ef526d9cf5034d56ebedb660,0x1 \ No newline at end of file
diff --git a/src/cz/crcs/ectester/data/composite/composite256.csv b/src/cz/crcs/ectester/data/composite/composite256.csv
new file mode 100644
index 0000000..7769ef2
--- /dev/null
+++ b/src/cz/crcs/ectester/data/composite/composite256.csv
@@ -0,0 +1 @@
+0xdc42862158cdcfc8fbe8c4ec28b02e0e12624ebc15f6486163df218253e1b953,0x01f355375dac7e69cc12c22ce747782bf998252f1c9c9c983273bc2b7e721461,0x22098140e10d5d88bcc96a558500a022d0252bfbe438d3475ef2d90261fe3b1f,0x3826ea1bacb3a90c653302db7fb7e6db4ea8d4f62e68eff73272d79fe61cfd8b,0x4ecd7019f52f3103f7810639f13cfc82fa52b47ad37d29a316bc052fbedde067,0xdc42862158cdcfc8fbe8c4ec28b02e0f78ce34249a0454ca049ad0ef7c2fcfea,0x1 \ No newline at end of file
diff --git a/src/cz/crcs/ectester/data/composite/composite384.csv b/src/cz/crcs/ectester/data/composite/composite384.csv
new file mode 100644
index 0000000..7516301
--- /dev/null
+++ b/src/cz/crcs/ectester/data/composite/composite384.csv
@@ -0,0 +1 @@
+0x91520c5e44d7a5e9c673943d2fc502d2b35a1c9edc6189da10277423d7381fd4671fc706ff1dce6f8513317842fb655b,0x6c64adff869b4e0b9f0dd35945b24e3ab389232b1e506a71306e389dddbf43ec6c73d290fab04976da246fd77a7e28dd,0x4004c33e4f8b4de57064543802ca11810c79aa9990796d5750dba07f3e4e98f3dd18b3abe052208741149c3ad0fe6f56,0x39bf2e7d67ad506e074e04c1b73a4add511b0579cdae07bd6c025c703ee31ff33c0445c2cb10e465f0f453d996751e4e,0x667ec38866be3e87229e31e6dabc2444e2603e8118c0316e592571e9a904423e4792c18c0360ae57fcdaec87901d2e0b,0x91520c5e44d7a5e9c673943d2fc502d2b35a1c9edc6189db347c42a7d0924b7cd1414d57f582a4b96177c911e9bb3ddd,0x1 \ No newline at end of file
diff --git a/src/cz/crcs/ectester/data/composite/composite521.csv b/src/cz/crcs/ectester/data/composite/composite521.csv
new file mode 100644
index 0000000..0cf36e7
--- /dev/null
+++ b/src/cz/crcs/ectester/data/composite/composite521.csv
@@ -0,0 +1 @@
+0x01c230ff4a64e33ad6da8361c7e2bf2a85e4ad5e88f8e3e8e7e3c42a6b9c9883b3690e2f82b38a684e823a257b3d5c4c2d7c34bfcb937fe5658c77247b28bc90c6af,0x00852cf32173a9b3a8a2316bbe6ba0b98efcc41247a17a8aead02ccc7cb90870daccd10a44e9385abf0d0ae175fa03c0facab1d2069b79795f36d10ce25430676805,0x0174e20e49d5970117cdfc61c0a0ae062519c0c4d577f42e0d3f1cd443732b8e62deb34922a4a6d85b8b7f6df61dadffec95c17babbfaf5c57598033be0b1a44e32f,0x01b1dbc5bd175cbd8699895698b10d047853679f048261094854134de1d6a81ffdd1cc31ede64293c9bee543ad2113c164bd1da92f5c3aa7ba7f0378bdb58251d870,0x0188b8c020cc1504eedb4fb859e40b8031cb26d9273efa2578bf7da01db994cf2a5cf2780b9a7ff6d33210b962ae20c56f71b7fa2b36deef33d7d675c6136acd73c1,0x1c230ff4a64e33ad6da8361c7e2bf2a85e4ad5e88f8e3e8e7e3c42a6b9c9883b36ec447771e871c188d3f04a4e407f1d659335dd0e351bd7f3f76a639726d6dc0d2,0x1 \ No newline at end of file
diff --git a/src/cz/crcs/ectester/data/nonprime/curves.xml b/src/cz/crcs/ectester/data/composite/curves.xml
index e0eb78a..e940efe 100644
--- a/src/cz/crcs/ectester/data/nonprime/curves.xml
+++ b/src/cz/crcs/ectester/data/composite/curves.xml
@@ -2,45 +2,45 @@
<curves xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../schema.xsd">
<curve>
- <id>nonprime128</id>
+ <id>composite128</id>
<bits>128</bits>
<field>prime</field>
- <file>nonprime128.csv</file>
+ <file>composite128.csv</file>
</curve>
<curve>
- <id>nonprime160</id>
+ <id>composite160</id>
<bits>160</bits>
<field>prime</field>
- <file>nonprime160.csv</file>
+ <file>composite160.csv</file>
</curve>
<curve>
- <id>nonprime192</id>
+ <id>composite192</id>
<bits>192</bits>
<field>prime</field>
- <file>nonprime192.csv</file>
+ <file>composite192.csv</file>
</curve>
<curve>
- <id>nonprime224</id>
+ <id>composite224</id>
<bits>224</bits>
<field>prime</field>
- <file>nonprime224.csv</file>
+ <file>composite224.csv</file>
</curve>
<curve>
- <id>nonprime256</id>
+ <id>composite256</id>
<bits>256</bits>
<field>prime</field>
- <file>nonprime256.csv</file>
+ <file>composite256.csv</file>
</curve>
<curve>
- <id>nonprime384</id>
+ <id>composite384</id>
<bits>384</bits>
<field>prime</field>
- <file>nonprime384.csv</file>
+ <file>composite384.csv</file>
</curve>
<curve>
- <id>nonprime521</id>
+ <id>composite521</id>
<bits>521</bits>
<field>prime</field>
- <file>nonprime521.csv</file>
+ <file>composite521.csv</file>
</curve>
</curves> \ No newline at end of file
diff --git a/src/cz/crcs/ectester/data/composite/keys.xml b/src/cz/crcs/ectester/data/composite/keys.xml
new file mode 100644
index 0000000..f333042
--- /dev/null
+++ b/src/cz/crcs/ectester/data/composite/keys.xml
@@ -0,0 +1,570 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<keys xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:noNamespaceSchemaLocation="../schema.xsd"
+ category="composite"
+ desc="Points on the non-prime curves, very small point orders(3-5).">
+ <pubkey>
+ <id>composite128/1</id>
+ <inline>0x746fa441b3a54d3c531bd59d119f400d,0x73aff68dbd96e1485cd2de0f6389cc70</inline>
+ <curve>composite/composite128</curve>
+ <desc>order = 3</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite128/2</id>
+ <inline>0x6e9dc37af66af0045d7a2e414d4bfb89,0x40b01d2f36c9a5b2a1cb28386dd12470</inline>
+ <curve>composite/composite128</curve>
+ <desc>order = 5</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite128/3</id>
+ <inline>0x32076e371a48d82777ce851969439ab8,0xb049b7d6ba6f2d9c6ce240bc689d3556</inline>
+ <curve>composite/composite128</curve>
+ <desc>order = 15</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite128/4</id>
+ <inline>0xc819a68e95796650cd7d11e8ad65806f,0x280e9c9b7e5d9a5e07653ee2afac83e7</inline>
+ <curve>composite/composite128</curve>
+ <desc>order = 59</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite128/5</id>
+ <inline>0x66f66346d87e2214bf1bfc3331628c81,0x8c1a74b09bbbb515b027d89dcc1cecb2</inline>
+ <curve>composite/composite128</curve>
+ <desc>order = 177</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite128/6</id>
+ <inline>0xa546a97bd17378bd6e5c42c4ab857cac,0x13b99be68904a968a8d4ca6feab23f40</inline>
+ <curve>composite/composite128</curve>
+ <desc>order = 295</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite128/7</id>
+ <inline>0x6ae9f873a9da27b41e676d3514c96e56,0x8f6479493be1835cee6b9f29df21f74f</inline>
+ <curve>composite/composite128</curve>
+ <desc>order = 885</desc>
+ </pubkey>
+
+ <pubkey>
+ <id>composite160/1</id>
+ <inline>0x37efeffb592df52c0080de1a5074505fb9bd7d6f,0x0000000000000000000000000000000000000000</inline>
+ <curve>composite/composite160</curve>
+ <desc>order = 2</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite160/2</id>
+ <inline>0x68684425389f5552a24b7c205e19da7a0c10a1cb,0x825ecf13c08f314cd6ad5eae73044c71e9876409</inline>
+ <curve>composite/composite160</curve>
+ <desc>order = 3</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite160/3</id>
+ <inline>0x61602e67d8e17442afb37c07ea9fff2beb6c5b63,0x525e64d325c225855df22c141ab48292e2f8f937</inline>
+ <curve>composite/composite160</curve>
+ <desc>order = 4</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite160/4</id>
+ <inline>0x041fea694918a16ede5e6c32e24f52f7827b7942,0x0c4005ad4b02d3f04ba662cca7c5ae4de9a9ba1e</inline>
+ <curve>composite/composite160</curve>
+ <desc>order = 6</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite160/5</id>
+ <inline>0x896db2058ec6de5bf0c3d4729449c161f3a6513c,0x168828b2d35afa6ae22cfb88470444ecfe31565a</inline>
+ <curve>composite/composite160</curve>
+ <desc>order = 12</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite160/6</id>
+ <inline>0x5addb1c0c2b73b1c3bfa6ee1d4fa55c3d6cb79c8,0x18cf3b6fc6ac06a6af69817234799a7bed594e16</inline>
+ <curve>composite/composite160</curve>
+ <desc>order = 107</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite160/7</id>
+ <inline>0x2a8b120fa9fb0901280068fac4c7343c91707974,0x3c64f73118aa3e6263f988428d4f8e931c835dbd</inline>
+ <curve>composite/composite160</curve>
+ <desc>order = 214</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite160/8</id>
+ <inline>0x09814e9b98b521aa510d24e2eb68484f9cd4f446,0x030f959368478d906bd830105e1e645f9a3e7aa7</inline>
+ <curve>composite/composite160</curve>
+ <desc>order = 321</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite160/9</id>
+ <inline>0x475d0927fdea77619a4c0f3d7f78fa17b1a82ebf,0x0f9ef6e4b67034f84f2568cf93bcd6244f99befb</inline>
+ <curve>composite/composite160</curve>
+ <desc>order = 428</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite160/10</id>
+ <inline>0x441a137ed2bbe1397492bacec4d8d98e54e88fcd,0x1ba3217b753b1fd483ebd0eb6ce6642ab8f9b1d1</inline>
+ <curve>composite/composite160</curve>
+ <desc>order = 642</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite160/11</id>
+ <inline>0x8fcb8221093a80fe3ab7ee7ce952ae78e21177c3,0x29c944efc6e04c8a3393470be6eff62e250795c8</inline>
+ <curve>composite/composite160</curve>
+ <desc>order = 504</desc>
+ </pubkey>
+
+ <pubkey>
+ <id>composite192/1</id>
+ <inline>0x8220bcea81992c46bc57e0dae661eeff29e66d64b33253c5,0x000000000000000000000000000000000000000000000000</inline>
+ <curve>composite/composite192</curve>
+ <desc>order = 2</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite192/2</id>
+ <inline>0x94863540fdd9e8f415df79e18aee4bdd0914127581b6bb15,0x3aa760e488d12f8f93b10da531e1dbc033db25729119839f</inline>
+ <curve>composite/composite192</curve>
+ <desc>order = 3</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite192/3</id>
+ <inline>0x0cdf12ec0389daa5dc4fb9a877082e7acc4d7054d7eb320d,0x2a135f63802b9bb9064ffe6d319942ada312b0a06a506c68</inline>
+ <curve>composite/composite192</curve>
+ <desc>order = 5</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite192/4</id>
+ <inline>0x3d2cf1aa8ef6db0076b8e026f5f5eeb612bed45c2bf57796,0x40b1b1c78925a90c4f9c994ee9ac8a97ce8e1e5613676ec1</inline>
+ <curve>composite/composite192</curve>
+ <desc>order = 6</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite192/5</id>
+ <inline>0x069f245ec322d6ed422f18b4df1b2728bcff22a08c160de7,0x792466cd587549a3ffb4dbe491d54e5df25494ba9be83472</inline>
+ <curve>composite/composite192</curve>
+ <desc>order = 9</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite192/6</id>
+ <inline>0x649ddaeba68589618a326232704a4324957c8191fd558c60,0x9461496a59b680b82f199144c29323e061273efa62a60faf</inline>
+ <curve>composite/composite192</curve>
+ <desc>order = 10</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite192/7</id>
+ <inline>0x319a75780c9090f769b69b082ad16dbce0e8bdbc16d04cd5,0x76b03040b524f21b3dd5008995459ca8ed35dae3722c7035</inline>
+ <curve>composite/composite192</curve>
+ <desc>order = 15</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite192/8</id>
+ <inline>0x77acef779d8562b4492309adf946f5970bb83190ad76a2e3,0x3e98d9c7b72a8bfd5c6f6a7c07478df003d273b1326caa6c</inline>
+ <curve>composite/composite192</curve>
+ <desc>order = 18</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite192/9</id>
+ <inline>0x1e90f09d5328536457f6d9fe58444f086e32229007ef12fc,0x26407fbfba9f796afa97ea2c2e986d3343d711015b220f44</inline>
+ <curve>composite/composite192</curve>
+ <desc>order = 19</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite192/10</id>
+ <inline>0xa2e5982912dd423cfb745a01088e540b41e19ba84107cddc,0xaa29fc9dd974238b9daf73997d3c4dd331584d87356f9549</inline>
+ <curve>composite/composite192</curve>
+ <desc>order = 30</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite192/11</id>
+ <inline>0x670c2e74df1cf4f473ab37ef685b2a915858863bc9c67868,0x2edc6b47a9fbe500c2f4f4b79bb44b229dd99194aca26d5a</inline>
+ <curve>composite/composite192</curve>
+ <desc>order = 38</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite192/12</id>
+ <inline>0x14c936965c39188192d431aaf96f37e0d398b5a858933b3c,0x19c6526df20625d92445007603f19dd120702351869f9ae3</inline>
+ <curve>composite/composite192</curve>
+ <desc>order = 45</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite192/13</id>
+ <inline>0x595694d6d70b13b372b2f1b4daf5ffd391513d9542b0d600,0x099f0ac0ce38d9920f3c59a63433230f4f945716c8d246f9</inline>
+ <curve>composite/composite192</curve>
+ <desc>order = 57</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite192/14</id>
+ <inline>0x09cf293ad9517ef1932fb81a4800d60a768356fd2980ae77,0x1b12ab568cb4ac07593bb7cd6bc8a9364bac3c345419026f</inline>
+ <curve>composite/composite192</curve>
+ <desc>order = 90</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite192/15</id>
+ <inline>0x9424bf6ba15758fc88df622be2a7055ebd34bb0e8e89c945,0x2611aaf3752880c39238f669d910ca591358e5aa06f95119</inline>
+ <curve>composite/composite192</curve>
+ <desc>order = 95</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite192/16</id>
+ <inline>0x4289f802fa238ded56eda2164532f205cdde5b9ba58b8226,0xa2e1cdff2ae8312f4882ff44e4ef1d9e16be424fecd624e3</inline>
+ <curve>composite/composite192</curve>
+ <desc>order = 114</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite192/17</id>
+ <inline>0x5cabb4f80e166127067104af1327a8531c053ec510702d6a,0x2fdd3945ffdde5e5389ff43ec323031f9c39b795fbb9f41b</inline>
+ <curve>composite/composite192</curve>
+ <desc>order = 171</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite192/18</id>
+ <inline>0x9a6f5c897b695025e86d74043bf6a5a2bdf38ea7e2fe8f0e,0x4367779cadb1f8a193ac73dc51293aa0e94e930456e89692</inline>
+ <curve>composite/composite192</curve>
+ <desc>order = 190</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite192/19</id>
+ <inline>0x30f786b7a6a3292c40a1a2ac6934646595787c4f003a08c3,0x84e4b9970c123d1f5188fca8f27152d377d85f2352543992</inline>
+ <curve>composite/composite192</curve>
+ <desc>order = 285</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite192/20</id>
+ <inline>0x042d015cd6633de4ed1700c346774ae17263b3e284b43162,0x7c27c4593d37115d93bfd33797ae73f0805117b032289e46</inline>
+ <curve>composite/composite192</curve>
+ <desc>order = 342</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite192/21</id>
+ <inline>0x9f62978f1018599e5139fa6560fe3a39c108674f361b1389,0x884e25851a5de235df686fbb870e6b7b81accb8bbc63cd15</inline>
+ <curve>composite/composite192</curve>
+ <desc>order = 570</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite192/22</id>
+ <inline>0x29d846d8f363bc58149fdf6eaced9dbc7692a0942f6ecabb,0x784af0c66238ef9cdf900eee2fdce8081d2aa59dd327c8c3</inline>
+ <curve>composite/composite192</curve>
+ <desc>order = 855</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite192/23</id>
+ <inline>0x3c6d33801293213a71f4a7d9b1091cabb89bcf7fc266084e,0x80eb1f866a3774c45811feaee8cda2f020e5aec70a2b5233</inline>
+ <curve>composite/composite192</curve>
+ <desc>order = 1710</desc>
+ </pubkey>
+
+ <pubkey>
+ <id>composite224/1</id>
+ <inline>0x14f89a6ef687659649fd6e0e6cb1f7f27c0f9f94fc872e7f54a9c856,0x00000000000000000000000000000000000000000000000000000000</inline>
+ <curve>composite/composite224</curve>
+ <desc>order = 2</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite224/2</id>
+ <inline>0xb4fbabc7cf96b62b08edbaa2df53346bbb871c121bbb35e771c74db5,0x61cf8b556f068f45ec69963964a0e8ab72c1fa0be48e2ea886235956</inline>
+ <curve>composite/composite224</curve>
+ <desc>order = 3</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite224/3</id>
+ <inline>0x5b98ef6f104d0cc4159cc793d52713bafbc2b37a9f64af8f962b1c5a,0x7935aef16b1cb800beca9ab322aa8ecf8281f870057a8e1fbdd72490</inline>
+ <curve>composite/composite224</curve>
+ <desc>order = 4</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite224/4</id>
+ <inline>0x3abe85a7ef34758c487373eb1f193a86f2b073b23a42ab3753596308,0xca9743c8ebf2fe6ceb300b93e0742ebe5f594b2ed84dba7f42f7aedc</inline>
+ <curve>composite/composite224</curve>
+ <desc>order = 6</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite224/5</id>
+ <inline>0x3b931bbe008a038b5a5b03cf34c4102ba919579bd0a81f066193ab76,0x794f623ed1525010559240ad899b19a841bbbb818a64f32fc28c9931</inline>
+ <curve>composite/composite224</curve>
+ <desc>order = 8</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite224/6</id>
+ <inline>0x6ca8b206ce077be28ff56c18295508ade9fce4a051d4975d61ee84d0,0x1f5720ccc870e31e83eb58f7c7c5ada471f7502ae5b2d87a69109df7</inline>
+ <curve>composite/composite224</curve>
+ <desc>order = 12</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite224/7</id>
+ <inline>0x45437959ee88358cd8ee9902139cf6c9eee124c8ee199bd1a1ecc2da,0x756ad18b140d6a13a010fc21f1c4cf45a67eeb4c3dd4b202e9ae775f</inline>
+ <curve>composite/composite224</curve>
+ <desc>order = 16</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite224/8</id>
+ <inline>0xd2074198f477e79a92918d4a5bc0ba104de4b0369104a8b51012595d,0x5025127d318e3c34e4fbbefb397874a3ed50a9cfd6ca455d4be02125</inline>
+ <curve>composite/composite224</curve>
+ <desc>order = 24</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite224/9</id>
+ <inline>0x4513ad86e9415caae932018d67c869dbbe001cd53e64d82d205f4e2a,0x130dbd3e71618ddb0464bf3e894c30e1dd3bc25bb01e09e5b50a001d</inline>
+ <curve>composite/composite224</curve>
+ <desc>order = 29</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite224/10</id>
+ <inline>0x2f4f470b72c093667e3f433a5189988adbb0fe1ae36482c3eeecf8a4,0xc1ac6c167ff41b0842f6470bac49d2a518da6c4c456c5657be15dec6</inline>
+ <curve>composite/composite224</curve>
+ <desc>order = 32</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite224/11</id>
+ <inline>0x58741bd7ac127ecf8aa7c9fd78d22d81f8c10bb971bb77c015f69cd6,0x52e914fc15edb4af4a53ebeaf8466d2b7cf1bfe4a78c036a8faa5cdf</inline>
+ <curve>composite/composite224</curve>
+ <desc>order = 48</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite224/12</id>
+ <inline>0x7a06eeb07784de6a15d1322243b4edbe24eb9d24869e1d9b7b883686,0xba5f157579eda39cc85cf04cd48f710133716a0fa0f5a48c3948381c</inline>
+ <curve>composite/composite224</curve>
+ <desc>order = 58</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite224/13</id>
+ <inline>0x67d78ae1eaa30a898a3497c0cff43675be6d8cd4e41971661e8622b8,0x2e7337490aa8220d721167f7d047952af1d68615b07619607c771f5d</inline>
+ <curve>composite/composite224</curve>
+ <desc>order = 87</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite224/</id>
+ <inline>0x30eea9b5158e7ba9ba6a2a955942e3324c6539a70e78270abe43f7fd,0xb3eb76bbcac6cd68e28688d70f431d26147bbb380bf1938d8038418e</inline>
+ <curve>composite/composite224</curve>
+ <desc>order = 96</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite224/14</id>
+ <inline>0x4a27a6d15ecdfa776161aa180a4cd2de898610aac1b274f7a85d7ef1,0xd92847404473b63060e1514fe6a431130a79512c867d89eda0e3c674</inline>
+ <curve>composite/composite224</curve>
+ <desc>order = 116</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite224/15</id>
+ <inline>0x6a7ed0907ecee744da1a57ec48ff6cfc0d8d77c67a585fd9750081e6,0x96106e57beafd660a8622f6341967fd4565ce9f4f09793cc3f287316</inline>
+ <curve>composite/composite224</curve>
+ <desc>order = 174</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite224/16</id>
+ <inline>0x52c53640843d85f9945b18452a96e48816bbb52c76d012b3bd197f79,0x9630aba4de01758e0aaddef1de1cfb070af629f80c3ad3bb21e3cce5</inline>
+ <curve>composite/composite224</curve>
+ <desc>order = 232</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite224/17</id>
+ <inline>0x88166b34bee1151dc0589da4bed6da8c6a2e9d4c78eb4ef21c5a9efd,0xbb92af84b1faffb9cbd516316e2c58dabd3867c5ac4dccb4b3d7c25c</inline>
+ <curve>composite/composite224</curve>
+ <desc>order = 348</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite224/18</id>
+ <inline>0x02edb51c0bc83b37cc89d2a7eab42719f0c847d61334022b6ca765c6,0xe08729140e552810499c414488de5751769d595940be05992e1e2977</inline>
+ <curve>composite/composite224</curve>
+ <desc>order = 464</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite224/19</id>
+ <inline>0x96967cf6b0ae919e50f815ac8ebcc35d0625b518d3fd095224b3c70a,0xd7d51361df21536593163d588bb4843a3ea53bfb114e43afa5eae2fb</inline>
+ <curve>composite/composite224</curve>
+ <desc>order = 696</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite224/20</id>
+ <inline>0xb2b0a8e35a9597549a98a150f2b2a311feb560c99923e5ad23befcfa,0x6275928b50ccfd639b81c77c65a8d016c7ea4a035975e871dd10ef5d</inline>
+ <curve>composite/composite224</curve>
+ <desc>order = 928</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite224/21</id>
+ <inline>0xd22b61d5ba5ee04d963bedcbb11165c23990b7053e5d9aef656eb078,0xa000d64563591df0b2f9e270247bca57b242e70f58ed57b4a8acec2e</inline>
+ <curve>composite/composite224</curve>
+ <desc>order = 1392</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite224/22</id>
+ <inline>0x1f126f0463995cd293799fc58cb3efa1459a539cbd2d88c8d7283cfc,0x51175df02b5884fce8bfd17a7a97ccf12a5fdfdec4c297b301e477cc</inline>
+ <curve>composite/composite224</curve>
+ <desc>order = 2784</desc>
+ </pubkey>
+
+ <pubkey>
+ <id>composite256/1</id>
+ <inline>0xadd83091be650ca4d78b81c1ae2851a9197a5fe33a136d368ecbb1fe06200764,0x0000000000000000000000000000000000000000000000000000000000000000</inline>
+ <curve>composite/composite256</curve>
+ <desc>order = 2</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite256/2</id>
+ <inline>0x8ca20dd1fa045339a171513fb1daa25fa7439e4b97c129c6039e4b9abbac1532,0xb565bde8fe9831f0bce07d70784dc1b7064c443b54b5e96408c1942e30437cc3</inline>
+ <curve>composite/composite256</curve>
+ <desc>order = 5</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite256/3</id>
+ <inline>0x7f25698dc1a9a0810dbd97e1918ea4a78f20783ce3ed4133df0fe6f66fe29c3f,0x0eff3a76e05d5e24b6b57c13f704ea2a54750502000c9d3f04d1ad932cfb6d3e</inline>
+ <curve>composite/composite256</curve>
+ <desc>order = 10</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite256/4</id>
+ <inline>0x8b6be31db337293200eb7a6a2ac144374223a4f76ea7b003eb789d0622cbc806,0xd9556692a39eb79d29c0a019d94f88737edd90006f55ecdc1ac204c81eb0c32b</inline>
+ <curve>composite/composite256</curve>
+ <desc>order = 17</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite256/5</id>
+ <inline>0x497170999460d32516062fa49929789e645a46e2db216e83a7f738d2c5c482d6,0xa1666de87f4f0de0ed523a962d386349d87b4e40c7c6db555e52e33da3de738b</inline>
+ <curve>composite/composite256</curve>
+ <desc>order = 34</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite256/6</id>
+ <inline>0x409a4d05651cfa87af444fa58f9d3fa3d5bbd25a8c9f7a6be200b5255bb06bac,0x3a08b088284a3da5c59246379b9b7aa36a86917f58c390cc49d2172e33d5c094</inline>
+ <curve>composite/composite256</curve>
+ <desc>order = 85</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite256/7</id>
+ <inline>0x5ebd412e2785d08f2fd0403a3df2d028a4c33c887637d75ce05543cb6c33f172,0xa3f76e00200df483e710db5724c08ef01f813959ae3205b5e4e3c7a051d303b8</inline>
+ <curve>composite/composite256</curve>
+ <desc>order = 170</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite256/8</id>
+ <inline>0x756ab6f97c43c68c38c8070766c3d86df422f8bc9c8e5ff8e8fee0af683b58e0,0x08746e1b87c5e8e582fa8a4e13b5971728af1e6c6f74a28478b0a606357c17b4</inline>
+ <curve>composite/composite256</curve>
+ <desc>order = 289</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite256/9</id>
+ <inline>0x0e02b7a9def8eb46986ad908a49109267beb19e331bc399eb036ec087c2081c5,0xa1e1fff1a84a3b131c072f76c1ed54293f5d82dc5c9c78c9984786648d172cb2</inline>
+ <curve>composite/composite256</curve>
+ <desc>order = 578</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite256/10</id>
+ <inline>0x5330e8ad3035f2b256091a362313ae5b7f183d11c3fce1528443cf2626911e09,0xb3abb61c1ac189a3980f8c6dfc4330997096dca089273000a7302682b40ff6bc</inline>
+ <curve>composite/composite256</curve>
+ <desc>order = 1445</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite256/11</id>
+ <inline>0x31f4f374d564ec73cd4e066ec84cdfa89be9389c2c0e557d0226af8f4a960768,0xba1b2169c90ea6cf612db29e4142c1483f9268012114148e28d35969669a7549</inline>
+ <curve>composite/composite256</curve>
+ <desc>order = 2890</desc>
+ </pubkey>
+
+ <pubkey>
+ <id>composite384/1</id>
+ <inline>0x5136c182f03241ec87e6eec1728c1fb2d6b2ddcd4d9abf2a110c337419c4ec7cc8386b7b9ea9f5cb18b0f3c2a78e6489,0x9130cb73f8064fddb24d8c6a216b57fe99df93bcc82c93343617a5246ca1643fe57a06d6112a1791d1bd3643fbd9c041</inline>
+ <curve>composite/composite384</curve>
+ <desc>order = 5</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite384/2</id>
+ <inline>0x20070cc3e17bf1c770ef297b679c54d6454a3cd5effd94e236677b10e3e8075a8a9902ecd71e930f19fda4d79931502d,0x04a21190dbba4b6efe62640887256163ba5c9fa706aad74f1bfebdc27ca6f9e2f9b37bf9abf2c72803968799e2c7e702</inline>
+ <curve>composite/composite384</curve>
+ <desc>order = 11</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite384/3</id>
+ <inline>0x6d24691bacf187946f24885f2bec9a6b09239f4b7d93651c8c62f0221ba695baa5f70c49028ca5bc462289edcec081fe,0x7489fb2fd719c84ab0a12eed6f5f45fabb7c19d49d9d185a4d4efcffc9d2110a1b5a04961251f1ab19908662c8dadc9a</inline>
+ <curve>composite/composite384</curve>
+ <desc>order = 55</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite384/4</id>
+ <inline>0x0c5dccdb9c83f4d3cad69213b80f2c969210a228b85dce49c8a31ad08cf232118c6eb03d3b7e3d7bc8add230c24d184b,0x6316b45ee6832912cf3d54fc1fb73de47753ed225074cf76ebcdecb5f0c797e166b90767e2f267a4ea6b43cf6cde23c1</inline>
+ <curve>composite/composite384</curve>
+ <desc>order = 61</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite384/5</id>
+ <inline>0x32a43b215a94626d6f28974d5622469ebbdb7a54fef268267066fa3199f343f044248f39bf6ef10bdfffa6cb80155bb7,0x3d6a14e4bfec02b2306649a6da4fbbac157cecb3faa6cc9e62b5a469ea7f17a6f5e66a7c6753eee3f9cb7bdd4d0f8a4c</inline>
+ <curve>composite/composite384</curve>
+ <desc>order = 305</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite384/6</id>
+ <inline>0x5baac92f9d31ffd1cf982566f6d4bc239350ffae1f6b068ec07fb200795f4a902978d080a70d5908bd8ee283723ee196,0x164584fff8bb749bb781eedc62f9a39f658490969d24bff24b5c91f7af7eb7dd967b0066daf54678788f241b67d1a78b</inline>
+ <curve>composite/composite384</curve>
+ <desc>order = 671</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite384/7</id>
+ <inline>0x3b2d81fd9001d4b208e9d6312553bc3c2ca0853145dd882188b4b197152faca8f1194826c745f74c90d96594dcc0794b,0x8fef63c95e804b6ab0bbe7142bb56ecaa24737e8f97466d68f0c9ca7c230480b28d958d69277fcbcd77f6a566423ddec</inline>
+ <curve>composite/composite384</curve>
+ <desc>order = 3355</desc>
+ </pubkey>
+
+ <pubkey>
+ <id>composite521/1</id>
+ <inline>0x01221fb007e1c2f7413b4d79a0265baf9d9ea2850e9e262b28ef5ead6f5f505d05cf731a891a443e7c1a35c867d7a3de4118f76d1cb68f6479eb85fe87fc9c6be098,0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</inline>
+ <curve>composite/composite521</curve>
+ <desc>order = 2</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite521/2</id>
+ <inline>0x00a4b42ad90c0e3f7e342d8d661b4d5162ab7928b4938ab660b2e6fea3213c5d4b420123f65141a8eb7b4a46173bfce6ea1577df94f6f934f72d459c4dd3c0ef038e,0x003316c4b6c5c6b3ab3eee7f1ff365cce6045fdc43d4e6c64efa7789f2626676b47b488e6612d291d60d4a788ddd2e8b1aa8bfff02e105a285532a20ac08fa1088e7</inline>
+ <curve>composite/composite521</curve>
+ <desc>order = 5</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite521/3</id>
+ <inline>0x0039ccb35a071951bda5c9c40edb1fbfa365ce59a2a89ee37739708d748cc66bb19cbcfa30cc4ef6bfbb48728e930d940e30c64b6d77c0e64c79c1bad49540084662,0x00d7016bd5016c8ba9601ec3dd44f77b8ff7bb3aaa6c358ac78bf931f7f4d140e4de1912c343bfe7956ab0b29aa9ec0922008d025e9895a8141bd7c88f913e745e29</inline>
+ <curve>composite/composite521</curve>
+ <desc>order = 10</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite521/4</id>
+ <inline>0x00426f8691e7f8afc0f7443206d3d44779b96c942714386733853530d3af4944e1ad38c7c1047f50e2bf8db7dfc0d4a8803976da934eaaa19e478ee026736ae8ac6d,0x00c97e0d2dedcd7b601430f1b8959c26c24918f6c9bd803bfe8cc015e7285956f878f27a33c91f64fec051f478cadcb7ed80c21127a7916d1e2c88ee921f1f65cf94</inline>
+ <curve>composite/composite521</curve>
+ <desc>order = 25</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite521/5</id>
+ <inline>0x001550be76b460f82f2fbf3ebfd9709c50c07ec18c250c01b7d934eda2bcf3b78f648b7dd813cfd58a3f12ab35ad015c03ed0e14af6ef1560706dd59fafc32ecd5aa,0x00eb5368f5c53f70d88bbb1f31afe0d730aac4523b998043b2029a441528f7ae3483773117184e8d2efc875fe5447b0eb4ffb781bb014c67505434477fc4d6e343cd</inline>
+ <curve>composite/composite521</curve>
+ <desc>order = 31</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite521/6</id>
+ <inline>0x016bad260ce10570e179138810152059e4ed8d1a6f5d8f8f2697ff98e8a1d8400cb5fb2334e5ccd88c3933add314e1f94b67ce99a4594da7754b39d58262c2275827,0x004a9b47353f119a01bb8dff6f63b58499a83c20a6fb8cbf84e717e35643c23eefef157891ca720b332355b70ee265bad5e057958ee42ab6a0f0675aa85caaad4a8d</inline>
+ <curve>composite/composite521</curve>
+ <desc>order = 50</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite521/7</id>
+ <inline>0x01320c8762348be979ddf729b42a3839bd8e6a99923b3bab663dbb298f470d0e3aac375a1d1aa7119ec5b2e82c7921f192bea4ac81b7259af3a417135e82ad56aa4b,0x01c0be727f0480ea5137efc3fd201fca223ecd58d34dcc31cbfd6b7909b3033bb7d65e006e4dfe7c80f2a75d0bb5256986077d16cdd3c4d74bddfedc700c5be39541</inline>
+ <curve>composite/composite521</curve>
+ <desc>order = 62</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite521/8</id>
+ <inline>0x0097c707694d550fe1533a9ce461e0317206385ee961cc3c646cbe84afc17a426023c8dd1e8db9df81bfd01b8cd123fd381c7fffd375f1776eb69994c94d67088ddc,0x00f3c16b5bb57044a555c5c89dc0fe1d2dec3406a645e66811d9a466a687884133cb9863394ddb19e2888d1f311d88060b0601f960d8a2ba86f67b4e1c8dad61326b</inline>
+ <curve>composite/composite521</curve>
+ <desc>order = 155</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite521/9</id>
+ <inline>0x014ae6ee5a69767d8abb24d62d0435807e36d9c42572eeb2a7d2852837f68d62dd97c4ae4ed4b47f1208e9b80c0e285b69db277be2072e104b1892892be5e09d1d6a,0x0008a34156d7f671159b1bd85ab0413092fb16de8a6ea9ddae20c427262d2636b926339bf1ad1a2a1518aba13d3ce858f7b232971ebdb3b54b61e1273defd0c20ba7</inline>
+ <curve>composite/composite521</curve>
+ <desc>order = 310</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite521/10</id>
+ <inline>0x00f6d940ce18d53a7706999da53c40cb8e4581a73069ae4358747257a169ab1532474b43e506603fcbd743b63b48551faa3bcac5fb1a40ca2b3f49f369fec36b7c7d,0x00f881dbc0ab0b74c20db84810eb476b1fb852398e87f8d909873f0b73cce0166baf43f07487a7a35a172000f5050c8531b108afb03c2654fa4a81b0538b58c4ddb1</inline>
+ <curve>composite/composite521</curve>
+ <desc>order = 775</desc>
+ </pubkey>
+ <pubkey>
+ <id>composite521/11</id>
+ <inline>0x0012538aae315d49a9b443382a19de468f57c7ff2494ecbaa54e927d3affb6d1d6503cb3acfbb0feee44caeb7fbef8804ac073c709a90eb3c7464a6644c9c5581e9b,0x01afeb2c9e1b519804e3a73364953368f915e6abc765adbd3ec7cfc2b808b1b4b46d575a664d06b4b455850cf295fd69eb823ca069cd2de90542db1496ad9095fad4</inline>
+ <curve>composite/composite521</curve>
+ <desc>order = 1550</desc>
+ </pubkey>
+</keys> \ No newline at end of file
diff --git a/src/cz/crcs/ectester/data/nonprime/keys.xml b/src/cz/crcs/ectester/data/nonprime/keys.xml
deleted file mode 100644
index c10ae65..0000000
--- a/src/cz/crcs/ectester/data/nonprime/keys.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<keys xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:noNamespaceSchemaLocation="../schema.xsd"
- category="nonprime"
- desc="Points on the non-prime curves, very small point orders(3-5).">
- <pubkey>
- <id>nonprime128-pub</id>
- <file>nonprime128_pub.csv</file>
- <curve>nonprime/nonprime128</curve>
- <desc>order = 5</desc>
- </pubkey>
- <pubkey>
- <id>nonprime160-pub</id>
- <file>nonprime160_pub.csv</file>
- <curve>nonprime/nonprime160</curve>
- <desc>order = 3</desc>
- </pubkey>
- <pubkey>
- <id>nonprime192-pub</id>
- <file>nonprime192_pub.csv</file>
- <curve>nonprime/nonprime192</curve>
- <desc>order = 3</desc>
- </pubkey>
- <pubkey>
- <id>nonprime224-pub</id>
- <file>nonprime224_pub.csv</file>
- <curve>nonprime/nonprime224</curve>
- <desc>order = 5</desc>
- </pubkey>
- <pubkey>
- <id>nonprime256-pub</id>
- <file>nonprime256_pub.csv</file>
- <curve>nonprime/nonprime256</curve>
- <desc>order = 3</desc>
- </pubkey>
- <pubkey>
- <id>nonprime384-pub</id>
- <file>nonprime384_pub.csv</file>
- <curve>nonprime/nonprime384</curve>
- <desc>order = 3</desc>
- </pubkey>
- <pubkey>
- <id>nonprime521-pub</id>
- <file>nonprime521_pub.csv</file>
- <curve>nonprime/nonprime521</curve>
- <desc>order = 5</desc>
- </pubkey>
-</keys> \ No newline at end of file
diff --git a/src/cz/crcs/ectester/data/nonprime/nonprime128.csv b/src/cz/crcs/ectester/data/nonprime/nonprime128.csv
deleted file mode 100644
index b666e20..0000000
--- a/src/cz/crcs/ectester/data/nonprime/nonprime128.csv
+++ /dev/null
@@ -1 +0,0 @@
-0xcfba21fd0483b1f300fa2506a5a566ef,0x36d9a5acac27a008e36cbe3e9f103fde,0xa67cf5fa09fb1db902068c87046ae21e,0x47d78391a4b9fff6a0db1292f9cd0e6a,0x9aed9c92f8bb3dbd42402165a270bd6f,0xcfba21fd0483b1f333d61a5af6ada2c7,0x1 \ No newline at end of file
diff --git a/src/cz/crcs/ectester/data/nonprime/nonprime128_pub.csv b/src/cz/crcs/ectester/data/nonprime/nonprime128_pub.csv
deleted file mode 100644
index a1fbe5c..0000000
--- a/src/cz/crcs/ectester/data/nonprime/nonprime128_pub.csv
+++ /dev/null
@@ -1 +0,0 @@
-0x63901e122761d9c16565b2f38e991f71,0xb9d99fbc3154a96ca23ecff770cbbe4f \ No newline at end of file
diff --git a/src/cz/crcs/ectester/data/nonprime/nonprime160.csv b/src/cz/crcs/ectester/data/nonprime/nonprime160.csv
deleted file mode 100644
index e685a11..0000000
--- a/src/cz/crcs/ectester/data/nonprime/nonprime160.csv
+++ /dev/null
@@ -1 +0,0 @@
-0xdc13490ff9857b111f44c0500770a6457e683223,0xa3ecd7d51e79d72d2700184c795aa8a6b8e66573,0x8ac43592905f995cb13f3694317bf470adafb645,0x5f8e88afc117c722859fe8e55647bca69ba82150,0x93e6dcaee271e9f2838c98b7d06eccc5d7c800e5,0xdc13490ff9857b111f446ef4a6d1e1715f6a6dff,0x1 \ No newline at end of file
diff --git a/src/cz/crcs/ectester/data/nonprime/nonprime160_pub.csv b/src/cz/crcs/ectester/data/nonprime/nonprime160_pub.csv
deleted file mode 100644
index 7060146..0000000
--- a/src/cz/crcs/ectester/data/nonprime/nonprime160_pub.csv
+++ /dev/null
@@ -1 +0,0 @@
-0x59c9c3c8aef29f1c1c500cafb4726da6086e6eb0,0xd695a76005eddb26afd40ee20904778bb3497bb1 \ No newline at end of file
diff --git a/src/cz/crcs/ectester/data/nonprime/nonprime192.csv b/src/cz/crcs/ectester/data/nonprime/nonprime192.csv
deleted file mode 100644
index c61f65c..0000000
--- a/src/cz/crcs/ectester/data/nonprime/nonprime192.csv
+++ /dev/null
@@ -1 +0,0 @@
-0xce714cc3a15ce7e5dab068c9a1f8be00aad480abccaeefc3,0x597c781f64c33eb8ef919c415911518ea323be88b9437caf,0xf81585a1b18f233d70add7ee1342d2035c386a92e3ab8320,0x150ff0a40deac6462b5987418617fdeeb6bfd76d4d60a067,0x843d577371c5dce122c2ff206b2f42fa0b842b49bdaf990f,0xce714cc3a15ce7e5dab068c9a30bc92915bd8662ae882887,0x1 \ No newline at end of file
diff --git a/src/cz/crcs/ectester/data/nonprime/nonprime192_pub.csv b/src/cz/crcs/ectester/data/nonprime/nonprime192_pub.csv
deleted file mode 100644
index 97c66cd..0000000
--- a/src/cz/crcs/ectester/data/nonprime/nonprime192_pub.csv
+++ /dev/null
@@ -1 +0,0 @@
-0x17047f91dbe33032c9d09bd29ceadd8a09ccc32ac6309541,0x6a726de54fbd59cfc352e838b337fa005a97180816135e6a \ No newline at end of file
diff --git a/src/cz/crcs/ectester/data/nonprime/nonprime224.csv b/src/cz/crcs/ectester/data/nonprime/nonprime224.csv
deleted file mode 100644
index cd4c5f8..0000000
--- a/src/cz/crcs/ectester/data/nonprime/nonprime224.csv
+++ /dev/null
@@ -1 +0,0 @@
-0xeed4c3d98f1c9b9518f116263db770366877d12df6a9cf08b96dd4bb,0x8d4dddb0317d6a6bf9a4dbbed3a43fa21f79869c5ab9729d239e9282,0x46873614be3dffc9218082322210c0616140286f2d160503c1a9250d,0x961bbb1fc9955a71c91a50aedcd2f14fccb660af992b0030b9c90b36,0x1c00f6d0bd405dd7d3016fb8c0c75e4ecec70fe61237f6d24008a5fd,0xeed4c3d98f1c9b9518f116263db821c36a06adae17162ad3162f68c3,0x1 \ No newline at end of file
diff --git a/src/cz/crcs/ectester/data/nonprime/nonprime224_pub.csv b/src/cz/crcs/ectester/data/nonprime/nonprime224_pub.csv
deleted file mode 100644
index aa2ab06..0000000
--- a/src/cz/crcs/ectester/data/nonprime/nonprime224_pub.csv
+++ /dev/null
@@ -1 +0,0 @@
-0xcfd92aea0f79190c48ca703eb8a9baa7099a23bb39578261fe4d0f04,0x257a3d98de44bd25404977a4ac7fc56d3d4e827f085b7cf5247524c4 \ No newline at end of file
diff --git a/src/cz/crcs/ectester/data/nonprime/nonprime256.csv b/src/cz/crcs/ectester/data/nonprime/nonprime256.csv
deleted file mode 100644
index 582b115..0000000
--- a/src/cz/crcs/ectester/data/nonprime/nonprime256.csv
+++ /dev/null
@@ -1 +0,0 @@
-0xc9a803b1eaf849f1c02cfd1dbfac68623985c88b37103b338ae11d2597ee8445,0x4841c5775a24a884ca36ec362b44645a2f60b25d002c4fc1d9f139870fe0cc71,0x1b097456751f3534190dae568f80a2c6ff55dddfe072a7dc6467a4b6476b6880,0xa1fd34a27afb1340b8e4a7db2a5ec5a1432c6dc8555af9f78fca2cf740cab2b7,0x98419c698cab6c7dbb53eb2751417b52ccded4680c5e09543f93c7886c3a173e,0xc9a803b1eaf849f1c02cfd1dbfac6863128c5b1fc5acd5b5e0fc0a7311fb5b1d,0x1 \ No newline at end of file
diff --git a/src/cz/crcs/ectester/data/nonprime/nonprime256_pub.csv b/src/cz/crcs/ectester/data/nonprime/nonprime256_pub.csv
deleted file mode 100644
index 60f475c..0000000
--- a/src/cz/crcs/ectester/data/nonprime/nonprime256_pub.csv
+++ /dev/null
@@ -1 +0,0 @@
-0x75fce70968862d53e29548aad70582514e960d8128bd3c5f8c4dbe2cf8dad653,0x55aa4b7d3882fb0a83bd00c9c3bae17f1024d64aec67e1db38ef671e6350beae \ No newline at end of file
diff --git a/src/cz/crcs/ectester/data/nonprime/nonprime384.csv b/src/cz/crcs/ectester/data/nonprime/nonprime384.csv
deleted file mode 100644
index ffcbe91..0000000
--- a/src/cz/crcs/ectester/data/nonprime/nonprime384.csv
+++ /dev/null
@@ -1 +0,0 @@
-0xd0df6c96cff7081be80d22b005758a2e2f046e15fe020ef886e21b492ac57257a923144bcad989ab6341bd3b700f914b,0x45c64503be019afd3462b361ad2b2a3bca0aeccc5494a624fb632455e62b4f0c98f944fa97c37811da039823cd77c906,0xd85583f7f11ad23ec75ed5a414153a06d6640936b8103f5df691fa95cf2afa78f3ea5addc225b144964048c9f7592ae4,0x2b1341d12dff4f9cf9427c4752962b4c2bdc8fbcd80652516c421cc523212a01ea63c79d6e9a9c84933e353e212416ec,0xce416c6e75fa9fd205ed48fc4e3099cbb1d6ed031b7ddbff1d634eb97a83d9b780cfd4dedfdd2c7604d143196c08d933,0xd0df6c96cff7081be80d22b005758a2e2f046e15fe020ef7664ed51d7701c86bf2a1e9f3002c26fe002314c3c92f1ca9,0x1 \ No newline at end of file
diff --git a/src/cz/crcs/ectester/data/nonprime/nonprime384_pub.csv b/src/cz/crcs/ectester/data/nonprime/nonprime384_pub.csv
deleted file mode 100644
index 236602e..0000000
--- a/src/cz/crcs/ectester/data/nonprime/nonprime384_pub.csv
+++ /dev/null
@@ -1 +0,0 @@
-0xa4bd575bf20300b0cf8a2f41dd5a03e908966a4229a5f22f5c190d3641ac2d32b7b24a63482cbbcd0c2257f834834ef1,0x38d51c8f9e90592f567e81d0e4855e79731b5797857a4c7dc270653bc9f0c31e84693007b09cebf710d5ae3237303949 \ No newline at end of file
diff --git a/src/cz/crcs/ectester/data/nonprime/nonprime521.csv b/src/cz/crcs/ectester/data/nonprime/nonprime521.csv
deleted file mode 100644
index b65c980..0000000
--- a/src/cz/crcs/ectester/data/nonprime/nonprime521.csv
+++ /dev/null
@@ -1 +0,0 @@
-0x01d3df430924956e210a605b4dbf4a2e909d7a801658978c88ffd68dcc817f5cc79cf188d9ee82d1a51c44cbd31e9cc5b816d76d5b1312b005f7b68919e275dac99f,0x00401639f36f2ee45fc164ea3e1f14f4803fd7a77ffdfb392c3f8fe95d1aea331467f4618d59aeee49d5d7c70caf320f7dd1ac166114f562413449991d3aa1a2c49e,0x004a26a8c47fce204ba953015fa86708c0de720f27523988b097e774168c15f7a215aaf18a5f1b9579ab3db935d45be14c9a87b71170396909b14d06f7a09975b3a6,0x01c880ae0a355a52791fc9600fd8b35726e9d799101489161c8f90a9c6631d09b3cb347584837d9deb8566a9c5846aded0d01eb947b4affd34e8ea7dbe733cbedafa,0x00050f12672f163f19d5d493eb82ef777b0213dd4e0cf75a9b99724fbdb54b0cc4e037bf86a48bac28467bdd936c314ce13f6ec7ec69ea09ae4f5444df4b2a117a66,0x01d3df430924956e210a605b4dbf4a2e909d7a801658978c88ffd68dcc817f5cc7ba0838717c1947f93cfdd3ed87ec2c2df181c7ada553346ec1495732a1e7ffe9b3,0x1 \ No newline at end of file
diff --git a/src/cz/crcs/ectester/data/nonprime/nonprime521_pub.csv b/src/cz/crcs/ectester/data/nonprime/nonprime521_pub.csv
deleted file mode 100644
index 9695924..0000000
--- a/src/cz/crcs/ectester/data/nonprime/nonprime521_pub.csv
+++ /dev/null
@@ -1 +0,0 @@
-0x002844df0f31f46a40e6c7006cde99155bd5d18d0e4150178a8e307d6aec08fd02d466c03c49b49c2654b7c9a32d88ca014016a7eddd44217be915505d228efb9389,0x0105921e2172c3050ba4c9d2e744fc5b7b5e8451751e6780c6de88229497be7d23550beefa0cb7fafebb4dd9fad1244c6733befe5a97710f0dc56dc08d9d9df9d846 \ No newline at end of file
diff --git a/src/cz/crcs/ectester/reader/DirtyLogger.java b/src/cz/crcs/ectester/reader/DirtyLogger.java
deleted file mode 100644
index 7a2c70d..0000000
--- a/src/cz/crcs/ectester/reader/DirtyLogger.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package cz.crcs.ectester.reader;
-
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * @author Petr Svenda petr@svenda.com
- * @author Jan Jancar johny@neuromancer.sk
- */
-public class DirtyLogger {
- FileWriter log;
- boolean systemOut;
-
- public DirtyLogger(String filePath) throws IOException {
- this(filePath, true);
- }
-
- public DirtyLogger(String filePath, boolean systemOut) throws IOException {
- if (filePath != null)
- this.log = new FileWriter(filePath);
- this.systemOut = systemOut;
- }
-
- public void println() {
- print("\n");
- }
-
- public void println(String logLine) {
- logLine += "\n";
- print(logLine);
- }
-
- public void print(String logLine) {
- if (systemOut) {
- System.out.print(logLine);
- }
- if (log != null) {
- try {
- log.write(logLine);
- } catch (IOException ignored) {
- }
- }
- }
-
- void flush() {
- try {
- if (log != null) log.flush();
- } catch (IOException ignored) {
- }
- }
-
- void close() throws IOException {
- if (log != null) log.close();
- }
-}
diff --git a/src/cz/crcs/ectester/reader/ECTester.java b/src/cz/crcs/ectester/reader/ECTester.java
index bb555f9..3de6094 100644
--- a/src/cz/crcs/ectester/reader/ECTester.java
+++ b/src/cz/crcs/ectester/reader/ECTester.java
@@ -22,20 +22,26 @@
package cz.crcs.ectester.reader;
import cz.crcs.ectester.applet.ECTesterApplet;
-import static cz.crcs.ectester.applet.ECTesterApplet.KeyAgreement_ALG_EC_SVDP_DH;
import cz.crcs.ectester.applet.EC_Consts;
import cz.crcs.ectester.data.EC_Store;
+import cz.crcs.ectester.reader.command.Command;
import cz.crcs.ectester.reader.ec.EC_Category;
import cz.crcs.ectester.reader.ec.EC_Data;
import cz.crcs.ectester.reader.ec.EC_Params;
+import cz.crcs.ectester.reader.output.*;
+import cz.crcs.ectester.reader.response.Response;
+import cz.crcs.ectester.reader.test.*;
import javacard.security.KeyPair;
import org.apache.commons.cli.*;
import javax.smartcardio.CardException;
+import javax.xml.parsers.ParserConfigurationException;
import java.io.*;
import java.nio.file.Files;
import java.util.*;
+import static cz.crcs.ectester.applet.ECTesterApplet.KeyAgreement_ALG_EC_SVDP_DH;
+
/**
* Reader part of ECTester, a tool for testing Elliptic curve support on javacards.
*
@@ -45,7 +51,9 @@ import java.util.*;
public class ECTester {
private CardMngr cardManager;
- private DirtyLogger systemOutLogger;
+ private OutputLogger logger;
+ private TestWriter testWriter;
+ private ResponseWriter respWriter;
private EC_Store dataStore;
private Config cfg;
@@ -98,7 +106,25 @@ public class ECTester {
cardManager.send(SELECT_ECTESTERAPPLET);
}
- systemOutLogger = new DirtyLogger(cfg.log, true);
+ // Setup logger, testWriter and respWriter
+ logger = new OutputLogger(true, cfg.log);
+ if (cfg.format == null) {
+ testWriter = new TextTestWriter(logger.getPrintStream());
+ } else {
+ switch (cfg.format) {
+ case "text":
+ testWriter = new TextTestWriter(logger.getPrintStream());
+ break;
+ case "xml":
+ testWriter = new XMLTestWriter(logger.getOutputStream());
+ break;
+ case "yaml":
+ case "yml":
+ testWriter = new YAMLTestWriter(logger.getPrintStream());
+ break;
+ }
+ }
+ respWriter = new ResponseWriter(logger.getPrintStream());
//do action
if (cli.hasOption("export")) {
@@ -115,7 +141,7 @@ public class ECTester {
//disconnect
cardManager.disconnectFromCard();
- systemOutLogger.close();
+ logger.close();
} catch (MissingOptionException moex) {
System.err.println("Missing required options, one of:");
@@ -154,11 +180,13 @@ public class ECTester {
} catch (ParseException | IOException ex) {
System.err.println(ex.getMessage());
} catch (CardException ex) {
- if (systemOutLogger != null)
- systemOutLogger.println(ex.getMessage());
+ if (logger != null)
+ logger.println(ex.getMessage());
+ } catch (ParserConfigurationException e) {
+ e.printStackTrace();
} finally {
- if (systemOutLogger != null)
- systemOutLogger.flush();
+ if (logger != null)
+ logger.flush();
}
}
@@ -204,11 +232,13 @@ public class ECTester {
*
* -i / --input <input_file>
* -o / --output <output_file>
+ * --format <format>
* -l / --log [log_file]
*
* -f / --fresh
* -s / --simulate
* -y / --yes
+ * -ka/ --ka-type <type>
*/
OptionGroup actions = new OptionGroup();
actions.setRequired(true);
@@ -216,11 +246,11 @@ public class ECTester {
actions.addOption(Option.builder("ln").longOpt("list-named").desc("Print the list of supported named curves and keys.").hasArg().argName("what").optionalArg(true).build());
actions.addOption(Option.builder("e").longOpt("export").desc("Export the defaut curve parameters of the card(if any).").build());
actions.addOption(Option.builder("g").longOpt("generate").desc("Generate [amount] of EC keys.").hasArg().argName("amount").optionalArg(true).build());
- actions.addOption(Option.builder("t").longOpt("test").desc("Test ECC support. [test_suite]:\n- default:\n- invalid:\n- wrong:\n- nonprime:\n- test-vectors:").hasArg().argName("test_suite").optionalArg(true).build());
+ actions.addOption(Option.builder("t").longOpt("test").desc("Test ECC support. [test_suite]:\n- default:\n- invalid:\n- wrong:\n- composite:\n- test-vectors:").hasArg().argName("test_suite").optionalArg(true).build());
actions.addOption(Option.builder("dh").longOpt("ecdh").desc("Do ECDH, [count] times.").hasArg().argName("count").optionalArg(true).build());
actions.addOption(Option.builder("dhc").longOpt("ecdhc").desc("Do ECDHC, [count] times.").hasArg().argName("count").optionalArg(true).build());
actions.addOption(Option.builder("dsa").longOpt("ecdsa").desc("Sign data with ECDSA, [count] times.").hasArg().argName("count").optionalArg(true).build());
-
+
opts.addOptionGroup(actions);
OptionGroup size = new OptionGroup();
@@ -256,6 +286,7 @@ public class ECTester {
opts.addOption(Option.builder("o").longOpt("output").desc("Output into file <output_file>.").hasArg().argName("output_file").build());
opts.addOption(Option.builder("l").longOpt("log").desc("Log output into file [log_file].").hasArg().argName("log_file").optionalArg(true).build());
opts.addOption(Option.builder("v").longOpt("verbose").desc("Turn on verbose logging.").build());
+ opts.addOption(Option.builder().longOpt("format").desc("Output format to use. One of: text,yml,xml.").hasArg().argName("format").build());
opts.addOption(Option.builder("f").longOpt("fresh").desc("Generate fresh keys (set domain parameters before every generation).").build());
opts.addOption(Option.builder("s").longOpt("simulate").desc("Simulate a card with jcardsim instead of using a terminal.").build());
@@ -325,7 +356,9 @@ public class ECTester {
}
sent.add(export);
- systemOutLogger.println(Response.toString(sent));
+ for (Response r : sent) {
+ respWriter.outputResponse(r);
+ }
EC_Params exported = new EC_Params(domain, export.getParams());
@@ -354,7 +387,7 @@ public class ECTester {
while (generated < cfg.generateAmount || cfg.generateAmount == 0) {
if ((cfg.fresh || generated == 0) && curve != null) {
Response fresh = curve.send();
- systemOutLogger.println(fresh.toString());
+ respWriter.outputResponse(fresh);
}
Command.Generate generate = new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_LOCAL);
@@ -372,7 +405,7 @@ public class ECTester {
break;
}
}
- systemOutLogger.println(response.toString());
+ respWriter.outputResponse(response);
String pub = Util.bytesToHex(export.getParameter(ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.PARAMETER_W), false);
String priv = Util.bytesToHex(export.getParameter(ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.PARAMETER_S), false);
@@ -382,7 +415,7 @@ public class ECTester {
generated++;
}
Response cleanup = new Command.Cleanup(cardManager).send();
- systemOutLogger.println(cleanup.toString());
+ respWriter.outputResponse(cleanup);
keysFile.close();
}
@@ -398,10 +431,10 @@ public class ECTester {
switch (cfg.testSuite) {
case "default":
- suite = new TestSuite.Default(dataStore, cfg, systemOutLogger);
+ suite = new DefaultSuite(dataStore, cfg);
break;
case "test-vectors":
- suite = new TestSuite.TestVectors(dataStore, cfg, systemOutLogger);
+ suite = new TestVectorSuite(dataStore, cfg);
break;
default:
// These tests are dangerous, prompt before them.
@@ -410,8 +443,8 @@ public class ECTester {
if (!cfg.yes) {
System.out.print("Do you want to proceed? (y/n): ");
Scanner in = new Scanner(System.in);
- String confirmation = in.nextLine();
- if (!Arrays.asList("yes", "YES", "y", "Y").contains(confirmation)) {
+ String confirmation = in.nextLine().toLowerCase();
+ if (!Arrays.asList("yes", "y").contains(confirmation)) {
return;
}
in.close();
@@ -420,13 +453,13 @@ public class ECTester {
switch (cfg.testSuite) {
case "wrong":
- suite = new TestSuite.Wrong(dataStore, cfg, systemOutLogger);
+ suite = new WrongCurvesSuite(dataStore, cfg);
break;
- case "nonprime":
- suite = new TestSuite.NonPrime(dataStore, cfg, systemOutLogger);
+ case "composite":
+ suite = new CompositeCurvesSuite(dataStore, cfg);
break;
case "invalid":
- suite = new TestSuite.Invalid(dataStore, cfg, systemOutLogger);
+ suite = new InvalidCurvesSuite(dataStore, cfg);
break;
default:
System.err.println("Unknown test suite.");
@@ -434,7 +467,10 @@ public class ECTester {
}
break;
}
- suite.run(cardManager);
+
+ TestRunner runner = new TestRunner(suite, testWriter);
+ suite.setup(cardManager);
+ runner.run();
}
/**
@@ -452,7 +488,9 @@ public class ECTester {
if (curve != null)
prepare.add(curve.send());
- systemOutLogger.println(Response.toString(prepare));
+ for (Response r : prepare) {
+ respWriter.outputResponse(r);
+ }
byte pubkey = (cfg.anyPublicKey || cfg.anyKey) ? ECTesterApplet.KEYPAIR_REMOTE : ECTesterApplet.KEYPAIR_LOCAL;
byte privkey = (cfg.anyPrivateKey || cfg.anyKey) ? ECTesterApplet.KEYPAIR_REMOTE : ECTesterApplet.KEYPAIR_LOCAL;
@@ -476,7 +514,9 @@ public class ECTester {
Response.ECDH perform = new Command.ECDH(cardManager, pubkey, privkey, ECTesterApplet.EXPORT_TRUE, EC_Consts.CORRUPTION_NONE, cfg.ECDHKA).send();
ecdh.add(perform);
- systemOutLogger.println(Response.toString(ecdh));
+ for (Response r : ecdh) {
+ respWriter.outputResponse(r);
+ }
if (!perform.successful() || !perform.hasSecret()) {
if (retry < 10) {
@@ -495,7 +535,7 @@ public class ECTester {
++done;
}
Response cleanup = new Command.Cleanup(cardManager).send();
- systemOutLogger.println(cleanup.toString());
+ respWriter.outputResponse(cleanup);
if (out != null)
out.close();
@@ -533,7 +573,9 @@ public class ECTester {
if (curve != null)
prepare.add(curve.send());
- systemOutLogger.println(Response.toString(prepare));
+ for (Response r : prepare) {
+ respWriter.outputResponse(r);
+ }
FileWriter out = null;
if (cfg.output != null) {
@@ -549,7 +591,9 @@ public class ECTester {
Response.ECDSA perform = new Command.ECDSA(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_TRUE, data).send();
ecdsa.add(perform);
- systemOutLogger.println(Response.toString(ecdsa));
+ for (Response r : ecdsa) {
+ respWriter.outputResponse(r);
+ }
if (!perform.successful() || !perform.hasSignature()) {
if (retry < 10) {
@@ -568,7 +612,7 @@ public class ECTester {
++done;
}
Response cleanup = new Command.Cleanup(cardManager).send();
- systemOutLogger.println(cleanup.toString());
+ respWriter.outputResponse(cleanup);
if (out != null)
out.close();
@@ -614,8 +658,9 @@ public class ECTester {
public boolean fresh = false;
public boolean simulate = false;
public boolean yes = false;
+ public String format;
- //Action-related ions
+ //Action-related options
public String listNamed;
public String testSuite;
public int generateAmount;
@@ -669,6 +714,13 @@ public class ECTester {
return true;
}
+ format = cli.getOptionValue("format", "text");
+ String formats[] = new String[]{"text", "xml", "yaml", "yml"};
+ if (!Arrays.asList(formats).contains(format)) {
+ System.err.println("Wrong output format " + format + ". Should be one of " + Arrays.toString(formats));
+ return false;
+ }
+
if ((key != null || namedKey != null) && (anyPublicKey || anyPrivateKey)) {
System.err.print("Can only specify the whole key with --key/--named-key or pubkey and privkey with --public/--named-public and --private/--named-private.");
return false;
@@ -739,10 +791,9 @@ public class ECTester {
}
testSuite = cli.getOptionValue("test", "default").toLowerCase();
- String[] tests = new String[]{"default", "nonprime", "invalid", "test-vectors", "wrong"};
- List<String> testsList = Arrays.asList(tests);
- if (!testsList.contains(testSuite)) {
- System.err.println("Unknown test case. Should be one of: " + Arrays.toString(tests));
+ String[] tests = new String[]{"default", "composite", "invalid", "test-vectors", "wrong"};
+ if (!Arrays.asList(tests).contains(testSuite)) {
+ System.err.println("Unknown test suite " + testSuite + ". Should be one of: " + Arrays.toString(tests));
return false;
}
diff --git a/src/cz/crcs/ectester/reader/Test.java b/src/cz/crcs/ectester/reader/Test.java
deleted file mode 100644
index 157e360..0000000
--- a/src/cz/crcs/ectester/reader/Test.java
+++ /dev/null
@@ -1,82 +0,0 @@
-package cz.crcs.ectester.reader;
-
-import javax.smartcardio.CardException;
-import java.util.function.BiFunction;
-
-/**
- * @author Jan Jancar johny@neuromancer.sk
- */
-public class Test {
- private boolean hasRun = false;
- private BiFunction<Command, Response, Result> callback;
- private Result result;
- private Result expected;
- private Command command;
- private Response response;
-
- public Test(Command command, Result expected) {
- this.command = command;
- this.expected = expected;
- }
-
- public Test(Command command, Result expected, BiFunction<Command, Response, Result> callback) {
- this(command, expected);
- this.callback = callback;
- }
-
- public Command getCommand() {
- return command;
- }
-
- public Response getResponse() {
- return response;
- }
-
- public Result getResult() {
- if (!hasRun) {
- return null;
- }
- return result;
- }
-
- public Result getExpected() {
- return expected;
- }
-
- public boolean ok() {
- return result == expected || expected == Result.ANY;
- }
-
- public void run() throws CardException {
- response = command.send();
- if (callback != null) {
- result = callback.apply(command, response);
- } else {
- if (response.successful()) {
- result = Result.SUCCESS;
- } else {
- result = Result.FAILURE;
- }
- }
- hasRun = true;
- }
-
- public boolean hasRun() {
- return hasRun;
- }
-
- @Override
- public String toString() {
- if (hasRun) {
- return (ok() ? "OK " : "NOK") + " " + response.toString();
- } else {
- return "";
- }
- }
-
- public enum Result {
- SUCCESS,
- FAILURE,
- ANY
- }
-}
diff --git a/src/cz/crcs/ectester/reader/TestSuite.java b/src/cz/crcs/ectester/reader/TestSuite.java
deleted file mode 100644
index 7118dd8..0000000
--- a/src/cz/crcs/ectester/reader/TestSuite.java
+++ /dev/null
@@ -1,314 +0,0 @@
-package cz.crcs.ectester.reader;
-
-import cz.crcs.ectester.applet.ECTesterApplet;
-import cz.crcs.ectester.applet.EC_Consts;
-import cz.crcs.ectester.data.EC_Store;
-import cz.crcs.ectester.reader.ec.*;
-import javacard.security.KeyPair;
-
-import javax.smartcardio.CardException;
-import java.io.IOException;
-import java.util.*;
-
-/**
- * @author Jan Jancar johny@neuromancer.sk
- */
-public abstract class TestSuite {
-
- EC_Store dataStore;
- ECTester.Config cfg;
- DirtyLogger systemOut;
- String name;
- List<Test> tests = new LinkedList<>();
-
- TestSuite(EC_Store dataStore, ECTester.Config cfg, DirtyLogger systemOut, String name) {
- this.dataStore = dataStore;
- this.cfg = cfg;
- this.systemOut = systemOut;
- this.name = name;
- }
-
- public List<Test> run(CardMngr cardManager) throws CardException, IOException {
- for (Test t : tests) {
- if (!t.hasRun()) {
- t.run();
- systemOut.println(t.toString());
- }
- }
- return tests;
- }
-
- public List<Test> getTests() {
- return Collections.unmodifiableList(tests);
- }
-
- public String getName() {
- return name;
- }
-
- /**
- * @param cardManager cardManager to send APDU through
- * @param generateExpected expected result of the Generate command
- * @param ecdhExpected expected result of the ordinary ECDH command
- * @param ecdsaExpected expected result of the ordinary ECDSA command
- * @return tests to run
- */
- List<Test> testCurve(CardMngr cardManager, Test.Result generateExpected, Test.Result ecdhExpected, Test.Result ecdsaExpected) {
- List<Test> tests = new LinkedList<>();
-
- tests.add(new Test(new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_BOTH), generateExpected));
- tests.add(new Test(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_NONE, EC_Consts.KA_ECDH), ecdhExpected));
- tests.add(new Test(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_COMPRESS, EC_Consts.KA_ECDH), ecdhExpected));
- tests.add(new Test(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_ONE, EC_Consts.KA_ECDH), Test.Result.FAILURE));
- tests.add(new Test(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_ZERO, EC_Consts.KA_ECDH), Test.Result.FAILURE));
- tests.add(new Test(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_MAX, EC_Consts.KA_ECDH), Test.Result.FAILURE));
- tests.add(new Test(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_FULLRANDOM, EC_Consts.KA_ECDH), Test.Result.FAILURE));
- tests.add(new Test(new Command.ECDSA(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, null), ecdsaExpected));
-
- return tests;
- }
-
- /**
- * @param cardManager cardManager to send APDU through
- * @param category category to test
- * @param field field to test (KeyPair.ALG_EC_FP || KeyPair.ALG_EC_F2M)
- * @param setExpected expected result of the Set (curve) command
- * @param generateExpected expected result of the Generate command
- * @param ecdhExpected expected result of the ordinary ECDH command
- * @param ecdsaExpected expected result of the ordinary ECDSA command
- * @return tests to run
- */
- List<Test> testCategory(CardMngr cardManager, String category, byte field, Test.Result setExpected, Test.Result generateExpected, Test.Result ecdhExpected, Test.Result ecdsaExpected) {
- List<Test> tests = new LinkedList<>();
- Map<String, EC_Curve> curves = dataStore.getObjects(EC_Curve.class, category);
- if (curves == null)
- return tests;
- for (Map.Entry<String, EC_Curve> entry : curves.entrySet()) {
- EC_Curve curve = entry.getValue();
- if (curve.getField() == field && (curve.getBits() == cfg.bits || cfg.all)) {
- tests.add(new Test(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), field), Test.Result.SUCCESS));
- tests.add(new Test(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), setExpected));
- tests.addAll(testCurve(cardManager, generateExpected, ecdhExpected, ecdsaExpected));
- tests.add(new Test(new Command.Cleanup(cardManager), Test.Result.ANY));
- }
- }
-
- return tests;
- }
-
- public static class Default extends TestSuite {
-
- public Default(EC_Store dataStore, ECTester.Config cfg, DirtyLogger systemOut) {
- super(dataStore, cfg, systemOut, "default");
- }
-
- @Override
- public List<Test> run(CardMngr cardManager) throws IOException, CardException {
- tests.add(new Test(new Command.Support(cardManager), Test.Result.ANY));
- if (cfg.namedCurve != null) {
- if (cfg.primeField) {
- tests.addAll(testCategory(cardManager, cfg.namedCurve, KeyPair.ALG_EC_FP, Test.Result.SUCCESS, Test.Result.SUCCESS, Test.Result.SUCCESS, Test.Result.SUCCESS));
- }
- if (cfg.binaryField) {
- tests.addAll(testCategory(cardManager, cfg.namedCurve, KeyPair.ALG_EC_F2M, Test.Result.SUCCESS, Test.Result.SUCCESS, Test.Result.SUCCESS, Test.Result.SUCCESS));
- }
- } else {
- if (cfg.all) {
- if (cfg.primeField) {
- //iterate over prime curve sizes used: EC_Consts.FP_SIZES
- for (short keyLength : EC_Consts.FP_SIZES) {
- defaultTests(cardManager, keyLength, KeyPair.ALG_EC_FP);
- }
- }
- if (cfg.binaryField) {
- //iterate over binary curve sizes used: EC_Consts.F2M_SIZES
- for (short keyLength : EC_Consts.F2M_SIZES) {
- defaultTests(cardManager, keyLength, KeyPair.ALG_EC_F2M);
- }
- }
- } else {
- if (cfg.primeField) {
- defaultTests(cardManager, (short) cfg.bits, KeyPair.ALG_EC_FP);
- }
-
- if (cfg.binaryField) {
- defaultTests(cardManager, (short) cfg.bits, KeyPair.ALG_EC_F2M);
- }
- }
- }
- return super.run(cardManager);
- }
-
- private void defaultTests(CardMngr cardManager, short keyLength, byte keyType) throws IOException {
- tests.add(new Test(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_BOTH, keyLength, keyType), Test.Result.SUCCESS));
- Command curve = Command.prepareCurve(cardManager, dataStore, cfg, ECTesterApplet.KEYPAIR_BOTH, keyLength, keyType);
- if (curve != null)
- tests.add(new Test(curve, Test.Result.SUCCESS));
- tests.addAll(testCurve(cardManager, Test.Result.SUCCESS, Test.Result.SUCCESS, Test.Result.SUCCESS));
- tests.add(new Test(new Command.Cleanup(cardManager), Test.Result.ANY));
- }
- }
-
- public static class TestVectors extends TestSuite {
-
- public TestVectors(EC_Store dataStore, ECTester.Config cfg, DirtyLogger systemOut) {
- super(dataStore, cfg, systemOut, "test");
- }
-
- @Override
- public List<Test> run(CardMngr cardManager) throws IOException, CardException {
- /* Set original curves (secg/nist/brainpool). Set keypairs from test vectors.
- * Do ECDH both ways, export and verify that the result is correct.
- */
- Map<String, EC_KAResult> results = dataStore.getObjects(EC_KAResult.class, "test");
- for (EC_KAResult result : results.values()) {
- EC_Curve curve = dataStore.getObject(EC_Curve.class, result.getCurve());
- if (cfg.namedCurve != null && !(result.getCurve().startsWith(cfg.namedCurve) || result.getCurve().equals(cfg.namedCurve))) {
- continue;
- }
- if (curve.getBits() != cfg.bits && !cfg.all) {
- continue;
- }
- if (curve.getField() == KeyPair.ALG_EC_FP && !cfg.primeField || curve.getField() == KeyPair.ALG_EC_F2M && !cfg.binaryField) {
- continue;
- }
- EC_Params onekey = dataStore.getObject(EC_Keypair.class, result.getOneKey());
- if (onekey == null) {
- onekey = dataStore.getObject(EC_Key.Private.class, result.getOneKey());
- }
- EC_Params otherkey = dataStore.getObject(EC_Keypair.class, result.getOtherKey());
- if (otherkey == null) {
- otherkey = dataStore.getObject(EC_Key.Public.class, result.getOtherKey());
- }
- if (onekey == null || otherkey == null) {
- throw new IOException("Test vector keys couldn't be located.");
- }
-
- tests.add(new Test(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), Test.Result.SUCCESS));
- tests.add(new Test(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), Test.Result.SUCCESS));
- //tests.add(new Test(new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_BOTH), Test.Result.SUCCESS));
- tests.add(new Test(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.CURVE_external, EC_Consts.PARAMETER_S, onekey.flatten(EC_Consts.PARAMETER_S)), Test.Result.SUCCESS));
- tests.add(new Test(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, EC_Consts.PARAMETER_W, otherkey.flatten(EC_Consts.PARAMETER_W)), Test.Result.SUCCESS));
- tests.add(new Test(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_TRUE, EC_Consts.CORRUPTION_NONE, result.getKA()), Test.Result.SUCCESS, (command, response) -> {
- Response.ECDH dh = (Response.ECDH) response;
- if (!dh.successful() || !dh.hasSecret())
- return Test.Result.FAILURE;
- if (!Util.compareBytes(dh.getSecret(), 0, result.getParam(0), 0, dh.secretLength())) {
- return Test.Result.FAILURE;
- }
- return Test.Result.SUCCESS;
- }));
- tests.add(new Test(new Command.Cleanup(cardManager), Test.Result.ANY));
-
- }
- return super.run(cardManager);
- }
- }
-
- public static class NonPrime extends TestSuite {
-
- public NonPrime(EC_Store dataStore, ECTester.Config cfg, DirtyLogger systemOut) {
- super(dataStore, cfg, systemOut, "nonprime");
- }
-
- @Override
- public List<Test> run(CardMngr cardManager) throws IOException, CardException {
- /* Do the default tests with the public keys set to provided smallorder keys
- * over non-prime order curves. Essentially small subgroup attacks.
- * These should fail, the curves aren't safe so that if the computation with
- * a small order public key succeeds the private key modulo the public key order
- * is revealed.
- */
- Map<String, EC_Key> keys = dataStore.getObjects(EC_Key.class, "nonprime");
- for (EC_Key key : keys.values()) {
- EC_Curve curve = dataStore.getObject(EC_Curve.class, key.getCurve());
- if (cfg.namedCurve != null && !(key.getCurve().startsWith(cfg.namedCurve) || key.getCurve().equals(cfg.namedCurve))) {
- continue;
- }
- if (curve.getField() == KeyPair.ALG_EC_FP && !cfg.primeField || curve.getField() == KeyPair.ALG_EC_F2M && !cfg.binaryField) {
- continue;
- }
- if ((curve.getBits() == cfg.bits || cfg.all)) {
- tests.add(new Test(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), Test.Result.SUCCESS));
- tests.add(new Test(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), Test.Result.ANY));
- tests.add(new Test(new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_LOCAL), Test.Result.ANY));
-
- //tests.add(new Test(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, key.getParams(), key.flatten()), Test.Result.ANY));
- //tests.add(new Test(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_NONE, EC_Consts.KA_ECDH), Test.Result.FAILURE));
- tests.add(new Test(new Command.ECDH_direct(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_NONE, EC_Consts.KA_ECDH, key.flatten()), Test.Result.FAILURE));
-
- tests.add(new Test(new Command.Cleanup(cardManager), Test.Result.ANY));
- }
- }
- return super.run(cardManager);
- }
- }
-
- public static class Invalid extends TestSuite {
-
- public Invalid(EC_Store dataStore, ECTester.Config cfg, DirtyLogger systemOut) {
- super(dataStore, cfg, systemOut, "invalid");
- }
-
- @Override
- public List<Test> run(CardMngr cardManager) throws CardException, IOException {
- /* Set original curves (secg/nist/brainpool). Generate local.
- * Try ECDH with invalid public keys of increasing (or decreasing) order.
- */
- Map<String, EC_Key.Public> pubkeys = dataStore.getObjects(EC_Key.Public.class, "invalid");
- Map<EC_Curve, List<EC_Key.Public>> curves = new HashMap<>();
- for (EC_Key.Public key : pubkeys.values()) {
- EC_Curve curve = dataStore.getObject(EC_Curve.class, key.getCurve());
- if (cfg.namedCurve != null && !(key.getCurve().startsWith(cfg.namedCurve) || key.getCurve().equals(cfg.namedCurve))) {
- continue;
- }
- if (curve.getBits() != cfg.bits && !cfg.all) {
- continue;
- }
- if (curve.getField() == KeyPair.ALG_EC_FP && !cfg.primeField || curve.getField() == KeyPair.ALG_EC_F2M && !cfg.binaryField) {
- continue;
- }
- List<EC_Key.Public> keys = curves.getOrDefault(curve, new LinkedList<>());
- keys.add(key);
- curves.putIfAbsent(curve, keys);
- }
- for (Map.Entry<EC_Curve, List<EC_Key.Public>> e : curves.entrySet()) {
- EC_Curve curve = e.getKey();
- List<EC_Key.Public> keys = e.getValue();
-
- tests.add(new Test(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), Test.Result.SUCCESS));
- tests.add(new Test(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), Test.Result.SUCCESS));
- tests.add(new Test(new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_LOCAL), Test.Result.SUCCESS));
- for (EC_Key.Public pub : keys) {
- // tests.add(new Test(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, pub.getParams(), pub.flatten()), Test.Result.ANY));
- // tests.add(new Test(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_NONE, EC_Consts.KA_ANY), Test.Result.FAILURE));
- tests.add(new Test(new Command.ECDH_direct(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_NONE, EC_Consts.KA_ANY, pub.flatten()), Test.Result.FAILURE));
- }
- tests.add(new Test(new Command.Cleanup(cardManager), Test.Result.ANY));
- }
-
- return super.run(cardManager);
- }
- }
-
- public static class Wrong extends TestSuite {
-
- public Wrong(EC_Store dataStore, ECTester.Config cfg, DirtyLogger systemOut) {
- super(dataStore, cfg, systemOut, "wrong");
- }
-
- @Override
- public List<Test> run(CardMngr cardManager) throws CardException, IOException {
- /* Just do the default tests on the wrong curves.
- * These should generally fail, the curves aren't curves.
- */
- if (cfg.primeField) {
- tests.addAll(testCategory(cardManager, cfg.testSuite, KeyPair.ALG_EC_FP, Test.Result.FAILURE, Test.Result.FAILURE, Test.Result.FAILURE, Test.Result.FAILURE));
- }
- if (cfg.binaryField) {
- tests.addAll(testCategory(cardManager, cfg.testSuite, KeyPair.ALG_EC_F2M, Test.Result.FAILURE, Test.Result.FAILURE, Test.Result.FAILURE, Test.Result.FAILURE));
- }
- return super.run(cardManager);
- }
- }
-}
diff --git a/src/cz/crcs/ectester/reader/Util.java b/src/cz/crcs/ectester/reader/Util.java
index 754cda3..4e1154b 100644
--- a/src/cz/crcs/ectester/reader/Util.java
+++ b/src/cz/crcs/ectester/reader/Util.java
@@ -28,15 +28,19 @@ public class Util {
array[offset] = (byte) ((value >> 8) & 0xFF);
}
- public static boolean compareBytes(byte[] one, int oneOffset, byte[] other, int otherOffset, int length) {
+ 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 false;
+ return i;
}
}
- return true;
+ return length;
+ }
+
+ public static boolean compareBytes(byte[] one, int oneOffset, byte[] other, int otherOffset, int length) {
+ return diffBytes(one, oneOffset, other, otherOffset, length) == length;
}
public static boolean allValue(byte[] array, byte value) {
@@ -283,10 +287,10 @@ public class Util {
public static String getSWString(short sw) {
if (sw == ISO7816.SW_NO_ERROR) {
- return "OK\t(0x9000)";
+ return "OK (0x9000)";
} else {
String str = getSW(sw);
- return String.format("fail\t(%s, 0x%04x)", str, sw);
+ return String.format("fail (%s, 0x%04x)", str, sw);
}
}
diff --git a/src/cz/crcs/ectester/reader/Command.java b/src/cz/crcs/ectester/reader/command/Command.java
index 8cb3a30..3c11456 100644
--- a/src/cz/crcs/ectester/reader/Command.java
+++ b/src/cz/crcs/ectester/reader/command/Command.java
@@ -1,8 +1,12 @@
-package cz.crcs.ectester.reader;
+package cz.crcs.ectester.reader.command;
import cz.crcs.ectester.applet.ECTesterApplet;
import cz.crcs.ectester.applet.EC_Consts;
import cz.crcs.ectester.data.EC_Store;
+import cz.crcs.ectester.reader.CardMngr;
+import cz.crcs.ectester.reader.ECTester;
+import cz.crcs.ectester.reader.response.Response;
+import cz.crcs.ectester.reader.Util;
import cz.crcs.ectester.reader.ec.EC_Curve;
import cz.crcs.ectester.reader.ec.EC_Key;
import cz.crcs.ectester.reader.ec.EC_Keypair;
@@ -160,7 +164,7 @@ public abstract class Command {
priv.readCSV(in);
in.close();
} else {
- priv = dataStore.getObject(EC_Key.Public.class, cfg.namedPrivateKey);
+ priv = dataStore.getObject(EC_Key.Private.class, cfg.namedPrivateKey);
if (priv == null) {
priv = dataStore.getObject(EC_Keypair.class, cfg.namedPrivateKey);
}
@@ -192,7 +196,7 @@ public abstract class Command {
* @param keyLength key length to set
* @param keyClass key class to allocate
*/
- protected Allocate(CardMngr cardManager, byte keyPair, short keyLength, byte keyClass) {
+ public Allocate(CardMngr cardManager, byte keyPair, short keyLength, byte keyClass) {
super(cardManager);
this.keyPair = keyPair;
this.keyLength = keyLength;
@@ -223,7 +227,7 @@ public abstract class Command {
* @param cardManager cardManager to send APDU through
* @param kaType which type of KeyAgreement to use
*/
- protected AllocateKeyAgreement(CardMngr cardManager, byte kaType) {
+ public AllocateKeyAgreement(CardMngr cardManager, byte kaType) {
super(cardManager);
this.kaType = kaType;
byte[] data = new byte[]{kaType};
@@ -249,7 +253,7 @@ public abstract class Command {
* @param cardManager cardManager to send APDU through
* @param keyPair which keyPair clear, local/remote (KEYPAIR_* || ...)
*/
- protected Clear(CardMngr cardManager, byte keyPair) {
+ public Clear(CardMngr cardManager, byte keyPair) {
super(cardManager);
this.keyPair = keyPair;
@@ -283,7 +287,7 @@ public abstract class Command {
* @param params parameters to set (EC_Consts.PARAMETER_* | ...)
* @param external external curve data, can be null
*/
- protected Set(CardMngr cardManager, byte keyPair, byte curve, short params, byte[] external) {
+ public Set(CardMngr cardManager, byte keyPair, byte curve, short params, byte[] external) {
super(cardManager);
this.keyPair = keyPair;
this.curve = curve;
@@ -325,7 +329,7 @@ public abstract class Command {
* @param params parameters to corrupt (EC_Consts.PARAMETER_* | ...)
* @param corruption corruption type (EC_Consts.CORRUPTION_*)
*/
- protected Corrupt(CardMngr cardManager, byte keyPair, byte key, short params, byte corruption) {
+ public Corrupt(CardMngr cardManager, byte keyPair, byte key, short params, byte corruption) {
super(cardManager);
this.keyPair = keyPair;
this.key = key;
@@ -360,7 +364,7 @@ public abstract class Command {
* @param cardManager cardManager to send APDU through
* @param keyPair which keyPair to generate, local/remote (KEYPAIR_* || ...)
*/
- protected Generate(CardMngr cardManager, byte keyPair) {
+ public Generate(CardMngr cardManager, byte keyPair) {
super(cardManager);
this.keyPair = keyPair;
@@ -392,7 +396,7 @@ public abstract class Command {
* @param key key to export from (EC_Consts.KEY_* | ...)
* @param params params to export (EC_Consts.PARAMETER_* | ...)
*/
- protected Export(CardMngr cardManager, byte keyPair, byte key, short params) {
+ public Export(CardMngr cardManager, byte keyPair, byte key, short params) {
super(cardManager);
this.keyPair = keyPair;
this.key = key;
@@ -433,7 +437,7 @@ public abstract class Command {
* @param corruption whether to invalidate the pubkey before ECDH (EC_Consts.CORRUPTION_* | ...)
* @param type ECDH algorithm type (EC_Consts.KA_* | ...)
*/
- protected ECDH(CardMngr cardManager, byte pubkey, byte privkey, byte export, short corruption, byte type) {
+ public ECDH(CardMngr cardManager, byte pubkey, byte privkey, byte export, short corruption, byte type) {
super(cardManager);
this.pubkey = pubkey;
this.privkey = privkey;
@@ -466,7 +470,17 @@ public abstract class Command {
private byte type;
private byte[] pubkey;
- protected ECDH_direct(CardMngr cardManager, byte privkey, byte export, short corruption, byte type, byte[] pubkey) {
+ /**
+ * Creates the INS_ECDH_DIRECT instruction.
+ *
+ * @param cardManager cardManager to send APDU through
+ * @param privkey keyPair to use for private key, (KEYPAIR_LOCAL || KEYPAIR_REMOTE)
+ * @param export whether to export ECDH secret
+ * @param corruption whether to invalidate the pubkey before ECDH (EC_Consts.CORRUPTION_* | ...)
+ * @param type ECDH algorithm type (EC_Consts.KA_* | ...)
+ * @param pubkey pubkey data to do ECDH with.
+ */
+ public ECDH_direct(CardMngr cardManager, byte privkey, byte export, short corruption, byte type, byte[] pubkey) {
super(cardManager);
this.privkey = privkey;
this.export = export;
@@ -504,7 +518,7 @@ public abstract class Command {
* @param export whether to export ECDSA signature
* @param raw data to sign, can be null, in which case random data is signed.
*/
- protected ECDSA(CardMngr cardManager, byte keyPair, byte export, byte[] raw) {
+ public ECDSA(CardMngr cardManager, byte keyPair, byte export, byte[] raw) {
super(cardManager);
this.keyPair = keyPair;
this.export = export;
@@ -537,7 +551,7 @@ public abstract class Command {
/**
* @param cardManager cardManager to send APDU through
*/
- protected Cleanup(CardMngr cardManager) {
+ public Cleanup(CardMngr cardManager) {
super(cardManager);
this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_CLEANUP, 0, 0);
@@ -560,7 +574,7 @@ public abstract class Command {
/**
* @param cardManager cardManager to send APDU through
*/
- protected Support(CardMngr cardManager) {
+ public Support(CardMngr cardManager) {
super(cardManager);
this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_SUPPORT, 0, 0);
diff --git a/src/cz/crcs/ectester/reader/ec/EC_Category.java b/src/cz/crcs/ectester/reader/ec/EC_Category.java
index 97dd1b4..41cbad8 100644
--- a/src/cz/crcs/ectester/reader/ec/EC_Category.java
+++ b/src/cz/crcs/ectester/reader/ec/EC_Category.java
@@ -6,6 +6,8 @@ import java.util.Objects;
import java.util.TreeMap;
/**
+ * A category of EC_Data objects, has a name, description and represents a directory in
+ * the cz.crcs.ectester.data package.
* @author Jan Jancar johny@neuromancer.sk
*/
public class EC_Category {
diff --git a/src/cz/crcs/ectester/reader/ec/EC_Curve.java b/src/cz/crcs/ectester/reader/ec/EC_Curve.java
index 45080fb..cb4a2df 100644
--- a/src/cz/crcs/ectester/reader/ec/EC_Curve.java
+++ b/src/cz/crcs/ectester/reader/ec/EC_Curve.java
@@ -4,6 +4,8 @@ import cz.crcs.ectester.applet.EC_Consts;
import javacard.security.KeyPair;
/**
+ * 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 {
diff --git a/src/cz/crcs/ectester/reader/ec/EC_Data.java b/src/cz/crcs/ectester/reader/ec/EC_Data.java
index 9dcbbe0..0ceddef 100644
--- a/src/cz/crcs/ectester/reader/ec/EC_Data.java
+++ b/src/cz/crcs/ectester/reader/ec/EC_Data.java
@@ -7,6 +7,10 @@ import java.util.*;
import java.util.regex.Pattern;
/**
+ * A list of byte arrays for holding EC data.
+ *
+ * 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 {
diff --git a/src/cz/crcs/ectester/reader/ec/EC_KAResult.java b/src/cz/crcs/ectester/reader/ec/EC_KAResult.java
index 28115f7..4a67bbe 100644
--- a/src/cz/crcs/ectester/reader/ec/EC_KAResult.java
+++ b/src/cz/crcs/ectester/reader/ec/EC_KAResult.java
@@ -3,6 +3,8 @@ package cz.crcs.ectester.reader.ec;
import cz.crcs.ectester.reader.Util;
/**
+ * A result of EC based Key agreement operation.
+ *
* @author Jan Jancar johny@neuromancer.sk
*/
public class EC_KAResult extends EC_Data {
diff --git a/src/cz/crcs/ectester/reader/ec/EC_Key.java b/src/cz/crcs/ectester/reader/ec/EC_Key.java
index cecd228..5077d5b 100644
--- a/src/cz/crcs/ectester/reader/ec/EC_Key.java
+++ b/src/cz/crcs/ectester/reader/ec/EC_Key.java
@@ -3,6 +3,8 @@ package cz.crcs.ectester.reader.ec;
import cz.crcs.ectester.applet.EC_Consts;
/**
+ * An abstract-like EC key. Concrete implementations create a public and private keys.
+ *
* @author Jan Jancar johny@neuromancer.sk
*/
public class EC_Key extends EC_Params {
@@ -20,11 +22,6 @@ public class EC_Key extends EC_Params {
this.desc = desc;
}
- private EC_Key(String id, short mask, String curve) {
- this(mask, curve);
- this.id = id;
- }
-
private EC_Key(String id, short mask, String curve, String desc) {
this(mask, curve, desc);
this.id = id;
@@ -38,6 +35,9 @@ public class EC_Key extends EC_Params {
return desc;
}
+ /**
+ * An EC public key, contains the W parameter.
+ */
public static class Public extends EC_Key {
public Public(String curve) {
@@ -48,12 +48,19 @@ public class EC_Key extends EC_Params {
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 "EC Public key, over " + getCurve() + (getDesc() == null ? "" : ": " + getDesc());
+ return "<" + getId() + "> EC Public key, over " + getCurve() + (getDesc() == null ? "" : ": " + getDesc());
}
}
+ /**
+ * An EC private key, contains the S parameter.
+ */
public static class Private extends EC_Key {
public Private(String curve) {
@@ -64,6 +71,10 @@ public class EC_Key extends EC_Params {
super(EC_Consts.PARAMETER_S, curve, desc);
}
+ public Private(String id, String curve, String desc) {
+ super(id, EC_Consts.PARAMETER_S, curve, desc);
+ }
+
@Override
public String toString() {
return "<" + getId() + "> EC Private key, over " + getCurve() + (getDesc() == null ? "" : ": " + getDesc());
diff --git a/src/cz/crcs/ectester/reader/ec/EC_Keypair.java b/src/cz/crcs/ectester/reader/ec/EC_Keypair.java
index 924906e..2643346 100644
--- a/src/cz/crcs/ectester/reader/ec/EC_Keypair.java
+++ b/src/cz/crcs/ectester/reader/ec/EC_Keypair.java
@@ -3,6 +3,8 @@ package cz.crcs.ectester.reader.ec;
import cz.crcs.ectester.applet.EC_Consts;
/**
+ * An EC keypair, contains both the W and S parameters.
+ *
* @author Jan Jancar johny@neuromancer.sk
*/
public class EC_Keypair extends EC_Params {
@@ -19,6 +21,11 @@ public class EC_Keypair extends EC_Params {
this.desc = desc;
}
+ public EC_Keypair(String id, String curve, String desc) {
+ this(curve, desc);
+ this.id = id;
+ }
+
public String getCurve() {
return curve;
}
diff --git a/src/cz/crcs/ectester/reader/ec/EC_Params.java b/src/cz/crcs/ectester/reader/ec/EC_Params.java
index ea2e633..6fb164b 100644
--- a/src/cz/crcs/ectester/reader/ec/EC_Params.java
+++ b/src/cz/crcs/ectester/reader/ec/EC_Params.java
@@ -8,6 +8,11 @@ import java.util.ArrayList;
import java.util.List;
/**
+ * A list of EC parameters, can contain a subset of the Fp/F2M, A, B, G, R, K, W, S parameters.
+ *
+ * The set of parameters is uniquely identified by a short bit string.
+ * The parameters can be exported to a byte array via <code>flatten()</code> or to a comma delimited
+ * string via <code>expand()</code>.
* @author Jan Jancar johny@neuromancer.sk
*/
public class EC_Params extends EC_Data {
diff --git a/src/cz/crcs/ectester/reader/output/OutputLogger.java b/src/cz/crcs/ectester/reader/output/OutputLogger.java
new file mode 100644
index 0000000..bf47a1f
--- /dev/null
+++ b/src/cz/crcs/ectester/reader/output/OutputLogger.java
@@ -0,0 +1,60 @@
+package cz.crcs.ectester.reader.output;
+
+import java.io.*;
+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/src/cz/crcs/ectester/reader/output/ResponseWriter.java b/src/cz/crcs/ectester/reader/output/ResponseWriter.java
new file mode 100644
index 0000000..c357233
--- /dev/null
+++ b/src/cz/crcs/ectester/reader/output/ResponseWriter.java
@@ -0,0 +1,39 @@
+package cz.crcs.ectester.reader.output;
+
+import cz.crcs.ectester.reader.Util;
+import cz.crcs.ectester.reader.response.Response;
+
+import java.io.PrintStream;
+
+/**
+ * @author Jan Jancar johny@neuromancer.sk
+ */
+public class ResponseWriter {
+ private PrintStream output;
+
+ public ResponseWriter(PrintStream output) {
+ this.output = output;
+ }
+
+ public String responseSuffix(Response r) {
+ StringBuilder suffix = new StringBuilder();
+ for (int j = 0; j < r.getNumSW(); ++j) {
+ short sw = r.getSW(j);
+ if (sw != 0) {
+ suffix.append(" ").append(Util.getSWString(sw));
+ }
+ }
+ if (suffix.length() == 0) {
+ suffix.append(" [").append(Util.getSW(r.getNaturalSW())).append("]");
+ }
+ return String.format("%4d ms ┃ %s", r.getDuration() / 1000000, suffix);
+ }
+
+ public void outputResponse(Response r) {
+ String out = "";
+ out += String.format("%-70s", r.getDescription()) + " ┃ ";
+ out += responseSuffix(r);
+ output.println(out);
+ output.flush();
+ }
+}
diff --git a/src/cz/crcs/ectester/reader/output/TeeOutputStream.java b/src/cz/crcs/ectester/reader/output/TeeOutputStream.java
new file mode 100644
index 0000000..2a1af99
--- /dev/null
+++ b/src/cz/crcs/ectester/reader/output/TeeOutputStream.java
@@ -0,0 +1,36 @@
+package cz.crcs.ectester.reader.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/src/cz/crcs/ectester/reader/output/TestWriter.java b/src/cz/crcs/ectester/reader/output/TestWriter.java
new file mode 100644
index 0000000..74c76fb
--- /dev/null
+++ b/src/cz/crcs/ectester/reader/output/TestWriter.java
@@ -0,0 +1,15 @@
+package cz.crcs.ectester.reader.output;
+
+import cz.crcs.ectester.reader.test.Test;
+import cz.crcs.ectester.reader.test.TestSuite;
+
+/**
+ * @author Jan Jancar johny@neuromancer.sk
+ */
+public interface TestWriter {
+ void begin(TestSuite suite);
+
+ void outputTest(Test t);
+
+ void end();
+}
diff --git a/src/cz/crcs/ectester/reader/output/TextTestWriter.java b/src/cz/crcs/ectester/reader/output/TextTestWriter.java
new file mode 100644
index 0000000..bcebcd5
--- /dev/null
+++ b/src/cz/crcs/ectester/reader/output/TextTestWriter.java
@@ -0,0 +1,85 @@
+package cz.crcs.ectester.reader.output;
+
+import cz.crcs.ectester.reader.test.Test;
+import cz.crcs.ectester.reader.test.TestSuite;
+
+import java.io.PrintStream;
+
+/**
+ * @author Jan Jancar johny@neuromancer.sk
+ */
+public class TextTestWriter implements TestWriter {
+ private PrintStream output;
+ private ResponseWriter respWriter;
+
+ public static int BASE_WIDTH = 76;
+
+ public TextTestWriter(PrintStream output) {
+ this.output = output;
+ this.respWriter = new ResponseWriter(output);
+ }
+
+ @Override
+ public void begin(TestSuite suite) {
+ output.println("=== Running test suite: " + suite.getName() + " ===");
+ output.println("=== " + suite.getDescription());
+ }
+
+ private String testString(Test t, int offset) {
+ if (!t.hasRun()) {
+ return null;
+ }
+
+ StringBuilder out = new StringBuilder();
+ if (t instanceof Test.Simple) {
+ Test.Simple test = (Test.Simple) t;
+ out.append(test.ok() ? "OK " : "NOK ");
+ out.append("━ ");
+ int width = BASE_WIDTH - (offset + out.length());
+ String widthSpec = "%-" + String.valueOf(width) + "s";
+ out.append(String.format(widthSpec, t.getDescription()));
+ out.append(" ┃ ");
+ out.append(String.format("%-9s", test.getResultValue().name()));
+ out.append(" ┃ ");
+ out.append(respWriter.responseSuffix(test.getResponse()));
+ } else {
+ Test.Compound test = (Test.Compound) t;
+ out.append(test.ok() ? "OK " : "NOK ");
+ out.append("┳ ");
+ int width = BASE_WIDTH - (offset + out.length());
+ String widthSpec = "%-" + String.valueOf(width) + "s";
+ out.append(String.format(widthSpec, t.getDescription()));
+ out.append(" ┃ ");
+ out.append(String.format("%-9s", test.getResultValue().name()));
+ out.append(" ┃ ");
+ out.append(test.getResultCause());
+ out.append(System.lineSeparator());
+ Test[] tests = test.getTests();
+ for (int i = 0; i < tests.length; ++i) {
+ if (i == tests.length - 1) {
+ out.append(" ┗ ");
+ } else {
+ out.append(" ┣ ");
+ }
+ out.append(testString(tests[i], offset + 6));
+ if (i != tests.length - 1) {
+ out.append(System.lineSeparator());
+ }
+ }
+ }
+
+ return out.toString();
+ }
+
+ @Override
+ public void outputTest(Test t) {
+ if (!t.hasRun())
+ return;
+ output.println(testString(t, 0));
+ output.flush();
+ }
+
+ @Override
+ public void end() {
+ }
+}
diff --git a/src/cz/crcs/ectester/reader/output/XMLTestWriter.java b/src/cz/crcs/ectester/reader/output/XMLTestWriter.java
new file mode 100644
index 0000000..beb758c
--- /dev/null
+++ b/src/cz/crcs/ectester/reader/output/XMLTestWriter.java
@@ -0,0 +1,145 @@
+package cz.crcs.ectester.reader.output;
+
+import cz.crcs.ectester.reader.Util;
+import cz.crcs.ectester.reader.command.Command;
+import cz.crcs.ectester.reader.response.Response;
+import cz.crcs.ectester.reader.test.Test;
+import cz.crcs.ectester.reader.test.TestSuite;
+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;
+
+/**
+ * @author Jan Jancar johny@neuromancer.sk
+ */
+public class XMLTestWriter implements TestWriter {
+ private OutputStream output;
+ private DocumentBuilder db;
+ private Document doc;
+ private Node root;
+
+ public XMLTestWriter(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.getDescription());
+
+ root = rootElem;
+ doc.appendChild(root);
+ }
+
+ private Element commandElement(Command c) {
+ Element commandElem = doc.createElement("command");
+
+ Element apdu = doc.createElement("apdu");
+ apdu.setTextContent(Util.bytesToHex(c.getAPDU().getBytes()));
+ commandElem.appendChild(apdu);
+
+ return commandElem;
+ }
+
+ private Element responseElement(Response r) {
+ Element responseElem = doc.createElement("response");
+ responseElem.setAttribute("successful", r.successful() ? "true" : "false");
+
+ Element apdu = doc.createElement("apdu");
+ apdu.setTextContent(Util.bytesToHex(r.getAPDU().getBytes()));
+ responseElem.appendChild(apdu);
+
+ Element naturalSW = doc.createElement("natural-sw");
+ naturalSW.setTextContent(String.valueOf(Short.toUnsignedInt(r.getNaturalSW())));
+ responseElem.appendChild(naturalSW);
+
+ Element sws = doc.createElement("sws");
+ for (int i = 0; i < r.getNumSW(); ++i) {
+ Element sw = doc.createElement("sw");
+ sw.setTextContent(String.valueOf(Short.toUnsignedInt(r.getSW(i))));
+ sws.appendChild(sw);
+ }
+ responseElem.appendChild(sws);
+
+ Element duration = doc.createElement("duration");
+ duration.setTextContent(String.valueOf(r.getDuration()));
+ responseElem.appendChild(duration);
+
+ Element description = doc.createElement("desc");
+ description.setTextContent(r.getDescription());
+ responseElem.appendChild(description);
+
+ return responseElem;
+ }
+
+ private Element testElement(Test t) {
+ Element testElem = doc.createElement("test");
+
+ if (t instanceof Test.Simple) {
+ Test.Simple test = (Test.Simple) t;
+ testElem.setAttribute("type", "simple");
+ testElem.appendChild(commandElement(test.getCommand()));
+ testElem.appendChild(responseElement(test.getResponse()));
+ } else if (t instanceof Test.Compound) {
+ Test.Compound test = (Test.Compound) t;
+ testElem.setAttribute("type", "compound");
+ for (Test innerTest : test.getTests()) {
+ testElem.appendChild(testElement(innerTest));
+ }
+ }
+
+ Element description = doc.createElement("desc");
+ 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);
+ testElem.appendChild(result);
+
+ return testElem;
+ }
+
+ @Override
+ public void outputTest(Test t) {
+ if (!t.hasRun())
+ return;
+ root.appendChild(testElement(t));
+ }
+
+ @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();
+ }
+ }
+} \ No newline at end of file
diff --git a/src/cz/crcs/ectester/reader/output/YAMLTestWriter.java b/src/cz/crcs/ectester/reader/output/YAMLTestWriter.java
new file mode 100644
index 0000000..3b2b72d
--- /dev/null
+++ b/src/cz/crcs/ectester/reader/output/YAMLTestWriter.java
@@ -0,0 +1,113 @@
+package cz.crcs.ectester.reader.output;
+
+import cz.crcs.ectester.reader.Util;
+import cz.crcs.ectester.reader.command.Command;
+import cz.crcs.ectester.reader.response.Response;
+import cz.crcs.ectester.reader.test.Test;
+import cz.crcs.ectester.reader.test.TestSuite;
+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;
+
+/**
+ * @author Jan Jancar johny@neuromancer.sk
+ */
+public class YAMLTestWriter implements TestWriter {
+ private PrintStream output;
+ private Map<String, Object> testRun;
+ private Map<String, String> testSuite;
+ private List<Object> tests;
+
+ public YAMLTestWriter(PrintStream output) {
+ this.output = output;
+ }
+
+ @Override
+ public void begin(TestSuite suite) {
+ output.println("---");
+ testRun = new HashMap<>();
+ testSuite = new HashMap<>();
+ tests = new LinkedList<>();
+ testSuite.put("name", suite.getName());
+ testSuite.put("desc", suite.getDescription());
+
+ testRun.put("suite", testSuite);
+ testRun.put("tests", tests);
+ }
+
+ private Map<String, Object> commandObject(Command c) {
+ Map<String, Object> commandObj = new HashMap<>();
+ commandObj.put("apdu", Util.bytesToHex(c.getAPDU().getBytes()));
+ return commandObj;
+ }
+
+ private Map<String, Object> responseObject(Response r) {
+ Map<String, Object> responseObj = new HashMap<>();
+ responseObj.put("successful", r.successful());
+ responseObj.put("apdu", Util.bytesToHex(r.getAPDU().getBytes()));
+ responseObj.put("natural_sw", Short.toUnsignedInt(r.getNaturalSW()));
+ List<Integer> sws = new LinkedList<>();
+ for (int i = 0; i < r.getNumSW(); ++i) {
+ sws.add(Short.toUnsignedInt(r.getSW(i)));
+ }
+ responseObj.put("sws", sws);
+ responseObj.put("duration", r.getDuration());
+ responseObj.put("desc", r.getDescription());
+ return responseObj;
+ }
+
+ private Map<String, Object> testObject(Test t) {
+ Map<String, Object> testObj = new HashMap<>();
+
+ if (t instanceof Test.Simple) {
+ Test.Simple test = (Test.Simple) t;
+ testObj.put("type", "simple");
+ testObj.put("command", commandObject(test.getCommand()));
+ testObj.put("response", responseObject(test.getResponse()));
+ } else if (t instanceof Test.Compound) {
+ Test.Compound test = (Test.Compound) t;
+ testObj.put("type", "compound");
+ List<Map<String, Object>> tests = new LinkedList<>();
+ for (Test innerTest : test.getTests()) {
+ tests.add(testObject(innerTest));
+ }
+ testObj.put("tests", tests);
+ }
+
+ 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);
+
+ return testObj;
+ }
+
+ @Override
+ public void outputTest(Test t) {
+ if (!t.hasRun())
+ return;
+ tests.add(testObject(t));
+ }
+
+ @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 HashMap<>();
+ result.put("testRun", testRun);
+ String out = yaml.dump(result);
+
+ output.println(out);
+ output.println("---");
+ }
+}
diff --git a/src/cz/crcs/ectester/reader/Response.java b/src/cz/crcs/ectester/reader/response/Response.java
index 3df956e..4abfd14 100644
--- a/src/cz/crcs/ectester/reader/Response.java
+++ b/src/cz/crcs/ectester/reader/response/Response.java
@@ -1,13 +1,13 @@
-package cz.crcs.ectester.reader;
+package cz.crcs.ectester.reader.response;
import cz.crcs.ectester.applet.ECTesterApplet;
import cz.crcs.ectester.applet.EC_Consts;
-import cz.crcs.ectester.reader.ec.EC_Curve;
+import cz.crcs.ectester.reader.Util;
+import cz.crcs.ectester.reader.ec.EC_Data;
import javacard.framework.ISO7816;
import javacard.security.KeyPair;
import javax.smartcardio.ResponseAPDU;
-import java.util.List;
/**
* @author Jan Jancar johny@neuromancer.sk
@@ -19,15 +19,15 @@ public abstract class Response {
private short[] sws;
private int numSW = 0;
private byte[][] params;
- //TODO replace params with EC_Data?
private boolean success = true;
+ private boolean error = false;
- protected Response(ResponseAPDU response, long time) {
+ public Response(ResponseAPDU response, long time) {
this.resp = response;
this.time = time;
}
- protected void parse(int numSW, int numParams) {
+ void parse(int numSW, int numParams) {
this.numSW = numSW;
this.sws = new short[numSW];
@@ -45,22 +45,28 @@ public abstract class Response {
}
} else {
success = false;
+ error = true;
}
}
- if ((short) resp.getSW() != ISO7816.SW_NO_ERROR)
+ if ((short) resp.getSW() != ISO7816.SW_NO_ERROR) {
success = false;
+ error = true;
+ }
+
//try to parse numParams..
params = new byte[numParams][];
for (int i = 0; i < numParams; i++) {
if (data.length - offset < 2) {
success = false;
+ error = true;
break;
}
short paramLength = Util.getShort(data, offset);
offset += 2;
if (data.length < offset + paramLength) {
+ error = true;
success = false;
break;
}
@@ -114,53 +120,19 @@ public abstract class Response {
return this.success;
}
- @Override
- public abstract String toString();
-
- public String toString(String inner) {
- StringBuilder suffix = new StringBuilder();
- for (int j = 0; j < getNumSW(); ++j) {
- short sw = getSW(j);
- if (sw != 0) {
- suffix.append(" ").append(Util.getSWString(sw));
- }
- }
- if (suffix.length() == 0) {
- suffix.append(" [").append(Util.getSW(getNaturalSW())).append("]");
- }
- return String.format("%-62s:%4d ms : %s", inner, time / 1000000, suffix);
- }
-
- public static String toString(List<Response> responses) {
- return toString(responses, null);
- }
-
- public static String toString(List<Response> responses, String prefix) {
- if (prefix != null)
- prefix += " | ";
- StringBuilder out = new StringBuilder();
- for (int i = 0; i < responses.size(); ++i) {
- Response r = responses.get(i);
-
- if (prefix != null)
- out.append(prefix);
-
- String message = r.toString();
- out.append(message);
- if (i < responses.size() - 1) {
- out.append("\n");
- }
- }
- return out.toString();
+ public boolean error() {
+ return this.error;
}
+ public abstract String getDescription();
/**
*
*/
public static class AllocateKeyAgreement extends Response {
byte kaType;
- protected AllocateKeyAgreement(ResponseAPDU response, long time, byte kaType) {
+
+ public AllocateKeyAgreement(ResponseAPDU response, long time, byte kaType) {
super(response, time);
this.kaType = kaType;
@@ -168,19 +140,19 @@ public abstract class Response {
}
@Override
- public String toString() {
- return super.toString(String.format("Allocate KeyAgreement(%s) object", Util.getKATypeString(this.kaType)));
+ public String getDescription() {
+ return String.format("Allocated KeyAgreement(%s) object", Util.getKATypeString(this.kaType));
}
}
-
+
public static class Allocate extends Response {
private byte keyPair;
private short keyLength;
private byte keyClass;
- protected Allocate(ResponseAPDU response, long time, byte keyPair, short keyLength, byte keyClass) {
+ public Allocate(ResponseAPDU response, long time, byte keyPair, short keyLength, byte keyClass) {
super(response, time);
this.keyPair = keyPair;
this.keyLength = keyLength;
@@ -193,7 +165,7 @@ public abstract class Response {
}
@Override
- public String toString() {
+ public String getDescription() {
String field = keyClass == KeyPair.ALG_EC_FP ? "ALG_EC_FP" : "ALG_EC_F2M";
String key;
if (keyPair == ECTesterApplet.KEYPAIR_BOTH) {
@@ -201,7 +173,7 @@ public abstract class Response {
} else {
key = ((keyPair == ECTesterApplet.KEYPAIR_LOCAL) ? "local" : "remote") + " keypair";
}
- return super.toString(String.format("Allocated %s %db %s", key, keyLength, field));
+ return String.format("Allocated %s %db %s", key, keyLength, field);
}
}
@@ -212,7 +184,7 @@ public abstract class Response {
private byte keyPair;
- protected Clear(ResponseAPDU response, long time, byte keyPair) {
+ public Clear(ResponseAPDU response, long time, byte keyPair) {
super(response, time);
this.keyPair = keyPair;
@@ -223,14 +195,14 @@ public abstract class Response {
}
@Override
- public String toString() {
+ public String getDescription() {
String key;
if (keyPair == ECTesterApplet.KEYPAIR_BOTH) {
key = "both keypairs";
} else {
key = ((keyPair == ECTesterApplet.KEYPAIR_LOCAL) ? "local" : "remote") + " keypair";
}
- return super.toString(String.format("Cleared %s", key));
+ return String.format("Cleared %s", key);
}
}
@@ -243,7 +215,7 @@ public abstract class Response {
private byte curve;
private short parameters;
- protected Set(ResponseAPDU response, long time, byte keyPair, byte curve, short parameters) {
+ public Set(ResponseAPDU response, long time, byte keyPair, byte curve, short parameters) {
super(response, time);
this.keyPair = keyPair;
this.curve = curve;
@@ -257,7 +229,7 @@ public abstract class Response {
}
@Override
- public String toString() {
+ public String getDescription() {
String name;
switch (curve) {
case EC_Consts.CURVE_default:
@@ -287,7 +259,7 @@ public abstract class Response {
} else {
pair = ((keyPair == ECTesterApplet.KEYPAIR_LOCAL) ? "local" : "remote") + " keypair";
}
- return super.toString(String.format("Set %s %s parameters on %s", name, what, pair));
+ return String.format("Set %s %s parameters on %s", name, what, pair);
}
}
@@ -302,7 +274,7 @@ public abstract class Response {
private short params;
private byte corruption;
- protected Corrupt(ResponseAPDU response, long time, byte keyPair, byte key, short params, byte corruption) {
+ public Corrupt(ResponseAPDU response, long time, byte keyPair, byte key, short params, byte corruption) {
super(response, time);
this.keyPair = keyPair;
this.key = key;
@@ -317,7 +289,7 @@ public abstract class Response {
}
@Override
- public String toString() {
+ public String getDescription() {
String corrupt = Util.getCorruption(corruption);
String pair;
@@ -326,7 +298,7 @@ public abstract class Response {
} else {
pair = ((keyPair == ECTesterApplet.KEYPAIR_LOCAL) ? "local" : "remote") + " keypair";
}
- return super.toString(String.format("Corrupted params of %s, %s", pair, corrupt));
+ return String.format("Corrupted params of %s, %s", pair, corrupt);
}
}
@@ -337,7 +309,7 @@ public abstract class Response {
private byte keyPair;
- protected Generate(ResponseAPDU response, long time, byte keyPair) {
+ public Generate(ResponseAPDU response, long time, byte keyPair) {
super(response, time);
this.keyPair = keyPair;
@@ -348,14 +320,14 @@ public abstract class Response {
}
@Override
- public String toString() {
+ public String getDescription() {
String key;
if (keyPair == ECTesterApplet.KEYPAIR_BOTH) {
key = "both keypairs";
} else {
key = ((keyPair == ECTesterApplet.KEYPAIR_LOCAL) ? "local" : "remote") + " keypair";
}
- return super.toString(String.format("Generated %s", key));
+ return String.format("Generated %s", key);
}
}
@@ -369,7 +341,7 @@ public abstract class Response {
private byte key;
private short parameters;
- protected Export(ResponseAPDU response, long time, byte keyPair, byte key, short parameters) {
+ public Export(ResponseAPDU response, long time, byte keyPair, byte key, short parameters) {
super(response, time);
this.keyPair = keyPair;
this.key = key;
@@ -452,7 +424,7 @@ public abstract class Response {
}
@Override
- public String toString() {
+ public String getDescription() {
String source;
if (key == EC_Consts.KEY_BOTH) {
source = "both keys";
@@ -465,7 +437,7 @@ public abstract class Response {
} else {
pair = ((keyPair == ECTesterApplet.KEYPAIR_LOCAL) ? "local" : "remote") + " keypair";
}
- return super.toString(String.format("Exported params from %s of %s", source, pair));
+ return String.format("Exported params from %s of %s", source, pair);
}
}
@@ -480,7 +452,7 @@ public abstract class Response {
private short corruption;
private byte type;
- protected ECDH(ResponseAPDU response, long time, byte pubkey, byte privkey, byte export, short corruption, byte type) {
+ public ECDH(ResponseAPDU response, long time, byte pubkey, byte privkey, byte export, short corruption, byte type) {
super(response, time);
this.pubkey = pubkey;
this.privkey = privkey;
@@ -504,7 +476,7 @@ public abstract class Response {
}
@Override
- public String toString() {
+ public String getDescription() {
String algo = Util.getKA(type);
String pub = pubkey == ECTesterApplet.KEYPAIR_LOCAL ? "local" : "remote";
@@ -516,7 +488,7 @@ public abstract class Response {
} else {
validity = Util.getCorruption(corruption);
}
- return super.toString(String.format("%s of %s pubkey and %s privkey(%s point)", algo, pub, priv, validity));
+ return String.format("%s of %s pubkey and %s privkey(%s point)", algo, pub, priv, validity);
}
}
@@ -529,7 +501,7 @@ public abstract class Response {
private byte export;
private byte[] raw;
- protected ECDSA(ResponseAPDU response, long time, byte keyPair, byte export, byte[] raw) {
+ public ECDSA(ResponseAPDU response, long time, byte keyPair, byte export, byte[] raw) {
super(response, time);
this.keyPair = keyPair;
this.export = export;
@@ -547,10 +519,10 @@ public abstract class Response {
}
@Override
- public String toString() {
+ public String getDescription() {
String key = keyPair == ECTesterApplet.KEYPAIR_LOCAL ? "local" : "remote";
String data = raw == null ? "random" : "provided";
- return super.toString(String.format("ECDSA with %s keypair(%s data)", key, data));
+ return String.format("ECDSA with %s keypair(%s data)", key, data);
}
}
@@ -559,15 +531,15 @@ public abstract class Response {
*/
public static class Cleanup extends Response {
- protected Cleanup(ResponseAPDU response, long time) {
+ public Cleanup(ResponseAPDU response, long time) {
super(response, time);
parse(1, 0);
}
@Override
- public String toString() {
- return super.toString("Requested JCSystem object deletion");
+ public String getDescription() {
+ return "Requested JCSystem object deletion";
}
}
@@ -577,15 +549,15 @@ public abstract class Response {
*/
public static class Support extends Response {
- protected Support(ResponseAPDU response, long time) {
+ public Support(ResponseAPDU response, long time) {
super(response, time);
parse(3, 0);
}
@Override
- public String toString() {
- return super.toString("Support of ECDH, ECDHC, ECDSA");
+ public String getDescription() {
+ return "Support of ECDH, ECDHC, ECDSA allocation";
}
}
}
diff --git a/src/cz/crcs/ectester/reader/test/CompositeCurvesSuite.java b/src/cz/crcs/ectester/reader/test/CompositeCurvesSuite.java
new file mode 100644
index 0000000..8e7ca31
--- /dev/null
+++ b/src/cz/crcs/ectester/reader/test/CompositeCurvesSuite.java
@@ -0,0 +1,53 @@
+package cz.crcs.ectester.reader.test;
+
+import cz.crcs.ectester.applet.ECTesterApplet;
+import cz.crcs.ectester.applet.EC_Consts;
+import cz.crcs.ectester.data.EC_Store;
+import cz.crcs.ectester.reader.CardMngr;
+import cz.crcs.ectester.reader.ECTester;
+import cz.crcs.ectester.reader.command.Command;
+import cz.crcs.ectester.reader.ec.EC_Curve;
+import cz.crcs.ectester.reader.ec.EC_Key;
+import javacard.security.KeyPair;
+
+import java.util.Map;
+
+import static cz.crcs.ectester.reader.test.Result.ExpectedValue;
+
+/**
+ * @author Jan Jancar johny@neuromancer.sk
+ */
+public class CompositeCurvesSuite extends TestSuite {
+
+ public CompositeCurvesSuite(EC_Store dataStore, ECTester.Config cfg) {
+ super(dataStore, cfg, "composite", "The composite suite tests ECDH over curves with composite order. This should generally fail, as using such a curve is unsafe.");
+ }
+
+ @Override
+ public void setup(CardMngr cardManager) {
+ /* Do the default tests with the public keys set to provided smallorder keys
+ * over composite order curves. Essentially small subgroup attacks.
+ * These should fail, the curves aren't safe so that if the computation with
+ * a small order public key succeeds the private key modulo the public key order
+ * is revealed.
+ */
+ Map<String, EC_Key> keys = dataStore.getObjects(EC_Key.class, "composite");
+ for (EC_Key key : keys.values()) {
+ EC_Curve curve = dataStore.getObject(EC_Curve.class, key.getCurve());
+ if (cfg.namedCurve != null && !(key.getCurve().startsWith(cfg.namedCurve) || key.getCurve().equals(cfg.namedCurve))) {
+ continue;
+ }
+ if (curve.getField() == KeyPair.ALG_EC_FP && !cfg.primeField || curve.getField() == KeyPair.ALG_EC_F2M && !cfg.binaryField) {
+ continue;
+ }
+ if ((curve.getBits() == cfg.bits || cfg.all)) {
+ tests.add(new Test.Simple(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), ExpectedValue.SUCCESS));
+ tests.add(new Test.Simple(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), ExpectedValue.ANY));
+ tests.add(new Test.Simple(new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_LOCAL), ExpectedValue.ANY));
+ Command ecdhCommand = new Command.ECDH_direct(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_NONE, EC_Consts.KA_ECDH, key.flatten());
+ tests.add(new Test.Simple(ecdhCommand, ExpectedValue.FAILURE, "Card correctly rejected to do ECDH over a composite order curve.", "Card incorrectly does ECDH over a composite order curve, leaks bits of private key."));
+ tests.add(new Test.Simple(new Command.Cleanup(cardManager), ExpectedValue.ANY));
+ }
+ }
+ }
+}
diff --git a/src/cz/crcs/ectester/reader/test/DefaultSuite.java b/src/cz/crcs/ectester/reader/test/DefaultSuite.java
new file mode 100644
index 0000000..736b7c5
--- /dev/null
+++ b/src/cz/crcs/ectester/reader/test/DefaultSuite.java
@@ -0,0 +1,69 @@
+package cz.crcs.ectester.reader.test;
+
+import cz.crcs.ectester.applet.ECTesterApplet;
+import cz.crcs.ectester.applet.EC_Consts;
+import cz.crcs.ectester.data.EC_Store;
+import cz.crcs.ectester.reader.CardMngr;
+import cz.crcs.ectester.reader.ECTester;
+import cz.crcs.ectester.reader.command.Command;
+import javacard.security.KeyPair;
+
+import java.io.IOException;
+
+import static cz.crcs.ectester.reader.test.Result.ExpectedValue;
+
+/**
+ * @author Jan Jancar johny@neuromancer.sk
+ */
+public class DefaultSuite extends TestSuite {
+
+ public DefaultSuite(EC_Store dataStore, ECTester.Config cfg) {
+ super(dataStore, cfg, "default", "The default test suite tests basic support of ECDH and ECDSA.");
+ }
+
+ @Override
+ public void setup(CardMngr cardManager) throws IOException {
+ tests.add(new Test.Simple(new Command.Support(cardManager), ExpectedValue.ANY));
+ if (cfg.namedCurve != null) {
+ String desc = "Default tests over the " + cfg.namedCurve + " curve category.";
+ if (cfg.primeField) {
+ tests.addAll(defaultCategoryTests(cardManager, cfg.namedCurve, KeyPair.ALG_EC_FP, ExpectedValue.SUCCESS, ExpectedValue.SUCCESS, ExpectedValue.SUCCESS, ExpectedValue.ANY, ExpectedValue.SUCCESS, desc));
+ }
+ if (cfg.binaryField) {
+ tests.addAll(defaultCategoryTests(cardManager, cfg.namedCurve, KeyPair.ALG_EC_F2M, ExpectedValue.SUCCESS, ExpectedValue.SUCCESS, ExpectedValue.SUCCESS, ExpectedValue.ANY, ExpectedValue.SUCCESS, desc));
+ }
+ } else {
+ if (cfg.all) {
+ if (cfg.primeField) {
+ //iterate over prime curve sizes used: EC_Consts.FP_SIZES
+ for (short keyLength : EC_Consts.FP_SIZES) {
+ defaultTests(cardManager, keyLength, KeyPair.ALG_EC_FP);
+ }
+ }
+ if (cfg.binaryField) {
+ //iterate over binary curve sizes used: EC_Consts.F2M_SIZES
+ for (short keyLength : EC_Consts.F2M_SIZES) {
+ defaultTests(cardManager, keyLength, KeyPair.ALG_EC_F2M);
+ }
+ }
+ } else {
+ if (cfg.primeField) {
+ defaultTests(cardManager, (short) cfg.bits, KeyPair.ALG_EC_FP);
+ }
+
+ if (cfg.binaryField) {
+ defaultTests(cardManager, (short) cfg.bits, KeyPair.ALG_EC_F2M);
+ }
+ }
+ }
+ }
+
+ private void defaultTests(CardMngr cardManager, short keyLength, byte keyType) throws IOException {
+ tests.add(new Test.Simple(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_BOTH, keyLength, keyType), ExpectedValue.SUCCESS));
+ Command curve = Command.prepareCurve(cardManager, dataStore, cfg, ECTesterApplet.KEYPAIR_BOTH, keyLength, keyType);
+ if (curve != null)
+ tests.add(new Test.Simple(curve, ExpectedValue.SUCCESS));
+ tests.add(defaultCurveTests(cardManager, ExpectedValue.SUCCESS, ExpectedValue.SUCCESS, ExpectedValue.ANY, ExpectedValue.SUCCESS, "Default tests."));
+ tests.add(new Test.Simple(new Command.Cleanup(cardManager), ExpectedValue.ANY));
+ }
+}
diff --git a/src/cz/crcs/ectester/reader/test/InvalidCurvesSuite.java b/src/cz/crcs/ectester/reader/test/InvalidCurvesSuite.java
new file mode 100644
index 0000000..f61b695
--- /dev/null
+++ b/src/cz/crcs/ectester/reader/test/InvalidCurvesSuite.java
@@ -0,0 +1,68 @@
+package cz.crcs.ectester.reader.test;
+
+import cz.crcs.ectester.applet.ECTesterApplet;
+import cz.crcs.ectester.applet.EC_Consts;
+import cz.crcs.ectester.data.EC_Store;
+import cz.crcs.ectester.reader.CardMngr;
+import cz.crcs.ectester.reader.ECTester;
+import cz.crcs.ectester.reader.command.Command;
+import cz.crcs.ectester.reader.ec.EC_Curve;
+import cz.crcs.ectester.reader.ec.EC_Key;
+import javacard.security.KeyPair;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import static cz.crcs.ectester.reader.test.Result.ExpectedValue;
+
+/**
+ * @author Jan Jancar johny@neuromancer.sk
+ */
+public class InvalidCurvesSuite extends TestSuite {
+
+ public InvalidCurvesSuite(EC_Store dataStore, ECTester.Config cfg) {
+ super(dataStore, cfg, "invalid", "The invalid curve suite tests whether the card rejects points outside of the curve during ECDH.");
+ }
+
+ @Override
+ public void setup(CardMngr cardManager) throws IOException {
+ /* Set original curves (secg/nist/brainpool). Generate local.
+ * Try ECDH with invalid public keys of increasing (or decreasing) order.
+ */
+ Map<String, EC_Key.Public> pubkeys = dataStore.getObjects(EC_Key.Public.class, "invalid");
+ Map<EC_Curve, List<EC_Key.Public>> curves = new HashMap<>();
+ for (EC_Key.Public key : pubkeys.values()) {
+ EC_Curve curve = dataStore.getObject(EC_Curve.class, key.getCurve());
+ if (cfg.namedCurve != null && !(key.getCurve().startsWith(cfg.namedCurve) || key.getCurve().equals(cfg.namedCurve))) {
+ continue;
+ }
+ if (curve.getBits() != cfg.bits && !cfg.all) {
+ continue;
+ }
+ if (curve.getField() == KeyPair.ALG_EC_FP && !cfg.primeField || curve.getField() == KeyPair.ALG_EC_F2M && !cfg.binaryField) {
+ continue;
+ }
+ List<EC_Key.Public> keys = curves.getOrDefault(curve, new LinkedList<>());
+ keys.add(key);
+ curves.putIfAbsent(curve, keys);
+ }
+ for (Map.Entry<EC_Curve, List<EC_Key.Public>> e : curves.entrySet()) {
+ EC_Curve curve = e.getKey();
+ List<EC_Key.Public> keys = e.getValue();
+
+ tests.add(new Test.Simple(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), ExpectedValue.SUCCESS));
+ tests.add(new Test.Simple(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), ExpectedValue.SUCCESS));
+ tests.add(new Test.Simple(new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_LOCAL), ExpectedValue.SUCCESS));
+ List<Test> ecdhTests = new LinkedList<>();
+ for (EC_Key.Public pub : keys) {
+ Command ecdhCommand = new Command.ECDH_direct(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_NONE, EC_Consts.KA_ANY, pub.flatten());
+ ecdhTests.add(new Test.Simple(ecdhCommand, ExpectedValue.FAILURE, "Card correctly rejected point on invalid curve." , "Card incorrectly accepted point on invalid curve."));
+ }
+ tests.add(Test.Compound.all(ExpectedValue.SUCCESS, "Invalid curve test of " + curve.getId(), ecdhTests.toArray(new Test[0])));
+ tests.add(new Test.Simple(new Command.Cleanup(cardManager), ExpectedValue.ANY));
+ }
+ }
+}
diff --git a/src/cz/crcs/ectester/reader/test/Result.java b/src/cz/crcs/ectester/reader/test/Result.java
new file mode 100644
index 0000000..82f0f32
--- /dev/null
+++ b/src/cz/crcs/ectester/reader/test/Result.java
@@ -0,0 +1,94 @@
+package cz.crcs.ectester.reader.test;
+
+/**
+ * @author Jan Jancar johny@neuromancer.sk
+ */
+public class Result {
+
+ private Value value;
+ private String cause;
+
+ public Result(Value value) {
+ this.value = value;
+ }
+
+ public Result(Value value, String cause) {
+ this(value);
+ this.cause = cause;
+ }
+
+ public Value getValue() {
+ return value;
+ }
+
+ public String 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;
+ }
+
+ /**
+ *
+ */
+ public enum Value {
+ SUCCESS(true),
+ FAILURE(false),
+ UXSUCCESS(false),
+ XFAILURE(true),
+ ERROR(false);
+
+ private boolean ok;
+
+ Value(boolean ok) {
+ this.ok = ok;
+ }
+
+ 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 SUCCESS;
+ }
+ 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 enum ExpectedValue {
+ SUCCESS,
+ FAILURE,
+ ANY
+ }
+}
diff --git a/src/cz/crcs/ectester/reader/test/Test.java b/src/cz/crcs/ectester/reader/test/Test.java
new file mode 100644
index 0000000..022ad56
--- /dev/null
+++ b/src/cz/crcs/ectester/reader/test/Test.java
@@ -0,0 +1,217 @@
+package cz.crcs.ectester.reader.test;
+
+import cz.crcs.ectester.reader.command.Command;
+import cz.crcs.ectester.reader.response.Response;
+
+import javax.smartcardio.CardException;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
+import static cz.crcs.ectester.reader.test.Result.ExpectedValue;
+import static cz.crcs.ectester.reader.test.Result.Value;
+
+/**
+ * An abstract test that can be run and has a Result.
+ *
+ * @author Jan Jancar johny@neuromancer.sk
+ */
+public abstract class Test {
+ boolean hasRun = false;
+ 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) {
+ return true;
+ }
+ return result.ok();
+ }
+
+ public abstract String getDescription();
+
+ public boolean hasRun() {
+ return hasRun;
+ }
+
+ public abstract void run() throws CardException;
+
+ /**
+ * A simple test that runs one Command to get and evaluate one Response
+ * to get a Result and compare it with the expected one.
+ */
+ public static class Simple extends Test {
+ private BiFunction<Command, Response, Result> callback;
+ private Command command;
+ private Response response;
+
+ public Simple(Command command, BiFunction<Command, Response, Result> callback) {
+ this.command = command;
+ this.callback = callback;
+ }
+
+ public Simple(Command command, ExpectedValue expected, String ok, String nok) {
+ this(command, (cmd, resp) -> {
+ Value resultValue = Value.fromExpected(expected, resp.successful(), resp.error());
+ return new Result(resultValue, resultValue.ok() ? ok : nok);
+ });
+ }
+
+ public Simple(Command command, ExpectedValue expected) {
+ this(command, expected, null, null);
+ }
+
+ public Command getCommand() {
+ return command;
+ }
+
+ public Response getResponse() {
+ return response;
+ }
+
+ @Override
+ public void run() throws CardException {
+ if (hasRun)
+ return;
+
+ response = command.send();
+ if (callback != null) {
+ result = callback.apply(command, response);
+ } else {
+ if (response.successful()) {
+ result = new Result(Value.SUCCESS);
+ } else {
+ result = new Result(Value.FAILURE);
+ }
+ }
+ hasRun = true;
+ }
+
+ @Override
+ public String getDescription() {
+ return response.getDescription();
+ }
+ }
+
+ /**
+ * A compound test that runs many Tests and has a Result dependent on all/some of their Results.
+ */
+ public static class Compound extends Test {
+ private Function<Test[], Result> callback;
+ private Test[] tests;
+ private String description;
+
+ private Compound(Function<Test[], Result> callback, Test... tests) {
+ this.callback = callback;
+ this.tests = tests;
+ }
+
+ private Compound(Function<Test[], Result> callback, String descripiton, Test... tests) {
+ this(callback, tests);
+ this.description = descripiton;
+ }
+
+ public static Compound function(Function<Test[], Result> callback, Test... tests) {
+ return new Compound(callback, tests);
+ }
+
+ public static Compound function(Function<Test[], Result> callback, String description, Test... tests) {
+ return new Compound(callback, description, tests);
+ }
+
+ public static Compound all(ExpectedValue what, Test... all) {
+ return new Compound((tests) -> {
+ for (Test test : tests) {
+ if (!Value.fromExpected(what, test.ok()).ok()) {
+ return new Result(Value.FAILURE, "At least one of the sub-tests did not have the expected result.");
+ }
+ }
+ return new Result(Value.SUCCESS, "All sub-tests had the expected result.");
+ }, all);
+ }
+
+ public static Compound all(ExpectedValue what, String description, Test... all) {
+ Compound result = Compound.all(what, all);
+ result.setDescription(description);
+ return result;
+ }
+
+ public static Compound any(ExpectedValue what, Test... any) {
+ return new Compound((tests) -> {
+ for (Test test : tests) {
+ if (Value.fromExpected(what, test.ok()).ok()) {
+ return new Result(Value.SUCCESS, "At least one of the sub-tests did have the expected result.");
+ }
+ }
+ return new Result(Value.FAILURE, "None of the sub-tests had the expected result.");
+ }, any);
+ }
+
+ public static Compound any(ExpectedValue what, String description, Test... any) {
+ Compound result = Compound.any(what, any);
+ result.setDescription(description);
+ return result;
+ }
+
+ public static Compound mask(ExpectedValue[] results, Test... masked) {
+ return new Compound((tests) -> {
+ for (int i = 0; i < results.length; ++i) {
+ if (!Value.fromExpected(results[i], tests[i].ok()).ok()) {
+ return new Result(Value.FAILURE, "At least one of the sub-tests did not match the result mask.");
+ }
+ }
+ return new Result(Value.SUCCESS, "All sub-tests matched the expected mask.");
+ }, masked);
+ }
+
+ public static Compound mask(ExpectedValue[] results, String description, Test... masked) {
+ Compound result = Compound.mask(results, masked);
+ result.setDescription(description);
+ return result;
+ }
+
+ public Test[] getTests() {
+ return tests;
+ }
+
+ @Override
+ public void run() throws CardException {
+ if (hasRun)
+ return;
+
+ for (Test test : tests) {
+ test.run();
+ }
+ result = callback.apply(tests);
+ this.hasRun = true;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ @Override
+ public String getDescription() {
+ return description;
+ }
+ }
+}
diff --git a/src/cz/crcs/ectester/reader/test/TestRunner.java b/src/cz/crcs/ectester/reader/test/TestRunner.java
new file mode 100644
index 0000000..baab6a8
--- /dev/null
+++ b/src/cz/crcs/ectester/reader/test/TestRunner.java
@@ -0,0 +1,29 @@
+package cz.crcs.ectester.reader.test;
+
+import cz.crcs.ectester.reader.output.TestWriter;
+
+import javax.smartcardio.CardException;
+
+/**
+ * @author Jan Jancar johny@neuromancer.sk
+ */
+public class TestRunner {
+ private TestSuite suite;
+ private TestWriter writer;
+
+ public TestRunner(TestSuite suite, TestWriter writer) {
+ this.suite = suite;
+ this.writer = writer;
+ }
+
+ public void run() throws CardException {
+ writer.begin(suite);
+ for (Test t : suite.getTests()) {
+ if (!t.hasRun()) {
+ t.run();
+ writer.outputTest(t);
+ }
+ }
+ writer.end();
+ }
+}
diff --git a/src/cz/crcs/ectester/reader/test/TestSuite.java b/src/cz/crcs/ectester/reader/test/TestSuite.java
new file mode 100644
index 0000000..f13317c
--- /dev/null
+++ b/src/cz/crcs/ectester/reader/test/TestSuite.java
@@ -0,0 +1,135 @@
+package cz.crcs.ectester.reader.test;
+
+import cz.crcs.ectester.applet.ECTesterApplet;
+import cz.crcs.ectester.applet.EC_Consts;
+import cz.crcs.ectester.data.EC_Store;
+import cz.crcs.ectester.reader.CardMngr;
+import cz.crcs.ectester.reader.ECTester;
+import cz.crcs.ectester.reader.command.Command;
+import cz.crcs.ectester.reader.ec.EC_Curve;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+
+import static cz.crcs.ectester.reader.test.Result.ExpectedValue;
+import static cz.crcs.ectester.reader.test.Result.Value;
+
+/**
+ * @author Jan Jancar johny@neuromancer.sk
+ */
+public abstract class TestSuite {
+ EC_Store dataStore;
+ ECTester.Config cfg;
+ String name;
+ String description;
+ List<Test> tests = new LinkedList<>();
+
+ TestSuite(EC_Store dataStore, ECTester.Config cfg, String name, String description) {
+ this.dataStore = dataStore;
+ this.cfg = cfg;
+ this.name = name;
+ this.description = description;
+ }
+
+ public abstract void setup(CardMngr cardManager) throws IOException;
+
+ public List<Test> getTests() {
+ return Collections.unmodifiableList(tests);
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * @param cardManager cardManager to send APDU through
+ * @param generateExpected expected result of the Generate command
+ * @param ecdhExpected expected result of the ordinary ECDH command
+ * @param ecdhCompressExpected expected result of ECDH with a compressed point
+ * @param ecdsaExpected expected result of the ordinary ECDSA command
+ * @param description compound test description
+ * @return test to run
+ */
+ Test defaultCurveTests(CardMngr cardManager, ExpectedValue generateExpected, ExpectedValue ecdhExpected, ExpectedValue ecdhCompressExpected, ExpectedValue ecdsaExpected, String description) {
+ List<Test> tests = new LinkedList<>();
+
+ tests.add(new Test.Simple(new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_BOTH), generateExpected));
+ tests.add(new Test.Simple(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_NONE, EC_Consts.KA_ECDH), ecdhExpected));
+ tests.add(new Test.Simple(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_COMPRESS, EC_Consts.KA_ECDH), ecdhExpected));
+ tests.add(new Test.Simple(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_ONE, EC_Consts.KA_ECDH), ExpectedValue.FAILURE));
+ tests.add(new Test.Simple(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_ZERO, EC_Consts.KA_ECDH), ExpectedValue.FAILURE));
+ tests.add(new Test.Simple(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_MAX, EC_Consts.KA_ECDH), ExpectedValue.FAILURE));
+ tests.add(new Test.Simple(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_FULLRANDOM, EC_Consts.KA_ECDH), ExpectedValue.FAILURE));
+ tests.add(new Test.Simple(new Command.ECDSA(cardManager, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, null), ecdsaExpected));
+
+ return Test.Compound.function((testArray) -> {
+ Function<ExpectedValue, String> shouldHave = (expected) -> {
+ switch (expected) {
+ case SUCCESS:
+ return "succeeded";
+ case FAILURE:
+ return "failed";
+ case ANY:
+ default:
+ return "";
+ }
+ };
+
+ for (int i = 0; i < testArray.length; ++i) {
+ Test t = testArray[i];
+ if (!t.ok()) {
+ if (i == 0) { // generate
+ return new Result(Value.FAILURE, "The generation of a key should have " + shouldHave.apply(generateExpected) + ", but it did not.");
+ } else if (i == 2) { // ecdh compress
+ return new Result(Value.FAILURE, "The ECDH should have " + shouldHave.apply(ecdhExpected) + ", but it did not.");
+ } else if (i == 1) { // ecdh normal
+ return new Result(Value.FAILURE, "The ECDH of a compressed point should have " + shouldHave.apply(ecdhCompressExpected) + ", but it did not.");
+ } else if (i <= 6) { // ecdh wrong, should fail
+ return new Result(Value.FAILURE, "The ECDH of a corrupted point should have failed, but it did not.");
+ } else { // ecdsa
+ return new Result(Value.FAILURE, "The ECDSA should have " + shouldHave.apply(ecdsaExpected) + ", but it did not.");
+ }
+ }
+ }
+ return new Result(Value.SUCCESS);
+ }, description, tests.toArray(new Test[0]));
+ }
+
+ /**
+ * @param cardManager cardManager to send APDU through
+ * @param category category to test
+ * @param field field to test (KeyPair.ALG_EC_FP || KeyPair.ALG_EC_F2M)
+ * @param setExpected expected result of the Set (curve) command
+ * @param generateExpected expected result of the Generate command
+ * @param ecdhExpected expected result of the ordinary ECDH command
+ * @param ecdhCompressedExpected expected result of the ECDH command with a compressed point.
+ * @param ecdsaExpected expected result of the ordinary ECDSA command
+ * @param description compound test description
+ * @return tests to run
+ */
+ List<Test> defaultCategoryTests(CardMngr cardManager, String category, byte field, ExpectedValue setExpected, ExpectedValue generateExpected, ExpectedValue ecdhExpected, ExpectedValue ecdhCompressedExpected, ExpectedValue ecdsaExpected, String description) {
+ List<Test> tests = new LinkedList<>();
+ Map<String, EC_Curve> curves = dataStore.getObjects(EC_Curve.class, category);
+ if (curves == null)
+ return tests;
+ for (Map.Entry<String, EC_Curve> entry : curves.entrySet()) {
+ EC_Curve curve = entry.getValue();
+ if (curve.getField() == field && (curve.getBits() == cfg.bits || cfg.all)) {
+ tests.add(new Test.Simple(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), field), ExpectedValue.SUCCESS));
+ tests.add(new Test.Simple(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), setExpected));
+ tests.add(defaultCurveTests(cardManager, generateExpected, ecdhExpected, ecdhCompressedExpected, ecdsaExpected, description));
+ tests.add(new Test.Simple(new Command.Cleanup(cardManager), ExpectedValue.ANY));
+ }
+ }
+
+ return tests;
+ }
+}
diff --git a/src/cz/crcs/ectester/reader/test/TestVectorSuite.java b/src/cz/crcs/ectester/reader/test/TestVectorSuite.java
new file mode 100644
index 0000000..ff46feb
--- /dev/null
+++ b/src/cz/crcs/ectester/reader/test/TestVectorSuite.java
@@ -0,0 +1,83 @@
+package cz.crcs.ectester.reader.test;
+
+import cz.crcs.ectester.applet.ECTesterApplet;
+import cz.crcs.ectester.applet.EC_Consts;
+import cz.crcs.ectester.data.EC_Store;
+import cz.crcs.ectester.reader.CardMngr;
+import cz.crcs.ectester.reader.ECTester;
+import cz.crcs.ectester.reader.Util;
+import cz.crcs.ectester.reader.command.Command;
+import cz.crcs.ectester.reader.ec.*;
+import cz.crcs.ectester.reader.response.Response;
+import javacard.security.KeyPair;
+
+import java.io.IOException;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import static cz.crcs.ectester.reader.test.Result.ExpectedValue;
+import static cz.crcs.ectester.reader.test.Result.Value;
+
+/**
+ * @author Jan Jancar johny@neuromancer.sk
+ */
+public class TestVectorSuite extends TestSuite {
+
+ public TestVectorSuite(EC_Store dataStore, ECTester.Config cfg) {
+ super(dataStore, cfg, "test", "The test-vectors suite contains a collection of test vectors which test basic ECDH correctness.");
+ }
+
+ @Override
+ public void setup(CardMngr cardManager) throws IOException {
+ /* Set original curves (secg/nist/brainpool). Set keypairs from test vectors.
+ * Do ECDH both ways, export and verify that the result is correct.
+ */
+ Map<String, EC_KAResult> results = dataStore.getObjects(EC_KAResult.class, "test");
+ for (EC_KAResult result : results.values()) {
+ EC_Curve curve = dataStore.getObject(EC_Curve.class, result.getCurve());
+ if (cfg.namedCurve != null && !(result.getCurve().startsWith(cfg.namedCurve) || result.getCurve().equals(cfg.namedCurve))) {
+ continue;
+ }
+ if (curve.getBits() != cfg.bits && !cfg.all) {
+ continue;
+ }
+ if (curve.getField() == KeyPair.ALG_EC_FP && !cfg.primeField || curve.getField() == KeyPair.ALG_EC_F2M && !cfg.binaryField) {
+ continue;
+ }
+ EC_Params onekey = dataStore.getObject(EC_Keypair.class, result.getOneKey());
+ if (onekey == null) {
+ onekey = dataStore.getObject(EC_Key.Private.class, result.getOneKey());
+ }
+ EC_Params otherkey = dataStore.getObject(EC_Keypair.class, result.getOtherKey());
+ if (otherkey == null) {
+ otherkey = dataStore.getObject(EC_Key.Public.class, result.getOtherKey());
+ }
+ if (onekey == null || otherkey == null) {
+ throw new IOException("Test vector keys couldn't be located.");
+ }
+ List<Test> testVector = new LinkedList<>();
+
+ testVector.add(new Test.Simple(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), ExpectedValue.SUCCESS));
+ testVector.add(new Test.Simple(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), ExpectedValue.SUCCESS));
+ //tests.add(new Test.Simple(new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_BOTH), ExpectedValue.SUCCESS));
+ testVector.add(new Test.Simple(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.CURVE_external, EC_Consts.PARAMETER_S, onekey.flatten(EC_Consts.PARAMETER_S)), ExpectedValue.SUCCESS));
+ testVector.add(new Test.Simple(new Command.Set(cardManager, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, EC_Consts.PARAMETER_W, otherkey.flatten(EC_Consts.PARAMETER_W)), ExpectedValue.SUCCESS));
+ testVector.add(new Test.Simple(new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_TRUE, EC_Consts.CORRUPTION_NONE, result.getKA()), (command, response) -> {
+ Response.ECDH dh = (Response.ECDH) response;
+ if (!dh.successful())
+ return new Result(Value.FAILURE, "ECDH was unsuccessful.");
+ if (!dh.hasSecret())
+ return new Result(Value.FAILURE, "ECDH response did not contain the derived secret.");
+ if (!Util.compareBytes(dh.getSecret(), 0, result.getParam(0), 0, dh.secretLength())) {
+ int firstDiff = Util.diffBytes(dh.getSecret(), 0, result.getParam(0), 0, dh.secretLength());
+ return new Result(Value.FAILURE, "ECDH derived secret does not match the test, first difference was at byte " + String.valueOf(firstDiff) + ".");
+ }
+ return new Result(Value.SUCCESS);
+ }));
+ tests.add(Test.Compound.all(ExpectedValue.SUCCESS, "Test vector " + result.getId(), testVector.toArray(new Test[0])));
+ tests.add(new Test.Simple(new Command.Cleanup(cardManager), ExpectedValue.ANY));
+
+ }
+ }
+}
diff --git a/src/cz/crcs/ectester/reader/test/WrongCurvesSuite.java b/src/cz/crcs/ectester/reader/test/WrongCurvesSuite.java
new file mode 100644
index 0000000..e9389b4
--- /dev/null
+++ b/src/cz/crcs/ectester/reader/test/WrongCurvesSuite.java
@@ -0,0 +1,34 @@
+package cz.crcs.ectester.reader.test;
+
+import cz.crcs.ectester.data.EC_Store;
+import cz.crcs.ectester.reader.CardMngr;
+import cz.crcs.ectester.reader.ECTester;
+import javacard.security.KeyPair;
+
+import java.io.IOException;
+
+import static cz.crcs.ectester.reader.test.Result.ExpectedValue;
+
+/**
+ * @author Jan Jancar johny@neuromancer.sk
+ */
+public class WrongCurvesSuite extends TestSuite {
+
+ public WrongCurvesSuite(EC_Store dataStore, ECTester.Config cfg) {
+ super(dataStore, cfg, "wrong", "The wrong curve suite tests whether the card rejects domain parameters which are not curves.");
+ }
+
+ @Override
+ public void setup(CardMngr cardManager) throws IOException {
+ /* Just do the default tests on the wrong curves.
+ * These should generally fail, the curves aren't curves.
+ */
+ String desc = "Default tests over wrong curve params.";
+ if (cfg.primeField) {
+ tests.addAll(defaultCategoryTests(cardManager, cfg.testSuite, KeyPair.ALG_EC_FP, ExpectedValue.FAILURE, ExpectedValue.FAILURE, ExpectedValue.FAILURE, ExpectedValue.FAILURE, ExpectedValue.FAILURE, desc));
+ }
+ if (cfg.binaryField) {
+ tests.addAll(defaultCategoryTests(cardManager, cfg.testSuite, KeyPair.ALG_EC_F2M, ExpectedValue.FAILURE, ExpectedValue.FAILURE, ExpectedValue.FAILURE, ExpectedValue.FAILURE, ExpectedValue.FAILURE, desc));
+ }
+ }
+}
diff --git a/src/cz/crcs/ectester/scripts/ectester.bat b/src/cz/crcs/ectester/scripts/ectester.bat
new file mode 100644
index 0000000..e20b855
--- /dev/null
+++ b/src/cz/crcs/ectester/scripts/ectester.bat
@@ -0,0 +1,34 @@
+@ECHO OFF
+SETLOCAL enabledelayedexpansion
+
+SET n=0
+:loop
+IF NOT "%1"=="" (
+ IF "%1"=="--dangerous" (
+ SET dangerous=1
+ ) ELSE (
+ SET positional[!n!]=%1
+ SET /A n+=1
+ )
+ SHIFT
+ GOTO :loop
+)
+
+IF NOT "%n%"=="1" (
+ ECHO "One argument expected:"
+ ECHO " ./ectester.bar [--dangerous] CARD_NAME"
+)
+
+SET card=!positional[%%0]!
+
+SET tests="default test-vectors"
+java -jar ECTester.jar -t default -a --format yaml -l %card%.default
+java -jar ECTester.jar -t test-vectors -a --format yaml -l %card%.test-vectors
+IF "%dangerous%"=="1" (
+ SET tests=%tests% "invalid wrong composite"
+ java -jar ECTester.jar -t invalid -a --format yaml -l %card%.invalid
+ java -jar ECTester.jar -t wrong -a --format yaml -l %card%.wrong
+ java -jar ECTester.jar -t composite -a --format yaml -l %card%.composite
+)
+
+zip %card%.zip %tests%
diff --git a/src/cz/crcs/ectester/scripts/ectester.sh b/src/cz/crcs/ectester/scripts/ectester.sh
new file mode 100755
index 0000000..8040096
--- /dev/null
+++ b/src/cz/crcs/ectester/scripts/ectester.sh
@@ -0,0 +1,48 @@
+#!/bin/bash
+
+dangerous="0"
+
+positional=()
+while [[ $# -gt 0 ]]
+do
+
+key="$1"
+case $key in
+ --dangerous)
+ dangerous=1
+ shift
+ ;;
+ *)
+ positional+=("$1")
+ shift
+ ;;
+esac
+done
+set -- "${positional[@]}"
+
+if [[ $# -lt 1 ]]; then
+ echo "At least one argument expected:" >&2
+ echo " ./ectester.sh [--dangerous] CARD_NAME [ECTester args]" >&2
+ exit 1
+fi
+
+card="$1"
+shift
+
+declare -a tests=("default" "test-vectors")
+if [[ "$dangerous" == "1" ]]; then
+ tests+=("invalid" "wrong" "composite")
+fi
+
+declare -a files=()
+for i in $(seq 0 $((${#tests[@]} - 1))); do
+ test="${tests[$i]}"
+ java -jar ECTester.jar -t ${test} -a --format yaml -l ${card}.${test} $@
+ files+=(${card}.$test)
+done
+
+if command -v tar 2>&1 >/dev/null; then
+ tar -czvf ${card}.tar.gz ${files[*]}
+elif command -v zip 2>&1 >/dev/null; then
+ zip ${card}.zip ${files[*]}
+fi