support zuc eia 256 bits

This commit is contained in:
Emman 2022-04-26 10:04:30 +08:00
parent 7632ebf376
commit eb56eac42a
4 changed files with 460 additions and 2 deletions

View File

@ -1,4 +1,4 @@
// Package zuc handle shangmi zuc stream cipher
// Package zuc handle shangmi zuc stream cipher, experimental/poc implementation.
package zuc
import (
@ -57,6 +57,21 @@ var zuc256_d0 = [16]byte{
0x40, 0x40, 0x40, 0x40, 0x40, 0x52, 0x10, 0x30,
}
var zuc256_d = [][16]byte{
{
0x22, 0x2F, 0x25, 0x2A, 0x6D, 0x40, 0x40, 0x40,
0x40, 0x40, 0x40, 0x40, 0x40, 0x52, 0x10, 0x30,
},
{
0x23, 0x2F, 0x24, 0x2A, 0x6D, 0x40, 0x40, 0x40,
0x40, 0x40, 0x40, 0x40, 0x40, 0x52, 0x10, 0x30,
},
{
0x23, 0x2F, 0x25, 0x2A, 0x6D, 0x40, 0x40, 0x40,
0x40, 0x40, 0x40, 0x40, 0x40, 0x52, 0x10, 0x30,
},
}
type zucState32 struct {
lfsr [16]uint32 // linear feedback shift register
r1 uint32

View File

@ -23,7 +23,6 @@ type ZUC128Mac struct {
}
// NewHash create hash for zuc-128 eia, with arguments key and iv.
// Hash byte (8 bits) level.
func NewHash(key, iv []byte) (*ZUC128Mac, error) {
k := len(key)
ivLen := len(iv)

191
zuc/eia256.go Normal file
View File

@ -0,0 +1,191 @@
package zuc
import (
"encoding/binary"
"fmt"
)
type ZUC256Mac struct {
zucState32
initState zucState32
tagSize int
k0 []uint32
t []uint32
x [chunk]byte
nx int
len uint64
}
// NewHash create hash for zuc-128 eia, with arguments key, iv and tagSize.
// The larger the tag size, the worse the performance.
func NewHash256(key, iv []byte, tagSize int) (*ZUC256Mac, error) {
k := len(key)
ivLen := len(iv)
mac := &ZUC256Mac{}
var d []byte
switch tagSize {
default:
return nil, fmt.Errorf("zuc/eia: invalid tag size %d, support 4/8/16 in bytes", tagSize)
case 4:
d = zuc256_d[0][:]
case 8:
d = zuc256_d[1][:]
case 16:
d = zuc256_d[2][:]
}
mac.tagSize = tagSize
mac.t = make([]uint32, mac.tagSize/4)
mac.k0 = make([]uint32, mac.tagSize/4)
switch k {
default:
return nil, fmt.Errorf("zuc/eia: invalid key size %d, expect 32 in bytes", k)
case 32: // ZUC-256
if ivLen != 23 {
return nil, fmt.Errorf("zuc/eia: invalid iv size %d, expect 23 in bytes", ivLen)
}
mac.loadKeyIV32(key, iv, d)
}
// initialization
for i := 0; i < 32; i++ {
x := mac.bitReconstruction()
w := mac.f32(x[0], x[1], x[2])
mac.enterInitMode(w >> 1)
}
// work state
x := mac.bitReconstruction()
mac.f32(x[0], x[1], x[2])
mac.enterWorkMode()
mac.initState.r1 = mac.r1
mac.initState.r2 = mac.r2
copy(mac.initState.lfsr[:], mac.lfsr[:])
mac.Reset()
return mac, nil
}
func (m *ZUC256Mac) Size() int {
return m.tagSize
}
func (m *ZUC256Mac) BlockSize() int {
return chunk
}
func (m *ZUC256Mac) Reset() {
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.t)
m.genKeywords(m.k0)
}
func (m *ZUC256Mac) block(p []byte) {
for len(p) >= chunk {
w := binary.BigEndian.Uint32(p)
k1 := m.genKeyword()
for i := 0; i < 32; i++ {
if w&0x80000000 == 0x80000000 {
for j := 0; j < m.tagSize/4; j++ {
m.t[j] ^= m.k0[j]
}
}
w <<= 1
var j int
for j = 0; j < m.tagSize/4-1; j++ {
m.k0[j] = (m.k0[j] << 1) | (m.k0[j+1] >> 31)
}
m.k0[j] = (m.k0[j] << 1) | (k1 >> 31)
k1 <<= 1
}
p = p[chunk:]
}
}
func (m *ZUC256Mac) 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 {
m.block(m.x[:])
m.nx = 0
}
p = p[n:]
}
if len(p) >= chunk {
n := len(p) &^ (chunk - 1)
m.block(p[:n])
p = p[n:]
}
if len(p) > 0 {
m.nx = copy(m.x[:], p)
}
return
}
func (m *ZUC256Mac) checkSum(additionalBits int, b byte) []byte {
if m.nx >= 4 {
panic("m.nx >= 4")
}
if m.nx > 0 || additionalBits > 0 {
m.x[m.nx] = b
w := binary.BigEndian.Uint32(m.x[:])
k1 := m.genKeyword()
for i := 0; i < 8*m.nx+additionalBits; i++ {
if w&0x80000000 == 0x80000000 {
for j := 0; j < m.tagSize/4; j++ {
m.t[j] ^= m.k0[j]
}
}
w <<= 1
var j int
for j = 0; j < m.tagSize/4-1; j++ {
m.k0[j] = (m.k0[j] << 1) | (m.k0[j+1] >> 31)
}
m.k0[j] = (m.k0[j] << 1) | (k1 >> 31)
k1 <<= 1
}
}
digest := make([]byte, m.tagSize)
for j := 0; j < m.tagSize/4; j++ {
m.t[j] ^= m.k0[j]
binary.BigEndian.PutUint32(digest[j*4:], m.t[j])
}
return digest
}
// Finish this function hash nbits data in p and return mac value
// In general, we will use byte level function, this is just for test/verify.
func (m *ZUC256Mac) 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)
return digest[:]
}
func (m *ZUC256Mac) 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[:]...)
}

