zuc: seekable stream refactoring and fix bug #277

This commit is contained in:
Sun Yimin 2024-11-29 15:19:19 +08:00 committed by GitHub
parent da9f9c1748
commit 895c0db489
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 35 additions and 24 deletions

View File

@ -7,11 +7,15 @@ import (
"github.com/emmansun/gmsm/internal/subtle" "github.com/emmansun/gmsm/internal/subtle"
) )
const RoundWords = 32 const (
RoundWords = 32
WordSize = 4
RoundBytes = RoundWords * WordSize
)
type eea struct { type eea struct {
zucState32 zucState32
x [4]byte // remaining bytes buffer x [WordSize]byte // remaining bytes buffer
xLen int // number of remaining bytes xLen int // number of remaining bytes
initState zucState32 initState zucState32
used uint64 used uint64
@ -47,10 +51,10 @@ func NewEEACipher(key []byte, count, bearer, direction uint32) (cipher.SeekableS
} }
func genKeyStreamRev32Generic(keyStream []byte, pState *zucState32) { func genKeyStreamRev32Generic(keyStream []byte, pState *zucState32) {
for len(keyStream) >= 4 { for len(keyStream) >= WordSize {
z := genKeyword(pState) z := genKeyword(pState)
byteorder.BEPutUint32(keyStream, z) byteorder.BEPutUint32(keyStream, z)
keyStream = keyStream[4:] keyStream = keyStream[WordSize:]
} }
} }
@ -74,17 +78,17 @@ func (c *eea) XORKeyStream(dst, src []byte) {
return return
} }
} }
words := (len(src) + 3) / 4 words := (len(src) + WordSize - 1) / WordSize
rounds := words / RoundWords rounds := words / RoundWords
var keyBytes [RoundWords * 4]byte var keyBytes [RoundBytes]byte
for i := 0; i < rounds; i++ { for i := 0; i < rounds; i++ {
genKeyStreamRev32(keyBytes[:], &c.zucState32) genKeyStreamRev32(keyBytes[:], &c.zucState32)
subtle.XORBytes(dst, src, keyBytes[:]) subtle.XORBytes(dst, src, keyBytes[:])
dst = dst[RoundWords*4:] dst = dst[RoundBytes:]
src = src[RoundWords*4:] src = src[RoundBytes:]
} }
if rounds*RoundWords < words { if processedWords := rounds * RoundWords; processedWords < words {
byteLen := 4 * (words - rounds*RoundWords) byteLen := WordSize * (words - processedWords)
genKeyStreamRev32(keyBytes[:byteLen], &c.zucState32) genKeyStreamRev32(keyBytes[:byteLen], &c.zucState32)
n := subtle.XORBytes(dst, src, keyBytes[:]) n := subtle.XORBytes(dst, src, keyBytes[:])
// save remaining key bytes // save remaining key bytes
@ -110,6 +114,7 @@ func (c *eea) XORKeyStreamAt(dst, src []byte, offset uint64) {
panic("zuc: invalid buffer overlap") panic("zuc: invalid buffer overlap")
} }
if offset < c.used { if offset < c.used {
// reset the state to the initial state
c.reset() c.reset()
} else if offset == c.used { } else if offset == c.used {
c.XORKeyStream(dst, src) c.XORKeyStream(dst, src)
@ -126,27 +131,24 @@ func (c *eea) XORKeyStreamAt(dst, src []byte, offset uint64) {
// forward the state to the offset // forward the state to the offset
// this part can be optimized by a little bit // this part can be optimized by a little bit
stepLen := uint64(RoundWords * 4) stepLen := uint64(RoundBytes)
var keys [RoundWords]uint32 var keys [RoundWords]uint32
for ; diff >= uint64(stepLen); diff -= stepLen { for ; diff >= uint64(stepLen); diff -= stepLen {
genKeyStream(keys[:], &c.zucState32) genKeyStream(keys[:], &c.zucState32)
c.used += stepLen c.used += stepLen
} }
// handle remaining key bytes
if diff > 0 { if diff > 0 {
limit := (diff + 3) / 4 limit := (diff + WordSize - 1) / WordSize
remaining := int(diff % 4)
genKeyStream(keys[:limit], &c.zucState32) genKeyStream(keys[:limit], &c.zucState32)
c.used += limit * 4 partiallyUsed := int(diff % WordSize)
if remaining > 0 { c.used += limit * WordSize
var keyBytes [4]byte if partiallyUsed > 0 {
c.used -= 4 // save remaining key bytes (less than 4 bytes)
c.xLen = 4 - remaining c.xLen = WordSize - partiallyUsed
if c.xLen > 0 { c.used -= uint64(c.xLen)
byteorder.BEPutUint32(keyBytes[:], keys[limit-1]) byteorder.BEPutUint32(c.x[:], keys[limit-1])
copy(c.x[:], keyBytes[remaining:]) copy(c.x[:], c.x[partiallyUsed:])
}
} }
} }
c.XORKeyStream(dst, src) c.XORKeyStream(dst, src)

View File

@ -103,6 +103,15 @@ func TestXORStreamAt(t *testing.T) {
t.Errorf("expected=%x, result=%x\n", dst1[:32], dst2[:32]) t.Errorf("expected=%x, result=%x\n", dst1[:32], dst2[:32])
} }
// test jump forward
for i := 0; i < 4; i++ {
c.XORKeyStreamAt(dst2[i:16], src2[i:16], uint64(i))
c.XORKeyStreamAt(dst2[32:64], src2[32:64], 32)
if !bytes.Equal(dst2[32:64], dst1[32:64]) {
t.Errorf("expected=%x, result=%x\n", dst1[32:64], dst2[32:64])
}
}
// test offset - used > 128 bytes case // test offset - used > 128 bytes case
c.XORKeyStreamAt(dst2[:16], src2[:16], 0) c.XORKeyStreamAt(dst2[:16], src2[:16], 0)
offset := 700 offset := 700