Skip to content
This repository has been archived by the owner on Oct 15, 2023. It is now read-only.

Commit

Permalink
fix: use right crypt algorithm, make sure bda is right
Browse files Browse the repository at this point in the history
  • Loading branch information
flyingpot committed Jun 26, 2023
1 parent a0240de commit eca1fb8
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 47 deletions.
13 changes: 10 additions & 3 deletions api.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"net/http"
"net/url"
"strconv"
"strings"
)

type GetTokenOptions struct {
Expand Down Expand Up @@ -43,7 +44,7 @@ func GetToken(options *GetTokenOptions) (GetTokenResult, error) {
options.Headers = make(map[string]string)
}
if _, ok := options.Headers["User-Agent"]; !ok {
options.Headers["User-Agent"] = "DEFAULT_USER_AGENT"
options.Headers["User-Agent"] = DEFAULT_USER_AGENT
}

options.Headers["Accept-Language"] = "en-US,en;q=0.9"
Expand All @@ -58,7 +59,6 @@ func GetToken(options *GetTokenOptions) (GetTokenResult, error) {
}

ua := options.Headers["User-Agent"]

formData := url.Values{
"bda": {GetBda(ua, options.Headers["Referer"], options.Location)},
"public_key": {options.PKey},
Expand All @@ -67,11 +67,18 @@ func GetToken(options *GetTokenOptions) (GetTokenResult, error) {
"rnd": {strconv.FormatFloat(rand.Float64(), 'f', -1, 64)},
}

if options.Site == "" {
formData.Del("site")
}

for key, value := range options.Data {
formData.Add("data["+key+"]", value)
}

req, err := http.NewRequest("POST", options.SURL+"/fc/gt2/public_key/"+options.PKey, bytes.NewBufferString(formData.Encode()))
form := strings.ReplaceAll(formData.Encode(), "+", "%20")
form = strings.ReplaceAll(form, "%28", "(")
form = strings.ReplaceAll(form, "%29", ")")
req, err := http.NewRequest("POST", options.SURL+"/fc/gt2/public_key/"+options.PKey, bytes.NewBufferString(form))
if err != nil {
return GetTokenResult{}, err
}
Expand Down
8 changes: 7 additions & 1 deletion api_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package funcaptcha

import (
"strings"
"testing"
)

Expand All @@ -10,5 +11,10 @@ func TestGetToken(t *testing.T) {
SURL: "https://tcr9i.chat.openai.com",
}
res, _ := GetToken(options)
println(res.Token)
if !strings.Contains(res.Token, "sup=") {
t.Errorf("Token does not contain 'sup='")
}
if !strings.Contains(res.Token, "rid=") {
t.Errorf("Token does not contain 'rid='")
}
}
151 changes: 111 additions & 40 deletions crypt.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"encoding/base64"
"encoding/hex"
"encoding/json"
"errors"
"hash"
)

type EncryptionData struct {
Expand All @@ -17,69 +19,138 @@ type EncryptionData struct {
S string `json:"s"`
}

var alphabet = "abcdefghijklmnopqrstuvwxyz"

func encrypt(data string, key string) string {
salt := generateSalt(8)
paddedData := pkcs7Pad([]byte(data))
encData, _ := AesEncrypt(data, key)

encDataJson, err := json.Marshal(encData)
if err != nil {
panic(err)
}

return string(encDataJson)
}

func AesDecrypt(cipherText string, password string) (string, error) {
data, err := base64.StdEncoding.DecodeString(cipherText)
if err != nil {
return "", err
}
if string(data[:8]) != "Salted__" {
return "", errors.New("invalid crypto js aes encryption")
}

salt := data[8:16]
cipherBytes := data[16:]
key, iv, err := DefaultEvpKDF([]byte(password), salt)
if err != nil {
return "", err
}

block, err := aes.NewCipher(key)
if err != nil {
return "", err
}

mode := cipher.NewCBCDecrypter(block, iv)
mode.CryptBlocks(cipherBytes, cipherBytes)

result := PKCS5UnPadding(cipherBytes)
return string(result), nil
}

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)

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(key))
md5Hash.Write([]byte(salt))
md5Hash.Write([]byte(password))
md5Hash.Write(salt)

dx = md5Hash.Sum(nil)
md5Hash.Reset()

salted += hex.EncodeToString(dx)
}

cipherBlock, err := aes.NewCipher([]byte(salted[:32]))
if err != nil {
panic(err)
}

iv := []byte(salted[32 : 32+16])

mode := cipher.NewCBCEncrypter(cipherBlock, iv)
mode.CryptBlocks(paddedData, paddedData)

encData := EncryptionData{
Ct: base64.StdEncoding.EncodeToString(paddedData),
cipherText := base64.StdEncoding.EncodeToString(cipherBytes)
encData := &EncryptionData{
Ct: cipherText,
Iv: salted[64 : 64+32],
S: hex.EncodeToString([]byte(salt)),
S: hex.EncodeToString(salt),
}
return encData, nil
}

encDataJson, err := json.Marshal(encData)
if err != nil {
panic(err)
// 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")
}

return string(encDataJson)
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 hexDecode(s string) []byte {
data, _ := hex.DecodeString(s)
return data
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 generateSalt(length int) string {
bs := make([]byte, length)
rand.Read(bs)

for i, b := range bs {
bs[i] = alphabet[b%byte(len(alphabet))]
}
return string(bs)
// 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 pkcs7Pad(data []byte) []byte {
blockSize := aes.BlockSize
padding := blockSize - len(data)%blockSize
padText := bytes.Repeat([]byte{byte(padding)}, padding)
return append(data, padText...)
func PKCS5Padding(src []byte, blockSize int) []byte {
padding := blockSize - len(src)%blockSize
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
return append(src, padtext...)
}
2 changes: 1 addition & 1 deletion crypt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package funcaptcha

import "testing"

func TestEncryptDecrypt(t *testing.T) {
func TestEncryptDecrypt1(t *testing.T) {
enc := encrypt("test-data", "test-key")
println(enc)
}
3 changes: 1 addition & 2 deletions util.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,11 @@ func Random() string {
func GetBda(userAgent string, referer string, location string) string {
fp := getFingerprint()
fe := prepareFe(fp)
now := time.Now().Unix()
bda := []Bda{
{Key: "api_type", Value: "js"},
{Key: "p", Value: 1},
{Key: "f", Value: GetMurmur128String(prepareF(fp), 31)},
{Key: "n", Value: base64.StdEncoding.EncodeToString([]byte(strconv.Itoa(int(math.Round(float64(now) / 1000)))))},
{Key: "n", Value: base64.StdEncoding.EncodeToString([]byte(strconv.Itoa(int(math.Round(float64(time.Now().Unix()))))))},
{Key: "wh", Value: Random() + "|" + Random()},
{Key: "enhanced_fp", Value: []Bda{
{
Expand Down

0 comments on commit eca1fb8

Please sign in to comment.