Skip to content

Commit 64ac0fb

Browse files
committed
rand: implement an unbiassed random integer from a range
Refer: swiftlang/swift#39143 for a description of the algorithm. It is optimal in the sense of having: * no divisions * minimal number of blocks of random bits from the generator
1 parent dbbdb94 commit 64ac0fb

File tree

3 files changed

+64
-1
lines changed

3 files changed

+64
-1
lines changed

crypto/rand/build.info

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
LIBS=../../libcrypto
22

33
$COMMON=rand_lib.c
4-
$CRYPTO=randfile.c rand_err.c rand_deprecated.c prov_seed.c rand_pool.c
4+
$CRYPTO=randfile.c rand_err.c rand_deprecated.c prov_seed.c rand_pool.c \
5+
rand_uniform.c
56

67
IF[{- !$disabled{'egd'} -}]
78
$CRYPTO=$CRYPTO rand_egd.c

crypto/rand/rand_uniform.c

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* Copyright 2023 The OpenSSL Project Authors. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License 2.0 (the "License"). You may not use
5+
* this file except in compliance with the License. You can obtain a copy
6+
* in the file LICENSE in the source distribution or at
7+
* https://www.openssl.org/source/license.html
8+
*/
9+
10+
#include "crypto/rand.h"
11+
#include "internal/common.h"
12+
13+
/*
14+
* Implementation an optimal random integer in a range function.
15+
* Refer: https://github.com/apple/swift/pull/39143 for a description
16+
* of the algorithm.
17+
*/
18+
uint32_t ossl_rand_uniform_uint32(OSSL_LIB_CTX *ctx, uint32_t upper, int *err)
19+
{
20+
uint32_t i, f; /* integer and fractional parts */
21+
uint32_t f2, rand; /* extra fractional part and random material */
22+
uint64_t prod; /* temporary holding double width product */
23+
const int n = 10; /* Maxiumum number of followup iterations */
24+
int j;
25+
26+
/* Get 32 bits of entropy */
27+
if (RAND_bytes_ex(ctx, (unsigned char *)&rand, sizeof(rand), 0) <= 0) {
28+
*err = 1;
29+
return 0;
30+
}
31+
prod = (uint64_t)upper * rand;
32+
i = prod >> 32;
33+
f = prod & 0xffffffff;
34+
if (likely(f <= -upper))
35+
return i;
36+
37+
for(j = 0; j < n; j++) {
38+
if (RAND_bytes_ex(ctx, (unsigned char *)&rand, sizeof(rand), 0) <= 0) {
39+
*err = 1;
40+
return 0;
41+
}
42+
prod = (uint64_t)upper * rand;
43+
f2 = prod >> 32;
44+
f += f2;
45+
/* On overflow, add the carry to our result */
46+
if (f < f2)
47+
return i + 1;
48+
/* For not all 1 bits, there is no carry so return the result */
49+
if (unlikely(f != 0xffffffff))
50+
return i;
51+
/* setup for the next word of randomness */
52+
f = prod & 0xffffffff;
53+
}
54+
/*
55+
* If we get here, we've consumed 32 * n + 32 bits with no firm decision,
56+
* this gives a bias with probability < 2^(32*n), likely acceptable.
57+
*/
58+
return i;
59+
}

include/crypto/rand.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,4 +140,7 @@ EVP_RAND_CTX *ossl_rand_get0_private_noncreating(OSSL_LIB_CTX *ctx);
140140
# else
141141
EVP_RAND_CTX *ossl_rand_get0_seed_noncreating(OSSL_LIB_CTX *ctx);
142142
# endif
143+
144+
uint32_t ossl_rand_uniform_uint32(OSSL_LIB_CTX *ctx, uint32_t upper, int *err);
145+
143146
#endif

0 commit comments

Comments
 (0)