完善不同加密方法的使用

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
+347 -29
View File
@@ -1,45 +1,363 @@
// Package sha3x 提供 SHA3-224/256/384/512 摘要计算与 SHAKE128/SHAKE256 可扩展输出函数(XOF),
// 并附带十六进制/Base64 编码与流式、文件接口。
//
// SHAKE 是 XOF:输出长度由调用方指定,必须显式校验。本包要求 outputLength 在
// [1, MaxShakeOutputLength] 之间,超出范围返回 error 而不是 panic 或分配巨大内存。
//
// 命名约定:带位数后缀的函数(Sum224、Hex224、New224 等)明确指定算法变体;
// 不带后缀的 Sum、Hex、Base64、New、SumReader、HexReader、HexFile 等价于 256 位变体。
package sha3x
import (
"encoding/base64"
"encoding/hex"
"fmt"
"hash"
"io"
"os"
"golang.org/x/crypto/sha3"
)
// SHA3-256 哈希
func Sha3_256(src string) string {
h := sha3.New256()
h.Write([]byte(src))
return hex.EncodeToString(h.Sum(nil))
const (
// Size224 是 SHA3-224 摘要的字节数(28)。
Size224 = 28
// Size256 是 SHA3-256 摘要的字节数(32)。
Size256 = 32
// Size384 是 SHA3-384 摘要的字节数(48)。
Size384 = 48
// Size512 是 SHA3-512 摘要的字节数(64)。
Size512 = 64
// Rate224 是 SHA3-224 的海绵分组(rate)字节数(144)。
Rate224 = 144
// Rate256 是 SHA3-256 的海绵分组(rate)字节数(136)。
Rate256 = 136
// Rate384 是 SHA3-384 的海绵分组(rate)字节数(104)。
Rate384 = 104
// Rate512 是 SHA3-512 的海绵分组(rate)字节数(72)。
Rate512 = 72
// MaxShakeOutputLength 是 SHAKE128/SHAKE256 单次允许请求的最大输出字节数(1 MiB)。
MaxShakeOutputLength = 1 << 20
)
// New224 返回一个计算 SHA3-224 摘要的 hash.Hash。
func New224() hash.Hash {
return sha3.New224()
}
// SHA3-384 哈希
func Sha3_384(src string) string {
h := sha3.New384()
h.Write([]byte(src))
return hex.EncodeToString(h.Sum(nil))
// New256 返回一个计算 SHA3-256 摘要的 hash.Hash。
func New256() hash.Hash {
return sha3.New256()
}
// SHA3-512 哈希
func Sha3_512(src string) string {
h := sha3.New512()
h.Write([]byte(src))
return hex.EncodeToString(h.Sum(nil))
// New384 返回一个计算 SHA3-384 摘要的 hash.Hash。
func New384() hash.Hash {
return sha3.New384()
}
// SHAKE128 可变长度哈希
func Shake128(src string, outputLength int) string {
h := sha3.NewShake128()
h.Write([]byte(src))
output := make([]byte, outputLength)
h.Read(output)
return hex.EncodeToString(output)
// New512 返回一个计算 SHA3-512 摘要的 hash.Hash。
func New512() hash.Hash {
return sha3.New512()
}
// SHAKE256 可变长度哈希
func Shake256(src string, outputLength int) string {
h := sha3.NewShake256()
h.Write([]byte(src))
output := make([]byte, outputLength)
h.Read(output)
return hex.EncodeToString(output)
// New 等价于 New256。
func New() hash.Hash {
return New256()
}
// NewShake128 返回一个 SHAKE128 XOF 实例,可流式 Write 输入后按需 Read 任意长度输出。
// 调用方需自行保证输出长度合理(参见 MaxShakeOutputLength)。
func NewShake128() sha3.ShakeHash {
return sha3.NewShake128()
}
// NewShake256 返回一个 SHAKE256 XOF 实例,可流式 Write 输入后按需 Read 任意长度输出。
// 调用方需自行保证输出长度合理(参见 MaxShakeOutputLength)。
func NewShake256() sha3.ShakeHash {
return sha3.NewShake256()
}
// Sum224 返回 data 的 SHA3-224 摘要(Size224 字节原始数据)。
func Sum224(data []byte) []byte {
return sum(sha3.New224(), data)
}
// Sum256 返回 data 的 SHA3-256 摘要(Size256 字节原始数据)。
func Sum256(data []byte) []byte {
return sum(sha3.New256(), data)
}
// Sum384 返回 data 的 SHA3-384 摘要(Size384 字节原始数据)。
func Sum384(data []byte) []byte {
return sum(sha3.New384(), data)
}
// Sum512 返回 data 的 SHA3-512 摘要(Size512 字节原始数据)。
func Sum512(data []byte) []byte {
return sum(sha3.New512(), data)
}
// Sum 等价于 Sum256。
func Sum(data []byte) []byte {
return Sum256(data)
}
// Hex224 返回 data 的 SHA3-224 摘要的小写十六进制字符串。
func Hex224(data []byte) string {
return hex.EncodeToString(Sum224(data))
}
// Hex256 返回 data 的 SHA3-256 摘要的小写十六进制字符串。
func Hex256(data []byte) string {
return hex.EncodeToString(Sum256(data))
}
// Hex384 返回 data 的 SHA3-384 摘要的小写十六进制字符串。
func Hex384(data []byte) string {
return hex.EncodeToString(Sum384(data))
}
// Hex512 返回 data 的 SHA3-512 摘要的小写十六进制字符串。
func Hex512(data []byte) string {
return hex.EncodeToString(Sum512(data))
}
// Hex 等价于 Hex256。
func Hex(data []byte) string {
return Hex256(data)
}
// Base64Sum224 返回 data 的 SHA3-224 摘要的标准 Base64 字符串。
func Base64Sum224(data []byte) string {
return base64.StdEncoding.EncodeToString(Sum224(data))
}
// Base64Sum256 返回 data 的 SHA3-256 摘要的标准 Base64 字符串。
func Base64Sum256(data []byte) string {
return base64.StdEncoding.EncodeToString(Sum256(data))
}
// Base64Sum384 返回 data 的 SHA3-384 摘要的标准 Base64 字符串。
func Base64Sum384(data []byte) string {
return base64.StdEncoding.EncodeToString(Sum384(data))
}
// Base64Sum512 返回 data 的 SHA3-512 摘要的标准 Base64 字符串。
func Base64Sum512(data []byte) string {
return base64.StdEncoding.EncodeToString(Sum512(data))
}
// Base64 等价于 Base64Sum256。
func Base64(data []byte) string {
return Base64Sum256(data)
}
// Sum224Reader 读取 r 中的全部数据并返回其 SHA3-224 摘要。
func Sum224Reader(r io.Reader) ([]byte, error) {
return sumReader(sha3.New224(), r)
}
// Sum256Reader 读取 r 中的全部数据并返回其 SHA3-256 摘要。
func Sum256Reader(r io.Reader) ([]byte, error) {
return sumReader(sha3.New256(), r)
}
// Sum384Reader 读取 r 中的全部数据并返回其 SHA3-384 摘要。
func Sum384Reader(r io.Reader) ([]byte, error) {
return sumReader(sha3.New384(), r)
}
// Sum512Reader 读取 r 中的全部数据并返回其 SHA3-512 摘要。
func Sum512Reader(r io.Reader) ([]byte, error) {
return sumReader(sha3.New512(), r)
}
// SumReader 等价于 Sum256Reader。
func SumReader(r io.Reader) ([]byte, error) {
return Sum256Reader(r)
}
// Hex224Reader 读取 r 中的全部数据并返回其 SHA3-224 摘要的小写十六进制字符串。
func Hex224Reader(r io.Reader) (string, error) {
return hexReader(sha3.New224(), r)
}
// Hex256Reader 读取 r 中的全部数据并返回其 SHA3-256 摘要的小写十六进制字符串。
func Hex256Reader(r io.Reader) (string, error) {
return hexReader(sha3.New256(), r)
}
// Hex384Reader 读取 r 中的全部数据并返回其 SHA3-384 摘要的小写十六进制字符串。
func Hex384Reader(r io.Reader) (string, error) {
return hexReader(sha3.New384(), r)
}
// Hex512Reader 读取 r 中的全部数据并返回其 SHA3-512 摘要的小写十六进制字符串。
func Hex512Reader(r io.Reader) (string, error) {
return hexReader(sha3.New512(), r)
}
// HexReader 等价于 Hex256Reader。
func HexReader(r io.Reader) (string, error) {
return Hex256Reader(r)
}
// HexFile224 计算 path 所指向文件的 SHA3-224 摘要并返回小写十六进制字符串。
func HexFile224(path string) (string, error) {
return hexFile(path, "SHA3-224", sha3.New224)
}
// HexFile256 计算 path 所指向文件的 SHA3-256 摘要并返回小写十六进制字符串。
func HexFile256(path string) (string, error) {
return hexFile(path, "SHA3-256", sha3.New256)
}
// HexFile384 计算 path 所指向文件的 SHA3-384 摘要并返回小写十六进制字符串。
func HexFile384(path string) (string, error) {
return hexFile(path, "SHA3-384", sha3.New384)
}
// HexFile512 计算 path 所指向文件的 SHA3-512 摘要并返回小写十六进制字符串。
func HexFile512(path string) (string, error) {
return hexFile(path, "SHA3-512", sha3.New512)
}
// HexFile 等价于 HexFile256。
func HexFile(path string) (string, error) {
return HexFile256(path)
}
// Shake128 返回 data 经 SHAKE128 扩展后的 outputLength 字节输出。
// outputLength 必须大于 0 且不超过 MaxShakeOutputLength,否则返回 error。
func Shake128(data []byte, outputLength int) ([]byte, error) {
return shakeSum(sha3.NewShake128(), data, outputLength)
}
// Shake256 返回 data 经 SHAKE256 扩展后的 outputLength 字节输出。
// outputLength 必须大于 0 且不超过 MaxShakeOutputLength,否则返回 error。
func Shake256(data []byte, outputLength int) ([]byte, error) {
return shakeSum(sha3.NewShake256(), data, outputLength)
}
// Shake128Hex 返回 data 经 SHAKE128 扩展后的 outputLength 字节输出的小写十六进制字符串。
// outputLength 必须大于 0 且不超过 MaxShakeOutputLength,否则返回 error。
func Shake128Hex(data []byte, outputLength int) (string, error) {
out, err := Shake128(data, outputLength)
if err != nil {
return "", err
}
return hex.EncodeToString(out), nil
}
// Shake256Hex 返回 data 经 SHAKE256 扩展后的 outputLength 字节输出的小写十六进制字符串。
// outputLength 必须大于 0 且不超过 MaxShakeOutputLength,否则返回 error。
func Shake256Hex(data []byte, outputLength int) (string, error) {
out, err := Shake256(data, outputLength)
if err != nil {
return "", err
}
return hex.EncodeToString(out), nil
}
// Shake128Reader 读取 r 中的全部数据并返回其 SHAKE128 扩展的 outputLength 字节输出。
// outputLength 必须大于 0 且不超过 MaxShakeOutputLength,否则返回 error。
func Shake128Reader(r io.Reader, outputLength int) ([]byte, error) {
return shakeSumReader(sha3.NewShake128(), r, outputLength)
}
// Shake256Reader 读取 r 中的全部数据并返回其 SHAKE256 扩展的 outputLength 字节输出。
// outputLength 必须大于 0 且不超过 MaxShakeOutputLength,否则返回 error。
func Shake256Reader(r io.Reader, outputLength int) ([]byte, error) {
return shakeSumReader(sha3.NewShake256(), r, outputLength)
}
// sum 把 data 一次性写入 h 并取出摘要。
func sum(h hash.Hash, data []byte) []byte {
_, _ = h.Write(data)
return h.Sum(nil)
}
// sumReader 把 r 的全部内容写入 h 并取出摘要。
func sumReader(h hash.Hash, r io.Reader) ([]byte, error) {
if r == nil {
return nil, fmt.Errorf("sha3x: 读取器为 nil")
}
if _, err := io.Copy(h, r); err != nil {
return nil, fmt.Errorf("sha3x: 读取数据失败: %w", err)
}
return h.Sum(nil), nil
}
// hexReader 读取 r 的全部内容并以小写十六进制返回摘要。
func hexReader(h hash.Hash, r io.Reader) (string, error) {
sum, err := sumReader(h, r)
if err != nil {
return "", err
}
return hex.EncodeToString(sum), nil
}
// hexFile 计算文件的小写十六进制摘要,name 仅用于错误信息。
func hexFile(path, name string, newHash func() hash.Hash) (string, error) {
f, err := os.Open(path)
if err != nil {
return "", fmt.Errorf("sha3x: 打开文件 %q 失败: %w", path, err)
}
defer f.Close()
sum, err := sumReader(newHash(), f)
if err != nil {
return "", fmt.Errorf("sha3x: 计算文件 %q 的 %s 摘要失败: %w", path, name, err)
}
return hex.EncodeToString(sum), nil
}
// checkShakeLength 校验 XOF 输出长度是否在允许范围内。
func checkShakeLength(outputLength int) error {
if outputLength <= 0 {
return fmt.Errorf("sha3x: SHAKE 输出长度必须大于 0,当前为 %d", outputLength)
}
if outputLength > MaxShakeOutputLength {
return fmt.Errorf("sha3x: SHAKE 输出长度 %d 超过上限 %d", outputLength, MaxShakeOutputLength)
}
return nil
}
// readShake 从已写入输入的 XOF 中精确读取 outputLength 字节。
func readShake(h sha3.ShakeHash, outputLength int) ([]byte, error) {
if err := checkShakeLength(outputLength); err != nil {
return nil, err
}
out := make([]byte, outputLength)
if _, err := io.ReadFull(h, out); err != nil {
return nil, fmt.Errorf("sha3x: 读取 SHAKE 输出失败: %w", err)
}
return out, nil
}
// shakeSum 计算 data 的 XOF 输出。
func shakeSum(h sha3.ShakeHash, data []byte, outputLength int) ([]byte, error) {
if err := checkShakeLength(outputLength); err != nil {
return nil, err
}
if _, err := h.Write(data); err != nil {
return nil, fmt.Errorf("sha3x: 写入 SHAKE 输入失败: %w", err)
}
return readShake(h, outputLength)
}
// shakeSumReader 计算 r 全部内容的 XOF 输出。
func shakeSumReader(h sha3.ShakeHash, r io.Reader, outputLength int) ([]byte, error) {
if r == nil {
return nil, fmt.Errorf("sha3x: 读取器为 nil")
}
if err := checkShakeLength(outputLength); err != nil {
return nil, err
}
if _, err := io.Copy(h, r); err != nil {
return nil, fmt.Errorf("sha3x: 读取数据失败: %w", err)
}
return readShake(h, outputLength)
}
+453 -170
View File
@@ -1,219 +1,502 @@
package sha3x
package sha3x_test
import (
"bytes"
"encoding/base64"
"encoding/hex"
"errors"
"hash"
"io"
"os"
"path/filepath"
"strings"
"testing"
"code.yun.ink/pkg/encryptx/sha3x"
"golang.org/x/crypto/sha3"
)
func TestSha3_256(t *testing.T) {
// 测试基本功能
input := "hello world"
hash := Sha3_256(input)
// 测试向量来源:NIST FIPS 202 / SHA-3 标准示例,并经 Python 3 hashlib 独立复算确认。
var vectors = []struct {
name string
newHash func() hash.Hash
sum func([]byte) []byte
hex func([]byte) string
b64 func([]byte) string
sumReader func(io.Reader) ([]byte, error)
hexReader func(io.Reader) (string, error)
hexFile func(string) (string, error)
size int
empty string
abc string
}{
{
name: "SHA3-224",
newHash: sha3x.New224,
sum: sha3x.Sum224,
hex: sha3x.Hex224,
b64: sha3x.Base64Sum224,
sumReader: sha3x.Sum224Reader,
hexReader: sha3x.Hex224Reader,
hexFile: sha3x.HexFile224,
size: sha3x.Size224,
empty: "6b4e03423667dbb73b6e15454f0eb1abd4597f9a1b078e3f5b5a6bc7",
abc: "e642824c3f8cf24ad09234ee7d3c766fc9a3a5168d0c94ad73b46fdf",
},
{
name: "SHA3-256",
newHash: sha3x.New256,
sum: sha3x.Sum256,
hex: sha3x.Hex256,
b64: sha3x.Base64Sum256,
sumReader: sha3x.Sum256Reader,
hexReader: sha3x.Hex256Reader,
hexFile: sha3x.HexFile256,
size: sha3x.Size256,
empty: "a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a",
abc: "3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532",
},
{
name: "SHA3-384",
newHash: sha3x.New384,
sum: sha3x.Sum384,
hex: sha3x.Hex384,
b64: sha3x.Base64Sum384,
sumReader: sha3x.Sum384Reader,
hexReader: sha3x.Hex384Reader,
hexFile: sha3x.HexFile384,
size: sha3x.Size384,
empty: "0c63a75b845e4f7d01107d852e4c2485c51a50aaaa94fc61995e71bbee983a2ac3713831264adb47fb6bd1e058d5f004",
abc: "ec01498288516fc926459f58e2c6ad8df9b473cb0fc08c2596da7cf0e49be4b298d88cea927ac7f539f1edf228376d25",
},
{
name: "SHA3-512",
newHash: sha3x.New512,
sum: sha3x.Sum512,
hex: sha3x.Hex512,
b64: sha3x.Base64Sum512,
sumReader: sha3x.Sum512Reader,
hexReader: sha3x.Hex512Reader,
hexFile: sha3x.HexFile512,
size: sha3x.Size512,
empty: "a69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a615b2123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26",
abc: "b751850b1a57168a5693cd924b6b096e08f621827444f70d884f5d0240d2712e10e116e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0",
},
}
// SHA3-256应该产生64字符的十六进制字符串
if len(hash) != 64 {
t.Errorf("SHA3-256哈希长度错误,期望: 64, 实际: %d", len(hash))
type failingReader struct{}
func (failingReader) Read([]byte) (int, error) { return 0, errors.New("模拟读取失败") }
func TestConstants(t *testing.T) {
sizes := map[string][2]int{
"SHA3-224": {sha3x.Size224, sha3x.Rate224},
"SHA3-256": {sha3x.Size256, sha3x.Rate256},
"SHA3-384": {sha3x.Size384, sha3x.Rate384},
"SHA3-512": {sha3x.Size512, sha3x.Rate512},
}
// 相同输入应该产生相同输出
hash2 := Sha3_256(input)
if hash != hash2 {
t.Errorf("相同输入产生了不同的哈希: %s vs %s", hash, hash2)
want := map[string][2]int{
"SHA3-224": {28, 144},
"SHA3-256": {32, 136},
"SHA3-384": {48, 104},
"SHA3-512": {64, 72},
}
// 不同输入应该产生不同输出
hash3 := Sha3_256("different input")
if hash == hash3 {
t.Errorf("不同输入产生了相同的哈希")
for name, got := range sizes {
if got != want[name] {
t.Errorf("%s: Size/Rate = %v, 期望 %v", name, got, want[name])
}
}
if sha3x.MaxShakeOutputLength != 1<<20 {
t.Errorf("MaxShakeOutputLength = %d, 期望 %d", sha3x.MaxShakeOutputLength, 1<<20)
}
}
func TestSha3_384(t *testing.T) {
// 测试基本功能
input := "hello world"
hash := Sha3_384(input)
// SHA3-384应该产生96字符的十六进制字符串
if len(hash) != 96 {
t.Errorf("SHA3-384哈希长度错误,期望: 96, 实际: %d", len(hash))
}
// 相同输入应该产生相同输出
hash2 := Sha3_384(input)
if hash != hash2 {
t.Errorf("相同输入产生了不同的哈希: %s vs %s", hash, hash2)
}
// 不同输入应该产生不同输出
hash3 := Sha3_384("different input")
if hash == hash3 {
t.Errorf("不同输入产生了相同的哈希")
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 := v.newHash()
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 TestSha3_512(t *testing.T) {
// 测试基本功能
input := "hello world"
hash := Sha3_512(input)
// SHA3-512应该产生128字符的十六进制字符串
if len(hash) != 128 {
t.Errorf("SHA3-512哈希长度错误,期望: 128, 实际: %d", len(hash))
func TestDefaultAliasesMatch256(t *testing.T) {
data := []byte("encryptx alias check")
if !bytes.Equal(sha3x.Sum(data), sha3x.Sum256(data)) {
t.Error("Sum 应等价于 Sum256")
}
// 相同输入应该产生相同输出
hash2 := Sha3_512(input)
if hash != hash2 {
t.Errorf("相同输入产生了不同的哈希: %s vs %s", hash, hash2)
if sha3x.Hex(data) != sha3x.Hex256(data) {
t.Error("Hex 应等价于 Hex256")
}
// 不同输入应该产生不同输出
hash3 := Sha3_512("different input")
if hash == hash3 {
t.Errorf("不同输入产生了相同的哈希")
if sha3x.Base64(data) != sha3x.Base64Sum256(data) {
t.Error("Base64 应等价于 Base64Sum256")
}
if h := sha3x.New(); h.Size() != sha3x.Size256 {
t.Errorf("New().Size() = %d, 期望 %d", h.Size(), sha3x.Size256)
}
if got, err := sha3x.SumReader(strings.NewReader(string(data))); err != nil {
t.Fatalf("SumReader 失败: %v", err)
} else if !bytes.Equal(got, sha3x.Sum256(data)) {
t.Error("SumReader 应等价于 Sum256Reader")
}
if got, err := sha3x.HexReader(strings.NewReader(string(data))); err != nil {
t.Fatalf("HexReader 失败: %v", err)
} else if got != sha3x.Hex256(data) {
t.Error("HexReader 应等价于 Hex256Reader")
}
}
func TestShake128(t *testing.T) {
// 测试基本功能
input := "hello world"
// 测试不同输出长度
hash16 := Shake128(input, 16) // 16字节
if len(hash16) != 32 { // 16字节 = 32个十六进制字符
t.Errorf("SHAKE128(16字节)哈希长度错误,期望: 32, 实际: %d", len(hash16))
}
hash32 := Shake128(input, 32) // 32字节
if len(hash32) != 64 { // 32字节 = 64个十六进制字符
t.Errorf("SHAKE128(32字节)哈希长度错误,期望: 64, 实际: %d", len(hash32))
}
// 相同输入和长度应该产生相同输出
hash16_2 := Shake128(input, 16)
if hash16 != hash16_2 {
t.Errorf("相同输入和长度产生了不同的哈希: %s vs %s", hash16, hash16_2)
}
// 不同长度应该产生不同输出
if hash16 == hash32 {
t.Errorf("不同长度产生了相同的哈希")
func TestStreamingMatchesSum(t *testing.T) {
data := []byte(strings.Repeat("encryptx-sha3-", 200))
for _, v := range vectors {
t.Run(v.name, func(t *testing.T) {
h := v.newHash()
for i := 0; i < len(data); i += 17 {
end := i + 17
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 TestShake256(t *testing.T) {
// 测试基本功能
input := "hello world"
// 测试不同输出长度
hash16 := Shake256(input, 16) // 16字节
if len(hash16) != 32 { // 16字节 = 32个十六进制字符
t.Errorf("SHAKE256(16字节)哈希长度错误,期望: 32, 实际: %d", len(hash16))
}
hash32 := Shake256(input, 32) // 32字节
if len(hash32) != 64 { // 32字节 = 64个十六进制字符
t.Errorf("SHAKE256(32字节)哈希长度错误,期望: 64, 实际: %d", len(hash32))
}
// 相同输入和长度应该产生相同输出
hash16_2 := Shake256(input, 16)
if hash16 != hash16_2 {
t.Errorf("相同输入和长度产生了不同的哈希: %s vs %s", hash16, hash16_2)
}
// 不同长度应该产生不同输出
if hash16 == hash32 {
t.Errorf("不同长度产生了相同的哈希")
func TestReaderConsistency(t *testing.T) {
const s = "encryptx 一致性检查"
for _, v := range vectors {
t.Run(v.name, func(t *testing.T) {
sum, err := v.sumReader(strings.NewReader(s))
if err != nil {
t.Fatalf("SumReader 失败: %v", err)
}
if want := v.sum([]byte(s)); !bytes.Equal(sum, want) {
t.Errorf("SumReader = %x, 期望 %x", sum, want)
}
hexStr, err := v.hexReader(strings.NewReader(s))
if err != nil {
t.Fatalf("HexReader 失败: %v", err)
}
if want := v.hex([]byte(s)); hexStr != want {
t.Errorf("HexReader = %s, 期望 %s", hexStr, want)
}
})
}
}
func TestSha3Consistency(t *testing.T) {
// 测试一致性
input := "test string"
// 多次计算应该得到相同结果
hash1 := Sha3_256(input)
hash2 := Sha3_256(input)
hash3 := Sha3_256(input)
if hash1 != hash2 || hash2 != hash3 {
t.Errorf("多次计算应该得到相同的哈希")
func TestReaderErrors(t *testing.T) {
readers := []func(io.Reader) ([]byte, error){
sha3x.Sum224Reader, sha3x.Sum256Reader, sha3x.Sum384Reader, sha3x.Sum512Reader, sha3x.SumReader,
}
// SHAKE也应该一致
shake1 := Shake128(input, 32)
shake2 := Shake128(input, 32)
shake3 := Shake128(input, 32)
if shake1 != shake2 || shake2 != shake3 {
t.Errorf("SHAKE多次计算应该得到相同的哈希")
for i, fn := range readers {
if _, err := fn(nil); err == nil {
t.Errorf("第 %d 个读取函数:nil 读取器应返回 error", i)
}
if _, err := fn(failingReader{}); err == nil {
t.Errorf("第 %d 个读取函数:读取失败时应返回 error", i)
}
}
hexReaders := []func(io.Reader) (string, error){
sha3x.Hex224Reader, sha3x.Hex256Reader, sha3x.Hex384Reader, sha3x.Hex512Reader, sha3x.HexReader,
}
for i, fn := range hexReaders {
if _, err := fn(nil); err == nil {
t.Errorf("第 %d 个十六进制读取函数:nil 读取器应返回 error", i)
}
if _, err := fn(failingReader{}); err == nil {
t.Errorf("第 %d 个十六进制读取函数:读取失败时应返回 error", i)
}
}
}
func TestSha3EdgeCases(t *testing.T) {
// 空字符串
emptyHash := Sha3_256("")
if len(emptyHash) != 64 {
t.Errorf("空字符串的SHA3-256哈希长度错误,期望: 64, 实际: %d", len(emptyHash))
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)
}
// 长字符串
longString := strings.Repeat("a", 10000)
longHash := Sha3_256(longString)
if len(longHash) != 64 {
t.Errorf("长字符串的SHA3-256哈希长度错误,期望: 64, 实际: %d", len(longHash))
for _, v := range vectors {
t.Run(v.name, func(t *testing.T) {
got, err := v.hexFile(path)
if err != nil {
t.Fatalf("HexFile 失败: %v", err)
}
if want := v.hex(content); got != want {
t.Errorf("HexFile = %s, 期望 %s", got, want)
}
})
}
// 特殊字符
specialChars := Sha3_256("!@#$%^&*()_+-=[]{}|;'\",./<>?")
if len(specialChars) != 64 {
t.Errorf("特殊字符的SHA3-256哈希长度错误,期望: 64, 实际: %d", len(specialChars))
if got, err := sha3x.HexFile(path); err != nil {
t.Fatalf("HexFile 失败: %v", err)
} else if want := sha3x.Hex256(content); got != want {
t.Errorf("HexFile = %s, 期望 %s", got, want)
}
}
func TestSha3VsSha2(t *testing.T) {
// SHA3和SHA2应该产生不同的哈希
input := "test string"
// sha2_256 := "test" // 这里应该调用sha256x.Sha256(input),但为了简化测试,我们直接比较
sha3_256 := Sha3_256(input)
// 由于SHA2和SHA3算法不同,它们应该产生不同的哈希
// 这里我们只检查长度是否相同
if len(sha3_256) != 64 {
t.Errorf("SHA3-256哈希长度错误,期望: 64, 实际: %d", len(sha3_256))
func TestHexFileErrors(t *testing.T) {
missing := filepath.Join(t.TempDir(), "不存在.bin")
files := []func(string) (string, error){
sha3x.HexFile224, sha3x.HexFile256, sha3x.HexFile384, sha3x.HexFile512, sha3x.HexFile,
}
for i, fn := range files {
if _, err := fn(missing); err == nil {
t.Errorf("第 %d 个文件函数:不存在的文件应返回 error", i)
}
if _, err := fn(""); err == nil {
t.Errorf("第 %d 个文件函数:空路径应返回 error", i)
}
if _, err := fn(t.TempDir()); err == nil {
t.Errorf("第 %d 个文件函数:目录路径应返回 error", i)
}
}
}
func BenchmarkSha3_256(b *testing.B) {
input := "benchmark test string"
b.ResetTimer()
for i := 0; i < b.N; i++ {
Sha3_256(input)
// 测试向量来源:NIST FIPS 202 SHAKE 示例,并经 Python 3 hashlib 独立复算确认。
func TestShakeVectors(t *testing.T) {
cases := []struct {
name string
fn func([]byte, int) ([]byte, error)
fnHex func([]byte, int) (string, error)
input string
n int
wantHex string
}{
{"SHAKE128 空串 16 字节", sha3x.Shake128, sha3x.Shake128Hex, "", 16, "7f9c2ba4e88f827d616045507605853e"},
{"SHAKE128 abc 16 字节", sha3x.Shake128, sha3x.Shake128Hex, "abc", 16, "5881092dd818bf5cf8a3ddb793fbcba7"},
{"SHAKE256 空串 32 字节", sha3x.Shake256, sha3x.Shake256Hex, "", 32, "46b9dd2b0ba88d13233b3feb743eeb243fcd52ea62b81b82b50c27646ed5762f"},
{"SHAKE256 abc 32 字节", sha3x.Shake256, sha3x.Shake256Hex, "abc", 32, "483366601360a8771c6863080cc4114d8db44530f8f1e1ee4f94ea37e78b5739"},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
out, err := tc.fn([]byte(tc.input), tc.n)
if err != nil {
t.Fatalf("计算失败: %v", err)
}
if len(out) != tc.n {
t.Fatalf("输出长度 = %d, 期望 %d", len(out), tc.n)
}
if got := hex.EncodeToString(out); got != tc.wantHex {
t.Errorf("输出 = %s, 期望 %s", got, tc.wantHex)
}
gotHex, err := tc.fnHex([]byte(tc.input), tc.n)
if err != nil {
t.Fatalf("计算十六进制失败: %v", err)
}
if gotHex != tc.wantHex {
t.Errorf("十六进制输出 = %s, 期望 %s", gotHex, tc.wantHex)
}
})
}
}
func BenchmarkSha3_512(b *testing.B) {
input := "benchmark test string"
b.ResetTimer()
for i := 0; i < b.N; i++ {
Sha3_512(input)
func TestShakeLengthValidation(t *testing.T) {
bad := []int{0, -1, -1024, sha3x.MaxShakeOutputLength + 1, 1 << 30}
for _, n := range bad {
if _, err := sha3x.Shake128([]byte("x"), n); err == nil {
t.Errorf("Shake128 长度 %d 应返回 error", n)
}
if _, err := sha3x.Shake256([]byte("x"), n); err == nil {
t.Errorf("Shake256 长度 %d 应返回 error", n)
}
if _, err := sha3x.Shake128Hex([]byte("x"), n); err == nil {
t.Errorf("Shake128Hex 长度 %d 应返回 error", n)
}
if _, err := sha3x.Shake256Hex([]byte("x"), n); err == nil {
t.Errorf("Shake256Hex 长度 %d 应返回 error", n)
}
if _, err := sha3x.Shake128Reader(strings.NewReader("x"), n); err == nil {
t.Errorf("Shake128Reader 长度 %d 应返回 error", n)
}
if _, err := sha3x.Shake256Reader(strings.NewReader("x"), n); err == nil {
t.Errorf("Shake256Reader 长度 %d 应返回 error", n)
}
}
if _, err := sha3x.Shake128Reader(nil, 16); err == nil {
t.Error("nil 读取器应返回 error")
}
if _, err := sha3x.Shake256Reader(nil, 16); err == nil {
t.Error("nil 读取器应返回 error")
}
if _, err := sha3x.Shake128Reader(failingReader{}, 16); err == nil {
t.Error("读取失败时应返回 error")
}
}
func BenchmarkShake128(b *testing.B) {
input := "benchmark test string"
b.ResetTimer()
for i := 0; i < b.N; i++ {
Shake128(input, 32)
func TestShakeOutputProperties(t *testing.T) {
full, err := sha3x.Shake128([]byte("xyz"), 64)
if err != nil {
t.Fatalf("计算失败: %v", err)
}
short, err := sha3x.Shake128([]byte("xyz"), 32)
if err != nil {
t.Fatalf("计算失败: %v", err)
}
if !bytes.Equal(short, full[:32]) {
t.Error("XOF 前缀性质不成立:短输出应是长输出的前缀")
}
other, err := sha3x.Shake128([]byte("xy"), 64)
if err != nil {
t.Fatalf("计算失败: %v", err)
}
if bytes.Equal(full, other) {
t.Error("不同输入的 SHAKE 输出不应相同")
}
max, err := sha3x.Shake256([]byte("x"), sha3x.MaxShakeOutputLength)
if err != nil {
t.Fatalf("上限长度计算失败: %v", err)
}
if len(max) != sha3x.MaxShakeOutputLength {
t.Errorf("上限长度输出 = %d, 期望 %d", len(max), sha3x.MaxShakeOutputLength)
}
}
func BenchmarkShake256(b *testing.B) {
input := "benchmark test string"
b.ResetTimer()
for i := 0; i < b.N; i++ {
Shake256(input, 32)
func TestShakeReaderConsistency(t *testing.T) {
const s = "encryptx shake reader"
data := []byte(s)
got, err := sha3x.Shake128Reader(strings.NewReader(s), 48)
if err != nil {
t.Fatalf("Shake128Reader 失败: %v", err)
}
if want, err := sha3x.Shake128(data, 48); err != nil {
t.Fatalf("Shake128 失败: %v", err)
} else if !bytes.Equal(got, want) {
t.Errorf("Shake128Reader = %x, 期望 %x", got, want)
}
got256, err := sha3x.Shake256Reader(strings.NewReader(s), 48)
if err != nil {
t.Fatalf("Shake256Reader 失败: %v", err)
}
if want, err := sha3x.Shake256(data, 48); err != nil {
t.Fatalf("Shake256 失败: %v", err)
} else if !bytes.Equal(got256, want) {
t.Errorf("Shake256Reader = %x, 期望 %x", got256, want)
}
}
func TestShakeStreaming(t *testing.T) {
data := []byte(strings.Repeat("encryptx-xof-", 64))
cases := []struct {
name string
newXOF func() sha3.ShakeHash
wantOut func([]byte, int) ([]byte, error)
}{
{"SHAKE128", sha3x.NewShake128, sha3x.Shake128},
{"SHAKE256", sha3x.NewShake256, sha3x.Shake256},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
h := tc.newXOF()
for i := 0; i < len(data); i += 5 {
end := i + 5
if end > len(data) {
end = len(data)
}
if _, err := h.Write(data[i:end]); err != nil {
t.Fatalf("Write 失败: %v", err)
}
}
out := make([]byte, 40)
if _, err := io.ReadFull(h, out); err != nil {
t.Fatalf("读取 XOF 输出失败: %v", err)
}
want, err := tc.wantOut(data, 40)
if err != nil {
t.Fatalf("计算期望值失败: %v", err)
}
if !bytes.Equal(out, want) {
t.Errorf("流式 XOF = %x, 期望 %x", out, want)
}
// 同一 XOF 实例续读时应保持输出连续。
rest := make([]byte, 24)
if _, err := io.ReadFull(h, rest); err != nil {
t.Fatalf("续读 XOF 输出失败: %v", err)
}
full, err := tc.wantOut(data, 64)
if err != nil {
t.Fatalf("计算期望值失败: %v", err)
}
if !bytes.Equal(rest, full[40:]) {
t.Errorf("续读输出 = %x, 期望 %x", rest, full[40:])
}
})
}
}
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)
}
got, err := v.hexReader(bytes.NewReader(data))
if err != nil {
t.Fatalf("HexReader 失败: %v", err)
}
if got != want {
t.Errorf("HexReader = %s, 期望 %s", got, want)
}
h := v.newHash()
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)
}
})
}
}
// largeData 生成确定性的 n 字节数据。
func largeData(n int) []byte {
b := make([]byte, n)
for i := range b {
b[i] = byte(i*31 + i/7)
}
return b
}