internal/cryptotest: add tests for the cipher.Stream interface

This commit is contained in:
Sun Yimin 2024-08-05 11:40:04 +08:00 committed by GitHub
parent 1b512e5772
commit a5d489ee59
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 418 additions and 38 deletions

View File

@ -7,6 +7,7 @@ import (
"encoding/hex"
"testing"
"github.com/emmansun/gmsm/internal/cryptotest"
"github.com/emmansun/gmsm/sm4"
)
@ -105,3 +106,24 @@ func TestCFBInverse(t *testing.T) {
t.Errorf("got: %x, want: %x", plaintextCopy, plaintext)
}
}
func TestCFBStream(t *testing.T) {
t.Run("SM4", func(t *testing.T) {
rng := newRandReader(t)
key := make([]byte, 16)
rng.Read(key)
block, err := sm4.NewCipher(key)
if err != nil {
panic(err)
}
t.Run("Encrypter", func(t *testing.T) {
cryptotest.TestStreamFromBlock(t, block, cipher.NewCFBEncrypter)
})
t.Run("Decrypter", func(t *testing.T) {
cryptotest.TestStreamFromBlock(t, block, cipher.NewCFBDecrypter)
})
})
}

View File

@ -5,6 +5,7 @@ import (
"crypto/cipher"
"testing"
"github.com/emmansun/gmsm/internal/cryptotest"
"github.com/emmansun/gmsm/sm4"
)
@ -56,12 +57,12 @@ var ctrSM4Tests = []struct {
0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
},
[]byte{
0xbc, 0x71, 0x0d, 0x76, 0x2d, 0x07, 0x0b, 0x26, 0x36, 0x1d, 0xa8, 0x2b, 0x54, 0x56, 0x5e, 0x46,
0xb0, 0x2b, 0x3d, 0xbd, 0xdd, 0x50, 0xd5, 0xb4, 0x58, 0xae, 0xcc, 0xb2, 0x5d, 0xa1, 0x05, 0xe1,
0x6a, 0xd7, 0x0b, 0xc0, 0x11, 0x75, 0xad, 0x43, 0xb0, 0x80, 0x6a, 0x2e, 0x7b, 0x9c, 0xa5, 0x45,
0x60, 0x24, 0x59, 0xa0, 0x6b, 0x7d, 0x13, 0x0d, 0xde, 0x42, 0xa3, 0xe0, 0x47, 0x68, 0x18, 0xd2,
0x00, 0xb8, 0x33, 0x1a, 0x66, 0x57, 0xd6, 0xbe, 0xb8, 0x5b, 0x72, 0x4f, 0x55, 0x0c, 0xd5, 0x2d,
0x96, 0xf3, 0xe4, 0x12, 0x37, 0xa2, 0x07, 0x44, 0x43, 0xa5, 0x43, 0x3a, 0x41, 0x33, 0x0d, 0xca,
0xbc, 0x71, 0x0d, 0x76, 0x2d, 0x07, 0x0b, 0x26, 0x36, 0x1d, 0xa8, 0x2b, 0x54, 0x56, 0x5e, 0x46,
0xb0, 0x2b, 0x3d, 0xbd, 0xdd, 0x50, 0xd5, 0xb4, 0x58, 0xae, 0xcc, 0xb2, 0x5d, 0xa1, 0x05, 0xe1,
0x6a, 0xd7, 0x0b, 0xc0, 0x11, 0x75, 0xad, 0x43, 0xb0, 0x80, 0x6a, 0x2e, 0x7b, 0x9c, 0xa5, 0x45,
0x60, 0x24, 0x59, 0xa0, 0x6b, 0x7d, 0x13, 0x0d, 0xde, 0x42, 0xa3, 0xe0, 0x47, 0x68, 0x18, 0xd2,
0x00, 0xb8, 0x33, 0x1a, 0x66, 0x57, 0xd6, 0xbe, 0xb8, 0x5b, 0x72, 0x4f, 0x55, 0x0c, 0xd5, 0x2d,
0x96, 0xf3, 0xe4, 0x12, 0x37, 0xa2, 0x07, 0x44, 0x43, 0xa5, 0x43, 0x3a, 0x41, 0x33, 0x0d, 0xca,
},
},
{
@ -79,14 +80,14 @@ var ctrSM4Tests = []struct {
0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10,
},
[]byte{
0xbc, 0x71, 0x0d, 0x76, 0x2d, 0x07, 0x0b, 0x26, 0x36, 0x1d, 0xa8, 0x2b, 0x54, 0x56, 0x5e, 0x46,
0xb0, 0x2b, 0x3d, 0xbd, 0xdd, 0x50, 0xd5, 0xb4, 0x58, 0xae, 0xcc, 0xb2, 0x5d, 0xa1, 0x05, 0xe1,
0x6a, 0xd7, 0x0b, 0xc0, 0x11, 0x75, 0xad, 0x43, 0xb0, 0x80, 0x6a, 0x2e, 0x7b, 0x9c, 0xa5, 0x45,
0x60, 0x24, 0x59, 0xa0, 0x6b, 0x7d, 0x13, 0x0d, 0xde, 0x42, 0xa3, 0xe0, 0x47, 0x68, 0x18, 0xd2,
0x00, 0xb8, 0x33, 0x1a, 0x66, 0x57, 0xd6, 0xbe, 0xb8, 0x5b, 0x72, 0x4f, 0x55, 0x0c, 0xd5, 0x2d,
0x96, 0xf3, 0xe4, 0x12, 0x37, 0xa2, 0x07, 0x44, 0x43, 0xa5, 0x43, 0x3a, 0x41, 0x33, 0x0d, 0xca,
0x41, 0xb7, 0x3a, 0x57, 0x17, 0x65, 0xef, 0x3d, 0xbb, 0xd1, 0x04, 0x5d, 0xb7, 0xf8, 0x71, 0x39,
0xff, 0x82, 0x01, 0x19, 0x75, 0xaf, 0x8a, 0x01, 0x8a, 0x0a, 0x26, 0x93, 0x85, 0xfd, 0x04, 0x5f,
0xbc, 0x71, 0x0d, 0x76, 0x2d, 0x07, 0x0b, 0x26, 0x36, 0x1d, 0xa8, 0x2b, 0x54, 0x56, 0x5e, 0x46,
0xb0, 0x2b, 0x3d, 0xbd, 0xdd, 0x50, 0xd5, 0xb4, 0x58, 0xae, 0xcc, 0xb2, 0x5d, 0xa1, 0x05, 0xe1,
0x6a, 0xd7, 0x0b, 0xc0, 0x11, 0x75, 0xad, 0x43, 0xb0, 0x80, 0x6a, 0x2e, 0x7b, 0x9c, 0xa5, 0x45,
0x60, 0x24, 0x59, 0xa0, 0x6b, 0x7d, 0x13, 0x0d, 0xde, 0x42, 0xa3, 0xe0, 0x47, 0x68, 0x18, 0xd2,
0x00, 0xb8, 0x33, 0x1a, 0x66, 0x57, 0xd6, 0xbe, 0xb8, 0x5b, 0x72, 0x4f, 0x55, 0x0c, 0xd5, 0x2d,
0x96, 0xf3, 0xe4, 0x12, 0x37, 0xa2, 0x07, 0x44, 0x43, 0xa5, 0x43, 0x3a, 0x41, 0x33, 0x0d, 0xca,
0x41, 0xb7, 0x3a, 0x57, 0x17, 0x65, 0xef, 0x3d, 0xbb, 0xd1, 0x04, 0x5d, 0xb7, 0xf8, 0x71, 0x39,
0xff, 0x82, 0x01, 0x19, 0x75, 0xaf, 0x8a, 0x01, 0x8a, 0x0a, 0x26, 0x93, 0x85, 0xfd, 0x04, 0x5f,
},
},
{
@ -109,27 +110,27 @@ var ctrSM4Tests = []struct {
0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10,
0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10,
},
[]byte{
0xbc, 0x71, 0x0d, 0x76, 0x2d, 0x07, 0x0b, 0x26, 0x36, 0x1d, 0xa8, 0x2b, 0x54, 0x56, 0x5e, 0x46,
0xb0, 0x2b, 0x3d, 0xbd, 0xdd, 0x50, 0xd5, 0xb4, 0x58, 0xae, 0xcc, 0xb2, 0x5d, 0xa1, 0x05, 0xe1,
0x6a, 0xd7, 0x0b, 0xc0, 0x11, 0x75, 0xad, 0x43, 0xb0, 0x80, 0x6a, 0x2e, 0x7b, 0x9c, 0xa5, 0x45,
0x60, 0x24, 0x59, 0xa0, 0x6b, 0x7d, 0x13, 0x0d, 0xde, 0x42, 0xa3, 0xe0, 0x47, 0x68, 0x18, 0xd2,
0x00, 0xb8, 0x33, 0x1a, 0x66, 0x57, 0xd6, 0xbe, 0xb8, 0x5b, 0x72, 0x4f, 0x55, 0x0c, 0xd5, 0x2d,
0x96, 0xf3, 0xe4, 0x12, 0x37, 0xa2, 0x07, 0x44, 0x43, 0xa5, 0x43, 0x3a, 0x41, 0x33, 0x0d, 0xca,
0x41, 0xb7, 0x3a, 0x57, 0x17, 0x65, 0xef, 0x3d, 0xbb, 0xd1, 0x04, 0x5d, 0xb7, 0xf8, 0x71, 0x39,
0xff, 0x82, 0x01, 0x19, 0x75, 0xaf, 0x8a, 0x01, 0x8a, 0x0a, 0x26, 0x93, 0x85, 0xfd, 0x04, 0x5f,
0x73, 0xd4, 0xc6, 0x3a, 0x81, 0x0a, 0x91, 0xe3, 0xb9, 0x17, 0x89, 0xdf, 0x4c, 0xcd, 0xe8, 0xe3,
0x4e, 0xe7, 0x8d, 0x52, 0x89, 0x93, 0xb9, 0xef, 0x42, 0xe7, 0x5d, 0x67, 0xa8, 0x25, 0xad, 0xf0,
0xe2, 0x45, 0x9d, 0x8c, 0x30, 0x61, 0x8a, 0x26, 0x90, 0x4f, 0x52, 0x61, 0xa0, 0x61, 0x62, 0xfb,
0x36, 0xc8, 0x95, 0xe2, 0x8d, 0x75, 0x86, 0xf5, 0xbf, 0x22, 0x1c, 0xdd, 0xc9, 0x52, 0x71, 0x5a,
0x7e, 0xb0, 0x56, 0xd6, 0x8a, 0x7e, 0xfa, 0x4f, 0xda, 0x6b, 0x97, 0x95, 0x23, 0xa7, 0xa8, 0x39,
0x76, 0x31, 0x10, 0x79, 0x47, 0x98, 0x5b, 0x71, 0xbf, 0xc9, 0x4c, 0xce, 0xb7, 0xd4, 0x19, 0x86,
0x04, 0x87, 0xc0, 0xba, 0xe8, 0xa5, 0x4c, 0xc8, 0x48, 0x9c, 0x28, 0xd3, 0x4b, 0x4d, 0xfc, 0x3f,
0x9b, 0xbc, 0xf3, 0xd1, 0x9d, 0x25, 0x43, 0x47, 0x37, 0xea, 0xb7, 0x5e, 0x0d, 0xdb, 0x58, 0xf1,
0xbc, 0x71, 0x0d, 0x76, 0x2d, 0x07, 0x0b, 0x26, 0x36, 0x1d, 0xa8, 0x2b, 0x54, 0x56, 0x5e, 0x46,
0xb0, 0x2b, 0x3d, 0xbd, 0xdd, 0x50, 0xd5, 0xb4, 0x58, 0xae, 0xcc, 0xb2, 0x5d, 0xa1, 0x05, 0xe1,
0x6a, 0xd7, 0x0b, 0xc0, 0x11, 0x75, 0xad, 0x43, 0xb0, 0x80, 0x6a, 0x2e, 0x7b, 0x9c, 0xa5, 0x45,
0x60, 0x24, 0x59, 0xa0, 0x6b, 0x7d, 0x13, 0x0d, 0xde, 0x42, 0xa3, 0xe0, 0x47, 0x68, 0x18, 0xd2,
0x00, 0xb8, 0x33, 0x1a, 0x66, 0x57, 0xd6, 0xbe, 0xb8, 0x5b, 0x72, 0x4f, 0x55, 0x0c, 0xd5, 0x2d,
0x96, 0xf3, 0xe4, 0x12, 0x37, 0xa2, 0x07, 0x44, 0x43, 0xa5, 0x43, 0x3a, 0x41, 0x33, 0x0d, 0xca,
0x41, 0xb7, 0x3a, 0x57, 0x17, 0x65, 0xef, 0x3d, 0xbb, 0xd1, 0x04, 0x5d, 0xb7, 0xf8, 0x71, 0x39,
0xff, 0x82, 0x01, 0x19, 0x75, 0xaf, 0x8a, 0x01, 0x8a, 0x0a, 0x26, 0x93, 0x85, 0xfd, 0x04, 0x5f,
0x73, 0xd4, 0xc6, 0x3a, 0x81, 0x0a, 0x91, 0xe3, 0xb9, 0x17, 0x89, 0xdf, 0x4c, 0xcd, 0xe8, 0xe3,
0x4e, 0xe7, 0x8d, 0x52, 0x89, 0x93, 0xb9, 0xef, 0x42, 0xe7, 0x5d, 0x67, 0xa8, 0x25, 0xad, 0xf0,
0xe2, 0x45, 0x9d, 0x8c, 0x30, 0x61, 0x8a, 0x26, 0x90, 0x4f, 0x52, 0x61, 0xa0, 0x61, 0x62, 0xfb,
0x36, 0xc8, 0x95, 0xe2, 0x8d, 0x75, 0x86, 0xf5, 0xbf, 0x22, 0x1c, 0xdd, 0xc9, 0x52, 0x71, 0x5a,
0x7e, 0xb0, 0x56, 0xd6, 0x8a, 0x7e, 0xfa, 0x4f, 0xda, 0x6b, 0x97, 0x95, 0x23, 0xa7, 0xa8, 0x39,
0x76, 0x31, 0x10, 0x79, 0x47, 0x98, 0x5b, 0x71, 0xbf, 0xc9, 0x4c, 0xce, 0xb7, 0xd4, 0x19, 0x86,
0x04, 0x87, 0xc0, 0xba, 0xe8, 0xa5, 0x4c, 0xc8, 0x48, 0x9c, 0x28, 0xd3, 0x4b, 0x4d, 0xfc, 0x3f,
0x9b, 0xbc, 0xf3, 0xd1, 0x9d, 0x25, 0x43, 0x47, 0x37, 0xea, 0xb7, 0x5e, 0x0d, 0xdb, 0x58, 0xf1,
},
},
},
}
func TestCTR_SM4(t *testing.T) {
@ -167,3 +168,19 @@ func TestCTR_SM4(t *testing.T) {
}
}
}
func TestCTRStream(t *testing.T) {
t.Run("SM4", func(t *testing.T) {
rng := newRandReader(t)
key := make([]byte, 16)
rng.Read(key)
block, err := sm4.NewCipher(key)
if err != nil {
panic(err)
}
cryptotest.TestStreamFromBlock(t, block, cipher.NewCTR)
})
}

