完善不同加密方法的使用
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
// Package ripemd160x 提供 RIPEMD-160 摘要计算,以及十六进制/Base64 编码与流式、文件接口。
|
||||
//
|
||||
// # 安全警告
|
||||
//
|
||||
// RIPEMD-160 的输出只有 160 位,且已不再被视为现代安全强度的哈希:
|
||||
// 它仅用于兼容旧系统(典型场景是 Bitcoin 等加密货币的 P2PKH 地址,
|
||||
// 即 RIPEMD160(SHA256(pubkey))),不可用于口令存储、口令派生或数字签名。
|
||||
// 其上游实现 golang.org/x/crypto/ripemd160 已被官方标记为废弃(deprecated)。
|
||||
// 新代码请使用 sha256x、sha512x、sha3x 或 blake2x。
|
||||
package ripemd160x
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"hash"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
//lint:ignore SA1019 RIPEMD-160 已被上游标记为废弃;本包保留它只为兼容既有系统(如比特币地址),包注释已明确警告其不可用于新设计。
|
||||
"golang.org/x/crypto/ripemd160"
|
||||
)
|
||||
|
||||
const (
|
||||
// Size 是 RIPEMD-160 摘要的字节数(20)。
|
||||
Size = ripemd160.Size
|
||||
// BlockSize 是 RIPEMD-160 分组的字节数(64)。
|
||||
BlockSize = ripemd160.BlockSize
|
||||
)
|
||||
|
||||
// New 返回一个计算 RIPEMD-160 摘要的 hash.Hash,可反复 Write 以流式处理数据。
|
||||
//
|
||||
// 安全警告:RIPEMD-160 仅用于兼容旧系统(如 Bitcoin 地址),不可用于口令或签名。
|
||||
func New() hash.Hash {
|
||||
return ripemd160.New()
|
||||
}
|
||||
|
||||
// Sum 返回 data 的 RIPEMD-160 摘要(Size 字节原始数据)。
|
||||
//
|
||||
// 安全警告:RIPEMD-160 仅用于兼容旧系统(如 Bitcoin 地址),不可用于口令或签名。
|
||||
func Sum(data []byte) []byte {
|
||||
h := ripemd160.New()
|
||||
_, _ = h.Write(data)
|
||||
return h.Sum(nil)
|
||||
}
|
||||
|
||||
// Hex 返回 data 的 RIPEMD-160 摘要的小写十六进制字符串。
|
||||
//
|
||||
// 安全警告:RIPEMD-160 仅用于兼容旧系统(如 Bitcoin 地址),不可用于口令或签名。
|
||||
func Hex(data []byte) string {
|
||||
return hex.EncodeToString(Sum(data))
|
||||
}
|
||||
|
||||
// Base64 返回 data 的 RIPEMD-160 摘要的标准 Base64 字符串。
|
||||
//
|
||||
// 安全警告:RIPEMD-160 仅用于兼容旧系统(如 Bitcoin 地址),不可用于口令或签名。
|
||||
func Base64(data []byte) string {
|
||||
return base64.StdEncoding.EncodeToString(Sum(data))
|
||||
}
|
||||
|
||||
// SumReader 读取 r 中的全部数据并返回其 RIPEMD-160 摘要;读取失败时返回包装后的 error。
|
||||
//
|
||||
// 安全警告:RIPEMD-160 仅用于兼容旧系统(如 Bitcoin 地址),不可用于口令或签名。
|
||||
func SumReader(r io.Reader) ([]byte, error) {
|
||||
if r == nil {
|
||||
return nil, fmt.Errorf("ripemd160x: 读取器为 nil")
|
||||
}
|
||||
h := ripemd160.New()
|
||||
if _, err := io.Copy(h, r); err != nil {
|
||||
return nil, fmt.Errorf("ripemd160x: 读取数据失败: %w", err)
|
||||
}
|
||||
return h.Sum(nil), nil
|
||||
}
|
||||
|
||||
// HexReader 读取 r 中的全部数据并返回其 RIPEMD-160 摘要的小写十六进制字符串。
|
||||
//
|
||||
// 安全警告:RIPEMD-160 仅用于兼容旧系统(如 Bitcoin 地址),不可用于口令或签名。
|
||||
func HexReader(r io.Reader) (string, error) {
|
||||
sum, err := SumReader(r)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return hex.EncodeToString(sum), nil
|
||||
}
|
||||
|
||||
// HexFile 计算 path 所指向文件的 RIPEMD-160 摘要并返回小写十六进制字符串;
|
||||
// 文件不存在或读取失败时返回包装后的 error,不 panic。
|
||||
//
|
||||
// 安全警告:RIPEMD-160 仅用于兼容旧系统(如 Bitcoin 地址),不可用于口令或签名。
|
||||
func HexFile(path string) (string, error) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("ripemd160x: 打开文件 %q 失败: %w", path, err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
sum, err := SumReader(f)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("ripemd160x: 计算文件 %q 摘要失败: %w", path, err)
|
||||
}
|
||||
return hex.EncodeToString(sum), nil
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
package ripemd160x_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"code.yun.ink/pkg/encryptx/ripemd160x"
|
||||
)
|
||||
|
||||
// 测试向量来源:RIPEMD-160 官方测试向量(Bosselaers/Preneel),
|
||||
// 与 golang.org/x/crypto/ripemd160 自带测试用例一致。
|
||||
var vectors = []struct {
|
||||
name string
|
||||
input string
|
||||
want string
|
||||
}{
|
||||
{"空串", "", "9c1185a5c5e9fc54612808977ee8f548b2258d31"},
|
||||
{"a", "a", "0bdc9d2d256b3ee9daae347be6f4dc835a467ffe"},
|
||||
{"abc", "abc", "8eb208f7e05d987a9b044a8e98c6b087f15a0bfc"},
|
||||
{"message digest", "message digest", "5d0689ef49d2fae572b881b123a85ffa21595f36"},
|
||||
{"a-z", "abcdefghijklmnopqrstuvwxyz", "f71c27109c692c1b56bbdceb5b9d2865b3708dbc"},
|
||||
}
|
||||
|
||||
type failingReader struct{}
|
||||
|
||||
func (failingReader) Read([]byte) (int, error) { return 0, errors.New("模拟读取失败") }
|
||||
|
||||
func TestConstants(t *testing.T) {
|
||||
if ripemd160x.Size != 20 {
|
||||
t.Fatalf("Size = %d, 期望 20", ripemd160x.Size)
|
||||
}
|
||||
if ripemd160x.BlockSize != 64 {
|
||||
t.Fatalf("BlockSize = %d, 期望 64", ripemd160x.BlockSize)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVectors(t *testing.T) {
|
||||
for _, v := range vectors {
|
||||
t.Run(v.name, func(t *testing.T) {
|
||||
data := []byte(v.input)
|
||||
sum := ripemd160x.Sum(data)
|
||||
if len(sum) != ripemd160x.Size {
|
||||
t.Fatalf("Sum 长度 = %d, 期望 %d", len(sum), ripemd160x.Size)
|
||||
}
|
||||
if got := hex.EncodeToString(sum); got != v.want {
|
||||
t.Errorf("Sum = %s, 期望 %s", got, v.want)
|
||||
}
|
||||
if got := ripemd160x.Hex(data); got != v.want {
|
||||
t.Errorf("Hex = %s, 期望 %s", got, v.want)
|
||||
}
|
||||
if want := base64.StdEncoding.EncodeToString(sum); ripemd160x.Base64(data) != want {
|
||||
t.Errorf("Base64 = %s, 期望 %s", ripemd160x.Base64(data), want)
|
||||
}
|
||||
h := ripemd160x.New()
|
||||
if _, err := h.Write(data); err != nil {
|
||||
t.Fatalf("Write 失败: %v", err)
|
||||
}
|
||||
if got := hex.EncodeToString(h.Sum(nil)); got != v.want {
|
||||
t.Errorf("New+Write = %s, 期望 %s", got, v.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStreamingMatchesSum(t *testing.T) {
|
||||
data := []byte(strings.Repeat("encryptx-ripemd160-", 100))
|
||||
h := ripemd160x.New()
|
||||
for i := 0; i < len(data); i += 19 {
|
||||
end := i + 19
|
||||
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), ripemd160x.Sum(data); !bytes.Equal(got, want) {
|
||||
t.Errorf("分块写入 = %x, 期望 %x", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReaderConsistency(t *testing.T) {
|
||||
const s = "encryptx 一致性检查"
|
||||
sum, err := ripemd160x.SumReader(strings.NewReader(s))
|
||||
if err != nil {
|
||||
t.Fatalf("SumReader 失败: %v", err)
|
||||
}
|
||||
if want := ripemd160x.Sum([]byte(s)); !bytes.Equal(sum, want) {
|
||||
t.Errorf("SumReader = %x, 期望 %x", sum, want)
|
||||
}
|
||||
hexStr, err := ripemd160x.HexReader(strings.NewReader(s))
|
||||
if err != nil {
|
||||
t.Fatalf("HexReader 失败: %v", err)
|
||||
}
|
||||
if want := ripemd160x.Hex([]byte(s)); hexStr != want {
|
||||
t.Errorf("HexReader = %s, 期望 %s", hexStr, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReaderErrors(t *testing.T) {
|
||||
if _, err := ripemd160x.SumReader(nil); err == nil {
|
||||
t.Fatal("nil 读取器应返回 error")
|
||||
}
|
||||
if _, err := ripemd160x.HexReader(nil); err == nil {
|
||||
t.Fatal("nil 读取器应返回 error")
|
||||
}
|
||||
if _, err := ripemd160x.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 := ripemd160x.HexFile(path)
|
||||
if err != nil {
|
||||
t.Fatalf("HexFile 失败: %v", err)
|
||||
}
|
||||
if want := ripemd160x.Hex(content); got != want {
|
||||
t.Errorf("HexFile = %s, 期望 %s", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHexFileErrors(t *testing.T) {
|
||||
if _, err := ripemd160x.HexFile(filepath.Join(t.TempDir(), "不存在.bin")); err == nil {
|
||||
t.Fatal("不存在的文件应返回 error")
|
||||
}
|
||||
if _, err := ripemd160x.HexFile(""); err == nil {
|
||||
t.Fatal("空路径应返回 error")
|
||||
}
|
||||
if _, err := ripemd160x.HexFile(t.TempDir()); err == nil {
|
||||
t.Fatal("目录路径应返回 error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLargeInput(t *testing.T) {
|
||||
data := largeData(1 << 20)
|
||||
want := ripemd160x.Hex(data)
|
||||
if got := ripemd160x.Hex(data); got != want {
|
||||
t.Errorf("重复计算结果不一致: %s vs %s", got, want)
|
||||
}
|
||||
|
||||
got, err := ripemd160x.HexReader(bytes.NewReader(data))
|
||||
if err != nil {
|
||||
t.Fatalf("HexReader 失败: %v", err)
|
||||
}
|
||||
if got != want {
|
||||
t.Errorf("HexReader = %s, 期望 %s", got, want)
|
||||
}
|
||||
|
||||
h := ripemd160x.New()
|
||||
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