gmsm/zuc/eia.go
2024-11-22 08:33:24 +08:00

253 lines
6.0 KiB
Go

package zuc
import (
"fmt"
"github.com/emmansun/gmsm/internal/byteorder"
)
const (
chunk = 16
)
type ZUC128Mac struct {
zucState32 // current zuc state
k0 [8]uint32 // keywords
t uint32 // tag
x [chunk]byte //buffer
nx int // remaining data in x
len uint64 // total data length
tagSize int // tag size
initState zucState32 // initial state for reset
}
// NewHash create hash for zuc-128 eia, with arguments key and iv.
// Both key/iv size are 16 in bytes.
func NewHash(key, iv []byte) (*ZUC128Mac, error) {
k := len(key)
ivLen := len(iv)
mac := &ZUC128Mac{}
mac.tagSize = 4
switch k {
default:
return nil, fmt.Errorf("zuc: invalid key size %d, expect 16 in bytes", k)
case 16: // ZUC-128
if ivLen != IVSize128 {
return nil, fmt.Errorf("zuc: invalid iv size %d, expect %d in bytes", ivLen, IVSize128)
}
mac.loadKeyIV16(key, iv)
}
// initialization
for i := 0; i < 32; i++ {
mac.bitReorganization()
w := mac.f32()
mac.enterInitMode(w >> 1)
}
// work state
mac.bitReorganization()
mac.f32()
mac.enterWorkMode()
mac.initState.r1 = mac.r1
mac.initState.r2 = mac.r2
copy(mac.initState.lfsr[:], mac.lfsr[:])
mac.Reset()
return mac, nil
}
func genIV4EIA(count, bearer, direction uint32) []byte {
iv := make([]byte, 16)
byteorder.BEPutUint32(iv, count)
copy(iv[9:12], iv[1:4])
iv[4] = byte(bearer << 3)
iv[12] = iv[4]
iv[8] = iv[0] ^ byte(direction<<7)
iv[14] = byte(direction << 7)
return iv
}
// NewEIAHash create hash for zuc-128 eia, with arguments key, count, bearer and direction
// The key must be 16 bytes long and iv must be 16 bytes long, otherwise, an error will be returned.
// The count is the 32-bit counter value, the bearer is the 5-bit bearer identity and the direction is the 1-bit
// transmission direction flag.
func NewEIAHash(key []byte, count, bearer, direction uint32) (*ZUC128Mac, error) {
return NewHash(key, genIV4EIA(count, bearer, direction))
}
func (m *ZUC128Mac) Size() int {
return m.tagSize
}
func (m *ZUC128Mac) BlockSize() int {
return chunk
}
// Reset resets the Hash to its initial state.
func (m *ZUC128Mac) Reset() {
m.t = 0
m.nx = 0
m.len = 0
m.r1 = m.initState.r1
m.r2 = m.initState.r2
copy(m.lfsr[:], m.initState.lfsr[:])
m.genKeywords(m.k0[:len(m.k0)/2])
}
func blockGeneric(m *ZUC128Mac, p []byte) {
// use 64 bits to shift left 2 keywords
var k64, t64 uint64
t64 = uint64(m.t) << 32
for len(p) >= chunk {
// generate next 4 keywords
m.genKeywords(m.k0[4:])
k64 = uint64(m.k0[0])<<32 | uint64(m.k0[1])
// process first 32 bits
w := byteorder.BEUint32(p[0:4])
for j := 0; j < 32; j++ {
// t64 ^= (w >> 31) ? k64 : 0
t64 ^= ^(uint64(w>>31) - 1) & k64
w <<= 1
k64 <<= 1
}
// process second 32 bits
k64 = uint64(m.k0[1])<<32 | uint64(m.k0[2])
w = byteorder.BEUint32(p[4:8])
for j := 0; j < 32; j++ {
t64 ^= ^(uint64(w>>31) - 1) & k64
w <<= 1
k64 <<= 1
}
// process third 32 bits
k64 = uint64(m.k0[2])<<32 | uint64(m.k0[3])
w = byteorder.BEUint32(p[8:12])
for j := 0; j < 32; j++ {
t64 ^= ^(uint64(w>>31) - 1) & k64
w <<= 1
k64 <<= 1
}
// process fourth 32 bits
k64 = uint64(m.k0[3])<<32 | uint64(m.k0[4])
w = byteorder.BEUint32(p[12:16])
for j := 0; j < 32; j++ {
t64 ^= ^(uint64(w>>31) - 1) & k64
w <<= 1
k64 <<= 1
}
// Move the new keywords to the first 4
copy(m.k0[:4], m.k0[4:])
p = p[chunk:]
}
m.t = uint32(t64 >> 32)
}
func (m *ZUC128Mac) Write(p []byte) (nn int, err error) {
nn = len(p)
m.len += uint64(nn)
if m.nx > 0 {
n := copy(m.x[m.nx:], p)
m.nx += n
if m.nx == chunk {
block(m, m.x[:])
m.nx = 0
}
p = p[n:]
}
if len(p) >= chunk {
n := len(p) &^ (chunk - 1)
block(m, p[:n])
p = p[n:]
}
if len(p) > 0 {
m.nx = copy(m.x[:], p)
}
return
}
func (m *ZUC128Mac) checkSum(additionalBits int, b byte) [4]byte {
if m.nx >= chunk {
panic("m.nx >= chunk")
}
kIdx := 0
if m.nx > 0 || additionalBits > 0 {
var k64, t64 uint64
t64 = uint64(m.t) << 32
m.x[m.nx] = b
// total bits to handle
nRemainBits := 8*m.nx + additionalBits
if nRemainBits > 2*32 {
// generate next 2 keywords
m.genKeywords(m.k0[4:6])
}
// nwords <= 4
nwords := (nRemainBits + 31) / 32
// process 32 bits at a time for first complete words
for i := 0; i < nwords-1; i++ {
k64 = uint64(m.k0[i])<<32 | uint64(m.k0[i+1])
w := byteorder.BEUint32(m.x[i*4:])
for j := 0; j < 32; j++ {
t64 ^= ^(uint64(w>>31) - 1) & k64
w <<= 1
k64 <<= 1
}
}
nRemainBits -= (nwords - 1) * 32
// current key word index, 0 <= kIdx <= 3
kIdx = nwords - 1
// process remaining bits less than 32
if nRemainBits > 0 {
k64 = uint64(m.k0[kIdx])<<32 | uint64(m.k0[kIdx+1])
w := byteorder.BEUint32(m.x[(nwords-1)*4:])
for j := 0; j < nRemainBits; j++ {
t64 ^= ^(uint64(w>>31) - 1) & k64
w <<= 1
k64 <<= 1
}
// Reset for fianal computation
m.k0[kIdx] = uint32(k64 >> 32) // key[LENGTH]
m.k0[kIdx+1] = m.k0[kIdx+2] // Last key word
}
m.t = uint32(t64 >> 32)
}
m.t ^= m.k0[kIdx]
m.t ^= m.k0[kIdx+1]
var digest [4]byte
byteorder.BEPutUint32(digest[:], m.t)
return digest
}
// Finish this function hash nbits data in p and return mac value, after this function call,
// the hash state will be reset.
// In general, we will use byte level function, this is just for test/verify.
// nbits: number of bits to hash in p.
func (m *ZUC128Mac) Finish(p []byte, nbits int) []byte {
if len(p) < (nbits+7)/8 {
panic("invalid p length")
}
nbytes := nbits / 8
nRemainBits := nbits - nbytes*8
if nbytes > 0 {
m.Write(p[:nbytes])
}
var b byte
if nRemainBits > 0 {
b = p[nbytes]
}
digest := m.checkSum(nRemainBits, b)
m.Reset()
return digest[:]
}
// Sum appends the current hash to in and returns the resulting slice.
// It does not change the underlying hash state.
func (m *ZUC128Mac) Sum(in []byte) []byte {
// Make a copy of d so that caller can keep writing and summing.
d0 := *m
hash := d0.checkSum(0, 0)
return append(in, hash[:]...)
}