View File

@ -5,6 +5,7 @@ import (
"crypto/cipher"
"testing"
"github.com/emmansun/gmsm/internal/cryptotest"
"github.com/emmansun/gmsm/sm4"
)
@ -71,3 +72,19 @@ func TestOFB(t *testing.T) {
}
}
}
func TestOFBStream(t *testing.T) {
t.Run("SM4", func(t *testing.T) {
rng := newRandReader(t)
key := make([]byte, 16)
rng.Read(key)
block, err := sm4.NewCipher(key)
if err != nil {
panic(err)
}
cryptotest.TestStreamFromBlock(t, block, cipher.NewOFB)
})
}

View File

@ -0,0 +1,242 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package cryptotest
import (
"bytes"
"crypto/cipher"
"fmt"
"strings"
"testing"
"github.com/emmansun/gmsm/internal/subtle"
)
// Each test is executed with each of the buffer lengths in bufLens.
var (
bufLens = []int{0, 1, 3, 4, 8, 10, 15, 16, 20, 32, 50, 4096, 5000}
bufCap = 10000
)
// MakeStream returns a cipher.Stream instance.
//
// Multiple calls to MakeStream must return equivalent instances,
// so for example the key and/or IV must be fixed.
type MakeStream func() cipher.Stream
// TestStream performs a set of tests on cipher.Stream implementations,
// checking the documented requirements of XORKeyStream.
func TestStream(t *testing.T, ms MakeStream) {
t.Run("XORSemantics", func(t *testing.T) {
if strings.Contains(t.Name(), "TestCFBStream") {
// This is ugly, but so is CFB's abuse of cipher.Stream.
// Don't want to make it easier for anyone else to do that.
t.Skip("CFB implements cipher.Stream but does not follow XOR semantics")
}
// Test that XORKeyStream inverts itself for encryption/decryption.
t.Run("Roundtrip", func(t *testing.T) {
for _, length := range bufLens {
t.Run(fmt.Sprintf("BuffLength=%d", length), func(t *testing.T) {
rng := newRandReader(t)
plaintext := make([]byte, length)
rng.Read(plaintext)
ciphertext := make([]byte, length)
decrypted := make([]byte, length)
ms().XORKeyStream(ciphertext, plaintext) // Encrypt plaintext
ms().XORKeyStream(decrypted, ciphertext) // Decrypt ciphertext
if !bytes.Equal(decrypted, plaintext) {
t.Errorf("plaintext is different after an encrypt/decrypt cycle; got %s, want %s", truncateHex(decrypted), truncateHex(plaintext))
}
})
}
})
// Test that XORKeyStream behaves the same as directly XORing
// plaintext with the stream.
t.Run("DirectXOR", func(t *testing.T) {
for _, length := range bufLens {
t.Run(fmt.Sprintf("BuffLength=%d", length), func(t *testing.T) {
rng := newRandReader(t)
plaintext := make([]byte, length)
rng.Read(plaintext)
// Encrypting all zeros should reveal the stream itself
stream, directXOR := make([]byte, length), make([]byte, length)
ms().XORKeyStream(stream, stream)
// Encrypt plaintext by directly XORing the stream
subtle.XORBytes(directXOR, stream, plaintext)
// Encrypt plaintext with XORKeyStream
ciphertext := make([]byte, length)
ms().XORKeyStream(ciphertext, plaintext)
if !bytes.Equal(ciphertext, directXOR) {
t.Errorf("xor semantics were not preserved; got %s, want %s", truncateHex(ciphertext), truncateHex(directXOR))
}
})
}
})
})
t.Run("AlterInput", func(t *testing.T) {
rng := newRandReader(t)
src, dst, before := make([]byte, bufCap), make([]byte, bufCap), make([]byte, bufCap)
rng.Read(src)
for _, length := range bufLens {
t.Run(fmt.Sprintf("BuffLength=%d", length), func(t *testing.T) {
copy(before, src)
ms().XORKeyStream(dst[:length], src[:length])
if !bytes.Equal(src, before) {
t.Errorf("XORKeyStream modified src; got %s, want %s", truncateHex(src), truncateHex(before))
}
})
}
})
t.Run("Aliasing", func(t *testing.T) {
rng := newRandReader(t)
buff, expectedOutput := make([]byte, bufCap), make([]byte, bufCap)
for _, length := range bufLens {
// Record what output is when src and dst are different
rng.Read(buff)
ms().XORKeyStream(expectedOutput[:length], buff[:length])
// Check that the same output is generated when src=dst alias to the same
// memory
ms().XORKeyStream(buff[:length], buff[:length])
if !bytes.Equal(buff[:length], expectedOutput[:length]) {
t.Errorf("block cipher produced different output when dst = src; got %x, want %x", buff[:length], expectedOutput[:length])
}
}
})
t.Run("OutOfBoundsWrite", func(t *testing.T) { // Issue 21104
rng := newRandReader(t)
plaintext := make([]byte, bufCap)
rng.Read(plaintext)
ciphertext := make([]byte, bufCap)
for _, length := range bufLens {
copy(ciphertext, plaintext) // Reset ciphertext buffer
t.Run(fmt.Sprintf("BuffLength=%d", length), func(t *testing.T) {
mustPanic(t, "output smaller than input", func() { ms().XORKeyStream(ciphertext[:length], plaintext) })
if !bytes.Equal(ciphertext[length:], plaintext[length:]) {
t.Errorf("XORKeyStream did out of bounds write; got %s, want %s", truncateHex(ciphertext[length:]), truncateHex(plaintext[length:]))
}
})
}
})
t.Run("BufferOverlap", func(t *testing.T) {
rng := newRandReader(t)
buff := make([]byte, bufCap)
rng.Read(buff)
for _, length := range bufLens {
if length == 0 || length == 1 {
continue
}
t.Run(fmt.Sprintf("BuffLength=%d", length), func(t *testing.T) {
// Make src and dst slices point to same array with inexact overlap
src := buff[:length]
dst := buff[1 : length+1]
mustPanic(t, "invalid buffer overlap", func() { ms().XORKeyStream(dst, src) })
// Only overlap on one byte
src = buff[:length]
dst = buff[length-1 : 2*length-1]
mustPanic(t, "invalid buffer overlap", func() { ms().XORKeyStream(dst, src) })
// src comes after dst with one byte overlap
src = buff[length-1 : 2*length-1]
dst = buff[:length]
mustPanic(t, "invalid buffer overlap", func() { ms().XORKeyStream(dst, src) })
})
}
})
t.Run("KeepState", func(t *testing.T) {
rng := newRandReader(t)
plaintext := make([]byte, bufCap)
rng.Read(plaintext)
ciphertext := make([]byte, bufCap)
// Make one long call to XORKeyStream
ms().XORKeyStream(ciphertext, plaintext)
for _, step := range bufLens {
if step == 0 {
continue
}
stepMsg := fmt.Sprintf("step %d: ", step)
dst := make([]byte, bufCap)
// Make a bunch of small calls to (stateful) XORKeyStream
stream := ms()
i := 0
for i+step < len(plaintext) {
stream.XORKeyStream(dst[i:], plaintext[i:i+step])
i += step
}
stream.XORKeyStream(dst[i:], plaintext[i:])
if !bytes.Equal(dst, ciphertext) {
t.Errorf(stepMsg+"successive XORKeyStream calls returned a different result than a single one; got %s, want %s", truncateHex(dst), truncateHex(ciphertext))
}
}
})
}
// TestStreamFromBlock creates a Stream from a cipher.Block used in a
// cipher.BlockMode. It addresses Issue 68377 by checking for a panic when the
// BlockMode uses an IV with incorrect length.
// For a valid IV, it also runs all TestStream tests on the resulting stream.
func TestStreamFromBlock(t *testing.T, block cipher.Block, blockMode func(b cipher.Block, iv []byte) cipher.Stream) {
t.Run("WrongIVLen", func(t *testing.T) {
t.Skip("see Issue 68377")
rng := newRandReader(t)
iv := make([]byte, block.BlockSize()+1)
rng.Read(iv)
mustPanic(t, "IV length must equal block size", func() { blockMode(block, iv) })
})
t.Run("BlockModeStream", func(t *testing.T) {
rng := newRandReader(t)
iv := make([]byte, block.BlockSize())
rng.Read(iv)
TestStream(t, func() cipher.Stream { return blockMode(block, iv) })
})
}
func truncateHex(b []byte) string {
numVals := 50
if len(b) <= numVals {
return fmt.Sprintf("%x", b)
}
return fmt.Sprintf("%x...", b[:numVals])
}

