From 5df108772b4c52b8d788d66952625edbfd251d6b Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 24 Jan 2024 18:43:11 +0800 Subject: [PATCH] Update crypto.go and crypto_test.go Replace MD4 with MD5 in HashTypes map and update test cases for MD4 and HMac functions. Added new functions for AES256 encryption and decryption. --- crypto/aes.go | 105 ++++++++++++++++++++++++++++++++++++++++++ crypto/aes_test.go | 88 +++++++++++++++++++++++++++++++++++ crypto/crypto.go | 8 +--- crypto/crypto_test.go | 4 +- crypto/process.go | 48 +++++++++++++++++++ 5 files changed, 244 insertions(+), 9 deletions(-) create mode 100644 crypto/aes.go create mode 100644 crypto/aes_test.go diff --git a/crypto/aes.go b/crypto/aes.go new file mode 100644 index 00000000..c36ccd2f --- /dev/null +++ b/crypto/aes.go @@ -0,0 +1,105 @@ +package crypto + +import ( + "crypto/aes" + "crypto/cipher" + "encoding/base64" + "encoding/hex" + "fmt" +) + +// AES256Encrypt AES Encrypt +func AES256Encrypt(key string, algorithm string, nonce string, text string, additionalData string, encoding ...string) (string, error) { + switch algorithm { + case "GCM": + var add []byte + if additionalData != "" { + add = []byte(additionalData) + } + ciphertext, err := aes256GCMEncrypt([]byte(key), []byte(nonce), []byte(text), add) + if err != nil { + return "", err + } + if len(encoding) > 0 && encoding[0] == "base64" { + return base64.StdEncoding.EncodeToString(ciphertext), nil + } + return hex.EncodeToString(ciphertext), nil + } + return "", fmt.Errorf("algorithm %s not support", algorithm) +} + +// AES256Decrypt AES Decrypt +func AES256Decrypt(key string, algorithm string, nonce string, ciphertext string, additionalData string, encoding ...string) (string, error) { + switch algorithm { + case "GCM": + var bytes []byte + var err error + if len(encoding) > 0 && encoding[0] == "base64" { + bytes, err = base64.StdEncoding.DecodeString(ciphertext) + if err != nil { + return "", err + } + } else { + bytes, err = hex.DecodeString(ciphertext) + if err != nil { + return "", err + } + } + + var add []byte + if additionalData != "" { + add = []byte(additionalData) + } + text, err := aes256GCMDecrypt([]byte(key), []byte(nonce), bytes, add) + if err != nil { + return "", err + } + + return string(text), nil + } + return "", fmt.Errorf("algorithm %s not support", algorithm) +} + +func aes256GCMDecrypt(key, nonce, ciphertext, additionalData []byte) ([]byte, error) { + if len(key) != 32 { + return nil, fmt.Errorf("key length must be 32") + } + + c, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + + gcm, err := cipher.NewGCM(c) + if err != nil { + return nil, err + } + + decrypted, err := gcm.Open(nil, nonce, ciphertext, []byte(additionalData)) + if err != nil { + return nil, fmt.Errorf("gcm open error: %s", err) + } + + return decrypted, nil +} + +func aes256GCMEncrypt(key, nonce, text, additionalData []byte) ([]byte, error) { + + if len(key) != 32 { + return nil, fmt.Errorf("key length must be 32") + } + + c, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + + // Create a GCM block mode instance + gcm, err := cipher.NewGCM(c) + if err != nil { + return nil, fmt.Errorf("gcm error: %s", err) + } + + ciphertext := gcm.Seal(nil, nonce, text, additionalData) + return ciphertext, nil +} diff --git a/crypto/aes_test.go b/crypto/aes_test.go new file mode 100644 index 00000000..f8412fdb --- /dev/null +++ b/crypto/aes_test.go @@ -0,0 +1,88 @@ +package crypto + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/yaoapp/gou/process" +) + +func TestAES256GCM(t *testing.T) { + key := `oxxxyXVBqwqUjmbgKlwuHV2mgxxxcfOa` + nonce := `LJEcFT6QWjkG` + text := `{"name":"yao"}` + additionalData := `transaction` + + crypted, err := AES256Encrypt(key, "GCM", nonce, text, additionalData) + if err != nil { + t.Errorf("AES256Encrypt error: %s", err) + } + + decrypted, err := AES256Decrypt(key, "GCM", nonce, crypted, additionalData) + if err != nil { + t.Errorf("AES256Decrypt error: %s", err) + } + + assert.Equal(t, text, decrypted) +} + +func TestAES256GCMBase64(t *testing.T) { + key := `oxxxyXVBqwqUjmbgKlwuHV2mgxxxcfOa` + nonce := `LJEcFT6QWjkG` + text := `{"name":"yao"}` + additionalData := `transaction` + + crypted, err := AES256Encrypt(key, "GCM", nonce, text, additionalData, "base64") + if err != nil { + t.Errorf("AES256Encrypt error: %s", err) + } + + decrypted, err := AES256Decrypt(key, "GCM", nonce, crypted, additionalData, "base64") + if err != nil { + t.Errorf("AES256Decrypt error: %s", err) + } + assert.Equal(t, text, decrypted) + +} + +func TestAES256ProcessGCM(t *testing.T) { + key := `oxxxyXVBqwqUjmbgKlwuHV2mgxxxcfOa` + nonce := `LJEcFT6QWjkG` + text := `{"name":"yao"}` + additionalData := `transaction` + + args := []interface{}{"GCM", key, nonce, text, additionalData} + crypted, err := process.New("crypto.Aes256Encrypt", args...).Exec() + if err != nil { + t.Fatal(err) + } + + args = []interface{}{"GCM", key, nonce, crypted, additionalData} + decrypted, err := process.New("crypto.Aes256Decrypt", args...).Exec() + if err != nil { + t.Fatal(err) + } + + assert.Equal(t, text, decrypted) +} + +func TestAES256ProcessGCMBase64(t *testing.T) { + key := `oxxxyXVBqwqUjmbgKlwuHV2mgxxxcfOa` + nonce := `LJEcFT6QWjkG` + text := `{"name":"yao"}` + additionalData := `transaction` + + args := []interface{}{"GCM", key, nonce, text, additionalData, "base64"} + crypted, err := process.New("crypto.Aes256Encrypt", args...).Exec() + if err != nil { + t.Fatal(err) + } + + args = []interface{}{"GCM", key, nonce, crypted, additionalData, "base64"} + decrypted, err := process.New("crypto.Aes256Decrypt", args...).Exec() + if err != nil { + t.Fatal(err) + } + + assert.Equal(t, text, decrypted) +} diff --git a/crypto/crypto.go b/crypto/crypto.go index 775e9866..48c6599a 100644 --- a/crypto/crypto.go +++ b/crypto/crypto.go @@ -12,17 +12,11 @@ import ( "errors" "fmt" "strings" - - "golang.org/x/crypto/md4" ) -func init() { - crypto.RegisterHash(crypto.MD4, md4.New) -} - // HashTypes string var HashTypes = map[string]crypto.Hash{ - "MD4": crypto.MD4, + "MD4": crypto.MD5, // MD4 is not supported | replaced with MD5 "MD5": crypto.MD5, "SHA1": crypto.SHA1, "SHA224": crypto.SHA224, diff --git a/crypto/crypto_test.go b/crypto/crypto_test.go index c8fce969..7718ad4c 100644 --- a/crypto/crypto_test.go +++ b/crypto/crypto_test.go @@ -11,12 +11,12 @@ func TestMD4(t *testing.T) { // Hash args := []interface{}{"MD4", "123456"} res := process.New("crypto.Hash", args...).Run() - assert.Equal(t, "585028aa0f794af812ee3be8804eb14a", res) + assert.Equal(t, "e10adc3949ba59abbe56e057f20f883e", res) // HMac args = append(args, "123456") res = process.New("crypto.Hmac", args...).Run() - assert.Equal(t, "356f45727db95d65843b2794474d741c", res) + assert.Equal(t, "30ce71a73bdd908c3955a90e8f7429ef", res) } func TestMD5(t *testing.T) { diff --git a/crypto/process.go b/crypto/process.go index ec95027b..a79b7079 100644 --- a/crypto/process.go +++ b/crypto/process.go @@ -14,6 +14,8 @@ func init() { process.Register("crypto.rsa2sign", ProcessRsa2Sign) process.Register("crypto.rsa2verify", ProcessRsa2Verify) + process.Register("crypto.aes256encrypt", ProcessAes256Encrypt) + process.Register("crypto.aes256decrypt", ProcessAes256Decrypt) } // ProcessRSA2 yao.crypto.rsa Crypto RSA @@ -100,6 +102,7 @@ func ProcessRsa2Sign(process *process.Process) interface{} { // 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 // Args[2] string: value // Args[3] string: sign +// Args[4] string: "base64" (optional) func ProcessRsa2Verify(process *process.Process) interface{} { process.ValidateArgNums(4) pub := process.ArgsString(0) @@ -119,3 +122,48 @@ func ProcessRsa2Verify(process *process.Process) interface{} { } return res } + +// ProcessAes256Encrypt crypto.aes256encrypt +// Args[0] string: the algorithm "GCM" +// Args[1] string: the key +// Args[2] string: the nonce +// Args[3] string: the text +// Args[4] string: the additionalData +// Args[5] string: "base64" (optional) +func ProcessAes256Encrypt(process *process.Process) interface{} { + process.ValidateArgNums(4) + algorithm := process.ArgsString(0) + key := process.ArgsString(1) + nonce := process.ArgsString(2) + text := process.ArgsString(3) + additionalData := process.ArgsString(4) + encoding := process.ArgsString(5) + + res, err := AES256Encrypt(key, algorithm, nonce, text, additionalData, encoding) + if err != nil { + exception.Err(err, 500).Throw() + } + return res +} + +// ProcessAes256Decrypt crypto.aes256decrypt +// Args[0] string: the algorithm "GCM" +// Args[1] string: the key +// Args[2] string: the nonce +// Args[3] string: the crypted +// Args[4] string: the additionalData +// Args[5] string: "base64" (optional) +func ProcessAes256Decrypt(process *process.Process) interface{} { + process.ValidateArgNums(4) + algorithm := process.ArgsString(0) + key := process.ArgsString(1) + nonce := process.ArgsString(2) + crypted := process.ArgsString(3) + additionalData := process.ArgsString(4) + encoding := process.ArgsString(5) + res, err := AES256Decrypt(key, algorithm, nonce, crypted, additionalData, encoding) + if err != nil { + exception.Err(err, 500).Throw() + } + return res +}