mirror of
https://github.com/libtom/libtomcrypt
synced 2026-08-25 20:26:07 -04:00
Merge pull request #436 from rmw42/feature/ecrecover
Implement ecc_recover_key to recover public key from hash+signature
This commit is contained in:
commit
c5e4679b7a
11 changed files with 724 additions and 154 deletions
|
|
@ -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.
|
||||
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 @@ 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
|
||||
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()}
|
||||
|
|
|
|||
|
|
@ -2338,6 +2338,10 @@
|
|||
RelativePath="src\pk\ecc\ecc_make_key.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="src\pk\ecc\ecc_recover_key.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="src\pk\ecc\ecc_set_curve.c"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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_)
|
||||
|
||||
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);
|
||||
#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_)
|
||||
|
||||
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);
|
||||
#define ecc_verify_hash_rfc7518(sig_, siglen_, hash_, hashlen_, stat_, key_) \
|
||||
ecc_verify_hash_ex(sig_, siglen_, hash_, hashlen_, LTC_ECCSIG_RFC7518, stat_, key_)
|
||||
|
||||
int ecc_verify_hash(const unsigned char *sig, unsigned long siglen,
|
||||
#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_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_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_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
|
||||
|
||||
|
|
|
|||
249
src/pk/ecc/ecc_recover_key.c
Normal file
249
src/pk/ecc/ecc_recover_key.c
Normal file
|
|
@ -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$ */
|
||||
|
|
@ -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 {
|
||||
/* 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_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 {
|
||||
/* Unknown signature format */
|
||||
err = CRYPT_ERROR;
|
||||
goto error;
|
||||
}
|
||||
|
||||
goto errnokey;
|
||||
error:
|
||||
ecc_free(&pubkey);
|
||||
|
|
@ -118,42 +170,6 @@ errnokey:
|
|||
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$ */
|
||||
|
|
|
|||
|
|
@ -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,23 +67,42 @@ static int _ecc_verify_hash(const unsigned char *sig, unsigned long siglen,
|
|||
goto error;
|
||||
}
|
||||
|
||||
if (sigformat == 1) {
|
||||
/* RFC7518 format */
|
||||
if ((siglen % 2) == 1) {
|
||||
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 {
|
||||
/* ASN.1 format */
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
/* check for zero */
|
||||
if (mp_cmp_d(r, 0) != LTC_MP_GT || mp_cmp_d(s, 0) != LTC_MP_GT ||
|
||||
|
|
@ -153,46 +184,10 @@ error:
|
|||
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$ */
|
||||
|
|
|
|||
269
tests/ecc_test.c
269
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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue