diff --git a/ChangeLog b/ChangeLog
index 80e07be85e6bc87cac288383f3f3d17d10c42e98..a0f9801fef2dada24d70a2e8c8fdd482d1088f46 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,13 @@
 2012-03-29  Niels Möller  <nisse@lysator.liu.se>
 
+	Implementation of Salsa20, contributed by Simon Josefsson.
+	* salsa20.h: New file.
+	* salsa20.c: New file.
+	* Makefile.in (nettle_SOURCES): Added salsa20.c
+	(HEADERS): Added salsa20.h.
+	* testsuite/Makefile.in (TS_NETTLE_SOURCES): Added salsa20-test.c.
+	* testsuite/salsa20-test.c: New test case.
+
 	* Makefile.in (soname links): Adding missing space before ].
 
 2012-03-23  Niels Möller  <nisse@lysator.liu.se>
diff --git a/Makefile.in b/Makefile.in
index a683452a7ac065d6ec1a24f9422622262a097184..a731c4ce82b6f6ccca75b0992aa3fd6bb9878536 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -79,6 +79,7 @@ nettle_SOURCES = aes-decrypt-internal.c aes-decrypt.c \
 		 md2.c md2-meta.c md4.c md4-meta.c \
 		 md5.c md5-compress.c md5-compat.c md5-meta.c \
 		 ripemd160.c ripemd160-compress.c ripemd160-meta.c \
+		 salsa20.c \
 		 sha1.c sha1-compress.c sha1-meta.c \
 		 sha256.c sha256-compress.c sha224-meta.c sha256-meta.c \
 		 sha512.c sha512-compress.c sha384-meta.c sha512-meta.c \
@@ -125,7 +126,7 @@ HEADERS = aes.h arcfour.h arctwo.h asn1.h bignum.h blowfish.h \
 	  memxor.h \
 	  nettle-meta.h nettle-types.h \
 	  pgp.h pkcs1.h realloc.h ripemd160.h rsa.h rsa-compat.h \
-	  sexp.h \
+	  salsa20.h sexp.h \
 	  serpent.h sha.h twofish.h \
 	  yarrow.h
 