View File

@ -10,9 +10,21 @@ import (
const RoundWords = 32
type eea struct {
zucState32
x [4]byte // remaining bytes buffer
xLen int // number of remaining bytes
}
// NewCipher create a stream cipher based on key and iv aguments.
func NewCipher(key, iv []byte) (cipher.Stream, error) {
return newZUCState(key, iv)
s, err := newZUCState(key, iv)
if err != nil {
return nil, err
}
c := new(eea)
c.zucState32 = *s
return c, nil
}
// NewEEACipher create a stream cipher based on key, count, bearer and direction arguments according specification.
@ -22,7 +34,13 @@ func NewEEACipher(key []byte, count, bearer, direction uint32) (cipher.Stream, e
copy(iv[8:12], iv[:4])
iv[4] = byte(((bearer << 1) | (direction & 1)) << 2)
iv[12] = iv[4]
return newZUCState(key, iv)
s, err := newZUCState(key, iv)
if err != nil {
return nil, err
}
c := new(eea)
c.zucState32 = *s
return c, nil
}
func genKeyStreamRev32Generic(keyStream []byte, pState *zucState32) {
@ -33,24 +51,41 @@ func genKeyStreamRev32Generic(keyStream []byte, pState *zucState32) {
}
}
func (c *zucState32) XORKeyStream(dst, src []byte) {
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")
}
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
}
}
words := (len(src) + 3) / 4
rounds := words / RoundWords
var keyBytes [RoundWords * 4]byte
for i := 0; i < rounds; i++ {
genKeyStreamRev32(keyBytes[:], c)
genKeyStreamRev32(keyBytes[:], &c.zucState32)
subtle.XORBytes(dst, src, keyBytes[:])
dst = dst[RoundWords*4:]
src = src[RoundWords*4:]
}
if rounds*RoundWords < words {
genKeyStreamRev32(keyBytes[:4*(words-rounds*RoundWords)], c)
subtle.XORBytes(dst, src, keyBytes[:])
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])
}
}
}

View File

@ -1,8 +1,11 @@
package zuc
import (
"crypto/cipher"
"encoding/hex"
"testing"
"github.com/emmansun/gmsm/internal/cryptotest"
)
var zucEEATests = []struct {
@ -62,6 +65,14 @@ func Test_EEA(t *testing.T) {
}
}
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 benchmarkStream(b *testing.B, buf []byte) {
b.SetBytes(int64(len(buf)))

View File

@ -2,7 +2,10 @@ package zuc
import (
"encoding/hex"
"hash"
"testing"
"github.com/emmansun/gmsm/internal/cryptotest"
)
var zucEIA256Tests = []struct {
@ -260,6 +263,27 @@ func TestEIA256_Finish(t *testing.T) {
}
}
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

View File

@ -3,7 +3,10 @@ package zuc
import (
"encoding/binary"
"encoding/hex"
"hash"
"testing"
"github.com/emmansun/gmsm/internal/cryptotest"
)
var key [16]byte
@ -204,3 +207,12 @@ func TestEIA_Sum(t *testing.T) {
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
})
})
}