forked from Silverfish/proton-bridge
GODT-1155 Update gopenpgp and use go-srp
This commit is contained in:
11
go.mod
11
go.mod
@ -7,8 +7,7 @@ go 1.13
|
|||||||
require (
|
require (
|
||||||
github.com/docker/docker-credential-helpers v0.6.3
|
github.com/docker/docker-credential-helpers v0.6.3
|
||||||
github.com/emersion/go-imap v1.0.6
|
github.com/emersion/go-imap v1.0.6
|
||||||
github.com/jameskeane/bcrypt v0.0.0-20170924085257-7509ea014998
|
github.com/jameskeane/bcrypt v0.0.0-20170924085257-7509ea014998 // indirect
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
|
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@ -17,8 +16,9 @@ require (
|
|||||||
github.com/ProtonMail/go-autostart v0.0.0-20181114175602-c5272053443a
|
github.com/ProtonMail/go-autostart v0.0.0-20181114175602-c5272053443a
|
||||||
github.com/ProtonMail/go-imap-id v0.0.0-20190926060100-f94a56b9ecde
|
github.com/ProtonMail/go-imap-id v0.0.0-20190926060100-f94a56b9ecde
|
||||||
github.com/ProtonMail/go-rfc5322 v0.8.0
|
github.com/ProtonMail/go-rfc5322 v0.8.0
|
||||||
|
github.com/ProtonMail/go-srp v0.0.0-20210514134713-bd9454f3fa01
|
||||||
github.com/ProtonMail/go-vcard v0.0.0-20180326232728-33aaa0a0c8a5
|
github.com/ProtonMail/go-vcard v0.0.0-20180326232728-33aaa0a0c8a5
|
||||||
github.com/ProtonMail/gopenpgp/v2 v2.1.3
|
github.com/ProtonMail/gopenpgp/v2 v2.1.9
|
||||||
github.com/PuerkitoBio/goquery v1.5.1
|
github.com/PuerkitoBio/goquery v1.5.1
|
||||||
github.com/abiosoft/ishell v2.0.0+incompatible
|
github.com/abiosoft/ishell v2.0.0+incompatible
|
||||||
github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db // indirect
|
github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db // indirect
|
||||||
@ -61,13 +61,14 @@ require (
|
|||||||
github.com/urfave/cli/v2 v2.2.0
|
github.com/urfave/cli/v2 v2.2.0
|
||||||
github.com/vmihailenco/msgpack/v5 v5.1.3
|
github.com/vmihailenco/msgpack/v5 v5.1.3
|
||||||
go.etcd.io/bbolt v1.3.5
|
go.etcd.io/bbolt v1.3.5
|
||||||
|
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2
|
||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4
|
||||||
golang.org/x/text v0.3.5-0.20201125200606-c27b9fd57aec
|
golang.org/x/text v0.3.5-0.20201125200606-c27b9fd57aec
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
replace (
|
replace (
|
||||||
github.com/docker/docker-credential-helpers => github.com/ProtonMail/docker-credential-helpers v1.1.0
|
github.com/docker/docker-credential-helpers => github.com/ProtonMail/docker-credential-helpers v1.1.0
|
||||||
github.com/emersion/go-imap => github.com/ProtonMail/go-imap v0.0.0-20201228133358-4db68cea0cac
|
github.com/emersion/go-imap => github.com/ProtonMail/go-imap v0.0.0-20201228133358-4db68cea0cac
|
||||||
github.com/jameskeane/bcrypt => github.com/ProtonMail/bcrypt v0.0.0-20170924085257-7509ea014998
|
github.com/jameskeane/bcrypt => github.com/ProtonMail/bcrypt v0.0.0-20210511135022-227b4adcab57
|
||||||
golang.org/x/crypto => github.com/ProtonMail/crypto v0.0.0-20201112115411-41db4ea0dd1c
|
|
||||||
)
|
)
|
||||||
|
|||||||
35
go.sum
35
go.sum
@ -8,16 +8,14 @@ github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMd
|
|||||||
github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=
|
github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=
|
||||||
github.com/Masterminds/semver/v3 v3.1.0 h1:Y2lUDsFKVRSYGojLJ1yLxSXdMmMYTYls0rCvoqmMUQk=
|
github.com/Masterminds/semver/v3 v3.1.0 h1:Y2lUDsFKVRSYGojLJ1yLxSXdMmMYTYls0rCvoqmMUQk=
|
||||||
github.com/Masterminds/semver/v3 v3.1.0/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
github.com/Masterminds/semver/v3 v3.1.0/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||||
github.com/ProtonMail/bcrypt v0.0.0-20170924085257-7509ea014998 h1:YT2uVwQiRQZxCaaahwfcgTq2j3j66w00n/27gb/zubs=
|
github.com/ProtonMail/bcrypt v0.0.0-20210511135022-227b4adcab57 h1:pHA4K54ifoogVLunGGHi3xyF5Nz4x+Uh3dJuy3NwGQQ=
|
||||||
github.com/ProtonMail/bcrypt v0.0.0-20170924085257-7509ea014998/go.mod h1:HecWFHognK8GfRDGnFQbW/LiV7A3MX3gZVs45vk5h8I=
|
github.com/ProtonMail/bcrypt v0.0.0-20210511135022-227b4adcab57/go.mod h1:HecWFHognK8GfRDGnFQbW/LiV7A3MX3gZVs45vk5h8I=
|
||||||
github.com/ProtonMail/crypto v0.0.0-20201112115411-41db4ea0dd1c h1:iaVbEOnskSGgcH7XQWHG6VPirHDRoYe+Idd0/dl4m8A=
|
|
||||||
github.com/ProtonMail/crypto v0.0.0-20201112115411-41db4ea0dd1c/go.mod h1:Pxr7w4gA2ikI4sWyYwEffm+oew1WAJHzG1SiDpQMkrI=
|
|
||||||
github.com/ProtonMail/docker-credential-helpers v1.1.0 h1:+kvUIpwWcbtP3WFv5sSvkFn/XLzSqPOB5AAthuk9xPk=
|
github.com/ProtonMail/docker-credential-helpers v1.1.0 h1:+kvUIpwWcbtP3WFv5sSvkFn/XLzSqPOB5AAthuk9xPk=
|
||||||
github.com/ProtonMail/docker-credential-helpers v1.1.0/go.mod h1:mK0aBveCxhnQ756AmaTfXMZDeULvheYVhF/MWMErN5g=
|
github.com/ProtonMail/docker-credential-helpers v1.1.0/go.mod h1:mK0aBveCxhnQ756AmaTfXMZDeULvheYVhF/MWMErN5g=
|
||||||
github.com/ProtonMail/go-autostart v0.0.0-20181114175602-c5272053443a h1:fXK2KsfnkBV9Nh+9SKzHchYjuE9s0vI20JG1mbtEAcc=
|
github.com/ProtonMail/go-autostart v0.0.0-20181114175602-c5272053443a h1:fXK2KsfnkBV9Nh+9SKzHchYjuE9s0vI20JG1mbtEAcc=
|
||||||
github.com/ProtonMail/go-autostart v0.0.0-20181114175602-c5272053443a/go.mod h1:oTGdE7/DlWIr23G0IKW3OXK9wZ5Hw1GGiaJFccTvZi4=
|
github.com/ProtonMail/go-autostart v0.0.0-20181114175602-c5272053443a/go.mod h1:oTGdE7/DlWIr23G0IKW3OXK9wZ5Hw1GGiaJFccTvZi4=
|
||||||
github.com/ProtonMail/go-crypto v0.0.0-20201208171014-cdb7591792e2 h1:pQkjJELHayW59jp7r4G5Dlmnicr5McejDfwsjcwI1SU=
|
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 h1:YoJbenK9C67SkzkDfmQuVln04ygHj3vjZfd9FL+GmQQ=
|
||||||
github.com/ProtonMail/go-crypto v0.0.0-20201208171014-cdb7591792e2/go.mod h1:HTM9X7e9oLwn7RiqLG0UVwVRJenLs3wN+tQ0NPAfwMQ=
|
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
|
||||||
github.com/ProtonMail/go-imap v0.0.0-20201228133358-4db68cea0cac h1:2xU3QncAiS/W3UlWZTkbNKW5WkLzk6Egl1T0xX+sbjs=
|
github.com/ProtonMail/go-imap v0.0.0-20201228133358-4db68cea0cac h1:2xU3QncAiS/W3UlWZTkbNKW5WkLzk6Egl1T0xX+sbjs=
|
||||||
github.com/ProtonMail/go-imap v0.0.0-20201228133358-4db68cea0cac/go.mod h1:yKASt+C3ZiDAiCSssxg9caIckWF/JG7ZQTO7GAmvicU=
|
github.com/ProtonMail/go-imap v0.0.0-20201228133358-4db68cea0cac/go.mod h1:yKASt+C3ZiDAiCSssxg9caIckWF/JG7ZQTO7GAmvicU=
|
||||||
github.com/ProtonMail/go-imap-id v0.0.0-20190926060100-f94a56b9ecde h1:5koQozTDELymYOyFbQ/VSubexAEXzDR8qGM5mO8GRdw=
|
github.com/ProtonMail/go-imap-id v0.0.0-20190926060100-f94a56b9ecde h1:5koQozTDELymYOyFbQ/VSubexAEXzDR8qGM5mO8GRdw=
|
||||||
@ -26,10 +24,12 @@ github.com/ProtonMail/go-mime v0.0.0-20190923161245-9b5a4261663a h1:W6RrgN/sTxg1
|
|||||||
github.com/ProtonMail/go-mime v0.0.0-20190923161245-9b5a4261663a/go.mod h1:NYt+V3/4rEeDuaev/zw1zCq8uqVEuPHzDPo3OZrlGJ4=
|
github.com/ProtonMail/go-mime v0.0.0-20190923161245-9b5a4261663a/go.mod h1:NYt+V3/4rEeDuaev/zw1zCq8uqVEuPHzDPo3OZrlGJ4=
|
||||||
github.com/ProtonMail/go-rfc5322 v0.8.0 h1:7emrf75n3CDIduQflx7aT1nJa5h/kGsiFKUYX/+IAkU=
|
github.com/ProtonMail/go-rfc5322 v0.8.0 h1:7emrf75n3CDIduQflx7aT1nJa5h/kGsiFKUYX/+IAkU=
|
||||||
github.com/ProtonMail/go-rfc5322 v0.8.0/go.mod h1:BwpTbkJxkMGkc+pC84AXZnwuWOisEULBpfPIyIKS/Us=
|
github.com/ProtonMail/go-rfc5322 v0.8.0/go.mod h1:BwpTbkJxkMGkc+pC84AXZnwuWOisEULBpfPIyIKS/Us=
|
||||||
|
github.com/ProtonMail/go-srp v0.0.0-20210514134713-bd9454f3fa01 h1:sRxNvPGnJFh6yWlSr9BpGsSrshFkZLClSm5oIi++a0I=
|
||||||
|
github.com/ProtonMail/go-srp v0.0.0-20210514134713-bd9454f3fa01/go.mod h1:jOXzdvWTILIJzl83yzi/EZcnnhpI+A/5EyflaeVfi/0=
|
||||||
github.com/ProtonMail/go-vcard v0.0.0-20180326232728-33aaa0a0c8a5 h1:Uga1DHFN4GUxuDQr0F71tpi8I9HqPIlZodZAI1lR6VQ=
|
github.com/ProtonMail/go-vcard v0.0.0-20180326232728-33aaa0a0c8a5 h1:Uga1DHFN4GUxuDQr0F71tpi8I9HqPIlZodZAI1lR6VQ=
|
||||||
github.com/ProtonMail/go-vcard v0.0.0-20180326232728-33aaa0a0c8a5/go.mod h1:oeP9CMN+ajWp5jKp1kue5daJNwMMxLF+ujPaUIoJWlA=
|
github.com/ProtonMail/go-vcard v0.0.0-20180326232728-33aaa0a0c8a5/go.mod h1:oeP9CMN+ajWp5jKp1kue5daJNwMMxLF+ujPaUIoJWlA=
|
||||||
github.com/ProtonMail/gopenpgp/v2 v2.1.3 h1:4+nFDJ9WtcUQTip/je2Ll3P21XhAUl4asWsafLrw97c=
|
github.com/ProtonMail/gopenpgp/v2 v2.1.9 h1:MdvkFBP8ldOHYOoaVct9LO+Zv5rl6VdeN1QurntRmkc=
|
||||||
github.com/ProtonMail/gopenpgp/v2 v2.1.3/go.mod h1:WeYndoqEcRR4/QbgRL24z6OwYX5T1RWerRk8NfZ6rJM=
|
github.com/ProtonMail/gopenpgp/v2 v2.1.9/go.mod h1:CHIXesUdnPxIxtJTg2P/cxoA0cvUwIBpZIS8SsY82QA=
|
||||||
github.com/PuerkitoBio/goquery v1.5.1 h1:PSPBGne8NIUWw+/7vFBV+kG2J/5MOjbzc7154OaKCSE=
|
github.com/PuerkitoBio/goquery v1.5.1 h1:PSPBGne8NIUWw+/7vFBV+kG2J/5MOjbzc7154OaKCSE=
|
||||||
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
|
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
|
||||||
github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0=
|
github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0=
|
||||||
@ -285,10 +285,21 @@ github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDf
|
|||||||
github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=
|
github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=
|
||||||
go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0=
|
go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0=
|
||||||
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
|
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
|
||||||
|
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||||
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w=
|
||||||
|
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||||
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
|
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
|
||||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||||
|
golang.org/x/mobile v0.0.0-20190910184405-b558ed863381/go.mod h1:p895TfNkDgPEmEQrNiOtIl3j98d/tGU95djDj7NfyjQ=
|
||||||
golang.org/x/mobile v0.0.0-20200801112145-973feb4309de/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4=
|
golang.org/x/mobile v0.0.0-20200801112145-973feb4309de/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4=
|
||||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
@ -305,13 +316,11 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
|
|||||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
|
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
|
||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
|
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
@ -320,6 +329,7 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h
|
|||||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@ -327,13 +337,10 @@ golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
|
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04 h1:cEhElsAv9LUt9ZUUocxzWe05oFLVd+AA2nstydTeI8g=
|
|
||||||
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44 h1:Bli41pIlzTzf3KEY06n+xnzK/BESIg2ze4Pgfh/aI8c=
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44 h1:Bli41pIlzTzf3KEY06n+xnzK/BESIg2ze4Pgfh/aI8c=
|
||||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
@ -347,8 +354,8 @@ golang.org/x/tools v0.0.0-20190327201419-c70d86f8b7cf/go.mod h1:LCzVGOaR6xXOjkQ3
|
|||||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190909214602-067311248421/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69 h1:yBHHx+XZqXJBm6Exke3N7V9gnlsyXxoCPEb1yVenjfk=
|
|
||||||
golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
|||||||
@ -68,7 +68,7 @@ func (f *frontendCLI) loginAccount(c *ishell.Context) { // nolint[funlen]
|
|||||||
}
|
}
|
||||||
|
|
||||||
f.Println("Authenticating ... ")
|
f.Println("Authenticating ... ")
|
||||||
client, auth, err := f.ie.Login(loginName, password)
|
client, auth, err := f.ie.Login(loginName, []byte(password))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
f.processAPIError(err)
|
f.processAPIError(err)
|
||||||
return
|
return
|
||||||
@ -96,7 +96,7 @@ func (f *frontendCLI) loginAccount(c *ishell.Context) { // nolint[funlen]
|
|||||||
}
|
}
|
||||||
|
|
||||||
f.Println("Adding account ...")
|
f.Println("Adding account ...")
|
||||||
user, err := f.ie.FinishLogin(client, auth, mailboxPassword)
|
user, err := f.ie.FinishLogin(client, auth, []byte(mailboxPassword))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithField("username", loginName).WithError(err).Error("Login was unsuccessful")
|
log.WithField("username", loginName).WithError(err).Error("Login was unsuccessful")
|
||||||
f.Println("Adding account was unsuccessful:", err)
|
f.Println("Adding account was unsuccessful:", err)
|
||||||
|
|||||||
@ -115,7 +115,7 @@ func (f *frontendCLI) loginAccount(c *ishell.Context) { // nolint[funlen]
|
|||||||
}
|
}
|
||||||
|
|
||||||
f.Println("Authenticating ... ")
|
f.Println("Authenticating ... ")
|
||||||
client, auth, err := f.bridge.Login(loginName, password)
|
client, auth, err := f.bridge.Login(loginName, []byte(password))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
f.processAPIError(err)
|
f.processAPIError(err)
|
||||||
return
|
return
|
||||||
@ -143,7 +143,7 @@ func (f *frontendCLI) loginAccount(c *ishell.Context) { // nolint[funlen]
|
|||||||
}
|
}
|
||||||
|
|
||||||
f.Println("Adding account ...")
|
f.Println("Adding account ...")
|
||||||
user, err := f.bridge.FinishLogin(client, auth, mailboxPassword)
|
user, err := f.bridge.FinishLogin(client, auth, []byte(mailboxPassword))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithField("username", loginName).WithError(err).Error("Login was unsuccessful")
|
log.WithField("username", loginName).WithError(err).Error("Login was unsuccessful")
|
||||||
f.Println("Adding account was unsuccessful:", err)
|
f.Println("Adding account was unsuccessful:", err)
|
||||||
|
|||||||
@ -186,7 +186,7 @@ func (a *Accounts) showLoginError(err error, scope string) bool {
|
|||||||
// 2: when has no 2FA but have MBOX
|
// 2: when has no 2FA but have MBOX
|
||||||
func (a *Accounts) Login(login, password string) int {
|
func (a *Accounts) Login(login, password string) int {
|
||||||
var err error
|
var err error
|
||||||
a.authClient, a.auth, err = a.um.Login(login, password)
|
a.authClient, a.auth, err = a.um.Login(login, []byte(password))
|
||||||
if a.showLoginError(err, "login") {
|
if a.showLoginError(err, "login") {
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
@ -230,7 +230,7 @@ func (a *Accounts) AddAccount(mailboxPassword string) int {
|
|||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := a.um.FinishLogin(a.authClient, a.auth, mailboxPassword)
|
user, err := a.um.FinishLogin(a.authClient, a.auth, []byte(mailboxPassword))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Error("Login was unsuccessful")
|
log.WithError(err).Error("Login was unsuccessful")
|
||||||
a.qml.SetAddAccountWarning("Failure: "+err.Error(), -2)
|
a.qml.SetAddAccountWarning("Failure: "+err.Error(), -2)
|
||||||
|
|||||||
@ -152,7 +152,7 @@ func (s *FrontendQt) showLoginError(err error, scope string) bool {
|
|||||||
// 2: when has no 2FA but have MBOX
|
// 2: when has no 2FA but have MBOX
|
||||||
func (s *FrontendQt) login(login, password string) int {
|
func (s *FrontendQt) login(login, password string) int {
|
||||||
var err error
|
var err error
|
||||||
s.authClient, s.auth, err = s.bridge.Login(login, password)
|
s.authClient, s.auth, err = s.bridge.Login(login, []byte(password))
|
||||||
if s.showLoginError(err, "login") {
|
if s.showLoginError(err, "login") {
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
@ -195,7 +195,7 @@ func (s *FrontendQt) addAccount(mailboxPassword string) int {
|
|||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := s.bridge.FinishLogin(s.authClient, s.auth, mailboxPassword)
|
user, err := s.bridge.FinishLogin(s.authClient, s.auth, []byte(mailboxPassword))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Error("Login was unsuccessful")
|
log.WithError(err).Error("Login was unsuccessful")
|
||||||
s.Qml.SetAddAccountWarning("Failure: "+err.Error(), -2)
|
s.Qml.SetAddAccountWarning("Failure: "+err.Error(), -2)
|
||||||
|
|||||||
@ -49,8 +49,8 @@ type Updater interface {
|
|||||||
|
|
||||||
// UserManager is an interface of users needed by frontend.
|
// UserManager is an interface of users needed by frontend.
|
||||||
type UserManager interface {
|
type UserManager interface {
|
||||||
Login(username, password string) (pmapi.Client, *pmapi.Auth, error)
|
Login(username string, password []byte) (pmapi.Client, *pmapi.Auth, error)
|
||||||
FinishLogin(client pmapi.Client, auth *pmapi.Auth, mailboxPassword string) (User, error)
|
FinishLogin(client pmapi.Client, auth *pmapi.Auth, mailboxPassword []byte) (User, error)
|
||||||
GetUsers() []User
|
GetUsers() []User
|
||||||
GetUser(query string) (User, error)
|
GetUser(query string) (User, error)
|
||||||
DeleteUser(userID string, clearCache bool) error
|
DeleteUser(userID string, clearCache bool) error
|
||||||
@ -94,7 +94,7 @@ func NewBridgeWrap(bridge *bridge.Bridge) *bridgeWrap { //nolint[golint]
|
|||||||
return &bridgeWrap{Bridge: bridge}
|
return &bridgeWrap{Bridge: bridge}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *bridgeWrap) FinishLogin(client pmapi.Client, auth *pmapi.Auth, mailboxPassword string) (User, error) {
|
func (b *bridgeWrap) FinishLogin(client pmapi.Client, auth *pmapi.Auth, mailboxPassword []byte) (User, error) {
|
||||||
return b.Bridge.FinishLogin(client, auth, mailboxPassword)
|
return b.Bridge.FinishLogin(client, auth, mailboxPassword)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,7 +134,7 @@ func NewImportExportWrap(ie *importexport.ImportExport) *importExportWrap { //no
|
|||||||
return &importExportWrap{ImportExport: ie}
|
return &importExportWrap{ImportExport: ie}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *importExportWrap) FinishLogin(client pmapi.Client, auth *pmapi.Auth, mailboxPassword string) (User, error) {
|
func (b *importExportWrap) FinishLogin(client pmapi.Client, auth *pmapi.Auth, mailboxPassword []byte) (User, error) {
|
||||||
return b.ImportExport.FinishLogin(client, auth, mailboxPassword)
|
return b.ImportExport.FinishLogin(client, auth, mailboxPassword)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -47,8 +47,8 @@ type Credentials struct {
|
|||||||
UserID, // Do not marshal; used as a key.
|
UserID, // Do not marshal; used as a key.
|
||||||
Name,
|
Name,
|
||||||
Emails,
|
Emails,
|
||||||
APIToken,
|
APIToken string
|
||||||
MailboxPassword,
|
MailboxPassword []byte
|
||||||
BridgePassword,
|
BridgePassword,
|
||||||
Version string
|
Version string
|
||||||
Timestamp int64
|
Timestamp int64
|
||||||
@ -58,15 +58,15 @@ type Credentials struct {
|
|||||||
|
|
||||||
func (s *Credentials) Marshal() string {
|
func (s *Credentials) Marshal() string {
|
||||||
items := []string{
|
items := []string{
|
||||||
s.Name, // 0
|
s.Name, // 0
|
||||||
s.Emails, // 1
|
s.Emails, // 1
|
||||||
s.APIToken, // 2
|
s.APIToken, // 2
|
||||||
s.MailboxPassword, // 3
|
string(s.MailboxPassword), // 3
|
||||||
s.BridgePassword, // 4
|
s.BridgePassword, // 4
|
||||||
s.Version, // 5
|
s.Version, // 5
|
||||||
"", // 6
|
"", // 6
|
||||||
"", // 7
|
"", // 7
|
||||||
"", // 8
|
"", // 8
|
||||||
}
|
}
|
||||||
|
|
||||||
items[6] = fmt.Sprint(s.Timestamp)
|
items[6] = fmt.Sprint(s.Timestamp)
|
||||||
@ -97,7 +97,7 @@ func (s *Credentials) Unmarshal(secret string) error {
|
|||||||
s.Name = items[0]
|
s.Name = items[0]
|
||||||
s.Emails = items[1]
|
s.Emails = items[1]
|
||||||
s.APIToken = items[2]
|
s.APIToken = items[2]
|
||||||
s.MailboxPassword = items[3]
|
s.MailboxPassword = []byte(items[3])
|
||||||
|
|
||||||
switch len(items) {
|
switch len(items) {
|
||||||
case itemLengthBridge:
|
case itemLengthBridge:
|
||||||
@ -143,11 +143,11 @@ func (s *Credentials) CheckPassword(password string) error {
|
|||||||
|
|
||||||
func (s *Credentials) Logout() {
|
func (s *Credentials) Logout() {
|
||||||
s.APIToken = ""
|
s.APIToken = ""
|
||||||
s.MailboxPassword = ""
|
s.MailboxPassword = []byte{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Credentials) IsConnected() bool {
|
func (s *Credentials) IsConnected() bool {
|
||||||
return s.APIToken != "" && s.MailboxPassword != ""
|
return s.APIToken != "" && len(s.MailboxPassword) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Credentials) SplitAPIToken() (string, string, error) {
|
func (s *Credentials) SplitAPIToken() (string, string, error) {
|
||||||
|
|||||||
@ -32,7 +32,7 @@ var wantCredentials = Credentials{
|
|||||||
Name: "name",
|
Name: "name",
|
||||||
Emails: "email1;email2",
|
Emails: "email1;email2",
|
||||||
APIToken: "token",
|
APIToken: "token",
|
||||||
MailboxPassword: "mailbox pass",
|
MailboxPassword: []byte("mailbox pass"),
|
||||||
BridgePassword: "bridge pass",
|
BridgePassword: "bridge pass",
|
||||||
Version: "k11",
|
Version: "k11",
|
||||||
Timestamp: time.Now().Unix(),
|
Timestamp: time.Now().Unix(),
|
||||||
@ -52,7 +52,7 @@ func TestUnmarshallImportExport(t *testing.T) {
|
|||||||
wantCredentials.Name,
|
wantCredentials.Name,
|
||||||
wantCredentials.Emails,
|
wantCredentials.Emails,
|
||||||
wantCredentials.APIToken,
|
wantCredentials.APIToken,
|
||||||
wantCredentials.MailboxPassword,
|
string(wantCredentials.MailboxPassword),
|
||||||
"k11",
|
"k11",
|
||||||
fmt.Sprint(wantCredentials.Timestamp),
|
fmt.Sprint(wantCredentials.Timestamp),
|
||||||
}
|
}
|
||||||
|
|||||||
@ -39,7 +39,7 @@ func NewStore(keychain *keychain.Keychain) *Store {
|
|||||||
return &Store{secrets: keychain}
|
return &Store{secrets: keychain}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) Add(userID, userName, uid, ref, mailboxPassword string, emails []string) (*Credentials, error) {
|
func (s *Store) Add(userID, userName, uid, ref string, mailboxPassword []byte, emails []string) (*Credentials, error) {
|
||||||
storeLocker.Lock()
|
storeLocker.Lock()
|
||||||
defer storeLocker.Unlock()
|
defer storeLocker.Unlock()
|
||||||
|
|
||||||
@ -108,7 +108,7 @@ func (s *Store) UpdateEmails(userID string, emails []string) (*Credentials, erro
|
|||||||
return credentials, s.saveCredentials(credentials)
|
return credentials, s.saveCredentials(credentials)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) UpdatePassword(userID, password string) (*Credentials, error) {
|
func (s *Store) UpdatePassword(userID string, password []byte) (*Credentials, error) {
|
||||||
storeLocker.Lock()
|
storeLocker.Lock()
|
||||||
defer storeLocker.Unlock()
|
defer storeLocker.Unlock()
|
||||||
|
|
||||||
|
|||||||
@ -277,7 +277,7 @@ func TestMarshal(t *testing.T) {
|
|||||||
Name: "007",
|
Name: "007",
|
||||||
Emails: "ja@pm.me;aj@cus.tom",
|
Emails: "ja@pm.me;aj@cus.tom",
|
||||||
APIToken: "sdfdsfsdfsdfsdf",
|
APIToken: "sdfdsfsdfsdfsdf",
|
||||||
MailboxPassword: "cdcdcdcd",
|
MailboxPassword: []byte("cdcdcdcd"),
|
||||||
BridgePassword: "wew123",
|
BridgePassword: "wew123",
|
||||||
Version: "k11",
|
Version: "k11",
|
||||||
Timestamp: 152469263742,
|
Timestamp: 152469263742,
|
||||||
|
|||||||
@ -108,7 +108,7 @@ func (m *MockCredentialsStorer) EXPECT() *MockCredentialsStorerMockRecorder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add mocks base method
|
// Add mocks base method
|
||||||
func (m *MockCredentialsStorer) Add(arg0, arg1, arg2, arg3, arg4 string, arg5 []string) (*credentials.Credentials, error) {
|
func (m *MockCredentialsStorer) Add(arg0, arg1, arg2, arg3 string, arg4 []byte, arg5 []string) (*credentials.Credentials, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "Add", arg0, arg1, arg2, arg3, arg4, arg5)
|
ret := m.ctrl.Call(m, "Add", arg0, arg1, arg2, arg3, arg4, arg5)
|
||||||
ret0, _ := ret[0].(*credentials.Credentials)
|
ret0, _ := ret[0].(*credentials.Credentials)
|
||||||
@ -212,7 +212,7 @@ func (mr *MockCredentialsStorerMockRecorder) UpdateEmails(arg0, arg1 interface{}
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UpdatePassword mocks base method
|
// UpdatePassword mocks base method
|
||||||
func (m *MockCredentialsStorer) UpdatePassword(arg0, arg1 string) (*credentials.Credentials, error) {
|
func (m *MockCredentialsStorer) UpdatePassword(arg0 string, arg1 []byte) (*credentials.Credentials, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "UpdatePassword", arg0, arg1)
|
ret := m.ctrl.Call(m, "UpdatePassword", arg0, arg1)
|
||||||
ret0, _ := ret[0].(*credentials.Credentials)
|
ret0, _ := ret[0].(*credentials.Credentials)
|
||||||
|
|||||||
@ -32,11 +32,11 @@ type PanicHandler interface {
|
|||||||
|
|
||||||
type CredentialsStorer interface {
|
type CredentialsStorer interface {
|
||||||
List() (userIDs []string, err error)
|
List() (userIDs []string, err error)
|
||||||
Add(userID, userName, uid, ref, mailboxPassword string, emails []string) (*credentials.Credentials, error)
|
Add(userID, userName, uid, ref string, mailboxPassword []byte, emails []string) (*credentials.Credentials, error)
|
||||||
Get(userID string) (*credentials.Credentials, error)
|
Get(userID string) (*credentials.Credentials, error)
|
||||||
SwitchAddressMode(userID string) (*credentials.Credentials, error)
|
SwitchAddressMode(userID string) (*credentials.Credentials, error)
|
||||||
UpdateEmails(userID string, emails []string) (*credentials.Credentials, error)
|
UpdateEmails(userID string, emails []string) (*credentials.Credentials, error)
|
||||||
UpdatePassword(userID, password string) (*credentials.Credentials, error)
|
UpdatePassword(userID string, password []byte) (*credentials.Credentials, error)
|
||||||
UpdateToken(userID, uid, ref string) (*credentials.Credentials, error)
|
UpdateToken(userID, uid, ref string) (*credentials.Credentials, error)
|
||||||
Logout(userID string) (*credentials.Credentials, error)
|
Logout(userID string) (*credentials.Credentials, error)
|
||||||
Delete(userID string) error
|
Delete(userID string) error
|
||||||
|
|||||||
@ -227,7 +227,7 @@ func (u *User) unlockIfNecessary() error {
|
|||||||
// client. Unlock should only finish unlocking when connection is back up.
|
// client. Unlock should only finish unlocking when connection is back up.
|
||||||
// That means it should try it fast enough and not retry if connection
|
// That means it should try it fast enough and not retry if connection
|
||||||
// is still down.
|
// is still down.
|
||||||
err := u.client.Unlock(pmapi.ContextWithoutRetry(context.Background()), []byte(u.creds.MailboxPassword))
|
err := u.client.Unlock(pmapi.ContextWithoutRetry(context.Background()), u.creds.MailboxPassword)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -364,7 +364,7 @@ func (u *User) UpdateUser(ctx context.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := u.client.ReloadKeys(ctx, []byte(u.creds.MailboxPassword)); err != nil {
|
if err := u.client.ReloadKeys(ctx, u.creds.MailboxPassword); err != nil {
|
||||||
return errors.Wrap(err, "failed to reload keys")
|
return errors.Wrap(err, "failed to reload keys")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -37,7 +37,7 @@ func TestUpdateUser(t *testing.T) {
|
|||||||
|
|
||||||
gomock.InOrder(
|
gomock.InOrder(
|
||||||
m.pmapiClient.EXPECT().UpdateUser(gomock.Any()).Return(nil, nil),
|
m.pmapiClient.EXPECT().UpdateUser(gomock.Any()).Return(nil, nil),
|
||||||
m.pmapiClient.EXPECT().ReloadKeys(gomock.Any(), []byte(testCredentials.MailboxPassword)).Return(nil),
|
m.pmapiClient.EXPECT().ReloadKeys(gomock.Any(), testCredentials.MailboxPassword).Return(nil),
|
||||||
m.pmapiClient.EXPECT().Addresses().Return([]*pmapi.Address{testPMAPIAddress}),
|
m.pmapiClient.EXPECT().Addresses().Return([]*pmapi.Address{testPMAPIAddress}),
|
||||||
|
|
||||||
m.credentialsStore.EXPECT().UpdateEmails("user", []string{testPMAPIAddress.Email}).Return(testCredentials, nil),
|
m.credentialsStore.EXPECT().UpdateEmails("user", []string{testPMAPIAddress.Email}).Return(testCredentials, nil),
|
||||||
|
|||||||
@ -46,7 +46,7 @@ func TestNewUserUnlockFails(t *testing.T) {
|
|||||||
m.credentialsStore.EXPECT().Get("user").Return(testCredentials, nil),
|
m.credentialsStore.EXPECT().Get("user").Return(testCredentials, nil),
|
||||||
m.pmapiClient.EXPECT().AddAuthRefreshHandler(gomock.Any()),
|
m.pmapiClient.EXPECT().AddAuthRefreshHandler(gomock.Any()),
|
||||||
m.pmapiClient.EXPECT().IsUnlocked().Return(false),
|
m.pmapiClient.EXPECT().IsUnlocked().Return(false),
|
||||||
m.pmapiClient.EXPECT().Unlock(gomock.Any(), []byte(testCredentials.MailboxPassword)).Return(errors.New("bad password")),
|
m.pmapiClient.EXPECT().Unlock(gomock.Any(), testCredentials.MailboxPassword).Return(errors.New("bad password")),
|
||||||
|
|
||||||
// Handle of unlock error.
|
// Handle of unlock error.
|
||||||
m.pmapiClient.EXPECT().AuthDelete(gomock.Any()).Return(nil),
|
m.pmapiClient.EXPECT().AuthDelete(gomock.Any()).Return(nil),
|
||||||
|
|||||||
@ -200,14 +200,14 @@ func (u *Users) closeAllConnections() {
|
|||||||
|
|
||||||
// Login authenticates a user by username/password, returning an authorised client and an auth object.
|
// Login authenticates a user by username/password, returning an authorised client and an auth object.
|
||||||
// The authorisation scope may not yet be full if the user has 2FA enabled.
|
// The authorisation scope may not yet be full if the user has 2FA enabled.
|
||||||
func (u *Users) Login(username, password string) (authClient pmapi.Client, auth *pmapi.Auth, err error) {
|
func (u *Users) Login(username string, password []byte) (authClient pmapi.Client, auth *pmapi.Auth, err error) {
|
||||||
u.crashBandicoot(username)
|
u.crashBandicoot(username)
|
||||||
|
|
||||||
return u.clientManager.NewClientWithLogin(context.Background(), username, password)
|
return u.clientManager.NewClientWithLogin(context.Background(), username, password)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FinishLogin finishes the login procedure and adds the user into the credentials store.
|
// FinishLogin finishes the login procedure and adds the user into the credentials store.
|
||||||
func (u *Users) FinishLogin(client pmapi.Client, auth *pmapi.Auth, password string) (user *User, err error) { //nolint[funlen]
|
func (u *Users) FinishLogin(client pmapi.Client, auth *pmapi.Auth, password []byte) (user *User, err error) { //nolint[funlen]
|
||||||
apiUser, passphrase, err := getAPIUser(context.Background(), client, password)
|
apiUser, passphrase, err := getAPIUser(context.Background(), client, password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -228,7 +228,7 @@ func (u *Users) FinishLogin(client pmapi.Client, auth *pmapi.Auth, password stri
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update the password in case the user changed it.
|
// Update the password in case the user changed it.
|
||||||
creds, err := u.credStorer.UpdatePassword(apiUser.ID, string(passphrase))
|
creds, err := u.credStorer.UpdatePassword(apiUser.ID, passphrase)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "failed to update password of user in credentials store")
|
return nil, errors.Wrap(err, "failed to update password of user in credentials store")
|
||||||
}
|
}
|
||||||
@ -264,7 +264,7 @@ func (u *Users) addNewUser(client pmapi.Client, apiUser *pmapi.User, auth *pmapi
|
|||||||
emails = client.Addresses().AllEmails()
|
emails = client.Addresses().AllEmails()
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := u.credStorer.Add(apiUser.ID, apiUser.Name, auth.UID, auth.RefreshToken, string(passphrase), emails); err != nil {
|
if _, err := u.credStorer.Add(apiUser.ID, apiUser.Name, auth.UID, auth.RefreshToken, passphrase, emails); err != nil {
|
||||||
return errors.Wrap(err, "failed to add user credentials to credentials store")
|
return errors.Wrap(err, "failed to add user credentials to credentials store")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -286,7 +286,7 @@ func (u *Users) addNewUser(client pmapi.Client, apiUser *pmapi.User, auth *pmapi
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAPIUser(ctx context.Context, client pmapi.Client, password string) (*pmapi.User, []byte, error) {
|
func getAPIUser(ctx context.Context, client pmapi.Client, password []byte) (*pmapi.User, []byte, error) {
|
||||||
salt, err := client.AuthSalt(ctx)
|
salt, err := client.AuthSalt(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, errors.Wrap(err, "failed to get salt")
|
return nil, nil, errors.Wrap(err, "failed to get salt")
|
||||||
|
|||||||
@ -37,7 +37,7 @@ func TestUsersFinishLoginBadMailboxPassword(t *testing.T) {
|
|||||||
|
|
||||||
// Set up mocks for FinishLogin.
|
// Set up mocks for FinishLogin.
|
||||||
m.pmapiClient.EXPECT().AuthSalt(gomock.Any()).Return("", nil)
|
m.pmapiClient.EXPECT().AuthSalt(gomock.Any()).Return("", nil)
|
||||||
m.pmapiClient.EXPECT().Unlock(gomock.Any(), []byte(testCredentials.MailboxPassword)).Return(errors.New("no keys could be unlocked"))
|
m.pmapiClient.EXPECT().Unlock(gomock.Any(), testCredentials.MailboxPassword).Return(errors.New("no keys could be unlocked"))
|
||||||
|
|
||||||
checkUsersFinishLogin(t, m, testAuthRefresh, testCredentials.MailboxPassword, "", ErrWrongMailboxPassword)
|
checkUsersFinishLogin(t, m, testAuthRefresh, testCredentials.MailboxPassword, "", ErrWrongMailboxPassword)
|
||||||
}
|
}
|
||||||
@ -69,7 +69,7 @@ func TestUsersFinishLoginExistingDisconnectedUser(t *testing.T) {
|
|||||||
// Mock process of FinishLogin of already added user.
|
// Mock process of FinishLogin of already added user.
|
||||||
gomock.InOrder(
|
gomock.InOrder(
|
||||||
m.pmapiClient.EXPECT().AuthSalt(gomock.Any()).Return("", nil),
|
m.pmapiClient.EXPECT().AuthSalt(gomock.Any()).Return("", nil),
|
||||||
m.pmapiClient.EXPECT().Unlock(gomock.Any(), []byte(testCredentials.MailboxPassword)).Return(nil),
|
m.pmapiClient.EXPECT().Unlock(gomock.Any(), testCredentials.MailboxPassword).Return(nil),
|
||||||
m.pmapiClient.EXPECT().CurrentUser(gomock.Any()).Return(testPMAPIUserDisconnected, nil),
|
m.pmapiClient.EXPECT().CurrentUser(gomock.Any()).Return(testPMAPIUserDisconnected, nil),
|
||||||
m.credentialsStore.EXPECT().UpdateToken(testCredentialsDisconnected.UserID, testAuthRefresh.UID, testAuthRefresh.RefreshToken).Return(testCredentials, nil),
|
m.credentialsStore.EXPECT().UpdateToken(testCredentialsDisconnected.UserID, testAuthRefresh.UID, testAuthRefresh.RefreshToken).Return(testCredentials, nil),
|
||||||
m.credentialsStore.EXPECT().UpdatePassword(testCredentialsDisconnected.UserID, testCredentials.MailboxPassword).Return(testCredentials, nil),
|
m.credentialsStore.EXPECT().UpdatePassword(testCredentialsDisconnected.UserID, testCredentials.MailboxPassword).Return(testCredentials, nil),
|
||||||
@ -101,7 +101,7 @@ func TestUsersFinishLoginConnectedUser(t *testing.T) {
|
|||||||
// Mock process of FinishLogin of already connected user.
|
// Mock process of FinishLogin of already connected user.
|
||||||
gomock.InOrder(
|
gomock.InOrder(
|
||||||
m.pmapiClient.EXPECT().AuthSalt(gomock.Any()).Return("", nil),
|
m.pmapiClient.EXPECT().AuthSalt(gomock.Any()).Return("", nil),
|
||||||
m.pmapiClient.EXPECT().Unlock(gomock.Any(), []byte(testCredentials.MailboxPassword)).Return(nil),
|
m.pmapiClient.EXPECT().Unlock(gomock.Any(), testCredentials.MailboxPassword).Return(nil),
|
||||||
m.pmapiClient.EXPECT().CurrentUser(gomock.Any()).Return(testPMAPIUser, nil),
|
m.pmapiClient.EXPECT().CurrentUser(gomock.Any()).Return(testPMAPIUser, nil),
|
||||||
m.pmapiClient.EXPECT().AuthDelete(gomock.Any()).Return(nil),
|
m.pmapiClient.EXPECT().AuthDelete(gomock.Any()).Return(nil),
|
||||||
)
|
)
|
||||||
@ -113,7 +113,7 @@ func TestUsersFinishLoginConnectedUser(t *testing.T) {
|
|||||||
r.EqualError(t, err, "user is already connected")
|
r.EqualError(t, err, "user is already connected")
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkUsersFinishLogin(t *testing.T, m mocks, auth *pmapi.Auth, mailboxPassword string, expectedUserID string, expectedErr error) {
|
func checkUsersFinishLogin(t *testing.T, m mocks, auth *pmapi.Auth, mailboxPassword []byte, expectedUserID string, expectedErr error) {
|
||||||
users := testNewUsers(t, m)
|
users := testNewUsers(t, m)
|
||||||
defer cleanUpUsersData(users)
|
defer cleanUpUsersData(users)
|
||||||
|
|
||||||
|
|||||||
@ -84,7 +84,7 @@ func TestNewUsersWithConnectedUserWithBadToken(t *testing.T) {
|
|||||||
m.clientManager.EXPECT().NewClient("uid", "", "acc", time.Time{}).Return(m.pmapiClient)
|
m.clientManager.EXPECT().NewClient("uid", "", "acc", time.Time{}).Return(m.pmapiClient)
|
||||||
m.pmapiClient.EXPECT().AddAuthRefreshHandler(gomock.Any())
|
m.pmapiClient.EXPECT().AddAuthRefreshHandler(gomock.Any())
|
||||||
m.pmapiClient.EXPECT().IsUnlocked().Return(false)
|
m.pmapiClient.EXPECT().IsUnlocked().Return(false)
|
||||||
m.pmapiClient.EXPECT().Unlock(gomock.Any(), []byte(testCredentials.MailboxPassword)).Return(errors.New("not authorized"))
|
m.pmapiClient.EXPECT().Unlock(gomock.Any(), testCredentials.MailboxPassword).Return(errors.New("not authorized"))
|
||||||
m.pmapiClient.EXPECT().AuthDelete(gomock.Any())
|
m.pmapiClient.EXPECT().AuthDelete(gomock.Any())
|
||||||
|
|
||||||
m.credentialsStore.EXPECT().List().Return([]string{"user"}, nil)
|
m.credentialsStore.EXPECT().List().Return([]string{"user"}, nil)
|
||||||
|
|||||||
@ -63,7 +63,7 @@ var (
|
|||||||
Name: "username",
|
Name: "username",
|
||||||
Emails: "user@pm.me",
|
Emails: "user@pm.me",
|
||||||
APIToken: "uid:acc",
|
APIToken: "uid:acc",
|
||||||
MailboxPassword: "pass",
|
MailboxPassword: []byte("pass"),
|
||||||
BridgePassword: "0123456789abcdef",
|
BridgePassword: "0123456789abcdef",
|
||||||
Version: "v1",
|
Version: "v1",
|
||||||
Timestamp: 123456789,
|
Timestamp: 123456789,
|
||||||
@ -76,7 +76,7 @@ var (
|
|||||||
Name: "usersname",
|
Name: "usersname",
|
||||||
Emails: "users@pm.me;anotheruser@pm.me;alsouser@pm.me",
|
Emails: "users@pm.me;anotheruser@pm.me;alsouser@pm.me",
|
||||||
APIToken: "uid:acc",
|
APIToken: "uid:acc",
|
||||||
MailboxPassword: "pass",
|
MailboxPassword: []byte("pass"),
|
||||||
BridgePassword: "0123456789abcdef",
|
BridgePassword: "0123456789abcdef",
|
||||||
Version: "v1",
|
Version: "v1",
|
||||||
Timestamp: 123456789,
|
Timestamp: 123456789,
|
||||||
@ -89,7 +89,7 @@ var (
|
|||||||
Name: "username",
|
Name: "username",
|
||||||
Emails: "user@pm.me",
|
Emails: "user@pm.me",
|
||||||
APIToken: "",
|
APIToken: "",
|
||||||
MailboxPassword: "",
|
MailboxPassword: []byte{},
|
||||||
BridgePassword: "0123456789abcdef",
|
BridgePassword: "0123456789abcdef",
|
||||||
Version: "v1",
|
Version: "v1",
|
||||||
Timestamp: 123456789,
|
Timestamp: 123456789,
|
||||||
@ -102,7 +102,7 @@ var (
|
|||||||
Name: "usersname",
|
Name: "usersname",
|
||||||
Emails: "users@pm.me;anotheruser@pm.me;alsouser@pm.me",
|
Emails: "users@pm.me;anotheruser@pm.me;alsouser@pm.me",
|
||||||
APIToken: "",
|
APIToken: "",
|
||||||
MailboxPassword: "",
|
MailboxPassword: []byte{},
|
||||||
BridgePassword: "0123456789abcdef",
|
BridgePassword: "0123456789abcdef",
|
||||||
Version: "v1",
|
Version: "v1",
|
||||||
Timestamp: 123456789,
|
Timestamp: 123456789,
|
||||||
@ -249,7 +249,7 @@ func mockAddingConnectedUser(m mocks) {
|
|||||||
gomock.InOrder(
|
gomock.InOrder(
|
||||||
// Mock of users.FinishLogin.
|
// Mock of users.FinishLogin.
|
||||||
m.pmapiClient.EXPECT().AuthSalt(gomock.Any()).Return("", nil),
|
m.pmapiClient.EXPECT().AuthSalt(gomock.Any()).Return("", nil),
|
||||||
m.pmapiClient.EXPECT().Unlock(gomock.Any(), []byte(testCredentials.MailboxPassword)).Return(nil),
|
m.pmapiClient.EXPECT().Unlock(gomock.Any(), testCredentials.MailboxPassword).Return(nil),
|
||||||
m.pmapiClient.EXPECT().CurrentUser(gomock.Any()).Return(testPMAPIUser, nil),
|
m.pmapiClient.EXPECT().CurrentUser(gomock.Any()).Return(testPMAPIUser, nil),
|
||||||
m.pmapiClient.EXPECT().Addresses().Return([]*pmapi.Address{testPMAPIAddress}),
|
m.pmapiClient.EXPECT().Addresses().Return([]*pmapi.Address{testPMAPIAddress}),
|
||||||
m.credentialsStore.EXPECT().Add("user", "username", testAuthRefresh.UID, testAuthRefresh.RefreshToken, testCredentials.MailboxPassword, []string{testPMAPIAddress.Email}).Return(testCredentials, nil),
|
m.credentialsStore.EXPECT().Add("user", "username", testAuthRefresh.UID, testAuthRefresh.RefreshToken, testCredentials.MailboxPassword, []string{testPMAPIAddress.Email}).Return(testCredentials, nil),
|
||||||
|
|||||||
@ -33,16 +33,24 @@ import (
|
|||||||
|
|
||||||
pmmime "github.com/ProtonMail/proton-bridge/pkg/mime"
|
pmmime "github.com/ProtonMail/proton-bridge/pkg/mime"
|
||||||
|
|
||||||
a "github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/require"
|
||||||
r "github.com/stretchr/testify/require"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const testAttachmentCleartext = `cc,
|
||||||
|
dille.
|
||||||
|
`
|
||||||
|
|
||||||
|
// Attachment cleartext encrypted with testPrivateKeyRing.
|
||||||
|
const testKeyPacket = `wcBMA0fcZ7XLgmf2AQf/cHhfDRM9zlIuBi+h2W6DKjbbyIHMkgF6ER3JEvn/tSruUH8KTGt0N7Z+a80FFMCuXn1Y1I/nW7MVrNhGuJZAF4OymD8ugvuoAMIQX0eCYEpPXzRIWJBZg82AuowmFMsv8Dgvq4bTZq4cttI3CZcxKUNXuAearmNpmgplUKWj5USmRXK4iGB3VFGjidXkxbElrP4fD5A/rfEZ5aJgCsegqcXxX3MEjWXi9pFzgd/9phOvl1ZFm9U9hNoVAW3QsgmVeihnKaDZUyf2Qsigij21QKAUxw9U3y89eTUIqZAcmIgqeDujA3RWBgJwjtY/lOyhEmkf3AWKzehvf1xtJmCWDg==`
|
||||||
|
const testDataPacket = `0ksB6S4f4l8C1NB8yzmd/jNi0xqEZsyTDLdTP+N4Qxh3NZjla+yGRvC9rGmoUL7XVyowsG/GKTf2LXF/5E5FkX/3WMYwIv1n11ExyAE=`
|
||||||
|
|
||||||
var testAttachment = &Attachment{
|
var testAttachment = &Attachment{
|
||||||
ID: "y6uKIlc2HdoHPAwPSrvf7dXoZNMYvBgxshYUN67cY5DJjL2O8NYewuvGHcYvCfd8LpEoAI_GdymO0Jr0mHlsEw==",
|
ID: "y6uKIlc2HdoHPAwPSrvf7dXoZNMYvBgxshYUN67cY5DJjL2O8NYewuvGHcYvCfd8LpEoAI_GdymO0Jr0mHlsEw==",
|
||||||
Name: "croutonmail.txt",
|
Name: "croutonmail.txt",
|
||||||
Size: 77,
|
Size: 77,
|
||||||
MIMEType: "text/plain",
|
MIMEType: "text/plain",
|
||||||
KeyPackets: "wcBMA0fcZ7XLgmf2AQgAiRsOlnm1kSB4/lr7tYe6pBsRGn10GqwUhrwU5PMKOHdCgnO12jO3y3CzP0Yl/jGhAYja9wLDqH8X0sk3tY32u4Sb1Qe5IuzggAiCa4dwOJj5gEFMTHMzjIMPHR7A70XqUxMhmILye8V4KRm/j4c1sxbzA1rM3lYBumQuB5l/ck0Kgt4ZqxHVXHK5Q1l65FHhSXRj8qnunasHa30TYNzP8nmBA8BinnJxpiQ7FGc2umnUhgkFtjm5ixu9vyjr9ukwDTbwAXXfmY+o7tK7kqIXJcmTL6k2UeC6Mz1AagQtRCRtU+bv/3zGojq/trZo9lom3naIeQYa36Ketmcpj2Qwjg==",
|
KeyPackets: testKeyPacket,
|
||||||
|
|
||||||
Header: textproto.MIMEHeader{
|
Header: textproto.MIMEHeader{
|
||||||
"Content-Description": {"You'll never believe what's in this text file"},
|
"Content-Description": {"You'll never believe what's in this text file"},
|
||||||
"X-Mailer": {"Microsoft Outlook 15.0", "Microsoft Live Mail 42.0"},
|
"X-Mailer": {"Microsoft Outlook 15.0", "Microsoft Live Mail 42.0"},
|
||||||
@ -50,12 +58,13 @@ var testAttachment = &Attachment{
|
|||||||
MessageID: "h3CD-DT7rLoAw1vmpcajvIPAl-wwDfXR2MHtWID3wuQURDBKTiGUAwd6E2WBbS44QQKeXImW-axm6X0hAfcVCA==",
|
MessageID: "h3CD-DT7rLoAw1vmpcajvIPAl-wwDfXR2MHtWID3wuQURDBKTiGUAwd6E2WBbS44QQKeXImW-axm6X0hAfcVCA==",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Part of GET /mail/messages/{id} response from server.
|
||||||
const testAttachmentJSON = `{
|
const testAttachmentJSON = `{
|
||||||
"ID": "y6uKIlc2HdoHPAwPSrvf7dXoZNMYvBgxshYUN67cY5DJjL2O8NYewuvGHcYvCfd8LpEoAI_GdymO0Jr0mHlsEw==",
|
"ID": "y6uKIlc2HdoHPAwPSrvf7dXoZNMYvBgxshYUN67cY5DJjL2O8NYewuvGHcYvCfd8LpEoAI_GdymO0Jr0mHlsEw==",
|
||||||
"Name": "croutonmail.txt",
|
"Name": "croutonmail.txt",
|
||||||
"Size": 77,
|
"Size": 77,
|
||||||
"MIMEType": "text/plain",
|
"MIMEType": "text/plain",
|
||||||
"KeyPackets": "wcBMA0fcZ7XLgmf2AQgAiRsOlnm1kSB4/lr7tYe6pBsRGn10GqwUhrwU5PMKOHdCgnO12jO3y3CzP0Yl/jGhAYja9wLDqH8X0sk3tY32u4Sb1Qe5IuzggAiCa4dwOJj5gEFMTHMzjIMPHR7A70XqUxMhmILye8V4KRm/j4c1sxbzA1rM3lYBumQuB5l/ck0Kgt4ZqxHVXHK5Q1l65FHhSXRj8qnunasHa30TYNzP8nmBA8BinnJxpiQ7FGc2umnUhgkFtjm5ixu9vyjr9ukwDTbwAXXfmY+o7tK7kqIXJcmTL6k2UeC6Mz1AagQtRCRtU+bv/3zGojq/trZo9lom3naIeQYa36Ketmcpj2Qwjg==",
|
"KeyPackets": "` + testKeyPacket + `",
|
||||||
"Headers": {
|
"Headers": {
|
||||||
"content-description": "You'll never believe what's in this text file",
|
"content-description": "You'll never believe what's in this text file",
|
||||||
"x-mailer": [
|
"x-mailer": [
|
||||||
@ -66,68 +75,66 @@ const testAttachmentJSON = `{
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
const testAttachmentCleartext = `cc,
|
// POST /mail/attachment/ response from server.
|
||||||
dille.
|
const testCreatedAttachmentBody = `{
|
||||||
`
|
|
||||||
|
|
||||||
const testAttachmentEncrypted = `wcBMA0fcZ7XLgmf2AQf/cHhfDRM9zlIuBi+h2W6DKjbbyIHMkgF6ER3JEvn/tSruUH8KTGt0N7Z+a80FFMCuXn1Y1I/nW7MVrNhGuJZAF4OymD8ugvuoAMIQX0eCYEpPXzRIWJBZg82AuowmFMsv8Dgvq4bTZq4cttI3CZcxKUNXuAearmNpmgplUKWj5USmRXK4iGB3VFGjidXkxbElrP4fD5A/rfEZ5aJgCsegqcXxX3MEjWXi9pFzgd/9phOvl1ZFm9U9hNoVAW3QsgmVeihnKaDZUyf2Qsigij21QKAUxw9U3y89eTUIqZAcmIgqeDujA3RWBgJwjtY/lOyhEmkf3AWKzehvf1xtJmCWDtJLAekuH+JfAtTQfMs5nf4zYtMahGbMkwy3Uz/jeEMYdzWY5WvshkbwvaxpqFC+11cqMLBvxik39i1xf+RORZF/91jGMCL9Z9dRMcgB`
|
|
||||||
|
|
||||||
const testCreateAttachmentBody = `{
|
|
||||||
"Code": 1000,
|
"Code": 1000,
|
||||||
"Attachment": {"ID": "y6uKIlc2HdoHPAwPSrvf7dXoZNMYvBgxshYUN67cY5DJjL2O8NYewuvGHcYvCfd8LpEoAI_GdymO0Jr0mHlsEw=="}
|
"Attachment": {"ID": "y6uKIlc2HdoHPAwPSrvf7dXoZNMYvBgxshYUN67cY5DJjL2O8NYewuvGHcYvCfd8LpEoAI_GdymO0Jr0mHlsEw=="}
|
||||||
}`
|
}`
|
||||||
|
|
||||||
func TestAttachment_UnmarshalJSON(t *testing.T) {
|
func TestAttachment_UnmarshalJSON(t *testing.T) {
|
||||||
|
r := require.New(t)
|
||||||
att := new(Attachment)
|
att := new(Attachment)
|
||||||
err := json.Unmarshal([]byte(testAttachmentJSON), att)
|
err := json.Unmarshal([]byte(testAttachmentJSON), att)
|
||||||
r.NoError(t, err)
|
r.NoError(err)
|
||||||
|
|
||||||
att.MessageID = testAttachment.MessageID // This isn't in the JSON object
|
att.MessageID = testAttachment.MessageID // This isn't in the server response
|
||||||
|
|
||||||
r.Equal(t, testAttachment, att)
|
r.Equal(testAttachment, att)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestClient_CreateAttachment(t *testing.T) {
|
func TestClient_CreateAttachment(t *testing.T) {
|
||||||
|
r := require.New(t)
|
||||||
s, c := newTestClient(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
s, c := newTestClient(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||||
r.NoError(t, checkMethodAndPath(req, "POST", "/mail/v4/attachments"))
|
r.NoError(checkMethodAndPath(req, "POST", "/mail/v4/attachments"))
|
||||||
|
|
||||||
contentType, params, err := pmmime.ParseMediaType(req.Header.Get("Content-Type"))
|
contentType, params, err := pmmime.ParseMediaType(req.Header.Get("Content-Type"))
|
||||||
r.NoError(t, err)
|
r.NoError(err)
|
||||||
r.Equal(t, "multipart/form-data", contentType)
|
r.Equal("multipart/form-data", contentType)
|
||||||
|
|
||||||
mr := multipart.NewReader(req.Body, params["boundary"])
|
mr := multipart.NewReader(req.Body, params["boundary"])
|
||||||
form, err := mr.ReadForm(10 * 1024)
|
form, err := mr.ReadForm(10 * 1024)
|
||||||
r.NoError(t, err)
|
r.NoError(err)
|
||||||
defer r.NoError(t, form.RemoveAll())
|
defer r.NoError(form.RemoveAll())
|
||||||
|
|
||||||
r.Equal(t, testAttachment.Name, form.Value["Filename"][0])
|
r.Equal(testAttachment.Name, form.Value["Filename"][0])
|
||||||
r.Equal(t, testAttachment.MessageID, form.Value["MessageID"][0])
|
r.Equal(testAttachment.MessageID, form.Value["MessageID"][0])
|
||||||
r.Equal(t, testAttachment.MIMEType, form.Value["MIMEType"][0])
|
r.Equal(testAttachment.MIMEType, form.Value["MIMEType"][0])
|
||||||
|
|
||||||
dataFile, err := form.File["DataPacket"][0].Open()
|
dataFile, err := form.File["DataPacket"][0].Open()
|
||||||
r.NoError(t, err)
|
r.NoError(err)
|
||||||
defer r.NoError(t, dataFile.Close())
|
defer r.NoError(dataFile.Close())
|
||||||
|
|
||||||
b, err := ioutil.ReadAll(dataFile)
|
b, err := ioutil.ReadAll(dataFile)
|
||||||
r.NoError(t, err)
|
r.NoError(err)
|
||||||
r.Equal(t, testAttachmentCleartext, string(b))
|
r.Equal(testAttachmentCleartext, string(b))
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|
||||||
fmt.Fprint(w, testCreateAttachmentBody)
|
fmt.Fprint(w, testCreatedAttachmentBody)
|
||||||
}))
|
}))
|
||||||
defer s.Close()
|
defer s.Close()
|
||||||
|
|
||||||
reader := strings.NewReader(testAttachmentCleartext) // In reality, this thing is encrypted
|
reader := strings.NewReader(testAttachmentCleartext) // In reality, this thing is encrypted
|
||||||
created, err := c.CreateAttachment(context.Background(), testAttachment, reader, strings.NewReader(""))
|
created, err := c.CreateAttachment(context.Background(), testAttachment, reader, strings.NewReader(""))
|
||||||
r.NoError(t, err)
|
r.NoError(err)
|
||||||
|
|
||||||
r.Equal(t, testAttachment.ID, created.ID)
|
r.Equal(testAttachment.ID, created.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestClient_GetAttachment(t *testing.T) {
|
func TestClient_GetAttachment(t *testing.T) {
|
||||||
|
r := require.New(t)
|
||||||
s, c := newTestClient(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
s, c := newTestClient(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||||
r.NoError(t, checkMethodAndPath(req, "GET", "/mail/v4/attachments/"+testAttachment.ID))
|
r.NoError(checkMethodAndPath(req, "GET", "/mail/v4/attachments/"+testAttachment.ID))
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
fmt.Fprint(w, testAttachmentCleartext)
|
fmt.Fprint(w, testAttachmentCleartext)
|
||||||
@ -135,39 +142,61 @@ func TestClient_GetAttachment(t *testing.T) {
|
|||||||
defer s.Close()
|
defer s.Close()
|
||||||
|
|
||||||
att, err := c.GetAttachment(context.Background(), testAttachment.ID)
|
att, err := c.GetAttachment(context.Background(), testAttachment.ID)
|
||||||
r.NoError(t, err)
|
r.NoError(err)
|
||||||
defer att.Close() //nolint[errcheck]
|
defer att.Close() //nolint[errcheck]
|
||||||
|
|
||||||
// In reality, r contains encrypted data
|
// In reality, r contains encrypted data
|
||||||
b, err := ioutil.ReadAll(att)
|
b, err := ioutil.ReadAll(att)
|
||||||
r.NoError(t, err)
|
r.NoError(err)
|
||||||
|
|
||||||
r.Equal(t, testAttachmentCleartext, string(b))
|
r.Equal(testAttachmentCleartext, string(b))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAttachment_Encrypt(t *testing.T) {
|
func TestAttachmentDecrypt(t *testing.T) {
|
||||||
data := bytes.NewBufferString(testAttachmentCleartext)
|
r := require.New(t)
|
||||||
r, err := testAttachment.Encrypt(testPublicKeyRing, data)
|
|
||||||
a.Nil(t, err)
|
|
||||||
b, err := ioutil.ReadAll(r)
|
|
||||||
a.Nil(t, err)
|
|
||||||
|
|
||||||
// Result is always different, so the best way is to test it by decrypting again.
|
rawKeyPacket, err := base64.StdEncoding.DecodeString(testKeyPacket)
|
||||||
// Another test for decrypting will help us to be sure it's working.
|
r.NoError(err)
|
||||||
dataEnc := bytes.NewBuffer(b)
|
|
||||||
decryptAndCheck(t, dataEnc)
|
rawDataPacket, err := base64.StdEncoding.DecodeString(testDataPacket)
|
||||||
|
r.NoError(err)
|
||||||
|
|
||||||
|
decryptAndCheck(r, bytes.NewBuffer(append(rawKeyPacket, rawDataPacket...)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAttachment_Decrypt(t *testing.T) {
|
func TestAttachmentEncrypt(t *testing.T) {
|
||||||
dataBytes, _ := base64.StdEncoding.DecodeString(testAttachmentEncrypted)
|
r := require.New(t)
|
||||||
dataReader := bytes.NewBuffer(dataBytes)
|
|
||||||
decryptAndCheck(t, dataReader)
|
encryptedReader, err := testAttachment.Encrypt(
|
||||||
|
testPublicKeyRing,
|
||||||
|
bytes.NewBufferString(testAttachmentCleartext),
|
||||||
|
)
|
||||||
|
r.NoError(err)
|
||||||
|
|
||||||
|
// The result is always different due to session key. The best way is to
|
||||||
|
// test result of encryption by decrypting again acn coparet to cleartext.
|
||||||
|
decryptAndCheck(r, encryptedReader)
|
||||||
}
|
}
|
||||||
|
|
||||||
func decryptAndCheck(t *testing.T, data io.Reader) {
|
func decryptAndCheck(r *require.Assertions, data io.Reader) {
|
||||||
r, err := testAttachment.Decrypt(data, testPrivateKeyRing)
|
// First separate KeyPacket from encrypted data. In our case keypacket
|
||||||
a.Nil(t, err)
|
// has 271 bytes.
|
||||||
b, err := ioutil.ReadAll(r)
|
raw, err := ioutil.ReadAll(data)
|
||||||
a.Nil(t, err)
|
r.NoError(err)
|
||||||
a.Equal(t, testAttachmentCleartext, string(b))
|
rawKeyPacket := raw[:271]
|
||||||
|
rawDataPacket := raw[271:]
|
||||||
|
|
||||||
|
// KeyPacket is retrieve by get GET /mail/messages/{id}
|
||||||
|
haveAttachment := &Attachment{
|
||||||
|
KeyPackets: base64.StdEncoding.EncodeToString(rawKeyPacket),
|
||||||
|
}
|
||||||
|
|
||||||
|
// DataPacket is received from GET /mail/attachments/{id}
|
||||||
|
decryptedReader, err := haveAttachment.Decrypt(bytes.NewBuffer(rawDataPacket), testPrivateKeyRing)
|
||||||
|
r.NoError(err)
|
||||||
|
|
||||||
|
b, err := ioutil.ReadAll(decryptedReader)
|
||||||
|
r.NoError(err)
|
||||||
|
|
||||||
|
r.Equal(testAttachmentCleartext, string(b))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,7 +22,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ProtonMail/gopenpgp/v2/crypto"
|
"github.com/ProtonMail/gopenpgp/v2/crypto"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func loadPMKeys(jsonKeys string) (keys *PMKeys) {
|
func loadPMKeys(jsonKeys string) (keys *PMKeys) {
|
||||||
@ -31,6 +31,7 @@ func loadPMKeys(jsonKeys string) (keys *PMKeys) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestPMKeys_GetKeyRingAndUnlock(t *testing.T) {
|
func TestPMKeys_GetKeyRingAndUnlock(t *testing.T) {
|
||||||
|
r := require.New(t)
|
||||||
addrKeysWithTokens := loadPMKeys(readTestFile("keyring_addressKeysWithTokens_JSON", false))
|
addrKeysWithTokens := loadPMKeys(readTestFile("keyring_addressKeysWithTokens_JSON", false))
|
||||||
addrKeysWithoutTokens := loadPMKeys(readTestFile("keyring_addressKeysWithoutTokens_JSON", false))
|
addrKeysWithoutTokens := loadPMKeys(readTestFile("keyring_addressKeysWithoutTokens_JSON", false))
|
||||||
addrKeysPrimaryHasToken := loadPMKeys(readTestFile("keyring_addressKeysPrimaryHasToken_JSON", false))
|
addrKeysPrimaryHasToken := loadPMKeys(readTestFile("keyring_addressKeysPrimaryHasToken_JSON", false))
|
||||||
@ -42,7 +43,7 @@ func TestPMKeys_GetKeyRingAndUnlock(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
userKey, err := crypto.NewKeyRing(key)
|
userKey, err := crypto.NewKeyRing(key)
|
||||||
assert.NoError(t, err, "Expected not to receive an error unlocking user key")
|
r.NoError(err, "Expected not to receive an error unlocking user key")
|
||||||
|
|
||||||
type args struct {
|
type args struct {
|
||||||
userKeyring *crypto.KeyRing
|
userKeyring *crypto.KeyRing
|
||||||
@ -77,9 +78,7 @@ func TestPMKeys_GetKeyRingAndUnlock(t *testing.T) {
|
|||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
kr, err := tt.keys.UnlockAll(tt.args.passphrase, tt.args.userKeyring) // nolint[scopelint]
|
kr, err := tt.keys.UnlockAll(tt.args.passphrase, tt.args.userKeyring) // nolint[scopelint]
|
||||||
if !assert.NoError(t, err) {
|
r.NoError(err)
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// assert at least one key has been decrypted
|
// assert at least one key has been decrypted
|
||||||
atLeastOneDecrypted := false
|
atLeastOneDecrypted := false
|
||||||
@ -96,7 +95,21 @@ func TestPMKeys_GetKeyRingAndUnlock(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.True(t, atLeastOneDecrypted)
|
r.True(atLeastOneDecrypted)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGopenpgpEncryptAttachment(t *testing.T) {
|
||||||
|
r := require.New(t)
|
||||||
|
|
||||||
|
wantMessage := crypto.NewPlainMessage([]byte(testAttachmentCleartext))
|
||||||
|
|
||||||
|
pgpSplitMessage, err := testPublicKeyRing.EncryptAttachment(wantMessage, "")
|
||||||
|
r.NoError(err)
|
||||||
|
|
||||||
|
haveMessage, err := testPrivateKeyRing.DecryptAttachment(pgpSplitMessage)
|
||||||
|
r.NoError(err)
|
||||||
|
|
||||||
|
r.Equal(wantMessage.Data, haveMessage.Data)
|
||||||
|
}
|
||||||
|
|||||||
@ -22,7 +22,7 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/srp"
|
"github.com/ProtonMail/go-srp"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (m *manager) NewClient(uid, acc, ref string, exp time.Time) Client {
|
func (m *manager) NewClient(uid, acc, ref string, exp time.Time) Client {
|
||||||
@ -44,7 +44,7 @@ func (m *manager) NewClientWithRefresh(ctx context.Context, uid, ref string) (Cl
|
|||||||
return c.withAuth(auth.AccessToken, auth.RefreshToken, expiresIn(auth.ExpiresIn)), auth, nil
|
return c.withAuth(auth.AccessToken, auth.RefreshToken, expiresIn(auth.ExpiresIn)), auth, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *manager) NewClientWithLogin(ctx context.Context, username, password string) (Client, *Auth, error) {
|
func (m *manager) NewClientWithLogin(ctx context.Context, username string, password []byte) (Client, *Auth, error) {
|
||||||
log.Trace("New client with login")
|
log.Trace("New client with login")
|
||||||
|
|
||||||
info, err := m.getAuthInfo(ctx, GetAuthInfoReq{Username: username})
|
info, err := m.getAuthInfo(ctx, GetAuthInfoReq{Username: username})
|
||||||
@ -52,12 +52,12 @@ func (m *manager) NewClientWithLogin(ctx context.Context, username, password str
|
|||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
srpAuth, err := srp.NewSrpAuth(info.Version, username, password, info.Salt, info.Modulus, info.ServerEphemeral)
|
srpAuth, err := srp.NewAuth(info.Version, username, password, info.Salt, info.Modulus, info.ServerEphemeral)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
proofs, err := srpAuth.GenerateSrpProofs(2048)
|
proofs, err := srpAuth.GenerateProofs(2048)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -29,7 +29,7 @@ import (
|
|||||||
type Manager interface {
|
type Manager interface {
|
||||||
NewClient(string, string, string, time.Time) Client
|
NewClient(string, string, string, time.Time) Client
|
||||||
NewClientWithRefresh(context.Context, string, string) (Client, *AuthRefresh, error)
|
NewClientWithRefresh(context.Context, string, string) (Client, *AuthRefresh, error)
|
||||||
NewClientWithLogin(context.Context, string, string) (Client, *Auth, error)
|
NewClientWithLogin(context.Context, string, []byte) (Client, *Auth, error)
|
||||||
|
|
||||||
DownloadAndVerify(kr *crypto.KeyRing, url, sig string) ([]byte, error)
|
DownloadAndVerify(kr *crypto.KeyRing, url, sig string) ([]byte, error)
|
||||||
ReportBug(context.Context, ReportBugReq) error
|
ReportBug(context.Context, ReportBugReq) error
|
||||||
|
|||||||
@ -670,7 +670,7 @@ func (mr *MockManagerMockRecorder) NewClient(arg0, arg1, arg2, arg3 interface{})
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewClientWithLogin mocks base method
|
// NewClientWithLogin mocks base method
|
||||||
func (m *MockManager) NewClientWithLogin(arg0 context.Context, arg1, arg2 string) (pmapi.Client, *pmapi.Auth, error) {
|
func (m *MockManager) NewClientWithLogin(arg0 context.Context, arg1 string, arg2 []byte) (pmapi.Client, *pmapi.Auth, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "NewClientWithLogin", arg0, arg1, arg2)
|
ret := m.ctrl.Call(m, "NewClientWithLogin", arg0, arg1, arg2)
|
||||||
ret0, _ := ret[0].(pmapi.Client)
|
ret0, _ := ret[0].(pmapi.Client)
|
||||||
|
|||||||
@ -20,13 +20,14 @@ package pmapi
|
|||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
|
||||||
"github.com/jameskeane/bcrypt"
|
"github.com/ProtonMail/go-srp"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
func HashMailboxPassword(password, salt string) ([]byte, error) {
|
// HashMailboxPassword expectects 128bit long salt encoded by standard base64.
|
||||||
|
func HashMailboxPassword(password []byte, salt string) ([]byte, error) {
|
||||||
if salt == "" {
|
if salt == "" {
|
||||||
return []byte(password), nil
|
return password, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
decodedSalt, err := base64.StdEncoding.DecodeString(salt)
|
decodedSalt, err := base64.StdEncoding.DecodeString(salt)
|
||||||
@ -34,15 +35,10 @@ func HashMailboxPassword(password, salt string) ([]byte, error) {
|
|||||||
return nil, errors.Wrap(err, "failed to decode salt")
|
return nil, errors.Wrap(err, "failed to decode salt")
|
||||||
}
|
}
|
||||||
|
|
||||||
encodedSalt := base64.NewEncoding("./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789").WithPadding(base64.NoPadding).EncodeToString(decodedSalt)
|
hash, err := srp.MailboxPassword(password, decodedSalt)
|
||||||
hashResult, err := bcrypt.Hash(password, "$2y$10$"+encodedSalt)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "failed to bcrypt-hash password")
|
return nil, errors.Wrap(err, "failed to hash password")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(hashResult) != 60 {
|
return hash[len(hash)-31:], nil
|
||||||
return nil, errors.New("pmapi: invalid mailbox password hash")
|
|
||||||
}
|
|
||||||
|
|
||||||
return []byte(hashResult[len(hashResult)-31:]), nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
44
pkg/pmapi/passwords_test.go
Normal file
44
pkg/pmapi/passwords_test.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
// Copyright (c) 2021 Proton Technologies AG
|
||||||
|
//
|
||||||
|
// This file is part of ProtonMail Bridge.
|
||||||
|
//
|
||||||
|
// ProtonMail Bridge is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// ProtonMail Bridge is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package pmapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMailboxPassword(t *testing.T) {
|
||||||
|
// wantHash was generated with passprase and salt defined below. It
|
||||||
|
// should not change when changing implementation of the function.
|
||||||
|
wantHash := []byte("B5nwpsJQSTJ16ldr64Vdq6oeCCn32Fi")
|
||||||
|
|
||||||
|
// Valid salt is 128bit long (16bytes)
|
||||||
|
// $echo aaaabbbbccccdddd | base64
|
||||||
|
salt := "YWFhYWJiYmJjY2NjZGRkZAo="
|
||||||
|
|
||||||
|
passphrase := []byte("random")
|
||||||
|
|
||||||
|
r := require.New(t)
|
||||||
|
_, err := HashMailboxPassword(passphrase, "badsalt")
|
||||||
|
r.Error(err)
|
||||||
|
|
||||||
|
haveHash, err := HashMailboxPassword(passphrase, salt)
|
||||||
|
r.NoError(err)
|
||||||
|
r.Equal(wantHash, haveHash)
|
||||||
|
}
|
||||||
107
pkg/srp/hash.go
107
pkg/srp/hash.go
@ -1,107 +0,0 @@
|
|||||||
// Copyright (c) 2021 Proton Technologies AG
|
|
||||||
//
|
|
||||||
// This file is part of ProtonMail Bridge.
|
|
||||||
//
|
|
||||||
// ProtonMail Bridge is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// ProtonMail Bridge is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package srp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/md5" //nolint[gosec]
|
|
||||||
"crypto/sha512"
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/hex"
|
|
||||||
"errors"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/jameskeane/bcrypt"
|
|
||||||
)
|
|
||||||
|
|
||||||
// BCryptHash function bcrypt algorithm to hash password with salt.
|
|
||||||
func BCryptHash(password string, salt string) (string, error) {
|
|
||||||
return bcrypt.Hash(password, salt)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExpandHash extends the byte data for SRP flow.
|
|
||||||
func ExpandHash(data []byte) []byte {
|
|
||||||
part0 := sha512.Sum512(append(data, 0))
|
|
||||||
part1 := sha512.Sum512(append(data, 1))
|
|
||||||
part2 := sha512.Sum512(append(data, 2))
|
|
||||||
part3 := sha512.Sum512(append(data, 3))
|
|
||||||
return bytes.Join([][]byte{
|
|
||||||
part0[:],
|
|
||||||
part1[:],
|
|
||||||
part2[:],
|
|
||||||
part3[:],
|
|
||||||
}, []byte{})
|
|
||||||
}
|
|
||||||
|
|
||||||
// HashPassword returns the hash of password argument. Based on version number
|
|
||||||
// following arguments are used in addition to password:
|
|
||||||
// * 0, 1, 2: userName and modulus
|
|
||||||
// * 3, 4: salt and modulus.
|
|
||||||
func HashPassword(authVersion int, password, userName string, salt, modulus []byte) ([]byte, error) {
|
|
||||||
switch authVersion {
|
|
||||||
case 4, 3:
|
|
||||||
return hashPasswordVersion3(password, salt, modulus)
|
|
||||||
case 2:
|
|
||||||
return hashPasswordVersion2(password, userName, modulus)
|
|
||||||
case 1:
|
|
||||||
return hashPasswordVersion1(password, userName, modulus)
|
|
||||||
case 0:
|
|
||||||
return hashPasswordVersion0(password, userName, modulus)
|
|
||||||
default:
|
|
||||||
return nil, errors.New("pmapi: unsupported auth version")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// CleanUserName returns the input string in lower-case without characters `_`,
|
|
||||||
// `.` and `-`.
|
|
||||||
func CleanUserName(userName string) string {
|
|
||||||
userName = strings.ReplaceAll(userName, "-", "")
|
|
||||||
userName = strings.ReplaceAll(userName, ".", "")
|
|
||||||
userName = strings.ReplaceAll(userName, "_", "")
|
|
||||||
return strings.ToLower(userName)
|
|
||||||
}
|
|
||||||
|
|
||||||
func hashPasswordVersion3(password string, salt, modulus []byte) (res []byte, err error) {
|
|
||||||
encodedSalt := base64.NewEncoding("./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789").WithPadding(base64.NoPadding).EncodeToString(append(salt, []byte("proton")...))
|
|
||||||
crypted, err := BCryptHash(password, "$2y$10$"+encodedSalt)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return ExpandHash(append([]byte(crypted), modulus...)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func hashPasswordVersion2(password, userName string, modulus []byte) (res []byte, err error) {
|
|
||||||
return hashPasswordVersion1(password, CleanUserName(userName), modulus)
|
|
||||||
}
|
|
||||||
|
|
||||||
func hashPasswordVersion1(password, userName string, modulus []byte) (res []byte, err error) {
|
|
||||||
prehashed := md5.Sum([]byte(strings.ToLower(userName))) //nolint[gosec]
|
|
||||||
encodedSalt := hex.EncodeToString(prehashed[:])
|
|
||||||
crypted, err := BCryptHash(password, "$2y$10$"+encodedSalt)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return ExpandHash(append([]byte(crypted), modulus...)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func hashPasswordVersion0(password, userName string, modulus []byte) (res []byte, err error) {
|
|
||||||
prehashed := sha512.Sum512([]byte(password))
|
|
||||||
return hashPasswordVersion1(base64.StdEncoding.EncodeToString(prehashed[:]), userName, modulus)
|
|
||||||
}
|
|
||||||
219
pkg/srp/srp.go
219
pkg/srp/srp.go
@ -1,219 +0,0 @@
|
|||||||
// Copyright (c) 2021 Proton Technologies AG
|
|
||||||
//
|
|
||||||
// This file is part of ProtonMail Bridge.
|
|
||||||
//
|
|
||||||
// ProtonMail Bridge is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// ProtonMail Bridge is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package srp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/rand"
|
|
||||||
"encoding/base64"
|
|
||||||
"errors"
|
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"golang.org/x/crypto/openpgp"
|
|
||||||
"golang.org/x/crypto/openpgp/clearsign"
|
|
||||||
)
|
|
||||||
|
|
||||||
//nolint[gochecknoglobals]
|
|
||||||
var (
|
|
||||||
ErrDataAfterModulus = errors.New("pm-srp: extra data after modulus")
|
|
||||||
ErrInvalidSignature = errors.New("pm-srp: invalid modulus signature")
|
|
||||||
RandReader = rand.Reader
|
|
||||||
)
|
|
||||||
|
|
||||||
// Store random reader in a variable to be able to overwrite it in tests
|
|
||||||
|
|
||||||
// Amored pubkey for modulus verification.
|
|
||||||
const modulusPubkey = `-----BEGIN PGP PUBLIC KEY BLOCK-----
|
|
||||||
|
|
||||||
xjMEXAHLgxYJKwYBBAHaRw8BAQdAFurWXXwjTemqjD7CXjXVyKf0of7n9Ctm
|
|
||||||
L8v9enkzggHNEnByb3RvbkBzcnAubW9kdWx1c8J3BBAWCgApBQJcAcuDBgsJ
|
|
||||||
BwgDAgkQNQWFxOlRjyYEFQgKAgMWAgECGQECGwMCHgEAAPGRAP9sauJsW12U
|
|
||||||
MnTQUZpsbJb53d0Wv55mZIIiJL2XulpWPQD/V6NglBd96lZKBmInSXX/kXat
|
|
||||||
Sv+y0io+LR8i2+jV+AbOOARcAcuDEgorBgEEAZdVAQUBAQdAeJHUz1c9+KfE
|
|
||||||
kSIgcBRE3WuXC4oj5a2/U3oASExGDW4DAQgHwmEEGBYIABMFAlwBy4MJEDUF
|
|
||||||
hcTpUY8mAhsMAAD/XQD8DxNI6E78meodQI+wLsrKLeHn32iLvUqJbVDhfWSU
|
|
||||||
WO4BAMcm1u02t4VKw++ttECPt+HUgPUq5pqQWe5Q2cW4TMsE
|
|
||||||
=Y4Mw
|
|
||||||
-----END PGP PUBLIC KEY BLOCK-----`
|
|
||||||
|
|
||||||
// ReadClearSignedMessage reads the clear text from signed message and verifies
|
|
||||||
// signature. There must be no data appended after signed message in input string.
|
|
||||||
// The message must be sign by key corresponding to `modulusPubkey`.
|
|
||||||
func ReadClearSignedMessage(signedMessage string) (string, error) {
|
|
||||||
modulusBlock, rest := clearsign.Decode([]byte(signedMessage))
|
|
||||||
if len(rest) != 0 {
|
|
||||||
return "", ErrDataAfterModulus
|
|
||||||
}
|
|
||||||
|
|
||||||
modulusKeyring, err := openpgp.ReadArmoredKeyRing(bytes.NewReader([]byte(modulusPubkey)))
|
|
||||||
if err != nil {
|
|
||||||
return "", errors.New("pm-srp: can not read modulus pubkey")
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = openpgp.CheckDetachedSignature(modulusKeyring, bytes.NewReader(modulusBlock.Bytes), modulusBlock.ArmoredSignature.Body, nil)
|
|
||||||
if err != nil {
|
|
||||||
return "", ErrInvalidSignature
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(modulusBlock.Bytes), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SrpProofs object.
|
|
||||||
type SrpProofs struct { //nolint[golint]
|
|
||||||
ClientProof, ClientEphemeral, ExpectedServerProof []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// SrpAuth stores byte data for the calculation of SRP proofs.
|
|
||||||
type SrpAuth struct { //nolint[golint]
|
|
||||||
Modulus, ServerEphemeral, HashedPassword []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewSrpAuth creates new SrpAuth from strings input. Salt and server ephemeral are in
|
|
||||||
// base64 format. Modulus is base64 with signature attached. The signature is
|
|
||||||
// verified against server key. The version controls password hash algorithm.
|
|
||||||
func NewSrpAuth(version int, username, password, salt, signedModulus, serverEphemeral string) (auth *SrpAuth, err error) {
|
|
||||||
data := &SrpAuth{}
|
|
||||||
|
|
||||||
// Modulus
|
|
||||||
var modulus string
|
|
||||||
modulus, err = ReadClearSignedMessage(signedModulus)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
data.Modulus, err = base64.StdEncoding.DecodeString(modulus)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Password
|
|
||||||
var decodedSalt []byte
|
|
||||||
if version >= 3 {
|
|
||||||
decodedSalt, err = base64.StdEncoding.DecodeString(salt)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
data.HashedPassword, err = HashPassword(version, password, username, decodedSalt, data.Modulus)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Server ephermeral
|
|
||||||
data.ServerEphemeral, err = base64.StdEncoding.DecodeString(serverEphemeral)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return data, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GenerateSrpProofs calculates SPR proofs.
|
|
||||||
func (s *SrpAuth) GenerateSrpProofs(length int) (res *SrpProofs, err error) { //nolint[funlen]
|
|
||||||
toInt := func(arr []byte) *big.Int {
|
|
||||||
var reversed = make([]byte, len(arr))
|
|
||||||
for i := 0; i < len(arr); i++ {
|
|
||||||
reversed[len(arr)-i-1] = arr[i]
|
|
||||||
}
|
|
||||||
return big.NewInt(0).SetBytes(reversed)
|
|
||||||
}
|
|
||||||
|
|
||||||
fromInt := func(num *big.Int) []byte {
|
|
||||||
var arr = num.Bytes()
|
|
||||||
var reversed = make([]byte, length/8)
|
|
||||||
for i := 0; i < len(arr); i++ {
|
|
||||||
reversed[len(arr)-i-1] = arr[i]
|
|
||||||
}
|
|
||||||
return reversed
|
|
||||||
}
|
|
||||||
|
|
||||||
generator := big.NewInt(2)
|
|
||||||
multiplier := toInt(ExpandHash(append(fromInt(generator), s.Modulus...)))
|
|
||||||
|
|
||||||
modulus := toInt(s.Modulus)
|
|
||||||
serverEphemeral := toInt(s.ServerEphemeral)
|
|
||||||
hashedPassword := toInt(s.HashedPassword)
|
|
||||||
|
|
||||||
modulusMinusOne := big.NewInt(0).Sub(modulus, big.NewInt(1))
|
|
||||||
|
|
||||||
if modulus.BitLen() != length {
|
|
||||||
return nil, errors.New("pm-srp: SRP modulus has incorrect size")
|
|
||||||
}
|
|
||||||
|
|
||||||
multiplier = multiplier.Mod(multiplier, modulus)
|
|
||||||
|
|
||||||
if multiplier.Cmp(big.NewInt(1)) <= 0 || multiplier.Cmp(modulusMinusOne) >= 0 {
|
|
||||||
return nil, errors.New("pm-srp: SRP multiplier is out of bounds")
|
|
||||||
}
|
|
||||||
|
|
||||||
if generator.Cmp(big.NewInt(1)) <= 0 || generator.Cmp(modulusMinusOne) >= 0 {
|
|
||||||
return nil, errors.New("pm-srp: SRP generator is out of bounds")
|
|
||||||
}
|
|
||||||
|
|
||||||
if serverEphemeral.Cmp(big.NewInt(1)) <= 0 || serverEphemeral.Cmp(modulusMinusOne) >= 0 {
|
|
||||||
return nil, errors.New("pm-srp: SRP server ephemeral is out of bounds")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check primality
|
|
||||||
// Doing exponentiation here is faster than a full call to ProbablyPrime while
|
|
||||||
// still perfectly accurate by Pocklington's theorem
|
|
||||||
if big.NewInt(0).Exp(big.NewInt(2), modulusMinusOne, modulus).Cmp(big.NewInt(1)) != 0 {
|
|
||||||
return nil, errors.New("pm-srp: SRP modulus is not prime")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check safe primality
|
|
||||||
if !big.NewInt(0).Rsh(modulus, 1).ProbablyPrime(10) {
|
|
||||||
return nil, errors.New("pm-srp: SRP modulus is not a safe prime")
|
|
||||||
}
|
|
||||||
|
|
||||||
var clientSecret, clientEphemeral, scramblingParam *big.Int
|
|
||||||
for {
|
|
||||||
for {
|
|
||||||
clientSecret, err = rand.Int(RandReader, modulusMinusOne)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if clientSecret.Cmp(big.NewInt(int64(length*2))) > 0 { // Very likely
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clientEphemeral = big.NewInt(0).Exp(generator, clientSecret, modulus)
|
|
||||||
scramblingParam = toInt(ExpandHash(append(fromInt(clientEphemeral), fromInt(serverEphemeral)...)))
|
|
||||||
if scramblingParam.Cmp(big.NewInt(0)) != 0 { // Very likely
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
subtracted := big.NewInt(0).Sub(serverEphemeral, big.NewInt(0).Mod(big.NewInt(0).Mul(big.NewInt(0).Exp(generator, hashedPassword, modulus), multiplier), modulus))
|
|
||||||
if subtracted.Cmp(big.NewInt(0)) < 0 {
|
|
||||||
subtracted.Add(subtracted, modulus)
|
|
||||||
}
|
|
||||||
exponent := big.NewInt(0).Mod(big.NewInt(0).Add(big.NewInt(0).Mul(scramblingParam, hashedPassword), clientSecret), modulusMinusOne)
|
|
||||||
sharedSession := big.NewInt(0).Exp(subtracted, exponent, modulus)
|
|
||||||
|
|
||||||
clientProof := ExpandHash(bytes.Join([][]byte{fromInt(clientEphemeral), fromInt(serverEphemeral), fromInt(sharedSession)}, []byte{}))
|
|
||||||
serverProof := ExpandHash(bytes.Join([][]byte{fromInt(clientEphemeral), clientProof, fromInt(sharedSession)}, []byte{}))
|
|
||||||
|
|
||||||
return &SrpProofs{ClientEphemeral: fromInt(clientEphemeral), ClientProof: clientProof, ExpectedServerProof: serverProof}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GenerateVerifier verifier for update pwds and create accounts.
|
|
||||||
func (s *SrpAuth) GenerateVerifier(length int) ([]byte, error) {
|
|
||||||
return nil, errors.New("pm-srp: the client doesn't need SRP GenerateVerifier")
|
|
||||||
}
|
|
||||||
@ -1,111 +0,0 @@
|
|||||||
// Copyright (c) 2021 Proton Technologies AG
|
|
||||||
//
|
|
||||||
// This file is part of ProtonMail Bridge.
|
|
||||||
//
|
|
||||||
// ProtonMail Bridge is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// ProtonMail Bridge is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package srp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/base64"
|
|
||||||
"math/rand"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
testServerEphemeral = "l13IQSVFBEV0ZZREuRQ4ZgP6OpGiIfIjbSDYQG3Yp39FkT2B/k3n1ZhwqrAdy+qvPPFq/le0b7UDtayoX4aOTJihoRvifas8Hr3icd9nAHqd0TUBbkZkT6Iy6UpzmirCXQtEhvGQIdOLuwvy+vZWh24G2ahBM75dAqwkP961EJMh67/I5PA5hJdQZjdPT5luCyVa7BS1d9ZdmuR0/VCjUOdJbYjgtIH7BQoZs+KacjhUN8gybu+fsycvTK3eC+9mCN2Y6GdsuCMuR3pFB0RF9eKae7cA6RbJfF1bjm0nNfWLXzgKguKBOeF3GEAsnCgK68q82/pq9etiUDizUlUBcA=="
|
|
||||||
testServerProof = "ffYFIhnhZJAflFJr9FfXbtdsBLkDGH+TUR5sj98wg0iVHyIhIVT6BeZD8tZA75tYlz7uYIanswweB3bjrGfITXfxERgQysQSoPUB284cX4VQm1IfTB/9LPma618MH8OULNluXVu2eizPWnvIn9VLXCaIX+38Xd6xOjmCQgfkpJy3Sh3ndikjqNCGWiKyvERVJi0nTmpAbHmcdeEp1K++ZRbebRhm2d018o/u4H2gu+MF39Hx12zMzEGNMwkNkgKSEQYlqmj57S6tW9JuB30zVZFnw6Krftg1QfJR6zCT1/J57OGp0A/7X/lC6Xz/I33eJvXOpG9GCRCbNiozFg9IXQ=="
|
|
||||||
|
|
||||||
testClientProof = "8dQtp6zIeEmu3D93CxPdEiCWiAE86uDmK33EpxyqReMwUrm/bTL+zCkWa/X7QgLNrt2FBAriyROhz5TEONgZq/PqZnBEBym6Rvo708KHu6S4LFdZkVc0+lgi7yQpNhU8bqB0BCqdSWd3Fjd3xbOYgO7/vnFK+p9XQZKwEh2RmGv97XHwoxefoyXK6BB+VVMkELd4vL7vdqBiOBU3ufOlSp+0XBMVltQ4oi5l1y21pzOA9cw5WTPIPMcQHffNFq/rReHYnqbBqiLlSLyw6K0PcVuN3bvr3rVYfdS1CsM/Rv1DzXlBUl39B2j82y6hdyGcTeplGyAnAcu0CimvynKBvQ=="
|
|
||||||
testModulus = "W2z5HBi8RvsfYzZTS7qBaUxxPhsfHJFZpu3Kd6s1JafNrCCH9rfvPLrfuqocxWPgWDH2R8neK7PkNvjxto9TStuY5z7jAzWRvFWN9cQhAKkdWgy0JY6ywVn22+HFpF4cYesHrqFIKUPDMSSIlWjBVmEJZ/MusD44ZT29xcPrOqeZvwtCffKtGAIjLYPZIEbZKnDM1Dm3q2K/xS5h+xdhjnndhsrkwm9U9oyA2wxzSXFL+pdfj2fOdRwuR5nW0J2NFrq3kJjkRmpO/Genq1UW+TEknIWAb6VzJJJA244K/H8cnSx2+nSNZO3bbo6Ys228ruV9A8m6DhxmS+bihN3ttQ=="
|
|
||||||
testModulusClearSign = `-----BEGIN PGP SIGNED MESSAGE-----
|
|
||||||
Hash: SHA256
|
|
||||||
|
|
||||||
W2z5HBi8RvsfYzZTS7qBaUxxPhsfHJFZpu3Kd6s1JafNrCCH9rfvPLrfuqocxWPgWDH2R8neK7PkNvjxto9TStuY5z7jAzWRvFWN9cQhAKkdWgy0JY6ywVn22+HFpF4cYesHrqFIKUPDMSSIlWjBVmEJZ/MusD44ZT29xcPrOqeZvwtCffKtGAIjLYPZIEbZKnDM1Dm3q2K/xS5h+xdhjnndhsrkwm9U9oyA2wxzSXFL+pdfj2fOdRwuR5nW0J2NFrq3kJjkRmpO/Genq1UW+TEknIWAb6VzJJJA244K/H8cnSx2+nSNZO3bbo6Ys228ruV9A8m6DhxmS+bihN3ttQ==
|
|
||||||
-----BEGIN PGP SIGNATURE-----
|
|
||||||
Version: ProtonMail
|
|
||||||
Comment: https://protonmail.com
|
|
||||||
|
|
||||||
wl4EARYIABAFAlwB1j0JEDUFhcTpUY8mAAD8CgEAnsFnF4cF0uSHKkXa1GIa
|
|
||||||
GO86yMV4zDZEZcDSJo0fgr8A/AlupGN9EdHlsrZLmTA1vhIx+rOgxdEff28N
|
|
||||||
kvNM7qIK
|
|
||||||
=q6vu
|
|
||||||
-----END PGP SIGNATURE-----`
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
// Only for tests, replace the default random reader by something that always
|
|
||||||
// return the same thing
|
|
||||||
RandReader = rand.New(rand.NewSource(42))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReadClearSigned(t *testing.T) {
|
|
||||||
cleartext, err := ReadClearSignedMessage(testModulusClearSign)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Expected no error but have ", err)
|
|
||||||
}
|
|
||||||
if cleartext != testModulus {
|
|
||||||
t.Fatalf("Expected message\n\t'%s'\nbut have\n\t'%s'", testModulus, cleartext)
|
|
||||||
}
|
|
||||||
|
|
||||||
lastChar := len(testModulusClearSign)
|
|
||||||
wrongSignature := testModulusClearSign[:lastChar-100]
|
|
||||||
wrongSignature += "c"
|
|
||||||
wrongSignature += testModulusClearSign[lastChar-99:]
|
|
||||||
_, err = ReadClearSignedMessage(wrongSignature)
|
|
||||||
if err != ErrInvalidSignature {
|
|
||||||
t.Fatal("Expected the ErrInvalidSignature but have ", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
wrongSignature = testModulusClearSign + "data after modulus"
|
|
||||||
_, err = ReadClearSignedMessage(wrongSignature)
|
|
||||||
if err != ErrDataAfterModulus {
|
|
||||||
t.Fatal("Expected the ErrDataAfterModulus but have ", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSRPauth(t *testing.T) {
|
|
||||||
srp, err := NewSrpAuth(4, "bridgetest", "test", "yKlc5/CvObfoiw==", testModulusClearSign, testServerEphemeral)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Expected no error but have ", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
proofs, err := srp.GenerateSrpProofs(2048)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Expected no error but have ", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
expectedProof, err := base64.StdEncoding.DecodeString(testServerProof)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Expected no error but have ", err)
|
|
||||||
}
|
|
||||||
if !bytes.Equal(proofs.ExpectedServerProof, expectedProof) {
|
|
||||||
t.Fatalf("Expected server proof\n\t'%s'\nbut have\n\t'%s'",
|
|
||||||
testServerProof,
|
|
||||||
base64.StdEncoding.EncodeToString(proofs.ExpectedServerProof),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
expectedProof, err = base64.StdEncoding.DecodeString(testClientProof)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Expected no error but have ", err)
|
|
||||||
}
|
|
||||||
if !bytes.Equal(proofs.ClientProof, expectedProof) {
|
|
||||||
t.Fatalf("Expected client proof\n\t'%s'\nbut have\n\t'%s'",
|
|
||||||
testClientProof,
|
|
||||||
base64.StdEncoding.EncodeToString(proofs.ClientProof),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -177,12 +177,12 @@ func (a *TestAccount) EnsureAddress(addressOrAddressTestID string) string {
|
|||||||
return addressOrAddressTestID
|
return addressOrAddressTestID
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *TestAccount) Password() string {
|
func (a *TestAccount) Password() []byte {
|
||||||
return a.password
|
return []byte(a.password)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *TestAccount) MailboxPassword() string {
|
func (a *TestAccount) MailboxPassword() []byte {
|
||||||
return a.mailboxPassword
|
return []byte(a.mailboxPassword)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *TestAccount) IsTwoFAEnabled() bool {
|
func (a *TestAccount) IsTwoFAEnabled() bool {
|
||||||
|
|||||||
@ -51,7 +51,7 @@ func (c *fakeCredStore) List() (userIDs []string, err error) {
|
|||||||
return keys, nil
|
return keys, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *fakeCredStore) Add(userID, userName, uid, ref, mailboxPassword string, emails []string) (*credentials.Credentials, error) {
|
func (c *fakeCredStore) Add(userID, userName, uid, ref string, mailboxPassword []byte, emails []string) (*credentials.Credentials, error) {
|
||||||
bridgePassword := bridgePassword
|
bridgePassword := bridgePassword
|
||||||
if c, ok := c.credentials[userID]; ok {
|
if c, ok := c.credentials[userID]; ok {
|
||||||
bridgePassword = c.BridgePassword
|
bridgePassword = c.BridgePassword
|
||||||
@ -80,7 +80,7 @@ func (c *fakeCredStore) UpdateEmails(userID string, emails []string) (*credentia
|
|||||||
return c.credentials[userID], nil
|
return c.credentials[userID], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *fakeCredStore) UpdatePassword(userID, password string) (*credentials.Credentials, error) {
|
func (c *fakeCredStore) UpdatePassword(userID string, password []byte) (*credentials.Credentials, error) {
|
||||||
creds, err := c.Get(userID)
|
creds, err := c.Get(userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -100,7 +100,7 @@ func (c *fakeCredStore) UpdateToken(userID, uid, ref string) (*credentials.Crede
|
|||||||
|
|
||||||
func (c *fakeCredStore) Logout(userID string) (*credentials.Credentials, error) {
|
func (c *fakeCredStore) Logout(userID string) (*credentials.Credentials, error) {
|
||||||
c.credentials[userID].APIToken = ""
|
c.credentials[userID].APIToken = ""
|
||||||
c.credentials[userID].MailboxPassword = ""
|
c.credentials[userID].MailboxPassword = []byte{}
|
||||||
return c.credentials[userID], nil
|
return c.credentials[userID], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -30,7 +30,7 @@ import (
|
|||||||
type PMAPIController interface {
|
type PMAPIController interface {
|
||||||
TurnInternetConnectionOff()
|
TurnInternetConnectionOff()
|
||||||
TurnInternetConnectionOn()
|
TurnInternetConnectionOn()
|
||||||
AddUser(user *pmapi.User, addresses *pmapi.AddressList, password string, twoFAEnabled bool) error
|
AddUser(user *pmapi.User, addresses *pmapi.AddressList, password []byte, twoFAEnabled bool) error
|
||||||
AddUserLabel(username string, label *pmapi.Label) error
|
AddUserLabel(username string, label *pmapi.Label) error
|
||||||
GetLabelIDs(username string, labelNames []string) ([]string, error)
|
GetLabelIDs(username string, labelNames []string) ([]string, error)
|
||||||
AddUserMessage(username string, message *pmapi.Message) (string, error)
|
AddUserMessage(username string, message *pmapi.Message) (string, error)
|
||||||
|
|||||||
@ -24,9 +24,9 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/ProtonMail/go-srp"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/store"
|
"github.com/ProtonMail/proton-bridge/internal/store"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/users"
|
"github.com/ProtonMail/proton-bridge/internal/users"
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/srp"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
@ -37,7 +37,7 @@ func (ctx *TestContext) GetUsers() *users.Users {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// LoginUser logs in the user with the given username, password, and mailbox password.
|
// LoginUser logs in the user with the given username, password, and mailbox password.
|
||||||
func (ctx *TestContext) LoginUser(username, password, mailboxPassword string) error {
|
func (ctx *TestContext) LoginUser(username string, password, mailboxPassword []byte) error {
|
||||||
srp.RandReader = rand.New(rand.NewSource(42)) //nolint[gosec] It is OK to use weaker random number generator here
|
srp.RandReader = rand.New(rand.NewSource(42)) //nolint[gosec] It is OK to use weaker random number generator here
|
||||||
|
|
||||||
client, auth, err := ctx.users.Login(username, password)
|
client, auth, err := ctx.users.Login(username, password)
|
||||||
|
|||||||
@ -61,7 +61,7 @@ func (ctl *Controller) ReorderAddresses(user *pmapi.User, addressIDs []string) e
|
|||||||
return api.ReorderAddresses(context.Background(), addressIDs)
|
return api.ReorderAddresses(context.Background(), addressIDs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctl *Controller) AddUser(user *pmapi.User, addresses *pmapi.AddressList, password string, twoFAEnabled bool) error {
|
func (ctl *Controller) AddUser(user *pmapi.User, addresses *pmapi.AddressList, password []byte, twoFAEnabled bool) error {
|
||||||
ctl.usersByUsername[user.Name] = &fakeUser{
|
ctl.usersByUsername[user.Name] = &fakeUser{
|
||||||
user: user,
|
user: user,
|
||||||
password: password,
|
password: password,
|
||||||
|
|||||||
@ -18,6 +18,7 @@
|
|||||||
package fakeapi
|
package fakeapi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/ProtonMail/proton-bridge/pkg/pmapi"
|
"github.com/ProtonMail/proton-bridge/pkg/pmapi"
|
||||||
@ -49,10 +50,10 @@ func (ctl *Controller) checkScope(uid string) bool {
|
|||||||
return session.hasFullScope
|
return session.hasFullScope
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctl *Controller) createSessionIfAuthorized(username, password string) (*fakeSession, error) {
|
func (ctl *Controller) createSessionIfAuthorized(username string, password []byte) (*fakeSession, error) {
|
||||||
// get user
|
// get user
|
||||||
user, ok := ctl.usersByUsername[username]
|
user, ok := ctl.usersByUsername[username]
|
||||||
if !ok || user.password != password {
|
if !ok || !bytes.Equal(user.password, password) {
|
||||||
return nil, errWrongNameOrPassword
|
return nil, errWrongNameOrPassword
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -21,6 +21,6 @@ import "github.com/ProtonMail/proton-bridge/pkg/pmapi"
|
|||||||
|
|
||||||
type fakeUser struct {
|
type fakeUser struct {
|
||||||
user *pmapi.User
|
user *pmapi.User
|
||||||
password string
|
password []byte
|
||||||
has2FA bool
|
has2FA bool
|
||||||
}
|
}
|
||||||
|
|||||||
@ -94,7 +94,7 @@ func (m *fakePMAPIManager) NewClientWithRefresh(_ context.Context, uid, ref stri
|
|||||||
return client, auth, nil
|
return client, auth, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *fakePMAPIManager) NewClientWithLogin(_ context.Context, username string, password string) (pmapi.Client, *pmapi.Auth, error) {
|
func (m *fakePMAPIManager) NewClientWithLogin(_ context.Context, username string, password []byte) (pmapi.Client, *pmapi.Auth, error) {
|
||||||
if err := m.controller.checkAndRecordCall(POST, "/auth/info", &pmapi.GetAuthInfoReq{Username: username}); err != nil {
|
if err := m.controller.checkAndRecordCall(POST, "/auth/info", &pmapi.GetAuthInfoReq{Username: username}); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,7 +25,7 @@ import (
|
|||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (ctl *Controller) AddUser(user *pmapi.User, addresses *pmapi.AddressList, password string, twoFAEnabled bool) error {
|
func (ctl *Controller) AddUser(user *pmapi.User, addresses *pmapi.AddressList, password []byte, twoFAEnabled bool) error {
|
||||||
if twoFAEnabled {
|
if twoFAEnabled {
|
||||||
return godog.ErrPending
|
return godog.ErrPending
|
||||||
}
|
}
|
||||||
|
|||||||
@ -45,7 +45,7 @@ func userLogsInWithBadPassword(bddUserID string) error {
|
|||||||
if account == nil {
|
if account == nil {
|
||||||
return godog.ErrPending
|
return godog.ErrPending
|
||||||
}
|
}
|
||||||
ctx.SetLastError(ctx.LoginUser(account.Username(), "you shall not pass!", "123"))
|
ctx.SetLastError(ctx.LoginUser(account.Username(), []byte("you shall not pass!"), []byte("123")))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user