Files
Cyrene_v2/plugins/channels/weixin_ilink/aes.go
T

80 lines
2.6 KiB
Go

package weixin_ilink
import (
"crypto/aes"
"errors"
"fmt"
)
// ErrBadCiphertext is returned when ciphertext is not a whole number of AES
// blocks or its padding is malformed.
var ErrBadCiphertext = errors.New("weixin_ilink: bad ciphertext or padding")
// pkcs7Pad appends PKCS#7 padding to src so its length becomes a multiple of
// blockSize. Every appended byte holds the pad length, so it self-describes
// and Unpad can strip it unambiguously.
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 strips PKCS#7 padding from src. It returns ErrBadCiphertext when
// the length is not a block multiple or the final pad byte is inconsistent.
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 encrypts src under AES with the supplied key using ECB mode.
// Go's crypto/cipher package does not ship ECB, so the mode is implemented
// here: src is padded to a block multiple and every block is encrypted
// independently with the same key. ECB is deterministic and not semantically
// secure; it is required here only because the iLink CDN protocol mandates it
// for media payloads.
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 decrypts src under AES with the supplied key and strips the
// PKCS#7 padding. ECB has no IV, so the first block decrypts directly with the
// key. It returns ErrBadCiphertext for malformed input.
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())
}