diff --git a/salsa20.c b/salsa20.c
new file mode 100644
index 0000000000000000000000000000000000000000..f81b755be88fb802aa81299ad0b39c0267bee76c
--- /dev/null
+++ b/salsa20.c
@@ -0,0 +1,178 @@
+/* salsa20.c
+ *
+ * The Salsa20 stream cipher.
+ */
+
+/* nettle, low-level cryptographics library
+ *
+ * Copyright (C) 2012 Simon Josefsson
+ *  
+ * The nettle library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at your
+ * option) any later version.
+ * 
+ * The nettle library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ * License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the nettle library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/* Based on:
+   salsa20-ref.c version 20051118
+   D. J. Bernstein
+   Public domain.
+*/
+
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <assert.h>
+
+#include "salsa20.h"
+
+#define ROTL32(x,n) ((((x))<<(n)) | (((x))>>(32-(n))))
+
+#define SWAP32(v)				\
+  ((ROTL32(v,  8) & 0x00FF00FFUL) |		\
+   (ROTL32(v, 24) & 0xFF00FF00UL))
+
+#ifdef WORDS_BIGENDIAN
+#define U32TO32_LITTLE(v) SWAP32(v)
+#else
+#define U32TO32_LITTLE(v) (v)
+#endif
+
+#define U8TO32_LITTLE(p) U32TO32_LITTLE(((uint32_t*)(p))[0])
+#define U32TO8_LITTLE(p, v) (((uint32_t*)(p))[0] = U32TO32_LITTLE(v))
+
+/*
+salsa20-ref.c version 20051118
+D. J. Bernstein
+Public domain.
+*/
+
+#define ROTATE(v,c) (ROTL32(v,c))
+#define XOR(v,w) ((v) ^ (w))
+#define PLUS(v,w) ((v) + (w))
+#define PLUSONE(v) (PLUS((v),1))
+
+static void salsa20_wordtobyte(uint8_t output[64],const uint32_t input[16])
+{
+  uint32_t x[16];
+  int i;
+
+  for (i = 0;i < 16;++i) x[i] = input[i];
+  for (i = 20;i > 0;i -= 2) {
+    x[ 4] = XOR(x[ 4],ROTATE(PLUS(x[ 0],x[12]), 7));
+    x[ 8] = XOR(x[ 8],ROTATE(PLUS(x[ 4],x[ 0]), 9));
+    x[12] = XOR(x[12],ROTATE(PLUS(x[ 8],x[ 4]),13));
+    x[ 0] = XOR(x[ 0],ROTATE(PLUS(x[12],x[ 8]),18));
+    x[ 9] = XOR(x[ 9],ROTATE(PLUS(x[ 5],x[ 1]), 7));
+    x[13] = XOR(x[13],ROTATE(PLUS(x[ 9],x[ 5]), 9));
+    x[ 1] = XOR(x[ 1],ROTATE(PLUS(x[13],x[ 9]),13));
+    x[ 5] = XOR(x[ 5],ROTATE(PLUS(x[ 1],x[13]),18));
+    x[14] = XOR(x[14],ROTATE(PLUS(x[10],x[ 6]), 7));
+    x[ 2] = XOR(x[ 2],ROTATE(PLUS(x[14],x[10]), 9));
+    x[ 6] = XOR(x[ 6],ROTATE(PLUS(x[ 2],x[14]),13));
+    x[10] = XOR(x[10],ROTATE(PLUS(x[ 6],x[ 2]),18));
+    x[ 3] = XOR(x[ 3],ROTATE(PLUS(x[15],x[11]), 7));
+    x[ 7] = XOR(x[ 7],ROTATE(PLUS(x[ 3],x[15]), 9));
+    x[11] = XOR(x[11],ROTATE(PLUS(x[ 7],x[ 3]),13));
+    x[15] = XOR(x[15],ROTATE(PLUS(x[11],x[ 7]),18));
+    x[ 1] = XOR(x[ 1],ROTATE(PLUS(x[ 0],x[ 3]), 7));
+    x[ 2] = XOR(x[ 2],ROTATE(PLUS(x[ 1],x[ 0]), 9));
+    x[ 3] = XOR(x[ 3],ROTATE(PLUS(x[ 2],x[ 1]),13));
+    x[ 0] = XOR(x[ 0],ROTATE(PLUS(x[ 3],x[ 2]),18));
+    x[ 6] = XOR(x[ 6],ROTATE(PLUS(x[ 5],x[ 4]), 7));
+    x[ 7] = XOR(x[ 7],ROTATE(PLUS(x[ 6],x[ 5]), 9));
+    x[ 4] = XOR(x[ 4],ROTATE(PLUS(x[ 7],x[ 6]),13));
+    x[ 5] = XOR(x[ 5],ROTATE(PLUS(x[ 4],x[ 7]),18));
+    x[11] = XOR(x[11],ROTATE(PLUS(x[10],x[ 9]), 7));
+    x[ 8] = XOR(x[ 8],ROTATE(PLUS(x[11],x[10]), 9));
+    x[ 9] = XOR(x[ 9],ROTATE(PLUS(x[ 8],x[11]),13));
+    x[10] = XOR(x[10],ROTATE(PLUS(x[ 9],x[ 8]),18));
+    x[12] = XOR(x[12],ROTATE(PLUS(x[15],x[14]), 7));
+    x[13] = XOR(x[13],ROTATE(PLUS(x[12],x[15]), 9));
+    x[14] = XOR(x[14],ROTATE(PLUS(x[13],x[12]),13));
+    x[15] = XOR(x[15],ROTATE(PLUS(x[14],x[13]),18));
+  }
+  for (i = 0;i < 16;++i) x[i] = PLUS(x[i],input[i]);
+  for (i = 0;i < 16;++i) U32TO8_LITTLE(output + 4 * i,x[i]);
+}
+
+static const char sigma[16] = "expand 32-byte k";
+static const char tau[16] = "expand 16-byte k";
+
+void
+salsa20_set_key(struct salsa20_ctx *ctx,
+		unsigned length, const uint8_t *key)
+{
+  const char *constants;
+
+  assert (length == SALSA20_MIN_KEY_SIZE || length == SALSA20_MAX_KEY_SIZE);
+
+  ctx->input[1] = U8TO32_LITTLE(key + 0);
+  ctx->input[2] = U8TO32_LITTLE(key + 4);
+  ctx->input[3] = U8TO32_LITTLE(key + 8);
+  ctx->input[4] = U8TO32_LITTLE(key + 12);
+  if (length == SALSA20_MAX_KEY_SIZE) { /* recommended */
+    key += 16;
+    constants = sigma;
+  } else { /* kbits == 128 */
+    constants = tau;
+  }
+  ctx->input[11] = U8TO32_LITTLE(key + 0);
+  ctx->input[12] = U8TO32_LITTLE(key + 4);
+  ctx->input[13] = U8TO32_LITTLE(key + 8);
+  ctx->input[14] = U8TO32_LITTLE(key + 12);
+  ctx->input[0] = U8TO32_LITTLE(constants + 0);
+  ctx->input[5] = U8TO32_LITTLE(constants + 4);
+  ctx->input[10] = U8TO32_LITTLE(constants + 8);
+  ctx->input[15] = U8TO32_LITTLE(constants + 12);
+}
+
+void
+salsa20_set_iv(struct salsa20_ctx *ctx, unsigned length, const uint8_t *iv)
+{
+  assert (length == SALSA20_IV_SIZE);
+
+  ctx->input[6] = U8TO32_LITTLE(iv + 0);
+  ctx->input[7] = U8TO32_LITTLE(iv + 4);
+  ctx->input[8] = 0;
+  ctx->input[9] = 0;
+}
+
+void
+salsa20_crypt(struct salsa20_ctx *ctx,
+	      unsigned length,
+	      uint8_t *c,
+	      const uint8_t *m)
+{
+  uint8_t output[64];
+  unsigned i;
+
+  if (!length) return;
+  for (;;) {
+    salsa20_wordtobyte(output,ctx->input);
+    ctx->input[8] = PLUSONE(ctx->input[8]);
+    if (!ctx->input[8]) {
+      ctx->input[9] = PLUSONE(ctx->input[9]);
+      /* stopping at 2^70 length per nonce is user's responsibility */
+    }
+    if (length <= 64) {
+      for (i = 0;i < length;++i) c[i] = m[i] ^ output[i];
+      return;
+    }
+    for (i = 0;i < 64;++i) c[i] = m[i] ^ output[i];
+    length -= 64;
+    c += 64;
+    m += 64;
+  }
+}
diff --git a/salsa20.h b/salsa20.h
new file mode 100644
index 0000000000000000000000000000000000000000..79f1505d62c9a20c442f317cbad0c456d365ddbf
--- /dev/null
+++ b/salsa20.h
@@ -0,0 +1,71 @@
+/* salsa20.h
+ *
+ * The Salsa20 stream cipher.
+ */
+
+/* nettle, low-level cryptographics library
+ *
+ * Copyright (C) 2012 Simon Josefsson
+ * Copyright (C) 2001 Niels Möller
+ *  
+ * The nettle library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at your
+ * option) any later version.
+ * 
+ * The nettle library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ * License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the nettle library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+ 
+#ifndef NETTLE_SALSA20_H_INCLUDED
+#define NETTLE_SALSA20_H_INCLUDED
+
+#include "nettle-types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Name mangling */
+#define salsa20_set_key nettle_salsa20_set_key
+#define salsa20_set_iv nettle_salsa20_set_iv
+#define salsa20_crypt nettle_salsa20_crypt
+
+/* Minimum and maximum keysizes, and a reasonable default. In
+ * octets.*/
+#define SALSA20_MIN_KEY_SIZE 16
+#define SALSA20_MAX_KEY_SIZE 32
+#define SALSA20_KEY_SIZE 32
+
+#define SALSA20_IV_SIZE 8
+
+struct salsa20_ctx
+{
+    uint32_t input[16];
+};
+
+void
+salsa20_set_key(struct salsa20_ctx *ctx,
+		unsigned length, const uint8_t *key);
+
+void
+salsa20_set_iv(struct salsa20_ctx *ctx,
+	       unsigned length, const uint8_t *iv);
+
+void
+salsa20_crypt(struct salsa20_ctx *ctx,
+	      unsigned length, uint8_t *dst,
+	      const uint8_t *src);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NETTLE_SALSA20_H_INCLUDED */
diff --git a/testsuite/.gitignore b/testsuite/.gitignore
index 1147cfb1fadd7a56f1bc7aa7bd6f04bac8f8e946..c9f46987991ca76c8171e79643d51e37feb9fb45 100644
--- a/testsuite/.gitignore
+++ b/testsuite/.gitignore
@@ -36,6 +36,7 @@
 /rsa-keygen-test
 /rsa-test
 /rsa2sexp-test
