diff -uNr a/linux/drivers/char/random.c b/linux/drivers/char/random.c --- a/linux/drivers/char/random.c 2aa54f2437d86a9f7089fd1a87b215497b793bf4dbfcdb3fc348e921a4fe6ba5df8862fe4a27a647ab52d3789d6e6b17d50a4eeae82b36f002d4ec2dd282b8da +++ b/linux/drivers/char/random.c 578e3cebb0401af4b3fc30e1d7449bcfe67e60d581c718765783b29caa8aa6a4b54d750e94d3bc7212040f15e9f535be21fc40f13c467e576d390b7568691f9a @@ -7,9 +7,12 @@ #include #include #include +#include #include #include +#include + #define INNER_RING_SIZE (16*1024) #define OUTER_RING_SIZE (2*1024*1024) @@ -27,9 +30,80 @@ __u32 w[5]; }; + +typedef int (*Fn_Good_Hash)(const unsigned char __user *data, size_t data_len); +typedef int (*Fn_Fast_Hash)(const unsigned char __user *data, size_t data_len, size_t output_len); +typedef void (*Fn_Self_Hash)(size_t to_hash); + +static int hash_good_sha1(const unsigned char __user *data, size_t data_len); +static int hash_fast_sha1(const unsigned char __user *data, size_t data_len, size_t output_len); +static void _outer_self_hash_sha1(size_t to_hash); + +static int hash_good_chacha20(const unsigned char __user *data, size_t data_len); +static int hash_fast_chacha20(const unsigned char __user *data, size_t data_len, size_t output_len); +static void _outer_self_hash_chacha20(size_t to_hash); + +static int hash_good_serpent(const unsigned char __user *data, size_t data_len); +static int hash_fast_serpent(const unsigned char __user *data, size_t data_len, size_t output_len); +static void _outer_self_hash_serpent(size_t to_hash); + +static int hash_good_keccak(const unsigned char __user *data, size_t data_len); +static int hash_fast_keccak(const unsigned char __user *data, size_t data_len, size_t output_len); +static void _outer_self_hash_keccak(size_t to_hash); + +static unsigned current_hashing_impl = 0; +static struct hashing_impl { + const char *name; + Fn_Good_Hash fn_good; + Fn_Fast_Hash fn_fast; + Fn_Self_Hash fn_self; +} hashing_impls[] = { + {"default", hash_good_sha1, hash_fast_chacha20, _outer_self_hash_sha1}, + {"sha1", hash_good_sha1, hash_fast_sha1, _outer_self_hash_sha1}, + {"serpent", hash_good_serpent, hash_fast_serpent, _outer_self_hash_serpent}, + {"chacha20", hash_good_chacha20, hash_fast_chacha20, _outer_self_hash_chacha20}, + {"keccak", hash_good_keccak, hash_fast_keccak, _outer_self_hash_keccak}, +}; + +static int +set_hashing_impl(int val) +{ + unsigned long flags; + + if (val < 0 || val >= ARRAY_SIZE(hashing_impls)) { + printk("Fail to set hashing implementation %d\n", val); + return -EINVAL; + } + + spin_lock_irqsave(&outer_lock, flags); + current_hashing_impl = val; + spin_unlock_irqrestore(&outer_lock, flags); + printk("Set RNG hash function set to %s\n", hashing_impls[val].name); + return 0; +} + + static int hash_good(const unsigned char __user *data, size_t data_len) { + return hashing_impls[current_hashing_impl].fn_good(data, data_len); +} + +static int +hash_fast(const unsigned char __user *data, size_t data_len, size_t output_len) +{ + return hashing_impls[current_hashing_impl].fn_fast(data, data_len, output_len); +} + +static void +_outer_self_hash(size_t to_hash) +{ + return hashing_impls[current_hashing_impl].fn_self(to_hash); +} + +static int +hash_good_sha1(const unsigned char __user *data, size_t data_len) +{ union sha1state hash; __u32 workspace[SHA_WORKSPACE_WORDS]; u8 s_block[SHA_MESSAGE_BYTES]; @@ -51,17 +125,153 @@ return 0; } +static struct serpent_ctx zero_key_serpent; +static int rand_initialize(void) +{ + /* s_key can be customizable in future */ + u8 s_key[SERPENT_MAX_KEY_SIZE] = {0}; + __serpent_setkey(&zero_key_serpent, &s_key[0], sizeof(s_key)); + return 0; +} +early_initcall(rand_initialize); + static int -hash_fast(const unsigned char __user *data, size_t data_len, size_t output_len) +hash_good_serpent(const unsigned char __user *data, size_t data_len) +{ + struct serpent_ctx sctx; + u8 s_block[SERPENT_BLOCK_SIZE]; + size_t nbytes; + size_t hashed = 0; + + sctx = zero_key_serpent; + while (hashed < data_len) { + nbytes = data_len - hashed; + nbytes = min_t(size_t, sizeof(s_block), nbytes); + memset(s_block, 0, sizeof(s_block)); + if (copy_from_user(s_block, data + hashed, nbytes)) { + return -EFAULT; + } + __serpent_encrypt(&sctx, s_block, s_block); + kfifo_in(&outer_ring, &s_block[0], sizeof(s_block)); + hashed += nbytes; + } + return 0; +} + +static int +hash_good_chacha20(const unsigned char __user *data, size_t data_len) +{ + u32 ctx[16] = {0}; + u8 s_block[CHACHA20_BLOCK_SIZE]; + size_t nbytes; + size_t hashed = 0; + + while (hashed < data_len) { + nbytes = data_len - hashed; + nbytes = min_t(size_t, sizeof(s_block), nbytes); + memset(s_block, 0, sizeof(s_block)); + if (copy_from_user(s_block, data + hashed, nbytes)) { + return -EFAULT; + } + chacha20_block(&ctx[0], s_block); + kfifo_in(&outer_ring, &s_block[0], nbytes); + hashed += nbytes; + } + return 0; +} + +static int +hash_good_keccak(const unsigned char __user *data, size_t data_len) +{ + struct keccak_state kst; + u8 s_block[KECCAK_BLOCK_SIZE]; + size_t nbytes; + size_t hashed = 0; + size_t squeezed = 0; + + keccak_init(&kst); + while (hashed < data_len) { + nbytes = data_len - hashed; + nbytes = min_t(size_t, sizeof(s_block), nbytes); + if (copy_from_user(s_block, data + hashed, nbytes)) { + return -EFAULT; + } + keccak_update(&kst, &s_block[0], nbytes); + hashed += nbytes; + } + + keccak_pad(&kst); + while (squeezed < data_len) { + nbytes = data_len - squeezed; + nbytes = min_t(size_t, sizeof(s_block), nbytes); + keccak_squeeze(&kst, s_block); + kfifo_in(&outer_ring, s_block, nbytes); + squeezed += nbytes; + } + return 0; +} + + +static int +hash_fast_sha1(const unsigned char __user *data, size_t data_len, size_t output_len) +{ + union sha1state hash; + __u32 workspace[SHA_WORKSPACE_WORDS]; + u8 s_block[SHA_MESSAGE_BYTES] = {0}; + size_t nbytes; + size_t hashed = 0; + + data_len = min_t(size_t, data_len, sizeof(hash)); + if (copy_from_user(&hash.c[0], data, data_len)) { + return -EFAULT; + } + + while (hashed < output_len) { + nbytes = output_len - hashed; + nbytes = min_t(size_t, sizeof(hash), nbytes); + sha_transform(hash.w, s_block, workspace); + nbytes = kfifo_in(&outer_ring, &hash.c[0], nbytes); + hashed += nbytes; + } + return 0; +} + +static int +hash_fast_serpent(const unsigned char __user *data, size_t data_len, size_t output_len) +{ + struct serpent_ctx sctx; + u8 s_key[SERPENT_MAX_KEY_SIZE]; + u8 s_block[SERPENT_BLOCK_SIZE] = {0}; + size_t nbytes; + size_t hashed = 0; + + data_len = min_t(size_t, data_len, sizeof(s_key)); + if (copy_from_user(&s_key[0], data, data_len)) { + return -EFAULT; + } + + __serpent_setkey(&sctx, &s_key[0], sizeof(s_key)); + while (hashed < output_len) { + nbytes = output_len - hashed; + nbytes = min_t(size_t, sizeof(s_block), nbytes); + __serpent_encrypt(&sctx, s_block, s_block); + nbytes = kfifo_in(&outer_ring, &s_block[0], nbytes); + hashed += nbytes; + } + return 0; +} + + +static int +hash_fast_chacha20(const unsigned char __user *data, size_t data_len, size_t output_len) { u32 ctx[16] = {0}; u8 s_block[CHACHA20_BLOCK_SIZE] = {0}; size_t nbytes; size_t hashed = 0; - if (copy_from_user(&ctx[0], - data, - min_t(size_t, data_len, sizeof(ctx)))) { + data_len = min_t(size_t, data_len, sizeof(ctx)); + if (copy_from_user(&ctx[0], data, data_len)) { return -EFAULT; } @@ -76,6 +286,32 @@ } static int +hash_fast_keccak(const unsigned char __user *data, size_t data_len, size_t output_len) +{ + struct keccak_state kst; + u8 s_block[KECCAK_BLOCK_SIZE] = {0}; + size_t nbytes; + size_t hashed = 0; + + data_len = min_t(size_t, data_len, sizeof(s_block)); + if (copy_from_user(&s_block[0], data, data_len)) { + return -EFAULT; + } + + keccak_init(&kst); + keccak_update(&kst, s_block, data_len); + keccak_pad(&kst); + while (hashed < output_len) { + nbytes = output_len - hashed; + nbytes = min_t(size_t, sizeof(s_block), nbytes); + keccak_squeeze(&kst, s_block); + nbytes = kfifo_in(&outer_ring, s_block, nbytes); + hashed += nbytes; + } + return 0; +} + +static int transfer_to_outer(const unsigned char __user *data, ssize_t len) { int outer_ring_length; @@ -208,7 +444,7 @@ } static void -_outer_self_hash(size_t to_hash) +_outer_self_hash_sha1(size_t to_hash) { union sha1state hash; __u32 workspace[SHA_WORKSPACE_WORDS]; @@ -219,10 +455,67 @@ sha_init(hash.w); while (hashed < to_hash) { memset(s_block, 0, sizeof(s_block)); - nbytes = min_t(size_t, sizeof(s_block), (to_hash - hashed)); + nbytes = min_t(size_t, sizeof(hash), (to_hash - hashed)); peeked = kfifo_out_peek_forward(&outer_ring, s_block, nbytes); sha_transform(hash.w, s_block, workspace); - nbytes = kfifo_in(&outer_ring, &hash.c[0], sizeof(hash)); + nbytes = kfifo_in(&outer_ring, &hash.c[0], nbytes); + hashed += nbytes; + } +} + +static void +_outer_self_hash_serpent(size_t to_hash) +{ + struct serpent_ctx sctx; + u8 s_block[SERPENT_BLOCK_SIZE]; + size_t nbytes, peeked; + size_t hashed = 0; + + sctx = zero_key_serpent; + while (hashed < to_hash) { + memset(s_block, 0, sizeof(s_block)); + nbytes = min_t(size_t, sizeof(s_block), (to_hash - hashed)); + peeked = kfifo_out_peek_forward(&outer_ring, s_block, nbytes); + __serpent_encrypt(&sctx, s_block, s_block); + nbytes = kfifo_in(&outer_ring, &s_block[0], nbytes); + hashed += nbytes; + } +} + +static void +_outer_self_hash_chacha20(size_t to_hash) +{ + u32 ctx[16] = {0}; + u8 s_block[CHACHA20_BLOCK_SIZE]; + size_t nbytes, peeked; + size_t hashed = 0; + + while (hashed < to_hash) { + memset(s_block, 0, sizeof(s_block)); + nbytes = min_t(size_t, sizeof(s_block), (to_hash - hashed)); + peeked = kfifo_out_peek_forward(&outer_ring, s_block, nbytes); + chacha20_block(&ctx[0], s_block); + nbytes = kfifo_in(&outer_ring, &s_block[0], nbytes); + hashed += nbytes; + } +} + +static void +_outer_self_hash_keccak(size_t to_hash) +{ + struct keccak_state kst; + u8 s_block[KECCAK_BLOCK_SIZE]; + size_t nbytes, peeked; + size_t hashed = 0; + + keccak_init(&kst); + while (hashed < to_hash) { + nbytes = min_t(size_t, sizeof(s_block), (to_hash - hashed)); + peeked = kfifo_out_peek_forward(&outer_ring, s_block, nbytes); + keccak_update(&kst, &s_block[0], nbytes); + keccak_pad(&kst); + keccak_squeeze(&kst, s_block); + nbytes = kfifo_in(&outer_ring, s_block, nbytes); hashed += nbytes; } } @@ -334,7 +627,7 @@ static long random_ioctl(struct file *f, unsigned int cmd, unsigned long arg) { - int size, ent_count; + int size, ent_count, val; int __user *p = (int __user *)arg; int retval; @@ -346,7 +639,11 @@ return -EFAULT; return 0; case RNDADDTOENTCNT: - return 0; + /* This is temporary hijacked to set the function index */ + if (get_user(val, p)) { + return -EFAULT; + } + return set_hashing_impl(val); case RNDADDENTROPY: if (!capable(CAP_SYS_ADMIN)) return -EPERM; @@ -580,38 +877,38 @@ } static int _proc_do_entropy(struct ctl_table *table, int write, - void __user *buffer, size_t *lenp, loff_t *ppos, + void __user *buffer, size_t *lenp, loff_t *ppos, int entropy) { - struct ctl_table fake_table; - int entropy_count; + struct ctl_table fake_table; + int entropy_count; - entropy_count = entropy; + entropy_count = entropy; - fake_table.data = &entropy_count; - fake_table.maxlen = sizeof(entropy_count); + fake_table.data = &entropy_count; + fake_table.maxlen = sizeof(entropy_count); - return proc_dointvec(&fake_table, write, buffer, lenp, ppos); + return proc_dointvec(&fake_table, write, buffer, lenp, ppos); } static int proc_do_entropy(struct ctl_table *table, int write, - void __user *buffer, size_t *lenp, loff_t *ppos) + void __user *buffer, size_t *lenp, loff_t *ppos) { - int entropy_count; + int entropy_count; - entropy_count = kfifo_len(&inner_ring); + entropy_count = kfifo_len(&inner_ring); - return _proc_do_entropy(table, write, buffer, lenp, ppos, entropy_count); + return _proc_do_entropy(table, write, buffer, lenp, ppos, entropy_count); } static int proc_do_outer_entropy(struct ctl_table *table, int write, - void __user *buffer, size_t *lenp, loff_t *ppos) + void __user *buffer, size_t *lenp, loff_t *ppos) { - int entropy_count; + int entropy_count; - entropy_count = kfifo_len(&outer_ring); + entropy_count = kfifo_len(&outer_ring); - return _proc_do_entropy(table, write, buffer, lenp, ppos, entropy_count); + return _proc_do_entropy(table, write, buffer, lenp, ppos, entropy_count); } @@ -625,7 +922,7 @@ .data = 0, }, { - .procname = "outer_ring_avail", + .procname = "outer_ring_avail", .maxlen = sizeof(int), .mode = 0444, .proc_handler = proc_do_outer_entropy, diff -uNr a/linux/include/linux/keccak.h b/linux/include/linux/keccak.h --- a/linux/include/linux/keccak.h 4354064c685428bf2d59eae8154efe7e7ea9e8e99e89dc89da4b85b7a395ea05926b722923159d499d2bb2ebc4f247d88e2953df4fa7254695f6a6a46ff7d187 +++ b/linux/include/linux/keccak.h a56dca4c6e29fdaf7a8eb1c9ffe85acd2a129c76e354c683dbc8e3b147fb0fdf50efb2e01292cfe764a6bb14632b670704910230446a3507517aed3a86561ab3 @@ -18,4 +18,7 @@ int keccak_update(struct keccak_state *sctx, const u8 *data, unsigned int len); int keccak_final(struct keccak_state *sctx, u8 *out, size_t out_len); +void keccak_pad(struct keccak_state *sctx); +void keccak_squeeze(struct keccak_state *sctx, u8 *out); + #endif diff -uNr a/linux/lib/keccak.c b/linux/lib/keccak.c --- a/linux/lib/keccak.c 7015be852f730f8b94d708ebac9387bcb4f2aea43f0165c606ad070e34a7c21320e1eeb64088f807bcd38b5b2f2e0b89bc1318d6dff27f7ca71e0f9319916239 +++ b/linux/lib/keccak.c 13d2dd5330cf26dd83161289e65d6fcfff34959a9bfd20b64d1e97911e38bd8c5d1136b35136ff8fc7160438f8421eade6e940872724fba94c679a79394dcd54 @@ -119,6 +119,34 @@ return 0; } +void +keccak_pad(struct keccak_state *sctx) +{ + unsigned int i; + unsigned int inlen = sctx->partial; + + sctx->buf[inlen++] = 0x01; + memset(sctx->buf + inlen, 0, sctx->rsiz - inlen); + sctx->buf[sctx->rsiz - 1] |= 0x80; + + for (i = 0; i < sctx->rsizw; i++) { + sctx->st[i] ^= get_unaligned_le64(sctx->buf + 8 * i); + } + +} + +void +keccak_squeeze(struct keccak_state *sctx, u8 *out) +{ + unsigned int i; + u64 st[KECCAK_STATE_SIZE] = {0}; + keccakf(sctx->st); + for (i = 0; i < KECCAK_STATE_SIZE; i++) { + st[i] = cpu_to_le64(sctx->st[i]); + } + memcpy(&out, &st[0], sctx->rsiz); +} + int keccak_final(struct keccak_state *sctx, u8 *out, size_t out_len) { unsigned int i; diff -uNr a/linux/manifest b/linux/manifest --- a/linux/manifest 51a083945a2ab178d93e1954ef131d51456a9248dcf92212f14d947e08c5c1ed19105394635d96dd8fab6e8868676d753f89ad09e8b828ec1ba8eda0ab93196c +++ b/linux/manifest 9c82f5a1724b87b8e7747f83595ee607156f01ac35a00119000a87146e3ade76876c619bdf5fb927ddfa9ff8b39028dda9656c762aaebf0ef9f389883a8ca910 @@ -1,3 +1,4 @@ 592941 bvt linux-genesis Genesis Linux 4.9.95 600263 bvt linux-fuckgoats-rng Add userspace-driven RNG code 602756 bvt linux-keccak Add keccak implementation, enabled by default +603783 bvt linux-benchmark Benchmark for different implementations of fast-, good-, and self- hashing diff -uNr a/linux/tools/rng/Makefile b/linux/tools/rng/Makefile --- a/linux/tools/rng/Makefile d06ed8cc03b76de5ac4cd934e1ad009b699b79522f04ea717eab670b4aeb9a4f2ef5fb34d24e1f37fc04326dc6b7392d6cf7849031aeb77df34948c3d2c48c18 +++ b/linux/tools/rng/Makefile b7e8416b1eb373052dcd9288432aa2d7f9850eb7e66567c9045a142cd3b6ea3f4766f86c11bc1c0c5d30b27a5a37943426ae25e3c0ed62111836ff014a5e3cd7 @@ -1,4 +1,10 @@ -all: random-feeder +all: random-feeder change-rng-hashfns random-feeder: random-feeder.c $(CC) -static -O2 -Wall -o $@ $^ + +change-rng-hashfns: change-rng-hashfns.c + $(CC) -static -O2 -Wall -o $@ $^ + +clean: + rm random-feeder change-rng-hashfns diff -uNr a/linux/tools/rng/change-rng-hashfns.c b/linux/tools/rng/change-rng-hashfns.c --- a/linux/tools/rng/change-rng-hashfns.c false +++ b/linux/tools/rng/change-rng-hashfns.c ed1421919e30bc74b25e959d0d66023005b932e0c0ac67f48e174d55da76b07e0eba4daa60daafd5457fe5e0faed0684d5b17e00948edea9a767f34ab8986084 @@ -0,0 +1,33 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + int impl; + int fd; + int r; + + if (argc != 2) { + fprintf(stderr, "usage: %s impl_no\n", argv[0]); + exit(EXIT_FAILURE); + } + fd = open("/dev/random", O_RDWR); + if (fd == -1) { + fprintf(stderr, "Error opening file: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + impl = atoi(argv[1]); + r = ioctl(fd, RNDADDTOENTCNT, &impl); + if (r == -1) { + fprintf(stderr, "Error setting hash implementation: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + return 0; +} diff -uNr a/linux/tools/rng/random-feeder.c b/linux/tools/rng/random-feeder.c --- a/linux/tools/rng/random-feeder.c a4359ded8370c051ad003d4883409fba2c897480b6d424e0185545bb33dbcab47de408cb131437e159091685237324e3229584f64702e9a7eb890a074eec8523 +++ b/linux/tools/rng/random-feeder.c 924bd8018748a2591f7a86384e9591175c5e2b565d7efdebec1bf5887d8c2a89f39be9686f279a058347e9563c8d2816bf58e73095dd3a0f2c08f802dabbe74a @@ -24,6 +24,7 @@ exit(EXIT_FAILURE); } cfmakeraw(&term); + term.c_cc[VMIN] = 255; r = cfsetspeed(&term, B115200); if (r == -1) { fprintf(stderr, "Error setting serial speed: %s\n", strerror(errno)); @@ -47,9 +48,10 @@ in = init_fg(argv[1]); out = open("/dev/random", O_WRONLY); while (1) { - char buf[256]; + char buf[4096]; ssize_t nread = 0; while (nread != sizeof(buf)) { + usleep(500000); ssize_t l = read(in, &buf[nread], sizeof(buf) - nread); if (l == -1) { fprintf(stderr, "Read failed: %s\n", strerror(errno));