From c4885092088431e7928e4459fda20cc0e8ceb201 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 18 Jul 2013 21:32:05 +0200 Subject: Add support for Salsa20. * src/gcrypt.h.in (GCRY_CIPHER_SALSA20): New. * cipher/salsa20.c: New. * configure.ac (available_ciphers): Add Salsa20. * cipher/cipher.c: Register Salsa20. (cipher_setiv): Allow to divert an IV to a cipher module. * src/cipher-proto.h (cipher_setiv_func_t): New. (cipher_extra_spec): Add field setiv. * src/cipher.h: Declare Salsa20 definitions. * tests/basic.c (check_stream_cipher): New. (check_stream_cipher_large_block): New. (check_cipher_modes): Run new test functions. (check_ciphers): Add simple test for Salsa20. Signed-off-by: Werner Koch --- NEWS | 3 + cipher/Makefile.am | 1 + cipher/cipher.c | 14 +- cipher/salsa20.c | 380 +++++++++++++++++++++++++++++++++++ configure.ac | 8 +- doc/gcrypt.texi | 45 ++++- src/cipher-proto.h | 4 + src/cipher.h | 4 +- src/gcrypt.h.in | 3 +- tests/basic.c | 569 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 10 files changed, 1023 insertions(+), 8 deletions(-) create mode 100644 cipher/salsa20.c diff --git a/NEWS b/NEWS index ac609930..b1ad7ac6 100644 --- a/NEWS +++ b/NEWS @@ -12,6 +12,8 @@ Noteworthy changes in version 1.6.0 (unreleased) * Added support for the IDEA cipher algorithm. + * Added support for the Salsa20 stream cipher. + * Added a random number generator to directly use the system's RNG. Also added an interface to prefer the use of a specified RNG. @@ -70,6 +72,7 @@ Noteworthy changes in version 1.6.0 (unreleased) gcry_pubkey_get_sexp NEW. GCRYCTL_DISABLE_LOCKED_SECMEM NEW. GCRYCTL_DISABLE_PRIV_DROP NEW. + GCRY_CIPHER_SALSA20 NEW. Noteworthy changes in version 1.5.0 (2011-06-29) diff --git a/cipher/Makefile.am b/cipher/Makefile.am index c2a94c58..75ad9875 100644 --- a/cipher/Makefile.am +++ b/cipher/Makefile.am @@ -67,6 +67,7 @@ md5.c \ rijndael.c rijndael-tables.h rijndael-amd64.S \ rmd160.c \ rsa.c \ +salsa20.c \ scrypt.c \ seed.c \ serpent.c serpent-sse2-amd64.S serpent-avx2-amd64.S \ diff --git a/cipher/cipher.c b/cipher/cipher.c index d7ebea84..08d61655 100644 --- a/cipher/cipher.c +++ b/cipher/cipher.c @@ -103,6 +103,10 @@ static struct cipher_table_entry #ifdef USE_IDEA { &_gcry_cipher_spec_idea, &dummy_extra_spec, GCRY_CIPHER_IDEA }, +#endif +#if USE_SALSA20 + { &_gcry_cipher_spec_salsa20, + &_gcry_cipher_extraspec_salsa20, GCRY_CIPHER_SALSA20 }, #endif { NULL } }; @@ -845,8 +849,16 @@ cipher_setkey (gcry_cipher_hd_t c, byte *key, unsigned int keylen) /* Set the IV to be used for the encryption context C to IV with length IVLEN. The length should match the required length. */ static void -cipher_setiv( gcry_cipher_hd_t c, const byte *iv, unsigned ivlen ) +cipher_setiv (gcry_cipher_hd_t c, const byte *iv, unsigned ivlen) { + /* If the cipher has its own IV handler, we use only this one. This + is currently used for stream ciphers requiring a nonce. */ + if (c->extraspec && c->extraspec->setiv) + { + c->extraspec->setiv (&c->context.c, iv, ivlen); + return; + } + memset (c->u_iv.iv, 0, c->cipher->blocksize); if (iv) { diff --git a/cipher/salsa20.c b/cipher/salsa20.c new file mode 100644 index 00000000..e26c3289 --- /dev/null +++ b/cipher/salsa20.c @@ -0,0 +1,380 @@ +/* salsa20.c - Bernstein's Salsa20 cipher + * Copyright (C) 2012 Simon Josefsson, Niels Möller + * Copyright (C) 2013 g10 Code GmbH + * + * This file is part of Libgcrypt. + * + * Libgcrypt 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. + * + * Libgcrypt 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 this program; if not, see . + * + * For a description of the algorithm, see: + * http://cr.yp.to/snuffle/spec.pdf + * http://cr.yp.to/snuffle/design.pdf + */ + +/* The code is based on the code in Nettle + (git commit id 9d2d8ddaee35b91a4e1a32ae77cba04bea3480e7) + which in turn is based on + salsa20-ref.c version 20051118 + D. J. Bernstein + Public domain. +*/ + + +#include +#include +#include +#include +#include "types.h" +#include "g10lib.h" +#include "cipher.h" +#include "bufhelp.h" + +#define SALSA20_MIN_KEY_SIZE 16 /* Bytes. */ +#define SALSA20_MAX_KEY_SIZE 32 /* Bytes. */ +#define SALSA20_BLOCK_SIZE 64 /* Bytes. */ +#define SALSA20_IV_SIZE 8 /* Bytes. */ +#define SALSA20_INPUT_LENGTH 16 /* Bytes. */ + +/* Number of rounds. The standard uses 20 rounds. In any case the + number of rounds must be even. */ +#define SALSA20_ROUNDS 20 + + +typedef struct +{ + /* Indices 1-4 and 11-14 holds the key (two identical copies for the + shorter key size), indices 0, 5, 10, 15 are constant, indices 6, 7 + are the IV, and indices 8, 9 are the block counter: + + C K K K + K C I I + B B C K + K K K C + */ + u32 input[SALSA20_INPUT_LENGTH]; + u32 pad[SALSA20_INPUT_LENGTH]; + unsigned int unused; /* bytes in the pad. */ +} SALSA20_context_t; + + +/* The masking of the right shift is needed to allow n == 0 (using + just 32 - n and 64 - n results in undefined behaviour). Most uses + of these macros use a constant and non-zero rotation count. */ +#define ROTL32(n,x) (((x)<<(n)) | ((x)>>((-(n)&31)))) + + +#ifdef WORDS_BIGENDIAN +# define LE_SWAP32(v) \ + ( (ROTL32( 8, v) & 0x00FF00FFul) \ + |(ROTL32(24, v) & 0xFF00FF00ul)) +#else +# define LE_SWAP32(v) (v) +#endif + +#define LE_READ_UINT32(p) \ + ( (((u32)(p)[3]) << 24) \ + | (((u32)(p)[2]) << 16) \ + | (((u32)(p)[1]) << 8) \ + | ((u32)(p)[0])) + + +static void salsa20_setiv (void *context, const byte *iv, unsigned int ivlen); +static const char *selftest (void); + + + +#if 0 +# define SALSA20_CORE_DEBUG(i) do { \ + unsigned debug_j; \ + for (debug_j = 0; debug_j < 16; debug_j++) \ + { \ + if (debug_j == 0) \ + fprintf(stderr, "%2d:", (i)); \ + else if (debug_j % 4 == 0) \ + fprintf(stderr, "\n "); \ + fprintf(stderr, " %8x", pad[debug_j]); \ + } \ + fprintf(stderr, "\n"); \ + } while (0) +#else +# define SALSA20_CORE_DEBUG(i) +#endif + +#define QROUND(x0, x1, x2, x3) \ + do { \ + x1 ^= ROTL32 ( 7, x0 + x3); \ + x2 ^= ROTL32 ( 9, x1 + x0); \ + x3 ^= ROTL32 (13, x2 + x1); \ + x0 ^= ROTL32 (18, x3 + x2); \ + } while(0) + +static void +salsa20_core (u32 *dst, const u32 *src) +{ + u32 pad[SALSA20_INPUT_LENGTH]; + unsigned int i; + + memcpy (pad, src, sizeof(pad)); + for (i = 0; i < SALSA20_ROUNDS; i += 2) + { + SALSA20_CORE_DEBUG (i); + QROUND (pad[0], pad[4], pad[8], pad[12]); + QROUND (pad[5], pad[9], pad[13], pad[1] ); + QROUND (pad[10], pad[14], pad[2], pad[6] ); + QROUND (pad[15], pad[3], pad[7], pad[11]); + + SALSA20_CORE_DEBUG (i+1); + QROUND (pad[0], pad[1], pad[2], pad[3] ); + QROUND (pad[5], pad[6], pad[7], pad[4] ); + QROUND (pad[10], pad[11], pad[8], pad[9] ); + QROUND (pad[15], pad[12], pad[13], pad[14]); + } + SALSA20_CORE_DEBUG (i); + + for (i = 0; i < SALSA20_INPUT_LENGTH; i++) + { + u32 t = pad[i] + src[i]; + dst[i] = LE_SWAP32 (t); + } +} +#undef QROUND +#undef SALSA20_CORE_DEBUG + +static gcry_err_code_t +salsa20_do_setkey (SALSA20_context_t *ctx, + const byte *key, unsigned int keylen) +{ + static int initialized; + static const char *selftest_failed; + + if (!initialized ) + { + initialized = 1; + selftest_failed = selftest (); + if (selftest_failed) + log_error ("SALSA20 selftest failed (%s)\n", selftest_failed ); + } + if (selftest_failed) + return GPG_ERR_SELFTEST_FAILED; + + if (keylen != SALSA20_MIN_KEY_SIZE + && keylen != SALSA20_MAX_KEY_SIZE) + return GPG_ERR_INV_KEYLEN; + + /* These constants are the little endian encoding of the string + "expand 32-byte k". For the 128 bit variant, the "32" in that + string will be fixed up to "16". */ + ctx->input[0] = 0x61707865; /* "apxe" */ + ctx->input[5] = 0x3320646e; /* "3 dn" */ + ctx->input[10] = 0x79622d32; /* "yb-2" */ + ctx->input[15] = 0x6b206574; /* "k et" */ + + ctx->input[1] = LE_READ_UINT32(key + 0); + ctx->input[2] = LE_READ_UINT32(key + 4); + ctx->input[3] = LE_READ_UINT32(key + 8); + ctx->input[4] = LE_READ_UINT32(key + 12); + if (keylen == SALSA20_MAX_KEY_SIZE) /* 256 bits */ + { + ctx->input[11] = LE_READ_UINT32(key + 16); + ctx->input[12] = LE_READ_UINT32(key + 20); + ctx->input[13] = LE_READ_UINT32(key + 24); + ctx->input[14] = LE_READ_UINT32(key + 28); + } + else /* 128 bits */ + { + ctx->input[11] = ctx->input[1]; + ctx->input[12] = ctx->input[2]; + ctx->input[13] = ctx->input[3]; + ctx->input[14] = ctx->input[4]; + + ctx->input[5] -= 0x02000000; /* Change to "1 dn". */ + ctx->input[10] += 0x00000004; /* Change to "yb-6". */ + } + + /* We default to a zero nonce. */ + salsa20_setiv (ctx, NULL, 0); + + return 0; +} + + +static gcry_err_code_t +salsa20_setkey (void *context, const byte *key, unsigned int keylen) +{ + SALSA20_context_t *ctx = (SALSA20_context_t *)context; + gcry_err_code_t rc = salsa20_do_setkey (ctx, key, keylen); + _gcry_burn_stack (300/* FIXME*/); + return rc; +} + + +static void +salsa20_setiv (void *context, const byte *iv, unsigned int ivlen) +{ + SALSA20_context_t *ctx = (SALSA20_context_t *)context; + + if (!iv) + { + ctx->input[6] = 0; + ctx->input[7] = 0; + } + else if (ivlen == SALSA20_IV_SIZE) + { + ctx->input[6] = LE_READ_UINT32(iv + 0); + ctx->input[7] = LE_READ_UINT32(iv + 4); + } + else + { + log_info ("WARNING: salsa20_setiv: bad ivlen=%u\n", ivlen); + ctx->input[6] = 0; + ctx->input[7] = 0; + } + /* Reset the block counter. */ + ctx->input[8] = 0; + ctx->input[9] = 0; + /* Reset the unused pad bytes counter. */ + ctx->unused = 0; +} + + + +/* Note: This function requires LENGTH > 0. */ +static void +salsa20_do_encrypt_stream (SALSA20_context_t *ctx, + byte *outbuf, const byte *inbuf, + unsigned int length) +{ + if (ctx->unused) + { + unsigned char *p = (void*)ctx->pad; + unsigned int n; + + gcry_assert (ctx->unused < SALSA20_BLOCK_SIZE); + + n = ctx->unused; + if (n > length) + n = length; + buf_xor (outbuf, inbuf, p + SALSA20_BLOCK_SIZE - ctx->unused, n); + length -= n; + outbuf += n; + inbuf += n; + ctx->unused -= n; + if (!length) + return; + gcry_assert (!ctx->unused); + } + + for (;;) + { + /* Create the next pad and bump the block counter. Note that it + is the user's duty to change to another nonce not later than + after 2^70 processed bytes. */ + salsa20_core (ctx->pad, ctx->input); + if (!++ctx->input[8]) + ctx->input[9]++; + + if (length <= SALSA20_BLOCK_SIZE) + { + buf_xor (outbuf, inbuf, ctx->pad, length); + ctx->unused = SALSA20_BLOCK_SIZE - length; + return; + } + buf_xor (outbuf, inbuf, ctx->pad, SALSA20_BLOCK_SIZE); + length -= SALSA20_BLOCK_SIZE; + outbuf += SALSA20_BLOCK_SIZE; + inbuf += SALSA20_BLOCK_SIZE; + } +} + + +static void +salsa20_encrypt_stream (void *context, + byte *outbuf, const byte *inbuf, unsigned int length) +{ + SALSA20_context_t *ctx = (SALSA20_context_t *)context; + + if (length) + { + salsa20_do_encrypt_stream (ctx, outbuf, inbuf, length); + _gcry_burn_stack (/* salsa20_do_encrypt_stream: */ + 2*sizeof (void*) + + 3*sizeof (void*) + sizeof (unsigned int) + /* salsa20_core: */ + + 2*sizeof (void*) + + 2*sizeof (void*) + + 64 + + sizeof (unsigned int) + + sizeof (u32) + ); + } +} + + + +static const char* +selftest (void) +{ + SALSA20_context_t ctx; + byte scratch[8+1]; + + static byte key_1[] = + { 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + static const byte nonce_1[] = + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + static const byte plaintext_1[] = + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + static const byte ciphertext_1[] = + { 0xE3, 0xBE, 0x8F, 0xDD, 0x8B, 0xEC, 0xA2, 0xE3}; + + salsa20_setkey (&ctx, key_1, sizeof key_1); + salsa20_setiv (&ctx, nonce_1, sizeof nonce_1); + scratch[8] = 0; + salsa20_encrypt_stream (&ctx, scratch, plaintext_1, sizeof plaintext_1); + if (memcmp (scratch, ciphertext_1, sizeof ciphertext_1)) + return "Salsa20 encryption test 1 failed."; + if (scratch[8]) + return "Salsa20 wrote too much."; + salsa20_setkey( &ctx, key_1, sizeof(key_1)); + salsa20_setiv (&ctx, nonce_1, sizeof nonce_1); + salsa20_encrypt_stream (&ctx, scratch, scratch, sizeof plaintext_1); + if (memcmp (scratch, plaintext_1, sizeof plaintext_1)) + return "Salsa20 decryption test 1 failed."; + return NULL; +} + + +gcry_cipher_spec_t _gcry_cipher_spec_salsa20 = + { + "SALSA20", /* name */ + NULL, /* aliases */ + NULL, /* oids */ + 1, /* blocksize in bytes. */ + SALSA20_MAX_KEY_SIZE*8, /* standard key length in bits. */ + sizeof (SALSA20_context_t), + salsa20_setkey, + NULL, + NULL, + salsa20_encrypt_stream, + salsa20_encrypt_stream + }; + +cipher_extra_spec_t _gcry_cipher_extraspec_salsa20 = + { + NULL, + NULL, + salsa20_setiv + }; diff --git a/configure.ac b/configure.ac index 13541bbc..06c0b790 100644 --- a/configure.ac +++ b/configure.ac @@ -184,7 +184,7 @@ LIBGCRYPT_CONFIG_HOST="$host" # Definitions for symmetric ciphers. available_ciphers="arcfour blowfish cast5 des aes twofish serpent rfc2268 seed" -available_ciphers="$available_ciphers camellia idea" +available_ciphers="$available_ciphers camellia idea salsa20" enabled_ciphers="" # Definitions for public-key ciphers. @@ -1356,6 +1356,12 @@ if test "$found" = "1" ; then AC_DEFINE(USE_IDEA, 1, [Defined if this module should be included]) fi +LIST_MEMBER(salsa20, $enabled_ciphers) +if test "$found" = "1" ; then + GCRYPT_CIPHERS="$GCRYPT_CIPHERS salsa20.lo" + AC_DEFINE(USE_SALSA20, 1, [Defined if this module should be included]) +fi + LIST_MEMBER(dsa, $enabled_pubkey_ciphers) if test "$found" = "1" ; then GCRYPT_PUBKEY_CIPHERS="$GCRYPT_PUBKEY_CIPHERS dsa.lo" diff --git a/doc/gcrypt.texi b/doc/gcrypt.texi index 4d244756..cfc01741 100644 --- a/doc/gcrypt.texi +++ b/doc/gcrypt.texi @@ -1487,8 +1487,7 @@ The value always evaluates to false. @item GCRY_CIPHER_IDEA @cindex IDEA -This is the IDEA algorithm. The constant is provided but there is -currently no implementation for it because the algorithm is patented. +This is the IDEA algorithm. @item GCRY_CIPHER_3DES @cindex 3DES @@ -1576,6 +1575,10 @@ A 128 bit cipher as described by RFC4269. The Camellia cipher by NTT. See @uref{http://info.isl.ntt.co.jp/@/crypt/@/eng/@/camellia/@/specifications.html}. +@item GCRY_CIPHER_SALSA20 +@cindex Salsa20 +This is the Salsa20 stream cipher. + @end table @node Available cipher modes @@ -1717,6 +1720,10 @@ Set the initialization vector used for encryption or decryption. The vector is passed as the buffer @var{K} of length @var{l} bytes and copied to internal data structures. The function checks that the IV matches the requirement of the selected algorithm and mode. + +This function is also used with the Salsa20 stream cipher to set or +update the required nonce. In this case it needs to be called after +setting the key. @end deftypefun @deftypefun gcry_error_t gcry_cipher_setctr (gcry_cipher_hd_t @var{h}, const void *@var{c}, size_t @var{l}) @@ -2355,6 +2362,34 @@ format should be used: @noindent Here, the data to be signed is directly given as an @var{MPI}. +@noindent +For DSA the input data is expected in this format: +@example +(data + (flags raw) + (value @var{mpi})) +@end example + +@noindent +Here, the data to be signed is directly given as an @var{MPI}. It is +expect that this MPI is the the hash value. For the standard DSA +using a MPI is not a problem in regard to leading zeroes because the +hash value is directly used as an MPI. For better standard +conformance it would be better to explicit use a memory string (like +with pkcs1) but that is currently not supported. However, for +deterministic DSA as specified in RFC6979 this can't be used. Instead +the following input is expected. + +@example +(data + (flags rfc6979) + (hash @var{hash-algo} @var{block})) +@end example + +Note that the provided hash-algo is used for the internal HMAC; it +should match the hash-algo used to create @var{block}. + + @noindent The signature is returned as a newly allocated S-expression in @var{r_sig} using this format for RSA: @@ -2380,6 +2415,7 @@ operation. For Elgamal signing (which is slow, yields large numbers and probably is not as secure as the other algorithms), the same format is used with "elg" replacing "dsa"; for ECDSA signing, the same format is used with "ecdsa" replacing "dsa". + @end deftypefun @c end gcry_pk_sign @@ -4115,7 +4151,10 @@ value. Two functions implement this kludge: Store @var{nbits} of the value @var{p} points to in @var{a} and mark @var{a} as an opaque value (i.e. an value that can't be used for any math calculation and is only used to store an arbitrary bit pattern in -@var{a}). +@var{a}). Ownership of @var{p} is taken by this function and thus the +user may not use dereference the passed value anymore. It is required +that them memory referenced by @var{p} has been allocated in a way +that @code{gcry_free} is able to release it. WARNING: Never use an opaque MPI for actual math operations. The only valid functions are gcry_mpi_get_opaque and gcry_mpi_release. Use diff --git a/src/cipher-proto.h b/src/cipher-proto.h index e2f913df..e9f4bab6 100644 --- a/src/cipher-proto.h +++ b/src/cipher-proto.h @@ -68,6 +68,9 @@ typedef gcry_sexp_t (*pk_get_curve_param_t)(const char *name); typedef gpg_err_code_t (*cipher_set_extra_info_t) (void *c, int what, const void *buffer, size_t buflen); +/* The type used to set an IV directly in the algorithm module. */ +typedef void (*cipher_setiv_func_t)(void *c, + const byte *iv, unsigned int ivlen); /* Extra module specification structures. These are used for internal modules which provide more functions than available through the @@ -76,6 +79,7 @@ typedef struct cipher_extra_spec { selftest_func_t selftest; cipher_set_extra_info_t set_extra_info; + cipher_setiv_func_t setiv; } cipher_extra_spec_t; typedef struct md_extra_spec diff --git a/src/cipher.h b/src/cipher.h index 26206135..bb927582 100644 --- a/src/cipher.h +++ b/src/cipher.h @@ -27,6 +27,7 @@ #include "../random/random.h" #define PUBKEY_FLAG_NO_BLINDING (1 << 0) +#define PUBKEY_FLAG_RFC6979 (1 << 1) enum pk_operation { @@ -194,12 +195,13 @@ extern gcry_cipher_spec_t _gcry_cipher_spec_camellia128; extern gcry_cipher_spec_t _gcry_cipher_spec_camellia192; extern gcry_cipher_spec_t _gcry_cipher_spec_camellia256; extern gcry_cipher_spec_t _gcry_cipher_spec_idea; +extern gcry_cipher_spec_t _gcry_cipher_spec_salsa20; extern cipher_extra_spec_t _gcry_cipher_extraspec_tripledes; extern cipher_extra_spec_t _gcry_cipher_extraspec_aes; extern cipher_extra_spec_t _gcry_cipher_extraspec_aes192; extern cipher_extra_spec_t _gcry_cipher_extraspec_aes256; - +extern cipher_extra_spec_t _gcry_cipher_extraspec_salsa20; /* Declarations for the digest specifications. */ extern gcry_md_spec_t _gcry_digest_spec_crc32; diff --git a/src/gcrypt.h.in b/src/gcrypt.h.in index 22928327..6bd615d1 100644 --- a/src/gcrypt.h.in +++ b/src/gcrypt.h.in @@ -812,7 +812,8 @@ enum gcry_cipher_algos GCRY_CIPHER_SEED = 309, /* 128 bit cipher described in RFC4269. */ GCRY_CIPHER_CAMELLIA128 = 310, GCRY_CIPHER_CAMELLIA192 = 311, - GCRY_CIPHER_CAMELLIA256 = 312 + GCRY_CIPHER_CAMELLIA256 = 312, + GCRY_CIPHER_SALSA20 = 313 }; /* The Rijndael algorithm is basically AES, so provide some macros. */ diff --git a/tests/basic.c b/tests/basic.c index d1b4002a..88ae1316 100644 --- a/tests/basic.c +++ b/tests/basic.c @@ -1,6 +1,7 @@ /* basic.c - basic regression tests * Copyright (C) 2001, 2002, 2003, 2005, 2008, * 2009 Free Software Foundation, Inc. + * Copyright (C) 2013 g10 Code GmbH * * This file is part of Libgcrypt. * @@ -1137,6 +1138,567 @@ check_ofb_cipher (void) } +static void +check_stream_cipher (void) +{ + struct tv + { + const char *name; + int algo; + int keylen; + int ivlen; + const char *key; + const char *iv; + struct data + { + int inlen; + const char *plaintext; + const char *out; + } data[MAX_DATA_LEN]; + } tv[] = { +#ifdef USE_SALSA20 + { + "Salsa20 128 bit, test 1", + GCRY_CIPHER_SALSA20, 16, 8, + "\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", + "\x00\x00\x00\x00\x00\x00\x00\x00", + { + { 8, + "\x00\x00\x00\x00\x00\x00\x00\x00", + "\x4D\xFA\x5E\x48\x1D\xA2\x3E\xA0" + } + } + }, + { + "Salsa20 128 bit, test 2", + GCRY_CIPHER_SALSA20, 16, 8, + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", + "\x80\x00\x00\x00\x00\x00\x00\x00", + { + { 8, + "\x00\x00\x00\x00\x00\x00\x00\x00", + "\xB6\x6C\x1E\x44\x46\xDD\x95\x57" + } + } + }, + { + "Salsa20 128 bit, test 3", + GCRY_CIPHER_SALSA20, 16, 8, + "\x00\x53\xA6\xF9\x4C\x9F\xF2\x45\x98\xEB\x3E\x91\xE4\x37\x8A\xDD", + "\x0D\x74\xDB\x42\xA9\x10\x77\xDE", + { + { 8, + "\x00\x00\x00\x00\x00\x00\x00\x00", + "\x05\xE1\xE7\xBE\xB6\x97\xD9\x99" + } + } + }, + { + "Salsa20 256 bit, test 1", + GCRY_CIPHER_SALSA20, 32, 8, + "\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", + "\x00\x00\x00\x00\x00\x00\x00\x00", + { + { 8, + "\x00\x00\x00\x00\x00\x00\x00\x00", + "\xE3\xBE\x8F\xDD\x8B\xEC\xA2\xE3" + } + } + }, + { + "Salsa20 256 bit, test 2", + GCRY_CIPHER_SALSA20, 32, 8, + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", + "\x80\x00\x00\x00\x00\x00\x00\x00", + { + { 8, + "\x00\x00\x00\x00\x00\x00\x00\x00", + "\x2A\xBA\x3D\xC4\x5B\x49\x47\x00" + } + } + }, + { + "Salsa20 256 bit, ecrypt verified, set 6, vector 0", + GCRY_CIPHER_SALSA20, 32, 8, + "\x00\x53\xA6\xF9\x4C\x9F\xF2\x45\x98\xEB\x3E\x91\xE4\x37\x8A\xDD" + "\x30\x83\xD6\x29\x7C\xCF\x22\x75\xC8\x1B\x6E\xC1\x14\x67\xBA\x0D", + "\x0D\x74\xDB\x42\xA9\x10\x77\xDE", + { + { 8, + "\x00\x00\x00\x00\x00\x00\x00\x00", + "\xF5\xFA\xD5\x3F\x79\xF9\xDF\x58" + }, + { 64, + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", + "\xF5\xFA\xD5\x3F\x79\xF9\xDF\x58\xC4\xAE\xA0\xD0\xED\x9A\x96\x01" + "\xF2\x78\x11\x2C\xA7\x18\x0D\x56\x5B\x42\x0A\x48\x01\x96\x70\xEA" + "\xF2\x4C\xE4\x93\xA8\x62\x63\xF6\x77\xB4\x6A\xCE\x19\x24\x77\x3D" + "\x2B\xB2\x55\x71\xE1\xAA\x85\x93\x75\x8F\xC3\x82\xB1\x28\x0B\x71" + } + } + } +#endif /*USE_SALSA20*/ + }; + + gcry_cipher_hd_t hde, hdd; + unsigned char out[MAX_DATA_LEN]; + int i, j; + gcry_error_t err = 0; + + + if (verbose) + fprintf (stderr, " Starting stream cipher checks.\n"); + + for (i = 0; i < sizeof (tv) / sizeof (tv[0]); i++) + { + if (verbose) + fprintf (stderr, " checking stream mode for %s [%i] (%s)\n", + gcry_cipher_algo_name (tv[i].algo), tv[i].algo, tv[i].name); + + if (gcry_cipher_get_algo_blklen(tv[i].algo) != 1) + { + fail ("stream, gcry_cipher_get_algo_blklen: bad block length\n"); + continue; + } + + err = gcry_cipher_open (&hde, tv[i].algo, GCRY_CIPHER_MODE_STREAM, 0); + if (!err) + err = gcry_cipher_open (&hdd, tv[i].algo, GCRY_CIPHER_MODE_STREAM, 0); + if (err) + { + fail ("stream, gcry_cipher_open for stream mode failed: %s\n", + gpg_strerror (err)); + continue; + } + + /* Now loop over all the data samples. */ + for (j = 0; tv[i].data[j].inlen; j++) + { + err = gcry_cipher_setkey (hde, tv[i].key, tv[i].keylen); + if (!err) + err = gcry_cipher_setkey (hdd, tv[i].key, tv[i].keylen); + if (err) + { + fail ("stream, gcry_cipher_setkey failed: %s\n", + gpg_strerror (err)); + goto next; + } + + err = gcry_cipher_setiv (hde, tv[i].iv, tv[i].ivlen); + if (!err) + err = gcry_cipher_setiv (hdd, tv[i].iv, tv[i].ivlen); + if (err) + { + fail ("stream, gcry_cipher_setiv failed: %s\n", + gpg_strerror (err)); + goto next; + } + + err = gcry_cipher_encrypt (hde, out, MAX_DATA_LEN, + tv[i].data[j].plaintext, + tv[i].data[j].inlen); + if (err) + { + fail ("stream, gcry_cipher_encrypt (%d, %d) failed: %s\n", + i, j, gpg_strerror (err)); + goto next; + } + + if (memcmp (tv[i].data[j].out, out, tv[i].data[j].inlen)) + { + fail ("stream, encrypt mismatch entry %d:%d\n", i, j); + mismatch (tv[i].data[j].out, tv[i].data[j].inlen, + out, tv[i].data[j].inlen); + } + + err = gcry_cipher_decrypt (hdd, out, tv[i].data[j].inlen, NULL, 0); + if (err) + { + fail ("stream, gcry_cipher_decrypt (%d, %d) failed: %s\n", + i, j, gpg_strerror (err)); + goto next; + } + + if (memcmp (tv[i].data[j].plaintext, out, tv[i].data[j].inlen)) + fail ("stream, decrypt mismatch entry %d:%d\n", i, j); + } + + + /* This time we encrypt and decrypt one byte at a time */ + for (j = 0; tv[i].data[j].inlen; j++) + { + int byteNum; + + err = gcry_cipher_setkey (hde, tv[i].key, tv[i].keylen); + if (!err) + err = gcry_cipher_setkey (hdd, tv[i].key, tv[i].keylen); + if (err) + { + fail ("stream, gcry_cipher_setkey failed: %s\n", + gpg_strerror (err)); + goto next; + } + + err = gcry_cipher_setiv (hde, tv[i].iv, tv[i].ivlen); + if (!err) + err = gcry_cipher_setiv (hdd, tv[i].iv, tv[i].ivlen); + if (err) + { + fail ("stream, gcry_cipher_setiv failed: %s\n", + gpg_strerror (err)); + goto next; + } + + for (byteNum = 0; byteNum < tv[i].data[j].inlen; ++byteNum) + { + err = gcry_cipher_encrypt (hde, out+byteNum, 1, + (tv[i].data[j].plaintext) + byteNum, + 1); + if (err) + { + fail ("stream, gcry_cipher_encrypt (%d, %d) failed: %s\n", + i, j, gpg_strerror (err)); + goto next; + } + } + + if (memcmp (tv[i].data[j].out, out, tv[i].data[j].inlen)) + fail ("stream, encrypt mismatch entry %d:%d (byte-wise)\n", i, j); + + for (byteNum = 0; byteNum < tv[i].data[j].inlen; ++byteNum) + { + err = gcry_cipher_decrypt (hdd, out+byteNum, 1, NULL, 0); + if (err) + { + fail ("stream, gcry_cipher_decrypt (%d, %d) failed: %s\n", + i, j, gpg_strerror (err)); + goto next; + } + } + + if (memcmp (tv[i].data[j].plaintext, out, tv[i].data[j].inlen)) + fail ("stream, decrypt mismatch entry %d:%d (byte-wise)\n", i, j); + } + + next: + gcry_cipher_close (hde); + gcry_cipher_close (hdd); + } + if (verbose) + fprintf (stderr, " Completed stream cipher checks.\n"); +} + + +static void +check_stream_cipher_large_block (void) +{ + struct tv + { + const char *name; + int algo; + int keylen; + int ivlen; + const char *key; + const char *iv; + struct data + { + int offset, length; + const char *result; + } data[MAX_DATA_LEN]; + } tv[] = { +#ifdef USE_SALSA20 + { + "Salsa20 256 bit, ecrypt verified, set 6, vector 0", + GCRY_CIPHER_SALSA20, 32, 8, + "\x00\x53\xA6\xF9\x4C\x9F\xF2\x45\x98\xEB\x3E\x91\xE4\x37\x8A\xDD" + "\x30\x83\xD6\x29\x7C\xCF\x22\x75\xC8\x1B\x6E\xC1\x14\x67\xBA\x0D", + "\x0D\x74\xDB\x42\xA9\x10\x77\xDE", + { + { 0, 64, + "\xF5\xFA\xD5\x3F\x79\xF9\xDF\x58\xC4\xAE\xA0\xD0\xED\x9A\x96\x01" + "\xF2\x78\x11\x2C\xA7\x18\x0D\x56\x5B\x42\x0A\x48\x01\x96\x70\xEA" + "\xF2\x4C\xE4\x93\xA8\x62\x63\xF6\x77\xB4\x6A\xCE\x19\x24\x77\x3D" + "\x2B\xB2\x55\x71\xE1\xAA\x85\x93\x75\x8F\xC3\x82\xB1\x28\x0B\x71" + }, + { 65472, 64, + "\xB7\x0C\x50\x13\x9C\x63\x33\x2E\xF6\xE7\x7A\xC5\x43\x38\xA4\x07" + "\x9B\x82\xBE\xC9\xF9\xA4\x03\xDF\xEA\x82\x1B\x83\xF7\x86\x07\x91" + "\x65\x0E\xF1\xB2\x48\x9D\x05\x90\xB1\xDE\x77\x2E\xED\xA4\xE3\xBC" + "\xD6\x0F\xA7\xCE\x9C\xD6\x23\xD9\xD2\xFD\x57\x58\xB8\x65\x3E\x70" + }, + { 65536, 64, + "\x81\x58\x2C\x65\xD7\x56\x2B\x80\xAE\xC2\xF1\xA6\x73\xA9\xD0\x1C" + "\x9F\x89\x2A\x23\xD4\x91\x9F\x6A\xB4\x7B\x91\x54\xE0\x8E\x69\x9B" + "\x41\x17\xD7\xC6\x66\x47\x7B\x60\xF8\x39\x14\x81\x68\x2F\x5D\x95" + "\xD9\x66\x23\xDB\xC4\x89\xD8\x8D\xAA\x69\x56\xB9\xF0\x64\x6B\x6E" + }, + { 131008, 64, + "\xA1\x3F\xFA\x12\x08\xF8\xBF\x50\x90\x08\x86\xFA\xAB\x40\xFD\x10" + "\xE8\xCA\xA3\x06\xE6\x3D\xF3\x95\x36\xA1\x56\x4F\xB7\x60\xB2\x42" + "\xA9\xD6\xA4\x62\x8C\xDC\x87\x87\x62\x83\x4E\x27\xA5\x41\xDA\x2A" + "\x5E\x3B\x34\x45\x98\x9C\x76\xF6\x11\xE0\xFE\xC6\xD9\x1A\xCA\xCC" + } + } + }, + { + "Salsa20 256 bit, ecrypt verified, set 6, vector 1", + GCRY_CIPHER_SALSA20, 32, 8, + "\x05\x58\xAB\xFE\x51\xA4\xF7\x4A\x9D\xF0\x43\x96\xE9\x3C\x8F\xE2" + "\x35\x88\xDB\x2E\x81\xD4\x27\x7A\xCD\x20\x73\xC6\x19\x6C\xBF\x12", + "\x16\x7D\xE4\x4B\xB2\x19\x80\xE7", + { + { 0, 64, + "\x39\x44\xF6\xDC\x9F\x85\xB1\x28\x08\x38\x79\xFD\xF1\x90\xF7\xDE" + "\xE4\x05\x3A\x07\xBC\x09\x89\x6D\x51\xD0\x69\x0B\xD4\xDA\x4A\xC1" + "\x06\x2F\x1E\x47\xD3\xD0\x71\x6F\x80\xA9\xB4\xD8\x5E\x6D\x60\x85" + "\xEE\x06\x94\x76\x01\xC8\x5F\x1A\x27\xA2\xF7\x6E\x45\xA6\xAA\x87" + }, + { 65472, 64, + "\x36\xE0\x3B\x4B\x54\xB0\xB2\xE0\x4D\x06\x9E\x69\x00\x82\xC8\xC5" + "\x92\xDF\x56\xE6\x33\xF5\xD8\xC7\x68\x2A\x02\xA6\x5E\xCD\x13\x71" + "\x8C\xA4\x35\x2A\xAC\xCB\x0D\xA2\x0E\xD6\xBB\xBA\x62\xE1\x77\xF2" + "\x10\xE3\x56\x0E\x63\xBB\x82\x2C\x41\x58\xCA\xA8\x06\xA8\x8C\x82" + }, + { 65536, 64, + "\x1B\x77\x9E\x7A\x91\x7C\x8C\x26\x03\x9F\xFB\x23\xCF\x0E\xF8\xE0" + "\x8A\x1A\x13\xB4\x3A\xCD\xD9\x40\x2C\xF5\xDF\x38\x50\x10\x98\xDF" + "\xC9\x45\xA6\xCC\x69\xA6\xA1\x73\x67\xBC\x03\x43\x1A\x86\xB3\xED" + "\x04\xB0\x24\x5B\x56\x37\x9B\xF9\x97\xE2\x58\x00\xAD\x83\x7D\x7D" + }, + { 131008, 64, + "\x7E\xC6\xDA\xE8\x1A\x10\x5E\x67\x17\x2A\x0B\x8C\x4B\xBE\x7D\x06" + "\xA7\xA8\x75\x9F\x91\x4F\xBE\xB1\xAF\x62\xC8\xA5\x52\xEF\x4A\x4F" + "\x56\x96\x7E\xA2\x9C\x74\x71\xF4\x6F\x3B\x07\xF7\xA3\x74\x6E\x95" + "\x3D\x31\x58\x21\xB8\x5B\x6E\x8C\xB4\x01\x22\xB9\x66\x35\x31\x3C" + } + } + }, + { + "Salsa20 256 bit, ecrypt verified, set 6, vector 2", + GCRY_CIPHER_SALSA20, 32, 8, + "\x0A\x5D\xB0\x03\x56\xA9\xFC\x4F\xA2\xF5\x48\x9B\xEE\x41\x94\xE7" + "\x3A\x8D\xE0\x33\x86\xD9\x2C\x7F\xD2\x25\x78\xCB\x1E\x71\xC4\x17", + "\x1F\x86\xED\x54\xBB\x22\x89\xF0", + { + { 0, 64, + "\x3F\xE8\x5D\x5B\xB1\x96\x0A\x82\x48\x0B\x5E\x6F\x4E\x96\x5A\x44" + "\x60\xD7\xA5\x45\x01\x66\x4F\x7D\x60\xB5\x4B\x06\x10\x0A\x37\xFF" + "\xDC\xF6\xBD\xE5\xCE\x3F\x48\x86\xBA\x77\xDD\x5B\x44\xE9\x56\x44" + "\xE4\x0A\x8A\xC6\x58\x01\x15\x5D\xB9\x0F\x02\x52\x2B\x64\x40\x23" + }, + { 65472, 64, + "\xC8\xD6\xE5\x4C\x29\xCA\x20\x40\x18\xA8\x30\xE2\x66\xCE\xEE\x0D" + "\x03\x7D\xC4\x7E\x92\x19\x47\x30\x2A\xCE\x40\xD1\xB9\x96\xA6\xD8" + "\x0B\x59\x86\x77\xF3\x35\x2F\x1D\xAA\x6D\x98\x88\xF8\x91\xAD\x95" + "\xA1\xC3\x2F\xFE\xB7\x1B\xB8\x61\xE8\xB0\x70\x58\x51\x51\x71\xC9" + }, + { 65536, 64, + "\xB7\x9F\xD7\x76\x54\x2B\x46\x20\xEF\xCB\x88\x44\x95\x99\xF2\x34" + "\x03\xE7\x4A\x6E\x91\xCA\xCC\x50\xA0\x5A\x8F\x8F\x3C\x0D\xEA\x8B" + "\x00\xE1\xA5\xE6\x08\x1F\x55\x26\xAE\x97\x5B\x3B\xC0\x45\x0F\x1A" + "\x0C\x8B\x66\xF8\x08\xF1\x90\x4B\x97\x13\x61\x13\x7C\x93\x15\x6F" + }, + { 131008, 64, + "\x79\x98\x20\x4F\xED\x70\xCE\x8E\x0D\x02\x7B\x20\x66\x35\xC0\x8C" + "\x8B\xC4\x43\x62\x26\x08\x97\x0E\x40\xE3\xAE\xDF\x3C\xE7\x90\xAE" + "\xED\xF8\x9F\x92\x26\x71\xB4\x53\x78\xE2\xCD\x03\xF6\xF6\x23\x56" + "\x52\x9C\x41\x58\xB7\xFF\x41\xEE\x85\x4B\x12\x35\x37\x39\x88\xC8" + } + } + }, + { + "Salsa20 256 bit, ecrypt verified, set 6, vector 3", + GCRY_CIPHER_SALSA20, 32, 8, + "\x0F\x62\xB5\x08\x5B\xAE\x01\x54\xA7\xFA\x4D\xA0\xF3\x46\x99\xEC" + "\x3F\x92\xE5\x38\x8B\xDE\x31\x84\xD7\x2A\x7D\xD0\x23\x76\xC9\x1C", + "\x28\x8F\xF6\x5D\xC4\x2B\x92\xF9", + { + { 0, 64, + "\x5E\x5E\x71\xF9\x01\x99\x34\x03\x04\xAB\xB2\x2A\x37\xB6\x62\x5B" + "\xF8\x83\xFB\x89\xCE\x3B\x21\xF5\x4A\x10\xB8\x10\x66\xEF\x87\xDA" + "\x30\xB7\x76\x99\xAA\x73\x79\xDA\x59\x5C\x77\xDD\x59\x54\x2D\xA2" + "\x08\xE5\x95\x4F\x89\xE4\x0E\xB7\xAA\x80\xA8\x4A\x61\x76\x66\x3F" + }, + { 65472, 64, + "\x2D\xA2\x17\x4B\xD1\x50\xA1\xDF\xEC\x17\x96\xE9\x21\xE9\xD6\xE2" + "\x4E\xCF\x02\x09\xBC\xBE\xA4\xF9\x83\x70\xFC\xE6\x29\x05\x6F\x64" + "\x91\x72\x83\x43\x6E\x2D\x3F\x45\x55\x62\x25\x30\x7D\x5C\xC5\xA5" + "\x65\x32\x5D\x89\x93\xB3\x7F\x16\x54\x19\x5C\x24\x0B\xF7\x5B\x16" + }, + { 65536, 64, + "\xAB\xF3\x9A\x21\x0E\xEE\x89\x59\x8B\x71\x33\x37\x70\x56\xC2\xFE" + "\xF4\x2D\xA7\x31\x32\x75\x63\xFB\x67\xC7\xBE\xDB\x27\xF3\x8C\x7C" + "\x5A\x3F\xC2\x18\x3A\x4C\x6B\x27\x7F\x90\x11\x52\x47\x2C\x6B\x2A" + "\xBC\xF5\xE3\x4C\xBE\x31\x5E\x81\xFD\x3D\x18\x0B\x5D\x66\xCB\x6C" + }, + { 131008, 64, + "\x1B\xA8\x9D\xBD\x3F\x98\x83\x97\x28\xF5\x67\x91\xD5\xB7\xCE\x23" + "\x50\x36\xDE\x84\x3C\xCC\xAB\x03\x90\xB8\xB5\x86\x2F\x1E\x45\x96" + "\xAE\x8A\x16\xFB\x23\xDA\x99\x7F\x37\x1F\x4E\x0A\xAC\xC2\x6D\xB8" + "\xEB\x31\x4E\xD4\x70\xB1\xAF\x6B\x9F\x8D\x69\xDD\x79\xA9\xD7\x50" + } + } + } +#endif /*USE_SALSA20*/ + }; + + + char zeroes[512]; + gcry_cipher_hd_t hde; + unsigned char *buffer; + unsigned char *p; + size_t buffersize; + unsigned int n; + int i, j; + gcry_error_t err = 0; + + if (verbose) + fprintf (stderr, " Starting large block stream cipher checks.\n"); + + memset (zeroes, 0, 512); + + buffersize = 128 * 1024; + buffer = gcry_xmalloc (buffersize+1024); + memset (buffer+buffersize, 0x5a, 1024); + + for (i = 0; i < sizeof (tv) / sizeof (tv[0]); i++) + { + if (verbose) + fprintf (stderr, " checking large block stream for %s [%i] (%s)\n", + gcry_cipher_algo_name (tv[i].algo), tv[i].algo, tv[i].name); + + err = gcry_cipher_open (&hde, tv[i].algo, GCRY_CIPHER_MODE_STREAM, 0); + if (err) + { + fail ("large stream, gcry_cipher_open for stream mode failed: %s\n", + gpg_strerror (err)); + continue; + } + + err = gcry_cipher_setkey (hde, tv[i].key, tv[i].keylen); + if (err) + { + fail ("large stream, gcry_cipher_setkey failed: %s\n", + gpg_strerror (err)); + goto next; + } + + err = gcry_cipher_setiv (hde, tv[i].iv, tv[i].ivlen); + if (err) + { + fail ("large stream, gcry_cipher_setiv failed: %s\n", + gpg_strerror (err)); + goto next; + } + + for (j=0, p=buffer; j < buffersize/512; j++, p += 512) + { + err = gcry_cipher_encrypt (hde, p, 512, zeroes, 512); + if (err) + { + fail ("large stream, " + "gcry_cipher_encrypt (%d) block %d failed: %s\n", + i, j, gpg_strerror (err)); + goto next; + } + } + for (j=0, p=buffer+buffersize; j < 1024; j++, p++) + if (*p != 0x5a) + die ("large stream, buffer corrupted at j=%d\n", j); + + /* Now loop over all the data samples. */ + for (j = 0; tv[i].data[j].length; j++) + { + assert (tv[i].data[j].offset + tv[i].data[j].length <= buffersize); + + if (memcmp (tv[i].data[j].result, + buffer + tv[i].data[j].offset, tv[i].data[j].length)) + { + fail ("large stream, encrypt mismatch entry %d:%d\n", i, j); + mismatch (tv[i].data[j].result, tv[i].data[j].length, + buffer + tv[i].data[j].offset, tv[i].data[j].length); + } + } + + /* + * Let's do the same thing again but using changing block sizes. + */ + err = gcry_cipher_setkey (hde, tv[i].key, tv[i].keylen); + if (err) + { + fail ("large stream, gcry_cipher_setkey failed: %s\n", + gpg_strerror (err)); + goto next; + } + + err = gcry_cipher_setiv (hde, tv[i].iv, tv[i].ivlen); + if (err) + { + fail ("large stream, gcry_cipher_setiv failed: %s\n", + gpg_strerror (err)); + goto next; + } + + for (n=0, p=buffer, j = 0; n < buffersize; n += j, p += j) + { + switch (j) + { + case 0: j = 1; break; + case 1: j = 64; break; + case 64: j= 384; break; + case 384: j = 63; break; + case 63: j = 512; break; + case 512: j = 32; break; + case 32: j = 503; break; + default: j = 509; break; + } + if ( n + j >= buffersize ) + j = buffersize - n; + assert (j <= 512); + err = gcry_cipher_encrypt (hde, p, j, zeroes, j); + if (err) + { + fail ("large stream, " + "gcry_cipher_encrypt (%d) offset %u failed: %s\n", + i, n, gpg_strerror (err)); + goto next; + } + } + for (j=0, p=buffer+buffersize; j < 1024; j++, p++) + if (*p != 0x5a) + die ("large stream, buffer corrupted at j=%d (line %d)\n", + j, __LINE__); + + /* Now loop over all the data samples. */ + for (j = 0; tv[i].data[j].length; j++) + { + assert (tv[i].data[j].offset + tv[i].data[j].length <= buffersize); + + if (memcmp (tv[i].data[j].result, + buffer + tv[i].data[j].offset, tv[i].data[j].length)) + { + fail ("large stream var, encrypt mismatch entry %d:%d\n", i, j); + mismatch (tv[i].data[j].result, tv[i].data[j].length, + buffer + tv[i].data[j].offset, tv[i].data[j].length); + } + } + + next: + gcry_cipher_close (hde); + } + + gcry_free (buffer); + if (verbose) + fprintf (stderr, " Completed large block stream cipher checks.\n"); +} + + + /* Check that our bulk encryption fucntions work properly. */ static void check_bulk_cipher_modes (void) @@ -1605,6 +2167,9 @@ check_ciphers (void) static int algos2[] = { #if USE_ARCFOUR GCRY_CIPHER_ARCFOUR, +#endif +#if USE_SALSA20 + GCRY_CIPHER_SALSA20, #endif 0 }; @@ -1644,7 +2209,7 @@ check_ciphers (void) continue; } if (verbose) - fprintf (stderr, " checking `%s'\n", + fprintf (stderr, " checking %s\n", gcry_cipher_algo_name (algos2[i])); check_one_cipher (algos2[i], GCRY_CIPHER_MODE_STREAM, 0); @@ -1669,6 +2234,8 @@ check_cipher_modes(void) check_ctr_cipher (); check_cfb_cipher (); check_ofb_cipher (); + check_stream_cipher (); + check_stream_cipher_large_block (); if (verbose) fprintf (stderr, "Completed Cipher Mode checks.\n"); -- cgit v1.2.1