完善不同加密方法的使用

This commit is contained in:
yun
2026-09-30 15:49:12 +08:00
parent 034315ddbd
commit 7db722f8b9
69 changed files with 21362 additions and 2563 deletions
+227
View File
@@ -0,0 +1,227 @@
// Package blake2x 提供 BLAKE2b 与 BLAKE2s 摘要计算,以及十六进制/Base64 编码与流式、文件接口。
//
// BLAKE2 是现代高速哈希(RFC 7693),在同等安全强度下通常比 SHA-2 更快,
// 常用于校验和、内容寻址(如 IPFS CID)与文件指纹。
//
// 命名约定:
// - 带 B 后缀的函数(Sum256B、New256B、Hex256B 等)使用 BLAKE2b,摘要长度 1..64 字节;
// - 带 S 后缀的函数(Sum256S、New256S、Hex256S 等)使用 BLAKE2s,摘要长度固定 256 位;
// - Sum512、Sum384 是 BLAKE2b 的固定长度便捷函数;
// - 不带后缀的 Sum、Hex、Base64、New、SumReader、HexReader、HexFile 等价于 BLAKE2b-512(默认变体)。
//
// 注意 BLAKE2b-256 与 BLAKE2s-256 摘要长度相同但结果完全不同,不可互换。
//
// 本包只暴露无密钥(unkeyed)哈希。带密钥的 BLAKE2 等同于 MAC,应按 MAC 的规范使用,
// 请使用专门的 MAC 包(如 hmacx)或直接调用 golang.org/x/crypto/blake2b.New(size, key)。
package blake2x
import (
"encoding/base64"
"encoding/hex"
"fmt"
"hash"
"io"
"os"
"golang.org/x/crypto/blake2b"
"golang.org/x/crypto/blake2s"
)
const (
// BlockSizeB 是 BLAKE2b 分组的字节数(128)。
BlockSizeB = blake2b.BlockSize
// BlockSizeS 是 BLAKE2s 分组的字节数(64)。
BlockSizeS = blake2s.BlockSize
// SizeB512 是 BLAKE2b-512 摘要的字节数(64)。
SizeB512 = blake2b.Size
// SizeB384 是 BLAKE2b-384 摘要的字节数(48)。
SizeB384 = blake2b.Size384
// SizeB256 是 BLAKE2b-256 摘要的字节数(32)。
SizeB256 = blake2b.Size256
// SizeS256 是 BLAKE2s-256 摘要的字节数(32)。
SizeS256 = blake2s.Size
// MinSizeB 是 BLAKE2b 允许的最小摘要字节数。
MinSizeB = 1
// MaxSizeB 是 BLAKE2b 允许的最大摘要字节数(64)。
MaxSizeB = blake2b.Size
)
// NewB 按指定摘要长度创建 BLAKE2b 哈希;size 必须在 [MinSizeB, MaxSizeB] 内,否则返回 error。
func NewB(size int) (hash.Hash, error) {
if size < MinSizeB || size > MaxSizeB {
return nil, fmt.Errorf("blake2x: BLAKE2b 摘要长度 %d 超出范围 [%d, %d]", size, MinSizeB, MaxSizeB)
}
h, err := blake2b.New(size, nil)
if err != nil {
return nil, fmt.Errorf("blake2x: 创建 BLAKE2b-%d 失败: %w", size, err)
}
return h, nil
}
// New512 返回 BLAKE2b-512 哈希。
func New512() (hash.Hash, error) {
return NewB(SizeB512)
}
// New384 返回 BLAKE2b-384 哈希。
func New384() (hash.Hash, error) {
return NewB(SizeB384)
}
// New256B 返回 BLAKE2b-256 哈希。
func New256B() (hash.Hash, error) {
return NewB(SizeB256)
}
// New256S 返回 BLAKE2s-256 哈希。
func New256S() (hash.Hash, error) {
h, err := blake2s.New256(nil)
if err != nil {
return nil, fmt.Errorf("blake2x: 创建 BLAKE2s-256 失败: %w", err)
}
return h, nil
}
// New 等价于 New512。
func New() (hash.Hash, error) {
return New512()
}
// SumB 返回 data 的 BLAKE2b 摘要,长度由 size 指定;
// size 必须在 [MinSizeB, MaxSizeB] 内,否则返回 error。
func SumB(size int, data []byte) ([]byte, error) {
h, err := NewB(size)
if err != nil {
return nil, err
}
_, _ = h.Write(data)
return h.Sum(nil), nil
}
// Sum512 返回 data 的 BLAKE2b-512 摘要(SizeB512 字节原始数据)。
func Sum512(data []byte) []byte {
s := blake2b.Sum512(data)
return s[:]
}
// Sum384 返回 data 的 BLAKE2b-384 摘要(SizeB384 字节原始数据)。
func Sum384(data []byte) []byte {
s := blake2b.Sum384(data)
return s[:]
}
// Sum256B 返回 data 的 BLAKE2b-256 摘要(SizeB256 字节原始数据)。
func Sum256B(data []byte) []byte {
s := blake2b.Sum256(data)
return s[:]
}
// Sum256S 返回 data 的 BLAKE2s-256 摘要(SizeS256 字节原始数据)。
func Sum256S(data []byte) []byte {
s := blake2s.Sum256(data)
return s[:]
}
// Sum 等价于 Sum512。
func Sum(data []byte) []byte {
return Sum512(data)
}
// HexB 返回 data 的 BLAKE2b 摘要的小写十六进制字符串,长度由 size 指定。
func HexB(size int, data []byte) (string, error) {
sum, err := SumB(size, data)
if err != nil {
return "", err
}
return hex.EncodeToString(sum), nil
}
// Hex512 返回 data 的 BLAKE2b-512 摘要的小写十六进制字符串。
func Hex512(data []byte) string {
return hex.EncodeToString(Sum512(data))
}
// Hex384 返回 data 的 BLAKE2b-384 摘要的小写十六进制字符串。
func Hex384(data []byte) string {
return hex.EncodeToString(Sum384(data))
}
// Hex256B 返回 data 的 BLAKE2b-256 摘要的小写十六进制字符串。
func Hex256B(data []byte) string {
return hex.EncodeToString(Sum256B(data))
}
// Hex256S 返回 data 的 BLAKE2s-256 摘要的小写十六进制字符串。
func Hex256S(data []byte) string {
return hex.EncodeToString(Sum256S(data))
}
// Hex 等价于 Hex512。
func Hex(data []byte) string {
return Hex512(data)
}
// Base64Sum512 返回 data 的 BLAKE2b-512 摘要的标准 Base64 字符串。
func Base64Sum512(data []byte) string {
return base64.StdEncoding.EncodeToString(Sum512(data))
}
// Base64Sum384 返回 data 的 BLAKE2b-384 摘要的标准 Base64 字符串。
func Base64Sum384(data []byte) string {
return base64.StdEncoding.EncodeToString(Sum384(data))
}
// Base64Sum256B 返回 data 的 BLAKE2b-256 摘要的标准 Base64 字符串。
func Base64Sum256B(data []byte) string {
return base64.StdEncoding.EncodeToString(Sum256B(data))
}
// Base64Sum256S 返回 data 的 BLAKE2s-256 摘要的标准 Base64 字符串。
func Base64Sum256S(data []byte) string {
return base64.StdEncoding.EncodeToString(Sum256S(data))
}
// Base64 等价于 Base64Sum512。
func Base64(data []byte) string {
return Base64Sum512(data)
}
// SumReader 读取 r 中的全部数据并返回其 BLAKE2b-512 摘要。
func SumReader(r io.Reader) ([]byte, error) {
if r == nil {
return nil, fmt.Errorf("blake2x: 读取器为 nil")
}
h, err := New512()
if err != nil {
return nil, err
}
if _, err := io.Copy(h, r); err != nil {
return nil, fmt.Errorf("blake2x: 读取数据失败: %w", err)
}
return h.Sum(nil), nil
}
// HexReader 读取 r 中的全部数据并返回其 BLAKE2b-512 摘要的小写十六进制字符串。
func HexReader(r io.Reader) (string, error) {
sum, err := SumReader(r)
if err != nil {
return "", err
}
return hex.EncodeToString(sum), nil
}
// HexFile 计算 path 所指向文件的 BLAKE2b-512 摘要并返回小写十六进制字符串;
// 文件不存在或读取失败时返回包装后的 error,不 panic。
func HexFile(path string) (string, error) {
f, err := os.Open(path)
if err != nil {
return "", fmt.Errorf("blake2x: 打开文件 %q 失败: %w", path, err)
}
defer f.Close()
sum, err := SumReader(f)
if err != nil {
return "", fmt.Errorf("blake2x: 计算文件 %q 摘要失败: %w", path, err)
}
return hex.EncodeToString(sum), nil
}
+348
View File
@@ -0,0 +1,348 @@
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
}