diff --git a/doc/crypt.tex b/doc/crypt.tex
index 437fca5de..50c973cfd 100644
--- a/doc/crypt.tex
+++ b/doc/crypt.tex
@@ -5549,6 +5549,22 @@ \subsection{Signature Generation}
This function creates the same ECDSA signature as \textit{ecc\_sign\_hash} only the output format is different.
The format follows \url{https://tools.ietf.org/html/rfc7518#section-3.4}, sometimes it is also called plain signature.
+\index{ecc\_sign\_hash_ex()}
+\begin{verbatim}
+int ecc_sign_hash_ex(const unsigned char *in,
+ unsigned long inlen,
+ unsigned char *out,
+ unsigned long *outlen,
+ prng_state *prng,
+ int wprng,
+ ecc_signature_type sigformat,
+ int *recid,
+ ecc_key *key);
+\end{verbatim}
+
+This function is an extended version of the ECDSA signature in \textit{ecc\_sign\_hash}, but with a choice of output formats
+and an optional output of the recovery ID for use with \textit{ecc\_recover\_key}.
+
\subsection{Signature Verification}
\index{ecc\_verify\_hash()}
\begin{verbatim}
@@ -5579,12 +5595,74 @@ \subsection{Signature Verification}
This function validate the ECDSA signature as \textit{ecc\_verify\_hash} only the signature input format
follows \url{https://tools.ietf.org/html/rfc7518#section-3.4}.
+\index{ecc\_verify\_hash_ex()}
+\begin{verbatim}
+int ecc_verify_hash_ex(const unsigned char *sig,
+ unsigned long siglen,
+ const unsigned char *hash,
+ unsigned long hashlen,
+ ecc_signature_type sigformat,
+ int *stat,
+ ecc_key *key);
+\end{verbatim}
+
+This function validates an ECDSA signature as \textit{ecc\_verify\_hash} but with a choice of signature formats.
+
{\bf BEWARE:} With ECC if you try to sign a hash that is bigger than your ECC key you can run into problems. The math
will still work, and in effect the signature will still work. With ECC keys the strength of the signature is limited
by the size of the hash, or the size of the key, whichever is smaller. For example, if you sign with SHA256 and a
P--192 key, you have in effect 96--bits of security. The library will not warn you if you make this mistake, so it
is important to check yourself before using the signatures.
+\subsection{Public Key Recovery}
+\index{ecc\_recover\_key()}
+\begin{verbatim}
+int ecc_recover_key(const unsigned char *sig,
+ unsigned long siglen,
+ const unsigned char *hash,
+ unsigned long hashlen,
+ int recid,
+ ecc_signature_type sigformat,
+ ecc_key *key);
+\end{verbatim}
+
+This function will recover (a) public key from the ECDSA signature in the array pointed to by \textit{sig} of length \textit{siglen} octets, the message digest
+pointed to by the array \textit{hash} of length \textit{hashlen}, and the recovery id \textit{recid}. It will store the recovered
+key into \textit{key} and return CRYPT\_OK if recovery succeeds, or an error if recovery fails.
+This is for compatibility with the (v,r,s) signatures used in Ethereum, where public keys are not explicitly shared,
+only the parity of the public key. For curves like secp256k1, recid will take values of 0 or 1, corresponding to the
+parity of the public key's y coordinate. For curves like secp112r2, with a cofactor of 4, values 0..7 are possible,
+with the low bit corresponding to the parity and the higher bits specifying the public key's x coordinate's multiple
+of the curve's order.
+If the signature format contains the recovery id (currently only \textit{LTC\_ECCSIG\_ETH27}), \textit{recid} can be -1
+which signals that the recovery id from the signature blob should be used. This means an application does not need to
+extract the recovery id from such a signature in order to use this function.
+The function \textit{ecc\_recover\_key} implements multiple signature formats, and the output is compliant for GF(p) curves.
+
+\subsection{Signature Formats}
+The following signature formats are suported:
+
+\begin{figure}[hpbt]
+\index{Signature Formats}
+\begin{small}
+\begin{center}
+\begin{tabular}{|l|l|}
+\hline \textbf{sigformat} & \textbf{description} \\
+\hline LTC\_ECCSIG\_ANSIX962 & ASN.1 encoded, ANSI X9.62 \\
+\hline LTC\_ECCSIG\_RFC7518 & raw R, S values as defined in RFC7518 \\
+\hline LTC\_ECCSIG\_ETH27 & raw R, S, V values (V has 27 added) \\
+\hline
+\end{tabular}
+\end{center}
+\end{small}
+\caption{Signature Formats}
+\label{fig:sigformat}
+\end{figure}
+
+The \textit{LTC\_ECCSIG\_ETH27} format is based on the Ethereum Yellow Paper, see \url{https://github.com/ethereum/yellowpaper}
+(Appendix F). However, convention allows the use of v=0,1 as equivalent to v=27,28 and both are accepted by
+\textit{ecc\_recover\_key}.
+
\mysection{Shared Secret (ECDH)}
To construct a Diffie-Hellman shared secret with a private and public ECC key, use the following function:
\index{ecc\_shared\_secret()}
diff --git a/libtomcrypt_VS2008.vcproj b/libtomcrypt_VS2008.vcproj
index c0e373bf7..e2f5016e4 100644
--- a/libtomcrypt_VS2008.vcproj
+++ b/libtomcrypt_VS2008.vcproj
@@ -2338,6 +2338,10 @@
RelativePath="src\pk\ecc\ecc_make_key.c"
>
+
+
diff --git a/makefile.mingw b/makefile.mingw
index 994f8c087..d6b271998 100644
--- a/makefile.mingw
+++ b/makefile.mingw
@@ -181,9 +181,9 @@ src/pk/ecc/ecc_ansi_x963_import.o src/pk/ecc/ecc_decrypt_key.o src/pk/ecc/ecc_en
src/pk/ecc/ecc_export.o src/pk/ecc/ecc_export_openssl.o src/pk/ecc/ecc_find_curve.o \
src/pk/ecc/ecc_free.o src/pk/ecc/ecc_get_key.o src/pk/ecc/ecc_get_oid_str.o src/pk/ecc/ecc_get_size.o \
src/pk/ecc/ecc_import.o src/pk/ecc/ecc_import_openssl.o src/pk/ecc/ecc_import_x509.o \
-src/pk/ecc/ecc_make_key.o src/pk/ecc/ecc_set_curve.o src/pk/ecc/ecc_set_curve_internal.o \
-src/pk/ecc/ecc_set_key.o src/pk/ecc/ecc_shared_secret.o src/pk/ecc/ecc_sign_hash.o \
-src/pk/ecc/ecc_sizes.o src/pk/ecc/ecc_test.o src/pk/ecc/ecc_verify_hash.o \
+src/pk/ecc/ecc_make_key.o src/pk/ecc/ecc_recover_key.o src/pk/ecc/ecc_set_curve.o \
+src/pk/ecc/ecc_set_curve_internal.o src/pk/ecc/ecc_set_key.o src/pk/ecc/ecc_shared_secret.o \
+src/pk/ecc/ecc_sign_hash.o src/pk/ecc/ecc_sizes.o src/pk/ecc/ecc_test.o src/pk/ecc/ecc_verify_hash.o \
src/pk/ecc/ltc_ecc_export_point.o src/pk/ecc/ltc_ecc_import_point.o src/pk/ecc/ltc_ecc_is_point.o \
src/pk/ecc/ltc_ecc_is_point_at_infinity.o src/pk/ecc/ltc_ecc_map.o src/pk/ecc/ltc_ecc_mul2add.o \
src/pk/ecc/ltc_ecc_mulmod.o src/pk/ecc/ltc_ecc_mulmod_timing.o src/pk/ecc/ltc_ecc_points.o \
diff --git a/makefile.msvc b/makefile.msvc
index 1aff6bb99..bdf9d4826 100644
--- a/makefile.msvc
+++ b/makefile.msvc
@@ -174,9 +174,9 @@ src/pk/ecc/ecc_ansi_x963_import.obj src/pk/ecc/ecc_decrypt_key.obj src/pk/ecc/ec
src/pk/ecc/ecc_export.obj src/pk/ecc/ecc_export_openssl.obj src/pk/ecc/ecc_find_curve.obj \
src/pk/ecc/ecc_free.obj src/pk/ecc/ecc_get_key.obj src/pk/ecc/ecc_get_oid_str.obj src/pk/ecc/ecc_get_size.obj \
src/pk/ecc/ecc_import.obj src/pk/ecc/ecc_import_openssl.obj src/pk/ecc/ecc_import_x509.obj \
-src/pk/ecc/ecc_make_key.obj src/pk/ecc/ecc_set_curve.obj src/pk/ecc/ecc_set_curve_internal.obj \
-src/pk/ecc/ecc_set_key.obj src/pk/ecc/ecc_shared_secret.obj src/pk/ecc/ecc_sign_hash.obj \
-src/pk/ecc/ecc_sizes.obj src/pk/ecc/ecc_test.obj src/pk/ecc/ecc_verify_hash.obj \
+src/pk/ecc/ecc_make_key.obj src/pk/ecc/ecc_recover_key.obj src/pk/ecc/ecc_set_curve.obj \
+src/pk/ecc/ecc_set_curve_internal.obj src/pk/ecc/ecc_set_key.obj src/pk/ecc/ecc_shared_secret.obj \
+src/pk/ecc/ecc_sign_hash.obj src/pk/ecc/ecc_sizes.obj src/pk/ecc/ecc_test.obj src/pk/ecc/ecc_verify_hash.obj \
src/pk/ecc/ltc_ecc_export_point.obj src/pk/ecc/ltc_ecc_import_point.obj src/pk/ecc/ltc_ecc_is_point.obj \
src/pk/ecc/ltc_ecc_is_point_at_infinity.obj src/pk/ecc/ltc_ecc_map.obj src/pk/ecc/ltc_ecc_mul2add.obj \
src/pk/ecc/ltc_ecc_mulmod.obj src/pk/ecc/ltc_ecc_mulmod_timing.obj src/pk/ecc/ltc_ecc_points.obj \
diff --git a/makefile.unix b/makefile.unix
index 94f2c6b0d..f7f18c414 100644
--- a/makefile.unix
+++ b/makefile.unix
@@ -191,9 +191,9 @@ src/pk/ecc/ecc_ansi_x963_import.o src/pk/ecc/ecc_decrypt_key.o src/pk/ecc/ecc_en
src/pk/ecc/ecc_export.o src/pk/ecc/ecc_export_openssl.o src/pk/ecc/ecc_find_curve.o \
src/pk/ecc/ecc_free.o src/pk/ecc/ecc_get_key.o src/pk/ecc/ecc_get_oid_str.o src/pk/ecc/ecc_get_size.o \
src/pk/ecc/ecc_import.o src/pk/ecc/ecc_import_openssl.o src/pk/ecc/ecc_import_x509.o \
-src/pk/ecc/ecc_make_key.o src/pk/ecc/ecc_set_curve.o src/pk/ecc/ecc_set_curve_internal.o \
-src/pk/ecc/ecc_set_key.o src/pk/ecc/ecc_shared_secret.o src/pk/ecc/ecc_sign_hash.o \
-src/pk/ecc/ecc_sizes.o src/pk/ecc/ecc_test.o src/pk/ecc/ecc_verify_hash.o \
+src/pk/ecc/ecc_make_key.o src/pk/ecc/ecc_recover_key.o src/pk/ecc/ecc_set_curve.o \
+src/pk/ecc/ecc_set_curve_internal.o src/pk/ecc/ecc_set_key.o src/pk/ecc/ecc_shared_secret.o \
+src/pk/ecc/ecc_sign_hash.o src/pk/ecc/ecc_sizes.o src/pk/ecc/ecc_test.o src/pk/ecc/ecc_verify_hash.o \
src/pk/ecc/ltc_ecc_export_point.o src/pk/ecc/ltc_ecc_import_point.o src/pk/ecc/ltc_ecc_is_point.o \
src/pk/ecc/ltc_ecc_is_point_at_infinity.o src/pk/ecc/ltc_ecc_map.o src/pk/ecc/ltc_ecc_mul2add.o \
src/pk/ecc/ltc_ecc_mulmod.o src/pk/ecc/ltc_ecc_mulmod_timing.o src/pk/ecc/ltc_ecc_points.o \
diff --git a/makefile_include.mk b/makefile_include.mk
index e3e1245a9..a656fe3dc 100644
--- a/makefile_include.mk
+++ b/makefile_include.mk
@@ -351,9 +351,9 @@ src/pk/ecc/ecc_ansi_x963_import.o src/pk/ecc/ecc_decrypt_key.o src/pk/ecc/ecc_en
src/pk/ecc/ecc_export.o src/pk/ecc/ecc_export_openssl.o src/pk/ecc/ecc_find_curve.o \
src/pk/ecc/ecc_free.o src/pk/ecc/ecc_get_key.o src/pk/ecc/ecc_get_oid_str.o src/pk/ecc/ecc_get_size.o \
src/pk/ecc/ecc_import.o src/pk/ecc/ecc_import_openssl.o src/pk/ecc/ecc_import_x509.o \
-src/pk/ecc/ecc_make_key.o src/pk/ecc/ecc_set_curve.o src/pk/ecc/ecc_set_curve_internal.o \
-src/pk/ecc/ecc_set_key.o src/pk/ecc/ecc_shared_secret.o src/pk/ecc/ecc_sign_hash.o \
-src/pk/ecc/ecc_sizes.o src/pk/ecc/ecc_test.o src/pk/ecc/ecc_verify_hash.o \
+src/pk/ecc/ecc_make_key.o src/pk/ecc/ecc_recover_key.o src/pk/ecc/ecc_set_curve.o \
+src/pk/ecc/ecc_set_curve_internal.o src/pk/ecc/ecc_set_key.o src/pk/ecc/ecc_shared_secret.o \
+src/pk/ecc/ecc_sign_hash.o src/pk/ecc/ecc_sizes.o src/pk/ecc/ecc_test.o src/pk/ecc/ecc_verify_hash.o \
src/pk/ecc/ltc_ecc_export_point.o src/pk/ecc/ltc_ecc_import_point.o src/pk/ecc/ltc_ecc_is_point.o \
src/pk/ecc/ltc_ecc_is_point_at_infinity.o src/pk/ecc/ltc_ecc_map.o src/pk/ecc/ltc_ecc_mul2add.o \
src/pk/ecc/ltc_ecc_mulmod.o src/pk/ecc/ltc_ecc_mulmod_timing.o src/pk/ecc/ltc_ecc_points.o \
diff --git a/src/headers/tomcrypt_pk.h b/src/headers/tomcrypt_pk.h
index 49014552c..bcb382f50 100644
--- a/src/headers/tomcrypt_pk.h
+++ b/src/headers/tomcrypt_pk.h
@@ -244,6 +244,16 @@ typedef struct {
void *k;
} ecc_key;
+/** Formats of ECC signatures */
+typedef enum ecc_signature_type_ {
+ /* ASN.1 encoded, ANSI X9.62 */
+ LTC_ECCSIG_ANSIX962 = 0x0,
+ /* raw R, S values */
+ LTC_ECCSIG_RFC7518 = 0x1,
+ /* raw R, S, V (+27) values */
+ LTC_ECCSIG_ETH27 = 0x2
+} ecc_signature_type;
+
/** the ECC params provided */
extern const ltc_ecc_curve ltc_ecc_curves[];
@@ -286,21 +296,30 @@ int ecc_decrypt_key(const unsigned char *in, unsigned long inlen,
unsigned char *out, unsigned long *outlen,
const ecc_key *key);
-int ecc_sign_hash_rfc7518(const unsigned char *in, unsigned long inlen,
- unsigned char *out, unsigned long *outlen,
- prng_state *prng, int wprng, const ecc_key *key);
+#define ecc_sign_hash_rfc7518(in_, inlen_, out_, outlen_, prng_, wprng_, key_) \
+ ecc_sign_hash_ex(in_, inlen_, out_, outlen_, prng_, wprng_, LTC_ECCSIG_RFC7518, NULL, key_)
+
+#define ecc_sign_hash(in_, inlen_, out_, outlen_, prng_, wprng_, key_) \
+ ecc_sign_hash_ex(in_, inlen_, out_, outlen_, prng_, wprng_, LTC_ECCSIG_ANSIX962, NULL, key_)
+
+#define ecc_verify_hash_rfc7518(sig_, siglen_, hash_, hashlen_, stat_, key_) \
+ ecc_verify_hash_ex(sig_, siglen_, hash_, hashlen_, LTC_ECCSIG_RFC7518, stat_, key_)
+
+#define ecc_verify_hash(sig_, siglen_, hash_, hashlen_, stat_, key_) \
+ ecc_verify_hash_ex(sig_, siglen_, hash_, hashlen_, LTC_ECCSIG_ANSIX962, stat_, key_)
-int ecc_sign_hash(const unsigned char *in, unsigned long inlen,
- unsigned char *out, unsigned long *outlen,
- prng_state *prng, int wprng, const ecc_key *key);
+int ecc_sign_hash_ex(const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen,
+ prng_state *prng, int wprng, ecc_signature_type sigformat,
+ int *recid, const ecc_key *key);
-int ecc_verify_hash_rfc7518(const unsigned char *sig, unsigned long siglen,
- const unsigned char *hash, unsigned long hashlen,
- int *stat, const ecc_key *key);
+int ecc_verify_hash_ex(const unsigned char *sig, unsigned long siglen,
+ const unsigned char *hash, unsigned long hashlen,
+ ecc_signature_type sigformat, int *stat, const ecc_key *key);
-int ecc_verify_hash(const unsigned char *sig, unsigned long siglen,
+int ecc_recover_key(const unsigned char *sig, unsigned long siglen,
const unsigned char *hash, unsigned long hashlen,
- int *stat, const ecc_key *key);
+ int recid, ecc_signature_type sigformat, ecc_key *key);
#endif
diff --git a/src/pk/ecc/ecc_recover_key.c b/src/pk/ecc/ecc_recover_key.c
new file mode 100644
index 000000000..25562a239
--- /dev/null
+++ b/src/pk/ecc/ecc_recover_key.c
@@ -0,0 +1,249 @@
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ */
+
+#include "tomcrypt_private.h"
+
+#ifdef LTC_MECC
+
+#ifdef LTC_ECC_SHAMIR
+
+/**
+ @file ecc_recover_key.c
+ ECC Crypto, Russ Williams
+*/
+
+/**
+ Recover ECC public key from signature and hash
+ @param sig The signature to verify
+ @param siglen The length of the signature (octets)
+ @param hash The hash (message digest) that was signed
+ @param hashlen The length of the hash (octets)
+ @param recid The recovery ID ("v"), can be -1 if signature contains it
+ @param sigformat The format of the signature (ecc_signature_type)
+ @param key The recovered public ECC key
+ @return CRYPT_OK if successful (even if the signature is not valid)
+*/
+int ecc_recover_key(const unsigned char *sig, unsigned long siglen,
+ const unsigned char *hash, unsigned long hashlen,
+ int recid, ecc_signature_type sigformat, ecc_key *key)
+{
+ ecc_point *mG = NULL, *mQ = NULL, *mR = NULL;
+ void *p, *m, *a, *b;
+ void *r, *s, *v, *w, *t1, *t2, *u1, *u2, *v1, *v2, *e, *x, *y, *a_plus3;
+ void *mu = NULL, *ma = NULL;
+ void *mp = NULL;
+ int err;
+ unsigned long pbits, pbytes, i, shift_right;
+ unsigned char ch, buf[MAXBLOCKSIZE];
+
+ LTC_ARGCHK(sig != NULL);
+ LTC_ARGCHK(hash != NULL);
+ LTC_ARGCHK(key != NULL);
+
+ /* BEWARE: requires sqrtmod_prime */
+ if (ltc_mp.sqrtmod_prime == NULL) {
+ return CRYPT_ERROR;
+ }
+
+ /* allocate ints */
+ if ((err = mp_init_multi(&r, &s, &v, &w, &t1, &t2, &u1, &u2, &v1, &v2, &e, &x, &y, &a_plus3, NULL)) != CRYPT_OK) {
+ return err;
+ }
+
+ p = key->dp.order;
+ m = key->dp.prime;
+ a = key->dp.A;
+ b = key->dp.B;
+ if ((err = mp_add_d(a, 3, a_plus3)) != CRYPT_OK) {
+ goto error;
+ }
+
+ /* allocate points */
+ mG = ltc_ecc_new_point();
+ mQ = ltc_ecc_new_point();
+ mR = ltc_ecc_new_point();
+ if (mR == NULL || mQ == NULL || mG == NULL) {
+ err = CRYPT_MEM;
+ goto error;
+ }
+
+ if (sigformat == LTC_ECCSIG_ANSIX962) {
+ /* ANSI X9.62 format - ASN.1 encoded SEQUENCE{ INTEGER(r), INTEGER(s) } */
+ if ((err = der_decode_sequence_multi_ex(sig, siglen, LTC_DER_SEQ_SEQUENCE | LTC_DER_SEQ_STRICT,
+ LTC_ASN1_INTEGER, 1UL, r,
+ LTC_ASN1_INTEGER, 1UL, s,
+ LTC_ASN1_EOL, 0UL, NULL)) != CRYPT_OK) { goto error; }
+ }
+ else if (sigformat == LTC_ECCSIG_RFC7518) {
+ /* RFC7518 format - raw (r,s) */
+ i = mp_unsigned_bin_size(key->dp.order);
+ if (siglen != (2*i)) {
+ err = CRYPT_INVALID_PACKET;
+ goto error;
+ }
+ if ((err = mp_read_unsigned_bin(r, (unsigned char *)sig, i)) != CRYPT_OK) { goto error; }
+ if ((err = mp_read_unsigned_bin(s, (unsigned char *)sig+i, i)) != CRYPT_OK) { goto error; }
+ }
+ else if (sigformat == LTC_ECCSIG_ETH27) {
+ /* Ethereum (v,r,s) format */
+ if (key->dp.oidlen != 5 || key->dp.oid[0] != 1 || key->dp.oid[1] != 3 ||
+ key->dp.oid[2] != 132 || key->dp.oid[3] != 0 || key->dp.oid[4] != 10) {
+ /* Only valid for secp256k1 - OID 1.3.132.0.10 */
+ err = CRYPT_ERROR; goto error;
+ }
+ if (siglen != 65) { /* Only secp256k1 curves use this format, so must be 65 bytes long */
+ err = CRYPT_INVALID_PACKET;
+ goto error;
+ }
+ i = (unsigned long)sig[64];
+ if ((i>=27) && (i<31)) i -= 27; /* Ethereum adds 27 to recovery ID */
+ if (recid >= 0 && ((unsigned long)recid != i)) {
+ /* Recovery ID specified, but doesn't match signature */
+ err = CRYPT_INVALID_PACKET;
+ goto error;
+ }
+ recid = i;
+ if ((err = mp_read_unsigned_bin(r, (unsigned char *)sig, 32)) != CRYPT_OK) { goto error; }
+ if ((err = mp_read_unsigned_bin(s, (unsigned char *)sig+32, 32)) != CRYPT_OK) { goto error; }
+ }
+ else {
+ /* Unknown signature format */
+ err = CRYPT_ERROR;
+ goto error;
+ }
+
+ if (recid < 0 || (unsigned long)recid >= 2*(key->dp.cofactor+1)) {
+ /* Recovery ID is out of range, reject it */
+ err = CRYPT_INVALID_ARG;
+ goto error;
+ }
+
+ /* check for zero */
+ if (mp_cmp_d(r, 0) != LTC_MP_GT || mp_cmp_d(s, 0) != LTC_MP_GT ||
+ mp_cmp(r, p) != LTC_MP_LT || mp_cmp(s, p) != LTC_MP_LT) {
+ err = CRYPT_INVALID_PACKET;
+ goto error;
+ }
+
+ /* read hash - truncate if needed */
+ pbits = mp_count_bits(p);
+ pbytes = (pbits+7) >> 3;
+ if (pbits > hashlen*8) {
+ if ((err = mp_read_unsigned_bin(e, (unsigned char *)hash, hashlen)) != CRYPT_OK) { goto error; }
+ }
+ else if (pbits % 8 == 0) {
+ if ((err = mp_read_unsigned_bin(e, (unsigned char *)hash, pbytes)) != CRYPT_OK) { goto error; }
+ }
+ else {
+ shift_right = 8 - pbits % 8;
+ for (i=0, ch=0; i> shift_right);
+ }
+ if ((err = mp_read_unsigned_bin(e, (unsigned char *)buf, pbytes)) != CRYPT_OK) { goto error; }
+ }
+
+ /* decompress point from r=(x mod p) - BEWARE: requires sqrtmod_prime */
+ /* x = r + p*(recid/2) */
+ if ((err = mp_set(x, recid/2)) != CRYPT_OK) { goto error; }
+ if ((err = mp_mulmod(p, x, m, x)) != CRYPT_OK) { goto error; }
+ if ((err = mp_add(x, r, x)) != CRYPT_OK) { goto error; }
+ /* compute x^3 */
+ if ((err = mp_sqr(x, t1)) != CRYPT_OK) { goto error; }
+ if ((err = mp_mulmod(t1, x, m, t1)) != CRYPT_OK) { goto error; }
+ /* compute x^3 + a*x */
+ if ((err = mp_mulmod(a, x, m, t2)) != CRYPT_OK) { goto error; }
+ if ((err = mp_add(t1, t2, t1)) != CRYPT_OK) { goto error; }
+ /* compute x^3 + a*x + b */
+ if ((err = mp_add(t1, b, t1)) != CRYPT_OK) { goto error; }
+ /* compute sqrt(x^3 + a*x + b) */
+ if ((err = mp_sqrtmod_prime(t1, m, t2)) != CRYPT_OK) { goto error; }
+
+ /* fill in mR */
+ if ((err = mp_copy(x, mR->x)) != CRYPT_OK) { goto error; }
+ if ((mp_isodd(t2) && (recid%2)) || (!mp_isodd(t2) && !(recid%2))) {
+ if ((err = mp_mod(t2, m, mR->y)) != CRYPT_OK) { goto error; }
+ }
+ else {
+ if ((err = mp_submod(m, t2, m, mR->y)) != CRYPT_OK) { goto error; }
+ }
+ if ((err = mp_set(mR->z, 1)) != CRYPT_OK) { goto error; }
+
+ /* w = r^-1 mod n */
+ if ((err = mp_invmod(r, p, w)) != CRYPT_OK) { goto error; }
+ /* v1 = sw */
+ if ((err = mp_mulmod(s, w, p, v1)) != CRYPT_OK) { goto error; }
+ /* v2 = -ew */
+ if ((err = mp_mulmod(e, w, p, v2)) != CRYPT_OK) { goto error; }
+ if ((err = mp_submod(p, v2, p, v2)) != CRYPT_OK) { goto error; }
+
+ /* w = s^-1 mod n */
+ if ((err = mp_invmod(s, p, w)) != CRYPT_OK) { goto error; }
+ /* u1 = ew */
+ if ((err = mp_mulmod(e, w, p, u1)) != CRYPT_OK) { goto error; }
+ /* u2 = rw */
+ if ((err = mp_mulmod(r, w, p, u2)) != CRYPT_OK) { goto error; }
+
+ /* find mG */
+ if ((err = ltc_ecc_copy_point(&key->dp.base, mG)) != CRYPT_OK) { goto error; }
+
+ /* find the montgomery mp */
+ if ((err = mp_montgomery_setup(m, &mp)) != CRYPT_OK) { goto error; }
+
+ /* for curves with a == -3 keep ma == NULL */
+ if (mp_cmp(a_plus3, m) != LTC_MP_EQ) {
+ if ((err = mp_init_multi(&mu, &ma, NULL)) != CRYPT_OK) { goto error; }
+ if ((err = mp_montgomery_normalization(mu, m)) != CRYPT_OK) { goto error; }
+ if ((err = mp_mulmod(a, mu, m, ma)) != CRYPT_OK) { goto error; }
+ }
+
+ /* recover mQ from mR */
+ /* compute v1*mR + v2*mG = mQ using Shamir's trick */
+ if ((err = ltc_mp.ecc_mul2add(mR, v1, mG, v2, mQ, ma, m)) != CRYPT_OK) { goto error; }
+
+ /* compute u1*mG + u2*mQ = mG using Shamir's trick */
+ if ((err = ltc_mp.ecc_mul2add(mG, u1, mQ, u2, mG, ma, m)) != CRYPT_OK) { goto error; }
+
+ /* v = X_x1 mod n */
+ if ((err = mp_mod(mG->x, p, v)) != CRYPT_OK) { goto error; }
+
+ /* does v == r */
+ if (mp_cmp(v, r) == LTC_MP_EQ) {
+ /* found public key which verifies signature */
+ if ((err = ltc_ecc_copy_point(mQ, &key->pubkey)) != CRYPT_OK) { goto error; }
+ /* point on the curve + other checks */
+ if ((err = ltc_ecc_verify_key(key)) != CRYPT_OK) { goto error; }
+
+ key->type = PK_PUBLIC;
+
+ err = CRYPT_OK;
+ }
+ else {
+ /* not found - recid is wrong or we're unable to calculate public key for some other reason */
+ err = CRYPT_INVALID_ARG;
+ }
+
+error:
+ if (ma != NULL) mp_clear(ma);
+ if (mu != NULL) mp_clear(mu);
+ if (mp != NULL) mp_montgomery_free(mp);
+ if (mR != NULL) ltc_ecc_del_point(mR);
+ if (mQ != NULL) ltc_ecc_del_point(mQ);
+ if (mG != NULL) ltc_ecc_del_point(mG);
+ mp_clear_multi(a_plus3, y, x, e, v2, v1, u2, u1, t2, t1, w, v, s, r, NULL);
+ return err;
+}
+
+#endif
+#endif
+
+/* ref: $Format:%D$ */
+/* git commit: $Format:%H$ */
+/* commit time: $Format:%ai$ */
diff --git a/src/pk/ecc/ecc_sign_hash.c b/src/pk/ecc/ecc_sign_hash.c
index 3302b0772..6764dae66 100644
--- a/src/pk/ecc/ecc_sign_hash.c
+++ b/src/pk/ecc/ecc_sign_hash.c
@@ -16,12 +16,27 @@
ECC Crypto, Tom St Denis
*/
-static int _ecc_sign_hash(const unsigned char *in, unsigned long inlen,
- unsigned char *out, unsigned long *outlen,
- prng_state *prng, int wprng, const ecc_key *key, int sigformat)
+/**
+ Sign a message digest
+ @param in The message digest to sign
+ @param inlen The length of the digest
+ @param out [out] The destination for the signature
+ @param outlen [in/out] The max size and resulting size of the signature
+ @param prng An active PRNG state
+ @param wprng The index of the PRNG you wish to use
+ @param sigformat The format of the signature to generate (ecc_signature_type)
+ @param recid [out] The recovery ID for this signature (optional)
+ @param key A private ECC key
+ @return CRYPT_OK if successful
+*/
+int ecc_sign_hash_ex(const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen,
+ prng_state *prng, int wprng, ecc_signature_type sigformat,
+ int *recid, const ecc_key *key)
{
ecc_key pubkey;
void *r, *s, *e, *p, *b;
+ int v = 0;
int err, max_iterations = LTC_PK_MAX_RETRIES;
unsigned long pbits, pbytes, i, shift_right;
unsigned char ch, buf[MAXBLOCKSIZE];
@@ -69,6 +84,18 @@ static int _ecc_sign_hash(const unsigned char *in, unsigned long inlen,
/* find r = x1 mod n */
if ((err = mp_mod(pubkey.pubkey.x, p, r)) != CRYPT_OK) { goto error; }
+ if (recid || sigformat==LTC_ECCSIG_ETH27) {
+ /* find recovery ID (if needed) */
+ v = 0;
+ if (mp_copy(pubkey.pubkey.x, s) != CRYPT_OK) { goto error; }
+ while (mp_cmp_d(s, 0) == LTC_MP_GT && mp_cmp(s, p) != LTC_MP_LT) {
+ /* Compute x1 div n... this will almost never be reached for curves with order 1 */
+ v += 2;
+ if ((err = mp_sub(s, p, s)) != CRYPT_OK) { goto error; }
+ }
+ if (mp_isodd(pubkey.pubkey.y)) v += 1;
+ }
+
if (mp_iszero(r) == LTC_MP_YES) {
ecc_free(&pubkey);
} else {
@@ -92,8 +119,17 @@ static int _ecc_sign_hash(const unsigned char *in, unsigned long inlen,
goto errnokey;
}
- if (sigformat == 1) {
- /* RFC7518 format */
+ if (recid) *recid = v;
+
+ if (sigformat == LTC_ECCSIG_ANSIX962) {
+ /* store as ASN.1 SEQUENCE { r, s -- integer } */
+ err = der_encode_sequence_multi(out, outlen,
+ LTC_ASN1_INTEGER, 1UL, r,
+ LTC_ASN1_INTEGER, 1UL, s,
+ LTC_ASN1_EOL, 0UL, NULL);
+ }
+ else if (sigformat == LTC_ECCSIG_RFC7518) {
+ /* RFC7518 format - raw (r,s) */
if (*outlen < 2*pbytes) { err = CRYPT_MEM; goto errnokey; }
zeromem(out, 2*pbytes);
i = mp_unsigned_bin_size(r);
@@ -103,13 +139,29 @@ static int _ecc_sign_hash(const unsigned char *in, unsigned long inlen,
*outlen = 2*pbytes;
err = CRYPT_OK;
}
+ else if (sigformat == LTC_ECCSIG_ETH27) {
+ /* Ethereum (v,r,s) format */
+ if (key->dp.oidlen != 5 || key->dp.oid[0] != 1 || key->dp.oid[1] != 3 ||
+ key->dp.oid[2] != 132 || key->dp.oid[3] != 0 || key->dp.oid[4] != 10) {
+ /* Only valid for secp256k1 - OID 1.3.132.0.10 */
+ err = CRYPT_ERROR; goto errnokey;
+ }
+ if (*outlen < 65) { err = CRYPT_MEM; goto errnokey; }
+ zeromem(out, 65);
+ i = mp_unsigned_bin_size(r);
+ if ((err = mp_to_unsigned_bin(r, out + 32 - i)) != CRYPT_OK) { goto errnokey; }
+ i = mp_unsigned_bin_size(s);
+ if ((err = mp_to_unsigned_bin(s, out + 64 - i)) != CRYPT_OK) { goto errnokey; }
+ out[64] = (unsigned char)(v + 27); /* Recovery ID is 27/28 for Ethereum */
+ *outlen = 65;
+ err = CRYPT_OK;
+ }
else {
- /* store as ASN.1 SEQUENCE { r, s -- integer } */
- err = der_encode_sequence_multi(out, outlen,
- LTC_ASN1_INTEGER, 1UL, r,
- LTC_ASN1_INTEGER, 1UL, s,
- LTC_ASN1_EOL, 0UL, NULL);
+ /* Unknown signature format */
+ err = CRYPT_ERROR;
+ goto error;
}
+
goto errnokey;
error:
ecc_free(&pubkey);
@@ -118,42 +170,6 @@ static int _ecc_sign_hash(const unsigned char *in, unsigned long inlen,
return err;
}
-/**
- Sign a message digest
- @param in The message digest to sign
- @param inlen The length of the digest
- @param out [out] The destination for the signature
- @param outlen [in/out] The max size and resulting size of the signature
- @param prng An active PRNG state
- @param wprng The index of the PRNG you wish to use
- @param key A private ECC key
- @return CRYPT_OK if successful
-*/
-int ecc_sign_hash(const unsigned char *in, unsigned long inlen,
- unsigned char *out, unsigned long *outlen,
- prng_state *prng, int wprng, const ecc_key *key)
-{
- return _ecc_sign_hash(in, inlen, out, outlen, prng, wprng, key, 0);
-}
-
-/**
- Sign a message digest in RFC7518 format
- @param in The message digest to sign
- @param inlen The length of the digest
- @param out [out] The destination for the signature
- @param outlen [in/out] The max size and resulting size of the signature
- @param prng An active PRNG state
- @param wprng The index of the PRNG you wish to use
- @param key A private ECC key
- @return CRYPT_OK if successful
-*/
-int ecc_sign_hash_rfc7518(const unsigned char *in, unsigned long inlen,
- unsigned char *out, unsigned long *outlen,
- prng_state *prng, int wprng, const ecc_key *key)
-{
- return _ecc_sign_hash(in, inlen, out, outlen, prng, wprng, key, 1);
-}
-
#endif
/* ref: $Format:%D$ */
diff --git a/src/pk/ecc/ecc_verify_hash.c b/src/pk/ecc/ecc_verify_hash.c
index f2a58940b..d18ef9349 100644
--- a/src/pk/ecc/ecc_verify_hash.c
+++ b/src/pk/ecc/ecc_verify_hash.c
@@ -16,12 +16,24 @@
ECC Crypto, Tom St Denis
*/
-static int _ecc_verify_hash(const unsigned char *sig, unsigned long siglen,
- const unsigned char *hash, unsigned long hashlen,
- int *stat, const ecc_key *key, int sigformat)
+/**
+ Verify an ECC signature in RFC7518 format
+ @param sig The signature to verify
+ @param siglen The length of the signature (octets)
+ @param hash The hash (message digest) that was signed
+ @param hashlen The length of the hash (octets)
+ @param sigformat The format of the signature (ecc_signature_type)
+ @param stat Result of signature, 1==valid, 0==invalid
+ @param key The corresponding public ECC key
+ @return CRYPT_OK if successful (even if the signature is not valid)
+*/
+int ecc_verify_hash_ex(const unsigned char *sig, unsigned long siglen,
+ const unsigned char *hash, unsigned long hashlen,
+ ecc_signature_type sigformat, int *stat, const ecc_key *key)
{
- ecc_point *mG = NULL, *mQ = NULL;
- void *r, *s, *v, *w, *u1, *u2, *e, *p, *m, *a, *a_plus3 = NULL, *mu = NULL, *ma = NULL;
+ ecc_point *mG = NULL, *mQ = NULL;
+ void *r, *s, *v, *w, *u1, *u2, *e, *p, *m, *a, *a_plus3;
+ void *mu = NULL, *ma = NULL;
void *mp = NULL;
int err;
unsigned long pbits, pbytes, i, shift_right;
@@ -55,22 +67,41 @@ static int _ecc_verify_hash(const unsigned char *sig, unsigned long siglen,
goto error;
}
- if (sigformat == 1) {
- /* RFC7518 format */
- if ((siglen % 2) == 1) {
+ if (sigformat == LTC_ECCSIG_ANSIX962) {
+ /* ANSI X9.62 format - ASN.1 encoded SEQUENCE{ INTEGER(r), INTEGER(s) } */
+ if ((err = der_decode_sequence_multi_ex(sig, siglen, LTC_DER_SEQ_SEQUENCE | LTC_DER_SEQ_STRICT,
+ LTC_ASN1_INTEGER, 1UL, r,
+ LTC_ASN1_INTEGER, 1UL, s,
+ LTC_ASN1_EOL, 0UL, NULL)) != CRYPT_OK) { goto error; }
+ }
+ else if (sigformat == LTC_ECCSIG_RFC7518) {
+ /* RFC7518 format - raw (r,s) */
+ i = mp_unsigned_bin_size(key->dp.order);
+ if (siglen != (2*i)) {
err = CRYPT_INVALID_PACKET;
goto error;
}
- i = siglen / 2;
if ((err = mp_read_unsigned_bin(r, (unsigned char *)sig, i)) != CRYPT_OK) { goto error; }
if ((err = mp_read_unsigned_bin(s, (unsigned char *)sig+i, i)) != CRYPT_OK) { goto error; }
}
+ else if (sigformat == LTC_ECCSIG_ETH27) {
+ /* Ethereum (v,r,s) format */
+ if (key->dp.oidlen != 5 || key->dp.oid[0] != 1 || key->dp.oid[1] != 3 ||
+ key->dp.oid[2] != 132 || key->dp.oid[3] != 0 || key->dp.oid[4] != 10) {
+ /* Only valid for secp256k1 - OID 1.3.132.0.10 */
+ err = CRYPT_ERROR; goto error;
+ }
+ if (siglen != 65) { /* Only secp256k1 curves use this format, so must be 65 bytes long */
+ err = CRYPT_INVALID_PACKET;
+ goto error;
+ }
+ if ((err = mp_read_unsigned_bin(r, (unsigned char *)sig, 32)) != CRYPT_OK) { goto error; }
+ if ((err = mp_read_unsigned_bin(s, (unsigned char *)sig+32, 32)) != CRYPT_OK) { goto error; }
+ }
else {
- /* ASN.1 format */
- if ((err = der_decode_sequence_multi_ex(sig, siglen, LTC_DER_SEQ_SEQUENCE | LTC_DER_SEQ_STRICT,
- LTC_ASN1_INTEGER, 1UL, r,
- LTC_ASN1_INTEGER, 1UL, s,
- LTC_ASN1_EOL, 0UL, NULL)) != CRYPT_OK) { goto error; }
+ /* Unknown signature format */
+ err = CRYPT_ERROR;
+ goto error;
}
/* check for zero */
@@ -153,46 +184,10 @@ static int _ecc_verify_hash(const unsigned char *sig, unsigned long siglen,
if (mu != NULL) mp_clear(mu);
if (ma != NULL) mp_clear(ma);
mp_clear_multi(r, s, v, w, u1, u2, e, a_plus3, NULL);
- if (mp != NULL) {
- mp_montgomery_free(mp);
- }
+ if (mp != NULL) mp_montgomery_free(mp);
return err;
}
-/**
- Verify an ECC signature
- @param sig The signature to verify
- @param siglen The length of the signature (octets)
- @param hash The hash (message digest) that was signed
- @param hashlen The length of the hash (octets)
- @param stat Result of signature, 1==valid, 0==invalid
- @param key The corresponding public ECC key
- @return CRYPT_OK if successful (even if the signature is not valid)
-*/
-int ecc_verify_hash(const unsigned char *sig, unsigned long siglen,
- const unsigned char *hash, unsigned long hashlen,
- int *stat, const ecc_key *key)
-{
- return _ecc_verify_hash(sig, siglen, hash, hashlen, stat, key, 0);
-}
-
-/**
- Verify an ECC signature in RFC7518 format
- @param sig The signature to verify
- @param siglen The length of the signature (octets)
- @param hash The hash (message digest) that was signed
- @param hashlen The length of the hash (octets)
- @param stat Result of signature, 1==valid, 0==invalid
- @param key The corresponding public ECC key
- @return CRYPT_OK if successful (even if the signature is not valid)
-*/
-int ecc_verify_hash_rfc7518(const unsigned char *sig, unsigned long siglen,
- const unsigned char *hash, unsigned long hashlen,
- int *stat, const ecc_key *key)
-{
- return _ecc_verify_hash(sig, siglen, hash, hashlen, stat, key, 1);
-}
-
#endif
/* ref: $Format:%D$ */
diff --git a/tests/ecc_test.c b/tests/ecc_test.c
index 80ddf2929..4e77c0f8c 100644
--- a/tests/ecc_test.c
+++ b/tests/ecc_test.c
@@ -350,6 +350,25 @@ static int _ecc_old_api(void)
return CRYPT_OK;
}
+static int _ecc_key_cmp(const int should_type, const ecc_key *should, const ecc_key *is)
+{
+ if (should_type != is->type) return CRYPT_ERROR;
+ if (should_type == PK_PRIVATE) {
+ if (mp_cmp(should->k, is->k) != LTC_MP_EQ) return CRYPT_ERROR;
+ }
+ if (mp_cmp(should->dp.prime, is->dp.prime) != LTC_MP_EQ) return CRYPT_ERROR;
+ if (mp_cmp(should->dp.A, is->dp.A) != LTC_MP_EQ) return CRYPT_ERROR;
+ if (mp_cmp(should->dp.B, is->dp.B) != LTC_MP_EQ) return CRYPT_ERROR;
+ if (mp_cmp(should->dp.order, is->dp.order) != LTC_MP_EQ) return CRYPT_ERROR;
+ if (mp_cmp(should->dp.base.x, is->dp.base.x) != LTC_MP_EQ) return CRYPT_ERROR;
+ if (mp_cmp(should->dp.base.y, is->dp.base.y) != LTC_MP_EQ) return CRYPT_ERROR;
+ if (mp_cmp(should->pubkey.x, is->pubkey.x) != LTC_MP_EQ) return CRYPT_ERROR;
+ if (mp_cmp(should->pubkey.y, is->pubkey.y) != LTC_MP_EQ) return CRYPT_ERROR;
+ if (should->dp.size != is->dp.size) return CRYPT_ERROR;
+ if (should->dp.cofactor != is->dp.cofactor) return CRYPT_ERROR;
+ return CRYPT_OK;
+}
+
static int _ecc_new_api(void)
{
const char* names[] = {
@@ -474,17 +493,17 @@ static int _ecc_new_api(void)
DO(ecc_set_curve(dp, &privkey));
DO(ecc_set_key(buf, len, PK_PRIVATE, &privkey));
-#ifndef USE_TFM
- /* XXX-FIXME: TFM does not support sqrtmod_prime */
- /* export compressed public key */
- len = sizeof(buf);
- DO(ecc_get_key(buf, &len, PK_PUBLIC|PK_COMPRESSED, &privkey));
- if (len != 1 + (unsigned)ecc_get_size(&privkey)) return CRYPT_FAIL_TESTVECTOR;
- /* load exported public+compressed key */
- DO(ecc_set_curve(dp, &pubkey));
- DO(ecc_set_key(buf, len, PK_PUBLIC, &pubkey));
- ecc_free(&pubkey);
-#endif
+ if (strcmp(ltc_mp.name, "TomsFastMath") != 0) {
+ /* XXX-FIXME: TFM does not support sqrtmod_prime */
+ /* export compressed public key */
+ len = sizeof(buf);
+ DO(ecc_get_key(buf, &len, PK_PUBLIC|PK_COMPRESSED, &privkey));
+ if (len != 1 + (unsigned)ecc_get_size(&privkey)) return CRYPT_FAIL_TESTVECTOR;
+ /* load exported public+compressed key */
+ DO(ecc_set_curve(dp, &pubkey));
+ DO(ecc_set_key(buf, len, PK_PUBLIC, &pubkey));
+ ecc_free(&pubkey);
+ }
/* export long public key */
len = sizeof(buf);
@@ -501,6 +520,26 @@ static int _ecc_new_api(void)
DO(ecc_verify_hash(buf, len, data16, 16, &stat, &pubkey));
if (stat != 1) return CRYPT_FAIL_TESTVECTOR;
+#ifdef LTC_ECC_SHAMIR
+ if (strcmp(ltc_mp.name, "TomsFastMath") != 0) {
+ /* XXX-FIXME: TFM does not support sqrtmod_prime */
+ int found = 0;
+ ecc_key reckey;
+ /* test recovery */
+ len = sizeof(buf);
+ DO(ecc_sign_hash(data16, 16, buf, &len, &yarrow_prng, find_prng ("yarrow"), &privkey));
+ DO(ecc_set_curve(dp, &reckey));
+ for (j = 0; j < 2*(1+(int)privkey.dp.cofactor); j++) {
+ stat = ecc_recover_key(buf, len, data16, 16, j, LTC_ECCSIG_ANSIX962, &reckey);
+ if (stat != CRYPT_OK) continue; /* last two will almost always fail, only possible if x<(prime mod order) */
+ stat = _ecc_key_cmp(PK_PUBLIC, &pubkey, &reckey);
+ if (stat == CRYPT_OK) found++;
+ }
+ if (found != 1) return CRYPT_FAIL_TESTVECTOR; /* unique match */
+ ecc_free(&reckey);
+ }
+#endif
+
/* test encryption */
len = sizeof(buf);
DO(ecc_encrypt_key(data16, 16, buf, &len, &yarrow_prng, find_prng("yarrow"), find_hash("sha256"), &pubkey));
@@ -517,25 +556,6 @@ static int _ecc_new_api(void)
return CRYPT_OK;
}
-static int _ecc_key_cmp(const int should_type, const ecc_key *should, const ecc_key *is)
-{
- if (should_type != is->type) return CRYPT_ERROR;
- if (should_type == PK_PRIVATE) {
- if (mp_cmp(should->k, is->k) != LTC_MP_EQ) return CRYPT_ERROR;
- }
- if (mp_cmp(should->dp.prime, is->dp.prime) != LTC_MP_EQ) return CRYPT_ERROR;
- if (mp_cmp(should->dp.A, is->dp.A) != LTC_MP_EQ) return CRYPT_ERROR;
- if (mp_cmp(should->dp.B, is->dp.B) != LTC_MP_EQ) return CRYPT_ERROR;
- if (mp_cmp(should->dp.order, is->dp.order) != LTC_MP_EQ) return CRYPT_ERROR;
- if (mp_cmp(should->dp.base.x, is->dp.base.x) != LTC_MP_EQ) return CRYPT_ERROR;
- if (mp_cmp(should->dp.base.y, is->dp.base.y) != LTC_MP_EQ) return CRYPT_ERROR;
- if (mp_cmp(should->pubkey.x, is->pubkey.x) != LTC_MP_EQ) return CRYPT_ERROR;
- if (mp_cmp(should->pubkey.y, is->pubkey.y) != LTC_MP_EQ) return CRYPT_ERROR;
- if (should->dp.size != is->dp.size) return CRYPT_ERROR;
- if (should->dp.cofactor != is->dp.cofactor) return CRYPT_ERROR;
- return CRYPT_OK;
-}
-
static int _ecc_import_export(void) {
const ltc_ecc_curve *cu;
ecc_key key, pri, pub;
@@ -896,6 +916,194 @@ static int _ecc_import_export(void) {
return CRYPT_OK;
}
+#ifdef LTC_ECC_SHAMIR
+static int _ecc_test_recovery(void)
+{
+ const char* names[] = {
+#ifdef LTC_ECC_SECP112R1
+ "SECP112R1", "ECC-112",
+ "secp112r1", /* name is case-insensitive */
+ "S E C-P-1_1_2r1", /* should pass fuzzy matching */
+#endif
+#ifdef LTC_ECC_SECP112R2
+ "SECP112R2",
+#endif
+#ifdef LTC_ECC_SECP128R1
+ "SECP128R1", "ECC-128",
+#endif
+#ifdef LTC_ECC_SECP128R2
+ "SECP128R2",
+#endif
+#ifdef LTC_ECC_SECP160R1
+ "SECP160R1", "ECC-160",
+#endif
+#ifdef LTC_ECC_SECP160R2
+ "SECP160R2",
+#endif
+#ifdef LTC_ECC_SECP160K1
+ "SECP160K1",
+#endif
+#ifdef LTC_ECC_BRAINPOOLP160R1
+ "BRAINPOOLP160R1",
+#endif
+#ifdef LTC_ECC_SECP192R1
+ "SECP192R1", "NISTP192", "PRIME192V1", "ECC-192", "P-192",
+#endif
+#ifdef LTC_ECC_PRIME192V2
+ "PRIME192V2",
+#endif
+#ifdef LTC_ECC_PRIME192V3
+ "PRIME192V3",
+#endif
+#ifdef LTC_ECC_SECP192K1
+ "SECP192K1",
+#endif
+#ifdef LTC_ECC_BRAINPOOLP192R1
+ "BRAINPOOLP192R1",
+#endif
+#ifdef LTC_ECC_SECP224R1
+ "SECP224R1", "NISTP224", "ECC-224", "P-224",
+#endif
+#ifdef LTC_ECC_SECP224K1
+ "SECP224K1",
+#endif
+#ifdef LTC_ECC_BRAINPOOLP224R1
+ "BRAINPOOLP224R1",
+#endif
+#ifdef LTC_ECC_PRIME239V1
+ "PRIME239V1",
+#endif
+#ifdef LTC_ECC_PRIME239V2
+ "PRIME239V2",
+#endif
+#ifdef LTC_ECC_PRIME239V3
+ "PRIME239V3",
+#endif
+#ifdef LTC_ECC_SECP256R1
+ "SECP256R1", "NISTP256", "PRIME256V1", "ECC-256", "P-256",
+#endif
+#ifdef LTC_ECC_SECP256K1
+ "SECP256K1",
+#endif
+#ifdef LTC_ECC_BRAINPOOLP256R1
+ "BRAINPOOLP256R1",
+#endif
+#ifdef LTC_ECC_BRAINPOOLP320R1
+ "BRAINPOOLP320R1",
+#endif
+#ifdef LTC_ECC_SECP384R1
+ "SECP384R1", "NISTP384", "ECC-384", "P-384",
+#endif
+#ifdef LTC_ECC_BRAINPOOLP384R1
+ "BRAINPOOLP384R1",
+#endif
+#ifdef LTC_ECC_BRAINPOOLP512R1
+ "BRAINPOOLP512R1",
+#endif
+#ifdef LTC_ECC_SECP521R1
+ "SECP521R1", "NISTP521", "ECC-521", "P-521",
+#endif
+ };
+ int i, recid, stat;
+ const ltc_ecc_curve* dp;
+ ecc_key key, privkey, pubkey, reckey;
+ unsigned char buf[1000];
+ unsigned long len;
+ unsigned char data16[16] = { 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1 };
+ unsigned char eth_hash[] = { /* Keccak-256 hash of "Hello World" */
+ 0x59, 0x2f, 0xa7, 0x43, 0x88, 0x9f, 0xc7, 0xf9, 0x2a, 0xc2, 0xa3, 0x7b, 0xb1, 0xf5, 0xba, 0x1d,
+ 0xaf, 0x2a, 0x5c, 0x84, 0x74, 0x1c, 0xa0, 0xe0, 0x06, 0x1d, 0x24, 0x3a, 0x2e, 0x67, 0x07, 0xba
+ };
+ unsigned char eth_pubkey[] = { /* Public part of randomly-generated key pair */
+ 0x04,
+ 0xc6, 0x99, 0x5f, 0xdc, 0xf4, 0xf2, 0xda, 0x6e, 0x79, 0xe0, 0x47, 0x12, 0xd3, 0xbe, 0x22, 0xe7,
+ 0x65, 0xc6, 0xa3, 0x32, 0x89, 0x1b, 0x34, 0xba, 0xc1, 0xb7, 0x01, 0x83, 0xed, 0xdd, 0xf1, 0xcc,
+ 0xbf, 0x20, 0xdd, 0xcd, 0x05, 0x4e, 0x49, 0xc8, 0xcb, 0x66, 0x6c, 0xb7, 0x71, 0x2f, 0x7e, 0xc1,
+ 0xd6, 0x1a, 0x4a, 0x42, 0x3d, 0xe5, 0xc2, 0x8d, 0x74, 0x03, 0x81, 0xe7, 0xea, 0xc5, 0x3c, 0x10
+ };
+ unsigned char eth_sig[] = { /* Signature of eth_hash to be verified against eth_pubkey */
+ 0xbd, 0x6d, 0xbb, 0xbe, 0x2d, 0xe7, 0x1d, 0x00, 0xae, 0x18, 0x57, 0x12, 0x1d, 0x63, 0xa5, 0x1b,
+ 0x0b, 0x42, 0x71, 0xa2, 0x80, 0x49, 0xe0, 0x5c, 0xfa, 0xc8, 0x1a, 0x0d, 0x8a, 0x88, 0x67, 0x56,
+ 0xf6, 0x67, 0x1b, 0x41, 0x46, 0x09, 0x4e, 0xd0, 0x44, 0x25, 0x18, 0xfd, 0xf4, 0xcd, 0x62, 0xa3,
+ 0xb7, 0x3c, 0x97, 0x55, 0xfa, 0x69, 0xf8, 0xef, 0xe9, 0xcf, 0x12, 0xaf, 0x48, 0x25, 0xe3, 0xe0,
+ 0x1b
+ };
+
+ /* XXX-FIXME: TFM does not support sqrtmod_prime */
+ if (strcmp(ltc_mp.name, "TomsFastMath") == 0) return CRYPT_NOP;
+
+#ifdef LTC_ECC_SECP256K1
+ DO(ecc_find_curve("SECP256K1", &dp));
+
+ DO(ecc_set_curve(dp, &pubkey));
+ DO(ecc_set_key(eth_pubkey, sizeof(eth_pubkey), PK_PUBLIC, &pubkey));
+
+ DO(ecc_set_curve(dp, &reckey));
+ stat = ecc_recover_key(eth_sig, sizeof(eth_sig)-1, eth_hash, sizeof(eth_hash), 0, LTC_ECCSIG_RFC7518, &reckey);
+ if (stat != CRYPT_OK) return CRYPT_FAIL_TESTVECTOR;
+ DO(_ecc_key_cmp(PK_PUBLIC, &pubkey, &reckey));
+ ecc_free(&reckey);
+
+ DO(ecc_set_curve(dp, &reckey));
+ stat = ecc_recover_key(eth_sig, sizeof(eth_sig), eth_hash, sizeof(eth_hash), -1, LTC_ECCSIG_ETH27, &reckey);
+ if (stat != CRYPT_OK) return CRYPT_FAIL_TESTVECTOR;
+ DO(_ecc_key_cmp(PK_PUBLIC, &pubkey, &reckey));
+ ecc_free(&reckey);
+
+ ecc_free(&pubkey);
+#endif
+
+ for (i = 0; i < (int)(sizeof(names)/sizeof(names[0])); i++) {
+ DO(ecc_find_curve(names[i], &dp));
+
+ /* generate new key */
+ DO(ecc_set_curve(dp, &key));
+ DO(ecc_generate_key(&yarrow_prng, find_prng ("yarrow"), &key));
+
+ /* export private key */
+ len = sizeof(buf);
+ DO(ecc_get_key(buf, &len, PK_PRIVATE, &key));
+ ecc_free(&key);
+
+ /* load exported private key */
+ DO(ecc_set_curve(dp, &privkey));
+ DO(ecc_set_key(buf, len, PK_PRIVATE, &privkey));
+
+ /* export long public key */
+ len = sizeof(buf);
+ DO(ecc_get_key(buf, &len, PK_PUBLIC, &privkey));
+ if (len != 1 + 2 * (unsigned)ecc_get_size(&privkey)) return CRYPT_FAIL_TESTVECTOR;
+
+ /* load exported public key */
+ DO(ecc_set_curve(dp, &pubkey));
+ DO(ecc_set_key(buf, len, PK_PUBLIC, &pubkey));
+
+ /* test signature */
+ len = sizeof(buf);
+ recid = 0;
+ DO(ecc_sign_hash_ex(data16, 16, buf, &len, &yarrow_prng, find_prng ("yarrow"), LTC_ECCSIG_RFC7518, &recid, &privkey));
+
+ /* test verification */
+ stat = 0;
+ DO(ecc_verify_hash_ex(buf, len, data16, 16, LTC_ECCSIG_RFC7518, &stat, &pubkey));
+ if (stat != 1) return CRYPT_FAIL_TESTVECTOR;
+
+ /* test recovery */
+ DO(ecc_set_curve(dp, &reckey));
+ stat = ecc_recover_key(buf, len, data16, 16, recid, LTC_ECCSIG_RFC7518, &reckey);
+ if (stat != CRYPT_OK) return CRYPT_FAIL_TESTVECTOR;
+ DO(_ecc_key_cmp(PK_PUBLIC, &pubkey, &reckey));
+
+ /* cleanup */
+ ecc_free(&reckey);
+ ecc_free(&privkey);
+ ecc_free(&pubkey);
+ }
+
+ return CRYPT_OK;
+}
+#endif
+
int ecc_tests(void)
{
if (ltc_mp.name == NULL) return CRYPT_NOP;
@@ -907,6 +1115,7 @@ int ecc_tests(void)
DO(_ecc_issue108());
#ifdef LTC_ECC_SHAMIR
DO(_ecc_test_shamir());
+ DO(_ecc_test_recovery());
#endif
return CRYPT_OK;
}