From f05ae61afff3e5bf0d70d354b1715a6e7fd97040 Mon Sep 17 00:00:00 2001 From: Unlock Music Dev Date: Sat, 19 Nov 2022 07:25:37 +0800 Subject: [PATCH] feat: adapt for qmc key v2 --- algo/qmc/key_dec.go | 56 +++++++++++++++++++++++++++++++++++++++++---- algo/qmc/qmc.go | 3 +++ 2 files changed, 54 insertions(+), 5 deletions(-) diff --git a/algo/qmc/key_dec.go b/algo/qmc/key_dec.go index 9f7ddf8..efc1ee0 100644 --- a/algo/qmc/key_dec.go +++ b/algo/qmc/key_dec.go @@ -1,8 +1,10 @@ package qmc import ( + "bytes" "encoding/base64" "errors" + "fmt" "math" "golang.org/x/crypto/tea" @@ -16,16 +18,30 @@ func simpleMakeKey(salt byte, length int) []byte { } return keyBuf } + +const rawKeyPrefixV2 = "QQMusic EncV2,Key:" + func DecryptKey(rawKey []byte) ([]byte, error) { rawKeyDec := make([]byte, base64.StdEncoding.DecodedLen(len(rawKey))) n, err := base64.StdEncoding.Decode(rawKeyDec, rawKey) if err != nil { return nil, err } - if n < 16 { + rawKeyDec = rawKeyDec[:n] + + if bytes.HasPrefix(rawKeyDec, []byte(rawKeyPrefixV2)) { + rawKeyDec, err = deriveKeyV2(bytes.TrimPrefix(rawKeyDec, []byte(rawKeyPrefixV2))) + if err != nil { + return nil, fmt.Errorf("deriveKeyV2 failed: %w", err) + } + } + return deriveKeyV1(rawKeyDec) +} + +func deriveKeyV1(rawKeyDec []byte) ([]byte, error) { + if len(rawKeyDec) < 16 { return nil, errors.New("key length is too short") } - rawKeyDec = rawKeyDec[:n] simpleKey := simpleMakeKey(106, 8) teaKey := make([]byte, 16) @@ -40,6 +56,37 @@ func DecryptKey(rawKey []byte) ([]byte, error) { } return append(rawKeyDec[:8], rs...), nil } + +var ( + deriveV2Key1 = []byte{ + 0x33, 0x38, 0x36, 0x5A, 0x4A, 0x59, 0x21, 0x40, + 0x23, 0x2A, 0x24, 0x25, 0x5E, 0x26, 0x29, 0x28, + } + + deriveV2Key2 = []byte{ + 0x2A, 0x2A, 0x23, 0x21, 0x28, 0x23, 0x24, 0x25, + 0x26, 0x5E, 0x61, 0x31, 0x63, 0x5A, 0x2C, 0x54, + } +) + +func deriveKeyV2(raw []byte) ([]byte, error) { + buf, err := decryptTencentTea(raw, deriveV2Key1) + if err != nil { + return nil, err + } + + buf, err = decryptTencentTea(buf, deriveV2Key2) + if err != nil { + return nil, err + } + + n, err := base64.StdEncoding.Decode(buf, buf) + if err != nil { + return nil, err + } + return buf[:n], nil +} + func decryptTencentTea(inBuf []byte, key []byte) ([]byte, error) { const saltLen = 2 const zeroLen = 7 @@ -59,9 +106,7 @@ func decryptTencentTea(inBuf []byte, key []byte) ([]byte, error) { blk.Decrypt(destBuf, inBuf) padLen := int(destBuf[0] & 0x7) outLen := len(inBuf) - 1 - padLen - saltLen - zeroLen - if padLen+saltLen != 8 { - return nil, errors.New("invalid pad len") - } + out := make([]byte, outLen) ivPrev := make([]byte, 8) @@ -108,6 +153,7 @@ func decryptTencentTea(inBuf []byte, key []byte) ([]byte, error) { return out, nil } + func xor8Bytes(dst, a, b []byte) { for i := 0; i < 8; i++ { dst[i] = a[i] ^ b[i] diff --git a/algo/qmc/qmc.go b/algo/qmc/qmc.go index 6be2a59..6028e6e 100644 --- a/algo/qmc/qmc.go +++ b/algo/qmc/qmc.go @@ -131,6 +131,9 @@ func (d *Decoder) readRawKey(rawKeyLen int64) error { return err } + // clean suffix NULs + rawKeyData = bytes.TrimRight(rawKeyData, "\x00") + d.decodedKey, err = DecryptKey(rawKeyData) if err != nil { return err