2026-03-14 15:39:21 +08:00
|
|
|
package hashx
|
|
|
|
|
|
|
|
|
|
import (
|
2026-03-18 13:43:18 +08:00
|
|
|
"bytes"
|
2026-03-14 15:39:21 +08:00
|
|
|
"crypto/md5"
|
|
|
|
|
"encoding/hex"
|
|
|
|
|
"os"
|
|
|
|
|
"path/filepath"
|
|
|
|
|
"testing"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func TestSha1StrMatchesSha1(t *testing.T) {
|
|
|
|
|
got := Sha1Str([]byte("abc"))
|
|
|
|
|
const want = "a9993e364706816aba3e25717850c26c9cd0d89d"
|
|
|
|
|
if got != want {
|
|
|
|
|
t.Fatalf("Sha1Str mismatch, got %s want %s", got, want)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestSM3AndCRC32A(t *testing.T) {
|
|
|
|
|
if len(SM3([]byte("abc"))) != 32 {
|
|
|
|
|
t.Fatalf("SM3 digest size must be 32")
|
|
|
|
|
}
|
|
|
|
|
if Crc32AStr([]byte("123456789")) != "181989fc" {
|
|
|
|
|
t.Fatalf("Crc32AStr mismatch")
|
|
|
|
|
}
|
|
|
|
|
if CheckCRC32A([]byte("123456789")) != 0x181989fc {
|
|
|
|
|
t.Fatalf("CheckCRC32A mismatch")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-18 14:32:55 +08:00
|
|
|
func TestSumAllUnsupportedMethod(t *testing.T) {
|
|
|
|
|
_, err := SumAll([]byte("abc"), []string{"sha1", "unknown"})
|
|
|
|
|
if err == nil {
|
|
|
|
|
t.Fatalf("expected unsupported method error")
|
2026-03-14 15:39:21 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestFileSumAndFileSumAll(t *testing.T) {
|
|
|
|
|
dir := t.TempDir()
|
|
|
|
|
path := filepath.Join(dir, "sum.txt")
|
|
|
|
|
data := []byte("hash-file-content")
|
|
|
|
|
if err := os.WriteFile(path, data, 0o644); err != nil {
|
|
|
|
|
t.Fatalf("WriteFile failed: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
calls := 0
|
|
|
|
|
h, err := FileSum(path, "md5", func(float64) { calls++ })
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("FileSum failed: %v", err)
|
|
|
|
|
}
|
|
|
|
|
expected := md5.Sum(data)
|
|
|
|
|
if h != hex.EncodeToString(expected[:]) {
|
|
|
|
|
t.Fatalf("md5 mismatch, got %s want %s", h, hex.EncodeToString(expected[:]))
|
|
|
|
|
}
|
|
|
|
|
if calls == 0 {
|
|
|
|
|
t.Fatalf("progress callback should be called")
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-18 14:32:55 +08:00
|
|
|
all, err := FileSumAll(path, []string{"sha1", "crc32"}, nil)
|
2026-03-14 15:39:21 +08:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("FileSumAll failed: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if _, ok := all["sha1"]; !ok {
|
|
|
|
|
t.Fatalf("expected sha1 in FileSumAll")
|
|
|
|
|
}
|
|
|
|
|
if _, ok := all["crc32"]; !ok {
|
|
|
|
|
t.Fatalf("expected crc32 in FileSumAll")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestFileSumUnsupportedMethod(t *testing.T) {
|
|
|
|
|
dir := t.TempDir()
|
|
|
|
|
path := filepath.Join(dir, "sum.txt")
|
|
|
|
|
if err := os.WriteFile(path, []byte("x"), 0o644); err != nil {
|
|
|
|
|
t.Fatalf("WriteFile failed: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if _, err := FileSum(path, "not-support", nil); err == nil {
|
|
|
|
|
t.Fatalf("expected unsupported method error")
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-03-18 14:32:55 +08:00
|
|
|
|
|
|
|
|
func TestFileSumAllUnsupportedMethod(t *testing.T) {
|
|
|
|
|
dir := t.TempDir()
|
|
|
|
|
path := filepath.Join(dir, "sum.txt")
|
|
|
|
|
if err := os.WriteFile(path, []byte("x"), 0o644); err != nil {
|
|
|
|
|
t.Fatalf("WriteFile failed: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if _, err := FileSumAll(path, []string{"sha256", "not-support"}, nil); err == nil {
|
|
|
|
|
t.Fatalf("expected unsupported method error")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-18 13:43:18 +08:00
|
|
|
func TestPBKDF2SHA256Vector(t *testing.T) {
|
|
|
|
|
got, err := DerivePBKDF2SHA256Key("password", []byte("salt"), 1, 32)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("DerivePBKDF2SHA256Key failed: %v", err)
|
|
|
|
|
}
|
|
|
|
|
const want = "120fb6cffcf8b32c43e7225256c4f837a86548c92ccc35480805987cb70be17b"
|
|
|
|
|
if hex.EncodeToString(got) != want {
|
|
|
|
|
t.Fatalf("pbkdf2-sha256 vector mismatch: got %x want %s", got, want)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestPBKDF2AndArgon2Deterministic(t *testing.T) {
|
|
|
|
|
a, err := DerivePBKDF2SHA512Key("password", []byte("salt"), 1000, 32)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("DerivePBKDF2SHA512Key failed: %v", err)
|
|
|
|
|
}
|
|
|
|
|
b, err := DerivePBKDF2SHA512Key("password", []byte("salt"), 1000, 32)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("DerivePBKDF2SHA512Key failed: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if !bytes.Equal(a, b) {
|
|
|
|
|
t.Fatalf("pbkdf2-sha512 must be deterministic")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
params := Argon2Params{Time: 1, Memory: 32 * 1024, Threads: 1, KeyLen: 32}
|
|
|
|
|
argA, err := DeriveArgon2idKey("password", []byte("salt-salt"), params)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("DeriveArgon2idKey failed: %v", err)
|
|
|
|
|
}
|
|
|
|
|
argB, err := DeriveArgon2idKey("password", []byte("salt-salt"), params)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("DeriveArgon2idKey failed: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if !bytes.Equal(argA, argB) {
|
|
|
|
|
t.Fatalf("argon2id must be deterministic")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestKDFInvalidParams(t *testing.T) {
|
|
|
|
|
if _, err := DerivePBKDF2SHA256Key("password", nil, 1, 32); err == nil {
|
|
|
|
|
t.Fatalf("expected pbkdf2 salt error")
|
|
|
|
|
}
|
|
|
|
|
if _, err := DerivePBKDF2SHA256Key("password", []byte("salt"), 0, 32); err == nil {
|
|
|
|
|
t.Fatalf("expected pbkdf2 iterations error")
|
|
|
|
|
}
|
|
|
|
|
if _, err := DeriveArgon2idKey("password", []byte("salt"), Argon2Params{}); err == nil {
|
|
|
|
|
t.Fatalf("expected argon2 params error")
|
|
|
|
|
}
|
|
|
|
|
}
|