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(); +}