Browse Source

Merge pull request #436 from rmw42/feature/ecrecover

Implement ecc_recover_key to recover public key from hash+signature
Steffen Jaeckel 7 years ago
parent
commit
c5e4679b7a

+ 78 - 0
doc/crypt.tex

@@ -5549,6 +5549,22 @@ int ecc_sign_hash_rfc7518(const unsigned char *in,
 This function creates the same ECDSA signature as \textit{ecc\_sign\_hash} only the output format is different.
 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.
 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}
 \subsection{Signature Verification}
 \index{ecc\_verify\_hash()}
 \index{ecc\_verify\_hash()}
 \begin{verbatim}
 \begin{verbatim}
@@ -5579,12 +5595,74 @@ int ecc_verify_hash_rfc7518(const unsigned char *sig,
 This function validate the ECDSA signature as \textit{ecc\_verify\_hash} only the signature input format
 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}.
 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
 {\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
 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
 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
 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.
 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)}
 \mysection{Shared Secret (ECDH)}
 To construct a Diffie-Hellman shared secret with a private and public ECC key, use the following function:
 To construct a Diffie-Hellman shared secret with a private and public ECC key, use the following function:
 \index{ecc\_shared\_secret()}
 \index{ecc\_shared\_secret()}

+ 4 - 0
libtomcrypt_VS2008.vcproj

@@ -2338,6 +2338,10 @@
 					RelativePath="src\pk\ecc\ecc_make_key.c"
 					RelativePath="src\pk\ecc\ecc_make_key.c"
 					>
 					>
 				</File>
 				</File>
+				<File
+					RelativePath="src\pk\ecc\ecc_recover_key.c"
+					>
+				</File>
 				<File
 				<File
 					RelativePath="src\pk\ecc\ecc_set_curve.c"
 					RelativePath="src\pk\ecc\ecc_set_curve.c"
 					>
 					>

+ 3 - 3
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_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_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_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_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_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 \
 src/pk/ecc/ltc_ecc_mulmod.o src/pk/ecc/ltc_ecc_mulmod_timing.o src/pk/ecc/ltc_ecc_points.o \

+ 3 - 3
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_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_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_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_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_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 \
 src/pk/ecc/ltc_ecc_mulmod.obj src/pk/ecc/ltc_ecc_mulmod_timing.obj src/pk/ecc/ltc_ecc_points.obj \

+ 3 - 3
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_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_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_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_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_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 \
 src/pk/ecc/ltc_ecc_mulmod.o src/pk/ecc/ltc_ecc_mulmod_timing.o src/pk/ecc/ltc_ecc_points.o \

+ 3 - 3
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_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_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_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_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_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 \
 src/pk/ecc/ltc_ecc_mulmod.o src/pk/ecc/ltc_ecc_mulmod_timing.o src/pk/ecc/ltc_ecc_points.o \

+ 30 - 11
src/headers/tomcrypt_pk.h

@@ -244,6 +244,16 @@ typedef struct {
     void *k;
     void *k;
 } ecc_key;
 } 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 */
 /** the ECC params provided */
 extern const ltc_ecc_curve ltc_ecc_curves[];
 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,
                            unsigned char *out, unsigned long *outlen,
                            const ecc_key *key);
                            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,
                      const unsigned char *hash, unsigned long hashlen,
-                     int *stat, const ecc_key *key);
+                     int recid, ecc_signature_type sigformat, ecc_key *key);
 
 
 #endif
 #endif
 
 

