12
12
13
13
/*
14
14
* Implementation an optimal random integer in a range function.
15
- * Refer: https://github.com/apple/swift/pull/39143 for a description
15
+ *
16
+ * Essentially it boils down to incrementally generating a fixed point
17
+ * number on the interval [0, 1) and multiplying this number by the upper
18
+ * range limit. Once it is certain what the fractional part contributes to
19
+ * the integral part of the product, the algorithm has produced a definitive
20
+ * result.
21
+ *
22
+ * Refer: https://github.com/apple/swift/pull/39143 for a fuller description
16
23
* of the algorithm.
17
24
*/
18
25
uint32_t ossl_rand_uniform_uint32 (OSSL_LIB_CTX * ctx , uint32_t upper , int * err )
@@ -29,17 +36,43 @@ uint32_t ossl_rand_uniform_uint32(OSSL_LIB_CTX *ctx, uint32_t upper, int *err)
29
36
}
30
37
if (unlikely (upper == 1 ))
31
38
return 0 ;
39
+
32
40
/* Get 32 bits of entropy */
33
41
if (RAND_bytes_ex (ctx , (unsigned char * )& rand , sizeof (rand ), 0 ) <= 0 ) {
34
42
* err = 1 ;
35
43
return 0 ;
36
44
}
45
+
46
+ /*
47
+ * We are generating a fixed point number on the interval [0, 1).
48
+ * Multiplying this by the range gives us a number on [0, upper).
49
+ * The high word of the multiplication result represents the integral
50
+ * part we want. The lower word is the fractional part. We can early exit if
51
+ * if the fractional part is small enough that no carry from the next lower
52
+ * word can cause an overflow and carry into the integer part. This
53
+ * happens when the fractional part is bounded by 2^32 - upper which
54
+ * can be simplified to just -upper (as an unsigned integer).
55
+ */
37
56
prod = (uint64_t )upper * rand ;
38
57
i = prod >> 32 ;
39
58
f = prod & 0xffffffff ;
40
59
if (likely (f <= 1 + ~upper )) /* 1+~upper == -upper but compilers whine */
41
60
return i ;
42
61
62
+ /*
63
+ * We're in the position where the carry from the next word *might* cause
64
+ * a carry to the integral part. The process here is to generate the next
65
+ * word, multiply it by the range and add that to the current word. If
66
+ * it overflows, the carry propagates to the integer part (return i+1).
67
+ * If it can no longer overflow regardless of further lower order bits,
68
+ * we are done (return i). If there is still a chance of overflow, we
69
+ * repeat the process with the next lower word.
70
+ *
71
+ * Each *bit* of randomness has a probability of one half of terminating
72
+ * this process, so each each word beyond the first has a probability
73
+ * of 2^-32 of not terminating the process. That is, we're extremely
74
+ * likely to stop very rapidly.
75
+ */
43
76
for (j = 0 ; j < max_followup_iterations ; j ++ ) {
44
77
if (RAND_bytes_ex (ctx , (unsigned char * )& rand , sizeof (rand ), 0 ) <= 0 ) {
45
78
* err = 1 ;
@@ -59,8 +92,8 @@ uint32_t ossl_rand_uniform_uint32(OSSL_LIB_CTX *ctx, uint32_t upper, int *err)
59
92
}
60
93
/*
61
94
* If we get here, we've consumed 32 * max_followup_iterations + 32 bits
62
- * with no firm decision, this gives a bias with probability < 2^(32*n),
63
- * likely acceptable.
95
+ * with no firm decision, this gives a bias with probability < 2^- (32*n),
96
+ * which is likely acceptable.
64
97
*/
65
98
return i ;
66
99
}
0 commit comments