Files
wechat_ipad_pro/clientsdk/android/Algorithm.go
2026-02-17 13:06:23 +08:00

543 lines
15 KiB
Go

package android
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/hmac"
"crypto/md5"
"crypto/rand"
"crypto/sha1"
"crypto/sha256"
"encoding/binary"
"encoding/hex"
"hash"
"io"
"xiawan/wx/clientsdk/android/mmproto"
"xiawan/wx/protobuf/wechat"
"github.com/gogo/protobuf/proto"
"golang.org/x/crypto/hkdf"
)
const (
HYBRID_ENC HYBRID_STATUS = 0
HYBRID_DEC HYBRID_STATUS = 1
)
type Client struct {
PubKey []byte
Privkey []byte
InitPubKey []byte
Externkey []byte
Version int
DeviceType string
clientHash hash.Hash
serverHash hash.Hash
curve elliptic.Curve
Status HYBRID_STATUS
}
func (h *Client) Init(Model string, version int, deviceType string) {
h.curve = elliptic.P256()
h.clientHash = sha256.New()
h.serverHash = sha256.New()
if Model == "IOS" {
h.Privkey, h.PubKey = GetECDH415Key()
h.Version = version
h.DeviceType = deviceType
h.InitPubKey, _ = hex.DecodeString("047ebe7604acf072b0ab0177ea551a7b72588f9b5d3801dfd7bb1bca8e33d1c3b8fa6e4e4026eb38d5bb365088a3d3167c83bdd0bbb46255f88a16ede6f7ab43b5")
}
if Model == "Android" {
h.Status = HYBRID_ENC
h.Version = version
h.DeviceType = deviceType
h.InitPubKey, _ = hex.DecodeString("0495BC6E5C1331AD172D0F35B1792C3CE63F91572ABD2DD6DF6DAC2D70195C3F6627CCA60307305D8495A8C38B4416C75021E823B6C97DFFE79C14CB7C3AF8A586")
}
}
func GetECDH415Key() (privKey []byte, pubKey []byte) {
privKey = nil
pubKey = nil
priv, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
pub := &priv.PublicKey
pubKey = elliptic.Marshal(pub.Curve, pub.X, pub.Y)
privKey = priv.D.Bytes()
return
}
func (h *Client) HybridEcdhPackAndroidEn(cmdid, cert, uin uint32, cookie, Data []byte) []byte {
EnData := h.encryptAndroid(Data)
//log.Infof("计算RQT数据: %s",hex.EncodeToString(EnData))
//rqt := RQT(EnData)
rqtx := CalcMsgCrcForData_7019(EnData)
//log.Infof("原RQT: %v", rqt)
//log.Infof("新RQT: %v", rqtx)
inputlen := len(EnData)
pack := append([]byte{}, cookie...)
pack = proto.EncodeVarint(uint64(cmdid))
pack = append(pack, proto.EncodeVarint(uint64(inputlen))...)
pack = append(pack, proto.EncodeVarint(uint64(inputlen))...)
pack = append(pack, proto.EncodeVarint(uint64(cert))...)
pack = append(pack, 2)
pack = append(pack, 0)
pack = append(pack, 0xfe)
pack = append(pack, proto.EncodeVarint(uint64(rqtx))...)
pack = append(pack, 0)
headLen := len(pack) + 11
headFlag := (12 << 12) | (len(cookie) << 8) | (headLen << 2) | 2
var hybridpack = new(bytes.Buffer)
hybridpack.WriteByte(0xbf)
binary.Write(hybridpack, binary.LittleEndian, uint16(headFlag))
binary.Write(hybridpack, binary.BigEndian, uint32(h.Version))
binary.Write(hybridpack, binary.BigEndian, uint32(uin))
hybridpack.Write(pack)
hybridpack.Write(EnData)
return hybridpack.Bytes()
}
func (h *Client) HybridEcdhPackAndroidUn(Data []byte) *PacketHeader {
var ph PacketHeader
readHeader := bytes.NewReader(Data)
binary.Read(readHeader, binary.LittleEndian, &ph.PacketCryptType)
binary.Read(readHeader, binary.LittleEndian, &ph.Flag)
cookieLen := (ph.Flag >> 8) & 0x0f
headerLen := (ph.Flag & 0xff) >> 2
ph.Cookies = make([]byte, cookieLen)
binary.Read(readHeader, binary.BigEndian, &ph.RetCode)
binary.Read(readHeader, binary.BigEndian, &ph.UICrypt)
binary.Read(readHeader, binary.LittleEndian, &ph.Cookies)
ph.Data = h.decryptAndroid(Data[headerLen:])
return &ph
}
func (h *Client) decryptAndroid(input []byte) []byte {
if h.Status != HYBRID_DEC {
return nil
}
var resp mmproto.HybridEcdhResp
proto.Unmarshal(input, &resp)
h.serverHash.Write(resp.GetGcm1())
// hServ := h.serverHash.Sum(nil)
// fmt.Printf("%x\n", hServ)
// ecdsa.Verify(h.clientEcdsaPub, resp.GetGcm2(), hServ)
ecKey := Ecdh(h.curve, resp.GetSecKey().GetKey(), h.Privkey)
h.clientHash.Write([]byte("415"))
h.clientHash.Write(resp.GetSecKey().GetKey())
h.clientHash.Write([]byte("1"))
hCli := h.clientHash.Sum(nil)
plain, _ := AesGcmDecryptWithUnCompress(ecKey[0:0x18], resp.GetGcm1(), hCli)
return plain
}
func (h *Client) encryptAndroid(input []byte) []byte {
if h.Status != HYBRID_ENC {
return nil
}
priv, x, y, error := elliptic.GenerateKey(h.curve, rand.Reader)
if error != nil {
return nil
}
h.Privkey = priv
h.PubKey = elliptic.Marshal(h.curve, x, y)
ecdhKey := Ecdh(h.curve, h.InitPubKey, h.Privkey)
//hash1
h1 := sha256.New()
h1.Write([]byte("1"))
h1.Write([]byte("415"))
h1.Write(h.PubKey)
h1Sum := h1.Sum(nil)
//Random
random := make([]byte, 32)
io.ReadFull(rand.Reader, random)
nonce1 := make([]byte, 12)
io.ReadFull(rand.Reader, nonce1)
gcm1, _ := AesGcmEncryptWithCompress(ecdhKey[0:0x18], nonce1, random, h1Sum)
//hkdf
salt, _ := hex.DecodeString("73656375726974792068646B6620657870616E64")
hkdfKey := make([]byte, 56)
hkdf.New(sha256.New, random, salt, h1Sum).Read(hkdfKey)
//hash2
h2 := sha256.New()
h2.Write([]byte("1"))
h2.Write([]byte("415"))
h2.Write(h.PubKey)
h2.Write(gcm1)
h2Sum := h2.Sum(nil)
nonce2 := make([]byte, 12)
io.ReadFull(rand.Reader, nonce2)
gcm2, _ := AesGcmEncryptWithCompress(hkdfKey[0:0x18], nonce2, input, h2Sum)
var nid int32 = 415
secKey := &mmproto.SecKey{
Nid: &nid,
Key: h.PubKey,
}
var ver int32 = 1
he := &mmproto.HybridEcdhReq{
Version: &ver,
SecKey: secKey,
Gcm1: gcm1,
Autokey: []byte{},
Gcm2: gcm2,
}
protoMsg, _ := proto.Marshal(he)
// update client
h.clientHash.Write(hkdfKey[0x18:0x38])
h.clientHash.Write(input)
// update server
h.serverHash.Write(gcm2)
h.Status = HYBRID_DEC
return protoMsg
}
func RQT(data []byte) int {
salt1 := [16]byte{0x5c, 0x50, 0x7b, 0x6b, 0x65, 0x4a, 0x13, 0x09, 0x45, 0x58, 0x7e, 0x11, 0x0c, 0x1f, 0x68, 0x79}
salt2 := [16]byte{0x36, 0x3a, 0x11, 0x01, 0x0f, 0x20, 0x79, 0x63, 0x2f, 0x32, 0x14, 0x7b, 0x66, 0x75, 0x02, 0x13}
pad1 := [0x30]byte{
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
}
pad2 := [0x30]byte{
0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
}
md5hash := md5.Sum(data)
hashstr := hex.EncodeToString(md5hash[:])
hash1Data := make([]byte, 16)
copy(hash1Data, salt1[:])
hash1Data = append(hash1Data, pad1[:]...)
hash1 := sha1.New()
hash1.Write(hash1Data)
hash1.Write([]byte(hashstr))
h1 := hash1.Sum(nil)
hash2Data := make([]byte, 16)
copy(hash2Data, salt2[:])
hash2Data = append(hash2Data, pad2[:]...)
hash2 := sha1.New()
hash2.Write(hash2Data)
hash2.Write(h1)
h2 := hash2.Sum(nil)
var b1, b2, b3 byte
size := len(h2)
for i := 0; i < size-2; i++ {
b1 = h2[i+0] - b1*0x7d
b2 = h2[i+1] - b2*0x7d
b3 = h2[i+2] - b3*0x7d
}
return (int(0x21) << 24) | ((int(b3) & 0x7f) << 16) | ((int(b2) & 0x7f) << 8) | (int(b1) & 0x7f)
}
func HybridHkdfExpand(prikey []byte, salt []byte, info []byte, outLen int) []byte {
h := hmac.New(sha256.New, prikey)
h.Write(salt)
T := h.Sum(nil)
return HkdfExpand(sha256.New, T, info, outLen)
}
func Hkdf_Expand(h func() hash.Hash, prk, info []byte, outLen int) []byte {
out := []byte{}
T := []byte{}
i := byte(1)
for len(out) < outLen {
block := append(T, info...)
block = append(block, i)
h := hmac.New(h, prk)
h.Write(block)
T = h.Sum(nil)
out = append(out, T...)
i++
}
return out[:outLen]
}
func HkdfExpand(h func() hash.Hash, prk, info []byte, outLen int) []byte {
out := []byte{}
T := []byte{}
i := byte(1)
for len(out) < outLen {
block := append(T, info...)
block = append(block, i)
h := hmac.New(h, prk)
h.Write(block)
T = h.Sum(nil)
out = append(out, T...)
i++
}
return out[:outLen]
}
// ios组包
func (h *Client) HybridEcdhPackIosEn(Cgi, Uin uint32, Cookies, Data []byte) []byte {
header := new(bytes.Buffer)
header.Write([]byte{0xbf})
header.Write([]byte{0x02}) //加密模式占坑,默认不压缩走12
encryptdata := h.encryptoIOS(Data)
cookielen := len(Cookies)
header.Write([]byte{byte((12 << 4) + cookielen)})
binary.Write(header, binary.BigEndian, int32(h.Version))
if Uin != 0 {
binary.Write(header, binary.BigEndian, int32(Uin))
} else {
header.Write([]byte{0x00, 0x00, 0x00, 0x00})
}
if len(Cookies) == 0xF {
header.Write(Cookies)
}
//log.Infof("计算RQT数据: %s",hex.EncodeToString(encryptdata))
//rqt := RQT(encryptdata)
rqtx := CalcMsgCrcForData_7019(encryptdata)
//log.Infof("原RQT: %v", rqt)
//log.Infof("新RQT: %v", rqtx)
header.Write(proto.EncodeVarint(uint64(Cgi)))
header.Write(proto.EncodeVarint(uint64(len(encryptdata))))
header.Write(proto.EncodeVarint(uint64(len(encryptdata))))
header.Write([]byte{0x90, 0x4E, 0x0D, 0x00, 0xFF})
// header.Write(proto.EncodeVarint(uint64(RqtIOS(encryptdata))))
header.Write(proto.EncodeVarint(uint64(rqtx)))
header.Write([]byte{0x00})
lens := len(header.Bytes())<<2 + 2
header.Bytes()[1] = byte(lens)
header.Write(encryptdata)
return header.Bytes()
}
func (h *Client) HybridEcdhPackIosUn(Data []byte) *PacketHeader {
var ph PacketHeader
var body []byte
var nCur int64
var bfbit byte
srcreader := bytes.NewReader(Data)
binary.Read(srcreader, binary.BigEndian, &bfbit)
if bfbit == byte(0xbf) {
nCur += 1
}
nLenHeader := Data[nCur] >> 2
nCur += 1
nLenCookie := Data[nCur] & 0xf
nCur += 1
nCur += 4
srcreader.Seek(nCur, io.SeekStart)
binary.Read(srcreader, binary.BigEndian, &ph.Uin)
nCur += 4
cookie_temp := Data[nCur : nCur+int64(nLenCookie)]
ph.Cookies = cookie_temp
nCur += int64(nLenCookie)
cgidata := Data[nCur:]
_, nSize := proto.DecodeVarint(cgidata)
nCur += int64(nSize)
LenProtobufData := Data[nCur:]
_, nLenProtobuf := proto.DecodeVarint(LenProtobufData)
nCur += int64(nLenProtobuf)
body = Data[nLenHeader:]
protobufdata := h.decryptoIOS(body)
ph.Data = protobufdata
return &ph
}
func (h *Client) decryptoIOS(Data []byte) []byte {
HybridEcdhResponse := &wechat.HybridEcdhResponse{}
err := proto.Unmarshal(Data, HybridEcdhResponse)
if err != nil {
return nil
}
decrptecdhkey := DoECDH415Key(h.Privkey, HybridEcdhResponse.GetSecECDHKey().GetBuffer())
m := sha256.New()
m.Write(decrptecdhkey)
decrptecdhkey = m.Sum(nil)
h.serverHash.Write([]byte("415"))
h.serverHash.Write(HybridEcdhResponse.GetSecECDHKey().GetBuffer())
h.serverHash.Write([]byte("1"))
mServerpubhashFinal_digest := h.serverHash.Sum(nil)
outdata := AesGcmDecryptWithcompressZlib(decrptecdhkey[:24], HybridEcdhResponse.GetDecryptdata(), mServerpubhashFinal_digest)
return outdata
}
func AesGcmDecryptWithcompressZlib(key []byte, ciphertext []byte, aad []byte) []byte {
ciphertextinput := ciphertext[:len(ciphertext)-0x1c]
endatanonce := ciphertext[len(ciphertext)-0x1c : len(ciphertext)-0x10]
data := new(bytes.Buffer)
data.Write(ciphertextinput)
data.Write(ciphertext[len(ciphertext)-0x10 : len(ciphertext)])
decrypt_data := NewAES_GCMDecrypter(key, data.Bytes(), endatanonce, aad)
if len(decrypt_data) > 0 {
return DoZlibUnCompress(decrypt_data)
} else {
return []byte{}
}
}
func RqtIOS(srcdata []byte) int {
h := md5.New()
h.Write(srcdata)
md5sign := hex.EncodeToString(h.Sum(nil))
key, _ := hex.DecodeString("6a664d5d537c253f736e48273a295e4f")
mac := hmac.New(sha1.New, key)
mac.Write([]byte(md5sign))
my_sign := string(mac.Sum(nil))
randvalue := 1
index := 0
temp0 := 0
temp1 := 0
temp2 := 0
for index = 0; index+2 < 20; index++ {
temp0 = (temp0&0xff)*0x83 + int(my_sign[index])
temp1 = (temp1&0xff)*0x83 + int(my_sign[index+1])
temp2 = (temp2&0xff)*0x83 + int(my_sign[index+2])
}
result := (temp2<<16)&0x7f0000 | temp0&0x7f | (randvalue&0x1f|0x20)<<24 | ((temp1 & 0x7f) << 8)
return result
}
func (h *Client) encryptoIOS(Data []byte) []byte {
ecdhkey := DoECDH415Key(h.Privkey, h.InitPubKey)
m := sha256.New()
m.Write(ecdhkey)
ecdhkey = m.Sum(nil)
mClientpubhash := sha256.New()
mClientpubhash.Write([]byte("1"))
mClientpubhash.Write([]byte("415"))
mClientpubhash.Write(h.PubKey)
mClientpubhash_digest := mClientpubhash.Sum(nil)
mRandomEncryptKey := make([]byte, 32)
io.ReadFull(rand.Reader, mRandomEncryptKey)
mNonce := make([]byte, 12)
io.ReadFull(rand.Reader, mNonce)
mEncryptdata := AesGcmEncryptWithCompressZlib(ecdhkey[:24], mRandomEncryptKey, mNonce, mClientpubhash_digest)
var mExternEncryptdata []byte
if len(h.Externkey) == 0x20 {
mExternEncryptdata = AesGcmEncryptWithCompressZlib(h.Externkey[:24], mRandomEncryptKey, mNonce, mClientpubhash_digest)
}
hkdfexpand_security_key := HybridHkdfExpand([]byte("security hdkf expand"), mRandomEncryptKey, mClientpubhash_digest, 56)
mClientpubhashFinal := sha256.New()
mClientpubhashFinal.Write([]byte("1"))
mClientpubhashFinal.Write([]byte("415"))
mClientpubhashFinal.Write(h.PubKey)
mClientpubhashFinal.Write(mEncryptdata)
mClientpubhashFinal.Write(mExternEncryptdata)
mClientpubhashFinal_digest := mClientpubhashFinal.Sum(nil)
mEncryptdataFinal := AesGcmEncryptWithCompressZlib(hkdfexpand_security_key[:24], Data, mNonce, mClientpubhashFinal_digest)
h.clientHash.Write(mEncryptdataFinal)
h.serverHash.Write(hkdfexpand_security_key[24:56])
h.serverHash.Write(Data)
HybridEcdhRequest := &wechat.HybridEcdhRequest{
Type: proto.Int32(1),
SecECDHKey: &wechat.BufferT{
ILen: proto.Uint32(415),
Buffer: h.PubKey,
},
Randomkeydata: mEncryptdata,
Randomkeyextenddata: mExternEncryptdata,
Encyptdata: mEncryptdataFinal,
}
reqdata, _ := proto.Marshal(HybridEcdhRequest)
return reqdata
}
func DoECDH415Key(privD, pubData []byte) []byte {
X, Y := elliptic.Unmarshal(elliptic.P256(), pubData)
if X == nil || Y == nil {
return nil
}
x, _ := elliptic.P256().ScalarMult(X, Y, privD)
return x.Bytes()
}
func AesGcmEncryptWithCompressZlib(key []byte, plaintext []byte, nonce []byte, aad []byte) []byte {
compressData := DoZlibCompress(plaintext)
//nonce := []byte(randSeq(12)) //获取随机密钥
encrypt_data := NewAES_GCMEncrypter(key, compressData, nonce, aad)
outdata := encrypt_data[:len(encrypt_data)-16]
retdata := new(bytes.Buffer)
retdata.Write(outdata)
retdata.Write(nonce)
retdata.Write(encrypt_data[len(encrypt_data)-16:])
return retdata.Bytes()
}
func NewAES_GCMEncrypter(key []byte, plaintext []byte, nonce []byte, aad []byte) []byte {
block, err := aes.NewCipher(key)
if err != nil {
return nil
}
aesgcm, err := cipher.NewGCM(block)
if err != nil {
return nil
}
ciphertext := aesgcm.Seal(nil, nonce, plaintext, aad)
return ciphertext
}
func NewAES_GCMDecrypter(key []byte, ciphertext []byte, nonce []byte, aad []byte) []byte {
block, err := aes.NewCipher(key)
if err != nil {
return nil
}
aesgcm, err := cipher.NewGCM(block)
if err != nil {
return nil
}
plaintext, err := aesgcm.Open(nil, nonce, ciphertext, aad)
if err != nil {
return nil
}
return plaintext
}