Index: /usr/src/sbin/geom/class/eli/geom_eli.c =================================================================== --- /usr/src/sbin/geom/class/eli/geom_eli.c (revision 276867) +++ /usr/src/sbin/geom/class/eli/geom_eli.c (working copy) @@ -758,6 +758,18 @@ G_ELI_VERSION_05); return; } + if ((md.md_ealgo == CRYPTO_CHACHA_8 || + md.md_ealgo == CRYPTO_CHACHA_12 || + md.md_ealgo == CRYPTO_CHACHA_20 || + md.md_ealgo == CRYPTO_XCHACHA_8 || + md.md_ealgo == CRYPTO_XCHACHA_12 || + md.md_ealgo == CRYPTO_XCHACHA_20) && + version < G_ELI_VERSION_05) { + gctl_error(req, + "ChaCha algorithm is supported starting from version %u.", + G_ELI_VERSION_05); + return; + } } val = gctl_get_intmax(req, "keylen"); md.md_keylen = val; Index: /usr/src/sys/crypto/chacha/chacha.h =================================================================== --- /usr/src/sys/crypto/chacha/chacha.h (revision 0) +++ /usr/src/sys/crypto/chacha/chacha.h (working copy) @@ -0,0 +1,1184 @@ +/*- + * Copyright (c) 2015 Rozhuk Ivan + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + * + * Author: Rozhuk Ivan + * + * chacha, hchacha and xchacha. + * + */ + +#ifndef CHACHA_H +#define CHACHA_H + +#ifdef _MSC_VER +# if _MSC_VER > 1000 +# pragma once +# endif // _MSC_VER > 1000 +#endif + + +#ifndef _WINDOWS +# include +# ifndef BSD +# define _GNU_SOURCE /* See feature_test_macros(7) */ +# define __USE_GNU 1 +# endif +# include +# include +# ifdef _KERNEL +# include +# else +# include /* memcpy, memmove, memset... */ +# include +# endif +# if __x86_64__ || __ppc64__ || __LP64__ +# define CHACHA_X64 +# else +# define CHACHA_X32 +# endif + static void *(*volatile chacha_memset_volatile)(void *, int, size_t) = memset; +# define chacha_bzero(mem, size) chacha_memset_volatile(mem, 0, size) +#else +# include +# define uint8_t unsigned char +# define uint32_t DWORD +# define size_t SIZE_T +# define chacha_bzero(mem, size) SecureZeroMemory(mem, size) +# define ROTL32(v, n) _lrotl(v, n) +# define htole64(n) n +# define le64toh(n) n +# if _WIN64 +# define CHACHA_X64 +# else +# define CHACHA_X32 +# endif +#endif +#if defined(_MSC_VER) || defined(__INTEL_COMPILER) +# define CHACHA_ALIGN(v) __declspec(align(v)) /* DECLSPEC_ALIGN() */ +#else /* GCC/clang */ +# define CHACHA_ALIGN(v) __attribute__((aligned(v))) +#endif + + +#define CHACHA_BLOCK_LEN 64 +#define CHACHA_KEY_128_LEN 16 /* 128 bit key */ +#define CHACHA_KEY_256_LEN 32 /* 256 bit key, normal */ +#define CHACHA_IV_LEN 8 /* 64 bit */ +#define XCHACHA_IV_LEN 24 /* 192 bit */ + + +/* Simple ChaCha context. */ +typedef struct chacha_context_s { + /* const(16) + key(32) + counter(8) + iv(8) */ + CHACHA_ALIGN(8) uint32_t state[(CHACHA_BLOCK_LEN / sizeof(uint32_t))]; + /* Temp buf for chacha transform block. */ + CHACHA_ALIGN(8) uint32_t x[(CHACHA_BLOCK_LEN / sizeof(uint32_t))]; + size_t rounds; +} chacha_context_t, *chacha_context_p; + +/* ChaCha context for streams handle. */ +typedef struct chacha_context_str_s { + CHACHA_ALIGN(8) chacha_context_t c; /* Original ChaCha context. */ + /* Key stream. */ + size_t ks_len; /* Key stream num bytes. */ + CHACHA_ALIGN(8) uint32_t ks[(CHACHA_BLOCK_LEN / sizeof(uint32_t))]; +} chacha_context_str_t, *chacha_context_str_p; + + +#define CHACHA_PTR_IS_ALIGNED4(p) (0 == (((size_t)p) & 3)) +#define CHACHA_PTR_IS_ALIGNED8(p) (0 == (((size_t)p) & 7)) +#define CHACHA_PTR_8TO32(ptr) ((uint32_t*)(void*)(ptr)) +#define CHACHA_PTR_8TO64(ptr) ((uint64_t*)(void*)(ptr)) + +/* interpret four 8 bit unsigned integers as a 32 bit unsigned integer in little endian */ +static inline uint32_t +U8TO32_LITTLE(const uint8_t *p) { + return + (((uint32_t)(p[0]) ) | + ((uint32_t)(p[1]) << 8) | + ((uint32_t)(p[2]) << 16) | + ((uint32_t)(p[3]) << 24)); +} + +/* store a 32 bit unsigned integer as four 8 bit unsigned integers in little endian */ +static inline void +U32TO8_LITTLE(uint8_t *p, uint32_t v) { + p[0] = (uint8_t)(v ); + p[1] = (uint8_t)(v >> 8); + p[2] = (uint8_t)(v >> 16); + p[3] = (uint8_t)(v >> 24); +} + + +/* sigma = "expand 32-byte k", as 4 little endian 32-bit unsigned integers */ +static const uint32_t chacha_constants_k256[4] = { + 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574 +}; +/* tau = "expand 16-byte k", as 4 little endian 32-bit unsigned integers */ +static const uint32_t chacha_constants_k128[4] = { + 0x61707865, 0x3120646e, 0x79622d36, 0x6b206574 +}; + +/* 32 bit left rotate */ +#ifndef ROTL32 +#define ROTL32(v, n) ((((uint32_t)(v)) << (n)) | (((uint32_t)(v)) >> (32 - (n)))) +#endif + +#define CHACHA_QUARTERROUND(a, b, c, d) { \ + a += b; d = ROTL32((d ^ a), 16); \ + c += d; b = ROTL32((b ^ c), 12); \ + a += b; d = ROTL32((d ^ a), 8); \ + c += d; b = ROTL32((b ^ c), 7); \ +} + +#define CHACHA_DOUBLEROUND(n) { \ + CHACHA_QUARTERROUND(n[0], n[4], n[ 8], n[12]) \ + CHACHA_QUARTERROUND(n[1], n[5], n[ 9], n[13]) \ + CHACHA_QUARTERROUND(n[2], n[6], n[10], n[14]) \ + CHACHA_QUARTERROUND(n[3], n[7], n[11], n[15]) \ + CHACHA_QUARTERROUND(n[0], n[5], n[10], n[15]) \ + CHACHA_QUARTERROUND(n[1], n[6], n[11], n[12]) \ + CHACHA_QUARTERROUND(n[2], n[7], n[ 8], n[13]) \ + CHACHA_QUARTERROUND(n[3], n[4], n[ 9], n[14]) \ +} + +#define CHACHA_BLOCK_ADD32(dst, src) { \ + ((uint32_t*)(dst))[ 0] += ((uint32_t*)(src))[ 0]; \ + ((uint32_t*)(dst))[ 1] += ((uint32_t*)(src))[ 1]; \ + ((uint32_t*)(dst))[ 2] += ((uint32_t*)(src))[ 2]; \ + ((uint32_t*)(dst))[ 3] += ((uint32_t*)(src))[ 3]; \ + ((uint32_t*)(dst))[ 4] += ((uint32_t*)(src))[ 4]; \ + ((uint32_t*)(dst))[ 5] += ((uint32_t*)(src))[ 5]; \ + ((uint32_t*)(dst))[ 6] += ((uint32_t*)(src))[ 6]; \ + ((uint32_t*)(dst))[ 7] += ((uint32_t*)(src))[ 7]; \ + ((uint32_t*)(dst))[ 8] += ((uint32_t*)(src))[ 8]; \ + ((uint32_t*)(dst))[ 9] += ((uint32_t*)(src))[ 9]; \ + ((uint32_t*)(dst))[10] += ((uint32_t*)(src))[10]; \ + ((uint32_t*)(dst))[11] += ((uint32_t*)(src))[11]; \ + ((uint32_t*)(dst))[12] += ((uint32_t*)(src))[12]; \ + ((uint32_t*)(dst))[13] += ((uint32_t*)(src))[13]; \ + ((uint32_t*)(dst))[14] += ((uint32_t*)(src))[14]; \ + ((uint32_t*)(dst))[15] += ((uint32_t*)(src))[15]; \ +} + +/* Src aligned to 4 byte, dst unligned. */ +#define CHACHA_BLOCK_COPY_2UNALIGN4(dst, src) { \ + U32TO8_LITTLE((((uint8_t*)(dst)) + 0), CHACHA_PTR_8TO32(src)[ 0]); \ + U32TO8_LITTLE((((uint8_t*)(dst)) + 4), CHACHA_PTR_8TO32(src)[ 1]); \ + U32TO8_LITTLE((((uint8_t*)(dst)) + 8), CHACHA_PTR_8TO32(src)[ 2]); \ + U32TO8_LITTLE((((uint8_t*)(dst)) + 12), CHACHA_PTR_8TO32(src)[ 3]); \ + U32TO8_LITTLE((((uint8_t*)(dst)) + 16), CHACHA_PTR_8TO32(src)[ 4]); \ + U32TO8_LITTLE((((uint8_t*)(dst)) + 20), CHACHA_PTR_8TO32(src)[ 5]); \ + U32TO8_LITTLE((((uint8_t*)(dst)) + 24), CHACHA_PTR_8TO32(src)[ 6]); \ + U32TO8_LITTLE((((uint8_t*)(dst)) + 28), CHACHA_PTR_8TO32(src)[ 7]); \ + U32TO8_LITTLE((((uint8_t*)(dst)) + 32), CHACHA_PTR_8TO32(src)[ 8]); \ + U32TO8_LITTLE((((uint8_t*)(dst)) + 36), CHACHA_PTR_8TO32(src)[ 9]); \ + U32TO8_LITTLE((((uint8_t*)(dst)) + 40), CHACHA_PTR_8TO32(src)[10]); \ + U32TO8_LITTLE((((uint8_t*)(dst)) + 44), CHACHA_PTR_8TO32(src)[11]); \ + U32TO8_LITTLE((((uint8_t*)(dst)) + 48), CHACHA_PTR_8TO32(src)[12]); \ + U32TO8_LITTLE((((uint8_t*)(dst)) + 52), CHACHA_PTR_8TO32(src)[13]); \ + U32TO8_LITTLE((((uint8_t*)(dst)) + 56), CHACHA_PTR_8TO32(src)[14]); \ + U32TO8_LITTLE((((uint8_t*)(dst)) + 60), CHACHA_PTR_8TO32(src)[15]); \ +} + +/* Src and dst aligned to 4 byte. */ +#define CHACHA_BLOCK_COPY_ALIGN4(dst, src) { \ + CHACHA_PTR_8TO32(dst)[ 0] = CHACHA_PTR_8TO32(src)[ 0]; \ + CHACHA_PTR_8TO32(dst)[ 1] = CHACHA_PTR_8TO32(src)[ 1]; \ + CHACHA_PTR_8TO32(dst)[ 2] = CHACHA_PTR_8TO32(src)[ 2]; \ + CHACHA_PTR_8TO32(dst)[ 3] = CHACHA_PTR_8TO32(src)[ 3]; \ + CHACHA_PTR_8TO32(dst)[ 4] = CHACHA_PTR_8TO32(src)[ 4]; \ + CHACHA_PTR_8TO32(dst)[ 5] = CHACHA_PTR_8TO32(src)[ 5]; \ + CHACHA_PTR_8TO32(dst)[ 6] = CHACHA_PTR_8TO32(src)[ 6]; \ + CHACHA_PTR_8TO32(dst)[ 7] = CHACHA_PTR_8TO32(src)[ 7]; \ + CHACHA_PTR_8TO32(dst)[ 8] = CHACHA_PTR_8TO32(src)[ 8]; \ + CHACHA_PTR_8TO32(dst)[ 9] = CHACHA_PTR_8TO32(src)[ 9]; \ + CHACHA_PTR_8TO32(dst)[10] = CHACHA_PTR_8TO32(src)[10]; \ + CHACHA_PTR_8TO32(dst)[11] = CHACHA_PTR_8TO32(src)[11]; \ + CHACHA_PTR_8TO32(dst)[12] = CHACHA_PTR_8TO32(src)[12]; \ + CHACHA_PTR_8TO32(dst)[13] = CHACHA_PTR_8TO32(src)[13]; \ + CHACHA_PTR_8TO32(dst)[14] = CHACHA_PTR_8TO32(src)[14]; \ + CHACHA_PTR_8TO32(dst)[15] = CHACHA_PTR_8TO32(src)[15]; \ +} + +/* Src and dst aligned to 8 bytes. */ +#define CHACHA_BLOCK_COPY_ALIGN8(dst, src) { \ + CHACHA_PTR_8TO64(dst)[0] = CHACHA_PTR_8TO64(src)[0]; \ + CHACHA_PTR_8TO64(dst)[1] = CHACHA_PTR_8TO64(src)[1]; \ + CHACHA_PTR_8TO64(dst)[2] = CHACHA_PTR_8TO64(src)[2]; \ + CHACHA_PTR_8TO64(dst)[3] = CHACHA_PTR_8TO64(src)[3]; \ + CHACHA_PTR_8TO64(dst)[4] = CHACHA_PTR_8TO64(src)[4]; \ + CHACHA_PTR_8TO64(dst)[5] = CHACHA_PTR_8TO64(src)[5]; \ + CHACHA_PTR_8TO64(dst)[6] = CHACHA_PTR_8TO64(src)[6]; \ + CHACHA_PTR_8TO64(dst)[7] = CHACHA_PTR_8TO64(src)[7]; \ +} + +/* Res and a unligned, b aligned to 4 byte. */ +#define CHACHA_BLOCK_XOR_2UNALIGN4_LINE(res, a, b, offset) \ + U32TO8_LITTLE((((uint8_t*)(res)) + (offset * 4)), \ + (U8TO32_LITTLE((((uint8_t*)(a)) + (offset * 4))) ^ \ + CHACHA_PTR_8TO32(b)[offset])) +#define CHACHA_BLOCK_XOR_2UNALIGN4(res, a, b) { \ + CHACHA_BLOCK_XOR_2UNALIGN4_LINE(res, a, b, 0); \ + CHACHA_BLOCK_XOR_2UNALIGN4_LINE(res, a, b, 1); \ + CHACHA_BLOCK_XOR_2UNALIGN4_LINE(res, a, b, 2); \ + CHACHA_BLOCK_XOR_2UNALIGN4_LINE(res, a, b, 3); \ + CHACHA_BLOCK_XOR_2UNALIGN4_LINE(res, a, b, 4); \ + CHACHA_BLOCK_XOR_2UNALIGN4_LINE(res, a, b, 5); \ + CHACHA_BLOCK_XOR_2UNALIGN4_LINE(res, a, b, 6); \ + CHACHA_BLOCK_XOR_2UNALIGN4_LINE(res, a, b, 7); \ + CHACHA_BLOCK_XOR_2UNALIGN4_LINE(res, a, b, 8); \ + CHACHA_BLOCK_XOR_2UNALIGN4_LINE(res, a, b, 9); \ + CHACHA_BLOCK_XOR_2UNALIGN4_LINE(res, a, b, 10); \ + CHACHA_BLOCK_XOR_2UNALIGN4_LINE(res, a, b, 11); \ + CHACHA_BLOCK_XOR_2UNALIGN4_LINE(res, a, b, 12); \ + CHACHA_BLOCK_XOR_2UNALIGN4_LINE(res, a, b, 13); \ + CHACHA_BLOCK_XOR_2UNALIGN4_LINE(res, a, b, 14); \ + CHACHA_BLOCK_XOR_2UNALIGN4_LINE(res, a, b, 15); \ +} + +/* Res, a and b aligned to 4 byte. */ +#define CHACHA_BLOCK_XOR_ALIGN4_LINE(res, a, b, offset) \ + CHACHA_PTR_8TO32(res)[offset] = \ + (CHACHA_PTR_8TO32(a)[offset] ^ CHACHA_PTR_8TO32(b)[offset]) +#define CHACHA_BLOCK_XOR_ALIGN4(res, a, b) { \ + CHACHA_BLOCK_XOR_ALIGN4_LINE(res, a, b, 0); \ + CHACHA_BLOCK_XOR_ALIGN4_LINE(res, a, b, 1); \ + CHACHA_BLOCK_XOR_ALIGN4_LINE(res, a, b, 2); \ + CHACHA_BLOCK_XOR_ALIGN4_LINE(res, a, b, 3); \ + CHACHA_BLOCK_XOR_ALIGN4_LINE(res, a, b, 4); \ + CHACHA_BLOCK_XOR_ALIGN4_LINE(res, a, b, 5); \ + CHACHA_BLOCK_XOR_ALIGN4_LINE(res, a, b, 6); \ + CHACHA_BLOCK_XOR_ALIGN4_LINE(res, a, b, 7); \ + CHACHA_BLOCK_XOR_ALIGN4_LINE(res, a, b, 8); \ + CHACHA_BLOCK_XOR_ALIGN4_LINE(res, a, b, 9); \ + CHACHA_BLOCK_XOR_ALIGN4_LINE(res, a, b, 10); \ + CHACHA_BLOCK_XOR_ALIGN4_LINE(res, a, b, 11); \ + CHACHA_BLOCK_XOR_ALIGN4_LINE(res, a, b, 12); \ + CHACHA_BLOCK_XOR_ALIGN4_LINE(res, a, b, 13); \ + CHACHA_BLOCK_XOR_ALIGN4_LINE(res, a, b, 14); \ + CHACHA_BLOCK_XOR_ALIGN4_LINE(res, a, b, 15); \ +} + +/* Res, a and b aligned to 8 bytes. */ +#define CHACHA_BLOCK_XOR_ALIGN8_LINE(res, a, b, offset) \ + CHACHA_PTR_8TO64(res)[offset] = \ + (CHACHA_PTR_8TO64(a)[offset] ^ CHACHA_PTR_8TO64(b)[offset]) +#define CHACHA_BLOCK_XOR_ALIGN8(res, a, b) { \ + CHACHA_BLOCK_XOR_ALIGN8_LINE(res, a, b, 0); \ + CHACHA_BLOCK_XOR_ALIGN8_LINE(res, a, b, 1); \ + CHACHA_BLOCK_XOR_ALIGN8_LINE(res, a, b, 2); \ + CHACHA_BLOCK_XOR_ALIGN8_LINE(res, a, b, 3); \ + CHACHA_BLOCK_XOR_ALIGN8_LINE(res, a, b, 4); \ + CHACHA_BLOCK_XOR_ALIGN8_LINE(res, a, b, 5); \ + CHACHA_BLOCK_XOR_ALIGN8_LINE(res, a, b, 6); \ + CHACHA_BLOCK_XOR_ALIGN8_LINE(res, a, b, 7); \ +} + +#ifdef CHACHA_X64 +# define CHACHA_BLOCK_XOR_ALIGN CHACHA_BLOCK_XOR_ALIGN8 +#else +# define CHACHA_BLOCK_XOR_ALIGN CHACHA_BLOCK_XOR_ALIGN4 +#endif + + + +/* key - 16/32 bytes */ +static inline void +chacha_key_set(chacha_context_p ctx, const uint8_t *key, const size_t key_size) { + + if (256 == key_size || CHACHA_KEY_256_LEN == key_size) { /* recommended */ + ctx->state[ 0] = chacha_constants_k256[0]; + ctx->state[ 1] = chacha_constants_k256[1]; + ctx->state[ 2] = chacha_constants_k256[2]; + ctx->state[ 3] = chacha_constants_k256[3]; + ctx->state[ 4] = U8TO32_LITTLE(key + 0); + ctx->state[ 5] = U8TO32_LITTLE(key + 4); + ctx->state[ 6] = U8TO32_LITTLE(key + 8); + ctx->state[ 7] = U8TO32_LITTLE(key + 12); + ctx->state[ 8] = U8TO32_LITTLE(key + 16); + ctx->state[ 9] = U8TO32_LITTLE(key + 20); + ctx->state[10] = U8TO32_LITTLE(key + 24); + ctx->state[11] = U8TO32_LITTLE(key + 28); + } else { + ctx->state[ 0] = chacha_constants_k128[0]; + ctx->state[ 1] = chacha_constants_k128[1]; + ctx->state[ 2] = chacha_constants_k128[2]; + ctx->state[ 3] = chacha_constants_k128[3]; + ctx->state[ 4] = U8TO32_LITTLE(key + 0); + ctx->state[ 5] = U8TO32_LITTLE(key + 4); + ctx->state[ 6] = U8TO32_LITTLE(key + 8); + ctx->state[ 7] = U8TO32_LITTLE(key + 12); + ctx->state[ 8] = U8TO32_LITTLE(key + 0); + ctx->state[ 9] = U8TO32_LITTLE(key + 4); + ctx->state[10] = U8TO32_LITTLE(key + 8); + ctx->state[11] = U8TO32_LITTLE(key + 12); + } +} + +/* counter - 8 bytes, optional */ +static inline void +chacha_counter_set(chacha_context_p ctx, const uint8_t *counter) { + + if (counter == NULL) { + ctx->state[12] = 0; + ctx->state[13] = 0; + } else { + ctx->state[12] = U8TO32_LITTLE(counter + 0); + ctx->state[13] = U8TO32_LITTLE(counter + 4); + } +} +static inline void +chacha_counter_set_u64(chacha_context_p ctx, uint64_t counter) { + + counter = htole64(counter); + ctx->state[12] = (uint32_t)counter; + ctx->state[13] = (uint32_t)(counter >> 32); +} +static inline uint64_t +chacha_counter_get_u64(chacha_context_p ctx) { + + return le64toh(((uint64_t)ctx->state[12]) | (((uint64_t)ctx->state[13]) << 32)); +} + +/* iv - 8 bytes, optional */ +static inline void +chacha_iv_set(chacha_context_p ctx, const uint8_t *iv) { + + if (iv == NULL) { + ctx->state[14] = 0; + ctx->state[15] = 0; + } else { + ctx->state[14] = U8TO32_LITTLE(iv + 0); + ctx->state[15] = U8TO32_LITTLE(iv + 4); + } +} + +/* key - 16/32 bytes + * iv - 16 bytes, optional + * rounds - 8/12/20 + * dst - point to 32 bytes buf + */ +static inline void +hchacha(const uint8_t *key, const size_t key_size, const uint8_t *iv, size_t rounds, + uint8_t *dst) { + size_t i; + chacha_context_t ctx;; + + chacha_key_set(&ctx, key, key_size); + /* Set IV. */ + if (NULL != iv) { + ctx.state[12] = U8TO32_LITTLE(iv + 0); /* Counter. */ + ctx.state[13] = U8TO32_LITTLE(iv + 4); + ctx.state[14] = U8TO32_LITTLE(iv + 8); /* IV. */ + ctx.state[15] = U8TO32_LITTLE(iv + 12); + } else { + ctx.state[12] = 0; /* Counter. */ + ctx.state[13] = 0; + ctx.state[14] = 0; /* IV. */ + ctx.state[15] = 0; + } + + for (i = 0; i < rounds; i += 2) { + CHACHA_DOUBLEROUND(ctx.state); + } + + /* Indices for the chacha constant. */ + U32TO8_LITTLE(dst + 0, ctx.state[ 0]); + U32TO8_LITTLE(dst + 4, ctx.state[ 1]); + U32TO8_LITTLE(dst + 8, ctx.state[ 2]); + U32TO8_LITTLE(dst + 12, ctx.state[ 3]); + /* Indices for the iv. */ + U32TO8_LITTLE(dst + 16, ctx.state[12]); + U32TO8_LITTLE(dst + 20, ctx.state[13]); + U32TO8_LITTLE(dst + 24, ctx.state[14]); + U32TO8_LITTLE(dst + 28, ctx.state[15]); + /* Zero only part of context. */ + chacha_bzero(ctx.state, CHACHA_BLOCK_LEN); +} + +/* key - 16/32 bytes + * iv - 24 bytes, optional + * rounds - 8/12/20 + * xchacha(key, counter, iv, src) = chacha(hchacha(key, iv[0:15]), counter, iv[16:23], src) + */ +static inline void +xchacha_set_key_iv_rounds(chacha_context_p ctx, const uint8_t *key, const size_t key_size, + const uint8_t *iv, const size_t rounds) { + + /* Chacha key allways 256 bits after hchacha */ + ctx->state[0] = chacha_constants_k256[0]; + ctx->state[1] = chacha_constants_k256[1]; + ctx->state[2] = chacha_constants_k256[2]; + ctx->state[3] = chacha_constants_k256[3]; + /* Gen 256 bits key from key and first part of iv. */ + hchacha(key, key_size, iv, rounds, (uint8_t*)&ctx->state[4]); + if (NULL != iv) { + chacha_iv_set(ctx, (iv + 16)); + } else { + chacha_iv_set(ctx, NULL); + } + ctx->rounds = rounds; +} + +/* Block tranform. */ +static inline void +chacha_block_aligned8(chacha_context_p ctx, const uint8_t *src, uint8_t *dst) { + size_t i; + + /* Load state into temp x. */ + CHACHA_BLOCK_COPY_ALIGN8(ctx->x, ctx->state); + /* Transform 1 temp x: rotl + add. */ + for (i = 0; i < ctx->rounds; i += 2) { + CHACHA_DOUBLEROUND(ctx->x); + } + /* Transform 2 temp x: x += state. */ + CHACHA_BLOCK_ADD32(ctx->x, ctx->state); + /* Load src data and xor with temp x and store to dst. */ + if (NULL != src) { + CHACHA_BLOCK_XOR_ALIGN8(dst, src, ctx->x); + } else { + CHACHA_BLOCK_COPY_ALIGN8(dst, ctx->x); + } + /* Increment the 64 bit counter, split in to two 32 bit halves. */ + /* Stopping at 2^70 bytes per nonce is user's responsibility. */ + ctx->state[12] ++; + if (0 == ctx->state[12]) + ctx->state[13] ++; +} +static inline void +chacha_block_aligned4(chacha_context_p ctx, const uint8_t *src, uint8_t *dst) { + size_t i; + + /* Load state into temp x. */ + CHACHA_BLOCK_COPY_ALIGN4(ctx->x, ctx->state); + /* Transform 1 temp x: rotl + add. */ + for (i = 0; i < ctx->rounds; i += 2) { + CHACHA_DOUBLEROUND(ctx->x); + } + /* Transform 2 temp x: x += state. */ + CHACHA_BLOCK_ADD32(ctx->x, ctx->state); + /* Load src data and xor with temp x and store to dst. */ + if (NULL != src) { + CHACHA_BLOCK_XOR_ALIGN4(dst, src, ctx->x); + } else { + CHACHA_BLOCK_COPY_ALIGN4(dst, ctx->x); + } + /* Increment the 64 bit counter, split in to two 32 bit halves. */ + /* Stopping at 2^70 bytes per nonce is user's responsibility. */ + ctx->state[12] ++; + if (0 == ctx->state[12]) + ctx->state[13] ++; +} +#ifdef CHACHA_X64 +# define chacha_block_aligned chacha_block_aligned8 +#else +# define chacha_block_aligned chacha_block_aligned4 +#endif +static inline void +chacha_block_unaligneg(chacha_context_p ctx, const uint8_t *src, uint8_t *dst) { + size_t i; + + /* Load state into temp x. */ + CHACHA_BLOCK_COPY_ALIGN4(ctx->x, ctx->state); + /* Transform 1 temp x: rotl + add. */ + for (i = 0; i < ctx->rounds; i += 2) { + CHACHA_DOUBLEROUND(ctx->x); + } + /* Transform 2 temp x: x += state. */ + CHACHA_BLOCK_ADD32(ctx->x, ctx->state); + /* Load src data and xor with temp x and store to dst. */ + if (NULL != src) { + CHACHA_BLOCK_XOR_2UNALIGN4(dst, src, ctx->x); + } else { + CHACHA_BLOCK_COPY_2UNALIGN4(dst, ctx->x); + } + /* Increment the 64 bit counter, split in to two 32 bit halves. */ + /* Stopping at 2^70 bytes per nonce is user's responsibility. */ + ctx->state[12] ++; + if (0 == ctx->state[12]) + ctx->state[13] ++; +} + +/* Buf tranform. */ +static inline void +chacha_blocks_transform(chacha_context_p ctx, const uint8_t *src, size_t blocks_count, + uint8_t *dst) { + + if (0 == blocks_count) + return; +#ifdef CHACHA_X64 + if ((CHACHA_PTR_IS_ALIGNED8(src) && CHACHA_PTR_IS_ALIGNED8(dst))) { + for (; 0 != blocks_count; blocks_count --) { + /* Load, transform and save block. */ + chacha_block_aligned8(ctx, src, dst); + if (NULL != src) + src += CHACHA_BLOCK_LEN; + dst += CHACHA_BLOCK_LEN; + } + } else +#endif + if ((CHACHA_PTR_IS_ALIGNED4(src) && CHACHA_PTR_IS_ALIGNED4(dst))) { + for (; 0 != blocks_count; blocks_count --) { + /* Load, transform and save block. */ + chacha_block_aligned4(ctx, src, dst); + if (NULL != src) + src += CHACHA_BLOCK_LEN; + dst += CHACHA_BLOCK_LEN; + } + } else { + for (; 0 != blocks_count; blocks_count --) { + /* Load, transform and save block. */ + chacha_block_unaligneg(ctx, src, dst); + if (NULL != src) + src += CHACHA_BLOCK_LEN; + dst += CHACHA_BLOCK_LEN; + } + } +} + + +/* key - 16/32 bytes + * counter - 8 bytes, optional + * iv - 8 bytes, optional + * rounds - 8/12/20 + */ +static inline void +chacha_init(chacha_context_p ctx, const uint8_t *key, const size_t key_size, + const uint8_t *counter, const uint8_t *iv, const size_t rounds) { + + chacha_key_set(ctx, key, key_size); + chacha_counter_set(ctx, counter); + chacha_iv_set(ctx, iv); + ctx->rounds = rounds; +} +/* key - 16/32 bytes + * counter - 8 bytes, optional + * iv - 24 bytes, optional + * rounds - 8/12/20 + * xchacha(key, counter, iv, src) = chacha(hchacha(key, iv[0:15]), counter, iv[16:23], src) + */ +static inline void +xchacha_init(chacha_context_p ctx, const uint8_t *key, const size_t key_size, + const uint8_t *counter, const uint8_t *iv, const size_t rounds) { + + xchacha_set_key_iv_rounds(ctx, key, key_size, iv, rounds); + chacha_counter_set(ctx, counter); +} + +static inline void +chacha_final(chacha_context_p ctx) { + + chacha_bzero(ctx, sizeof(chacha_context_t)); +} + + +/* ChaCha partial messages process / stream. */ +static inline void +chacha_str_init(chacha_context_str_p ctx, const uint8_t *key, const size_t key_size, + const uint8_t *counter, const uint8_t *iv, const size_t rounds) { + + chacha_init(&ctx->c, key, key_size, counter, iv, rounds); + ctx->ks_len = 0; +} + +static inline void +xchacha_str_init(chacha_context_str_p ctx, const uint8_t *key, const size_t key_size, + const uint8_t *counter, const uint8_t *iv, const size_t rounds) { + + xchacha_init(&ctx->c, key, key_size, counter, iv, rounds); + ctx->ks_len = 0; +} + +static inline void +chacha_str_data_crypt(chacha_context_str_p ctx, const uint8_t *src, size_t bytes, + uint8_t *dst) { + uint8_t *ptr; + size_t i, count; + + if (0 == bytes) + return; + count = ctx->ks_len; + if (0 != count) { /* Have saved key stream. */ + /* Key stream start point. */ + ptr = (((uint8_t*)ctx->ks) + (CHACHA_BLOCK_LEN - ctx->ks_len)); + /* Limit num of bytes to process. */ + if (count > bytes) + count = bytes; + if (NULL != src) { + /* XOR with key stream. Slow. */ + for (i = 0; i < count; i ++) + dst[i] = src[i] ^ ptr[i]; + src += count; + } else { + memcpy(dst, ptr, count); + } + ctx->ks_len -= count; /* Update key stream saved len. */ + dst += count; + bytes -= count; + if (0 == bytes) + return; + } + if (bytes >= CHACHA_BLOCK_LEN) { + count = (bytes / CHACHA_BLOCK_LEN); /* blocks_count */ + chacha_blocks_transform(&ctx->c, src, count, dst); + count *= CHACHA_BLOCK_LEN; /* blocks_size */ + if (NULL != src) + src += count; + dst += count; + bytes -= count; + } + /*Process tail. */ + if (0 != bytes) { /* Incomplete data block. */ + /* Replace src and dst buf with temp buf, copy src data to temp. */ + ctx->ks_len = (CHACHA_BLOCK_LEN - bytes); + ptr = dst; + dst = (uint8_t*)ctx->ks; + if (NULL != src) { + memcpy(dst, src, bytes); + memset((dst + bytes), 0, ctx->ks_len); + src = dst; + } + /* Transform block. */ + chacha_block_aligned(&ctx->c, src, dst); + /* Copy result from temp buf to dst. */ + memcpy(ptr, dst, bytes); + } +} + +static inline void +chacha_str_final(chacha_context_str_p ctx) { + + chacha_bzero(ctx, sizeof(chacha_context_str_t)); +} + +/* One shot chacha. */ +/* key - 16/32 bytes + * counter - 8 bytes, optional + * iv - 8 bytes, optional + * rounds - 8/12/20 + */ +static inline void +chacha(const uint8_t *key, const size_t key_size, const uint8_t *counter, const uint8_t *iv, + const size_t rounds, const uint8_t *src, const size_t bytes, uint8_t *dst) { + chacha_context_str_t ctx; + + if (0 == bytes) + return; + chacha_str_init(&ctx, key, key_size, counter, iv, rounds); + chacha_str_data_crypt(&ctx, src, bytes, dst); + chacha_str_final(&ctx); +} + +/* One shot xchacha. */ +/* key - 16/32 bytes + * counter - 8 bytes, optional + * iv - 24 bytes, optional + * rounds - 8/12/20 + * xchacha(key, counter, iv, src) = chacha(hchacha(key, iv[0:15]), counter, iv[16:23], src) + */ +static inline void +xchacha(const uint8_t *key, const size_t key_size, const uint8_t *counter, const uint8_t *iv, + const size_t rounds, const uint8_t *src, const size_t bytes, uint8_t *dst) { + chacha_context_str_t ctx; + + if (0 == bytes) + return; + xchacha_str_init(&ctx, key, key_size, counter, iv, rounds); + chacha_str_data_crypt(&ctx, src, bytes, dst); + chacha_str_final(&ctx); +} + + + + +#ifdef CHACHA_SELF_TEST + +typedef struct chacha_test1_vectors_s { + size_t rounds; + uint8_t *key; + size_t key_size; + uint8_t *count; + uint8_t *iv; /* nonce */ + size_t data_size; + uint8_t *plain; + uint8_t *encrypted; /* key stream */ +} chacha_tst1v_t, *chacha_tst1v_p; + +static chacha_tst1v_t chacha_tst1v[] = { + /* From: https://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-00 */ + /* From: http://www.potaroo.net/ietf/idref/draft-nir-cfrg-chacha20-poly1305/#page-23 */ + { /* A.1: Test Vector #2 */ + /*.rounds =*/ 20, + /*.key =*/ (uint8_t*)"0000000000000000000000000000000000000000000000000000000000000000", + /*.key_size =*/ 64, + /*.count =*/ (uint8_t*)"0000000000000001", + /*.iv =*/ (uint8_t*)"0000000000000000", + /*.data_size =*/ 128, + /*.plain =*/ (uint8_t*)NULL, + /*.encrypted =*/ (uint8_t*)"9f07e7be5551387a98ba977c732d080dcb0f29a048e3656912c6533e32ee7aed29b721769ce64e43d57133b074d839d531ed1f28510afb45ace10a1f4b794d6f", + }, { + /*.rounds =*/ 20, + /*.key =*/ (uint8_t*)"0000000000000000000000000000000000000000000000000000000000000001", + /*.key_size =*/ 64, + /*.count =*/ (uint8_t*)NULL, + /*.iv =*/ (uint8_t*)"0000000000000000", + /*.data_size =*/ 120, + /*.plain =*/ (uint8_t*)NULL, + /*.encrypted =*/ (uint8_t*)"4540f05a9f1fb296d7736e7b208e3c96eb4fe1834688d2604f450952ed432d41bbe2a0b6ea7566d2a5d1e7e20d42af2c53d792b1c43fea817e9ad275", + }, { /* A.1: Test Vector #3 */ + /*.rounds =*/ 20, + /*.key =*/ (uint8_t*)"0000000000000000000000000000000000000000000000000000000000000001", + /*.key_size =*/ 64, + /*.count =*/ (uint8_t*)"0000000000000001", + /*.iv =*/ (uint8_t*)"0000000000000000", + /*.data_size =*/ 128, + /*.plain =*/ (uint8_t*)NULL, + /*.encrypted =*/ (uint8_t*)"3aeb5224ecf849929b9d828db1ced4dd832025e8018b8160b82284f3c949aa5a8eca00bbb4a73bdad192b5c42f73f2fd4e273644c8b36125a64addeb006c13a0", + }, { + /*.rounds =*/ 20, + /*.key =*/ (uint8_t*)"0000000000000000000000000000000000000000000000000000000000000000", + /*.key_size =*/ 64, + /*.count =*/ (uint8_t*)NULL, + /*.iv =*/ (uint8_t*)"0000000000000001", + /*.data_size =*/ 120, + /*.plain =*/ (uint8_t*)NULL, + /*.encrypted =*/ (uint8_t*)"de9cba7bf3d69ef5e786dc63973f653a0b49e015adbff7134fcb7df137821031e85a050278a7084527214f73efc7fa5b5277062eb7a0433e445f41e3", + }, { + /*.rounds =*/ 20, + /*.key =*/ (uint8_t*)"0000000000000000000000000000000000000000000000000000000000000000", + /*.key_size =*/ 64, + /*.count =*/ (uint8_t*)NULL, + /*.iv =*/ (uint8_t*)"0100000000000000", + /*.data_size =*/ 120, + /*.plain =*/ (uint8_t*)NULL, + /*.encrypted =*/ (uint8_t*)"ef3fdfd6c61578fbf5cf35bd3dd33b8009631634d21e42ac33960bd138e50d32111e4caf237ee53ca8ad6426194a88545ddc497a0b466e7d6bbdb004", + }, { + /*.rounds =*/ 20, + /*.key =*/ (uint8_t*)"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + /*.key_size =*/ 64, + /*.count =*/ (uint8_t*)NULL, + /*.iv =*/ (uint8_t*)"0001020304050607", + /*.data_size =*/ 500, + /*.plain =*/ (uint8_t*)NULL, + /*.encrypted =*/ (uint8_t*)"f798a189f195e66982105ffb640bb7757f579da31602fc93ec01ac56f85ac3c134a4547b733b46413042c9440049176905d3be59ea1c53f15916155c2be8241a38008b9a26bc35941e2444177c8ade6689de95264986d95889fb60e84629c9bd9a5acb1cc118be563eb9b3a4a472f82e09a7e778492b562ef7130e88dfe031c79db9d4f7c7a899151b9a475032b63fc385245fe054e3dd5a97a5f576fe064025d3ce042c566ab2c507b138db853e3d6959660996546cc9c4a6eafdc777c040d70eaf46f76dad3979e5c5360c3317166a1c894c94a371876a94df7628fe4eaaf2ccb27d5aaae0ad7ad0f9d4b6ad3b54098746d4524d38407a6deb", + }, { /* A.1: Test Vector #4 */ + /*.rounds =*/ 20, + /*.key =*/ (uint8_t*)"00ff000000000000000000000000000000000000000000000000000000000000", + /*.key_size =*/ 64, + /*.count =*/ (uint8_t*)"0000000000000002", + /*.iv =*/ (uint8_t*)"0000000000000000", + /*.data_size =*/ 128, + /*.plain =*/ (uint8_t*)NULL, + /*.encrypted =*/ (uint8_t*)"72d54dfbf12ec44b362692df94137f328fea8da73990265ec1bbbea1ae9af0ca13b25aa26cb4a648cb9b9d1be65b2c0924a66c54d545ec1b7374f4872e99f096", + }, { /* A.1: Test Vector #5 */ + /*.rounds =*/ 20, + /*.key =*/ (uint8_t*)"0000000000000000000000000000000000000000000000000000000000000000", + /*.key_size =*/ 64, + /*.count =*/ (uint8_t*)"0000000000000000", + /*.iv =*/ (uint8_t*)"0000000000000002", + /*.data_size =*/ 128, + /*.plain =*/ (uint8_t*)NULL, + /*.encrypted =*/ (uint8_t*)"c2c64d378cd536374ae204b9ef933fcd1a8b2288b3dfa49672ab765b54ee27c78a970e0e955c14f3a88e741b97c286f75f8fc299e8148362fa198a39531bed6d", + }, { /* A.2: Test Vector #2 */ + /*.rounds =*/ 20, + /*.key =*/ (uint8_t*)"0000000000000000000000000000000000000000000000000000000000000001", + /*.key_size =*/ 64, + /*.count =*/ (uint8_t*)"0000000000000001", + /*.iv =*/ (uint8_t*)"0000000000000002", + /*.data_size =*/ 750, + /*.plain =*/ (uint8_t*)"416e79207375626d697373696f6e20746f20746865204945544620696e74656e6465642062792074686520436f6e7472696275746f7220666f72207075626c69636174696f6e20617320616c6c206f722070617274206f6620616e204945544620496e7465726e65742d4472616674206f722052464320616e6420616e792073746174656d656e74206d6164652077697468696e2074686520636f6e74657874206f6620616e204945544620616374697669747920697320636f6e7369646572656420616e20224945544620436f6e747269627574696f6e222e20537563682073746174656d656e747320696e636c756465206f72616c2073746174656d656e747320696e20494554462073657373696f6e732c2061732077656c6c206173207772697474656e20616e6420656c656374726f6e696320636f6d6d756e69636174696f6e73206d61646520617420616e792074696d65206f7220706c6163652c207768696368206172652061646472657373656420746f", + /*.encrypted =*/ (uint8_t*)"a3fbf07df3fa2fde4f376ca23e82737041605d9f4f4f57bd8cff2c1d4b7955ec2a97948bd3722915c8f3d337f7d370050e9e96d647b7c39f56e031ca5eb6250d4042e02785ececfa4b4bb5e8ead0440e20b6e8db09d881a7c6132f420e52795042bdfa7773d8a9051447b3291ce1411c680465552aa6c405b7764d5e87bea85ad00f8449ed8f72d0d662ab052691ca66424bc86d2df80ea41f43abf937d3259dc4b2d0dfb48a6c9139ddd7f76966e928e635553ba76c5c879d7b35d49eb2e62b0871cdac638939e25e8a1e0ef9d5280fa8ca328b351c3c765989cbcf3daa8b6ccc3aaf9f3979c92b3720fc88dc95ed84a1be059c6499b9fda236e7e818b04b0bc39c1e876b193bfe5569753f88128cc08aaa9b63d1a16f80ef2554d7189c411f5869ca52c5b83fa36ff216b9c1d30062bebcfd2dc5bce0911934fda79a86f6e698ced759c3ff9b6477338f3da4f9cd8514ea9982ccafb341b2384dd902f3d1ab7ac61dd29c6f21ba5b862f3730e37cfdc4fd806c22f221", + }, { /* A.2: Test Vector #3 */ + /*.rounds =*/ 20, + /*.key =*/ (uint8_t*)"1c9240a5eb55d38af333888604f6b5f0473917c1402b80099dca5cbc207075c0", + /*.key_size =*/ 64, + /*.count =*/ (uint8_t*)"000000000000002a", + /*.iv =*/ (uint8_t*)"0000000000000002", + /*.data_size =*/ 254, + /*.plain =*/ (uint8_t*)"2754776173206272696c6c69672c20616e642074686520736c6974687920746f7665730a446964206779726520616e642067696d626c6520696e2074686520776162653a0a416c6c206d696d737920776572652074686520626f726f676f7665732c0a416e6420746865206d6f6d65207261746873206f757467726162652e", + /*.encrypted =*/ (uint8_t*)"62e6347f95ed87a45ffae7426f27a1df5fb69110044c0d73118effa95b01e5cf166d3df2d721caf9b21e5fb14c616871fd84c54f9d65b283196c7fe4f60553ebf39c6402c42234e32a356b3e764312a61a5532055716ead6962568f87d3f3f7704c6a8d1bcd1bf4d50d6154b6da731b187b58dfd728afa36757a797ac188d1", + }, + /* From: http://tools.ietf.org/html/draft-strombergson-chacha-test-vectors-00 */ + { /* TC1: All zero key and IV. key: 128, rounds: 8. */ + /*.rounds =*/ 8, + /*.key =*/ (uint8_t*)"00000000000000000000000000000000", + /*.key_size =*/ 32, + /*.count =*/ (uint8_t*)"0000000000000000", + /*.iv =*/ (uint8_t*)"0000000000000000", + /*.data_size =*/ 128, + /*.plain =*/ (uint8_t*)NULL, + /*.encrypted =*/ (uint8_t*)"e28a5fa4a67f8c5defed3e6fb7303486aa8427d31419a729572d777953491120b64ab8e72b8deb85cd6aea7cb6089a101824beeb08814a428aab1fa2c816081b", + }, { /* TC1: All zero key and IV. key: 128, rounds: 12. */ + /*.rounds =*/ 12, + /*.key =*/ (uint8_t*)"00000000000000000000000000000000", + /*.key_size =*/ 32, + /*.count =*/ (uint8_t*)"0000000000000000", + /*.iv =*/ (uint8_t*)"0000000000000000", + /*.data_size =*/ 128, + /*.plain =*/ (uint8_t*)NULL, + /*.encrypted =*/ (uint8_t*)"e1047ba9476bf8ff312c01b4345a7d8ca5792b0ad467313f1dc412b5fdce32410dea8b68bd774c36a920f092a04d3f95274fbeff97bc8491fcef37f85970b450", + }, { /* TC1: All zero key and IV. key: 128, rounds: 20. */ + /*.rounds =*/ 20, + /*.key =*/ (uint8_t*)"00000000000000000000000000000000", + /*.key_size =*/ 32, + /*.count =*/ (uint8_t*)"0000000000000000", + /*.iv =*/ (uint8_t*)"0000000000000000", + /*.data_size =*/ 128, + /*.plain =*/ (uint8_t*)NULL, + /*.encrypted =*/ (uint8_t*)"89670952608364fd00b2f90936f031c8e756e15dba04b8493d00429259b20f46cc04f111246b6c2ce066be3bfb32d9aa0fddfbc12123d4b9e44f34dca05a103f", + }, { /* TC1: All zero key and IV. key: 256, rounds: 8. */ + /*.rounds =*/ 8, + /*.key =*/ (uint8_t*)"0000000000000000000000000000000000000000000000000000000000000000", + /*.key_size =*/ 64, + /*.count =*/ (uint8_t*)"0000000000000000", + /*.iv =*/ (uint8_t*)"0000000000000000", + /*.data_size =*/ 128, + /*.plain =*/ (uint8_t*)NULL, + /*.encrypted =*/ (uint8_t*)"3e00ef2f895f40d67f5bb8e81f09a5a12c840ec3ce9a7f3b181be188ef711a1e984ce172b9216f419f445367456d5619314a42a3da86b001387bfdb80e0cfe42", + }, { /* TC1: All zero key and IV. key: 256, rounds: 12. */ + /*.rounds =*/ 12, + /*.key =*/ (uint8_t*)"0000000000000000000000000000000000000000000000000000000000000000", + /*.key_size =*/ 64, + /*.count =*/ (uint8_t*)"0000000000000000", + /*.iv =*/ (uint8_t*)"0000000000000000", + /*.data_size =*/ 128, + /*.plain =*/ (uint8_t*)NULL, + /*.encrypted =*/ (uint8_t*)"9bf49a6a0755f953811fce125f2683d50429c3bb49e074147e0089a52eae155f0564f879d27ae3c02ce82834acfa8c793a629f2ca0de6919610be82f411326be", + }, { /* TC1: All zero key and IV. key: 256, rounds: 20. */ + /*.rounds =*/ 20, + /*.key =*/ (uint8_t*)"0000000000000000000000000000000000000000000000000000000000000000", + /*.key_size =*/ 64, + /*.count =*/ (uint8_t*)"0000000000000000", + /*.iv =*/ (uint8_t*)"0000000000000000", + /*.data_size =*/ 128, + /*.plain =*/ (uint8_t*)NULL, + /*.encrypted =*/ (uint8_t*)"76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da41597c5157488d7724e03fb8d84a376a43b8f41518a11cc387b669b2ee6586", + }, { /* TC8: Random key and IV. key: 128, rounds: 8. */ + /*.rounds =*/ 8, + /*.key =*/ (uint8_t*)"c46ec1b18ce8a878725a37e780dfb735", + /*.key_size =*/ 32, + /*.count =*/ (uint8_t*)"0000000000000000", + /*.iv =*/ (uint8_t*)"1ada31d5cf688221", + /*.data_size =*/ 128, + /*.plain =*/ (uint8_t*)NULL, + /*.encrypted =*/ (uint8_t*)"6a870108859f679118f3e205e2a56a6826ef5a60a4102ac8d4770059fcb7c7bae02f5ce004a6bfbbea53014dd82107c0aa1c7ce11b7d78f2d50bd3602bbd2594", + }, { /* TC8: Random key and IV. key: 128, rounds: 12. */ + /*.rounds =*/ 12, + /*.key =*/ (uint8_t*)"c46ec1b18ce8a878725a37e780dfb735", + /*.key_size =*/ 32, + /*.count =*/ (uint8_t*)"0000000000000000", + /*.iv =*/ (uint8_t*)"1ada31d5cf688221", + /*.data_size =*/ 128, + /*.plain =*/ (uint8_t*)NULL, + /*.encrypted =*/ (uint8_t*)"b02bd81eb55c8f68b5e9ca4e307079bc225bd22007eddc6702801820709ce09807046a0d2aa552bfdbb49466176d56e32d519e10f5ad5f2746e241e09bdf9959", + }, { /* TC8: Random key and IV. key: 128, rounds: 20. */ + /*.rounds =*/ 20, + /*.key =*/ (uint8_t*)"c46ec1b18ce8a878725a37e780dfb735", + /*.key_size =*/ 32, + /*.count =*/ (uint8_t*)"0000000000000000", + /*.iv =*/ (uint8_t*)"1ada31d5cf688221", + /*.data_size =*/ 128, + /*.plain =*/ (uint8_t*)NULL, + /*.encrypted =*/ (uint8_t*)"826abdd84460e2e9349f0ef4af5b179b426e4b2d109a9c5bb44000ae51bea90a496beeef62a76850ff3f0402c4ddc99f6db07f151c1c0dfac2e56565d6289625", + }, { /* TC8: Random key and IV. key: 256, rounds: 8. */ + /*.rounds =*/ 8, + /*.key =*/ (uint8_t*)"c46ec1b18ce8a878725a37e780dfb7351f68ed2e194c79fbc6aebee1a667975d", + /*.key_size =*/ 64, + /*.count =*/ (uint8_t*)"0000000000000000", + /*.iv =*/ (uint8_t*)"1ada31d5cf688221", + /*.data_size =*/ 128, + /*.plain =*/ (uint8_t*)NULL, + /*.encrypted =*/ (uint8_t*)"838751b42d8ddd8a3d77f48825a2ba752cf4047cb308a5978ef274973be374c96ad848065871417b08f034e681fe46a93f7d5c61d1306614d4aaf257a7cff08b", + }, { /* TC8: Random key and IV. key: 256, rounds: 12. */ + /*.rounds =*/ 12, + /*.key =*/ (uint8_t*)"c46ec1b18ce8a878725a37e780dfb7351f68ed2e194c79fbc6aebee1a667975d", + /*.key_size =*/ 64, + /*.count =*/ (uint8_t*)"0000000000000000", + /*.iv =*/ (uint8_t*)"1ada31d5cf688221", + /*.data_size =*/ 128, + /*.plain =*/ (uint8_t*)NULL, + /*.encrypted =*/ (uint8_t*)"1482072784bc6d06b4e73bdc118bc0103c7976786ca918e06986aa251f7e9cc1b2749a0a16ee83b4242d2e99b08d7c20092b80bc466c87283b61b1b39d0ffbab", + }, { /* TC8: Random key and IV. key: 256, rounds: 20. */ + /*.rounds =*/ 20, + /*.key =*/ (uint8_t*)"c46ec1b18ce8a878725a37e780dfb7351f68ed2e194c79fbc6aebee1a667975d", + /*.key_size =*/ 64, + /*.count =*/ (uint8_t*)"0000000000000000", + /*.iv =*/ (uint8_t*)"1ada31d5cf688221", + /*.data_size =*/ 128, + /*.plain =*/ (uint8_t*)NULL, + /*.encrypted =*/ (uint8_t*)"f63a89b75c2271f9368816542ba52f06ed49241792302b00b5e8f80ae9a473afc25b218f519af0fdd406362e8d69de7f54c604a6e00f353f110f771bdca8ab92", + }, { /* NULL */ + /*.rounds =*/ 0, + /*.key =*/ NULL, + /*.key_size =*/ 0, + /*.count =*/ NULL, + /*.iv =*/ NULL, + /*.data_size =*/ 0, + /*.plain =*/ NULL, + /*.encrypted =*/ NULL, + } +}; + + +#define CHACHA_TEST_LEN 2048 +/* + * hchacha/8 test + * key [192,193,194,..223] + * iv [16,17,18,..31] + */ +static const unsigned char expected_hchacha[32] = { + 0xe6, 0x19, 0x0f, 0x48, 0xf1, 0xc0, 0x2a, 0x68, + 0xb8, 0xf2, 0x2e, 0xf8, 0xbc, 0xfd, 0x41, 0x06, + 0x7b, 0xa9, 0x36, 0xf3, 0x63, 0x2f, 0x5c, 0x6d, + 0x40, 0x39, 0x24, 0xb3, 0x74, 0x68, 0xcb, 0xdd, +}; + +/* + * oneshot chacha+xchacha/8 test + * key [192,193,194,..223] + * iv [16,17,18,..31] + */ +/* xor of all the blocks from the one-shot test sequence */ +static const unsigned char expected_chacha_oneshot[CHACHA_BLOCK_LEN] = { + 0x21, 0x5b, 0x81, 0x79, 0x74, 0xef, 0x98, 0x89, + 0xc6, 0x40, 0x47, 0x53, 0x42, 0x01, 0x24, 0x88, + 0x21, 0xa3, 0xb6, 0xc8, 0x43, 0x62, 0x0b, 0x00, + 0x19, 0xd0, 0xd5, 0xee, 0x6c, 0x21, 0xf8, 0x51, + 0xa8, 0xb3, 0x45, 0x56, 0x72, 0xc1, 0x85, 0x0e, + 0xe1, 0x43, 0xbe, 0xd6, 0xa6, 0x8b, 0x3d, 0xdc, + 0x3d, 0xf7, 0x64, 0xfd, 0x80, 0x0c, 0xd9, 0x58, + 0xf8, 0x06, 0x40, 0xf4, 0xc2, 0x14, 0xba, 0x84, +}; +/* xor of all the blocks from the one-shot test sequence */ +static const unsigned char expected_xchacha_oneshot[CHACHA_BLOCK_LEN] = { + 0x01, 0xd1, 0x84, 0x26, 0x1b, 0x7d, 0x44, 0x4d, + 0x3a, 0x8f, 0xef, 0x3f, 0x1e, 0x11, 0xb5, 0xa0, + 0x07, 0x04, 0x46, 0x4c, 0xfb, 0x6b, 0xd0, 0x30, + 0x42, 0x3d, 0xfa, 0x56, 0x71, 0x33, 0x96, 0xdb, + 0xef, 0x0f, 0x09, 0xc1, 0xde, 0x41, 0xc5, 0xa8, + 0xba, 0x37, 0x59, 0x3f, 0x43, 0xc3, 0xf8, 0xc4, + 0xce, 0xd5, 0xf0, 0x51, 0x5f, 0x2c, 0x5e, 0xcf, + 0xe2, 0x5e, 0x68, 0x95, 0x7a, 0x5c, 0x02, 0xea, +}; + +/* Import from little-endian hex string (L->H). */ +static inline int +chacha_import_le_hex(uint8_t *a, size_t count, uint8_t *buf, size_t buf_size) { + register uint8_t *r_pos, *r_pos_max, *w_pos, *w_pos_max, cur_char, byte; + register size_t cnt; + + if (0 == count || 0 == buf_size) + return (EINVAL); + if ((count * sizeof(uint8_t)) < (buf_size / 2)) + return (EOVERFLOW); + r_pos = buf; + r_pos_max = (r_pos + buf_size); + w_pos = (uint8_t*)a; + w_pos_max = (w_pos + (count * sizeof(uint8_t))); + + for (cnt = 0; r_pos < r_pos_max; r_pos ++) { + cur_char = (*r_pos); + if ('0' <= cur_char && '9' >= cur_char) + cur_char -= '0'; + else if ('a' <= cur_char && 'f' >= cur_char) + cur_char -= ('a' - 10); + else if ('A' <= cur_char && 'F' >= cur_char) + cur_char -= ('A' - 10); + else + continue; + byte = ((byte << 4) | cur_char); + cnt ++; + if (2 > cnt) /* Wait untill 4 + 4 bit before write a byte. */ + continue; + if (w_pos == w_pos_max) + return (EOVERFLOW); + (*w_pos ++) = byte; + byte = 0; + cnt = 0; + } + memset(w_pos, 0, (w_pos_max - w_pos)); + return (0); +} +/* Import from big-endian hex string (H->L). */ +static inline int +chacha_import_be_hex(uint8_t *a, size_t count, uint8_t *buf, size_t buf_size) { + register uint8_t *r_pos, *w_pos, *w_pos_max, cur_char, byte; + register size_t cnt; + + if (0 == count || 0 == buf_size) + return (EINVAL); + if ((count * sizeof(uint8_t)) < (buf_size / 2)) + return (EOVERFLOW); + r_pos = (buf + (buf_size - 1)); + w_pos = (uint8_t*)a; + w_pos_max = (w_pos + (count * sizeof(uint8_t))); + + for (cnt = 0; r_pos >= buf; r_pos --) { + cur_char = (*r_pos); + if ('0' <= cur_char && '9' >= cur_char) + cur_char -= '0'; + else if ('a' <= cur_char && 'f' >= cur_char) + cur_char -= ('a' - 10); + else if ('A' <= cur_char && 'F' >= cur_char) + cur_char -= ('A' - 10); + else + continue; + byte = ((byte >> 4) | (cur_char << 4)); + cnt ++; + if (2 > cnt) /* Wait untill 4 + 4 bit before write a byte. */ + continue; + if (w_pos == w_pos_max) + return (EOVERFLOW); + (*w_pos ++) = byte; + byte = 0; + cnt = 0; + } + memset(w_pos, 0, (w_pos_max - w_pos)); + return (0); +} + +/* XOR all blocks into one block. */ +static inline void +chacha_res_compact(const uint8_t *encrypted, const uint8_t *plain, const size_t len, + uint8_t *dst) { + size_t i, h; + + for (i = 0; i < CHACHA_BLOCK_LEN; i ++) + dst[i] ^= plain[i]; + for (h = 1; h < (len / CHACHA_BLOCK_LEN); h ++) { + for (i = 0; i < CHACHA_BLOCK_LEN; i ++) + dst[i] ^= encrypted[(h * CHACHA_BLOCK_LEN) + i] ^ plain[(h * CHACHA_BLOCK_LEN) + i]; + } +} + +/* 0 - OK, non zero - error */ +static inline int +chacha_self_test() { + int error = 0; + size_t i, h; + chacha_tst1v_t tst1v; + uint8_t key[CHACHA_KEY_256_LEN]; + uint8_t count[8]; + uint8_t iv[XCHACHA_IV_LEN]; + uint8_t plain[CHACHA_TEST_LEN]; + uint8_t encrypted[CHACHA_TEST_LEN]; + uint8_t result[CHACHA_TEST_LEN]; + chacha_context_str_t ctx; + + for (i = 0; 0 != chacha_tst1v[i].rounds; i ++) { + memset(&tst1v, 0, sizeof(tst1v)); + + tst1v.rounds = chacha_tst1v[i].rounds; + + chacha_import_le_hex(key, sizeof(key), chacha_tst1v[i].key, chacha_tst1v[i].key_size); + tst1v.key = key; + tst1v.key_size = (chacha_tst1v[i].key_size / 2); + + if (NULL != chacha_tst1v[i].count) { + //chacha_import_le_hex(count, sizeof(count), chacha_tst1v[i].count, 16); + chacha_import_be_hex(count, sizeof(count), chacha_tst1v[i].count, 16); + tst1v.count = count; + } + + if (NULL != chacha_tst1v[i].iv) { + chacha_import_le_hex(iv, sizeof(iv), chacha_tst1v[i].iv, 16); + tst1v.iv = iv; + } + + tst1v.data_size = (chacha_tst1v[i].data_size / 2); + if (NULL != chacha_tst1v[i].plain) { + chacha_import_le_hex(plain, sizeof(plain), chacha_tst1v[i].plain, chacha_tst1v[i].data_size); + tst1v.plain = plain; + } + if (NULL != chacha_tst1v[i].encrypted) { + chacha_import_le_hex(encrypted, sizeof(encrypted), chacha_tst1v[i].encrypted, chacha_tst1v[i].data_size); + tst1v.encrypted = encrypted; + } + + chacha(tst1v.key, tst1v.key_size, tst1v.count, tst1v.iv, + tst1v.rounds, tst1v.plain, tst1v.data_size, result); + if (0 != memcmp(tst1v.encrypted, result, tst1v.data_size)) + error ++; + } + + /* From: https://github.com/floodyberry/chacha-opt/blob/master/app/extensions/chacha/impl.c */ + /* key [192, 193, 194, ...223], iv [16, 17, 18, ...31], rounds = 8 */ + for (i = 0; i < sizeof(key); i ++) + key[i] = (uint8_t)(i + 192); + for (i = 0; i < sizeof(iv); i ++) + iv[i] = (uint8_t)(i + 16); + /* Init src. */ + for (i = 0, h = 0; i < CHACHA_TEST_LEN; i ++) { + h += (h + i + 0x55); + h ^= (h >> 3); + plain[i] = (uint8_t)h; + } + /* hchacha. */ + hchacha(key, 256, iv, 8, result); + if (0 != memcmp(expected_hchacha, result, sizeof(expected_hchacha))) + error ++; + /* xchacha one-shot. */ + xchacha(key, 256, NULL, iv, 8, plain, CHACHA_TEST_LEN, result); + chacha_res_compact(result, plain, CHACHA_TEST_LEN, result); + if (0 != memcmp(expected_xchacha_oneshot, result, CHACHA_BLOCK_LEN)) + error ++; + /* xchacha by blocks. */ + xchacha_str_init(&ctx, key, 256, NULL, iv, 8); + for (i = 0; i < CHACHA_TEST_LEN; i ++) { + chacha_str_data_crypt(&ctx, &plain[i], 1, &result[i]); + } + chacha_str_final(&ctx); + chacha_res_compact(result, plain, CHACHA_TEST_LEN, result); + if (0 != memcmp(expected_xchacha_oneshot, result, CHACHA_BLOCK_LEN)) + error ++; + /* xchacha by blocks 2. */ + xchacha_str_init(&ctx, key, 256, NULL, iv, 8); + chacha_str_data_crypt(&ctx, plain, 1, result); + chacha_str_data_crypt(&ctx, &plain[1], (CHACHA_TEST_LEN - 1), &result[1]); + chacha_str_final(&ctx); + chacha_res_compact(result, plain, CHACHA_TEST_LEN, result); + if (0 != memcmp(expected_xchacha_oneshot, result, CHACHA_BLOCK_LEN)) + error ++; + /* xchacha by blocks 3. */ + xchacha_str_init(&ctx, key, 256, NULL, iv, 8); + chacha_str_data_crypt(&ctx, plain, 63, result); + chacha_str_data_crypt(&ctx, &plain[63], 130, &result[63]); + chacha_str_data_crypt(&ctx, &plain[193], (CHACHA_TEST_LEN - 193), &result[193]); + chacha_str_final(&ctx); + chacha_res_compact(result, plain, CHACHA_TEST_LEN, result); + if (0 != memcmp(expected_xchacha_oneshot, result, CHACHA_BLOCK_LEN)) + error ++; + /* chacha one-shot. */ + chacha(key, 256, NULL, iv, 8, plain, CHACHA_TEST_LEN, result); + chacha_res_compact(result, plain, CHACHA_TEST_LEN, result); + if (0 != memcmp(expected_chacha_oneshot, result, CHACHA_BLOCK_LEN)) + error ++; + + return (error); +} +#endif + +#endif /* CHACHA_H */ Property changes on: /usr/src/sys/crypto/chacha/chacha.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: /usr/src/sys/geom/eli/g_eli.c =================================================================== --- /usr/src/sys/geom/eli/g_eli.c (revision 276867) +++ /usr/src/sys/geom/eli/g_eli.c (working copy) @@ -363,8 +363,11 @@ bzero(&crie, sizeof(crie)); crie.cri_alg = sc->sc_ealgo; crie.cri_klen = sc->sc_ekeylen; - if (sc->sc_ealgo == CRYPTO_AES_XTS) - crie.cri_klen <<= 1; + switch (sc->sc_ealgo) { + case CRYPTO_AES_XTS: + crie.cri_klen <<= 1; /* Double key size. */ + break; + } if ((sc->sc_flags & G_ELI_FLAG_FIRST_KEY) != 0) { crie.cri_key = g_eli_key_hold(sc, 0, LIST_FIRST(&sc->sc_geom->consumer)->provider->sectorsize); @@ -581,6 +584,9 @@ switch (sc->sc_ealgo) { case CRYPTO_AES_XTS: + case CRYPTO_CHACHA_8: + case CRYPTO_CHACHA_12: + case CRYPTO_CHACHA_20: bcopy(off, iv, sizeof(off)); bzero(iv + sizeof(off), size - sizeof(off)); break; @@ -594,6 +600,8 @@ SHA256_Update(&ctx, off, sizeof(off)); SHA256_Final(hash, &ctx); bcopy(hash, iv, MIN(sizeof(hash), size)); + bzero(iv + MIN(sizeof(hash), size), + size - MIN(sizeof(hash), size)); break; } } Index: /usr/src/sys/geom/eli/g_eli.h =================================================================== --- /usr/src/sys/geom/eli/g_eli.h (revision 276867) +++ /usr/src/sys/geom/eli/g_eli.h (working copy) @@ -396,6 +396,22 @@ return (CRYPTO_CAMELLIA_CBC); else if (strcasecmp("camellia-cbc", name) == 0) return (CRYPTO_CAMELLIA_CBC); + else if (strcasecmp("chacha", name) == 0) + return (CRYPTO_CHACHA_20); + else if (strcasecmp("chacha8", name) == 0) + return (CRYPTO_CHACHA_8); + else if (strcasecmp("chacha12", name) == 0) + return (CRYPTO_CHACHA_12); + else if (strcasecmp("chacha20", name) == 0) + return (CRYPTO_CHACHA_20); + else if (strcasecmp("xchacha", name) == 0) + return (CRYPTO_XCHACHA_20); + else if (strcasecmp("xchacha8", name) == 0) + return (CRYPTO_XCHACHA_8); + else if (strcasecmp("xchacha12", name) == 0) + return (CRYPTO_XCHACHA_12); + else if (strcasecmp("xchacha20", name) == 0) + return (CRYPTO_XCHACHA_20); else if (strcasecmp("3des", name) == 0) return (CRYPTO_3DES_CBC); else if (strcasecmp("3des-cbc", name) == 0) @@ -437,6 +453,18 @@ return ("Blowfish-CBC"); case CRYPTO_CAMELLIA_CBC: return ("CAMELLIA-CBC"); + case CRYPTO_CHACHA_8: + return ("ChaCha8"); + case CRYPTO_CHACHA_12: + return ("ChaCha12"); + case CRYPTO_CHACHA_20: + return ("ChaCha20"); + case CRYPTO_XCHACHA_8: + return ("XChaCha8"); + case CRYPTO_XCHACHA_12: + return ("XChaCha12"); + case CRYPTO_XCHACHA_20: + return ("XChaCha20"); case CRYPTO_3DES_CBC: return ("3DES-CBC"); case CRYPTO_MD5_HMAC: @@ -528,6 +556,21 @@ default: return (0); } + case CRYPTO_CHACHA_8: + case CRYPTO_CHACHA_12: + case CRYPTO_CHACHA_20: + case CRYPTO_XCHACHA_8: + case CRYPTO_XCHACHA_12: + case CRYPTO_XCHACHA_20: + switch (keylen) { + case 0: + return (256); + case 128: + case 256: + return (keylen); + default: + return (0); + } case CRYPTO_BLF_CBC: if (keylen == 0) return (128); Index: /usr/src/sys/geom/eli/g_eli_crypto.c =================================================================== --- /usr/src/sys/geom/eli/g_eli_crypto.c (revision 276867) +++ /usr/src/sys/geom/eli/g_eli_crypto.c (working copy) @@ -43,6 +43,8 @@ #endif #include +#include + #ifdef _KERNEL MALLOC_DECLARE(M_ELI); @@ -166,6 +168,24 @@ } break; #endif + case CRYPTO_CHACHA_8: + chacha(key, keysize, NULL, NULL, 8, data, datasize, data); + return (0); + case CRYPTO_CHACHA_12: + chacha(key, keysize, NULL, NULL, 12, data, datasize, data); + return (0); + case CRYPTO_CHACHA_20: + chacha(key, keysize, NULL, NULL, 20, data, datasize, data); + return (0); + case CRYPTO_XCHACHA_8: + xchacha(key, keysize, NULL, NULL, 8, data, datasize, data); + return (0); + case CRYPTO_XCHACHA_12: + xchacha(key, keysize, NULL, NULL, 12, data, datasize, data); + return (0); + case CRYPTO_XCHACHA_20: + xchacha(key, keysize, NULL, NULL, 20, data, datasize, data); + return (0); case CRYPTO_3DES_CBC: type = EVP_des_ede3_cbc(); break; @@ -203,9 +223,24 @@ const u_char *key, size_t keysize) { - /* We prefer AES-CBC for metadata protection. */ - if (algo == CRYPTO_AES_XTS) + switch (algo) { + case CRYPTO_AES_XTS: + /* We prefer AES-CBC for metadata protection. */ algo = CRYPTO_AES_CBC; + break; + case CRYPTO_CHACHA_8: + case CRYPTO_CHACHA_12: + case CRYPTO_CHACHA_20: + /* We prefer more secure ChaCha20 for metadata protection. */ + algo = CRYPTO_CHACHA_20; + break; + case CRYPTO_XCHACHA_8: + case CRYPTO_XCHACHA_12: + case CRYPTO_XCHACHA_20: + /* We prefer more secure XChaCha20 for metadata protection. */ + algo = CRYPTO_XCHACHA_20; + break; + } return (g_eli_crypto_cipher(algo, 1, data, datasize, key, keysize)); } @@ -215,9 +250,24 @@ const u_char *key, size_t keysize) { - /* We prefer AES-CBC for metadata protection. */ - if (algo == CRYPTO_AES_XTS) + switch (algo) { + case CRYPTO_AES_XTS: + /* We prefer AES-CBC for metadata protection. */ algo = CRYPTO_AES_CBC; + break; + case CRYPTO_CHACHA_8: + case CRYPTO_CHACHA_12: + case CRYPTO_CHACHA_20: + /* We prefer more secure ChaCha20 for metadata protection. */ + algo = CRYPTO_CHACHA_20; + break; + case CRYPTO_XCHACHA_8: + case CRYPTO_XCHACHA_12: + case CRYPTO_XCHACHA_20: + /* We prefer more secure XChaCha20 for metadata protection. */ + algo = CRYPTO_XCHACHA_20; + break; + } return (g_eli_crypto_cipher(algo, 0, data, datasize, key, keysize)); } Index: /usr/src/sys/geom/eli/g_eli_integrity.c =================================================================== --- /usr/src/sys/geom/eli/g_eli_integrity.c (revision 276867) +++ /usr/src/sys/geom/eli/g_eli_integrity.c (working copy) @@ -504,8 +504,11 @@ crde->crd_alg = sc->sc_ealgo; crde->crd_key = g_eli_key_hold(sc, dstoff, encr_secsize); crde->crd_klen = sc->sc_ekeylen; - if (sc->sc_ealgo == CRYPTO_AES_XTS) - crde->crd_klen <<= 1; + switch (sc->sc_ealgo) { + case CRYPTO_AES_XTS: + crde->crd_klen <<= 1; /* Double key size. */ + break; + } g_eli_crypto_ivgen(sc, dstoff, crde->crd_iv, sizeof(crde->crd_iv)); Index: /usr/src/sys/geom/eli/g_eli_key.c =================================================================== --- /usr/src/sys/geom/eli/g_eli_key.c (revision 276867) +++ /usr/src/sys/geom/eli/g_eli_key.c (working copy) @@ -226,6 +226,9 @@ */ switch (sc->sc_ealgo) { case CRYPTO_AES_XTS: + case CRYPTO_CHACHA_8: + case CRYPTO_CHACHA_12: + case CRYPTO_CHACHA_20: break; default: SHA256_Init(&sc->sc_ivctx); Index: /usr/src/sys/geom/eli/g_eli_privacy.c =================================================================== --- /usr/src/sys/geom/eli/g_eli_privacy.c (revision 276867) +++ /usr/src/sys/geom/eli/g_eli_privacy.c (working copy) @@ -301,8 +301,11 @@ crd->crd_alg = sc->sc_ealgo; crd->crd_key = g_eli_key_hold(sc, dstoff, secsize); crd->crd_klen = sc->sc_ekeylen; - if (sc->sc_ealgo == CRYPTO_AES_XTS) - crd->crd_klen <<= 1; + switch (sc->sc_ealgo) { + case CRYPTO_AES_XTS: + crd->crd_klen <<= 1; /* Double key size. */ + break; + } g_eli_crypto_ivgen(sc, dstoff, crd->crd_iv, sizeof(crd->crd_iv)); crd->crd_next = NULL; Index: /usr/src/sys/opencrypto/cryptodev.c =================================================================== --- /usr/src/sys/opencrypto/cryptodev.c (revision 276867) +++ /usr/src/sys/opencrypto/cryptodev.c (working copy) @@ -432,15 +432,33 @@ case CRYPTO_ARC4: txform = &enc_xform_arc4; break; - case CRYPTO_CAMELLIA_CBC: - txform = &enc_xform_camellia; - break; + case CRYPTO_CAMELLIA_CBC: + txform = &enc_xform_camellia; + break; + case CRYPTO_CHACHA_8: + txform = &enc_xform_chacha_8; + break; + case CRYPTO_CHACHA_12: + txform = &enc_xform_chacha_12; + break; + case CRYPTO_CHACHA_20: + txform = &enc_xform_chacha_20; + break; + case CRYPTO_XCHACHA_8: + txform = &enc_xform_xchacha_8; + break; + case CRYPTO_XCHACHA_12: + txform = &enc_xform_xchacha_12; + break; + case CRYPTO_XCHACHA_20: + txform = &enc_xform_xchacha_20; + break; case CRYPTO_AES_ICM: txform = &enc_xform_aes_icm; - break; + break; case CRYPTO_AES_NIST_GCM_16: txform = &enc_xform_aes_nist_gcm; - break; + break; default: CRYPTDEB("invalid cipher"); Index: /usr/src/sys/opencrypto/cryptodev.h =================================================================== --- /usr/src/sys/opencrypto/cryptodev.h (revision 276867) +++ /usr/src/sys/opencrypto/cryptodev.h (working copy) @@ -104,7 +104,8 @@ #define RIJNDAEL128_BLOCK_LEN 16 #define AES_BLOCK_LEN RIJNDAEL128_BLOCK_LEN #define CAMELLIA_BLOCK_LEN 16 -#define EALG_MAX_BLOCK_LEN AES_BLOCK_LEN /* Keep this updated */ +#define CHACHA_BLOCK_LEN 64 +#define EALG_MAX_BLOCK_LEN CHACHA_BLOCK_LEN /* Keep this updated */ /* Maximum hash algorithm result length */ #define AALG_MAX_RESULT_LEN 64 /* Keep this updated */ @@ -139,7 +140,13 @@ #define CRYPTO_AES_128_NIST_GMAC 26 /* auth side */ #define CRYPTO_AES_192_NIST_GMAC 27 /* auth side */ #define CRYPTO_AES_256_NIST_GMAC 28 /* auth side */ -#define CRYPTO_ALGORITHM_MAX 28 /* Keep updated - see below */ +#define CRYPTO_CHACHA_8 29 +#define CRYPTO_CHACHA_12 30 +#define CRYPTO_CHACHA_20 31 +#define CRYPTO_XCHACHA_8 32 +#define CRYPTO_XCHACHA_12 33 +#define CRYPTO_XCHACHA_20 34 +#define CRYPTO_ALGORITHM_MAX 34 /* Keep updated - see below */ #define CRYPTO_ALGO_VALID(x) ((x) >= CRYPTO_ALGORITHM_MIN && \ (x) <= CRYPTO_ALGORITHM_MAX) Index: /usr/src/sys/opencrypto/cryptosoft.c =================================================================== --- /usr/src/sys/opencrypto/cryptosoft.c (revision 276867) +++ /usr/src/sys/opencrypto/cryptosoft.c (working copy) @@ -120,7 +120,7 @@ if (!(crd->crd_flags & CRD_F_IV_PRESENT)) crypto_copyback(flags, buf, crd->crd_inject, ivlen, iv); - } else { /* Decryption */ + } else { /* Decryption */ /* IV explicitly provided ? */ if (crd->crd_flags & CRD_F_IV_EXPLICIT) bcopy(crd->crd_iv, iv, ivlen); @@ -799,6 +799,24 @@ case CRYPTO_CAMELLIA_CBC: txf = &enc_xform_camellia; goto enccommon; + case CRYPTO_CHACHA_8: + txf = &enc_xform_chacha_8; + goto enccommon; + case CRYPTO_CHACHA_12: + txf = &enc_xform_chacha_12; + goto enccommon; + case CRYPTO_CHACHA_20: + txf = &enc_xform_chacha_20; + goto enccommon; + case CRYPTO_XCHACHA_8: + txf = &enc_xform_xchacha_8; + goto enccommon; + case CRYPTO_XCHACHA_12: + txf = &enc_xform_xchacha_12; + goto enccommon; + case CRYPTO_XCHACHA_20: + txf = &enc_xform_xchacha_20; + goto enccommon; case CRYPTO_NULL_CBC: txf = &enc_xform_null; goto enccommon; @@ -1003,6 +1021,12 @@ case CRYPTO_AES_NIST_GCM_16: case CRYPTO_AES_NIST_GMAC: case CRYPTO_CAMELLIA_CBC: + case CRYPTO_CHACHA_8: + case CRYPTO_CHACHA_12: + case CRYPTO_CHACHA_20: + case CRYPTO_XCHACHA_8: + case CRYPTO_XCHACHA_12: + case CRYPTO_XCHACHA_20: case CRYPTO_NULL_CBC: txf = swd->sw_exf; @@ -1129,6 +1153,12 @@ case CRYPTO_AES_XTS: case CRYPTO_AES_ICM: case CRYPTO_CAMELLIA_CBC: + case CRYPTO_CHACHA_8: + case CRYPTO_CHACHA_12: + case CRYPTO_CHACHA_20: + case CRYPTO_XCHACHA_8: + case CRYPTO_XCHACHA_12: + case CRYPTO_XCHACHA_20: if ((crp->crp_etype = swcr_encdec(crd, sw, crp->crp_buf, crp->crp_flags)) != 0) goto done; @@ -1236,7 +1266,13 @@ REGISTER(CRYPTO_AES_128_NIST_GMAC); REGISTER(CRYPTO_AES_192_NIST_GMAC); REGISTER(CRYPTO_AES_256_NIST_GMAC); - REGISTER(CRYPTO_CAMELLIA_CBC); + REGISTER(CRYPTO_CAMELLIA_CBC); + REGISTER(CRYPTO_CHACHA_8); + REGISTER(CRYPTO_CHACHA_12); + REGISTER(CRYPTO_CHACHA_20); + REGISTER(CRYPTO_XCHACHA_8); + REGISTER(CRYPTO_XCHACHA_12); + REGISTER(CRYPTO_XCHACHA_20); REGISTER(CRYPTO_DEFLATE_COMP); #undef REGISTER Index: /usr/src/sys/opencrypto/xform.c =================================================================== --- /usr/src/sys/opencrypto/xform.c (revision 276867) +++ /usr/src/sys/opencrypto/xform.c (working copy) @@ -18,6 +18,8 @@ * * AES XTS implementation in 2008 by Damien Miller * + * ChaCha, XChaCha implementation in 2015 by Rozhuk Ivan + * * Copyright (C) 1995, 1996, 1997, 1998, 1999 by John Ioannidis, * Angelos D. Keromytis and Niels Provos. * @@ -63,6 +65,7 @@ #include #include #include +#include #include #include @@ -85,6 +88,12 @@ static int aes_icm_setkey(u_int8_t **, u_int8_t *, int); static int aes_xts_setkey(u_int8_t **, u_int8_t *, int); static int cml_setkey(u_int8_t **, u_int8_t *, int); +static int chacha8_setkey(u_int8_t **, u_int8_t *, int); +static int chacha12_setkey(u_int8_t **, u_int8_t *, int); +static int chacha20_setkey(u_int8_t **, u_int8_t *, int); +static int xchacha8_setkey(u_int8_t **, u_int8_t *, int); +static int xchacha12_setkey(u_int8_t **, u_int8_t *, int); +static int xchacha20_setkey(u_int8_t **, u_int8_t *, int); static void null_encrypt(caddr_t, u_int8_t *); static void des1_encrypt(caddr_t, u_int8_t *); @@ -107,6 +116,7 @@ static void cml_decrypt(caddr_t, u_int8_t *); static void aes_icm_crypt(caddr_t, u_int8_t *); +static void chacha_crypt(caddr_t, u_int8_t *); static void null_zerokey(u_int8_t **); static void des1_zerokey(u_int8_t **); @@ -118,10 +128,14 @@ static void aes_icm_zerokey(u_int8_t **); static void aes_xts_zerokey(u_int8_t **); static void cml_zerokey(u_int8_t **); +static void chacha_zerokey(u_int8_t **); +static void xchacha_zerokey(u_int8_t **); static void aes_icm_reinit(caddr_t, u_int8_t *); static void aes_xts_reinit(caddr_t, u_int8_t *); static void aes_gcm_reinit(caddr_t, u_int8_t *); +static void chacha_reinit(caddr_t, u_int8_t *); +static void xchacha_reinit(caddr_t, u_int8_t *); static void null_init(void *); static void null_reinit(void *ctx, const u_int8_t *buf, u_int16_t len); @@ -281,6 +295,67 @@ NULL, }; +struct enc_xform enc_xform_chacha_8 = { + CRYPTO_CHACHA_8, "ChaCha8", + CHACHA_BLOCK_LEN, CHACHA_IV_LEN, CHACHA_KEY_128_LEN, CHACHA_KEY_256_LEN, + chacha_crypt, + chacha_crypt, + chacha8_setkey, + chacha_zerokey, + chacha_reinit, +}; + +struct enc_xform enc_xform_chacha_12 = { + CRYPTO_CHACHA_12, "ChaCha12", + CHACHA_BLOCK_LEN, CHACHA_IV_LEN, CHACHA_KEY_128_LEN, CHACHA_KEY_256_LEN, + chacha_crypt, + chacha_crypt, + chacha12_setkey, + chacha_zerokey, + chacha_reinit, +}; + +struct enc_xform enc_xform_chacha_20 = { + CRYPTO_CHACHA_20, "ChaCha20", + CHACHA_BLOCK_LEN, CHACHA_IV_LEN, CHACHA_KEY_128_LEN, CHACHA_KEY_256_LEN, + chacha_crypt, + chacha_crypt, + chacha20_setkey, + chacha_zerokey, + chacha_reinit, +}; + +struct enc_xform enc_xform_xchacha_8 = { + CRYPTO_XCHACHA_8, "XChaCha8", + CHACHA_BLOCK_LEN, XCHACHA_IV_LEN, CHACHA_KEY_128_LEN, CHACHA_KEY_256_LEN, + chacha_crypt, + chacha_crypt, + xchacha8_setkey, + xchacha_zerokey, + xchacha_reinit, +}; + +struct enc_xform enc_xform_xchacha_12 = { + CRYPTO_XCHACHA_12, "XChaCha12", + CHACHA_BLOCK_LEN, XCHACHA_IV_LEN, CHACHA_KEY_128_LEN, CHACHA_KEY_256_LEN, + chacha_crypt, + chacha_crypt, + xchacha12_setkey, + xchacha_zerokey, + xchacha_reinit, +}; + +struct enc_xform enc_xform_xchacha_20 = { + CRYPTO_XCHACHA_20, "XChaCha20", + CHACHA_BLOCK_LEN, XCHACHA_IV_LEN, CHACHA_KEY_128_LEN, CHACHA_KEY_256_LEN, + chacha_crypt, + chacha_crypt, + xchacha20_setkey, + xchacha_zerokey, + xchacha_reinit, +}; + + /* Authentication instances */ struct auth_hash auth_hash_null = { /* NB: context isn't used */ CRYPTO_NULL_HMAC, "NULL-HMAC", @@ -868,6 +943,121 @@ *sched = NULL; } +static int +chacha_setkey(u_int8_t **sched, u_int8_t *key, int len, size_t rounds) +{ + chacha_context_p ctx; + + if (len != CHACHA_KEY_128_LEN && len != CHACHA_KEY_256_LEN) + return (EINVAL); + ctx = malloc(sizeof(chacha_context_t), M_CRYPTO_DATA, + M_NOWAIT | M_ZERO); + if (ctx == NULL) + return (ENOMEM); + *sched = (u_int8_t*)ctx; + chacha_init(ctx, key, len, NULL, NULL, rounds); + return 0; +} + +static int +xchacha_setkey(u_int8_t **sched, u_int8_t *key, int len, size_t rounds) +{ + chacha_context_str_p ctx; + + if (len != CHACHA_KEY_128_LEN && len != CHACHA_KEY_256_LEN) + return (EINVAL); + ctx = malloc(sizeof(chacha_context_str_t), M_CRYPTO_DATA, + M_NOWAIT | M_ZERO); + if (ctx == NULL) + return (ENOMEM); + *sched = (u_int8_t*)ctx; + /* Save key in context for xchacha_init() in xchacha_reinit(). */ + ctx->ks_len = len; + bcopy(key, ctx->ks, len); + xchacha_init(&ctx->c, (uint8_t*)ctx->ks, ctx->ks_len, NULL, + (((uint8_t*)ctx->ks) + CHACHA_KEY_256_LEN), rounds); + return 0; +} + +static int +chacha8_setkey(u_int8_t **sched, u_int8_t *key, int len) +{ + return (chacha_setkey(sched, key, len, 8)); +} + +static int +chacha12_setkey(u_int8_t **sched, u_int8_t *key, int len) +{ + return (chacha_setkey(sched, key, len, 12)); +} + +static int +chacha20_setkey(u_int8_t **sched, u_int8_t *key, int len) +{ + return (chacha_setkey(sched, key, len, 20)); +} + +static int +xchacha8_setkey(u_int8_t **sched, u_int8_t *key, int len) +{ + return (xchacha_setkey(sched, key, len, 8)); +} + +static int +xchacha12_setkey(u_int8_t **sched, u_int8_t *key, int len) +{ + return (xchacha_setkey(sched, key, len, 12)); +} + +static int +xchacha20_setkey(u_int8_t **sched, u_int8_t *key, int len) +{ + return (xchacha_setkey(sched, key, len, 20)); +} + +static void +chacha_reinit(caddr_t sched, u_int8_t *iv) +{ + chacha_iv_set((chacha_context_p)sched, iv); +} + +static void +xchacha_reinit(caddr_t sched, u_int8_t *iv) +{ + chacha_context_str_p ctx = (chacha_context_str_p)sched; + + /* Save iv in context for xchacha_init() in xchacha_setkey(). */ + bcopy(iv, (((uint8_t*)ctx->ks) + CHACHA_KEY_256_LEN), XCHACHA_IV_LEN); + xchacha_set_key_iv_rounds(&ctx->c, (uint8_t*)ctx->ks, ctx->ks_len, + (((uint8_t*)ctx->ks) + CHACHA_KEY_256_LEN), ctx->c.rounds); +} + +static void +chacha_crypt(caddr_t sched, u_int8_t *blk) +{ + if (ALIGNED_POINTER(blk, size_t)) { + chacha_block_aligned((chacha_context_p)sched, blk, blk); + } else { + chacha_block_unaligneg((chacha_context_p)sched, blk, blk); + } +} + +static void +chacha_zerokey(u_int8_t **sched) +{ + bzero(*sched, sizeof(chacha_context_t)); + free(*sched, M_CRYPTO_DATA); + *sched = NULL; +} + +static void +xchacha_zerokey(u_int8_t **sched) +{ + bzero(*sched, sizeof(chacha_context_str_t)); + free(*sched, M_CRYPTO_DATA); + *sched = NULL; +} + /* * And now for auth. */ Index: /usr/src/sys/opencrypto/xform.h =================================================================== --- /usr/src/sys/opencrypto/xform.h (revision 276867) +++ /usr/src/sys/opencrypto/xform.h (working copy) @@ -99,6 +99,12 @@ extern struct enc_xform enc_xform_aes_xts; extern struct enc_xform enc_xform_arc4; extern struct enc_xform enc_xform_camellia; +extern struct enc_xform enc_xform_chacha_8; +extern struct enc_xform enc_xform_chacha_12; +extern struct enc_xform enc_xform_chacha_20; +extern struct enc_xform enc_xform_xchacha_8; +extern struct enc_xform enc_xform_xchacha_12; +extern struct enc_xform enc_xform_xchacha_20; extern struct auth_hash auth_hash_null; extern struct auth_hash auth_hash_key_md5;