From 025af96d4fdc489e162dfd5ca66180f60a975c78 Mon Sep 17 00:00:00 2001 From: Jackson Tian Date: Sat, 17 Aug 2024 00:04:18 +0800 Subject: [PATCH] move utils into internal package --- credentials/internal/utils/utils.go | 146 ++++++++++++++++++++++ credentials/internal/utils/utils_test.go | 152 +++++++++++++++++++++++ 2 files changed, 298 insertions(+) create mode 100644 credentials/internal/utils/utils.go create mode 100644 credentials/internal/utils/utils_test.go diff --git a/credentials/internal/utils/utils.go b/credentials/internal/utils/utils.go new file mode 100644 index 0000000..7468407 --- /dev/null +++ b/credentials/internal/utils/utils.go @@ -0,0 +1,146 @@ +package utils + +import ( + "crypto" + "crypto/hmac" + "crypto/md5" + "crypto/rand" + "crypto/rsa" + "crypto/sha1" + "crypto/x509" + "encoding/base64" + "encoding/hex" + "hash" + "io" + rand2 "math/rand" + "net/url" + "time" +) + +type uuid [16]byte + +const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + +var hookRead = func(fn func(p []byte) (n int, err error)) func(p []byte) (n int, err error) { + return fn +} + +var hookRSA = func(fn func(rand io.Reader, priv *rsa.PrivateKey, hash crypto.Hash, hashed []byte) ([]byte, error)) func(rand io.Reader, priv *rsa.PrivateKey, hash crypto.Hash, hashed []byte) ([]byte, error) { + return fn +} + +// GetUUID returns a uuid +func GetUUID() (uuidHex string) { + uuid := newUUID() + uuidHex = hex.EncodeToString(uuid[:]) + return +} + +// RandStringBytes returns a rand string +func RandStringBytes(n int) string { + b := make([]byte, n) + for i := range b { + b[i] = letterBytes[rand2.Intn(len(letterBytes))] + } + return string(b) +} + +// ShaHmac1 return a string which has been hashed +func ShaHmac1(source, secret string) string { + key := []byte(secret) + hmac := hmac.New(sha1.New, key) + hmac.Write([]byte(source)) + signedBytes := hmac.Sum(nil) + signedString := base64.StdEncoding.EncodeToString(signedBytes) + return signedString +} + +// Sha256WithRsa return a string which has been hashed with Rsa +func Sha256WithRsa(source, secret string) string { + decodeString, err := base64.StdEncoding.DecodeString(secret) + if err != nil { + panic(err) + } + private, err := x509.ParsePKCS8PrivateKey(decodeString) + if err != nil { + panic(err) + } + + h := crypto.Hash.New(crypto.SHA256) + h.Write([]byte(source)) + hashed := h.Sum(nil) + signature, err := hookRSA(rsa.SignPKCS1v15)(rand.Reader, private.(*rsa.PrivateKey), + crypto.SHA256, hashed) + if err != nil { + panic(err) + } + + return base64.StdEncoding.EncodeToString(signature) +} + +// GetMD5Base64 returns a string which has been base64 +func GetMD5Base64(bytes []byte) (base64Value string) { + md5Ctx := md5.New() + md5Ctx.Write(bytes) + md5Value := md5Ctx.Sum(nil) + base64Value = base64.StdEncoding.EncodeToString(md5Value) + return +} + +// GetTimeInFormatISO8601 returns a time string +func GetTimeInFormatISO8601() (timeStr string) { + gmt := time.FixedZone("GMT", 0) + + return time.Now().In(gmt).Format("2006-01-02T15:04:05Z") +} + +// GetURLFormedMap returns a url encoded string +func GetURLFormedMap(source map[string]string) (urlEncoded string) { + urlEncoder := url.Values{} + for key, value := range source { + urlEncoder.Add(key, value) + } + urlEncoded = urlEncoder.Encode() + return +} + +func newUUID() uuid { + ns := uuid{} + safeRandom(ns[:]) + u := newFromHash(md5.New(), ns, RandStringBytes(16)) + u[6] = (u[6] & 0x0f) | (byte(2) << 4) + u[8] = (u[8]&(0xff>>2) | (0x02 << 6)) + + return u +} + +func newFromHash(h hash.Hash, ns uuid, name string) uuid { + u := uuid{} + h.Write(ns[:]) + h.Write([]byte(name)) + copy(u[:], h.Sum(nil)) + + return u +} + +func safeRandom(dest []byte) { + if _, err := hookRead(rand.Read)(dest); err != nil { + panic(err) + } +} + +func (u uuid) String() string { + buf := make([]byte, 36) + + hex.Encode(buf[0:8], u[0:4]) + buf[8] = '-' + hex.Encode(buf[9:13], u[4:6]) + buf[13] = '-' + hex.Encode(buf[14:18], u[6:8]) + buf[18] = '-' + hex.Encode(buf[19:23], u[8:10]) + buf[23] = '-' + hex.Encode(buf[24:], u[10:]) + + return string(buf) +} diff --git a/credentials/internal/utils/utils_test.go b/credentials/internal/utils/utils_test.go new file mode 100644 index 0000000..4a88f30 --- /dev/null +++ b/credentials/internal/utils/utils_test.go @@ -0,0 +1,152 @@ +package utils + +import ( + "crypto" + "crypto/rsa" + "errors" + "io" + "regexp" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGetUUID(t *testing.T) { + uuid := newUUID() + assert.Equal(t, 16, len(uuid)) + assert.Equal(t, 36, len(uuid.String())) + uuidString := GetUUID() + assert.Equal(t, 32, len(uuidString)) +} + +func TestGetMD5Base64(t *testing.T) { + assert.Equal(t, "ERIHLmRX2uZmssDdxQnnxQ==", + GetMD5Base64([]byte("That's all folks!!"))) + assert.Equal(t, "GsJRdI3kAbAnHo/0+3wWJw==", + GetMD5Base64([]byte("中文也没啥问题"))) +} + +func TestGetTimeInFormatISO8601(t *testing.T) { + s := GetTimeInFormatISO8601() + assert.Equal(t, 20, len(s)) + // 2006-01-02T15:04:05Z + re := regexp.MustCompile(`^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$`) + assert.True(t, re.MatchString(s)) +} + +func TestGetURLFormedMap(t *testing.T) { + m := make(map[string]string) + m["key"] = "value" + s := GetURLFormedMap(m) + assert.Equal(t, "key=value", s) + m["key2"] = "http://domain/?key=value&key2=value2" + s2 := GetURLFormedMap(m) + assert.Equal(t, "key=value&key2=http%3A%2F%2Fdomain%2F%3Fkey%3Dvalue%26key2%3Dvalue2", s2) +} + +func TestShaHmac1(t *testing.T) { + result := ShaHmac1("source", "secret") + assert.Equal(t, "Jv4yi8SobFhg5t1C7nWLbhBSFZQ=", result) + + assert.Equal(t, "CqCYIa39h9SSWuXnTz8F5hh9UPA=", ShaHmac1("中文", "secret")) +} + +func TestSha256WithRsa(t *testing.T) { + secret := ` +MIICeQIBADANBgkqhkiG9w0BAQEFAASCAmMwggJfAgEAAoGBAOJC+2WXtkXZ+6sa +3+qJp4mDOsiZb3BghHT9nVbjTeaw4hsZWHYxQ6l6XDmTg4twPB59LOGAlAjYrT31 +3pdwEawnmdf6zyF93Zvxxpy7lO2HoxYKSjbtXO4I0pcq3WTnw2xlbhqHvrcuWwt+ +FqH9akzcnwHjc03siZBzt/dwDL3vAgMBAAECgYEAzwgZPqFuUEYgaTVDFDl2ynYA +kNMMzBgUu3Pgx0Nf4amSitdLQYLcdbQXtTtMT4eYCxHgwkpDqkCRbLOQRKNwFo0I +oaCuhjZlxWcKil4z4Zb/zB7gkeuXPOVUjFSS3FogsRWMtnNAMgR/yJRlbcg/Puqk +Magt/yDk+7cJCe6H96ECQQDxMT4S+tVP9nOw//QT39Dk+kWe/YVEhnWnCMZmGlEq +1gnN6qpUi68ts6b3BVgrDPrPN6wm/Z9vpcKNeWpIvxXRAkEA8CcT2UEUwDGRKAUu +WVPJqdAJjpjc072eRF5g792NyO+TAF6thBlDKNslRvFQDB6ymLsjfy8JYCnGbbSb +WqbHvwJBAIs7KeI6+jiWxGJA3t06LpSABQCqyOut0u0Bm8YFGyXnOPGtrXXwzMdN +Fe0zIJp5e69zK+W2Mvt4bL7OgBROeoECQQDsE+4uLw0gFln0tosmovhmp60NcfX7 +bLbtzL2MbwbXlbOztF7ssgzUWAHgKI6hK3g0LhsqBuo3jzmSVO43giZvAkEA08Nm +2TI9EvX6DfCVfPOiKZM+Pijh0xLN4Dn8qUgt3Tcew/vfj4WA2ZV6qiJqL01vMsHc +vftlY0Hs1vNXcaBgEA==` + result := Sha256WithRsa("source", secret) + assert.Equal(t, "UNyJPD27jjSNl70b02E/DUtgtNESdtAuxbNBZTlksk1t/GYjiQNRlFIubp/EGKcWsqs7p5SFKnNiSRqWG3A51VmJFBXXtyW1nwLC9xY/MbUj6JVWNYCuLkPWM942O+GAk7N+G8ZQZt7ib2MhruDAUmv1lLN26lDaCPBX2MJQJCo=", result) + + assert.Equal(t, "CKE0osxUnFFH+oYP3Q427saucBuignE+Mrni63G9w46yZFtVoXFOu5lNiNCnUtaPNpGmBf9X5oGCY+otqPf7bP93nB59rfdteQs0sS65PWH9yjH8RwYCWGCbuyRul/0qIv/nYYGzkLON1C1Vx9Z4Yep6llYuJang5RIXrAkQLmQ=", Sha256WithRsa("中文", secret)) +} + +func TestSha256WithRsa_DecodeString_Error(t *testing.T) { + defer func() { // 进行异常捕捉 + err := recover() + assert.NotNil(t, err) + assert.Equal(t, "illegal base64 data at input byte 0", err.(error).Error()) + }() + secret := `==` + Sha256WithRsa("source", secret) +} + +func TestSha256WithRsa_ParsePKCS8PrivateKey_Error(t *testing.T) { + defer func() { // 进行异常捕捉 + err := recover() + assert.NotNil(t, err) + assert.Equal(t, "asn1: structure error: length too large", err.(error).Error()) + }() + secret := `Jv4yi8SobFhg5t1C7nWLbhBSFZQ=` + Sha256WithRsa("source", secret) +} + +func TestHookRead(t *testing.T) { + fn := func(p []byte) (n int, err error) { + return 0, errors.New("hookRead") + } + result := hookRead(fn) + n, err := result(nil) + assert.Equal(t, 0, n) + assert.Equal(t, "hookRead", err.Error()) + + originHookRead := hookRead + hookRead = func(old func(p []byte) (n int, err error)) func(p []byte) (n int, err error) { + return fn + } + defer func() { + err := recover() + assert.Equal(t, "hookRead", err.(error).Error()) + hookRead = originHookRead + }() + safeRandom([]byte("credentialtest")) +} + +func TestHookRSA(t *testing.T) { + fn := func(rand io.Reader, priv *rsa.PrivateKey, hash crypto.Hash, hashed []byte) ([]byte, error) { + return nil, errors.New("hookRSA") + } + result := hookRSA(fn) + hash := crypto.Hash(10) + byt, err := result(nil, nil, hash, nil) + assert.Nil(t, byt) + assert.Equal(t, "hookRSA", err.Error()) + + originHookRSA := hookRSA + hookRSA = func(old func(rand io.Reader, priv *rsa.PrivateKey, hash crypto.Hash, hashed []byte) ([]byte, error)) func(rand io.Reader, priv *rsa.PrivateKey, hash crypto.Hash, hashed []byte) ([]byte, error) { + return fn + } + defer func() { + err := recover() + assert.Equal(t, "hookRSA", err.(error).Error()) + hookRSA = originHookRSA + }() + secret := ` +MIICeQIBADANBgkqhkiG9w0BAQEFAASCAmMwggJfAgEAAoGBAOJC+2WXtkXZ+6sa +3+qJp4mDOsiZb3BghHT9nVbjTeaw4hsZWHYxQ6l6XDmTg4twPB59LOGAlAjYrT31 +3pdwEawnmdf6zyF93Zvxxpy7lO2HoxYKSjbtXO4I0pcq3WTnw2xlbhqHvrcuWwt+ +FqH9akzcnwHjc03siZBzt/dwDL3vAgMBAAECgYEAzwgZPqFuUEYgaTVDFDl2ynYA +kNMMzBgUu3Pgx0Nf4amSitdLQYLcdbQXtTtMT4eYCxHgwkpDqkCRbLOQRKNwFo0I +oaCuhjZlxWcKil4z4Zb/zB7gkeuXPOVUjFSS3FogsRWMtnNAMgR/yJRlbcg/Puqk +Magt/yDk+7cJCe6H96ECQQDxMT4S+tVP9nOw//QT39Dk+kWe/YVEhnWnCMZmGlEq +1gnN6qpUi68ts6b3BVgrDPrPN6wm/Z9vpcKNeWpIvxXRAkEA8CcT2UEUwDGRKAUu +WVPJqdAJjpjc072eRF5g792NyO+TAF6thBlDKNslRvFQDB6ymLsjfy8JYCnGbbSb +WqbHvwJBAIs7KeI6+jiWxGJA3t06LpSABQCqyOut0u0Bm8YFGyXnOPGtrXXwzMdN +Fe0zIJp5e69zK+W2Mvt4bL7OgBROeoECQQDsE+4uLw0gFln0tosmovhmp60NcfX7 +bLbtzL2MbwbXlbOztF7ssgzUWAHgKI6hK3g0LhsqBuo3jzmSVO43giZvAkEA08Nm +2TI9EvX6DfCVfPOiKZM+Pijh0xLN4Dn8qUgt3Tcew/vfj4WA2ZV6qiJqL01vMsHc +vftlY0Hs1vNXcaBgEA==` + Sha256WithRsa("source", secret) +}