From c49cc862eb169cc3a782961ab3ec58a7c2c6d022 Mon Sep 17 00:00:00 2001 From: Marko Kreen Date: Wed, 9 Jul 2014 21:40:45 +0300 Subject: [PATCH] csrandom, keccak_prng, chacha: new modules csrandom implements arc4random_buf api. keccak_prng implements PRNG based on Keccak sponge. chacha implements ChaCha hash. --- Makefile | 4 + doc/mainpage.dox | 2 + m4/usual.m4 | 2 +- test/force_compat.sed | 2 + test/test_crypto.c | 229 +++++++++++++++++++++++++++++++++++- usual/crypto/chacha.c | 156 +++++++++++++++++++++++++ usual/crypto/chacha.h | 71 +++++++++++ usual/crypto/csrandom.c | 234 +++++++++++++++++++++++++++++++++++++ usual/crypto/csrandom.h | 45 +++++++ usual/crypto/entropy.c | 181 ++++++++++++++++++++++++++++ usual/crypto/entropy.h | 37 ++++++ usual/crypto/keccak_prng.c | 41 +++++++ usual/crypto/keccak_prng.h | 20 ++++ 13 files changed, 1021 insertions(+), 3 deletions(-) create mode 100644 usual/crypto/chacha.c create mode 100644 usual/crypto/chacha.h create mode 100644 usual/crypto/csrandom.c create mode 100644 usual/crypto/csrandom.h create mode 100644 usual/crypto/entropy.c create mode 100644 usual/crypto/entropy.h create mode 100644 usual/crypto/keccak_prng.c create mode 100644 usual/crypto/keccak_prng.h diff --git a/Makefile b/Makefile index 6b774bd..30bfdef 100644 --- a/Makefile +++ b/Makefile @@ -18,9 +18,13 @@ libusual_a_SOURCES = usual/config.h.in \ usual/cbtree.h usual/cbtree.c \ usual/cfparser.h usual/cfparser.c \ usual/config_msvc.h \ + usual/crypto/chacha.h usual/crypto/chacha.c \ + usual/crypto/csrandom.h usual/crypto/csrandom.c \ usual/crypto/digest.h usual/crypto/digest.c \ + usual/crypto/entropy.h usual/crypto/entropy.c \ usual/crypto/hmac.h usual/crypto/hmac.c \ usual/crypto/keccak.h usual/crypto/keccak.c \ + usual/crypto/keccak_prng.h usual/crypto/keccak_prng.c \ usual/crypto/md5.h usual/crypto/md5.c \ usual/crypto/sha1.h usual/crypto/sha1.c \ usual/crypto/sha256.h usual/crypto/sha256.c \ diff --git a/doc/mainpage.dox b/doc/mainpage.dox index c177d66..7e9b54d 100644 --- a/doc/mainpage.dox +++ b/doc/mainpage.dox @@ -70,6 +70,7 @@ * Jenkins' SpookyHash for 64-bit CPUs * XXHash for 32-bit CPUs * Cryptography + * Cryptographically Secure Randomness * Common API for cryptographic message digests * HMAC with digest * MD5 hash @@ -78,6 +79,7 @@ * SHA512/384 hashes * SHA3/SHAKE hashes * Keccak sponge API + * PRNG based on Keccak * Memory Allocation * Context Allocator framework * Extra allocators diff --git a/m4/usual.m4 b/m4/usual.m4 index 864f90e..178fee1 100644 --- a/m4/usual.m4 +++ b/m4/usual.m4 @@ -212,7 +212,7 @@ AC_CHECK_FUNCS(fnmatch mbsnrtowcs nl_langinfo strtod_l) ### Functions provided only on win32 AC_CHECK_FUNCS(localtime_r gettimeofday recvmsg sendmsg usleep getrusage) ### Functions used by libusual itself -AC_CHECK_FUNCS(syslog mmap getpeerucred) +AC_CHECK_FUNCS(syslog mmap getpeerucred arc4random_buf getentropy) ### win32: link with ws2_32 AC_SEARCH_LIBS(WSAGetLastError, ws2_32) AC_FUNC_STRERROR_R diff --git a/test/force_compat.sed b/test/force_compat.sed index dc3033e..bd7f0f5 100644 --- a/test/force_compat.sed +++ b/test/force_compat.sed @@ -12,3 +12,5 @@ /^#define.*CTYPE_ON_CHAR/s,.*,/* & */, /^#define.*FNMATCH/s,.*,/* & */, /^#define.*MBSNRTOWCS/s,.*,/* & */, +/^#define.*GETENTROPY/s,.*,/* & */, +/^#define.*ARC4RANDOM/s,.*,/* & */, diff --git a/test/test_crypto.c b/test/test_crypto.c index ed13606..407d236 100644 --- a/test/test_crypto.c +++ b/test/test_crypto.c @@ -1,8 +1,7 @@ #include -#include "tinytest.h" -#include "tinytest_macros.h" +#include "test_common.h" #define str_check(a, b) tt_str_op(a, ==, b) @@ -19,6 +18,9 @@ #include #include #include +#include +#include +#include #include static const char *mkhex(const uint8_t *src, int len) @@ -509,6 +511,226 @@ static void test_hmac(void *ptr) end:; } +/* + * keccak_prng + */ + +static void test_keccak_prng(void *z) +{ + struct KeccakPRNG state; + const char *ent = "The quick brown fox jumps over the lazy dog."; + const char *ent2 = "More entropy."; + uint8_t buf[32]; + int i; + + tt_assert(keccak_prng_init(&state, 1) == false); + tt_assert(keccak_prng_init(&state, 1024) == true); + tt_assert(keccak_prng_extract(&state, buf, 32) == false); + + keccak_prng_add_data(&state, ent, strlen(ent)); + + tt_assert(keccak_prng_extract(&state, buf, 32) == true); + str_check(mkhex(buf, 32), "ab7192d2b11f51c7dd744e7b3441febf397ca07bf812cceae122ca4ded638788"); + + if (1) { + tt_assert(keccak_prng_extract(&state, buf, 32) == true); + } else { + for (i = 0; i < 32; i++) { + tt_assert(keccak_prng_extract(&state, buf+i, 1) == true); + } + } + str_check(mkhex(buf, 32), "9064f8db9230f173f6d1ab6e24b6e50f065b039f799f5592360a6558eb52d760"); + + tt_assert(keccak_prng_extract(&state, buf, 32) == true); + str_check(mkhex(buf, 32), "7ca34f68abb61bbd1821c0a499599426031a56c495b3cf91b84cacafb9be816b"); + + tt_assert(keccak_prng_extract(&state, buf, 32) == true); + str_check(mkhex(buf, 32), "e7afb50b3a1c80f654ba212be0ad8a4be8f6a476bfcc66b9401fe65924bd547d"); + + keccak_prng_add_data(&state, ent2, strlen(ent2)); + + tt_assert(keccak_prng_extract(&state, buf, 32) == true); + str_check(mkhex(buf, 32), "ec9f73358469f4b7fea10dfb7dfaa768f573089b8e00507ec3a1fdfb2e60b35d"); +end:; +} + +/* + * chacha. + */ + +static const char *run_chacha(const char *key, const char *iv, uint32_t c1, uint32_t c2) +{ + int klen = strlen(key) / 2; + void *kb, *ivb; + struct ChaCha ctx; + uint8_t output[128]; + + if (klen != 32 && klen != 16) + return "KeyError"; + if (strlen(iv) != 8*2) + return "IvError"; + + kb = fromhex(key, klen); + if (klen == 32) { + chacha_set_key_256(&ctx, kb); + } else { + chacha_set_key_128(&ctx, kb); + } + free(kb); + + ivb = fromhex(iv, 8); + chacha_set_nonce(&ctx, c1, c2, ivb); + free(ivb); + + if (1) { + static unsigned int blkver = 0; + static const int blklens[] = {128, 1, 7, 11, 66}; + int blk = blklens[blkver++]; + int n, need = sizeof(output); + uint8_t *dst = output; + if (blkver >= ARRAY_NELEM(blklens)) blkver = 0; + while (need > 0) { + n = (need > blk) ? blk : need; + chacha_keystream(&ctx, dst, n); + dst += n; + need -= n; + } + } else { + chacha_keystream(&ctx, output, sizeof(output)); + } + + return mkhex(output, sizeof(output)); +} + +/* https://tools.ietf.org/html/draft-strombergson-chacha-test-vectors-01 */ +static void test_chacha(void *z) +{ + /* TC1: All zero key and IV. */ + str_check(run_chacha("00000000000000000000000000000000", "0000000000000000", 0, 0), + "89670952608364fd00b2f90936f031c8e756e15dba04b8493d00429259b20f46cc04f111246b6c2ce066be3bfb32d9aa0fddfbc12123d4b9e44f34dca05a103f" + "6cd135c2878c832b5896b134f6142a9d4d8d0d8f1026d20a0a81512cbce6e9758a7143d021978022a384141a80cea3062f41f67a752e66ad3411984c787e30ad"); + str_check(run_chacha("0000000000000000000000000000000000000000000000000000000000000000", "0000000000000000", 0, 0), + "76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da41597c5157488d7724e03fb8d84a376a43b8f41518a11cc387b669b2ee6586" + "9f07e7be5551387a98ba977c732d080dcb0f29a048e3656912c6533e32ee7aed29b721769ce64e43d57133b074d839d531ed1f28510afb45ace10a1f4b794d6f"); + + /* TC2: Single bit in key set. All zero IV. */ + str_check(run_chacha("01000000000000000000000000000000", "0000000000000000", 0, 0), + "ae56060d04f5b597897ff2af1388dbceff5a2a4920335dc17a3cb1b1b10fbe70ece8f4864d8c7cdf0076453a8291c7dbeb3aa9c9d10e8ca36be4449376ed7c42" + "fc3d471c34a36fbbf616bc0a0e7c523030d944f43ec3e78dd6a12466547cb4f7b3cebd0a5005e762e562d1375b7ac44593a991b85d1a60fba2035dfaa2a642d5"); + str_check(run_chacha("0100000000000000000000000000000000000000000000000000000000000000", "0000000000000000", 0, 0), + "c5d30a7ce1ec119378c84f487d775a8542f13ece238a9455e8229e888de85bbd29eb63d0a17a5b999b52da22be4023eb07620a54f6fa6ad8737b71eb0464dac0" + "10f656e6d1fd55053e50c4875c9930a33f6d0263bd14dfd6ab8c70521c19338b2308b95cf8d0bb7d202d2102780ea3528f1cb48560f76b20f382b942500fceac"); + + /* TC3: Single bit in IV set. All zero key. */ + str_check(run_chacha("00000000000000000000000000000000", "0100000000000000", 0, 0), + "1663879eb3f2c9949e2388caa343d361bb132771245ae6d027ca9cb010dc1fa7178dc41f8278bc1f64b3f12769a24097f40d63a86366bdb36ac08abe60c07fe8" + "b057375c89144408cc744624f69f7f4ccbd93366c92fc4dfcada65f1b959d8c64dfc50de711fb46416c2553cc60f21bbfd006491cb17888b4fb3521c4fdd8745"); + str_check(run_chacha("0000000000000000000000000000000000000000000000000000000000000000", "0100000000000000", 0, 0), + "ef3fdfd6c61578fbf5cf35bd3dd33b8009631634d21e42ac33960bd138e50d32111e4caf237ee53ca8ad6426194a88545ddc497a0b466e7d6bbdb0041b2f586b" + "5305e5e44aff19b235936144675efbe4409eb7e8e5f1430f5f5836aeb49bb5328b017c4b9dc11f8a03863fa803dc71d5726b2b6b31aa32708afe5af1d6b69058"); + + /* TC4: All bits in key and IV are set. */ + str_check(run_chacha("ffffffffffffffffffffffffffffffff", "ffffffffffffffff", 0, 0), + "992947c3966126a0e660a3e95db048de091fb9e0185b1e41e41015bb7ee50150399e4760b262f9d53f26d8dd19e56f5c506ae0c3619fa67fb0c408106d0203ee" + "40ea3cfa61fa32a2fda8d1238a2135d9d4178775240f99007064a6a7f0c731b67c227c52ef796b6bed9f9059ba0614bcf6dd6e38917f3b150e576375be50ed67"); + str_check(run_chacha("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "ffffffffffffffff", 0, 0), + "d9bf3f6bce6ed0b54254557767fb57443dd4778911b606055c39cc25e674b8363feabc57fde54f790c52c8ae43240b79d49042b777bfd6cb80e931270b7f50eb" + "5bac2acd86a836c5dc98c116c1217ec31d3a63a9451319f097f3b4d6dab0778719477d24d24b403a12241d7cca064f790f1d51ccaff6b1667d4bbca1958c4306"); + + /* TC5: Every even bit set in key and IV. */ + str_check(run_chacha("55555555555555555555555555555555", "5555555555555555", 0, 0), + "357d7d94f966778f5815a2051dcb04133b26b0ead9f57dd09927837bc3067e4b6bf299ad81f7f50c8da83c7810bfc17bb6f4813ab6c326957045fd3fd5e19915" + "ec744a6b9bf8cbdcb36d8b6a5499c68a08ef7be6cc1e93f2f5bcd2cad4e47c18a3e5d94b5666382c6d130d822dd56aacb0f8195278e7b292495f09868ddf12cc"); + str_check(run_chacha("5555555555555555555555555555555555555555555555555555555555555555", "5555555555555555", 0, 0), + "bea9411aa453c5434a5ae8c92862f564396855a9ea6e22d6d3b50ae1b3663311a4a3606c671d605ce16c3aece8e61ea145c59775017bee2fa6f88afc758069f7" + "e0b8f676e644216f4d2a3422d7fa36c6c4931aca950e9da42788e6d0b6d1cd838ef652e97b145b14871eae6c6804c7004db5ac2fce4c68c726d004b10fcaba86"); + + /* TC6: Every odd bit set in key and IV. */ + str_check(run_chacha("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaa", 0, 0), + "fc79acbd58526103862776aab20f3b7d8d3149b2fab65766299316b6e5b16684de5de548c1b7d083efd9e3052319e0c6254141da04a6586df800f64d46b01c87" + "1f05bc67e07628ebe6f6865a2177e0b66a558aa7cc1e8ff1a98d27f7071f8335efce4537bb0ef7b573b32f32765f29007da53bba62e7a44d006f41eb28fe15d6"); + str_check(run_chacha("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaa", 0, 0), + "9aa2a9f656efde5aa7591c5fed4b35aea2895dec7cb4543b9e9f21f5e7bcbcf3c43c748a970888f8248393a09d43e0b7e164bc4d0b0fb240a2d72115c4808906" + "72184489440545d021d97ef6b693dfe5b2c132d47e6f041c9063651f96b623e62a11999a23b6f7c461b2153026ad5e866a2e597ed07b8401dec63a0934c6b2a9"); + + /* TC7: Sequence patterns in key and IV. */ + str_check(run_chacha("00112233445566778899aabbccddeeff", "0f1e2d3c4b5a6978", 0, 0), + "d1abf630467eb4f67f1cfb47cd626aae8afedbbe4ff8fc5fe9cfae307e74ed451f1404425ad2b54569d5f18148939971abb8fafc88ce4ac7fe1c3d1f7a1eb7ca" + "e76ca87b61a9713541497760dd9ae059350cad0dcedfaa80a883119a1a6f987fd1ce91fd8ee0828034b411200a9745a285554475d12afc04887fef3516d12a2c"); + str_check(run_chacha("00112233445566778899aabbccddeeffffeeddccbbaa99887766554433221100", "0f1e2d3c4b5a6978", 0, 0), + "9fadf409c00811d00431d67efbd88fba59218d5d6708b1d685863fabbb0e961eea480fd6fb532bfd494b2151015057423ab60a63fe4f55f7a212e2167ccab931" + "fbfd29cf7bc1d279eddf25dd316bb8843d6edee0bd1ef121d12fa17cbc2c574cccab5e275167b08bd686f8a09df87ec3ffb35361b94ebfa13fec0e4889d18da5"); + + /* TC8: Random Key */ + str_check(run_chacha("c46ec1b18ce8a878725a37e780dfb735", "1ada31d5cf688221", 0, 0), + "826abdd84460e2e9349f0ef4af5b179b426e4b2d109a9c5bb44000ae51bea90a496beeef62a76850ff3f0402c4ddc99f6db07f151c1c0dfac2e56565d6289625" + "5b23132e7b469c7bfb88fa95d44ca5ae3e45e848a4108e98bad7a9eb15512784a6a9e6e591dce674120acaf9040ff50ff3ac30ccfb5e14204f5e4268b90a8804"); + str_check(run_chacha("c46ec1b18ce8a878725a37e780dfb7351f68ed2e194c79fbc6aebee1a667975d", "1ada31d5cf688221", 0, 0), + "f63a89b75c2271f9368816542ba52f06ed49241792302b00b5e8f80ae9a473afc25b218f519af0fdd406362e8d69de7f54c604a6e00f353f110f771bdca8ab92" + "e5fbc34e60a1d9a9db17345b0a402736853bf910b060bdf1f897b6290f01d138ae2c4c90225ba9ea14d518f55929dea098ca7a6ccfe61227053c84e49a4a3332"); + + /* Counter overflow */ + str_check(run_chacha("c46ec1b18ce8a878725a37e780dfb735", "1ada31d5cf688221", 0xFFFFFFFF, 0), + "9220e40c9fa71e1e210b216b5f1c829e73d31c6891447084d5c5bc29db812ec2f4c64a594214a3437cb04548f9a1c4839f03405f1e2b5db69d9df11474a4610c" + "60f15b0e01460b61d8ac73cac1de084c741c157fb75d52719ed98a62bcada8187041035178a1845b164f0d82b4fc20a6ad1668d3177fac688cb08d3df75281fe"); + str_check(run_chacha("c46ec1b18ce8a878725a37e780dfb7351f68ed2e194c79fbc6aebee1a667975d", "1ada31d5cf688221", 0xFFFFFFFF, 0xFFFFFFFF), + "489b569d1a0649a3185f04dfda7cbb688503f3485ea0754e7a9c17452e0f6a123a1e24d4313d79c9bf7c4fd714211dca39c1717f29b7d137158b7f620bdcb759" + "f63a89b75c2271f9368816542ba52f06ed49241792302b00b5e8f80ae9a473afc25b218f519af0fdd406362e8d69de7f54c604a6e00f353f110f771bdca8ab92"); +end:; +} + +/* + * csrandom. + */ + +static uint32_t calc_lim(uint32_t val) +{ + uint32_t mod, lim; + + /* mod = 2**32 % x = (2**32 - x) % x */ + mod = -val % val; + + /* wait for value in range [0 .. 2**32 - mod ) */ + lim = -mod; + + return lim; +} + +static void test_csrandom(void *z) +{ + uint32_t half, v, lim, i, v2; + + half = 1 << 31; + for (v = 2; v < 1024; v++) { + lim = calc_lim(v); + int_check(lim % v, 0); + tt_assert(lim >= v || lim == 0); + } + + for (v = half - 1024; v < half + 1024; v++) { + lim = calc_lim(v); + int_check(lim % v, 0); + tt_assert(lim >= v || lim == 0); + } + + for (v = 0xFFFFFF00U; v != 0; v++) { + lim = calc_lim(v); + int_check(lim % v, 0); + tt_assert(lim >= v); + } + + lim = 0; + v = csrandom(); + for (i = 0; i < 100; i++) { + v2 = csrandom(); + if (v == v2) + lim++; + v = v2; + } + tt_assert(lim < 10); +end:; +} + /* * Launcher. */ @@ -527,6 +749,9 @@ struct testcase_t crypto_tests[] = { { "shake128", test_shake128 }, { "shake256", test_shake256 }, { "hmac", test_hmac }, + { "keccak_prng", test_keccak_prng }, + { "chacha", test_chacha }, + { "csrandom", test_csrandom }, END_OF_TESTCASES }; diff --git a/usual/crypto/chacha.c b/usual/crypto/chacha.c new file mode 100644 index 0000000..dcfa753 --- /dev/null +++ b/usual/crypto/chacha.c @@ -0,0 +1,156 @@ +/* + * ChaCha cipher. + * + * Copyright (c) 2014 Marko Kreen + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Based on: chacha-ref.c version 20080118 / D. J. Bernstein / Public domain. + */ + +#include + +#include +#include + +/* ChaCha rounds: 8, 12, 20 */ +#define CHACHA_ROUNDS 20 + +/* mix 4 values */ +#define QUARTERROUND(a, b, c, d) \ + do { \ + a += b; d ^= a; d = rol32(d, 16); \ + c += d; b ^= c; b = rol32(b, 12); \ + a += b; d ^= a; d = rol32(d, 8); \ + c += d; b ^= c; b = rol32(b, 7); \ + } while (0) + +/* mix full state. must not be inlined */ +static void chacha_mix(struct ChaCha *ctx) +{ + uint32_t *output = ctx->u.output32; + int i; + uint32_t x[16]; + + memcpy(x, ctx->state, CHACHA_BLOCK_SIZE); + + for (i = 0; i < CHACHA_ROUNDS/2; i++) { + /* column round */ + QUARTERROUND(x[0], x[4], x[ 8], x[12]); + QUARTERROUND(x[1], x[5], x[ 9], x[13]); + QUARTERROUND(x[2], x[6], x[10], x[14]); + QUARTERROUND(x[3], x[7], x[11], x[15]); + + /* diagonal round */ + QUARTERROUND(x[0], x[5] ,x[10], x[15]); + QUARTERROUND(x[1], x[6], x[11], x[12]); + QUARTERROUND(x[2], x[7], x[ 8], x[13]); + QUARTERROUND(x[3], x[4], x[ 9], x[14]); + } + + for (i = 0; i < 16; i++) + output[i] = htole32(x[i] + ctx->state[i]); + + ctx->pos = 0; + + ctx->state[12]++; + if (!ctx->state[12]) + ctx->state[13]++; +} + +void chacha_set_key_256(struct ChaCha *ctx, const void *key) +{ + unsigned int i; + + memcpy(&ctx->state[0], "expand 32-byte k", 16); + memcpy(&ctx->state[4], key, 32); + for (i = 0; i < 12; i++) + ctx->state[i] = le32toh(ctx->state[i]); + + ctx->pos = CHACHA_BLOCK_SIZE; +} + +void chacha_set_key_128(struct ChaCha *ctx, const void *key) +{ + unsigned int i; + + memcpy(&ctx->state[0], "expand 16-byte k", 16); + memcpy(&ctx->state[4], key, 16); + memcpy(&ctx->state[8], key, 16); + for (i = 0; i < 12; i++) + ctx->state[i] = le32toh(ctx->state[i]); + + ctx->pos = CHACHA_BLOCK_SIZE; +} + +void chacha_set_nonce(struct ChaCha *ctx, uint32_t counter_low, uint32_t counter_high, const void *iv) +{ + const uint8_t *_iv = iv; + + ctx->state[12] = counter_low; + ctx->state[13] = counter_high; + if (_iv) { + ctx->state[14] = le32dec(_iv); + ctx->state[15] = le32dec(_iv + 4); + } + + ctx->pos = CHACHA_BLOCK_SIZE; +} + +void chacha_keystream(struct ChaCha *ctx, void *stream, size_t bytes) +{ + unsigned int n, avail; + const uint8_t *ks = ctx->u.output8; + uint8_t *dst = stream; + + while (bytes > 0) { + if (ctx->pos >= CHACHA_BLOCK_SIZE) + chacha_mix(ctx); + + avail = CHACHA_BLOCK_SIZE - ctx->pos; + n = (bytes > avail) ? avail : bytes; + + memcpy(dst, ks + ctx->pos, n); + + bytes -= n; + dst += n; + ctx->pos += n; + } +} + +void chacha_keystream_xor(struct ChaCha *ctx, const void *plain, void *encrypted, size_t bytes) +{ + unsigned int i, n, avail; + const uint8_t *ks = ctx->u.output8; + const uint8_t *src = plain; + uint8_t *dst = encrypted; + + while (bytes > 0) { + if (ctx->pos >= CHACHA_BLOCK_SIZE) + chacha_mix(ctx); + + avail = CHACHA_BLOCK_SIZE - ctx->pos; + n = (bytes > avail) ? avail : bytes; + + for (i = 0; i < n; i++) + dst[i] = src[i] ^ ks[i]; + + bytes -= n; + dst += n; + src += n; + ctx->pos += n; + } +} + diff --git a/usual/crypto/chacha.h b/usual/crypto/chacha.h new file mode 100644 index 0000000..07093db --- /dev/null +++ b/usual/crypto/chacha.h @@ -0,0 +1,71 @@ +/* + * ChaCha cipher. + * + * Copyright (c) 2014 Marko Kreen + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * @file + * ChaCha cipher. + */ + +#ifndef _CHACHA_CLEAN_H_ +#define _CHACHA_CLEAN_H_ + +#include + +#define CHACHA_KEY_SIZE 32 +#define CHACHA_IV_SIZE 8 +#define CHACHA_BLOCK_SIZE 64 + +/** + * ChaCha state. + */ +struct ChaCha { + uint32_t state[16]; + union { + uint32_t output32[16]; + uint8_t output8[16*4]; + } u; + unsigned int pos; +}; + +/** + * Set 256-bit key. + */ +void chacha_set_key_256(struct ChaCha *ctx, const void *key); + +/** + * Set 128-bit key. + */ +void chacha_set_key_128(struct ChaCha *ctx, const void *key); + +/** + * Set 2x32-bit counter and 8-byte IV. + */ +void chacha_set_nonce(struct ChaCha *ctx, uint32_t counter_low, uint32_t counter_high, const void *iv); + +/** + * Extract plain keystream. + */ +void chacha_keystream(struct ChaCha *ctx, void *stream, size_t bytes); + +/** + * XOR data with keystream. + */ +void chacha_keystream_xor(struct ChaCha *ctx, const void *plain, void *encrypted, size_t bytes); + +#endif + diff --git a/usual/crypto/csrandom.c b/usual/crypto/csrandom.c new file mode 100644 index 0000000..69458bc --- /dev/null +++ b/usual/crypto/csrandom.c @@ -0,0 +1,234 @@ +/* + * Cryptographically Secure Randomness. + * + * Copyright (c) 2014 Marko Kreen + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include +#include + +#ifdef HAVE_ARC4RANDOM_BUF + +/* + * Simply wrap arc4random_buf() API. + */ + +uint32_t csrandom(void) +{ + return arc4random(); +} + +void csrandom_bytes(void *buf, size_t nbytes) +{ + arc4random_buf(buf, nbytes); +} + +uint32_t csrandom_range(upper_bound) +{ + return arc4random_uniform(upper_bound); +} + +#else /* !HAVE_ARC4RANDOM_BUF */ + +#define USE_KECCAK + +#ifdef USE_KECCAK + +/* + * Keccak-based PRNG. + */ + +static struct KeccakPRNG prng_keccak; + +static void impl_init(void) +{ + char buf[32]; + + if (getentropy(buf, sizeof(buf)) != 0) + errx(1, "Cannot get system entropy"); + + if (!keccak_prng_init(&prng_keccak, 576)) + errx(1, "Cannot initialize PRNG"); + keccak_prng_add_data(&prng_keccak, buf, sizeof(buf)); + + explicit_bzero(buf, sizeof(buf)); +} + +static void impl_extract(void *buf, size_t nbytes) +{ + keccak_prng_extract(&prng_keccak, buf, nbytes); +} + +#else + +/* + * ChaCha-based PRNG. + */ + +static struct ChaCha prng_chacha; + +static void impl_init(void) +{ + uint8_t buf[CHACHA_KEY_SIZE + CHACHA_IV_SIZE]; + + if (getentropy(buf, sizeof(buf)) != 0) + errx(1, "Cannot get system entropy"); + + chacha_set_key_256(&prng_chacha, buf); + chacha_set_nonce(&prng_chacha, 0, 0, buf + CHACHA_KEY_SIZE); + + explicit_bzero(buf, sizeof(buf)); +} + +static void impl_extract(void *buf, size_t nbytes) +{ + chacha_keystream(&prng_chacha, buf, nbytes); +} + +#endif + +/* + * Locking + */ + +static pthread_once_t once_init = PTHREAD_ONCE_INIT; +static pthread_mutex_t prng_mutex; + +static pid_t last_pid; +static int first_init_done; + + +#ifdef HAVE_PTHREAD_ATFORK +static void atfork_child(void) +{ + pthread_once_t tmp = PTHREAD_ONCE_INIT; + memcpy(&once_init, &tmp, sizeof(tmp)); + last_pid = -1; +} +#endif + +static void setup_locking(void) +{ + int err; + char buf[128]; + + last_pid = -1; + + /* reset lock */ + memset(&prng_mutex, 0, sizeof(prng_mutex)); + err = pthread_mutex_init(&prng_mutex, NULL); + if (err != 0) + errx(1, "csrandom lock init failed: %s", strerror_r(err, buf, sizeof(buf))); + + /* do the rest really only once */ + if (first_init_done) + return; + first_init_done = true; + +#ifdef HAVE_PTHREAD_ATFORK + pthread_atfork(NULL, NULL, atfork_child); +#endif +} + +static void prng_lock(void) +{ + int err; + char buf[128]; + + err = pthread_mutex_lock(&prng_mutex); + if (err != 0) + errx(1, "csrandom lock failed: %s", strerror_r(err, buf, sizeof(buf))); +} + +static void prng_unlock(void) +{ + int err; + char buf[128]; + + err = pthread_mutex_unlock(&prng_mutex); + if (err != 0) + errx(1, "csrandom unlock failed: %s", strerror_r(err, buf, sizeof(buf))); +} + +/* + * Make sure state is initialized. + */ + +static void prng_check_and_lock(void) +{ + bool reseed = false; + pid_t new_pid; + + pthread_once(&once_init, setup_locking); + + prng_lock(); + + new_pid = getpid(); + if (new_pid != last_pid) { + reseed = true; + last_pid = new_pid; + } + + if (reseed) + impl_init(); +} + +/* + * Public API follows + */ + +void csrandom_bytes(void *buf, size_t nbytes) +{ + prng_check_and_lock(); + impl_extract(buf, nbytes); + prng_unlock(); +} + +uint32_t csrandom(void) +{ + uint32_t val; + csrandom_bytes(&val, sizeof(val)); + return val; +} + +uint32_t csrandom_range(uint32_t upper_bound) +{ + uint32_t mod, lim, val; + + if (upper_bound <= 1) + return 0; + + /* 2**32 % x == (2**32 - x) % x */ + mod = -upper_bound % upper_bound; + + /* wait for value in range [0 .. 2**32-mod) */ + lim = -mod; + + /* loop until good value appears */ + while (1) { + val = csrandom(); + if (val < lim || lim == 0) + return val % upper_bound; + } +} + +#endif /* !HAVE_ARC4RANDOM_BUF */ + diff --git a/usual/crypto/csrandom.h b/usual/crypto/csrandom.h new file mode 100644 index 0000000..13b744a --- /dev/null +++ b/usual/crypto/csrandom.h @@ -0,0 +1,45 @@ +/* + * Cryptographically Secure Randomness. + * + * Copyright (c) 2014 Marko Kreen + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * @file + * + * Cryptographically Secure Randomness. + */ + +#ifndef _USUAL_CRYPTO_CSRANDOM_H_ +#define _USUAL_CRYPTO_CSRANDOM_H_ + +#include + +/** + * Return random uint32_t. + */ +uint32_t csrandom(void); + +/** + * Return unsigned integer in range. + */ +uint32_t csrandom_range(uint32_t upper_bound); + +/** + * Fill buffer with random bytes. + */ +void csrandom_bytes(void *buf, size_t nbytes); + +#endif diff --git a/usual/crypto/entropy.c b/usual/crypto/entropy.c new file mode 100644 index 0000000..bdf91fc --- /dev/null +++ b/usual/crypto/entropy.c @@ -0,0 +1,181 @@ +/* + * Load entropy. + * + * Copyright (c) 2014 Marko Kreen + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define _CRT_RAND_S + +#include +#include +#include + +#ifndef HAVE_GETENTROPY + +/* + * Load system entropy. + */ + +#if defined(_WIN32) || defined(_WIN64) + +/* + * Windows + * + * It's possible to get entropy via: + * - CryptGenRandom. Uses RtlGenRandom, requires CryptoAPI. + * - rand_s(). Uses RtlGenRandom, Requires VS2005 CRT, WindowsXP+. + * - RtlGenRandom(). Internal func, no public definition. + */ + +int getentropy(void *dst, size_t len) +{ + uint8_t *d = dst; + unsigned int val, n; + errno_t err; + + while (len > 0) { + err = rand_s(&val); + if (err != 0) + return -1; + n = (len > sizeof(val)) ? sizeof(val) : len; + memcpy(d, &val, n); + d += n; + } + explicit_bzero(&val, sizeof(val)); + return 0; +} + +#elif defined(HAVE_GETRANDOM) + +int getentropy(void *dst, size_t len) +{ + int res; + + if (len > 256) + goto eio; + res = getrandom(dst, len, 0); + if (res < 0) + return -1; + if (res == len) + return res; +eio: + errno = EIO; + return -1; +} + +#else /* UNIX-like system */ + +#include +#include + +/* open and check device node */ +static int open_devrandom(const char *dev) +{ + int fd; + int oflags = O_RDONLY; +#ifdef O_CLOEXEC + oflags |= O_CLOEXEC; +#endif + +open_loop: + fd = open(dev, oflags); + if (fd == -1) { + if (errno == EINTR) + goto open_loop; + return -1; + } + +#ifndef O_CLOEXEC + fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); +#endif + + /* + * Lightly verify that the device node looks sane + */ + + { + struct stat st; + if (fstat(fd, &st) == -1 || !S_ISCHR(st.st_mode)) + goto fail; + } +#ifdef RNDGETENTCNT + { + int cnt; + if (ioctl(fd, RNDGETENTCNT, &cnt) == -1) + goto fail; + } +#endif + + /* seems fine */ + return fd; + +fail: + close(fd); + return -1; +} + +/* + * Read normal random devices under /dev. + */ + +static const char *devlist[] = { + "/dev/urandom", + "/dev/random", + NULL, +}; + +int getentropy(void *dst, size_t bytes) +{ + uint8_t *d = dst; + size_t need = bytes; + int fd, res; + unsigned int i; + + for (i = 0; devlist[i]; i++) { + reopen: + fd = open_devrandom(devlist[i]); + if (fd == -1) + continue; + + while (need > 0) { + res = read(fd, d, need); + if (res > 0) { + /* successful read */ + need -= res; + d += res; + } else if (res == 0) { + /* eof - open again */ + close(fd); + goto reopen; + } else if (errno == EINTR) { + /* signal - retry read */ + } else { + close(fd); + /* random error, fail */ + return -1; + } + } + close(fd); + return 0; + } + + errno = EIO; + return -1; +} + +#endif /* unix */ + +#endif /* !HAVE_GETENTROPY */ + diff --git a/usual/crypto/entropy.h b/usual/crypto/entropy.h new file mode 100644 index 0000000..4949683 --- /dev/null +++ b/usual/crypto/entropy.h @@ -0,0 +1,37 @@ +/* + * Load entropy from kernel. + * + * Copyright (c) 2014 Marko Kreen + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * @file + * Load entropy from OS. + */ + +#ifndef _USUAL_CRYPTO_ENTROPY_H_ +#define _USUAL_CRYPTO_ENTROPY_H_ + +#include + +#ifndef HAVE_GETENTROPY + +/** + * Fetch entropy from OS kernel. + */ +int getentropy(void *dst, size_t len); + +#endif /* !HAVE_GETENTROPY */ +#endif /* _USUAL_CRYPTO_ENTROPY_H_ */ diff --git a/usual/crypto/keccak_prng.c b/usual/crypto/keccak_prng.c new file mode 100644 index 0000000..6eb1ab2 --- /dev/null +++ b/usual/crypto/keccak_prng.c @@ -0,0 +1,41 @@ +/* + * PRNG based on Keccak. + */ + + +#include + +bool keccak_prng_init(struct KeccakPRNG *prng, int capacity) +{ + if (!keccak_init(&prng->ctx, capacity)) + return false; + prng->extracting = false; + prng->have_data = false; + return true; +} + +void keccak_prng_add_data(struct KeccakPRNG *prng, const void *data, size_t len) +{ + if (prng->extracting) { + keccak_rewind(&prng->ctx); + prng->extracting = false; + } + + keccak_absorb(&prng->ctx, data, len); + + if (!prng->have_data && len > 0) + prng->have_data = true; +} + +bool keccak_prng_extract(struct KeccakPRNG *prng, void *data, size_t len) +{ + if (!prng->have_data) + return false; + if (!prng->extracting) { + keccak_pad(&prng->ctx, "\x01", 1); + prng->extracting = true; + } + keccak_squeeze(&prng->ctx, data, len); + return true; +} + diff --git a/usual/crypto/keccak_prng.h b/usual/crypto/keccak_prng.h new file mode 100644 index 0000000..ddd0b33 --- /dev/null +++ b/usual/crypto/keccak_prng.h @@ -0,0 +1,20 @@ +#ifndef _USUAL_CRYPTO_KECCAK_PRNG_H_ +#define _USUAL_CRYPTO_KECCAK_PRNG_H_ + +#include + +/* + * PRNG based on Keccak. + */ + +struct KeccakPRNG { + struct KeccakContext ctx; + bool extracting; + bool have_data; +}; + +bool keccak_prng_init(struct KeccakPRNG *prng, int capacity); +void keccak_prng_add_data(struct KeccakPRNG *prng, const void *data, size_t len); +bool keccak_prng_extract(struct KeccakPRNG *prng, void *data, size_t len); + +#endif -- 2.39.5