+/salsa20-test
 /serpent-test
 /sexp-format-test
 /sexp-test
diff --git a/testsuite/.test-rules.make b/testsuite/.test-rules.make
index e8f6650b21e3e438d6323c609013fc88ced7f07c..10c993f894e8bc83cccbca2219d4ed9614aff0e3 100644
--- a/testsuite/.test-rules.make
+++ b/testsuite/.test-rules.make
@@ -49,6 +49,9 @@ memxor-test$(EXEEXT): memxor-test.$(OBJEXT)
 ripemd160-test$(EXEEXT): ripemd160-test.$(OBJEXT)
 	$(LINK) ripemd160-test.$(OBJEXT) $(TEST_OBJS) -o ripemd160-test$(EXEEXT)
 
+salsa20-test$(EXEEXT): salsa20-test.$(OBJEXT)
+	$(LINK) salsa20-test.$(OBJEXT) $(TEST_OBJS) -o salsa20-test$(EXEEXT)
+
 sha1-test$(EXEEXT): sha1-test.$(OBJEXT)
 	$(LINK) sha1-test.$(OBJEXT) $(TEST_OBJS) -o sha1-test$(EXEEXT)
 
diff --git a/testsuite/Makefile.in b/testsuite/Makefile.in
index 8dfc62b7be6c42c787be4f222a3275c593bfc04a..10ffae900dd48ba607c4264544807e275bdb77e6 100644
--- a/testsuite/Makefile.in
+++ b/testsuite/Makefile.in
@@ -18,6 +18,7 @@ TS_NETTLE_SOURCES = aes-test.c arcfour-test.c arctwo-test.c \
 		    md2-test.c md4-test.c md5-test.c md5-compat-test.c \
 		    memxor-test.c \
 		    ripemd160-test.c \
