@@ -20,6 +20,12 @@ impl<const LIMBS: usize> Random for Uint<LIMBS> {
20
20
/// Fill the given limbs slice with random bits.
21
21
///
22
22
/// NOTE: Assumes that the limbs in the given slice are zeroed!
23
+ ///
24
+ /// When combined with a platform-independent "4-byte sequential" `rng`, this function is
25
+ /// platform-independent. We consider an RNG "`X`-byte sequential" whenever
26
+ /// `rng.fill_bytes(&mut bytes[..i]); rng.fill_bytes(&mut bytes[i..])` constructs the same `bytes`,
27
+ /// as long as `i` is a multiple of `X`.
28
+ /// Note that the `TryRngCore` trait does _not_ require this behaviour from `rng`.
23
29
pub ( crate ) fn random_bits_core < R : TryRngCore + ?Sized > (
24
30
rng : & mut R ,
25
31
zeroed_limbs : & mut [ Limb ] ,
@@ -39,12 +45,26 @@ pub(crate) fn random_bits_core<R: TryRngCore + ?Sized>(
39
45
for i in 0 ..nonzero_limbs - 1 {
40
46
rng. try_fill_bytes ( & mut buffer)
41
47
. map_err ( RandomBitsError :: RandCore ) ?;
42
- zeroed_limbs[ i] = Limb ( Word :: from_be_bytes ( buffer) ) ;
48
+ zeroed_limbs[ i] = Limb ( Word :: from_le_bytes ( buffer) ) ;
43
49
}
44
50
45
- rng. try_fill_bytes ( & mut buffer)
51
+ // This algorithm should sample the same number of random bytes, regardless of the pointer width
52
+ // of the target platform. To this end, special attention has to be paid to the case where
53
+ // bit_length - 1 < 32 mod 64. Bit strings of that size can be represented using `2X+1` 32-bit
54
+ // words or `X+1` 64-bit words. Note that 64*(X+1) - 32*(2X+1) = 32. Hence, if we sample full
55
+ // words only, a 64-bit platform will sample 32 bits more than a 32-bit platform. We prevent
56
+ // this by forcing both platforms to only sample 4 bytes for the last word in this case.
57
+ let slice = if partial_limb > 0 && partial_limb <= 32 {
58
+ // Note: we do not have to zeroize the second half of the buffer, as the mask will take
59
+ // care of this in the end.
60
+ & mut buffer[ 0 ..4 ]
61
+ } else {
62
+ buffer. as_mut_slice ( )
63
+ } ;
64
+
65
+ rng. try_fill_bytes ( slice)
46
66
. map_err ( RandomBitsError :: RandCore ) ?;
47
- zeroed_limbs[ nonzero_limbs - 1 ] = Limb ( Word :: from_be_bytes ( buffer) & mask) ;
67
+ zeroed_limbs[ nonzero_limbs - 1 ] = Limb ( Word :: from_le_bytes ( buffer) & mask) ;
48
68
49
69
Ok ( ( ) )
50
70
}
@@ -144,9 +164,31 @@ where
144
164
145
165
#[ cfg( test) ]
146
166
mod tests {
147
- use crate :: { Limb , NonZero , RandomBits , RandomMod , U256 } ;
167
+ use crate :: uint:: rand:: random_bits_core;
168
+ use crate :: { Limb , NonZero , Random , RandomBits , RandomMod , U256 , U1024 , Uint } ;
148
169
use rand_chacha:: ChaCha8Rng ;
149
- use rand_core:: SeedableRng ;
170
+ use rand_core:: { RngCore , SeedableRng } ;
171
+
172
+ const RANDOM_OUTPUT : U1024 = Uint :: from_be_hex ( concat ! [
173
+ "A484C4C693EECC47C3B919AE0D16DF2259CD1A8A9B8EA8E0862878227D4B40A3" ,
174
+ "C54302F2EB1E2F69E17653A37F1BCC44277FA208E6B31E08CDC4A23A7E88E660" ,
175
+ "EF781C7DD2D368BAD438539D6A2E923C8CAE14CB947EB0BDE10D666732024679" ,
176
+ "0F6760A48F9B887CB2FB0D3281E2A6E67746A55FBAD8C037B585F767A79A3B6C"
177
+ ] ) ;
178
+
179
+ /// Construct a 4-sequential `rng`, i.e., an `rng` such that
180
+ /// `rng.fill_bytes(&mut buffer[..x]); rng.fill_bytes(&mut buffer[x..])` will construct the
181
+ /// same `buffer`, for `x` any in `0..buffer.len()` that is `0 mod 4`.
182
+ fn get_four_sequential_rng ( ) -> ChaCha8Rng {
183
+ ChaCha8Rng :: seed_from_u64 ( 0 )
184
+ }
185
+
186
+ /// Make sure the random value constructed is consistent across platforms
187
+ #[ test]
188
+ fn random_platform_independence ( ) {
189
+ let mut rng = get_four_sequential_rng ( ) ;
190
+ assert_eq ! ( U1024 :: random( & mut rng) , RANDOM_OUTPUT ) ;
191
+ }
150
192
151
193
#[ test]
152
194
fn random_mod ( ) {
@@ -219,4 +261,47 @@ mod tests {
219
261
assert_eq ! ( res, U256 :: ZERO ) ;
220
262
}
221
263
}
264
+
265
+ /// Make sure the random_bits output is consistent across platforms
266
+ #[ test]
267
+ fn random_bits_platform_independence ( ) {
268
+ let mut rng = get_four_sequential_rng ( ) ;
269
+
270
+ let bit_length = 989 ;
271
+ let mut val = U1024 :: ZERO ;
272
+ random_bits_core ( & mut rng, val. as_limbs_mut ( ) , bit_length) . expect ( "safe" ) ;
273
+
274
+ assert_eq ! (
275
+ val,
276
+ RANDOM_OUTPUT . bitand( & U1024 :: ONE . shl( bit_length) . wrapping_sub( & Uint :: ONE ) )
277
+ ) ;
278
+
279
+ // Test that the RNG is in the same state
280
+ let mut state = [ 0u8 ; 16 ] ;
281
+ rng. fill_bytes ( & mut state) ;
282
+
283
+ assert_eq ! (
284
+ state,
285
+ [
286
+ 198 , 196 , 132 , 164 , 240 , 211 , 223 , 12 , 36 , 189 , 139 , 48 , 94 , 1 , 123 , 253
287
+ ]
288
+ ) ;
289
+ }
290
+
291
+ /// Test that random bytes are sampled consecutively.
292
+ #[ test]
293
+ fn random_bits_4_bytes_sequential ( ) {
294
+ // Test for multiples of 4 bytes, i.e., multiples of 32 bits.
295
+ let bit_lengths = [ 0 , 32 , 64 , 128 , 192 , 992 ] ;
296
+
297
+ for bit_length in bit_lengths {
298
+ let mut rng = get_four_sequential_rng ( ) ;
299
+ let mut first = U1024 :: ZERO ;
300
+ let mut second = U1024 :: ZERO ;
301
+ random_bits_core ( & mut rng, first. as_limbs_mut ( ) , bit_length) . expect ( "safe" ) ;
302
+ random_bits_core ( & mut rng, second. as_limbs_mut ( ) , U1024 :: BITS - bit_length)
303
+ . expect ( "safe" ) ;
304
+ assert_eq ! ( second. shl( bit_length) . bitor( & first) , RANDOM_OUTPUT ) ;
305
+ }
306
+ }
222
307
}
0 commit comments