feat: Refactor crypto decryption functions for consistency and error handling (#302)

* feat: Refactor crypto decryption functions for consistency and error handling

- Close #301
- Refactored and renamed decryption functions across multiple files for consistency
- Updated cookie sorting method to sort in descending order
- Added new encryption functions for AES in CBC and GCM modes and DES in CBC mode
- Added error handling to decryption functions and created new error variables for invalid ciphertext length and decode failures
- Test cases added for encryption and decryption functions
- Removed unused code and imports.

* chore: Add new words to .typos.toml dictionary

- Add new terms to `.typos.toml` dictionary
- Improve code formatting and readability
- Refactor functions for better performance
- Update comments and documentation
- Resolve minor bugs and errors

* refactor: Refactor crypto package for better structure and readability

- Refactored and cleaned up crypto package code for better readability
- Renamed `ToByteArray` method to `bytes` for consistency
- Modified `DecryptWithDPAPI` method to use `outBlob.bytes()` for efficiency
- Added comments and removed unused methods in `loginPBE`
- Refactored `nssPBE` and `metaPBE` Decrypt methods to use `deriveKeyAndIV` helper method
- Improved overall maintainability and organization of codebase

* refactor: Refactor firefox password encryption and decryption.

- Implement ASN1PBE interface with various PBE struct types and encryption/decryption methods
- Fix naming and remove unused variables in browsingdata and crypto files
- Add tests for ASN1PBE implementation using external assertion package
- Refactor and improve error handling in firefox file functions related to master key retrieval
- Add input validation and AES-GCM encryption function to crypto file
This commit is contained in:
ᴍᴏᴏɴD4ʀᴋ
2024-01-27 22:30:28 +08:00
parent c150b22c1b
commit 591b97ce6d
13 changed files with 771 additions and 299 deletions
+194
View File
@@ -0,0 +1,194 @@
package crypto
import (
"crypto/hmac"
"crypto/sha1"
"crypto/sha256"
"encoding/asn1"
"errors"
"golang.org/x/crypto/pbkdf2"
)
type ASN1PBE interface {
Decrypt(globalSalt []byte) ([]byte, error)
Encrypt(globalSalt, plaintext []byte) ([]byte, error)
}
func NewASN1PBE(b []byte) (pbe ASN1PBE, err error) {
var (
nss nssPBE
meta metaPBE
login loginPBE
)
if _, err := asn1.Unmarshal(b, &nss); err == nil {
return nss, nil
}
if _, err := asn1.Unmarshal(b, &meta); err == nil {
return meta, nil
}
if _, err := asn1.Unmarshal(b, &login); err == nil {
return login, nil
}
return nil, ErrDecodeASN1Failed
}
var ErrDecodeASN1Failed = errors.New("decode ASN1 data failed")
// nssPBE Struct
//
// SEQUENCE (2 elem)
// OBJECT IDENTIFIER
// SEQUENCE (2 elem)
// OCTET STRING (20 byte)
// INTEGER 1
// OCTET STRING (16 byte)
type nssPBE struct {
AlgoAttr struct {
asn1.ObjectIdentifier
SaltAttr struct {
EntrySalt []byte
Len int
}
}
Encrypted []byte
}
// Decrypt decrypts the encrypted password with the global salt.
func (n nssPBE) Decrypt(globalSalt []byte) ([]byte, error) {
key, iv := n.deriveKeyAndIV(globalSalt)
return DES3Decrypt(key, iv, n.Encrypted)
}
func (n nssPBE) Encrypt(globalSalt []byte, plaintext []byte) ([]byte, error) {
key, iv := n.deriveKeyAndIV(globalSalt)
return DES3Encrypt(key, iv, plaintext)
}
// deriveKeyAndIV derives the key and initialization vector (IV)
// from the global salt and entry salt.
func (n nssPBE) deriveKeyAndIV(globalSalt []byte) ([]byte, []byte) {
salt := n.AlgoAttr.SaltAttr.EntrySalt
hashPrefix := sha1.Sum(globalSalt)
compositeHash := sha1.Sum(append(hashPrefix[:], salt...))
paddedEntrySalt := paddingZero(salt, 20)
hmacProcessor := hmac.New(sha1.New, compositeHash[:])
hmacProcessor.Write(paddedEntrySalt)
paddedEntrySalt = append(paddedEntrySalt, salt...)
keyComponent1 := hmac.New(sha1.New, compositeHash[:])
keyComponent1.Write(paddedEntrySalt)
hmacWithSalt := append(hmacProcessor.Sum(nil), salt...)
keyComponent2 := hmac.New(sha1.New, compositeHash[:])
keyComponent2.Write(hmacWithSalt)
key := append(keyComponent1.Sum(nil), keyComponent2.Sum(nil)...)
iv := key[len(key)-8:]
return key[:24], iv
}
// MetaPBE Struct
//
// SEQUENCE (2 elem)
// OBJECT IDENTIFIER
// SEQUENCE (2 elem)
// SEQUENCE (2 elem)
// OBJECT IDENTIFIER
// SEQUENCE (4 elem)
// OCTET STRING (32 byte)
// INTEGER 1
// INTEGER 32
// SEQUENCE (1 elem)
// OBJECT IDENTIFIER
// SEQUENCE (2 elem)
// OBJECT IDENTIFIER
// OCTET STRING (14 byte)
// OCTET STRING (16 byte)
type metaPBE struct {
AlgoAttr algoAttr
Encrypted []byte
}
type algoAttr struct {
asn1.ObjectIdentifier
Data struct {
Data struct {
asn1.ObjectIdentifier
SlatAttr slatAttr
}
IVData ivAttr
}
}
type ivAttr struct {
asn1.ObjectIdentifier
IV []byte
}
type slatAttr struct {
EntrySalt []byte
IterationCount int
KeySize int
Algorithm struct {
asn1.ObjectIdentifier
}
}
func (m metaPBE) Decrypt(globalSalt []byte) ([]byte, error) {
key, iv := m.deriveKeyAndIV(globalSalt)
return AES128CBCDecrypt(key, iv, m.Encrypted)
}
func (m metaPBE) Encrypt(globalSalt, plaintext []byte) ([]byte, error) {
key, iv := m.deriveKeyAndIV(globalSalt)
return AES128CBCEncrypt(key, iv, plaintext)
}
func (m metaPBE) deriveKeyAndIV(globalSalt []byte) ([]byte, []byte) {
password := sha1.Sum(globalSalt)
salt := m.AlgoAttr.Data.Data.SlatAttr.EntrySalt
iter := m.AlgoAttr.Data.Data.SlatAttr.IterationCount
keyLen := m.AlgoAttr.Data.Data.SlatAttr.KeySize
key := pbkdf2.Key(password[:], salt, iter, keyLen, sha256.New)
iv := append([]byte{4, 14}, m.AlgoAttr.Data.IVData.IV...)
return key, iv
}
// loginPBE Struct
//
// OCTET STRING (16 byte)
// SEQUENCE (2 elem)
// OBJECT IDENTIFIER
// OCTET STRING (8 byte)
// OCTET STRING (16 byte)
type loginPBE struct {
CipherText []byte
Data struct {
asn1.ObjectIdentifier
IV []byte
}
Encrypted []byte
}
func (l loginPBE) Decrypt(globalSalt []byte) ([]byte, error) {
key, iv := l.deriveKeyAndIV(globalSalt)
return DES3Decrypt(key, iv, l.Encrypted)
}
func (l loginPBE) Encrypt(globalSalt, plaintext []byte) ([]byte, error) {
key, iv := l.deriveKeyAndIV(globalSalt)
return DES3Encrypt(key, iv, plaintext)
}
func (l loginPBE) deriveKeyAndIV(globalSalt []byte) ([]byte, []byte) {
return globalSalt, l.Data.IV
}