diff options
author | Theodore Ts'o <tytso@mit.edu> | 2016-05-04 13:29:18 -0400 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2016-07-03 00:58:01 -0400 |
commit | c92e040d575a7389d72e7e6f25e2033bfb867f8b (patch) | |
tree | 688c89943d4c14e46d678c6540fc0658debdbef2 | |
parent | 1e7f583af67be4ff091d0aeb863c649efd7a9112 (diff) |
random: add backtracking protection to the CRNG
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
-rw-r--r-- | drivers/char/random.c | 54 |
1 files changed, 49 insertions, 5 deletions
diff --git a/drivers/char/random.c b/drivers/char/random.c index 2a30d9718a1b..783dee11cdc9 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c | |||
@@ -438,7 +438,8 @@ static int crng_init_cnt = 0; | |||
438 | #define CRNG_INIT_CNT_THRESH (2*CHACHA20_KEY_SIZE) | 438 | #define CRNG_INIT_CNT_THRESH (2*CHACHA20_KEY_SIZE) |
439 | static void _extract_crng(struct crng_state *crng, | 439 | static void _extract_crng(struct crng_state *crng, |
440 | __u8 out[CHACHA20_BLOCK_SIZE]); | 440 | __u8 out[CHACHA20_BLOCK_SIZE]); |
441 | static void extract_crng(__u8 out[CHACHA20_BLOCK_SIZE]); | 441 | static void _crng_backtrack_protect(struct crng_state *crng, |
442 | __u8 tmp[CHACHA20_BLOCK_SIZE], int used); | ||
442 | static void process_random_ready_list(void); | 443 | static void process_random_ready_list(void); |
443 | 444 | ||
444 | /********************************************************************** | 445 | /********************************************************************** |
@@ -826,8 +827,11 @@ static void crng_reseed(struct crng_state *crng, struct entropy_store *r) | |||
826 | num = extract_entropy(r, &buf, 32, 16, 0); | 827 | num = extract_entropy(r, &buf, 32, 16, 0); |
827 | if (num == 0) | 828 | if (num == 0) |
828 | return; | 829 | return; |
829 | } else | 830 | } else { |
830 | _extract_crng(&primary_crng, buf.block); | 831 | _extract_crng(&primary_crng, buf.block); |
832 | _crng_backtrack_protect(&primary_crng, buf.block, | ||
833 | CHACHA20_KEY_SIZE); | ||
834 | } | ||
831 | spin_lock_irqsave(&primary_crng.lock, flags); | 835 | spin_lock_irqsave(&primary_crng.lock, flags); |
832 | for (i = 0; i < 8; i++) { | 836 | for (i = 0; i < 8; i++) { |
833 | unsigned long rv; | 837 | unsigned long rv; |
@@ -889,9 +893,46 @@ static void extract_crng(__u8 out[CHACHA20_BLOCK_SIZE]) | |||
889 | _extract_crng(crng, out); | 893 | _extract_crng(crng, out); |
890 | } | 894 | } |
891 | 895 | ||
896 | /* | ||
897 | * Use the leftover bytes from the CRNG block output (if there is | ||
898 | * enough) to mutate the CRNG key to provide backtracking protection. | ||
899 | */ | ||
900 | static void _crng_backtrack_protect(struct crng_state *crng, | ||
901 | __u8 tmp[CHACHA20_BLOCK_SIZE], int used) | ||
902 | { | ||
903 | unsigned long flags; | ||
904 | __u32 *s, *d; | ||
905 | int i; | ||
906 | |||
907 | used = round_up(used, sizeof(__u32)); | ||
908 | if (used + CHACHA20_KEY_SIZE > CHACHA20_BLOCK_SIZE) { | ||
909 | extract_crng(tmp); | ||
910 | used = 0; | ||
911 | } | ||
912 | spin_lock_irqsave(&crng->lock, flags); | ||
913 | s = (__u32 *) &tmp[used]; | ||
914 | d = &crng->state[4]; | ||
915 | for (i=0; i < 8; i++) | ||
916 | *d++ ^= *s++; | ||
917 | spin_unlock_irqrestore(&crng->lock, flags); | ||
918 | } | ||
919 | |||
920 | static void crng_backtrack_protect(__u8 tmp[CHACHA20_BLOCK_SIZE], int used) | ||
921 | { | ||
922 | struct crng_state *crng = NULL; | ||
923 | |||
924 | #ifdef CONFIG_NUMA | ||
925 | if (crng_node_pool) | ||
926 | crng = crng_node_pool[numa_node_id()]; | ||
927 | if (crng == NULL) | ||
928 | #endif | ||
929 | crng = &primary_crng; | ||
930 | _crng_backtrack_protect(crng, tmp, used); | ||
931 | } | ||
932 | |||
892 | static ssize_t extract_crng_user(void __user *buf, size_t nbytes) | 933 | static ssize_t extract_crng_user(void __user *buf, size_t nbytes) |
893 | { | 934 | { |
894 | ssize_t ret = 0, i; | 935 | ssize_t ret = 0, i = CHACHA20_BLOCK_SIZE; |
895 | __u8 tmp[CHACHA20_BLOCK_SIZE]; | 936 | __u8 tmp[CHACHA20_BLOCK_SIZE]; |
896 | int large_request = (nbytes > 256); | 937 | int large_request = (nbytes > 256); |
897 | 938 | ||
@@ -916,6 +957,7 @@ static ssize_t extract_crng_user(void __user *buf, size_t nbytes) | |||
916 | buf += i; | 957 | buf += i; |
917 | ret += i; | 958 | ret += i; |
918 | } | 959 | } |
960 | crng_backtrack_protect(tmp, i); | ||
919 | 961 | ||
920 | /* Wipe data just written to memory */ | 962 | /* Wipe data just written to memory */ |
921 | memzero_explicit(tmp, sizeof(tmp)); | 963 | memzero_explicit(tmp, sizeof(tmp)); |
@@ -1473,8 +1515,10 @@ void get_random_bytes(void *buf, int nbytes) | |||
1473 | if (nbytes > 0) { | 1515 | if (nbytes > 0) { |
1474 | extract_crng(tmp); | 1516 | extract_crng(tmp); |
1475 | memcpy(buf, tmp, nbytes); | 1517 | memcpy(buf, tmp, nbytes); |
1476 | memzero_explicit(tmp, nbytes); | 1518 | crng_backtrack_protect(tmp, nbytes); |
1477 | } | 1519 | } else |
1520 | crng_backtrack_protect(tmp, CHACHA20_BLOCK_SIZE); | ||
1521 | memzero_explicit(tmp, sizeof(tmp)); | ||
1478 | } | 1522 | } |
1479 | EXPORT_SYMBOL(get_random_bytes); | 1523 | EXPORT_SYMBOL(get_random_bytes); |
1480 | 1524 | ||