chacha20: add SetCounter method
Fixes golang/go#35506 Change-Id: I5cfc6b4dc07ab368e370edaee11841c2c1377f82 GitHub-Last-Rev: 16147a1668a903532f2d3777b873ddad8f0f26f5 GitHub-Pull-Request: golang/crypto#108 Reviewed-on: https://go-review.googlesource.com/c/crypto/+/206638 Run-TryBot: Filippo Valsorda <filippo@golang.org> Reviewed-by: Filippo Valsorda <filippo@golang.org>
This commit is contained in:
committed by
Filippo Valsorda
parent
0f24fbd83d
commit
eddbd5da47
@@ -136,6 +136,33 @@ func quarterRound(a, b, c, d uint32) (uint32, uint32, uint32, uint32) {
|
|||||||
return a, b, c, d
|
return a, b, c, d
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetCounter sets the Cipher counter. The next invocation of XORKeyStream will
|
||||||
|
// behave as if (64 * counter) bytes had been encrypted so far.
|
||||||
|
//
|
||||||
|
// To prevent accidental counter reuse, SetCounter panics if counter is
|
||||||
|
// less than the current value.
|
||||||
|
func (s *Cipher) SetCounter(counter uint32) {
|
||||||
|
// Internally, s may buffer multiple blocks, which complicates this
|
||||||
|
// implementation slightly. When checking whether the counter has rolled
|
||||||
|
// back, we must use both s.counter and s.len to determine how many blocks
|
||||||
|
// we have already output.
|
||||||
|
outputCounter := s.counter - uint32(s.len)/blockSize
|
||||||
|
if counter < outputCounter {
|
||||||
|
panic("chacha20: SetCounter attempted to rollback counter")
|
||||||
|
}
|
||||||
|
|
||||||
|
// In the general case, we set the new counter value and reset s.len to 0,
|
||||||
|
// causing the next call to XORKeyStream to refill the buffer. However, if
|
||||||
|
// we're advancing within the existing buffer, we can save work by simply
|
||||||
|
// setting s.len.
|
||||||
|
if counter < s.counter {
|
||||||
|
s.len = int(s.counter-counter) * blockSize
|
||||||
|
} else {
|
||||||
|
s.counter = counter
|
||||||
|
s.len = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// XORKeyStream XORs each byte in the given slice with a byte from the
|
// XORKeyStream XORs each byte in the given slice with a byte from the
|
||||||
// cipher's key stream. Dst and src must overlap entirely or not at all.
|
// cipher's key stream. Dst and src must overlap entirely or not at all.
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -110,6 +110,52 @@ func TestStep(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSetCounter(t *testing.T) {
|
||||||
|
newCipher := func() *Cipher {
|
||||||
|
s, _ := NewUnauthenticatedCipher(make([]byte, KeySize), make([]byte, NonceSize))
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
s := newCipher()
|
||||||
|
src := bytes.Repeat([]byte("test"), 32) // two 64-byte blocks
|
||||||
|
dst1 := make([]byte, len(src))
|
||||||
|
s.XORKeyStream(dst1, src)
|
||||||
|
// advance counter to 1 and xor second block
|
||||||
|
s = newCipher()
|
||||||
|
s.SetCounter(1)
|
||||||
|
dst2 := make([]byte, len(src))
|
||||||
|
s.XORKeyStream(dst2[64:], src[64:])
|
||||||
|
if !bytes.Equal(dst1[64:], dst2[64:]) {
|
||||||
|
t.Error("failed to produce identical output using SetCounter")
|
||||||
|
}
|
||||||
|
|
||||||
|
// test again with unaligned blocks; SetCounter should reset the buffer
|
||||||
|
s = newCipher()
|
||||||
|
s.XORKeyStream(dst1[:70], src[:70])
|
||||||
|
s = newCipher()
|
||||||
|
s.XORKeyStream([]byte{0}, []byte{0})
|
||||||
|
s.SetCounter(1)
|
||||||
|
s.XORKeyStream(dst2[64:70], src[64:70])
|
||||||
|
if !bytes.Equal(dst1[64:70], dst2[64:70]) {
|
||||||
|
t.Error("SetCounter did not reset buffer")
|
||||||
|
}
|
||||||
|
|
||||||
|
// advancing to a lower counter value should cause a panic
|
||||||
|
panics := func(fn func()) (p bool) {
|
||||||
|
defer func() { p = recover() != nil }()
|
||||||
|
fn()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !panics(func() { s.SetCounter(0) }) {
|
||||||
|
t.Error("counter decreasing should trigger a panic")
|
||||||
|
}
|
||||||
|
// advancing to ^uint32(0) and then calling XORKeyStream should cause a panic
|
||||||
|
s = newCipher()
|
||||||
|
s.SetCounter(^uint32(0))
|
||||||
|
if !panics(func() { s.XORKeyStream([]byte{0}, []byte{0}) }) {
|
||||||
|
t.Error("counter overflowing should trigger a panic")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func benchmarkChaCha20(b *testing.B, step, count int) {
|
func benchmarkChaCha20(b *testing.B, step, count int) {
|
||||||
tot := step * count
|
tot := step * count
|
||||||
src := make([]byte, tot)
|
src := make([]byte, tot)
|
||||||
|
|||||||
Reference in New Issue
Block a user