dvc890's picture
Upload 42 files
581b6d4 verified
package funcaptcha
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/md5"
"crypto/rand"
"encoding/base64"
"encoding/hex"
"encoding/json"
"errors"
"hash"
)
type encryptionData struct {
Ct string `json:"ct"`
Iv string `json:"iv"`
S string `json:"s"`
}
func Encrypt(data string, key string) string {
encData, _ := aesEncrypt(data, key)
encDataJson, err := json.Marshal(encData)
if err != nil {
panic(err)
}
return string(encDataJson)
}
func aesEncrypt(content string, password string) (*encryptionData, error) {
salt := make([]byte, 8)
_, err := rand.Read(salt)
if err != nil {
return nil, err
}
key, iv, err := defaultEvpKDF([]byte(password), salt)
if err != nil {
return nil, err
}
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
mode := cipher.NewCBCEncrypter(block, iv)
cipherBytes := pKCS5Padding([]byte(content), aes.BlockSize)
mode.CryptBlocks(cipherBytes, cipherBytes)
//TODO: remove redundant code
md5Hash := md5.New()
salted := ""
var dx []byte
for i := 0; i < 3; i++ {
md5Hash.Write(dx)
md5Hash.Write([]byte(password))
md5Hash.Write(salt)
dx = md5Hash.Sum(nil)
md5Hash.Reset()
salted += hex.EncodeToString(dx)
}
cipherText := base64.StdEncoding.EncodeToString(cipherBytes)
encData := &encryptionData{
Ct: cipherText,
Iv: salted[64 : 64+32],
S: hex.EncodeToString(salt),
}
return encData, nil
}
// https://stackoverflow.com/questions/27677236/encryption-in-javascript-and-decryption-with-php/27678978#27678978
// https://github.com/brix/crypto-js/blob/8e6d15bf2e26d6ff0af5277df2604ca12b60a718/src/evpkdf.js#L55
func evpKDF(password []byte, salt []byte, keySize int, iterations int, hashAlgorithm string) ([]byte, error) {
var block []byte
var hasher hash.Hash
derivedKeyBytes := make([]byte, 0)
switch hashAlgorithm {
case "md5":
hasher = md5.New()
default:
return []byte{}, errors.New("not implement hasher algorithm")
}
for len(derivedKeyBytes) < keySize*4 {
if len(block) > 0 {
hasher.Write(block)
}
hasher.Write(password)
hasher.Write(salt)
block = hasher.Sum([]byte{})
hasher.Reset()
for i := 1; i < iterations; i++ {
hasher.Write(block)
block = hasher.Sum([]byte{})
hasher.Reset()
}
derivedKeyBytes = append(derivedKeyBytes, block...)
}
return derivedKeyBytes[:keySize*4], nil
}
func defaultEvpKDF(password []byte, salt []byte) (key []byte, iv []byte, err error) {
// https://github.com/brix/crypto-js/blob/8e6d15bf2e26d6ff0af5277df2604ca12b60a718/src/cipher-core.js#L775
keySize := 256 / 32
ivSize := 128 / 32
derivedKeyBytes, err := evpKDF(password, salt, keySize+ivSize, 1, "md5")
if err != nil {
return []byte{}, []byte{}, err
}
return derivedKeyBytes[:keySize*4], derivedKeyBytes[keySize*4:], nil
}
func pKCS5Padding(src []byte, blockSize int) []byte {
padding := blockSize - len(src)%blockSize
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
return append(src, padtext...)
}
func Decrypt(data string, password string, fallbackPass string) string {
decDataText, err := AesDecrypt(data, password, fallbackPass)
if err != nil {
println(err.Error())
}
return decDataText
}
func AesDecrypt(baseText string, password string, fallbackPass string) (string, error) {
encBytes, err := base64.StdEncoding.DecodeString(baseText)
if err != nil {
return "", err
}
var encData encryptionData
err = json.Unmarshal(encBytes, &encData)
if err != nil {
return "", err
}
cipherBytes, err := base64.StdEncoding.DecodeString(encData.Ct)
if err != nil {
return "", err
}
salt, err := hex.DecodeString(encData.S)
if err != nil {
return "", err
}
dstBytes := make([]byte, len(cipherBytes))
decrypt:
key, _, err := DefaultEvpKDF([]byte(password), salt)
iv, _ := hex.DecodeString(encData.Iv)
if err != nil {
return "", err
}
block, err := aes.NewCipher(key)
if err != nil {
return "", err
}
mode := cipher.NewCBCDecrypter(block, iv)
mode.CryptBlocks(dstBytes, cipherBytes)
result := PKCS5UnPadding(dstBytes)
if !json.Valid(result) {
if password == fallbackPass {
return "", errors.New("decryption can't get the correct result")
} else {
password = fallbackPass
goto decrypt
}
}
return string(result), nil
}
// https://stackoverflow.com/questions/27677236/encryption-in-javascript-and-decryption-with-php/27678978#27678978
// https://github.com/brix/crypto-js/blob/8e6d15bf2e26d6ff0af5277df2604ca12b60a718/src/evpkdf.js#L55
func EvpKDF(password []byte, salt []byte, keySize int, iterations int, hashAlgorithm string) ([]byte, error) {
var block []byte
var hasher hash.Hash
derivedKeyBytes := make([]byte, 0)
switch hashAlgorithm {
case "md5":
hasher = md5.New()
default:
return []byte{}, errors.New("not implement hasher algorithm")
}
for len(derivedKeyBytes) < keySize*4 {
if len(block) > 0 {
hasher.Write(block)
}
hasher.Write(password)
hasher.Write(salt)
block = hasher.Sum([]byte{})
hasher.Reset()
for i := 1; i < iterations; i++ {
hasher.Write(block)
block = hasher.Sum([]byte{})
hasher.Reset()
}
derivedKeyBytes = append(derivedKeyBytes, block...)
}
return derivedKeyBytes[:keySize*4], nil
}
func DefaultEvpKDF(password []byte, salt []byte) (key []byte, iv []byte, err error) {
// https://github.com/brix/crypto-js/blob/8e6d15bf2e26d6ff0af5277df2604ca12b60a718/src/cipher-core.js#L775
keySize := 256 / 32
ivSize := 128 / 32
derivedKeyBytes, err := EvpKDF(password, salt, keySize+ivSize, 1, "md5")
if err != nil {
return []byte{}, []byte{}, err
}
return derivedKeyBytes[:keySize*4], derivedKeyBytes[keySize*4:], nil
}
// https://stackoverflow.com/questions/41579325/golang-how-do-i-decrypt-with-des-cbc-and-pkcs7
func PKCS5UnPadding(src []byte) []byte {
length := len(src)
unpadding := int(src[length-1])
return src[:(length - unpadding)]
}
func PKCS5Padding(src []byte, blockSize int) []byte {
padding := blockSize - len(src)%blockSize
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
return append(src, padtext...)
}