- 只翻注释/文档字符串:剥掉行尾注释后,每条「-」都有逐字节相同的「+」对应行(8 组,代码零改动) - 保留协议/线上标识符原文:errcode、bot_token、context_token、getupdates、binded_redirect、PKCS#7、ECB 等 - 术语对齐既有译法:owner→主人、peer→对端、long poll→长轮询、backoff→退避、sentinel error→哨兵错误 - 已知既有欠债(未顺手修):login_reuse_test.go 在 HEAD 上就未 gofmt 干净;login_test.go 的 TestStartLogin_ScanStatusTransition 91 行 >80(HEAD 同为 91 行)
75 lines
2.5 KiB
Go
75 lines
2.5 KiB
Go
package weixin_ilink
|
||
|
||
import (
|
||
"crypto/aes"
|
||
"errors"
|
||
"fmt"
|
||
)
|
||
|
||
// ErrBadCiphertext 在密文长度不是 AES 分组整数倍、或其填充格式非法时返回。
|
||
var ErrBadCiphertext = errors.New("weixin_ilink: bad ciphertext or padding")
|
||
|
||
// pkcs7Pad 按 PKCS#7 向 src 追加填充,使其长度成为 blockSize 的整数倍。
|
||
// 每个追加字节都写入填充长度,因此它是自描述的,Unpad 可以无歧义地剥离。
|
||
func pkcs7Pad(src []byte, blockSize int) []byte {
|
||
n := blockSize - len(src)%blockSize
|
||
out := make([]byte, len(src)+n)
|
||
copy(out, src)
|
||
for i := len(src); i < len(out); i++ {
|
||
out[i] = byte(n)
|
||
}
|
||
return out
|
||
}
|
||
|
||
// pkcs7Unpad 从 src 剥离 PKCS#7 填充。长度不是分组整数倍、或末尾填充字节不自洽
|
||
// 时返回 ErrBadCiphertext。
|
||
func pkcs7Unpad(src []byte, blockSize int) ([]byte, error) {
|
||
if len(src) == 0 || len(src)%blockSize != 0 {
|
||
return nil, ErrBadCiphertext
|
||
}
|
||
n := int(src[len(src)-1])
|
||
if n < 1 || n > blockSize {
|
||
return nil, ErrBadCiphertext
|
||
}
|
||
for _, b := range src[len(src)-n:] {
|
||
if int(b) != n {
|
||
return nil, ErrBadCiphertext
|
||
}
|
||
}
|
||
return src[:len(src)-n], nil
|
||
}
|
||
|
||
// aesECBEncrypt 用给定密钥以 ECB 模式对 src 做 AES 加密。Go 的 crypto/cipher
|
||
// 不提供 ECB,所以此模式在此自行实现:先把 src 填充到分组整数倍,再用同一密钥
|
||
// 逐块独立加密。ECB 是确定性的、不具备语义安全性;这里之所以需要它,仅仅是因为
|
||
// iLink CDN 协议对媒体负载强制要求该模式。
|
||
func aesECBEncrypt(key, src []byte) ([]byte, error) {
|
||
block, err := aes.NewCipher(key)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("weixin_ilink: new aes cipher: %w", err)
|
||
}
|
||
padded := pkcs7Pad(src, block.BlockSize())
|
||
out := make([]byte, len(padded))
|
||
for i := 0; i < len(padded); i += block.BlockSize() {
|
||
block.Encrypt(out[i:i+block.BlockSize()], padded[i:i+block.BlockSize()])
|
||
}
|
||
return out, nil
|
||
}
|
||
|
||
// aesECBDecrypt 用给定密钥对 src 做 AES 解密并剥离 PKCS#7 填充。ECB 没有 IV,
|
||
// 所以首块直接用密钥解密。输入格式非法时返回 ErrBadCiphertext。
|
||
func aesECBDecrypt(key, src []byte) ([]byte, error) {
|
||
block, err := aes.NewCipher(key)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("weixin_ilink: new aes cipher: %w", err)
|
||
}
|
||
if len(src) == 0 || len(src)%block.BlockSize() != 0 {
|
||
return nil, ErrBadCiphertext
|
||
}
|
||
out := make([]byte, len(src))
|
||
for i := 0; i < len(src); i += block.BlockSize() {
|
||
block.Decrypt(out[i:i+block.BlockSize()], src[i:i+block.BlockSize()])
|
||
}
|
||
return pkcs7Unpad(out, block.BlockSize())
|
||
}
|