Coverage Report

Created: 2025-07-12 06:16

/src/unit/src/nxt_random.c
Line
Count
Source (jump to first uncovered line)
1
2
/*
3
 * Copyright (C) Igor Sysoev
4
 * Copyright (C) NGINX, Inc.
5
 */
6
7
8
#include <nxt_main.h>
9
10
11
/*
12
 * The pseudorandom generator based on OpenBSD arc4random.  Although it is
13
 * usually stated that arc4random uses RC4 pseudorandom generation algorithm
14
 * they are actually different in nxt_random_add().
15
 */
16
17
18
8
#define NXT_RANDOM_KEY_SIZE  128
19
20
21
nxt_inline void nxt_random_start_schedule(nxt_random_t *r);
22
static void nxt_random_stir(nxt_random_t *r);
23
static void nxt_random_add(nxt_random_t *r, const u_char *key, uint32_t len);
24
nxt_inline uint8_t nxt_random_byte(nxt_random_t *r);
25
26
27
void
28
nxt_random_init(nxt_random_t *r)
29
2
{
30
2
    nxt_random_start_schedule(r);
31
32
2
    nxt_random_stir(r);
33
2
}
34
35
36
nxt_inline void
37
nxt_random_start_schedule(nxt_random_t *r)
38
2
{
39
2
    nxt_uint_t  i;
40
41
2
    r->i = 0;
42
2
    r->j = 0;
43
44
514
    for (i = 0; i < 256; i++) {
45
512
        r->s[i] = i;
46
512
    }
47
2
}
48
49
50
static void
51
nxt_random_stir(nxt_random_t *r)
52
2
{
53
2
    int             fd;
54
2
    ssize_t         n;
55
2
    struct timeval  tv;
56
2
    union {
57
2
        uint32_t    value[4];
58
2
        u_char      bytes[NXT_RANDOM_KEY_SIZE];
59
2
    } key;
60
61
2
#if (NXT_HAVE_GETRANDOM)
62
63
2
    n = getrandom(&key, NXT_RANDOM_KEY_SIZE, 0);
64
65
#elif (NXT_HAVE_LINUX_SYS_GETRANDOM)
66
67
    /* Linux 3.17 SYS_getrandom. */
68
69
    n = syscall(SYS_getrandom, &key, NXT_RANDOM_KEY_SIZE, 0);
70
71
#elif (NXT_HAVE_GETENTROPY || NXT_HAVE_GETENTROPY_SYS_RANDOM)
72
73
    n = 0;
74
75
    if (getentropy(&key, NXT_RANDOM_KEY_SIZE) == 0) {
76
        n = NXT_RANDOM_KEY_SIZE;
77
    }
78
79
#else
80
81
    n = 0;
82
83
#endif
84
85
2
    if (n != NXT_RANDOM_KEY_SIZE) {
86
0
        fd = open("/dev/urandom", O_RDONLY);
87
88
0
        if (fd >= 0) {
89
0
            n = read(fd, &key, NXT_RANDOM_KEY_SIZE);
90
0
            (void) close(fd);
91
0
        }
92
0
    }
93
94
2
    if (n != NXT_RANDOM_KEY_SIZE) {
95
0
        (void) gettimeofday(&tv, NULL);
96
97
        /* XOR with stack garbage. */
98
99
0
        key.value[0] ^= tv.tv_usec;
100
0
        key.value[1] ^= tv.tv_sec;
101
0
        key.value[2] ^= nxt_pid;
102
0
        key.value[3] ^= (uintptr_t) nxt_thread_tid(nxt_thread());
103
0
    }
104
105
2
    nxt_random_add(r, key.bytes, NXT_RANDOM_KEY_SIZE);
106
107
    /* Drop the first 3072 bytes. */
108
6.14k
    for (n = 3072; n != 0; n--) {
109
6.14k
        (void) nxt_random_byte(r);
110
6.14k
    }
111
112
    /* Stir again after 1,600,000 bytes. */
113
2
    r->count = 400000;
114
2
}
115
116
117
static void
118
nxt_random_add(nxt_random_t *r, const u_char *key, uint32_t len)
119
2
{
120
2
    uint8_t   val;
121
2
    uint32_t  n;
122
123
514
    for (n = 0; n < 256; n++) {
124
512
        val = r->s[r->i];
125
512
        r->j += val + key[n % len];
126
127
512
        r->s[r->i] = r->s[r->j];
128
512
        r->s[r->j] = val;
129
130
512
        r->i++;
131
512
    }
132
133
    /* This index is not decremented in RC4 algorithm. */
134
2
    r->i--;
135
136
2
    r->j = r->i;
137
2
}
138
139
140
uint32_t
141
nxt_random(nxt_random_t *r)
142
0
{
143
0
    uint32_t  val;
144
145
0
    r->count--;
146
147
0
    if (r->count <= 0) {
148
0
        nxt_random_stir(r);
149
0
    }
150
151
0
    val  = (uint32_t) nxt_random_byte(r) << 24;
152
0
    val |= (uint32_t) nxt_random_byte(r) << 16;
153
0
    val |= (uint32_t) nxt_random_byte(r) << 8;
154
0
    val |= (uint32_t) nxt_random_byte(r);
155
156
0
    return val;
157
0
}
158
159
160
nxt_inline uint8_t
161
nxt_random_byte(nxt_random_t *r)
162
6.14k
{
163
6.14k
    uint8_t  si, sj;
164
165
6.14k
    r->i++;
166
6.14k
    si = r->s[r->i];
167
6.14k
    r->j += si;
168
169
6.14k
    sj = r->s[r->j];
170
6.14k
    r->s[r->i] = sj;
171
6.14k
    r->s[r->j] = si;
172
173
6.14k
    si += sj;
174
175
6.14k
    return r->s[si];
176
6.14k
}
177
178
179
#if (NXT_TESTS)
180
181
nxt_int_t
182
nxt_random_test(nxt_thread_t *thr)
183
{
184
    nxt_uint_t    n;
185
    nxt_random_t  r;
186
187
    nxt_random_start_schedule(&r);
188
189
    r.count = 400000;
190
191
    nxt_random_add(&r, (u_char *) "arc4random", nxt_length("arc4random"));
192
193
    /*
194
     * Test arc4random() numbers.
195
     * RC4 pseudorandom numbers would be 0x4642AFC3 and 0xBAF0FFF0.
196
     */
197
198
    if (nxt_random(&r) == 0xD6270B27) {
199
200
        for (n = 100000; n != 0; n--) {
201
            (void) nxt_random(&r);
202
        }
203
204
        if (nxt_random(&r) == 0x6FCAE186) {
205
            nxt_log_error(NXT_LOG_NOTICE, thr->log, "arc4random test passed");
206
207
            return NXT_OK;
208
        }
209
    }
210
211
    nxt_log_error(NXT_LOG_NOTICE, thr->log, "arc4random test failed");
212
213
    return NXT_ERROR;
214
}
215
216
#endif