XSalsa20: make hsalsa20 a public API function - #728
Conversation
|
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>
#define CHECK(e) do { if ((e) != 0) { fprintf(stderr, "FAIL line %d\n", __LINE__); return 1; } } while(0)
static int ltc_secretbox_create(unsigned char *out, const unsigned char *msg, unsigned long msglen, const unsigned char nonce[24], const unsigned char key[32])
{
salsa20_state st;
poly1305_state poly;
unsigned char polykey[32];
unsigned long taglen = 16;
int err;
if ((err = xsalsa20_setup(&st, key, 32, nonce, 24, 20)) != CRYPT_OK) return err;
if ((err = salsa20_keystream(&st, polykey, 32)) != CRYPT_OK) { salsa20_done(&st); return err; }
if ((err = salsa20_crypt(&st, msg, msglen, out + 16)) != CRYPT_OK) { salsa20_done(&st); return err; }
salsa20_done(&st);
if ((err = poly1305_init(&poly, polykey, 32)) != CRYPT_OK) return err;
if ((err = poly1305_process(&poly, out + 16, msglen)) != CRYPT_OK) return err;
if ((err = poly1305_done(&poly, out, &taglen)) != CRYPT_OK) return err;
return CRYPT_OK;
}
static int ltc_secretbox_open(unsigned char *msg, const unsigned char *box, unsigned long boxlen, const unsigned char nonce[24], const unsigned char key[32])
{
salsa20_state st;
poly1305_state poly;
unsigned char polykey[32], tag[16];
unsigned long taglen = 16, msglen;
int err;
if (boxlen < 16) return CRYPT_ERROR;
msglen = boxlen - 16;
if ((err = xsalsa20_setup(&st, key, 32, nonce, 24, 20)) != CRYPT_OK) return err;
if ((err = salsa20_keystream(&st, polykey, 32)) != CRYPT_OK) { salsa20_done(&st); return err; }
if ((err = poly1305_init(&poly, polykey, 32)) != CRYPT_OK) { salsa20_done(&st); return err; }
if ((err = poly1305_process(&poly, box + 16, msglen)) != CRYPT_OK) { salsa20_done(&st); return err; }
if ((err = poly1305_done(&poly, tag, &taglen)) != CRYPT_OK) { salsa20_done(&st); return err; }
if (memcmp(tag, box, 16) != 0) { salsa20_done(&st); return CRYPT_ERROR; }
if ((err = salsa20_crypt(&st, box + 16, msglen, msg)) != CRYPT_OK) { salsa20_done(&st); return err; }
salsa20_done(&st);
return CRYPT_OK;
}
static int ltc_cryptobox_create(unsigned char *out, const unsigned char *msg, unsigned long msglen, const unsigned char nonce[24], const unsigned char recipient_pk[32], const unsigned char sender_sk[32])
{
curve25519_key sk, pk;
unsigned char shared[32], symkey[32];
static const unsigned char zero16[16] = {0};
unsigned long shared_len = 32;
int err;
if ((err = x25519_import_raw(sender_sk, 32, PK_PRIVATE, &sk)) != CRYPT_OK) return err;
if ((err = x25519_import_raw(recipient_pk, 32, PK_PUBLIC, &pk)) != CRYPT_OK) return err;
if ((err = x25519_shared_secret(&sk, &pk, shared, &shared_len)) != CRYPT_OK) return err;
if ((err = xsalsa20_hsalsa20(symkey, 32, shared, 32, zero16, 16, 20)) != CRYPT_OK) return err;
return ltc_secretbox_create(out, msg, msglen, nonce, symkey);
}
static int ltc_cryptobox_open(unsigned char *msg, const unsigned char *box, unsigned long boxlen, const unsigned char nonce[24], const unsigned char sender_pk[32], const unsigned char recipient_sk[32])
{
curve25519_key sk, pk;
unsigned char shared[32], symkey[32];
static const unsigned char zero16[16] = {0};
unsigned long shared_len = 32;
int err;
if ((err = x25519_import_raw(recipient_sk, 32, PK_PRIVATE, &sk)) != CRYPT_OK) return err;
if ((err = x25519_import_raw(sender_pk, 32, PK_PUBLIC, &pk)) != CRYPT_OK) return err;
if ((err = x25519_shared_secret(&sk, &pk, shared, &shared_len)) != CRYPT_OK) return err;
if ((err = xsalsa20_hsalsa20(symkey, 32, shared, 32, zero16, 16, 20)) != CRYPT_OK) return err;
return ltc_secretbox_open(msg, box, boxlen, nonce, symkey);
}
static void ltc_seal_nonce(unsigned char nonce[24], const unsigned char eph_pk[32], const unsigned char recipient_pk[32])
{
blake2bmac_state bst;
unsigned long noncelen = 24;
blake2bmac_init(&bst, 24, NULL, 0);
blake2bmac_process(&bst, eph_pk, 32);
blake2bmac_process(&bst, recipient_pk, 32);
blake2bmac_done(&bst, nonce, &noncelen);
}
static int ltc_sealbox_create(unsigned char *out, const unsigned char *msg, unsigned long msglen, const unsigned char recipient_pk[32])
{
curve25519_key eph;
prng_state prng;
unsigned char nonce[24];
int prng_idx, err;
prng_idx = find_prng("sprng");
if (prng_idx == -1) return CRYPT_ERROR;
if ((err = x25519_make_key(&prng, prng_idx, &eph)) != CRYPT_OK) return err;
memcpy(out, eph.pub, 32);
ltc_seal_nonce(nonce, eph.pub, recipient_pk);
return ltc_cryptobox_create(out + 32, msg, msglen, nonce, recipient_pk, eph.priv);
}
static int ltc_sealbox_open(unsigned char *msg, const unsigned char *box, unsigned long boxlen, const unsigned char recipient_pk[32], const unsigned char recipient_sk[32])
{
unsigned char nonce[24];
if (boxlen < 48) return CRYPT_ERROR;
ltc_seal_nonce(nonce, box, recipient_pk);
return ltc_cryptobox_open(msg, box + 32, boxlen - 32, nonce, box, recipient_sk);
}
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];
printf(" sodium create -> ltc open ... ");
CHECK(crypto_secretbox_easy(sbox, (const unsigned 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, (const unsigned 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");
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];
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));
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, (const unsigned 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");
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 = msglen + 48; /* 32 eph_pk + 16 tag */
unsigned char sealed[256], dec[256];
crypto_box_keypair(pk, sk);
printf(" sodium create -> ltc open ... ");
CHECK(crypto_box_seal(sealed, (const unsigned 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, (const unsigned char *)msg, msglen, pk));
CHECK(crypto_box_seal_open(dec, sealed, sealedlen, pk, sk));
CHECK(memcmp(dec, msg, msglen));
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("All tests passed.\n");
return 0;
} |
|
How about adding the libsodium API under an |
|
do you mean having something like this in our public API? 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);
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_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_sealedbox_create(const unsigned char *msg, unsigned long msglen,
const unsigned char *pk, unsigned long pklen,
unsigned char *out, unsigned long *outlen);
int ltc_sealedbox_open(const unsigned char *enc, unsigned long enclen,
const unsigned char *pk, unsigned long pklen,
const unsigned char *sk, unsigned long sklen,
unsigned char *out, unsigned long *outlen);FYI here is how original libsodium api looks like: |
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.) |
|
for now, maybe |
|
I'd prefer to directly separate the demo from the boxes and put the boxes to a separate folder ... |
93fa641 to
0f65af2
Compare
0f65af2 to
711a67d
Compare
711a67d to
466a2c5
Compare
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