2026-09-30 15:49:12 +08:00
|
|
|
|
package hmacx_test
|
2025-11-03 23:08:53 +08:00
|
|
|
|
|
|
|
|
|
|
import (
|
2026-09-30 15:49:12 +08:00
|
|
|
|
"bytes"
|
|
|
|
|
|
"crypto/sha256"
|
|
|
|
|
|
"encoding/hex"
|
|
|
|
|
|
"errors"
|
|
|
|
|
|
"hash"
|
|
|
|
|
|
"strings"
|
2025-11-03 23:08:53 +08:00
|
|
|
|
"testing"
|
2026-09-30 15:49:12 +08:00
|
|
|
|
|
|
|
|
|
|
"code.yun.ink/pkg/encryptx/hmacx"
|
2025-11-03 23:08:53 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
2026-09-30 15:49:12 +08:00
|
|
|
|
// rfc4231Case 表示 RFC 4231 中的一个测试用例。
|
|
|
|
|
|
// 来源:RFC 4231 "Identifiers and Test Vectors for HMAC-SHA-224, HMAC-SHA-256,
|
|
|
|
|
|
// HMAC-SHA-384, and HMAC-SHA-512"(https://www.rfc-editor.org/rfc/rfc4231.txt)。
|
|
|
|
|
|
type rfc4231Case struct {
|
|
|
|
|
|
name string
|
|
|
|
|
|
key []byte
|
|
|
|
|
|
data []byte
|
|
|
|
|
|
sha256 string
|
|
|
|
|
|
sha384 string
|
|
|
|
|
|
sha512 string
|
|
|
|
|
|
}
|
2025-11-03 23:08:53 +08:00
|
|
|
|
|
2026-09-30 15:49:12 +08:00
|
|
|
|
// rfc4231Cases 为 RFC 4231 第 4.2~4.5 节(Test Case 1~4)的 SHA-256/384/512 摘要,
|
|
|
|
|
|
// 以及 RFC 2202 Test Case 1~4 的 HMAC-SHA1 摘要(两组定义的 key/data 相同)。
|
|
|
|
|
|
var rfc4231Cases = []rfc4231Case{
|
|
|
|
|
|
{
|
|
|
|
|
|
name: "RFC4231-TestCase1",
|
|
|
|
|
|
key: bytes.Repeat([]byte{0x0b}, 20),
|
|
|
|
|
|
data: []byte("Hi There"),
|
|
|
|
|
|
sha256: "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7",
|
|
|
|
|
|
sha384: "afd03944d84895626b0825f4ab46907f15f9dadbe4101ec682aa034c7cebc59cfaea9ea9076ede7f4af152e8b2fa9cb6",
|
|
|
|
|
|
sha512: "87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cdedaa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854",
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
name: "RFC4231-TestCase2",
|
|
|
|
|
|
key: []byte("Jefe"),
|
|
|
|
|
|
data: []byte("what do ya want for nothing?"),
|
|
|
|
|
|
sha256: "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843",
|
|
|
|
|
|
sha384: "af45d2e376484031617f78d2b58a6b1b9c7ef464f5a01b47e42ec3736322445e8e2240ca5e69e2c78b3239ecfab21649",
|
|
|
|
|
|
sha512: "164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737",
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
name: "RFC4231-TestCase3",
|
|
|
|
|
|
key: bytes.Repeat([]byte{0xaa}, 20),
|
|
|
|
|
|
data: bytes.Repeat([]byte{0xdd}, 50),
|
|
|
|
|
|
sha256: "773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe",
|
|
|
|
|
|
sha384: "88062608d3e6ad8a0aa2ace014c8a86f0aa635d947ac9febe83ef4e55966144b2a5ab39dc13814b94e3ab6e101a34f27",
|
|
|
|
|
|
sha512: "fa73b0089d56a284efb0f0756c890be9b1b5dbdd8ee81a3655f83e33b2279d39bf3e848279a722c806b485a47e67c807b946a337bee8942674278859e13292fb",
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
name: "RFC4231-TestCase4",
|
|
|
|
|
|
key: []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19},
|
|
|
|
|
|
data: bytes.Repeat([]byte{0xcd}, 50),
|
|
|
|
|
|
sha256: "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b",
|
|
|
|
|
|
sha384: "3e8a69b7783c25851933ab6290af6ca77a9981480850009cc5577c6e1f573b4e6801dd23c4a7d679ccf8a386c674cffb",
|
|
|
|
|
|
sha512: "b0ba465637458c6990e5a8c5f61d4af7e576d97ff94b872de76f8050361ee3dba91ca5c11aa25eb4d679275cc5788063a5f19741120c4f2de2adebeb10a298dd",
|
|
|
|
|
|
},
|
|
|
|
|
|
}
|
2025-11-03 23:08:53 +08:00
|
|
|
|
|
2026-09-30 15:49:12 +08:00
|
|
|
|
// rfc2202SHA1Cases 为 RFC 2202(HMAC-MD5/HMAC-SHA-1 测试向量)Test Case 1~4 的
|
|
|
|
|
|
// HMAC-SHA1 期望值,key/data 与 rfc4231Cases 的前四项完全一致。
|
|
|
|
|
|
var rfc2202SHA1Cases = []string{
|
|
|
|
|
|
"b617318655057264e28bc0b6fb378c8ef146be00",
|
|
|
|
|
|
"effcdf6ae5eb2fa2d27416d5f184df9c259a7c79",
|
|
|
|
|
|
"125d7342b9ac11cd91a39af48aa17b4f63f175d3",
|
|
|
|
|
|
"4c9007f4026250c6bc8414f9bf50c86c2d7235da",
|
|
|
|
|
|
}
|
2025-11-03 23:08:53 +08:00
|
|
|
|
|
2026-09-30 15:49:12 +08:00
|
|
|
|
// TestSumKnownVectors 用 RFC 4231 与 RFC 2202 的官方测试向量核对摘要计算。
|
|
|
|
|
|
func TestSumKnownVectors(t *testing.T) {
|
|
|
|
|
|
for i, tc := range rfc4231Cases {
|
|
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
|
|
|
|
if got := hex.EncodeToString(hmacx.SumSHA256(tc.data, tc.key)); got != tc.sha256 {
|
|
|
|
|
|
t.Errorf("HMAC-SHA256 不匹配:\n got %s\nwant %s", got, tc.sha256)
|
|
|
|
|
|
}
|
|
|
|
|
|
if got := hex.EncodeToString(hmacx.SumSHA384(tc.data, tc.key)); got != tc.sha384 {
|
|
|
|
|
|
t.Errorf("HMAC-SHA384 不匹配:\n got %s\nwant %s", got, tc.sha384)
|
|
|
|
|
|
}
|
|
|
|
|
|
if got := hex.EncodeToString(hmacx.SumSHA512(tc.data, tc.key)); got != tc.sha512 {
|
|
|
|
|
|
t.Errorf("HMAC-SHA512 不匹配:\n got %s\nwant %s", got, tc.sha512)
|
|
|
|
|
|
}
|
|
|
|
|
|
if got := hex.EncodeToString(hmacx.SumSHA1(tc.data, tc.key)); got != rfc2202SHA1Cases[i] {
|
|
|
|
|
|
t.Errorf("HMAC-SHA1 不匹配:\n got %s\nwant %s", got, rfc2202SHA1Cases[i])
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
2025-11-03 23:08:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-30 15:49:12 +08:00
|
|
|
|
// TestSumGenericMatchesSpecialized 校验通用入口 Sum 与各专用函数一致。
|
|
|
|
|
|
func TestSumGenericMatchesSpecialized(t *testing.T) {
|
|
|
|
|
|
key := []byte("0123456789abcdef0123456789abcdef")
|
|
|
|
|
|
msg := []byte("generic entry point")
|
2025-11-03 23:08:53 +08:00
|
|
|
|
|
2026-09-30 15:49:12 +08:00
|
|
|
|
// Sum + sha256.New 必须与 SumSHA256 完全一致。
|
|
|
|
|
|
if got, want := hmacx.Sum(sha256.New, msg, key), hmacx.SumSHA256(msg, key); !bytes.Equal(got, want) {
|
|
|
|
|
|
t.Errorf("Sum(sha256.New) 与 SumSHA256 不一致: %x vs %x", got, want)
|
2025-11-03 23:08:53 +08:00
|
|
|
|
}
|
2026-09-30 15:49:12 +08:00
|
|
|
|
// 摘要长度必须正确,否则 Verify* 的长度校验会失效。
|
|
|
|
|
|
if got := len(hmacx.SumSHA1(msg, key)); got != 20 {
|
|
|
|
|
|
t.Errorf("HMAC-SHA1 摘要长度 = %d, 期望 20", got)
|
2025-11-03 23:08:53 +08:00
|
|
|
|
}
|
2026-09-30 15:49:12 +08:00
|
|
|
|
if got := len(hmacx.SumSHA256(msg, key)); got != 32 {
|
|
|
|
|
|
t.Errorf("HMAC-SHA256 摘要长度 = %d, 期望 32", got)
|
|
|
|
|
|
}
|
|
|
|
|
|
if got := len(hmacx.SumSHA384(msg, key)); got != 48 {
|
|
|
|
|
|
t.Errorf("HMAC-SHA384 摘要长度 = %d, 期望 48", got)
|
|
|
|
|
|
}
|
|
|
|
|
|
if got := len(hmacx.SumSHA512(msg, key)); got != 64 {
|
|
|
|
|
|
t.Errorf("HMAC-SHA512 摘要长度 = %d, 期望 64", got)
|
2025-11-03 23:08:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-30 15:49:12 +08:00
|
|
|
|
// TestHexFunctions 校验十六进制便利函数与原始摘要一致且格式正确。
|
|
|
|
|
|
func TestHexFunctions(t *testing.T) {
|
|
|
|
|
|
key := []byte("a-key-with-enough-entropy")
|
|
|
|
|
|
msg := []byte("hex convenience")
|
2025-11-03 23:08:53 +08:00
|
|
|
|
|
2026-09-30 15:49:12 +08:00
|
|
|
|
cases := []struct {
|
|
|
|
|
|
name string
|
|
|
|
|
|
got string
|
|
|
|
|
|
raw []byte
|
|
|
|
|
|
}{
|
|
|
|
|
|
{"HexSHA1", hmacx.HexSHA1(msg, key), hmacx.SumSHA1(msg, key)},
|
|
|
|
|
|
{"HexSHA256", hmacx.HexSHA256(msg, key), hmacx.SumSHA256(msg, key)},
|
|
|
|
|
|
{"HexSHA384", hmacx.HexSHA384(msg, key), hmacx.SumSHA384(msg, key)},
|
|
|
|
|
|
{"HexSHA512", hmacx.HexSHA512(msg, key), hmacx.SumSHA512(msg, key)},
|
|
|
|
|
|
}
|
|
|
|
|
|
for _, c := range cases {
|
|
|
|
|
|
if c.got != hex.EncodeToString(c.raw) {
|
|
|
|
|
|
t.Errorf("%s 与原始摘要不一致: %s", c.name, c.got)
|
|
|
|
|
|
}
|
|
|
|
|
|
if c.got != strings.ToLower(c.got) {
|
|
|
|
|
|
t.Errorf("%s 应为小写十六进制: %s", c.name, c.got)
|
|
|
|
|
|
}
|
|
|
|
|
|
if len(c.got) != len(c.raw)*2 {
|
|
|
|
|
|
t.Errorf("%s 长度 = %d, 期望 %d", c.name, len(c.got), len(c.raw)*2)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-11-03 23:08:53 +08:00
|
|
|
|
|
2026-09-30 15:49:12 +08:00
|
|
|
|
// TestVerifyAcceptsValidMAC 校验 Verify* 对正确 MAC 返回 true。
|
|
|
|
|
|
func TestVerifyAcceptsValidMAC(t *testing.T) {
|
|
|
|
|
|
key := []byte("0123456789abcdef0123456789abcdef")
|
|
|
|
|
|
msg := []byte("verify the good mac")
|
|
|
|
|
|
|
|
|
|
|
|
if !hmacx.VerifySHA1(msg, key, hmacx.SumSHA1(msg, key)) {
|
|
|
|
|
|
t.Error("VerifySHA1 对正确 MAC 返回了 false")
|
|
|
|
|
|
}
|
|
|
|
|
|
if !hmacx.VerifySHA256(msg, key, hmacx.SumSHA256(msg, key)) {
|
|
|
|
|
|
t.Error("VerifySHA256 对正确 MAC 返回了 false")
|
|
|
|
|
|
}
|
|
|
|
|
|
if !hmacx.VerifySHA384(msg, key, hmacx.SumSHA384(msg, key)) {
|
|
|
|
|
|
t.Error("VerifySHA384 对正确 MAC 返回了 false")
|
|
|
|
|
|
}
|
|
|
|
|
|
if !hmacx.VerifySHA512(msg, key, hmacx.SumSHA512(msg, key)) {
|
|
|
|
|
|
t.Error("VerifySHA512 对正确 MAC 返回了 false")
|
|
|
|
|
|
}
|
|
|
|
|
|
if !hmacx.Verify(sha256.New, msg, key, hmacx.Sum(sha256.New, msg, key)) {
|
|
|
|
|
|
t.Error("Verify(sha256.New) 对正确 MAC 返回了 false")
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TestVerifyRejectsTamperedMAC 校验单比特翻转会被拒绝(且不 panic)。
|
|
|
|
|
|
func TestVerifyRejectsTamperedMAC(t *testing.T) {
|
|
|
|
|
|
key := []byte("0123456789abcdef0123456789abcdef")
|
|
|
|
|
|
msg := []byte("verify the tampered mac")
|
|
|
|
|
|
|
|
|
|
|
|
tamper := func(src []byte) []byte {
|
|
|
|
|
|
out := append([]byte(nil), src...)
|
|
|
|
|
|
out[0] ^= 0x01 // 翻转 1 bit
|
|
|
|
|
|
return out
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
tests := []struct {
|
|
|
|
|
|
name string
|
|
|
|
|
|
good []byte
|
|
|
|
|
|
ver func(message, key, expectedMAC []byte) bool
|
|
|
|
|
|
}{
|
|
|
|
|
|
{"SHA1", hmacx.SumSHA1(msg, key), hmacx.VerifySHA1},
|
|
|
|
|
|
{"SHA256", hmacx.SumSHA256(msg, key), hmacx.VerifySHA256},
|
|
|
|
|
|
{"SHA384", hmacx.SumSHA384(msg, key), hmacx.VerifySHA384},
|
|
|
|
|
|
{"SHA512", hmacx.SumSHA512(msg, key), hmacx.VerifySHA512},
|
|
|
|
|
|
}
|
|
|
|
|
|
for _, tc := range tests {
|
|
|
|
|
|
if tc.ver(msg, key, tamper(tc.good)) {
|
|
|
|
|
|
t.Errorf("Verify%s 接受了被篡改 1 bit 的 MAC", tc.name)
|
|
|
|
|
|
}
|
|
|
|
|
|
// 错误的密钥同样必须被拒绝。
|
|
|
|
|
|
if tc.ver(msg, []byte("another-key-with-32-bytes-len!!"), tc.good) {
|
|
|
|
|
|
t.Errorf("Verify%s 接受了错误密钥下的 MAC", tc.name)
|
|
|
|
|
|
}
|
|
|
|
|
|
// 错误的消息同样必须被拒绝。
|
|
|
|
|
|
if tc.ver([]byte("another message"), key, tc.good) {
|
|
|
|
|
|
t.Errorf("Verify%s 接受了错误消息下的 MAC", tc.name)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TestVerifyRejectsWrongLengthMAC 校验长度不符时返回 false 而不是 panic。
|
|
|
|
|
|
func TestVerifyRejectsWrongLengthMAC(t *testing.T) {
|
|
|
|
|
|
key := []byte("0123456789abcdef")
|
|
|
|
|
|
msg := []byte("length check")
|
|
|
|
|
|
|
|
|
|
|
|
good := hmacx.SumSHA256(msg, key)
|
|
|
|
|
|
|
|
|
|
|
|
// 长度不足、超长、空切片、nil 都必须安全返回 false。
|
|
|
|
|
|
lengths := [][]byte{
|
|
|
|
|
|
nil,
|
|
|
|
|
|
{},
|
|
|
|
|
|
good[:len(good)-1],
|
|
|
|
|
|
append(append([]byte(nil), good...), 0x00),
|
|
|
|
|
|
make([]byte, 64),
|
|
|
|
|
|
make([]byte, 1),
|
|
|
|
|
|
}
|
|
|
|
|
|
for _, mac := range lengths {
|
|
|
|
|
|
func() {
|
|
|
|
|
|
defer func() {
|
|
|
|
|
|
if r := recover(); r != nil {
|
|
|
|
|
|
t.Fatalf("VerifySHA256 对长度 %d 的 MAC 发生了 panic: %v", len(mac), r)
|
|
|
|
|
|
}
|
|
|
|
|
|
}()
|
|
|
|
|
|
if hmacx.VerifySHA256(msg, key, mac) {
|
|
|
|
|
|
t.Errorf("VerifySHA256 接受了长度 %d(期望 32)的 MAC", len(mac))
|
|
|
|
|
|
}
|
|
|
|
|
|
}()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 其余算法的长度校验同样必须生效。
|
|
|
|
|
|
if hmacx.VerifySHA1(msg, key, good) {
|
|
|
|
|
|
t.Error("VerifySHA1 接受了 32 字节的 MAC(期望 20)")
|
|
|
|
|
|
}
|
|
|
|
|
|
if hmacx.VerifySHA384(msg, key, good) {
|
|
|
|
|
|
t.Error("VerifySHA384 接受了 32 字节的 MAC(期望 48)")
|
|
|
|
|
|
}
|
|
|
|
|
|
if hmacx.VerifySHA512(msg, key, good) {
|
|
|
|
|
|
t.Error("VerifySHA512 接受了 32 字节的 MAC(期望 64)")
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TestVerifyEdgeCases 覆盖 nil 哈希构造函数等边界输入,确保不 panic。
|
|
|
|
|
|
func TestVerifyEdgeCases(t *testing.T) {
|
|
|
|
|
|
defer func() {
|
|
|
|
|
|
if r := recover(); r != nil {
|
|
|
|
|
|
t.Fatalf("边界输入导致 panic: %v", r)
|
|
|
|
|
|
}
|
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
|
|
var nilHash func() hash.Hash
|
|
|
|
|
|
if hmacx.Verify(nilHash, []byte("msg"), []byte("key"), []byte("mac")) {
|
|
|
|
|
|
t.Error("Verify(nil) 应返回 false")
|
|
|
|
|
|
}
|
|
|
|
|
|
if hmacx.Sum(nilHash, []byte("msg"), []byte("key")) != nil {
|
|
|
|
|
|
t.Error("Sum(nil) 应返回 nil")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 空消息与空密钥是合法输入:HMAC 仍然可以计算,只是空密钥没有认证意义。
|
|
|
|
|
|
if got := len(hmacx.SumSHA256(nil, nil)); got != 32 {
|
|
|
|
|
|
t.Errorf("空消息/空密钥的 HMAC-SHA256 长度 = %d, 期望 32", got)
|
|
|
|
|
|
}
|
|
|
|
|
|
if !hmacx.VerifySHA256(nil, nil, hmacx.SumSHA256(nil, nil)) {
|
|
|
|
|
|
t.Error("VerifySHA256 对空消息/空密钥的正确 MAC 返回了 false")
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TestSecureCompare 校验常量时间比较函数的行为。
|
|
|
|
|
|
func TestSecureCompare(t *testing.T) {
|
|
|
|
|
|
a := []byte("constant-time-comparison")
|
|
|
|
|
|
|
|
|
|
|
|
if !hmacx.SecureCompare(a, append([]byte(nil), a...)) {
|
|
|
|
|
|
t.Error("SecureCompare 对相同内容返回了 false")
|
|
|
|
|
|
}
|
|
|
|
|
|
if hmacx.SecureCompare(a, []byte("constant-time-comparisou")) {
|
|
|
|
|
|
t.Error("SecureCompare 对仅末字节不同的内容返回了 true")
|
|
|
|
|
|
}
|
|
|
|
|
|
if hmacx.SecureCompare(a, a[:len(a)-1]) {
|
|
|
|
|
|
t.Error("SecureCompare 对长度不同的内容返回了 true")
|
|
|
|
|
|
}
|
|
|
|
|
|
if !hmacx.SecureCompare(nil, nil) {
|
|
|
|
|
|
t.Error("SecureCompare(nil, nil) 应为 true")
|
|
|
|
|
|
}
|
|
|
|
|
|
if hmacx.SecureCompare(nil, []byte{}) != true {
|
|
|
|
|
|
// nil 与空切片长度都为 0,应视为相等。
|
|
|
|
|
|
t.Error("SecureCompare(nil, []byte{}) 应为 true")
|
|
|
|
|
|
}
|
|
|
|
|
|
// 与 Verify 的结果必须一致。
|
|
|
|
|
|
key := []byte("0123456789abcdef")
|
|
|
|
|
|
msg := []byte("consistency")
|
|
|
|
|
|
mac := hmacx.SumSHA256(msg, key)
|
|
|
|
|
|
if !hmacx.SecureCompare(mac, hmacx.SumSHA256(msg, key)) {
|
|
|
|
|
|
t.Error("SecureCompare 对相同 MAC 返回了 false")
|
|
|
|
|
|
}
|
|
|
|
|
|
if !hmacx.VerifySHA256(msg, key, mac) {
|
|
|
|
|
|
t.Error("VerifySHA256 与 SecureCompare 结果不一致")
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// errReader 是一个总是返回错误的 io.Reader,用于测试流式读取的错误路径。
|
|
|
|
|
|
type errReader struct{}
|
|
|
|
|
|
|
|
|
|
|
|
func (errReader) Read([]byte) (int, error) {
|
|
|
|
|
|
return 0, errors.New("模拟的读取失败")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// shortReader 在返回全部数据后返回错误,模拟"读一半失败"的场景。
|
|
|
|
|
|
type shortReader struct {
|
|
|
|
|
|
data []byte
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (s *shortReader) Read(p []byte) (int, error) {
|
|
|
|
|
|
if len(s.data) == 0 {
|
|
|
|
|
|
return 0, errors.New("模拟的读取中断")
|
|
|
|
|
|
}
|
|
|
|
|
|
n := copy(p, s.data)
|
|
|
|
|
|
s.data = s.data[n:]
|
|
|
|
|
|
return n, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TestSumReaderSHA256 校验流式计算与整体计算一致,以及错误路径。
|
|
|
|
|
|
func TestSumReaderSHA256(t *testing.T) {
|
|
|
|
|
|
key := []byte("0123456789abcdef0123456789abcdef")
|
|
|
|
|
|
|
|
|
|
|
|
payloads := [][]byte{
|
|
|
|
|
|
nil,
|
|
|
|
|
|
{},
|
|
|
|
|
|
[]byte("a"),
|
|
|
|
|
|
[]byte("streaming payload that is longer than one internal block boundary 0123456789"),
|
|
|
|
|
|
bytes.Repeat([]byte("block"), 1000),
|
|
|
|
|
|
}
|
|
|
|
|
|
for i, payload := range payloads {
|
|
|
|
|
|
got, err := hmacx.SumReaderSHA256(bytes.NewReader(payload), key)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
t.Fatalf("用例 %d: SumReaderSHA256 返回错误: %v", i, err)
|
|
|
|
|
|
}
|
|
|
|
|
|
want := hmacx.SumSHA256(payload, key)
|
|
|
|
|
|
if !bytes.Equal(got, want) {
|
|
|
|
|
|
t.Errorf("用例 %d: SumReaderSHA256 与 SumSHA256 不一致: %x vs %x", i, got, want)
|
|
|
|
|
|
}
|
|
|
|
|
|
if !hmacx.VerifySHA256(payload, key, got) {
|
|
|
|
|
|
t.Errorf("用例 %d: 流式摘要未能通过 VerifySHA256", i)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 读取失败必须返回 error 与 nil 摘要,绝不返回部分结果。
|
|
|
|
|
|
got, err := hmacx.SumReaderSHA256(errReader{}, key)
|
|
|
|
|
|
if err == nil {
|
|
|
|
|
|
t.Error("读取失败时 SumReaderSHA256 未返回错误")
|
|
|
|
|
|
}
|
|
|
|
|
|
if got != nil {
|
|
|
|
|
|
t.Errorf("读取失败时 SumReaderSHA256 应返回 nil,实际 %x", got)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
got, err = hmacx.SumReaderSHA256(&shortReader{data: []byte("partial data")}, key)
|
|
|
|
|
|
if err == nil {
|
|
|
|
|
|
t.Error("读取中断时 SumReaderSHA256 未返回错误")
|
|
|
|
|
|
}
|
|
|
|
|
|
if got != nil {
|
|
|
|
|
|
t.Errorf("读取中断时 SumReaderSHA256 应返回 nil,实际 %x", got)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// nil reader 不能 panic。
|
|
|
|
|
|
func() {
|
|
|
|
|
|
defer func() {
|
|
|
|
|
|
if r := recover(); r != nil {
|
|
|
|
|
|
t.Fatalf("nil reader 导致 panic: %v", r)
|
|
|
|
|
|
}
|
|
|
|
|
|
}()
|
|
|
|
|
|
got, err := hmacx.SumReaderSHA256(nil, key)
|
|
|
|
|
|
if err == nil {
|
|
|
|
|
|
t.Error("nil reader 应返回错误")
|
|
|
|
|
|
}
|
|
|
|
|
|
if got != nil {
|
|
|
|
|
|
t.Errorf("nil reader 时应返回 nil,实际 %x", got)
|
|
|
|
|
|
}
|
|
|
|
|
|
}()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TestConstants 校验导出的长度常量为文档约定的值。
|
|
|
|
|
|
func TestConstants(t *testing.T) {
|
|
|
|
|
|
if hmacx.MinKeyLength != 16 {
|
|
|
|
|
|
t.Errorf("MinKeyLength = %d, 期望 16", hmacx.MinKeyLength)
|
|
|
|
|
|
}
|
|
|
|
|
|
if hmacx.DefaultKeyLength != 32 {
|
|
|
|
|
|
t.Errorf("DefaultKeyLength = %d, 期望 32", hmacx.DefaultKeyLength)
|
|
|
|
|
|
}
|
|
|
|
|
|
if hmacx.DefaultKeyLength < hmacx.MinKeyLength {
|
|
|
|
|
|
t.Error("DefaultKeyLength 不应小于 MinKeyLength")
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TestNoPanicOnAnyInput 用一批畸形输入整体验证不 panic。
|
|
|
|
|
|
func TestNoPanicOnAnyInput(t *testing.T) {
|
|
|
|
|
|
defer func() {
|
|
|
|
|
|
if r := recover(); r != nil {
|
|
|
|
|
|
t.Fatalf("畸形输入导致 panic: %v", r)
|
|
|
|
|
|
}
|
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
|
|
msgs := [][]byte{nil, {}, []byte("x"), bytes.Repeat([]byte{0xff}, 1<<10)}
|
|
|
|
|
|
keys := [][]byte{nil, {}, []byte("k"), bytes.Repeat([]byte{0x00}, 200)}
|
|
|
|
|
|
for _, m := range msgs {
|
|
|
|
|
|
for _, k := range keys {
|
|
|
|
|
|
_ = hmacx.SumSHA1(m, k)
|
|
|
|
|
|
_ = hmacx.SumSHA384(m, k)
|
|
|
|
|
|
_ = hmacx.HexSHA512(m, k)
|
|
|
|
|
|
_ = hmacx.VerifySHA256(m, k, nil)
|
|
|
|
|
|
_ = hmacx.SecureCompare(m, k)
|
|
|
|
|
|
}
|
2025-11-03 23:08:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|