+		    salsa20-test.c \
 		    sha1-test.c sha224-test.c sha256-test.c \
 		    sha384-test.c sha512-test.c \
 		    serpent-test.c twofish-test.c \
diff --git a/testsuite/salsa20-test.c b/testsuite/salsa20-test.c
new file mode 100644
index 0000000000000000000000000000000000000000..82fc140696f0b35a90cb73266d756c3b10c67759
--- /dev/null
+++ b/testsuite/salsa20-test.c
@@ -0,0 +1,89 @@
+#include "testutils.h"
+#include "salsa20.h"
+
+static void
+test_salsa20(unsigned key_length,
+	     const uint8_t *key,
+	     unsigned iv_length,
+	     const uint8_t *iv,
+	     unsigned length,
+	     const uint8_t *cleartext,
+	     const uint8_t *ciphertext)
+{
+  struct salsa20_ctx ctx;
+  uint8_t *data = xalloc(length);
+
+  salsa20_set_key(&ctx, key_length, key);
+  salsa20_set_iv(&ctx, iv_length, iv);
+  salsa20_crypt(&ctx, length, data, cleartext);
+
+  if (!MEMEQ(length, data, ciphertext))
+    {
+      fprintf(stderr, "Encrypt failed:\nInput:");
+      print_hex(length, cleartext);
+      fprintf(stderr, "\nOutput: ");
+      print_hex(length, data);
+      fprintf(stderr, "\nExpected:");
+      print_hex(length, ciphertext);
+      fprintf(stderr, "\n");
+      FAIL();
+    }
+  salsa20_set_key(&ctx, key_length, key);
+  salsa20_set_iv(&ctx, iv_length, iv);
+  salsa20_crypt(&ctx, length, data, data);
+
+  if (!MEMEQ(length, data, cleartext))
+    {
+      fprintf(stderr, "Decrypt failed:\nInput:");
+      print_hex(length, ciphertext);
+      fprintf(stderr, "\nOutput: ");
+      print_hex(length, data);
+      fprintf(stderr, "\nExpected:");
+      print_hex(length, cleartext);
+      fprintf(stderr, "\n");
+      FAIL();
+    }
+
+  free(data);
+}
+  
+int
+test_main(void)
+{
+  /* http://www.ecrypt.eu.org/stream/svn/viewcvs.cgi/ecrypt/trunk/submissions/salsa20/full/verified.test-vectors?logsort=rev&rev=210&view=markup */
+
+  test_salsa20(HL("80000000 00000000 00000000 00000000"),
+	       HL("00000000 00000000"),
+	       HL("00000000 00000000"),
+	       H("4DFA5E48 1DA23EA0"));
+
+  test_salsa20(HL("00000000 00000000 00000000 00000000"),
+	       HL("80000000 00000000"),
+	       HL("00000000 00000000"),
+	       H("B66C1E44 46DD9557"));
+
+  test_salsa20(HL("0053A6F94C9FF24598EB3E91E4378ADD"),
+	       HL("0D74DB42A91077DE"),
+	       HL("00000000 00000000"),
+	       H("05E1E7BE B697D999"));
+
+  test_salsa20(HL("80000000 00000000 00000000 00000000"
+		  "00000000 00000000 00000000 00000000"),
+	       HL("00000000 00000000"),
+	       HL("00000000 00000000"),
+	       H("E3BE8FDD 8BECA2E3"));
+
+  test_salsa20(HL("00000000 00000000 00000000 00000000"
+		  "00000000 00000000 00000000 00000000"),
+	       HL("80000000 00000000"),
+	       HL("00000000 00000000"),
+	       H("2ABA3DC45B494700"));
+
+  test_salsa20(HL("0053A6F94C9FF24598EB3E91E4378ADD"
+		  "3083D6297CCF2275C81B6EC11467BA0D"),
+	       HL("0D74DB42A91077DE"),
+	       HL("00000000 00000000"),
+	       H("F5FAD53F 79F9DF58"));
+
+  SUCCESS();
+}