@@ -2,8 +2,12 @@ package chromium
22
33import (
44 "database/sql"
5+ "encoding/json"
6+ "errors"
57
8+ "github.com/moond4rk/hackbrowserdata/crypto"
69 "github.com/moond4rk/hackbrowserdata/crypto/keyretriever"
10+ "github.com/moond4rk/hackbrowserdata/log"
711 "github.com/moond4rk/hackbrowserdata/types"
812 "github.com/moond4rk/hackbrowserdata/utils/sqliteutil"
913)
@@ -12,8 +16,26 @@ const (
1216 defaultCreditCardQuery = `SELECT COALESCE(guid, ''), name_on_card, expiration_month, expiration_year,
1317 card_number_encrypted, COALESCE(nickname, ''), COALESCE(billing_address_id, '') FROM credit_cards`
1418 countCreditCardQuery = `SELECT COUNT(*) FROM credit_cards`
19+
20+ yandexCreditCardQuery = `SELECT guid, public_data, private_data FROM records`
21+ yandexCreditCardCountQuery = `SELECT COUNT(*) FROM records`
1522)
1623
24+ // yandexPublicData is the plaintext JSON in records.public_data.
25+ type yandexPublicData struct {
26+ CardHolder string `json:"card_holder"`
27+ CardTitle string `json:"card_title"`
28+ ExpireDateYear string `json:"expire_date_year"`
29+ ExpireDateMonth string `json:"expire_date_month"`
30+ }
31+
32+ // yandexPrivateData is the AES-GCM-sealed JSON in records.private_data.
33+ type yandexPrivateData struct {
34+ FullCardNumber string `json:"full_card_number"`
35+ PinCode string `json:"pin_code"`
36+ SecretComment string `json:"secret_comment"`
37+ }
38+
1739func extractCreditCards (keys keyretriever.MasterKeys , path string ) ([]types.CreditCardEntry , error ) {
1840 cards , err := sqliteutil .QueryRows (path , false , defaultCreditCardQuery ,
1941 func (rows * sql.Rows ) (types.CreditCardEntry , error ) {
@@ -39,6 +61,72 @@ func extractCreditCards(keys keyretriever.MasterKeys, path string) ([]types.Cred
3961 return cards , nil
4062}
4163
64+ // extractYandexCreditCards reads the records table (not Chromium's credit_cards). AAD = guid. See RFC-012 §4.
65+ func extractYandexCreditCards (keys keyretriever.MasterKeys , path string ) ([]types.CreditCardEntry , error ) {
66+ dataKey , err := loadYandexDataKey (path , keys .V10 )
67+ if err != nil {
68+ if errors .Is (err , errYandexMasterPasswordSet ) {
69+ log .Warnf ("%s: %v" , path , err )
70+ return nil , nil
71+ }
72+ return nil , err
73+ }
74+
75+ return sqliteutil .QueryRows (path , false , yandexCreditCardQuery ,
76+ func (rows * sql.Rows ) (types.CreditCardEntry , error ) {
77+ var guid , publicData string
78+ var privateData []byte
79+ if err := rows .Scan (& guid , & publicData , & privateData ); err != nil {
80+ return types.CreditCardEntry {}, err
81+ }
82+
83+ var public yandexPublicData
84+ if publicData != "" {
85+ if err := json .Unmarshal ([]byte (publicData ), & public ); err != nil {
86+ log .Debugf ("yandex: parse public_data for %s: %v" , guid , err )
87+ }
88+ }
89+ entry := types.CreditCardEntry {
90+ GUID : guid ,
91+ Name : public .CardHolder ,
92+ ExpMonth : public .ExpireDateMonth ,
93+ ExpYear : public .ExpireDateYear ,
94+ NickName : public .CardTitle ,
95+ }
96+
97+ plaintext , err := crypto .AESGCMDecryptBlob (dataKey , privateData , yandexCardAAD (guid , nil ))
98+ if err != nil {
99+ log .Debugf ("yandex: decrypt card %s: %v" , guid , err )
100+ return entry , nil
101+ }
102+
103+ var private yandexPrivateData
104+ if err := json .Unmarshal (plaintext , & private ); err != nil {
105+ log .Debugf ("yandex: parse private_data for %s: %v" , guid , err )
106+ return entry , nil
107+ }
108+ entry .Number = private .FullCardNumber
109+ entry .CVC = private .PinCode
110+ entry .Comment = private .SecretComment
111+ return entry , nil
112+ })
113+ }
114+
42115func countCreditCards (path string ) (int , error ) {
43116 return sqliteutil .CountRows (path , false , countCreditCardQuery )
44117}
118+
119+ func countYandexCreditCards (path string ) (int , error ) {
120+ return sqliteutil .CountRows (path , false , yandexCreditCardCountQuery )
121+ }
122+
123+ // yandexCardAAD is the raw guid bytes (+ keyID if the profile has a master password).
124+ func yandexCardAAD (guid string , keyID []byte ) []byte {
125+ if len (keyID ) == 0 {
126+ return []byte (guid )
127+ }
128+ out := make ([]byte , 0 , len (guid )+ len (keyID ))
129+ out = append (out , guid ... )
130+ out = append (out , keyID ... )
131+ return out
132+ }
0 commit comments