diff --git a/crypto/crypto.go b/crypto/crypto.go index 32fecb59..67fd9a0b 100644 --- a/crypto/crypto.go +++ b/crypto/crypto.go @@ -37,6 +37,12 @@ var HashTypes = map[string]crypto.Hash{ "BLAKE2b_512": crypto.BLAKE2b_512, } +type hmacOption struct { + keyEncoding string // base64 | hex + valueEncoding string // base64 | hex + outputEncoding string // base64 | hex +} + // Hash string func Hash(hash crypto.Hash, value string) (string, error) { h := hash.New() @@ -62,6 +68,67 @@ func Hmac(hash crypto.Hash, value string, key string, encoding ...string) (strin return fmt.Sprintf("%x", mac.Sum(nil)), nil } +// HmacWith the Keyed-Hash Message Authentication Code (HMAC) +func HmacWith(option *hmacOption, hash crypto.Hash, value string, key string) (string, error) { + var k []byte + var v []byte + var err error + if option == nil { + option = &hmacOption{} + } + + switch option.keyEncoding { + case "base64": + k, err = base64.StdEncoding.DecodeString(key) + if err != nil { + return "", err + } + break + + case "hex": + k, err = hex.DecodeString(key) + if err != nil { + return "", err + } + break + + default: + k = []byte(key) + } + + switch option.valueEncoding { + case "base64": + v, err = base64.StdEncoding.DecodeString(value) + if err != nil { + return "", err + } + break + case "hex": + v, err = hex.DecodeString(value) + if err != nil { + return "", err + } + + default: + v = []byte(value) + } + + mac := hmac.New(hash.New, k) + _, err = mac.Write(v) + if err != nil { + return "", err + } + + switch option.outputEncoding { + case "base64": + return base64.StdEncoding.EncodeToString(mac.Sum(nil)), nil + case "hex": + return fmt.Sprintf("%x", mac.Sum(nil)), nil + default: + return fmt.Sprintf("%x", mac.Sum(nil)), nil + } +} + // RSA2Sign RSA2 Sign func RSA2Sign(prikey string, hash crypto.Hash, value string, encoding ...string) (string, error) { diff --git a/crypto/crypto_test.go b/crypto/crypto_test.go index 06927907..e4342ca7 100644 --- a/crypto/crypto_test.go +++ b/crypto/crypto_test.go @@ -1,6 +1,9 @@ package crypto import ( + "crypto" + "encoding/base64" + "encoding/hex" "testing" "github.com/stretchr/testify/assert" @@ -150,3 +153,72 @@ func TestRSA2SignProcessBase64(t *testing.T) { } assert.Equal(t, true, valid) } + +// ProcessHmacWith tests + +func TestHmacWith(t *testing.T) { + keyhex := hex.EncodeToString([]byte("key")) + valuehex := hex.EncodeToString([]byte("value")) + keybase64 := base64.StdEncoding.EncodeToString([]byte("key")) + valuebase64 := base64.StdEncoding.EncodeToString([]byte("value")) + + tests := []struct { + name string + option *hmacOption + hash crypto.Hash + value string + key string + wantErr bool + }{ + { + name: "Test with hex encoding", + option: &hmacOption{ + keyEncoding: "hex", + valueEncoding: "hex", + outputEncoding: "hex", + }, + hash: crypto.SHA256, + value: valuehex, + key: keyhex, + wantErr: false, + }, + { + name: "Test with base64 encoding", + option: &hmacOption{ + keyEncoding: "base64", + valueEncoding: "base64", + outputEncoding: "base64", + }, + hash: crypto.SHA256, + value: valuebase64, + key: keybase64, + wantErr: false, + }, + { + name: "Test with default encoding", + option: &hmacOption{}, + hash: crypto.SHA256, + value: "value", + key: "key", + wantErr: false, + }, + { + name: "Test with nil option", + option: nil, + hash: crypto.SHA256, + value: "value", + key: "key", + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, err := HmacWith(tt.option, tt.hash, tt.value, tt.key) + if (err != nil) != tt.wantErr { + t.Errorf("HmacWith() error = %v, wantErr %v", err, tt.wantErr) + return + } + }) + } +} diff --git a/crypto/process.go b/crypto/process.go index a79b7079..f6e5ab81 100644 --- a/crypto/process.go +++ b/crypto/process.go @@ -12,6 +12,7 @@ func init() { process.Alias("yao.crypto.hash", "crypto.Hash") process.Alias("yao.crypto.hmac", "crypto.Hmac") + process.Register("crypto.hmacwith", ProcessHmacWith) process.Register("crypto.rsa2sign", ProcessRsa2Sign) process.Register("crypto.rsa2verify", ProcessRsa2Verify) process.Register("crypto.aes256encrypt", ProcessAes256Encrypt) @@ -49,7 +50,7 @@ func ProcessHash(process *process.Process) interface{} { // Args[0] string: the hash function name. MD4/MD5/SHA1/SHA224/SHA256/SHA384/SHA512/MD5SHA1/RIPEMD160/SHA3_224/SHA3_256/SHA3_384/SHA3_512/SHA512_224/SHA512_256/BLAKE2s_256/BLAKE2b_256/BLAKE2b_384/BLAKE2b_512 // Args[1] string: value // Args[2] string: key -// Args[3] string: base64 +// Args[3] string: base64 (optional) func ProcessHmac(process *process.Process) interface{} { process.ValidateArgNums(3) typ := process.ArgsString(0) @@ -73,6 +74,40 @@ func ProcessHmac(process *process.Process) interface{} { return res } +// ProcessHmacWith yao.crypto.hmac Crypto the Keyed-Hash Message Authentication Code (HMAC) Hash +// Args[0] map: option {"key": "base64", "value": "base64", "output": "base64"} // hex/base64 +// Args[1] string: value +// Args[2] string: key +func ProcessHmacWith(process *process.Process) interface{} { + process.ValidateArgNums(3) + option := process.ArgsMap(0) + value := process.ArgsString(1) + key := process.ArgsString(2) + typ := "SHA256" + o := &hmacOption{} + if v, has := option["key"].(string); has { + o.keyEncoding = v + } + if v, has := option["value"].(string); has { + o.valueEncoding = v + } + if v, has := option["output"].(string); has { + o.outputEncoding = v + } + if v, has := option["type"].(string); has { + typ = v + } + h, has := HashTypes[typ] + if !has { + exception.New("%s does not support", 400, typ).Throw() + } + res, err := HmacWith(o, h, value, key) + if err != nil { + exception.New("%s error: %s value: %s", 400, typ, err, value).Throw() + } + return res +} + // ProcessRsa2Sign crypto.rsa2sign // Args[0] string: the private key // Args[1] string: the hash function name. MD4/MD5/SHA1/SHA224/SHA256/SHA384/SHA512/MD5SHA1/RIPEMD160/SHA3_224/SHA3_256/SHA3_384/SHA3_512/SHA512_224/SHA512_256/BLAKE2s_256/BLAKE2b_256/BLAKE2b_384/BLAKE2b_512