diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 00000000..9702cb9a --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,69 @@ +run: + timeout: '5m' + skip-dirs: + - 'assets' + allow-parallel-runners: true + modules-download-mode: 'readonly' + +linters: + enable: + - 'asciicheck' + - 'deadcode' + - 'depguard' + - 'dogsled' + - 'errorlint' + - 'exportloopref' + - 'gofmt' + - 'goheader' + - 'goimports' + - 'gomodguard' + - 'goprintffuncname' + - 'gosec' + - 'govet' + - 'ineffassign' + - 'makezero' + - 'misspell' + - 'paralleltest' + - 'prealloc' + - 'predeclared' + - 'revive' + - 'typecheck' + - 'unconvert' + - 'varcheck' + - 'whitespace' + disable: +# unsupported lint with golang 1.18+ ref: https://github.com/golangci/golangci-lint/issues/2649 + - 'bodyclose' + - 'gosimple' + - 'noctx' + - 'sqlclosecheck' + - 'staticcheck' + - 'structcheck' + - 'stylecheck' + - 'unused' + - 'errcheck' + +issues: + exclude-use-default: false + exclude: + - should have a package comment + - should have comment + # G101: Potential hardcoded credentials + - G101 + # G103: Use of unsafe calls should be audited + - G103 + # G304: Potential file inclusion via variable + - G304 + # G404, G401, G502, G505: weak cryptographic list + - G401 + - G404 + - G502 + - G505 + exclude-rules: + - path: internal/browser/browser\.go + linters: + - 'deadcode' + - 'varcheck' + - 'unused' + max-issues-per-linter: 0 + max-same-issues: 0 diff --git a/go.mod b/go.mod index f2418f0a..eb0f7de7 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module hack-browser-data -go 1.18 +go 1.19 require ( github.com/gocarina/gocsv v0.0.0-20211203214250-4735fba0c1d9 diff --git a/internal/browingdata/bookmark/bookmark.go b/internal/browingdata/bookmark/bookmark.go index 2e455352..0dcd03f5 100644 --- a/internal/browingdata/bookmark/bookmark.go +++ b/internal/browingdata/bookmark/bookmark.go @@ -6,14 +6,14 @@ import ( "sort" "time" - "github.com/tidwall/gjson" - "hack-browser-data/internal/item" "hack-browser-data/internal/log" "hack-browser-data/internal/utils/fileutil" "hack-browser-data/internal/utils/typeutil" + // import sqlite3 driver _ "github.com/mattn/go-sqlite3" + "github.com/tidwall/gjson" ) type ChromiumBookmark []bookmark @@ -51,7 +51,7 @@ func getBookmarkChildren(value gjson.Result, w *ChromiumBookmark) (children gjso const ( bookmarkID = "id" bookmarkAdded = "date_added" - bookmarkUrl = "url" + bookmarkURL = "url" bookmarkName = "name" bookmarkType = "type" bookmarkChildren = "children" @@ -60,7 +60,7 @@ func getBookmarkChildren(value gjson.Result, w *ChromiumBookmark) (children gjso bm := bookmark{ ID: value.Get(bookmarkID).Int(), Name: value.Get(bookmarkName).String(), - URL: value.Get(bookmarkUrl).String(), + URL: value.Get(bookmarkURL).String(), DateAdded: typeutil.TimeEpoch(value.Get(bookmarkAdded).Int()), } children = value.Get(bookmarkChildren) diff --git a/internal/browingdata/browsingdata.go b/internal/browingdata/browsingdata.go index b8aa751f..7e1c275e 100644 --- a/internal/browingdata/browsingdata.go +++ b/internal/browingdata/browsingdata.go @@ -53,17 +53,22 @@ func (d *Data) Output(dir, browserName, flag string) { // if the length of the export data is 0, then it is not necessary to output continue } - filename := fileutil.Filename(browserName, source.Name(), output.Ext()) + filename := fileutil.ItemName(browserName, source.Name(), output.Ext()) f, err := output.CreateFile(dir, filename) if err != nil { - log.Errorf("create file error %s", err) + log.Errorf("create file %s error %s", filename, err.Error()) + continue } if err := output.Write(source, f); err != nil { - log.Errorf("%s write to file %s error %s", source.Name(), filename, err.Error()) + log.Errorf("write to file %s error %s", filename, err.Error()) + continue + } + if err := f.Close(); err != nil { + log.Errorf("close file %s error %s", filename, err.Error()) + continue } log.Noticef("output to file %s success", path.Join(dir, filename)) - f.Close() } } diff --git a/internal/browingdata/cookie/cookie.go b/internal/browingdata/cookie/cookie.go index 14cae53a..7f0e4f40 100644 --- a/internal/browingdata/cookie/cookie.go +++ b/internal/browingdata/cookie/cookie.go @@ -6,12 +6,13 @@ import ( "sort" "time" - _ "github.com/mattn/go-sqlite3" - "hack-browser-data/internal/decrypter" "hack-browser-data/internal/item" "hack-browser-data/internal/log" "hack-browser-data/internal/utils/typeutil" + + // import sqlite3 driver + _ "github.com/mattn/go-sqlite3" ) type ChromiumCookie []cookie @@ -72,7 +73,7 @@ func (c *ChromiumCookie) Parse(masterKey []byte) error { if len(encryptValue) > 0 { var err error if masterKey == nil { - value, err = decrypter.DPApi(encryptValue) + value, err = decrypter.DPAPI(encryptValue) } else { value, err = decrypter.Chromium(masterKey, encryptValue) } @@ -118,10 +119,10 @@ func (f *FirefoxCookie) Parse(masterKey []byte) error { for rows.Next() { var ( name, value, host, path string - isSecure, isHttpOnly int + isSecure, isHTTPOnly int creationTime, expiry int64 ) - if err = rows.Scan(&name, &value, &host, &path, &creationTime, &expiry, &isSecure, &isHttpOnly); err != nil { + if err = rows.Scan(&name, &value, &host, &path, &creationTime, &expiry, &isSecure, &isHTTPOnly); err != nil { log.Warn(err) } *f = append(*f, cookie{ @@ -129,7 +130,7 @@ func (f *FirefoxCookie) Parse(masterKey []byte) error { Host: host, Path: path, IsSecure: typeutil.IntToBool(isSecure), - IsHTTPOnly: typeutil.IntToBool(isHttpOnly), + IsHTTPOnly: typeutil.IntToBool(isHTTPOnly), CreateDate: typeutil.TimeStamp(creationTime / 1000000), ExpireDate: typeutil.TimeStamp(expiry), Value: value, diff --git a/internal/browingdata/creditcard/creditcard.go b/internal/browingdata/creditcard/creditcard.go index 781fe90e..b77d5273 100644 --- a/internal/browingdata/creditcard/creditcard.go +++ b/internal/browingdata/creditcard/creditcard.go @@ -4,11 +4,12 @@ import ( "database/sql" "os" - _ "github.com/mattn/go-sqlite3" - "hack-browser-data/internal/decrypter" "hack-browser-data/internal/item" "hack-browser-data/internal/log" + + // import sqlite3 driver + _ "github.com/mattn/go-sqlite3" ) type ChromiumCreditCard []card @@ -56,7 +57,7 @@ func (c *ChromiumCreditCard) Parse(masterKey []byte) error { NickName: nickname, } if masterKey == nil { - value, err = decrypter.DPApi(encryptValue) + value, err = decrypter.DPAPI(encryptValue) if err != nil { return err } @@ -112,7 +113,7 @@ func (c *YandexCreditCard) Parse(masterKey []byte) error { NickName: nickname, } if masterKey == nil { - value, err = decrypter.DPApi(encryptValue) + value, err = decrypter.DPAPI(encryptValue) if err != nil { return err } diff --git a/internal/browingdata/download/download.go b/internal/browingdata/download/download.go index 55bcc2d8..a74d2423 100644 --- a/internal/browingdata/download/download.go +++ b/internal/browingdata/download/download.go @@ -11,6 +11,7 @@ import ( "hack-browser-data/internal/log" "hack-browser-data/internal/utils/typeutil" + // import sqlite3 driver _ "github.com/mattn/go-sqlite3" "github.com/tidwall/gjson" ) @@ -19,7 +20,7 @@ type ChromiumDownload []download type download struct { TargetPath string - Url string + URL string TotalBytes int64 StartTime time.Time EndTime time.Time @@ -44,15 +45,15 @@ func (c *ChromiumDownload) Parse(masterKey []byte) error { defer rows.Close() for rows.Next() { var ( - targetPath, tabUrl, mimeType string + targetPath, tabURL, mimeType string totalBytes, startTime, endTime int64 ) - if err := rows.Scan(&targetPath, &tabUrl, &totalBytes, &startTime, &endTime, &mimeType); err != nil { + if err := rows.Scan(&targetPath, &tabURL, &totalBytes, &startTime, &endTime, &mimeType); err != nil { log.Warn(err) } data := download{ TargetPath: targetPath, - Url: tabUrl, + URL: tabURL, TotalBytes: totalBytes, StartTime: typeutil.TimeEpoch(startTime), EndTime: typeutil.TimeEpoch(endTime), @@ -119,7 +120,7 @@ func (f *FirefoxDownload) Parse(masterKey []byte) error { fileSize := gjson.Get(json, "fileSize") *f = append(*f, download{ TargetPath: path, - Url: url, + URL: url, TotalBytes: fileSize.Int(), StartTime: typeutil.TimeStamp(dateAdded / 1000000), EndTime: typeutil.TimeStamp(endTime.Int() / 1000), diff --git a/internal/browingdata/extension/extension.go b/internal/browingdata/extension/extension.go index 4758ce21..9a1bd014 100644 --- a/internal/browingdata/extension/extension.go +++ b/internal/browingdata/extension/extension.go @@ -3,11 +3,11 @@ package extension import ( "os" - "github.com/tidwall/gjson" - "hack-browser-data/internal/item" "hack-browser-data/internal/log" "hack-browser-data/internal/utils/fileutil" + + "github.com/tidwall/gjson" ) type ChromiumExtension []*extension diff --git a/internal/browingdata/history/history.go b/internal/browingdata/history/history.go index 2b67e586..25c0df2c 100644 --- a/internal/browingdata/history/history.go +++ b/internal/browingdata/history/history.go @@ -6,18 +6,19 @@ import ( "sort" "time" - _ "github.com/mattn/go-sqlite3" - "hack-browser-data/internal/item" "hack-browser-data/internal/log" "hack-browser-data/internal/utils/typeutil" + + // import sqlite3 driver + _ "github.com/mattn/go-sqlite3" ) type ChromiumHistory []history type history struct { Title string - Url string + URL string VisitCount int LastVisitTime time.Time } @@ -48,7 +49,7 @@ func (c *ChromiumHistory) Parse(masterKey []byte) error { log.Warn(err) } data := history{ - Url: url, + URL: url, Title: title, VisitCount: visitCount, LastVisitTime: typeutil.TimeEpoch(lastVisitTime), @@ -109,7 +110,7 @@ func (f *FirefoxHistory) Parse(masterKey []byte) error { } *f = append(*f, history{ Title: title, - Url: url, + URL: url, VisitCount: visitCount, LastVisitTime: typeutil.TimeStamp(visitDate / 1000000), }) diff --git a/internal/browingdata/localstorage/localstorage.go b/internal/browingdata/localstorage/localstorage.go index ccd7340c..8999c2c8 100644 --- a/internal/browingdata/localstorage/localstorage.go +++ b/internal/browingdata/localstorage/localstorage.go @@ -7,11 +7,11 @@ import ( "os" "strings" - "github.com/syndtr/goleveldb/leveldb" - "hack-browser-data/internal/item" "hack-browser-data/internal/log" "hack-browser-data/internal/utils/typeutil" + + "github.com/syndtr/goleveldb/leveldb" ) type ChromiumLocalStorage []storage diff --git a/internal/browingdata/outputter.go b/internal/browingdata/outputter.go index 4b2c43c6..1e9a3d05 100644 --- a/internal/browingdata/outputter.go +++ b/internal/browingdata/outputter.go @@ -52,7 +52,7 @@ func (o *OutPutter) CreateFile(dir, filename string) (*os.File, error) { if dir != "" { if _, err := os.Stat(dir); os.IsNotExist(err) { - err := os.MkdirAll(dir, 0o777) + err := os.MkdirAll(dir, 0o750) if err != nil { return nil, err } @@ -62,7 +62,7 @@ func (o *OutPutter) CreateFile(dir, filename string) (*os.File, error) { var file *os.File var err error p := filepath.Join(dir, filename) - file, err = os.OpenFile(p, os.O_TRUNC|os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o666) + file, err = os.OpenFile(filepath.Clean(p), os.O_TRUNC|os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o600) if err != nil { return nil, err } @@ -72,7 +72,6 @@ func (o *OutPutter) CreateFile(dir, filename string) (*os.File, error) { func (o *OutPutter) Ext() string { if o.json { return "json" - } else { - return "csv" } + return "csv" } diff --git a/internal/browingdata/outputter_test.go b/internal/browingdata/outputter_test.go index 83f83613..ca3c6abf 100644 --- a/internal/browingdata/outputter_test.go +++ b/internal/browingdata/outputter_test.go @@ -6,6 +6,7 @@ import ( ) func TestNewOutPutter(t *testing.T) { + t.Parallel() out := NewOutPutter("json") if out == nil { t.Error("New() returned nil") diff --git a/internal/browingdata/password/password.go b/internal/browingdata/password/password.go index 77ea4090..d6ddd068 100644 --- a/internal/browingdata/password/password.go +++ b/internal/browingdata/password/password.go @@ -4,18 +4,18 @@ import ( "bytes" "database/sql" "encoding/base64" - "io/ioutil" "os" "sort" "time" - _ "github.com/mattn/go-sqlite3" - "github.com/tidwall/gjson" - "hack-browser-data/internal/decrypter" "hack-browser-data/internal/item" "hack-browser-data/internal/log" "hack-browser-data/internal/utils/typeutil" + + // import sqlite3 driver + _ "github.com/mattn/go-sqlite3" + "github.com/tidwall/gjson" ) type ChromiumPassword []loginData @@ -25,7 +25,7 @@ type loginData struct { encryptPass []byte encryptUser []byte Password string - LoginUrl string + LoginURL string CreateDate time.Time } @@ -58,12 +58,12 @@ func (c *ChromiumPassword) Parse(masterKey []byte) error { login := loginData{ UserName: username, encryptPass: pwd, - LoginUrl: url, + LoginURL: url, } if len(pwd) > 0 { var err error if masterKey == nil { - password, err = decrypter.DPApi(pwd) + password, err = decrypter.DPAPI(pwd) } else { password, err = decrypter.Chromium(masterKey, pwd) } @@ -125,13 +125,13 @@ func (c *YandexPassword) Parse(masterKey []byte) error { login := loginData{ UserName: username, encryptPass: pwd, - LoginUrl: url, + LoginURL: url, } if len(pwd) > 0 { var err error if masterKey == nil { - password, err = decrypter.DPApi(pwd) + password, err = decrypter.DPAPI(pwd) } else { password, err = decrypter.Chromium(masterKey, pwd) } @@ -196,7 +196,7 @@ func (f *FirefoxPassword) Parse(masterKey []byte) error { if err != nil { return err } - allLogin, err := getFirefoxLoginData(item.TempFirefoxPassword) + allLogin, err := getFirefoxLoginData() if err != nil { return err } @@ -218,7 +218,7 @@ func (f *FirefoxPassword) Parse(masterKey []byte) error { return err } *f = append(*f, loginData{ - LoginUrl: v.LoginUrl, + LoginURL: v.LoginURL, UserName: string(user), Password: string(pwd), CreateDate: v.CreateDate, @@ -251,12 +251,12 @@ func getFirefoxDecryptKey(key4file string) (item1, item2, a11, a102 []byte, err return item1, item2, a11, a102, nil } -func getFirefoxLoginData(loginJson string) (l []loginData, err error) { - s, err := ioutil.ReadFile(loginJson) +func getFirefoxLoginData() (l []loginData, err error) { + s, err := os.ReadFile(item.TempFirefoxPassword) if err != nil { return nil, err } - defer os.Remove(loginJson) + defer os.Remove(item.TempFirefoxPassword) h := gjson.GetBytes(s, "logins") if h.Exists() { for _, v := range h.Array() { @@ -265,7 +265,7 @@ func getFirefoxLoginData(loginJson string) (l []loginData, err error) { user []byte pass []byte ) - m.LoginUrl = v.Get("formSubmitURL").String() + m.LoginURL = v.Get("formSubmitURL").String() user, err = base64.StdEncoding.DecodeString(v.Get("encryptedUsername").String()) if err != nil { return nil, err diff --git a/internal/browser/browser.go b/internal/browser/browser.go index bec0c37b..9dcd5c0e 100644 --- a/internal/browser/browser.go +++ b/internal/browser/browser.go @@ -119,14 +119,13 @@ const ( chromeBetaName = "Chrome Beta" chromiumName = "Chromium" edgeName = "Microsoft Edge" - speed360Name = "360speed" - qqBrowserName = "QQ" braveName = "Brave" operaName = "Opera" operaGXName = "OperaGX" vivaldiName = "Vivaldi" coccocName = "CocCoc" yandexName = "Yandex" - - firefoxName = "Firefox" + firefoxName = "Firefox" + speed360Name = "360speed" + qqBrowserName = "QQ" ) diff --git a/internal/browser/chromium/chromium.go b/internal/browser/chromium/chromium.go index dd0d246d..7f716e6a 100644 --- a/internal/browser/chromium/chromium.go +++ b/internal/browser/chromium/chromium.go @@ -32,7 +32,7 @@ func New(name, storage, profilePath string, items []item.Item) ([]*chromium, err if err != nil { return nil, err } - var chromiumList []*chromium + chromiumList := make([]*chromium, 0, len(multiItemPaths)) for user, itemPaths := range multiItemPaths { chromiumList = append(chromiumList, &chromium{ name: fileutil.BrowserName(name, user), diff --git a/internal/browser/chromium/chromium_darwin.go b/internal/browser/chromium/chromium_darwin.go index ab5e6e2d..e905d647 100644 --- a/internal/browser/chromium/chromium_darwin.go +++ b/internal/browser/chromium/chromium_darwin.go @@ -28,10 +28,9 @@ func (c *chromium) GetMasterKey() ([]byte, error) { ) // don't need chromium key file for macOS defer os.Remove(item.TempChromiumKey) - // defer os.Remove(item.TempChromiumKey) // Get the master key from the keychain // $ security find-generic-password -wa 'Chrome' - cmd = exec.Command("security", "find-generic-password", "-wa", c.storage) + cmd = exec.Command("security", "find-generic-password", "-wa", strings.TrimSpace(c.storage)) //nolint:gosec cmd.Stdout = &stdout cmd.Stderr = &stderr err := cmd.Run() diff --git a/internal/browser/chromium/chromium_linux.go b/internal/browser/chromium/chromium_linux.go index 173971a5..f9893bd1 100644 --- a/internal/browser/chromium/chromium_linux.go +++ b/internal/browser/chromium/chromium_linux.go @@ -32,7 +32,9 @@ func (c *chromium) GetMasterKey() ([]byte, error) { return nil, err } defer func() { - session.Close() + if err := session.Close(); err != nil { + log.Errorf("close session failed: %v", err) + } }() collections, err := svc.GetAllCollections() if err != nil { diff --git a/internal/browser/chromium/chromium_windows.go b/internal/browser/chromium/chromium_windows.go index 0cd68467..a54b0e73 100644 --- a/internal/browser/chromium/chromium_windows.go +++ b/internal/browser/chromium/chromium_windows.go @@ -24,14 +24,14 @@ func (c *chromium) GetMasterKey() ([]byte, error) { } defer os.Remove(keyFile) encryptedKey := gjson.Get(keyFile, "os_crypt.encrypted_key") - if encryptedKey.Exists() { - pureKey, err := base64.StdEncoding.DecodeString(encryptedKey.String()) - if err != nil { - return nil, errDecodeMasterKeyFailed - } - c.masterKey, err = decrypter.DPApi(pureKey[5:]) - log.Infof("%s initialized master key success", c.name) - return c.masterKey, err + if !encryptedKey.Exists() { + return nil, nil } - return nil, nil + pureKey, err := base64.StdEncoding.DecodeString(encryptedKey.String()) + if err != nil { + return nil, errDecodeMasterKeyFailed + } + c.masterKey, err = decrypter.DPAPI(pureKey[5:]) + log.Infof("%s initialized master key success", c.name) + return c.masterKey, err } diff --git a/internal/browser/firefox/firefox.go b/internal/browser/firefox/firefox.go index 69441dc0..fc0dcbd4 100644 --- a/internal/browser/firefox/firefox.go +++ b/internal/browser/firefox/firefox.go @@ -35,7 +35,8 @@ func New(name, storage, profilePath string, items []item.Item) ([]*firefox, erro if err != nil { return nil, err } - var firefoxList []*firefox + + firefoxList := make([]*firefox, 0, len(multiItemPaths)) for name, itemPaths := range multiItemPaths { firefoxList = append(firefoxList, &firefox{ name: fmt.Sprintf("firefox-%s", name), diff --git a/internal/decrypter/decrypter.go b/internal/decrypter/decrypter.go index a00c3f38..415d6814 100644 --- a/internal/decrypter/decrypter.go +++ b/internal/decrypter/decrypter.go @@ -14,11 +14,9 @@ import ( ) var ( - errSecurityKeyIsEmpty = errors.New("input [security find-generic-password -wa 'Chrome'] in terminal") - errPasswordIsEmpty = errors.New("password is empty") - errDecryptFailed = errors.New("decrypt encrypted value failed") - errDecodeASN1Failed = errors.New("decode ASN1 data failed") - errEncryptedLength = errors.New("length of encrypted password less than block size") + errPasswordIsEmpty = errors.New("password is empty") + errDecodeASN1Failed = errors.New("decode ASN1 data failed") + errEncryptedLength = errors.New("length of encrypted password less than block size") ) type ASN1PBE interface { @@ -44,13 +42,13 @@ func NewASN1PBE(b []byte) (pbe ASN1PBE, err error) { } // nssPBE Struct -// SEQUENCE (2 elem) -// SEQUENCE (2 elem) -// OBJECT IDENTIFIER -// SEQUENCE (2 elem) -// OCTET STRING (20 byte) -// INTEGER 1 -// OCTET STRING (16 byte) +// +// 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 @@ -62,10 +60,6 @@ type nssPBE struct { Encrypted []byte } -func (n nssPBE) entrySalt() []byte { - return n.AlgoAttr.SaltAttr.EntrySalt -} - func (n nssPBE) Decrypt(globalSalt, masterPwd []byte) (key []byte, err error) { glmp := append(globalSalt, masterPwd...) hp := sha1.Sum(glmp) @@ -82,26 +76,34 @@ func (n nssPBE) Decrypt(globalSalt, masterPwd []byte) (key []byte, err error) { k2.Write(tkPlus) k := append(k1.Sum(nil), k2.Sum(nil)...) iv := k[len(k)-8:] - return des3Decrypt(k[:24], iv, n.Encrypted) + return des3Decrypt(k[:24], iv, n.encrypted()) +} + +func (n nssPBE) entrySalt() []byte { + return n.AlgoAttr.SaltAttr.EntrySalt +} + +func (n nssPBE) encrypted() []byte { + return n.Encrypted } // MetaPBE Struct -// SEQUENCE (2 elem) -// 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) +// +// 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 @@ -135,8 +137,8 @@ type slatAttr struct { func (m metaPBE) Decrypt(globalSalt, masterPwd []byte) (key2 []byte, err error) { k := sha1.Sum(globalSalt) key := pbkdf2.Key(k[:], m.entrySalt(), m.iterationCount(), m.keySize(), sha256.New) - iv := append([]byte{4, 14}, m.AlgoAttr.Data.IVData.IV...) - return aes128CBCDecrypt(key, iv, m.Encrypted) + iv := append([]byte{4, 14}, m.iv()...) + return aes128CBCDecrypt(key, iv, m.encrypted()) } func (m metaPBE) entrySalt() []byte { @@ -151,13 +153,21 @@ func (m metaPBE) keySize() int { return m.AlgoAttr.Data.Data.SlatAttr.KeySize } +func (m metaPBE) iv() []byte { + return m.AlgoAttr.Data.IVData.IV +} + +func (m metaPBE) encrypted() []byte { + return m.Encrypted +} + // loginPBE Struct -// SEQUENCE (3 elem) -// OCTET STRING (16 byte) -// SEQUENCE (2 elem) -// OBJECT IDENTIFIER -// OCTET STRING (8 byte) -// OCTET STRING (16 byte) +// +// OCTET STRING (16 byte) +// SEQUENCE (2 elem) +// OBJECT IDENTIFIER +// OCTET STRING (8 byte) +// OCTET STRING (16 byte) type loginPBE struct { CipherText []byte Data struct { @@ -168,7 +178,15 @@ type loginPBE struct { } func (l loginPBE) Decrypt(globalSalt, masterPwd []byte) (key []byte, err error) { - return des3Decrypt(globalSalt, l.Data.IV, l.Encrypted) + return des3Decrypt(globalSalt, l.iv(), l.encrypted()) +} + +func (l loginPBE) iv() []byte { + return l.Data.IV +} + +func (l loginPBE) encrypted() []byte { + return l.Encrypted } func aes128CBCDecrypt(key, iv, encryptPass []byte) ([]byte, error) { @@ -197,7 +215,7 @@ func pkcs5UnPadding(src []byte, blockSize int) []byte { return src[:n-paddingNum] } -// des3Decrypt use for decrypter firefox PBE +// des3Decrypt use for decrypt firefox PBE func des3Decrypt(key, iv []byte, src []byte) ([]byte, error) { block, err := des.NewTripleDESCipher(key) if err != nil { @@ -213,10 +231,9 @@ func paddingZero(s []byte, l int) []byte { h := l - len(s) if h <= 0 { return s - } else { - for i := len(s); i < l; i++ { - s = append(s, 0) - } - return s } + for i := len(s); i < l; i++ { + s = append(s, 0) + } + return s } diff --git a/internal/decrypter/decrypter_darwin.go b/internal/decrypter/decrypter_darwin.go index d003e121..78c874d5 100644 --- a/internal/decrypter/decrypter_darwin.go +++ b/internal/decrypter/decrypter_darwin.go @@ -1,17 +1,21 @@ package decrypter +var ( + errSecurityKeyIsEmpty = errors.New("input [security find-generic-password -wa 'Chrome'] in terminal") +) + func Chromium(key, encryptPass []byte) ([]byte, error) { - if len(encryptPass) > 3 { - if len(key) == 0 { - return nil, errSecurityKeyIsEmpty - } - chromeIV := []byte{32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32} - return aes128CBCDecrypt(key, chromeIV, encryptPass[3:]) - } else { - return nil, errDecryptFailed + if len(encryptPass) <= 3 { + return nil, errPasswordIsEmpty + } + if len(key) == 0 { + return nil, errSecurityKeyIsEmpty } + + iv := []byte{32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32} + return aes128CBCDecrypt(key, iv, encryptPass[3:]) } -func DPApi(data []byte) ([]byte, error) { +func DPAPI(data []byte) ([]byte, error) { return nil, nil } diff --git a/internal/decrypter/decrypter_linux.go b/internal/decrypter/decrypter_linux.go index ccd5866e..715f610f 100644 --- a/internal/decrypter/decrypter_linux.go +++ b/internal/decrypter/decrypter_linux.go @@ -1,17 +1,14 @@ package decrypter func Chromium(key, encryptPass []byte) ([]byte, error) { - chromeIV := []byte{32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32} - if len(encryptPass) > 3 { - if len(key) == 0 { - return nil, errSecurityKeyIsEmpty - } - return aes128CBCDecrypt(key, chromeIV, encryptPass[3:]) - } else { - return nil, errDecryptFailed + if len(encryptPass) < 3 { + return nil, errPasswordIsEmpty } + + chromeIV := []byte{32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32} + return aes128CBCDecrypt(key, chromeIV, encryptPass[3:]) } -func DPApi(data []byte) ([]byte, error) { +func DPAPI(data []byte) ([]byte, error) { return nil, nil } diff --git a/internal/decrypter/decrypter_windows.go b/internal/decrypter/decrypter_windows.go index 25692496..8c17f8c4 100644 --- a/internal/decrypter/decrypter_windows.go +++ b/internal/decrypter/decrypter_windows.go @@ -8,25 +8,29 @@ import ( ) func Chromium(key, encryptPass []byte) ([]byte, error) { - if len(encryptPass) > 15 { - // remove Prefix 'v10' - return aesGCMDecrypt(encryptPass[15:], key, encryptPass[3:15]) - } else { + if len(encryptPass) < 3 { return nil, errPasswordIsEmpty } + if len(key) == 0 { + return nil, errSecurityKeyIsEmpty + } + + return aesGCMDecrypt(encryptPass[15:], key, encryptPass[3:15]) } func ChromiumForYandex(key, encryptPass []byte) ([]byte, error) { - if len(encryptPass) > 15 { - // remove Prefix 'v10' - // gcmBlockSize = 16 - // gcmTagSize = 16 - // gcmMinimumTagSize = 12 // NIST SP 800-38D recommends tags with 12 or more bytes. - // gcmStandardNonceSize = 12 - return aesGCMDecrypt(encryptPass[12:], key, encryptPass[0:12]) - } else { + if len(encryptPass) < 3 { return nil, errPasswordIsEmpty } + if len(key) == 0 { + return nil, errSecurityKeyIsEmpty + } + // remove Prefix 'v10' + // gcmBlockSize = 16 + // gcmTagSize = 16 + // gcmMinimumTagSize = 12 // NIST SP 800-38D recommends tags with 12 or more bytes. + // gcmStandardNonceSize = 12 + return aesGCMDecrypt(encryptPass[12:], key, encryptPass[0:12]) } // chromium > 80 https://source.chromium.org/chromium/chromium/src/+/master:components/os_crypt/os_crypt_win.cc @@ -67,9 +71,12 @@ func (b *dataBlob) ToByteArray() []byte { return d } -// DPApi +// DPAPI (Data Protection Application Programming Interface) +// is a simple cryptographic application programming interface +// available as a built-in component in Windows 2000 and +// later versions of Microsoft Windows operating systems // chrome < 80 https://chromium.googlesource.com/chromium/src/+/76f496a7235c3432983421402951d73905c8be96/components/os_crypt/os_crypt_win.cc#82 -func DPApi(data []byte) ([]byte, error) { +func DPAPI(data []byte) ([]byte, error) { dllCrypt := syscall.NewLazyDLL("Crypt32.dll") dllKernel := syscall.NewLazyDLL("Kernel32.dll") procDecryptData := dllCrypt.NewProc("CryptUnprotectData") diff --git a/internal/utils/fileutil/filetutil.go b/internal/utils/fileutil/filetutil.go index 801557e5..1a94ce59 100644 --- a/internal/utils/fileutil/filetutil.go +++ b/internal/utils/fileutil/filetutil.go @@ -5,7 +5,6 @@ import ( "bytes" "errors" "fmt" - "io/ioutil" "os" "path" "path/filepath" @@ -55,7 +54,7 @@ func FilesInFolder(dir, filename string) ([]string, error) { // ReadFile reads the file from the provided path func ReadFile(filename string) (string, error) { - s, err := ioutil.ReadFile(filename) + s, err := os.ReadFile(filename) return string(s), err } @@ -71,20 +70,20 @@ func CopyDir(src, dst, skip string) error { // CopyDirHasSuffix copies the directory from the source to the destination // contain is the file if you want to copy, and rename copied filename with dir/index_filename func CopyDirHasSuffix(src, dst, suffix string) error { - var filelist []string + var files []string err := filepath.Walk(src, func(path string, f os.FileInfo, err error) error { if !f.IsDir() && strings.HasSuffix(strings.ToLower(f.Name()), suffix) { - filelist = append(filelist, path) + files = append(files, path) } return err }) if err != nil { return err } - if err := os.MkdirAll(dst, 0o755); err != nil { + if err := os.MkdirAll(dst, 0o700); err != nil { return err } - for index, file := range filelist { + for index, file := range files { // p = dir/index_file p := fmt.Sprintf("%s/%d_%s", dst, index, BaseDir(file)) err = CopyFile(file, p) @@ -97,20 +96,19 @@ func CopyDirHasSuffix(src, dst, suffix string) error { // CopyFile copies the file from the source to the destination func CopyFile(src, dst string) error { - // TODO: Handle read file error - d, err := ioutil.ReadFile(src) + s, err := os.ReadFile(src) if err != nil { return err } - err = ioutil.WriteFile(dst, d, 0o777) + err = os.WriteFile(dst, s, 0o600) if err != nil { return err } return nil } -// Filename returns the filename from the provided path -func Filename(browser, item, ext string) string { +// ItemName returns the filename from the provided path +func ItemName(browser, item, ext string) string { replace := strings.NewReplacer(" ", "_", ".", "_", "-", "_") return strings.ToLower(fmt.Sprintf("%s_%s.%s", replace.Replace(browser), item, ext)) } @@ -137,26 +135,27 @@ func ParentBaseDir(p string) string { // CompressDir compresses the directory into a zip file func CompressDir(dir string) error { - files, err := ioutil.ReadDir(dir) + files, err := os.ReadDir(dir) if err != nil { return err } b := new(bytes.Buffer) zw := zip.NewWriter(b) for _, f := range files { - fw, _ := zw.Create(f.Name()) - fileName := path.Join(dir, f.Name()) - fileContent, err := ioutil.ReadFile(fileName) + fw, err := zw.Create(f.Name()) + if err != nil { + return err + } + name := path.Join(dir, f.Name()) + content, err := os.ReadFile(name) if err != nil { - zw.Close() return err } - _, err = fw.Write(fileContent) + _, err = fw.Write(content) if err != nil { - zw.Close() return err } - err = os.Remove(fileName) + err = os.Remove(name) if err != nil { return err } @@ -165,7 +164,7 @@ func CompressDir(dir string) error { return err } filename := filepath.Join(dir, fmt.Sprintf("%s.zip", dir)) - outFile, err := os.Create(filename) + outFile, err := os.Create(filepath.Clean(filename)) if err != nil { return err } @@ -173,5 +172,5 @@ func CompressDir(dir string) error { if err != nil { return err } - return nil + return outFile.Close() } diff --git a/internal/utils/typeutil/typeutil_test.go b/internal/utils/typeutil/typeutil_test.go index 7d5dc283..68e0aeb6 100644 --- a/internal/utils/typeutil/typeutil_test.go +++ b/internal/utils/typeutil/typeutil_test.go @@ -4,13 +4,15 @@ import ( "testing" ) -var reverseTestCases = [][]any{ - {1, 2, 3, 4, 5}, - {"1", "2", "3", "4", "5"}, - {"1", 2, "3", "4", 5}, -} - func TestReverse(t *testing.T) { + t.Parallel() + + reverseTestCases := [][]any{ + {1, 2, 3, 4, 5}, + {"1", "2", "3", "4", "5"}, + {"1", 2, "3", "4", 5}, + } + for _, ts := range reverseTestCases { h := Reverse(ts) for i := 0; i < len(ts); i++ {