diff --git a/commands/Kconfig b/commands/Kconfig index bc0885c..ab8e09a 100644 --- a/commands/Kconfig +++ b/commands/Kconfig @@ -2115,6 +2115,12 @@ help decode spd eeprom +config CMD_SEED + tristate + prompt "seed" + help + Seed the pseudo random number generator (PRNG) + # end Miscellaneous commands endmenu diff --git a/commands/Makefile b/commands/Makefile index 601f15f..ab59021 100644 --- a/commands/Makefile +++ b/commands/Makefile @@ -120,3 +120,4 @@ obj-$(CONFIG_CMD_SPD_DECODE) += spd_decode.o obj-$(CONFIG_CMD_MMC_EXTCSD) += mmc_extcsd.o obj-$(CONFIG_CMD_NAND_BITFLIP) += nand-bitflip.o +obj-$(CONFIG_CMD_SEED) += seed.o diff --git a/commands/seed.c b/commands/seed.c new file mode 100644 index 0000000..e378cd7 --- /dev/null +++ b/commands/seed.c @@ -0,0 +1,44 @@ +/* + * (c) 2017 Oleksij Rempel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include + +static int do_seed(int argc, char *argv[]) +{ + if (argc < 2) + return COMMAND_ERROR_USAGE; + + if (isdigit(*argv[1])) { + srand(simple_strtoul(argv[1], NULL, 0)); + return 0; + } + + printf("numerical parameter expected\n"); + return 1; +} + +BAREBOX_CMD_HELP_START(seed) +BAREBOX_CMD_HELP_TEXT("Seed the pseudo random number generator") +BAREBOX_CMD_HELP_END + +BAREBOX_CMD_START(seed) + .cmd = do_seed, + BAREBOX_CMD_DESC("seed the PRNG") + BAREBOX_CMD_OPTS("VALUE") + BAREBOX_CMD_GROUP(CMD_GRP_MISC) + BAREBOX_CMD_HELP(cmd_seed_help) +BAREBOX_CMD_END diff --git a/commands/stddev.c b/commands/stddev.c index 318d057..93da2c7 100644 --- a/commands/stddev.c +++ b/commands/stddev.c @@ -17,6 +17,7 @@ #include #include +#include static ssize_t zero_read(struct cdev *cdev, void *buf, size_t count, loff_t offset, ulong flags) { @@ -100,3 +101,31 @@ } device_initcall(null_init); + +static ssize_t prng_read(struct cdev *cdev, void *buf, size_t count, loff_t offset, ulong flags) +{ + get_random_bytes(buf, count); + return count; +} + +static struct file_operations prngops = { + .read = prng_read, + .lseek = dev_lseek_default, +}; + +static int prng_init(void) +{ + struct cdev *cdev; + + cdev = xzalloc(sizeof (*cdev)); + + cdev->name = "prng"; + cdev->flags = DEVFS_IS_CHARACTER_DEV; + cdev->ops = &prngops; + + devfs_create(cdev); + + return 0; +} + +device_initcall(prng_init); diff --git a/common/password.c b/common/password.c index d52b746..74d328f 100644 --- a/common/password.c +++ b/common/password.c @@ -365,7 +365,11 @@ char *salt = passwd_sum; int keylen = PBKDF2_LENGTH - PBKDF2_SALT_LEN; - get_random_bytes(passwd_sum, PBKDF2_SALT_LEN); + ret = get_crypto_bytes(passwd_sum, PBKDF2_SALT_LEN); + if (ret) { + pr_err("Can't get random numbers\n"); + return ret; + } ret = pkcs5_pbkdf2_hmac_sha1(passwd, length, salt, PBKDF2_SALT_LEN, PBKDF2_COUNT, keylen, key); diff --git a/drivers/Kconfig b/drivers/Kconfig index bf31115..44159f8 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -22,6 +22,7 @@ source "drivers/input/Kconfig" source "drivers/watchdog/Kconfig" source "drivers/pwm/Kconfig" +source "drivers/hw_random/Kconfig" source "drivers/dma/Kconfig" source "drivers/gpio/Kconfig" source "drivers/w1/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 0fadf4e..d46cc28 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -22,6 +22,7 @@ obj-y += dma/ obj-y += watchdog/ obj-y += gpio/ +obj-$(CONFIG_HWRNG) += hw_random/ obj-$(CONFIG_OFTREE) += of/ obj-$(CONFIG_W1) += w1/ obj-y += pinctrl/ diff --git a/drivers/crypto/caam/Kconfig b/drivers/crypto/caam/Kconfig index cf05d1c..2ab509d 100644 --- a/drivers/crypto/caam/Kconfig +++ b/drivers/crypto/caam/Kconfig @@ -29,6 +29,7 @@ config CRYPTO_DEV_FSL_CAAM_RNG bool "Register caam RNG device" depends on CRYPTO_DEV_FSL_CAAM + depends on HWRNG default y help Selecting this will register the SEC4 hardware rng. diff --git a/drivers/crypto/caam/caamrng.c b/drivers/crypto/caam/caamrng.c index 0fef171..31a9273 100644 --- a/drivers/crypto/caam/caamrng.c +++ b/drivers/crypto/caam/caamrng.c @@ -35,6 +35,7 @@ #include #include #include +#include #include "regs.h" #include "intern.h" @@ -54,7 +55,7 @@ /* Buffer, its dma address and lock */ struct buf_data { - u8 buf[RN_BUF_SIZE]; + u8 *buf; dma_addr_t addr; u32 hw_desc[DESC_JOB_O_LEN]; #define BUF_NOT_EMPTY 0 @@ -71,7 +72,7 @@ unsigned int cur_buf_idx; int current_buf; struct buf_data bufs[2]; - struct cdev cdev; + struct hwrng rng; }; static struct caam_rng_ctx *rng_ctx; @@ -116,8 +117,9 @@ return err; } -static int caam_read(struct caam_rng_ctx *ctx, void *data, size_t max, bool wait) +static int caam_read(struct hwrng *rng, void *data, size_t max, bool wait) { + struct caam_rng_ctx *ctx = container_of(rng, struct caam_rng_ctx, rng); struct buf_data *bd = &ctx->bufs[ctx->current_buf]; int next_buf_idx, copied_idx; int err; @@ -162,7 +164,7 @@ dev_dbg(ctx->jrdev, "switched to buffer %d\n", ctx->current_buf); /* since there already is some data read, don't wait */ - return copied_idx + caam_read(ctx, data + copied_idx, + return copied_idx + caam_read(rng, data + copied_idx, max - copied_idx, false); } @@ -216,6 +218,8 @@ struct buf_data *bd = &ctx->bufs[buf_id]; int err; + bd->buf = dma_alloc(RN_BUF_SIZE); + err = rng_create_job_desc(ctx, buf_id); if (err) return err; @@ -248,29 +252,6 @@ return 0; } -static ssize_t random_read(struct cdev *cdev, void *buf, size_t count, - loff_t offset, ulong flags) -{ - struct caam_rng_ctx *ctx = container_of(cdev, struct caam_rng_ctx, cdev); - - return caam_read(ctx, buf, count, true); -} - -static struct file_operations randomops = { - .read = random_read, - .lseek = dev_lseek_default, -}; - -static int caam_init_devrandom(struct caam_rng_ctx *ctx, struct device_d *dev) -{ - ctx->cdev.name = "hwrng"; - ctx->cdev.flags = DEVFS_IS_CHARACTER_DEV; - ctx->cdev.ops = &randomops; - ctx->cdev.dev = dev; - - return devfs_create(&ctx->cdev); -} - int caam_rng_probe(struct device_d *dev, struct device_d *jrdev) { int err; @@ -281,9 +262,14 @@ if (err) return err; - err = caam_init_devrandom(rng_ctx, dev); - if (err) + rng_ctx->rng.name = dev->name; + rng_ctx->rng.read = caam_read; + + err = hwrng_register(dev, &rng_ctx->rng); + if (err) { + dev_err(dev, "rng-caam registering failed (%d)\n", err); return err; + } dev_info(dev, "registering rng-caam\n"); diff --git a/drivers/hw_random/Kconfig b/drivers/hw_random/Kconfig new file mode 100644 index 0000000..807fcad --- /dev/null +++ b/drivers/hw_random/Kconfig @@ -0,0 +1,6 @@ +menuconfig HWRNG + bool "HWRNG Support" + help + Support for HWRNG(Hardware Random Number Generator) devices. + + If unsure, say no. diff --git a/drivers/hw_random/Makefile b/drivers/hw_random/Makefile new file mode 100644 index 0000000..15307b1 --- /dev/null +++ b/drivers/hw_random/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_HWRNG) += core.o diff --git a/drivers/hw_random/core.c b/drivers/hw_random/core.c new file mode 100644 index 0000000..ef2a988 --- /dev/null +++ b/drivers/hw_random/core.c @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2016 Pengutronix, Steffen Trumtrar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * derived from Linux kernel drivers/char/hw_random/core.c + */ + +#include +#include +#include + +static LIST_HEAD(hwrngs); + +#define RNG_BUFFER_SIZE 32 + +int hwrng_get_data(struct hwrng *rng, void *buffer, size_t size, int wait) +{ + return rng->read(rng, buffer, size, wait); +} + +static int hwrng_init(struct hwrng *rng) +{ + int ret = 0; + + if (rng->init) + ret = rng->init(rng); + + if (!ret) + list_add_tail(&rng->list, &hwrngs); + + return ret; +} + +static ssize_t rng_dev_read(struct cdev *cdev, void *buf, size_t size, + loff_t offset, unsigned long flags) +{ + struct hwrng *rng = container_of(cdev, struct hwrng, cdev); + size_t count = size; + ssize_t cur = 0; + int len; + + memset(buf, 0, size); + + while (count) { + int max = min(count, (size_t)RNG_BUFFER_SIZE); + len = hwrng_get_data(rng, rng->buf, max, true); + if (len < 0) { + cur = len; + break; + } + + memcpy(buf + cur, rng->buf, len); + + count -= len; + cur += len; + } + + return cur; +} + +static struct file_operations rng_chrdev_ops = { + .read = rng_dev_read, + .lseek = dev_lseek_default, +}; + +static int hwrng_register_cdev(struct hwrng *rng) +{ + struct device_d *dev = rng->dev; + const char *alias; + char *devname; + int err; + + alias = of_alias_get(dev->device_node); + if (alias) { + devname = xstrdup(alias); + } else { + err = cdev_find_free_index("hwrng"); + if (err < 0) { + dev_err(dev, "no index found to name device\n"); + return err; + } + devname = xasprintf("hwrng%d", err); + } + + rng->cdev.name = devname; + rng->cdev.flags = DEVFS_IS_CHARACTER_DEV; + rng->cdev.ops = &rng_chrdev_ops; + rng->cdev.dev = rng->dev; + + return devfs_create(&rng->cdev); +} + +struct hwrng *hwrng_get_first(void) +{ + if (list_empty(&hwrngs)) + return ERR_PTR(-ENODEV); + else + return list_first_entry(&hwrngs, struct hwrng, list); +} + +int hwrng_register(struct device_d *dev, struct hwrng *rng) +{ + int err; + + if (rng->name == NULL || rng->read == NULL) + return -EINVAL; + + rng->buf = xzalloc(RNG_BUFFER_SIZE); + rng->dev = dev; + + err = hwrng_init(rng); + if (err) { + free(rng->buf); + return err; + } + + err = hwrng_register_cdev(rng); + if (err) + free(rng->buf); + + return err; +} diff --git a/include/linux/hw_random.h b/include/linux/hw_random.h new file mode 100644 index 0000000..bae4421 --- /dev/null +++ b/include/linux/hw_random.h @@ -0,0 +1,47 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef LINUX_HWRANDOM_H_ +#define LINUX_HWRANDOM_H_ + +#include + +/** + * struct hwrng - Hardware Random Number Generator driver + * @name: Unique RNG name. + * @init: Initialization callback (can be NULL). + * @read: New API. drivers can fill up to max bytes of data + * into the buffer. The buffer is aligned for any type. + */ +struct hwrng { + const char *name; + int (*init)(struct hwrng *rng); + int (*read)(struct hwrng *rng, void *data, size_t max, bool wait); + + struct list_head list; + + struct cdev cdev; + struct device_d *dev; + void *buf; +}; + +/* Register a new Hardware Random Number Generator driver. */ +int hwrng_register(struct device_d *dev, struct hwrng *rng); +int hwrng_get_data(struct hwrng *rng, void *buffer, size_t size, int wait); + +#ifdef CONFIG_HWRNG +struct hwrng *hwrng_get_first(void); +#else +static inline struct hwrng *hwrng_get_first(void) { return ERR_PTR(-ENODEV); }; +#endif + +#endif /* LINUX_HWRANDOM_H_ */ diff --git a/include/stdlib.h b/include/stdlib.h index f318506..ee3f229 100644 --- a/include/stdlib.h +++ b/include/stdlib.h @@ -13,6 +13,7 @@ /* fill a buffer with pseudo-random data */ void get_random_bytes(void *buf, int len); +int get_crypto_bytes(void *buf, int len); static inline u32 random32(void) { diff --git a/lib/Kconfig b/lib/Kconfig index 8a94ce0..9562b1b 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -90,6 +90,15 @@ transferring packets over serial links described in RFC916. This implementation is used for controlling barebox over serial ports. +config ALLOW_PRNG_FALLBACK + bool "Allow fallback to PRNG if HWRNG not available." + help + WARNING: it is not secure!! + + get_crypto_bytes() users like cmd_password relay on HWRNG. If HWRNG is not + available and this option is disabled, cmd_password will fail. + Enable it on your own risk. + source lib/gui/Kconfig source lib/fonts/Kconfig diff --git a/lib/random.c b/lib/random.c index 210fea9..759271f 100644 --- a/lib/random.c +++ b/lib/random.c @@ -1,5 +1,6 @@ #include #include +#include static unsigned int random_seed; @@ -18,6 +19,11 @@ random_seed = seed; } +/** + * get_random_bytes - get pseudo random numbers. + * This interface can be good enough to generate MAC address + * or use for NAND test. + */ void get_random_bytes(void *_buf, int len) { char *buf = _buf; @@ -25,3 +31,49 @@ while (len--) *buf++ = rand() % 256; } + +/** + * get_crypto_bytes - get random numbers suitable for cryptographic needs. + */ +static int _get_crypto_bytes(void *buf, int len) +{ + struct hwrng *rng; + + rng = hwrng_get_first(); + if (IS_ERR(rng)) + return PTR_ERR(rng); + + while (len) { + int bytes = hwrng_get_data(rng, buf, len, true); + if (!bytes) + return -ENOMEDIUM; + + if (bytes < 0) + return bytes; + + len -= bytes; + buf = buf + bytes; + } + + return 0; +} + +int get_crypto_bytes(void *buf, int len) +{ + int err; + + err = _get_crypto_bytes(buf, len); + if (!err) + return 0; + + if (!IS_ENABLED(CONFIG_ALLOW_PRNG_FALLBACK)) { + pr_err("error: no HWRNG available!\n"); + return err; + } + + pr_warn("warning: falling back to Pseudo RNG source!\n"); + + get_random_bytes(buf, len); + + return 0; +}