XSalsa20
XSalsa20
Salsa20 with a nonce long enough to pick at random, the NaCl one.
- Keyspace
- 2^256
- Decode
- same options back
- Works on
- UTF-8 or hex, hex out
- Family
- 6 in arx
Options
Access
- Create
create("xsalsa20") - CLI
ciphers encode xsalsa20 'ATTACK AT DAWN' --key 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f --nonce 000102030405060708090a0b0c0d0e0f1011121314151617 - Tryplayground with the sample above
- Kinrabbit, salsa20, chacha20, xchacha20 +1
Eight bytes of nonce is a problem. Pick them at random and two messages share one after about four billion messages. That's the birthday bound, and four billion isn't that many. Bernstein's fix, in "Extending the Salsa20 nonce" from 2008, was XSalsa20. NaCl and libsodium encrypt crypto_secretbox with it.
How it runs
The nonce is 24 bytes. The first 16 go through HSalsa20 with the key. That's the Salsa20 rounds on a state where nonce and counter would be, except nothing gets added back at the end. Eight of the output words become a new 256-bit key.
Then plain Salsa20 runs under that subkey, with the last 8 nonce bytes as its nonce. One extra pass of the rounds per message, and that's the whole cost.
Why does it work? A different nonce gives a different subkey. Collisions now need 24 equal bytes, and that's far enough for random nonces.
Keys, nonce and counter
The key is 64 hex digits, 256 bits only. nonce is 48 hex digits and required. counter works as in Salsa20, default 0.
const xsalsa = create("xsalsa20");
const key = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f";
const nonce = "000102030405060708090a0b0c0d0e0f1011121314151617";
xsalsa.encode("ATTACK AT DAWN", { key, nonce }).text; // "3de234ee9ed5e607db77992c737d"
xsalsa.decode("3de234ee9ed5e607db77992c737d", { key, nonce }).text; // "ATTACK AT DAWN"
Got a NaCl secretbox? The first 32 bytes of its stream go to the Poly1305 key, and the message starts after them. So counter: 0 gives you the MAC key, and the text sits at byte 32 of block 0. This package doesn't open secretboxes. It gives you the stream to do it by hand.
xsalsa20(data, key, nonce, counter) from @agntn/ciphers/salsa runs the same thing on Uint8Array.
Checked against
Two of the XSalsa20 vectors in Crypto++, which Wei Dai made with naclcrypto-20090308. The 139-byte stream of the first by digest, the other one both ways.
A key that isn't 64 hex digits is an InvalidOptionError, and so is a nonce that isn't 48 or a bad counter. A missing key or nonce is a MissingOptionError.
Random nonces stop being scary here. A reused one is still as bad as in Salsa20, and there's still no tag. Puzzles and learning only.