From e6a8b84935baf870c4a200cda62f3b3217d9f027 Mon Sep 17 00:00:00 2001 From: Sun Yimin Date: Tue, 3 Dec 2024 08:20:59 +0800 Subject: [PATCH] cbcmac: supplement test cases #281 --- cbcmac/cbcmac_test.go | 145 +++++++++++++++++++++++++++++++ internal/cryptotest/aead.go | 8 +- internal/cryptotest/block.go | 18 ++-- internal/cryptotest/blockmode.go | 12 +-- internal/cryptotest/stream.go | 10 +-- zuc/eea_test.go | 23 +---- 6 files changed, 173 insertions(+), 43 deletions(-) diff --git a/cbcmac/cbcmac_test.go b/cbcmac/cbcmac_test.go index 8f60a99..29ab1d6 100644 --- a/cbcmac/cbcmac_test.go +++ b/cbcmac/cbcmac_test.go @@ -2,8 +2,11 @@ package cbcmac import ( "bytes" + "crypto/aes" + "encoding/hex" "testing" + "github.com/emmansun/gmsm/internal/cryptotest" "github.com/emmansun/gmsm/sm4" ) @@ -254,3 +257,145 @@ func TestCBCRMAC(t *testing.T) { } } } + +func TestMustPanic(t *testing.T) { + t.Run("invalid size", func(t *testing.T) { + key := make([]byte, 16) + block, _ := sm4.NewCipher(key) + cryptotest.MustPanic(t, "cbcmac: invalid size", func() { + NewCBCMAC(block, 0) + NewCBCMAC(block, 17) + NewEMAC(sm4.NewCipher, key, key, 0) + NewEMAC(sm4.NewCipher, key, key, 17) + NewANSIRetailMAC(sm4.NewCipher, key, key, 0) + NewANSIRetailMAC(sm4.NewCipher, key, key, 17) + NewMACDES(sm4.NewCipher, key, key, 0) + NewMACDES(sm4.NewCipher, key, key, 17) + NewCMAC(block, 0) + NewCMAC(block, 17) + NewLMAC(sm4.NewCipher, key, 0) + NewLMAC(sm4.NewCipher, key, 17) + NewTRCBCMAC(block, 0) + NewTRCBCMAC(block, 17) + NewCBCRMAC(block, 0) + NewCBCRMAC(block, 17) + }) + }) + t.Run("invalid key size", func(t *testing.T) { + key := make([]byte, 16) + cryptotest.MustPanic(t, "cbcmac: invalid size", func() { + NewEMAC(sm4.NewCipher, key[:15], key, 8) + NewEMAC(sm4.NewCipher, key, key[:15], 8) + NewANSIRetailMAC(sm4.NewCipher, key[:15], key, 8) + NewANSIRetailMAC(sm4.NewCipher, key, key[:15], 8) + NewMACDES(sm4.NewCipher, key[:15], key, 8) + NewMACDES(sm4.NewCipher, key, key[:15], 8) + NewLMAC(sm4.NewCipher, key[:15], 8) + }) + }) + +} + +func fromHex(s string) []byte { + b, err := hex.DecodeString(s) + if err != nil { + panic(err) + } + return b +} + +// Test vectors for CMAC-AES from NIST +// http://csrc.nist.gov/publications/nistpubs/800-38B/SP_800-38B.pdf +// Appendix D +var testVectors = []struct { + key, msg, hash string + tagsize int +}{ + // AES-128 vectors + { + key: "2b7e151628aed2a6abf7158809cf4f3c", + msg: "", + hash: "bb1d6929e95937287fa37d129b756746", + tagsize: 16, + }, + { + key: "2b7e151628aed2a6abf7158809cf4f3c", + msg: "6bc1bee22e409f96e93d7e117393172a", + hash: "070a16b46b4d4144f79bdd9dd04a287c", + tagsize: 16, + }, + { + key: "2b7e151628aed2a6abf7158809cf4f3c", + msg: "6bc1bee22e409f96e93d7e117393172a" + + "ae2d8a571e03ac9c9eb76fac45af8e51" + + "30c81c46a35ce411", + hash: "dfa66747de9ae63030ca32611497c827", + tagsize: 16, + }, + { + key: "2b7e151628aed2a6abf7158809cf4f3c", + msg: "6bc1bee22e409f96e93d7e117393172a" + + "ae2d8a571e03ac9c9eb76fac45af8e51" + + "30c81c46a35ce411", + hash: "dfa66747de9ae63030ca32611497c827", + tagsize: 16, + }, + { + key: "2b7e151628aed2a6abf7158809cf4f3c", + msg: "6bc1bee22e409f96e93d7e117393172a" + + "ae2d8a571e03ac9c9eb76fac45af8e51" + + "30c81c46a35ce411", + hash: "dfa66747de9ae63030ca3261", + tagsize: 12, + }, + // AES-256 vectors + { + key: "603deb1015ca71be2b73aef0857d7781" + + "1f352c073b6108d72d9810a30914dff4", + msg: "", + hash: "028962f61b7bf89efc6b551f4667d983", + tagsize: 16, + }, + { + key: "603deb1015ca71be2b73aef0857d7781" + + "1f352c073b6108d72d9810a30914dff4", + msg: "6bc1bee22e409f96e93d7e117393172a", + hash: "28a7023f452e8f82bd4bf28d8c37c35c", + tagsize: 16, + }, + { + key: "603deb1015ca71be2b73aef0857d7781" + + "1f352c073b6108d72d9810a30914dff4", + msg: "6bc1bee22e409f96e93d7e117393172a" + + "ae2d8a571e03ac9c9eb76fac45af8e51" + + "30c81c46a35ce411", + hash: "aaf3d8f1de5640c232f5b169b9c911e6", + tagsize: 16, + }, + { + key: "603deb1015ca71be2b73aef0857d7781" + + "1f352c073b6108d72d9810a30914dff4", + msg: "6bc1bee22e409f96e93d7e117393172a" + + "ae2d8a571e03ac9c9eb76fac45af8e51" + + "30c81c46a35ce411", + hash: "aaf3d8f1de5640c232f5b169", + tagsize: 12, + }, +} + +func TestCMACAES(t *testing.T) { + for i, v := range testVectors { + key := fromHex(v.key) + msg := fromHex(v.msg) + hash := fromHex(v.hash) + block, err := aes.NewCipher(key) + if err != nil { + t.Errorf("#%d: failed to create cipher: %v", i, err) + } + mac := NewCMAC(block, v.tagsize) + tag := mac.MAC(msg) + if !bytes.Equal(tag, hash) { + t.Errorf("#%d: expect tag %x, got %x", i, hash, tag) + } + } +} diff --git a/internal/cryptotest/aead.go b/internal/cryptotest/aead.go index 85a9c92..653d1f7 100644 --- a/internal/cryptotest/aead.go +++ b/internal/cryptotest/aead.go @@ -131,12 +131,12 @@ func TestAEAD(t *testing.T, mAEAD MakeAEAD) { // Make plaintext and dst slices point to same array with inexact overlap. plaintext := buff[:ptLen] dst := buff[1:1] // Shift dst to not start at start of plaintext. - mustPanic(t, "invalid buffer overlap", func() { sealMsg(t, aead, dst, nonce, plaintext, addData) }) + MustPanic(t, "invalid buffer overlap", func() { sealMsg(t, aead, dst, nonce, plaintext, addData) }) // Only overlap on one byte plaintext = buff[:ptLen] dst = buff[ptLen-1 : ptLen-1] - mustPanic(t, "invalid buffer overlap", func() { sealMsg(t, aead, dst, nonce, plaintext, addData) }) + MustPanic(t, "invalid buffer overlap", func() { sealMsg(t, aead, dst, nonce, plaintext, addData) }) }) t.Run("Open", func(t *testing.T) { @@ -160,7 +160,7 @@ func TestAEAD(t *testing.T, mAEAD MakeAEAD) { ciphertext := buff[:len(validCT)] copy(ciphertext, validCT) dst := buff[1:1] // Shift dst to not start at start of ciphertext. - mustPanic(t, "invalid buffer overlap", func() { aead.Open(dst, nonce, ciphertext, addData) }) + MustPanic(t, "invalid buffer overlap", func() { aead.Open(dst, nonce, ciphertext, addData) }) // Only overlap on one byte. ciphertext = buff[:len(validCT)] @@ -171,7 +171,7 @@ func TestAEAD(t *testing.T, mAEAD MakeAEAD) { // the ciphertext. beforeTag := len(validCT) - aead.Overhead() dst = buff[beforeTag-1 : beforeTag-1] - mustPanic(t, "invalid buffer overlap", func() { aead.Open(dst, nonce, ciphertext, addData) }) + MustPanic(t, "invalid buffer overlap", func() { aead.Open(dst, nonce, ciphertext, addData) }) }) }) } diff --git a/internal/cryptotest/block.go b/internal/cryptotest/block.go index a1c3bd2..407c13e 100644 --- a/internal/cryptotest/block.go +++ b/internal/cryptotest/block.go @@ -205,17 +205,17 @@ func testCipher(t *testing.T, cipher func(dst, src []byte), blockSize int) { // Make src and dst slices point to same array with inexact overlap src := buff[:blockSize] dst := buff[1 : blockSize+1] - mustPanic(t, "invalid buffer overlap", func() { cipher(dst, src) }) + MustPanic(t, "invalid buffer overlap", func() { cipher(dst, src) }) // Only overlap on one byte src = buff[:blockSize] dst = buff[blockSize-1 : 2*blockSize-1] - mustPanic(t, "invalid buffer overlap", func() { cipher(dst, src) }) + MustPanic(t, "invalid buffer overlap", func() { cipher(dst, src) }) // src comes after dst with one byte overlap src = buff[blockSize-1 : 2*blockSize-1] dst = buff[:blockSize] - mustPanic(t, "invalid buffer overlap", func() { cipher(dst, src) }) + MustPanic(t, "invalid buffer overlap", func() { cipher(dst, src) }) }) // Test short input/output. @@ -228,17 +228,17 @@ func testCipher(t *testing.T, cipher func(dst, src []byte), blockSize int) { byteSlice := func(n int) []byte { return make([]byte, n+1)[0:n] } // Off by one byte - mustPanic(t, "input not full block", func() { cipher(byteSlice(blockSize), byteSlice(blockSize-1)) }) - mustPanic(t, "output not full block", func() { cipher(byteSlice(blockSize-1), byteSlice(blockSize)) }) + MustPanic(t, "input not full block", func() { cipher(byteSlice(blockSize), byteSlice(blockSize-1)) }) + MustPanic(t, "output not full block", func() { cipher(byteSlice(blockSize-1), byteSlice(blockSize)) }) // Small slices - mustPanic(t, "input not full block", func() { cipher(byteSlice(1), byteSlice(1)) }) - mustPanic(t, "input not full block", func() { cipher(byteSlice(100), byteSlice(1)) }) - mustPanic(t, "output not full block", func() { cipher(byteSlice(1), byteSlice(100)) }) + MustPanic(t, "input not full block", func() { cipher(byteSlice(1), byteSlice(1)) }) + MustPanic(t, "input not full block", func() { cipher(byteSlice(100), byteSlice(1)) }) + MustPanic(t, "output not full block", func() { cipher(byteSlice(1), byteSlice(100)) }) }) } -func mustPanic(t *testing.T, msg string, f func()) { +func MustPanic(t *testing.T, msg string, f func()) { t.Helper() defer func() { diff --git a/internal/cryptotest/blockmode.go b/internal/cryptotest/blockmode.go index d3271e5..b4f6438 100644 --- a/internal/cryptotest/blockmode.go +++ b/internal/cryptotest/blockmode.go @@ -57,7 +57,7 @@ func testBlockMode(t *testing.T, bm MakeBlockMode, b cipher.Block, iv []byte) { t.Run("WrongIVLen", func(t *testing.T) { iv := make([]byte, b.BlockSize()+1) - mustPanic(t, "IV length must equal block size", func() { bm(b, iv) }) + MustPanic(t, "IV length must equal block size", func() { bm(b, iv) }) }) t.Run("AlterInput", func(t *testing.T) { @@ -135,7 +135,7 @@ func testBlockMode(t *testing.T, bm MakeBlockMode, b cipher.Block, iv []byte) { src = make([]byte, blockSize*3) rng.Read(src) - mustPanic(t, "output smaller than input", func() { + MustPanic(t, "output smaller than input", func() { bm(b, iv).CryptBlocks(dst, src) }) @@ -182,17 +182,17 @@ func testBlockMode(t *testing.T, bm MakeBlockMode, b cipher.Block, iv []byte) { // Make src and dst slices point to same array with inexact overlap src := buff[:blockSize] dst := buff[1 : blockSize+1] - mustPanic(t, "invalid buffer overlap", func() { bm(b, iv).CryptBlocks(dst, src) }) + MustPanic(t, "invalid buffer overlap", func() { bm(b, iv).CryptBlocks(dst, src) }) // Only overlap on one byte src = buff[:blockSize] dst = buff[blockSize-1 : 2*blockSize-1] - mustPanic(t, "invalid buffer overlap", func() { bm(b, iv).CryptBlocks(dst, src) }) + MustPanic(t, "invalid buffer overlap", func() { bm(b, iv).CryptBlocks(dst, src) }) // src comes after dst with one byte overlap src = buff[blockSize-1 : 2*blockSize-1] dst = buff[:blockSize] - mustPanic(t, "invalid buffer overlap", func() { bm(b, iv).CryptBlocks(dst, src) }) + MustPanic(t, "invalid buffer overlap", func() { bm(b, iv).CryptBlocks(dst, src) }) }) // Input to CryptBlocks should be a multiple of BlockSize @@ -201,7 +201,7 @@ func testBlockMode(t *testing.T, bm MakeBlockMode, b cipher.Block, iv []byte) { for _, srcSize := range []int{blockSize - 1, blockSize + 1, 2*blockSize - 1, 2*blockSize + 1} { src := make([]byte, srcSize) dst := make([]byte, 3*blockSize) // Make a dst large enough for all src - mustPanic(t, "input not full blocks", func() { bm(b, iv).CryptBlocks(dst, src) }) + MustPanic(t, "input not full blocks", func() { bm(b, iv).CryptBlocks(dst, src) }) } }) diff --git a/internal/cryptotest/stream.go b/internal/cryptotest/stream.go index f4191d6..f5f529d 100644 --- a/internal/cryptotest/stream.go +++ b/internal/cryptotest/stream.go @@ -135,7 +135,7 @@ func TestStream(t *testing.T, ms MakeStream) { 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) }) + 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:])) @@ -159,17 +159,17 @@ func TestStream(t *testing.T, ms MakeStream) { // 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) }) + 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) }) + 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) }) + MustPanic(t, "invalid buffer overlap", func() { ms().XORKeyStream(dst, src) }) }) } }) @@ -220,7 +220,7 @@ func TestStreamFromBlock(t *testing.T, block cipher.Block, blockMode func(b ciph rng := newRandReader(t) iv := make([]byte, block.BlockSize()+1) rng.Read(iv) - mustPanic(t, "IV length must equal block size", func() { blockMode(block, iv) }) + MustPanic(t, "IV length must equal block size", func() { blockMode(block, iv) }) }) t.Run("BlockModeStream", func(t *testing.T) { diff --git a/zuc/eea_test.go b/zuc/eea_test.go index 6ca802d..3320a0b 100644 --- a/zuc/eea_test.go +++ b/zuc/eea_test.go @@ -141,40 +141,25 @@ func TestXORStreamAt(t *testing.T) { // Make src and dst slices point to same array with inexact overlap src := buff[:32] dst := buff[1 : 32+1] - mustPanic(t, "invalid buffer overlap", func() { c.XORKeyStreamAt(dst, src, 0) }) + 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] - mustPanic(t, "invalid buffer overlap", func() { c.XORKeyStreamAt(dst, src, 0) }) + 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] - mustPanic(t, "invalid buffer overlap", func() { c.XORKeyStreamAt(dst, src, 0) }) + 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] - mustPanic(t, "output smaller than input", func() { c.XORKeyStreamAt(dst, src, 0) }) + cryptotest.MustPanic(t, "output smaller than input", func() { c.XORKeyStreamAt(dst, src, 0) }) }) } -func mustPanic(t *testing.T, msg string, f func()) { - t.Helper() - - defer func() { - t.Helper() - - err := recover() - - if err == nil { - t.Errorf("function did not panic for %q", msg) - } - }() - f() -} - func benchmarkStream(b *testing.B, buf []byte) { b.SetBytes(int64(len(buf)))