diff --git a/.gitignore b/.gitignore index 7b9dbba1..23139844 100644 --- a/.gitignore +++ b/.gitignore @@ -65,6 +65,7 @@ test/default/box7 test/default/box8 test/default/box_easy test/default/box_easy2 +test/default/chacha20 test/default/core1 test/default/core2 test/default/core3 diff --git a/src/libsodium/Makefile.am b/src/libsodium/Makefile.am index fa58cd7a..9d807f02 100644 --- a/src/libsodium/Makefile.am +++ b/src/libsodium/Makefile.am @@ -167,6 +167,9 @@ libsodium_la_SOURCES = \ crypto_stream/aes256estream/stream_aes256estream_api.c \ crypto_stream/aes256estream/hongjun/api.h \ crypto_stream/aes256estream/hongjun/ecrypt-sync.h \ + crypto_stream/chacha20/stream_chacha20_api.c \ + crypto_stream/chacha20/ref/api.h \ + crypto_stream/chacha20/ref/stream_chacha20_ref.c \ crypto_stream/salsa20/stream_salsa20_api.c \ crypto_stream/salsa2012/stream_salsa2012_api.c \ crypto_stream/salsa2012/ref/api.h \ diff --git a/src/libsodium/crypto_stream/chacha20/ref/api.h b/src/libsodium/crypto_stream/chacha20/ref/api.h new file mode 100644 index 00000000..4aed8bfb --- /dev/null +++ b/src/libsodium/crypto_stream/chacha20/ref/api.h @@ -0,0 +1,12 @@ + +#include "crypto_stream_chacha20.h" + +int +crypto_stream_chacha20_ref(unsigned char *c, unsigned long long clen, + const unsigned char *n, const unsigned char *k); + +int +crypto_stream_chacha20_ref_xor(unsigned char *c, const unsigned char *m, + unsigned long long mlen, const unsigned char *n, + const unsigned char *k); + diff --git a/src/libsodium/crypto_stream/chacha20/ref/stream_chacha20_ref.c b/src/libsodium/crypto_stream/chacha20/ref/stream_chacha20_ref.c new file mode 100644 index 00000000..dc04b536 --- /dev/null +++ b/src/libsodium/crypto_stream/chacha20/ref/stream_chacha20_ref.c @@ -0,0 +1,266 @@ + +/* $OpenBSD: chacha.c,v 1.1 2013/11/21 00:45:44 djm Exp $ */ + +/* + chacha-merged.c version 20080118 + D. J. Bernstein + Public domain. + */ + +#include +#include + +#include "api.h" +#include "crypto_stream_chacha20.h" +#include "utils.h" + +struct chacha_ctx { + uint32_t input[16]; +}; + +typedef uint8_t u8; +typedef uint32_t u32; + +typedef struct chacha_ctx chacha_ctx; + +#define U8C(v) (v##U) +#define U32C(v) (v##U) + +#define U8V(v) ((u8)(v) & U8C(0xFF)) +#define U32V(v) ((u32)(v) & U32C(0xFFFFFFFF)) + +#define ROTL32(v, n) \ + (U32V((v) << (n)) | ((v) >> (32 - (n)))) + +#define U8TO32_LITTLE(p) \ + (((u32)((p)[0]) ) | \ + ((u32)((p)[1]) << 8) | \ + ((u32)((p)[2]) << 16) | \ + ((u32)((p)[3]) << 24)) + +#define U32TO8_LITTLE(p, v) \ + do { \ + (p)[0] = U8V((v) ); \ + (p)[1] = U8V((v) >> 8); \ + (p)[2] = U8V((v) >> 16); \ + (p)[3] = U8V((v) >> 24); \ + } while (0) + +#define ROTATE(v,c) (ROTL32(v,c)) +#define XOR(v,w) ((v) ^ (w)) +#define PLUS(v,w) (U32V((v) + (w))) +#define PLUSONE(v) (PLUS((v),1)) + +#define QUARTERROUND(a,b,c,d) \ + a = PLUS(a,b); d = ROTATE(XOR(d,a),16); \ + c = PLUS(c,d); b = ROTATE(XOR(b,c),12); \ + a = PLUS(a,b); d = ROTATE(XOR(d,a), 8); \ + c = PLUS(c,d); b = ROTATE(XOR(b,c), 7); + +static const unsigned char sigma[16] = { + 'e', 'x', 'p', 'a', 'n', 'd', ' ', '3', '2', '-', 'b', 'y', 't', 'e', ' ', 'k' +}; + +static void +chacha_keysetup(chacha_ctx *x, const u8 *k) +{ + const unsigned char *constants; + + x->input[4] = U8TO32_LITTLE(k + 0); + x->input[5] = U8TO32_LITTLE(k + 4); + x->input[6] = U8TO32_LITTLE(k + 8); + x->input[7] = U8TO32_LITTLE(k + 12); + k += 16; + constants = sigma; + x->input[8] = U8TO32_LITTLE(k + 0); + x->input[9] = U8TO32_LITTLE(k + 4); + x->input[10] = U8TO32_LITTLE(k + 8); + x->input[11] = U8TO32_LITTLE(k + 12); + x->input[0] = U8TO32_LITTLE(constants + 0); + x->input[1] = U8TO32_LITTLE(constants + 4); + x->input[2] = U8TO32_LITTLE(constants + 8); + x->input[3] = U8TO32_LITTLE(constants + 12); +} + +static void +chacha_ivsetup(chacha_ctx *x, const u8 *iv, const u8 *counter) +{ + x->input[12] = counter == NULL ? 0 : U8TO32_LITTLE(counter + 0); + x->input[13] = counter == NULL ? 0 : U8TO32_LITTLE(counter + 4); + x->input[14] = U8TO32_LITTLE(iv + 0); + x->input[15] = U8TO32_LITTLE(iv + 4); +} + +static void +chacha_encrypt_bytes(chacha_ctx *x, const u8 *m, u8 *c, unsigned long long bytes) +{ + u32 x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; + u32 j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15; + u8 *ctarget = NULL; + u8 tmp[64]; + unsigned long long i; + + if (!bytes) { + return; + } + j0 = x->input[0]; + j1 = x->input[1]; + j2 = x->input[2]; + j3 = x->input[3]; + j4 = x->input[4]; + j5 = x->input[5]; + j6 = x->input[6]; + j7 = x->input[7]; + j8 = x->input[8]; + j9 = x->input[9]; + j10 = x->input[10]; + j11 = x->input[11]; + j12 = x->input[12]; + j13 = x->input[13]; + j14 = x->input[14]; + j15 = x->input[15]; + + for (;;) { + if (bytes < 64) { + for (i = 0; i < bytes; ++i) { + tmp[i] = m[i]; + } + m = tmp; + ctarget = c; + c = tmp; + } + x0 = j0; + x1 = j1; + x2 = j2; + x3 = j3; + x4 = j4; + x5 = j5; + x6 = j6; + x7 = j7; + x8 = j8; + x9 = j9; + x10 = j10; + x11 = j11; + x12 = j12; + x13 = j13; + x14 = j14; + x15 = j15; + for (i = 20; i > 0; i -= 2) { + QUARTERROUND(x0, x4, x8, x12) + QUARTERROUND(x1, x5, x9, x13) + QUARTERROUND(x2, x6, x10, x14) + QUARTERROUND(x3, x7, x11, x15) + QUARTERROUND(x0, x5, x10, x15) + QUARTERROUND(x1, x6, x11, x12) + QUARTERROUND(x2, x7, x8, x13) + QUARTERROUND(x3, x4, x9, x14) + } + x0 = PLUS(x0, j0); + x1 = PLUS(x1, j1); + x2 = PLUS(x2, j2); + x3 = PLUS(x3, j3); + x4 = PLUS(x4, j4); + x5 = PLUS(x5, j5); + x6 = PLUS(x6, j6); + x7 = PLUS(x7, j7); + x8 = PLUS(x8, j8); + x9 = PLUS(x9, j9); + x10 = PLUS(x10, j10); + x11 = PLUS(x11, j11); + x12 = PLUS(x12, j12); + x13 = PLUS(x13, j13); + x14 = PLUS(x14, j14); + x15 = PLUS(x15, j15); + + x0 = XOR(x0, U8TO32_LITTLE(m + 0)); + x1 = XOR(x1, U8TO32_LITTLE(m + 4)); + x2 = XOR(x2, U8TO32_LITTLE(m + 8)); + x3 = XOR(x3, U8TO32_LITTLE(m + 12)); + x4 = XOR(x4, U8TO32_LITTLE(m + 16)); + x5 = XOR(x5, U8TO32_LITTLE(m + 20)); + x6 = XOR(x6, U8TO32_LITTLE(m + 24)); + x7 = XOR(x7, U8TO32_LITTLE(m + 28)); + x8 = XOR(x8, U8TO32_LITTLE(m + 32)); + x9 = XOR(x9, U8TO32_LITTLE(m + 36)); + x10 = XOR(x10, U8TO32_LITTLE(m + 40)); + x11 = XOR(x11, U8TO32_LITTLE(m + 44)); + x12 = XOR(x12, U8TO32_LITTLE(m + 48)); + x13 = XOR(x13, U8TO32_LITTLE(m + 52)); + x14 = XOR(x14, U8TO32_LITTLE(m + 56)); + x15 = XOR(x15, U8TO32_LITTLE(m + 60)); + + j12 = PLUSONE(j12); + if (!j12) { + j13 = PLUSONE(j13); + /* stopping at 2^70 bytes per nonce is user's responsibility */ + } + + U32TO8_LITTLE(c + 0, x0); + U32TO8_LITTLE(c + 4, x1); + U32TO8_LITTLE(c + 8, x2); + U32TO8_LITTLE(c + 12, x3); + U32TO8_LITTLE(c + 16, x4); + U32TO8_LITTLE(c + 20, x5); + U32TO8_LITTLE(c + 24, x6); + U32TO8_LITTLE(c + 28, x7); + U32TO8_LITTLE(c + 32, x8); + U32TO8_LITTLE(c + 36, x9); + U32TO8_LITTLE(c + 40, x10); + U32TO8_LITTLE(c + 44, x11); + U32TO8_LITTLE(c + 48, x12); + U32TO8_LITTLE(c + 52, x13); + U32TO8_LITTLE(c + 56, x14); + U32TO8_LITTLE(c + 60, x15); + + if (bytes <= 64) { + if (bytes < 64) { + for (i = 0; i < bytes; ++i) { + ctarget[i] = c[i]; + } + } + x->input[12] = j12; + x->input[13] = j13; + return; + } + bytes -= 64; + c += 64; + m += 64; + } +} + +int +crypto_stream_chacha20_ref(unsigned char *c, unsigned long long clen, + const unsigned char *n, const unsigned char *k) +{ + struct chacha_ctx ctx; + + if (!clen) { + return 0; + } + (void) sizeof(int[crypto_stream_chacha20_KEYBYTES == 256 / 8 ? 1 : -1]); + chacha_keysetup(&ctx, k); + chacha_ivsetup(&ctx, n, NULL); + memset(c, 0, clen); + chacha_encrypt_bytes(&ctx, c, c, clen); + sodium_memzero(&ctx, sizeof ctx); + + return 0; +} + +int +crypto_stream_chacha20_ref_xor(unsigned char *c, const unsigned char *m, + unsigned long long mlen, const unsigned char *n, + const unsigned char *k) +{ + struct chacha_ctx ctx; + + if (!mlen) { + return 0; + } + chacha_keysetup(&ctx, k); + chacha_ivsetup(&ctx, n, NULL); + chacha_encrypt_bytes(&ctx, m, c, mlen); + sodium_memzero(&ctx, sizeof ctx); + + return 0; +} diff --git a/src/libsodium/crypto_stream/chacha20/stream_chacha20_api.c b/src/libsodium/crypto_stream/chacha20/stream_chacha20_api.c new file mode 100644 index 00000000..bc7ae706 --- /dev/null +++ b/src/libsodium/crypto_stream/chacha20/stream_chacha20_api.c @@ -0,0 +1,27 @@ +#include "crypto_stream_chacha20.h" +#include "ref/api.h" + +size_t +crypto_stream_chacha20_keybytes(void) { + return crypto_stream_chacha20_KEYBYTES; +} + +size_t +crypto_stream_chacha20_noncebytes(void) { + return crypto_stream_chacha20_NONCEBYTES; +} + +int +crypto_stream_chacha20(unsigned char *c, unsigned long long clen, + const unsigned char *n, const unsigned char *k) +{ + return crypto_stream_chacha20_ref(c, clen, n, k); +} + +int +crypto_stream_chacha20_xor(unsigned char *c, const unsigned char *m, + unsigned long long mlen, const unsigned char *n, + const unsigned char *k) +{ + return crypto_stream_chacha20_ref_xor(c, m, mlen, n, k); +} diff --git a/src/libsodium/include/sodium.h b/src/libsodium/include/sodium.h index 8da36f50..96e8091d 100644 --- a/src/libsodium/include/sodium.h +++ b/src/libsodium/include/sodium.h @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include diff --git a/src/libsodium/include/sodium/crypto_stream_chacha20.h b/src/libsodium/include/sodium/crypto_stream_chacha20.h new file mode 100644 index 00000000..df66d554 --- /dev/null +++ b/src/libsodium/include/sodium/crypto_stream_chacha20.h @@ -0,0 +1,43 @@ +#ifndef crypto_stream_chacha20_H +#define crypto_stream_chacha20_H + +/* + * WARNING: This is just a stream cipher. It is NOT authenticated encryption. + * While it provides some protection against eavesdropping, it does NOT + * provide any security against active attacks. + * Unless you know what you're doing, what you are looking for is probably + * the crypto_box functions. + */ + +#include +#include "export.h" + +#ifdef __cplusplus +# if __GNUC__ +# pragma GCC diagnostic ignored "-Wlong-long" +# endif +extern "C" { +#endif + +#define crypto_stream_chacha20_KEYBYTES 32U +SODIUM_EXPORT +size_t crypto_stream_chacha20_keybytes(void); + +#define crypto_stream_chacha20_NONCEBYTES 8U +SODIUM_EXPORT +size_t crypto_stream_chacha20_noncebytes(void); + +SODIUM_EXPORT +int crypto_stream_chacha20(unsigned char *c, unsigned long long clen, + const unsigned char *n, const unsigned char *k); + +SODIUM_EXPORT +int crypto_stream_chacha20_xor(unsigned char *c, const unsigned char *m, + unsigned long long mlen, const unsigned char *n, + const unsigned char *k); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/test/default/Makefile.am b/test/default/Makefile.am index 8b9e1130..f4a4aab1 100644 --- a/test/default/Makefile.am +++ b/test/default/Makefile.am @@ -14,6 +14,7 @@ EXTRA_DIST = \ box8.exp \ box_easy.exp \ box_easy2.exp \ + chacha20.exp \ core1.exp \ core2.exp \ core3.exp \ @@ -68,6 +69,7 @@ DISTCLEANFILES = \ box8.res \ box_easy.res \ box_easy2.res \ + chacha20.res \ core1.res \ core2.res \ core3.res \ @@ -130,6 +132,7 @@ TESTS_TARGETS = \ box8 \ box_easy \ box_easy2 \ + chacha20 \ core1 \ core2 \ core3 \ @@ -214,6 +217,9 @@ box_easy_LDADD = $(TESTS_LDADD) box_easy2_SOURCE = cmptest.h box_easy2.c box_easy2_LDADD = $(TESTS_LDADD) +chacha20_SOURCE = cmptest.h chacha20.c +chacha20_LDADD = $(TESTS_LDADD) + core1_SOURCE = cmptest.h core1.c core1_LDADD = $(TESTS_LDADD) diff --git a/test/default/chacha20.c b/test/default/chacha20.c new file mode 100644 index 00000000..4be4fa26 --- /dev/null +++ b/test/default/chacha20.c @@ -0,0 +1,44 @@ + +#include +#include + +#define TEST_NAME "chacha20" +#include "cmptest.h" + +static void tv(void) +{ + static struct { + const char *key_hex; + const char *nonce_hex; + } tests[] = { + {"0000000000000000000000000000000000000000000000000000000000000000","0000000000000000"}, + {"0000000000000000000000000000000000000000000000000000000000000001","0000000000000000"}, + {"0000000000000000000000000000000000000000000000000000000000000000","0000000000000001"}, + {"0000000000000000000000000000000000000000000000000000000000000000","0100000000000000"}, + }; + unsigned char key[crypto_stream_chacha20_KEYBYTES]; + unsigned char nonce[crypto_stream_chacha20_NONCEBYTES]; + unsigned char out[60]; + char out_hex[60 * 2 + 1]; + size_t i = 0U; + + do { + sodium_hex2bin((unsigned char *) key, sizeof key, + tests[i].key_hex, strlen(tests[i].key_hex), + NULL, NULL, NULL); + sodium_hex2bin(nonce, sizeof nonce, + tests[i].nonce_hex, strlen(tests[i].nonce_hex), + NULL, NULL, NULL); + crypto_stream_chacha20(out, sizeof out, nonce, key); + sodium_bin2hex(out_hex, sizeof out_hex, out, sizeof out); + printf("[%s]\n", out_hex); + } while (++i < (sizeof tests) / (sizeof tests[0])); +}; + +int main(void) +{ + tv(); + + return 0; +} +