From fc8a72e617539cd8de95afe728e88b3bbba7a6f5 Mon Sep 17 00:00:00 2001 From: "kayos@tcp.direct" Date: Tue, 14 Nov 2023 14:29:25 -0800 Subject: [PATCH] Feat/Refactor[hash]: !!BREAKING!! add CRC32 and general refactor !!BREAKING!! --- common_test.go | 55 ---------------- hash/hash.go | 55 ++++++++++++---- hash/hash_test.go | 158 ++++++++++++++++++++++++---------------------- 3 files changed, 124 insertions(+), 144 deletions(-) diff --git a/common_test.go b/common_test.go index 298176e..d16e47e 100644 --- a/common_test.go +++ b/common_test.go @@ -2,67 +2,12 @@ package common import ( "errors" - "fmt" - "io" - "os" "sync" "testing" "git.tcp.direct/kayos/common/entropy" - "git.tcp.direct/kayos/common/hash" - "git.tcp.direct/kayos/common/squish" ) -var needle = []byte(entropy.RandStr(16)) - -func TestBlakeEqualAndB64(t *testing.T) { - var clone = make([]byte, len(needle)) - copy(clone, needle) - - if !hash.BlakeEqual(needle, clone) { - t.Fatalf("BlakeEqual failed! Values %v and %v should have been equal.\n|---->Lengths: %d and %d", - needle, clone, len(needle), len(clone), - ) - } - - falseclone := []byte(entropy.RandStr(16)) - if hash.BlakeEqual(needle, falseclone) { - t.Fatalf("BlakeEqual failed! Values %v and %v should NOT have been equal.\n|---->Lengths: %d and %d", - needle, clone, len(needle), len(clone), - ) - } - - var based = [2][]byte{needle, clone} - - based[0] = []byte(squish.B64e(based[0])) - based[1] = []byte(squish.B64e(based[0])) - - if hash.BlakeEqual(based[0], based[1]) { - t.Fatalf("Base64 encoding failed! Values %v and %v should NOT have been equal.\n|---->Lengths: %d and %d", - based[0], based[1], len(based[0]), len(based[1]), - ) - } - - // sneakin in some code coverage rq dwai nbd - bogusRd, bogusWrt := io.Pipe() - bogusRd2, bogusWrt2 := io.Pipe() - t.Logf("\n") - go func() { - Fprint(io.MultiWriter(bogusWrt, os.Stdout), fmt.Sprintf("[PASS] based[0] = %s", string(based[0]))) - Fprintf(io.MultiWriter(bogusWrt2, os.Stdout), "\n[PASS] based[1] = %s", string(based[0])) - }() - _ = bogusWrt.CloseWithError(io.ErrClosedPipe) - _ = bogusWrt2.CloseWithError(io.ErrClosedPipe) - _, err := bogusRd.Read([]byte{}) - if err == nil { - t.Fatalf("should have been an error...") - } - _, err = bogusRd2.Read([]byte{}) - if err == nil { - t.Fatalf("should have been an error...") - } -} - func TestAbs(t *testing.T) { var start = int32(entropy.RNG(5)) for start < 1 { diff --git a/hash/hash.go b/hash/hash.go index b5b0324..094dab9 100644 --- a/hash/hash.go +++ b/hash/hash.go @@ -1,7 +1,6 @@ package hash import ( - "bytes" "crypto/md5" //nolint:gosec "crypto/sha1" //nolint:gosec "crypto/sha256" @@ -9,6 +8,7 @@ import ( "errors" "fmt" "hash" + "hash/crc32" "io" "os" "sync" @@ -25,8 +25,37 @@ const ( TypeSHA256 TypeSHA512 TypeMD5 + TypeCRC32 ) +var typeToString = map[Type]string{ + TypeNull: "null", TypeBlake2b: "blake2b", TypeSHA1: "sha1", + TypeSHA256: "sha256", TypeSHA512: "sha512", + TypeMD5: "md5", TypeCRC32: "crc32", +} + +var stringToType = map[string]Type{ + "null": TypeNull, "blake2b": TypeBlake2b, "sha1": TypeSHA1, + "sha256": TypeSHA256, "sha512": TypeSHA512, + "md5": TypeMD5, "crc32": TypeCRC32, +} + +func StringToType(s string) Type { + t, ok := stringToType[s] + if !ok { + return TypeNull + } + return t +} + +func (t Type) String() string { + s, ok := typeToString[t] + if !ok { + return "unknown" + } + return s +} + var ( sha1Pool = &sync.Pool{ New: func() interface{} { @@ -54,6 +83,11 @@ var ( return h }, } + crc32Pool = &sync.Pool{ + New: func() interface{} { + return crc32.NewIEEE() + }, + } ) func Sum(ht Type, b []byte) []byte { @@ -74,6 +108,9 @@ func Sum(ht Type, b []byte) []byte { case TypeMD5: h = md5Pool.Get().(hash.Hash) defer md5Pool.Put(h) + case TypeCRC32: + h = crc32Pool.Get().(hash.Hash) + defer crc32Pool.Put(h) default: return nil } @@ -83,13 +120,8 @@ func Sum(ht Type, b []byte) []byte { return sum } -// Blake2bSum ignores all errors and gives you a blakae2b 64 hash value as a byte slice. (or panics somehow) -func Blake2bSum(b []byte) []byte { - return Sum(TypeBlake2b, b) -} - -// BlakeFileChecksum will attempt to calculate a blake2b checksum of the given file path's contents. -func BlakeFileChecksum(path string) (buf []byte, err error) { +// SumFile will attempt to calculate a blake2b checksum of the given file path's contents. +func SumFile(ht Type, path string) (buf []byte, err error) { var f *os.File f, err = os.Open(path) if err != nil { @@ -107,10 +139,5 @@ func BlakeFileChecksum(path string) (buf []byte, err error) { return nil, errors.New("file is empty") } - return Sum(TypeBlake2b, buf), nil -} - -// BlakeEqual will take in two byte slices, hash them with blake2b, and tell you if the resulting checksums match. -func BlakeEqual(a []byte, b []byte) bool { - return bytes.Equal(Blake2bSum(a), Blake2bSum(b)) + return Sum(ht, buf), nil } diff --git a/hash/hash_test.go b/hash/hash_test.go index 78dc700..6cb61bd 100644 --- a/hash/hash_test.go +++ b/hash/hash_test.go @@ -4,10 +4,11 @@ import ( "bytes" "encoding/base64" "os" + "path/filepath" + "strings" "testing" "git.tcp.direct/kayos/common/entropy" - "git.tcp.direct/kayos/common/squish" ) const ( @@ -16,89 +17,96 @@ const ( kayosSHA1 = "M23ElC0sQYAK+MaMZVmza2L8mss=" kayosSHA256 = "BagY0TmoGR3O7t80BGm4K6UHPlqEg6HJirwQmhrPK4U=" kayosSHA512 = "xiuo2na76acrWXCTTR++O1pPZabOhyj8nbfb5Go3e1pEq9VJYIsOioTXalf2GCuERmFecWkmaL5QI8mIXXWpNA==" + kayosCRC32 = "xtig5w==" ) -func TestBlake2bsum(t *testing.T) { - og := squish.B64d(kayosBlake2b) - newc := Blake2bSum([]byte("kayos\n")) - if !bytes.Equal(newc, og) { - t.Fatalf("wanted: %v, got %v", kayosBlake2b, squish.B64e(newc)) - } - if !BlakeEqual([]byte("kayos\n"), []byte{107, 97, 121, 111, 115, 10}) { - t.Fatalf("BlakeEqual should have been true. %s should == %s", []byte("kayos\n"), []byte{107, 97, 121, 111, 115, 92, 110}) - } - t.Logf("[blake2bSum] success: %s", kayosBlake2b) -} - -func TestBlakeFileChecksum(t *testing.T) { - path := t.TempDir() + "/blake2b.dat" - err := os.WriteFile(path, []byte{107, 97, 121, 111, 115, 10}, os.ModePerm) - if err != nil { - t.Errorf("[FAIL] failed to write test fle for TestBlakeFileChecksum: %s", err.Error()) - } - filecheck, err2 := BlakeFileChecksum(path) - if err2 != nil { - t.Errorf("[FAIL] failed to read test fle for TestBlakeFileChecksum: %s", err2.Error()) - } - if len(filecheck) == 0 { - t.Errorf("[FAIL] got nil output from BlakeFileChecksum") - } - if !bytes.Equal(filecheck, squish.B64d(kayosBlake2b)) { - t.Fatalf("[FAIL] wanted: %v, got %v", kayosBlake2b, squish.B64e(filecheck)) - } - badfile, err3 := BlakeFileChecksum(t.TempDir() + "/" + entropy.RandStr(50)) - if err3 == nil { - t.Errorf("[FAIL] shouldn't have been able to read phony file") - } - if len(badfile) != 0 { - t.Errorf("[FAIL] got non-nil output from bogus file: %v", badfile) - } - if !bytes.Equal(filecheck, squish.B64d(kayosBlake2b)) { - t.Fatalf("[FAIL] wanted: %v, got %v", kayosBlake2b, squish.B64e(filecheck)) - } - err = os.WriteFile(path+".empty", []byte{}, os.ModePerm) - if err != nil { - t.Errorf("[FAIL] failed to write test fle for TestBlakeFileChecksum: %s", err.Error()) - } - _, err4 := BlakeFileChecksum(path + ".empty") - if err4 == nil { - t.Fatalf("[FAIL] should have failed to read empty file") - } -} +var kayosByteSlice = []byte{107, 97, 121, 111, 115, 10} func TestSum(t *testing.T) { + t.Parallel() if Sum(TypeNull, []byte("yeet")) != nil { t.Fatal("Sum(TypeNull, []byte(\"yeet\")) should have returned nil") } var ( - ogsha1, _ = base64.StdEncoding.DecodeString(kayosSHA1) - ogsha256, _ = base64.StdEncoding.DecodeString(kayosSHA256) - ogsha512, _ = base64.StdEncoding.DecodeString(kayosSHA512) - ogmd5, _ = base64.StdEncoding.DecodeString(kayosMD5) - newsha1 = Sum(TypeSHA1, []byte("kayos\n")) - newsha256 = Sum(TypeSHA256, []byte("kayos\n")) - newsha512 = Sum(TypeSHA512, []byte("kayos\n")) - newmd5 = Sum(TypeMD5, []byte("kayos\n")) + ogsha1, _ = base64.StdEncoding.DecodeString(kayosSHA1) + ogsha256, _ = base64.StdEncoding.DecodeString(kayosSHA256) + ogsha512, _ = base64.StdEncoding.DecodeString(kayosSHA512) + ogmd5, _ = base64.StdEncoding.DecodeString(kayosMD5) + ogBlake2b, _ = base64.StdEncoding.DecodeString(kayosBlake2b) + ogCRC32, _ = base64.StdEncoding.DecodeString(kayosCRC32) + valids = map[Type][]byte{ + TypeSHA1: ogsha1, + TypeSHA256: ogsha256, + TypeSHA512: ogsha512, + TypeMD5: ogmd5, + TypeCRC32: ogCRC32, + TypeBlake2b: ogBlake2b, + } ) - - if !bytes.Equal(newsha1, ogsha1) { - t.Fatalf("[sha1] wanted: %v, got %v", ogsha1, newsha1) + for k, v := range valids { + typeToTest := k + valueToTest := v + t.Run(typeToTest.String()+"/string_check", func(t *testing.T) { + t.Parallel() + if !strings.EqualFold(typeToTest.String(), StringToType(typeToTest.String()).String()) { + t.Errorf("[FAIL] %s: wanted %s, got %s", typeToTest.String(), typeToTest.String(), StringToType(typeToTest.String()).String()) + } + }) + t.Run(typeToTest.String()+"/static_check", func(t *testing.T) { + t.Parallel() + mySum := Sum(typeToTest, kayosByteSlice) + if !bytes.Equal(mySum, valueToTest) { + t.Errorf("[FAIL] %s: wanted %v, got %v", typeToTest.String(), valueToTest, mySum) + } + }) + t.Run(typeToTest.String()+"/file_check", func(t *testing.T) { + t.Parallel() + path := filepath.Join(t.TempDir(), typeToTest.String()) // for coverage + if err := os.WriteFile(path, kayosByteSlice, os.ModePerm); err != nil { + t.Fatalf("[FAIL] failed to write test fle for TestSum: %s", err.Error()) + } + res, err := SumFile(typeToTest, path) + if err != nil { + t.Fatalf("[FAIL] failed to read test fle for TestSum: %s", err.Error()) + } + if !bytes.Equal(res, valueToTest) { + t.Errorf("[FAIL] %s: wanted %v, got %v", typeToTest.String(), valueToTest, res) + } + }) + } + t.Run("bad file", func(t *testing.T) { + t.Parallel() + _, err := SumFile(TypeSHA1, "/dev/null") + if err == nil { + t.Fatal("SumFile should have returned an error") + } + if _, err = SumFile(TypeSHA1, entropy.RandStrWithUpper(500)); err == nil { + t.Fatal("SumFile should have returned an error") + } + }) + t.Run("unknown type", func(t *testing.T) { + t.Parallel() + if Type(uint8(94)).String() != "unknown" { + t.Fatal("Type(uint(9453543)).String() should have returned \"unknown\"") + } + if StringToType(entropy.RandStr(10)) != TypeNull { + t.Fatal("bogus string should have returned TypeNull") + } + }) +} + +var benchData = []byte(entropy.RandStrWithUpper(5000)) + +func BenchmarkSum(b *testing.B) { + for _, sumType := range []Type{TypeSHA1, TypeSHA256, TypeSHA512, TypeBlake2b, TypeMD5} { + b.Run(sumType.String(), func(b *testing.B) { + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + b.SetBytes(int64(len(benchData))) + Sum(sumType, benchData) + } + }) } - t.Logf("[sha1] success: %s", kayosSHA1) - - if !bytes.Equal(newsha256, ogsha256) { - t.Fatalf("[sha256] wanted: %v, got %v", ogsha256, newsha256) - } - t.Logf("[sha256] success: %s", kayosSHA256) - - if !bytes.Equal(newsha512, ogsha512) { - t.Fatalf("[sha512] wanted: %v, got %v", ogsha512, newsha512) - } - t.Logf("[sha512] success: %s", kayosSHA512) - - if !bytes.Equal(newmd5, ogmd5) { - t.Fatalf("[md5] wanted: %v, got %v", ogmd5, newmd5) - } - t.Logf("[md5] success: %s", kayosMD5) }