Implement the ChaCha family of stream ciphers.

This commit is contained in:
Dag-Erling Smørgrav 2017-03-16 18:02:56 +01:00
parent 1ca9e1928e
commit b67f0dbf00
7 changed files with 2360 additions and 1 deletions

View file

@ -12,3 +12,8 @@ The Cryb libraries include code written by Dag-Erling Smørgrav for the
University of Oslo. The University of Oslo has graciously granted the University of Oslo. The University of Oslo has graciously granted the
Cryb.to project permission to reuse this code under the 3-clause New Cryb.to project permission to reuse this code under the 3-clause New
BSD License. BSD License.
The Cryb libraries include test vectors generated by Joachim
Strömbergson and published under the 2-clause Simplified BSD License.
Joachim Strömbergson has graciously granted the Cryb.to project
permission to reuse this code under the 3-clause New BSD License.

66
include/cryb/chacha.h Normal file
View file

@ -0,0 +1,66 @@
/*-
* Copyright (c) 2017 Dag-Erling Smørgrav
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef CRYB_CHACHA_H_INCLUDED
#define CRYB_CHACHA_H_INCLUDED
#ifndef CRYB_TO
#include <cryb/to.h>
#endif
#include <cryb/cipher.h>
CRYB_BEGIN
#define chacha_cipher cryb_chacha_cipher
#define chacha_ctx cryb_chacha_ctx
#define chacha_init cryb_chacha_init
#define chacha_reset cryb_chacha_reset
#define chacha_keystream cryb_chacha_keystream
#define chacha_encrypt cryb_chacha_encrypt
#define chacha_decrypt cryb_chacha_decrypt
#define chacha_finish cryb_chacha_finish
extern cipher_algorithm chacha_cipher;
typedef struct {
uint32_t state[16];
unsigned int rounds;
} chacha_ctx;
void chacha_init(chacha_ctx *, cipher_mode, const uint8_t *, size_t);
void chacha_reset(chacha_ctx *, const uint8_t *, unsigned int);
size_t chacha_keystream(chacha_ctx *, uint8_t *, size_t);
size_t chacha_encrypt(chacha_ctx *, const void *, uint8_t *, size_t);
size_t chacha_decrypt(chacha_ctx *, const uint8_t *, void *, size_t);
void chacha_finish(chacha_ctx *);
CRYB_END
#endif

View file

@ -4,6 +4,7 @@ lib_LTLIBRARIES = libcryb-cipher.la
libcryb_cipher_la_SOURCES = \ libcryb_cipher_la_SOURCES = \
cryb_aes.c \ cryb_aes.c \
cryb_chacha.c \
cryb_rc4.c \ cryb_rc4.c \
\ \
cryb_cipher.c cryb_cipher.c

188
lib/cipher/cryb_chacha.c Normal file
View file

@ -0,0 +1,188 @@
/*-
* Copyright (c) 2017 Dag-Erling Smørgrav
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "cryb/impl.h"
#include <stdint.h>
#include <string.h>
#include <cryb/bitwise.h>
#include <cryb/endian.h>
#include <cryb/memset_s.h>
#include <cryb/chacha.h>
#define CHACHA_QR(x, a, b, c, d) \
do { \
x[a] = x[a] + x[b]; x[d] = rol32(x[d] ^ x[a], 16); \
x[c] = x[c] + x[d]; x[b] = rol32(x[b] ^ x[c], 12); \
x[a] = x[a] + x[b]; x[d] = rol32(x[d] ^ x[a], 8); \
x[c] = x[c] + x[d]; x[b] = rol32(x[b] ^ x[c], 7); \
} while (0)
static const char magic128[] = "expand 16-byte k";
static const char magic256[] = "expand 32-byte k";
/*
* Fill the state array with 16 bytes of magic and 32 bytes of key,
* repeating the key if necessary. The 8-byte stream position and the
* 8-byte nonce are initialized to all-zeroes. The number of rounds is
* set to 20, the most commonly used value.
*/
void
chacha_init(chacha_ctx *ctx, cipher_mode mode, const uint8_t *key, size_t keylen)
{
(void)mode;
memset(ctx, 0, sizeof *ctx);
if (keylen == 32) {
/* magic */
ctx->state[ 0] = le32dec(magic256 + 0);
ctx->state[ 1] = le32dec(magic256 + 4);
ctx->state[ 2] = le32dec(magic256 + 8);
ctx->state[ 3] = le32dec(magic256 + 12);
/* first half of key */
ctx->state[ 4] = le32dec(key + 0);
ctx->state[ 5] = le32dec(key + 4);
ctx->state[ 6] = le32dec(key + 8);
ctx->state[ 7] = le32dec(key + 12);
/* second half of key */
ctx->state[ 8] = le32dec(key + 16);
ctx->state[ 9] = le32dec(key + 20);
ctx->state[10] = le32dec(key + 24);
ctx->state[11] = le32dec(key + 28);
} else {
/* magic */
ctx->state[ 0] = le32dec(magic128 + 0);
ctx->state[ 1] = le32dec(magic128 + 4);
ctx->state[ 2] = le32dec(magic128 + 8);
ctx->state[ 3] = le32dec(magic128 + 12);
/* first half of key */
ctx->state[ 4] = le32dec(key + 0);
ctx->state[ 5] = le32dec(key + 4);
ctx->state[ 6] = le32dec(key + 8);
ctx->state[ 7] = le32dec(key + 12);
/* repeat first half of key */
ctx->state[ 8] = le32dec(key + 0);
ctx->state[ 9] = le32dec(key + 4);
ctx->state[10] = le32dec(key + 8);
ctx->state[11] = le32dec(key + 12);
}
ctx->rounds = 20;
}
/*
* Reset the stream position, load a new nonce, and change the number of
* rounds if requested.
*/
void
chacha_reset(chacha_ctx *ctx, const uint8_t *nonce, unsigned int rounds)
{
/* reset stream counter */
ctx->state[12] = 0;
ctx->state[13] = 0;
/* copy nonce */
ctx->state[14] = le32dec(nonce + 0);
ctx->state[15] = le32dec(nonce + 4);
/* set rounds if specified */
if (rounds != 0)
ctx->rounds = rounds;
}
/*
* Generate a block of keystream.
*/
size_t
chacha_keystream(chacha_ctx *ctx, uint8_t *ks, size_t len)
{
return (chacha_encrypt(ctx, NULL, ks, len));
}
/*
* Encryption: generate a block of keystream, xor it with the plaintext to
* produce the ciphertext, and increment the stream position.
*/
size_t
chacha_encrypt(chacha_ctx *ctx, const void *vpt, uint8_t *ct, size_t len)
{
const uint8_t *pt = vpt;
uint64_t ctr;
uint32_t mix[16];
uint8_t ks[64];
unsigned int b, i;
len -= len % sizeof ks;
for (b = 0; b < len; b += sizeof ks) {
memcpy(mix, ctx->state, sizeof mix);
for (i = 0; i < ctx->rounds; i += 2) {
CHACHA_QR(mix, 0, 4, 8, 12);
CHACHA_QR(mix, 1, 5, 9, 13);
CHACHA_QR(mix, 2, 6, 10, 14);
CHACHA_QR(mix, 3, 7, 11, 15);
CHACHA_QR(mix, 0, 5, 10, 15);
CHACHA_QR(mix, 1, 6, 11, 12);
CHACHA_QR(mix, 2, 7, 8, 13);
CHACHA_QR(mix, 3, 4, 9, 14);
}
for (i = 0; i < 16; ++i)
le32enc(ks + i * 4, ctx->state[i] + mix[i]);
if (pt == NULL) {
memcpy(ct, ks, sizeof ks);
ct += sizeof ks;
} else {
for (i = 0; i < 64 && i < len; ++i)
*ct++ = *pt++ ^ ks[i];
}
ctr = le64dec(ctx->state + 12);
le64enc(ctx->state + 12, ++ctr);
}
return (len);
}
/*
* Decryption: identical to encryption.
*/
size_t
chacha_decrypt(chacha_ctx *ctx, const uint8_t *ct, void *vpt, size_t len)
{
return (chacha_encrypt(ctx, ct, vpt, len));
}
/*
* Wipe our state.
*/
void
chacha_finish(chacha_ctx *ctx)
{
(void)memset_s(ctx, 0, sizeof *ctx, sizeof *ctx);
}

1
t/.gitignore vendored
View file

@ -1,6 +1,7 @@
/t_adler /t_adler
/t_aes /t_aes
/t_assert /t_assert
/t_chacha
/t_cipher /t_cipher
/t_core /t_core
/t_ctype /t_ctype

View file

@ -79,8 +79,9 @@ endif CRYB_RAND
if CRYB_CIPHER if CRYB_CIPHER
TESTS += t_cipher TESTS += t_cipher
t_cipher_LDADD = $(libt) $(libcipher) t_cipher_LDADD = $(libt) $(libcipher)
TESTS += t_aes t_rc4 TESTS += t_aes t_chacha t_rc4
t_aes_LDADD = $(libt) $(libcipher) t_aes_LDADD = $(libt) $(libcipher)
t_chacha_LDADD = $(libt) $(libcipher)
t_rc4_LDADD = $(libt) $(libcipher) t_rc4_LDADD = $(libt) $(libcipher)
endif CRYB_CIPHER endif CRYB_CIPHER

2097
t/t_chacha.c Normal file

File diff suppressed because it is too large Load diff