@@ -5,6 +5,8 @@ use crate::{
55} ;
66use hybrid_array:: Array ;
77use module_lattice:: { ArraySize , Field , Truncate } ;
8+ #[ cfg( feature = "zeroize" ) ]
9+ use zeroize:: Zeroize ;
810
911// Algorithm 13 BytesToBits
1012fn bit_set ( z : & [ u8 ] , i : usize ) -> bool {
@@ -95,46 +97,90 @@ pub(crate) fn sample_in_ball(rho: &[u8], tau: usize) -> Polynomial {
9597fn rej_ntt_poly ( rho : & [ u8 ] , r : u8 , s : u8 ) -> NttPolynomial {
9698 let mut j = 0 ;
9799 let mut ctx = G :: default ( ) . absorb ( rho) . absorb ( & [ s] ) . absorb ( & [ r] ) ;
98-
99100 let mut a = NttPolynomial :: default ( ) ;
100- let mut s = [ 0u8 ; 3 ] ;
101- while j < 256 {
102- ctx. squeeze ( & mut s) ;
103- if let Some ( x) = coeff_from_three_bytes ( s) {
101+
102+ // Squeeze 840 bytes (5 SHAKE128 blocks) in a single call rather than 3 bytes
103+ // at a time. The rejection probability is ~0.098%, so 280 candidates are
104+ // almost always sufficient while requiring the same 5 Keccak-f permutations.
105+ let mut buf = [ 0u8 ; 840 ] ;
106+ ctx. squeeze ( & mut buf) ;
107+
108+ for chunk in buf. chunks_exact ( 3 ) {
109+ if let Some ( x) = coeff_from_three_bytes ( [ chunk[ 0 ] , chunk[ 1 ] , chunk[ 2 ] ] ) {
104110 a. 0 [ j] = x;
105111 j += 1 ;
112+ if j == 256 {
113+ break ;
114+ }
106115 }
107116 }
108117
118+ // Fallback: astronomically unlikely (~10^-44), but required for correctness.
119+ let mut tmp = [ 0u8 ; 3 ] ;
120+ while j < 256 {
121+ ctx. squeeze ( & mut tmp) ;
122+ if let Some ( x) = coeff_from_three_bytes ( tmp) {
123+ a. 0 [ j] = x;
124+ j += 1 ;
125+ }
126+ }
127+ #[ cfg( feature = "zeroize" ) ]
128+ {
129+ buf. zeroize ( ) ;
130+ tmp. zeroize ( ) ;
131+ }
109132 a
110133}
111134
112135// Algorithm 31 RejBoundedPoly
113136fn rej_bounded_poly ( rho : & [ u8 ] , eta : Eta , r : u16 ) -> Polynomial {
114137 let mut j = 0 ;
115138 let mut ctx = H :: default ( ) . absorb ( rho) . absorb ( & r. to_le_bytes ( ) ) ;
116-
117139 let mut a = Polynomial :: default ( ) ;
118- let mut z = [ 0u8 ] ;
119- while j < 256 {
120- ctx. squeeze ( & mut z) ;
121- let ( z0, z1) = coeffs_from_byte ( z[ 0 ] , eta) ;
122140
123- if let Some ( z) = z0 {
124- a. 0 [ j] = z;
141+ // The reference implementation uses 136 bytes (1 SHAKE256 block) for eta=2 and 272 bytes (2 blocks) for eta=4.
142+ let mut buf = [ 0u8 ; 272 ] ;
143+ ctx. squeeze ( & mut buf) ;
144+
145+ for & byte in & buf {
146+ let ( z0, z1) = coeffs_from_byte ( byte, eta) ;
147+ if let Some ( x) = z0 {
148+ a. 0 [ j] = x;
125149 j += 1 ;
150+ if j == 256 {
151+ break ;
152+ }
126153 }
127-
128- if j == 256 {
129- break ;
154+ if let Some ( x) = z1 {
155+ a. 0 [ j] = x;
156+ j += 1 ;
157+ if j == 256 {
158+ break ;
159+ }
130160 }
161+ }
131162
132- if let Some ( z) = z1 {
133- a. 0 [ j] = z;
163+ // Fallback: astronomically unlikely, but required for correctness.
164+ let mut tmp = [ 0u8 ; 1 ] ;
165+ while j < 256 {
166+ ctx. squeeze ( & mut tmp) ;
167+ let ( z0, z1) = coeffs_from_byte ( tmp[ 0 ] , eta) ;
168+ if let Some ( x) = z0 {
169+ a. 0 [ j] = x;
134170 j += 1 ;
135171 }
172+ if j < 256 {
173+ if let Some ( x) = z1 {
174+ a. 0 [ j] = x;
175+ j += 1 ;
176+ }
177+ }
178+ }
179+ #[ cfg( feature = "zeroize" ) ]
180+ {
181+ buf. zeroize ( ) ;
182+ tmp. zeroize ( ) ;
136183 }
137-
138184 a
139185}
140186
0 commit comments