From 7354d01a2ce32bb88688736289149564d0f3149f Mon Sep 17 00:00:00 2001 From: Karel Miko Date: Sun, 12 Apr 2026 23:47:40 +0200 Subject: [PATCH 1/2] Ed448 + X448 --- doc/crypt.tex | 262 +++++- src/headers/tomcrypt_custom.h | 3 + src/headers/tomcrypt_pk.h | 80 ++ src/headers/tomcrypt_private.h | 29 + src/misc/crypt/crypt.c | 4 + src/misc/pem/pem_pkcs.c | 8 + src/pk/asn1/oid/pk_get.c | 2 + src/pk/asn1/x509/x509_import_spki.c | 4 + src/pk/ec448/ec448_common.c | 937 ++++++++++++++++++++++ src/pk/ec448/ec448_crypto_ctx.c | 51 ++ src/pk/ec448/ec448_export.c | 98 +++ src/pk/ec448/ec448_import_pkcs8.c | 86 ++ src/pk/ed448/ed448_export.c | 31 + src/pk/ed448/ed448_import.c | 35 + src/pk/ed448/ed448_import_pkcs8.c | 33 + src/pk/ed448/ed448_import_raw.c | 42 + src/pk/ed448/ed448_import_x509.c | 45 ++ src/pk/ed448/ed448_make_key.c | 36 + src/pk/ed448/ed448_sign.c | 136 ++++ src/pk/ed448/ed448_verify.c | 143 ++++ src/pk/x448/x448_export.c | 31 + src/pk/x448/x448_import.c | 35 + src/pk/x448/x448_import_pkcs8.c | 33 + src/pk/x448/x448_import_raw.c | 41 + src/pk/x448/x448_import_x509.c | 45 ++ src/pk/x448/x448_make_key.c | 42 + src/pk/x448/x448_shared_secret.c | 43 + tests/ed448_test.c | 439 ++++++++++ tests/pem/openssl_ed448_pk.pem | 4 + tests/pem/openssl_ed448_sk.pem | 4 + tests/pem/openssl_ed448_x509.pem | 11 + tests/pem/openssl_x448_pk.pem | 4 + tests/pem/openssl_x448_sk.pem | 4 + tests/pem/pkcs/openssl_ed448_sk.pk8 | 4 + tests/pem/pkcs/openssl_ed448_sk_pbes1.pk8 | 5 + tests/pem/pkcs/openssl_ed448_sk_pbes2.pk8 | 6 + tests/pem/pkcs/openssl_x448_sk.pk8 | 4 + tests/pem/pkcs/openssl_x448_sk_pbes1.pk8 | 5 + tests/pem/pkcs/openssl_x448_sk_pbes2.pk8 | 6 + tests/pem_test.c | 2 + tests/test.c | 2 + tests/tomcrypt_test.h | 2 + tests/x448_test.c | 231 ++++++ 43 files changed, 3057 insertions(+), 11 deletions(-) create mode 100644 src/pk/ec448/ec448_common.c create mode 100644 src/pk/ec448/ec448_crypto_ctx.c create mode 100644 src/pk/ec448/ec448_export.c create mode 100644 src/pk/ec448/ec448_import_pkcs8.c create mode 100644 src/pk/ed448/ed448_export.c create mode 100644 src/pk/ed448/ed448_import.c create mode 100644 src/pk/ed448/ed448_import_pkcs8.c create mode 100644 src/pk/ed448/ed448_import_raw.c create mode 100644 src/pk/ed448/ed448_import_x509.c create mode 100644 src/pk/ed448/ed448_make_key.c create mode 100644 src/pk/ed448/ed448_sign.c create mode 100644 src/pk/ed448/ed448_verify.c create mode 100644 src/pk/x448/x448_export.c create mode 100644 src/pk/x448/x448_import.c create mode 100644 src/pk/x448/x448_import_pkcs8.c create mode 100644 src/pk/x448/x448_import_raw.c create mode 100644 src/pk/x448/x448_import_x509.c create mode 100644 src/pk/x448/x448_make_key.c create mode 100644 src/pk/x448/x448_shared_secret.c create mode 100644 tests/ed448_test.c create mode 100644 tests/pem/openssl_ed448_pk.pem create mode 100644 tests/pem/openssl_ed448_sk.pem create mode 100644 tests/pem/openssl_ed448_x509.pem create mode 100644 tests/pem/openssl_x448_pk.pem create mode 100644 tests/pem/openssl_x448_sk.pem create mode 100644 tests/pem/pkcs/openssl_ed448_sk.pk8 create mode 100644 tests/pem/pkcs/openssl_ed448_sk_pbes1.pk8 create mode 100644 tests/pem/pkcs/openssl_ed448_sk_pbes2.pk8 create mode 100644 tests/pem/pkcs/openssl_x448_sk.pk8 create mode 100644 tests/pem/pkcs/openssl_x448_sk_pbes1.pk8 create mode 100644 tests/pem/pkcs/openssl_x448_sk_pbes2.pk8 create mode 100644 tests/x448_test.c diff --git a/doc/crypt.tex b/doc/crypt.tex index 4e68dce55..6b2034213 100644 --- a/doc/crypt.tex +++ b/doc/crypt.tex @@ -6094,16 +6094,21 @@ \chapter{Elliptic Curve Cryptography - $Montgomery/Twisted Edwards$} \mysection{Introduction} The library provides functionality for \textit{Curve25519}-based \textit{X25519} Diffie-Hellman shared secrets -and \textit{EdDSA} a.k.a \textit{Ed25519} signature schemes. +and \textit{EdDSA} a.k.a \textit{Ed25519} signature schemes as well as the corresponding +\textit{Curve448}/\textit{ec448}-based \textit{X448} and \textit{Ed448} algorithms. -The implementation is based on the \textit{tweetnacl}\footnote{\url{https://tweetnacl.cr.yp.to/}} reference implementation +The Curve25519 implementation is based on the \textit{tweetnacl}\footnote{\url{https://tweetnacl.cr.yp.to/}} reference implementation as provided by Daniel J. Bernstein et.al. and only slightly modified to better fit in the library. +The Curve448/\textit{ec448} implementation follows the same overall code structure and arithmetic layout pattern. -Both algorithms share the key structure called \textit{curve25519\_key} which is used by all Curve25519 functions. +The Curve25519 family shares the key structure called \textit{curve25519\_key} which is used by all +Curve25519 functions. The Curve448 family uses the \textit{curve448\_key} structure. As Curve25519 and Ed25519 are based on the same elliptic curve, but use different mathematics, the keys are not compatible to each other. +Likewise X448 and Ed448 keys are not compatible to each other. + It is possible to convert a Curve-Key to an Ed-Key and vice-versa, but this is not provided (yet). @@ -6122,7 +6127,7 @@ \subsection{X25519 Key Operations} curve25519_key *key); \end{verbatim} -To generate a fresh X25529 key, one can use \textit{x25519\_make\_key} which will create a private\&public key-pair. +To generate a fresh X25519 key, one can use \textit{x25519\_make\_key} which will create a private\&public key-pair. \index{x25519\_import} \begin{verbatim} int x25519_import(const unsigned char *in, @@ -6217,7 +6222,7 @@ \subsection{EdDSA Key Operations} curve25519_key *key); \end{verbatim} -To generate a fresh Ed25529 key, one can use \textit{ed25519\_make\_key} which will create a private\&public key-pair. +To generate a fresh Ed25519 key, one can use \textit{ed25519\_make\_key} which will create a private\&public key-pair. \index{ed25519\_import} \begin{verbatim} @@ -6333,6 +6338,232 @@ \subsection{EdDSA Cryptographic Operations} pointed to by the array \textit{msg} of length \textit{msglen}. It will store a non--zero value in \textit{stat} if the signature is valid. Note: the function will not return an error if the signature is invalid. It will only return an error if the actual signature payload is an invalid format. +\mysection{Curve448-based Diffie-Hellman Key Exchange - X448} + +The library provides the Diffie-Hellman Key Exchange algorithm \textit{X448} for curve448 as specified in RFC 7748. + +\subsection{X448 Key Operations} + +The \textit{X448} algorithm API provides the following set of functions to create, import and export keys. + +\index{x448\_make\_key} +\begin{verbatim} +int x448_make_key( prng_state *prng, + int wprng, + curve448_key *key); +\end{verbatim} + +To generate a fresh X448 key, one can use \textit{x448\_make\_key} which will create a private\&public key-pair. +\index{x448\_import} +\begin{verbatim} +int x448_import(const unsigned char *in, + unsigned long inlen, + curve448_key *key); +\end{verbatim} + +The \textit{x448\_import} function can be used to import a public key in DER-encoded \textit{SubjectPublicKeyInfo} format. + +\index{x448\_import\_raw} +\begin{verbatim} +int x448_import_raw(const unsigned char *in, + unsigned long inlen, + int which, + curve448_key *key); +\end{verbatim} + +To import a public or private key in raw format, one can use the function \textit{x448\_import\_raw}. + +\index{x448\_import\_x509} +\begin{verbatim} +int x448_import_x509(const unsigned char *in, + unsigned long inlen, + curve448_key *key); +\end{verbatim} + +To import a public key from a DER-encoded \textit{X.509} certificate, one can use the function \textit{x448\_import\_x509}. + +\index{x448\_import\_pkcs8} +\begin{verbatim} +int x448_import_pkcs8(const unsigned char *in, + unsigned long inlen, + const password_ctx *pw_ctx, + curve448_key *key); +\end{verbatim} + +To import a private key in the \textit{OneAsymmetricKey} a.k.a \textit{PKCS \#8} format, either plain or PBES encrypted, +one can use the function \textit{x448\_import\_pkcs8}. + +\index{x448\_export} +\begin{verbatim} +int x448_export( unsigned char *out, + unsigned long *outlen, + int which, + const curve448_key *key); +\end{verbatim} + +To export a key, the function \textit{x448\_export} is provided. + +It has support for the following output formats: + +\begin{figure}[H] +\begin{center} +\begin{tabular}{|c|c|} +\hline \textbf{which} & \textbf{output format} \\ +\hline PK\_PRIVATE & Raw \\ +\hline PK\_PRIVATE \& PK\_STD & PKCS \#8 \\ +\hline PK\_PUBLIC & Raw \\ +\hline PK\_PUBLIC \& PK\_STD & SubjectPublicKeyInfo \\ +\hline +\end{tabular} +\end{center} +\caption{Possible x448\_export() output formats} +\end{figure} + +\subsection{X448 Cryptographic Operations} + +To construct a Diffie-Hellman shared secret with a private and a public X448 key, use the following function: + +\index{x448\_shared\_secret} +\begin{verbatim} +int x448_shared_secret(const curve448_key *private_key, + const curve448_key *public_key, + unsigned char *out, + unsigned long *outlen); +\end{verbatim} + +This will construct the shared secret between the private- and the public-key and store the result in \textit{out} of length \textit{outlen}. + +\mysection{Curve448-based EdDSA Signature Scheme - Ed448} + +The library provides the EdDSA algorithm for the edwards448 curve in the PureEdDSA variant as specified in RFC 8032. + +\subsection{Ed448 Key Operations} + +The \textit{Ed448} algorithm API provides the following set of functions to create, import and export keys. + +\index{ed448\_make\_key} +\begin{verbatim} +int ed448_make_key( prng_state *prng, + int wprng, + curve448_key *key); +\end{verbatim} + +To generate a fresh Ed448 key, one can use \textit{ed448\_make\_key} which will create a private\&public key-pair. + +\index{ed448\_import} +\begin{verbatim} +int ed448_import(const unsigned char *in, + unsigned long inlen, + curve448_key *key); +\end{verbatim} + +The \textit{ed448\_import} function can be used to import a public key in DER-encoded \textit{SubjectPublicKeyInfo} format. + +\index{ed448\_import\_raw} +\begin{verbatim} +int ed448_import_raw(const unsigned char *in, + unsigned long inlen, + int which, + curve448_key *key); +\end{verbatim} + +To import a public or private key in raw format, one can use the function \textit{ed448\_import\_raw}. + +\index{ed448\_import\_x509} +\begin{verbatim} +int ed448_import_x509(const unsigned char *in, + unsigned long inlen, + curve448_key *key); +\end{verbatim} + +To import a public key from a DER-encoded \textit{X.509} certificate, one can use the function \textit{ed448\_import\_x509}. + +\index{ed448\_import\_pkcs8} +\begin{verbatim} +int ed448_import_pkcs8(const unsigned char *in, + unsigned long inlen, + const password_ctx *pw_ctx, + curve448_key *key); +\end{verbatim} + +To import a private key in the \textit{OneAsymmetricKey} a.k.a \textit{PKCS \#8} format, either plain or PBES encrypted, +one can use the function \textit{ed448\_import\_pkcs8}. + +\index{ed448\_export} +\begin{verbatim} +int ed448_export( unsigned char *out, + unsigned long *outlen, + int which, + const curve448_key *key); +\end{verbatim} + +To export a key, the function \textit{ed448\_export} is provided. + +It has support for the following output formats: + +\begin{figure}[H] +\begin{center} +\begin{tabular}{|c|c|} +\hline \textbf{which} & \textbf{output format} \\ +\hline PK\_PRIVATE & Raw \\ +\hline PK\_PRIVATE \& PK\_STD & PKCS \#8 \\ +\hline PK\_PUBLIC & Raw \\ +\hline PK\_PUBLIC \& PK\_STD & SubjectPublicKeyInfo \\ +\hline +\end{tabular} +\end{center} +\caption{Possible ed448\_export() output formats} +\end{figure} + +\subsection{Ed448 Cryptographic Operations} + +To sign and/or verify a message use the following functions: + +\index{ed448\_sign} +\index{ed448ctx\_sign} +\index{ed448ph\_sign} +\begin{verbatim} +int ed448_sign(const unsigned char *msg, unsigned long msglen, + unsigned char *sig, unsigned long *siglen, + const curve448_key *private_key); +int ed448ctx_sign(const unsigned char *msg, unsigned long msglen, + unsigned char *sig, unsigned long *siglen, + const unsigned char *ctx, unsigned long ctxlen, + const curve448_key *private_key); +int ed448ph_sign(const unsigned char *msg, unsigned long msglen, + unsigned char *sig, unsigned long *siglen, + const unsigned char *ctx, unsigned long ctxlen, + const curve448_key *private_key); +\end{verbatim} + +These functions will EdDSA sign the message stored in the array pointed to by \textit{msg} of length \textit{msglen} octets. The signature +will be stored in the array pointed to by \textit{sig} of length \textit{siglen} octets. The \texttt{ctx} and \texttt{ph} variants also +allow passing a context \textit{ctx} of length \textit{ctxlen} octets. This context is allowed to be max. 255 octets long. + +\index{ed448\_verify} +\index{ed448ctx\_verify} +\index{ed448ph\_verify} +\begin{verbatim} +int ed448_verify(const unsigned char *msg, unsigned long msglen, + const unsigned char *sig, unsigned long siglen, + int *stat, + const curve448_key *public_key); +int ed448ctx_verify(const unsigned char *msg, unsigned long msglen, + const unsigned char *sig, unsigned long siglen, + const unsigned char *ctx, unsigned long ctxlen, + int *stat, + const curve448_key *public_key); +int ed448ph_verify(const unsigned char *msg, unsigned long msglen, + const unsigned char *sig, unsigned long siglen, + const unsigned char *ctx, unsigned long ctxlen, + int *stat, + const curve448_key *public_key); +\end{verbatim} + +These functions will verify the EdDSA signature in the array pointed to by \textit{sig} of length \textit{siglen} octets, against the message +pointed to by the array \textit{msg} of length \textit{msglen}. It will store a non--zero value in \textit{stat} if the signature is valid. Note: +the function will not return an error if the signature is invalid. It will only return an error if the actual signature payload is an invalid format. + \chapter{Digital Signature Algorithm} \mysection{Introduction} @@ -6638,6 +6869,8 @@ \chapter{The PKA Union} LTC_PKA_X25519, LTC_PKA_ED25519, LTC_PKA_DH, + LTC_PKA_X448, + LTC_PKA_ED448, }; typedef struct { @@ -6646,6 +6879,10 @@ \chapter{The PKA Union} curve25519_key x25519; curve25519_key ed25519; #endif +#ifdef LTC_CURVE448 + curve448_key x448; + curve448_key ed448; +#endif #ifdef LTC_MDH dh_key dh; #endif @@ -7927,14 +8164,14 @@ \subsection{PKCS PEM files} \begin{table}[H] \begin{minipage}{\textwidth} \begin{small} -\begin{tabular}{|l|l|l|l|l|} +\begin{tabular}{|l|l|l|l|p{4.1cm}|} \hline \textbf{Identifier} & \textbf{Key type} & \textbf{File content} & \textbf{Standard} & \textbf{Algorithm} \\ -\hline \texttt{BEGIN CERTIFICATE} & Public & Plain & \texttt{X.509} & DH, DSA, ECC, Ed25519, RSA, X25519 \\ +\hline \texttt{BEGIN CERTIFICATE} & Public & Plain & \texttt{X.509} & DH, DSA, ECC, Ed25519, Ed448, RSA, X25519, X448 \\ \hline \texttt{BEGIN DSA PRIVATE KEY} & Private & Maybe encrypted & \texttt{OpenSSL\footnote{There are two de-facto standard for DSA private key structures, LibTomCrypt implements OpenSSL's}} & DSA \\ \hline \texttt{BEGIN EC PRIVATE KEY} & Private & Maybe encrypted & \texttt{RFC 5915} & ECC \\ -\hline \texttt{BEGIN ENCRYPTED PRIVATE KEY} & Private & Encrypted & \texttt{PKCS \#8} & DH, DSA, ECC, Ed25519, RSA, X25519 \\ -\hline \texttt{BEGIN PRIVATE KEY} & Private & Plain & \texttt{PKCS \#8} & DH, DSA, ECC, Ed25519, RSA, X25519 \\ -\hline \texttt{BEGIN PUBLIC KEY} & Public & Plain & \texttt{X.509\footnote{Specifically, SubjectPublicKeyInfo}} & DH, DSA, ECC, Ed25519, RSA, X25519 \\ +\hline \texttt{BEGIN ENCRYPTED PRIVATE KEY} & Private & Encrypted & \texttt{PKCS \#8} & DH, DSA, ECC, Ed25519, Ed448, RSA, X25519, X448 \\ +\hline \texttt{BEGIN PRIVATE KEY} & Private & Plain & \texttt{PKCS \#8} & DH, DSA, ECC, Ed25519, Ed448, RSA, X25519, X448 \\ +\hline \texttt{BEGIN PUBLIC KEY} & Public & Plain & \texttt{X.509\footnote{Specifically, SubjectPublicKeyInfo}} & DH, DSA, ECC, Ed25519, Ed448, RSA, X25519, X448 \\ \hline \texttt{BEGIN RSA PRIVATE KEY} & Private & Maybe encrypted & \texttt{PKCS \#1} & RSA \\ \hline \texttt{BEGIN RSA PUBLIC KEY} & Public & Plain & \texttt{PKCS \#1} & RSA \\ \hline @@ -8401,8 +8638,11 @@ \subsection{Depadding} "RSA", "DSA", "ECC", - "Curve25519", + "X25519", + "Ed25519", "DH", + "X448", + "Ed448", }; static int password_get(void **p, unsigned long *l, void *u) diff --git a/src/headers/tomcrypt_custom.h b/src/headers/tomcrypt_custom.h index 970e9c67b..d8af4136a 100644 --- a/src/headers/tomcrypt_custom.h +++ b/src/headers/tomcrypt_custom.h @@ -453,6 +453,9 @@ /* Ed25519 & X25519 */ #define LTC_CURVE25519 +/* Ed448 & X448 */ +#define LTC_CURVE448 + /* ECC */ #define LTC_MECC diff --git a/src/headers/tomcrypt_pk.h b/src/headers/tomcrypt_pk.h index a6b893128..0aa1a12e5 100644 --- a/src/headers/tomcrypt_pk.h +++ b/src/headers/tomcrypt_pk.h @@ -40,6 +40,8 @@ enum ltc_pka_id { LTC_PKA_X25519, LTC_PKA_ED25519, LTC_PKA_DH, + LTC_PKA_X448, + LTC_PKA_ED448, LTC_PKA_NUM }; @@ -533,6 +535,80 @@ int x25519_shared_secret(const curve25519_key *private_key, #endif /* LTC_CURVE25519 */ + +#ifdef LTC_CURVE448 + +typedef struct { + /** Type of key, PK_PRIVATE or PK_PUBLIC */ + enum public_key_type type; + /** Algorithm: LTC_PKA_X448 or LTC_PKA_ED448 */ + enum ltc_pka_id pka; + /** The private key (56 bytes for X448, 57 bytes for Ed448) */ + unsigned char priv[57]; + /** The public key (56 bytes for X448, 57 bytes for Ed448) */ + unsigned char pub[57]; +} curve448_key; + +/** X448 Diffie-Hellman key exchange */ +int x448_make_key(prng_state *prng, int wprng, curve448_key *key); + +int x448_export( unsigned char *out, unsigned long *outlen, + int which, + const curve448_key *key); + +int x448_import(const unsigned char *in, unsigned long inlen, curve448_key *key); +int x448_import_raw(const unsigned char *in, unsigned long inlen, int which, curve448_key *key); +int x448_import_x509(const unsigned char *in, unsigned long inlen, curve448_key *key); +int x448_import_pkcs8(const unsigned char *in, unsigned long inlen, + const password_ctx *pw_ctx, + curve448_key *key); + +int x448_shared_secret(const curve448_key *private_key, + const curve448_key *public_key, + unsigned char *out, unsigned long *outlen); + +/** Ed448 Signature API */ +int ed448_make_key(prng_state *prng, int wprng, curve448_key *key); + +int ed448_export( unsigned char *out, unsigned long *outlen, + int which, + const curve448_key *key); + +int ed448_import(const unsigned char *in, unsigned long inlen, curve448_key *key); +int ed448_import_raw(const unsigned char *in, unsigned long inlen, int which, curve448_key *key); +int ed448_import_x509(const unsigned char *in, unsigned long inlen, curve448_key *key); +int ed448_import_pkcs8(const unsigned char *in, unsigned long inlen, + const password_ctx *pw_ctx, + curve448_key *key); + +int ed448_sign(const unsigned char *msg, unsigned long msglen, + unsigned char *sig, unsigned long *siglen, + const curve448_key *private_key); +int ed448ctx_sign(const unsigned char *msg, unsigned long msglen, + unsigned char *sig, unsigned long *siglen, + const unsigned char *ctx, unsigned long ctxlen, + const curve448_key *private_key); +int ed448ph_sign(const unsigned char *msg, unsigned long msglen, + unsigned char *sig, unsigned long *siglen, + const unsigned char *ctx, unsigned long ctxlen, + const curve448_key *private_key); +int ed448_verify(const unsigned char *msg, unsigned long msglen, + const unsigned char *sig, unsigned long siglen, + int *stat, + const curve448_key *public_key); +int ed448ctx_verify(const unsigned char *msg, unsigned long msglen, + const unsigned char *sig, unsigned long siglen, + const unsigned char *ctx, unsigned long ctxlen, + int *stat, + const curve448_key *public_key); +int ed448ph_verify(const unsigned char *msg, unsigned long msglen, + const unsigned char *sig, unsigned long siglen, + const unsigned char *ctx, unsigned long ctxlen, + int *stat, + const curve448_key *public_key); + +#endif /* LTC_CURVE448 */ + #ifdef LTC_MDSA /* Max diff between group and modulus size in bytes (max case: L=8192bits, N=256bits) */ @@ -628,6 +704,10 @@ typedef struct { curve25519_key x25519; curve25519_key ed25519; #endif +#ifdef LTC_CURVE448 + curve448_key x448; + curve448_key ed448; +#endif #ifdef LTC_MDH dh_key dh; #endif diff --git a/src/headers/tomcrypt_private.h b/src/headers/tomcrypt_private.h index 4de5e5edc..edb83a50e 100644 --- a/src/headers/tomcrypt_private.h +++ b/src/headers/tomcrypt_private.h @@ -59,6 +59,8 @@ enum ltc_oid_id { LTC_OID_EC_PRIMEF, LTC_OID_X25519, LTC_OID_ED25519, + LTC_OID_X448, + LTC_OID_ED448, LTC_OID_DH, LTC_OID_NUM }; @@ -652,6 +654,33 @@ int ec25519_crypto_ctx( unsigned char *out, unsigned long *outlen, const unsigned char *ctx, unsigned long ctxlen); #endif /* LTC_CURVE25519 */ +#ifdef LTC_CURVE448 +int ec448_export(unsigned char *out, unsigned long *outlen, int which, const curve448_key *key); +int ec448_import_pkcs8(const unsigned char *in, unsigned long inlen, + const password_ctx *pw_ctx, enum ltc_oid_id id, curve448_key *key); +int ec448_import_pkcs8_asn1(ltc_asn1_list *alg_id, ltc_asn1_list *priv_key, + enum ltc_oid_id id, curve448_key *key); +int x448_import_pkcs8_asn1(ltc_asn1_list *alg_id, ltc_asn1_list *priv_key, + curve448_key *key); +int ec448_keypair_internal(prng_state *prng, int wprng, unsigned char *pk, unsigned char *sk); +int ec448_sk_to_pk_internal(unsigned char *pk, const unsigned char *sk); +int ec448_sign_internal(unsigned char *sm, unsigned long long *smlen, + const unsigned char *m, unsigned long long mlen, + const unsigned char *sk, const unsigned char *pk, + const unsigned char *ctx, unsigned long long cs); +int ec448_verify_internal(int *stat, unsigned char *m, unsigned long long *mlen, + const unsigned char *sm, unsigned long long smlen, + const unsigned char *ctx, unsigned long long cs, + const unsigned char *pk); +int ec448_prehash_internal(unsigned char *out, const unsigned char *msg, unsigned long long msglen); +int ec448_crypto_ctx(unsigned char *out, unsigned long *outlen, unsigned char flag, + const unsigned char *ctx, unsigned long ctxlen); +int ec448_scalarmult_internal(unsigned char *out, const unsigned char *scalar, const unsigned char *point); +int ec448_scalarmult_base_internal(unsigned char *out, const unsigned char *scalar); +int ed448_import_pkcs8_asn1(ltc_asn1_list *alg_id, ltc_asn1_list *priv_key, + curve448_key *key); +#endif /* LTC_CURVE448 */ + #ifdef LTC_DER #define LTC_ASN1_IS_TYPE(e, t) (((e) != NULL) && ((e)->type == (t))) diff --git a/src/misc/crypt/crypt.c b/src/misc/crypt/crypt.c index a07a191ba..3773f71fc 100644 --- a/src/misc/crypt/crypt.c +++ b/src/misc/crypt/crypt.c @@ -378,6 +378,10 @@ const char *crypt_build_settings = " X25519\n" #endif #endif +#if defined(LTC_CURVE448) + " Ed448\n" + " X448\n" +#endif #if defined(LTC_PK_MAX_RETRIES) " "NAME_VALUE(LTC_PK_MAX_RETRIES)"\n" #endif diff --git a/src/misc/pem/pem_pkcs.c b/src/misc/pem/pem_pkcs.c index 17aa47705..5abf243f4 100644 --- a/src/misc/pem/pem_pkcs.c +++ b/src/misc/pem/pem_pkcs.c @@ -63,6 +63,10 @@ static const struct { [LTC_OID_X25519] = { LTC_PKA_X25519, (pkcs8_import_fn)x25519_import_pkcs8_asn1 }, [LTC_OID_ED25519] = { LTC_PKA_ED25519, (pkcs8_import_fn)ed25519_import_pkcs8_asn1 }, #endif +#ifdef LTC_CURVE448 + [LTC_OID_X448] = { LTC_PKA_X448, (pkcs8_import_fn)x448_import_pkcs8_asn1 }, + [LTC_OID_ED448] = { LTC_PKA_ED448, (pkcs8_import_fn)ed448_import_pkcs8_asn1 }, +#endif }; static int s_import_pkcs8(unsigned char *asn1_cert, unsigned long asn1_len, ltc_pka_key *k, const password_ctx *pw_ctx) @@ -122,6 +126,10 @@ static const import_fn s_import_openssl_fns[LTC_PKA_NUM] = { [LTC_PKA_X25519] = (import_fn)x25519_import, [LTC_PKA_ED25519] = (import_fn)ed25519_import, #endif +#ifdef LTC_CURVE448 + [LTC_PKA_X448] = (import_fn)x448_import, + [LTC_PKA_ED448] = (import_fn)ed448_import, +#endif }; static int s_decode(struct get_char *g, ltc_pka_key *k, const password_ctx *pw_ctx) diff --git a/src/pk/asn1/oid/pk_get.c b/src/pk/asn1/oid/pk_get.c index 1fd5872e2..8b3d3505b 100644 --- a/src/pk/asn1/oid/pk_get.c +++ b/src/pk/asn1/oid/pk_get.c @@ -18,6 +18,8 @@ static const oid_table_entry pka_oids[] = { { LTC_OID_EC_PRIMEF, LTC_PKA_EC, "1.2.840.10045.1.1" }, { LTC_OID_X25519, LTC_PKA_X25519, "1.3.101.110" }, { LTC_OID_ED25519, LTC_PKA_ED25519, "1.3.101.112" }, + { LTC_OID_X448, LTC_PKA_X448, "1.3.101.111" }, + { LTC_OID_ED448, LTC_PKA_ED448, "1.3.101.113" }, { LTC_OID_DH, LTC_PKA_DH, "1.2.840.113549.1.3.1" }, }; diff --git a/src/pk/asn1/x509/x509_import_spki.c b/src/pk/asn1/x509/x509_import_spki.c index 8b360852e..16bb6948a 100644 --- a/src/pk/asn1/x509/x509_import_spki.c +++ b/src/pk/asn1/x509/x509_import_spki.c @@ -25,6 +25,10 @@ static const import_fn s_import_x509_fns[LTC_PKA_NUM] = { [LTC_PKA_X25519] = (import_fn)x25519_import_x509, [LTC_PKA_ED25519] = (import_fn)ed25519_import_x509, #endif +#ifdef LTC_CURVE448 + [LTC_PKA_X448] = (import_fn)x448_import_x509, + [LTC_PKA_ED448] = (import_fn)ed448_import_x509, +#endif }; int x509_import_spki(const unsigned char *asn1_cert, unsigned long asn1_len, ltc_pka_key *k, ltc_asn1_list **root) diff --git a/src/pk/ec448/ec448_common.c b/src/pk/ec448/ec448_common.c new file mode 100644 index 000000000..7ea9ad8f0 --- /dev/null +++ b/src/pk/ec448/ec448_common.c @@ -0,0 +1,937 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ +#include "tomcrypt_private.h" + +/** + @file ec448_common.c + Shared field arithmetic and primitives for Ed448 and X448 + Field arithmetic over p = 2^448 - 2^224 - 1 (Goldilocks prime) + Edwards curve: x^2 + y^2 = 1 + d*x^2*y^2, d = -39081 + RFC 8032 Section 5.2 + + The field representation (16 limbs of 28 bits in long64) and the overall + code structure follow the TweetNaCl pattern used for Curve25519 in + ec25519/tweetnacl.c. TweetNaCl uses 16 limbs of 16 bits for GF(2^255-19); + here the limb width is scaled to 28 bits so that 16 * 28 = 448. This keeps + the same loop bounds, carry propagation shape and multiplication layout, + trading performance for code simplicity. +*/ + +#ifdef LTC_CURVE448 + +/* Field element: 16 limbs of 28 bits each (16 * 28 = 448) + Each limb nominally fits in 28 bits but may temporarily exceed that + during computation; we use long64 for headroom +*/ +typedef long64 gf448[16]; + +/* field constants */ +static const gf448 gf448_0 = {0}; +static const gf448 gf448_1 = {1}; + +/* d = -39081 mod p = p - 39081 + p in 28-bit limbs: all 0x0FFFFFFF except limb 8 = 0x0FFFFFFE + d: limb 0 = 0x0FFFFFFF - 39081 = 0x0FFF6756, limb 8 = 0x0FFFFFFE, rest 0x0FFFFFFF +*/ +static const gf448 ed448_d = { + 0x0FFF6756, 0x0FFFFFFF, 0x0FFFFFFF, 0x0FFFFFFF, + 0x0FFFFFFF, 0x0FFFFFFF, 0x0FFFFFFF, 0x0FFFFFFF, + 0x0FFFFFFE, 0x0FFFFFFF, 0x0FFFFFFF, 0x0FFFFFFF, + 0x0FFFFFFF, 0x0FFFFFFF, 0x0FFFFFFF, 0x0FFFFFFF +}; + +/* Ed448 base point in compressed bytes (57 bytes, little-endian y, sign bit in top of last byte) */ +static const unsigned char ed448_base_point[57] = { + 0x14, 0xfa, 0x30, 0xf2, 0x5b, 0x79, 0x08, 0x98, + 0xad, 0xc8, 0xd7, 0x4e, 0x2c, 0x13, 0xbd, 0xfd, + 0xc4, 0x39, 0x7c, 0xe6, 0x1c, 0xff, 0xd3, 0x3a, + 0xd7, 0xc2, 0xa0, 0x05, 0x1e, 0x9c, 0x78, 0x87, + 0x40, 0x98, 0xa3, 0x6c, 0x73, 0x73, 0xea, 0x4b, + 0x62, 0xc7, 0xc9, 0x56, 0x37, 0x20, 0x76, 0x88, + 0x24, 0xbc, 0xb6, 0x6e, 0x71, 0x46, 0x3f, 0x69, + 0x00 +}; + +/* Group order L (little-endian, 57 bytes) */ +static const unsigned char ed448_order[57] = { + 0xf3, 0x44, 0x58, 0xab, 0x92, 0xc2, 0x78, 0x23, + 0x55, 0x8f, 0xc5, 0x8d, 0x72, 0xc2, 0x6c, 0x21, + 0x90, 0x36, 0xd6, 0xae, 0x49, 0xdb, 0x4e, 0xc4, + 0xe9, 0x23, 0xca, 0x7c, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, + 0x00 +}; + +/* Group order L as ulong64 array for modular reduction (byte-wise) */ +static const ulong64 L448[57] = { + 0xf3, 0x44, 0x58, 0xab, 0x92, 0xc2, 0x78, 0x23, + 0x55, 0x8f, 0xc5, 0x8d, 0x72, 0xc2, 0x6c, 0x21, + 0x90, 0x36, 0xd6, 0xae, 0x49, 0xdb, 0x4e, 0xc4, + 0xe9, 0x23, 0xca, 0x7c, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, + 0x00 +}; + +/* Field arithmetic for GF(2^448 - 2^224 - 1) - 16 limbs, 28 bits each */ + +static LTC_INLINE void s_gf448_copy(gf448 r, const gf448 a) +{ + int i; + for (i = 0; i < 16; ++i) r[i] = a[i]; +} + +/* Carry propagation + Overflow from limb[15] wraps: 2^448 == 2^224 + 1 (mod p) + So excess from limb 15 adds to limb 0 and limb 8 +*/ +static void s_gf448_carry(gf448 o) +{ + int i; + long64 c; + /* propagate carries from limb 0 to 14 */ + for (i = 0; i < 15; ++i) { + c = o[i] >> 28; + o[i+1] += c; + o[i] -= c << 28; + } + /* limb 15 overflow: 2^(28*16) = 2^448 == 2^224 + 1 */ + c = o[15] >> 28; + o[0] += c; /* + c * 1 */ + o[8] += c; /* + c * 2^224 */ + o[15] -= c << 28; + /* one more pass to settle the extra from limb 0 and 8 */ + for (i = 0; i < 15; ++i) { + c = o[i] >> 28; + o[i+1] += c; + o[i] -= c << 28; + } + c = o[15] >> 28; + o[0] += c; + o[8] += c; + o[15] -= c << 28; +} + +/* Conditional swap: if b==1, swap p and q; if b==0, no-op. Constant-time */ +static void s_gf448_cswap(gf448 p, gf448 q, int b) +{ + long64 t, i, mask = ~((long64)b - 1); + for (i = 0; i < 16; ++i) { + t = mask & (p[i] ^ q[i]); + p[i] ^= t; + q[i] ^= t; + } +} + +/* Pack a field element into 56 bytes (little-endian); fully reduces mod p first */ +static void s_gf448_encode(unsigned char *o, const gf448 n) +{ + int i, j; + long64 b; + gf448 m, t; + for (i = 0; i < 16; ++i) t[i] = n[i]; + + /* carry 3 times to ensure all limbs in [0, 2^28) */ + s_gf448_carry(t); + s_gf448_carry(t); + s_gf448_carry(t); + + /* Subtract p and check borrow p: limbs 0..7 = 0x0FFFFFFF, limb 8 = 0x0FFFFFFE, limbs 9..15 = 0x0FFFFFFF */ + for (j = 0; j < 2; ++j) { + m[0] = t[0] - (long64)0x0FFFFFFF; + for (i = 1; i < 16; i++) { + long64 sub; + if (i == 8) sub = (long64)0x0FFFFFFE; + else sub = (long64)0x0FFFFFFF; + m[i] = t[i] - sub - ((m[i-1] >> 28) & 1); + m[i-1] &= 0x0FFFFFFF; + } + b = (m[15] >> 28) & 1; + m[15] &= 0x0FFFFFFF; + s_gf448_cswap(t, m, 1 - (int)b); + } + + /* Serialize: 16 limbs * 28 bits = 448 bits = 56 bytes + each limb contributes 28 bits = 3.5 bytes + Limb i holds bits [28*i .. 28*i+27] + Byte j holds bits [8*j .. 8*j+7] + */ + XMEMSET(o, 0, 56); + for (i = 0; i < 16; i++) { + /* limb i holds bits starting at bit_offset = 28*i */ + unsigned long bit_off = (unsigned long)i * 28; + unsigned long byte_off = bit_off / 8; + unsigned long bit_shift = bit_off % 8; + /* We need to spread 28 bits starting at byte_off, bit_shift */ + ulong64 val = (ulong64)(t[i] & 0x0FFFFFFF); + val <<= bit_shift; + for (j = 0; j < 5 && byte_off + (unsigned long)j < 56; j++) { + o[byte_off + j] |= (unsigned char)(val & 0xFF); + val >>= 8; + } + } +} + +/* Unpack 56 bytes (little-endian) into a field element */ +static void s_gf448_decode(gf448 o, const unsigned char *n) +{ + int i; + XMEMSET(o, 0, sizeof(gf448)); + for (i = 0; i < 16; i++) { + unsigned long bit_off = (unsigned long)i * 28; + unsigned long byte_off = bit_off / 8; + unsigned long bit_shift = bit_off % 8; + ulong64 val = 0; + int j; + for (j = 0; j < 5 && byte_off + (unsigned long)j < 56; j++) { + val |= ((ulong64)n[byte_off + j]) << (8 * (unsigned long)j); + } + val >>= bit_shift; + o[i] = (long64)(val & 0x0FFFFFFF); + } +} + +/* constant-time equality check */ +static int s_gf448_neq(const gf448 a, const gf448 b) +{ + unsigned char c[56], d[56]; + int i; + ulong32 diff = 0; + s_gf448_encode(c, a); + s_gf448_encode(d, b); + for (i = 0; i < 56; ++i) diff |= (ulong32)(c[i] ^ d[i]); + return (1 & ((diff - 1) >> 8)) - 1; +} + +/* return parity (low bit) of field element */ +static unsigned char s_gf448_parity(const gf448 a) +{ + unsigned char d[56]; + s_gf448_encode(d, a); + return d[0] & 1; +} + +/* Addition */ +static LTC_INLINE void s_gf448_add(gf448 o, const gf448 a, const gf448 b) +{ + int i; + for (i = 0; i < 16; ++i) o[i] = a[i] + b[i]; +} + +/* Subtraction */ +static LTC_INLINE void s_gf448_sub(gf448 o, const gf448 a, const gf448 b) +{ + int i; + for (i = 0; i < 16; ++i) o[i] = a[i] - b[i]; +} + +/* Multiplication: o = a * b mod p + Schoolbook 16x16 -> 31 limbs, then reduce using Goldilocks + 2^448 == 2^224 + 1, so for product limbs t[16..30]: + t[i] += t[i+16] (the "1" part) + t[i+8] += t[i+16] (the "2^224" part, since limb 8 = 224 bits) + Then carry +*/ +static void s_gf448_mul(gf448 o, const gf448 a, const gf448 b) +{ + long64 i, j; + long64 t[31]; + for (i = 0; i < 31; ++i) t[i] = 0; + for (i = 0; i < 16; ++i) + for (j = 0; j < 16; ++j) + t[i + j] += a[i] * b[j]; + + /* reduce t[16..30] */ + for (i = 14; i >= 0; --i) { + t[i] += t[i + 16]; + t[i + 8] += t[i + 16]; + t[i + 16] = 0; + } + /* Now t[0..15] holds the result, but t[8..15] may have gotten extra from + t[16..23] additions. t[15] overflow wraps via Goldilocks + */ + for (i = 0; i < 16; ++i) o[i] = t[i]; + s_gf448_carry(o); + s_gf448_carry(o); +} + +/* Squaring */ +static LTC_INLINE void s_gf448_sqr(gf448 o, const gf448 a) +{ + s_gf448_mul(o, a, a); +} + +/* Inversion: o = a^(p-2) mod p + p-2 = 2^448 - 2^224 - 3 + + p-2 in binary (bit 447 to bit 0): + bits 447..225: all 1 (223 ones) + bit 224: 0 + bits 223..2: all 1 (222 ones) + bit 1: 0 + bit 0: 1 + + Square-and-multiply from MSB: start with a^1, then for each bit: + square, then multiply by a if bit is 1 +*/ +static void s_gf448_inv(gf448 o, const gf448 inp) +{ + gf448 t; + int i; + + s_gf448_copy(t, inp); + + /* Process from bit 446 down (bit 447 is the leading 1, we start with a^1) */ + /* bits 446..225: all 1 (222 bits) */ + for (i = 0; i < 222; i++) { + s_gf448_sqr(t, t); + s_gf448_mul(t, t, inp); + } + + /* bit 224: 0 -> just square */ + s_gf448_sqr(t, t); + + /* bits 223..2: all 1 (222 bits) */ + for (i = 0; i < 222; i++) { + s_gf448_sqr(t, t); + s_gf448_mul(t, t, inp); + } + + /* bit 1: 0 -> just square */ + s_gf448_sqr(t, t); + + /* bit 0: 1 -> square and multiply */ + s_gf448_sqr(t, t); + s_gf448_mul(t, t, inp); + + s_gf448_copy(o, t); +} + +/* Square root: o = a^((p+1)/4) mod p + Since p == 3 (mod 4), this gives the square root of a when it exists + (p+1)/4 = 2^446 - 2^222 = 2^222 * (2^224 - 1) + In binary: 224 ones (bits 445..222) followed by 222 zeros (bits 221..0) +*/ +static void s_gf448_sqrt(gf448 o, const gf448 inp) +{ + gf448 t; + int i; + + s_gf448_copy(t, inp); + + /* bit 445 is the MSB; we start with a^1. bits 444..222: all 1 (223 bits) */ + for (i = 0; i < 223; i++) { + s_gf448_sqr(t, t); + s_gf448_mul(t, t, inp); + } + + /* bits 221..0: all 0 (222 bits) -> just square 222 times */ + for (i = 0; i < 222; i++) { + s_gf448_sqr(t, t); + } + + s_gf448_copy(o, t); +} + + +/* Edwards curve point operations + Curve: x^2 + y^2 = 1 + d*x^2*y^2 (a = 1) + Extended coordinates: (X : Y : Z : T) where x = X/Z, y = Y/Z, T = X*Y/Z +*/ + +/* Unified point addition (works for doubling too) + Extended coordinates on x^2 + y^2 = 1 + d*x^2*y^2 (a=1): + A = X1*X2, B = Y1*Y2, C = d*T1*T2, D = Z1*Z2 + E = (X1+Y1)(X2+Y2) - A - B + F = D - C, G = D + C, H = B - A + X3 = E*F, Y3 = G*H, T3 = E*H, Z3 = F*G +*/ +static void s_ed448_point_add(gf448 p[4], gf448 q[4]) +{ + gf448 a, b, c, d, e, f, g, h, t; + + s_gf448_mul(a, p[0], q[0]); /* A = X1*X2 */ + s_gf448_mul(b, p[1], q[1]); /* B = Y1*Y2 */ + s_gf448_mul(c, p[3], q[3]); + s_gf448_mul(c, c, ed448_d); /* C = d*T1*T2 */ + s_gf448_mul(d, p[2], q[2]); /* D = Z1*Z2 */ + + s_gf448_add(t, p[0], p[1]); /* X1+Y1 */ + s_gf448_add(e, q[0], q[1]); /* X2+Y2 */ + s_gf448_mul(e, t, e); /* (X1+Y1)(X2+Y2) */ + s_gf448_sub(e, e, a); /* - A */ + s_gf448_sub(e, e, b); /* - B -> E */ + + s_gf448_sub(f, d, c); /* F = D - C */ + s_gf448_add(g, d, c); /* G = D + C */ + s_gf448_sub(h, b, a); /* H = B - A (since a=1) */ + + s_gf448_mul(p[0], e, f); /* X3 = E*F */ + s_gf448_mul(p[1], g, h); /* Y3 = G*H */ + s_gf448_mul(p[2], f, g); /* Z3 = F*G */ + s_gf448_mul(p[3], e, h); /* T3 = E*H */ +} + +/* Conditional swap of two points (constant time) */ +static void s_ed448_point_cswap(gf448 p[4], gf448 q[4], unsigned char b) +{ + int i; + for (i = 0; i < 4; ++i) s_gf448_cswap(p[i], q[i], b); +} + +/* Ed448 encoding: 57 bytes = 456 bits + Bytes 0..55 contain the 448-bit little-endian y-coordinate. + Bits 0..6 of byte 56 are zero for canonical encodings. + Bit 7 of byte 56 stores the parity/sign bit of x. +*/ +static void s_ed448_point_encode(unsigned char *r, gf448 p[4]) +{ + gf448 tx, ty, zi; + s_gf448_inv(zi, p[2]); + s_gf448_mul(tx, p[0], zi); + s_gf448_mul(ty, p[1], zi); + s_gf448_encode(r, ty); + /* bit 455 = bit 7 of byte 56 = sign of x */ + r[56] = 0; + r[56] |= s_gf448_parity(tx) << 7; +} + +/* Scalar multiplication: p = [s] * q; Constant-time double-and-add with conditional swap */ +static void s_ed448_scalarmult(gf448 p[4], gf448 q[4], const unsigned char *s) +{ + int i; + /* p = identity = (0, 1, 1, 0) */ + s_gf448_copy(p[0], gf448_0); + s_gf448_copy(p[1], gf448_1); + s_gf448_copy(p[2], gf448_1); + s_gf448_copy(p[3], gf448_0); + + /* Ed448 scalars are 456 bits (57 bytes), but the group order is ~446 bits + Clamped scalar has at most 448 bits (a[56]=0, a[55]|=0x80 sets bit 447) + We scan all 456 bits to be safe + */ + for (i = 455; i >= 0; --i) { + unsigned char b = (s[i / 8] >> (i & 7)) & 1; + s_ed448_point_cswap(p, q, b); + s_ed448_point_add(q, p); + s_ed448_point_add(p, p); + s_ed448_point_cswap(p, q, b); + } +} + +/* Scalar base multiplication: p = [s] * B */ +static void s_ed448_scalarmult_base(gf448 p[4], const unsigned char *s) +{ + gf448 q[4]; + + /* decode base point: unpack y from first 56 bytes */ + s_gf448_decode(q[1], ed448_base_point); + + /* set Z = 1 */ + s_gf448_copy(q[2], gf448_1); + + /* recover x from y: + x^2 + y^2 = 1 + d*x^2*y^2 => x^2 = (1 - y^2) / (1 - d*y^2) */ + { + gf448 y2, num, den, den_inv, x2, x_cand; + s_gf448_sqr(y2, q[1]); /* y^2 */ + s_gf448_sub(num, gf448_1, y2); /* 1 - y^2 */ + s_gf448_mul(den, y2, ed448_d); /* d * y^2 */ + s_gf448_sub(den, gf448_1, den); /* 1 - d*y^2 */ + s_gf448_inv(den_inv, den); + s_gf448_mul(x2, num, den_inv); /* x^2 */ + + /* x = x2^((p+1)/4) -- works since p == 3 mod 4 */ + s_gf448_sqrt(x_cand, x2); + + /* Check sign bit. The base point encoding has sign bit = 0 */ + if (s_gf448_parity(x_cand) != ((ed448_base_point[56] >> 7) & 1)) { + /* negate x */ + s_gf448_sub(x_cand, gf448_0, x_cand); + } + s_gf448_copy(q[0], x_cand); + } + + /* T = X * Y */ + s_gf448_mul(q[3], q[0], q[1]); + + s_ed448_scalarmult(p, q, s); +} + +/* Decode a point from 57-byte encoding; returns 0 on success, -1 on failure */ +static int s_ed448_point_decode(gf448 r[4], const unsigned char p[57]) +{ + gf448 y2, num, den, den_inv, x2, x_cand, chk; + unsigned char sign_bit; + + /* y from the first 56 bytes (448 bits) */ + s_gf448_decode(r[1], p); + /* bit 7 of byte 56 is the x sign bit */ + sign_bit = (p[56] >> 7) & 1; + + s_gf448_copy(r[2], gf448_1); + + /* x^2 = (1 - y^2) / (1 - d*y^2) */ + s_gf448_sqr(y2, r[1]); + s_gf448_sub(num, gf448_1, y2); /* 1 - y^2 */ + s_gf448_mul(den, y2, ed448_d); /* d * y^2 */ + s_gf448_sub(den, gf448_1, den); /* 1 - d*y^2 */ + s_gf448_inv(den_inv, den); + s_gf448_mul(x2, num, den_inv); + + /* Compute square root candidate */ + s_gf448_sqrt(x_cand, x2); + + /* Verify: x_cand^2 == x2 ? */ + s_gf448_sqr(chk, x_cand); + if (s_gf448_neq(chk, x2)) { + /* Not a valid point */ + return -1; + } + + /* Adjust sign */ + if (s_gf448_parity(x_cand) != sign_bit) { + s_gf448_sub(x_cand, gf448_0, x_cand); + } + + s_gf448_copy(r[0], x_cand); + s_gf448_mul(r[3], r[0], r[1]); /* T = X * Y */ + + return 0; +} + +/* Scalar arithmetic modulo L (group order) */ + +/* Reduce x[0..113] modulo L, store 57-byte result in r + + Uses the relation 2^448 == 4*c (mod L), where c = 2^446 - L is a 28-byte number + Byte position i (for i >= 56) represents 2^(8*i) = 2^(8*(i-56)) * 2^448 + == 2^(8*(i-56)) * 4*c (mod L) + So x[i] for i >= 56 folds into positions (i-56)..(i-56+27) as 4*x[i]*c[j] +*/ +static void s_sc448_reduce(unsigned char *r, long64 x[114]) +{ + long64 carry, i, j; + int round; + + /* c = 2^446 - L (28 bytes, little-endian) */ + static const long64 C448[28] = { + 0x0D, 0xBB, 0xA7, 0x54, 0x6D, 0x3D, 0x87, 0xDC, + 0xAA, 0x70, 0x3A, 0x72, 0x8D, 0x3D, 0x93, 0xDE, + 0x6F, 0xC9, 0x29, 0x51, 0xB6, 0x24, 0xB1, 0x3B, + 0x16, 0xDC, 0x35, 0x83 + }; + + /* Perform up to 4 rounds of folding to guarantee full reduction */ + for (round = 0; round < 4; round++) { + /* Fold bytes 56..113 into lower positions */ + for (i = 113; i >= 56; --i) { + if (x[i] == 0) continue; + for (j = 0; j < 28; j++) { + x[i - 56 + j] += 4 * x[i] * C448[j]; + } + x[i] = 0; + } + + /* Carry normalize from 0 to 113 */ + carry = 0; + for (i = 0; i < 114; i++) { + x[i] += carry; + carry = x[i] >> 8; + x[i] &= 255; + } + /* If no overflow into high bytes, we can stop early */ + if (carry == 0) { + int done = 1; + for (i = 56; i < 114; i++) { + if (x[i] != 0) { done = 0; break; } + } + if (done) break; + } + } + + /* Now x[0..55] holds the value, x[56..113] should be zero + Up to 4 conditional subtractions of L ensure x < L */ + for (round = 0; round < 4; round++) { + long64 t[57], borrow; + borrow = 0; + for (i = 0; i < 57; i++) { + t[i] = x[i] - (long64)L448[i] - borrow; + borrow = (t[i] >> 63) & 1; + t[i] &= 255; + } + /* If borrow=0 => x >= L, replace x with t. If borrow=1 => x < L, keep x */ + if (borrow == 0) { + for (i = 0; i < 57; i++) x[i] = t[i]; + } + } + + for (i = 0; i < 57; i++) { + r[i] = (unsigned char)(x[i] & 255); + } +} + +static void s_sc448_reduce_buf(unsigned char *r) +{ + long64 x[114]; + int i; + for (i = 0; i < 114; ++i) x[i] = (ulong64)r[i]; + for (i = 0; i < 114; ++i) r[i] = 0; + s_sc448_reduce(r, x); +} + +/* Check if scalar s < L (group order); returns 1 if s < L, 0 otherwise */ +static int s_sc448_lt_order(const unsigned char *s) +{ + int i; + /* s and ed448_order are both 57 bytes LE */ + for (i = 56; i >= 0; i--) { + if (s[i] < ed448_order[i]) return 1; + if (s[i] > ed448_order[i]) return 0; + } + return 0; /* equal means not strictly less */ +} + +/* Ed448 internal API functions */ + +/** + Derive public key from secret key + RFC 8032 Section 5.2.5: + 1. SHAKE256(sk, 57) -> 114 bytes + 2. Clamp first 57 bytes: a[0] &= 0xFC, a[55] |= 0x80, a[56] = 0 + 3. pk = [a]B +*/ +int ec448_sk_to_pk_internal(unsigned char *pk, const unsigned char *sk) +{ + unsigned char az[114]; + gf448 p[4]; + int err; + + { unsigned long azlen = 114; if ((err = sha3_shake_memory(256, sk, 57, az, &azlen)) != CRYPT_OK) return err; } + + /* clamp */ + az[0] &= 0xFC; + az[55] |= 0x80; + az[56] = 0; + + s_ed448_scalarmult_base(p, az); + s_ed448_point_encode(pk, p); + + zeromem(az, sizeof(az)); + return CRYPT_OK; +} + +/** + Generate Ed448 keypair +*/ +int ec448_keypair_internal(prng_state *prng, int wprng, unsigned char *pk, unsigned char *sk) +{ + int err; + + if ((err = prng_is_valid(wprng)) != CRYPT_OK) return err; + + if (prng_descriptor[wprng].read(sk, 57, prng) != 57) { + return CRYPT_ERROR_READPRNG; + } + + if ((err = ec448_sk_to_pk_internal(pk, sk)) != CRYPT_OK) { + return err; + } + + return CRYPT_OK; +} + +/** + Ed448 sign + + RFC 8032 Section 5.2.6: + 1. SHAKE256(sk, 57) -> (a, prefix) where a is clamped first 57 bytes + 2. r = SHAKE256(DOM4(0,ctx) || prefix || msg) mod L + 3. R = [r]B encoded + 4. h = SHAKE256(DOM4(0,ctx) || R || pk || msg) mod L + 5. S = (r + h*a) mod L + 6. sig = R || S (114 bytes) + + The sm buffer receives R || S || msg (smlen = mlen + 114) + ctx/cs: DOM4 context; for plain Ed448, ctx=NULL, cs=0 but DOM4 is still prepended +*/ +int ec448_sign_internal(unsigned char *sm, unsigned long long *smlen, + const unsigned char *m, unsigned long long mlen, + const unsigned char *sk, const unsigned char *pk, + const unsigned char *ctx, unsigned long long cs) +{ + unsigned char az[114], nonce[114], hram[114]; + long64 x[114]; + long64 i, j; + gf448 p[4]; + int err; + + /* ctx/cs is a pre-built DOM4 prefix that gets prepended to all hashes + The wrapper functions build it: + ed448_sign -> DOM4(0, "") + ed448ctx_sign -> DOM4(0, ctx) + ed448ph_sign -> DOM4(1, ctx) + */ + + /* Hash secret key */ + { unsigned long azlen = 114; if ((err = sha3_shake_memory(256, sk, 57, az, &azlen)) != CRYPT_OK) return err; } + + /* Clamp scalar a */ + az[0] &= 0xFC; + az[55] |= 0x80; + az[56] = 0; + + /* Compute nonce r = SHAKE256(ctx || prefix || msg) mod L */ + /* ctx already contains DOM4 prefix from the wrapper */ + *smlen = mlen + 114; + + if (ctx != NULL && cs > 0) { + hash_state md; + if ((err = sha3_shake_init(&md, 256)) != CRYPT_OK) goto cleanup; + if ((err = sha3_shake_process(&md, ctx, (unsigned long)cs)) != CRYPT_OK) goto cleanup; + if ((err = sha3_shake_process(&md, az + 57, 57)) != CRYPT_OK) goto cleanup; + if ((err = sha3_shake_process(&md, m, (unsigned long)mlen)) != CRYPT_OK) goto cleanup; + if ((err = sha3_shake_done(&md, nonce, 114)) != CRYPT_OK) goto cleanup; + } + else { + hash_state md; + if ((err = sha3_shake_init(&md, 256)) != CRYPT_OK) goto cleanup; + if ((err = sha3_shake_process(&md, az + 57, 57)) != CRYPT_OK) goto cleanup; + if ((err = sha3_shake_process(&md, m, (unsigned long)mlen)) != CRYPT_OK) goto cleanup; + if ((err = sha3_shake_done(&md, nonce, 114)) != CRYPT_OK) goto cleanup; + } + + s_sc448_reduce_buf(nonce); + + /* R = [r]B */ + s_ed448_scalarmult_base(p, nonce); + s_ed448_point_encode(sm, p); /* sm[0..56] = R */ + + /* Compute h = SHAKE256(ctx || R || pk || msg) mod L */ + { + hash_state md; + if ((err = sha3_shake_init(&md, 256)) != CRYPT_OK) goto cleanup; + if (ctx != NULL && cs > 0) { + if ((err = sha3_shake_process(&md, ctx, (unsigned long)cs)) != CRYPT_OK) goto cleanup; + } + if ((err = sha3_shake_process(&md, sm, 57)) != CRYPT_OK) goto cleanup; /* R */ + if ((err = sha3_shake_process(&md, pk, 57)) != CRYPT_OK) goto cleanup; /* pk */ + if ((err = sha3_shake_process(&md, m, (unsigned long)mlen)) != CRYPT_OK) goto cleanup; /* msg */ + if ((err = sha3_shake_done(&md, hram, 114)) != CRYPT_OK) goto cleanup; + } + s_sc448_reduce_buf(hram); + + /* S = (r + h * a) mod L */ + for (i = 0; i < 114; ++i) x[i] = 0; + for (i = 0; i < 57; ++i) x[i] = (ulong64)nonce[i]; + for (i = 0; i < 57; ++i) + for (j = 0; j < 57; ++j) + x[i + j] += (long64)hram[i] * (long64)(ulong64)az[j]; + s_sc448_reduce(sm + 57, x); /* sm[57..113] = S */ + + err = CRYPT_OK; + +cleanup: + zeromem(az, sizeof(az)); + zeromem(nonce, sizeof(nonce)); + zeromem(hram, sizeof(hram)); + zeromem(x, sizeof(x)); + return err; +} + +/** + Ed448 verify + + RFC 8032 Section 5.2.7: + 1. Parse sig as R (57 bytes) || S (57 bytes) + 2. Decode R, pk as points + 3. Check S < L + 4. h = SHAKE256(DOM4(0,ctx) || R || pk || msg) mod L + 5. Check [S]B == R + [h]A + + sm = sig || msg (smlen = siglen + msglen, siglen = 114) + Returns stat=1 if valid, stat=0 if invalid +*/ +int ec448_verify_internal(int *stat, unsigned char *m, unsigned long long *mlen, + const unsigned char *sm, unsigned long long smlen, + const unsigned char *ctx, unsigned long long cs, + const unsigned char *pk) +{ + unsigned char hram[114]; + unsigned char t[57]; + gf448 p[4], q[4], r_point[4]; + unsigned long long i; + int err; + unsigned char s_bytes[57]; + + *stat = 0; + + if (*mlen < smlen) return CRYPT_BUFFER_OVERFLOW; + *mlen = (unsigned long long)-1; + if (smlen < 114) return CRYPT_INVALID_ARG; + + /* Decode public key A */ + if (s_ed448_point_decode(q, pk) != 0) return CRYPT_ERROR; + + /* Copy sm to m for manipulation */ + XMEMMOVE(m, sm, (unsigned long)smlen); + + /* Extract S (bytes 57..113 of signature) */ + XMEMCPY(s_bytes, m + 57, 57); + + /* Check S < L */ + if (!s_sc448_lt_order(s_bytes)) { + for (i = 0; i < smlen - 114; ++i) m[i] = 0; + return CRYPT_OK; + } + + /* Decode R (bytes 0..56 of signature) */ + if (s_ed448_point_decode(r_point, m) != 0) { + for (i = 0; i < smlen - 114; ++i) m[i] = 0; + return CRYPT_OK; + } + + /* Replace S in m with pk for hashing: m = R || pk || msg */ + XMEMCPY(m + 57, pk, 57); + + /* h = SHAKE256(ctx || R || pk || msg) mod L */ + { + hash_state md; + if ((err = sha3_shake_init(&md, 256)) != CRYPT_OK) return err; + if (ctx != NULL && cs > 0) { + if ((err = sha3_shake_process(&md, ctx, (unsigned long)cs)) != CRYPT_OK) return err; + } + if ((err = sha3_shake_process(&md, m, (unsigned long)smlen)) != CRYPT_OK) return err; + if ((err = sha3_shake_done(&md, hram, 114)) != CRYPT_OK) return err; + } + s_sc448_reduce_buf(hram); + + /* Compute [h]A */ + s_ed448_scalarmult(p, q, hram); + + /* Add R: p = R + [h]A */ + s_ed448_point_add(p, r_point); + + /* Compute [S]B */ + s_ed448_scalarmult_base(q, s_bytes); + + /* Compare: [S]B == R + [h]A */ + s_ed448_point_encode(t, q); + { + unsigned char t2[57]; + s_ed448_point_encode(t2, p); + + /* Constant-time comparison */ + { + ulong32 diff = 0; + int idx; + for (idx = 0; idx < 57; idx++) diff |= (ulong32)(t[idx] ^ t2[idx]); + if (diff == 0) { + *stat = 1; + smlen -= 114; + XMEMMOVE(m, m + 114, (unsigned long)smlen); + *mlen = smlen; + } + else { + for (i = 0; i < smlen - 114; ++i) m[i] = 0; + zeromem(m, (unsigned long)(smlen - 114)); + } + } + } + + return CRYPT_OK; +} + +/* Ed448 prehash: SHAKE256(msg) truncated to 64 bytes */ +int ec448_prehash_internal(unsigned char *out, const unsigned char *msg, unsigned long long msglen) +{ + unsigned long phlen = 64; + return sha3_shake_memory(256, msg, (unsigned long)msglen, out, &phlen); +} + + +/* X448 scalar multiplication (RFC 7748 Montgomery ladder) */ +int ec448_scalarmult_internal(unsigned char *out, const unsigned char *scalar, const unsigned char *point) +{ + unsigned char sk[56]; + gf448 x1, x2, z2, x3, z3; + gf448 a, aa, b, bb, e, c, d, da, cb, t; + static const gf448 a24 = {39081}; + int i; + unsigned char swap = 0; + + /* clamp scalar */ + XMEMCPY(sk, scalar, 56); + sk[0] &= 252; + sk[55] |= 128; + + /* decode u-coordinate */ + s_gf448_decode(x1, point); + + /* x_2 = 1, z_2 = 0, x_3 = u, z_3 = 1 */ + s_gf448_copy(x3, x1); + s_gf448_copy(x2, gf448_1); + s_gf448_copy(z2, gf448_0); + s_gf448_copy(z3, gf448_1); + + for (i = 447; i >= 0; --i) { + unsigned char k_t = (sk[i / 8] >> (i & 7)) & 1; + swap ^= k_t; + s_gf448_cswap(x2, x3, swap); + s_gf448_cswap(z2, z3, swap); + swap = k_t; + + s_gf448_add(a, x2, z2); /* A = x_2 + z_2 */ + s_gf448_sqr(aa, a); /* AA = A^2 */ + s_gf448_sub(b, x2, z2); /* B = x_2 - z_2 */ + s_gf448_sqr(bb, b); /* BB = B^2 */ + s_gf448_sub(e, aa, bb); /* E = AA - BB */ + s_gf448_add(c, x3, z3); /* C = x_3 + z_3 */ + s_gf448_sub(d, x3, z3); /* D = x_3 - z_3 */ + s_gf448_mul(da, d, a); /* DA = D * A */ + s_gf448_mul(cb, c, b); /* CB = C * B */ + + s_gf448_add(x3, da, cb); /* x_3 = (DA + CB)^2 */ + s_gf448_sqr(x3, x3); + + s_gf448_sub(z3, da, cb); /* z_3 = x_1*(DA-CB)^2 */ + s_gf448_sqr(z3, z3); + s_gf448_mul(z3, x1, z3); + + s_gf448_mul(x2, aa, bb); /* x_2 = AA * BB */ + + s_gf448_mul(t, a24, e); /* z_2 = E*(AA + a24*E) */ + s_gf448_add(t, aa, t); + s_gf448_mul(z2, e, t); + } + + s_gf448_cswap(x2, x3, swap); + s_gf448_cswap(z2, z3, swap); + + s_gf448_inv(t, z2); + s_gf448_mul(x2, x2, t); + + s_gf448_encode(out, x2); + + zeromem(sk, sizeof(sk)); + return CRYPT_OK; +} + +/* X448 scalar multiplication with base point u=5 */ +static const unsigned char s_x448_basepoint[56] = { + 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 +}; + +int ec448_scalarmult_base_internal(unsigned char *out, const unsigned char *scalar) +{ + ec448_scalarmult_internal(out, scalar, s_x448_basepoint); + return CRYPT_OK; +} + +#endif diff --git a/src/pk/ec448/ec448_crypto_ctx.c b/src/pk/ec448/ec448_crypto_ctx.c new file mode 100644 index 000000000..335d3c916 --- /dev/null +++ b/src/pk/ec448/ec448_crypto_ctx.c @@ -0,0 +1,51 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ +#include "tomcrypt_private.h" + +/** + @file ec448_crypto_ctx.c + Build DOM4 context prefix for Ed448ctx / Ed448ph (RFC 8032, Section 5.2). + DOM4(x, y) = "SigEd448" || octet(x) || octet(OLEN(y)) || y +*/ + +#ifdef LTC_CURVE448 + +/** + @param out [out] Destination buffer + @param outlen [in/out] Max size / resulting size + @param flag 0 for Ed448/Ed448ctx, 1 for Ed448ph + @param ctx Context string (may be NULL if ctxlen==0) + @param ctxlen Length of context (0..255) + @return CRYPT_OK if successful +*/ +int ec448_crypto_ctx(unsigned char *out, unsigned long *outlen, unsigned char flag, + const unsigned char *ctx, unsigned long ctxlen) +{ + unsigned char *buf = out; + + const char *prefix = "SigEd448"; + const unsigned long prefix_len = 8; + const unsigned char ctxlen8 = (unsigned char)ctxlen; + + if (ctxlen > 255u) return CRYPT_INPUT_TOO_LONG; + if (*outlen < prefix_len + 2u + ctxlen) return CRYPT_BUFFER_OVERFLOW; + + XMEMCPY(buf, prefix, prefix_len); + buf += prefix_len; + XMEMCPY(buf, &flag, 1); + buf++; + XMEMCPY(buf, &ctxlen8, 1); + buf++; + + if (ctxlen > 0u) { + LTC_ARGCHK(ctx != NULL); + XMEMCPY(buf, ctx, ctxlen); + buf += ctxlen; + } + + *outlen = (unsigned long)(buf - out); + + return CRYPT_OK; +} + +#endif diff --git a/src/pk/ec448/ec448_export.c b/src/pk/ec448/ec448_export.c new file mode 100644 index 000000000..7b1934c1e --- /dev/null +++ b/src/pk/ec448/ec448_export.c @@ -0,0 +1,98 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ +#include "tomcrypt_private.h" + +/** + @file ec448_export.c + Generic export of a Curve448 key to a binary packet +*/ + +#ifdef LTC_CURVE448 + +/** + Generic export of a Curve448 key to a binary packet + @param out [out] The destination for the key + @param outlen [in/out] The max size and resulting size of the key + @param which Which type of key (PK_PRIVATE, PK_PUBLIC|PK_STD or PK_PUBLIC) + @param key The key you wish to export + @return CRYPT_OK if successful +*/ +int ec448_export( unsigned char *out, unsigned long *outlen, + int which, + const curve448_key *key) +{ + int err, std; + const char* OID; + unsigned long oid[16], oidlen; + ltc_asn1_list alg_id[1]; + enum ltc_oid_id oid_id; + unsigned char private_key[59]; + unsigned long version, private_key_len = sizeof(private_key); + unsigned long key_sz; + + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + LTC_ARGCHK(key != NULL); + + std = which & PK_STD; + which &= ~PK_STD; + if ((err = pk_get_oid_id(key->pka, &oid_id)) != CRYPT_OK) { + return err; + } + + /* X448 keys are 56 bytes, Ed448 keys are 57 bytes */ + key_sz = (key->pka == LTC_PKA_ED448) ? 57uL : 56uL; + + if (which == PK_PRIVATE) { + if(key->type != PK_PRIVATE) return CRYPT_PK_INVALID_TYPE; + + if (std == PK_STD) { + if ((err = pk_get_oid(oid_id, &OID)) != CRYPT_OK) { + return err; + } + oidlen = LTC_ARRAY_SIZE(oid); + if ((err = pk_oid_str_to_num(OID, oid, &oidlen)) != CRYPT_OK) { + return err; + } + + LTC_SET_ASN1(alg_id, 0, LTC_ASN1_OBJECT_IDENTIFIER, oid, oidlen); + + /* encode private key as PKCS#8 */ + if ((err = der_encode_octet_string(key->priv, key_sz, private_key, &private_key_len)) != CRYPT_OK) { + return err; + } + + version = 0; + err = der_encode_sequence_multi(out, outlen, + LTC_ASN1_SHORT_INTEGER, 1uL, &version, + LTC_ASN1_SEQUENCE, 1uL, alg_id, + LTC_ASN1_OCTET_STRING, private_key_len, private_key, + LTC_ASN1_EOL, 0uL, NULL); + } else { + if (*outlen < key_sz) { + err = CRYPT_BUFFER_OVERFLOW; + } else { + XMEMCPY(out, key->priv, key_sz); + err = CRYPT_OK; + } + *outlen = key_sz; + } + } else { + if (std == PK_STD) { + /* encode public key as SubjectPublicKeyInfo */ + err = x509_encode_subject_public_key_info(out, outlen, oid_id, key->pub, key_sz, LTC_ASN1_EOL, NULL, 0); + } else { + if (*outlen < key_sz) { + err = CRYPT_BUFFER_OVERFLOW; + } else { + XMEMCPY(out, key->pub, key_sz); + err = CRYPT_OK; + } + *outlen = key_sz; + } + } + + return err; +} + +#endif diff --git a/src/pk/ec448/ec448_import_pkcs8.c b/src/pk/ec448/ec448_import_pkcs8.c new file mode 100644 index 000000000..0de083472 --- /dev/null +++ b/src/pk/ec448/ec448_import_pkcs8.c @@ -0,0 +1,86 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ +#include "tomcrypt_private.h" + +/** + @file ec448_import_pkcs8.c + Generic import of a Curve448 private key in PKCS#8 format +*/ + +#ifdef LTC_CURVE448 + +typedef int (*sk_to_pk)(unsigned char *pk, const unsigned char *sk); + +int ec448_import_pkcs8_asn1(ltc_asn1_list *alg_id, ltc_asn1_list *priv_key, + enum ltc_oid_id id, + curve448_key *key) +{ + int err; + unsigned long key_len; + sk_to_pk fp; + + LTC_ARGCHK(key != NULL); + LTC_ARGCHK(ltc_mp.name != NULL); + + LTC_UNUSED_PARAM(alg_id); + + switch (id) { + case LTC_OID_X448: + fp = ec448_scalarmult_base_internal; + break; + case LTC_OID_ED448: + fp = ec448_sk_to_pk_internal; + break; + default: + return CRYPT_PK_INVALID_TYPE; + } + + key_len = sizeof(key->priv); + if ((err = der_decode_octet_string(priv_key->data, priv_key->size, key->priv, &key_len)) == CRYPT_OK) { + fp(key->pub, key->priv); + key->type = PK_PRIVATE; + err = pk_get_pka_id(id, &key->pka); + } + return err; +} + +/** + Generic import of a Curve448 private key in PKCS#8 format + @param in The packet to import from + @param inlen It's length (octets) + @param pw_ctx The password context when decrypting the private key + @param id The type of the private key + @param key [out] Destination for newly imported key + @return CRYPT_OK if successful, on error all allocated memory is freed automatically +*/ +int ec448_import_pkcs8(const unsigned char *in, unsigned long inlen, + const password_ctx *pw_ctx, + enum ltc_oid_id id, + curve448_key *key) +{ + int err; + ltc_asn1_list *l = NULL; + ltc_asn1_list *alg_id, *priv_key; + enum ltc_oid_id pka; + + LTC_ARGCHK(in != NULL); + + err = pkcs8_decode_flexi(in, inlen, pw_ctx, &l); + if (err != CRYPT_OK) return err; + + if ((err = pkcs8_get_children(l, &pka, &alg_id, &priv_key)) != CRYPT_OK) { + goto LBL_DER_FREE; + } + if (pka != id) { + err = CRYPT_INVALID_PACKET; + goto LBL_DER_FREE; + } + + err = ec448_import_pkcs8_asn1(alg_id, priv_key, id, key); + +LBL_DER_FREE: + der_free_sequence_flexi(l); + return err; +} + +#endif diff --git a/src/pk/ed448/ed448_export.c b/src/pk/ed448/ed448_export.c new file mode 100644 index 000000000..4e1b58568 --- /dev/null +++ b/src/pk/ed448/ed448_export.c @@ -0,0 +1,31 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ +#include "tomcrypt_private.h" + +/** + @file ed448_export.c + Export an Ed448 key to a binary packet, Steffen Jaeckel +*/ + +#ifdef LTC_CURVE448 + +/** + Export an Ed448 key to a binary packet + @param out [out] The destination for the key + @param outlen [in/out] The max size and resulting size of the Ed448 key + @param which Which type of key (PK_PRIVATE, PK_PUBLIC|PK_STD or PK_PUBLIC) + @param key The key you wish to export + @return CRYPT_OK if successful +*/ +int ed448_export( unsigned char *out, unsigned long *outlen, + int which, + const curve448_key *key) +{ + LTC_ARGCHK(key != NULL); + + if (key->pka != LTC_PKA_ED448) return CRYPT_PK_INVALID_TYPE; + + return ec448_export(out, outlen, which, key); +} + +#endif diff --git a/src/pk/ed448/ed448_import.c b/src/pk/ed448/ed448_import.c new file mode 100644 index 000000000..f4db0918c --- /dev/null +++ b/src/pk/ed448/ed448_import.c @@ -0,0 +1,35 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ +#include "tomcrypt_private.h" + +/** + @file ed448_import.c + Import an Ed448 key from a SubjectPublicKeyInfo, Steffen Jaeckel +*/ + +#ifdef LTC_CURVE448 + +/** + Import an Ed448 public key + @param in The packet to read + @param inlen The length of the input packet + @param key [out] Where to import the key to + @return CRYPT_OK if successful, on error all allocated memory is freed automatically +*/ +int ed448_import(const unsigned char *in, unsigned long inlen, curve448_key *key) +{ + int err; + unsigned long key_len; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(key != NULL); + + key_len = sizeof(key->pub); + if ((err = x509_decode_subject_public_key_info(in, inlen, LTC_OID_ED448, key->pub, &key_len, LTC_ASN1_EOL, NULL, 0uL)) == CRYPT_OK) { + key->type = PK_PUBLIC; + key->pka = LTC_PKA_ED448; + } + return err; +} + +#endif diff --git a/src/pk/ed448/ed448_import_pkcs8.c b/src/pk/ed448/ed448_import_pkcs8.c new file mode 100644 index 000000000..1a6ab4ed1 --- /dev/null +++ b/src/pk/ed448/ed448_import_pkcs8.c @@ -0,0 +1,33 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ +#include "tomcrypt_private.h" + +/** + @file ed448_import_pkcs8.c + Import an Ed448 key in PKCS#8 format, Steffen Jaeckel +*/ + +#ifdef LTC_CURVE448 + +int ed448_import_pkcs8_asn1(ltc_asn1_list *alg_id, ltc_asn1_list *priv_key, + curve448_key *key) +{ + return ec448_import_pkcs8_asn1(alg_id, priv_key, LTC_OID_ED448, key); +} + +/** + Import an Ed448 private key in PKCS#8 format + @param in The packet to import from + @param inlen It's length (octets) + @param pw_ctx The password context when decrypting the private key + @param key [out] Destination for newly imported key + @return CRYPT_OK if successful, on error all allocated memory is freed automatically +*/ +int ed448_import_pkcs8(const unsigned char *in, unsigned long inlen, + const password_ctx *pw_ctx, + curve448_key *key) +{ + return ec448_import_pkcs8(in, inlen, pw_ctx, LTC_OID_ED448, key); +} + +#endif diff --git a/src/pk/ed448/ed448_import_raw.c b/src/pk/ed448/ed448_import_raw.c new file mode 100644 index 000000000..b887d4b87 --- /dev/null +++ b/src/pk/ed448/ed448_import_raw.c @@ -0,0 +1,42 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ +#include "tomcrypt_private.h" + +/** + @file ed448_import_raw.c + Set the parameters of an Ed448 key, Steffen Jaeckel +*/ + +#ifdef LTC_CURVE448 + +/** + Set the parameters of an Ed448 key + + @param in The key + @param inlen The length of the key + @param which Which type of key (PK_PRIVATE or PK_PUBLIC) + @param key [out] Destination of the key + @return CRYPT_OK if successful +*/ +int ed448_import_raw(const unsigned char *in, unsigned long inlen, int which, curve448_key *key) +{ + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(key != NULL); + + if (which == PK_PRIVATE) { + LTC_ARGCHK(inlen == 57uL); + XMEMCPY(key->priv, in, sizeof(key->priv)); + ec448_sk_to_pk_internal(key->pub, key->priv); + } else if (which == PK_PUBLIC) { + LTC_ARGCHK(inlen == 57uL); + XMEMCPY(key->pub, in, sizeof(key->pub)); + } else { + return CRYPT_INVALID_ARG; + } + key->pka = LTC_PKA_ED448; + key->type = which; + + return CRYPT_OK; +} + +#endif diff --git a/src/pk/ed448/ed448_import_x509.c b/src/pk/ed448/ed448_import_x509.c new file mode 100644 index 000000000..c90acf42e --- /dev/null +++ b/src/pk/ed448/ed448_import_x509.c @@ -0,0 +1,45 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ +#include "tomcrypt_private.h" + +/** + @file ed448_import_x509.c + Import an Ed448 key from a X.509 certificate, Steffen Jaeckel +*/ + +#ifdef LTC_CURVE448 + +static int s_ed448_decode(const unsigned char *in, unsigned long inlen, curve448_key *key) +{ + if (inlen != sizeof(key->pub)) return CRYPT_PK_INVALID_SIZE; + XMEMCPY(key->pub, in, sizeof(key->pub)); + return CRYPT_OK; +} + +/** + Import an Ed448 public key from a X.509 certificate + @param in The DER encoded X.509 certificate + @param inlen The length of the certificate + @param key [out] Where to import the key to + @return CRYPT_OK if successful, on error all allocated memory is freed automatically +*/ +int ed448_import_x509(const unsigned char *in, unsigned long inlen, curve448_key *key) +{ + int err; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(key != NULL); + + if ((err = x509_decode_public_key_from_certificate(in, inlen, + LTC_OID_ED448, + LTC_ASN1_EOL, NULL, NULL, + (public_key_decode_cb)s_ed448_decode, key)) != CRYPT_OK) { + return err; + } + key->type = PK_PUBLIC; + key->pka = LTC_PKA_ED448; + + return err; +} + +#endif diff --git a/src/pk/ed448/ed448_make_key.c b/src/pk/ed448/ed448_make_key.c new file mode 100644 index 000000000..0170dff04 --- /dev/null +++ b/src/pk/ed448/ed448_make_key.c @@ -0,0 +1,36 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ +#include "tomcrypt_private.h" + +/** + @file ed448_make_key.c + Create an Ed448 key, Steffen Jaeckel +*/ + +#ifdef LTC_CURVE448 + +/** + Create an Ed448 key + @param prng An active PRNG state + @param wprng The index of the PRNG desired + @param key [out] Destination of a newly created private key pair + @return CRYPT_OK if successful +*/ +int ed448_make_key(prng_state *prng, int wprng, curve448_key *key) +{ + int err; + + LTC_ARGCHK(prng != NULL); + LTC_ARGCHK(key != NULL); + + if ((err = ec448_keypair_internal(prng, wprng, key->pub, key->priv)) != CRYPT_OK) { + return err; + } + + key->type = PK_PRIVATE; + key->pka = LTC_PKA_ED448; + + return err; +} + +#endif diff --git a/src/pk/ed448/ed448_sign.c b/src/pk/ed448/ed448_sign.c new file mode 100644 index 000000000..5f6d2dce3 --- /dev/null +++ b/src/pk/ed448/ed448_sign.c @@ -0,0 +1,136 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ +#include "tomcrypt_private.h" + +/** + @file ed448_sign.c + Create an Ed448 signature, Steffen Jaeckel +*/ + +#ifdef LTC_CURVE448 + +static int s_ed448_sign(const unsigned char *msg, unsigned long msglen, + unsigned char *sig, unsigned long *siglen, + const unsigned char *ctx, unsigned long ctxlen, + const curve448_key *private_key) +{ + unsigned char *s; + unsigned long long smlen; + int err; + + LTC_ARGCHK(msg != NULL); + LTC_ARGCHK(sig != NULL); + LTC_ARGCHK(siglen != NULL); + LTC_ARGCHK(private_key != NULL); + + if (private_key->pka != LTC_PKA_ED448) return CRYPT_PK_INVALID_TYPE; + if (private_key->type != PK_PRIVATE) return CRYPT_PK_INVALID_TYPE; + + if (*siglen < 114uL) { + *siglen = 114uL; + return CRYPT_BUFFER_OVERFLOW; + } + + smlen = msglen + 114; + s = XMALLOC((unsigned long)smlen); + if (s == NULL) return CRYPT_MEM; + + err = ec448_sign_internal(s, &smlen, + msg, msglen, + private_key->priv, private_key->pub, + ctx, ctxlen); + + XMEMCPY(sig, s, 114uL); + *siglen = 114uL; + +#ifdef LTC_CLEAN_STACK + zeromem(s, (unsigned long)smlen); +#endif + XFREE(s); + + return err; +} + +/** + Create an Ed448ctx signature. + @param msg The data to be signed + @param msglen [in] The size of the data to be signed + @param sig [out] The destination of the signature + @param siglen [in/out] The max size and resulting size of the signature + @param ctx [in] The context + @param ctxlen [in] The size of the context + @param private_key The private Ed448 key in the pair + @return CRYPT_OK if successful +*/ +int ed448ctx_sign(const unsigned char *msg, unsigned long msglen, + unsigned char *sig, unsigned long *siglen, + const unsigned char *ctx, unsigned long ctxlen, + const curve448_key *private_key) +{ + int err; + unsigned char ctx_prefix[266]; + unsigned long ctx_prefix_size = sizeof(ctx_prefix); + + LTC_ARGCHK(ctx != NULL); + + if ((err = ec448_crypto_ctx(ctx_prefix, &ctx_prefix_size, 0, ctx, ctxlen)) != CRYPT_OK) + return err; + + return s_ed448_sign(msg, msglen, sig, siglen, ctx_prefix, ctx_prefix_size, private_key); +} + +/** + Create an Ed448ph signature. + @param msg The data to be signed + @param msglen [in] The size of the data to be signed + @param sig [out] The destination of the signature + @param siglen [in/out] The max size and resulting size of the signature + @param ctx [in] The context + @param ctxlen [in] The size of the context + @param private_key The private Ed448 key in the pair + @return CRYPT_OK if successful +*/ +int ed448ph_sign(const unsigned char *msg, unsigned long msglen, + unsigned char *sig, unsigned long *siglen, + const unsigned char *ctx, unsigned long ctxlen, + const curve448_key *private_key) +{ + int err; + unsigned char msg_hash[64]; + unsigned char ctx_prefix[266]; + unsigned long ctx_prefix_size = sizeof(ctx_prefix); + + if ((err = ec448_crypto_ctx(ctx_prefix, &ctx_prefix_size, 1, ctx, ctxlen)) != CRYPT_OK) + return err; + + if ((err = ec448_prehash_internal(msg_hash, msg, msglen)) != CRYPT_OK) + return err; + + return s_ed448_sign(msg_hash, sizeof(msg_hash), sig, siglen, ctx_prefix, ctx_prefix_size, private_key); +} + +/** + Create an Ed448 signature (pure mode). + @param msg The data to be signed + @param msglen [in] The size of the data to be signed + @param sig [out] The destination of the signature + @param siglen [in/out] The max size and resulting size of the signature + @param private_key The private Ed448 key in the pair + @return CRYPT_OK if successful +*/ +int ed448_sign(const unsigned char *msg, unsigned long msglen, + unsigned char *sig, unsigned long *siglen, + const curve448_key *private_key) +{ + int err; + unsigned char ctx_prefix[266]; + unsigned long ctx_prefix_size = sizeof(ctx_prefix); + + /* Pure Ed448 still uses DOM4 with flag=0 and empty context */ + if ((err = ec448_crypto_ctx(ctx_prefix, &ctx_prefix_size, 0, NULL, 0)) != CRYPT_OK) + return err; + + return s_ed448_sign(msg, msglen, sig, siglen, ctx_prefix, ctx_prefix_size, private_key); +} + +#endif diff --git a/src/pk/ed448/ed448_verify.c b/src/pk/ed448/ed448_verify.c new file mode 100644 index 000000000..b39d5185a --- /dev/null +++ b/src/pk/ed448/ed448_verify.c @@ -0,0 +1,143 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ +#include "tomcrypt_private.h" + +/** + @file ed448_verify.c + Verify an Ed448 signature, Steffen Jaeckel +*/ + +#ifdef LTC_CURVE448 + +static int s_ed448_verify(const unsigned char *msg, unsigned long msglen, + const unsigned char *sig, unsigned long siglen, + const unsigned char *ctx, unsigned long ctxlen, + int *stat, + const curve448_key *public_key) +{ + unsigned char *m; + unsigned long long mlen; + int err; + + LTC_ARGCHK(msg != NULL); + LTC_ARGCHK(sig != NULL); + LTC_ARGCHK(stat != NULL); + LTC_ARGCHK(public_key != NULL); + + *stat = 0; + + if (siglen != 114uL) return CRYPT_INVALID_ARG; + if (public_key->pka != LTC_PKA_ED448) return CRYPT_PK_INVALID_TYPE; + + mlen = msglen + siglen; + if ((mlen < msglen) || (mlen < siglen)) return CRYPT_OVERFLOW; + + m = XMALLOC((unsigned long)mlen); + if (m == NULL) return CRYPT_MEM; + + XMEMCPY(m, sig, siglen); + XMEMCPY(m + siglen, msg, msglen); + + err = ec448_verify_internal(stat, + m, &mlen, + m, mlen, + ctx, ctxlen, + public_key->pub); + +#ifdef LTC_CLEAN_STACK + zeromem(m, msglen + siglen); +#endif + XFREE(m); + + return err; +} + +/** + Verify an Ed448ctx signature. + @param msg [in] The data to be verified + @param msglen [in] The size of the data to be verified + @param sig [in] The signature to be verified + @param siglen [in] The size of the signature to be verified + @param ctx [in] The context + @param ctxlen [in] The size of the context + @param stat [out] The result of the signature verification, 1==valid, 0==invalid + @param public_key [in] The public Ed448 key in the pair + @return CRYPT_OK if successful +*/ +int ed448ctx_verify(const unsigned char *msg, unsigned long msglen, + const unsigned char *sig, unsigned long siglen, + const unsigned char *ctx, unsigned long ctxlen, + int *stat, + const curve448_key *public_key) +{ + int err; + unsigned char ctx_prefix[266]; + unsigned long ctx_prefix_size = sizeof(ctx_prefix); + + LTC_ARGCHK(ctx != NULL); + + if ((err = ec448_crypto_ctx(ctx_prefix, &ctx_prefix_size, 0, ctx, ctxlen)) != CRYPT_OK) + return err; + + return s_ed448_verify(msg, msglen, sig, siglen, ctx_prefix, ctx_prefix_size, stat, public_key); +} + +/** + Verify an Ed448ph signature. + @param msg [in] The data to be verified + @param msglen [in] The size of the data to be verified + @param sig [in] The signature to be verified + @param siglen [in] The size of the signature to be verified + @param ctx [in] The context + @param ctxlen [in] The size of the context + @param stat [out] The result of the signature verification, 1==valid, 0==invalid + @param public_key [in] The public Ed448 key in the pair + @return CRYPT_OK if successful +*/ +int ed448ph_verify(const unsigned char *msg, unsigned long msglen, + const unsigned char *sig, unsigned long siglen, + const unsigned char *ctx, unsigned long ctxlen, + int *stat, + const curve448_key *public_key) +{ + int err; + unsigned char msg_hash[64]; + unsigned char ctx_prefix[266]; + unsigned long ctx_prefix_size = sizeof(ctx_prefix); + + if ((err = ec448_crypto_ctx(ctx_prefix, &ctx_prefix_size, 1, ctx, ctxlen)) != CRYPT_OK) + return err; + + if ((err = ec448_prehash_internal(msg_hash, msg, msglen)) != CRYPT_OK) + return err; + + return s_ed448_verify(msg_hash, sizeof(msg_hash), sig, siglen, ctx_prefix, ctx_prefix_size, stat, public_key); +} + +/** + Verify an Ed448 signature (pure mode). + @param msg [in] The data to be verified + @param msglen [in] The size of the data to be verified + @param sig [in] The signature to be verified + @param siglen [in] The size of the signature to be verified + @param stat [out] The result of the signature verification, 1==valid, 0==invalid + @param public_key [in] The public Ed448 key in the pair + @return CRYPT_OK if successful +*/ +int ed448_verify(const unsigned char *msg, unsigned long msglen, + const unsigned char *sig, unsigned long siglen, + int *stat, + const curve448_key *public_key) +{ + int err; + unsigned char ctx_prefix[266]; + unsigned long ctx_prefix_size = sizeof(ctx_prefix); + + /* Pure Ed448 still uses DOM4 with flag=0 and empty context */ + if ((err = ec448_crypto_ctx(ctx_prefix, &ctx_prefix_size, 0, NULL, 0)) != CRYPT_OK) + return err; + + return s_ed448_verify(msg, msglen, sig, siglen, ctx_prefix, ctx_prefix_size, stat, public_key); +} + +#endif diff --git a/src/pk/x448/x448_export.c b/src/pk/x448/x448_export.c new file mode 100644 index 000000000..33d0b1749 --- /dev/null +++ b/src/pk/x448/x448_export.c @@ -0,0 +1,31 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ +#include "tomcrypt_private.h" + +/** + @file x448_export.c + Export a X448 key to a binary packet +*/ + +#ifdef LTC_CURVE448 + +/** + Export a X448 key to a binary packet + @param out [out] The destination for the key + @param outlen [in/out] The max size and resulting size of the X448 key + @param which Which type of key (PK_PRIVATE, PK_PUBLIC|PK_STD or PK_PUBLIC) + @param key The key you wish to export + @return CRYPT_OK if successful +*/ +int x448_export( unsigned char *out, unsigned long *outlen, + int which, + const curve448_key *key) +{ + LTC_ARGCHK(key != NULL); + + if (key->pka != LTC_PKA_X448) return CRYPT_PK_INVALID_TYPE; + + return ec448_export(out, outlen, which, key); +} + +#endif diff --git a/src/pk/x448/x448_import.c b/src/pk/x448/x448_import.c new file mode 100644 index 000000000..ea6f757e8 --- /dev/null +++ b/src/pk/x448/x448_import.c @@ -0,0 +1,35 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ +#include "tomcrypt_private.h" + +/** + @file x448_import.c + Import a X448 key from a SubjectPublicKeyInfo +*/ + +#ifdef LTC_CURVE448 + +/** + Import a X448 key + @param in The packet to read + @param inlen The length of the input packet + @param key [out] Where to import the key to + @return CRYPT_OK if successful, on error all allocated memory is freed automatically +*/ +int x448_import(const unsigned char *in, unsigned long inlen, curve448_key *key) +{ + int err; + unsigned long key_len; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(key != NULL); + + key_len = 56uL; + if ((err = x509_decode_subject_public_key_info(in, inlen, LTC_OID_X448, key->pub, &key_len, LTC_ASN1_EOL, NULL, 0uL)) == CRYPT_OK) { + key->type = PK_PUBLIC; + key->pka = LTC_PKA_X448; + } + return err; +} + +#endif diff --git a/src/pk/x448/x448_import_pkcs8.c b/src/pk/x448/x448_import_pkcs8.c new file mode 100644 index 000000000..c0512923a --- /dev/null +++ b/src/pk/x448/x448_import_pkcs8.c @@ -0,0 +1,33 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ +#include "tomcrypt_private.h" + +/** + @file x448_import_pkcs8.c + Import a X448 key in PKCS#8 format +*/ + +#ifdef LTC_CURVE448 + +int x448_import_pkcs8_asn1(ltc_asn1_list *alg_id, ltc_asn1_list *priv_key, + curve448_key *key) +{ + return ec448_import_pkcs8_asn1(alg_id, priv_key, LTC_OID_X448, key); +} + +/** + Import a X448 private key in PKCS#8 format + @param in The packet to import from + @param inlen It's length (octets) + @param pw_ctx The password context when decrypting the private key + @param key [out] Destination for newly imported key + @return CRYPT_OK if successful, on error all allocated memory is freed automatically +*/ +int x448_import_pkcs8(const unsigned char *in, unsigned long inlen, + const password_ctx *pw_ctx, + curve448_key *key) +{ + return ec448_import_pkcs8(in, inlen, pw_ctx, LTC_OID_X448, key); +} + +#endif diff --git a/src/pk/x448/x448_import_raw.c b/src/pk/x448/x448_import_raw.c new file mode 100644 index 000000000..66ba3114b --- /dev/null +++ b/src/pk/x448/x448_import_raw.c @@ -0,0 +1,41 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ +#include "tomcrypt_private.h" + +/** + @file x448_import_raw.c + Set the parameters of a X448 key +*/ + +#ifdef LTC_CURVE448 + +/** + Set the parameters of a X448 key + + @param in The key + @param inlen The length of the key + @param which Which type of key (PK_PRIVATE or PK_PUBLIC) + @param key [out] Destination of the key + @return CRYPT_OK if successful +*/ +int x448_import_raw(const unsigned char *in, unsigned long inlen, int which, curve448_key *key) +{ + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(inlen == 56uL); + LTC_ARGCHK(key != NULL); + + if (which == PK_PRIVATE) { + XMEMCPY(key->priv, in, 56uL); + ec448_scalarmult_base_internal(key->pub, key->priv); + } else if (which == PK_PUBLIC) { + XMEMCPY(key->pub, in, 56uL); + } else { + return CRYPT_INVALID_ARG; + } + key->pka = LTC_PKA_X448; + key->type = which; + + return CRYPT_OK; +} + +#endif diff --git a/src/pk/x448/x448_import_x509.c b/src/pk/x448/x448_import_x509.c new file mode 100644 index 000000000..f6822908c --- /dev/null +++ b/src/pk/x448/x448_import_x509.c @@ -0,0 +1,45 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ +#include "tomcrypt_private.h" + +/** + @file x448_import_x509.c + Import a X448 key from a X.509 certificate +*/ + +#ifdef LTC_CURVE448 + +static int s_x448_decode(const unsigned char *in, unsigned long inlen, curve448_key *key) +{ + if (inlen != 56uL) return CRYPT_PK_INVALID_SIZE; + XMEMCPY(key->pub, in, 56uL); + return CRYPT_OK; +} + +/** + Import a X448 public key from a X.509 certificate + @param in The DER encoded X.509 certificate + @param inlen The length of the certificate + @param key [out] Where to import the key to + @return CRYPT_OK if successful, on error all allocated memory is freed automatically +*/ +int x448_import_x509(const unsigned char *in, unsigned long inlen, curve448_key *key) +{ + int err; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(key != NULL); + + if ((err = x509_decode_public_key_from_certificate(in, inlen, + LTC_OID_X448, + LTC_ASN1_EOL, NULL, NULL, + (public_key_decode_cb)s_x448_decode, key)) != CRYPT_OK) { + return err; + } + key->type = PK_PUBLIC; + key->pka = LTC_PKA_X448; + + return err; +} + +#endif diff --git a/src/pk/x448/x448_make_key.c b/src/pk/x448/x448_make_key.c new file mode 100644 index 000000000..07b80ce1a --- /dev/null +++ b/src/pk/x448/x448_make_key.c @@ -0,0 +1,42 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ +#include "tomcrypt_private.h" + +/** + @file x448_make_key.c + Create a X448 key +*/ + +#ifdef LTC_CURVE448 + +/** + Create a X448 key + @param prng An active PRNG state + @param wprng The index of the PRNG desired + @param key [out] Destination of a newly created private key pair + @return CRYPT_OK if successful +*/ +int x448_make_key(prng_state *prng, int wprng, curve448_key *key) +{ + int err; + + LTC_ARGCHK(prng != NULL); + LTC_ARGCHK(key != NULL); + + if ((err = prng_is_valid(wprng)) != CRYPT_OK) { + return err; + } + + if (prng_descriptor[wprng].read(key->priv, 56uL, prng) != 56uL) { + return CRYPT_ERROR_READPRNG; + } + + ec448_scalarmult_base_internal(key->pub, key->priv); + + key->type = PK_PRIVATE; + key->pka = LTC_PKA_X448; + + return err; +} + +#endif diff --git a/src/pk/x448/x448_shared_secret.c b/src/pk/x448/x448_shared_secret.c new file mode 100644 index 000000000..f53d3e0fe --- /dev/null +++ b/src/pk/x448/x448_shared_secret.c @@ -0,0 +1,43 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ +#include "tomcrypt_private.h" + +/** + @file x448_shared_secret.c + Create a X448 shared secret +*/ + +#ifdef LTC_CURVE448 + +/** + Create a X448 shared secret. + @param private_key The private X448 key in the pair + @param public_key The public X448 key in the pair + @param out [out] The destination of the shared data + @param outlen [in/out] The max size and resulting size of the shared data. + @return CRYPT_OK if successful +*/ +int x448_shared_secret(const curve448_key *private_key, + const curve448_key *public_key, + unsigned char *out, unsigned long *outlen) +{ + LTC_ARGCHK(private_key != NULL); + LTC_ARGCHK(public_key != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + if (public_key->pka != LTC_PKA_X448) return CRYPT_PK_INVALID_TYPE; + if (private_key->type != PK_PRIVATE) return CRYPT_PK_INVALID_TYPE; + + if (*outlen < 56uL) { + *outlen = 56uL; + return CRYPT_BUFFER_OVERFLOW; + } + + ec448_scalarmult_internal(out, private_key->priv, public_key->pub); + *outlen = 56uL; + + return CRYPT_OK; +} + +#endif diff --git a/tests/ed448_test.c b/tests/ed448_test.c new file mode 100644 index 000000000..68ca17ad1 --- /dev/null +++ b/tests/ed448_test.c @@ -0,0 +1,439 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ +#include "tomcrypt_test.h" + +#ifdef LTC_CURVE448 + +typedef struct { + const char *secret_key; + const char *public_key; + const char *message; + const char *signature; + const char *context; +} rfc_8032_t; + +static int s_ed448_keygen_sign_verify(void) +{ + curve448_key key, pubkey; + const unsigned char msg[] = "test message"; + unsigned char sig[114], pk_buf[57]; + unsigned long siglen, pklen; + int stat, prng_idx; + + prng_idx = find_prng("yarrow"); + + /* keygen */ + DO(ed448_make_key(&yarrow_prng, prng_idx, &key)); + ENSURE(key.type == PK_PRIVATE); + ENSURE(key.pka == LTC_PKA_ED448); + + /* sign */ + siglen = sizeof(sig); + DO(ed448_sign(msg, sizeof(msg), sig, &siglen, &key)); + ENSURE(siglen == 114); + + /* export/import public key */ + pklen = sizeof(pk_buf); + DO(ed448_export(pk_buf, &pklen, PK_PUBLIC, &key)); + DO(ed448_import_raw(pk_buf, pklen, PK_PUBLIC, &pubkey)); + + /* verify */ + stat = 0; + DO(ed448_verify(msg, sizeof(msg), sig, siglen, &stat, &pubkey)); + ENSURE(stat == 1); + + /* corrupted sig must fail */ + sig[0] ^= 1; + stat = 1; + DO(ed448_verify(msg, sizeof(msg), sig, siglen, &stat, &pubkey)); + ENSURE(stat == 0); + + /* sign with context */ + siglen = sizeof(sig); + DO(ed448ctx_sign(msg, sizeof(msg), sig, &siglen, + (const unsigned char *)"ctx", 3, &key)); + stat = 0; + DO(ed448ctx_verify(msg, sizeof(msg), sig, siglen, + (const unsigned char *)"ctx", 3, &stat, &pubkey)); + ENSURE(stat == 1); + + /* wrong context must fail */ + stat = 1; + DO(ed448ctx_verify(msg, sizeof(msg), sig, siglen, + (const unsigned char *)"bad", 3, &stat, &pubkey)); + ENSURE(stat == 0); + + return CRYPT_OK; +} + +/* RFC 8032 Section 5.2.6 - Ed448 (pure, no context) */ +static int s_rfc_8032_5_2_6_pure_test(void) +{ + const rfc_8032_t vectors[] = { + { + /* 1: empty message */ + "6c82a562cb808d10d632be89c8513ebf6c929f34ddfa8c9f63c9960ef6e348a3" + "528c8a3fcc2f044e39a3fc5b94492f8f032e7549a20098f95b", + "5fd7449b59b461fd2ce787ec616ad46a1da1342485a70e1f8a0ea75d80e96778" + "edf124769b46c7061bd6783df1e50f6cd1fa1abeafe8256180", + "", + "533a37f6bbe457251f023c0d88f976ae2dfb504a843e34d2074fd823d41a591f" + "2b233f034f628281f2fd7a22ddd47d7828c59bd0a21bfd3980ff0d2028d4b18a" + "9df63e006c5d1c2d345b925d8dc00b4104852db99ac5c7cdda8530a113a0f4db" + "b61149f05a7363268c71d95808ff2e652600", + NULL + }, + { + /* 2: 1 byte message */ + "c4eab05d357007c632f3dbb48489924d552b08fe0c353a0d4a1f00acda2c463a" + "fbea67c5e8d2877c5e3bc397a659949ef8021e954e0a12274e", + "43ba28f430cdff456ae531545f7ecd0ac834a55d9358c0372bfa0c6c6798c086" + "6aea01eb00742802b8438ea4cb82169c235160627b4c3a9480", + "03", + "26b8f91727bd62897af15e41eb43c377efb9c610d48f2335cb0bd0087810f435" + "2541b143c4b981b7e18f62de8ccdf633fc1bf037ab7cd779805e0dbcc0aae1cb" + "cee1afb2e027df36bc04dcecbf154336c19f0af7e0a6472905e799f1953d2a0f" + "f3348ab21aa4adafd1d234441cf807c03a00", + NULL + }, + { + /* 4: 11 byte message */ + "cd23d24f714274e744343237b93290f511f6425f98e64459ff203e8985083ffd" + "f60500553abc0e05cd02184bdb89c4ccd67e187951267eb328", + "dcea9e78f35a1bf3499a831b10b86c90aac01cd84b67a0109b55a36e9328b1e3" + "65fce161d71ce7131a543ea4cb5f7e9f1d8b00696447001400", + "0c3e544074ec63b0265e0c", + "1f0a8888ce25e8d458a21130879b840a9089d999aaba039eaf3e3afa090a09d3" + "89dba82c4ff2ae8ac5cdfb7c55e94d5d961a29fe0109941e00b8dbdeea6d3b05" + "1068df7254c0cdc129cbe62db2dc957dbb47b51fd3f213fb8698f064774250a5" + "028961c9bf8ffd973fe5d5c206492b140e00", + NULL + }, + { + /* 5: 12 byte message */ + "258cdd4ada32ed9c9ff54e63756ae582fb8fab2ac721f2c8e676a72768513d93" + "9f63dddb55609133f29adf86ec9929dccb52c1c5fd2ff7e21b", + "3ba16da0c6f2cc1f30187740756f5e798d6bc5fc015d7c63cc9510ee3fd44adc" + "24d8e968b6e46e6f94d19b945361726bd75e149ef09817f580", + "64a65f3cdedcdd66811e2915", + "7eeeab7c4e50fb799b418ee5e3197ff6bf15d43a14c34389b59dd1a7b1b85b4a" + "e90438aca634bea45e3a2695f1270f07fdcdf7c62b8efeaf00b45c2c96ba457e" + "b1a8bf075a3db28e5c24f6b923ed4ad747c3c9e03c7079efb87cb110d3a99861" + "e72003cbae6d6b8b827e4e6c143064ff3c00", + NULL + }, + { + /* 6: 13 byte message */ + "7ef4e84544236752fbb56b8f31a23a10e42814f5f55ca037cdcc11c64c9a3b29" + "49c1bb60700314611732a6c2fea98eebc0266a11a93970100e", + "b3da079b0aa493a5772029f0467baebee5a8112d9d3a22532361da294f7bb381" + "5c5dc59e176b4d9f381ca0938e13c6c07b174be65dfa578e80", + "64a65f3cdedcdd66811e2915e7", + "6a12066f55331b6c22acd5d5bfc5d71228fbda80ae8dec26bdd306743c5027cb" + "4890810c162c027468675ecf645a83176c0d7323a2ccde2d80efe5a1268e8aca" + "1d6fbc194d3f77c44986eb4ab4177919ad8bec33eb47bbb5fc6e28196fd1caf5" + "6b4e7e0ba5519234d047155ac727a1053100", + NULL + }, + { + /* 7: 64 byte message */ + "d65df341ad13e008567688baedda8e9dcdc17dc024974ea5b4227b6530e339bf" + "f21f99e68ca6968f3cca6dfe0fb9f4fab4fa135d5542ea3f01", + "df9705f58edbab802c7f8363cfe5560ab1c6132c20a9f1dd163483a26f8ac53a" + "39d6808bf4a1dfbd261b099bb03b3fb50906cb28bd8a081f00", + "bd0f6a3747cd561bdddf4640a332461a4a30a12a434cd0bf40d766d9c6d458e5" + "512204a30c17d1f50b5079631f64eb3112182da3005835461113718d1a5ef944", + "554bc2480860b49eab8532d2a533b7d578ef473eeb58c98bb2d0e1ce488a98b1" + "8dfde9b9b90775e67f47d4a1c3482058efc9f40d2ca033a0801b63d45b3b722e" + "f552bad3b4ccb667da350192b61c508cf7b6b5adadc2c8d9a446ef003fb05cba" + "5f30e88e36ec2703b349ca229c2670833900", + NULL + }, + { + /* 8: 256 byte message */ + "2ec5fe3c17045abdb136a5e6a913e32ab75ae68b53d2fc149b77e504132d3756" + "9b7e766ba74a19bd6162343a21c8590aa9cebca9014c636df5", + "79756f014dcfe2079f5dd9e718be4171e2ef2486a08f25186f6bff43a9936b9b" + "fe12402b08ae65798a3d81e22e9ec80e7690862ef3d4ed3a00", + "15777532b0bdd0d1389f636c5f6b9ba734c90af572877e2d272dd078aa1e567c" + "fa80e12928bb542330e8409f3174504107ecd5efac61ae7504dabe2a602ede89" + "e5cca6257a7c77e27a702b3ae39fc769fc54f2395ae6a1178cab4738e543072f" + "c1c177fe71e92e25bf03e4ecb72f47b64d0465aaea4c7fad372536c8ba516a60" + "39c3c2a39f0e4d832be432dfa9a706a6e5c7e19f397964ca4258002f7c0541b5" + "90316dbc5622b6b2a6fe7a4abffd96105eca76ea7b98816af0748c10df048ce0" + "12d901015a51f189f3888145c03650aa23ce894c3bd889e030d565071c59f409" + "a9981b51878fd6fc110624dcbcde0bf7a69ccce38fabdf86f3bef6044819de11", + "c650ddbb0601c19ca11439e1640dd931f43c518ea5bea70d3dcde5f4191fe53f" + "00cf966546b72bcc7d58be2b9badef28743954e3a44a23f880e8d4f1cfce2d7a" + "61452d26da05896f0a50da66a239a8a188b6d825b3305ad77b73fbac0836ecc6" + "0987fd08527c1a8e80d5823e65cafe2a3d00", + NULL + }, + { + /* 9: 1023 byte message */ + "872d093780f5d3730df7c212664b37b8a0f24f56810daa8382cd4fa3f77634ec" + "44dc54f1c2ed9bea86fafb7632d8be199ea165f5ad55dd9ce8", + "a81b2e8a70a5ac94ffdbcc9badfc3feb0801f258578bb114ad44ece1ec0e799d" + "a08effb81c5d685c0c56f64eecaef8cdf11cc38737838cf400", + "6ddf802e1aae4986935f7f981ba3f0351d6273c0a0c22c9c0e8339168e675412" + "a3debfaf435ed651558007db4384b650fcc07e3b586a27a4f7a00ac8a6fec2cd" + "86ae4bf1570c41e6a40c931db27b2faa15a8cedd52cff7362c4e6e23daec0fbc" + "3a79b6806e316efcc7b68119bf46bc76a26067a53f296dafdbdc11c77f7777e9" + "72660cf4b6a9b369a6665f02e0cc9b6edfad136b4fabe723d2813db3136cfde9" + "b6d044322fee2947952e031b73ab5c603349b307bdc27bc6cb8b8bbd7bd32321" + "9b8033a581b59eadebb09b3c4f3d2277d4f0343624acc817804728b25ab79717" + "2b4c5c21a22f9c7839d64300232eb66e53f31c723fa37fe387c7d3e50bdf9813" + "a30e5bb12cf4cd930c40cfb4e1fc622592a49588794494d56d24ea4b40c89fc0" + "596cc9ebb961c8cb10adde976a5d602b1c3f85b9b9a001ed3c6a4d3b1437f520" + "96cd1956d042a597d561a596ecd3d1735a8d570ea0ec27225a2c4aaff26306d1" + "526c1af3ca6d9cf5a2c98f47e1c46db9a33234cfd4d81f2c98538a09ebe76998" + "d0d8fd25997c7d255c6d66ece6fa56f11144950f027795e653008f4bd7ca2dee" + "85d8e90f3dc315130ce2a00375a318c7c3d97be2c8ce5b6db41a6254ff264fa6" + "155baee3b0773c0f497c573f19bb4f4240281f0b1f4f7be857a4e59d416c06b4" + "c50fa09e1810ddc6b1467baeac5a3668d11b6ecaa901440016f389f80acc4db9" + "77025e7f5924388c7e340a732e554440e76570f8dd71b7d640b3450d1fd5f041" + "0a18f9a3494f707c717b79b4bf75c98400b096b21653b5d217cf3565c9597456" + "f70703497a078763829bc01bb1cbc8fa04eadc9a6e3f6699587a9e75c94e5bab" + "0036e0b2e711392cff0047d0d6b05bd2a588bc109718954259f1d86678a579a3" + "120f19cfb2963f177aeb70f2d4844826262e51b80271272068ef5b3856fa8535" + "aa2a88b2d41f2a0e2fda7624c2850272ac4a2f561f8f2f7a318bfd5caf969614" + "9e4ac824ad3460538fdc25421beec2cc6818162d06bbed0c40a387192349db67" + "a118bada6cd5ab0140ee273204f628aad1c135f770279a651e24d8c14d75a605" + "9d76b96a6fd857def5e0b354b27ab937a5815d16b5fae407ff18222c6d1ed263" + "be68c95f32d908bd895cd76207ae726487567f9a67dad79abec316f683b17f2d" + "02bf07e0ac8b5bc6162cf94697b3c27cd1fea49b27f23ba2901871962506520c" + "392da8b6ad0d99f7013fbc06c2c17a569500c8a7696481c1cd33e9b14e40b82e" + "79a5f5db82571ba97bae3ad3e0479515bb0e2b0f3bfcd1fd33034efc6245eddd" + "7ee2086ddae2600d8ca73e214e8c2b0bdb2b047c6a464a562ed77b73d2d841c4" + "b34973551257713b753632efba348169abc90a68f42611a40126d7cb21b58695" + "568186f7e569d2ff0f9e745d0487dd2eb997cafc5abf9dd102e62ff66cba87", + "e301345a41a39a4d72fff8df69c98075a0cc082b802fc9b2b6bc503f926b65bd" + "df7f4c8f1cb49f6396afc8a70abe6d8aef0db478d4c6b2970076c6a0484fe76d" + "76b3a97625d79f1ce240e7c576750d295528286f719b413de9ada3e8eb78ed57" + "3603ce30d8bb761785dc30dbc320869e1a00", + NULL + } + }; + unsigned int n; + unsigned long mlen, slen, plen, siglen, buflen; + unsigned char msg[1024], sec[57], pub[57], sig[114], buf[114]; + curve448_key key, key2; + int ret; + const int should = 1; + + for (n = 0; n < sizeof(vectors)/sizeof(vectors[0]); ++n) { + slen = sizeof(sec); + DO(base16_decode(vectors[n].secret_key, XSTRLEN(vectors[n].secret_key), sec, &slen)); + plen = sizeof(pub); + DO(base16_decode(vectors[n].public_key, XSTRLEN(vectors[n].public_key), pub, &plen)); + if (XSTRLEN(vectors[n].message) > 0) { + mlen = sizeof(msg); + DO(base16_decode(vectors[n].message, XSTRLEN(vectors[n].message), msg, &mlen)); + } else { + mlen = 0; + } + siglen = sizeof(sig); + DO(base16_decode(vectors[n].signature, XSTRLEN(vectors[n].signature), sig, &siglen)); + + /* sign with private key and compare */ + DO(ed448_import_raw(sec, slen, PK_PRIVATE, &key)); + buflen = sizeof(buf); + DO(ed448_sign(msg, mlen, buf, &buflen, &key)); + COMPARE_TESTVECTOR(buf, buflen, sig, siglen, "Ed448 RFC8032 5.2.6 - sign", n); + + /* verify with private key */ + DO(ed448_verify(msg, mlen, sig, siglen, &ret, &key)); + COMPARE_TESTVECTOR(&ret, sizeof(ret), &should, sizeof(should), "Ed448 RFC8032 5.2.6 - verify w/ privkey", n); + + /* verify with public key only */ + DO(ed448_import_raw(pub, plen, PK_PUBLIC, &key2)); + DO(ed448_verify(msg, mlen, sig, siglen, &ret, &key2)); + COMPARE_TESTVECTOR(&ret, sizeof(ret), &should, sizeof(should), "Ed448 RFC8032 5.2.6 - verify w/ pubkey", n); + + zeromem(&key, sizeof(key)); + zeromem(&key2, sizeof(key2)); + } + return CRYPT_OK; +} + +/* RFC 8032 Section 5.2.6 - Ed448 with context (vector 3) */ +static int s_rfc_8032_5_2_6_ctx_test(void) +{ + const rfc_8032_t vectors[] = { + { + /* 3: 1 byte message, context "foo" */ + "c4eab05d357007c632f3dbb48489924d552b08fe0c353a0d4a1f00acda2c463a" + "fbea67c5e8d2877c5e3bc397a659949ef8021e954e0a12274e", + "43ba28f430cdff456ae531545f7ecd0ac834a55d9358c0372bfa0c6c6798c086" + "6aea01eb00742802b8438ea4cb82169c235160627b4c3a9480", + "03", + "d4f8f6131770dd46f40867d6fd5d5055de43541f8c5e35abbcd001b32a89f7d2" + "151f7647f11d8ca2ae279fb842d607217fce6e042f6815ea000c85741de5c8da" + "1144a6a1aba7f96de42505d7a7298524fda538fccbbb754f578c1cad10d54d0d" + "5428407e85dcbc98a49155c13764e66c3c00", + "666f6f" + } + }; + unsigned int n; + unsigned long mlen, slen, plen, siglen, buflen, ctxlen; + unsigned char msg[1024], sec[57], pub[57], sig[114], buf[114], ctx[64]; + curve448_key key, key2; + int ret; + const int should = 1; + + for (n = 0; n < sizeof(vectors)/sizeof(vectors[0]); ++n) { + slen = sizeof(sec); + DO(base16_decode(vectors[n].secret_key, XSTRLEN(vectors[n].secret_key), sec, &slen)); + plen = sizeof(pub); + DO(base16_decode(vectors[n].public_key, XSTRLEN(vectors[n].public_key), pub, &plen)); + mlen = sizeof(msg); + DO(base16_decode(vectors[n].message, XSTRLEN(vectors[n].message), msg, &mlen)); + siglen = sizeof(sig); + DO(base16_decode(vectors[n].signature, XSTRLEN(vectors[n].signature), sig, &siglen)); + ctxlen = sizeof(ctx); + DO(base16_decode(vectors[n].context, XSTRLEN(vectors[n].context), ctx, &ctxlen)); + + /* sign with context and compare */ + DO(ed448_import_raw(sec, slen, PK_PRIVATE, &key)); + buflen = sizeof(buf); + DO(ed448ctx_sign(msg, mlen, buf, &buflen, ctx, ctxlen, &key)); + COMPARE_TESTVECTOR(buf, buflen, sig, siglen, "Ed448ctx RFC8032 5.2.6 - sign", n); + + /* verify with private key */ + DO(ed448ctx_verify(msg, mlen, sig, siglen, ctx, ctxlen, &ret, &key)); + COMPARE_TESTVECTOR(&ret, sizeof(ret), &should, sizeof(should), "Ed448ctx RFC8032 5.2.6 - verify w/ privkey", n); + + /* verify with public key only */ + DO(ed448_import_raw(pub, plen, PK_PUBLIC, &key2)); + DO(ed448ctx_verify(msg, mlen, sig, siglen, ctx, ctxlen, &ret, &key2)); + COMPARE_TESTVECTOR(&ret, sizeof(ret), &should, sizeof(should), "Ed448ctx RFC8032 5.2.6 - verify w/ pubkey", n); + + zeromem(&key, sizeof(key)); + zeromem(&key2, sizeof(key2)); + } + return CRYPT_OK; +} + +/* RFC 8032 Section 5.2.6 - Ed448ph (prehash) */ +static int s_rfc_8032_5_2_6_ph_test(void) +{ + const rfc_8032_t vectors[] = { + { + /* Ed448ph without context */ + "833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42" + "ef7822e0d5104127dc05d6dbefde69e3ab2cec7c867c6e2c49", + "259b71c19f83ef77a7abd26524cbdb3161b590a48f7d17de3ee0ba9c52beb743" + "c09428a131d6b1b57303d90d8132c276d5ed3d5d01c0f53880", + "616263", + "822f6901f7480f3d5f562c592994d9693602875614483256505600bbc281ae38" + "1f54d6bce2ea911574932f52a4e6cadd78769375ec3ffd1b801a0d9b3f4030cd" + "433964b6457ea39476511214f97469b57dd32dbc560a9a94d00bff07620464a3" + "ad203df7dc7ce360c3cd3696d9d9fab90f00", + NULL + }, + { + /* Ed448ph with context "foo" */ + "833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42" + "ef7822e0d5104127dc05d6dbefde69e3ab2cec7c867c6e2c49", + "259b71c19f83ef77a7abd26524cbdb3161b590a48f7d17de3ee0ba9c52beb743" + "c09428a131d6b1b57303d90d8132c276d5ed3d5d01c0f53880", + "616263", + "c32299d46ec8ff02b54540982814dce9a05812f81962b649d528095916a2aa48" + "1065b1580423ef927ecf0af5888f90da0f6a9a85ad5dc3f280d91224ba9911a3" + "653d00e484e2ce232521481c8658df304bb7745a73514cdb9bf3e15784ab7128" + "4f8d0704a608c54a6b62d97beb511d132100", + "666f6f" + } + }; + unsigned int n; + unsigned long mlen, slen, plen, siglen, buflen, ctxlen; + unsigned char msg[1024], sec[57], pub[57], sig[114], buf[114], ctx[64]; + curve448_key key; + int ret; + const int should = 1; + + for (n = 0; n < sizeof(vectors)/sizeof(vectors[0]); ++n) { + slen = sizeof(sec); + DO(base16_decode(vectors[n].secret_key, XSTRLEN(vectors[n].secret_key), sec, &slen)); + plen = sizeof(pub); + DO(base16_decode(vectors[n].public_key, XSTRLEN(vectors[n].public_key), pub, &plen)); + mlen = sizeof(msg); + DO(base16_decode(vectors[n].message, XSTRLEN(vectors[n].message), msg, &mlen)); + siglen = sizeof(sig); + DO(base16_decode(vectors[n].signature, XSTRLEN(vectors[n].signature), sig, &siglen)); + + if (vectors[n].context != NULL) { + ctxlen = sizeof(ctx); + DO(base16_decode(vectors[n].context, XSTRLEN(vectors[n].context), ctx, &ctxlen)); + } else { + ctxlen = 0; + } + + DO(ed448_import_raw(sec, slen, PK_PRIVATE, &key)); + buflen = sizeof(buf); + DO(ed448ph_sign(msg, mlen, buf, &buflen, ctx, ctxlen, &key)); + COMPARE_TESTVECTOR(buf, buflen, sig, siglen, "Ed448ph RFC8032 5.2.6 - sign", n); + + DO(ed448ph_verify(msg, mlen, sig, siglen, ctx, ctxlen, &ret, &key)); + COMPARE_TESTVECTOR(&ret, sizeof(ret), &should, sizeof(should), "Ed448ph RFC8032 5.2.6 - verify", n); + + zeromem(&key, sizeof(key)); + } + return CRYPT_OK; +} + +/* Export/import round-trip for Ed448 (requires MPI for DER/PKCS8) */ +static int s_ed448_compat_test(void) +{ + curve448_key priv, pub, imported; + unsigned char buf[1024]; + unsigned long buflen; + int prng_idx = find_prng("yarrow"); + + XMEMSET(&priv, 0, sizeof(priv)); + XMEMSET(&pub, 0, sizeof(pub)); + XMEMSET(&imported, 0, sizeof(imported)); + + DO(ed448_make_key(&yarrow_prng, prng_idx, &priv)); + + /* private PKCS#8 export -> import */ + buflen = sizeof(buf); + DO(ed448_export(buf, &buflen, PK_PRIVATE | PK_STD, &priv)); + DO(ed448_import_pkcs8(buf, buflen, NULL, &imported)); + COMPARE_TESTVECTOR(&priv, sizeof(priv), &imported, sizeof(imported), "Ed448 priv pkcs8 round-trip", __LINE__); + XMEMSET(&imported, 0, sizeof(imported)); + + /* public raw export -> import */ + buflen = sizeof(buf); + DO(ed448_export(buf, &buflen, PK_PUBLIC, &priv)); + DO(ed448_import_raw(buf, buflen, PK_PUBLIC, &pub)); + + /* public DER export -> import */ + buflen = sizeof(buf); + DO(ed448_export(buf, &buflen, PK_PUBLIC | PK_STD, &priv)); + DO(ed448_import(buf, buflen, &imported)); + COMPARE_TESTVECTOR(&pub, sizeof(pub), &imported, sizeof(imported), "Ed448 pub DER round-trip", __LINE__); + + return CRYPT_OK; +} + +int ed448_test(void) +{ + DO(s_ed448_keygen_sign_verify()); + DO(s_rfc_8032_5_2_6_pure_test()); + DO(s_rfc_8032_5_2_6_ctx_test()); + DO(s_rfc_8032_5_2_6_ph_test()); + if (ltc_mp.name != NULL) { + DO(s_ed448_compat_test()); + } + return CRYPT_OK; +} + +#else + +int ed448_test(void) +{ + return CRYPT_NOP; +} + +#endif diff --git a/tests/pem/openssl_ed448_pk.pem b/tests/pem/openssl_ed448_pk.pem new file mode 100644 index 000000000..c31b24012 --- /dev/null +++ b/tests/pem/openssl_ed448_pk.pem @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MEMwBQYDK2VxAzoAZkavwN2Jb7JwbSR79T0JZUDV1VOCgm1AlFdsW9dV+O57w7E2 ++y9O4Ejkhya86byArbdRsOk7ovqA +-----END PUBLIC KEY----- diff --git a/tests/pem/openssl_ed448_sk.pem b/tests/pem/openssl_ed448_sk.pem new file mode 100644 index 000000000..7d6ee84c5 --- /dev/null +++ b/tests/pem/openssl_ed448_sk.pem @@ -0,0 +1,4 @@ +-----BEGIN PRIVATE KEY----- +MEcCAQAwBQYDK2VxBDsEOe/w/F8aSpwuG9cBnsAkhloXWhPJEVhCxzWdXebe27Tu +f1R24A1BP7pjIihNdlXTsxv+sFuKWMR5ng== +-----END PRIVATE KEY----- diff --git a/tests/pem/openssl_ed448_x509.pem b/tests/pem/openssl_ed448_x509.pem new file mode 100644 index 000000000..33de3f859 --- /dev/null +++ b/tests/pem/openssl_ed448_x509.pem @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBhDCCAQSgAwIBAgIUA30c9xlNmn6aa6MeW/sattDShU8wBQYDK2VxMBExDzAN +BgNVBAMMBkNyeXB0WDAgFw0yNjA0MTQxMjAwMDNaGA8yMzAwMDEyNzEyMDAwM1ow +ETEPMA0GA1UEAwwGQ3J5cHRYMEMwBQYDK2VxAzoAZkavwN2Jb7JwbSR79T0JZUDV +1VOCgm1AlFdsW9dV+O57w7E2+y9O4Ejkhya86byArbdRsOk7ovqAo1MwUTAdBgNV +HQ4EFgQUrowTD38+WWdAZuwA1WLZu/7xsGIwHwYDVR0jBBgwFoAUrowTD38+WWdA +ZuwA1WLZu/7xsGIwDwYDVR0TAQH/BAUwAwEB/zAFBgMrZXEDcwDD6A7cE22i0417 +jNIVCCMPxY09OkC5yFLXV3MilpvyWyUQFz/J4XWxpnRyWYaap6EeZLzowTtPFgCH +uPj4rCSVFQkBRaMkBv1zsFUXz7mAg01aNZFu7aDO+n+ctpl/8+BLw1+lQm4BiOGw +47xwywstBgA= +-----END CERTIFICATE----- diff --git a/tests/pem/openssl_x448_pk.pem b/tests/pem/openssl_x448_pk.pem new file mode 100644 index 000000000..3df5c9529 --- /dev/null +++ b/tests/pem/openssl_x448_pk.pem @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MEIwBQYDK2VvAzkApScinIzW1CGVm4zV1j6fw7GsbeHGFy8QY7JLd8tXGiaXzr+/ +OwJqRZ9X1QhfEYso6no4d1fP9N0= +-----END PUBLIC KEY----- diff --git a/tests/pem/openssl_x448_sk.pem b/tests/pem/openssl_x448_sk.pem new file mode 100644 index 000000000..f6c4fe672 --- /dev/null +++ b/tests/pem/openssl_x448_sk.pem @@ -0,0 +1,4 @@ +-----BEGIN PRIVATE KEY----- +MEYCAQAwBQYDK2VvBDoEOPBQlJ/c83DaT71qCGj25lKh7rr3q6pRBIqHCjExTXD0 +vM82EulDzmjR9WU9zaS00j2LU4XXKpvV +-----END PRIVATE KEY----- diff --git a/tests/pem/pkcs/openssl_ed448_sk.pk8 b/tests/pem/pkcs/openssl_ed448_sk.pk8 new file mode 100644 index 000000000..7d6ee84c5 --- /dev/null +++ b/tests/pem/pkcs/openssl_ed448_sk.pk8 @@ -0,0 +1,4 @@ +-----BEGIN PRIVATE KEY----- +MEcCAQAwBQYDK2VxBDsEOe/w/F8aSpwuG9cBnsAkhloXWhPJEVhCxzWdXebe27Tu +f1R24A1BP7pjIihNdlXTsxv+sFuKWMR5ng== +-----END PRIVATE KEY----- diff --git a/tests/pem/pkcs/openssl_ed448_sk_pbes1.pk8 b/tests/pem/pkcs/openssl_ed448_sk_pbes1.pk8 new file mode 100644 index 000000000..c7574555e --- /dev/null +++ b/tests/pem/pkcs/openssl_ed448_sk_pbes1.pk8 @@ -0,0 +1,5 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MHAwHAYKKoZIhvcNAQwBAzAOBAjJ1wdj8L5FKgICCAAEUFrnEEVG1fA7XOx15h9T +H0TLtbCm/UYnFLh8GDGVjM+q/OYrfnTv5a6P5G/Ze7tMVsSCpgZBlSL9/Q6NJ37P +2X/Jd5vwyJSRzm+qeugIci5d +-----END ENCRYPTED PRIVATE KEY----- diff --git a/tests/pem/pkcs/openssl_ed448_sk_pbes2.pk8 b/tests/pem/pkcs/openssl_ed448_sk_pbes2.pk8 new file mode 100644 index 000000000..27f9ef3a2 --- /dev/null +++ b/tests/pem/pkcs/openssl_ed448_sk_pbes2.pk8 @@ -0,0 +1,6 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIGrMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAhhZGWQAxrkOAICCAAw +DAYIKoZIhvcNAgsFADAdBglghkgBZQMEASoEEBWa/IgJqnE1O2ibVuIkqVwEUIEG +2Wj76gtauZF1wEIts6PN/of37CQjdI8qBT95f0NemnXA9KWhqdT7zCVoZykdwTtr +QveA4USScJ1NjGRCgLcoh+RW0/7pQLI1s06UIptb +-----END ENCRYPTED PRIVATE KEY----- diff --git a/tests/pem/pkcs/openssl_x448_sk.pk8 b/tests/pem/pkcs/openssl_x448_sk.pk8 new file mode 100644 index 000000000..f6c4fe672 --- /dev/null +++ b/tests/pem/pkcs/openssl_x448_sk.pk8 @@ -0,0 +1,4 @@ +-----BEGIN PRIVATE KEY----- +MEYCAQAwBQYDK2VvBDoEOPBQlJ/c83DaT71qCGj25lKh7rr3q6pRBIqHCjExTXD0 +vM82EulDzmjR9WU9zaS00j2LU4XXKpvV +-----END PRIVATE KEY----- diff --git a/tests/pem/pkcs/openssl_x448_sk_pbes1.pk8 b/tests/pem/pkcs/openssl_x448_sk_pbes1.pk8 new file mode 100644 index 000000000..b0c6a9289 --- /dev/null +++ b/tests/pem/pkcs/openssl_x448_sk_pbes1.pk8 @@ -0,0 +1,5 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MHAwHAYKKoZIhvcNAQwBAzAOBAgZ7uqzZ0UIPAICCAAEUIoyzd9lPvjne3Bg7lk9 +PIHPqflbG9jCtk9192bVNj8+QHA1+XMEB9Ay3/Ljr+hQwiTS1tVELbzqyOoNYxpF +H91YfGjRZDrh2+k/K5MtLFUh +-----END ENCRYPTED PRIVATE KEY----- diff --git a/tests/pem/pkcs/openssl_x448_sk_pbes2.pk8 b/tests/pem/pkcs/openssl_x448_sk_pbes2.pk8 new file mode 100644 index 000000000..14c70e5c5 --- /dev/null +++ b/tests/pem/pkcs/openssl_x448_sk_pbes2.pk8 @@ -0,0 +1,6 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIGrMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAhzD7h+saEo1gICCAAw +DAYIKoZIhvcNAgsFADAdBglghkgBZQMEASoEEFcOyy6Y7TG3Z8wklZO14U8EUKKD +vdlVxsVBpoMpRTbYKen6rbSnAhysBG24K5AVyMAcsTXL1uzGHTIrV3xSjW4W8NV2 +N1MjPECNVcxH5HvsCOZ0lqYrj4IdSk4hL1+9Jrzz +-----END ENCRYPTED PRIVATE KEY----- diff --git a/tests/pem_test.c b/tests/pem_test.c index 4dc14cbd7..652a341b8 100644 --- a/tests/pem_test.c +++ b/tests/pem_test.c @@ -86,6 +86,8 @@ static int s_key_cmp(ltc_pka_key *key) case LTC_PKA_ED25519: case LTC_PKA_X25519: case LTC_PKA_DH: + case LTC_PKA_X448: + case LTC_PKA_ED448: return CRYPT_OK; default: return CRYPT_INVALID_ARG; diff --git a/tests/test.c b/tests/test.c index a26604ec4..4db3cd3fa 100644 --- a/tests/test.c +++ b/tests/test.c @@ -34,6 +34,8 @@ static const test_function test_functions[] = LTC_TEST_FN(dsa_test), LTC_TEST_FN(ed25519_test), LTC_TEST_FN(x25519_test), + LTC_TEST_FN(ed448_test), + LTC_TEST_FN(x448_test), LTC_TEST_FN(file_test), LTC_TEST_FN(multi_test), LTC_TEST_FN(pem_test), diff --git a/tests/tomcrypt_test.h b/tests/tomcrypt_test.h index 747ede567..78e9e595f 100644 --- a/tests/tomcrypt_test.h +++ b/tests/tomcrypt_test.h @@ -40,7 +40,9 @@ int prng_test(void); int mpi_test(void); int padding_test(void); int x25519_test(void); +int x448_test(void); int ed25519_test(void); +int ed448_test(void); int ssh_test(void); int argon2_test(void); int bcrypt_test(void); diff --git a/tests/x448_test.c b/tests/x448_test.c new file mode 100644 index 000000000..aae4e0eb4 --- /dev/null +++ b/tests/x448_test.c @@ -0,0 +1,231 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ +#include "tomcrypt_test.h" + +#ifdef LTC_CURVE448 + +/* RFC 7748 Section 5.2 - X448 scalar multiplication test vectors */ +static int s_x448_rfc7748_scalarmult_test(void) +{ + /* First test vector */ + static const unsigned char k1[56] = { + 0x3d, 0x26, 0x2f, 0xdd, 0xf9, 0xec, 0x8e, 0x88, + 0x49, 0x52, 0x66, 0xfe, 0xa1, 0x9a, 0x34, 0xd2, + 0x88, 0x82, 0xac, 0xef, 0x04, 0x51, 0x04, 0xd0, + 0xd1, 0xaa, 0xe1, 0x21, 0x70, 0x0a, 0x77, 0x9c, + 0x98, 0x4c, 0x24, 0xf8, 0xcd, 0xd7, 0x8f, 0xbf, + 0xf4, 0x49, 0x43, 0xeb, 0xa3, 0x68, 0xf5, 0x4b, + 0x29, 0x25, 0x9a, 0x4f, 0x1c, 0x60, 0x0a, 0xd3 + }; + static const unsigned char u1[56] = { + 0x06, 0xfc, 0xe6, 0x40, 0xfa, 0x34, 0x87, 0xbf, + 0xda, 0x5f, 0x6c, 0xf2, 0xd5, 0x26, 0x3f, 0x8a, + 0xad, 0x88, 0x33, 0x4c, 0xbd, 0x07, 0x43, 0x7f, + 0x02, 0x0f, 0x08, 0xf9, 0x81, 0x4d, 0xc0, 0x31, + 0xdd, 0xbd, 0xc3, 0x8c, 0x19, 0xc6, 0xda, 0x25, + 0x83, 0xfa, 0x54, 0x29, 0xdb, 0x94, 0xad, 0xa1, + 0x8a, 0xa7, 0xa7, 0xfb, 0x4e, 0xf8, 0xa0, 0x86 + }; + static const unsigned char expected1[56] = { + 0xce, 0x3e, 0x4f, 0xf9, 0x5a, 0x60, 0xdc, 0x66, + 0x97, 0xda, 0x1d, 0xb1, 0xd8, 0x5e, 0x6a, 0xfb, + 0xdf, 0x79, 0xb5, 0x0a, 0x24, 0x12, 0xd7, 0x54, + 0x6d, 0x5f, 0x23, 0x9f, 0xe1, 0x4f, 0xba, 0xad, + 0xeb, 0x44, 0x5f, 0xc6, 0x6a, 0x01, 0xb0, 0x77, + 0x9d, 0x98, 0x22, 0x39, 0x61, 0x11, 0x1e, 0x21, + 0x76, 0x62, 0x82, 0xf7, 0x3d, 0xd9, 0x6b, 0x6f + }; + + /* Second test vector */ + static const unsigned char k2[56] = { + 0x20, 0x3d, 0x49, 0x44, 0x28, 0xb8, 0x39, 0x93, + 0x52, 0x66, 0x5d, 0xdc, 0xa4, 0x2f, 0x9d, 0xe8, + 0xfe, 0xf6, 0x00, 0x90, 0x8e, 0x0d, 0x46, 0x1c, + 0xb0, 0x21, 0xf8, 0xc5, 0x38, 0x34, 0x5d, 0xd7, + 0x7c, 0x3e, 0x48, 0x06, 0xe2, 0x5f, 0x46, 0xd3, + 0x31, 0x5c, 0x44, 0xe0, 0xa5, 0xb4, 0x37, 0x12, + 0x82, 0xdd, 0x2c, 0x8d, 0x5b, 0xe3, 0x09, 0x5b + }; + static const unsigned char u2[56] = { + 0x0f, 0xbc, 0xc2, 0xf9, 0x93, 0xcd, 0x56, 0xd3, + 0x30, 0x5b, 0x0b, 0x7d, 0x9e, 0x55, 0xd4, 0xc1, + 0xa8, 0xfb, 0x5d, 0xbb, 0x52, 0xf8, 0xe9, 0xa1, + 0xe9, 0xb6, 0x20, 0x1b, 0x16, 0x5d, 0x01, 0x58, + 0x94, 0xe5, 0x6c, 0x4d, 0x35, 0x70, 0xbe, 0xe5, + 0x2f, 0xe2, 0x05, 0xe2, 0x8a, 0x78, 0xb9, 0x1c, + 0xdf, 0xbd, 0xe7, 0x1c, 0xe8, 0xd1, 0x57, 0xdb + }; + static const unsigned char expected2[56] = { + 0xe4, 0x3a, 0x6e, 0x84, 0xc5, 0x41, 0x24, 0x19, + 0x69, 0xe1, 0xbc, 0x13, 0xaf, 0xae, 0xba, 0x34, + 0xe8, 0xe0, 0x91, 0x22, 0xaf, 0x0f, 0xaf, 0x6a, + 0x45, 0xf8, 0xd2, 0x51, 0x38, 0xb4, 0x80, 0x0f, + 0x07, 0xc8, 0x62, 0xd8, 0x04, 0xac, 0xaf, 0x18, + 0xd2, 0xc5, 0xf5, 0x02, 0x76, 0xcd, 0xf0, 0xbd, + 0x06, 0x8e, 0x95, 0x73, 0x73, 0xd3, 0x52, 0x97 + }; + + unsigned char out[56]; + + ec448_scalarmult_internal(out, k1, u1); + COMPARE_TESTVECTOR(out, 56, expected1, 56, "X448 RFC7748 5.2 #1", 0); + + ec448_scalarmult_internal(out, k2, u2); + COMPARE_TESTVECTOR(out, 56, expected2, 56, "X448 RFC7748 5.2 #2", 1); + + return CRYPT_OK; +} + +/* RFC 7748 Section 5.2 - X448 iterative scalar multiplication test + * + * Start with k = u = 5 (as 56-byte little-endian). + * Each iteration: out = X448(k, u), then u = old_k, k = out. + */ +static int s_x448_rfc7748_iter_test(void) +{ + /* Expected result after 1 iteration */ + static const unsigned char expected_1[56] = { + 0x3f, 0x48, 0x2c, 0x8a, 0x9f, 0x19, 0xb0, 0x1e, + 0x6c, 0x46, 0xee, 0x97, 0x11, 0xd9, 0xdc, 0x14, + 0xfd, 0x4b, 0xf6, 0x7a, 0xf3, 0x07, 0x65, 0xc2, + 0xae, 0x2b, 0x84, 0x6a, 0x4d, 0x23, 0xa8, 0xcd, + 0x0d, 0xb8, 0x97, 0x08, 0x62, 0x39, 0x49, 0x2c, + 0xaf, 0x35, 0x0b, 0x51, 0xf8, 0x33, 0x86, 0x8b, + 0x9b, 0xc2, 0xb3, 0xbc, 0xa9, 0xcf, 0x41, 0x13 + }; + + /* Expected result after 1000 iterations */ + static const unsigned char expected_1000[56] = { + 0xaa, 0x3b, 0x47, 0x49, 0xd5, 0x5b, 0x9d, 0xaf, + 0x1e, 0x5b, 0x00, 0x28, 0x88, 0x26, 0xc4, 0x67, + 0x27, 0x4c, 0xe3, 0xeb, 0xbd, 0xd5, 0xc1, 0x7b, + 0x97, 0x5e, 0x09, 0xd4, 0xaf, 0x6c, 0x67, 0xcf, + 0x10, 0xd0, 0x87, 0x20, 0x2d, 0xb8, 0x82, 0x86, + 0xe2, 0xb7, 0x9f, 0xce, 0xea, 0x3e, 0xc3, 0x53, + 0xef, 0x54, 0xfa, 0xa2, 0x6e, 0x21, 0x9f, 0x38 + }; + + unsigned char k[56], u[56], out[56], tmp[56]; + unsigned long i; + + /* k = u = 5 (little-endian) */ + XMEMSET(k, 0, 56); + XMEMSET(u, 0, 56); + k[0] = 5; + u[0] = 5; + + /* 1 iteration */ + ec448_scalarmult_internal(out, k, u); + XMEMCPY(u, k, 56); + XMEMCPY(k, out, 56); + COMPARE_TESTVECTOR(k, 56, expected_1, 56, "X448 RFC7748 iter=1", 0); + + /* Continue to 1000 iterations (we already did 1) */ + for (i = 1; i < 1000; i++) { + ec448_scalarmult_internal(out, k, u); + XMEMCPY(tmp, k, 56); + XMEMCPY(k, out, 56); + XMEMCPY(u, tmp, 56); + } + COMPARE_TESTVECTOR(k, 56, expected_1000, 56, "X448 RFC7748 iter=1000", 1); + + return CRYPT_OK; +} + +static int s_x448_keygen_dh_test(void) +{ + curve448_key alice, bob; + unsigned char ss_a[56], ss_b[56]; + unsigned long sslen; + int prng_idx; + + prng_idx = find_prng("yarrow"); + + /* Generate two key pairs */ + DO(x448_make_key(&yarrow_prng, prng_idx, &alice)); + DO(x448_make_key(&yarrow_prng, prng_idx, &bob)); + ENSURE(alice.type == PK_PRIVATE); + ENSURE(alice.pka == LTC_PKA_X448); + + /* DH: both sides compute the same shared secret */ + sslen = sizeof(ss_a); + DO(x448_shared_secret(&alice, &bob, ss_a, &sslen)); + ENSURE(sslen == 56); + + sslen = sizeof(ss_b); + DO(x448_shared_secret(&bob, &alice, ss_b, &sslen)); + ENSURE(sslen == 56); + + COMPARE_TESTVECTOR(ss_a, 56, ss_b, 56, "X448 DH shared secret", 0); + + /* Export/import raw round-trip */ + { + curve448_key imported; + unsigned char buf[56]; + unsigned long buflen = sizeof(buf); + + DO(x448_export(buf, &buflen, PK_PUBLIC, &alice)); + ENSURE(buflen == 56); + DO(x448_import_raw(buf, buflen, PK_PUBLIC, &imported)); + ENSURE(imported.type == PK_PUBLIC); + ENSURE(imported.pka == LTC_PKA_X448); + COMPARE_TESTVECTOR(imported.pub, 56, alice.pub, 56, "X448 import_raw pub", 0); + } + + return CRYPT_OK; +} + +/* Export/import round-trip (requires MPI for DER/PKCS8) */ +static int s_x448_compat_test(void) +{ + curve448_key priv, pub, imported; + unsigned char buf[1024]; + unsigned long buflen; + int prng_idx = find_prng("yarrow"); + + XMEMSET(&priv, 0, sizeof(priv)); + XMEMSET(&pub, 0, sizeof(pub)); + XMEMSET(&imported, 0, sizeof(imported)); + + DO(x448_make_key(&yarrow_prng, prng_idx, &priv)); + + /* private PKCS#8 export -> import */ + buflen = sizeof(buf); + DO(x448_export(buf, &buflen, PK_PRIVATE | PK_STD, &priv)); + DO(x448_import_pkcs8(buf, buflen, NULL, &imported)); + COMPARE_TESTVECTOR(&priv, sizeof(priv), &imported, sizeof(imported), "X448 priv pkcs8 round-trip", __LINE__); + XMEMSET(&imported, 0, sizeof(imported)); + + /* public raw export -> import */ + buflen = sizeof(buf); + DO(x448_export(buf, &buflen, PK_PUBLIC, &priv)); + DO(x448_import_raw(buf, buflen, PK_PUBLIC, &pub)); + + /* public DER export -> import */ + buflen = sizeof(buf); + DO(x448_export(buf, &buflen, PK_PUBLIC | PK_STD, &priv)); + DO(x448_import(buf, buflen, &imported)); + COMPARE_TESTVECTOR(&pub, sizeof(pub), &imported, sizeof(imported), "X448 pub DER round-trip", __LINE__); + + return CRYPT_OK; +} + +int x448_test(void) +{ + DO(s_x448_rfc7748_scalarmult_test()); + DO(s_x448_rfc7748_iter_test()); + DO(s_x448_keygen_dh_test()); + if (ltc_mp.name != NULL) { + DO(s_x448_compat_test()); + } + return CRYPT_OK; +} + +#else + +int x448_test(void) +{ + return CRYPT_NOP; +} + +#endif From 2c494d5111c76ec8a3f2499c617ab82ac445858a Mon Sep 17 00:00:00 2001 From: Karel Miko Date: Tue, 14 Apr 2026 16:30:45 +0200 Subject: [PATCH 2/2] Update makefiles --- libtomcrypt_VS2008.vcproj | 88 +++++++++++++++++++++++++++++++++++++++ makefile.mingw | 77 ++++++++++++++++++---------------- makefile.msvc | 77 ++++++++++++++++++---------------- makefile.unix | 77 ++++++++++++++++++---------------- makefile_include.mk | 77 ++++++++++++++++++---------------- sources.cmake | 19 +++++++++ tests/sources.cmake | 2 + 7 files changed, 277 insertions(+), 140 deletions(-) diff --git a/libtomcrypt_VS2008.vcproj b/libtomcrypt_VS2008.vcproj index 69a5e2d69..a8b8f1fb3 100644 --- a/libtomcrypt_VS2008.vcproj +++ b/libtomcrypt_VS2008.vcproj @@ -2443,6 +2443,26 @@ > + + + + + + + + + + @@ -2679,6 +2699,42 @@ > + + + + + + + + + + + + + + + + + + @@ -2811,6 +2867,38 @@ > + + + + + + + + + + + + + + + +