Skip to content

Commit

Permalink
✨ feat: encodx - add sub package, add some files from strutil pkg
Browse files Browse the repository at this point in the history
  • Loading branch information
inhere committed Sep 16, 2023
1 parent f2b6edc commit bd472a7
Show file tree
Hide file tree
Showing 9 changed files with 323 additions and 0 deletions.
66 changes: 66 additions & 0 deletions encodx/encodx.go
@@ -0,0 +1,66 @@
package encodx

import (
"encoding/base32"
"encoding/base64"
)

// BaseEncoder interface
type BaseEncoder interface {
Encode(dst []byte, src []byte)
EncodeToString(src []byte) string
Decode(dst []byte, src []byte) (n int, err error)
DecodeString(s string) ([]byte, error)
}

//
// -------------------- base encode --------------------
//

// base32 encoding with no padding
var (
B32Std = base32.StdEncoding.WithPadding(base32.NoPadding)
B32Hex = base32.HexEncoding.WithPadding(base32.NoPadding)
)

// B32Encode base32 encode
func B32Encode(str string) string {
return B32Std.EncodeToString([]byte(str))
}

// B32Decode base32 decode
func B32Decode(str string) string {
dec, _ := B32Std.DecodeString(str)
return string(dec)
}

// base64 encoding with no padding
var (
B64Std = base64.StdEncoding.WithPadding(base64.NoPadding)
B64URL = base64.URLEncoding.WithPadding(base64.NoPadding)
)

// B64Encode base64 encode
func B64Encode(str string) string {
return B64Std.EncodeToString([]byte(str))
}

// B64EncodeBytes base64 encode
func B64EncodeBytes(src []byte) []byte {
buf := make([]byte, B64Std.EncodedLen(len(src)))
B64Std.Encode(buf, src)
return buf
}

// B64Decode base64 decode
func B64Decode(str string) string {
dec, _ := B64Std.DecodeString(str)
return string(dec)
}

// B64DecodeBytes base64 decode
func B64DecodeBytes(str []byte) []byte {
dbuf := make([]byte, B64Std.DecodedLen(len(str)))
n, _ := B64Std.Decode(dbuf, str)
return dbuf[:n]
}
29 changes: 29 additions & 0 deletions encodx/encodx_test.go
@@ -0,0 +1,29 @@
package encodx_test

import (
"testing"

"github.com/gookit/goutil/encodx"
"github.com/gookit/goutil/testutil/assert"
)

func TestBaseDecode(t *testing.T) {
is := assert.New(t)

is.Eq("GEZGCYTD", encodx.B32Encode("12abc"))
is.Eq("12abc", encodx.B32Decode("GEZGCYTD"))

// b23 hex
is.Eq("64P62OJ3", encodx.B32Hex.EncodeToString([]byte("12abc")))
// fmt.Println(time.Now().Format("20060102150405"))
dateStr := "20230908101122"
is.Eq("68O34CPG74O3GC9G64OJ4CG", encodx.B32Hex.EncodeToString([]byte(dateStr)))

is.Eq("YWJj", encodx.B64Encode("abc"))
is.Eq("abc", encodx.B64Decode("YWJj"))

is.Eq([]byte("YWJj"), encodx.B64EncodeBytes([]byte("abc")))
is.Eq([]byte("abc"), encodx.B64DecodeBytes([]byte("YWJj")))

is.Eq("MTJhYmM", encodx.B64URL.EncodeToString([]byte("12abc")))
}
122 changes: 122 additions & 0 deletions encodx/hashutil/hashutil.go
@@ -0,0 +1,122 @@
package hashutil

import (
"crypto/md5"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"encoding/hex"
"fmt"
"hash"
"hash/crc32"
"hash/crc64"
"strings"

"github.com/gookit/goutil/encodx"
)

// hash algorithm names
const (
AlgoCRC32 = "crc32"
AlgoCRC64 = "crc64"
AlgoMD5 = "md5"
AlgoSHA1 = "sha1"
AlgoSHA224 = "sha224"
AlgoSHA256 = "sha256"
AlgoSHA384 = "sha384"
AlgoSHA512 = "sha512"
)

// MD5 generate md5 string by given src
func MD5(src any) string {
return string(HexBytes(AlgoMD5, src))
}

// ShortMD5 Generate a 16-bit md5 bytes.
// remove first 8 and last 8 bytes from 32-bit md5.
func ShortMD5(src any) string {
return string(HexBytes(AlgoMD5, src)[8:24])
}

// Hash generate hex hash string by given algorithm
func Hash(algo string, src any) string {
return string(HexBytes(algo, src))
}

// HexBytes generate hex hash bytes by given algorithm
func HexBytes(algo string, src any) []byte {
bs := HashSum(algo, src)
dst := make([]byte, hex.EncodedLen(len(bs)))
hex.Encode(dst, bs)
return dst
}

// Hash32 generate hash by given algorithm, then use base32 encode.
func Hash32(algo string, src any) string {
return string(Base32Bytes(algo, src))
}

// Base32Bytes generate base32 hash bytes by given algorithm
func Base32Bytes(algo string, src any) []byte {
bs := HashSum(algo, src)
dst := make([]byte, encodx.B32Hex.EncodedLen(len(bs)))
encodx.B32Hex.Encode(dst, bs)
return dst
}

