feat: Support decryption of Firefox 144+ (AES-256-CBC) (#498)

* feat: Support decryption of Firefox 144+ (AES-256-CBC)

* docs: Add Firefox 144+ support to README
This commit is contained in:
slimwang
2026-02-14 01:18:53 +08:00
committed by GitHub
parent 2f3f4a908a
commit 54f55a03ed
5 changed files with 56 additions and 4 deletions
+27 -2
View File
@@ -179,12 +179,37 @@ type loginPBE struct {
func (l loginPBE) Decrypt(globalSalt []byte) ([]byte, error) {
key, iv := l.deriveKeyAndIV(globalSalt)
return DES3Decrypt(key, iv, l.Encrypted)
// The encryption algorithm can be reliably inferred from IV length:
// - 8 bytes : 3DES-CBC (legacy Firefox versions)
// - 16 bytes : AES-CBC (Firefox 144+)
if len(iv) == 8 {
// Use 3DES for old Firefox versions
return DES3Decrypt(key[:24], iv, l.Encrypted)
} else if len(iv) == 16 {
// Firefox 144+ uses 32-byte keys (AES-256-CBC)
// Note: AES128CBCDecrypt is a misnomer - it actually supports all AES key lengths
return AES128CBCDecrypt(key, iv, l.Encrypted)
}
return nil, errors.New("unsupported IV length for loginPBE decryption")
}
func (l loginPBE) Encrypt(globalSalt, plaintext []byte) ([]byte, error) {
key, iv := l.deriveKeyAndIV(globalSalt)
return DES3Encrypt(key, iv, plaintext)
// The encryption algorithm can be reliably inferred from IV length:
// - 8 bytes : 3DES-CBC (legacy Firefox versions)
// - 16 bytes : AES-CBC (Firefox 144+)
// This avoids relying on NSS-specific OIDs, which have changed historically.
if len(iv) == 8 {
// Use 3DES for old Firefox versions
return DES3Encrypt(key[:24], iv, plaintext)
} else if len(iv) == 16 {
// Firefox 144+ uses 32-byte keys (AES-256-CBC)
// Note: AES128CBCDecrypt is a misnomer - it actually supports all AES key lengths
return AES128CBCEncrypt(key, iv, plaintext)
}
return nil, errors.New("unsupported IV length for loginPBE encryption")
}
func (l loginPBE) deriveKeyAndIV(globalSalt []byte) ([]byte, []byte) {
+5
View File
@@ -11,6 +11,11 @@ import (
var ErrCiphertextLengthIsInvalid = errors.New("ciphertext length is invalid")
// AES128CBCDecrypt decrypts data using AES-CBC mode.
// Note: Despite the function name, this supports all AES key sizes.
// The Go standard library's aes.NewCipher automatically selects the AES variant
// based on the key length: 16 bytes (AES-128), 24 bytes (AES-192), or 32 bytes (AES-256).
// TODO: Rename to AESCBCDecrypt to avoid confusion about supported key lengths.
func AES128CBCDecrypt(key, iv, ciphertext []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {