Update Dependencies (#390)

Co-authored-by: Norwin Roosen <git@nroo.de>
Co-authored-by: Norwin <git@nroo.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/390
Reviewed-by: 6543 <6543@obermui.de>
Reviewed-by: Andrew Thornton <art27@cantab.net>
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-committed-by: Norwin <noerw@noreply.gitea.io>
This commit is contained in:
Norwin 2021-08-30 23:18:50 +08:00 committed by Andrew Thornton
parent 4b9907fb54
commit d6df0a53b5
665 changed files with 29466 additions and 24547 deletions

43
go.mod
View File

@ -6,34 +6,35 @@ require (
code.gitea.io/gitea-vet v0.2.1 code.gitea.io/gitea-vet v0.2.1
code.gitea.io/sdk/gitea v0.15.0 code.gitea.io/sdk/gitea v0.15.0
gitea.com/noerw/unidiff-comments v0.0.0-20201219085024-64aec5658f2b gitea.com/noerw/unidiff-comments v0.0.0-20201219085024-64aec5658f2b
github.com/AlecAivazis/survey/v2 v2.2.8 github.com/AlecAivazis/survey/v2 v2.3.1
github.com/Microsoft/go-winio v0.4.16 // indirect github.com/ProtonMail/go-crypto v0.0.0-20210707164159-52430bf6b52c // indirect
github.com/adrg/xdg v0.3.1 github.com/adrg/xdg v0.3.3
github.com/araddon/dateparse v0.0.0-20210207001429-0eec95c9db7e github.com/alecthomas/chroma v0.9.2 // indirect
github.com/charmbracelet/glamour v0.2.0 github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect github.com/charmbracelet/glamour v0.3.0
github.com/go-git/go-git/v5 v5.2.0 github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
github.com/go-git/go-git/v5 v5.4.2
github.com/hashicorp/go-version v1.3.0 // indirect github.com/hashicorp/go-version v1.3.0 // indirect
github.com/imdario/mergo v0.3.11 // indirect github.com/kevinburke/ssh_config v1.1.0 // indirect
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-colorable v0.1.8 // indirect github.com/mattn/go-colorable v0.1.8 // indirect
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
github.com/muesli/termenv v0.7.4 github.com/microcosm-cc/bluemonday v1.0.15 // indirect
github.com/muesli/reflow v0.3.0 // indirect
github.com/muesli/termenv v0.9.0
github.com/olekukonko/tablewriter v0.0.5 github.com/olekukonko/tablewriter v0.0.5
github.com/rivo/uniseg v0.2.0 // indirect github.com/sergi/go-diff v1.2.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
github.com/stretchr/testify v1.7.0 github.com/stretchr/testify v1.7.0
github.com/urfave/cli/v2 v2.3.0 github.com/urfave/cli/v2 v2.3.0
github.com/xanzy/ssh-agent v0.3.0 // indirect github.com/xanzy/ssh-agent v0.3.1 // indirect
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 github.com/yuin/goldmark v1.4.0 // indirect
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 // indirect golang.org/x/crypto v0.0.0-20210817164053-32db794688a5
golang.org/x/sys v0.0.0-20210305034016-7844c3c200c3 // indirect golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d // indirect
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d // indirect golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf // indirect
golang.org/x/text v0.3.5 // indirect golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b // indirect
golang.org/x/tools v0.1.0 // indirect golang.org/x/text v0.3.7 // indirect
golang.org/x/tools v0.1.5 // indirect
gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v2 v2.4.0
) )
replace github.com/charmbracelet/glamour => github.com/noerw/glamour v0.2.1-0.20210305125354-f0a29f1de0c2 replace github.com/charmbracelet/glamour => github.com/noerw/glamour v0.3.0-patch

169
go.sum
View File

@ -4,22 +4,27 @@ code.gitea.io/sdk/gitea v0.15.0 h1:tsNhxDM/2N1Ohv1Xq5UWrht/esg0WmtRj4wsHVHriTg=
code.gitea.io/sdk/gitea v0.15.0/go.mod h1:klY2LVI3s3NChzIk/MzMn7G1FHrfU7qd63iSMVoHRBA= code.gitea.io/sdk/gitea v0.15.0/go.mod h1:klY2LVI3s3NChzIk/MzMn7G1FHrfU7qd63iSMVoHRBA=
gitea.com/noerw/unidiff-comments v0.0.0-20201219085024-64aec5658f2b h1:CLYsMGcGLohESQDMth+RgJ4cB3CCHToxnj0zBbvB3sE= gitea.com/noerw/unidiff-comments v0.0.0-20201219085024-64aec5658f2b h1:CLYsMGcGLohESQDMth+RgJ4cB3CCHToxnj0zBbvB3sE=
gitea.com/noerw/unidiff-comments v0.0.0-20201219085024-64aec5658f2b/go.mod h1:Fc8iyPm4NINRWujeIk2bTfcbGc4ZYY29/oMAAGcr4qI= gitea.com/noerw/unidiff-comments v0.0.0-20201219085024-64aec5658f2b/go.mod h1:Fc8iyPm4NINRWujeIk2bTfcbGc4ZYY29/oMAAGcr4qI=
github.com/AlecAivazis/survey/v2 v2.2.8 h1:TgxCwybKdBckmC+/P9/5h49rw/nAHe/itZL0dgHs+Q0= github.com/AlecAivazis/survey/v2 v2.3.1 h1:lzkuHA60pER7L4eYL8qQJor4bUWlJe4V0gqAT19tdOA=
github.com/AlecAivazis/survey/v2 v2.2.8/go.mod h1:9DYvHgXtiXm6nCn+jXnOXLKbH+Yo9u8fAS/SduGdoPk= github.com/AlecAivazis/survey/v2 v2.3.1/go.mod h1:TH2kPCDU3Kqq7pLbnCWwZXDBjnhZtmsCle5EiYDJ2fg=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
github.com/Microsoft/go-winio v0.4.16 h1:FtSW/jqD+l4ba5iPBj9CODVtgfYAD8w2wS923g/cFDk=
github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
github.com/Microsoft/go-winio v0.5.0 h1:Elr9Wn+sGKPlkaBvwu4mTrxtmOp3F3yV9qhaHbXGjwU=
github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw= github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw=
github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc= github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc=
github.com/adrg/xdg v0.3.1 h1:uIyL9BYfXaFgDyVRKE8wjtm6ETQULweQqTofphEFJYY= github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
github.com/adrg/xdg v0.3.1/go.mod h1:7I2hH/IT30IsupOpKZ5ue7/qNi3CoKzD6tL3HwpaRMQ= github.com/ProtonMail/go-crypto v0.0.0-20210707164159-52430bf6b52c h1:FP7mMdsXy0ybzar1sJeIcZtaJka0U/ZmLTW4wRpolYk=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= github.com/ProtonMail/go-crypto v0.0.0-20210707164159-52430bf6b52c/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk=
github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
github.com/adrg/xdg v0.3.3 h1:s/tV7MdqQnzB1nKY8aqHvAMD+uCiuEDzVB5HLRY849U=
github.com/adrg/xdg v0.3.3/go.mod h1:61xAR2VZcggl2St4O9ohF5qCKe08+JDmE4VNzPFQvOQ=
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 h1:smF2tmSOzy2Mm+0dGI2AIUHY+w0BUc+4tn40djz7+6U= github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 h1:smF2tmSOzy2Mm+0dGI2AIUHY+w0BUc+4tn40djz7+6U=
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI= github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI=
github.com/alecthomas/chroma v0.8.1 h1:ym20sbvyC6RXz45u4qDglcgr8E313oPROshcuCHqiEE= github.com/alecthomas/chroma v0.8.2/go.mod h1:sko8vR34/90zvl5QdcUdvzL3J8NKjAUx9va9jPuFNoM=
github.com/alecthomas/chroma v0.8.1/go.mod h1:sko8vR34/90zvl5QdcUdvzL3J8NKjAUx9va9jPuFNoM= github.com/alecthomas/chroma v0.9.2 h1:yU1sE2+TZbLIQPMk30SolL2Hn53SR/Pv750f7qZ/XMs=
github.com/alecthomas/chroma v0.9.2/go.mod h1:eMuEnpA18XbG/WhOWtCzJHS7WqEtDAI+HxdwoW0nVSk=
github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721 h1:JHZL0hZKJ1VENNfmXvHbgYlbUOvpzYzvy2aZU5gXVeo= github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721 h1:JHZL0hZKJ1VENNfmXvHbgYlbUOvpzYzvy2aZU5gXVeo=
github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0= github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0=
github.com/alecthomas/kong v0.2.4/go.mod h1:kQOmtJgV+Lb4aj+I2LEn40cbtawdWJ9Y8QLq+lElKxE= github.com/alecthomas/kong v0.2.4/go.mod h1:kQOmtJgV+Lb4aj+I2LEn40cbtawdWJ9Y8QLq+lElKxE=
@ -27,25 +32,25 @@ github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897 h1:p9Sln00KOTlrYkx
github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ= github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/araddon/dateparse v0.0.0-20210207001429-0eec95c9db7e h1:OjdSMCht0ZVX7IH0nTdf00xEustvbtUGRgMh3gbdmOg= github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de h1:FxWPpzIjnTlhPwqqXc4/vE0f7GvRjuAsbW+HOIe8KnA=
github.com/araddon/dateparse v0.0.0-20210207001429-0eec95c9db7e/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw= github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/chris-ramon/douceur v0.2.0 h1:IDMEdxlEUUBYBKE4z/mJnFyVXox+MjuEVDJNN27glkU=
github.com/chris-ramon/douceur v0.2.0/go.mod h1:wDW5xjJdeoMm1mRt4sD4c/LbF/mWdEpRXQKjTR8nIBE=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ= github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ=
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk= github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dlclark/regexp2 v1.2.0 h1:8sAhBGEM0dRWogWqWyQeIJnxjWO6oIjl8FKqREDsGfk=
github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
@ -54,12 +59,13 @@ github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
github.com/go-git/go-billy/v5 v5.0.0 h1:7NQHvd9FVid8VL4qVUMm8XifBK+2xCoZ2lSk0agRrHM= github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34=
github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12 h1:PbKy9zOy4aAKrJ5pibIRpVO2BXnK1Tlcg+caKI7Ox5M= github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw= github.com/go-git/go-git-fixtures/v4 v4.2.1 h1:n9gGL1Ct/yIw+nfsfr8s4+sbhT+Ncu2SubfXjIWgci8=
github.com/go-git/go-git/v5 v5.2.0 h1:YPBLG/3UK1we1ohRkncLjaXWLW+HKp5QNM/jTli2JgI= github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0=
github.com/go-git/go-git/v5 v5.2.0/go.mod h1:kh02eMX+wdqqxgNMEyq8YgwlIOsDOa9homkUq1PoTMs= github.com/go-git/go-git/v5 v5.4.2 h1:BXyZu9t0VkbiHtqrsvdq39UDhGJTl1h55VW6CSC4aY4=
github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc=
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
@ -69,55 +75,60 @@ github.com/hashicorp/go-version v1.3.0 h1:McDWVJIU/y+u1BRV06dPaLfLCaT7fUTJLp5r04
github.com/hashicorp/go-version v1.3.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.3.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174 h1:WlZsjVhE8Af9IcZDGgJGQpNflI3+MJSBhsgT5PCtzBQ= github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174 h1:WlZsjVhE8Af9IcZDGgJGQpNflI3+MJSBhsgT5PCtzBQ=
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A= github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A=
github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 h1:DowS9hvgyYSX4TO5NpyC606/Z4SxnNYbT+WX27or6Ck=
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kevinburke/ssh_config v1.1.0 h1:pH/t1WS9NzT8go394IqZeJTMHVm6Cr6ZJ6AQ+mdNo/o=
github.com/kevinburke/ssh_config v1.1.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.4 h1:5Myjjh3JY/NaAi4IsUbHADytDyl1VE1Y9PXDlL+P/VQ= github.com/kr/pty v1.1.4 h1:5Myjjh3JY/NaAi4IsUbHADytDyl1VE1Y9PXDlL+P/VQ=
github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A=
github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA=
github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg=
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/microcosm-cc/bluemonday v1.0.4 h1:p0L+CTpo/PLFdkoPcJemLXG+fpMD7pYOoDEq1axMbGg= github.com/microcosm-cc/bluemonday v1.0.6/go.mod h1:HOT/6NaBlR0f9XlxD3zolN6Z3N8Lp4pvhp+jLS5ihnI=
github.com/microcosm-cc/bluemonday v1.0.4/go.mod h1:8iwZnFn2CDDNZ0r6UXhF4xawGvzaqzCRa1n3/lO3W2w= github.com/microcosm-cc/bluemonday v1.0.15 h1:J4uN+qPng9rvkBZBoBb8YGR+ijuklIMpSOZZLjYpbeY=
github.com/microcosm-cc/bluemonday v1.0.15/go.mod h1:ZLvAzeakRwrGnzQEvstVzVt3ZpqOF2+sdFr0Om+ce30=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/muesli/reflow v0.2.0 h1:2o0UBJPHHH4fa2GCXU4Rg4DwOtWPMekCeyc5EWbAQp0=
github.com/muesli/reflow v0.2.0/go.mod h1:qT22vjVmM9MIUeLgsVYe/Ye7eZlbv9dZjL3dVhUqLX8= github.com/muesli/reflow v0.2.0/go.mod h1:qT22vjVmM9MIUeLgsVYe/Ye7eZlbv9dZjL3dVhUqLX8=
github.com/muesli/termenv v0.7.4 h1:/pBqvU5CpkY53tU0vVn+xgs2ZTX63aH5nY+SSps5Xa8= github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
github.com/muesli/termenv v0.7.4/go.mod h1:pZ7qY9l3F7e5xsAOS0zCew2tME+p7bWeBkotCEcIIcc= github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/muesli/termenv v0.8.1/go.mod h1:kzt/D/4a88RoheZmwfqorY3A+tnsSMA9HJC/fQSFKo0=
github.com/muesli/termenv v0.9.0 h1:wnbOaGz+LUR3jNT0zOzinPnyDaCZUQRZj9GxK8eRVl8=
github.com/muesli/termenv v0.9.0/go.mod h1:R/LzAKf+suGs4IsO95y7+7DpFHO0KABgnZqtlyx2mBw=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/noerw/glamour v0.2.1-0.20210305125354-f0a29f1de0c2 h1:ACjOTGUGi7rt3JQU9GIFFs8sueFGShy6GcGjQhMmKjs= github.com/noerw/glamour v0.3.0-patch h1:yc3wdbUIySok6KYeX5BtWnlj+PvP1uYeCeTSwq2rtSw=
github.com/noerw/glamour v0.2.1-0.20210305125354-f0a29f1de0c2/go.mod h1:WIVFX8Y2VIK1Y/1qtXYL/Vvzqlcbo3VgVop9i2piPkE= github.com/noerw/glamour v0.3.0-patch/go.mod h1:TzF0koPZhqq0YVBNL100cPHznAAjVj7fksX2RInwjGw=
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@ -135,10 +146,12 @@ github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7
github.com/seletskiy/tplutil v0.0.0-20200921103632-f880f6245597 h1:nZY1S2jo+VtDrUfjO9XYI137O41hhRkxZNV5Fb5ixCA= github.com/seletskiy/tplutil v0.0.0-20200921103632-f880f6245597 h1:nZY1S2jo+VtDrUfjO9XYI137O41hhRkxZNV5Fb5ixCA=
github.com/seletskiy/tplutil v0.0.0-20200921103632-f880f6245597/go.mod h1:F8CBHSOjnzjx9EeXyWJTAzJyVxN+Y8JH2WjLMn4utiw= github.com/seletskiy/tplutil v0.0.0-20200921103632-f880f6245597/go.mod h1:F8CBHSOjnzjx9EeXyWJTAzJyVxN+Y8JH2WjLMn4utiw=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA= github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA=
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@ -147,74 +160,83 @@ github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M= github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI=
github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=
github.com/xanzy/ssh-agent v0.3.1 h1:AmzO1SSWxw73zxFZPRwaMN1MohDw8UyHnmuxyceTEGo=
github.com/xanzy/ssh-agent v0.3.1/go.mod h1:QIE4lCeL7nkC25x+yA3LBIYfwCc1TFziCtG7cBAac6w=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.1 h1:eVwehsLsZlCJCwXyGLgg+Q4iFWE/eTIMG0e8waCmm/I= github.com/yuin/goldmark v1.3.3/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.3.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.0 h1:OtISOGfH6sOWa1/qXqqAiOIAO6Z5J3AEAE18WAq6BiQ=
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark-emoji v1.0.1 h1:ctuWEyzGBwiucEqxzwe0SOYDXPAucOrE9NQC18Wa1os= github.com/yuin/goldmark-emoji v1.0.1 h1:ctuWEyzGBwiucEqxzwe0SOYDXPAucOrE9NQC18Wa1os=
github.com/yuin/goldmark-emoji v1.0.1/go.mod h1:2w1E6FEWLcDQkoTE+7HU6QF1F6SLlNGjRIBbIZQFqkQ= github.com/yuin/goldmark-emoji v1.0.1/go.mod h1:2w1E6FEWLcDQkoTE+7HU6QF1F6SLlNGjRIBbIZQFqkQ=
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/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-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ=
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
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-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k=
golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d h1:LO7XpTYMwTqxjLcGWPijK3vRXg1aWdlNOVOHRq45d7c=
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
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/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-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/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-20190221075227-b4e8571b14e0/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-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-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190530182044-ad28b68e88f1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/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-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210305034016-7844c3c200c3 h1:RdE7htvBru4I4VZQofQjCZk5W9+aLNlSF5n0zgVwm8s= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210305034016-7844c3c200c3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf h1:2ucpDCmfkl8Bd/FsLtiD653Wf96cW37s+iGx93zsu4k=
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE= golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
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.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
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-20200325010219-a49f79bcc224/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY= golang.org/x/tools v0.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
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=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -222,8 +244,9 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1N
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@ -3,7 +3,7 @@
[![Build Status](https://travis-ci.org/AlecAivazis/survey.svg?branch=feature%2Fpretty)](https://travis-ci.org/AlecAivazis/survey) [![Build Status](https://travis-ci.org/AlecAivazis/survey.svg?branch=feature%2Fpretty)](https://travis-ci.org/AlecAivazis/survey)
[![GoDoc](http://img.shields.io/badge/godoc-reference-5272B4.svg)](https://pkg.go.dev/github.com/AlecAivazis/survey/v2) [![GoDoc](http://img.shields.io/badge/godoc-reference-5272B4.svg)](https://pkg.go.dev/github.com/AlecAivazis/survey/v2)
A library for building interactive prompts. A library for building interactive and accessible prompts on terminals supporting ANSI escape sequences.
<img width="550" src="https://thumbs.gfycat.com/VillainousGraciousKouprey-size_restricted.gif"/> <img width="550" src="https://thumbs.gfycat.com/VillainousGraciousKouprey-size_restricted.gif"/>
@ -343,10 +343,12 @@ survey.AskOne(prompt, &color, survey.WithValidator(survey.Required))
validators include: validators include:
| name | valid types | description | notes | | name | valid types | description | notes |
| ------------ | ----------- | ----------------------------------------------------------- | ------------------------------------------------------------------------------------- | | ------------ | -------------- | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
| Required | any | Rejects zero values of the response type | Boolean values pass straight through since the zero value (false) is a valid response | | Required | any | Rejects zero values of the response type | Boolean values pass straight through since the zero value (false) is a valid response |
| MinLength(n) | string | Enforces that a response is at least the given length | | | MinLength(n) | string | Enforces that a response is at least the given length | |
| MaxLength(n) | string | Enforces that a response is no longer than the given length | | | MaxLength(n) | string | Enforces that a response is no longer than the given length | |
| MaxItems(n) | []OptionAnswer | Enforces that a response has no more selections of the indicated | |
| MinItems(n) | []OptionAnswer | Enforces that a response has no less selections of the indicated | |
## Help Text ## Help Text
@ -456,6 +458,12 @@ For some examples, you can see any of the tests in this repo.
## FAQ ## FAQ
### What kinds of IO are supported by `survey`?
survey aims to support most terminal emulators; it expects support for ANSI escape sequences.
This means that reading from piped stdin or writing to piped stdout is **not supported**,
and likely to break your application in these situations. See [#337](https://github.com/AlecAivazis/survey/pull/337#issue-581351617)
### Why isn't sending a SIGINT (aka. CTRL-C) signal working? ### Why isn't sending a SIGINT (aka. CTRL-C) signal working?
When you send an interrupt signal to the process, it only interrupts the current prompt instead of the entire process. This manifests in a `github.com/AlecAivazis/survey/v2/terminal.InterruptErr` being returned from `Ask` and `AskOne`. If you want to stop the process, handle the returned error in your code: When you send an interrupt signal to the process, it only interrupts the current prompt instead of the entire process. This manifests in a `github.com/AlecAivazis/survey/v2/terminal.InterruptErr` being returned from `Ask` and `AskOne`. If you want to stop the process, handle the returned error in your code:

View File

@ -1,19 +1,19 @@
task "install-deps" { task "install-deps" {
description = "Install all of package dependencies" description = "Install all of package dependencies"
pipeline = [ pipeline = [
"go get {{.files}}", "go get -v {{.files}}",
] ]
} }
task "tests" { task "tests" {
description = "Run the test suite" description = "Run the test suite"
command = "go test {{.files}}" command = "go test -v {{.files}}"
environment { environment = {
GOFLAGS = "-mod=vendor" GOFLAGS = "-mod=vendor"
} }
} }
variables { variables = {
files = "$(go list -v ./... | grep -iEv \"tests|examples\")" files = "$(go list -v ./... | grep -iEv \"tests|examples\")"
} }

View File

@ -29,7 +29,7 @@ var TemplateFuncsNoColor = map[string]interface{}{
//for colored output. The second string does not contain escape codes //for colored output. The second string does not contain escape codes
//and can be used by the renderer for layout purposes. //and can be used by the renderer for layout purposes.
func RunTemplate(tmpl string, data interface{}) (string, string, error) { func RunTemplate(tmpl string, data interface{}) (string, string, error) {
tPair, err := getTemplatePair(tmpl) tPair, err := GetTemplatePair(tmpl)
if err != nil { if err != nil {
return "", "", err return "", "", err
} }
@ -52,12 +52,12 @@ var (
memoMutex = &sync.RWMutex{} memoMutex = &sync.RWMutex{}
) )
//getTemplatePair returns a pair of compiled templates where the //GetTemplatePair returns a pair of compiled templates where the
//first template is generated for user-facing output and the //first template is generated for user-facing output and the
//second is generated for use by the renderer. The second //second is generated for use by the renderer. The second
//template does not contain any color escape codes, whereas //template does not contain any color escape codes, whereas
//the first template may or may not depending on DisableColor. //the first template may or may not depending on DisableColor.
func getTemplatePair(tmpl string) ([2]*template.Template, error) { func GetTemplatePair(tmpl string) ([2]*template.Template, error) {
memoMutex.RLock() memoMutex.RLock()
if t, ok := memoizedGetTemplate[tmpl]; ok { if t, ok := memoizedGetTemplate[tmpl]; ok {
memoMutex.RUnlock() memoMutex.RUnlock()

View File

@ -24,6 +24,11 @@ type OptionAnswer struct {
Index int Index int
} }
type reflectField struct {
value reflect.Value
fieldType reflect.StructField
}
func OptionAnswerList(incoming []string) []OptionAnswer { func OptionAnswerList(incoming []string) []OptionAnswer {
list := []OptionAnswer{} list := []OptionAnswer{}
for i, opt := range incoming { for i, opt := range incoming {
@ -63,13 +68,12 @@ func WriteAnswer(t interface{}, name string, v interface{}) (err error) {
} }
// get the name of the field that matches the string we were given // get the name of the field that matches the string we were given
fieldIndex, err := findFieldIndex(elem, name) field, _, err := findField(elem, name)
// if something went wrong // if something went wrong
if err != nil { if err != nil {
// bubble up // bubble up
return err return err
} }
field := elem.Field(fieldIndex)
// handle references to the Settable interface aswell // handle references to the Settable interface aswell
if s, ok := field.Interface().(Settable); ok { if s, ok := field.Interface().(Settable); ok {
// use the interface method // use the interface method
@ -156,37 +160,51 @@ func IsFieldNotMatch(err error) (string, bool) {
// BUG(AlecAivazis): the current implementation might cause weird conflicts if there are // BUG(AlecAivazis): the current implementation might cause weird conflicts if there are
// two fields with same name that only differ by casing. // two fields with same name that only differ by casing.
func findFieldIndex(s reflect.Value, name string) (int, error) { func findField(s reflect.Value, name string) (reflect.Value, reflect.StructField, error) {
// the type of the value
sType := s.Type() fields := flattenFields(s)
// first look for matching tags so we can overwrite matching field names // first look for matching tags so we can overwrite matching field names
for i := 0; i < sType.NumField(); i++ { for _, f := range fields {
// the field we are current scanning
field := sType.Field(i)
// the value of the survey tag // the value of the survey tag
tag := field.Tag.Get(tagName) tag := f.fieldType.Tag.Get(tagName)
// if the tag matches the name we are looking for // if the tag matches the name we are looking for
if tag != "" && tag == name { if tag != "" && tag == name {
// then we found our index // then we found our index
return i, nil return f.value, f.fieldType, nil
} }
} }
// then look for matching names // then look for matching names
for i := 0; i < sType.NumField(); i++ { for _, f := range fields {
// the field we are current scanning
field := sType.Field(i)
// if the name of the field matches what we're looking for // if the name of the field matches what we're looking for
if strings.ToLower(field.Name) == strings.ToLower(name) { if strings.ToLower(f.fieldType.Name) == strings.ToLower(name) {
return i, nil return f.value, f.fieldType, nil
} }
} }
// we didn't find the field // we didn't find the field
return -1, errFieldNotMatch{name} return reflect.Value{}, reflect.StructField{}, errFieldNotMatch{name}
}
func flattenFields(s reflect.Value) []reflectField {
sType := s.Type()
numField := sType.NumField()
fields := make([]reflectField, 0, numField)
for i := 0; i < numField; i++ {
fieldType := sType.Field(i)
field := s.Field(i)
if field.Kind() == reflect.Struct && fieldType.Anonymous {
// field is a promoted structure
for _, f := range flattenFields(field) {
fields = append(fields, f)
}
continue
}
fields = append(fields, reflectField{field, fieldType})
}
return fields
} }
// isList returns true if the element is something we can Len() // isList returns true if the element is something we can Len()

View File

@ -11,9 +11,9 @@ require (
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/testify v1.2.1 github.com/stretchr/testify v1.2.1
golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5 golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5 // indirect
golang.org/x/sys v0.0.0-20190530182044-ad28b68e88f1 // indirect golang.org/x/term v0.0.0-20210503060354-a79de5458b56
golang.org/x/text v0.3.0 golang.org/x/text v0.3.3
) )
go 1.13 go 1.13

View File

@ -25,7 +25,11 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
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-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-20190530182044-ad28b68e88f1 h1:R4dVlxdmKenVdMRS/tTspEpSTRWINYrHD8ySIU9yCIU= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
golang.org/x/sys v0.0.0-20190530182044-ad28b68e88f1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/term v0.0.0-20210503060354-a79de5458b56 h1:b8jxX3zqjpqb2LklXPzKSGJhzyxCOZSz8ncv8Nv+y7w=
golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY=
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.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

View File

@ -1,6 +1,8 @@
package survey package survey
import ( import (
"errors"
"github.com/AlecAivazis/survey/v2/core" "github.com/AlecAivazis/survey/v2/core"
"github.com/AlecAivazis/survey/v2/terminal" "github.com/AlecAivazis/survey/v2/terminal"
) )
@ -19,8 +21,8 @@ type Input struct {
Default string Default string
Help string Help string
Suggest func(toComplete string) []string Suggest func(toComplete string) []string
typedAnswer string
answer string answer string
typedAnswer string
options []core.OptionAnswer options []core.OptionAnswer
selectedIndex int selectedIndex int
showingHelp bool showingHelp bool
@ -58,24 +60,14 @@ var InputQuestionTemplate = `
{{- if and .Suggest }}{{color "cyan"}}{{ print .Config.SuggestInput }} for suggestions{{end -}} {{- if and .Suggest }}{{color "cyan"}}{{ print .Config.SuggestInput }} for suggestions{{end -}}
]{{color "reset"}} {{end}} ]{{color "reset"}} {{end}}
{{- if .Default}}{{color "white"}}({{.Default}}) {{color "reset"}}{{end}} {{- if .Default}}{{color "white"}}({{.Default}}) {{color "reset"}}{{end}}
{{- .Answer -}}
{{- end}}` {{- end}}`
func (i *Input) OnChange(key rune, config *PromptConfig) (bool, error) { func (i *Input) onRune(config *PromptConfig) terminal.OnRuneFn {
if key == terminal.KeyEnter || key == '\n' { return terminal.OnRuneFn(func(key rune, line []rune) ([]rune, bool, error) {
if i.answer != config.HelpInput || i.Help == "" { if i.options != nil && (key == terminal.KeyEnter || key == '\n') {
// we're done return []rune(i.answer), true, nil
return true, nil } else if i.options != nil && key == terminal.KeyEscape {
} else {
i.answer = ""
i.showingHelp = true
}
} else if key == terminal.KeyDeleteWord || key == terminal.KeyDeleteLine {
i.answer = ""
} else if key == terminal.KeyEscape && i.Suggest != nil {
if len(i.options) > 0 {
i.answer = i.typedAnswer i.answer = i.typedAnswer
}
i.options = nil i.options = nil
} else if key == terminal.KeyArrowUp && len(i.options) > 0 { } else if key == terminal.KeyArrowUp && len(i.options) > 0 {
if i.selectedIndex == 0 { if i.selectedIndex == 0 {
@ -92,25 +84,31 @@ func (i *Input) OnChange(key rune, config *PromptConfig) (bool, error) {
} }
i.answer = i.options[i.selectedIndex].Value i.answer = i.options[i.selectedIndex].Value
} else if key == terminal.KeyTab && i.Suggest != nil { } else if key == terminal.KeyTab && i.Suggest != nil {
i.answer = string(line)
i.typedAnswer = i.answer
options := i.Suggest(i.answer) options := i.Suggest(i.answer)
i.selectedIndex = 0 i.selectedIndex = 0
i.typedAnswer = i.answer if len(options) == 0 {
if len(options) > 0 { return line, false, nil
}
i.answer = options[0] i.answer = options[0]
if len(options) == 1 { if len(options) == 1 {
i.typedAnswer = i.answer
i.options = nil i.options = nil
} else { } else {
i.options = core.OptionAnswerList(options) i.options = core.OptionAnswerList(options)
} }
} else {
if i.options == nil {
return line, false, nil
} }
} else if key == terminal.KeyDelete || key == terminal.KeyBackspace {
if i.answer != "" { if key >= terminal.KeySpace {
runeAnswer := []rune(i.answer)
i.answer = string(runeAnswer[0 : len(runeAnswer)-1])
}
} else if key >= terminal.KeySpace {
i.answer += string(key) i.answer += string(key)
}
i.typedAnswer = i.answer i.typedAnswer = i.answer
i.options = nil i.options = nil
} }
@ -128,9 +126,16 @@ func (i *Input) OnChange(key rune, config *PromptConfig) (bool, error) {
}, },
) )
return err != nil, err if err == nil {
err = readLineAgain
}
return []rune(i.typedAnswer), true, err
})
} }
var readLineAgain = errors.New("read line again")
func (i *Input) Prompt(config *PromptConfig) (interface{}, error) { func (i *Input) Prompt(config *PromptConfig) (interface{}, error) {
// render the template // render the template
err := i.Render( err := i.Render(
@ -138,6 +143,7 @@ func (i *Input) Prompt(config *PromptConfig) (interface{}, error) {
InputTemplateData{ InputTemplateData{
Input: *i, Input: *i,
Config: config, Config: config,
ShowHelp: i.showingHelp,
}, },
) )
if err != nil { if err != nil {
@ -150,30 +156,39 @@ func (i *Input) Prompt(config *PromptConfig) (interface{}, error) {
defer rr.RestoreTermMode() defer rr.RestoreTermMode()
cursor := i.NewCursor() cursor := i.NewCursor()
if !config.ShowCursor {
cursor.Hide() // hide the cursor cursor.Hide() // hide the cursor
defer cursor.Show() // show the cursor when we're done defer cursor.Show() // show the cursor when we're done
}
var line []rune
// start waiting for input
for { for {
r, _, err := rr.ReadRune() if i.options != nil {
if err != nil { line = []rune{}
return "", err }
}
if r == terminal.KeyInterrupt { line, err = rr.ReadLineWithDefault(0, line, i.onRune(config))
return "", terminal.InterruptErr if err == readLineAgain {
} continue
if r == terminal.KeyEndTransmission {
break
} }
b, err := i.OnChange(r, config)
if err != nil { if err != nil {
return "", err return "", err
} }
if b {
break break
} }
i.answer = string(line)
// readline print an empty line, go up before we render the follow up
cursor.Up(1)
// if we ran into the help string
if i.answer == config.HelpInput && i.Help != "" {
// show the help and prompt again
i.showingHelp = true
return i.Prompt(config)
} }
// if the line is empty // if the line is empty

View File

@ -45,9 +45,27 @@ type MultiSelectTemplateData struct {
ShowHelp bool ShowHelp bool
PageEntries []core.OptionAnswer PageEntries []core.OptionAnswer
Config *PromptConfig Config *PromptConfig
// These fields are used when rendering an individual option
CurrentOpt core.OptionAnswer
CurrentIndex int
}
// IterateOption sets CurrentOpt and CurrentIndex appropriately so a multiselect option can be rendered individually
func (m MultiSelectTemplateData) IterateOption(ix int, opt core.OptionAnswer) interface{} {
copy := m
copy.CurrentIndex = ix
copy.CurrentOpt = opt
return copy
} }
var MultiSelectQuestionTemplate = ` var MultiSelectQuestionTemplate = `
{{- define "option"}}
{{- if eq .SelectedIndex .CurrentIndex }}{{color .Config.Icons.SelectFocus.Format }}{{ .Config.Icons.SelectFocus.Text }}{{color "reset"}}{{else}} {{end}}
{{- if index .Checked .CurrentOpt.Index }}{{color .Config.Icons.MarkedOption.Format }} {{ .Config.Icons.MarkedOption.Text }} {{else}}{{color .Config.Icons.UnmarkedOption.Format }} {{ .Config.Icons.UnmarkedOption.Text }} {{end}}
{{- color "reset"}}
{{- " "}}{{- .CurrentOpt.Value}}
{{end}}
{{- if .ShowHelp }}{{- color .Config.Icons.Help.Format }}{{ .Config.Icons.Help.Text }} {{ .Help }}{{color "reset"}}{{"\n"}}{{end}} {{- if .ShowHelp }}{{- color .Config.Icons.Help.Format }}{{ .Config.Icons.Help.Text }} {{ .Help }}{{color "reset"}}{{"\n"}}{{end}}
{{- color .Config.Icons.Question.Format }}{{ .Config.Icons.Question.Text }} {{color "reset"}} {{- color .Config.Icons.Question.Format }}{{ .Config.Icons.Question.Text }} {{color "reset"}}
{{- color "default+hb"}}{{ .Message }}{{ .FilterMessage }}{{color "reset"}} {{- color "default+hb"}}{{ .Message }}{{ .FilterMessage }}{{color "reset"}}
@ -56,10 +74,7 @@ var MultiSelectQuestionTemplate = `
{{- " "}}{{- color "cyan"}}[Use arrows to move, space to select, <right> to all, <left> to none, type to filter{{- if and .Help (not .ShowHelp)}}, {{ .Config.HelpInput }} for more help{{end}}]{{color "reset"}} {{- " "}}{{- color "cyan"}}[Use arrows to move, space to select, <right> to all, <left> to none, type to filter{{- if and .Help (not .ShowHelp)}}, {{ .Config.HelpInput }} for more help{{end}}]{{color "reset"}}
{{- "\n"}} {{- "\n"}}
{{- range $ix, $option := .PageEntries}} {{- range $ix, $option := .PageEntries}}
{{- if eq $ix $.SelectedIndex }}{{color $.Config.Icons.SelectFocus.Format }}{{ $.Config.Icons.SelectFocus.Text }}{{color "reset"}}{{else}} {{end}} {{- template "option" $.IterateOption $ix $option}}
{{- if index $.Checked $option.Index }}{{color $.Config.Icons.MarkedOption.Format }} {{ $.Config.Icons.MarkedOption.Text }} {{else}}{{color $.Config.Icons.UnmarkedOption.Format }} {{ $.Config.Icons.UnmarkedOption.Text }} {{end}}
{{- color "reset"}}
{{- " "}}{{$option.Value}}{{"\n"}}
{{- end}} {{- end}}
{{- end}}` {{- end}}`
@ -159,18 +174,17 @@ func (m *MultiSelect) OnChange(key rune, config *PromptConfig) {
// and we have modified the filter then we should move the page back! // and we have modified the filter then we should move the page back!
opts, idx := paginate(pageSize, options, m.selectedIndex) opts, idx := paginate(pageSize, options, m.selectedIndex)
// render the options tmplData := MultiSelectTemplateData{
m.Render(
MultiSelectQuestionTemplate,
MultiSelectTemplateData{
MultiSelect: *m, MultiSelect: *m,
SelectedIndex: idx, SelectedIndex: idx,
Checked: m.checked, Checked: m.checked,
ShowHelp: m.showingHelp, ShowHelp: m.showingHelp,
PageEntries: opts, PageEntries: opts,
Config: config, Config: config,
}, }
)
// render the options
m.RenderWithCursorOffset(MultiSelectQuestionTemplate, tmplData, opts, idx)
} }
func (m *MultiSelect) filterOptions(config *PromptConfig) []core.OptionAnswer { func (m *MultiSelect) filterOptions(config *PromptConfig) []core.OptionAnswer {
@ -250,20 +264,21 @@ func (m *MultiSelect) Prompt(config *PromptConfig) (interface{}, error) {
opts, idx := paginate(pageSize, core.OptionAnswerList(m.Options), m.selectedIndex) opts, idx := paginate(pageSize, core.OptionAnswerList(m.Options), m.selectedIndex)
cursor := m.NewCursor() cursor := m.NewCursor()
cursor.Save() // for proper cursor placement during selection
cursor.Hide() // hide the cursor cursor.Hide() // hide the cursor
defer cursor.Show() // show the cursor when we're done defer cursor.Show() // show the cursor when we're done
defer cursor.Restore() // clear any accessibility offsetting on exit
// ask the question tmplData := MultiSelectTemplateData{
err := m.Render(
MultiSelectQuestionTemplate,
MultiSelectTemplateData{
MultiSelect: *m, MultiSelect: *m,
SelectedIndex: idx, SelectedIndex: idx,
Checked: m.checked, Checked: m.checked,
PageEntries: opts, PageEntries: opts,
Config: config, Config: config,
}, }
)
// ask the question
err := m.RenderWithCursorOffset(MultiSelectQuestionTemplate, tmplData, opts, idx)
if err != nil { if err != nil {
return "", err return "", err
} }

View File

@ -7,7 +7,7 @@ import (
"github.com/AlecAivazis/survey/v2/core" "github.com/AlecAivazis/survey/v2/core"
"github.com/AlecAivazis/survey/v2/terminal" "github.com/AlecAivazis/survey/v2/terminal"
goterm "golang.org/x/crypto/ssh/terminal" "golang.org/x/term"
) )
type Renderer struct { type Renderer struct {
@ -69,6 +69,14 @@ func (r *Renderer) Error(config *PromptConfig, invalid error) error {
return nil return nil
} }
func (r *Renderer) OffsetCursor(offset int) {
cursor := r.NewCursor()
for offset > 0 {
cursor.PreviousLine(1)
offset--
}
}
func (r *Renderer) Render(tmpl string, data interface{}) error { func (r *Renderer) Render(tmpl string, data interface{}) error {
// cleanup the currently rendered text // cleanup the currently rendered text
lineCount := r.countLines(r.renderedText) lineCount := r.countLines(r.renderedText)
@ -91,6 +99,21 @@ func (r *Renderer) Render(tmpl string, data interface{}) error {
return nil return nil
} }
func (r *Renderer) RenderWithCursorOffset(tmpl string, data IterableOpts, opts []core.OptionAnswer, idx int) error {
cursor := r.NewCursor()
cursor.Restore() // clear any accessibility offsetting
if err := r.Render(tmpl, data); err != nil {
return err
}
cursor.Save()
offset := computeCursorOffset(MultiSelectQuestionTemplate, data, opts, idx, r.termWidthSafe())
r.OffsetCursor(offset)
return nil
}
// appendRenderedError appends text to the renderer's error buffer // appendRenderedError appends text to the renderer's error buffer
// which is used to track what has been printed. It is not exported // which is used to track what has been printed. It is not exported
// as errors should only be displayed via Error(config, error). // as errors should only be displayed via Error(config, error).
@ -119,19 +142,24 @@ func (r *Renderer) resetPrompt(lines int) {
func (r *Renderer) termWidth() (int, error) { func (r *Renderer) termWidth() (int, error) {
fd := int(r.stdio.Out.Fd()) fd := int(r.stdio.Out.Fd())
termWidth, _, err := goterm.GetSize(fd) termWidth, _, err := term.GetSize(fd)
return termWidth, err return termWidth, err
} }
// countLines will return the count of `\n` with the addition of any func (r *Renderer) termWidthSafe() int {
// lines that have wrapped due to narrow terminal width
func (r *Renderer) countLines(buf bytes.Buffer) int {
w, err := r.termWidth() w, err := r.termWidth()
if err != nil || w == 0 { if err != nil || w == 0 {
// if we got an error due to terminal.GetSize not being supported // if we got an error due to terminal.GetSize not being supported
// on current platform then just assume a very wide terminal // on current platform then just assume a very wide terminal
w = 10000 w = 10000
} }
return w
}
// countLines will return the count of `\n` with the addition of any
// lines that have wrapped due to narrow terminal width
func (r *Renderer) countLines(buf bytes.Buffer) int {
w := r.termWidthSafe()
bufBytes := buf.Bytes() bufBytes := buf.Bytes()

View File

@ -43,9 +43,26 @@ type SelectTemplateData struct {
ShowAnswer bool ShowAnswer bool
ShowHelp bool ShowHelp bool
Config *PromptConfig Config *PromptConfig
// These fields are used when rendering an individual option
CurrentOpt core.OptionAnswer
CurrentIndex int
}
// IterateOption sets CurrentOpt and CurrentIndex appropriately so a select option can be rendered individually
func (s SelectTemplateData) IterateOption(ix int, opt core.OptionAnswer) interface{} {
copy := s
copy.CurrentIndex = ix
copy.CurrentOpt = opt
return copy
} }
var SelectQuestionTemplate = ` var SelectQuestionTemplate = `
{{- define "option"}}
{{- if eq .SelectedIndex .CurrentIndex }}{{color .Config.Icons.SelectFocus.Format }}{{ .Config.Icons.SelectFocus.Text }} {{else}}{{color "default"}} {{end}}
{{- .CurrentOpt.Value}}
{{- color "reset"}}
{{end}}
{{- if .ShowHelp }}{{- color .Config.Icons.Help.Format }}{{ .Config.Icons.Help.Text }} {{ .Help }}{{color "reset"}}{{"\n"}}{{end}} {{- if .ShowHelp }}{{- color .Config.Icons.Help.Format }}{{ .Config.Icons.Help.Text }} {{ .Help }}{{color "reset"}}{{"\n"}}{{end}}
{{- color .Config.Icons.Question.Format }}{{ .Config.Icons.Question.Text }} {{color "reset"}} {{- color .Config.Icons.Question.Format }}{{ .Config.Icons.Question.Text }} {{color "reset"}}
{{- color "default+hb"}}{{ .Message }}{{ .FilterMessage }}{{color "reset"}} {{- color "default+hb"}}{{ .Message }}{{ .FilterMessage }}{{color "reset"}}
@ -53,10 +70,8 @@ var SelectQuestionTemplate = `
{{- else}} {{- else}}
{{- " "}}{{- color "cyan"}}[Use arrows to move, type to filter{{- if and .Help (not .ShowHelp)}}, {{ .Config.HelpInput }} for more help{{end}}]{{color "reset"}} {{- " "}}{{- color "cyan"}}[Use arrows to move, type to filter{{- if and .Help (not .ShowHelp)}}, {{ .Config.HelpInput }} for more help{{end}}]{{color "reset"}}
{{- "\n"}} {{- "\n"}}
{{- range $ix, $choice := .PageEntries}} {{- range $ix, $option := .PageEntries}}
{{- if eq $ix $.SelectedIndex }}{{color $.Config.Icons.SelectFocus.Format }}{{ $.Config.Icons.SelectFocus.Text }} {{else}}{{color "default"}} {{end}} {{- template "option" $.IterateOption $ix $option}}
{{- $choice.Value}}
{{- color "reset"}}{{"\n"}}
{{- end}} {{- end}}
{{- end}}` {{- end}}`
@ -152,17 +167,16 @@ func (s *Select) OnChange(key rune, config *PromptConfig) bool {
// and we have modified the filter then we should move the page back! // and we have modified the filter then we should move the page back!
opts, idx := paginate(pageSize, options, s.selectedIndex) opts, idx := paginate(pageSize, options, s.selectedIndex)
// render the options tmplData := SelectTemplateData{
s.Render(
SelectQuestionTemplate,
SelectTemplateData{
Select: *s, Select: *s,
SelectedIndex: idx, SelectedIndex: idx,
ShowHelp: s.showingHelp, ShowHelp: s.showingHelp,
PageEntries: opts, PageEntries: opts,
Config: config, Config: config,
}, }
)
// render the options
s.RenderWithCursorOffset(SelectQuestionTemplate, tmplData, opts, idx)
// keep prompting // keep prompting
return false return false
@ -234,16 +248,22 @@ func (s *Select) Prompt(config *PromptConfig) (interface{}, error) {
// figure out the options and index to render // figure out the options and index to render
opts, idx := paginate(pageSize, core.OptionAnswerList(s.Options), sel) opts, idx := paginate(pageSize, core.OptionAnswerList(s.Options), sel)
// ask the question cursor := s.NewCursor()
err := s.Render( cursor.Save() // for proper cursor placement during selection
SelectQuestionTemplate, cursor.Hide() // hide the cursor
SelectTemplateData{ defer cursor.Show() // show the cursor when we're done
defer cursor.Restore() // clear any accessibility offsetting on exit
tmplData := SelectTemplateData{
Select: *s, Select: *s,
PageEntries: opts,
SelectedIndex: idx, SelectedIndex: idx,
ShowHelp: s.showingHelp,
PageEntries: opts,
Config: config, Config: config,
}, }
)
// ask the question
err := s.RenderWithCursorOffset(SelectQuestionTemplate, tmplData, opts, idx)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -255,10 +275,6 @@ func (s *Select) Prompt(config *PromptConfig) (interface{}, error) {
rr.SetTermMode() rr.SetTermMode()
defer rr.RestoreTermMode() defer rr.RestoreTermMode()
cursor := s.NewCursor()
cursor.Hide() // hide the cursor
defer cursor.Show() // show the cursor when we're done
// start waiting for input // start waiting for input
for { for {
r, _, err := rr.ReadRune() r, _, err := rr.ReadRune()
@ -317,6 +333,8 @@ func (s *Select) Prompt(config *PromptConfig) (interface{}, error) {
} }
func (s *Select) Cleanup(config *PromptConfig, val interface{}) error { func (s *Select) Cleanup(config *PromptConfig, val interface{}) error {
cursor := s.NewCursor()
cursor.Restore()
return s.Render( return s.Render(
SelectQuestionTemplate, SelectQuestionTemplate,
SelectTemplateData{ SelectTemplateData{

View File

@ -1,10 +1,12 @@
package survey package survey
import ( import (
"bytes"
"errors" "errors"
"io" "io"
"os" "os"
"strings" "strings"
"unicode/utf8"
"github.com/AlecAivazis/survey/v2/core" "github.com/AlecAivazis/survey/v2/core"
"github.com/AlecAivazis/survey/v2/terminal" "github.com/AlecAivazis/survey/v2/terminal"
@ -55,6 +57,7 @@ func defaultAskOptions() *AskOptions {
return strings.Contains(strings.ToLower(value), filter) return strings.Contains(strings.ToLower(value), filter)
}, },
KeepFilter: false, KeepFilter: false,
ShowCursor: false,
}, },
} }
} }
@ -114,6 +117,7 @@ type PromptConfig struct {
SuggestInput string SuggestInput string
Filter func(filter string, option string, index int) bool Filter func(filter string, option string, index int) bool
KeepFilter bool KeepFilter bool
ShowCursor bool
} }
// Prompt is the primary interface for the objects that can take user input // Prompt is the primary interface for the objects that can take user input
@ -219,6 +223,17 @@ func WithIcons(setIcons func(*IconSet)) AskOpt {
} }
} }
// WithShowCursor sets the show cursor behavior when prompting the user
func WithShowCursor(ShowCursor bool) AskOpt {
return func(options *AskOptions) error {
// set the page size
options.PromptConfig.ShowCursor = ShowCursor
// nothing went wrong
return nil
}
}
/* /*
AskOne performs the prompt for a single prompt and asks for validation if required. AskOne performs the prompt for a single prompt and asks for validation if required.
Response types should be something that can be casted from the response type designated Response types should be something that can be casted from the response type designated
@ -398,3 +413,42 @@ func paginate(pageSize int, choices []core.OptionAnswer, sel int) ([]core.Option
// return the subset we care about and the index // return the subset we care about and the index
return choices[start:end], cursor return choices[start:end], cursor
} }
type IterableOpts interface {
IterateOption(int, core.OptionAnswer) interface{}
}
func computeCursorOffset(tmpl string, data IterableOpts, opts []core.OptionAnswer, idx, tWidth int) int {
tmpls, err := core.GetTemplatePair(tmpl)
if err != nil {
return 0
}
t := tmpls[0]
renderOpt := func(ix int, opt core.OptionAnswer) string {
buf := bytes.NewBufferString("")
t.ExecuteTemplate(buf, "option", data.IterateOption(ix, opt))
return buf.String()
}
offset := len(opts) - idx
for i, o := range opts {
if i < idx {
continue
}
renderedOpt := renderOpt(i, o)
valWidth := utf8.RuneCount([]byte(renderedOpt))
if valWidth > tWidth {
splitCount := valWidth / tWidth
if valWidth%tWidth == 0 {
splitCount -= 1
}
offset += splitCount
}
}
return offset
}

View File

@ -43,11 +43,13 @@ func (c *Cursor) Back(n int) {
// NextLine moves cursor to beginning of the line n lines down. // NextLine moves cursor to beginning of the line n lines down.
func (c *Cursor) NextLine(n int) { func (c *Cursor) NextLine(n int) {
c.Down(1) c.Down(1)
c.HorizontalAbsolute(0)
} }
// PreviousLine moves cursor to beginning of the line n lines up. // PreviousLine moves cursor to beginning of the line n lines up.
func (c *Cursor) PreviousLine(n int) { func (c *Cursor) PreviousLine(n int) {
c.Up(1) c.Up(1)
c.HorizontalAbsolute(0)
} }
// HorizontalAbsolute moves cursor horizontally to x. // HorizontalAbsolute moves cursor horizontally to x.
@ -167,8 +169,11 @@ func (c *Cursor) Size(buf *bytes.Buffer) (*Coord, error) {
// hide the cursor (so it doesn't blink when getting the size of the terminal) // hide the cursor (so it doesn't blink when getting the size of the terminal)
c.Hide() c.Hide()
defer c.Show()
// save the current location of the cursor // save the current location of the cursor
c.Save() c.Save()
defer c.Restore()
// move the cursor to the very bottom of the terminal // move the cursor to the very bottom of the terminal
c.Move(999, 999) c.Move(999, 999)
@ -179,11 +184,6 @@ func (c *Cursor) Size(buf *bytes.Buffer) (*Coord, error) {
return nil, err return nil, err
} }
// move back where we began
c.Restore()
// show the cursor
c.Show()
// since the bottom was calculated in the lower right corner, it // since the bottom was calculated in the lower right corner, it
// is the dimensions we are looking for // is the dimensions we are looking for
return bottom, nil return bottom, nil

View File

@ -31,7 +31,13 @@ func (rr *RuneReader) printChar(char rune, mask rune) {
} }
} }
func (rr *RuneReader) ReadLine(mask rune) ([]rune, error) { type OnRuneFn func(rune, []rune) ([]rune, bool, error)
func (rr *RuneReader) ReadLine(mask rune, onRunes ...OnRuneFn) ([]rune, error) {
return rr.ReadLineWithDefault(mask, []rune{}, onRunes...)
}
func (rr *RuneReader) ReadLineWithDefault(mask rune, d []rune, onRunes ...OnRuneFn) ([]rune, error) {
line := []rune{} line := []rune{}
// we only care about horizontal displacements from the origin so start counting at 0 // we only care about horizontal displacements from the origin so start counting at 0
index := 0 index := 0
@ -41,19 +47,56 @@ func (rr *RuneReader) ReadLine(mask rune) ([]rune, error) {
Out: rr.stdio.Out, Out: rr.stdio.Out,
} }
onRune := func(r rune, line []rune) ([]rune, bool, error) {
return line, false, nil
}
// if the user pressed a key the caller was interested in capturing
if len(onRunes) > 0 {
onRune = onRunes[0]
}
// we get the terminal width and height (if resized after this point the property might become invalid) // we get the terminal width and height (if resized after this point the property might become invalid)
terminalSize, _ := cursor.Size(rr.Buffer()) terminalSize, _ := cursor.Size(rr.Buffer())
// we set the current location of the cursor once // we set the current location of the cursor once
cursorCurrent, _ := cursor.Location(rr.Buffer()) cursorCurrent, _ := cursor.Location(rr.Buffer())
increment := func() {
if cursorCurrent.CursorIsAtLineEnd(terminalSize) {
cursorCurrent.X = COORDINATE_SYSTEM_BEGIN
cursorCurrent.Y++
} else {
cursorCurrent.X++
}
}
decrement := func() {
if cursorCurrent.CursorIsAtLineBegin() {
cursorCurrent.X = terminalSize.X
cursorCurrent.Y--
} else {
cursorCurrent.X--
}
}
if len(d) > 0 {
index = len(d)
fmt.Fprint(rr.stdio.Out, string(d))
line = d
for range d {
increment()
}
}
for { for {
// wait for some input // wait for some input
r, _, err := rr.ReadRune() r, _, err := rr.ReadRune()
if err != nil { if err != nil {
return line, err return line, err
} }
// increment cursor location
cursorCurrent.X++ if l, stop, err := onRune(r, line); stop || err != nil {
return l, err
}
// if the user pressed enter or some other newline/termination like ctrl+d // if the user pressed enter or some other newline/termination like ctrl+d
if r == '\r' || r == '\n' || r == KeyEndTransmission { if r == '\r' || r == '\n' || r == KeyEndTransmission {
@ -63,13 +106,10 @@ func (rr *RuneReader) ReadLine(mask rune) ([]rune, error) {
EraseLine(rr.stdio.Out, ERASE_LINE_END) EraseLine(rr.stdio.Out, ERASE_LINE_END)
cursor.PreviousLine(1) cursor.PreviousLine(1)
cursor.Forward(int(terminalSize.X)) cursor.Forward(int(terminalSize.X))
cursorCurrent.X = terminalSize.X
cursorCurrent.Y--
} else { } else {
cursor.Back(1) cursor.Back(1)
cursorCurrent.X--
} }
decrement()
index-- index--
} }
// move the cursor the a new line // move the cursor the a new line
@ -148,6 +188,7 @@ func (rr *RuneReader) ReadLine(mask rune) ([]rune, error) {
// decrement the index // decrement the index
index-- index--
decrement()
} else { } else {
// otherwise the user pressed backspace while at the beginning of the line // otherwise the user pressed backspace while at the beginning of the line
soundBell(rr.stdio.Out) soundBell(rr.stdio.Out)
@ -170,6 +211,7 @@ func (rr *RuneReader) ReadLine(mask rune) ([]rune, error) {
} }
//decrement the index //decrement the index
index-- index--
decrement()
} else { } else {
// otherwise we are at the beginning of where we started reading lines // otherwise we are at the beginning of where we started reading lines
@ -192,6 +234,7 @@ func (rr *RuneReader) ReadLine(mask rune) ([]rune, error) {
cursor.Forward(runeWidth(line[index])) cursor.Forward(runeWidth(line[index]))
} }
index++ index++
increment()
} else { } else {
// otherwise we are at the end of the word and can't go past // otherwise we are at the end of the word and can't go past
@ -208,12 +251,11 @@ func (rr *RuneReader) ReadLine(mask rune) ([]rune, error) {
if cursorCurrent.CursorIsAtLineBegin() { if cursorCurrent.CursorIsAtLineBegin() {
cursor.PreviousLine(1) cursor.PreviousLine(1)
cursor.Forward(int(terminalSize.X)) cursor.Forward(int(terminalSize.X))
cursorCurrent.X = terminalSize.X
cursorCurrent.Y-- cursorCurrent.Y--
cursorCurrent.X = terminalSize.X
} else { } else {
cursor.Back(runeWidth(line[index-1])) cursor.Back(runeWidth(line[index-1]))
cursorCurrent.X-- cursorCurrent.X -= Short(runeWidth(line[index-1]))
} }
index-- index--
} }
@ -223,12 +265,11 @@ func (rr *RuneReader) ReadLine(mask rune) ([]rune, error) {
for index != len(line) { for index != len(line) {
if cursorCurrent.CursorIsAtLineEnd(terminalSize) { if cursorCurrent.CursorIsAtLineEnd(terminalSize) {
cursor.NextLine(1) cursor.NextLine(1)
cursorCurrent.X = COORDINATE_SYSTEM_BEGIN
cursorCurrent.Y++ cursorCurrent.Y++
cursorCurrent.X = COORDINATE_SYSTEM_BEGIN
} else { } else {
cursor.Forward(runeWidth(line[index])) cursor.Forward(runeWidth(line[index]))
cursorCurrent.X++ cursorCurrent.X += Short(runeWidth(line[index]))
} }
index++ index++
} }
@ -277,6 +318,7 @@ func (rr *RuneReader) ReadLine(mask rune) ([]rune, error) {
line = append(line, r) line = append(line, r)
// save the location of the cursor // save the location of the cursor
index++ index++
increment()
// print out the character // print out the character
rr.printChar(r, mask) rr.printChar(r, mask)
} else { } else {
@ -293,7 +335,7 @@ func (rr *RuneReader) ReadLine(mask rune) ([]rune, error) {
EraseLine(rr.stdio.Out, ERASE_LINE_END) EraseLine(rr.stdio.Out, ERASE_LINE_END)
// print out the character // print out the character
rr.printChar(char, mask) rr.printChar(char, mask)
cursorCurrent.X++ increment()
} }
// if we are at the last line, we want to visually insert a new line and append to it. // if we are at the last line, we want to visually insert a new line and append to it.
if cursorCurrent.CursorIsAtLineEnd(terminalSize) && cursorCurrent.Y == terminalSize.Y { if cursorCurrent.CursorIsAtLineEnd(terminalSize) && cursorCurrent.Y == terminalSize.Y {
@ -316,6 +358,7 @@ func (rr *RuneReader) ReadLine(mask rune) ([]rune, error) {
} }
// increment the index // increment the index
index++ index++
increment()
} }
} }

View File

@ -4,6 +4,8 @@ import (
"errors" "errors"
"fmt" "fmt"
"reflect" "reflect"
"github.com/AlecAivazis/survey/v2/core"
) )
// Required does not allow an empty value // Required does not allow an empty value
@ -58,6 +60,44 @@ func MinLength(length int) Validator {
} }
} }
// MaxItems requires that the list is no longer than the specified value
func MaxItems(numberItems int) Validator {
// return a validator that checks the length of the list
return func(val interface{}) error {
if list, ok := val.([]core.OptionAnswer); ok {
// if the list is longer than the given value
if len(list) > numberItems {
// yell loudly
return fmt.Errorf("value is too long. Max items is %v", numberItems)
}
} else {
// otherwise we cannot convert the value into a list of answer and cannot enforce length
return fmt.Errorf("cannot impose the length on something other than a list of answers")
}
// the input is fine
return nil
}
}
// MinItems requires that the list is longer or equal in length to the specified value
func MinItems(numberItems int) Validator {
// return a validator that checks the length of the list
return func(val interface{}) error {
if list, ok := val.([]core.OptionAnswer); ok {
// if the list is shorter than the given value
if len(list) < numberItems {
// yell loudly
return fmt.Errorf("value is too long. Min items is %v", numberItems)
}
} else {
// otherwise we cannot convert the value into a list of answer and cannot enforce length
return fmt.Errorf("cannot impose the length on something other than a list of answers")
}
// the input is fine
return nil
}
}
// ComposeValidators is a variadic function used to create one validator from many. // ComposeValidators is a variadic function used to create one validator from many.
func ComposeValidators(validators ...Validator) Validator { func ComposeValidators(validators ...Validator) Validator {
// return a validator that calls each one sequentially // return a validator that calls each one sequentially

1
vendor/github.com/Microsoft/go-winio/CODEOWNERS generated vendored Normal file
View File

@ -0,0 +1 @@
* @microsoft/containerplat

View File

@ -1,4 +1,4 @@
# go-winio # go-winio [![Build Status](https://github.com/microsoft/go-winio/actions/workflows/ci.yml/badge.svg)](https://github.com/microsoft/go-winio/actions/workflows/ci.yml)
This repository contains utilities for efficiently performing Win32 IO operations in This repository contains utilities for efficiently performing Win32 IO operations in
Go. Currently, this is focused on accessing named pipes and other file handles, and Go. Currently, this is focused on accessing named pipes and other file handles, and

View File

@ -5,21 +5,14 @@ package winio
import ( import (
"os" "os"
"runtime" "runtime"
"syscall"
"unsafe" "unsafe"
)
//sys getFileInformationByHandleEx(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) = GetFileInformationByHandleEx "golang.org/x/sys/windows"
//sys setFileInformationByHandle(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) = SetFileInformationByHandle
const (
fileBasicInfo = 0
fileIDInfo = 0x12
) )
// FileBasicInfo contains file access time and file attributes information. // FileBasicInfo contains file access time and file attributes information.
type FileBasicInfo struct { type FileBasicInfo struct {
CreationTime, LastAccessTime, LastWriteTime, ChangeTime syscall.Filetime CreationTime, LastAccessTime, LastWriteTime, ChangeTime windows.Filetime
FileAttributes uint32 FileAttributes uint32
pad uint32 // padding pad uint32 // padding
} }
@ -27,7 +20,7 @@ type FileBasicInfo struct {
// GetFileBasicInfo retrieves times and attributes for a file. // GetFileBasicInfo retrieves times and attributes for a file.
func GetFileBasicInfo(f *os.File) (*FileBasicInfo, error) { func GetFileBasicInfo(f *os.File) (*FileBasicInfo, error) {
bi := &FileBasicInfo{} bi := &FileBasicInfo{}
if err := getFileInformationByHandleEx(syscall.Handle(f.Fd()), fileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil { if err := windows.GetFileInformationByHandleEx(windows.Handle(f.Fd()), windows.FileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil {
return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err} return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err}
} }
runtime.KeepAlive(f) runtime.KeepAlive(f)
@ -36,13 +29,32 @@ func GetFileBasicInfo(f *os.File) (*FileBasicInfo, error) {
// SetFileBasicInfo sets times and attributes for a file. // SetFileBasicInfo sets times and attributes for a file.
func SetFileBasicInfo(f *os.File, bi *FileBasicInfo) error { func SetFileBasicInfo(f *os.File, bi *FileBasicInfo) error {
if err := setFileInformationByHandle(syscall.Handle(f.Fd()), fileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil { if err := windows.SetFileInformationByHandle(windows.Handle(f.Fd()), windows.FileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil {
return &os.PathError{Op: "SetFileInformationByHandle", Path: f.Name(), Err: err} return &os.PathError{Op: "SetFileInformationByHandle", Path: f.Name(), Err: err}
} }
runtime.KeepAlive(f) runtime.KeepAlive(f)
return nil return nil
} }
// FileStandardInfo contains extended information for the file.
// FILE_STANDARD_INFO in WinBase.h
// https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_standard_info
type FileStandardInfo struct {
AllocationSize, EndOfFile int64
NumberOfLinks uint32
DeletePending, Directory bool
}
// GetFileStandardInfo retrieves ended information for the file.
func GetFileStandardInfo(f *os.File) (*FileStandardInfo, error) {
si := &FileStandardInfo{}
if err := windows.GetFileInformationByHandleEx(windows.Handle(f.Fd()), windows.FileStandardInfo, (*byte)(unsafe.Pointer(si)), uint32(unsafe.Sizeof(*si))); err != nil {
return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err}
}
runtime.KeepAlive(f)
return si, nil
}
// FileIDInfo contains the volume serial number and file ID for a file. This pair should be // FileIDInfo contains the volume serial number and file ID for a file. This pair should be
// unique on a system. // unique on a system.
type FileIDInfo struct { type FileIDInfo struct {
@ -53,7 +65,7 @@ type FileIDInfo struct {
// GetFileID retrieves the unique (volume, file ID) pair for a file. // GetFileID retrieves the unique (volume, file ID) pair for a file.
func GetFileID(f *os.File) (*FileIDInfo, error) { func GetFileID(f *os.File) (*FileIDInfo, error) {
fileID := &FileIDInfo{} fileID := &FileIDInfo{}
if err := getFileInformationByHandleEx(syscall.Handle(f.Fd()), fileIDInfo, (*byte)(unsafe.Pointer(fileID)), uint32(unsafe.Sizeof(*fileID))); err != nil { if err := windows.GetFileInformationByHandleEx(windows.Handle(f.Fd()), windows.FileIdInfo, (*byte)(unsafe.Pointer(fileID)), uint32(unsafe.Sizeof(*fileID))); err != nil {
return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err} return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err}
} }
runtime.KeepAlive(f) runtime.KeepAlive(f)

View File

@ -4,6 +4,6 @@ go 1.12
require ( require (
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/sirupsen/logrus v1.4.1 github.com/sirupsen/logrus v1.7.0
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3 golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c
) )

View File

@ -1,16 +1,14 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.4.1 h1:GL2rEmy6nsikmW0r8opw9JIRScdMF5hA8cOYLH7In1k= github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3 h1:7TYNF4UdlohbFwpNH04CoPMp1cHUZgO1Ebq5r2hIjfo= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

View File

@ -1,3 +1,5 @@
// +build windows
package winio package winio
import ( import (

View File

@ -1,3 +1,5 @@
// +build windows
// Package guid provides a GUID type. The backing structure for a GUID is // Package guid provides a GUID type. The backing structure for a GUID is
// identical to that used by the golang.org/x/sys/windows GUID type. // identical to that used by the golang.org/x/sys/windows GUID type.
// There are two main binary encodings used for a GUID, the big-endian encoding, // There are two main binary encodings used for a GUID, the big-endian encoding,

View File

@ -30,6 +30,7 @@ const (
SeBackupPrivilege = "SeBackupPrivilege" SeBackupPrivilege = "SeBackupPrivilege"
SeRestorePrivilege = "SeRestorePrivilege" SeRestorePrivilege = "SeRestorePrivilege"
SeSecurityPrivilege = "SeSecurityPrivilege"
) )
const ( const (

View File

@ -1,3 +1,3 @@
package winio package winio
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go file.go pipe.go sd.go fileinfo.go privilege.go backup.go hvsock.go //go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zsyscall_windows.go file.go pipe.go sd.go fileinfo.go privilege.go backup.go hvsock.go

View File

@ -19,6 +19,7 @@ const (
var ( var (
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
errERROR_EINVAL error = syscall.EINVAL
) )
// errnoErr returns common boxed Errno values, to prevent // errnoErr returns common boxed Errno values, to prevent
@ -26,7 +27,7 @@ var (
func errnoErr(e syscall.Errno) error { func errnoErr(e syscall.Errno) error {
switch e { switch e {
case 0: case 0:
return nil return errERROR_EINVAL
case errnoERROR_IO_PENDING: case errnoERROR_IO_PENDING:
return errERROR_IO_PENDING return errERROR_IO_PENDING
} }
@ -37,243 +38,62 @@ func errnoErr(e syscall.Errno) error {
} }
var ( var (
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
modws2_32 = windows.NewLazySystemDLL("ws2_32.dll")
modntdll = windows.NewLazySystemDLL("ntdll.dll")
modadvapi32 = windows.NewLazySystemDLL("advapi32.dll") modadvapi32 = windows.NewLazySystemDLL("advapi32.dll")
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
modntdll = windows.NewLazySystemDLL("ntdll.dll")
modws2_32 = windows.NewLazySystemDLL("ws2_32.dll")
procCancelIoEx = modkernel32.NewProc("CancelIoEx") procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges")
procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort") procConvertSecurityDescriptorToStringSecurityDescriptorW = modadvapi32.NewProc("ConvertSecurityDescriptorToStringSecurityDescriptorW")
procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus")
procSetFileCompletionNotificationModes = modkernel32.NewProc("SetFileCompletionNotificationModes")
procWSAGetOverlappedResult = modws2_32.NewProc("WSAGetOverlappedResult")
procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe")
procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW")
procCreateFileW = modkernel32.NewProc("CreateFileW")
procGetNamedPipeInfo = modkernel32.NewProc("GetNamedPipeInfo")
procGetNamedPipeHandleStateW = modkernel32.NewProc("GetNamedPipeHandleStateW")
procLocalAlloc = modkernel32.NewProc("LocalAlloc")
procNtCreateNamedPipeFile = modntdll.NewProc("NtCreateNamedPipeFile")
procRtlNtStatusToDosErrorNoTeb = modntdll.NewProc("RtlNtStatusToDosErrorNoTeb")
procRtlDosPathNameToNtPathName_U = modntdll.NewProc("RtlDosPathNameToNtPathName_U")
procRtlDefaultNpAcl = modntdll.NewProc("RtlDefaultNpAcl")
procLookupAccountNameW = modadvapi32.NewProc("LookupAccountNameW")
procConvertSidToStringSidW = modadvapi32.NewProc("ConvertSidToStringSidW") procConvertSidToStringSidW = modadvapi32.NewProc("ConvertSidToStringSidW")
procConvertStringSecurityDescriptorToSecurityDescriptorW = modadvapi32.NewProc("ConvertStringSecurityDescriptorToSecurityDescriptorW") procConvertStringSecurityDescriptorToSecurityDescriptorW = modadvapi32.NewProc("ConvertStringSecurityDescriptorToSecurityDescriptorW")
procConvertSecurityDescriptorToStringSecurityDescriptorW = modadvapi32.NewProc("ConvertSecurityDescriptorToStringSecurityDescriptorW")
procLocalFree = modkernel32.NewProc("LocalFree")
procGetSecurityDescriptorLength = modadvapi32.NewProc("GetSecurityDescriptorLength") procGetSecurityDescriptorLength = modadvapi32.NewProc("GetSecurityDescriptorLength")
procGetFileInformationByHandleEx = modkernel32.NewProc("GetFileInformationByHandleEx")
procSetFileInformationByHandle = modkernel32.NewProc("SetFileInformationByHandle")
procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges")
procImpersonateSelf = modadvapi32.NewProc("ImpersonateSelf") procImpersonateSelf = modadvapi32.NewProc("ImpersonateSelf")
procRevertToSelf = modadvapi32.NewProc("RevertToSelf") procLookupAccountNameW = modadvapi32.NewProc("LookupAccountNameW")
procOpenThreadToken = modadvapi32.NewProc("OpenThreadToken")
procGetCurrentThread = modkernel32.NewProc("GetCurrentThread")
procLookupPrivilegeValueW = modadvapi32.NewProc("LookupPrivilegeValueW")
procLookupPrivilegeNameW = modadvapi32.NewProc("LookupPrivilegeNameW")
procLookupPrivilegeDisplayNameW = modadvapi32.NewProc("LookupPrivilegeDisplayNameW") procLookupPrivilegeDisplayNameW = modadvapi32.NewProc("LookupPrivilegeDisplayNameW")
procLookupPrivilegeNameW = modadvapi32.NewProc("LookupPrivilegeNameW")
procLookupPrivilegeValueW = modadvapi32.NewProc("LookupPrivilegeValueW")
procOpenThreadToken = modadvapi32.NewProc("OpenThreadToken")
procRevertToSelf = modadvapi32.NewProc("RevertToSelf")
procBackupRead = modkernel32.NewProc("BackupRead") procBackupRead = modkernel32.NewProc("BackupRead")
procBackupWrite = modkernel32.NewProc("BackupWrite") procBackupWrite = modkernel32.NewProc("BackupWrite")
procCancelIoEx = modkernel32.NewProc("CancelIoEx")
procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe")
procCreateFileW = modkernel32.NewProc("CreateFileW")
procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort")
procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW")
procGetCurrentThread = modkernel32.NewProc("GetCurrentThread")
procGetNamedPipeHandleStateW = modkernel32.NewProc("GetNamedPipeHandleStateW")
procGetNamedPipeInfo = modkernel32.NewProc("GetNamedPipeInfo")
procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus")
procLocalAlloc = modkernel32.NewProc("LocalAlloc")
procLocalFree = modkernel32.NewProc("LocalFree")
procSetFileCompletionNotificationModes = modkernel32.NewProc("SetFileCompletionNotificationModes")
procNtCreateNamedPipeFile = modntdll.NewProc("NtCreateNamedPipeFile")
procRtlDefaultNpAcl = modntdll.NewProc("RtlDefaultNpAcl")
procRtlDosPathNameToNtPathName_U = modntdll.NewProc("RtlDosPathNameToNtPathName_U")
procRtlNtStatusToDosErrorNoTeb = modntdll.NewProc("RtlNtStatusToDosErrorNoTeb")
procWSAGetOverlappedResult = modws2_32.NewProc("WSAGetOverlappedResult")
procbind = modws2_32.NewProc("bind") procbind = modws2_32.NewProc("bind")
) )
func cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) { func adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) {
r1, _, e1 := syscall.Syscall(procCancelIoEx.Addr(), 2, uintptr(file), uintptr(unsafe.Pointer(o)), 0)
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) {
r0, _, e1 := syscall.Syscall6(procCreateIoCompletionPort.Addr(), 4, uintptr(file), uintptr(port), uintptr(key), uintptr(threadCount), 0, 0)
newport = syscall.Handle(r0)
if newport == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) {
r1, _, e1 := syscall.Syscall6(procGetQueuedCompletionStatus.Addr(), 5, uintptr(port), uintptr(unsafe.Pointer(bytes)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(o)), uintptr(timeout), 0)
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) {
r1, _, e1 := syscall.Syscall(procSetFileCompletionNotificationModes.Addr(), 2, uintptr(h), uintptr(flags), 0)
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func wsaGetOverlappedResult(h syscall.Handle, o *syscall.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) {
var _p0 uint32 var _p0 uint32
if wait { if releaseAll {
_p0 = 1 _p0 = 1
} else {
_p0 = 0
} }
r1, _, e1 := syscall.Syscall6(procWSAGetOverlappedResult.Addr(), 5, uintptr(h), uintptr(unsafe.Pointer(o)), uintptr(unsafe.Pointer(bytes)), uintptr(_p0), uintptr(unsafe.Pointer(flags)), 0) r0, _, e1 := syscall.Syscall6(procAdjustTokenPrivileges.Addr(), 6, uintptr(token), uintptr(_p0), uintptr(unsafe.Pointer(input)), uintptr(outputSize), uintptr(unsafe.Pointer(output)), uintptr(unsafe.Pointer(requiredSize)))
success = r0 != 0
if true {
err = errnoErr(e1)
}
return
}
func convertSecurityDescriptorToStringSecurityDescriptor(sd *byte, revision uint32, secInfo uint32, sddl **uint16, sddlSize *uint32) (err error) {
r1, _, e1 := syscall.Syscall6(procConvertSecurityDescriptorToStringSecurityDescriptorW.Addr(), 5, uintptr(unsafe.Pointer(sd)), uintptr(revision), uintptr(secInfo), uintptr(unsafe.Pointer(sddl)), uintptr(unsafe.Pointer(sddlSize)), 0)
if r1 == 0 { if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1) err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) {
r1, _, e1 := syscall.Syscall(procConnectNamedPipe.Addr(), 2, uintptr(pipe), uintptr(unsafe.Pointer(o)), 0)
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) {
var _p0 *uint16
_p0, err = syscall.UTF16PtrFromString(name)
if err != nil {
return
}
return _createNamedPipe(_p0, flags, pipeMode, maxInstances, outSize, inSize, defaultTimeout, sa)
}
func _createNamedPipe(name *uint16, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) {
r0, _, e1 := syscall.Syscall9(procCreateNamedPipeW.Addr(), 8, uintptr(unsafe.Pointer(name)), uintptr(flags), uintptr(pipeMode), uintptr(maxInstances), uintptr(outSize), uintptr(inSize), uintptr(defaultTimeout), uintptr(unsafe.Pointer(sa)), 0)
handle = syscall.Handle(r0)
if handle == syscall.InvalidHandle {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func createFile(name string, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) {
var _p0 *uint16
_p0, err = syscall.UTF16PtrFromString(name)
if err != nil {
return
}
return _createFile(_p0, access, mode, sa, createmode, attrs, templatefile)
}
func _createFile(name *uint16, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) {
r0, _, e1 := syscall.Syscall9(procCreateFileW.Addr(), 7, uintptr(unsafe.Pointer(name)), uintptr(access), uintptr(mode), uintptr(unsafe.Pointer(sa)), uintptr(createmode), uintptr(attrs), uintptr(templatefile), 0, 0)
handle = syscall.Handle(r0)
if handle == syscall.InvalidHandle {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) {
r1, _, e1 := syscall.Syscall6(procGetNamedPipeInfo.Addr(), 5, uintptr(pipe), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(outSize)), uintptr(unsafe.Pointer(inSize)), uintptr(unsafe.Pointer(maxInstances)), 0)
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) {
r1, _, e1 := syscall.Syscall9(procGetNamedPipeHandleStateW.Addr(), 7, uintptr(pipe), uintptr(unsafe.Pointer(state)), uintptr(unsafe.Pointer(curInstances)), uintptr(unsafe.Pointer(maxCollectionCount)), uintptr(unsafe.Pointer(collectDataTimeout)), uintptr(unsafe.Pointer(userName)), uintptr(maxUserNameSize), 0, 0)
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func localAlloc(uFlags uint32, length uint32) (ptr uintptr) {
r0, _, _ := syscall.Syscall(procLocalAlloc.Addr(), 2, uintptr(uFlags), uintptr(length), 0)
ptr = uintptr(r0)
return
}
func ntCreateNamedPipeFile(pipe *syscall.Handle, access uint32, oa *objectAttributes, iosb *ioStatusBlock, share uint32, disposition uint32, options uint32, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntstatus) {
r0, _, _ := syscall.Syscall15(procNtCreateNamedPipeFile.Addr(), 14, uintptr(unsafe.Pointer(pipe)), uintptr(access), uintptr(unsafe.Pointer(oa)), uintptr(unsafe.Pointer(iosb)), uintptr(share), uintptr(disposition), uintptr(options), uintptr(typ), uintptr(readMode), uintptr(completionMode), uintptr(maxInstances), uintptr(inboundQuota), uintptr(outputQuota), uintptr(unsafe.Pointer(timeout)), 0)
status = ntstatus(r0)
return
}
func rtlNtStatusToDosError(status ntstatus) (winerr error) {
r0, _, _ := syscall.Syscall(procRtlNtStatusToDosErrorNoTeb.Addr(), 1, uintptr(status), 0, 0)
if r0 != 0 {
winerr = syscall.Errno(r0)
}
return
}
func rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntstatus) {
r0, _, _ := syscall.Syscall6(procRtlDosPathNameToNtPathName_U.Addr(), 4, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(ntName)), uintptr(filePart), uintptr(reserved), 0, 0)
status = ntstatus(r0)
return
}
func rtlDefaultNpAcl(dacl *uintptr) (status ntstatus) {
r0, _, _ := syscall.Syscall(procRtlDefaultNpAcl.Addr(), 1, uintptr(unsafe.Pointer(dacl)), 0, 0)
status = ntstatus(r0)
return
}
func lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) {
var _p0 *uint16
_p0, err = syscall.UTF16PtrFromString(accountName)
if err != nil {
return
}
return _lookupAccountName(systemName, _p0, sid, sidSize, refDomain, refDomainSize, sidNameUse)
}
func _lookupAccountName(systemName *uint16, accountName *uint16, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) {
r1, _, e1 := syscall.Syscall9(procLookupAccountNameW.Addr(), 7, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(accountName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(sidSize)), uintptr(unsafe.Pointer(refDomain)), uintptr(unsafe.Pointer(refDomainSize)), uintptr(unsafe.Pointer(sidNameUse)), 0, 0)
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
} }
return return
} }
@ -281,11 +101,7 @@ func _lookupAccountName(systemName *uint16, accountName *uint16, sid *byte, sidS
func convertSidToStringSid(sid *byte, str **uint16) (err error) { func convertSidToStringSid(sid *byte, str **uint16) (err error) {
r1, _, e1 := syscall.Syscall(procConvertSidToStringSidW.Addr(), 2, uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(str)), 0) r1, _, e1 := syscall.Syscall(procConvertSidToStringSidW.Addr(), 2, uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(str)), 0)
if r1 == 0 { if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1) err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
} }
return return
} }
@ -302,29 +118,8 @@ func convertStringSecurityDescriptorToSecurityDescriptor(str string, revision ui
func _convertStringSecurityDescriptorToSecurityDescriptor(str *uint16, revision uint32, sd *uintptr, size *uint32) (err error) { func _convertStringSecurityDescriptorToSecurityDescriptor(str *uint16, revision uint32, sd *uintptr, size *uint32) (err error) {
r1, _, e1 := syscall.Syscall6(procConvertStringSecurityDescriptorToSecurityDescriptorW.Addr(), 4, uintptr(unsafe.Pointer(str)), uintptr(revision), uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(size)), 0, 0) r1, _, e1 := syscall.Syscall6(procConvertStringSecurityDescriptorToSecurityDescriptorW.Addr(), 4, uintptr(unsafe.Pointer(str)), uintptr(revision), uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(size)), 0, 0)
if r1 == 0 { if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1) err = errnoErr(e1)
} else {
err = syscall.EINVAL
} }
}
return
}
func convertSecurityDescriptorToStringSecurityDescriptor(sd *byte, revision uint32, secInfo uint32, sddl **uint16, sddlSize *uint32) (err error) {
r1, _, e1 := syscall.Syscall6(procConvertSecurityDescriptorToStringSecurityDescriptorW.Addr(), 5, uintptr(unsafe.Pointer(sd)), uintptr(revision), uintptr(secInfo), uintptr(unsafe.Pointer(sddl)), uintptr(unsafe.Pointer(sddlSize)), 0)
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func localFree(mem uintptr) {
syscall.Syscall(procLocalFree.Addr(), 1, uintptr(mem), 0, 0)
return return
} }
@ -334,94 +129,62 @@ func getSecurityDescriptorLength(sd uintptr) (len uint32) {
return return
} }
func getFileInformationByHandleEx(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) {
r1, _, e1 := syscall.Syscall6(procGetFileInformationByHandleEx.Addr(), 4, uintptr(h), uintptr(class), uintptr(unsafe.Pointer(buffer)), uintptr(size), 0, 0)
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func setFileInformationByHandle(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) {
r1, _, e1 := syscall.Syscall6(procSetFileInformationByHandle.Addr(), 4, uintptr(h), uintptr(class), uintptr(unsafe.Pointer(buffer)), uintptr(size), 0, 0)
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) {
var _p0 uint32
if releaseAll {
_p0 = 1
} else {
_p0 = 0
}
r0, _, e1 := syscall.Syscall6(procAdjustTokenPrivileges.Addr(), 6, uintptr(token), uintptr(_p0), uintptr(unsafe.Pointer(input)), uintptr(outputSize), uintptr(unsafe.Pointer(output)), uintptr(unsafe.Pointer(requiredSize)))
success = r0 != 0
if true {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func impersonateSelf(level uint32) (err error) { func impersonateSelf(level uint32) (err error) {
r1, _, e1 := syscall.Syscall(procImpersonateSelf.Addr(), 1, uintptr(level), 0, 0) r1, _, e1 := syscall.Syscall(procImpersonateSelf.Addr(), 1, uintptr(level), 0, 0)
if r1 == 0 { if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1) err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
} }
return return
} }
func revertToSelf() (err error) { func lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) {
r1, _, e1 := syscall.Syscall(procRevertToSelf.Addr(), 0, 0, 0, 0) var _p0 *uint16
_p0, err = syscall.UTF16PtrFromString(accountName)
if err != nil {
return
}
return _lookupAccountName(systemName, _p0, sid, sidSize, refDomain, refDomainSize, sidNameUse)
}
func _lookupAccountName(systemName *uint16, accountName *uint16, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) {
r1, _, e1 := syscall.Syscall9(procLookupAccountNameW.Addr(), 7, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(accountName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(sidSize)), uintptr(unsafe.Pointer(refDomain)), uintptr(unsafe.Pointer(refDomainSize)), uintptr(unsafe.Pointer(sidNameUse)), 0, 0)
if r1 == 0 { if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1) err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
} }
return return
} }
func openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) { func lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) {
var _p0 uint32 var _p0 *uint16
if openAsSelf { _p0, err = syscall.UTF16PtrFromString(systemName)
_p0 = 1 if err != nil {
} else { return
_p0 = 0
} }
r1, _, e1 := syscall.Syscall6(procOpenThreadToken.Addr(), 4, uintptr(thread), uintptr(accessMask), uintptr(_p0), uintptr(unsafe.Pointer(token)), 0, 0) return _lookupPrivilegeDisplayName(_p0, name, buffer, size, languageId)
}
func _lookupPrivilegeDisplayName(systemName *uint16, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) {
r1, _, e1 := syscall.Syscall6(procLookupPrivilegeDisplayNameW.Addr(), 5, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), uintptr(unsafe.Pointer(languageId)), 0)
if r1 == 0 { if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1) err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
} }
return return
} }
func getCurrentThread() (h syscall.Handle) { func lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) {
r0, _, _ := syscall.Syscall(procGetCurrentThread.Addr(), 0, 0, 0, 0) var _p0 *uint16
h = syscall.Handle(r0) _p0, err = syscall.UTF16PtrFromString(systemName)
if err != nil {
return
}
return _lookupPrivilegeName(_p0, luid, buffer, size)
}
func _lookupPrivilegeName(systemName *uint16, luid *uint64, buffer *uint16, size *uint32) (err error) {
r1, _, e1 := syscall.Syscall6(procLookupPrivilegeNameW.Addr(), 4, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(luid)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), 0, 0)
if r1 == 0 {
err = errnoErr(e1)
}
return return
} }
@ -442,53 +205,27 @@ func lookupPrivilegeValue(systemName string, name string, luid *uint64) (err err
func _lookupPrivilegeValue(systemName *uint16, name *uint16, luid *uint64) (err error) { func _lookupPrivilegeValue(systemName *uint16, name *uint16, luid *uint64) (err error) {
r1, _, e1 := syscall.Syscall(procLookupPrivilegeValueW.Addr(), 3, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(luid))) r1, _, e1 := syscall.Syscall(procLookupPrivilegeValueW.Addr(), 3, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(luid)))
if r1 == 0 { if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1) err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
} }
return return
} }
func lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) { func openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) {
var _p0 *uint16 var _p0 uint32
_p0, err = syscall.UTF16PtrFromString(systemName) if openAsSelf {
if err != nil { _p0 = 1
return
} }
return _lookupPrivilegeName(_p0, luid, buffer, size) r1, _, e1 := syscall.Syscall6(procOpenThreadToken.Addr(), 4, uintptr(thread), uintptr(accessMask), uintptr(_p0), uintptr(unsafe.Pointer(token)), 0, 0)
}
func _lookupPrivilegeName(systemName *uint16, luid *uint64, buffer *uint16, size *uint32) (err error) {
r1, _, e1 := syscall.Syscall6(procLookupPrivilegeNameW.Addr(), 4, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(luid)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), 0, 0)
if r1 == 0 { if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1) err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
} }
return return
} }
func lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) { func revertToSelf() (err error) {
var _p0 *uint16 r1, _, e1 := syscall.Syscall(procRevertToSelf.Addr(), 0, 0, 0, 0)
_p0, err = syscall.UTF16PtrFromString(systemName)
if err != nil {
return
}
return _lookupPrivilegeDisplayName(_p0, name, buffer, size, languageId)
}
func _lookupPrivilegeDisplayName(systemName *uint16, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) {
r1, _, e1 := syscall.Syscall6(procLookupPrivilegeDisplayNameW.Addr(), 5, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), uintptr(unsafe.Pointer(languageId)), 0)
if r1 == 0 { if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1) err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
} }
return return
} }
@ -501,22 +238,14 @@ func backupRead(h syscall.Handle, b []byte, bytesRead *uint32, abort bool, proce
var _p1 uint32 var _p1 uint32
if abort { if abort {
_p1 = 1 _p1 = 1
} else {
_p1 = 0
} }
var _p2 uint32 var _p2 uint32
if processSecurity { if processSecurity {
_p2 = 1 _p2 = 1
} else {
_p2 = 0
} }
r1, _, e1 := syscall.Syscall9(procBackupRead.Addr(), 7, uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesRead)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)), 0, 0) r1, _, e1 := syscall.Syscall9(procBackupRead.Addr(), 7, uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesRead)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)), 0, 0)
if r1 == 0 { if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1) err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
} }
return return
} }
@ -529,22 +258,162 @@ func backupWrite(h syscall.Handle, b []byte, bytesWritten *uint32, abort bool, p
var _p1 uint32 var _p1 uint32
if abort { if abort {
_p1 = 1 _p1 = 1
} else {
_p1 = 0
} }
var _p2 uint32 var _p2 uint32
if processSecurity { if processSecurity {
_p2 = 1 _p2 = 1
} else {
_p2 = 0
} }
r1, _, e1 := syscall.Syscall9(procBackupWrite.Addr(), 7, uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesWritten)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)), 0, 0) r1, _, e1 := syscall.Syscall9(procBackupWrite.Addr(), 7, uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesWritten)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)), 0, 0)
if r1 == 0 { if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1) err = errnoErr(e1)
} else {
err = syscall.EINVAL
} }
return
}
func cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) {
r1, _, e1 := syscall.Syscall(procCancelIoEx.Addr(), 2, uintptr(file), uintptr(unsafe.Pointer(o)), 0)
if r1 == 0 {
err = errnoErr(e1)
}
return
}
func connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) {
r1, _, e1 := syscall.Syscall(procConnectNamedPipe.Addr(), 2, uintptr(pipe), uintptr(unsafe.Pointer(o)), 0)
if r1 == 0 {
err = errnoErr(e1)
}
return
}
func createFile(name string, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) {
var _p0 *uint16
_p0, err = syscall.UTF16PtrFromString(name)
if err != nil {
return
}
return _createFile(_p0, access, mode, sa, createmode, attrs, templatefile)
}
func _createFile(name *uint16, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) {
r0, _, e1 := syscall.Syscall9(procCreateFileW.Addr(), 7, uintptr(unsafe.Pointer(name)), uintptr(access), uintptr(mode), uintptr(unsafe.Pointer(sa)), uintptr(createmode), uintptr(attrs), uintptr(templatefile), 0, 0)
handle = syscall.Handle(r0)
if handle == syscall.InvalidHandle {
err = errnoErr(e1)
}
return
}
func createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) {
r0, _, e1 := syscall.Syscall6(procCreateIoCompletionPort.Addr(), 4, uintptr(file), uintptr(port), uintptr(key), uintptr(threadCount), 0, 0)
newport = syscall.Handle(r0)
if newport == 0 {
err = errnoErr(e1)
}
return
}
func createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) {
var _p0 *uint16
_p0, err = syscall.UTF16PtrFromString(name)
if err != nil {
return
}
return _createNamedPipe(_p0, flags, pipeMode, maxInstances, outSize, inSize, defaultTimeout, sa)
}
func _createNamedPipe(name *uint16, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) {
r0, _, e1 := syscall.Syscall9(procCreateNamedPipeW.Addr(), 8, uintptr(unsafe.Pointer(name)), uintptr(flags), uintptr(pipeMode), uintptr(maxInstances), uintptr(outSize), uintptr(inSize), uintptr(defaultTimeout), uintptr(unsafe.Pointer(sa)), 0)
handle = syscall.Handle(r0)
if handle == syscall.InvalidHandle {
err = errnoErr(e1)
}
return
}
func getCurrentThread() (h syscall.Handle) {
r0, _, _ := syscall.Syscall(procGetCurrentThread.Addr(), 0, 0, 0, 0)
h = syscall.Handle(r0)
return
}
func getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) {
r1, _, e1 := syscall.Syscall9(procGetNamedPipeHandleStateW.Addr(), 7, uintptr(pipe), uintptr(unsafe.Pointer(state)), uintptr(unsafe.Pointer(curInstances)), uintptr(unsafe.Pointer(maxCollectionCount)), uintptr(unsafe.Pointer(collectDataTimeout)), uintptr(unsafe.Pointer(userName)), uintptr(maxUserNameSize), 0, 0)
if r1 == 0 {
err = errnoErr(e1)
}
return
}
func getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) {
r1, _, e1 := syscall.Syscall6(procGetNamedPipeInfo.Addr(), 5, uintptr(pipe), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(outSize)), uintptr(unsafe.Pointer(inSize)), uintptr(unsafe.Pointer(maxInstances)), 0)
if r1 == 0 {
err = errnoErr(e1)
}
return
}
func getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) {
r1, _, e1 := syscall.Syscall6(procGetQueuedCompletionStatus.Addr(), 5, uintptr(port), uintptr(unsafe.Pointer(bytes)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(o)), uintptr(timeout), 0)
if r1 == 0 {
err = errnoErr(e1)
}
return
}
func localAlloc(uFlags uint32, length uint32) (ptr uintptr) {
r0, _, _ := syscall.Syscall(procLocalAlloc.Addr(), 2, uintptr(uFlags), uintptr(length), 0)
ptr = uintptr(r0)
return
}
func localFree(mem uintptr) {
syscall.Syscall(procLocalFree.Addr(), 1, uintptr(mem), 0, 0)
return
}
func setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) {
r1, _, e1 := syscall.Syscall(procSetFileCompletionNotificationModes.Addr(), 2, uintptr(h), uintptr(flags), 0)
if r1 == 0 {
err = errnoErr(e1)
}
return
}
func ntCreateNamedPipeFile(pipe *syscall.Handle, access uint32, oa *objectAttributes, iosb *ioStatusBlock, share uint32, disposition uint32, options uint32, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntstatus) {
r0, _, _ := syscall.Syscall15(procNtCreateNamedPipeFile.Addr(), 14, uintptr(unsafe.Pointer(pipe)), uintptr(access), uintptr(unsafe.Pointer(oa)), uintptr(unsafe.Pointer(iosb)), uintptr(share), uintptr(disposition), uintptr(options), uintptr(typ), uintptr(readMode), uintptr(completionMode), uintptr(maxInstances), uintptr(inboundQuota), uintptr(outputQuota), uintptr(unsafe.Pointer(timeout)), 0)
status = ntstatus(r0)
return
}
func rtlDefaultNpAcl(dacl *uintptr) (status ntstatus) {
r0, _, _ := syscall.Syscall(procRtlDefaultNpAcl.Addr(), 1, uintptr(unsafe.Pointer(dacl)), 0, 0)
status = ntstatus(r0)
return
}
func rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntstatus) {
r0, _, _ := syscall.Syscall6(procRtlDosPathNameToNtPathName_U.Addr(), 4, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(ntName)), uintptr(filePart), uintptr(reserved), 0, 0)
status = ntstatus(r0)
return
}
func rtlNtStatusToDosError(status ntstatus) (winerr error) {
r0, _, _ := syscall.Syscall(procRtlNtStatusToDosErrorNoTeb.Addr(), 1, uintptr(status), 0, 0)
if r0 != 0 {
winerr = syscall.Errno(r0)
}
return
}
func wsaGetOverlappedResult(h syscall.Handle, o *syscall.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) {
var _p0 uint32
if wait {
_p0 = 1
}
r1, _, e1 := syscall.Syscall6(procWSAGetOverlappedResult.Addr(), 5, uintptr(h), uintptr(unsafe.Pointer(o)), uintptr(unsafe.Pointer(bytes)), uintptr(_p0), uintptr(unsafe.Pointer(flags)), 0)
if r1 == 0 {
err = errnoErr(e1)
} }
return return
} }
@ -552,11 +421,7 @@ func backupWrite(h syscall.Handle, b []byte, bytesWritten *uint32, abort bool, p
func bind(s syscall.Handle, name unsafe.Pointer, namelen int32) (err error) { func bind(s syscall.Handle, name unsafe.Pointer, namelen int32) (err error) {
r1, _, e1 := syscall.Syscall(procbind.Addr(), 3, uintptr(s), uintptr(name), uintptr(namelen)) r1, _, e1 := syscall.Syscall(procbind.Addr(), 3, uintptr(s), uintptr(name), uintptr(namelen))
if r1 == socketError { if r1 == socketError {
if e1 != 0 {
err = errnoErr(e1) err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
} }
return return
} }

3
vendor/github.com/ProtonMail/go-crypto/AUTHORS generated vendored Normal file
View File

@ -0,0 +1,3 @@
# This source code refers to The Go Authors for copyright purposes.
# The master list of authors is in the main Go distribution,
# visible at https://tip.golang.org/AUTHORS.

3
vendor/github.com/ProtonMail/go-crypto/CONTRIBUTORS generated vendored Normal file
View File

@ -0,0 +1,3 @@
# This source code was written by the Go contributors.
# The master list of contributors is in the main Go distribution,
# visible at https://tip.golang.org/CONTRIBUTORS.

27
vendor/github.com/ProtonMail/go-crypto/LICENSE generated vendored Normal file
View File

@ -0,0 +1,27 @@
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

22
vendor/github.com/ProtonMail/go-crypto/PATENTS generated vendored Normal file
View File

@ -0,0 +1,22 @@
Additional IP Rights Grant (Patents)
"This implementation" means the copyrightable works distributed by
Google as part of the Go project.
Google hereby grants to You a perpetual, worldwide, non-exclusive,
no-charge, royalty-free, irrevocable (except as stated in this section)
patent license to make, have made, use, offer to sell, sell, import,
transfer and otherwise run, modify and propagate the contents of this
implementation of Go, where such license applies only to those patent
claims, both currently owned or controlled by Google and acquired in
the future, licensable by Google that are necessarily infringed by this
implementation of Go. This grant does not include claims that would be
infringed only as a consequence of further modification of this
implementation. If you or your agent or exclusive licensee institute or
order or agree to the institution of patent litigation against any
entity (including a cross-claim or counterclaim in a lawsuit) alleging
that this implementation of Go or any code incorporated within this
implementation of Go constitutes direct or contributory patent
infringement, or inducement of patent infringement, then any patent
rights granted to you under this License for this implementation of Go
shall terminate as of the date such litigation is filed.

View File

@ -0,0 +1,381 @@
package bitcurves
// Copyright 2010 The Go Authors. All rights reserved.
// Copyright 2011 ThePiachu. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package bitelliptic implements several Koblitz elliptic curves over prime
// fields.
// This package operates, internally, on Jacobian coordinates. For a given
// (x, y) position on the curve, the Jacobian coordinates are (x1, y1, z1)
// where x = x1/z1² and y = y1/z1³. The greatest speedups come when the whole
// calculation can be performed within the transform (as in ScalarMult and
// ScalarBaseMult). But even for Add and Double, it's faster to apply and
// reverse the transform than to operate in affine coordinates.
import (
"crypto/elliptic"
"io"
"math/big"
"sync"
)
// A BitCurve represents a Koblitz Curve with a=0.
// See http://www.hyperelliptic.org/EFD/g1p/auto-shortw.html
type BitCurve struct {
Name string
P *big.Int // the order of the underlying field
N *big.Int // the order of the base point
B *big.Int // the constant of the BitCurve equation
Gx, Gy *big.Int // (x,y) of the base point
BitSize int // the size of the underlying field
}
// Params returns the parameters of the given BitCurve (see BitCurve struct)
func (bitCurve *BitCurve) Params() (cp *elliptic.CurveParams) {
cp = new(elliptic.CurveParams)
cp.Name = bitCurve.Name
cp.P = bitCurve.P
cp.N = bitCurve.N
cp.Gx = bitCurve.Gx
cp.Gy = bitCurve.Gy
cp.BitSize = bitCurve.BitSize
return cp
}
// IsOnCurve returns true if the given (x,y) lies on the BitCurve.
func (bitCurve *BitCurve) IsOnCurve(x, y *big.Int) bool {
// y² = x³ + b
y2 := new(big.Int).Mul(y, y) //y²
y2.Mod(y2, bitCurve.P) //y²%P
x3 := new(big.Int).Mul(x, x) //x²
x3.Mul(x3, x) //x³
x3.Add(x3, bitCurve.B) //x³+B
x3.Mod(x3, bitCurve.P) //(x³+B)%P
return x3.Cmp(y2) == 0
}
// affineFromJacobian reverses the Jacobian transform. See the comment at the
// top of the file.
func (bitCurve *BitCurve) affineFromJacobian(x, y, z *big.Int) (xOut, yOut *big.Int) {
if z.Cmp(big.NewInt(0)) == 0 {
panic("bitcurve: Can't convert to affine with Jacobian Z = 0")
}
// x = YZ^2 mod P
zinv := new(big.Int).ModInverse(z, bitCurve.P)
zinvsq := new(big.Int).Mul(zinv, zinv)
xOut = new(big.Int).Mul(x, zinvsq)
xOut.Mod(xOut, bitCurve.P)
// y = YZ^3 mod P
zinvsq.Mul(zinvsq, zinv)
yOut = new(big.Int).Mul(y, zinvsq)
yOut.Mod(yOut, bitCurve.P)
return xOut, yOut
}
// Add returns the sum of (x1,y1) and (x2,y2)
func (bitCurve *BitCurve) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) {
z := new(big.Int).SetInt64(1)
x, y, z := bitCurve.addJacobian(x1, y1, z, x2, y2, z)
return bitCurve.affineFromJacobian(x, y, z)
}
// addJacobian takes two points in Jacobian coordinates, (x1, y1, z1) and
// (x2, y2, z2) and returns their sum, also in Jacobian form.
func (bitCurve *BitCurve) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int, *big.Int, *big.Int) {
// See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl
z1z1 := new(big.Int).Mul(z1, z1)
z1z1.Mod(z1z1, bitCurve.P)
z2z2 := new(big.Int).Mul(z2, z2)
z2z2.Mod(z2z2, bitCurve.P)
u1 := new(big.Int).Mul(x1, z2z2)
u1.Mod(u1, bitCurve.P)
u2 := new(big.Int).Mul(x2, z1z1)
u2.Mod(u2, bitCurve.P)
h := new(big.Int).Sub(u2, u1)
if h.Sign() == -1 {
h.Add(h, bitCurve.P)
}
i := new(big.Int).Lsh(h, 1)
i.Mul(i, i)
j := new(big.Int).Mul(h, i)
s1 := new(big.Int).Mul(y1, z2)
s1.Mul(s1, z2z2)
s1.Mod(s1, bitCurve.P)
s2 := new(big.Int).Mul(y2, z1)
s2.Mul(s2, z1z1)
s2.Mod(s2, bitCurve.P)
r := new(big.Int).Sub(s2, s1)
if r.Sign() == -1 {
r.Add(r, bitCurve.P)
}
r.Lsh(r, 1)
v := new(big.Int).Mul(u1, i)
x3 := new(big.Int).Set(r)
x3.Mul(x3, x3)
x3.Sub(x3, j)
x3.Sub(x3, v)
x3.Sub(x3, v)
x3.Mod(x3, bitCurve.P)
y3 := new(big.Int).Set(r)
v.Sub(v, x3)
y3.Mul(y3, v)
s1.Mul(s1, j)
s1.Lsh(s1, 1)
y3.Sub(y3, s1)
y3.Mod(y3, bitCurve.P)
z3 := new(big.Int).Add(z1, z2)
z3.Mul(z3, z3)
z3.Sub(z3, z1z1)
if z3.Sign() == -1 {
z3.Add(z3, bitCurve.P)
}
z3.Sub(z3, z2z2)
if z3.Sign() == -1 {
z3.Add(z3, bitCurve.P)
}
z3.Mul(z3, h)
z3.Mod(z3, bitCurve.P)
return x3, y3, z3
}
// Double returns 2*(x,y)
func (bitCurve *BitCurve) Double(x1, y1 *big.Int) (*big.Int, *big.Int) {
z1 := new(big.Int).SetInt64(1)
return bitCurve.affineFromJacobian(bitCurve.doubleJacobian(x1, y1, z1))
}
// doubleJacobian takes a point in Jacobian coordinates, (x, y, z), and
// returns its double, also in Jacobian form.
func (bitCurve *BitCurve) doubleJacobian(x, y, z *big.Int) (*big.Int, *big.Int, *big.Int) {
// See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l
a := new(big.Int).Mul(x, x) //X1²
b := new(big.Int).Mul(y, y) //Y1²
c := new(big.Int).Mul(b, b) //B²
d := new(big.Int).Add(x, b) //X1+B
d.Mul(d, d) //(X1+B)²
d.Sub(d, a) //(X1+B)²-A
d.Sub(d, c) //(X1+B)²-A-C
d.Mul(d, big.NewInt(2)) //2*((X1+B)²-A-C)
e := new(big.Int).Mul(big.NewInt(3), a) //3*A
f := new(big.Int).Mul(e, e) //E²
x3 := new(big.Int).Mul(big.NewInt(2), d) //2*D
x3.Sub(f, x3) //F-2*D
x3.Mod(x3, bitCurve.P)
y3 := new(big.Int).Sub(d, x3) //D-X3
y3.Mul(e, y3) //E*(D-X3)
y3.Sub(y3, new(big.Int).Mul(big.NewInt(8), c)) //E*(D-X3)-8*C
y3.Mod(y3, bitCurve.P)
z3 := new(big.Int).Mul(y, z) //Y1*Z1
z3.Mul(big.NewInt(2), z3) //3*Y1*Z1
z3.Mod(z3, bitCurve.P)
return x3, y3, z3
}
//TODO: double check if it is okay
// ScalarMult returns k*(Bx,By) where k is a number in big-endian form.
func (bitCurve *BitCurve) ScalarMult(Bx, By *big.Int, k []byte) (*big.Int, *big.Int) {
// We have a slight problem in that the identity of the group (the
// point at infinity) cannot be represented in (x, y) form on a finite
// machine. Thus the standard add/double algorithm has to be tweaked
// slightly: our initial state is not the identity, but x, and we
// ignore the first true bit in |k|. If we don't find any true bits in
// |k|, then we return nil, nil, because we cannot return the identity
// element.
Bz := new(big.Int).SetInt64(1)
x := Bx
y := By
z := Bz
seenFirstTrue := false
for _, byte := range k {
for bitNum := 0; bitNum < 8; bitNum++ {
if seenFirstTrue {
x, y, z = bitCurve.doubleJacobian(x, y, z)
}
if byte&0x80 == 0x80 {
if !seenFirstTrue {
seenFirstTrue = true
} else {
x, y, z = bitCurve.addJacobian(Bx, By, Bz, x, y, z)
}
}
byte <<= 1
}
}
if !seenFirstTrue {
return nil, nil
}
return bitCurve.affineFromJacobian(x, y, z)
}
// ScalarBaseMult returns k*G, where G is the base point of the group and k is
// an integer in big-endian form.
func (bitCurve *BitCurve) ScalarBaseMult(k []byte) (*big.Int, *big.Int) {
return bitCurve.ScalarMult(bitCurve.Gx, bitCurve.Gy, k)
}
var mask = []byte{0xff, 0x1, 0x3, 0x7, 0xf, 0x1f, 0x3f, 0x7f}
//TODO: double check if it is okay
// GenerateKey returns a public/private key pair. The private key is generated
// using the given reader, which must return random data.
func (bitCurve *BitCurve) GenerateKey(rand io.Reader) (priv []byte, x, y *big.Int, err error) {
byteLen := (bitCurve.BitSize + 7) >> 3
priv = make([]byte, byteLen)
for x == nil {
_, err = io.ReadFull(rand, priv)
if err != nil {
return
}
// We have to mask off any excess bits in the case that the size of the
// underlying field is not a whole number of bytes.
priv[0] &= mask[bitCurve.BitSize%8]
// This is because, in tests, rand will return all zeros and we don't
// want to get the point at infinity and loop forever.
priv[1] ^= 0x42
x, y = bitCurve.ScalarBaseMult(priv)
}
return
}
// Marshal converts a point into the form specified in section 4.3.6 of ANSI
// X9.62.
func (bitCurve *BitCurve) Marshal(x, y *big.Int) []byte {
byteLen := (bitCurve.BitSize + 7) >> 3
ret := make([]byte, 1+2*byteLen)
ret[0] = 4 // uncompressed point
xBytes := x.Bytes()
copy(ret[1+byteLen-len(xBytes):], xBytes)
yBytes := y.Bytes()
copy(ret[1+2*byteLen-len(yBytes):], yBytes)
return ret
}
// Unmarshal converts a point, serialised by Marshal, into an x, y pair. On
// error, x = nil.
func (bitCurve *BitCurve) Unmarshal(data []byte) (x, y *big.Int) {
byteLen := (bitCurve.BitSize + 7) >> 3
if len(data) != 1+2*byteLen {
return
}
if data[0] != 4 { // uncompressed form
return
}
x = new(big.Int).SetBytes(data[1 : 1+byteLen])
y = new(big.Int).SetBytes(data[1+byteLen:])
return
}
//curve parameters taken from:
//http://www.secg.org/collateral/sec2_final.pdf
var initonce sync.Once
var secp160k1 *BitCurve
var secp192k1 *BitCurve
var secp224k1 *BitCurve
var secp256k1 *BitCurve
func initAll() {
initS160()
initS192()
initS224()
initS256()
}
func initS160() {
// See SEC 2 section 2.4.1
secp160k1 = new(BitCurve)
secp160k1.Name = "secp160k1"
secp160k1.P, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73", 16)
secp160k1.N, _ = new(big.Int).SetString("0100000000000000000001B8FA16DFAB9ACA16B6B3", 16)
secp160k1.B, _ = new(big.Int).SetString("0000000000000000000000000000000000000007", 16)
secp160k1.Gx, _ = new(big.Int).SetString("3B4C382CE37AA192A4019E763036F4F5DD4D7EBB", 16)
secp160k1.Gy, _ = new(big.Int).SetString("938CF935318FDCED6BC28286531733C3F03C4FEE", 16)
secp160k1.BitSize = 160
}
func initS192() {
// See SEC 2 section 2.5.1
secp192k1 = new(BitCurve)
secp192k1.Name = "secp192k1"
secp192k1.P, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37", 16)
secp192k1.N, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8D", 16)
secp192k1.B, _ = new(big.Int).SetString("000000000000000000000000000000000000000000000003", 16)
secp192k1.Gx, _ = new(big.Int).SetString("DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D", 16)
secp192k1.Gy, _ = new(big.Int).SetString("9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D", 16)
secp192k1.BitSize = 192
}
func initS224() {
// See SEC 2 section 2.6.1
secp224k1 = new(BitCurve)
secp224k1.Name = "secp224k1"
secp224k1.P, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFE56D", 16)
secp224k1.N, _ = new(big.Int).SetString("010000000000000000000000000001DCE8D2EC6184CAF0A971769FB1F7", 16)
secp224k1.B, _ = new(big.Int).SetString("00000000000000000000000000000000000000000000000000000005", 16)
secp224k1.Gx, _ = new(big.Int).SetString("A1455B334DF099DF30FC28A169A467E9E47075A90F7E650EB6B7A45C", 16)
secp224k1.Gy, _ = new(big.Int).SetString("7E089FED7FBA344282CAFBD6F7E319F7C0B0BD59E2CA4BDB556D61A5", 16)
secp224k1.BitSize = 224
}
func initS256() {
// See SEC 2 section 2.7.1
secp256k1 = new(BitCurve)
secp256k1.Name = "secp256k1"
secp256k1.P, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", 16)
secp256k1.N, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 16)
secp256k1.B, _ = new(big.Int).SetString("0000000000000000000000000000000000000000000000000000000000000007", 16)
secp256k1.Gx, _ = new(big.Int).SetString("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", 16)
secp256k1.Gy, _ = new(big.Int).SetString("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", 16)
secp256k1.BitSize = 256
}
// S160 returns a BitCurve which implements secp160k1 (see SEC 2 section 2.4.1)
func S160() *BitCurve {
initonce.Do(initAll)
return secp160k1
}
// S192 returns a BitCurve which implements secp192k1 (see SEC 2 section 2.5.1)
func S192() *BitCurve {
initonce.Do(initAll)
return secp192k1
}
// S224 returns a BitCurve which implements secp224k1 (see SEC 2 section 2.6.1)
func S224() *BitCurve {
initonce.Do(initAll)
return secp224k1
}
// S256 returns a BitCurve which implements bitcurves (see SEC 2 section 2.7.1)
func S256() *BitCurve {
initonce.Do(initAll)
return secp256k1
}

View File

@ -0,0 +1,134 @@
// Package brainpool implements Brainpool elliptic curves.
// Implementation of rcurves is from github.com/ebfe/brainpool
// Note that these curves are implemented with naive, non-constant time operations
// and are likely not suitable for enviroments where timing attacks are a concern.
package brainpool
import (
"crypto/elliptic"
"math/big"
"sync"
)
var (
once sync.Once
p256t1, p384t1, p512t1 *elliptic.CurveParams
p256r1, p384r1, p512r1 *rcurve
)
func initAll() {
initP256t1()
initP384t1()
initP512t1()
initP256r1()
initP384r1()
initP512r1()
}
func initP256t1() {
p256t1 = &elliptic.CurveParams{Name: "brainpoolP256t1"}
p256t1.P, _ = new(big.Int).SetString("A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377", 16)
p256t1.N, _ = new(big.Int).SetString("A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7", 16)
p256t1.B, _ = new(big.Int).SetString("662C61C430D84EA4FE66A7733D0B76B7BF93EBC4AF2F49256AE58101FEE92B04", 16)
p256t1.Gx, _ = new(big.Int).SetString("A3E8EB3CC1CFE7B7732213B23A656149AFA142C47AAFBC2B79A191562E1305F4", 16)
p256t1.Gy, _ = new(big.Int).SetString("2D996C823439C56D7F7B22E14644417E69BCB6DE39D027001DABE8F35B25C9BE", 16)
p256t1.BitSize = 256
}
func initP256r1() {
twisted := p256t1
params := &elliptic.CurveParams{
Name: "brainpoolP256r1",
P: twisted.P,
N: twisted.N,
BitSize: twisted.BitSize,
}
params.Gx, _ = new(big.Int).SetString("8BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262", 16)
params.Gy, _ = new(big.Int).SetString("547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997", 16)
z, _ := new(big.Int).SetString("3E2D4BD9597B58639AE7AA669CAB9837CF5CF20A2C852D10F655668DFC150EF0", 16)
p256r1 = newrcurve(twisted, params, z)
}
func initP384t1() {
p384t1 = &elliptic.CurveParams{Name: "brainpoolP384t1"}
p384t1.P, _ = new(big.Int).SetString("8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB71123ACD3A729901D1A71874700133107EC53", 16)
p384t1.N, _ = new(big.Int).SetString("8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B31F166E6CAC0425A7CF3AB6AF6B7FC3103B883202E9046565", 16)
p384t1.B, _ = new(big.Int).SetString("7F519EADA7BDA81BD826DBA647910F8C4B9346ED8CCDC64E4B1ABD11756DCE1D2074AA263B88805CED70355A33B471EE", 16)
p384t1.Gx, _ = new(big.Int).SetString("18DE98B02DB9A306F2AFCD7235F72A819B80AB12EBD653172476FECD462AABFFC4FF191B946A5F54D8D0AA2F418808CC", 16)
p384t1.Gy, _ = new(big.Int).SetString("25AB056962D30651A114AFD2755AD336747F93475B7A1FCA3B88F2B6A208CCFE469408584DC2B2912675BF5B9E582928", 16)
p384t1.BitSize = 384
}
func initP384r1() {
twisted := p384t1
params := &elliptic.CurveParams{
Name: "brainpoolP384r1",
P: twisted.P,
N: twisted.N,
BitSize: twisted.BitSize,
}
params.Gx, _ = new(big.Int).SetString("1D1C64F068CF45FFA2A63A81B7C13F6B8847A3E77EF14FE3DB7FCAFE0CBD10E8E826E03436D646AAEF87B2E247D4AF1E", 16)
params.Gy, _ = new(big.Int).SetString("8ABE1D7520F9C2A45CB1EB8E95CFD55262B70B29FEEC5864E19C054FF99129280E4646217791811142820341263C5315", 16)
z, _ := new(big.Int).SetString("41DFE8DD399331F7166A66076734A89CD0D2BCDB7D068E44E1F378F41ECBAE97D2D63DBC87BCCDDCCC5DA39E8589291C", 16)
p384r1 = newrcurve(twisted, params, z)
}
func initP512t1() {
p512t1 = &elliptic.CurveParams{Name: "brainpoolP512t1"}
p512t1.P, _ = new(big.Int).SetString("AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308717D4D9B009BC66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F3", 16)
p512t1.N, _ = new(big.Int).SetString("AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA70330870553E5C414CA92619418661197FAC10471DB1D381085DDADDB58796829CA90069", 16)
p512t1.B, _ = new(big.Int).SetString("7CBBBCF9441CFAB76E1890E46884EAE321F70C0BCB4981527897504BEC3E36A62BCDFA2304976540F6450085F2DAE145C22553B465763689180EA2571867423E", 16)
p512t1.Gx, _ = new(big.Int).SetString("640ECE5C12788717B9C1BA06CBC2A6FEBA85842458C56DDE9DB1758D39C0313D82BA51735CDB3EA499AA77A7D6943A64F7A3F25FE26F06B51BAA2696FA9035DA", 16)
p512t1.Gy, _ = new(big.Int).SetString("5B534BD595F5AF0FA2C892376C84ACE1BB4E3019B71634C01131159CAE03CEE9D9932184BEEF216BD71DF2DADF86A627306ECFF96DBB8BACE198B61E00F8B332", 16)
p512t1.BitSize = 512
}
func initP512r1() {
twisted := p512t1
params := &elliptic.CurveParams{
Name: "brainpoolP512r1",
P: twisted.P,
N: twisted.N,
BitSize: twisted.BitSize,
}
params.Gx, _ = new(big.Int).SetString("81AEE4BDD82ED9645A21322E9C4C6A9385ED9F70B5D916C1B43B62EEF4D0098EFF3B1F78E2D0D48D50D1687B93B97D5F7C6D5047406A5E688B352209BCB9F822", 16)
params.Gy, _ = new(big.Int).SetString("7DDE385D566332ECC0EABFA9CF7822FDF209F70024A57B1AA000C55B881F8111B2DCDE494A5F485E5BCA4BD88A2763AED1CA2B2FA8F0540678CD1E0F3AD80892", 16)
z, _ := new(big.Int).SetString("12EE58E6764838B69782136F0F2D3BA06E27695716054092E60A80BEDB212B64E585D90BCE13761F85C3F1D2A64E3BE8FEA2220F01EBA5EEB0F35DBD29D922AB", 16)
p512r1 = newrcurve(twisted, params, z)
}
// P256t1 returns a Curve which implements Brainpool P256t1 (see RFC 5639, section 3.4)
func P256t1() elliptic.Curve {
once.Do(initAll)
return p256t1
}
// P256r1 returns a Curve which implements Brainpool P256r1 (see RFC 5639, section 3.4)
func P256r1() elliptic.Curve {
once.Do(initAll)
return p256r1
}
// P384t1 returns a Curve which implements Brainpool P384t1 (see RFC 5639, section 3.6)
func P384t1() elliptic.Curve {
once.Do(initAll)
return p384t1
}
// P384r1 returns a Curve which implements Brainpool P384r1 (see RFC 5639, section 3.6)
func P384r1() elliptic.Curve {
once.Do(initAll)
return p384r1
}
// P512t1 returns a Curve which implements Brainpool P512t1 (see RFC 5639, section 3.7)
func P512t1() elliptic.Curve {
once.Do(initAll)
return p512t1
}
// P512r1 returns a Curve which implements Brainpool P512r1 (see RFC 5639, section 3.7)
func P512r1() elliptic.Curve {
once.Do(initAll)
return p512r1
}

View File

@ -0,0 +1,83 @@
package brainpool
import (
"crypto/elliptic"
"math/big"
)
var _ elliptic.Curve = (*rcurve)(nil)
type rcurve struct {
twisted elliptic.Curve
params *elliptic.CurveParams
z *big.Int
zinv *big.Int
z2 *big.Int
z3 *big.Int
zinv2 *big.Int
zinv3 *big.Int
}
var (
two = big.NewInt(2)
three = big.NewInt(3)
)
func newrcurve(twisted elliptic.Curve, params *elliptic.CurveParams, z *big.Int) *rcurve {
zinv := new(big.Int).ModInverse(z, params.P)
return &rcurve{
twisted: twisted,
params: params,
z: z,
zinv: zinv,
z2: new(big.Int).Exp(z, two, params.P),
z3: new(big.Int).Exp(z, three, params.P),
zinv2: new(big.Int).Exp(zinv, two, params.P),
zinv3: new(big.Int).Exp(zinv, three, params.P),
}
}
func (curve *rcurve) toTwisted(x, y *big.Int) (*big.Int, *big.Int) {
var tx, ty big.Int
tx.Mul(x, curve.z2)
tx.Mod(&tx, curve.params.P)
ty.Mul(y, curve.z3)
ty.Mod(&ty, curve.params.P)
return &tx, &ty
}
func (curve *rcurve) fromTwisted(tx, ty *big.Int) (*big.Int, *big.Int) {
var x, y big.Int
x.Mul(tx, curve.zinv2)
x.Mod(&x, curve.params.P)
y.Mul(ty, curve.zinv3)
y.Mod(&y, curve.params.P)
return &x, &y
}
func (curve *rcurve) Params() *elliptic.CurveParams {
return curve.params
}
func (curve *rcurve) IsOnCurve(x, y *big.Int) bool {
return curve.twisted.IsOnCurve(curve.toTwisted(x, y))
}
func (curve *rcurve) Add(x1, y1, x2, y2 *big.Int) (x, y *big.Int) {
tx1, ty1 := curve.toTwisted(x1, y1)
tx2, ty2 := curve.toTwisted(x2, y2)
return curve.fromTwisted(curve.twisted.Add(tx1, ty1, tx2, ty2))
}
func (curve *rcurve) Double(x1, y1 *big.Int) (x, y *big.Int) {
return curve.fromTwisted(curve.twisted.Double(curve.toTwisted(x1, y1)))
}
func (curve *rcurve) ScalarMult(x1, y1 *big.Int, scalar []byte) (x, y *big.Int) {
tx1, ty1 := curve.toTwisted(x1, y1)
return curve.fromTwisted(curve.twisted.ScalarMult(tx1, ty1, scalar))
}
func (curve *rcurve) ScalarBaseMult(scalar []byte) (x, y *big.Int) {
return curve.fromTwisted(curve.twisted.ScalarBaseMult(scalar))
}

162
vendor/github.com/ProtonMail/go-crypto/eax/eax.go generated vendored Normal file
View File

@ -0,0 +1,162 @@
// Copyright (C) 2019 ProtonTech AG
// Package eax provides an implementation of the EAX
// (encrypt-authenticate-translate) mode of operation, as described in
// Bellare, Rogaway, and Wagner "THE EAX MODE OF OPERATION: A TWO-PASS
// AUTHENTICATED-ENCRYPTION SCHEME OPTIMIZED FOR SIMPLICITY AND EFFICIENCY."
// In FSE'04, volume 3017 of LNCS, 2004
package eax
import (
"crypto/cipher"
"crypto/subtle"
"errors"
"github.com/ProtonMail/go-crypto/internal/byteutil"
)
const (
defaultTagSize = 16
defaultNonceSize = 16
)
type eax struct {
block cipher.Block // Only AES-{128, 192, 256} supported
tagSize int // At least 12 bytes recommended
nonceSize int
}
func (e *eax) NonceSize() int {
return e.nonceSize
}
func (e *eax) Overhead() int {
return e.tagSize
}
// NewEAX returns an EAX instance with AES-{KEYLENGTH} and default nonce and
// tag lengths. Supports {128, 192, 256}- bit key length.
func NewEAX(block cipher.Block) (cipher.AEAD, error) {
return NewEAXWithNonceAndTagSize(block, defaultNonceSize, defaultTagSize)
}
// NewEAXWithNonceAndTagSize returns an EAX instance with AES-{keyLength} and
// given nonce and tag lengths in bytes. Panics on zero nonceSize and
// exceedingly long tags.
//
// It is recommended to use at least 12 bytes as tag length (see, for instance,
// NIST SP 800-38D).
//
// Only to be used for compatibility with existing cryptosystems with
// non-standard parameters. For all other cases, prefer NewEAX.
func NewEAXWithNonceAndTagSize(
block cipher.Block, nonceSize, tagSize int) (cipher.AEAD, error) {
if nonceSize < 1 {
return nil, eaxError("Cannot initialize EAX with nonceSize = 0")
}
if tagSize > block.BlockSize() {
return nil, eaxError("Custom tag length exceeds blocksize")
}
return &eax{
block: block,
tagSize: tagSize,
nonceSize: nonceSize,
}, nil
}
func (e *eax) Seal(dst, nonce, plaintext, adata []byte) []byte {
if len(nonce) > e.nonceSize {
panic("crypto/eax: Nonce too long for this instance")
}
ret, out := byteutil.SliceForAppend(dst, len(plaintext) + e.tagSize)
omacNonce := e.omacT(0, nonce)
omacAdata := e.omacT(1, adata)
// Encrypt message using CTR mode and omacNonce as IV
ctr := cipher.NewCTR(e.block, omacNonce)
ciphertextData := out[:len(plaintext)]
ctr.XORKeyStream(ciphertextData, plaintext)
omacCiphertext := e.omacT(2, ciphertextData)
tag := out[len(plaintext):]
for i := 0; i < e.tagSize; i++ {
tag[i] = omacCiphertext[i] ^ omacNonce[i] ^ omacAdata[i]
}
return ret
}
func (e* eax) Open(dst, nonce, ciphertext, adata []byte) ([]byte, error) {
if len(nonce) > e.nonceSize {
panic("crypto/eax: Nonce too long for this instance")
}
if len(ciphertext) < e.tagSize {
return nil, eaxError("Ciphertext shorter than tag length")
}
sep := len(ciphertext) - e.tagSize
// Compute tag
omacNonce := e.omacT(0, nonce)
omacAdata := e.omacT(1, adata)
omacCiphertext := e.omacT(2, ciphertext[:sep])
tag := make([]byte, e.tagSize)
for i := 0; i < e.tagSize; i++ {
tag[i] = omacCiphertext[i] ^ omacNonce[i] ^ omacAdata[i]
}
// Compare tags
if subtle.ConstantTimeCompare(ciphertext[sep:], tag) != 1 {
return nil, eaxError("Tag authentication failed")
}
// Decrypt ciphertext
ret, out := byteutil.SliceForAppend(dst, len(ciphertext))
ctr := cipher.NewCTR(e.block, omacNonce)
ctr.XORKeyStream(out, ciphertext[:sep])
return ret[:sep], nil
}
// Tweakable OMAC - Calls OMAC_K([t]_n || plaintext)
func (e *eax) omacT(t byte, plaintext []byte) []byte {
blockSize := e.block.BlockSize()
byteT := make([]byte, blockSize)
byteT[blockSize-1] = t
concat := append(byteT, plaintext...)
return e.omac(concat)
}
func (e *eax) omac(plaintext []byte) []byte {
blockSize := e.block.BlockSize()
// L ← E_K(0^n); B ← 2L; P ← 4L
L := make([]byte, blockSize)
e.block.Encrypt(L, L)
B := byteutil.GfnDouble(L)
P := byteutil.GfnDouble(B)
// CBC with IV = 0
cbc := cipher.NewCBCEncrypter(e.block, make([]byte, blockSize))
padded := e.pad(plaintext, B, P)
cbcCiphertext := make([]byte, len(padded))
cbc.CryptBlocks(cbcCiphertext, padded)
return cbcCiphertext[len(cbcCiphertext)-blockSize:]
}
func (e *eax) pad(plaintext, B, P []byte) []byte {
// if |M| in {n, 2n, 3n, ...}
blockSize := e.block.BlockSize()
if len(plaintext) != 0 && len(plaintext)%blockSize == 0 {
return byteutil.RightXor(plaintext, B)
}
// else return (M || 1 || 0^(n1(|M| % n))) xor→ P
ending := make([]byte, blockSize-len(plaintext)%blockSize)
ending[0] = 0x80
padded := append(plaintext, ending...)
return byteutil.RightXor(padded, P)
}
func eaxError(err string) error {
return errors.New("crypto/eax: " + err)
}

View File

@ -0,0 +1,58 @@
package eax
// Test vectors from
// https://web.cs.ucdavis.edu/~rogaway/papers/eax.pdf
var testVectors = []struct {
msg, key, nonce, header, ciphertext string
}{
{"",
"233952DEE4D5ED5F9B9C6D6FF80FF478",
"62EC67F9C3A4A407FCB2A8C49031A8B3",
"6BFB914FD07EAE6B",
"E037830E8389F27B025A2D6527E79D01"},
{"F7FB",
"91945D3F4DCBEE0BF45EF52255F095A4",
"BECAF043B0A23D843194BA972C66DEBD",
"FA3BFD4806EB53FA",
"19DD5C4C9331049D0BDAB0277408F67967E5"},
{"1A47CB4933",
"01F74AD64077F2E704C0F60ADA3DD523",
"70C3DB4F0D26368400A10ED05D2BFF5E",
"234A3463C1264AC6",
"D851D5BAE03A59F238A23E39199DC9266626C40F80"},
{"481C9E39B1",
"D07CF6CBB7F313BDDE66B727AFD3C5E8",
"8408DFFF3C1A2B1292DC199E46B7D617",
"33CCE2EABFF5A79D",
"632A9D131AD4C168A4225D8E1FF755939974A7BEDE"},
{"40D0C07DA5E4",
"35B6D0580005BBC12B0587124557D2C2",
"FDB6B06676EEDC5C61D74276E1F8E816",
"AEB96EAEBE2970E9",
"071DFE16C675CB0677E536F73AFE6A14B74EE49844DD"},
{"4DE3B35C3FC039245BD1FB7D",
"BD8E6E11475E60B268784C38C62FEB22",
"6EAC5C93072D8E8513F750935E46DA1B",
"D4482D1CA78DCE0F",
"835BB4F15D743E350E728414ABB8644FD6CCB86947C5E10590210A4F"},
{"8B0A79306C9CE7ED99DAE4F87F8DD61636",
"7C77D6E813BED5AC98BAA417477A2E7D",
"1A8C98DCD73D38393B2BF1569DEEFC19",
"65D2017990D62528",
"02083E3979DA014812F59F11D52630DA30137327D10649B0AA6E1C181DB617D7F2"},
{"1BDA122BCE8A8DBAF1877D962B8592DD2D56",
"5FFF20CAFAB119CA2FC73549E20F5B0D",
"DDE59B97D722156D4D9AFF2BC7559826",
"54B9F04E6A09189A",
"2EC47B2C4954A489AFC7BA4897EDCDAE8CC33B60450599BD02C96382902AEF7F832A"},
{"6CF36720872B8513F6EAB1A8A44438D5EF11",
"A4A4782BCFFD3EC5E7EF6D8C34A56123",
"B781FCF2F75FA5A8DE97A9CA48E522EC",
"899A175897561D7E",
"0DE18FD0FDD91E7AF19F1D8EE8733938B1E8E7F6D2231618102FDB7FE55FF1991700"},
{"CA40D7446E545FFAED3BD12A740A659FFBBB3CEAB7",
"8395FCF1E95BEBD697BD010BC766AAC3",
"22E7ADD93CFC6393C57EC0B3C17D6B44",
"126735FCC320D25A",
"CB8920F87A6C75CFF39627B56E3ED197C552D295A7CFC46AFC253B4652B1AF3795B124AB6E"},
}

View File

@ -0,0 +1,131 @@
// These vectors include key length in {128, 192, 256}, tag size 128, and
// random nonce, header, and plaintext lengths.
// This file was automatically generated.
package eax
var randomVectors = []struct {
key, nonce, header, plaintext, ciphertext string
}{
{"DFDE093F36B0356E5A81F609786982E3",
"1D8AC604419001816905BA72B14CED7E",
"152A1517A998D7A24163FCDD146DE81AC347C8B97088F502093C1ABB8F6E33D9A219C34D7603A18B1F5ABE02E56661B7D7F67E81EC08C1302EF38D80A859486D450E94A4F26AD9E68EEBBC0C857A0FC5CF9E641D63D565A7E361BC8908F5A8DC8FD6",
"1C8EAAB71077FE18B39730A3156ADE29C5EE824C7EE86ED2A253B775603FB237116E654F6FEC588DD27F523A0E01246FE73FE348491F2A8E9ABC6CA58D663F71CDBCF4AD798BE46C42AE6EE8B599DB44A1A48D7BBBBA0F7D2750181E1C5E66967F7D57CBD30AFBDA5727",
"79E7E150934BBEBF7013F61C60462A14D8B15AF7A248AFB8A344EF021C1500E16666891D6E973D8BB56B71A371F12CA34660C4410C016982B20F547E3762A58B7BF4F20236CADCF559E2BE7D783B13723B2741FC7CDC8997D839E39A3DDD2BADB96743DD7049F1BDB0516A262869915B3F70498AFB7B191BF960"},
{"F10619EF02E5D94D7550EB84ED364A21",
"8DC0D4F2F745BBAE835CC5574B942D20",
"FE561358F2E8DF7E1024FF1AE9A8D36EBD01352214505CB99D644777A8A1F6027FA2BDBFC529A9B91136D5F2416CFC5F0F4EC3A1AFD32BDDA23CA504C5A5CB451785FABF4DFE4CD50D817491991A60615B30286361C100A95D1712F2A45F8E374461F4CA2B",
"D7B5A971FC219631D30EFC3664AE3127D9CF3097DAD9C24AC7905D15E8D9B25B026B31D68CAE00975CDB81EB1FD96FD5E1A12E2BB83FA25F1B1D91363457657FC03875C27F2946C5",
"2F336ED42D3CC38FC61660C4CD60BA4BD438B05F5965D8B7B399D2E7167F5D34F792D318F94DB15D67463AC449E13D568CC09BFCE32A35EE3EE96A041927680AE329811811E27F2D1E8E657707AF99BA96D13A478D695D59"},
{"429F514EFC64D98A698A9247274CFF45",
"976AA5EB072F912D126ACEBC954FEC38",
"A71D89DC5B6CEDBB7451A27C3C2CAE09126DB4C421",
"5632FE62AB1DC549D54D3BC3FC868ACCEDEFD9ECF5E9F8",
"848AE4306CA8C7F416F8707625B7F55881C0AB430353A5C967CDA2DA787F581A70E34DBEBB2385"},
{"398138F309085F47F8457CDF53895A63",
"F8A8A7F2D28E5FFF7BBC2F24353F7A36",
"5D633C21BA7764B8855CAB586F3746E236AD486039C83C6B56EFA9C651D38A41D6B20DAEE3418BFEA44B8BD6",
"A3BBAA91920AF5E10659818B1B3B300AC79BFC129C8329E75251F73A66D3AE0128EB91D5031E0A65C329DB7D1E9C0493E268",
"D078097267606E5FB07CFB7E2B4B718172A82C6A4CEE65D549A4DFB9838003BD2FBF64A7A66988AC1A632FD88F9E9FBB57C5A78AD2E086EACBA3DB68511D81C2970A"},
{"7A4151EBD3901B42CBA45DAFB2E931BA",
"0FC88ACEE74DD538040321C330974EB8",
"250464FB04733BAB934C59E6AD2D6AE8D662CBCFEFBE61E5A308D4211E58C4C25935B72C69107722E946BFCBF416796600542D76AEB73F2B25BF53BAF97BDEB36ED3A7A51C31E7F170EB897457E7C17571D1BA0A908954E9",
"88C41F3EBEC23FAB8A362D969CAC810FAD4F7CA6A7F7D0D44F060F92E37E1183768DD4A8C733F71C96058D362A39876D183B86C103DE",
"74A25B2182C51096D48A870D80F18E1CE15867778E34FCBA6BD7BFB3739FDCD42AD0F2D9F4EBA29085285C6048C15BCE5E5166F1F962D3337AA88E6062F05523029D0A7F0BF9"},
{"BFB147E1CD5459424F8C0271FC0E0DC5",
"EABCC126442BF373969EA3015988CC45",
"4C0880E1D71AA2C7",
"BE1B5EC78FBF73E7A6682B21BA7E0E5D2D1C7ABE",
"5660D7C1380E2F306895B1402CB2D6C37876504276B414D120F4CF92FDDDBB293A238EA0"},
{"595DD6F52D18BC2CA8EB4EDAA18D9FA3",
"0F84B5D36CF4BC3B863313AF3B4D2E97",
"30AE6CC5F99580F12A779D98BD379A60948020C0B6FBD5746B30BA3A15C6CD33DAF376C70A9F15B6C0EB410A93161F7958AE23",
"8EF3687A1642B070970B0B91462229D1D76ABC154D18211F7152AA9FF368",
"317C1DDB11417E5A9CC4DDE7FDFF6659A5AC4B31DE025212580A05CDAC6024D3E4AE7C2966E52B9129E9ECDBED86"},
{"44E6F2DC8FDC778AD007137D11410F50",
"270A237AD977F7187AA6C158A0BAB24F",
"509B0F0EB12E2AA5C5BA2DE553C07FAF4CE0C9E926531AA709A3D6224FCB783ACCF1559E10B1123EBB7D52E8AB54E6B5352A9ED0D04124BF0E9D9BACFD7E32B817B2E625F5EE94A64EDE9E470DE7FE6886C19B294F9F828209FE257A78",
"8B3D7815DF25618A5D0C55A601711881483878F113A12EC36CF64900549A3199555528559DC118F789788A55FAFD944E6E99A9CA3F72F238CD3F4D88223F7A745992B3FAED1848",
"1CC00D79F7AD82FDA71B58D286E5F34D0CC4CEF30704E771CC1E50746BDF83E182B078DB27149A42BAE619DF0F85B0B1090AD55D3B4471B0D6F6ECCD09C8F876B30081F0E7537A9624F8AAF29DA85E324122EFB4D68A56"},
{"BB7BC352A03044B4428D8DBB4B0701FDEC4649FD17B81452",
"8B4BBE26CCD9859DCD84884159D6B0A4",
"2212BEB0E78E0F044A86944CF33C8D5C80D9DBE1034BF3BCF73611835C7D3A52F5BD2D81B68FD681B68540A496EE5DA16FD8AC8824E60E1EC2042BE28FB0BFAD4E4B03596446BDD8C37D936D9B3D5295BE19F19CF5ACE1D33A46C952CE4DE5C12F92C1DD051E04AEED",
"9037234CC44FFF828FABED3A7084AF40FA7ABFF8E0C0EFB57A1CC361E18FC4FAC1AB54F3ABFE9FF77263ACE16C3A",
"A9391B805CCD956081E0B63D282BEA46E7025126F1C1631239C33E92AA6F92CD56E5A4C56F00FF9658E93D48AF4EF0EF81628E34AD4DB0CDAEDCD2A17EE7"},
{"99C0AD703196D2F60A74E6B378B838B31F82EA861F06FC4E",
"92745C018AA708ECFEB1667E9F3F1B01",
"828C69F376C0C0EC651C67749C69577D589EE39E51404D80EBF70C8660A8F5FD375473F4A7C611D59CB546A605D67446CE2AA844135FCD78BB5FBC90222A00D42920BB1D7EEDFB0C4672554F583EF23184F89063CDECBE482367B5F9AF3ACBC3AF61392BD94CBCD9B64677",
"A879214658FD0A5B0E09836639BF82E05EC7A5EF71D4701934BDA228435C68AC3D5CEB54997878B06A655EEACEFB1345C15867E7FE6C6423660C8B88DF128EBD6BCD85118DBAE16E9252FFB204324E5C8F38CA97759BDBF3CB0083",
"51FE87996F194A2585E438B023B345439EA60D1AEBED4650CDAF48A4D4EEC4FC77DC71CC4B09D3BEEF8B7B7AF716CE2B4EFFB3AC9E6323C18AC35E0AA6E2BBBC8889490EB6226C896B0D105EAB42BFE7053CCF00ED66BA94C1BA09A792AA873F0C3B26C5C5F9A936E57B25"},
{"7086816D00D648FB8304AA8C9E552E1B69A9955FB59B25D1",
"0F45CF7F0BF31CCEB85D9DA10F4D749F",
"93F27C60A417D9F0669E86ACC784FC8917B502DAF30A6338F11B30B94D74FEFE2F8BE1BBE2EAD10FAB7EED3C6F72B7C3ECEE1937C32ED4970A6404E139209C05",
"877F046601F3CBE4FB1491943FA29487E738F94B99AF206262A1D6FF856C9AA0B8D4D08A54370C98F8E88FA3DCC2B14C1F76D71B2A4C7963AEE8AF960464C5BEC8357AD00DC8",
"FE96906B895CE6A8E72BC72344E2C8BB3C63113D70EAFA26C299BAFE77A8A6568172EB447FB3E86648A0AF3512DEB1AAC0819F3EC553903BF28A9FB0F43411237A774BF9EE03E445D280FBB9CD12B9BAAB6EF5E52691"},
{"062F65A896D5BF1401BADFF70E91B458E1F9BD4888CB2E4D",
"5B11EA1D6008EBB41CF892FCA5B943D1",
"BAF4FF5C8242",
"A8870E091238355984EB2F7D61A865B9170F440BFF999A5993DD41A10F4440D21FF948DDA2BF663B2E03AC3324492DC5E40262ECC6A65C07672353BE23E7FB3A9D79FF6AA38D97960905A38DECC312CB6A59E5467ECF06C311CD43ADC0B543EDF34FE8BE611F176460D5627CA51F8F8D9FED71F55C",
"B10E127A632172CF8AA7539B140D2C9C2590E6F28C3CB892FC498FCE56A34F732FBFF32E79C7B9747D9094E8635A0C084D6F0247F9768FB5FF83493799A9BEC6C39572120C40E9292C8C947AE8573462A9108C36D9D7112E6995AE5867E6C8BB387D1C5D4BEF524F391B9FD9F0A3B4BFA079E915BCD920185CFD38D114C558928BD7D47877"},
{"38A8E45D6D705A11AF58AED5A1344896998EACF359F2E26A",
"FD82B5B31804FF47D44199B533D0CF84",
"DE454D4E62FE879F2050EE3E25853623D3E9AC52EEC1A1779A48CFAF5ECA0BFDE44749391866D1",
"B804",
"164BB965C05EBE0931A1A63293EDF9C38C27"},
{"34C33C97C6D7A0850DA94D78A58DC61EC717CD7574833068",
"343BE00DA9483F05C14F2E9EB8EA6AE8",
"78312A43EFDE3CAE34A65796FF059A3FE15304EEA5CF1D9306949FE5BF3349D4977D4EBE76C040FE894C5949E4E4D6681153DA87FB9AC5062063CA2EA183566343362370944CE0362D25FC195E124FD60E8682E665D13F2229DDA3E4B2CB1DCA",
"CC11BB284B1153578E4A5ED9D937B869DAF00F5B1960C23455CA9CC43F486A3BE0B66254F1041F04FDF459C8640465B6E1D2CF899A381451E8E7FCB50CF87823BE77E24B132BBEEDC72E53369B275E1D8F49ECE59F4F215230AC4FE133FC80E4F634EE80BA4682B62C86",
"E7F703DC31A95E3A4919FF957836CB76C063D81702AEA4703E1C2BF30831E58C4609D626EC6810E12EAA5B930F049FF9EFC22C3E3F1EBD4A1FB285CB02A1AC5AD46B425199FC0A85670A5C4E3DAA9636C8F64C199F42F18AAC8EA7457FD377F322DD7752D7D01B946C8F0A97E6113F0D50106F319AFD291AAACE"},
{"C6ECF7F053573E403E61B83052A343D93CBCC179D1E835BE",
"E280E13D7367042E3AA09A80111B6184",
"21486C9D7A9647",
"5F2639AFA6F17931853791CD8C92382BBB677FD72D0AB1A080D0E49BFAA21810E963E4FACD422E92F65CBFAD5884A60CD94740DF31AF02F95AA57DA0C4401B0ED906",
"5C51DB20755302070C45F52E50128A67C8B2E4ED0EACB7E29998CCE2E8C289DD5655913EC1A51CC3AABE5CDC2402B2BE7D6D4BF6945F266FBD70BA9F37109067157AE7530678B45F64475D4EBFCB5FFF46A5"},
{"5EC6CF7401BC57B18EF154E8C38ACCA8959E57D2F3975FF5",
"656B41CB3F9CF8C08BAD7EBFC80BD225",
"6B817C2906E2AF425861A7EF59BA5801F143EE2A139EE72697CDE168B4",
"2C0E1DDC9B1E5389BA63845B18B1F8A1DB062037151BCC56EF7C21C0BB4DAE366636BBA975685D7CC5A94AFBE89C769016388C56FB7B57CE750A12B718A8BDCF70E80E8659A8330EFC8F86640F21735E8C80E23FE43ABF23507CE3F964AE4EC99D",
"ED780CF911E6D1AA8C979B889B0B9DC1ABE261832980BDBFB576901D9EF5AB8048998E31A15BE54B3E5845A4D136AD24D0BDA1C3006168DF2F8AC06729CB0818867398150020131D8F04EDF1923758C9EABB5F735DE5EA1758D4BC0ACFCA98AFD202E9839B8720253693B874C65586C6F0"},
{"C92F678EB2208662F5BCF3403EC05F5961E957908A3E79421E1D25FC19054153",
"DA0F3A40983D92F2D4C01FED33C7A192",
"2B6E9D26DB406A0FAB47608657AA10EFC2B4AA5F459B29FF85AC9A40BFFE7AEB04F77E9A11FAAA116D7F6D4DA417671A9AB02C588E0EF59CB1BFB4B1CC931B63A3B3A159FCEC97A04D1E6F0C7E6A9CEF6B0ABB04758A69F1FE754DF4C2610E8C46B6CF413BDB31351D55BEDCB7B4A13A1C98E10984475E0F2F957853",
"F37326A80E08",
"83519E53E321D334F7C10B568183775C0E9AAE55F806"},
{"6847E0491BE57E72995D186D50094B0B3593957A5146798FCE68B287B2FB37B5",
"3EE1182AEBB19A02B128F28E1D5F7F99",
"D9F35ABB16D776CE",
"DB7566ED8EA95BDF837F23DB277BAFBC5E70D1105ADFD0D9EF15475051B1EF94709C67DCA9F8D5",
"2CDCED0C9EBD6E2A508822A685F7DCD1CDD99E7A5FCA786C234E7F7F1D27EC49751AD5DCFA30C5EDA87C43CAE3B919B6BBCFE34C8EDA59"},
{"82B019673642C08388D3E42075A4D5D587558C229E4AB8F660E37650C4C41A0A",
"336F5D681E0410FAE7B607246092C6DC",
"D430CBD8FE435B64214E9E9CDC5DE99D31CFCFB8C10AA0587A49DF276611",
"998404153AD77003E1737EDE93ED79859EE6DCCA93CB40C4363AA817ABF2DBBD46E42A14A7183B6CC01E12A577888141363D0AE011EB6E8D28C0B235",
"9BEF69EEB60BD3D6065707B7557F25292A8872857CFBD24F2F3C088E4450995333088DA50FD9121221C504DF1D0CD5EFE6A12666C5D5BB12282CF4C19906E9CFAB97E9BDF7F49DC17CFC384B"},
{"747B2E269B1859F0622C15C8BAD6A725028B1F94B8DB7326948D1E6ED663A8BC",
"AB91F7245DDCE3F1C747872D47BE0A8A",
"3B03F786EF1DDD76E1D42646DA4CD2A5165DC5383CE86D1A0B5F13F910DC278A4E451EE0192CBA178E13B3BA27FDC7840DF73D2E104B",
"6B803F4701114F3E5FE21718845F8416F70F626303F545BE197189E0A2BA396F37CE06D389EB2658BC7D56D67868708F6D0D32",
"1570DDB0BCE75AA25D1957A287A2C36B1A5F2270186DA81BA6112B7F43B0F3D1D0ED072591DCF1F1C99BBB25621FC39B896FF9BD9413A2845363A9DCD310C32CF98E57"},
{"02E59853FB29AEDA0FE1C5F19180AD99A12FF2F144670BB2B8BADF09AD812E0A",
"C691294EF67CD04D1B9242AF83DD1421",
"879334DAE3",
"1E17F46A98FEF5CBB40759D95354",
"FED8C3FF27DDF6313AED444A2985B36CBA268AAD6AAC563C0BA28F6DB5DB"},
{"F6C1FB9B4188F2288FF03BD716023198C3582CF2A037FC2F29760916C2B7FCDB",
"4228DA0678CA3534588859E77DFF014C",
"D8153CAF35539A61DD8D05B3C9B44F01E564FB9348BCD09A1C23B84195171308861058F0A3CD2A55B912A3AAEE06FF4D356C77275828F2157C2FC7C115DA39E443210CCC56BEDB0CC99BBFB227ABD5CC454F4E7F547C7378A659EEB6A7E809101A84F866503CB18D4484E1FA09B3EC7FC75EB2E35270800AA7",
"23B660A779AD285704B12EC1C580387A47BEC7B00D452C6570",
"5AA642BBABA8E49849002A2FAF31DB8FC7773EFDD656E469CEC19B3206D4174C9A263D0A05484261F6"},
{"8FF6086F1FADB9A3FBE245EAC52640C43B39D43F89526BB5A6EBA47710931446",
"943188480C99437495958B0AE4831AA9",
"AD5CD0BDA426F6EBA23C8EB23DC73FF9FEC173355EDBD6C9344C4C4383F211888F7CE6B29899A6801DF6B38651A7C77150941A",
"80CD5EA8D7F81DDF5070B934937912E8F541A5301877528EB41AB60C020968D459960ED8FB73083329841A",
"ABAE8EB7F36FCA2362551E72DAC890BA1BB6794797E0FC3B67426EC9372726ED4725D379EA0AC9147E48DCD0005C502863C2C5358A38817C8264B5"},
{"A083B54E6B1FE01B65D42FCD248F97BB477A41462BBFE6FD591006C022C8FD84",
"B0490F5BD68A52459556B3749ACDF40E",
"8892E047DA5CFBBDF7F3CFCBD1BD21C6D4C80774B1826999234394BD3E513CC7C222BB40E1E3140A152F19B3802F0D036C24A590512AD0E8",
"D7B15752789DC94ED0F36778A5C7BBB207BEC32BAC66E702B39966F06E381E090C6757653C3D26A81EC6AD6C364D66867A334C91BB0B8A8A4B6EACDF0783D09010AEBA2DD2062308FE99CC1F",
"C071280A732ADC93DF272BF1E613B2BB7D46FC6665EF2DC1671F3E211D6BDE1D6ADDD28DF3AA2E47053FC8BB8AE9271EC8BC8B2CFFA320D225B451685B6D23ACEFDD241FE284F8ADC8DB07F456985B14330BBB66E0FB212213E05B3E"},
}

View File

@ -0,0 +1,92 @@
// Copyright (C) 2019 ProtonTech AG
// This file contains necessary tools for the aex and ocb packages.
//
// These functions SHOULD NOT be used elsewhere, since they are optimized for
// specific input nature in the EAX and OCB modes of operation.
package byteutil
// GfnDouble computes 2 * input in the field of 2^n elements.
// The irreducible polynomial in the finite field for n=128 is
// x^128 + x^7 + x^2 + x + 1 (equals 0x87)
// Constant-time execution in order to avoid side-channel attacks
func GfnDouble(input []byte) []byte {
if len(input) != 16 {
panic("Doubling in GFn only implemented for n = 128")
}
// If the first bit is zero, return 2L = L << 1
// Else return (L << 1) xor 0^120 10000111
shifted := ShiftBytesLeft(input)
shifted[15] ^= ((input[0] >> 7) * 0x87)
return shifted
}
// ShiftBytesLeft outputs the byte array corresponding to x << 1 in binary.
func ShiftBytesLeft(x []byte) []byte {
l := len(x)
dst := make([]byte, l)
for i := 0; i < l-1; i++ {
dst[i] = (x[i] << 1) | (x[i+1] >> 7)
}
dst[l-1] = x[l-1] << 1
return dst
}
// ShiftNBytesLeft puts in dst the byte array corresponding to x << n in binary.
func ShiftNBytesLeft(dst, x []byte, n int) {
// Erase first n / 8 bytes
copy(dst, x[n/8:])
// Shift the remaining n % 8 bits
bits := uint(n % 8)
l := len(dst)
for i := 0; i < l-1; i++ {
dst[i] = (dst[i] << bits) | (dst[i+1] >> uint(8 - bits))
}
dst[l-1] = dst[l-1] << bits
// Append trailing zeroes
dst = append(dst, make([]byte, n/8)...)
}
// XorBytesMut assumes equal input length, replaces X with X XOR Y
func XorBytesMut(X, Y []byte) {
for i := 0; i < len(X); i++ {
X[i] ^= Y[i]
}
}
// XorBytes assumes equal input length, puts X XOR Y into Z
func XorBytes(Z, X, Y []byte) {
for i := 0; i < len(X); i++ {
Z[i] = X[i] ^ Y[i]
}
}
// RightXor XORs smaller input (assumed Y) at the right of the larger input (assumed X)
func RightXor(X, Y []byte) []byte {
offset := len(X) - len(Y)
xored := make([]byte, len(X));
copy(xored, X)
for i := 0; i < len(Y); i++ {
xored[offset + i] ^= Y[i]
}
return xored
}
// SliceForAppend takes a slice and a requested number of bytes. It returns a
// slice with the contents of the given slice followed by that many bytes and a
// second slice that aliases into it and contains only the extra bytes. If the
// original slice has sufficient capacity then no allocation is performed.
func SliceForAppend(in []byte, n int) (head, tail []byte) {
if total := len(in) + n; cap(in) >= total {
head = in[:total]
} else {
head = make([]byte, total)
copy(head, in)
}
tail = head[len(in):]
return
}

317
vendor/github.com/ProtonMail/go-crypto/ocb/ocb.go generated vendored Normal file
View File

@ -0,0 +1,317 @@
// Copyright (C) 2019 ProtonTech AG
// Package ocb provides an implementation of the OCB (offset codebook) mode of
// operation, as described in RFC-7253 of the IRTF and in Rogaway, Bellare,
// Black and Krovetz - OCB: A BLOCK-CIPHER MODE OF OPERATION FOR EFFICIENT
// AUTHENTICATED ENCRYPTION (2003).
// Security considerations (from RFC-7253): A private key MUST NOT be used to
// encrypt more than 2^48 blocks. Tag length should be at least 12 bytes (a
// brute-force forging adversary succeeds after 2^{tag length} attempts). A
// single key SHOULD NOT be used to decrypt ciphertext with different tag
// lengths. Nonces need not be secret, but MUST NOT be reused.
// This package only supports underlying block ciphers with 128-bit blocks,
// such as AES-{128, 192, 256}, but may be extended to other sizes.
package ocb
import (
"bytes"
"crypto/cipher"
"crypto/subtle"
"errors"
"github.com/ProtonMail/go-crypto/internal/byteutil"
"math/bits"
)
type ocb struct {
block cipher.Block
tagSize int
nonceSize int
mask mask
// Optimized en/decrypt: For each nonce N used to en/decrypt, the 'Ktop'
// internal variable can be reused for en/decrypting with nonces sharing
// all but the last 6 bits with N. The prefix of the first nonce used to
// compute the new Ktop, and the Ktop value itself, are stored in
// reusableKtop. If using incremental nonces, this saves one block cipher
// call every 63 out of 64 OCB encryptions, and stores one nonce and one
// output of the block cipher in memory only.
reusableKtop reusableKtop
}
type mask struct {
// L_*, L_$, (L_i)_{i ∈ N}
lAst []byte
lDol []byte
L [][]byte
}
type reusableKtop struct {
noncePrefix []byte
Ktop []byte
}
const (
defaultTagSize = 16
defaultNonceSize = 15
)
const (
enc = iota
dec
)
func (o *ocb) NonceSize() int {
return o.nonceSize
}
func (o *ocb) Overhead() int {
return o.tagSize
}
// NewOCB returns an OCB instance with the given block cipher and default
// tag and nonce sizes.
func NewOCB(block cipher.Block) (cipher.AEAD, error) {
return NewOCBWithNonceAndTagSize(block, defaultNonceSize, defaultTagSize)
}
// NewOCBWithNonceAndTagSize returns an OCB instance with the given block
// cipher, nonce length, and tag length. Panics on zero nonceSize and
// exceedingly long tag size.
//
// It is recommended to use at least 12 bytes as tag length.
func NewOCBWithNonceAndTagSize(
block cipher.Block, nonceSize, tagSize int) (cipher.AEAD, error) {
if block.BlockSize() != 16 {
return nil, ocbError("Block cipher must have 128-bit blocks")
}
if nonceSize < 1 {
return nil, ocbError("Incorrect nonce length")
}
if nonceSize >= block.BlockSize() {
return nil, ocbError("Nonce length exceeds blocksize - 1")
}
if tagSize > block.BlockSize() {
return nil, ocbError("Custom tag length exceeds blocksize")
}
return &ocb{
block: block,
tagSize: tagSize,
nonceSize: nonceSize,
mask: initializeMaskTable(block),
reusableKtop: reusableKtop{
noncePrefix: nil,
Ktop: nil,
},
}, nil
}
func (o *ocb) Seal(dst, nonce, plaintext, adata []byte) []byte {
if len(nonce) > o.nonceSize {
panic("crypto/ocb: Incorrect nonce length given to OCB")
}
ret, out := byteutil.SliceForAppend(dst, len(plaintext)+o.tagSize)
o.crypt(enc, out, nonce, adata, plaintext)
return ret
}
func (o *ocb) Open(dst, nonce, ciphertext, adata []byte) ([]byte, error) {
if len(nonce) > o.nonceSize {
panic("Nonce too long for this instance")
}
if len(ciphertext) < o.tagSize {
return nil, ocbError("Ciphertext shorter than tag length")
}
sep := len(ciphertext) - o.tagSize
ret, out := byteutil.SliceForAppend(dst, len(ciphertext))
ciphertextData := ciphertext[:sep]
tag := ciphertext[sep:]
o.crypt(dec, out, nonce, adata, ciphertextData)
if subtle.ConstantTimeCompare(ret[sep:], tag) == 1 {
ret = ret[:sep]
return ret, nil
}
for i := range out {
out[i] = 0
}
return nil, ocbError("Tag authentication failed")
}
// On instruction enc (resp. dec), crypt is the encrypt (resp. decrypt)
// function. It returns the resulting plain/ciphertext with the tag appended.
func (o *ocb) crypt(instruction int, Y, nonce, adata, X []byte) []byte {
//
// Consider X as a sequence of 128-bit blocks
//
// Note: For encryption (resp. decryption), X is the plaintext (resp., the
// ciphertext without the tag).
blockSize := o.block.BlockSize()
//
// Nonce-dependent and per-encryption variables
//
// Zero out the last 6 bits of the nonce into truncatedNonce to see if Ktop
// is already computed.
truncatedNonce := make([]byte, len(nonce))
copy(truncatedNonce, nonce)
truncatedNonce[len(truncatedNonce)-1] &= 192
Ktop := make([]byte, blockSize)
if bytes.Equal(truncatedNonce, o.reusableKtop.noncePrefix) {
Ktop = o.reusableKtop.Ktop
} else {
// Nonce = num2str(TAGLEN mod 128, 7) || zeros(120 - bitlen(N)) || 1 || N
paddedNonce := append(make([]byte, blockSize-1-len(nonce)), 1)
paddedNonce = append(paddedNonce, truncatedNonce...)
paddedNonce[0] |= byte(((8 * o.tagSize) % (8 * blockSize)) << 1)
// Last 6 bits of paddedNonce are already zero. Encrypt into Ktop
paddedNonce[blockSize-1] &= 192
Ktop = paddedNonce
o.block.Encrypt(Ktop, Ktop)
o.reusableKtop.noncePrefix = truncatedNonce
o.reusableKtop.Ktop = Ktop
}
// Stretch = Ktop || ((lower half of Ktop) XOR (lower half of Ktop << 8))
xorHalves := make([]byte, blockSize/2)
byteutil.XorBytes(xorHalves, Ktop[:blockSize/2], Ktop[1:1+blockSize/2])
stretch := append(Ktop, xorHalves...)
bottom := int(nonce[len(nonce)-1] & 63)
offset := make([]byte, len(stretch))
byteutil.ShiftNBytesLeft(offset, stretch, bottom)
offset = offset[:blockSize]
//
// Process any whole blocks
//
// Note: For encryption Y is ciphertext || tag, for decryption Y is
// plaintext || tag.
checksum := make([]byte, blockSize)
m := len(X) / blockSize
for i := 0; i < m; i++ {
index := bits.TrailingZeros(uint(i + 1))
if len(o.mask.L)-1 < index {
o.mask.extendTable(index)
}
byteutil.XorBytesMut(offset, o.mask.L[bits.TrailingZeros(uint(i+1))])
blockX := X[i*blockSize : (i+1)*blockSize]
blockY := Y[i*blockSize : (i+1)*blockSize]
byteutil.XorBytes(blockY, blockX, offset)
switch instruction {
case enc:
o.block.Encrypt(blockY, blockY)
byteutil.XorBytesMut(blockY, offset)
byteutil.XorBytesMut(checksum, blockX)
case dec:
o.block.Decrypt(blockY, blockY)
byteutil.XorBytesMut(blockY, offset)
byteutil.XorBytesMut(checksum, blockY)
}
}
//
// Process any final partial block and compute raw tag
//
tag := make([]byte, blockSize)
if len(X)%blockSize != 0 {
byteutil.XorBytesMut(offset, o.mask.lAst)
pad := make([]byte, blockSize)
o.block.Encrypt(pad, offset)
chunkX := X[blockSize*m:]
chunkY := Y[blockSize*m : len(X)]
byteutil.XorBytes(chunkY, chunkX, pad[:len(chunkX)])
// P_* || bit(1) || zeroes(127) - len(P_*)
switch instruction {
case enc:
paddedY := append(chunkX, byte(128))
paddedY = append(paddedY, make([]byte, blockSize-len(chunkX)-1)...)
byteutil.XorBytesMut(checksum, paddedY)
case dec:
paddedX := append(chunkY, byte(128))
paddedX = append(paddedX, make([]byte, blockSize-len(chunkY)-1)...)
byteutil.XorBytesMut(checksum, paddedX)
}
byteutil.XorBytes(tag, checksum, offset)
byteutil.XorBytesMut(tag, o.mask.lDol)
o.block.Encrypt(tag, tag)
byteutil.XorBytesMut(tag, o.hash(adata))
copy(Y[blockSize*m+len(chunkY):], tag[:o.tagSize])
} else {
byteutil.XorBytes(tag, checksum, offset)
byteutil.XorBytesMut(tag, o.mask.lDol)
o.block.Encrypt(tag, tag)
byteutil.XorBytesMut(tag, o.hash(adata))
copy(Y[blockSize*m:], tag[:o.tagSize])
}
return Y
}
// This hash function is used to compute the tag. Per design, on empty input it
// returns a slice of zeros, of the same length as the underlying block cipher
// block size.
func (o *ocb) hash(adata []byte) []byte {
//
// Consider A as a sequence of 128-bit blocks
//
A := make([]byte, len(adata))
copy(A, adata)
blockSize := o.block.BlockSize()
//
// Process any whole blocks
//
sum := make([]byte, blockSize)
offset := make([]byte, blockSize)
m := len(A) / blockSize
for i := 0; i < m; i++ {
chunk := A[blockSize*i : blockSize*(i+1)]
index := bits.TrailingZeros(uint(i + 1))
// If the mask table is too short
if len(o.mask.L)-1 < index {
o.mask.extendTable(index)
}
byteutil.XorBytesMut(offset, o.mask.L[index])
byteutil.XorBytesMut(chunk, offset)
o.block.Encrypt(chunk, chunk)
byteutil.XorBytesMut(sum, chunk)
}
//
// Process any final partial block; compute final hash value
//
if len(A)%blockSize != 0 {
byteutil.XorBytesMut(offset, o.mask.lAst)
// Pad block with 1 || 0 ^ 127 - bitlength(a)
ending := make([]byte, blockSize-len(A)%blockSize)
ending[0] = 0x80
encrypted := append(A[blockSize*m:], ending...)
byteutil.XorBytesMut(encrypted, offset)
o.block.Encrypt(encrypted, encrypted)
byteutil.XorBytesMut(sum, encrypted)
}
return sum
}
func initializeMaskTable(block cipher.Block) mask {
//
// Key-dependent variables
//
lAst := make([]byte, block.BlockSize())
block.Encrypt(lAst, lAst)
lDol := byteutil.GfnDouble(lAst)
L := make([][]byte, 1)
L[0] = byteutil.GfnDouble(lDol)
return mask{
lAst: lAst,
lDol: lDol,
L: L,
}
}
// Extends the L array of mask m up to L[limit], with L[i] = GfnDouble(L[i-1])
func (m *mask) extendTable(limit int) {
for i := len(m.L); i <= limit; i++ {
m.L = append(m.L, byteutil.GfnDouble(m.L[i-1]))
}
}
func ocbError(err string) error {
return errors.New("crypto/ocb: " + err)
}

View File

@ -0,0 +1,136 @@
// In the test vectors provided by RFC 7253, the "bottom"
// internal variable, which defines "offset" for the first time, does not
// exceed 15. However, it can attain values up to 63.
// These vectors include key length in {128, 192, 256}, tag size 128, and
// random nonce, header, and plaintext lengths.
// This file was automatically generated.
package ocb
var randomVectors = []struct {
key, nonce, header, plaintext, ciphertext string
}{
{"9438C5D599308EAF13F800D2D31EA7F0",
"C38EE4801BEBFFA1CD8635BE",
"0E507B7DADD8A98CDFE272D3CB6B3E8332B56AE583FB049C0874D4200BED16BD1A044182434E9DA0E841F182DFD5B3016B34641CED0784F1745F63AB3D0DA22D3351C9EF9A658B8081E24498EBF61FCE40DA6D8E184536",
"962D227786FB8913A8BAD5DC3250",
"EEDEF5FFA5986D1E3BF86DDD33EF9ADC79DCA06E215FA772CCBA814F63AD"},
{"BA7DE631C7D6712167C6724F5B9A2B1D",
"35263EBDA05765DC0E71F1F5",
"0103257B4224507C0242FEFE821EA7FA42E0A82863E5F8B68F7D881B4B44FA428A2B6B21D2F591260802D8AB6D83",
"9D6D1FC93AE8A64E7889B7B2E3521EFA9B920A8DDB692E6F833DDC4A38AFA535E5E2A3ED82CB7E26404AB86C54D01C4668F28398C2DF33D5D561CBA1C8DCFA7A912F5048E545B59483C0E3221F54B14DAA2E4EB657B3BEF9554F34CAD69B2724AE962D3D8A",
"E93852D1985C5E775655E937FA79CE5BF28A585F2AF53A5018853B9634BE3C84499AC0081918FDCE0624494D60E25F76ACD6853AC7576E3C350F332249BFCABD4E73CEABC36BE4EDDA40914E598AE74174A0D7442149B26990899491BDDFE8FC54D6C18E83AE9E9A6FFBF5D376565633862EEAD88D"},
{"2E74B25289F6FD3E578C24866E9C72A5",
"FD912F15025AF8414642BA1D1D",
"FB5FB8C26F365EEDAB5FE260C6E3CCD27806729C8335F146063A7F9EA93290E56CF84576EB446350D22AD730547C267B1F0BBB97EB34E1E2C41A",
"6C092EBF78F76EE8C1C6E592277D9545BA16EDB67BC7D8480B9827702DC2F8A129E2B08A2CE710CA7E1DA45CE162BB6CD4B512E632116E2211D3C90871EFB06B8D4B902681C7FB",
"6AC0A77F26531BF4F354A1737F99E49BE32ECD909A7A71AD69352906F54B08A9CE9B8CA5D724CBFFC5673437F23F630697F3B84117A1431D6FA8CC13A974FB4AD360300522E09511B99E71065D5AC4BBCB1D791E864EF4"},
{"E7EC507C802528F790AFF5303A017B17",
"4B97A7A568940A9E3CE7A99E93031E",
"28349BDC5A09390C480F9B8AA3EDEA3DDB8B9D64BCA322C570B8225DF0E31190DAB25A4014BA39519E02ABFB12B89AA28BBFD29E486E7FB28734258C817B63CED9912DBAFEBB93E2798AB2890DE3B0ACFCFF906AB15563EF7823CE83D27CDB251195E22BD1337BCBDE65E7C2C427321C463C2777BFE5AEAA",
"9455B3EA706B74",
"7F33BA3EA848D48A96B9530E26888F43EBD4463C9399B6"},
{"6C928AA3224736F28EE7378DE0090191",
"8936138E2E4C6A13280017A1622D",
"6202717F2631565BDCDC57C6584543E72A7C8BD444D0D108ED35069819633C",
"DA0691439E5F035F3E455269D14FE5C201C8C9B0A3FE2D3F86BCC59387C868FE65733D388360B31E3CE28B4BF6A8BE636706B536D5720DB66B47CF1C7A5AFD6F61E0EF90F1726D6B0E169F9A768B2B7AE4EE00A17F630AC905FCAAA1B707FFF25B3A1AAE83B504837C64A5639B2A34002B300EC035C9B43654DA55",
"B8804D182AB0F0EEB464FA7BD1329AD6154F982013F3765FEDFE09E26DAC078C9C1439BFC1159D6C02A25E3FF83EF852570117B315852AD5EE20E0FA3AA0A626B0E43BC0CEA38B44579DD36803455FB46989B90E6D229F513FD727AF8372517E9488384C515D6067704119C931299A0982EDDFB9C2E86A90C450C077EB222511EC9CCABC9FCFDB19F70088"},
{"ECEA315CA4B3F425B0C9957A17805EA4",
"664CDAE18403F4F9BA13015A44FC",
"642AFB090D6C6DB46783F08B01A3EF2A8FEB5736B531EAC226E7888FCC8505F396818F83105065FACB3267485B9E5E4A0261F621041C08FCCB2A809A49AB5252A91D0971BCC620B9D614BD77E57A0EED2FA5",
"6852C31F8083E20E364CEA21BB7854D67CEE812FE1C9ED2425C0932A90D3780728D1BB",
"2ECEF962A9695A463ADABB275BDA9FF8B2BA57AEC2F52EFFB700CD9271A74D2A011C24AEA946051BD6291776429B7E681BA33E"},
{"4EE616C4A58AAA380878F71A373461F6",
"91B8C9C176D9C385E9C47E52",
"CDA440B7F9762C572A718AC754EDEECC119E5EE0CCB9FEA4FFB22EEE75087C032EBF3DA9CDD8A28CC010B99ED45143B41A4BA50EA2A005473F89639237838867A57F23B0F0ED3BF22490E4501DAC9C658A9B9F",
"D6E645FA9AE410D15B8123FD757FA356A8DBE9258DDB5BE88832E615910993F497EC",
"B70ED7BF959FB2AAED4F36174A2A99BFB16992C8CDF369C782C4DB9C73DE78C5DB8E0615F647243B97ACDB24503BC9CADC48"},
{"DCD475773136C830D5E3D0C5FE05B7FF",
"BB8E1FBB483BE7616A922C4A",
"36FEF2E1CB29E76A6EA663FC3AF66ECD7404F466382F7B040AABED62293302B56E8783EF7EBC21B4A16C3E78A7483A0A403F253A2CDC5BBF79DC3DAE6C73F39A961D8FBBE8D41B",
"441E886EA38322B2437ECA7DEB5282518865A66780A454E510878E61BFEC3106A3CD93D2A02052E6F9E1832F9791053E3B76BF4C07EFDD6D4106E3027FABB752E60C1AA425416A87D53938163817A1051EBA1D1DEEB4B9B25C7E97368B52E5911A31810B0EC5AF547559B6142D9F4C4A6EF24A4CF75271BF9D48F62B",
"1BE4DD2F4E25A6512C2CC71D24BBB07368589A94C2714962CD0ACE5605688F06342587521E75F0ACAFFD86212FB5C34327D238DB36CF2B787794B9A4412E7CD1410EA5DDD2450C265F29CF96013CD213FD2880657694D718558964BC189B4A84AFCF47EB012935483052399DBA5B088B0A0477F20DFE0E85DCB735E21F22A439FB837DD365A93116D063E607"},
{"3FBA2B3D30177FFE15C1C59ED2148BB2C091F5615FBA7C07",
"FACF804A4BEBF998505FF9DE",
"8213B9263B2971A5BDA18DBD02208EE1",
"15B323926993B326EA19F892D704439FC478828322AF72118748284A1FD8A6D814E641F70512FD706980337379F31DC63355974738D7FEA87AD2858C0C2EBBFBE74371C21450072373C7B651B334D7C4D43260B9D7CCD3AF9EDB",
"6D35DC1469B26E6AAB26272A41B46916397C24C485B61162E640A062D9275BC33DDCFD3D9E1A53B6C8F51AC89B66A41D59B3574197A40D9B6DCF8A4E2A001409C8112F16B9C389E0096179DB914E05D6D11ED0005AD17E1CE105A2F0BAB8F6B1540DEB968B7A5428FF44"},
{"53B52B8D4D748BCDF1DDE68857832FA46227FA6E2F32EFA1",
"0B0EF53D4606B28D1398355F",
"F23882436349094AF98BCACA8218E81581A043B19009E28EFBF2DE37883E04864148CC01D240552CA8844EC1456F42034653067DA67E80F87105FD06E14FF771246C9612867BE4D215F6D761",
"F15030679BD4088D42CAC9BF2E9606EAD4798782FA3ED8C57EBE7F84A53236F51B25967C6489D0CD20C9EEA752F9BC",
"67B96E2D67C3729C96DAEAEDF821D61C17E648643A2134C5621FEC621186915AD80864BFD1EB5B238BF526A679385E012A457F583AFA78134242E9D9C1B4E4"},
{"0272DD80F23399F49BFC320381A5CD8225867245A49A7D41",
"5C83F4896D0738E1366B1836",
"69B0337289B19F73A12BAEEA857CCAF396C11113715D9500CCCF48BA08CFF12BC8B4BADB3084E63B85719DB5058FA7C2C11DEB096D7943CFA7CAF5",
"C01AD10FC8B562CD17C7BC2FAB3E26CBDFF8D7F4DEA816794BBCC12336991712972F52816AABAB244EB43B0137E2BAC1DD413CE79531E78BEF782E6B439612BB3AEF154DE3502784F287958EBC159419F9EBA27916A28D6307324129F506B1DE80C1755A929F87",
"FEFE52DD7159C8DD6E8EC2D3D3C0F37AB6CB471A75A071D17EC4ACDD8F3AA4D7D4F7BB559F3C09099E3D9003E5E8AA1F556B79CECDE66F85B08FA5955E6976BF2695EA076388A62D2AD5BAB7CBF1A7F3F4C8D5CDF37CDE99BD3E30B685D9E5EEE48C7C89118EF4878EB89747F28271FA2CC45F8E9E7601"},
{"3EEAED04A455D6E5E5AB53CFD5AFD2F2BC625C7BF4BE49A5",
"36B88F63ADBB5668588181D774",
"D367E3CB3703E762D23C6533188EF7028EFF9D935A3977150361997EC9DEAF1E4794BDE26AA8B53C124980B1362EC86FCDDFC7A90073171C1BAEE351A53234B86C66E8AB92FAE99EC6967A6D3428892D80",
"573454C719A9A55E04437BF7CBAAF27563CCCD92ADD5E515CD63305DFF0687E5EEF790C5DCA5C0033E9AB129505E2775438D92B38F08F3B0356BA142C6F694",
"E9F79A5B432D9E682C9AAA5661CFC2E49A0FCB81A431E54B42EB73DD3BED3F377FEC556ABA81624BA64A5D739AD41467460088F8D4F442180A9382CA635745473794C382FCDDC49BA4EB6D8A44AE3C"},
{"B695C691538F8CBD60F039D0E28894E3693CC7C36D92D79D",
"BC099AEB637361BAC536B57618",
"BFFF1A65AE38D1DC142C71637319F5F6508E2CB33C9DCB94202B359ED5A5ED8042E7F4F09231D32A7242976677E6F4C549BF65FADC99E5AF43F7A46FD95E16C2",
"081DF3FD85B415D803F0BE5AC58CFF0023FDDED99788296C3731D8",
"E50C64E3614D94FE69C47092E46ACC9957C6FEA2CCBF96BC62FBABE7424753C75F9C147C42AE26FE171531"},
{"C9ACBD2718F0689A1BE9802A551B6B8D9CF5614DAF5E65ED",
"B1B0AAF373B8B026EB80422051D8",
"6648C0E61AC733C76119D23FB24548D637751387AA2EAE9D80E912B7BD486CAAD9EAF4D7A5FE2B54AAD481E8EC94BB4D558000896E2010462B70C9FED1E7273080D1",
"189F591F6CB6D59AFEDD14C341741A8F1037DC0DF00FC57CE65C30F49E860255CEA5DC6019380CC0FE8880BC1A9E685F41C239C38F36E3F2A1388865C5C311059C0A",
"922A5E949B61D03BE34AB5F4E58607D4504EA14017BB363DAE3C873059EA7A1C77A746FB78981671D26C2CF6D9F24952D510044CE02A10177E9DB42D0145211DFE6E84369C5E3BC2669EAB4147B2822895F9"},
{"7A832BD2CF5BF4919F353CE2A8C86A5E406DA2D52BE16A72",
"2F2F17CECF7E5A756D10785A3CB9DB",
"61DA05E3788CC2D8405DBA70C7A28E5AF699863C9F72E6C6770126929F5D6FA267F005EBCF49495CB46400958A3AE80D1289D1C671",
"44E91121195A41AF14E8CFDBD39A4B517BE0DF1A72977ED8A3EEF8EEDA1166B2EB6DB2C4AE2E74FA0F0C74537F659BFBD141E5DDEC67E64EDA85AABD3F52C85A785B9FB3CECD70E7DF",
"BEDF596EA21288D2B84901E188F6EE1468B14D5161D3802DBFE00D60203A24E2AB62714BF272A45551489838C3A7FEAADC177B591836E73684867CCF4E12901DCF2064058726BBA554E84ADC5136F507E961188D4AF06943D3"},
{"1508E8AE9079AA15F1CEC4F776B4D11BCCB061B58AA56C18",
"BCA625674F41D1E3AB47672DC0C3",
"8B12CF84F16360F0EAD2A41BC021530FFCEC7F3579CAE658E10E2D3D81870F65AFCED0C77C6C4C6E6BA424FF23088C796BA6195ABA35094BF1829E089662E7A95FC90750AE16D0C8AFA55DAC789D7735B970B58D4BE7CEC7341DA82A0179A01929C27A59C5063215B859EA43",
"E525422519ECE070E82C",
"B47BC07C3ED1C0A43BA52C43CBACBCDBB29CAF1001E09FDF7107"},
{"7550C2761644E911FE9ADD119BAC07376BEA442845FEAD876D7E7AC1B713E464",
"36D2EC25ADD33CDEDF495205BBC923",
"7FCFE81A3790DE97FFC3DE160C470847EA7E841177C2F759571CBD837EA004A6CA8C6F4AEBFF2E9FD552D73EB8A30705D58D70C0B67AEEA280CBBF0A477358ACEF1E7508F2735CD9A0E4F9AC92B8C008F575D3B6278F1C18BD01227E3502E5255F3AB1893632AD00C717C588EF652A51A43209E7EE90",
"2B1A62F8FDFAA3C16470A21AD307C9A7D03ADE8EF72C69B06F8D738CDE578D7AEFD0D40BD9C022FB9F580DF5394C998ACCCEFC5471A3996FB8F1045A81FDC6F32D13502EA65A211390C8D882B8E0BEFD8DD8CBEF51D1597B124E9F7F",
"C873E02A22DB89EB0787DB6A60B99F7E4A0A085D5C4232A81ADCE2D60AA36F92DDC33F93DD8640AC0E08416B187FB382B3EC3EE85A64B0E6EE41C1366A5AD2A282F66605E87031CCBA2FA7B2DA201D975994AADE3DD1EE122AE09604AD489B84BF0C1AB7129EE16C6934850E"},
{"A51300285E554FDBDE7F771A9A9A80955639DD87129FAEF74987C91FB9687C71",
"81691D5D20EC818FCFF24B33DECC",
"C948093218AA9EB2A8E44A87EEA73FC8B6B75A196819A14BD83709EA323E8DF8B491045220E1D88729A38DBCFFB60D3056DAD4564498FD6574F74512945DEB34B69329ACED9FFC05D5D59DFCD5B973E2ACAFE6AD1EF8BBBC49351A2DD12508ED89ED",
"EB861165DAF7625F827C6B574ED703F03215",
"C6CD1CE76D2B3679C1B5AA1CFD67CCB55444B6BFD3E22C81CBC9BB738796B83E54E3"},
{"8CE0156D26FAEB7E0B9B800BBB2E9D4075B5EAC5C62358B0E7F6FCE610223282",
"D2A7B94DD12CDACA909D3AD7",
"E021A78F374FC271389AB9A3E97077D755",
"7C26000B58929F5095E1CEE154F76C2A299248E299F9B5ADE6C403AA1FD4A67FD4E0232F214CE7B919EE7A1027D2B76C57475715CD078461",
"C556FB38DF069B56F337B5FF5775CE6EAA16824DFA754F20B78819028EA635C3BB7AA731DE8776B2DCB67DCA2D33EEDF3C7E52EA450013722A41755A0752433ED17BDD5991AAE77A"},
{"1E8000A2CE00A561C9920A30BF0D7B983FEF8A1014C8F04C35CA6970E6BA02BD",
"65ED3D63F79F90BBFD19775E",
"336A8C0B7243582A46B221AA677647FCAE91",
"134A8B34824A290E7B",
"914FBEF80D0E6E17F8BDBB6097EBF5FBB0554952DC2B9E5151"},
{"53D5607BBE690B6E8D8F6D97F3DF2BA853B682597A214B8AA0EA6E598650AF15",
"C391A856B9FE234E14BA1AC7BB40FF",
"479682BC21349C4BE1641D5E78FE2C79EC1B9CF5470936DCAD9967A4DCD7C4EFADA593BC9EDE71E6A08829B8580901B61E274227E9D918502DE3",
"EAD154DC09C5E26C5D26FF33ED148B27120C7F2C23225CC0D0631B03E1F6C6D96FEB88C1A4052ACB4CE746B884B6502931F407021126C6AAB8C514C077A5A38438AE88EE",
"938821286EBB671D999B87C032E1D6055392EB564E57970D55E545FC5E8BAB90E6E3E3C0913F6320995FC636D72CD9919657CC38BD51552F4A502D8D1FE56DB33EBAC5092630E69EBB986F0E15CEE9FC8C052501"},
{"294362FCC984F440CEA3E9F7D2C06AF20C53AAC1B3738CA2186C914A6E193ABB",
"B15B61C8BB39261A8F55AB178EC3",
"D0729B6B75BB",
"2BD089ADCE9F334BAE3B065996C7D616DD0C27DF4218DCEEA0FBCA0F968837CE26B0876083327E25681FDDD620A32EC0DA12F73FAE826CC94BFF2B90A54D2651",
"AC94B25E4E21DE2437B806966CCD5D9385EF0CD4A51AB9FA6DE675C7B8952D67802E9FEC1FDE9F5D1EAB06057498BC0EEA454804FC9D2068982A3E24182D9AC2E7AB9994DDC899A604264583F63D066B"},
{"959DBFEB039B1A5B8CE6A44649B602AAA5F98A906DB96143D202CD2024F749D9",
"01D7BDB1133E9C347486C1EFA6",
"F3843955BD741F379DD750585EDC55E2CDA05CCBA8C1F4622AC2FE35214BC3A019B8BD12C4CC42D9213D1E1556941E8D8450830287FFB3B763A13722DD4140ED9846FB5FFF745D7B0B967D810A068222E10B259AF1D392035B0D83DC1498A6830B11B2418A840212599171E0258A1C203B05362978",
"A21811232C950FA8B12237C2EBD6A7CD2C3A155905E9E0C7C120",
"63C1CE397B22F1A03F1FA549B43178BC405B152D3C95E977426D519B3DFCA28498823240592B6EEE7A14"},
{"096AE499F5294173F34FF2B375F0E5D5AB79D0D03B33B1A74D7D576826345DF4",
"0C52B3D11D636E5910A4DD76D32C",
"229E9ECA3053789E937447BC719467075B6138A142DA528DA8F0CF8DDF022FD9AF8E74779BA3AC306609",
"8B7A00038783E8BAF6EDEAE0C4EAB48FC8FD501A588C7E4A4DB71E3604F2155A97687D3D2FFF8569261375A513CF4398CE0F87CA1658A1050F6EF6C4EA3E25",
"C20B6CF8D3C8241825FD90B2EDAC7593600646E579A8D8DAAE9E2E40C3835FE801B2BE4379131452BC5182C90307B176DFBE2049544222FE7783147B690774F6D9D7CEF52A91E61E298E9AA15464AC"},
}

View File

@ -0,0 +1,78 @@
package ocb
import (
"encoding/hex"
)
// Test vectors from https://tools.ietf.org/html/rfc7253. Note that key is
// shared accross tests.
var testKey, _ = hex.DecodeString("000102030405060708090A0B0C0D0E0F")
var rfc7253testVectors = []struct {
nonce, header, plaintext, ciphertext string
}{
{"BBAA99887766554433221100",
"",
"",
"785407BFFFC8AD9EDCC5520AC9111EE6"},
{"BBAA99887766554433221101",
"0001020304050607",
"0001020304050607",
"6820B3657B6F615A5725BDA0D3B4EB3A257C9AF1F8F03009"},
{"BBAA99887766554433221102",
"0001020304050607",
"",
"81017F8203F081277152FADE694A0A00"},
{"BBAA99887766554433221103",
"",
"0001020304050607",
"45DD69F8F5AAE72414054CD1F35D82760B2CD00D2F99BFA9"},
{"BBAA99887766554433221104",
"000102030405060708090A0B0C0D0E0F",
"000102030405060708090A0B0C0D0E0F",
"571D535B60B277188BE5147170A9A22C3AD7A4FF3835B8C5701C1CCEC8FC3358"},
{"BBAA99887766554433221105",
"000102030405060708090A0B0C0D0E0F",
"",
"8CF761B6902EF764462AD86498CA6B97"},
{"BBAA99887766554433221106",
"",
"000102030405060708090A0B0C0D0E0F",
"5CE88EC2E0692706A915C00AEB8B2396F40E1C743F52436BDF06D8FA1ECA343D"},
{"BBAA99887766554433221107",
"000102030405060708090A0B0C0D0E0F1011121314151617",
"000102030405060708090A0B0C0D0E0F1011121314151617",
"1CA2207308C87C010756104D8840CE1952F09673A448A122C92C62241051F57356D7F3C90BB0E07F"},
{"BBAA99887766554433221108",
"000102030405060708090A0B0C0D0E0F1011121314151617",
"",
"6DC225A071FC1B9F7C69F93B0F1E10DE"},
{"BBAA99887766554433221109",
"",
"000102030405060708090A0B0C0D0E0F1011121314151617",
"221BD0DE7FA6FE993ECCD769460A0AF2D6CDED0C395B1C3CE725F32494B9F914D85C0B1EB38357FF"},
{"BBAA9988776655443322110A",
"000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F",
"000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F",
"BD6F6C496201C69296C11EFD138A467ABD3C707924B964DEAFFC40319AF5A48540FBBA186C5553C68AD9F592A79A4240"},
{"BBAA9988776655443322110B",
"000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F",
"",
"FE80690BEE8A485D11F32965BC9D2A32"},
{"BBAA9988776655443322110C",
"",
"000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F",
"2942BFC773BDA23CABC6ACFD9BFD5835BD300F0973792EF46040C53F1432BCDFB5E1DDE3BC18A5F840B52E653444D5DF"},
{"BBAA9988776655443322110D",
"000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627",
"000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627",
"D5CA91748410C1751FF8A2F618255B68A0A12E093FF454606E59F9C1D0DDC54B65E8628E568BAD7AED07BA06A4A69483A7035490C5769E60"},
{"BBAA9988776655443322110E",
"000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627",
"",
"C5CD9D1850C141E358649994EE701B68"},
{"BBAA9988776655443322110F",
"",
"000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627",
"4412923493C57D5DE0D700F753CCE0D1D2D95060122E9F15A5DDBFC5787E50B5CC55EE507BCB084E479AD363AC366B95A98CA5F3000B1479"},
}

View File

@ -0,0 +1,24 @@
package ocb
// Second set of test vectors from https://tools.ietf.org/html/rfc7253
var rfc7253TestVectorTaglen96 = struct {
key, nonce, header, plaintext, ciphertext string
}{"0F0E0D0C0B0A09080706050403020100",
"BBAA9988776655443322110D",
"000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627",
"000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627",
"1792A4E31E0755FB03E31B22116E6C2DDF9EFD6E33D536F1A0124B0A55BAE884ED93481529C76B6AD0C515F4D1CDD4FDAC4F02AA"}
var rfc7253AlgorithmTest = []struct {
KEYLEN, TAGLEN int
OUTPUT string }{
{128, 128, "67E944D23256C5E0B6C61FA22FDF1EA2"},
{192, 128, "F673F2C3E7174AAE7BAE986CA9F29E17"},
{256, 128, "D90EB8E9C977C88B79DD793D7FFA161C"},
{128, 96, "77A3D8E73589158D25D01209"},
{192, 96, "05D56EAD2752C86BE6932C5E"},
{256, 96, "5458359AC23B0CBA9E6330DD"},
{128, 64, "192C9B7BD90BA06A"},
{192, 64, "0066BC6E0EF34E24"},
{256, 64, "7D4EA5D445501CBE"},
}

View File

@ -0,0 +1,153 @@
// Copyright 2014 Matthew Endsley
// All rights reserved
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted providing that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
// IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// Package keywrap is an implementation of the RFC 3394 AES key wrapping
// algorithm. This is used in OpenPGP with elliptic curve keys.
package keywrap
import (
"crypto/aes"
"encoding/binary"
"errors"
)
var (
// ErrWrapPlaintext is returned if the plaintext is not a multiple
// of 64 bits.
ErrWrapPlaintext = errors.New("keywrap: plainText must be a multiple of 64 bits")
// ErrUnwrapCiphertext is returned if the ciphertext is not a
// multiple of 64 bits.
ErrUnwrapCiphertext = errors.New("keywrap: cipherText must by a multiple of 64 bits")
// ErrUnwrapFailed is returned if unwrapping a key fails.
ErrUnwrapFailed = errors.New("keywrap: failed to unwrap key")
// NB: the AES NewCipher call only fails if the key is an invalid length.
// ErrInvalidKey is returned when the AES key is invalid.
ErrInvalidKey = errors.New("keywrap: invalid AES key")
)
// Wrap a key using the RFC 3394 AES Key Wrap Algorithm.
func Wrap(key, plainText []byte) ([]byte, error) {
if len(plainText)%8 != 0 {
return nil, ErrWrapPlaintext
}
c, err := aes.NewCipher(key)
if err != nil {
return nil, ErrInvalidKey
}
nblocks := len(plainText) / 8
// 1) Initialize variables.
var block [aes.BlockSize]byte
// - Set A = IV, an initial value (see 2.2.3)
for ii := 0; ii < 8; ii++ {
block[ii] = 0xA6
}
// - For i = 1 to n
// - Set R[i] = P[i]
intermediate := make([]byte, len(plainText))
copy(intermediate, plainText)
// 2) Calculate intermediate values.
for ii := 0; ii < 6; ii++ {
for jj := 0; jj < nblocks; jj++ {
// - B = AES(K, A | R[i])
copy(block[8:], intermediate[jj*8:jj*8+8])
c.Encrypt(block[:], block[:])
// - A = MSB(64, B) ^ t where t = (n*j)+1
t := uint64(ii*nblocks + jj + 1)
val := binary.BigEndian.Uint64(block[:8]) ^ t
binary.BigEndian.PutUint64(block[:8], val)
// - R[i] = LSB(64, B)
copy(intermediate[jj*8:jj*8+8], block[8:])
}
}
// 3) Output results.
// - Set C[0] = A
// - For i = 1 to n
// - C[i] = R[i]
return append(block[:8], intermediate...), nil
}
// Unwrap a key using the RFC 3394 AES Key Wrap Algorithm.
func Unwrap(key, cipherText []byte) ([]byte, error) {
if len(cipherText)%8 != 0 {
return nil, ErrUnwrapCiphertext
}
c, err := aes.NewCipher(key)
if err != nil {
return nil, ErrInvalidKey
}
nblocks := len(cipherText)/8 - 1
// 1) Initialize variables.
var block [aes.BlockSize]byte
// - Set A = C[0]
copy(block[:8], cipherText[:8])
// - For i = 1 to n
// - Set R[i] = C[i]
intermediate := make([]byte, len(cipherText)-8)
copy(intermediate, cipherText[8:])
// 2) Compute intermediate values.
for jj := 5; jj >= 0; jj-- {
for ii := nblocks - 1; ii >= 0; ii-- {
// - B = AES-1(K, (A ^ t) | R[i]) where t = n*j+1
// - A = MSB(64, B)
t := uint64(jj*nblocks + ii + 1)
val := binary.BigEndian.Uint64(block[:8]) ^ t
binary.BigEndian.PutUint64(block[:8], val)
copy(block[8:], intermediate[ii*8:ii*8+8])
c.Decrypt(block[:], block[:])
// - R[i] = LSB(B, 64)
copy(intermediate[ii*8:ii*8+8], block[8:])
}
}
// 3) Output results.
// - If A is an appropriate initial value (see 2.2.3),
for ii := 0; ii < 8; ii++ {
if block[ii] != 0xA6 {
return nil, ErrUnwrapFailed
}
}
// - For i = 1 to n
// - P[i] = R[i]
return intermediate, nil
}

View File

@ -4,13 +4,13 @@
// Package armor implements OpenPGP ASCII Armor, see RFC 4880. OpenPGP Armor is // Package armor implements OpenPGP ASCII Armor, see RFC 4880. OpenPGP Armor is
// very similar to PEM except that it has an additional CRC checksum. // very similar to PEM except that it has an additional CRC checksum.
package armor // import "golang.org/x/crypto/openpgp/armor" package armor // import "github.com/ProtonMail/go-crypto/openpgp/armor"
import ( import (
"bufio" "bufio"
"bytes" "bytes"
"encoding/base64" "encoding/base64"
"golang.org/x/crypto/openpgp/errors" "github.com/ProtonMail/go-crypto/openpgp/errors"
"io" "io"
) )

View File

@ -4,7 +4,10 @@
package openpgp package openpgp
import "hash" import (
"hash"
"io"
)
// NewCanonicalTextHash reformats text written to it into the canonical // NewCanonicalTextHash reformats text written to it into the canonical
// form and then applies the hash h. See RFC 4880, section 5.2.1. // form and then applies the hash h. See RFC 4880, section 5.2.1.
@ -19,28 +22,31 @@ type canonicalTextHash struct {
var newline = []byte{'\r', '\n'} var newline = []byte{'\r', '\n'}
func (cth *canonicalTextHash) Write(buf []byte) (int, error) { func writeCanonical(cw io.Writer, buf []byte, s *int) (int, error) {
start := 0 start := 0
for i, c := range buf { for i, c := range buf {
switch cth.s { switch *s {
case 0: case 0:
if c == '\r' { if c == '\r' {
cth.s = 1 *s = 1
} else if c == '\n' { } else if c == '\n' {
cth.h.Write(buf[start:i]) cw.Write(buf[start:i])
cth.h.Write(newline) cw.Write(newline)
start = i + 1 start = i + 1
} }
case 1: case 1:
cth.s = 0 *s = 0
} }
} }
cth.h.Write(buf[start:]) cw.Write(buf[start:])
return len(buf), nil return len(buf), nil
} }
func (cth *canonicalTextHash) Write(buf []byte) (int, error) {
return writeCanonical(cth.h, buf, &cth.s)
}
func (cth *canonicalTextHash) Sum(in []byte) []byte { func (cth *canonicalTextHash) Sum(in []byte) []byte {
return cth.h.Sum(in) return cth.h.Sum(in)
} }

View File

@ -0,0 +1,165 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package ecdh implements ECDH encryption, suitable for OpenPGP,
// as specified in RFC 6637, section 8.
package ecdh
import (
"bytes"
"crypto/elliptic"
"errors"
"io"
"math/big"
"github.com/ProtonMail/go-crypto/openpgp/aes/keywrap"
"github.com/ProtonMail/go-crypto/openpgp/internal/algorithm"
"github.com/ProtonMail/go-crypto/openpgp/internal/ecc"
)
type KDF struct {
Hash algorithm.Hash
Cipher algorithm.Cipher
}
type PublicKey struct {
ecc.CurveType
elliptic.Curve
X, Y *big.Int
KDF
}
type PrivateKey struct {
PublicKey
D []byte
}
func GenerateKey(c elliptic.Curve, kdf KDF, rand io.Reader) (priv *PrivateKey, err error) {
priv = new(PrivateKey)
priv.PublicKey.Curve = c
priv.PublicKey.KDF = kdf
priv.D, priv.PublicKey.X, priv.PublicKey.Y, err = elliptic.GenerateKey(c, rand)
return
}
func Encrypt(random io.Reader, pub *PublicKey, msg, curveOID, fingerprint []byte) (vsG, c []byte, err error) {
if len(msg) > 40 {
return nil, nil, errors.New("ecdh: message too long")
}
// the sender MAY use 21, 13, and 5 bytes of padding for AES-128,
// AES-192, and AES-256, respectively, to provide the same number of
// octets, 40 total, as an input to the key wrapping method.
padding := make([]byte, 40-len(msg))
for i := range padding {
padding[i] = byte(40 - len(msg))
}
m := append(msg, padding...)
if pub.CurveType == ecc.Curve25519 {
return X25519Encrypt(random, pub, m, curveOID, fingerprint)
}
d, x, y, err := elliptic.GenerateKey(pub.Curve, random)
if err != nil {
return nil, nil, err
}
vsG = elliptic.Marshal(pub.Curve, x, y)
zbBig, _ := pub.Curve.ScalarMult(pub.X, pub.Y, d)
byteLen := (pub.Curve.Params().BitSize + 7) >> 3
zb := make([]byte, byteLen)
zbBytes := zbBig.Bytes()
copy(zb[byteLen-len(zbBytes):], zbBytes)
z, err := buildKey(pub, zb, curveOID, fingerprint, false, false)
if err != nil {
return nil, nil, err
}
if c, err = keywrap.Wrap(z, m); err != nil {
return nil, nil, err
}
return vsG, c, nil
}
func Decrypt(priv *PrivateKey, vsG, m, curveOID, fingerprint []byte) (msg []byte, err error) {
if priv.PublicKey.CurveType == ecc.Curve25519 {
return X25519Decrypt(priv, vsG, m, curveOID, fingerprint)
}
x, y := elliptic.Unmarshal(priv.Curve, vsG)
zbBig, _ := priv.Curve.ScalarMult(x, y, priv.D)
byteLen := (priv.Curve.Params().BitSize + 7) >> 3
zb := make([]byte, byteLen)
zbBytes := zbBig.Bytes()
copy(zb[byteLen-len(zbBytes):], zbBytes)
z, err := buildKey(&priv.PublicKey, zb, curveOID, fingerprint, false, false)
if err != nil {
return nil, err
}
c, err := keywrap.Unwrap(z, m)
if err != nil {
return nil, err
}
return c[:len(c)-int(c[len(c)-1])], nil
}
func buildKey(pub *PublicKey, zb []byte, curveOID, fingerprint []byte, stripLeading, stripTrailing bool) ([]byte, error) {
// Param = curve_OID_len || curve_OID || public_key_alg_ID || 03
// || 01 || KDF_hash_ID || KEK_alg_ID for AESKeyWrap
// || "Anonymous Sender " || recipient_fingerprint;
param := new(bytes.Buffer)
if _, err := param.Write(curveOID); err != nil {
return nil, err
}
algKDF := []byte{18, 3, 1, pub.KDF.Hash.Id(), pub.KDF.Cipher.Id()}
if _, err := param.Write(algKDF); err != nil {
return nil, err
}
if _, err := param.Write([]byte("Anonymous Sender ")); err != nil {
return nil, err
}
// For v5 keys, the 20 leftmost octets of the fingerprint are used.
if _, err := param.Write(fingerprint[:20]); err != nil {
return nil, err
}
if param.Len() - len(curveOID) != 45 {
return nil, errors.New("ecdh: malformed KDF Param")
}
// MB = Hash ( 00 || 00 || 00 || 01 || ZB || Param );
h := pub.KDF.Hash.New()
if _, err := h.Write([]byte{0x0, 0x0, 0x0, 0x1}); err != nil {
return nil, err
}
zbLen := len(zb)
i := 0
j := zbLen - 1
if stripLeading {
// Work around old go crypto bug where the leading zeros are missing.
for ; i < zbLen && zb[i] == 0; i++ {}
}
if stripTrailing {
// Work around old OpenPGP.js bug where insignificant trailing zeros in
// this little-endian number are missing.
// (See https://github.com/openpgpjs/openpgpjs/pull/853.)
for ; j >= 0 && zb[j] == 0; j-- {}
}
if _, err := h.Write(zb[i:j+1]); err != nil {
return nil, err
}
if _, err := h.Write(param.Bytes()); err != nil {
return nil, err
}
mb := h.Sum(nil)
return mb[:pub.KDF.Cipher.KeySize()], nil // return oBits leftmost bits of MB.
}

View File

@ -0,0 +1,157 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package ecdh implements ECDH encryption, suitable for OpenPGP,
// as specified in RFC 6637, section 8.
package ecdh
import (
"errors"
"io"
"math/big"
"github.com/ProtonMail/go-crypto/openpgp/aes/keywrap"
"github.com/ProtonMail/go-crypto/openpgp/internal/ecc"
"golang.org/x/crypto/curve25519"
)
// Generates a private-public key-pair.
// 'priv' is a private key; a scalar belonging to the set
// 2^{254} + 8 * [0, 2^{251}), in order to avoid the small subgroup of the
// curve. 'pub' is simply 'priv' * G where G is the base point.
// See https://cr.yp.to/ecdh.html and RFC7748, sec 5.
func x25519GenerateKeyPairBytes(rand io.Reader) (priv [32]byte, pub [32]byte, err error) {
var n, helper = new(big.Int), new(big.Int)
n.SetUint64(1)
n.Lsh(n, 252)
helper.SetString("27742317777372353535851937790883648493", 10)
n.Add(n, helper)
for true {
_, err = io.ReadFull(rand, priv[:])
if err != nil {
return
}
// The following ensures that the private key is a number of the form
// 2^{254} + 8 * [0, 2^{251}), in order to avoid the small subgroup of
// of the curve.
priv[0] &= 248
priv[31] &= 127
priv[31] |= 64
// If the scalar is out of range, sample another random number.
if new(big.Int).SetBytes(priv[:]).Cmp(n) >= 0 {
continue
}
curve25519.ScalarBaseMult(&pub, &priv)
return
}
return
}
// X25519GenerateKey samples the key pair according to the correct distribution.
// It also sets the given key-derivation function and returns the *PrivateKey
// object along with an error.
func X25519GenerateKey(rand io.Reader, kdf KDF) (priv *PrivateKey, err error) {
ci := ecc.FindByName("Curve25519")
priv = new(PrivateKey)
priv.PublicKey.Curve = ci.Curve
d, pubKey, err := x25519GenerateKeyPairBytes(rand)
if err != nil {
return nil, err
}
priv.PublicKey.KDF = kdf
priv.D = make([]byte, 32)
copyReversed(priv.D, d[:])
priv.PublicKey.CurveType = ci.CurveType
priv.PublicKey.Curve = ci.Curve
/*
* Note that ECPoint.point differs from the definition of public keys in
* [Curve25519] in two ways: (1) the byte-ordering is big-endian, which is
* more uniform with how big integers are represented in TLS, and (2) there
* is an additional length byte (so ECpoint.point is actually 33 bytes),
* again for uniformity (and extensibility).
*/
var encodedKey = make([]byte, 33)
encodedKey[0] = 0x40
copy(encodedKey[1:], pubKey[:])
priv.PublicKey.X = new(big.Int).SetBytes(encodedKey[:])
priv.PublicKey.Y = new(big.Int)
return priv, nil
}
func X25519Encrypt(random io.Reader, pub *PublicKey, msg, curveOID, fingerprint []byte) (vsG, c []byte, err error) {
d, ephemeralKey, err := x25519GenerateKeyPairBytes(random)
if err != nil {
return nil, nil, err
}
var pubKey [32]byte
if pub.X.BitLen() > 33*264 {
return nil, nil, errors.New("ecdh: invalid key")
}
copy(pubKey[:], pub.X.Bytes()[1:])
var zb [32]byte
curve25519.ScalarBaseMult(&zb, &d)
curve25519.ScalarMult(&zb, &d, &pubKey)
z, err := buildKey(pub, zb[:], curveOID, fingerprint, false, false)
if err != nil {
return nil, nil, err
}
if c, err = keywrap.Wrap(z, msg); err != nil {
return nil, nil, err
}
var vsg [33]byte
vsg[0] = 0x40
copy(vsg[1:], ephemeralKey[:])
return vsg[:], c, nil
}
func X25519Decrypt(priv *PrivateKey, vsG, m, curveOID, fingerprint []byte) (msg []byte, err error) {
var zb, d, ephemeralKey [32]byte
if len(vsG) != 33 || vsG[0] != 0x40 {
return nil, errors.New("ecdh: invalid key")
}
copy(ephemeralKey[:], vsG[1:33])
copyReversed(d[:], priv.D)
curve25519.ScalarBaseMult(&zb, &d)
curve25519.ScalarMult(&zb, &d, &ephemeralKey)
var c []byte
for i := 0; i < 3; i++ {
// Try buildKey three times for compat, see comments in buildKey.
z, err := buildKey(&priv.PublicKey, zb[:], curveOID, fingerprint, i == 1, i == 2)
if err != nil {
return nil, err
}
res, err := keywrap.Unwrap(z, m)
if i == 2 && err != nil {
// Only return an error after we've tried all variants of buildKey.
return nil, err
}
c = res
if err == nil {
break
}
}
return c[:len(c)-int(c[len(c)-1])], nil
}
func copyReversed(out []byte, in []byte) {
l := len(in)
for i := 0; i < l; i++ {
out[i] = in[l-i-1]
}
}

View File

@ -10,7 +10,7 @@
// This form of ElGamal embeds PKCS#1 v1.5 padding, which may make it // This form of ElGamal embeds PKCS#1 v1.5 padding, which may make it
// unsuitable for other protocols. RSA should be used in preference in any // unsuitable for other protocols. RSA should be used in preference in any
// case. // case.
package elgamal // import "golang.org/x/crypto/openpgp/elgamal" package elgamal // import "github.com/ProtonMail/go-crypto/openpgp/elgamal"
import ( import (
"crypto/rand" "crypto/rand"

View File

@ -3,7 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// Package errors contains common error types for the OpenPGP packages. // Package errors contains common error types for the OpenPGP packages.
package errors // import "golang.org/x/crypto/openpgp/errors" package errors // import "github.com/ProtonMail/go-crypto/openpgp/errors"
import ( import (
"strconv" "strconv"
@ -41,6 +41,25 @@ func (b SignatureError) Error() string {
return "openpgp: invalid signature: " + string(b) return "openpgp: invalid signature: " + string(b)
} }
var ErrMDCHashMismatch error = SignatureError("MDC hash mismatch")
var ErrMDCMissing error = SignatureError("MDC packet not found")
type signatureExpiredError int
func (se signatureExpiredError) Error() string {
return "openpgp: signature expired"
}
var ErrSignatureExpired error = signatureExpiredError(0)
type keyExpiredError int
func (ke keyExpiredError) Error() string {
return "openpgp: key expired"
}
var ErrKeyExpired error = keyExpiredError(0)
type keyIncorrectError int type keyIncorrectError int
func (ki keyIncorrectError) Error() string { func (ki keyIncorrectError) Error() string {
@ -49,6 +68,14 @@ func (ki keyIncorrectError) Error() string {
var ErrKeyIncorrect error = keyIncorrectError(0) var ErrKeyIncorrect error = keyIncorrectError(0)
// KeyInvalidError indicates that the public key parameters are invalid
// as they do not match the private ones
type KeyInvalidError string
func (e KeyInvalidError) Error() string {
return "openpgp: invalid key: " + string(e)
}
type unknownIssuerError int type unknownIssuerError int
func (unknownIssuerError) Error() string { func (unknownIssuerError) Error() string {
@ -70,3 +97,20 @@ type UnknownPacketTypeError uint8
func (upte UnknownPacketTypeError) Error() string { func (upte UnknownPacketTypeError) Error() string {
return "openpgp: unknown packet type: " + strconv.Itoa(int(upte)) return "openpgp: unknown packet type: " + strconv.Itoa(int(upte))
} }
// AEADError indicates that there is a problem when initializing or using a
// AEAD instance, configuration struct, nonces or index values.
type AEADError string
func (ae AEADError) Error() string {
return "openpgp: aead error: " + string(ae)
}
// ErrDummyPrivateKey results when operations are attempted on a private key
// that is just a dummy key. See
// https://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=blob;f=doc/DETAILS;h=fe55ae16ab4e26d8356dc574c9e8bc935e71aef1;hb=23191d7851eae2217ecdac6484349849a24fd94a#l1109
type ErrDummyPrivateKey string
func (dke ErrDummyPrivateKey) Error() string {
return "openpgp: s2k GNU dummy key: " + string(dke)
}

View File

@ -0,0 +1,65 @@
// Copyright (C) 2019 ProtonTech AG
package algorithm
import (
"crypto/cipher"
"github.com/ProtonMail/go-crypto/eax"
"github.com/ProtonMail/go-crypto/ocb"
)
// AEADMode defines the Authenticated Encryption with Associated Data mode of
// operation.
type AEADMode uint8
// Supported modes of operation (see RFC4880bis [EAX] and RFC7253)
const (
AEADModeEAX = AEADMode(1)
AEADModeOCB = AEADMode(2)
AEADModeGCM = AEADMode(100)
)
// TagLength returns the length in bytes of authentication tags.
func (mode AEADMode) TagLength() int {
switch mode {
case AEADModeEAX:
return 16
case AEADModeOCB:
return 16
case AEADModeGCM:
return 16
default:
return 0
}
}
// NonceLength returns the length in bytes of nonces.
func (mode AEADMode) NonceLength() int {
switch mode {
case AEADModeEAX:
return 16
case AEADModeOCB:
return 15
case AEADModeGCM:
return 12
default:
return 0
}
}
// New returns a fresh instance of the given mode
func (mode AEADMode) New(block cipher.Block) (alg cipher.AEAD) {
var err error
switch mode {
case AEADModeEAX:
alg, err = eax.NewEAX(block)
case AEADModeOCB:
alg, err = ocb.NewOCB(block)
case AEADModeGCM:
alg, err = cipher.NewGCM(block)
}
if err != nil {
panic(err.Error())
}
return alg
}

View File

@ -0,0 +1,107 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package algorithm
import (
"crypto/aes"
"crypto/cipher"
"crypto/des"
"golang.org/x/crypto/cast5"
)
// Cipher is an official symmetric key cipher algorithm. See RFC 4880,
// section 9.2.
type Cipher interface {
// Id returns the algorithm ID, as a byte, of the cipher.
Id() uint8
// KeySize returns the key size, in bytes, of the cipher.
KeySize() int
// BlockSize returns the block size, in bytes, of the cipher.
BlockSize() int
// New returns a fresh instance of the given cipher.
New(key []byte) cipher.Block
}
// The following constants mirror the OpenPGP standard (RFC 4880).
const (
TripleDES = CipherFunction(2)
CAST5 = CipherFunction(3)
AES128 = CipherFunction(7)
AES192 = CipherFunction(8)
AES256 = CipherFunction(9)
)
// CipherById represents the different block ciphers specified for OpenPGP. See
// http://www.iana.org/assignments/pgp-parameters/pgp-parameters.xhtml#pgp-parameters-13
var CipherById = map[uint8]Cipher{
TripleDES.Id(): TripleDES,
CAST5.Id(): CAST5,
AES128.Id(): AES128,
AES192.Id(): AES192,
AES256.Id(): AES256,
}
type CipherFunction uint8
// ID returns the algorithm Id, as a byte, of cipher.
func (sk CipherFunction) Id() uint8 {
return uint8(sk)
}
var keySizeByID = map[uint8]int{
TripleDES.Id(): 24,
CAST5.Id(): cast5.KeySize,
AES128.Id(): 16,
AES192.Id(): 24,
AES256.Id(): 32,
}
// KeySize returns the key size, in bytes, of cipher.
func (cipher CipherFunction) KeySize() int {
switch cipher {
case TripleDES:
return 24
case CAST5:
return cast5.KeySize
case AES128:
return 16
case AES192:
return 24
case AES256:
return 32
}
return 0
}
// BlockSize returns the block size, in bytes, of cipher.
func (cipher CipherFunction) BlockSize() int {
switch cipher {
case TripleDES:
return des.BlockSize
case CAST5:
return 8
case AES128, AES192, AES256:
return 16
}
return 0
}
// New returns a fresh instance of the given cipher.
func (cipher CipherFunction) New(key []byte) (block cipher.Block) {
var err error
switch cipher {
case TripleDES:
block, err = des.NewTripleDESCipher(key)
case CAST5:
block, err = cast5.NewCipher(key)
case AES128, AES192, AES256:
block, err = aes.NewCipher(key)
}
if err != nil {
panic(err.Error())
}
return
}

View File

@ -0,0 +1,86 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package algorithm
import (
"crypto"
"fmt"
"hash"
)
// Hash is an official hash function algorithm. See RFC 4880, section 9.4.
type Hash interface {
// Id returns the algorithm ID, as a byte, of Hash.
Id() uint8
// Available reports whether the given hash function is linked into the binary.
Available() bool
// HashFunc simply returns the value of h so that Hash implements SignerOpts.
HashFunc() crypto.Hash
// New returns a new hash.Hash calculating the given hash function. New
// panics if the hash function is not linked into the binary.
New() hash.Hash
// Size returns the length, in bytes, of a digest resulting from the given
// hash function. It doesn't require that the hash function in question be
// linked into the program.
Size() int
// String is the name of the hash function corresponding to the given
// OpenPGP hash id.
String() string
}
// The following vars mirror the crypto/Hash supported hash functions.
var (
MD5 Hash = cryptoHash{1, crypto.MD5}
SHA1 Hash = cryptoHash{2, crypto.SHA1}
RIPEMD160 Hash = cryptoHash{3, crypto.RIPEMD160}
SHA256 Hash = cryptoHash{8, crypto.SHA256}
SHA384 Hash = cryptoHash{9, crypto.SHA384}
SHA512 Hash = cryptoHash{10, crypto.SHA512}
SHA224 Hash = cryptoHash{11, crypto.SHA224}
)
// HashById represents the different hash functions specified for OpenPGP. See
// http://www.iana.org/assignments/pgp-parameters/pgp-parameters.xhtml#pgp-parameters-14
var (
HashById = map[uint8]Hash{
MD5.Id(): MD5,
SHA1.Id(): SHA1,
RIPEMD160.Id(): RIPEMD160,
SHA256.Id(): SHA256,
SHA384.Id(): SHA384,
SHA512.Id(): SHA512,
SHA224.Id(): SHA224,
}
)
// cryptoHash contains pairs relating OpenPGP's hash identifier with
// Go's crypto.Hash type. See RFC 4880, section 9.4.
type cryptoHash struct {
id uint8
crypto.Hash
}
// Id returns the algorithm ID, as a byte, of cryptoHash.
func (h cryptoHash) Id() uint8 {
return h.id
}
var hashNames = map[uint8]string{
MD5.Id(): "MD5",
SHA1.Id(): "SHA1",
RIPEMD160.Id(): "RIPEMD160",
SHA256.Id(): "SHA256",
SHA384.Id(): "SHA384",
SHA512.Id(): "SHA512",
SHA224.Id(): "SHA224",
}
func (h cryptoHash) String() string {
s, ok := hashNames[h.id]
if !ok {
panic(fmt.Sprintf("Unsupported hash function %d", h.id))
}
return s
}

View File

@ -0,0 +1,118 @@
package ecc
import (
"github.com/ProtonMail/go-crypto/openpgp/internal/encoding"
"crypto/elliptic"
"bytes"
"github.com/ProtonMail/go-crypto/bitcurves"
"github.com/ProtonMail/go-crypto/brainpool"
)
type SignatureAlgorithm uint8
const (
ECDSA SignatureAlgorithm = 1
EdDSA SignatureAlgorithm = 2
)
type CurveInfo struct {
Name string
Oid *encoding.OID
Curve elliptic.Curve
SigAlgorithm SignatureAlgorithm
CurveType CurveType
}
var curves = []CurveInfo{
{
Name: "NIST curve P-256",
Oid: encoding.NewOID([]byte{0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07}),
Curve: elliptic.P256(),
CurveType: NISTCurve,
SigAlgorithm: ECDSA,
},
{
Name: "NIST curve P-384",
Oid: encoding.NewOID([]byte{0x2B, 0x81, 0x04, 0x00, 0x22}),
Curve: elliptic.P384(),
CurveType: NISTCurve,
SigAlgorithm: ECDSA,
},
{
Name: "NIST curve P-521",
Oid: encoding.NewOID([]byte{0x2B, 0x81, 0x04, 0x00, 0x23}),
Curve: elliptic.P521(),
CurveType: NISTCurve,
SigAlgorithm: ECDSA,
},
{
Name: "SecP256k1",
Oid: encoding.NewOID([]byte{0x2B, 0x81, 0x04, 0x00, 0x0A}),
Curve: bitcurves.S256(),
CurveType: BitCurve,
SigAlgorithm: ECDSA,
},
{
Name: "Curve25519",
Oid: encoding.NewOID([]byte{0x2B, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01}),
Curve: elliptic.P256(),// filler
CurveType: Curve25519,
SigAlgorithm: ECDSA,
},
{
Name: "Ed25519",
Oid: encoding.NewOID([]byte{0x2B, 0x06, 0x01, 0x04, 0x01, 0xDA, 0x47, 0x0F, 0x01}),
Curve: elliptic.P256(), // filler
CurveType: NISTCurve,
SigAlgorithm: EdDSA,
},
{
Name: "Brainpool P256r1",
Oid: encoding.NewOID([]byte{0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x07}),
Curve: brainpool.P256r1(),
CurveType: BrainpoolCurve,
SigAlgorithm: ECDSA,
},
{
Name: "BrainpoolP384r1",
Oid: encoding.NewOID([]byte{0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0B}),
Curve: brainpool.P384r1(),
CurveType: BrainpoolCurve,
SigAlgorithm: ECDSA,
},
{
Name: "BrainpoolP512r1",
Oid: encoding.NewOID([]byte{0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0D}),
Curve: brainpool.P512r1(),
CurveType: BrainpoolCurve,
SigAlgorithm: ECDSA,
},
}
func FindByCurve(curve elliptic.Curve) *CurveInfo {
for _, curveInfo := range curves {
if curveInfo.Curve == curve {
return &curveInfo
}
}
return nil
}
func FindByOid(oid encoding.Field) *CurveInfo {
var rawBytes = oid.Bytes()
for _, curveInfo := range curves {
if bytes.Equal(curveInfo.Oid.Bytes(), rawBytes) {
return &curveInfo
}
}
return nil
}
func FindByName(name string) *CurveInfo {
for _, curveInfo := range curves {
if curveInfo.Name == name {
return &curveInfo
}
}
return nil
}

View File

@ -0,0 +1,10 @@
package ecc
type CurveType uint8
const (
NISTCurve CurveType = 1
Curve25519 CurveType = 2
BitCurve CurveType = 3
BrainpoolCurve CurveType = 4
)

View File

@ -0,0 +1,27 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package encoding implements openpgp packet field encodings as specified in
// RFC 4880 and 6637.
package encoding
import "io"
// Field is an encoded field of an openpgp packet.
type Field interface {
// Bytes returns the decoded data.
Bytes() []byte
// BitLength is the size in bits of the decoded data.
BitLength() uint16
// EncodedBytes returns the encoded data.
EncodedBytes() []byte
// EncodedLength is the size in bytes of the encoded data.
EncodedLength() uint16
// ReadFrom reads the next Field from r.
ReadFrom(r io.Reader) (int64, error)
}

View File

@ -0,0 +1,91 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package encoding
import (
"io"
"math/big"
"math/bits"
)
// An MPI is used to store the contents of a big integer, along with the bit
// length that was specified in the original input. This allows the MPI to be
// reserialized exactly.
type MPI struct {
bytes []byte
bitLength uint16
}
// NewMPI returns a MPI initialized with bytes.
func NewMPI(bytes []byte) *MPI {
for len(bytes) != 0 && bytes[0] == 0 {
bytes = bytes[1:]
}
if len(bytes) == 0 {
bitLength := uint16(0)
return &MPI{bytes, bitLength}
}
bitLength := 8*uint16(len(bytes)-1) + uint16(bits.Len8(bytes[0]))
return &MPI{bytes, bitLength}
}
// Bytes returns the decoded data.
func (m *MPI) Bytes() []byte {
return m.bytes
}
// BitLength is the size in bits of the decoded data.
func (m *MPI) BitLength() uint16 {
return m.bitLength
}
// EncodedBytes returns the encoded data.
func (m *MPI) EncodedBytes() []byte {
return append([]byte{byte(m.bitLength >> 8), byte(m.bitLength)}, m.bytes...)
}
// EncodedLength is the size in bytes of the encoded data.
func (m *MPI) EncodedLength() uint16 {
return uint16(2 + len(m.bytes))
}
// ReadFrom reads into m the next MPI from r.
func (m *MPI) ReadFrom(r io.Reader) (int64, error) {
var buf [2]byte
n, err := io.ReadFull(r, buf[0:])
if err != nil {
if err == io.EOF {
err = io.ErrUnexpectedEOF
}
return int64(n), err
}
m.bitLength = uint16(buf[0])<<8 | uint16(buf[1])
m.bytes = make([]byte, (int(m.bitLength)+7)/8)
nn, err := io.ReadFull(r, m.bytes)
if err == io.EOF {
err = io.ErrUnexpectedEOF
}
// remove leading zero bytes from malformed GnuPG encoded MPIs:
// https://bugs.gnupg.org/gnupg/issue1853
// for _, b := range m.bytes {
// if b != 0 {
// break
// }
// m.bytes = m.bytes[1:]
// m.bitLength -= 8
// }
return int64(n) + int64(nn), err
}
// SetBig initializes m with the bits from n.
func (m *MPI) SetBig(n *big.Int) *MPI {
m.bytes = n.Bytes()
m.bitLength = uint16(n.BitLen())
return m
}

View File

@ -0,0 +1,88 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package encoding
import (
"io"
"github.com/ProtonMail/go-crypto/openpgp/errors"
)
// OID is used to store a variable-length field with a one-octet size
// prefix. See https://tools.ietf.org/html/rfc6637#section-9.
type OID struct {
bytes []byte
}
const (
// maxOID is the maximum number of bytes in a OID.
maxOID = 254
// reservedOIDLength1 and reservedOIDLength2 are OID lengths that the RFC
// specifies are reserved.
reservedOIDLength1 = 0
reservedOIDLength2 = 0xff
)
// NewOID returns a OID initialized with bytes.
func NewOID(bytes []byte) *OID {
switch len(bytes) {
case reservedOIDLength1, reservedOIDLength2:
panic("encoding: NewOID argument length is reserved")
default:
if len(bytes) > maxOID {
panic("encoding: NewOID argment too large")
}
}
return &OID{
bytes: bytes,
}
}
// Bytes returns the decoded data.
func (o *OID) Bytes() []byte {
return o.bytes
}
// BitLength is the size in bits of the decoded data.
func (o *OID) BitLength() uint16 {
return uint16(len(o.bytes) * 8)
}
// EncodedBytes returns the encoded data.
func (o *OID) EncodedBytes() []byte {
return append([]byte{byte(len(o.bytes))}, o.bytes...)
}
// EncodedLength is the size in bytes of the encoded data.
func (o *OID) EncodedLength() uint16 {
return uint16(1 + len(o.bytes))
}
// ReadFrom reads into b the next OID from r.
func (o *OID) ReadFrom(r io.Reader) (int64, error) {
var buf [1]byte
n, err := io.ReadFull(r, buf[:])
if err != nil {
if err == io.EOF {
err = io.ErrUnexpectedEOF
}
return int64(n), err
}
switch buf[0] {
case reservedOIDLength1, reservedOIDLength2:
return int64(n), errors.UnsupportedError("reserved for future extensions")
}
o.bytes = make([]byte, buf[0])
nn, err := io.ReadFull(r, o.bytes)
if err == io.EOF {
err = io.ErrUnexpectedEOF
}
return int64(n) + int64(nn), err
}

View File

@ -0,0 +1,375 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package openpgp
import (
"crypto"
"crypto/rand"
"crypto/rsa"
goerrors "errors"
"io"
"math/big"
"github.com/ProtonMail/go-crypto/openpgp/ecdh"
"github.com/ProtonMail/go-crypto/openpgp/errors"
"github.com/ProtonMail/go-crypto/openpgp/internal/algorithm"
"github.com/ProtonMail/go-crypto/openpgp/packet"
"golang.org/x/crypto/ed25519"
)
// NewEntity returns an Entity that contains a fresh RSA/RSA keypair with a
// single identity composed of the given full name, comment and email, any of
// which may be empty but must not contain any of "()<>\x00".
// If config is nil, sensible defaults will be used.
func NewEntity(name, comment, email string, config *packet.Config) (*Entity, error) {
creationTime := config.Now()
keyLifetimeSecs := config.KeyLifetime()
uid := packet.NewUserId(name, comment, email)
if uid == nil {
return nil, errors.InvalidArgumentError("user id field contained invalid characters")
}
// Generate a primary signing key
primaryPrivRaw, err := newSigner(config)
if err != nil {
return nil, err
}
primary := packet.NewSignerPrivateKey(creationTime, primaryPrivRaw)
if config != nil && config.V5Keys {
primary.UpgradeToV5()
}
isPrimaryId := true
selfSignature := &packet.Signature{
Version: primary.PublicKey.Version,
SigType: packet.SigTypePositiveCert,
PubKeyAlgo: primary.PublicKey.PubKeyAlgo,
Hash: config.Hash(),
CreationTime: creationTime,
KeyLifetimeSecs: &keyLifetimeSecs,
IssuerKeyId: &primary.PublicKey.KeyId,
IssuerFingerprint: primary.PublicKey.Fingerprint,
IsPrimaryId: &isPrimaryId,
FlagsValid: true,
FlagSign: true,
FlagCertify: true,
MDC: true, // true by default, see 5.8 vs. 5.14
AEAD: config.AEAD() != nil,
V5Keys: config != nil && config.V5Keys,
}
// Set the PreferredHash for the SelfSignature from the packet.Config.
// If it is not the must-implement algorithm from rfc4880bis, append that.
selfSignature.PreferredHash = []uint8{hashToHashId(config.Hash())}
if config.Hash() != crypto.SHA256 {
selfSignature.PreferredHash = append(selfSignature.PreferredHash, hashToHashId(crypto.SHA256))
}
// Likewise for DefaultCipher.
selfSignature.PreferredSymmetric = []uint8{uint8(config.Cipher())}
if config.Cipher() != packet.CipherAES128 {
selfSignature.PreferredSymmetric = append(selfSignature.PreferredSymmetric, uint8(packet.CipherAES128))
}
// And for DefaultMode.
selfSignature.PreferredAEAD = []uint8{uint8(config.AEAD().Mode())}
if config.AEAD().Mode() != packet.AEADModeEAX {
selfSignature.PreferredAEAD = append(selfSignature.PreferredAEAD, uint8(packet.AEADModeEAX))
}
// User ID binding signature
err = selfSignature.SignUserId(uid.Id, &primary.PublicKey, primary, config)
if err != nil {
return nil, err
}
// Generate an encryption subkey
subPrivRaw, err := newDecrypter(config)
if err != nil {
return nil, err
}
sub := packet.NewDecrypterPrivateKey(creationTime, subPrivRaw)
sub.IsSubkey = true
sub.PublicKey.IsSubkey = true
if config != nil && config.V5Keys {
sub.UpgradeToV5()
}
// NOTE: No KeyLifetimeSecs here, but we will not return this subkey in EncryptionKey()
// if the primary/master key has expired.
subKey := Subkey{
PublicKey: &sub.PublicKey,
PrivateKey: sub,
Sig: &packet.Signature{
Version: primary.PublicKey.Version,
CreationTime: creationTime,
SigType: packet.SigTypeSubkeyBinding,
PubKeyAlgo: primary.PublicKey.PubKeyAlgo,
Hash: config.Hash(),
FlagsValid: true,
FlagEncryptStorage: true,
FlagEncryptCommunications: true,
IssuerKeyId: &primary.PublicKey.KeyId,
},
}
// Subkey binding signature
err = subKey.Sig.SignKey(subKey.PublicKey, primary, config)
if err != nil {
return nil, err
}
return &Entity{
PrimaryKey: &primary.PublicKey,
PrivateKey: primary,
Identities: map[string]*Identity{
uid.Id: &Identity{
Name: uid.Id,
UserId: uid,
SelfSignature: selfSignature,
Signatures: []*packet.Signature{selfSignature},
},
},
Subkeys: []Subkey{subKey},
}, nil
}
// AddSigningSubkey adds a signing keypair as a subkey to the Entity.
// If config is nil, sensible defaults will be used.
func (e *Entity) AddSigningSubkey(config *packet.Config) error {
creationTime := config.Now()
keyLifetimeSecs := config.KeyLifetime()
subPrivRaw, err := newSigner(config)
if err != nil {
return err
}
sub := packet.NewSignerPrivateKey(creationTime, subPrivRaw)
subkey := Subkey{
PublicKey: &sub.PublicKey,
PrivateKey: sub,
Sig: &packet.Signature{
Version: e.PrimaryKey.Version,
CreationTime: creationTime,
KeyLifetimeSecs: &keyLifetimeSecs,
SigType: packet.SigTypeSubkeyBinding,
PubKeyAlgo: e.PrimaryKey.PubKeyAlgo,
Hash: config.Hash(),
FlagsValid: true,
FlagSign: true,
IssuerKeyId: &e.PrimaryKey.KeyId,
EmbeddedSignature: &packet.Signature{
Version: e.PrimaryKey.Version,
CreationTime: creationTime,
SigType: packet.SigTypePrimaryKeyBinding,
PubKeyAlgo: sub.PublicKey.PubKeyAlgo,
Hash: config.Hash(),
IssuerKeyId: &e.PrimaryKey.KeyId,
},
},
}
if config != nil && config.V5Keys {
subkey.PublicKey.UpgradeToV5()
}
err = subkey.Sig.EmbeddedSignature.CrossSignKey(subkey.PublicKey, e.PrimaryKey, subkey.PrivateKey, config)
if err != nil {
return err
}
subkey.PublicKey.IsSubkey = true
subkey.PrivateKey.IsSubkey = true
if err = subkey.Sig.SignKey(subkey.PublicKey, e.PrivateKey, config); err != nil {
return err
}
e.Subkeys = append(e.Subkeys, subkey)
return nil
}
// AddEncryptionSubkey adds an encryption keypair as a subkey to the Entity.
// If config is nil, sensible defaults will be used.
func (e *Entity) AddEncryptionSubkey(config *packet.Config) error {
creationTime := config.Now()
keyLifetimeSecs := config.KeyLifetime()
subPrivRaw, err := newDecrypter(config)
if err != nil {
return err
}
sub := packet.NewDecrypterPrivateKey(creationTime, subPrivRaw)
subkey := Subkey{
PublicKey: &sub.PublicKey,
PrivateKey: sub,
Sig: &packet.Signature{
Version: e.PrimaryKey.Version,
CreationTime: creationTime,
KeyLifetimeSecs: &keyLifetimeSecs,
SigType: packet.SigTypeSubkeyBinding,
PubKeyAlgo: e.PrimaryKey.PubKeyAlgo,
Hash: config.Hash(),
FlagsValid: true,
FlagEncryptStorage: true,
FlagEncryptCommunications: true,
IssuerKeyId: &e.PrimaryKey.KeyId,
},
}
if config != nil && config.V5Keys {
subkey.PublicKey.UpgradeToV5()
}
subkey.PublicKey.IsSubkey = true
subkey.PrivateKey.IsSubkey = true
if err = subkey.Sig.SignKey(subkey.PublicKey, e.PrivateKey, config); err != nil {
return err
}
e.Subkeys = append(e.Subkeys, subkey)
return nil
}
// Generates a signing key
func newSigner(config *packet.Config) (signer crypto.Signer, err error) {
switch config.PublicKeyAlgorithm() {
case packet.PubKeyAlgoRSA:
bits := config.RSAModulusBits()
if bits < 1024 {
return nil, errors.InvalidArgumentError("bits must be >= 1024")
}
if config != nil && len(config.RSAPrimes) >= 2 {
primes := config.RSAPrimes[0:2]
config.RSAPrimes = config.RSAPrimes[2:]
return generateRSAKeyWithPrimes(config.Random(), 2, bits, primes)
}
return rsa.GenerateKey(config.Random(), bits)
case packet.PubKeyAlgoEdDSA:
_, priv, err := ed25519.GenerateKey(config.Random())
if err != nil {
return nil, err
}
return &priv, nil
default:
return nil, errors.InvalidArgumentError("unsupported public key algorithm")
}
}
// Generates an encryption/decryption key
func newDecrypter(config *packet.Config) (decrypter interface{}, err error) {
switch config.PublicKeyAlgorithm() {
case packet.PubKeyAlgoRSA:
bits := config.RSAModulusBits()
if bits < 1024 {
return nil, errors.InvalidArgumentError("bits must be >= 1024")
}
if config != nil && len(config.RSAPrimes) >= 2 {
primes := config.RSAPrimes[0:2]
config.RSAPrimes = config.RSAPrimes[2:]
return generateRSAKeyWithPrimes(config.Random(), 2, bits, primes)
}
return rsa.GenerateKey(config.Random(), bits)
case packet.PubKeyAlgoEdDSA:
fallthrough // When passing EdDSA, we generate an ECDH subkey
case packet.PubKeyAlgoECDH:
var kdf = ecdh.KDF{
Hash: algorithm.SHA512,
Cipher: algorithm.AES256,
}
return ecdh.X25519GenerateKey(config.Random(), kdf)
default:
return nil, errors.InvalidArgumentError("unsupported public key algorithm")
}
}
var bigOne = big.NewInt(1)
// generateRSAKeyWithPrimes generates a multi-prime RSA keypair of the
// given bit size, using the given random source and prepopulated primes.
func generateRSAKeyWithPrimes(random io.Reader, nprimes int, bits int, prepopulatedPrimes []*big.Int) (*rsa.PrivateKey, error) {
priv := new(rsa.PrivateKey)
priv.E = 65537
if nprimes < 2 {
return nil, goerrors.New("generateRSAKeyWithPrimes: nprimes must be >= 2")
}
if bits < 1024 {
return nil, goerrors.New("generateRSAKeyWithPrimes: bits must be >= 1024")
}
primes := make([]*big.Int, nprimes)
NextSetOfPrimes:
for {
todo := bits
// crypto/rand should set the top two bits in each prime.
// Thus each prime has the form
// p_i = 2^bitlen(p_i) × 0.11... (in base 2).
// And the product is:
// P = 2^todo × α
// where α is the product of nprimes numbers of the form 0.11...
//
// If α < 1/2 (which can happen for nprimes > 2), we need to
// shift todo to compensate for lost bits: the mean value of 0.11...
// is 7/8, so todo + shift - nprimes * log2(7/8) ~= bits - 1/2
// will give good results.
if nprimes >= 7 {
todo += (nprimes - 2) / 5
}
for i := 0; i < nprimes; i++ {
var err error
if len(prepopulatedPrimes) == 0 {
primes[i], err = rand.Prime(random, todo/(nprimes-i))
if err != nil {
return nil, err
}
} else {
primes[i] = prepopulatedPrimes[0]
prepopulatedPrimes = prepopulatedPrimes[1:]
}
todo -= primes[i].BitLen()
}
// Make sure that primes is pairwise unequal.
for i, prime := range primes {
for j := 0; j < i; j++ {
if prime.Cmp(primes[j]) == 0 {
continue NextSetOfPrimes
}
}
}
n := new(big.Int).Set(bigOne)
totient := new(big.Int).Set(bigOne)
pminus1 := new(big.Int)
for _, prime := range primes {
n.Mul(n, prime)
pminus1.Sub(prime, bigOne)
totient.Mul(totient, pminus1)
}
if n.BitLen() != bits {
// This should never happen for nprimes == 2 because
// crypto/rand should set the top two bits in each prime.
// For nprimes > 2 we hope it does not happen often.
continue NextSetOfPrimes
}
priv.D = new(big.Int)
e := big.NewInt(int64(priv.E))
ok := priv.D.ModInverse(e, totient)
if ok != nil {
priv.Primes = primes
priv.N = n
break
}
}
priv.Precompute()
return priv, nil
}

View File

@ -5,13 +5,13 @@
package openpgp package openpgp
import ( import (
"crypto/rsa" goerrors "errors"
"io" "io"
"time" "time"
"golang.org/x/crypto/openpgp/armor" "github.com/ProtonMail/go-crypto/openpgp/armor"
"golang.org/x/crypto/openpgp/errors" "github.com/ProtonMail/go-crypto/openpgp/errors"
"golang.org/x/crypto/openpgp/packet" "github.com/ProtonMail/go-crypto/openpgp/packet"
) )
// PublicKeyType is the armor type for a PGP public key. // PublicKeyType is the armor type for a PGP public key.
@ -71,9 +71,9 @@ type KeyRing interface {
DecryptionKeys() []Key DecryptionKeys() []Key
} }
// primaryIdentity returns the Identity marked as primary or the first identity // PrimaryIdentity returns the Identity marked as primary or the first identity
// if none are so marked. // if none are so marked.
func (e *Entity) primaryIdentity() *Identity { func (e *Entity) PrimaryIdentity() *Identity {
var firstIdentity *Identity var firstIdentity *Identity
for _, ident := range e.Identities { for _, ident := range e.Identities {
if firstIdentity == nil { if firstIdentity == nil {
@ -86,18 +86,24 @@ func (e *Entity) primaryIdentity() *Identity {
return firstIdentity return firstIdentity
} }
// encryptionKey returns the best candidate Key for encrypting a message to the // EncryptionKey returns the best candidate Key for encrypting a message to the
// given Entity. // given Entity.
func (e *Entity) encryptionKey(now time.Time) (Key, bool) { func (e *Entity) EncryptionKey(now time.Time) (Key, bool) {
candidateSubkey := -1 // Fail to find any encryption key if the primary key has expired.
i := e.PrimaryIdentity()
primaryKeyExpired := e.PrimaryKey.KeyExpired(i.SelfSignature, now)
if primaryKeyExpired {
return Key{}, false
}
// Iterate the keys to find the newest key // Iterate the keys to find the newest, unexpired one
candidateSubkey := -1
var maxTime time.Time var maxTime time.Time
for i, subkey := range e.Subkeys { for i, subkey := range e.Subkeys {
if subkey.Sig.FlagsValid && if subkey.Sig.FlagsValid &&
subkey.Sig.FlagEncryptCommunications && subkey.Sig.FlagEncryptCommunications &&
subkey.PublicKey.PubKeyAlgo.CanEncrypt() && subkey.PublicKey.PubKeyAlgo.CanEncrypt() &&
!subkey.Sig.KeyExpired(now) && !subkey.PublicKey.KeyExpired(subkey.Sig, now) &&
(maxTime.IsZero() || subkey.Sig.CreationTime.After(maxTime)) { (maxTime.IsZero() || subkey.Sig.CreationTime.After(maxTime)) {
candidateSubkey = i candidateSubkey = i
maxTime = subkey.Sig.CreationTime maxTime = subkey.Sig.CreationTime
@ -112,30 +118,44 @@ func (e *Entity) encryptionKey(now time.Time) (Key, bool) {
// If we don't have any candidate subkeys for encryption and // If we don't have any candidate subkeys for encryption and
// the primary key doesn't have any usage metadata then we // the primary key doesn't have any usage metadata then we
// assume that the primary key is ok. Or, if the primary key is // assume that the primary key is ok. Or, if the primary key is
// marked as ok to encrypt to, then we can obviously use it. // marked as ok to encrypt with, then we can obviously use it.
i := e.primaryIdentity() // Also, check expiry again just to be safe.
if !i.SelfSignature.FlagsValid || i.SelfSignature.FlagEncryptCommunications && if !i.SelfSignature.FlagsValid || i.SelfSignature.FlagEncryptCommunications &&
e.PrimaryKey.PubKeyAlgo.CanEncrypt() && e.PrimaryKey.PubKeyAlgo.CanEncrypt() && !primaryKeyExpired {
!i.SelfSignature.KeyExpired(now) {
return Key{e, e.PrimaryKey, e.PrivateKey, i.SelfSignature}, true return Key{e, e.PrimaryKey, e.PrivateKey, i.SelfSignature}, true
} }
// This Entity appears to be signing only.
return Key{}, false return Key{}, false
} }
// signingKey return the best candidate Key for signing a message with this // SigningKey return the best candidate Key for signing a message with this
// Entity. // Entity.
func (e *Entity) signingKey(now time.Time) (Key, bool) { func (e *Entity) SigningKey(now time.Time) (Key, bool) {
candidateSubkey := -1 return e.SigningKeyById(now, 0)
}
for i, subkey := range e.Subkeys { // SigningKeyById return the Key for signing a message with this
// Entity and keyID.
func (e *Entity) SigningKeyById(now time.Time, id uint64) (Key, bool) {
// Fail to find any signing key if the primary key has expired.
i := e.PrimaryIdentity()
primaryKeyExpired := e.PrimaryKey.KeyExpired(i.SelfSignature, now)
if primaryKeyExpired {
return Key{}, false
}
// Iterate the keys to find the newest, unexpired one
candidateSubkey := -1
var maxTime time.Time
for idx, subkey := range e.Subkeys {
if subkey.Sig.FlagsValid && if subkey.Sig.FlagsValid &&
subkey.Sig.FlagSign && subkey.Sig.FlagSign &&
subkey.PublicKey.PubKeyAlgo.CanSign() && subkey.PublicKey.PubKeyAlgo.CanSign() &&
!subkey.Sig.KeyExpired(now) { !subkey.PublicKey.KeyExpired(subkey.Sig, now) &&
candidateSubkey = i (maxTime.IsZero() || subkey.Sig.CreationTime.After(maxTime)) &&
break (id == 0 || subkey.PrivateKey.KeyId == id) {
candidateSubkey = idx
maxTime = subkey.Sig.CreationTime
} }
} }
@ -145,13 +165,15 @@ func (e *Entity) signingKey(now time.Time) (Key, bool) {
} }
// If we have no candidate subkey then we assume that it's ok to sign // If we have no candidate subkey then we assume that it's ok to sign
// with the primary key. // with the primary key. Or, if the primary key is marked as ok to
i := e.primaryIdentity() // sign with, then we can use it. Also, check expiry again just to be safe.
if !i.SelfSignature.FlagsValid || i.SelfSignature.FlagSign && if !i.SelfSignature.FlagsValid || i.SelfSignature.FlagSign &&
!i.SelfSignature.KeyExpired(now) { e.PrimaryKey.PubKeyAlgo.CanSign() && !primaryKeyExpired &&
(id == 0 || e.PrivateKey.KeyId == id) {
return Key{e, e.PrimaryKey, e.PrivateKey, i.SelfSignature}, true return Key{e, e.PrimaryKey, e.PrivateKey, i.SelfSignature}, true
} }
// No keys with a valid Signing Flag or no keys matched the id passed in
return Key{}, false return Key{}, false
} }
@ -420,11 +442,14 @@ func addUserID(e *Entity, packets *packet.Reader, pkt *packet.UserId) error {
break break
} }
if (sig.SigType == packet.SigTypePositiveCert || sig.SigType == packet.SigTypeGenericCert) && sig.IssuerKeyId != nil && *sig.IssuerKeyId == e.PrimaryKey.KeyId { if (sig.SigType == packet.SigTypePositiveCert || sig.SigType == packet.SigTypeGenericCert) && sig.CheckKeyIdOrFingerprint(e.PrimaryKey) {
if err = e.PrimaryKey.VerifyUserIdSignature(pkt.Id, e.PrimaryKey, sig); err != nil { if err = e.PrimaryKey.VerifyUserIdSignature(pkt.Id, e.PrimaryKey, sig); err != nil {
return errors.StructuralError("user ID self-signature invalid: " + err.Error()) return errors.StructuralError("user ID self-signature invalid: " + err.Error())
} }
if identity.SelfSignature == nil || sig.CreationTime.After(identity.SelfSignature.CreationTime) {
identity.SelfSignature = sig identity.SelfSignature = sig
}
identity.Signatures = append(identity.Signatures, sig)
e.Identities[pkt.Id] = identity e.Identities[pkt.Id] = identity
} else { } else {
identity.Signatures = append(identity.Signatures, sig) identity.Signatures = append(identity.Signatures, sig)
@ -465,7 +490,6 @@ func addSubkey(e *Entity, packets *packet.Reader, pub *packet.PublicKey, priv *p
case packet.SigTypeSubkeyRevocation: case packet.SigTypeSubkeyRevocation:
subKey.Sig = sig subKey.Sig = sig
case packet.SigTypeSubkeyBinding: case packet.SigTypeSubkeyBinding:
if shouldReplaceSubkeySig(subKey.Sig, sig) { if shouldReplaceSubkeySig(subKey.Sig, sig) {
subKey.Sig = sig subKey.Sig = sig
} }
@ -497,99 +521,30 @@ func shouldReplaceSubkeySig(existingSig, potentialNewSig *packet.Signature) bool
return potentialNewSig.CreationTime.After(existingSig.CreationTime) return potentialNewSig.CreationTime.After(existingSig.CreationTime)
} }
const defaultRSAKeyBits = 2048
// NewEntity returns an Entity that contains a fresh RSA/RSA keypair with a
// single identity composed of the given full name, comment and email, any of
// which may be empty but must not contain any of "()<>\x00".
// If config is nil, sensible defaults will be used.
func NewEntity(name, comment, email string, config *packet.Config) (*Entity, error) {
creationTime := config.Now()
bits := defaultRSAKeyBits
if config != nil && config.RSABits != 0 {
bits = config.RSABits
}
uid := packet.NewUserId(name, comment, email)
if uid == nil {
return nil, errors.InvalidArgumentError("user id field contained invalid characters")
}
signingPriv, err := rsa.GenerateKey(config.Random(), bits)
if err != nil {
return nil, err
}
encryptingPriv, err := rsa.GenerateKey(config.Random(), bits)
if err != nil {
return nil, err
}
e := &Entity{
PrimaryKey: packet.NewRSAPublicKey(creationTime, &signingPriv.PublicKey),
PrivateKey: packet.NewRSAPrivateKey(creationTime, signingPriv),
Identities: make(map[string]*Identity),
}
isPrimaryId := true
e.Identities[uid.Id] = &Identity{
Name: uid.Id,
UserId: uid,
SelfSignature: &packet.Signature{
CreationTime: creationTime,
SigType: packet.SigTypePositiveCert,
PubKeyAlgo: packet.PubKeyAlgoRSA,
Hash: config.Hash(),
IsPrimaryId: &isPrimaryId,
FlagsValid: true,
FlagSign: true,
FlagCertify: true,
IssuerKeyId: &e.PrimaryKey.KeyId,
},
}
err = e.Identities[uid.Id].SelfSignature.SignUserId(uid.Id, e.PrimaryKey, e.PrivateKey, config)
if err != nil {
return nil, err
}
// If the user passes in a DefaultHash via packet.Config,
// set the PreferredHash for the SelfSignature.
if config != nil && config.DefaultHash != 0 {
e.Identities[uid.Id].SelfSignature.PreferredHash = []uint8{hashToHashId(config.DefaultHash)}
}
// Likewise for DefaultCipher.
if config != nil && config.DefaultCipher != 0 {
e.Identities[uid.Id].SelfSignature.PreferredSymmetric = []uint8{uint8(config.DefaultCipher)}
}
e.Subkeys = make([]Subkey, 1)
e.Subkeys[0] = Subkey{
PublicKey: packet.NewRSAPublicKey(creationTime, &encryptingPriv.PublicKey),
PrivateKey: packet.NewRSAPrivateKey(creationTime, encryptingPriv),
Sig: &packet.Signature{
CreationTime: creationTime,
SigType: packet.SigTypeSubkeyBinding,
PubKeyAlgo: packet.PubKeyAlgoRSA,
Hash: config.Hash(),
FlagsValid: true,
FlagEncryptStorage: true,
FlagEncryptCommunications: true,
IssuerKeyId: &e.PrimaryKey.KeyId,
},
}
e.Subkeys[0].PublicKey.IsSubkey = true
e.Subkeys[0].PrivateKey.IsSubkey = true
err = e.Subkeys[0].Sig.SignKey(e.Subkeys[0].PublicKey, e.PrivateKey, config)
if err != nil {
return nil, err
}
return e, nil
}
// SerializePrivate serializes an Entity, including private key material, but // SerializePrivate serializes an Entity, including private key material, but
// excluding signatures from other entities, to the given Writer. // excluding signatures from other entities, to the given Writer.
// Identities and subkeys are re-signed in case they changed since NewEntry. // Identities and subkeys are re-signed in case they changed since NewEntry.
// If config is nil, sensible defaults will be used. // If config is nil, sensible defaults will be used.
func (e *Entity) SerializePrivate(w io.Writer, config *packet.Config) (err error) { func (e *Entity) SerializePrivate(w io.Writer, config *packet.Config) (err error) {
if e.PrivateKey.Dummy() {
return errors.ErrDummyPrivateKey("dummy private key cannot re-sign identities")
}
return e.serializePrivate(w, config, true)
}
// SerializePrivateWithoutSigning serializes an Entity, including private key
// material, but excluding signatures from other entities, to the given Writer.
// Self-signatures of identities and subkeys are not re-signed. This is useful
// when serializing GNU dummy keys, among other things.
// If config is nil, sensible defaults will be used.
func (e *Entity) SerializePrivateWithoutSigning(w io.Writer, config *packet.Config) (err error) {
return e.serializePrivate(w, config, false)
}
func (e *Entity) serializePrivate(w io.Writer, config *packet.Config, reSign bool) (err error) {
if e.PrivateKey == nil {
return goerrors.New("openpgp: private key is missing")
}
err = e.PrivateKey.Serialize(w) err = e.PrivateKey.Serialize(w)
if err != nil { if err != nil {
return return
@ -599,10 +554,12 @@ func (e *Entity) SerializePrivate(w io.Writer, config *packet.Config) (err error
if err != nil { if err != nil {
return return
} }
if reSign {
err = ident.SelfSignature.SignUserId(ident.UserId.Id, e.PrimaryKey, e.PrivateKey, config) err = ident.SelfSignature.SignUserId(ident.UserId.Id, e.PrimaryKey, e.PrivateKey, config)
if err != nil { if err != nil {
return return
} }
}
err = ident.SelfSignature.Serialize(w) err = ident.SelfSignature.Serialize(w)
if err != nil { if err != nil {
return return
@ -613,10 +570,19 @@ func (e *Entity) SerializePrivate(w io.Writer, config *packet.Config) (err error
if err != nil { if err != nil {
return return
} }
if reSign {
err = subkey.Sig.SignKey(subkey.PublicKey, e.PrivateKey, config) err = subkey.Sig.SignKey(subkey.PublicKey, e.PrivateKey, config)
if err != nil { if err != nil {
return return
} }
if subkey.Sig.EmbeddedSignature != nil {
err = subkey.Sig.EmbeddedSignature.CrossSignKey(subkey.PublicKey, e.PrimaryKey,
subkey.PrivateKey, config)
if err != nil {
return
}
}
}
err = subkey.Sig.Serialize(w) err = subkey.Sig.Serialize(w)
if err != nil { if err != nil {
return return
@ -637,10 +603,6 @@ func (e *Entity) Serialize(w io.Writer) error {
if err != nil { if err != nil {
return err return err
} }
err = ident.SelfSignature.Serialize(w)
if err != nil {
return err
}
for _, sig := range ident.Signatures { for _, sig := range ident.Signatures {
err = sig.Serialize(w) err = sig.Serialize(w)
if err != nil { if err != nil {
@ -679,6 +641,7 @@ func (e *Entity) SignIdentity(identity string, signer *Entity, config *packet.Co
} }
sig := &packet.Signature{ sig := &packet.Signature{
Version: signer.PrivateKey.Version,
SigType: packet.SigTypeGenericCert, SigType: packet.SigTypeGenericCert,
PubKeyAlgo: signer.PrivateKey.PubKeyAlgo, PubKeyAlgo: signer.PrivateKey.PubKeyAlgo,
Hash: config.Hash(), Hash: config.Hash(),
@ -691,3 +654,54 @@ func (e *Entity) SignIdentity(identity string, signer *Entity, config *packet.Co
ident.Signatures = append(ident.Signatures, sig) ident.Signatures = append(ident.Signatures, sig)
return nil return nil
} }
// RevokeKey generates a key revocation signature (packet.SigTypeKeyRevocation) with the
// specified reason code and text (RFC4880 section-5.2.3.23).
// If config is nil, sensible defaults will be used.
func (e *Entity) RevokeKey(reason packet.ReasonForRevocation, reasonText string, config *packet.Config) error {
reasonCode := uint8(reason)
revSig := &packet.Signature{
Version: e.PrimaryKey.Version,
CreationTime: config.Now(),
SigType: packet.SigTypeKeyRevocation,
PubKeyAlgo: packet.PubKeyAlgoRSA,
Hash: config.Hash(),
RevocationReason: &reasonCode,
RevocationReasonText: reasonText,
IssuerKeyId: &e.PrimaryKey.KeyId,
}
if err := revSig.RevokeKey(e.PrimaryKey, e.PrivateKey, config); err != nil {
return err
}
e.Revocations = append(e.Revocations, revSig)
return nil
}
// RevokeSubkey generates a subkey revocation signature (packet.SigTypeSubkeyRevocation) for
// a subkey with the specified reason code and text (RFC4880 section-5.2.3.23).
// If config is nil, sensible defaults will be used.
func (e *Entity) RevokeSubkey(sk *Subkey, reason packet.ReasonForRevocation, reasonText string, config *packet.Config) error {
if err := e.PrimaryKey.VerifyKeySignature(sk.PublicKey, sk.Sig); err != nil {
return errors.InvalidArgumentError("given subkey is not associated with this key")
}
reasonCode := uint8(reason)
revSig := &packet.Signature{
Version: e.PrimaryKey.Version,
CreationTime: config.Now(),
SigType: packet.SigTypeSubkeyRevocation,
PubKeyAlgo: packet.PubKeyAlgoRSA,
Hash: config.Hash(),
RevocationReason: &reasonCode,
RevocationReasonText: reasonText,
IssuerKeyId: &e.PrimaryKey.KeyId,
}
if err := revSig.RevokeKey(sk.PublicKey, e.PrivateKey, config); err != nil {
return err
}
sk.Sig = revSig
return nil
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,56 @@
// Copyright (C) 2019 ProtonTech AG
package packet
import "math/bits"
// AEADConfig collects a number of AEAD parameters along with sensible defaults.
// A nil AEADConfig is valid and results in all default values.
type AEADConfig struct {
// The AEAD mode of operation.
DefaultMode AEADMode
// Amount of octets in each chunk of data
ChunkSize uint64
}
// Mode returns the AEAD mode of operation.
func (conf *AEADConfig) Mode() AEADMode {
if conf == nil || conf.DefaultMode == 0 {
return AEADModeEAX
}
mode := conf.DefaultMode
if mode != AEADModeEAX && mode != AEADModeOCB &&
mode != AEADModeExperimentalGCM {
panic("AEAD mode unsupported")
}
return mode
}
// ChunkSizeByte returns the byte indicating the chunk size. The effective
// chunk size is computed with the formula uint64(1) << (chunkSizeByte + 6)
func (conf *AEADConfig) ChunkSizeByte() byte {
if conf == nil || conf.ChunkSize == 0 {
return 12 // 1 << (12 + 6) == 262144 bytes
}
chunkSize := conf.ChunkSize
exponent := bits.Len64(chunkSize) - 1
switch {
case exponent < 6:
exponent = 6
case exponent > 27:
exponent = 27
}
return byte(exponent - 6)
}
// decodeAEADChunkSize returns the effective chunk size. In 32-bit systems, the
// maximum returned value is 1 << 30.
func decodeAEADChunkSize(c byte) int {
size := uint64(1 << (c + 6))
if size != uint64(int(size)) {
return 1 << 30
}
return int(size)
}

View File

@ -0,0 +1,364 @@
// Copyright (C) 2019 ProtonTech AG
package packet
import (
"bytes"
"crypto/cipher"
"crypto/rand"
"encoding/binary"
"io"
"github.com/ProtonMail/go-crypto/openpgp/errors"
"github.com/ProtonMail/go-crypto/openpgp/internal/algorithm"
)
// AEADEncrypted represents an AEAD Encrypted Packet (tag 20, RFC4880bis-5.16).
type AEADEncrypted struct {
cipher CipherFunction
mode AEADMode
chunkSizeByte byte
Contents io.Reader // Encrypted chunks and tags
initialNonce []byte // Referred to as IV in RFC4880-bis
}
// Only currently defined version
const aeadEncryptedVersion = 1
// An AEAD opener/sealer, its configuration, and data for en/decryption.
type aeadCrypter struct {
aead cipher.AEAD
chunkSize int
initialNonce []byte
associatedData []byte // Chunk-independent associated data
chunkIndex []byte // Chunk counter
bytesProcessed int // Amount of plaintext bytes encrypted/decrypted
buffer bytes.Buffer // Buffered bytes accross chunks
}
// aeadEncrypter encrypts and writes bytes. It encrypts when necessary according
// to the AEAD block size, and buffers the extra encrypted bytes for next write.
type aeadEncrypter struct {
aeadCrypter // Embedded plaintext sealer
writer io.WriteCloser // 'writer' is a partialLengthWriter
}
// aeadDecrypter reads and decrypts bytes. It buffers extra decrypted bytes when
// necessary, similar to aeadEncrypter.
type aeadDecrypter struct {
aeadCrypter // Embedded ciphertext opener
reader io.Reader // 'reader' is a partialLengthReader
peekedBytes []byte // Used to detect last chunk
eof bool
}
func (ae *AEADEncrypted) parse(buf io.Reader) error {
headerData := make([]byte, 4)
if n, err := io.ReadFull(buf, headerData); n < 4 {
return errors.AEADError("could not read aead header:" + err.Error())
}
// Read initial nonce
mode := AEADMode(headerData[2])
nonceLen := mode.NonceLength()
if nonceLen == 0 {
return errors.AEADError("unknown mode")
}
initialNonce := make([]byte, nonceLen)
if n, err := io.ReadFull(buf, initialNonce); n < nonceLen {
return errors.AEADError("could not read aead nonce:" + err.Error())
}
ae.Contents = buf
ae.initialNonce = initialNonce
c := headerData[1]
if _, ok := algorithm.CipherById[c]; !ok {
return errors.UnsupportedError("unknown cipher: " + string(c))
}
ae.cipher = CipherFunction(c)
ae.mode = mode
ae.chunkSizeByte = byte(headerData[3])
return nil
}
// Decrypt returns a io.ReadCloser from which decrypted bytes can be read, or
// an error.
func (ae *AEADEncrypted) Decrypt(ciph CipherFunction, key []byte) (io.ReadCloser, error) {
return ae.decrypt(key)
}
// decrypt prepares an aeadCrypter and returns a ReadCloser from which
// decrypted bytes can be read (see aeadDecrypter.Read()).
func (ae *AEADEncrypted) decrypt(key []byte) (io.ReadCloser, error) {
blockCipher := ae.cipher.new(key)
aead := ae.mode.new(blockCipher)
// Carry the first tagLen bytes
tagLen := ae.mode.TagLength()
peekedBytes := make([]byte, tagLen)
n, err := io.ReadFull(ae.Contents, peekedBytes)
if n < tagLen || (err != nil && err != io.EOF) {
return nil, errors.AEADError("Not enough data to decrypt:" + err.Error())
}
chunkSize := decodeAEADChunkSize(ae.chunkSizeByte)
return &aeadDecrypter{
aeadCrypter: aeadCrypter{
aead: aead,
chunkSize: chunkSize,
initialNonce: ae.initialNonce,
associatedData: ae.associatedData(),
chunkIndex: make([]byte, 8),
},
reader: ae.Contents,
peekedBytes: peekedBytes}, nil
}
// Read decrypts bytes and reads them into dst. It decrypts when necessary and
// buffers extra decrypted bytes. It returns the number of bytes copied into dst
// and an error.
func (ar *aeadDecrypter) Read(dst []byte) (n int, err error) {
// Return buffered plaintext bytes from previous calls
if ar.buffer.Len() > 0 {
return ar.buffer.Read(dst)
}
// Return EOF if we've previously validated the final tag
if ar.eof {
return 0, io.EOF
}
// Read a chunk
tagLen := ar.aead.Overhead()
cipherChunkBuf := new(bytes.Buffer)
_, errRead := io.CopyN(cipherChunkBuf, ar.reader, int64(ar.chunkSize + tagLen))
cipherChunk := cipherChunkBuf.Bytes()
if errRead != nil && errRead != io.EOF {
return 0, errRead
}
decrypted, errChunk := ar.openChunk(cipherChunk)
if errChunk != nil {
return 0, errChunk
}
// Return decrypted bytes, buffering if necessary
if len(dst) < len(decrypted) {
n = copy(dst, decrypted[:len(dst)])
ar.buffer.Write(decrypted[len(dst):])
} else {
n = copy(dst, decrypted)
}
// Check final authentication tag
if errRead == io.EOF {
errChunk := ar.validateFinalTag(ar.peekedBytes)
if errChunk != nil {
return n, errChunk
}
ar.eof = true // Mark EOF for when we've returned all buffered data
}
return
}
// Close is noOp. The final authentication tag of the stream was already
// checked in the last Read call. In the future, this function could be used to
// wipe the reader and peeked, decrypted bytes, if necessary.
func (ar *aeadDecrypter) Close() (err error) {
return nil
}
// SerializeAEADEncrypted initializes the aeadCrypter and returns a writer.
// This writer encrypts and writes bytes (see aeadEncrypter.Write()).
func SerializeAEADEncrypted(w io.Writer, key []byte, cipher CipherFunction, mode AEADMode, config *Config) (io.WriteCloser, error) {
writeCloser := noOpCloser{w}
writer, err := serializeStreamHeader(writeCloser, packetTypeAEADEncrypted)
if err != nil {
return nil, err
}
// Data for en/decryption: tag, version, cipher, aead mode, chunk size
aeadConf := config.AEAD()
prefix := []byte{
0xD4,
aeadEncryptedVersion,
byte(config.Cipher()),
byte(aeadConf.Mode()),
aeadConf.ChunkSizeByte(),
}
n, err := writer.Write(prefix[1:])
if err != nil || n < 4 {
return nil, errors.AEADError("could not write AEAD headers")
}
// Sample nonce
nonceLen := aeadConf.Mode().NonceLength()
nonce := make([]byte, nonceLen)
n, err = rand.Read(nonce)
if err != nil {
panic("Could not sample random nonce")
}
_, err = writer.Write(nonce)
if err != nil {
return nil, err
}
blockCipher := CipherFunction(config.Cipher()).new(key)
alg := AEADMode(aeadConf.Mode()).new(blockCipher)
chunkSize := decodeAEADChunkSize(aeadConf.ChunkSizeByte())
return &aeadEncrypter{
aeadCrypter: aeadCrypter{
aead: alg,
chunkSize: chunkSize,
associatedData: prefix,
chunkIndex: make([]byte, 8),
initialNonce: nonce,
},
writer: writer}, nil
}
// Write encrypts and writes bytes. It encrypts when necessary and buffers extra
// plaintext bytes for next call. When the stream is finished, Close() MUST be
// called to append the final tag.
func (aw *aeadEncrypter) Write(plaintextBytes []byte) (n int, err error) {
// Append plaintextBytes to existing buffered bytes
n, err = aw.buffer.Write(plaintextBytes)
if err != nil {
return n, err
}
// Encrypt and write chunks
for aw.buffer.Len() >= aw.chunkSize {
plainChunk := aw.buffer.Next(aw.chunkSize)
encryptedChunk, err := aw.sealChunk(plainChunk)
if err != nil {
return n, err
}
_, err = aw.writer.Write(encryptedChunk)
if err != nil {
return n, err
}
}
return
}
// Close encrypts and writes the remaining buffered plaintext if any, appends
// the final authentication tag, and closes the embedded writer. This function
// MUST be called at the end of a stream.
func (aw *aeadEncrypter) Close() (err error) {
// Encrypt and write a chunk if there's buffered data left, or if we haven't
// written any chunks yet.
if aw.buffer.Len() > 0 || aw.bytesProcessed == 0 {
plainChunk := aw.buffer.Bytes()
lastEncryptedChunk, err := aw.sealChunk(plainChunk)
if err != nil {
return err
}
_, err = aw.writer.Write(lastEncryptedChunk)
if err != nil {
return err
}
}
// Compute final tag (associated data: packet tag, version, cipher, aead,
// chunk size, index, total number of encrypted octets).
adata := append(aw.associatedData[:], aw.chunkIndex[:]...)
adata = append(adata, make([]byte, 8)...)
binary.BigEndian.PutUint64(adata[13:], uint64(aw.bytesProcessed))
nonce := aw.computeNextNonce()
finalTag := aw.aead.Seal(nil, nonce, nil, adata)
_, err = aw.writer.Write(finalTag)
if err != nil {
return err
}
return aw.writer.Close()
}
// sealChunk Encrypts and authenticates the given chunk.
func (aw *aeadEncrypter) sealChunk(data []byte) ([]byte, error) {
if len(data) > aw.chunkSize {
return nil, errors.AEADError("chunk exceeds maximum length")
}
if aw.associatedData == nil {
return nil, errors.AEADError("can't seal without headers")
}
adata := append(aw.associatedData, aw.chunkIndex...)
nonce := aw.computeNextNonce()
encrypted := aw.aead.Seal(nil, nonce, data, adata)
aw.bytesProcessed += len(data)
if err := aw.aeadCrypter.incrementIndex(); err != nil {
return nil, err
}
return encrypted, nil
}
// openChunk decrypts and checks integrity of an encrypted chunk, returning
// the underlying plaintext and an error. It access peeked bytes from next
// chunk, to identify the last chunk and decrypt/validate accordingly.
func (ar *aeadDecrypter) openChunk(data []byte) ([]byte, error) {
tagLen := ar.aead.Overhead()
// Restore carried bytes from last call
chunkExtra := append(ar.peekedBytes, data...)
// 'chunk' contains encrypted bytes, followed by an authentication tag.
chunk := chunkExtra[:len(chunkExtra)-tagLen]
ar.peekedBytes = chunkExtra[len(chunkExtra)-tagLen:]
adata := append(ar.associatedData, ar.chunkIndex...)
nonce := ar.computeNextNonce()
plainChunk, err := ar.aead.Open(nil, nonce, chunk, adata)
if err != nil {
return nil, err
}
ar.bytesProcessed += len(plainChunk)
if err = ar.aeadCrypter.incrementIndex(); err != nil {
return nil, err
}
return plainChunk, nil
}
// Checks the summary tag. It takes into account the total decrypted bytes into
// the associated data. It returns an error, or nil if the tag is valid.
func (ar *aeadDecrypter) validateFinalTag(tag []byte) error {
// Associated: tag, version, cipher, aead, chunk size, index, and octets
amountBytes := make([]byte, 8)
binary.BigEndian.PutUint64(amountBytes, uint64(ar.bytesProcessed))
adata := append(ar.associatedData, ar.chunkIndex...)
adata = append(adata, amountBytes...)
nonce := ar.computeNextNonce()
_, err := ar.aead.Open(nil, nonce, tag, adata)
if err != nil {
return err
}
return nil
}
// Associated data for chunks: tag, version, cipher, mode, chunk size byte
func (ae *AEADEncrypted) associatedData() []byte {
return []byte{
0xD4,
aeadEncryptedVersion,
byte(ae.cipher),
byte(ae.mode),
ae.chunkSizeByte}
}
// computeNonce takes the incremental index and computes an eXclusive OR with
// the least significant 8 bytes of the receivers' initial nonce (see sec.
// 5.16.1 and 5.16.2). It returns the resulting nonce.
func (wo *aeadCrypter) computeNextNonce() (nonce []byte) {
nonce = make([]byte, len(wo.initialNonce))
copy(nonce, wo.initialNonce)
offset := len(wo.initialNonce) - 8
for i := 0; i < 8; i++ {
nonce[i+offset] ^= wo.chunkIndex[i]
}
return
}
// incrementIndex perfoms an integer increment by 1 of the integer represented by the
// slice, modifying it accordingly.
func (wo *aeadCrypter) incrementIndex() error {
index := wo.chunkIndex
if len(index) == 0 {
return errors.AEADError("Index has length 0")
}
for i := len(index) - 1; i >= 0; i-- {
if index[i] < 255 {
index[i]++
return nil
}
index[i] = 0
}
return errors.AEADError("cannot further increment index")
}

View File

@ -8,7 +8,7 @@ import (
"compress/bzip2" "compress/bzip2"
"compress/flate" "compress/flate"
"compress/zlib" "compress/zlib"
"golang.org/x/crypto/openpgp/errors" "github.com/ProtonMail/go-crypto/openpgp/errors"
"io" "io"
"strconv" "strconv"
) )

View File

@ -8,6 +8,7 @@ import (
"crypto" "crypto"
"crypto/rand" "crypto/rand"
"io" "io"
"math/big"
"time" "time"
) )
@ -46,6 +47,37 @@ type Config struct {
// RSABits is the number of bits in new RSA keys made with NewEntity. // RSABits is the number of bits in new RSA keys made with NewEntity.
// If zero, then 2048 bit keys are created. // If zero, then 2048 bit keys are created.
RSABits int RSABits int
// The public key algorithm to use - will always create a signing primary
// key and encryption subkey.
Algorithm PublicKeyAlgorithm
// Some known primes that are optionally prepopulated by the caller
RSAPrimes []*big.Int
// AEADConfig configures the use of the new AEAD Encrypted Data Packet,
// defined in the draft of the next version of the OpenPGP specification.
// If a non-nil AEADConfig is passed, usage of this packet is enabled. By
// default, it is disabled. See the documentation of AEADConfig for more
// configuration options related to AEAD.
// **Note: using this option may break compatibility with other OpenPGP
// implementations, as well as future versions of this library.**
AEADConfig *AEADConfig
// V5Keys configures version 5 key generation. If false, this package still
// supports version 5 keys, but produces version 4 keys.
V5Keys bool
// "The validity period of the key. This is the number of seconds after
// the key creation time that the key expires. If this is not present
// or has a value of zero, the key never expires. This is found only on
// a self-signature.""
// https://tools.ietf.org/html/rfc4880#section-5.2.3.6
KeyLifetimeSecs uint32
// "The validity period of the signature. This is the number of seconds
// after the signature creation time that the signature expires. If
// this is not present or has a value of zero, it never expires."
// https://tools.ietf.org/html/rfc4880#section-5.2.3.10
SigLifetimeSecs uint32
// SigningKeyId is used to specify the signing key to use (by Key ID).
// By default, the signing key is selected automatically, preferring
// signing subkeys if available.
SigningKeyId uint64
} }
func (c *Config) Random() io.Reader { func (c *Config) Random() io.Reader {
@ -76,6 +108,22 @@ func (c *Config) Now() time.Time {
return c.Time() return c.Time()
} }
// KeyLifetime returns the validity period of the key.
func (c *Config) KeyLifetime() uint32 {
if c == nil {
return 0
}
return c.KeyLifetimeSecs
}
// SigLifetime returns the validity period of the signature.
func (c *Config) SigLifetime() uint32 {
if c == nil {
return 0
}
return c.SigLifetimeSecs
}
func (c *Config) Compression() CompressionAlgo { func (c *Config) Compression() CompressionAlgo {
if c == nil { if c == nil {
return CompressionNone return CompressionNone
@ -89,3 +137,31 @@ func (c *Config) PasswordHashIterations() int {
} }
return c.S2KCount return c.S2KCount
} }
func (c *Config) RSAModulusBits() int {
if c == nil || c.RSABits == 0 {
return 2048
}
return c.RSABits
}
func (c *Config) PublicKeyAlgorithm() PublicKeyAlgorithm {
if c == nil || c.Algorithm == 0 {
return PubKeyAlgoRSA
}
return c.Algorithm
}
func (c *Config) AEAD() *AEADConfig {
if c == nil {
return nil
}
return c.AEADConfig
}
func (c *Config) SigningKey() uint64 {
if c == nil {
return 0
}
return c.SigningKeyId
}

View File

@ -12,8 +12,10 @@ import (
"math/big" "math/big"
"strconv" "strconv"
"golang.org/x/crypto/openpgp/elgamal" "github.com/ProtonMail/go-crypto/openpgp/ecdh"
"golang.org/x/crypto/openpgp/errors" "github.com/ProtonMail/go-crypto/openpgp/elgamal"
"github.com/ProtonMail/go-crypto/openpgp/errors"
"github.com/ProtonMail/go-crypto/openpgp/internal/encoding"
) )
const encryptedKeyVersion = 3 const encryptedKeyVersion = 3
@ -26,7 +28,7 @@ type EncryptedKey struct {
CipherFunc CipherFunction // only valid after a successful Decrypt CipherFunc CipherFunction // only valid after a successful Decrypt
Key []byte // only valid after a successful Decrypt Key []byte // only valid after a successful Decrypt
encryptedMPI1, encryptedMPI2 parsedMPI encryptedMPI1, encryptedMPI2 encoding.Field
} }
func (e *EncryptedKey) parse(r io.Reader) (err error) { func (e *EncryptedKey) parse(r io.Reader) (err error) {
@ -42,17 +44,28 @@ func (e *EncryptedKey) parse(r io.Reader) (err error) {
e.Algo = PublicKeyAlgorithm(buf[9]) e.Algo = PublicKeyAlgorithm(buf[9])
switch e.Algo { switch e.Algo {
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly:
e.encryptedMPI1.bytes, e.encryptedMPI1.bitLength, err = readMPI(r) e.encryptedMPI1 = new(encoding.MPI)
if err != nil { if _, err = e.encryptedMPI1.ReadFrom(r); err != nil {
return return
} }
case PubKeyAlgoElGamal: case PubKeyAlgoElGamal:
e.encryptedMPI1.bytes, e.encryptedMPI1.bitLength, err = readMPI(r) e.encryptedMPI1 = new(encoding.MPI)
if err != nil { if _, err = e.encryptedMPI1.ReadFrom(r); err != nil {
return return
} }
e.encryptedMPI2.bytes, e.encryptedMPI2.bitLength, err = readMPI(r)
if err != nil { e.encryptedMPI2 = new(encoding.MPI)
if _, err = e.encryptedMPI2.ReadFrom(r); err != nil {
return
}
case PubKeyAlgoECDH:
e.encryptedMPI1 = new(encoding.MPI)
if _, err = e.encryptedMPI1.ReadFrom(r); err != nil {
return
}
e.encryptedMPI2 = new(encoding.OID)
if _, err = e.encryptedMPI2.ReadFrom(r); err != nil {
return return
} }
} }
@ -72,6 +85,16 @@ func checksumKeyMaterial(key []byte) uint16 {
// private key must have been decrypted first. // private key must have been decrypted first.
// If config is nil, sensible defaults will be used. // If config is nil, sensible defaults will be used.
func (e *EncryptedKey) Decrypt(priv *PrivateKey, config *Config) error { func (e *EncryptedKey) Decrypt(priv *PrivateKey, config *Config) error {
if e.KeyId != 0 && e.KeyId != priv.KeyId {
return errors.InvalidArgumentError("cannot decrypt encrypted session key for key id " + strconv.FormatUint(e.KeyId, 16) + " with private key id " + strconv.FormatUint(priv.KeyId, 16))
}
if e.Algo != priv.PubKeyAlgo {
return errors.InvalidArgumentError("cannot decrypt encrypted session key of type " + strconv.Itoa(int(e.Algo)) + " with private key of type " + strconv.Itoa(int(priv.PubKeyAlgo)))
}
if priv.Dummy() {
return errors.ErrDummyPrivateKey("dummy key found")
}
var err error var err error
var b []byte var b []byte
@ -81,13 +104,18 @@ func (e *EncryptedKey) Decrypt(priv *PrivateKey, config *Config) error {
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly:
// Supports both *rsa.PrivateKey and crypto.Decrypter // Supports both *rsa.PrivateKey and crypto.Decrypter
k := priv.PrivateKey.(crypto.Decrypter) k := priv.PrivateKey.(crypto.Decrypter)
b, err = k.Decrypt(config.Random(), padToKeySize(k.Public().(*rsa.PublicKey), e.encryptedMPI1.bytes), nil) b, err = k.Decrypt(config.Random(), padToKeySize(k.Public().(*rsa.PublicKey), e.encryptedMPI1.Bytes()), nil)
case PubKeyAlgoElGamal: case PubKeyAlgoElGamal:
c1 := new(big.Int).SetBytes(e.encryptedMPI1.bytes) c1 := new(big.Int).SetBytes(e.encryptedMPI1.Bytes())
c2 := new(big.Int).SetBytes(e.encryptedMPI2.bytes) c2 := new(big.Int).SetBytes(e.encryptedMPI2.Bytes())
b, err = elgamal.Decrypt(priv.PrivateKey.(*elgamal.PrivateKey), c1, c2) b, err = elgamal.Decrypt(priv.PrivateKey.(*elgamal.PrivateKey), c1, c2)
case PubKeyAlgoECDH:
vsG := e.encryptedMPI1.Bytes()
m := e.encryptedMPI2.Bytes()
oid := priv.PublicKey.oid.EncodedBytes()
b, err = ecdh.Decrypt(priv.PrivateKey.(*ecdh.PrivateKey), vsG, m, oid, priv.PublicKey.Fingerprint[:])
default: default:
err = errors.InvalidArgumentError("cannot decrypted encrypted session key with private key of type " + strconv.Itoa(int(priv.PubKeyAlgo))) err = errors.InvalidArgumentError("cannot decrypt encrypted session key with private key of type " + strconv.Itoa(int(priv.PubKeyAlgo)))
} }
if err != nil { if err != nil {
@ -110,14 +138,19 @@ func (e *EncryptedKey) Serialize(w io.Writer) error {
var mpiLen int var mpiLen int
switch e.Algo { switch e.Algo {
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly:
mpiLen = 2 + len(e.encryptedMPI1.bytes) mpiLen = int(e.encryptedMPI1.EncodedLength())
case PubKeyAlgoElGamal: case PubKeyAlgoElGamal:
mpiLen = 2 + len(e.encryptedMPI1.bytes) + 2 + len(e.encryptedMPI2.bytes) mpiLen = int(e.encryptedMPI1.EncodedLength()) + int(e.encryptedMPI2.EncodedLength())
case PubKeyAlgoECDH:
mpiLen = int(e.encryptedMPI1.EncodedLength()) + int(e.encryptedMPI2.EncodedLength())
default: default:
return errors.InvalidArgumentError("don't know how to serialize encrypted key type " + strconv.Itoa(int(e.Algo))) return errors.InvalidArgumentError("don't know how to serialize encrypted key type " + strconv.Itoa(int(e.Algo)))
} }
serializeHeader(w, packetTypeEncryptedKey, 1 /* version */ +8 /* key id */ +1 /* algo */ +mpiLen) err := serializeHeader(w, packetTypeEncryptedKey, 1 /* version */ +8 /* key id */ +1 /* algo */ +mpiLen)
if err != nil {
return err
}
w.Write([]byte{encryptedKeyVersion}) w.Write([]byte{encryptedKeyVersion})
binary.Write(w, binary.BigEndian, e.KeyId) binary.Write(w, binary.BigEndian, e.KeyId)
@ -125,14 +158,23 @@ func (e *EncryptedKey) Serialize(w io.Writer) error {
switch e.Algo { switch e.Algo {
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly:
writeMPIs(w, e.encryptedMPI1) _, err := w.Write(e.encryptedMPI1.EncodedBytes())
return err
case PubKeyAlgoElGamal: case PubKeyAlgoElGamal:
writeMPIs(w, e.encryptedMPI1, e.encryptedMPI2) if _, err := w.Write(e.encryptedMPI1.EncodedBytes()); err != nil {
return err
}
_, err := w.Write(e.encryptedMPI2.EncodedBytes())
return err
case PubKeyAlgoECDH:
if _, err := w.Write(e.encryptedMPI1.EncodedBytes()); err != nil {
return err
}
_, err := w.Write(e.encryptedMPI2.EncodedBytes())
return err
default: default:
panic("internal error") panic("internal error")
} }
return nil
} }
// SerializeEncryptedKey serializes an encrypted key packet to w that contains // SerializeEncryptedKey serializes an encrypted key packet to w that contains
@ -156,6 +198,8 @@ func SerializeEncryptedKey(w io.Writer, pub *PublicKey, cipherFunc CipherFunctio
return serializeEncryptedKeyRSA(w, config.Random(), buf, pub.PublicKey.(*rsa.PublicKey), keyBlock) return serializeEncryptedKeyRSA(w, config.Random(), buf, pub.PublicKey.(*rsa.PublicKey), keyBlock)
case PubKeyAlgoElGamal: case PubKeyAlgoElGamal:
return serializeEncryptedKeyElGamal(w, config.Random(), buf, pub.PublicKey.(*elgamal.PublicKey), keyBlock) return serializeEncryptedKeyElGamal(w, config.Random(), buf, pub.PublicKey.(*elgamal.PublicKey), keyBlock)
case PubKeyAlgoECDH:
return serializeEncryptedKeyECDH(w, config.Random(), buf, pub.PublicKey.(*ecdh.PublicKey), keyBlock, pub.oid, pub.Fingerprint)
case PubKeyAlgoDSA, PubKeyAlgoRSASignOnly: case PubKeyAlgoDSA, PubKeyAlgoRSASignOnly:
return errors.InvalidArgumentError("cannot encrypt to public key of type " + strconv.Itoa(int(pub.PubKeyAlgo))) return errors.InvalidArgumentError("cannot encrypt to public key of type " + strconv.Itoa(int(pub.PubKeyAlgo)))
} }
@ -169,7 +213,8 @@ func serializeEncryptedKeyRSA(w io.Writer, rand io.Reader, header [10]byte, pub
return errors.InvalidArgumentError("RSA encryption failed: " + err.Error()) return errors.InvalidArgumentError("RSA encryption failed: " + err.Error())
} }
packetLen := 10 /* header length */ + 2 /* mpi size */ + len(cipherText) cipherMPI := encoding.NewMPI(cipherText)
packetLen := 10 /* header length */ + int(cipherMPI.EncodedLength())
err = serializeHeader(w, packetTypeEncryptedKey, packetLen) err = serializeHeader(w, packetTypeEncryptedKey, packetLen)
if err != nil { if err != nil {
@ -179,7 +224,8 @@ func serializeEncryptedKeyRSA(w io.Writer, rand io.Reader, header [10]byte, pub
if err != nil { if err != nil {
return err return err
} }
return writeMPI(w, 8*uint16(len(cipherText)), cipherText) _, err = w.Write(cipherMPI.EncodedBytes())
return err
} }
func serializeEncryptedKeyElGamal(w io.Writer, rand io.Reader, header [10]byte, pub *elgamal.PublicKey, keyBlock []byte) error { func serializeEncryptedKeyElGamal(w io.Writer, rand io.Reader, header [10]byte, pub *elgamal.PublicKey, keyBlock []byte) error {
@ -200,9 +246,37 @@ func serializeEncryptedKeyElGamal(w io.Writer, rand io.Reader, header [10]byte,
if err != nil { if err != nil {
return err return err
} }
err = writeBig(w, c1) if _, err = w.Write(new(encoding.MPI).SetBig(c1).EncodedBytes()); err != nil {
return err
}
_, err = w.Write(new(encoding.MPI).SetBig(c2).EncodedBytes())
return err
}
func serializeEncryptedKeyECDH(w io.Writer, rand io.Reader, header [10]byte, pub *ecdh.PublicKey, keyBlock []byte, oid encoding.Field, fingerprint []byte) error {
vsG, c, err := ecdh.Encrypt(rand, pub, keyBlock, oid.EncodedBytes(), fingerprint)
if err != nil {
return errors.InvalidArgumentError("ECDH encryption failed: " + err.Error())
}
g := encoding.NewMPI(vsG)
m := encoding.NewOID(c)
packetLen := 10 /* header length */
packetLen += int(g.EncodedLength()) + int(m.EncodedLength())
err = serializeHeader(w, packetTypeEncryptedKey, packetLen)
if err != nil { if err != nil {
return err return err
} }
return writeBig(w, c2)
_, err = w.Write(header[:])
if err != nil {
return err
}
if _, err = w.Write(g.EncodedBytes()); err != nil {
return err
}
_, err = w.Write(m.EncodedBytes())
return err
} }

View File

@ -11,6 +11,7 @@ import (
// LiteralData represents an encrypted file. See RFC 4880, section 5.9. // LiteralData represents an encrypted file. See RFC 4880, section 5.9.
type LiteralData struct { type LiteralData struct {
Format uint8
IsBinary bool IsBinary bool
FileName string FileName string
Time uint32 // Unix epoch time. Either creation time or modification time. 0 means undefined. Time uint32 // Unix epoch time. Either creation time or modification time. 0 means undefined.
@ -31,7 +32,8 @@ func (l *LiteralData) parse(r io.Reader) (err error) {
return return
} }
l.IsBinary = buf[0] == 'b' l.Format = buf[0]
l.IsBinary = l.Format == 'b'
fileNameLen := int(buf[1]) fileNameLen := int(buf[1])
_, err = readFull(r, buf[:fileNameLen]) _, err = readFull(r, buf[:fileNameLen])

View File

@ -85,8 +85,7 @@ type ocfbDecrypter struct {
// NewOCFBDecrypter returns a cipher.Stream which decrypts data with OpenPGP's // NewOCFBDecrypter returns a cipher.Stream which decrypts data with OpenPGP's
// cipher feedback mode using the given cipher.Block. Prefix must be the first // cipher feedback mode using the given cipher.Block. Prefix must be the first
// blockSize + 2 bytes of the ciphertext, where blockSize is the cipher.Block's // blockSize + 2 bytes of the ciphertext, where blockSize is the cipher.Block's
// block size. If an incorrect key is detected then nil is returned. On // block size. On successful exit, blockSize+2 bytes of decrypted data are written into
// successful exit, blockSize+2 bytes of decrypted data are written into
// prefix. Resync determines if the "resynchronization step" from RFC 4880, // prefix. Resync determines if the "resynchronization step" from RFC 4880,
// 13.9 step 7 is performed. Different parts of OpenPGP vary on this point. // 13.9 step 7 is performed. Different parts of OpenPGP vary on this point.
func NewOCFBDecrypter(block cipher.Block, prefix []byte, resync OCFBResyncOption) cipher.Stream { func NewOCFBDecrypter(block cipher.Block, prefix []byte, resync OCFBResyncOption) cipher.Stream {
@ -112,11 +111,6 @@ func NewOCFBDecrypter(block cipher.Block, prefix []byte, resync OCFBResyncOption
prefixCopy[blockSize] ^= x.fre[0] prefixCopy[blockSize] ^= x.fre[0]
prefixCopy[blockSize+1] ^= x.fre[1] prefixCopy[blockSize+1] ^= x.fre[1]
if prefixCopy[blockSize-2] != prefixCopy[blockSize] ||
prefixCopy[blockSize-1] != prefixCopy[blockSize+1] {
return nil
}
if resync { if resync {
block.Encrypt(x.fre, prefix[2:]) block.Encrypt(x.fre, prefix[2:])
} else { } else {

View File

@ -7,8 +7,8 @@ package packet
import ( import (
"crypto" "crypto"
"encoding/binary" "encoding/binary"
"golang.org/x/crypto/openpgp/errors" "github.com/ProtonMail/go-crypto/openpgp/errors"
"golang.org/x/crypto/openpgp/s2k" "github.com/ProtonMail/go-crypto/openpgp/s2k"
"io" "io"
"strconv" "strconv"
) )

View File

@ -9,7 +9,7 @@ import (
"io" "io"
"io/ioutil" "io/ioutil"
"golang.org/x/crypto/openpgp/errors" "github.com/ProtonMail/go-crypto/openpgp/errors"
) )
// OpaquePacket represents an OpenPGP packet as raw, unparsed data. This is // OpaquePacket represents an OpenPGP packet as raw, unparsed data. This is

View File

@ -4,20 +4,16 @@
// Package packet implements parsing and serialization of OpenPGP packets, as // Package packet implements parsing and serialization of OpenPGP packets, as
// specified in RFC 4880. // specified in RFC 4880.
package packet // import "golang.org/x/crypto/openpgp/packet" package packet // import "github.com/ProtonMail/go-crypto/openpgp/packet"
import ( import (
"bufio" "bytes"
"crypto/aes"
"crypto/cipher" "crypto/cipher"
"crypto/des"
"crypto/rsa" "crypto/rsa"
"io" "io"
"math/big"
"math/bits"
"golang.org/x/crypto/cast5" "github.com/ProtonMail/go-crypto/openpgp/errors"
"golang.org/x/crypto/openpgp/errors" "github.com/ProtonMail/go-crypto/openpgp/internal/algorithm"
) )
// readFull is the same as io.ReadFull except that reading zero bytes returns // readFull is the same as io.ReadFull except that reading zero bytes returns
@ -100,68 +96,43 @@ func (r *partialLengthReader) Read(p []byte) (n int, err error) {
// See RFC 4880, section 4.2.2.4. // See RFC 4880, section 4.2.2.4.
type partialLengthWriter struct { type partialLengthWriter struct {
w io.WriteCloser w io.WriteCloser
buf bytes.Buffer
lengthByte [1]byte lengthByte [1]byte
sentFirst bool
buf []byte
} }
// RFC 4880 4.2.2.4: the first partial length MUST be at least 512 octets long.
const minFirstPartialWrite = 512
func (w *partialLengthWriter) Write(p []byte) (n int, err error) { func (w *partialLengthWriter) Write(p []byte) (n int, err error) {
off := 0 bufLen := w.buf.Len()
if !w.sentFirst { if bufLen > 512 {
if len(w.buf) > 0 || len(p) < minFirstPartialWrite { for power := uint(30); ; power-- {
off = len(w.buf)
w.buf = append(w.buf, p...)
if len(w.buf) < minFirstPartialWrite {
return len(p), nil
}
p = w.buf
w.buf = nil
}
w.sentFirst = true
}
power := uint8(30)
for len(p) > 0 {
l := 1 << power l := 1 << power
if len(p) < l { if bufLen >= l {
power = uint8(bits.Len32(uint32(len(p)))) - 1 w.lengthByte[0] = 224 + uint8(power)
l = 1 << power
}
w.lengthByte[0] = 224 + power
_, err = w.w.Write(w.lengthByte[:]) _, err = w.w.Write(w.lengthByte[:])
if err == nil {
var m int
m, err = w.w.Write(p[:l])
n += m
}
if err != nil { if err != nil {
if n < off { return
return 0, err
} }
return n - off, err var m int
m, err = w.w.Write(w.buf.Next(l))
if err != nil {
return
} }
p = p[l:] if m != l {
return 0, io.ErrShortWrite
} }
return n - off, nil break
}
}
}
return w.buf.Write(p)
} }
func (w *partialLengthWriter) Close() error { func (w *partialLengthWriter) Close() (err error) {
if len(w.buf) > 0 { len := w.buf.Len()
// In this case we can't send a 512 byte packet. err = serializeLength(w.w, len)
// Just send what we have. if err != nil {
p := w.buf
w.sentFirst = true
w.buf = nil
if _, err := w.Write(p); err != nil {
return err return err
} }
} _, err = w.buf.WriteTo(w.w)
w.lengthByte[0] = 0
_, err := w.w.Write(w.lengthByte[:])
if err != nil { if err != nil {
return err return err
} }
@ -246,25 +217,43 @@ func readHeader(r io.Reader) (tag packetType, length int64, contents io.Reader,
// serializeHeader writes an OpenPGP packet header to w. See RFC 4880, section // serializeHeader writes an OpenPGP packet header to w. See RFC 4880, section
// 4.2. // 4.2.
func serializeHeader(w io.Writer, ptype packetType, length int) (err error) { func serializeHeader(w io.Writer, ptype packetType, length int) (err error) {
var buf [6]byte err = serializeType(w, ptype)
if err != nil {
return
}
return serializeLength(w, length)
}
// serializeType writes an OpenPGP packet type to w. See RFC 4880, section
// 4.2.
func serializeType(w io.Writer, ptype packetType) (err error) {
var buf [1]byte
buf[0] = 0x80 | 0x40 | byte(ptype)
_, err = w.Write(buf[:])
return
}
// serializeLength writes an OpenPGP packet length to w. See RFC 4880, section
// 4.2.2.
func serializeLength(w io.Writer, length int) (err error) {
var buf [5]byte
var n int var n int
buf[0] = 0x80 | 0x40 | byte(ptype)
if length < 192 { if length < 192 {
buf[1] = byte(length) buf[0] = byte(length)
n = 2 n = 1
} else if length < 8384 { } else if length < 8384 {
length -= 192 length -= 192
buf[1] = 192 + byte(length>>8) buf[0] = 192 + byte(length>>8)
buf[2] = byte(length) buf[1] = byte(length)
n = 3 n = 2
} else { } else {
buf[1] = 255 buf[0] = 255
buf[2] = byte(length >> 24) buf[1] = byte(length >> 24)
buf[3] = byte(length >> 16) buf[2] = byte(length >> 16)
buf[4] = byte(length >> 8) buf[3] = byte(length >> 8)
buf[5] = byte(length) buf[4] = byte(length)
n = 6 n = 5
} }
_, err = w.Write(buf[:n]) _, err = w.Write(buf[:n])
@ -275,9 +264,7 @@ func serializeHeader(w io.Writer, ptype packetType, length int) (err error) {
// length of the packet is unknown. It returns a io.WriteCloser which can be // length of the packet is unknown. It returns a io.WriteCloser which can be
// used to write the contents of the packet. See RFC 4880, section 4.2. // used to write the contents of the packet. See RFC 4880, section 4.2.
func serializeStreamHeader(w io.WriteCloser, ptype packetType) (out io.WriteCloser, err error) { func serializeStreamHeader(w io.WriteCloser, ptype packetType) (out io.WriteCloser, err error) {
var buf [1]byte err = serializeType(w, ptype)
buf[0] = 0x80 | 0x40 | byte(ptype)
_, err = w.Write(buf[:])
if err != nil { if err != nil {
return return
} }
@ -329,19 +316,13 @@ const (
packetTypePublicSubkey packetType = 14 packetTypePublicSubkey packetType = 14
packetTypeUserAttribute packetType = 17 packetTypeUserAttribute packetType = 17
packetTypeSymmetricallyEncryptedMDC packetType = 18 packetTypeSymmetricallyEncryptedMDC packetType = 18
packetTypeAEADEncrypted packetType = 20
) )
// peekVersion detects the version of a public key packet about to // EncryptedDataPacket holds encrypted data. It is currently implemented by
// be read. A bufio.Reader at the original position of the io.Reader // SymmetricallyEncrypted and AEADEncrypted.
// is returned. type EncryptedDataPacket interface {
func peekVersion(r io.Reader) (bufr *bufio.Reader, ver byte, err error) { Decrypt(CipherFunction, []byte) (io.ReadCloser, error)
bufr = bufio.NewReader(r)
var verBuf []byte
if verBuf, err = bufr.Peek(1); err != nil {
return
}
ver = verBuf[0]
return
} }
// Read reads a single OpenPGP packet from the given io.Reader. If there is an // Read reads a single OpenPGP packet from the given io.Reader. If there is an
@ -356,16 +337,7 @@ func Read(r io.Reader) (p Packet, err error) {
case packetTypeEncryptedKey: case packetTypeEncryptedKey:
p = new(EncryptedKey) p = new(EncryptedKey)
case packetTypeSignature: case packetTypeSignature:
var version byte
// Detect signature version
if contents, version, err = peekVersion(contents); err != nil {
return
}
if version < 4 {
p = new(SignatureV3)
} else {
p = new(Signature) p = new(Signature)
}
case packetTypeSymmetricKeyEncrypted: case packetTypeSymmetricKeyEncrypted:
p = new(SymmetricKeyEncrypted) p = new(SymmetricKeyEncrypted)
case packetTypeOnePassSignature: case packetTypeOnePassSignature:
@ -377,20 +349,12 @@ func Read(r io.Reader) (p Packet, err error) {
} }
p = pk p = pk
case packetTypePublicKey, packetTypePublicSubkey: case packetTypePublicKey, packetTypePublicSubkey:
var version byte
if contents, version, err = peekVersion(contents); err != nil {
return
}
isSubkey := tag == packetTypePublicSubkey isSubkey := tag == packetTypePublicSubkey
if version < 4 {
p = &PublicKeyV3{IsSubkey: isSubkey}
} else {
p = &PublicKey{IsSubkey: isSubkey} p = &PublicKey{IsSubkey: isSubkey}
}
case packetTypeCompressed: case packetTypeCompressed:
p = new(Compressed) p = new(Compressed)
case packetTypeSymmetricallyEncrypted: case packetTypeSymmetricallyEncrypted:
p = new(SymmetricallyEncrypted) err = errors.UnsupportedError("Symmetrically encrypted packets without MDC are not supported")
case packetTypeLiteralData: case packetTypeLiteralData:
p = new(LiteralData) p = new(LiteralData)
case packetTypeUserId: case packetTypeUserId:
@ -401,6 +365,8 @@ func Read(r io.Reader) (p Packet, err error) {
se := new(SymmetricallyEncrypted) se := new(SymmetricallyEncrypted)
se.MDC = true se.MDC = true
p = se p = se
case packetTypeAEADEncrypted:
p = new(AEADEncrypted)
default: default:
err = errors.UnknownPacketTypeError(tag) err = errors.UnknownPacketTypeError(tag)
} }
@ -418,8 +384,8 @@ func Read(r io.Reader) (p Packet, err error) {
type SignatureType uint8 type SignatureType uint8
const ( const (
SigTypeBinary SignatureType = 0 SigTypeBinary SignatureType = 0x00
SigTypeText = 1 SigTypeText = 0x01
SigTypeGenericCert = 0x10 SigTypeGenericCert = 0x10
SigTypePersonaCert = 0x11 SigTypePersonaCert = 0x11
SigTypeCasualCert = 0x12 SigTypeCasualCert = 0x12
@ -443,6 +409,8 @@ const (
// RFC 6637, Section 5. // RFC 6637, Section 5.
PubKeyAlgoECDH PublicKeyAlgorithm = 18 PubKeyAlgoECDH PublicKeyAlgorithm = 18
PubKeyAlgoECDSA PublicKeyAlgorithm = 19 PubKeyAlgoECDSA PublicKeyAlgorithm = 19
// https://www.ietf.org/archive/id/draft-koch-eddsa-for-openpgp-04.txt
PubKeyAlgoEdDSA PublicKeyAlgorithm = 22
// Deprecated in RFC 4880, Section 13.5. Use key flags instead. // Deprecated in RFC 4880, Section 13.5. Use key flags instead.
PubKeyAlgoRSAEncryptOnly PublicKeyAlgorithm = 2 PubKeyAlgoRSAEncryptOnly PublicKeyAlgorithm = 2
@ -453,7 +421,7 @@ const (
// key of the given type. // key of the given type.
func (pka PublicKeyAlgorithm) CanEncrypt() bool { func (pka PublicKeyAlgorithm) CanEncrypt() bool {
switch pka { switch pka {
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoElGamal: case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoElGamal, PubKeyAlgoECDH:
return true return true
} }
return false return false
@ -463,7 +431,7 @@ func (pka PublicKeyAlgorithm) CanEncrypt() bool {
// sign a message. // sign a message.
func (pka PublicKeyAlgorithm) CanSign() bool { func (pka PublicKeyAlgorithm) CanSign() bool {
switch pka { switch pka {
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA, PubKeyAlgoECDSA: case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA, PubKeyAlgoECDSA, PubKeyAlgoEdDSA:
return true return true
} }
return false return false
@ -471,7 +439,7 @@ func (pka PublicKeyAlgorithm) CanSign() bool {
// CipherFunction represents the different block ciphers specified for OpenPGP. See // CipherFunction represents the different block ciphers specified for OpenPGP. See
// http://www.iana.org/assignments/pgp-parameters/pgp-parameters.xhtml#pgp-parameters-13 // http://www.iana.org/assignments/pgp-parameters/pgp-parameters.xhtml#pgp-parameters-13
type CipherFunction uint8 type CipherFunction algorithm.CipherFunction
const ( const (
Cipher3DES CipherFunction = 2 Cipher3DES CipherFunction = 2
@ -483,81 +451,17 @@ const (
// KeySize returns the key size, in bytes, of cipher. // KeySize returns the key size, in bytes, of cipher.
func (cipher CipherFunction) KeySize() int { func (cipher CipherFunction) KeySize() int {
switch cipher { return algorithm.CipherFunction(cipher).KeySize()
case Cipher3DES:
return 24
case CipherCAST5:
return cast5.KeySize
case CipherAES128:
return 16
case CipherAES192:
return 24
case CipherAES256:
return 32
}
return 0
} }
// blockSize returns the block size, in bytes, of cipher. // blockSize returns the block size, in bytes, of cipher.
func (cipher CipherFunction) blockSize() int { func (cipher CipherFunction) blockSize() int {
switch cipher { return algorithm.CipherFunction(cipher).BlockSize()
case Cipher3DES:
return des.BlockSize
case CipherCAST5:
return 8
case CipherAES128, CipherAES192, CipherAES256:
return 16
}
return 0
} }
// new returns a fresh instance of the given cipher. // new returns a fresh instance of the given cipher.
func (cipher CipherFunction) new(key []byte) (block cipher.Block) { func (cipher CipherFunction) new(key []byte) (block cipher.Block) {
switch cipher { return algorithm.CipherFunction(cipher).New(key)
case Cipher3DES:
block, _ = des.NewTripleDESCipher(key)
case CipherCAST5:
block, _ = cast5.NewCipher(key)
case CipherAES128, CipherAES192, CipherAES256:
block, _ = aes.NewCipher(key)
}
return
}
// readMPI reads a big integer from r. The bit length returned is the bit
// length that was specified in r. This is preserved so that the integer can be
// reserialized exactly.
func readMPI(r io.Reader) (mpi []byte, bitLength uint16, err error) {
var buf [2]byte
_, err = readFull(r, buf[0:])
if err != nil {
return
}
bitLength = uint16(buf[0])<<8 | uint16(buf[1])
numBytes := (int(bitLength) + 7) / 8
mpi = make([]byte, numBytes)
_, err = readFull(r, mpi)
// According to RFC 4880 3.2. we should check that the MPI has no leading
// zeroes (at least when not an encrypted MPI?), but this implementation
// does generate leading zeroes, so we keep accepting them.
return
}
// writeMPI serializes a big integer to w.
func writeMPI(w io.Writer, bitLength uint16, mpiBytes []byte) (err error) {
// Note that we can produce leading zeroes, in violation of RFC 4880 3.2.
// Implementations seem to be tolerant of them, and stripping them would
// make it complex to guarantee matching re-serialization.
_, err = w.Write([]byte{byte(bitLength >> 8), byte(bitLength)})
if err == nil {
_, err = w.Write(mpiBytes)
}
return
}
// writeBig serializes a *big.Int to w.
func writeBig(w io.Writer, i *big.Int) error {
return writeMPI(w, uint16(i.BitLen()), i.Bytes())
} }
// padToKeySize left-pads a MPI with zeroes to match the length of the // padToKeySize left-pads a MPI with zeroes to match the length of the
@ -582,3 +486,37 @@ const (
CompressionZIP CompressionAlgo = 1 CompressionZIP CompressionAlgo = 1
CompressionZLIB CompressionAlgo = 2 CompressionZLIB CompressionAlgo = 2
) )
// AEADMode represents the different Authenticated Encryption with Associated
// Data specified for OpenPGP.
type AEADMode algorithm.AEADMode
const (
AEADModeEAX AEADMode = 1
AEADModeOCB AEADMode = 2
AEADModeExperimentalGCM AEADMode = 100
)
func (mode AEADMode) NonceLength() int {
return algorithm.AEADMode(mode).NonceLength()
}
func (mode AEADMode) TagLength() int {
return algorithm.AEADMode(mode).TagLength()
}
// new returns a fresh instance of the given mode.
func (mode AEADMode) new(block cipher.Block) cipher.AEAD {
return algorithm.AEADMode(mode).New(block)
}
// ReasonForRevocation represents a revocation reason code as per RFC4880
// section 5.2.3.23.
type ReasonForRevocation uint8
const (
NoReason ReasonForRevocation = 0
KeySuperseded ReasonForRevocation = 1
KeyCompromised ReasonForRevocation = 2
KeyRetired ReasonForRevocation = 3
)

View File

@ -0,0 +1,780 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package packet
import (
"bytes"
"crypto"
"crypto/cipher"
"crypto/dsa"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
"fmt"
"io"
"io/ioutil"
"math/big"
"strconv"
"time"
"github.com/ProtonMail/go-crypto/openpgp/internal/ecc"
"golang.org/x/crypto/curve25519"
"github.com/ProtonMail/go-crypto/openpgp/ecdh"
"github.com/ProtonMail/go-crypto/openpgp/elgamal"
"github.com/ProtonMail/go-crypto/openpgp/errors"
"github.com/ProtonMail/go-crypto/openpgp/internal/encoding"
"github.com/ProtonMail/go-crypto/openpgp/s2k"
"golang.org/x/crypto/ed25519"
)
// PrivateKey represents a possibly encrypted private key. See RFC 4880,
// section 5.5.3.
type PrivateKey struct {
PublicKey
Encrypted bool // if true then the private key is unavailable until Decrypt has been called.
encryptedData []byte
cipher CipherFunction
s2k func(out, in []byte)
// An *{rsa|dsa|elgamal|ecdh|ecdsa|ed25519}.PrivateKey or
// crypto.Signer/crypto.Decrypter (Decryptor RSA only).
PrivateKey interface{}
sha1Checksum bool
iv []byte
// Type of encryption of the S2K packet
// Allowed values are 0 (Not encrypted), 254 (SHA1), or
// 255 (2-byte checksum)
s2kType S2KType
// Full parameters of the S2K packet
s2kParams *s2k.Params
}
//S2KType s2k packet type
type S2KType uint8
const (
// S2KNON unencrypt
S2KNON S2KType = 0
// S2KSHA1 sha1 sum check
S2KSHA1 S2KType = 254
// S2KCHECKSUM sum check
S2KCHECKSUM S2KType = 255
)
func NewRSAPrivateKey(creationTime time.Time, priv *rsa.PrivateKey) *PrivateKey {
pk := new(PrivateKey)
pk.PublicKey = *NewRSAPublicKey(creationTime, &priv.PublicKey)
pk.PrivateKey = priv
return pk
}
func NewDSAPrivateKey(creationTime time.Time, priv *dsa.PrivateKey) *PrivateKey {
pk := new(PrivateKey)
pk.PublicKey = *NewDSAPublicKey(creationTime, &priv.PublicKey)
pk.PrivateKey = priv
return pk
}
func NewElGamalPrivateKey(creationTime time.Time, priv *elgamal.PrivateKey) *PrivateKey {
pk := new(PrivateKey)
pk.PublicKey = *NewElGamalPublicKey(creationTime, &priv.PublicKey)
pk.PrivateKey = priv
return pk
}
func NewECDSAPrivateKey(creationTime time.Time, priv *ecdsa.PrivateKey) *PrivateKey {
pk := new(PrivateKey)
pk.PublicKey = *NewECDSAPublicKey(creationTime, &priv.PublicKey)
pk.PrivateKey = priv
return pk
}
func NewEdDSAPrivateKey(creationTime time.Time, priv *ed25519.PrivateKey) *PrivateKey {
pk := new(PrivateKey)
pub := priv.Public().(ed25519.PublicKey)
pk.PublicKey = *NewEdDSAPublicKey(creationTime, &pub)
pk.PrivateKey = priv
return pk
}
func NewECDHPrivateKey(creationTime time.Time, priv *ecdh.PrivateKey) *PrivateKey {
pk := new(PrivateKey)
pk.PublicKey = *NewECDHPublicKey(creationTime, &priv.PublicKey)
pk.PrivateKey = priv
return pk
}
// NewSignerPrivateKey creates a PrivateKey from a crypto.Signer that
// implements RSA, ECDSA or EdDSA.
func NewSignerPrivateKey(creationTime time.Time, signer crypto.Signer) *PrivateKey {
pk := new(PrivateKey)
// In general, the public Keys should be used as pointers. We still
// type-switch on the values, for backwards-compatibility.
switch pubkey := signer.Public().(type) {
case *rsa.PublicKey:
pk.PublicKey = *NewRSAPublicKey(creationTime, pubkey)
case rsa.PublicKey:
pk.PublicKey = *NewRSAPublicKey(creationTime, &pubkey)
case *ecdsa.PublicKey:
pk.PublicKey = *NewECDSAPublicKey(creationTime, pubkey)
case ecdsa.PublicKey:
pk.PublicKey = *NewECDSAPublicKey(creationTime, &pubkey)
case *ed25519.PublicKey:
pk.PublicKey = *NewEdDSAPublicKey(creationTime, pubkey)
case ed25519.PublicKey:
pk.PublicKey = *NewEdDSAPublicKey(creationTime, &pubkey)
default:
panic("openpgp: unknown crypto.Signer type in NewSignerPrivateKey")
}
pk.PrivateKey = signer
return pk
}
// NewDecrypterPrivateKey creates a PrivateKey from a *{rsa|elgamal|ecdh}.PrivateKey.
func NewDecrypterPrivateKey(creationTime time.Time, decrypter interface{}) *PrivateKey {
pk := new(PrivateKey)
switch priv := decrypter.(type) {
case *rsa.PrivateKey:
pk.PublicKey = *NewRSAPublicKey(creationTime, &priv.PublicKey)
case *elgamal.PrivateKey:
pk.PublicKey = *NewElGamalPublicKey(creationTime, &priv.PublicKey)
case *ecdh.PrivateKey:
pk.PublicKey = *NewECDHPublicKey(creationTime, &priv.PublicKey)
default:
panic("openpgp: unknown decrypter type in NewDecrypterPrivateKey")
}
pk.PrivateKey = decrypter
return pk
}
func (pk *PrivateKey) parse(r io.Reader) (err error) {
err = (&pk.PublicKey).parse(r)
if err != nil {
return
}
v5 := pk.PublicKey.Version == 5
var buf [1]byte
_, err = readFull(r, buf[:])
if err != nil {
return
}
pk.s2kType = S2KType(buf[0])
var optCount [1]byte
if v5 {
if _, err = readFull(r, optCount[:]); err != nil {
return
}
}
switch pk.s2kType {
case S2KNON:
pk.s2k = nil
pk.Encrypted = false
case S2KSHA1, S2KCHECKSUM:
if v5 && pk.s2kType == S2KCHECKSUM {
return errors.StructuralError("wrong s2k identifier for version 5")
}
_, err = readFull(r, buf[:])
if err != nil {
return
}
pk.cipher = CipherFunction(buf[0])
pk.s2kParams, err = s2k.ParseIntoParams(r)
if err != nil {
return
}
if pk.s2kParams.Dummy() {
return
}
pk.s2k, err = pk.s2kParams.Function()
if err != nil {
return
}
pk.Encrypted = true
if pk.s2kType == S2KSHA1 {
pk.sha1Checksum = true
}
default:
return errors.UnsupportedError("deprecated s2k function in private key")
}
if pk.Encrypted {
blockSize := pk.cipher.blockSize()
if blockSize == 0 {
return errors.UnsupportedError("unsupported cipher in private key: " + strconv.Itoa(int(pk.cipher)))
}
pk.iv = make([]byte, blockSize)
_, err = readFull(r, pk.iv)
if err != nil {
return
}
}
var privateKeyData []byte
if v5 {
var n [4]byte /* secret material four octet count */
_, err = readFull(r, n[:])
if err != nil {
return
}
count := uint32(uint32(n[0])<<24 | uint32(n[1])<<16 | uint32(n[2])<<8 | uint32(n[3]))
if !pk.Encrypted {
count = count + 2 /* two octet checksum */
}
privateKeyData = make([]byte, count)
_, err = readFull(r, privateKeyData)
if err != nil {
return
}
} else {
privateKeyData, err = ioutil.ReadAll(r)
if err != nil {
return
}
}
if !pk.Encrypted {
return pk.parsePrivateKey(privateKeyData)
}
pk.encryptedData = privateKeyData
return
}
// Dummy returns true if the private key is a dummy key. This is a GNU extension.
func (pk *PrivateKey) Dummy() bool {
return pk.s2kParams.Dummy()
}
func mod64kHash(d []byte) uint16 {
var h uint16
for _, b := range d {
h += uint16(b)
}
return h
}
func (pk *PrivateKey) Serialize(w io.Writer) (err error) {
contents := bytes.NewBuffer(nil)
err = pk.PublicKey.serializeWithoutHeaders(contents)
if err != nil {
return
}
if _, err = contents.Write([]byte{uint8(pk.s2kType)}); err != nil {
return
}
optional := bytes.NewBuffer(nil)
if pk.Encrypted || pk.Dummy() {
optional.Write([]byte{uint8(pk.cipher)})
if err := pk.s2kParams.Serialize(optional); err != nil {
return err
}
if pk.Encrypted {
optional.Write(pk.iv)
}
}
if pk.Version == 5 {
contents.Write([]byte{uint8(optional.Len())})
}
io.Copy(contents, optional)
if !pk.Dummy() {
l := 0
var priv []byte
if !pk.Encrypted {
buf := bytes.NewBuffer(nil)
err = pk.serializePrivateKey(buf)
if err != nil {
return err
}
l = buf.Len()
if pk.sha1Checksum {
h := sha1.New()
h.Write(buf.Bytes())
buf.Write(h.Sum(nil))
} else {
checksum := mod64kHash(buf.Bytes())
buf.Write([]byte{byte(checksum >> 8), byte(checksum)})
}
priv = buf.Bytes()
} else {
priv, l = pk.encryptedData, len(pk.encryptedData)
}
if pk.Version == 5 {
contents.Write([]byte{byte(l >> 24), byte(l >> 16), byte(l >> 8), byte(l)})
}
contents.Write(priv)
}
ptype := packetTypePrivateKey
if pk.IsSubkey {
ptype = packetTypePrivateSubkey
}
err = serializeHeader(w, ptype, contents.Len())
if err != nil {
return
}
_, err = io.Copy(w, contents)
if err != nil {
return
}
return
}
func serializeRSAPrivateKey(w io.Writer, priv *rsa.PrivateKey) error {
if _, err := w.Write(new(encoding.MPI).SetBig(priv.D).EncodedBytes()); err != nil {
return err
}
if _, err := w.Write(new(encoding.MPI).SetBig(priv.Primes[1]).EncodedBytes()); err != nil {
return err
}
if _, err := w.Write(new(encoding.MPI).SetBig(priv.Primes[0]).EncodedBytes()); err != nil {
return err
}
_, err := w.Write(new(encoding.MPI).SetBig(priv.Precomputed.Qinv).EncodedBytes())
return err
}
func serializeDSAPrivateKey(w io.Writer, priv *dsa.PrivateKey) error {
_, err := w.Write(new(encoding.MPI).SetBig(priv.X).EncodedBytes())
return err
}
func serializeElGamalPrivateKey(w io.Writer, priv *elgamal.PrivateKey) error {
_, err := w.Write(new(encoding.MPI).SetBig(priv.X).EncodedBytes())
return err
}
func serializeECDSAPrivateKey(w io.Writer, priv *ecdsa.PrivateKey) error {
_, err := w.Write(new(encoding.MPI).SetBig(priv.D).EncodedBytes())
return err
}
func serializeEdDSAPrivateKey(w io.Writer, priv *ed25519.PrivateKey) error {
keySize := ed25519.PrivateKeySize - ed25519.PublicKeySize
_, err := w.Write(encoding.NewMPI((*priv)[:keySize]).EncodedBytes())
return err
}
func serializeECDHPrivateKey(w io.Writer, priv *ecdh.PrivateKey) error {
_, err := w.Write(encoding.NewMPI(priv.D).EncodedBytes())
return err
}
// Decrypt decrypts an encrypted private key using a passphrase.
func (pk *PrivateKey) Decrypt(passphrase []byte) error {
if pk.Dummy() {
return errors.ErrDummyPrivateKey("dummy key found")
}
if !pk.Encrypted {
return nil
}
key := make([]byte, pk.cipher.KeySize())
pk.s2k(key, passphrase)
block := pk.cipher.new(key)
cfb := cipher.NewCFBDecrypter(block, pk.iv)
data := make([]byte, len(pk.encryptedData))
cfb.XORKeyStream(data, pk.encryptedData)
if pk.sha1Checksum {
if len(data) < sha1.Size {
return errors.StructuralError("truncated private key data")
}
h := sha1.New()
h.Write(data[:len(data)-sha1.Size])
sum := h.Sum(nil)
if !bytes.Equal(sum, data[len(data)-sha1.Size:]) {
return errors.StructuralError("private key checksum failure")
}
data = data[:len(data)-sha1.Size]
} else {
if len(data) < 2 {
return errors.StructuralError("truncated private key data")
}
var sum uint16
for i := 0; i < len(data)-2; i++ {
sum += uint16(data[i])
}
if data[len(data)-2] != uint8(sum>>8) ||
data[len(data)-1] != uint8(sum) {
return errors.StructuralError("private key checksum failure")
}
data = data[:len(data)-2]
}
err := pk.parsePrivateKey(data)
if _, ok := err.(errors.KeyInvalidError); ok {
return errors.KeyInvalidError("invalid key parameters")
}
if err != nil {
return err
}
// Mark key as unencrypted
pk.s2kType = S2KNON
pk.s2k = nil
pk.Encrypted = false
pk.encryptedData = nil
return nil
}
// Encrypt encrypts an unencrypted private key using a passphrase.
func (pk *PrivateKey) Encrypt(passphrase []byte) error {
priv := bytes.NewBuffer(nil)
err := pk.serializePrivateKey(priv)
if err != nil {
return err
}
//Default config of private key encryption
pk.cipher = CipherAES256
s2kConfig := &s2k.Config{
S2KMode: 3, //Iterated
S2KCount: 65536,
Hash: crypto.SHA256,
}
pk.s2kParams, err = s2k.Generate(rand.Reader, s2kConfig)
if err != nil {
return err
}
privateKeyBytes := priv.Bytes()
key := make([]byte, pk.cipher.KeySize())
pk.sha1Checksum = true
pk.s2k, err = pk.s2kParams.Function()
if err != nil {
return err
}
pk.s2k(key, passphrase)
block := pk.cipher.new(key)
pk.iv = make([]byte, pk.cipher.blockSize())
_, err = rand.Read(pk.iv)
if err != nil {
return err
}
cfb := cipher.NewCFBEncrypter(block, pk.iv)
if pk.sha1Checksum {
pk.s2kType = S2KSHA1
h := sha1.New()
h.Write(privateKeyBytes)
sum := h.Sum(nil)
privateKeyBytes = append(privateKeyBytes, sum...)
} else {
pk.s2kType = S2KCHECKSUM
var sum uint16
for _, b := range privateKeyBytes {
sum += uint16(b)
}
priv.Write([]byte{uint8(sum >> 8), uint8(sum)})
}
pk.encryptedData = make([]byte, len(privateKeyBytes))
cfb.XORKeyStream(pk.encryptedData, privateKeyBytes)
pk.Encrypted = true
pk.PrivateKey = nil
return err
}
func (pk *PrivateKey) serializePrivateKey(w io.Writer) (err error) {
switch priv := pk.PrivateKey.(type) {
case *rsa.PrivateKey:
err = serializeRSAPrivateKey(w, priv)
case *dsa.PrivateKey:
err = serializeDSAPrivateKey(w, priv)
case *elgamal.PrivateKey:
err = serializeElGamalPrivateKey(w, priv)
case *ecdsa.PrivateKey:
err = serializeECDSAPrivateKey(w, priv)
case *ed25519.PrivateKey:
err = serializeEdDSAPrivateKey(w, priv)
case *ecdh.PrivateKey:
err = serializeECDHPrivateKey(w, priv)
default:
err = errors.InvalidArgumentError("unknown private key type")
}
return
}
func (pk *PrivateKey) parsePrivateKey(data []byte) (err error) {
switch pk.PublicKey.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoRSAEncryptOnly:
return pk.parseRSAPrivateKey(data)
case PubKeyAlgoDSA:
return pk.parseDSAPrivateKey(data)
case PubKeyAlgoElGamal:
return pk.parseElGamalPrivateKey(data)
case PubKeyAlgoECDSA:
return pk.parseECDSAPrivateKey(data)
case PubKeyAlgoECDH:
return pk.parseECDHPrivateKey(data)
case PubKeyAlgoEdDSA:
return pk.parseEdDSAPrivateKey(data)
}
panic("impossible")
}
func (pk *PrivateKey) parseRSAPrivateKey(data []byte) (err error) {
rsaPub := pk.PublicKey.PublicKey.(*rsa.PublicKey)
rsaPriv := new(rsa.PrivateKey)
rsaPriv.PublicKey = *rsaPub
buf := bytes.NewBuffer(data)
d := new(encoding.MPI)
if _, err := d.ReadFrom(buf); err != nil {
return err
}
p := new(encoding.MPI)
if _, err := p.ReadFrom(buf); err != nil {
return err
}
q := new(encoding.MPI)
if _, err := q.ReadFrom(buf); err != nil {
return err
}
rsaPriv.D = new(big.Int).SetBytes(d.Bytes())
rsaPriv.Primes = make([]*big.Int, 2)
rsaPriv.Primes[0] = new(big.Int).SetBytes(p.Bytes())
rsaPriv.Primes[1] = new(big.Int).SetBytes(q.Bytes())
if err := rsaPriv.Validate(); err != nil {
return errors.KeyInvalidError(err.Error())
}
rsaPriv.Precompute()
pk.PrivateKey = rsaPriv
return nil
}
func (pk *PrivateKey) parseDSAPrivateKey(data []byte) (err error) {
dsaPub := pk.PublicKey.PublicKey.(*dsa.PublicKey)
dsaPriv := new(dsa.PrivateKey)
dsaPriv.PublicKey = *dsaPub
buf := bytes.NewBuffer(data)
x := new(encoding.MPI)
if _, err := x.ReadFrom(buf); err != nil {
return err
}
dsaPriv.X = new(big.Int).SetBytes(x.Bytes())
if err := validateDSAParameters(dsaPriv); err != nil {
return err
}
pk.PrivateKey = dsaPriv
return nil
}
func (pk *PrivateKey) parseElGamalPrivateKey(data []byte) (err error) {
pub := pk.PublicKey.PublicKey.(*elgamal.PublicKey)
priv := new(elgamal.PrivateKey)
priv.PublicKey = *pub
buf := bytes.NewBuffer(data)
x := new(encoding.MPI)
if _, err := x.ReadFrom(buf); err != nil {
return err
}
priv.X = new(big.Int).SetBytes(x.Bytes())
if err := validateElGamalParameters(priv); err != nil {
return err
}
pk.PrivateKey = priv
return nil
}
func (pk *PrivateKey) parseECDSAPrivateKey(data []byte) (err error) {
ecdsaPub := pk.PublicKey.PublicKey.(*ecdsa.PublicKey)
ecdsaPriv := new(ecdsa.PrivateKey)
ecdsaPriv.PublicKey = *ecdsaPub
buf := bytes.NewBuffer(data)
d := new(encoding.MPI)
if _, err := d.ReadFrom(buf); err != nil {
return err
}
ecdsaPriv.D = new(big.Int).SetBytes(d.Bytes())
if err := validateECDSAParameters(ecdsaPriv); err != nil {
return err
}
pk.PrivateKey = ecdsaPriv
return nil
}
func (pk *PrivateKey) parseECDHPrivateKey(data []byte) (err error) {
ecdhPub := pk.PublicKey.PublicKey.(*ecdh.PublicKey)
ecdhPriv := new(ecdh.PrivateKey)
ecdhPriv.PublicKey = *ecdhPub
buf := bytes.NewBuffer(data)
d := new(encoding.MPI)
if _, err := d.ReadFrom(buf); err != nil {
return err
}
ecdhPriv.D = d.Bytes()
if err := validateECDHParameters(ecdhPriv); err != nil {
return err
}
pk.PrivateKey = ecdhPriv
return nil
}
func (pk *PrivateKey) parseEdDSAPrivateKey(data []byte) (err error) {
eddsaPub := pk.PublicKey.PublicKey.(*ed25519.PublicKey)
eddsaPriv := make(ed25519.PrivateKey, ed25519.PrivateKeySize)
buf := bytes.NewBuffer(data)
d := new(encoding.MPI)
if _, err := d.ReadFrom(buf); err != nil {
return err
}
priv := d.Bytes()
copy(eddsaPriv[32-len(priv):32], priv)
copy(eddsaPriv[32:], (*eddsaPub)[:])
if err := validateEdDSAParameters(&eddsaPriv); err != nil {
return err
}
pk.PrivateKey = &eddsaPriv
return nil
}
func validateECDSAParameters(priv *ecdsa.PrivateKey) error {
return validateCommonECC(priv.Curve, priv.D.Bytes(), priv.X, priv.Y)
}
func validateECDHParameters(priv *ecdh.PrivateKey) error {
if priv.CurveType != ecc.Curve25519 {
return validateCommonECC(priv.Curve, priv.D, priv.X, priv.Y)
}
// Handle Curve25519
Q := priv.X.Bytes()[1:]
var d [32]byte
// Copy reversed d
l := len(priv.D)
for i := 0; i < l; i++ {
d[i] = priv.D[l-i-1]
}
var expectedQ [32]byte
curve25519.ScalarBaseMult(&expectedQ, &d)
if !bytes.Equal(Q, expectedQ[:]) {
return errors.KeyInvalidError("ECDH curve25519: invalid point")
}
return nil
}
func validateCommonECC(curve elliptic.Curve, d []byte, X, Y *big.Int) error {
// the public point should not be at infinity (0,0)
zero := new(big.Int)
if X.Cmp(zero) == 0 && Y.Cmp(zero) == 0 {
return errors.KeyInvalidError(fmt.Sprintf("ecc (%s): infinity point", curve.Params().Name))
}
// re-derive the public point Q' = (X,Y) = dG
// to compare to declared Q in public key
expectedX, expectedY := curve.ScalarBaseMult(d)
if X.Cmp(expectedX) != 0 || Y.Cmp(expectedY) != 0 {
return errors.KeyInvalidError(fmt.Sprintf("ecc (%s): invalid point", curve.Params().Name))
}
return nil
}
func validateEdDSAParameters(priv *ed25519.PrivateKey) error {
// In EdDSA, the serialized public point is stored as part of private key (together with the seed),
// hence we can re-derive the key from the seed
seed := priv.Seed()
expectedPriv := ed25519.NewKeyFromSeed(seed)
if !bytes.Equal(*priv, expectedPriv) {
return errors.KeyInvalidError("eddsa: invalid point")
}
return nil
}
func validateDSAParameters(priv *dsa.PrivateKey) error {
p := priv.P // group prime
q := priv.Q // subgroup order
g := priv.G // g has order q mod p
x := priv.X // secret
y := priv.Y // y == g**x mod p
one := big.NewInt(1)
// expect g, y >= 2 and g < p
if g.Cmp(one) <= 0 || y.Cmp(one) <= 0 || g.Cmp(p) > 0 {
return errors.KeyInvalidError("dsa: invalid group")
}
// expect p > q
if p.Cmp(q) <= 0 {
return errors.KeyInvalidError("dsa: invalid group prime")
}
// q should be large enough and divide p-1
pSub1 := new(big.Int).Sub(p, one)
if q.BitLen() < 150 || new(big.Int).Mod(pSub1, q).Cmp(big.NewInt(0)) != 0 {
return errors.KeyInvalidError("dsa: invalid order")
}
// confirm that g has order q mod p
if !q.ProbablyPrime(32) || new(big.Int).Exp(g, q, p).Cmp(one) != 0 {
return errors.KeyInvalidError("dsa: invalid order")
}
// check y
if new(big.Int).Exp(g, x, p).Cmp(y) != 0 {
return errors.KeyInvalidError("dsa: mismatching values")
}
return nil
}
func validateElGamalParameters(priv *elgamal.PrivateKey) error {
p := priv.P // group prime
g := priv.G // g has order p-1 mod p
x := priv.X // secret
y := priv.Y // y == g**x mod p
one := big.NewInt(1)
// Expect g, y >= 2 and g < p
if g.Cmp(one) <= 0 || y.Cmp(one) <= 0 || g.Cmp(p) > 0 {
return errors.KeyInvalidError("elgamal: invalid group")
}
if p.BitLen() < 1024 {
return errors.KeyInvalidError("elgamal: group order too small")
}
pSub1 := new(big.Int).Sub(p, one)
if new(big.Int).Exp(g, pSub1, p).Cmp(one) != 0 {
return errors.KeyInvalidError("elgamal: invalid group")
}
// Since p-1 is not prime, g might have a smaller order that divides p-1.
// We cannot confirm the exact order of g, but we make sure it is not too small.
gExpI := new(big.Int).Set(g)
i := 1
threshold := 2 << 17 // we want order > threshold
for i < threshold {
i++ // we check every order to make sure key validation is not easily bypassed by guessing y'
gExpI.Mod(new(big.Int).Mul(gExpI, g), p)
if gExpI.Cmp(one) == 0 {
return errors.KeyInvalidError("elgamal: order too small")
}
}
// Check y
if new(big.Int).Exp(g, x, p).Cmp(y) != 0 {
return errors.KeyInvalidError("elgamal: mismatching values")
}
return nil
}

View File

@ -0,0 +1,12 @@
package packet
// Generated with `gpg --export-secret-keys "Test Key 2"`
const privKeyRSAHex = "9501fe044cc349a8010400b70ca0010e98c090008d45d1ee8f9113bd5861fd57b88bacb7c68658747663f1e1a3b5a98f32fda6472373c024b97359cd2efc88ff60f77751adfbf6af5e615e6a1408cfad8bf0cea30b0d5f53aa27ad59089ba9b15b7ebc2777a25d7b436144027e3bcd203909f147d0e332b240cf63d3395f5dfe0df0a6c04e8655af7eacdf0011010001fe0303024a252e7d475fd445607de39a265472aa74a9320ba2dac395faa687e9e0336aeb7e9a7397e511b5afd9dc84557c80ac0f3d4d7bfec5ae16f20d41c8c84a04552a33870b930420e230e179564f6d19bb153145e76c33ae993886c388832b0fa042ddda7f133924f3854481533e0ede31d51278c0519b29abc3bf53da673e13e3e1214b52413d179d7f66deee35cac8eacb060f78379d70ef4af8607e68131ff529439668fc39c9ce6dfef8a5ac234d234802cbfb749a26107db26406213ae5c06d4673253a3cbee1fcbae58d6ab77e38d6e2c0e7c6317c48e054edadb5a40d0d48acb44643d998139a8a66bb820be1f3f80185bc777d14b5954b60effe2448a036d565c6bc0b915fcea518acdd20ab07bc1529f561c58cd044f723109b93f6fd99f876ff891d64306b5d08f48bab59f38695e9109c4dec34013ba3153488ce070268381ba923ee1eb77125b36afcb4347ec3478c8f2735b06ef17351d872e577fa95d0c397c88c71b59629a36aec"
// Generated by `gpg --export-secret-keys` followed by a manual extraction of
// the ElGamal subkey from the packets.
const privKeyElGamalHex = "9d0157044df9ee1a100400eb8e136a58ec39b582629cdadf830bc64e0a94ed8103ca8bb247b27b11b46d1d25297ef4bcc3071785ba0c0bedfe89eabc5287fcc0edf81ab5896c1c8e4b20d27d79813c7aede75320b33eaeeaa586edc00fd1036c10133e6ba0ff277245d0d59d04b2b3421b7244aca5f4a8d870c6f1c1fbff9e1c26699a860b9504f35ca1d700030503fd1ededd3b840795be6d9ccbe3c51ee42e2f39233c432b831ddd9c4e72b7025a819317e47bf94f9ee316d7273b05d5fcf2999c3a681f519b1234bbfa6d359b4752bd9c3f77d6b6456cde152464763414ca130f4e91d91041432f90620fec0e6d6b5116076c2985d5aeaae13be492b9b329efcaf7ee25120159a0a30cd976b42d7afe030302dae7eb80db744d4960c4df930d57e87fe81412eaace9f900e6c839817a614ddb75ba6603b9417c33ea7b6c93967dfa2bcff3fa3c74a5ce2c962db65b03aece14c96cbd0038fc"
// pkcs1PrivKeyHex is a PKCS#1, RSA private key.
// Generated by `openssl genrsa 1024 | openssl rsa -outform DER | xxd -p`
const pkcs1PrivKeyHex = "3082025d02010002818100e98edfa1c3b35884a54d0b36a6a603b0290fa85e49e30fa23fc94fef9c6790bc4849928607aa48d809da326fb42a969d06ad756b98b9c1a90f5d4a2b6d0ac05953c97f4da3120164a21a679793ce181c906dc01d235cc085ddcdf6ea06c389b6ab8885dfd685959e693138856a68a7e5db263337ff82a088d583a897cf2d59e9020301000102818100b6d5c9eb70b02d5369b3ee5b520a14490b5bde8a317d36f7e4c74b7460141311d1e5067735f8f01d6f5908b2b96fbd881f7a1ab9a84d82753e39e19e2d36856be960d05ac9ef8e8782ea1b6d65aee28fdfe1d61451e8cff0adfe84322f12cf455028b581cf60eb9e0e140ba5d21aeba6c2634d7c65318b9a665fc01c3191ca21024100fa5e818da3705b0fa33278bb28d4b6f6050388af2d4b75ec9375dd91ccf2e7d7068086a8b82a8f6282e4fbbdb8a7f2622eb97295249d87acea7f5f816f54d347024100eecf9406d7dc49cdfb95ab1eff4064de84c7a30f64b2798936a0d2018ba9eb52e4b636f82e96c49cc63b80b675e91e40d1b2e4017d4b9adaf33ab3d9cf1c214f024100c173704ace742c082323066226a4655226819a85304c542b9dacbeacbf5d1881ee863485fcf6f59f3a604f9b42289282067447f2b13dfeed3eab7851fc81e0550240741fc41f3fc002b382eed8730e33c5d8de40256e4accee846667f536832f711ab1d4590e7db91a8a116ac5bff3be13d3f9243ff2e976662aa9b395d907f8e9c9024046a5696c9ef882363e06c9fa4e2f5b580906452befba03f4a99d0f873697ef1f851d2226ca7934b30b7c3e80cb634a67172bbbf4781735fe3e09263e2dd723e7"

View File

@ -0,0 +1,825 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package packet
import (
"crypto"
"crypto/dsa"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rsa"
"crypto/sha1"
"crypto/sha256"
_ "crypto/sha512"
"encoding/binary"
"fmt"
"hash"
"io"
"math/big"
"strconv"
"time"
"github.com/ProtonMail/go-crypto/openpgp/ecdh"
"github.com/ProtonMail/go-crypto/openpgp/elgamal"
"github.com/ProtonMail/go-crypto/openpgp/errors"
"github.com/ProtonMail/go-crypto/openpgp/internal/algorithm"
"github.com/ProtonMail/go-crypto/openpgp/internal/ecc"
"github.com/ProtonMail/go-crypto/openpgp/internal/encoding"
"golang.org/x/crypto/ed25519"
)
type kdfHashFunction byte
type kdfAlgorithm byte
// PublicKey represents an OpenPGP public key. See RFC 4880, section 5.5.2.
type PublicKey struct {
Version int
CreationTime time.Time
PubKeyAlgo PublicKeyAlgorithm
PublicKey interface{} // *rsa.PublicKey, *dsa.PublicKey, *ecdsa.PublicKey or *eddsa.PublicKey
Fingerprint []byte
KeyId uint64
IsSubkey bool
// RFC 4880 fields
n, e, p, q, g, y encoding.Field
// RFC 6637 fields
// oid contains the OID byte sequence identifying the elliptic curve used
oid encoding.Field
// kdf stores key derivation function parameters
// used for ECDH encryption. See RFC 6637, Section 9.
kdf encoding.Field
}
// UpgradeToV5 updates the version of the key to v5, and updates all necessary
// fields.
func (pk *PublicKey) UpgradeToV5() {
pk.Version = 5
pk.setFingerprintAndKeyId()
}
// signingKey provides a convenient abstraction over signature verification
// for v3 and v4 public keys.
type signingKey interface {
SerializeForHash(io.Writer) error
SerializeSignaturePrefix(io.Writer)
serializeWithoutHeaders(io.Writer) error
}
// NewRSAPublicKey returns a PublicKey that wraps the given rsa.PublicKey.
func NewRSAPublicKey(creationTime time.Time, pub *rsa.PublicKey) *PublicKey {
pk := &PublicKey{
Version: 4,
CreationTime: creationTime,
PubKeyAlgo: PubKeyAlgoRSA,
PublicKey: pub,
n: new(encoding.MPI).SetBig(pub.N),
e: new(encoding.MPI).SetBig(big.NewInt(int64(pub.E))),
}
pk.setFingerprintAndKeyId()
return pk
}
// NewDSAPublicKey returns a PublicKey that wraps the given dsa.PublicKey.
func NewDSAPublicKey(creationTime time.Time, pub *dsa.PublicKey) *PublicKey {
pk := &PublicKey{
Version: 4,
CreationTime: creationTime,
PubKeyAlgo: PubKeyAlgoDSA,
PublicKey: pub,
p: new(encoding.MPI).SetBig(pub.P),
q: new(encoding.MPI).SetBig(pub.Q),
g: new(encoding.MPI).SetBig(pub.G),
y: new(encoding.MPI).SetBig(pub.Y),
}
pk.setFingerprintAndKeyId()
return pk
}
// NewElGamalPublicKey returns a PublicKey that wraps the given elgamal.PublicKey.
func NewElGamalPublicKey(creationTime time.Time, pub *elgamal.PublicKey) *PublicKey {
pk := &PublicKey{
Version: 4,
CreationTime: creationTime,
PubKeyAlgo: PubKeyAlgoElGamal,
PublicKey: pub,
p: new(encoding.MPI).SetBig(pub.P),
g: new(encoding.MPI).SetBig(pub.G),
y: new(encoding.MPI).SetBig(pub.Y),
}
pk.setFingerprintAndKeyId()
return pk
}
func NewECDSAPublicKey(creationTime time.Time, pub *ecdsa.PublicKey) *PublicKey {
pk := &PublicKey{
Version: 4,
CreationTime: creationTime,
PubKeyAlgo: PubKeyAlgoECDSA,
PublicKey: pub,
p: encoding.NewMPI(elliptic.Marshal(pub.Curve, pub.X, pub.Y)),
}
curveInfo := ecc.FindByCurve(pub.Curve)
if curveInfo == nil {
panic("unknown elliptic curve")
}
pk.oid = curveInfo.Oid
pk.setFingerprintAndKeyId()
return pk
}
func NewECDHPublicKey(creationTime time.Time, pub *ecdh.PublicKey) *PublicKey {
var pk *PublicKey
var curveInfo *ecc.CurveInfo
var kdf = encoding.NewOID([]byte{0x1, pub.Hash.Id(), pub.Cipher.Id()})
if pub.CurveType == ecc.Curve25519 {
pk = &PublicKey{
Version: 4,
CreationTime: creationTime,
PubKeyAlgo: PubKeyAlgoECDH,
PublicKey: pub,
p: encoding.NewMPI(pub.X.Bytes()),
kdf: kdf,
}
curveInfo = ecc.FindByName("Curve25519")
} else {
pk = &PublicKey{
Version: 4,
CreationTime: creationTime,
PubKeyAlgo: PubKeyAlgoECDH,
PublicKey: pub,
p: encoding.NewMPI(elliptic.Marshal(pub.Curve, pub.X, pub.Y)),
kdf: kdf,
}
curveInfo = ecc.FindByCurve(pub.Curve)
}
if curveInfo == nil {
panic("unknown elliptic curve")
}
pk.oid = curveInfo.Oid
pk.setFingerprintAndKeyId()
return pk
}
func NewEdDSAPublicKey(creationTime time.Time, pub *ed25519.PublicKey) *PublicKey {
curveInfo := ecc.FindByName("Ed25519")
pk := &PublicKey{
Version: 4,
CreationTime: creationTime,
PubKeyAlgo: PubKeyAlgoEdDSA,
PublicKey: pub,
oid: curveInfo.Oid,
// Native point format, see draft-koch-eddsa-for-openpgp-04, Appendix B
p: encoding.NewMPI(append([]byte{0x40}, *pub...)),
}
pk.setFingerprintAndKeyId()
return pk
}
func (pk *PublicKey) parse(r io.Reader) (err error) {
// RFC 4880, section 5.5.2
var buf [6]byte
_, err = readFull(r, buf[:])
if err != nil {
return
}
if buf[0] != 4 && buf[0] != 5 {
return errors.UnsupportedError("public key version " + strconv.Itoa(int(buf[0])))
}
pk.Version = int(buf[0])
if pk.Version == 5 {
var n [4]byte
_, err = readFull(r, n[:])
if err != nil {
return
}
}
pk.CreationTime = time.Unix(int64(uint32(buf[1])<<24|uint32(buf[2])<<16|uint32(buf[3])<<8|uint32(buf[4])), 0)
pk.PubKeyAlgo = PublicKeyAlgorithm(buf[5])
switch pk.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly:
err = pk.parseRSA(r)
case PubKeyAlgoDSA:
err = pk.parseDSA(r)
case PubKeyAlgoElGamal:
err = pk.parseElGamal(r)
case PubKeyAlgoECDSA:
err = pk.parseECDSA(r)
case PubKeyAlgoECDH:
err = pk.parseECDH(r)
case PubKeyAlgoEdDSA:
err = pk.parseEdDSA(r)
default:
err = errors.UnsupportedError("public key type: " + strconv.Itoa(int(pk.PubKeyAlgo)))
}
if err != nil {
return
}
pk.setFingerprintAndKeyId()
return
}
func (pk *PublicKey) setFingerprintAndKeyId() {
// RFC 4880, section 12.2
if pk.Version == 5 {
fingerprint := sha256.New()
pk.SerializeForHash(fingerprint)
pk.Fingerprint = make([]byte, 32)
copy(pk.Fingerprint, fingerprint.Sum(nil))
pk.KeyId = binary.BigEndian.Uint64(pk.Fingerprint[:8])
} else {
fingerprint := sha1.New()
pk.SerializeForHash(fingerprint)
pk.Fingerprint = make([]byte, 20)
copy(pk.Fingerprint, fingerprint.Sum(nil))
pk.KeyId = binary.BigEndian.Uint64(pk.Fingerprint[12:20])
}
}
// parseRSA parses RSA public key material from the given Reader. See RFC 4880,
// section 5.5.2.
func (pk *PublicKey) parseRSA(r io.Reader) (err error) {
pk.n = new(encoding.MPI)
if _, err = pk.n.ReadFrom(r); err != nil {
return
}
pk.e = new(encoding.MPI)
if _, err = pk.e.ReadFrom(r); err != nil {
return
}
if len(pk.e.Bytes()) > 3 {
err = errors.UnsupportedError("large public exponent")
return
}
rsa := &rsa.PublicKey{
N: new(big.Int).SetBytes(pk.n.Bytes()),
E: 0,
}
for i := 0; i < len(pk.e.Bytes()); i++ {
rsa.E <<= 8
rsa.E |= int(pk.e.Bytes()[i])
}
pk.PublicKey = rsa
return
}
// parseDSA parses DSA public key material from the given Reader. See RFC 4880,
// section 5.5.2.
func (pk *PublicKey) parseDSA(r io.Reader) (err error) {
pk.p = new(encoding.MPI)
if _, err = pk.p.ReadFrom(r); err != nil {
return
}
pk.q = new(encoding.MPI)
if _, err = pk.q.ReadFrom(r); err != nil {
return
}
pk.g = new(encoding.MPI)
if _, err = pk.g.ReadFrom(r); err != nil {
return
}
pk.y = new(encoding.MPI)
if _, err = pk.y.ReadFrom(r); err != nil {
return
}
dsa := new(dsa.PublicKey)
dsa.P = new(big.Int).SetBytes(pk.p.Bytes())
dsa.Q = new(big.Int).SetBytes(pk.q.Bytes())
dsa.G = new(big.Int).SetBytes(pk.g.Bytes())
dsa.Y = new(big.Int).SetBytes(pk.y.Bytes())
pk.PublicKey = dsa
return
}
// parseElGamal parses ElGamal public key material from the given Reader. See
// RFC 4880, section 5.5.2.
func (pk *PublicKey) parseElGamal(r io.Reader) (err error) {
pk.p = new(encoding.MPI)
if _, err = pk.p.ReadFrom(r); err != nil {
return
}
pk.g = new(encoding.MPI)
if _, err = pk.g.ReadFrom(r); err != nil {
return
}
pk.y = new(encoding.MPI)
if _, err = pk.y.ReadFrom(r); err != nil {
return
}
elgamal := new(elgamal.PublicKey)
elgamal.P = new(big.Int).SetBytes(pk.p.Bytes())
elgamal.G = new(big.Int).SetBytes(pk.g.Bytes())
elgamal.Y = new(big.Int).SetBytes(pk.y.Bytes())
pk.PublicKey = elgamal
return
}
// parseECDSA parses ECDSA public key material from the given Reader. See
// RFC 6637, Section 9.
func (pk *PublicKey) parseECDSA(r io.Reader) (err error) {
pk.oid = new(encoding.OID)
if _, err = pk.oid.ReadFrom(r); err != nil {
return
}
pk.p = new(encoding.MPI)
if _, err = pk.p.ReadFrom(r); err != nil {
return
}
var c elliptic.Curve
curveInfo := ecc.FindByOid(pk.oid)
if curveInfo == nil || curveInfo.SigAlgorithm != ecc.ECDSA {
return errors.UnsupportedError(fmt.Sprintf("unsupported oid: %x", pk.oid))
}
c = curveInfo.Curve
x, y := elliptic.Unmarshal(c, pk.p.Bytes())
if x == nil {
return errors.UnsupportedError("failed to parse EC point")
}
pk.PublicKey = &ecdsa.PublicKey{Curve: c, X: x, Y: y}
return
}
// parseECDH parses ECDH public key material from the given Reader. See
// RFC 6637, Section 9.
func (pk *PublicKey) parseECDH(r io.Reader) (err error) {
pk.oid = new(encoding.OID)
if _, err = pk.oid.ReadFrom(r); err != nil {
return
}
pk.p = new(encoding.MPI)
if _, err = pk.p.ReadFrom(r); err != nil {
return
}
pk.kdf = new(encoding.OID)
if _, err = pk.kdf.ReadFrom(r); err != nil {
return
}
curveInfo := ecc.FindByOid(pk.oid)
if curveInfo == nil {
return errors.UnsupportedError(fmt.Sprintf("unsupported oid: %x", pk.oid))
}
c := curveInfo.Curve
cType := curveInfo.CurveType
var x, y *big.Int
if cType == ecc.Curve25519 {
x = new(big.Int)
x.SetBytes(pk.p.Bytes())
} else {
x, y = elliptic.Unmarshal(c, pk.p.Bytes())
}
if x == nil {
return errors.UnsupportedError("failed to parse EC point")
}
if kdfLen := len(pk.kdf.Bytes()); kdfLen < 3 {
return errors.UnsupportedError("unsupported ECDH KDF length: " + strconv.Itoa(kdfLen))
}
if reserved := pk.kdf.Bytes()[0]; reserved != 0x01 {
return errors.UnsupportedError("unsupported KDF reserved field: " + strconv.Itoa(int(reserved)))
}
kdfHash, ok := algorithm.HashById[pk.kdf.Bytes()[1]]
if !ok {
return errors.UnsupportedError("unsupported ECDH KDF hash: " + strconv.Itoa(int(pk.kdf.Bytes()[1])))
}
kdfCipher, ok := algorithm.CipherById[pk.kdf.Bytes()[2]]
if !ok {
return errors.UnsupportedError("unsupported ECDH KDF cipher: " + strconv.Itoa(int(pk.kdf.Bytes()[2])))
}
pk.PublicKey = &ecdh.PublicKey{
CurveType: cType,
Curve: c,
X: x,
Y: y,
KDF: ecdh.KDF{
Hash: kdfHash,
Cipher: kdfCipher,
},
}
return
}
func (pk *PublicKey) parseEdDSA(r io.Reader) (err error) {
pk.oid = new(encoding.OID)
if _, err = pk.oid.ReadFrom(r); err != nil {
return
}
curveInfo := ecc.FindByOid(pk.oid)
if curveInfo == nil || curveInfo.SigAlgorithm != ecc.EdDSA {
return errors.UnsupportedError(fmt.Sprintf("unsupported oid: %x", pk.oid))
}
pk.p = new(encoding.MPI)
if _, err = pk.p.ReadFrom(r); err != nil {
return
}
eddsa := make(ed25519.PublicKey, ed25519.PublicKeySize)
switch flag := pk.p.Bytes()[0]; flag {
case 0x04:
// TODO: see _grcy_ecc_eddsa_ensure_compact in grcypt
return errors.UnsupportedError("unsupported EdDSA compression: " + strconv.Itoa(int(flag)))
case 0x40:
copy(eddsa[:], pk.p.Bytes()[1:])
default:
return errors.UnsupportedError("unsupported EdDSA compression: " + strconv.Itoa(int(flag)))
}
pk.PublicKey = &eddsa
return
}
// SerializeForHash serializes the PublicKey to w with the special packet
// header format needed for hashing.
func (pk *PublicKey) SerializeForHash(w io.Writer) error {
pk.SerializeSignaturePrefix(w)
return pk.serializeWithoutHeaders(w)
}
// SerializeSignaturePrefix writes the prefix for this public key to the given Writer.
// The prefix is used when calculating a signature over this public key. See
// RFC 4880, section 5.2.4.
func (pk *PublicKey) SerializeSignaturePrefix(w io.Writer) {
var pLength = pk.algorithmSpecificByteCount()
if pk.Version == 5 {
pLength += 10 // version, timestamp (4), algorithm, key octet count (4).
w.Write([]byte{
0x9A,
byte(pLength >> 24),
byte(pLength >> 16),
byte(pLength >> 8),
byte(pLength),
})
return
}
pLength += 6
w.Write([]byte{0x99, byte(pLength >> 8), byte(pLength)})
}
func (pk *PublicKey) Serialize(w io.Writer) (err error) {
length := 6 // 6 byte header
length += pk.algorithmSpecificByteCount()
if pk.Version == 5 {
length += 4 // octet key count
}
packetType := packetTypePublicKey
if pk.IsSubkey {
packetType = packetTypePublicSubkey
}
err = serializeHeader(w, packetType, length)
if err != nil {
return
}
return pk.serializeWithoutHeaders(w)
}
func (pk *PublicKey) algorithmSpecificByteCount() int {
length := 0
switch pk.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly:
length += int(pk.n.EncodedLength())
length += int(pk.e.EncodedLength())
case PubKeyAlgoDSA:
length += int(pk.p.EncodedLength())
length += int(pk.q.EncodedLength())
length += int(pk.g.EncodedLength())
length += int(pk.y.EncodedLength())
case PubKeyAlgoElGamal:
length += int(pk.p.EncodedLength())
length += int(pk.g.EncodedLength())
length += int(pk.y.EncodedLength())
case PubKeyAlgoECDSA:
length += int(pk.oid.EncodedLength())
length += int(pk.p.EncodedLength())
case PubKeyAlgoECDH:
length += int(pk.oid.EncodedLength())
length += int(pk.p.EncodedLength())
length += int(pk.kdf.EncodedLength())
case PubKeyAlgoEdDSA:
length += int(pk.oid.EncodedLength())
length += int(pk.p.EncodedLength())
default:
panic("unknown public key algorithm")
}
return length
}
// serializeWithoutHeaders marshals the PublicKey to w in the form of an
// OpenPGP public key packet, not including the packet header.
func (pk *PublicKey) serializeWithoutHeaders(w io.Writer) (err error) {
t := uint32(pk.CreationTime.Unix())
if _, err = w.Write([]byte{
byte(pk.Version),
byte(t >> 24), byte(t >> 16), byte(t >> 8), byte(t),
byte(pk.PubKeyAlgo),
}); err != nil {
return
}
if pk.Version == 5 {
n := pk.algorithmSpecificByteCount()
if _, err = w.Write([]byte{
byte(n >> 24), byte(n >> 16), byte(n >> 8), byte(n),
}); err != nil {
return
}
}
switch pk.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly:
if _, err = w.Write(pk.n.EncodedBytes()); err != nil {
return
}
_, err = w.Write(pk.e.EncodedBytes())
return
case PubKeyAlgoDSA:
if _, err = w.Write(pk.p.EncodedBytes()); err != nil {
return
}
if _, err = w.Write(pk.q.EncodedBytes()); err != nil {
return
}
if _, err = w.Write(pk.g.EncodedBytes()); err != nil {
return
}
_, err = w.Write(pk.y.EncodedBytes())
return
case PubKeyAlgoElGamal:
if _, err = w.Write(pk.p.EncodedBytes()); err != nil {
return
}
if _, err = w.Write(pk.g.EncodedBytes()); err != nil {
return
}
_, err = w.Write(pk.y.EncodedBytes())
return
case PubKeyAlgoECDSA:
if _, err = w.Write(pk.oid.EncodedBytes()); err != nil {
return
}
_, err = w.Write(pk.p.EncodedBytes())
return
case PubKeyAlgoECDH:
if _, err = w.Write(pk.oid.EncodedBytes()); err != nil {
return
}
if _, err = w.Write(pk.p.EncodedBytes()); err != nil {
return
}
_, err = w.Write(pk.kdf.EncodedBytes())
return
case PubKeyAlgoEdDSA:
if _, err = w.Write(pk.oid.EncodedBytes()); err != nil {
return
}
_, err = w.Write(pk.p.EncodedBytes())
return
}
return errors.InvalidArgumentError("bad public-key algorithm")
}
// CanSign returns true iff this public key can generate signatures
func (pk *PublicKey) CanSign() bool {
return pk.PubKeyAlgo != PubKeyAlgoRSAEncryptOnly && pk.PubKeyAlgo != PubKeyAlgoElGamal && pk.PubKeyAlgo != PubKeyAlgoECDH
}
// VerifySignature returns nil iff sig is a valid signature, made by this
// public key, of the data hashed into signed. signed is mutated by this call.
func (pk *PublicKey) VerifySignature(signed hash.Hash, sig *Signature) (err error) {
if !pk.CanSign() {
return errors.InvalidArgumentError("public key cannot generate signatures")
}
if sig.Version == 5 && (sig.SigType == 0x00 || sig.SigType == 0x01) {
sig.AddMetadataToHashSuffix()
}
signed.Write(sig.HashSuffix)
hashBytes := signed.Sum(nil)
if hashBytes[0] != sig.HashTag[0] || hashBytes[1] != sig.HashTag[1] {
return errors.SignatureError("hash tag doesn't match")
}
if pk.PubKeyAlgo != sig.PubKeyAlgo {
return errors.InvalidArgumentError("public key and signature use different algorithms")
}
switch pk.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
rsaPublicKey, _ := pk.PublicKey.(*rsa.PublicKey)
err = rsa.VerifyPKCS1v15(rsaPublicKey, sig.Hash, hashBytes, padToKeySize(rsaPublicKey, sig.RSASignature.Bytes()))
if err != nil {
return errors.SignatureError("RSA verification failure")
}
return nil
case PubKeyAlgoDSA:
dsaPublicKey, _ := pk.PublicKey.(*dsa.PublicKey)
// Need to truncate hashBytes to match FIPS 186-3 section 4.6.
subgroupSize := (dsaPublicKey.Q.BitLen() + 7) / 8
if len(hashBytes) > subgroupSize {
hashBytes = hashBytes[:subgroupSize]
}
if !dsa.Verify(dsaPublicKey, hashBytes, new(big.Int).SetBytes(sig.DSASigR.Bytes()), new(big.Int).SetBytes(sig.DSASigS.Bytes())) {
return errors.SignatureError("DSA verification failure")
}
return nil
case PubKeyAlgoECDSA:
ecdsaPublicKey := pk.PublicKey.(*ecdsa.PublicKey)
if !ecdsa.Verify(ecdsaPublicKey, hashBytes, new(big.Int).SetBytes(sig.ECDSASigR.Bytes()), new(big.Int).SetBytes(sig.ECDSASigS.Bytes())) {
return errors.SignatureError("ECDSA verification failure")
}
return nil
case PubKeyAlgoEdDSA:
eddsaPublicKey := pk.PublicKey.(*ed25519.PublicKey)
sigR := sig.EdDSASigR.Bytes()
sigS := sig.EdDSASigS.Bytes()
eddsaSig := make([]byte, ed25519.SignatureSize)
copy(eddsaSig[32-len(sigR):32], sigR)
copy(eddsaSig[64-len(sigS):], sigS)
if !ed25519.Verify(*eddsaPublicKey, hashBytes, eddsaSig) {
return errors.SignatureError("EdDSA verification failure")
}
return nil
default:
return errors.SignatureError("Unsupported public key algorithm used in signature")
}
}
// keySignatureHash returns a Hash of the message that needs to be signed for
// pk to assert a subkey relationship to signed.
func keySignatureHash(pk, signed signingKey, hashFunc crypto.Hash) (h hash.Hash, err error) {
if !hashFunc.Available() {
return nil, errors.UnsupportedError("hash function")
}
h = hashFunc.New()
// RFC 4880, section 5.2.4
err = pk.SerializeForHash(h)
if err != nil {
return nil, err
}
err = signed.SerializeForHash(h)
return
}
// VerifyKeySignature returns nil iff sig is a valid signature, made by this
// public key, of signed.
func (pk *PublicKey) VerifyKeySignature(signed *PublicKey, sig *Signature) error {
h, err := keySignatureHash(pk, signed, sig.Hash)
if err != nil {
return err
}
if err = pk.VerifySignature(h, sig); err != nil {
return err
}
if sig.FlagSign {
// Signing subkeys must be cross-signed. See
// https://www.gnupg.org/faq/subkey-cross-certify.html.
if sig.EmbeddedSignature == nil {
return errors.StructuralError("signing subkey is missing cross-signature")
}
// Verify the cross-signature. This is calculated over the same
// data as the main signature, so we cannot just recursively
// call signed.VerifyKeySignature(...)
if h, err = keySignatureHash(pk, signed, sig.EmbeddedSignature.Hash); err != nil {
return errors.StructuralError("error while hashing for cross-signature: " + err.Error())
}
if err := signed.VerifySignature(h, sig.EmbeddedSignature); err != nil {
return errors.StructuralError("error while verifying cross-signature: " + err.Error())
}
}
return nil
}
func keyRevocationHash(pk signingKey, hashFunc crypto.Hash) (h hash.Hash, err error) {
if !hashFunc.Available() {
return nil, errors.UnsupportedError("hash function")
}
h = hashFunc.New()
// RFC 4880, section 5.2.4
err = pk.SerializeForHash(h)
return
}
// VerifyRevocationSignature returns nil iff sig is a valid signature, made by this
// public key.
func (pk *PublicKey) VerifyRevocationSignature(sig *Signature) (err error) {
h, err := keyRevocationHash(pk, sig.Hash)
if err != nil {
return err
}
return pk.VerifySignature(h, sig)
}
// VerifySubkeyRevocationSignature returns nil iff sig is a valid subkey revocation signature,
// made by the passed in signingKey.
func (pk *PublicKey) VerifySubkeyRevocationSignature(sig *Signature, signingKey *PublicKey) (err error) {
h, err := keyRevocationHash(pk, sig.Hash)
if err != nil {
return err
}
return signingKey.VerifySignature(h, sig)
}
// userIdSignatureHash returns a Hash of the message that needs to be signed
// to assert that pk is a valid key for id.
func userIdSignatureHash(id string, pk *PublicKey, hashFunc crypto.Hash) (h hash.Hash, err error) {
if !hashFunc.Available() {
return nil, errors.UnsupportedError("hash function")
}
h = hashFunc.New()
// RFC 4880, section 5.2.4
pk.SerializeSignaturePrefix(h)
pk.serializeWithoutHeaders(h)
var buf [5]byte
buf[0] = 0xb4
buf[1] = byte(len(id) >> 24)
buf[2] = byte(len(id) >> 16)
buf[3] = byte(len(id) >> 8)
buf[4] = byte(len(id))
h.Write(buf[:])
h.Write([]byte(id))
return
}
// VerifyUserIdSignature returns nil iff sig is a valid signature, made by this
// public key, that id is the identity of pub.
func (pk *PublicKey) VerifyUserIdSignature(id string, pub *PublicKey, sig *Signature) (err error) {
h, err := userIdSignatureHash(id, pub, sig.Hash)
if err != nil {
return err
}
return pk.VerifySignature(h, sig)
}
// KeyIdString returns the public key's fingerprint in capital hex
// (e.g. "6C7EE1B8621CC013").
func (pk *PublicKey) KeyIdString() string {
return fmt.Sprintf("%X", pk.Fingerprint[12:20])
}
// KeyIdShortString returns the short form of public key's fingerprint
// in capital hex, as shown by gpg --list-keys (e.g. "621CC013").
func (pk *PublicKey) KeyIdShortString() string {
return fmt.Sprintf("%X", pk.Fingerprint[16:20])
}
// BitLength returns the bit length for the given public key.
func (pk *PublicKey) BitLength() (bitLength uint16, err error) {
switch pk.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly:
bitLength = pk.n.BitLength()
case PubKeyAlgoDSA:
bitLength = pk.p.BitLength()
case PubKeyAlgoElGamal:
bitLength = pk.p.BitLength()
case PubKeyAlgoECDSA:
bitLength = pk.p.BitLength()
case PubKeyAlgoECDH:
bitLength = pk.p.BitLength()
case PubKeyAlgoEdDSA:
bitLength = pk.p.BitLength()
default:
err = errors.InvalidArgumentError("bad public-key algorithm")
}
return
}
// KeyExpired returns whether sig is a self-signature of a key that has
// expired or is created in the future.
func (pk *PublicKey) KeyExpired(sig *Signature, currentTime time.Time) bool {
if pk.CreationTime.After(currentTime) {
return true
}
if sig.KeyLifetimeSecs == nil || *sig.KeyLifetimeSecs == 0 {
return false
}
expiry := pk.CreationTime.Add(time.Duration(*sig.KeyLifetimeSecs) * time.Second)
return currentTime.After(expiry)
}

View File

@ -0,0 +1,24 @@
package packet
const rsaFingerprintHex = "5fb74b1d03b1e3cb31bc2f8aa34d7e18c20c31bb"
const rsaPkDataHex = "988d044d3c5c10010400b1d13382944bd5aba23a4312968b5095d14f947f600eb478e14a6fcb16b0e0cac764884909c020bc495cfcc39a935387c661507bdb236a0612fb582cac3af9b29cc2c8c70090616c41b662f4da4c1201e195472eb7f4ae1ccbcbf9940fe21d985e379a5563dde5b9a23d35f1cfaa5790da3b79db26f23695107bfaca8e7b5bcd0011010001"
const dsaFingerprintHex = "eece4c094db002103714c63c8e8fbe54062f19ed"
const dsaPkDataHex = "9901a2044d432f89110400cd581334f0d7a1e1bdc8b9d6d8c0baf68793632735d2bb0903224cbaa1dfbf35a60ee7a13b92643421e1eb41aa8d79bea19a115a677f6b8ba3c7818ce53a6c2a24a1608bd8b8d6e55c5090cbde09dd26e356267465ae25e69ec8bdd57c7bbb2623e4d73336f73a0a9098f7f16da2e25252130fd694c0e8070c55a812a423ae7f00a0ebf50e70c2f19c3520a551bd4b08d30f23530d3d03ff7d0bf4a53a64a09dc5e6e6e35854b7d70c882b0c60293401958b1bd9e40abec3ea05ba87cf64899299d4bd6aa7f459c201d3fbbd6c82004bdc5e8a9eb8082d12054cc90fa9d4ec251a843236a588bf49552441817436c4f43326966fe85447d4e6d0acf8fa1ef0f014730770603ad7634c3088dc52501c237328417c31c89ed70400b2f1a98b0bf42f11fefc430704bebbaa41d9f355600c3facee1e490f64208e0e094ea55e3a598a219a58500bf78ac677b670a14f4e47e9cf8eab4f368cc1ddcaa18cc59309d4cc62dd4f680e73e6cc3e1ce87a84d0925efbcb26c575c093fc42eecf45135fabf6403a25c2016e1774c0484e440a18319072c617cc97ac0a3bb0"
const ecdsaFingerprintHex = "9892270b38b8980b05c8d56d43fe956c542ca00b"
const ecdsaPkDataHex = "9893045071c29413052b8104002304230401f4867769cedfa52c325018896245443968e52e51d0c2df8d939949cb5b330f2921711fbee1c9b9dddb95d15cb0255e99badeddda7cc23d9ddcaacbc290969b9f24019375d61c2e4e3b36953a28d8b2bc95f78c3f1d592fb24499be348656a7b17e3963187b4361afe497bc5f9f81213f04069f8e1fb9e6a6290ae295ca1a92b894396cb4"
const ecdhFingerprintHex = "722354df2475a42164d1d49faa8b938f9a201946"
const ecdhPkDataHex = "b90073044d53059212052b810400220303042faa84024a20b6735c4897efa5bfb41bf85b7eefeab5ca0cb9ffc8ea04a46acb25534a577694f9e25340a4ab5223a9dd1eda530c8aa2e6718db10d7e672558c7736fe09369ea5739a2a3554bf16d41faa50562f11c6d39bbd5dffb6b9a9ec91803010909"
const eddsaFingerprintHex = "b2d5e5ec0e6deca6bc8eeeb00907e75e1dd99ad8"
const eddsaPkDataHex = "98330456e2132b16092b06010401da470f01010740bbda39266affa511a8c2d02edf690fb784b0499c4406185811a163539ef11dc1b41d74657374696e67203c74657374696e674074657374696e672e636f6d3e8879041316080021050256e2132b021b03050b09080702061508090a0b020416020301021e01021780000a09100907e75e1dd99ad86d0c00fe39d2008359352782bc9b61ac382584cd8eff3f57a18c2287e3afeeb05d1f04ba00fe2d0bc1ddf3ff8adb9afa3e7d9287244b4ec567f3db4d60b74a9b5465ed528203"
// Source: https://sites.google.com/site/brainhub/pgpecckeys#TOC-ECC-NIST-P-384-key
const ecc384PubHex = `99006f044d53059213052b81040022030304f6b8c5aced5b84ef9f4a209db2e4a9dfb70d28cb8c10ecd57674a9fa5a67389942b62d5e51367df4c7bfd3f8e500feecf07ed265a621a8ebbbe53e947ec78c677eba143bd1533c2b350e1c29f82313e1e1108eba063be1e64b10e6950e799c2db42465635f6473615f64685f333834203c6f70656e70677040627261696e6875622e6f72673e8900cb04101309005305024d530592301480000000002000077072656665727265642d656d61696c2d656e636f64696e67407067702e636f6d7067706d696d65040b090807021901051b03000000021602051e010000000415090a08000a0910098033880f54719fca2b0180aa37350968bd5f115afd8ce7bc7b103822152dbff06d0afcda835329510905b98cb469ba208faab87c7412b799e7b633017f58364ea480e8a1a3f253a0c5f22c446e8be9a9fce6210136ee30811abbd49139de28b5bdf8dc36d06ae748579e9ff503b90073044d53059212052b810400220303042faa84024a20b6735c4897efa5bfb41bf85b7eefeab5ca0cb9ffc8ea04a46acb25534a577694f9e25340a4ab5223a9dd1eda530c8aa2e6718db10d7e672558c7736fe09369ea5739a2a3554bf16d41faa50562f11c6d39bbd5dffb6b9a9ec9180301090989008404181309000c05024d530592051b0c000000000a0910098033880f54719f80970180eee7a6d8fcee41ee4f9289df17f9bcf9d955dca25c583b94336f3a2b2d4986dc5cf417b8d2dc86f741a9e1a6d236c0e3017d1c76575458a0cfb93ae8a2b274fcc65ceecd7a91eec83656ba13219969f06945b48c56bd04152c3a0553c5f2f4bd1267`

View File

@ -5,8 +5,9 @@
package packet package packet
import ( import (
"golang.org/x/crypto/openpgp/errors"
"io" "io"
"github.com/ProtonMail/go-crypto/openpgp/errors"
) )
// Reader reads packets from an io.Reader and allows packets to be 'unread' so // Reader reads packets from an io.Reader and allows packets to be 'unread' so
@ -42,6 +43,7 @@ func (r *Reader) Next() (p Packet, err error) {
r.readers = r.readers[:len(r.readers)-1] r.readers = r.readers[:len(r.readers)-1]
continue continue
} }
// TODO: Add strict mode that rejects unknown packets, instead of ignoring them.
if _, ok := err.(errors.UnknownPacketTypeError); !ok { if _, ok := err.(errors.UnknownPacketTypeError); !ok {
return nil, err return nil, err
} }

View File

@ -17,8 +17,9 @@ import (
"strconv" "strconv"
"time" "time"
"golang.org/x/crypto/openpgp/errors" "github.com/ProtonMail/go-crypto/openpgp/errors"
"golang.org/x/crypto/openpgp/s2k" "github.com/ProtonMail/go-crypto/openpgp/internal/encoding"
"github.com/ProtonMail/go-crypto/openpgp/s2k"
) )
const ( const (
@ -31,6 +32,7 @@ const (
// Signature represents a signature. See RFC 4880, section 5.2. // Signature represents a signature. See RFC 4880, section 5.2.
type Signature struct { type Signature struct {
Version int
SigType SignatureType SigType SignatureType
PubKeyAlgo PublicKeyAlgorithm PubKeyAlgo PublicKeyAlgorithm
Hash crypto.Hash Hash crypto.Hash
@ -40,11 +42,18 @@ type Signature struct {
// HashTag contains the first two bytes of the hash for fast rejection // HashTag contains the first two bytes of the hash for fast rejection
// of bad signed data. // of bad signed data.
HashTag [2]byte HashTag [2]byte
// Metadata includes format, filename and time, and is protected by v5
// signatures of type 0x00 or 0x01. This metadata is included into the hash
// computation; if nil, six 0x00 bytes are used instead. See section 5.2.4.
Metadata *LiteralData
CreationTime time.Time CreationTime time.Time
RSASignature parsedMPI RSASignature encoding.Field
DSASigR, DSASigS parsedMPI DSASigR, DSASigS encoding.Field
ECDSASigR, ECDSASigS parsedMPI ECDSASigR, ECDSASigS encoding.Field
EdDSASigR, EdDSASigS encoding.Field
// rawSubpackets contains the unparsed subpackets, in order. // rawSubpackets contains the unparsed subpackets, in order.
rawSubpackets []outputSubpacket rawSubpackets []outputSubpacket
@ -54,9 +63,16 @@ type Signature struct {
SigLifetimeSecs, KeyLifetimeSecs *uint32 SigLifetimeSecs, KeyLifetimeSecs *uint32
PreferredSymmetric, PreferredHash, PreferredCompression []uint8 PreferredSymmetric, PreferredHash, PreferredCompression []uint8
PreferredAEAD []uint8
IssuerKeyId *uint64 IssuerKeyId *uint64
IssuerFingerprint []byte
IsPrimaryId *bool IsPrimaryId *bool
// PolicyURI can be set to the URI of a document that describes the
// policy under which the signature was issued. See RFC 4880, section
// 5.2.3.20 for details.
PolicyURI string
// FlagsValid is set if any flags were given. See RFC 4880, section // FlagsValid is set if any flags were given. See RFC 4880, section
// 5.2.3.21 for details. // 5.2.3.21 for details.
FlagsValid bool FlagsValid bool
@ -67,9 +83,10 @@ type Signature struct {
RevocationReason *uint8 RevocationReason *uint8
RevocationReasonText string RevocationReasonText string
// MDC is set if this signature has a feature packet that indicates // In a self-signature, these flags are set there is a features subpacket
// support for MDC subpackets. // indicating that the issuer implementation supports these features
MDC bool // (section 5.2.5.25).
MDC, AEAD, V5Keys bool
// EmbeddedSignature, if non-nil, is a signature of the parent key, by // EmbeddedSignature, if non-nil, is a signature of the parent key, by
// this key. This prevents an attacker from claiming another's signing // this key. This prevents an attacker from claiming another's signing
@ -86,11 +103,11 @@ func (sig *Signature) parse(r io.Reader) (err error) {
if err != nil { if err != nil {
return return
} }
if buf[0] != 4 { if buf[0] != 4 && buf[0] != 5 {
err = errors.UnsupportedError("signature packet version " + strconv.Itoa(int(buf[0]))) err = errors.UnsupportedError("signature packet version " + strconv.Itoa(int(buf[0])))
return return
} }
sig.Version = int(buf[0])
_, err = readFull(r, buf[:5]) _, err = readFull(r, buf[:5])
if err != nil { if err != nil {
return return
@ -98,7 +115,7 @@ func (sig *Signature) parse(r io.Reader) (err error) {
sig.SigType = SignatureType(buf[0]) sig.SigType = SignatureType(buf[0])
sig.PubKeyAlgo = PublicKeyAlgorithm(buf[1]) sig.PubKeyAlgo = PublicKeyAlgorithm(buf[1])
switch sig.PubKeyAlgo { switch sig.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA, PubKeyAlgoECDSA: case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA, PubKeyAlgoECDSA, PubKeyAlgoEdDSA:
default: default:
err = errors.UnsupportedError("public key algorithm " + strconv.Itoa(int(sig.PubKeyAlgo))) err = errors.UnsupportedError("public key algorithm " + strconv.Itoa(int(sig.PubKeyAlgo)))
return return
@ -111,24 +128,12 @@ func (sig *Signature) parse(r io.Reader) (err error) {
} }
hashedSubpacketsLength := int(buf[3])<<8 | int(buf[4]) hashedSubpacketsLength := int(buf[3])<<8 | int(buf[4])
l := 6 + hashedSubpacketsLength hashedSubpackets := make([]byte, hashedSubpacketsLength)
sig.HashSuffix = make([]byte, l+6)
sig.HashSuffix[0] = 4
copy(sig.HashSuffix[1:], buf[:5])
hashedSubpackets := sig.HashSuffix[6:l]
_, err = readFull(r, hashedSubpackets) _, err = readFull(r, hashedSubpackets)
if err != nil { if err != nil {
return return
} }
// See RFC 4880, section 5.2.4 sig.buildHashSuffix(hashedSubpackets)
trailer := sig.HashSuffix[l:]
trailer[0] = 4
trailer[1] = 0xff
trailer[2] = uint8(l >> 24)
trailer[3] = uint8(l >> 16)
trailer[4] = uint8(l >> 8)
trailer[5] = uint8(l)
err = parseSignatureSubpackets(sig, hashedSubpackets, true) err = parseSignatureSubpackets(sig, hashedSubpackets, true)
if err != nil { if err != nil {
return return
@ -156,16 +161,33 @@ func (sig *Signature) parse(r io.Reader) (err error) {
switch sig.PubKeyAlgo { switch sig.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
sig.RSASignature.bytes, sig.RSASignature.bitLength, err = readMPI(r) sig.RSASignature = new(encoding.MPI)
_, err = sig.RSASignature.ReadFrom(r)
case PubKeyAlgoDSA: case PubKeyAlgoDSA:
sig.DSASigR.bytes, sig.DSASigR.bitLength, err = readMPI(r) sig.DSASigR = new(encoding.MPI)
if err == nil { if _, err = sig.DSASigR.ReadFrom(r); err != nil {
sig.DSASigS.bytes, sig.DSASigS.bitLength, err = readMPI(r) return
} }
sig.DSASigS = new(encoding.MPI)
_, err = sig.DSASigS.ReadFrom(r)
case PubKeyAlgoECDSA: case PubKeyAlgoECDSA:
sig.ECDSASigR.bytes, sig.ECDSASigR.bitLength, err = readMPI(r) sig.ECDSASigR = new(encoding.MPI)
if err == nil { if _, err = sig.ECDSASigR.ReadFrom(r); err != nil {
sig.ECDSASigS.bytes, sig.ECDSASigS.bitLength, err = readMPI(r) return
}
sig.ECDSASigS = new(encoding.MPI)
_, err = sig.ECDSASigS.ReadFrom(r)
case PubKeyAlgoEdDSA:
sig.EdDSASigR = new(encoding.MPI)
if _, err = sig.EdDSASigR.ReadFrom(r); err != nil {
return
}
sig.EdDSASigS = new(encoding.MPI)
if _, err = sig.EdDSASigS.ReadFrom(r); err != nil {
return
} }
default: default:
panic("unreachable") panic("unreachable")
@ -201,10 +223,13 @@ const (
prefHashAlgosSubpacket signatureSubpacketType = 21 prefHashAlgosSubpacket signatureSubpacketType = 21
prefCompressionSubpacket signatureSubpacketType = 22 prefCompressionSubpacket signatureSubpacketType = 22
primaryUserIdSubpacket signatureSubpacketType = 25 primaryUserIdSubpacket signatureSubpacketType = 25
policyUriSubpacket signatureSubpacketType = 26
keyFlagsSubpacket signatureSubpacketType = 27 keyFlagsSubpacket signatureSubpacketType = 27
reasonForRevocationSubpacket signatureSubpacketType = 29 reasonForRevocationSubpacket signatureSubpacketType = 29
featuresSubpacket signatureSubpacketType = 30 featuresSubpacket signatureSubpacketType = 30
embeddedSignatureSubpacket signatureSubpacketType = 32 embeddedSignatureSubpacket signatureSubpacketType = 32
issuerFingerprintSubpacket signatureSubpacketType = 33
prefAeadAlgosSubpacket signatureSubpacketType = 34
) )
// parseSignatureSubpacket parses a single subpacket. len(subpacket) is >= 1. // parseSignatureSubpacket parses a single subpacket. len(subpacket) is >= 1.
@ -290,6 +315,9 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r
sig.PreferredSymmetric = make([]byte, len(subpacket)) sig.PreferredSymmetric = make([]byte, len(subpacket))
copy(sig.PreferredSymmetric, subpacket) copy(sig.PreferredSymmetric, subpacket)
case issuerSubpacket: case issuerSubpacket:
if sig.Version > 4 {
err = errors.StructuralError("issuer subpacket found in v5 key")
}
// Issuer, section 5.2.3.5 // Issuer, section 5.2.3.5
if len(subpacket) != 8 { if len(subpacket) != 8 {
err = errors.StructuralError("issuer subpacket with bad length") err = errors.StructuralError("issuer subpacket with bad length")
@ -361,9 +389,21 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r
case featuresSubpacket: case featuresSubpacket:
// Features subpacket, section 5.2.3.24 specifies a very general // Features subpacket, section 5.2.3.24 specifies a very general
// mechanism for OpenPGP implementations to signal support for new // mechanism for OpenPGP implementations to signal support for new
// features. In practice, the subpacket is used exclusively to // features.
// indicate support for MDC-protected encryption. if !isHashed {
sig.MDC = len(subpacket) >= 1 && subpacket[0]&1 == 1 return
}
if len(subpacket) > 0 {
if subpacket[0]&0x01 != 0 {
sig.MDC = true
}
if subpacket[0]&0x02 != 0 {
sig.AEAD = true
}
if subpacket[0]&0x04 != 0 {
sig.V5Keys = true
}
}
case embeddedSignatureSubpacket: case embeddedSignatureSubpacket:
// Only usage is in signatures that cross-certify // Only usage is in signatures that cross-certify
// signing subkeys. section 5.2.3.26 describes the // signing subkeys. section 5.2.3.26 describes the
@ -382,6 +422,32 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r
if sigType := sig.EmbeddedSignature.SigType; sigType != SigTypePrimaryKeyBinding { if sigType := sig.EmbeddedSignature.SigType; sigType != SigTypePrimaryKeyBinding {
return nil, errors.StructuralError("cross-signature has unexpected type " + strconv.Itoa(int(sigType))) return nil, errors.StructuralError("cross-signature has unexpected type " + strconv.Itoa(int(sigType)))
} }
case policyUriSubpacket:
// Policy URI, section 5.2.3.20
if !isHashed {
return
}
sig.PolicyURI = string(subpacket)
case issuerFingerprintSubpacket:
v, l := subpacket[0], len(subpacket[1:])
if v == 5 && l != 32 || v != 5 && l != 20 {
return nil, errors.StructuralError("bad fingerprint length")
}
sig.IssuerFingerprint = make([]byte, l)
copy(sig.IssuerFingerprint, subpacket[1:])
sig.IssuerKeyId = new(uint64)
if v == 5 {
*sig.IssuerKeyId = binary.BigEndian.Uint64(subpacket[1:9])
} else {
*sig.IssuerKeyId = binary.BigEndian.Uint64(subpacket[13:21])
}
case prefAeadAlgosSubpacket:
// Preferred symmetric algorithms, section 5.2.3.8
if !isHashed {
return
}
sig.PreferredAEAD = make([]byte, len(subpacket))
copy(sig.PreferredAEAD, subpacket)
default: default:
if isCritical { if isCritical {
err = errors.UnsupportedError("unknown critical signature subpacket type " + strconv.Itoa(int(packetType))) err = errors.UnsupportedError("unknown critical signature subpacket type " + strconv.Itoa(int(packetType)))
@ -406,6 +472,13 @@ func subpacketLengthLength(length int) int {
return 5 return 5
} }
func (sig *Signature) CheckKeyIdOrFingerprint(pk *PublicKey) bool {
if sig.IssuerFingerprint != nil && len(sig.IssuerFingerprint) >= 20 {
return bytes.Equal(sig.IssuerFingerprint, pk.Fingerprint)
}
return sig.IssuerKeyId != nil && *sig.IssuerKeyId == pk.KeyId
}
// serializeSubpacketLength marshals the given length into to. // serializeSubpacketLength marshals the given length into to.
func serializeSubpacketLength(to []byte, length int) int { func serializeSubpacketLength(to []byte, length int) int {
// RFC 4880, Section 4.2.2. // RFC 4880, Section 4.2.2.
@ -446,6 +519,9 @@ func serializeSubpackets(to []byte, subpackets []outputSubpacket, hashed bool) {
if subpacket.hashed == hashed { if subpacket.hashed == hashed {
n := serializeSubpacketLength(to, len(subpacket.contents)+1) n := serializeSubpacketLength(to, len(subpacket.contents)+1)
to[n] = byte(subpacket.subpacketType) to[n] = byte(subpacket.subpacketType)
if subpacket.isCritical {
to[n] |= 0x80
}
to = to[1+n:] to = to[1+n:]
n = copy(to, subpacket.contents) n = copy(to, subpacket.contents)
to = to[n:] to = to[n:]
@ -454,49 +530,66 @@ func serializeSubpackets(to []byte, subpackets []outputSubpacket, hashed bool) {
return return
} }
// KeyExpired returns whether sig is a self-signature of a key that has // SigExpired returns whether sig is a signature that has expired or is created
// expired. // in the future.
func (sig *Signature) KeyExpired(currentTime time.Time) bool { func (sig *Signature) SigExpired(currentTime time.Time) bool {
if sig.KeyLifetimeSecs == nil { if sig.CreationTime.After(currentTime) {
return true
}
if sig.SigLifetimeSecs == nil || *sig.SigLifetimeSecs == 0 {
return false return false
} }
expiry := sig.CreationTime.Add(time.Duration(*sig.KeyLifetimeSecs) * time.Second) expiry := sig.CreationTime.Add(time.Duration(*sig.SigLifetimeSecs) * time.Second)
return currentTime.After(expiry) return currentTime.After(expiry)
} }
// buildHashSuffix constructs the HashSuffix member of sig in preparation for signing. // buildHashSuffix constructs the HashSuffix member of sig in preparation for signing.
func (sig *Signature) buildHashSuffix() (err error) { func (sig *Signature) buildHashSuffix(hashedSubpackets []byte) (err error) {
hashedSubpacketsLen := subpacketsLength(sig.outSubpackets, true) hash, ok := s2k.HashToHashId(sig.Hash)
var ok bool
l := 6 + hashedSubpacketsLen
sig.HashSuffix = make([]byte, l+6)
sig.HashSuffix[0] = 4
sig.HashSuffix[1] = uint8(sig.SigType)
sig.HashSuffix[2] = uint8(sig.PubKeyAlgo)
sig.HashSuffix[3], ok = s2k.HashToHashId(sig.Hash)
if !ok { if !ok {
sig.HashSuffix = nil sig.HashSuffix = nil
return errors.InvalidArgumentError("hash cannot be represented in OpenPGP: " + strconv.Itoa(int(sig.Hash))) return errors.InvalidArgumentError("hash cannot be represented in OpenPGP: " + strconv.Itoa(int(sig.Hash)))
} }
sig.HashSuffix[4] = byte(hashedSubpacketsLen >> 8)
sig.HashSuffix[5] = byte(hashedSubpacketsLen) hashedFields := bytes.NewBuffer([]byte{
serializeSubpackets(sig.HashSuffix[6:l], sig.outSubpackets, true) uint8(sig.Version),
trailer := sig.HashSuffix[l:] uint8(sig.SigType),
trailer[0] = 4 uint8(sig.PubKeyAlgo),
trailer[1] = 0xff uint8(hash),
trailer[2] = byte(l >> 24) uint8(len(hashedSubpackets) >> 8),
trailer[3] = byte(l >> 16) uint8(len(hashedSubpackets)),
trailer[4] = byte(l >> 8) })
trailer[5] = byte(l) hashedFields.Write(hashedSubpackets)
var l uint64 = uint64(6 + len(hashedSubpackets))
if sig.Version == 5 {
hashedFields.Write([]byte{0x05, 0xff})
hashedFields.Write([]byte{
uint8(l >> 56), uint8(l >> 48), uint8(l >> 40), uint8(l >> 32),
uint8(l >> 24), uint8(l >> 16), uint8(l >> 8), uint8(l),
})
} else {
hashedFields.Write([]byte{0x04, 0xff})
hashedFields.Write([]byte{
uint8(l >> 24), uint8(l >> 16), uint8(l >> 8), uint8(l),
})
}
sig.HashSuffix = make([]byte, hashedFields.Len())
copy(sig.HashSuffix, hashedFields.Bytes())
return return
} }
func (sig *Signature) signPrepareHash(h hash.Hash) (digest []byte, err error) { func (sig *Signature) signPrepareHash(h hash.Hash) (digest []byte, err error) {
err = sig.buildHashSuffix() hashedSubpacketsLen := subpacketsLength(sig.outSubpackets, true)
hashedSubpackets := make([]byte, hashedSubpacketsLen)
serializeSubpackets(hashedSubpackets, sig.outSubpackets, true)
err = sig.buildHashSuffix(hashedSubpackets)
if err != nil { if err != nil {
return return
} }
if sig.Version == 5 && (sig.SigType == 0x00 || sig.SigType == 0x01) {
sig.AddMetadataToHashSuffix()
}
h.Write(sig.HashSuffix) h.Write(sig.HashSuffix)
digest = h.Sum(nil) digest = h.Sum(nil)
@ -509,17 +602,26 @@ func (sig *Signature) signPrepareHash(h hash.Hash) (digest []byte, err error) {
// On success, the signature is stored in sig. Call Serialize to write it out. // On success, the signature is stored in sig. Call Serialize to write it out.
// If config is nil, sensible defaults will be used. // If config is nil, sensible defaults will be used.
func (sig *Signature) Sign(h hash.Hash, priv *PrivateKey, config *Config) (err error) { func (sig *Signature) Sign(h hash.Hash, priv *PrivateKey, config *Config) (err error) {
sig.outSubpackets = sig.buildSubpackets() if priv.Dummy() {
return errors.ErrDummyPrivateKey("dummy key found")
}
sig.Version = priv.PublicKey.Version
sig.IssuerFingerprint = priv.PublicKey.Fingerprint
sig.outSubpackets, err = sig.buildSubpackets(priv.PublicKey)
if err != nil {
return err
}
digest, err := sig.signPrepareHash(h) digest, err := sig.signPrepareHash(h)
if err != nil { if err != nil {
return return
} }
switch priv.PubKeyAlgo { switch priv.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
// supports both *rsa.PrivateKey and crypto.Signer // supports both *rsa.PrivateKey and crypto.Signer
sig.RSASignature.bytes, err = priv.PrivateKey.(crypto.Signer).Sign(config.Random(), digest, sig.Hash) sigdata, err := priv.PrivateKey.(crypto.Signer).Sign(config.Random(), digest, sig.Hash)
sig.RSASignature.bitLength = uint16(8 * len(sig.RSASignature.bytes)) if err == nil {
sig.RSASignature = encoding.NewMPI(sigdata)
}
case PubKeyAlgoDSA: case PubKeyAlgoDSA:
dsaPriv := priv.PrivateKey.(*dsa.PrivateKey) dsaPriv := priv.PrivateKey.(*dsa.PrivateKey)
@ -530,10 +632,8 @@ func (sig *Signature) Sign(h hash.Hash, priv *PrivateKey, config *Config) (err e
} }
r, s, err := dsa.Sign(config.Random(), dsaPriv, digest) r, s, err := dsa.Sign(config.Random(), dsaPriv, digest)
if err == nil { if err == nil {
sig.DSASigR.bytes = r.Bytes() sig.DSASigR = new(encoding.MPI).SetBig(r)
sig.DSASigR.bitLength = uint16(8 * len(sig.DSASigR.bytes)) sig.DSASigS = new(encoding.MPI).SetBig(s)
sig.DSASigS.bytes = s.Bytes()
sig.DSASigS.bitLength = uint16(8 * len(sig.DSASigS.bytes))
} }
case PubKeyAlgoECDSA: case PubKeyAlgoECDSA:
var r, s *big.Int var r, s *big.Int
@ -548,8 +648,14 @@ func (sig *Signature) Sign(h hash.Hash, priv *PrivateKey, config *Config) (err e
} }
} }
if err == nil { if err == nil {
sig.ECDSASigR = fromBig(r) sig.ECDSASigR = new(encoding.MPI).SetBig(r)
sig.ECDSASigS = fromBig(s) sig.ECDSASigS = new(encoding.MPI).SetBig(s)
}
case PubKeyAlgoEdDSA:
sigdata, err := priv.PrivateKey.(crypto.Signer).Sign(config.Random(), digest, crypto.Hash(0))
if err == nil {
sig.EdDSASigR = encoding.NewMPI(sigdata[:32])
sig.EdDSASigS = encoding.NewMPI(sigdata[32:])
} }
default: default:
err = errors.UnsupportedError("public key algorithm: " + strconv.Itoa(int(sig.PubKeyAlgo))) err = errors.UnsupportedError("public key algorithm: " + strconv.Itoa(int(sig.PubKeyAlgo)))
@ -576,6 +682,9 @@ func unwrapECDSASig(b []byte) (r, s *big.Int, err error) {
// Serialize to write it out. // Serialize to write it out.
// If config is nil, sensible defaults will be used. // If config is nil, sensible defaults will be used.
func (sig *Signature) SignUserId(id string, pub *PublicKey, priv *PrivateKey, config *Config) error { func (sig *Signature) SignUserId(id string, pub *PublicKey, priv *PrivateKey, config *Config) error {
if priv.Dummy() {
return errors.ErrDummyPrivateKey("dummy key found")
}
h, err := userIdSignatureHash(id, pub, sig.Hash) h, err := userIdSignatureHash(id, pub, sig.Hash)
if err != nil { if err != nil {
return err return err
@ -583,10 +692,25 @@ func (sig *Signature) SignUserId(id string, pub *PublicKey, priv *PrivateKey, co
return sig.Sign(h, priv, config) return sig.Sign(h, priv, config)
} }
// CrossSignKey computes a signature from signingKey on pub hashed using hashKey. On success,
// the signature is stored in sig. Call Serialize to write it out.
// If config is nil, sensible defaults will be used.
func (sig *Signature) CrossSignKey(pub *PublicKey, hashKey *PublicKey, signingKey *PrivateKey,
config *Config) error {
h, err := keySignatureHash(hashKey, pub, sig.Hash)
if err != nil {
return err
}
return sig.Sign(h, signingKey, config)
}
// SignKey computes a signature from priv, asserting that pub is a subkey. On // SignKey computes a signature from priv, asserting that pub is a subkey. On
// success, the signature is stored in sig. Call Serialize to write it out. // success, the signature is stored in sig. Call Serialize to write it out.
// If config is nil, sensible defaults will be used. // If config is nil, sensible defaults will be used.
func (sig *Signature) SignKey(pub *PublicKey, priv *PrivateKey, config *Config) error { func (sig *Signature) SignKey(pub *PublicKey, priv *PrivateKey, config *Config) error {
if priv.Dummy() {
return errors.ErrDummyPrivateKey("dummy key found")
}
h, err := keySignatureHash(&priv.PublicKey, pub, sig.Hash) h, err := keySignatureHash(&priv.PublicKey, pub, sig.Hash)
if err != nil { if err != nil {
return err return err
@ -594,26 +718,40 @@ func (sig *Signature) SignKey(pub *PublicKey, priv *PrivateKey, config *Config)
return sig.Sign(h, priv, config) return sig.Sign(h, priv, config)
} }
// RevokeKey computes a revocation signature of pub using priv. On success, the signature is
// stored in sig. Call Serialize to write it out.
// If config is nil, sensible defaults will be used.
func (sig *Signature) RevokeKey(pub *PublicKey, priv *PrivateKey, config *Config) error {
h, err := keyRevocationHash(pub, sig.Hash)
if err != nil {
return err
}
return sig.Sign(h, priv, config)
}
// Serialize marshals sig to w. Sign, SignUserId or SignKey must have been // Serialize marshals sig to w. Sign, SignUserId or SignKey must have been
// called first. // called first.
func (sig *Signature) Serialize(w io.Writer) (err error) { func (sig *Signature) Serialize(w io.Writer) (err error) {
if len(sig.outSubpackets) == 0 { if len(sig.outSubpackets) == 0 {
sig.outSubpackets = sig.rawSubpackets sig.outSubpackets = sig.rawSubpackets
} }
if sig.RSASignature.bytes == nil && sig.DSASigR.bytes == nil && sig.ECDSASigR.bytes == nil { if sig.RSASignature == nil && sig.DSASigR == nil && sig.ECDSASigR == nil && sig.EdDSASigR == nil {
return errors.InvalidArgumentError("Signature: need to call Sign, SignUserId or SignKey before Serialize") return errors.InvalidArgumentError("Signature: need to call Sign, SignUserId or SignKey before Serialize")
} }
sigLength := 0 sigLength := 0
switch sig.PubKeyAlgo { switch sig.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
sigLength = 2 + len(sig.RSASignature.bytes) sigLength = int(sig.RSASignature.EncodedLength())
case PubKeyAlgoDSA: case PubKeyAlgoDSA:
sigLength = 2 + len(sig.DSASigR.bytes) sigLength = int(sig.DSASigR.EncodedLength())
sigLength += 2 + len(sig.DSASigS.bytes) sigLength += int(sig.DSASigS.EncodedLength())
case PubKeyAlgoECDSA: case PubKeyAlgoECDSA:
sigLength = 2 + len(sig.ECDSASigR.bytes) sigLength = int(sig.ECDSASigR.EncodedLength())
sigLength += 2 + len(sig.ECDSASigS.bytes) sigLength += int(sig.ECDSASigS.EncodedLength())
case PubKeyAlgoEdDSA:
sigLength = int(sig.EdDSASigR.EncodedLength())
sigLength += int(sig.EdDSASigS.EncodedLength())
default: default:
panic("impossible") panic("impossible")
} }
@ -622,16 +760,29 @@ func (sig *Signature) Serialize(w io.Writer) (err error) {
length := len(sig.HashSuffix) - 6 /* trailer not included */ + length := len(sig.HashSuffix) - 6 /* trailer not included */ +
2 /* length of unhashed subpackets */ + unhashedSubpacketsLen + 2 /* length of unhashed subpackets */ + unhashedSubpacketsLen +
2 /* hash tag */ + sigLength 2 /* hash tag */ + sigLength
if sig.Version == 5 {
length -= 4 // eight-octet instead of four-octet big endian
}
err = serializeHeader(w, packetTypeSignature, length) err = serializeHeader(w, packetTypeSignature, length)
if err != nil { if err != nil {
return return
} }
err = sig.serializeBody(w)
if err != nil {
return err
}
return
}
_, err = w.Write(sig.HashSuffix[:len(sig.HashSuffix)-6]) func (sig *Signature) serializeBody(w io.Writer) (err error) {
hashedSubpacketsLen := uint16(uint16(sig.HashSuffix[4])<<8) | uint16(sig.HashSuffix[5])
fields := sig.HashSuffix[:6+hashedSubpacketsLen]
_, err = w.Write(fields)
if err != nil { if err != nil {
return return
} }
unhashedSubpacketsLen := subpacketsLength(sig.outSubpackets, false)
unhashedSubpackets := make([]byte, 2+unhashedSubpacketsLen) unhashedSubpackets := make([]byte, 2+unhashedSubpacketsLen)
unhashedSubpackets[0] = byte(unhashedSubpacketsLen >> 8) unhashedSubpackets[0] = byte(unhashedSubpacketsLen >> 8)
unhashedSubpackets[1] = byte(unhashedSubpacketsLen) unhashedSubpackets[1] = byte(unhashedSubpacketsLen)
@ -648,11 +799,22 @@ func (sig *Signature) Serialize(w io.Writer) (err error) {
switch sig.PubKeyAlgo { switch sig.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
err = writeMPIs(w, sig.RSASignature) _, err = w.Write(sig.RSASignature.EncodedBytes())
case PubKeyAlgoDSA: case PubKeyAlgoDSA:
err = writeMPIs(w, sig.DSASigR, sig.DSASigS) if _, err = w.Write(sig.DSASigR.EncodedBytes()); err != nil {
return
}
_, err = w.Write(sig.DSASigS.EncodedBytes())
case PubKeyAlgoECDSA: case PubKeyAlgoECDSA:
err = writeMPIs(w, sig.ECDSASigR, sig.ECDSASigS) if _, err = w.Write(sig.ECDSASigR.EncodedBytes()); err != nil {
return
}
_, err = w.Write(sig.ECDSASigS.EncodedBytes())
case PubKeyAlgoEdDSA:
if _, err = w.Write(sig.EdDSASigR.EncodedBytes()); err != nil {
return
}
_, err = w.Write(sig.EdDSASigS.EncodedBytes())
default: default:
panic("impossible") panic("impossible")
} }
@ -667,17 +829,20 @@ type outputSubpacket struct {
contents []byte contents []byte
} }
func (sig *Signature) buildSubpackets() (subpackets []outputSubpacket) { func (sig *Signature) buildSubpackets(issuer PublicKey) (subpackets []outputSubpacket, err error) {
creationTime := make([]byte, 4) creationTime := make([]byte, 4)
binary.BigEndian.PutUint32(creationTime, uint32(sig.CreationTime.Unix())) binary.BigEndian.PutUint32(creationTime, uint32(sig.CreationTime.Unix()))
subpackets = append(subpackets, outputSubpacket{true, creationTimeSubpacket, false, creationTime}) subpackets = append(subpackets, outputSubpacket{true, creationTimeSubpacket, false, creationTime})
if sig.IssuerKeyId != nil { if sig.IssuerKeyId != nil && sig.Version == 4 {
keyId := make([]byte, 8) keyId := make([]byte, 8)
binary.BigEndian.PutUint64(keyId, *sig.IssuerKeyId) binary.BigEndian.PutUint64(keyId, *sig.IssuerKeyId)
subpackets = append(subpackets, outputSubpacket{true, issuerSubpacket, false, keyId}) subpackets = append(subpackets, outputSubpacket{true, issuerSubpacket, true, keyId})
}
if sig.IssuerFingerprint != nil {
contents := append([]uint8{uint8(issuer.Version)}, sig.IssuerFingerprint...)
subpackets = append(subpackets, outputSubpacket{true, issuerFingerprintSubpacket, true, contents})
} }
if sig.SigLifetimeSecs != nil && *sig.SigLifetimeSecs != 0 { if sig.SigLifetimeSecs != nil && *sig.SigLifetimeSecs != 0 {
sigLifetime := make([]byte, 4) sigLifetime := make([]byte, 4)
binary.BigEndian.PutUint32(sigLifetime, *sig.SigLifetimeSecs) binary.BigEndian.PutUint32(sigLifetime, *sig.SigLifetimeSecs)
@ -703,7 +868,22 @@ func (sig *Signature) buildSubpackets() (subpackets []outputSubpacket) {
subpackets = append(subpackets, outputSubpacket{true, keyFlagsSubpacket, false, []byte{flags}}) subpackets = append(subpackets, outputSubpacket{true, keyFlagsSubpacket, false, []byte{flags}})
} }
// The following subpackets may only appear in self-signatures // The following subpackets may only appear in self-signatures.
var features = byte(0x00)
if sig.MDC {
features |= 0x01
}
if sig.AEAD {
features |= 0x02
}
if sig.V5Keys {
features |= 0x04
}
if features != 0x00 {
subpackets = append(subpackets, outputSubpacket{true, featuresSubpacket, false, []byte{features}})
}
if sig.KeyLifetimeSecs != nil && *sig.KeyLifetimeSecs != 0 { if sig.KeyLifetimeSecs != nil && *sig.KeyLifetimeSecs != 0 {
keyLifetime := make([]byte, 4) keyLifetime := make([]byte, 4)
@ -727,5 +907,77 @@ func (sig *Signature) buildSubpackets() (subpackets []outputSubpacket) {
subpackets = append(subpackets, outputSubpacket{true, prefCompressionSubpacket, false, sig.PreferredCompression}) subpackets = append(subpackets, outputSubpacket{true, prefCompressionSubpacket, false, sig.PreferredCompression})
} }
if len(sig.PolicyURI) > 0 {
subpackets = append(subpackets, outputSubpacket{true, policyUriSubpacket, false, []uint8(sig.PolicyURI)})
}
if len(sig.PreferredAEAD) > 0 {
subpackets = append(subpackets, outputSubpacket{true, prefAeadAlgosSubpacket, false, sig.PreferredAEAD})
}
// Revocation reason appears only in revocation signatures and is serialized as per section 5.2.3.23.
if sig.RevocationReason != nil {
subpackets = append(subpackets, outputSubpacket{true, reasonForRevocationSubpacket, true,
append([]uint8{*sig.RevocationReason}, []uint8(sig.RevocationReasonText)...)})
}
// EmbeddedSignature appears only in subkeys capable of signing and is serialized as per section 5.2.3.26.
if sig.EmbeddedSignature != nil {
var buf bytes.Buffer
err = sig.EmbeddedSignature.serializeBody(&buf)
if err != nil {
return
}
subpackets = append(subpackets, outputSubpacket{true, embeddedSignatureSubpacket, true, buf.Bytes()})
}
return return
} }
// AddMetadataToHashSuffix modifies the current hash suffix to include metadata
// (format, filename, and time). Version 5 keys protect this data including it
// in the hash computation. See section 5.2.4.
func (sig *Signature) AddMetadataToHashSuffix() {
if sig == nil || sig.Version != 5 {
return
}
if sig.SigType != 0x00 && sig.SigType != 0x01 {
return
}
lit := sig.Metadata
if lit == nil {
// This will translate into six 0x00 bytes.
lit = &LiteralData{}
}
// Extract the current byte count
n := sig.HashSuffix[len(sig.HashSuffix)-8:]
l := uint64(
uint64(n[0])<<56 | uint64(n[1])<<48 | uint64(n[2])<<40 | uint64(n[3])<<32 |
uint64(n[4])<<24 | uint64(n[5])<<16 | uint64(n[6])<<8 | uint64(n[7]))
suffix := bytes.NewBuffer(nil)
suffix.Write(sig.HashSuffix[:l])
// Add the metadata
var buf [4]byte
buf[0] = lit.Format
fileName := lit.FileName
if len(lit.FileName) > 255 {
fileName = fileName[:255]
}
buf[1] = byte(len(fileName))
suffix.Write(buf[:2])
suffix.Write([]byte(lit.FileName))
binary.BigEndian.PutUint32(buf[:], lit.Time)
suffix.Write(buf[:])
// Update the counter and restore trailing bytes
l = uint64(suffix.Len())
suffix.Write([]byte{0x05, 0xff})
suffix.Write([]byte{
uint8(l >> 56), uint8(l >> 48), uint8(l >> 40), uint8(l >> 32),
uint8(l >> 24), uint8(l >> 16), uint8(l >> 8), uint8(l),
})
sig.HashSuffix = suffix.Bytes()
}

View File

@ -0,0 +1,267 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package packet
import (
"bytes"
"crypto/cipher"
"io"
"strconv"
"github.com/ProtonMail/go-crypto/openpgp/errors"
"github.com/ProtonMail/go-crypto/openpgp/s2k"
)
// This is the largest session key that we'll support. Since no 512-bit cipher
// has even been seriously used, this is comfortably large.
const maxSessionKeySizeInBytes = 64
// SymmetricKeyEncrypted represents a passphrase protected session key. See RFC
// 4880, section 5.3.
type SymmetricKeyEncrypted struct {
Version int
CipherFunc CipherFunction
Mode AEADMode
s2k func(out, in []byte)
aeadNonce []byte
encryptedKey []byte
}
func (ske *SymmetricKeyEncrypted) parse(r io.Reader) error {
// RFC 4880, section 5.3.
var buf [2]byte
if _, err := readFull(r, buf[:]); err != nil {
return err
}
ske.Version = int(buf[0])
if ske.Version != 4 && ske.Version != 5 {
return errors.UnsupportedError("unknown SymmetricKeyEncrypted version")
}
ske.CipherFunc = CipherFunction(buf[1])
if ske.CipherFunc.KeySize() == 0 {
return errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(buf[1])))
}
if ske.Version == 5 {
mode := make([]byte, 1)
if _, err := r.Read(mode); err != nil {
return errors.StructuralError("cannot read AEAD octect from packet")
}
ske.Mode = AEADMode(mode[0])
}
var err error
if ske.s2k, err = s2k.Parse(r); err != nil {
if _, ok := err.(errors.ErrDummyPrivateKey); ok {
return errors.UnsupportedError("missing key GNU extension in session key")
}
return err
}
if ske.Version == 5 {
// AEAD nonce
nonce := make([]byte, ske.Mode.NonceLength())
_, err := readFull(r, nonce)
if err != nil && err != io.ErrUnexpectedEOF {
return err
}
ske.aeadNonce = nonce
}
encryptedKey := make([]byte, maxSessionKeySizeInBytes)
// The session key may follow. We just have to try and read to find
// out. If it exists then we limit it to maxSessionKeySizeInBytes.
n, err := readFull(r, encryptedKey)
if err != nil && err != io.ErrUnexpectedEOF {
return err
}
if n != 0 {
if n == maxSessionKeySizeInBytes {
return errors.UnsupportedError("oversized encrypted session key")
}
ske.encryptedKey = encryptedKey[:n]
}
return nil
}
// Decrypt attempts to decrypt an encrypted session key and returns the key and
// the cipher to use when decrypting a subsequent Symmetrically Encrypted Data
// packet.
func (ske *SymmetricKeyEncrypted) Decrypt(passphrase []byte) ([]byte, CipherFunction, error) {
key := make([]byte, ske.CipherFunc.KeySize())
ske.s2k(key, passphrase)
if len(ske.encryptedKey) == 0 {
return key, ske.CipherFunc, nil
}
switch ske.Version {
case 4:
plaintextKey, cipherFunc, err := ske.decryptV4(key)
return plaintextKey, cipherFunc, err
case 5:
plaintextKey, err := ske.decryptV5(key)
return plaintextKey, CipherFunction(0), err
}
err := errors.UnsupportedError("unknown SymmetricKeyEncrypted version")
return nil, CipherFunction(0), err
}
func (ske *SymmetricKeyEncrypted) decryptV4(key []byte) ([]byte, CipherFunction, error) {
// the IV is all zeros
iv := make([]byte, ske.CipherFunc.blockSize())
c := cipher.NewCFBDecrypter(ske.CipherFunc.new(key), iv)
plaintextKey := make([]byte, len(ske.encryptedKey))
c.XORKeyStream(plaintextKey, ske.encryptedKey)
cipherFunc := CipherFunction(plaintextKey[0])
if cipherFunc.blockSize() == 0 {
return nil, ske.CipherFunc, errors.UnsupportedError(
"unknown cipher: " + strconv.Itoa(int(cipherFunc)))
}
plaintextKey = plaintextKey[1:]
if len(plaintextKey) != cipherFunc.KeySize() {
return nil, cipherFunc, errors.StructuralError(
"length of decrypted key not equal to cipher keysize")
}
return plaintextKey, cipherFunc, nil
}
func (ske *SymmetricKeyEncrypted) decryptV5(key []byte) ([]byte, error) {
blockCipher := CipherFunction(ske.CipherFunc).new(key)
aead := ske.Mode.new(blockCipher)
adata := []byte{0xc3, byte(5), byte(ske.CipherFunc), byte(ske.Mode)}
plaintextKey, err := aead.Open(nil, ske.aeadNonce, ske.encryptedKey, adata)
if err != nil {
return nil, err
}
return plaintextKey, nil
}
// SerializeSymmetricKeyEncrypted serializes a symmetric key packet to w.
// The packet contains a random session key, encrypted by a key derived from
// the given passphrase. The session key is returned and must be passed to
// SerializeSymmetricallyEncrypted or SerializeAEADEncrypted, depending on
// whether config.AEADConfig != nil.
// If config is nil, sensible defaults will be used.
func SerializeSymmetricKeyEncrypted(w io.Writer, passphrase []byte, config *Config) (key []byte, err error) {
cipherFunc := config.Cipher()
keySize := cipherFunc.KeySize()
if keySize == 0 {
return nil, errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(cipherFunc)))
}
sessionKey := make([]byte, keySize)
_, err = io.ReadFull(config.Random(), sessionKey)
if err != nil {
return
}
err = SerializeSymmetricKeyEncryptedReuseKey(w, sessionKey, passphrase, config)
if err != nil {
return
}
key = sessionKey
return
}
// SerializeSymmetricKeyEncryptedReuseKey serializes a symmetric key packet to w.
// The packet contains the given session key, encrypted by a key derived from
// the given passphrase. The session key must be passed to
// SerializeSymmetricallyEncrypted or SerializeAEADEncrypted, depending on
// whether config.AEADConfig != nil.
// If config is nil, sensible defaults will be used.
func SerializeSymmetricKeyEncryptedReuseKey(w io.Writer, sessionKey []byte, passphrase []byte, config *Config) (err error) {
var version int
if config.AEAD() != nil {
version = 5
} else {
version = 4
}
cipherFunc := config.Cipher()
keySize := cipherFunc.KeySize()
if keySize == 0 {
return errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(cipherFunc)))
}
s2kBuf := new(bytes.Buffer)
keyEncryptingKey := make([]byte, keySize)
// s2k.Serialize salts and stretches the passphrase, and writes the
// resulting key to keyEncryptingKey and the s2k descriptor to s2kBuf.
err = s2k.Serialize(s2kBuf, keyEncryptingKey, config.Random(), passphrase, &s2k.Config{Hash: config.Hash(), S2KCount: config.PasswordHashIterations()})
if err != nil {
return
}
s2kBytes := s2kBuf.Bytes()
var packetLength int
switch version {
case 4:
packetLength = 2 /* header */ + len(s2kBytes) + 1 /* cipher type */ + keySize
case 5:
nonceLen := config.AEAD().Mode().NonceLength()
tagLen := config.AEAD().Mode().TagLength()
packetLength = 3 + len(s2kBytes) + nonceLen + keySize + tagLen
}
err = serializeHeader(w, packetTypeSymmetricKeyEncrypted, packetLength)
if err != nil {
return
}
buf := make([]byte, 2)
// Symmetric Key Encrypted Version
buf[0] = byte(version)
// Cipher function
buf[1] = byte(cipherFunc)
if version == 5 {
// AEAD mode
buf = append(buf, byte(config.AEAD().Mode()))
}
_, err = w.Write(buf)
if err != nil {
return
}
_, err = w.Write(s2kBytes)
if err != nil {
return
}
switch version {
case 4:
iv := make([]byte, cipherFunc.blockSize())
c := cipher.NewCFBEncrypter(cipherFunc.new(keyEncryptingKey), iv)
encryptedCipherAndKey := make([]byte, keySize+1)
c.XORKeyStream(encryptedCipherAndKey, buf[1:])
c.XORKeyStream(encryptedCipherAndKey[1:], sessionKey)
_, err = w.Write(encryptedCipherAndKey)
if err != nil {
return
}
case 5:
blockCipher := cipherFunc.new(keyEncryptingKey)
mode := config.AEAD().Mode()
aead := mode.new(blockCipher)
// Sample nonce using random reader
nonce := make([]byte, config.AEAD().Mode().NonceLength())
_, err = io.ReadFull(config.Random(), nonce)
if err != nil {
return
}
// Seal and write (encryptedData includes auth. tag)
adata := []byte{0xc3, byte(5), byte(cipherFunc), byte(mode)}
encryptedData := aead.Seal(nil, nonce, sessionKey, adata)
_, err = w.Write(nonce)
if err != nil {
return
}
_, err = w.Write(encryptedData)
if err != nil {
return
}
}
return
}

View File

@ -8,18 +8,19 @@ import (
"crypto/cipher" "crypto/cipher"
"crypto/sha1" "crypto/sha1"
"crypto/subtle" "crypto/subtle"
"golang.org/x/crypto/openpgp/errors"
"hash" "hash"
"io" "io"
"strconv" "strconv"
"github.com/ProtonMail/go-crypto/openpgp/errors"
) )
// SymmetricallyEncrypted represents a symmetrically encrypted byte string. The // SymmetricallyEncrypted represents a symmetrically encrypted byte string. The
// encrypted contents will consist of more OpenPGP packets. See RFC 4880, // encrypted Contents will consist of more OpenPGP packets. See RFC 4880,
// sections 5.7 and 5.13. // sections 5.7 and 5.13.
type SymmetricallyEncrypted struct { type SymmetricallyEncrypted struct {
MDC bool // true iff this is a type 18 packet and thus has an embedded MAC. MDC bool // true iff this is a type 18 packet and thus has an embedded MAC.
contents io.Reader Contents io.Reader
prefix []byte prefix []byte
} }
@ -37,13 +38,13 @@ func (se *SymmetricallyEncrypted) parse(r io.Reader) error {
return errors.UnsupportedError("unknown SymmetricallyEncrypted version") return errors.UnsupportedError("unknown SymmetricallyEncrypted version")
} }
} }
se.contents = r se.Contents = r
return nil return nil
} }
// Decrypt returns a ReadCloser, from which the decrypted contents of the // Decrypt returns a ReadCloser, from which the decrypted Contents of the
// packet can be read. An incorrect key can, with high probability, be detected // packet can be read. An incorrect key will only be detected after trying
// immediately and this will result in a KeyIncorrect error being returned. // to decrypt the entire data.
func (se *SymmetricallyEncrypted) Decrypt(c CipherFunction, key []byte) (io.ReadCloser, error) { func (se *SymmetricallyEncrypted) Decrypt(c CipherFunction, key []byte) (io.ReadCloser, error) {
keySize := c.KeySize() keySize := c.KeySize()
if keySize == 0 { if keySize == 0 {
@ -55,7 +56,7 @@ func (se *SymmetricallyEncrypted) Decrypt(c CipherFunction, key []byte) (io.Read
if se.prefix == nil { if se.prefix == nil {
se.prefix = make([]byte, c.blockSize()+2) se.prefix = make([]byte, c.blockSize()+2)
_, err := readFull(se.contents, se.prefix) _, err := readFull(se.Contents, se.prefix)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -70,11 +71,8 @@ func (se *SymmetricallyEncrypted) Decrypt(c CipherFunction, key []byte) (io.Read
} }
s := NewOCFBDecrypter(c.new(key), se.prefix, ocfbResync) s := NewOCFBDecrypter(c.new(key), se.prefix, ocfbResync)
if s == nil {
return nil, errors.ErrKeyIncorrect
}
plaintext := cipher.StreamReader{S: s, R: se.contents} plaintext := cipher.StreamReader{S: s, R: se.Contents}
if se.MDC { if se.MDC {
// MDC packets have an embedded hash that we need to check. // MDC packets have an embedded hash that we need to check.
@ -104,7 +102,7 @@ const mdcTrailerSize = 1 /* tag byte */ + 1 /* length byte */ + sha1.Size
// An seMDCReader wraps an io.Reader, maintains a running hash and keeps hold // An seMDCReader wraps an io.Reader, maintains a running hash and keeps hold
// of the most recent 22 bytes (mdcTrailerSize). Upon EOF, those bytes form an // of the most recent 22 bytes (mdcTrailerSize). Upon EOF, those bytes form an
// MDC packet containing a hash of the previous contents which is checked // MDC packet containing a hash of the previous Contents which is checked
// against the running hash. See RFC 4880, section 5.13. // against the running hash. See RFC 4880, section 5.13.
type seMDCReader struct { type seMDCReader struct {
in io.Reader in io.Reader
@ -180,7 +178,7 @@ const mdcPacketTagByte = byte(0x80) | 0x40 | 19
func (ser *seMDCReader) Close() error { func (ser *seMDCReader) Close() error {
if ser.error { if ser.error {
return errors.SignatureError("error during reading") return errors.ErrMDCMissing
} }
for !ser.eof { for !ser.eof {
@ -191,18 +189,20 @@ func (ser *seMDCReader) Close() error {
break break
} }
if err != nil { if err != nil {
return errors.SignatureError("error during reading") return errors.ErrMDCMissing
} }
} }
if ser.trailer[0] != mdcPacketTagByte || ser.trailer[1] != sha1.Size {
return errors.SignatureError("MDC packet not found")
}
ser.h.Write(ser.trailer[:2]) ser.h.Write(ser.trailer[:2])
final := ser.h.Sum(nil) final := ser.h.Sum(nil)
if subtle.ConstantTimeCompare(final, ser.trailer[2:]) != 1 { if subtle.ConstantTimeCompare(final, ser.trailer[2:]) != 1 {
return errors.SignatureError("hash mismatch") return errors.ErrMDCHashMismatch
}
// The hash already includes the MDC header, but we still check its value
// to confirm encryption correctness
if ser.trailer[0] != mdcPacketTagByte || ser.trailer[1] != sha1.Size {
return errors.ErrMDCMissing
} }
return nil return nil
} }
@ -253,7 +253,7 @@ func (c noOpCloser) Close() error {
// to w and returns a WriteCloser to which the to-be-encrypted packets can be // to w and returns a WriteCloser to which the to-be-encrypted packets can be
// written. // written.
// If config is nil, sensible defaults will be used. // If config is nil, sensible defaults will be used.
func SerializeSymmetricallyEncrypted(w io.Writer, c CipherFunction, key []byte, config *Config) (contents io.WriteCloser, err error) { func SerializeSymmetricallyEncrypted(w io.Writer, c CipherFunction, key []byte, config *Config) (Contents io.WriteCloser, err error) {
if c.KeySize() != len(key) { if c.KeySize() != len(key) {
return nil, errors.InvalidArgumentError("SymmetricallyEncrypted.Serialize: bad key length") return nil, errors.InvalidArgumentError("SymmetricallyEncrypted.Serialize: bad key length")
} }
@ -285,6 +285,6 @@ func SerializeSymmetricallyEncrypted(w io.Writer, c CipherFunction, key []byte,
h := sha1.New() h := sha1.New()
h.Write(iv) h.Write(iv)
h.Write(iv[blockSize-2:]) h.Write(iv[blockSize-2:])
contents = &seMDCWriter{w: plaintext, h: h} Contents = &seMDCWriter{w: plaintext, h: h}
return return
} }

View File

@ -69,7 +69,10 @@ func (uat *UserAttribute) parse(r io.Reader) (err error) {
func (uat *UserAttribute) Serialize(w io.Writer) (err error) { func (uat *UserAttribute) Serialize(w io.Writer) (err error) {
var buf bytes.Buffer var buf bytes.Buffer
for _, sp := range uat.Contents { for _, sp := range uat.Contents {
sp.Serialize(&buf) err = sp.Serialize(&buf)
if err != nil {
return err
}
} }
if err = serializeHeader(w, packetTypeUserAttribute, buf.Len()); err != nil { if err = serializeHeader(w, packetTypeUserAttribute, buf.Len()); err != nil {
return err return err

View File

@ -3,7 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// Package openpgp implements high level operations on OpenPGP messages. // Package openpgp implements high level operations on OpenPGP messages.
package openpgp // import "golang.org/x/crypto/openpgp" package openpgp // import "github.com/ProtonMail/go-crypto/openpgp"
import ( import (
"crypto" "crypto"
@ -12,9 +12,9 @@ import (
"io" "io"
"strconv" "strconv"
"golang.org/x/crypto/openpgp/armor" "github.com/ProtonMail/go-crypto/openpgp/armor"
"golang.org/x/crypto/openpgp/errors" "github.com/ProtonMail/go-crypto/openpgp/errors"
"golang.org/x/crypto/openpgp/packet" "github.com/ProtonMail/go-crypto/openpgp/packet"
) )
// SignatureType is the armor type for a PGP signature. // SignatureType is the armor type for a PGP signature.
@ -56,9 +56,9 @@ type MessageDetails struct {
// been consumed. Once EOF has been seen, the following fields are // been consumed. Once EOF has been seen, the following fields are
// valid. (An authentication code failure is reported as a // valid. (An authentication code failure is reported as a
// SignatureError error when reading from UnverifiedBody.) // SignatureError error when reading from UnverifiedBody.)
Signature *packet.Signature // the signature packet itself.
SignatureError error // nil if the signature is good. SignatureError error // nil if the signature is good.
Signature *packet.Signature // the signature packet itself, if v4 (default) UnverifiedSignatures []*packet.Signature // all other unverified signature packets.
SignatureV3 *packet.SignatureV3 // the signature packet if it is a v2 or v3 signature
decrypted io.ReadCloser decrypted io.ReadCloser
} }
@ -88,7 +88,8 @@ func ReadMessage(r io.Reader, keyring KeyRing, prompt PromptFunction, config *pa
var symKeys []*packet.SymmetricKeyEncrypted var symKeys []*packet.SymmetricKeyEncrypted
var pubKeys []keyEnvelopePair var pubKeys []keyEnvelopePair
var se *packet.SymmetricallyEncrypted // Integrity protected encrypted packet: SymmetricallyEncrypted or AEADEncrypted
var edp packet.EncryptedDataPacket
packets := packet.NewReader(r) packets := packet.NewReader(r)
md = new(MessageDetails) md = new(MessageDetails)
@ -113,7 +114,7 @@ ParsePackets:
// This packet contains the decryption key encrypted to a public key. // This packet contains the decryption key encrypted to a public key.
md.EncryptedToKeyIds = append(md.EncryptedToKeyIds, p.KeyId) md.EncryptedToKeyIds = append(md.EncryptedToKeyIds, p.KeyId)
switch p.Algo { switch p.Algo {
case packet.PubKeyAlgoRSA, packet.PubKeyAlgoRSAEncryptOnly, packet.PubKeyAlgoElGamal: case packet.PubKeyAlgoRSA, packet.PubKeyAlgoRSAEncryptOnly, packet.PubKeyAlgoElGamal, packet.PubKeyAlgoECDH:
break break
default: default:
continue continue
@ -127,8 +128,8 @@ ParsePackets:
for _, k := range keys { for _, k := range keys {
pubKeys = append(pubKeys, keyEnvelopePair{k, p}) pubKeys = append(pubKeys, keyEnvelopePair{k, p})
} }
case *packet.SymmetricallyEncrypted: case *packet.SymmetricallyEncrypted, *packet.AEADEncrypted:
se = p edp = p.(packet.EncryptedDataPacket)
break ParsePackets break ParsePackets
case *packet.Compressed, *packet.LiteralData, *packet.OnePassSignature: case *packet.Compressed, *packet.LiteralData, *packet.OnePassSignature:
// This message isn't encrypted. // This message isn't encrypted.
@ -136,7 +137,7 @@ ParsePackets:
return nil, errors.StructuralError("key material not followed by encrypted message") return nil, errors.StructuralError("key material not followed by encrypted message")
} }
packets.Unread(p) packets.Unread(p)
return readSignedMessage(packets, nil, keyring) return readSignedMessage(packets, nil, keyring, config)
} }
} }
@ -158,12 +159,13 @@ FindKey:
} }
if !pk.key.PrivateKey.Encrypted { if !pk.key.PrivateKey.Encrypted {
if len(pk.encryptedKey.Key) == 0 { if len(pk.encryptedKey.Key) == 0 {
pk.encryptedKey.Decrypt(pk.key.PrivateKey, config) errDec := pk.encryptedKey.Decrypt(pk.key.PrivateKey, config)
} if errDec != nil {
if len(pk.encryptedKey.Key) == 0 {
continue continue
} }
decrypted, err = se.Decrypt(pk.encryptedKey.CipherFunc, pk.encryptedKey.Key) }
// Try to decrypt symmetrically encrypted
decrypted, err = edp.Decrypt(pk.encryptedKey.CipherFunc, pk.encryptedKey.Key)
if err != nil && err != errors.ErrKeyIncorrect { if err != nil && err != errors.ErrKeyIncorrect {
return nil, err return nil, err
} }
@ -198,8 +200,12 @@ FindKey:
if len(symKeys) != 0 && passphrase != nil { if len(symKeys) != 0 && passphrase != nil {
for _, s := range symKeys { for _, s := range symKeys {
key, cipherFunc, err := s.Decrypt(passphrase) key, cipherFunc, err := s.Decrypt(passphrase)
// On wrong passphrase, session key decryption is very likely to result in an invalid cipherFunc:
// only for < 5% of cases we will proceed to decrypt the data
if err == nil { if err == nil {
decrypted, err = se.Decrypt(cipherFunc, key) decrypted, err = edp.Decrypt(cipherFunc, key)
// TODO: ErrKeyIncorrect is no longer thrown on SEIP decryption,
// but it might still be relevant for when we implement AEAD decryption (otherwise, remove?)
if err != nil && err != errors.ErrKeyIncorrect { if err != nil && err != errors.ErrKeyIncorrect {
return nil, err return nil, err
} }
@ -207,7 +213,6 @@ FindKey:
break FindKey break FindKey
} }
} }
} }
} }
} }
@ -216,13 +221,17 @@ FindKey:
if err := packets.Push(decrypted); err != nil { if err := packets.Push(decrypted); err != nil {
return nil, err return nil, err
} }
return readSignedMessage(packets, md, keyring) mdFinal, sensitiveParsingErr := readSignedMessage(packets, md, keyring, config)
if sensitiveParsingErr != nil {
return nil, errors.StructuralError("parsing error")
}
return mdFinal, nil
} }
// readSignedMessage reads a possibly signed message if mdin is non-zero then // readSignedMessage reads a possibly signed message if mdin is non-zero then
// that structure is updated and returned. Otherwise a fresh MessageDetails is // that structure is updated and returned. Otherwise a fresh MessageDetails is
// used. // used.
func readSignedMessage(packets *packet.Reader, mdin *MessageDetails, keyring KeyRing) (md *MessageDetails, err error) { func readSignedMessage(packets *packet.Reader, mdin *MessageDetails, keyring KeyRing, config *packet.Config) (md *MessageDetails, err error) {
if mdin == nil { if mdin == nil {
mdin = new(MessageDetails) mdin = new(MessageDetails)
} }
@ -231,6 +240,7 @@ func readSignedMessage(packets *packet.Reader, mdin *MessageDetails, keyring Key
var p packet.Packet var p packet.Packet
var h hash.Hash var h hash.Hash
var wrappedHash hash.Hash var wrappedHash hash.Hash
var prevLast bool
FindLiteralData: FindLiteralData:
for { for {
p, err = packets.Next() p, err = packets.Next()
@ -243,14 +253,17 @@ FindLiteralData:
return nil, err return nil, err
} }
case *packet.OnePassSignature: case *packet.OnePassSignature:
if !p.IsLast { if prevLast {
return nil, errors.UnsupportedError("nested signatures") return nil, errors.UnsupportedError("nested signature packets")
}
if p.IsLast {
prevLast = true
} }
h, wrappedHash, err = hashForSignature(p.Hash, p.SigType) h, wrappedHash, err = hashForSignature(p.Hash, p.SigType)
if err != nil { if err != nil {
md = nil md.SignatureError = err
return
} }
md.IsSigned = true md.IsSigned = true
@ -265,8 +278,8 @@ FindLiteralData:
} }
} }
if md.SignedBy != nil { if md.IsSigned && md.SignatureError == nil {
md.UnverifiedBody = &signatureCheckReader{packets, h, wrappedHash, md} md.UnverifiedBody = &signatureCheckReader{packets, h, wrappedHash, md, config}
} else if md.decrypted != nil { } else if md.decrypted != nil {
md.UnverifiedBody = checkReader{md} md.UnverifiedBody = checkReader{md}
} else { } else {
@ -282,6 +295,9 @@ FindLiteralData:
// returns two hashes. The second should be used to hash the message itself and // returns two hashes. The second should be used to hash the message itself and
// performs any needed preprocessing. // performs any needed preprocessing.
func hashForSignature(hashId crypto.Hash, sigType packet.SignatureType) (hash.Hash, hash.Hash, error) { func hashForSignature(hashId crypto.Hash, sigType packet.SignatureType) (hash.Hash, hash.Hash, error) {
if hashId == crypto.MD5 {
return nil, nil, errors.UnsupportedError("insecure hash algorithm: MD5")
}
if !hashId.Available() { if !hashId.Available() {
return nil, nil, errors.UnsupportedError("hash not available: " + strconv.Itoa(int(hashId))) return nil, nil, errors.UnsupportedError("hash not available: " + strconv.Itoa(int(hashId)))
} }
@ -304,15 +320,21 @@ type checkReader struct {
md *MessageDetails md *MessageDetails
} }
func (cr checkReader) Read(buf []byte) (n int, err error) { func (cr checkReader) Read(buf []byte) (int, error) {
n, err = cr.md.LiteralData.Body.Read(buf) n, sensitiveParsingError := cr.md.LiteralData.Body.Read(buf)
if err == io.EOF { if sensitiveParsingError == io.EOF {
mdcErr := cr.md.decrypted.Close() mdcErr := cr.md.decrypted.Close()
if mdcErr != nil { if mdcErr != nil {
err = mdcErr return n, mdcErr
} }
return n, io.EOF
} }
return
if sensitiveParsingError != nil {
return n, errors.StructuralError("parsing error")
}
return n, nil
} }
// signatureCheckReader wraps an io.Reader from a LiteralData packet and hashes // signatureCheckReader wraps an io.Reader from a LiteralData packet and hashes
@ -322,26 +344,51 @@ type signatureCheckReader struct {
packets *packet.Reader packets *packet.Reader
h, wrappedHash hash.Hash h, wrappedHash hash.Hash
md *MessageDetails md *MessageDetails
config *packet.Config
} }
func (scr *signatureCheckReader) Read(buf []byte) (n int, err error) { func (scr *signatureCheckReader) Read(buf []byte) (int, error) {
n, err = scr.md.LiteralData.Body.Read(buf) n, sensitiveParsingError := scr.md.LiteralData.Body.Read(buf)
// Hash only if required
if scr.md.SignedBy != nil {
scr.wrappedHash.Write(buf[:n]) scr.wrappedHash.Write(buf[:n])
if err == io.EOF {
var p packet.Packet
p, scr.md.SignatureError = scr.packets.Next()
if scr.md.SignatureError != nil {
return
} }
if sensitiveParsingError == io.EOF {
var p packet.Packet
var readError error
var sig *packet.Signature
p, readError = scr.packets.Next()
for readError == nil {
var ok bool var ok bool
if scr.md.Signature, ok = p.(*packet.Signature); ok { if sig, ok = p.(*packet.Signature); ok {
if sig.Version == 5 && (sig.SigType == 0x00 || sig.SigType == 0x01) {
sig.Metadata = scr.md.LiteralData
}
// If signature KeyID matches
if scr.md.SignedBy != nil && *sig.IssuerKeyId == scr.md.SignedByKeyId {
scr.md.Signature = sig
scr.md.SignatureError = scr.md.SignedBy.PublicKey.VerifySignature(scr.h, scr.md.Signature) scr.md.SignatureError = scr.md.SignedBy.PublicKey.VerifySignature(scr.h, scr.md.Signature)
} else if scr.md.SignatureV3, ok = p.(*packet.SignatureV3); ok { if scr.md.SignatureError == nil && scr.md.Signature.SigExpired(scr.config.Now()) {
scr.md.SignatureError = scr.md.SignedBy.PublicKey.VerifySignatureV3(scr.h, scr.md.SignatureV3) scr.md.SignatureError = errors.ErrSignatureExpired
}
} else { } else {
scr.md.SignatureError = errors.StructuralError("LiteralData not followed by Signature") scr.md.UnverifiedSignatures = append(scr.md.UnverifiedSignatures, sig)
return }
}
p, readError = scr.packets.Next()
}
if scr.md.SignedBy != nil && scr.md.Signature == nil {
if scr.md.UnverifiedSignatures == nil {
scr.md.SignatureError = errors.StructuralError("LiteralData not followed by signature")
} else {
scr.md.SignatureError = errors.StructuralError("No matching signature found")
}
} }
// The SymmetricallyEncrypted packet, if any, might have an // The SymmetricallyEncrypted packet, if any, might have an
@ -350,24 +397,39 @@ func (scr *signatureCheckReader) Read(buf []byte) (n int, err error) {
if scr.md.decrypted != nil { if scr.md.decrypted != nil {
mdcErr := scr.md.decrypted.Close() mdcErr := scr.md.decrypted.Close()
if mdcErr != nil { if mdcErr != nil {
err = mdcErr return n, mdcErr
} }
} }
return n, io.EOF
} }
return
if sensitiveParsingError != nil {
return n, errors.StructuralError("parsing error")
}
return n, nil
} }
// CheckDetachedSignature takes a signed file and a detached signature and // CheckDetachedSignature takes a signed file and a detached signature and
// returns the signer if the signature is valid. If the signer isn't known, // returns the signer if the signature is valid. If the signer isn't known,
// ErrUnknownIssuer is returned. // ErrUnknownIssuer is returned.
func CheckDetachedSignature(keyring KeyRing, signed, signature io.Reader) (signer *Entity, err error) { func CheckDetachedSignature(keyring KeyRing, signed, signature io.Reader, config *packet.Config) (signer *Entity, err error) {
var expectedHashes []crypto.Hash
return CheckDetachedSignatureAndHash(keyring, signed, signature, expectedHashes, config)
}
// CheckDetachedSignatureAndHash performs the same actions as
// CheckDetachedSignature and checks that the expected hash functions were used.
func CheckDetachedSignatureAndHash(keyring KeyRing, signed, signature io.Reader, expectedHashes []crypto.Hash, config *packet.Config) (signer *Entity, err error) {
var issuerKeyId uint64 var issuerKeyId uint64
var hashFunc crypto.Hash var hashFunc crypto.Hash
var sigType packet.SignatureType var sigType packet.SignatureType
var keys []Key var keys []Key
var p packet.Packet var p packet.Packet
expectedHashesLen := len(expectedHashes)
packets := packet.NewReader(signature) packets := packet.NewReader(signature)
var sig *packet.Signature
for { for {
p, err = packets.Next() p, err = packets.Next()
if err == io.EOF { if err == io.EOF {
@ -377,20 +439,25 @@ func CheckDetachedSignature(keyring KeyRing, signed, signature io.Reader) (signe
return nil, err return nil, err
} }
switch sig := p.(type) { var ok bool
case *packet.Signature: sig, ok = p.(*packet.Signature)
if !ok {
return nil, errors.StructuralError("non signature packet found")
}
if sig.IssuerKeyId == nil { if sig.IssuerKeyId == nil {
return nil, errors.StructuralError("signature doesn't have an issuer") return nil, errors.StructuralError("signature doesn't have an issuer")
} }
issuerKeyId = *sig.IssuerKeyId issuerKeyId = *sig.IssuerKeyId
hashFunc = sig.Hash hashFunc = sig.Hash
sigType = sig.SigType sigType = sig.SigType
case *packet.SignatureV3:
issuerKeyId = sig.IssuerKeyId for i, expectedHash := range expectedHashes {
hashFunc = sig.Hash if hashFunc == expectedHash {
sigType = sig.SigType break
default: }
return nil, errors.StructuralError("non signature packet found") if i+1 == expectedHashesLen {
return nil, errors.StructuralError("hash algorithm mismatch with cleartext message headers")
}
} }
keys = keyring.KeysByIdUsage(issuerKeyId, packet.KeyFlagSign) keys = keyring.KeysByIdUsage(issuerKeyId, packet.KeyFlagSign)
@ -413,16 +480,15 @@ func CheckDetachedSignature(keyring KeyRing, signed, signature io.Reader) (signe
} }
for _, key := range keys { for _, key := range keys {
switch sig := p.(type) {
case *packet.Signature:
err = key.PublicKey.VerifySignature(h, sig) err = key.PublicKey.VerifySignature(h, sig)
case *packet.SignatureV3:
err = key.PublicKey.VerifySignatureV3(h, sig)
default:
panic("unreachable")
}
if err == nil { if err == nil {
now := config.Now()
if sig.SigExpired(now) {
return key.Entity, errors.ErrSignatureExpired
}
if key.PublicKey.KeyExpired(key.SelfSignature, now) {
return key.Entity, errors.ErrKeyExpired
}
return key.Entity, nil return key.Entity, nil
} }
} }
@ -432,11 +498,11 @@ func CheckDetachedSignature(keyring KeyRing, signed, signature io.Reader) (signe
// CheckArmoredDetachedSignature performs the same actions as // CheckArmoredDetachedSignature performs the same actions as
// CheckDetachedSignature but expects the signature to be armored. // CheckDetachedSignature but expects the signature to be armored.
func CheckArmoredDetachedSignature(keyring KeyRing, signed, signature io.Reader) (signer *Entity, err error) { func CheckArmoredDetachedSignature(keyring KeyRing, signed, signature io.Reader, config *packet.Config) (signer *Entity, err error) {
body, err := readArmored(signature, SignatureType) body, err := readArmored(signature, SignatureType)
if err != nil { if err != nil {
return return
} }
return CheckDetachedSignature(keyring, signed, body) return CheckDetachedSignature(keyring, signed, body, config)
} }

File diff suppressed because one or more lines are too long

View File

@ -4,7 +4,7 @@
// Package s2k implements the various OpenPGP string-to-key transforms as // Package s2k implements the various OpenPGP string-to-key transforms as
// specified in RFC 4800 section 3.7.1. // specified in RFC 4800 section 3.7.1.
package s2k // import "golang.org/x/crypto/openpgp/s2k" package s2k // import "github.com/ProtonMail/go-crypto/openpgp/s2k"
import ( import (
"crypto" "crypto"
@ -12,49 +12,67 @@ import (
"io" "io"
"strconv" "strconv"
"golang.org/x/crypto/openpgp/errors" "github.com/ProtonMail/go-crypto/openpgp/errors"
"github.com/ProtonMail/go-crypto/openpgp/internal/algorithm"
) )
// Config collects configuration parameters for s2k key-stretching // Config collects configuration parameters for s2k key-stretching
// transformatioms. A nil *Config is valid and results in all default // transformations. A nil *Config is valid and results in all default
// values. Currently, Config is used only by the Serialize function in // values. Currently, Config is used only by the Serialize function in
// this package. // this package.
type Config struct { type Config struct {
// S2KMode is the mode of s2k function.
// It can be 0 (simple), 1(salted), 3(iterated)
// 2(reserved) 100-110(private/experimental).
S2KMode uint8
// Hash is the default hash function to be used. If // Hash is the default hash function to be used. If
// nil, SHA1 is used. // nil, SHA256 is used.
Hash crypto.Hash Hash crypto.Hash
// S2KCount is only used for symmetric encryption. It // S2KCount is only used for symmetric encryption. It
// determines the strength of the passphrase stretching when // determines the strength of the passphrase stretching when
// the said passphrase is hashed to produce a key. S2KCount // the said passphrase is hashed to produce a key. S2KCount
// should be between 1024 and 65011712, inclusive. If Config // should be between 65536 and 65011712, inclusive. If Config
// is nil or S2KCount is 0, the value 65536 used. Not all // is nil or S2KCount is 0, the value 16777216 used. Not all
// values in the above range can be represented. S2KCount will // values in the above range can be represented. S2KCount will
// be rounded up to the next representable value if it cannot // be rounded up to the next representable value if it cannot
// be encoded exactly. When set, it is strongly encrouraged to // be encoded exactly. See RFC 4880 Section 3.7.1.3.
// use a value that is at least 65536. See RFC 4880 Section
// 3.7.1.3.
S2KCount int S2KCount int
} }
// Params contains all the parameters of the s2k packet
type Params struct {
// mode is the mode of s2k function.
// It can be 0 (simple), 1(salted), 3(iterated)
// 2(reserved) 100-110(private/experimental).
mode uint8
// hashId is the ID of the hash function used in any of the modes
hashId byte
// salt is a byte array to use as a salt in hashing process
salt []byte
// countByte is used to determine how many rounds of hashing are to
// be performed in s2k mode 3. See RFC 4880 Section 3.7.1.3.
countByte byte
}
func (c *Config) hash() crypto.Hash { func (c *Config) hash() crypto.Hash {
if c == nil || uint(c.Hash) == 0 { if c == nil || uint(c.Hash) == 0 {
// SHA1 is the historical default in this package. return crypto.SHA256
return crypto.SHA1
} }
return c.Hash return c.Hash
} }
func (c *Config) encodedCount() uint8 { // EncodedCount get encoded count
func (c *Config) EncodedCount() uint8 {
if c == nil || c.S2KCount == 0 { if c == nil || c.S2KCount == 0 {
return 96 // The common case. Correspoding to 65536 return 224 // The common case. Corresponding to 16777216
} }
i := c.S2KCount i := c.S2KCount
switch { switch {
// Behave like GPG. Should we make 65536 the lowest value used? case i < 65536:
case i < 1024: i = 65536
i = 1024
case i > 65011712: case i > 65011712:
i = 65011712 i = 65011712
} }
@ -68,11 +86,11 @@ func (c *Config) encodedCount() uint8 {
// if i is not in the above range (encodedCount above takes care to // if i is not in the above range (encodedCount above takes care to
// pass i in the correct range). See RFC 4880 Section 3.7.7.1. // pass i in the correct range). See RFC 4880 Section 3.7.7.1.
func encodeCount(i int) uint8 { func encodeCount(i int) uint8 {
if i < 1024 || i > 65011712 { if i < 65536 || i > 65011712 {
panic("count arg i outside the required range") panic("count arg i outside the required range")
} }
for encoded := 0; encoded < 256; encoded++ { for encoded := 96; encoded < 256; encoded++ {
count := decodeCount(uint8(encoded)) count := decodeCount(uint8(encoded))
if count >= i { if count >= i {
return uint8(encoded) return uint8(encoded)
@ -151,9 +169,44 @@ func Iterated(out []byte, h hash.Hash, in []byte, salt []byte, count int) {
} }
} }
// Generate generates valid parameters from given configuration.
// It will enforce salted + hashed s2k method
func Generate(rand io.Reader, c *Config) (*Params, error) {
hashId, ok := HashToHashId(c.Hash)
if !ok {
return nil, errors.UnsupportedError("no such hash")
}
params := &Params{
mode: 3, // Enforce iterared + salted method
hashId: hashId,
salt: make([]byte, 8),
countByte: c.EncodedCount(),
}
if _, err := io.ReadFull(rand, params.salt); err != nil {
return nil, err
}
return params, nil
}
// Parse reads a binary specification for a string-to-key transformation from r // Parse reads a binary specification for a string-to-key transformation from r
// and returns a function which performs that transform. // and returns a function which performs that transform. If the S2K is a special
// GNU extension that indicates that the private key is missing, then the error
// returned is errors.ErrDummyPrivateKey.
func Parse(r io.Reader) (f func(out, in []byte), err error) { func Parse(r io.Reader) (f func(out, in []byte), err error) {
params, err := ParseIntoParams(r)
if err != nil {
return nil, err
}
return params.Function()
}
// ParseIntoParams reads a binary specification for a string-to-key
// transformation from r and returns a struct describing the s2k parameters.
func ParseIntoParams(r io.Reader) (params *Params, err error) {
var buf [9]byte var buf [9]byte
_, err = io.ReadFull(r, buf[:2]) _, err = io.ReadFull(r, buf[:2])
@ -161,91 +214,135 @@ func Parse(r io.Reader) (f func(out, in []byte), err error) {
return return
} }
hash, ok := HashIdToHash(buf[1]) params = &Params{
if !ok { mode: buf[0],
return nil, errors.UnsupportedError("hash for S2K function: " + strconv.Itoa(int(buf[1]))) hashId: buf[1],
} }
if !hash.Available() {
return nil, errors.UnsupportedError("hash not available: " + strconv.Itoa(int(hash)))
}
h := hash.New()
switch buf[0] { switch params.mode {
case 0: case 0:
f := func(out, in []byte) { return params, nil
Simple(out, h, in)
}
return f, nil
case 1: case 1:
_, err = io.ReadFull(r, buf[:8]) _, err = io.ReadFull(r, buf[:8])
if err != nil { if err != nil {
return return nil, err
} }
f := func(out, in []byte) {
Salted(out, h, in, buf[:8]) params.salt = buf[:8]
} return params, nil
return f, nil
case 3: case 3:
_, err = io.ReadFull(r, buf[:9]) _, err = io.ReadFull(r, buf[:9])
if err != nil { if err != nil {
return return nil, err
} }
count := decodeCount(buf[8])
params.salt = buf[:8]
params.countByte = buf[8]
return params, nil
case 101:
// This is a GNU extension. See
// https://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=blob;f=doc/DETAILS;h=fe55ae16ab4e26d8356dc574c9e8bc935e71aef1;hb=23191d7851eae2217ecdac6484349849a24fd94a#l1109
if _, err = io.ReadFull(r, buf[:4]); err != nil {
return nil, err
}
if buf[0] == 'G' && buf[1] == 'N' && buf[2] == 'U' && buf[3] == 1 {
return params, nil
}
return nil, errors.UnsupportedError("GNU S2K extension")
}
return nil, errors.UnsupportedError("S2K function")
}
func (params *Params) Dummy() bool {
return params != nil && params.mode == 101
}
func (params *Params) Function() (f func(out, in []byte), err error) {
if params.Dummy() {
return nil, errors.ErrDummyPrivateKey("dummy key found")
}
hashObj, ok := HashIdToHash(params.hashId)
if !ok {
return nil, errors.UnsupportedError("hash for S2K function: " + strconv.Itoa(int(params.hashId)))
}
if !hashObj.Available() {
return nil, errors.UnsupportedError("hash not available: " + strconv.Itoa(int(hashObj)))
}
switch params.mode {
case 0:
f := func(out, in []byte) { f := func(out, in []byte) {
Iterated(out, h, in, buf[:8], count) Simple(out, hashObj.New(), in)
} }
return f, nil
case 1:
f := func(out, in []byte) {
Salted(out, hashObj.New(), in, params.salt)
}
return f, nil
case 3:
f := func(out, in []byte) {
Iterated(out, hashObj.New(), in, params.salt, decodeCount(params.countByte))
}
return f, nil return f, nil
} }
return nil, errors.UnsupportedError("S2K function") return nil, errors.UnsupportedError("S2K function")
} }
func (params *Params) Serialize(w io.Writer) (err error) {
if _, err = w.Write([]byte{params.mode}); err != nil {
return
}
if _, err = w.Write([]byte{params.hashId}); err != nil {
return
}
if params.Dummy() {
_, err = w.Write(append([]byte("GNU"), 1))
return
}
if params.mode > 0 {
if _, err = w.Write(params.salt); err != nil {
return
}
if params.mode == 3 {
_, err = w.Write([]byte{params.countByte})
}
}
return
}
// Serialize salts and stretches the given passphrase and writes the // Serialize salts and stretches the given passphrase and writes the
// resulting key into key. It also serializes an S2K descriptor to // resulting key into key. It also serializes an S2K descriptor to
// w. The key stretching can be configured with c, which may be // w. The key stretching can be configured with c, which may be
// nil. In that case, sensible defaults will be used. // nil. In that case, sensible defaults will be used.
func Serialize(w io.Writer, key []byte, rand io.Reader, passphrase []byte, c *Config) error { func Serialize(w io.Writer, key []byte, rand io.Reader, passphrase []byte, c *Config) error {
var buf [11]byte params, err := Generate(rand, c)
buf[0] = 3 /* iterated and salted */ if err != nil {
buf[1], _ = HashToHashId(c.hash())
salt := buf[2:10]
if _, err := io.ReadFull(rand, salt); err != nil {
return err return err
} }
encodedCount := c.encodedCount() err = params.Serialize(w)
count := decodeCount(encodedCount) if err != nil {
buf[10] = encodedCount
if _, err := w.Write(buf[:]); err != nil {
return err return err
} }
Iterated(key, c.hash().New(), passphrase, salt, count) f, err := params.Function()
if err != nil {
return err
}
f(key, passphrase)
return nil return nil
} }
// hashToHashIdMapping contains pairs relating OpenPGP's hash identifier with
// Go's crypto.Hash type. See RFC 4880, section 9.4.
var hashToHashIdMapping = []struct {
id byte
hash crypto.Hash
name string
}{
{1, crypto.MD5, "MD5"},
{2, crypto.SHA1, "SHA1"},
{3, crypto.RIPEMD160, "RIPEMD160"},
{8, crypto.SHA256, "SHA256"},
{9, crypto.SHA384, "SHA384"},
{10, crypto.SHA512, "SHA512"},
{11, crypto.SHA224, "SHA224"},
}
// HashIdToHash returns a crypto.Hash which corresponds to the given OpenPGP // HashIdToHash returns a crypto.Hash which corresponds to the given OpenPGP
// hash id. // hash id.
func HashIdToHash(id byte) (h crypto.Hash, ok bool) { func HashIdToHash(id byte) (h crypto.Hash, ok bool) {
for _, m := range hashToHashIdMapping { if hash, ok := algorithm.HashById[id]; ok {
if m.id == id { return hash.HashFunc(), true
return m.hash, true
}
} }
return 0, false return 0, false
} }
@ -253,20 +350,17 @@ func HashIdToHash(id byte) (h crypto.Hash, ok bool) {
// HashIdToString returns the name of the hash function corresponding to the // HashIdToString returns the name of the hash function corresponding to the
// given OpenPGP hash id. // given OpenPGP hash id.
func HashIdToString(id byte) (name string, ok bool) { func HashIdToString(id byte) (name string, ok bool) {
for _, m := range hashToHashIdMapping { if hash, ok := algorithm.HashById[id]; ok {
if m.id == id { return hash.String(), true
return m.name, true
} }
}
return "", false return "", false
} }
// HashIdToHash returns an OpenPGP hash id which corresponds the given Hash. // HashIdToHash returns an OpenPGP hash id which corresponds the given Hash.
func HashToHashId(h crypto.Hash) (id byte, ok bool) { func HashToHashId(h crypto.Hash) (id byte, ok bool) {
for _, m := range hashToHashIdMapping { for id, hash := range algorithm.HashById {
if m.hash == h { if hash.HashFunc() == h {
return m.id, true return id, true
} }
} }
return 0, false return 0, false

View File

@ -11,10 +11,10 @@ import (
"strconv" "strconv"
"time" "time"
"golang.org/x/crypto/openpgp/armor" "github.com/ProtonMail/go-crypto/openpgp/armor"
"golang.org/x/crypto/openpgp/errors" "github.com/ProtonMail/go-crypto/openpgp/errors"
"golang.org/x/crypto/openpgp/packet" "github.com/ProtonMail/go-crypto/openpgp/packet"
"golang.org/x/crypto/openpgp/s2k" "github.com/ProtonMail/go-crypto/openpgp/s2k"
) )
// DetachSign signs message with the private key from signer (which must // DetachSign signs message with the private key from signer (which must
@ -60,27 +60,35 @@ func armoredDetachSign(w io.Writer, signer *Entity, message io.Reader, sigType p
} }
func detachSign(w io.Writer, signer *Entity, message io.Reader, sigType packet.SignatureType, config *packet.Config) (err error) { func detachSign(w io.Writer, signer *Entity, message io.Reader, sigType packet.SignatureType, config *packet.Config) (err error) {
if signer.PrivateKey == nil { signingKey, ok := signer.SigningKeyById(config.Now(), config.SigningKey())
if !ok {
return errors.InvalidArgumentError("no valid signing keys")
}
if signingKey.PrivateKey == nil {
return errors.InvalidArgumentError("signing key doesn't have a private key") return errors.InvalidArgumentError("signing key doesn't have a private key")
} }
if signer.PrivateKey.Encrypted { if signingKey.PrivateKey.Encrypted {
return errors.InvalidArgumentError("signing key is encrypted") return errors.InvalidArgumentError("signing key is encrypted")
} }
sig := new(packet.Signature) sig := new(packet.Signature)
sig.SigType = sigType sig.SigType = sigType
sig.PubKeyAlgo = signer.PrivateKey.PubKeyAlgo sig.PubKeyAlgo = signingKey.PrivateKey.PubKeyAlgo
sig.Hash = config.Hash() sig.Hash = config.Hash()
sig.CreationTime = config.Now() sig.CreationTime = config.Now()
sig.IssuerKeyId = &signer.PrivateKey.KeyId sigLifetimeSecs := config.SigLifetime()
sig.SigLifetimeSecs = &sigLifetimeSecs
sig.IssuerKeyId = &signingKey.PrivateKey.KeyId
h, wrappedHash, err := hashForSignature(sig.Hash, sig.SigType) h, wrappedHash, err := hashForSignature(sig.Hash, sig.SigType)
if err != nil { if err != nil {
return return
} }
io.Copy(wrappedHash, message) if _, err = io.Copy(wrappedHash, message); err != nil {
return err
}
err = sig.Sign(h, signer.PrivateKey, config) err = sig.Sign(h, signingKey.PrivateKey, config)
if err != nil { if err != nil {
return return
} }
@ -115,18 +123,27 @@ func SymmetricallyEncrypt(ciphertext io.Writer, passphrase []byte, hints *FileHi
if err != nil { if err != nil {
return return
} }
w, err := packet.SerializeSymmetricallyEncrypted(ciphertext, config.Cipher(), key, config)
var w io.WriteCloser
if config.AEAD() != nil {
w, err = packet.SerializeAEADEncrypted(ciphertext, key, config.Cipher(), config.AEAD().Mode(), config)
if err != nil { if err != nil {
return return
} }
} else {
w, err = packet.SerializeSymmetricallyEncrypted(ciphertext, config.Cipher(), key, config)
if err != nil {
return
}
}
literaldata := w literalData := w
if algo := config.Compression(); algo != packet.CompressionNone { if algo := config.Compression(); algo != packet.CompressionNone {
var compConfig *packet.CompressionConfig var compConfig *packet.CompressionConfig
if config != nil { if config != nil {
compConfig = config.CompressionConfig compConfig = config.CompressionConfig
} }
literaldata, err = packet.SerializeCompressed(w, algo, compConfig) literalData, err = packet.SerializeCompressed(w, algo, compConfig)
if err != nil { if err != nil {
return return
} }
@ -136,7 +153,7 @@ func SymmetricallyEncrypt(ciphertext io.Writer, passphrase []byte, hints *FileHi
if !hints.ModTime.IsZero() { if !hints.ModTime.IsZero() {
epochSeconds = uint32(hints.ModTime.Unix()) epochSeconds = uint32(hints.ModTime.Unix())
} }
return packet.SerializeLiteral(literaldata, hints.IsBinary, hints.FileName, epochSeconds) return packet.SerializeLiteral(literalData, hints.IsBinary, hints.FileName, epochSeconds)
} }
// intersectPreferences mutates and returns a prefix of a that contains only // intersectPreferences mutates and returns a prefix of a that contains only
@ -164,15 +181,51 @@ func hashToHashId(h crypto.Hash) uint8 {
return v return v
} }
// EncryptText encrypts a message to a number of recipients and, optionally,
// signs it. Optional information is contained in 'hints', also encrypted, that
// aids the recipients in processing the message. The resulting WriteCloser
// must be closed after the contents of the file have been written. If config
// is nil, sensible defaults will be used. The signing is done in text mode.
func EncryptText(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHints, config *packet.Config) (plaintext io.WriteCloser, err error) {
return encrypt(ciphertext, ciphertext, to, signed, hints, packet.SigTypeText, config)
}
// Encrypt encrypts a message to a number of recipients and, optionally, signs
// it. hints contains optional information, that is also encrypted, that aids
// the recipients in processing the message. The resulting WriteCloser must
// be closed after the contents of the file have been written.
// If config is nil, sensible defaults will be used.
func Encrypt(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHints, config *packet.Config) (plaintext io.WriteCloser, err error) {
return encrypt(ciphertext, ciphertext, to, signed, hints, packet.SigTypeBinary, config)
}
// EncryptSplit encrypts a message to a number of recipients and, optionally, signs
// it. hints contains optional information, that is also encrypted, that aids
// the recipients in processing the message. The resulting WriteCloser must
// be closed after the contents of the file have been written.
// If config is nil, sensible defaults will be used.
func EncryptSplit(keyWriter io.Writer, dataWriter io.Writer, to []*Entity, signed *Entity, hints *FileHints, config *packet.Config) (plaintext io.WriteCloser, err error) {
return encrypt(keyWriter, dataWriter, to, signed, hints, packet.SigTypeBinary, config)
}
// EncryptTextSplit encrypts a message to a number of recipients and, optionally, signs
// it. hints contains optional information, that is also encrypted, that aids
// the recipients in processing the message. The resulting WriteCloser must
// be closed after the contents of the file have been written.
// If config is nil, sensible defaults will be used.
func EncryptTextSplit(keyWriter io.Writer, dataWriter io.Writer, to []*Entity, signed *Entity, hints *FileHints, config *packet.Config) (plaintext io.WriteCloser, err error) {
return encrypt(keyWriter, dataWriter, to, signed, hints, packet.SigTypeText, config)
}
// writeAndSign writes the data as a payload package and, optionally, signs // writeAndSign writes the data as a payload package and, optionally, signs
// it. hints contains optional information, that is also encrypted, // it. hints contains optional information, that is also encrypted,
// that aids the recipients in processing the message. The resulting // that aids the recipients in processing the message. The resulting
// WriteCloser must be closed after the contents of the file have been // WriteCloser must be closed after the contents of the file have been
// written. If config is nil, sensible defaults will be used. // written. If config is nil, sensible defaults will be used.
func writeAndSign(payload io.WriteCloser, candidateHashes []uint8, signed *Entity, hints *FileHints, config *packet.Config) (plaintext io.WriteCloser, err error) { func writeAndSign(payload io.WriteCloser, candidateHashes []uint8, signed *Entity, hints *FileHints, sigType packet.SignatureType, config *packet.Config) (plaintext io.WriteCloser, err error) {
var signer *packet.PrivateKey var signer *packet.PrivateKey
if signed != nil { if signed != nil {
signKey, ok := signed.signingKey(config.Now()) signKey, ok := signed.SigningKeyById(config.Now(), config.SigningKey())
if !ok { if !ok {
return nil, errors.InvalidArgumentError("no valid signing keys") return nil, errors.InvalidArgumentError("no valid signing keys")
} }
@ -214,7 +267,7 @@ func writeAndSign(payload io.WriteCloser, candidateHashes []uint8, signed *Entit
if signer != nil { if signer != nil {
ops := &packet.OnePassSignature{ ops := &packet.OnePassSignature{
SigType: packet.SigTypeBinary, SigType: sigType,
Hash: hash, Hash: hash,
PubKeyAlgo: signer.PubKeyAlgo, PubKeyAlgo: signer.PubKeyAlgo,
KeyId: signer.KeyId, KeyId: signer.KeyId,
@ -247,17 +300,29 @@ func writeAndSign(payload io.WriteCloser, candidateHashes []uint8, signed *Entit
} }
if signer != nil { if signer != nil {
return signatureWriter{payload, literalData, hash, hash.New(), signer, config}, nil h, wrappedHash, err := hashForSignature(hash, sigType)
if err != nil {
return nil, err
}
metadata := &packet.LiteralData{
Format: 't',
FileName: hints.FileName,
Time: epochSeconds,
}
if hints.IsBinary {
metadata.Format = 'b'
}
return signatureWriter{payload, literalData, hash, wrappedHash, h, signer, sigType, config, metadata}, nil
} }
return literalData, nil return literalData, nil
} }
// Encrypt encrypts a message to a number of recipients and, optionally, signs // encrypt encrypts a message to a number of recipients and, optionally, signs
// it. hints contains optional information, that is also encrypted, that aids // it. hints contains optional information, that is also encrypted, that aids
// the recipients in processing the message. The resulting WriteCloser must // the recipients in processing the message. The resulting WriteCloser must
// be closed after the contents of the file have been written. // be closed after the contents of the file have been written.
// If config is nil, sensible defaults will be used. // If config is nil, sensible defaults will be used.
func Encrypt(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHints, config *packet.Config) (plaintext io.WriteCloser, err error) { func encrypt(keyWriter io.Writer, dataWriter io.Writer, to []*Entity, signed *Entity, hints *FileHints, sigType packet.SignatureType, config *packet.Config) (plaintext io.WriteCloser, err error) {
if len(to) == 0 { if len(to) == 0 {
return nil, errors.InvalidArgumentError("no encryption recipient provided") return nil, errors.InvalidArgumentError("no encryption recipient provided")
} }
@ -276,21 +341,39 @@ func Encrypt(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHint
hashToHashId(crypto.SHA1), hashToHashId(crypto.SHA1),
hashToHashId(crypto.RIPEMD160), hashToHashId(crypto.RIPEMD160),
} }
candidateAeadModes := []uint8{
uint8(packet.AEADModeEAX),
uint8(packet.AEADModeOCB),
uint8(packet.AEADModeExperimentalGCM),
}
candidateCompression := []uint8{
uint8(packet.CompressionNone),
uint8(packet.CompressionZIP),
uint8(packet.CompressionZLIB),
}
// In the event that a recipient doesn't specify any supported ciphers // In the event that a recipient doesn't specify any supported ciphers
// or hash functions, these are the ones that we assume that every // or hash functions, these are the ones that we assume that every
// implementation supports. // implementation supports.
defaultCiphers := candidateCiphers[len(candidateCiphers)-1:] defaultCiphers := candidateCiphers[0:1]
defaultHashes := candidateHashes[len(candidateHashes)-1:] defaultHashes := candidateHashes[0:1]
defaultAeadModes := candidateAeadModes[0:1]
defaultCompression := candidateCompression[0:1]
encryptKeys := make([]Key, len(to)) encryptKeys := make([]Key, len(to))
// AEAD is used only if every key supports it.
aeadSupported := true
for i := range to { for i := range to {
var ok bool var ok bool
encryptKeys[i], ok = to[i].encryptionKey(config.Now()) encryptKeys[i], ok = to[i].EncryptionKey(config.Now())
if !ok { if !ok {
return nil, errors.InvalidArgumentError("cannot encrypt a message to key id " + strconv.FormatUint(to[i].PrimaryKey.KeyId, 16) + " because it has no encryption keys") return nil, errors.InvalidArgumentError("cannot encrypt a message to key id " + strconv.FormatUint(to[i].PrimaryKey.KeyId, 16) + " because it has no encryption keys")
} }
sig := to[i].primaryIdentity().SelfSignature sig := to[i].PrimaryIdentity().SelfSignature
if sig.AEAD == false {
aeadSupported = false
}
preferredSymmetric := sig.PreferredSymmetric preferredSymmetric := sig.PreferredSymmetric
if len(preferredSymmetric) == 0 { if len(preferredSymmetric) == 0 {
@ -300,15 +383,26 @@ func Encrypt(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHint
if len(preferredHashes) == 0 { if len(preferredHashes) == 0 {
preferredHashes = defaultHashes preferredHashes = defaultHashes
} }
preferredAeadModes := sig.PreferredAEAD
if len(preferredAeadModes) == 0 {
preferredAeadModes = defaultAeadModes
}
preferredCompression := sig.PreferredCompression
if len(preferredCompression) == 0 {
preferredCompression = defaultCompression
}
candidateCiphers = intersectPreferences(candidateCiphers, preferredSymmetric) candidateCiphers = intersectPreferences(candidateCiphers, preferredSymmetric)
candidateHashes = intersectPreferences(candidateHashes, preferredHashes) candidateHashes = intersectPreferences(candidateHashes, preferredHashes)
candidateAeadModes = intersectPreferences(candidateAeadModes, preferredAeadModes)
candidateCompression = intersectPreferences(candidateCompression, preferredCompression)
} }
if len(candidateCiphers) == 0 || len(candidateHashes) == 0 { if len(candidateCiphers) == 0 || len(candidateHashes) == 0 || len(candidateAeadModes) == 0 {
return nil, errors.InvalidArgumentError("cannot encrypt because recipient set shares no common algorithms") return nil, errors.InvalidArgumentError("cannot encrypt because recipient set shares no common algorithms")
} }
cipher := packet.CipherFunction(candidateCiphers[0]) cipher := packet.CipherFunction(candidateCiphers[0])
mode := packet.AEADMode(candidateAeadModes[0])
// If the cipher specified by config is a candidate, we'll use that. // If the cipher specified by config is a candidate, we'll use that.
configuredCipher := config.Cipher() configuredCipher := config.Cipher()
for _, c := range candidateCiphers { for _, c := range candidateCiphers {
@ -325,17 +419,29 @@ func Encrypt(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHint
} }
for _, key := range encryptKeys { for _, key := range encryptKeys {
if err := packet.SerializeEncryptedKey(ciphertext, key.PublicKey, cipher, symKey, config); err != nil { if err := packet.SerializeEncryptedKey(keyWriter, key.PublicKey, cipher, symKey, config); err != nil {
return nil, err return nil, err
} }
} }
payload, err := packet.SerializeSymmetricallyEncrypted(ciphertext, cipher, symKey, config) var payload io.WriteCloser
if aeadSupported {
payload, err = packet.SerializeAEADEncrypted(dataWriter, symKey, cipher, mode, config)
if err != nil { if err != nil {
return return
} }
} else {
payload, err = packet.SerializeSymmetricallyEncrypted(dataWriter, cipher, symKey, config)
if err != nil {
return
}
}
payload, err = handleCompression(payload, candidateCompression, config)
if err != nil {
return nil, err
}
return writeAndSign(payload, candidateHashes, signed, hints, config) return writeAndSign(payload, candidateHashes, signed, hints, sigType, config)
} }
// Sign signs a message. The resulting WriteCloser must be closed after the // Sign signs a message. The resulting WriteCloser must be closed after the
@ -355,13 +461,17 @@ func Sign(output io.Writer, signed *Entity, hints *FileHints, config *packet.Con
hashToHashId(crypto.SHA1), hashToHashId(crypto.SHA1),
hashToHashId(crypto.RIPEMD160), hashToHashId(crypto.RIPEMD160),
} }
defaultHashes := candidateHashes[len(candidateHashes)-1:] defaultHashes := candidateHashes[0:1]
preferredHashes := signed.primaryIdentity().SelfSignature.PreferredHash preferredHashes := signed.PrimaryIdentity().SelfSignature.PreferredHash
if len(preferredHashes) == 0 { if len(preferredHashes) == 0 {
preferredHashes = defaultHashes preferredHashes = defaultHashes
} }
candidateHashes = intersectPreferences(candidateHashes, preferredHashes) candidateHashes = intersectPreferences(candidateHashes, preferredHashes)
return writeAndSign(noOpCloser{output}, candidateHashes, signed, hints, config) if len(candidateHashes) == 0 {
return nil, errors.InvalidArgumentError("cannot sign because signing key shares no common algorithms with candidate hashes")
}
return writeAndSign(noOpCloser{output}, candidateHashes, signed, hints, packet.SigTypeBinary, config)
} }
// signatureWriter hashes the contents of a message while passing it along to // signatureWriter hashes the contents of a message while passing it along to
@ -371,23 +481,35 @@ type signatureWriter struct {
encryptedData io.WriteCloser encryptedData io.WriteCloser
literalData io.WriteCloser literalData io.WriteCloser
hashType crypto.Hash hashType crypto.Hash
wrappedHash hash.Hash
h hash.Hash h hash.Hash
signer *packet.PrivateKey signer *packet.PrivateKey
sigType packet.SignatureType
config *packet.Config config *packet.Config
metadata *packet.LiteralData // V5 signatures protect document metadata
} }
func (s signatureWriter) Write(data []byte) (int, error) { func (s signatureWriter) Write(data []byte) (int, error) {
s.h.Write(data) s.wrappedHash.Write(data)
switch s.sigType {
case packet.SigTypeBinary:
return s.literalData.Write(data) return s.literalData.Write(data)
case packet.SigTypeText:
flag := 0
return writeCanonical(s.literalData, data, &flag)
}
return 0, errors.UnsupportedError("unsupported signature type: " + strconv.Itoa(int(s.sigType)))
} }
func (s signatureWriter) Close() error { func (s signatureWriter) Close() error {
sig := &packet.Signature{ sig := &packet.Signature{
SigType: packet.SigTypeBinary, Version: s.signer.Version,
SigType: s.sigType,
PubKeyAlgo: s.signer.PubKeyAlgo, PubKeyAlgo: s.signer.PubKeyAlgo,
Hash: s.hashType, Hash: s.hashType,
CreationTime: s.config.Now(), CreationTime: s.config.Now(),
IssuerKeyId: &s.signer.KeyId, IssuerKeyId: &s.signer.KeyId,
Metadata: s.metadata,
} }
if err := sig.Sign(s.h, s.signer, s.config); err != nil { if err := sig.Sign(s.h, s.signer, s.config); err != nil {
@ -416,3 +538,31 @@ func (c noOpCloser) Write(data []byte) (n int, err error) {
func (c noOpCloser) Close() error { func (c noOpCloser) Close() error {
return nil return nil
} }
func handleCompression(compressed io.WriteCloser, candidateCompression []uint8, config *packet.Config) (data io.WriteCloser, err error) {
data = compressed
confAlgo := config.Compression()
if confAlgo == packet.CompressionNone {
return
}
finalAlgo := packet.CompressionNone
// if compression specified by config available we will use it
for _, c := range candidateCompression {
if uint8(confAlgo) == c {
finalAlgo = confAlgo
break
}
}
if finalAlgo != packet.CompressionNone {
var compConfig *packet.CompressionConfig
if config != nil {
compConfig = config.CompressionConfig
}
data, err = packet.SerializeCompressed(compressed, finalAlgo, compConfig)
if err != nil {
return
}
}
return data, nil
}

42
vendor/github.com/acomagu/bufpipe/README.md generated vendored Normal file
View File

@ -0,0 +1,42 @@
# bufpipe: Buffered Pipe
[![CircleCI](https://img.shields.io/circleci/build/github/acomagu/bufpipe.svg?style=flat-square)](https://circleci.com/gh/acomagu/bufpipe) [![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg?style=flat-square)](https://godoc.org/github.com/acomagu/bufpipe)
The buffered version of io.Pipe. It's safe for concurrent use.
## How does it differ from io.Pipe?
Writes never block because the pipe has variable-sized buffer.
```Go
r, w := bufpipe.New(nil)
io.WriteString(w, "abc") // No blocking.
io.WriteString(w, "def") // No blocking, too.
w.Close()
io.Copy(os.Stdout, r)
// Output: abcdef
```
[Playground](https://play.golang.org/p/PdyBAS3pVob)
## How does it differ from bytes.Buffer?
Reads block if the internal buffer is empty until the writer is closed.
```Go
r, w := bufpipe.New(nil)
done := make(chan struct{})
go func() {
io.Copy(os.Stdout, r) // The reads block until the writer is closed.
done <- struct{}{}
}()
io.WriteString(w, "abc")
io.WriteString(w, "def")
w.Close()
<-done
// Output: abcdef
```
[Playground](https://play.golang.org/p/UppmyLeRgX6)

128
vendor/github.com/acomagu/bufpipe/bufpipe.go generated vendored Normal file
View File

@ -0,0 +1,128 @@
package bufpipe
import (
"bytes"
"errors"
"io"
"sync"
)
// ErrClosedPipe is the error used for read or write operations on a closed pipe.
var ErrClosedPipe = errors.New("bufpipe: read/write on closed pipe")
type pipe struct {
cond *sync.Cond
buf *bytes.Buffer
rerr, werr error
}
// A PipeReader is the read half of a pipe.
type PipeReader struct {
*pipe
}
// A PipeWriter is the write half of a pipe.
type PipeWriter struct {
*pipe
}
// New creates a synchronous pipe using buf as its initial contents. It can be
// used to connect code expecting an io.Reader with code expecting an io.Writer.
//
// Unlike io.Pipe, writes never block because the internal buffer has variable
// size. Reads block only when the buffer is empty.
//
// It is safe to call Read and Write in parallel with each other or with Close.
// Parallel calls to Read and parallel calls to Write are also safe: the
// individual calls will be gated sequentially.
//
// The new pipe takes ownership of buf, and the caller should not use buf after
// this call. New is intended to prepare a PipeReader to read existing data. It
// can also be used to set the initial size of the internal buffer for writing.
// To do that, buf should have the desired capacity but a length of zero.
func New(buf []byte) (*PipeReader, *PipeWriter) {
p := &pipe{
buf: bytes.NewBuffer(buf),
cond: sync.NewCond(new(sync.Mutex)),
}
return &PipeReader{
pipe: p,
}, &PipeWriter{
pipe: p,
}
}
// Read implements the standard Read interface: it reads data from the pipe,
// reading from the internal buffer, otherwise blocking until a writer arrives
// or the write end is closed. If the write end is closed with an error, that
// error is returned as err; otherwise err is io.EOF.
func (r *PipeReader) Read(data []byte) (int, error) {
r.cond.L.Lock()
defer r.cond.L.Unlock()
RETRY:
n, err := r.buf.Read(data)
// If not closed and no read, wait for writing.
if err == io.EOF && r.rerr == nil && n == 0 {
r.cond.Wait()
goto RETRY
}
if err == io.EOF {
return n, r.rerr
}
return n, err
}
// Close closes the reader; subsequent writes from the write half of the pipe
// will return error ErrClosedPipe.
func (r *PipeReader) Close() error {
return r.CloseWithError(nil)
}
// CloseWithError closes the reader; subsequent writes to the write half of the
// pipe will return the error err.
func (r *PipeReader) CloseWithError(err error) error {
r.cond.L.Lock()
defer r.cond.L.Unlock()
if err == nil {
err = ErrClosedPipe
}
r.werr = err
return nil
}
// Write implements the standard Write interface: it writes data to the internal
// buffer. If the read end is closed with an error, that err is returned as err;
// otherwise err is ErrClosedPipe.
func (w *PipeWriter) Write(data []byte) (int, error) {
w.cond.L.Lock()
defer w.cond.L.Unlock()
if w.werr != nil {
return 0, w.werr
}
n, err := w.buf.Write(data)
w.cond.Signal()
return n, err
}
// Close closes the writer; subsequent reads from the read half of the pipe will
// return io.EOF once the internal buffer get empty.
func (w *PipeWriter) Close() error {
return w.CloseWithError(nil)
}
// Close closes the writer; subsequent reads from the read half of the pipe will
// return err once the internal buffer get empty.
func (w *PipeWriter) CloseWithError(err error) error {
w.cond.L.Lock()
defer w.cond.L.Unlock()
if err == nil {
err = io.EOF
}
w.rerr = err
return nil
}

2
vendor/github.com/acomagu/bufpipe/doc.go generated vendored Normal file
View File

@ -0,0 +1,2 @@
// Package bufpipe provides a IO pipe, has variable-sized buffer.
package bufpipe

5
vendor/github.com/acomagu/bufpipe/go.mod generated vendored Normal file
View File

@ -0,0 +1,5 @@
module github.com/acomagu/bufpipe
go 1.12
require github.com/matryer/is v1.2.0

2
vendor/github.com/acomagu/bufpipe/go.sum generated vendored Normal file
View File

@ -0,0 +1,2 @@
github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A=
github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=

98
vendor/github.com/adrg/xdg/README.md generated vendored
View File

@ -1,6 +1,6 @@
<h1 align="center"> <h1 align="center">
<div> <div>
<img src="https://raw.githubusercontent.com/adrg/adrg.github.io/master/assets/projects/xdg/logo.png" height="80px" alt="xdg logo"/> <img src="https://raw.githubusercontent.com/adrg/adrg.github.io/master/assets/projects/xdg/logo.svg" alt="xdg logo"/>
</div> </div>
</h1> </h1>
@ -44,7 +44,7 @@ applications should use the XDG defined locations instead of hardcoding paths.
The package also includes the locations of well known [user directories](https://wiki.archlinux.org/index.php/XDG_user_directories) The package also includes the locations of well known [user directories](https://wiki.archlinux.org/index.php/XDG_user_directories)
and an implementation of the [state directory](https://wiki.debian.org/XDGBaseDirectorySpecification#Proposal:_STATE_directory) proposal. and an implementation of the [state directory](https://wiki.debian.org/XDGBaseDirectorySpecification#Proposal:_STATE_directory) proposal.
Windows, macOS and most flavors of Unix are supported. Most flavors of Unix, Windows, macOS and Plan 9 are supported.
Full documentation can be found at: https://pkg.go.dev/github.com/adrg/xdg. Full documentation can be found at: https://pkg.go.dev/github.com/adrg/xdg.
@ -58,75 +58,56 @@ present in the environment.
#### XDG Base Directory #### XDG Base Directory
| | Unix | macOS | Windows | | | Unix | macOS | Windows | Plan 9 |
| :-------------- | :---------------------------------- | :------------------------------------------------------------------------------------ | :-------------------------------------- | | :--------------------------------------------- | :---------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------- | :------------------------- |
| XDG_DATA_HOME | `~/.local/share` | `~/Library/Application Support` | `%LOCALAPPDATA%` | | <kbd><b><samp>XDG_DATA_HOME</samp></b></kbd> | <kbd>~/.local/share</kbd> | <kbd>~/Library/Application Support</kbd> | <kbd>%LOCALAPPDATA%</kbd> | <kbd>$home/lib</kbd> |
| XDG_DATA_DIRS | `/usr/local/share`<br/>`/usr/share` | `/Library/Application Support` | `%APPDATA%\Roaming`<br/>`%PROGRAMDATA%` | | <kbd><b><samp>XDG_DATA_DIRS</samp></b></kbd> | <kbd>/usr/local/share</kbd><br/><kbd>/usr/share</kbd> | <kbd>/Library/Application Support</kbd> | <kbd>%APPDATA%\Roaming</kbd><br/><kbd>%PROGRAMDATA%</kbd> | <kbd>/lib</kbd> |
| XDG_CONFIG_HOME | `~/.config` | `~/Library/Application Support` | `%LOCALAPPDATA%` | | <kbd><b><samp>XDG_CONFIG_HOME</samp></b></kbd> | <kbd>~/.config</kbd> | <kbd>~/Library/Application Support</kbd> | <kbd>%LOCALAPPDATA%</kbd> | <kbd>$home/lib</kbd> |
| XDG_CONFIG_DIRS | `/etc/xdg` | `~/Library/Preferences`<br/>`/Library/Application Support`<br/>`/Library/Preferences` | `%PROGRAMDATA%` | | <kbd><b><samp>XDG_CONFIG_DIRS</samp></b></kbd> | <kbd>/etc/xdg</kbd> | <kbd>~/Library/Preferences</kbd><br/><kbd>/Library/Application Support</kbd><br/><kbd>/Library/Preferences</kbd> | <kbd>%PROGRAMDATA%</kbd> | <kbd>/lib</kbd> |
| XDG_CACHE_HOME | `~/.cache` | `~/Library/Caches` | `%LOCALAPPDATA%\cache` | | <kbd><b><samp>XDG_CACHE_HOME</samp></b></kbd> | <kbd>~/.cache</kbd> | <kbd>~/Library/Caches</kbd> | <kbd>%LOCALAPPDATA%\cache</kbd> | <kbd>$home/lib/cache</kbd> |
| XDG_RUNTIME_DIR | `/run/user/UID` | `~/Library/Application Support` | `%LOCALAPPDATA%` | | <kbd><b><samp>XDG_RUNTIME_DIR</samp></b></kbd> | <kbd>/run/user/UID</kbd> | <kbd>~/Library/Application Support</kbd> | <kbd>%LOCALAPPDATA%</kbd> | <kbd>/tmp</kbd> |
#### XDG user directories #### XDG user directories
| | Unix | macOS | Windows | | | Unix | macOS | Windows | Plan 9 |
| :------------------ | :------------ | :------------ | :------------------------ | | :------------------------------------------------- | :--------------------- | :--------------------- | :--------------------------------- | :------------------------- |
| XDG_DESKTOP_DIR | `~/Desktop` | `~/Desktop` | `%USERPROFILE%/Desktop` | | <kbd><b><samp>XDG_DESKTOP_DIR</samp></b></kbd> | <kbd>~/Desktop</kbd> | <kbd>~/Desktop</kbd> | <kbd>%USERPROFILE%\Desktop</kbd> | <kbd>$home/desktop</kbd> |
| XDG_DOWNLOAD_DIR | `~/Downloads` | `~/Downloads` | `%USERPROFILE%/Downloads` | | <kbd><b><samp>XDG_DOWNLOAD_DIR</samp></b></kbd> | <kbd>~/Downloads</kbd> | <kbd>~/Downloads</kbd> | <kbd>%USERPROFILE%\Downloads</kbd> | <kbd>$home/downloads</kbd> |
| XDG_DOCUMENTS_DIR | `~/Documents` | `~/Documents` | `%USERPROFILE%/Documents` | | <kbd><b><samp>XDG_DOCUMENTS_DIR</samp></b></kbd> | <kbd>~/Documents</kbd> | <kbd>~/Documents</kbd> | <kbd>%USERPROFILE%\Documents</kbd> | <kbd>$home/documents</kbd> |
| XDG_MUSIC_DIR | `~/Music` | `~/Music` | `%USERPROFILE%/Music` | | <kbd><b><samp>XDG_MUSIC_DIR</samp></b></kbd> | <kbd>~/Music</kbd> | <kbd>~/Music</kbd> | <kbd>%USERPROFILE%\Music</kbd> | <kbd>$home/music</kbd> |
| XDG_PICTURES_DIR | `~/Pictures` | `~/Pictures` | `%USERPROFILE%/Pictures` | | <kbd><b><samp>XDG_PICTURES_DIR</samp></b></kbd> | <kbd>~/Pictures</kbd> | <kbd>~/Pictures</kbd> | <kbd>%USERPROFILE%\Pictures</kbd> | <kbd>$home/pictures</kbd> |
| XDG_VIDEOS_DIR | `~/Videos` | `~/Movies` | `%USERPROFILE%/Videos` | | <kbd><b><samp>XDG_VIDEOS_DIR</samp></b></kbd> | <kbd>~/Videos</kbd> | <kbd>~/Movies</kbd> | <kbd>%USERPROFILE%\Videos</kbd> | <kbd>$home/videos</kbd> |
| XDG_TEMPLATES_DIR | `~/Templates` | `~/Templates` | `%USERPROFILE%/Templates` | | <kbd><b><samp>XDG_TEMPLATES_DIR</samp></b></kbd> | <kbd>~/Templates</kbd> | <kbd>~/Templates</kbd> | <kbd>%USERPROFILE%\Templates</kbd> | <kbd>$home/templates</kbd> |
| XDG_PUBLICSHARE_DIR | `~/Public` | `~/Public` | `%PUBLIC%` | | <kbd><b><samp>XDG_PUBLICSHARE_DIR</samp></b></kbd> | <kbd>~/Public</kbd> | <kbd>~/Public</kbd> | <kbd>%PUBLIC%</kbd> | <kbd>$home/public</kbd> |
#### Non-standard directories #### Non-standard directories
State directory State directory
``` | | Unix | macOS | Windows | Plan 9 |
Unix | :-------------------------------------------- | :------------------------ | :--------------------------------------- | :------------------------ | :------------------------- |
• ~/.local/state | <kbd><b><samp>XDG_STATE_HOME</samp></b></kbd> | <kbd>~/.local/state</kbd> | <kbd>~/Library/Application Support</kbd> | <kbd>%LOCALAPPDATA%</kbd> | <kbd>$home/lib/state</kbd> |
macOS
• ~/Library/Application Support
Windows
• %LOCALAPPDATA%
```
Application directories Application directories
``` | Unix | macOS | Windows | Plan 9 |
Unix | :--------------------------------------- | :----------------------- | :----------------------------------------------------------------- | :-------------------- |
• $XDG_DATA_HOME/applications | <kbd>$XDG_DATA_HOME/applications</kbd> | <kbd>/Applications</kbd> | <kbd>%APPDATA%\Roaming\Microsoft\Windows\Start Menu\Programs</kbd> | <kbd>$home/bin</kbd> |
• ~/.local/share/applications | <kbd>~/.local/share/applications</kbd> | | | <kbd>/bin</kbd> |
• /usr/local/share/applications | <kbd>/usr/local/share/applications</kbd> | | | |
• /usr/share/applications | <kbd>/usr/share/applications</kbd> | | | |
• $XDG_DATA_DIRS/applications | <kbd>$XDG_DATA_DIRS/applications</kbd> | | | |
macOS
• /Applications
Windows
• %APPDATA%\Roaming\Microsoft\Windows\Start Menu\Programs
```
Font directories Font directories
``` | Unix | macOS | Windows | Plan 9 |
Unix | :-------------------------------- | :-------------------------------- | :------------------------------------------------ | :------------------------ |
• $XDG_DATA_HOME/fonts | <kbd>$XDG_DATA_HOME/fonts</kbd> | <kbd>~/Library/Fonts</kbd> | <kbd>%windir%\Fonts</kbd> | <kbd>$home/lib/font</kbd> |
• ~/.fonts | <kbd>~/.fonts</kbd> | <kbd>/Library/Fonts</kbd> | <kbd>%LOCALAPPDATA%\Microsoft\Windows\Fonts</kbd> | <kbd>/lib/font</kbd> |
• ~/.local/share/fonts | <kbd>~/.local/share/fonts</kbd> | <kbd>/System/Library/Fonts</kbd> | | |
• /usr/local/share/fonts | <kbd>/usr/local/share/fonts</kbd> | <kbd>/Network/Library/Fonts</kbd> | | |
• /usr/share/fonts | <kbd>/usr/share/fonts</kbd> | | | |
• $XDG_DATA_DIRS/fonts | <kbd>$XDG_DATA_DIRS/fonts</kbd> | | | |
macOS
• ~/Library/Fonts
• /Library/Fonts
• /System/Library/Fonts
• /Network/Library/Fonts
Windows
• %windir%\Fonts
• %LOCALAPPDATA%\Microsoft\Windows\Fonts
```
## Usage ## Usage
@ -228,7 +209,8 @@ See [CONTRIBUTING.MD](CONTRIBUTING.md).
[wichert](https://github.com/wichert), [wichert](https://github.com/wichert),
[bouncepaw](https://github.com/bouncepaw), [bouncepaw](https://github.com/bouncepaw),
[gabriel-vasile](https://github.com/gabriel-vasile), [gabriel-vasile](https://github.com/gabriel-vasile),
[KalleDK](https://github.com/KalleDK). [KalleDK](https://github.com/KalleDK),
[djdv](https://github.com/djdv).
## References ## References

11
vendor/github.com/adrg/xdg/codecov.yml generated vendored Normal file
View File

@ -0,0 +1,11 @@
coverage:
status:
project:
default:
target: 90%
threshold: 1%
patch:
default:
target: 100%
ignore:
- "paths_plan9.go"

2
vendor/github.com/adrg/xdg/go.mod generated vendored
View File

@ -2,4 +2,4 @@ module github.com/adrg/xdg
go 1.14 go 1.14
require github.com/stretchr/testify v1.6.1 require github.com/stretchr/testify v1.7.0

5
vendor/github.com/adrg/xdg/go.sum generated vendored
View File

@ -2,9 +2,10 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=

42
vendor/github.com/adrg/xdg/paths_plan9.go generated vendored Normal file
View File

@ -0,0 +1,42 @@
package xdg
import (
"path/filepath"
)
func initBaseDirs(home string) {
homeLibDir := filepath.Join(home, "lib")
rootLibDir := "/lib"
// Initialize base directories.
baseDirs.dataHome = xdgPath(envDataHome, homeLibDir)
baseDirs.data = xdgPaths(envDataDirs, rootLibDir)
baseDirs.configHome = xdgPath(envConfigHome, homeLibDir)
baseDirs.config = xdgPaths(envConfigDirs, rootLibDir)
baseDirs.cacheHome = xdgPath(envCacheHome, filepath.Join(homeLibDir, "cache"))
baseDirs.runtime = xdgPath(envRuntimeDir, "/tmp")
// Initialize non-standard directories.
baseDirs.stateHome = xdgPath(envStateHome, filepath.Join(homeLibDir, "state"))
baseDirs.applications = []string{
filepath.Join(home, "bin"),
"/bin",
}
baseDirs.fonts = []string{
filepath.Join(homeLibDir, "font"),
"/lib/font",
}
}
func initUserDirs(home string) {
UserDirs.Desktop = xdgPath(envDesktopDir, filepath.Join(home, "desktop"))
UserDirs.Download = xdgPath(envDownloadDir, filepath.Join(home, "downloads"))
UserDirs.Documents = xdgPath(envDocumentsDir, filepath.Join(home, "documents"))
UserDirs.Music = xdgPath(envMusicDir, filepath.Join(home, "music"))
UserDirs.Pictures = xdgPath(envPicturesDir, filepath.Join(home, "pictures"))
UserDirs.Videos = xdgPath(envVideosDir, filepath.Join(home, "videos"))
UserDirs.Templates = xdgPath(envTemplatesDir, filepath.Join(home, "templates"))
UserDirs.PublicShare = xdgPath(envPublicShareDir, filepath.Join(home, "public"))
}

View File

@ -1,4 +1,4 @@
// +build aix dragonfly freebsd linux nacl netbsd openbsd solaris // +build aix dragonfly freebsd js,wasm nacl linux netbsd openbsd solaris
package xdg package xdg

10
vendor/github.com/adrg/xdg/stat.go generated vendored Normal file
View File

@ -0,0 +1,10 @@
// +build !windows
package xdg
import "os"
func pathExists(path string) bool {
_, err := os.Stat(path)
return err == nil || os.IsExist(err)
}

15
vendor/github.com/adrg/xdg/stat_windows.go generated vendored Normal file
View File

@ -0,0 +1,15 @@
package xdg
import (
"os"
"path/filepath"
)
func pathExists(path string) bool {
fi, err := os.Lstat(path)
if fi != nil && fi.Mode()&os.ModeSymlink != 0 {
_, err = filepath.EvalSymlinks(path)
}
return err == nil || os.IsExist(err)
}

View File

@ -33,11 +33,6 @@ func homeDir() string {
return "" return ""
} }
func exists(path string) bool {
_, err := os.Stat(path)
return err == nil || os.IsExist(err)
}
func expandPath(path, homeDir string) string { func expandPath(path, homeDir string) string {
if path == "" || homeDir == "" { if path == "" || homeDir == "" {
return path return path
@ -58,7 +53,7 @@ func createPath(name string, paths []string) (string, error) {
path := filepath.Join(p, name) path := filepath.Join(p, name)
dir := filepath.Dir(path) dir := filepath.Dir(path)
if exists(dir) { if pathExists(dir) {
return path, nil return path, nil
} }
if err := os.MkdirAll(dir, os.ModeDir|0700); err == nil { if err := os.MkdirAll(dir, os.ModeDir|0700); err == nil {
@ -76,7 +71,7 @@ func searchFile(name string, paths []string) (string, error) {
var searchedPaths []string var searchedPaths []string
for _, p := range paths { for _, p := range paths {
path := filepath.Join(p, name) path := filepath.Join(p, name)
if exists(path) { if pathExists(path) {
return path, nil return path, nil
} }

View File

@ -25,6 +25,17 @@ linters:
- testpackage - testpackage
- godot - godot
- nestif - nestif
- paralleltest
- nlreturn
- cyclop
- exhaustivestruct
- gci
- gofumpt
- errorlint
- exhaustive
- ifshort
- wrapcheck
- stylecheck
linters-settings: linters-settings:
govet: govet:
@ -36,6 +47,11 @@ linters-settings:
goconst: goconst:
min-len: 8 min-len: 8
min-occurrences: 3 min-occurrences: 3
forbidigo:
forbid:
- (Must)?NewLexer
exclude_godoc_examples: false
issues: issues:
max-per-linter: 0 max-per-linter: 0

View File

@ -6,6 +6,8 @@ release:
brews: brews:
- -
install: bin.install "chroma" install: bin.install "chroma"
env:
- CGO_ENABLED=0
builds: builds:
- goos: - goos:
- linux - linux

Some files were not shown because too many files have changed in this diff Show More