diff --git a/doc/crypt.tex b/doc/crypt.tex index 98332af20..317fc0e6e 100644 --- a/doc/crypt.tex +++ b/doc/crypt.tex @@ -1,4 +1,5 @@ \documentclass[synpaper]{book} +\usepackage[T1]{fontenc} \usepackage{geometry} \usepackage{hyperref} \usepackage{makeidx} @@ -44,6 +45,7 @@ \def\C{{\mathbb C}} \def\Q{{\mathbb Q}} \definecolor{DGray}{gray}{0.5} +\newcommand{\code}[1]{\texttt{\textit{#1}}} \newcommand{\emailaddr}[1]{\mbox{$<${#1}$>$}} \def\twiddle{\raisebox{0.3ex}{\mbox{\tiny $\sim$}}} \def\gap{\vspace{0.5ex}} @@ -5801,8 +5803,14 @@ \subsection{ANSI X9.63 Import (deprecated)} \mysection{Signatures (ECDSA)} -There are also functions to sign and verify messages. They use the ANSI X9.62 ECDSA algorithm to generate and verify signatures in the -ANSI X9.62 format. +There are also functions to sign and verify messages. They use the \textit{ANSI X9.62} \textit{ECDSA} algorithm to generate and verify signatures in the +\textit{ANSI X9.62} format. + +\textbf{BEWARE:} With \textit{ECC} if you try to sign a hash that is bigger than your \textit{ECC} key you can run into problems. The math +will still work, and in effect the signature will still work. With \textit{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{Signature Generation} To sign a message digest (hash) use the following function: @@ -5815,12 +5823,12 @@ \subsection{Signature Generation} unsigned long *outlen, prng_state *prng, int wprng, - ecc_key *key); + const ecc_key *key); \end{verbatim} -This function will ECDSA sign the message digest stored in the array pointed to by \textit{in} of length \textit{inlen} octets. The signature -will be stored in the array pointed to by \textit{out} of length \textit{outlen} octets. The function requires a properly seeded PRNG, and -the ECC \textit{key} provided must be a private key. +This function will \textit{ECDSA} sign the message digest stored in the array pointed to by \code{in} of length \code{inlen} octets. The signature +will be stored in the array pointed to by \code{out} of length \code{outlen} octets. The function requires a properly seeded \textit{PRNG}, and +the \textit{ECC} \code{key} provided must be a private key. \index{ecc\_sign\_hash\_rfc7518()} \begin{verbatim} @@ -5830,27 +5838,53 @@ \subsection{Signature Generation} unsigned long *outlen, prng_state *prng, int wprng, - ecc_key *key); + const ecc_key *key); \end{verbatim} -This function creates the same ECDSA signature as \textit{ecc\_sign\_hash} only the output format is different. +This function creates the same \textit{ECDSA} signature as \code{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()} +\index{ecc\_sign\_hash\_rfc7518\_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); +int ecc_sign_hash_rfc7518_ex(const unsigned char *in, + unsigned long inlen, + unsigned char *out, + unsigned long *outlen, + prng_state *prng, + int wprng, + int *recid, + const ecc_key *key); +\end{verbatim} + +This function is an extended version of the \textit{ECDSA} signature in \code{ecc\_sign\_hash\_rfc7518()}, but with an additional output of the recovery ID +for use with \code{ecc\_recover\_key()}. + +\index{ecc\_sign\_hash\_rfc5656()} +\begin{verbatim} +int ecc_sign_hash_rfc5656(const unsigned char *in, + unsigned long inlen, + unsigned char *out, + unsigned long *outlen, + prng_state *prng, + int wprng, + const ecc_key *key); +\end{verbatim} + +This function creates an \textit{ECDSA} signature and the output format is according to \textit{RFC5656}, i.e. \textit{SSH} compatible. + +\index{ecc\_sign\_hash\_eth27()} +\begin{verbatim} +int ecc_sign_hash_eth27(const unsigned char *in, + unsigned long inlen, + unsigned char *out, + unsigned long *outlen, + prng_state *prng, + int wprng, + const 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}. +This function creates an \textit{ECDSA} signature and the output format is according to the Ethereum format. +With this API the curve is limited to \textit{secp256k1}. \subsection{Signature Verification} \index{ecc\_verify\_hash()} @@ -5860,14 +5894,14 @@ \subsection{Signature Verification} const unsigned char *hash, unsigned long hashlen, int *stat, - ecc_key *key); + const ecc_key *key); \end{verbatim} -This function will verify the ECDSA signature in the array pointed to by \textit{sig} of length \textit{siglen} octets, against the message digest -pointed to by the array \textit{hash} of length \textit{hashlen}. It will store a non--zero value in \textit{stat} if the signature is valid. Note: +This function will verify the \textit{ECDSA} signature in the array pointed to by \code{sig} of length \code{siglen} octets, against the message digest +pointed to by the array \code{hash} of length \code{hashlen}. It will store a non--zero value in \code{stat} if the signature is valid. Note: the function will not return an error if the signature is invalid. It will return an error, if the actual signature payload is an invalid format. -The ECC \textit{key} must be the public (or private) ECC key corresponding to the key that performed the signature. -The function \textit{ecc\_verify\_hash} implements signature format according to X9.62 ECDSA, and the output is compliant for GF(p) curves. +The \textit{ECC} \code{key} must be the public (or private) \textit{ECC} key corresponding to the key that performed the signature. +The function \code{ecc\_verify\_hash()} implements signature format according to \textit{ANSI X9.62} EC\textit{DSA}, and the output is compliant for GF(p) curves. \index{ecc\_verify\_hash\_rfc7518()} \begin{verbatim} @@ -5876,30 +5910,36 @@ \subsection{Signature Verification} const unsigned char *hash, unsigned long hashlen, int *stat, - ecc_key *key); + const ecc_key *key); \end{verbatim} -This function validate the ECDSA signature as \textit{ecc\_verify\_hash} only the signature input format +This function validates the \textit{ECDSA} signature as \code{ecc\_verify\_hash()}, only the signature input format follows \url{https://tools.ietf.org/html/rfc7518#section-3.4}. -\index{ecc\_verify\_hash\_ex()} +\index{ecc\_verify\_hash\_rfc5656()} \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); +int ecc_verify_hash_rfc5656(const unsigned char *sig, + unsigned long siglen, + const unsigned char *hash, + unsigned long hashlen, + int *stat, + const ecc_key *key); \end{verbatim} -This function validates an ECDSA signature as \textit{ecc\_verify\_hash} but with a choice of signature formats. +This function validates the \textit{ECDSA} signature according to the format defined in \textit{RFC5656}, i.e. \textit{SSH} compatible. -{\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. + +\index{ecc\_verify\_hash\_eth27()} +\begin{verbatim} +int ecc_verify_hash_eth27(const unsigned char *sig, + unsigned long siglen, + const unsigned char *hash, + unsigned long hashlen, + int *stat, + const ecc_key *key); +\end{verbatim} + +This function validates the \textit{ECDSA} signature according to the Ethereum format. \subsection{Public Key Recovery} \index{ecc\_recover\_key()} @@ -5913,18 +5953,18 @@ \subsection{Public Key Recovery} 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 function will recover (a) public key from the \textit{ECDSA} signature in the array pointed to by \code{sig} of length \code{siglen} octets, the message digest +pointed to by the array \code{hash} of length \code{hashlen}, and the recovery id \code{recid}. It will store the recovered +key into \code{key} and return \code{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, +only the parity of the public key. For curves like \textit{secp256k1}, \code{recid} will take values of 0 or 1, corresponding to the +parity of the public key's y coordinate. For curves like \textit{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 +If the signature format contains the recovery id (currently only \code{LTC\_ECCSIG\_ETH27}), \code{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. +The function \code{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: @@ -5935,10 +5975,10 @@ \subsection{Signature Formats} \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\_ANSIX962 & ASN.1 encoded, \textit{ANSI X9.62} \\ +\hline LTC\_ECCSIG\_RFC7518 & raw R, S values as defined in \textit{RFC7518} \\ \hline LTC\_ECCSIG\_ETH27 & raw R, S, V values (V has 27 added) \\ -\hline LTC\_ECCSIG\_RFC5656 & SSH+ECDSA format as defined in RFC5656 \\ +\hline LTC\_ECCSIG\_RFC5656 & \textit{SSH+ECDSA} format as defined in \textit{RFC5656} \\ \hline \end{tabular} \end{center} @@ -5947,9 +5987,13 @@ \subsection{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} +The \code{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}. +\code{ecc\_recover\_key()}. + +\textbf{NOTE:} If you're using a tailored version of libtomcrypt, it is possible to disable \code{LTC\_DER} which will disable +the option to use \code{LTC\_ECCSIG\_ANSIX962}. Also it is possible to disable \code{LTC\_SSH} which will disable +the option to use \code{LTC\_ECCSIG\_RFC5656}. \mysection{Shared Secret (ECDH)} To construct a Diffie-Hellman shared secret with a private and public ECC key, use the following function: diff --git a/libtomcrypt_VS2008.vcproj b/libtomcrypt_VS2008.vcproj index 84b8555b9..fd493541d 100644 --- a/libtomcrypt_VS2008.vcproj +++ b/libtomcrypt_VS2008.vcproj @@ -2510,6 +2510,22 @@ RelativePath="src\pk\ecc\ecc_sign_hash.c" > + + + + + + + + @@ -2522,6 +2538,22 @@ RelativePath="src\pk\ecc\ecc_verify_hash.c" > + + + + + + + + diff --git a/makefile.mingw b/makefile.mingw index 1234941b9..f94253c84 100644 --- a/makefile.mingw +++ b/makefile.mingw @@ -193,8 +193,12 @@ src/pk/ecc/ecc_get_size.o src/pk/ecc/ecc_import.o src/pk/ecc/ecc_import_openssl. src/pk/ecc/ecc_import_pkcs8.o src/pk/ecc/ecc_import_x509.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_ssh_ecdsa_encode_name.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/ecc_sign_hash_eth27.o src/pk/ecc/ecc_sign_hash_internal.o \ +src/pk/ecc/ecc_sign_hash_rfc5656.o src/pk/ecc/ecc_sign_hash_rfc7518.o src/pk/ecc/ecc_sizes.o \ +src/pk/ecc/ecc_ssh_ecdsa_encode_name.o src/pk/ecc/ecc_verify_hash.o src/pk/ecc/ecc_verify_hash_eth27.o \ +src/pk/ecc/ecc_verify_hash_internal.o src/pk/ecc/ecc_verify_hash_rfc5656.o \ +src/pk/ecc/ecc_verify_hash_rfc7518.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 \ src/pk/ecc/ltc_ecc_projective_add_point.o src/pk/ecc/ltc_ecc_projective_dbl_point.o \ diff --git a/makefile.msvc b/makefile.msvc index 99782a591..efadaf0a1 100644 --- a/makefile.msvc +++ b/makefile.msvc @@ -186,8 +186,12 @@ src/pk/ecc/ecc_get_size.obj src/pk/ecc/ecc_import.obj src/pk/ecc/ecc_import_open src/pk/ecc/ecc_import_pkcs8.obj src/pk/ecc/ecc_import_x509.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_ssh_ecdsa_encode_name.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/ecc_sign_hash_eth27.obj src/pk/ecc/ecc_sign_hash_internal.obj \ +src/pk/ecc/ecc_sign_hash_rfc5656.obj src/pk/ecc/ecc_sign_hash_rfc7518.obj src/pk/ecc/ecc_sizes.obj \ +src/pk/ecc/ecc_ssh_ecdsa_encode_name.obj src/pk/ecc/ecc_verify_hash.obj src/pk/ecc/ecc_verify_hash_eth27.obj \ +src/pk/ecc/ecc_verify_hash_internal.obj src/pk/ecc/ecc_verify_hash_rfc5656.obj \ +src/pk/ecc/ecc_verify_hash_rfc7518.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 \ src/pk/ecc/ltc_ecc_projective_add_point.obj src/pk/ecc/ltc_ecc_projective_dbl_point.obj \ diff --git a/makefile.unix b/makefile.unix index 46afebadc..360c6a472 100644 --- a/makefile.unix +++ b/makefile.unix @@ -207,8 +207,12 @@ src/pk/ecc/ecc_get_size.o src/pk/ecc/ecc_import.o src/pk/ecc/ecc_import_openssl. src/pk/ecc/ecc_import_pkcs8.o src/pk/ecc/ecc_import_x509.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_ssh_ecdsa_encode_name.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/ecc_sign_hash_eth27.o src/pk/ecc/ecc_sign_hash_internal.o \ +src/pk/ecc/ecc_sign_hash_rfc5656.o src/pk/ecc/ecc_sign_hash_rfc7518.o src/pk/ecc/ecc_sizes.o \ +src/pk/ecc/ecc_ssh_ecdsa_encode_name.o src/pk/ecc/ecc_verify_hash.o src/pk/ecc/ecc_verify_hash_eth27.o \ +src/pk/ecc/ecc_verify_hash_internal.o src/pk/ecc/ecc_verify_hash_rfc5656.o \ +src/pk/ecc/ecc_verify_hash_rfc7518.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 \ src/pk/ecc/ltc_ecc_projective_add_point.o src/pk/ecc/ltc_ecc_projective_dbl_point.o \ diff --git a/makefile_include.mk b/makefile_include.mk index ebacc4812..bedc86f9a 100644 --- a/makefile_include.mk +++ b/makefile_include.mk @@ -369,8 +369,12 @@ src/pk/ecc/ecc_get_size.o src/pk/ecc/ecc_import.o src/pk/ecc/ecc_import_openssl. src/pk/ecc/ecc_import_pkcs8.o src/pk/ecc/ecc_import_x509.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_ssh_ecdsa_encode_name.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/ecc_sign_hash_eth27.o src/pk/ecc/ecc_sign_hash_internal.o \ +src/pk/ecc/ecc_sign_hash_rfc5656.o src/pk/ecc/ecc_sign_hash_rfc7518.o src/pk/ecc/ecc_sizes.o \ +src/pk/ecc/ecc_ssh_ecdsa_encode_name.o src/pk/ecc/ecc_verify_hash.o src/pk/ecc/ecc_verify_hash_eth27.o \ +src/pk/ecc/ecc_verify_hash_internal.o src/pk/ecc/ecc_verify_hash_rfc5656.o \ +src/pk/ecc/ecc_verify_hash_rfc7518.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 \ src/pk/ecc/ltc_ecc_projective_add_point.o src/pk/ecc/ltc_ecc_projective_dbl_point.o \ diff --git a/sources.cmake b/sources.cmake index ee7316437..a82a6ab82 100644 --- a/sources.cmake +++ b/sources.cmake @@ -426,9 +426,17 @@ src/pk/ecc/ecc_set_curve_internal.c src/pk/ecc/ecc_set_key.c src/pk/ecc/ecc_shared_secret.c src/pk/ecc/ecc_sign_hash.c +src/pk/ecc/ecc_sign_hash_eth27.c +src/pk/ecc/ecc_sign_hash_internal.c +src/pk/ecc/ecc_sign_hash_rfc5656.c +src/pk/ecc/ecc_sign_hash_rfc7518.c src/pk/ecc/ecc_sizes.c src/pk/ecc/ecc_ssh_ecdsa_encode_name.c src/pk/ecc/ecc_verify_hash.c +src/pk/ecc/ecc_verify_hash_eth27.c +src/pk/ecc/ecc_verify_hash_internal.c +src/pk/ecc/ecc_verify_hash_rfc5656.c +src/pk/ecc/ecc_verify_hash_rfc7518.c src/pk/ecc/ltc_ecc_export_point.c src/pk/ecc/ltc_ecc_import_point.c src/pk/ecc/ltc_ecc_is_point.c diff --git a/src/headers/tomcrypt_custom.h b/src/headers/tomcrypt_custom.h index e12740dde..b1731b9a3 100644 --- a/src/headers/tomcrypt_custom.h +++ b/src/headers/tomcrypt_custom.h @@ -622,9 +622,11 @@ #define LTC_PKCS_8 #endif -#ifdef LTC_PKCS_8 +#if defined(LTC_PKCS_8) && defined(LTC_DER) #define LTC_PADDING #define LTC_PBES +#else + #undef LTC_PKCS_8 #endif #if defined(LTC_CLEAN_STACK) @@ -664,7 +666,7 @@ #error ASN.1 DER requires MPI functionality #endif -#if (defined(LTC_MDSA) || defined(LTC_MRSA) || defined(LTC_MECC)) && !defined(LTC_DER) +#if (defined(LTC_MDSA) || defined(LTC_MRSA)) && !defined(LTC_DER) #error PK requires ASN.1 DER functionality, make sure LTC_DER is enabled #endif diff --git a/src/headers/tomcrypt_pk.h b/src/headers/tomcrypt_pk.h index dcf714d7d..8ba34d843 100644 --- a/src/headers/tomcrypt_pk.h +++ b/src/headers/tomcrypt_pk.h @@ -312,22 +312,25 @@ int ecc_make_key(prng_state *prng, int wprng, int keysize, ecc_key *key); int ecc_make_key_ex(prng_state *prng, int wprng, ecc_key *key, const ltc_ecc_curve *cu); void ecc_free(ecc_key *key); +#if defined(LTC_DER) int ecc_export(unsigned char *out, unsigned long *outlen, int type, const ecc_key *key); int ecc_import(const unsigned char *in, unsigned long inlen, ecc_key *key); int ecc_import_ex(const unsigned char *in, unsigned long inlen, ecc_key *key, const ltc_ecc_curve *cu); -int ecc_ansi_x963_export(const ecc_key *key, unsigned char *out, unsigned long *outlen); -int ecc_ansi_x963_import(const unsigned char *in, unsigned long inlen, ecc_key *key); -int ecc_ansi_x963_import_ex(const unsigned char *in, unsigned long inlen, ecc_key *key, const ltc_ecc_curve *cu); +int ecc_export_openssl(unsigned char *out, unsigned long *outlen, int type, const ecc_key *key); +int ecc_import_openssl(const unsigned char *in, unsigned long inlen, ecc_key *key); +int ecc_import_pkcs8(const unsigned char *in, unsigned long inlen, const password_ctx *pw_ctx, ecc_key *key); +int ecc_import_x509(const unsigned char *in, unsigned long inlen, ecc_key *key); +#endif -int ecc_export_openssl(unsigned char *out, unsigned long *outlen, int type, const ecc_key *key); -int ecc_import_openssl(const unsigned char *in, unsigned long inlen, ecc_key *key); -int ecc_import_pkcs8(const unsigned char *in, unsigned long inlen, const password_ctx *pw_ctx, ecc_key *key); -int ecc_import_x509(const unsigned char *in, unsigned long inlen, ecc_key *key); +int ecc_ansi_x963_export(const ecc_key *key, unsigned char *out, unsigned long *outlen); +int ecc_ansi_x963_import(const unsigned char *in, unsigned long inlen, ecc_key *key); +int ecc_ansi_x963_import_ex(const unsigned char *in, unsigned long inlen, ecc_key *key, const ltc_ecc_curve *cu); int ecc_shared_secret(const ecc_key *private_key, const ecc_key *public_key, unsigned char *out, unsigned long *outlen); +#if defined(LTC_DER) int ecc_encrypt_key(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen, prng_state *prng, int wprng, int hash, @@ -337,30 +340,85 @@ int ecc_decrypt_key(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen, 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_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_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_verify_hash(const unsigned char *sig, + unsigned long siglen, + const unsigned char *hash, + unsigned long hashlen, + int *stat, + const ecc_key *key); +#endif -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_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); + +int ecc_sign_hash_rfc7518_ex(const unsigned char *in, + unsigned long inlen, + unsigned char *out, + unsigned long *outlen, + prng_state *prng, + int wprng, + 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); + +#if defined(LTC_SSH) +int ecc_sign_hash_rfc5656(const unsigned char *in, + unsigned long inlen, + unsigned char *out, + unsigned long *outlen, + prng_state *prng, + int wprng, + const ecc_key *key); + +int ecc_verify_hash_rfc5656(const unsigned char *sig, + unsigned long siglen, + const unsigned char *hash, + unsigned long hashlen, + int *stat, + const ecc_key *key); +#endif -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); +int ecc_sign_hash_eth27(const unsigned char *in, + unsigned long inlen, + unsigned char *out, + unsigned long *outlen, + prng_state *prng, + int wprng, + const ecc_key *key); + +int ecc_verify_hash_eth27(const unsigned char *sig, + unsigned long siglen, + const unsigned char *hash, + unsigned long hashlen, + 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 recid, + ecc_signature_type sigformat, + ecc_key *key); #endif diff --git a/src/headers/tomcrypt_private.h b/src/headers/tomcrypt_private.h index 78f3d08f5..71a1ed606 100644 --- a/src/headers/tomcrypt_private.h +++ b/src/headers/tomcrypt_private.h @@ -84,6 +84,7 @@ typedef int (*fn_kdf_t)(const struct password *pwd, int iteration_count, int hash_idx, unsigned char *out, unsigned long *outlen); +#if defined(LTC_PBES) typedef struct { /* KDF */ fn_kdf_t kdf; @@ -107,6 +108,7 @@ typedef struct /* only used for RC2 */ unsigned long key_bits; } pbes_arg; +#endif typedef struct { const pbes_properties *data; @@ -362,11 +364,14 @@ struct get_char { void copy_or_zeromem(const unsigned char* src, unsigned char* dest, unsigned long len, int coz); void password_free(struct password *pw, const struct password_ctx *ctx); +#if defined(LTC_PBES) int pbes_decrypt(const pbes_arg *arg, unsigned char *dec_data, unsigned long *dec_size); int pbes1_extract(const ltc_asn1_list *s, pbes_arg *res); int pbes2_extract(const ltc_asn1_list *s, pbes_arg *res); +#endif +#ifdef LTC_PEM int pem_decrypt(unsigned char *data, unsigned long *datalen, unsigned char *key, unsigned long keylen, unsigned char *iv, unsigned long ivlen, @@ -378,6 +383,7 @@ int pem_get_char_from_file(struct get_char *g); #endif /* LTC_NO_FILE */ int pem_get_char_from_buf(struct get_char *g); int pem_read(void *pem, unsigned long *w, struct pem_headers *hdr, struct get_char *g); +#endif /* tomcrypt_pk.h */ @@ -387,10 +393,14 @@ int rand_bn_upto(void *N, void *limit, prng_state *prng, int wprng); int pk_get_oid(enum ltc_oid_id id, const char **st); int pk_get_pka_id(enum ltc_oid_id id, enum ltc_pka_id *pka); int pk_get_oid_id(enum ltc_pka_id pka, enum ltc_oid_id *oid); +#ifdef LTC_DER int pk_get_oid_from_asn1(const ltc_asn1_list *oid, enum ltc_oid_id *id); +#endif int pk_oid_str_to_num(const char *OID, unsigned long *oid, unsigned long *oidlen); int pk_oid_num_to_str(const unsigned long *oid, unsigned long oidlen, char *OID, unsigned long *outlen); +int pk_oid_cmp_with_ulong(const char *o1, const unsigned long *o2, unsigned long o2size); + /* ---- DH Routines ---- */ #ifdef LTC_MRSA int rsa_init(rsa_key *key); @@ -416,10 +426,20 @@ int ecc_set_curve_from_mpis(void *a, void *b, void *prime, void *order, void *gx int ecc_copy_curve(const ecc_key *srckey, ecc_key *key); int ecc_set_curve_by_size(int size, ecc_key *key); int ecc_import_subject_public_key_info(const unsigned char *in, unsigned long inlen, ecc_key *key); +#ifdef LTC_DER int ecc_import_pkcs8_asn1(ltc_asn1_list *alg_id, ltc_asn1_list *priv_key, ecc_key *key); +#endif int ecc_import_with_curve(const unsigned char *in, unsigned long inlen, int type, ecc_key *key); int ecc_import_with_oid(const unsigned char *in, unsigned long inlen, unsigned long *oid, unsigned long oid_len, int type, ecc_key *key); +int ecc_sign_hash_internal(const unsigned char *in, unsigned long inlen, + void *r, void *s, prng_state *prng, int wprng, + int *recid, const ecc_key *key); + +int ecc_verify_hash_internal(void *r, void *s, + const unsigned char *hash, unsigned long hashlen, + int *stat, const ecc_key *key); + #ifdef LTC_SSH int ecc_ssh_ecdsa_encode_name(char *buffer, unsigned long *buflen, const ecc_key *key); #endif @@ -604,7 +624,6 @@ int x509_decode_subject_public_key_info(const unsigned char *in, unsigned long i enum ltc_oid_id algorithm, void *public_key, unsigned long *public_key_len, ltc_asn1_type parameters_type, ltc_asn1_list* parameters, unsigned long *parameters_len); -int pk_oid_cmp_with_ulong(const char *o1, const unsigned long *o2, unsigned long o2size); int pk_oid_cmp_with_asn1(const char *o1, const ltc_asn1_list *o2); #endif /* LTC_DER */ diff --git a/src/pk/asn1/oid/pk_oid_cmp.c b/src/pk/asn1/oid/pk_oid_cmp.c index 04a379bff..a62d2c638 100644 --- a/src/pk/asn1/oid/pk_oid_cmp.c +++ b/src/pk/asn1/oid/pk_oid_cmp.c @@ -2,8 +2,6 @@ /* SPDX-License-Identifier: Unlicense */ #include "tomcrypt_private.h" -#ifdef LTC_DER - /* Compare an OID string to an array of `unsigned long`. @return CRYPT_OK if equal @@ -28,6 +26,8 @@ int pk_oid_cmp_with_ulong(const char *o1, const unsigned long *o2, unsigned long return CRYPT_OK; } +#ifdef LTC_DER + /* Compare an OID string to an OID element decoded from ASN.1. @return CRYPT_OK if equal diff --git a/src/pk/ecc/ecc_decrypt_key.c b/src/pk/ecc/ecc_decrypt_key.c index ad2d9b135..6697eda00 100644 --- a/src/pk/ecc/ecc_decrypt_key.c +++ b/src/pk/ecc/ecc_decrypt_key.c @@ -8,7 +8,7 @@ ECC Crypto, Tom St Denis */ -#ifdef LTC_MECC +#if defined(LTC_MECC) && defined(LTC_DER) /** Decrypt an ECC encrypted key diff --git a/src/pk/ecc/ecc_encrypt_key.c b/src/pk/ecc/ecc_encrypt_key.c index 9929ff5af..bac62bf77 100644 --- a/src/pk/ecc/ecc_encrypt_key.c +++ b/src/pk/ecc/ecc_encrypt_key.c @@ -8,7 +8,7 @@ ECC Crypto, Tom St Denis */ -#ifdef LTC_MECC +#if defined(LTC_MECC) && defined(LTC_DER) /** Encrypt a symmetric key with ECC diff --git a/src/pk/ecc/ecc_export.c b/src/pk/ecc/ecc_export.c index edbe4c39e..69b53bc71 100644 --- a/src/pk/ecc/ecc_export.c +++ b/src/pk/ecc/ecc_export.c @@ -8,7 +8,7 @@ ECC Crypto, Tom St Denis */ -#ifdef LTC_MECC +#if defined(LTC_MECC) && defined(LTC_DER) /** Export an ECC key as a binary packet diff --git a/src/pk/ecc/ecc_export_openssl.c b/src/pk/ecc/ecc_export_openssl.c index 35d83b706..35d8ed05d 100644 --- a/src/pk/ecc/ecc_export_openssl.c +++ b/src/pk/ecc/ecc_export_openssl.c @@ -3,7 +3,7 @@ #include "tomcrypt_private.h" -#ifdef LTC_MECC +#if defined(LTC_MECC) && defined(LTC_DER) /** Export an ECC key as a binary packet diff --git a/src/pk/ecc/ecc_import.c b/src/pk/ecc/ecc_import.c index 48e050639..181fc9a96 100644 --- a/src/pk/ecc/ecc_import.c +++ b/src/pk/ecc/ecc_import.c @@ -8,7 +8,7 @@ ECC Crypto, Tom St Denis */ -#ifdef LTC_MECC +#if defined(LTC_MECC) && defined(LTC_DER) /** Import an ECC key from a binary packet diff --git a/src/pk/ecc/ecc_import_openssl.c b/src/pk/ecc/ecc_import_openssl.c index 36a1bac94..7023b84f8 100644 --- a/src/pk/ecc/ecc_import_openssl.c +++ b/src/pk/ecc/ecc_import_openssl.c @@ -3,7 +3,7 @@ #include "tomcrypt_private.h" -#ifdef LTC_MECC +#if defined(LTC_MECC) && defined(LTC_DER) static int s_ecc_import_private_with_oid(const unsigned char *in, unsigned long inlen, ecc_key *key) { diff --git a/src/pk/ecc/ecc_import_pkcs8.c b/src/pk/ecc/ecc_import_pkcs8.c index f9c473b69..9df67890f 100644 --- a/src/pk/ecc/ecc_import_pkcs8.c +++ b/src/pk/ecc/ecc_import_pkcs8.c @@ -3,7 +3,7 @@ #include "tomcrypt_private.h" -#ifdef LTC_MECC +#if defined(LTC_MECC) && defined(LTC_DER) int ecc_import_pkcs8_asn1(ltc_asn1_list *alg_id, ltc_asn1_list *priv_key, ecc_key *key) { diff --git a/src/pk/ecc/ecc_import_x509.c b/src/pk/ecc/ecc_import_x509.c index 5217c8508..2b4ea77ab 100644 --- a/src/pk/ecc/ecc_import_x509.c +++ b/src/pk/ecc/ecc_import_x509.c @@ -2,7 +2,7 @@ /* SPDX-License-Identifier: Unlicense */ #include "tomcrypt_private.h" -#ifdef LTC_MECC +#if defined(LTC_MECC) && defined(LTC_DER) static int s_ecc_import_x509_with_oid(const unsigned char *in, unsigned long inlen, ecc_key *key) { diff --git a/src/pk/ecc/ecc_recover_key.c b/src/pk/ecc/ecc_recover_key.c index a507a7867..3b6332b54 100644 --- a/src/pk/ecc/ecc_recover_key.c +++ b/src/pk/ecc/ecc_recover_key.c @@ -3,9 +3,7 @@ #include "tomcrypt_private.h" -#ifdef LTC_MECC - -#ifdef LTC_ECC_SHAMIR +#if defined(LTC_MECC) && defined(LTC_ECC_SHAMIR) /** @file ecc_recover_key.c @@ -67,14 +65,7 @@ int ecc_recover_key(const unsigned char *sig, unsigned long siglen, 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, LTC_NULL)) != CRYPT_OK) { goto error; } - } - else if (sigformat == LTC_ECCSIG_RFC7518) { + if (sigformat == LTC_ECCSIG_RFC7518) { /* RFC7518 format - raw (r,s) */ i = ltc_mp_unsigned_bin_size(key->dp.order); if (siglen != (2*i)) { @@ -105,6 +96,15 @@ int ecc_recover_key(const unsigned char *sig, unsigned long siglen, if ((err = ltc_mp_read_unsigned_bin(r, sig, 32)) != CRYPT_OK) { goto error; } if ((err = ltc_mp_read_unsigned_bin(s, sig+32, 32)) != CRYPT_OK) { goto error; } } +#ifdef LTC_DER + else 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, LTC_NULL)) != CRYPT_OK) { goto error; } + } +#endif #ifdef LTC_SSH else if (sigformat == LTC_ECCSIG_RFC5656) { char name[64], name2[64]; @@ -116,7 +116,7 @@ int ecc_recover_key(const unsigned char *sig, unsigned long siglen, LTC_SSHDATA_STRING, name, &namelen, LTC_SSHDATA_MPINT, r, LTC_SSHDATA_MPINT, s, - LTC_SSHDATA_EOL, NULL)) != CRYPT_OK) { goto error; } + LTC_SSHDATA_EOL, LTC_NULL)) != CRYPT_OK) { goto error; } /* Check curve matches identifier string */ @@ -257,4 +257,3 @@ int ecc_recover_key(const unsigned char *sig, unsigned long siglen, } #endif -#endif diff --git a/src/pk/ecc/ecc_sign_hash.c b/src/pk/ecc/ecc_sign_hash.c index b50fa4f12..c9c278b31 100644 --- a/src/pk/ecc/ecc_sign_hash.c +++ b/src/pk/ecc/ecc_sign_hash.c @@ -3,178 +3,39 @@ #include "tomcrypt_private.h" -#ifdef LTC_MECC +#if defined(LTC_MECC) && defined(LTC_DER) /** - @file ecc_sign_hash.c - ECC Crypto, Tom St Denis -*/ - -/** - Sign a message digest + Sign a message digest (ANSI X9.62 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 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) +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) { - 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]; + int err; + void *r, *s; - LTC_ARGCHK(in != NULL); LTC_ARGCHK(out != NULL); LTC_ARGCHK(outlen != NULL); - LTC_ARGCHK(key != NULL); - - /* is this a private key? */ - if (key->type != PK_PRIVATE) { - return CRYPT_PK_NOT_PRIVATE; - } - - /* init the bignums */ - if ((err = ltc_mp_init_multi(&r, &s, &e, &b, LTC_NULL)) != CRYPT_OK) { - return err; - } - - /* get the hash and load it as a bignum into 'e' */ - p = key->dp.order; - pbits = ltc_mp_count_bits(p); - pbytes = (pbits+7) >> 3; - if (pbits > inlen*8) { - if ((err = ltc_mp_read_unsigned_bin(e, in, inlen)) != CRYPT_OK) { goto errnokey; } - } - else if (pbits % 8 == 0) { - if ((err = ltc_mp_read_unsigned_bin(e, in, pbytes)) != CRYPT_OK) { goto errnokey; } - } - else { - shift_right = 8 - pbits % 8; - for (i=0, ch=0; i> shift_right); - } - if ((err = ltc_mp_read_unsigned_bin(e, buf, pbytes)) != CRYPT_OK) { goto errnokey; } - } - - /* make up a key and export the public copy */ - do { - if ((err = ecc_copy_curve(key, &pubkey)) != CRYPT_OK) { goto errnokey; } - if ((err = ecc_generate_key(prng, wprng, &pubkey)) != CRYPT_OK) { goto errnokey; } - /* find r = x1 mod n */ - if ((err = ltc_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 (ltc_mp_copy(pubkey.pubkey.x, s) != CRYPT_OK) { goto error; } - while (ltc_mp_cmp_d(s, 0) == LTC_MP_GT && ltc_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 = ltc_mp_sub(s, p, s)) != CRYPT_OK) { goto error; } - } - if (ltc_mp_isodd(pubkey.pubkey.y)) v += 1; - } - - if (ltc_mp_iszero(r) == LTC_MP_YES) { - ecc_free(&pubkey); - } else { - if ((err = rand_bn_upto(b, p, prng, wprng)) != CRYPT_OK) { goto error; } /* b = blinding value */ - /* find s = (e + xr)/k */ - if ((err = ltc_mp_mulmod(pubkey.k, b, p, pubkey.k)) != CRYPT_OK) { goto error; } /* k = kb */ - if ((err = ltc_mp_invmod(pubkey.k, p, pubkey.k)) != CRYPT_OK) { goto error; } /* k = 1/kb */ - if ((err = ltc_mp_mulmod(key->k, r, p, s)) != CRYPT_OK) { goto error; } /* s = xr */ - if ((err = ltc_mp_mulmod(pubkey.k, s, p, s)) != CRYPT_OK) { goto error; } /* s = xr/kb */ - if ((err = ltc_mp_mulmod(pubkey.k, e, p, e)) != CRYPT_OK) { goto error; } /* e = e/kb */ - if ((err = ltc_mp_add(e, s, s)) != CRYPT_OK) { goto error; } /* s = e/kb + xr/kb */ - if ((err = ltc_mp_mulmod(s, b, p, s)) != CRYPT_OK) { goto error; } /* s = b(e/kb + xr/kb) = (e + xr)/k */ - ecc_free(&pubkey); - if (ltc_mp_iszero(s) == LTC_MP_NO) { - break; - } - } - } while (--max_iterations > 0); - - if (max_iterations == 0) { - goto errnokey; - } - - 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 = ltc_mp_unsigned_bin_size(r); - if ((err = ltc_mp_to_unsigned_bin(r, out + (pbytes - i))) != CRYPT_OK) { goto errnokey; } - i = ltc_mp_unsigned_bin_size(s); - if ((err = ltc_mp_to_unsigned_bin(s, out + (2*pbytes - i))) != CRYPT_OK) { goto errnokey; } - *outlen = 2*pbytes; - err = CRYPT_OK; - } - else if (sigformat == LTC_ECCSIG_ETH27) { - /* Ethereum (v,r,s) format */ - if (pk_oid_cmp_with_ulong("1.3.132.0.10", key->dp.oid, key->dp.oidlen) != CRYPT_OK) { - /* 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 = ltc_mp_unsigned_bin_size(r); - if ((err = ltc_mp_to_unsigned_bin(r, out + 32 - i)) != CRYPT_OK) { goto errnokey; } - i = ltc_mp_unsigned_bin_size(s); - if ((err = ltc_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; - } -#ifdef LTC_SSH - else if (sigformat == LTC_ECCSIG_RFC5656) { - /* Get identifier string */ - char name[64]; - unsigned long namelen = sizeof(name); - if ((err = ecc_ssh_ecdsa_encode_name(name, &namelen, key)) != CRYPT_OK) { goto errnokey; } - - /* Store as SSH data sequence, per RFC4251 */ - err = ssh_encode_sequence_multi(out, outlen, - LTC_SSHDATA_STRING, name, namelen, - LTC_SSHDATA_MPINT, r, - LTC_SSHDATA_MPINT, s, - LTC_SSHDATA_EOL, NULL); - } -#endif - else { - /* Unknown signature format */ - err = CRYPT_ERROR; - goto error; - } + if ((err = ltc_mp_init_multi(&r, &s, LTC_NULL)) != CRYPT_OK) return err; + if ((err = ecc_sign_hash_internal(in, inlen, r, s, prng, wprng, NULL, key)) != CRYPT_OK) goto error; - goto errnokey; + /* 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); error: - ecc_free(&pubkey); -errnokey: - ltc_mp_deinit_multi(r, s, e, b, LTC_NULL); + ltc_mp_deinit_multi(r, s, LTC_NULL); return err; } diff --git a/src/pk/ecc/ecc_sign_hash_eth27.c b/src/pk/ecc/ecc_sign_hash_eth27.c new file mode 100644 index 000000000..dd06d14cb --- /dev/null +++ b/src/pk/ecc/ecc_sign_hash_eth27.c @@ -0,0 +1,57 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +#include "tomcrypt_private.h" + +#ifdef LTC_MECC + +/** + Sign a message digest (Ethereum format with recovery_id+27) + @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_eth27(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + prng_state *prng, int wprng, const ecc_key *key) +{ + int err, recid; + void *r, *s; + unsigned long i; + + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + LTC_ARGCHK(key != NULL); + + /* Only valid for secp256k1 - OID 1.3.132.0.10 */ + if (pk_oid_cmp_with_ulong("1.3.132.0.10", key->dp.oid, key->dp.oidlen) != CRYPT_OK) { + return CRYPT_ERROR; + } + if (*outlen < 65) { + *outlen = 65; + return CRYPT_BUFFER_OVERFLOW; + } + + if ((err = ltc_mp_init_multi(&r, &s, LTC_NULL)) != CRYPT_OK) return err; + if ((err = ecc_sign_hash_internal(in, inlen, r, s, prng, wprng, &recid, key)) != CRYPT_OK) goto error; + + zeromem(out, 65); + *outlen = 65; + i = ltc_mp_unsigned_bin_size(r); + if ((err = ltc_mp_to_unsigned_bin(r, out + 32 - i)) != CRYPT_OK) goto error; + i = ltc_mp_unsigned_bin_size(s); + if ((err = ltc_mp_to_unsigned_bin(s, out + 64 - i)) != CRYPT_OK) goto error; + out[64] = (unsigned char)(recid + 27); /* Recovery ID is 27/28 for Ethereum */ + err = CRYPT_OK; + +error: + ltc_mp_deinit_multi(r, s, LTC_NULL); + return err; +} + +#endif diff --git a/src/pk/ecc/ecc_sign_hash_internal.c b/src/pk/ecc/ecc_sign_hash_internal.c new file mode 100644 index 000000000..ef4628150 --- /dev/null +++ b/src/pk/ecc/ecc_sign_hash_internal.c @@ -0,0 +1,111 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +#include "tomcrypt_private.h" + +#ifdef LTC_MECC + +int ecc_sign_hash_internal(const unsigned char *in, unsigned long inlen, + void *r, void *s, prng_state *prng, int wprng, + int *recid, const ecc_key *key) +{ + ecc_key pubkey; + void *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]; + + LTC_ARGCHK(r != NULL); + LTC_ARGCHK(s != NULL); + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(key != NULL); + + /* is this a private key? */ + if (key->type != PK_PRIVATE) { + return CRYPT_PK_NOT_PRIVATE; + } + + /* init the bignums */ + if ((err = ltc_mp_init_multi(&e, &b, LTC_NULL)) != CRYPT_OK) { + return err; + } + + /* get the hash and load it as a bignum into 'e' */ + p = key->dp.order; + pbits = ltc_mp_count_bits(p); + pbytes = (pbits+7) >> 3; + if (pbits > inlen*8) { + if ((err = ltc_mp_read_unsigned_bin(e, (unsigned char *)in, inlen)) != CRYPT_OK) { goto errnokey; } + } + else if (pbits % 8 == 0) { + if ((err = ltc_mp_read_unsigned_bin(e, (unsigned char *)in, pbytes)) != CRYPT_OK) { goto errnokey; } + } + else { + if (pbytes >= MAXBLOCKSIZE) { + err = CRYPT_BUFFER_OVERFLOW; + goto error; + } + shift_right = 8 - pbits % 8; + for (i=0, ch=0; i> shift_right); + } + if ((err = ltc_mp_read_unsigned_bin(e, (unsigned char *)buf, pbytes)) != CRYPT_OK) { goto errnokey; } + } + + /* make up a key and export the public copy */ + do { + if ((err = ecc_copy_curve(key, &pubkey)) != CRYPT_OK) { goto errnokey; } + if ((err = ecc_generate_key(prng, wprng, &pubkey)) != CRYPT_OK) { goto errnokey; } + + /* find r = x1 mod n */ + if ((err = ltc_mp_mod(pubkey.pubkey.x, p, r)) != CRYPT_OK) { goto error; } + + if (recid) { + /* find recovery ID (if needed) */ + v = 0; + if (ltc_mp_copy(pubkey.pubkey.x, s) != CRYPT_OK) { goto error; } + while (ltc_mp_cmp_d(s, 0) == LTC_MP_GT && ltc_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 = ltc_mp_sub(s, p, s)) != CRYPT_OK) { goto error; } + } + if (ltc_mp_isodd(pubkey.pubkey.y)) v += 1; + } + + if (ltc_mp_iszero(r) == LTC_MP_YES) { + ecc_free(&pubkey); + } else { + if ((err = rand_bn_upto(b, p, prng, wprng)) != CRYPT_OK) { goto error; } /* b = blinding value */ + /* find s = (e + xr)/k */ + if ((err = ltc_mp_mulmod(pubkey.k, b, p, pubkey.k)) != CRYPT_OK) { goto error; } /* k = kb */ + if ((err = ltc_mp_invmod(pubkey.k, p, pubkey.k)) != CRYPT_OK) { goto error; } /* k = 1/kb */ + if ((err = ltc_mp_mulmod(key->k, r, p, s)) != CRYPT_OK) { goto error; } /* s = xr */ + if ((err = ltc_mp_mulmod(pubkey.k, s, p, s)) != CRYPT_OK) { goto error; } /* s = xr/kb */ + if ((err = ltc_mp_mulmod(pubkey.k, e, p, e)) != CRYPT_OK) { goto error; } /* e = e/kb */ + if ((err = ltc_mp_add(e, s, s)) != CRYPT_OK) { goto error; } /* s = e/kb + xr/kb */ + if ((err = ltc_mp_mulmod(s, b, p, s)) != CRYPT_OK) { goto error; } /* s = b(e/kb + xr/kb) = (e + xr)/k */ + ecc_free(&pubkey); + if (ltc_mp_iszero(s) == LTC_MP_NO) { + break; + } + } + } while (--max_iterations > 0); + + if (max_iterations == 0) { + goto errnokey; + } + + if (recid) *recid = v; + + goto errnokey; +error: + ecc_free(&pubkey); +errnokey: + ltc_mp_deinit_multi(e, b, LTC_NULL); + return err; +} + +#endif diff --git a/src/pk/ecc/ecc_sign_hash_rfc5656.c b/src/pk/ecc/ecc_sign_hash_rfc5656.c new file mode 100644 index 000000000..fd9f10e24 --- /dev/null +++ b/src/pk/ecc/ecc_sign_hash_rfc5656.c @@ -0,0 +1,48 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +#include "tomcrypt_private.h" + +#if defined(LTC_MECC) && defined(LTC_SSH) + +/** + Sign a message digest (RFC5656 / SSH 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_rfc5656(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + prng_state *prng, int wprng, const ecc_key *key) +{ + int err; + void *r, *s; + char name[64]; + unsigned long namelen = sizeof(name); + + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + /* Get identifier string */ + if ((err = ecc_ssh_ecdsa_encode_name(name, &namelen, key)) != CRYPT_OK) return err; + + if ((err = ltc_mp_init_multi(&r, &s, LTC_NULL)) != CRYPT_OK) return err; + if ((err = ecc_sign_hash_internal(in, inlen, r, s, prng, wprng, NULL, key)) != CRYPT_OK) goto error; + + /* Store as SSH data sequence, per RFC4251 */ + err = ssh_encode_sequence_multi(out, outlen, + LTC_SSHDATA_STRING, name, namelen, + LTC_SSHDATA_MPINT, r, + LTC_SSHDATA_MPINT, s, + LTC_SSHDATA_EOL, LTC_NULL); +error: + ltc_mp_deinit_multi(r, s, LTC_NULL); + return err; +} + +#endif diff --git a/src/pk/ecc/ecc_sign_hash_rfc7518.c b/src/pk/ecc/ecc_sign_hash_rfc7518.c new file mode 100644 index 000000000..1faefca15 --- /dev/null +++ b/src/pk/ecc/ecc_sign_hash_rfc7518.c @@ -0,0 +1,73 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +#include "tomcrypt_private.h" + +#ifdef LTC_MECC + +/** + Sign a message digest (RFC7518 format + recovery_id) + @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 recid [out] Recovery ID + @param key A private ECC key + @return CRYPT_OK if successful +*/ +int ecc_sign_hash_rfc7518_ex(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + prng_state *prng, int wprng, + int *recid, const ecc_key *key) +{ + int err; + void *r, *s; + unsigned long pbytes, i; + + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + LTC_ARGCHK(key != NULL); + + /* RFC7518 format - raw (r,s) */ + pbytes = ltc_mp_unsigned_bin_size(key->dp.order); + if (*outlen < 2 * pbytes) { + *outlen = 2 * pbytes; + return CRYPT_BUFFER_OVERFLOW; + } + + if ((err = ltc_mp_init_multi(&r, &s, LTC_NULL)) != CRYPT_OK) return err; + if ((err = ecc_sign_hash_internal(in, inlen, r, s, prng, wprng, recid, key)) != CRYPT_OK) goto error; + + zeromem(out, 2 * pbytes); + *outlen = 2 * pbytes; + i = ltc_mp_unsigned_bin_size(r); + if ((err = ltc_mp_to_unsigned_bin(r, out + pbytes - i)) != CRYPT_OK) goto error; + i = ltc_mp_unsigned_bin_size(s); + err = ltc_mp_to_unsigned_bin(s, out + 2 * pbytes - i); + +error: + ltc_mp_deinit_multi(r, s, LTC_NULL); + return err; +} + +/** + Sign a message digest (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_rfc7518_ex(in, inlen, out, outlen, prng, wprng, NULL, key); +} + +#endif diff --git a/src/pk/ecc/ecc_verify_hash.c b/src/pk/ecc/ecc_verify_hash.c index 6c74ca448..494c474e2 100644 --- a/src/pk/ecc/ecc_verify_hash.c +++ b/src/pk/ecc/ecc_verify_hash.c @@ -3,7 +3,7 @@ #include "tomcrypt_private.h" -#ifdef LTC_MECC +#if defined(LTC_MECC) && defined(LTC_DER) /** @file ecc_verify_hash.c @@ -11,195 +11,36 @@ */ /** - Verify an ECC signature in RFC7518 format + Verify an ECC signature (ANSI X9.62 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 stat [out] 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) +int ecc_verify_hash(const unsigned char *sig, unsigned long siglen, + const unsigned char *hash, unsigned long hashlen, + 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; - void *mu = NULL, *ma = NULL; - void *mp = NULL; - int err; - unsigned long pbits, pbytes, i, shift_right; - unsigned char ch, buf[MAXBLOCKSIZE]; + void *r, *s; + int err; - LTC_ARGCHK(sig != NULL); - LTC_ARGCHK(hash != NULL); - LTC_ARGCHK(stat != NULL); - LTC_ARGCHK(key != NULL); + LTC_ARGCHK(sig != NULL); - /* default to invalid signature */ - *stat = 0; + if ((err = ltc_mp_init_multi(&r, &s, NULL)) != CRYPT_OK) return err; - /* allocate ints */ - if ((err = ltc_mp_init_multi(&r, &s, &v, &w, &u1, &u2, &e, &a_plus3, LTC_NULL)) != CRYPT_OK) { - return err; - } - - p = key->dp.order; - m = key->dp.prime; - a = key->dp.A; - if ((err = ltc_mp_add_d(a, 3, a_plus3)) != CRYPT_OK) { - goto error; - } - - /* allocate points */ - mG = ltc_ecc_new_point(); - mQ = ltc_ecc_new_point(); - if (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, + /* 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, LTC_NULL)) != CRYPT_OK) { goto error; } - } - else if (sigformat == LTC_ECCSIG_RFC7518) { - /* RFC7518 format - raw (r,s) */ - i = ltc_mp_unsigned_bin_size(key->dp.order); - if (siglen != (2 * i)) { - err = CRYPT_INVALID_PACKET; - goto error; - } - if ((err = ltc_mp_read_unsigned_bin(r, sig, i)) != CRYPT_OK) { goto error; } - if ((err = ltc_mp_read_unsigned_bin(s, sig+i, i)) != CRYPT_OK) { goto error; } - } - else if (sigformat == LTC_ECCSIG_ETH27) { - /* Ethereum (v,r,s) format */ - if (pk_oid_cmp_with_ulong("1.3.132.0.10", key->dp.oid, key->dp.oidlen) != CRYPT_OK) { - /* 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 = ltc_mp_read_unsigned_bin(r, sig, 32)) != CRYPT_OK) { goto error; } - if ((err = ltc_mp_read_unsigned_bin(s, sig+32, 32)) != CRYPT_OK) { goto error; } - } -#ifdef LTC_SSH - else if (sigformat == LTC_ECCSIG_RFC5656) { - char name[64], name2[64]; - unsigned long namelen = sizeof(name); - unsigned long name2len = sizeof(name2); - - /* Decode as SSH data sequence, per RFC4251 */ - if ((err = ssh_decode_sequence_multi(sig, &siglen, - LTC_SSHDATA_STRING, name, &namelen, - LTC_SSHDATA_MPINT, r, - LTC_SSHDATA_MPINT, s, - LTC_SSHDATA_EOL, NULL)) != CRYPT_OK) { goto error; } - - - /* Check curve matches identifier string */ - if ((err = ecc_ssh_ecdsa_encode_name(name2, &name2len, key)) != CRYPT_OK) { goto error; } - if ((namelen != name2len) || (XSTRCMP(name, name2) != 0)) { - err = CRYPT_INVALID_ARG; - goto error; - } - } -#endif - else { - /* Unknown signature format */ - err = CRYPT_ERROR; - goto error; - } - - /* check for zero */ - if (ltc_mp_cmp_d(r, 0) != LTC_MP_GT || ltc_mp_cmp_d(s, 0) != LTC_MP_GT || - ltc_mp_cmp(r, p) != LTC_MP_LT || ltc_mp_cmp(s, p) != LTC_MP_LT) { - err = CRYPT_INVALID_PACKET; - goto error; - } - - /* read hash - truncate if needed */ - pbits = ltc_mp_count_bits(p); - pbytes = (pbits+7) >> 3; - if (pbits > hashlen*8) { - if ((err = ltc_mp_read_unsigned_bin(e, hash, hashlen)) != CRYPT_OK) { goto error; } - } - else if (pbits % 8 == 0) { - if ((err = ltc_mp_read_unsigned_bin(e, hash, pbytes)) != CRYPT_OK) { goto error; } - } - else { - shift_right = 8 - pbits % 8; - for (i=0, ch=0; i> shift_right); - } - if ((err = ltc_mp_read_unsigned_bin(e, buf, pbytes)) != CRYPT_OK) { goto error; } - } - - /* w = s^-1 mod n */ - if ((err = ltc_mp_invmod(s, p, w)) != CRYPT_OK) { goto error; } - - /* u1 = ew */ - if ((err = ltc_mp_mulmod(e, w, p, u1)) != CRYPT_OK) { goto error; } - - /* u2 = rw */ - if ((err = ltc_mp_mulmod(r, w, p, u2)) != CRYPT_OK) { goto error; } - - /* find mG and mQ */ - if ((err = ltc_ecc_copy_point(&key->dp.base, mG)) != CRYPT_OK) { goto error; } - if ((err = ltc_ecc_copy_point(&key->pubkey, mQ)) != CRYPT_OK) { goto error; } - - /* find the montgomery mp */ - if ((err = ltc_mp_montgomery_setup(m, &mp)) != CRYPT_OK) { goto error; } - - /* for curves with a == -3 keep ma == NULL */ - if (ltc_mp_cmp(a_plus3, m) != LTC_MP_EQ) { - if ((err = ltc_mp_init_multi(&mu, &ma, LTC_NULL)) != CRYPT_OK) { goto error; } - if ((err = ltc_mp_montgomery_normalization(mu, m)) != CRYPT_OK) { goto error; } - if ((err = ltc_mp_mulmod(a, mu, m, ma)) != CRYPT_OK) { goto error; } - } - - /* compute u1*mG + u2*mQ = mG */ - if (ltc_mp.ecc_mul2add == NULL) { - if ((err = ltc_mp.ecc_ptmul(u1, mG, mG, a, m, 0)) != CRYPT_OK) { goto error; } - if ((err = ltc_mp.ecc_ptmul(u2, mQ, mQ, a, m, 0)) != CRYPT_OK) { goto error; } - - /* add them */ - if ((err = ltc_mp.ecc_ptadd(mQ, mG, mG, ma, m, mp)) != CRYPT_OK) { goto error; } - - /* reduce */ - if ((err = ltc_mp.ecc_map(mG, m, mp)) != CRYPT_OK) { goto error; } - } else { - /* use Shamir's trick to compute u1*mG + u2*mQ using half of the doubles */ - if ((err = ltc_mp.ecc_mul2add(mG, u1, mQ, u2, mG, ma, m)) != CRYPT_OK) { goto error; } - } - - /* v = X_x1 mod n */ - if ((err = ltc_mp_mod(mG->x, p, v)) != CRYPT_OK) { goto error; } - /* does v == r */ - if (ltc_mp_cmp(v, r) == LTC_MP_EQ) { - *stat = 1; - } + err = ecc_verify_hash_internal(r, s, hash, hashlen, stat, key); - /* clear up and return */ - err = CRYPT_OK; error: - if (mG != NULL) ltc_ecc_del_point(mG); - if (mQ != NULL) ltc_ecc_del_point(mQ); - if (mu != NULL) ltc_mp_clear(mu); - if (ma != NULL) ltc_mp_clear(ma); - ltc_mp_deinit_multi(r, s, v, w, u1, u2, e, a_plus3, LTC_NULL); - if (mp != NULL) ltc_mp_montgomery_free(mp); + ltc_mp_deinit_multi(r, s, LTC_NULL); return err; } diff --git a/src/pk/ecc/ecc_verify_hash_eth27.c b/src/pk/ecc/ecc_verify_hash_eth27.c new file mode 100644 index 000000000..587ecd2da --- /dev/null +++ b/src/pk/ecc/ecc_verify_hash_eth27.c @@ -0,0 +1,53 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +#include "tomcrypt_private.h" + +#ifdef LTC_MECC + +/** + @file ecc_verify_hash.c + ECC Crypto, Tom St Denis +*/ + +/** + Verify an ECC signature (Ethereum format with recovery_id+27) + @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 [out] 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_eth27(const unsigned char *sig, unsigned long siglen, + const unsigned char *hash, unsigned long hashlen, + int *stat, const ecc_key *key) +{ + void *r, *s; + int err; + + LTC_ARGCHK(sig != NULL); + LTC_ARGCHK(key != NULL); + + /* Only valid for secp256k1 - OID 1.3.132.0.10 */ + if (pk_oid_cmp_with_ulong("1.3.132.0.10", key->dp.oid, key->dp.oidlen) != CRYPT_OK) { + return CRYPT_ERROR; + } + /* Only secp256k1 curves uses this format, so must be 65 bytes long */ + if (siglen != 65) { + return CRYPT_INVALID_PACKET; + } + + if ((err = ltc_mp_init_multi(&r, &s, LTC_NULL)) != CRYPT_OK) return err; + if ((err = ltc_mp_read_unsigned_bin(r, (unsigned char *)sig, 32)) != CRYPT_OK) goto error; + if ((err = ltc_mp_read_unsigned_bin(s, (unsigned char *)sig + 32, 32)) != CRYPT_OK) goto error; + + err = ecc_verify_hash_internal(r, s, hash, hashlen, stat, key); + +error: + ltc_mp_deinit_multi(r, s, LTC_NULL); + return err; +} + +#endif diff --git a/src/pk/ecc/ecc_verify_hash_internal.c b/src/pk/ecc/ecc_verify_hash_internal.c new file mode 100644 index 000000000..ae666110d --- /dev/null +++ b/src/pk/ecc/ecc_verify_hash_internal.c @@ -0,0 +1,137 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +#include "tomcrypt_private.h" + +#ifdef LTC_MECC + +int ecc_verify_hash_internal(void *r, void *s, + const unsigned char *hash, unsigned long hashlen, + int *stat, const ecc_key *key) +{ + ecc_point *mG = NULL, *mQ = NULL; + void *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; + unsigned char ch, buf[MAXBLOCKSIZE]; + + LTC_ARGCHK(r != NULL); + LTC_ARGCHK(s != NULL); + LTC_ARGCHK(hash != NULL); + LTC_ARGCHK(stat != NULL); + LTC_ARGCHK(key != NULL); + + /* default to invalid signature */ + *stat = 0; + + /* allocate ints */ + if ((err = ltc_mp_init_multi(&v, &w, &u1, &u2, &e, &a_plus3, LTC_NULL)) != CRYPT_OK) { + return err; + } + + p = key->dp.order; + m = key->dp.prime; + a = key->dp.A; + if ((err = ltc_mp_add_d(a, 3, a_plus3)) != CRYPT_OK) { + goto error; + } + + /* allocate points */ + mG = ltc_ecc_new_point(); + mQ = ltc_ecc_new_point(); + if (mQ == NULL || mG == NULL) { + err = CRYPT_MEM; + goto error; + } + + /* check for zero */ + if (ltc_mp_cmp_d(r, 0) != LTC_MP_GT || ltc_mp_cmp_d(s, 0) != LTC_MP_GT || + ltc_mp_cmp(r, p) != LTC_MP_LT || ltc_mp_cmp(s, p) != LTC_MP_LT) { + err = CRYPT_INVALID_PACKET; + goto error; + } + + /* read hash - truncate if needed */ + pbits = ltc_mp_count_bits(p); + pbytes = (pbits+7) >> 3; + if (pbits > hashlen*8) { + if ((err = ltc_mp_read_unsigned_bin(e, (unsigned char *)hash, hashlen)) != CRYPT_OK) { goto error; } + } + else if (pbits % 8 == 0) { + if ((err = ltc_mp_read_unsigned_bin(e, (unsigned char *)hash, pbytes)) != CRYPT_OK) { goto error; } + } + else { + if (pbytes >= MAXBLOCKSIZE) { + err = CRYPT_BUFFER_OVERFLOW; + goto error; + } + shift_right = 8 - pbits % 8; + for (i=0, ch=0; i> shift_right); + } + if ((err = ltc_mp_read_unsigned_bin(e, (unsigned char *)buf, pbytes)) != CRYPT_OK) { goto error; } + } + + /* w = s^-1 mod n */ + if ((err = ltc_mp_invmod(s, p, w)) != CRYPT_OK) { goto error; } + + /* u1 = ew */ + if ((err = ltc_mp_mulmod(e, w, p, u1)) != CRYPT_OK) { goto error; } + + /* u2 = rw */ + if ((err = ltc_mp_mulmod(r, w, p, u2)) != CRYPT_OK) { goto error; } + + /* find mG and mQ */ + if ((err = ltc_ecc_copy_point(&key->dp.base, mG)) != CRYPT_OK) { goto error; } + if ((err = ltc_ecc_copy_point(&key->pubkey, mQ)) != CRYPT_OK) { goto error; } + + /* find the montgomery mp */ + if ((err = ltc_mp_montgomery_setup(m, &mp)) != CRYPT_OK) { goto error; } + + /* for curves with a == -3 keep ma == NULL */ + if (ltc_mp_cmp(a_plus3, m) != LTC_MP_EQ) { + if ((err = ltc_mp_init_multi(&mu, &ma, NULL)) != CRYPT_OK) { goto error; } + if ((err = ltc_mp_montgomery_normalization(mu, m)) != CRYPT_OK) { goto error; } + if ((err = ltc_mp_mulmod(a, mu, m, ma)) != CRYPT_OK) { goto error; } + } + + /* compute u1*mG + u2*mQ = mG */ + if (ltc_mp.ecc_mul2add == NULL) { + if ((err = ltc_mp.ecc_ptmul(u1, mG, mG, a, m, 0)) != CRYPT_OK) { goto error; } + if ((err = ltc_mp.ecc_ptmul(u2, mQ, mQ, a, m, 0)) != CRYPT_OK) { goto error; } + + /* add them */ + if ((err = ltc_mp.ecc_ptadd(mQ, mG, mG, ma, m, mp)) != CRYPT_OK) { goto error; } + + /* reduce */ + if ((err = ltc_mp.ecc_map(mG, m, mp)) != CRYPT_OK) { goto error; } + } else { + /* use Shamir's trick to compute u1*mG + u2*mQ using half of the doubles */ + if ((err = ltc_mp.ecc_mul2add(mG, u1, mQ, u2, mG, ma, m)) != CRYPT_OK) { goto error; } + } + + /* v = X_x1 mod n */ + if ((err = ltc_mp_mod(mG->x, p, v)) != CRYPT_OK) { goto error; } + + /* does v == r */ + if (ltc_mp_cmp(v, r) == LTC_MP_EQ) { + *stat = 1; + } + + /* clear up and return */ + err = CRYPT_OK; +error: + if (mG != NULL) ltc_ecc_del_point(mG); + if (mQ != NULL) ltc_ecc_del_point(mQ); + if (mu != NULL) ltc_mp_clear(mu); + if (ma != NULL) ltc_mp_clear(ma); + ltc_mp_deinit_multi(v, w, u1, u2, e, a_plus3, LTC_NULL); + if (mp != NULL) ltc_mp_montgomery_free(mp); + return err; +} + +#endif diff --git a/src/pk/ecc/ecc_verify_hash_rfc5656.c b/src/pk/ecc/ecc_verify_hash_rfc5656.c new file mode 100644 index 000000000..027e4a70b --- /dev/null +++ b/src/pk/ecc/ecc_verify_hash_rfc5656.c @@ -0,0 +1,65 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +#include "tomcrypt_private.h" + +#if defined(LTC_MECC) && defined(LTC_SSH) + +/** + @file ecc_verify_hash.c + ECC Crypto, Tom St Denis +*/ + +/** + Verify an ECC signature (RFC5656 / SSH 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 [out] 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_rfc5656(const unsigned char *sig, unsigned long siglen, + const unsigned char *hash, unsigned long hashlen, + int *stat, const ecc_key *key) +{ + void *r, *s; + int err; + char name[64], name2[64]; + unsigned long namelen = sizeof(name); + unsigned long name2len = sizeof(name2); + unsigned long slen = siglen; + + LTC_ARGCHK(sig != NULL); + LTC_ARGCHK(key != NULL); + + if ((err = ltc_mp_init_multi(&r, &s, LTC_NULL)) != CRYPT_OK) return err; + + /* Decode as SSH data sequence, per RFC4251 */ + if ((err = ssh_decode_sequence_multi(sig, &slen, + LTC_SSHDATA_STRING, name, &namelen, + LTC_SSHDATA_MPINT, r, + LTC_SSHDATA_MPINT, s, + LTC_SSHDATA_EOL, LTC_NULL)) != CRYPT_OK) goto error; + + if (slen != siglen) { + err = CRYPT_INVALID_PACKET; + goto error; + } + + /* Check curve matches identifier string */ + if ((err = ecc_ssh_ecdsa_encode_name(name2, &name2len, key)) != CRYPT_OK) goto error; + if (XSTRCMP(name,name2) != 0) { + err = CRYPT_INVALID_ARG; + goto error; + } + + err = ecc_verify_hash_internal(r, s, hash, hashlen, stat, key); + +error: + ltc_mp_deinit_multi(r, s, LTC_NULL); + return err; +} + +#endif diff --git a/src/pk/ecc/ecc_verify_hash_rfc7518.c b/src/pk/ecc/ecc_verify_hash_rfc7518.c new file mode 100644 index 000000000..b048bdc20 --- /dev/null +++ b/src/pk/ecc/ecc_verify_hash_rfc7518.c @@ -0,0 +1,52 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +#include "tomcrypt_private.h" + +#ifdef LTC_MECC + +/** + @file ecc_verify_hash.c + ECC Crypto, Tom St Denis +*/ + +/** + Verify an ECC signature (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 [out] 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) +{ + void *r, *s; + int err; + unsigned long i; + + LTC_ARGCHK(sig != NULL); + LTC_ARGCHK(key != NULL); + + if ((err = ltc_mp_init_multi(&r, &s, LTC_NULL)) != CRYPT_OK) return err; + + /* RFC7518 format - raw (r,s) */ + i = ltc_mp_unsigned_bin_size(key->dp.order); + if (siglen != (2 * i)) { + err = CRYPT_INVALID_PACKET; + goto error; + } + if ((err = ltc_mp_read_unsigned_bin(r, (unsigned char *)sig, i)) != CRYPT_OK) goto error; + if ((err = ltc_mp_read_unsigned_bin(s, (unsigned char *)sig + i, i)) != CRYPT_OK) goto error; + + err = ecc_verify_hash_internal(r, s, hash, hashlen, stat, key); + +error: + ltc_mp_deinit_multi(r, s, LTC_NULL); + return err; +} + +#endif diff --git a/tests/ecc_test.c b/tests/ecc_test.c index 74c65fcf8..6c15f4a1c 100644 --- a/tests/ecc_test.c +++ b/tests/ecc_test.c @@ -616,10 +616,9 @@ static int s_ecc_new_api(void) #ifdef LTC_SSH /* test SSH+ECDSA/RFC5656 signature */ len = sizeof(buf); - DO(ecc_sign_hash_ex(data16, 16, buf, &len, &yarrow_prng, find_prng ("yarrow"), - LTC_ECCSIG_RFC5656, NULL, &privkey)); + DO(ecc_sign_hash_rfc5656(data16, 16, buf, &len, &yarrow_prng, find_prng ("yarrow"), &privkey)); stat = 0; - DO(ecc_verify_hash_ex(buf, len, data16, 16, LTC_ECCSIG_RFC5656, &stat, &pubkey)); + DO(ecc_verify_hash_rfc5656(buf, len, data16, 16, &stat, &pubkey)); if (stat != 1) return CRYPT_FAIL_TESTVECTOR; #endif @@ -1517,6 +1516,44 @@ static int s_ecc_import_export(void) { } #ifdef LTC_ECC_SHAMIR +static int s_ecc_test_ethereum(void) +{ +#ifdef LTC_ECC_SECP256K1 + int stat; + const ltc_ecc_curve* dp; + ecc_key key, reckey; + unsigned char buf[128]; + unsigned long len; + unsigned char data16[16] = { 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1 }; + + DO(ecc_find_curve("SECP256K1", &dp)); + + DO(ecc_make_key_ex(&yarrow_prng, find_prng ("yarrow"), &key, dp)); + + /* test Ethereum signature */ + len = sizeof(buf); + DO(ecc_sign_hash_eth27(data16, 16, buf, &len, &yarrow_prng, find_prng ("yarrow"), &key)); + stat = 0; + DO(ecc_verify_hash_eth27(buf, len, data16, 16, &stat, &key)); + if (stat != 1) return CRYPT_FAIL_TESTVECTOR; + + /* XXX-FIXME: TFM does not support sqrtmod_prime */ + if (strcmp(ltc_mp.name, "TomsFastMath") != 0) { + DO(ecc_set_curve(dp, &reckey)); + DO(ecc_recover_key(buf, len, data16, 16, -1, LTC_ECCSIG_ETH27, &reckey)); + DO(ecc_key_cmp(PK_PUBLIC, &key, &reckey)); + + /* cleanup */ + ecc_free(&reckey); + } + /* cleanup */ + ecc_free(&key); + return CRYPT_OK; +#else + return CRYPT_NOP; +#endif +} + static int s_ecc_test_recovery(void) { int i, recid, stat; @@ -1554,14 +1591,12 @@ static int s_ecc_test_recovery(void) 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_recover_key(eth_sig, sizeof(eth_sig)-1, eth_hash, sizeof(eth_hash), 0, LTC_ECCSIG_RFC7518, &reckey)); 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_recover_key(eth_sig, sizeof(eth_sig), eth_hash, sizeof(eth_hash), -1, LTC_ECCSIG_ETH27, &reckey)); DO(ecc_key_cmp(PK_PUBLIC, &pubkey, &reckey)); ecc_free(&reckey); @@ -1596,11 +1631,11 @@ static int s_ecc_test_recovery(void) /* 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)); + DO(ecc_sign_hash_rfc7518_ex(data16, 16, buf, &len, &yarrow_prng, find_prng ("yarrow"), &recid, &privkey)); /* test verification */ stat = 0; - DO(ecc_verify_hash_ex(buf, len, data16, 16, LTC_ECCSIG_RFC7518, &stat, &pubkey)); + DO(ecc_verify_hash_rfc7518(buf, len, data16, 16, &stat, &pubkey)); if (stat != 1) return CRYPT_FAIL_TESTVECTOR; /* test recovery */ @@ -1633,6 +1668,7 @@ int ecc_test(void) #ifdef LTC_ECC_SHAMIR DO(s_ecc_test_shamir()); DO(s_ecc_test_recovery()); + DO(s_ecc_test_ethereum()); #endif return CRYPT_OK; }