+ 249 - 0
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<pbytes; i++) {
+        buf[i] = ch;
+        ch = (hash[i] << (8-shift_right));
+        buf[i] = buf[i] ^ (hash[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$ */

+ 62 - 46
src/pk/ecc/ecc_sign_hash.c

@@ -16,12 +16,27 @@
   ECC Crypto, Tom St Denis
   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;
    ecc_key       pubkey;
    void          *r, *s, *e, *p, *b;
    void          *r, *s, *e, *p, *b;
+   int           v = 0;
    int           err, max_iterations = LTC_PK_MAX_RETRIES;
    int           err, max_iterations = LTC_PK_MAX_RETRIES;
    unsigned long pbits, pbytes, i, shift_right;
    unsigned long pbits, pbytes, i, shift_right;
    unsigned char ch, buf[MAXBLOCKSIZE];
    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 */
       /* find r = x1 mod n */
       if ((err = mp_mod(pubkey.pubkey.x, p, r)) != CRYPT_OK)               { goto error; }
       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) {
       if (mp_iszero(r) == LTC_MP_YES) {
          ecc_free(&pubkey);
          ecc_free(&pubkey);
       } else {
       } else {
@@ -92,8 +119,17 @@ static int _ecc_sign_hash(const unsigned char *in,  unsigned long inlen,
       goto errnokey;
       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; }
       if (*outlen < 2*pbytes) { err = CRYPT_MEM; goto errnokey; }
       zeromem(out, 2*pbytes);
       zeromem(out, 2*pbytes);
       i = mp_unsigned_bin_size(r);
       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;
       *outlen = 2*pbytes;
       err = CRYPT_OK;
       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 {
    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;
    goto errnokey;
 error:
 error:
    ecc_free(&pubkey);
    ecc_free(&pubkey);
@@ -118,42 +170,6 @@ errnokey:
    return err;
    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
 #endif
 
 
 /* ref:         $Format:%D$ */
 /* ref:         $Format:%D$ */

+ 46 - 51
src/pk/ecc/ecc_verify_hash.c

@@ -16,12 +16,24 @@
   ECC Crypto, Tom St Denis
   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;
    void          *mp = NULL;
    int           err;
    int           err;
    unsigned long pbits, pbytes, i, shift_right;
    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;
       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;
          err = CRYPT_INVALID_PACKET;
          goto error;
          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(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; }
       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 {
    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 */
    /* check for zero */
@@ -153,46 +184,10 @@ error:
    if (mu != NULL) mp_clear(mu);
    if (mu != NULL) mp_clear(mu);
    if (ma != NULL) mp_clear(ma);
    if (ma != NULL) mp_clear(ma);
    mp_clear_multi(r, s, v, w, u1, u2, e, a_plus3, NULL);
    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;
    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
 #endif
 
 
 /* ref:         $Format:%D$ */
 /* ref:         $Format:%D$ */

+ 239 - 30
tests/ecc_test.c

@@ -350,6 +350,25 @@ static int _ecc_old_api(void)
    return CRYPT_OK;
    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)
 static int _ecc_new_api(void)
 {
 {
    const char* names[] = {
    const char* names[] = {
@@ -474,17 +493,17 @@ static int _ecc_new_api(void)
       DO(ecc_set_curve(dp, &privkey));
       DO(ecc_set_curve(dp, &privkey));
       DO(ecc_set_key(buf, len, PK_PRIVATE, &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 */
       /* export long public key */
       len = sizeof(buf);
       len = sizeof(buf);
@@ -501,6 +520,26 @@ static int _ecc_new_api(void)
       DO(ecc_verify_hash(buf, len, data16, 16, &stat, &pubkey));
       DO(ecc_verify_hash(buf, len, data16, 16, &stat, &pubkey));
       if (stat != 1) return CRYPT_FAIL_TESTVECTOR;
       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 */
       /* test encryption */
       len = sizeof(buf);
       len = sizeof(buf);
       DO(ecc_encrypt_key(data16, 16, buf, &len, &yarrow_prng, find_prng("yarrow"), find_hash("sha256"), &pubkey));
       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;
    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) {
 static int _ecc_import_export(void) {
    const ltc_ecc_curve *cu;
    const ltc_ecc_curve *cu;
    ecc_key key, pri, pub;
    ecc_key key, pri, pub;
@@ -896,6 +916,194 @@ static int _ecc_import_export(void) {
    return CRYPT_OK;
    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)
 int ecc_tests(void)
 {
 {
    if (ltc_mp.name == NULL) return CRYPT_NOP;
    if (ltc_mp.name == NULL) return CRYPT_NOP;
@@ -907,6 +1115,7 @@ int ecc_tests(void)
    DO(_ecc_issue108());
    DO(_ecc_issue108());
 #ifdef LTC_ECC_SHAMIR
 #ifdef LTC_ECC_SHAMIR
    DO(_ecc_test_shamir());
    DO(_ecc_test_shamir());
+   DO(_ecc_test_recovery());
 #endif
 #endif
    return CRYPT_OK;
    return CRYPT_OK;
 }
 }