Uh oh!
There was an error while loading. Please reload this page.
XSalsa20: make hsalsa20 a public API function - #728
Conversation
karel-m
commented
Apr 16, 2026
If anybody is interested in how interoperability between libtomcrypt and libsodium works, here it is: /* Interoperability test between libtomcrypt and libsodium for: 1. secretbox (XSalsa20-Poly1305) 2. cryptobox (X25519 + HSalsa20 + XSalsa20-Poly1305) 3. sealbox (ephemeral X25519 + BLAKE2b nonce + cryptobox)*/#include<stdio.h>#include<string.h>#include<tomcrypt.h>#include<sodium.h>#defineCHECK(e) do { if ((e) != 0) { fprintf(stderr, "FAIL line %d\n", __LINE__); return 1; } } while(0)
staticintltc_secretbox_create(unsigned char*out, constunsigned char*msg, unsigned longmsglen, constunsigned charnonce[24], constunsigned charkey[32])
{
salsa20_statest;
poly1305_statepoly;
unsigned charpolykey[32];
unsigned longtaglen=16;
interr;
if ((err=xsalsa20_setup(&st, key, 32, nonce, 24, 20)) !=CRYPT_OK) returnerr;
if ((err=salsa20_keystream(&st, polykey, 32)) !=CRYPT_OK) { salsa20_done(&st); returnerr; }
if ((err=salsa20_crypt(&st, msg, msglen, out+16)) !=CRYPT_OK) { salsa20_done(&st); returnerr; }
salsa20_done(&st);
if ((err=poly1305_init(&poly, polykey, 32)) !=CRYPT_OK) returnerr;
if ((err=poly1305_process(&poly, out+16, msglen)) !=CRYPT_OK) returnerr;
if ((err=poly1305_done(&poly, out, &taglen)) !=CRYPT_OK) returnerr;
returnCRYPT_OK;
}
staticintltc_secretbox_open(unsigned char*msg, constunsigned char*box, unsigned longboxlen, constunsigned charnonce[24], constunsigned charkey[32])
{
salsa20_statest;
poly1305_statepoly;
unsigned charpolykey[32], tag[16];
unsigned longtaglen=16, msglen;
interr;
if (boxlen<16) returnCRYPT_ERROR;
msglen=boxlen-16;
if ((err=xsalsa20_setup(&st, key, 32, nonce, 24, 20)) !=CRYPT_OK) returnerr;
if ((err=salsa20_keystream(&st, polykey, 32)) !=CRYPT_OK) { salsa20_done(&st); returnerr; }
if ((err=poly1305_init(&poly, polykey, 32)) !=CRYPT_OK) { salsa20_done(&st); returnerr; }
if ((err=poly1305_process(&poly, box+16, msglen)) !=CRYPT_OK) { salsa20_done(&st); returnerr; }
if ((err=poly1305_done(&poly, tag, &taglen)) !=CRYPT_OK) { salsa20_done(&st); returnerr; }
if (memcmp(tag, box, 16) !=0) { salsa20_done(&st); returnCRYPT_ERROR; }
if ((err=salsa20_crypt(&st, box+16, msglen, msg)) !=CRYPT_OK) { salsa20_done(&st); returnerr; }
salsa20_done(&st);
returnCRYPT_OK;
}
staticintltc_cryptobox_create(unsigned char*out, constunsigned char*msg, unsigned longmsglen, constunsigned charnonce[24], constunsigned charrecipient_pk[32], constunsigned charsender_sk[32])
{
curve25519_keysk, pk;
unsigned charshared[32], symkey[32];
staticconstunsigned charzero16[16] = {0};
unsigned longshared_len=32;
interr;
if ((err=x25519_import_raw(sender_sk, 32, PK_PRIVATE, &sk)) !=CRYPT_OK) returnerr;
if ((err=x25519_import_raw(recipient_pk, 32, PK_PUBLIC, &pk)) !=CRYPT_OK) returnerr;
if ((err=x25519_shared_secret(&sk, &pk, shared, &shared_len)) !=CRYPT_OK) returnerr;
if ((err=xsalsa20_hsalsa20(symkey, 32, shared, 32, zero16, 16, 20)) !=CRYPT_OK) returnerr;
returnltc_secretbox_create(out, msg, msglen, nonce, symkey);
}
staticintltc_cryptobox_open(unsigned char*msg, constunsigned char*box, unsigned longboxlen, constunsigned charnonce[24], constunsigned charsender_pk[32], constunsigned charrecipient_sk[32])
{
curve25519_keysk, pk;
unsigned charshared[32], symkey[32];
staticconstunsigned charzero16[16] = {0};
unsigned longshared_len=32;
interr;
if ((err=x25519_import_raw(recipient_sk, 32, PK_PRIVATE, &sk)) !=CRYPT_OK) returnerr;
if ((err=x25519_import_raw(sender_pk, 32, PK_PUBLIC, &pk)) !=CRYPT_OK) returnerr;
if ((err=x25519_shared_secret(&sk, &pk, shared, &shared_len)) !=CRYPT_OK) returnerr;
if ((err=xsalsa20_hsalsa20(symkey, 32, shared, 32, zero16, 16, 20)) !=CRYPT_OK) returnerr;
returnltc_secretbox_open(msg, box, boxlen, nonce, symkey);
}
staticvoidltc_seal_nonce(unsigned charnonce[24], constunsigned chareph_pk[32], constunsigned charrecipient_pk[32])
{
blake2bmac_statebst;
unsigned longnoncelen=24;
blake2bmac_init(&bst, 24, NULL, 0);
blake2bmac_process(&bst, eph_pk, 32);
blake2bmac_process(&bst, recipient_pk, 32);
blake2bmac_done(&bst, nonce, &noncelen);
}
staticintltc_sealbox_create(unsigned char*out, constunsigned char*msg, unsigned longmsglen, constunsigned charrecipient_pk[32])
{
curve25519_keyeph;
prng_stateprng;
unsigned charnonce[24];
intprng_idx, err;
prng_idx=find_prng("sprng");
if (prng_idx==-1) returnCRYPT_ERROR;
if ((err=x25519_make_key(&prng, prng_idx, &eph)) !=CRYPT_OK) returnerr;
memcpy(out, eph.pub, 32);
ltc_seal_nonce(nonce, eph.pub, recipient_pk);
returnltc_cryptobox_create(out+32, msg, msglen, nonce, recipient_pk, eph.priv);
}
staticintltc_sealbox_open(unsigned char*msg, constunsigned char*box, unsigned longboxlen, constunsigned charrecipient_pk[32], constunsigned charrecipient_sk[32])
{
unsigned charnonce[24];
if (boxlen<48) returnCRYPT_ERROR;
ltc_seal_nonce(nonce, box, recipient_pk);
returnltc_cryptobox_open(msg, box+32, boxlen-32, nonce, box, recipient_sk);
}
staticinttest_secretbox(void)
{
constunsigned charkey[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
};
constunsigned charnonce[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
};
constchar*msg="Hello from the interop test!";
unsigned longmsglen=strlen(msg);
unsigned charsbox[256], lbox[256], dec[256];
printf(" sodium create -> ltc open ... ");
CHECK(crypto_secretbox_easy(sbox, (constunsigned char*)msg, msglen, nonce, key));
CHECK(ltc_secretbox_open(dec, sbox, msglen+16, nonce, key));
CHECK(memcmp(dec, msg, msglen));
printf("OK\n");
printf(" ltc create -> sodium open ... ");
CHECK(ltc_secretbox_create(lbox, (constunsigned char*)msg, msglen, nonce, key));
CHECK(crypto_secretbox_open_easy(dec, lbox, msglen+16, nonce, key));
CHECK(memcmp(dec, msg, msglen));
printf("OK\n");
printf(" identical ciphertext ... ");
CHECK(memcmp(sbox, lbox, msglen+16));
printf("OK\n");
return0;
}
staticinttest_cryptobox(void)
{
unsigned charalice_pk[32], alice_sk[32];
unsigned charbob_pk[32], bob_sk[32];
constunsigned charnonce[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
};
constchar*msg="Testing crypto_box interop between libtomcrypt and libsodium.";
unsigned longmsglen=strlen(msg);
unsigned charsbox[256], lbox[256], dec[256];
crypto_box_keypair(alice_pk, alice_sk);
crypto_box_keypair(bob_pk, bob_sk);
printf(" sodium create -> ltc open ... ");
CHECK(crypto_box_easy(sbox, (constunsigned char*)msg, msglen, nonce, bob_pk, alice_sk));
CHECK(ltc_cryptobox_open(dec, sbox, msglen+16, nonce, alice_pk, bob_sk));
CHECK(memcmp(dec, msg, msglen));
printf("OK\n");
printf(" ltc create -> sodium open ... ");
CHECK(ltc_cryptobox_create(lbox, (constunsigned char*)msg, msglen, nonce, bob_pk, alice_sk));
CHECK(crypto_box_open_easy(dec, lbox, msglen+16, 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");
return0;
}
staticinttest_sealbox(void)
{
unsigned charpk[32], sk[32];
constchar*msg="Sealed box: anonymous public-key authenticated encryption!";
unsigned longmsglen=strlen(msg);
unsigned longsealedlen=msglen+48; /* 32 eph_pk + 16 tag */unsigned charsealed[256], dec[256];
crypto_box_keypair(pk, sk);
printf(" sodium create -> ltc open ... ");
CHECK(crypto_box_seal(sealed, (constunsigned char*)msg, msglen, pk));
CHECK(ltc_sealbox_open(dec, sealed, sealedlen, pk, sk));
CHECK(memcmp(dec, msg, msglen));
printf("OK\n");
printf(" ltc create -> sodium open ... ");
CHECK(ltc_sealbox_create(sealed, (constunsigned char*)msg, msglen, pk));
CHECK(crypto_box_seal_open(dec, sealed, sealedlen, pk, sk));
CHECK(memcmp(dec, msg, msglen));
printf("OK\n");
return0;
}
intmain(void)
{
if (sodium_init() <0) {
fprintf(stderr, "sodium_init() failed\n");
return1;
}
if (register_prng(&sprng_desc) ==-1) {
fprintf(stderr, "register_prng(sprng) failed\n");
return1;
}
printf("Test 1: secretbox (XSalsa20-Poly1305)\n");
if (test_secretbox() !=0) return1;
printf("Test 2: cryptobox (X25519 + HSalsa20 + secretbox)\n");
if (test_cryptobox() !=0) return1;
printf("Test 3: sealbox (ephemeral X25519 + BLAKE2b + cryptobox)\n");
if (test_sealbox() !=0) return1;
printf("All tests passed.\n");
return0;
} |
sjaeckel
commented
Apr 24, 2026
How about adding the libsodium API under an |
do you mean having something like this in our public API? intltc_secretbox_create(constunsigned char*msg, unsigned longmsglen,
constunsigned char*nonce, unsigned longnoncelen,
constunsigned char*key, unsigned longkeylen,
unsigned char*out, unsigned long*outlen);
intltc_secretbox_open(constunsigned char*enc, unsigned longenclen,
constunsigned char*nonce, unsigned longnoncelen,
constunsigned char*key, unsigned longkeylen,
unsigned char*out, unsigned long*outlen);
intltc_cryptobox_create(constunsigned char*msg, unsigned longmsglen,
constunsigned char*nonce, unsigned longnoncelen,
constunsigned char*pk, unsigned longpklen,
constunsigned char*sk, unsigned longsklen,
unsigned char*out, unsigned long*outlen);
intltc_cryptobox_open(constunsigned char*enc, unsigned longenclen,
constunsigned char*nonce, unsigned longnoncelen,
constunsigned char*pk, unsigned longpklen,
constunsigned char*sk, unsigned longsklen,
unsigned char*out, unsigned long*outlen);
intltc_sealedbox_create(constunsigned char*msg, unsigned longmsglen,
constunsigned char*pk, unsigned longpklen,
unsigned char*out, unsigned long*outlen);
intltc_sealedbox_open(constunsigned char*enc, unsigned longenclen,
constunsigned char*pk, unsigned longpklen,
constunsigned char*sk, unsigned longsklen,
unsigned char*out, unsigned long*outlen);FYI here is how original libsodium api looks like: |
sjaeckel
commented
Apr 29, 2026
I like that API proposal. Not sure though if we should add it as an official API or as an inofficial one in demos, like the gcm-file one ... if we add it, we should also have it tested. (Which I will add now for the gcm-file one.) |
karel-m
commented
Apr 29, 2026
for now, maybe |
I'd prefer to directly separate the demo from the boxes and put the boxes to a separate folder ... |
0f65af2 to
711a67dCompare711a67d to
466a2c5Compare
sjaeckel
left a comment
There was a problem hiding this comment.
Basically LGTM
If we put the boxes into the library we should also have them in the docs. (That's also why I suggested to have the boxes in the demos instead of the library, but I'm fine with both.)
We should also run the demo in CI (or something like that ... maybe turn it into a testcase or so...) to ensure that this doesn't break, especially if the boxes API is provided as an official API.
While checking the interoperability of libtomcrypt with NaCl/libsodium seal box, crypto box and secret box, I ended up with this patch.
HSalsa20 was a missing piece needed to implement NaCl's crypto_box (= authenticated public-key encryption using X25519 + HSalsa20 + XSalsa20-Poly1305).
Technically HSalsa20 was already implemented inside
xsalsa20_setup(..)- this PR simply exposes it asxsalsa20_hsalsa20(..)as part of the public API.NaCl sealed box (crypto_box_seal) needed unkeyed BLAKE2b. However,
blake2bmac_init()rejected (NULL, 0) even thoughblake2b_init()supports it. This PR allows NULL key when keylen is 0 (in line with the BLAKE2 specification).Checklist