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/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 @@
>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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/sources.cmake b/tests/sources.cmake
index ae348a644..3fdde979c 100644
--- a/tests/sources.cmake
+++ b/tests/sources.cmake
@@ -12,6 +12,7 @@ dh_test.c
dsa_test.c
ecc_test.c
ed25519_test.c
+ed448_test.c
file_test.c
mac_test.c
misc_test.c
@@ -36,5 +37,6 @@ ssh_test.c
store_test.c
test.c
x25519_test.c
+x448_test.c
)
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