mirror of
https://github.com/emmansun/gmsm.git
synced 2025-04-26 20:26:19 +08:00
zuc: move implementation detail to internal
This commit is contained in:
parent
537c80a28b
commit
a49eecd572
@ -2,8 +2,8 @@
|
||||
package zuc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/bits"
|
||||
"strconv"
|
||||
|
||||
"github.com/emmansun/gmsm/internal/byteorder"
|
||||
)
|
||||
@ -195,21 +195,33 @@ func (s *zucState32) loadKeyIV32(key, iv, d []byte) {
|
||||
s.lfsr[14] = makeFieldValue4(uint32(key[14]), uint32(d[14]|(key[31]>>4)), uint32(iv[16]), uint32(iv[9]))
|
||||
}
|
||||
|
||||
type KeySizeError int
|
||||
|
||||
func (k KeySizeError) Error() string {
|
||||
return "zuc: invalid key size " + strconv.Itoa(int(k))
|
||||
}
|
||||
|
||||
type IVSizeError int
|
||||
|
||||
func (k IVSizeError) Error() string {
|
||||
return "zuc: invalid IV size " + strconv.Itoa(int(k))
|
||||
}
|
||||
|
||||
func newZUCState(key, iv []byte) (*zucState32, error) {
|
||||
k := len(key)
|
||||
ivLen := len(iv)
|
||||
state := &zucState32{}
|
||||
switch k {
|
||||
default:
|
||||
return nil, fmt.Errorf("zuc: invalid key size %d, we support 16/32 now", k)
|
||||
return nil, KeySizeError(k)
|
||||
case 16: // ZUC-128
|
||||
if ivLen != IVSize128 {
|
||||
return nil, fmt.Errorf("zuc: invalid iv size %d, expect %d in bytes", ivLen, IVSize128)
|
||||
return nil, IVSizeError(ivLen)
|
||||
}
|
||||
state.loadKeyIV16(key, iv)
|
||||
case 32: // ZUC-256
|
||||
if ivLen != IVSize256 {
|
||||
return nil, fmt.Errorf("zuc: invalid iv size %d, expect %d in bytes", ivLen, IVSize256)
|
||||
return nil, IVSizeError(ivLen)
|
||||
}
|
||||
state.loadKeyIV32(key, iv, zuc256_d0[:])
|
||||
}
|
166
internal/zuc/eea.go
Normal file
166
internal/zuc/eea.go
Normal file
@ -0,0 +1,166 @@
|
||||
package zuc
|
||||
|
||||
import (
|
||||
"github.com/emmansun/gmsm/internal/alias"
|
||||
"github.com/emmansun/gmsm/internal/byteorder"
|
||||
"github.com/emmansun/gmsm/internal/subtle"
|
||||
)
|
||||
|
||||
const (
|
||||
// number of words in a round
|
||||
RoundWords = 32
|
||||
// number of bytes in a word
|
||||
WordSize = 4
|
||||
WordMask = WordSize - 1
|
||||
// number of bytes in a round
|
||||
RoundBytes = RoundWords * WordSize
|
||||
)
|
||||
|
||||
type eea struct {
|
||||
zucState32
|
||||
x [WordSize]byte // remaining bytes buffer
|
||||
xLen int // number of remaining bytes
|
||||
initState zucState32 // initial state for reset
|
||||
used uint64 // number of key bytes processed, current offset
|
||||
}
|
||||
|
||||
// NewCipher create a stream cipher based on key and iv aguments.
|
||||
// The key must be 16 bytes long and iv must be 16 bytes long for zuc 128;
|
||||
// or the key must be 32 bytes long and iv must be 23 bytes long for zuc 256;
|
||||
// otherwise, an error will be returned.
|
||||
func NewCipher(key, iv []byte) (*eea, error) {
|
||||
s, err := newZUCState(key, iv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c := new(eea)
|
||||
c.zucState32 = *s
|
||||
c.initState = *s
|
||||
c.used = 0
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// NewEEACipher create a stream cipher based on key, count, bearer and direction arguments according specification.
|
||||
// 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 NewEEACipher(key []byte, count, bearer, direction uint32) (*eea, error) {
|
||||
iv := make([]byte, 16)
|
||||
byteorder.BEPutUint32(iv, count)
|
||||
copy(iv[8:12], iv[:4])
|
||||
iv[4] = byte(((bearer << 1) | (direction & 1)) << 2)
|
||||
iv[12] = iv[4]
|
||||
return NewCipher(key, iv)
|
||||
}
|
||||
|
||||
func genKeyStreamRev32Generic(keyStream []byte, pState *zucState32) {
|
||||
for len(keyStream) >= WordSize {
|
||||
z := genKeyword(pState)
|
||||
byteorder.BEPutUint32(keyStream, z)
|
||||
keyStream = keyStream[WordSize:]
|
||||
}
|
||||
}
|
||||
|
||||
func (c *eea) XORKeyStream(dst, src []byte) {
|
||||
if len(dst) < len(src) {
|
||||
panic("zuc: output smaller than input")
|
||||
}
|
||||
if alias.InexactOverlap(dst[:len(src)], src) {
|
||||
panic("zuc: invalid buffer overlap")
|
||||
}
|
||||
used := len(src)
|
||||
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])
|
||||
c.used += uint64(used)
|
||||
return
|
||||
}
|
||||
}
|
||||
var keyBytes [RoundBytes]byte
|
||||
for len(src) >= RoundBytes {
|
||||
genKeyStreamRev32(keyBytes[:], &c.zucState32)
|
||||
subtle.XORBytes(dst, src, keyBytes[:])
|
||||
dst = dst[RoundBytes:]
|
||||
src = src[RoundBytes:]
|
||||
}
|
||||
if len(src) > 0 {
|
||||
byteLen := (len(src) + WordMask) &^ WordMask
|
||||
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])
|
||||
}
|
||||
}
|
||||
c.used += uint64(used)
|
||||
}
|
||||
|
||||
func (c *eea) reset() {
|
||||
c.zucState32 = c.initState
|
||||
c.xLen = 0
|
||||
c.used = 0
|
||||
}
|
||||
|
||||
// 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.
|
||||
// If the offset is equal to the current offset, the function behaves the same as XORKeyStream.
|
||||
// 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) {
|
||||
if offset < c.used {
|
||||
c.reset()
|
||||
}
|
||||
if offset == c.used {
|
||||
return
|
||||
}
|
||||
gap := offset - c.used
|
||||
if gap <= uint64(c.xLen) {
|
||||
// offset is within the remaining key bytes
|
||||
c.xLen -= int(gap)
|
||||
c.used += gap
|
||||
if c.xLen > 0 {
|
||||
// adjust remaining key bytes
|
||||
copy(c.x[:], c.x[gap:])
|
||||
}
|
||||
return
|
||||
}
|
||||
// consumed all remaining key bytes first
|
||||
if c.xLen > 0 {
|
||||
c.used += uint64(c.xLen)
|
||||
gap -= uint64(c.xLen)
|
||||
c.xLen = 0
|
||||
}
|
||||
|
||||
// forward the state to the offset
|
||||
c.used += gap
|
||||
stepLen := uint64(RoundBytes)
|
||||
var keyStream [RoundWords]uint32
|
||||
for gap >= stepLen {
|
||||
genKeyStream(keyStream[:], &c.zucState32)
|
||||
gap -= stepLen
|
||||
}
|
||||
|
||||
if gap > 0 {
|
||||
numWords := (gap + WordMask) / WordSize
|
||||
genKeyStream(keyStream[:numWords], &c.zucState32)
|
||||
partiallyUsed := int(gap & WordMask)
|
||||
if partiallyUsed > 0 {
|
||||
// save remaining key bytes (less than 4 bytes)
|
||||
c.xLen = WordSize - partiallyUsed
|
||||
byteorder.BEPutUint32(c.x[:], keyStream[numWords-1])
|
||||
copy(c.x[:], c.x[partiallyUsed:])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *eea) XORKeyStreamAt(dst, src []byte, offset uint64) {
|
||||
c.seek(offset)
|
||||
c.XORKeyStream(dst, src)
|
||||
}
|
259
internal/zuc/eea_test.go
Normal file
259
internal/zuc/eea_test.go
Normal file
@ -0,0 +1,259 @@
|
||||
package zuc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/cipher"
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
|
||||
"github.com/emmansun/gmsm/internal/cryptotest"
|
||||
)
|
||||
|
||||
var zucEEATests = []struct {
|
||||
key string
|
||||
count uint32
|
||||
bearer uint32
|
||||
direction uint32
|
||||
in string
|
||||
out string
|
||||
}{
|
||||
{
|
||||
"173d14ba5003731d7a60049470f00a29",
|
||||
0x66035492,
|
||||
0xf,
|
||||
0,
|
||||
"6cf65340735552ab0c9752fa6f9025fe0bd675d9005875b2",
|
||||
"a6c85fc66afb8533aafc2518dfe784940ee1e4b030238cc8",
|
||||
},
|
||||
{
|
||||
"e5bd3ea0eb55ade866c6ac58bd54302a",
|
||||
0x56823,
|
||||
0x18,
|
||||
1,
|
||||
"14a8ef693d678507bbe7270a7f67ff5006c3525b9807e467c4e56000ba338f5d429559036751822246c80d3b38f07f4be2d8ff5805f5132229bde93bbbdcaf382bf1ee972fbf9977bada8945847a2a6c9ad34a667554e04d1f7fa2c33241bd8f01ba220d",
|
||||
"131d43e0dea1be5c5a1bfd971d852cbf712d7b4f57961fea3208afa8bca433f456ad09c7417e58bc69cf8866d1353f74865e80781d202dfb3ecff7fcbc3b190fe82a204ed0e350fc0f6f2613b2f2bca6df5a473a57a4a00d985ebad880d6f23864a07b01",
|
||||
},
|
||||
{
|
||||
"e13fed21b46e4e7ec31253b2bb17b3e0",
|
||||
0x2738cdaa,
|
||||
0x1a,
|
||||
0,
|
||||
"8d74e20d54894e06d3cb13cb3933065e8674be62adb1c72b3a646965ab63cb7b7854dfdc27e84929f49c64b872a490b13f957b64827e71f41fbd4269a42c97f824537027f86e9f4ad82d1df451690fdd98b6d03f3a0ebe3a312d6b840ba5a1820b2a2c9709c090d245ed267cf845ae41fa975d3333ac3009fd40eba9eb5b885714b768b697138baf21380eca49f644d48689e4215760b906739f0d2b3f091133ca15d981cbe401baf72d05ace05cccb2d297f4ef6a5f58d91246cfa77215b892ab441d5278452795ccb7f5d79057a1c4f77f80d46db2033cb79bedf8e60551ce10c667f62a97abafabbcd6772018df96a282ea737ce2cb331211f60d5354ce78f9918d9c206ca042c9b62387dd709604a50af16d8d35a8906be484cf2e74a9289940364353249b27b4c9ae29eddfc7da6418791a4e7baa0660fa64511f2d685cc3a5ff70e0d2b74292e3b8a0cd6b04b1c790b8ead2703708540dea2fc09c3da770f65449c84d817a4f551055e19ab85018a0028b71a144d96791e9a3577933504eee0060340c69d274e1bf9d805dcbcc1a6faa976800b6ff2b671dc463652fa8a33ee50974c1c21be01eabb2167430269d72ee511c9dde30797c9a25d86ce74f5b961be5fdfb6807814039e7137636bd1d7fa9e09efd2007505906a5ac45dfdeed7757bbee745749c29633350bee0ea6f409df458016",
|
||||
"94eaa4aa30a57137ddf09b97b25618a20a13e2f10fa5bf8161a879cc2ae797a6b4cf2d9df31debb9905ccfec97de605d21c61ab8531b7f3c9da5f03931f8a0642de48211f5f52ffea10f392a047669985da454a28f080961a6c2b62daa17f33cd60a4971f48d2d909394a55f48117ace43d708e6b77d3dc46d8bc017d4d1abb77b7428c042b06f2f99d8d07c9879d99600127a31985f1099bbd7d6c1519ede8f5eeb4a610b349ac01ea2350691756bd105c974a53eddb35d1d4100b012e522ab41f4c5f2fde76b59cb8b96d885cfe4080d1328a0d636cc0edc05800b76acca8fef672084d1f52a8bbd8e0993320992c7ffbae17c408441e0ee883fc8a8b05e22f5ff7f8d1b48c74c468c467a028f09fd7ce91109a570a2d5c4d5f4fa18c5dd3e4562afe24ef771901f59af645898acef088abae07e92d52eb2de55045bb1b7c4164ef2d7a6cac15eeb926d7ea2f08b66e1f759f3aee44614725aa3c7482b30844c143ff87b53f1e583c501257dddd096b81268daa303f17234c2333541f0bb8e190648c5807c866d7193228609adb948686f7de294a802cc38f7fe5208f5ea3196d0167b9bdd02f0d2a5221ca508f893af5c4b4bb9f4f520fd84289b3dbe7e61497a7e2a584037ea637b6981127174af57b471df4b2768fd79c1540fb3edf2ea22cb69bec0cf8d933d9c6fdd645e850591cca3d62c0c",
|
||||
},
|
||||
}
|
||||
|
||||
func Test_EEA(t *testing.T) {
|
||||
for i, test := range zucEEATests {
|
||||
key, err := hex.DecodeString(test.key)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
c, err := NewEEACipher(key, test.count, test.bearer, test.direction)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
in, err := hex.DecodeString(test.in)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
out := make([]byte, len(in))
|
||||
copy(out, in)
|
||||
c.XORKeyStream(out, out)
|
||||
if hex.EncodeToString(out) != test.out {
|
||||
t.Errorf("case %d, expected=%s, result=%s\n", i+1, test.out, hex.EncodeToString(out))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEEAStream(t *testing.T) {
|
||||
cryptotest.TestStream(t, func() cipher.Stream {
|
||||
key, _ := hex.DecodeString(zucEEATests[0].key)
|
||||
c, _ := NewEEACipher(key, zucEEATests[0].count, zucEEATests[0].bearer, zucEEATests[0].direction)
|
||||
return c
|
||||
})
|
||||
}
|
||||
|
||||
func TestXORStreamAt(t *testing.T) {
|
||||
key, err := hex.DecodeString(zucEEATests[0].key)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
c, err := NewEEACipher(key, zucEEATests[0].count, zucEEATests[0].bearer, zucEEATests[0].direction)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
src := make([]byte, 1000)
|
||||
expected := make([]byte, 1000)
|
||||
dst := make([]byte, 1000)
|
||||
c.XORKeyStream(expected, src)
|
||||
|
||||
t.Run("Reset and forward to offset", func(t *testing.T) {
|
||||
for i := 0; i < 65; i++ {
|
||||
c.XORKeyStreamAt(dst[i:], src[i:], uint64(i))
|
||||
if !bytes.Equal(expected[i:], dst[i:]) {
|
||||
t.Errorf("At %d, expected=%x, result=%x\n", i, expected[i:], dst[i:])
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Offset equals to the current position", func(t *testing.T) {
|
||||
c.XORKeyStreamAt(dst[:16], src[:16], 0)
|
||||
c.XORKeyStreamAt(dst[16:32], src[16:32], 16)
|
||||
if !bytes.Equal(dst[:32], expected[:32]) {
|
||||
t.Errorf("expected=%x, result=%x\n", expected[:32], dst[:32])
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Jump and forward (incomplete word): gap > xLen", func(t *testing.T) {
|
||||
for i := 0; i < 4; i++ {
|
||||
c.XORKeyStreamAt(dst[i:16], src[i:16], uint64(i))
|
||||
c.XORKeyStreamAt(dst[32:64], src[32:64], 32)
|
||||
if !bytes.Equal(dst[32:64], expected[32:64]) {
|
||||
t.Errorf("expected=%x, result=%x\n", expected[32:64], dst[32:64])
|
||||
}
|
||||
}
|
||||
for i := 1; i < 4; i++ {
|
||||
c.XORKeyStreamAt(dst[:i], src[:i], 0)
|
||||
c.XORKeyStreamAt(dst[32:64], src[32:64], 32)
|
||||
if !bytes.Equal(dst[32:64], expected[32:64]) {
|
||||
t.Errorf("expected=%x, result=%x\n", expected[32:64], dst[32:64])
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Jump and forward (incomplete word): gap <= xLen", func(t *testing.T) {
|
||||
c.XORKeyStreamAt(dst[:1], src[:1], 0)
|
||||
c.XORKeyStreamAt(dst[3:16], src[3:16], 3)
|
||||
if !bytes.Equal(dst[3:16], expected[3:16]) {
|
||||
t.Errorf("expected=%x, result=%x\n", expected[3:16], dst[3:16])
|
||||
}
|
||||
c.XORKeyStreamAt(dst[:1], src[:1], 0)
|
||||
c.XORKeyStreamAt(dst[4:16], src[4:16], 4)
|
||||
if !bytes.Equal(dst[4:16], expected[4:16]) {
|
||||
t.Errorf("expected=%x, result=%x\n", expected[3:16], dst[3:16])
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Jump and forward (skipped keys more than 128)", func(t *testing.T) {
|
||||
// test offset - used > 128 bytes case
|
||||
c.XORKeyStreamAt(dst[:16], src[:16], 0)
|
||||
offset := 700
|
||||
c.XORKeyStreamAt(dst[offset:], src[offset:], uint64(offset))
|
||||
if !bytes.Equal(dst[offset:], expected[offset:]) {
|
||||
t.Errorf("expected=%x, result=%x\n", expected[offset:], dst[offset:])
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Mixed XORKeyStreamAt with XORKeyStream", func(t *testing.T) {
|
||||
// XORKeyStreamAt with XORKeyStream
|
||||
c.XORKeyStreamAt(dst[:16], src[:16], 0)
|
||||
c.XORKeyStream(dst[16:31], src[16:31])
|
||||
c.XORKeyStreamAt(dst[31:64], src[31:64], 31)
|
||||
c.XORKeyStream(dst[64:128], src[64:128])
|
||||
if !bytes.Equal(dst[:128], expected[:128]) {
|
||||
t.Errorf("expected=%x, result=%x\n", expected[:128], dst[:128])
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("BufferOverlap", func(t *testing.T) {
|
||||
buff := make([]byte, 100)
|
||||
// Make src and dst slices point to same array with inexact overlap
|
||||
src := buff[:32]
|
||||
dst := buff[1 : 32+1]
|
||||
cryptotest.MustPanic(t, "invalid buffer overlap", func() { c.XORKeyStreamAt(dst, src, 0) })
|
||||
|
||||
// Only overlap on one byte
|
||||
src = buff[:32]
|
||||
dst = buff[32-1 : 2*32-1]
|
||||
cryptotest.MustPanic(t, "invalid buffer overlap", func() { c.XORKeyStreamAt(dst, src, 0) })
|
||||
|
||||
// src comes after dst with one byte overlap
|
||||
src = buff[32-1 : 2*32-1]
|
||||
dst = buff[:32]
|
||||
cryptotest.MustPanic(t, "invalid buffer overlap", func() { c.XORKeyStreamAt(dst, src, 0) })
|
||||
|
||||
// length of dst is less than src
|
||||
src = buff[:32]
|
||||
dst = buff[32:63]
|
||||
cryptotest.MustPanic(t, "output smaller than input", func() { c.XORKeyStreamAt(dst, src, 0) })
|
||||
})
|
||||
}
|
||||
|
||||
func TestIssue284(t *testing.T) {
|
||||
key, err := hex.DecodeString(zucEEATests[0].key)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
c, err := NewEEACipher(key, zucEEATests[0].count, zucEEATests[0].bearer, zucEEATests[0].direction)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
src := make([]byte, RoundBytes*2)
|
||||
expected := make([]byte, RoundBytes*2)
|
||||
dst := make([]byte, RoundBytes*2)
|
||||
c.XORKeyStream(expected, src)
|
||||
|
||||
for i := RoundBytes - 3; i < RoundBytes+5; i++ {
|
||||
c.XORKeyStreamAt(dst, src[:i], 0)
|
||||
c.XORKeyStream(dst[i:], src[i:])
|
||||
if !bytes.Equal(expected, dst) {
|
||||
t.Fatalf("failed for len %v", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func benchmarkStream(b *testing.B, buf []byte) {
|
||||
b.SetBytes(int64(len(buf)))
|
||||
|
||||
var key [16]byte
|
||||
var iv [16]byte
|
||||
|
||||
stream, _ := NewCipher(key[:], iv[:])
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
stream.XORKeyStream(buf, buf)
|
||||
}
|
||||
}
|
||||
|
||||
const almost1K = 1024 - 5
|
||||
const almost8K = 8*1024 - 5
|
||||
|
||||
func BenchmarkEncrypt1K(b *testing.B) {
|
||||
benchmarkStream(b, make([]byte, almost1K))
|
||||
}
|
||||
|
||||
func BenchmarkEncrypt8K(b *testing.B) {
|
||||
benchmarkStream(b, make([]byte, almost8K))
|
||||
}
|
||||
|
||||
func benchmarkSeek(b *testing.B, offset uint64) {
|
||||
var key [16]byte
|
||||
var iv [16]byte
|
||||
|
||||
stream, _ := NewCipher(key[:], iv[:])
|
||||
|
||||
eea, ok := stream.(*eea)
|
||||
if !ok {
|
||||
b.Fatal("not an eea")
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
eea.reset()
|
||||
eea.seek(offset)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSeek1K(b *testing.B) {
|
||||
benchmarkSeek(b, 1024)
|
||||
}
|
||||
|
||||
func BenchmarkSeek8K(b *testing.B) {
|
||||
benchmarkSeek(b, 8*1024)
|
||||
}
|
||||
|
||||
func BenchmarkSeek1M(b *testing.B) {
|
||||
benchmarkSeek(b, 1024*1024)
|
||||
}
|
252
internal/zuc/eia.go
Normal file
252
internal/zuc/eia.go
Normal file
@ -0,0 +1,252 @@
|
||||
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[:]...)
|
||||
}
|
218
internal/zuc/eia_test.go
Normal file
218
internal/zuc/eia_test.go
Normal file
@ -0,0 +1,218 @@
|
||||
package zuc
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"hash"
|
||||
"testing"
|
||||
|
||||
"github.com/emmansun/gmsm/internal/byteorder"
|
||||
"github.com/emmansun/gmsm/internal/cryptotest"
|
||||
)
|
||||
|
||||
var key [16]byte
|
||||
var iv [16]byte
|
||||
|
||||
var buf = make([]byte, 8192)
|
||||
|
||||
func benchmarkSize(b *testing.B, size int) {
|
||||
bench, _ := NewHash(key[:], iv[:])
|
||||
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(b *testing.B) {
|
||||
benchmarkSize(b, 8)
|
||||
}
|
||||
|
||||
func BenchmarkHash1K(b *testing.B) {
|
||||
benchmarkSize(b, 1024)
|
||||
}
|
||||
|
||||
func BenchmarkHash8K(b *testing.B) {
|
||||
benchmarkSize(b, 8192)
|
||||
}
|
||||
|
||||
var zucEIATests = []struct {
|
||||
key []byte
|
||||
count uint32
|
||||
bearer uint32
|
||||
direction uint32
|
||||
in []uint32
|
||||
nbits int
|
||||
mac string
|
||||
}{
|
||||
{
|
||||
[]byte{
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
},
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
[]uint32{0x00000000},
|
||||
1,
|
||||
"c8a9595e",
|
||||
},
|
||||
{
|
||||
[]byte{
|
||||
0xc9, 0xe6, 0xce, 0xc4, 0x60, 0x7c, 0x72, 0xdb,
|
||||
0x00, 0x0a, 0xef, 0xa8, 0x83, 0x85, 0xab, 0x0a,
|
||||
},
|
||||
0xa94059da,
|
||||
0x0a,
|
||||
1,
|
||||
[]uint32{
|
||||
0x983b41d4, 0x7d780c9e, 0x1ad11d7e, 0xb70391b1,
|
||||
0xde0b35da, 0x2dc62f83, 0xe7b78d63, 0x06ca0ea0,
|
||||
0x7e941b7b, 0xe91348f9, 0xfcb170e2, 0x217fecd9,
|
||||
0x7f9f68ad, 0xb16e5d7d, 0x21e569d2, 0x80ed775c,
|
||||
0xebde3f40, 0x93c53881, 0x00000000,
|
||||
},
|
||||
0x241,
|
||||
"fae8ff0b",
|
||||
},
|
||||
{
|
||||
[]byte{
|
||||
0x6b, 0x8b, 0x08, 0xee, 0x79, 0xe0, 0xb5, 0x98,
|
||||
0x2d, 0x6d, 0x12, 0x8e, 0xa9, 0xf2, 0x20, 0xcb,
|
||||
},
|
||||
0x561eb2dd,
|
||||
0x1c,
|
||||
0,
|
||||
[]uint32{
|
||||
0x5bad7247, 0x10ba1c56, 0xd5a315f8, 0xd40f6e09,
|
||||
0x3780be8e, 0x8de07b69, 0x92432018, 0xe08ed96a,
|
||||
0x5734af8b, 0xad8a575d, 0x3a1f162f, 0x85045cc7,
|
||||
0x70925571, 0xd9f5b94e, 0x454a77c1, 0x6e72936b,
|
||||
0xf016ae15, 0x7499f054, 0x3b5d52ca, 0xa6dbeab6,
|
||||
0x97d2bb73, 0xe41b8075, 0xdce79b4b, 0x86044f66,
|
||||
0x1d4485a5, 0x43dd7860, 0x6e0419e8, 0x059859d3,
|
||||
0xcb2b67ce, 0x0977603f, 0x81ff839e, 0x33185954,
|
||||
0x4cfbc8d0, 0x0fef1a4c, 0x8510fb54, 0x7d6b06c6,
|
||||
0x11ef44f1, 0xbce107cf, 0xa45a06aa, 0xb360152b,
|
||||
0x28dc1ebe, 0x6f7fe09b, 0x0516f9a5, 0xb02a1bd8,
|
||||
0x4bb0181e, 0x2e89e19b, 0xd8125930, 0xd178682f,
|
||||
0x3862dc51, 0xb636f04e, 0x720c47c3, 0xce51ad70,
|
||||
0xd94b9b22, 0x55fbae90, 0x6549f499, 0xf8c6d399,
|
||||
0x47ed5e5d, 0xf8e2def1, 0x13253e7b, 0x08d0a76b,
|
||||
0x6bfc68c8, 0x12f375c7, 0x9b8fe5fd, 0x85976aa6,
|
||||
0xd46b4a23, 0x39d8ae51, 0x47f680fb, 0xe70f978b,
|
||||
0x38effd7b, 0x2f7866a2, 0x2554e193, 0xa94e98a6,
|
||||
0x8b74bd25, 0xbb2b3f5f, 0xb0a5fd59, 0x887f9ab6,
|
||||
0x8159b717, 0x8d5b7b67, 0x7cb546bf, 0x41eadca2,
|
||||
0x16fc1085, 0x0128f8bd, 0xef5c8d89, 0xf96afa4f,
|
||||
0xa8b54885, 0x565ed838, 0xa950fee5, 0xf1c3b0a4,
|
||||
0xf6fb71e5, 0x4dfd169e, 0x82cecc72, 0x66c850e6,
|
||||
0x7c5ef0ba, 0x960f5214, 0x060e71eb, 0x172a75fc,
|
||||
0x1486835c, 0xbea65344, 0x65b055c9, 0x6a72e410,
|
||||
0x52241823, 0x25d83041, 0x4b40214d, 0xaa8091d2,
|
||||
0xe0fb010a, 0xe15c6de9, 0x0850973b, 0xdf1e423b,
|
||||
0xe148a237, 0xb87a0c9f, 0x34d4b476, 0x05b803d7,
|
||||
0x43a86a90, 0x399a4af3, 0x96d3a120, 0x0a62f3d9,
|
||||
0x507962e8, 0xe5bee6d3, 0xda2bb3f7, 0x237664ac,
|
||||
0x7a292823, 0x900bc635, 0x03b29e80, 0xd63f6067,
|
||||
0xbf8e1716, 0xac25beba, 0x350deb62, 0xa99fe031,
|
||||
0x85eb4f69, 0x937ecd38, 0x7941fda5, 0x44ba67db,
|
||||
0x09117749, 0x38b01827, 0xbcc69c92, 0xb3f772a9,
|
||||
0xd2859ef0, 0x03398b1f, 0x6bbad7b5, 0x74f7989a,
|
||||
0x1d10b2df, 0x798e0dbf, 0x30d65874, 0x64d24878,
|
||||
0xcd00c0ea, 0xee8a1a0c, 0xc753a279, 0x79e11b41,
|
||||
0xdb1de3d5, 0x038afaf4, 0x9f5c682c, 0x3748d8a3,
|
||||
0xa9ec54e6, 0xa371275f, 0x1683510f, 0x8e4f9093,
|
||||
0x8f9ab6e1, 0x34c2cfdf, 0x4841cba8, 0x8e0cff2b,
|
||||
0x0bcc8e6a, 0xdcb71109, 0xb5198fec, 0xf1bb7e5c,
|
||||
0x531aca50, 0xa56a8a3b, 0x6de59862, 0xd41fa113,
|
||||
0xd9cd9578, 0x08f08571, 0xd9a4bb79, 0x2af271f6,
|
||||
0xcc6dbb8d, 0xc7ec36e3, 0x6be1ed30, 0x8164c31c,
|
||||
0x7c0afc54, 0x1c000000,
|
||||
},
|
||||
0x1626,
|
||||
"0ca12792",
|
||||
},
|
||||
}
|
||||
|
||||
func TestEIA_Finish(t *testing.T) {
|
||||
for i, test := range zucEIATests {
|
||||
h, err := NewEIAHash(test.key, test.count, test.bearer, test.direction)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
in := make([]byte, len(test.in)*4)
|
||||
for j, v := range test.in {
|
||||
byteorder.BEPutUint32(in[j*4:], v)
|
||||
}
|
||||
|
||||
mac := h.Finish(in, test.nbits)
|
||||
if hex.EncodeToString(mac) != test.mac {
|
||||
t.Errorf("case %d, expected=%s, result=%s\n", i+1, test.mac, hex.EncodeToString(mac))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEIA_NewHash(t *testing.T) {
|
||||
key := make([]byte, 16)
|
||||
iv := make([]byte, 16)
|
||||
_, err := NewHash(key[:1], iv)
|
||||
if err == nil {
|
||||
t.Fatal("error is expected")
|
||||
}
|
||||
|
||||
_, err = NewHash(key, iv[:1])
|
||||
if err == nil {
|
||||
t.Fatal("error is expected")
|
||||
}
|
||||
|
||||
h, err := NewHash(key, iv)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if h.Size() != 4 {
|
||||
t.Fatal("eia3 mac size should be 4 bytes")
|
||||
}
|
||||
if h.BlockSize() != 16 {
|
||||
t.Fatal("current eia3 implementation's block size should be 16 bytes")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestEIA_Sum(t *testing.T) {
|
||||
expected := "6c2db416"
|
||||
h, err := NewEIAHash(zucEIATests[1].key, zucEIATests[1].count, zucEIATests[1].bearer, zucEIATests[1].direction)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = h.Write([]byte("emmansun"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = h.Write([]byte("shangmi1"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = h.Write([]byte("emmansun shangmi"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = h.Write([]byte("emmansun shangmi 1234"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
mac := h.Sum(nil)
|
||||
if hex.EncodeToString(mac) != expected {
|
||||
t.Errorf("expected=%s, result=%s\n", expected, hex.EncodeToString(mac))
|
||||
}
|
||||
}
|
||||
|
||||
func TestEIAHash(t *testing.T) {
|
||||
t.Run("EIA-128", func(t *testing.T) {
|
||||
cryptotest.TestHash(t, func() hash.Hash {
|
||||
h, _ := NewEIAHash(zucEIATests[0].key, zucEIATests[0].count, zucEIATests[0].bearer, zucEIATests[0].direction)
|
||||
return h
|
||||
})
|
||||
})
|
||||
}
|
146
zuc/eea.go
146
zuc/eea.go
@ -1,13 +1,16 @@
|
||||
// Package zuc implements ShangMi(SM) zuc stream cipher and integrity algorithm.
|
||||
package zuc
|
||||
|
||||
import (
|
||||
"github.com/emmansun/gmsm/cipher"
|
||||
"github.com/emmansun/gmsm/internal/alias"
|
||||
"github.com/emmansun/gmsm/internal/byteorder"
|
||||
"github.com/emmansun/gmsm/internal/subtle"
|
||||
"github.com/emmansun/gmsm/internal/zuc"
|
||||
)
|
||||
|
||||
const (
|
||||
// IV size in bytes for zuc 128
|
||||
IVSize128 = 16
|
||||
// IV size in bytes for zuc 256
|
||||
IVSize256 = 23
|
||||
// number of words in a round
|
||||
RoundWords = 32
|
||||
// number of bytes in a word
|
||||
@ -17,28 +20,12 @@ const (
|
||||
RoundBytes = RoundWords * WordSize
|
||||
)
|
||||
|
||||
type eea struct {
|
||||
zucState32
|
||||
x [WordSize]byte // remaining bytes buffer
|
||||
xLen int // number of remaining bytes
|
||||
initState zucState32 // initial state for reset
|
||||
used uint64 // number of key bytes processed, current offset
|
||||
}
|
||||
|
||||
// NewCipher create a stream cipher based on key and iv aguments.
|
||||
// The key must be 16 bytes long and iv must be 16 bytes long for zuc 128;
|
||||
// or the key must be 32 bytes long and iv must be 23 bytes long for zuc 256;
|
||||
// otherwise, an error will be returned.
|
||||
func NewCipher(key, iv []byte) (cipher.SeekableStream, error) {
|
||||
s, err := newZUCState(key, iv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c := new(eea)
|
||||
c.zucState32 = *s
|
||||
c.initState = *s
|
||||
c.used = 0
|
||||
return c, nil
|
||||
return zuc.NewCipher(key, iv)
|
||||
}
|
||||
|
||||
// NewEEACipher create a stream cipher based on key, count, bearer and direction arguments according specification.
|
||||
@ -46,122 +33,5 @@ func NewCipher(key, iv []byte) (cipher.SeekableStream, error) {
|
||||
// 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 NewEEACipher(key []byte, count, bearer, direction uint32) (cipher.SeekableStream, error) {
|
||||
iv := make([]byte, 16)
|
||||
byteorder.BEPutUint32(iv, count)
|
||||
copy(iv[8:12], iv[:4])
|
||||
iv[4] = byte(((bearer << 1) | (direction & 1)) << 2)
|
||||
iv[12] = iv[4]
|
||||
return NewCipher(key, iv)
|
||||
}
|
||||
|
||||
func genKeyStreamRev32Generic(keyStream []byte, pState *zucState32) {
|
||||
for len(keyStream) >= WordSize {
|
||||
z := genKeyword(pState)
|
||||
byteorder.BEPutUint32(keyStream, z)
|
||||
keyStream = keyStream[WordSize:]
|
||||
}
|
||||
}
|
||||
|
||||
func (c *eea) XORKeyStream(dst, src []byte) {
|
||||
if len(dst) < len(src) {
|
||||
panic("zuc: output smaller than input")
|
||||
}
|
||||
if alias.InexactOverlap(dst[:len(src)], src) {
|
||||
panic("zuc: invalid buffer overlap")
|
||||
}
|
||||
used := len(src)
|
||||
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])
|
||||
c.used += uint64(used)
|
||||
return
|
||||
}
|
||||
}
|
||||
var keyBytes [RoundBytes]byte
|
||||
for len(src) >= RoundBytes {
|
||||
genKeyStreamRev32(keyBytes[:], &c.zucState32)
|
||||
subtle.XORBytes(dst, src, keyBytes[:])
|
||||
dst = dst[RoundBytes:]
|
||||
src = src[RoundBytes:]
|
||||
}
|
||||
if len(src) > 0 {
|
||||
byteLen := (len(src) + WordMask) &^ WordMask
|
||||
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])
|
||||
}
|
||||
}
|
||||
c.used += uint64(used)
|
||||
}
|
||||
|
||||
func (c *eea) reset() {
|
||||
c.zucState32 = c.initState
|
||||
c.xLen = 0
|
||||
c.used = 0
|
||||
}
|
||||
|
||||
// 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.
|
||||
// If the offset is equal to the current offset, the function behaves the same as XORKeyStream.
|
||||
// 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) {
|
||||
if offset < c.used {
|
||||
c.reset()
|
||||
}
|
||||
if offset == c.used {
|
||||
return
|
||||
}
|
||||
gap := offset - c.used
|
||||
if gap <= uint64(c.xLen) {
|
||||
// offset is within the remaining key bytes
|
||||
c.xLen -= int(gap)
|
||||
c.used += gap
|
||||
if c.xLen > 0 {
|
||||
// adjust remaining key bytes
|
||||
copy(c.x[:], c.x[gap:])
|
||||
}
|
||||
return
|
||||
}
|
||||
// consumed all remaining key bytes first
|
||||
if c.xLen > 0 {
|
||||
c.used += uint64(c.xLen)
|
||||
gap -= uint64(c.xLen)
|
||||
c.xLen = 0
|
||||
}
|
||||
|
||||
// forward the state to the offset
|
||||
c.used += gap
|
||||
stepLen := uint64(RoundBytes)
|
||||
var keyStream [RoundWords]uint32
|
||||
for gap >= stepLen {
|
||||
genKeyStream(keyStream[:], &c.zucState32)
|
||||
gap -= stepLen
|
||||
}
|
||||
|
||||
if gap > 0 {
|
||||
numWords := (gap + WordMask) / WordSize
|
||||
genKeyStream(keyStream[:numWords], &c.zucState32)
|
||||
partiallyUsed := int(gap & WordMask)
|
||||
if partiallyUsed > 0 {
|
||||
// save remaining key bytes (less than 4 bytes)
|
||||
c.xLen = WordSize - partiallyUsed
|
||||
byteorder.BEPutUint32(c.x[:], keyStream[numWords-1])
|
||||
copy(c.x[:], c.x[partiallyUsed:])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *eea) XORKeyStreamAt(dst, src []byte, offset uint64) {
|
||||
c.seek(offset)
|
||||
c.XORKeyStream(dst, src)
|
||||
return zuc.NewEEACipher(key, count, bearer, direction)
|
||||
}
|
||||
|
@ -227,33 +227,3 @@ func BenchmarkEncrypt1K(b *testing.B) {
|
||||
func BenchmarkEncrypt8K(b *testing.B) {
|
||||
benchmarkStream(b, make([]byte, almost8K))
|
||||
}
|
||||
|
||||
func benchmarkSeek(b *testing.B, offset uint64) {
|
||||
var key [16]byte
|
||||
var iv [16]byte
|
||||
|
||||
stream, _ := NewCipher(key[:], iv[:])
|
||||
|
||||
eea, ok := stream.(*eea)
|
||||
if !ok {
|
||||
b.Fatal("not an eea")
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
eea.reset()
|
||||
eea.seek(offset)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSeek1K(b *testing.B) {
|
||||
benchmarkSeek(b, 1024)
|
||||
}
|
||||
|
||||
func BenchmarkSeek8K(b *testing.B) {
|
||||
benchmarkSeek(b, 8*1024)
|
||||
}
|
||||
|
||||
func BenchmarkSeek1M(b *testing.B) {
|
||||
benchmarkSeek(b, 1024*1024)
|
||||
}
|
||||
|
260
zuc/eia.go
260
zuc/eia.go
@ -1,252 +1,36 @@
|
||||
// Package zuc provides implementations of the ZUC stream cipher and its related
|
||||
// cryptographic functions.
|
||||
package zuc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"hash"
|
||||
|
||||
"github.com/emmansun/gmsm/internal/byteorder"
|
||||
"github.com/emmansun/gmsm/internal/zuc"
|
||||
)
|
||||
|
||||
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
|
||||
// EIA is an interface that extends the hash.Hash interface with an additional
|
||||
// Finish method, which finalizes the hash computation with a specified number
|
||||
// of bits.
|
||||
type EIA interface {
|
||||
hash.Hash
|
||||
Finish(p []byte, nbits int) []byte
|
||||
}
|
||||
|
||||
// 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)
|
||||
// NewHash creates a new instance of the ZUC-based hash function with the given
|
||||
// key and initialization vector (IV).
|
||||
func NewHash(key, iv []byte) (EIA, error) {
|
||||
return zuc.NewHash(key, iv)
|
||||
}
|
||||
|
||||
// initialization
|
||||
for i := 0; i < 32; i++ {
|
||||
mac.bitReorganization()
|
||||
w := mac.f32()
|
||||
mac.enterInitMode(w >> 1)
|
||||
// NewEIAHash creates a new instance of the ZUC-based EIA (Encryption Integrity
|
||||
// Algorithm) hash function with the given key, count, bearer, and direction.
|
||||
func NewEIAHash(key []byte, count, bearer, direction uint32) (EIA, error) {
|
||||
return zuc.NewEIAHash(key, count, bearer, direction)
|
||||
}
|
||||
|
||||
// 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[:]...)
|
||||
// NewHash256 creates a new instance of the ZUC256-based hash function with the
|
||||
// given key, initialization vector (IV), and tag size.
|
||||
func NewHash256(key, iv []byte, tagSize int) (EIA, error) {
|
||||
return zuc.NewHash256(key, iv, tagSize)
|
||||
}
|
||||
|
326
zuc/eia_test.go
326
zuc/eia_test.go
@ -216,3 +216,329 @@ func TestEIAHash(t *testing.T) {
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
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 TestEIA_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 TestEIA_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 TestEIA_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 TestEIA256_Sum32(t *testing.T) {
|
||||
expected := "f4f20d7c"
|
||||
h, err := NewHash256(zucEIA256Tests[2].key, zucEIA256Tests[2].iv, 4)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = h.Write([]byte("emmansun"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = h.Write([]byte("shangmi1"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = h.Write([]byte("emmansun shangmi"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = h.Write([]byte("emmansun shangmi 1234"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
mac := h.Sum(nil)
|
||||
if hex.EncodeToString(mac) != expected {
|
||||
t.Errorf("expected=%s, result=%s\n", expected, hex.EncodeToString(mac))
|
||||
}
|
||||
}
|
||||
|
||||
func TestEIA256_Finish(t *testing.T) {
|
||||
expected := []struct {
|
||||
expected string
|
||||
macLen int
|
||||
}{
|
||||
{
|
||||
"9dd592c4",
|
||||
4,
|
||||
},
|
||||
{
|
||||
"1f6f71e386a2ce01",
|
||||
8,
|
||||
},
|
||||
{
|
||||
"bf5339cfd87bba97d70ef4f5973af8bb",
|
||||
16,
|
||||
},
|
||||
}
|
||||
for _, exp := range expected {
|
||||
h, err := NewHash256(zucEIA256Tests[2].key, zucEIA256Tests[2].iv, exp.macLen)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
mac := h.Finish([]byte("emmansunshangmi1emmansun shangmiemmansun shangmi 12345"), 8*53+4)
|
||||
if hex.EncodeToString(mac) != exp.expected {
|
||||
t.Errorf("expected=%s, result=%s\n", exp.expected, hex.EncodeToString(mac))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEIA256Hash(t *testing.T) {
|
||||
t.Run("EIA-256-32", func(t *testing.T) {
|
||||
cryptotest.TestHash(t, func() hash.Hash {
|
||||
h, _ := NewHash256(zucEIA256Tests[0].key, zucEIA256Tests[0].iv, 4)
|
||||
return h
|
||||
})
|
||||
})
|
||||
t.Run("EIA-256-64", func(t *testing.T) {
|
||||
cryptotest.TestHash(t, func() hash.Hash {
|
||||
h, _ := NewHash256(zucEIA256Tests[0].key, zucEIA256Tests[0].iv, 8)
|
||||
return h
|
||||
})
|
||||
})
|
||||
t.Run("EIA-256-128", func(t *testing.T) {
|
||||
cryptotest.TestHash(t, func() hash.Hash {
|
||||
h, _ := NewHash256(zucEIA256Tests[0].key, zucEIA256Tests[0].iv, 16)
|
||||
return h
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
@ -167,7 +167,7 @@ func ExampleNewHash256_tagSize16() {
|
||||
// Output: fd8d10ea65b6369cccc07d50b4657d84
|
||||
}
|
||||
|
||||
func ExampleZUC128Mac_Finish() {
|
||||
func ExampleEIA_Finish() {
|
||||
key := make([]byte, 16)
|
||||
iv := make([]byte, 16)
|
||||
h, err := zuc.NewHash(key, iv)
|
||||
@ -178,7 +178,7 @@ func ExampleZUC128Mac_Finish() {
|
||||
// Output: c8a9595e
|
||||
}
|
||||
|
||||
func ExampleZUC128Mac_Finish_mixed() {
|
||||
func ExampleEIA_Finish_mixed() {
|
||||
key := []byte{
|
||||
0xc9, 0xe6, 0xce, 0xc4, 0x60, 0x7c, 0x72, 0xdb,
|
||||
0x00, 0x0a, 0xef, 0xa8, 0x83, 0x85, 0xab, 0x0a,
|
||||
|
Loading…
x
Reference in New Issue
Block a user