aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJ08nY2019-02-22 10:29:28 +0100
committerJ08nY2019-02-22 10:29:28 +0100
commitb6daaef0a884bd154a848bdb73919b3b82d0df98 (patch)
tree7a4034a9862324b0988050cfe9f13c66d633daec
parent687a09baf6fd858d393b8f284cfe7236b52d7457 (diff)
parentfea5c7b1cbd539b105b42c4bde65d0b9b6f0b8fc (diff)
downloadECTester-b6daaef0a884bd154a848bdb73919b3b82d0df98.tar.gz
ECTester-b6daaef0a884bd154a848bdb73919b3b82d0df98.tar.zst
ECTester-b6daaef0a884bd154a848bdb73919b3b82d0df98.zip
-rw-r--r--README.md166
-rw-r--r--docs/FORMAT.md41
-rw-r--r--docs/IMPLEMENTATIONS.md34
-rw-r--r--docs/TESTS.md79
-rw-r--r--src/cz/crcs/ectester/applet/AppletBase.java37
-rw-r--r--src/cz/crcs/ectester/applet/ECKeyGenerator.java154
-rw-r--r--src/cz/crcs/ectester/applet/ECKeyTester.java63
-rw-r--r--src/cz/crcs/ectester/common/ec/EC_Category.java2
-rw-r--r--src/cz/crcs/ectester/common/output/BaseTextTestWriter.java6
-rw-r--r--src/cz/crcs/ectester/common/util/CardUtil.java17
-rw-r--r--src/cz/crcs/ectester/common/util/ECUtil.java74
-rw-r--r--src/cz/crcs/ectester/data/cofactor/cofactor128p56467.csv1
-rw-r--r--src/cz/crcs/ectester/data/cofactor/cofactor128p65521.csv1
-rw-r--r--src/cz/crcs/ectester/data/cofactor/cofactor128p65535.csv1
-rw-r--r--src/cz/crcs/ectester/data/cofactor/cofactor192p2.csv1
-rw-r--r--src/cz/crcs/ectester/data/cofactor/cofactor192p4.csv1
-rw-r--r--src/cz/crcs/ectester/data/cofactor/cofactor192p8.csv1
-rw-r--r--src/cz/crcs/ectester/data/cofactor/curves.xml30
-rw-r--r--src/cz/crcs/ectester/data/cofactor/keys.xml19
-rw-r--r--src/cz/crcs/ectester/data/degenerate/cofactor.xml123
-rw-r--r--src/cz/crcs/ectester/data/degenerate/keys.xml2
-rw-r--r--src/cz/crcs/ectester/data/invalid/secg/secp128r1.xml6
-rw-r--r--src/cz/crcs/ectester/data/misc/results.xml4
-rw-r--r--src/cz/crcs/ectester/data/twist/cofactor/cofactor128p4.xml44
-rw-r--r--src/cz/crcs/ectester/data/twist/cofactor/cofactor160p4.xml74
-rw-r--r--src/cz/crcs/ectester/data/twist/keys.xml6
-rw-r--r--src/cz/crcs/ectester/data/wycheproof/keys.xml86
-rw-r--r--src/cz/crcs/ectester/reader/CardMngr.java11
-rw-r--r--src/cz/crcs/ectester/reader/ECTesterReader.java185
-rw-r--r--src/cz/crcs/ectester/reader/command/Command.java122
-rw-r--r--src/cz/crcs/ectester/reader/output/TextTestWriter.java1
-rw-r--r--src/cz/crcs/ectester/reader/output/XMLTestWriter.java2
-rw-r--r--src/cz/crcs/ectester/reader/response/Response.java22
-rw-r--r--src/cz/crcs/ectester/reader/test/CardCofactorSuite.java5
-rw-r--r--src/cz/crcs/ectester/reader/test/CardCompositeSuite.java14
-rw-r--r--src/cz/crcs/ectester/reader/test/CardCompressionSuite.java4
-rw-r--r--src/cz/crcs/ectester/reader/test/CardDefaultSuite.java9
-rw-r--r--src/cz/crcs/ectester/reader/test/CardDegenerateSuite.java19
-rw-r--r--src/cz/crcs/ectester/reader/test/CardEdgeCasesSuite.java67
-rw-r--r--src/cz/crcs/ectester/reader/test/CardInvalidSuite.java14
-rw-r--r--src/cz/crcs/ectester/reader/test/CardMiscSuite.java2
-rw-r--r--src/cz/crcs/ectester/reader/test/CardSignatureSuite.java2
-rw-r--r--src/cz/crcs/ectester/reader/test/CardTestSuite.java22
-rw-r--r--src/cz/crcs/ectester/reader/test/CardTestVectorSuite.java86
-rw-r--r--src/cz/crcs/ectester/reader/test/CardTwistSuite.java16
-rw-r--r--src/cz/crcs/ectester/reader/test/CommandTest.java2
-rw-r--r--src/cz/crcs/ectester/reader/test/PerformanceTest.java42
-rw-r--r--src/cz/crcs/ectester/standalone/ECTesterStandalone.java94
-rw-r--r--src/cz/crcs/ectester/standalone/consts/SignatureIdent.java1
-rwxr-xr-xutil/plot_gen.py36
-rw-r--r--util/utils.py34
51 files changed, 1455 insertions, 430 deletions
diff --git a/README.md b/README.md
index d23922d..a766235 100644
--- a/README.md
+++ b/README.md
@@ -30,7 +30,7 @@ ant -f build-applet.xml build -Dcap=ectester221.cap
2. Run `java -jar dist/ECTesterReader.jar -t`.
3. Inspect output log with annotated results.
-Following operations are tested in the default suite:
+Following operations are tested in the default test suite:
- Allocation of new KeyPair class for specified parameters
- Generation of KeyPair with default curve
- Setting of custom curve and KeyPair generation
@@ -45,7 +45,7 @@ See `java -jar ECTesterReader.jar -h`, `java -jar ECTesterReader.jar -ls` and [D
-V,--version Print version info.
-h,--help Print help.
-ln,--list-named <what> Print the list of supported named
- curves and keys, (CurveDB and KeyDB).
+ curves and keys.
-ls,--list-suites List supported test suites.
-e,--export Export the defaut curve parameters
of the card(if any).
@@ -53,46 +53,40 @@ See `java -jar ECTesterReader.jar -h`, `java -jar ECTesterReader.jar -ls` and [D
-t,--test <test_suite[:from[:to]]> Test ECC support. Optionally specify
a test number to run only a part of
a test suite. <test_suite>:
- - default
- - compression
- - invalid
- - twist
- - degenerate
- - cofactor
- - wrong
- - signature
- - composite
- - test-vectors
- - edge-cases
- - miscellaneous
+ - default:
+ - compression:
+ - invalid:
+ - twist:
+ - degenerate:
+ - cofactor:
+ - wrong:
+ - signature:
+ - composite:
+ - test-vectors:
+ - edge-cases:
+ - miscellaneous:
-dh,--ecdh <count> Do EC KeyAgreement (ECDH...),
[count] times.
-dsa,--ecdsa <count> Sign data with ECDSA, [count] times.
-nf,--info Get applet info.
-
-b,--bit-size <bits> Set curve size.
-fp,--prime-field Use a prime field.
-f2m,--binary-field Use a binary field.
-
-nc,--named-curve <cat/id> Use a named curve, from CurveDB:
<cat/id>
-c,--curve <curve_file> Use curve from file <curve_file>
(field,a,b,gx,gy,r,k).
-u,--custom Use a custom curve (applet-side
embedded, SECG curves).
-
-npub,--named-public <cat/id> Use public key from KeyDB: <cat/id>
-pub,--public <pubkey_file> Use public key from file
<pubkey_file> (wx,wy).
-
-npriv,--named-private <cat/id> Use private key from KeyDB: <cat/id>
-priv,--private <privkey_file> Use private key from file
<privkey_file> (s).
-
- -nk,--named-key <cat/id> Use KeyPair from KeyDB: <cat/id>
- -k,--key <key_file> Use KeyPair from file <key_file>
+ -nk,--named-key <cat/id> Use keyPair from KeyDB: <cat/id>
+ -k,--key <key_file> Use keyPair from file <key_file>
(wx,wy,s).
-
-i,--input <input_file> Input from file <input_file>, for
ECDSA signing.
-o,--output <output_file> Output into file <output_file>. The
@@ -103,9 +97,18 @@ See `java -jar ECTesterReader.jar -h`, `java -jar ECTesterReader.jar -ls` and [D
-v,--verbose Turn on verbose logging.
--format <format> Output format to use. One of:
text,yml,xml.
-
+ --fixed Generate key(s) only once, keep them
+ for later operations.
+ --fixed-private Generate private key only once, keep
+ it for later ECDH.
+ --fixed-public Generate public key only once, keep
+ it for later ECDH.
-f,--fresh Generate fresh keys (set domain
parameters before every generation).
+ --time Output better timing values, by
+ running command in dry run mode and
+ normal mode, and subtracting the
+ two.
--cleanup Send the cleanup command trigerring
JCSystem.requestObjectDeletion()
after some operations.
@@ -113,10 +116,10 @@ See `java -jar ECTesterReader.jar -h`, `java -jar ECTesterReader.jar -ls` and [D
instead of using a terminal.
-y,--yes Accept all warnings and prompts.
-ka,--ka-type <type> Set KeyAgreement object [type],
- corresponds to JavaCard KeyAgreement
+ corresponds to JC.KeyAgreement
constants.
-sig,--sig-type <type> Set Signature object [type],
- corresponds to JavaCard Signature
+ corresponds to JC.Signature
constants.
-C,--color Print stuff with color, requires
ANSI terminal.
@@ -134,7 +137,7 @@ For format of this file see [FORMAT](docs/FORMAT.md).
#### Test
`-t / --test [test_suite]`
-Perform support,performance and vulnerability tests of ECC.
+Perform support, performance and vulnerability tests of ECC.
To select which tests will be performed, it is possible to enter the test suite name with a suffix
which specifies the number of the first test to be run, and optionally the number of the last test to be run as `-t <test_suite>[:start_index[:stop_index]]`.
@@ -146,15 +149,19 @@ For more info about the test suites see [TESTS](docs/TESTS.md).
#### Generate
`-g / --generate [amount]`
-Generates batches of EC keypairs and exports them.
+Generates batch of EC keypairs and exports them.
+
Use with `-o / --output [out_file]` to output the generated keys to a file.
+Use with `--time` to measure time as a difference of real duration of the operation and the dry-run duration of the operation.
For format of this file see [FORMAT](docs/FORMAT.md).
#### ECDH
`-dh / --ecdh [count]`
Performs ECDH.
+
Use with `-o / --output [out_file]` to output into a file.
+Use with `--time` to measure time as a difference of real duration of the operation and the dry-run duration of the operation.
For format of this file see [FORMAT](docs/FORMAT.md).
Respects the KeyAgreement type specified in `-ka / --ka-type [type]`.
@@ -165,6 +172,7 @@ Respects the KeyAgreement type specified in `-ka / --ka-type [type]`.
Performs ECDSA.
Useful with `-i / --input [in_file]` to sign the contents of a file.
Use with `-o / --output [out_file]` to output into a file.
+Use with `--time` to measure time as a difference of real duration of the operation and the dry-run duration of the operation.
For format of these files see [FORMAT](docs/FORMAT.md).
Respects the Signature type specified in `-sig / --sig-type [type]`.
@@ -172,9 +180,7 @@ Respects the Signature type specified in `-sig / --sig-type [type]`.
`-ln / --list-named []`
Lists categories of curves, keys and keypairs embedded in ECTester's jar, along with some information about them.
-These can be used as arguments to the `-n[c|k|pub|priv] / --named-[curve|key|public|private]` parameters.
-
-With the format: `category/name`.
+These can be used as arguments to the `-n[c|k|pub|priv] / --named-[curve|key|public|private]` parameters, using the format: `category/name`.
For example:
`secg/secp192r1` identifies the SECG 192 bit prime field curve known as `secp192r1`.
@@ -194,9 +200,10 @@ Get and print ECTester applet info from an applet installed on a card.
Outputs:
- ECTester applet version
- - ECTester APDU support
+ - ECTester APDU support (basic/extended APDU)
- JavaCard API version
- JavaCard cleanup support
+ - ECTester internal array sizes and APDU buffer size
### Example
@@ -298,58 +305,81 @@ To install, place them in `${java.home}/jre/lib/security/`.
### Options
```
-usage: ECTesterStandalone.jar [-V] [-h] [-C]
- [ (ecdh [-b <n>] [-nc <cat/id>] [-cn <name>] [-t <type>] [--key-type <algorithm>] [-n <amount>]) |
- (ecdsa [-b <n>] [-nc <cat/id>] [-cn <name>] [-t <type>] [-n <amount>] [-f <file>]) |
- (export [-b <n>] [-t <type>]) |
- (generate [-b <n>] [-nc <cat/id>] [-cn <name>] [-n <amount>] [-t <type>]) |
- (list-data [what]) |
- (list-libs) |
- (list-suites) |
- (test [-b <n>] [-nc <cat/id>] [-cn <name>] [-gt <type>] [-kt <type>] [-st <type>] [-f <format>] [--key-type <algorithm>]
- <test-suite>) ]
- [lib]
+usage: ECTesterStandalone.jar [-V] [-h <command>] [-C] [
+(ecdh [-b <n>] [-nc <cat/id>] [-cn <name>] [-o <output_file>] [-t <type>] [--key-type <algorithm>] [-n <amount>]
+ [-npriv <cat/id>] [--fixed-private] [-npub <cat/id>] [--fixed-public]) |
+(ecdsa [-b <n>] [-nc <cat/id>] [-cn <name>] [-o <output_file>] [-npriv <cat/id>] [-npub <cat/id>] [-t <type>]
+ [-n <amount>] [-f <file>]) |
+(export [-b <n>] [-o <output_file>] [-t <type>]) |
+(generate [-b <n>] [-nc <cat/id>] [-cn <name>] [-o <output_file>] [-n <amount>] [-t <type>]) |
+(list-data [what]) |
+(list-libs) |
+(list-suites) |
+(test [-b <n>] [-nc <cat/id>] [-cn <name>] [-gt <type>] [-kt <type>] [-st <type>] [-f <format>] [--key-type <algorithm>] <test-suite>) ]
+[lib]
- ecdh: | Perform EC based KeyAgreement. |
- -b,--bits <n> What size of curve to use.
- -nc,--named-curve <cat/id> Use a named curve, from CurveDB: <cat/id>
- -cn,--curve-name <name> Use a named curve, search from curves
- supported by the library: <name>
- -t,--type <type> Set KeyAgreement object [type].
- --key-type <algorithm> Set the key [algorithm] for which the key
- should be derived in KeyAgreements with
- KDF. Default is "AES".
- -n,--amount <amount> Do ECDH [amount] times.
+ -V,--version Print version info.
+ -h,--help <command> Print help(about <command>).
+ -C,--color Print stuff with color, requires ANSI terminal.
+ [lib] What library to use.
- ecdsa: | Perform EC based Signature. |
- -b,--bits <n> What size of curve to use.
- -nc,--named-curve <cat/id> Use a named curve, from CurveDB: <cat/id>
- -cn,--curve-name <name> Use a named curve, search from curves
- supported by the library: <name>
- -t,--type <type> Set Signature object [type].
- -n,--amount <amount> Do ECDSA [amount] times.
- -f,--file <file> Input [file] to sign.
+ ecdh: | Perform EC based KeyAgreement. |
+ -b,--bits <n> What size of curve to use.
+ -nc,--named-curve <cat/id> Use a named curve, from CurveDB:
+ <cat/id>
+ -cn,--curve-name <name> Use a named curve, search from curves
+ supported by the library: <name>
+ -o,--output <output_file> Output into file <output_file>.
+ -t,--type <type> Set KeyAgreement object [type].
+ --key-type <algorithm> Set the key [algorithm] for which the
+ key should be derived in
+ KeyAgreements with KDF. Default is
+ "AES".
+ -n,--amount <amount> Do ECDH [amount] times.
+ -npriv,--named-private <cat/id> Use a named private key, from
+ CurveDB: <cat/id>
+ --fixed-private Perform ECDH with fixed private key.
+ -npub,--named-public <cat/id> Use a named public key, from CurveDB:
+ <cat/id>
+ --fixed-public Perform ECDH with fixed public key.
+
+ ecdsa: | Perform EC based Signature. |
+ -b,--bits <n> What size of curve to use.
+ -nc,--named-curve <cat/id> Use a named curve, from CurveDB:
+ <cat/id>
+ -cn,--curve-name <name> Use a named curve, search from curves
+ supported by the library: <name>
+ -o,--output <output_file> Output into file <output_file>.
+ -npriv,--named-private <cat/id> Use a named private key, from
+ CurveDB: <cat/id>
+ -npub,--named-public <cat/id> Use a named public key, from CurveDB:
+ <cat/id>
+ -t,--type <type> Set Signature object [type].
+ -n,--amount <amount> Do ECDSA [amount] times.
+ -f,--file <file> Input [file] to sign.
- export: | Export default curve parameters. |
+ export: | Export default curve parameters. |
-b,--bits <n> What size of curve to use.
+ -o,--output <output_file> Output into file <output_file>.
-t,--type <type> Set KeyPair object [type].
- generate: | Generate EC keypairs. |
+ generate: | Generate EC keypairs. |
-b,--bits <n> What size of curve to use.
-nc,--named-curve <cat/id> Use a named curve, from CurveDB: <cat/id>
-cn,--curve-name <name> Use a named curve, search from curves
supported by the library: <name>
+ -o,--output <output_file> Output into file <output_file>.
-n,--amount <amount> Generate [amount] of EC keys.
-t,--type <type> Set KeyPairGenerator object [type].
- list-data: | List/show contained EC domain parameters/keys. |
- [what] what to list.
+ list-data: | List/show contained EC domain parameters/keys. |
+ [what] what to list.
- list-libs: | List supported libraries. |
+ list-libs: | List supported libraries. |
- list-suites: | List supported test suites. |
+ list-suites: | List supported test suites. |
- test: | Test a library. |
+ test: | Test a library. |
-b,--bits <n> What size of curve to use.
-nc,--named-curve <cat/id> Use a named curve, from CurveDB: <cat/id>
-cn,--curve-name <name> Use a named curve, search from curves
@@ -362,6 +392,6 @@ usage: ECTesterStandalone.jar [-V] [-h] [-C]
--key-type <algorithm> Set the key [algorithm] for which the key
should be derived in KeyAgreements with
KDF. Default is "AES".
- <test-suite> The test suite to run.
+ <test-suite> The test suite to run.
```
diff --git a/docs/FORMAT.md b/docs/FORMAT.md
index bde2543..16af130 100644
--- a/docs/FORMAT.md
+++ b/docs/FORMAT.md
@@ -1,6 +1,14 @@
# Format
ECTester mostly reads/outputs data in either human-readable format or using CSV.
+## Test runs
+By default test runs are output in a human readable format, however YAML and XML is also supported and can be selected
+by using the `--format` option. Also, prefixing the output file name when using the `-o/--output` option allows to output
+the same test run in different formats to different files.
+
+For example:
+`--format yaml -o default_output.yaml -o xml:output_file.xml -o text:readable_text_file.txt `
+
## Curves
Input files for the `-c/--curve` option should be in CSV, little-endian hexadecimal format.
Output of the `-e/--export` option will also be in this format.
@@ -42,22 +50,37 @@ Input files for the `-k/--key`, `-pub/--public` and `-priv/--private` options sh
## Key generation output(CSV)
Output of the `-g/--generate` option.
-`index;time;pubW;privS`
+For ECTesterReader this has the format:
+
+`index;genTime[milli];exportTime[milli];pubW;privS` where `pubW` is the public key used in ANSI X9.62 format,
+`privS` is the private key, `genTime` is the time required to generate the keypair and `exportTime` is the time required to export it (send it to the reader).
+
+For ECTesterStandalone:
+
+`index;time[nano];pubW;privS`
## KeyAgreement output(CSV)
Output of the `-dh/--ecdh` option.
-`index;time;pubW;privS;secret`
+For ECTesterReader this has the format:
+
+`index;time[milli];pubW;privS;secret` where `pubW` is the public key used in ANSI X9.62 format, `privS` is the private key
+and `secret` is the KeyAgreement result.
+
+For ECTesterStandalone this has the format: and the same meaning as for ECTesterReader.
+
+`index;time[nano];pubW;privS;secret` and the same meaning as for ECTesterReader.
## Signature output(CSV)
Output of the `-dsa/--ecdsa` option.
-`index;time;signature`
+For ECTesterReader this has the format:
-## Test runs
-By default test runs are output in a human readable format, however YAML and XML is also supported and can be selected
-by using the `--format` option. Also, prefixing the output file name when using the `-o/--output` option allows to output
-the same test run in different formats to different files.
+`index;signTime[milli];verifyTime[milli];data;pubW;privS;signature;nonce;valid` where `pubW` is the public key used
+in ANSI X9.62 format, `privS` is the private key, `signTime` and `verifyTime` are the durations of the sign and verify operations,
+`data` is the signed data (if available), `signature` is the produced signature, `nonce` is the `k` (nonce) value recovered from the signature
+abd the private key (if possible), `valid` denotes the verification result.
-For example:
-`--format yaml -o default_output.yaml -o xml:output_file.xml -o text:readable_text_file.txt `
+For ECTesterStandalone this has the format:
+
+ `index;signTime[nano];verifyTime[nano];data;pubW;privS;signature;nonce;verified` and the same meaning as for ECTesterReader. \ No newline at end of file
diff --git a/docs/IMPLEMENTATIONS.md b/docs/IMPLEMENTATIONS.md
index b4a4ea8..d333ed5 100644
--- a/docs/IMPLEMENTATIONS.md
+++ b/docs/IMPLEMENTATIONS.md
@@ -249,7 +249,7 @@ Uses binary addition chain.
INPUT: k = (k_{t-1}, ..., k_1, k_0)_2, P ∈ E(F_q).
OUTPUT: [k]P.
1. Q ← ∞.
- 2. For i from t - 1 downto 0 do
+ 2. For i from 0 to t-1 do
2.1 If k_i = 1 then Q ← Q + P.
2.2 P ← 2P.
3. Return(Q).
@@ -432,7 +432,7 @@ The same name, Montgomery ladder, is used both for the general ladder idea of ex
INPUT: k = (k_{t-1}, ..., k_1, k_0)_2, P ∈ E(F_q).
OUTPUT: [k]P .
1. P_1 ← P and P_2 ← [2]P
- 2. For i = t − 2 downto 0 do
+ 2. For i = t − 1 downto 0 do
2.1 If k_i = 0 then
P_1 ← [2]P_1; P_2 ← P_1 + P_2.
Else
@@ -443,13 +443,13 @@ The same name, Montgomery ladder, is used both for the general ladder idea of ex
INPUT: G ∈ E(F_q), k = (1, k_{t−2}, ..., k_0)2
OUTPUT: Y = kG
- R0 ← G; R1 ← [2]G
- for j = t − 2 downto 0 do
- if (k_j = 0) then
- R1 ← R0 + R1; R0 ← [2]R0
- else [if (kj = 1)]
- R0 ← R0 + R1; R1 ← [2]R1
- return R0
+ 1. R0 ← G; R1 ← [2]G
+ 2. for j = t − 2 downto 0 do
+ 2.1 if (k_j = 0) then
+ R1 ← R0 + R1; R0 ← [2]R0
+ else [if (kj = 1)]
+ R0 ← R0 + R1; R1 ← [2]R1
+ 3. return R0
Montgomery addition formulas (Projective coordinates/XZ coordinates):[^2]
@@ -536,17 +536,27 @@ y_n &= \frac{(x_n + x_1)((x_n + x_1)(x_{n+1} + x_1) + x_1^2 + y_1)}{x_1} + y_1
## References
[^1]: HANKERSON, Darrel; MENEZES, Alfred J.; VANSTONE, Scott. Guide to Elliptic Curve Cryptography. New York, USA: Springer, 2004. ISBN 9780387218465. Available from DOI: [10.1007/b97644](https://dx.doi.org/10.1007/b97644).
-[^2]: COHEN, Henri; FREY, Gerhard; AVANZI, Roberto M.; DOCHE, Christophe; LANGE,
-Tanja; NGUYEN, Kim; VERCAUTEREN, Frederik. Handbook of Elliptic and Hyper-
-elliptic Curve Cryptography. CRC Press, 2005-07-19. Discrete Mathematics and It’s Applications, no. 34. ISBN 9781584885184.
+
+[^2]: COHEN, Henri; FREY, Gerhard; AVANZI, Roberto M.; DOCHE, Christophe; LANGE, Tanja; NGUYEN, Kim; VERCAUTEREN, Frederik. Handbook of Elliptic and Hyper-elliptic Curve Cryptography. CRC Press, 2005-07-19. Discrete Mathematics and It’s Applications, no. 34. ISBN 9781584885184.
+
[^3]: BERNSTEIN, Daniel J.; LANGE, Tanja. Explicit Formulas Database, <https://www.hyperelliptic.org/EFD/>
+
[^4]: <http://point-at-infinity.org/ecc/>
+
[^5]: KNUTH, Donald: The Art of Computer Programming, Volume 2: Seminumerical algorithms
+
[^6]: GORDON, Daniel M.: A survey of fast exponentiation methods.
+
[^7]: MORAIN, Francois; OLIVOS, Jorge: Speeding up the computations on an elliptic curve using addition-subtraction chains.
+
[^8]: JOYE, Marc; YEN, Sung-Ming: The Montgomery Powering Ladder.
+
[^9]: MOLLER, Bodo: Securing Elliptic Curve Point Multiplication against Side-Channel Attacks.
+
[^10]: MOLLER, Bodo: Improved Techniques for Fast Exponentiation.
+
[^11]: MOLLER, Bodo: Fractional Windows Revisited: Improved Signed-Digit Representations for Efficient Exponentiation.
+
[^12]: KOYAMA, Kenji; TSURUOKA, Yukio: Speeding up Elliptic Cryptosystems by Using a Signed Binary Window Method.
+
[^13]: GALLANT, Robert P.; LAMBERT, Robert J.; VANSTONE, Scott A.: Faster point multiplication on elliptic curves with efficient endomorphisms. \ No newline at end of file
diff --git a/docs/TESTS.md b/docs/TESTS.md
index 25f61a8..5d32d9d 100644
--- a/docs/TESTS.md
+++ b/docs/TESTS.md
@@ -17,7 +17,7 @@
confirmation before running, be cautious.**
## Default
-Tests the default curves present on the card. These might not be present or the card might not even support ECC.
+Tests support for ECC and the presence of default curves on the target. These might not be present or the target might not even support ECC.
Tests keypair allocation, generation, ECDH and ECDSA. ECDH is first tested with two valid generated keypairs, then
with a compressed public key to test support for compressed points.
@@ -25,7 +25,7 @@ This test suite is run if no argument is provided to `-t / --test`.
## Test-Vectors
-Tests using known test vectors provided by NIST/SECG/Brainpool:
+Tests ECDH using known test vectors provided by NIST/SECG/Brainpool:
[SECG - GEC2](http://read.pudn.com/downloads168/doc/772358/TestVectorsforSEC%201-gec2.pdf)
@@ -37,8 +37,12 @@ Tests using known test vectors provided by NIST/SECG/Brainpool:
## Compression
-Tests support for compression of public points in ECDH as specified in ANSI X9.62. Tests ECDH with points in compressed
-and hybrid form. Also tests card response to a hybrid point with wrong `y` coordinate and to the point at infinity(as public key in ECDH).
+Tests support for compression of public points in ECDH as specified in ANSI X9.62. The standard specifies two forms of point compression,
+fully compressed point contains the `x` coordinate and one bit of the `y` coordinate, from which the whole point can be reconstructed, hybrid form
+of a compressed point contains both the `x` and `y` coordinates but also one bit of the `y` coordinate.
+
+Tests ECDH with points in compressed and hybrid form. Also tests target response to a hybrid point with wrong `y` coordinate and to the point at infinity(as public key in ECDH).
+Tests ECDH with invalid compressed point, where `x` does not lie on the curve.
- Compressed form, valid
- Hybrid form, valid
@@ -48,29 +52,35 @@ and hybrid form. Also tests card response to a hybrid point with wrong `y` coord
## Miscellaneous
-Some miscellaneous tests, tries ECDH and ECDSA over supersingular curves, anomalous curves and Barreto-Naehrig curves with small embedding degree and CM discriminant.
+Some miscellaneous tests, tries ECDH and ECDSA over super-singular curves, anomalous curves and Barreto-Naehrig curves with small embedding degree and CM discriminant.
+Also tests ECDH over MNT curves, M curves and Curve25519 transformed into short Weierstrass form.
## Signature
-Tests ECDSA verification, with invalid signatures.
+Tests ECDSA verification, with well-formed but invalid and malformed signatures.
- - Well-formed(DER) invalid signatures:
- - r = random, s = random
- - r = 0, s = random
- - r = random, s = 0
- - r = 1, s = random
- - r = random, s = 1
- - r = 0, s = 0
- - r = 0, s = 1
- - r = 1, s = 0
- - r = 1, s = 1
- - s = p
- - s = 2 * p
- - Invalid signatures:
- - Signature shorter than specified in ASN.1 SEQUENCE header.
- - Signature longer than specified in ASN.1 SEQUENCE header.
- - r shorter/longer than specified in its ASN.1 header.
- - s shorter/longer than specified in its ASN.1 header.
+- Well-formed(DER) invalid signatures:
+ - r = random, s = random
+ - r = 0, s = random
+ - r = random, s = 0
+ - r = 1, s = random
+ - r = random, s = 1
+ - r = 0, s = 0
+ - r = 0, s = 1
+ - r = 1, s = 0
+ - r = 1, s = 1
+ - r = random, s = p
+ - r = random, s = 2 * p
+- Invalid signatures:
+ - Signature shorter than specified in ASN.1 SEQUENCE header.
+ - Signature longer than specified in ASN.1 SEQUENCE header.
+ - r shorter/longer than specified in its ASN.1 header.
+ - s shorter/longer than specified in its ASN.1 header.
+ - ASN.1 SEQUENCE has indefinite length.
+ - ASN.1 SEQUENCE has length that will overflow a 16 bit integer.
+ - ASN.1 SEQUENCE has length that will overflow a 32 bit integer.
+ - ASN.1 SEQUENCE has length that will overflow a 64 bit integer.
+- Test verifying a valid signature, but with a negated public key.
## Wrong
@@ -91,9 +101,9 @@ This test suite also does some additional tests with corrupting the parameters:
- G = infinity
- r = 0
- r = 1
- - r = some prime larger than original r (and \[r\]G != infinity)
- - r = some prime smaller than original r (and \[r\]G != infninity)
- - r = some composite number (and \[r\]G != infinity)
+ - r = some prime larger than original r (and [r]G != infinity)
+ - r = some prime smaller than original r (and [r]G != infninity)
+ - r = some composite number (and [r]G != infinity)
- k = 0xff
- k = 0
@@ -104,7 +114,7 @@ This test suite also does some additional tests with corrupting the parameters:
## 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
+These tests should generally fail, a success here implies the target will use a non-secure curve if such curve is set
by the applet. Operations over such curves are susceptible to small-subgroup attacks.
- r = quite a smooth number, many small factors, r = \|G\|
@@ -113,37 +123,37 @@ by the applet. Operations over such curves are susceptible to small-subgroup att
This is performed over a 160 bit field size, in two passes:
- First pass tests the full range from 2 bits to 152, with more frequent tests towards the beginning and end.
- The second pass tests the range 140 - 158 bits with one bit steps.
-
+
- r = p * q = \|G\|
- r = G = Carmichael number = p * q * s
- - \[r\]G = infinity but r != \|G\|, so \|G\| divides r
+ - [r]G = infinity but r != \|G\|, so \|G\| divides r
## Invalid
Tests using known named curves from several categories(SECG/NIST/Brainpool) against pre-generated *invalid* public keys.
-ECDH should definitely fail, a success here implies the card is susceptible to invalid curve attacks.
+ECDH should definitely fail, a success here implies the target is susceptible to invalid curve attacks.
See [Practical Invalid Curve Attacks on TLS-ECDH](https://www.nds.rub.de/media/nds/veroeffentlichungen/2015/09/14/main-full.pdf) for more information.
## Twist
Tests using known named curves froms several categories(SECG/NIST) against pre-generated points on twists of said curves.
-ECDH should fail, a success here implies the card is not twist secure, if a curve with an unsecure twist is used,
-the card might compute on the twist, if a point on the twist is supplied.
+ECDH should fail, a success here implies the target is not twist secure, if a curve with an unsecure twist is used,
+the target might compute on the twist, if a point on the twist is supplied.
See [SafeCurves on twist security](https://safecurves.cr.yp.to/twist.html) for more information.
## Degenerate
Tests using known named curves froms several categories(SECG/NIST) against pre-generated points on the degenerate line
-`Y: x = 0`. ECDH should fail, a success here might mean the card does not check that the point lies on the correct curve
+`Y: x = 0`. ECDH should fail, a success here might mean the target does not check that the point lies on the correct curve
and uses a curve model vulnerable to such degenerate points.
See [Degenerate Curve Attacks - Extending Invalid Curve Attacks to Edwards Curves and Other Models](https://eprint.iacr.org/2015/1233.pdf) for more information.
## Cofactor
-Tests whether the card correctly rejects points that lie on the curve but not on the subgroup generated by the specified generator
+Tests whether the target correctly rejects points that lie on the curve but not on the subgroup generated by the specified generator
during ECDH. Does this with curves where the cofactor subgroup has small order, then with curves that have order equal to the product
of two large primes, sets the generator with order of one prime and tries points on the subgroup of the other prime order.
@@ -166,6 +176,7 @@ Custom edge-case private key values over SECG curves are tested:
- s < r, s = r, s > r
- s = r - 1, s = r + 1
- s = k\*r - 1, s = k\*r, s = k\*r + 1
+ - s = 111111...1111, s = 101010...1010, s = 010101...0101
- s around r (s < r, on a curve where \|r\| > \|p\|)
- s around p (on a curve where where \|r\| > \|p\|)
- s around 0 (s > 0, on a curve where \|r\| > \|p\|)
diff --git a/src/cz/crcs/ectester/applet/AppletBase.java b/src/cz/crcs/ectester/applet/AppletBase.java
index 48e7785..c77294e 100644
--- a/src/cz/crcs/ectester/applet/AppletBase.java
+++ b/src/cz/crcs/ectester/applet/AppletBase.java
@@ -24,6 +24,7 @@ public abstract class AppletBase extends Applet {
public static final byte INS_ALLOCATE_KA = (byte) 0x76;
public static final byte INS_ALLOCATE_SIG = (byte) 0x77;
public static final byte INS_GET_INFO = (byte) 0x78;
+ public static final byte INS_SET_DRY_RUN_MODE = (byte) 0x79;
// PARAMETERS for P1 and P2
public static final byte KEYPAIR_LOCAL = (byte) 0x01;
@@ -31,6 +32,8 @@ public abstract class AppletBase extends Applet {
public static final byte KEYPAIR_BOTH = KEYPAIR_LOCAL | KEYPAIR_REMOTE;
public static final byte EXPORT_TRUE = (byte) 0xff;
public static final byte EXPORT_FALSE = (byte) 0x00;
+ public static final byte MODE_NORMAL = (byte) 0xaa;
+ public static final byte MODE_DRY_RUN = (byte) 0xbb;
// STATUS WORDS
public static final short SW_SIG_VERIFY_FAIL = (short) 0x0ee1;
@@ -159,6 +162,9 @@ public abstract class AppletBase extends Applet {
case INS_GET_INFO:
length = insGetInfo(apdu);
break;
+ case INS_SET_DRY_RUN_MODE:
+ length = insSetDryRunMode(apdu);
+ break;
default:
// The INS code is not supported by the dispatcher
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
@@ -545,6 +551,26 @@ public abstract class AppletBase extends Applet {
}
/**
+ * Set the dry run mode of the applet.
+ *
+ * @param apdu P1 = byte mode (MODE_* || ...)
+ * @return length of response
+ */
+ private short insSetDryRunMode(APDU apdu) {
+ byte[] apdubuf = apdu.getBuffer();
+ byte mode = apduArray[ISO7816.OFFSET_P1];
+
+ short len = 0;
+ if (mode == MODE_NORMAL) {
+ len = setDryRunMode(apdubuf, false, (short) 0);
+ }
+ if (mode == MODE_DRY_RUN) {
+ len = setDryRunMode(apdubuf, true, (short) 0);
+ }
+ return len;
+ }
+
+ /**
* @param keyPair which keyPair to use, local/remote (KEYPAIR_* | ...)
* @param keyLength key length to set
* @param keyClass key class to allocate
@@ -883,4 +909,15 @@ public abstract class AppletBase extends Applet {
length += 2;
return length;
}
+
+ private short setDryRunMode(byte[] buffer, boolean mode, short offset) {
+ if (keyTester != null) {
+ keyTester.setDryRun(mode);
+ }
+ if (keyGenerator != null) {
+ keyGenerator.setDryRun(mode);
+ }
+ Util.setShort(buffer, offset, ISO7816.SW_NO_ERROR);
+ return 2;
+ }
}
diff --git a/src/cz/crcs/ectester/applet/ECKeyGenerator.java b/src/cz/crcs/ectester/applet/ECKeyGenerator.java
index 4326752..30910ca 100644
--- a/src/cz/crcs/ectester/applet/ECKeyGenerator.java
+++ b/src/cz/crcs/ectester/applet/ECKeyGenerator.java
@@ -14,6 +14,7 @@ import javacard.security.KeyPair;
public class ECKeyGenerator {
private short sw = ISO7816.SW_NO_ERROR;
+ private boolean dryRun = false;
/**
* @param keyClass
@@ -24,12 +25,14 @@ public class ECKeyGenerator {
sw = ISO7816.SW_NO_ERROR;
KeyPair ecKeyPair = null;
try {
- ecKeyPair = new KeyPair(keyClass, keyLength);
+ if (!dryRun) {
+ ecKeyPair = new KeyPair(keyClass, keyLength);
- if (ecKeyPair.getPublic() == null || ecKeyPair.getPrivate() == null) {
- try {
- ecKeyPair.genKeyPair();
- } catch (Exception ignored) {
+ if (ecKeyPair.getPublic() == null || ecKeyPair.getPrivate() == null) {
+ try {
+ ecKeyPair.genKeyPair();
+ } catch (Exception ignored) {
+ }
}
}
} catch (CardRuntimeException ce) {
@@ -46,8 +49,10 @@ public class ECKeyGenerator {
public short clearPair(KeyPair keypair, byte key) {
try {
sw = AppletUtil.keypairCheck(keypair);
- if ((key & EC_Consts.KEY_PUBLIC) != 0) keypair.getPublic().clearKey();
- if ((key & EC_Consts.KEY_PRIVATE) != 0) keypair.getPrivate().clearKey();
+ if (!dryRun) {
+ if ((key & EC_Consts.KEY_PUBLIC) != 0) keypair.getPublic().clearKey();
+ if ((key & EC_Consts.KEY_PRIVATE) != 0) keypair.getPrivate().clearKey();
+ }
} catch (CardRuntimeException ce) {
sw = ce.getReason();
}
@@ -61,7 +66,9 @@ public class ECKeyGenerator {
public short generatePair(KeyPair keypair) {
try {
sw = AppletUtil.keypairCheck(keypair);
- keypair.genKeyPair();
+ if (!dryRun) {
+ keypair.genKeyPair();
+ }
} catch (CardRuntimeException ce) {
sw = ce.getReason();
}
@@ -187,25 +194,35 @@ public class ECKeyGenerator {
try {
sw = AppletUtil.keypairCheck(keypair);
- ECPublicKey ecPublicKey = (ECPublicKey) keypair.getPublic();
- ECPrivateKey ecPrivateKey = (ECPrivateKey) keypair.getPrivate();
+ ECPublicKey ecPublicKey = null;
+ ECPrivateKey ecPrivateKey = null;
+ if (!dryRun) {
+ ecPublicKey = (ECPublicKey) keypair.getPublic();
+ ecPrivateKey = (ECPrivateKey) keypair.getPrivate();
+ }
switch (param) {
case EC_Consts.PARAMETER_FP:
- if ((key & EC_Consts.KEY_PUBLIC) != 0) ecPublicKey.setFieldFP(data, offset, length);
- if ((key & EC_Consts.KEY_PRIVATE) != 0) ecPrivateKey.setFieldFP(data, offset, length);
+ if (!dryRun) {
+ if ((key & EC_Consts.KEY_PUBLIC) != 0) ecPublicKey.setFieldFP(data, offset, length);
+ if ((key & EC_Consts.KEY_PRIVATE) != 0) ecPrivateKey.setFieldFP(data, offset, length);
+ }
break;
case EC_Consts.PARAMETER_F2M:
if (length == 4) {
short i = Util.getShort(data, (short) (offset + 2));
- if ((key & EC_Consts.KEY_PUBLIC) != 0) ecPublicKey.setFieldF2M(i);
- if ((key & EC_Consts.KEY_PRIVATE) != 0) ecPrivateKey.setFieldF2M(i);
+ if (!dryRun) {
+ if ((key & EC_Consts.KEY_PUBLIC) != 0) ecPublicKey.setFieldF2M(i);
+ if ((key & EC_Consts.KEY_PRIVATE) != 0) ecPrivateKey.setFieldF2M(i);
+ }
} else if (length == 8) {
short i1 = Util.getShort(data, (short) (offset + 2));
short i2 = Util.getShort(data, (short) (offset + 4));
short i3 = Util.getShort(data, (short) (offset + 6));
- if ((key & EC_Consts.KEY_PUBLIC) != 0) ecPublicKey.setFieldF2M(i1, i2, i3);
- if ((key & EC_Consts.KEY_PRIVATE) != 0) ecPrivateKey.setFieldF2M(i1, i2, i3);
+ if (!dryRun) {
+ if ((key & EC_Consts.KEY_PUBLIC) != 0) ecPublicKey.setFieldF2M(i1, i2, i3);
+ if ((key & EC_Consts.KEY_PRIVATE) != 0) ecPrivateKey.setFieldF2M(i1, i2, i3);
+ }
// if ((key & EC_Consts.KEY_PUBLIC) != 0) ecPublicKey.setFieldF2M(i3, i2, i1);
// if ((key & EC_Consts.KEY_PRIVATE) != 0) ecPrivateKey.setFieldF2M(i3, i2, i1);
} else {
@@ -213,20 +230,28 @@ public class ECKeyGenerator {
}
break;
case EC_Consts.PARAMETER_A:
- if ((key & EC_Consts.KEY_PUBLIC) != 0) ecPublicKey.setA(data, offset, length);
- if ((key & EC_Consts.KEY_PRIVATE) != 0) ecPrivateKey.setA(data, offset, length);
+ if (!dryRun) {
+ if ((key & EC_Consts.KEY_PUBLIC) != 0) ecPublicKey.setA(data, offset, length);
+ if ((key & EC_Consts.KEY_PRIVATE) != 0) ecPrivateKey.setA(data, offset, length);
+ }
break;
case EC_Consts.PARAMETER_B:
- if ((key & EC_Consts.KEY_PUBLIC) != 0) ecPublicKey.setB(data, offset, length);
- if ((key & EC_Consts.KEY_PRIVATE) != 0) ecPrivateKey.setB(data, offset, length);
+ if (!dryRun) {
+ if ((key & EC_Consts.KEY_PUBLIC) != 0) ecPublicKey.setB(data, offset, length);
+ if ((key & EC_Consts.KEY_PRIVATE) != 0) ecPrivateKey.setB(data, offset, length);
+ }
break;
case EC_Consts.PARAMETER_G:
- if ((key & EC_Consts.KEY_PUBLIC) != 0) ecPublicKey.setG(data, offset, length);
- if ((key & EC_Consts.KEY_PRIVATE) != 0) ecPrivateKey.setG(data, offset, length);
+ if (!dryRun) {
+ if ((key & EC_Consts.KEY_PUBLIC) != 0) ecPublicKey.setG(data, offset, length);
+ if ((key & EC_Consts.KEY_PRIVATE) != 0) ecPrivateKey.setG(data, offset, length);
+ }
break;
case EC_Consts.PARAMETER_R:
- if ((key & EC_Consts.KEY_PUBLIC) != 0) ecPublicKey.setR(data, offset, length);
- if ((key & EC_Consts.KEY_PRIVATE) != 0) ecPrivateKey.setR(data, offset, length);
+ if (!dryRun) {
+ if ((key & EC_Consts.KEY_PUBLIC) != 0) ecPublicKey.setR(data, offset, length);
+ if ((key & EC_Consts.KEY_PRIVATE) != 0) ecPrivateKey.setR(data, offset, length);
+ }
break;
case EC_Consts.PARAMETER_K:
short k = 0;
@@ -238,14 +263,20 @@ public class ECKeyGenerator {
} else if (length == 1) {
k = data[offset];
}
- if ((key & EC_Consts.KEY_PUBLIC) != 0) ecPublicKey.setK(k);
- if ((key & EC_Consts.KEY_PRIVATE) != 0) ecPrivateKey.setK(k);
+ if (!dryRun) {
+ if ((key & EC_Consts.KEY_PUBLIC) != 0) ecPublicKey.setK(k);
+ if ((key & EC_Consts.KEY_PRIVATE) != 0) ecPrivateKey.setK(k);
+ }
break;
case EC_Consts.PARAMETER_S:
- if ((key & EC_Consts.KEY_PRIVATE) != 0) ecPrivateKey.setS(data, offset, length);
+ if (!dryRun) {
+ if ((key & EC_Consts.KEY_PRIVATE) != 0) ecPrivateKey.setS(data, offset, length);
+ }
break;
case EC_Consts.PARAMETER_W:
- if ((key & EC_Consts.KEY_PUBLIC) != 0) ecPublicKey.setW(data, offset, length);
+ if (!dryRun) {
+ if ((key & EC_Consts.KEY_PUBLIC) != 0) ecPublicKey.setW(data, offset, length);
+ }
break;
default:
ISOException.throwIt(ISO7816.SW_FUNC_NOT_SUPPORTED);
@@ -310,54 +341,75 @@ public class ECKeyGenerator {
short length = 0;
try {
sw = AppletUtil.keypairCheck(keypair);
- ECPublicKey ecPublicKey = (ECPublicKey) keypair.getPublic();
- ECPrivateKey ecPrivateKey = (ECPrivateKey) keypair.getPrivate();
+
+ ECPublicKey ecPublicKey = null;
+ ECPrivateKey ecPrivateKey = null;
+ if (!dryRun) {
+ ecPublicKey = (ECPublicKey) keypair.getPublic();
+ ecPrivateKey = (ECPrivateKey) keypair.getPrivate();
+ }
switch (param) {
case EC_Consts.PARAMETER_FP:
- if ((key & EC_Consts.KEY_PUBLIC) != 0) length = ecPublicKey.getField(outputBuffer, outputOffset);
- if ((key & EC_Consts.KEY_PRIVATE) != 0) length = ecPrivateKey.getField(outputBuffer, outputOffset);
+ if (!dryRun) {
+ if ((key & EC_Consts.KEY_PUBLIC) != 0)
+ length = ecPublicKey.getField(outputBuffer, outputOffset);
+ if ((key & EC_Consts.KEY_PRIVATE) != 0)
+ length = ecPrivateKey.getField(outputBuffer, outputOffset);
+ }
break;
case EC_Consts.PARAMETER_F2M:
- if ((key & EC_Consts.KEY_PUBLIC) != 0) {
+ if ((key & EC_Consts.KEY_PUBLIC) != 0 && !dryRun) {
Util.setShort(outputBuffer, outputOffset, ecPublicKey.getSize());
length = 2;
length += ecPublicKey.getField(outputBuffer, (short) (outputOffset + 2));
}
- if ((key & EC_Consts.KEY_PRIVATE) != 0) {
+ if ((key & EC_Consts.KEY_PRIVATE) != 0 && !dryRun) {
Util.setShort(outputBuffer, outputOffset, ecPrivateKey.getSize());
length = 2;
length += ecPrivateKey.getField(outputBuffer, (short) (outputOffset + 2));
}
break;
case EC_Consts.PARAMETER_A:
- if ((key & EC_Consts.KEY_PUBLIC) != 0) length = ecPublicKey.getA(outputBuffer, outputOffset);
- if ((key & EC_Consts.KEY_PRIVATE) != 0) length = ecPrivateKey.getA(outputBuffer, outputOffset);
+ if (!dryRun) {
+ if ((key & EC_Consts.KEY_PUBLIC) != 0) length = ecPublicKey.getA(outputBuffer, outputOffset);
+ if ((key & EC_Consts.KEY_PRIVATE) != 0) length = ecPrivateKey.getA(outputBuffer, outputOffset);
+ }
break;
case EC_Consts.PARAMETER_B:
- if ((key & EC_Consts.KEY_PUBLIC) != 0) length = ecPublicKey.getB(outputBuffer, outputOffset);
- if ((key & EC_Consts.KEY_PRIVATE) != 0) length = ecPrivateKey.getB(outputBuffer, outputOffset);
+ if (!dryRun) {
+ if ((key & EC_Consts.KEY_PUBLIC) != 0) length = ecPublicKey.getB(outputBuffer, outputOffset);
+ if ((key & EC_Consts.KEY_PRIVATE) != 0) length = ecPrivateKey.getB(outputBuffer, outputOffset);
+ }
break;
case EC_Consts.PARAMETER_G:
- if ((key & EC_Consts.KEY_PUBLIC) != 0) length = ecPublicKey.getG(outputBuffer, outputOffset);
- if ((key & EC_Consts.KEY_PRIVATE) != 0) length = ecPrivateKey.getG(outputBuffer, outputOffset);
+ if (!dryRun) {
+ if ((key & EC_Consts.KEY_PUBLIC) != 0) length = ecPublicKey.getG(outputBuffer, outputOffset);
+ if ((key & EC_Consts.KEY_PRIVATE) != 0) length = ecPrivateKey.getG(outputBuffer, outputOffset);
+ }
break;
case EC_Consts.PARAMETER_R:
- if ((key & EC_Consts.KEY_PUBLIC) != 0) length = ecPublicKey.getR(outputBuffer, outputOffset);
- if ((key & EC_Consts.KEY_PRIVATE) != 0) length = ecPrivateKey.getR(outputBuffer, outputOffset);
+ if (!dryRun) {
+ if ((key & EC_Consts.KEY_PUBLIC) != 0) length = ecPublicKey.getR(outputBuffer, outputOffset);
+ if ((key & EC_Consts.KEY_PRIVATE) != 0) length = ecPrivateKey.getR(outputBuffer, outputOffset);
+ }
break;
case EC_Consts.PARAMETER_K:
- length = 2;
- if ((key & EC_Consts.KEY_PUBLIC) != 0)
- Util.setShort(outputBuffer, outputOffset, ecPublicKey.getK());
- if ((key & EC_Consts.KEY_PRIVATE) != 0)
- Util.setShort(outputBuffer, outputOffset, ecPrivateKey.getK());
+ if (!dryRun) {
+ length = 2;
+ if ((key & EC_Consts.KEY_PUBLIC) != 0)
+ Util.setShort(outputBuffer, outputOffset, ecPublicKey.getK());
+ if ((key & EC_Consts.KEY_PRIVATE) != 0)
+ Util.setShort(outputBuffer, outputOffset, ecPrivateKey.getK());
+ }
break;
case EC_Consts.PARAMETER_W:
- if ((key & EC_Consts.KEY_PUBLIC) != 0) length = ecPublicKey.getW(outputBuffer, outputOffset);
+ if ((key & EC_Consts.KEY_PUBLIC) != 0 && !dryRun)
+ length = ecPublicKey.getW(outputBuffer, outputOffset);
break;
case EC_Consts.PARAMETER_S:
- if ((key & EC_Consts.KEY_PRIVATE) != 0) length = ecPrivateKey.getS(outputBuffer, outputOffset);
+ if ((key & EC_Consts.KEY_PRIVATE) != 0 && !dryRun)
+ length = ecPrivateKey.getS(outputBuffer, outputOffset);
break;
default:
ISOException.throwIt(ISO7816.SW_FUNC_NOT_SUPPORTED);
@@ -439,4 +491,8 @@ public class ECKeyGenerator {
public short getSW() {
return sw;
}
+
+ public void setDryRun(boolean dryRun) {
+ this.dryRun = dryRun;
+ }
}
diff --git a/src/cz/crcs/ectester/applet/ECKeyTester.java b/src/cz/crcs/ectester/applet/ECKeyTester.java
index 0e46971..89590d0 100644
--- a/src/cz/crcs/ectester/applet/ECKeyTester.java
+++ b/src/cz/crcs/ectester/applet/ECKeyTester.java
@@ -18,12 +18,15 @@ public class ECKeyTester {
private short sigType = 0;
private short sw = ISO7816.SW_NO_ERROR;
+ private boolean dryRun = false;
public short allocateKA(byte algorithm) {
sw = ISO7816.SW_NO_ERROR;
try {
- ecKeyAgreement = KeyAgreement.getInstance(algorithm, false);
- kaType = algorithm;
+ if (!dryRun) {
+ ecKeyAgreement = KeyAgreement.getInstance(algorithm, false);
+ kaType = algorithm;
+ }
} catch (CardRuntimeException ce) {
sw = ce.getReason();
}
@@ -33,8 +36,10 @@ public class ECKeyTester {
public short allocateSig(byte algorithm) {
sw = ISO7816.SW_NO_ERROR;
try {
- ecdsaSignature = Signature.getInstance(algorithm, false);
- sigType = algorithm;
+ if (!dryRun) {
+ ecdsaSignature = Signature.getInstance(algorithm, false);
+ sigType = algorithm;
+ }
} catch (CardRuntimeException ce) {
sw = ce.getReason();
}
@@ -61,11 +66,13 @@ public class ECKeyTester {
sw = AppletUtil.kaCheck(ecKeyAgreement);
sw = AppletUtil.keypairCheck(privatePair);
sw = AppletUtil.keypairCheck(publicPair);
- short pubkeyLength = ((ECPublicKey) publicPair.getPublic()).getW(pubkeyBuffer, pubkeyOffset);
- ecKeyAgreement.init(privatePair.getPrivate());
+ if (!dryRun) {
+ short pubkeyLength = ((ECPublicKey) publicPair.getPublic()).getW(pubkeyBuffer, pubkeyOffset);
+ ecKeyAgreement.init(privatePair.getPrivate());
- pubkeyLength = EC_Consts.transformParameter(transformation, pubkeyBuffer, pubkeyOffset, pubkeyLength);
- length = ecKeyAgreement.generateSecret(pubkeyBuffer, pubkeyOffset, pubkeyLength, outputBuffer, outputOffset);
+ pubkeyLength = EC_Consts.transformParameter(transformation, pubkeyBuffer, pubkeyOffset, pubkeyLength);
+ length = ecKeyAgreement.generateSecret(pubkeyBuffer, pubkeyOffset, pubkeyLength, outputBuffer, outputOffset);
+ }
} catch (CardRuntimeException ce) {
sw = ce.getReason();
}
@@ -88,9 +95,11 @@ public class ECKeyTester {
sw = AppletUtil.kaCheck(ecKeyAgreement);
sw = AppletUtil.keypairCheck(privatePair);
- ecKeyAgreement.init(privatePair.getPrivate());
- pubkeyLength = EC_Consts.transformParameter(transformation, pubkey, pubkeyOffset, pubkeyLength);
- length = ecKeyAgreement.generateSecret(pubkey, pubkeyOffset, pubkeyLength, outpuBuffer, outputOffset);
+ if (!dryRun) {
+ ecKeyAgreement.init(privatePair.getPrivate());
+ pubkeyLength = EC_Consts.transformParameter(transformation, pubkey, pubkeyOffset, pubkeyLength);
+ length = ecKeyAgreement.generateSecret(pubkey, pubkeyOffset, pubkeyLength, outpuBuffer, outputOffset);
+ }
} catch (CardRuntimeException ce) {
sw = ce.getReason();
}
@@ -116,13 +125,14 @@ public class ECKeyTester {
try {
sw = AppletUtil.signCheck(ecdsaSignature);
- ecdsaSignature.init(signKey, Signature.MODE_SIGN);
- length = ecdsaSignature.sign(inputBuffer, inputOffset, inputLength, sigBuffer, sigOffset);
+ if (!dryRun) {
+ ecdsaSignature.init(signKey, Signature.MODE_SIGN);
+ length = ecdsaSignature.sign(inputBuffer, inputOffset, inputLength, sigBuffer, sigOffset);
- ecdsaSignature.init(verifyKey, Signature.MODE_VERIFY);
- boolean correct = ecdsaSignature.verify(inputBuffer, inputOffset, inputLength, sigBuffer, sigOffset, length);
- if (!correct) {
- sw = AppletBase.SW_SIG_VERIFY_FAIL;
+ ecdsaSignature.init(verifyKey, Signature.MODE_VERIFY);
+ if (!ecdsaSignature.verify(inputBuffer, inputOffset, inputLength, sigBuffer, sigOffset, length)) {
+ sw = AppletBase.SW_SIG_VERIFY_FAIL;
+ }
}
} catch (CardRuntimeException ce) {
sw = ce.getReason();
@@ -144,8 +154,10 @@ public class ECKeyTester {
try {
sw = AppletUtil.signCheck(ecdsaSignature);
- ecdsaSignature.init(signKey, Signature.MODE_SIGN);
- length = ecdsaSignature.sign(inputBuffer, inputOffset, inputLength, sigBuffer, sigOffset);
+ if (!dryRun) {
+ ecdsaSignature.init(signKey, Signature.MODE_SIGN);
+ length = ecdsaSignature.sign(inputBuffer, inputOffset, inputLength, sigBuffer, sigOffset);
+ }
} catch (CardRuntimeException ce) {
sw = ce.getReason();
}
@@ -167,10 +179,11 @@ public class ECKeyTester {
try {
sw = AppletUtil.signCheck(ecdsaSignature);
- ecdsaSignature.init(verifyKey, Signature.MODE_VERIFY);
- boolean correct = ecdsaSignature.verify(inputBuffer, inputOffset, inputLength, sigBuffer, sigOffset, sigLength);
- if (!correct) {
- sw = AppletBase.SW_SIG_VERIFY_FAIL;
+ if (!dryRun) {
+ ecdsaSignature.init(verifyKey, Signature.MODE_VERIFY);
+ if (!ecdsaSignature.verify(inputBuffer, inputOffset, inputLength, sigBuffer, sigOffset, sigLength)) {
+ sw = AppletBase.SW_SIG_VERIFY_FAIL;
+ }
}
} catch (CardRuntimeException ce) {
sw = ce.getReason();
@@ -205,4 +218,8 @@ public class ECKeyTester {
public short getSW() {
return sw;
}
+
+ public void setDryRun(boolean dryRun) {
+ this.dryRun = dryRun;
+ }
}
diff --git a/src/cz/crcs/ectester/common/ec/EC_Category.java b/src/cz/crcs/ectester/common/ec/EC_Category.java
index 05c910c..1eb818f 100644
--- a/src/cz/crcs/ectester/common/ec/EC_Category.java
+++ b/src/cz/crcs/ectester/common/ec/EC_Category.java
@@ -92,7 +92,7 @@ public class EC_Category {
}
String[] headers = new String[]{"Public keys", "Private keys", "KeyPairs", "Results(KA)", "Results(SIG)"};
- Class[] classes = new Class[]{EC_Key.Public.class, EC_Key.Private.class, EC_Keypair.class, EC_KAResult.class, EC_SigResult.class};
+ Class<EC_Data>[] classes = new Class[]{EC_Key.Public.class, EC_Key.Private.class, EC_Keypair.class, EC_KAResult.class, EC_SigResult.class};
for (int i = 0; i < headers.length; ++i) {
Map<String, EC_Data> data = getObjects(classes[i]);
size = data.size();
diff --git a/src/cz/crcs/ectester/common/output/BaseTextTestWriter.java b/src/cz/crcs/ectester/common/output/BaseTextTestWriter.java
index 85b32a4..8ad50c7 100644
--- a/src/cz/crcs/ectester/common/output/BaseTextTestWriter.java
+++ b/src/cz/crcs/ectester/common/output/BaseTextTestWriter.java
@@ -56,7 +56,7 @@ public abstract class BaseTextTestWriter implements TestWriter {
String line = "";
if (prefix.equals("")) {
- char charLine[] = new char[BASE_WIDTH + 24];
+ char[] charLine = new char[BASE_WIDTH + 24];
new String(new char[BASE_WIDTH + 24]).replace("\0", "━").getChars(0, charLine.length - 1, charLine, 0);
charLine[0] = '■';
charLine[4] = '┳';
@@ -70,7 +70,7 @@ public abstract class BaseTextTestWriter implements TestWriter {
out.append(t.ok() ? Colors.ok(" OK ") : Colors.error("NOK "));
out.append(compound ? (prefix.equals("") ? "╋ " : "┳ ") : "━ ");
int width = BASE_WIDTH - (prefix.length() + 6);
- String widthSpec = "%-" + String.valueOf(width) + "s";
+ String widthSpec = "%-" + width + "s";
String desc = ((prefix.equals("")) ? "(" + index + ") " : "") + t.getDescription();
out.append(String.format(widthSpec, desc));
out.append(" ┃ ");
@@ -87,7 +87,7 @@ public abstract class BaseTextTestWriter implements TestWriter {
if (compound) {
CompoundTest test = (CompoundTest) t;
- out.append(String.valueOf(result.getCause()));
+ out.append(result.getCause());
out.append(System.lineSeparator());
Test[] tests = test.getStartedTests();
for (int i = 0; i < tests.length; ++i) {
diff --git a/src/cz/crcs/ectester/common/util/CardUtil.java b/src/cz/crcs/ectester/common/util/CardUtil.java
index 7483c32..a761949 100644
--- a/src/cz/crcs/ectester/common/util/CardUtil.java
+++ b/src/cz/crcs/ectester/common/util/CardUtil.java
@@ -31,6 +31,23 @@ public class CardUtil {
}
}
+ public static String getSigHashAlgo(byte sigType) {
+ switch (sigType) {
+ case EC_Consts.Signature_ALG_ECDSA_SHA:
+ return "SHA-1";
+ case EC_Consts.Signature_ALG_ECDSA_SHA_224:
+ return "SHA-224";
+ case EC_Consts.Signature_ALG_ECDSA_SHA_256:
+ return "SHA-256";
+ case EC_Consts.Signature_ALG_ECDSA_SHA_384:
+ return "SHA-384";
+ case EC_Consts.Signature_ALG_ECDSA_SHA_512:
+ return "SHA-512";
+ default:
+ return null;
+ }
+ }
+
public static byte getKA(String name) {
switch (name) {
case "DH":
diff --git a/src/cz/crcs/ectester/common/util/ECUtil.java b/src/cz/crcs/ectester/common/util/ECUtil.java
index 1706ca0..511f93f 100644
--- a/src/cz/crcs/ectester/common/util/ECUtil.java
+++ b/src/cz/crcs/ectester/common/util/ECUtil.java
@@ -3,9 +3,18 @@ package cz.crcs.ectester.common.util;
import cz.crcs.ectester.applet.EC_Consts;
import cz.crcs.ectester.common.ec.*;
import cz.crcs.ectester.data.EC_Store;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1StreamParser;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERSequenceParser;
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import java.io.IOException;
import java.math.BigInteger;
+import java.nio.charset.StandardCharsets;
import java.security.KeyPair;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.spec.*;
@@ -183,6 +192,27 @@ public class ECUtil {
}
}
+ public static byte[] semiRandomKey(EC_Curve curve) {
+ int bytes = (curve.getBits() + 7) / 8;
+ byte[] result = new byte[bytes];
+ SHA1Digest digest = new SHA1Digest();
+ byte[] curveName = curve.getId().getBytes(StandardCharsets.US_ASCII);
+ digest.update(curveName, 0, curveName.length);
+ int written = 0;
+ while (written < bytes) {
+ byte[] dig = new byte[digest.getDigestSize()];
+ digest.doFinal(dig, 0);
+ int toWrite = digest.getDigestSize() > bytes - written ? bytes - written : digest.getDigestSize();
+ System.arraycopy(dig, 0, result, written, toWrite);
+ written += toWrite;
+ digest.update(dig, 0, dig.length);
+ }
+ BigInteger priv = new BigInteger(1, result);
+ BigInteger order = new BigInteger(1, curve.getParam(EC_Consts.PARAMETER_R)[0]);
+ priv = priv.mod(order);
+ return toByteArray(priv, curve.getBits());
+ }
+
private static ECPoint toPoint(EC_Params params) {
return new ECPoint(
new BigInteger(1, params.getParam(EC_Consts.PARAMETER_W)[0]),
@@ -194,25 +224,34 @@ public class ECUtil {
}
public static ECPublicKey toPublicKey(EC_Key.Public pubkey) {
+ if (pubkey == null) {
+ return null;
+ }
EC_Curve curve = EC_Store.getInstance().getObject(EC_Curve.class, pubkey.getCurve());
if (curve == null) {
- throw new IllegalArgumentException("pubkey curve nor found: " + pubkey.getCurve());
+ throw new IllegalArgumentException("pubkey curve not found: " + pubkey.getCurve());
}
return new RawECPublicKey(toPoint(pubkey), curve.toSpec());
}
public static ECPrivateKey toPrivateKey(EC_Key.Private privkey) {
+ if (privkey == null) {
+ return null;
+ }
EC_Curve curve = EC_Store.getInstance().getObject(EC_Curve.class, privkey.getCurve());
if (curve == null) {
- throw new IllegalArgumentException("privkey curve nor found: " + privkey.getCurve());
+ throw new IllegalArgumentException("privkey curve not found: " + privkey.getCurve());
}
return new RawECPrivateKey(toScalar(privkey), curve.toSpec());
}
public static KeyPair toKeyPair(EC_Keypair kp) {
+ if (kp == null) {
+ return null;
+ }
EC_Curve curve = EC_Store.getInstance().getObject(EC_Curve.class, kp.getCurve());
if (curve == null) {
- throw new IllegalArgumentException("keypair curve nor found: " + kp.getCurve());
+ throw new IllegalArgumentException("keypair curve not found: " + kp.getCurve());
}
ECPublicKey pubkey = new RawECPublicKey(toPoint(kp), curve.toSpec());
ECPrivateKey privkey = new RawECPrivateKey(toScalar(kp), curve.toSpec());
@@ -222,4 +261,33 @@ public class ECUtil {
public static byte[] toDERSignature(byte[] r, byte[] s) {
return ByteUtil.concatenate(new byte[]{0x30, (byte) (r.length + s.length + 4), 0x02, (byte) r.length}, r, new byte[]{0x02, (byte) s.length}, s);
}
+
+ public static BigInteger[] fromDERSignature(byte[] signature) throws IOException {
+ ASN1StreamParser parser = new ASN1StreamParser(signature);
+ DERSequence sequence = (DERSequence) ((DERSequenceParser) parser.readObject()).getLoadedObject();
+ ASN1Integer r = (ASN1Integer) sequence.getObjectAt(0);
+ ASN1Integer s = (ASN1Integer) sequence.getObjectAt(1);
+ return new BigInteger[]{r.getPositiveValue(), s.getPositiveValue()};
+ }
+
+ public static BigInteger recoverSignatureNonce(byte[] signature, byte[] data, BigInteger privkey, ECParameterSpec params, String hashType) {
+ try {
+ int bitSize = params.getOrder().bitLength();
+ MessageDigest md = MessageDigest.getInstance(hashType);
+ byte[] hash = md.digest(data);
+ BigInteger hashInt = new BigInteger(1, hash);
+ hashInt = hashInt.and(BigInteger.ONE.shiftLeft(bitSize + 1).subtract(BigInteger.ONE));
+
+ BigInteger[] sigPair = fromDERSignature(signature);
+ BigInteger r = sigPair[0];
+ BigInteger s = sigPair[1];
+
+ BigInteger rd = privkey.multiply(r).mod(params.getOrder());
+ BigInteger hrd = hashInt.add(rd).mod(params.getOrder());
+ return s.modInverse(params.getOrder()).multiply(hrd).mod(params.getOrder());
+ } catch (NoSuchAlgorithmException | IOException nsae) {
+ nsae.printStackTrace();
+ return null;
+ }
+ }
}
diff --git a/src/cz/crcs/ectester/data/cofactor/cofactor128p56467.csv b/src/cz/crcs/ectester/data/cofactor/cofactor128p56467.csv
new file mode 100644
index 0000000..193f6a7
--- /dev/null
+++ b/src/cz/crcs/ectester/data/cofactor/cofactor128p56467.csv
@@ -0,0 +1 @@
+0xe8e100a50b479105f40c312de4bc7127,0x854c8cdc7389dbb3da8a949ce4598ebe,0x4e592cbd1471bba6dec1106cfa99f969,0x7a6c7f7f8305853831d7c99dd23b03aa,0xa3ad04379cb4789bd64e7d99a7874e0b,0x00010e47ea4c399c7ddb49c9915c3b5d,0xdc93 \ No newline at end of file
diff --git a/src/cz/crcs/ectester/data/cofactor/cofactor128p65521.csv b/src/cz/crcs/ectester/data/cofactor/cofactor128p65521.csv
new file mode 100644
index 0000000..80a1eb3
--- /dev/null
+++ b/src/cz/crcs/ectester/data/cofactor/cofactor128p65521.csv
@@ -0,0 +1 @@
+0xdc068a34e30288e08b495798af63ebc7,0xdc068a34e3027b1ccb5209bee1c3ebc7,0xdc054fb5cb170758f9fe7d1b5f63ebc7,0xc0d6edec3ac87edf8499d1885fd03e7b,0x81cb302f36ecd3ff93cd6314ce059e14,0x0000dc136f586930b2b948e64bb6e653,0xfff1 \ No newline at end of file
diff --git a/src/cz/crcs/ectester/data/cofactor/cofactor128p65535.csv b/src/cz/crcs/ectester/data/cofactor/cofactor128p65535.csv
new file mode 100644
index 0000000..54da6cc
--- /dev/null
+++ b/src/cz/crcs/ectester/data/cofactor/cofactor128p65535.csv
@@ -0,0 +1 @@
+0xdd94e89ef3fba74afc2a67cb91546a93,0x6cf4828ab4960df2b9fcab3990e3959a,0x80a5c32206c83f769c5ed3e4f5b2ea4e,0xd7a4bb4b7e9ad9e81895caeaeac8b739,0x45ebc51cf353974b02b36b9912de041b,0x0000dd95c634ba30617af48fd4eb321b,0xffff \ No newline at end of file
diff --git a/src/cz/crcs/ectester/data/cofactor/cofactor192p2.csv b/src/cz/crcs/ectester/data/cofactor/cofactor192p2.csv
new file mode 100644
index 0000000..4de7049
--- /dev/null
+++ b/src/cz/crcs/ectester/data/cofactor/cofactor192p2.csv
@@ -0,0 +1 @@
+0x79cb22319472d019a08f7ae4f99c3edea2f167773210eab7,0x2b1afa29b6e9f7e148f7eb306ae942a506546c7129d56a3f,0x704d348ecd838ed800911bab3298aeebbf1c03b5489bca5d,0x61fc509a9d967735e8f18b0a4ba323134989c7711f44c35b,0x4fe31f28e2ee2a41f6fd661e417d32832bee6f3e164b167e,0x3ce59118ca39680cd047bd72595564c3953a773f01833de3,0x02 \ No newline at end of file
diff --git a/src/cz/crcs/ectester/data/cofactor/cofactor192p4.csv b/src/cz/crcs/ectester/data/cofactor/cofactor192p4.csv
new file mode 100644
index 0000000..645f031
--- /dev/null
+++ b/src/cz/crcs/ectester/data/cofactor/cofactor192p4.csv
@@ -0,0 +1 @@
+0x8cceb84c81521937bef0925a3aaf09195a59c3f99ae06135,0x6ad5a0b617af4ac05f668ae0236f0a485290c36ef609efb5,0x3289c9a3f4f0364147634d40c2f7604e4bc98773daefc954,0x314789e7e4e448b000d235cc51251e70cd8c92c11d1858f9,0x74459b81d5322dc2c631d3ba964e8b4c8f1e4196939a5579,0x2333ae132054864defbc24965da70e7dbdb87ba264315991,0x04 \ No newline at end of file
diff --git a/src/cz/crcs/ectester/data/cofactor/cofactor192p8.csv b/src/cz/crcs/ectester/data/cofactor/cofactor192p8.csv
new file mode 100644
index 0000000..ee39445
--- /dev/null
+++ b/src/cz/crcs/ectester/data/cofactor/cofactor192p8.csv
@@ -0,0 +1 @@
+0xa9a93bb887865d349841624bd8281c589a8e0196ae724eed,0x1b8b108f729cc205fb0ec88825d7d696e3df62ed328bd535,0x81078fdf85b1ee56ea3e27f6dedcca6f5eb9b645f536dc68,0x37369946896227fce5bfe8f760ba827080caa6700d8d8aaa,0x7f54e11bf72549866571fb70b383b6d1451973c11e3fd082,0x1535277710f0cba693082c4985a9197e9e759aa3571eb787,0x08 \ No newline at end of file
diff --git a/src/cz/crcs/ectester/data/cofactor/curves.xml b/src/cz/crcs/ectester/data/cofactor/curves.xml
index 7cf2a9a..0b8c52e 100644
--- a/src/cz/crcs/ectester/data/cofactor/curves.xml
+++ b/src/cz/crcs/ectester/data/cofactor/curves.xml
@@ -44,6 +44,24 @@
<field>prime</field>
<file>cofactor128p128.csv</file>
</curve>
+ <curve>
+ <id>large/cofactor128p56467</id>
+ <bits>128</bits>
+ <field>prime</field>
+ <file>cofactor128p56467.csv</file>
+ </curve>
+ <curve>
+ <id>large/cofactor128p65521</id>
+ <bits>128</bits>
+ <field>prime</field>
+ <file>cofactor128p65521.csv</file>
+ </curve>
+ <curve>
+ <id>large/cofactor128p65535</id>
+ <bits>128</bits>
+ <field>prime</field>
+ <file>cofactor128p65535.csv</file>
+ </curve>
<curve>
<id>cofactor160p2</id>
@@ -88,6 +106,18 @@
<field>prime</field>
<file>cofactor192p2.csv</file>
</curve>
+ <curve>
+ <id>cofactor192p4</id>
+ <bits>192</bits>
+ <field>prime</field>
+ <file>cofactor192p4.csv</file>
+ </curve>
+ <curve>
+ <id>cofactor192p8</id>
+ <bits>192</bits>
+ <field>prime</field>
+ <file>cofactor192p8.csv</file>
+ </curve>
<curve>
<id>cofactor163t2</id>
diff --git a/src/cz/crcs/ectester/data/cofactor/keys.xml b/src/cz/crcs/ectester/data/cofactor/keys.xml
index b4c0c90..2be7238 100644
--- a/src/cz/crcs/ectester/data/cofactor/keys.xml
+++ b/src/cz/crcs/ectester/data/cofactor/keys.xml
@@ -702,4 +702,23 @@
<curve>composite/pq/composite256/2</curve>
<desc>cofactor order = 0x000000000000000000000000000000000000000000000000743bc7ea193d40db</desc>
</pubkey>
+
+ <pubkey>
+ <id>large/cofactor128p56467/0</id>
+ <inline>0x8afd6cc280e0be7163bb6f285a7c6391,0xae64e0f1afc7bd5c75e2f36a7d85f668</inline>
+ <curve>cofactor/large/cofactor128p56467</curve>
+ <desc>cofactor order = 0xdc93</desc>
+ </pubkey>
+ <pubkey>
+ <id>large/cofactor128p65521/0</id>
+ <inline>0x70e43816ed51388caa54a68b6c500352,0xab05b43e2cde6086b12350abe79b9175</inline>
+ <curve>cofactor/large/cofactor128p65521</curve>
+ <desc>cofactor order = 0xfff1</desc>
+ </pubkey>
+ <pubkey>
+ <id>large/cofactor128p65535/0</id>
+ <inline>0x39d6ea56c3eb6382d2d7a9d327a191fd,0x3ebb3f4626d05df38572af3ae5fa60f2</inline>
+ <curve>cofactor/large/cofactor128p65535</curve>
+ <desc>cofactor order = 0xffff</desc>
+ </pubkey>
</keys>
diff --git a/src/cz/crcs/ectester/data/degenerate/cofactor.xml b/src/cz/crcs/ectester/data/degenerate/cofactor.xml
new file mode 100644
index 0000000..647515b
--- /dev/null
+++ b/src/cz/crcs/ectester/data/degenerate/cofactor.xml
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<pubkey>
+ <id>cofactor128p4/0</id>
+ <inline>0x00000000000000000000000000000000,0x94d9020b666fbb599609485472a9246e</inline>
+ <curve>cofactor/cofactor128p4</curve>
+ <desc>degenerate order = 2</desc>
+</pubkey>
+<pubkey>
+ <id>cofactor128p4/1</id>
+ <inline>0x00000000000000000000000000000000,0x2d3a81f8b8d96e6db96a04fb6cf432de</inline>
+ <curve>cofactor/cofactor128p4</curve>
+ <desc>degenerate order = 3</desc>
+</pubkey>
+<pubkey>
+ <id>cofactor128p4/2</id>
+ <inline>0x00000000000000000000000000000000,0x639272497e0865cea0e17677b6bc5575</inline>
+ <curve>cofactor/cofactor128p4</curve>
+ <desc>degenerate order = 7</desc>
+</pubkey>
+<pubkey>
+ <id>cofactor128p4/3</id>
+ <inline>0x00000000000000000000000000000000,0x072aba3ae7aeb770332600a630e503d1</inline>
+ <curve>cofactor/cofactor128p4</curve>
+ <desc>degenerate order = 5297</desc>
+</pubkey>
+<pubkey>
+ <id>cofactor128p4/4</id>
+ <inline>0x00000000000000000000000000000000,0x17b45a35afdff5c5150a7c0a7ee34825</inline>
+ <curve>cofactor/cofactor128p4</curve>
+ <desc>degenerate order = 31134053800693</desc>
+</pubkey>
+<pubkey>
+ <id>cofactor128p4/5</id>
+ <inline>0x00000000000000000000000000000000,0x6fd5d6e491bf5a15eb1d38554caad86c</inline>
+ <curve>cofactor/cofactor128p4</curve>
+ <desc>degenerate order = 28564500657606656383</desc>
+</pubkey>
+<pubkey>
+ <id>cofactor128p4/gen</id>
+ <inline>0x00000000000000000000000000000000,0x00000000000000000000000000000005</inline>
+ <curve>cofactor/cofactor128p4</curve>
+ <desc>generator of Fp^*</desc>
+</pubkey>
+
+<pubkey>
+ <id>cofactor160p4/0</id>
+ <inline>0x0000000000000000000000000000000000000000,0x93ab454ad26dae3b521d5b61a48c94cab3c4aa9c</inline>
+ <curve>cofactor/cofactor160p4</curve>
+ <desc>degenerate order = 2</desc>
+</pubkey>
+<pubkey>
+ <id>cofactor160p4/1</id>
+ <inline>0x0000000000000000000000000000000000000000,0xbad87d0931716ec918e43e76b57971cc613e153</inline>
+ <curve>cofactor/cofactor160p4</curve>
+ <desc>degenerate order = 4</desc>
+</pubkey>
+<pubkey>
+ <id>cofactor160p4/2</id>
+ <inline>0x0000000000000000000000000000000000000000,0x4428069aa7ac1865eb52c5b4c885ec832d89b36d</inline>
+ <curve>cofactor/cofactor160p4</curve>
+ <desc>degenerate order = 3</desc>
+</pubkey>
+<pubkey>
+ <id>cofactor160p4/3</id>
+ <inline>0x0000000000000000000000000000000000000000,0x6eb71aefce923ebf8b07c6f1f59b1c30d43b74ae</inline>
+ <curve>cofactor/cofactor160p4</curve>
+ <desc>degenerate order = 23</desc>
+</pubkey>
+<pubkey>
+ <id>cofactor160p4/4</id>
+ <inline>0x0000000000000000000000000000000000000000,0x3c5ff8c94b31b46f92575e0b77b0366afe24dfc1</inline>
+ <curve>cofactor/cofactor160p4</curve>
+ <desc>degenerate order = 11443</desc>
+</pubkey>
+<pubkey>
+ <id>cofactor160p4/5</id>
+ <inline>0x0000000000000000000000000000000000000000,0xd8e2287382e057de70e1f45f70d8dad85d27025</inline>
+ <curve>cofactor/cofactor160p4</curve>
+ <desc>degenerate order = 352281613501590816479</desc>
+</pubkey>
+<pubkey>
+ <id>cofactor160p4/5</id>
+ <inline>0x0000000000000000000000000000000000000000,0x36911d265f6d795a2efd10c20aae0f3ec5f815f4</inline>
+ <curve>cofactor/cofactor160p4</curve>
+ <desc>degenerate order = 757721821606925858951</desc>
+</pubkey>
+<pubkey>
+ <id>cofactor160p4/gen</id>
+ <inline>0x0000000000000000000000000000000000000000,0x0000000000000000000000000000000000000002</inline>
+ <curve>cofactor/cofactor160p4</curve>
+ <desc>generator of Fp^*</desc>
+</pubkey>
+
+<pubkey>
+ <id>cofactor192p4/0</id>
+ <inline>0x000000000000000000000000000000000000000000000000,0x8cceb84c81521937bef0925a3aaf09195a59c3f99ae06134</inline>
+ <curve>cofactor/cofactor192p4</curve>
+ <desc>degenerate order = 2</desc>
+</pubkey>
+<pubkey>
+ <id>cofactor192p4/1</id>
+ <inline>0x000000000000000000000000000000000000000000000000,0x63ca4f21e0e4f6a833f914468e00e4d817f472d54aca5a64</inline>
+ <curve>cofactor/cofactor192p4</curve>
+ <desc>degenerate order = 4</desc>
+</pubkey>
+<pubkey>
+ <id>cofactor192p4/2</id>
+ <inline>0x000000000000000000000000000000000000000000000000,0x7ce088c401bfc705e70da9928c04ed6e1bf100c26b253028</inline>
+ <curve>cofactor/cofactor192p4</curve>
+ <desc>degenerate order = 5</desc>
+</pubkey>
+<pubkey>
+ <id>cofactor192p4/3</id>
+ <inline>0x000000000000000000000000000000000000000000000000,0x000000000000000000000000000000000000000000100000</inline>
+ <curve>cofactor/cofactor192p4</curve>
+ <desc>degenerate order = 172629492300688965054638881592440218548130640356589228457</desc>
+</pubkey>
+<pubkey>
+ <id>cofactor192p4/gen</id>
+ <inline>0x000000000000000000000000000000000000000000000000,0x000000000000000000000000000000000000000000000002</inline>
+ <curve>cofactor/cofactor192p4</curve>
+ <desc>generator of Fp^*</desc>
+</pubkey>
diff --git a/src/cz/crcs/ectester/data/degenerate/keys.xml b/src/cz/crcs/ectester/data/degenerate/keys.xml
index b999ca0..498cf26 100644
--- a/src/cz/crcs/ectester/data/degenerate/keys.xml
+++ b/src/cz/crcs/ectester/data/degenerate/keys.xml
@@ -2,6 +2,7 @@
<!DOCTYPE keys [
<!ENTITY secg SYSTEM "degenerate/secg.xml">
<!ENTITY brainpool SYSTEM "degenerate/brainpool.xml">
+ <!ENTITY cofactor SYSTEM "degenerate/cofactor.xml">
]>
<keys xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../schema.xsd">
@@ -11,4 +12,5 @@
-->
&secg;
&brainpool;
+ &cofactor;
</keys> \ No newline at end of file
diff --git a/src/cz/crcs/ectester/data/invalid/secg/secp128r1.xml b/src/cz/crcs/ectester/data/invalid/secg/secp128r1.xml
index 8903688..e010003 100644
--- a/src/cz/crcs/ectester/data/invalid/secg/secp128r1.xml
+++ b/src/cz/crcs/ectester/data/invalid/secg/secp128r1.xml
@@ -85,7 +85,7 @@
</pubkey>
<pubkey>
<id>secp128r1/14</id>
- <inline>0x98b36c442de5c741c70fa80a31d72fa,0x251e9a04ffe799cf4776575be582f108</inline>
+ <inline>0x098b36c442de5c741c70fa80a31d72fa,0x251e9a04ffe799cf4776575be582f108</inline>
<curve>secg/secp128r1</curve>
<desc>invalid order = 47</desc>
</pubkey>
@@ -109,7 +109,7 @@
</pubkey>
<pubkey>
<id>secp128r1/18</id>
- <inline>0x9ce43ec4dcaf95993d8ab00efcc7199a,0x7fb6d895c27bc326a33cb8111e865a9</inline>
+ <inline>0x9ce43ec4dcaf95993d8ab00efcc7199a,0x07fb6d895c27bc326a33cb8111e865a9</inline>
<curve>secg/secp128r1</curve>
<desc>invalid order = 67</desc>
</pubkey>
@@ -139,7 +139,7 @@
</pubkey>
<pubkey>
<id>secp128r1/23</id>
- <inline>0x6803013e75597fb7f83f1f8681af11d,0x32490d391f8a2b1de83212dd218b3a5a</inline>
+ <inline>0x06803013e75597fb7f83f1f8681af11d,0x32490d391f8a2b1de83212dd218b3a5a</inline>
<curve>secg/secp128r1</curve>
<desc>invalid order = 89</desc>
</pubkey>
diff --git a/src/cz/crcs/ectester/data/misc/results.xml b/src/cz/crcs/ectester/data/misc/results.xml
index ba8c83c..07601b1 100644
--- a/src/cz/crcs/ectester/data/misc/results.xml
+++ b/src/cz/crcs/ectester/data/misc/results.xml
@@ -7,8 +7,8 @@
<inline>0xdb6f7cd6a06846bf9da9b4928caa5e4b7c8f58d9</inline>
<!-- == SHA1(0x1D0F27241C177385B0D5025029FABD5D5D8475DA4E267DCD177B49C63605C25A) -->
<curve>secg/secp256r1</curve>
- <onekey>other/openssl-bug/pkey</onekey>
- <otherkey>other/openssl-bug/skey</otherkey>
+ <onekey>misc/openssl-bug/pkey</onekey>
+ <otherkey>misc/openssl-bug/skey</otherkey>
<desc>https://eprint.iacr.org/2011/633</desc>
</kaResult>
</results> \ No newline at end of file
diff --git a/src/cz/crcs/ectester/data/twist/cofactor/cofactor128p4.xml b/src/cz/crcs/ectester/data/twist/cofactor/cofactor128p4.xml
new file mode 100644
index 0000000..b558f8e
--- /dev/null
+++ b/src/cz/crcs/ectester/data/twist/cofactor/cofactor128p4.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<pubkey>
+ <id>cofactor128p4/0</id>
+ <inline>0x72294f8a7c88d510343c19b8251d7dd6,0x00000000000000000000000000000000</inline>
+ <curve>cofactor/cofactor128p4</curve>
+ <desc>twist order = 2</desc>
+</pubkey>
+<pubkey>
+ <id>cofactor128p4/1</id>
+ <inline>0x5d20662769138fe1506f2a2b44fd34c1,0x15d63a5aba305ccdee9f65e3f2c1d4e8</inline>
+ <curve>cofactor/cofactor128p4</curve>
+ <desc>twist order = 4</desc>
+</pubkey>
+<pubkey>
+ <id>cofactor128p4/2</id>
+ <inline>0x0b843b9da795292bfc598bae47fd0955,0x2944056236d430e404f6fd058a7a6624</inline>
+ <curve>cofactor/cofactor128p4</curve>
+ <desc>twist order = 17</desc>
+</pubkey>
+<pubkey>
+ <id>cofactor128p4/3</id>
+ <inline>0x663a7a5a7370a48f98ef5ba0cc2d19a1,0x13d59851b95e3916e1149b1f8345325d</inline>
+ <curve>cofactor/cofactor128p4</curve>
+ <desc>twist order = 37</desc>
+</pubkey>
+<pubkey>
+ <id>cofactor128p4/4</id>
+ <inline>0x415d46d2beb2357a567efeedd3e052a0,0x8b202b706af555d470fb42fb5919a64</inline>
+ <curve>cofactor/cofactor128p4</curve>
+ <desc>twist order = 24422261</desc>
+</pubkey>
+<pubkey>
+ <id>cofactor128p4/5</id>
+ <inline>0x6707ea110f83e67a9f6a43c184587bc6,0x1c44db735c6b30165e40660ecc5d8c3c</inline>
+ <curve>cofactor/cofactor128p4</curve>
+ <desc>twist order = 87024861802858114445834597</desc>
+</pubkey>
+<pubkey>
+ <id>cofactor128p4/gen</id>
+ <inline>0x5a1c6fd7a138377f22dabe0840a02ede,0x39395b4be5f4c131a0a5f778be1166e5</inline>
+ <curve>cofactor/cofactor128p4</curve>
+ <desc>twist generator</desc>
+</pubkey>
+
diff --git a/src/cz/crcs/ectester/data/twist/cofactor/cofactor160p4.xml b/src/cz/crcs/ectester/data/twist/cofactor/cofactor160p4.xml
new file mode 100644
index 0000000..bb712af
--- /dev/null
+++ b/src/cz/crcs/ectester/data/twist/cofactor/cofactor160p4.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<pubkey>
+ <id>cofactor160p4/0</id>
+ <inline>0x0c43497bdc7c1fddd18368da4894a98a612f09ec,0x0000000000000000000000000000000000000000</inline>
+ <curve>cofactor/cofactor160p4</curve>
+ <desc>twist order = 2</desc>
+</pubkey>
+<pubkey>
+ <id>cofactor160p4/1</id>
+ <inline>0x526ba726c52a6c998994733747dc27db793ce64b,0x3f432767051371f91355e6a14488883bc51c881e</inline>
+ <curve>cofactor/cofactor160p4</curve>
+ <desc>twist order = 4</desc>
+</pubkey>
+<pubkey>
+ <id>cofactor160p4/2</id>
+ <inline>0x380cd2b93ff179b2411c721879d7fbed95ef1d68,0x7b36aafe70fa88d2522931555d91e072a89eaff0</inline>
+ <curve>cofactor/cofactor160p4</curve>
+ <desc>twist order = 8</desc>
+</pubkey>
+<pubkey>
+ <id>cofactor160p4/3</id>
+ <inline>0x640d26a5aa07b529bf39bb4d4ad79346f677e2e9,0x22c90f648dfd349f8ac76c4aa0e4fd7278bc4516</inline>
+ <curve>cofactor/cofactor160p4</curve>
+ <desc>twist order = 16</desc>
+</pubkey>
+<pubkey>
+ <id>cofactor160p4/4</id>
+ <inline>0x8dab044b1a87809667b940b43d913b00fa194c8,0x20652c81133c9e51a16d0ecbcd6f81111afc03c3</inline>
+ <curve>cofactor/cofactor160p4</curve>
+ <desc>twist order = 3</desc>
+</pubkey>
+<pubkey>
+ <id>cofactor160p4/5</id>
+ <inline>0x66e27b0bfaf5269dbca67fa71ea3a117f29f4ef9,0x2b9499a775ae8f7fba1884b3d852429757312c93</inline>
+ <curve>cofactor/cofactor160p4</curve>
+ <desc>twist order = 13</desc>
+</pubkey>
+<pubkey>
+ <id>cofactor160p4/6</id>
+ <inline>0x4758716ac3b6cfb971ea0a673c4eebbad085fbd8,0x6ab9c8044435062299d14bdb6d6a41faf0bb0067</inline>
+ <curve>cofactor/cofactor160p4</curve>
+ <desc>twist order = 169</desc>
+</pubkey>
+<pubkey>
+ <id>cofactor160p4/7</id>
+ <inline>0x76690f13f9fdec12b156a40f5a7c0f25b420e7e0,0x8cab0d69936dcb3b64007f2fd2881f18f627ade5</inline>
+ <curve>cofactor/cofactor160p4</curve>
+ <desc>twist order = 107</desc>
+</pubkey>
+<pubkey>
+ <id>cofactor160p4/8</id>
+ <inline>0x178c6e5bb98247299631a52d32a55e61711a21fb,0x6e9171b2aab5bbbe488d9c3c367cf0536bf19e1a</inline>
+ <curve>cofactor/cofactor160p4</curve>
+ <desc>twist order = 15259</desc>
+</pubkey>
+<pubkey>
+ <id>cofactor160p4/9</id>
+ <inline>0x08e82adfcb0ff539bf58e4f232f4721f3a014904,0x7ba4d134fa420dbf7fdff4986361d625e87ca27d</inline>
+ <curve>cofactor/cofactor160p4</curve>
+ <desc>twist order = 322336986893916431</desc>
+</pubkey>
+<pubkey>
+ <id>cofactor160p4/10</id>
+ <inline>0x1d6ee4ac5b0da602078684f14bab3510915f7fef,0x229903e44fe1dd7e4ef1d3dd4edc0ed05c712bef</inline>
+ <curve>cofactor/cofactor160p4</curve>
+ <desc>twist order = 197469859348064237101</desc>
+</pubkey>
+<pubkey>
+ <id>cofactor160p4/gen</id>
+ <inline>0x5603c1fd03c11eb2ab5f7abb998658a791a71202,0x3151be9d7f447756c8e85f5ac82c1ee410727157</inline>
+ <curve>cofactor/cofactor160p4</curve>
+ <desc>twist generator</desc>
+</pubkey>
+
diff --git a/src/cz/crcs/ectester/data/twist/keys.xml b/src/cz/crcs/ectester/data/twist/keys.xml
index df208bf..3292004 100644
--- a/src/cz/crcs/ectester/data/twist/keys.xml
+++ b/src/cz/crcs/ectester/data/twist/keys.xml
@@ -21,6 +21,9 @@
<!ENTITY secp256r1 SYSTEM "twist/secg/secp256r1.xml">
<!ENTITY secp384r1 SYSTEM "twist/secg/secp384r1.xml">
<!ENTITY secp521r1 SYSTEM "twist/secg/secp521r1.xml">
+
+ <!ENTITY cofactor128p4 SYSTEM "twist/cofactor/cofactor128p4.xml">
+ <!ENTITY cofactor160p4 SYSTEM "twist/cofactor/cofactor160p4.xml">
]>
<keys xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../schema.xsd">
@@ -45,4 +48,7 @@
&secp256r1;
&secp384r1;
&secp521r1;
+
+ &cofactor128p4;
+ &cofactor160p4;
</keys> \ No newline at end of file
diff --git a/src/cz/crcs/ectester/data/wycheproof/keys.xml b/src/cz/crcs/ectester/data/wycheproof/keys.xml
index 46359df..7ca174d 100644
--- a/src/cz/crcs/ectester/data/wycheproof/keys.xml
+++ b/src/cz/crcs/ectester/data/wycheproof/keys.xml
@@ -302,7 +302,7 @@
</pubkey>
<privkey>
<id>addsub/brainpoolP224r1/1s</id>
- <inline>0x0d7c134aa264366862a18302575d0fb98d116bc4b6ddebca3a5a792dd</inline>
+ <inline>0xd7c134aa264366862a18302575d0fb98d116bc4b6ddebca3a5a792dd</inline>
<curve>brainpool/brainpoolP224r1</curve>
<desc>tcId = 441</desc>
</privkey>
@@ -315,7 +315,7 @@
</pubkey>
<privkey>
<id>addsub/brainpoolP224r1/2s</id>
- <inline>0x0d7c134aa264366862a18302575d0fb98d116bc4b6ddebca3a5a7935d</inline>
+ <inline>0xd7c134aa264366862a18302575d0fb98d116bc4b6ddebca3a5a7935d</inline>
<curve>brainpool/brainpoolP224r1</curve>
<desc>tcId = 442</desc>
</privkey>
@@ -328,7 +328,7 @@
</pubkey>
<privkey>
<id>addsub/brainpoolP224r1/3s</id>
- <inline>0x0d7c134aa264366862a18302575d0fb98d116bc4b6ddebca3a5a7939d</inline>
+ <inline>0xd7c134aa264366862a18302575d0fb98d116bc4b6ddebca3a5a7939d</inline>
<curve>brainpool/brainpoolP224r1</curve>
<desc>tcId = 444</desc>
</privkey>
@@ -341,7 +341,7 @@
</pubkey>
<privkey>
<id>addsub/brainpoolP256r1/1s</id>
- <inline>0x0a9fb57dba1eea9bc3e660a909d838d718c397aa3b561a6f7901e0e82974855f5</inline>
+ <inline>0xa9fb57dba1eea9bc3e660a909d838d718c397aa3b561a6f7901e0e82974855f5</inline>
<curve>brainpool/brainpoolP256r1</curve>
<desc>tcId = 524</desc>
</privkey>
@@ -354,7 +354,7 @@
</pubkey>
<privkey>
<id>addsub/brainpoolP256r1/2s</id>
- <inline>0x0a9fb57dba1eea9bc3e660a909d838d718c397aa3b561a6f7901e0e8297485675</inline>
+ <inline>0xa9fb57dba1eea9bc3e660a909d838d718c397aa3b561a6f7901e0e8297485675</inline>
<curve>brainpool/brainpoolP256r1</curve>
<desc>tcId = 525</desc>
</privkey>
@@ -367,7 +367,7 @@
</pubkey>
<privkey>
<id>addsub/brainpoolP256r1/3s</id>
- <inline>0x0a9fb57dba1eea9bc3e660a909d838d718c397aa3b561a6f7901e0e8297485695</inline>
+ <inline>0xa9fb57dba1eea9bc3e660a909d838d718c397aa3b561a6f7901e0e8297485695</inline>
<curve>brainpool/brainpoolP256r1</curve>
<desc>tcId = 526</desc>
</privkey>
@@ -380,7 +380,7 @@
</pubkey>
<privkey>
<id>addsub/brainpoolP256r1/4s</id>
- <inline>0x0a9fb57dba1eea9bc3e660a909d838d718c397aa3b561a6f7901e0e82974856a5</inline>
+ <inline>0xa9fb57dba1eea9bc3e660a909d838d718c397aa3b561a6f7901e0e82974856a5</inline>
<curve>brainpool/brainpoolP256r1</curve>
<desc>tcId = 528</desc>
</privkey>
@@ -393,7 +393,7 @@
</pubkey>
<privkey>
<id>addsub/brainpoolP320r1/1s</id>
- <inline>0x0d35e472036bc4fb7e13c785ed201e065f98fcfa5b68f12a32d482ec7ee8658e98691555b44c59233</inline>
+ <inline>0xd35e472036bc4fb7e13c785ed201e065f98fcfa5b68f12a32d482ec7ee8658e98691555b44c59233</inline>
<curve>brainpool/brainpoolP320r1</curve>
<desc>tcId = 604</desc>
</privkey>
@@ -406,7 +406,7 @@
</pubkey>
<privkey>
<id>addsub/brainpoolP320r1/2s</id>
- <inline>0x0d35e472036bc4fb7e13c785ed201e065f98fcfa5b68f12a32d482ec7ee8658e98691555b44c592b3</inline>
+ <inline>0xd35e472036bc4fb7e13c785ed201e065f98fcfa5b68f12a32d482ec7ee8658e98691555b44c592b3</inline>
<curve>brainpool/brainpoolP320r1</curve>
<desc>tcId = 605</desc>
</privkey>
@@ -419,7 +419,7 @@
</pubkey>
<privkey>
<id>addsub/brainpoolP320r1/3s</id>
- <inline>0x0d35e472036bc4fb7e13c785ed201e065f98fcfa5b68f12a32d482ec7ee8658e98691555b44c592f3</inline>
+ <inline>0xd35e472036bc4fb7e13c785ed201e065f98fcfa5b68f12a32d482ec7ee8658e98691555b44c592f3</inline>
<curve>brainpool/brainpoolP320r1</curve>
<desc>tcId = 606</desc>
</privkey>
@@ -432,7 +432,7 @@
</pubkey>
<privkey>
<id>addsub/brainpoolP320r1/4s</id>
- <inline>0x0d35e472036bc4fb7e13c785ed201e065f98fcfa5b68f12a32d482ec7ee8658e98691555b44c59303</inline>
+ <inline>0xd35e472036bc4fb7e13c785ed201e065f98fcfa5b68f12a32d482ec7ee8658e98691555b44c59303</inline>
<curve>brainpool/brainpoolP320r1</curve>
<desc>tcId = 607</desc>
</privkey>
@@ -445,7 +445,7 @@
</pubkey>
<privkey>
<id>addsub/brainpoolP320r1/5s</id>
- <inline>0x0d35e472036bc4fb7e13c785ed201e065f98fcfa5b68f12a32d482ec7ee8658e98691555b44c5930b</inline>
+ <inline>0xd35e472036bc4fb7e13c785ed201e065f98fcfa5b68f12a32d482ec7ee8658e98691555b44c5930b</inline>
<curve>brainpool/brainpoolP320r1</curve>
<desc>tcId = 608</desc>
</privkey>
@@ -458,7 +458,7 @@
</pubkey>
<privkey>
<id>addsub/brainpoolP320r1/6s</id>
- <inline>0x0d35e472036bc4fb7e13c785ed201e065f98fcfa5b68f12a32d482ec7ee8658e98691555b44c5930f</inline>
+ <inline>0xd35e472036bc4fb7e13c785ed201e065f98fcfa5b68f12a32d482ec7ee8658e98691555b44c5930f</inline>
<curve>brainpool/brainpoolP320r1</curve>
<desc>tcId = 610</desc>
</privkey>
@@ -471,7 +471,7 @@
</pubkey>
<privkey>
<id>addsub/brainpoolP384r1/1s</id>
- <inline>0x08cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b31f166e6cac0425a7cf3ab6af6b7fc3103b883202e904652f</inline>
+ <inline>0x8cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b31f166e6cac0425a7cf3ab6af6b7fc3103b883202e904652f</inline>
<curve>brainpool/brainpoolP384r1</curve>
<desc>tcId = 684</desc>
</privkey>
@@ -484,7 +484,7 @@
</pubkey>
<privkey>
<id>addsub/brainpoolP384r1/2s</id>
- <inline>0x08cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b31f166e6cac0425a7cf3ab6af6b7fc3103b883202e904654f</inline>
+ <inline>0x8cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b31f166e6cac0425a7cf3ab6af6b7fc3103b883202e904654f</inline>
<curve>brainpool/brainpoolP384r1</curve>
<desc>tcId = 685</desc>
</privkey>
@@ -497,7 +497,7 @@
</pubkey>
<privkey>
<id>addsub/brainpoolP384r1/3s</id>
- <inline>0x08cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b31f166e6cac0425a7cf3ab6af6b7fc3103b883202e904655f</inline>
+ <inline>0x8cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b31f166e6cac0425a7cf3ab6af6b7fc3103b883202e904655f</inline>
<curve>brainpool/brainpoolP384r1</curve>
<desc>tcId = 686</desc>
</privkey>
@@ -510,7 +510,7 @@
</pubkey>
<privkey>
<id>addsub/brainpoolP384r1/4s</id>
- <inline>0x08cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b31f166e6cac0425a7cf3ab6af6b7fc3103b883202e9046563</inline>
+ <inline>0x8cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b31f166e6cac0425a7cf3ab6af6b7fc3103b883202e9046563</inline>
<curve>brainpool/brainpoolP384r1</curve>
<desc>tcId = 688</desc>
</privkey>
@@ -523,7 +523,7 @@
</pubkey>
<privkey>
<id>addsub/brainpoolP512r1/1s</id>
- <inline>0x0aadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca70330870553e5c414ca92619418661197fac10471db1d381085ddaddb58796829ca9003b</inline>
+ <inline>0xaadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca70330870553e5c414ca92619418661197fac10471db1d381085ddaddb58796829ca9003b</inline>
<curve>brainpool/brainpoolP512r1</curve>
<desc>tcId = 774</desc>
</privkey>
@@ -536,7 +536,7 @@
</pubkey>
<privkey>
<id>addsub/brainpoolP512r1/2s</id>
- <inline>0x0aadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca70330870553e5c414ca92619418661197fac10471db1d381085ddaddb58796829ca9005b</inline>
+ <inline>0xaadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca70330870553e5c414ca92619418661197fac10471db1d381085ddaddb58796829ca9005b</inline>
<curve>brainpool/brainpoolP512r1</curve>
<desc>tcId = 775</desc>
</privkey>
@@ -549,7 +549,7 @@
</pubkey>
<privkey>
<id>addsub/brainpoolP512r1/3s</id>
- <inline>0x0aadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca70330870553e5c414ca92619418661197fac10471db1d381085ddaddb58796829ca90063</inline>
+ <inline>0xaadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca70330870553e5c414ca92619418661197fac10471db1d381085ddaddb58796829ca90063</inline>
<curve>brainpool/brainpoolP512r1</curve>
<desc>tcId = 776</desc>
</privkey>
@@ -562,7 +562,7 @@
</pubkey>
<privkey>
<id>addsub/brainpoolP512r1/4s</id>
- <inline>0x0aadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca70330870553e5c414ca92619418661197fac10471db1d381085ddaddb58796829ca90067</inline>
+ <inline>0xaadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca70330870553e5c414ca92619418661197fac10471db1d381085ddaddb58796829ca90067</inline>
<curve>brainpool/brainpoolP512r1</curve>
<desc>tcId = 778</desc>
</privkey>
@@ -575,7 +575,7 @@
</pubkey>
<privkey>
<id>addsub/brainpoolP224t1/1s</id>
- <inline>0x0d7c134aa264366862a18302575d0fb98d116bc4b6ddebca3a5a792dd</inline>
+ <inline>0xd7c134aa264366862a18302575d0fb98d116bc4b6ddebca3a5a792dd</inline>
<curve>brainpool/brainpoolP224t1</curve>
<desc>tcId = 854</desc>
</privkey>
@@ -588,7 +588,7 @@
</pubkey>
<privkey>
<id>addsub/brainpoolP224t1/2s</id>
- <inline>0x0d7c134aa264366862a18302575d0fb98d116bc4b6ddebca3a5a7935d</inline>
+ <inline>0xd7c134aa264366862a18302575d0fb98d116bc4b6ddebca3a5a7935d</inline>
<curve>brainpool/brainpoolP224t1</curve>
<desc>tcId = 855</desc>
</privkey>
@@ -601,7 +601,7 @@
</pubkey>
<privkey>
<id>addsub/brainpoolP224t1/3s</id>
- <inline>0x0d7c134aa264366862a18302575d0fb98d116bc4b6ddebca3a5a7939d</inline>
+ <inline>0xd7c134aa264366862a18302575d0fb98d116bc4b6ddebca3a5a7939d</inline>
<curve>brainpool/brainpoolP224t1</curve>
<desc>tcId = 857</desc>
</privkey>
@@ -614,7 +614,7 @@
</pubkey>
<privkey>
<id>addsub/brainpoolP256t1/1s</id>
- <inline>0x0a9fb57dba1eea9bc3e660a909d838d718c397aa3b561a6f7901e0e82974855f5</inline>
+ <inline>0xa9fb57dba1eea9bc3e660a909d838d718c397aa3b561a6f7901e0e82974855f5</inline>
<curve>brainpool/brainpoolP256t1</curve>
<desc>tcId = 935</desc>
</privkey>
@@ -627,7 +627,7 @@
</pubkey>
<privkey>
<id>addsub/brainpoolP256t1/2s</id>
- <inline>0x0a9fb57dba1eea9bc3e660a909d838d718c397aa3b561a6f7901e0e8297485675</inline>
+ <inline>0xa9fb57dba1eea9bc3e660a909d838d718c397aa3b561a6f7901e0e8297485675</inline>
<curve>brainpool/brainpoolP256t1</curve>
<desc>tcId = 936</desc>
</privkey>
@@ -640,7 +640,7 @@
</pubkey>
<privkey>
<id>addsub/brainpoolP256t1/3s</id>
- <inline>0x0a9fb57dba1eea9bc3e660a909d838d718c397aa3b561a6f7901e0e8297485695</inline>
+ <inline>0xa9fb57dba1eea9bc3e660a909d838d718c397aa3b561a6f7901e0e8297485695</inline>
<curve>brainpool/brainpoolP256t1</curve>
<desc>tcId = 937</desc>
</privkey>
@@ -653,7 +653,7 @@
</pubkey>
<privkey>
<id>addsub/brainpoolP256t1/4s</id>
- <inline>0x0a9fb57dba1eea9bc3e660a909d838d718c397aa3b561a6f7901e0e82974856a5</inline>
+ <inline>0xa9fb57dba1eea9bc3e660a909d838d718c397aa3b561a6f7901e0e82974856a5</inline>
<curve>brainpool/brainpoolP256t1</curve>
<desc>tcId = 939</desc>
</privkey>
@@ -666,7 +666,7 @@
</pubkey>
<privkey>
<id>addsub/brainpoolP320t1/1s</id>
- <inline>0x0d35e472036bc4fb7e13c785ed201e065f98fcfa5b68f12a32d482ec7ee8658e98691555b44c59233</inline>
+ <inline>0xd35e472036bc4fb7e13c785ed201e065f98fcfa5b68f12a32d482ec7ee8658e98691555b44c59233</inline>
<curve>brainpool/brainpoolP320t1</curve>
<desc>tcId = 1015</desc>
</privkey>
@@ -679,7 +679,7 @@
</pubkey>
<privkey>
<id>addsub/brainpoolP320t1/2s</id>
- <inline>0x0d35e472036bc4fb7e13c785ed201e065f98fcfa5b68f12a32d482ec7ee8658e98691555b44c592b3</inline>
+ <inline>0xd35e472036bc4fb7e13c785ed201e065f98fcfa5b68f12a32d482ec7ee8658e98691555b44c592b3</inline>
<curve>brainpool/brainpoolP320t1</curve>
<desc>tcId = 1016</desc>
</privkey>
@@ -692,7 +692,7 @@
</pubkey>
<privkey>
<id>addsub/brainpoolP320t1/3s</id>
- <inline>0x0d35e472036bc4fb7e13c785ed201e065f98fcfa5b68f12a32d482ec7ee8658e98691555b44c592f3</inline>
+ <inline>0xd35e472036bc4fb7e13c785ed201e065f98fcfa5b68f12a32d482ec7ee8658e98691555b44c592f3</inline>
<curve>brainpool/brainpoolP320t1</curve>
<desc>tcId = 1017</desc>
</privkey>
@@ -705,7 +705,7 @@
</pubkey>
<privkey>
<id>addsub/brainpoolP320t1/4s</id>
- <inline>0x0d35e472036bc4fb7e13c785ed201e065f98fcfa5b68f12a32d482ec7ee8658e98691555b44c59303</inline>
+ <inline>0xd35e472036bc4fb7e13c785ed201e065f98fcfa5b68f12a32d482ec7ee8658e98691555b44c59303</inline>
<curve>brainpool/brainpoolP320t1</curve>
<desc>tcId = 1018</desc>
</privkey>
@@ -718,7 +718,7 @@
</pubkey>
<privkey>
<id>addsub/brainpoolP320t1/5s</id>
- <inline>0x0d35e472036bc4fb7e13c785ed201e065f98fcfa5b68f12a32d482ec7ee8658e98691555b44c5930b</inline>
+ <inline>0xd35e472036bc4fb7e13c785ed201e065f98fcfa5b68f12a32d482ec7ee8658e98691555b44c5930b</inline>
<curve>brainpool/brainpoolP320t1</curve>
<desc>tcId = 1019</desc>
</privkey>
@@ -731,7 +731,7 @@
</pubkey>
<privkey>
<id>addsub/brainpoolP320t1/6s</id>
- <inline>0x0d35e472036bc4fb7e13c785ed201e065f98fcfa5b68f12a32d482ec7ee8658e98691555b44c5930f</inline>
+ <inline>0xd35e472036bc4fb7e13c785ed201e065f98fcfa5b68f12a32d482ec7ee8658e98691555b44c5930f</inline>
<curve>brainpool/brainpoolP320t1</curve>
<desc>tcId = 1021</desc>
</privkey>
@@ -744,7 +744,7 @@
</pubkey>
<privkey>
<id>addsub/brainpoolP384t1/1s</id>
- <inline>0x08cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b31f166e6cac0425a7cf3ab6af6b7fc3103b883202e904652f</inline>
+ <inline>0x8cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b31f166e6cac0425a7cf3ab6af6b7fc3103b883202e904652f</inline>
<curve>brainpool/brainpoolP384t1</curve>
<desc>tcId = 1093</desc>
</privkey>
@@ -757,7 +757,7 @@
</pubkey>
<privkey>
<id>addsub/brainpoolP384t1/2s</id>
- <inline>0x08cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b31f166e6cac0425a7cf3ab6af6b7fc3103b883202e904654f</inline>
+ <inline>0x8cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b31f166e6cac0425a7cf3ab6af6b7fc3103b883202e904654f</inline>
<curve>brainpool/brainpoolP384t1</curve>
<desc>tcId = 1094</desc>
</privkey>
@@ -770,7 +770,7 @@
</pubkey>
<privkey>
<id>addsub/brainpoolP384t1/3s</id>
- <inline>0x08cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b31f166e6cac0425a7cf3ab6af6b7fc3103b883202e904655f</inline>
+ <inline>0x8cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b31f166e6cac0425a7cf3ab6af6b7fc3103b883202e904655f</inline>
<curve>brainpool/brainpoolP384t1</curve>
<desc>tcId = 1095</desc>
</privkey>
@@ -783,7 +783,7 @@
</pubkey>
<privkey>
<id>addsub/brainpoolP384t1/4s</id>
- <inline>0x08cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b31f166e6cac0425a7cf3ab6af6b7fc3103b883202e9046563</inline>
+ <inline>0x8cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b31f166e6cac0425a7cf3ab6af6b7fc3103b883202e9046563</inline>
<curve>brainpool/brainpoolP384t1</curve>
<desc>tcId = 1097</desc>
</privkey>
@@ -796,7 +796,7 @@
</pubkey>
<privkey>
<id>addsub/brainpoolP512t1/1s</id>
- <inline>0x0aadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca70330870553e5c414ca92619418661197fac10471db1d381085ddaddb58796829ca9003b</inline>
+ <inline>0xaadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca70330870553e5c414ca92619418661197fac10471db1d381085ddaddb58796829ca9003b</inline>
<curve>brainpool/brainpoolP512t1</curve>
<desc>tcId = 1185</desc>
</privkey>
@@ -809,7 +809,7 @@
</pubkey>
<privkey>
<id>addsub/brainpoolP512t1/2s</id>
- <inline>0x0aadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca70330870553e5c414ca92619418661197fac10471db1d381085ddaddb58796829ca9005b</inline>
+ <inline>0xaadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca70330870553e5c414ca92619418661197fac10471db1d381085ddaddb58796829ca9005b</inline>
<curve>brainpool/brainpoolP512t1</curve>
<desc>tcId = 1186</desc>
</privkey>
@@ -822,7 +822,7 @@
</pubkey>
<privkey>
<id>addsub/brainpoolP512t1/3s</id>
- <inline>0x0aadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca70330870553e5c414ca92619418661197fac10471db1d381085ddaddb58796829ca90063</inline>
+ <inline>0xaadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca70330870553e5c414ca92619418661197fac10471db1d381085ddaddb58796829ca90063</inline>
<curve>brainpool/brainpoolP512t1</curve>
<desc>tcId = 1187</desc>
</privkey>
@@ -835,7 +835,7 @@
</pubkey>
<privkey>
<id>addsub/brainpoolP512t1/4s</id>
- <inline>0x0aadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca70330870553e5c414ca92619418661197fac10471db1d381085ddaddb58796829ca90067</inline>
+ <inline>0xaadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca70330870553e5c414ca92619418661197fac10471db1d381085ddaddb58796829ca90067</inline>
<curve>brainpool/brainpoolP512t1</curve>
<desc>tcId = 1189</desc>
</privkey>
@@ -878,7 +878,7 @@
<!-- CVE-2017-10176 tests -->
<privkey>
<id>cve_2017_10176/secp521r1/1s</id>
- <inline>0x1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863f7</inline>
+ <inline>0x01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e913863f7</inline>
<curve>secg/secp521r1</curve>
<desc>tcId = 280</desc>
</privkey>
diff --git a/src/cz/crcs/ectester/reader/CardMngr.java b/src/cz/crcs/ectester/reader/CardMngr.java
index e6835dd..8b6241d 100644
--- a/src/cz/crcs/ectester/reader/CardMngr.java
+++ b/src/cz/crcs/ectester/reader/CardMngr.java
@@ -72,7 +72,14 @@ public class CardMngr {
terminal = terminalList.get(i);
if (terminal.isCardPresent()) {
- card = terminal.connect("*");
+ try {
+ card = terminal.connect("T=1");
+ } catch (CardException ex) {
+ if (verbose)
+ System.out.println("T=1 failed, trying protocol '*'");
+ card = terminal.connect("*");
+ }
+
if (verbose)
System.out.println("card: " + card);
channel = card.getBasicChannel();
@@ -345,7 +352,7 @@ public class CardMngr {
return responseAPDU;
}
- public ResponseAPDU sendAPDU(byte apdu[]) throws CardException {
+ public ResponseAPDU sendAPDU(byte[] apdu) throws CardException {
CommandAPDU commandAPDU = new CommandAPDU(apdu);
return sendAPDU(commandAPDU);
}
diff --git a/src/cz/crcs/ectester/reader/ECTesterReader.java b/src/cz/crcs/ectester/reader/ECTesterReader.java
index c94a544..e5e6061 100644
--- a/src/cz/crcs/ectester/reader/ECTesterReader.java
+++ b/src/cz/crcs/ectester/reader/ECTesterReader.java
@@ -26,10 +26,12 @@ import cz.crcs.ectester.applet.ECTesterApplet;
import cz.crcs.ectester.applet.EC_Consts;
import cz.crcs.ectester.common.cli.CLITools;
import cz.crcs.ectester.common.cli.Colors;
+import cz.crcs.ectester.common.ec.EC_Curve;
import cz.crcs.ectester.common.output.OutputLogger;
import cz.crcs.ectester.common.output.TestWriter;
import cz.crcs.ectester.common.util.ByteUtil;
import cz.crcs.ectester.common.util.CardUtil;
+import cz.crcs.ectester.common.util.ECUtil;
import cz.crcs.ectester.common.util.FileUtil;
import cz.crcs.ectester.data.EC_Store;
import cz.crcs.ectester.reader.command.Command;
@@ -46,14 +48,13 @@ import javax.smartcardio.CardException;
import javax.smartcardio.ResponseAPDU;
import javax.xml.parsers.ParserConfigurationException;
import java.io.*;
+import java.math.BigInteger;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.security.Security;
-import java.util.Arrays;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Scanner;
+import java.security.spec.ECParameterSpec;
+import java.util.*;
import java.util.jar.Manifest;
import static cz.crcs.ectester.applet.EC_Consts.KeyAgreement_ALG_EC_SVDP_DH;
@@ -331,10 +332,15 @@ public class ECTesterReader {
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().longOpt("fixed").desc("Generate key(s) only once, keep them for later operations.").build());
+ opts.addOption(Option.builder().longOpt("fixed-private").desc("Generate private key only once, keep it for later ECDH.").build());
+ opts.addOption(Option.builder().longOpt("fixed-public").desc("Generate public key only once, keep it for later ECDH.").build());
opts.addOption(Option.builder("f").longOpt("fresh").desc("Generate fresh keys (set domain parameters before every generation).").build());
+ opts.addOption(Option.builder().longOpt("time").desc("Output better timing values, by running command in dry run mode and normal mode, and subtracting the two.").build());
opts.addOption(Option.builder().longOpt("cleanup").desc("Send the cleanup command trigerring JCSystem.requestObjectDeletion() after some operations.").build());
opts.addOption(Option.builder("s").longOpt("simulate").desc("Simulate a card with jcardsim instead of using a terminal.").build());
opts.addOption(Option.builder("y").longOpt("yes").desc("Accept all warnings and prompts.").build());
+ opts.addOption(Option.builder("to").longOpt("test-options").desc("Test options to use.").hasArg().argName("options").build());
opts.addOption(Option.builder("ka").longOpt("ka-type").desc("Set KeyAgreement object [type], corresponds to JC.KeyAgreement constants.").hasArg().argName("type").optionalArg(true).build());
opts.addOption(Option.builder("sig").longOpt("sig-type").desc("Set Signature object [type], corresponds to JC.Signature constants.").hasArg().argName("type").optionalArg(true).build());
@@ -364,6 +370,8 @@ public class ECTesterReader {
System.out.println("\t" + line);
}
}
+ System.out.println();
+ System.out.println("For more information, look at the documentation at https://github.com/crocs-muni/ECTester.");
}
private void info() throws CardException {
@@ -439,7 +447,7 @@ public class ECTesterReader {
respWriter.outputResponse(allocate);
OutputStreamWriter keysFile = FileUtil.openFiles(cfg.outputs);
- keysFile.write("index;genTime;exportTime;pubW;privS\n");
+ keysFile.write("index;genTime[milli];exportTime[milli];pubW;privS\n");
int generated = 0;
int retry = 0;
@@ -450,7 +458,12 @@ public class ECTesterReader {
}
Command.Generate generate = new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_LOCAL);
+ long time = 0;
+ if (cfg.time) {
+ time = -Command.dryRunTime(cardManager, generate, 2, respWriter);
+ }
Response.Generate response = generate.send();
+ time += response.getDuration();
respWriter.outputResponse(response);
Response.Export export = new Command.Export(cardManager, ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.KEY_BOTH, EC_Consts.PARAMETERS_KEYPAIR).send();
@@ -468,7 +481,7 @@ public class ECTesterReader {
String pub = ByteUtil.bytesToHex(export.getParameter(ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.PARAMETER_W), false);
String priv = ByteUtil.bytesToHex(export.getParameter(ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.PARAMETER_S), false);
- String line = String.format("%d;%d;%d;%s;%s\n", generated, response.getDuration() / 1000000, export.getDuration() / 1000000, pub, priv);
+ String line = String.format("%d;%d;%d;%s;%s\n", generated, time / 1000000, export.getDuration() / 1000000, pub, priv);
keysFile.write(line);
keysFile.flush();
generated++;
@@ -572,38 +585,63 @@ public class ECTesterReader {
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;
-
- List<Command> generate = new LinkedList<>();
- generate.add(new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_BOTH));
- if (cfg.anyPublicKey || cfg.anyPrivateKey || cfg.anyKey) {
- generate.add(Command.prepareKey(cardManager, EC_Store.getInstance(), cfg, ECTesterApplet.KEYPAIR_REMOTE));
- }
-
OutputStreamWriter out = null;
if (cfg.outputs != null) {
out = FileUtil.openFiles(cfg.outputs);
- out.write("index;time;pubW;privS;secret\n");
+ out.write("index;time[milli];pubW;privS;secret\n");
+ }
+
+ Response gen = new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_BOTH).send();
+ respWriter.outputResponse(gen);
+ if (cfg.anyPublicKey || cfg.anyKey) {
+ Response prep = Command.prepareKey(cardManager, EC_Store.getInstance(), cfg, ECTesterApplet.KEYPAIR_REMOTE).send();
+ respWriter.outputResponse(prep);
+ }
+ if (cfg.anyPrivateKey || cfg.anyKey) {
+ Response prep = Command.prepareKey(cardManager, EC_Store.getInstance(), cfg, ECTesterApplet.KEYPAIR_LOCAL).send();
+ respWriter.outputResponse(prep);
+ }
+
+ byte kp = ECTesterApplet.KEYPAIR_BOTH;
+ if (cfg.fixedPrivate || cfg.anyPrivateKey) {
+ kp ^= ECTesterApplet.KEYPAIR_LOCAL;
+ }
+ if (cfg.fixedPublic || cfg.anyPublicKey) {
+ kp ^= ECTesterApplet.KEYPAIR_REMOTE;
+ }
+ if (cfg.fixedKey || cfg.anyKey) {
+ kp = 0;
+ }
+
+ Command generate = null;
+ if (kp != 0) {
+ generate = new Command.Generate(cardManager, kp);
}
int retry = 0;
int done = 0;
while (done < cfg.ECKACount) {
- List<Response> ecdh = Command.sendAll(generate);
- for (Response r : ecdh) {
- respWriter.outputResponse(r);
+ if (generate != null) {
+ Response regen = generate.send();
+ respWriter.outputResponse(regen);
}
Response.Export export = new Command.Export(cardManager, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.KEY_BOTH, EC_Consts.PARAMETERS_KEYPAIR).send();
respWriter.outputResponse(export);
- byte pubkey_bytes[] = export.getParameter(pubkey, EC_Consts.PARAMETER_W);
- byte privkey_bytes[] = export.getParameter(privkey, EC_Consts.PARAMETER_S);
+ byte[] pubkey_bytes = export.getParameter(ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.PARAMETER_W);
+ byte[] privkey_bytes = export.getParameter(ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.PARAMETER_S);
+
+ Command.ECDH perform = new Command.ECDH(cardManager, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_TRUE, EC_Consts.TRANSFORMATION_NONE, cfg.ECKAType);
- Response.ECDH perform = new Command.ECDH(cardManager, pubkey, privkey, ECTesterApplet.EXPORT_TRUE, EC_Consts.TRANSFORMATION_NONE, cfg.ECKAType).send();
- respWriter.outputResponse(perform);
+ long time = 0;
+ if (cfg.time) {
+ time = -Command.dryRunTime(cardManager, perform, 2, respWriter);
+ }
+
+ Response.ECDH result = perform.send();
+ respWriter.outputResponse(result);
- if (!perform.successful() || !perform.hasSecret()) {
+ if (!result.successful() || !result.hasSecret()) {
if (retry < 10) {
++retry;
continue;
@@ -614,7 +652,9 @@ public class ECTesterReader {
}
if (out != null) {
- out.write(String.format("%d;%d;%s;%s;%s\n", done, perform.getDuration() / 1000000, ByteUtil.bytesToHex(pubkey_bytes, false), ByteUtil.bytesToHex(privkey_bytes, false), ByteUtil.bytesToHex(perform.getSecret(), false)));
+ time += result.getDuration();
+
+ out.write(String.format("%d;%d;%s;%s;%s\n", done, time / 1000000, ByteUtil.bytesToHex(pubkey_bytes, false), ByteUtil.bytesToHex(privkey_bytes, false), ByteUtil.bytesToHex(result.getSecret(), false)));
}
++done;
@@ -636,7 +676,7 @@ public class ECTesterReader {
*/
private void ecdsa() throws CardException, IOException {
//read file, if asked to sign
- byte[] data = null;
+ byte[] data;
if (cfg.input != null) {
File in = new File(cfg.input);
long len = in.length();
@@ -644,6 +684,10 @@ public class ECTesterReader {
throw new FileNotFoundException(cfg.input);
}
data = Files.readAllBytes(in.toPath());
+ } else {
+ Random rand = new Random();
+ data = new byte[32];
+ rand.nextBytes(data);
}
Command generate;
@@ -667,18 +711,56 @@ public class ECTesterReader {
OutputStreamWriter out = FileUtil.openFiles(cfg.outputs);
if (out != null) {
- out.write("index;time;signature\n");
+ out.write("index;signTime[milli];verifyTime[milli];data;pubW;privS;signature;nonce;valid\n");
+ }
+
+ Command.Export export = new Command.Export(cardManager, ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.KEY_BOTH, EC_Consts.PARAMETERS_KEYPAIR);
+ Response.Export exported = null;
+ if (cfg.fixedKey) {
+ respWriter.outputResponse(generate.send());
+ exported = export.send();
+ respWriter.outputResponse(exported);
}
int retry = 0;
int done = 0;
while (done < cfg.ECDSACount) {
- respWriter.outputResponse(generate.send());
+ if (!cfg.fixedKey) {
+ respWriter.outputResponse(generate.send());
+ exported = export.send();
+ respWriter.outputResponse(exported);
+ }
+
+ Command.ECDSA_sign sign = new Command.ECDSA_sign(cardManager, ECTesterApplet.KEYPAIR_LOCAL, cfg.ECDSAType, ECTesterApplet.EXPORT_TRUE, data);
- Response.ECDSA perform = new Command.ECDSA(cardManager, ECTesterApplet.KEYPAIR_LOCAL, cfg.ECDSAType, ECTesterApplet.EXPORT_TRUE, data).send();
- respWriter.outputResponse(perform);
+ long signTime = 0;
+ if (cfg.time) {
+ signTime = -Command.dryRunTime(cardManager, sign, 2, respWriter);
+ }
- if (!perform.successful() || !perform.hasSignature()) {
+ Response.ECDSA signResp = sign.send();
+ signTime += signResp.getDuration();
+ respWriter.outputResponse(signResp);
+ if (!signResp.successful() || !signResp.hasSignature()) {
+ if (retry < 10) {
+ ++retry;
+ continue;
+ } else {
+ System.err.println(Colors.error("Couldn't obtain ECDSA signature from card response."));
+ break;
+ }
+ }
+ byte[] signature = signResp.getSignature();
+ Command.ECDSA_verify verify = new Command.ECDSA_verify(cardManager, ECTesterApplet.KEYPAIR_LOCAL, cfg.ECDSAType, data, signature);
+ long verifyTime = 0;
+ if (cfg.time) {
+ verifyTime = -Command.dryRunTime(cardManager, verify, 2, respWriter);
+ }
+ Response.ECDSA verifyResp = verify.send();
+ verifyTime += verifyResp.getDuration();
+ respWriter.outputResponse(verifyResp);
+
+ if (verifyResp.error()) {
if (retry < 10) {
++retry;
continue;
@@ -689,7 +771,20 @@ public class ECTesterReader {
}
if (out != null) {
- out.write(String.format("%d;%d;%s\n", done, perform.getDuration() / 1000000, ByteUtil.bytesToHex(perform.getSignature(), false)));
+ String pub = ByteUtil.bytesToHex(exported.getParameter(ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.PARAMETER_W), false);
+ String priv = ByteUtil.bytesToHex(exported.getParameter(ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.PARAMETER_S), false);
+ String dataString = (cfg.input != null) ? "" : ByteUtil.bytesToHex(data, false);
+ BigInteger privkey = new BigInteger(1, exported.getParameter(ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.PARAMETER_S));
+ EC_Curve actualCurve = Command.findCurve(EC_Store.getInstance(), cfg, cfg.bits, keyClass);
+ String k = "";
+ if (actualCurve != null) {
+ ECParameterSpec params = actualCurve.toSpec();
+ BigInteger kValue = ECUtil.recoverSignatureNonce(signature, data, privkey, params, CardUtil.getSigHashAlgo(cfg.ECDSAType));
+ if (kValue != null) {
+ k = ByteUtil.bytesToHex(kValue.toByteArray(), false);
+ }
+ }
+ out.write(String.format("%d;%d;%d;%s;%s;%s;%s;%s;%d\n", done, signTime / 1000000, verifyTime / 1000000, dataString, pub, priv, ByteUtil.bytesToHex(signature, false), k, verifyResp.successful() ? 1 : 0));
}
++done;
@@ -733,6 +828,9 @@ public class ECTesterReader {
public String key;
public boolean anyKeypart = false;
+ public boolean fixedKey = false;
+ public boolean fixedPrivate = false;
+ public boolean fixedPublic = false;
public String log;
@@ -740,6 +838,7 @@ public class ECTesterReader {
public String input;
public String[] outputs;
public boolean fresh = false;
+ public boolean time = false;
public boolean cleanup = false;
public boolean simulate = false;
public boolean yes = false;
@@ -756,6 +855,7 @@ public class ECTesterReader {
public byte ECKAType = KeyAgreement_ALG_EC_SVDP_DH;
public int ECDSACount;
public byte ECDSAType = Signature_ALG_ECDSA_SHA;
+ public Set<String> testOptions;
/**
* Reads and validates options, also sets defaults.
@@ -786,6 +886,9 @@ public class ECTesterReader {
key = cli.getOptionValue("key");
anyKey = (key != null) || (namedKey != null);
anyKeypart = anyKey || anyPublicKey || anyPrivateKey;
+ fixedKey = cli.hasOption("fixed");
+ fixedPrivate = cli.hasOption("fixed-private");
+ fixedPublic = cli.hasOption("fixed-public");
if (cli.hasOption("log")) {
log = cli.getOptionValue("log", String.format("ECTESTER_log_%d.log", System.currentTimeMillis() / 1000));
@@ -795,6 +898,7 @@ public class ECTesterReader {
input = cli.getOptionValue("input");
outputs = cli.getOptionValues("output");
fresh = cli.hasOption("fresh");
+ time = cli.hasOption("time");
cleanup = cli.hasOption("cleanup");
simulate = cli.hasOption("simulate");
yes = cli.hasOption("yes");
@@ -807,7 +911,7 @@ public class ECTesterReader {
}
format = cli.getOptionValue("format");
- String formats[] = new String[]{"text", "xml", "yaml", "yml"};
+ String[] formats = new String[]{"text", "xml", "yaml", "yml"};
if (format != null && !Arrays.asList(formats).contains(format)) {
System.err.println(Colors.error("Wrong output format " + format + ". Should be one of " + Arrays.toString(formats)));
return false;
@@ -906,6 +1010,21 @@ public class ECTesterReader {
System.err.println(Colors.error("Unknown test suite " + testSuite + ". Should be one of: " + Arrays.toString(tests)));
return false;
}
+
+ String[] opts = cli.getOptionValue("test-options", "").split(",");
+ List<String> validOpts = Arrays.asList("preset");
+ testOptions = new HashSet<>();
+ for (String opt : opts) {
+ if (opt.equals("")) {
+ continue;
+ }
+ if (!validOpts.contains(opt)) {
+ System.err.println(Colors.error("Unknown test option " + opt + ". Should be one of: " + "preset."));
+ return false;
+ } else {
+ testOptions.add(opt);
+ }
+ }
} else if (cli.hasOption("ecdh")) {
if (primeField == binaryField) {
System.err.print(Colors.error("Need to specify field with -fp or -f2m. (not both)"));
diff --git a/src/cz/crcs/ectester/reader/command/Command.java b/src/cz/crcs/ectester/reader/command/Command.java
index a3560df..bf2441f 100644
--- a/src/cz/crcs/ectester/reader/command/Command.java
+++ b/src/cz/crcs/ectester/reader/command/Command.java
@@ -11,6 +11,7 @@ import cz.crcs.ectester.common.util.CardUtil;
import cz.crcs.ectester.data.EC_Store;
import cz.crcs.ectester.reader.CardMngr;
import cz.crcs.ectester.reader.ECTesterReader;
+import cz.crcs.ectester.reader.output.ResponseWriter;
import cz.crcs.ectester.reader.response.Response;
import javacard.security.KeyPair;
@@ -28,6 +29,11 @@ import java.util.List;
public abstract class Command implements Cloneable {
CommandAPDU cmd;
CardMngr cardManager;
+ // Workaround for a stupid Java bug that went unfixed for !12! years,
+ // and for the even more stupid module system, which cannot properly work
+ // with the fact that JCardSim has some java.* packages...
+ final byte[] GOD_DAMN_JAVA_BUG_6474858_AND_GOD_DAMN_JAVA_12_MODULE_SYSTEM = new byte[]{0};
+
Command(CardMngr cardManager) {
this.cardManager = cardManager;
@@ -54,23 +60,11 @@ public abstract class Command implements Cloneable {
return (Command) super.clone();
}
-
- /**
- * @param keyPair which keyPair/s (local/remote) to set curve domain parameters on
- * @param keyLength key length to choose
- * @param keyClass key class to choose
- * @return a Command to send in order to prepare the curve on the keypairs.
- * @throws IOException if curve file cannot be found/opened
- */
- public static Command prepareCurve(CardMngr cardManager, EC_Store dataStore, ECTesterReader.Config cfg, byte keyPair, short keyLength, byte keyClass) throws IOException {
-
+ public static EC_Curve findCurve(EC_Store dataStore, ECTesterReader.Config cfg, short keyLength, byte keyClass) throws IOException {
if (cfg.customCurve) {
- // Set custom curve (one of the SECG curves embedded applet-side)
- short domainParams = keyClass == KeyPair.ALG_EC_FP ? EC_Consts.PARAMETERS_DOMAIN_FP : EC_Consts.PARAMETERS_DOMAIN_F2M;
- return new Command.Set(cardManager, keyPair, EC_Consts.getCurve(keyLength, keyClass), domainParams, null);
+ byte curveId = EC_Consts.getCurve(keyLength, keyClass);
+ return dataStore.getObject(EC_Curve.class, "secg", CardUtil.getCurveName(curveId));
} else if (cfg.namedCurve != null) {
- // Set a named curve.
- // parse cfg.namedCurve -> cat / id | cat | id
EC_Curve curve = dataStore.getObject(EC_Curve.class, cfg.namedCurve);
if (curve == null) {
throw new IOException("Curve could no be found.");
@@ -81,34 +75,44 @@ public abstract class Command implements Cloneable {
if (curve.getField() != keyClass) {
throw new IOException("Curve field mismatch.");
}
-
- byte[] external = curve.flatten();
- if (external == null) {
- throw new IOException("Couldn't read named curve data.");
- }
- return new Command.Set(cardManager, keyPair, EC_Consts.CURVE_external, curve.getParams(), external);
+ return curve;
} else if (cfg.curveFile != null) {
- // Set curve loaded from a file
EC_Curve curve = new EC_Curve(null, keyLength, keyClass);
FileInputStream in = new FileInputStream(cfg.curveFile);
curve.readCSV(in);
in.close();
+ return curve;
+ } else {
+ return null;
+ }
+ }
+
+
+ /**
+ * @param keyPair which keyPair/s (local/remote) to set curve domain parameters on
+ * @param keyLength key length to choose
+ * @param keyClass key class to choose
+ * @return a Command to send in order to prepare the curve on the keypairs.
+ * @throws IOException if curve file cannot be found/opened
+ */
+ public static Command prepareCurve(CardMngr cardManager, EC_Store dataStore, ECTesterReader.Config cfg, byte keyPair, short keyLength, byte keyClass) throws IOException {
+ if (cfg.customCurve) {
+ // Set custom curve (one of the SECG curves embedded applet-side)
+ short domainParams = keyClass == KeyPair.ALG_EC_FP ? EC_Consts.PARAMETERS_DOMAIN_FP : EC_Consts.PARAMETERS_DOMAIN_F2M;
+ return new Command.Set(cardManager, keyPair, EC_Consts.getCurve(keyLength, keyClass), domainParams, null);
+ }
- byte[] external = curve.flatten();
- if (external == null) {
- throw new IOException("Couldn't read the curve file correctly.");
+ EC_Curve curve = findCurve(dataStore, cfg, keyLength, keyClass);
+ if ((curve == null || curve.flatten() == null) && (cfg.namedCurve != null || cfg.curveFile != null)) {
+ if (cfg.namedCurve != null) {
+ throw new IOException("Couldn't read named curve data.");
}
- return new Command.Set(cardManager, keyPair, EC_Consts.CURVE_external, curve.getParams(), external);
- } else {
- // Set default curve
- /* This command was generally causing problems for simulating on jcardsim.
- * Since there, .clearKey() resets all the keys values, even the domain.
- * This might break some other stuff.. But should not.
- */
- //commands.add(new Command.Clear(cardManager, keyPair));
+ throw new IOException("Couldn't read the curve file correctly.");
+ } else if (curve == null) {
return null;
}
+ return new Command.Set(cardManager, keyPair, EC_Consts.CURVE_external, curve.getParams(), curve.flatten());
}
@@ -196,6 +200,19 @@ public abstract class Command implements Cloneable {
return new Command.Set(cardManager, keyPair, EC_Consts.CURVE_external, params, data);
}
+ public static long dryRunTime(CardMngr cardManager, Command cmd, int num, ResponseWriter respWriter) throws CardException {
+ long time = 0;
+ respWriter.outputResponse(new Command.SetDryRunMode(cardManager, ECTesterApplet.MODE_DRY_RUN).send());
+ for (int i = 0; i < num; ++i) {
+ Response dry = cmd.send();
+ respWriter.outputResponse(dry);
+ time += dry.getDuration();
+ }
+ time /= num;
+ respWriter.outputResponse(new Command.SetDryRunMode(cardManager, ECTesterApplet.MODE_NORMAL).send());
+ return time;
+ }
+
/**
*
*/
@@ -324,7 +341,7 @@ public abstract class Command implements Cloneable {
super(cardManager);
this.keyPair = keyPair;
- this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_CLEAR, keyPair, 0x00);
+ this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_CLEAR, keyPair, 0x00, GOD_DAMN_JAVA_BUG_6474858_AND_GOD_DAMN_JAVA_12_MODULE_SYSTEM);
}
@Override
@@ -474,7 +491,7 @@ public abstract class Command implements Cloneable {
super(cardManager);
this.keyPair = keyPair;
- this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_GENERATE, keyPair, 0);
+ this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_GENERATE, keyPair, 0, GOD_DAMN_JAVA_BUG_6474858_AND_GOD_DAMN_JAVA_12_MODULE_SYSTEM);
}
@Override
@@ -846,7 +863,7 @@ public abstract class Command implements Cloneable {
public Cleanup(CardMngr cardManager) {
super(cardManager);
- this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_CLEANUP, 0, 0);
+ this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_CLEANUP, 0, 0, GOD_DAMN_JAVA_BUG_6474858_AND_GOD_DAMN_JAVA_12_MODULE_SYSTEM);
}
@Override
@@ -874,7 +891,7 @@ public abstract class Command implements Cloneable {
public GetInfo(CardMngr cardManager) {
super(cardManager);
- this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_GET_INFO, 0, 0);
+ this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_GET_INFO, 0, 0, GOD_DAMN_JAVA_BUG_6474858_AND_GOD_DAMN_JAVA_12_MODULE_SYSTEM);
}
@Override
@@ -890,5 +907,36 @@ public abstract class Command implements Cloneable {
return "Get applet info";
}
}
+
+ /**
+ *
+ */
+ public static class SetDryRunMode extends Command {
+ private byte dryRunMode;
+
+ /**
+ * @param cardManager
+ * @param dryRunMode
+ */
+ public SetDryRunMode(CardMngr cardManager, byte dryRunMode) {
+ super(cardManager);
+ this.dryRunMode = dryRunMode;
+
+ this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_SET_DRY_RUN_MODE, dryRunMode, 0, GOD_DAMN_JAVA_BUG_6474858_AND_GOD_DAMN_JAVA_12_MODULE_SYSTEM);
+ }
+
+ @Override
+ public Response.SetDryRunMode send() throws CardException {
+ long elapsed = -System.nanoTime();
+ ResponseAPDU response = cardManager.send(cmd);
+ elapsed += System.nanoTime();
+ return new Response.SetDryRunMode(response, getDescription(), elapsed);
+ }
+
+ @Override
+ public String getDescription() {
+ return (dryRunMode == ECTesterApplet.MODE_NORMAL ? "Disable" : "Enable") + " dry run mode";
+ }
+ }
}
diff --git a/src/cz/crcs/ectester/reader/output/TextTestWriter.java b/src/cz/crcs/ectester/reader/output/TextTestWriter.java
index e89d403..2775647 100644
--- a/src/cz/crcs/ectester/reader/output/TextTestWriter.java
+++ b/src/cz/crcs/ectester/reader/output/TextTestWriter.java
@@ -52,6 +52,7 @@ public class TextTestWriter extends BaseTextTestWriter {
sb.append("═══ ").append(Colors.underline("Card ATR:")).append(" ").append(ByteUtil.bytesToHex(cardSuite.getCard().getATR().getBytes(), false)).append(System.lineSeparator());
sb.append("═══ ").append(Colors.underline("JavaCard version:")).append(" ").append(info.getJavaCardVersion()).append(System.lineSeparator());
sb.append("═══ ").append(Colors.underline("Array sizes (apduBuf, ram, ram2, apduArr):")).append(" ").append(String.format("%d %d %d %d", info.getApduBufferLength(), info.getRamArrayLength(), info.getRamArray2Length(), info.getApduArrayLength())).append(System.lineSeparator());
+ sb.append("═══ ").append(Colors.underline("Test options:")).append(" ").append(String.join(",", cardSuite.getCfg().testOptions)).append(System.lineSeparator());
CardMngr.CPLC cplc = cardSuite.getCard().getCPLC();
if (!cplc.values().isEmpty()) {
sb.append("═══ ").append(Colors.underline("Card CPLC data:")).append(System.lineSeparator());
diff --git a/src/cz/crcs/ectester/reader/output/XMLTestWriter.java b/src/cz/crcs/ectester/reader/output/XMLTestWriter.java
index 9add072..fc41805 100644
--- a/src/cz/crcs/ectester/reader/output/XMLTestWriter.java
+++ b/src/cz/crcs/ectester/reader/output/XMLTestWriter.java
@@ -114,7 +114,7 @@ public class XMLTestWriter extends BaseXMLTestWriter {
Response.GetInfo info = new Command.GetInfo(card).send();
result.setAttribute("version", info.getVersion());
result.setAttribute("javacard", String.format("%.1f", info.getJavaCardVersion()));
- result.setAttribute("base", String.format("%#x",info.getBase()));
+ result.setAttribute("base", String.format("%#x", info.getBase()));
result.setAttribute("cleanup", String.valueOf(info.getCleanupSupport()));
Element arrays = doc.createElement("arrays");
Element apduBuf = doc.createElement("length");
diff --git a/src/cz/crcs/ectester/reader/response/Response.java b/src/cz/crcs/ectester/reader/response/Response.java
index 235564e..6232423 100644
--- a/src/cz/crcs/ectester/reader/response/Response.java
+++ b/src/cz/crcs/ectester/reader/response/Response.java
@@ -474,11 +474,11 @@ public abstract class Response {
byte major = (byte) (jcVersion >> 8);
byte minor = (byte) (jcVersion & 0xff);
int minorSize;
- if (minor == 0) {
- minorSize = 1;
- } else {
- minorSize = (int) Math.ceil(Math.log10(minor));
- }
+ if (minor == 0) {
+ minorSize = 1;
+ } else {
+ minorSize = (int) Math.ceil(Math.log10(minor));
+ }
return (major + ((float) (minor) / (minorSize * 10)));
}
@@ -502,4 +502,16 @@ public abstract class Response {
return apduArrayLength;
}
}
+
+ /**
+ *
+ */
+ public static class SetDryRunMode extends Response {
+
+ public SetDryRunMode(ResponseAPDU response, String description, long time) {
+ super(response, description, time);
+
+ parse(1, 0);
+ }
+ }
}
diff --git a/src/cz/crcs/ectester/reader/test/CardCofactorSuite.java b/src/cz/crcs/ectester/reader/test/CardCofactorSuite.java
index 172c8af..982e07a 100644
--- a/src/cz/crcs/ectester/reader/test/CardCofactorSuite.java
+++ b/src/cz/crcs/ectester/reader/test/CardCofactorSuite.java
@@ -25,7 +25,8 @@ import static cz.crcs.ectester.common.test.Result.ExpectedValue;
*/
public class CardCofactorSuite extends CardTestSuite {
public CardCofactorSuite(TestWriter writer, ECTesterReader.Config cfg, CardMngr cardManager) {
- super(writer, cfg, cardManager, "cofactor", "The cofactor test suite tests whether the card correctly rejects points on the curve but not in the subgroup generated by the generator during ECDH.");
+ super(writer, cfg, cardManager, "cofactor", "The cofactor test suite tests whether the card correctly rejects points on the curve",
+ "but not in the subgroup generated by the generator(so of small order, dividing the cofactor) during ECDH.");
}
@Override
@@ -38,7 +39,7 @@ public class CardCofactorSuite extends CardTestSuite {
Test allocate = CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), ExpectedValue.SUCCESS);
Test set = CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), ExpectedValue.SUCCESS);
- Test generate = CommandTest.expect(new Command.Generate(this.card, ECTesterApplet.KEYPAIR_LOCAL), ExpectedValue.SUCCESS);
+ Test generate = genOrPreset(curve, ExpectedValue.SUCCESS);
Test prepare = CompoundTest.all(ExpectedValue.SUCCESS, "Prepare and generate keypair on " + curve.getId() + ".", allocate, set, generate);
diff --git a/src/cz/crcs/ectester/reader/test/CardCompositeSuite.java b/src/cz/crcs/ectester/reader/test/CardCompositeSuite.java
index 4bf9290..93d50e8 100644
--- a/src/cz/crcs/ectester/reader/test/CardCompositeSuite.java
+++ b/src/cz/crcs/ectester/reader/test/CardCompositeSuite.java
@@ -25,7 +25,8 @@ import static cz.crcs.ectester.common.test.Result.ExpectedValue;
public class CardCompositeSuite extends CardTestSuite {
public CardCompositeSuite(TestWriter writer, ECTesterReader.Config cfg, CardMngr cardManager) {
- super(writer, cfg, cardManager, "composite", "The composite suite runs ECDH over curves with composite order. This should generally fail, as using such a curve is unsafe.");
+ super(writer, cfg, cardManager, "composite", "The composite suite runs ECDH over curves with composite order.",
+ "Various types of compositeness is tested: smooth numbers, Carmichael pseudoprime, prime square, product of two large primes.");
}
@Override
@@ -48,11 +49,18 @@ public class CardCompositeSuite extends CardTestSuite {
}
tests.add(allocate);
tests.add(CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), ExpectedValue.ANY));
- tests.add(CommandTest.expect(new Command.Generate(this.card, ECTesterApplet.KEYPAIR_LOCAL), ExpectedValue.ANY));
+
+ String name;
+ if (cfg.testOptions.contains("preset")) {
+ name = "preset semi-random key";
+ } else {
+ name = "generated key";
+ }
+ tests.add(genOrPreset(curve, ExpectedValue.ANY));
for (EC_Key key : curveKeys.getValue()) {
Command ecdhCommand = new Command.ECDH_direct(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, EC_Consts.TRANSFORMATION_NONE, EC_Consts.KeyAgreement_ALG_EC_SVDP_DH, key.flatten());
Test ecdh = CommandTest.expect(ecdhCommand, ExpectedValue.FAILURE, "Card correctly rejected to do ECDH over a composite order curve.", "Card incorrectly does ECDH over a composite order curve, leaks bits of private key.");
- tests.add(CompoundTest.greedyAllTry(ExpectedValue.SUCCESS, "Composite test of " + curve.getId() + ", " + key.getDesc(), ecdh));
+ tests.add(CompoundTest.greedyAllTry(ExpectedValue.SUCCESS, "Composite test of " + curve.getId() + ", with " + name + ", " + key.getDesc(), ecdh));
}
doTest(CompoundTest.all(ExpectedValue.SUCCESS, "Composite test of " + curve.getId() + ".", tests.toArray(new Test[0])));
}
diff --git a/src/cz/crcs/ectester/reader/test/CardCompressionSuite.java b/src/cz/crcs/ectester/reader/test/CardCompressionSuite.java
index 291cc04..c86c0b1 100644
--- a/src/cz/crcs/ectester/reader/test/CardCompressionSuite.java
+++ b/src/cz/crcs/ectester/reader/test/CardCompressionSuite.java
@@ -29,7 +29,9 @@ import java.util.Map;
public class CardCompressionSuite extends CardTestSuite {
public CardCompressionSuite(TestWriter writer, ECTesterReader.Config cfg, CardMngr cardManager) {
super(writer, cfg, cardManager, "compression", "The compression test suite tests cards support for compressed points in ECDH (as per ANSI X9.62).",
- "It also tests for handling of bogus input by using the point at infinity and a hybrid point with the y coordinate corrupted.");
+ "It also tests for handling of bogus input in ECDH by using the point at infinity and a hybrid point with the y coordinate corrupted.",
+ "It also tests handling of compressed point in ECDH, where the x coordinate is invalid and therefore",
+ "a quadratic non-residue will be computed and (square root-ed) during decompression.");
}
@Override
diff --git a/src/cz/crcs/ectester/reader/test/CardDefaultSuite.java b/src/cz/crcs/ectester/reader/test/CardDefaultSuite.java
index 91f9ef6..ebece61 100644
--- a/src/cz/crcs/ectester/reader/test/CardDefaultSuite.java
+++ b/src/cz/crcs/ectester/reader/test/CardDefaultSuite.java
@@ -10,7 +10,6 @@ import cz.crcs.ectester.common.util.CardUtil;
import cz.crcs.ectester.reader.CardMngr;
import cz.crcs.ectester.reader.ECTesterReader;
import cz.crcs.ectester.reader.command.Command;
-import cz.crcs.ectester.reader.response.Response;
import javacard.security.KeyPair;
import java.util.LinkedList;
@@ -29,7 +28,7 @@ import static cz.crcs.ectester.common.test.Result.Value;
public class CardDefaultSuite extends CardTestSuite {
public CardDefaultSuite(TestWriter writer, ECTesterReader.Config cfg, CardMngr cardManager) {
- super(writer, cfg, cardManager, "default", "The default test suite tests basic support of ECDH and ECDSA.");
+ super(writer, cfg, cardManager, "default", "The default test suite tests basic support and performance of ECDH and ECDSA.");
}
@Override
@@ -83,7 +82,7 @@ public class CardDefaultSuite extends CardTestSuite {
Test compound;
if (ka.ok()) {
- Test perfTest = runTest(PerformanceTest.repeat(ecdh, 10));
+ Test perfTest = runTest(PerformanceTest.repeat(this.card, ecdh, 10));
compound = runTest(CompoundTest.function(kaCallback, kaDesc, allocate, ka, kaCompressed, perfTest));
} else {
compound = runTest(CompoundTest.function(kaCallback, kaDesc, allocate, ka, kaCompressed));
@@ -114,10 +113,10 @@ public class CardDefaultSuite extends CardTestSuite {
Test compound;
if (expect.ok()) {
Command ecdsaSign = new Command.ECDSA_sign(this.card, ECTesterApplet.KEYPAIR_LOCAL, sigType, ECTesterApplet.EXPORT_TRUE, sigData);
- PerformanceTest signTest = runTest(PerformanceTest.repeat("Sign", ecdsaSign, 10));
+ PerformanceTest signTest = runTest(PerformanceTest.repeat(this.card, "Sign", ecdsaSign, 10));
byte[] signature = signTest.getResponses()[0].getParam(0);
Command ecdsaVerify = new Command.ECDSA_verify(this.card, ECTesterApplet.KEYPAIR_LOCAL, sigType, sigData, signature);
- PerformanceTest verifyTest = runTest(PerformanceTest.repeat("Verify", ecdsaVerify, 10));
+ PerformanceTest verifyTest = runTest(PerformanceTest.repeat(this.card, "Verify", ecdsaVerify, 10));
compound = runTest(CompoundTest.all(ExpectedValue.SUCCESS, signDesc, allocate, expect, signTest, verifyTest));
} else {
compound = runTest(CompoundTest.all(ExpectedValue.SUCCESS, signDesc, allocate, expect));
diff --git a/src/cz/crcs/ectester/reader/test/CardDegenerateSuite.java b/src/cz/crcs/ectester/reader/test/CardDegenerateSuite.java
index f434d4d..730c70b 100644
--- a/src/cz/crcs/ectester/reader/test/CardDegenerateSuite.java
+++ b/src/cz/crcs/ectester/reader/test/CardDegenerateSuite.java
@@ -25,7 +25,7 @@ public class CardDegenerateSuite extends CardTestSuite {
public CardDegenerateSuite(TestWriter writer, ECTesterReader.Config cfg, CardMngr cardManager) {
super(writer, cfg, cardManager, "degenerate", "The degenerate suite tests whether the card rejects points outside of the curve during ECDH.",
- "The tested points lie on a part of the plane for which some Edwards, Hessian and Huff form addition formulas work.");
+ "The tested points lie on a part of the plane for which some Edwards, Hessian and Huff form addition formulas degenerate into exponentiation in the base finite field.");
}
@Override
@@ -36,27 +36,32 @@ public class CardDegenerateSuite extends CardTestSuite {
EC_Curve curve = e.getKey();
List<EC_Key.Public> keys = e.getValue();
- Test allocate = CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), Result.ExpectedValue.SUCCESS);
+ Test allocate = runTest(CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), Result.ExpectedValue.SUCCESS));
+ if (!allocate.ok()) {
+ doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, "No support for " + curve.getId() + ".", allocate));
+ continue;
+ }
Test set = CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), Result.ExpectedValue.SUCCESS);
Test generate = CommandTest.expect(new Command.Generate(this.card, ECTesterApplet.KEYPAIR_LOCAL), Result.ExpectedValue.SUCCESS);
- Test prepare = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Prepare and generate keypair on " + curve.getId(), allocate, set, generate);
+ Test prepare = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Prepare and generate keypair on " + curve.getId() + ".", allocate, set, generate);
List<Test> ecdhTests = new LinkedList<>();
for (EC_Key.Public pub : keys) {
Test setPub = CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, pub.getParams(), pub.flatten()), Result.ExpectedValue.FAILURE);
- Test ecdh = CommandTest.expect(new Command.ECDH(this.card, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, EC_Consts.TRANSFORMATION_NONE, EC_Consts.KeyAgreement_ALG_EC_SVDP_DH), Result.ExpectedValue.FAILURE);
+ Test ecdh = CommandTest.expect(new Command.ECDH(this.card, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, EC_Consts.TRANSFORMATION_NONE, EC_Consts.KeyAgreement_ALG_EC_SVDP_DH), Result.ExpectedValue.FAILURE, "Card correctly rejected point on degenerate curve.", "Card incorrectly accepted point on degenerate curve.");
Test objectEcdh = CompoundTest.any(Result.ExpectedValue.SUCCESS, CardUtil.getKATypeString(EC_Consts.KeyAgreement_ALG_EC_SVDP_DH) + " test with degenerate pubkey.", setPub, ecdh);
Command ecdhCommand = new Command.ECDH_direct(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, EC_Consts.TRANSFORMATION_NONE, EC_Consts.KeyAgreement_ALG_EC_SVDP_DH, pub.flatten());
Test rawEcdh = CommandTest.expect(ecdhCommand, Result.ExpectedValue.FAILURE, "Card correctly rejected point on degenerate curve.", "Card incorrectly accepted point on degenerate curve.");
ecdhTests.add(CompoundTest.all(Result.ExpectedValue.SUCCESS, pub.getId() + " degenerate key test.", objectEcdh, rawEcdh));
+ //TODO: actually get the result of ECDH here, as well as export privkey and compare to exponentiation in Fp^*.
}
- Test ecdh = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Perform ECDH with degenerate public points", ecdhTests.toArray(new Test[0]));
+ Test ecdh = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Perform ECDH with degenerate public points.", ecdhTests.toArray(new Test[0]));
if (cfg.cleanup) {
Test cleanup = CommandTest.expect(new Command.Cleanup(this.card), Result.ExpectedValue.ANY);
- doTest(CompoundTest.greedyAllTry(Result.ExpectedValue.SUCCESS, "Degenerate curve test of " + curve.getId(), prepare, ecdh, cleanup));
+ doTest(CompoundTest.greedyAllTry(Result.ExpectedValue.SUCCESS, "Degenerate curve test of " + curve.getId() + ".", prepare, ecdh, cleanup));
} else {
- doTest(CompoundTest.greedyAllTry(Result.ExpectedValue.SUCCESS, "Degenerate curve test of " + curve.getId(), prepare, ecdh));
+ doTest(CompoundTest.greedyAllTry(Result.ExpectedValue.SUCCESS, "Degenerate curve test of " + curve.getId() + ".", prepare, ecdh));
}
}
diff --git a/src/cz/crcs/ectester/reader/test/CardEdgeCasesSuite.java b/src/cz/crcs/ectester/reader/test/CardEdgeCasesSuite.java
index ccec401..53f3b6b 100644
--- a/src/cz/crcs/ectester/reader/test/CardEdgeCasesSuite.java
+++ b/src/cz/crcs/ectester/reader/test/CardEdgeCasesSuite.java
@@ -33,6 +33,7 @@ public class CardEdgeCasesSuite extends CardTestSuite {
public CardEdgeCasesSuite(TestWriter writer, ECTesterReader.Config cfg, CardMngr cardManager) {
super(writer, cfg, cardManager, "edge-cases", "The edge-cases test suite tests various inputs to ECDH which may cause an implementation to achieve a certain edge-case state during it.",
"Some of the data is from the google/Wycheproof project. Tests include CVE-2017-10176 and CVE-2017-8932.",
+ "Also tests values of the private key and public key that would trigger the OpenSSL modualr multiplication bug on the P-256 curve.",
"Various edge private key values are also tested.");
}
@@ -91,7 +92,7 @@ public class CardEdgeCasesSuite extends CardTestSuite {
int firstDiff = ByteUtil.diffBytes(dh.getSecret(), 0, value.getData(0), 0, dh.secretLength());
System.err.println(ByteUtil.bytesToHex(dh.getSecret()));
System.err.println(ByteUtil.bytesToHex(value.getData(0)));
- return new Result(Result.Value.FAILURE, "ECDH derived secret does not match the test-vector, first difference was at byte " + String.valueOf(firstDiff) + ".");
+ return new Result(Result.Value.FAILURE, "ECDH derived secret does not match the test-vector, first difference was at byte " + firstDiff + ".");
}
return new Result(Result.Value.SUCCESS);
}
@@ -104,6 +105,10 @@ public class CardEdgeCasesSuite extends CardTestSuite {
curveTests.add(one);
}
+ if (cfg.cleanup) {
+ curveTests.add(CommandTest.expect(new Command.Cleanup(this.card), Result.ExpectedValue.ANY));
+ }
+
Test curveTest = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Tests", curveTests.toArray(new Test[0]));
groupTests.add(CompoundTest.greedyAllTry(Result.ExpectedValue.SUCCESS, "Tests on " + curve.getId() + ".", prepareCurve, curveTest));
}
@@ -149,8 +154,22 @@ public class CardEdgeCasesSuite extends CardTestSuite {
continue;
}
Test set = CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), Result.ExpectedValue.SUCCESS);
- Test generate = CommandTest.expect(new Command.Generate(this.card, ECTesterApplet.KEYPAIR_LOCAL), Result.ExpectedValue.SUCCESS);
- Test setup = CompoundTest.all(Result.ExpectedValue.SUCCESS, "KeyPair setup.", key, set, generate);
+ Test generate = genOrPreset(curve, Result.ExpectedValue.SUCCESS);
+ CommandTest export = CommandTest.expect(new Command.Export(this.card, ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.KEY_PUBLIC, EC_Consts.PARAMETER_W), Result.ExpectedValue.SUCCESS);
+ Test setup = runTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, "KeyPair setup.", key, set, generate, export));
+
+ byte[] pParam = curve.getParam(EC_Consts.PARAMETER_FP)[0];
+ BigInteger p = new BigInteger(1, pParam);
+ byte[] wParam = ((Response.Export) export.getResponse()).getParameter(ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.PARAMETER_W);
+ byte[] yValue = new byte[(wParam.length - 1) / 2];
+ System.arraycopy(wParam, (wParam.length / 2) + 1, yValue, 0, yValue.length);
+ BigInteger y = new BigInteger(1, yValue);
+ BigInteger negY = p.subtract(y);
+ byte[] newY = ECUtil.toByteArray(negY, curve.getBits());
+ System.arraycopy(newY, 0, wParam, (wParam.length / 2) + 1, newY.length);
+
+ EC_Params negYParams = makeParams(newY);
+ Test negYTest = ecdhTest(new Command.Set(this.card, ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.CURVE_external, negYParams.getParams(), negYParams.flatten()), "ECDH with pubkey negated.", Result.ExpectedValue.FAILURE, Result.ExpectedValue.FAILURE);
Test zeroS = ecdhTest(new Command.Transform(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, EC_Consts.PARAMETER_S, EC_Consts.TRANSFORMATION_ZERO), "ECDH with S = 0.", Result.ExpectedValue.FAILURE, Result.ExpectedValue.FAILURE);
Test oneS = ecdhTest(new Command.Transform(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, EC_Consts.PARAMETER_S, EC_Consts.TRANSFORMATION_ONE), "ECDH with S = 1.", Result.ExpectedValue.FAILURE, Result.ExpectedValue.FAILURE);
@@ -164,8 +183,21 @@ public class CardEdgeCasesSuite extends CardTestSuite {
BigInteger full = BigInteger.valueOf(1).shiftLeft(R.bitLength() - 1).subtract(BigInteger.ONE);
+ BigInteger alternate = full;
+ for (int i = 0; i < R.bitLength(); i += 2) {
+ alternate = alternate.clearBit(i);
+ }
+
+ BigInteger alternateOther = alternate.xor(full);
+
+ EC_Params alternateParams = makeParams(alternate);
+ Test alternateS = ecdhTest(new Command.Set(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, alternateParams.getParams(), alternateParams.flatten()), "ECDH with S = 101010101...01010.", Result.ExpectedValue.SUCCESS, Result.ExpectedValue.SUCCESS);
+
+ EC_Params alternateOtherParams = makeParams(alternateOther);
+ Test alternateOtherS = ecdhTest(new Command.Set(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, alternateOtherParams.getParams(), alternateOtherParams.flatten()), "ECDH with S = 010101010...10101.", Result.ExpectedValue.SUCCESS, Result.ExpectedValue.SUCCESS);
+
EC_Params fullParams = makeParams(full);
- Test fullS = ecdhTest(new Command.Set(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, fullParams.getParams(), fullParams.flatten()), "ECDH with S = 2^((log2 r) - 1) - 1.", Result.ExpectedValue.SUCCESS, Result.ExpectedValue.SUCCESS);
+ Test fullS = ecdhTest(new Command.Set(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, fullParams.getParams(), fullParams.flatten()), "ECDH with S = 111111111...11111 (but < r).", Result.ExpectedValue.SUCCESS, Result.ExpectedValue.SUCCESS);
EC_Params smallerParams = makeParams(smaller);
Test smallerS = ecdhTest(new Command.Set(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, smallerParams.getParams(), smallerParams.flatten()), "ECDH with S < r.", Result.ExpectedValue.SUCCESS, Result.ExpectedValue.SUCCESS);
@@ -191,20 +223,22 @@ public class CardEdgeCasesSuite extends CardTestSuite {
BigInteger krm1 = kr.subtract(BigInteger.ONE);
BigInteger krp1 = kr.add(BigInteger.ONE);
+ Result.ExpectedValue kExpected = K.equals(BigInteger.ONE) ? Result.ExpectedValue.SUCCESS : Result.ExpectedValue.FAILURE;
+
EC_Params krParams = makeParams(kr);
Test krS /*ONE!*/ = ecdhTest(new Command.Set(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, krParams.getParams(), krParams.flatten()), "ECDH with S = k * r.", Result.ExpectedValue.FAILURE, Result.ExpectedValue.FAILURE);
EC_Params krm1Params = makeParams(krm1);
- Test krm1S = ecdhTest(new Command.Set(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, krm1Params.getParams(), krm1Params.flatten()), "ECDH with S = (k * r) - 1.", Result.ExpectedValue.FAILURE, Result.ExpectedValue.FAILURE);
+ Test krm1S = ecdhTest(new Command.Set(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, krm1Params.getParams(), krm1Params.flatten()), "ECDH with S = (k * r) - 1.", kExpected, kExpected);
EC_Params krp1Params = makeParams(krp1);
- Test krp1S = ecdhTest(new Command.Set(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, krp1Params.getParams(), krp1Params.flatten()), "ECDH with S = (k * r) + 1.", Result.ExpectedValue.FAILURE, Result.ExpectedValue.FAILURE);
+ Test krp1S = ecdhTest(new Command.Set(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, krp1Params.getParams(), krp1Params.flatten()), "ECDH with S = (k * r) + 1.", Result.ExpectedValue.ANY, Result.ExpectedValue.ANY);
if (cfg.cleanup) {
Test cleanup = CommandTest.expect(new Command.Cleanup(this.card), Result.ExpectedValue.ANY);
- doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, "Tests with edge-case private key values over " + curve.getId() + ".", setup, zeroS, oneS, fullS, smallerS, exactS, largerS, rm1S, rp1S, krS, krm1S, krp1S, cleanup));
+ doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, "Tests with edge-case private key values over " + curve.getId() + ".", setup, negYTest, zeroS, oneS, alternateS, alternateOtherS, fullS, smallerS, exactS, largerS, rm1S, rp1S, krS, krm1S, krp1S, cleanup));
} else {
- doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, "Tests with edge-case private key values over " + curve.getId() + ".", setup, zeroS, oneS, fullS, smallerS, exactS, largerS, rm1S, rp1S, krS, krm1S, krp1S));
+ doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, "Tests with edge-case private key values over " + curve.getId() + ".", setup, negYTest, zeroS, oneS, alternateS, alternateOtherS, fullS, smallerS, exactS, largerS, rm1S, rp1S, krS, krm1S, krp1S));
}
}
@@ -249,7 +283,7 @@ public class CardEdgeCasesSuite extends CardTestSuite {
int i = 0;
for (BigInteger nearZero : zeros) {
EC_Params params = makeParams(nearZero);
- zeroTests[i++] = ecdhTest(new Command.Set(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, params.getParams(), params.flatten()), nearZero.toString(16), Result.ExpectedValue.ANY, Result.ExpectedValue.ANY);
+ zeroTests[i++] = ecdhTestBoth(new Command.Set(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, params.getParams(), params.flatten()), nearZero.toString(16), Result.ExpectedValue.SUCCESS, Result.ExpectedValue.SUCCESS);
}
Test zeroTest = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Near zero.", zeroTests);
@@ -257,7 +291,7 @@ public class CardEdgeCasesSuite extends CardTestSuite {
i = 0;
for (BigInteger nearP : ps) {
EC_Params params = makeParams(nearP);
- pTests[i++] = ecdhTest(new Command.Set(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, params.getParams(), params.flatten()), nearP.toString(16) + (nearP.compareTo(p) > 0 ? " (>p)" : " (<=p)"), Result.ExpectedValue.ANY, Result.ExpectedValue.ANY);
+ pTests[i++] = ecdhTestBoth(new Command.Set(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, params.getParams(), params.flatten()), nearP.toString(16) + (nearP.compareTo(p) > 0 ? " (>p)" : " (<=p)"), Result.ExpectedValue.SUCCESS, Result.ExpectedValue.SUCCESS);
}
Test pTest = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Near p.", pTests);
@@ -265,12 +299,23 @@ public class CardEdgeCasesSuite extends CardTestSuite {
i = 0;
for (BigInteger nearR : rs) {
EC_Params params = makeParams(nearR);
- rTests[i++] = ecdhTest(new Command.Set(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, params.getParams(), params.flatten()), nearR.toString(16) + (nearR.compareTo(r) > 0 ? " (>r)" : " (<=r)"), Result.ExpectedValue.ANY, Result.ExpectedValue.ANY);
+ if (nearR.compareTo(r) >= 0) {
+ rTests[i++] = ecdhTest(new Command.Set(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, params.getParams(), params.flatten()), nearR.toString(16) + " (>=r)", Result.ExpectedValue.FAILURE, Result.ExpectedValue.FAILURE);
+ } else {
+ rTests[i++] = ecdhTestBoth(new Command.Set(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, params.getParams(), params.flatten()), nearR.toString(16) + " (<r)", Result.ExpectedValue.SUCCESS, Result.ExpectedValue.SUCCESS);
+ }
}
Test rTest = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Near r.", rTests);
doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, "Test private key values near zero, near p and near/larger than the order.", setup, zeroTest, pTest, rTest));
}
+ private Test ecdhTestBoth(Command setPriv, String desc, Result.ExpectedValue setExpect, Result.ExpectedValue ecdhExpect) {
+ Test set = CommandTest.expect(setPriv, setExpect);
+ Test ecdh = CommandTest.expect(new Command.ECDH(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_TRUE, EC_Consts.TRANSFORMATION_NONE, EC_Consts.KeyAgreement_ALG_EC_SVDP_DH), ecdhExpect);
+
+ return CompoundTest.all(Result.ExpectedValue.SUCCESS, desc, set, ecdh);
+ }
+
private Test ecdhTest(Command setPriv, String desc, Result.ExpectedValue setExpect, Result.ExpectedValue ecdhExpect) {
Test set = CommandTest.expect(setPriv, setExpect);
Test ecdh = CommandTest.expect(new Command.ECDH(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_TRUE, EC_Consts.TRANSFORMATION_NONE, EC_Consts.KeyAgreement_ALG_EC_SVDP_DH), ecdhExpect);
diff --git a/src/cz/crcs/ectester/reader/test/CardInvalidSuite.java b/src/cz/crcs/ectester/reader/test/CardInvalidSuite.java
index 3b9e0e5..9c4b54c 100644
--- a/src/cz/crcs/ectester/reader/test/CardInvalidSuite.java
+++ b/src/cz/crcs/ectester/reader/test/CardInvalidSuite.java
@@ -40,11 +40,15 @@ public class CardInvalidSuite extends CardTestSuite {
EC_Curve curve = e.getKey();
List<EC_Key.Public> keys = e.getValue();
- Test allocate = CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), ExpectedValue.SUCCESS);
+ Test allocate = runTest(CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), ExpectedValue.SUCCESS));
+ if (!allocate.ok()) {
+ doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, "No support for " + curve.getId() + ".", allocate));
+ continue;
+ }
Test set = CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), ExpectedValue.SUCCESS);
Test generate = CommandTest.expect(new Command.Generate(this.card, ECTesterApplet.KEYPAIR_LOCAL), ExpectedValue.SUCCESS);
- Test prepare = CompoundTest.all(ExpectedValue.SUCCESS, "Prepare and generate keypair on " + curve.getId(), allocate, set, generate);
+ Test prepare = CompoundTest.all(ExpectedValue.SUCCESS, "Prepare and generate keypair on " + curve.getId() + ".", allocate, set, generate);
List<Test> ecdhTests = new LinkedList<>();
for (EC_Key.Public pub : keys) {
@@ -55,13 +59,13 @@ public class CardInvalidSuite extends CardTestSuite {
Test rawEcdh = CommandTest.expect(ecdhCommand, ExpectedValue.FAILURE, "Card correctly rejected point on invalid curve.", "Card incorrectly accepted point on invalid curve.");
ecdhTests.add(CompoundTest.all(Result.ExpectedValue.SUCCESS, pub.getId() + " invalid key test.", objectEcdh, rawEcdh));
}
- Test ecdh = CompoundTest.all(ExpectedValue.SUCCESS, "Perform ECDH with invalid public points", ecdhTests.toArray(new Test[0]));
+ Test ecdh = CompoundTest.all(ExpectedValue.SUCCESS, "Perform ECDH with invalid public points.", ecdhTests.toArray(new Test[0]));
if (cfg.cleanup) {
Test cleanup = CommandTest.expect(new Command.Cleanup(this.card), ExpectedValue.ANY);
- doTest(CompoundTest.greedyAllTry(ExpectedValue.SUCCESS, "Invalid curve test of " + curve.getId(), prepare, ecdh, cleanup));
+ doTest(CompoundTest.greedyAllTry(ExpectedValue.SUCCESS, "Invalid curve test of " + curve.getId() + ".", prepare, ecdh, cleanup));
} else {
- doTest(CompoundTest.greedyAllTry(ExpectedValue.SUCCESS, "Invalid curve test of " + curve.getId(), prepare, ecdh));
+ doTest(CompoundTest.greedyAllTry(ExpectedValue.SUCCESS, "Invalid curve test of " + curve.getId() + ".", prepare, ecdh));
}
}
}
diff --git a/src/cz/crcs/ectester/reader/test/CardMiscSuite.java b/src/cz/crcs/ectester/reader/test/CardMiscSuite.java
index a2ce2ce..b1163c3 100644
--- a/src/cz/crcs/ectester/reader/test/CardMiscSuite.java
+++ b/src/cz/crcs/ectester/reader/test/CardMiscSuite.java
@@ -56,7 +56,7 @@ public class CardMiscSuite extends CardTestSuite {
}
Test set = CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), Result.ExpectedValue.SUCCESS);
- Test generate = CommandTest.expect(new Command.Generate(this.card, ECTesterApplet.KEYPAIR_BOTH), Result.ExpectedValue.ANY);
+ Test generate = genOrPreset(curve, Result.ExpectedValue.ANY);
Test ka = CommandTest.expect(new Command.ECDH(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.TRANSFORMATION_NONE, EC_Consts.KeyAgreement_ALG_EC_SVDP_DH), expected);
Test sig = CommandTest.expect(new Command.ECDSA(this.card, ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.Signature_ALG_ECDSA_SHA, ECTesterApplet.EXPORT_FALSE, null), expected);
Test perform = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Perform ECDH and ECDSA.", ka, sig);
diff --git a/src/cz/crcs/ectester/reader/test/CardSignatureSuite.java b/src/cz/crcs/ectester/reader/test/CardSignatureSuite.java
index 20546c8..0fa58d3 100644
--- a/src/cz/crcs/ectester/reader/test/CardSignatureSuite.java
+++ b/src/cz/crcs/ectester/reader/test/CardSignatureSuite.java
@@ -22,7 +22,7 @@ import java.util.Map;
*/
public class CardSignatureSuite extends CardTestSuite {
public CardSignatureSuite(TestWriter writer, ECTesterReader.Config cfg, CardMngr cardManager) {
- super(writer, cfg, cardManager, "signature", "Test verifying various wrong ECDSA values.");
+ super(writer, cfg, cardManager, "signature", "The signature test suite tests verifying various malformed and well-formed but invalid ECDSA signatures.");
}
@Override
diff --git a/src/cz/crcs/ectester/reader/test/CardTestSuite.java b/src/cz/crcs/ectester/reader/test/CardTestSuite.java
index 3578f9c..73acbe7 100644
--- a/src/cz/crcs/ectester/reader/test/CardTestSuite.java
+++ b/src/cz/crcs/ectester/reader/test/CardTestSuite.java
@@ -1,9 +1,17 @@
package cz.crcs.ectester.reader.test;
+import cz.crcs.ectester.applet.ECTesterApplet;
+import cz.crcs.ectester.applet.EC_Consts;
+import cz.crcs.ectester.common.ec.EC_Curve;
+import cz.crcs.ectester.common.ec.EC_Params;
import cz.crcs.ectester.common.output.TestWriter;
+import cz.crcs.ectester.common.test.Result;
+import cz.crcs.ectester.common.test.Test;
import cz.crcs.ectester.common.test.TestSuite;
+import cz.crcs.ectester.common.util.ECUtil;
import cz.crcs.ectester.reader.CardMngr;
import cz.crcs.ectester.reader.ECTesterReader;
+import cz.crcs.ectester.reader.command.Command;
/**
* @author Jan Jancar johny@neuromancer.sk
@@ -21,4 +29,18 @@ public abstract class CardTestSuite extends TestSuite {
public CardMngr getCard() {
return card;
}
+
+ public ECTesterReader.Config getCfg() {
+ return cfg;
+ }
+
+ public Test genOrPreset(EC_Curve curve, Result.ExpectedValue expected) {
+ if (cfg.testOptions.contains("preset")) {
+ byte[] presetPriv = ECUtil.semiRandomKey(curve);
+ EC_Params privParms = new EC_Params(EC_Consts.PARAMETER_S, new byte[][]{presetPriv});
+ return CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.CURVE_external, privParms.getParams(), privParms.flatten()), expected);
+ } else {
+ return CommandTest.expect(new Command.Generate(this.card, ECTesterApplet.KEYPAIR_LOCAL), expected);
+ }
+ }
}
diff --git a/src/cz/crcs/ectester/reader/test/CardTestVectorSuite.java b/src/cz/crcs/ectester/reader/test/CardTestVectorSuite.java
index 3abcebb..b6dc904 100644
--- a/src/cz/crcs/ectester/reader/test/CardTestVectorSuite.java
+++ b/src/cz/crcs/ectester/reader/test/CardTestVectorSuite.java
@@ -30,6 +30,7 @@ import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.function.BiFunction;
import java.util.stream.Collectors;
import static cz.crcs.ectester.common.test.Result.ExpectedValue;
@@ -84,7 +85,7 @@ public class CardTestVectorSuite extends CardTestSuite {
return new Result(Value.FAILURE, "ECDH response did not contain the derived secret.");
if (!ByteUtil.compareBytes(dh.getSecret(), 0, result.getData(0), 0, dh.secretLength())) {
int firstDiff = ByteUtil.diffBytes(dh.getSecret(), 0, result.getData(0), 0, dh.secretLength());
- return new Result(Value.FAILURE, "ECDH derived secret does not match the test-vector, first difference was at byte " + String.valueOf(firstDiff) + ".");
+ return new Result(Value.FAILURE, "ECDH derived secret does not match the test-vector, first difference was at byte " + firstDiff + ".");
}
return new Result(Value.SUCCESS);
}
@@ -96,10 +97,12 @@ public class CardTestVectorSuite extends CardTestSuite {
}
KeyAgreement ka;
+ Signature sig;
KeyFactory kf;
MessageDigest md;
try {
ka = KeyAgreement.getInstance("ECDH", "BC");
+ sig = Signature.getInstance("ECDSAwithSHA1", "BC");
kf = KeyFactory.getInstance("ECDH", "BC");
md = MessageDigest.getInstance("SHA1", "BC");
} catch (NoSuchAlgorithmException | NoSuchProviderException ex) {
@@ -119,8 +122,26 @@ public class CardTestVectorSuite extends CardTestSuite {
testVector.add(allocate);
testVector.add(CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), ExpectedValue.SUCCESS));
testVector.add(CommandTest.expect(new Command.Generate(this.card, ECTesterApplet.KEYPAIR_BOTH), ExpectedValue.SUCCESS));
- CommandTest export = CommandTest.expect(new Command.Export(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.KEY_BOTH, EC_Consts.PARAMETERS_KEYPAIR), ExpectedValue.ANY);
- testVector.add(export);
+ CommandTest exportLocal = CommandTest.expect(new Command.Export(this.card, ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.KEY_PUBLIC, EC_Consts.PARAMETER_W), ExpectedValue.ANY);
+ CommandTest exportRemote = CommandTest.expect(new Command.Export(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.KEY_PRIVATE, EC_Consts.PARAMETER_S), ExpectedValue.ANY);
+ testVector.add(exportLocal);
+ testVector.add(exportRemote);
+ BiFunction<Response.Export, Response.Export, Key[]> getKeys = (localData, remoteData) -> {
+ byte[] pkey = localData.getParameter(ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.PARAMETER_W);
+ byte[] skey = remoteData.getParameter(ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.PARAMETER_S);
+ ECParameterSpec spec = curve.toSpec();
+ ECPrivateKeySpec privKeySpec = new ECPrivateKeySpec(new BigInteger(1, skey), spec);
+ ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(ECUtil.fromX962(pkey, curve.toCurve()), spec);
+ PrivateKey privKey;
+ PublicKey pubKey;
+ try {
+ privKey = kf.generatePrivate(privKeySpec);
+ pubKey = kf.generatePublic(pubKeySpec);
+ } catch (InvalidKeySpecException ex) {
+ return null;
+ }
+ return new Key[]{privKey, pubKey};
+ };
TestCallback<CommandTestable> kaCallback = new TestCallback<CommandTestable>() {
@Override
public Result apply(CommandTestable testable) {
@@ -131,19 +152,17 @@ public class CardTestVectorSuite extends CardTestSuite {
return new Result(Value.FAILURE, "ECDH response did not contain the derived secret.");
}
byte[] secret = ecdhData.getSecret();
- Response.Export keyData = (Response.Export) export.getResponse();
- byte[] pkey = keyData.getParameter(ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.PARAMETER_W);
- byte[] skey = keyData.getParameter(ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.PARAMETER_S);
- ECParameterSpec spec = curve.toSpec();
- ECPrivateKeySpec privKeySpec = new ECPrivateKeySpec(new BigInteger(1, skey), spec);
- ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(ECUtil.fromX962(pkey, curve.toCurve()), spec);
- PrivateKey privKey;
- PublicKey pubkey;
+ Response.Export localData = (Response.Export) exportLocal.getResponse();
+ Response.Export remoteData = (Response.Export) exportRemote.getResponse();
+ Key[] keys = getKeys.apply(localData, remoteData);
+ if (keys == null) {
+ return new Result(Value.SUCCESS, "Result could not be verified. keyData unavailable.");
+ }
+ PrivateKey privKey = (PrivateKey) keys[0];
+ PublicKey pubKey = (PublicKey) keys[1];
try {
- privKey = kf.generatePrivate(privKeySpec);
- pubkey = kf.generatePublic(pubKeySpec);
ka.init(privKey);
- ka.doPhase(pubkey, true);
+ ka.doPhase(pubKey, true);
byte[] rawDerived = ka.generateSecret();
int fieldSize = (curve.getBits() + 7) / 8;
if (rawDerived.length < fieldSize) {
@@ -163,14 +182,47 @@ public class CardTestVectorSuite extends CardTestSuite {
if (diff == secret.length) {
return new Result(Value.SUCCESS, "Derived secret matched expected value.");
} else {
- return new Result(Value.FAILURE, "Derived secret does not match expected value, first difference was at byte " + String.valueOf(diff) + ".");
+ return new Result(Value.FAILURE, "Derived secret does not match expected value, first difference was at byte " + diff + ".");
+ }
+ } catch (InvalidKeyException ex) {
+ return new Result(Value.SUCCESS, "Result could not be verified. " + ex.getMessage());
+ }
+ }
+ };
+ Test ecdhTest = CommandTest.function(new Command.ECDH(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_TRUE, EC_Consts.TRANSFORMATION_NONE, EC_Consts.KeyAgreement_ALG_EC_SVDP_DH), kaCallback);
+ byte[] data = new byte[32];
+ TestCallback<CommandTestable> sigCallback = new TestCallback<CommandTestable>() {
+ @Override
+ public Result apply(CommandTestable testable) {
+ Response.ECDSA ecdsaData = (Response.ECDSA) testable.getResponse();
+ if (!ecdsaData.successful())
+ return new Result(Value.FAILURE, "ECDSA was unsuccessful.");
+ if (!ecdsaData.hasSignature()) {
+ return new Result(Value.FAILURE, "ECDSA response did not contain the signature.");
+ }
+ byte[] signature = ecdsaData.getSignature();
+ Response.Export localData = (Response.Export) exportLocal.getResponse();
+ Response.Export remoteData = (Response.Export) exportRemote.getResponse();
+ Key[] keys = getKeys.apply(localData, remoteData);
+ if (keys == null) {
+ return new Result(Value.SUCCESS, "Result could not be verified. keyData unavailable.");
+ }
+ PublicKey pubKey = (PublicKey) keys[1];
+ try {
+ sig.initVerify(pubKey);
+ sig.update(data);
+ if (sig.verify(signature)) {
+ return new Result(Value.SUCCESS, "Signature verified.");
+ } else {
+ return new Result(Value.FAILURE, "Signature failed to verify.");
}
- } catch (InvalidKeySpecException | InvalidKeyException ex) {
+ } catch (InvalidKeyException | SignatureException ex) {
return new Result(Value.SUCCESS, "Result could not be verified. " + ex.getMessage());
}
}
};
- testVector.add(CommandTest.function(new Command.ECDH(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_TRUE, EC_Consts.TRANSFORMATION_NONE, EC_Consts.KeyAgreement_ALG_EC_SVDP_DH), kaCallback));
+ Test ecdsaTest = CommandTest.function(new Command.ECDSA_sign(this.card, ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.Signature_ALG_ECDSA_SHA, ECTesterApplet.EXPORT_TRUE, data), sigCallback);
+ testVector.add(CompoundTest.all(ExpectedValue.SUCCESS, "", ecdhTest, ecdsaTest));
if (cfg.cleanup) {
testVector.add(CommandTest.expect(new Command.Cleanup(this.card), ExpectedValue.ANY));
}
diff --git a/src/cz/crcs/ectester/reader/test/CardTwistSuite.java b/src/cz/crcs/ectester/reader/test/CardTwistSuite.java
index 3df4c65..4929d52 100644
--- a/src/cz/crcs/ectester/reader/test/CardTwistSuite.java
+++ b/src/cz/crcs/ectester/reader/test/CardTwistSuite.java
@@ -34,29 +34,33 @@ public class CardTwistSuite extends CardTestSuite {
EC_Curve curve = e.getKey();
List<EC_Key.Public> keys = e.getValue();
- Test allocate = CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), Result.ExpectedValue.SUCCESS);
+ Test allocate = runTest(CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), Result.ExpectedValue.SUCCESS));
+ if (!allocate.ok()) {
+ doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, "No support for " + curve.getId() + ".", allocate));
+ continue;
+ }
Test set = CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), Result.ExpectedValue.SUCCESS);
Test generate = CommandTest.expect(new Command.Generate(this.card, ECTesterApplet.KEYPAIR_LOCAL), Result.ExpectedValue.SUCCESS);
- Test prepare = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Prepare and generate keypair on " + curve.getId(), allocate, set, generate);
+ Test prepare = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Prepare and generate keypair on " + curve.getId() + ".", allocate, set, generate);
List<Test> ecdhTests = new LinkedList<>();
for (EC_Key.Public pub : keys) {
Test setPub = CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, pub.getParams(), pub.flatten()), Result.ExpectedValue.FAILURE);
- Test ecdh = CommandTest.expect(new Command.ECDH(this.card, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, EC_Consts.TRANSFORMATION_NONE, EC_Consts.KeyAgreement_ALG_EC_SVDP_DH), Result.ExpectedValue.FAILURE);
+ Test ecdh = CommandTest.expect(new Command.ECDH(this.card, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, EC_Consts.TRANSFORMATION_NONE, EC_Consts.KeyAgreement_ALG_EC_SVDP_DH), Result.ExpectedValue.FAILURE, "Card correctly rejected point on twist.", "Card incorrectly accepted point on twist.");
Test objectEcdh = CompoundTest.any(Result.ExpectedValue.SUCCESS, CardUtil.getKATypeString(EC_Consts.KeyAgreement_ALG_EC_SVDP_DH) + " test with twist pubkey.", setPub, ecdh);
Command ecdhCommand = new Command.ECDH_direct(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, EC_Consts.TRANSFORMATION_NONE, EC_Consts.KeyAgreement_ALG_EC_SVDP_DH, pub.flatten());
Test rawEcdh = CommandTest.expect(ecdhCommand, Result.ExpectedValue.FAILURE, "Card correctly rejected point on twist.", "Card incorrectly accepted point on twist.");
ecdhTests.add(CompoundTest.all(Result.ExpectedValue.SUCCESS, pub.getId() + " twist key test.", objectEcdh, rawEcdh));
}
- Test ecdh = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Perform ECDH with public points on twist", ecdhTests.toArray(new Test[0]));
+ Test ecdh = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Perform ECDH with public points on twist.", ecdhTests.toArray(new Test[0]));
Test tests = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Do tests.", ecdh);
if (cfg.cleanup) {
Test cleanup = CommandTest.expect(new Command.Cleanup(this.card), Result.ExpectedValue.ANY);
- doTest(CompoundTest.greedyAllTry(Result.ExpectedValue.SUCCESS, "Twist test of " + curve.getId(), prepare, tests, cleanup));
+ doTest(CompoundTest.greedyAllTry(Result.ExpectedValue.SUCCESS, "Twist test of " + curve.getId() + ".", prepare, tests, cleanup));
} else {
- doTest(CompoundTest.greedyAllTry(Result.ExpectedValue.SUCCESS, "Twist test of " + curve.getId(), prepare, tests));
+ doTest(CompoundTest.greedyAllTry(Result.ExpectedValue.SUCCESS, "Twist test of " + curve.getId() + ".", prepare, tests));
}
}
}
diff --git a/src/cz/crcs/ectester/reader/test/CommandTest.java b/src/cz/crcs/ectester/reader/test/CommandTest.java
index adad191..b05d3e4 100644
--- a/src/cz/crcs/ectester/reader/test/CommandTest.java
+++ b/src/cz/crcs/ectester/reader/test/CommandTest.java
@@ -32,7 +32,7 @@ public class CommandTest extends SimpleTest<CommandTestable> {
@Override
public Result apply(CommandTestable commandTestable) {
Result.Value resultValue = Result.Value.fromExpected(expected, commandTestable.ok(), commandTestable.error());
- return new Result(resultValue, resultValue.ok() ? ok : nok);
+ return new Result(resultValue, commandTestable.error() ? commandTestable.errorCause() : (resultValue.ok() ? ok : nok));
}
});
}
diff --git a/src/cz/crcs/ectester/reader/test/PerformanceTest.java b/src/cz/crcs/ectester/reader/test/PerformanceTest.java
index f9a4472..f9cba46 100644
--- a/src/cz/crcs/ectester/reader/test/PerformanceTest.java
+++ b/src/cz/crcs/ectester/reader/test/PerformanceTest.java
@@ -1,18 +1,24 @@
package cz.crcs.ectester.reader.test;
+import cz.crcs.ectester.applet.ECTesterApplet;
import cz.crcs.ectester.common.test.Result;
import cz.crcs.ectester.common.test.SimpleTest;
import cz.crcs.ectester.common.test.TestCallback;
+import cz.crcs.ectester.common.test.TestException;
+import cz.crcs.ectester.reader.CardMngr;
import cz.crcs.ectester.reader.command.Command;
import cz.crcs.ectester.reader.response.Response;
+import javax.smartcardio.CardException;
import java.util.Arrays;
/**
* @author Jan Jancar johny@neuromancer.sk
*/
public class PerformanceTest extends SimpleTest<CommandTestable> {
+ private CardMngr cardManager;
private long[] times;
+ private long[] reducedTimes;
private Response[] responses;
private long mean;
private long median;
@@ -20,23 +26,24 @@ public class PerformanceTest extends SimpleTest<CommandTestable> {
private int count;
private String desc;
- private PerformanceTest(CommandTestable testable, int count, String desc) {
+ private PerformanceTest(CardMngr cardManager, CommandTestable testable, int count, String desc) {
super(testable, new TestCallback<CommandTestable>() {
@Override
public Result apply(CommandTestable testable) {
return new Result(Result.Value.SUCCESS);
}
});
+ this.cardManager = cardManager;
this.count = count;
this.desc = desc;
}
- public static PerformanceTest repeat(Command cmd, int count) {
- return new PerformanceTest(new CommandTestable(cmd), count, null);
+ public static PerformanceTest repeat(CardMngr cardManager, Command cmd, int count) {
+ return new PerformanceTest(cardManager, new CommandTestable(cmd), count, null);
}
- public static PerformanceTest repeat(String desc, Command cmd, int count) {
- return new PerformanceTest(new CommandTestable(cmd), count, desc);
+ public static PerformanceTest repeat(CardMngr cardManager, String desc, Command cmd, int count) {
+ return new PerformanceTest(cardManager, new CommandTestable(cmd), count, desc);
}
@Override
@@ -47,18 +54,35 @@ public class PerformanceTest extends SimpleTest<CommandTestable> {
@Override
protected void runSelf() {
+ long baseTime;
+ try {
+ new Command.SetDryRunMode(cardManager, ECTesterApplet.MODE_DRY_RUN).send();
+ testable.run();
+ baseTime = testable.getResponse().getDuration();
+ testable.reset();
+ testable.run();
+ baseTime += testable.getResponse().getDuration();
+ testable.reset();
+ baseTime /= 2;
+ new Command.SetDryRunMode(cardManager, ECTesterApplet.MODE_NORMAL).send();
+ } catch (CardException ce) {
+ throw new TestException(ce);
+ }
+
times = new long[count];
+ reducedTimes = new long[count];
responses = new Response[count];
for (int i = 0; i < count; ++i) {
testable.run();
responses[i] = testable.getResponse();
times[i] = responses[i].getDuration();
+ reducedTimes[i] = times[i] - baseTime;
testable.reset();
}
- mean = Arrays.stream(times).sum() / count;
+ mean = Arrays.stream(reducedTimes).sum() / count;
- long[] sorted = times.clone();
+ long[] sorted = reducedTimes.clone();
Arrays.sort(sorted);
if (count % 2 == 0) {
median = (sorted[(count / 2) - 1] + sorted[count / 2]) / 2;
@@ -99,6 +123,10 @@ public class PerformanceTest extends SimpleTest<CommandTestable> {
return times;
}
+ public long[] getReducedTimes() {
+ return reducedTimes;
+ }
+
public long getMean() {
return mean;
}
diff --git a/src/cz/crcs/ectester/standalone/ECTesterStandalone.java b/src/cz/crcs/ectester/standalone/ECTesterStandalone.java
index 707f031..f3fe840 100644
--- a/src/cz/crcs/ectester/standalone/ECTesterStandalone.java
+++ b/src/cz/crcs/ectester/standalone/ECTesterStandalone.java
@@ -24,6 +24,8 @@ package cz.crcs.ectester.standalone;
import cz.crcs.ectester.common.cli.*;
import cz.crcs.ectester.common.ec.EC_Curve;
+import cz.crcs.ectester.common.ec.EC_Key;
+import cz.crcs.ectester.common.ec.EC_Keypair;
import cz.crcs.ectester.common.output.TestWriter;
import cz.crcs.ectester.common.test.TestException;
import cz.crcs.ectester.common.util.ByteUtil;
@@ -51,6 +53,7 @@ import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintStream;
+import java.math.BigInteger;
import java.nio.file.Files;
import java.security.*;
import java.security.interfaces.ECPrivateKey;
@@ -140,7 +143,6 @@ public class ECTesterStandalone {
System.err.println("Invalid algorithm parameter: " + e.getMessage());
} catch (NoSuchAlgorithmException nsaex) {
System.err.println("Algorithm not supported by the selected library: " + nsaex.getMessage());
- nsaex.printStackTrace();
} catch (InvalidKeyException | SignatureException e) {
e.printStackTrace();
}
@@ -150,6 +152,8 @@ public class ECTesterStandalone {
Map<String, ParserOptions> actions = new TreeMap<>();
Option namedCurve = Option.builder("nc").longOpt("named-curve").desc("Use a named curve, from CurveDB: <cat/id>").hasArg().argName("cat/id").optionalArg(false).build();
+ Option namedPublic = Option.builder("npub").longOpt("named-public").desc("Use a named public key, from CurveDB: <cat/id>").hasArg().argName("cat/id").optionalArg(false).build();
+ Option namedPrivate = Option.builder("npriv").longOpt("named-private").desc("Use a named private key, from CurveDB: <cat/id>").hasArg().argName("cat/id").optionalArg(false).build();
Option curveName = Option.builder("cn").longOpt("curve-name").desc("Use a named curve, search from curves supported by the library: <name>").hasArg().argName("name").optionalArg(false).build();
Option bits = Option.builder("b").longOpt("bits").hasArg().argName("n").optionalArg(false).desc("What size of curve to use.").build();
Option output = Option.builder("o").longOpt("output").desc("Output into file <output_file>.").hasArgs().argName("output_file").optionalArg(false).build();
@@ -176,7 +180,9 @@ public class ECTesterStandalone {
ecdhOpts.addOption(Option.builder("t").longOpt("type").desc("Set KeyAgreement object [type].").hasArg().argName("type").optionalArg(false).build());
ecdhOpts.addOption(Option.builder().longOpt("key-type").desc("Set the key [algorithm] for which the key should be derived in KeyAgreements with KDF. Default is \"AES\".").hasArg().argName("algorithm").optionalArg(false).build());
ecdhOpts.addOption(Option.builder("n").longOpt("amount").hasArg().argName("amount").optionalArg(false).desc("Do ECDH [amount] times.").build());
+ ecdhOpts.addOption(namedPrivate);
ecdhOpts.addOption(Option.builder().longOpt("fixed-private").desc("Perform ECDH with fixed private key.").build());
+ ecdhOpts.addOption(namedPublic);
ecdhOpts.addOption(Option.builder().longOpt("fixed-public").desc("Perform ECDH with fixed public key.").build());
ParserOptions ecdh = new ParserOptions(new DefaultParser(), ecdhOpts, "Perform EC based KeyAgreement.");
actions.put("ecdh", ecdh);
@@ -185,7 +191,9 @@ public class ECTesterStandalone {
ecdsaOpts.addOption(bits);
ecdsaOpts.addOption(namedCurve);
ecdsaOpts.addOption(curveName);
- ecdhOpts.addOption(output);
+ ecdsaOpts.addOption(output);
+ ecdsaOpts.addOption(namedPrivate);
+ ecdsaOpts.addOption(namedPublic);
ecdsaOpts.addOption(Option.builder("t").longOpt("type").desc("Set Signature object [type].").hasArg().argName("type").optionalArg(false).build());
ecdsaOpts.addOption(Option.builder("n").longOpt("amount").hasArg().argName("amount").optionalArg(false).desc("Do ECDSA [amount] times.").build());
ecdsaOpts.addOption(Option.builder("f").longOpt("file").hasArg().argName("file").optionalArg(false).desc("Input [file] to sign.").build());
@@ -196,7 +204,7 @@ public class ECTesterStandalone {
generateOpts.addOption(bits);
generateOpts.addOption(namedCurve);
generateOpts.addOption(curveName);
- ecdhOpts.addOption(output);
+ generateOpts.addOption(output);
generateOpts.addOption(Option.builder("n").longOpt("amount").hasArg().argName("amount").optionalArg(false).desc("Generate [amount] of EC keys.").build());
generateOpts.addOption(Option.builder("t").longOpt("type").hasArg().argName("type").optionalArg(false).desc("Set KeyPairGenerator object [type].").build());
ParserOptions generate = new ParserOptions(new DefaultParser(), generateOpts, "Generate EC keypairs.");
@@ -204,7 +212,7 @@ public class ECTesterStandalone {
Options exportOpts = new Options();
exportOpts.addOption(bits);
- ecdhOpts.addOption(output);
+ exportOpts.addOption(output);
exportOpts.addOption(Option.builder("t").longOpt("type").hasArg().argName("type").optionalArg(false).desc("Set KeyPair object [type].").build());
ParserOptions export = new ParserOptions(new DefaultParser(), exportOpts, "Export default curve parameters.");
actions.put("export", export);
@@ -345,28 +353,48 @@ public class ECTesterStandalone {
out.println("index;time[nano];pubW;privS;secret");
KeyPair one = null;
- if (cli.hasOption("ecdh.fixed-private")) {
+ if (cli.hasOption("ecdh.fixed-private") && !cli.hasOption("ecdh.named-private")) {
one = kpg.genKeyPair();
}
KeyPair other = null;
- if (cli.hasOption("ecdh.fixed-public")) {
+ if (cli.hasOption("ecdh.fixed-public") && !cli.hasOption("ecdh.named-public")) {
other = kpg.genKeyPair();
}
+ ECPrivateKey privkey = null;
+ if (cli.hasOption("ecdh.named-private")) {
+ privkey = ECUtil.toPrivateKey(EC_Store.getInstance().getObject(EC_Key.Private.class, cli.getOptionValue("ecdh.named-private")));
+ if (privkey == null) {
+ privkey = (ECPrivateKey) ECUtil.toKeyPair(EC_Store.getInstance().getObject(EC_Keypair.class, cli.getOptionValue("ecdh.named-private"))).getPrivate();
+ }
+ }
+ ECPublicKey pubkey = null;
+ if (cli.hasOption("ecdh.named-public")) {
+ pubkey = ECUtil.toPublicKey(EC_Store.getInstance().getObject(EC_Key.Public.class, cli.getOptionValue("ecdh.named-public")));
+ if (pubkey == null) {
+ pubkey = (ECPublicKey) ECUtil.toKeyPair(EC_Store.getInstance().getObject(EC_Keypair.class, cli.getOptionValue("ecdh.named-public"))).getPublic();
+ }
+ }
+
int amount = Integer.parseInt(cli.getOptionValue("ecdh.amount", "1"));
for (int i = 0; i < amount; ++i) {
- if (!cli.hasOption("ecdh.fixed-private")) {
+ if (!cli.hasOption("ecdh.fixed-private") && !cli.hasOption("ecdh.named-private")) {
one = kpg.genKeyPair();
}
- if (!cli.hasOption("ecdh.fixed-public")) {
+ if (!cli.hasOption("ecdh.fixed-public") && !cli.hasOption("ecdh.named-public")) {
other = kpg.genKeyPair();
}
- ECPrivateKey privkey = (ECPrivateKey) one.getPrivate();
- ECPublicKey pubkey = (ECPublicKey) other.getPublic();
+ if (!cli.hasOption("ecdh.named-private")) {
+ privkey = (ECPrivateKey) one.getPrivate();
+ }
+
+ if (!cli.hasOption("ecdh.named-public")) {
+ pubkey = (ECPublicKey) other.getPublic();
+ }
long elapsed = -System.nanoTime();
- if (spec instanceof ECParameterSpec) {
+ if (spec instanceof ECParameterSpec && lib instanceof NativeECLibrary) {
ka.init(privkey, spec);
} else {
ka.init(privkey);
@@ -415,7 +443,7 @@ public class ECTesterStandalone {
data = Files.readAllBytes(in.toPath());
dataString = "";
} else {
- SecureRandom random = new SecureRandom();
+ Random random = new Random();
data = new byte[32];
random.nextBytes(data);
dataString = ByteUtil.bytesToHex(data, false);
@@ -455,6 +483,7 @@ public class ECTesterStandalone {
}
Signature sig = sigIdent.getInstance(lib.getProvider());
KeyPairGenerator kpg = kpIdent.getInstance(lib.getProvider());
+ ECParameterSpec spec = null;
if (cli.hasOption("ecdsa.bits")) {
int bits = Integer.parseInt(cli.getOptionValue("ecdsa.bits"));
kpg.initialize(bits);
@@ -465,7 +494,8 @@ public class ECTesterStandalone {
System.err.println("Curve not found: " + curveName);
return;
}
- kpg.initialize(curve.toSpec());
+ spec = curve.toSpec();
+ kpg.initialize(spec);
} else if (cli.hasOption("ecdsa.curve-name")) {
String curveName = cli.getOptionValue("ecdsa.curve-name");
kpg.initialize(new ECGenParameterSpec(curveName));
@@ -478,14 +508,35 @@ public class ECTesterStandalone {
out = System.out;
}
- out.println("index;data;signTime[nano];verifyTime[nano];pubW;privS;signature;verified");
+ out.println("index;signTime[nano];verifyTime[nano];data;pubW;privS;signature;nonce;verified");
+
+ ECPrivateKey privkey = null;
+ if (cli.hasOption("ecdsa.named-private")) {
+ privkey = ECUtil.toPrivateKey(EC_Store.getInstance().getObject(EC_Key.Private.class, cli.getOptionValue("ecdsa.named-private")));
+ if (privkey == null) {
+ privkey = (ECPrivateKey) ECUtil.toKeyPair(EC_Store.getInstance().getObject(EC_Keypair.class, cli.getOptionValue("ecdsa.named-private"))).getPrivate();
+ }
+ }
+ ECPublicKey pubkey = null;
+ if (cli.hasOption("ecdsa.named-public")) {
+ pubkey = ECUtil.toPublicKey(EC_Store.getInstance().getObject(EC_Key.Public.class, cli.getOptionValue("ecdsa.named-public")));
+ if (pubkey == null) {
+ pubkey = (ECPublicKey) ECUtil.toKeyPair(EC_Store.getInstance().getObject(EC_Keypair.class, cli.getOptionValue("ecdsa.named-public"))).getPublic();
+ }
+ }
int amount = Integer.parseInt(cli.getOptionValue("ecdsa.amount", "1"));
for (int i = 0; i < amount; ++i) {
- KeyPair one = kpg.genKeyPair();
+ if (!cli.hasOption("ecdsa.named-private") || !cli.hasOption("ecdsa.named-public")) {
+ KeyPair one = kpg.genKeyPair();
- ECPrivateKey privkey = (ECPrivateKey) one.getPrivate();
- ECPublicKey pubkey = (ECPublicKey) one.getPublic();
+ if (!cli.hasOption("ecdsa.named-private")) {
+ privkey = (ECPrivateKey) one.getPrivate();
+ }
+ if (!cli.hasOption("ecdsa.named-public")) {
+ pubkey = (ECPublicKey) one.getPublic();
+ }
+ }
sig.initSign(privkey);
sig.update(data);
@@ -510,7 +561,14 @@ public class ECTesterStandalone {
String pub = ByteUtil.bytesToHex(ECUtil.toX962Uncompressed(pubkey.getW(), pubkey.getParams()), false);
String priv = ByteUtil.bytesToHex(privkey.getS().toByteArray(), false);
String sign = ByteUtil.bytesToHex(signature, false);
- out.println(String.format("%d;%s;%d;%d;%s;%s;%s;%d", i, dataString, signTime, verifyTime, pub, priv, sign, verified ? 1 : 0));
+ String k = "";
+ if (spec != null) {
+ BigInteger kValue = ECUtil.recoverSignatureNonce(signature, data, privkey.getS(), spec, sigIdent.getHashAlgo());
+ if (kValue != null) {
+ k = ByteUtil.bytesToHex(kValue.toByteArray(), false);
+ }
+ }
+ out.println(String.format("%d;%d;%d;%s;%s;%s;%s;%s;%d", i, signTime, verifyTime, dataString, pub, priv, sign, k, verified ? 1 : 0));
}
if (cli.hasOption("ecdsa.output")) {
diff --git a/src/cz/crcs/ectester/standalone/consts/SignatureIdent.java b/src/cz/crcs/ectester/standalone/consts/SignatureIdent.java
index e40731b..7f9adb4 100644
--- a/src/cz/crcs/ectester/standalone/consts/SignatureIdent.java
+++ b/src/cz/crcs/ectester/standalone/consts/SignatureIdent.java
@@ -104,6 +104,7 @@ public class SignatureIdent extends Ident {
int split = alias.indexOf("with");
this.hash = alias.substring(0, split);
this.sig = alias.substring(split + 4);
+ break;
}
}
}
diff --git a/util/plot_gen.py b/util/plot_gen.py
index 0d518e6..4ee1ddc 100755
--- a/util/plot_gen.py
+++ b/util/plot_gen.py
@@ -18,12 +18,13 @@ from matplotlib import ticker, colors
from copy import deepcopy
import argparse
-from utils import hw, moving_average, plot_hist
+from utils import hw, moving_average, plot_hist, miller_correction
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Plot results of ECTester key generation timing.")
parser.add_argument("-o", "--output", dest="output", type=argparse.FileType("wb"), help="Write image to [file], do not display.", metavar="file")
parser.add_argument("--priv", dest="priv", action="store_true", help="Show private key MSB heatmap plot.")
+ parser.add_argument("--entropy", dest="entropy", action="store_true", help="Show estimated entropy of private key MSB conditioned on time of generation.")
parser.add_argument("--hist", dest="hist", action="store_true", help="Show keygen time histogram.")
parser.add_argument("--export-hist", dest="export_hist", action="store_true", help="Show export time histogram.")
parser.add_argument("--avg", dest="avg", action="store_true", help="Show moving average of keygen time.")
@@ -103,21 +104,6 @@ if __name__ == "__main__":
sorted_data = np.sort(data, order="gen_time")
- i = 0
- entropies = {}
- while i < len(data):
- time_val = sorted_data["gen_time"][i]
- j = i
- msbs = [0 for _ in range(256)]
- while j < len(data) and sorted_data["gen_time"][j] == time_val:
- msbs[(sorted_data["priv"][j] >> (bit_size - 8)) & 0xff] += 1
- j += 1
- if j - 100 > i:
- entropies[time_val] = entropy(msbs, base=2)
- i = j
-
- entropy = sum(entropies.values())/len(entropies)
-
cmap = deepcopy(plt.cm.plasma)
cmap.set_bad("black")
@@ -174,7 +160,23 @@ if __name__ == "__main__":
fig.colorbar(im, ax=axe_priv_hist)
fig.text(0.01, 0.02, "Data size: {}".format(len(gen_time_data)), size="small")
- fig.text(0.01, 0.04, "Entropy of privkey MSB(estimated): {:.2f} b".format(entropy), size="small")
+
+ if opts.entropy:
+ i = 0
+ entropies = {}
+ while i < len(data):
+ time_val = sorted_data["gen_time"][i]
+ j = i
+ msbs = [0 for _ in range(256)]
+ while j < len(data) and sorted_data["gen_time"][j] == time_val:
+ msbs[(sorted_data["priv"][j] >> (bit_size - 8)) & 0xff] += 1
+ j += 1
+ if j - 100 > i:
+ entropies[time_val] = miller_correction(entropy(msbs, base=2), j - i, 256)
+ i = j
+
+ entropy = sum(entropies.values())/len(entropies)
+ fig.text(0.01, 0.04, "Entropy of privkey MSB(estimated): {:.2f} b".format(entropy), size="small")
if opts.output is None:
plt.show()
diff --git a/util/utils.py b/util/utils.py
new file mode 100644
index 0000000..bddfc35
--- /dev/null
+++ b/util/utils.py
@@ -0,0 +1,34 @@
+import numpy as np
+from matplotlib import ticker
+
+
+def hw(i):
+ res = 0
+ while i:
+ res += 1
+ i &= i - 1
+ return res
+
+
+def moving_average(a, n) :
+ ret = np.cumsum(a, dtype=float)
+ ret[n:] = ret[n:] - ret[:-n]
+ return ret[n - 1:] / n
+
+
+def plot_hist(axes, data, xlabel=None, log=False):
+ time_max = max(data)
+ time_min = min(data)
+ time_avg = np.average(data)
+ time_median = np.median(data)
+ axes.hist(data, bins=time_max - time_min, log=log)
+ axes.axvline(x=time_avg, alpha=0.7, linestyle="dotted", color="blue", label="avg = {}".format(time_avg))
+ axes.axvline(x=time_median, alpha=0.7, linestyle="dotted", color="green", label="median = {}".format(time_median))
+ axes.set_ylabel("count" + ("\n(log)" if log else ""))
+ axes.set_xlabel("time" if xlabel is None else xlabel)
+ axes.xaxis.set_major_locator(ticker.MaxNLocator())
+ axes.legend(loc="best")
+
+
+def miller_correction(entropy, samples, bins):
+ return entropy + (bins - 1)/(2*samples)