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 (
magic = "zuceea"
stateSize = (16 + 6) * 4 // zucState32 size in bytes
magic = "zuceea"
stateSize = (16 + 6) * 4 // zucState32 size in bytes
minMarshaledSize = len(magic) + stateSize + 8 + 4*3
)
@ -276,6 +276,21 @@ func (c *eea) reset(offset uint64) {
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.
//
// 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.
// Note: This method is not thread-safe.
func (c *eea) seek(offset uint64) {
c.fastForward(offset)
if offset < c.used {
c.reset(offset)
}

View File

@ -275,7 +275,7 @@ func TestEEAXORKeyStreamAtWithBucketSize(t *testing.T) {
}
clear(dst)
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)
}
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.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) {
@ -394,7 +417,7 @@ func TestUnmarshalBinary_InvalidRemainingBytes(t *testing.T) {
data[xLenOffset+3] = 8 // xLen = 8
// Truncate data so remaining bytes < xLen
truncated := data[:minMarshaledSize + 4]
truncated := data[:minMarshaledSize+4]
c2 := NewEmptyCipher()
err = c2.UnmarshalBinary(truncated)