@@ -42,10 +42,14 @@ type Cipher struct {
4242
4343 // The last len bytes of buf are leftover key stream bytes from the previous
4444 // XORKeyStream invocation. The size of buf depends on how many blocks are
45- // computed at a time.
45+ // computed at a time by xorKeyStreamBlocks .
4646 buf [bufSize ]byte
4747 len int
4848
49+ // overflow is set when the counter overflowed, no more blocks can be
50+ // generated, and the next XORKeyStream call should panic.
51+ overflow bool
52+
4953 // The counter-independent results of the first round are cached after they
5054 // are computed the first time.
5155 precompDone bool
@@ -140,15 +144,18 @@ func quarterRound(a, b, c, d uint32) (uint32, uint32, uint32, uint32) {
140144// SetCounter sets the Cipher counter. The next invocation of XORKeyStream will
141145// behave as if (64 * counter) bytes had been encrypted so far.
142146//
143- // To prevent accidental counter reuse, SetCounter panics if counter is
144- // less than the current value.
147+ // To prevent accidental counter reuse, SetCounter panics if counter is less
148+ // than the current value.
149+ //
150+ // Note that the execution time of XORKeyStream is not independent of the
151+ // counter value.
145152func (s * Cipher ) SetCounter (counter uint32 ) {
146153 // Internally, s may buffer multiple blocks, which complicates this
147154 // implementation slightly. When checking whether the counter has rolled
148155 // back, we must use both s.counter and s.len to determine how many blocks
149156 // we have already output.
150157 outputCounter := s .counter - uint32 (s .len )/ blockSize
151- if counter < outputCounter {
158+ if s . overflow || counter < outputCounter {
152159 panic ("chacha20: SetCounter attempted to rollback counter" )
153160 }
154161
@@ -197,34 +204,52 @@ func (s *Cipher) XORKeyStream(dst, src []byte) {
197204 dst [i ] = src [i ] ^ b
198205 }
199206 s .len -= len (keyStream )
200- src = src [len (keyStream ):]
201- dst = dst [len (keyStream ):]
207+ dst , src = dst [len (keyStream ):], src [len (keyStream ):]
208+ }
209+ if len (src ) == 0 {
210+ return
202211 }
203212
204- const blocksPerBuf = bufSize / blockSize
205- numBufs := (uint64 (len (src )) + bufSize - 1 ) / bufSize
206- if uint64 (s .counter )+ numBufs * blocksPerBuf >= 1 << 32 {
213+ // If we'd need to let the counter overflow and keep generating output,
214+ // panic immediately. If instead we'd only reach the last block, remember
215+ // not to generate any more output after the buffer is drained.
216+ numBlocks := (uint64 (len (src )) + blockSize - 1 ) / blockSize
217+ if s .overflow || uint64 (s .counter )+ numBlocks > 1 << 32 {
207218 panic ("chacha20: counter overflow" )
219+ } else if uint64 (s .counter )+ numBlocks == 1 << 32 {
220+ s .overflow = true
208221 }
209222
210223 // xorKeyStreamBlocks implementations expect input lengths that are a
211224 // multiple of bufSize. Platform-specific ones process multiple blocks at a
212225 // time, so have bufSizes that are a multiple of blockSize.
213226
214- rem := len (src ) % bufSize
215- full := len (src ) - rem
216-
227+ full := len (src ) - len (src )% bufSize
217228 if full > 0 {
218229 s .xorKeyStreamBlocks (dst [:full ], src [:full ])
219230 }
231+ dst , src = dst [full :], src [full :]
232+
233+ // If using a multi-block xorKeyStreamBlocks would overflow, use the generic
234+ // one that does one block at a time.
235+ const blocksPerBuf = bufSize / blockSize
236+ if uint64 (s .counter )+ blocksPerBuf > 1 << 32 {
237+ s .buf = [bufSize ]byte {}
238+ numBlocks := (len (src ) + blockSize - 1 ) / blockSize
239+ buf := s .buf [bufSize - numBlocks * blockSize :]
240+ copy (buf , src )
241+ s .xorKeyStreamBlocksGeneric (buf , buf )
242+ s .len = len (buf ) - copy (dst , buf )
243+ return
244+ }
220245
221246 // If we have a partial (multi-)block, pad it for xorKeyStreamBlocks, and
222247 // keep the leftover keystream for the next XORKeyStream invocation.
223- if rem > 0 {
248+ if len ( src ) > 0 {
224249 s .buf = [bufSize ]byte {}
225- copy (s .buf [:], src [ full :] )
250+ copy (s .buf [:], src )
226251 s .xorKeyStreamBlocks (s .buf [:], s .buf [:])
227- s .len = bufSize - copy (dst [ full :] , s .buf [:])
252+ s .len = bufSize - copy (dst , s .buf [:])
228253 }
229254}
230255
@@ -308,9 +333,6 @@ func (s *Cipher) xorKeyStreamBlocksGeneric(dst, src []byte) {
308333 addXor (dst [60 :64 ], src [60 :64 ], x15 , c15 )
309334
310335 s .counter += 1
311- if s .counter == 0 {
312- panic ("chacha20: internal error: counter overflow" )
313- }
314336
315337 src , dst = src [blockSize :], dst [blockSize :]
316338 }
0 commit comments