package blake2x_test import ( "bytes" "encoding/base64" "encoding/hex" "errors" "os" "path/filepath" "strings" "testing" "code.yun.ink/pkg/encryptx/blake2x" ) // 测试向量来源:RFC 7693 / BLAKE2 官方测试向量,并经 Python 3 hashlib 独立复算确认。 var vectors = []struct { name string newHash func() (hashLike, error) sum func([]byte) []byte hex func([]byte) string b64 func([]byte) string size int empty string abc string }{ { name: "BLAKE2b-512", newHash: func() (hashLike, error) { return blake2x.New512() }, sum: blake2x.Sum512, hex: blake2x.Hex512, b64: blake2x.Base64Sum512, size: blake2x.SizeB512, empty: "786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce", abc: "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923", }, { name: "BLAKE2b-384", newHash: func() (hashLike, error) { return blake2x.New384() }, sum: blake2x.Sum384, hex: blake2x.Hex384, b64: blake2x.Base64Sum384, size: blake2x.SizeB384, empty: "b32811423377f52d7862286ee1a72ee540524380fda1724a6f25d7978c6fd3244a6caf0498812673c5e05ef583825100", abc: "6f56a82c8e7ef526dfe182eb5212f7db9df1317e57815dbda46083fc30f54ee6c66ba83be64b302d7cba6ce15bb556f4", }, { name: "BLAKE2b-256", newHash: func() (hashLike, error) { return blake2x.New256B() }, sum: blake2x.Sum256B, hex: blake2x.Hex256B, b64: blake2x.Base64Sum256B, size: blake2x.SizeB256, empty: "0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8", abc: "bddd813c634239723171ef3fee98579b94964e3bb1cb3e427262c8c068d52319", }, { name: "BLAKE2s-256", newHash: func() (hashLike, error) { return blake2x.New256S() }, sum: blake2x.Sum256S, hex: blake2x.Hex256S, b64: blake2x.Base64Sum256S, size: blake2x.SizeS256, empty: "69217a3079908094e11121d042354a7c1f55b6482ca1a51e1b250dfd1ed0eef9", abc: "508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982", }, } type hashLike interface { Write([]byte) (int, error) Sum([]byte) []byte Size() int } type failingReader struct{} func (failingReader) Read([]byte) (int, error) { return 0, errors.New("模拟读取失败") } func TestConstants(t *testing.T) { if blake2x.BlockSizeB != 128 || blake2x.BlockSizeS != 64 { t.Errorf("BlockSizeB/BlockSizeS = %d/%d, 期望 128/64", blake2x.BlockSizeB, blake2x.BlockSizeS) } if blake2x.SizeB512 != 64 || blake2x.SizeB384 != 48 || blake2x.SizeB256 != 32 || blake2x.SizeS256 != 32 { t.Errorf("摘要长度常量错误: %d/%d/%d/%d", blake2x.SizeB512, blake2x.SizeB384, blake2x.SizeB256, blake2x.SizeS256) } if blake2x.MinSizeB != 1 || blake2x.MaxSizeB != 64 { t.Errorf("MinSizeB/MaxSizeB = %d/%d, 期望 1/64", blake2x.MinSizeB, blake2x.MaxSizeB) } } func TestVectors(t *testing.T) { for _, v := range vectors { t.Run(v.name, func(t *testing.T) { cases := []struct { name string input string want string }{ {"空串", "", v.empty}, {"abc", "abc", v.abc}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { data := []byte(tc.input) sum := v.sum(data) if len(sum) != v.size { t.Fatalf("Sum 长度 = %d, 期望 %d", len(sum), v.size) } if got := hex.EncodeToString(sum); got != tc.want { t.Errorf("Sum = %s, 期望 %s", got, tc.want) } if got := v.hex(data); got != tc.want { t.Errorf("Hex = %s, 期望 %s", got, tc.want) } if want := base64.StdEncoding.EncodeToString(sum); v.b64(data) != want { t.Errorf("Base64 = %s, 期望 %s", v.b64(data), want) } h, err := v.newHash() if err != nil { t.Fatalf("创建哈希失败: %v", err) } if h.Size() != v.size { t.Errorf("Size() = %d, 期望 %d", h.Size(), v.size) } if _, err := h.Write(data); err != nil { t.Fatalf("Write 失败: %v", err) } if got := hex.EncodeToString(h.Sum(nil)); got != tc.want { t.Errorf("New+Write = %s, 期望 %s", got, tc.want) } }) } }) } } func TestDefaultAliasesMatch512(t *testing.T) { data := []byte("encryptx blake2 alias check") if !bytes.Equal(blake2x.Sum(data), blake2x.Sum512(data)) { t.Error("Sum 应等价于 Sum512") } if blake2x.Hex(data) != blake2x.Hex512(data) { t.Error("Hex 应等价于 Hex512") } if blake2x.Base64(data) != blake2x.Base64Sum512(data) { t.Error("Base64 应等价于 Base64Sum512") } h, err := blake2x.New() if err != nil { t.Fatalf("New 失败: %v", err) } if h.Size() != blake2x.SizeB512 { t.Errorf("New().Size() = %d, 期望 %d", h.Size(), blake2x.SizeB512) } } func TestVariant256BAnd256SDiffer(t *testing.T) { data := []byte("abc") if blake2x.Hex256B(data) == blake2x.Hex256S(data) { t.Error("BLAKE2b-256 与 BLAKE2s-256 的摘要不应相同") } } func TestSumBSizes(t *testing.T) { data := []byte("arbitrary length blake2b") for size := blake2x.MinSizeB; size <= blake2x.MaxSizeB; size++ { sum, err := blake2x.SumB(size, data) if err != nil { t.Fatalf("SumB(%d) 失败: %v", size, err) } if len(sum) != size { t.Errorf("SumB(%d) 长度 = %d", size, len(sum)) } h, err := blake2x.NewB(size) if err != nil { t.Fatalf("NewB(%d) 失败: %v", size, err) } if h.Size() != size { t.Errorf("NewB(%d).Size() = %d", size, h.Size()) } if _, err := h.Write(data); err != nil { t.Fatalf("Write 失败: %v", err) } if !bytes.Equal(h.Sum(nil), sum) { t.Errorf("SumB(%d) 与 NewB(%d) 结果不一致", size, size) } hexStr, err := blake2x.HexB(size, data) if err != nil { t.Fatalf("HexB(%d) 失败: %v", size, err) } if hexStr != hex.EncodeToString(sum) { t.Errorf("HexB(%d) 与 SumB(%d) 结果不一致", size, size) } } if got, err := blake2x.SumB(blake2x.SizeB512, data); err != nil { t.Fatalf("SumB(64) 失败: %v", err) } else if !bytes.Equal(got, blake2x.Sum512(data)) { t.Error("SumB(64) 应等于 Sum512") } if got, err := blake2x.SumB(blake2x.SizeB384, data); err != nil { t.Fatalf("SumB(48) 失败: %v", err) } else if !bytes.Equal(got, blake2x.Sum384(data)) { t.Error("SumB(48) 应等于 Sum384") } if got, err := blake2x.SumB(blake2x.SizeB256, data); err != nil { t.Fatalf("SumB(32) 失败: %v", err) } else if !bytes.Equal(got, blake2x.Sum256B(data)) { t.Error("SumB(32) 应等于 Sum256B") } } func TestInvalidSizes(t *testing.T) { for _, size := range []int{-1, 0, 65, 128, 1 << 20} { if _, err := blake2x.NewB(size); err == nil { t.Errorf("NewB(%d) 应返回 error", size) } if _, err := blake2x.SumB(size, []byte("x")); err == nil { t.Errorf("SumB(%d) 应返回 error", size) } if _, err := blake2x.HexB(size, []byte("x")); err == nil { t.Errorf("HexB(%d) 应返回 error", size) } } } func TestStreamingMatchesSum(t *testing.T) { data := []byte(strings.Repeat("encryptx-blake2-", 128)) for _, v := range vectors { t.Run(v.name, func(t *testing.T) { h, err := v.newHash() if err != nil { t.Fatalf("创建哈希失败: %v", err) } for i := 0; i < len(data); i += 23 { end := i + 23 if end > len(data) { end = len(data) } if _, err := h.Write(data[i:end]); err != nil { t.Fatalf("Write 失败: %v", err) } } if got, want := h.Sum(nil), v.sum(data); !bytes.Equal(got, want) { t.Errorf("分块写入 = %x, 期望 %x", got, want) } }) } } func TestReaderConsistency(t *testing.T) { const s = "encryptx blake2 reader" sum, err := blake2x.SumReader(strings.NewReader(s)) if err != nil { t.Fatalf("SumReader 失败: %v", err) } if want := blake2x.Sum512([]byte(s)); !bytes.Equal(sum, want) { t.Errorf("SumReader = %x, 期望 %x", sum, want) } hexStr, err := blake2x.HexReader(strings.NewReader(s)) if err != nil { t.Fatalf("HexReader 失败: %v", err) } if want := blake2x.Hex512([]byte(s)); hexStr != want { t.Errorf("HexReader = %s, 期望 %s", hexStr, want) } } func TestReaderErrors(t *testing.T) { if _, err := blake2x.SumReader(nil); err == nil { t.Fatal("nil 读取器应返回 error") } if _, err := blake2x.HexReader(nil); err == nil { t.Fatal("nil 读取器应返回 error") } if _, err := blake2x.SumReader(failingReader{}); err == nil { t.Fatal("读取失败时应返回 error") } } func TestHexFile(t *testing.T) { dir := t.TempDir() content := []byte("文件内容 file content\n") path := filepath.Join(dir, "sample.bin") if err := os.WriteFile(path, content, 0o600); err != nil { t.Fatalf("写入测试文件失败: %v", err) } got, err := blake2x.HexFile(path) if err != nil { t.Fatalf("HexFile 失败: %v", err) } if want := blake2x.Hex512(content); got != want { t.Errorf("HexFile = %s, 期望 %s", got, want) } } func TestHexFileErrors(t *testing.T) { if _, err := blake2x.HexFile(filepath.Join(t.TempDir(), "不存在.bin")); err == nil { t.Fatal("不存在的文件应返回 error") } if _, err := blake2x.HexFile(""); err == nil { t.Fatal("空路径应返回 error") } if _, err := blake2x.HexFile(t.TempDir()); err == nil { t.Fatal("目录路径应返回 error") } } func TestLargeInput(t *testing.T) { data := largeData(1 << 20) for _, v := range vectors { t.Run(v.name, func(t *testing.T) { want := v.hex(data) if got := v.hex(data); got != want { t.Errorf("重复计算结果不一致: %s vs %s", got, want) } h, err := v.newHash() if err != nil { t.Fatalf("创建哈希失败: %v", err) } if _, err := h.Write(data[:1<<19]); err != nil { t.Fatalf("Write 失败: %v", err) } if _, err := h.Write(data[1<<19:]); err != nil { t.Fatalf("Write 失败: %v", err) } if got := hex.EncodeToString(h.Sum(nil)); got != want { t.Errorf("1MiB 分块 = %s, 期望 %s", got, want) } }) } got, err := blake2x.HexReader(bytes.NewReader(data)) if err != nil { t.Fatalf("HexReader 失败: %v", err) } if want := blake2x.Hex512(data); got != want { t.Errorf("1MiB HexReader = %s, 期望 %s", got, want) } } // largeData 生成确定性的 n 字节数据。 func largeData(n int) []byte { b := make([]byte, n) for i := range b { b[i] = byte(i*31 + i/7) } return b }