LCOV - code coverage report
Current view: top level - src - rand.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 7 38 18.4 %
Date: 2023-08-10 00:00:00 Functions: 2 6 33.3 %

          Line data    Source code
       1             : /* rand.c
       2             :  * strophe XMPP client library -- pseudo-random number generator
       3             :  *
       4             :  * Copyright (C) 2014 Dmitry Podgorny <pasis.ua@gmail.com>
       5             :  *
       6             :  *  This software is provided AS-IS with no warranty, either express
       7             :  *  or implied.
       8             :  *
       9             :  *  This program is dual licensed under the MIT and GPLv3 licenses.
      10             :  */
      11             : 
      12             : /** @file
      13             :  *  Pseudo-random number generator.
      14             :  *
      15             :  *  Implemented Hash_DRBG mechanism according to NIST SP 800-90A.
      16             :  *  Hash function is SHA1.
      17             :  */
      18             : 
      19             : /** @defgroup Random Pseudo-random number generator
      20             :  */
      21             : 
      22             : #include <assert.h>
      23             : #include <string.h> /* memeset */
      24             : #include <time.h>   /* clock, time */
      25             : 
      26             : #if !defined(_WIN32)
      27             : #include <unistd.h>
      28             : #endif
      29             : 
      30             : #if !defined(DONT_USE_GETRANDOM) && defined(__linux__) && \
      31             :     defined(__GLIBC_PREREQ)
      32             : #if __GLIBC_PREREQ(2, 25)
      33             : #define USE_GETRANDOM
      34             : #include <sys/random.h>
      35             : #include <errno.h>
      36             : #endif
      37             : #endif
      38             : 
      39             : #include "common.h"  /* strophe_alloc, strophe_free */
      40             : #include "ostypes.h" /* uint8_t, uint32_t, size_t */
      41             : 
      42             : #ifndef USE_GETRANDOM
      43             : #include "sha1.h"
      44             : 
      45             : #define outlen SHA1_DIGEST_SIZE
      46             : #define seedlen (440 / 8)
      47             : #define reseed_interval 0x7fffffff
      48             : 
      49             : /* maximum number of bytes that can be generated per call */
      50             : #define GENERATE_MAX (outlen * 10)
      51             : #define ENTROPY_MAX 128
      52             : #define NONCE_MAX 8
      53             : 
      54             : #define RESEED_NEEDED (-1)
      55             : 
      56             : struct Hash_DRBG_CTX_struc {
      57             :     uint8_t V[seedlen];
      58             :     uint8_t C[seedlen];
      59             :     uint32_t reseed_counter;
      60             : };
      61             : typedef struct Hash_DRBG_CTX_struc Hash_DRBG_CTX;
      62             : 
      63             : struct _xmpp_rand_t {
      64             :     int inited;
      65             :     unsigned reseed_count;
      66             :     Hash_DRBG_CTX ctx;
      67             : };
      68             : 
      69             : /* returns smallest number mupliple of y that not less than x */
      70             : #define round_up(x, y) (((x) + (y)-1) / (y) * (y))
      71             : /* returns smallest integer number that not less than x/y */
      72             : #define div_round_up(x, y) (((x) + (y)-1) / (y))
      73             : 
      74             : /* adds two arrays as numbers in big-endian representation and stores
      75             :  * result in the first one.
      76             :  */
      77             : static void
      78             : arr_add(uint8_t *arr1, size_t arr1_len, uint8_t *arr2, size_t arr2_len)
      79             : {
      80             :     size_t i;
      81             :     uint32_t acc;
      82             :     uint32_t carry = 0;
      83             : 
      84             :     assert(arr1_len >= arr2_len);
      85             : 
      86             :     for (i = 1; (i <= arr2_len) || (carry != 0 && i <= arr1_len); ++i) {
      87             :         acc = (uint32_t)arr1[arr1_len - i] + carry;
      88             :         if (i <= arr2_len)
      89             :             acc += (uint32_t)arr2[arr2_len - i];
      90             :         carry = acc >> 8;
      91             :         arr1[arr1_len - i] = (uint8_t)(acc & 0xff);
      92             :     }
      93             : }
      94             : 
      95             : /* stores 32-bit number in big-endian representation */
      96             : static void store_be32(uint32_t val, uint8_t be[4])
      97             : {
      98             :     be[0] = (uint8_t)((val >> 24) & 0xff);
      99             :     be[1] = (uint8_t)((val >> 16) & 0xff);
     100             :     be[2] = (uint8_t)((val >> 8) & 0xff);
     101             :     be[3] = (uint8_t)(val & 0xff);
     102             : }
     103             : 
     104             : static void Hash_df(uint8_t *input_string,
     105             :                     size_t input_string_len,
     106             :                     uint8_t *output_string,
     107             :                     size_t no_of_bytes_to_return)
     108             : {
     109             :     uint8_t counter;
     110             :     uint8_t temp[round_up(seedlen, outlen)];
     111             :     uint8_t conj[ENTROPY_MAX + NONCE_MAX + seedlen + 6];
     112             :     size_t len;
     113             :     size_t i;
     114             :     size_t offset;
     115             : 
     116             :     assert(no_of_bytes_to_return <= sizeof(temp));
     117             :     assert(input_string_len + 5 <= sizeof(conj));
     118             : 
     119             :     len = div_round_up(no_of_bytes_to_return, outlen);
     120             :     for (i = 1; i <= len; ++i) {
     121             :         offset = (i - 1) * outlen;
     122             :         counter = (uint8_t)i;
     123             :         conj[0] = counter;
     124             :         store_be32((uint32_t)no_of_bytes_to_return * 8, conj + 1);
     125             :         memcpy(conj + 5, input_string, input_string_len);
     126             :         crypto_SHA1(conj, input_string_len + 5, temp + offset);
     127             :     }
     128             : 
     129             :     memcpy(output_string, temp, no_of_bytes_to_return);
     130             : }
     131             : 
     132             : /* assume personalization_string is zero length string */
     133             : static void Hash_DRBG_Instantiate(Hash_DRBG_CTX *ctx,
     134             :                                   uint8_t *entropy_input,
     135             :                                   size_t entropy_input_len,
     136             :                                   uint8_t *nonce,
     137             :                                   size_t nonce_len)
     138             : {
     139             :     uint8_t seed_material[ENTROPY_MAX + NONCE_MAX];
     140             :     uint8_t seed0[seedlen + 1];
     141             :     uint8_t *seed = seed0 + 1;
     142             : 
     143             :     assert(entropy_input_len <= ENTROPY_MAX);
     144             :     assert(nonce_len <= NONCE_MAX);
     145             :     assert(nonce != NULL || nonce_len == 0);
     146             : 
     147             :     memcpy(seed_material, entropy_input, entropy_input_len);
     148             :     if (nonce != NULL)
     149             :         memcpy(seed_material + entropy_input_len, nonce, nonce_len);
     150             :     Hash_df(seed_material, entropy_input_len + nonce_len, seed, seedlen);
     151             :     seed0[0] = 0;
     152             : 
     153             :     memcpy(ctx->V, seed, seedlen);
     154             :     Hash_df(seed0, sizeof(seed0), ctx->C, seedlen);
     155             :     ctx->reseed_counter = 1;
     156             : }
     157             : 
     158             : /* assume additional_input is zero length string */
     159             : static void Hash_DRBG_Reseed(Hash_DRBG_CTX *ctx,
     160             :                              uint8_t *entropy_input,
     161             :                              size_t entropy_input_len)
     162             : {
     163             :     uint8_t seed_material[1 + seedlen + ENTROPY_MAX];
     164             :     uint8_t seed0[seedlen + 1];
     165             :     uint8_t *seed = seed0 + 1;
     166             : 
     167             :     assert(entropy_input_len <= ENTROPY_MAX);
     168             : 
     169             :     seed_material[0] = 1;
     170             :     memcpy(seed_material + 1, ctx->V, seedlen);
     171             :     memcpy(seed_material + 1 + seedlen, entropy_input, entropy_input_len);
     172             :     Hash_df(seed_material, entropy_input_len + seedlen + 1, seed, seedlen);
     173             :     seed0[0] = 0;
     174             : 
     175             :     memcpy(ctx->V, seed, seedlen);
     176             :     Hash_df(seed0, sizeof(seed0), ctx->C, seedlen);
     177             :     ctx->reseed_counter = 1;
     178             : }
     179             : 
     180             : static void
     181             : Hashgen(uint8_t *V, uint8_t *output, size_t requested_number_of_bytes)
     182             : {
     183             :     uint8_t data[seedlen];
     184             :     uint8_t W[GENERATE_MAX];
     185             :     uint8_t i1 = 1;
     186             :     size_t m;
     187             :     size_t i;
     188             :     size_t offset;
     189             : 
     190             :     assert(requested_number_of_bytes <= sizeof(W));
     191             : 
     192             :     m = div_round_up(requested_number_of_bytes, outlen);
     193             :     memcpy(data, V, seedlen);
     194             :     for (i = 1; i <= m; ++i) {
     195             :         offset = (i - 1) * outlen;
     196             :         crypto_SHA1(data, seedlen, W + offset);
     197             :         /* increase data by 1 */
     198             :         arr_add(data, sizeof(data), &i1, 1);
     199             :     }
     200             : 
     201             :     memcpy(output, W, requested_number_of_bytes);
     202             : }
     203             : 
     204             : /* assume additional_input is zero length string */
     205             : static int Hash_DRBG_Generate(Hash_DRBG_CTX *ctx,
     206             :                               uint8_t *output,
     207             :                               size_t requested_number_of_bytes)
     208             : {
     209             :     uint8_t H[outlen];
     210             :     uint8_t V3[seedlen + 1];
     211             :     uint8_t reseed_counter[4];
     212             : 
     213             :     if (ctx->reseed_counter > reseed_interval || ctx->reseed_counter == 0)
     214             :         return RESEED_NEEDED;
     215             : 
     216             :     Hashgen(ctx->V, output, requested_number_of_bytes);
     217             : 
     218             :     V3[0] = 3;
     219             :     memcpy(V3 + 1, ctx->V, seedlen);
     220             :     crypto_SHA1(V3, sizeof(V3), H);
     221             :     arr_add(ctx->V, sizeof(ctx->V), ctx->C, sizeof(ctx->C));
     222             :     arr_add(ctx->V, sizeof(ctx->V), H, sizeof(H));
     223             :     store_be32(ctx->reseed_counter, reseed_counter);
     224             :     arr_add(ctx->V, sizeof(ctx->V), reseed_counter, sizeof(reseed_counter));
     225             : 
     226             :     ++ctx->reseed_counter;
     227             :     return 0;
     228             : }
     229             : 
     230             : #define ENTROPY_ACCUMULATE(ptr, last, type, arg)          \
     231             :     do {                                                  \
     232             :         type __arg = (type)(arg);                         \
     233             :         if ((char *)ptr + sizeof(__arg) < (char *)last) { \
     234             :             *(type *)ptr = __arg;                         \
     235             :             ptr = (void *)((char *)ptr + sizeof(__arg));  \
     236             :         }                                                 \
     237             :     } while (0)
     238             : 
     239             : static void xmpp_rand_reseed(xmpp_rand_t *rand)
     240             : {
     241             :     uint8_t entropy[ENTROPY_MAX];
     242             :     uint8_t *ptr = entropy;
     243             :     const uint8_t *last = entropy + sizeof(entropy);
     244             :     size_t len;
     245             : 
     246             :     /* entropy:
     247             :      *  1. time_stamp()
     248             :      *  2. clock(3)
     249             :      *  3. xmpp_rand_t address to make unique seed within one process
     250             :      *  4. counter to make unique seed within one context
     251             :      *  5. stack address
     252             :      *  6. local ports of every connection in list (getsockname)
     253             :      *  7. other non-constant info that can be retieved from socket
     254             :      *
     255             :      *  rand(3) can't be used as it isn't thread-safe.
     256             :      *  XXX 6 and 7 are not implemented yet.
     257             :      */
     258             : 
     259             :     ENTROPY_ACCUMULATE(ptr, last, uint64_t, time_stamp());
     260             :     ENTROPY_ACCUMULATE(ptr, last, clock_t, clock());
     261             :     ENTROPY_ACCUMULATE(ptr, last, void *, rand);
     262             :     ENTROPY_ACCUMULATE(ptr, last, unsigned, ++rand->reseed_count);
     263             :     ENTROPY_ACCUMULATE(ptr, last, void *, &entropy);
     264             :     len = ptr - entropy;
     265             : 
     266             :     if (rand->inited) {
     267             :         Hash_DRBG_Reseed(&rand->ctx, entropy, len);
     268             :     } else {
     269             :         Hash_DRBG_Instantiate(&rand->ctx, entropy, len, NULL, 0);
     270             :         rand->inited = 1;
     271             :     }
     272             : }
     273             : 
     274             : xmpp_rand_t *xmpp_rand_new(xmpp_ctx_t *ctx)
     275             : {
     276             :     xmpp_rand_t *out = strophe_alloc(ctx, sizeof(*out));
     277             :     if (out != NULL) {
     278             :         memset(out, 0, sizeof(*out));
     279             :     }
     280             :     return out;
     281             : }
     282             : 
     283             : void xmpp_rand_free(xmpp_ctx_t *ctx, xmpp_rand_t *rand)
     284             : {
     285             :     strophe_free(ctx, rand);
     286             : }
     287             : 
     288             : void xmpp_rand_bytes(xmpp_rand_t *rand, unsigned char *output, size_t len)
     289             : {
     290             :     int rc;
     291             :     size_t gen, tot = 0;
     292             :     while (tot < len) {
     293             :         gen = len - tot;
     294             :         if (gen > GENERATE_MAX)
     295             :             gen = GENERATE_MAX;
     296             :         rc = Hash_DRBG_Generate(&rand->ctx, (uint8_t *)output + tot, gen);
     297             :         if (rc == RESEED_NEEDED) {
     298             :             xmpp_rand_reseed(rand);
     299             :             rc = Hash_DRBG_Generate(&rand->ctx, (uint8_t *)output + tot, gen);
     300             :             assert(rc == 0);
     301             :         }
     302             :         tot += gen;
     303             :     }
     304             : }
     305             : 
     306             : #else
     307             : 
     308           0 : static int _read_getrandom(void *p, size_t n)
     309             : {
     310           0 :     unsigned char *q = (unsigned char *)p;
     311           0 :     while (n > 0u) {
     312           0 :         ssize_t ret = getrandom(q, n, 0);
     313           0 :         if (ret < 0) {
     314           0 :             if (errno == EINTR) {
     315           0 :                 continue;
     316             :             }
     317             :             return 1;
     318             :         }
     319           0 :         q += ret;
     320           0 :         n -= (size_t)ret;
     321             :     }
     322             :     return 0;
     323             : }
     324             : 
     325             : struct _xmpp_rand_t {
     326             :     char nothing;
     327             : };
     328             : 
     329             : static xmpp_rand_t _xmpp_rand;
     330             : 
     331           3 : xmpp_rand_t *xmpp_rand_new(xmpp_ctx_t *ctx)
     332             : {
     333           3 :     UNUSED(ctx);
     334           3 :     return &_xmpp_rand;
     335             : }
     336             : 
     337           3 : void xmpp_rand_free(xmpp_ctx_t *ctx, xmpp_rand_t *rand)
     338             : {
     339           3 :     UNUSED(ctx);
     340           3 :     assert(rand == &_xmpp_rand);
     341           3 : }
     342             : 
     343           0 : void xmpp_rand_bytes(xmpp_rand_t *rand, unsigned char *output, size_t len)
     344             : {
     345           0 :     assert(rand == &_xmpp_rand);
     346           0 :     assert(_read_getrandom(output, len) == 0);
     347           0 : }
     348             : 
     349             : #endif
     350             : 
     351           0 : int xmpp_rand(xmpp_rand_t *rand)
     352             : {
     353           0 :     int result;
     354             : 
     355           0 :     xmpp_rand_bytes(rand, (unsigned char *)&result, sizeof(result));
     356           0 :     return result;
     357             : }
     358             : 
     359           0 : static void rand_byte2hex(unsigned char byte, char *hex)
     360             : {
     361           0 :     static const char hex_tbl[16] = {'0', '1', '2', '3', '4', '5', '6', '7',
     362             :                                      '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
     363             : 
     364           0 :     hex[0] = hex_tbl[(byte >> 4) & 0x0f];
     365           0 :     hex[1] = hex_tbl[byte & 0x0f];
     366             : }
     367             : 
     368           0 : void xmpp_rand_nonce(xmpp_rand_t *rand, char *output, size_t len)
     369             : {
     370           0 :     size_t i;
     371           0 :     const size_t rand_len = len / 2;
     372             : 
     373             :     /*
     374             :      * We don't want to use any allocation here, because this function
     375             :      * can't fail. Also we want to avoid VLA.
     376             :      * Current implementation uses half of the output buffer for random buffer
     377             :      * generation and then converts it to HEX representation.
     378             :      */
     379             : 
     380           0 :     if (rand_len > 0) {
     381           0 :         xmpp_rand_bytes(rand, (unsigned char *)output, rand_len);
     382           0 :         for (i = rand_len; i > 0; --i)
     383           0 :             rand_byte2hex(output[i - 1], &output[(i - 1) * 2]);
     384             :     }
     385           0 :     if (len > 0)
     386           0 :         output[len - 1] = '\0';
     387           0 : }

Generated by: LCOV version 1.14