diff --git a/doc/crypt.tex b/doc/crypt.tex
index a6b470cba..0b199fac2 100644
--- a/doc/crypt.tex
+++ b/doc/crypt.tex
@@ -1301,49 +1301,85 @@ \chapter{Stream Ciphers}
err = chacha_done(&st);
\end{verbatim}
-\mysection{Salsa20}
+\mysection{Salsa20 and XSalsa20}
+
+\textit{Salsa20} was Daniel Bernstein's submission to the EU eSTREAM
+competition where a reduced-round version, \textit{Salsa20/12}, was named
+one of the winners. A third version, \textit{Salsa20/8}, was also evaluated.
+\vspace{1mm}
+
+While 20 rounds is the conservative default number of rounds, eSTREAM deemed
+12 rounds to be a decent balance between strength and better performance.
+The 8-round version, while still secure as of this writing, is faster but
+does not enjoy the same margin of safety. Regardless of the number of rounds,
+\textit{Salsa20} accepts either a 128- or a 256-bit key, a 64-bit IV, and a
+64-bit counter.
+\vspace{1mm}
+
+\textit{XSalsa20} is yet another variant of \textit{Salsa20} designed to accept
+only a 256-bit key and a longer 192-bit nonce, initialization being the only
+difference between \textit{XSalsa20} and \textit{Salsa20}. Even the
+\textit{salsa20\_state} is the same. Thereafter, salsa20\_crypt(),
+salsa20\_keystream(), and salsa20\_done() are used unaltered.
+salsa20\_ivctr64() is NOT used with xsalsa20\_setup().
+\vspace{1mm}
+
+To initialize \textit{Salsa20} for 8, 12, or 20 rounds with a 128- or a
+256-bit key (16 or 32 bytes), a 64-bit IV (8 bytes), and counter (typically
+zero), use:
-\textit{Salsa20} is the forerunner of the ChaCha stream cipher. The ChaCha cipher is
-Salsa20 with a few minor tweaks to further improve its strength, and in so doing, increase its
-speed performance by about 5 percent. Unless you need Salsa20 for some reason, you should
-probably choose ChaCha instead.
-
-In April 2008 \textit{Salsa20/12} was named one of the winners in the EU eSTREAM competition.
-Salsa20 was originally submitted by Daniel Bernstein with 20 rounds of strength but the
-12-round reduced-round version was deemed to have sufficient strength and declared a winner.
-Even the 8-round reduced-round version, Salsa20/8, has withstood attack.
-
-For more information about Salsa20 see \url{https://en.wikipedia.org/wiki/Salsa20}.
-
-Supported key size: 16 or 32 bytes (128 or 256 bits).
-
-You can initialize Salsa20 with 64bit \textit{nonce} + 64bit \textit{counter}:
\begin{verbatim}
salsa20_state st;
+ulong64 counter = 0;
err = salsa20_setup(&st, key, key_len, rounds);
-err = salsa20_ivctr64(&st, nonce, 8, initial_64bit_ctr);
+err = salsa20_ivctr64(&st, nonce, 8, counter);
\end{verbatim}
-The \textit{salsa20\_setup} takes the number of rounds as a parameter -- choose 20 (the default)
-if you are not sure. As always never ever use the same key + nonce pair more than once.
+To initialize \textit{XSalsa20} for the recommended 20 rounds with a 256-bit
+key (32 bytes) and a 192-bit nonce (24 bytes), use:
-For the actual encryption or decryption you have to call:
+\begin{verbatim}
+salsa20_state st;
+err = xsalsa20_setup(&st, key, key_len, nonce, nonce_len, rounds);
+\end{verbatim}
+
+Both \textit{Salsa20} and \textit{XSalsa20} use the following functions. To
+encrypt or decrypt call:
\begin{verbatim}
err = salsa20_crypt(&st, in_buffer, in_len, out_buffer);
\end{verbatim}
-If you just want a random stream of bytes initialize the cipher with a truly random \textit{key}
-(32 bytes), a truly random \textit{nonce} (8 bytes) and zero initial counter. After that you can
-get a stream of pseudo--random bytes via:
+For a random keystream initialize the cipher with a truly random \textit{key}
+and random \textit{nonce} after which you can get a stream of
+pseudo--random bytes via:
\begin{verbatim}
err = salsa20_keystream(&st, out_buffer, out_len);
\end{verbatim}
-When finished you should wipe the state:
+Finally, when finished you should wipe the state.
\begin{verbatim}
err = salsa20_done(&st);
\end{verbatim}
+For both \textit{Salsa20} and \textit{XSalsa20} rounds must be an even number
+and if set to 0 the default number of rounds, 20, will be used.
+\vspace{1mm}
+
+If you define \textit{LTC_XSALSA20} to include \textit{XSalsa20} in a minimal
+\textit{libtomcrypt} library build, you must also define \textit{LTC_SALSA20}.
+\vspace{1mm}
+
+As always, never ever use the same key + nonce/IV pair more than once.
+\vspace{1mm}
+
+For more information about Salsa20 see
+\url{https://en.wikipedia.org/wiki/Salsa20}.
+\vspace{1mm}
+
+For more information about XSalsa20 see
+\url{https://cr.yp.to/snuffle/xsalsa-20081128.pdf}.
+\vspace{1mm}
+
\mysection{Sosemanuk}
\textit{Sosemanuk}, along with Salsa20, HC-128, and Rabbit, was named one of the winners in
diff --git a/libtomcrypt_VS2008.vcproj b/libtomcrypt_VS2008.vcproj
index ccdc8420e..ed97a90ca 100644
--- a/libtomcrypt_VS2008.vcproj
+++ b/libtomcrypt_VS2008.vcproj
@@ -2634,6 +2634,14 @@
RelativePath="src\stream\salsa20\salsa20_test.c"
>
+
+
+
+
ivlen == 8);
+ LTC_ARGCHK(st->ivlen == 8 || st->ivlen == 24);
if (st->ksleft > 0) {
j = MIN(st->ksleft, inlen);
diff --git a/src/stream/salsa20/xsalsa20_setup.c b/src/stream/salsa20/xsalsa20_setup.c
new file mode 100755
index 000000000..d89d65e2f
--- /dev/null
+++ b/src/stream/salsa20/xsalsa20_setup.c
@@ -0,0 +1,137 @@
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ */
+
+/* The implementation is based on:
+ * "Extending the Salsa20 nonce", https://cr.yp.to/snuffle/xsalsa-20081128.pdf
+ * "Salsa20 specification", http://cr.yp.to/snuffle/spec.pdf
+ * and salsa20-ref.c version 20051118
+ * Public domain from D. J. Bernstein
+ */
+
+#include "tomcrypt.h"
+
+#ifdef LTC_XSALSA20
+
+static const char * const constants = "expand 32-byte k";
+
+#define QUARTERROUND(a,b,c,d) \
+ x[b] ^= (ROL((x[a] + x[d]), 7)); \
+ x[c] ^= (ROL((x[b] + x[a]), 9)); \
+ x[d] ^= (ROL((x[c] + x[b]), 13)); \
+ x[a] ^= (ROL((x[d] + x[c]), 18));
+
+/* use modified salsa20 doubleround (no final addition as in salsa20) */
+static void _xsalsa20_doubleround(ulong32 *x, int rounds)
+{
+ int i = 0;
+
+ for (i = rounds; i > 0; i -= 2) {
+ /* columnround */
+ QUARTERROUND( 0, 4, 8,12)
+ QUARTERROUND( 5, 9,13, 1)
+ QUARTERROUND(10,14, 2, 6)
+ QUARTERROUND(15, 3, 7,11)
+ /* rowround */
+ QUARTERROUND( 0, 1, 2, 3)
+ QUARTERROUND( 5, 6, 7, 4)
+ QUARTERROUND(10,11, 8, 9)
+ QUARTERROUND(15,12,13,14)
+ }
+}
+
+#undef QUARTERROUND
+
+/**
+ 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 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];
+ int i = 0;
+
+ LTC_ARGCHK(st != NULL);
+ LTC_ARGCHK(key != NULL);
+ LTC_ARGCHK(keylen == 32);
+ LTC_ARGCHK(nonce != NULL);
+ LTC_ARGCHK(noncelen == 24);
+ if (rounds == 0) rounds = 20;
+ LTC_ARGCHK(rounds % 2 == 0); /* number of rounds must be evenly divisible by 2 */
+
+ /* load the state to "hash" the key */
+ LOAD32L(x[ 0], constants + 0);
+ LOAD32L(x[ 5], constants + 4);
+ LOAD32L(x[10], constants + 8);
+ LOAD32L(x[15], constants + 12);
+ LOAD32L(x[ 1], key + 0);
+ LOAD32L(x[ 2], key + 4);
+ LOAD32L(x[ 3], key + 8);
+ LOAD32L(x[ 4], key + 12);
+ LOAD32L(x[11], key + 16);
+ 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);
+
+ /* use modified salsa20 doubleround (no final addition) */
+ _xsalsa20_doubleround(x, rounds);
+
+ /* extract the subkey */
+ for (i = 0; i < 8; ++i) {
+ STORE32L(x[sti[i]], subkey + 4 * i);
+ }
+
+ /* load the final initial state */
+ LOAD32L(st->input[ 0], constants + 0);
+ LOAD32L(st->input[ 5], constants + 4);
+ LOAD32L(st->input[10], constants + 8);
+ LOAD32L(st->input[15], constants + 12);
+ LOAD32L(st->input[ 1], subkey + 0);
+ LOAD32L(st->input[ 2], subkey + 4);
+ LOAD32L(st->input[ 3], subkey + 8);
+ LOAD32L(st->input[ 4], subkey + 12);
+ LOAD32L(st->input[11], subkey + 16);
+ LOAD32L(st->input[12], subkey + 20);
+ LOAD32L(st->input[13], subkey + 24);
+ LOAD32L(st->input[14], subkey + 28);
+ LOAD32L(st->input[ 6], &(nonce[16]) + 0);
+ LOAD32L(st->input[ 7], &(nonce[16]) + 4);
+ st->input[ 8] = 0;
+ st->input[ 9] = 0;
+ st->rounds = rounds;
+ st->ksleft = 0;
+ st->ivlen = 24; /* set switch to say nonce/IV has been loaded */
+
+#ifdef LTC_CLEAN_STACK
+ zeromem(x, sizeof(x));
+ zeromem(subkey, sizeof(subkey));
+#endif
+
+ return CRYPT_OK;
+}
+
+
+#endif
+
+/* ref: $Format:%D$ */
+/* git commit: $Format:%H$ */
+/* commit time: $Format:%ai$ */
diff --git a/src/stream/salsa20/xsalsa20_test.c b/src/stream/salsa20/xsalsa20_test.c
new file mode 100755
index 000000000..4a1d7b178
--- /dev/null
+++ b/src/stream/salsa20/xsalsa20_test.c
@@ -0,0 +1,94 @@
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ */
+
+/* The implementation is based on:
+ * "Extending the Salsa20 nonce", https://cr.yp.to/snuffle/xsalsa-20081128.pdf
+ * "Salsa20 specification", http://cr.yp.to/snuffle/spec.pdf
+ * and salsa20-ref.c version 20051118
+ * Public domain from D. J. Bernstein
+ */
+
+#include "tomcrypt.h"
+
+#ifdef LTC_XSALSA20
+
+#ifdef LTC_SHA256
+int _sha256(unsigned char *hash, const unsigned char *data, const int datalen) {
+ hash_state md;
+ sha256_init(&md);
+ sha256_process(&md, data, datalen);
+ sha256_done(&md, hash);
+ return CRYPT_OK;
+}
+#endif
+
+int xsalsa20_test(void)
+{
+#ifndef LTC_TEST
+ return CRYPT_NOP;
+#else
+
+ /***************************************************************************
+ * verify a round trip:
+ */
+ {
+ 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 nonce[] = {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 void *msg = "Kilroy was here!";
+ unsigned char msglen = 17; /* includes trailing NULL */
+ int rounds = 20;
+ unsigned char ciphertext[17];
+ unsigned char msg2[17];
+ salsa20_state st;
+ int err;
+
+ if ((err = xsalsa20_setup(&st, key, 32, nonce, 24, rounds)) != CRYPT_OK) return err;
+ if ((err = salsa20_crypt(&st, msg, msglen, ciphertext)) != CRYPT_OK) return err;
+ if ((err = salsa20_done(&st)) != CRYPT_OK) return err;
+
+ if ((err = xsalsa20_setup(&st, key, 32, nonce, 24, rounds)) != CRYPT_OK) return err;
+ if ((err = salsa20_crypt(&st, ciphertext, msglen, msg2)) != CRYPT_OK) return err;
+ if ((err = salsa20_done(&st)) != CRYPT_OK) return err;
+
+ if (compare_testvector(msg, msglen, msg2, msglen, "XSALSA20-TV1", 1)) return CRYPT_FAIL_TESTVECTOR;
+ }
+
+#ifdef LTC_SHA256
+ /***************************************************************************
+ * verify correct generation of a keystream
+ */
+ {
+ 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 nonce[] = {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 unsigned char expecthash[] = {0x6a,0x60,0x57,0x65,0x27,0xe0,0x00,0x51,0x6d,0xb0,0xda,0x60,0x46,0x20,0xf6,0xd0,0x95,0x65,0x45,0x39,0xf4,0x86,0x83,0x43,0x64,0xdf,0xd9,0x5a,0x6f,0x3f,0xbe,0xb7};
+ int rounds = 20;
+ unsigned char keystream[91101];
+ unsigned long keystreamlen = 91101;
+ unsigned char hash[32];
+ salsa20_state st;
+ int err;
+
+ if ((err = xsalsa20_setup(&st, key, 32, nonce, 24, rounds)) != CRYPT_OK) return err;
+ if ((err = salsa20_keystream(&st, keystream, keystreamlen)) != CRYPT_OK) return err;
+ if ((err = salsa20_done(&st)) != CRYPT_OK) return err;
+ if ((err = _sha256(hash, keystream, keystreamlen)) != CRYPT_OK) return err;
+ if (compare_testvector(hash, sizeof(hash), expecthash, sizeof(expecthash), "XSALSA20-TV2", 1)) return CRYPT_FAIL_TESTVECTOR;
+ }
+
+ return CRYPT_OK;
+#endif
+
+#endif
+}
+
+#endif
+
+/* ref: $Format:%D$ */
+/* git commit: $Format:%H$ */
+/* commit time: $Format:%ai$ */
diff --git a/tests/cipher_hash_test.c b/tests/cipher_hash_test.c
index 99b7b4c9b..d40bb3a99 100644
--- a/tests/cipher_hash_test.c
+++ b/tests/cipher_hash_test.c
@@ -26,6 +26,9 @@ int cipher_hash_test(void)
#ifdef LTC_SALSA20
DO(salsa20_test());
#endif
+#ifdef LTC_XSALSA20
+ DO(xsalsa20_test());
+#endif
#ifdef LTC_SOSEMANUK
DO(sosemanuk_test());
#endif