From c6c30c15a2614722d7fcf5902e60cabc314d9ca5 Mon Sep 17 00:00:00 2001 From: kalifun Date: Wed, 8 Feb 2023 11:54:08 +0800 Subject: [PATCH 1/7] :construction: Mac edge support Directory structure is similar to chrome --- browser/edge/find.go | 46 ++++++++-------------- go.mod | 3 -- go.sum | 62 ++---------------------------- internal/edge/find/find.go | 5 +++ internal/edge/find/find_darwin.go | 18 +++++++++ internal/edge/find/find_others.go | 9 +++++ internal/edge/find/find_unix.go | 12 ++++++ internal/edge/find/find_windows.go | 24 ++++++++++++ 8 files changed, 86 insertions(+), 93 deletions(-) create mode 100644 internal/edge/find/find.go create mode 100644 internal/edge/find/find_darwin.go create mode 100644 internal/edge/find/find_others.go create mode 100644 internal/edge/find/find_unix.go create mode 100644 internal/edge/find/find_windows.go diff --git a/browser/edge/find.go b/browser/edge/find.go index 34c038e..d07c1b7 100644 --- a/browser/edge/find.go +++ b/browser/edge/find.go @@ -3,15 +3,11 @@ package edge import ( - "errors" - "os" - "path/filepath" - "github.com/zellyn/kooky" "github.com/zellyn/kooky/internal/chrome" "github.com/zellyn/kooky/internal/chrome/find" "github.com/zellyn/kooky/internal/cookies" - "github.com/zellyn/kooky/internal/ie" + edgefind "github.com/zellyn/kooky/internal/edge/find" _ "github.com/zellyn/kooky/internal/ie/find" ) @@ -26,41 +22,29 @@ func init() { } func (f *edgeFinder) FindCookieStores() ([]kooky.CookieStore, error) { - locApp := os.Getenv(`LocalAppData`) - if len(locApp) == 0 { - return nil, errors.New(`%LocalAppData% is empty`) - } - - var cookiesFiles []kooky.CookieStore - - // Blink based - newRoot := func() ([]string, error) { - return []string{filepath.Join(locApp, `Microsoft`, `Edge`, `User Data`)}, nil - } - blinkCookiesFiles, err := find.FindCookieStoreFiles(newRoot, `edge`) + files, err := find.FindCookieStoreFiles(edgefind.GetEdgeRoots(), `edge`) if err != nil { return nil, err } - for _, cookiesFile := range blinkCookiesFiles { - cookiesFiles = append( - cookiesFiles, + + var ret []kooky.CookieStore + for _, file := range files { + ret = append( + ret, &cookies.CookieJar{ - CookieStore: &ie.CookieStore{ - CookieStore: &chrome.CookieStore{ - DefaultCookieStore: cookies.DefaultCookieStore{ - BrowserStr: cookiesFile.Browser, - ProfileStr: cookiesFile.Profile, - OSStr: cookiesFile.OS, - IsDefaultProfileBool: cookiesFile.IsDefaultProfile, - FileNameStr: cookiesFile.Path, - }, + CookieStore: &chrome.CookieStore{ + DefaultCookieStore: cookies.DefaultCookieStore{ + BrowserStr: file.Browser, + ProfileStr: file.Profile, + OSStr: file.OS, + IsDefaultProfileBool: file.IsDefaultProfile, + FileNameStr: file.Path, }, }, }, ) } - - return cookiesFiles, nil + return ret, nil } /* diff --git a/go.mod b/go.mod index 8a375b2..cbd8d62 100644 --- a/go.mod +++ b/go.mod @@ -21,9 +21,6 @@ require ( github.com/Velocidex/json v0.0.0-20220224052537-92f3c0326e5a // indirect github.com/Velocidex/yaml/v2 v2.2.8 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/godbus/dbus v4.1.0+incompatible // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/gonuts/binary v0.2.0 // indirect - github.com/smartystreets/goconvey v1.6.4 // indirect - gopkg.in/ini.v1 v1.62.0 // indirect ) diff --git a/go.sum b/go.sum index 5f1ad80..f474768 100644 --- a/go.sum +++ b/go.sum @@ -1,118 +1,62 @@ github.com/Velocidex/json v0.0.0-20220224052537-92f3c0326e5a h1:AeXPUzhU0yhID/v5JJEIkjaE85ASe+Vh4Kuv1RSLL+4= github.com/Velocidex/json v0.0.0-20220224052537-92f3c0326e5a/go.mod h1:ukJBuruT9b24pdgZwWDvOaCYHeS03B7oQPCUWh25bwM= github.com/Velocidex/ordereddict v0.0.0-20191106020901-97c468e5e403/go.mod h1:pxJpvN5ISMtDwrdIdqnJ3ZrjIngCw+WT6gfNil6Zjvo= -github.com/Velocidex/ordereddict v0.0.0-20220107075049-3dbe58412844 h1:mzLkFvHqUgpZJ6M+TF4oCybAvkEEZxkQBH71BDeyHW4= -github.com/Velocidex/ordereddict v0.0.0-20220107075049-3dbe58412844/go.mod h1:Y5Tfx5SKGOzkulpqfonrdILSPIuNg+GqKE/DhVJgnpg= github.com/Velocidex/ordereddict v0.0.0-20220411103415-79032cf99b1d h1:XnifmnLRjinjYzZgbog7LQr2XbmEFI81kuiufpn+JUQ= github.com/Velocidex/ordereddict v0.0.0-20220411103415-79032cf99b1d/go.mod h1:XJDUbaGh2U9e0z78L5O2OXf1hE1wSxnJ7nSlQmA+bIs= github.com/Velocidex/yaml/v2 v2.2.8 h1:GUrSy4SBJ6RjGt43k6MeBKtw2z/27gh4A3hfFmFY3No= github.com/Velocidex/yaml/v2 v2.2.8/go.mod h1:PlXIg/Pxmoja48C1vMHo7C5pauAZvLq/UEPOQ3DsjS4= github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ= +github.com/alecthomas/repr v0.0.0-20210801044451-80ca428c5142 h1:8Uy0oSf5co/NZXje7U1z8Mpep++QJOldL2hs/sBQf48= github.com/alecthomas/repr v0.0.0-20210801044451-80ca428c5142/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= github.com/bobesa/go-domain-util v0.0.0-20190911083921-4033b5f7dd89 h1:2pkAuIM8OF1fy4ToFpMnI4oE+VeUNRbGrpSLKshK0oQ= github.com/bobesa/go-domain-util v0.0.0-20190911083921-4033b5f7dd89/go.mod h1:/09nEjna1UMoasyyQDhOrIn8hi2v2kiJglPWed1idck= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/danieljoos/wincred v1.0.2/go.mod h1:SnuYRW9lp1oJrZX/dXJqr0cPK5gYXqx3EJbmjhLdK9U= github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7hqDjlFjiygg= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/go-ini/ini v1.62.0 h1:7VJT/ZXjzqSrvtraFp4ONq80hTcRQth1c9ZnQ3uNQvU= -github.com/go-ini/ini v1.62.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-ini/ini v1.66.4 h1:dKjMqkcbkzfddhIhyglTPgMoJnkvmG+bSLrU9cTHc5M= github.com/go-ini/ini v1.66.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-sqlite/sqlite3 v0.0.0-20180313105335-53dd8e640ee7 h1:ow5vK9Q/DSKkxbEIJHBST6g+buBDwdaDIyk1dGGwpQo= github.com/go-sqlite/sqlite3 v0.0.0-20180313105335-53dd8e640ee7/go.mod h1:JxSQ+SvsjFb+p8Y+bn+GhTkiMfKVGBD0fq43ms2xw04= -github.com/godbus/dbus v4.1.0+incompatible h1:WqqLRTsQic3apZUK9qC5sGNfXthmPXzUZ7nQPrNITa4= -github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gonuts/binary v0.2.0 h1:caITwMWAoQWlL0RNvv2lTU/AHqAJlVuu6nZmNgfbKW4= github.com/gonuts/binary v0.2.0/go.mod h1:kM+CtBrCGDSKdv8WXTuCUsw+loiy8f/QEI8YCCC0M/E= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/keybase/go-keychain v0.0.0-20200502122510-cda31fe0c86d h1:gVjhBCfVGl32RIBooOANzfw+0UqX8HU+yPlMv8vypcg= -github.com/keybase/go-keychain v0.0.0-20200502122510-cda31fe0c86d/go.mod h1:W6EbaYmb4RldPn0N3gvVHjY1wmU59kbymhW9NATWhwY= github.com/keybase/go-keychain v0.0.0-20220408132150-ad3b4a8fd4a7 h1:ttxQhWhqiYEOVLMhmhIRQnZDLmYaBJVP7goucV3FJxM= github.com/keybase/go-keychain v0.0.0-20220408132150-ad3b4a8fd4a7/go.mod h1:enrU/ug069Om7vWxuFE6nikLI2BZNwevMiGSo43Kt5w= -github.com/keybase/go.dbus v0.0.0-20200324223359-a94be52c0b03/go.mod h1:a8clEhrrGV/d76/f9r2I41BwANMihfZYV9C223vaxqE= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/zalando/go-keyring v0.1.0 h1:ffq972Aoa4iHNzBlUHgK5Y+k8+r/8GvcGd80/OFZb/k= -github.com/zalando/go-keyring v0.1.0/go.mod h1:RaxNwUITJaHVdQ0VC7pELPZ3tOWn13nr0gZMZEhpVU0= github.com/zalando/go-keyring v0.2.1 h1:MBRN/Z8H4U5wEKXiD67YbDAr5cj/DOStmSga70/2qKc= github.com/zalando/go-keyring v0.2.1/go.mod h1:g63M2PPn0w5vjmEbwAX3ib5I+41zdm4esSETOn9Y6Dw= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E= -golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20220408190544-5352b0902921 h1:iU7T1X1J6yxDr0rda54sWGkHgOp5XJrqm79gcNlC2VM= golang.org/x/crypto v0.0.0-20220408190544-5352b0902921/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102 h1:42cLlJJdEh+ySyeUUbEQ5bsTiq8voBeTuweGVkY6Puw= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20220407224826-aac1ed45d8e3 h1:EN5+DfgmRMvRUrMGERW2gQl3Vc+Z7ZMnI/xdEpPSf0c= golang.org/x/net v0.0.0-20220407224826-aac1ed45d8e3/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201107080550-4d91cf3a1aaf h1:kt3wY1Lu5MJAnKTfoMR52Cu4gwvna4VTzNOiT8tY73s= -golang.org/x/sys v0.0.0-20201107080550-4d91cf3a1aaf/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f h1:8w7RhxzTVgUzw/AH/9mUV5q0vMgy40SQRursCcfmkCw= golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= -gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= www.velocidex.com/golang/go-ese v0.1.0 h1:LObdPh6uoAAbz50MqF4WdJHAn9+Fcr/9kyW0fMsjxlc= www.velocidex.com/golang/go-ese v0.1.0/go.mod h1:d3PHzQhyhe+AO9RYBnDKZ40As15T+38zr++Dnv4ufuc= diff --git a/internal/edge/find/find.go b/internal/edge/find/find.go new file mode 100644 index 0000000..2cf158a --- /dev/null +++ b/internal/edge/find/find.go @@ -0,0 +1,5 @@ +package find + +func GetEdgeRoots() (rootsFunc func() ([]string, error)) { + return edgeRoots +} diff --git a/internal/edge/find/find_darwin.go b/internal/edge/find/find_darwin.go new file mode 100644 index 0000000..d7f2c61 --- /dev/null +++ b/internal/edge/find/find_darwin.go @@ -0,0 +1,18 @@ +//go:build darwin + +package find + +import ( + "errors" + "os" + "path/filepath" +) + +func edgeRoots() ([]string, error) { + // "$HOME/Library/Application Support" + cfgDir, err := os.UserConfigDir() + if err != nil { + return nil, err + } + return []string{filepath.Join(cfgDir, `Microsoft Edge`)}, nil +} diff --git a/internal/edge/find/find_others.go b/internal/edge/find/find_others.go new file mode 100644 index 0000000..d4432b1 --- /dev/null +++ b/internal/edge/find/find_others.go @@ -0,0 +1,9 @@ +//go:build plan9 || android || ios || js || aix + +package find + +import "errors" + +func edgeRoots() ([]string, error) { + return nil, errors.New(`not implemented`) +} diff --git a/internal/edge/find/find_unix.go b/internal/edge/find/find_unix.go new file mode 100644 index 0000000..e33c94c --- /dev/null +++ b/internal/edge/find/find_unix.go @@ -0,0 +1,12 @@ +//go:build !windows && !darwin && !plan9 && !android && !js && !aix + +package find + +import ( + "os" + "path/filepath" +) + +func edgeRoots() ([]string, error) { + return nil, errors.New(`not implemented`) +} diff --git a/internal/edge/find/find_windows.go b/internal/edge/find/find_windows.go new file mode 100644 index 0000000..a29bcec --- /dev/null +++ b/internal/edge/find/find_windows.go @@ -0,0 +1,24 @@ +//go:build windows +// +build windows + +package find + +import ( + "errors" + "os" + "path/filepath" +) + +func edgeRoots() ([]string, error) { + // AppData Local + locApp := os.Getenv(`LocalAppData`) + if len(locApp) == 0 { + return nil, errors.New(`%LocalAppData% is empty`) + } + + var ret = []string{ + filepath.Join(locApp, `Microsoft`, `Edge`, `User Data`), + } + + return ret, nil +} From 0e5bb316238b29f3bec6224e72664c81bbf162fe Mon Sep 17 00:00:00 2001 From: kalifun Date: Wed, 8 Feb 2023 11:54:08 +0800 Subject: [PATCH 2/7] :construction: Mac edge support Directory structure is similar to chrome --- browser/edge/find.go | 46 ++++++++-------------- go.mod | 3 -- go.sum | 62 ++---------------------------- internal/edge/find/find.go | 5 +++ internal/edge/find/find_darwin.go | 18 +++++++++ internal/edge/find/find_others.go | 9 +++++ internal/edge/find/find_unix.go | 12 ++++++ internal/edge/find/find_windows.go | 24 ++++++++++++ 8 files changed, 86 insertions(+), 93 deletions(-) create mode 100644 internal/edge/find/find.go create mode 100644 internal/edge/find/find_darwin.go create mode 100644 internal/edge/find/find_others.go create mode 100644 internal/edge/find/find_unix.go create mode 100644 internal/edge/find/find_windows.go diff --git a/browser/edge/find.go b/browser/edge/find.go index 34c038e..d07c1b7 100644 --- a/browser/edge/find.go +++ b/browser/edge/find.go @@ -3,15 +3,11 @@ package edge import ( - "errors" - "os" - "path/filepath" - "github.com/zellyn/kooky" "github.com/zellyn/kooky/internal/chrome" "github.com/zellyn/kooky/internal/chrome/find" "github.com/zellyn/kooky/internal/cookies" - "github.com/zellyn/kooky/internal/ie" + edgefind "github.com/zellyn/kooky/internal/edge/find" _ "github.com/zellyn/kooky/internal/ie/find" ) @@ -26,41 +22,29 @@ func init() { } func (f *edgeFinder) FindCookieStores() ([]kooky.CookieStore, error) { - locApp := os.Getenv(`LocalAppData`) - if len(locApp) == 0 { - return nil, errors.New(`%LocalAppData% is empty`) - } - - var cookiesFiles []kooky.CookieStore - - // Blink based - newRoot := func() ([]string, error) { - return []string{filepath.Join(locApp, `Microsoft`, `Edge`, `User Data`)}, nil - } - blinkCookiesFiles, err := find.FindCookieStoreFiles(newRoot, `edge`) + files, err := find.FindCookieStoreFiles(edgefind.GetEdgeRoots(), `edge`) if err != nil { return nil, err } - for _, cookiesFile := range blinkCookiesFiles { - cookiesFiles = append( - cookiesFiles, + + var ret []kooky.CookieStore + for _, file := range files { + ret = append( + ret, &cookies.CookieJar{ - CookieStore: &ie.CookieStore{ - CookieStore: &chrome.CookieStore{ - DefaultCookieStore: cookies.DefaultCookieStore{ - BrowserStr: cookiesFile.Browser, - ProfileStr: cookiesFile.Profile, - OSStr: cookiesFile.OS, - IsDefaultProfileBool: cookiesFile.IsDefaultProfile, - FileNameStr: cookiesFile.Path, - }, + CookieStore: &chrome.CookieStore{ + DefaultCookieStore: cookies.DefaultCookieStore{ + BrowserStr: file.Browser, + ProfileStr: file.Profile, + OSStr: file.OS, + IsDefaultProfileBool: file.IsDefaultProfile, + FileNameStr: file.Path, }, }, }, ) } - - return cookiesFiles, nil + return ret, nil } /* diff --git a/go.mod b/go.mod index 8a375b2..cbd8d62 100644 --- a/go.mod +++ b/go.mod @@ -21,9 +21,6 @@ require ( github.com/Velocidex/json v0.0.0-20220224052537-92f3c0326e5a // indirect github.com/Velocidex/yaml/v2 v2.2.8 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/godbus/dbus v4.1.0+incompatible // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/gonuts/binary v0.2.0 // indirect - github.com/smartystreets/goconvey v1.6.4 // indirect - gopkg.in/ini.v1 v1.62.0 // indirect ) diff --git a/go.sum b/go.sum index 5f1ad80..f474768 100644 --- a/go.sum +++ b/go.sum @@ -1,118 +1,62 @@ github.com/Velocidex/json v0.0.0-20220224052537-92f3c0326e5a h1:AeXPUzhU0yhID/v5JJEIkjaE85ASe+Vh4Kuv1RSLL+4= github.com/Velocidex/json v0.0.0-20220224052537-92f3c0326e5a/go.mod h1:ukJBuruT9b24pdgZwWDvOaCYHeS03B7oQPCUWh25bwM= github.com/Velocidex/ordereddict v0.0.0-20191106020901-97c468e5e403/go.mod h1:pxJpvN5ISMtDwrdIdqnJ3ZrjIngCw+WT6gfNil6Zjvo= -github.com/Velocidex/ordereddict v0.0.0-20220107075049-3dbe58412844 h1:mzLkFvHqUgpZJ6M+TF4oCybAvkEEZxkQBH71BDeyHW4= -github.com/Velocidex/ordereddict v0.0.0-20220107075049-3dbe58412844/go.mod h1:Y5Tfx5SKGOzkulpqfonrdILSPIuNg+GqKE/DhVJgnpg= github.com/Velocidex/ordereddict v0.0.0-20220411103415-79032cf99b1d h1:XnifmnLRjinjYzZgbog7LQr2XbmEFI81kuiufpn+JUQ= github.com/Velocidex/ordereddict v0.0.0-20220411103415-79032cf99b1d/go.mod h1:XJDUbaGh2U9e0z78L5O2OXf1hE1wSxnJ7nSlQmA+bIs= github.com/Velocidex/yaml/v2 v2.2.8 h1:GUrSy4SBJ6RjGt43k6MeBKtw2z/27gh4A3hfFmFY3No= github.com/Velocidex/yaml/v2 v2.2.8/go.mod h1:PlXIg/Pxmoja48C1vMHo7C5pauAZvLq/UEPOQ3DsjS4= github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ= +github.com/alecthomas/repr v0.0.0-20210801044451-80ca428c5142 h1:8Uy0oSf5co/NZXje7U1z8Mpep++QJOldL2hs/sBQf48= github.com/alecthomas/repr v0.0.0-20210801044451-80ca428c5142/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= github.com/bobesa/go-domain-util v0.0.0-20190911083921-4033b5f7dd89 h1:2pkAuIM8OF1fy4ToFpMnI4oE+VeUNRbGrpSLKshK0oQ= github.com/bobesa/go-domain-util v0.0.0-20190911083921-4033b5f7dd89/go.mod h1:/09nEjna1UMoasyyQDhOrIn8hi2v2kiJglPWed1idck= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/danieljoos/wincred v1.0.2/go.mod h1:SnuYRW9lp1oJrZX/dXJqr0cPK5gYXqx3EJbmjhLdK9U= github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7hqDjlFjiygg= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/go-ini/ini v1.62.0 h1:7VJT/ZXjzqSrvtraFp4ONq80hTcRQth1c9ZnQ3uNQvU= -github.com/go-ini/ini v1.62.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-ini/ini v1.66.4 h1:dKjMqkcbkzfddhIhyglTPgMoJnkvmG+bSLrU9cTHc5M= github.com/go-ini/ini v1.66.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-sqlite/sqlite3 v0.0.0-20180313105335-53dd8e640ee7 h1:ow5vK9Q/DSKkxbEIJHBST6g+buBDwdaDIyk1dGGwpQo= github.com/go-sqlite/sqlite3 v0.0.0-20180313105335-53dd8e640ee7/go.mod h1:JxSQ+SvsjFb+p8Y+bn+GhTkiMfKVGBD0fq43ms2xw04= -github.com/godbus/dbus v4.1.0+incompatible h1:WqqLRTsQic3apZUK9qC5sGNfXthmPXzUZ7nQPrNITa4= -github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gonuts/binary v0.2.0 h1:caITwMWAoQWlL0RNvv2lTU/AHqAJlVuu6nZmNgfbKW4= github.com/gonuts/binary v0.2.0/go.mod h1:kM+CtBrCGDSKdv8WXTuCUsw+loiy8f/QEI8YCCC0M/E= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/keybase/go-keychain v0.0.0-20200502122510-cda31fe0c86d h1:gVjhBCfVGl32RIBooOANzfw+0UqX8HU+yPlMv8vypcg= -github.com/keybase/go-keychain v0.0.0-20200502122510-cda31fe0c86d/go.mod h1:W6EbaYmb4RldPn0N3gvVHjY1wmU59kbymhW9NATWhwY= github.com/keybase/go-keychain v0.0.0-20220408132150-ad3b4a8fd4a7 h1:ttxQhWhqiYEOVLMhmhIRQnZDLmYaBJVP7goucV3FJxM= github.com/keybase/go-keychain v0.0.0-20220408132150-ad3b4a8fd4a7/go.mod h1:enrU/ug069Om7vWxuFE6nikLI2BZNwevMiGSo43Kt5w= -github.com/keybase/go.dbus v0.0.0-20200324223359-a94be52c0b03/go.mod h1:a8clEhrrGV/d76/f9r2I41BwANMihfZYV9C223vaxqE= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/zalando/go-keyring v0.1.0 h1:ffq972Aoa4iHNzBlUHgK5Y+k8+r/8GvcGd80/OFZb/k= -github.com/zalando/go-keyring v0.1.0/go.mod h1:RaxNwUITJaHVdQ0VC7pELPZ3tOWn13nr0gZMZEhpVU0= github.com/zalando/go-keyring v0.2.1 h1:MBRN/Z8H4U5wEKXiD67YbDAr5cj/DOStmSga70/2qKc= github.com/zalando/go-keyring v0.2.1/go.mod h1:g63M2PPn0w5vjmEbwAX3ib5I+41zdm4esSETOn9Y6Dw= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E= -golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20220408190544-5352b0902921 h1:iU7T1X1J6yxDr0rda54sWGkHgOp5XJrqm79gcNlC2VM= golang.org/x/crypto v0.0.0-20220408190544-5352b0902921/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102 h1:42cLlJJdEh+ySyeUUbEQ5bsTiq8voBeTuweGVkY6Puw= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20220407224826-aac1ed45d8e3 h1:EN5+DfgmRMvRUrMGERW2gQl3Vc+Z7ZMnI/xdEpPSf0c= golang.org/x/net v0.0.0-20220407224826-aac1ed45d8e3/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201107080550-4d91cf3a1aaf h1:kt3wY1Lu5MJAnKTfoMR52Cu4gwvna4VTzNOiT8tY73s= -golang.org/x/sys v0.0.0-20201107080550-4d91cf3a1aaf/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f h1:8w7RhxzTVgUzw/AH/9mUV5q0vMgy40SQRursCcfmkCw= golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= -gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= www.velocidex.com/golang/go-ese v0.1.0 h1:LObdPh6uoAAbz50MqF4WdJHAn9+Fcr/9kyW0fMsjxlc= www.velocidex.com/golang/go-ese v0.1.0/go.mod h1:d3PHzQhyhe+AO9RYBnDKZ40As15T+38zr++Dnv4ufuc= diff --git a/internal/edge/find/find.go b/internal/edge/find/find.go new file mode 100644 index 0000000..2cf158a --- /dev/null +++ b/internal/edge/find/find.go @@ -0,0 +1,5 @@ +package find + +func GetEdgeRoots() (rootsFunc func() ([]string, error)) { + return edgeRoots +} diff --git a/internal/edge/find/find_darwin.go b/internal/edge/find/find_darwin.go new file mode 100644 index 0000000..d7f2c61 --- /dev/null +++ b/internal/edge/find/find_darwin.go @@ -0,0 +1,18 @@ +//go:build darwin + +package find + +import ( + "errors" + "os" + "path/filepath" +) + +func edgeRoots() ([]string, error) { + // "$HOME/Library/Application Support" + cfgDir, err := os.UserConfigDir() + if err != nil { + return nil, err + } + return []string{filepath.Join(cfgDir, `Microsoft Edge`)}, nil +} diff --git a/internal/edge/find/find_others.go b/internal/edge/find/find_others.go new file mode 100644 index 0000000..d4432b1 --- /dev/null +++ b/internal/edge/find/find_others.go @@ -0,0 +1,9 @@ +//go:build plan9 || android || ios || js || aix + +package find + +import "errors" + +func edgeRoots() ([]string, error) { + return nil, errors.New(`not implemented`) +} diff --git a/internal/edge/find/find_unix.go b/internal/edge/find/find_unix.go new file mode 100644 index 0000000..e33c94c --- /dev/null +++ b/internal/edge/find/find_unix.go @@ -0,0 +1,12 @@ +//go:build !windows && !darwin && !plan9 && !android && !js && !aix + +package find + +import ( + "os" + "path/filepath" +) + +func edgeRoots() ([]string, error) { + return nil, errors.New(`not implemented`) +} diff --git a/internal/edge/find/find_windows.go b/internal/edge/find/find_windows.go new file mode 100644 index 0000000..a29bcec --- /dev/null +++ b/internal/edge/find/find_windows.go @@ -0,0 +1,24 @@ +//go:build windows +// +build windows + +package find + +import ( + "errors" + "os" + "path/filepath" +) + +func edgeRoots() ([]string, error) { + // AppData Local + locApp := os.Getenv(`LocalAppData`) + if len(locApp) == 0 { + return nil, errors.New(`%LocalAppData% is empty`) + } + + var ret = []string{ + filepath.Join(locApp, `Microsoft`, `Edge`, `User Data`), + } + + return ret, nil +} From b59e092d48ff93b8a18df56a5389b069e34f4947 Mon Sep 17 00:00:00 2001 From: kalifun Date: Wed, 8 Feb 2023 20:52:28 +0800 Subject: [PATCH 3/7] =?UTF-8?q?=F0=9F=9A=A8=20remove=20error?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/edge/find/find_darwin.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/edge/find/find_darwin.go b/internal/edge/find/find_darwin.go index d7f2c61..f8c8f82 100644 --- a/internal/edge/find/find_darwin.go +++ b/internal/edge/find/find_darwin.go @@ -3,7 +3,6 @@ package find import ( - "errors" "os" "path/filepath" ) From 44e9e566c4ce28b1233f6deaaca06cfc28a98c1d Mon Sep 17 00:00:00 2001 From: kalifun Date: Wed, 8 Feb 2023 23:02:11 +0800 Subject: [PATCH 4/7] =?UTF-8?q?=F0=9F=92=A5=20Implementation=20of=20reuse?= =?UTF-8?q?=20of=20chrome?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Because the keychain was written to death, I don't want to destroy the original code. --- browser/edge/edge.go | 6 +- browser/edge/find.go | 20 +- internal/edge/cookiestore.go | 26 +++ internal/edge/edge.go | 330 +++++++++++++++++++++++++++++++ internal/edge/edge_darwin_cgo.go | 38 ++++ internal/edge/edge_notwindows.go | 16 ++ 6 files changed, 422 insertions(+), 14 deletions(-) create mode 100644 internal/edge/cookiestore.go create mode 100644 internal/edge/edge.go create mode 100644 internal/edge/edge_darwin_cgo.go create mode 100644 internal/edge/edge_notwindows.go diff --git a/browser/edge/edge.go b/browser/edge/edge.go index ca5a40b..53d858d 100644 --- a/browser/edge/edge.go +++ b/browser/edge/edge.go @@ -9,8 +9,8 @@ import ( "os" "github.com/zellyn/kooky" - "github.com/zellyn/kooky/internal/chrome" "github.com/zellyn/kooky/internal/cookies" + "github.com/zellyn/kooky/internal/edge" "github.com/zellyn/kooky/internal/ie" ) @@ -27,7 +27,6 @@ func ReadCookies(filename string, filters ...kooky.Filter) ([]*kooky.Cookie, err // CookieJar returns an initiated http.CookieJar based on the cookies stored by // the Edge browser. Set cookies are memory stored and do not modify any // browser files. -// func CookieJar(filename string, filters ...kooky.Filter) (http.CookieJar, error) { j, err := cookieStore(filename, filters...) if err != nil { @@ -41,7 +40,6 @@ func CookieJar(filename string, filters ...kooky.Filter) (http.CookieJar, error) } // CookieStore has to be closed with CookieStore.Close() after use. -// func CookieStore(filename string, filters ...kooky.Filter) (kooky.CookieStore, error) { return cookieStore(filename, filters...) } @@ -50,7 +48,7 @@ func cookieStore(filename string, filters ...kooky.Filter) (*cookies.CookieJar, m := map[string]func(f *os.File, s *ie.CookieStore, browser string){ `sqlite`: func(f *os.File, s *ie.CookieStore, browser string) { f.Close() - c := &chrome.CookieStore{} + c := &edge.CookieStore{} c.FileNameStr = filename c.BrowserStr = `edge` s.CookieStore = c diff --git a/browser/edge/find.go b/browser/edge/find.go index d07c1b7..10093f4 100644 --- a/browser/edge/find.go +++ b/browser/edge/find.go @@ -1,5 +1,3 @@ -//go:build windows - package edge import ( @@ -7,8 +5,8 @@ import ( "github.com/zellyn/kooky/internal/chrome" "github.com/zellyn/kooky/internal/chrome/find" "github.com/zellyn/kooky/internal/cookies" + "github.com/zellyn/kooky/internal/edge" edgefind "github.com/zellyn/kooky/internal/edge/find" - _ "github.com/zellyn/kooky/internal/ie/find" ) // TODO !windows platforms @@ -32,13 +30,15 @@ func (f *edgeFinder) FindCookieStores() ([]kooky.CookieStore, error) { ret = append( ret, &cookies.CookieJar{ - CookieStore: &chrome.CookieStore{ - DefaultCookieStore: cookies.DefaultCookieStore{ - BrowserStr: file.Browser, - ProfileStr: file.Profile, - OSStr: file.OS, - IsDefaultProfileBool: file.IsDefaultProfile, - FileNameStr: file.Path, + CookieStore: &edge.CookieStore{ + CookieStore: chrome.CookieStore{ + DefaultCookieStore: cookies.DefaultCookieStore{ + BrowserStr: file.Browser, + ProfileStr: file.Profile, + OSStr: file.OS, + IsDefaultProfileBool: file.IsDefaultProfile, + FileNameStr: file.Path, + }, }, }, }, diff --git a/internal/edge/cookiestore.go b/internal/edge/cookiestore.go new file mode 100644 index 0000000..1824e41 --- /dev/null +++ b/internal/edge/cookiestore.go @@ -0,0 +1,26 @@ +package edge + +import ( + "github.com/zellyn/kooky/internal/chrome" + "github.com/zellyn/kooky/internal/cookies" +) + +type CookieStore struct { + chrome.CookieStore +} + +func (s *CookieStore) Open() error { + return s.CookieStore.Open() +} + +func (s *CookieStore) Close() error { + return s.CookieStore.Close() +} + +var _ cookies.CookieStore = (*CookieStore)(nil) + +// returns the previous password for later restoration +// used in tests +func (s *CookieStore) SetKeyringPassword(password []byte) []byte { + return s.CookieStore.SetKeyringPassword(password) +} diff --git a/internal/edge/edge.go b/internal/edge/edge.go new file mode 100644 index 0000000..542889e --- /dev/null +++ b/internal/edge/edge.go @@ -0,0 +1,330 @@ +package edge + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "crypto/sha1" + "errors" + "fmt" + "runtime" + "sync" + + "github.com/zellyn/kooky" + "github.com/zellyn/kooky/internal/timex" + "github.com/zellyn/kooky/internal/utils" + "golang.org/x/crypto/pbkdf2" +) + +func (s *CookieStore) ReadCookies(filters ...kooky.Filter) ([]*kooky.Cookie, error) { + if s == nil { + return nil, errors.New(`cookie store is nil`) + } + if err := s.Open(); err != nil { + return nil, err + } else if s.Database == nil { + return nil, errors.New(`database is nil`) + } + + var cookies []*kooky.Cookie + + headerMappings := map[string]string{ + "secure": "is_secure", + "httponly": "is_httponly", + } + err := utils.VisitTableRows(s.Database, `cookies`, headerMappings, func(rowID *int64, row utils.TableRow) error { + cookie := &kooky.Cookie{ + Creation: timex.FromFILETIME(*rowID * 10), + } + + var err error + + cookie.Domain, err = row.String(`host_key`) + if err != nil { + return err + } + + cookie.Name, err = row.String(`name`) + if err != nil { + return err + } + + cookie.Path, err = row.String(`path`) + if err != nil { + return err + } + + if expires_utc, err := row.Int64(`expires_utc`); err == nil { + // https://cs.chromium.org/chromium/src/base/time/time.h?l=452&rcl=fceb9a030c182e939a436a540e6dacc70f161cb1 + if expires_utc != 0 { + cookie.Expires = timex.FromFILETIME(expires_utc * 10) + } + } else { + return err + } + + cookie.Secure, err = row.Bool(`is_secure`) + if err != nil { + return err + } + + cookie.HttpOnly, err = row.Bool(`is_httponly`) + if err != nil { + return err + } + + encrypted_value, err := row.BytesStringOrFallback(`encrypted_value`, nil) + if err != nil { + return err + } else if len(encrypted_value) > 0 { + if decrypted, err := s.decrypt(encrypted_value); err == nil { + cookie.Value = string(decrypted) + } else { + return fmt.Errorf("decrypting cookie %v: %w", cookie, err) + } + } else { + cookie.Value, err = row.String(`value`) + if err != nil { + return err + } + } + + if kooky.FilterCookie(cookie, filters...) { + cookies = append(cookies, cookie) + } + + return nil + }) + if err != nil { + return nil, err + } + + return cookies, nil +} + +// key might be the absolute path of the `Local State` file containing the encrypted key +// or a similar identifier +var keyringPasswordMap = keyringPasswordMapType{ + v: make(map[string][]byte), +} + +type keyringPasswordMapType struct { + mu sync.RWMutex + v map[string][]byte +} + +func (k *keyringPasswordMapType) get(key string) (val []byte, ok bool) { + if k == nil { + return + } + k.mu.RLock() + defer k.mu.RUnlock() + val, ok = k.v[key] + return val, ok +} +func (k *keyringPasswordMapType) set(key string, val []byte) { + k.mu.Lock() + defer k.mu.Unlock() + k.v[key] = val +} + +// "mock_password" from https://github.com/chromium/chromium/blob/34f6b421d6d255b27e01d82c3c19f49a455caa06/crypto/mock_apple_keychain.cc#L75 +var ( + fallbackPasswordLinux = [...]byte{'p', 'e', 'a', 'n', 'u', 't', 's'} + fallbackPasswordMacOS = [...]byte{'m', 'o', 'c', 'k', '_', 'p', 'a', 's', 's', 'w', 'o', 'r', 'd'} // mock keychain + prefixDPAPI = [...]byte{1, 0, 0, 0, 208, 140, 157, 223, 1, 21, 209, 17, 140, 122, 0, 192, 79, 194, 151, 235} // 0x01000000D08C9DDF0115D1118C7A00C04FC297EB +) + +func (s *CookieStore) decrypt(encrypted []byte) ([]byte, error) { + if s == nil { + return nil, errors.New(`cookie store is nil`) + } + if len(encrypted) == 0 { + return nil, errors.New(`empty encrypted value`) + } + + if len(encrypted) <= 3 { + return nil, fmt.Errorf(`encrypted value is too short (%d<=3)`, len(encrypted)) + } + + // try to reuse previously successful decryption method + if s.DecryptionMethod != nil { + decrypted, err := s.DecryptionMethod(encrypted, s.PasswordBytes) + if err == nil { + return decrypted, nil + } else { + s.DecryptionMethod = nil + } + } + + var decrypt func(encrypted, password []byte) ([]byte, error) + + // prioritize previously selected platform then current platform and then other platforms in order of usage on non-server computers + // TODO: mobile + var osMap = map[string]struct{}{} // used for deduplication + var oss []string + for _, opsys := range []string{s.OSStr, runtime.GOOS, `windows`, `darwin`, `linux`} { + if _, ok := osMap[opsys]; ok { + continue + } + oss = append(oss, opsys) + osMap[opsys] = struct{}{} + } + + for _, opsys := range oss { + // "useSavedKeyringPassword" and "tryNr" have to preserve state between retries + var useSavedKeyringPassword bool = true + var tryNr int + tryAgain: + var password, keyringPassword, fallbackPassword []byte + var needsKeyringQuerying bool + switch opsys { + case `windows`: + switch { + case bytes.HasPrefix(encrypted, prefixDPAPI[:]): + // present before Chrome v80 on Windows + decrypt = func(encrypted, _ []byte) ([]byte, error) { + return decryptDPAPI(encrypted) + } + case bytes.HasPrefix(encrypted, []byte(`v10`)): + fallthrough + default: + needsKeyringQuerying = true + decrypt = decryptAES256GCM + } + case `darwin`: + needsKeyringQuerying = true + fallbackPassword = fallbackPasswordMacOS[:] + decrypt = func(encrypted, password []byte) ([]byte, error) { + return decryptAESCBC(encrypted, password, aescbcIterationsMacOS) + } + case `linux`: + switch { + case bytes.HasPrefix(encrypted, []byte(`v11`)): + needsKeyringQuerying = true + fallbackPassword = fallbackPasswordLinux[:] + case bytes.HasPrefix(encrypted, []byte(`v10`)): + password = fallbackPasswordLinux[:] + default: + password = fallbackPasswordLinux[:] + } + decrypt = func(encrypted, password []byte) ([]byte, error) { + return decryptAESCBC(encrypted, password, aescbcIterationsLinux) + } + } + if decrypt == nil { + continue + } + + if needsKeyringQuerying { + switch tryNr { + case 0, 1: + pw, err := s.getKeyringPassword(useSavedKeyringPassword) + if err == nil { + password = pw + } else { + password = fallbackPassword + tryNr = 2 // skip querying + } + // query keyring passwords on try #1 without simply returning saved ones + useSavedKeyringPassword = false + case 2: + password = fallbackPassword + } + tryNr++ + } + + decrypted, err := decrypt(encrypted, password) + if err == nil { + s.DecryptionMethod = decrypt + s.OSStr = opsys + s.PasswordBytes = password + if len(keyringPassword) > 0 { + s.KeyringPasswordBytes = keyringPassword + } + return decrypted, nil + } else if tryNr > 0 && tryNr < 3 { + goto tryAgain + } + } + + return nil, errors.New(`unknown encryption method`) +} + +func decryptAES256GCM(encrypted, password []byte) ([]byte, error) { + // https://stackoverflow.com/a/60423699 + + if len(encrypted) < 3+12+16 { + return nil, errors.New(`encrypted value too short`) + } + + /* encoded value consists of: { + "v10" (3 bytes) + nonce (12 bytes) + ciphertext (variable size) + tag (16 bytes) + } + */ + nonce := encrypted[3 : 3+12] + ciphertextWithTag := encrypted[3+12:] + + block, err := aes.NewCipher(password) + if err != nil { + return nil, err + } + + // default size for nonce and tag match + aesgcm, err := cipher.NewGCM(block) + if err != nil { + return nil, err + } + plaintext, err := aesgcm.Open(nil, nonce, ciphertextWithTag, nil) + if err != nil { + return nil, err + } + + return plaintext, nil +} + +const ( + aescbcSalt = `saltysalt` + aescbcIV = ` ` + aescbcIterationsLinux = 1 + aescbcIterationsMacOS = 1003 + aescbcLength = 16 +) + +func decryptAESCBC(encrypted, password []byte, iterations int) ([]byte, error) { + if len(encrypted) == 0 { + return nil, errors.New("empty encrypted value") + } + + if len(encrypted) <= 3 { + return nil, fmt.Errorf("too short encrypted value (%d<=3)", len(encrypted)) + } + + // strip "v##" + encrypted = encrypted[3:] + + key := pbkdf2.Key(password, []byte(aescbcSalt), iterations, aescbcLength, sha1.New) + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + + decrypted := make([]byte, len(encrypted)) + cbc := cipher.NewCBCDecrypter(block, []byte(aescbcIV)) + cbc.CryptBlocks(decrypted, encrypted) + + // In the padding scheme the last bytes + // have a value equal to the padding length, always in (1,16] + if len(decrypted)%aescbcLength != 0 { + return nil, fmt.Errorf("decrypted data block length is not a multiple of %d", aescbcLength) + } + paddingLen := int(decrypted[len(decrypted)-1]) + if paddingLen > 16 { + return nil, fmt.Errorf("invalid last block padding length: %d", paddingLen) + } + + return decrypted[:len(decrypted)-paddingLen], nil +} diff --git a/internal/edge/edge_darwin_cgo.go b/internal/edge/edge_darwin_cgo.go new file mode 100644 index 0000000..6836398 --- /dev/null +++ b/internal/edge/edge_darwin_cgo.go @@ -0,0 +1,38 @@ +//go:build darwin && cgo +// +build darwin,cgo + +package edge + +import ( + "errors" + "fmt" + + keychain "github.com/keybase/go-keychain" +) + +// getKeyringPassword retrieves the Microsoft Edge Safe Storage password, +// caching it for future calls. +func (s *CookieStore) getKeyringPassword(useSaved bool) ([]byte, error) { + if s == nil { + return nil, errors.New(`cookie store is nil`) + } + if useSaved && s.KeyringPasswordBytes != nil { + return s.KeyringPasswordBytes, nil + } + + kpmKey := `keychain_` + s.BrowserStr + if useSaved { + if kpw, ok := keyringPasswordMap.get(kpmKey); ok { + return kpw, nil + } + } + + password, err := keychain.GetGenericPassword("Microsoft Edge Safe Storage", "Microsoft Edge", "", "") + if err != nil { + return nil, fmt.Errorf("error reading 'Microsoft Edge Safe Storage' keychain password: %w", err) + } + s.KeyringPasswordBytes = password + keyringPasswordMap.set(kpmKey, password) + + return s.KeyringPasswordBytes, nil +} diff --git a/internal/edge/edge_notwindows.go b/internal/edge/edge_notwindows.go new file mode 100644 index 0000000..08ed2e4 --- /dev/null +++ b/internal/edge/edge_notwindows.go @@ -0,0 +1,16 @@ +//go:build !windows +// +build !windows + +package edge + +import ( + "errors" +) + +// TODO: DPAPI offline decryption +// https://elie.net/talk/reversing-dpapi-and-stealing-windows-secrets-offline/ +// https://raw.githubusercontent.com/comaeio/OPCDE/master/2017/The%20Blackbox%20of%20DPAPI%20the%20gift%20that%20keeps%20on%20giving%20-%20Bartosz%20Inglot/The%20Blackbox%20of%20DPAPI%20-%20Bart%20Inglot.pdf + +func decryptDPAPI(data []byte) ([]byte, error) { + return nil, errors.New(`DPAPI method not implemented on this platform`) +} From 7e971731ebbd70fa2d7ae1671ec6c00e14751566 Mon Sep 17 00:00:00 2001 From: kalifun Date: Thu, 9 Feb 2023 09:29:13 +0800 Subject: [PATCH 5/7] Create edge_windows.go Additional Realizations --- internal/edge/edge_windows.go | 125 ++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 internal/edge/edge_windows.go diff --git a/internal/edge/edge_windows.go b/internal/edge/edge_windows.go new file mode 100644 index 0000000..ab4f136 --- /dev/null +++ b/internal/edge/edge_windows.go @@ -0,0 +1,125 @@ +package edge + +// https://groups.google.com/d/msg/golang-nuts/bUetmxErnTw/GHC5obCcmTMJ +// https://play.golang.org/p/fknP9AuLU- +// https://stackoverflow.com/a/60423699 + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "errors" + "io/ioutil" + "path/filepath" + "unsafe" + + "golang.org/x/sys/windows" +) + +const ( + cryptprotect_ui_forbidden = 0x1 +) + +var ( + dllcrypt32 = windows.NewLazySystemDLL("Crypt32.dll") + dllkernel32 = windows.NewLazySystemDLL("Kernel32.dll") + + procDecryptData = dllcrypt32.NewProc("CryptUnprotectData") + procLocalFree = dllkernel32.NewProc("LocalFree") +) + +type data_blob struct { + cbData uint32 + pbData *byte +} + +func newBlob(d []byte) *data_blob { + if len(d) == 0 { + return &data_blob{} + } + return &data_blob{ + pbData: &d[0], + cbData: uint32(len(d)), + } +} + +func (b *data_blob) toByteArray() []byte { + d := make([]byte, b.cbData) + copy(d, (*[1 << 30]byte)(unsafe.Pointer(b.pbData))[:]) + return d +} + +func decryptDPAPI(data []byte) ([]byte, error) { + var outblob data_blob + r, _, err := procDecryptData.Call(uintptr(unsafe.Pointer(newBlob(data))), 0, 0, 0, 0, cryptprotect_ui_forbidden, uintptr(unsafe.Pointer(&outblob))) + if r == 0 { + return nil, err + } + defer procLocalFree.Call(uintptr(unsafe.Pointer(outblob.pbData))) + return outblob.toByteArray(), nil +} + +// requires the path of the "Local State" json file relative to the cookie store file +// to be the same as originally +func (s *CookieStore) getKeyringPassword(useSaved bool) ([]byte, error) { + // this master key is used globally for all Chrome profiles + + if s == nil { + return nil, errors.New(`cookie store is nil`) + } + if useSaved && s.KeyringPasswordBytes != nil { + return s.KeyringPasswordBytes, nil + } + + var stateFile string + // the "Local State" json file is normally one or two directory above the "Cookies" database + dir := filepath.Dir(s.FileNameStr) + if filepath.Base(dir) == `Network` { // Chrome 96 + dir = filepath.Dir(dir) + } + stateFile, err := filepath.Abs(filepath.Join(filepath.Dir(dir), `Local State`)) + if err != nil { + return nil, err + } + + if useSaved { + if kpw, ok := keyringPasswordMap.get(stateFile); ok { + return kpw, nil + } + } + + stateBytes, err := ioutil.ReadFile(stateFile) + if err != nil { + return nil, err + } + + var localState struct { + OSCrypt struct { + EncryptedKey string `json:"encrypted_key"` + } `json:"os_crypt"` + } + if err := json.Unmarshal(stateBytes, &localState); err != nil { + return nil, err + } + + key, err := base64.StdEncoding.DecodeString(localState.OSCrypt.EncryptedKey) + if err != nil { + return nil, err + } + + if len(key) < 5 || !bytes.HasPrefix(key, []byte(`DPAPI`)) { + return nil, errors.New(`not an DPAPI key`) + } + key = key[5:] // strip "DPAPI" + key, err = decryptDPAPI(key) + if err != nil { + return nil, err + } + if len(key) != 32 { + return nil, errors.New(`master key is not 32 bytes long`) + } + s.KeyringPasswordBytes = key + keyringPasswordMap.set(stateFile, key) + + return s.KeyringPasswordBytes, nil +} From ee7e91ef01e2796f72ea9d387b775adead03fa45 Mon Sep 17 00:00:00 2001 From: kalifun Date: Thu, 9 Feb 2023 15:23:03 +0800 Subject: [PATCH 6/7] Update chrome_darwin_cgo.go chrome and edge get a different Safe Storage --- internal/chrome/chrome_darwin_cgo.go | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/internal/chrome/chrome_darwin_cgo.go b/internal/chrome/chrome_darwin_cgo.go index 7bc66bc..5e87a91 100644 --- a/internal/chrome/chrome_darwin_cgo.go +++ b/internal/chrome/chrome_darwin_cgo.go @@ -27,12 +27,32 @@ func (s *CookieStore) getKeyringPassword(useSaved bool) ([]byte, error) { } } - password, err := keychain.GetGenericPassword("Chrome Safe Storage", "Chrome", "", "") + localSafeStorage := describeSafeStorage(s.BrowserStr) + password, err := keychain.GetGenericPassword(localSafeStorage.name, localSafeStorage.account, "", "") if err != nil { - return nil, fmt.Errorf("error reading 'Chrome Safe Storage' keychain password: %w", err) + return nil, fmt.Errorf(localSafeStorage.errorMsg, localSafeStorage.name, err) } s.KeyringPasswordBytes = password keyringPasswordMap.set(kpmKey, password) return s.KeyringPasswordBytes, nil } + +type safeStorage struct { + name string + account string + errorMsg string +} + +func describeSafeStorage(browserName string) safeStorage { + defaultStore := safeStorage{ + name: "Chrome Safe Storage", + account: "Chrome", + errorMsg: "error reading '%s' keychain password: %w", + } + if browserName == `edge` { + defaultStore.name = "Microsoft Edge Safe Storage" + defaultStore.account = "Microsoft Edge" + } + return defaultStore +} From ea43877a07c1f19f88f9c4ed8d0a798b7ec077de Mon Sep 17 00:00:00 2001 From: kalifun Date: Thu, 9 Feb 2023 15:30:34 +0800 Subject: [PATCH 7/7] removing duplicate logic reuse chrome's code --- browser/edge/edge.go | 4 +- browser/edge/find.go | 19 +- internal/edge/cookiestore.go | 26 --- internal/edge/edge.go | 330 ------------------------------- internal/edge/edge_darwin_cgo.go | 38 ---- internal/edge/edge_notwindows.go | 16 -- internal/edge/edge_windows.go | 125 ------------ 7 files changed, 9 insertions(+), 549 deletions(-) delete mode 100644 internal/edge/cookiestore.go delete mode 100644 internal/edge/edge.go delete mode 100644 internal/edge/edge_darwin_cgo.go delete mode 100644 internal/edge/edge_notwindows.go delete mode 100644 internal/edge/edge_windows.go diff --git a/browser/edge/edge.go b/browser/edge/edge.go index 53d858d..2428c66 100644 --- a/browser/edge/edge.go +++ b/browser/edge/edge.go @@ -9,8 +9,8 @@ import ( "os" "github.com/zellyn/kooky" + "github.com/zellyn/kooky/internal/chrome" "github.com/zellyn/kooky/internal/cookies" - "github.com/zellyn/kooky/internal/edge" "github.com/zellyn/kooky/internal/ie" ) @@ -48,7 +48,7 @@ func cookieStore(filename string, filters ...kooky.Filter) (*cookies.CookieJar, m := map[string]func(f *os.File, s *ie.CookieStore, browser string){ `sqlite`: func(f *os.File, s *ie.CookieStore, browser string) { f.Close() - c := &edge.CookieStore{} + c := &chrome.CookieStore{} c.FileNameStr = filename c.BrowserStr = `edge` s.CookieStore = c diff --git a/browser/edge/find.go b/browser/edge/find.go index 10093f4..b67498b 100644 --- a/browser/edge/find.go +++ b/browser/edge/find.go @@ -5,12 +5,9 @@ import ( "github.com/zellyn/kooky/internal/chrome" "github.com/zellyn/kooky/internal/chrome/find" "github.com/zellyn/kooky/internal/cookies" - "github.com/zellyn/kooky/internal/edge" edgefind "github.com/zellyn/kooky/internal/edge/find" ) -// TODO !windows platforms - type edgeFinder struct{} var _ kooky.CookieStoreFinder = (*edgeFinder)(nil) @@ -30,15 +27,13 @@ func (f *edgeFinder) FindCookieStores() ([]kooky.CookieStore, error) { ret = append( ret, &cookies.CookieJar{ - CookieStore: &edge.CookieStore{ - CookieStore: chrome.CookieStore{ - DefaultCookieStore: cookies.DefaultCookieStore{ - BrowserStr: file.Browser, - ProfileStr: file.Profile, - OSStr: file.OS, - IsDefaultProfileBool: file.IsDefaultProfile, - FileNameStr: file.Path, - }, + CookieStore: &chrome.CookieStore{ + DefaultCookieStore: cookies.DefaultCookieStore{ + BrowserStr: file.Browser, + ProfileStr: file.Profile, + OSStr: file.OS, + IsDefaultProfileBool: file.IsDefaultProfile, + FileNameStr: file.Path, }, }, }, diff --git a/internal/edge/cookiestore.go b/internal/edge/cookiestore.go deleted file mode 100644 index 1824e41..0000000 --- a/internal/edge/cookiestore.go +++ /dev/null @@ -1,26 +0,0 @@ -package edge - -import ( - "github.com/zellyn/kooky/internal/chrome" - "github.com/zellyn/kooky/internal/cookies" -) - -type CookieStore struct { - chrome.CookieStore -} - -func (s *CookieStore) Open() error { - return s.CookieStore.Open() -} - -func (s *CookieStore) Close() error { - return s.CookieStore.Close() -} - -var _ cookies.CookieStore = (*CookieStore)(nil) - -// returns the previous password for later restoration -// used in tests -func (s *CookieStore) SetKeyringPassword(password []byte) []byte { - return s.CookieStore.SetKeyringPassword(password) -} diff --git a/internal/edge/edge.go b/internal/edge/edge.go deleted file mode 100644 index 542889e..0000000 --- a/internal/edge/edge.go +++ /dev/null @@ -1,330 +0,0 @@ -package edge - -import ( - "bytes" - "crypto/aes" - "crypto/cipher" - "crypto/sha1" - "errors" - "fmt" - "runtime" - "sync" - - "github.com/zellyn/kooky" - "github.com/zellyn/kooky/internal/timex" - "github.com/zellyn/kooky/internal/utils" - "golang.org/x/crypto/pbkdf2" -) - -func (s *CookieStore) ReadCookies(filters ...kooky.Filter) ([]*kooky.Cookie, error) { - if s == nil { - return nil, errors.New(`cookie store is nil`) - } - if err := s.Open(); err != nil { - return nil, err - } else if s.Database == nil { - return nil, errors.New(`database is nil`) - } - - var cookies []*kooky.Cookie - - headerMappings := map[string]string{ - "secure": "is_secure", - "httponly": "is_httponly", - } - err := utils.VisitTableRows(s.Database, `cookies`, headerMappings, func(rowID *int64, row utils.TableRow) error { - cookie := &kooky.Cookie{ - Creation: timex.FromFILETIME(*rowID * 10), - } - - var err error - - cookie.Domain, err = row.String(`host_key`) - if err != nil { - return err - } - - cookie.Name, err = row.String(`name`) - if err != nil { - return err - } - - cookie.Path, err = row.String(`path`) - if err != nil { - return err - } - - if expires_utc, err := row.Int64(`expires_utc`); err == nil { - // https://cs.chromium.org/chromium/src/base/time/time.h?l=452&rcl=fceb9a030c182e939a436a540e6dacc70f161cb1 - if expires_utc != 0 { - cookie.Expires = timex.FromFILETIME(expires_utc * 10) - } - } else { - return err - } - - cookie.Secure, err = row.Bool(`is_secure`) - if err != nil { - return err - } - - cookie.HttpOnly, err = row.Bool(`is_httponly`) - if err != nil { - return err - } - - encrypted_value, err := row.BytesStringOrFallback(`encrypted_value`, nil) - if err != nil { - return err - } else if len(encrypted_value) > 0 { - if decrypted, err := s.decrypt(encrypted_value); err == nil { - cookie.Value = string(decrypted) - } else { - return fmt.Errorf("decrypting cookie %v: %w", cookie, err) - } - } else { - cookie.Value, err = row.String(`value`) - if err != nil { - return err - } - } - - if kooky.FilterCookie(cookie, filters...) { - cookies = append(cookies, cookie) - } - - return nil - }) - if err != nil { - return nil, err - } - - return cookies, nil -} - -// key might be the absolute path of the `Local State` file containing the encrypted key -// or a similar identifier -var keyringPasswordMap = keyringPasswordMapType{ - v: make(map[string][]byte), -} - -type keyringPasswordMapType struct { - mu sync.RWMutex - v map[string][]byte -} - -func (k *keyringPasswordMapType) get(key string) (val []byte, ok bool) { - if k == nil { - return - } - k.mu.RLock() - defer k.mu.RUnlock() - val, ok = k.v[key] - return val, ok -} -func (k *keyringPasswordMapType) set(key string, val []byte) { - k.mu.Lock() - defer k.mu.Unlock() - k.v[key] = val -} - -// "mock_password" from https://github.com/chromium/chromium/blob/34f6b421d6d255b27e01d82c3c19f49a455caa06/crypto/mock_apple_keychain.cc#L75 -var ( - fallbackPasswordLinux = [...]byte{'p', 'e', 'a', 'n', 'u', 't', 's'} - fallbackPasswordMacOS = [...]byte{'m', 'o', 'c', 'k', '_', 'p', 'a', 's', 's', 'w', 'o', 'r', 'd'} // mock keychain - prefixDPAPI = [...]byte{1, 0, 0, 0, 208, 140, 157, 223, 1, 21, 209, 17, 140, 122, 0, 192, 79, 194, 151, 235} // 0x01000000D08C9DDF0115D1118C7A00C04FC297EB -) - -func (s *CookieStore) decrypt(encrypted []byte) ([]byte, error) { - if s == nil { - return nil, errors.New(`cookie store is nil`) - } - if len(encrypted) == 0 { - return nil, errors.New(`empty encrypted value`) - } - - if len(encrypted) <= 3 { - return nil, fmt.Errorf(`encrypted value is too short (%d<=3)`, len(encrypted)) - } - - // try to reuse previously successful decryption method - if s.DecryptionMethod != nil { - decrypted, err := s.DecryptionMethod(encrypted, s.PasswordBytes) - if err == nil { - return decrypted, nil - } else { - s.DecryptionMethod = nil - } - } - - var decrypt func(encrypted, password []byte) ([]byte, error) - - // prioritize previously selected platform then current platform and then other platforms in order of usage on non-server computers - // TODO: mobile - var osMap = map[string]struct{}{} // used for deduplication - var oss []string - for _, opsys := range []string{s.OSStr, runtime.GOOS, `windows`, `darwin`, `linux`} { - if _, ok := osMap[opsys]; ok { - continue - } - oss = append(oss, opsys) - osMap[opsys] = struct{}{} - } - - for _, opsys := range oss { - // "useSavedKeyringPassword" and "tryNr" have to preserve state between retries - var useSavedKeyringPassword bool = true - var tryNr int - tryAgain: - var password, keyringPassword, fallbackPassword []byte - var needsKeyringQuerying bool - switch opsys { - case `windows`: - switch { - case bytes.HasPrefix(encrypted, prefixDPAPI[:]): - // present before Chrome v80 on Windows - decrypt = func(encrypted, _ []byte) ([]byte, error) { - return decryptDPAPI(encrypted) - } - case bytes.HasPrefix(encrypted, []byte(`v10`)): - fallthrough - default: - needsKeyringQuerying = true - decrypt = decryptAES256GCM - } - case `darwin`: - needsKeyringQuerying = true - fallbackPassword = fallbackPasswordMacOS[:] - decrypt = func(encrypted, password []byte) ([]byte, error) { - return decryptAESCBC(encrypted, password, aescbcIterationsMacOS) - } - case `linux`: - switch { - case bytes.HasPrefix(encrypted, []byte(`v11`)): - needsKeyringQuerying = true - fallbackPassword = fallbackPasswordLinux[:] - case bytes.HasPrefix(encrypted, []byte(`v10`)): - password = fallbackPasswordLinux[:] - default: - password = fallbackPasswordLinux[:] - } - decrypt = func(encrypted, password []byte) ([]byte, error) { - return decryptAESCBC(encrypted, password, aescbcIterationsLinux) - } - } - if decrypt == nil { - continue - } - - if needsKeyringQuerying { - switch tryNr { - case 0, 1: - pw, err := s.getKeyringPassword(useSavedKeyringPassword) - if err == nil { - password = pw - } else { - password = fallbackPassword - tryNr = 2 // skip querying - } - // query keyring passwords on try #1 without simply returning saved ones - useSavedKeyringPassword = false - case 2: - password = fallbackPassword - } - tryNr++ - } - - decrypted, err := decrypt(encrypted, password) - if err == nil { - s.DecryptionMethod = decrypt - s.OSStr = opsys - s.PasswordBytes = password - if len(keyringPassword) > 0 { - s.KeyringPasswordBytes = keyringPassword - } - return decrypted, nil - } else if tryNr > 0 && tryNr < 3 { - goto tryAgain - } - } - - return nil, errors.New(`unknown encryption method`) -} - -func decryptAES256GCM(encrypted, password []byte) ([]byte, error) { - // https://stackoverflow.com/a/60423699 - - if len(encrypted) < 3+12+16 { - return nil, errors.New(`encrypted value too short`) - } - - /* encoded value consists of: { - "v10" (3 bytes) - nonce (12 bytes) - ciphertext (variable size) - tag (16 bytes) - } - */ - nonce := encrypted[3 : 3+12] - ciphertextWithTag := encrypted[3+12:] - - block, err := aes.NewCipher(password) - if err != nil { - return nil, err - } - - // default size for nonce and tag match - aesgcm, err := cipher.NewGCM(block) - if err != nil { - return nil, err - } - plaintext, err := aesgcm.Open(nil, nonce, ciphertextWithTag, nil) - if err != nil { - return nil, err - } - - return plaintext, nil -} - -const ( - aescbcSalt = `saltysalt` - aescbcIV = ` ` - aescbcIterationsLinux = 1 - aescbcIterationsMacOS = 1003 - aescbcLength = 16 -) - -func decryptAESCBC(encrypted, password []byte, iterations int) ([]byte, error) { - if len(encrypted) == 0 { - return nil, errors.New("empty encrypted value") - } - - if len(encrypted) <= 3 { - return nil, fmt.Errorf("too short encrypted value (%d<=3)", len(encrypted)) - } - - // strip "v##" - encrypted = encrypted[3:] - - key := pbkdf2.Key(password, []byte(aescbcSalt), iterations, aescbcLength, sha1.New) - block, err := aes.NewCipher(key) - if err != nil { - return nil, err - } - - decrypted := make([]byte, len(encrypted)) - cbc := cipher.NewCBCDecrypter(block, []byte(aescbcIV)) - cbc.CryptBlocks(decrypted, encrypted) - - // In the padding scheme the last bytes - // have a value equal to the padding length, always in (1,16] - if len(decrypted)%aescbcLength != 0 { - return nil, fmt.Errorf("decrypted data block length is not a multiple of %d", aescbcLength) - } - paddingLen := int(decrypted[len(decrypted)-1]) - if paddingLen > 16 { - return nil, fmt.Errorf("invalid last block padding length: %d", paddingLen) - } - - return decrypted[:len(decrypted)-paddingLen], nil -} diff --git a/internal/edge/edge_darwin_cgo.go b/internal/edge/edge_darwin_cgo.go deleted file mode 100644 index 6836398..0000000 --- a/internal/edge/edge_darwin_cgo.go +++ /dev/null @@ -1,38 +0,0 @@ -//go:build darwin && cgo -// +build darwin,cgo - -package edge - -import ( - "errors" - "fmt" - - keychain "github.com/keybase/go-keychain" -) - -// getKeyringPassword retrieves the Microsoft Edge Safe Storage password, -// caching it for future calls. -func (s *CookieStore) getKeyringPassword(useSaved bool) ([]byte, error) { - if s == nil { - return nil, errors.New(`cookie store is nil`) - } - if useSaved && s.KeyringPasswordBytes != nil { - return s.KeyringPasswordBytes, nil - } - - kpmKey := `keychain_` + s.BrowserStr - if useSaved { - if kpw, ok := keyringPasswordMap.get(kpmKey); ok { - return kpw, nil - } - } - - password, err := keychain.GetGenericPassword("Microsoft Edge Safe Storage", "Microsoft Edge", "", "") - if err != nil { - return nil, fmt.Errorf("error reading 'Microsoft Edge Safe Storage' keychain password: %w", err) - } - s.KeyringPasswordBytes = password - keyringPasswordMap.set(kpmKey, password) - - return s.KeyringPasswordBytes, nil -} diff --git a/internal/edge/edge_notwindows.go b/internal/edge/edge_notwindows.go deleted file mode 100644 index 08ed2e4..0000000 --- a/internal/edge/edge_notwindows.go +++ /dev/null @@ -1,16 +0,0 @@ -//go:build !windows -// +build !windows - -package edge - -import ( - "errors" -) - -// TODO: DPAPI offline decryption -// https://elie.net/talk/reversing-dpapi-and-stealing-windows-secrets-offline/ -// https://raw.githubusercontent.com/comaeio/OPCDE/master/2017/The%20Blackbox%20of%20DPAPI%20the%20gift%20that%20keeps%20on%20giving%20-%20Bartosz%20Inglot/The%20Blackbox%20of%20DPAPI%20-%20Bart%20Inglot.pdf - -func decryptDPAPI(data []byte) ([]byte, error) { - return nil, errors.New(`DPAPI method not implemented on this platform`) -} diff --git a/internal/edge/edge_windows.go b/internal/edge/edge_windows.go deleted file mode 100644 index ab4f136..0000000 --- a/internal/edge/edge_windows.go +++ /dev/null @@ -1,125 +0,0 @@ -package edge - -// https://groups.google.com/d/msg/golang-nuts/bUetmxErnTw/GHC5obCcmTMJ -// https://play.golang.org/p/fknP9AuLU- -// https://stackoverflow.com/a/60423699 - -import ( - "bytes" - "encoding/base64" - "encoding/json" - "errors" - "io/ioutil" - "path/filepath" - "unsafe" - - "golang.org/x/sys/windows" -) - -const ( - cryptprotect_ui_forbidden = 0x1 -) - -var ( - dllcrypt32 = windows.NewLazySystemDLL("Crypt32.dll") - dllkernel32 = windows.NewLazySystemDLL("Kernel32.dll") - - procDecryptData = dllcrypt32.NewProc("CryptUnprotectData") - procLocalFree = dllkernel32.NewProc("LocalFree") -) - -type data_blob struct { - cbData uint32 - pbData *byte -} - -func newBlob(d []byte) *data_blob { - if len(d) == 0 { - return &data_blob{} - } - return &data_blob{ - pbData: &d[0], - cbData: uint32(len(d)), - } -} - -func (b *data_blob) toByteArray() []byte { - d := make([]byte, b.cbData) - copy(d, (*[1 << 30]byte)(unsafe.Pointer(b.pbData))[:]) - return d -} - -func decryptDPAPI(data []byte) ([]byte, error) { - var outblob data_blob - r, _, err := procDecryptData.Call(uintptr(unsafe.Pointer(newBlob(data))), 0, 0, 0, 0, cryptprotect_ui_forbidden, uintptr(unsafe.Pointer(&outblob))) - if r == 0 { - return nil, err - } - defer procLocalFree.Call(uintptr(unsafe.Pointer(outblob.pbData))) - return outblob.toByteArray(), nil -} - -// requires the path of the "Local State" json file relative to the cookie store file -// to be the same as originally -func (s *CookieStore) getKeyringPassword(useSaved bool) ([]byte, error) { - // this master key is used globally for all Chrome profiles - - if s == nil { - return nil, errors.New(`cookie store is nil`) - } - if useSaved && s.KeyringPasswordBytes != nil { - return s.KeyringPasswordBytes, nil - } - - var stateFile string - // the "Local State" json file is normally one or two directory above the "Cookies" database - dir := filepath.Dir(s.FileNameStr) - if filepath.Base(dir) == `Network` { // Chrome 96 - dir = filepath.Dir(dir) - } - stateFile, err := filepath.Abs(filepath.Join(filepath.Dir(dir), `Local State`)) - if err != nil { - return nil, err - } - - if useSaved { - if kpw, ok := keyringPasswordMap.get(stateFile); ok { - return kpw, nil - } - } - - stateBytes, err := ioutil.ReadFile(stateFile) - if err != nil { - return nil, err - } - - var localState struct { - OSCrypt struct { - EncryptedKey string `json:"encrypted_key"` - } `json:"os_crypt"` - } - if err := json.Unmarshal(stateBytes, &localState); err != nil { - return nil, err - } - - key, err := base64.StdEncoding.DecodeString(localState.OSCrypt.EncryptedKey) - if err != nil { - return nil, err - } - - if len(key) < 5 || !bytes.HasPrefix(key, []byte(`DPAPI`)) { - return nil, errors.New(`not an DPAPI key`) - } - key = key[5:] // strip "DPAPI" - key, err = decryptDPAPI(key) - if err != nil { - return nil, err - } - if len(key) != 32 { - return nil, errors.New(`master key is not 32 bytes long`) - } - s.KeyringPasswordBytes = key - keyringPasswordMap.set(stateFile, key) - - return s.KeyringPasswordBytes, nil -}