Files
djk/internal/app/provider_hongguo_comment_sign.go

206 lines
7.4 KiB
Go

package app
import (
"crypto/aes"
"crypto/cipher"
"crypto/md5"
"crypto/rand"
"encoding/base64"
"encoding/binary"
"encoding/hex"
"math/bits"
"net/http"
"strconv"
"time"
)
type hongguoCommentKey struct{}
type hongguoCommentNonce [11]byte
func newHongguoCommentNonce() (hongguoCommentNonce, error) {
var nonce hongguoCommentNonce
_, err := rand.Read(nonce[:])
return nonce, err
}
func signHongguoCommentRequest(request *http.Request, nonce hongguoCommentNonce, now time.Time) {
stamp := uint32(now.Unix())
query := request.URL.RawQuery
head2, head3 := (nonce[0]%7+1)<<5, 0xe0|nonce[1]&15
seed := make([]byte, 20)
hash := md5.Sum([]byte(query))
copy(seed, hash[:4])
copy(seed[12:16], []byte{0, 1, 7, 4})
binary.BigEndian.PutUint32(seed[16:], stamp)
key := [8]byte{0x4a, 0, 0x16, head3, 0x47, 0x6c, 0, head2}
var box [256]byte
for i := range box {
box[i] = byte(i)
}
j := byte(0)
for i := range box {
j += box[i] + key[i&7]
box[i] = box[j]
}
j = 0
for offset := range seed {
i := byte(offset + 1)
j += box[i]
box[i] = box[j]
seed[offset] ^= box[box[i]+box[j]]
}
for i := range seed {
seed[i] = bits.Reverse8(bits.RotateLeft8(seed[i], 4)^seed[(i+1)%len(seed)]) ^ 0xeb
}
request.Header.Set("X-Gorgon", hex.EncodeToString(append([]byte{0x84, 4, head2, head3, 0, 0}, seed...)))
request.Header.Set("X-Khronos", strconv.FormatUint(uint64(stamp), 10))
request.Header.Set("X-SS-Req-Ticket", strconv.FormatInt(now.UnixMilli(), 10))
request.Header.Set("X-SS-STUB", "")
request.Header.Set("X-Argus", hongguoCommentArgus(query, stamp, nonce))
request.Header.Set("X-Ladon", hongguoCommentLadon(request.URL.Query().Get("aid"), stamp, nonce[2:6]))
}
func hongguoCommentPad(data []byte) []byte {
padding := 16 - len(data)%16
out := append([]byte(nil), data...)
for i := 0; i < padding; i++ {
out = append(out, byte(padding))
}
return out
}
func hongguoCommentLadon(aid string, timestamp uint32, prefix []byte) string {
hash := md5.Sum(append(append([]byte(nil), prefix...), aid...))
digest := []byte(hex.EncodeToString(hash[:]))
left, right := binary.LittleEndian.Uint64(digest), binary.LittleEndian.Uint64(digest[8:])
queue := []uint64{binary.LittleEndian.Uint64(digest[16:]), binary.LittleEndian.Uint64(digest[24:])}
var keys [34]uint64
for i := range keys {
keys[i] = left
mixed := (bits.RotateLeft64(right, -8) + left) ^ uint64(i)
queue = append(queue, mixed)
left = mixed ^ bits.RotateLeft64(left, 3)
right, queue = queue[0], queue[1:]
}
data := hongguoCommentPad([]byte(strconv.FormatUint(uint64(timestamp), 10) + "-1611921764-3019"))
for offset := 0; offset < len(data); offset += 16 {
left, right := binary.LittleEndian.Uint64(data[offset:]), binary.LittleEndian.Uint64(data[offset+8:])
for _, key := range keys {
right = key ^ (left + bits.RotateLeft64(right, -8))
left = right ^ bits.RotateLeft64(left, 3)
}
binary.LittleEndian.PutUint64(data[offset:], left)
binary.LittleEndian.PutUint64(data[offset+8:], right)
}
return base64.StdEncoding.EncodeToString(append(append([]byte(nil), prefix...), data...))
}
func hongguoCommentArgus(query string, timestamp uint32, nonce hongguoCommentNonce) string {
key, _ := hex.DecodeString("ac1adaae95a7af94a5114ab3b3a97dd80050aa0a39314c40528caec95256c28c")
var message []byte
varint := func(field, value uint64) {
message = binary.AppendUvarint(message, field*8)
message = binary.AppendUvarint(message, value)
}
bytesField := func(field uint64, value []byte) {
message = binary.AppendUvarint(message, field*8+2)
message = binary.AppendUvarint(message, uint64(len(value)))
message = append(message, value...)
}
varint(1, 0x20200929*2)
varint(2, 2)
varint(3, uint64(binary.LittleEndian.Uint32(nonce[6:10])))
bytesField(4, []byte("3019"))
bytesField(6, []byte("1611921764"))
bytesField(7, []byte("6.8.1.32"))
bytesField(8, []byte("v04.07.01-ml-android"))
varint(9, 135135744)
bytesField(10, make([]byte, 8))
varint(12, uint64(timestamp)*2)
bodyHash, queryHash := hongguoSM3(make([]byte, 16)), hongguoSM3([]byte(query))
bytesField(13, bodyHash[:6])
bytesField(14, queryHash[:6])
nested := []byte{8, 2 + (nonce[10]%49)*2}
for _, field := range []uint64{2, 3, 4} {
nested = binary.AppendUvarint(nested, field*8)
nested = binary.AppendUvarint(nested, 1388734)
}
bytesField(15, nested)
bytesField(20, []byte("none"))
varint(21, 738)
data := hongguoCommentPad(message)
seed := append(append(append([]byte(nil), key...), 0x3c, 0xcc, 0x56, 0x7b), key...)
derived := hongguoSM3(seed)
var rounds [72]uint64
for i := 0; i < 4; i++ {
rounds[i] = binary.LittleEndian.Uint64(derived[i*8:])
}
for i := 4; i < len(rounds); i++ {
mixed := bits.RotateLeft64(rounds[i-1], -3) ^ rounds[i-3]
mixed ^= bits.RotateLeft64(mixed, -1)
rounds[i] = ^rounds[i-4] ^ mixed ^ (uint64(0x3dc94c3a046d678b)>>uint((i-4)%62))&1 ^ 3
}
for offset := 0; offset < len(data); offset += 16 {
left, right := binary.LittleEndian.Uint64(data[offset:]), binary.LittleEndian.Uint64(data[offset+8:])
for _, round := range rounds {
left, right = right, left^(bits.RotateLeft64(right, 1)&bits.RotateLeft64(right, 8))^bits.RotateLeft64(right, 2)^round
}
binary.LittleEndian.PutUint64(data[offset:], left)
binary.LittleEndian.PutUint64(data[offset+8:], right)
}
xor := []byte{0xd0, 0x4f, 0xfd, 0xff, 0xd0, 0x4f, 0xfd, 0xff}
for i := range data {
data[i] ^= xor[i%8]
}
data = append(xor, data...)
for left, right := 0, len(data)-1; left < right; left, right = left+1, right-1 {
data[left], data[right] = data[right], data[left]
}
container := append([]byte{0xa6, 0xe7, 0x83, 0xee, 0x70, 0x01, 0x10, 0x09, 0x18}, data...)
container = hongguoCommentPad(append(container, 0x56, 0x7b))
aesKey, iv := md5.Sum(key[:16]), md5.Sum(key[16:])
block, _ := aes.NewCipher(aesKey[:])
cipher.NewCBCEncrypter(block, iv[:]).CryptBlocks(container, container)
return base64.StdEncoding.EncodeToString(append([]byte{0x3c, 0xcc}, container...))
}
func hongguoSM3(input []byte) [32]byte {
length := uint64(len(input)) * 8
data := append(append([]byte(nil), input...), 0x80)
for len(data)%64 != 56 {
data = append(data, 0)
}
data = binary.BigEndian.AppendUint64(data, length)
state := [8]uint32{0x7380166f, 0x4914b2b9, 0x172442d7, 0xda8a0600, 0xa96f30bc, 0x163138aa, 0xe38dee4d, 0xb0fb0e4e}
for offset := 0; offset < len(data); offset += 64 {
var words [68]uint32
for i := 0; i < 16; i++ {
words[i] = binary.BigEndian.Uint32(data[offset+i*4:])
}
for i := 16; i < len(words); i++ {
v := words[i-16] ^ words[i-9] ^ bits.RotateLeft32(words[i-3], 15)
words[i] = v ^ bits.RotateLeft32(v, 15) ^ bits.RotateLeft32(v, 23) ^ bits.RotateLeft32(words[i-13], 7) ^ words[i-6]
}
a, b, c, d, e, f, g, h := state[0], state[1], state[2], state[3], state[4], state[5], state[6], state[7]
for i := 0; i < 64; i++ {
t, ff, gg := uint32(0x79cc4519), a^b^c, e^f^g
if i >= 16 {
t, ff, gg = 0x7a879d8a, (a&b)|(a&c)|(b&c), (e&f)|(^e&g)
}
ss1 := bits.RotateLeft32(bits.RotateLeft32(a, 12)+e+bits.RotateLeft32(t, i), 7)
tt1 := ff + d + (ss1 ^ bits.RotateLeft32(a, 12)) + (words[i] ^ words[i+4])
tt2 := gg + h + ss1 + words[i]
a, b, c, d = tt1, a, bits.RotateLeft32(b, 9), c
e, f, g, h = tt2^bits.RotateLeft32(tt2, 9)^bits.RotateLeft32(tt2, 17), e, bits.RotateLeft32(f, 19), g
}
for i, value := range [8]uint32{a, b, c, d, e, f, g, h} {
state[i] ^= value
}
}
var result [32]byte
for i, value := range state {
binary.BigEndian.PutUint32(result[i*4:], value)
}
return result
}