// Hash64 generate hash by given algorithm, then use base64 encode.
func Hash64(algo string, src any) string {
return string(Base64Bytes(algo, src))
}

// Base64Bytes generate base64 hash bytes by given algorithm
func Base64Bytes(algo string, src any) []byte {
bs := HashSum(algo, src)
dst := make([]byte, encodx.B64Std.EncodedLen(len(bs)))
encodx.B64Std.Encode(dst, bs)
return dst
}

// HashSum generate hash sum bytes by given algorithm
func HashSum(algo string, src any) []byte {
hh := NewHash(algo)
switch val := src.(type) {
case []byte:
hh.Write(val)
case string:
hh.Write([]byte(val))
default:
hh.Write([]byte(fmt.Sprint(src)))
}
return hh.Sum(nil)
}

// NewHash create hash.Hash instance
//
// algo: crc32, crc64, md5, sha1, sha224, sha256, sha384, sha512, sha512_224, sha512_256
func NewHash(algo string) hash.Hash {
switch strings.ToLower(algo) {
case AlgoCRC32:
return crc32.NewIEEE()
case AlgoCRC64:
return crc64.New(crc64.MakeTable(crc64.ISO))
case AlgoMD5:
return md5.New()
case AlgoSHA1:
return sha1.New()
case AlgoSHA224:
return sha256.New224()
case AlgoSHA256:
return sha256.New()
case AlgoSHA384:
return sha512.New384()
case AlgoSHA512:
return sha512.New()
case "sha512_224":
return sha512.New512_224()
case "sha512_256":
return sha512.New512_256()
default:
panic("invalid hash algorithm:" + algo)
}
}
75 changes: 75 additions & 0 deletions encodx/hashutil/hashutil_test.go
@@ -0,0 +1,75 @@
package hashutil_test

import (
"testing"

"github.com/gookit/goutil/encodx/hashutil"
"github.com/gookit/goutil/testutil/assert"
)

func TestHash(t *testing.T) {
tests := []struct {
src any
algo string
want string
}{
{"abc12", "crc32", "b744b523"},
{"abc12", "crc64", "41b31776c4200000"},
{"abc12", "md5", "b2157e7b2ae716a747597717f1efb7a0"},
{"abc12", "sha1", "8fe670fef2b8c74ef8987cdfccdb32e96ad4f9a2"},
}

for _, tt := range tests {
assert.Equal(t, tt.want, hashutil.Hash(tt.algo, tt.src))
}

assert.Panics(t, func() {
hashutil.Hash("unknown", nil)
})
}

func TestHash32(t *testing.T) {
tests := []struct {
src any
algo string
want string
}{
{"abc12", "crc32", "MT2BA8O"},
{"abc12", "crc64", "86PHETM440000"},
{"abc12", "md5", "M8ANSUPASSBAEHQPESBV3RTNK0"},
{"abc12", "sha1", "HVJ71VNIN33KTU4OFJFSPMPIT5LD9UD2"},
}

for _, tt := range tests {
assert.Equal(t, tt.want, hashutil.Hash32(tt.algo, tt.src))
}
}

func TestHash64(t *testing.T) {
tests := []struct {
src any
algo string
want string
}{
{"abc12", "crc32", "t0S1Iw"},
{"abc12", "crc64", "QbMXdsQgAAA"},
{"abc12", "md5", "shV+eyrnFqdHWXcX8e+3oA"},
{"abc12", "sha1", "j+Zw/vK4x074mHzfzNsy6WrU+aI"},
}

for _, tt := range tests {
assert.Equal(t, tt.want, hashutil.Hash64(tt.algo, tt.src))
}
}

func TestMd5(t *testing.T) {
assert.NotEmpty(t, hashutil.MD5("abc"))
assert.NotEmpty(t, hashutil.MD5([]int{12, 34}))

assert.Eq(t, "202cb962ac59075b964b07152d234b70", hashutil.MD5("123"))
assert.Eq(t, "900150983cd24fb0d6963f7d28e17f72", hashutil.MD5("abc"))

// short md5
assert.Eq(t, "ac59075b964b0715", hashutil.ShortMD5("123"))
assert.Eq(t, "3cd24fb0d6963f7d", hashutil.ShortMD5("abc"))
}
30 changes: 30 additions & 0 deletions encodx/hashutil/passwd.go
@@ -0,0 +1,30 @@
package hashutil

import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
)

// HashPasswd for quick hash an input password string, use sha256.
func HashPasswd(pwd, key string) string {
hm := hmac.New(sha256.New, []byte(key))
hm.Write([]byte(pwd))

return hex.EncodeToString(hm.Sum(nil))
}

// VerifyPasswd for quick verify input password is valid
//
// - wantPwd from db or config, generated by EncryptPasswd()
func VerifyPasswd(wantPwd, pwd, key string) bool {
decBts, err := hex.DecodeString(wantPwd)
if err != nil {
return false
}

hm := hmac.New(sha256.New, []byte(key))
hm.Write([]byte(pwd))

return hmac.Equal(decBts, hm.Sum(nil))
}
1 change: 1 addition & 0 deletions encodx/hashutil/passwd_test.go
@@ -0,0 +1 @@
package hashutil_test
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 comments on commit bd472a7

Please sign in to comment.