Re: randomness (crypto?) code example wanted please?
Taylor R Campbell <[email protected]> Sun, 25 Jun 2017 18:18:13 +0000
| Newsgroups | gmane.os.netbsd.devel.crypto,gmane.os.netbsd.devel.userlevel |
|---|---|
| Message-ID | <[email protected]> |
This is a multi-part message in MIME format. --=_QLRq+F7LuULmDL79b3iqp8BA/mvvs3yY Content-Transfer-Encoding: quoted-printable > Date: Sun, 25 Jun 2017 15:21:54 +0700 > From: Robert Elz <[email protected]> >=20 > The definition of RANDOM is that users can assign to it to set the > seed, so we need a method whereby if an integer of some arbitrary > number of bits is assigned by the user/script we can use that to > generate a repeatable sequence of "random" numbers. (Remember the shell > works with char *'s so the "integer" here is just a string of digits, > it has no inherent max value, though we can limit what we use any way we = like.) Attached is a self-contained example (a) algorithm to use that provides a strong security model like arc4random(3), (b) shrandom API you could use inside the /bin/sh implementation, and (c) trivial test program to demonstrate it. % make shrandom cc -O2 -o shrandom shrandom.c % ./shrandom deterministic 29234 32575 8779 20450 nondeterministic 855 17187 3659 9030 Should be pretty self-explanatory. Let me know if you want to extend it past potential 16-bit outputs, and if so, how far you want to extend it (32-bit? 64-bit? whatever fits in memory? -- probably not a good idea to allow setting RANDOM_BITS=3D100000000000 have the effect of DoSing $RANDOM). It is certainly not the fastest way to get this (a single ChaCha core to get 15 bits of output is tremendous overkill), but it was fifteen minutes to write from parts on hand and should be very easy to audit; it's hard to imagine that shell $RANDOM will be a bottleneck; and it's easy to increase the throughput (simply buffer outputs from ChaCha). > I am planning to extend that so that if a null string is assigned to > RANDOM (which will be its initial value at sh start) then the seed > gets fetched from /dev/urandom (or /dev/random if someone can convince > me why that would be better.) /dev/urandom is the correct choice here, or sysctl kern.arandom, but you can just get 32 bytes from arc4random(3) instead with less code and likely negligible cost. --=_QLRq+F7LuULmDL79b3iqp8BA/mvvs3yY Content-Type: text/plain; charset="ISO-8859-1"; name="shrandom" Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename="shrandom.c" /*- * Copyright (c) 2017 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Taylor R. Campbell. * * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMI= TED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICUL= AR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION 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 T= HE * POSSIBILITY OF SUCH DAMAGE. */ #include <sys/types.h> #include <sys/endian.h> #include <sha2.h> #include <string.h> /* explicit_memset -- not needed for >=3Dnetbsd7 */ void *(*volatile shrandom_explicit_memset_impl)(void *, int, size_t) =3D me= mset; void * shrandom_explicit_memset(void *buf, int c, size_t len) { return (*shrandom_explicit_memset_impl)(buf, c, len); } #define explicit_memset shrandom_explicit_memset /* ChaCha core */ #define crypto_core_OUTPUTBYTES 64 #define crypto_core_INPUTBYTES 16 #define crypto_core_KEYBYTES 32 #define crypto_core_CONSTBYTES 16 #define crypto_core_ROUNDS 20 static uint32_t rotate(uint32_t u, unsigned c) { return (u << c) | (u >> (32 - c)); } #define QUARTERROUND(a, b, c, d) do { \ (a) +=3D (b); (d) ^=3D (a); (d) =3D rotate((d), 16); \ (c) +=3D (d); (b) ^=3D (c); (b) =3D rotate((b), 12); \ (a) +=3D (b); (d) ^=3D (a); (d) =3D rotate((d), 8); \ (c) +=3D (d); (b) ^=3D (c); (b) =3D rotate((b), 7); \ } while (/*CONSTCOND*/0) const uint8_t crypto_core_constant32[16] =3D "expand 32-byte k"; static void crypto_core(uint8_t *out, const uint8_t *in, const uint8_t *k, const uint8_t *c) { uint32_t x0,x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15; uint32_t j0,j1,j2,j3,j4,j5,j6,j7,j8,j9,j10,j11,j12,j13,j14,j15; int i; j0 =3D x0 =3D le32dec(c + 0); j1 =3D x1 =3D le32dec(c + 4); j2 =3D x2 =3D le32dec(c + 8); j3 =3D x3 =3D le32dec(c + 12); j4 =3D x4 =3D le32dec(k + 0); j5 =3D x5 =3D le32dec(k + 4); j6 =3D x6 =3D le32dec(k + 8); j7 =3D x7 =3D le32dec(k + 12); j8 =3D x8 =3D le32dec(k + 16); j9 =3D x9 =3D le32dec(k + 20); j10 =3D x10 =3D le32dec(k + 24); j11 =3D x11 =3D le32dec(k + 28); j12 =3D x12 =3D le32dec(in + 0); j13 =3D x13 =3D le32dec(in + 4); j14 =3D x14 =3D le32dec(in + 8); j15 =3D x15 =3D le32dec(in + 12); for (i =3D crypto_core_ROUNDS; i > 0; i -=3D 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); } le32enc(out + 0, x0 + j0); le32enc(out + 4, x1 + j1); le32enc(out + 8, x2 + j2); le32enc(out + 12, x3 + j3); le32enc(out + 16, x4 + j4); le32enc(out + 20, x5 + j5); le32enc(out + 24, x6 + j6); le32enc(out + 28, x7 + j7); le32enc(out + 32, x8 + j8); le32enc(out + 36, x9 + j9); le32enc(out + 40, x10 + j10); le32enc(out + 44, x11 + j11); le32enc(out + 48, x12 + j12); le32enc(out + 52, x13 + j13); le32enc(out + 56, x14 + j14); le32enc(out + 60, x15 + j15); } /* ChaCha self-test */ /* * http://tools.ietf.org/html/draft-strombergson-chacha-test-vectors-00 */ __CTASSERT(crypto_core_ROUNDS =3D=3D 20); static const uint8_t crypto_core_selftest_vector[64] =3D { 0x76,0xb8,0xe0,0xad,0xa0,0xf1,0x3d,0x90, 0x40,0x5d,0x6a,0xe5,0x53,0x86,0xbd,0x28, 0xbd,0xd2,0x19,0xb8,0xa0,0x8d,0xed,0x1a, 0xa8,0x36,0xef,0xcc,0x8b,0x77,0x0d,0xc7, 0xda,0x41,0x59,0x7c,0x51,0x57,0x48,0x8d, 0x77,0x24,0xe0,0x3f,0xb8,0xd8,0x4a,0x37, 0x6a,0x43,0xb8,0xf4,0x15,0x18,0xa1,0x1c, 0xc3,0x87,0xb6,0x69,0xb2,0xee,0x65,0x86, }; static int crypto_core_selftest(void) { const uint8_t nonce[crypto_core_INPUTBYTES] =3D {0}; const uint8_t key[crypto_core_KEYBYTES] =3D {0}; uint8_t block[64]; unsigned i; crypto_core(block, nonce, key, crypto_core_constant32); for (i =3D 0; i < 64; i++) { if (block[i] !=3D crypto_core_selftest_vector[i]) return -1; } return 0; } /* shrandom: 15-bit outputs for shell $RANDOM */ struct shrandom { uint8_t shr_key[crypto_core_KEYBYTES]; }; int shrandom_selftest(void) { /* XXX exercise for reader: check known output on some fixed seed */ return crypto_core_selftest(); } void shrandom_init(struct shrandom *shr, const void *seed, size_t len) { SHA256_CTX sha256; __CTASSERT(crypto_core_KEYBYTES =3D=3D 32); SHA256_Init(&sha256); SHA256_Update(&sha256, seed, len); SHA256_Final(shr->shr_key, &sha256); } uint16_t shrandom_next(struct shrandom *shr) { const uint8_t input[crypto_core_INPUTBYTES] =3D {0}; uint8_t output[crypto_core_OUTPUTBYTES]; uint16_t r; /* output :=3D chacha(output, input, key, constant) */ crypto_core(output, input, shr->shr_key, crypto_core_constant32); /* key :=3D output[0..31] */ __CTASSERT(crypto_core_KEYBYTES <=3D crypto_core_OUTPUTBYTES + 2); memcpy(shr->shr_key, output, crypto_core_KEYBYTES); /* r :=3D output[32] + 2^16 output[33] */ r =3D le16dec(output + crypto_core_KEYBYTES); /* erase key */ explicit_memset(output, 0, sizeof output); return r & 0x7fff; } void shrandom_fini(struct shrandom *shr) { explicit_memset(shr, 0, sizeof(*shr)); } /* Test program */ #include <stdio.h> #include <stdlib.h> int main(int argc, char **argv) { uint8_t buf[32]; struct shrandom shr; unsigned i; (void)argc; (void)argv; if (shrandom_selftest() !=3D 0) abort(); printf("deterministic\n"); shrandom_init(&shr, "hello", 6); for (i =3D 0; i < 4; i++) printf("%hu%s", shrandom_next(&shr), i < 3? " " : "\n"); shrandom_fini(&shr); printf("nondeterministic\n"); arc4random_buf(&buf, sizeof buf); shrandom_init(&shr, buf, sizeof buf); for (i =3D 0; i < 4; i++) printf("%hu%s", shrandom_next(&shr), i < 3? " " : "\n"); shrandom_fini(&shr); return 0; } --=_QLRq+F7LuULmDL79b3iqp8BA/mvvs3yY--