From 85895ad8c319f32b3ac5bc07c50c0a7073269503 Mon Sep 17 00:00:00 2001 From: Karel Miko Date: Thu, 16 Apr 2026 17:04:51 +0200 Subject: [PATCH 1/5] make hsalsa20 a public API function --- doc/crypt.tex | 21 ++++++- src/headers/tomcrypt_cipher.h | 4 ++ src/stream/salsa20/xsalsa20_setup.c | 85 ++++++++++++++++++++--------- src/stream/salsa20/xsalsa20_test.c | 23 ++++++++ 4 files changed, 105 insertions(+), 28 deletions(-) diff --git a/doc/crypt.tex b/doc/crypt.tex index e29cc2aba..50a863cfa 100644 --- a/doc/crypt.tex +++ b/doc/crypt.tex @@ -1492,11 +1492,30 @@ \subsection{HChaCha20} As always, never ever use the same key + nonce/IV pair more than once. \vspace{1mm} +\subsection{HSalsa20} + +\textit{HSalsa20} is the key derivation function underlying \textit{XSalsa20}. It applies the +Salsa20 core (without the final addition step) to a 256-bit key and a 128-bit input, +producing a 256-bit derived key. It is also useful as a standalone KDF, for example in NaCl-style +\textit{crypto\_box} constructions where it derives a symmetric key from an X25519 shared secret. + +\index{xsalsa20\_hsalsa20()} +\begin{verbatim} +int xsalsa20_hsalsa20(unsigned char *out, unsigned long outlen, + const unsigned char *key, unsigned long keylen, + const unsigned char *in, unsigned long inlen, + int rounds); +\end{verbatim} +This derives a 32-byte subkey from a 32-byte \textit{key} and a 16-byte \textit{in} using +\textit{rounds} Salsa20 rounds (0 = default 20). The output is stored in \textit{out} +(\textit{outlen} must be 32, \textit{keylen} must be 32, \textit{inlen} must be 16). +\vspace{1mm} + For more information about Salsa20 see \url{https://en.wikipedia.org/wiki/Salsa20}. \vspace{1mm} -For more information about XSalsa20 see +For more information about XSalsa20 and HSalsa20 see \url{https://cr.yp.to/snuffle/xsalsa-20081128.pdf}. \vspace{1mm} diff --git a/src/headers/tomcrypt_cipher.h b/src/headers/tomcrypt_cipher.h index 6e900bb50..ced1a4260 100644 --- a/src/headers/tomcrypt_cipher.h +++ b/src/headers/tomcrypt_cipher.h @@ -1096,6 +1096,10 @@ int salsa20_memory(const unsigned char *key, unsigned long keylen, unsigned #ifdef LTC_XSALSA20 +int xsalsa20_hsalsa20(unsigned char *out, unsigned long outlen, + const unsigned char *key, unsigned long keylen, + const unsigned char *in, unsigned long inlen, + int rounds); int xsalsa20_setup(salsa20_state *st, const unsigned char *key, unsigned long keylen, const unsigned char *nonce, unsigned long noncelen, int rounds); diff --git a/src/stream/salsa20/xsalsa20_setup.c b/src/stream/salsa20/xsalsa20_setup.c index c764a896c..eb2cc502a 100644 --- a/src/stream/salsa20/xsalsa20_setup.c +++ b/src/stream/salsa20/xsalsa20_setup.c @@ -40,34 +40,37 @@ static void s_xsalsa20_doubleround(ulong32 *x, int rounds) #undef QUARTERROUND /** - Initialize an XSalsa20 context - @param st [out] The destination of the XSalsa20 state + HSalsa20: derive a 256-bit subkey from a 256-bit key and 128-bit input. + This is the Salsa20 core (double-rounds) without the final addition step, + extracting output from state positions {0,5,10,15,6,7,8,9}. + @param out [out] The derived 32-byte subkey + @param outlen The length of the output buffer, must be 32 (octets) @param key The secret key @param keylen The length of the secret key, must be 32 (octets) - @param nonce The nonce - @param noncelen The length of the nonce, must be 24 (octets) + @param in The 16-byte input (nonce or constant) + @param inlen The length of the input, must be 16 (octets) @param rounds Number of rounds (must be evenly divisible by 2, default is 20) @return CRYPT_OK if successful */ -int xsalsa20_setup(salsa20_state *st, const unsigned char *key, unsigned long keylen, - const unsigned char *nonce, unsigned long noncelen, - int rounds) +int xsalsa20_hsalsa20(unsigned char *out, unsigned long outlen, + const unsigned char *key, unsigned long keylen, + const unsigned char *in, unsigned long inlen, + int rounds) { const char * const constants = "expand 32-byte k"; - const int sti[] = {0, 5, 10, 15, 6, 7, 8, 9}; /* indices used to build subkey fm x */ - ulong32 x[64]; /* input to & output fm doubleround */ - unsigned char subkey[32]; + const int sti[] = {0, 5, 10, 15, 6, 7, 8, 9}; + ulong32 x[16]; int i; - LTC_ARGCHK(st != NULL); - LTC_ARGCHK(key != NULL); - LTC_ARGCHK(keylen == 32); - LTC_ARGCHK(nonce != NULL); - LTC_ARGCHK(noncelen == 24); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen == 32); + LTC_ARGCHK(key != NULL); + LTC_ARGCHK(keylen == 32); + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(inlen == 16); if (rounds == 0) rounds = 20; - LTC_ARGCHK(rounds % 2 == 0); /* number of rounds must be evenly divisible by 2 */ + LTC_ARGCHK(rounds % 2 == 0); - /* load the state to "hash" the key */ LOAD32L(x[ 0], constants + 0); LOAD32L(x[ 5], constants + 4); LOAD32L(x[10], constants + 8); @@ -80,20 +83,48 @@ int xsalsa20_setup(salsa20_state *st, const unsigned char *key, unsigned long ke LOAD32L(x[12], key + 20); LOAD32L(x[13], key + 24); LOAD32L(x[14], key + 28); - LOAD32L(x[ 6], nonce + 0); - LOAD32L(x[ 7], nonce + 4); - LOAD32L(x[ 8], nonce + 8); - LOAD32L(x[ 9], nonce + 12); + LOAD32L(x[ 6], in + 0); + LOAD32L(x[ 7], in + 4); + LOAD32L(x[ 8], in + 8); + LOAD32L(x[ 9], in + 12); - /* use modified salsa20 doubleround (no final addition) */ s_xsalsa20_doubleround(x, rounds); - /* extract the subkey */ for (i = 0; i < 8; ++i) { - STORE32L(x[sti[i]], subkey + 4 * i); + STORE32L(x[sti[i]], out + 4 * i); } - /* load the final initial state */ + zeromem(x, sizeof(x)); + return CRYPT_OK; +} + +/** + Initialize an XSalsa20 context + @param st [out] The destination of the XSalsa20 state + @param key The secret key + @param keylen The length of the secret key, must be 32 (octets) + @param nonce The nonce + @param noncelen The length of the nonce, must be 24 (octets) + @param rounds Number of rounds (must be evenly divisible by 2, default is 20) + @return CRYPT_OK if successful +*/ +int xsalsa20_setup(salsa20_state *st, const unsigned char *key, unsigned long keylen, + const unsigned char *nonce, unsigned long noncelen, + int rounds) +{ + const char * const constants = "expand 32-byte k"; + unsigned char subkey[32]; + int err; + + LTC_ARGCHK(st != NULL); + LTC_ARGCHK(nonce != NULL); + LTC_ARGCHK(noncelen == 24); + if (rounds == 0) rounds = 20; + + /* HSalsa20: derive subkey from key and first 16 bytes of nonce */ + if ((err = xsalsa20_hsalsa20(subkey, 32, key, keylen, nonce, 16, rounds)) != CRYPT_OK) goto cleanup; + + /* load the final initial state with the derived subkey */ LOAD32L(st->input[ 0], constants + 0); LOAD32L(st->input[ 5], constants + 4); LOAD32L(st->input[10], constants + 8); @@ -114,12 +145,12 @@ int xsalsa20_setup(salsa20_state *st, const unsigned char *key, unsigned long ke st->ksleft = 0; st->ivlen = 24; /* set switch to say nonce/IV has been loaded */ +cleanup: #ifdef LTC_CLEAN_STACK - zeromem(x, sizeof(x)); zeromem(subkey, sizeof(subkey)); #endif - return CRYPT_OK; + return err; } diff --git a/src/stream/salsa20/xsalsa20_test.c b/src/stream/salsa20/xsalsa20_test.c index a267cdaad..f2a5004f5 100644 --- a/src/stream/salsa20/xsalsa20_test.c +++ b/src/stream/salsa20/xsalsa20_test.c @@ -28,6 +28,29 @@ int xsalsa20_test(void) return CRYPT_NOP; #else + /*************************************************************************** + * TV0: HSalsa20 known-answer test + * From the NaCl test suite / https://cr.yp.to/snuffle/xsalsa-20081128.pdf + */ + { + const unsigned char key[] = { + 0x1b,0x27,0x55,0x64,0x73,0xe9,0x85,0xd4,0x62,0xcd,0x51,0x19,0x7a,0x9a,0x46,0xc7, + 0x60,0x09,0x54,0x9e,0xac,0x64,0x74,0xf2,0x06,0xc4,0xee,0x08,0x44,0xf6,0x83,0x89 + }; + const unsigned char in[] = { + 0x69,0x69,0x6e,0xe9,0x55,0xb6,0x2b,0x73,0xcd,0x62,0xbd,0xa8,0x75,0xfc,0x73,0xd6 + }; + const unsigned char expected[] = { + 0xdc,0x90,0x8d,0xda,0x0b,0x93,0x44,0xa9,0x53,0x62,0x9b,0x73,0x38,0x20,0x77,0x88, + 0x80,0xf3,0xce,0xb4,0x21,0xbb,0x61,0xb9,0x1c,0xbd,0x4c,0x3e,0x66,0x25,0x6c,0xe4 + }; + unsigned char out[32]; + int err; + + if ((err = xsalsa20_hsalsa20(out, 32, key, 32, in, 16, 20)) != CRYPT_OK) return err; + if (ltc_compare_testvector(out, 32, expected, 32, "XSALSA20-TV0 (HSalsa20)", 0)) return CRYPT_FAIL_TESTVECTOR; + } + /*************************************************************************** * verify a round trip: */ From 0d2037e46af3c14fda64f11d331515162791e348 Mon Sep 17 00:00:00 2001 From: Karel Miko Date: Thu, 16 Apr 2026 17:07:27 +0200 Subject: [PATCH 2/5] blake2bmac_init/blake2smac_init - allows NULL key when keylen is 0 (in line with the BLAKE2 spec) --- src/mac/blake2/blake2bmac.c | 2 +- src/mac/blake2/blake2smac.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mac/blake2/blake2bmac.c b/src/mac/blake2/blake2bmac.c index ac26e106a..c8387b7f1 100644 --- a/src/mac/blake2/blake2bmac.c +++ b/src/mac/blake2/blake2bmac.c @@ -16,7 +16,7 @@ int blake2bmac_init(blake2bmac_state *st, unsigned long outlen, const unsigned char *key, unsigned long keylen) { LTC_ARGCHK(st != NULL); - LTC_ARGCHK(key != NULL); + LTC_ARGCHK(key != NULL || keylen == 0); return blake2b_init(st, outlen, key, keylen); } diff --git a/src/mac/blake2/blake2smac.c b/src/mac/blake2/blake2smac.c index f42c7aec3..5ba39c86f 100644 --- a/src/mac/blake2/blake2smac.c +++ b/src/mac/blake2/blake2smac.c @@ -16,7 +16,7 @@ int blake2smac_init(blake2smac_state *st, unsigned long outlen, const unsigned char *key, unsigned long keylen) { LTC_ARGCHK(st != NULL); - LTC_ARGCHK(key != NULL); + LTC_ARGCHK(key != NULL || keylen == 0); return blake2s_init(st, outlen, key, keylen); } From 00317f68cf8a34da1f46ea6daf449c88468630f5 Mon Sep 17 00:00:00 2001 From: Karel Miko Date: Tue, 5 May 2026 13:09:18 +0200 Subject: [PATCH 3/5] ltc boxes (libsodium compatible) --- src/boxes/ltc_cryptobox_create.c | 96 +++++++++++++++++++++++++++++++ src/boxes/ltc_cryptobox_open.c | 96 +++++++++++++++++++++++++++++++ src/boxes/ltc_sealedbox_create.c | 97 ++++++++++++++++++++++++++++++++ src/boxes/ltc_sealedbox_open.c | 79 ++++++++++++++++++++++++++ src/boxes/ltc_secretbox_create.c | 67 ++++++++++++++++++++++ src/boxes/ltc_secretbox_open.c | 72 ++++++++++++++++++++++++ src/headers/tomcrypt_misc.h | 57 +++++++++++++++++++ src/headers/tomcrypt_private.h | 9 +++ 8 files changed, 573 insertions(+) create mode 100644 src/boxes/ltc_cryptobox_create.c create mode 100644 src/boxes/ltc_cryptobox_open.c create mode 100644 src/boxes/ltc_sealedbox_create.c create mode 100644 src/boxes/ltc_sealedbox_open.c create mode 100644 src/boxes/ltc_secretbox_create.c create mode 100644 src/boxes/ltc_secretbox_open.c diff --git a/src/boxes/ltc_cryptobox_create.c b/src/boxes/ltc_cryptobox_create.c new file mode 100644 index 000000000..401a501a2 --- /dev/null +++ b/src/boxes/ltc_cryptobox_create.c @@ -0,0 +1,96 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +#include "tomcrypt_private.h" + +/** + @file ltc_cryptobox_create.c + libsodium-compatible crypto_box encryption helpers +*/ + +#if defined(LTC_XSALSA20) && defined(LTC_POLY1305) && defined(LTC_CURVE25519) && defined(LTC_BLAKE2B) + +/** + Encrypt a message with crypto_box using imported X25519 keys + @param msg The plaintext message to authenticate and encrypt + @param msglen The length of the plaintext message in octets + @param nonce The nonce to use for XSalsa20-Poly1305, must be 24 octets + @param noncelen The length of the nonce, must be LTC_SECRETBOX_NONCELEN + @param pk The recipient public X25519 key as an initialized key object + @param sk The sender private X25519 key as an initialized key object + @param out [out] The destination for the 16-octet tag followed by ciphertext + @param outlen [in/out] Available out size on entry, bytes written or required on return + @return CRYPT_OK if successful +*/ +int ltc_cryptobox_create_ck(const unsigned char *msg, unsigned long msglen, + const unsigned char *nonce, unsigned long noncelen, + const curve25519_key *pk, + const curve25519_key *sk, + unsigned char *out, unsigned long *outlen) +{ + unsigned char shared[LTC_BOX_KEYLEN], symkey[LTC_BOX_KEYLEN]; + const unsigned char zero16[16] = {0}; + unsigned long shared_len = sizeof(shared); + int err; + + LTC_ARGCHK(msg != NULL); + LTC_ARGCHK(nonce != NULL); + LTC_ARGCHK(pk != NULL); + LTC_ARGCHK(sk != NULL); + LTC_ARGCHK(noncelen == LTC_SECRETBOX_NONCELEN); + if (pk->pka != LTC_PKA_X25519) return CRYPT_PK_INVALID_TYPE; + if (sk->pka != LTC_PKA_X25519) return CRYPT_PK_INVALID_TYPE; + if (sk->type != PK_PRIVATE) return CRYPT_PK_INVALID_TYPE; + + if ((err = x25519_shared_secret(sk, pk, shared, &shared_len)) != CRYPT_OK) goto cleanup; + if ((err = xsalsa20_hsalsa20(symkey, sizeof(symkey), shared, shared_len, zero16, sizeof(zero16), 20)) != CRYPT_OK) goto cleanup; + err = ltc_secretbox_create(msg, msglen, nonce, noncelen, symkey, sizeof(symkey), out, outlen); + +cleanup: + zeromem(shared, sizeof(shared)); + zeromem(symkey, sizeof(symkey)); + return err; +} + +/** + Encrypt a message with crypto_box using raw 32-byte X25519 keys + @param msg The plaintext message to authenticate and encrypt + @param msglen The length of the plaintext message in octets + @param nonce The nonce to use for XSalsa20-Poly1305, must be 24 octets + @param noncelen The length of the nonce, must be LTC_SECRETBOX_NONCELEN + @param pk The recipient raw public X25519 key, must be 32 octets + @param pklen The length of the recipient public key, must be LTC_BOX_KEYLEN + @param sk The sender raw private X25519 key, must be 32 octets + @param sklen The length of the sender private key, must be LTC_BOX_KEYLEN + @param out [out] The destination for the 16-octet tag followed by ciphertext + @param outlen [in/out] Available out size on entry, bytes written or required on return + @return CRYPT_OK if successful +*/ +int ltc_cryptobox_create(const unsigned char *msg, unsigned long msglen, + const unsigned char *nonce, unsigned long noncelen, + const unsigned char *pk, unsigned long pklen, + const unsigned char *sk, unsigned long sklen, + unsigned char *out, unsigned long *outlen) +{ + curve25519_key sender_sk, recipient_pk; + int err; + + LTC_ARGCHK(msg != NULL); + LTC_ARGCHK(nonce != NULL); + LTC_ARGCHK(pk != NULL); + LTC_ARGCHK(sk != NULL); + LTC_ARGCHK(noncelen == LTC_SECRETBOX_NONCELEN); + LTC_ARGCHK(pklen == LTC_BOX_KEYLEN); + LTC_ARGCHK(sklen == LTC_BOX_KEYLEN); + + if ((err = x25519_import_raw(sk, sklen, PK_PRIVATE, &sender_sk)) != CRYPT_OK) return err; + if ((err = x25519_import_raw(pk, pklen, PK_PUBLIC, &recipient_pk)) != CRYPT_OK) goto cleanup; + err = ltc_cryptobox_create_ck(msg, msglen, nonce, noncelen, &recipient_pk, &sender_sk, out, outlen); + +cleanup: + zeromem(&sender_sk, sizeof(sender_sk)); + zeromem(&recipient_pk, sizeof(recipient_pk)); + return err; +} + +#endif diff --git a/src/boxes/ltc_cryptobox_open.c b/src/boxes/ltc_cryptobox_open.c new file mode 100644 index 000000000..27c1a6aae --- /dev/null +++ b/src/boxes/ltc_cryptobox_open.c @@ -0,0 +1,96 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +#include "tomcrypt_private.h" + +/** + @file ltc_cryptobox_open.c + libsodium-compatible crypto_box decryption helpers +*/ + +#if defined(LTC_XSALSA20) && defined(LTC_POLY1305) && defined(LTC_CURVE25519) && defined(LTC_BLAKE2B) + +/** + Decrypt a crypto_box message using imported X25519 keys + @param enc The encrypted input, a 16-octet tag followed by ciphertext + @param enclen The length of the encrypted input in octets + @param nonce The nonce used for encryption, must be 24 octets + @param noncelen The length of the nonce, must be LTC_SECRETBOX_NONCELEN + @param pk The sender public X25519 key as an initialized key object + @param sk The recipient private X25519 key as an initialized key object + @param out [out] The destination for the decrypted plaintext + @param outlen [in/out] Available out size on entry, bytes written or required on return + @return CRYPT_OK if successful +*/ +int ltc_cryptobox_open_ck(const unsigned char *enc, unsigned long enclen, + const unsigned char *nonce, unsigned long noncelen, + const curve25519_key *pk, + const curve25519_key *sk, + unsigned char *out, unsigned long *outlen) +{ + unsigned char shared[LTC_BOX_KEYLEN], symkey[LTC_BOX_KEYLEN]; + const unsigned char zero16[16] = {0}; + unsigned long shared_len = sizeof(shared); + int err; + + LTC_ARGCHK(enc != NULL); + LTC_ARGCHK(nonce != NULL); + LTC_ARGCHK(pk != NULL); + LTC_ARGCHK(sk != NULL); + LTC_ARGCHK(noncelen == LTC_SECRETBOX_NONCELEN); + if (pk->pka != LTC_PKA_X25519) return CRYPT_PK_INVALID_TYPE; + if (sk->pka != LTC_PKA_X25519) return CRYPT_PK_INVALID_TYPE; + if (sk->type != PK_PRIVATE) return CRYPT_PK_INVALID_TYPE; + + if ((err = x25519_shared_secret(sk, pk, shared, &shared_len)) != CRYPT_OK) goto cleanup; + if ((err = xsalsa20_hsalsa20(symkey, sizeof(symkey), shared, shared_len, zero16, sizeof(zero16), 20)) != CRYPT_OK) goto cleanup; + err = ltc_secretbox_open(enc, enclen, nonce, noncelen, symkey, sizeof(symkey), out, outlen); + +cleanup: + zeromem(shared, sizeof(shared)); + zeromem(symkey, sizeof(symkey)); + return err; +} + +/** + Decrypt a crypto_box message using raw 32-byte X25519 keys + @param enc The encrypted input, a 16-octet tag followed by ciphertext + @param enclen The length of the encrypted input in octets + @param nonce The nonce used for encryption, must be 24 octets + @param noncelen The length of the nonce, must be LTC_SECRETBOX_NONCELEN + @param pk The sender raw public X25519 key, must be 32 octets + @param pklen The length of the sender public key, must be LTC_BOX_KEYLEN + @param sk The recipient raw private X25519 key, must be 32 octets + @param sklen The length of the recipient private key, must be LTC_BOX_KEYLEN + @param out [out] The destination for the decrypted plaintext + @param outlen [in/out] Available out size on entry, bytes written or required on return + @return CRYPT_OK if successful +*/ +int ltc_cryptobox_open(const unsigned char *enc, unsigned long enclen, + const unsigned char *nonce, unsigned long noncelen, + const unsigned char *pk, unsigned long pklen, + const unsigned char *sk, unsigned long sklen, + unsigned char *out, unsigned long *outlen) +{ + curve25519_key recipient_sk, sender_pk; + int err; + + LTC_ARGCHK(enc != NULL); + LTC_ARGCHK(nonce != NULL); + LTC_ARGCHK(pk != NULL); + LTC_ARGCHK(sk != NULL); + LTC_ARGCHK(noncelen == LTC_SECRETBOX_NONCELEN); + LTC_ARGCHK(pklen == LTC_BOX_KEYLEN); + LTC_ARGCHK(sklen == LTC_BOX_KEYLEN); + + if ((err = x25519_import_raw(sk, sklen, PK_PRIVATE, &recipient_sk)) != CRYPT_OK) return err; + if ((err = x25519_import_raw(pk, pklen, PK_PUBLIC, &sender_pk)) != CRYPT_OK) goto cleanup; + err = ltc_cryptobox_open_ck(enc, enclen, nonce, noncelen, &sender_pk, &recipient_sk, out, outlen); + +cleanup: + zeromem(&recipient_sk, sizeof(recipient_sk)); + zeromem(&sender_pk, sizeof(sender_pk)); + return err; +} + +#endif diff --git a/src/boxes/ltc_sealedbox_create.c b/src/boxes/ltc_sealedbox_create.c new file mode 100644 index 000000000..84c9cffbc --- /dev/null +++ b/src/boxes/ltc_sealedbox_create.c @@ -0,0 +1,97 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +#include "tomcrypt_private.h" + +/** + @file ltc_sealedbox_create.c + libsodium-compatible sealed box encryption helper +*/ + +#if defined(LTC_XSALSA20) && defined(LTC_POLY1305) && defined(LTC_CURVE25519) && defined(LTC_BLAKE2B) + +/** + Encrypt a message with a crypto_box sealed box using an imported recipient key + @param msg The plaintext message to encrypt for the recipient public key + @param msglen The length of the plaintext message in octets + @param pk The recipient public X25519 key as an initialized key object + @param prng An active PRNG state used to generate the ephemeral X25519 key + @param wprng The registered PRNG descriptor index matching prng + @param out [out] The destination for ephemeral public key || tag || ciphertext + @param outlen [in/out] Available out size on entry, bytes written or required on return + @return CRYPT_OK if successful +*/ +int ltc_sealedbox_create_ck(const unsigned char *msg, unsigned long msglen, + const curve25519_key *pk, + prng_state *prng, int wprng, + unsigned char *out, unsigned long *outlen) +{ + curve25519_key eph; + hash_state md; + unsigned char nonce[LTC_SECRETBOX_NONCELEN]; + unsigned long need; + unsigned long inner_outlen; + int err; + + LTC_ARGCHK(msg != NULL); + LTC_ARGCHK(pk != NULL); + LTC_ARGCHK(prng != NULL); + LTC_ARGCHK(outlen != NULL); + if (pk->pka != LTC_PKA_X25519) return CRYPT_PK_INVALID_TYPE; + if (msglen > ULONG_MAX - LTC_SEALBOX_OVERHEAD) return CRYPT_OVERFLOW; + need = msglen + LTC_SEALBOX_OVERHEAD; + if (*outlen < need) { + *outlen = need; + return CRYPT_BUFFER_OVERFLOW; + } + LTC_ARGCHK(out != NULL); + *outlen = need; + + if ((err = x25519_make_key(prng, wprng, &eph)) != CRYPT_OK) return err; + + XMEMCPY(out, eph.pub, LTC_SEALBOX_PREAMBLE); + if ((err = blake2b_init(&md, sizeof(nonce), NULL, 0)) != CRYPT_OK) goto cleanup; + if ((err = blake2b_process(&md, eph.pub, LTC_SEALBOX_PREAMBLE)) != CRYPT_OK) goto cleanup; + if ((err = blake2b_process(&md, pk->pub, LTC_SEALBOX_PREAMBLE)) != CRYPT_OK) goto cleanup; + if ((err = blake2b_done(&md, nonce)) != CRYPT_OK) goto cleanup; + inner_outlen = *outlen - LTC_SEALBOX_PREAMBLE; + err = ltc_cryptobox_create_ck(msg, msglen, nonce, sizeof(nonce), pk, &eph, out + LTC_SEALBOX_PREAMBLE, &inner_outlen); + if (err == CRYPT_OK) *outlen = LTC_SEALBOX_PREAMBLE + inner_outlen; + +cleanup: + zeromem(&md, sizeof(md)); + zeromem(nonce, sizeof(nonce)); + zeromem(&eph, sizeof(eph)); + return err; +} + +/** + Encrypt a message with a crypto_box sealed box using a raw recipient key + @param msg The plaintext message to encrypt for the recipient public key + @param msglen The length of the plaintext message in octets + @param pk The recipient raw public X25519 key, must be 32 octets + @param pklen The length of the recipient public key, must be LTC_BOX_KEYLEN + @param prng An active PRNG state used to generate the ephemeral X25519 key + @param wprng The registered PRNG descriptor index matching prng + @param out [out] The destination for ephemeral public key || tag || ciphertext + @param outlen [in/out] Available out size on entry, bytes written or required on return + @return CRYPT_OK if successful +*/ +int ltc_sealedbox_create(const unsigned char *msg, unsigned long msglen, + const unsigned char *pk, unsigned long pklen, + prng_state *prng, int wprng, + unsigned char *out, unsigned long *outlen) +{ + curve25519_key recipient_pk; + int err; + + LTC_ARGCHK(pk != NULL); + LTC_ARGCHK(pklen == LTC_BOX_KEYLEN); + + if ((err = x25519_import_raw(pk, pklen, PK_PUBLIC, &recipient_pk)) != CRYPT_OK) return err; + err = ltc_sealedbox_create_ck(msg, msglen, &recipient_pk, prng, wprng, out, outlen); + zeromem(&recipient_pk, sizeof(recipient_pk)); + return err; +} + +#endif diff --git a/src/boxes/ltc_sealedbox_open.c b/src/boxes/ltc_sealedbox_open.c new file mode 100644 index 000000000..55ec1ca79 --- /dev/null +++ b/src/boxes/ltc_sealedbox_open.c @@ -0,0 +1,79 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +#include "tomcrypt_private.h" + +/** + @file ltc_sealedbox_open.c + libsodium-compatible sealed box decryption helper +*/ + +#if defined(LTC_XSALSA20) && defined(LTC_POLY1305) && defined(LTC_CURVE25519) && defined(LTC_BLAKE2B) + +/** + Decrypt a crypto_box sealed box message using an imported recipient key + @param enc The encrypted sealed box: ephemeral public key || tag || ciphertext + @param enclen The length of the encrypted sealed box in octets + @param sk The recipient private X25519 key as an initialized key object + @param out [out] The destination for the decrypted plaintext + @param outlen [in/out] Available out size on entry, bytes written or required on return + @return CRYPT_OK if successful +*/ +int ltc_sealedbox_open_ck(const unsigned char *enc, unsigned long enclen, + const curve25519_key *sk, + unsigned char *out, unsigned long *outlen) +{ + curve25519_key eph_pk; + hash_state md; + unsigned char nonce[LTC_SECRETBOX_NONCELEN]; + int err; + + LTC_ARGCHK(enc != NULL); + LTC_ARGCHK(sk != NULL); + LTC_ARGCHK(enclen >= LTC_SEALBOX_OVERHEAD); + if (sk->pka != LTC_PKA_X25519) return CRYPT_PK_INVALID_TYPE; + if (sk->type != PK_PRIVATE) return CRYPT_PK_INVALID_TYPE; + + if ((err = x25519_import_raw(enc, LTC_SEALBOX_PREAMBLE, PK_PUBLIC, &eph_pk)) != CRYPT_OK) return err; + if ((err = blake2b_init(&md, sizeof(nonce), NULL, 0)) != CRYPT_OK) goto cleanup; + if ((err = blake2b_process(&md, enc, LTC_SEALBOX_PREAMBLE)) != CRYPT_OK) goto cleanup; + if ((err = blake2b_process(&md, sk->pub, LTC_SEALBOX_PREAMBLE)) != CRYPT_OK) goto cleanup; + if ((err = blake2b_done(&md, nonce)) != CRYPT_OK) goto cleanup; + err = ltc_cryptobox_open_ck(enc + LTC_SEALBOX_PREAMBLE, enclen - LTC_SEALBOX_PREAMBLE, nonce, sizeof(nonce), &eph_pk, sk, out, outlen); + +cleanup: + zeromem(&md, sizeof(md)); + zeromem(&eph_pk, sizeof(eph_pk)); + zeromem(nonce, sizeof(nonce)); + return err; +} + +/** + Decrypt a crypto_box sealed box message using a raw recipient private key + @param enc The encrypted sealed box: ephemeral public key || tag || ciphertext + @param enclen The length of the encrypted sealed box in octets + @param sk The recipient raw private X25519 key, must be 32 octets + @param sklen The length of the recipient private key, must be LTC_BOX_KEYLEN + @param out [out] The destination for the decrypted plaintext + @param outlen [in/out] Available out size on entry, bytes written or required on return + @return CRYPT_OK if successful +*/ +int ltc_sealedbox_open(const unsigned char *enc, unsigned long enclen, + const unsigned char *sk, unsigned long sklen, + unsigned char *out, unsigned long *outlen) +{ + curve25519_key recipient_sk; + int err; + + LTC_ARGCHK(enc != NULL); + LTC_ARGCHK(sk != NULL); + LTC_ARGCHK(sklen == LTC_BOX_KEYLEN); + LTC_ARGCHK(enclen >= LTC_SEALBOX_OVERHEAD); + + if ((err = x25519_import_raw(sk, sklen, PK_PRIVATE, &recipient_sk)) != CRYPT_OK) return err; + err = ltc_sealedbox_open_ck(enc, enclen, &recipient_sk, out, outlen); + zeromem(&recipient_sk, sizeof(recipient_sk)); + return err; +} + +#endif diff --git a/src/boxes/ltc_secretbox_create.c b/src/boxes/ltc_secretbox_create.c new file mode 100644 index 000000000..aec56f881 --- /dev/null +++ b/src/boxes/ltc_secretbox_create.c @@ -0,0 +1,67 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +#include "tomcrypt_private.h" + +/** + @file ltc_secretbox_create.c + libsodium-compatible secretbox encryption helper +*/ + +#if defined(LTC_XSALSA20) && defined(LTC_POLY1305) + +/** + Encrypt a message with XSalsa20-Poly1305 secretbox + @param msg The plaintext message to authenticate and encrypt + @param msglen The length of the plaintext message in octets + @param nonce The nonce to use for XSalsa20-Poly1305, must be 24 octets + @param noncelen The length of the nonce, must be LTC_SECRETBOX_NONCELEN + @param key The symmetric secretbox key, must be 32 octets + @param keylen The length of the symmetric key, must be LTC_SECRETBOX_KEYLEN + @param out [out] The destination for the 16-octet tag followed by ciphertext + @param outlen [in/out] Available out size on entry, bytes written or required on return + @return CRYPT_OK if successful +*/ +int ltc_secretbox_create(const unsigned char *msg, unsigned long msglen, + const unsigned char *nonce, unsigned long noncelen, + const unsigned char *key, unsigned long keylen, + unsigned char *out, unsigned long *outlen) +{ + salsa20_state st; + poly1305_state poly; + unsigned char polykey[LTC_SECRETBOX_KEYLEN]; + unsigned long need; + unsigned long taglen = LTC_SECRETBOX_TAGLEN; + int err; + + LTC_ARGCHK(msg != NULL); + LTC_ARGCHK(nonce != NULL); + LTC_ARGCHK(key != NULL); + LTC_ARGCHK(noncelen == LTC_SECRETBOX_NONCELEN); + LTC_ARGCHK(keylen == LTC_SECRETBOX_KEYLEN); + LTC_ARGCHK(outlen != NULL); + if (msglen > ULONG_MAX - LTC_SECRETBOX_TAGLEN) return CRYPT_OVERFLOW; + need = msglen + LTC_SECRETBOX_TAGLEN; + if (*outlen < need) { + *outlen = need; + return CRYPT_BUFFER_OVERFLOW; + } + LTC_ARGCHK(out != NULL); + *outlen = need; + + if ((err = xsalsa20_setup(&st, key, keylen, nonce, noncelen, 20)) != CRYPT_OK) goto cleanup; + if ((err = salsa20_keystream(&st, polykey, sizeof(polykey))) != CRYPT_OK) goto cleanup; + if ((err = salsa20_crypt(&st, msg, msglen, out + LTC_SECRETBOX_TAGLEN)) != CRYPT_OK) goto cleanup; + + if ((err = poly1305_init(&poly, polykey, sizeof(polykey))) != CRYPT_OK) goto cleanup; + if ((err = poly1305_process(&poly, out + LTC_SECRETBOX_TAGLEN, msglen)) != CRYPT_OK) goto cleanup; + err = poly1305_done(&poly, out, &taglen); + +cleanup: + salsa20_done(&st); + zeromem(&poly, sizeof(poly)); + zeromem(polykey, sizeof(polykey)); + return err; +} + +#endif diff --git a/src/boxes/ltc_secretbox_open.c b/src/boxes/ltc_secretbox_open.c new file mode 100644 index 000000000..9a569a07b --- /dev/null +++ b/src/boxes/ltc_secretbox_open.c @@ -0,0 +1,72 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +#include "tomcrypt_private.h" + +/** + @file ltc_secretbox_open.c + libsodium-compatible secretbox decryption helper +*/ + +#if defined(LTC_XSALSA20) && defined(LTC_POLY1305) + +/** + Decrypt a XSalsa20-Poly1305 secretbox message + @param enc The encrypted input, a 16-octet tag followed by ciphertext + @param enclen The length of the encrypted input in octets + @param nonce The nonce used for encryption, must be 24 octets + @param noncelen The length of the nonce, must be LTC_SECRETBOX_NONCELEN + @param key The symmetric secretbox key, must be 32 octets + @param keylen The length of the symmetric key, must be LTC_SECRETBOX_KEYLEN + @param out [out] The destination for the decrypted plaintext + @param outlen [in/out] Available out size on entry, bytes written or required on return + @return CRYPT_OK if successful +*/ +int ltc_secretbox_open(const unsigned char *enc, unsigned long enclen, + const unsigned char *nonce, unsigned long noncelen, + const unsigned char *key, unsigned long keylen, + unsigned char *out, unsigned long *outlen) +{ + salsa20_state st; + poly1305_state poly; + unsigned char polykey[LTC_SECRETBOX_KEYLEN], tag[LTC_SECRETBOX_TAGLEN]; + unsigned long taglen = LTC_SECRETBOX_TAGLEN; + unsigned long msglen; + int err; + + LTC_ARGCHK(enc != NULL); + LTC_ARGCHK(nonce != NULL); + LTC_ARGCHK(key != NULL); + LTC_ARGCHK(noncelen == LTC_SECRETBOX_NONCELEN); + LTC_ARGCHK(keylen == LTC_SECRETBOX_KEYLEN); + LTC_ARGCHK(enclen >= LTC_SECRETBOX_TAGLEN); + + msglen = enclen - LTC_SECRETBOX_TAGLEN; + LTC_ARGCHK(outlen != NULL); + if (*outlen < msglen) { + *outlen = msglen; + return CRYPT_BUFFER_OVERFLOW; + } + LTC_ARGCHK(out != NULL); + *outlen = msglen; + + if ((err = xsalsa20_setup(&st, key, keylen, nonce, noncelen, 20)) != CRYPT_OK) return err; + if ((err = salsa20_keystream(&st, polykey, sizeof(polykey))) != CRYPT_OK) goto done; + if ((err = poly1305_init(&poly, polykey, sizeof(polykey))) != CRYPT_OK) goto done; + if ((err = poly1305_process(&poly, enc + LTC_SECRETBOX_TAGLEN, msglen)) != CRYPT_OK) goto done; + if ((err = poly1305_done(&poly, tag, &taglen)) != CRYPT_OK) goto done; + if (mem_neq(tag, enc, LTC_SECRETBOX_TAGLEN) != 0) { + err = CRYPT_ERROR; + goto done; + } + err = salsa20_crypt(&st, enc + LTC_SECRETBOX_TAGLEN, msglen, out); + +done: + salsa20_done(&st); + zeromem(&poly, sizeof(poly)); + zeromem(tag, sizeof(tag)); + zeromem(polykey, sizeof(polykey)); + return err; +} + +#endif diff --git a/src/headers/tomcrypt_misc.h b/src/headers/tomcrypt_misc.h index be63f5507..122eab1ab 100644 --- a/src/headers/tomcrypt_misc.h +++ b/src/headers/tomcrypt_misc.h @@ -109,6 +109,63 @@ int hkdf(int hash_idx, #endif /* LTC_HKDF */ +/* ---- libsodium box-compatible helpers ---- */ +#if defined(LTC_XSALSA20) && defined(LTC_POLY1305) +int ltc_secretbox_create(const unsigned char *msg, unsigned long msglen, + const unsigned char *nonce, unsigned long noncelen, + const unsigned char *key, unsigned long keylen, + unsigned char *out, unsigned long *outlen); + +int ltc_secretbox_open(const unsigned char *enc, unsigned long enclen, + const unsigned char *nonce, unsigned long noncelen, + const unsigned char *key, unsigned long keylen, + unsigned char *out, unsigned long *outlen); +#endif + +#if defined(LTC_XSALSA20) && defined(LTC_POLY1305) && defined(LTC_CURVE25519) && defined(LTC_BLAKE2B) +int ltc_cryptobox_create(const unsigned char *msg, unsigned long msglen, + const unsigned char *nonce, unsigned long noncelen, + const unsigned char *pk, unsigned long pklen, + const unsigned char *sk, unsigned long sklen, + unsigned char *out, unsigned long *outlen); + +int ltc_cryptobox_create_ck(const unsigned char *msg, unsigned long msglen, + const unsigned char *nonce, unsigned long noncelen, + const curve25519_key *pk, + const curve25519_key *sk, + unsigned char *out, unsigned long *outlen); + +int ltc_cryptobox_open(const unsigned char *enc, unsigned long enclen, + const unsigned char *nonce, unsigned long noncelen, + const unsigned char *pk, unsigned long pklen, + const unsigned char *sk, unsigned long sklen, + unsigned char *out, unsigned long *outlen); + +int ltc_cryptobox_open_ck(const unsigned char *enc, unsigned long enclen, + const unsigned char *nonce, unsigned long noncelen, + const curve25519_key *pk, + const curve25519_key *sk, + unsigned char *out, unsigned long *outlen); + +int ltc_sealedbox_create(const unsigned char *msg, unsigned long msglen, + const unsigned char *pk, unsigned long pklen, + prng_state *prng, int wprng, + unsigned char *out, unsigned long *outlen); + +int ltc_sealedbox_create_ck(const unsigned char *msg, unsigned long msglen, + const curve25519_key *pk, + prng_state *prng, int wprng, + unsigned char *out, unsigned long *outlen); + +int ltc_sealedbox_open(const unsigned char *enc, unsigned long enclen, + const unsigned char *sk, unsigned long sklen, + unsigned char *out, unsigned long *outlen); + +int ltc_sealedbox_open_ck(const unsigned char *enc, unsigned long enclen, + const curve25519_key *sk, + unsigned char *out, unsigned long *outlen); +#endif + /* ---- MEM routines ---- */ int mem_neq(const void *a, const void *b, size_t len); void zeromem(volatile void *out, size_t outlen); diff --git a/src/headers/tomcrypt_private.h b/src/headers/tomcrypt_private.h index 6df57ef9f..c90e3e06d 100644 --- a/src/headers/tomcrypt_private.h +++ b/src/headers/tomcrypt_private.h @@ -37,6 +37,15 @@ LTC_STATIC_ASSERT(correct_ltc_uintptr_size, sizeof(ltc_uintptr) == sizeof(void*) #define LTC_OID_MAX_STRLEN 256 +#if defined(LTC_XSALSA20) && defined(LTC_POLY1305) && defined(LTC_CURVE25519) && defined(LTC_BLAKE2B) +#define LTC_SECRETBOX_NONCELEN 24uL +#define LTC_SECRETBOX_KEYLEN 32uL +#define LTC_BOX_KEYLEN 32uL +#define LTC_SECRETBOX_TAGLEN 16uL +#define LTC_SEALBOX_PREAMBLE 32uL +#define LTC_SEALBOX_OVERHEAD (LTC_SEALBOX_PREAMBLE + LTC_SECRETBOX_TAGLEN) +#endif + /* `NULL` as defined by the standard is not guaranteed to be of a pointer * type. In order to make sure that in vararg API's a pointer type is used, * define our own version and use that one internally. From 1577c79f09095b86b6f8fa519cfed8616d469e25 Mon Sep 17 00:00:00 2001 From: Karel Miko Date: Tue, 5 May 2026 13:12:22 +0200 Subject: [PATCH 4/5] libsodium interoperability demo --- demos/libsodium-interop.c | 194 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 demos/libsodium-interop.c diff --git a/demos/libsodium-interop.c b/demos/libsodium-interop.c new file mode 100644 index 000000000..eaa3ff691 --- /dev/null +++ b/demos/libsodium-interop.c @@ -0,0 +1,194 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* Interoperability test between libtomcrypt and libsodium for: + 1. secretbox (XSalsa20-Poly1305) + 2. cryptobox (X25519 + HSalsa20 + XSalsa20-Poly1305) + 3. sealbox (ephemeral X25519 + BLAKE2b nonce + cryptobox) + + Build (run from the repo root, after `make`): + cc -Isrc/headers -o demos/libsodium-interop demos/libsodium-interop.c libtomcrypt.a -lsodium + + Run: + ./demos/libsodium-interop +*/ + +#include +#include +#include +#include + +#define CHECK(e) do { if ((e) != 0) { fprintf(stderr, "FAIL line %d\n", __LINE__); return 1; } } while(0) + +static int test_secretbox(void) +{ + const unsigned char key[32] = { + 0x1b,0x27,0x55,0x64,0x73,0xe9,0x85,0xd4,0x62,0xcd,0x51,0x19,0x7a,0x9a,0x46,0xc7, + 0x60,0x09,0x54,0x9e,0xac,0x64,0x74,0xf2,0x06,0xc4,0xee,0x08,0x44,0xf6,0x83,0x89 + }; + const unsigned char nonce[24] = { + 0x69,0x69,0x6e,0xe9,0x55,0xb6,0x2b,0x73,0xcd,0x62,0xbd,0xa8,0x75,0xfc,0x73,0xd6, + 0x82,0x19,0xe0,0x03,0x6b,0x7a,0x0b,0x37 + }; + const char *msg = "Hello from the interop test!"; + unsigned long msglen = strlen(msg); + unsigned char sbox[256], lbox[256], dec[256]; + unsigned long boxlen, declen; + + printf(" sodium create -> ltc open ... "); + CHECK(crypto_secretbox_easy(sbox, (const unsigned char *)msg, msglen, nonce, key)); + declen = sizeof(dec); + CHECK(ltc_secretbox_open(sbox, msglen + 16, nonce, 24, key, 32, dec, &declen)); + CHECK(declen != msglen); + CHECK(memcmp(dec, msg, msglen)); + printf("OK\n"); + printf(" ltc create -> sodium open ... "); + boxlen = sizeof(lbox); + CHECK(ltc_secretbox_create((const unsigned char *)msg, msglen, nonce, 24, key, 32, lbox, &boxlen)); + CHECK(boxlen != msglen + 16); + CHECK(crypto_secretbox_open_easy(dec, lbox, boxlen, nonce, key)); + CHECK(memcmp(dec, msg, msglen)); + printf("OK\n"); + printf(" identical ciphertext ... "); + CHECK(memcmp(sbox, lbox, msglen + 16)); + printf("OK\n"); + return 0; +} + +static int test_cryptobox(void) +{ + unsigned char alice_pk[32], alice_sk[32]; + unsigned char bob_pk[32], bob_sk[32]; + const unsigned char nonce[24] = { + 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c, + 0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18 + }; + const char *msg = "Testing crypto_box interop between libtomcrypt and libsodium."; + unsigned long msglen = strlen(msg); + unsigned char sbox[256], lbox[256], dec[256]; + unsigned long boxlen, declen; + + crypto_box_keypair(alice_pk, alice_sk); + crypto_box_keypair(bob_pk, bob_sk); + printf(" sodium create -> ltc open ... "); + CHECK(crypto_box_easy(sbox, (const unsigned char *)msg, msglen, nonce, bob_pk, alice_sk)); + declen = sizeof(dec); + CHECK(ltc_cryptobox_open(sbox, msglen + 16, nonce, 24, alice_pk, 32, bob_sk, 32, dec, &declen)); + CHECK(declen != msglen); + CHECK(memcmp(dec, msg, msglen)); + printf("OK\n"); + printf(" ltc create -> sodium open ... "); + boxlen = sizeof(lbox); + CHECK(ltc_cryptobox_create((const unsigned char *)msg, msglen, nonce, 24, bob_pk, 32, alice_sk, 32, lbox, &boxlen)); + CHECK(boxlen != msglen + 16); + CHECK(crypto_box_open_easy(dec, lbox, boxlen, nonce, alice_pk, bob_sk)); + CHECK(memcmp(dec, msg, msglen)); + printf("OK\n"); + printf(" identical ciphertext ... "); + CHECK(memcmp(sbox, lbox, msglen + 16)); + printf("OK\n"); + return 0; +} + +static int test_sealbox(void) +{ + unsigned char pk[32], sk[32]; + const char *msg = "Sealed box: anonymous public-key authenticated encryption!"; + unsigned long msglen = strlen(msg); + unsigned long sealedlen_expected = msglen + 48; /* 32 eph_pk + 16 tag */ + unsigned char sealed[256], dec[256]; + unsigned long sealedlen, declen; + prng_state prng; + + crypto_box_keypair(pk, sk); + printf(" sodium create -> ltc open ... "); + CHECK(crypto_box_seal(sealed, (const unsigned char *)msg, msglen, pk)); + declen = sizeof(dec); + CHECK(ltc_sealedbox_open(sealed, sealedlen_expected, sk, 32, dec, &declen)); + CHECK(declen != msglen); + CHECK(memcmp(dec, msg, msglen)); + printf("OK\n"); + printf(" ltc create -> sodium open ... "); + sealedlen = sizeof(sealed); + CHECK(ltc_sealedbox_create((const unsigned char *)msg, msglen, pk, 32, &prng, find_prng("sprng"), sealed, &sealedlen)); + CHECK(sealedlen != sealedlen_expected); + CHECK(crypto_box_seal_open(dec, sealed, sealedlen, pk, sk)); + CHECK(memcmp(dec, msg, msglen)); + printf("OK\n"); + return 0; +} + +static int test_secretbox_ck(void) +{ + /* Demonstrates the keyed (_ck) cryptobox variants and the size-query path. */ + unsigned char alice_pk_raw[32], alice_sk_raw[32]; + unsigned char bob_pk_raw[32], bob_sk_raw[32]; + curve25519_key alice_pk, alice_sk, bob_pk, bob_sk; + const unsigned char nonce[24] = { + 0xaa,0xbb,0xcc,0xdd,0xee,0xff,0x00,0x11,0x22,0x33,0x44,0x55, + 0x66,0x77,0x88,0x99,0xaa,0xbb,0xcc,0xdd,0xee,0xff,0x00,0x11 + }; + const char *msg = "_ck variants accept already-imported curve25519_key objects."; + unsigned long msglen = strlen(msg); + unsigned char sbox[256], lbox[256], dec[256]; + unsigned long boxlen, declen, need; + + crypto_box_keypair(alice_pk_raw, alice_sk_raw); + crypto_box_keypair(bob_pk_raw, bob_sk_raw); + CHECK(x25519_import_raw(alice_pk_raw, 32, PK_PUBLIC, &alice_pk)); + CHECK(x25519_import_raw(alice_sk_raw, 32, PK_PRIVATE, &alice_sk)); + CHECK(x25519_import_raw(bob_pk_raw, 32, PK_PUBLIC, &bob_pk)); + CHECK(x25519_import_raw(bob_sk_raw, 32, PK_PRIVATE, &bob_sk)); + + printf(" size-query (out=NULL) returns required length ... "); + need = 0; + if (ltc_cryptobox_create_ck((const unsigned char *)msg, msglen, nonce, 24, &bob_pk, &alice_sk, NULL, &need) != CRYPT_BUFFER_OVERFLOW) { + fprintf(stderr, "FAIL line %d\n", __LINE__); + return 1; + } + CHECK(need != msglen + 16); + printf("OK\n"); + + printf(" sodium create -> ltc_cryptobox_open_ck ... "); + CHECK(crypto_box_easy(sbox, (const unsigned char *)msg, msglen, nonce, bob_pk_raw, alice_sk_raw)); + declen = sizeof(dec); + CHECK(ltc_cryptobox_open_ck(sbox, msglen + 16, nonce, 24, &alice_pk, &bob_sk, dec, &declen)); + CHECK(declen != msglen); + CHECK(memcmp(dec, msg, msglen)); + printf("OK\n"); + + printf(" ltc_cryptobox_create_ck -> sodium open ... "); + boxlen = sizeof(lbox); + CHECK(ltc_cryptobox_create_ck((const unsigned char *)msg, msglen, nonce, 24, &bob_pk, &alice_sk, lbox, &boxlen)); + CHECK(boxlen != msglen + 16); + CHECK(crypto_box_open_easy(dec, lbox, boxlen, nonce, alice_pk_raw, bob_sk_raw)); + CHECK(memcmp(dec, msg, msglen)); + printf("OK\n"); + + printf(" identical ciphertext ... "); + CHECK(memcmp(sbox, lbox, msglen + 16)); + printf("OK\n"); + return 0; +} + +int main(void) +{ + if (sodium_init() < 0) { + fprintf(stderr, "sodium_init() failed\n"); + return 1; + } + if (register_prng(&sprng_desc) == -1) { + fprintf(stderr, "register_prng(sprng) failed\n"); + return 1; + } + printf("Test 1: secretbox (XSalsa20-Poly1305)\n"); + if (test_secretbox() != 0) return 1; + printf("Test 2: cryptobox (X25519 + HSalsa20 + secretbox)\n"); + if (test_cryptobox() != 0) return 1; + printf("Test 3: sealbox (ephemeral X25519 + BLAKE2b + cryptobox)\n"); + if (test_sealbox() != 0) return 1; + printf("Test 4: cryptobox _ck variants and size-query path\n"); + if (test_secretbox_ck() != 0) return 1; + printf("All tests passed.\n"); + return 0; +} From 0f65af26325e623a8ef3d075f513d494e94010e4 Mon Sep 17 00:00:00 2001 From: Karel Miko Date: Tue, 5 May 2026 13:12:38 +0200 Subject: [PATCH 5/5] Update makefiles --- libtomcrypt_VS2008.vcproj | 28 ++++++++++++++++++++++++++++ makefile.mingw | 16 +++++++++------- makefile.msvc | 16 +++++++++------- makefile.unix | 16 +++++++++------- makefile_include.mk | 16 +++++++++------- sources.cmake | 6 ++++++ 6 files changed, 70 insertions(+), 28 deletions(-) diff --git a/libtomcrypt_VS2008.vcproj b/libtomcrypt_VS2008.vcproj index 11cfac1d5..6db934dc0 100644 --- a/libtomcrypt_VS2008.vcproj +++ b/libtomcrypt_VS2008.vcproj @@ -328,6 +328,34 @@ + + + + + + + + + + + + + + diff --git a/makefile.mingw b/makefile.mingw index df39d6fbc..400c406a4 100644 --- a/makefile.mingw +++ b/makefile.mingw @@ -35,13 +35,15 @@ LIBMAIN_I =libtomcrypt.dll.a LIBMAIN_D =libtomcrypt.dll #List of objects to compile (all goes to libtomcrypt.a) -OBJECTS=src/ciphers/aes/aes.o src/ciphers/aes/aes_desc.o src/ciphers/aes/aes_enc.o \ -src/ciphers/aes/aes_enc_desc.o src/ciphers/aes/aesni.o src/ciphers/anubis.o src/ciphers/blowfish.o \ -src/ciphers/camellia.o src/ciphers/cast5.o src/ciphers/des.o src/ciphers/idea.o src/ciphers/kasumi.o \ -src/ciphers/khazad.o src/ciphers/kseed.o src/ciphers/multi2.o src/ciphers/noekeon.o src/ciphers/rc2.o \ -src/ciphers/rc5.o src/ciphers/rc6.o src/ciphers/safer/safer.o src/ciphers/safer/saferp.o \ -src/ciphers/serpent.o src/ciphers/skipjack.o src/ciphers/sm4.o src/ciphers/tea.o \ -src/ciphers/twofish/twofish.o src/ciphers/xtea.o src/encauth/ccm/ccm_add_aad.o \ +OBJECTS=src/boxes/ltc_cryptobox_create.o src/boxes/ltc_cryptobox_open.o \ +src/boxes/ltc_sealedbox_create.o src/boxes/ltc_sealedbox_open.o src/boxes/ltc_secretbox_create.o \ +src/boxes/ltc_secretbox_open.o src/ciphers/aes/aes.o src/ciphers/aes/aes_desc.o \ +src/ciphers/aes/aes_enc.o src/ciphers/aes/aes_enc_desc.o src/ciphers/aes/aesni.o src/ciphers/anubis.o \ +src/ciphers/blowfish.o src/ciphers/camellia.o src/ciphers/cast5.o src/ciphers/des.o src/ciphers/idea.o \ +src/ciphers/kasumi.o src/ciphers/khazad.o src/ciphers/kseed.o src/ciphers/multi2.o src/ciphers/noekeon.o \ +src/ciphers/rc2.o src/ciphers/rc5.o src/ciphers/rc6.o src/ciphers/safer/safer.o \ +src/ciphers/safer/saferp.o src/ciphers/serpent.o src/ciphers/skipjack.o src/ciphers/sm4.o \ +src/ciphers/tea.o src/ciphers/twofish/twofish.o src/ciphers/xtea.o src/encauth/ccm/ccm_add_aad.o \ src/encauth/ccm/ccm_add_nonce.o src/encauth/ccm/ccm_done.o src/encauth/ccm/ccm_init.o \ src/encauth/ccm/ccm_memory.o src/encauth/ccm/ccm_process.o src/encauth/ccm/ccm_reset.o \ src/encauth/ccm/ccm_test.o src/encauth/chachapoly/chacha20poly1305_add_aad.o \ diff --git a/makefile.msvc b/makefile.msvc index e9028e8ab..f4f3238c4 100644 --- a/makefile.msvc +++ b/makefile.msvc @@ -28,13 +28,15 @@ VERSION=1.18.2-develop LIBMAIN_S =tomcrypt.lib #List of objects to compile (all goes to tomcrypt.lib) -OBJECTS=src/ciphers/aes/aes.obj src/ciphers/aes/aes_desc.obj src/ciphers/aes/aes_enc.obj \ -src/ciphers/aes/aes_enc_desc.obj src/ciphers/aes/aesni.obj src/ciphers/anubis.obj src/ciphers/blowfish.obj \ -src/ciphers/camellia.obj src/ciphers/cast5.obj src/ciphers/des.obj src/ciphers/idea.obj src/ciphers/kasumi.obj \ -src/ciphers/khazad.obj src/ciphers/kseed.obj src/ciphers/multi2.obj src/ciphers/noekeon.obj src/ciphers/rc2.obj \ -src/ciphers/rc5.obj src/ciphers/rc6.obj src/ciphers/safer/safer.obj src/ciphers/safer/saferp.obj \ -src/ciphers/serpent.obj src/ciphers/skipjack.obj src/ciphers/sm4.obj src/ciphers/tea.obj \ -src/ciphers/twofish/twofish.obj src/ciphers/xtea.obj src/encauth/ccm/ccm_add_aad.obj \ +OBJECTS=src/boxes/ltc_cryptobox_create.obj src/boxes/ltc_cryptobox_open.obj \ +src/boxes/ltc_sealedbox_create.obj src/boxes/ltc_sealedbox_open.obj src/boxes/ltc_secretbox_create.obj \ +src/boxes/ltc_secretbox_open.obj src/ciphers/aes/aes.obj src/ciphers/aes/aes_desc.obj \ +src/ciphers/aes/aes_enc.obj src/ciphers/aes/aes_enc_desc.obj src/ciphers/aes/aesni.obj src/ciphers/anubis.obj \ +src/ciphers/blowfish.obj src/ciphers/camellia.obj src/ciphers/cast5.obj src/ciphers/des.obj src/ciphers/idea.obj \ +src/ciphers/kasumi.obj src/ciphers/khazad.obj src/ciphers/kseed.obj src/ciphers/multi2.obj src/ciphers/noekeon.obj \ +src/ciphers/rc2.obj src/ciphers/rc5.obj src/ciphers/rc6.obj src/ciphers/safer/safer.obj \ +src/ciphers/safer/saferp.obj src/ciphers/serpent.obj src/ciphers/skipjack.obj src/ciphers/sm4.obj \ +src/ciphers/tea.obj src/ciphers/twofish/twofish.obj src/ciphers/xtea.obj src/encauth/ccm/ccm_add_aad.obj \ src/encauth/ccm/ccm_add_nonce.obj src/encauth/ccm/ccm_done.obj src/encauth/ccm/ccm_init.obj \ src/encauth/ccm/ccm_memory.obj src/encauth/ccm/ccm_process.obj src/encauth/ccm/ccm_reset.obj \ src/encauth/ccm/ccm_test.obj src/encauth/chachapoly/chacha20poly1305_add_aad.obj \ diff --git a/makefile.unix b/makefile.unix index b07b43b8c..cce82f7c3 100644 --- a/makefile.unix +++ b/makefile.unix @@ -49,13 +49,15 @@ VERSION_PC=1.18.2 LIBMAIN_S =libtomcrypt.a #List of objects to compile (all goes to libtomcrypt.a) -OBJECTS=src/ciphers/aes/aes.o src/ciphers/aes/aes_desc.o src/ciphers/aes/aes_enc.o \ -src/ciphers/aes/aes_enc_desc.o src/ciphers/aes/aesni.o src/ciphers/anubis.o src/ciphers/blowfish.o \ -src/ciphers/camellia.o src/ciphers/cast5.o src/ciphers/des.o src/ciphers/idea.o src/ciphers/kasumi.o \ -src/ciphers/khazad.o src/ciphers/kseed.o src/ciphers/multi2.o src/ciphers/noekeon.o src/ciphers/rc2.o \ -src/ciphers/rc5.o src/ciphers/rc6.o src/ciphers/safer/safer.o src/ciphers/safer/saferp.o \ -src/ciphers/serpent.o src/ciphers/skipjack.o src/ciphers/sm4.o src/ciphers/tea.o \ -src/ciphers/twofish/twofish.o src/ciphers/xtea.o src/encauth/ccm/ccm_add_aad.o \ +OBJECTS=src/boxes/ltc_cryptobox_create.o src/boxes/ltc_cryptobox_open.o \ +src/boxes/ltc_sealedbox_create.o src/boxes/ltc_sealedbox_open.o src/boxes/ltc_secretbox_create.o \ +src/boxes/ltc_secretbox_open.o src/ciphers/aes/aes.o src/ciphers/aes/aes_desc.o \ +src/ciphers/aes/aes_enc.o src/ciphers/aes/aes_enc_desc.o src/ciphers/aes/aesni.o src/ciphers/anubis.o \ +src/ciphers/blowfish.o src/ciphers/camellia.o src/ciphers/cast5.o src/ciphers/des.o src/ciphers/idea.o \ +src/ciphers/kasumi.o src/ciphers/khazad.o src/ciphers/kseed.o src/ciphers/multi2.o src/ciphers/noekeon.o \ +src/ciphers/rc2.o src/ciphers/rc5.o src/ciphers/rc6.o src/ciphers/safer/safer.o \ +src/ciphers/safer/saferp.o src/ciphers/serpent.o src/ciphers/skipjack.o src/ciphers/sm4.o \ +src/ciphers/tea.o src/ciphers/twofish/twofish.o src/ciphers/xtea.o src/encauth/ccm/ccm_add_aad.o \ src/encauth/ccm/ccm_add_nonce.o src/encauth/ccm/ccm_done.o src/encauth/ccm/ccm_init.o \ src/encauth/ccm/ccm_memory.o src/encauth/ccm/ccm_process.o src/encauth/ccm/ccm_reset.o \ src/encauth/ccm/ccm_test.o src/encauth/chachapoly/chacha20poly1305_add_aad.o \ diff --git a/makefile_include.mk b/makefile_include.mk index 0577b5bda..3ff91046c 100644 --- a/makefile_include.mk +++ b/makefile_include.mk @@ -220,13 +220,15 @@ library: $(call print-help,library,Builds the library) $(LIBNAME) # List of objects to compile (all goes to libtomcrypt.a) ifndef AMALGAM -OBJECTS=src/ciphers/aes/aes.o src/ciphers/aes/aes_desc.o src/ciphers/aes/aes_enc.o \ -src/ciphers/aes/aes_enc_desc.o src/ciphers/aes/aesni.o src/ciphers/anubis.o src/ciphers/blowfish.o \ -src/ciphers/camellia.o src/ciphers/cast5.o src/ciphers/des.o src/ciphers/idea.o src/ciphers/kasumi.o \ -src/ciphers/khazad.o src/ciphers/kseed.o src/ciphers/multi2.o src/ciphers/noekeon.o src/ciphers/rc2.o \ -src/ciphers/rc5.o src/ciphers/rc6.o src/ciphers/safer/safer.o src/ciphers/safer/saferp.o \ -src/ciphers/serpent.o src/ciphers/skipjack.o src/ciphers/sm4.o src/ciphers/tea.o \ -src/ciphers/twofish/twofish.o src/ciphers/xtea.o src/encauth/ccm/ccm_add_aad.o \ +OBJECTS=src/boxes/ltc_cryptobox_create.o src/boxes/ltc_cryptobox_open.o \ +src/boxes/ltc_sealedbox_create.o src/boxes/ltc_sealedbox_open.o src/boxes/ltc_secretbox_create.o \ +src/boxes/ltc_secretbox_open.o src/ciphers/aes/aes.o src/ciphers/aes/aes_desc.o \ +src/ciphers/aes/aes_enc.o src/ciphers/aes/aes_enc_desc.o src/ciphers/aes/aesni.o src/ciphers/anubis.o \ +src/ciphers/blowfish.o src/ciphers/camellia.o src/ciphers/cast5.o src/ciphers/des.o src/ciphers/idea.o \ +src/ciphers/kasumi.o src/ciphers/khazad.o src/ciphers/kseed.o src/ciphers/multi2.o src/ciphers/noekeon.o \ +src/ciphers/rc2.o src/ciphers/rc5.o src/ciphers/rc6.o src/ciphers/safer/safer.o \ +src/ciphers/safer/saferp.o src/ciphers/serpent.o src/ciphers/skipjack.o src/ciphers/sm4.o \ +src/ciphers/tea.o src/ciphers/twofish/twofish.o src/ciphers/xtea.o src/encauth/ccm/ccm_add_aad.o \ src/encauth/ccm/ccm_add_nonce.o src/encauth/ccm/ccm_done.o src/encauth/ccm/ccm_init.o \ src/encauth/ccm/ccm_memory.o src/encauth/ccm/ccm_process.o src/encauth/ccm/ccm_reset.o \ src/encauth/ccm/ccm_test.o src/encauth/chachapoly/chacha20poly1305_add_aad.o \ diff --git a/sources.cmake b/sources.cmake index 0d3ef35e0..15f09f2f6 100644 --- a/sources.cmake +++ b/sources.cmake @@ -1,4 +1,10 @@ set(SOURCES +src/boxes/ltc_cryptobox_create.c +src/boxes/ltc_cryptobox_open.c +src/boxes/ltc_sealedbox_create.c +src/boxes/ltc_sealedbox_open.c +src/boxes/ltc_secretbox_create.c +src/boxes/ltc_secretbox_open.c src/ciphers/aes/aes.c src/ciphers/aes/aes_desc.c src/ciphers/aes/aes_tab.c