完善不同加密方法的使用
This commit is contained in:
+124
-6
@@ -1,14 +1,132 @@
|
||||
// Package sha512x 提供 SHA-384 与 SHA-512 摘要计算,以及十六进制/Base64 编码与流式、文件接口。
|
||||
//
|
||||
// 命名约定:带位数后缀的函数(Sum384、Hex384、New384 等)明确指定算法变体;
|
||||
// 不带后缀的 Sum、Hex、Base64、New、SumReader、HexReader、HexFile 等价于 512 位变体,
|
||||
// 因为 SHA-512 是本包最常用的变体。
|
||||
package sha512x
|
||||
|
||||
import (
|
||||
"crypto/sha512"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"hash"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
// Sha512加密
|
||||
func Sha512(src string) string {
|
||||
m := sha512.New()
|
||||
m.Write([]byte(src))
|
||||
res := hex.EncodeToString(m.Sum(nil))
|
||||
return res
|
||||
const (
|
||||
// Size384 是 SHA-384 摘要的字节数(48)。
|
||||
Size384 = sha512.Size384
|
||||
// Size512 是 SHA-512 摘要的字节数(64)。
|
||||
Size512 = sha512.Size
|
||||
// BlockSize 是 SHA-384/SHA-512 分组的字节数(128)。
|
||||
BlockSize = sha512.BlockSize
|
||||
)
|
||||
|
||||
// New384 返回一个计算 SHA-384 摘要的 hash.Hash。
|
||||
func New384() hash.Hash {
|
||||
return sha512.New384()
|
||||
}
|
||||
|
||||
// New512 返回一个计算 SHA-512 摘要的 hash.Hash。
|
||||
func New512() hash.Hash {
|
||||
return sha512.New()
|
||||
}
|
||||
|
||||
// New 等价于 New512。
|
||||
func New() hash.Hash {
|
||||
return New512()
|
||||
}
|
||||
|
||||
// Sum384 返回 data 的 SHA-384 摘要(Size384 字节原始数据)。
|
||||
func Sum384(data []byte) []byte {
|
||||
return sum(sha512.New384(), data)
|
||||
}
|
||||
|
||||
// Sum512 返回 data 的 SHA-512 摘要(Size512 字节原始数据)。
|
||||
func Sum512(data []byte) []byte {
|
||||
return sum(sha512.New(), data)
|
||||
}
|
||||
|
||||
// Sum 等价于 Sum512。
|
||||
func Sum(data []byte) []byte {
|
||||
return Sum512(data)
|
||||
}
|
||||
|
||||
// Hex384 返回 data 的 SHA-384 摘要的小写十六进制字符串。
|
||||
func Hex384(data []byte) string {
|
||||
return hex.EncodeToString(Sum384(data))
|
||||
}
|
||||
|
||||
// Hex512 返回 data 的 SHA-512 摘要的小写十六进制字符串。
|
||||
func Hex512(data []byte) string {
|
||||
return hex.EncodeToString(Sum512(data))
|
||||
}
|
||||
|
||||
// Hex 等价于 Hex512。
|
||||
func Hex(data []byte) string {
|
||||
return Hex512(data)
|
||||
}
|
||||
|
||||
// Base64Sum384 返回 data 的 SHA-384 摘要的标准 Base64 字符串。
|
||||
func Base64Sum384(data []byte) string {
|
||||
return base64.StdEncoding.EncodeToString(Sum384(data))
|
||||
}
|
||||
|
||||
// Base64Sum512 返回 data 的 SHA-512 摘要的标准 Base64 字符串。
|
||||
func Base64Sum512(data []byte) string {
|
||||
return base64.StdEncoding.EncodeToString(Sum512(data))
|
||||
}
|
||||
|
||||
// Base64 等价于 Base64Sum512。
|
||||
func Base64(data []byte) string {
|
||||
return Base64Sum512(data)
|
||||
}
|
||||
|
||||
// SumReader 读取 r 中的全部数据并返回其 SHA-512 摘要;读取失败时返回包装后的 error。
|
||||
func SumReader(r io.Reader) ([]byte, error) {
|
||||
return sumReader(sha512.New(), r)
|
||||
}
|
||||
|
||||
// HexReader 读取 r 中的全部数据并返回其 SHA-512 摘要的小写十六进制字符串。
|
||||
func HexReader(r io.Reader) (string, error) {
|
||||
sum, err := SumReader(r)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return hex.EncodeToString(sum), nil
|
||||
}
|
||||
|
||||
// HexFile 计算 path 所指向文件的 SHA-512 摘要并返回小写十六进制字符串;
|
||||
// 文件不存在或读取失败时返回包装后的 error,不 panic。
|
||||
func HexFile(path string) (string, error) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("sha512x: 打开文件 %q 失败: %w", path, err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
sum, err := sumReader(sha512.New(), f)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("sha512x: 计算文件 %q 摘要失败: %w", path, err)
|
||||
}
|
||||
return hex.EncodeToString(sum), nil
|
||||
}
|
||||
|
||||
// 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("sha512x: 读取器为 nil")
|
||||
}
|
||||
if _, err := io.Copy(h, r); err != nil {
|
||||
return nil, fmt.Errorf("sha512x: 读取数据失败: %w", err)
|
||||
}
|
||||
return h.Sum(nil), nil
|
||||
}
|
||||
|
||||
+230
-58
@@ -1,80 +1,252 @@
|
||||
package sha512x
|
||||
package sha512x_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"code.yun.ink/pkg/encryptx/sha512x"
|
||||
)
|
||||
|
||||
func TestSha512(t *testing.T) {
|
||||
// 测试基本功能
|
||||
input := "hello world"
|
||||
hash := Sha512(input)
|
||||
// 测试向量来源:FIPS 180-4 示例与 NIST 标准测试向量,并经 Python 3 hashlib 独立复算确认。
|
||||
var vectors384 = []struct {
|
||||
name string
|
||||
input string
|
||||
want string
|
||||
}{
|
||||
{"空串", "", "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b"},
|
||||
{"abc", "abc", "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7"},
|
||||
}
|
||||
|
||||
// SHA512应该产生128字符的十六进制字符串
|
||||
if len(hash) != 128 {
|
||||
t.Errorf("SHA512哈希长度错误,期望: 128, 实际: %d", len(hash))
|
||||
var vectors512 = []struct {
|
||||
name string
|
||||
input string
|
||||
want string
|
||||
}{
|
||||
{"空串", "", "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"},
|
||||
{"abc", "abc", "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"},
|
||||
}
|
||||
|
||||
type failingReader struct{}
|
||||
|
||||
func (failingReader) Read([]byte) (int, error) { return 0, errors.New("模拟读取失败") }
|
||||
|
||||
func TestConstants(t *testing.T) {
|
||||
if sha512x.Size384 != 48 || sha512x.Size512 != 64 {
|
||||
t.Fatalf("Size384 = %d, Size512 = %d, 期望 48 / 64", sha512x.Size384, sha512x.Size512)
|
||||
}
|
||||
|
||||
// 相同输入应该产生相同输出
|
||||
hash2 := Sha512(input)
|
||||
if hash != hash2 {
|
||||
t.Errorf("相同输入产生了不同的哈希: %s vs %s", hash, hash2)
|
||||
}
|
||||
|
||||
// 不同输入应该产生不同输出
|
||||
hash3 := Sha512("different input")
|
||||
if hash == hash3 {
|
||||
t.Errorf("不同输入产生了相同的哈希")
|
||||
if sha512x.BlockSize != 128 {
|
||||
t.Fatalf("BlockSize = %d, 期望 128", sha512x.BlockSize)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSha512Consistency(t *testing.T) {
|
||||
// 测试一致性
|
||||
input := "test string"
|
||||
|
||||
// 多次计算应该得到相同结果
|
||||
hash1 := Sha512(input)
|
||||
hash2 := Sha512(input)
|
||||
hash3 := Sha512(input)
|
||||
|
||||
if hash1 != hash2 || hash2 != hash3 {
|
||||
t.Errorf("多次计算应该得到相同的哈希")
|
||||
func TestVectors384(t *testing.T) {
|
||||
for _, v := range vectors384 {
|
||||
t.Run(v.name, func(t *testing.T) {
|
||||
data := []byte(v.input)
|
||||
sum := sha512x.Sum384(data)
|
||||
if len(sum) != sha512x.Size384 {
|
||||
t.Fatalf("Sum384 长度 = %d, 期望 %d", len(sum), sha512x.Size384)
|
||||
}
|
||||
if got := hex.EncodeToString(sum); got != v.want {
|
||||
t.Errorf("Sum384 = %s, 期望 %s", got, v.want)
|
||||
}
|
||||
if got := sha512x.Hex384(data); got != v.want {
|
||||
t.Errorf("Hex384 = %s, 期望 %s", got, v.want)
|
||||
}
|
||||
if want := base64.StdEncoding.EncodeToString(sum); sha512x.Base64Sum384(data) != want {
|
||||
t.Errorf("Base64Sum384 = %s, 期望 %s", sha512x.Base64Sum384(data), want)
|
||||
}
|
||||
h := sha512x.New384()
|
||||
if _, err := h.Write(data); err != nil {
|
||||
t.Fatalf("Write 失败: %v", err)
|
||||
}
|
||||
if got := hex.EncodeToString(h.Sum(nil)); got != v.want {
|
||||
t.Errorf("New384+Write = %s, 期望 %s", got, v.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSha512EdgeCases(t *testing.T) {
|
||||
// 空字符串
|
||||
emptyHash := Sha512("")
|
||||
if len(emptyHash) != 128 {
|
||||
t.Errorf("空字符串的SHA512哈希长度错误,期望: 128, 实际: %d", len(emptyHash))
|
||||
}
|
||||
|
||||
// 长字符串
|
||||
longString := strings.Repeat("a", 10000)
|
||||
longHash := Sha512(longString)
|
||||
if len(longHash) != 128 {
|
||||
t.Errorf("长字符串的SHA512哈希长度错误,期望: 128, 实际: %d", len(longHash))
|
||||
}
|
||||
|
||||
// 特殊字符
|
||||
specialChars := Sha512("!@#$%^&*()_+-=[]{}|;':\",./<>?")
|
||||
if len(specialChars) != 128 {
|
||||
t.Errorf("特殊字符的SHA512哈希长度错误,期望: 128, 实际: %d", len(specialChars))
|
||||
func TestVectors512(t *testing.T) {
|
||||
for _, v := range vectors512 {
|
||||
t.Run(v.name, func(t *testing.T) {
|
||||
data := []byte(v.input)
|
||||
sum := sha512x.Sum512(data)
|
||||
if len(sum) != sha512x.Size512 {
|
||||
t.Fatalf("Sum512 长度 = %d, 期望 %d", len(sum), sha512x.Size512)
|
||||
}
|
||||
if got := hex.EncodeToString(sum); got != v.want {
|
||||
t.Errorf("Sum512 = %s, 期望 %s", got, v.want)
|
||||
}
|
||||
if got := sha512x.Hex512(data); got != v.want {
|
||||
t.Errorf("Hex512 = %s, 期望 %s", got, v.want)
|
||||
}
|
||||
if want := base64.StdEncoding.EncodeToString(sum); sha512x.Base64Sum512(data) != want {
|
||||
t.Errorf("Base64Sum512 = %s, 期望 %s", sha512x.Base64Sum512(data), want)
|
||||
}
|
||||
h := sha512x.New512()
|
||||
if _, err := h.Write(data); err != nil {
|
||||
t.Fatalf("Write 失败: %v", err)
|
||||
}
|
||||
if got := hex.EncodeToString(h.Sum(nil)); got != v.want {
|
||||
t.Errorf("New512+Write = %s, 期望 %s", got, v.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSha512(b *testing.B) {
|
||||
input := "benchmark test string"
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
Sha512(input)
|
||||
func TestDefaultAliasesMatch512(t *testing.T) {
|
||||
data := []byte("encryptx alias check")
|
||||
if !bytes.Equal(sha512x.Sum(data), sha512x.Sum512(data)) {
|
||||
t.Error("Sum 应等价于 Sum512")
|
||||
}
|
||||
if sha512x.Hex(data) != sha512x.Hex512(data) {
|
||||
t.Error("Hex 应等价于 Hex512")
|
||||
}
|
||||
if sha512x.Base64(data) != sha512x.Base64Sum512(data) {
|
||||
t.Error("Base64 应等价于 Base64Sum512")
|
||||
}
|
||||
if h := sha512x.New(); h.Size() != sha512x.Size512 {
|
||||
t.Errorf("New().Size() = %d, 期望 %d", h.Size(), sha512x.Size512)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSha512Long(b *testing.B) {
|
||||
input := strings.Repeat("benchmark test string ", 100) // 约2.5KB
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
Sha512(input)
|
||||
func TestVariantsDiffer(t *testing.T) {
|
||||
data := []byte("abc")
|
||||
if sha512x.Hex384(data) == sha512x.Hex512(data) {
|
||||
t.Error("SHA-384 与 SHA-512 的摘要不应相同")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStreamingMatchesSum(t *testing.T) {
|
||||
data := []byte(strings.Repeat("encryptx-sha512-", 100))
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
h hashLike
|
||||
whole []byte
|
||||
}{
|
||||
{"SHA-384", sha512x.New384(), sha512x.Sum384(data)},
|
||||
{"SHA-512", sha512x.New512(), sha512x.Sum512(data)},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
for i := 0; i < len(data); i += 11 {
|
||||
end := i + 11
|
||||
if end > len(data) {
|
||||
end = len(data)
|
||||
}
|
||||
if _, err := tc.h.Write(data[i:end]); err != nil {
|
||||
t.Fatalf("Write 失败: %v", err)
|
||||
}
|
||||
}
|
||||
if sum := tc.h.Sum(nil); !bytes.Equal(sum, tc.whole) {
|
||||
t.Errorf("分块写入 = %x, 期望 %x", sum, tc.whole)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// hashLike 是测试用的最小哈希接口。
|
||||
type hashLike interface {
|
||||
Write([]byte) (int, error)
|
||||
Sum([]byte) []byte
|
||||
}
|
||||
|
||||
func TestReaderConsistency(t *testing.T) {
|
||||
const s = "encryptx 一致性检查"
|
||||
sum, err := sha512x.SumReader(strings.NewReader(s))
|
||||
if err != nil {
|
||||
t.Fatalf("SumReader 失败: %v", err)
|
||||
}
|
||||
if want := sha512x.Sum512([]byte(s)); !bytes.Equal(sum, want) {
|
||||
t.Errorf("SumReader = %x, 期望 %x", sum, want)
|
||||
}
|
||||
hexStr, err := sha512x.HexReader(strings.NewReader(s))
|
||||
if err != nil {
|
||||
t.Fatalf("HexReader 失败: %v", err)
|
||||
}
|
||||
if want := sha512x.Hex512([]byte(s)); hexStr != want {
|
||||
t.Errorf("HexReader = %s, 期望 %s", hexStr, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReaderErrors(t *testing.T) {
|
||||
if _, err := sha512x.SumReader(nil); err == nil {
|
||||
t.Fatal("nil 读取器应返回 error")
|
||||
}
|
||||
if _, err := sha512x.HexReader(nil); err == nil {
|
||||
t.Fatal("nil 读取器应返回 error")
|
||||
}
|
||||
if _, err := sha512x.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 := sha512x.HexFile(path)
|
||||
if err != nil {
|
||||
t.Fatalf("HexFile 失败: %v", err)
|
||||
}
|
||||
if want := sha512x.Hex512(content); got != want {
|
||||
t.Errorf("HexFile = %s, 期望 %s", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHexFileErrors(t *testing.T) {
|
||||
if _, err := sha512x.HexFile(filepath.Join(t.TempDir(), "不存在.bin")); err == nil {
|
||||
t.Fatal("不存在的文件应返回 error")
|
||||
}
|
||||
if _, err := sha512x.HexFile(""); err == nil {
|
||||
t.Fatal("空路径应返回 error")
|
||||
}
|
||||
if _, err := sha512x.HexFile(t.TempDir()); err == nil {
|
||||
t.Fatal("目录路径应返回 error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLargeInput(t *testing.T) {
|
||||
data := largeData(1 << 20)
|
||||
want := sha512x.Hex512(data)
|
||||
if got := sha512x.Hex512(data); got != want {
|
||||
t.Errorf("重复计算结果不一致: %s vs %s", got, want)
|
||||
}
|
||||
|
||||
got, err := sha512x.HexReader(bytes.NewReader(data))
|
||||
if err != nil {
|
||||
t.Fatalf("HexReader 失败: %v", err)
|
||||
}
|
||||
if got != want {
|
||||
t.Errorf("HexReader = %s, 期望 %s", got, want)
|
||||
}
|
||||
|
||||
h := sha512x.New512()
|
||||
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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user