Ed448 + X448

This commit is contained in:
Karel Miko 2026-04-12 23:47:40 +02:00 committed by Steffen Jaeckel
parent c80285ba04
commit 076a11037a
43 changed files with 3057 additions and 11 deletions

View file

@ -5933,16 +5933,21 @@ ECCEncrypt ::= SEQUENCE {
\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).
@ -5961,7 +5966,7 @@ int x25519_make_key( prng_state *prng,
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,
@ -6056,7 +6061,7 @@ int ed25519_make_key( prng_state *prng,
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}
@ -6172,6 +6177,232 @@ These functions will verify the EdDSA signature in the array pointed to by \text
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}
@ -6477,6 +6708,8 @@ enum ltc_pka_id {
LTC_PKA_X25519,
LTC_PKA_ED25519,
LTC_PKA_DH,
LTC_PKA_X448,
LTC_PKA_ED448,
};
typedef struct {
@ -6485,6 +6718,10 @@ typedef struct {
curve25519_key x25519;
curve25519_key ed25519;
#endif
#ifdef LTC_CURVE448
curve448_key x448;
curve448_key ed448;
#endif
#ifdef LTC_MDH
dh_key dh;
#endif
@ -7766,14 +8003,14 @@ The identifiers in the PEM headers recognized are as follows:
\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
@ -8240,8 +8477,11 @@ static const char *pka_algos[] = {
"RSA",
"DSA",
"ECC",
"Curve25519",
"X25519",
"Ed25519",
"DH",
"X448",
"Ed448",
};
static int password_get(void **p, unsigned long *l, void *u)

View file

@ -454,6 +454,9 @@
/* Ed25519 & X25519 */
#define LTC_CURVE25519
/* Ed448 & X448 */
#define LTC_CURVE448
/* ECC */
#define LTC_MECC

View file

@ -41,6 +41,8 @@ enum ltc_pka_id {
LTC_PKA_ED25519,
LTC_PKA_DH,
LTC_PKA_RSA_PSS,
LTC_PKA_X448,
LTC_PKA_ED448,
LTC_PKA_NUM
};
@ -629,6 +631,80 @@ int x25519_shared_secret(const curve25519_key *private_key,
#endif /* LTC_CURVE25519 */
#ifdef LTC_CURVE448
typedef struct {
/** Type of key, PK_PRIVATE or PK_PUBLIC */
enum public_key_type type;
/** Algorithm: LTC_PKA_X448 or LTC_PKA_ED448 */
enum ltc_pka_id pka;
/** The private key (56 bytes for X448, 57 bytes for Ed448) */
unsigned char priv[57];
/** The public key (56 bytes for X448, 57 bytes for Ed448) */
unsigned char pub[57];
} curve448_key;
/** X448 Diffie-Hellman key exchange */
int x448_make_key(prng_state *prng, int wprng, curve448_key *key);
int x448_export( unsigned char *out, unsigned long *outlen,
int which,
const curve448_key *key);
int x448_import(const unsigned char *in, unsigned long inlen, curve448_key *key);
int x448_import_raw(const unsigned char *in, unsigned long inlen, int which, curve448_key *key);
int x448_import_x509(const unsigned char *in, unsigned long inlen, curve448_key *key);
int x448_import_pkcs8(const unsigned char *in, unsigned long inlen,
const password_ctx *pw_ctx,
curve448_key *key);
int x448_shared_secret(const curve448_key *private_key,
const curve448_key *public_key,
unsigned char *out, unsigned long *outlen);
/** Ed448 Signature API */
int ed448_make_key(prng_state *prng, int wprng, curve448_key *key);
int ed448_export( unsigned char *out, unsigned long *outlen,
int which,
const curve448_key *key);
int ed448_import(const unsigned char *in, unsigned long inlen, curve448_key *key);
int ed448_import_raw(const unsigned char *in, unsigned long inlen, int which, curve448_key *key);
int ed448_import_x509(const unsigned char *in, unsigned long inlen, curve448_key *key);
int ed448_import_pkcs8(const unsigned char *in, unsigned long inlen,
const password_ctx *pw_ctx,
curve448_key *key);
int ed448_sign(const unsigned char *msg, unsigned long msglen,
unsigned char *sig, unsigned long *siglen,
const curve448_key *private_key);
int ed448ctx_sign(const unsigned char *msg, unsigned long msglen,
unsigned char *sig, unsigned long *siglen,
const unsigned char *ctx, unsigned long ctxlen,
const curve448_key *private_key);
int ed448ph_sign(const unsigned char *msg, unsigned long msglen,
unsigned char *sig, unsigned long *siglen,
const unsigned char *ctx, unsigned long ctxlen,
const curve448_key *private_key);
int ed448_verify(const unsigned char *msg, unsigned long msglen,
const unsigned char *sig, unsigned long siglen,
int *stat,
const curve448_key *public_key);
int ed448ctx_verify(const unsigned char *msg, unsigned long msglen,
const unsigned char *sig, unsigned long siglen,
const unsigned char *ctx, unsigned long ctxlen,
int *stat,
const curve448_key *public_key);
int ed448ph_verify(const unsigned char *msg, unsigned long msglen,
const unsigned char *sig, unsigned long siglen,
const unsigned char *ctx, unsigned long ctxlen,
int *stat,
const curve448_key *public_key);
#endif /* LTC_CURVE448 */
#ifdef LTC_MDSA
/* Max diff between group and modulus size in bytes (max case: L=8192bits, N=256bits) */
@ -724,6 +800,10 @@ typedef struct {
curve25519_key x25519;
curve25519_key ed25519;
#endif
#ifdef LTC_CURVE448
curve448_key x448;
curve448_key ed448;
#endif
#ifdef LTC_MDH
dh_key dh;
#endif

View file

@ -59,6 +59,8 @@ enum ltc_oid_id {
LTC_OID_EC_PRIMEF,
LTC_OID_X25519,
LTC_OID_ED25519,
LTC_OID_X448,
LTC_OID_ED448,
LTC_OID_DH,
LTC_OID_RSA_OAEP,
LTC_OID_RSA_MGF1,
@ -713,6 +715,33 @@ int ec25519_crypto_ctx( unsigned char *out, unsigned long *outlen,
const unsigned char *ctx, unsigned long ctxlen);
#endif /* LTC_CURVE25519 */
#ifdef LTC_CURVE448
int ec448_export(unsigned char *out, unsigned long *outlen, int which, const curve448_key *key);
int ec448_import_pkcs8(const unsigned char *in, unsigned long inlen,
const password_ctx *pw_ctx, enum ltc_oid_id id, curve448_key *key);
int ec448_import_pkcs8_asn1(ltc_asn1_list *alg_id, ltc_asn1_list *priv_key,
enum ltc_oid_id id, curve448_key *key);
int x448_import_pkcs8_asn1(ltc_asn1_list *alg_id, ltc_asn1_list *priv_key,
curve448_key *key);
int ec448_keypair_internal(prng_state *prng, int wprng, unsigned char *pk, unsigned char *sk);
int ec448_sk_to_pk_internal(unsigned char *pk, const unsigned char *sk);
int ec448_sign_internal(unsigned char *sm, unsigned long long *smlen,
const unsigned char *m, unsigned long long mlen,
const unsigned char *sk, const unsigned char *pk,
const unsigned char *ctx, unsigned long long cs);
int ec448_verify_internal(int *stat, unsigned char *m, unsigned long long *mlen,
const unsigned char *sm, unsigned long long smlen,
const unsigned char *ctx, unsigned long long cs,
const unsigned char *pk);
int ec448_prehash_internal(unsigned char *out, const unsigned char *msg, unsigned long long msglen);
int ec448_crypto_ctx(unsigned char *out, unsigned long *outlen, unsigned char flag,
const unsigned char *ctx, unsigned long ctxlen);
int ec448_scalarmult_internal(unsigned char *out, const unsigned char *scalar, const unsigned char *point);
int ec448_scalarmult_base_internal(unsigned char *out, const unsigned char *scalar);
int ed448_import_pkcs8_asn1(ltc_asn1_list *alg_id, ltc_asn1_list *priv_key,
curve448_key *key);
#endif /* LTC_CURVE448 */
#ifdef LTC_DER
#define LTC_ASN1_IS_TYPE(e, t) (((e) != NULL) && ((e)->type == (t)))

View file

@ -384,6 +384,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

View file

@ -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)

View file

@ -19,6 +19,8 @@ static const oid_table_entry pka_oids[] = {
{ LTC_OID_EC_PRIMEF, LTC_PKA_EC, NULL, "1.2.840.10045.1.1" },
{ LTC_OID_X25519, LTC_PKA_X25519, NULL, "1.3.101.110" },
{ LTC_OID_ED25519, LTC_PKA_ED25519, NULL, "1.3.101.112" },
{ LTC_OID_X448, LTC_PKA_X448, NULL, "1.3.101.111" },
{ LTC_OID_ED448, LTC_PKA_ED448, NULL, "1.3.101.113" },
{ LTC_OID_DH, LTC_PKA_DH, NULL, "1.2.840.113549.1.3.1" },
{ LTC_OID_RSA_OAEP, LTC_PKA_RSA, NULL, "1.2.840.113549.1.1.7" },
{ LTC_OID_RSA_MGF1, LTC_PKA_RSA, NULL, "1.2.840.113549.1.1.8" },

View file

@ -52,6 +52,10 @@ static const import_fn s_import_spki_fns[LTC_PKA_NUM] = {
[LTC_PKA_X25519] = (import_fn)s_x25519_import_spki,
[LTC_PKA_ED25519] = (import_fn)s_ed25519_import_spki,
#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)

937
src/pk/ec448/ec448_common.c Normal file
View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

136
src/pk/ed448/ed448_sign.c Normal file
View file

@ -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

143
src/pk/ed448/ed448_verify.c Normal file
View file

@ -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

31
src/pk/x448/x448_export.c Normal file
View file

@ -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

35
src/pk/x448/x448_import.c Normal file
View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

439
tests/ed448_test.c Normal file
View file

@ -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

View file

@ -0,0 +1,4 @@
-----BEGIN PUBLIC KEY-----
MEMwBQYDK2VxAzoAZkavwN2Jb7JwbSR79T0JZUDV1VOCgm1AlFdsW9dV+O57w7E2
+y9O4Ejkhya86byArbdRsOk7ovqA
-----END PUBLIC KEY-----

View file

@ -0,0 +1,4 @@
-----BEGIN PRIVATE KEY-----
MEcCAQAwBQYDK2VxBDsEOe/w/F8aSpwuG9cBnsAkhloXWhPJEVhCxzWdXebe27Tu
f1R24A1BP7pjIihNdlXTsxv+sFuKWMR5ng==
-----END PRIVATE KEY-----

View file

@ -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-----

View file

@ -0,0 +1,4 @@
-----BEGIN PUBLIC KEY-----
MEIwBQYDK2VvAzkApScinIzW1CGVm4zV1j6fw7GsbeHGFy8QY7JLd8tXGiaXzr+/
OwJqRZ9X1QhfEYso6no4d1fP9N0=
-----END PUBLIC KEY-----

View file

@ -0,0 +1,4 @@
-----BEGIN PRIVATE KEY-----
MEYCAQAwBQYDK2VvBDoEOPBQlJ/c83DaT71qCGj25lKh7rr3q6pRBIqHCjExTXD0
vM82EulDzmjR9WU9zaS00j2LU4XXKpvV
-----END PRIVATE KEY-----

View file

@ -0,0 +1,4 @@
-----BEGIN PRIVATE KEY-----
MEcCAQAwBQYDK2VxBDsEOe/w/F8aSpwuG9cBnsAkhloXWhPJEVhCxzWdXebe27Tu
f1R24A1BP7pjIihNdlXTsxv+sFuKWMR5ng==
-----END PRIVATE KEY-----

View file

@ -0,0 +1,5 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
MHAwHAYKKoZIhvcNAQwBAzAOBAjJ1wdj8L5FKgICCAAEUFrnEEVG1fA7XOx15h9T
H0TLtbCm/UYnFLh8GDGVjM+q/OYrfnTv5a6P5G/Ze7tMVsSCpgZBlSL9/Q6NJ37P
2X/Jd5vwyJSRzm+qeugIci5d
-----END ENCRYPTED PRIVATE KEY-----

View file

@ -0,0 +1,6 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIGrMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAhhZGWQAxrkOAICCAAw
DAYIKoZIhvcNAgsFADAdBglghkgBZQMEASoEEBWa/IgJqnE1O2ibVuIkqVwEUIEG
2Wj76gtauZF1wEIts6PN/of37CQjdI8qBT95f0NemnXA9KWhqdT7zCVoZykdwTtr
QveA4USScJ1NjGRCgLcoh+RW0/7pQLI1s06UIptb
-----END ENCRYPTED PRIVATE KEY-----

View file

@ -0,0 +1,4 @@
-----BEGIN PRIVATE KEY-----
MEYCAQAwBQYDK2VvBDoEOPBQlJ/c83DaT71qCGj25lKh7rr3q6pRBIqHCjExTXD0
vM82EulDzmjR9WU9zaS00j2LU4XXKpvV
-----END PRIVATE KEY-----

View file

@ -0,0 +1,5 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
MHAwHAYKKoZIhvcNAQwBAzAOBAgZ7uqzZ0UIPAICCAAEUIoyzd9lPvjne3Bg7lk9
PIHPqflbG9jCtk9192bVNj8+QHA1+XMEB9Ay3/Ljr+hQwiTS1tVELbzqyOoNYxpF
H91YfGjRZDrh2+k/K5MtLFUh
-----END ENCRYPTED PRIVATE KEY-----

View file

@ -0,0 +1,6 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIGrMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAhzD7h+saEo1gICCAAw
DAYIKoZIhvcNAgsFADAdBglghkgBZQMEASoEEFcOyy6Y7TG3Z8wklZO14U8EUKKD
vdlVxsVBpoMpRTbYKen6rbSnAhysBG24K5AVyMAcsTXL1uzGHTIrV3xSjW4W8NV2
N1MjPECNVcxH5HvsCOZ0lqYrj4IdSk4hL1+9Jrzz
-----END ENCRYPTED PRIVATE KEY-----

View file

@ -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;

View file

@ -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),

View file

@ -41,7 +41,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);

231
tests/x448_test.c Normal file
View file

@ -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