Files
HackBrowserData/crypto/crypto.go
T
Roger 76e2615db2 refactor(windows): clean up Chrome ABE module (#574)
* refactor(abe): remove --abe-key flag and its global state
* refactor(abe): rework scratch protocol and Go/C structure
2026-04-19 15:20:51 +08:00

176 lines
5.0 KiB
Go

package crypto
import (
"crypto/aes"
"crypto/cipher"
"crypto/des"
"fmt"
)
// AESCBCEncrypt encrypts data using AES-CBC mode with PKCS5 padding.
// Supports all AES key sizes: 16 bytes (AES-128), 24 bytes (AES-192), or 32 bytes (AES-256).
func AESCBCEncrypt(key, iv, plaintext []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
return cbcEncrypt(block, iv, plaintext)
}
// AESCBCDecrypt decrypts data using AES-CBC mode with PKCS5 unpadding.
// Supports all AES key sizes: 16 bytes (AES-128), 24 bytes (AES-192), or 32 bytes (AES-256).
func AESCBCDecrypt(key, iv, ciphertext []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
return cbcDecrypt(block, iv, ciphertext)
}
// DES3Encrypt encrypts data using 3DES-CBC mode with PKCS5 padding.
func DES3Encrypt(key, iv, plaintext []byte) ([]byte, error) {
block, err := des.NewTripleDESCipher(key)
if err != nil {
return nil, err
}
return cbcEncrypt(block, iv, plaintext)
}
// DES3Decrypt decrypts data using 3DES-CBC mode with PKCS5 unpadding.
func DES3Decrypt(key, iv, ciphertext []byte) ([]byte, error) {
block, err := des.NewTripleDESCipher(key)
if err != nil {
return nil, err
}
return cbcDecrypt(block, iv, ciphertext)
}
// gcmNonceSize is the AES-GCM standard nonce size used by Chromium's v10/v20
// cipher formats. Cross-platform because the v20 ciphertext layout is the
// same regardless of host OS (only Windows currently produces v20).
const gcmNonceSize = 12
// DecryptChromiumV20 decrypts a Chromium v20 (App-Bound Encryption) ciphertext.
// Format: "v20" prefix (3B) + nonce (12B) + AES-GCM(payload + 16B tag).
//
// Cross-platform: v20 is only produced by Chrome on Windows today, but the
// decryption math is platform-neutral. Keeping it here rather than in
// crypto_windows.go ensures the routing in browser/chromium/decrypt.go stays
// testable on Linux/macOS CI.
func DecryptChromiumV20(key, ciphertext []byte) ([]byte, error) {
if len(ciphertext) < versionPrefixLen+gcmNonceSize {
return nil, errShortCiphertext
}
nonce := ciphertext[versionPrefixLen : versionPrefixLen+gcmNonceSize]
payload := ciphertext[versionPrefixLen+gcmNonceSize:]
return AESGCMDecrypt(key, nonce, payload)
}
// AESGCMEncrypt encrypts data using AES-GCM mode.
func AESGCMEncrypt(key, nonce, plaintext []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
aead, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
if len(nonce) != aead.NonceSize() {
return nil, errInvalidNonceLen
}
return aead.Seal(nil, nonce, plaintext, nil), nil
}
// AESGCMDecrypt decrypts data using AES-GCM mode.
func AESGCMDecrypt(key, nonce, ciphertext []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
aead, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
if len(nonce) != aead.NonceSize() {
return nil, errInvalidNonceLen
}
return aead.Open(nil, nonce, ciphertext, nil)
}
// cbcEncrypt adds PKCS5 padding and encrypts plaintext in CBC mode.
func cbcEncrypt(block cipher.Block, iv, plaintext []byte) ([]byte, error) {
if len(iv) != block.BlockSize() {
return nil, errInvalidIVLength
}
padded := pkcs5Padding(plaintext, block.BlockSize())
dst := make([]byte, len(padded))
cipher.NewCBCEncrypter(block, iv).CryptBlocks(dst, padded)
return dst, nil
}
// cbcDecrypt decrypts ciphertext in CBC mode and removes PKCS5 padding.
func cbcDecrypt(block cipher.Block, iv, ciphertext []byte) ([]byte, error) {
bs := block.BlockSize()
if len(iv) != bs {
return nil, errInvalidIVLength
}
if len(ciphertext) < bs {
return nil, errShortCiphertext
}
if len(ciphertext)%bs != 0 {
return nil, errInvalidBlockSize
}
dst := make([]byte, len(ciphertext))
cipher.NewCBCDecrypter(block, iv).CryptBlocks(dst, ciphertext)
dst, err := pkcs5UnPadding(dst, bs)
if err != nil {
return nil, fmt.Errorf("decrypt: %w", err)
}
return dst, nil
}
// paddingZero pads src with zero bytes to the given length.
// Returns src unchanged if already long enough; otherwise returns a new slice.
func paddingZero(src []byte, length int) []byte {
if len(src) >= length {
return src
}
dst := make([]byte, length)
copy(dst, src)
return dst
}
// pkcs5Padding adds PKCS5/PKCS7 padding to src.
// Always returns a new slice; never modifies src.
func pkcs5Padding(src []byte, blockSize int) []byte {
n := blockSize - (len(src) % blockSize)
dst := make([]byte, len(src)+n)
copy(dst, src)
for i := len(src); i < len(dst); i++ {
dst[i] = byte(n)
}
return dst
}
// pkcs5UnPadding removes PKCS5/PKCS7 padding from src.
func pkcs5UnPadding(src []byte, blockSize int) ([]byte, error) {
length := len(src)
if length == 0 {
return nil, errInvalidPadding
}
padding := int(src[length-1])
if padding < 1 || padding > blockSize || padding > length {
return nil, errInvalidPadding
}
for _, b := range src[length-padding:] {
if int(b) != padding {
return nil, errInvalidPadding
}
}
return src[:length-padding], nil
}