Skip to content

Commit 591b97c

Browse files
committed
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
1 parent c150b22 commit 591b97c

File tree

13 files changed

+750
-278
lines changed

13 files changed

+750
-278
lines changed

.typos.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,7 @@
33
Readed = "Readed"
44
Sie = "Sie"
55
OT = "OT"
6+
Encrypter = "Encrypter"
7+
Decrypter = "Decrypter"
68
[files]
79
extend-exclude = ["go.mod", "go.sum"]

browser/chromium/chromium_windows.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ func (c *Chromium) GetMasterKey() ([]byte, error) {
3333
if err != nil {
3434
return nil, errDecodeMasterKeyFailed
3535
}
36-
c.masterKey, err = crypto.DPAPI(key[5:])
36+
c.masterKey, err = crypto.DecryptWithDPAPI(key[5:])
3737
if err != nil {
3838
slog.Error("decrypt master key failed", "err", err)
3939
return nil, err

browser/firefox/firefox.go

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ func (f *Firefox) GetMasterKey() ([]byte, error) {
8686
defer os.Remove(tempFilename)
8787
defer keyDB.Close()
8888

89-
globalSalt, metaBytes, err := queryMetaData(keyDB)
89+
metaItem1, metaItem2, err := queryMetaData(keyDB)
9090
if err != nil {
9191
return nil, fmt.Errorf("query metadata error: %w", err)
9292
}
@@ -96,16 +96,16 @@ func (f *Firefox) GetMasterKey() ([]byte, error) {
9696
return nil, fmt.Errorf("query NSS private error: %w", err)
9797
}
9898

99-
return processMasterKey(globalSalt, metaBytes, nssA11, nssA102)
99+
return processMasterKey(metaItem1, metaItem2, nssA11, nssA102)
100100
}
101101

102102
func queryMetaData(db *sql.DB) ([]byte, []byte, error) {
103103
const query = `SELECT item1, item2 FROM metaData WHERE id = 'password'`
104-
var globalSalt, metaBytes []byte
105-
if err := db.QueryRow(query).Scan(&globalSalt, &metaBytes); err != nil {
104+
var metaItem1, metaItem2 []byte
105+
if err := db.QueryRow(query).Scan(&metaItem1, &metaItem2); err != nil {
106106
return nil, nil, err
107107
}
108-
return globalSalt, metaBytes, nil
108+
return metaItem1, metaItem2, nil
109109
}
110110

111111
func queryNssPrivate(db *sql.DB) ([]byte, []byte, error) {
@@ -119,37 +119,40 @@ func queryNssPrivate(db *sql.DB) ([]byte, []byte, error) {
119119

120120
// processMasterKey process master key of Firefox.
121121
// Process the metaBytes and nssA11 with the corresponding cryptographic operations.
122-
func processMasterKey(globalSalt, metaBytes, nssA11, nssA102 []byte) ([]byte, error) {
123-
metaPBE, err := crypto.NewASN1PBE(metaBytes)
122+
func processMasterKey(metaItem1, metaItem2, nssA11, nssA102 []byte) ([]byte, error) {
123+
metaPBE, err := crypto.NewASN1PBE(metaItem2)
124124
if err != nil {
125-
return nil, err
125+
return nil, fmt.Errorf("error creating ASN1PBE from metaItem2: %w", err)
126126
}
127127

128-
k, err := metaPBE.Decrypt(globalSalt)
128+
flag, err := metaPBE.Decrypt(metaItem1)
129129
if err != nil {
130-
return nil, err
130+
return nil, fmt.Errorf("error decrypting master key: %w", err)
131131
}
132+
const passwordCheck = "password-check"
132133

133-
if !bytes.Contains(k, []byte("password-check")) {
134-
return nil, errors.New("password-check not found")
134+
if !bytes.Contains(flag, []byte(passwordCheck)) {
135+
return nil, errors.New("flag verification failed: password-check not found")
135136
}
136-
keyLin := []byte{248, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}
137+
138+
var keyLin = []byte{248, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}
137139
if !bytes.Equal(nssA102, keyLin) {
138-
return nil, errors.New("nssA102 not equal keyLin")
140+
return nil, errors.New("master key verification failed: nssA102 not equal to expected value")
139141
}
140-
nssPBE, err := crypto.NewASN1PBE(nssA11)
142+
143+
nssA11PBE, err := crypto.NewASN1PBE(nssA11)
141144
if err != nil {
142-
return nil, err
145+
return nil, fmt.Errorf("error creating ASN1PBE from nssA11: %w", err)
143146
}
144-
finallyKey, err := nssPBE.Decrypt(globalSalt)
147+
148+
finallyKey, err := nssA11PBE.Decrypt(metaItem1)
145149
if err != nil {
146-
return nil, err
150+
return nil, fmt.Errorf("error decrypting final key: %w", err)
147151
}
148152
if len(finallyKey) < 24 {
149-
return nil, errors.New("finallyKey length less than 24")
153+
return nil, errors.New("length of final key is less than 24 bytes")
150154
}
151-
finallyKey = finallyKey[:24]
152-
return finallyKey, nil
155+
return finallyKey[:24], nil
153156
}
154157

155158
func (f *Firefox) Name() string {

browsingdata/cookie/cookie.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,9 @@ func (c *ChromiumCookie) Parse(masterKey []byte) error {
7272
}
7373
if len(encryptValue) > 0 {
7474
if len(masterKey) == 0 {
75-
value, err = crypto.DPAPI(encryptValue)
75+
value, err = crypto.DecryptWithDPAPI(encryptValue)
7676
} else {
77-
value, err = crypto.DecryptPass(masterKey, encryptValue)
77+
value, err = crypto.DecryptWithChromium(masterKey, encryptValue)
7878
}
7979
if err != nil {
8080
slog.Error("decrypt chromium cookie error", "err", err)

browsingdata/creditcard/creditcard.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,9 @@ func (c *ChromiumCreditCard) Parse(masterKey []byte) error {
5959
}
6060
if len(encryptValue) > 0 {
6161
if len(masterKey) == 0 {
62-
value, err = crypto.DPAPI(encryptValue)
62+
value, err = crypto.DecryptWithDPAPI(encryptValue)
6363
} else {
64-
value, err = crypto.DecryptPass(masterKey, encryptValue)
64+
value, err = crypto.DecryptWithChromium(masterKey, encryptValue)
6565
}
6666
if err != nil {
6767
slog.Error("decrypt chromium credit card error", "err", err)
@@ -114,9 +114,9 @@ func (c *YandexCreditCard) Parse(masterKey []byte) error {
114114
}
115115
if len(encryptValue) > 0 {
116116
if len(masterKey) == 0 {
117-
value, err = crypto.DPAPI(encryptValue)
117+
value, err = crypto.DecryptWithDPAPI(encryptValue)
118118
} else {
119-
value, err = crypto.DecryptPass(masterKey, encryptValue)
119+
value, err = crypto.DecryptWithChromium(masterKey, encryptValue)
120120
}
121121
if err != nil {
122122
slog.Error("decrypt chromium credit card error", "err", err)

browsingdata/password/password.go

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,9 @@ func (c *ChromiumPassword) Parse(masterKey []byte) error {
6161
}
6262
if len(pwd) > 0 {
6363
if len(masterKey) == 0 {
64-
password, err = crypto.DPAPI(pwd)
64+
password, err = crypto.DecryptWithDPAPI(pwd)
6565
} else {
66-
password, err = crypto.DecryptPass(masterKey, pwd)
66+
password, err = crypto.DecryptWithChromium(masterKey, pwd)
6767
}
6868
if err != nil {
6969
slog.Error("decrypt chromium password error", "err", err)
@@ -129,9 +129,9 @@ func (c *YandexPassword) Parse(masterKey []byte) error {
129129

130130
if len(pwd) > 0 {
131131
if len(masterKey) == 0 {
132-
password, err = crypto.DPAPI(pwd)
132+
password, err = crypto.DecryptWithDPAPI(pwd)
133133
} else {
134-
password, err = crypto.DecryptPass(masterKey, pwd)
134+
password, err = crypto.DecryptWithChromium(masterKey, pwd)
135135
}
136136
if err != nil {
137137
slog.Error("decrypt yandex password error", "err", err)
@@ -162,12 +162,7 @@ func (c *YandexPassword) Len() int {
162162

163163
type FirefoxPassword []loginData
164164

165-
const (
166-
queryMetaData = `SELECT item1, item2 FROM metaData WHERE id = 'password'`
167-
queryNssPrivate = `SELECT a11, a102 from nssPrivate`
168-
)
169-
170-
func (f *FirefoxPassword) Parse(masterKey []byte) error {
165+
func (f *FirefoxPassword) Parse(globalSalt []byte) error {
171166
logins, err := getFirefoxLoginData()
172167
if err != nil {
173168
return err
@@ -182,11 +177,11 @@ func (f *FirefoxPassword) Parse(masterKey []byte) error {
182177
if err != nil {
183178
return err
184179
}
185-
user, err := userPBE.Decrypt(masterKey)
180+
user, err := userPBE.Decrypt(globalSalt)
186181
if err != nil {
187182
return err
188183
}
189-
pwd, err := pwdPBE.Decrypt(masterKey)
184+
pwd, err := pwdPBE.Decrypt(globalSalt)
190185
if err != nil {
191186
return err
192187
}

crypto/asn1pbe.go

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
package crypto
2+
3+
import (
4+
"crypto/hmac"
5+
"crypto/sha1"
6+
"crypto/sha256"
7+
"encoding/asn1"
8+
"errors"
9+
10+
"golang.org/x/crypto/pbkdf2"
11+
)
12+
13+
type ASN1PBE interface {
14+
Decrypt(globalSalt []byte) ([]byte, error)
15+
16+
Encrypt(globalSalt, plaintext []byte) ([]byte, error)
17+
}
18+
19+
func NewASN1PBE(b []byte) (pbe ASN1PBE, err error) {
20+
var (
21+
nss nssPBE
22+
meta metaPBE
23+
login loginPBE
24+
)
25+
if _, err := asn1.Unmarshal(b, &nss); err == nil {
26+
return nss, nil
27+
}
28+
if _, err := asn1.Unmarshal(b, &meta); err == nil {
29+
return meta, nil
30+
}
31+
if _, err := asn1.Unmarshal(b, &login); err == nil {
32+
return login, nil
33+
}
34+
return nil, ErrDecodeASN1Failed
35+
}
36+
37+
var ErrDecodeASN1Failed = errors.New("decode ASN1 data failed")
38+
39+
// nssPBE Struct
40+
//
41+
// SEQUENCE (2 elem)
42+
// OBJECT IDENTIFIER
43+
// SEQUENCE (2 elem)
44+
// OCTET STRING (20 byte)
45+
// INTEGER 1
46+
// OCTET STRING (16 byte)
47+
type nssPBE struct {
48+
AlgoAttr struct {
49+
asn1.ObjectIdentifier
50+
SaltAttr struct {
51+
EntrySalt []byte
52+
Len int
53+
}
54+
}
55+
Encrypted []byte
56+
}
57+
58+
// Decrypt decrypts the encrypted password with the global salt.
59+
func (n nssPBE) Decrypt(globalSalt []byte) ([]byte, error) {
60+
key, iv := n.deriveKeyAndIV(globalSalt)
61+
62+
return DES3Decrypt(key, iv, n.Encrypted)
63+
}
64+
65+
func (n nssPBE) Encrypt(globalSalt []byte, plaintext []byte) ([]byte, error) {
66+
key, iv := n.deriveKeyAndIV(globalSalt)
67+
68+
return DES3Encrypt(key, iv, plaintext)
69+
}
70+
71+
// deriveKeyAndIV derives the key and initialization vector (IV)
72+
// from the global salt and entry salt.
73+
func (n nssPBE) deriveKeyAndIV(globalSalt []byte) ([]byte, []byte) {
74+
salt := n.AlgoAttr.SaltAttr.EntrySalt
75+
hashPrefix := sha1.Sum(globalSalt)
76+
compositeHash := sha1.Sum(append(hashPrefix[:], salt...))
77+
paddedEntrySalt := paddingZero(salt, 20)
78+
79+
hmacProcessor := hmac.New(sha1.New, compositeHash[:])
80+
hmacProcessor.Write(paddedEntrySalt)
81+
82+
paddedEntrySalt = append(paddedEntrySalt, salt...)
83+
keyComponent1 := hmac.New(sha1.New, compositeHash[:])
84+
keyComponent1.Write(paddedEntrySalt)
85+
86+
hmacWithSalt := append(hmacProcessor.Sum(nil), salt...)
87+
keyComponent2 := hmac.New(sha1.New, compositeHash[:])
88+
keyComponent2.Write(hmacWithSalt)
89+
90+
key := append(keyComponent1.Sum(nil), keyComponent2.Sum(nil)...)
91+
iv := key[len(key)-8:]
92+
return key[:24], iv
93+
}
94+
95+
// MetaPBE Struct
96+
//
97+
// SEQUENCE (2 elem)
98+
// OBJECT IDENTIFIER
99+
// SEQUENCE (2 elem)
100+
// SEQUENCE (2 elem)
101+
// OBJECT IDENTIFIER
102+
// SEQUENCE (4 elem)
103+
// OCTET STRING (32 byte)
104+
// INTEGER 1
105+
// INTEGER 32
106+
// SEQUENCE (1 elem)
107+
// OBJECT IDENTIFIER
108+
// SEQUENCE (2 elem)
109+
// OBJECT IDENTIFIER
110+
// OCTET STRING (14 byte)
111+
// OCTET STRING (16 byte)
112+
type metaPBE struct {
113+
AlgoAttr algoAttr
114+
Encrypted []byte
115+
}
116+
117+
type algoAttr struct {
118+
asn1.ObjectIdentifier
119+
Data struct {
120+
Data struct {
121+
asn1.ObjectIdentifier
122+
SlatAttr slatAttr
123+
}
124+
IVData ivAttr
125+
}
126+
}
127+
128+
type ivAttr struct {
129+
asn1.ObjectIdentifier
130+
IV []byte
131+
}
132+
133+
type slatAttr struct {
134+
EntrySalt []byte
135+
IterationCount int
136+
KeySize int
137+
Algorithm struct {
138+
asn1.ObjectIdentifier
139+
}
140+
}
141+
142+
func (m metaPBE) Decrypt(globalSalt []byte) ([]byte, error) {
143+
key, iv := m.deriveKeyAndIV(globalSalt)
144+
145+
return AES128CBCDecrypt(key, iv, m.Encrypted)
146+
}
147+
148+
func (m metaPBE) Encrypt(globalSalt, plaintext []byte) ([]byte, error) {
149+
key, iv := m.deriveKeyAndIV(globalSalt)
150+
151+
return AES128CBCEncrypt(key, iv, plaintext)
152+
}
153+
154+
func (m metaPBE) deriveKeyAndIV(globalSalt []byte) ([]byte, []byte) {
155+
password := sha1.Sum(globalSalt)
156+
157+
salt := m.AlgoAttr.Data.Data.SlatAttr.EntrySalt
158+
iter := m.AlgoAttr.Data.Data.SlatAttr.IterationCount
159+
keyLen := m.AlgoAttr.Data.Data.SlatAttr.KeySize
160+
161+
key := pbkdf2.Key(password[:], salt, iter, keyLen, sha256.New)
162+
iv := append([]byte{4, 14}, m.AlgoAttr.Data.IVData.IV...)
163+
return key, iv
164+
}
165+
166+
// loginPBE Struct
167+
//
168+
// OCTET STRING (16 byte)
169+
// SEQUENCE (2 elem)
170+
// OBJECT IDENTIFIER
171+
// OCTET STRING (8 byte)
172+
// OCTET STRING (16 byte)
173+
type loginPBE struct {
174+
CipherText []byte
175+
Data struct {
176+
asn1.ObjectIdentifier
177+
IV []byte
178+
}
179+
Encrypted []byte
180+
}
181+
182+
func (l loginPBE) Decrypt(globalSalt []byte) ([]byte, error) {
183+
key, iv := l.deriveKeyAndIV(globalSalt)
184+
return DES3Decrypt(key, iv, l.Encrypted)
185+
}
186+
187+
func (l loginPBE) Encrypt(globalSalt, plaintext []byte) ([]byte, error) {
188+
key, iv := l.deriveKeyAndIV(globalSalt)
189+
return DES3Encrypt(key, iv, plaintext)
190+
}
191+
192+
func (l loginPBE) deriveKeyAndIV(globalSalt []byte) ([]byte, []byte) {
193+
return globalSalt, l.Data.IV
194+
}

0 commit comments

Comments
 (0)