LCOV - code coverage report
Current view: top level - src/util - random.c (source / functions) Hit Total Coverage
Test: passgen-test.info Lines: 206 208 99.0 %
Date: 2024-05-03 06:05:14 Functions: 35 35 100.0 %

          Line data    Source code
       1             : #include "passgen/util/random.h"
       2             : #include "passgen/assert.h"
       3             : #include <stdlib.h>
       4             : #include <string.h>
       5             : 
       6          25 : static bool _strprefix(const char *prefix, const char *string) {
       7          25 :     size_t prefix_len = strlen(prefix);
       8         104 :     for(size_t i = 0; i < prefix_len; i++) {
       9          93 :         if(prefix[i] != string[i]) {
      10          14 :             return false;
      11             :         }
      12             :     }
      13          11 :     return true;
      14             : }
      15             : 
      16             : #ifdef __linux__
      17             : #define PASSGEN_RANDOM_HAVE_SYSTEM
      18             : #include <sys/random.h>
      19             : 
      20       57096 : size_t passgen_random_read_system(void *dest, size_t size, void *data) {
      21             :     (void) data;
      22       57096 :     return getrandom(dest, size, 0);
      23             : }
      24             : #endif
      25             : 
      26             : #ifdef __APPLE__
      27             : #define PASSGEN_RANDOM_HAVE_SYSTEM
      28             : 
      29             : size_t passgen_random_read_system(void *dest, size_t size, void *data) {
      30             :     (void) data;
      31             :     arc4random_buf(dest, size);
      32             :     return size;
      33             : }
      34             : #endif
      35             : 
      36             : #ifndef PASSGEN_RANDOM_HAVE_SYSTEM
      37             : static const char *passgen_random_default_device = "/dev/urandom";
      38             : #endif
      39             : 
      40           5 : size_t passgen_random_read_file(void *dest, size_t size, void *data) {
      41           5 :     return fread(dest, 1, size, data);
      42             : }
      43             : 
      44           5 : void passgen_random_close_file(void *data) {
      45           5 :     fclose(data);
      46           5 : }
      47             : 
      48          17 : void passgen_random_close_system(void *data) {
      49             :     (void) data;
      50          17 : }
      51             : 
      52       39425 : static uint64_t xorshift64(uint64_t *state) {
      53       39425 :     uint64_t x = *state;
      54       39425 :     x ^= x << 13;
      55       39425 :     x ^= x >> 7;
      56       39425 :     x ^= x << 17;
      57       39425 :     return *state = x;
      58             : }
      59             : 
      60         308 : size_t passgen_random_read_xorshift(void *dest, size_t size, void *data) {
      61         308 :     size_t written = 0;
      62             :     uint64_t result;
      63             : 
      64             :     // fill all whole uint64 blocks
      65       39732 :     while((size - written) >= sizeof(result)) {
      66       39424 :         result = xorshift64(data);
      67       39424 :         memcpy(dest + written, &result, sizeof(result));
      68       39424 :         written += sizeof(result);
      69             :     }
      70             : 
      71             :     // maybe fill the last incomplete block
      72         308 :     if(size != written) {
      73           1 :         result = xorshift64(data);
      74           1 :         memcpy(dest + written, &result, size - written);
      75           1 :         written += size - written;
      76             :     }
      77             : 
      78         308 :     return written;
      79             : }
      80             : 
      81          17 : void passgen_random_close_xorshift(void *data) {
      82          17 :     free(data);
      83          17 : }
      84             : 
      85           1 : passgen_random *passgen_random_new_xorshift(uint64_t seed) {
      86           1 :     passgen_random *random = malloc(sizeof(passgen_random));
      87           1 :     if(!random) return NULL;
      88             : 
      89           1 :     return passgen_random_open_xorshift(random, seed);
      90             : }
      91             : 
      92       57411 : void passgen_random_reload(passgen_random *random) {
      93       57411 :     passgen_assert(random != NULL);
      94             : 
      95             :     // read random data.
      96             :     size_t bytes =
      97       57411 :         random->read(&random->buffer, sizeof(random->buffer), random->data);
      98             :     (void) bytes;
      99             : 
     100             :     // make sure we've read enough.
     101       57411 :     passgen_assert(bytes == sizeof(random->buffer));
     102             : 
     103             :     // reset position in ring buffer.
     104       57411 :     random->pos = 0;
     105       57411 : }
     106             : 
     107             : passgen_random *
     108          17 : passgen_random_open_xorshift(passgen_random *random, uint64_t seed) {
     109             :     // create state
     110          17 :     uint64_t *state = malloc(sizeof(uint64_t));
     111          17 :     if(!state) return NULL;
     112             : 
     113             :     // initialise state
     114          17 :     *state = seed;
     115             : 
     116          17 :     random->data = state;
     117          17 :     random->read = passgen_random_read_xorshift;
     118          17 :     random->close = passgen_random_close_xorshift;
     119          17 :     passgen_random_reload(random);
     120             : 
     121          17 :     return random;
     122             : }
     123             : 
     124           3 : size_t passgen_random_read_zero(void *dest, size_t size, void *data) {
     125             :     (void) data;
     126           3 :     memset(dest, 0, size);
     127           3 :     return size;
     128             : }
     129             : 
     130           3 : void passgen_random_close_zero(void *data) {
     131             :     (void) data;
     132           3 : }
     133             : 
     134           3 : passgen_random *passgen_random_open_zero(passgen_random *random) {
     135           3 :     random->data = NULL;
     136           3 :     random->read = passgen_random_read_zero;
     137           3 :     random->close = passgen_random_close_zero;
     138           3 :     passgen_random_reload(random);
     139           3 :     return random;
     140             : }
     141             : 
     142           1 : passgen_random *passgen_random_new_zero() {
     143           1 :     passgen_random *random = malloc(sizeof(passgen_random));
     144           1 :     if(!random) return NULL;
     145           1 :     return passgen_random_open_zero(random);
     146             : }
     147             : 
     148             : #ifdef PASSGEN_RANDOM_HAVE_SYSTEM
     149          53 : passgen_random *passgen_random_open_system(passgen_random *random) {
     150          53 :     random->data = NULL;
     151          53 :     random->read = passgen_random_read_system;
     152          53 :     random->close = passgen_random_close_system;
     153          53 :     passgen_random_reload(random);
     154          53 :     return random;
     155             : }
     156             : #else
     157             : passgen_random *passgen_random_open_system(passgen_random *random) {
     158             :     (void) random;
     159             : 
     160             :     return NULL;
     161             : }
     162             : #endif
     163             : 
     164    11645391 : void passgen_random_read(passgen_random *random, void *data, size_t bytes) {
     165    11645391 :     if(bytes <= sizeof(random->buffer)) {
     166             :         // maximum bytes we can have right now
     167    11645390 :         size_t left = sizeof(random->buffer) - random->pos;
     168             : 
     169    11645390 :         if(bytes < left) {
     170             :             // if we have enough, just copy it over.
     171    11588057 :             memcpy(data, &random->buffer[random->pos], bytes);
     172    11588057 :             random->pos += bytes;
     173             :         } else {
     174             :             // if we don't have enough, copy over whatever we have and
     175             :             // reload.
     176       57333 :             memcpy(data, random->buffer, left);
     177       57333 :             passgen_random_reload(random);
     178             : 
     179             :             // if there's more to be read, recurse.
     180       57333 :             if(bytes != left) {
     181          20 :                 passgen_random_read(random, data + left, bytes - left);
     182             :             }
     183             :         }
     184             :     } else {
     185           1 :         size_t ret = random->read(data, bytes, random->data);
     186           1 :         if(ret != bytes) {
     187           0 :             fprintf(
     188             :                 stderr,
     189             :                 "Error reading from randomness source: trying to read %zu "
     190             :                 "bytes but got %zu",
     191             :                 bytes,
     192             :                 ret);
     193           0 :             abort();
     194             :         }
     195             :     }
     196    11645391 : }
     197             : 
     198          13 : passgen_random *passgen_random_new(const char *desc) {
     199          13 :     passgen_random *random = malloc(sizeof(passgen_random));
     200          13 :     passgen_assert(random);
     201             : 
     202          13 :     passgen_random *ret = passgen_random_open(random, desc);
     203          13 :     if(!ret) {
     204           5 :         free(random);
     205             :     }
     206             : 
     207          13 :     return ret;
     208             : }
     209             : 
     210           2 : passgen_random *passgen_random_new_path(const char *path) {
     211           2 :     passgen_random *random = malloc(sizeof(passgen_random));
     212           2 :     if(!random) return NULL;
     213             : 
     214           2 :     if(!passgen_random_open_path(random, path)) {
     215           1 :         free(random);
     216           1 :         return NULL;
     217             :     }
     218             : 
     219           1 :     return random;
     220             : }
     221             : 
     222           1 : passgen_random *passgen_random_new_file(FILE *file) {
     223           1 :     passgen_random *random = malloc(sizeof(passgen_random));
     224           1 :     if(!random) return NULL;
     225             : 
     226           1 :     passgen_random_open_file(random, file);
     227             : 
     228           1 :     return random;
     229             : }
     230             : 
     231             : passgen_random *
     232          17 : passgen_random_open_parse(passgen_random *random, const char *desc) {
     233          17 :     if(strcmp("zero", desc) == 0) {
     234           2 :         return passgen_random_open_zero(random);
     235             :     }
     236             : 
     237             :     // check if we should read randomness from this file
     238          15 :     if(_strprefix("file:", desc)) {
     239           5 :         return passgen_random_open_path(random, &desc[5]);
     240             :     }
     241             : 
     242             :     // check if we should use the xorshift PRNG with the given seed
     243          10 :     if(_strprefix("xorshift:", desc)) {
     244           6 :         const char *seed_str = &desc[9];
     245           6 :         if(*seed_str == 0) {
     246           2 :             return NULL;
     247             :         }
     248             : 
     249           4 :         uint64_t seed = atoll(seed_str);
     250           4 :         if(seed == 0) {
     251           2 :             return NULL;
     252             :         }
     253             : 
     254           2 :         return passgen_random_open_xorshift(random, seed);
     255             :     }
     256             : 
     257             :     // check if we should use the system default
     258           4 :     if(0 == strcmp(desc, "system")) {
     259           2 :         return passgen_random_open_system(random);
     260             :     }
     261             : 
     262           2 :     return NULL;
     263             : }
     264             : 
     265          68 : passgen_random *passgen_random_open(passgen_random *random, const char *desc) {
     266          68 :     if(desc) {
     267          17 :         return passgen_random_open_parse(random, desc);
     268             :     }
     269             : 
     270             : #ifdef PASSGEN_RANDOM_HAVE_SYSTEM
     271          51 :     return passgen_random_open_system(random);
     272             : #else
     273             :     passgen_random *rand =
     274             :         passgen_random_open_path(random, passgen_random_default_device);
     275             :     if(!rand) {
     276             :         fprintf(
     277             :             stderr,
     278             :             "error: cannot open system randomness device '%s'\n",
     279             :             passgen_random_default_device);
     280             :     }
     281             :     return rand;
     282             : #endif
     283             : }
     284             : 
     285             : passgen_random *
     286           9 : passgen_random_open_path(passgen_random *random, const char *path) {
     287           9 :     FILE *device = fopen(path, "r");
     288           9 :     if(!device) {
     289           5 :         return NULL;
     290             :     }
     291             : 
     292           4 :     return passgen_random_open_file(random, device);
     293             : 
     294             :     passgen_random_reload(random);
     295             : 
     296             :     return random;
     297             : }
     298             : 
     299           5 : passgen_random *passgen_random_open_file(passgen_random *random, FILE *file) {
     300           5 :     random->data = file;
     301           5 :     random->read = passgen_random_read_file;
     302           5 :     random->close = passgen_random_close_file;
     303           5 :     passgen_random_reload(random);
     304           5 :     return random;
     305             : }
     306             : 
     307          42 : void passgen_random_close(passgen_random *random) {
     308             :     // close randomness source
     309          42 :     random->close(random->data);
     310             : 
     311             :     // reset members to prevent accidental use-after-free
     312          42 :     random->data = NULL;
     313          42 :     random->read = NULL;
     314          42 :     random->close = NULL;
     315             : 
     316             :     // overwrite random buffer with zeroes to remove sensitive data
     317          42 :     memset(random->buffer, 0, PASSGEN_RANDOM_BUFFER_LENGTH);
     318          42 : }
     319             : 
     320          12 : void passgen_random_free(passgen_random *random) {
     321          12 :     passgen_random_close(random);
     322          12 :     free(random);
     323          12 : }
     324             : 
     325      651623 : inline uint8_t passgen_random_u8(passgen_random *random) {
     326             :     uint8_t data;
     327      651623 :     passgen_random_read(random, &data, sizeof(data));
     328      651623 :     return data;
     329             : }
     330             : 
     331     2540288 : inline uint16_t passgen_random_u16(passgen_random *random) {
     332             :     uint16_t data;
     333     2540288 :     passgen_random_read(random, &data, sizeof(data));
     334     2540288 :     return data;
     335             : }
     336             : 
     337     3656815 : inline uint32_t passgen_random_u32(passgen_random *random) {
     338             :     uint32_t data;
     339     3656815 :     passgen_random_read(random, &data, sizeof(data));
     340     3656815 :     return data;
     341             : }
     342             : 
     343     4794601 : inline uint64_t passgen_random_u64(passgen_random *random) {
     344             :     uint64_t data;
     345     4794601 :     passgen_random_read(random, &data, sizeof(data));
     346     4794601 :     return data;
     347             : }
     348             : 
     349           6 : inline bool passgen_random_bool(passgen_random *random) {
     350             :     uint8_t data;
     351           6 :     passgen_random_read(random, &data, sizeof(data));
     352           6 :     return (data & 128) == 0;
     353             : }
     354             : 
     355      466230 : inline uint8_t passgen_random_u8_max(passgen_random *random, uint8_t max) {
     356      466230 :     passgen_assert(max);
     357             : 
     358      466230 :     uint8_t mask = max;
     359      466230 :     mask |= mask >> 4;
     360      466230 :     mask |= mask >> 2;
     361      466230 :     mask |= mask >> 1;
     362             : 
     363             :     uint8_t num;
     364             :     do {
     365      649822 :         num = passgen_random_u8(random) & mask;
     366      649822 :     } while(num >= max);
     367             : 
     368      466230 :     return num;
     369             : }
     370             : 
     371     1216512 : inline uint16_t passgen_random_u16_max(passgen_random *random, uint16_t max) {
     372     1216512 :     passgen_assert(max);
     373             : 
     374     1216512 :     uint16_t mask = max;
     375     1216512 :     mask |= mask >> 8;
     376     1216512 :     mask |= mask >> 4;
     377     1216512 :     mask |= mask >> 2;
     378     1216512 :     mask |= mask >> 1;
     379             : 
     380             :     uint16_t num;
     381             : 
     382             :     do {
     383     1549127 :         num = passgen_random_u16(random) & mask;
     384     1549127 :     } while(num >= max);
     385             : 
     386     1216512 :     return num;
     387             : }
     388             : 
     389     1434624 : inline uint32_t passgen_random_u32_max(passgen_random *random, uint32_t max) {
     390     1434624 :     passgen_assert(max);
     391             : 
     392     1434624 :     uint32_t mask = max;
     393     1434624 :     mask |= mask >> 16;
     394     1434624 :     mask |= mask >> 8;
     395     1434624 :     mask |= mask >> 4;
     396     1434624 :     mask |= mask >> 2;
     397     1434624 :     mask |= mask >> 1;
     398             : 
     399             :     uint32_t num;
     400             : 
     401             :     do {
     402     1903225 :         num = passgen_random_u32(random) & mask;
     403     1903225 :     } while(num >= max);
     404             : 
     405     1434624 :     return num;
     406             : }
     407             : 
     408     2172348 : inline uint64_t passgen_random_u64_max(passgen_random *random, uint64_t max) {
     409     2172348 :     passgen_assert(max);
     410             : 
     411     2172348 :     uint64_t mask = max;
     412     2172348 :     mask |= mask >> 32;
     413     2172348 :     mask |= mask >> 16;
     414     2172348 :     mask |= mask >> 8;
     415     2172348 :     mask |= mask >> 4;
     416     2172348 :     mask |= mask >> 2;
     417     2172348 :     mask |= mask >> 1;
     418             : 
     419             :     uint64_t num;
     420             : 
     421             :     do {
     422     2866874 :         num = passgen_random_u64(random) & mask;
     423     2866874 :     } while(num >= max);
     424             : 
     425     2172348 :     return num;
     426             : }

Generated by: LCOV version 1.14