253
zuc/eia256_test.go Normal file
View File

@ -0,0 +1,253 @@
package zuc
import (
"encoding/hex"
"testing"
)
var zucEIA256Tests = []struct {
key []byte
iv []byte
msg []byte
nMsgs int
mac32 string
mac64 string
mac128 string
}{
{
[]byte{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
},
[]byte{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
},
[]byte{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
},
1,
"9b972a74",
"673e54990034d38c",
"d85e54bbcb9600967084c952a1654b26",
},
{
[]byte{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
},
[]byte{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
},
[]byte{
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
},
10,
"8754f5cf",
"130dc225e72240cc",
"df1e8307b31cc62beca1ac6f8190c22f",
},
{
[]byte{
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
},
[]byte{
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
},
[]byte{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
},
1,
"1f3079b4",
"8c71394d39957725",
"a35bb274b567c48b28319f111af34fbd",
},
{
[]byte{
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
},
[]byte{
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
},
[]byte{
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
},
10,
"5c7c8b88",
"ea1dee544bb6223b",
"3a83b554be408ca5494124ed9d473205",
},
}
func Test_Finish256_32(t *testing.T) {
for i, test := range zucEIA256Tests {
h, err := NewHash256(test.key, test.iv, 4)
if err != nil {
t.Error(err)
}
for j := 0; j < test.nMsgs; j++ {
_, err = h.Write(test.msg)
if err != nil {
t.Error(err)
}
}
digest := h.Sum(nil)
if hex.EncodeToString(digest) != test.mac32 {
t.Errorf("case %d, expected=%s, result=%s\n", i+1, test.mac32, hex.EncodeToString(digest))
}
h.Reset()
for j := 0; j < test.nMsgs; j++ {
_, err = h.Write(test.msg)
if err != nil {
t.Error(err)
}
}
digest = h.Sum(nil)
if hex.EncodeToString(digest) != test.mac32 {
t.Errorf("case %d, expected=%s, result=%s\n", i+1, test.mac32, hex.EncodeToString(digest))
}
}
}
func Test_Finish256_64(t *testing.T) {
for i, test := range zucEIA256Tests {
h, err := NewHash256(test.key, test.iv, 8)
if err != nil {
t.Error(err)
}
for j := 0; j < test.nMsgs; j++ {
_, err = h.Write(test.msg)
if err != nil {
t.Error(err)
}
}
digest := h.Sum(nil)
if hex.EncodeToString(digest) != test.mac64 {
t.Errorf("case %d, expected=%s, result=%s\n", i+1, test.mac64, hex.EncodeToString(digest))
}
h.Reset()
for j := 0; j < test.nMsgs; j++ {
_, err = h.Write(test.msg)
if err != nil {
t.Error(err)
}
}
digest = h.Sum(nil)
if hex.EncodeToString(digest) != test.mac64 {
t.Errorf("case %d, expected=%s, result=%s\n", i+1, test.mac64, hex.EncodeToString(digest))
}
}
}
func Test_Finish256_128(t *testing.T) {
for i, test := range zucEIA256Tests {
h, err := NewHash256(test.key, test.iv, 16)
if err != nil {
t.Error(err)
}
for j := 0; j < test.nMsgs; j++ {
_, err = h.Write(test.msg)
if err != nil {
t.Error(err)
}
}
digest := h.Sum(nil)
if hex.EncodeToString(digest) != test.mac128 {
t.Errorf("case %d, expected=%s, result=%s\n", i+1, test.mac128, hex.EncodeToString(digest))
}
h.Reset()
for j := 0; j < test.nMsgs; j++ {
_, err = h.Write(test.msg)
if err != nil {
t.Error(err)
}
}
digest = h.Sum(nil)
if hex.EncodeToString(digest) != test.mac128 {
t.Errorf("case %d, expected=%s, result=%s\n", i+1, test.mac128, hex.EncodeToString(digest))
}
}
}
func benchmark256Size(b *testing.B, size, tagSize int) {
var key [32]byte
var iv [23]byte
var buf = make([]byte, 8192)
bench, _ := NewHash256(key[:], iv[:], tagSize)
b.SetBytes(int64(size))
sum := make([]byte, bench.Size())
for i := 0; i < b.N; i++ {
bench.Reset()
bench.Write(buf[:size])
bench.Sum(sum[:0])
}
}
func BenchmarkHash8Bytes_Tag32(b *testing.B) {
benchmark256Size(b, 8, 4)
}
func BenchmarkHash8Bytes_Tag64(b *testing.B) {
benchmark256Size(b, 8, 8)
}
func BenchmarkHash8Bytes_Tag128(b *testing.B) {
benchmark256Size(b, 8, 16)
}
func BenchmarkHash1K_Tag32(b *testing.B) {
benchmark256Size(b, 1024, 4)
}
func BenchmarkHash1K_Tag64(b *testing.B) {
benchmark256Size(b, 1024, 8)
}
func BenchmarkHash1K_Tag128(b *testing.B) {
benchmark256Size(b, 1024, 16)
}
func BenchmarkHash8K_Tag32(b *testing.B) {
benchmark256Size(b, 8192, 4)
}
func BenchmarkHash8K_Tag64(b *testing.B) {
benchmark256Size(b, 8192, 8)
}
func BenchmarkHash8K_Tag128(b *testing.B) {
benchmark256Size(b, 8192, 16)
}