diff options
102 files changed, 5016 insertions, 1545 deletions
@@ -8,6 +8,8 @@ /dist/ECTesterStandalone.jar /dist/ECTesterStandalone-dist.jar /applet/ectester.cap +/applet/ectester221.cap +/applet/ectester222.cap # Test runs in /dist /dist/*.default diff --git a/.gitmodules b/.gitmodules index 0fb6b09..bab94d9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "ext/mscng"] path = ext/mscng url = https://github.com/J08nY/mscng-dev-kit +[submodule "ext/boringssl"] + path = ext/boringssl + url = https://boringssl.googlesource.com/boringssl diff --git a/.travis.yml b/.travis.yml index 3ca102d..aa4daf9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,7 @@ addons: - libtommath-dev - libssl-dev - libcrypto++-dev + - libgcrypt11-dev - gcc-6 - g++-6 language: java @@ -31,7 +32,8 @@ deploy: file: - "dist/ECTesterReader-dist.jar" - "dist/ECTesterReader.jar" - - "applet/ectester.cap" + - "applet/ectester221.cap" + - "applet/ectester222.cap" - "dist/ECTesterStandalone-dist.jar" - "dist/ECTesterStandalone.jar" - "dist/ECTester.zip" @@ -4,7 +4,7 @@ Tests support and behavior of elliptic curve cryptography implementations on JavaCards (`TYPE_EC_FP` and `TYPE_EC_F2M`) and on selected software libraries. For more information on ECC support on JavaCards see the [github page](https://crocs-muni.github.io/ECTester/), with results, tables and docs. -## Build +## Setup ECTester uses ant. There are three parts of ECTester, the JavaCard applet used for testing, the reader app which controls it and the standalone app which tests software libraries. ```bash @@ -16,89 +16,110 @@ ant -f build-applet.xml build # To build the applet (cap) -> "ap Build produces both a lightweight version of the JARs and a full version of the JARs with dependencies included, the latter has the `*-dist.jar` suffix. The standalone build tries building test binaries for all the supported libraries, and silently fails if the library is not properly supported. +The applet comes in two flavors, targeting JavaCard 2.2.1 and 2.2.2. The 2.2.2 version supports extended length APDUs which are necessary for some commands +to work properly. Use the `cap` ant property to specify which CAP file to build, either `ectester221.cap` or `ectester222.cap`. + +To build the 221 version do: +```bash +ant -f build-applet.xml build -Dcap=ectester221.cap +``` + ## JavaCard testing -1. Upload `!uploader/ectester.cap` using your favorite tool (e.g., [GlobalPlatformPro tool](https://github.com/martinpaljak/GlobalPlatform)) -2. Run `java -jar dist/ECTesterReader.jar -t` -3. Inspect output log with annotated results +1. Upload `applet/ectester.cap` using your favorite tool (e.g., [GlobalPlatformPro tool](https://github.com/martinpaljak/GlobalPlatform)) or the `build-applet.xml` ant file. +2. Run `java -jar dist/ECTesterReader.jar -t`. +3. Inspect output log with annotated results. -Following operations are tested: +Following operations are tested in the default suite: - Allocation of new KeyPair class for specified parameters - Generation of KeyPair with default curve - Setting of custom curve and KeyPair generation - Generation of shared secret via ECDH - Signature via ECDSA -See `java -jar ECTesterReader.jar -h` for more. +See `java -jar ECTesterReader.jar -h`, `java -jar ECTesterReader.jar -ls` and [DOCS](docs/TESTS.md) for more. ### Options ``` - -dsa,--ecdsa <count> Sign data with ECDSA, [count] times. - -t,--test <test_suite> Test ECC support. [test_suite]: - - default: - - invalid: - - compression: - - twist: - - degenerate: - - cofactor: - - wrong: - - composite: - - test-vectors: - -dh,--ecdh <count> Do EC KeyAgreement (ECDH...), [count] - times. - -e,--export Export the defaut curve parameters of - the card(if any). - -V,--version Print version info. - -ln,--list-named <what> Print the list of supported named - curves and keys. - -h,--help Print help. - - -a,--all Test all curve sizes. - -b,--bit-size <bits> Set curve size. - - -fp,--prime-field Use a prime field. - -f2m,--binary-field Use a binary field. - - -c,--curve <curve_file> Use curve from file <curve_file> - (field,a,b,gx,gy,r,k). - -nc,--named-curve <cat/id> Use a named curve, from CurveDB: - <cat/id> - -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). - -priv,--private <privkey_file> Use private key from file - <privkey_file> (s). - -npriv,--named-private <cat/id> Use private key from KeyDB: <cat/id> - -k,--key <key_file> Use keyPair from fileĀ <key_file> - (wx,wy,s). - -nk,--named-key <cat/id> Use keyPair from KeyDB: <cat/id> + -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). + -ls,--list-suites List supported test suites. + -e,--export Export the defaut curve parameters + of the card(if any). + -g,--generate <amount> Generate <amount> of EC keys. + -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 + -dh,--ecdh <count> Do EC KeyAgreement (ECDH...), + [count] times. + -dsa,--ecdsa <count> Sign data with ECDSA, [count] times. + -nf,--info Get applet info. - -i,--input <input_file> Input from fileĀ <input_file>, for ECDSA - signing. - -o,--output <output_file> Output into file <output_file>. - -l,--log <log_file> Log output into file [log_file]. - -v,--verbose Turn on verbose logging. - --format <format> Output format to use. One of: - text,yml,xml. - -f,--fresh Generate fresh keys (set domain - parameters before every generation). - --cleanup Send the cleanup command trigerring - JCSystem.requestObjectDeletion() - after some operations. - -s,--simulate Simulate a card with jcardsim instead - of using a terminal. - -y,--yes Accept all warnings and prompts. - - -ka,--ka-type <type> Set KeyAgreement object [type], - corresponds to JC.KeyAgreement - constants. - -sig,--sig-type <type> Set Signature object [type], - corresponds to JC.Signature constants. - -C,--color Print stuff with color, requires ANSI - terminal. + -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> + (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 + file can be prefixed by the format + (one of text,yml,xml), such as: + xml:<output_file>. + -l,--log <log_file> Log output into file [log_file]. + -v,--verbose Turn on verbose logging. + --format <format> Output format to use. One of: + text,yml,xml. + + -f,--fresh Generate fresh keys (set domain + parameters before every generation). + --cleanup Send the cleanup command trigerring + JCSystem.requestObjectDeletion() + after some operations. + -s,--simulate Simulate a card with jcardsim + instead of using a terminal. + -y,--yes Accept all warnings and prompts. + -ka,--ka-type <type> Set KeyAgreement object [type], + corresponds to JavaCard KeyAgreement + constants. + -sig,--sig-type <type> Set Signature object [type], + corresponds to JavaCard Signature + constants. + -C,--color Print stuff with color, requires + ANSI terminal. ``` ### Actions @@ -160,48 +181,71 @@ For example: For more info about the curves and curve categories see [CURVES](docs/CURVES.md). +#### List test suites +`-ls / --list-suites` + +Lists the implemented test suites and gives their short description. + +#### Get applet info +`-nf / --info` + +Get and print ECTester applet info from an applet installed on a card. + +Outputs: + + - ECTester applet version + - ECTester APDU support + - JavaCard API version + - JavaCard cleanup support + ### Example Snippet below shows running the default test suite while simulating(`-s`), so using JCardSim. This shows that JCardsim simulates 112b Fp support with default curve present and supports ECDH, ECDHC and ECDSA. - > java -jar ECTesterReader.jar -t -s - āāā Running test suite: default āāā - āāā The default test suite tests basic support of ECDH and ECDSA. - āāā Date: 2018.05.02 20:29:38 - āāā ECTester version: v0.2.0 - āāā Card ATR: 3bfa1800008131fe454a434f5033315632333298 - OK ā³ (0) Tests of 112b ALG_EC_FP support. ā SUCCESS ā All sub-tests matched the expected mask. - ⣠OK ā Allocate both keypairs 112b ALG_EC_FP ā SUCCESS ā 22 ms ā OK (0x9000) OK (0x9000) - ⣠OK ā Generate both keypairs ā SUCCESS ā 23 ms ā OK (0x9000) OK (0x9000) - ⣠OK ā Allocate both keypairs 112b ALG_EC_FP ā SUCCESS ā 0 ms ā OK (0x9000) OK (0x9000) - ⣠OK ā Set custom curve parameters on both keypairs ā SUCCESS ā 0 ms ā OK (0x9000) OK (0x9000) - ⣠OK ā Generate both keypairs ā SUCCESS ā 8 ms ā OK (0x9000) OK (0x9000) - ⣠OK ā³ KeyAgreement tests. ā SUCCESS ā Some sub-tests did have the expected result. - ā ⣠OK ā³ Test of the ALG_EC_SVDP_DH KeyAgreement. ā SUCCESS ā Some ECDH is supported. - ā ā ⣠OK ā Allocate KeyAgreement(ALG_EC_SVDP_DH) object ā SUCCESS ā 1 ms ā OK (0x9000) - ā ā ⣠OK ā ALG_EC_SVDP_DH of local pubkey and remote privkey(unchanged point) ā SUCCESS ā 2 ms ā OK (0x9000) - ā ā ⣠OK ā ALG_EC_SVDP_DH of local pubkey and remote privkey(COMPRESSED point) ā SUCCESS ā 2 ms ā OK (0x9000) - ā ā ā OK ā Mean = 1722885 ns, Median = 1718807 ns, Mode = 1614047 ns ā SUCCESS ā 1 ms ā OK (0x9000) - ā ⣠OK ā³ Test of the ALG_EC_SVDP_DHC KeyAgreement. ā SUCCESS ā Some ECDH is supported. - ā ā ⣠OK ā Allocate KeyAgreement(ALG_EC_SVDP_DHC) object ā SUCCESS ā 0 ms ā OK (0x9000) - ā ā ⣠OK ā ALG_EC_SVDP_DHC of local pubkey and remote privkey(unchanged point) ā SUCCESS ā 1 ms ā OK (0x9000) - ā ā ⣠OK ā ALG_EC_SVDP_DHC of local pubkey and remote privkey(COMPRESSED point) ā SUCCESS ā 1 ms ā OK (0x9000) - ā ā ā OK ā Mean = 1563980 ns, Median = 1549170 ns, Mode = 1514747 ns ā SUCCESS ā 1 ms ā OK (0x9000) - ā ⣠NOK ā Allocate KeyAgreement(ALG_EC_SVDP_DH_PLAIN) object ā FAILURE ā 0 ms ā fail (NO_SUCH_ALG, 0x0003) - ā ⣠NOK ā Allocate KeyAgreement(ALG_EC_SVDP_DHC_PLAIN) object ā FAILURE ā 0 ms ā fail (NO_SUCH_ALG, 0x0003) - ā ⣠NOK ā Allocate KeyAgreement(ALG_EC_PACE_GM) object ā FAILURE ā 0 ms ā fail (NO_SUCH_ALG, 0x0003) - ā ā NOK ā Allocate KeyAgreement(ALG_EC_SVDP_DH_PLAIN_XY) object ā FAILURE ā 0 ms ā fail (NO_SUCH_ALG, 0x0003) - ā OK ā³ Signature tests. ā SUCCESS ā Some sub-tests did have the expected result. - ⣠OK ā³ Test of the ALG_ECDSA_SHA signature. ā SUCCESS ā All sub-tests had the expected result. - ā ⣠OK ā Allocate Signature(ALG_ECDSA_SHA) object ā SUCCESS ā 3 ms ā OK (0x9000) - ā ⣠OK ā ALG_ECDSA_SHA with local keypair(random data) ā SUCCESS ā 14 ms ā OK (0x9000) - ā ⣠OK ā Sign (Mean = 1890914 ns, Median = 1500125 ns, Mode = 1422588 ns) ā SUCCESS ā 1 ms ā OK (0x9000) - ā ā OK ā Verify (Mean = 1873952 ns, Median = 1870348 ns, Mode = 1843902 ns) ā SUCCESS ā 1 ms ā OK (0x9000) - ⣠NOK ā Allocate Signature(ALG_ECDSA_SHA_224) object ā FAILURE ā 0 ms ā fail (NO_SUCH_ALG, 0x0003) - ⣠NOK ā Allocate Signature(ALG_ECDSA_SHA_256) object ā FAILURE ā 0 ms ā fail (NO_SUCH_ALG, 0x0003) - ⣠NOK ā Allocate Signature(ALG_ECDSA_SHA_384) object ā FAILURE ā 0 ms ā fail (NO_SUCH_ALG, 0x0003) - ā NOK ā Allocate Signature(ALG_ECDSA_SHA_512) object ā FAILURE ā 0 ms ā fail (NO_SUCH_ALG, 0x0003) +``` +> java -jar ECTesterReader.jar -t -s +āāā Running test suite: default āāā +āāā The default test suite tests basic support of ECDH and ECDSA. +āāā Date: 2018.05.02 20:29:38 +āāā ECTester version: v0.3.0 +āāā Card ATR: 3bfa1800008131fe454a434f5033315632333298 +ā āāāā³āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā³āāāāāāāāāāāā³āāāāāāāāāā + OK ā (0) Get applet info: v0.3.0; 3.0; basic ā SUCCESS ā All sub-tests had the expected result. + ā OK ā Get applet info ā SUCCESS ā 0 ms ā OK (0x9000) +ā āāāā³āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā³āāāāāāāāāāāā³āāāāāāāāāā + OK ā (1) Tests of 112b ALG_EC_FP support. ā SUCCESS ā All sub-tests matched the expected mask. + ⣠OK ā Allocate both keypairs 112b ALG_EC_FP ā SUCCESS ā 166 ms ā OK (0x9000) OK (0x9000) + ⣠OK ā Generate both keypairs ā SUCCESS ā 19 ms ā OK (0x9000) OK (0x9000) + ⣠OK ā Allocate both keypairs 112b ALG_EC_FP ā SUCCESS ā 0 ms ā OK (0x9000) OK (0x9000) + ⣠OK ā Set custom curve parameters on both keypairs ā SUCCESS ā 0 ms ā OK (0x9000) OK (0x9000) + ⣠OK ā Generate both keypairs ā SUCCESS ā 5 ms ā OK (0x9000) OK (0x9000) + ⣠OK ā³ KeyAgreement tests. ā SUCCESS ā Some sub-tests did have the expected result. + ā ⣠OK ā³ Test of the ALG_EC_SVDP_DH KeyAgreement. ā SUCCESS ā Some ECDH is supported. + ā ā ⣠OK ā Allocate KeyAgreement(ALG_EC_SVDP_DH) object ā SUCCESS ā 1 ms ā OK (0x9000) + ā ā ⣠OK ā ALG_EC_SVDP_DH of local pubkey and remote privkey ā SUCCESS ā 2 ms ā OK (0x9000) + ā ā ⣠OK ā ALG_EC_SVDP_DH of local pubkey and remote privkey(COMPRESSED point) ā SUCCESS ā 3 ms ā OK (0x9000) + ā ā ā OK ā Mean = 1879950 ns, Median = 1835076 ns, Mode = 1763287 ns ā SUCCESS ā 1 ms ā OK (0x9000) + ā ⣠OK ā³ Test of the ALG_EC_SVDP_DHC KeyAgreement. ā SUCCESS ā Some ECDH is supported. + ā ā ⣠OK ā Allocate KeyAgreement(ALG_EC_SVDP_DHC) object ā SUCCESS ā 0 ms ā OK (0x9000) + ā ā ⣠OK ā ALG_EC_SVDP_DHC of local pubkey and remote privkey ā SUCCESS ā 1 ms ā OK (0x9000) + ā ā ⣠OK ā ALG_EC_SVDP_DHC of local pubkey and remote privkey(COMPRESSED point) ā SUCCESS ā 2 ms ā OK (0x9000) + ā ā ā OK ā Mean = 1748499 ns, Median = 1760792 ns, Mode = 1647372 ns ā SUCCESS ā 1 ms ā OK (0x9000) + ā ⣠NOK ā Allocate KeyAgreement(ALG_EC_SVDP_DH_PLAIN) object ā FAILURE ā 0 ms ā fail (NO_SUCH_ALG, 0x0003) + ā ⣠NOK ā Allocate KeyAgreement(ALG_EC_SVDP_DHC_PLAIN) object ā FAILURE ā 0 ms ā fail (NO_SUCH_ALG, 0x0003) + ā ⣠NOK ā Allocate KeyAgreement(ALG_EC_PACE_GM) object ā FAILURE ā 0 ms ā fail (NO_SUCH_ALG, 0x0003) + ā ā NOK ā Allocate KeyAgreement(ALG_EC_SVDP_DH_PLAIN_XY) object ā FAILURE ā 0 ms ā fail (NO_SUCH_ALG, 0x0003) + ā OK ā³ Signature tests. ā SUCCESS ā Some sub-tests did have the expected result. + ⣠OK ā³ Test of the ALG_ECDSA_SHA signature. ā SUCCESS ā All sub-tests had the expected result. + ā ⣠OK ā Allocate Signature(ALG_ECDSA_SHA) object ā SUCCESS ā 2 ms ā OK (0x9000) + ā ⣠OK ā ALG_ECDSA_SHA with local keypair(random data) ā SUCCESS ā 17 ms ā OK (0x9000) + ā ⣠OK ā Sign (Mean = 1451086 ns, Median = 1413292 ns, Mode = 1378296 ns) ā SUCCESS ā 1 ms ā OK (0x9000) + ā ā OK ā Verify (Mean = 1850022 ns, Median = 1837022 ns, Mode = 1744613 ns) ā SUCCESS ā 1 ms ā OK (0x9000) + ⣠NOK ā Allocate Signature(ALG_ECDSA_SHA_224) object ā FAILURE ā 0 ms ā fail (NO_SUCH_ALG, 0x0003) + ⣠NOK ā Allocate Signature(ALG_ECDSA_SHA_256) object ā FAILURE ā 0 ms ā fail (NO_SUCH_ALG, 0x0003) + ⣠NOK ā Allocate Signature(ALG_ECDSA_SHA_384) object ā FAILURE ā 0 ms ā fail (NO_SUCH_ALG, 0x0003) + ā NOK ā Allocate Signature(ALG_ECDSA_SHA_512) object ā FAILURE ā 0 ms ā fail (NO_SUCH_ALG, 0x0003) +``` #### Legend - Some general information about the test suite and card is output first, test data follows after. @@ -222,13 +266,16 @@ If you are interested in testing support for other JavaCard algorithms, please v ## Standalone library testing Currently supported libraries include: - - BouncyCastle - - SunEC - - OpenSSL - - Crypto++ - - libtomcrypt - - botan - - Microsoft CNG + - [BouncyCastle](https://bouncycastle.org/java.html) + - [Sun EC](https://docs.oracle.com/javase/7/docs/technotes/guides/security/SunProviders.html#SunEC) + - [OpenSSL](https://www.openssl.org/) + - [BoringSSL](https://boringssl.googlesource.com/boringssl) + - [wolfSSL](https://www.wolfssl.com/) + - [Crypto++](https://cryptopp.com/) + - [libtomcrypt](http://www.libtom.net/LibTomCrypt/) + - [libgcrypt](https://www.gnupg.org/related_software/libgcrypt/) + - [Botan](https://botan.randombit.net/) + - [Microsoft CNG](https://msdn.microsoft.com/en-us/library/windows/desktop/aa376210(v=vs.85).aspx) For more information on ECC libraries see [LIBS](docs/LIBS.md). diff --git a/applet/gppro_upload.bat b/applet/gppro_upload.bat index 0b31b1e..50a41d7 100644 --- a/applet/gppro_upload.bat +++ b/applet/gppro_upload.bat @@ -1,7 +1,14 @@ -gp.exe -deletedeps -verbose -delete 4C6162616B417070 -gp.exe -deletedeps -verbose -delete 4A43416C6754657374 -gp.exe -deletedeps -verbose -delete 4543546573746572 - -gp.exe -install ectester.cap -verbose -d +if "%1" == "" { + set err=yes +} +if "%2" == "" { + set err=yes +} +if "%err" == "yes" { + echo "gppro_upload.bat <AID> <CAP file>" + exit +} +gp.exe -deletedeps -verbose -delete %1 +gp.exe -install %2 -verbose -d diff --git a/applet/gppro_upload.sh b/applet/gppro_upload.sh index da971af..055d51d 100755 --- a/applet/gppro_upload.sh +++ b/applet/gppro_upload.sh @@ -1,8 +1,10 @@ #!/usr/bin/env bash -java -jar gp.jar -deletedeps -verbose -delete 4C6162616B417070 -java -jar gp.jar -deletedeps -verbose -delete 4A43416C6754657374 -java -jar gp.jar -deletedeps -verbose -delete 4543546573746572 +if [ "$#" -ne 2 ]; then + echo "gppro_upload.sh <AID> <CAP file>" >&2 + exit 1 +fi -java -jar gp.jar -install ectester.cap -verbose -d +java -jar gp.jar -deletedeps -verbose -delete $1 +java -jar gp.jar -install $2 -verbose -d diff --git a/applet/gppro_upload_emv.bat b/applet/gppro_upload_emv.bat index 1f2f397..4094623 100644 --- a/applet/gppro_upload_emv.bat +++ b/applet/gppro_upload_emv.bat @@ -1,7 +1,15 @@ -gp.exe -deletedeps -verbose -emv -delete 4C6162616B417070 -gp.exe -deletedeps -verbose -emv -delete 4A43416C6754657374 -gp.exe -deletedeps -verbose -emv -delete 4543546573746572 +if "%1" == "" { + set err=yes +} +if "%2" == "" { + set err=yes +} +if "%err" == "yes" { + echo "gppro_upload_emv.bat <AID> <CAP file>" + exit +} -gp.exe -install ectester.cap -verbose -emv -d +gp.exe -deletedeps -verbose -emv -delete %1 +gp.exe -install %2 -verbose -emv -d diff --git a/applet/gppro_upload_emv.sh b/applet/gppro_upload_emv.sh index 8317833..9e1a62e 100755 --- a/applet/gppro_upload_emv.sh +++ b/applet/gppro_upload_emv.sh @@ -1,8 +1,10 @@ #!/usr/bin/env bash -java -jar gp.jar --deletedeps --verbose -emv --delete 4C6162616B417070 -java -jar gp.jar --deletedeps --verbose -emv --delete 4A43416C6754657374 -java -jar gp.jar --deletedeps --verbose -emv --delete 4543546573746572 +if [ "$#" -ne 2 ]; then + echo "gppro_upload.sh <AID> <CAP file>" >&2 + exit 1 +fi -java -jar gp.jar --install ectester.cap --verbose --emv -d +java -jar gp.jar --deletedeps --verbose -emv --delete $1 +java -jar gp.jar --install $2 --verbose --emv -d diff --git a/build-applet.xml b/build-applet.xml index f8f15e2..cfd95e6 100644 --- a/build-applet.xml +++ b/build-applet.xml @@ -1,39 +1,88 @@ <?xml version="1.0" encoding="UTF-8"?> <project basedir="." default="build" name="ECTesterApplet"> - <!-- Based on Martin Paljak's applets playground --> - <!-- Applet building dependencies --> - <property name="SDK_PATH" value="ext/sdks"/> + <!-- Based on Martin Paljak's applets playground --> + <!-- Applet building dependencies --> + <property name="SDK_PATH" value="ext/sdks"/> - <property name="JC212" value="${SDK_PATH}/jc212_kit"/> - <property name="JC221" value="${SDK_PATH}/jc221_kit"/> - <property name="JC222" value="${SDK_PATH}/jc222_kit"/> - <property name="JC303" value="${SDK_PATH}/jc303_kit"/> - <property name="JC304" value="${SDK_PATH}/jc304_kit"/> - <property name="JC305u1" value="${SDK_PATH}/jc305u1_kit"/> + <property name="JC212" value="${SDK_PATH}/jc212_kit"/> + <property name="JC221" value="${SDK_PATH}/jc221_kit"/> + <property name="JC222" value="${SDK_PATH}/jc222_kit"/> + <property name="JC303" value="${SDK_PATH}/jc303_kit"/> + <property name="JC304" value="${SDK_PATH}/jc304_kit"/> + <property name="JC305u1" value="${SDK_PATH}/jc305u1_kit"/> - <!-- ant-javacard task from javacard.pro --> - <taskdef name="javacard" classname="pro.javacard.ant.JavaCard" classpath="ext/ant-javacard.jar"/> + <!-- ant-javacard task from javacard.pro --> + <taskdef name="javacard" classname="pro.javacard.ant.JavaCard" classpath="ext/ant-javacard.jar"/> - <target name="build" description="Builds the CAP file."> - <javacard jckit="${JC222}"> - <cap output="applet/ectester.cap" sources="src/cz/crcs/ectester/applet" aid="4543546573746572"> - <applet class="cz.crcs.ectester.applet.ECTesterApplet" aid="45435465737465723031"/> - </cap> - </javacard> - </target> + <!-- applet properties --> + <property name="pkg_aid" value="4543546573746572"/> + <property name="applet_aid" value="45435465737465723031"/> + <property name="cap_221" value="ectester221.cap"/> + <property name="cap_222" value="ectester222.cap"/> + <property name="cap" value="${cap_222}"/> - <target name="upload" depends="build" description="Uploads the CAP file to the card, using gp.jar"> - <exec dir="applet" executable="./gppro_upload.sh" osfamily="unix"/> - <exec dir="applet" executable="./gppro_upload.bat" osfamily="windows"/> - </target> - <target name="upload-emv" depends="build" - description="Uploads the CAP file to the card, using gp.jar, uses the EMV protocol"> - <exec dir="applet" executable="./gppro_upload_emv.sh" osfamily="unix"/> - <exec dir="applet" executable="./gppro_upload_emv.bat" osfamily="windows"/> - </target> - <target name="clean" description="Clean the built CAP file."> - <delete file="applet/ectester.cap" quiet="true"/> - </target> + <condition property="cap_extended"> + <equals arg1="${cap}" arg2="${cap_222}"/> + </condition> + + <target name="build-check"> + <fail message="Invalid cap name!"> + <condition> + <not> + <or> + <equals arg1="${cap}" arg2="${cap_222}"/> + <equals arg1="${cap}" arg2="${cap_221}"/> + </or> + </not> + </condition> + </fail> + </target> + + <target name="build-221" description="Builds the CAP file for JavaCard 2.2.1." unless="cap_extended"> + <javacard jckit="${JC221}"> + <cap output="applet/ectester221.cap" sources="src/cz/crcs/ectester/applet" aid="${pkg_aid}" excludes="ECTesterAppletExtended.*"> + <applet class="cz.crcs.ectester.applet.ECTesterApplet" aid="${applet_aid}"/> + </cap> + </javacard> + </target> + + <target name="build-222" description="Builds the CAP file for JavaCard 2.2.2." if="cap_extended"> + <javacard jckit="${JC222}"> + <cap output="applet/ectester222.cap" sources="src/cz/crcs/ectester/applet" aid="${pkg_aid}" excludes="ECTesterApplet.*"> + <applet class="cz.crcs.ectester.applet.ECTesterAppletExtended" aid="${applet_aid}"/> + </cap> + </javacard> + </target> + + <target name="build" description="Builds the CAP file." depends="build-check,build-221,build-222"> + </target> + + <target name="upload" depends="build" description="Uploads the CAP file to the card, using gp.jar."> + <exec dir="applet" executable="./gppro_upload.sh" osfamily="unix"> + <arg value="${pkg_aid}"/> + <arg value="${cap}"/> + </exec> + <exec dir="applet" executable="./gppro_upload.bat" osfamily="windows"> + <arg value="${pkg_aid}"/> + <arg value="${cap}"/> + </exec> + </target> + + <target name="upload-emv" depends="build" description="Uploads the CAP file to the card, using gp.jar, uses the EMV protocol"> + <exec dir="applet" executable="./gppro_upload_emv.sh" osfamily="unix"> + <arg value="${pkg_aid}"/> + <arg value="${cap}"/> + </exec> + <exec dir="applet" executable="./gppro_upload_emv.bat" osfamily="windows"> + <arg value="${pkg_aid}"/> + <arg value="${cap}"/> + </exec> + </target> + + <target name="clean" description="Clean the built CAP files."> + <delete file="applet/${cap_221}" quiet="true"/> + <delete file="applet/${cap_222}" quiet="true"/> + </target> </project> diff --git a/build-standalone.xml b/build-standalone.xml index 685f791..9382a8f 100644 --- a/build-standalone.xml +++ b/build-standalone.xml @@ -106,11 +106,22 @@ <arg value="Makefile.bat"/> </exec> </target> + <target name="libs-debug"> + <exec dir="src/cz/crcs/ectester/standalone/libs/jni" failonerror="true" executable="make" osfamily="unix"> + <arg value="-k"/> + <arg value="DEBUG=1"/> + </exec> + <exec dir="src/cz/crcs/ectester/standalone/libs/jni" failonerror="true" executable="cmd" osfamily="windows"> + <arg value="/c"/> + <arg value="Makefile.bat"/> + <env key="DEBUG" value="1"/> + </exec> + </target> <target name="-post-compile" depends="libs-try"/> + <target name="-post-clean"> - <exec dir="src/cz/crcs/ectester/standalone/libs/jni" failonerror="true" executable="make" - osfamily="unix"> + <exec dir="src/cz/crcs/ectester/standalone/libs/jni" failonerror="true" executable="make" osfamily="unix"> <arg value="clean"/> </exec> <exec dir="src/cz/crcs/ectester/standalone/libs/jni" failonerror="true" executable="cmd" osfamily="windows"> @@ -157,6 +168,20 @@ <class name="cz.crcs.ectester.standalone.libs.jni.NativeECPrivateKey$Mscng"/> <class name="cz.crcs.ectester.standalone.libs.jni.NativeKeyAgreementSpi$Mscng"/> <class name="cz.crcs.ectester.standalone.libs.jni.NativeSignatureSpi$Mscng"/> + <class name="cz.crcs.ectester.standalone.libs.BoringsslLib"/> + <class name="cz.crcs.ectester.standalone.libs.jni.NativeProvider$Boringssl"/> + <class name="cz.crcs.ectester.standalone.libs.jni.NativeKeyPairGeneratorSpi$Boringssl"/> + <class name="cz.crcs.ectester.standalone.libs.jni.NativeECPublicKey$Boringssl"/> + <class name="cz.crcs.ectester.standalone.libs.jni.NativeECPrivateKey$Boringssl"/> + <class name="cz.crcs.ectester.standalone.libs.jni.NativeKeyAgreementSpi$Boringssl"/> + <class name="cz.crcs.ectester.standalone.libs.jni.NativeSignatureSpi$Boringssl"/> + <class name="cz.crcs.ectester.standalone.libs.GcryptLib"/> + <class name="cz.crcs.ectester.standalone.libs.jni.NativeProvider$Gcrypt"/> + <class name="cz.crcs.ectester.standalone.libs.jni.NativeKeyPairGeneratorSpi$Gcrypt"/> + <class name="cz.crcs.ectester.standalone.libs.jni.NativeECPublicKey$Gcrypt"/> + <class name="cz.crcs.ectester.standalone.libs.jni.NativeECPrivateKey$Gcrypt"/> + <class name="cz.crcs.ectester.standalone.libs.jni.NativeKeyAgreementSpi$Gcrypt"/> + <class name="cz.crcs.ectester.standalone.libs.jni.NativeSignatureSpi$Gcrypt"/> </javah> </target> </project> @@ -19,8 +19,12 @@ <echo message="ECTester is built using three ant files:"/> <echo message=" - build-applet.xml - For building the JavaCard applet."/> <echo message=" -> build: Build the applet."/> + <echo message=" -> build-221: Build the applet for JavaCard 2.2.1."/> + <echo message=" -> build-222: Build the applet for JavaCard 2.2.2."/> <echo message=" -> upload: Upload the applet using the GPPro tool."/> - <echo message=" -> upload-emv: Uploat the applet using the GPPro tool and the EMV key protocol."/> + <echo message=" (use `-Dcap=ectester221.cap` to upload the 2.2.1 version.)"/> + <echo message=" -> upload-emv: Upload the applet using the GPPro tool and the EMV key protocol."/> + <echo message=" (use `-Dcap=ectester221.cap` to upload the 2.2.1 version.)"/> <echo message=" - build-reader.xml - For building the reader app."/> <echo message=" -> jar: Build the reader app."/> <echo message=" -> package: Build the reader app into a distributable single jar, along with dependencies."/> diff --git a/docs/LIBS.md b/docs/LIBS.md index 00dcaaf..d41f98a 100644 --- a/docs/LIBS.md +++ b/docs/LIBS.md @@ -3,15 +3,16 @@ Popular libraries with at least some ECC support: - [NSS](https://hg.mozilla.org/projects/nss) - - [libgcrypt](https://www.gnupg.org/related_software/libgcrypt/) - [mbedTLS](https://tls.mbed.org/) + - [LibreSSL](https://www.libressl.org/) - [Nettle](http://www.lysator.liu.se/~nisse/nettle/) - [OpenSSL (FIPS mode)](https://www.openssl.org/docs/fipsnotes.html) - - [BoringSSL](https://boringssl.googlesource.com/boringssl) - [Microsoft .NET crypto](https://docs.microsoft.com/en-us/dotnet/standard/security/cryptography-model) # Supported libraries +Libraries that ECTester can test. + - [BouncyCastle](https://bouncycastle.org/java.html) - Java - Works with the short Weierstrass curve model. @@ -54,6 +55,8 @@ Popular libraries with at least some ECC support: - Uses Lopez-Dahab (Montgomery) ladder, XZ coordinates (ec2_mont.c): Fast multiplication on elliptic curves over GF(2^m) without precomputation (Algorithm 2P) - Contains an implementation of IEEE P1363 algorithm A.10.3 using affine coordinates (ec2_aff.c) - Has some custom arithmetic for some of the NIST primes. + - [WolfCrypt](https://www.wolfssl.com) + - C + Java - [OpenSSL](https://www.openssl.org/) - C - For prime field curves: @@ -61,23 +64,41 @@ Popular libraries with at least some ECC support: - Also uses multiplication with precomputation by wNAF splitting(ec_mult.c) - For binary field curves: - Uses Jacobian coordinates, and Lopez-Dahab ladder, also uses wNAF-based interleaving multi-exponentiation method(ec2_smpl.c) - - [Botan](https://botan.randombit.net/) - - C++ - - Uses blinded(randomized) Montgomery ladder. - - <https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-add-1998-cmo-2> - - <https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-1986-cc> - - <https://eprint.iacr.org/2015/657> - - ECTester supports v2.4.0 and up. - - [libtomcrypt](http://www.libtom.net/LibTomCrypt/) + - [BoringSSL](https://boringssl.googlesource.com/boringssl) - C - - Uses Jacobian coordinates. - - Sliding window scalar multiplication algorithm. + - Supports prime field curves only: + - Use Jacobian coordinates, and Montgomery ladder, also uses optimized arithmetic on NIST P-224, P-256. + - Bundled as a git submodule in `ext/boringssl`. To build and use run: +```bash +cd ext/boringssl +mkdir build && cd build +cmake -DBUILD_SHARED_LIBS=1 -GNinja .. +ninja +``` - [Crypto++](https://cryptopp.com/) - C++ - For prime field curves: - Uses projective coordinates and sliding window scalar multiplication algorithm. - For binary field curves: - Uses affine coordinates and sliding window scalar multiplication algorithm. + - [libtomcrypt](http://www.libtom.net/LibTomCrypt/) + - C + - Uses Jacobian coordinates. + - Sliding window scalar multiplication algorithm. + - [libgcrypt](https://www.gnupg.org/related_software/libgcrypt/) + - C + - Only supports prime field curves. + - Uses short Weierstrass, Montgomery and Twisted Edwards models. + - Uses left-to-right double-and-add always scalar multiplication and Jacobian coordinates in short Weierstrass form. + - Uses Montgomery ladder and X-only in Montgomery form. + - Uses left-to-right double-and-add always scalar multiplication in Twisted Edwards form. + - [Botan](https://botan.randombit.net/) + - C++ + - Uses blinded(randomized) Montgomery ladder. + - <https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-add-1998-cmo-2> + - <https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-1986-cc> + - <https://eprint.iacr.org/2015/657> + - ECTester supports v2.4.0 and up. - [Microsoft CNG](https://msdn.microsoft.com/en-us/library/windows/desktop/aa376210(v=vs.85).aspx) - C API. - Closed source. diff --git a/docs/TESTS.md b/docs/TESTS.md index c639722..25f61a8 100644 --- a/docs/TESTS.md +++ b/docs/TESTS.md @@ -40,6 +40,12 @@ Tests using known test vectors provided by NIST/SECG/Brainpool: 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). + - Compressed form, valid + - Hybrid form, valid + - Hybrid form, invalid `y` + - Point at infinity + - Compressed form, invalid, `x^3 + ax + b` results in quadratic non-residue in modular square root computation. + ## Miscellaneous Some miscellaneous tests, tries ECDH and ECDSA over supersingular curves, anomalous curves and Barreto-Naehrig curves with small embedding degree and CM discriminant. diff --git a/ext/ant-javacard.jar b/ext/ant-javacard.jar Binary files differindex c4fb69e..f36143a 100644 --- a/ext/ant-javacard.jar +++ b/ext/ant-javacard.jar diff --git a/ext/boringssl b/ext/boringssl new file mode 160000 +Subproject 80aa6949756d327476750f9ea2c9700aa2a027c diff --git a/lib/wolfcrypt-jni.jar b/lib/wolfcrypt-jni.jar Binary files differnew file mode 100644 index 0000000..890ae14 --- /dev/null +++ b/lib/wolfcrypt-jni.jar diff --git a/nbproject/reader/manifest.mf b/nbproject/reader/manifest.mf index c2a00ee..4bb6334 100644 --- a/nbproject/reader/manifest.mf +++ b/nbproject/reader/manifest.mf @@ -1,4 +1,4 @@ Manifest-Version: 1.0 -Class-Path: lib/jcardsim-3.0.4-SNAPSHOT.jar lib/commons-cli-1.4.jar lib/snakeyaml-1.19.jar +Class-Path: lib/bcprov-jdk15on-1.58.jar lib/jcardsim-3.0.4-SNAPSHOT.jar lib/commons-cli-1.4.jar lib/snakeyaml-1.19.jar Main-Class: cz.crcs.ectester.reader.ECTesterReader diff --git a/nbproject/reader/project.properties b/nbproject/reader/project.properties index 8e11520..c00c309 100644 --- a/nbproject/reader/project.properties +++ b/nbproject/reader/project.properties @@ -6,7 +6,7 @@ annotation.processing.source.output=${build.generated.sources.dir}/ap-source-out application.title=ECTesterReader application.vendor=xsvenda build.classes.dir=${build.dir}/classes -build.classes.excludes=**/*.java,**/*.form,**/*.c,**/*.cpp,**/*.hpp,**/*.h,**/*.a,**/*.o,**/Makefile +build.classes.excludes=**/*.java,**/*.form,**/*.c,**/*.cpp,**/*.hpp,**/*.h,**/*.a,**/*.o,**/*.so,**/Makefile,**/Makefile.bat # This directory is removed when the project is cleaned: build.dir=build build.generated.dir=${build.dir}/generated @@ -22,7 +22,7 @@ debug.classpath=\ debug.test.classpath=\ ${run.test.classpath} # Files in build.classes.dir which should be excluded from distribution jar -dist.archive.excludes= +dist.archive.excludes=**/*.java,**/*.form,**/*.c,**/*.cpp,**/*.hpp,**/*.h,**/*.a,**/*.o,**/*.so,**/Makefile,**/Makefile.bat # This directory is removed when the project is cleaned: dist.dir=dist dist.jar=${dist.dir}/ECTesterReader.jar diff --git a/nbproject/standalone/manifest.mf b/nbproject/standalone/manifest.mf index 5e8f5d9..ad7aacb 100644 --- a/nbproject/standalone/manifest.mf +++ b/nbproject/standalone/manifest.mf @@ -1,4 +1,4 @@ Manifest-Version: 1.0 -Class-Path: lib/bcprov-jdk15on-1.58.jar lib/jcardsim-3.0.4-SNAPSHOT.jar lib/commons-cli-1.4.jar lib/snakeyaml-1.19.jar +Class-Path: lib/bcprov-jdk15on-1.58.jar lib/wolfcrypt-jni.jar lib/jcardsim-3.0.4-SNAPSHOT.jar lib/commons-cli-1.4.jar lib/snakeyaml-1.19.jar Main-Class: cz.crcs.ectester.standalone.ECTesterStandalone diff --git a/nbproject/standalone/project.properties b/nbproject/standalone/project.properties index 7ad08a1..29ad3cc 100644 --- a/nbproject/standalone/project.properties +++ b/nbproject/standalone/project.properties @@ -6,7 +6,7 @@ annotation.processing.source.output=${build.generated.sources.dir}/ap-source-out application.title=ECTesterStandalone application.vendor=xsvenda build.classes.dir=${build.dir}/classes -build.classes.excludes=**/*.java,**/*.form,**/*.c,**/*.cpp,**/*.hpp,**/*.h,**/*.a,**/*.o,**/Makefile +build.classes.excludes=**/*.java,**/*.form,**/*.c,**/*.cpp,**/*.hpp,**/*.h,**/*.a,**/*.o,**/Makefile,**/Makefile.bat # This directory is removed when the project is cleaned: build.dir=build build.generated.dir=${build.dir}/generated @@ -22,7 +22,7 @@ debug.classpath=\ debug.test.classpath=\ ${run.test.classpath} # Files in build.classes.dir which should be excluded from distribution jar -dist.archive.excludes=**/*.java,**/*.form,**/*.c,**/*.cpp,**/*.hpp,**/*.h,**/*.a,**/*.o,**/Makefile +dist.archive.excludes=**/*.java,**/*.form,**/*.c,**/*.cpp,**/*.hpp,**/*.h,**/*.a,**/*.o,**/Makefile,**/Makefile.bat # This directory is removed when the project is cleaned: dist.dir=dist dist.jar=${dist.dir}/ECTesterStandalone.jar @@ -34,6 +34,7 @@ includes=**/common/**,**/standalone/**,**/data/**,**/applet/* jar.compress=true javac.classpath=\ lib/bcprov-jdk15on-1.58.jar:\ + lib/wolfcrypt-jni.jar:\ lib/jcardsim-3.0.4-SNAPSHOT.jar:\ lib/commons-cli-1.4.jar:\ lib/snakeyaml-1.19.jar diff --git a/src/cz/crcs/ectester/applet/AppletBase.java b/src/cz/crcs/ectester/applet/AppletBase.java new file mode 100644 index 0000000..4e488b5 --- /dev/null +++ b/src/cz/crcs/ectester/applet/AppletBase.java @@ -0,0 +1,886 @@ +package cz.crcs.ectester.applet; + +import javacard.framework.*; +import javacard.security.*; + +public abstract class AppletBase extends Applet { + + // MAIN INSTRUCTION CLASS + public static final byte CLA_ECTESTERAPPLET = (byte) 0xB0; + + // INSTRUCTIONS + public static final byte INS_ALLOCATE = (byte) 0x5a; + public static final byte INS_CLEAR = (byte) 0x5b; + public static final byte INS_SET = (byte) 0x5c; + public static final byte INS_TRANSFORM = (byte) 0x5d; + public static final byte INS_GENERATE = (byte) 0x5e; + public static final byte INS_EXPORT = (byte) 0x5f; + public static final byte INS_ECDH = (byte) 0x70; + public static final byte INS_ECDH_DIRECT = (byte) 0x71; + public static final byte INS_ECDSA = (byte) 0x72; + public static final byte INS_ECDSA_SIGN = (byte) 0x73; + public static final byte INS_ECDSA_VERIFY = (byte) 0x74; + public static final byte INS_CLEANUP = (byte) 0x75; + public static final byte INS_ALLOCATE_KA = (byte) 0x76; + public static final byte INS_ALLOCATE_SIG = (byte) 0x77; + public static final byte INS_GET_INFO = (byte) 0x78; + + // PARAMETERS for P1 and P2 + public static final byte KEYPAIR_LOCAL = (byte) 0x01; + public static final byte KEYPAIR_REMOTE = (byte) 0x02; + public static final byte KEYPAIR_BOTH = KEYPAIR_LOCAL | KEYPAIR_REMOTE; + public static final byte EXPORT_TRUE = (byte) 0xff; + public static final byte EXPORT_FALSE = (byte) 0x00; + + // STATUS WORDS + public static final short SW_SIG_VERIFY_FAIL = (short) 0x0ee1; + public static final short SW_DH_DHC_MISMATCH = (short) 0x0ee2; + public static final short SW_KEYPAIR_NULL = (short) 0x0ee3; + public static final short SW_KA_NULL = (short) 0x0ee4; + public static final short SW_SIGNATURE_NULL = (short) 0x0ee5; + public static final short SW_OBJECT_NULL = (short) 0x0ee6; + public static final short SW_Exception = (short) 0xff01; + public static final short SW_ArrayIndexOutOfBoundsException = (short) 0xff02; + public static final short SW_ArithmeticException = (short) 0xff03; + public static final short SW_ArrayStoreException = (short) 0xff04; + public static final short SW_NullPointerException = (short) 0xff05; + public static final short SW_NegativeArraySizeException = (short) 0xff06; + public static final short SW_CryptoException_prefix = (short) 0xf100; + public static final short SW_SystemException_prefix = (short) 0xf200; + public static final short SW_PINException_prefix = (short) 0xf300; + public static final short SW_TransactionException_prefix = (short) 0xf400; + public static final short SW_CardRuntimeException_prefix = (short) 0xf500; + + // + public static final short BASE_221 = (short) 0x0221; + public static final short BASE_222 = (short) 0x0222; + + // + public static final byte[] VERSION = {'v', '0', '.', '3', '.', '0'}; + + public static final short ARRAY_LENGTH = 0x100; + public static final short APDU_MAX_LENGTH = 1024; + + // TEMPORARRY ARRAY IN RAM + byte[] ramArray = null; + byte[] ramArray2 = null; + byte[] apduArray = null; + + RandomData randomData = null; + + ECKeyTester keyTester = null; + ECKeyGenerator keyGenerator = null; + KeyPair localKeypair = null; + KeyPair remoteKeypair = null; + + protected AppletBase(byte[] buffer, short offset, byte length) { + if (length > 9) { + /* + short dataOffset = offset; + // shift to privilege offset + dataOffset += (short) (1 + buffer[offset]); + // finally shift to Application specific offset + dataOffset += (short) (1 + buffer[dataOffset]); + // go to proprietary data + dataOffset++; + */ + + ramArray = JCSystem.makeTransientByteArray(ARRAY_LENGTH, JCSystem.CLEAR_ON_RESET); + ramArray2 = JCSystem.makeTransientByteArray(ARRAY_LENGTH, JCSystem.CLEAR_ON_RESET); + apduArray = JCSystem.makeTransientByteArray(APDU_MAX_LENGTH, JCSystem.CLEAR_ON_RESET); + + randomData = RandomData.getInstance(RandomData.ALG_SECURE_RANDOM); + EC_Consts.randomData = randomData; + + keyGenerator = new ECKeyGenerator(); + keyTester = new ECKeyTester(); + } + } + + public void process(APDU apdu) throws ISOException { + // get the APDU buffer + byte[] apduBuffer = apdu.getBuffer(); + byte cla = apduBuffer[ISO7816.OFFSET_CLA]; + byte ins = apduBuffer[ISO7816.OFFSET_INS]; + + // ignore the applet select command dispatched to the process + if (selectingApplet()) { + return; + } + + if (cla == CLA_ECTESTERAPPLET) { + try { + readAPDU(apdu, apduArray, APDU_MAX_LENGTH); + + short length = 0; + switch (ins) { + case INS_ALLOCATE_KA: + length = insAllocateKA(apdu); + break; + case INS_ALLOCATE_SIG: + length = insAllocateSig(apdu); + break; + case INS_ALLOCATE: + length = insAllocate(apdu); + break; + case INS_CLEAR: + length = insClear(apdu); + break; + case INS_SET: + length = insSet(apdu); + break; + case INS_TRANSFORM: + length = insTransform(apdu); + break; + case INS_GENERATE: + length = insGenerate(apdu); + break; + case INS_EXPORT: + length = insExport(apdu); + break; + case INS_ECDH: + length = insECDH(apdu); + break; + case INS_ECDH_DIRECT: + length = insECDH_direct(apdu); + break; + case INS_ECDSA: + length = insECDSA(apdu); + break; + case INS_ECDSA_SIGN: + length = insECDSA_sign(apdu); + break; + case INS_ECDSA_VERIFY: + length = insECDSA_verify(apdu); + break; + case INS_CLEANUP: + length = insCleanup(apdu); + break; + case INS_GET_INFO: + length = insGetInfo(apdu); + break; + default: + // The INS code is not supported by the dispatcher + ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); + break; + } + apdu.setOutgoingAndSend((short) 0, length); + + } catch (ISOException e) { + throw e; // Our exception from code, just re-emit + } catch (ArrayIndexOutOfBoundsException e) { + ISOException.throwIt(SW_ArrayIndexOutOfBoundsException); + } catch (ArithmeticException e) { + ISOException.throwIt(SW_ArithmeticException); + } catch (ArrayStoreException e) { + ISOException.throwIt(SW_ArrayStoreException); + } catch (NullPointerException e) { + ISOException.throwIt(SW_NullPointerException); + } catch (NegativeArraySizeException e) { + ISOException.throwIt(SW_NegativeArraySizeException); + } catch (CryptoException e) { + ISOException.throwIt((short) (SW_CryptoException_prefix | e.getReason())); + } catch (SystemException e) { + ISOException.throwIt((short) (SW_SystemException_prefix | e.getReason())); + } catch (PINException e) { + ISOException.throwIt((short) (SW_PINException_prefix | e.getReason())); + } catch (TransactionException e) { + ISOException.throwIt((short) (SW_TransactionException_prefix | e.getReason())); + } catch (CardRuntimeException e) { + ISOException.throwIt((short) (SW_CardRuntimeException_prefix | e.getReason())); + } catch (Exception e) { + ISOException.throwIt(SW_Exception); + } + + } else ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED); + } + + private void readAPDU(APDU apdu, byte[] buffer, short length) { + short read = apdu.setIncomingAndReceive(); + read += getOffsetCdata(apdu); + short total = getIncomingLength(apdu); + if (total > length) { + return; + } + + byte[] apduBuffer = apdu.getBuffer(); + short sum = 0; + + do { + Util.arrayCopyNonAtomic(apduBuffer, (short) 0, buffer, sum, read); + sum += read; + read = apdu.receiveBytes((short) 0); + } while (sum < total); + } + + abstract short getOffsetCdata(APDU apdu); + + abstract short getIncomingLength(APDU apdu); + + abstract short getBase(); + + /** + * Allocates KeyAgreement object, returns allocate SW. + * + * @param apdu DATA = byte KeyAgreementType + * @return length of response + */ + private short insAllocateKA(APDU apdu) { + short cdata = getOffsetCdata(apdu); + byte kaType = apduArray[cdata]; + short sw = keyTester.allocateKA(kaType); + Util.setShort(apdu.getBuffer(), (short) 0, sw); + return 2; + } + + /** + * Allocates a Signature object, returns allocate SW. + * + * @param apdu DATA = byte SignatureType + * @return length of response + */ + private short insAllocateSig(APDU apdu) { + short cdata = getOffsetCdata(apdu); + byte sigType = apduArray[cdata]; + short sw = keyTester.allocateSig(sigType); + Util.setShort(apdu.getBuffer(), (short) 0, sw); + return 2; + } + + /** + * Allocates local and remote keyPairs. + * returns allocate SWs + * + * @param apdu P1 = byte keyPair (KEYPAIR_* | ...) + * P2 = + * DATA = short keyLength + * byte keyClass + * @return length of response + */ + private short insAllocate(APDU apdu) { + byte keyPair = apduArray[ISO7816.OFFSET_P1]; + short cdata = getOffsetCdata(apdu); + short keyLength = Util.getShort(apduArray, cdata); + byte keyClass = apduArray[(short) (cdata + 2)]; + + return allocate(keyPair, keyLength, keyClass, apdu.getBuffer(), (short) 0); + } + + /** + * Clears local and remote keyPair's keys {@code .clearKey()}. + * returns clearKey SWs + * + * @param apdu P1 = byte keyPair (KEYPAIR_* | ...) + * P2 = + * @return length of response + */ + private short insClear(APDU apdu) { + byte keyPair = apduArray[ISO7816.OFFSET_P1]; + + short len = 0; + if ((keyPair & KEYPAIR_LOCAL) != 0) { + len += clear(localKeypair, apdu.getBuffer(), (short) 0); + } + if ((keyPair & KEYPAIR_REMOTE) != 0) { + len += clear(remoteKeypair, apdu.getBuffer(), len); + } + + return len; + } + + /** + * Sets curve parameters on local and remote keyPairs. + * returns setCurve SWs + * + * @param apdu P1 = byte keyPair (KEYPAIR_* | ...) + * P2 = byte curve (EC_Consts.CURVE_*) + * DATA = short params (EC_Consts.PARAMETER_* | ...) + * <p> + * if curveID = CURVE_EXTERNAL: + * [short paramLength, byte[] param], + * for all params in params, + * in order: field,a,b,g,r,k,w,s + * @return length of response + */ + private short insSet(APDU apdu) { + byte keyPair = apduArray[ISO7816.OFFSET_P1]; + byte curve = apduArray[ISO7816.OFFSET_P2]; + short cdata = getOffsetCdata(apdu); + short params = Util.getShort(apduArray, cdata); + + short len = 0; + + if ((keyPair & KEYPAIR_LOCAL) != 0) { + len += set(localKeypair, curve, params, apduArray, (short) (cdata + 2), apdu.getBuffer(), (short) 0); + } + if ((keyPair & KEYPAIR_REMOTE) != 0) { + len += set(remoteKeypair, curve, params, apduArray, (short) (cdata + 2), apdu.getBuffer(), len); + } + + return len; + } + + /** + * Transforms curve paramaters of local and remote keyPairs. + * returns transformCurve SWs + * + * @param apdu P1 = byte keyPair (KEYPAIR_* | ...) + * P2 = byte key (EC_Consts.KEY_* | ...) + * DATA = short params (EC_Consts.PARAMETER_* | ...) + * short transformation (EC_Consts.TRANSFORMATION_* || ...) + * @return length of response + */ + private short insTransform(APDU apdu) { + byte keyPair = apduArray[ISO7816.OFFSET_P1]; + byte key = apduArray[ISO7816.OFFSET_P2]; + short cdata = getOffsetCdata(apdu); + short params = Util.getShort(apduArray, cdata); + short transformation = Util.getShort(apduArray, (short) (cdata + 2)); + + short len = 0; + if ((keyPair & KEYPAIR_LOCAL) != 0) { + len += transform(localKeypair, key, params, transformation, apdu.getBuffer(), (short) 0); + } + + if ((keyPair & KEYPAIR_REMOTE) != 0) { + len += transform(remoteKeypair, key, params, transformation, apdu.getBuffer(), len); + } + + return len; + } + + /** + * Generates the local and remote keyPairs. + * returns generate SWs + * + * @param apdu P1 = byte keyPair (KEYPAIR_* | ...) + * P2 = + * @return length of response + */ + private short insGenerate(APDU apdu) { + byte keyPair = apduArray[ISO7816.OFFSET_P1]; + + short len = 0; + if ((keyPair & KEYPAIR_LOCAL) != 0) { + len += generate(localKeypair, apdu.getBuffer(), (short) 0); + } + if ((keyPair & KEYPAIR_REMOTE) != 0) { + len += generate(remoteKeypair, apdu.getBuffer(), len); + } + + return len; + } + + /** + * Exports selected key and domain parameters from the selected keyPair and key. + * + * @param apdu P1 = byte keyPair (KEYPAIR_* | ...) + * P2 = byte key (EC_Consts.KEY_* | ...) + * DATA = short params + * @return length of response + */ + private short insExport(APDU apdu) { + byte keyPair = apduArray[ISO7816.OFFSET_P1]; + byte key = apduArray[ISO7816.OFFSET_P2]; + short cdata = getOffsetCdata(apdu); + short params = Util.getShort(apduArray, cdata); + + short swOffset = 0; + short len = (short) (keyPair == KEYPAIR_BOTH ? 4 : 2); + + if ((keyPair & KEYPAIR_LOCAL) != 0) { + len += export(localKeypair, key, params, apdu.getBuffer(), swOffset, len); + swOffset += 2; + } + if ((keyPair & KEYPAIR_REMOTE) != 0) { + len += export(remoteKeypair, key, params, apdu.getBuffer(), swOffset, len); + } + + return len; + } + + /** + * Performs ECDH, between the pubkey specified in P1(local/remote) and the privkey specified in P2(local/remote). + * returns deriveSecret SW, {@code if(export == EXPORT_TRUE)} => short secretlen, byte[] secret + * + * @param apdu P1 = byte pubkey (KEYPAIR_*) + * P2 = byte privkey (KEYPAIR_*) + * DATA = byte export (EXPORT_TRUE || EXPORT_FALSE) + * short transformation (EC_Consts.TRANSFORMATION_* | ...) + * byte type (EC_Consts.KA_* | ...) + * @return length of response + */ + private short insECDH(APDU apdu) { + byte pubkey = apduArray[ISO7816.OFFSET_P1]; + byte privkey = apduArray[ISO7816.OFFSET_P2]; + short cdata = getOffsetCdata(apdu); + byte export = apduArray[cdata]; + short transformation = Util.getShort(apduArray, (short) (cdata + 1)); + byte type = apduArray[(short) (cdata + 3)]; + + return ecdh(pubkey, privkey, export, transformation, type, apdu.getBuffer(), (short) 0); + } + + /** + * Performs ECDH, directly between the privkey specified in P1(local/remote) and the raw data + * + * @param apdu P1 = byte privkey (KEYPAIR_*) + * P2 = byte export (EXPORT_TRUE || EXPORT_FALSE) + * DATA = short transformation (EC_Consts.TRANSFORMATION_* | ...) + * byte type (EC_Consts.KA_* | ...) + * short length + * byte[] pubkey + * @return length of response + */ + private short insECDH_direct(APDU apdu) { + byte privkey = apduArray[ISO7816.OFFSET_P1]; + byte export = apduArray[ISO7816.OFFSET_P2]; + short cdata = getOffsetCdata(apdu); + short transformation = Util.getShort(apduArray, cdata); + byte type = apduArray[(short) (cdata + 2)]; + short length = Util.getShort(apduArray, (short) (cdata + 3)); + + return ecdh_direct(privkey, export, transformation, type, (short) (cdata + 5), length, apdu.getBuffer(), (short) 0); + } + + /** + * Performs ECDSA signature and verification on data provided or random, using the keyPair in P1(local/remote). + * returns ecdsa SW, {@code if(export == EXPORT_TRUE)} => short signature_length, byte[] signature + * + * @param apdu P1 = byte keyPair (KEYPAIR_*) + * P2 = byte export (EXPORT_TRUE || EXPORT_FALSE) + * DATA = byte sigType + * short dataLength (00 = random data generated, !00 = data length) + * byte[] data + * @return length of response + */ + private short insECDSA(APDU apdu) { + byte keyPair = apduArray[ISO7816.OFFSET_P1]; + byte export = apduArray[ISO7816.OFFSET_P2]; + short cdata = getOffsetCdata(apdu); + byte sigType = apduArray[cdata]; + + short len = 0; + if ((keyPair & KEYPAIR_LOCAL) != 0) { + len += ecdsa(localKeypair, sigType, export, apduArray, (short) (cdata + 1), apdu.getBuffer(), (short) 0); + } + if ((keyPair & KEYPAIR_REMOTE) != 0) { + len += ecdsa(remoteKeypair, sigType, export, apduArray, (short) (cdata + 1), apdu.getBuffer(), len); + } + + return len; + } + + /** + * @param apdu P1 = byte keyPair (KEYPAIR_*) + * P2 = byte export (EXPORT_TRUE || EXPORT_FALSE) + * DATA = byte sigType + * short dataLength (00 = random data generated, !00 = data length) + * byte[] data + * @return length of response + */ + private short insECDSA_sign(APDU apdu) { + byte keyPair = apduArray[ISO7816.OFFSET_P1]; + byte export = apduArray[ISO7816.OFFSET_P2]; + short cdata = getOffsetCdata(apdu); + byte sigType = apduArray[cdata]; + + short len = 0; + if ((keyPair & KEYPAIR_LOCAL) != 0) { + len += ecdsa_sign(localKeypair, sigType, export, apduArray, (short) (cdata + 1), apdu.getBuffer(), (short) 0); + } + if ((keyPair & KEYPAIR_REMOTE) != 0) { + len += ecdsa_sign(remoteKeypair, sigType, export, apduArray, (short) (cdata + 1), apdu.getBuffer(), len); + } + return len; + } + + /** + * @param apdu P1 = byte keyPair (KEYPAIR_*) + * P2 = byte sigType + * DATA = short dataLength (00 = random data generated, !00 = data length) + * byte[] data + * short sigLength + * byte[] signature + * @return length of response + */ + private short insECDSA_verify(APDU apdu) { + byte keyPair = apduArray[ISO7816.OFFSET_P1]; + byte sigType = apduArray[ISO7816.OFFSET_P2]; + short cdata = getOffsetCdata(apdu); + + short len = 0; + if ((keyPair & KEYPAIR_LOCAL) != 0) { + len += ecdsa_verify(localKeypair, sigType, apduArray, cdata, apdu.getBuffer(), (short) 0); + } + if ((keyPair & KEYPAIR_REMOTE) != 0) { + len += ecdsa_verify(remoteKeypair, sigType, apduArray, cdata, apdu.getBuffer(), len); + } + return len; + } + + + /** + * Performs card memory cleanup via JCSystem.requestObjectDeletion() + * + * @param apdu no data + * @return length of response + */ + private short insCleanup(APDU apdu) { + byte[] apdubuf = apdu.getBuffer(); + + return cleanup(apdubuf, (short) 0); + } + + /** + * Gathers info about the applet and the card environment. + * + * @param apdu no data + * @return length of response + */ + private short insGetInfo(APDU apdu) { + byte[] apdubuf = apdu.getBuffer(); + + return getInfo(apdubuf, (short) 0); + } + + /** + * @param keyPair which keyPair to use, local/remote (KEYPAIR_* | ...) + * @param keyLength key length to set + * @param keyClass key class to allocate + * @param outBuffer buffer to write sw to + * @param outOffset offset into buffer + * @return length of data written to the buffer + */ + private short allocate(byte keyPair, short keyLength, byte keyClass, byte[] outBuffer, short outOffset) { + short length = 0; + if ((keyPair & KEYPAIR_LOCAL) != 0) { + localKeypair = keyGenerator.allocatePair(keyClass, keyLength); + Util.setShort(outBuffer, outOffset, keyGenerator.getSW()); + length += 2; + } + + if ((keyPair & KEYPAIR_REMOTE) != 0) { + remoteKeypair = keyGenerator.allocatePair(keyClass, keyLength); + Util.setShort(outBuffer, (short) (outOffset + length), keyGenerator.getSW()); + length += 2; + } + + return length; + } + + /** + * @param keyPair KeyPair to clear + * @param outBuffer buffer to write sw to + * @param outOffset offset into buffer + * @return length of data written to the buffer + */ + private short clear(KeyPair keyPair, byte[] outBuffer, short outOffset) { + short sw = keyGenerator.clearPair(keyPair, EC_Consts.KEY_BOTH); + Util.setShort(outBuffer, outOffset, sw); + + return 2; + } + + /** + * @param keyPair KeyPair to set params on + * @param curve curve to set (EC_Consts.CURVE_*) + * @param params parameters to set (EC_Consts.PARAMETER_* | ...) + * @param inBuffer buffer to read params from + * @param inOffset input offset in buffer + * @param outBuffer buffer to write sw to + * @param outOffset output offset in buffer + * @return length of data written to the buffer + */ + private short set(KeyPair keyPair, byte curve, short params, byte[] inBuffer, short inOffset, byte[] outBuffer, short outOffset) { + short sw = ISO7816.SW_NO_ERROR; + + switch (curve) { + case EC_Consts.CURVE_default: + //default, dont set anything + break; + case EC_Consts.CURVE_external: + //external + sw = keyGenerator.setExternalCurve(keyPair, params, inBuffer, inOffset); + break; + default: + //custom + sw = keyGenerator.setCurve(keyPair, curve, params, ramArray, (short) 0); + break; + } + + Util.setShort(outBuffer, outOffset, sw); + return 2; + } + + /** + * @param keyPair KeyPair to transform + * @param key key to transform (EC_Consts.KEY_* | ...) + * @param params parameters to transform (EC_Consts.PARAMETER_* | ...) + * @param transformation transformation type (EC_Consts.TRANSFORMATION_*) + * @param outBuffer buffer to output sw to + * @param outOffset output offset in buffer + * @return length of data written to the buffer + */ + private short transform(KeyPair keyPair, byte key, short params, short transformation, byte[] outBuffer, short outOffset) { + short sw = keyGenerator.transformCurve(keyPair, key, params, transformation, ramArray, (short) 0); + Util.setShort(outBuffer, outOffset, sw); + return 2; + } + + /** + * @param keyPair KeyPair to generate + * @param outBuffer buffer to output sw to + * @param outOffset output offset in buffer + * @return length of data written to the buffer + */ + private short generate(KeyPair keyPair, byte[] outBuffer, short outOffset) { + short sw = keyGenerator.generatePair(keyPair); + Util.setShort(outBuffer, outOffset, sw); + + return 2; + } + + /** + * @param keyPair KeyPair to export from + * @param key which key to export from (EC_Consts.KEY_PUBLIC | EC_Consts.KEY_PRIVATE) + * @param params which params to export (EC_Consts.PARAMETER_* | ...) + * @param outBuffer buffer to export params to + * @param swOffset offset to output sw to buffer + * @param outOffset output offset in buffer + * @return length of data written to the buffer + */ + private short export(KeyPair keyPair, byte key, short params, byte[] outBuffer, short swOffset, short outOffset) { + short length = 0; + + short sw = ISO7816.SW_NO_ERROR; + if ((key & EC_Consts.KEY_PUBLIC) != 0) { + //export params from public + length += keyGenerator.exportParameters(keyPair, EC_Consts.KEY_PUBLIC, params, outBuffer, outOffset); + sw = keyGenerator.getSW(); + } + if ((key & EC_Consts.KEY_PRIVATE) != 0 && sw == ISO7816.SW_NO_ERROR) { + //export params from private + length += keyGenerator.exportParameters(keyPair, EC_Consts.KEY_PRIVATE, params, outBuffer, (short) (outOffset + length)); + sw = keyGenerator.getSW(); + } + Util.setShort(outBuffer, swOffset, sw); + + return length; + } + + /** + * @param pubkey keyPair to use for public key, (KEYPAIR_LOCAL || KEYPAIR_REMOTE) + * @param privkey keyPair to use for private key, (KEYPAIR_LOCAL || KEYPAIR_REMOTE) + * @param export whether to export ECDH secret + * @param transformation whether to transform the pubkey before ECDH + * @param type KeyAgreement type to test + * @param outBuffer buffer to write sw to, and export ECDH secret {@code if(export == EXPORT_TRUE)} + * @param outOffset output offset in buffer + * @return length of data written to the buffer + */ + private short ecdh(byte pubkey, byte privkey, byte export, short transformation, byte type, byte[] outBuffer, short outOffset) { + short length = 0; + + KeyPair pub = ((pubkey & KEYPAIR_LOCAL) != 0) ? localKeypair : remoteKeypair; + KeyPair priv = ((privkey & KEYPAIR_LOCAL) != 0) ? localKeypair : remoteKeypair; + + short secretLength = 0; + if (keyTester.getKaType() == type) { + secretLength = keyTester.testKA(priv, pub, ramArray, (short) 0, ramArray2, (short) 0, transformation); + } else { + short allocateSW = keyTester.allocateKA(type); + if (allocateSW == ISO7816.SW_NO_ERROR) { + secretLength = keyTester.testKA(priv, pub, ramArray, (short) 0, ramArray2, (short) 0, transformation); + } + } + Util.setShort(outBuffer, outOffset, keyTester.getSW()); + length += 2; + + if ((export == EXPORT_TRUE)) { + Util.setShort(outBuffer, (short) (outOffset + length), secretLength); + length += 2; + Util.arrayCopyNonAtomic(ramArray2, (short) 0, outBuffer, (short) (outOffset + length), secretLength); + length += secretLength; + } + + return length; + } + + private short ecdh_direct(byte privkey, byte export, short transformation, byte type, short keyOffset, short keyLength, byte[] outBuffer, short outOffset) { + short length = 0; + + KeyPair priv = ((privkey & KEYPAIR_LOCAL) != 0) ? localKeypair : remoteKeypair; + + short secretLength = 0; + if (keyTester.getKaType() == type) { + secretLength = keyTester.testKA_direct(priv, apduArray, keyOffset, keyLength, ramArray2, (short) 0, transformation); + } else { + short allocateSW = keyTester.allocateKA(type); + if (allocateSW == ISO7816.SW_NO_ERROR) { + secretLength = keyTester.testKA_direct(priv, apduArray, keyOffset, keyLength, ramArray2, (short) 0, transformation); + } + } + + Util.setShort(outBuffer, outOffset, keyTester.getSW()); + length += 2; + + if ((export == EXPORT_TRUE)) { + Util.setShort(outBuffer, (short) (outOffset + length), secretLength); + length += 2; + Util.arrayCopyNonAtomic(ramArray2, (short) 0, outBuffer, (short) (outOffset + length), secretLength); + length += secretLength; + } + return length; + } + + /** + * @param sign keyPair to use for signing and verification + * @param sigType Signature type to use + * @param export whether to export ECDSA signature + * @param inBuffer buffer to read dataLength and data to sign from + * @param inOffset input offset in buffer + * @param outBuffer buffer to write sw to, and export ECDSA signature {@code if(export == EXPORT_TRUE)} + * @param outOffset output offset in buffer + * @return length of data written to the buffer + */ + private short ecdsa(KeyPair sign, byte sigType, byte export, byte[] inBuffer, short inOffset, byte[] outBuffer, short outOffset) { + short length = 0; + + short dataLength = Util.getShort(inBuffer, inOffset); + if (dataLength == 0) { //no data to sign + //generate random + dataLength = 64; + randomData.generateData(ramArray, (short) 0, dataLength); + } else { + Util.arrayCopyNonAtomic(inBuffer, (short) (inOffset + 2), ramArray, (short) 0, dataLength); + } + + short signatureLength = 0; + if (keyTester.getSigType() == sigType) { + signatureLength = keyTester.testECDSA((ECPrivateKey) sign.getPrivate(), (ECPublicKey) sign.getPublic(), ramArray, (short) 0, dataLength, ramArray2, (short) 0); + } else { + short allocateSW = keyTester.allocateSig(sigType); + if (allocateSW == ISO7816.SW_NO_ERROR) { + signatureLength = keyTester.testECDSA((ECPrivateKey) sign.getPrivate(), (ECPublicKey) sign.getPublic(), ramArray, (short) 0, dataLength, ramArray2, (short) 0); + } + } + Util.setShort(outBuffer, outOffset, keyTester.getSW()); + length += 2; + + if (export == EXPORT_TRUE) { + Util.setShort(outBuffer, (short) (outOffset + length), signatureLength); + length += 2; + + Util.arrayCopyNonAtomic(ramArray2, (short) 0, outBuffer, (short) (outOffset + length), signatureLength); + length += signatureLength; + } + + return length; + } + + private short ecdsa_sign(KeyPair sign, byte sigType, byte export, byte[] inBuffer, short inOffset, byte[] outBuffer, short outOffset) { + short length = 0; + + short dataLength = Util.getShort(inBuffer, inOffset); + if (dataLength == 0) { //no data to sign + //generate random + dataLength = 64; + randomData.generateData(ramArray, (short) 0, dataLength); + } else { + Util.arrayCopyNonAtomic(inBuffer, (short) (inOffset + 2), ramArray, (short) 0, dataLength); + } + + short signatureLength = 0; + if (keyTester.getSigType() == sigType) { + signatureLength = keyTester.testECDSA_sign((ECPrivateKey) sign.getPrivate(), ramArray, (short) 0, dataLength, ramArray2, (short) 0); + } else { + short allocateSW = keyTester.allocateSig(sigType); + if (allocateSW == ISO7816.SW_NO_ERROR) { + signatureLength = keyTester.testECDSA_sign((ECPrivateKey) sign.getPrivate(), ramArray, (short) 0, dataLength, ramArray2, (short) 0); + } + } + Util.setShort(outBuffer, outOffset, keyTester.getSW()); + length += 2; + + if (export == EXPORT_TRUE) { + Util.setShort(outBuffer, (short) (outOffset + length), signatureLength); + length += 2; + + Util.arrayCopyNonAtomic(ramArray2, (short) 0, outBuffer, (short) (outOffset + length), signatureLength); + length += signatureLength; + } + + return length; + } + + private short ecdsa_verify(KeyPair verify, byte sigType, byte[] inBuffer, short inOffset, byte[] outBuffer, short outOffset) { + short length = 0; + + short dataLength = Util.getShort(inBuffer, inOffset); + short dataOffset = (short) (inOffset + 2); + short sigLength = Util.getShort(inBuffer, (short) (dataOffset + dataLength)); + short sigOffset = (short) (dataOffset + dataLength + 2); + + if (keyTester.getSigType() == sigType) { + keyTester.testECDSA_verify((ECPublicKey) verify.getPublic(), inBuffer, dataOffset, dataLength, inBuffer, sigOffset, sigLength); + } else { + short allocateSW = keyTester.allocateSig(sigType); + if (allocateSW == ISO7816.SW_NO_ERROR) { + keyTester.testECDSA_verify((ECPublicKey) verify.getPublic(), inBuffer, dataOffset, dataLength, inBuffer, sigOffset, sigLength); + } + } + Util.setShort(outBuffer, outOffset, keyTester.getSW()); + length += 2; + + return length; + } + + /** + * @param buffer buffer to write sw to + * @param offset output offset in buffer + * @return length of data written to the buffer + */ + private short cleanup(byte[] buffer, short offset) { + short sw = ISO7816.SW_NO_ERROR; + try { + if (JCSystem.isObjectDeletionSupported()) + JCSystem.requestObjectDeletion(); + } catch (CardRuntimeException crex) { + sw = crex.getReason(); + } + + Util.setShort(buffer, offset, sw); + return 2; + } + + /** + * @param buffer buffer to write sw to + * @param offset output offset in buffer + * @return length of data written to the buffer + */ + private short getInfo(byte[] buffer, short offset) { + short length = 0; + Util.setShort(buffer, (short) (offset + length), ISO7816.SW_NO_ERROR); + length += 2; + Util.setShort(buffer, (short) (offset + length), (short) VERSION.length); + length += 2; + Util.arrayCopyNonAtomic(VERSION, (short) 0, buffer, (short) (offset + length), (short) (VERSION.length)); + length += VERSION.length; + Util.setShort(buffer, (short) (offset + length), getBase()); + length += 2; + Util.setShort(buffer, (short) (offset + length), JCSystem.getVersion()); + length += 2; + Util.setShort(buffer, (short) (offset + length), (short) (JCSystem.isObjectDeletionSupported() ? 1 : 0)); + length += 2; + Util.setShort(buffer, (short) (offset + length), (short) buffer.length); + length += 2; + Util.setShort(buffer, (short) (offset + length), (short) ramArray.length); + length += 2; + Util.setShort(buffer, (short) (offset + length), (short) ramArray2.length); + length += 2; + Util.setShort(buffer, (short) (offset + length), (short) apduArray.length); + length += 2; + return length; + } +} diff --git a/src/cz/crcs/ectester/applet/AppletUtil.java b/src/cz/crcs/ectester/applet/AppletUtil.java index 532b44e..9869aa4 100644 --- a/src/cz/crcs/ectester/applet/AppletUtil.java +++ b/src/cz/crcs/ectester/applet/AppletUtil.java @@ -1,9 +1,7 @@ package cz.crcs.ectester.applet; -import javacard.framework.APDU; import javacard.framework.ISO7816; import javacard.framework.ISOException; -import javacard.framework.Util; import javacard.security.KeyAgreement; import javacard.security.KeyPair; import javacard.security.Signature; @@ -20,37 +18,18 @@ public class AppletUtil { } public static short objCheck(Object obj) { - return nullCheck(obj, ECTesterApplet.SW_OBJECT_NULL); + return nullCheck(obj, AppletBase.SW_OBJECT_NULL); } public static short keypairCheck(KeyPair keyPair) { - return nullCheck(keyPair, ECTesterApplet.SW_KEYPAIR_NULL); + return nullCheck(keyPair, AppletBase.SW_KEYPAIR_NULL); } public static short kaCheck(KeyAgreement keyAgreement) { - return nullCheck(keyAgreement, ECTesterApplet.SW_KA_NULL); + return nullCheck(keyAgreement, AppletBase.SW_KA_NULL); } public static short signCheck(Signature signature) { - return nullCheck(signature, ECTesterApplet.SW_SIGNATURE_NULL); - } - - public static short readAPDU(APDU apdu, byte[] buffer, short length) { - short read = apdu.setIncomingAndReceive(); - read += apdu.getOffsetCdata(); - short total = apdu.getIncomingLength(); - if (total > length) { - return 0; - } - byte[] apduBuffer = apdu.getBuffer(); - - short sum = 0; - - do { - Util.arrayCopyNonAtomic(apduBuffer, (short) 0, buffer, sum, read); - sum += read; - read = apdu.receiveBytes((short) 0); - } while (sum < total); - return 0; + return nullCheck(signature, AppletBase.SW_SIGNATURE_NULL); } } diff --git a/src/cz/crcs/ectester/applet/ECKeyGenerator.java b/src/cz/crcs/ectester/applet/ECKeyGenerator.java index 7c52e8f..4326752 100644 --- a/src/cz/crcs/ectester/applet/ECKeyGenerator.java +++ b/src/cz/crcs/ectester/applet/ECKeyGenerator.java @@ -204,11 +204,10 @@ public class ECKeyGenerator { 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); - // TODO: fix this, ^^ fails on jcardsim, but is up to spec - if ((key & EC_Consts.KEY_PUBLIC) != 0) ecPublicKey.setFieldF2M(i3, i2, i1); - if ((key & EC_Consts.KEY_PRIVATE) != 0) ecPrivateKey.setFieldF2M(i3, i2, i1); + 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 { sw = ISO7816.SW_UNKNOWN; } diff --git a/src/cz/crcs/ectester/applet/ECKeyTester.java b/src/cz/crcs/ectester/applet/ECKeyTester.java index 6b5aa6b..0e46971 100644 --- a/src/cz/crcs/ectester/applet/ECKeyTester.java +++ b/src/cz/crcs/ectester/applet/ECKeyTester.java @@ -122,7 +122,7 @@ public class ECKeyTester { ecdsaSignature.init(verifyKey, Signature.MODE_VERIFY); boolean correct = ecdsaSignature.verify(inputBuffer, inputOffset, inputLength, sigBuffer, sigOffset, length); if (!correct) { - sw = ECTesterApplet.SW_SIG_VERIFY_FAIL; + sw = AppletBase.SW_SIG_VERIFY_FAIL; } } catch (CardRuntimeException ce) { sw = ce.getReason(); @@ -170,7 +170,7 @@ public class ECKeyTester { ecdsaSignature.init(verifyKey, Signature.MODE_VERIFY); boolean correct = ecdsaSignature.verify(inputBuffer, inputOffset, inputLength, sigBuffer, sigOffset, sigLength); if (!correct) { - sw = ECTesterApplet.SW_SIG_VERIFY_FAIL; + sw = AppletBase.SW_SIG_VERIFY_FAIL; } } catch (CardRuntimeException ce) { sw = ce.getReason(); diff --git a/src/cz/crcs/ectester/applet/ECTesterApplet.java b/src/cz/crcs/ectester/applet/ECTesterApplet.java index 17c8faf..41bee9b 100644 --- a/src/cz/crcs/ectester/applet/ECTesterApplet.java +++ b/src/cz/crcs/ectester/applet/ECTesterApplet.java @@ -26,9 +26,9 @@ */ package cz.crcs.ectester.applet; -import javacard.framework.*; -import javacard.security.*; -import javacardx.apdu.ExtendedLength; +import javacard.framework.APDU; +import javacard.framework.ISO7816; +import javacard.framework.ISOException; /** * Applet part of ECTester, a tool for testing Elliptic curve support on javacards. @@ -36,90 +36,9 @@ import javacardx.apdu.ExtendedLength; * @author Petr Svenda petr@svenda.com * @author Jan Jancar johny@neuromancer.sk */ -public class ECTesterApplet extends Applet implements ExtendedLength { - - // MAIN INSTRUCTION CLASS - public static final byte CLA_ECTESTERAPPLET = (byte) 0xB0; - - // INSTRUCTIONS - public static final byte INS_ALLOCATE = (byte) 0x5a; - public static final byte INS_CLEAR = (byte) 0x5b; - public static final byte INS_SET = (byte) 0x5c; - public static final byte INS_TRANSFORM = (byte) 0x5d; - public static final byte INS_GENERATE = (byte) 0x5e; - public static final byte INS_EXPORT = (byte) 0x5f; - public static final byte INS_ECDH = (byte) 0x70; - public static final byte INS_ECDH_DIRECT = (byte) 0x71; - public static final byte INS_ECDSA = (byte) 0x72; - public static final byte INS_ECDSA_SIGN = (byte) 0x73; - public static final byte INS_ECDSA_VERIFY = (byte) 0x74; - public static final byte INS_CLEANUP = (byte) 0x75; - public static final byte INS_ALLOCATE_KA = (byte) 0x76; - public static final byte INS_ALLOCATE_SIG = (byte) 0x77; - - - // PARAMETERS for P1 and P2 - public static final byte KEYPAIR_LOCAL = (byte) 0x01; - public static final byte KEYPAIR_REMOTE = (byte) 0x02; - public static final byte KEYPAIR_BOTH = KEYPAIR_LOCAL | KEYPAIR_REMOTE; - public static final byte EXPORT_TRUE = (byte) 0xff; - public static final byte EXPORT_FALSE = (byte) 0x00; - - // STATUS WORDS - public static final short SW_SIG_VERIFY_FAIL = (short) 0x0ee1; - public static final short SW_DH_DHC_MISMATCH = (short) 0x0ee2; - public static final short SW_KEYPAIR_NULL = (short) 0x0ee3; - public static final short SW_KA_NULL = (short) 0x0ee4; - public static final short SW_SIGNATURE_NULL = (short) 0x0ee5; - public static final short SW_OBJECT_NULL = (short) 0x0ee6; - public static final short SW_Exception = (short) 0xff01; - public static final short SW_ArrayIndexOutOfBoundsException = (short) 0xff02; - public static final short SW_ArithmeticException = (short) 0xff03; - public static final short SW_ArrayStoreException = (short) 0xff04; - public static final short SW_NullPointerException = (short) 0xff05; - public static final short SW_NegativeArraySizeException = (short) 0xff06; - public static final short SW_CryptoException_prefix = (short) 0xf100; - public static final short SW_SystemException_prefix = (short) 0xf200; - public static final short SW_PINException_prefix = (short) 0xf300; - public static final short SW_TransactionException_prefix = (short) 0xf400; - public static final short SW_CardRuntimeException_prefix = (short) 0xf500; - - private static final short ARRAY_LENGTH = (short) 0xff; - private static final short APDU_MAX_LENGTH = (short) 1024; - // TEMPORARRY ARRAY IN RAM - private byte[] ramArray = null; - private byte[] ramArray2 = null; - private byte[] apduArray = null; - - private RandomData randomData = null; - - private ECKeyTester keyTester = null; - private ECKeyGenerator keyGenerator = null; - private KeyPair localKeypair = null; - private KeyPair remoteKeypair = null; - +public class ECTesterApplet extends AppletBase { protected ECTesterApplet(byte[] buffer, short offset, byte length) { - if (length > 9) { - /* - short dataOffset = offset; - // shift to privilege offset - dataOffset += (short) (1 + buffer[offset]); - // finally shift to Application specific offset - dataOffset += (short) (1 + buffer[dataOffset]); - // go to proprietary data - dataOffset++; - */ - - ramArray = JCSystem.makeTransientByteArray(ARRAY_LENGTH, JCSystem.CLEAR_ON_RESET); - ramArray2 = JCSystem.makeTransientByteArray(ARRAY_LENGTH, JCSystem.CLEAR_ON_RESET); - apduArray = JCSystem.makeTransientByteArray(APDU_MAX_LENGTH, JCSystem.CLEAR_ON_RESET); - - randomData = RandomData.getInstance(RandomData.ALG_SECURE_RANDOM); - EC_Consts.randomData = randomData; - - keyGenerator = new ECKeyGenerator(); - keyTester = new ECKeyTester(); - } + super(buffer, offset, length); register(); } @@ -128,722 +47,16 @@ public class ECTesterApplet extends Applet implements ExtendedLength { new ECTesterApplet(bArray, bOffset, bLength); } - public void process(APDU apdu) throws ISOException { - // get the APDU buffer - byte[] apduBuffer = apdu.getBuffer(); - byte cla = apduBuffer[ISO7816.OFFSET_CLA]; - byte ins = apduBuffer[ISO7816.OFFSET_INS]; - - // ignore the applet select command dispached to the process - if (selectingApplet()) { - return; - } - - if (cla == CLA_ECTESTERAPPLET) { - try { - - AppletUtil.readAPDU(apdu, apduArray, APDU_MAX_LENGTH); - - short length = 0; - switch (ins) { - case INS_ALLOCATE_KA: - length = insAllocateKA(apdu); - break; - case INS_ALLOCATE_SIG: - length = insAllocateSig(apdu); - break; - case INS_ALLOCATE: - length = insAllocate(apdu); - break; - case INS_CLEAR: - length = insClear(apdu); - break; - case INS_SET: - length = insSet(apdu); - break; - case INS_TRANSFORM: - length = insTransform(apdu); - break; - case INS_GENERATE: - length = insGenerate(apdu); - break; - case INS_EXPORT: - length = insExport(apdu); - break; - case INS_ECDH: - length = insECDH(apdu); - break; - case INS_ECDH_DIRECT: - length = insECDH_direct(apdu); - break; - case INS_ECDSA: - length = insECDSA(apdu); - break; - case INS_ECDSA_SIGN: - length = insECDSA_sign(apdu); - break; - case INS_ECDSA_VERIFY: - length = insECDSA_verify(apdu); - break; - case INS_CLEANUP: - length = insCleanup(apdu); - break; - default: - // The INS code is not supported by the dispatcher - ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); - break; - } - apdu.setOutgoingAndSend((short) 0, length); - - } catch (ISOException e) { - throw e; // Our exception from code, just re-emit - } catch (ArrayIndexOutOfBoundsException e) { - ISOException.throwIt(SW_ArrayIndexOutOfBoundsException); - } catch (ArithmeticException e) { - ISOException.throwIt(SW_ArithmeticException); - } catch (ArrayStoreException e) { - ISOException.throwIt(SW_ArrayStoreException); - } catch (NullPointerException e) { - ISOException.throwIt(SW_NullPointerException); - } catch (NegativeArraySizeException e) { - ISOException.throwIt(SW_NegativeArraySizeException); - } catch (CryptoException e) { - ISOException.throwIt((short) (SW_CryptoException_prefix | e.getReason())); - } catch (SystemException e) { - ISOException.throwIt((short) (SW_SystemException_prefix | e.getReason())); - } catch (PINException e) { - ISOException.throwIt((short) (SW_PINException_prefix | e.getReason())); - } catch (TransactionException e) { - ISOException.throwIt((short) (SW_TransactionException_prefix | e.getReason())); - } catch (CardRuntimeException e) { - ISOException.throwIt((short) (SW_CardRuntimeException_prefix | e.getReason())); - } catch (Exception e) { - ISOException.throwIt(SW_Exception); - } - - } else ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED); - } - - /** - * Allocates KeyAgreement object, returns allocate SW. - * - * @param apdu DATA = byte KeyAgreementType - * @return length of response - */ - private short insAllocateKA(APDU apdu) { - short cdata = apdu.getOffsetCdata(); - byte kaType = apduArray[cdata]; - short sw = keyTester.allocateKA(kaType); - Util.setShort(apdu.getBuffer(), (short) 0, sw); - return 2; - } - - /** - * Allocates a Signature object, returns allocate SW. - * - * @param apdu DATA = byte SignatureType - * @return length of response - */ - private short insAllocateSig(APDU apdu) { - short cdata = apdu.getOffsetCdata(); - byte sigType = apduArray[cdata]; - short sw = keyTester.allocateSig(sigType); - Util.setShort(apdu.getBuffer(), (short) 0, sw); - return 2; - } - - /** - * Allocates local and remote keyPairs. - * returns allocate SWs - * - * @param apdu P1 = byte keyPair (KEYPAIR_* | ...) - * P2 = - * DATA = short keyLength - * byte keyClass - * @return length of response - */ - private short insAllocate(APDU apdu) { - byte keyPair = apduArray[ISO7816.OFFSET_P1]; - short cdata = apdu.getOffsetCdata(); - short keyLength = Util.getShort(apduArray, cdata); - byte keyClass = apduArray[(short) (cdata + 2)]; - - return allocate(keyPair, keyLength, keyClass, apdu.getBuffer(), (short) 0); - } - - /** - * Clears local and remote keyPair's keys {@code .clearKey()}. - * returns clearKey SWs - * - * @param apdu P1 = byte keyPair (KEYPAIR_* | ...) - * P2 = - * @return length of response - */ - private short insClear(APDU apdu) { - byte keyPair = apduArray[ISO7816.OFFSET_P1]; - - short len = 0; - if ((keyPair & KEYPAIR_LOCAL) != 0) { - len += clear(localKeypair, apdu.getBuffer(), (short) 0); - } - if ((keyPair & KEYPAIR_REMOTE) != 0) { - len += clear(remoteKeypair, apdu.getBuffer(), len); - } - - return len; - } - - /** - * Sets curve parameters on local and remote keyPairs. - * returns setCurve SWs - * - * @param apdu P1 = byte keyPair (KEYPAIR_* | ...) - * P2 = byte curve (EC_Consts.CURVE_*) - * DATA = short params (EC_Consts.PARAMETER_* | ...) - * <p> - * if curveID = CURVE_EXTERNAL: - * [short paramLength, byte[] param], - * for all params in params, - * in order: field,a,b,g,r,k,w,s - * @return length of response - */ - private short insSet(APDU apdu) { - byte keyPair = apduArray[ISO7816.OFFSET_P1]; - byte curve = apduArray[ISO7816.OFFSET_P2]; - short cdata = apdu.getOffsetCdata(); - short params = Util.getShort(apduArray, cdata); - - short len = 0; - - if ((keyPair & KEYPAIR_LOCAL) != 0) { - len += set(localKeypair, curve, params, apduArray, (short) (cdata + 2), apdu.getBuffer(), (short) 0); - } - if ((keyPair & KEYPAIR_REMOTE) != 0) { - len += set(remoteKeypair, curve, params, apduArray, (short) (cdata + 2), apdu.getBuffer(), len); - } - - return len; - } - - /** - * Transforms curve paramaters of local and remote keyPairs. - * returns transformCurve SWs - * - * @param apdu P1 = byte keyPair (KEYPAIR_* | ...) - * P2 = byte key (EC_Consts.KEY_* | ...) - * DATA = short params (EC_Consts.PARAMETER_* | ...) - * short transformation (EC_Consts.TRANSFORMATION_* || ...) - * @return length of response - */ - private short insTransform(APDU apdu) { - byte keyPair = apduArray[ISO7816.OFFSET_P1]; - byte key = apduArray[ISO7816.OFFSET_P2]; - short cdata = apdu.getOffsetCdata(); - short params = Util.getShort(apduArray, cdata); - short transformation = Util.getShort(apduArray, (short) (cdata + 2)); - - short len = 0; - if ((keyPair & KEYPAIR_LOCAL) != 0) { - len += transform(localKeypair, key, params, transformation, apdu.getBuffer(), (short) 0); - } - - if ((keyPair & KEYPAIR_REMOTE) != 0) { - len += transform(remoteKeypair, key, params, transformation, apdu.getBuffer(), len); - } - - return len; - } - - /** - * Generates the local and remote keyPairs. - * returns generate SWs - * - * @param apdu P1 = byte keyPair (KEYPAIR_* | ...) - * P2 = - * @return length of response - */ - private short insGenerate(APDU apdu) { - byte keyPair = apduArray[ISO7816.OFFSET_P1]; - - short len = 0; - if ((keyPair & KEYPAIR_LOCAL) != 0) { - len += generate(localKeypair, apdu.getBuffer(), (short) 0); - } - if ((keyPair & KEYPAIR_REMOTE) != 0) { - len += generate(remoteKeypair, apdu.getBuffer(), len); - } - - return len; - } - - /** - * Exports selected key and domain parameters from the selected keyPair and key. - * - * @param apdu P1 = byte keyPair (KEYPAIR_* | ...) - * P2 = byte key (EC_Consts.KEY_* | ...) - * DATA = short params - * @return length of response - */ - private short insExport(APDU apdu) { - byte keyPair = apduArray[ISO7816.OFFSET_P1]; - byte key = apduArray[ISO7816.OFFSET_P2]; - short cdata = apdu.getOffsetCdata(); - short params = Util.getShort(apduArray, cdata); - - short swOffset = 0; - short len = (short) (keyPair == KEYPAIR_BOTH ? 4 : 2); - - if ((keyPair & KEYPAIR_LOCAL) != 0) { - len += export(localKeypair, key, params, apdu.getBuffer(), swOffset, len); - swOffset += 2; - } - if ((keyPair & KEYPAIR_REMOTE) != 0) { - len += export(remoteKeypair, key, params, apdu.getBuffer(), swOffset, len); - } - - return len; - } - - /** - * Performs ECDH, between the pubkey specified in P1(local/remote) and the privkey specified in P2(local/remote). - * returns deriveSecret SW, {@code if(export == EXPORT_TRUE)} => short secretlen, byte[] secret - * - * @param apdu P1 = byte pubkey (KEYPAIR_*) - * P2 = byte privkey (KEYPAIR_*) - * DATA = byte export (EXPORT_TRUE || EXPORT_FALSE) - * short transformation (EC_Consts.TRANSFORMATION_* | ...) - * byte type (EC_Consts.KA_* | ...) - * @return length of response - */ - private short insECDH(APDU apdu) { - byte pubkey = apduArray[ISO7816.OFFSET_P1]; - byte privkey = apduArray[ISO7816.OFFSET_P2]; - short cdata = apdu.getOffsetCdata(); - byte export = apduArray[cdata]; - short transformation = Util.getShort(apduArray, (short) (cdata + 1)); - byte type = apduArray[(short) (cdata + 3)]; - - return ecdh(pubkey, privkey, export, transformation, type, apdu.getBuffer(), (short) 0); - } - - /** - * Performs ECDH, directly between the privkey specified in P1(local/remote) and the raw data - * - * @param apdu P1 = byte privkey (KEYPAIR_*) - * P2 = byte export (EXPORT_TRUE || EXPORT_FALSE) - * DATA = short transformation (EC_Consts.TRANSFORMATION_* | ...) - * byte type (EC_Consts.KA_* | ...) - * short length - * byte[] pubkey - * @return length of response - */ - private short insECDH_direct(APDU apdu) { - byte privkey = apduArray[ISO7816.OFFSET_P1]; - byte export = apduArray[ISO7816.OFFSET_P2]; - short cdata = apdu.getOffsetCdata(); - short transformation = Util.getShort(apduArray, cdata); - byte type = apduArray[(short) (cdata + 2)]; - short length = Util.getShort(apduArray, (short) (cdata + 3)); - - return ecdh_direct(privkey, export, transformation, type, (short) (cdata + 5), length, apdu.getBuffer(), (short) 0); - } - - /** - * Performs ECDSA signature and verification on data provided or random, using the keyPair in P1(local/remote). - * returns ecdsa SW, {@code if(export == EXPORT_TRUE)} => short signature_length, byte[] signature - * - * @param apdu P1 = byte keyPair (KEYPAIR_*) - * P2 = byte export (EXPORT_TRUE || EXPORT_FALSE) - * DATA = byte sigType - * short dataLength (00 = random data generated, !00 = data length) - * byte[] data - * @return length of response - */ - private short insECDSA(APDU apdu) { - byte keyPair = apduArray[ISO7816.OFFSET_P1]; - byte export = apduArray[ISO7816.OFFSET_P2]; - short cdata = apdu.getOffsetCdata(); - byte sigType = apduArray[cdata]; - - short len = 0; - if ((keyPair & KEYPAIR_LOCAL) != 0) { - len += ecdsa(localKeypair, sigType, export, apduArray, (short) (cdata + 1), apdu.getBuffer(), (short) 0); - } - if ((keyPair & KEYPAIR_REMOTE) != 0) { - len += ecdsa(remoteKeypair, sigType, export, apduArray, (short) (cdata + 1), apdu.getBuffer(), len); - } - - return len; - } - - /** - * @param apdu P1 = byte keyPair (KEYPAIR_*) - * P2 = byte export (EXPORT_TRUE || EXPORT_FALSE) - * DATA = byte sigType - * short dataLength (00 = random data generated, !00 = data length) - * byte[] data - * @return length of response - */ - private short insECDSA_sign(APDU apdu) { - byte keyPair = apduArray[ISO7816.OFFSET_P1]; - byte export = apduArray[ISO7816.OFFSET_P2]; - short cdata = apdu.getOffsetCdata(); - byte sigType = apduArray[cdata]; - - short len = 0; - if ((keyPair & KEYPAIR_LOCAL) != 0) { - len += ecdsa_sign(localKeypair, sigType, export, apduArray, (short) (cdata + 1), apdu.getBuffer(), (short) 0); - } - if ((keyPair & KEYPAIR_REMOTE) != 0) { - len += ecdsa_sign(remoteKeypair, sigType, export, apduArray, (short) (cdata + 1), apdu.getBuffer(), len); - } - return len; - } - - /** - * @param apdu P1 = byte keyPair (KEYPAIR_*) - * P2 = byte sigType - * DATA = short dataLength (00 = random data generated, !00 = data length) - * byte[] data - * short sigLength - * byte[] signature - * @return length of response - */ - private short insECDSA_verify(APDU apdu) { - byte keyPair = apduArray[ISO7816.OFFSET_P1]; - byte sigType = apduArray[ISO7816.OFFSET_P2]; - short cdata = apdu.getOffsetCdata(); - - short len = 0; - if ((keyPair & KEYPAIR_LOCAL) != 0) { - len += ecdsa_verify(localKeypair, sigType, apduArray, cdata, apdu.getBuffer(), (short) 0); - } - if ((keyPair & KEYPAIR_REMOTE) != 0) { - len += ecdsa_verify(remoteKeypair, sigType, apduArray, cdata, apdu.getBuffer(), len); - } - return len; - } - - - /** - * Performs card memory cleanup via JCSystem.requestObjectDeletion() - * - * @param apdu no data - * @return length of response - */ - private short insCleanup(APDU apdu) { - byte[] apdubuf = apdu.getBuffer(); - - return cleanup(apdubuf, (short) 0); - } - - /** - * @param keyPair which keyPair to use, local/remote (KEYPAIR_* | ...) - * @param keyLength key length to set - * @param keyClass key class to allocate - * @param outBuffer buffer to write sw to - * @param outOffset offset into buffer - * @return length of data written to the buffer - */ - private short allocate(byte keyPair, short keyLength, byte keyClass, byte[] outBuffer, short outOffset) { - short length = 0; - if ((keyPair & KEYPAIR_LOCAL) != 0) { - localKeypair = keyGenerator.allocatePair(keyClass, keyLength); - Util.setShort(outBuffer, outOffset, keyGenerator.getSW()); - length += 2; - } - - if ((keyPair & KEYPAIR_REMOTE) != 0) { - remoteKeypair = keyGenerator.allocatePair(keyClass, keyLength); - Util.setShort(outBuffer, (short) (outOffset + length), keyGenerator.getSW()); - length += 2; - } - - return length; - } - - /** - * @param keyPair KeyPair to clear - * @param outBuffer buffer to write sw to - * @param outOffset offset into buffer - * @return length of data written to the buffer - */ - private short clear(KeyPair keyPair, byte[] outBuffer, short outOffset) { - short sw = keyGenerator.clearPair(keyPair, EC_Consts.KEY_BOTH); - Util.setShort(outBuffer, outOffset, sw); - - return 2; - } - - /** - * @param keyPair KeyPair to set params on - * @param curve curve to set (EC_Consts.CURVE_*) - * @param params parameters to set (EC_Consts.PARAMETER_* | ...) - * @param inBuffer buffer to read params from - * @param inOffset input offset in buffer - * @param outBuffer buffer to write sw to - * @param outOffset output offset in buffer - * @return length of data written to the buffer - */ - private short set(KeyPair keyPair, byte curve, short params, byte[] inBuffer, short inOffset, byte[] outBuffer, short outOffset) { - short sw = ISO7816.SW_NO_ERROR; - - switch (curve) { - case EC_Consts.CURVE_default: - //default, dont set anything - break; - case EC_Consts.CURVE_external: - //external - sw = keyGenerator.setExternalCurve(keyPair, params, inBuffer, inOffset); - break; - default: - //custom - sw = keyGenerator.setCurve(keyPair, curve, params, ramArray, (short) 0); - break; - } - - Util.setShort(outBuffer, outOffset, sw); - return 2; - } - - /** - * @param keyPair KeyPair to transform - * @param key key to transform (EC_Consts.KEY_* | ...) - * @param params parameters to transform (EC_Consts.PARAMETER_* | ...) - * @param transformation transformation type (EC_Consts.TRANSFORMATION_*) - * @param outBuffer buffer to output sw to - * @param outOffset output offset in buffer - * @return length of data written to the buffer - */ - private short transform(KeyPair keyPair, byte key, short params, short transformation, byte[] outBuffer, short outOffset) { - short sw = keyGenerator.transformCurve(keyPair, key, params, transformation, ramArray, (short) 0); - Util.setShort(outBuffer, outOffset, sw); - return 2; - } - - /** - * @param keyPair KeyPair to generate - * @param outBuffer buffer to output sw to - * @param outOffset output offset in buffer - * @return length of data written to the buffer - */ - private short generate(KeyPair keyPair, byte[] outBuffer, short outOffset) { - short sw = keyGenerator.generatePair(keyPair); - Util.setShort(outBuffer, outOffset, sw); - - return 2; - } - - /** - * @param keyPair KeyPair to export from - * @param key which key to export from (EC_Consts.KEY_PUBLIC | EC_Consts.KEY_PRIVATE) - * @param params which params to export (EC_Consts.PARAMETER_* | ...) - * @param outBuffer buffer to export params to - * @param swOffset offset to output sw to buffer - * @param outOffset output offset in buffer - * @return length of data written to the buffer - */ - private short export(KeyPair keyPair, byte key, short params, byte[] outBuffer, short swOffset, short outOffset) { - short length = 0; - - short sw = ISO7816.SW_NO_ERROR; - if ((key & EC_Consts.KEY_PUBLIC) != 0) { - //export params from public - length += keyGenerator.exportParameters(keyPair, EC_Consts.KEY_PUBLIC, params, outBuffer, outOffset); - sw = keyGenerator.getSW(); - } - if ((key & EC_Consts.KEY_PRIVATE) != 0 && sw == ISO7816.SW_NO_ERROR) { - //export params from private - length += keyGenerator.exportParameters(keyPair, EC_Consts.KEY_PRIVATE, params, outBuffer, (short) (outOffset + length)); - sw = keyGenerator.getSW(); - } - Util.setShort(outBuffer, swOffset, sw); - - return length; - } - - /** - * @param pubkey keyPair to use for public key, (KEYPAIR_LOCAL || KEYPAIR_REMOTE) - * @param privkey keyPair to use for private key, (KEYPAIR_LOCAL || KEYPAIR_REMOTE) - * @param export whether to export ECDH secret - * @param transformation whether to transform the pubkey before ECDH - * @param type KeyAgreement type to test - * @param outBuffer buffer to write sw to, and export ECDH secret {@code if(export == EXPORT_TRUE)} - * @param outOffset output offset in buffer - * @return length of data written to the buffer - */ - private short ecdh(byte pubkey, byte privkey, byte export, short transformation, byte type, byte[] outBuffer, short outOffset) { - short length = 0; - - KeyPair pub = ((pubkey & KEYPAIR_LOCAL) != 0) ? localKeypair : remoteKeypair; - KeyPair priv = ((privkey & KEYPAIR_LOCAL) != 0) ? localKeypair : remoteKeypair; - - short secretLength = 0; - if (keyTester.getKaType() == type) { - secretLength = keyTester.testKA(priv, pub, ramArray, (short) 0, ramArray2, (short) 0, transformation); - } else { - short allocateSW = keyTester.allocateKA(type); - if (allocateSW == ISO7816.SW_NO_ERROR) { - secretLength = keyTester.testKA(priv, pub, ramArray, (short) 0, ramArray2, (short) 0, transformation); - } - } - Util.setShort(outBuffer, outOffset, keyTester.getSW()); - length += 2; - - if ((export == EXPORT_TRUE)) { - Util.setShort(outBuffer, (short) (outOffset + length), secretLength); - length += 2; - Util.arrayCopyNonAtomic(ramArray2, (short) 0, outBuffer, (short) (outOffset + length), secretLength); - length += secretLength; - } - - return length; - } - - private short ecdh_direct(byte privkey, byte export, short transformation, byte type, short keyOffset, short keyLength, byte[] outBuffer, short outOffset) { - short length = 0; - - KeyPair priv = ((privkey & KEYPAIR_LOCAL) != 0) ? localKeypair : remoteKeypair; - - short secretLength = 0; - if (keyTester.getKaType() == type) { - secretLength = keyTester.testKA_direct(priv, apduArray, keyOffset, keyLength, ramArray2, (short) 0, transformation); - } else { - short allocateSW = keyTester.allocateKA(type); - if (allocateSW == ISO7816.SW_NO_ERROR) { - secretLength = keyTester.testKA_direct(priv, apduArray, keyOffset, keyLength, ramArray2, (short) 0, transformation); - } - } - - Util.setShort(outBuffer, outOffset, keyTester.getSW()); - length += 2; - - if ((export == EXPORT_TRUE)) { - Util.setShort(outBuffer, (short) (outOffset + length), secretLength); - length += 2; - Util.arrayCopyNonAtomic(ramArray2, (short) 0, outBuffer, (short) (outOffset + length), secretLength); - length += secretLength; - } - return length; - } - - /** - * @param sign keyPair to use for signing and verification - * @param sigType Signature type to use - * @param export whether to export ECDSA signature - * @param inBuffer buffer to read dataLength and data to sign from - * @param inOffset input offset in buffer - * @param outBuffer buffer to write sw to, and export ECDSA signature {@code if(export == EXPORT_TRUE)} - * @param outOffset output offset in buffer - * @return length of data written to the buffer - */ - private short ecdsa(KeyPair sign, byte sigType, byte export, byte[] inBuffer, short inOffset, byte[] outBuffer, short outOffset) { - short length = 0; - - short dataLength = Util.getShort(inBuffer, inOffset); - if (dataLength == 0) { //no data to sign - //generate random - dataLength = 64; - randomData.generateData(ramArray, (short) 0, dataLength); - } else { - Util.arrayCopyNonAtomic(inBuffer, (short) (inOffset + 2), ramArray, (short) 0, dataLength); - } - - short signatureLength = 0; - if (keyTester.getSigType() == sigType) { - signatureLength = keyTester.testECDSA((ECPrivateKey) sign.getPrivate(), (ECPublicKey) sign.getPublic(), ramArray, (short) 0, dataLength, ramArray2, (short) 0); - } else { - short allocateSW = keyTester.allocateSig(sigType); - if (allocateSW == ISO7816.SW_NO_ERROR) { - signatureLength = keyTester.testECDSA((ECPrivateKey) sign.getPrivate(), (ECPublicKey) sign.getPublic(), ramArray, (short) 0, dataLength, ramArray2, (short) 0); - } - } - Util.setShort(outBuffer, outOffset, keyTester.getSW()); - length += 2; - - if (export == EXPORT_TRUE) { - Util.setShort(outBuffer, (short) (outOffset + length), signatureLength); - length += 2; - - Util.arrayCopyNonAtomic(ramArray2, (short) 0, outBuffer, (short) (outOffset + length), signatureLength); - length += signatureLength; - } - - return length; - } - - private short ecdsa_sign(KeyPair sign, byte sigType, byte export, byte[] inBuffer, short inOffset, byte[] outBuffer, short outOffset) { - short length = 0; - - short dataLength = Util.getShort(inBuffer, inOffset); - if (dataLength == 0) { //no data to sign - //generate random - dataLength = 64; - randomData.generateData(ramArray, (short) 0, dataLength); - } else { - Util.arrayCopyNonAtomic(inBuffer, (short) (inOffset + 2), ramArray, (short) 0, dataLength); - } - - short signatureLength = 0; - if (keyTester.getSigType() == sigType) { - signatureLength = keyTester.testECDSA_sign((ECPrivateKey) sign.getPrivate(), ramArray, (short) 0, dataLength, ramArray2, (short) 0); - } else { - short allocateSW = keyTester.allocateSig(sigType); - if (allocateSW == ISO7816.SW_NO_ERROR) { - signatureLength = keyTester.testECDSA_sign((ECPrivateKey) sign.getPrivate(), ramArray, (short) 0, dataLength, ramArray2, (short) 0); - } - } - Util.setShort(outBuffer, outOffset, keyTester.getSW()); - length += 2; - - if (export == EXPORT_TRUE) { - Util.setShort(outBuffer, (short) (outOffset + length), signatureLength); - length += 2; - - Util.arrayCopyNonAtomic(ramArray2, (short) 0, outBuffer, (short) (outOffset + length), signatureLength); - length += signatureLength; - } - - return length; + short getOffsetCdata(APDU apdu) { + return ISO7816.OFFSET_CDATA; } - private short ecdsa_verify(KeyPair verify, byte sigType, byte[] inBuffer, short inOffset, byte[] outBuffer, short outOffset) { - short length = 0; - - short dataLength = Util.getShort(inBuffer, inOffset); - short dataOffset = (short) (inOffset + 2); - short sigLength = Util.getShort(inBuffer, (short) (dataOffset + dataLength)); - short sigOffset = (short) (dataOffset + dataLength + 2); - - if (keyTester.getSigType() == sigType) { - keyTester.testECDSA_verify((ECPublicKey) verify.getPublic(), inBuffer, dataOffset, dataLength, inBuffer, sigOffset, sigLength); - } else { - short allocateSW = keyTester.allocateSig(sigType); - if (allocateSW == ISO7816.SW_NO_ERROR) { - keyTester.testECDSA_verify((ECPublicKey) verify.getPublic(), inBuffer, dataOffset, dataLength, inBuffer, sigOffset, sigLength); - } - } - Util.setShort(outBuffer, outOffset, keyTester.getSW()); - length += 2; - - return length; + short getIncomingLength(APDU apdu) { + byte[] apduBuffer = apdu.getBuffer(); + return apduBuffer[ISO7816.OFFSET_LC]; } - /** - * @param buffer buffer to write sw to - * @param offset output offset in buffer - * @return length of data written to the buffer - */ - private short cleanup(byte[] buffer, short offset) { - short sw = ISO7816.SW_NO_ERROR; - try { - if (JCSystem.isObjectDeletionSupported()) - JCSystem.requestObjectDeletion(); - } catch (CardRuntimeException crex) { - sw = crex.getReason(); - } - - Util.setShort(buffer, offset, sw); - return 2; + short getBase() { + return AppletBase.BASE_221; } } diff --git a/src/cz/crcs/ectester/applet/ECTesterAppletExtended.java b/src/cz/crcs/ectester/applet/ECTesterAppletExtended.java new file mode 100644 index 0000000..83e0851 --- /dev/null +++ b/src/cz/crcs/ectester/applet/ECTesterAppletExtended.java @@ -0,0 +1,61 @@ +/* + * ECTester, tool for testing Elliptic curve cryptography implementations. + * Copyright (c) 2016-2018 Petr Svenda <petr@svenda.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +/* + * PACKAGEID: 4543546573746572 + * APPLETID: 45435465737465723031 + */ +package cz.crcs.ectester.applet; + +import javacard.framework.APDU; +import javacard.framework.ISOException; +import javacardx.apdu.ExtendedLength; + +/** + * Applet part of ECTester, a tool for testing Elliptic curve support on javacards. + * + * @author Petr Svenda petr@svenda.com + * @author Jan Jancar johny@neuromancer.sk + */ +public class ECTesterAppletExtended extends AppletBase implements ExtendedLength { + protected ECTesterAppletExtended(byte[] buffer, short offset, byte length) { + super(buffer, offset, length); + register(); + } + + public static void install(byte[] bArray, short bOffset, byte bLength) throws ISOException { + // applet instance creation + new ECTesterAppletExtended(bArray, bOffset, bLength); + } + + short getOffsetCdata(APDU apdu) { + return apdu.getOffsetCdata(); + } + + short getIncomingLength(APDU apdu) { + return apdu.getIncomingLength(); + } + + short getBase() { + return AppletBase.BASE_222; + } +} diff --git a/src/cz/crcs/ectester/common/ec/EC_Category.java b/src/cz/crcs/ectester/common/ec/EC_Category.java index 8af308c..05c910c 100644 --- a/src/cz/crcs/ectester/common/ec/EC_Category.java +++ b/src/cz/crcs/ectester/common/ec/EC_Category.java @@ -91,56 +91,21 @@ public class EC_Category { out.append(System.lineSeparator()); } - Map<String, EC_Key> keys = getObjects(EC_Key.class); - size = keys.size(); - if (size > 0) { - out.append(Colors.bold("\t\tKeys: ")); - for (Map.Entry<String, EC_Key> key : keys.entrySet()) { - out.append(key.getKey()); - size--; - if (size > 0) - out.append(", "); + 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}; + for (int i = 0; i < headers.length; ++i) { + Map<String, EC_Data> data = getObjects(classes[i]); + size = data.size(); + if (size > 0) { + out.append(Colors.bold(String.format("\t\t%s: ", headers[i]))); + for (Map.Entry<String, EC_Data> key : data.entrySet()) { + out.append(key.getKey()); + size--; + if (size > 0) + out.append(", "); + } + out.append(System.lineSeparator()); } - out.append(System.lineSeparator()); - } - - Map<String, EC_Keypair> keypairs = getObjects(EC_Keypair.class); - size = keypairs.size(); - if (size > 0) { - out.append(Colors.bold("\t\tKeypairs: ")); - for (Map.Entry<String, EC_Keypair> key : keypairs.entrySet()) { - out.append(key.getKey()); - size--; - if (size > 0) - out.append(", "); - } - out.append(System.lineSeparator()); - } - - Map<String, EC_KAResult> kaResults = getObjects(EC_KAResult.class); - size = kaResults.size(); - if (size > 0) { - out.append(Colors.bold("\t\tResults(KA): ")); - for (Map.Entry<String, EC_KAResult> result : kaResults.entrySet()) { - out.append(result.getKey()); - size--; - if (size > 0) - out.append(", "); - } - out.append(System.lineSeparator()); - } - - Map<String, EC_SigResult> sigResults = getObjects(EC_SigResult.class); - size = sigResults.size(); - if (size > 0) { - out.append(Colors.bold("\t\tResults(SIG): ")); - for (Map.Entry<String, EC_SigResult> result : sigResults.entrySet()) { - out.append(result.getKey()); - size--; - if (size > 0) - out.append(", "); - } - out.append(System.lineSeparator()); } return out.toString(); } diff --git a/src/cz/crcs/ectester/common/ec/EC_SigResult.java b/src/cz/crcs/ectester/common/ec/EC_SigResult.java index f1ab0f5..d97ced1 100644 --- a/src/cz/crcs/ectester/common/ec/EC_SigResult.java +++ b/src/cz/crcs/ectester/common/ec/EC_SigResult.java @@ -69,7 +69,7 @@ public class EC_SigResult extends EC_Data { @Override public String toString() { - return "<" + getId() + "> " + sig + " result over " + curve + ", " + signKey + " + " + verifyKey + (data == null ? "" : " of data \"" + data + "\"") + (desc == null ? "" : ": " + desc) + System.lineSeparator() + super.toString(); + return "<" + getId() + "> " + sig + " result over " + curve + ", " + signKey + " + " + verifyKey + (data == null ? "" : " of data \"" + data + "\"") + (desc == null ? "" : ": " + desc) + System.lineSeparator() + super.toString(); } } diff --git a/src/cz/crcs/ectester/common/output/BaseTextTestWriter.java b/src/cz/crcs/ectester/common/output/BaseTextTestWriter.java index f60f8bb..85b32a4 100644 --- a/src/cz/crcs/ectester/common/output/BaseTextTestWriter.java +++ b/src/cz/crcs/ectester/common/output/BaseTextTestWriter.java @@ -106,6 +106,9 @@ public abstract class BaseTextTestWriter implements TestWriter { } else { SimpleTest<? extends BaseTestable> test = (SimpleTest<? extends BaseTestable>) t; out.append(testableString(test.getTestable())); + if (t.getResult().getCause() != null) { + out.append(" ā ").append(t.getResult().getCause().toString()); + } } return line + out.toString(); } diff --git a/src/cz/crcs/ectester/common/util/CardUtil.java b/src/cz/crcs/ectester/common/util/CardUtil.java index e7b370c..7483c32 100644 --- a/src/cz/crcs/ectester/common/util/CardUtil.java +++ b/src/cz/crcs/ectester/common/util/CardUtil.java @@ -327,16 +327,22 @@ public class CardUtil { public static byte getKAType(String kaTypeString) { switch (kaTypeString) { + case "DH": case "ALG_EC_SVDP_DH": return EC_Consts.KeyAgreement_ALG_EC_SVDP_DH; + case "DH_PLAIN": case "ALG_EC_SVDP_DH_PLAIN": return EC_Consts.KeyAgreement_ALG_EC_SVDP_DH_PLAIN; + case "PACE_GM": case "ALG_EC_PACE_GM": return EC_Consts.KeyAgreement_ALG_EC_PACE_GM; + case "DH_PLAIN_XY": case "ALG_EC_SVDP_DH_PLAIN_XY": return EC_Consts.KeyAgreement_ALG_EC_SVDP_DH_PLAIN_XY; + case "DHC": case "ALG_EC_SVDP_DHC": return EC_Consts.KeyAgreement_ALG_EC_SVDP_DHC; + case "DHC_PLAIN": case "ALG_EC_SVDP_DHC_PLAIN": return EC_Consts.KeyAgreement_ALG_EC_SVDP_DHC_PLAIN; default: @@ -373,14 +379,19 @@ public class CardUtil { public static byte getSigType(String sigTypeString) { switch (sigTypeString) { + case "ECDSA_SHA": case "ALG_ECDSA_SHA": return EC_Consts.Signature_ALG_ECDSA_SHA; + case "ECDSA_SHA_224": case "ALG_ECDSA_SHA_224": return EC_Consts.Signature_ALG_ECDSA_SHA_224; + case "ECDSA_SHA_256": case "ALG_ECDSA_SHA_256": return EC_Consts.Signature_ALG_ECDSA_SHA_256; + case "ECDSA_SHA_384": case "ALG_ECDSA_SHA_384": return EC_Consts.Signature_ALG_ECDSA_SHA_384; + case "ECDSA_SHA_512": case "ALG_ECDSA_SHA_512": return EC_Consts.Signature_ALG_ECDSA_SHA_512; default: @@ -409,6 +420,58 @@ public class CardUtil { } } + public static String getCurveName(byte curve) { + String result = ""; + switch (curve) { + case EC_Consts.CURVE_default: + result = "default"; + break; + case EC_Consts.CURVE_external: + result = "external"; + break; + case EC_Consts.CURVE_secp112r1: + result = "secp112r1"; + break; + case EC_Consts.CURVE_secp128r1: + result = "secp128r1"; + break; + case EC_Consts.CURVE_secp160r1: + result = "secp160r1"; + break; + case EC_Consts.CURVE_secp192r1: + result = "secp192r1"; + break; + case EC_Consts.CURVE_secp224r1: + result = "secp224r1"; + break; + case EC_Consts.CURVE_secp256r1: + result = "secp256r1"; + break; + case EC_Consts.CURVE_secp384r1: + result = "secp384r1"; + break; + case EC_Consts.CURVE_secp521r1: + result = "secp521r1"; + break; + case EC_Consts.CURVE_sect163r1: + result = "sect163r1"; + break; + case EC_Consts.CURVE_sect233r1: + result = "sect233r1"; + break; + case EC_Consts.CURVE_sect283r1: + result = "sect283r1"; + break; + case EC_Consts.CURVE_sect409r1: + result = "sect409r1"; + break; + case EC_Consts.CURVE_sect571r1: + result = "sect571r1"; + break; + } + return result; + } + public static String getParameterString(short params) { String what = ""; if (params == EC_Consts.PARAMETERS_DOMAIN_F2M || params == EC_Consts.PARAMETERS_DOMAIN_FP) { diff --git a/src/cz/crcs/ectester/common/util/ECUtil.java b/src/cz/crcs/ectester/common/util/ECUtil.java index 6c3ad58..1706ca0 100644 --- a/src/cz/crcs/ectester/common/util/ECUtil.java +++ b/src/cz/crcs/ectester/common/util/ECUtil.java @@ -31,6 +31,14 @@ public class ECUtil { return raw; } + public static byte[] toX962Compressed(byte[][] point) { + if (point.length != 2) { + return null; + } + byte ybit = (byte) (point[1][point[1].length - 1] % 2); + return ByteUtil.concatenate(new byte[]{(byte) (0x02 | ybit)}, point[0]); + } + public static byte[] toX962Compressed(ECPoint point, int bits) { if (point.equals(ECPoint.POINT_INFINITY)) { return new byte[]{0}; diff --git a/src/cz/crcs/ectester/data/EC_Store.java b/src/cz/crcs/ectester/data/EC_Store.java index f1d4260..d104fa4 100644 --- a/src/cz/crcs/ectester/data/EC_Store.java +++ b/src/cz/crcs/ectester/data/EC_Store.java @@ -350,7 +350,7 @@ public class EC_Store { return getObject(objClass, query.substring(0, split), query.substring(split + 1)); } - private static <T extends EC_Data> List<Map.Entry<EC_Curve, List<T>>> mapKeyToCurve(Collection<T> data, Function<T, String> getter) { + private static <T extends EC_Data> Map<EC_Curve, List<T>> mapKeyToCurve(Collection<T> data, Function<T, String> getter) { Map<EC_Curve, List<T>> curves = new TreeMap<>(); for (T item : data) { EC_Curve curve = EC_Store.getInstance().getObject(EC_Curve.class, getter.apply(item)); @@ -361,21 +361,18 @@ public class EC_Store { for (List<T> keyList : curves.values()) { Collections.sort(keyList); } - List<Map.Entry<EC_Curve, List<T>>> curveList = new LinkedList<>(); - curveList.addAll(curves.entrySet()); - Comparator<Map.Entry<EC_Curve, List<T>>> c = Comparator.comparing(Map.Entry::getKey); - return curveList; + return curves; } - public static <T extends EC_Key> List<Map.Entry<EC_Curve, List<T>>> mapKeyToCurve(Collection<T> keys) { + public static <T extends EC_Key> Map<EC_Curve, List<T>> mapKeyToCurve(Collection<T> keys) { return mapKeyToCurve(keys, EC_Key::getCurve); } - public static List<Map.Entry<EC_Curve, List<EC_KAResult>>> mapResultToCurve(Collection<EC_KAResult> results) { + public static Map<EC_Curve, List<EC_KAResult>> mapResultToCurve(Collection<EC_KAResult> results) { return mapKeyToCurve(results, EC_KAResult::getCurve); } - public static <T extends EC_Data> List<Map.Entry<String, List<T>>> mapToPrefix(Collection<T> data) { + public static <T extends EC_Data> Map<String, List<T>> mapToPrefix(Collection<T> data) { Map<String, List<T>> groups = new TreeMap<>(); for (T item : data) { String prefix = item.getId().split("/")[0]; @@ -386,10 +383,7 @@ public class EC_Store { for (List<T> itemList : groups.values()) { Collections.sort(itemList); } - List<Map.Entry<String, List<T>>> result = new LinkedList<>(); - result.addAll(groups.entrySet()); - result.sort(Comparator.comparing(Map.Entry::getKey)); - return result; + return groups; } public static EC_Store getInstance() { diff --git a/src/cz/crcs/ectester/data/bn/curves.xml b/src/cz/crcs/ectester/data/bn/curves.xml index ddf2263..c5ca22f 100644 --- a/src/cz/crcs/ectester/data/bn/curves.xml +++ b/src/cz/crcs/ectester/data/bn/curves.xml @@ -6,95 +6,111 @@ <bits>158</bits> <field>prime</field> <file>bn158.csv</file> + <desc>Barreto-Naehrig curve from eprint 2010/429.</desc> </curve> <curve> <id>bn190</id> <bits>190</bits> <field>prime</field> <file>bn190.csv</file> + <desc>Barreto-Naehrig curve from eprint 2010/429.</desc> </curve> <curve> <id>bn222</id> <bits>222</bits> <field>prime</field> <file>bn222.csv</file> + <desc>Barreto-Naehrig curve from eprint 2010/429.</desc> </curve> <curve> <id>bn254</id> <bits>254</bits> <field>prime</field> <file>bn254.csv</file> + <desc>Barreto-Naehrig curve from eprint 2010/429.</desc> </curve> <curve> <id>bn286</id> <bits>286</bits> <field>prime</field> <file>bn286.csv</file> + <desc>Barreto-Naehrig curve from eprint 2010/429.</desc> </curve> <curve> <id>bn318</id> <bits>318</bits> <field>prime</field> <file>bn318.csv</file> + <desc>Barreto-Naehrig curve from eprint 2010/429.</desc> </curve> <curve> <id>bn350</id> <bits>350</bits> <field>prime</field> <file>bn350.csv</file> + <desc>Barreto-Naehrig curve from eprint 2010/429.</desc> </curve> <curve> <id>bn382</id> <bits>382</bits> <field>prime</field> <file>bn382.csv</file> + <desc>Barreto-Naehrig curve from eprint 2010/429.</desc> </curve> <curve> <id>bn414</id> <bits>414</bits> <field>prime</field> <file>bn414.csv</file> + <desc>Barreto-Naehrig curve from eprint 2010/429.</desc> </curve> <curve> <id>bn446</id> <bits>446</bits> <field>prime</field> <file>bn446.csv</file> + <desc>Barreto-Naehrig curve from eprint 2010/429.</desc> </curve> <curve> <id>bn478</id> <bits>478</bits> <field>prime</field> <file>bn478.csv</file> + <desc>Barreto-Naehrig curve from eprint 2010/429.</desc> </curve> <curve> <id>bn510</id> <bits>510</bits> <field>prime</field> <file>bn510.csv</file> + <desc>Barreto-Naehrig curve from eprint 2010/429.</desc> </curve> <curve> <id>bn542</id> <bits>542</bits> <field>prime</field> <file>bn542.csv</file> + <desc>Barreto-Naehrig curve from eprint 2010/429.</desc> </curve> <curve> <id>bn574</id> <bits>574</bits> <field>prime</field> <file>bn574.csv</file> + <desc>Barreto-Naehrig curve from eprint 2010/429.</desc> </curve> <curve> <id>bn606</id> <bits>606</bits> <field>prime</field> <file>bn606.csv</file> + <desc>Barreto-Naehrig curve from eprint 2010/429.</desc> </curve> <curve> <id>bn638</id> <bits>638</bits> <field>prime</field> <file>bn638.csv</file> + <desc>Barreto-Naehrig curve from eprint 2010/429.</desc> </curve> </curves>
\ No newline at end of file diff --git a/src/cz/crcs/ectester/data/categories.xml b/src/cz/crcs/ectester/data/categories.xml index 082f92c..0776b99 100644 --- a/src/cz/crcs/ectester/data/categories.xml +++ b/src/cz/crcs/ectester/data/categories.xml @@ -5,7 +5,7 @@ <category> <name>anomalous</name> <directory>anomalous</directory> - <desc>These prime field curves have the same order as the field order, and are susceptible to attacks reducing ECDLP over a multiplicative group of the curve, to DLP over an additive group of the underlying field, which is easy (linear time).</desc> + <desc>These prime field curves have the same order as the field order. Some are from https://dspace.jaist.ac.jp/dspace/bitstream/10119/4464/1/73-61.pdf.</desc> </category> <category> <name>brainpool</name> @@ -43,6 +43,11 @@ <desc>Barreto-Naehrig curves from: A Family of Implementation-Friendly BN Elliptic Curves - https://eprint.iacr.org/2010/429.pdf.</desc> </category> <category> + <name>MNT</name> + <directory>mnt</directory> + <desc>MNT (Miyaji, Nakabayashi, and Takano) example curves from: New explicit conditions of elliptic curve traces for FR-reduction - https://dspace.jaist.ac.jp/dspace/bitstream/10119/4432/1/73-48.pdf.</desc> + </category> + <category> <name>other</name> <directory>other</directory> <desc>An assortment of some other curves.</desc> @@ -94,4 +99,9 @@ <directory>supersingular</directory> <desc>Some supersingular curves, over F_p with order equal to p + 1.</desc> </category> + <category> + <name>misc</name> + <directory>misc</directory> + <desc>Miscellaneous data.</desc> + </category> </categories>
\ No newline at end of file diff --git a/src/cz/crcs/ectester/data/misc/keys.xml b/src/cz/crcs/ectester/data/misc/keys.xml new file mode 100644 index 0000000..e17b45b --- /dev/null +++ b/src/cz/crcs/ectester/data/misc/keys.xml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="utf-8" ?> +<keys xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../schema.xsd"> + <privkey> + <id>openssl-bug/skey</id> + <inline>0x4543A4D2C9DDD5516FD16D5498034C76D4EAB340276B6BAD8FF57756F4ECA8E6</inline> + <curve>secg/secp256r1</curve> + </privkey> + <pubkey> + <id>openssl-bug/pkey</id> + <inline>0x296D416994A4801B9A48E8C67C98E0C05DE1C0E85D4DC676F32FEACDC4998F0E,0xA91F9BE06C1D50EEB0295A35CA0F130F17EA647147626318E28AEC97F0653749</inline> + <curve>secg/secp256r1</curve> + </pubkey> + + <pubkey> + <id>compression/128/non-residue</id> + <inline>0xb6707fa8afeddf79b9579e8dda4eaf51,0x000000000000000000000000000000</inline> + <curve>secg/secp128r1</curve> + </pubkey> + <pubkey> + <id>compression/160/non-residue</id> + <inline>0xb1cb90992ff28689c6f160dcfb51b9525492e3d9,0x0000000000000000000000000000000000000000</inline> + <curve>secg/secp160r1</curve> + </pubkey> + <pubkey> + <id>compression/192/non-residue</id> + <inline>0x8910baef94195e069c142b129e97507bfc2e19b53b707441,0x000000000000000000000000000000000000000000000000</inline> + <curve>secg/secp192r1</curve> + </pubkey> + <pubkey> + <id>compression/224/non-residue</id> + <inline>0xafd44b41555e8bea506518b35405d4f5be78355d6342e7f5287bd748,0x00000000000000000000000000000000000000000000000000000000</inline> + <curve>secg/secp224r1</curve> + </pubkey> + <pubkey> + <id>compression/256/non-residue</id> + <inline>0xeb7a88c476ede6ecae7909aa19631d9918762e851c38a3ea00fe50b7b2e2e656,0x0000000000000000000000000000000000000000000000000000000000000000</inline> + <curve>secg/secp256r1</curve> + </pubkey> + <pubkey> + <id>compression/384/non-residue</id> + <inline>0x45d50b222c11c0f20946133382a988caf2d4f64e669340ba60a5ab3151a6bf3883e7e77a6d358fd07db411bc8ad0f375,0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</inline> + <curve>secg/secp384r1</curve> + </pubkey> + <pubkey> + <id>compression/521/non-residue</id> + <inline>0x1d7b127de8415bbf498c26f7a17c9e39dcd866b68359bc8e139f401f8ee8489419fb6166850c98cce7e1fdc620902961656d72f9b42703f06ccb9fe6e218e7e3fe3,0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</inline> + <curve>secg/secp521r1</curve> + </pubkey> +</keys> diff --git a/src/cz/crcs/ectester/data/other/results.xml b/src/cz/crcs/ectester/data/misc/results.xml index ba8c83c..ba8c83c 100644 --- a/src/cz/crcs/ectester/data/other/results.xml +++ b/src/cz/crcs/ectester/data/misc/results.xml diff --git a/src/cz/crcs/ectester/data/mnt/curves.xml b/src/cz/crcs/ectester/data/mnt/curves.xml new file mode 100644 index 0000000..0087a5a --- /dev/null +++ b/src/cz/crcs/ectester/data/mnt/curves.xml @@ -0,0 +1,74 @@ +<?xml version="1.0" encoding="utf-8" ?> +<curves xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../schema.xsd"> + <curve> + <id>mnt1</id> + <bits>170</bits> + <field>prime</field> + <file>mnt1.csv</file> + <desc>Example 1</desc> + </curve> + <curve> + <id>mnt2/1</id> + <bits>159</bits> + <field>prime</field> + <file>mnt2_1.csv</file> + <desc>Example 2/1</desc> + </curve> + <curve> + <id>mnt2/2</id> + <bits>159</bits> + <field>prime</field> + <file>mnt2_2.csv</file> + <desc>Example 2/2</desc> + </curve> + <curve> + <id>mnt3/1</id> + <bits>160</bits> + <field>prime</field> + <file>mnt3_1.csv</file> + <desc>Example 3/1</desc> + </curve> + <curve> + <id>mnt3/2</id> + <bits>160</bits> + <field>prime</field> + <file>mnt3_2.csv</file> + <desc>Example 3/2</desc> + </curve> + <curve> + <id>mnt3/3</id> + <bits>160</bits> + <field>prime</field> + <file>mnt3_3.csv</file> + <desc>Example 3/3</desc> + </curve> + <curve> + <id>mnt4</id> + <bits>240</bits> + <field>prime</field> + <file>mnt4.csv</file> + <desc>Example 4</desc> + </curve> + <curve> + <id>mnt5/1</id> + <bits>240</bits> + <field>prime</field> + <file>mnt5_1.csv</file> + <desc>Example 5/1</desc> + </curve> + <curve> + <id>mnt5/2</id> + <bits>240</bits> + <field>prime</field> + <file>mnt5_2.csv</file> + <desc>Example 5/2</desc> + </curve> + <curve> + <id>mnt5/3</id> + <bits>240</bits> + <field>prime</field> + <file>mnt5_3.csv</file> + <desc>Example 5/3</desc> + </curve> +</curves>
\ No newline at end of file diff --git a/src/cz/crcs/ectester/data/mnt/mnt1.csv b/src/cz/crcs/ectester/data/mnt/mnt1.csv new file mode 100644 index 0000000..7ff5784 --- /dev/null +++ b/src/cz/crcs/ectester/data/mnt/mnt1.csv @@ -0,0 +1 @@ +0x26dccacc5041939206cf2b7dec50950e3c9fa4827af,0x22ffbb20cc052993fa27dc507800b624c650e4ff3d2,0x1c7be6fa8da953b5624efc72406af7fa77499803d08,0x25a3ae778f7ef6586abae5acde21e54b6c64edf33d0,0x05b4ace33aa53c670ce35535d6c273698a182da557d,0x0000a60fd646ad409b3312c3b23ba64e082ad7b354d,0x01
\ No newline at end of file diff --git a/src/cz/crcs/ectester/data/mnt/mnt2_1.csv b/src/cz/crcs/ectester/data/mnt/mnt2_1.csv new file mode 100644 index 0000000..a53376f --- /dev/null +++ b/src/cz/crcs/ectester/data/mnt/mnt2_1.csv @@ -0,0 +1 @@ +0x5affffffffffff4b46081000000059bb1bf600b7,0x3dd24a7e5c0bdfaccc215e22760469c73ee9d879,0x478c31a992b294e19f6e4416f958646dddede5e3,0x2725af3d7dea98cb9242ac6ddb9bd89bdcf38898,0x480b4184ed2c50c0230b4c73ca939c1b6b7f1103,0x5affffffffffff4b46081000000059bb1bf600b5,0x01
\ No newline at end of file diff --git a/src/cz/crcs/ectester/data/mnt/mnt2_2.csv b/src/cz/crcs/ectester/data/mnt/mnt2_2.csv new file mode 100644 index 0000000..20dd8f4 --- /dev/null +++ b/src/cz/crcs/ectester/data/mnt/mnt2_2.csv @@ -0,0 +1 @@ +0x5affffffffffff4b46081000000059bb1bf600b7,0x07b29491c1a02cd87844f5098d0381f6c45d6523,0x41cc630bd66ac817d43358b108ad3d214037993c,0x0d76b3e1f1ed76a282fa99575d29ff2e587049e9,0x36e1557ed145ad409f924420e12f74a900fab054,0x5affffffffffff4b46081000000059bb1bf600b5,0x01
\ No newline at end of file diff --git a/src/cz/crcs/ectester/data/mnt/mnt3_1.csv b/src/cz/crcs/ectester/data/mnt/mnt3_1.csv new file mode 100644 index 0000000..d00719d --- /dev/null +++ b/src/cz/crcs/ectester/data/mnt/mnt3_1.csv @@ -0,0 +1 @@ +0x8afffffffffffeeb0fa77000000089f0dd49fac7,0x6d01fd0a017c62075ae999977379867e07f2a6d4,0x7701535c00fd965341d38bba4cfbdcf9a4651825,0x1781998103c3ca14ea76b9d3a700a53e1c784789,0x53352dde04447c25c9bb332a3c7634d3b8801f34,0x8afffffffffffeeb0fa77000000089f0dd49fac5,0x01
\ No newline at end of file diff --git a/src/cz/crcs/ectester/data/mnt/mnt3_2.csv b/src/cz/crcs/ectester/data/mnt/mnt3_2.csv new file mode 100644 index 0000000..86d8191 --- /dev/null +++ b/src/cz/crcs/ectester/data/mnt/mnt3_2.csv @@ -0,0 +1 @@ +0x8afffffffffffeeb0fa77000000089f0dd49fac7,0x5fbe0085bd2b23afcd5b9c7704aeed2bfdbe89e4,0x3fd4005928c76d1fde3d12fa031f48c7fe7f0698,0x494e297179d42c761701ab03b2e5bca98a24dfe7,0x3274201d6596252a780390a222e3763bbecfe5f1,0x8afffffffffffeeb0fa77000000089f0dd49fac5,0x01
\ No newline at end of file diff --git a/src/cz/crcs/ectester/data/mnt/mnt3_3.csv b/src/cz/crcs/ectester/data/mnt/mnt3_3.csv new file mode 100644 index 0000000..348b30d --- /dev/null +++ b/src/cz/crcs/ectester/data/mnt/mnt3_3.csv @@ -0,0 +1 @@ +0x8afffffffffffeeb0fa77000000089f0dd49fac7,0x2ddf23acb05a91bda6ba9c20d7a584aa25075ce0,0x1f8125c46a31e79fd6cc25298b23ab130cd22b5a,0x3f710d05b65b5e16ae1b946d3fc582b16a927432,0x4a30945c64fd7f85e148ba816005468447616b1f,0x8afffffffffffeeb0fa77000000089f0dd49fac5,0x01
\ No newline at end of file diff --git a/src/cz/crcs/ectester/data/mnt/mnt4.csv b/src/cz/crcs/ectester/data/mnt/mnt4.csv new file mode 100644 index 0000000..ea5eaa1 --- /dev/null +++ b/src/cz/crcs/ectester/data/mnt/mnt4.csv @@ -0,0 +1 @@ +0xa2ffffffffffffffffffffffffc298b00000000000000000000005c866cf,0x4be28760aa064734852cb4ff51ef2928a7a3cd75087c35cb1433714f7407,0x329704eb1c042f7858c878aa369f70c5c517de4e05a823dcb8224b8a4d5a,0x82556d57811807a0d7675674b3d57222cfbf9a2a2a2cd146572d7b67627e,0x73afacea28dc870baa1d5b0bd4300ddd975e2eefc7c2db508fc2e92a8345,0xa2ffffffffffffffffffffffffc298b00000000000000000000005c866cd,0x01
\ No newline at end of file diff --git a/src/cz/crcs/ectester/data/mnt/mnt5_1.csv b/src/cz/crcs/ectester/data/mnt/mnt5_1.csv new file mode 100644 index 0000000..2fd1622 --- /dev/null +++ b/src/cz/crcs/ectester/data/mnt/mnt5_1.csv @@ -0,0 +1 @@ +0xd2fffffffffffffffffffffffe9058d000000000000000000000a0271007,0xd149265d4687dcab1f2046e0947e51ac5e8e7f25916d35539d4df2e9017a,0x489e7783a1f584712bd4f6d48cf2d1ca2c975678936e639083991c5fc369,0x1d871a744f1e02ed15d7d84abd95e80476e6307085f12dba27092ff06d60,0x5c0c8bae9661303107b0077949dee16a7f6dde4982657b9196de23d9f9d0,0xd2fffffffffffffffffffffffe9058d000000000000000000000a0271005,0x01
\ No newline at end of file diff --git a/src/cz/crcs/ectester/data/mnt/mnt5_2.csv b/src/cz/crcs/ectester/data/mnt/mnt5_2.csv new file mode 100644 index 0000000..18ec3a5 --- /dev/null +++ b/src/cz/crcs/ectester/data/mnt/mnt5_2.csv @@ -0,0 +1 @@ +0xd2fffffffffffffffffffffffe9058d000000000000000000000a0271007,0x26caaced434c5a4c2c9c1b09e0ddc167548a95516e7c81b20702485c9809,0x6031c89e2cdd91881dbd675beac3f3df8db1b8e0f45301215a01baf56ab3,0x16e55a2ef696238a7aaf19e51b6a81e1582f28b4bcb6575ab4e0331e569b,0x38de9844643fc9db3c568ec528983da16a177d56145a1d4bf88a2340d839,0xd2fffffffffffffffffffffffe9058d000000000000000000000a0271005,0x01
\ No newline at end of file diff --git a/src/cz/crcs/ectester/data/mnt/mnt5_3.csv b/src/cz/crcs/ectester/data/mnt/mnt5_3.csv new file mode 100644 index 0000000..73fce1d --- /dev/null +++ b/src/cz/crcs/ectester/data/mnt/mnt5_3.csv @@ -0,0 +1 @@ +0xd2fffffffffffffffffffffffe9058d000000000000000000000a0271007,0x44cfc0f3bc92ec82f818b443b564cf25dee3ebae7902e370f9e80283d3bd,0x2ddfd5f7d30c9daca565cd8278eddf6e9497f27450ac97a0a69aac57e27e,0xb071579c8cc322dc7fdce378e5b539b4b7580823aba3cfdd6637cbfa0bbb,0x15d1b75795732b1e2db1efa55cdbb19357e0aa0422cc03b442809339cf02,0xd2fffffffffffffffffffffffe9058d000000000000000000000a0271005,0x01
\ No newline at end of file diff --git a/src/cz/crcs/ectester/data/other/keys.xml b/src/cz/crcs/ectester/data/other/keys.xml deleted file mode 100644 index d06de81..0000000 --- a/src/cz/crcs/ectester/data/other/keys.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version="1.0" encoding="utf-8" ?> -<keys xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../schema.xsd"> - <privkey> - <id>openssl-bug/skey</id> - <inline>0x4543A4D2C9DDD5516FD16D5498034C76D4EAB340276B6BAD8FF57756F4ECA8E6</inline> - <curve>secg/secp256r1</curve> - </privkey> - <pubkey> - <id>openssl-bug/pkey</id> - <inline>0x296D416994A4801B9A48E8C67C98E0C05DE1C0E85D4DC676F32FEACDC4998F0E,0xA91F9BE06C1D50EEB0295A35CA0F130F17EA647147626318E28AEC97F0653749</inline> - <curve>secg/secp256r1</curve> - </pubkey> -</keys>
\ No newline at end of file diff --git a/src/cz/crcs/ectester/data/wrong/results.xml b/src/cz/crcs/ectester/data/wrong/results.xml index 92f43df..e3f0967 100644 --- a/src/cz/crcs/ectester/data/wrong/results.xml +++ b/src/cz/crcs/ectester/data/wrong/results.xml @@ -121,6 +121,42 @@ <desc>Well-formed invalid signature with s = 2 * p.</desc> </sigResult> <sigResult> + <id>nok/length_overflow16</id> + <sig>SHA1</sig> + <inline>0x3083ff000002203988322ab9f52c7f11d5d1aa92a2ac0b00275bcad8e934682257323fda672482022052231597382268e8f3b82b99e386ebb7c7db1a8b4a8bdacd496190314e4c5bad</inline> + <curve>secg/secp256r1</curve> + <signkey>wrong/default_priv</signkey> + <verifykey>wrong/default_pub</verifykey> + <desc>Invalid signature, that is shorter than specified in its ASN.1 SEQUENCE length header and its length overflows 16bits.</desc> + </sigResult> + <sigResult> + <id>nok/length_overflow32</id> + <sig>SHA1</sig> + <inline>0x3085ff0000000002203988322ab9f52c7f11d5d1aa92a2ac0b00275bcad8e934682257323fda672482022052231597382268e8f3b82b99e386ebb7c7db1a8b4a8bdacd496190314e4c5bad</inline> + <curve>secg/secp256r1</curve> + <signkey>wrong/default_priv</signkey> + <verifykey>wrong/default_pub</verifykey> + <desc>Invalid signature, that is shorter than specified in its ASN.1 SEQUENCE length header and its length overflows 32bits.</desc> + </sigResult> + <sigResult> + <id>nok/length_overflow64</id> + <sig>SHA1</sig> + <inline>0x3089ff000000000000000002203988322ab9f52c7f11d5d1aa92a2ac0b00275bcad8e934682257323fda672482022052231597382268e8f3b82b99e386ebb7c7db1a8b4a8bdacd496190314e4c5bad</inline> + <curve>secg/secp256r1</curve> + <signkey>wrong/default_priv</signkey> + <verifykey>wrong/default_pub</verifykey> + <desc>Invalid signature, that is shorter than specified in its ASN.1 SEQUENCE length header and its length overflows 64bits.</desc> + </sigResult> + <sigResult> + <id>nok/length_indefinite</id> + <sig>SHA1</sig> + <inline>0x308002203988322ab9f52c7f11d5d1aa92a2ac0b00275bcad8e934682257323fda672482022052231597382268e8f3b82b99e386ebb7c7db1a8b4a8bdacd496190314e4c5bad</inline> + <curve>secg/secp256r1</curve> + <signkey>wrong/default_priv</signkey> + <verifykey>wrong/default_pub</verifykey> + <desc>Invalid signature, with indefinite length.</desc> + </sigResult> + <sigResult> <id>nok/long</id> <sig>SHA1</sig> <inline>0x30420220e641671e6415629dc8398e35ae1362cb647f293a92553b1594d57fff58df302c02206baafface035e3758eea0dd9ef734976c70b6dd06f4d81d33f5e28bfb8730624</inline> diff --git a/src/cz/crcs/ectester/reader/CardMngr.java b/src/cz/crcs/ectester/reader/CardMngr.java index 921a9c8..e6835dd 100644 --- a/src/cz/crcs/ectester/reader/CardMngr.java +++ b/src/cz/crcs/ectester/reader/CardMngr.java @@ -331,7 +331,7 @@ public class CardMngr { if (responseAPDU.getSW1() == (byte) 0x61) { CommandAPDU apduToSend = new CommandAPDU((byte) 0x00, (byte) 0xC0, (byte) 0x00, (byte) 0x00, - responseAPDU.getSW1()); + responseAPDU.getSW2()); responseAPDU = channel.transmit(apduToSend); if (verbose) diff --git a/src/cz/crcs/ectester/reader/ECTesterReader.java b/src/cz/crcs/ectester/reader/ECTesterReader.java index 0c04453..abc7264 100644 --- a/src/cz/crcs/ectester/reader/ECTesterReader.java +++ b/src/cz/crcs/ectester/reader/ECTesterReader.java @@ -37,15 +37,19 @@ import cz.crcs.ectester.reader.output.FileTestWriter; import cz.crcs.ectester.reader.output.ResponseWriter; import cz.crcs.ectester.reader.response.Response; import cz.crcs.ectester.reader.test.*; +import javacard.framework.ISO7816; import javacard.security.KeyPair; import org.apache.commons.cli.*; +import org.bouncycastle.jce.provider.BouncyCastleProvider; import javax.smartcardio.CardException; +import javax.smartcardio.ResponseAPDU; import javax.xml.parsers.ParserConfigurationException; import java.io.*; 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; @@ -141,14 +145,24 @@ public class ECTesterReader { System.err.println(Colors.error("Failed to connect to card.")); System.exit(1); } - cardManager.send(SELECT_ECTESTERAPPLET); + ResponseAPDU selectResp = cardManager.send(SELECT_ECTESTERAPPLET); + if ((short) selectResp.getSW() != ISO7816.SW_NO_ERROR) { + System.err.println(Colors.error("Failed to select ECTester applet, is it installed?")); + cardManager.disconnectFromCard(); + System.exit(1); + } } - // Setup logger, testWriter and respWriter + // Setup logger and respWriter logger = new OutputLogger(true, cfg.log); - respWriter = new ResponseWriter(logger.getPrintStream()); + // Try adding the BouncyCastleProvider, which might be used in some parts of ECTester. + try { + Security.addProvider(new BouncyCastleProvider()); + } catch (SecurityException | NoClassDefFoundError ignored) { + } + //do action if (cli.hasOption("export")) { export(); @@ -160,6 +174,8 @@ public class ECTesterReader { ecdh(); } else if (cli.hasOption("ecdsa")) { ecdsa(); + } else if (cli.hasOption("info")) { + info(); } //disconnect @@ -232,6 +248,8 @@ public class ECTesterReader { * -dh / --ecdh [count]] * -dsa / --ecdsa [count] * -ln / --list-named [obj] + * -ls / --list-suites + * -nfo / --info * * Options: * -b / --bit-size <b> // -a / --all @@ -272,12 +290,13 @@ public class ECTesterReader { actions.addOption(Option.builder("V").longOpt("version").desc("Print version info.").build()); actions.addOption(Option.builder("h").longOpt("help").desc("Print help.").build()); actions.addOption(Option.builder("ln").longOpt("list-named").desc("Print the list of supported named curves and keys.").hasArg().argName("what").optionalArg(true).build()); + actions.addOption(Option.builder("ls").longOpt("list-suites").desc("List supported test suites.").build()); actions.addOption(Option.builder("e").longOpt("export").desc("Export the defaut curve parameters of the card(if any).").build()); actions.addOption(Option.builder("g").longOpt("generate").desc("Generate <amount> of EC keys.").hasArg().argName("amount").optionalArg(true).build()); actions.addOption(Option.builder("t").longOpt("test").desc("Test ECC support. Optionally specify a test number to run only a part of a test suite. <test_suite>:\n- default:\n- compression:\n- invalid:\n- twist:\n- degenerate:\n- cofactor:\n- wrong:\n- signature:\n- composite:\n- test-vectors:\n- edge-cases:\n- miscellaneous:").hasArg().argName("test_suite[:from[:to]]").optionalArg(true).build()); actions.addOption(Option.builder("dh").longOpt("ecdh").desc("Do EC KeyAgreement (ECDH...), [count] times.").hasArg().argName("count").optionalArg(true).build()); actions.addOption(Option.builder("dsa").longOpt("ecdsa").desc("Sign data with ECDSA, [count] times.").hasArg().argName("count").optionalArg(true).build()); - actions.addOption(Option.builder("ls").longOpt("list-suites").desc("List supported test suites.").build()); + actions.addOption(Option.builder("nf").longOpt("info").desc("Get applet info.").build()); opts.addOptionGroup(actions); @@ -347,6 +366,15 @@ public class ECTesterReader { } } + private void info() throws CardException { + Response.GetInfo info = new Command.GetInfo(cardManager).send(); + System.out.println(String.format("ECTester applet version: %s", info.getVersion())); + System.out.println(String.format("ECTester applet APDU support: %s", (info.getBase() == ECTesterApplet.BASE_221) ? "basic" : "extended length")); + System.out.println(String.format("JavaCard API version: %.1f", info.getJavaCardVersion())); + System.out.println(String.format("JavaCard supports system cleanup: %s", info.getCleanupSupport())); + System.out.println(String.format("Array sizes (apduBuf, ram, ram2, apduArr): %d %d %d %d", info.getApduBufferLength(), info.getRamArrayLength(), info.getRamArray2Length(), info.getApduArrayLength())); + } + /** * Exports default card/simulation EC domain parameters to output file. * @@ -405,13 +433,13 @@ public class ECTesterReader { */ private void generate() throws CardException, IOException { byte keyClass = cfg.primeField ? KeyPair.ALG_EC_FP : KeyPair.ALG_EC_F2M; + Command curve = Command.prepareCurve(cardManager, EC_Store.getInstance(), cfg, ECTesterApplet.KEYPAIR_LOCAL, cfg.bits, keyClass); Response allocate = new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_LOCAL, cfg.bits, keyClass).send(); respWriter.outputResponse(allocate); - Command curve = Command.prepareCurve(cardManager, EC_Store.getInstance(), cfg, ECTesterApplet.KEYPAIR_LOCAL, cfg.bits, keyClass); OutputStreamWriter keysFile = FileUtil.openFiles(cfg.outputs); - keysFile.write("index;time;pubW;privS\n"); + keysFile.write("index;genTime;exportTime;pubW;privS\n"); int generated = 0; int retry = 0; @@ -423,10 +451,10 @@ public class ECTesterReader { Command.Generate generate = new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_LOCAL); Response.Generate response = generate.send(); - long elapsed = response.getDuration(); respWriter.outputResponse(response); Response.Export export = new Command.Export(cardManager, ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.KEY_BOTH, EC_Consts.PARAMETERS_KEYPAIR).send(); + respWriter.outputResponse(export); if (!response.successful() || !export.successful()) { if (retry < 10) { @@ -440,7 +468,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;%s;%s\n", generated, elapsed / 1000000, pub, priv); + String line = String.format("%d;%d;%d;%s;%s\n", generated, response.getDuration() / 1000000, export.getDuration() / 1000000, pub, priv); keysFile.write(line); keysFile.flush(); generated++; @@ -533,10 +561,10 @@ public class ECTesterReader { */ private void ecdh() throws IOException, CardException { byte keyClass = cfg.primeField ? KeyPair.ALG_EC_FP : KeyPair.ALG_EC_F2M; + Command curve = Command.prepareCurve(cardManager, EC_Store.getInstance(), cfg, ECTesterApplet.KEYPAIR_BOTH, cfg.bits, keyClass); List<Response> prepare = new LinkedList<>(); prepare.add(new Command.AllocateKeyAgreement(cardManager, cfg.ECKAType).send()); // Prepare KeyAgreement or required type prepare.add(new Command.Allocate(cardManager, ECTesterApplet.KEYPAIR_BOTH, cfg.bits, keyClass).send()); - Command curve = Command.prepareCurve(cardManager, EC_Store.getInstance(), cfg, ECTesterApplet.KEYPAIR_BOTH, cfg.bits, keyClass); if (curve != null) prepare.add(curve.send()); diff --git a/src/cz/crcs/ectester/reader/command/Command.java b/src/cz/crcs/ectester/reader/command/Command.java index 5a4af21..a3560df 100644 --- a/src/cz/crcs/ectester/reader/command/Command.java +++ b/src/cz/crcs/ectester/reader/command/Command.java @@ -133,6 +133,9 @@ public abstract class Command implements Cloneable { } else { keypair = dataStore.getObject(EC_Keypair.class, cfg.namedKey); } + if (keypair == null) { + throw new IOException("KeyPair not found."); + } data = keypair.flatten(); if (data == null) { @@ -155,6 +158,9 @@ public abstract class Command implements Cloneable { pub = dataStore.getObject(EC_Keypair.class, cfg.namedPublicKey); } } + if (pub == null) { + throw new IOException("Public key not found."); + } byte[] pubkey = pub.flatten(EC_Consts.PARAMETER_W); if (pubkey == null) { @@ -177,6 +183,9 @@ public abstract class Command implements Cloneable { priv = dataStore.getObject(EC_Keypair.class, cfg.namedPrivateKey); } } + if (priv == null) { + throw new IOException("Private key not found."); + } byte[] privkey = priv.flatten(EC_Consts.PARAMETER_S); if (privkey == null) { @@ -383,18 +392,7 @@ public abstract class Command implements Cloneable { @Override public String getDescription() { - String name; - switch (curve) { - case EC_Consts.CURVE_default: - name = "default"; - break; - case EC_Consts.CURVE_external: - name = "external"; - break; - default: - name = "custom"; - break; - } + String name = CardUtil.getCurveName(curve); String what = CardUtil.getParameterString(params); String pair; @@ -864,5 +862,33 @@ public abstract class Command implements Cloneable { return "Request JCSystem object deletion"; } } + + /** + * + */ + public static class GetInfo extends Command { + + /** + * @param cardManager cardManager to send APDU through + */ + public GetInfo(CardMngr cardManager) { + super(cardManager); + + this.cmd = new CommandAPDU(ECTesterApplet.CLA_ECTESTERAPPLET, ECTesterApplet.INS_GET_INFO, 0, 0); + } + + @Override + public Response.GetInfo send() throws CardException { + long elapsed = -System.nanoTime(); + ResponseAPDU response = cardManager.send(cmd); + elapsed += System.nanoTime(); + return new Response.GetInfo(response, getDescription(), elapsed); + } + + @Override + public String getDescription() { + return "Get applet info"; + } + } } diff --git a/src/cz/crcs/ectester/reader/output/TextTestWriter.java b/src/cz/crcs/ectester/reader/output/TextTestWriter.java index ad35012..e89d403 100644 --- a/src/cz/crcs/ectester/reader/output/TextTestWriter.java +++ b/src/cz/crcs/ectester/reader/output/TextTestWriter.java @@ -1,5 +1,6 @@ package cz.crcs.ectester.reader.output; +import cz.crcs.ectester.applet.ECTesterApplet; import cz.crcs.ectester.common.cli.Colors; import cz.crcs.ectester.common.output.BaseTextTestWriter; import cz.crcs.ectester.common.test.TestSuite; @@ -7,6 +8,7 @@ import cz.crcs.ectester.common.test.Testable; import cz.crcs.ectester.common.util.ByteUtil; 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 cz.crcs.ectester.reader.test.CardTestSuite; import cz.crcs.ectester.reader.test.CommandTestable; @@ -43,9 +45,13 @@ public class TextTestWriter extends BaseTextTestWriter { if (suite instanceof CardTestSuite) { CardTestSuite cardSuite = (CardTestSuite) suite; StringBuilder sb = new StringBuilder(); - sb.append("āāā ").append(Colors.underline("ECTester version:")).append(" ").append(ECTesterReader.VERSION).append(ECTesterReader.GIT_COMMIT).append(System.lineSeparator()); - sb.append("āāā ").append(Colors.underline("Card ATR:")).append(" ").append(ByteUtil.bytesToHex(cardSuite.getCard().getATR().getBytes(), false)).append(System.lineSeparator()); try { + sb.append("āāā ").append(Colors.underline("ECTester version:")).append(" ").append(ECTesterReader.VERSION).append(ECTesterReader.GIT_COMMIT).append(System.lineSeparator()); + Response.GetInfo info = new Command.GetInfo(cardSuite.getCard()).send(); + sb.append("āāā ").append(Colors.underline("ECTester applet version:")).append(" ").append(info.getVersion()).append(info.getBase() == ECTesterApplet.BASE_221 ? "" : " (extended length)").append(System.lineSeparator()); + 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()); 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 00cc6c6..9add072 100644 --- a/src/cz/crcs/ectester/reader/output/XMLTestWriter.java +++ b/src/cz/crcs/ectester/reader/output/XMLTestWriter.java @@ -108,6 +108,37 @@ public class XMLTestWriter extends BaseXMLTestWriter { return result; } + private Element appletElement(CardMngr card) { + Element result = doc.createElement("applet"); + try { + 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("cleanup", String.valueOf(info.getCleanupSupport())); + Element arrays = doc.createElement("arrays"); + Element apduBuf = doc.createElement("length"); + apduBuf.setAttribute("name", "apduBuf"); + apduBuf.setTextContent(String.valueOf(info.getApduBufferLength())); + Element ramArray = doc.createElement("length"); + ramArray.setAttribute("name", "ramArray"); + ramArray.setTextContent(String.valueOf(info.getRamArrayLength())); + Element ramArray2 = doc.createElement("length"); + ramArray2.setAttribute("name", "ramArray2"); + ramArray2.setTextContent(String.valueOf(info.getRamArray2Length())); + Element apduArray = doc.createElement("length"); + apduArray.setAttribute("name", "apduArray"); + apduArray.setTextContent(String.valueOf(info.getApduArrayLength())); + arrays.appendChild(apduBuf); + arrays.appendChild(ramArray); + arrays.appendChild(ramArray2); + arrays.appendChild(apduArray); + result.appendChild(arrays); + } catch (CardException ignored) { + } + return result; + } + @Override protected Element deviceElement(TestSuite suite) { if (suite instanceof CardTestSuite) { @@ -116,6 +147,7 @@ public class XMLTestWriter extends BaseXMLTestWriter { result.setAttribute("type", "card"); result.setAttribute("ectester", ECTesterReader.VERSION + ECTesterReader.GIT_COMMIT); result.appendChild(cplcElement(cardSuite.getCard())); + result.appendChild(appletElement(cardSuite.getCard())); Element atr = doc.createElement("ATR"); atr.setTextContent(ByteUtil.bytesToHex(cardSuite.getCard().getATR().getBytes(), false)); diff --git a/src/cz/crcs/ectester/reader/output/YAMLTestWriter.java b/src/cz/crcs/ectester/reader/output/YAMLTestWriter.java index 7c99a4a..56ecb71 100644 --- a/src/cz/crcs/ectester/reader/output/YAMLTestWriter.java +++ b/src/cz/crcs/ectester/reader/output/YAMLTestWriter.java @@ -83,6 +83,25 @@ public class YAMLTestWriter extends BaseYAMLTestWriter { return result; } + private Map<String, Object> appletObject(CardMngr card) { + Map<String, Object> result = new LinkedHashMap<>(); + try { + Response.GetInfo info = new Command.GetInfo(card).send(); + result.put("version", info.getVersion()); + result.put("javacard", info.getJavaCardVersion()); + result.put("base", info.getBase()); + result.put("cleanup", info.getCleanupSupport()); + Map<String, Integer> arrays = new LinkedHashMap<>(); + arrays.put("apduBuf", Short.toUnsignedInt(info.getApduBufferLength())); + arrays.put("ramArray", Short.toUnsignedInt(info.getRamArrayLength())); + arrays.put("ramArray2", Short.toUnsignedInt(info.getRamArray2Length())); + arrays.put("apduArray", Short.toUnsignedInt(info.getApduArrayLength())); + result.put("arrays", arrays); + } catch (CardException ignored) { + } + return result; + } + @Override protected Map<String, Object> deviceObject(TestSuite suite) { if (suite instanceof CardTestSuite) { @@ -91,6 +110,7 @@ public class YAMLTestWriter extends BaseYAMLTestWriter { result.put("type", "card"); result.put("ectester", ECTesterReader.VERSION + ECTesterReader.GIT_COMMIT); result.put("cplc", cplcObject(cardSuite.getCard())); + result.put("applet", appletObject(cardSuite.getCard())); result.put("ATR", ByteUtil.bytesToHex(cardSuite.getCard().getATR().getBytes(), false)); return result; } diff --git a/src/cz/crcs/ectester/reader/response/Response.java b/src/cz/crcs/ectester/reader/response/Response.java index 4814e41..235564e 100644 --- a/src/cz/crcs/ectester/reader/response/Response.java +++ b/src/cz/crcs/ectester/reader/response/Response.java @@ -79,6 +79,10 @@ public abstract class Response { return resp; } + public byte[] getData() { + return resp.getData(); + } + public long getDuration() { return time; } @@ -304,7 +308,7 @@ public abstract class Response { if (pair == keyPair && param == mask) { return index; } - if ((parameters & mask) != 0 && (pair & keyPair) != 0) { + if ((parameters & mask) != 0 && (pair & this.keyPair) != 0) { if (mask == EC_Consts.PARAMETER_W) { if ((key & EC_Consts.KEY_PUBLIC) != 0) index++; @@ -424,4 +428,78 @@ public abstract class Response { parse(1, 0); } } + + /** + * + */ + public static class GetInfo extends Response { + private short base; + private short jcVersion; + private short cleanupSupport; + private short apduBufferLength; + private short ramArrayLength; + private short ramArray2Length; + private short apduArrayLength; + + public GetInfo(ResponseAPDU response, String description, long time) { + super(response, description, time); + + parse(1, 1); + int offset = 2 + 2 + getParamLength(0); + byte[] data = getData(); + base = ByteUtil.getShort(data, offset); + offset += 2; + jcVersion = ByteUtil.getShort(data, offset); + offset += 2; + cleanupSupport = ByteUtil.getShort(data, offset); + offset += 2; + apduBufferLength = ByteUtil.getShort(data, offset); + offset += 2; + ramArrayLength = ByteUtil.getShort(data, offset); + offset += 2; + ramArray2Length = ByteUtil.getShort(data, offset); + offset += 2; + apduArrayLength = ByteUtil.getShort(data, offset); + } + + public String getVersion() { + return new String(getParam(0)); + } + + public short getBase() { + return base; + } + + public float getJavaCardVersion() { + 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)); + } + return (major + ((float) (minor) / (minorSize * 10))); + } + + public boolean getCleanupSupport() { + return cleanupSupport == 1; + } + + public short getApduBufferLength() { + return apduBufferLength; + } + + public short getRamArrayLength() { + return ramArrayLength; + } + + public short getRamArray2Length() { + return ramArray2Length; + } + + public short getApduArrayLength() { + return apduArrayLength; + } + } } diff --git a/src/cz/crcs/ectester/reader/test/CardCofactorSuite.java b/src/cz/crcs/ectester/reader/test/CardCofactorSuite.java index 710b704..172c8af 100644 --- a/src/cz/crcs/ectester/reader/test/CardCofactorSuite.java +++ b/src/cz/crcs/ectester/reader/test/CardCofactorSuite.java @@ -31,8 +31,8 @@ public class CardCofactorSuite extends CardTestSuite { @Override protected void runTests() throws Exception { Map<String, EC_Key.Public> pubkeys = EC_Store.getInstance().getObjects(EC_Key.Public.class, "cofactor"); - List<Map.Entry<EC_Curve, List<EC_Key.Public>>> curveList = EC_Store.mapKeyToCurve(pubkeys.values()); - for (Map.Entry<EC_Curve, List<EC_Key.Public>> e : curveList) { + Map<EC_Curve, List<EC_Key.Public>> curveList = EC_Store.mapKeyToCurve(pubkeys.values()); + for (Map.Entry<EC_Curve, List<EC_Key.Public>> e : curveList.entrySet()) { EC_Curve curve = e.getKey(); List<EC_Key.Public> keys = e.getValue(); @@ -46,7 +46,7 @@ public class CardCofactorSuite extends CardTestSuite { 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 objectEcdh = CompoundTest.all(Result.ExpectedValue.SUCCESS, CardUtil.getKATypeString(EC_Consts.KeyAgreement_ALG_EC_SVDP_DH) + " test with degenerate pubkey.", setPub, ecdh); + 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, ExpectedValue.FAILURE, "Card correctly rejected point on non-generator subgroup.", "Card incorrectly accepted point on non-generator subgroup."); ecdhTests.add(CompoundTest.all(Result.ExpectedValue.SUCCESS, pub.getId() + " cofactor key test.", objectEcdh, rawEcdh)); diff --git a/src/cz/crcs/ectester/reader/test/CardCompositeSuite.java b/src/cz/crcs/ectester/reader/test/CardCompositeSuite.java index 336b371..4bf9290 100644 --- a/src/cz/crcs/ectester/reader/test/CardCompositeSuite.java +++ b/src/cz/crcs/ectester/reader/test/CardCompositeSuite.java @@ -37,8 +37,8 @@ public class CardCompositeSuite extends CardTestSuite { * is revealed. */ Map<String, EC_Key> keys = EC_Store.getInstance().getObjects(EC_Key.class, "composite"); - List<Map.Entry<EC_Curve, List<EC_Key>>> mappedKeys = EC_Store.mapKeyToCurve(keys.values()); - for (Map.Entry<EC_Curve, List<EC_Key>> curveKeys : mappedKeys) { + Map<EC_Curve, List<EC_Key>> mappedKeys = EC_Store.mapKeyToCurve(keys.values()); + for (Map.Entry<EC_Curve, List<EC_Key>> curveKeys : mappedKeys.entrySet()) { EC_Curve curve = curveKeys.getKey(); List<Test> tests = new LinkedList<>(); Test allocate = runTest(CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_LOCAL, curve.getBits(), curve.getField()), ExpectedValue.SUCCESS)); @@ -59,35 +59,35 @@ public class CardCompositeSuite extends CardTestSuite { Map<String, EC_Curve> results = EC_Store.getInstance().getObjects(EC_Curve.class, "composite"); - List<Map.Entry<String, List<EC_Curve>>> groupList = EC_Store.mapToPrefix(results.values()); + Map<String, List<EC_Curve>> groups = EC_Store.mapToPrefix(results.values()); /* Test the whole curves with both keypairs generated on card(no small-order public points provided). */ - List<EC_Curve> wholeCurves = groupList.stream().filter((e) -> e.getKey().equals("whole")).findFirst().get().getValue(); + List<EC_Curve> wholeCurves = groups.entrySet().stream().filter((e) -> e.getKey().equals("whole")).findFirst().get().getValue(); testGroup(wholeCurves, "Composite generator order", ExpectedValue.FAILURE, "Card rejected to do ECDH with composite order generator.", "Card did not reject to do ECDH with composite order generator."); /* Also test having a G of small order, so small R. */ - List<EC_Curve> smallRCurves = groupList.stream().filter((e) -> e.getKey().equals("small")).findFirst().get().getValue(); + List<EC_Curve> smallRCurves = groups.entrySet().stream().filter((e) -> e.getKey().equals("small")).findFirst().get().getValue(); testGroup(smallRCurves, "Small generator order", ExpectedValue.FAILURE, "Card correctly rejected to do ECDH over a small order generator.", "Card incorrectly does ECDH over a small order generator."); /* Test increasingly larger prime R, to determine where/if card behavior changes. */ - List<EC_Curve> varyingCurves = groupList.stream().filter((e) -> e.getKey().equals("varying")).findFirst().get().getValue(); + List<EC_Curve> varyingCurves = groups.entrySet().stream().filter((e) -> e.getKey().equals("varying")).findFirst().get().getValue(); testGroup(varyingCurves, null, ExpectedValue.ANY, "", ""); /* Also test having a G of large but composite order, R = p * q, */ - List<EC_Curve> pqCurves = groupList.stream().filter((e) -> e.getKey().equals("pq")).findFirst().get().getValue(); + List<EC_Curve> pqCurves = groups.entrySet().stream().filter((e) -> e.getKey().equals("pq")).findFirst().get().getValue(); testGroup(pqCurves, null, ExpectedValue.ANY, "", ""); /* Also test having G or large order being a Carmichael pseudoprime, R = p * q * r, */ - List<EC_Curve> ppCurves = groupList.stream().filter((e) -> e.getKey().equals("pp")).findFirst().get().getValue(); + List<EC_Curve> ppCurves = groups.entrySet().stream().filter((e) -> e.getKey().equals("pp")).findFirst().get().getValue(); testGroup(ppCurves, "Generator order = Carmichael pseudoprime", ExpectedValue.ANY, "", ""); /* Also test rg0 curves. */ - List<EC_Curve> rg0Curves = groupList.stream().filter((e) -> e.getKey().equals("rg0")).findFirst().get().getValue(); + List<EC_Curve> rg0Curves = groups.entrySet().stream().filter((e) -> e.getKey().equals("rg0")).findFirst().get().getValue(); testGroup(rg0Curves, null, ExpectedValue.ANY, "", ""); } diff --git a/src/cz/crcs/ectester/reader/test/CardCompressionSuite.java b/src/cz/crcs/ectester/reader/test/CardCompressionSuite.java index ae25bf1..291cc04 100644 --- a/src/cz/crcs/ectester/reader/test/CardCompressionSuite.java +++ b/src/cz/crcs/ectester/reader/test/CardCompressionSuite.java @@ -2,6 +2,8 @@ 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_Key; import cz.crcs.ectester.common.output.TestWriter; import cz.crcs.ectester.common.test.CompoundTest; import cz.crcs.ectester.common.test.Result; @@ -9,6 +11,7 @@ import cz.crcs.ectester.common.test.Test; 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.data.EC_Store; import cz.crcs.ectester.reader.CardMngr; import cz.crcs.ectester.reader.ECTesterReader; import cz.crcs.ectester.reader.command.Command; @@ -18,6 +21,7 @@ import javacard.security.KeyPair; import java.security.spec.ECPoint; import java.util.LinkedList; import java.util.List; +import java.util.Map; /** * @author Jan Jancar johny@neuromancer.sk @@ -51,6 +55,10 @@ public class CardCompressionSuite extends CardTestSuite { if (cfg.binaryField) { runCompression(KeyPair.ALG_EC_F2M); } + + // Now, do ECDH over SECG curves and give the implementation a compressed key that is not a quadratic residue in + // decompression. + runNonResidue(); } private void runCompression(byte field) throws Exception { @@ -59,27 +67,28 @@ public class CardCompressionSuite extends CardTestSuite { for (short keyLength : keySizes) { String spec = keyLength + "b " + CardUtil.getKeyTypeString(field); + byte curveId = EC_Consts.getCurve(keyLength, field); Test allocateFirst = runTest(CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, keyLength, field), Result.ExpectedValue.SUCCESS)); if (!allocateFirst.ok()) { - doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, "No support for " + spec + ".", allocateFirst)); + doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, "No support for compression test on " + spec + ".", allocateFirst)); continue; } List<Test> compressionTests = new LinkedList<>(); compressionTests.add(allocateFirst); - Test setCustom = runTest(CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.getCurve(keyLength, field), domain, null), Result.ExpectedValue.SUCCESS)); + Test setCustom = runTest(CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, curveId, domain, null), Result.ExpectedValue.SUCCESS)); Test genCustom = runTest(CommandTest.expect(new Command.Generate(this.card, ECTesterApplet.KEYPAIR_BOTH), Result.ExpectedValue.SUCCESS)); compressionTests.add(setCustom); compressionTests.add(genCustom); Response.Export key = new Command.Export(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.KEY_PUBLIC, EC_Consts.PARAMETER_W).send(); byte[] pubkey = key.getParameter(ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.KEY_PUBLIC); + EC_Curve secgCurve = EC_Store.getInstance().getObject(EC_Curve.class, "secg", CardUtil.getCurveName(curveId)); ECPoint pub; try { - pub = ECUtil.fromX962(pubkey, null); + pub = ECUtil.fromX962(pubkey, secgCurve.toCurve()); } catch (IllegalArgumentException iae) { - // TODO: use external SECG curves so we have them here. doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, "", compressionTests.toArray(new Test[0]))); continue; } @@ -119,4 +128,26 @@ public class CardCompressionSuite extends CardTestSuite { doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, "Compression test of " + spec + ".", compressionTests.toArray(new Test[0]))); } } + + private void runNonResidue() { + Map<String, EC_Key.Public> otherKeys = EC_Store.getInstance().getObjects(EC_Key.Public.class, "misc"); + List<EC_Key.Public> compressionKeys = EC_Store.mapToPrefix(otherKeys.values()).get("compression"); + + for (EC_Key.Public key : compressionKeys) { + EC_Curve curve = EC_Store.getInstance().getObject(EC_Curve.class, key.getCurve()); + List<Test> tests = new LinkedList<>(); + Test allocate = runTest(CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_LOCAL, curve.getBits(), curve.getField()), Result.ExpectedValue.SUCCESS)); + if (!allocate.ok()) { + doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, "No support for non-residue test on " + curve.getBits() + "b " + curve.getId() + ".", allocate)); + continue; + } + tests.add(allocate); + tests.add(CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), Result.ExpectedValue.SUCCESS)); + tests.add(CommandTest.expect(new Command.Generate(this.card, ECTesterApplet.KEYPAIR_LOCAL), Result.ExpectedValue.SUCCESS)); + byte[] pointData = ECUtil.toX962Compressed(key.getParam(EC_Consts.PARAMETER_W)); + byte[] pointDataEncoded = ByteUtil.prependLength(pointData); + tests.add(CommandTest.expect(new Command.ECDH_direct(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, EC_Consts.TRANSFORMATION_NONE, EC_Consts.KeyAgreement_ALG_EC_SVDP_DH, pointDataEncoded), Result.ExpectedValue.FAILURE)); + doTest(CompoundTest.greedyAll(Result.ExpectedValue.SUCCESS, "Non-residue test of " + curve.getId() + ".", tests.toArray(new Test[0]))); + } + } } diff --git a/src/cz/crcs/ectester/reader/test/CardDefaultSuite.java b/src/cz/crcs/ectester/reader/test/CardDefaultSuite.java index e495b00..91f9ef6 100644 --- a/src/cz/crcs/ectester/reader/test/CardDefaultSuite.java +++ b/src/cz/crcs/ectester/reader/test/CardDefaultSuite.java @@ -10,6 +10,7 @@ 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; diff --git a/src/cz/crcs/ectester/reader/test/CardDegenerateSuite.java b/src/cz/crcs/ectester/reader/test/CardDegenerateSuite.java index c926a4d..f434d4d 100644 --- a/src/cz/crcs/ectester/reader/test/CardDegenerateSuite.java +++ b/src/cz/crcs/ectester/reader/test/CardDegenerateSuite.java @@ -31,8 +31,8 @@ public class CardDegenerateSuite extends CardTestSuite { @Override protected void runTests() throws Exception { Map<String, EC_Key.Public> pubkeys = EC_Store.getInstance().getObjects(EC_Key.Public.class, "degenerate"); - List<Map.Entry<EC_Curve, List<EC_Key.Public>>> curveList = EC_Store.mapKeyToCurve(pubkeys.values()); - for (Map.Entry<EC_Curve, List<EC_Key.Public>> e : curveList) { + Map<EC_Curve, List<EC_Key.Public>> curveList = EC_Store.mapKeyToCurve(pubkeys.values()); + for (Map.Entry<EC_Curve, List<EC_Key.Public>> e : curveList.entrySet()) { EC_Curve curve = e.getKey(); List<EC_Key.Public> keys = e.getValue(); diff --git a/src/cz/crcs/ectester/reader/test/CardEdgeCasesSuite.java b/src/cz/crcs/ectester/reader/test/CardEdgeCasesSuite.java index b68b2ec..ccec401 100644 --- a/src/cz/crcs/ectester/reader/test/CardEdgeCasesSuite.java +++ b/src/cz/crcs/ectester/reader/test/CardEdgeCasesSuite.java @@ -39,8 +39,8 @@ public class CardEdgeCasesSuite extends CardTestSuite { @Override protected void runTests() throws Exception { Map<String, EC_KAResult> results = EC_Store.getInstance().getObjects(EC_KAResult.class, "wycheproof"); - List<Map.Entry<String, List<EC_KAResult>>> groupList = EC_Store.mapToPrefix(results.values()); - for (Map.Entry<String, List<EC_KAResult>> e : groupList) { + Map<String, List<EC_KAResult>> groups = EC_Store.mapToPrefix(results.values()); + for (Map.Entry<String, List<EC_KAResult>> e : groups.entrySet()) { String description = null; switch (e.getKey()) { case "addsub": @@ -55,8 +55,8 @@ public class CardEdgeCasesSuite extends CardTestSuite { } List<Test> groupTests = new LinkedList<>(); - List<Map.Entry<EC_Curve, List<EC_KAResult>>> curveList = EC_Store.mapResultToCurve(e.getValue()); - for (Map.Entry<EC_Curve, List<EC_KAResult>> c : curveList) { + Map<EC_Curve, List<EC_KAResult>> curveList = EC_Store.mapResultToCurve(e.getValue()); + for (Map.Entry<EC_Curve, List<EC_KAResult>> c : curveList.entrySet()) { EC_Curve curve = c.getKey(); List<Test> curveTests = new LinkedList<>(); @@ -111,7 +111,7 @@ public class CardEdgeCasesSuite extends CardTestSuite { } { - EC_KAResult openssl_bug = EC_Store.getInstance().getObject(EC_KAResult.class, "other", "openssl-bug"); + EC_KAResult openssl_bug = EC_Store.getInstance().getObject(EC_KAResult.class, "misc", "openssl-bug"); EC_Curve curve = EC_Store.getInstance().getObject(EC_Curve.class, openssl_bug.getCurve()); EC_Key.Private skey = EC_Store.getInstance().getObject(EC_Key.Private.class, openssl_bug.getOtherKey()); EC_Key.Public pkey = EC_Store.getInstance().getObject(EC_Key.Public.class, openssl_bug.getOneKey()); @@ -145,7 +145,7 @@ public class CardEdgeCasesSuite extends CardTestSuite { for (EC_Curve curve : curves) { Test key = runTest(CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), KeyPair.ALG_EC_FP), Result.ExpectedValue.SUCCESS)); if (!key.ok()) { - doTest(CompoundTest.all(Result.ExpectedValue.FAILURE, "No support for " + curve.getBits() + "b ALG_EC_FP.", key)); + doTest(CompoundTest.all(Result.ExpectedValue.FAILURE, "No support for " + curve.getBits() + "b " + curve.getId() + ".", key)); continue; } Test set = CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), Result.ExpectedValue.SUCCESS); @@ -238,7 +238,7 @@ public class CardEdgeCasesSuite extends CardTestSuite { Test key = runTest(CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, secp160r1.getBits(), KeyPair.ALG_EC_FP), Result.ExpectedValue.SUCCESS)); if (!key.ok()) { - doTest(CompoundTest.all(Result.ExpectedValue.FAILURE, "No support for " + secp160r1.getBits() + "b ALG_EC_FP.", key)); + doTest(CompoundTest.all(Result.ExpectedValue.FAILURE, "No support for " + secp160r1.getBits() + "b secp160r1.", key)); return; } Test set = CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, secp160r1.getParams(), secp160r1.flatten()), Result.ExpectedValue.SUCCESS); diff --git a/src/cz/crcs/ectester/reader/test/CardInvalidSuite.java b/src/cz/crcs/ectester/reader/test/CardInvalidSuite.java index 17c5d4b..3b9e0e5 100644 --- a/src/cz/crcs/ectester/reader/test/CardInvalidSuite.java +++ b/src/cz/crcs/ectester/reader/test/CardInvalidSuite.java @@ -35,8 +35,8 @@ public class CardInvalidSuite extends CardTestSuite { * Try ECDH with invalid public keys of increasing order. */ Map<String, EC_Key.Public> pubkeys = EC_Store.getInstance().getObjects(EC_Key.Public.class, "invalid"); - List<Map.Entry<EC_Curve, List<EC_Key.Public>>> curveList = EC_Store.mapKeyToCurve(pubkeys.values()); - for (Map.Entry<EC_Curve, List<EC_Key.Public>> e : curveList) { + Map<EC_Curve, List<EC_Key.Public>> curveList = EC_Store.mapKeyToCurve(pubkeys.values()); + for (Map.Entry<EC_Curve, List<EC_Key.Public>> e : curveList.entrySet()) { EC_Curve curve = e.getKey(); List<EC_Key.Public> keys = e.getValue(); diff --git a/src/cz/crcs/ectester/reader/test/CardMiscSuite.java b/src/cz/crcs/ectester/reader/test/CardMiscSuite.java index 8623e36..a2ce2ce 100644 --- a/src/cz/crcs/ectester/reader/test/CardMiscSuite.java +++ b/src/cz/crcs/ectester/reader/test/CardMiscSuite.java @@ -7,12 +7,14 @@ import cz.crcs.ectester.common.output.TestWriter; import cz.crcs.ectester.common.test.CompoundTest; import cz.crcs.ectester.common.test.Result; import cz.crcs.ectester.common.test.Test; -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.command.Command; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; import java.util.Map; /** @@ -21,7 +23,9 @@ import java.util.Map; public class CardMiscSuite extends CardTestSuite { public CardMiscSuite(TestWriter writer, ECTesterReader.Config cfg, CardMngr cardManager) { - super(writer, cfg, cardManager, "miscellaneous", "Some miscellaneous tests, tries ECDH and ECDSA over supersingular curves, anomalous curves and some Barreto-Naehrig curves with small embedding degree and CM discriminant."); + super(writer, cfg, cardManager, "miscellaneous", "Some miscellaneous tests, tries ECDH and ECDSA over supersingular curves, anomalous curves,", + "Barreto-Naehrig curves with small embedding degree and CM discriminant, MNT curves,", + "some Montgomery curves transformed to short Weierstrass form and Curve25519 transformed to short Weierstrass form."); } @Override @@ -29,32 +33,45 @@ public class CardMiscSuite extends CardTestSuite { Map<String, EC_Curve> anCurves = EC_Store.getInstance().getObjects(EC_Curve.class, "anomalous"); Map<String, EC_Curve> ssCurves = EC_Store.getInstance().getObjects(EC_Curve.class, "supersingular"); Map<String, EC_Curve> bnCurves = EC_Store.getInstance().getObjects(EC_Curve.class, "Barreto-Naehrig"); + Map<String, EC_Curve> mntCurves = EC_Store.getInstance().getObjects(EC_Curve.class, "MNT"); + List<EC_Curve> mCurves = new ArrayList<>(); + mCurves.add(EC_Store.getInstance().getObject(EC_Curve.class, "other", "M-221")); + mCurves.add(EC_Store.getInstance().getObject(EC_Curve.class, "other", "M-383")); + mCurves.add(EC_Store.getInstance().getObject(EC_Curve.class, "other", "M-511")); + EC_Curve curve25519 = EC_Store.getInstance().getObject(EC_Curve.class, "other", "Curve25519"); - testCurves(anCurves, "anomalous", Result.ExpectedValue.FAILURE); - testCurves(ssCurves, "supersingular", Result.ExpectedValue.FAILURE); - testCurves(bnCurves, "Barreto-Naehrig", Result.ExpectedValue.ANY); + testCurves(anCurves.values(), "anomalous", Result.ExpectedValue.FAILURE); + testCurves(ssCurves.values(), "supersingular", Result.ExpectedValue.FAILURE); + testCurves(bnCurves.values(), "Barreto-Naehrig", Result.ExpectedValue.SUCCESS); + testCurves(mntCurves.values(), "MNT", Result.ExpectedValue.SUCCESS); + testCurves(mCurves, "Montgomery", Result.ExpectedValue.SUCCESS); + testCurve(curve25519, "Montgomery", Result.ExpectedValue.SUCCESS); } - private void testCurves(Map<String, EC_Curve> curves, String catName, Result.ExpectedValue expected) throws Exception { - for (EC_Curve curve : curves.values()) { - Test allocateFirst = runTest(CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), Result.ExpectedValue.SUCCESS)); - if (!allocateFirst.ok()) { - doTest(CompoundTest.all(Result.ExpectedValue.FAILURE, "No support for " + curve.getBits() + "b " + CardUtil.getKeyTypeString(curve.getField()) + ".", allocateFirst)); - continue; - } + private void testCurve(EC_Curve curve, String catName, Result.ExpectedValue expected) { + Test allocateFirst = runTest(CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), Result.ExpectedValue.SUCCESS)); + if (!allocateFirst.ok()) { + doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, "No support for " + curve.getBits() + "b " + catName + " curve: " + curve.getId() + ".", allocateFirst)); + return; + } + + 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 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); - 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 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); + if (cfg.cleanup) { + Test cleanup = CommandTest.expect(new Command.Cleanup(this.card), Result.ExpectedValue.ANY); + doTest(CompoundTest.greedyAll(Result.ExpectedValue.SUCCESS, "Tests over " + curve.getBits() + "b " + catName + " curve: " + curve.getId() + ".", allocateFirst, set, generate, perform, cleanup)); + } else { + doTest(CompoundTest.greedyAll(Result.ExpectedValue.SUCCESS, "Tests over " + curve.getBits() + "b " + catName + " curve: " + curve.getId() + ".", allocateFirst, set, generate, perform)); + } + } - if (cfg.cleanup) { - Test cleanup = CommandTest.expect(new Command.Cleanup(this.card), Result.ExpectedValue.ANY); - doTest(CompoundTest.greedyAll(Result.ExpectedValue.SUCCESS, "Tests over " + curve.getBits() + " " + catName + " curve: " + curve.getId() + ".", allocateFirst, set, generate, perform, cleanup)); - } else { - doTest(CompoundTest.greedyAll(Result.ExpectedValue.SUCCESS, "Tests over " + curve.getBits() + " " + catName + " curve: " + curve.getId() + ".", allocateFirst, set, generate, perform)); - } + private void testCurves(Collection<EC_Curve> curves, String catName, Result.ExpectedValue expected) { + for (EC_Curve curve : curves) { + testCurve(curve, catName, expected); } } } diff --git a/src/cz/crcs/ectester/reader/test/CardSignatureSuite.java b/src/cz/crcs/ectester/reader/test/CardSignatureSuite.java index 59def74..20546c8 100644 --- a/src/cz/crcs/ectester/reader/test/CardSignatureSuite.java +++ b/src/cz/crcs/ectester/reader/test/CardSignatureSuite.java @@ -28,16 +28,16 @@ public class CardSignatureSuite extends CardTestSuite { @Override protected void runTests() throws Exception { Map<String, EC_SigResult> results = EC_Store.getInstance().getObjects(EC_SigResult.class, "wrong"); - List<Map.Entry<String, List<EC_SigResult>>> groupList = EC_Store.mapToPrefix(results.values()); + Map<String, List<EC_SigResult>> groups = EC_Store.mapToPrefix(results.values()); - List<EC_SigResult> nok = groupList.stream().filter((e) -> e.getKey().equals("nok")).findFirst().get().getValue(); + List<EC_SigResult> nok = groups.entrySet().stream().filter((e) -> e.getKey().equals("nok")).findFirst().get().getValue(); byte[] data = "Some stuff that is not the actual data".getBytes(); for (EC_SigResult sig : nok) { ecdsaTest(sig, Result.ExpectedValue.FAILURE, data); } - List<EC_SigResult> ok = groupList.stream().filter((e) -> e.getKey().equals("ok")).findFirst().get().getValue(); + List<EC_SigResult> ok = groups.entrySet().stream().filter((e) -> e.getKey().equals("ok")).findFirst().get().getValue(); for (EC_SigResult sig : ok) { ecdsaTest(sig, Result.ExpectedValue.SUCCESS, null); } diff --git a/src/cz/crcs/ectester/reader/test/CardTestVectorSuite.java b/src/cz/crcs/ectester/reader/test/CardTestVectorSuite.java index fbdf103..3abcebb 100644 --- a/src/cz/crcs/ectester/reader/test/CardTestVectorSuite.java +++ b/src/cz/crcs/ectester/reader/test/CardTestVectorSuite.java @@ -9,16 +9,28 @@ import cz.crcs.ectester.common.test.Result; import cz.crcs.ectester.common.test.Test; import cz.crcs.ectester.common.test.TestCallback; 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.data.EC_Store; 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 javax.crypto.KeyAgreement; import java.io.IOException; +import java.math.BigInteger; +import java.security.*; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPrivateKeySpec; +import java.security.spec.ECPublicKeySpec; +import java.security.spec.InvalidKeySpecException; +import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import static cz.crcs.ectester.common.test.Result.ExpectedValue; import static cz.crcs.ectester.common.test.Result.Value; @@ -52,8 +64,13 @@ public class CardTestVectorSuite extends CardTestSuite { throw new IOException("Test vector keys couldn't be located."); } List<Test> testVector = new LinkedList<>(); + 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(ExpectedValue.SUCCESS, "No support for " + curve.getBits() + "b " + CardUtil.getKeyTypeString(curve.getField()) + ".", allocate)); + continue; + } - testVector.add(CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), ExpectedValue.SUCCESS)); + 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.Set(this.card, ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.CURVE_external, EC_Consts.PARAMETER_S, onekey.flatten(EC_Consts.PARAMETER_S)), ExpectedValue.SUCCESS)); testVector.add(CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, EC_Consts.PARAMETER_W, otherkey.flatten(EC_Consts.PARAMETER_W)), ExpectedValue.SUCCESS)); @@ -75,7 +92,89 @@ public class CardTestVectorSuite extends CardTestSuite { if (cfg.cleanup) { testVector.add(CommandTest.expect(new Command.Cleanup(this.card), ExpectedValue.ANY)); } - doTest(CompoundTest.greedyAll(ExpectedValue.SUCCESS, "Test vector " + result.getId(), testVector.toArray(new Test[0]))); + doTest(CompoundTest.greedyAll(ExpectedValue.SUCCESS, "Test vector " + result.getId() + ".", testVector.toArray(new Test[0]))); + } + + KeyAgreement ka; + KeyFactory kf; + MessageDigest md; + try { + ka = KeyAgreement.getInstance("ECDH", "BC"); + kf = KeyFactory.getInstance("ECDH", "BC"); + md = MessageDigest.getInstance("SHA1", "BC"); + } catch (NoSuchAlgorithmException | NoSuchProviderException ex) { + return; + } + + List<EC_Curve> testCurves = new ArrayList<>(); + testCurves.addAll(EC_Store.getInstance().getObjects(EC_Curve.class, "secg").values().stream().filter((curve) -> curve.getField() == KeyPair.ALG_EC_FP).collect(Collectors.toList())); + testCurves.addAll(EC_Store.getInstance().getObjects(EC_Curve.class, "brainpool").values().stream().filter((curve) -> curve.getField() == KeyPair.ALG_EC_FP).collect(Collectors.toList())); + for (EC_Curve curve : testCurves) { + List<Test> testVector = new LinkedList<>(); + 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(ExpectedValue.SUCCESS, "No support for " + curve.getBits() + "b " + CardUtil.getKeyTypeString(curve.getField()) + ".", allocate)); + continue; + } + 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); + TestCallback<CommandTestable> kaCallback = new TestCallback<CommandTestable>() { + @Override + public Result apply(CommandTestable testable) { + Response.ECDH ecdhData = (Response.ECDH) testable.getResponse(); + if (!ecdhData.successful()) + return new Result(Value.FAILURE, "ECDH was unsuccessful."); + if (!ecdhData.hasSecret()) { + 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; + try { + privKey = kf.generatePrivate(privKeySpec); + pubkey = kf.generatePublic(pubKeySpec); + ka.init(privKey); + ka.doPhase(pubkey, true); + byte[] rawDerived = ka.generateSecret(); + int fieldSize = (curve.getBits() + 7) / 8; + if (rawDerived.length < fieldSize) { + byte[] padded = new byte[fieldSize]; + System.arraycopy(rawDerived, 0, padded, fieldSize - rawDerived.length, rawDerived.length); + rawDerived = padded; + } + byte[] derived = md.digest(rawDerived); + if (secret.length != derived.length) { + if (secret.length < derived.length) { + return new Result(Value.FAILURE, String.format("Derived secret was shorter than expected: %d vs %d (expected).", secret.length, derived.length)); + } else { + return new Result(Value.FAILURE, String.format("Derived secret was longer than expected: %d vs %d (expected).", secret.length, derived.length)); + } + } + int diff = ByteUtil.diffBytes(derived, 0, secret, 0, secret.length); + 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) + "."); + } + } catch (InvalidKeySpecException | InvalidKeyException 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)); + if (cfg.cleanup) { + testVector.add(CommandTest.expect(new Command.Cleanup(this.card), ExpectedValue.ANY)); + } + doTest(CompoundTest.greedyAll(ExpectedValue.SUCCESS, "Validation test on " + curve.getId() + ".", testVector.toArray(new Test[0]))); } } } diff --git a/src/cz/crcs/ectester/reader/test/CardTwistSuite.java b/src/cz/crcs/ectester/reader/test/CardTwistSuite.java index 6ad4ce6..3df4c65 100644 --- a/src/cz/crcs/ectester/reader/test/CardTwistSuite.java +++ b/src/cz/crcs/ectester/reader/test/CardTwistSuite.java @@ -29,8 +29,8 @@ public class CardTwistSuite extends CardTestSuite { @Override protected void runTests() throws Exception { Map<String, EC_Key.Public> pubkeys = EC_Store.getInstance().getObjects(EC_Key.Public.class, "twist"); - List<Map.Entry<EC_Curve, List<EC_Key.Public>>> curveList = EC_Store.mapKeyToCurve(pubkeys.values()); - for (Map.Entry<EC_Curve, List<EC_Key.Public>> e : curveList) { + Map<EC_Curve, List<EC_Key.Public>> curveList = EC_Store.mapKeyToCurve(pubkeys.values()); + for (Map.Entry<EC_Curve, List<EC_Key.Public>> e : curveList.entrySet()) { EC_Curve curve = e.getKey(); List<EC_Key.Public> keys = e.getValue(); diff --git a/src/cz/crcs/ectester/standalone/ECTesterStandalone.java b/src/cz/crcs/ectester/standalone/ECTesterStandalone.java index 1d7b821..e19377d 100644 --- a/src/cz/crcs/ectester/standalone/ECTesterStandalone.java +++ b/src/cz/crcs/ectester/standalone/ECTesterStandalone.java @@ -28,6 +28,7 @@ import cz.crcs.ectester.common.output.TestWriter; import cz.crcs.ectester.common.test.TestException; import cz.crcs.ectester.common.util.ByteUtil; 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.standalone.consts.KeyAgreementIdent; import cz.crcs.ectester.standalone.consts.KeyPairGeneratorIdent; @@ -49,6 +50,7 @@ import javax.xml.parsers.ParserConfigurationException; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; +import java.io.PrintStream; import java.nio.file.Files; import java.security.*; import java.security.interfaces.ECPrivateKey; @@ -66,7 +68,17 @@ import java.util.stream.Collectors; * @version v0.3.0 */ public class ECTesterStandalone { - private ProviderECLibrary[] libs = new ProviderECLibrary[]{new SunECLib(), new BouncyCastleLib(), new TomcryptLib(), new BotanLib(), new CryptoppLib(), new OpensslLib(), new MscngLib()}; + private ProviderECLibrary[] libs = new ProviderECLibrary[]{ + new SunECLib(), + new BouncyCastleLib(), + new TomcryptLib(), + new BotanLib(), + new CryptoppLib(), + new OpensslLib(), + new BoringsslLib(), + new GcryptLib(), + new MscngLib(), + new WolfCryptLib()}; private Config cfg; private Options opts = new Options(); @@ -140,6 +152,7 @@ public class ECTesterStandalone { 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 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(); Options testOpts = new Options(); testOpts.addOption(bits); @@ -159,9 +172,12 @@ public class ECTesterStandalone { ecdhOpts.addOption(bits); ecdhOpts.addOption(namedCurve); ecdhOpts.addOption(curveName); + ecdhOpts.addOption(output); 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(Option.builder().longOpt("fixed-private").desc("Perform ECDH with fixed private key.").build()); + 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); @@ -169,6 +185,7 @@ public class ECTesterStandalone { ecdsaOpts.addOption(bits); ecdsaOpts.addOption(namedCurve); ecdsaOpts.addOption(curveName); + ecdhOpts.addOption(output); 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()); @@ -179,6 +196,7 @@ public class ECTesterStandalone { generateOpts.addOption(bits); generateOpts.addOption(namedCurve); generateOpts.addOption(curveName); + ecdhOpts.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."); @@ -186,6 +204,7 @@ public class ECTesterStandalone { Options exportOpts = new Options(); exportOpts.addOption(bits); + ecdhOpts.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); @@ -222,6 +241,7 @@ public class ECTesterStandalone { for (ProviderECLibrary lib : libs) { if (lib.isInitialized() && (cfg.selected == null || lib == cfg.selected)) { System.out.println("\t- " + Colors.bold(lib.name())); + System.out.println(Colors.bold("\t\t- Supports native timing: ") + lib.supportsNativeTiming()); Set<KeyPairGeneratorIdent> kpgs = lib.getKPGs(); if (!kpgs.isEmpty()) { System.out.println(Colors.bold("\t\t- KeyPairGenerators: ") + String.join(", ", kpgs.stream().map(KeyPairGeneratorIdent::getName).collect(Collectors.toList()))); @@ -259,7 +279,7 @@ public class ECTesterStandalone { /** * */ - private void ecdh() throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException { + private void ecdh() throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, FileNotFoundException { ProviderECLibrary lib = cfg.selected; String algo = cli.getOptionValue("ecdh.type", "ECDH"); @@ -292,63 +312,90 @@ public class ECTesterStandalone { if (kaIdent == null || kpIdent == null) { throw new NoSuchAlgorithmException(algo); - } else { - KeyAgreement ka = kaIdent.getInstance(lib.getProvider()); - KeyPairGenerator kpg = kpIdent.getInstance(lib.getProvider()); - AlgorithmParameterSpec spec = null; - if (cli.hasOption("ecdh.bits")) { - int bits = Integer.parseInt(cli.getOptionValue("ecdh.bits")); - kpg.initialize(bits); - } else if (cli.hasOption("ecdh.named-curve")) { - String curveName = cli.getOptionValue("ecdh.named-curve"); - EC_Curve curve = EC_Store.getInstance().getObject(EC_Curve.class, curveName); - if (curve == null) { - System.err.println("Curve not found: " + curveName); - return; - } - spec = curve.toSpec(); - kpg.initialize(spec); - } else if (cli.hasOption("ecdh.curve-name")) { - String curveName = cli.getOptionValue("ecdh.curve-name"); - spec = new ECGenParameterSpec(curveName); - kpg.initialize(spec); + } + + KeyAgreement ka = kaIdent.getInstance(lib.getProvider()); + KeyPairGenerator kpg = kpIdent.getInstance(lib.getProvider()); + AlgorithmParameterSpec spec = null; + if (cli.hasOption("ecdh.bits")) { + int bits = Integer.parseInt(cli.getOptionValue("ecdh.bits")); + kpg.initialize(bits); + } else if (cli.hasOption("ecdh.named-curve")) { + String curveName = cli.getOptionValue("ecdh.named-curve"); + EC_Curve curve = EC_Store.getInstance().getObject(EC_Curve.class, curveName); + if (curve == null) { + System.err.println("Curve not found: " + curveName); + return; } + spec = curve.toSpec(); + kpg.initialize(spec); + } else if (cli.hasOption("ecdh.curve-name")) { + String curveName = cli.getOptionValue("ecdh.curve-name"); + spec = new ECGenParameterSpec(curveName); + kpg.initialize(spec); + } - System.out.println("index;nanotime;pubW;privS;secret"); + PrintStream out; + if (cli.hasOption("ecdh.output")) { + out = new PrintStream(FileUtil.openStream(cli.getOptionValues("ecdh.output"))); + } else { + out = System.out; + } - int amount = Integer.parseInt(cli.getOptionValue("ecdh.amount", "1")); - for (int i = 0; i < amount; ++i) { - KeyPair one = kpg.genKeyPair(); - KeyPair other = kpg.genKeyPair(); + out.println("index;time[nano];pubW;privS;secret"); - ECPrivateKey privkey = (ECPrivateKey) one.getPrivate(); - ECPublicKey pubkey = (ECPublicKey) other.getPublic(); + KeyPair one = null; + if (cli.hasOption("ecdh.fixed-private")) { + one = kpg.genKeyPair(); + } + KeyPair other = null; + if (cli.hasOption("ecdh.fixed-public")) { + other = kpg.genKeyPair(); + } - long elapsed = -System.nanoTime(); - if (spec != null) { - ka.init(privkey, spec); - } else { - ka.init(privkey); - } - ka.doPhase(pubkey, true); - elapsed += System.nanoTime(); - SecretKey derived; - byte[] result; - elapsed -= System.nanoTime(); - if (kaIdent.requiresKeyAlgo()) { - derived = ka.generateSecret(keyAlgo); - result = derived.getEncoded(); - } else { - result = ka.generateSecret(); - } - elapsed += System.nanoTime(); - ka = kaIdent.getInstance(lib.getProvider()); + int amount = Integer.parseInt(cli.getOptionValue("ecdh.amount", "1")); + for (int i = 0; i < amount; ++i) { + if (!cli.hasOption("ecdh.fixed-private")) { + one = kpg.genKeyPair(); + } + if (!cli.hasOption("ecdh.fixed-public")) { + other = kpg.genKeyPair(); + } + + ECPrivateKey privkey = (ECPrivateKey) one.getPrivate(); + ECPublicKey pubkey = (ECPublicKey) other.getPublic(); - String pub = ByteUtil.bytesToHex(ECUtil.toX962Uncompressed(pubkey.getW(), pubkey.getParams()), false); - String priv = ByteUtil.bytesToHex(privkey.getS().toByteArray(), false); - String dh = ByteUtil.bytesToHex(result, false); - System.out.println(String.format("%d;%d;%s;%s;%s", i, elapsed, pub, priv, dh)); + long elapsed = -System.nanoTime(); + if (spec instanceof ECParameterSpec) { + ka.init(privkey, spec); + } else { + ka.init(privkey); } + ka.doPhase(pubkey, true); + elapsed += System.nanoTime(); + SecretKey derived; + byte[] result; + elapsed -= System.nanoTime(); + if (kaIdent.requiresKeyAlgo()) { + derived = ka.generateSecret(keyAlgo); + result = derived.getEncoded(); + } else { + result = ka.generateSecret(); + } + elapsed += System.nanoTime(); + if (lib.supportsNativeTiming()) { + elapsed = lib.getLastNativeTiming(); + } + ka = kaIdent.getInstance(lib.getProvider()); + + String pub = ByteUtil.bytesToHex(ECUtil.toX962Uncompressed(pubkey.getW(), pubkey.getParams()), false); + String priv = ByteUtil.bytesToHex(privkey.getS().toByteArray(), false); + String dh = ByteUtil.bytesToHex(result, false); + out.println(String.format("%d;%d;%s;%s;%s", i, elapsed, pub, priv, dh)); + } + + if (cli.hasOption("ecdh.output")) { + out.close(); } } @@ -405,61 +452,76 @@ public class ECTesterStandalone { if (sigIdent == null || kpIdent == null) { throw new NoSuchAlgorithmException(algo); - } else { - Signature sig = sigIdent.getInstance(lib.getProvider()); - KeyPairGenerator kpg = kpIdent.getInstance(lib.getProvider()); - if (cli.hasOption("ecdsa.bits")) { - int bits = Integer.parseInt(cli.getOptionValue("ecdsa.bits")); - kpg.initialize(bits); - } else if (cli.hasOption("ecdsa.named-curve")) { - String curveName = cli.getOptionValue("ecdsa.named-curve"); - EC_Curve curve = EC_Store.getInstance().getObject(EC_Curve.class, curveName); - if (curve == null) { - System.err.println("Curve not found: " + curveName); - return; - } - kpg.initialize(curve.toSpec()); - } else if (cli.hasOption("ecdsa.curve-name")) { - String curveName = cli.getOptionValue("ecdsa.curve-name"); - kpg.initialize(new ECGenParameterSpec(curveName)); + } + Signature sig = sigIdent.getInstance(lib.getProvider()); + KeyPairGenerator kpg = kpIdent.getInstance(lib.getProvider()); + if (cli.hasOption("ecdsa.bits")) { + int bits = Integer.parseInt(cli.getOptionValue("ecdsa.bits")); + kpg.initialize(bits); + } else if (cli.hasOption("ecdsa.named-curve")) { + String curveName = cli.getOptionValue("ecdsa.named-curve"); + EC_Curve curve = EC_Store.getInstance().getObject(EC_Curve.class, curveName); + if (curve == null) { + System.err.println("Curve not found: " + curveName); + return; } + kpg.initialize(curve.toSpec()); + } else if (cli.hasOption("ecdsa.curve-name")) { + String curveName = cli.getOptionValue("ecdsa.curve-name"); + kpg.initialize(new ECGenParameterSpec(curveName)); + } - System.out.println("index;data;signtime;verifytime;pubW;privS;signature;verified"); - - int amount = Integer.parseInt(cli.getOptionValue("ecdsa.amount", "1")); - for (int i = 0; i < amount; ++i) { - KeyPair one = kpg.genKeyPair(); + PrintStream out; + if (cli.hasOption("ecdsa.output")) { + out = new PrintStream(FileUtil.openStream(cli.getOptionValues("ecdh.output"))); + } else { + out = System.out; + } - ECPrivateKey privkey = (ECPrivateKey) one.getPrivate(); - ECPublicKey pubkey = (ECPublicKey) one.getPublic(); + out.println("index;data;signTime[nano];verifyTime[nano];pubW;privS;signature;verified"); - sig.initSign(privkey); - sig.update(data); + int amount = Integer.parseInt(cli.getOptionValue("ecdsa.amount", "1")); + for (int i = 0; i < amount; ++i) { + KeyPair one = kpg.genKeyPair(); - long signTime = -System.nanoTime(); - byte[] signature = sig.sign(); - signTime += System.nanoTime(); + ECPrivateKey privkey = (ECPrivateKey) one.getPrivate(); + ECPublicKey pubkey = (ECPublicKey) one.getPublic(); - sig.initVerify(pubkey); - sig.update(data); + sig.initSign(privkey); + sig.update(data); - long verifyTime = -System.nanoTime(); - boolean verified = sig.verify(signature); - verifyTime += System.nanoTime(); + long signTime = -System.nanoTime(); + byte[] signature = sig.sign(); + signTime += System.nanoTime(); + if (lib.supportsNativeTiming()) { + signTime = lib.getLastNativeTiming(); + } + sig.initVerify(pubkey); + sig.update(data); - 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); - System.out.println(String.format("%d;%s;%d;%d;%s;%s;%s;%d", i, dataString, signTime, verifyTime, pub, priv, sign, verified ? 1 : 0)); + long verifyTime = -System.nanoTime(); + boolean verified = sig.verify(signature); + verifyTime += System.nanoTime(); + if (lib.supportsNativeTiming()) { + verifyTime = lib.getLastNativeTiming(); } + + 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)); + } + + if (cli.hasOption("ecdsa.output")) { + out.close(); } } /** * */ - private void generate() throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { + private void generate() throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, FileNotFoundException { ProviderECLibrary lib = cfg.selected; KeyPairGeneratorIdent ident = null; String algo = cli.getOptionValue("generate.type", "EC"); @@ -471,37 +533,51 @@ public class ECTesterStandalone { } if (ident == null) { throw new NoSuchAlgorithmException(algo); - } else { - KeyPairGenerator kpg = ident.getInstance(lib.getProvider()); - if (cli.hasOption("generate.bits")) { - int bits = Integer.parseInt(cli.getOptionValue("generate.bits")); - kpg.initialize(bits); - } else if (cli.hasOption("generate.named-curve")) { - String curveName = cli.getOptionValue("generate.named-curve"); - EC_Curve curve = EC_Store.getInstance().getObject(EC_Curve.class, curveName); - if (curve == null) { - System.err.println("Curve not found: " + curveName); - return; - } - kpg.initialize(curve.toSpec()); - } else if (cli.hasOption("generate.curve-name")) { - String curveName = cli.getOptionValue("generate.curve-name"); - kpg.initialize(new ECGenParameterSpec(curveName)); + } + KeyPairGenerator kpg = ident.getInstance(lib.getProvider()); + if (cli.hasOption("generate.bits")) { + int bits = Integer.parseInt(cli.getOptionValue("generate.bits")); + kpg.initialize(bits); + } else if (cli.hasOption("generate.named-curve")) { + String curveName = cli.getOptionValue("generate.named-curve"); + EC_Curve curve = EC_Store.getInstance().getObject(EC_Curve.class, curveName); + if (curve == null) { + System.err.println("Curve not found: " + curveName); + return; } - System.out.println("index;nanotime;pubW;privS"); + kpg.initialize(curve.toSpec()); + } else if (cli.hasOption("generate.curve-name")) { + String curveName = cli.getOptionValue("generate.curve-name"); + kpg.initialize(new ECGenParameterSpec(curveName)); + } - int amount = Integer.parseInt(cli.getOptionValue("generate.amount", "1")); - for (int i = 0; i < amount; ++i) { - long elapsed = -System.nanoTime(); - KeyPair kp = kpg.genKeyPair(); - elapsed += System.nanoTime(); - ECPublicKey publicKey = (ECPublicKey) kp.getPublic(); - ECPrivateKey privateKey = (ECPrivateKey) kp.getPrivate(); + PrintStream out; + if (cli.hasOption("generate.output")) { + out = new PrintStream(FileUtil.openStream(cli.getOptionValues("ecdh.output"))); + } else { + out = System.out; + } - String pub = ByteUtil.bytesToHex(ECUtil.toX962Uncompressed(publicKey.getW(), publicKey.getParams()), false); - String priv = ByteUtil.bytesToHex(privateKey.getS().toByteArray(), false); - System.out.println(String.format("%d;%d;%s;%s", i, elapsed, pub, priv)); + out.println("index;time[nano];pubW;privS"); + + int amount = Integer.parseInt(cli.getOptionValue("generate.amount", "1")); + for (int i = 0; i < amount || amount == 0; ++i) { + long elapsed = -System.nanoTime(); + KeyPair kp = kpg.genKeyPair(); + elapsed += System.nanoTime(); + if (lib.supportsNativeTiming()) { + elapsed = lib.getLastNativeTiming(); } + ECPublicKey publicKey = (ECPublicKey) kp.getPublic(); + ECPrivateKey privateKey = (ECPrivateKey) kp.getPrivate(); + + String pub = ByteUtil.bytesToHex(ECUtil.toX962Uncompressed(publicKey.getW(), publicKey.getParams()), false); + String priv = ByteUtil.bytesToHex(privateKey.getS().toByteArray(), false); + out.println(String.format("%d;%d;%s;%s", i, elapsed, pub, priv)); + } + + if (cli.hasOption("generate.output")) { + out.close(); } } @@ -545,19 +621,18 @@ public class ECTesterStandalone { } if (ident == null) { throw new NoSuchAlgorithmException(algo); - } else { - KeyPairGenerator kpg = ident.getInstance(lib.getProvider()); - if (cli.hasOption("export.bits")) { - int bits = Integer.parseInt(cli.getOptionValue("export.bits")); - kpg.initialize(bits); - } - KeyPair kp = kpg.genKeyPair(); - ECPrivateKey privateKey = (ECPrivateKey) kp.getPrivate(); - ECParameterSpec params = privateKey.getParams(); - System.out.println(params); - EC_Curve curve = EC_Curve.fromSpec(params); - curve.writeCSV(System.out); } + KeyPairGenerator kpg = ident.getInstance(lib.getProvider()); + if (cli.hasOption("export.bits")) { + int bits = Integer.parseInt(cli.getOptionValue("export.bits")); + kpg.initialize(bits); + } + KeyPair kp = kpg.genKeyPair(); + ECPrivateKey privateKey = (ECPrivateKey) kp.getPrivate(); + ECParameterSpec params = privateKey.getParams(); + System.out.println(params); + EC_Curve curve = EC_Curve.fromSpec(params); + curve.writeCSV(System.out); } public static void main(String[] args) { diff --git a/src/cz/crcs/ectester/standalone/consts/KeyAgreementIdent.java b/src/cz/crcs/ectester/standalone/consts/KeyAgreementIdent.java index 66d8235..808f298 100644 --- a/src/cz/crcs/ectester/standalone/consts/KeyAgreementIdent.java +++ b/src/cz/crcs/ectester/standalone/consts/KeyAgreementIdent.java @@ -11,6 +11,8 @@ import java.util.List; */ public class KeyAgreementIdent extends Ident { private boolean requiresKeyAlgo; + private String kdf; + private String algo; private static final List<KeyAgreementIdent> ALL = new LinkedList<>(); @@ -73,6 +75,19 @@ public class KeyAgreementIdent extends Ident { private KeyAgreementIdent(String name, String... aliases) { super(name, aliases); + if (name.contains("with")) { + int split = name.indexOf("with"); + this.algo = name.substring(0, split); + this.kdf = name.substring(split + 4); + } else { + for (String alias : aliases) { + if (alias.contains("with")) { + int split = alias.indexOf("with"); + this.algo = alias.substring(0, split); + this.kdf = alias.substring(split + 4); + } + } + } } private KeyAgreementIdent(String name, boolean requiresKeyAlgo, String... aliases) { @@ -84,6 +99,14 @@ public class KeyAgreementIdent extends Ident { return requiresKeyAlgo; } + public String getKdfAlgo() { + return kdf; + } + + public String getBaseAlgo() { + return algo; + } + public KeyAgreement getInstance(Provider provider) throws NoSuchAlgorithmException { KeyAgreement instance = getInstance((algorithm, provider1) -> { try { diff --git a/src/cz/crcs/ectester/standalone/consts/SignatureIdent.java b/src/cz/crcs/ectester/standalone/consts/SignatureIdent.java index dea8abe..e40731b 100644 --- a/src/cz/crcs/ectester/standalone/consts/SignatureIdent.java +++ b/src/cz/crcs/ectester/standalone/consts/SignatureIdent.java @@ -10,6 +10,9 @@ import java.util.List; * @author Jan Jancar johny@neuromancer.sk */ public class SignatureIdent extends Ident { + private String hash; + private String sig; + private static final List<SignatureIdent> ALL = new LinkedList<>(); static { @@ -52,9 +55,8 @@ public class SignatureIdent extends Ident { ALL.add(new SignatureIdent("ECGOST3410-2012-512", "GOST-3410-2012-512")); ALL.add(new SignatureIdent("GOST3411-2012-512withECGOST3410-2012-512", "GOST3411-2012-512/ECGOST3410-2012-5120", "1.2.643.7.1.1.3.3")); ALL.add(new SignatureIdent("SM3withSM2")); - // ECDDSA - ALL.add(new SignatureIdent("ECDDSA", "DETECDSA", "ECDETDSA")); - ALL.add(new SignatureIdent("SHA1withECDDSA", "SHA1withDETECDSA")); + // ECDDSA (rfc6979?) + ALL.add(new SignatureIdent("ECDDSA", "SHA1withECDDSA", "SHA1withDETECDSA", "DETECDSA", "ECDETDSA")); ALL.add(new SignatureIdent("SHA224withECDDSA", "SHA224withDETECDSA")); ALL.add(new SignatureIdent("SHA256withECDDSA", "SHA256withDETECDSA")); ALL.add(new SignatureIdent("SHA384withECDDSA", "SHA384withDETECDSA")); @@ -92,6 +94,19 @@ public class SignatureIdent extends Ident { private SignatureIdent(String name, String... aliases) { super(name, aliases); + if (name.contains("with")) { + int split = name.indexOf("with"); + this.hash = name.substring(0, split); + this.sig = name.substring(split + 4); + } else { + for (String alias : aliases) { + if (alias.contains("with")) { + int split = alias.indexOf("with"); + this.hash = alias.substring(0, split); + this.sig = alias.substring(split + 4); + } + } + } } public Signature getInstance(Provider provider) throws NoSuchAlgorithmException { @@ -105,4 +120,12 @@ public class SignatureIdent extends Ident { instance.getProvider(); return instance; } + + public String getHashAlgo() { + return hash; + } + + public String getSigType() { + return sig; + } } diff --git a/src/cz/crcs/ectester/standalone/libs/BoringsslLib.java b/src/cz/crcs/ectester/standalone/libs/BoringsslLib.java new file mode 100644 index 0000000..35a48a8 --- /dev/null +++ b/src/cz/crcs/ectester/standalone/libs/BoringsslLib.java @@ -0,0 +1,28 @@ +package cz.crcs.ectester.standalone.libs; + +import java.security.Provider; +import java.util.Set; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class BoringsslLib extends NativeECLibrary { + public BoringsslLib() { + super("boringssl_provider", "lib_boringssl.so"); + } + + @Override + public native boolean supportsNativeTiming(); + + @Override + public native long getNativeTimingResolution(); + + @Override + public native long getLastNativeTiming(); + + @Override + native Provider createProvider(); + + @Override + public native Set<String> getCurves(); +} diff --git a/src/cz/crcs/ectester/standalone/libs/BotanLib.java b/src/cz/crcs/ectester/standalone/libs/BotanLib.java index cd28791..34fb178 100644 --- a/src/cz/crcs/ectester/standalone/libs/BotanLib.java +++ b/src/cz/crcs/ectester/standalone/libs/BotanLib.java @@ -13,6 +13,15 @@ public class BotanLib extends NativeECLibrary { } @Override + public native boolean supportsNativeTiming(); + + @Override + public native long getNativeTimingResolution(); + + @Override + public native long getLastNativeTiming(); + + @Override native Provider createProvider(); @Override diff --git a/src/cz/crcs/ectester/standalone/libs/CryptoppLib.java b/src/cz/crcs/ectester/standalone/libs/CryptoppLib.java index 5112d7d..5153df5 100644 --- a/src/cz/crcs/ectester/standalone/libs/CryptoppLib.java +++ b/src/cz/crcs/ectester/standalone/libs/CryptoppLib.java @@ -13,6 +13,15 @@ public class CryptoppLib extends NativeECLibrary { } @Override + public native boolean supportsNativeTiming(); + + @Override + public native long getNativeTimingResolution(); + + @Override + public native long getLastNativeTiming(); + + @Override native Provider createProvider(); @Override diff --git a/src/cz/crcs/ectester/standalone/libs/GcryptLib.java b/src/cz/crcs/ectester/standalone/libs/GcryptLib.java new file mode 100644 index 0000000..ef20f97 --- /dev/null +++ b/src/cz/crcs/ectester/standalone/libs/GcryptLib.java @@ -0,0 +1,29 @@ +package cz.crcs.ectester.standalone.libs; + +import java.security.Provider; +import java.util.Set; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class GcryptLib extends NativeECLibrary { + + public GcryptLib() { + super("gcrypt_provider", "gcrypt", "gpg-error"); + } + + @Override + public native boolean supportsNativeTiming(); + + @Override + public native long getNativeTimingResolution(); + + @Override + public native long getLastNativeTiming(); + + @Override + native Provider createProvider(); + + @Override + public native Set<String> getCurves(); +} diff --git a/src/cz/crcs/ectester/standalone/libs/MscngLib.java b/src/cz/crcs/ectester/standalone/libs/MscngLib.java index 527a65b..354199a 100644 --- a/src/cz/crcs/ectester/standalone/libs/MscngLib.java +++ b/src/cz/crcs/ectester/standalone/libs/MscngLib.java @@ -13,6 +13,15 @@ public class MscngLib extends NativeECLibrary { } @Override + public native boolean supportsNativeTiming(); + + @Override + public native long getNativeTimingResolution(); + + @Override + public native long getLastNativeTiming(); + + @Override native Provider createProvider(); @Override diff --git a/src/cz/crcs/ectester/standalone/libs/NativeECLibrary.java b/src/cz/crcs/ectester/standalone/libs/NativeECLibrary.java index 6b98cc1..7870377 100644 --- a/src/cz/crcs/ectester/standalone/libs/NativeECLibrary.java +++ b/src/cz/crcs/ectester/standalone/libs/NativeECLibrary.java @@ -47,39 +47,19 @@ public abstract class NativeECLibrary extends ProviderECLibrary { appData = Paths.get(System.getProperty("user.home"), ".local", "share"); } } + /* Resolve and create the ECTester directories in appData. */ Path libDir = appData.resolve("ECTesterStandalone"); File libDirFile = libDir.toFile(); + Path libReqDir = libDir.resolve("lib"); + File libReqDirFile = libReqDir.toFile(); Path libPath = libDir.resolve(resource + "." + suffix); - File libFile = libPath.toFile(); - URL jarURL = NativeECLibrary.class.getResource(LIB_RESOURCE_DIR + resource + "." + suffix); - if (jarURL == null) { - return false; - } - URLConnection jarConnection = jarURL.openConnection(); - - /* Only write the file if it does not exist, - * or if the existing one is older than the - * one in the JAR. - */ - boolean write = false; - if (libDirFile.isDirectory() && libFile.isFile()) { - long jarModified = jarConnection.getLastModified(); + /* Create directory for shims and for requirements. */ + libDirFile.mkdirs(); + libReqDirFile.mkdirs(); - long libModified = Files.getLastModifiedTime(libPath).toMillis(); - if (jarModified > libModified) { - write = true; - } - } else { - libDir.toFile().mkdirs(); - libFile.createNewFile(); - write = true; - } - - if (write) { - Files.copy(jarConnection.getInputStream(), libPath, StandardCopyOption.REPLACE_EXISTING); - } - jarConnection.getInputStream().close(); + /* Write the shim. */ + writeNewer(resource + "." + suffix, libPath); /* * Need to hack in /usr/local/lib to path. @@ -98,12 +78,24 @@ public abstract class NativeECLibrary extends ProviderECLibrary { } } - for (String requirement : requriements) { - System.loadLibrary(requirement); - } - - if (suffix.equals("so")) { - System.setProperty("java.library.path", path); + /* Load the requirements, if they are bundled, write them in and load them. */ + try { + for (String requirement : requriements) { + if (requirement.endsWith(suffix)) { + /* The requirement is bundled, write it */ + Path reqPath = libReqDir.resolve(requirement); + writeNewer(requirement, reqPath); + System.load(reqPath.toString()); + } else { + System.loadLibrary(requirement); + } + } + } catch (UnsatisfiedLinkError ule) { + return false; + } finally { + if (suffix.equals("so")) { + System.setProperty("java.library.path", path); + } } System.load(libPath.toString()); @@ -116,5 +108,36 @@ public abstract class NativeECLibrary extends ProviderECLibrary { return false; } + private boolean isNewer(URLConnection jarConn, Path realPath) throws IOException { + if (realPath.toFile().isFile()) { + long jarModified = jarConn.getLastModified(); + long realModified = Files.getLastModifiedTime(realPath).toMillis(); + return jarModified > realModified; + } + return true; + } + + private boolean writeNewer(String resource, Path outPath) throws IOException { + URL reqURL = NativeECLibrary.class.getResource(LIB_RESOURCE_DIR + resource); + if (reqURL == null) { + return false; + } + URLConnection reqConn = reqURL.openConnection(); + if (isNewer(reqConn, outPath)) { + Files.copy(reqConn.getInputStream(), outPath, StandardCopyOption.REPLACE_EXISTING); + } + reqConn.getInputStream().close(); + return true; + } + + @Override + public abstract boolean supportsNativeTiming(); + + @Override + public abstract long getNativeTimingResolution(); + + @Override + public abstract long getLastNativeTiming(); + abstract Provider createProvider(); } diff --git a/src/cz/crcs/ectester/standalone/libs/OpensslLib.java b/src/cz/crcs/ectester/standalone/libs/OpensslLib.java index e558336..4f44a2a 100644 --- a/src/cz/crcs/ectester/standalone/libs/OpensslLib.java +++ b/src/cz/crcs/ectester/standalone/libs/OpensslLib.java @@ -12,6 +12,15 @@ public class OpensslLib extends NativeECLibrary { } @Override + public native boolean supportsNativeTiming(); + + @Override + public native long getNativeTimingResolution(); + + @Override + public native long getLastNativeTiming(); + + @Override native Provider createProvider(); @Override diff --git a/src/cz/crcs/ectester/standalone/libs/ProviderECLibrary.java b/src/cz/crcs/ectester/standalone/libs/ProviderECLibrary.java index 9108eaf..83a9dc9 100644 --- a/src/cz/crcs/ectester/standalone/libs/ProviderECLibrary.java +++ b/src/cz/crcs/ectester/standalone/libs/ProviderECLibrary.java @@ -62,6 +62,18 @@ public abstract class ProviderECLibrary implements ECLibrary { return results; } + public boolean supportsNativeTiming() { + return false; + } + + public long getNativeTimingResolution() { + return 0; + } + + public long getLastNativeTiming() { + return 0; + } + @Override public Set<KeyAgreementIdent> getKAs() { return getIdents("KeyAgreement", KeyAgreementIdent::get); diff --git a/src/cz/crcs/ectester/standalone/libs/TomcryptLib.java b/src/cz/crcs/ectester/standalone/libs/TomcryptLib.java index 78db00e..6ac74c9 100644 --- a/src/cz/crcs/ectester/standalone/libs/TomcryptLib.java +++ b/src/cz/crcs/ectester/standalone/libs/TomcryptLib.java @@ -13,6 +13,16 @@ public class TomcryptLib extends NativeECLibrary { } @Override + public native boolean supportsNativeTiming(); + + @Override + public native long getNativeTimingResolution(); + + @Override + public native long getLastNativeTiming(); + + + @Override native Provider createProvider(); @Override diff --git a/src/cz/crcs/ectester/standalone/libs/WolfCryptLib.java b/src/cz/crcs/ectester/standalone/libs/WolfCryptLib.java new file mode 100644 index 0000000..b58eb91 --- /dev/null +++ b/src/cz/crcs/ectester/standalone/libs/WolfCryptLib.java @@ -0,0 +1,18 @@ +package cz.crcs.ectester.standalone.libs; + +import com.wolfssl.provider.jce.WolfCryptProvider; + +import java.util.HashSet; +import java.util.Set; + +public class WolfCryptLib extends ProviderECLibrary { + + public WolfCryptLib() { + super(new WolfCryptProvider()); + } + + @Override + public Set<String> getCurves() { + return new HashSet<>(); + } +} diff --git a/src/cz/crcs/ectester/standalone/libs/jni/Makefile b/src/cz/crcs/ectester/standalone/libs/jni/Makefile index 006a3b1..c8ab47b 100644 --- a/src/cz/crcs/ectester/standalone/libs/jni/Makefile +++ b/src/cz/crcs/ectester/standalone/libs/jni/Makefile @@ -1,3 +1,28 @@ +############################################################################### +## General CC setup. + +CC?=gcc +CXX?=g++ + +LFLAGS+=-fPIC -shared +CFLAGS+=-fPIC -I"$(JNI_INCLUDEDIR)" -I"$(JNI_PLATFORMINCLUDEDIR)" -I. +CXXFLAGS+=-fPIC -I"$(JNI_INCLUDEDIR)" -I"$(JNI_PLATFORMINCLUDEDIR)" -I. + +DEBUG ?= 0 + +ifeq ($(DEBUG), 1) + CFLAGS+=-g -Wall + LFLAGS+=-g + CXXFLAGS+=-g -Wall +else + CFLAGS+=-O2 + LFLAGS+=-O2 + CXXFLAGS+=-O2 +endif + +############################################################################### +## Java JNI setup. + ifeq ($(JAVA_HOME),) ifeq ($(OS),Windows_NT) which = $(shell where $1) @@ -24,56 +49,55 @@ ifeq ($(JNI_PLATFORM),) else ifeq ($(findstring linux,$(TARGETTRIPLET)),linux) JNI_PLATFORM:= linux - # add more checks here endif endif endif JNI_PLATFORMINCLUDEDIR ?= $(JNI_INCLUDEDIR)/$(JNI_PLATFORM) -LOCAL_INCLUDES = /usr/local/include -LOCAL_LIBS = /usr/local/lib - -CC?=gcc -CXX?=g++ -STRIP?=strip - -LFLAGS+=-fPIC -shared -CFLAGS+=-fPIC -I"$(JNI_INCLUDEDIR)" -I"$(JNI_PLATFORMINCLUDEDIR)" -I. -CXXFLAGS+=-fPIC -I"$(JNI_INCLUDEDIR)" -I"$(JNI_PLATFORMINCLUDEDIR)" -I. - -DEBUG ?= 0 - -ifeq ($(DEBUG), 1) - CFLAGS+=-g - LFLAGS+=-g - CXXFLAGS+=-g -else - CFLAGS+=-O2 - LFLAGS+=-O2 - CXXFLAGS+=-O2 -endif +############################################################################### +## Targets. -all: tomcrypt_provider.so botan_provider.so cryptopp_provider.so openssl_provider.so +all: tomcrypt_provider.so botan_provider.so cryptopp_provider.so openssl_provider.so boringssl_provider.so gcrypt_provider.so # Common utils c_utils.o: c_utils.c $(CC) $(CFLAGS) -c $< +c_timing.o: c_timing.c + $(CC) $(CFLAGS) -c $< + cpp_utils.o: cpp_utils.cpp $(CXX) $(CXXFLAGS) -c $< # OpenSSL shim -openssl_provider.so: openssl.o c_utils.o +openssl_provider.so: openssl.o c_utils.o c_timing.o $(CC) $(LFLAGS) -o $@ $^ -L. $(shell pkg-config --libs openssl) openssl.o: openssl.c $(CC) $(shell pkg-config --cflags openssl) $(CFLAGS) -c $< +# BoringSSL shim +boringssl_provider.so: boringssl.o c_utils.o c_timing.o + $(CC) $(LFLAGS) -o $@ $^ -L. ../../../../../../../ext/boringssl/build/crypto/libcrypto.so + cp ../../../../../../../ext/boringssl/build/crypto/libcrypto.so lib_boringssl.so + +boringssl.o: boringssl.c + $(CC) -I../../../../../../../ext/boringssl/include/ $(CFLAGS) -c $< + + +# libgcrypt shim +gcrypt_provider.so: gcrypt.o c_utils.o c_timing.o + $(CC) $(LFLAGS) -o $@ $^ -L. $(shell libgcrypt-config --libs) + +gcrypt.o: gcrypt.c + $(CC) $(shell libgcrypt-config --cflags) $(CFLAGS) -c $< + + # Libtomcrypt shim -tomcrypt_provider.so: tomcrypt.o c_utils.o +tomcrypt_provider.so: tomcrypt.o c_utils.o c_timing.o $(CC) $(LFLAGS) -o $@ $^ -L. -ltommath $(shell pkg-config --libs libtomcrypt) tomcrypt.o: tomcrypt.c @@ -81,7 +105,7 @@ tomcrypt.o: tomcrypt.c # Botan-2 shim -botan_provider.so: botan.o cpp_utils.o +botan_provider.so: botan.o cpp_utils.o c_timing.o $(CXX) $(LFLAGS) -o $@ $^ -L. $(shell pkg-config --libs botan-2) botan.o: botan.cpp @@ -89,15 +113,24 @@ botan.o: botan.cpp # Crypto++ shim -cryptopp_provider.so: cryptopp.o cpp_utils.o +cryptopp_provider.so: cryptopp.o cpp_utils.o c_timing.o $(CXX) $(LFLAGS) -o $@ $^ -L. $(shell pkg-config --libs libcrypto++) cryptopp.o: cryptopp.cpp $(CXX) $(shell pkg-config --cflags libcrypto++) $(CXXFLAGS) -c $< +help: + @echo "# This makefile builds the JNI shims necessary to test native libraries." + @echo "# Targets:" + @echo " - openssl_provider.so" + @echo " - boringssl_provider.so" + @echo " - gcrypt_provider.so" + @echo " - tomcrypt_provider.so" + @echo " - botan_provider.so" + @echo " - cryptopp_provider.so" clean: rm -rf *.o rm -rf *.so -.PHONY: all clean
\ No newline at end of file +.PHONY: all help clean
\ No newline at end of file diff --git a/src/cz/crcs/ectester/standalone/libs/jni/NativeECPrivateKey.java b/src/cz/crcs/ectester/standalone/libs/jni/NativeECPrivateKey.java index 4cd4a9d..6d8101f 100644 --- a/src/cz/crcs/ectester/standalone/libs/jni/NativeECPrivateKey.java +++ b/src/cz/crcs/ectester/standalone/libs/jni/NativeECPrivateKey.java @@ -85,6 +85,18 @@ public abstract class NativeECPrivateKey implements ECPrivateKey { } } + public static class Boringssl extends Raw { + public Boringssl(byte[] keyData, ECParameterSpec params) { + super(keyData, params); + } + } + + public static class Gcrypt extends Raw { + public Gcrypt(byte[] keyData, ECParameterSpec params) { + super(keyData, params); + } + } + public static class Mscng extends Raw { // 0 -> implicit (meta = curveName UTF16, header = full); // 1 -> explicit (meta = null, header = full); diff --git a/src/cz/crcs/ectester/standalone/libs/jni/NativeECPublicKey.java b/src/cz/crcs/ectester/standalone/libs/jni/NativeECPublicKey.java index 33dd3ef..dcb8b3f 100644 --- a/src/cz/crcs/ectester/standalone/libs/jni/NativeECPublicKey.java +++ b/src/cz/crcs/ectester/standalone/libs/jni/NativeECPublicKey.java @@ -86,6 +86,18 @@ public abstract class NativeECPublicKey implements ECPublicKey { } } + public static class Boringssl extends ANSIX962 { + public Boringssl(byte[] keyData, ECParameterSpec params) { + super(keyData, params); + } + } + + public static class Gcrypt extends ANSIX962 { + public Gcrypt(byte[] keyData, ECParameterSpec params) { + super(keyData, params); + } + } + public static class Mscng extends ANSIX962 { // 0 -> implicit (meta = curveName UTF16, header = full); // 1 -> explicit (meta = null, header = full); diff --git a/src/cz/crcs/ectester/standalone/libs/jni/NativeKeyAgreementSpi.java b/src/cz/crcs/ectester/standalone/libs/jni/NativeKeyAgreementSpi.java index fdbdccf..7b531f5 100644 --- a/src/cz/crcs/ectester/standalone/libs/jni/NativeKeyAgreementSpi.java +++ b/src/cz/crcs/ectester/standalone/libs/jni/NativeKeyAgreementSpi.java @@ -65,7 +65,7 @@ public abstract class NativeKeyAgreementSpi extends KeyAgreementSpi { @Override protected void engineInit(Key key, AlgorithmParameterSpec params, SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { if (!(params instanceof ECParameterSpec)) { - throw new InvalidAlgorithmParameterException(); + throw new InvalidAlgorithmParameterException(params.toString()); } engineInit(key, random); this.params = params; @@ -234,6 +234,47 @@ public abstract class NativeKeyAgreementSpi extends KeyAgreementSpi { } } + public abstract static class Boringssl extends SimpleKeyAgreementSpi { + private String type; + + public Boringssl(String type) { + this.type = type; + } + + @Override + native byte[] generateSecret(byte[] pubkey, byte[] privkey, ECParameterSpec params); + + @Override + native SecretKey generateSecret(byte[] pubkey, byte[] privkey, ECParameterSpec params, String algorithm); + } + + public static class BoringsslECDH extends Boringssl { + public BoringsslECDH() { + super("ECDH"); + } + } + + public abstract static class Gcrypt extends SimpleKeyAgreementSpi { + private String type; + + public Gcrypt(String type) { + this.type = type; + } + + @Override + native byte[] generateSecret(byte[] pubkey, byte[] privkey, ECParameterSpec params); + + @Override + native SecretKey generateSecret(byte[] pubkey, byte[] privkey, ECParameterSpec params, String algorithm); + } + + public static class GcryptECDH extends Gcrypt { + public GcryptECDH() { + super("ECDH"); + } + } + + public abstract static class Mscng extends ExtendedKeyAgreementSpi { private String type; diff --git a/src/cz/crcs/ectester/standalone/libs/jni/NativeKeyPairGeneratorSpi.java b/src/cz/crcs/ectester/standalone/libs/jni/NativeKeyPairGeneratorSpi.java index aa83479..78656fc 100644 --- a/src/cz/crcs/ectester/standalone/libs/jni/NativeKeyPairGeneratorSpi.java +++ b/src/cz/crcs/ectester/standalone/libs/jni/NativeKeyPairGeneratorSpi.java @@ -180,6 +180,42 @@ public abstract class NativeKeyPairGeneratorSpi extends KeyPairGeneratorSpi { native KeyPair generate(AlgorithmParameterSpec params, SecureRandom random); } + public static class Boringssl extends NativeKeyPairGeneratorSpi { + public Boringssl() { + initialize(256, new SecureRandom()); + } + + @Override + native boolean keysizeSupported(int keysize); + + @Override + native boolean paramsSupported(AlgorithmParameterSpec params); + + @Override + native KeyPair generate(int keysize, SecureRandom random); + + @Override + native KeyPair generate(AlgorithmParameterSpec params, SecureRandom random); + } + + public static class Gcrypt extends NativeKeyPairGeneratorSpi { + + public Gcrypt() { + } + + @Override + native boolean keysizeSupported(int keysize); + + @Override + native boolean paramsSupported(AlgorithmParameterSpec params); + + @Override + native KeyPair generate(int keysize, SecureRandom random); + + @Override + native KeyPair generate(AlgorithmParameterSpec params, SecureRandom random); + } + public static abstract class Mscng extends NativeKeyPairGeneratorSpi { private String type; diff --git a/src/cz/crcs/ectester/standalone/libs/jni/NativeProvider.java b/src/cz/crcs/ectester/standalone/libs/jni/NativeProvider.java index fef2930..99baa97 100644 --- a/src/cz/crcs/ectester/standalone/libs/jni/NativeProvider.java +++ b/src/cz/crcs/ectester/standalone/libs/jni/NativeProvider.java @@ -64,6 +64,26 @@ public abstract class NativeProvider extends Provider { native void setup(); } + public static class Boringssl extends NativeProvider { + + public Boringssl(String name, double version, String info) { + super(name, version, info); + } + + @Override + native void setup(); + } + + public static class Gcrypt extends NativeProvider { + + public Gcrypt(String name, double version, String info) { + super(name, version, info); + } + + @Override + native void setup(); + } + public static class Mscng extends NativeProvider { public Mscng(String name, double version, String info) { diff --git a/src/cz/crcs/ectester/standalone/libs/jni/NativeSignatureSpi.java b/src/cz/crcs/ectester/standalone/libs/jni/NativeSignatureSpi.java index 602b1c4..f2b1ab9 100644 --- a/src/cz/crcs/ectester/standalone/libs/jni/NativeSignatureSpi.java +++ b/src/cz/crcs/ectester/standalone/libs/jni/NativeSignatureSpi.java @@ -328,6 +328,118 @@ public abstract class NativeSignatureSpi extends SignatureSpi { } } + public abstract static class Boringssl extends SimpleSignatureSpi { + private String type; + + public Boringssl(String type) { + this.type = type; + } + + @Override + native byte[] sign(byte[] data, byte[] privkey, ECParameterSpec params); + + @Override + native boolean verify(byte[] signature, byte[] data, byte[] pubkey, ECParameterSpec params); + } + + public static class BoringsslECDSAwithNONE extends Boringssl { + + public BoringsslECDSAwithNONE() { + super("NONEwithECDSA"); + } + } + + public abstract static class Gcrypt extends SimpleSignatureSpi { + private String type; + + public Gcrypt(String type) { + this.type = type; + } + + @Override + native byte[] sign(byte[] data, byte[] privkey, ECParameterSpec params); + + @Override + native boolean verify(byte[] signature, byte[] data, byte[] pubkey, ECParameterSpec params); + } + + public static class GcryptECDSAwithNONE extends Gcrypt { + + public GcryptECDSAwithNONE() { + super("NONEwithECDSA"); + } + } + + public static class GcryptECDSAwithSHA1 extends Gcrypt { + + public GcryptECDSAwithSHA1() { + super("SHA1withECDSA"); + } + } + + public static class GcryptECDSAwithSHA224 extends Gcrypt { + + public GcryptECDSAwithSHA224() { + super("SHA224withECDSA"); + } + } + + public static class GcryptECDSAwithSHA256 extends Gcrypt { + + public GcryptECDSAwithSHA256() { + super("SHA256withECDSA"); + } + } + + public static class GcryptECDSAwithSHA384 extends Gcrypt { + + public GcryptECDSAwithSHA384() { + super("SHA384withECDSA"); + } + } + + public static class GcryptECDSAwithSHA512 extends Gcrypt { + + public GcryptECDSAwithSHA512() { + super("SHA512withECDSA"); + } + } + + public static class GcryptECDDSAwithSHA1 extends Gcrypt { + + public GcryptECDDSAwithSHA1() { + super("SHA1withECDDSA"); + } + } + + public static class GcryptECDDSAwithSHA224 extends Gcrypt { + + public GcryptECDDSAwithSHA224() { + super("SHA224withECDDSA"); + } + } + + public static class GcryptECDDSAwithSHA256 extends Gcrypt { + + public GcryptECDDSAwithSHA256() { + super("SHA256withECDDSA"); + } + } + + public static class GcryptECDDSAwithSHA384 extends Gcrypt { + + public GcryptECDDSAwithSHA384() { + super("SHA384withECDDSA"); + } + } + + public static class GcryptECDDSAwithSHA512 extends Gcrypt { + + public GcryptECDDSAwithSHA512() { + super("SHA512withECDDSA"); + } + } + public abstract static class Mscng extends ExtendedSignatureSpi { private String type; diff --git a/src/cz/crcs/ectester/standalone/libs/jni/boringssl.c b/src/cz/crcs/ectester/standalone/libs/jni/boringssl.c new file mode 100644 index 0000000..0484d28 --- /dev/null +++ b/src/cz/crcs/ectester/standalone/libs/jni/boringssl.c @@ -0,0 +1,528 @@ +#include "native.h" +#include <string.h> + +#include <openssl/conf.h> +#include <openssl/opensslv.h> +#include <openssl/objects.h> +#include <openssl/obj_mac.h> +#include <openssl/bn.h> +#include <openssl/evp.h> +#include <openssl/err.h> +#include <openssl/ec.h> +#include <openssl/ecdh.h> +#include <openssl/ecdsa.h> + +#include "c_utils.h" +#include "c_timing.h" + + +static jclass provider_class; + +JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_BoringsslLib_createProvider(JNIEnv *env, jobject self) { + /* Create the custom provider. */ + jclass local_provider_class = (*env)->FindClass(env, "cz/crcs/ectester/standalone/libs/jni/NativeProvider$Boringssl"); + provider_class = (*env)->NewGlobalRef(env, local_provider_class); + + jmethodID init = (*env)->GetMethodID(env, local_provider_class, "<init>", "(Ljava/lang/String;DLjava/lang/String;)V"); + + jstring name = (*env)->NewStringUTF(env, OPENSSL_VERSION_TEXT); + long ver_hi = (OPENSSL_VERSION_NUMBER & 0xff000000L) >> 28; + long ver_mid = (OPENSSL_VERSION_NUMBER & 0xff0000L) >> 20; + long ver_low = (OPENSSL_VERSION_NUMBER & 0xff00L) >> 12; + double version = (double)ver_hi + ((double)ver_mid/10) + ((double)ver_low/100); + + return (*env)->NewObject(env, provider_class, init, name, version, name); +} + +JNIEXPORT void JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeProvider_00024Boringssl_setup(JNIEnv *env, jobject self) { + ERR_load_crypto_strings(); + CRYPTO_library_init(); + + INIT_PROVIDER(env, provider_class); + + ADD_KPG(env, self, "EC", "Boringssl"); + ADD_KA(env, self, "ECDH", "BoringsslECDH"); + ADD_SIG(env, self, "NONEwithECDSA", "BoringsslECDSAwithNONE"); + + init_classes(env, "Boringssl"); +} + +JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_BoringsslLib_getCurves(JNIEnv *env, jobject self) { + jclass hash_set_class = (*env)->FindClass(env, "java/util/TreeSet"); + + jmethodID hash_set_ctr = (*env)->GetMethodID(env, hash_set_class, "<init>", "()V"); + jmethodID hash_set_add = (*env)->GetMethodID(env, hash_set_class, "add", "(Ljava/lang/Object;)Z"); + + jobject result = (*env)->NewObject(env, hash_set_class, hash_set_ctr); + + size_t ncurves = EC_get_builtin_curves(NULL, 0); + EC_builtin_curve curves[ncurves]; + EC_get_builtin_curves(curves, ncurves); + + for (size_t i = 0; i < ncurves; ++i) { + jstring curve_name = (*env)->NewStringUTF(env, OBJ_nid2sn(curves[i].nid)); + (*env)->CallBooleanMethod(env, result, hash_set_add, curve_name); + } + + return result; +} + +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_00024Boringssl_keysizeSupported(JNIEnv *env, jobject self, jint keysize) { + size_t ncurves = EC_get_builtin_curves(NULL, 0); + EC_builtin_curve curves[ncurves]; + EC_get_builtin_curves(curves, ncurves); + + for (size_t i = 0; i < ncurves; ++i) { + EC_GROUP *curve = EC_GROUP_new_by_curve_name(curves[i].nid); + if (EC_GROUP_get_degree(curve) == keysize) { + EC_GROUP_free(curve); + return JNI_TRUE; + } + EC_GROUP_free(curve); + } + return JNI_FALSE; +} + +static jobject bignum_to_biginteger(JNIEnv *env, const BIGNUM *bn) { + jmethodID biginteger_init = (*env)->GetMethodID(env, biginteger_class, "<init>", "(I[B)V"); + int size = BN_num_bytes(bn); + jbyteArray bytes = (*env)->NewByteArray(env, size); + jbyte *data = (*env)->GetByteArrayElements(env, bytes, NULL); + BN_bn2bin(bn, (unsigned char *) data); + (*env)->ReleaseByteArrayElements(env, bytes, data, 0); + jobject result = (*env)->NewObject(env, biginteger_class, biginteger_init, 1, bytes); + return result; +} + +static BIGNUM *biginteger_to_bignum(JNIEnv *env, jobject bigint) { + jmethodID to_byte_array = (*env)->GetMethodID(env, biginteger_class, "toByteArray", "()[B"); + + jbyteArray byte_array = (jbyteArray) (*env)->CallObjectMethod(env, bigint, to_byte_array); + jsize byte_length = (*env)->GetArrayLength(env, byte_array); + jbyte *byte_data = (*env)->GetByteArrayElements(env, byte_array, NULL); + BIGNUM *result = BN_bin2bn((unsigned char *) byte_data, byte_length, NULL); + (*env)->ReleaseByteArrayElements(env, byte_array, byte_data, JNI_ABORT); + return result; +} + +static EC_GROUP *create_curve(JNIEnv *env, jobject params) { + jmethodID get_curve = (*env)->GetMethodID(env, ec_parameter_spec_class, "getCurve", "()Ljava/security/spec/EllipticCurve;"); + jobject elliptic_curve = (*env)->CallObjectMethod(env, params, get_curve); + + jmethodID get_field = (*env)->GetMethodID(env, elliptic_curve_class, "getField", "()Ljava/security/spec/ECField;"); + jobject field = (*env)->CallObjectMethod(env, elliptic_curve, get_field); + + if ((*env)->IsInstanceOf(env, field, f2m_field_class)) { + return NULL; + } + + jmethodID get_a = (*env)->GetMethodID(env, elliptic_curve_class, "getA", "()Ljava/math/BigInteger;"); + jobject a = (*env)->CallObjectMethod(env, elliptic_curve, get_a); + BIGNUM *a_bn = biginteger_to_bignum(env, a); + + jmethodID get_b = (*env)->GetMethodID(env, elliptic_curve_class, "getB", "()Ljava/math/BigInteger;"); + jobject b = (*env)->CallObjectMethod(env, elliptic_curve, get_b); + BIGNUM *b_bn = biginteger_to_bignum(env, b); + + jmethodID get_g = (*env)->GetMethodID(env, ec_parameter_spec_class, "getGenerator", "()Ljava/security/spec/ECPoint;"); + jobject g = (*env)->CallObjectMethod(env, params, get_g); + + jmethodID get_x = (*env)->GetMethodID(env, point_class, "getAffineX", "()Ljava/math/BigInteger;"); + jobject gx = (*env)->CallObjectMethod(env, g, get_x); + BIGNUM *gx_bn = biginteger_to_bignum(env, gx); + + jmethodID get_y = (*env)->GetMethodID(env, point_class, "getAffineY", "()Ljava/math/BigInteger;"); + jobject gy = (*env)->CallObjectMethod(env, g, get_y); + BIGNUM *gy_bn = biginteger_to_bignum(env, gy); + + EC_GROUP *result; + EC_POINT *g_point; + + jmethodID get_p = (*env)->GetMethodID(env, fp_field_class, "getP", "()Ljava/math/BigInteger;"); + jobject p = (*env)->CallObjectMethod(env, field, get_p); + + BIGNUM *p_bn = biginteger_to_bignum(env, p); + result = EC_GROUP_new_curve_GFp(p_bn, a_bn, b_bn, NULL); + BN_free(p_bn); + BN_free(a_bn); + BN_free(b_bn); + + if (!result) { + throw_new(env, "java/security/InvalidAlgorithmParameterException", "Error creating EC_GROUP, EC_GROUP_new_curve_GFp."); + BN_free(gx_bn); BN_free(gy_bn); + return NULL; + } + + g_point = EC_POINT_new(result); + if(!EC_POINT_set_affine_coordinates_GFp(result, g_point, gx_bn, gy_bn, NULL)) { + throw_new(env, "java/security/InvalidAlgorithmParameterException", "Error creating EC_GROUP, EC_POINT_set_affine_coordinates_GFp."); + BN_free(gx_bn); BN_free(gy_bn); EC_POINT_free(g_point); EC_GROUP_free(result); + return NULL; + } + + jmethodID get_n = (*env)->GetMethodID(env, ec_parameter_spec_class, "getOrder", "()Ljava/math/BigInteger;"); + jobject n = (*env)->CallObjectMethod(env, params, get_n); + BIGNUM *n_bn = biginteger_to_bignum(env, n); + + jmethodID get_h = (*env)->GetMethodID(env, ec_parameter_spec_class, "getCofactor", "()I"); + jint h = (*env)->CallIntMethod(env, params, get_h); + BIGNUM *h_bn = BN_new(); + BN_set_word(h_bn, h); + + if (!EC_GROUP_set_generator(result, g_point, n_bn, h_bn)) { + throw_new(env, "java/security/InvalidAlgorithmParameterException", "Error creating EC_GROUP, EC_GROUP_set_generator."); + BN_free(n_bn); BN_free(h_bn); BN_free(gx_bn); BN_free(gy_bn); EC_POINT_free(g_point); EC_GROUP_free(result); + return NULL; + } + + EC_POINT_free(g_point); + BN_free(gx_bn); + BN_free(gy_bn); + BN_free(n_bn); + BN_free(h_bn); + + return result; +} + +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_00024Boringssl_paramsSupported(JNIEnv *env, jobject self, jobject params){ + if (params == NULL) { + return JNI_FALSE; + } + + if ((*env)->IsInstanceOf(env, params, ec_parameter_spec_class)) { + EC_GROUP *curve = create_curve(env, params); + jboolean result = !curve; + EC_GROUP_free(curve); + return result; + } else if ((*env)->IsInstanceOf(env, params, ecgen_parameter_spec_class)) { + jmethodID get_name = (*env)->GetMethodID(env, ecgen_parameter_spec_class, "getName", "()Ljava/lang/String;"); + jstring name = (*env)->CallObjectMethod(env, params, get_name); + const char *utf_name = (*env)->GetStringUTFChars(env, name, NULL); + size_t ncurves = EC_get_builtin_curves(NULL, 0); + EC_builtin_curve curves[ncurves]; + EC_get_builtin_curves(curves, ncurves); + for (size_t i = 0; i < ncurves; ++i) { + if (strcasecmp(utf_name, OBJ_nid2sn(curves[i].nid)) == 0) { + (*env)->ReleaseStringUTFChars(env, name, utf_name); + return JNI_TRUE; + } + } + (*env)->ReleaseStringUTFChars(env, name, utf_name); + return JNI_FALSE; + } else { + return JNI_FALSE; + } +} + +static jobject create_ec_param_spec(JNIEnv *env, const EC_GROUP *curve) { + BIGNUM *a; + BIGNUM *b; + + BIGNUM *gx; + BIGNUM *gy; + jobject field; + + BIGNUM *p = BN_new(); + a = BN_new(); + b = BN_new(); + if (!EC_GROUP_get_curve_GFp(curve, p, a, b, NULL)) { + throw_new(env, "java/security/InvalidAlgorithmParameterException", "Error creating ECParameterSpec, EC_GROUP_get_curve_GFp."); + BN_free(p); BN_free(a); BN_free(b); + return NULL; + } + + jobject p_int = bignum_to_biginteger(env, p); + + jmethodID fp_field_init = (*env)->GetMethodID(env, fp_field_class, "<init>", "(Ljava/math/BigInteger;)V"); + field = (*env)->NewObject(env, fp_field_class, fp_field_init, p_int); + + BN_free(p); + + gx = BN_new(); + gy = BN_new(); + if (!EC_POINT_get_affine_coordinates_GFp(curve, EC_GROUP_get0_generator(curve), gx, gy, NULL)) { + throw_new(env, "java/security/InvalidAlgorithmParameterException", "Error creating ECParameterSpec, EC_POINT_get_affine_coordinates_GFp."); + BN_free(a); BN_free(b); BN_free(gx); BN_free(gy); + return NULL; + } + + jobject a_int = bignum_to_biginteger(env, a); + jobject b_int = bignum_to_biginteger(env, b); + + jmethodID elliptic_curve_init = (*env)->GetMethodID(env, elliptic_curve_class, "<init>", "(Ljava/security/spec/ECField;Ljava/math/BigInteger;Ljava/math/BigInteger;)V"); + jobject elliptic_curve = (*env)->NewObject(env, elliptic_curve_class, elliptic_curve_init, field, a_int, b_int); + + BN_free(a); + BN_free(b); + + jobject gx_int = bignum_to_biginteger(env, gx); + jobject gy_int = bignum_to_biginteger(env, gy); + + BN_free(gx); + BN_free(gy); + + jmethodID point_init = (*env)->GetMethodID(env, point_class, "<init>", "(Ljava/math/BigInteger;Ljava/math/BigInteger;)V"); + jobject g = (*env)->NewObject(env, point_class, point_init, gx_int, gy_int); + + jobject order = bignum_to_biginteger(env, EC_GROUP_get0_order(curve)); + BIGNUM *h = BN_new(); + EC_GROUP_get_cofactor(curve, h, NULL); + jint cofactor = BN_get_word(h); + BN_free(h); + + jmethodID ec_parameter_spec_init = (*env)->GetMethodID(env, ec_parameter_spec_class, "<init>", "(Ljava/security/spec/EllipticCurve;Ljava/security/spec/ECPoint;Ljava/math/BigInteger;I)V"); + return (*env)->NewObject(env, ec_parameter_spec_class, ec_parameter_spec_init, elliptic_curve, g, order, cofactor); +} + +static jobject generate_from_curve(JNIEnv *env, const EC_GROUP *curve) { + jint keysize = EC_GROUP_get_degree(curve); + unsigned long key_bytes = (keysize + 7) / 8; + + EC_KEY *key = EC_KEY_new(); + EC_KEY_set_group(key, curve); + + native_timing_start(); + int err = EC_KEY_generate_key(key); + native_timing_stop(); + + if (!err) { + throw_new(env, "java/security/GeneralSecurityException", "Error generating key, EC_KEY_generate_key."); + EC_KEY_free(key); + return NULL; + } + + jbyteArray priv_bytes = (*env)->NewByteArray(env, key_bytes); + jbyte *key_priv = (*env)->GetByteArrayElements(env, priv_bytes, NULL); + BN_bn2bin_padded((unsigned char *) key_priv, key_bytes, EC_KEY_get0_private_key(key)); + (*env)->ReleaseByteArrayElements(env, priv_bytes, key_priv, 0); + + unsigned long key_len = 2*key_bytes + 1; + jbyteArray pub_bytes = (*env)->NewByteArray(env, key_len); + jbyte *key_pub = (*env)->GetByteArrayElements(env, pub_bytes, NULL); + EC_POINT_point2oct(curve, EC_KEY_get0_public_key(key), POINT_CONVERSION_UNCOMPRESSED, (unsigned char *) key_pub, key_len, NULL); + (*env)->ReleaseByteArrayElements(env, pub_bytes, key_pub, 0); + + EC_KEY_free(key); + + jobject ec_param_spec = create_ec_param_spec(env, curve); + + jobject ec_pub_param_spec = (*env)->NewLocalRef(env, ec_param_spec); + jmethodID ec_pub_init = (*env)->GetMethodID(env, pubkey_class, "<init>", "([BLjava/security/spec/ECParameterSpec;)V"); + jobject pubkey = (*env)->NewObject(env, pubkey_class, ec_pub_init, pub_bytes, ec_pub_param_spec); + + jobject ec_priv_param_spec = (*env)->NewLocalRef(env, ec_param_spec); + jmethodID ec_priv_init = (*env)->GetMethodID(env, privkey_class, "<init>", "([BLjava/security/spec/ECParameterSpec;)V"); + jobject privkey = (*env)->NewObject(env, privkey_class, ec_priv_init, priv_bytes, ec_priv_param_spec); + + jmethodID keypair_init = (*env)->GetMethodID(env, keypair_class, "<init>", "(Ljava/security/PublicKey;Ljava/security/PrivateKey;)V"); + return (*env)->NewObject(env, keypair_class, keypair_init, pubkey, privkey); +} + +JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_00024Boringssl_generate__ILjava_security_SecureRandom_2(JNIEnv *env, jobject self, jint keysize, jobject random) { + size_t ncurves = EC_get_builtin_curves(NULL, 0); + EC_builtin_curve curves[ncurves]; + EC_get_builtin_curves(curves, ncurves); + + EC_GROUP *curve = NULL; + for (size_t i = 0; i < ncurves; ++i) { + curve = EC_GROUP_new_by_curve_name(curves[i].nid); + if (EC_GROUP_get_degree(curve) == keysize) { + break; + } + EC_GROUP_free(curve); + } + + if (!curve) { + throw_new(env, "java/security/InvalidAlgorithmParameterException", "Curve for given bitsize not found."); + return NULL; + } + + jobject result = generate_from_curve(env, curve); + EC_GROUP_free(curve); + return result; +} + +JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_00024Boringssl_generate__Ljava_security_spec_AlgorithmParameterSpec_2Ljava_security_SecureRandom_2(JNIEnv *env, jobject self, jobject params, jobject random) { + if ((*env)->IsInstanceOf(env, params, ec_parameter_spec_class)) { + EC_GROUP *curve = create_curve(env, params); + jobject result = generate_from_curve(env, curve); + EC_GROUP_free(curve); + return result; + } else if ((*env)->IsInstanceOf(env, params, ecgen_parameter_spec_class)) { + jmethodID get_name = (*env)->GetMethodID(env, ecgen_parameter_spec_class, "getName", "()Ljava/lang/String;"); + jstring name = (*env)->CallObjectMethod(env, params, get_name); + const char* utf_name = (*env)->GetStringUTFChars(env, name, NULL); + size_t ncurves = EC_get_builtin_curves(NULL, 0); + EC_builtin_curve curves[ncurves]; + EC_get_builtin_curves(curves, ncurves); + EC_GROUP *curve = NULL; + for (size_t i = 0; i < ncurves; ++i) { + if (strcasecmp(utf_name, OBJ_nid2sn(curves[i].nid)) == 0) { + curve = EC_GROUP_new_by_curve_name(curves[i].nid); + break; + } + } + (*env)->ReleaseStringUTFChars(env, name, utf_name); + if (!curve) { + throw_new(env, "java/security/InvalidAlgorithmParameterException", "Curve for given bitsize not found."); + return NULL; + } + jobject result = generate_from_curve(env, curve); + EC_GROUP_free(curve); + return result; + } else { + throw_new(env, "java/security/InvalidAlgorithmParameterException", "Curve not found."); + return NULL; + } +} + +EC_KEY *barray_to_pubkey(JNIEnv *env, const EC_GROUP *curve, jbyteArray pub) { + EC_KEY *result = EC_KEY_new(); + EC_KEY_set_group(result, curve); + jsize pub_len = (*env)->GetArrayLength(env, pub); + jbyte *pub_data = (*env)->GetByteArrayElements(env, pub, NULL); + EC_POINT *pub_point = EC_POINT_new(curve); + EC_POINT_oct2point(curve, pub_point, (unsigned char *) pub_data, pub_len, NULL); + (*env)->ReleaseByteArrayElements(env, pub, pub_data, JNI_ABORT); + EC_KEY_set_public_key(result, pub_point); + EC_POINT_free(pub_point); + return result; +} + +EC_KEY *barray_to_privkey(JNIEnv *env, const EC_GROUP *curve, jbyteArray priv) { + EC_KEY *result = EC_KEY_new(); + EC_KEY_set_group(result, curve); + jsize priv_len = (*env)->GetArrayLength(env, priv); + jbyte *priv_data = (*env)->GetByteArrayElements(env, priv, NULL); + BIGNUM *s = BN_bin2bn((unsigned char *) priv_data, priv_len, NULL); + (*env)->ReleaseByteArrayElements(env, priv, priv_data, JNI_ABORT); + EC_KEY_set_private_key(result, s); + BN_free(s); + return result; +} + +JNIEXPORT jbyteArray JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyAgreementSpi_00024Boringssl_generateSecret___3B_3BLjava_security_spec_ECParameterSpec_2(JNIEnv *env, jobject self, jbyteArray pubkey, jbyteArray privkey, jobject params) { + EC_GROUP *curve = create_curve(env, params); + if (!curve) { + throw_new(env, "java/security/InvalidAlgorithmParameterException", "Curve not found."); + return NULL; + } + + EC_KEY *pub = barray_to_pubkey(env, curve, pubkey); + EC_KEY *priv = barray_to_privkey(env, curve, privkey); + + int field_size = EC_GROUP_get_degree(curve); + size_t secret_len = (field_size + 7)/8; + + //TODO: Do more KeyAgreements here, but will have to do the hash-fun manually, + // probably using the ECDH_KDF_X9_62 by wrapping it and dynamically choosing the EVP_MD. from the type string. + jbyteArray result = (*env)->NewByteArray(env, secret_len); + jbyte *result_data = (*env)->GetByteArrayElements(env, result, NULL); + + native_timing_start(); + int err = ECDH_compute_key(result_data, secret_len, EC_KEY_get0_public_key(pub), priv, NULL); + native_timing_stop(); + + if (err <= 0) { + throw_new(env, "java/security/GeneralSecurityException", "Error computing ECDH, ECDH_compute_key."); + EC_KEY_free(pub); EC_KEY_free(priv); EC_GROUP_free(curve); + (*env)->ReleaseByteArrayElements(env, result, result_data, JNI_ABORT); + return NULL; + } + (*env)->ReleaseByteArrayElements(env, result, result_data, 0); + + EC_KEY_free(pub); + EC_KEY_free(priv); + EC_GROUP_free(curve); + return result; +} + +JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyAgreementSpi_00024Boringssl_generateSecret___3B_3BLjava_security_spec_ECParameterSpec_2Ljava_lang_String_2(JNIEnv *env, jobject self, jbyteArray pubkey, jbyteArray privkey, jobject params, jstring algorithm) { + throw_new(env, "java/lang/UnsupportedOperationException", "Not supported."); + return NULL; +} + +JNIEXPORT jbyteArray JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSignatureSpi_00024Boringssl_sign(JNIEnv *env, jobject self, jbyteArray data, jbyteArray privkey, jobject params) { + EC_GROUP *curve = create_curve(env, params); + if (!curve) { + throw_new(env, "java/security/InvalidAlgorithmParameterException", "Curve not found."); + return NULL; + } + + EC_KEY *priv = barray_to_privkey(env, curve, privkey); + + jsize data_size = (*env)->GetArrayLength(env, data); + jbyte *data_data = (*env)->GetByteArrayElements(env, data, NULL); + // TODO: Do more Signatures here, maybe use the EVP interface to get to the hashes easier and not hash manually? + + native_timing_start(); + ECDSA_SIG *signature = ECDSA_do_sign((unsigned char *) data_data, data_size, priv); + native_timing_stop(); + + (*env)->ReleaseByteArrayElements(env, data, data_data, JNI_ABORT); + if (!signature) { + throw_new(env, "java/security/GeneralSecurityException", "Error signing, ECDSA_do_sign."); + EC_KEY_free(priv); EC_GROUP_free(curve); + return NULL; + } + + jsize sig_len = i2d_ECDSA_SIG(signature, NULL); + jbyteArray result = (*env)->NewByteArray(env, sig_len); + jbyte *result_data = (*env)->GetByteArrayElements(env, result, NULL); + jbyte *result_data_ptr = result_data; + i2d_ECDSA_SIG(signature, (unsigned char **)&result_data_ptr); + (*env)->ReleaseByteArrayElements(env, result, result_data, 0); + + ECDSA_SIG_free(signature); + EC_KEY_free(priv); + EC_GROUP_free(curve); + return result; +} + +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSignatureSpi_00024Boringssl_verify(JNIEnv *env, jobject self, jbyteArray signature, jbyteArray data, jbyteArray pubkey, jobject params) { + EC_GROUP *curve = create_curve(env, params); + if (!curve) { + throw_new(env, "java/security/InvalidAlgorithmParameterException", "Curve not found."); + return JNI_FALSE; + } + + EC_KEY *pub = barray_to_pubkey(env, curve, pubkey); + + jsize sig_len = (*env)->GetArrayLength(env, signature); + jbyte *sig_data = (*env)->GetByteArrayElements(env, signature, NULL); + jbyte *sig_data_ptr = sig_data; + ECDSA_SIG *sig_obj = d2i_ECDSA_SIG(NULL, (const unsigned char **)&sig_data_ptr, sig_len); + (*env)->ReleaseByteArrayElements(env, signature, sig_data, JNI_ABORT); + + jsize data_size = (*env)->GetArrayLength(env, data); + jbyte *data_data = (*env)->GetByteArrayElements(env, data, NULL); + + native_timing_start(); + int result = ECDSA_do_verify((unsigned char *) data_data, data_size, sig_obj, pub); + native_timing_stop(); + + if (result < 0) { + throw_new(env, "java/security/GeneralSecurityException", "Error verifying, ECDSA_do_verify."); + EC_KEY_free(pub); EC_GROUP_free(curve); ECDSA_SIG_free(sig_obj); + (*env)->ReleaseByteArrayElements(env, data, data_data, JNI_ABORT); + return JNI_FALSE; + } + (*env)->ReleaseByteArrayElements(env, data, data_data, JNI_ABORT); + + ECDSA_SIG_free(sig_obj); + EC_KEY_free(pub); + EC_GROUP_free(curve); + return (result == 1) ? JNI_TRUE : JNI_FALSE; +} + +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_BoringsslLib_supportsNativeTiming(JNIEnv *env, jobject this) { + return native_timing_supported(); +} + +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_BoringsslLib_getNativeTimingResolution(JNIEnv *env, jobject this) { + return native_timing_resolution(); +} + +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_BoringsslLib_getLastNativeTiming(JNIEnv *env, jobject this) { + return native_timing_last(); +}
\ No newline at end of file diff --git a/src/cz/crcs/ectester/standalone/libs/jni/botan.cpp b/src/cz/crcs/ectester/standalone/libs/jni/botan.cpp index 207532d..813b9f8 100644 --- a/src/cz/crcs/ectester/standalone/libs/jni/botan.cpp +++ b/src/cz/crcs/ectester/standalone/libs/jni/botan.cpp @@ -17,8 +17,10 @@ #include <botan/ecdh.h> #include <botan/pubkey.h> #include "cpp_utils.hpp" +#include "c_timing.h" static jclass provider_class; +static Botan::AutoSeeded_RNG rng; JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_BotanLib_createProvider(JNIEnv *env, jobject self) { /* Create the custom provider. */ @@ -145,7 +147,7 @@ static Botan::BigInt bigint_from_biginteger(JNIEnv *env, jobject biginteger) { jbyteArray byte_array = (jbyteArray) env->CallObjectMethod(biginteger, to_byte_array); jsize byte_length = env->GetArrayLength(byte_array); jbyte *byte_data = env->GetByteArrayElements(byte_array, NULL); - Botan::BigInt result((unsigned uint8_t*) byte_data, byte_length); + Botan::BigInt result((unsigned char *) byte_data, byte_length); env->ReleaseByteArrayElements(byte_array, byte_data, JNI_ABORT); return result; } @@ -157,10 +159,6 @@ static Botan::EC_Group group_from_params(JNIEnv *env, jobject params) { jmethodID get_field = env->GetMethodID(elliptic_curve_class, "getField", "()Ljava/security/spec/ECField;"); jobject field = env->CallObjectMethod(elliptic_curve, get_field); - - jmethodID get_bits = env->GetMethodID(fp_field_class, "getFieldSize", "()I"); - jint bits = env->CallIntMethod(field, get_bits); - jint bytes = (bits + 7) / 8; jmethodID get_a = env->GetMethodID(elliptic_curve_class, "getA", "()Ljava/math/BigInteger;"); jobject a = env->CallObjectMethod(elliptic_curve, get_a); @@ -238,8 +236,6 @@ static jobject params_from_group(JNIEnv *env, Botan::EC_Group group) { } static jobject generate_from_group(JNIEnv* env, jobject self, Botan::EC_Group group) { - Botan::AutoSeeded_RNG rng; - jclass botan_kpg_class = env->FindClass("cz/crcs/ectester/standalone/libs/jni/NativeKeyPairGeneratorSpi$Botan"); jfieldID type_id = env->GetFieldID(botan_kpg_class, "type", "Ljava/lang/String;"); jstring type = (jstring) env->GetObjectField(self, type_id); @@ -249,6 +245,7 @@ static jobject generate_from_group(JNIEnv* env, jobject self, Botan::EC_Group gr std::unique_ptr<Botan::EC_PrivateKey> skey; try { + native_timing_start(); if (type_str == "ECDH") { skey = std::make_unique<Botan::ECDH_PrivateKey>(rng, group); } else if (type_str == "ECDSA") { @@ -258,6 +255,7 @@ static jobject generate_from_group(JNIEnv* env, jobject self, Botan::EC_Group gr } else if (type_str == "ECGDSA") { skey = std::make_unique<Botan::ECGDSA_PrivateKey>(rng, group); } + native_timing_stop(); } catch (Botan::Exception & ex) { throw_new(env, "java/security/GeneralSecurityException", ex.what()); return NULL; @@ -299,7 +297,7 @@ JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPai for (auto it = curves.begin(); it != curves.end(); ++it) { Botan::EC_Group curve_group = Botan::EC_Group(*it); size_t curve_size = curve_group.get_p_bits(); - if (curve_size == keysize) { + if (curve_size == (size_t) keysize) { //generate on this group. Even thou no default groups are present... return generate_from_group(env, self, curve_group); } @@ -349,11 +347,9 @@ jbyteArray generate_secret(JNIEnv *env, jobject self, jbyteArray pubkey, jbyteAr jsize privkey_length = env->GetArrayLength(privkey); jbyte *privkey_data = env->GetByteArrayElements(privkey, NULL); - Botan::BigInt privkey_scalar((unsigned uint8_t*) privkey_data, privkey_length); + Botan::BigInt privkey_scalar((unsigned char *) privkey_data, privkey_length); env->ReleaseByteArrayElements(privkey, privkey_data, JNI_ABORT); - Botan::AutoSeeded_RNG rng; - Botan::ECDH_PrivateKey skey(rng, curve_group, privkey_scalar); jsize pubkey_length = env->GetArrayLength(pubkey); @@ -378,7 +374,9 @@ jbyteArray generate_secret(JNIEnv *env, jobject self, jbyteArray pubkey, jbyteAr std::vector<uint8_t> derived; try { + native_timing_start(); derived = Botan::unlock(ka.derive_key(key_len, pkey.public_value()).bits_of()); + native_timing_stop(); } catch (Botan::Exception & ex) { throw_new(env, "java/security/GeneralSecurityException", ex.what()); return NULL; @@ -419,8 +417,6 @@ JNIEXPORT jbyteArray JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSig Botan::BigInt privkey_scalar((uint8_t*) privkey_bytes, privkey_length); env->ReleaseByteArrayElements(privkey, privkey_bytes, JNI_ABORT); - Botan::AutoSeeded_RNG rng; - std::unique_ptr<Botan::EC_PrivateKey> skey; if (type_str.find("ECDSA") != std::string::npos) { skey = std::make_unique<Botan::ECDSA_PrivateKey>(rng, curve_group, privkey_scalar); @@ -430,28 +426,30 @@ JNIEXPORT jbyteArray JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSig skey = std::make_unique<Botan::ECGDSA_PrivateKey>(rng, curve_group, privkey_scalar); } - std::string kdf; + std::string emsa; if (type_str.find("NONE") != std::string::npos) { - kdf = "Raw"; + emsa = "Raw"; } else if (type_str.find("SHA1") != std::string::npos) { - kdf = "EMSA1(SHA-1)"; + emsa = "EMSA1(SHA-1)"; } else if (type_str.find("SHA224") != std::string::npos) { - kdf = "EMSA1(SHA-224)"; + emsa = "EMSA1(SHA-224)"; } else if (type_str.find("SHA256") != std::string::npos) { - kdf = "EMSA1(SHA-256)"; + emsa = "EMSA1(SHA-256)"; } else if (type_str.find("SHA384") != std::string::npos) { - kdf = "EMSA1(SHA-384)"; + emsa = "EMSA1(SHA-384)"; } else if (type_str.find("SHA512") != std::string::npos) { - kdf = "EMSA1(SHA-512)"; + emsa = "EMSA1(SHA-512)"; } - Botan::PK_Signer signer(*skey, rng, kdf, Botan::DER_SEQUENCE); + Botan::PK_Signer signer(*skey, rng, emsa, Botan::DER_SEQUENCE); jsize data_length = env->GetArrayLength(data); jbyte *data_bytes = env->GetByteArrayElements(data, NULL); std::vector<uint8_t> sig; try { + native_timing_start(); sig = signer.sign_message((uint8_t*) data_bytes, data_length, rng); + native_timing_stop(); } catch (Botan::Exception & ex) { throw_new(env, "java/security/GeneralSecurityException", ex.what()); env->ReleaseByteArrayElements(data, data_bytes, JNI_ABORT); @@ -491,22 +489,22 @@ JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSigna pkey = std::make_unique<Botan::ECGDSA_PublicKey>(curve_group, public_point); } - std::string kdf; + std::string emsa; if (type_str.find("NONE") != std::string::npos) { - kdf = "Raw"; + emsa = "Raw"; } else if (type_str.find("SHA1") != std::string::npos) { - kdf = "EMSA1(SHA-1)"; + emsa = "EMSA1(SHA-1)"; } else if (type_str.find("SHA224") != std::string::npos) { - kdf = "EMSA1(SHA-224)"; + emsa = "EMSA1(SHA-224)"; } else if (type_str.find("SHA256") != std::string::npos) { - kdf = "EMSA1(SHA-256)"; + emsa = "EMSA1(SHA-256)"; } else if (type_str.find("SHA384") != std::string::npos) { - kdf = "EMSA1(SHA-384)"; + emsa = "EMSA1(SHA-384)"; } else if (type_str.find("SHA512") != std::string::npos) { - kdf = "EMSA1(SHA-512)"; + emsa = "EMSA1(SHA-512)"; } - Botan::PK_Verifier verifier(*pkey, kdf, Botan::DER_SEQUENCE); + Botan::PK_Verifier verifier(*pkey, emsa, Botan::DER_SEQUENCE); jsize data_length = env->GetArrayLength(data); jsize sig_length = env->GetArrayLength(signature); @@ -515,7 +513,9 @@ JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSigna bool result; try { + native_timing_start(); result = verifier.verify_message((uint8_t*)data_bytes, data_length, (uint8_t*)sig_bytes, sig_length); + native_timing_stop(); } catch (Botan::Exception & ex) { throw_new(env, "java/security/GeneralSecurityException", ex.what()); env->ReleaseByteArrayElements(data, data_bytes, JNI_ABORT); @@ -528,4 +528,16 @@ JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSigna return JNI_TRUE; } return JNI_FALSE; +} + +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_BotanLib_supportsNativeTiming(JNIEnv *env, jobject self) { + return native_timing_supported(); +} + +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_BotanLib_getNativeTimingResolution(JNIEnv *env, jobject self) { + return native_timing_resolution(); +} + +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_BotanLib_getLastNativeTiming(JNIEnv *env, jobject self) { + return native_timing_last(); }
\ No newline at end of file diff --git a/src/cz/crcs/ectester/standalone/libs/jni/c_timing.c b/src/cz/crcs/ectester/standalone/libs/jni/c_timing.c new file mode 100644 index 0000000..be46398 --- /dev/null +++ b/src/cz/crcs/ectester/standalone/libs/jni/c_timing.c @@ -0,0 +1,57 @@ +#include "c_timing.h" +#include <time.h> + +#if _POSIX_TIMERS > 0 + +struct timespec start = {0}; +struct timespec end = {0}; + +jboolean native_timing_supported() { + return JNI_TRUE; +} + +jlong native_timing_resolution() { + struct timespec timeval; + clock_getres(CLOCK_MONOTONIC, &timeval); + return timeval.tv_nsec; +} + + +void native_timing_start() { + clock_gettime(CLOCK_MONOTONIC, &start); +} + + +void native_timing_stop() { + clock_gettime(CLOCK_MONOTONIC, &end); +} + + +jlong native_timing_last() { + jlong res = (end.tv_sec - start.tv_sec) * 1000000000 + (end.tv_nsec - start.tv_nsec); + if (res < 0) { + return 0; + } else { + return res; + } +} + +#else + +jboolean native_timing_supported() { + return JNI_FALSE; +} + +jlong native_timing_resolution() { + return 0; +} + +void native_timing_start() {} + +void native_timing_stop() {} + +jlong native_timing_last() { + return 0; +} + +#endif
\ No newline at end of file diff --git a/src/cz/crcs/ectester/standalone/libs/jni/c_timing.h b/src/cz/crcs/ectester/standalone/libs/jni/c_timing.h new file mode 100644 index 0000000..bce2a19 --- /dev/null +++ b/src/cz/crcs/ectester/standalone/libs/jni/c_timing.h @@ -0,0 +1,38 @@ +#pragma once + +#include <jni.h> +#include <unistd.h> + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * + */ +jboolean native_timing_supported(); + +/** + * + */ +jlong native_timing_resolution(); + +/** + * + */ +void native_timing_start(); + +/** + * + */ +void native_timing_stop(); + +/** + * + */ +jlong native_timing_last(); + +#ifdef __cplusplus +} +#endif
\ No newline at end of file diff --git a/src/cz/crcs/ectester/standalone/libs/jni/c_utils.c b/src/cz/crcs/ectester/standalone/libs/jni/c_utils.c index 49cab44..81d1fb8 100644 --- a/src/cz/crcs/ectester/standalone/libs/jni/c_utils.c +++ b/src/cz/crcs/ectester/standalone/libs/jni/c_utils.c @@ -75,7 +75,7 @@ void throw_new_var(JNIEnv *env, const char *class, const char *format, ...) { char buffer[2048]; va_list args; va_start(args, format); - int res = vsnprintf(buffer, 2048, format, args); + vsnprintf(buffer, 2048, format, args); va_end(args); throw_new(env, class, buffer); } @@ -105,4 +105,109 @@ jint get_kdf_bits(JNIEnv *env, jstring algorithm) { } (*env)->ReleaseStringUTFChars(env, algorithm, algo_data); return result; +} + +jbyteArray asn1_der_encode(JNIEnv *env, const jbyte *r, size_t r_len, const jbyte *s, size_t s_len) { + jbyte r_length = (jbyte) r_len + (r[0] & 0x80 ? 1 : 0); + jbyte s_length = (jbyte) s_len + (s[0] & 0x80 ? 1 : 0); + + // R and S are < 128 bytes, so 1 byte tag + 1 byte len + len bytes value + size_t seq_value_len = 2 + r_length + 2 + s_length; + size_t whole_len = seq_value_len; + + // The SEQUENCE length might be >= 128, so more bytes of length + size_t seq_len_len = 0; + if (seq_value_len >= 128) { + size_t s = seq_value_len; + while ((s = s >> 8)) { + seq_len_len++; + } + } + // seq_len_len bytes for length and one for length of length + whole_len += seq_len_len + 1; + + // 1 byte tag for SEQUENCE + whole_len += 1; + + jbyteArray result = (jbyteArray) (*env)->NewByteArray(env, whole_len); + jbyte *data = (*env)->GetByteArrayElements(env, result, NULL); + size_t i = 0; + data[i++] = 0x30; // SEQUENCE + if (seq_value_len < 128) { + data[i++] = (jbyte) seq_value_len; + } else { + data[i++] = (jbyte) (seq_len_len | (1 << 7)); + for (size_t j = 0; j < seq_len_len; ++j) { + data[i++] = (jbyte) (seq_value_len & (0xff << (seq_len_len - j))); + } + } + data[i++] = 0x02; //INTEGER + data[i++] = r_length; + if (r[0] & 0x80) { + data[i++] = 0; + } + memcpy(data + i, r, r_len); + i += r_len; + data[i++] = 0x02; //INTEGER + data[i++] = s_length; + if (s[0] & 0x80) { + data[i++] = 0; + } + memcpy(data + i, s, s_len); + i += s_len; + (*env)->ReleaseByteArrayElements(env, result, data, 0); + + return result; +} + +bool asn1_der_decode(JNIEnv *env, jbyteArray sig, jbyte **r_data, size_t *r_len, jbyte **s_data, size_t *s_len) { + size_t sig_len = (*env)->GetArrayLength(env, sig); + jbyte *data = (*env)->GetByteArrayElements(env, sig, NULL); + size_t i = 0; + if (data[i++] != 0x30) {//SEQUENCE + (*env)->ReleaseByteArrayElements(env, sig, data, JNI_ABORT); + return false; + } + size_t seq_value_len = 0; + if (!(data[i] & 0x80)) { + seq_value_len = data[i++]; + } else { + size_t seq_len_len = data[i++] & 0x7f; + while (seq_len_len > 0) { + seq_value_len |= (data[i++] << (seq_len_len - 1)); + seq_len_len--; + } + } + + if (data[i++] != 0x02) {//INTEGER + (*env)->ReleaseByteArrayElements(env, sig, data, JNI_ABORT); + return false; + } + size_t r_length = data[i++]; + jbyte *r_out = malloc(r_length); + memcpy(r_out, data + i, r_length); + i += r_length; + + if (data[i++] != 0x02) {//INTEGER + free(r_out); + (*env)->ReleaseByteArrayElements(env, sig, data, JNI_ABORT); + return false; + } + size_t s_length = data[i++]; + jbyte *s_out = malloc(s_length); + memcpy(s_out, data + i, s_length); + i += s_length; + + (*env)->ReleaseByteArrayElements(env, sig, data, JNI_ABORT); + if (i != sig_len) { + free(r_out); + free(s_out); + return false; + } + + *r_len = r_length; + *r_data = r_out; + *s_len = s_length; + *s_data = s_out; + return true; }
\ No newline at end of file diff --git a/src/cz/crcs/ectester/standalone/libs/jni/c_utils.h b/src/cz/crcs/ectester/standalone/libs/jni/c_utils.h index b767b61..82c3538 100644 --- a/src/cz/crcs/ectester/standalone/libs/jni/c_utils.h +++ b/src/cz/crcs/ectester/standalone/libs/jni/c_utils.h @@ -1,6 +1,7 @@ #pragma once #include "native.h" +#include <stdbool.h> /** * Classes that are accessed alot are cached here, manually. @@ -39,6 +40,16 @@ void throw_new_var(JNIEnv *env, const char *class, const char *format, ...); jint get_kdf_bits(JNIEnv *env, jstring algorithm); /** + * DER encode the r and s values. + */ +jbyteArray asn1_der_encode(JNIEnv *env, const jbyte *r, size_t r_len, const jbyte *s, size_t s_len); + +/** + * DER decode a signature into r and s values. + */ +bool asn1_der_decode(JNIEnv *env, jbyteArray sig, jbyte **r_data, size_t *r_len, jbyte **s_data, size_t *s_len); + +/** * Some useful defines to init the provider. */ #define INIT_PROVIDER(env, provider_class) jmethodID provider_put = (*env)->GetMethodID(env, provider_class, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;") diff --git a/src/cz/crcs/ectester/standalone/libs/jni/cryptopp.cpp b/src/cz/crcs/ectester/standalone/libs/jni/cryptopp.cpp index 32121c5..089724e 100644 --- a/src/cz/crcs/ectester/standalone/libs/jni/cryptopp.cpp +++ b/src/cz/crcs/ectester/standalone/libs/jni/cryptopp.cpp @@ -23,7 +23,6 @@ using CryptoPP::byte; #include "cryptopp/osrng.h" using CryptoPP::AutoSeededRandomPool; -using CryptoPP::AutoSeededX917RNG; #include "cryptopp/sha.h" using CryptoPP::SHA1; @@ -71,8 +70,10 @@ using CryptoPP::Integer; #include "cpp_utils.hpp" +#include "c_timing.h" static jclass provider_class; +static AutoSeededRandomPool rng; JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_CryptoppLib_createProvider(JNIEnv *env, jobject self) { @@ -89,7 +90,7 @@ JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_CryptoppLib_crea std::stringstream ss; ss << lib_name << " "; ss << info_str[0]; - for (int i = 1; i < info_str.size(); ++i) { + for (size_t i = 1; i < info_str.size(); ++i) { ss << "." << info_str[i]; } @@ -470,6 +471,8 @@ template <> jobject params_from_group<EC2N>(JNIEnv *env, DL_GroupParameters_EC<E //pentanomial ks = env->NewIntArray(3); to_find = 3; + } else { + return NULL; } jint *ks_data = env->GetIntArrayElements(ks, NULL); for (int i = m - 1; i > 0 && found < to_find; --i) { @@ -492,12 +495,13 @@ template <> jobject params_from_group<EC2N>(JNIEnv *env, DL_GroupParameters_EC<E } template <class EC> jobject generate_from_group(JNIEnv *env, DL_GroupParameters_EC<EC> group, jobject params) { - AutoSeededRandomPool rng; typename ECDH<EC>::Domain ec_domain(group); SecByteBlock priv(ec_domain.PrivateKeyLength()), pub(ec_domain.PublicKeyLength()); try { + native_timing_start(); ec_domain.GenerateKeyPair(rng, priv, pub); + native_timing_stop(); } catch (Exception & ex) { throw_new(env, "java/security/GeneralSecurityException", ex.what()); return NULL; @@ -578,7 +582,9 @@ JNIEXPORT jbyteArray JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKey try { secret = std::make_unique<SecByteBlock>(dh_agreement.AgreedValueLength()); + native_timing_start(); success = dh_agreement.Agree(*secret, private_key, public_key); + native_timing_stop(); } catch (Exception & ex) { throw_new(env, "java/security/GeneralSecurityException", ex.what()); return NULL; @@ -588,12 +594,18 @@ JNIEXPORT jbyteArray JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKey try { secret = std::make_unique<SecByteBlock>(dh_agreement.AgreedValueLength()); + native_timing_start(); success = dh_agreement.Agree(*secret, private_key, public_key); + native_timing_stop(); } catch (Exception & ex) { throw_new(env, "java/security/GeneralSecurityException", ex.what()); return NULL; } } + if (!success) { + throw_new(env, "java/security/GeneralSecurityException", "Agreement was unsuccessful."); + return NULL; + } jbyteArray result = env->NewByteArray(secret->size()); jbyte *result_data = env->GetByteArrayElements(result, NULL); @@ -610,7 +622,6 @@ JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyAgr template <class EC, class H> jbyteArray sign_message(JNIEnv *env, DL_GroupParameters_EC<EC> group, jbyteArray data, const Integer & private_key_x) { - AutoSeededRandomPool prng; typename ECDSA<EC, H>::PrivateKey pkey; pkey.Initialize(group, private_key_x); @@ -620,7 +631,9 @@ jbyteArray sign_message(JNIEnv *env, DL_GroupParameters_EC<EC> group, jbyteArray jsize data_length = env->GetArrayLength(data); jbyte *data_bytes = env->GetByteArrayElements(data, NULL); - size_t len = signer.SignMessage(prng, (byte *)data_bytes, data_length, (byte *)signature.c_str()); + native_timing_start(); + size_t len = signer.SignMessage(rng, (byte *)data_bytes, data_length, (byte *)signature.c_str()); + native_timing_stop(); env->ReleaseByteArrayElements(data, data_bytes, JNI_ABORT); signature.resize(len); @@ -648,7 +661,7 @@ JNIEXPORT jbyteArray JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSig Integer private_key_x((byte *) privkey_data, (size_t) privkey_length); env->ReleaseByteArrayElements(privkey, privkey_data, JNI_ABORT); - jbyteArray result; + jbyteArray result = NULL; std::unique_ptr<DL_GroupParameters_EC<ECP>> ecp_group = fp_group_from_params(env, params); if (ecp_group == nullptr) { @@ -705,7 +718,9 @@ jboolean verify_message(JNIEnv *env, DL_GroupParameters_EC<EC> group, jbyteArray jsize data_length = env->GetArrayLength(data); jbyte *data_bytes = env->GetByteArrayElements(data, NULL); + native_timing_start(); bool result = verifier.VerifyMessage((byte *)data_bytes, data_length, sig, sig_len); + native_timing_stop(); env->ReleaseByteArrayElements(data, data_bytes, JNI_ABORT); return result; @@ -750,3 +765,15 @@ JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSigna // unreachable return JNI_FALSE; } + +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_CryptoppLib_supportsNativeTiming(JNIEnv *env, jobject self) { + return native_timing_supported(); +} + +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_CryptoppLib_getNativeTimingResolution(JNIEnv *env, jobject self) { + return native_timing_resolution(); +} + +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_CryptoppLib_getLastNativeTiming(JNIEnv *env, jobject self) { + return native_timing_last(); +}
\ No newline at end of file diff --git a/src/cz/crcs/ectester/standalone/libs/jni/gcrypt.c b/src/cz/crcs/ectester/standalone/libs/jni/gcrypt.c new file mode 100644 index 0000000..359d0f4 --- /dev/null +++ b/src/cz/crcs/ectester/standalone/libs/jni/gcrypt.c @@ -0,0 +1,635 @@ +#include "native.h" +#include <stdio.h> +#include <ctype.h> +#include <stdbool.h> +#include <gcrypt.h> +#include "c_utils.h" +#include "c_timing.h" + +static jclass provider_class; + + +JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_GcryptLib_createProvider(JNIEnv *env, jobject this){ + /* Create the custom provider. */ + jclass local_provider_class = (*env)->FindClass(env, "cz/crcs/ectester/standalone/libs/jni/NativeProvider$Gcrypt"); + provider_class = (*env)->NewGlobalRef(env, local_provider_class); + + jmethodID init = (*env)->GetMethodID(env, local_provider_class, "<init>", "(Ljava/lang/String;DLjava/lang/String;)V"); + + const char *running_with = gcry_check_version(GCRYPT_VERSION); + if (!running_with) { + return NULL; + } + char full_name[strlen("libgcrypt ") + strlen(running_with) + 1]; + strcpy(full_name, "libgcrypt "); + strcat(full_name, running_with); + jstring name = (*env)->NewStringUTF(env, full_name); + double version = strtod(running_with, NULL); + + return (*env)->NewObject(env, provider_class, init, name, version, name); +} + +JNIEXPORT void JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeProvider_00024Gcrypt_setup(JNIEnv *env, jobject this) { + gcry_control(GCRYCTL_DISABLE_SECMEM, 0); + //gcry_control(GCRYCTL_SET_DEBUG_FLAGS, 1); + gcry_control(GCRYCTL_ENABLE_QUICK_RANDOM, 0); + gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); + + INIT_PROVIDER(env, provider_class); + + ADD_KPG(env, this, "EC", "Gcrypt"); + ADD_KA(env, this, "ECDH", "GcryptECDH"); + ADD_SIG(env, this, "NONEwithECDSA", "GcryptECDSAwithNONE"); + ADD_SIG(env, this, "SHA1withECDSA", "GcryptECDSAwithSHA1"); + ADD_SIG(env, this, "SHA224withECDSA", "GcryptECDSAwithSHA224"); + ADD_SIG(env, this, "SHA256withECDSA", "GcryptECDSAwithSHA256"); + ADD_SIG(env, this, "SHA384withECDSA", "GcryptECDSAwithSHA384"); + ADD_SIG(env, this, "SHA512withECDSA", "GcryptECDSAwithSHA512"); + ADD_SIG(env, this, "SHA1withECDDSA", "GcryptECDDSAwithSHA1"); + ADD_SIG(env, this, "SHA224withECDDSA", "GcryptECDDSAwithSHA224"); + ADD_SIG(env, this, "SHA256withECDDSA", "GcryptECDDSAwithSHA256"); + ADD_SIG(env, this, "SHA384withECDDSA", "GcryptECDDSAwithSHA384"); + ADD_SIG(env, this, "SHA512withECDDSA", "GcryptECDDSAwithSHA512"); + + init_classes(env, "Gcrypt"); +} + +JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_GcryptLib_getCurves(JNIEnv *env, jobject this) { + jclass hash_set_class = (*env)->FindClass(env, "java/util/TreeSet"); + + jmethodID hash_set_ctr = (*env)->GetMethodID(env, hash_set_class, "<init>", "()V"); + jmethodID hash_set_add = (*env)->GetMethodID(env, hash_set_class, "add", "(Ljava/lang/Object;)Z"); + + jobject result = (*env)->NewObject(env, hash_set_class, hash_set_ctr); + + const char *name; + unsigned int nbits; + + for (size_t i = 0; (name = gcry_pk_get_curve(NULL, i, &nbits)); i++){ + jstring curve_name = (*env)->NewStringUTF(env, name); + (*env)->CallBooleanMethod(env, result, hash_set_add, curve_name); + } + + return result; +} + +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_00024Gcrypt_keysizeSupported(JNIEnv *env, jobject this, jint keysize) { + const char *name; + unsigned int nbits; + + for (size_t i = 0; (name = gcry_pk_get_curve(NULL, i, &nbits)); i++){ + if (nbits == keysize) { + return JNI_TRUE; + } + } + + return JNI_FALSE; +} + +/* +static void print_sexp(gcry_sexp_t sexp) { + size_t len = gcry_sexp_sprint(sexp, GCRYSEXP_FMT_ADVANCED, NULL, 0); + char string[len]; + gcry_sexp_sprint(sexp, GCRYSEXP_FMT_ADVANCED, string, len); + printf("%s\n", string); + fflush(stdout); +} + +static void print_chrray(unsigned char *arr, size_t len) { + for (size_t i = 0; i < len; ++i) { + printf("%02x,", ((unsigned char) arr[i] & 0xff)); + } + printf("\n"); +} +*/ + +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_00024Gcrypt_paramsSupported(JNIEnv *env, jobject this, jobject params) { + if (params == NULL) { + return JNI_FALSE; + } + + if ((*env)->IsInstanceOf(env, params, ec_parameter_spec_class)) { + return JNI_FALSE; + } else if ((*env)->IsInstanceOf(env, params, ecgen_parameter_spec_class)) { + jmethodID get_name = (*env)->GetMethodID(env, ecgen_parameter_spec_class, "getName", "()Ljava/lang/String;"); + jstring name = (*env)->CallObjectMethod(env, params, get_name); + const char *utf_name = (*env)->GetStringUTFChars(env, name, NULL); + gcry_sexp_t curve_sexp; + gcry_sexp_build(&curve_sexp, NULL, "(public-key (ecc (curve %s)))", utf_name); + unsigned int nbits; + const char *ret_name = gcry_pk_get_curve(curve_sexp, 0, &nbits); + (*env)->ReleaseStringUTFChars(env, name, utf_name); + gcry_sexp_release(curve_sexp); + return ret_name ? JNI_TRUE : JNI_FALSE; + } else { + return JNI_FALSE; + } +} + +static gcry_mpi_t bytearray_to_mpi(JNIEnv *env, jbyteArray array) { + if (!array) { + return NULL; + } + + gcry_mpi_t result; + + size_t length = (*env)->GetArrayLength(env, array); + jbyte data[length + 1]; + data[0] = 0; + (*env)->GetByteArrayRegion(env, array, 0, length, data + 1); + gcry_mpi_scan(&result, GCRYMPI_FMT_STD, data, length + 1, NULL); + return result; +} + +static jbyteArray mpi_to_bytearray0(JNIEnv *env, gcry_mpi_t mpi, size_t start, size_t len) { + if (!mpi) { + return NULL; + } + + size_t mpi_len = 0; + gcry_mpi_print(GCRYMPI_FMT_USG, NULL, 0, &mpi_len, mpi); + if (start >= mpi_len) { + return NULL; + } + if (start + len > mpi_len || len == 0) { + len = mpi_len - start; + } + unsigned char buff[mpi_len]; + gcry_mpi_print(GCRYMPI_FMT_USG, buff, mpi_len, NULL, mpi); + jbyteArray bytes = (*env)->NewByteArray(env, len); + jbyte *data = (*env)->GetByteArrayElements(env, bytes, NULL); + memcpy(data, buff + start, len); + (*env)->ReleaseByteArrayElements(env, bytes, data, 0); + return bytes; +} + +static jbyteArray mpi_to_bytearray(JNIEnv *env, gcry_mpi_t mpi) { + return mpi_to_bytearray0(env, mpi, 0, 0); +} + +static jobject mpi_to_biginteger(JNIEnv *env, gcry_mpi_t mpi) { + if (!mpi) { + return NULL; + } + + jmethodID biginteger_init = (*env)->GetMethodID(env, biginteger_class, "<init>", "(I[B)V"); + jbyteArray bytes = mpi_to_bytearray(env, mpi); + jobject result = (*env)->NewObject(env, biginteger_class, biginteger_init, 1, bytes); + return result; +} + +static gcry_mpi_t biginteger_to_mpi(JNIEnv *env, jobject bigint) { + if (!bigint) { + return NULL; + } + + jmethodID to_byte_array = (*env)->GetMethodID(env, biginteger_class, "toByteArray", "()[B"); + jbyteArray byte_array = (jbyteArray) (*env)->CallObjectMethod(env, bigint, to_byte_array); + return bytearray_to_mpi(env, byte_array); +} + +static jint mpi_to_jint(gcry_mpi_t mpi) { + jint result = 0; + unsigned long nbits = gcry_mpi_get_nbits(mpi); + int max_bits = sizeof(jint) * 8; + for (size_t i = 0; i < nbits && i < max_bits; ++i) { + if (gcry_mpi_test_bit(mpi, nbits - i - 1)) { + result = ((result << 1) | 1); + } else { + result = (result << 1); + } + } + return result; +} + +static jobject buff_to_ecpoint(JNIEnv *env, gcry_buffer_t buff) { + jint coord_size = (buff.len - 1) / 2; + jmethodID biginteger_init = (*env)->GetMethodID(env, biginteger_class, "<init>", "(I[B)V"); + + jbyteArray x_bytes = (*env)->NewByteArray(env, coord_size); + jbyte *x_data = (*env)->GetByteArrayElements(env, x_bytes, NULL); + memcpy(x_data, ((char *) buff.data) + 1, coord_size); + (*env)->ReleaseByteArrayElements(env, x_bytes, x_data, 0); + jobject xi = (*env)->NewObject(env, biginteger_class, biginteger_init, 1, x_bytes); + + jbyteArray y_bytes = (*env)->NewByteArray(env, coord_size); + jbyte *y_data = (*env)->GetByteArrayElements(env, y_bytes, NULL); + memcpy(y_data, ((char *) buff.data) + 1 + coord_size, coord_size); + (*env)->ReleaseByteArrayElements(env, y_bytes, y_data, 0); + jobject yi = (*env)->NewObject(env, biginteger_class, biginteger_init, 1, y_bytes); + + jmethodID point_init = (*env)->GetMethodID(env, point_class, "<init>", "(Ljava/math/BigInteger;Ljava/math/BigInteger;)V"); + return (*env)->NewObject(env, point_class, point_init, xi, yi); +} + +static jobject create_ec_param_spec(JNIEnv *env, gcry_sexp_t key) { + jobject result = NULL; + gcry_mpi_t p, a, b, n, h; + gcry_buffer_t g = {0}; + gcry_error_t err = gcry_sexp_extract_param(key, "ecc", "pab&g+nh", &p, &a, &b, &g, &n, &h, NULL); + if (gcry_err_code(err) != GPG_ERR_NO_ERROR) { + throw_new_var(env, "java/security/GeneralSecurityException", "Error exporting domain parameters. Error: %ui", gcry_err_code(err)); + goto end; + } + + jobject pi = mpi_to_biginteger(env, p); + jmethodID fp_field_init = (*env)->GetMethodID(env, fp_field_class, "<init>", "(Ljava/math/BigInteger;)V"); + jobject field = (*env)->NewObject(env, fp_field_class, fp_field_init, pi); + + jobject ai = mpi_to_biginteger(env, a); + jobject bi = mpi_to_biginteger(env, b); + + jmethodID elliptic_curve_init = (*env)->GetMethodID(env, elliptic_curve_class, "<init>", "(Ljava/security/spec/ECField;Ljava/math/BigInteger;Ljava/math/BigInteger;)V"); + jobject elliptic_curve = (*env)->NewObject(env, elliptic_curve_class, elliptic_curve_init, field, ai, bi); + + jobject gen = buff_to_ecpoint(env, g); + + jobject order = mpi_to_biginteger(env, n); + jint cofactor = mpi_to_jint(h); + + jmethodID ec_parameter_spec_init = (*env)->GetMethodID(env, ec_parameter_spec_class, "<init>", "(Ljava/security/spec/EllipticCurve;Ljava/security/spec/ECPoint;Ljava/math/BigInteger;I)V"); + result = (*env)->NewObject(env, ec_parameter_spec_class, ec_parameter_spec_init, elliptic_curve, gen, order, cofactor); + +end: + gcry_mpi_release(p); + gcry_mpi_release(a); + gcry_mpi_release(b); + gcry_free(g.data); + gcry_mpi_release(n); + gcry_mpi_release(h); + return result; +} + +static jobject generate_from_sexp(JNIEnv *env, gcry_sexp_t gen_sexp) { + jobject result = NULL; + gcry_sexp_t key_sexp; + + native_timing_start(); + gcry_error_t err = gcry_pk_genkey(&key_sexp, gen_sexp); + native_timing_stop(); + + if (gcry_err_code(err) != GPG_ERR_NO_ERROR) { + throw_new_var(env, "java/security/GeneralSecurityException", "Error generating key. Error: %ui", gcry_err_code(err)); + goto release_sexp; + } + gcry_sexp_t pkey = gcry_sexp_find_token(key_sexp, "public-key", 0); + gcry_sexp_t skey = gcry_sexp_find_token(key_sexp, "private-key", 0); + + jobject ec_param_spec = create_ec_param_spec(env, skey); + if (!ec_param_spec) { + goto release_keypair; + } + + gcry_buffer_t q = {0}; + gcry_mpi_t d; + err = gcry_sexp_extract_param(skey, "ecc", "&q+d", &q, &d, NULL); + + jbyteArray pub_bytes = (*env)->NewByteArray(env, q.size); + jbyte *key_pub = (*env)->GetByteArrayElements(env, pub_bytes, NULL); + memcpy(key_pub, q.data, q.size); + (*env)->ReleaseByteArrayElements(env, pub_bytes, key_pub, 0); + + size_t priv_len = 0; + gcry_mpi_print(GCRYMPI_FMT_USG, NULL, 0, &priv_len, d); + jbyteArray priv_bytes = (*env)->NewByteArray(env, priv_len); + jbyte *key_priv = (*env)->GetByteArrayElements(env, priv_bytes, NULL); + gcry_mpi_print(GCRYMPI_FMT_USG, (unsigned char *) key_priv, priv_len, NULL, d); + (*env)->ReleaseByteArrayElements(env, priv_bytes, key_priv, 0); + + jobject ec_pub_param_spec = (*env)->NewLocalRef(env, ec_param_spec); + jmethodID ec_pub_init = (*env)->GetMethodID(env, pubkey_class, "<init>", "([BLjava/security/spec/ECParameterSpec;)V"); + jobject pubkey = (*env)->NewObject(env, pubkey_class, ec_pub_init, pub_bytes, ec_pub_param_spec); + + jobject ec_priv_param_spec = (*env)->NewLocalRef(env, ec_param_spec); + jmethodID ec_priv_init = (*env)->GetMethodID(env, privkey_class, "<init>", "([BLjava/security/spec/ECParameterSpec;)V"); + jobject privkey = (*env)->NewObject(env, privkey_class, ec_priv_init, priv_bytes, ec_priv_param_spec); + + jmethodID keypair_init = (*env)->GetMethodID(env, keypair_class, "<init>", "(Ljava/security/PublicKey;Ljava/security/PrivateKey;)V"); + result = (*env)->NewObject(env, keypair_class, keypair_init, pubkey, privkey); + + gcry_mpi_release(d); + gcry_free(q.data); + +release_keypair: + gcry_sexp_release(pkey); + gcry_sexp_release(skey); +release_sexp: + gcry_sexp_release(key_sexp); + return result; +} + +JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_00024Gcrypt_generate__ILjava_security_SecureRandom_2(JNIEnv *env, jobject this, jint keysize, jobject random) { + gcry_sexp_t gen_sexp; + gcry_sexp_build(&gen_sexp, NULL, "(genkey (ecc (flags no-keytest param) (nbits %d)))", keysize); + + jobject result = generate_from_sexp(env, gen_sexp); + gcry_sexp_release(gen_sexp); + return result; +} + +JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_00024Gcrypt_generate__Ljava_security_spec_AlgorithmParameterSpec_2Ljava_security_SecureRandom_2(JNIEnv *env, jobject this, jobject params, jobject random) { + if ((*env)->IsInstanceOf(env, params, ec_parameter_spec_class)) { + return NULL; + } else if ((*env)->IsInstanceOf(env, params, ecgen_parameter_spec_class)) { + jmethodID get_name = (*env)->GetMethodID(env, ecgen_parameter_spec_class, "getName", "()Ljava/lang/String;"); + jstring name = (*env)->CallObjectMethod(env, params, get_name); + const char *utf_name = (*env)->GetStringUTFChars(env, name, NULL); + gcry_sexp_t gen_sexp; + gcry_sexp_build(&gen_sexp, NULL, "(genkey (ecc (flags no-keytest param) (curve %s)))", utf_name); + (*env)->ReleaseStringUTFChars(env, name, utf_name); + jobject result = generate_from_sexp(env, gen_sexp); + gcry_sexp_release(gen_sexp); + return result; + } else { + return NULL; + } +} + +static gcry_sexp_t create_key(JNIEnv *env, jobject ec_param_spec, const char *key_fmt, gcry_mpi_t q, gcry_mpi_t d) { + gcry_mpi_t p, a, b, g, n, h; + + jmethodID get_curve = (*env)->GetMethodID(env, ec_parameter_spec_class, "getCurve", "()Ljava/security/spec/EllipticCurve;"); + jobject elliptic_curve = (*env)->CallObjectMethod(env, ec_param_spec, get_curve); + + jmethodID get_field = (*env)->GetMethodID(env, elliptic_curve_class, "getField", "()Ljava/security/spec/ECField;"); + jobject field = (*env)->CallObjectMethod(env, elliptic_curve, get_field); + + jmethodID get_bits = (*env)->GetMethodID(env, fp_field_class, "getFieldSize", "()I"); + jint bits = (*env)->CallIntMethod(env, field, get_bits); + jint bytes = (bits + 7) / 8; + + jmethodID get_a = (*env)->GetMethodID(env, elliptic_curve_class, "getA", "()Ljava/math/BigInteger;"); + jobject big_a = (*env)->CallObjectMethod(env, elliptic_curve, get_a); + a = biginteger_to_mpi(env, big_a); + + jmethodID get_b = (*env)->GetMethodID(env, elliptic_curve_class, "getB", "()Ljava/math/BigInteger;"); + jobject big_b = (*env)->CallObjectMethod(env, elliptic_curve, get_b); + b = biginteger_to_mpi(env, big_b); + + jmethodID get_p = (*env)->GetMethodID(env, fp_field_class, "getP", "()Ljava/math/BigInteger;"); + jobject big_p = (*env)->CallObjectMethod(env, field, get_p); + p = biginteger_to_mpi(env, big_p); + + jmethodID get_g = (*env)->GetMethodID(env, ec_parameter_spec_class, "getGenerator", "()Ljava/security/spec/ECPoint;"); + jobject g_point = (*env)->CallObjectMethod(env, ec_param_spec, get_g); + + jmethodID get_x = (*env)->GetMethodID(env, point_class, "getAffineX", "()Ljava/math/BigInteger;"); + jobject gx = (*env)->CallObjectMethod(env, g_point, get_x); + + jmethodID get_y = (*env)->GetMethodID(env, point_class, "getAffineY", "()Ljava/math/BigInteger;"); + jobject gy = (*env)->CallObjectMethod(env, g_point, get_y); + + jmethodID to_byte_array = (*env)->GetMethodID(env, biginteger_class, "toByteArray", "()[B"); + + jbyteArray gx_bytes = (jbyteArray) (*env)->CallObjectMethod(env, gx, to_byte_array); + size_t gx_len = (*env)->GetArrayLength(env, gx_bytes); + jbyteArray gy_bytes = (jbyteArray) (*env)->CallObjectMethod(env, gy, to_byte_array); + size_t gy_len = (*env)->GetArrayLength(env, gy_bytes); + unsigned char g_data[1 + 2 * bytes]; + g_data[0] = 0x04; + jbyte *gx_data = (*env)->GetByteArrayElements(env, gx_bytes, NULL); + memcpy(g_data + 1, gx_data + (gx_len - bytes), bytes); + (*env)->ReleaseByteArrayElements(env, gx_bytes, gx_data, JNI_ABORT); + jbyte *gy_data = (*env)->GetByteArrayElements(env, gy_bytes, NULL); + memcpy(g_data + 1 + bytes, gy_data + (gy_len - bytes), bytes); + (*env)->ReleaseByteArrayElements(env, gy_bytes, gy_data, JNI_ABORT); + + gcry_mpi_scan(&g, GCRYMPI_FMT_USG, g_data, 1 + 2 * bytes, NULL); + + jmethodID get_n = (*env)->GetMethodID(env, ec_parameter_spec_class, "getOrder", "()Ljava/math/BigInteger;"); + jobject big_n = (*env)->CallObjectMethod(env, ec_param_spec, get_n); + n = biginteger_to_mpi(env, big_n); + + jmethodID get_h = (*env)->GetMethodID(env, ec_parameter_spec_class, "getCofactor", "()I"); + jint jh = (*env)->CallIntMethod(env, ec_param_spec, get_h); + h = gcry_mpi_set_ui(NULL, jh); + + gcry_sexp_t inner = NULL; + if (q && d) { + gcry_sexp_build(&inner, NULL, "(ecc (flags param) (p %m) (a %m) (b %m) (g %m) (n %m) (h %m) (q %M) (d %M))", p, a, b, g, n, h, q, d, NULL); + } else if (q && !d) { + gcry_sexp_build(&inner, NULL, "(ecc (flags param) (p %m) (a %m) (b %m) (g %m) (n %m) (h %m) (q %m))", p, a, b, g, n, h, q, NULL); + } else if (!q && d) { + gcry_sexp_build(&inner, NULL, "(ecc (flags param) (p %m) (a %m) (b %m) (g %m) (n %m) (h %m) (d %m))", p, a, b, g, n, h, d, NULL); + } + gcry_sexp_t result; + gcry_sexp_build(&result, NULL, key_fmt, inner, NULL); + gcry_sexp_release(inner); + return result; +} + +static gcry_sexp_t create_pubkey(JNIEnv *env, jobject ec_param_spec, jbyteArray pubkey) { + gcry_mpi_t q = bytearray_to_mpi(env, pubkey); + gcry_sexp_t result = create_key(env, ec_param_spec, "(public-key %S)", q, NULL); + gcry_mpi_release(q); + return result; +} + +static gcry_sexp_t create_privkey(JNIEnv *env, jobject ec_param_spec, jbyteArray pubkey, jbyteArray privkey) { + gcry_mpi_t q = bytearray_to_mpi(env, pubkey); + gcry_mpi_t d = bytearray_to_mpi(env, privkey); + gcry_sexp_t result = create_key(env, ec_param_spec, "(private-key %S)", q, d); + gcry_mpi_release(q); + gcry_mpi_release(d); + return result; +} + +JNIEXPORT jbyteArray JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyAgreementSpi_00024Gcrypt_generateSecret___3B_3BLjava_security_spec_ECParameterSpec_2(JNIEnv *env, jobject this, jbyteArray pubkey, jbyteArray privkey, jobject params) { + jbyteArray result = NULL; + gcry_sexp_t pub = create_pubkey(env, params, pubkey); + gcry_mpi_t priv = bytearray_to_mpi(env, privkey); + + gcry_sexp_t enc_sexp; + gcry_sexp_build(&enc_sexp, NULL, "(data (flags raw) (value %M))", priv, NULL); + gcry_sexp_t res_sexp; + // TODO: figure out why ecc_encrypt_raw takes signed representation.. Nobody uses that., everybody uses unsigned reduced mod p. + + native_timing_start(); + gcry_error_t err = gcry_pk_encrypt(&res_sexp, enc_sexp, pub); + native_timing_stop(); + + if (gcry_err_code(err) != GPG_ERR_NO_ERROR) { + throw_new_var(env, "java/security/GeneralSecurityException", "Error performing ECDH. Error: %ui", gcry_err_code(err)); + goto end; + } + + gcry_mpi_t derived; + err = gcry_sexp_extract_param(res_sexp, NULL, "s", &derived, NULL); + + size_t derived_bytes; + gcry_mpi_print(GCRYMPI_FMT_USG, NULL, 0, &derived_bytes, derived); + size_t coord_bytes = (derived_bytes - 1) / 2; + result = mpi_to_bytearray0(env, derived, 1, coord_bytes); + + gcry_mpi_release(derived); +end: + gcry_sexp_release(enc_sexp); + gcry_sexp_release(res_sexp); + gcry_sexp_release(pub); + gcry_mpi_release(priv); + return result; +} + +JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyAgreementSpi_00024Gcrypt_generateSecret___3B_3BLjava_security_spec_ECParameterSpec_2Ljava_lang_String_2(JNIEnv *env, jobject this, jbyteArray pubkey, jbyteArray privkey, jobject params, jstring algorithm) { + throw_new(env, "java/lang/UnsupportedOperationException", "Not supported."); + return NULL; +} + +static int starts_with(const char *whole, const char *prefix) { + return !strncmp(whole, prefix, strlen(prefix)); +} + +static int get_hash_algo(const char *sig_type) { + if (starts_with(sig_type, "SHA1")) { + return GCRY_MD_SHA1; + } else if (starts_with(sig_type, "SHA224")) { + return GCRY_MD_SHA224; + } else if (starts_with(sig_type, "SHA256")) { + return GCRY_MD_SHA256; + } else if (starts_with(sig_type, "SHA384")) { + return GCRY_MD_SHA384; + } else if (starts_with(sig_type, "SHA512")) { + return GCRY_MD_SHA512; + } else { + return GCRY_MD_NONE; + } +} + +static const char *get_sig_algo(const char *sig_type) { + const char *start = strstr(sig_type, "with") + strlen("with"); + if (starts_with(start, "ECDSA")) { + return NULL; + } else if (starts_with(start, "ECDDSA")) { + return "rfc6979"; + } else { + return NULL; + } +} + +static void get_sign_data_sexp(JNIEnv *env, gcry_sexp_t *result, jobject this, jbyteArray data) { + jclass sig_class = (*env)->FindClass(env, "cz/crcs/ectester/standalone/libs/jni/NativeSignatureSpi$Gcrypt"); + jfieldID type_id = (*env)->GetFieldID(env, sig_class, "type", "Ljava/lang/String;"); + jstring type = (jstring)(*env)->GetObjectField(env, this, type_id); + const char* type_data = (*env)->GetStringUTFChars(env, type, NULL); + int hash_algo = get_hash_algo(type_data); + const char *sig_algo = get_sig_algo(type_data); + const char *with = strstr(type_data, "with"); + char hash_name[with - type_data + 1]; + memcpy(hash_name, type_data, with - type_data); + for (size_t i = 0; i < with - type_data; ++i) { + hash_name[i] = tolower(hash_name[i]); + } + hash_name[with - type_data] = 0; + (*env)->ReleaseStringUTFChars(env, type, type_data); + + if (hash_algo == GCRY_MD_NONE) { + gcry_mpi_t data_mpi = bytearray_to_mpi(env, data); + gcry_sexp_build(result, NULL, "(data (flags raw param) (value %M))", data_mpi); + gcry_mpi_release(data_mpi); + } else { + unsigned int hash_len = gcry_md_get_algo_dlen(hash_algo); + size_t data_len = (*env)->GetArrayLength(env, data); + jbyte *data_bytes = (*env)->GetByteArrayElements(env, data, NULL); + unsigned char out_hash[hash_len]; + gcry_md_hash_buffer(hash_algo, out_hash, data_bytes, data_len); + (*env)->ReleaseByteArrayElements(env, data, data_bytes, JNI_ABORT); + gcry_mpi_t hash_mpi; + gcry_mpi_scan(&hash_mpi, GCRYMPI_FMT_USG, out_hash, hash_len, NULL); + if (!sig_algo) { + gcry_sexp_build(result, NULL, "(data (flags raw param) (value %M))", hash_mpi); + } else { + gcry_sexp_build(result, NULL, "(data (flags %s param) (hash %s %M))", sig_algo, hash_name, hash_mpi); + } + gcry_mpi_release(hash_mpi); + } +} + +JNIEXPORT jbyteArray JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSignatureSpi_00024Gcrypt_sign(JNIEnv *env, jobject this, jbyteArray data, jbyteArray privkey, jobject params) { + jbyteArray result = NULL; + gcry_sexp_t priv_sexp = create_privkey(env, params, NULL, privkey); + + gcry_sexp_t data_sexp; + get_sign_data_sexp(env, &data_sexp, this, data); + + gcry_sexp_t res_sexp; + native_timing_start(); + gcry_error_t err = gcry_pk_sign(&res_sexp, data_sexp, priv_sexp); + native_timing_stop(); + if (gcry_err_code(err) != GPG_ERR_NO_ERROR) { + throw_new_var(env, "java/security/GeneralSecurityException", "Error performing ECDSA. Error: %ui", gcry_err_code(err)); + goto release_init; + } + + gcry_buffer_t r_buf = {0}; + gcry_buffer_t s_buf = {0}; + err = gcry_sexp_extract_param(res_sexp, "ecdsa", "&rs", &r_buf, &s_buf, NULL); + if (gcry_err_code(err) != GPG_ERR_NO_ERROR) { + throw_new_var(env, "java/security/GeneralSecurityException", "Error extracting ECDSA output. Error: %ui", gcry_err_code(err)); + goto release_res; + } + result = asn1_der_encode(env, r_buf.data, r_buf.len, s_buf.data, s_buf.len); + + gcry_free(r_buf.data); + gcry_free(s_buf.data); +release_res: + gcry_sexp_release(res_sexp); +release_init: + gcry_sexp_release(priv_sexp); + gcry_sexp_release(data_sexp); + return result; +} + +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSignatureSpi_00024Gcrypt_verify(JNIEnv *env, jobject this, jbyteArray sig, jbyteArray data, jbyteArray pubkey, jobject params) { + jboolean result = JNI_FALSE; + gcry_sexp_t pub_sexp = create_pubkey(env, params, pubkey); + + gcry_sexp_t data_sexp; + get_sign_data_sexp(env, &data_sexp, this, data); + + size_t r_len, s_len; + jbyte *r_data, *s_data; + bool decode = asn1_der_decode(env, sig, &r_data, &r_len, &s_data, &s_len); + if (!decode) { + throw_new(env, "java/security/GeneralSecurityException", "Error decoding sig."); + goto release_init; + } + + gcry_mpi_t r_mpi, s_mpi; + gcry_mpi_scan(&r_mpi, GCRYMPI_FMT_USG, r_data, r_len, NULL); + gcry_mpi_scan(&s_mpi, GCRYMPI_FMT_USG, s_data, s_len, NULL); + free(r_data); + free(s_data); + + gcry_sexp_t sig_sexp; + gcry_sexp_build(&sig_sexp, NULL, "(sig-val (ecdsa (r %M) (s %M)))", r_mpi, s_mpi); + + native_timing_start(); + gcry_error_t err = gcry_pk_verify(sig_sexp, data_sexp, pub_sexp); + native_timing_stop(); + + if (gcry_err_code(err) != GPG_ERR_NO_ERROR) { + if (gcry_err_code(err) != GPG_ERR_BAD_SIGNATURE) { + throw_new(env, "java/security/GeneralSecurityException", "Error verif sig."); + goto release_init; + } + } else { + result = JNI_TRUE; + } + +release_init: + gcry_sexp_release(pub_sexp); + gcry_sexp_release(data_sexp); + return result; +} + +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_GcryptLib_supportsNativeTiming(JNIEnv *env, jobject this) { + return native_timing_supported(); +} + +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_GcryptLib_getNativeTimingResolution(JNIEnv *env, jobject this) { + return native_timing_resolution(); +} + +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_GcryptLib_getLastNativeTiming(JNIEnv *env, jobject this) { + return native_timing_last(); +}
\ No newline at end of file diff --git a/src/cz/crcs/ectester/standalone/libs/jni/mscng.c b/src/cz/crcs/ectester/standalone/libs/jni/mscng.c index 5820afd..568e924 100644 --- a/src/cz/crcs/ectester/standalone/libs/jni/mscng.c +++ b/src/cz/crcs/ectester/standalone/libs/jni/mscng.c @@ -1211,4 +1211,16 @@ JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSigna throw_new_var(env, "java/security/GeneralSecurityException", "Error 0x%x returned by BCryptVerifySignature\n", status);
return JNI_FALSE;
}
+}
+
+JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_MscngLib_supportsNativeTiming(JNIEnv *env, jobject self) {
+ return JNI_FALSE;
+}
+
+JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_MscngLib_getNativeTimingResolution(JNIEnv *env, jobject self) {
+ return 0;
+}
+
+JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_MscngLib_getLastNativeTiming(JNIEnv *env, jobject self) {
+ return 0;
}
\ No newline at end of file diff --git a/src/cz/crcs/ectester/standalone/libs/jni/native.h b/src/cz/crcs/ectester/standalone/libs/jni/native.h index e3bf3d8..e410204 100644 --- a/src/cz/crcs/ectester/standalone/libs/jni/native.h +++ b/src/cz/crcs/ectester/standalone/libs/jni/native.h @@ -9,6 +9,30 @@ extern "C" { #endif /* * Class: cz_crcs_ectester_standalone_libs_TomcryptLib + * Method: supportsNativeTiming + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_TomcryptLib_supportsNativeTiming + (JNIEnv *, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_TomcryptLib + * Method: getNativeTimingResolution + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_TomcryptLib_getNativeTimingResolution + (JNIEnv *, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_TomcryptLib + * Method: getLastNativeTiming + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_TomcryptLib_getLastNativeTiming + (JNIEnv *, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_TomcryptLib * Method: createProvider * Signature: ()Ljava/security/Provider; */ @@ -190,6 +214,30 @@ extern "C" { #endif /* * Class: cz_crcs_ectester_standalone_libs_BotanLib + * Method: supportsNativeTiming + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_BotanLib_supportsNativeTiming + (JNIEnv *, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_BotanLib + * Method: getNativeTimingResolution + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_BotanLib_getNativeTimingResolution + (JNIEnv *, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_BotanLib + * Method: getLastNativeTiming + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_BotanLib_getLastNativeTiming + (JNIEnv *, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_BotanLib * Method: createProvider * Signature: ()Ljava/security/Provider; */ @@ -371,6 +419,30 @@ extern "C" { #endif /* * Class: cz_crcs_ectester_standalone_libs_CryptoppLib + * Method: supportsNativeTiming + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_CryptoppLib_supportsNativeTiming + (JNIEnv *, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_CryptoppLib + * Method: getNativeTimingResolution + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_CryptoppLib_getNativeTimingResolution + (JNIEnv *, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_CryptoppLib + * Method: getLastNativeTiming + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_CryptoppLib_getLastNativeTiming + (JNIEnv *, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_CryptoppLib * Method: createProvider * Signature: ()Ljava/security/Provider; */ @@ -552,6 +624,30 @@ extern "C" { #endif /* * Class: cz_crcs_ectester_standalone_libs_OpensslLib + * Method: supportsNativeTiming + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_OpensslLib_supportsNativeTiming + (JNIEnv *, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_OpensslLib + * Method: getNativeTimingResolution + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_OpensslLib_getNativeTimingResolution + (JNIEnv *, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_OpensslLib + * Method: getLastNativeTiming + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_OpensslLib_getLastNativeTiming + (JNIEnv *, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_OpensslLib * Method: createProvider * Signature: ()Ljava/security/Provider; */ @@ -733,6 +829,30 @@ extern "C" { #endif /* * Class: cz_crcs_ectester_standalone_libs_MscngLib + * Method: supportsNativeTiming + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_MscngLib_supportsNativeTiming + (JNIEnv *, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_MscngLib + * Method: getNativeTimingResolution + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_MscngLib_getNativeTimingResolution + (JNIEnv *, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_MscngLib + * Method: getLastNativeTiming + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_MscngLib_getLastNativeTiming + (JNIEnv *, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_MscngLib * Method: createProvider * Signature: ()Ljava/security/Provider; */ @@ -905,3 +1025,413 @@ JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSigna } #endif #endif +/* Header for class cz_crcs_ectester_standalone_libs_BoringsslLib */ + +#ifndef _Included_cz_crcs_ectester_standalone_libs_BoringsslLib +#define _Included_cz_crcs_ectester_standalone_libs_BoringsslLib +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: cz_crcs_ectester_standalone_libs_BoringsslLib + * Method: supportsNativeTiming + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_BoringsslLib_supportsNativeTiming + (JNIEnv *, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_BoringsslLib + * Method: getNativeTimingResolution + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_BoringsslLib_getNativeTimingResolution + (JNIEnv *, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_BoringsslLib + * Method: getLastNativeTiming + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_BoringsslLib_getLastNativeTiming + (JNIEnv *, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_BoringsslLib + * Method: createProvider + * Signature: ()Ljava/security/Provider; + */ +JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_BoringsslLib_createProvider + (JNIEnv *, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_BoringsslLib + * Method: getCurves + * Signature: ()Ljava/util/Set; + */ +JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_BoringsslLib_getCurves + (JNIEnv *, jobject); + +#ifdef __cplusplus +} +#endif +#endif +/* Header for class cz_crcs_ectester_standalone_libs_jni_NativeProvider_Boringssl */ + +#ifndef _Included_cz_crcs_ectester_standalone_libs_jni_NativeProvider_Boringssl +#define _Included_cz_crcs_ectester_standalone_libs_jni_NativeProvider_Boringssl +#ifdef __cplusplus +extern "C" { +#endif +#undef cz_crcs_ectester_standalone_libs_jni_NativeProvider_Boringssl_serialVersionUID +#define cz_crcs_ectester_standalone_libs_jni_NativeProvider_Boringssl_serialVersionUID 1421746759512286392LL +#undef cz_crcs_ectester_standalone_libs_jni_NativeProvider_Boringssl_MAX_ARRAY_SIZE +#define cz_crcs_ectester_standalone_libs_jni_NativeProvider_Boringssl_MAX_ARRAY_SIZE 2147483639L +#undef cz_crcs_ectester_standalone_libs_jni_NativeProvider_Boringssl_KEYS +#define cz_crcs_ectester_standalone_libs_jni_NativeProvider_Boringssl_KEYS 0L +#undef cz_crcs_ectester_standalone_libs_jni_NativeProvider_Boringssl_VALUES +#define cz_crcs_ectester_standalone_libs_jni_NativeProvider_Boringssl_VALUES 1L +#undef cz_crcs_ectester_standalone_libs_jni_NativeProvider_Boringssl_ENTRIES +#define cz_crcs_ectester_standalone_libs_jni_NativeProvider_Boringssl_ENTRIES 2L +#undef cz_crcs_ectester_standalone_libs_jni_NativeProvider_Boringssl_serialVersionUID +#define cz_crcs_ectester_standalone_libs_jni_NativeProvider_Boringssl_serialVersionUID 4112578634029874840LL +#undef cz_crcs_ectester_standalone_libs_jni_NativeProvider_Boringssl_serialVersionUID +#define cz_crcs_ectester_standalone_libs_jni_NativeProvider_Boringssl_serialVersionUID -4298000515446427739LL +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeProvider_Boringssl + * Method: setup + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeProvider_00024Boringssl_setup + (JNIEnv *, jobject); + +#ifdef __cplusplus +} +#endif +#endif +/* Header for class cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_Boringssl */ + +#ifndef _Included_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_Boringssl +#define _Included_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_Boringssl +#ifdef __cplusplus +extern "C" { +#endif +#undef cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_Boringssl_DEFAULT_KEYSIZE +#define cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_Boringssl_DEFAULT_KEYSIZE 256L +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_Boringssl + * Method: keysizeSupported + * Signature: (I)Z + */ +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_00024Boringssl_keysizeSupported + (JNIEnv *, jobject, jint); + +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_Boringssl + * Method: paramsSupported + * Signature: (Ljava/security/spec/AlgorithmParameterSpec;)Z + */ +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_00024Boringssl_paramsSupported + (JNIEnv *, jobject, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_Boringssl + * Method: generate + * Signature: (ILjava/security/SecureRandom;)Ljava/security/KeyPair; + */ +JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_00024Boringssl_generate__ILjava_security_SecureRandom_2 + (JNIEnv *, jobject, jint, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_Boringssl + * Method: generate + * Signature: (Ljava/security/spec/AlgorithmParameterSpec;Ljava/security/SecureRandom;)Ljava/security/KeyPair; + */ +JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_00024Boringssl_generate__Ljava_security_spec_AlgorithmParameterSpec_2Ljava_security_SecureRandom_2 + (JNIEnv *, jobject, jobject, jobject); + +#ifdef __cplusplus +} +#endif +#endif +/* Header for class cz_crcs_ectester_standalone_libs_jni_NativeECPublicKey_Boringssl */ + +#ifndef _Included_cz_crcs_ectester_standalone_libs_jni_NativeECPublicKey_Boringssl +#define _Included_cz_crcs_ectester_standalone_libs_jni_NativeECPublicKey_Boringssl +#ifdef __cplusplus +extern "C" { +#endif +#ifdef __cplusplus +} +#endif +#endif +/* Header for class cz_crcs_ectester_standalone_libs_jni_NativeECPrivateKey_Boringssl */ + +#ifndef _Included_cz_crcs_ectester_standalone_libs_jni_NativeECPrivateKey_Boringssl +#define _Included_cz_crcs_ectester_standalone_libs_jni_NativeECPrivateKey_Boringssl +#ifdef __cplusplus +extern "C" { +#endif +#ifdef __cplusplus +} +#endif +#endif +/* Header for class cz_crcs_ectester_standalone_libs_jni_NativeKeyAgreementSpi_Boringssl */ + +#ifndef _Included_cz_crcs_ectester_standalone_libs_jni_NativeKeyAgreementSpi_Boringssl +#define _Included_cz_crcs_ectester_standalone_libs_jni_NativeKeyAgreementSpi_Boringssl +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeKeyAgreementSpi_Boringssl + * Method: generateSecret + * Signature: ([B[BLjava/security/spec/ECParameterSpec;)[B + */ +JNIEXPORT jbyteArray JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyAgreementSpi_00024Boringssl_generateSecret___3B_3BLjava_security_spec_ECParameterSpec_2 + (JNIEnv *, jobject, jbyteArray, jbyteArray, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeKeyAgreementSpi_Boringssl + * Method: generateSecret + * Signature: ([B[BLjava/security/spec/ECParameterSpec;Ljava/lang/String;)Ljavax/crypto/SecretKey; + */ +JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyAgreementSpi_00024Boringssl_generateSecret___3B_3BLjava_security_spec_ECParameterSpec_2Ljava_lang_String_2 + (JNIEnv *, jobject, jbyteArray, jbyteArray, jobject, jstring); + +#ifdef __cplusplus +} +#endif +#endif +/* Header for class cz_crcs_ectester_standalone_libs_jni_NativeSignatureSpi_Boringssl */ + +#ifndef _Included_cz_crcs_ectester_standalone_libs_jni_NativeSignatureSpi_Boringssl +#define _Included_cz_crcs_ectester_standalone_libs_jni_NativeSignatureSpi_Boringssl +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeSignatureSpi_Boringssl + * Method: sign + * Signature: ([B[BLjava/security/spec/ECParameterSpec;)[B + */ +JNIEXPORT jbyteArray JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSignatureSpi_00024Boringssl_sign + (JNIEnv *, jobject, jbyteArray, jbyteArray, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeSignatureSpi_Boringssl + * Method: verify + * Signature: ([B[B[BLjava/security/spec/ECParameterSpec;)Z + */ +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSignatureSpi_00024Boringssl_verify + (JNIEnv *, jobject, jbyteArray, jbyteArray, jbyteArray, jobject); + +#ifdef __cplusplus +} +#endif +#endif +/* Header for class cz_crcs_ectester_standalone_libs_GcryptLib */ + +#ifndef _Included_cz_crcs_ectester_standalone_libs_GcryptLib +#define _Included_cz_crcs_ectester_standalone_libs_GcryptLib +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: cz_crcs_ectester_standalone_libs_GcryptLib + * Method: supportsNativeTiming + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_GcryptLib_supportsNativeTiming + (JNIEnv *, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_GcryptLib + * Method: getNativeTimingResolution + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_GcryptLib_getNativeTimingResolution + (JNIEnv *, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_GcryptLib + * Method: getLastNativeTiming + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_GcryptLib_getLastNativeTiming + (JNIEnv *, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_GcryptLib + * Method: createProvider + * Signature: ()Ljava/security/Provider; + */ +JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_GcryptLib_createProvider + (JNIEnv *, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_GcryptLib + * Method: getCurves + * Signature: ()Ljava/util/Set; + */ +JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_GcryptLib_getCurves + (JNIEnv *, jobject); + +#ifdef __cplusplus +} +#endif +#endif +/* Header for class cz_crcs_ectester_standalone_libs_jni_NativeProvider_Gcrypt */ + +#ifndef _Included_cz_crcs_ectester_standalone_libs_jni_NativeProvider_Gcrypt +#define _Included_cz_crcs_ectester_standalone_libs_jni_NativeProvider_Gcrypt +#ifdef __cplusplus +extern "C" { +#endif +#undef cz_crcs_ectester_standalone_libs_jni_NativeProvider_Gcrypt_serialVersionUID +#define cz_crcs_ectester_standalone_libs_jni_NativeProvider_Gcrypt_serialVersionUID 1421746759512286392LL +#undef cz_crcs_ectester_standalone_libs_jni_NativeProvider_Gcrypt_MAX_ARRAY_SIZE +#define cz_crcs_ectester_standalone_libs_jni_NativeProvider_Gcrypt_MAX_ARRAY_SIZE 2147483639L +#undef cz_crcs_ectester_standalone_libs_jni_NativeProvider_Gcrypt_KEYS +#define cz_crcs_ectester_standalone_libs_jni_NativeProvider_Gcrypt_KEYS 0L +#undef cz_crcs_ectester_standalone_libs_jni_NativeProvider_Gcrypt_VALUES +#define cz_crcs_ectester_standalone_libs_jni_NativeProvider_Gcrypt_VALUES 1L +#undef cz_crcs_ectester_standalone_libs_jni_NativeProvider_Gcrypt_ENTRIES +#define cz_crcs_ectester_standalone_libs_jni_NativeProvider_Gcrypt_ENTRIES 2L +#undef cz_crcs_ectester_standalone_libs_jni_NativeProvider_Gcrypt_serialVersionUID +#define cz_crcs_ectester_standalone_libs_jni_NativeProvider_Gcrypt_serialVersionUID 4112578634029874840LL +#undef cz_crcs_ectester_standalone_libs_jni_NativeProvider_Gcrypt_serialVersionUID +#define cz_crcs_ectester_standalone_libs_jni_NativeProvider_Gcrypt_serialVersionUID -4298000515446427739LL +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeProvider_Gcrypt + * Method: setup + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeProvider_00024Gcrypt_setup + (JNIEnv *, jobject); + +#ifdef __cplusplus +} +#endif +#endif +/* Header for class cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_Gcrypt */ + +#ifndef _Included_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_Gcrypt +#define _Included_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_Gcrypt +#ifdef __cplusplus +extern "C" { +#endif +#undef cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_Gcrypt_DEFAULT_KEYSIZE +#define cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_Gcrypt_DEFAULT_KEYSIZE 256L +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_Gcrypt + * Method: keysizeSupported + * Signature: (I)Z + */ +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_00024Gcrypt_keysizeSupported + (JNIEnv *, jobject, jint); + +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_Gcrypt + * Method: paramsSupported + * Signature: (Ljava/security/spec/AlgorithmParameterSpec;)Z + */ +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_00024Gcrypt_paramsSupported + (JNIEnv *, jobject, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_Gcrypt + * Method: generate + * Signature: (ILjava/security/SecureRandom;)Ljava/security/KeyPair; + */ +JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_00024Gcrypt_generate__ILjava_security_SecureRandom_2 + (JNIEnv *, jobject, jint, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_Gcrypt + * Method: generate + * Signature: (Ljava/security/spec/AlgorithmParameterSpec;Ljava/security/SecureRandom;)Ljava/security/KeyPair; + */ +JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_00024Gcrypt_generate__Ljava_security_spec_AlgorithmParameterSpec_2Ljava_security_SecureRandom_2 + (JNIEnv *, jobject, jobject, jobject); + +#ifdef __cplusplus +} +#endif +#endif +/* Header for class cz_crcs_ectester_standalone_libs_jni_NativeECPublicKey_Gcrypt */ + +#ifndef _Included_cz_crcs_ectester_standalone_libs_jni_NativeECPublicKey_Gcrypt +#define _Included_cz_crcs_ectester_standalone_libs_jni_NativeECPublicKey_Gcrypt +#ifdef __cplusplus +extern "C" { +#endif +#ifdef __cplusplus +} +#endif +#endif +/* Header for class cz_crcs_ectester_standalone_libs_jni_NativeECPrivateKey_Gcrypt */ + +#ifndef _Included_cz_crcs_ectester_standalone_libs_jni_NativeECPrivateKey_Gcrypt +#define _Included_cz_crcs_ectester_standalone_libs_jni_NativeECPrivateKey_Gcrypt +#ifdef __cplusplus +extern "C" { +#endif +#ifdef __cplusplus +} +#endif +#endif +/* Header for class cz_crcs_ectester_standalone_libs_jni_NativeKeyAgreementSpi_Gcrypt */ + +#ifndef _Included_cz_crcs_ectester_standalone_libs_jni_NativeKeyAgreementSpi_Gcrypt +#define _Included_cz_crcs_ectester_standalone_libs_jni_NativeKeyAgreementSpi_Gcrypt +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeKeyAgreementSpi_Gcrypt + * Method: generateSecret + * Signature: ([B[BLjava/security/spec/ECParameterSpec;)[B + */ +JNIEXPORT jbyteArray JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyAgreementSpi_00024Gcrypt_generateSecret___3B_3BLjava_security_spec_ECParameterSpec_2 + (JNIEnv *, jobject, jbyteArray, jbyteArray, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeKeyAgreementSpi_Gcrypt + * Method: generateSecret + * Signature: ([B[BLjava/security/spec/ECParameterSpec;Ljava/lang/String;)Ljavax/crypto/SecretKey; + */ +JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyAgreementSpi_00024Gcrypt_generateSecret___3B_3BLjava_security_spec_ECParameterSpec_2Ljava_lang_String_2 + (JNIEnv *, jobject, jbyteArray, jbyteArray, jobject, jstring); + +#ifdef __cplusplus +} +#endif +#endif +/* Header for class cz_crcs_ectester_standalone_libs_jni_NativeSignatureSpi_Gcrypt */ + +#ifndef _Included_cz_crcs_ectester_standalone_libs_jni_NativeSignatureSpi_Gcrypt +#define _Included_cz_crcs_ectester_standalone_libs_jni_NativeSignatureSpi_Gcrypt +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeSignatureSpi_Gcrypt + * Method: sign + * Signature: ([B[BLjava/security/spec/ECParameterSpec;)[B + */ +JNIEXPORT jbyteArray JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSignatureSpi_00024Gcrypt_sign + (JNIEnv *, jobject, jbyteArray, jbyteArray, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeSignatureSpi_Gcrypt + * Method: verify + * Signature: ([B[B[BLjava/security/spec/ECParameterSpec;)Z + */ +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSignatureSpi_00024Gcrypt_verify + (JNIEnv *, jobject, jbyteArray, jbyteArray, jbyteArray, jobject); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/cz/crcs/ectester/standalone/libs/jni/openssl.c b/src/cz/crcs/ectester/standalone/libs/jni/openssl.c index 255834a..a63c2fb 100644 --- a/src/cz/crcs/ectester/standalone/libs/jni/openssl.c +++ b/src/cz/crcs/ectester/standalone/libs/jni/openssl.c @@ -1,6 +1,5 @@ #include "native.h" #include <string.h> -#include <stdio.h> #include <openssl/conf.h> #include <openssl/opensslv.h> @@ -13,6 +12,8 @@ #include <openssl/ecdsa.h> #include "c_utils.h" +#include "c_timing.h" + static jclass provider_class; @@ -88,7 +89,7 @@ static jobject bignum_to_biginteger(JNIEnv *env, const BIGNUM *bn) { int size = BN_num_bytes(bn); jbyteArray bytes = (*env)->NewByteArray(env, size); jbyte *data = (*env)->GetByteArrayElements(env, bytes, NULL); - BN_bn2bin(bn, data); + BN_bn2bin(bn, (unsigned char *) data); (*env)->ReleaseByteArrayElements(env, bytes, data, 0); jobject result = (*env)->NewObject(env, biginteger_class, biginteger_init, 1, bytes); return result; @@ -100,7 +101,7 @@ static BIGNUM *biginteger_to_bignum(JNIEnv *env, jobject bigint) { jbyteArray byte_array = (jbyteArray) (*env)->CallObjectMethod(env, bigint, to_byte_array); jsize byte_length = (*env)->GetArrayLength(env, byte_array); jbyte *byte_data = (*env)->GetByteArrayElements(env, byte_array, NULL); - BIGNUM *result = BN_bin2bn(byte_data, byte_length, NULL); + BIGNUM *result = BN_bin2bn((unsigned char *) byte_data, byte_length, NULL); (*env)->ReleaseByteArrayElements(env, byte_array, byte_data, JNI_ABORT); return result; } @@ -112,10 +113,6 @@ static EC_GROUP *create_curve(JNIEnv *env, jobject params) { jmethodID get_field = (*env)->GetMethodID(env, elliptic_curve_class, "getField", "()Ljava/security/spec/ECField;"); jobject field = (*env)->CallObjectMethod(env, elliptic_curve, get_field); - jmethodID get_bits = (*env)->GetMethodID(env, fp_field_class, "getFieldSize", "()I"); - jint bits = (*env)->CallIntMethod(env, field, get_bits); - jint bytes = (bits + 7) / 8; - jmethodID get_a = (*env)->GetMethodID(env, elliptic_curve_class, "getA", "()Ljava/math/BigInteger;"); jobject a = (*env)->CallObjectMethod(env, elliptic_curve, get_a); BIGNUM *a_bn = biginteger_to_bignum(env, a); @@ -286,7 +283,7 @@ static jobject create_ec_param_spec(JNIEnv *env, const EC_GROUP *curve) { if (basis_type == NID_X9_62_tpBasis) { ks = (*env)->NewIntArray(env, 1); ks_data = (*env)->GetIntArrayElements(env, ks, NULL); - if (!EC_GROUP_get_trinomial_basis(curve, &ks_data[0])) { + if (!EC_GROUP_get_trinomial_basis(curve, (unsigned int *) &ks_data[0])) { throw_new(env, "java/security/InvalidAlgorithmParameterException", "Error creating ECParameterSpec, EC_GROUP_get_trinomial_basis."); BN_free(a); BN_free(b); (*env)->ReleaseIntArrayElements(env, ks, ks_data, JNI_ABORT); @@ -295,7 +292,7 @@ static jobject create_ec_param_spec(JNIEnv *env, const EC_GROUP *curve) { } else if (basis_type == NID_X9_62_ppBasis) { ks = (*env)->NewIntArray(env, 3); ks_data = (*env)->GetIntArrayElements(env, ks, NULL); - if (!EC_GROUP_get_pentanomial_basis(curve, &ks_data[0], &ks_data[1], &ks_data[2])) { + if (!EC_GROUP_get_pentanomial_basis(curve, (unsigned int *) &ks_data[0], (unsigned int *) &ks_data[1], (unsigned int *) &ks_data[2])) { throw_new(env, "java/security/InvalidAlgorithmParameterException", "Error creating ECParameterSpec, EC_GROUP_get_pentanomial_basis."); BN_free(a); BN_free(b); (*env)->ReleaseIntArrayElements(env, ks, ks_data, JNI_ABORT); @@ -327,7 +324,6 @@ static jobject create_ec_param_spec(JNIEnv *env, const EC_GROUP *curve) { jmethodID elliptic_curve_init = (*env)->GetMethodID(env, elliptic_curve_class, "<init>", "(Ljava/security/spec/ECField;Ljava/math/BigInteger;Ljava/math/BigInteger;)V"); jobject elliptic_curve = (*env)->NewObject(env, elliptic_curve_class, elliptic_curve_init, field, a_int, b_int); - fflush(stderr); BN_free(a); BN_free(b); @@ -354,7 +350,12 @@ static jobject generate_from_curve(JNIEnv *env, const EC_GROUP *curve) { EC_KEY *key = EC_KEY_new(); EC_KEY_set_group(key, curve); - if (!EC_KEY_generate_key(key)) { + + native_timing_start(); + int result = EC_KEY_generate_key(key); + native_timing_stop(); + + if (!result) { throw_new(env, "java/security/GeneralSecurityException", "Error generating key, EC_KEY_generate_key."); EC_KEY_free(key); return NULL; @@ -362,13 +363,13 @@ static jobject generate_from_curve(JNIEnv *env, const EC_GROUP *curve) { jbyteArray priv_bytes = (*env)->NewByteArray(env, key_bytes); jbyte *key_priv = (*env)->GetByteArrayElements(env, priv_bytes, NULL); - BN_bn2binpad(EC_KEY_get0_private_key(key), key_priv, key_bytes); + BN_bn2binpad(EC_KEY_get0_private_key(key), (unsigned char *) key_priv, key_bytes); (*env)->ReleaseByteArrayElements(env, priv_bytes, key_priv, 0); unsigned long key_len = 2*key_bytes + 1; jbyteArray pub_bytes = (*env)->NewByteArray(env, key_len); jbyte *key_pub = (*env)->GetByteArrayElements(env, pub_bytes, NULL); - EC_POINT_point2oct(curve, EC_KEY_get0_public_key(key), POINT_CONVERSION_UNCOMPRESSED, key_pub, key_len, NULL); + EC_POINT_point2oct(curve, EC_KEY_get0_public_key(key), POINT_CONVERSION_UNCOMPRESSED, (unsigned char *) key_pub, key_len, NULL); (*env)->ReleaseByteArrayElements(env, pub_bytes, key_pub, 0); EC_KEY_free(key); @@ -377,7 +378,7 @@ static jobject generate_from_curve(JNIEnv *env, const EC_GROUP *curve) { jobject ec_pub_param_spec = (*env)->NewLocalRef(env, ec_param_spec); jmethodID ec_pub_init = (*env)->GetMethodID(env, pubkey_class, "<init>", "([BLjava/security/spec/ECParameterSpec;)V"); - jobject pubkey = (*env)->NewObject(env, pubkey_class, ec_pub_init, pub_bytes, ec_param_spec); + jobject pubkey = (*env)->NewObject(env, pubkey_class, ec_pub_init, pub_bytes, ec_pub_param_spec); jobject ec_priv_param_spec = (*env)->NewLocalRef(env, ec_param_spec); jmethodID ec_priv_init = (*env)->GetMethodID(env, privkey_class, "<init>", "([BLjava/security/spec/ECParameterSpec;)V"); @@ -424,7 +425,7 @@ JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPai size_t ncurves = EC_get_builtin_curves(NULL, 0); EC_builtin_curve curves[ncurves]; EC_get_builtin_curves(curves, ncurves); - EC_GROUP *curve; + EC_GROUP *curve = NULL; for (size_t i = 0; i < ncurves; ++i) { if (strcasecmp(utf_name, OBJ_nid2sn(curves[i].nid)) == 0) { curve = EC_GROUP_new_by_curve_name(curves[i].nid); @@ -451,7 +452,7 @@ EC_KEY *barray_to_pubkey(JNIEnv *env, const EC_GROUP *curve, jbyteArray pub) { jsize pub_len = (*env)->GetArrayLength(env, pub); jbyte *pub_data = (*env)->GetByteArrayElements(env, pub, NULL); EC_POINT *pub_point = EC_POINT_new(curve); - EC_POINT_oct2point(curve, pub_point, pub_data, pub_len, NULL); + EC_POINT_oct2point(curve, pub_point, (unsigned char *) pub_data, pub_len, NULL); (*env)->ReleaseByteArrayElements(env, pub, pub_data, JNI_ABORT); EC_KEY_set_public_key(result, pub_point); EC_POINT_free(pub_point); @@ -463,7 +464,7 @@ EC_KEY *barray_to_privkey(JNIEnv *env, const EC_GROUP *curve, jbyteArray priv) EC_KEY_set_group(result, curve); jsize priv_len = (*env)->GetArrayLength(env, priv); jbyte *priv_data = (*env)->GetByteArrayElements(env, priv, NULL); - BIGNUM *s = BN_bin2bn(priv_data, priv_len, NULL); + BIGNUM *s = BN_bin2bn((unsigned char *) priv_data, priv_len, NULL); (*env)->ReleaseByteArrayElements(env, priv, priv_data, JNI_ABORT); EC_KEY_set_private_key(result, s); BN_free(s); @@ -487,7 +488,12 @@ JNIEXPORT jbyteArray JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKey // probably using the ECDH_KDF_X9_62 by wrapping it and dynamically choosing the EVP_MD. from the type string. jbyteArray result = (*env)->NewByteArray(env, secret_len); jbyte *result_data = (*env)->GetByteArrayElements(env, result, NULL); - if (ECDH_compute_key(result_data, secret_len, EC_KEY_get0_public_key(pub), priv, NULL) <= 0) { + + native_timing_start(); + int err = ECDH_compute_key(result_data, secret_len, EC_KEY_get0_public_key(pub), priv, NULL); + native_timing_stop(); + + if (err <= 0) { throw_new(env, "java/security/GeneralSecurityException", "Error computing ECDH, ECDH_compute_key."); EC_KEY_free(pub); EC_KEY_free(priv); EC_GROUP_free(curve); (*env)->ReleaseByteArrayElements(env, result, result_data, JNI_ABORT); @@ -518,7 +524,11 @@ JNIEXPORT jbyteArray JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSig jsize data_size = (*env)->GetArrayLength(env, data); jbyte *data_data = (*env)->GetByteArrayElements(env, data, NULL); // TODO: Do more Signatures here, maybe use the EVP interface to get to the hashes easier and not hash manually? - ECDSA_SIG *signature = ECDSA_do_sign(data_data, data_size, priv); + + native_timing_start(); + ECDSA_SIG *signature = ECDSA_do_sign((unsigned char *) data_data, data_size, priv); + native_timing_stop(); + (*env)->ReleaseByteArrayElements(env, data, data_data, JNI_ABORT); if (!signature) { throw_new(env, "java/security/GeneralSecurityException", "Error signing, ECDSA_do_sign."); @@ -556,7 +566,11 @@ JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSigna jsize data_size = (*env)->GetArrayLength(env, data); jbyte *data_data = (*env)->GetByteArrayElements(env, data, NULL); - int result = ECDSA_do_verify(data_data, data_size, sig_obj, pub); + + native_timing_start(); + int result = ECDSA_do_verify((unsigned char *) data_data, data_size, sig_obj, pub); + native_timing_stop(); + if (result < 0) { throw_new(env, "java/security/GeneralSecurityException", "Error verifying, ECDSA_do_verify."); EC_KEY_free(pub); EC_GROUP_free(curve); ECDSA_SIG_free(sig_obj); @@ -570,3 +584,15 @@ JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSigna EC_GROUP_free(curve); return (result == 1) ? JNI_TRUE : JNI_FALSE; } + +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_OpensslLib_supportsNativeTiming(JNIEnv *env, jobject this) { + return native_timing_supported(); +} + +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_OpensslLib_getNativeTimingResolution(JNIEnv *env, jobject this) { + return native_timing_resolution(); +} + +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_OpensslLib_getLastNativeTiming(JNIEnv *env, jobject this) { + return native_timing_last(); +}
\ No newline at end of file diff --git a/src/cz/crcs/ectester/standalone/libs/jni/tomcrypt.c b/src/cz/crcs/ectester/standalone/libs/jni/tomcrypt.c index fdf5663..471190e 100644 --- a/src/cz/crcs/ectester/standalone/libs/jni/tomcrypt.c +++ b/src/cz/crcs/ectester/standalone/libs/jni/tomcrypt.c @@ -3,6 +3,7 @@ #include <string.h> #include <tomcrypt.h> #include "c_utils.h" +#include "c_timing.h" static prng_state ltc_prng; static jclass provider_class; @@ -20,7 +21,6 @@ JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_TomcryptLib_crea return (*env)->NewObject(env, provider_class, init, name, version, name); } - JNIEXPORT void JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeProvider_00024TomCrypt_setup(JNIEnv *env, jobject this) { /* Initialize libtommath as the math lib. */ ltc_mp = ltm_desc; @@ -243,26 +243,30 @@ static void free_curve(ltc_ecc_set_type *curve) { static jobject generate_from_curve(JNIEnv *env, const ltc_ecc_set_type *curve) { ecc_key key; - int err; - if ((err = ecc_make_key_ex(<c_prng, find_prng("yarrow"), &key, curve)) != CRYPT_OK) { + + native_timing_start(); + int err = ecc_make_key_ex(<c_prng, find_prng("yarrow"), &key, curve); + native_timing_stop(); + + if (err != CRYPT_OK) { throw_new(env, "java/security/GeneralSecurityException", error_to_string(err)); return NULL; } unsigned long key_len = 2*curve->size + 1; jbyteArray pub_bytes = (*env)->NewByteArray(env, key_len); jbyte *key_pub = (*env)->GetByteArrayElements(env, pub_bytes, NULL); - ecc_ansi_x963_export(&key, key_pub, &key_len); + ecc_ansi_x963_export(&key, (unsigned char *) key_pub, &key_len); (*env)->ReleaseByteArrayElements(env, pub_bytes, key_pub, 0); jobject ec_param_spec = create_ec_param_spec(env, curve); jobject ec_pub_param_spec = (*env)->NewLocalRef(env, ec_param_spec); jmethodID ec_pub_init = (*env)->GetMethodID(env, pubkey_class, "<init>", "([BLjava/security/spec/ECParameterSpec;)V"); - jobject pubkey = (*env)->NewObject(env, pubkey_class, ec_pub_init, pub_bytes, ec_param_spec); + jobject pubkey = (*env)->NewObject(env, pubkey_class, ec_pub_init, pub_bytes, ec_pub_param_spec); jbyteArray priv_bytes = (*env)->NewByteArray(env, curve->size); jbyte *key_priv = (*env)->GetByteArrayElements(env, priv_bytes, NULL); - ltc_mp.unsigned_write(key.k, key_priv); + ltc_mp.unsigned_write(key.k, (unsigned char *) key_priv); (*env)->ReleaseByteArrayElements(env, priv_bytes, key_priv, 0); jobject ec_priv_param_spec = (*env)->NewLocalRef(env, ec_param_spec); @@ -334,7 +338,7 @@ static jboolean privkey_from_bytes(JNIEnv *env, jbyteArray privkey, const ltc_ec out->idx = -1; out->dp = curve; ltc_mp.init(&out->k); - ltc_mp.unsigned_read(out->k, priv_data, (unsigned long) curve->size); + ltc_mp.unsigned_read(out->k, (unsigned char *) priv_data, (unsigned long) curve->size); (*env)->ReleaseByteArrayElements(env, privkey, priv_data, JNI_ABORT); return JNI_TRUE; @@ -355,8 +359,8 @@ static jboolean pubkey_from_bytes(JNIEnv *env, jbyteArray pubkey, const ltc_ecc_ out->dp = curve; ltc_init_multi(&out->pubkey.x, &out->pubkey.y, &out->pubkey.z, NULL); ltc_mp.set_int(out->pubkey.z, 1); - ltc_mp.unsigned_read(out->pubkey.x, pub_data + 1, (unsigned long) curve->size); - ltc_mp.unsigned_read(out->pubkey.y, pub_data + 1 + curve->size, (unsigned long) curve->size); + ltc_mp.unsigned_read(out->pubkey.x, (unsigned char *) pub_data + 1, (unsigned long) curve->size); + ltc_mp.unsigned_read(out->pubkey.y, (unsigned char *) pub_data + 1 + curve->size, (unsigned long) curve->size); (*env)->ReleaseByteArrayElements(env, pubkey, pub_data, JNI_ABORT); @@ -380,8 +384,12 @@ JNIEXPORT jbyteArray JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKey unsigned char result[curve->size]; unsigned long output_len = curve->size; - int err; - if ((err = ecc_shared_secret(&priv, &pub, result, &output_len)) != CRYPT_OK) { + + native_timing_start(); + int err = ecc_shared_secret(&priv, &pub, result, &output_len); + native_timing_stop(); + + if (err != CRYPT_OK) { throw_new(env, "java/security/GeneralSecurityException", error_to_string(err)); free_curve(curve); return NULL; @@ -416,8 +424,12 @@ JNIEXPORT jbyteArray JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSig unsigned char result[curve->size*4]; unsigned long output_len = curve->size*4; - int err; - if ((err = ecc_sign_hash(data_data, data_size, result, &output_len, <c_prng, find_prng("yarrow"), &priv)) != CRYPT_OK) { + + native_timing_start(); + int err = ecc_sign_hash((unsigned char *) data_data, data_size, result, &output_len, <c_prng, find_prng("yarrow"), &priv); + native_timing_stop(); + + if (err != CRYPT_OK) { throw_new(env, "java/security/GeneralSecurityException", error_to_string(err)); free_curve(curve); (*env)->ReleaseByteArrayElements(env, data, data_data, JNI_ABORT); @@ -450,9 +462,12 @@ JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSigna jsize sig_size = (*env)->GetArrayLength(env, signature); jbyte *sig_data = (*env)->GetByteArrayElements(env, signature, NULL); - int err; int result; - if ((err = ecc_verify_hash(sig_data, sig_size, data_data, data_size, &result, &pub)) != CRYPT_OK) { + native_timing_start(); + int err = ecc_verify_hash((unsigned char *) sig_data, sig_size, (unsigned char *) data_data, data_size, &result, &pub); + native_timing_stop(); + + if (err != CRYPT_OK) { throw_new(env, "java/security/GeneralSecurityException", error_to_string(err)); free_curve(curve); (*env)->ReleaseByteArrayElements(env, data, data_data, JNI_ABORT); @@ -464,4 +479,16 @@ JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSigna (*env)->ReleaseByteArrayElements(env, signature, sig_data, JNI_ABORT); free_curve(curve); return result; +} + +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_TomcryptLib_supportsNativeTiming(JNIEnv *env, jobject this) { + return native_timing_supported(); +} + +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_TomcryptLib_getNativeTimingResolution(JNIEnv *env, jobject this) { + return native_timing_resolution(); +} + +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_TomcryptLib_getLastNativeTiming(JNIEnv *env, jobject this) { + return native_timing_last(); }
\ No newline at end of file diff --git a/src/cz/crcs/ectester/standalone/output/TextTestWriter.java b/src/cz/crcs/ectester/standalone/output/TextTestWriter.java index bf9ec7d..d7be4dc 100644 --- a/src/cz/crcs/ectester/standalone/output/TextTestWriter.java +++ b/src/cz/crcs/ectester/standalone/output/TextTestWriter.java @@ -33,10 +33,11 @@ public class TextTestWriter extends BaseTextTestWriter { protected String testableString(Testable t) { if (t instanceof StandaloneTestable) { StandaloneTestable<?> testable = (StandaloneTestable) t; - String stage = testable.getStage().name(); + Enum<?> stage = testable.getStage(); + String stageName = stage.name(); String exception = causeString(testable.getException()); String errorCause = causeString(testable.errorCause()); - return stage + exception + errorCause; + return String.format("[%d/%d] %s %s %s", stage.ordinal() + 1, stage.getClass().getEnumConstants().length, stageName, exception, errorCause); } return ""; } diff --git a/util/plot_dh.py b/util/plot_dh.py index 468e73a..60e20ae 100755 --- a/util/plot_dh.py +++ b/util/plot_dh.py @@ -18,10 +18,17 @@ import argparse from copy import deepcopy from operator import itemgetter +from utils import hw, moving_average, plot_hist + if __name__ == "__main__": parser = argparse.ArgumentParser(description="Plot ECTester ECDH 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("--skip-first", dest="skip_first", action="store_true", help="Skip first entry, as it's usually a large outlier.") + parser.add_argument("--priv", dest="priv", action="store_true", help="Show private key MSB heatmap plot.") + parser.add_argument("--hist", dest="hist", action="store_true", help="Show time histogram.") + parser.add_argument("--hw-hist", dest="hw_hist", action="store_true", help="Show Hamming weight heatmap (private key Hamming weight and time).") + parser.add_argument("--avg", dest="avg", action="store_true", help="Show moving average of time.") + parser.add_argument("--log", dest="log", action="store_true", help="Use logarithmic scale.") + parser.add_argument("--skip-first", dest="skip_first", nargs="?", const=1, type=int, help="Skip first entry, as it's usually a large outlier.") parser.add_argument("-t", "--title", dest="title", nargs="?", default="", type=str, help="What title to give the figure.") parser.add_argument("file", type=str, help="The file to plot(csv).") @@ -34,18 +41,17 @@ if __name__ == "__main__": hx = lambda x: int(x, 16) data = np.genfromtxt(opts.file, delimiter=";", skip_header=1, converters={2: hx, 3: hx, 4: hx}, dtype=np.dtype([("index","u4"), ("time","u4"), ("pub", "O"), ("priv", "O"), ("secret","O")])) if opts.skip_first: - data = data[1:] + data = data[opts.skip_first:] + time_data = data["time"] if "nano" in header_names[1]: unit = r"$\mu s$" - time_data = map(lambda x: x[1]//1000, data) + time_data = np.array(list(map(lambda x: x//1000, time_data))) else: unit = r"ms" - time_data = map(itemgetter(1), data) - time_data = list(time_data) - priv_data = list(map(itemgetter(2), data)) - pub_data = list(map(itemgetter(3), data)) - secret_data = list(map(itemgetter(4), data)) + priv_data = data["priv"] + pub_data = data["pub"] + secret_data = data["secret"] plt.style.use("ggplot") fig = plt.figure() @@ -58,43 +64,37 @@ if __name__ == "__main__": layout_kwargs["rect"] = [0, 0.02, 1, 0.98] fig.tight_layout(**layout_kwargs) - axe_hist = fig.add_subplot(2,1,1) time_max = max(time_data) - time_avg = np.average(time_data) - time_median = np.median(time_data) - axe_hist.hist(time_data, bins=time_max//3, log=True) - axe_hist.axvline(x=time_avg, alpha=0.7, linestyle="dotted", color="red", label="avg = {}".format(time_avg)) - axe_hist.axvline(x=time_median, alpha=0.7, linestyle="dotted", color="green", label="median = {}".format(time_median)) - axe_hist.set_ylabel("count\n(log)") - axe_hist.set_xlabel("time ({})".format(unit)) - axe_hist.xaxis.set_major_locator(ticker.MaxNLocator()) - axe_hist.legend(loc="best") + time_min = min(time_data) + bit_size = len(bin(max(priv_data))) - 2 - priv_bit_bins = {} - for i in range(len(data)): - skey = priv_data[i] - time = time_data[i] - skey_hw = 0 - while skey: - skey_hw += 1 - skey &= skey - 1 - if skey_hw in priv_bit_bins: - priv_bit_bins[skey_hw].append(time) - else: - priv_bit_bins[skey_hw] = [time] - priv_bit_x = [] - priv_bit_y = [] - for k,v in priv_bit_bins.items(): - priv_bit_x.extend([k] * len(v)) - priv_bit_y.extend(v) - - axe_priv_hist = fig.add_subplot(2,1,2) - h, xe, ye = np.histogram2d(priv_bit_x, priv_bit_y, bins=[max(priv_bit_bins) - min(priv_bit_bins), (time_max - min(time_data))//5]) cmap = deepcopy(plt.cm.plasma) cmap.set_bad("black") + + norm = colors.Normalize() + if opts.log: + norm = colors.LogNorm() + + axe_private = fig.add_subplot(3,1,1) + priv_msb = np.array(list(map(lambda x: x >> (bit_size - 8), priv_data)), dtype=np.dtype("u1")) + heatmap, xedges, yedges = np.histogram2d(priv_msb, time_data, bins=[128, time_max - time_min]) + extent = [xedges[0], xedges[-1], yedges[0], yedges[-1]] + axe_private.imshow(heatmap.T, extent=extent, aspect="auto", cmap=cmap, origin="low", interpolation="nearest", norm=norm) + axe_private.set_xlabel("private key MSB value") + axe_private.set_ylabel("ECDH time ({})".format(unit)) + + axe_hist = fig.add_subplot(3,1,2) + plot_hist(axe_hist, time_data, "ECDH time ({})".format(unit), opts.log) + axe_hist.legend(loc="best") + + axe_priv_hist = fig.add_subplot(3,1,3) + priv_hw = np.array(list(map(hw, priv_data)), dtype=np.dtype("u2")) + h, xe, ye = np.histogram2d(priv_hw, time_data, bins=[max(priv_hw) - min(priv_hw), time_max - time_min]) im = axe_priv_hist.imshow(h.T, origin="low", cmap=cmap, aspect="auto", extent=[xe[0], xe[-1], ye[0], ye[-1]], norm=colors.LogNorm()) + axe_priv_hist.axvline(x=bit_size//2, alpha=0.7, linestyle="dotted", color="white", label=str(bit_size//2) + " bits") axe_priv_hist.set_xlabel("private key Hamming weight") axe_priv_hist.set_ylabel("time ({})".format(unit)) + axe_priv_hist.legend(loc="best") fig.colorbar(im, ax=axe_priv_hist) fig.text(0.01, 0.02, "Data size: {}".format(len(time_data)), size="small") diff --git a/util/plot_gen.py b/util/plot_gen.py index 98d8261..0d518e6 100755 --- a/util/plot_gen.py +++ b/util/plot_gen.py @@ -12,21 +12,24 @@ # import numpy as np +from scipy.stats import entropy import matplotlib.pyplot as plt -import matplotlib.ticker as ticker -import matplotlib.colors as colors -from operator import itemgetter +from matplotlib import ticker, colors from copy import deepcopy import argparse +from utils import hw, moving_average, plot_hist + 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("--pub", dest="pub", action="store_true", help="Show public key scatter plot.") - parser.add_argument("--priv", dest="priv", action="store_true", help="Show private key scatter plot.") - parser.add_argument("--hist", dest="hist", action="store_true", help="Show histogram.") - parser.add_argument("--hw-hist", dest="hw_hist", action="store_true", help="Show Hamming weight 2D histogram (private key Hamming weight and generation time).") - parser.add_argument("--skip-first", dest="skip_first", action="store_true", help="Skip first entry, as it's usually a large outlier.") + parser.add_argument("--priv", dest="priv", action="store_true", help="Show private key MSB heatmap plot.") + 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.") + parser.add_argument("--hw-hist", dest="hw_hist", action="store_true", help="Show Hamming weight heatmap (private key Hamming weight and keygen time).") + parser.add_argument("--log", dest="log", action="store_true", help="Use logarithmic scale.") + parser.add_argument("--skip-first", dest="skip_first", nargs="?", const=1, type=int, help="Skip first entry, as it's usually a large outlier.") parser.add_argument("-t", "--title", dest="title", type=str, nargs="?", default="", help="What title to give the figure.") parser.add_argument("file", type=str, help="The file to plot(csv).") @@ -35,27 +38,53 @@ if __name__ == "__main__": with open(opts.file, "r") as f: header = f.readline() header_names = header.split(";") + if len(header_names) not in (4, 5): + print("Bad data?") + exit(1) - plots = [opts.priv, opts.pub, opts.hist, opts.hw_hist] + plots = [opts.priv, opts.hist, opts.export_hist, opts.avg, opts.hw_hist] n_plots = sum(plots) if n_plots == 0: - n_plots = 4 - plots = [True, True, True, True] + plots = [True for _ in range(5)] + if len(header_names) == 4: + n_plots = 4 + plots[2] = False + else: + n_plots = 5 + + + if plots[2] and len(header_names) != 5: + n_plots = n_plots - 1 + if n_plots == 0: + print("Nothing to plot.") + exit(1) + plots[2] = False hx = lambda x: int(x, 16) - data = np.genfromtxt(opts.file, delimiter=";", skip_header=1, converters={2: hx, 3: hx}, dtype=np.dtype([("index","u4"), ("time","u4"), ("pub", "O"), ("priv", "O")])) + if len(header_names) == 4: + data = np.genfromtxt(opts.file, delimiter=";", skip_header=1, converters={2: hx, 3: hx}, dtype=np.dtype([("index", "u4"), ("gen_time", "u4"), ("pub", "O"), ("priv", "O")])) + else: + data = np.genfromtxt(opts.file, delimiter=";", skip_header=1, converters={3: hx, 4: hx}, dtype=np.dtype([("index", "u4"), ("gen_time", "u4"), ("export_time", "u4"), ("pub", "O"), ("priv", "O")])) + if opts.skip_first: - data = data[1:] + data = data[opts.skip_first:] - if "nano" in header_names[1]: - unit = r"$\mu s$" - time_data = map(lambda x: x[1]//1000, data) - else: - unit = r"ms" - time_data = map(itemgetter(1), data) - time_data = list(time_data) - priv_data = list(map(itemgetter(2), data)) - pub_data = list(map(itemgetter(3), data)) + + gen_time_data = data["gen_time"] + export_time_data = None + if "export_time" in data.dtype.names: + export_time_data = data["export_time"] + pub_data = data["pub"] + priv_data = data["priv"] + + gen_unit = "ms" + if header_names[1].endswith("[nano]"): + gen_unit = r"$\mu s$" + np.floor_divide(gen_time_data, 1000, out=gen_time_data) + export_unit = "ms" + if len(header_names) == 5 and header_names[2].endswith("[nano]"): + export_unit = r"$\mu s$" + np.floor_divide(export_time_data, 1000, out=export_time_data) plt.style.use("ggplot") fig = plt.figure() @@ -66,66 +95,86 @@ if __name__ == "__main__": elif opts.title: fig.suptitle(opts.title) layout_kwargs["rect"] = [0, 0.02, 1, 0.98] - fig.tight_layout(**layout_kwargs) + fig.tight_layout(**layout_kwargs) + + max_gen_time = max(gen_time_data) + min_gen_time = min(gen_time_data) + bit_size = len(bin(max(priv_data))) - 2 + + 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") + + norm = colors.Normalize() + if opts.log: + norm = colors.LogNorm() plot_i = 1 if plots[0]: axe_private = fig.add_subplot(n_plots, 1, plot_i) - axe_private.scatter(time_data, priv_data, marker="x", s=10) - axe_private.set_ylabel("private key value\n(big endian)") - axe_private.set_xlabel("time ({})".format(unit)) + priv_msb = np.array(list(map(lambda x: x >> (bit_size - 8), priv_data)), dtype=np.dtype("u1")) + max_msb = max(priv_msb) + min_msb = min(priv_msb) + heatmap, xedges, yedges = np.histogram2d(priv_msb, gen_time_data, bins=[max_msb - min_msb, max_gen_time - min_gen_time]) + extent = [min_msb, max_msb, yedges[0], yedges[-1]] + axe_private.imshow(heatmap.T, extent=extent, aspect="auto", cmap=cmap, origin="low", interpolation="nearest", norm=norm) + axe_private.set_xlabel("private key MSB value") + axe_private.set_ylabel("keygen time ({})".format(gen_unit)) plot_i += 1 if plots[1]: - axe_public = fig.add_subplot(n_plots, 1, plot_i) - axe_public.scatter(time_data, pub_data, marker="x", s=10) - axe_public.set_ylabel("public key value\n(big endian)") - axe_public.set_xlabel("time ({})".format(unit)) + axe_hist = fig.add_subplot(n_plots, 1, plot_i) + plot_hist(axe_hist, gen_time_data, "keygen time ({})".format(gen_unit), opts.log) plot_i += 1 if plots[2]: axe_hist = fig.add_subplot(n_plots, 1, plot_i) - time_max = max(time_data) - time_avg = np.average(time_data) - time_median = np.median(time_data) - axe_hist.hist(time_data, bins=time_max//3, log=True) - axe_hist.axvline(x=time_avg, alpha=0.7, linestyle="dotted", color="red", label="avg = {}".format(time_avg)) - axe_hist.axvline(x=time_median, alpha=0.7, linestyle="dotted", color="green", label="median = {}".format(time_median)) - axe_hist.set_ylabel("count\n(log)") - axe_hist.set_xlabel("time ({})".format(unit)) - axe_hist.xaxis.set_major_locator(ticker.MaxNLocator()) - axe_hist.legend(loc="best") + plot_hist(axe_hist, export_time_data, "export time ({})".format(export_unit), opts.log) plot_i += 1 if plots[3]: - priv_bit_bins = {} - for i in range(len(data)): - skey = priv_data[i] - time = time_data[i] - skey_hw = 0 - while skey: - skey_hw += 1 - skey &= skey - 1 - if skey_hw in priv_bit_bins: - priv_bit_bins[skey_hw].append(time) - else: - priv_bit_bins[skey_hw] = [time] - priv_bit_x = [] - priv_bit_y = [] - for k,v in priv_bit_bins.items(): - priv_bit_x.extend([k] * len(v)) - priv_bit_y.extend(v) + axe_avg = fig.add_subplot(n_plots, 1, plot_i) + #if len(header_names) == 5: + # axe_other = axe_avg.twinx() + # axe_other.plot(moving_average(export_time_data, 100), color="green", alpha=0.6, label="export, window = 100") + # axe_other.plot(moving_average(export_time_data, 1000), color="yellow", alpha=0.6, label="export, window = 1000") + # axe_other.legend(loc="lower right") + axe_avg.plot(moving_average(gen_time_data, 100), label="window = 100") + axe_avg.plot(moving_average(gen_time_data, 1000), label="window = 1000") + axe_avg.set_ylabel("keygen time ({})".format(gen_unit)) + axe_avg.set_xlabel("index") + axe_avg.legend(loc="best") + plot_i += 1 + + if plots[4]: axe_priv_hist = fig.add_subplot(n_plots, 1, plot_i) - h, xe, ye = np.histogram2d(priv_bit_x, priv_bit_y, bins=[max(priv_bit_bins) - min(priv_bit_bins), (max(time_data) - min(time_data))//5]) - cmap = deepcopy(plt.cm.plasma) - cmap.set_bad("black") - im = axe_priv_hist.imshow(h.T, origin="low", cmap=cmap, aspect="auto", extent=[xe[0], xe[-1], ye[0], ye[-1]], norm=colors.LogNorm()) + priv_hw = np.array(list(map(hw, priv_data)), dtype=np.dtype("u2")) + h, xe, ye = np.histogram2d(priv_hw, gen_time_data, bins=[max(priv_hw) - min(priv_hw), max_gen_time - min_gen_time]) + im = axe_priv_hist.imshow(h.T, origin="low", cmap=cmap, aspect="auto", extent=[xe[0], xe[-1], ye[0], ye[-1]], norm=norm) + axe_priv_hist.axvline(x=bit_size//2, alpha=0.7, linestyle="dotted", color="white", label=str(bit_size//2) + " bits") axe_priv_hist.set_xlabel("private key Hamming weight") - axe_priv_hist.set_ylabel("time ({})".format(unit)) + axe_priv_hist.set_ylabel("keygen time ({})".format(gen_unit)) + axe_priv_hist.legend(loc="best") fig.colorbar(im, ax=axe_priv_hist) - if plot_i > 2: - fig.text(0.01, 0.02, "Data size: {}".format(len(time_data)), size="small") + 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.output is None: plt.show() |
