@@ -10,8 +10,7 @@ use ark_ec::{
1010use ark_ff:: { field_hashers:: DefaultFieldHasher , PrimeField } ;
1111use ark_serialize:: { CanonicalDeserialize , CanonicalSerialize } ;
1212use itertools:: Itertools ;
13- use rand:: distr:: Uniform ;
14- use rand:: RngExt ;
13+ use rand:: Rng ;
1514use serde:: { Deserialize , Serialize } ;
1615use serde_with:: DeserializeAs ;
1716use sha2:: { digest:: Update , Digest , Sha256 } ;
@@ -216,11 +215,8 @@ pub fn encrypt<I: AsRef<[u8]>, M: AsRef<[u8]>>(
216215 let gid = master. projective_pairing ( id. as_ref ( ) ) ?;
217216
218217 // 2. Derive random sigma
219- let sigma: [ u8 ; 16 ] = ( 0 ..16 )
220- . map ( |_| rng. sample ( Uniform :: new ( 0u8 , 8u8 ) . unwrap ( ) ) )
221- . collect_vec ( )
222- . try_into ( )
223- . map_err ( |_| IBEError :: MessageSize ) ?;
218+ let mut sigma = [ 0u8 ; 16 ] ;
219+ rng. fill_bytes ( & mut sigma) ;
224220
225221 // 3. Derive r from sigma and msg
226222 let r: ScalarField = {
@@ -382,4 +378,43 @@ mod tests {
382378 let x = vec ! [ ] ;
383379 assert_eq ! ( xor( & a, & b) , x) ;
384380 }
381+
382+ /// Sigma must use the full [0, 256) byte range.
383+ ///
384+ /// The original code used `Uniform::new(0u8, 8u8)` which produces values
385+ /// in [0, 8) — only 3 bits of entropy per byte, 48 bits total for 16 bytes
386+ /// instead of the required 128 bits.
387+ ///
388+ /// Reference implementations:
389+ /// Go: crypto/rand.Read(sigma) — full CSPRNG
390+ /// JS: randomBytes(msg.length) — full CSPRNG (@noble/hashes/utils)
391+ #[ test]
392+ fn test_sigma_uses_full_byte_range ( ) {
393+ let mut rng = rand:: rng ( ) ;
394+ let mut seen = [ false ; 256 ] ;
395+
396+ // Generate enough sigma values to cover the full byte range.
397+ // With 16 bytes per sigma and 256 possible values, ~100 iterations
398+ // is statistically sufficient (coupon collector: ~1500 bytes needed,
399+ // 100 * 16 = 1600).
400+ for _ in 0 ..100 {
401+ let mut sigma = [ 0u8 ; 16 ] ;
402+ rng. fill_bytes ( & mut sigma) ;
403+ for & byte in & sigma {
404+ seen[ byte as usize ] = true ;
405+ }
406+ }
407+
408+ let covered = seen. iter ( ) . filter ( |& & v| v) . count ( ) ;
409+ // With 1600 random bytes from a uniform [0, 256) distribution,
410+ // the probability of missing even a single value is < 0.002%.
411+ // The old buggy code would only ever produce values in [0, 8),
412+ // covering at most 8 out of 256 values.
413+ assert ! (
414+ covered > 200 ,
415+ "sigma byte coverage too low: {covered}/256 — only [0, {}) seen, \
416+ expected full [0, 256) byte range",
417+ seen. iter( ) . rposition( |& v| v) . unwrap_or( 0 ) + 1 ,
418+ ) ;
419+ }
385420}
0 commit comments