internal/zuc: support fast forward

This commit is contained in:
Sun Yimin 2025-09-30 16:14:06 +08:00 committed by GitHub
parent 47b23bc827
commit a0912994f3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 43 additions and 4 deletions

View File

@ -28,8 +28,8 @@ type eea struct {
} }
const ( const (
magic = "zuceea" magic = "zuceea"
stateSize = (16 + 6) * 4 // zucState32 size in bytes stateSize = (16 + 6) * 4 // zucState32 size in bytes
minMarshaledSize = len(magic) + stateSize + 8 + 4*3 minMarshaledSize = len(magic) + stateSize + 8 + 4*3
) )
@ -276,6 +276,21 @@ func (c *eea) reset(offset uint64) {
c.used = n * uint64(c.bucketSize) c.used = n * uint64(c.bucketSize)
} }
func (c *eea) fastForward(offset uint64) {
// fast forward, check and adjust state if needed
var n uint64
if c.bucketSize > 0 {
n = offset / uint64(c.bucketSize)
expectedStateIndex := int(n)
if expectedStateIndex > c.stateIndex && expectedStateIndex < len(c.states) {
c.stateIndex = int(n)
c.zucState32 = *c.states[n]
c.xLen = 0
c.used = n * uint64(c.bucketSize)
}
}
}
// seek sets the offset for the next XORKeyStream operation. // seek sets the offset for the next XORKeyStream operation.
// //
// If the offset is less than the current offset, the state will be reset to the initial state. // If the offset is less than the current offset, the state will be reset to the initial state.
@ -283,6 +298,7 @@ func (c *eea) reset(offset uint64) {
// If the offset is greater than the current offset, the function will forward the state to the offset. // If the offset is greater than the current offset, the function will forward the state to the offset.
// Note: This method is not thread-safe. // Note: This method is not thread-safe.
func (c *eea) seek(offset uint64) { func (c *eea) seek(offset uint64) {
c.fastForward(offset)
if offset < c.used { if offset < c.used {
c.reset(offset) c.reset(offset)
} }

View File

@ -275,7 +275,7 @@ func TestEEAXORKeyStreamAtWithBucketSize(t *testing.T) {
} }
clear(dst) clear(dst)
bucketCipher.XORKeyStreamAt(dst[513:768], src[513:768], 513) bucketCipher.XORKeyStreamAt(dst[513:768], src[513:768], 513)
if bucketCipher.stateIndex != 0 { if bucketCipher.stateIndex != 4 {
t.Fatalf("expected=%d, result=%d\n", 0, bucketCipher.stateIndex) t.Fatalf("expected=%d, result=%d\n", 0, bucketCipher.stateIndex)
} }
if len(bucketCipher.states) != 7 { if len(bucketCipher.states) != 7 {
@ -296,6 +296,29 @@ func TestEEAXORKeyStreamAtWithBucketSize(t *testing.T) {
t.Fatalf("expected=%x, result=%x\n", expected[512:768], dst[512:768]) t.Fatalf("expected=%x, result=%x\n", expected[512:768], dst[512:768])
} }
}) })
t.Run("Rotate end to start, end to start", func(t *testing.T) {
bucketCipher, err := NewEEACipherWithBucketSize(key, zucEEATests[0].count, zucEEATests[0].bearer, zucEEATests[0].direction, 128)
if err != nil {
t.Error(err)
}
clear(dst)
for i := len(src) - RoundBytes; i >= 0; i -= RoundBytes {
offset := i
bucketCipher.XORKeyStreamAt(dst[offset:offset+RoundBytes], src[offset:offset+RoundBytes], uint64(offset))
if !bytes.Equal(expected[offset:offset+RoundBytes], dst[offset:offset+RoundBytes]) {
t.Fatalf("at %d, expected=%x, result=%x\n", offset, expected[offset:offset+RoundBytes], dst[offset:offset+RoundBytes])
}
}
clear(dst)
for i := len(src) - RoundBytes; i >= 0; i -= RoundBytes {
offset := i
bucketCipher.XORKeyStreamAt(dst[offset:offset+RoundBytes], src[offset:offset+RoundBytes], uint64(offset))
if !bytes.Equal(expected[offset:offset+RoundBytes], dst[offset:offset+RoundBytes]) {
t.Fatalf("at %d, expected=%x, result=%x\n", offset, expected[offset:offset+RoundBytes], dst[offset:offset+RoundBytes])
}
}
})
} }
func TestMarshalUnmarshalBinary(t *testing.T) { func TestMarshalUnmarshalBinary(t *testing.T) {
@ -394,7 +417,7 @@ func TestUnmarshalBinary_InvalidRemainingBytes(t *testing.T) {
data[xLenOffset+3] = 8 // xLen = 8 data[xLenOffset+3] = 8 // xLen = 8
// Truncate data so remaining bytes < xLen // Truncate data so remaining bytes < xLen
truncated := data[:minMarshaledSize + 4] truncated := data[:minMarshaledSize+4]
c2 := NewEmptyCipher() c2 := NewEmptyCipher()
err = c2.UnmarshalBinary(truncated) err = c2.UnmarshalBinary(truncated)