2022-04-19 11:25:14 +08:00
|
|
|
package zuc
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/cipher"
|
|
|
|
"encoding/binary"
|
|
|
|
|
2022-08-18 14:49:35 +08:00
|
|
|
"github.com/emmansun/gmsm/internal/alias"
|
2022-04-19 11:25:14 +08:00
|
|
|
"github.com/emmansun/gmsm/internal/subtle"
|
|
|
|
)
|
|
|
|
|
2022-07-01 11:00:42 +08:00
|
|
|
const RoundWords = 32
|
2022-06-30 11:29:42 +08:00
|
|
|
|
2024-08-05 11:40:04 +08:00
|
|
|
type eea struct {
|
|
|
|
zucState32
|
|
|
|
x [4]byte // remaining bytes buffer
|
|
|
|
xLen int // number of remaining bytes
|
|
|
|
}
|
|
|
|
|
2022-04-19 11:25:14 +08:00
|
|
|
// NewCipher create a stream cipher based on key and iv aguments.
|
|
|
|
func NewCipher(key, iv []byte) (cipher.Stream, error) {
|
2024-08-05 11:40:04 +08:00
|
|
|
s, err := newZUCState(key, iv)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
c := new(eea)
|
|
|
|
c.zucState32 = *s
|
|
|
|
return c, nil
|
2022-04-19 11:25:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewEEACipher create a stream cipher based on key, count, bearer and direction arguments according specification.
|
|
|
|
func NewEEACipher(key []byte, count, bearer, direction uint32) (cipher.Stream, error) {
|
|
|
|
iv := make([]byte, 16)
|
|
|
|
binary.BigEndian.PutUint32(iv, count)
|
|
|
|
copy(iv[8:12], iv[:4])
|
|
|
|
iv[4] = byte(((bearer << 1) | (direction & 1)) << 2)
|
|
|
|
iv[12] = iv[4]
|
2024-08-05 11:40:04 +08:00
|
|
|
s, err := newZUCState(key, iv)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
c := new(eea)
|
|
|
|
c.zucState32 = *s
|
|
|
|
return c, nil
|
2022-04-19 11:25:14 +08:00
|
|
|
}
|
|
|
|
|
2023-09-29 09:57:27 +08:00
|
|
|
func genKeyStreamRev32Generic(keyStream []byte, pState *zucState32) {
|
|
|
|
for len(keyStream) >= 4 {
|
|
|
|
z := genKeyword(pState)
|
|
|
|
binary.BigEndian.PutUint32(keyStream, z)
|
|
|
|
keyStream = keyStream[4:]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-05 11:40:04 +08:00
|
|
|
func (c *eea) XORKeyStream(dst, src []byte) {
|
2023-09-29 09:57:27 +08:00
|
|
|
if len(dst) < len(src) {
|
|
|
|
panic("zuc: output smaller than input")
|
|
|
|
}
|
|
|
|
if alias.InexactOverlap(dst[:len(src)], src) {
|
|
|
|
panic("zuc: invalid buffer overlap")
|
|
|
|
}
|
2024-08-05 11:40:04 +08:00
|
|
|
if c.xLen > 0 {
|
|
|
|
// handle remaining key bytes
|
|
|
|
n := subtle.XORBytes(dst, src, c.x[:c.xLen])
|
|
|
|
c.xLen -= n
|
|
|
|
dst = dst[n:]
|
|
|
|
src = src[n:]
|
|
|
|
if c.xLen > 0 {
|
|
|
|
copy(c.x[:], c.x[n:c.xLen+n])
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
2022-04-19 11:25:14 +08:00
|
|
|
words := (len(src) + 3) / 4
|
2022-06-30 11:29:42 +08:00
|
|
|
rounds := words / RoundWords
|
|
|
|
var keyBytes [RoundWords * 4]byte
|
|
|
|
for i := 0; i < rounds; i++ {
|
2024-08-05 11:40:04 +08:00
|
|
|
genKeyStreamRev32(keyBytes[:], &c.zucState32)
|
2022-08-18 14:49:35 +08:00
|
|
|
subtle.XORBytes(dst, src, keyBytes[:])
|
2022-06-30 11:29:42 +08:00
|
|
|
dst = dst[RoundWords*4:]
|
|
|
|
src = src[RoundWords*4:]
|
|
|
|
}
|
|
|
|
if rounds*RoundWords < words {
|
2024-08-05 11:40:04 +08:00
|
|
|
byteLen := 4 * (words - rounds*RoundWords)
|
|
|
|
genKeyStreamRev32(keyBytes[:byteLen], &c.zucState32)
|
|
|
|
n := subtle.XORBytes(dst, src, keyBytes[:])
|
|
|
|
// save remaining key bytes
|
|
|
|
c.xLen = byteLen - n
|
|
|
|
if c.xLen > 0 {
|
|
|
|
copy(c.x[:], keyBytes[n:byteLen])
|
|
|
|
}
|
2022-04-19 11:25:14 +08:00
|
|
|
}
|
|
|
|
}
|