diff options
72 files changed, 2306 insertions, 685 deletions
diff --git a/!uploader/ectester.cap b/!uploader/ectester.cap Binary files differindex 3b9e650..b883965 100644 --- a/!uploader/ectester.cap +++ b/!uploader/ectester.cap 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 @@ -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 @@ -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 Binary files differindex 8195c6f..b5d07c5 100644 --- a/dist/ECTester.jar +++ b/dist/ECTester.jar 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 Binary files differnew file mode 100644 index 0000000..7c73a76 --- /dev/null +++ b/dist/lib/snakeyaml-1.19.jar 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 @@ -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 Binary files differnew file mode 100644 index 0000000..7c73a76 --- /dev/null +++ b/lib/snakeyaml-1.19.jar 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 |
