Update Dependencies #390

Merged
zeripath merged 2 commits from noerw/tea:update-deps into master 2021-08-30 15:18:52 +00:00
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"/>
@ -295,7 +295,7 @@ survey.AskOne(prompt, &color, survey.WithFilter(myFilter))
## Keeping the filter active ## Keeping the filter active
By default the filter will disappear if the user selects one of the filtered elements. Once the user selects one element the filter setting is gone. By default the filter will disappear if the user selects one of the filtered elements. Once the user selects one element the filter setting is gone.
However the user can prevent this from happening and keep the filter active for multiple selections in a e.g. MultiSelect: However the user can prevent this from happening and keep the filter active for multiple selections in a e.g. MultiSelect:
@ -342,11 +342,13 @@ survey.AskOne(prompt, &color, survey.WithValidator(survey.Required))
`survey` comes prepackaged with a few validators to fit common situations. Currently these `survey` comes prepackaged with a few validators to fit common situations. Currently these
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,86 +60,90 @@ 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 { i.selectedIndex = len(i.options) - 1
i.selectedIndex = len(i.options) - 1 } else {
} else { i.selectedIndex--
i.selectedIndex-- }
} i.answer = i.options[i.selectedIndex].Value
i.answer = i.options[i.selectedIndex].Value } else if (key == terminal.KeyArrowDown || key == terminal.KeyTab) && len(i.options) > 0 {
} else if (key == terminal.KeyArrowDown || key == terminal.KeyTab) && len(i.options) > 0 { if i.selectedIndex == len(i.options)-1 {
if i.selectedIndex == len(i.options)-1 { i.selectedIndex = 0
} else {
i.selectedIndex++
}
i.answer = i.options[i.selectedIndex].Value
} else if key == terminal.KeyTab && i.Suggest != nil {
i.answer = string(line)
i.typedAnswer = i.answer
options := i.Suggest(i.answer)
i.selectedIndex = 0 i.selectedIndex = 0
} else { if len(options) == 0 {
i.selectedIndex++ return line, false, nil
} }
i.answer = i.options[i.selectedIndex].Value
} else if key == terminal.KeyTab && i.Suggest != nil {
options := i.Suggest(i.answer)
i.selectedIndex = 0
i.typedAnswer = i.answer
if len(options) > 0 {
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 {
} else if key == terminal.KeyDelete || key == terminal.KeyBackspace { if i.options == nil {
if i.answer != "" { return line, false, nil
runeAnswer := []rune(i.answer) }
i.answer = string(runeAnswer[0 : len(runeAnswer)-1])
}
} else if key >= terminal.KeySpace {
i.answer += string(key)
i.typedAnswer = i.answer
i.options = nil
}
pageSize := config.PageSize if key >= terminal.KeySpace {
opts, idx := paginate(pageSize, i.options, i.selectedIndex) i.answer += string(key)
err := i.Render( }
InputQuestionTemplate, i.typedAnswer = i.answer
InputTemplateData{
Input: *i,
Answer: i.answer,
ShowHelp: i.showingHelp,
SelectedIndex: idx,
PageEntries: opts,
Config: config,
},
)
return err != nil, err i.options = nil
}
pageSize := config.PageSize
opts, idx := paginate(pageSize, i.options, i.selectedIndex)
err := i.Render(
InputQuestionTemplate,
InputTemplateData{
Input: *i,
Answer: i.answer,
ShowHelp: i.showingHelp,
SelectedIndex: idx,
PageEntries: opts,
Config: config,
},
)
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(
InputQuestionTemplate, InputQuestionTemplate,
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()
cursor.Hide() // hide the cursor if !config.ShowCursor {
defer cursor.Show() // show the cursor when we're done cursor.Hide() // hide the cursor
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)
tmplData := MultiSelectTemplateData{
MultiSelect: *m,
SelectedIndex: idx,
Checked: m.checked,
ShowHelp: m.showingHelp,
PageEntries: opts,
Config: config,
}
// render the options // render the options
m.Render( m.RenderWithCursorOffset(MultiSelectQuestionTemplate, tmplData, opts, idx)
MultiSelectQuestionTemplate,
MultiSelectTemplateData{
MultiSelect: *m,
SelectedIndex: idx,
Checked: m.checked,
ShowHelp: m.showingHelp,
PageEntries: opts,
Config: config,
},
)
} }
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.Hide() // hide the cursor cursor.Save() // for proper cursor placement during selection
defer cursor.Show() // show the cursor when we're done cursor.Hide() // hide the cursor
defer cursor.Show() // show the cursor when we're done
defer cursor.Restore() // clear any accessibility offsetting on exit
tmplData := MultiSelectTemplateData{
MultiSelect: *m,
SelectedIndex: idx,
Checked: m.checked,
PageEntries: opts,
Config: config,
}
// ask the question // ask the question
err := m.Render( err := m.RenderWithCursorOffset(MultiSelectQuestionTemplate, tmplData, opts, idx)
MultiSelectQuestionTemplate,
MultiSelectTemplateData{
MultiSelect: *m,
SelectedIndex: idx,
Checked: m.checked,
PageEntries: opts,
Config: config,
},
)
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)
tmplData := SelectTemplateData{
Select: *s,
SelectedIndex: idx,
ShowHelp: s.showingHelp,
PageEntries: opts,
Config: config,
}
// render the options // render the options
s.Render( s.RenderWithCursorOffset(SelectQuestionTemplate, tmplData, opts, idx)
SelectQuestionTemplate,
SelectTemplateData{
Select: *s,
SelectedIndex: idx,
ShowHelp: s.showingHelp,
PageEntries: opts,
Config: config,
},
)
// 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)
cursor := s.NewCursor()
cursor.Save() // for proper cursor placement during selection
cursor.Hide() // hide the cursor
defer cursor.Show() // show the cursor when we're done
defer cursor.Restore() // clear any accessibility offsetting on exit
tmplData := SelectTemplateData{
Select: *s,
SelectedIndex: idx,
ShowHelp: s.showingHelp,
PageEntries: opts,
Config: config,
}
// ask the question // ask the question
err := s.Render( err := s.RenderWithCursorOffset(SelectQuestionTemplate, tmplData, opts, idx)
SelectQuestionTemplate,
SelectTemplateData{
Select: *s,
PageEntries: opts,
SelectedIndex: idx,
Config: config,
},
)
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

@ -28,8 +28,9 @@ const (
ERROR_NOT_ALL_ASSIGNED syscall.Errno = 1300 ERROR_NOT_ALL_ASSIGNED syscall.Errno = 1300
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,126 +118,73 @@ 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 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
}
func getSecurityDescriptorLength(sd uintptr) (len uint32) { func getSecurityDescriptorLength(sd uintptr) (len uint32) {
r0, _, _ := syscall.Syscall(procGetSecurityDescriptorLength.Addr(), 1, uintptr(sd), 0, 0) r0, _, _ := syscall.Syscall(procGetSecurityDescriptorLength.Addr(), 1, uintptr(sd), 0, 0)
len = uint32(r0) len = uint32(r0)
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 { return
err = syscall.EINVAL }
}
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())
} }
identity.SelfSignature = sig if identity.SelfSignature == nil || sig.CreationTime.After(identity.SelfSignature.CreationTime) {
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,9 +554,11 @@ func (e *Entity) SerializePrivate(w io.Writer, config *packet.Config) (err error
if err != nil { if err != nil {
return return
} }
err = ident.SelfSignature.SignUserId(ident.UserId.Id, e.PrimaryKey, e.PrivateKey, config) if reSign {
if err != nil { err = ident.SelfSignature.SignUserId(ident.UserId.Id, e.PrimaryKey, e.PrivateKey, config)
return if err != nil {
return
}
} }
err = ident.SelfSignature.Serialize(w) err = ident.SelfSignature.Serialize(w)
if err != nil { if err != nil {
@ -613,9 +570,18 @@ func (e *Entity) SerializePrivate(w io.Writer, config *packet.Config) (err error
if err != nil { if err != nil {
return return
} }
err = subkey.Sig.SignKey(subkey.PublicKey, e.PrivateKey, config) if reSign {
if err != nil { err = subkey.Sig.SignKey(subkey.PublicKey, e.PrivateKey, config)
return if err != nil {
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 {
@ -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
}

View File

@ -0,0 +1,336 @@
package openpgp
const expiringKeyHex = "c6c04d0451d0c680010800abbb021fd03ffc4e96618901180c3fdcb060ee69eeead97b91256d11420d80b5f1b51930248044130bd300605cf8a05b7a40d3d8cfb0a910be2e3db50dcd50a9c54064c2a5550801daa834ff4480b33d3d3ca495ff8a4e84a886977d17d998f881241a874083d8b995beab555b6d22b8a4817ab17ac3e7304f7d4d2c05c495fb2218348d3bc13651db1d92732e368a9dd7dcefa6eddff30b94706a9aaee47e9d39321460b740c59c6fc3c2fd8ab6c0fb868cb87c0051f0321301fe0f0e1820b15e7fb7063395769b525005c7e30a7ce85984f5cac00504e7b4fdc45d74958de8388436fd5c7ba9ea121f1c851b5911dd1b47a14d81a09e92ef37721e2325b6790011010001cd00c2c07b041001080025050251d0c680050900278d00060b09070803020415080a0203160201021901021b03021e01000a0910e7b484133a890a35ae4b0800a1beb82e7f28eaf5273d6af9d3391314f6280b2b624eaca2851f89a9ebcaf80ac589ebd509f168bc4322106ca2e2ce77a76e071a3c7444787d65216b5f05e82c77928860b92aace3b7d0327db59492f422eb9dfab7249266d37429870b091a98aba8724c2259ebf8f85093f21255eafa75aa841e31d94f2ac891b9755fed455e539044ee69fc47950b80e003fc9f298d695660f28329eaa38037c367efde1727458e514faf990d439a21461b719edaddf9296d3d0647b43ca56cb8dbf63b4fcf8b9968e7928c463470fab3b98e44d0d95645062f94b2d04fe56bd52822b71934db8ce845622c40b92fcbe765a142e7f38b61a6aa9606c8e8858dcd3b6eb1894acec04d0451d1f06b01080088bea67444e1789390e7c0335c86775502d58ec783d99c8ef4e06de235ed3dd4b0467f6f358d818c7d8989d43ec6d69fcbc8c32632d5a1b605e3fa8e41d695fcdcaa535936cd0157f9040dce362519803b908eafe838bb13216c885c6f93e9e8d5745607f0d062322085d6bdc760969149a8ff8dd9f5c18d9bfe2e6f63a06e17694cf1f67587c6fb70e9aebf90ffc528ca3b615ac7c9d4a21ea4f7c06f2e98fbbd90a859b8608bf9ea638e3a54289ce44c283110d0c45fa458de6251cd6e7baf71f80f12c8978340490fd90c92b81736ae902ed958e478dceae2835953d189c45d182aff02ea2be61b81d8e94430f041d638647b43e2fcb45fd512fbf5068b810011010001c2c06504180108000f050251d1f06b050900081095021b0c000a0910e7b484133a890a35e63407fe2ec88d6d1e6c9ce7553ece0cb2524747217bad29f251d33df84599ffcc900141a355abd62126800744068a5e05dc167056aa9205273dc7765a2ed49db15c2a83b8d6e6429c902136f1e12229086c1c10c0053242c2a4ae1930db58163387a48cad64607ff2153c320e42843dec28e3fce90e7399d63ac0affa2fee1f0adc0953c89eb3f46ef1d6c04328ed13b491669d5120a3782e3ffb7c69575fb77eebd108794f4dda9d34be2bae57e8e59ec8ebfda2f6f06104b2321be408ea146e2db482b00c5055c8618de36ac9716f80da2617e225556d0fce61b01c8cea2d1e0ea982c31711060ca370f2739366e1e708f38405d784b49d16a26cf62d152eae734327cec04d0451d1f07b010800d5af91c5e7c2fd8951c8d254eab0c97cdcb66822f868b79b78c366255059a68fd74ebca9adb9b970cd9e586690e6e0756705432306878c897b10a4b4ca0005966f99ac8fa4e6f9caf54bf8e53844544beee9872a7ac64c119cf1393d96e674254b661f61ee975633d0e8a8672531edb6bb8e211204e7754a9efa802342118eee850beea742bac95a3f706cc2024cf6037a308bb68162b2f53b9a6346a96e6d31871a2456186e24a1c7a82b82ac04afdfd57cd7fb9ba77a9c760d40b76a170f7be525e5fb6a9848cc726e806187710d9b190387df28700f321f988a392899f93815cc937f309129eb94d5299c5547cb2c085898e6639496e70d746c9d3fb9881d0011010001c2c06504180108000f050251d1f07b050900266305021b0c000a0910e7b484133a890a35bff207fd10dfe8c4a6ea1dd30568012b6fd6891a763c87ad0f7a1d112aad9e8e3239378a3b85588c235865bac2e614348cb4f216d7217f53b3ef48c192e0a4d31d64d7bfa5faccf21155965fa156e887056db644a05ad08a85cc6152d1377d9e37b46f4ff462bbe68ace2dc586ef90070314576c985d8037c2ba63f0a7dc17a62e15bd77e88bc61d9d00858979709f12304264a4cf4225c5cf86f12c8e19486cb9cdcc69f18f027e5f16f4ca8b50e28b3115eaff3a345acd21f624aef81f6ede515c1b55b26b84c1e32264754eab672d5489b287e7277ea855e0a5ff2aa9e8b8c76d579a964ec225255f4d57bf66639ccb34b64798846943e162a41096a7002ca21c7f56"
const subkeyUsageHex = "988d04533a52bc010400d26af43085558f65b9e7dbc90cb9238015259aed5e954637adcfa2181548b2d0b60c65f1f42ec5081cbf1bc0a8aa4900acfb77070837c58f26012fbce297d70afe96e759ad63531f0037538e70dbf8e384569b9720d99d8eb39d8d0a2947233ed242436cb6ac7dfe74123354b3d0119b5c235d3dd9c9d6c004f8ffaf67ad8583001101000188b7041f010200210502533b8552170c8001ce094aa433f7040bb2ddf0be3893cb843d0fe70c020700000a0910a42704b92866382aa98404009d63d916a27543da4221c60087c33f1c44bec9998c5438018ed370cca4962876c748e94b73eb39c58eb698063f3fd6346d58dd2a11c0247934c4a9d71f24754f7468f96fb24c3e791dd2392b62f626148ad724189498cbf993db2df7c0cdc2d677c35da0f16cb16c9ce7c33b4de65a4a91b1d21a130ae9cc26067718910ef8e2b417556d627261203c756d627261407379642e65642e61753e88b80413010200220502533a52bc021b03060b090807030206150802090a0b0416020301021e01021780000a0910a42704b92866382a47840400c0c2bd04f5fca586de408b395b3c280a278259c93eaaa8b79a53b97003f8ed502a8a00446dd9947fb462677e4fcac0dac2f0701847d15130aadb6cd9e0705ea0cf5f92f129136c7be21a718d46c8e641eb7f044f2adae573e11ae423a0a9ca51324f03a8a2f34b91fa40c3cc764bee4dccadedb54c768ba0469b683ea53f1c29b88d04533a52bc01040099c92a5d6f8b744224da27bc2369127c35269b58bec179de6bbc038f749344222f85a31933224f26b70243c4e4b2d242f0c4777eaef7b5502f9dad6d8bf3aaeb471210674b74de2d7078af497d55f5cdad97c7bedfbc1b41e8065a97c9c3d344b21fc81d27723af8e374bc595da26ea242dccb6ae497be26eea57e563ed517e90011010001889f0418010200090502533a52bc021b0c000a0910a42704b92866382afa1403ff70284c2de8a043ff51d8d29772602fa98009b7861c540535f874f2c230af8caf5638151a636b21f8255003997ccd29747fdd06777bb24f9593bd7d98a3e887689bf902f999915fcc94625ae487e5d13e6616f89090ebc4fdc7eb5cad8943e4056995bb61c6af37f8043016876a958ec7ebf39c43d20d53b7f546cfa83e8d2604b88d04533b8283010400c0b529316dbdf58b4c54461e7e669dc11c09eb7f73819f178ccd4177b9182b91d138605fcf1e463262fabefa73f94a52b5e15d1904635541c7ea540f07050ce0fb51b73e6f88644cec86e91107c957a114f69554548a85295d2b70bd0b203992f76eb5d493d86d9eabcaa7ef3fc7db7e458438db3fcdb0ca1cc97c638439a9170011010001889f0418010200090502533b8283021b0c000a0910a42704b92866382adc6d0400cfff6258485a21675adb7a811c3e19ebca18851533f75a7ba317950b9997fda8d1a4c8c76505c08c04b6c2cc31dc704d33da36a21273f2b388a1a706f7c3378b66d887197a525936ed9a69acb57fe7f718133da85ec742001c5d1864e9c6c8ea1b94f1c3759cebfd93b18606066c063a63be86085b7e37bdbc65f9a915bf084bb901a204533b85cd110400aed3d2c52af2b38b5b67904b0ef73d6dd7aef86adb770e2b153cd22489654dcc91730892087bb9856ae2d9f7ed1eb48f214243fe86bfe87b349ebd7c30e630e49c07b21fdabf78b7a95c8b7f969e97e3d33f2e074c63552ba64a2ded7badc05ce0ea2be6d53485f6900c7860c7aa76560376ce963d7271b9b54638a4028b573f00a0d8854bfcdb04986141568046202192263b9b67350400aaa1049dbc7943141ef590a70dcb028d730371d92ea4863de715f7f0f16d168bd3dc266c2450457d46dcbbf0b071547e5fbee7700a820c3750b236335d8d5848adb3c0da010e998908dfd93d961480084f3aea20b247034f8988eccb5546efaa35a92d0451df3aaf1aee5aa36a4c4d462c760ecd9cebcabfbe1412b1f21450f203fd126687cd486496e971a87fd9e1a8a765fe654baa219a6871ab97768596ab05c26c1aeea8f1a2c72395a58dbc12ef9640d2b95784e974a4d2d5a9b17c25fedacfe551bda52602de8f6d2e48443f5dd1a2a2a8e6a5e70ecdb88cd6e766ad9745c7ee91d78cc55c3d06536b49c3fee6c3d0b6ff0fb2bf13a314f57c953b8f4d93bf88e70418010200090502533b85cd021b0200520910a42704b92866382a47200419110200060502533b85cd000a091042ce2c64bc0ba99214b2009e26b26852c8b13b10c35768e40e78fbbb48bd084100a0c79d9ea0844fa5853dd3c85ff3ecae6f2c9dd6c557aa04008bbbc964cd65b9b8299d4ebf31f41cc7264b8cf33a00e82c5af022331fac79efc9563a822497ba012953cefe2629f1242fcdcb911dbb2315985bab060bfd58261ace3c654bdbbe2e8ed27a46e836490145c86dc7bae15c011f7e1ffc33730109b9338cd9f483e7cef3d2f396aab5bd80efb6646d7e778270ee99d934d187dd98"
const revokedKeyHex = "988d045331ce82010400c4fdf7b40a5477f206e6ee278eaef888ca73bf9128a9eef9f2f1ddb8b7b71a4c07cfa241f028a04edb405e4d916c61d6beabc333813dc7b484d2b3c52ee233c6a79b1eea4e9cc51596ba9cd5ac5aeb9df62d86ea051055b79d03f8a4fa9f38386f5bd17529138f3325d46801514ea9047977e0829ed728e68636802796801be10011010001889f04200102000905025331d0e3021d03000a0910a401d9f09a34f7c042aa040086631196405b7e6af71026b88e98012eab44aa9849f6ef3fa930c7c9f23deaedba9db1538830f8652fb7648ec3fcade8dbcbf9eaf428e83c6cbcc272201bfe2fbb90d41963397a7c0637a1a9d9448ce695d9790db2dc95433ad7be19eb3de72dacf1d6db82c3644c13eae2a3d072b99bb341debba012c5ce4006a7d34a1f4b94b444526567205265766f6b657220283c52656727732022424d204261726973746122204b657920262530305c303e5c29203c72656740626d626172697374612e636f2e61753e88b704130102002205025331ce82021b03060b090807030206150802090a0b0416020301021e01021780000a0910a401d9f09a34f7c0019c03f75edfbeb6a73e7225ad3cc52724e2872e04260d7daf0d693c170d8c4b243b8767bc7785763533febc62ec2600c30603c433c095453ede59ff2fcabeb84ce32e0ed9d5cf15ffcbc816202b64370d4d77c1e9077d74e94a16fb4fa2e5bec23a56d7a73cf275f91691ae1801a976fcde09e981a2f6327ac27ea1fecf3185df0d56889c04100102000605025331cfb5000a0910fe9645554e8266b64b4303fc084075396674fb6f778d302ac07cef6bc0b5d07b66b2004c44aef711cbac79617ef06d836b4957522d8772dd94bf41a2f4ac8b1ee6d70c57503f837445a74765a076d07b829b8111fc2a918423ddb817ead7ca2a613ef0bfb9c6b3562aec6c3cf3c75ef3031d81d95f6563e4cdcc9960bcb386c5d757b104fcca5fe11fc709df884604101102000605025331cfe7000a09107b15a67f0b3ddc0317f6009e360beea58f29c1d963a22b962b80788c3fa6c84e009d148cfde6b351469b8eae91187eff07ad9d08fcaab88d045331ce820104009f25e20a42b904f3fa555530fe5c46737cf7bd076c35a2a0d22b11f7e0b61a69320b768f4a80fe13980ce380d1cfc4a0cd8fbe2d2e2ef85416668b77208baa65bf973fe8e500e78cc310d7c8705cdb34328bf80e24f0385fce5845c33bc7943cf6b11b02348a23da0bf6428e57c05135f2dc6bd7c1ce325d666d5a5fd2fd5e410011010001889f04180102000905025331ce82021b0c000a0910a401d9f09a34f7c0418003fe34feafcbeaef348a800a0d908a7a6809cc7304017d820f70f0474d5e23cb17e38b67dc6dca282c6ca00961f4ec9edf2738d0f087b1d81e4871ef08e1798010863afb4eac4c44a376cb343be929c5be66a78cfd4456ae9ec6a99d97f4e1c3ff3583351db2147a65c0acef5c003fb544ab3a2e2dc4d43646f58b811a6c3a369d1f"
const revokedSubkeyHex = "988d04533121f6010400aefc803a3e4bb1a61c86e8a86d2726c6a43e0079e9f2713f1fa017e9854c83877f4aced8e331d675c67ea83ddab80aacbfa0b9040bb12d96f5a3d6be09455e2a76546cbd21677537db941cab710216b6d24ec277ee0bd65b910f416737ed120f6b93a9d3b306245c8cfd8394606fdb462e5cf43c551438d2864506c63367fc890011010001b41d416c696365203c616c69636540626d626172697374612e636f2e61753e88bb041301020025021b03060b090807030206150802090a0b0416020301021e01021780050253312798021901000a09104ef7e4beccde97f015a803ff5448437780f63263b0df8442a995e7f76c221351a51edd06f2063d8166cf3157aada4923dfc44aa0f2a6a4da5cf83b7fe722ba8ab416c976e77c6b5682e7f1069026673bd0de56ba06fd5d7a9f177607f277d9b55ff940a638c3e68525c67517e2b3d976899b93ca267f705b3e5efad7d61220e96b618a4497eab8d04403d23f8846041011020006050253312910000a09107b15a67f0b3ddc03d96e009f50b6365d86c4be5d5e9d0ea42d5e56f5794c617700a0ab274e19c2827780016d23417ce89e0a2c0d987d889c04100102000605025331cf7a000a0910a401d9f09a34f7c0ee970400aca292f213041c9f3b3fc49148cbda9d84afee6183c8dd6c5ff2600b29482db5fecd4303797be1ee6d544a20a858080fec43412061c9a71fae4039fd58013b4ae341273e6c66ad4c7cdd9e68245bedb260562e7b166f2461a1032f2b38c0e0e5715fb3d1656979e052b55ca827a76f872b78a9fdae64bc298170bfcebedc1271b41a416c696365203c616c696365407379646973702e6f722e61753e88b804130102002205025331278b021b03060b090807030206150802090a0b0416020301021e01021780000a09104ef7e4beccde97f06a7003fa03c3af68d272ebc1fa08aa72a03b02189c26496a2833d90450801c4e42c5b5f51ad96ce2d2c9cef4b7c02a6a2fcf1412d6a2d486098eb762f5010a201819c17fd2888aec8eda20c65a3b75744de7ee5cc8ac7bfc470cbe3cb982720405a27a3c6a8c229cfe36905f881b02ed5680f6a8f05866efb9d6c5844897e631deb949ca8846041011020006050253312910000a09107b15a67f0b3ddc0347bc009f7fa35db59147469eb6f2c5aaf6428accb138b22800a0caa2f5f0874bacc5909c652a57a31beda65eddd5889c04100102000605025331cf7a000a0910a401d9f09a34f7c0316403ff46f2a5c101256627f16384d34a38fb47a6c88ba60506843e532d91614339fccae5f884a5741e7582ffaf292ba38ee10a270a05f139bde3814b6a077e8cd2db0f105ebea2a83af70d385f13b507fac2ad93ff79d84950328bb86f3074745a8b7f9b64990fb142e2a12976e27e8d09a28dc5621f957ac49091116da410ac3cbde1b88d04533121f6010400cbd785b56905e4192e2fb62a720727d43c4fa487821203cf72138b884b78b701093243e1d8c92a0248a6c0203a5a88693da34af357499abacaf4b3309c640797d03093870a323b4b6f37865f6eaa2838148a67df4735d43a90ca87942554cdf1c4a751b1e75f9fd4ce4e97e278d6c1c7ed59d33441df7d084f3f02beb68896c70011010001889f0418010200090502533121f6021b0c000a09104ef7e4beccde97f0b98b03fc0a5ccf6a372995835a2f5da33b282a7d612c0ab2a97f59cf9fff73e9110981aac2858c41399afa29624a7fd8a0add11654e3d882c0fd199e161bdad65e5e2548f7b68a437ea64293db1246e3011cbb94dc1bcdeaf0f2539bd88ff16d95547144d97cead6a8c5927660a91e6db0d16eb36b7b49a3525b54d1644e65599b032b7eb901a204533127a0110400bd3edaa09eff9809c4edc2c2a0ebe52e53c50a19c1e49ab78e6167bf61473bb08f2050d78a5cbbc6ed66aff7b42cd503f16b4a0b99fa1609681fca9b7ce2bbb1a5b3864d6cdda4d7ef7849d156d534dea30fb0efb9e4cf8959a2b2ce623905882d5430b995a15c3b9fe92906086788b891002924f94abe139b42cbbfaaabe42f00a0b65dc1a1ad27d798adbcb5b5ad02d2688c89477b03ff4eebb6f7b15a73b96a96bed201c0e5e4ea27e4c6e2dd1005b94d4b90137a5b1cf5e01c6226c070c4cc999938101578877ee76d296b9aab8246d57049caacf489e80a3f40589cade790a020b1ac146d6f7a6241184b8c7fcde680eae3188f5dcbe846d7f7bdad34f6fcfca08413e19c1d5df83fc7c7c627d493492e009c2f52a80400a2fe82de87136fd2e8845888c4431b032ba29d9a29a804277e31002a8201fb8591a3e55c7a0d0881496caf8b9fb07544a5a4879291d0dc026a0ea9e5bd88eb4aa4947bbd694b25012e208a250d65ddc6f1eea59d3aed3b4ec15fcab85e2afaa23a40ab1ef9ce3e11e1bc1c34a0e758e7aa64deb8739276df0af7d4121f834a9b88e70418010200090502533127a0021b02005209104ef7e4beccde97f047200419110200060502533127a0000a0910dbce4ee19529437fe045009c0b32f5ead48ee8a7e98fac0dea3d3e6c0e2c552500a0ad71fadc5007cfaf842d9b7db3335a8cdad15d3d1a6404009b08e2c68fe8f3b45c1bb72a4b3278cdf3012aa0f229883ad74aa1f6000bb90b18301b2f85372ca5d6b9bf478d235b733b1b197d19ccca48e9daf8e890cb64546b4ce1b178faccfff07003c172a2d4f5ebaba9f57153955f3f61a9b80a4f5cb959908f8b211b03b7026a8a82fc612bfedd3794969bcf458c4ce92be215a1176ab88d045331d144010400a5063000c5aaf34953c1aa3bfc95045b3aab9882b9a8027fecfe2142dc6b47ba8aca667399990244d513dd0504716908c17d92c65e74219e004f7b83fc125e575dd58efec3ab6dd22e3580106998523dea42ec75bf9aa111734c82df54630bebdff20fe981cfc36c76f865eb1c2fb62c9e85bc3a6e5015a361a2eb1c8431578d0011010001889f04280102000905025331d433021d03000a09104ef7e4beccde97f02e5503ff5e0630d1b65291f4882b6d40a29da4616bb5088717d469fbcc3648b8276de04a04988b1f1b9f3e18f52265c1f8b6c85861691c1a6b8a3a25a1809a0b32ad330aec5667cb4262f4450649184e8113849b05e5ad06a316ea80c001e8e71838190339a6e48bbde30647bcf245134b9a97fa875c1d83a9862cae87ffd7e2c4ce3a1b89013d04180102000905025331d144021b0200a809104ef7e4beccde97f09d2004190102000605025331d144000a0910677815e371c2fd23522203fe22ab62b8e7a151383cea3edd3a12995693911426f8ccf125e1f6426388c0010f88d9ca7da2224aee8d1c12135998640c5e1813d55a93df472faae75bef858457248db41b4505827590aeccf6f9eb646da7f980655dd3050c6897feddddaca90676dee856d66db8923477d251712bb9b3186b4d0114daf7d6b59272b53218dd1da94a03ff64006fcbe71211e5daecd9961fba66cdb6de3f914882c58ba5beddeba7dcb950c1156d7fba18c19ea880dccc800eae335deec34e3b84ac75ffa24864f782f87815cda1c0f634b3dd2fa67cea30811d21723d21d9551fa12ccbcfa62b6d3a15d01307b99925707992556d50065505b090aadb8579083a20fe65bd2a270da9b011"
const missingCrossSignatureKey = `-----BEGIN PGP PUBLIC KEY BLOCK-----
Charset: UTF-8
mQENBFMYynYBCACVOZ3/e8Bm2b9KH9QyIlHGo/i1bnkpqsgXj8tpJ2MIUOnXMMAY
ztW7kKFLCmgVdLIC0vSoLA4yhaLcMojznh/2CcUglZeb6Ao8Gtelr//Rd5DRfPpG
zqcfUo+m+eO1co2Orabw0tZDfGpg5p3AYl0hmxhUyYSc/xUq93xL1UJzBFgYXY54
QsM8dgeQgFseSk/YvdP5SMx1ev+eraUyiiUtWzWrWC1TdyRa5p4UZg6Rkoppf+WJ
QrW6BWrhAtqATHc8ozV7uJjeONjUEq24roRc/OFZdmQQGK6yrzKnnbA6MdHhqpdo
9kWDcXYb7pSE63Lc+OBa5X2GUVvXJLS/3nrtABEBAAG0F2ludmFsaWQtc2lnbmlu
Zy1zdWJrZXlziQEoBBMBAgASBQJTnKB5AhsBAgsHAhUIAh4BAAoJEO3UDQUIHpI/
dN4H/idX4FQ1LIZCnpHS/oxoWQWfpRgdKAEM0qCqjMgiipJeEwSQbqjTCynuh5/R
JlODDz85ABR06aoF4l5ebGLQWFCYifPnJZ/Yf5OYcMGtb7dIbqxWVFL9iLMO/oDL
ioI3dotjPui5e+2hI9pVH1UHB/bZ/GvMGo6Zg0XxLPolKQODMVjpjLAQ0YJ3spew
RAmOGre6tIvbDsMBnm8qREt7a07cBJ6XK7xjxYaZHQBiHVxyEWDa6gyANONx8duW
/fhQ/zDTnyVM/ik6VO0Ty9BhPpcEYLFwh5c1ilFari1ta3e6qKo6ZGa9YMk/REhu
yBHd9nTkI+0CiQUmbckUiVjDKKe5AQ0EUxjKdgEIAJcXQeP+NmuciE99YcJoffxv
2gVLU4ZXBNHEaP0mgaJ1+tmMD089vUQAcyGRvw8jfsNsVZQIOAuRxY94aHQhIRHR
bUzBN28ofo/AJJtfx62C15xt6fDKRV6HXYqAiygrHIpEoRLyiN69iScUsjIJeyFL
C8wa72e8pSL6dkHoaV1N9ZH/xmrJ+k0vsgkQaAh9CzYufncDxcwkoP+aOlGtX1gP
WwWoIbz0JwLEMPHBWvDDXQcQPQTYQyj+LGC9U6f9VZHN25E94subM1MjuT9OhN9Y
MLfWaaIc5WyhLFyQKW2Upofn9wSFi8ubyBnv640Dfd0rVmaWv7LNTZpoZ/GbJAMA
EQEAAYkBHwQYAQIACQUCU5ygeQIbAgAKCRDt1A0FCB6SP0zCB/sEzaVR38vpx+OQ
MMynCBJrakiqDmUZv9xtplY7zsHSQjpd6xGflbU2n+iX99Q+nav0ETQZifNUEd4N
1ljDGQejcTyKD6Pkg6wBL3x9/RJye7Zszazm4+toJXZ8xJ3800+BtaPoI39akYJm
+ijzbskvN0v/j5GOFJwQO0pPRAFtdHqRs9Kf4YanxhedB4dIUblzlIJuKsxFit6N
lgGRblagG3Vv2eBszbxzPbJjHCgVLR3RmrVezKOsZjr/2i7X+xLWIR0uD3IN1qOW
CXQxLBizEEmSNVNxsp7KPGTLnqO3bPtqFirxS9PJLIMPTPLNBY7ZYuPNTMqVIUWF
4artDmrG
=7FfJ
-----END PGP PUBLIC KEY BLOCK-----`
const invalidCrossSignatureKey = `-----BEGIN PGP PUBLIC KEY BLOCK-----
mQENBFMYynYBCACVOZ3/e8Bm2b9KH9QyIlHGo/i1bnkpqsgXj8tpJ2MIUOnXMMAY
ztW7kKFLCmgVdLIC0vSoLA4yhaLcMojznh/2CcUglZeb6Ao8Gtelr//Rd5DRfPpG
zqcfUo+m+eO1co2Orabw0tZDfGpg5p3AYl0hmxhUyYSc/xUq93xL1UJzBFgYXY54
QsM8dgeQgFseSk/YvdP5SMx1ev+eraUyiiUtWzWrWC1TdyRa5p4UZg6Rkoppf+WJ
QrW6BWrhAtqATHc8ozV7uJjeONjUEq24roRc/OFZdmQQGK6yrzKnnbA6MdHhqpdo
9kWDcXYb7pSE63Lc+OBa5X2GUVvXJLS/3nrtABEBAAG0F2ludmFsaWQtc2lnbmlu
Zy1zdWJrZXlziQEoBBMBAgASBQJTnKB5AhsBAgsHAhUIAh4BAAoJEO3UDQUIHpI/
dN4H/idX4FQ1LIZCnpHS/oxoWQWfpRgdKAEM0qCqjMgiipJeEwSQbqjTCynuh5/R
JlODDz85ABR06aoF4l5ebGLQWFCYifPnJZ/Yf5OYcMGtb7dIbqxWVFL9iLMO/oDL
ioI3dotjPui5e+2hI9pVH1UHB/bZ/GvMGo6Zg0XxLPolKQODMVjpjLAQ0YJ3spew
RAmOGre6tIvbDsMBnm8qREt7a07cBJ6XK7xjxYaZHQBiHVxyEWDa6gyANONx8duW
/fhQ/zDTnyVM/ik6VO0Ty9BhPpcEYLFwh5c1ilFari1ta3e6qKo6ZGa9YMk/REhu
yBHd9nTkI+0CiQUmbckUiVjDKKe5AQ0EUxjKdgEIAIINDqlj7X6jYKc6DjwrOkjQ
UIRWbQQar0LwmNilehmt70g5DCL1SYm9q4LcgJJ2Nhxj0/5qqsYib50OSWMcKeEe
iRXpXzv1ObpcQtI5ithp0gR53YPXBib80t3bUzomQ5UyZqAAHzMp3BKC54/vUrSK
FeRaxDzNLrCeyI00+LHNUtwghAqHvdNcsIf8VRumK8oTm3RmDh0TyjASWYbrt9c8
R1Um3zuoACOVy+mEIgIzsfHq0u7dwYwJB5+KeM7ZLx+HGIYdUYzHuUE1sLwVoELh
+SHIGHI1HDicOjzqgajShuIjj5hZTyQySVprrsLKiXS6NEwHAP20+XjayJ/R3tEA
EQEAAYkCPgQYAQIBKAUCU5ygeQIbAsBdIAQZAQIABgUCU5ygeQAKCRCpVlnFZmhO
52RJB/9uD1MSa0wjY6tHOIgquZcP3bHBvHmrHNMw9HR2wRCMO91ZkhrpdS3ZHtgb
u3/55etj0FdvDo1tb8P8FGSVtO5Vcwf5APM8sbbqoi8L951Q3i7qt847lfhu6sMl
w0LWFvPTOLHrliZHItPRjOltS1WAWfr2jUYhsU9ytaDAJmvf9DujxEOsN5G1YJep
54JCKVCkM/y585Zcnn+yxk/XwqoNQ0/iJUT9qRrZWvoeasxhl1PQcwihCwss44A+
YXaAt3hbk+6LEQuZoYS73yR3WHj+42tfm7YxRGeubXfgCEz/brETEWXMh4pe0vCL
bfWrmfSPq2rDegYcAybxRQz0lF8PAAoJEO3UDQUIHpI/exkH/0vQfdHA8g/N4T6E
i6b1CUVBAkvtdJpCATZjWPhXmShOw62gkDw306vHPilL4SCvEEi4KzG72zkp6VsB
DSRcpxCwT4mHue+duiy53/aRMtSJ+vDfiV1Vhq+3sWAck/yUtfDU9/u4eFaiNok1
8/Gd7reyuZt5CiJnpdPpjCwelK21l2w7sHAnJF55ITXdOxI8oG3BRKufz0z5lyDY
s2tXYmhhQIggdgelN8LbcMhWs/PBbtUr6uZlNJG2lW1yscD4aI529VjwJlCeo745
U7pO4eF05VViUJ2mmfoivL3tkhoTUWhx8xs8xCUcCg8DoEoSIhxtOmoTPR22Z9BL
6LCg2mg=
=Dhm4
-----END PGP PUBLIC KEY BLOCK-----`
const goodCrossSignatureKey = `-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1
mI0EVUqeVwEEAMufHRrMPWK3gyvi0O0tABCs/oON9zV9KDZlr1a1M91ShCSFwCPo
7r80PxdWVWcj0V5h50/CJYtpN3eE/mUIgW2z1uDYQF1OzrQ8ubrksfsJvpAhENom
lTQEppv9mV8qhcM278teb7TX0pgrUHLYF5CfPdp1L957JLLXoQR/lwLVABEBAAG0
E2dvb2Qtc2lnbmluZy1zdWJrZXmIuAQTAQIAIgUCVUqeVwIbAwYLCQgHAwIGFQgC
CQoLBBYCAwECHgECF4AACgkQNRjL95IRWP69XQQAlH6+eyXJN4DZTLX78KGjHrsw
6FCvxxClEPtPUjcJy/1KCRQmtLAt9PbbA78dvgzjDeZMZqRAwdjyJhjyg/fkU2OH
7wq4ktjUu+dLcOBb+BFMEY+YjKZhf6EJuVfxoTVr5f82XNPbYHfTho9/OABKH6kv
X70PaKZhbwnwij8Nts65AaIEVUqftREEAJ3WxZfqAX0bTDbQPf2CMT2IVMGDfhK7
GyubOZgDFFjwUJQvHNvsrbeGLZ0xOBumLINyPO1amIfTgJNm1iiWFWfmnHReGcDl
y5mpYG60Mb79Whdcer7CMm3AqYh/dW4g6IB02NwZMKoUHo3PXmFLxMKXnWyJ0clw
R0LI/Qn509yXAKDh1SO20rqrBM+EAP2c5bfI98kyNwQAi3buu94qo3RR1ZbvfxgW
CKXDVm6N99jdZGNK7FbRifXqzJJDLcXZKLnstnC4Sd3uyfyf1uFhmDLIQRryn5m+
LBYHfDBPN3kdm7bsZDDq9GbTHiFZUfm/tChVKXWxkhpAmHhU/tH6GGzNSMXuIWSO
aOz3Rqq0ED4NXyNKjdF9MiwD/i83S0ZBc0LmJYt4Z10jtH2B6tYdqnAK29uQaadx
yZCX2scE09UIm32/w7pV77CKr1Cp/4OzAXS1tmFzQ+bX7DR+Gl8t4wxr57VeEMvl
BGw4Vjh3X8//m3xynxycQU18Q1zJ6PkiMyPw2owZ/nss3hpSRKFJsxMLhW3fKmKr
Ey2KiOcEGAECAAkFAlVKn7UCGwIAUgkQNRjL95IRWP5HIAQZEQIABgUCVUqftQAK
CRD98VjDN10SqkWrAKDTpEY8D8HC02E/KVC5YUI01B30wgCgurpILm20kXEDCeHp
C5pygfXw1DJrhAP+NyPJ4um/bU1I+rXaHHJYroYJs8YSweiNcwiHDQn0Engh/mVZ
SqLHvbKh2dL/RXymC3+rjPvQf5cup9bPxNMa6WagdYBNAfzWGtkVISeaQW+cTEp/
MtgVijRGXR/lGLGETPg2X3Afwn9N9bLMBkBprKgbBqU7lpaoPupxT61bL70=
=vtbN
-----END PGP PUBLIC KEY BLOCK-----`
const revokedUserIDKey = `-----BEGIN PGP PUBLIC KEY BLOCK-----
mQENBFsgO5EBCADhREPmcjsPkXe1z7ctvyWL0S7oa9JaoGZ9oPDHFDlQxd0qlX2e
DZJZDg0qYvVixmaULIulApq1puEsaJCn3lHUbHlb4PYKwLEywYXM28JN91KtLsz/
uaEX2KC5WqeP40utmzkNLq+oRX/xnRMgwbO7yUNVG2UlEa6eI+xOXO3YtLdmJMBW
ClQ066ZnOIzEo1JxnIwha1CDBMWLLfOLrg6l8InUqaXbtEBbnaIYO6fXVXELUjkx
nmk7t/QOk0tXCy8muH9UDqJkwDUESY2l79XwBAcx9riX8vY7vwC34pm22fAUVLCJ
x1SJx0J8bkeNp38jKM2Zd9SUQqSbfBopQ4pPABEBAAG0I0dvbGFuZyBHb3BoZXIg
PG5vLXJlcGx5QGdvbGFuZy5jb20+iQFUBBMBCgA+FiEE5Ik5JLcNx6l6rZfw1oFy
9I6cUoMFAlsgO5ECGwMFCQPCZwAFCwkIBwMFFQoJCAsFFgIDAQACHgECF4AACgkQ
1oFy9I6cUoMIkwf8DNPeD23i4jRwd/pylbvxwZintZl1fSwTJW1xcOa1emXaEtX2
depuqhP04fjlRQGfsYAQh7X9jOJxAHjTmhqFBi5sD7QvKU00cPFYbJ/JTx0B41bl
aXnSbGhRPh63QtEZL7ACAs+shwvvojJqysx7kyVRu0EW2wqjXdHwR/SJO6nhNBa2
DXzSiOU/SUA42mmG+5kjF8Aabq9wPwT9wjraHShEweNerNMmOqJExBOy3yFeyDpa
XwEZFzBfOKoxFNkIaVf5GSdIUGhFECkGvBMB935khftmgR8APxdU4BE7XrXexFJU
8RCuPXonm4WQOwTWR0vQg64pb2WKAzZ8HhwTGbQiR29sYW5nIEdvcGhlciA8cmV2
b2tlZEBnb2xhbmcuY29tPokBNgQwAQoAIBYhBOSJOSS3Dcepeq2X8NaBcvSOnFKD
BQJbIDv3Ah0AAAoJENaBcvSOnFKDfWMIAKhI/Tvu3h8fSUxp/gSAcduT6bC1JttG
0lYQ5ilKB/58lBUA5CO3ZrKDKlzW3M8VEcvohVaqeTMKeoQd5rCZq8KxHn/KvN6N
s85REfXfniCKfAbnGgVXX3kDmZ1g63pkxrFu0fDZjVDXC6vy+I0sGyI/Inro0Pzb
tvn0QCsxjapKK15BtmSrpgHgzVqVg0cUp8vqZeKFxarYbYB2idtGRci4b9tObOK0
BSTVFy26+I/mrFGaPrySYiy2Kz5NMEcRhjmTxJ8jSwEr2O2sUR0yjbgUAXbTxDVE
/jg5fQZ1ACvBRQnB7LvMHcInbzjyeTM3FazkkSYQD6b97+dkWwb1iWG5AQ0EWyA7
kQEIALkg04REDZo1JgdYV4x8HJKFS4xAYWbIva1ZPqvDNmZRUbQZR2+gpJGEwn7z
VofGvnOYiGW56AS5j31SFf5kro1+1bZQ5iOONBng08OOo58/l1hRseIIVGB5TGSa
PCdChKKHreJI6hS3mShxH6hdfFtiZuB45rwoaArMMsYcjaezLwKeLc396cpUwwcZ
snLUNd1Xu5EWEF2OdFkZ2a1qYdxBvAYdQf4+1Nr+NRIx1u1NS9c8jp3PuMOkrQEi
bNtc1v6v0Jy52mKLG4y7mC/erIkvkQBYJdxPaP7LZVaPYc3/xskcyijrJ/5ufoD8
K71/ShtsZUXSQn9jlRaYR0EbojMAEQEAAYkBPAQYAQoAJhYhBOSJOSS3Dcepeq2X
8NaBcvSOnFKDBQJbIDuRAhsMBQkDwmcAAAoJENaBcvSOnFKDkFMIAIt64bVZ8x7+
TitH1bR4pgcNkaKmgKoZz6FXu80+SnbuEt2NnDyf1cLOSimSTILpwLIuv9Uft5Pb
OraQbYt3xi9yrqdKqGLv80bxqK0NuryNkvh9yyx5WoG1iKqMj9/FjGghuPrRaT4l
QinNAghGVkEy1+aXGFrG2DsOC1FFI51CC2WVTzZ5RwR2GpiNRfESsU1rZAUqf/2V
yJl9bD5R4SUNy8oQmhOxi+gbhD4Ao34e4W0ilibslI/uawvCiOwlu5NGd8zv5n+U
heiQvzkApQup5c+BhH5zFDFdKJ2CBByxw9+7QjMFI/wgLixKuE0Ob2kAokXf7RlB
7qTZOahrETw=
=IKnw
-----END PGP PUBLIC KEY BLOCK-----`
const keyWithSubKey = `-----BEGIN PGP PUBLIC KEY BLOCK-----
mI0EWyKwKQEEALwXhKBnyaaNFeK3ljfc/qn9X/QFw+28EUfgZPHjRmHubuXLE2uR
s3ZoSXY2z7Dkv+NyHYMt8p+X8q5fR7JvUjK2XbPyKoiJVnHINll83yl67DaWfKNL
EjNoO0kIfbXfCkZ7EG6DL+iKtuxniGTcnGT47e+HJSqb/STpLMnWwXjBABEBAAG0
I0dvbGFuZyBHb3BoZXIgPG5vLXJlcGx5QGdvbGFuZy5jb20+iM4EEwEKADgWIQQ/
lRafP/p9PytHbwxMvYJsOQdOOAUCWyKwKQIbAwULCQgHAwUVCgkICwUWAgMBAAIe
AQIXgAAKCRBMvYJsOQdOOOsFBAC62mXww8XuqvYLcVOvHkWLT6mhxrQOJXnlfpn7
2uBV9CMhoG/Ycd43NONsJrB95Apr9TDIqWnVszNbqPCuBhZQSGLdbiDKjxnCWBk0
69qv4RNtkpOhYB7jK4s8F5oQZqId6JasT/PmJTH92mhBYhhTQr0GYFuPX2UJdkw9
Sn9C67iNBFsisDUBBAC3A+Yo9lgCnxi/pfskyLrweYif6kIXWLAtLTsM6g/6jt7b
wTrknuCPyTv0QKGXsAEe/cK/Xq3HvX9WfXPGIHc/X56ZIsHQ+RLowbZV/Lhok1IW
FAuQm8axr/by80cRwFnzhfPc/ukkAq2Qyj4hLsGblu6mxeAhzcp8aqmWOO2H9QAR
AQABiLYEKAEKACAWIQQ/lRafP/p9PytHbwxMvYJsOQdOOAUCWyK16gIdAAAKCRBM
vYJsOQdOOB1vA/4u4uLONsE+2GVOyBsHyy7uTdkuxaR9b54A/cz6jT/tzUbeIzgx
22neWhgvIEghnUZd0vEyK9k1wy5vbDlEo6nKzHso32N1QExGr5upRERAxweDxGOj
7luDwNypI7QcifE64lS/JmlnunwRCdRWMKc0Fp+7jtRc5mpwyHN/Suf5RokBagQY
AQoAIBYhBD+VFp8/+n0/K0dvDEy9gmw5B044BQJbIrA1AhsCAL8JEEy9gmw5B044
tCAEGQEKAB0WIQSNdnkaWY6t62iX336UXbGvYdhXJwUCWyKwNQAKCRCUXbGvYdhX
JxJSA/9fCPHP6sUtGF1o3G1a3yvOUDGr1JWcct9U+QpbCt1mZoNopCNDDQAJvDWl
mvDgHfuogmgNJRjOMznvahbF+wpTXmB7LS0SK412gJzl1fFIpK4bgnhu0TwxNsO1
8UkCZWqxRMgcNUn9z6XWONK8dgt5JNvHSHrwF4CxxwjL23AAtK+FA/UUoi3U4kbC
0XnSr1Sl+mrzQi1+H7xyMe7zjqe+gGANtskqexHzwWPUJCPZ5qpIa2l8ghiUim6b
4ymJ+N8/T8Yva1FaPEqfMzzqJr8McYFm0URioXJPvOAlRxdHPteZ0qUopt/Jawxl
Xt6B9h1YpeLoJwjwsvbi98UTRs0jXwoY
=3fWu
-----END PGP PUBLIC KEY BLOCK-----`
const keyWithSubKeyAndBadSelfSigOrder = `-----BEGIN PGP PUBLIC KEY BLOCK-----
mI0EWyLLDQEEAOqIOpJ/ha1OYAGduu9tS3rBz5vyjbNgJO4sFveEM0mgsHQ0X9/L
plonW+d0gRoO1dhJ8QICjDAc6+cna1DE3tEb5m6JtQ30teLZuqrR398Cf6w7NNVz
r3lrlmnH9JaKRuXl7tZciwyovneBfZVCdtsRZjaLI1uMQCz/BToiYe3DABEBAAG0
I0dvbGFuZyBHb3BoZXIgPG5vLXJlcGx5QGdvbGFuZy5jb20+iM4EEwEKADgWIQRZ
sixZOfQcZdW0wUqmgmdsv1O9xgUCWyLLDQIbAwULCQgHAwUVCgkICwUWAgMBAAIe
AQIXgAAKCRCmgmdsv1O9xql2A/4pix98NxjhdsXtazA9agpAKeADf9tG4Za27Gj+
3DCww/E4iP2X35jZimSm/30QRB6j08uGCqd9vXkkJxtOt63y/IpVOtWX6vMWSTUm
k8xKkaYMP0/IzKNJ1qC/qYEUYpwERBKg9Z+k99E2Ql4kRHdxXUHq6OzY79H18Y+s
GdeM/riNBFsiyxsBBAC54Pxg/8ZWaZX1phGdwfe5mek27SOYpC0AxIDCSOdMeQ6G
HPk38pywl1d+S+KmF/F4Tdi+kWro62O4eG2uc/T8JQuRDUhSjX0Qa51gPzJrUOVT
CFyUkiZ/3ZDhtXkgfuso8ua2ChBgR9Ngr4v43tSqa9y6AK7v0qjxD1x+xMrjXQAR
AQABiQFxBBgBCgAmAhsCFiEEWbIsWTn0HGXVtMFKpoJnbL9TvcYFAlsizTIFCQAN
MRcAv7QgBBkBCgAdFiEEJcoVUVJIk5RWj1c/o62jUpRPICQFAlsiyxsACgkQo62j
UpRPICQq5gQApoWIigZxXFoM0uw4uJBS5JFZtirTANvirZV5RhndwHeMN6JttaBS
YnjyA4+n1D+zB2VqliD2QrsX12KJN6rGOehCtEIClQ1Hodo9nC6kMzzAwW1O8bZs
nRJmXV+bsvD4sidLZLjdwOVa3Cxh6pvq4Uur6a7/UYx121hEY0Qx0s8JEKaCZ2y/
U73GGi0D/i20VW8AWYAPACm2zMlzExKTOAV01YTQH/3vW0WLrOse53WcIVZga6es
HuO4So0SOEAvxKMe5HpRIu2dJxTvd99Bo9xk9xJU0AoFrO0vNCRnL+5y68xMlODK
lEw5/kl0jeaTBp6xX0HDQOEVOpPGUwWV4Ij2EnvfNDXaE1vK1kffiQFrBBgBCgAg
AhsCFiEEWbIsWTn0HGXVtMFKpoJnbL9TvcYFAlsi0AYAv7QgBBkBCgAdFiEEJcoV
UVJIk5RWj1c/o62jUpRPICQFAlsiyxsACgkQo62jUpRPICQq5gQApoWIigZxXFoM
0uw4uJBS5JFZtirTANvirZV5RhndwHeMN6JttaBSYnjyA4+n1D+zB2VqliD2QrsX
12KJN6rGOehCtEIClQ1Hodo9nC6kMzzAwW1O8bZsnRJmXV+bsvD4sidLZLjdwOVa
3Cxh6pvq4Uur6a7/UYx121hEY0Qx0s8JEKaCZ2y/U73GRl0EAJokkXmy4zKDHWWi
wvK9gi2gQgRkVnu2AiONxJb5vjeLhM/07BRmH6K1o+w3fOeEQp4FjXj1eQ5fPSM6
Hhwx2CTl9SDnPSBMiKXsEFRkmwQ2AAsQZLmQZvKBkLZYeBiwf+IY621eYDhZfo+G
1dh1WoUCyREZsJQg2YoIpWIcvw+a
=bNRo
-----END PGP PUBLIC KEY BLOCK-----
`
const onlySubkeyNoPrivateKey = `-----BEGIN PGP PRIVATE KEY BLOCK-----
Version: GnuPG v1
lQCVBFggvocBBAC7vBsHn7MKmS6IiiZNTXdciplVgS9cqVd+RTdIAoyNTcsiV1H0
GQ3QtodOPeDlQDNoqinqaobd7R9g3m3hS53Nor7yBZkCWQ5x9v9JxRtoAq0sklh1
I1X2zEqZk2l6YrfBF/64zWrhjnW3j23szkrAIVu0faQXbQ4z56tmZrw11wARAQAB
/gdlAkdOVQG0CUdOVSBEdW1teYi4BBMBAgAiBQJYIL6HAhsDBgsJCAcDAgYVCAIJ
CgsEFgIDAQIeAQIXgAAKCRCd1xxWp1CYAnjGA/9synn6ZXJUKAXQzySgmCZvCIbl
rqBfEpxwLG4Q/lONhm5vthAE0z49I8hj5Gc5e2tLYUtq0o0OCRdCrYHa/efOYWpJ
6RsK99bePOisVzmOABLIgZkcr022kHoMCmkPgv9CUGKP1yqbGl+zzAwQfUjRUmvD
ZIcWLHi2ge4GzPMPi50B2ARYIL6cAQQAxWHnicKejAFcFcF1/3gUSgSH7eiwuBPX
M7vDdgGzlve1o1jbV4tzrjN9jsCl6r0nJPDMfBSzgLr1auNTRG6HpJ4abcOx86ED
Ad+avDcQPZb7z3dPhH/gb2lQejZsHh7bbeOS8WMSzHV3RqCLd8J/xwWPNR5zKn1f
yp4IGfopidMAEQEAAQAD+wQOelnR82+dxyM2IFmZdOB9wSXQeCVOvxSaNMh6Y3lk
UOOkO8Nlic4x0ungQRvjoRs4wBmCuwFK/MII6jKui0B7dn/NDf51i7rGdNGuJXDH
e676By1sEY/NGkc74jr74T+5GWNU64W0vkpfgVmjSAzsUtpmhJMXsc7beBhJdnVl
AgDKCb8hZqj1alcdmLoNvb7ibA3K/V8J462CPD7bMySPBa/uayoFhNxibpoXml2r
oOtHa5izF3b0/9JY97F6rqkdAgD6GdTJ+xmlCoz1Sewoif1I6krq6xoa7gOYpIXo
UL1Afr+LiJeyAnF/M34j/kjIVmPanZJjry0kkjHE5ILjH3uvAf4/6n9np+Th8ujS
YDCIzKwR7639+H+qccOaddCep8Y6KGUMVdD/vTKEx1rMtK+hK/CDkkkxnFslifMJ
kqoqv3WUqCWJAT0EGAECAAkFAlggvpwCGwIAqAkQndccVqdQmAKdIAQZAQIABgUC
WCC+nAAKCRDmGUholQPwvQk+A/9latnSsR5s5/1A9TFki11GzSEnfLbx46FYOdkW
n3YBxZoPQGxNA1vIn8GmouxZInw9CF4jdOJxEdzLlYQJ9YLTLtN5tQEMl/19/bR8
/qLacAZ9IOezYRWxxZsyn6//jfl7A0Y+FV59d4YajKkEfItcIIlgVBSW6T+TNQT3
R+EH5HJ/A/4/AN0CmBhhE2vGzTnVU0VPrE4V64pjn1rufFdclgpixNZCuuqpKpoE
VVHn6mnBf4njKjZrAGPs5kfQ+H4NsM7v3Zz4yV6deu9FZc4O6E+V1WJ38rO8eBix
7G2jko106CC6vtxsCPVIzY7aaG3H5pjRtomw+pX7SzrQ7FUg2PGumg==
=F/T0
-----END PGP PRIVATE KEY BLOCK-----`
const ecdsaPrivateKey = `-----BEGIN PGP PRIVATE KEY BLOCK-----
xaUEX1KsSRMIKoZIzj0DAQcCAwTpYqJsnJiFhKKh+8TulWD+lVmerBFNS+Ii
B+nlG3T0xQQ4Sy5eIjJ0CExIQQzi3EElF/Z2l4F3WC5taFA11NgA/gkDCHSS
PThf1M2K4LN8F1MRcvR+sb7i0nH55ojkwuVB1DE6jqIT9m9i+mX1tzjSAS+6
lPQiweCJvG7xTC7Hs3AzRapf/r1At4TB+v+5G2/CKynNFEJpbGwgPGJpbGxA
aG9tZS5jb20+wncEEBMIAB8FAl9SrEkGCwkHCAMCBBUICgIDFgIBAhkBAhsD
Ah4BAAoJEMpwT3+q3+xqw5UBAMebZN9isEZ1ML+R/jWAAWMwa/knMugrEZ1v
Bl9+ZwM0AQCZdf80/wYY4Nve01qSRFv8OmKswLli3TvDv6FKc4cLz8epBF9S
rEkSCCqGSM49AwEHAgMEAjKnT9b5wY2bf9TpAV3d7OUfPOxKj9c4VzeVzSrH
AtQgo/MuI1cdYVURicV4i76DNjFhQHQFTk7BrC+C2u1yqQMBCAf+CQMIHImA
iYfzQtjgQWSFZYUkCFpbbwhNF0ch+3HNaZkaHCnZRIsWsRnc6FCb6lRQyK9+
Dq59kHlduE5QgY40894jfmP2JdJHU6nBdYrivbEdbMJhBBgTCAAJBQJfUqxJ
AhsMAAoJEMpwT3+q3+xqUI0BAMykhV08kQ4Ip9Qlbss6Jdufv7YrU0Vd5hou
b5TmiPd0APoDBh3qIic+aLLUcAuG3+Gt1P1AbUlmqV61ozn1WfHxfw==
=KLN8
-----END PGP PRIVATE KEY BLOCK-----`
const dsaPrivateKeyWithElGamalSubkey = `-----BEGIN PGP PRIVATE KEY BLOCK-----
lQOBBF9/MLsRCACeaF6BI0jTgDAs86t8/kXPfwlPvR2MCYzB0BCqAdcq1hV/GTYd
oNmJRna/ZJfsI/vf+d8Nv+EYOQkPheFS1MJVBitkAXjQPgm8i1tQWen1FCWZxqGk
/vwZYF4yo8GhZ+Wxi3w09W9Cp9QM/CTmyE1Xe7wpPBGe+oD+me8Zxjyt8JBS4Qx+
gvWbfHxfHnggh4pz7U8QkItlLsBNQEdX4R5+zwRN66g2ZSX/shaa/EkVnihUhD7r
njP9I51ORWucTQD6OvgooaNQZCkQ/Se9TzdakwWKS2XSIFXiY/e2E5ZgKI/pfKDU
iA/KessxddPb7nP/05OIJqg9AoDrD4vmehLzAQD+zsUS3LDU1m9/cG4LMsQbT2VK
Te4HqbGIAle+eu/asQf8DDJMrbZpiJZvADum9j0TJ0oep6VdMbzo9RSDKvlLKT9m
kG63H8oDWnCZm1a+HmGq9YIX+JHWmsLXXsFLeEouLzHO+mZo0X28eji3V2T87hyR
MmUM0wFo4k7jK8uVmkDXv3XwNp2uByWxUKZd7EnWmcEZWqIiexJ7XpCS0Pg3tRaI
zxve0SRe/dxfUPnTk/9KQ9hS6DWroBKquL182zx1Fggh4LIWWE2zq+UYn8BI0E8A
rmIDFJdF8ymFQGRrEy6g79NnkPmkrZWsgMRYY65P6v4zLVmqohJKkpm3/Uxa6QAP
CCoPh/JTOvPeCP2bOJH8z4Z9Py3ouMIjofQW8sXqRgf/RIHbh0KsINHrwwZ4gVIr
MK3RofpaYxw1ztPIWb4cMWoWZHH1Pxh7ggTGSBpAhKXkiWw2Rxat8QF5aA7e962c
bLvVv8dqsPrD/RnVJHag89cbPTzjn7gY9elE8EM8ithV3oQkwHTr4avYlpDZsgNd
hUW3YgRwGo31tdzxoG04AcpV2t+07P8XMPr9hsfWs4rHohXPi38Hseu1Ji+dBoWQ
3+1w/HH3o55s+jy4Ruaz78AIrjbmAJq+6rA2mIcCgrhw3DnzuwQAKeBvSeqn9zfS
ZC812osMBVmkycwelpaIh64WZ0vWL3GvdXDctV2kXM+qVpDTLEny0LuiXxrwCKQL
Ev4HAwK9uQBcreDEEud7pfRb8EYP5lzO2ZA7RaIvje6EWAGBvJGMRT0QQE5SGqc7
Fw5geigBdt+vVyRuNNhg3c2fdn/OBQaYu0J/8AiOogG8EaM8tCFlbGdhbWFsQGRz
YS5jb20gPGVsZ2FtYWxAZHNhLmNvbT6IkAQTEQgAOBYhBI+gnfiHQxB35/Dp0XAQ
aE/rsWC5BQJffzC7AhsDBQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAAAoJEHAQaE/r
sWC5A4EA/0GcJmyPtN+Klc7b9sVT3JgKTRnB/URxOJfYJofP0hZLAQCkqyMO+adV
JvbgDH0zaITQWZSSXPqpgMpCA6juTrDsd50CawRffzC7EAgAxFFFSAAEQzWTgKU5
EBtpxxoPzHqcChawTHRxHxjcELXzmUBS5PzfA1HXSPnNqK/x3Ut5ycC3CsW41Fnt
Gm3706Wu9VFbFZVn55F9lPiplUo61n5pqMvOr1gmuQsdXiTa0t5FRa4TZ2VSiHFw
vdAVSPTUsT4ZxJ1rPyFYRtq1n3pQcvdZowd07r0JnzTMjLLMFYCKhwIowoOC4zqJ
iB8enjwOlpaqBATRm9xpVF7SJkroPF6/B1vdhj7E3c1aJyHlo0PYBAg756sSHWHg
UuLyUQ4TA0hcCVenn/L/aSY2LnbdZB1EBhlYjA7dTCgwIqsQhfQmPkjz6g64A7+Y
HbbrLwADBQgAk14QIEQ+J/VHetpQV/jt2pNsFK1kVK7mXK0spTExaC2yj2sXlHjL
Ie3bO5T/KqmIaBEB5db5fA5xK9cZt79qrQHDKsEqUetUeMUWLBx77zBsus3grIgy
bwDZKseRzQ715pwxquxQlScGoDIBKEh08HpwHkq140eIj3w+MAIfndaZaSCNaxaP
Snky7BQmJ7Wc7qrIwoQP6yrnUqyW2yNi81nJYUhxjChqaFSlwzLs/iNGryBKo0ic
BqVIRjikKHBlwBng6WyrltQo/Vt9GG8w+lqaAVXbJRlaBZJUR+2NKi/YhP3qQse3
v8fi4kns0gh5LK+2C01RvdX4T49QSExuIf4HAwLJqYIGwadA2uem5v7/765ZtFWV
oL0iZ0ueTJDby4wTFDpLVzzDi/uVcB0ZRFrGOp7w6OYcNYTtV8n3xmli2Q5Trw0c
wZVzvg+ABKWiv7faBjMczIFF8y6WZKOIeAQYEQgAIBYhBI+gnfiHQxB35/Dp0XAQ
aE/rsWC5BQJffzC7AhsMAAoJEHAQaE/rsWC5ZmIA/jhS4r4lClbvjuPWt0Yqdn7R
fss2SPMYvMrrDh42aE0OAQD8xn4G6CN8UtW9xihXOY6FpxiJ/sMc2VaneeUd34oa
4g==
=XZm8
-----END PGP PRIVATE KEY BLOCK-----`
// https://tests.sequoia-pgp.org/#Certificate_expiration
// P _ U p
const expiringPrimaryUIDKey = `-----BEGIN PGP PUBLIC KEY BLOCK-----
xsDNBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv
/seOXpgecTdOcVttfzC8ycIKrt3aQTiwOG/ctaR4Bk/t6ayNFfdUNxHWk4WCKzdz
/56fW2O0F23qIRd8UUJp5IIlN4RDdRCtdhVQIAuzvp2oVy/LaS2kxQoKvph/5pQ/
5whqsyroEWDJoSV0yOb25B/iwk/pLUFoyhDG9bj0kIzDxrEqW+7Ba8nocQlecMF3
X5KMN5kp2zraLv9dlBBpWW43XktjcCZgMy20SouraVma8Je/ECwUWYUiAZxLIlMv
9CurEOtxUw6N3RdOtLmYZS9uEnn5y1UkF88o8Nku890uk6BrewFzJyLAx5wRZ4F0
qV/yq36UWQ0JB/AUGhHVPdFf6pl6eaxBwT5GXvbBUibtf8YI2og5RsgTWtXfU7eb
SGXrl5ZMpbA6mbfhd0R8aPxWfmDWiIOhBufhMCvUHh1sApMKVZnvIff9/0Dca3wb
vLIwa3T4CyshfT0AEQEAAc0hQm9iIEJhYmJhZ2UgPGJvYkBvcGVucGdwLmV4YW1w
bGU+wsEUBBMBCgBIBYJfiWKKBYkBX+/UBQsJCAcCCRD7/MgqAV5zMAYVCgkICwIE
FgIDAQIXgAIbAwIeARYhBNGmbhojsYLJmA94jPv8yCoBXnMwAAAf0wv/a++a3DkL
CttbK4LRIiSry2wb97mxYQoWYzvkKYD1IP/KiamRwzjBKuRSE0qZ2uonQDRGc+zl
1H9dwkDLL4T9uJngCCPCBgFW/hOFPvF+WYEKHtOzunqx6KwDHkpdH+hpzfFzIhDo
aXiVnDGvJ3H/bVTKq1m2KPXO2ckkXPQXJX9Fx6kPHdvcq+3ZI75IzbD/ue5lBcsy
OKLzVu+KLxzlzGui6v1V0fTvU/uqvHlvUxcDAqMnIsDUPakjK2RDeQ38qtN6+PQ5
/dT7vx2Wtzesqn2eDbDf5uRfSgmp2hJLJniAKjMCBVAJiOgPb0LXUIcwCGxaiWOA
g5ZvNWjX5bZ/FxqpLpOE9OReI5YY7ns7zqP4thLYYWe0Qdp9a2ezVrgzgrh/SLla
d73x/S9TrmLtYGGlbVUByJW+GXjW2Tt6iaa/WDFzx8NvZ/wzIAdGSEfLcvS+JBSP
2ppdY5Ac/2dK3PzYABkHvB/rhXIwlXnrFDU9efRHZfFqQqGauA64wfdIzsDNBF2l
nPIBDADWML9cbGMrp12CtF9b2P6z9TTT74S8iyBOzaSvdGDQY/sUtZXRg21HWamX
nn9sSXvIDEINOQ6A9QxdxoqWdCHrOuW3ofneYXoG+zeKc4dC86wa1TR2q9vW+RMX
SO4uImA+Uzula/6k1DogDf28qhCxMwG/i/m9g1c/0aApuDyKdQ1PXsHHNlgd/Dn6
rrd5y2AObaifV7wIhEJnvqgFXDN2RXGjLeCOHV4Q2WTYPg/S4k1nMXVDwZXrvIsA
0YwIMgIT86Rafp1qKlgPNbiIlC1g9RY/iFaGN2b4Ir6GDohBQSfZW2+LXoPZuVE/
wGlQ01rh827KVZW4lXvqsge+wtnWlszcselGATyzqOK9LdHPdZGzROZYI2e8c+pa
LNDdVPL6vdRBUnkCaEkOtl1mr2JpQi5nTU+gTX4IeInC7E+1a9UDF/Y85ybUz8XV
8rUnR76UqVC7KidNepdHbZjjXCt8/Zo+Tec9JNbYNQB/e9ExmDntmlHEsSEQzFwz
j8sxH48AEQEAAcLA9gQYAQoAIBYhBNGmbhojsYLJmA94jPv8yCoBXnMwBQJdpZzy
AhsMAAoJEPv8yCoBXnMw6f8L/26C34dkjBffTzMj5Bdzm8MtF67OYneJ4TQMw7+4
1IL4rVcSKhIhk/3Ud5knaRtP2ef1+5F66h9/RPQOJ5+tvBwhBAcUWSupKnUrdVaZ
QanYmtSxcVV2PL9+QEiNN3tzluhaWO//rACxJ+K/ZXQlIzwQVTpNhfGzAaMVV9zp
f3u0k14itcv6alKY8+rLZvO1wIIeRZLmU0tZDD5HtWDvUV7rIFI1WuoLb+KZgbYn
3OWjCPHVdTrdZ2CqnZbG3SXw6awH9bzRLV9EXkbhIMez0deCVdeo+wFFklh8/5VK
2b0vk/+wqMJxfpa1lHvJLobzOP9fvrswsr92MA2+k901WeISR7qEzcI0Fdg8AyFA
ExaEK6VyjP7SXGLwvfisw34OxuZr3qmx1Sufu4toH3XrB7QJN8XyqqbsGxUCBqWi
f9RSK4xjzRTe56iPeiSJJOIciMP9i2ldI+KgLycyeDvGoBj0HCLO3gVaBe4ubVrj
5KjhX2PVNEJd3XZRzaXZE2aAMQ==
=522n
-----END PGP PUBLIC KEY BLOCK-----`

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) l := 1 << power
w.buf = append(w.buf, p...) if bufLen >= l {
if len(w.buf) < minFirstPartialWrite { w.lengthByte[0] = 224 + uint8(power)
return len(p), nil _, err = w.w.Write(w.lengthByte[:])
if err != nil {
return
}
var m int
m, err = w.w.Write(w.buf.Next(l))
if err != nil {
return
}
if m != l {
return 0, io.ErrShortWrite
}
break
} }
p = w.buf
w.buf = nil
} }
w.sentFirst = true
} }
return w.buf.Write(p)
power := uint8(30)
for len(p) > 0 {
l := 1 << power
if len(p) < l {
power = uint8(bits.Len32(uint32(len(p)))) - 1
l = 1 << power
}
w.lengthByte[0] = 224 + power
_, 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 n < off {
return 0, err
}
return n - off, err
}
p = p[l:]
}
return n - off, nil
} }
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 return err
w.sentFirst = true
w.buf = nil
if _, err := w.Write(p); err != nil {
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 p = new(Signature)
// Detect signature version
if contents, version, err = peekVersion(contents); err != nil {
return
}
if version < 4 {
p = new(SignatureV3)
} else {
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 = &PublicKey{IsSubkey: isSubkey}
p = &PublicKeyV3{IsSubkey: isSubkey}
} else {
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
@ -39,12 +41,19 @@ type Signature struct {
HashSuffix []byte HashSuffix []byte
// 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.)
SignatureError error // nil if the signature is good. Signature *packet.Signature // the signature packet itself.
Signature *packet.Signature // the signature packet itself, if v4 (default) SignatureError error // nil if the signature is good.
SignatureV3 *packet.SignatureV3 // the signature packet if it is a v2 or v3 signature UnverifiedSignatures []*packet.Signature // all other unverified signature packets.
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 {
continue
}
} }
if len(pk.encryptedKey.Key) == 0 { // Try to decrypt symmetrically encrypted
continue decrypted, err = edp.Decrypt(pk.encryptedKey.CipherFunc, pk.encryptedKey.Key)
}
decrypted, err = se.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)
scr.wrappedHash.Write(buf[:n])
if err == io.EOF { // Hash only if required
if scr.md.SignedBy != nil {
scr.wrappedHash.Write(buf[:n])
}
if sensitiveParsingError == io.EOF {
var p packet.Packet var p packet.Packet
p, scr.md.SignatureError = scr.packets.Next() var readError error
if scr.md.SignatureError != nil { var sig *packet.Signature
return
p, readError = scr.packets.Next()
for readError == nil {
var ok bool
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)
if scr.md.SignatureError == nil && scr.md.Signature.SigExpired(scr.config.Now()) {
scr.md.SignatureError = errors.ErrSignatureExpired
}
} else {
scr.md.UnverifiedSignatures = append(scr.md.UnverifiedSignatures, sig)
}
}
p, readError = scr.packets.Next()
} }
var ok bool if scr.md.SignedBy != nil && scr.md.Signature == nil {
if scr.md.Signature, ok = p.(*packet.Signature); ok { if scr.md.UnverifiedSignatures == nil {
scr.md.SignatureError = scr.md.SignedBy.PublicKey.VerifySignature(scr.h, scr.md.Signature) scr.md.SignatureError = errors.StructuralError("LiteralData not followed by signature")
} else if scr.md.SignatureV3, ok = p.(*packet.SignatureV3); ok { } else {
scr.md.SignatureError = scr.md.SignedBy.PublicKey.VerifySignatureV3(scr.h, scr.md.SignatureV3) scr.md.SignatureError = errors.StructuralError("No matching signature found")
} else { }
scr.md.SignatureError = errors.StructuralError("LiteralData not followed by Signature")
return
} }
// 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,21 +439,26 @@ 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 sig.IssuerKeyId == nil { if !ok {
return nil, errors.StructuralError("signature doesn't have an issuer")
}
issuerKeyId = *sig.IssuerKeyId
hashFunc = sig.Hash
sigType = sig.SigType
case *packet.SignatureV3:
issuerKeyId = sig.IssuerKeyId
hashFunc = sig.Hash
sigType = sig.SigType
default:
return nil, errors.StructuralError("non signature packet found") return nil, errors.StructuralError("non signature packet found")
} }
if sig.IssuerKeyId == nil {
return nil, errors.StructuralError("signature doesn't have an issuer")
}
issuerKeyId = *sig.IssuerKeyId
hashFunc = sig.Hash
sigType = sig.SigType
for i, expectedHash := range expectedHashes {
if hashFunc == expectedHash {
break
}
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)
if len(keys) > 0 { if len(keys) > 0 {
@ -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) { err = key.PublicKey.VerifySignature(h, sig)
case *packet.Signature:
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)
} }

View File

@ -0,0 +1,173 @@
package openpgp
const testKey1KeyId = 0xA34D7E18C20C31BB
const testKey3KeyId = 0x338934250CCC0360
const testKeyP256KeyId = 0xd44a2c495918513e
const signedInput = "Signed message\nline 2\nline 3\n"
const signedTextInput = "Signed message\r\nline 2\r\nline 3\r\n"
const recipientUnspecifiedHex = "848c0300000000000000000103ff62d4d578d03cf40c3da998dfe216c074fa6ddec5e31c197c9666ba292830d91d18716a80f699f9d897389a90e6d62d0238f5f07a5248073c0f24920e4bc4a30c2d17ee4e0cae7c3d4aaa4e8dced50e3010a80ee692175fa0385f62ecca4b56ee6e9980aa3ec51b61b077096ac9e800edaf161268593eedb6cc7027ff5cb32745d250010d407a6221ae22ef18469b444f2822478c4d190b24d36371a95cb40087cdd42d9399c3d06a53c0673349bfb607927f20d1e122bde1e2bf3aa6cae6edf489629bcaa0689539ae3b718914d88ededc3b"
const detachedSignatureHex = "889c04000102000605024d449cd1000a0910a34d7e18c20c31bb167603ff57718d09f28a519fdc7b5a68b6a3336da04df85e38c5cd5d5bd2092fa4629848a33d85b1729402a2aab39c3ac19f9d573f773cc62c264dc924c067a79dfd8a863ae06c7c8686120760749f5fd9b1e03a64d20a7df3446ddc8f0aeadeaeba7cbaee5c1e366d65b6a0c6cc749bcb912d2f15013f812795c2e29eb7f7b77f39ce77"
const detachedSignatureTextHex = "889c04010102000605024d449d21000a0910a34d7e18c20c31bbc8c60400a24fbef7342603a41cb1165767bd18985d015fb72fe05db42db36cfb2f1d455967f1e491194fbf6cf88146222b23bf6ffbd50d17598d976a0417d3192ff9cc0034fd00f287b02e90418bbefe609484b09231e4e7a5f3562e199bf39909ab5276c4d37382fe088f6b5c3426fc1052865da8b3ab158672d58b6264b10823dc4b39"
const detachedSignatureDSAHex = "884604001102000605024d6c4eac000a0910338934250ccc0360f18d00a087d743d6405ed7b87755476629600b8b694a39e900a0abff8126f46faf1547c1743c37b21b4ea15b8f83"
const detachedSignatureP256Hex = "885e0400130a0006050256e5bb00000a0910d44a2c495918513edef001009841a4f792beb0befccb35c8838a6a87d9b936beaa86db6745ddc7b045eee0cf00fd1ac1f78306b17e965935dd3f8bae4587a76587e4af231efe19cc4011a8434817"
// The plaintext is https://www.gutenberg.org/cache/epub/1080/pg1080.txt
const modestProposalSha512 = "lbbrB1+WP3T9AaC9OQqBdOcCjgeEQadlulXsNPgVx0tyqPzDHwUugZ2gE7V0ESKAw6kAVfgkcuvfgxAAGaeHtw=="
const testKeys1And2Hex = "988d044d3c5c10010400b1d13382944bd5aba23a4312968b5095d14f947f600eb478e14a6fcb16b0e0cac764884909c020bc495cfcc39a935387c661507bdb236a0612fb582cac3af9b29cc2c8c70090616c41b662f4da4c1201e195472eb7f4ae1ccbcbf9940fe21d985e379a5563dde5b9a23d35f1cfaa5790da3b79db26f23695107bfaca8e7b5bcd0011010001b41054657374204b6579203120285253412988b804130102002205024d3c5c10021b03060b090807030206150802090a0b0416020301021e01021780000a0910a34d7e18c20c31bbb5b304009cc45fe610b641a2c146331be94dade0a396e73ca725e1b25c21708d9cab46ecca5ccebc23055879df8f99eea39b377962a400f2ebdc36a7c99c333d74aeba346315137c3ff9d0a09b0273299090343048afb8107cf94cbd1400e3026f0ccac7ecebbc4d78588eb3e478fe2754d3ca664bcf3eac96ca4a6b0c8d7df5102f60f6b0020003b88d044d3c5c10010400b201df61d67487301f11879d514f4248ade90c8f68c7af1284c161098de4c28c2850f1ec7b8e30f959793e571542ffc6532189409cb51c3d30dad78c4ad5165eda18b20d9826d8707d0f742e2ab492103a85bbd9ddf4f5720f6de7064feb0d39ee002219765bb07bcfb8b877f47abe270ddeda4f676108cecb6b9bb2ad484a4f0011010001889f04180102000905024d3c5c10021b0c000a0910a34d7e18c20c31bb1a03040085c8d62e16d05dc4e9dad64953c8a2eed8b6c12f92b1575eeaa6dcf7be9473dd5b24b37b6dffbb4e7c99ed1bd3cb11634be19b3e6e207bed7505c7ca111ccf47cb323bf1f8851eb6360e8034cbff8dd149993c959de89f8f77f38e7e98b8e3076323aa719328e2b408db5ec0d03936efd57422ba04f925cdc7b4c1af7590e40ab0020003988d044d3c5c33010400b488c3e5f83f4d561f317817538d9d0397981e9aef1321ca68ebfae1cf8b7d388e19f4b5a24a82e2fbbf1c6c26557a6c5845307a03d815756f564ac7325b02bc83e87d5480a8fae848f07cb891f2d51ce7df83dcafdc12324517c86d472cc0ee10d47a68fd1d9ae49a6c19bbd36d82af597a0d88cc9c49de9df4e696fc1f0b5d0011010001b42754657374204b6579203220285253412c20656e637279707465642070726976617465206b65792988b804130102002205024d3c5c33021b03060b090807030206150802090a0b0416020301021e01021780000a0910d4984f961e35246b98940400908a73b6a6169f700434f076c6c79015a49bee37130eaf23aaa3cfa9ce60bfe4acaa7bc95f1146ada5867e0079babb38804891f4f0b8ebca57a86b249dee786161a755b7a342e68ccf3f78ed6440a93a6626beb9a37aa66afcd4f888790cb4bb46d94a4ae3eb3d7d3e6b00f6bfec940303e89ec5b32a1eaaacce66497d539328b0020003b88d044d3c5c33010400a4e913f9442abcc7f1804ccab27d2f787ffa592077ca935a8bb23165bd8d57576acac647cc596b2c3f814518cc8c82953c7a4478f32e0cf645630a5ba38d9618ef2bc3add69d459ae3dece5cab778938d988239f8c5ae437807075e06c828019959c644ff05ef6a5a1dab72227c98e3a040b0cf219026640698d7a13d8538a570011010001889f04180102000905024d3c5c33021b0c000a0910d4984f961e35246b26c703ff7ee29ef53bc1ae1ead533c408fa136db508434e233d6e62be621e031e5940bbd4c08142aed0f82217e7c3e1ec8de574bc06ccf3c36633be41ad78a9eacd209f861cae7b064100758545cc9dd83db71806dc1cfd5fb9ae5c7474bba0c19c44034ae61bae5eca379383339dece94ff56ff7aa44a582f3e5c38f45763af577c0934b0020003"
const testKeys1And2PrivateHex = "9501d8044d3c5c10010400b1d13382944bd5aba23a4312968b5095d14f947f600eb478e14a6fcb16b0e0cac764884909c020bc495cfcc39a935387c661507bdb236a0612fb582cac3af9b29cc2c8c70090616c41b662f4da4c1201e195472eb7f4ae1ccbcbf9940fe21d985e379a5563dde5b9a23d35f1cfaa5790da3b79db26f23695107bfaca8e7b5bcd00110100010003ff4d91393b9a8e3430b14d6209df42f98dc927425b881f1209f319220841273a802a97c7bdb8b3a7740b3ab5866c4d1d308ad0d3a79bd1e883aacf1ac92dfe720285d10d08752a7efe3c609b1d00f17f2805b217be53999a7da7e493bfc3e9618fd17018991b8128aea70a05dbce30e4fbe626aa45775fa255dd9177aabf4df7cf0200c1ded12566e4bc2bb590455e5becfb2e2c9796482270a943343a7835de41080582c2be3caf5981aa838140e97afa40ad652a0b544f83eb1833b0957dce26e47b0200eacd6046741e9ce2ec5beb6fb5e6335457844fb09477f83b050a96be7da043e17f3a9523567ed40e7a521f818813a8b8a72209f1442844843ccc7eb9805442570200bdafe0438d97ac36e773c7162028d65844c4d463e2420aa2228c6e50dc2743c3d6c72d0d782a5173fe7be2169c8a9f4ef8a7cf3e37165e8c61b89c346cdc6c1799d2b41054657374204b6579203120285253412988b804130102002205024d3c5c10021b03060b090807030206150802090a0b0416020301021e01021780000a0910a34d7e18c20c31bbb5b304009cc45fe610b641a2c146331be94dade0a396e73ca725e1b25c21708d9cab46ecca5ccebc23055879df8f99eea39b377962a400f2ebdc36a7c99c333d74aeba346315137c3ff9d0a09b0273299090343048afb8107cf94cbd1400e3026f0ccac7ecebbc4d78588eb3e478fe2754d3ca664bcf3eac96ca4a6b0c8d7df5102f60f6b00200009d01d8044d3c5c10010400b201df61d67487301f11879d514f4248ade90c8f68c7af1284c161098de4c28c2850f1ec7b8e30f959793e571542ffc6532189409cb51c3d30dad78c4ad5165eda18b20d9826d8707d0f742e2ab492103a85bbd9ddf4f5720f6de7064feb0d39ee002219765bb07bcfb8b877f47abe270ddeda4f676108cecb6b9bb2ad484a4f00110100010003fd17a7490c22a79c59281fb7b20f5e6553ec0c1637ae382e8adaea295f50241037f8997cf42c1ce26417e015091451b15424b2c59eb8d4161b0975630408e394d3b00f88d4b4e18e2cc85e8251d4753a27c639c83f5ad4a571c4f19d7cd460b9b73c25ade730c99df09637bd173d8e3e981ac64432078263bb6dc30d3e974150dd0200d0ee05be3d4604d2146fb0457f31ba17c057560785aa804e8ca5530a7cd81d3440d0f4ba6851efcfd3954b7e68908fc0ba47f7ac37bf559c6c168b70d3a7c8cd0200da1c677c4bce06a068070f2b3733b0a714e88d62aa3f9a26c6f5216d48d5c2b5624144f3807c0df30be66b3268eeeca4df1fbded58faf49fc95dc3c35f134f8b01fd1396b6c0fc1b6c4f0eb8f5e44b8eace1e6073e20d0b8bc5385f86f1cf3f050f66af789f3ef1fc107b7f4421e19e0349c730c68f0a226981f4e889054fdb4dc149e8e889f04180102000905024d3c5c10021b0c000a0910a34d7e18c20c31bb1a03040085c8d62e16d05dc4e9dad64953c8a2eed8b6c12f92b1575eeaa6dcf7be9473dd5b24b37b6dffbb4e7c99ed1bd3cb11634be19b3e6e207bed7505c7ca111ccf47cb323bf1f8851eb6360e8034cbff8dd149993c959de89f8f77f38e7e98b8e3076323aa719328e2b408db5ec0d03936efd57422ba04f925cdc7b4c1af7590e40ab00200009501fe044d3c5c33010400b488c3e5f83f4d561f317817538d9d0397981e9aef1321ca68ebfae1cf8b7d388e19f4b5a24a82e2fbbf1c6c26557a6c5845307a03d815756f564ac7325b02bc83e87d5480a8fae848f07cb891f2d51ce7df83dcafdc12324517c86d472cc0ee10d47a68fd1d9ae49a6c19bbd36d82af597a0d88cc9c49de9df4e696fc1f0b5d0011010001fe030302e9030f3c783e14856063f16938530e148bc57a7aa3f3e4f90df9dceccdc779bc0835e1ad3d006e4a8d7b36d08b8e0de5a0d947254ecfbd22037e6572b426bcfdc517796b224b0036ff90bc574b5509bede85512f2eefb520fb4b02aa523ba739bff424a6fe81c5041f253f8d757e69a503d3563a104d0d49e9e890b9d0c26f96b55b743883b472caa7050c4acfd4a21f875bdf1258d88bd61224d303dc9df77f743137d51e6d5246b88c406780528fd9a3e15bab5452e5b93970d9dcc79f48b38651b9f15bfbcf6da452837e9cc70683d1bdca94507870f743e4ad902005812488dd342f836e72869afd00ce1850eea4cfa53ce10e3608e13d3c149394ee3cbd0e23d018fcbcb6e2ec5a1a22972d1d462ca05355d0d290dd2751e550d5efb38c6c89686344df64852bf4ff86638708f644e8ec6bd4af9b50d8541cb91891a431326ab2e332faa7ae86cfb6e0540aa63160c1e5cdd5a4add518b303fff0a20117c6bc77f7cfbaf36b04c865c6c2b42754657374204b6579203220285253412c20656e637279707465642070726976617465206b65792988b804130102002205024d3c5c33021b03060b090807030206150802090a0b0416020301021e01021780000a0910d4984f961e35246b98940400908a73b6a6169f700434f076c6c79015a49bee37130eaf23aaa3cfa9ce60bfe4acaa7bc95f1146ada5867e0079babb38804891f4f0b8ebca57a86b249dee786161a755b7a342e68ccf3f78ed6440a93a6626beb9a37aa66afcd4f888790cb4bb46d94a4ae3eb3d7d3e6b00f6bfec940303e89ec5b32a1eaaacce66497d539328b00200009d01fe044d3c5c33010400a4e913f9442abcc7f1804ccab27d2f787ffa592077ca935a8bb23165bd8d57576acac647cc596b2c3f814518cc8c82953c7a4478f32e0cf645630a5ba38d9618ef2bc3add69d459ae3dece5cab778938d988239f8c5ae437807075e06c828019959c644ff05ef6a5a1dab72227c98e3a040b0cf219026640698d7a13d8538a570011010001fe030302e9030f3c783e148560f936097339ae381d63116efcf802ff8b1c9360767db5219cc987375702a4123fd8657d3e22700f23f95020d1b261eda5257e9a72f9a918e8ef22dd5b3323ae03bbc1923dd224db988cadc16acc04b120a9f8b7e84da9716c53e0334d7b66586ddb9014df604b41be1e960dcfcbc96f4ed150a1a0dd070b9eb14276b9b6be413a769a75b519a53d3ecc0c220e85cd91ca354d57e7344517e64b43b6e29823cbd87eae26e2b2e78e6dedfbb76e3e9f77bcb844f9a8932eb3db2c3f9e44316e6f5d60e9e2a56e46b72abe6b06dc9a31cc63f10023d1f5e12d2a3ee93b675c96f504af0001220991c88db759e231b3320dcedf814dcf723fd9857e3d72d66a0f2af26950b915abdf56c1596f46a325bf17ad4810d3535fb02a259b247ac3dbd4cc3ecf9c51b6c07cebb009c1506fba0a89321ec8683e3fd009a6e551d50243e2d5092fefb3321083a4bad91320dc624bd6b5dddf93553e3d53924c05bfebec1fb4bd47e89a1a889f04180102000905024d3c5c33021b0c000a0910d4984f961e35246b26c703ff7ee29ef53bc1ae1ead533c408fa136db508434e233d6e62be621e031e5940bbd4c08142aed0f82217e7c3e1ec8de574bc06ccf3c36633be41ad78a9eacd209f861cae7b064100758545cc9dd83db71806dc1cfd5fb9ae5c7474bba0c19c44034ae61bae5eca379383339dece94ff56ff7aa44a582f3e5c38f45763af577c0934b0020000"
const dsaElGamalTestKeysHex = "9501e1044dfcb16a110400aa3e5c1a1f43dd28c2ffae8abf5cfce555ee874134d8ba0a0f7b868ce2214beddc74e5e1e21ded354a95d18acdaf69e5e342371a71fbb9093162e0c5f3427de413a7f2c157d83f5cd2f9d791256dc4f6f0e13f13c3302af27f2384075ab3021dff7a050e14854bbde0a1094174855fc02f0bae8e00a340d94a1f22b32e48485700a0cec672ac21258fb95f61de2ce1af74b2c4fa3e6703ff698edc9be22c02ae4d916e4fa223f819d46582c0516235848a77b577ea49018dcd5e9e15cff9dbb4663a1ae6dd7580fa40946d40c05f72814b0f88481207e6c0832c3bded4853ebba0a7e3bd8e8c66df33d5a537cd4acf946d1080e7a3dcea679cb2b11a72a33a2b6a9dc85f466ad2ddf4c3db6283fa645343286971e3dd700703fc0c4e290d45767f370831a90187e74e9972aae5bff488eeff7d620af0362bfb95c1a6c3413ab5d15a2e4139e5d07a54d72583914661ed6a87cce810be28a0aa8879a2dd39e52fb6fe800f4f181ac7e328f740cde3d09a05cecf9483e4cca4253e60d4429ffd679d9996a520012aad119878c941e3cf151459873bdfc2a9563472fe0303027a728f9feb3b864260a1babe83925ce794710cfd642ee4ae0e5b9d74cee49e9c67b6cd0ea5dfbb582132195a121356a1513e1bca73e5b80c58c7ccb4164453412f456c47616d616c2054657374204b65792031886204131102002205024dfcb16a021b03060b090807030206150802090a0b0416020301021e01021780000a091033af447ccd759b09fadd00a0b8fd6f5a790bad7e9f2dbb7632046dc4493588db009c087c6a9ba9f7f49fab221587a74788c00db4889ab00200009d0157044dfcb16a1004008dec3f9291205255ccff8c532318133a6840739dd68b03ba942676f9038612071447bf07d00d559c5c0875724ea16a4c774f80d8338b55fca691a0522e530e604215b467bbc9ccfd483a1da99d7bc2648b4318fdbd27766fc8bfad3fddb37c62b8ae7ccfe9577e9b8d1e77c1d417ed2c2ef02d52f4da11600d85d3229607943700030503ff506c94c87c8cab778e963b76cf63770f0a79bf48fb49d3b4e52234620fc9f7657f9f8d56c96a2b7c7826ae6b57ebb2221a3fe154b03b6637cea7e6d98e3e45d87cf8dc432f723d3d71f89c5192ac8d7290684d2c25ce55846a80c9a7823f6acd9bb29fa6cd71f20bc90eccfca20451d0c976e460e672b000df49466408d527affe0303027a728f9feb3b864260abd761730327bca2aaa4ea0525c175e92bf240682a0e83b226f97ecb2e935b62c9a133858ce31b271fa8eb41f6a1b3cd72a63025ce1a75ee4180dcc284884904181102000905024dfcb16a021b0c000a091033af447ccd759b09dd0b009e3c3e7296092c81bee5a19929462caaf2fff3ae26009e218c437a2340e7ea628149af1ec98ec091a43992b00200009501e1044dfcb1be1104009f61faa61aa43df75d128cbe53de528c4aec49ce9360c992e70c77072ad5623de0a3a6212771b66b39a30dad6781799e92608316900518ec01184a85d872365b7d2ba4bacfb5882ea3c2473d3750dc6178cc1cf82147fb58caa28b28e9f12f6d1efcb0534abed644156c91cca4ab78834268495160b2400bc422beb37d237c2300a0cac94911b6d493bda1e1fbc6feeca7cb7421d34b03fe22cec6ccb39675bb7b94a335c2b7be888fd3906a1125f33301d8aa6ec6ee6878f46f73961c8d57a3e9544d8ef2a2cbfd4d52da665b1266928cfe4cb347a58c412815f3b2d2369dec04b41ac9a71cc9547426d5ab941cccf3b18575637ccfb42df1a802df3cfe0a999f9e7109331170e3a221991bf868543960f8c816c28097e503fe319db10fb98049f3a57d7c80c420da66d56f3644371631fad3f0ff4040a19a4fedc2d07727a1b27576f75a4d28c47d8246f27071e12d7a8de62aad216ddbae6aa02efd6b8a3e2818cda48526549791ab277e447b3a36c57cefe9b592f5eab73959743fcc8e83cbefec03a329b55018b53eec196765ae40ef9e20521a603c551efe0303020950d53a146bf9c66034d00c23130cce95576a2ff78016ca471276e8227fb30b1ffbd92e61804fb0c3eff9e30b1a826ee8f3e4730b4d86273ca977b4164453412f456c47616d616c2054657374204b65792032886204131102002205024dfcb1be021b03060b090807030206150802090a0b0416020301021e01021780000a0910a86bf526325b21b22bd9009e34511620415c974750a20df5cb56b182f3b48e6600a0a9466cb1a1305a84953445f77d461593f1d42bc1b00200009d0157044dfcb1be1004009565a951da1ee87119d600c077198f1c1bceb0f7aa54552489298e41ff788fa8f0d43a69871f0f6f77ebdfb14a4260cf9fbeb65d5844b4272a1904dd95136d06c3da745dc46327dd44a0f16f60135914368c8039a34033862261806bb2c5ce1152e2840254697872c85441ccb7321431d75a747a4bfb1d2c66362b51ce76311700030503fc0ea76601c196768070b7365a200e6ddb09307f262d5f39eec467b5f5784e22abdf1aa49226f59ab37cb49969d8f5230ea65caf56015abda62604544ed526c5c522bf92bed178a078789f6c807b6d34885688024a5bed9e9f8c58d11d4b82487b44c5f470c5606806a0443b79cadb45e0f897a561a53f724e5349b9267c75ca17fe0303020950d53a146bf9c660bc5f4ce8f072465e2d2466434320c1e712272fafc20e342fe7608101580fa1a1a367e60486a7cd1246b7ef5586cf5e10b32762b710a30144f12dd17dd4884904181102000905024dfcb1be021b0c000a0910a86bf526325b21b2904c00a0b2b66b4b39ccffda1d10f3ea8d58f827e30a8b8e009f4255b2d8112a184e40cde43a34e8655ca7809370b0020000"
const signedMessageHex = "a3019bc0cbccc0c4b8d8b74ee2108fe16ec6d3ca490cbe362d3f8333d3f352531472538b8b13d353b97232f352158c20943157c71c16064626063656269052062e4e01987e9b6fccff4b7df3a34c534b23e679cbec3bc0f8f6e64dfb4b55fe3f8efa9ce110ddb5cd79faf1d753c51aecfa669f7e7aa043436596cccc3359cb7dd6bbe9ecaa69e5989d9e57209571edc0b2fa7f57b9b79a64ee6e99ce1371395fee92fec2796f7b15a77c386ff668ee27f6d38f0baa6c438b561657377bf6acff3c5947befd7bf4c196252f1d6e5c524d0300"
const signedTextMessageHex = "a3019bc0cbccc8c4b8d8b74ee2108fe16ec6d36a250cbece0c178233d3f352531472538b8b13d35379b97232f352158ca0b4312f57c71c1646462606365626906a062e4e019811591798ff99bf8afee860b0d8a8c2a85c3387e3bcf0bb3b17987f2bbcfab2aa526d930cbfd3d98757184df3995c9f3e7790e36e3e9779f06089d4c64e9e47dd6202cb6e9bc73c5d11bb59fbaf89d22d8dc7cf199ddf17af96e77c5f65f9bbed56f427bd8db7af37f6c9984bf9385efaf5f184f986fb3e6adb0ecfe35bbf92d16a7aa2a344fb0bc52fb7624f0200"
const signedEncryptedMessageHex = "c18c032a67d68660df41c70103ff5a84c9a72f80e74ef0384c2d6a9ebfe2b09e06a8f298394f6d2abf174e40934ab0ec01fb2d0ddf21211c6fe13eb238563663b017a6b44edca552eb4736c4b7dc6ed907dd9e12a21b51b64b46f902f76fb7aaf805c1db8070574d8d0431a23e324a750f77fb72340a17a42300ee4ca8207301e95a731da229a63ab9c6b44541fbd2c11d016d810b3b3b2b38f15b5b40f0a4910332829c2062f1f7cc61f5b03677d73c54cafa1004ced41f315d46444946faae571d6f426e6dbd45d9780eb466df042005298adabf7ce0ef766dfeb94cd449c7ed0046c880339599c4711af073ce649b1e237c40b50a5536283e03bdbb7afad78bd08707715c67fb43295f905b4c479178809d429a8e167a9a8c6dfd8ab20b4edebdc38d6dec879a3202e1b752690d9bb5b0c07c5a227c79cc200e713a99251a4219d62ad5556900cf69bd384b6c8e726c7be267471d0d23af956da165af4af757246c2ebcc302b39e8ef2fccb4971b234fcda22d759ddb20e27269ee7f7fe67898a9de721bfa02ab0becaa046d00ea16cb1afc4e2eab40d0ac17121c565686e5cbd0cbdfbd9d6db5c70278b9c9db5a83176d04f61fbfbc4471d721340ede2746e5c312ded4f26787985af92b64fae3f253dbdde97f6a5e1996fd4d865599e32ff76325d3e9abe93184c02988ee89a4504356a4ef3b9b7a57cbb9637ca90af34a7676b9ef559325c3cca4e29d69fec1887f5440bb101361d744ad292a8547f22b4f22b419a42aa836169b89190f46d9560824cb2ac6e8771de8223216a5e647e132ab9eebcba89569ab339cb1c3d70fe806b31f4f4c600b4103b8d7583ebff16e43dcda551e6530f975122eb8b29"
const verifiedSignatureEncryptedMessageHex = "c2b304000108000605026048f6d600210910a34d7e18c20c31bb1621045fb74b1d03b1e3cb31bc2f8aa34d7e18c20c31bb9a3b0400a32ddac1af259c1b0abab0041327ea04970944401978fb647dd1cf9aba4f164e43f0d8a9389501886474bdd4a6e77f6aea945c07dfbf87743835b44cc2c39a1f9aeecfa83135abc92e18e50396f2e6a06c44e0188b0081effbfb4160d28f118d4ff73dd199a102e47cffd8c7ff2bacd83ae72b5820c021a486766dd587b5da61"
const unverifiedSignatureEncryptedMessageHex = "c2b304000108000605026048f6d600210910a34d7e18c20c31bb1621045fb74b1d03b1e3cb31bc2f8aa34d7e18c20c31bb9a3b0400a32ddac1af259c1b0abab0041327ea04970944401978fb647dd1cf9aba4f164e43f0d8a9389501886474bdd4a6e77f6aea945c07dfbf87743835b44cc2c39a1f9aeecfa83135abc92e18e50396f2e6a06c44e0188b0081effbfb4160d28f118d4ff73dd199a102e47cffd8c7ff2bacd83ae72b5820c021a486766dd587b5da61"
const signedEncryptedMessage2Hex = "85010e03cf6a7abcd43e36731003fb057f5495b79db367e277cdbe4ab90d924ddee0c0381494112ff8c1238fb0184af35d1731573b01bc4c55ecacd2aafbe2003d36310487d1ecc9ac994f3fada7f9f7f5c3a64248ab7782906c82c6ff1303b69a84d9a9529c31ecafbcdb9ba87e05439897d87e8a2a3dec55e14df19bba7f7bd316291c002ae2efd24f83f9e3441203fc081c0c23dc3092a454ca8a082b27f631abf73aca341686982e8fbda7e0e7d863941d68f3de4a755c2964407f4b5e0477b3196b8c93d551dd23c8beef7d0f03fbb1b6066f78907faf4bf1677d8fcec72651124080e0b7feae6b476e72ab207d38d90b958759fdedfc3c6c35717c9dbfc979b3cfbbff0a76d24a5e57056bb88acbd2a901ef64bc6e4db02adc05b6250ff378de81dca18c1910ab257dff1b9771b85bb9bbe0a69f5989e6d1710a35e6dfcceb7d8fb5ccea8db3932b3d9ff3fe0d327597c68b3622aec8e3716c83a6c93f497543b459b58ba504ed6bcaa747d37d2ca746fe49ae0a6ce4a8b694234e941b5159ff8bd34b9023da2814076163b86f40eed7c9472f81b551452d5ab87004a373c0172ec87ea6ce42ccfa7dbdad66b745496c4873d8019e8c28d6b3"
const signatureEncryptedMessage2Hex = "c24604001102000605024dfd0166000a091033af447ccd759b09bae600a096ec5e63ecf0a403085e10f75cc3bab327663282009f51fad9df457ed8d2b70d8a73c76e0443eac0f377"
const symmetricallyEncryptedCompressedHex = "c32e040903085a357c1a7b5614ed00cc0d1d92f428162058b3f558a0fb0980d221ebac6c97d5eda4e0fe32f6e706e94dd263012d6ca1ef8c4bbd324098225e603a10c85ebf09cbf7b5aeeb5ce46381a52edc51038b76a8454483be74e6dcd1e50d5689a8ae7eceaeefed98a0023d49b22eb1f65c2aa1ef1783bb5e1995713b0457102ec3c3075fe871267ffa4b686ad5d52000d857"
const dsaTestKeyHex = "9901a2044d6c49de110400cb5ce438cf9250907ac2ba5bf6547931270b89f7c4b53d9d09f4d0213a5ef2ec1f26806d3d259960f872a4a102ef1581ea3f6d6882d15134f21ef6a84de933cc34c47cc9106efe3bd84c6aec12e78523661e29bc1a61f0aab17fa58a627fd5fd33f5149153fbe8cd70edf3d963bc287ef875270ff14b5bfdd1bca4483793923b00a0fe46d76cb6e4cbdc568435cd5480af3266d610d303fe33ae8273f30a96d4d34f42fa28ce1112d425b2e3bf7ea553d526e2db6b9255e9dc7419045ce817214d1a0056dbc8d5289956a4b1b69f20f1105124096e6a438f41f2e2495923b0f34b70642607d45559595c7fe94d7fa85fc41bf7d68c1fd509ebeaa5f315f6059a446b9369c277597e4f474a9591535354c7e7f4fd98a08aa60400b130c24ff20bdfbf683313f5daebf1c9b34b3bdadfc77f2ddd72ee1fb17e56c473664bc21d66467655dd74b9005e3a2bacce446f1920cd7017231ae447b67036c9b431b8179deacd5120262d894c26bc015bffe3d827ba7087ad9b700d2ca1f6d16cc1786581e5dd065f293c31209300f9b0afcc3f7c08dd26d0a22d87580b4db41054657374204b65792033202844534129886204131102002205024d6c49de021b03060b090807030206150802090a0b0416020301021e01021780000a0910338934250ccc03607e0400a0bdb9193e8a6b96fc2dfc108ae848914b504481f100a09c4dc148cb693293a67af24dd40d2b13a9e36794"
const dsaTestKeyPrivateHex = "9501bb044d6c49de110400cb5ce438cf9250907ac2ba5bf6547931270b89f7c4b53d9d09f4d0213a5ef2ec1f26806d3d259960f872a4a102ef1581ea3f6d6882d15134f21ef6a84de933cc34c47cc9106efe3bd84c6aec12e78523661e29bc1a61f0aab17fa58a627fd5fd33f5149153fbe8cd70edf3d963bc287ef875270ff14b5bfdd1bca4483793923b00a0fe46d76cb6e4cbdc568435cd5480af3266d610d303fe33ae8273f30a96d4d34f42fa28ce1112d425b2e3bf7ea553d526e2db6b9255e9dc7419045ce817214d1a0056dbc8d5289956a4b1b69f20f1105124096e6a438f41f2e2495923b0f34b70642607d45559595c7fe94d7fa85fc41bf7d68c1fd509ebeaa5f315f6059a446b9369c277597e4f474a9591535354c7e7f4fd98a08aa60400b130c24ff20bdfbf683313f5daebf1c9b34b3bdadfc77f2ddd72ee1fb17e56c473664bc21d66467655dd74b9005e3a2bacce446f1920cd7017231ae447b67036c9b431b8179deacd5120262d894c26bc015bffe3d827ba7087ad9b700d2ca1f6d16cc1786581e5dd065f293c31209300f9b0afcc3f7c08dd26d0a22d87580b4d00009f592e0619d823953577d4503061706843317e4fee083db41054657374204b65792033202844534129886204131102002205024d6c49de021b03060b090807030206150802090a0b0416020301021e01021780000a0910338934250ccc03607e0400a0bdb9193e8a6b96fc2dfc108ae848914b504481f100a09c4dc148cb693293a67af24dd40d2b13a9e36794"
const p256TestKeyHex = "98520456e5b83813082a8648ce3d030107020304a2072cd6d21321266c758cc5b83fab0510f751cb8d91897cddb7047d8d6f185546e2107111b0a95cb8ef063c33245502af7a65f004d5919d93ee74eb71a66253b424502d3235362054657374204b6579203c696e76616c6964406578616d706c652e636f6d3e8879041313080021050256e5b838021b03050b09080702061508090a0b020416020301021e01021780000a0910d44a2c495918513e54e50100dfa64f97d9b47766fc1943c6314ba3f2b2a103d71ad286dc5b1efb96a345b0c80100dbc8150b54241f559da6ef4baacea6d31902b4f4b1bdc09b34bf0502334b7754b8560456e5b83812082a8648ce3d030107020304bfe3cea9cee13486f8d518aa487fecab451f25467d2bf08e58f63e5fa525d5482133e6a79299c274b068ef0be448152ad65cf11cf764348588ca4f6a0bcf22b6030108078861041813080009050256e5b838021b0c000a0910d44a2c495918513e4a4800ff49d589fa64024ad30be363a032e3a0e0e6f5db56ba4c73db850518bf0121b8f20100fd78e065f4c70ea5be9df319ea67e493b936fc78da834a71828043d3154af56e"
const p256TestKeyPrivateHex = "94a50456e5b83813082a8648ce3d030107020304a2072cd6d21321266c758cc5b83fab0510f751cb8d91897cddb7047d8d6f185546e2107111b0a95cb8ef063c33245502af7a65f004d5919d93ee74eb71a66253fe070302f0c2bfb0b6c30f87ee1599472b8636477eab23ced13b271886a4b50ed34c9d8436af5af5b8f88921f0efba6ef8c37c459bbb88bc1c6a13bbd25c4ce9b1e97679569ee77645d469bf4b43de637f5561b424502d3235362054657374204b6579203c696e76616c6964406578616d706c652e636f6d3e8879041313080021050256e5b838021b03050b09080702061508090a0b020416020301021e01021780000a0910d44a2c495918513e54e50100dfa64f97d9b47766fc1943c6314ba3f2b2a103d71ad286dc5b1efb96a345b0c80100dbc8150b54241f559da6ef4baacea6d31902b4f4b1bdc09b34bf0502334b77549ca90456e5b83812082a8648ce3d030107020304bfe3cea9cee13486f8d518aa487fecab451f25467d2bf08e58f63e5fa525d5482133e6a79299c274b068ef0be448152ad65cf11cf764348588ca4f6a0bcf22b603010807fe0703027510012471a603cfee2968dce19f732721ddf03e966fd133b4e3c7a685b788705cbc46fb026dc94724b830c9edbaecd2fb2c662f23169516cacd1fe423f0475c364ecc10abcabcfd4bbbda1a36a1bd8861041813080009050256e5b838021b0c000a0910d44a2c495918513e4a4800ff49d589fa64024ad30be363a032e3a0e0e6f5db56ba4c73db850518bf0121b8f20100fd78e065f4c70ea5be9df319ea67e493b936fc78da834a71828043d3154af56e"
const armoredPrivateKeyBlock = `-----BEGIN PGP PRIVATE KEY BLOCK-----
Version: GnuPG v1.4.10 (GNU/Linux)
lQHYBE2rFNoBBADFwqWQIW/DSqcB4yCQqnAFTJ27qS5AnB46ccAdw3u4Greeu3Bp
idpoHdjULy7zSKlwR1EA873dO/k/e11Ml3dlAFUinWeejWaK2ugFP6JjiieSsrKn
vWNicdCS4HTWn0X4sjl0ZiAygw6GNhqEQ3cpLeL0g8E9hnYzJKQ0LWJa0QARAQAB
AAP/TB81EIo2VYNmTq0pK1ZXwUpxCrvAAIG3hwKjEzHcbQznsjNvPUihZ+NZQ6+X
0HCfPAdPkGDCLCb6NavcSW+iNnLTrdDnSI6+3BbIONqWWdRDYJhqZCkqmG6zqSfL
IdkJgCw94taUg5BWP/AAeQrhzjChvpMQTVKQL5mnuZbUCeMCAN5qrYMP2S9iKdnk
VANIFj7656ARKt/nf4CBzxcpHTyB8+d2CtPDKCmlJP6vL8t58Jmih+kHJMvC0dzn
gr5f5+sCAOOe5gt9e0am7AvQWhdbHVfJU0TQJx+m2OiCJAqGTB1nvtBLHdJnfdC9
TnXXQ6ZXibqLyBies/xeY2sCKL5qtTMCAKnX9+9d/5yQxRyrQUHt1NYhaXZnJbHx
q4ytu0eWz+5i68IYUSK69jJ1NWPM0T6SkqpB3KCAIv68VFm9PxqG1KmhSrQIVGVz
dCBLZXmIuAQTAQIAIgUCTasU2gIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AA
CgkQO9o98PRieSoLhgQAkLEZex02Qt7vGhZzMwuN0R22w3VwyYyjBx+fM3JFETy1
ut4xcLJoJfIaF5ZS38UplgakHG0FQ+b49i8dMij0aZmDqGxrew1m4kBfjXw9B/v+
eIqpODryb6cOSwyQFH0lQkXC040pjq9YqDsO5w0WYNXYKDnzRV0p4H1pweo2VDid
AdgETasU2gEEAN46UPeWRqKHvA99arOxee38fBt2CI08iiWyI8T3J6ivtFGixSqV
bRcPxYO/qLpVe5l84Nb3X71GfVXlc9hyv7CD6tcowL59hg1E/DC5ydI8K8iEpUmK
/UnHdIY5h8/kqgGxkY/T/hgp5fRQgW1ZoZxLajVlMRZ8W4tFtT0DeA+JABEBAAEA
A/0bE1jaaZKj6ndqcw86jd+QtD1SF+Cf21CWRNeLKnUds4FRRvclzTyUMuWPkUeX
TaNNsUOFqBsf6QQ2oHUBBK4VCHffHCW4ZEX2cd6umz7mpHW6XzN4DECEzOVksXtc
lUC1j4UB91DC/RNQqwX1IV2QLSwssVotPMPqhOi0ZLNY7wIA3n7DWKInxYZZ4K+6
rQ+POsz6brEoRHwr8x6XlHenq1Oki855pSa1yXIARoTrSJkBtn5oI+f8AzrnN0BN
oyeQAwIA/7E++3HDi5aweWrViiul9cd3rcsS0dEnksPhvS0ozCJiHsq/6GFmy7J8
QSHZPteedBnZyNp5jR+H7cIfVN3KgwH/Skq4PsuPhDq5TKK6i8Pc1WW8MA6DXTdU
nLkX7RGmMwjC0DBf7KWAlPjFaONAX3a8ndnz//fy1q7u2l9AZwrj1qa1iJ8EGAEC
AAkFAk2rFNoCGwwACgkQO9o98PRieSo2/QP/WTzr4ioINVsvN1akKuekmEMI3LAp
BfHwatufxxP1U+3Si/6YIk7kuPB9Hs+pRqCXzbvPRrI8NHZBmc8qIGthishdCYad
AHcVnXjtxrULkQFGbGvhKURLvS9WnzD/m1K2zzwxzkPTzT9/Yf06O6Mal5AdugPL
VrM0m72/jnpKo04=
=zNCn
-----END PGP PRIVATE KEY BLOCK-----`
const e2ePublicKey = `-----BEGIN PGP PUBLIC KEY BLOCK-----
Charset: UTF-8
xv8AAABSBAAAAAATCCqGSM49AwEHAgME1LRoXSpOxtHXDUdmuvzchyg6005qIBJ4
sfaSxX7QgH9RV2ONUhC+WiayCNADq+UMzuR/vunSr4aQffXvuGnR383/AAAAFDxk
Z2lsQHlhaG9vLWluYy5jb20+wv8AAACGBBATCAA4/wAAAAWCVGvAG/8AAAACiwn/
AAAACZC2VkQCOjdvYf8AAAAFlQgJCgv/AAAAA5YBAv8AAAACngEAAE1BAP0X8veD
24IjmI5/C6ZAfVNXxgZZFhTAACFX75jUA3oD6AEAzoSwKf1aqH6oq62qhCN/pekX
+WAsVMBhNwzLpqtCRjLO/wAAAFYEAAAAABIIKoZIzj0DAQcCAwT50ain7vXiIRv8
B1DO3x3cE/aattZ5sHNixJzRCXi2vQIA5QmOxZ6b5jjUekNbdHG3SZi1a2Ak5mfX
fRxC/5VGAwEIB8L/AAAAZQQYEwgAGP8AAAAFglRrwBz/AAAACZC2VkQCOjdvYQAA
FJAA9isX3xtGyMLYwp2F3nXm7QEdY5bq5VUcD/RJlj792VwA/1wH0pCzVLl4Q9F9
ex7En5r7rHR5xwX82Msc+Rq9dSyO
=7MrZ
-----END PGP PUBLIC KEY BLOCK-----`
const dsaKeyWithSHA512 = `9901a2044f04b07f110400db244efecc7316553ee08d179972aab87bb1214de7692593fcf5b6feb1c80fba268722dd464748539b85b81d574cd2d7ad0ca2444de4d849b8756bad7768c486c83a824f9bba4af773d11742bdfb4ac3b89ef8cc9452d4aad31a37e4b630d33927bff68e879284a1672659b8b298222fc68f370f3e24dccacc4a862442b9438b00a0ea444a24088dc23e26df7daf8f43cba3bffc4fe703fe3d6cd7fdca199d54ed8ae501c30e3ec7871ea9cdd4cf63cfe6fc82281d70a5b8bb493f922cd99fba5f088935596af087c8d818d5ec4d0b9afa7f070b3d7c1dd32a84fca08d8280b4890c8da1dde334de8e3cad8450eed2a4a4fcc2db7b8e5528b869a74a7f0189e11ef097ef1253582348de072bb07a9fa8ab838e993cef0ee203ff49298723e2d1f549b00559f886cd417a41692ce58d0ac1307dc71d85a8af21b0cf6eaa14baf2922d3a70389bedf17cc514ba0febbd107675a372fe84b90162a9e88b14d4b1c6be855b96b33fb198c46f058568817780435b6936167ebb3724b680f32bf27382ada2e37a879b3d9de2abe0c3f399350afd1ad438883f4791e2e3b4184453412068617368207472756e636174696f6e207465737488620413110a002205024f04b07f021b03060b090807030206150802090a0b0416020301021e01021780000a0910ef20e0cefca131581318009e2bf3bf047a44d75a9bacd00161ee04d435522397009a03a60d51bd8a568c6c021c8d7cf1be8d990d6417b0020003`
const unknownHashFunctionHex = `8a00000040040001990006050253863c24000a09103b4fe6acc0b21f32ffff01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101`
const rsaSignatureBadMPIlength = `8a00000040040001030006050253863c24000a09103b4fe6acc0b21f32ffff0101010101010101010101010101010101010101010101010101010101010101010101010101`
const missingHashFunctionHex = `8a00000040040001030006050253863c24000a09103b4fe6acc0b21f32ffff0101010101010101010101010101010101010101010101010101010101010101010101`
const campbellQuine = `a0b001000300fcffa0b001000d00f2ff000300fcffa0b001000d00f2ff8270a01c00000500faff8270a01c00000500faff000500faff001400ebff8270a01c00000500faff000500faff001400ebff428821c400001400ebff428821c400001400ebff428821c400001400ebff428821c400001400ebff428821c400000000ffff000000ffff000b00f4ff428821c400000000ffff000000ffff000b00f4ff0233214c40000100feff000233214c40000100feff0000`
const keyV4forVerifyingSignedMessageV3 = `-----BEGIN PGP PUBLIC KEY BLOCK-----
Comment: GPGTools - https://gpgtools.org
mI0EVfxoFQEEAMBIqmbDfYygcvP6Phr1wr1XI41IF7Qixqybs/foBF8qqblD9gIY
BKpXjnBOtbkcVOJ0nljd3/sQIfH4E0vQwK5/4YRQSI59eKOqd6Fx+fWQOLG+uu6z
tewpeCj9LLHvibx/Sc7VWRnrznia6ftrXxJ/wHMezSab3tnGC0YPVdGNABEBAAG0
JEdvY3J5cHRvIFRlc3QgS2V5IDx0aGVtYXhAZ21haWwuY29tPoi5BBMBCgAjBQJV
/GgVAhsDBwsJCAcDAgEGFQgCCQoLBBYCAwECHgECF4AACgkQeXnQmhdGW9PFVAP+
K7TU0qX5ArvIONIxh/WAweyOk884c5cE8f+3NOPOOCRGyVy0FId5A7MmD5GOQh4H
JseOZVEVCqlmngEvtHZb3U1VYtVGE5WZ+6rQhGsMcWP5qaT4soYwMBlSYxgYwQcx
YhN9qOr292f9j2Y//TTIJmZT4Oa+lMxhWdqTfX+qMgG4jQRV/GgVAQQArhFSiij1
b+hT3dnapbEU+23Z1yTu1DfF6zsxQ4XQWEV3eR8v+8mEDDNcz8oyyF56k6UQ3rXi
UMTIwRDg4V6SbZmaFbZYCOwp/EmXJ3rfhm7z7yzXj2OFN22luuqbyVhuL7LRdB0M
pxgmjXb4tTvfgKd26x34S+QqUJ7W6uprY4sAEQEAAYifBBgBCgAJBQJV/GgVAhsM
AAoJEHl50JoXRlvT7y8D/02ckx4OMkKBZo7viyrBw0MLG92i+DC2bs35PooHR6zz
786mitjOp5z2QWNLBvxC70S0qVfCIz8jKupO1J6rq6Z8CcbLF3qjm6h1omUBf8Nd
EfXKD2/2HV6zMKVknnKzIEzauh+eCKS2CeJUSSSryap/QLVAjRnckaES/OsEWhNB
=RZia
-----END PGP PUBLIC KEY BLOCK-----
`
const signedMessageV3 = `-----BEGIN PGP MESSAGE-----
Comment: GPGTools - https://gpgtools.org
owGbwMvMwMVYWXlhlrhb9GXG03JJDKF/MtxDMjKLFYAoUaEktbhEITe1uDgxPVWP
q5NhKjMrWAVcC9evD8z/bF/uWNjqtk/X3y5/38XGRQHm/57rrDRYuGnTw597Xqka
uM3137/hH3Os+Jf2dc0fXOITKwJvXJvecPVs0ta+Vg7ZO1MLn8w58Xx+6L58mbka
DGHyU9yTueZE8D+QF/Tz28Y78dqtF56R1VPn9Xw4uJqrWYdd7b3vIZ1V6R4Nh05d
iT57d/OhWwA=
=hG7R
-----END PGP MESSAGE-----
`
// https://mailarchive.ietf.org/arch/msg/openpgp/9SheW_LENE0Kxf7haNllovPyAdY/
const v5PrivKey = `-----BEGIN PGP PRIVATE KEY BLOCK-----
lGEFXJH05BYAAAAtCSsGAQQB2kcPAQEHQFhZlVcVVtwf+21xNQPX+ecMJJBL0MPd
fj75iux+my8QAAAAAAAiAQCHZ1SnSUmWqxEsoI6facIVZQu6mph3cBFzzTvcm5lA
Ng5ctBhlbW1hLmdvbGRtYW5AZXhhbXBsZS5uZXSIlgUTFggASCIhBRk0e8mHJGQC
X5nfPsLgAA7ZiEiS4fez6kyUAJFZVptUBQJckfTkAhsDBQsJCAcCAyICAQYVCgkI
CwIEFgIDAQIeBwIXgAAA9cAA/jiR3yMsZMeEQ40u6uzEoXa6UXeV/S3wwJAXRJy9
M8s0AP9vuL/7AyTfFXwwzSjDnYmzS0qAhbLDQ643N+MXGBJ2BZxmBVyR9OQSAAAA
MgorBgEEAZdVAQUBAQdA+nysrzml2UCweAqtpDuncSPlvrcBWKU0yfU0YvYWWAoD
AQgHAAAAAAAiAP9OdAPppjU1WwpqjIItkxr+VPQRT8Zm/Riw7U3F6v3OiBFHiHoF
GBYIACwiIQUZNHvJhyRkAl+Z3z7C4AAO2YhIkuH3s+pMlACRWVabVAUCXJH05AIb
DAAAOSQBAP4BOOIR/sGLNMOfeb5fPs/02QMieoiSjIBnijhob2U5AQC+RtOHCHx7
TcIYl5/Uyoi+FOvPLcNw4hOv2nwUzSSVAw==
=IiS2
-----END PGP PRIVATE KEY BLOCK-----`
// Generated with the above private key
const v5PrivKeyMsg = `-----BEGIN PGP MESSAGE-----
Version: OpenPGP.js v4.10.7
Comment: https://openpgpjs.org
xA0DAQoWGTR7yYckZAIByxF1B21zZy50eHRfbIGSdGVzdMJ3BQEWCgAGBQJf
bIGSACMiIQUZNHvJhyRkAl+Z3z7C4AAO2YhIkuH3s+pMlACRWVabVDQvAP9G
y29VPonFXqi2zKkpZrvyvZxg+n5e8Nt9wNbuxeCd3QD/TtO2s+JvjrE4Siwv
UQdl5MlBka1QSNbMq2Bz7XwNPg4=
=6lbM
-----END PGP MESSAGE-----`

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)
if err != nil { var w io.WriteCloser
return if config.AEAD() != nil {
w, err = packet.SerializeAEADEncrypted(ciphertext, key, config.Cipher(), config.AEAD().Mode(), config)
if err != nil {
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 {
return
}
} else {
payload, err = packet.SerializeSymmetricallyEncrypted(dataWriter, cipher, symKey, config)
if err != nil {
return
}
}
payload, err = handleCompression(payload, candidateCompression, config)
if err != nil { if err != nil {
return 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)
return s.literalData.Write(data) switch s.sigType {
case packet.SigTypeBinary:
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

View File

@ -1,12 +0,0 @@
sudo: false
language: go
go:
- "1.13.x"
script:
- go test -v ./...
- curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s v1.26.0
- ./bin/golangci-lint run
- git clean -fdx .
after_success:
curl -sL https://git.io/goreleaser | bash && goreleaser

View File

@ -1,5 +1,7 @@
.PHONY: chromad upload all .PHONY: chromad upload all
VERSION ?= $(shell git describe --tags --dirty --always)
all: README.md tokentype_string.go all: README.md tokentype_string.go
README.md: lexers/*/*.go README.md: lexers/*/*.go
@ -9,10 +11,8 @@ tokentype_string.go: types.go
go generate go generate
chromad: chromad:
(cd ./cmd/chromad && go get github.com/GeertJohan/go.rice/rice@master && go install github.com/GeertJohan/go.rice/rice)
rm -f chromad rm -f chromad
(export CGOENABLED=0 GOOS=linux ; cd ./cmd/chromad && go build -o ../../chromad .) (export CGOENABLED=0 GOOS=linux ; cd ./cmd/chromad && go build -ldflags="-X 'main.version=$(VERSION)'" -o ../../chromad .)
rice append -i ./cmd/chromad --exec=./chromad
upload: chromad upload: chromad
scp chromad root@swapoff.org: && \ scp chromad root@swapoff.org: && \

View File

@ -1,4 +1,4 @@
# Chroma — A general purpose syntax highlighter in pure Go [![Golang Documentation](https://godoc.org/github.com/alecthomas/chroma?status.svg)](https://godoc.org/github.com/alecthomas/chroma) [![Build Status](https://travis-ci.org/alecthomas/chroma.svg)](https://travis-ci.org/alecthomas/chroma) [![Gitter chat](https://badges.gitter.im/alecthomas.svg)](https://gitter.im/alecthomas/Lobby) # Chroma — A general purpose syntax highlighter in pure Go [![Golang Documentation](https://godoc.org/github.com/alecthomas/chroma?status.svg)](https://godoc.org/github.com/alecthomas/chroma) [![CircleCI](https://img.shields.io/circleci/project/github/alecthomas/chroma.svg)](https://circleci.com/gh/alecthomas/chroma) [![Go Report Card](https://goreportcard.com/badge/github.com/alecthomas/chroma)](https://goreportcard.com/report/github.com/alecthomas/chroma) [![Slack chat](https://img.shields.io/static/v1?logo=slack&style=flat&label=slack&color=green&message=gophers)](https://invite.slack.golangbridge.org/)
> **NOTE:** As Chroma has just been released, its API is still in flux. That said, the high-level interface should not change significantly. > **NOTE:** As Chroma has just been released, its API is still in flux. That said, the high-level interface should not change significantly.
@ -36,29 +36,30 @@ translators for Pygments lexers and styles.
Prefix | Language Prefix | Language
:----: | -------- :----: | --------
A | ABAP, ABNF, ActionScript, ActionScript 3, Ada, Angular2, ANTLR, ApacheConf, APL, AppleScript, Arduino, Awk A | ABAP, ABNF, ActionScript, ActionScript 3, Ada, Angular2, ANTLR, ApacheConf, APL, AppleScript, Arduino, Awk
B | Ballerina, Base Makefile, Bash, Batchfile, BlitzBasic, BNF, Brainfuck B | Ballerina, Base Makefile, Bash, Batchfile, BibTeX, BlitzBasic, BNF, Brainfuck
C | C, C#, C++, Cap'n Proto, Cassandra CQL, Ceylon, CFEngine3, cfstatement, ChaiScript, Cheetah, Clojure, CMake, COBOL, CoffeeScript, Common Lisp, Coq, Crystal, CSS, Cython C | C, C#, C++, Caddyfile, Caddyfile Directives, Cap'n Proto, Cassandra CQL, Ceylon, CFEngine3, cfstatement, ChaiScript, Cheetah, Clojure, CMake, COBOL, CoffeeScript, Common Lisp, Coq, Crystal, CSS, Cython
D | D, Dart, Diff, Django/Jinja, Docker, DTD D | D, Dart, Diff, Django/Jinja, Docker, DTD, Dylan
E | EBNF, Elixir, Elm, EmacsLisp, Erlang E | EBNF, Elixir, Elm, EmacsLisp, Erlang
F | Factor, Fish, Forth, Fortran, FSharp F | Factor, Fish, Forth, Fortran, FSharp
G | GAS, GDScript, Genshi, Genshi HTML, Genshi Text, GLSL, Gnuplot, Go, Go HTML Template, Go Text Template, GraphQL, Groovy G | GAS, GDScript, Genshi, Genshi HTML, Genshi Text, Gherkin, GLSL, Gnuplot, Go, Go HTML Template, Go Text Template, GraphQL, Groff, Groovy
H | Handlebars, Haskell, Haxe, HCL, Hexdump, HTML, HTTP, Hy H | Handlebars, Haskell, Haxe, HCL, Hexdump, HLB, HTML, HTTP, Hy
I | Idris, INI, Io I | Idris, Igor, INI, Io
J | J, Java, JavaScript, JSON, Julia, Jungle J | J, Java, JavaScript, JSON, Julia, Jungle
K | Kotlin K | Kotlin
L | Lighttpd configuration file, LLVM, Lua L | Lighttpd configuration file, LLVM, Lua
M | Mako, markdown, Mason, Mathematica, Matlab, MiniZinc, MLIR, Modula-2, MonkeyC, MorrowindScript, Myghty, MySQL M | Mako, markdown, Mason, Mathematica, Matlab, MiniZinc, MLIR, Modula-2, MonkeyC, MorrowindScript, Myghty, MySQL
N | NASM, Newspeak, Nginx configuration file, Nim, Nix N | NASM, Newspeak, Nginx configuration file, Nim, Nix
O | Objective-C, OCaml, Octave, OpenSCAD, Org Mode O | Objective-C, OCaml, Octave, OpenSCAD, Org Mode
P | PacmanConf, Perl, PHP, Pig, PkgConfig, PL/pgSQL, plaintext, PostgreSQL SQL dialect, PostScript, POVRay, PowerShell, Prolog, Protocol Buffer, Puppet, Python, Python 3 P | PacmanConf, Perl, PHP, PHTML, Pig, PkgConfig, PL/pgSQL, plaintext, Pony, PostgreSQL SQL dialect, PostScript, POVRay, PowerShell, Prolog, PromQL, Protocol Buffer, Puppet, Python 2, Python
Q | QBasic Q | QBasic
R | R, Racket, Ragel, react, reg, reStructuredText, Rexx, Ruby, Rust R | R, Racket, Ragel, Raku, react, ReasonML, reg, reStructuredText, Rexx, Ruby, Rust
S | Sass, Scala, Scheme, Scilab, SCSS, Smalltalk, Smarty, SML, Snobol, Solidity, SPARQL, SQL, SquidConf, Swift, SYSTEMD, systemverilog S | SAS, Sass, Scala, Scheme, Scilab, SCSS, Smalltalk, Smarty, Snobol, Solidity, SPARQL, SQL, SquidConf, Standard ML, Stylus, Svelte, Swift, SYSTEMD, systemverilog
T | TableGen, TASM, Tcl, Tcsh, Termcap, Terminfo, Terraform, TeX, Thrift, TOML, TradingView, Transact-SQL, Turing, Turtle, Twig, TypeScript, TypoScript, TypoScriptCssData, TypoScriptHtmlData T | TableGen, TASM, Tcl, Tcsh, Termcap, Terminfo, Terraform, TeX, Thrift, TOML, TradingView, Transact-SQL, Turing, Turtle, Twig, TypeScript, TypoScript, TypoScriptCssData, TypoScriptHtmlData
V | VB.net, verilog, VHDL, VimL, vue V | VB.net, verilog, VHDL, VimL, vue
W | WDTE W | WDTE
X | XML, Xorg X | XML, Xorg
Y | YAML Y | YAML, YANG
Z | Zig
_I will attempt to keep this section up to date, but an authoritative list can be _I will attempt to keep this section up to date, but an authoritative list can be
@ -183,7 +184,7 @@ following constructor options:
- `ClassPrefix(prefix)` - prefix each generated CSS class. - `ClassPrefix(prefix)` - prefix each generated CSS class.
- `TabWidth(width)` - Set the rendered tab width, in characters. - `TabWidth(width)` - Set the rendered tab width, in characters.
- `WithLineNumbers()` - Render line numbers (style with `LineNumbers`). - `WithLineNumbers()` - Render line numbers (style with `LineNumbers`).
- `LinkableLineNumbers()` - Make the line numbers linkable. - `LinkableLineNumbers()` - Make the line numbers linkable and be a link to themselves.
- `HighlightLines(ranges)` - Highlight lines in these ranges (style with `LineHighlight`). - `HighlightLines(ranges)` - Highlight lines in these ranges (style with `LineHighlight`).
- `LineNumbersInTable()` - Use a table for formatting line numbers and code, rather than spans. - `LineNumbersInTable()` - Use a table for formatting line numbers and code, rather than spans.
@ -209,13 +210,13 @@ using the included Python 3 script `pygments2chroma.py`. I use something like
the following: the following:
```sh ```sh
python3 ~/Projects/chroma/_tools/pygments2chroma.py \ python3 _tools/pygments2chroma.py \
pygments.lexers.jvm.KotlinLexer \ pygments.lexers.jvm.KotlinLexer \
> ~/Projects/chroma/lexers/kotlin.go \ > lexers/k/kotlin.go \
&& gofmt -s -w ~/Projects/chroma/lexers/*.go && gofmt -s -w lexers/k/kotlin.go
``` ```
See notes in [pygments-lexers.go](https://github.com/alecthomas/chroma/blob/master/pygments-lexers.txt) See notes in [pygments-lexers.txt](https://github.com/alecthomas/chroma/blob/master/pygments-lexers.txt)
for a list of lexers, and notes on some of the issues importing them. for a list of lexers, and notes on some of the issues importing them.
<a id="markdown-formatters" name="formatters"></a> <a id="markdown-formatters" name="formatters"></a>
@ -254,12 +255,30 @@ A command-line interface to Chroma is included. It can be installed with:
go get -u github.com/alecthomas/chroma/cmd/chroma go get -u github.com/alecthomas/chroma/cmd/chroma
``` ```
The CLI can be used as a preprocessor to colorise output of `less(1)`,
see documentation for the `LESSOPEN` environment variable.
The `--fail` flag can be used to suppress output and return with exit status
1 to facilitate falling back to some other preprocessor in case chroma
does not resolve a specific lexer to use for the given file. For example:
```shell
export LESSOPEN='| p() { chroma --fail "$1" || cat "$1"; }; p "%s"'
```
Replace `cat` with your favourite fallback preprocessor.
When invoked as `.lessfilter`, the `--fail` flag is automatically turned
on under the hood for easy integration with [lesspipe shipping with
Debian and derivatives](https://manpages.debian.org/lesspipe#USER_DEFINED_FILTERS);
for that setup the `chroma` executable can be just symlinked to `~/.lessfilter`.
<a id="markdown-whats-missing-compared-to-pygments" name="whats-missing-compared-to-pygments"></a> <a id="markdown-whats-missing-compared-to-pygments" name="whats-missing-compared-to-pygments"></a>
## What's missing compared to Pygments? ## What's missing compared to Pygments?
- Quite a few lexers, for various reasons (pull-requests welcome): - Quite a few lexers, for various reasons (pull-requests welcome):
- Pygments lexers for complex languages often include custom code to - Pygments lexers for complex languages often include custom code to
handle certain aspects, such as Perl6's ability to nest code inside handle certain aspects, such as Raku's ability to nest code inside
regular expressions. These require time and effort to convert. regular expressions. These require time and effort to convert.
- I mostly only converted languages I had heard of, to reduce the porting cost. - I mostly only converted languages I had heard of, to reduce the porting cost.
- Some more esoteric features of Pygments are omitted for simplicity. - Some more esoteric features of Pygments are omitted for simplicity.

View File

@ -131,7 +131,7 @@ var (
} }
defaultPreWrapper = preWrapper{ defaultPreWrapper = preWrapper{
start: func(code bool, styleAttr string) string { start: func(code bool, styleAttr string) string {
return fmt.Sprintf("<pre%s>", styleAttr) return fmt.Sprintf(`<pre tabindex="0"%s>`, styleAttr)
}, },
end: func(code bool) string { end: func(code bool) string {
return "</pre>" return "</pre>"
@ -211,7 +211,7 @@ func (f *Formatter) writeHTML(w io.Writer, style *chroma.Style, tokens []chroma.
fmt.Fprintf(w, "<span%s>", f.styleAttr(css, chroma.LineHighlight)) fmt.Fprintf(w, "<span%s>", f.styleAttr(css, chroma.LineHighlight))
} }
fmt.Fprintf(w, "<span%s%s>%*d\n</span>", f.styleAttr(css, chroma.LineNumbersTable), f.lineIDAttribute(line), lineDigits, line) fmt.Fprintf(w, "<span%s%s>%s\n</span>", f.styleAttr(css, chroma.LineNumbersTable), f.lineIDAttribute(line), f.lineTitleWithLinkIfNeeded(lineDigits, line))
if highlight { if highlight {
fmt.Fprintf(w, "</span>") fmt.Fprintf(w, "</span>")
@ -237,7 +237,7 @@ func (f *Formatter) writeHTML(w io.Writer, style *chroma.Style, tokens []chroma.
} }
if f.lineNumbers && !wrapInTable { if f.lineNumbers && !wrapInTable {
fmt.Fprintf(w, "<span%s%s>%*d</span>", f.styleAttr(css, chroma.LineNumbers), f.lineIDAttribute(line), lineDigits, line) fmt.Fprintf(w, "<span%s%s>%s</span>", f.styleAttr(css, chroma.LineNumbers), f.lineIDAttribute(line), f.lineTitleWithLinkIfNeeded(lineDigits, line))
} }
for _, token := range tokens { for _, token := range tokens {
@ -272,7 +272,19 @@ func (f *Formatter) lineIDAttribute(line int) string {
if !f.linkableLineNumbers { if !f.linkableLineNumbers {
return "" return ""
} }
return fmt.Sprintf(" id=\"%s%d\"", f.lineNumbersIDPrefix, line) return fmt.Sprintf(" id=\"%s\"", f.lineID(line))
}
func (f *Formatter) lineTitleWithLinkIfNeeded(lineDigits, line int) string {
title := fmt.Sprintf("%*d", lineDigits, line)
if !f.linkableLineNumbers {
return title
}
return fmt.Sprintf("<a style=\"outline: none; text-decoration:none; color:inherit\" href=\"#%s\">%s</a>", f.lineID(line), title)
}
func (f *Formatter) lineID(line int) string {
return fmt.Sprintf("%s%d", f.lineNumbersIDPrefix, line)
} }
func (f *Formatter) shouldHighlight(highlightIndex, line int) (bool, bool) { func (f *Formatter) shouldHighlight(highlightIndex, line int) (bool, bool) {

View File

@ -120,7 +120,7 @@ func maxLineWidth(lines [][]chroma.Token) int {
for _, tokens := range lines { for _, tokens := range lines {
length := 0 length := 0
for _, token := range tokens { for _, token := range tokens {
length += len(strings.Replace(token.String(), ` `, " ", -1)) length += len(strings.ReplaceAll(token.String(), ` `, " "))
} }
if length > maxWidth { if length > maxWidth {
maxWidth = length maxWidth = length
@ -136,7 +136,7 @@ func (f *Formatter) writeTokenBackgrounds(w io.Writer, lines [][]chroma.Token, s
for index, tokens := range lines { for index, tokens := range lines {
lineLength := 0 lineLength := 0
for _, token := range tokens { for _, token := range tokens {
length := len(strings.Replace(token.String(), ` `, " ", -1)) length := len(strings.ReplaceAll(token.String(), ` `, " "))
tokenBackground := style.Get(token.Type).Background tokenBackground := style.Get(token.Type).Background
if tokenBackground.IsSet() && tokenBackground != style.Get(chroma.Background).Background { if tokenBackground.IsSet() && tokenBackground != style.Get(chroma.Background).Background {
fmt.Fprintf(w, "<rect id=\"%s\" x=\"%dch\" y=\"%fem\" width=\"%dch\" height=\"1.2em\" fill=\"%s\" />\n", escapeString(token.String()), lineLength, 1.2*float64(index)+0.25, length, style.Get(token.Type).Background.String()) fmt.Fprintf(w, "<rect id=\"%s\" x=\"%dch\" y=\"%fem\" width=\"%dch\" height=\"1.2em\" fill=\"%s\" />\n", escapeString(token.String()), lineLength, 1.2*float64(index)+0.25, length, style.Get(token.Type).Background.String())

View File

@ -17,6 +17,20 @@ var c = chroma.MustParseColour
var ttyTables = map[int]*ttyTable{ var ttyTables = map[int]*ttyTable{
8: { 8: {
foreground: map[chroma.Colour]string{
c("#000000"): "\033[30m", c("#7f0000"): "\033[31m", c("#007f00"): "\033[32m", c("#7f7fe0"): "\033[33m",
c("#00007f"): "\033[34m", c("#7f007f"): "\033[35m", c("#007f7f"): "\033[36m", c("#e5e5e5"): "\033[37m",
c("#555555"): "\033[1m\033[30m", c("#ff0000"): "\033[1m\033[31m", c("#00ff00"): "\033[1m\033[32m", c("#ffff00"): "\033[1m\033[33m",
c("#0000ff"): "\033[1m\033[34m", c("#ff00ff"): "\033[1m\033[35m", c("#00ffff"): "\033[1m\033[36m", c("#ffffff"): "\033[1m\033[37m",
},
background: map[chroma.Colour]string{
c("#000000"): "\033[40m", c("#7f0000"): "\033[41m", c("#007f00"): "\033[42m", c("#7f7fe0"): "\033[43m",
c("#00007f"): "\033[44m", c("#7f007f"): "\033[45m", c("#007f7f"): "\033[46m", c("#e5e5e5"): "\033[47m",
c("#555555"): "\033[1m\033[40m", c("#ff0000"): "\033[1m\033[41m", c("#00ff00"): "\033[1m\033[42m", c("#ffff00"): "\033[1m\033[43m",
c("#0000ff"): "\033[1m\033[44m", c("#ff00ff"): "\033[1m\033[45m", c("#00ffff"): "\033[1m\033[46m", c("#ffffff"): "\033[1m\033[47m",
},
},
16: {
foreground: map[chroma.Colour]string{ foreground: map[chroma.Colour]string{
c("#000000"): "\033[30m", c("#7f0000"): "\033[31m", c("#007f00"): "\033[32m", c("#7f7fe0"): "\033[33m", c("#000000"): "\033[30m", c("#7f0000"): "\033[31m", c("#007f00"): "\033[32m", c("#7f7fe0"): "\033[33m",
c("#00007f"): "\033[34m", c("#7f007f"): "\033[35m", c("#007f7f"): "\033[36m", c("#e5e5e5"): "\033[37m", c("#00007f"): "\033[34m", c("#7f007f"): "\033[35m", c("#007f7f"): "\033[36m", c("#e5e5e5"): "\033[37m",
@ -227,15 +241,11 @@ type indexedTTYFormatter struct {
func (c *indexedTTYFormatter) Format(w io.Writer, style *chroma.Style, it chroma.Iterator) (err error) { func (c *indexedTTYFormatter) Format(w io.Writer, style *chroma.Style, it chroma.Iterator) (err error) {
theme := styleToEscapeSequence(c.table, style) theme := styleToEscapeSequence(c.table, style)
for token := it(); token != chroma.EOF; token = it() { for token := it(); token != chroma.EOF; token = it() {
// TODO: Cache token lookups?
clr, ok := theme[token.Type] clr, ok := theme[token.Type]
if !ok { if !ok {
clr, ok = theme[token.Type.SubCategory()] clr, ok = theme[token.Type.SubCategory()]
if !ok { if !ok {
clr = theme[token.Type.Category()] clr = theme[token.Type.Category()]
// if !ok {
// clr = theme[chroma.InheritStyle]
// }
} }
} }
if clr != "" { if clr != "" {
@ -249,10 +259,22 @@ func (c *indexedTTYFormatter) Format(w io.Writer, style *chroma.Style, it chroma
return nil return nil
} }
// TTY is an 8-colour terminal formatter.
//
// The Lab colour space is used to map RGB values to the most appropriate index colour.
var TTY = Register("terminal", &indexedTTYFormatter{ttyTables[8]})
// TTY8 is an 8-colour terminal formatter. // TTY8 is an 8-colour terminal formatter.
// //
// The Lab colour space is used to map RGB values to the most appropriate index colour. // The Lab colour space is used to map RGB values to the most appropriate index colour.
var TTY8 = Register("terminal", &indexedTTYFormatter{ttyTables[8]}) var TTY8 = Register("terminal8", &indexedTTYFormatter{ttyTables[8]})
// TTY16 is a 16-colour terminal formatter.
//
// It uses \033[3xm for normal colours and \033[90Xm for bright colours.
//
// The Lab colour space is used to map RGB values to the most appropriate index colour.
var TTY16 = Register("terminal16", &indexedTTYFormatter{ttyTables[16]})
// TTY256 is a 256-colour terminal formatter. // TTY256 is a 256-colour terminal formatter.
// //

View File

@ -8,7 +8,7 @@ require (
github.com/alecthomas/kong v0.2.4 github.com/alecthomas/kong v0.2.4
github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897 // indirect github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897 // indirect
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964
github.com/dlclark/regexp2 v1.2.0 github.com/dlclark/regexp2 v1.4.0
github.com/mattn/go-colorable v0.1.6 github.com/mattn/go-colorable v0.1.6
github.com/mattn/go-isatty v0.0.12 github.com/mattn/go-isatty v0.0.12
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect

View File

@ -11,13 +11,12 @@ github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9
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.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=
github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE= github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE=
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-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= 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/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
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=
@ -26,7 +25,6 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
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/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.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=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=

View File

@ -4,7 +4,7 @@ import "strings"
// An Iterator across tokens. // An Iterator across tokens.
// //
// nil will be returned at the end of the Token stream. // EOF will be returned at the end of the Token stream.
// //
// If an error occurs within an Iterator, it may propagate this in a panic. Formatters should recover. // If an error occurs within an Iterator, it may propagate this in a panic. Formatters should recover.
type Iterator func() Token type Iterator func() Token

View File

@ -2,6 +2,7 @@ package chroma
import ( import (
"fmt" "fmt"
"strings"
) )
var ( var (
@ -98,9 +99,11 @@ type Lexer interface {
// Lexers is a slice of lexers sortable by name. // Lexers is a slice of lexers sortable by name.
type Lexers []Lexer type Lexers []Lexer
func (l Lexers) Len() int { return len(l) } func (l Lexers) Len() int { return len(l) }
func (l Lexers) Swap(i, j int) { l[i], l[j] = l[j], l[i] } func (l Lexers) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
func (l Lexers) Less(i, j int) bool { return l[i].Config().Name < l[j].Config().Name } func (l Lexers) Less(i, j int) bool {
return strings.ToLower(l[i].Config().Name) < strings.ToLower(l[j].Config().Name)
}
// PrioritisedLexers is a slice of lexers sortable by priority. // PrioritisedLexers is a slice of lexers sortable by priority.
type PrioritisedLexers []Lexer type PrioritisedLexers []Lexer

View File

@ -3,6 +3,9 @@
The tests in this directory feed a known input `testdata/<name>.actual` into the parser for `<name>` and check The tests in this directory feed a known input `testdata/<name>.actual` into the parser for `<name>` and check
that its output matches `<name>.exported`. that its output matches `<name>.exported`.
It is also possible to perform several tests on a same parser `<name>`, by placing know inputs `*.actual` into a
directory `testdata/<name>/`.
## Running the tests ## Running the tests
Run the tests as normal: Run the tests as normal:

View File

@ -6,7 +6,7 @@ import (
) )
// ABAP lexer. // ABAP lexer.
var Abap = internal.Register(MustNewLexer( var Abap = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "ABAP", Name: "ABAP",
Aliases: []string{"abap"}, Aliases: []string{"abap"},
@ -14,7 +14,11 @@ var Abap = internal.Register(MustNewLexer(
MimeTypes: []string{"text/x-abap"}, MimeTypes: []string{"text/x-abap"},
CaseInsensitive: true, CaseInsensitive: true,
}, },
Rules{ abapRules,
))
func abapRules() Rules {
return Rules{
"common": { "common": {
{`\s+`, Text, nil}, {`\s+`, Text, nil},
{`^\*.*$`, CommentSingle, nil}, {`^\*.*$`, CommentSingle, nil},
@ -52,5 +56,5 @@ var Abap = internal.Register(MustNewLexer(
{`[/;:()\[\],.]`, Punctuation, nil}, {`[/;:()\[\],.]`, Punctuation, nil},
{`(!)(\w+)`, ByGroups(Operator, Name), nil}, {`(!)(\w+)`, ByGroups(Operator, Name), nil},
}, },
}, }
)) }

View File

@ -6,14 +6,18 @@ import (
) )
// Abnf lexer. // Abnf lexer.
var Abnf = internal.Register(MustNewLexer( var Abnf = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "ABNF", Name: "ABNF",
Aliases: []string{"abnf"}, Aliases: []string{"abnf"},
Filenames: []string{"*.abnf"}, Filenames: []string{"*.abnf"},
MimeTypes: []string{"text/x-abnf"}, MimeTypes: []string{"text/x-abnf"},
}, },
Rules{ abnfRules,
))
func abnfRules() Rules {
return Rules{
"root": { "root": {
{`;.*$`, CommentSingle, nil}, {`;.*$`, CommentSingle, nil},
{`(%[si])?"[^"]*"`, Literal, nil}, {`(%[si])?"[^"]*"`, Literal, nil},
@ -34,5 +38,5 @@ var Abnf = internal.Register(MustNewLexer(
{`\s+`, Text, nil}, {`\s+`, Text, nil},
{`.`, Text, nil}, {`.`, Text, nil},
}, },
}, }
)) }

View File

@ -6,7 +6,7 @@ import (
) )
// Actionscript lexer. // Actionscript lexer.
var Actionscript = internal.Register(MustNewLexer( var Actionscript = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "ActionScript", Name: "ActionScript",
Aliases: []string{"as", "actionscript"}, Aliases: []string{"as", "actionscript"},
@ -15,7 +15,11 @@ var Actionscript = internal.Register(MustNewLexer(
NotMultiline: true, NotMultiline: true,
DotAll: true, DotAll: true,
}, },
Rules{ actionscriptRules,
))
func actionscriptRules() Rules {
return Rules{
"root": { "root": {
{`\s+`, Text, nil}, {`\s+`, Text, nil},
{`//.*?\n`, CommentSingle, nil}, {`//.*?\n`, CommentSingle, nil},
@ -35,5 +39,5 @@ var Actionscript = internal.Register(MustNewLexer(
{`"(\\\\|\\"|[^"])*"`, LiteralStringDouble, nil}, {`"(\\\\|\\"|[^"])*"`, LiteralStringDouble, nil},
{`'(\\\\|\\'|[^'])*'`, LiteralStringSingle, nil}, {`'(\\\\|\\'|[^'])*'`, LiteralStringSingle, nil},
}, },
}, }
)) }

View File

@ -6,7 +6,7 @@ import (
) )
// Actionscript 3 lexer. // Actionscript 3 lexer.
var Actionscript3 = internal.Register(MustNewLexer( var Actionscript3 = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "ActionScript 3", Name: "ActionScript 3",
Aliases: []string{"as3", "actionscript3"}, Aliases: []string{"as3", "actionscript3"},
@ -14,7 +14,11 @@ var Actionscript3 = internal.Register(MustNewLexer(
MimeTypes: []string{"application/x-actionscript3", "text/x-actionscript3", "text/actionscript3"}, MimeTypes: []string{"application/x-actionscript3", "text/x-actionscript3", "text/actionscript3"},
DotAll: true, DotAll: true,
}, },
Rules{ actionscript3Rules,
))
func actionscript3Rules() Rules {
return Rules{
"root": { "root": {
{`\s+`, Text, nil}, {`\s+`, Text, nil},
{`(function\s+)([$a-zA-Z_]\w*)(\s*)(\()`, ByGroups(KeywordDeclaration, NameFunction, Text, Operator), Push("funcparams")}, {`(function\s+)([$a-zA-Z_]\w*)(\s*)(\()`, ByGroups(KeywordDeclaration, NameFunction, Text, Operator), Push("funcparams")},
@ -52,5 +56,5 @@ var Actionscript3 = internal.Register(MustNewLexer(
{`,`, Operator, Pop(1)}, {`,`, Operator, Pop(1)},
Default(Pop(1)), Default(Pop(1)),
}, },
}, }
)) }

View File

@ -6,7 +6,7 @@ import (
) )
// Ada lexer. // Ada lexer.
var Ada = internal.Register(MustNewLexer( var Ada = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "Ada", Name: "Ada",
Aliases: []string{"ada", "ada95", "ada2005"}, Aliases: []string{"ada", "ada95", "ada2005"},
@ -14,7 +14,11 @@ var Ada = internal.Register(MustNewLexer(
MimeTypes: []string{"text/x-ada"}, MimeTypes: []string{"text/x-ada"},
CaseInsensitive: true, CaseInsensitive: true,
}, },
Rules{ adaRules,
))
func adaRules() Rules {
return Rules{
"root": { "root": {
{`[^\S\n]+`, Text, nil}, {`[^\S\n]+`, Text, nil},
{`--.*?\n`, CommentSingle, nil}, {`--.*?\n`, CommentSingle, nil},
@ -110,5 +114,5 @@ var Ada = internal.Register(MustNewLexer(
{`\)`, Punctuation, Pop(1)}, {`\)`, Punctuation, Pop(1)},
Include("root"), Include("root"),
}, },
}, }
)) }

48
vendor/github.com/alecthomas/chroma/lexers/a/al.go generated vendored Normal file
View File

@ -0,0 +1,48 @@
package a
import (
. "github.com/alecthomas/chroma" // nolint
"github.com/alecthomas/chroma/lexers/internal"
)
// Al lexer.
var Al = internal.Register(MustNewLazyLexer(
&Config{
Name: "AL",
Aliases: []string{"al"},
Filenames: []string{"*.al", "*.dal"},
MimeTypes: []string{"text/x-al"},
DotAll: true,
CaseInsensitive: true,
},
alRules,
))
// https://github.com/microsoft/AL/blob/master/grammar/alsyntax.tmlanguage
func alRules() Rules {
return Rules{
"root": {
{`\s+`, TextWhitespace, nil},
{`(?s)\/\*.*?\\*\*\/`, CommentMultiline, nil},
{`(?s)//.*?\n`, CommentSingle, nil},
{`\"([^\"])*\"`, Text, nil},
{`'([^'])*'`, LiteralString, nil},
{`\b(?i:(ARRAY|ASSERTERROR|BEGIN|BREAK|CASE|DO|DOWNTO|ELSE|END|EVENT|EXIT|FOR|FOREACH|FUNCTION|IF|IMPLEMENTS|IN|INDATASET|INTERFACE|INTERNAL|LOCAL|OF|PROCEDURE|PROGRAM|PROTECTED|REPEAT|RUNONCLIENT|SECURITYFILTERING|SUPPRESSDISPOSE|TEMPORARY|THEN|TO|TRIGGER|UNTIL|VAR|WHILE|WITH|WITHEVENTS))\b`, Keyword, nil},
{`\b(?i:(AND|DIV|MOD|NOT|OR|XOR))\b`, OperatorWord, nil},
{`\b(?i:(AVERAGE|CONST|COUNT|EXIST|FIELD|FILTER|LOOKUP|MAX|MIN|ORDER|SORTING|SUM|TABLEDATA|UPPERLIMIT|WHERE|ASCENDING|DESCENDING))\b`, Keyword, nil},
// Added new objects types of BC 2021 wave 1 (REPORTEXTENSION|Entitlement|PermissionSet|PermissionSetExtension)
{`\b(?i:(CODEUNIT|PAGE|PAGEEXTENSION|PAGECUSTOMIZATION|DOTNET|ENUM|ENUMEXTENSION|VALUE|QUERY|REPORT|TABLE|TABLEEXTENSION|XMLPORT|PROFILE|CONTROLADDIN|REPORTEXTENSION|Entitlement|PermissionSet|PermissionSetExtension))\b`, Keyword, nil},
{`\b(?i:(Action|Array|Automation|BigInteger|BigText|Blob|Boolean|Byte|Char|ClientType|Code|Codeunit|CompletionTriggerErrorLevel|ConnectionType|Database|DataClassification|DataScope|Date|DateFormula|DateTime|Decimal|DefaultLayout|Dialog|Dictionary|DotNet|DotNetAssembly|DotNetTypeDeclaration|Duration|Enum|ErrorInfo|ErrorType|ExecutionContext|ExecutionMode|FieldClass|FieldRef|FieldType|File|FilterPageBuilder|Guid|InStream|Integer|Joker|KeyRef|List|ModuleDependencyInfo|ModuleInfo|None|Notification|NotificationScope|ObjectType|Option|OutStream|Page|PageResult|Query|Record|RecordId|RecordRef|Report|ReportFormat|SecurityFilter|SecurityFiltering|Table|TableConnectionType|TableFilter|TestAction|TestField|TestFilterField|TestPage|TestPermissions|TestRequestPage|Text|TextBuilder|TextConst|TextEncoding|Time|TransactionModel|TransactionType|Variant|Verbosity|Version|XmlPort|HttpContent|HttpHeaders|HttpClient|HttpRequestMessage|HttpResponseMessage|JsonToken|JsonValue|JsonArray|JsonObject|View|Views|XmlAttribute|XmlAttributeCollection|XmlComment|XmlCData|XmlDeclaration|XmlDocument|XmlDocumentType|XmlElement|XmlNamespaceManager|XmlNameTable|XmlNode|XmlNodeList|XmlProcessingInstruction|XmlReadOptions|XmlText|XmlWriteOptions|WebServiceActionContext|WebServiceActionResultCode|SessionSettings))\b`, Keyword, nil},
{`\b([<>]=|<>|<|>)\b?`, Operator, nil},
{`\b(\-|\+|\/|\*)\b`, Operator, nil},
{`\s*(\:=|\+=|-=|\/=|\*=)\s*?`, Operator, nil},
{`\b(?i:(ADDFIRST|ADDLAST|ADDAFTER|ADDBEFORE|ACTION|ACTIONS|AREA|ASSEMBLY|CHARTPART|CUEGROUP|CUSTOMIZES|COLUMN|DATAITEM|DATASET|ELEMENTS|EXTENDS|FIELD|FIELDGROUP|FIELDATTRIBUTE|FIELDELEMENT|FIELDGROUPS|FIELDS|FILTER|FIXED|GRID|GROUP|MOVEAFTER|MOVEBEFORE|KEY|KEYS|LABEL|LABELS|LAYOUT|MODIFY|MOVEFIRST|MOVELAST|MOVEBEFORE|MOVEAFTER|PART|REPEATER|USERCONTROL|REQUESTPAGE|SCHEMA|SEPARATOR|SYSTEMPART|TABLEELEMENT|TEXTATTRIBUTE|TEXTELEMENT|TYPE))\b`, Keyword, nil},
{`\s*[(\.\.)&\|]\s*`, Operator, nil},
{`\b((0(x|X)[0-9a-fA-F]*)|(([0-9]+\.?[0-9]*)|(\.[0-9]+))((e|E)(\+|-)?[0-9]+)?)(L|l|UL|ul|u|U|F|f|ll|LL|ull|ULL)?\b`, LiteralNumber, nil},
{`[;:,]`, Punctuation, nil},
{`#[ \t]*(if|else|elif|endif|define|undef|region|endregion|pragma)\b.*?\n`, CommentPreproc, nil},
{`\w+`, Text, nil},
{`.`, Text, nil},
},
}
}

View File

@ -6,14 +6,18 @@ import (
) )
// Angular2 lexer. // Angular2 lexer.
var Angular2 = internal.Register(MustNewLexer( var Angular2 = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "Angular2", Name: "Angular2",
Aliases: []string{"ng2"}, Aliases: []string{"ng2"},
Filenames: []string{}, Filenames: []string{},
MimeTypes: []string{}, MimeTypes: []string{},
}, },
Rules{ angular2Rules,
))
func angular2Rules() Rules {
return Rules{
"root": { "root": {
{`[^{([*#]+`, Other, nil}, {`[^{([*#]+`, Other, nil},
{`(\{\{)(\s*)`, ByGroups(CommentPreproc, Text), Push("ngExpression")}, {`(\{\{)(\s*)`, ByGroups(CommentPreproc, Text), Push("ngExpression")},
@ -38,5 +42,5 @@ var Angular2 = internal.Register(MustNewLexer(
{`'.*?'`, LiteralString, Pop(1)}, {`'.*?'`, LiteralString, Pop(1)},
{`[^\s>]+`, LiteralString, Pop(1)}, {`[^\s>]+`, LiteralString, Pop(1)},
}, },
}, }
)) }

View File

@ -6,14 +6,18 @@ import (
) )
// ANTLR lexer. // ANTLR lexer.
var ANTLR = internal.Register(MustNewLexer( var ANTLR = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "ANTLR", Name: "ANTLR",
Aliases: []string{"antlr"}, Aliases: []string{"antlr"},
Filenames: []string{}, Filenames: []string{},
MimeTypes: []string{}, MimeTypes: []string{},
}, },
Rules{ antlrRules,
))
func antlrRules() Rules {
return Rules{
"whitespace": { "whitespace": {
{`\s+`, TextWhitespace, nil}, {`\s+`, TextWhitespace, nil},
}, },
@ -97,5 +101,5 @@ var ANTLR = internal.Register(MustNewLexer(
{`(\$[a-zA-Z]+)(\.?)(text|value)?`, ByGroups(NameVariable, Punctuation, NameProperty), nil}, {`(\$[a-zA-Z]+)(\.?)(text|value)?`, ByGroups(NameVariable, Punctuation, NameProperty), nil},
{`(\\\\|\\\]|\\\[|[^\[\]])+`, Other, nil}, {`(\\\\|\\\]|\\\[|[^\[\]])+`, Other, nil},
}, },
}, }
)) }

View File

@ -6,7 +6,7 @@ import (
) )
// Apacheconf lexer. // Apacheconf lexer.
var Apacheconf = internal.Register(MustNewLexer( var Apacheconf = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "ApacheConf", Name: "ApacheConf",
Aliases: []string{"apacheconf", "aconf", "apache"}, Aliases: []string{"apacheconf", "aconf", "apache"},
@ -14,7 +14,11 @@ var Apacheconf = internal.Register(MustNewLexer(
MimeTypes: []string{"text/x-apacheconf"}, MimeTypes: []string{"text/x-apacheconf"},
CaseInsensitive: true, CaseInsensitive: true,
}, },
Rules{ apacheconfRules,
))
func apacheconfRules() Rules {
return Rules{
"root": { "root": {
{`\s+`, Text, nil}, {`\s+`, Text, nil},
{`(#.*?)$`, Comment, nil}, {`(#.*?)$`, Comment, nil},
@ -34,5 +38,5 @@ var Apacheconf = internal.Register(MustNewLexer(
{`"([^"\\]*(?:\\.[^"\\]*)*)"`, LiteralStringDouble, nil}, {`"([^"\\]*(?:\\.[^"\\]*)*)"`, LiteralStringDouble, nil},
{`[^\s"\\]+`, Text, nil}, {`[^\s"\\]+`, Text, nil},
}, },
}, }
)) }

View File

@ -6,14 +6,18 @@ import (
) )
// Apl lexer. // Apl lexer.
var Apl = internal.Register(MustNewLexer( var Apl = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "APL", Name: "APL",
Aliases: []string{"apl"}, Aliases: []string{"apl"},
Filenames: []string{"*.apl"}, Filenames: []string{"*.apl"},
MimeTypes: []string{}, MimeTypes: []string{},
}, },
Rules{ aplRules,
))
func aplRules() Rules {
return Rules{
"root": { "root": {
{`\s+`, Text, nil}, {`\s+`, Text, nil},
{`[⍝#].*$`, CommentSingle, nil}, {`[⍝#].*$`, CommentSingle, nil},
@ -32,5 +36,5 @@ var Apl = internal.Register(MustNewLexer(
{`[⍺⍵⍶⍹∇:]`, NameBuiltinPseudo, nil}, {`[⍺⍵⍶⍹∇:]`, NameBuiltinPseudo, nil},
{`[{}]`, KeywordType, nil}, {`[{}]`, KeywordType, nil},
}, },
}, }
)) }

View File

@ -6,7 +6,7 @@ import (
) )
// Applescript lexer. // Applescript lexer.
var Applescript = internal.Register(MustNewLexer( var Applescript = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "AppleScript", Name: "AppleScript",
Aliases: []string{"applescript"}, Aliases: []string{"applescript"},
@ -14,7 +14,11 @@ var Applescript = internal.Register(MustNewLexer(
MimeTypes: []string{}, MimeTypes: []string{},
DotAll: true, DotAll: true,
}, },
Rules{ applescriptRules,
))
func applescriptRules() Rules {
return Rules{
"root": { "root": {
{`\s+`, Text, nil}, {`\s+`, Text, nil},
{`¬\n`, LiteralStringEscape, nil}, {`¬\n`, LiteralStringEscape, nil},
@ -51,5 +55,5 @@ var Applescript = internal.Register(MustNewLexer(
{`[^*(]+`, CommentMultiline, nil}, {`[^*(]+`, CommentMultiline, nil},
{`[*(]`, CommentMultiline, nil}, {`[*(]`, CommentMultiline, nil},
}, },
}, }
)) }

View File

@ -6,7 +6,7 @@ import (
) )
// Arduino lexer. // Arduino lexer.
var Arduino = internal.Register(MustNewLexer( var Arduino = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "Arduino", Name: "Arduino",
Aliases: []string{"arduino"}, Aliases: []string{"arduino"},
@ -14,7 +14,11 @@ var Arduino = internal.Register(MustNewLexer(
MimeTypes: []string{"text/x-arduino"}, MimeTypes: []string{"text/x-arduino"},
EnsureNL: true, EnsureNL: true,
}, },
Rules{ arduinoRules,
))
func arduinoRules() Rules {
return Rules{
"statements": { "statements": {
{Words(``, `\b`, `catch`, `const_cast`, `delete`, `dynamic_cast`, `explicit`, `export`, `friend`, `mutable`, `namespace`, `new`, `operator`, `private`, `protected`, `public`, `reinterpret_cast`, `restrict`, `static_cast`, `template`, `this`, `throw`, `throws`, `try`, `typeid`, `typename`, `using`, `virtual`, `constexpr`, `nullptr`, `decltype`, `thread_local`, `alignas`, `alignof`, `static_assert`, `noexcept`, `override`, `final`), Keyword, nil}, {Words(``, `\b`, `catch`, `const_cast`, `delete`, `dynamic_cast`, `explicit`, `export`, `friend`, `mutable`, `namespace`, `new`, `operator`, `private`, `protected`, `public`, `reinterpret_cast`, `restrict`, `static_cast`, `template`, `this`, `throw`, `throws`, `try`, `typeid`, `typename`, `using`, `virtual`, `constexpr`, `nullptr`, `decltype`, `thread_local`, `alignas`, `alignof`, `static_assert`, `noexcept`, `override`, `final`), Keyword, nil},
{`char(16_t|32_t)\b`, KeywordType, nil}, {`char(16_t|32_t)\b`, KeywordType, nil},
@ -106,5 +110,5 @@ var Arduino = internal.Register(MustNewLexer(
{`^\s*#endif.*?(?<!\\)\n`, CommentPreproc, Pop(1)}, {`^\s*#endif.*?(?<!\\)\n`, CommentPreproc, Pop(1)},
{`.*?\n`, Comment, nil}, {`.*?\n`, Comment, nil},
}, },
}, }
)) }

View File

@ -6,14 +6,18 @@ import (
) )
// Awk lexer. // Awk lexer.
var Awk = internal.Register(MustNewLexer( var Awk = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "Awk", Name: "Awk",
Aliases: []string{"awk", "gawk", "mawk", "nawk"}, Aliases: []string{"awk", "gawk", "mawk", "nawk"},
Filenames: []string{"*.awk"}, Filenames: []string{"*.awk"},
MimeTypes: []string{"application/x-awk"}, MimeTypes: []string{"application/x-awk"},
}, },
Rules{ awkRules,
))
func awkRules() Rules {
return Rules{
"commentsandwhitespace": { "commentsandwhitespace": {
{`\s+`, Text, nil}, {`\s+`, Text, nil},
{`#.*$`, CommentSingle, nil}, {`#.*$`, CommentSingle, nil},
@ -44,5 +48,5 @@ var Awk = internal.Register(MustNewLexer(
{`"(\\\\|\\"|[^"])*"`, LiteralStringDouble, nil}, {`"(\\\\|\\"|[^"])*"`, LiteralStringDouble, nil},
{`'(\\\\|\\'|[^'])*'`, LiteralStringSingle, nil}, {`'(\\\\|\\'|[^'])*'`, LiteralStringSingle, nil},
}, },
}, }
)) }

View File

@ -6,7 +6,7 @@ import (
) )
// Ballerina lexer. // Ballerina lexer.
var Ballerina = internal.Register(MustNewLexer( var Ballerina = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "Ballerina", Name: "Ballerina",
Aliases: []string{"ballerina"}, Aliases: []string{"ballerina"},
@ -14,7 +14,11 @@ var Ballerina = internal.Register(MustNewLexer(
MimeTypes: []string{"text/x-ballerina"}, MimeTypes: []string{"text/x-ballerina"},
DotAll: true, DotAll: true,
}, },
Rules{ ballerinaRules,
))
func ballerinaRules() Rules {
return Rules{
"root": { "root": {
{`[^\S\n]+`, Text, nil}, {`[^\S\n]+`, Text, nil},
{`//.*?\n`, CommentSingle, nil}, {`//.*?\n`, CommentSingle, nil},
@ -42,5 +46,5 @@ var Ballerina = internal.Register(MustNewLexer(
"import": { "import": {
{`[\w.]+`, NameNamespace, Pop(1)}, {`[\w.]+`, NameNamespace, Pop(1)},
}, },
}, }
)) }

View File

@ -7,17 +7,27 @@ import (
"github.com/alecthomas/chroma/lexers/internal" "github.com/alecthomas/chroma/lexers/internal"
) )
// TODO(moorereason): can this be factored away?
var bashAnalyserRe = regexp.MustCompile(`(?m)^#!.*/bin/(?:env |)(?:bash|zsh|sh|ksh)`) var bashAnalyserRe = regexp.MustCompile(`(?m)^#!.*/bin/(?:env |)(?:bash|zsh|sh|ksh)`)
// Bash lexer. // Bash lexer.
var Bash = internal.Register(MustNewLexer( var Bash = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "Bash", Name: "Bash",
Aliases: []string{"bash", "sh", "ksh", "zsh", "shell"}, Aliases: []string{"bash", "sh", "ksh", "zsh", "shell"},
Filenames: []string{"*.sh", "*.ksh", "*.bash", "*.ebuild", "*.eclass", "*.exheres-0", "*.exlib", "*.zsh", "*.zshrc", ".bashrc", "bashrc", ".bash_*", "bash_*", "zshrc", ".zshrc", "PKGBUILD"}, Filenames: []string{"*.sh", "*.ksh", "*.bash", "*.ebuild", "*.eclass", ".env", "*.env", "*.exheres-0", "*.exlib", "*.zsh", "*.zshrc", ".bashrc", "bashrc", ".bash_*", "bash_*", "zshrc", ".zshrc", "PKGBUILD"},
MimeTypes: []string{"application/x-sh", "application/x-shellscript"}, MimeTypes: []string{"application/x-sh", "application/x-shellscript"},
}, },
Rules{ bashRules,
).SetAnalyser(func(text string) float32 {
if bashAnalyserRe.FindString(text) != "" {
return 1.0
}
return 0.0
}))
func bashRules() Rules {
return Rules{
"root": { "root": {
Include("basic"), Include("basic"),
{"`", LiteralStringBacktick, Push("backticks")}, {"`", LiteralStringBacktick, Push("backticks")},
@ -86,10 +96,5 @@ var Bash = internal.Register(MustNewLexer(
{"`", LiteralStringBacktick, Pop(1)}, {"`", LiteralStringBacktick, Pop(1)},
Include("root"), Include("root"),
}, },
},
).SetAnalyser(func(text string) float32 {
if bashAnalyserRe.FindString(text) != "" {
return 1.0
} }
return 0.0 }
}))

View File

@ -6,7 +6,7 @@ import (
) )
// Batchfile lexer. // Batchfile lexer.
var Batchfile = internal.Register(MustNewLexer( var Batchfile = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "Batchfile", Name: "Batchfile",
Aliases: []string{"bat", "batch", "dosbatch", "winbatch"}, Aliases: []string{"bat", "batch", "dosbatch", "winbatch"},
@ -14,7 +14,11 @@ var Batchfile = internal.Register(MustNewLexer(
MimeTypes: []string{"application/x-dos-batch"}, MimeTypes: []string{"application/x-dos-batch"},
CaseInsensitive: true, CaseInsensitive: true,
}, },
Rules{ batchfileRules,
))
func batchfileRules() Rules {
return Rules{
"root": { "root": {
{`\)((?=\()|(?=\^?[\t\v\f\r ,;=\xa0]|[&<>|\n\x1a]))(?:(?:[^\n\x1a^]|\^[\n\x1a]?[\w\W])*)`, CommentSingle, nil}, {`\)((?=\()|(?=\^?[\t\v\f\r ,;=\xa0]|[&<>|\n\x1a]))(?:(?:[^\n\x1a^]|\^[\n\x1a]?[\w\W])*)`, CommentSingle, nil},
{`(?=((?:(?<=^[^:])|^[^:]?)[\t\v\f\r ,;=\xa0]*)(:))`, Text, Push("follow")}, {`(?=((?:(?<=^[^:])|^[^:]?)[\t\v\f\r ,;=\xa0]*)(:))`, Text, Push("follow")},
@ -190,5 +194,5 @@ var Batchfile = internal.Register(MustNewLexer(
{`else(?=\^?[\t\v\f\r ,;=\xa0]|[&<>|\n\x1a])`, Keyword, Pop(1)}, {`else(?=\^?[\t\v\f\r ,;=\xa0]|[&<>|\n\x1a])`, Keyword, Pop(1)},
Default(Pop(1)), Default(Pop(1)),
}, },
}, }
)) }

View File

@ -6,7 +6,7 @@ import (
) )
// Bibtex lexer. // Bibtex lexer.
var Bibtex = internal.Register(MustNewLexer( var Bibtex = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "BibTeX", Name: "BibTeX",
Aliases: []string{"bib", "bibtex"}, Aliases: []string{"bib", "bibtex"},
@ -15,7 +15,11 @@ var Bibtex = internal.Register(MustNewLexer(
NotMultiline: true, NotMultiline: true,
CaseInsensitive: true, CaseInsensitive: true,
}, },
Rules{ bibtexRules,
))
func bibtexRules() Rules {
return Rules{
"root": { "root": {
Include("whitespace"), Include("whitespace"),
{`@comment`, Comment, nil}, {`@comment`, Comment, nil},
@ -72,5 +76,5 @@ var Bibtex = internal.Register(MustNewLexer(
"whitespace": { "whitespace": {
{`\s+`, Text, nil}, {`\s+`, Text, nil},
}, },
}, }
)) }

View File

@ -6,7 +6,7 @@ import (
) )
// Blitzbasic lexer. // Blitzbasic lexer.
var Blitzbasic = internal.Register(MustNewLexer( var Blitzbasic = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "BlitzBasic", Name: "BlitzBasic",
Aliases: []string{"blitzbasic", "b3d", "bplus"}, Aliases: []string{"blitzbasic", "b3d", "bplus"},
@ -14,7 +14,11 @@ var Blitzbasic = internal.Register(MustNewLexer(
MimeTypes: []string{"text/x-bb"}, MimeTypes: []string{"text/x-bb"},
CaseInsensitive: true, CaseInsensitive: true,
}, },
Rules{ blitzbasicRules,
))
func blitzbasicRules() Rules {
return Rules{
"root": { "root": {
{`[ \t]+`, Text, nil}, {`[ \t]+`, Text, nil},
{`;.*?\n`, CommentSingle, nil}, {`;.*?\n`, CommentSingle, nil},
@ -44,5 +48,5 @@ var Blitzbasic = internal.Register(MustNewLexer(
{`"C?`, LiteralStringDouble, Pop(1)}, {`"C?`, LiteralStringDouble, Pop(1)},
{`[^"]+`, LiteralStringDouble, nil}, {`[^"]+`, LiteralStringDouble, nil},
}, },
}, }
)) }

View File

@ -6,19 +6,23 @@ import (
) )
// Bnf lexer. // Bnf lexer.
var Bnf = internal.Register(MustNewLexer( var Bnf = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "BNF", Name: "BNF",
Aliases: []string{"bnf"}, Aliases: []string{"bnf"},
Filenames: []string{"*.bnf"}, Filenames: []string{"*.bnf"},
MimeTypes: []string{"text/x-bnf"}, MimeTypes: []string{"text/x-bnf"},
}, },
Rules{ bnfRules,
))
func bnfRules() Rules {
return Rules{
"root": { "root": {
{`(<)([ -;=?-~]+)(>)`, ByGroups(Punctuation, NameClass, Punctuation), nil}, {`(<)([ -;=?-~]+)(>)`, ByGroups(Punctuation, NameClass, Punctuation), nil},
{`::=`, Operator, nil}, {`::=`, Operator, nil},
{`[^<>:]+`, Text, nil}, {`[^<>:]+`, Text, nil},
{`.`, Text, nil}, {`.`, Text, nil},
}, },
}, }
)) }

View File

@ -6,14 +6,18 @@ import (
) )
// Brainfuck lexer. // Brainfuck lexer.
var Brainfuck = internal.Register(MustNewLexer( var Brainfuck = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "Brainfuck", Name: "Brainfuck",
Aliases: []string{"brainfuck", "bf"}, Aliases: []string{"brainfuck", "bf"},
Filenames: []string{"*.bf", "*.b"}, Filenames: []string{"*.bf", "*.b"},
MimeTypes: []string{"application/x-brainfuck"}, MimeTypes: []string{"application/x-brainfuck"},
}, },
Rules{ brainfuckRules,
))
func brainfuckRules() Rules {
return Rules{
"common": { "common": {
{`[.,]+`, NameTag, nil}, {`[.,]+`, NameTag, nil},
{`[+-]+`, NameBuiltin, nil}, {`[+-]+`, NameBuiltin, nil},
@ -30,5 +34,5 @@ var Brainfuck = internal.Register(MustNewLexer(
{`\]`, Keyword, Pop(1)}, {`\]`, Keyword, Pop(1)},
Include("common"), Include("common"),
}, },
}, }
)) }

View File

@ -6,14 +6,19 @@ import (
) )
// C lexer. // C lexer.
var C = internal.Register(MustNewLexer( var C = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "C", Name: "C",
Aliases: []string{"c"}, Aliases: []string{"c"},
Filenames: []string{"*.c", "*.h", "*.idc"}, Filenames: []string{"*.c", "*.h", "*.idc", "*.x[bp]m"},
MimeTypes: []string{"text/x-chdr", "text/x-csrc"}, MimeTypes: []string{"text/x-chdr", "text/x-csrc", "image/x-xbitmap", "image/x-xpixmap"},
EnsureNL: true,
}, },
Rules{ cRules,
))
func cRules() Rules {
return Rules{
"whitespace": { "whitespace": {
{`^#if\s+0`, CommentPreproc, Push("if0")}, {`^#if\s+0`, CommentPreproc, Push("if0")},
{`^#`, CommentPreproc, Push("macro")}, {`^#`, CommentPreproc, Push("macro")},
@ -38,7 +43,7 @@ var C = internal.Register(MustNewLexer(
{`[~!%^&*+=|?:<>/-]`, Operator, nil}, {`[~!%^&*+=|?:<>/-]`, Operator, nil},
{`[()\[\],.]`, Punctuation, nil}, {`[()\[\],.]`, Punctuation, nil},
{Words(``, `\b`, `asm`, `auto`, `break`, `case`, `const`, `continue`, `default`, `do`, `else`, `enum`, `extern`, `for`, `goto`, `if`, `register`, `restricted`, `return`, `sizeof`, `static`, `struct`, `switch`, `typedef`, `union`, `volatile`, `while`), Keyword, nil}, {Words(``, `\b`, `asm`, `auto`, `break`, `case`, `const`, `continue`, `default`, `do`, `else`, `enum`, `extern`, `for`, `goto`, `if`, `register`, `restricted`, `return`, `sizeof`, `static`, `struct`, `switch`, `typedef`, `union`, `volatile`, `while`), Keyword, nil},
{`(bool|int|long|float|short|double|char|unsigned|signed|void)\b`, KeywordType, nil}, {`(bool|int|long|float|short|double|char((8|16|32)_t)?|unsigned|signed|void|u?int(_fast|_least|)(8|16|32|64)_t)\b`, KeywordType, nil},
{Words(``, `\b`, `inline`, `_inline`, `__inline`, `naked`, `restrict`, `thread`, `typename`), KeywordReserved, nil}, {Words(``, `\b`, `inline`, `_inline`, `__inline`, `naked`, `restrict`, `thread`, `typename`), KeywordReserved, nil},
{`(__m(128i|128d|128|64))\b`, KeywordReserved, nil}, {`(__m(128i|128d|128|64))\b`, KeywordReserved, nil},
{Words(`__`, `\b`, `asm`, `int8`, `based`, `except`, `int16`, `stdcall`, `cdecl`, `fastcall`, `int32`, `declspec`, `finally`, `int64`, `try`, `leave`, `wchar_t`, `w64`, `unaligned`, `raise`, `noop`, `identifier`, `forceinline`, `assume`), KeywordReserved, nil}, {Words(`__`, `\b`, `asm`, `int8`, `based`, `except`, `int16`, `stdcall`, `cdecl`, `fastcall`, `int32`, `declspec`, `finally`, `int64`, `try`, `leave`, `wchar_t`, `w64`, `unaligned`, `raise`, `noop`, `identifier`, `forceinline`, `assume`), KeywordReserved, nil},
@ -87,5 +92,5 @@ var C = internal.Register(MustNewLexer(
{`^\s*#endif.*?(?<!\\)\n`, CommentPreproc, Pop(1)}, {`^\s*#endif.*?(?<!\\)\n`, CommentPreproc, Pop(1)},
{`.*?\n`, Comment, nil}, {`.*?\n`, Comment, nil},
}, },
}, }
)) }

View File

@ -6,143 +6,149 @@ import (
) )
// caddyfileCommon are the rules common to both of the lexer variants // caddyfileCommon are the rules common to both of the lexer variants
var caddyfileCommon = Rules{ func caddyfileCommonRules() Rules {
"site_block_common": { return Rules{
// Import keyword "site_block_common": {
{`(import)(\s+)([^\s]+)`, ByGroups(Keyword, Text, NameVariableMagic), nil}, // Import keyword
// Matcher definition {`(import)(\s+)([^\s]+)`, ByGroups(Keyword, Text, NameVariableMagic), nil},
{`@[^\s]+(?=\s)`, NameDecorator, Push("matcher")}, // Matcher definition
// Matcher token stub for docs {`@[^\s]+(?=\s)`, NameDecorator, Push("matcher")},
{`\[\<matcher\>\]`, NameDecorator, Push("matcher")}, // Matcher token stub for docs
// These cannot have matchers but may have things that look like {`\[\<matcher\>\]`, NameDecorator, Push("matcher")},
// matchers in their arguments, so we just parse as a subdirective. // These cannot have matchers but may have things that look like
{`try_files`, Keyword, Push("subdirective")}, // matchers in their arguments, so we just parse as a subdirective.
// These are special, they can nest more directives {`try_files`, Keyword, Push("subdirective")},
{`handle_errors|handle|route|handle_path|not`, Keyword, Push("nested_directive")}, // These are special, they can nest more directives
// Any other directive {`handle_errors|handle|route|handle_path|not`, Keyword, Push("nested_directive")},
{`[^\s#]+`, Keyword, Push("directive")}, // Any other directive
Include("base"), {`[^\s#]+`, Keyword, Push("directive")},
}, Include("base"),
"matcher": { },
{`\{`, Punctuation, Push("block")}, "matcher": {
// Not can be one-liner {`\{`, Punctuation, Push("block")},
{`not`, Keyword, Push("deep_not_matcher")}, // Not can be one-liner
// Any other same-line matcher {`not`, Keyword, Push("deep_not_matcher")},
{`[^\s#]+`, Keyword, Push("arguments")}, // Any other same-line matcher
// Terminators {`[^\s#]+`, Keyword, Push("arguments")},
{`\n`, Text, Pop(1)}, // Terminators
{`\}`, Punctuation, Pop(1)}, {`\n`, Text, Pop(1)},
Include("base"), {`\}`, Punctuation, Pop(1)},
}, Include("base"),
"block": { },
{`\}`, Punctuation, Pop(2)}, "block": {
// Not can be one-liner {`\}`, Punctuation, Pop(2)},
{`not`, Keyword, Push("not_matcher")}, // Not can be one-liner
// Any other subdirective {`not`, Keyword, Push("not_matcher")},
{`[^\s#]+`, Keyword, Push("subdirective")}, // Any other subdirective
Include("base"), {`[^\s#]+`, Keyword, Push("subdirective")},
}, Include("base"),
"nested_block": { },
{`\}`, Punctuation, Pop(2)}, "nested_block": {
// Matcher definition {`\}`, Punctuation, Pop(2)},
{`@[^\s]+(?=\s)`, NameDecorator, Push("matcher")}, // Matcher definition
// Something that starts with literally < is probably a docs stub {`@[^\s]+(?=\s)`, NameDecorator, Push("matcher")},
{`\<[^#]+\>`, Keyword, Push("nested_directive")}, // Something that starts with literally < is probably a docs stub
// Any other directive {`\<[^#]+\>`, Keyword, Push("nested_directive")},
{`[^\s#]+`, Keyword, Push("nested_directive")}, // Any other directive
Include("base"), {`[^\s#]+`, Keyword, Push("nested_directive")},
}, Include("base"),
"not_matcher": { },
{`\}`, Punctuation, Pop(2)}, "not_matcher": {
{`\{(?=\s)`, Punctuation, Push("block")}, {`\}`, Punctuation, Pop(2)},
{`[^\s#]+`, Keyword, Push("arguments")}, {`\{(?=\s)`, Punctuation, Push("block")},
{`\s+`, Text, nil}, {`[^\s#]+`, Keyword, Push("arguments")},
}, {`\s+`, Text, nil},
"deep_not_matcher": { },
{`\}`, Punctuation, Pop(2)}, "deep_not_matcher": {
{`\{(?=\s)`, Punctuation, Push("block")}, {`\}`, Punctuation, Pop(2)},
{`[^\s#]+`, Keyword, Push("deep_subdirective")}, {`\{(?=\s)`, Punctuation, Push("block")},
{`\s+`, Text, nil}, {`[^\s#]+`, Keyword, Push("deep_subdirective")},
}, {`\s+`, Text, nil},
"directive": { },
{`\{(?=\s)`, Punctuation, Push("block")}, "directive": {
Include("matcher_token"), {`\{(?=\s)`, Punctuation, Push("block")},
Include("comments_pop_1"), Include("matcher_token"),
{`\n`, Text, Pop(1)}, Include("comments_pop_1"),
Include("base"), {`\n`, Text, Pop(1)},
}, Include("base"),
"nested_directive": { },
{`\{(?=\s)`, Punctuation, Push("nested_block")}, "nested_directive": {
Include("matcher_token"), {`\{(?=\s)`, Punctuation, Push("nested_block")},
Include("comments_pop_1"), Include("matcher_token"),
{`\n`, Text, Pop(1)}, Include("comments_pop_1"),
Include("base"), {`\n`, Text, Pop(1)},
}, Include("base"),
"subdirective": { },
{`\{(?=\s)`, Punctuation, Push("block")}, "subdirective": {
Include("comments_pop_1"), {`\{(?=\s)`, Punctuation, Push("block")},
{`\n`, Text, Pop(1)}, Include("comments_pop_1"),
Include("base"), {`\n`, Text, Pop(1)},
}, Include("base"),
"arguments": { },
{`\{(?=\s)`, Punctuation, Push("block")}, "arguments": {
Include("comments_pop_2"), {`\{(?=\s)`, Punctuation, Push("block")},
{`\\\n`, Text, nil}, // Skip escaped newlines Include("comments_pop_2"),
{`\n`, Text, Pop(2)}, {`\\\n`, Text, nil}, // Skip escaped newlines
Include("base"), {`\n`, Text, Pop(2)},
}, Include("base"),
"deep_subdirective": { },
{`\{(?=\s)`, Punctuation, Push("block")}, "deep_subdirective": {
Include("comments_pop_3"), {`\{(?=\s)`, Punctuation, Push("block")},
{`\n`, Text, Pop(3)}, Include("comments_pop_3"),
Include("base"), {`\n`, Text, Pop(3)},
}, Include("base"),
"matcher_token": { },
{`@[^\s]+`, NameDecorator, Push("arguments")}, // Named matcher "matcher_token": {
{`/[^\s]+`, NameDecorator, Push("arguments")}, // Path matcher {`@[^\s]+`, NameDecorator, Push("arguments")}, // Named matcher
{`\*`, NameDecorator, Push("arguments")}, // Wildcard path matcher {`/[^\s]+`, NameDecorator, Push("arguments")}, // Path matcher
{`\[\<matcher\>\]`, NameDecorator, Push("arguments")}, // Matcher token stub for docs {`\*`, NameDecorator, Push("arguments")}, // Wildcard path matcher
}, {`\[\<matcher\>\]`, NameDecorator, Push("arguments")}, // Matcher token stub for docs
"comments": { },
{`^#.*\n`, CommentSingle, nil}, // Comment at start of line "comments": {
{`\s+#.*\n`, CommentSingle, nil}, // Comment preceded by whitespace {`^#.*\n`, CommentSingle, nil}, // Comment at start of line
}, {`\s+#.*\n`, CommentSingle, nil}, // Comment preceded by whitespace
"comments_pop_1": { },
{`^#.*\n`, CommentSingle, Pop(1)}, // Comment at start of line "comments_pop_1": {
{`\s+#.*\n`, CommentSingle, Pop(1)}, // Comment preceded by whitespace {`^#.*\n`, CommentSingle, Pop(1)}, // Comment at start of line
}, {`\s+#.*\n`, CommentSingle, Pop(1)}, // Comment preceded by whitespace
"comments_pop_2": { },
{`^#.*\n`, CommentSingle, Pop(2)}, // Comment at start of line "comments_pop_2": {
{`\s+#.*\n`, CommentSingle, Pop(2)}, // Comment preceded by whitespace {`^#.*\n`, CommentSingle, Pop(2)}, // Comment at start of line
}, {`\s+#.*\n`, CommentSingle, Pop(2)}, // Comment preceded by whitespace
"comments_pop_3": { },
{`^#.*\n`, CommentSingle, Pop(3)}, // Comment at start of line "comments_pop_3": {
{`\s+#.*\n`, CommentSingle, Pop(3)}, // Comment preceded by whitespace {`^#.*\n`, CommentSingle, Pop(3)}, // Comment at start of line
}, {`\s+#.*\n`, CommentSingle, Pop(3)}, // Comment preceded by whitespace
"base": { },
Include("comments"), "base": {
{`(on|off|first|last|before|after|internal|strip_prefix|strip_suffix|replace)\b`, NameConstant, nil}, Include("comments"),
{`(https?://)?([a-z0-9.-]+)(:)([0-9]+)`, ByGroups(Name, Name, Punctuation, LiteralNumberInteger), nil}, {`(on|off|first|last|before|after|internal|strip_prefix|strip_suffix|replace)\b`, NameConstant, nil},
{`[a-z-]+/[a-z-+]+`, LiteralString, nil}, {`(https?://)?([a-z0-9.-]+)(:)([0-9]+)`, ByGroups(Name, Name, Punctuation, LiteralNumberInteger), nil},
{`[0-9]+[km]?\b`, LiteralNumberInteger, nil}, {`[a-z-]+/[a-z-+]+`, LiteralString, nil},
{`\{[\w+.\$-]+\}`, LiteralStringEscape, nil}, // Placeholder {`[0-9]+[km]?\b`, LiteralNumberInteger, nil},
{`\[(?=[^#{}$]+\])`, Punctuation, nil}, {`\{[\w+.\$-]+\}`, LiteralStringEscape, nil}, // Placeholder
{`\]|\|`, Punctuation, nil}, {`\[(?=[^#{}$]+\])`, Punctuation, nil},
{`[^\s#{}$\]]+`, LiteralString, nil}, {`\]|\|`, Punctuation, nil},
{`/[^\s#]*`, Name, nil}, {`[^\s#{}$\]]+`, LiteralString, nil},
{`\s+`, Text, nil}, {`/[^\s#]*`, Name, nil},
}, {`\s+`, Text, nil},
},
}
} }
// Caddyfile lexer. // Caddyfile lexer.
var Caddyfile = internal.Register(MustNewLexer( var Caddyfile = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "Caddyfile", Name: "Caddyfile",
Aliases: []string{"caddyfile", "caddy"}, Aliases: []string{"caddyfile", "caddy"},
Filenames: []string{"Caddyfile*"}, Filenames: []string{"Caddyfile*"},
MimeTypes: []string{}, MimeTypes: []string{},
}, },
Rules{ caddyfileRules,
))
func caddyfileRules() Rules {
return Rules{
"root": { "root": {
Include("comments"), Include("comments"),
// Global options block // Global options block
@ -186,21 +192,25 @@ var Caddyfile = internal.Register(MustNewLexer(
{`\}`, Punctuation, Pop(2)}, {`\}`, Punctuation, Pop(2)},
Include("site_block_common"), Include("site_block_common"),
}, },
}.Merge(caddyfileCommon), }.Merge(caddyfileCommonRules())
)) }
// Caddyfile directive-only lexer. // Caddyfile directive-only lexer.
var CaddyfileDirectives = internal.Register(MustNewLexer( var CaddyfileDirectives = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "Caddyfile Directives", Name: "Caddyfile Directives",
Aliases: []string{"caddyfile-directives", "caddyfile-d", "caddy-d"}, Aliases: []string{"caddyfile-directives", "caddyfile-d", "caddy-d"},
Filenames: []string{}, Filenames: []string{},
MimeTypes: []string{}, MimeTypes: []string{},
}, },
Rules{ caddyfileDirectivesRules,
))
func caddyfileDirectivesRules() Rules {
return Rules{
// Same as "site_block" in Caddyfile // Same as "site_block" in Caddyfile
"root": { "root": {
Include("site_block_common"), Include("site_block_common"),
}, },
}.Merge(caddyfileCommon), }.Merge(caddyfileCommonRules())
)) }

View File

@ -6,14 +6,18 @@ import (
) )
// Cap'N'Proto Proto lexer. // Cap'N'Proto Proto lexer.
var CapNProto = internal.Register(MustNewLexer( var CapNProto = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "Cap'n Proto", Name: "Cap'n Proto",
Aliases: []string{"capnp"}, Aliases: []string{"capnp"},
Filenames: []string{"*.capnp"}, Filenames: []string{"*.capnp"},
MimeTypes: []string{}, MimeTypes: []string{},
}, },
Rules{ capNProtoRules,
))
func capNProtoRules() Rules {
return Rules{
"root": { "root": {
{`#.*?$`, CommentSingle, nil}, {`#.*?$`, CommentSingle, nil},
{`@[0-9a-zA-Z]*`, NameDecorator, nil}, {`@[0-9a-zA-Z]*`, NameDecorator, nil},
@ -57,5 +61,5 @@ var CapNProto = internal.Register(MustNewLexer(
{`[])]`, NameAttribute, Pop(1)}, {`[])]`, NameAttribute, Pop(1)},
Default(Pop(1)), Default(Pop(1)),
}, },
}, }
)) }

View File

@ -6,7 +6,7 @@ import (
) )
// Ceylon lexer. // Ceylon lexer.
var Ceylon = internal.Register(MustNewLexer( var Ceylon = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "Ceylon", Name: "Ceylon",
Aliases: []string{"ceylon"}, Aliases: []string{"ceylon"},
@ -14,7 +14,11 @@ var Ceylon = internal.Register(MustNewLexer(
MimeTypes: []string{"text/x-ceylon"}, MimeTypes: []string{"text/x-ceylon"},
DotAll: true, DotAll: true,
}, },
Rules{ ceylonRules,
))
func ceylonRules() Rules {
return Rules{
"root": { "root": {
{`^(\s*(?:[a-zA-Z_][\w.\[\]]*\s+)+?)([a-zA-Z_]\w*)(\s*)(\()`, ByGroups(UsingSelf("root"), NameFunction, Text, Operator), nil}, {`^(\s*(?:[a-zA-Z_][\w.\[\]]*\s+)+?)([a-zA-Z_]\w*)(\s*)(\()`, ByGroups(UsingSelf("root"), NameFunction, Text, Operator), nil},
{`[^\S\n]+`, Text, nil}, {`[^\S\n]+`, Text, nil},
@ -59,5 +63,5 @@ var Ceylon = internal.Register(MustNewLexer(
{`\*/`, CommentMultiline, Pop(1)}, {`\*/`, CommentMultiline, Pop(1)},
{`[*/]`, CommentMultiline, nil}, {`[*/]`, CommentMultiline, nil},
}, },
}, }
)) }

View File

@ -6,14 +6,18 @@ import (
) )
// Cfengine3 lexer. // Cfengine3 lexer.
var Cfengine3 = internal.Register(MustNewLexer( var Cfengine3 = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "CFEngine3", Name: "CFEngine3",
Aliases: []string{"cfengine3", "cf3"}, Aliases: []string{"cfengine3", "cf3"},
Filenames: []string{"*.cf"}, Filenames: []string{"*.cf"},
MimeTypes: []string{}, MimeTypes: []string{},
}, },
Rules{ cfengine3Rules,
))
func cfengine3Rules() Rules {
return Rules{
"root": { "root": {
{`#.*?\n`, Comment, nil}, {`#.*?\n`, Comment, nil},
{`(body)(\s+)(\S+)(\s+)(control)`, ByGroups(Keyword, Text, Keyword, Text, Keyword), nil}, {`(body)(\s+)(\S+)(\s+)(control)`, ByGroups(Keyword, Text, Keyword, Text, Keyword), nil},
@ -52,5 +56,5 @@ var Cfengine3 = internal.Register(MustNewLexer(
{`\w+`, NameVariable, nil}, {`\w+`, NameVariable, nil},
{`\s+`, Text, nil}, {`\s+`, Text, nil},
}, },
}, }
)) }

View File

@ -6,7 +6,7 @@ import (
) )
// Chaiscript lexer. // Chaiscript lexer.
var Chaiscript = internal.Register(MustNewLexer( var Chaiscript = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "ChaiScript", Name: "ChaiScript",
Aliases: []string{"chai", "chaiscript"}, Aliases: []string{"chai", "chaiscript"},
@ -14,7 +14,11 @@ var Chaiscript = internal.Register(MustNewLexer(
MimeTypes: []string{"text/x-chaiscript", "application/x-chaiscript"}, MimeTypes: []string{"text/x-chaiscript", "application/x-chaiscript"},
DotAll: true, DotAll: true,
}, },
Rules{ chaiscriptRules,
))
func chaiscriptRules() Rules {
return Rules{
"commentsandwhitespace": { "commentsandwhitespace": {
{`\s+`, Text, nil}, {`\s+`, Text, nil},
{`//.*?\n`, CommentSingle, nil}, {`//.*?\n`, CommentSingle, nil},
@ -59,5 +63,5 @@ var Chaiscript = internal.Register(MustNewLexer(
{`[^\\"$]+`, LiteralStringDouble, nil}, {`[^\\"$]+`, LiteralStringDouble, nil},
{`"`, LiteralStringDouble, Pop(1)}, {`"`, LiteralStringDouble, Pop(1)},
}, },
}, }
)) }

View File

@ -7,14 +7,18 @@ import (
) )
// Cheetah lexer. // Cheetah lexer.
var Cheetah = internal.Register(MustNewLexer( var Cheetah = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "Cheetah", Name: "Cheetah",
Aliases: []string{"cheetah", "spitfire"}, Aliases: []string{"cheetah", "spitfire"},
Filenames: []string{"*.tmpl", "*.spt"}, Filenames: []string{"*.tmpl", "*.spt"},
MimeTypes: []string{"application/x-cheetah", "application/x-spitfire"}, MimeTypes: []string{"application/x-cheetah", "application/x-spitfire"},
}, },
Rules{ cheetahRules,
))
func cheetahRules() Rules {
return Rules{
"root": { "root": {
{`(##[^\n]*)$`, ByGroups(Comment), nil}, {`(##[^\n]*)$`, ByGroups(Comment), nil},
{`#[*](.|\n)*?[*]#`, Comment, nil}, {`#[*](.|\n)*?[*]#`, Comment, nil},
@ -33,5 +37,5 @@ var Cheetah = internal.Register(MustNewLexer(
`, Other, nil}, `, Other, nil},
{`\s+`, Text, nil}, {`\s+`, Text, nil},
}, },
}, }
)) }

View File

@ -230,7 +230,7 @@ var (
) )
// Common Lisp lexer. // Common Lisp lexer.
var CommonLisp = internal.Register(TypeRemappingLexer(MustNewLexer( var CommonLisp = internal.Register(TypeRemappingLexer(MustNewLazyLexer(
&Config{ &Config{
Name: "Common Lisp", Name: "Common Lisp",
Aliases: []string{"common-lisp", "cl", "lisp"}, Aliases: []string{"common-lisp", "cl", "lisp"},
@ -238,7 +238,19 @@ var CommonLisp = internal.Register(TypeRemappingLexer(MustNewLexer(
MimeTypes: []string{"text/x-common-lisp"}, MimeTypes: []string{"text/x-common-lisp"},
CaseInsensitive: true, CaseInsensitive: true,
}, },
Rules{ commonLispRules,
), TypeMapping{
{NameVariable, NameFunction, clBuiltinFunctions},
{NameVariable, Keyword, clSpecialForms},
{NameVariable, NameBuiltin, clMacros},
{NameVariable, Keyword, clLambdaListKeywords},
{NameVariable, Keyword, clDeclarations},
{NameVariable, KeywordType, clBuiltinTypes},
{NameVariable, NameClass, clBuiltinClasses},
}))
func commonLispRules() Rules {
return Rules{
"root": { "root": {
Default(Push("body")), Default(Push("body")),
}, },
@ -294,13 +306,5 @@ var CommonLisp = internal.Register(TypeRemappingLexer(MustNewLexer(
{`\(`, Punctuation, Push("body")}, {`\(`, Punctuation, Push("body")},
{`\)`, Punctuation, Pop(1)}, {`\)`, Punctuation, Pop(1)},
}, },
}, }
), TypeMapping{ }
{NameVariable, NameFunction, clBuiltinFunctions},
{NameVariable, Keyword, clSpecialForms},
{NameVariable, NameBuiltin, clMacros},
{NameVariable, Keyword, clLambdaListKeywords},
{NameVariable, Keyword, clDeclarations},
{NameVariable, KeywordType, clBuiltinTypes},
{NameVariable, NameClass, clBuiltinClasses},
}))

View File

@ -6,14 +6,18 @@ import (
) )
// Clojure lexer. // Clojure lexer.
var Clojure = internal.Register(MustNewLexer( var Clojure = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "Clojure", Name: "Clojure",
Aliases: []string{"clojure", "clj"}, Aliases: []string{"clojure", "clj"},
Filenames: []string{"*.clj"}, Filenames: []string{"*.clj"},
MimeTypes: []string{"text/x-clojure", "application/x-clojure"}, MimeTypes: []string{"text/x-clojure", "application/x-clojure"},
}, },
Rules{ clojureRules,
))
func clojureRules() Rules {
return Rules{
"root": { "root": {
{`;.*$`, CommentSingle, nil}, {`;.*$`, CommentSingle, nil},
{`[,\s]+`, Text, nil}, {`[,\s]+`, Text, nil},
@ -34,5 +38,5 @@ var Clojure = internal.Register(MustNewLexer(
{`(\{|\})`, Punctuation, nil}, {`(\{|\})`, Punctuation, nil},
{`(\(|\))`, Punctuation, nil}, {`(\(|\))`, Punctuation, nil},
}, },
}, }
)) }

View File

@ -6,14 +6,18 @@ import (
) )
// Cmake lexer. // Cmake lexer.
var Cmake = internal.Register(MustNewLexer( var Cmake = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "CMake", Name: "CMake",
Aliases: []string{"cmake"}, Aliases: []string{"cmake"},
Filenames: []string{"*.cmake", "CMakeLists.txt"}, Filenames: []string{"*.cmake", "CMakeLists.txt"},
MimeTypes: []string{"text/x-cmake"}, MimeTypes: []string{"text/x-cmake"},
}, },
Rules{ cmakeRules,
))
func cmakeRules() Rules {
return Rules{
"root": { "root": {
{`\b(\w+)([ \t]*)(\()`, ByGroups(NameBuiltin, Text, Punctuation), Push("args")}, {`\b(\w+)([ \t]*)(\()`, ByGroups(NameBuiltin, Text, Punctuation), Push("args")},
Include("keywords"), Include("keywords"),
@ -40,5 +44,5 @@ var Cmake = internal.Register(MustNewLexer(
{`[ \t]+`, Text, nil}, {`[ \t]+`, Text, nil},
{`#.*\n`, Comment, nil}, {`#.*\n`, Comment, nil},
}, },
}, }
)) }

View File

@ -6,7 +6,7 @@ import (
) )
// Cobol lexer. // Cobol lexer.
var Cobol = internal.Register(MustNewLexer( var Cobol = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "COBOL", Name: "COBOL",
Aliases: []string{"cobol"}, Aliases: []string{"cobol"},
@ -14,7 +14,11 @@ var Cobol = internal.Register(MustNewLexer(
MimeTypes: []string{"text/x-cobol"}, MimeTypes: []string{"text/x-cobol"},
CaseInsensitive: true, CaseInsensitive: true,
}, },
Rules{ cobolRules,
))
func cobolRules() Rules {
return Rules{
"root": { "root": {
Include("comment"), Include("comment"),
Include("strings"), Include("strings"),
@ -47,5 +51,5 @@ var Cobol = internal.Register(MustNewLexer(
{`[+-]?\d*\.\d+(E[-+]?\d+)?`, LiteralNumberFloat, nil}, {`[+-]?\d*\.\d+(E[-+]?\d+)?`, LiteralNumberFloat, nil},
{`[+-]?\d+\.\d*(E[-+]?\d+)?`, LiteralNumberFloat, nil}, {`[+-]?\d+\.\d*(E[-+]?\d+)?`, LiteralNumberFloat, nil},
}, },
}, }
)) }

View File

@ -6,7 +6,7 @@ import (
) )
// Coffeescript lexer. // Coffeescript lexer.
var Coffeescript = internal.Register(MustNewLexer( var Coffeescript = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "CoffeeScript", Name: "CoffeeScript",
Aliases: []string{"coffee-script", "coffeescript", "coffee"}, Aliases: []string{"coffee-script", "coffeescript", "coffee"},
@ -15,7 +15,11 @@ var Coffeescript = internal.Register(MustNewLexer(
NotMultiline: true, NotMultiline: true,
DotAll: true, DotAll: true,
}, },
Rules{ coffeescriptRules,
))
func coffeescriptRules() Rules {
return Rules{
"commentsandwhitespace": { "commentsandwhitespace": {
{`\s+`, Text, nil}, {`\s+`, Text, nil},
{`###[^#].*?###`, CommentMultiline, nil}, {`###[^#].*?###`, CommentMultiline, nil},
@ -87,5 +91,5 @@ var Coffeescript = internal.Register(MustNewLexer(
{`#|\\.|\'|"`, LiteralString, nil}, {`#|\\.|\'|"`, LiteralString, nil},
Include("strings"), Include("strings"),
}, },
}, }
)) }

View File

@ -6,7 +6,7 @@ import (
) )
// Cfstatement lexer. // Cfstatement lexer.
var Cfstatement = internal.Register(MustNewLexer( var Cfstatement = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "cfstatement", Name: "cfstatement",
Aliases: []string{"cfs"}, Aliases: []string{"cfs"},
@ -15,7 +15,11 @@ var Cfstatement = internal.Register(MustNewLexer(
NotMultiline: true, NotMultiline: true,
CaseInsensitive: true, CaseInsensitive: true,
}, },
Rules{ cfstatementRules,
))
func cfstatementRules() Rules {
return Rules{
"root": { "root": {
{`//.*?\n`, CommentSingle, nil}, {`//.*?\n`, CommentSingle, nil},
{`/\*(?:.|\n)*?\*/`, CommentMultiline, nil}, {`/\*(?:.|\n)*?\*/`, CommentMultiline, nil},
@ -44,5 +48,5 @@ var Cfstatement = internal.Register(MustNewLexer(
{`#`, LiteralStringDouble, nil}, {`#`, LiteralStringDouble, nil},
{`"`, LiteralStringDouble, Pop(1)}, {`"`, LiteralStringDouble, Pop(1)},
}, },
}, }
)) }

View File

@ -6,14 +6,18 @@ import (
) )
// Coq lexer. // Coq lexer.
var Coq = internal.Register(MustNewLexer( var Coq = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "Coq", Name: "Coq",
Aliases: []string{"coq"}, Aliases: []string{"coq"},
Filenames: []string{"*.v"}, Filenames: []string{"*.v"},
MimeTypes: []string{"text/x-coq"}, MimeTypes: []string{"text/x-coq"},
}, },
Rules{ coqRules,
))
func coqRules() Rules {
return Rules{
"root": { "root": {
{`\s+`, Text, nil}, {`\s+`, Text, nil},
{`false|true|\(\)|\[\]`, NameBuiltinPseudo, nil}, {`false|true|\(\)|\[\]`, NameBuiltinPseudo, nil},
@ -59,5 +63,5 @@ var Coq = internal.Register(MustNewLexer(
{`[a-z][a-z0-9_\']*`, Name, Pop(1)}, {`[a-z][a-z0-9_\']*`, Name, Pop(1)},
Default(Pop(1)), Default(Pop(1)),
}, },
}, }
)) }

View File

@ -6,7 +6,7 @@ import (
) )
// CPP lexer. // CPP lexer.
var CPP = internal.Register(MustNewLexer( var CPP = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "C++", Name: "C++",
Aliases: []string{"cpp", "c++"}, Aliases: []string{"cpp", "c++"},
@ -14,7 +14,11 @@ var CPP = internal.Register(MustNewLexer(
MimeTypes: []string{"text/x-c++hdr", "text/x-c++src"}, MimeTypes: []string{"text/x-c++hdr", "text/x-c++src"},
EnsureNL: true, EnsureNL: true,
}, },
Rules{ cppRules,
))
func cppRules() Rules {
return Rules{
"statements": { "statements": {
{Words(``, `\b`, `catch`, `const_cast`, `delete`, `dynamic_cast`, `explicit`, `export`, `friend`, `mutable`, `namespace`, `new`, `operator`, `private`, `protected`, `public`, `reinterpret_cast`, `restrict`, `static_cast`, `template`, `this`, `throw`, `throws`, `try`, `typeid`, `typename`, `using`, `virtual`, `constexpr`, `nullptr`, `decltype`, `thread_local`, `alignas`, `alignof`, `static_assert`, `noexcept`, `override`, `final`, `concept`, `requires`, `consteval`, `co_await`, `co_return`, `co_yield`), Keyword, nil}, {Words(``, `\b`, `catch`, `const_cast`, `delete`, `dynamic_cast`, `explicit`, `export`, `friend`, `mutable`, `namespace`, `new`, `operator`, `private`, `protected`, `public`, `reinterpret_cast`, `restrict`, `static_cast`, `template`, `this`, `throw`, `throws`, `try`, `typeid`, `typename`, `using`, `virtual`, `constexpr`, `nullptr`, `decltype`, `thread_local`, `alignas`, `alignof`, `static_assert`, `noexcept`, `override`, `final`, `concept`, `requires`, `consteval`, `co_await`, `co_return`, `co_yield`), Keyword, nil},
{`(enum)\b(\s+)(class)\b(\s*)`, ByGroups(Keyword, Text, Keyword, Text), Push("classname")}, {`(enum)\b(\s+)(class)\b(\s*)`, ByGroups(Keyword, Text, Keyword, Text), Push("classname")},
@ -102,5 +106,5 @@ var CPP = internal.Register(MustNewLexer(
{`^\s*#endif.*?(?<!\\)\n`, CommentPreproc, Pop(1)}, {`^\s*#endif.*?(?<!\\)\n`, CommentPreproc, Pop(1)},
{`.*?\n`, Comment, nil}, {`.*?\n`, Comment, nil},
}, },
}, }
)) }

View File

@ -6,7 +6,7 @@ import (
) )
// CassandraCQL lexer. // CassandraCQL lexer.
var CassandraCQL = internal.Register(MustNewLexer( var CassandraCQL = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "Cassandra CQL", Name: "Cassandra CQL",
Aliases: []string{"cassandra", "cql"}, Aliases: []string{"cassandra", "cql"},
@ -15,7 +15,11 @@ var CassandraCQL = internal.Register(MustNewLexer(
NotMultiline: true, NotMultiline: true,
CaseInsensitive: true, CaseInsensitive: true,
}, },
Rules{ cassandraCQLRules,
))
func cassandraCQLRules() Rules {
return Rules{
"root": { "root": {
{`\s+`, TextWhitespace, nil}, {`\s+`, TextWhitespace, nil},
{`(--|\/\/).*\n?`, CommentSingle, nil}, {`(--|\/\/).*\n?`, CommentSingle, nil},
@ -23,7 +27,8 @@ var CassandraCQL = internal.Register(MustNewLexer(
{`(ascii|bigint|blob|boolean|counter|date|decimal|double|float|frozen|inet|int|list|map|set|smallint|text|time|timestamp|timeuuid|tinyint|tuple|uuid|varchar|varint)\b`, NameBuiltin, nil}, {`(ascii|bigint|blob|boolean|counter|date|decimal|double|float|frozen|inet|int|list|map|set|smallint|text|time|timestamp|timeuuid|tinyint|tuple|uuid|varchar|varint)\b`, NameBuiltin, nil},
{Words(``, `\b`, `ADD`, `AGGREGATE`, `ALL`, `ALLOW`, `ALTER`, `AND`, `ANY`, `APPLY`, `AS`, `ASC`, `AUTHORIZE`, `BATCH`, `BEGIN`, `BY`, `CLUSTERING`, `COLUMNFAMILY`, `COMPACT`, `CONSISTENCY`, `COUNT`, `CREATE`, `CUSTOM`, `DELETE`, `DESC`, `DISTINCT`, `DROP`, `EACH_QUORUM`, `ENTRIES`, `EXISTS`, `FILTERING`, `FROM`, `FULL`, `GRANT`, `IF`, `IN`, `INDEX`, `INFINITY`, `INSERT`, `INTO`, `KEY`, `KEYS`, `KEYSPACE`, `KEYSPACES`, `LEVEL`, `LIMIT`, `LOCAL_ONE`, `LOCAL_QUORUM`, `MATERIALIZED`, `MODIFY`, `NAN`, `NORECURSIVE`, `NOSUPERUSER`, `NOT`, `OF`, `ON`, `ONE`, `ORDER`, `PARTITION`, `PASSWORD`, `PER`, `PERMISSION`, `PERMISSIONS`, `PRIMARY`, `QUORUM`, `RENAME`, `REVOKE`, `SCHEMA`, `SELECT`, `STATIC`, `STORAGE`, `SUPERUSER`, `TABLE`, `THREE`, `TO`, `TOKEN`, `TRUNCATE`, `TTL`, `TWO`, `TYPE`, `UNLOGGED`, `UPDATE`, `USE`, `USER`, `USERS`, `USING`, `VALUES`, `VIEW`, `WHERE`, `WITH`, `WRITETIME`, `REPLICATION`, `OR`, `REPLACE`, `FUNCTION`, `CALLED`, `INPUT`, `RETURNS`, `LANGUAGE`, `ROLE`, `ROLES`, `TRIGGER`, `DURABLE_WRITES`, `LOGIN`, `OPTIONS`, `LOGGED`, `SFUNC`, `STYPE`, `FINALFUNC`, `INITCOND`, `IS`, `CONTAINS`, `JSON`, `PAGING`, `OFF`), Keyword, nil}, {Words(``, `\b`, `ADD`, `AGGREGATE`, `ALL`, `ALLOW`, `ALTER`, `AND`, `ANY`, `APPLY`, `AS`, `ASC`, `AUTHORIZE`, `BATCH`, `BEGIN`, `BY`, `CLUSTERING`, `COLUMNFAMILY`, `COMPACT`, `CONSISTENCY`, `COUNT`, `CREATE`, `CUSTOM`, `DELETE`, `DESC`, `DISTINCT`, `DROP`, `EACH_QUORUM`, `ENTRIES`, `EXISTS`, `FILTERING`, `FROM`, `FULL`, `GRANT`, `IF`, `IN`, `INDEX`, `INFINITY`, `INSERT`, `INTO`, `KEY`, `KEYS`, `KEYSPACE`, `KEYSPACES`, `LEVEL`, `LIMIT`, `LOCAL_ONE`, `LOCAL_QUORUM`, `MATERIALIZED`, `MODIFY`, `NAN`, `NORECURSIVE`, `NOSUPERUSER`, `NOT`, `OF`, `ON`, `ONE`, `ORDER`, `PARTITION`, `PASSWORD`, `PER`, `PERMISSION`, `PERMISSIONS`, `PRIMARY`, `QUORUM`, `RENAME`, `REVOKE`, `SCHEMA`, `SELECT`, `STATIC`, `STORAGE`, `SUPERUSER`, `TABLE`, `THREE`, `TO`, `TOKEN`, `TRUNCATE`, `TTL`, `TWO`, `TYPE`, `UNLOGGED`, `UPDATE`, `USE`, `USER`, `USERS`, `USING`, `VALUES`, `VIEW`, `WHERE`, `WITH`, `WRITETIME`, `REPLICATION`, `OR`, `REPLACE`, `FUNCTION`, `CALLED`, `INPUT`, `RETURNS`, `LANGUAGE`, `ROLE`, `ROLES`, `TRIGGER`, `DURABLE_WRITES`, `LOGIN`, `OPTIONS`, `LOGGED`, `SFUNC`, `STYPE`, `FINALFUNC`, `INITCOND`, `IS`, `CONTAINS`, `JSON`, `PAGING`, `OFF`), Keyword, nil},
{"[+*/<>=~!@#%^&|`?-]+", Operator, nil}, {"[+*/<>=~!@#%^&|`?-]+", Operator, nil},
{`(?s)(java|javascript)(\s+)(AS)(\s+)('|\$\$)(.*?)(\5)`, {
`(?s)(java|javascript)(\s+)(AS)(\s+)('|\$\$)(.*?)(\5)`,
UsingByGroup( UsingByGroup(
internal.Get, internal.Get,
1, 6, 1, 6,
@ -65,5 +70,5 @@ var CassandraCQL = internal.Register(MustNewLexer(
{`[^\$]+`, LiteralStringHeredoc, nil}, {`[^\$]+`, LiteralStringHeredoc, nil},
{`\$\$`, LiteralStringHeredoc, Pop(1)}, {`\$\$`, LiteralStringHeredoc, Pop(1)},
}, },
}, }
)) }

View File

@ -6,7 +6,7 @@ import (
) )
// Crystal lexer. // Crystal lexer.
var Crystal = internal.Register(MustNewLexer( var Crystal = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "Crystal", Name: "Crystal",
Aliases: []string{"cr", "crystal"}, Aliases: []string{"cr", "crystal"},
@ -14,7 +14,11 @@ var Crystal = internal.Register(MustNewLexer(
MimeTypes: []string{"text/x-crystal"}, MimeTypes: []string{"text/x-crystal"},
DotAll: true, DotAll: true,
}, },
Rules{ crystalRules,
))
func crystalRules() Rules {
return Rules{
"root": { "root": {
{`#.*?$`, CommentSingle, nil}, {`#.*?$`, CommentSingle, nil},
{Words(``, `\b`, `abstract`, `asm`, `as`, `begin`, `break`, `case`, `do`, `else`, `elsif`, `end`, `ensure`, `extend`, `ifdef`, `if`, `include`, `instance_sizeof`, `next`, `of`, `pointerof`, `private`, `protected`, `rescue`, `return`, `require`, `sizeof`, `super`, `then`, `typeof`, `unless`, `until`, `when`, `while`, `with`, `yield`), Keyword, nil}, {Words(``, `\b`, `abstract`, `asm`, `as`, `begin`, `break`, `case`, `do`, `else`, `elsif`, `end`, `ensure`, `extend`, `ifdef`, `if`, `include`, `instance_sizeof`, `next`, `of`, `pointerof`, `private`, `protected`, `rescue`, `return`, `require`, `sizeof`, `super`, `then`, `typeof`, `unless`, `until`, `when`, `while`, `with`, `yield`), Keyword, nil},
@ -258,5 +262,5 @@ var Crystal = internal.Register(MustNewLexer(
{`[\\#<>]`, LiteralStringRegex, nil}, {`[\\#<>]`, LiteralStringRegex, nil},
{`[^\\#<>]+`, LiteralStringRegex, nil}, {`[^\\#<>]+`, LiteralStringRegex, nil},
}, },
}, }
)) }

View File

@ -6,7 +6,7 @@ import (
) )
// CSharp lexer. // CSharp lexer.
var CSharp = internal.Register(MustNewLexer( var CSharp = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "C#", Name: "C#",
Aliases: []string{"csharp", "c#"}, Aliases: []string{"csharp", "c#"},
@ -15,7 +15,11 @@ var CSharp = internal.Register(MustNewLexer(
DotAll: true, DotAll: true,
EnsureNL: true, EnsureNL: true,
}, },
Rules{ cSharpRules,
))
func cSharpRules() Rules {
return Rules{
"root": { "root": {
{`^\s*\[.*?\]`, NameAttribute, nil}, {`^\s*\[.*?\]`, NameAttribute, nil},
{`[^\S\n]+`, Text, nil}, {`[^\S\n]+`, Text, nil},
@ -29,7 +33,7 @@ var CSharp = internal.Register(MustNewLexer(
{`\$@?"(""|[^"])*"`, LiteralString, nil}, {`\$@?"(""|[^"])*"`, LiteralString, nil},
{`"(\\\\|\\"|[^"\n])*["\n]`, LiteralString, nil}, {`"(\\\\|\\"|[^"\n])*["\n]`, LiteralString, nil},
{`'\\.'|'[^\\]'`, LiteralStringChar, nil}, {`'\\.'|'[^\\]'`, LiteralStringChar, nil},
{`[0-9](\.[0-9]*)?([eE][+-][0-9]+)?[flFLdD]?|0[xX][0-9a-fA-F]+[Ll]?`, LiteralNumber, nil}, {`0[xX][0-9a-fA-F]+[Ll]?|[0-9_](\.[0-9]*)?([eE][+-]?[0-9]+)?[flFLdD]?`, LiteralNumber, nil},
{`#[ \t]*(if|endif|else|elif|define|undef|line|error|warning|region|endregion|pragma)\b.*?\n`, CommentPreproc, nil}, {`#[ \t]*(if|endif|else|elif|define|undef|line|error|warning|region|endregion|pragma)\b.*?\n`, CommentPreproc, nil},
{`\b(extern)(\s+)(alias)\b`, ByGroups(Keyword, Text, Keyword), nil}, {`\b(extern)(\s+)(alias)\b`, ByGroups(Keyword, Text, Keyword), nil},
{`(abstract|as|async|await|base|break|by|case|catch|checked|const|continue|default|delegate|do|else|enum|event|explicit|extern|false|finally|fixed|for|foreach|goto|if|implicit|in|interface|internal|is|let|lock|new|null|on|operator|out|override|params|private|protected|public|readonly|ref|return|sealed|sizeof|stackalloc|static|switch|this|throw|true|try|typeof|unchecked|unsafe|virtual|void|while|get|set|new|partial|yield|add|remove|value|alias|ascending|descending|from|group|into|orderby|select|thenby|where|join|equals)\b`, Keyword, nil}, {`(abstract|as|async|await|base|break|by|case|catch|checked|const|continue|default|delegate|do|else|enum|event|explicit|extern|false|finally|fixed|for|foreach|goto|if|implicit|in|interface|internal|is|let|lock|new|null|on|operator|out|override|params|private|protected|public|readonly|ref|return|sealed|sizeof|stackalloc|static|switch|this|throw|true|try|typeof|unchecked|unsafe|virtual|void|while|get|set|new|partial|yield|add|remove|value|alias|ascending|descending|from|group|into|orderby|select|thenby|where|join|equals)\b`, Keyword, nil},
@ -47,5 +51,5 @@ var CSharp = internal.Register(MustNewLexer(
{`(?=\()`, Text, Pop(1)}, {`(?=\()`, Text, Pop(1)},
{`(@?[_a-zA-Z]\w*|\.)+`, NameNamespace, Pop(1)}, {`(@?[_a-zA-Z]\w*|\.)+`, NameNamespace, Pop(1)},
}, },
}, }
)) }

View File

@ -6,14 +6,18 @@ import (
) )
// CSS lexer. // CSS lexer.
var CSS = internal.Register(MustNewLexer( var CSS = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "CSS", Name: "CSS",
Aliases: []string{"css"}, Aliases: []string{"css"},
Filenames: []string{"*.css"}, Filenames: []string{"*.css"},
MimeTypes: []string{"text/css"}, MimeTypes: []string{"text/css"},
}, },
Rules{ cssRules,
))
func cssRules() Rules {
return Rules{
"root": { "root": {
Include("basics"), Include("basics"),
}, },
@ -39,6 +43,18 @@ var CSS = internal.Register(MustNewLexer(
Include("basics"), Include("basics"),
{`\}`, Punctuation, Pop(2)}, {`\}`, Punctuation, Pop(2)},
}, },
"atparenthesis": {
Include("common-values"),
{`/\*(?:.|\n)*?\*/`, Comment, nil},
Include("numeric-values"),
{`[*+/-]`, Operator, nil},
{`[,]`, Punctuation, nil},
{`"(\\\\|\\"|[^"])*"`, LiteralStringDouble, nil},
{`'(\\\\|\\'|[^'])*'`, LiteralStringSingle, nil},
{`[a-zA-Z_-]\w*`, Name, nil},
{`\(`, Punctuation, Push("atparenthesis")},
{`\)`, Punctuation, Pop(1)},
},
"content": { "content": {
{`\s+`, Text, nil}, {`\s+`, Text, nil},
{`\}`, Punctuation, Pop(1)}, {`\}`, Punctuation, Pop(1)},
@ -73,6 +89,7 @@ var CSS = internal.Register(MustNewLexer(
{`"(\\\\|\\"|[^"])*"`, LiteralStringDouble, nil}, {`"(\\\\|\\"|[^"])*"`, LiteralStringDouble, nil},
{`'(\\\\|\\'|[^'])*'`, LiteralStringSingle, nil}, {`'(\\\\|\\'|[^'])*'`, LiteralStringSingle, nil},
{`[a-zA-Z_-]\w*`, Name, nil}, {`[a-zA-Z_-]\w*`, Name, nil},
{`\(`, Punctuation, Push("atparenthesis")},
{`\)`, Punctuation, Pop(1)}, {`\)`, Punctuation, Pop(1)},
}, },
"common-values": { "common-values": {
@ -100,5 +117,5 @@ var CSS = internal.Register(MustNewLexer(
{`%`, KeywordType, nil}, {`%`, KeywordType, nil},
Default(Pop(1)), Default(Pop(1)),
}, },
}, }
)) }

View File

@ -6,14 +6,18 @@ import (
) )
// Cython lexer. // Cython lexer.
var Cython = internal.Register(MustNewLexer( var Cython = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "Cython", Name: "Cython",
Aliases: []string{"cython", "pyx", "pyrex"}, Aliases: []string{"cython", "pyx", "pyrex"},
Filenames: []string{"*.pyx", "*.pxd", "*.pxi"}, Filenames: []string{"*.pyx", "*.pxd", "*.pxi"},
MimeTypes: []string{"text/x-cython", "application/x-cython"}, MimeTypes: []string{"text/x-cython", "application/x-cython"},
}, },
Rules{ cythonRules,
))
func cythonRules() Rules {
return Rules{
"root": { "root": {
{`\n`, Text, nil}, {`\n`, Text, nil},
{`^(\s*)("""(?:.|\n)*?""")`, ByGroups(Text, LiteralStringDoc), nil}, {`^(\s*)("""(?:.|\n)*?""")`, ByGroups(Text, LiteralStringDoc), nil},
@ -131,5 +135,5 @@ var Cython = internal.Register(MustNewLexer(
Include("strings"), Include("strings"),
Include("nl"), Include("nl"),
}, },
}, }
)) }

View File

@ -6,7 +6,7 @@ import (
) )
// PHP lexer for pure PHP code (not embedded in HTML). // PHP lexer for pure PHP code (not embedded in HTML).
var PHP = internal.Register(MustNewLexer( var PHP = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "PHP", Name: "PHP",
Aliases: []string{"php", "php3", "php4", "php5"}, Aliases: []string{"php", "php3", "php4", "php5"},
@ -16,65 +16,71 @@ var PHP = internal.Register(MustNewLexer(
CaseInsensitive: true, CaseInsensitive: true,
EnsureNL: true, EnsureNL: true,
}, },
phpCommonRules.Rename("php", "root"), phpRules,
)) ))
var phpCommonRules = Rules{ func phpRules() Rules {
"php": { return phpCommonRules().Rename("php", "root")
{`\?>`, CommentPreproc, Pop(1)}, }
{`(<<<)([\'"]?)((?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*)(\2\n.*?\n\s*)(\3)(;?)(\n)`, ByGroups(LiteralString, LiteralString, LiteralStringDelimiter, LiteralString, LiteralStringDelimiter, Punctuation, Text), nil},
{`\s+`, Text, nil}, func phpCommonRules() Rules {
{`#.*?\n`, CommentSingle, nil}, return Rules{
{`//.*?\n`, CommentSingle, nil}, "php": {
{`/\*\*/`, CommentMultiline, nil}, {`\?>`, CommentPreproc, Pop(1)},
{`/\*\*.*?\*/`, LiteralStringDoc, nil}, {`(<<<)([\'"]?)((?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*)(\2\n.*?\n\s*)(\3)(;?)(\n)`, ByGroups(LiteralString, LiteralString, LiteralStringDelimiter, LiteralString, LiteralStringDelimiter, Punctuation, Text), nil},
{`/\*.*?\*/`, CommentMultiline, nil}, {`\s+`, Text, nil},
{`(->|::)(\s*)((?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*)`, ByGroups(Operator, Text, NameAttribute), nil}, {`#.*?\n`, CommentSingle, nil},
{`[~!%^&*+=|:.<>/@-]+`, Operator, nil}, {`//.*?\n`, CommentSingle, nil},
{`\?`, Operator, nil}, {`/\*\*/`, CommentMultiline, nil},
{`[\[\]{}();,]+`, Punctuation, nil}, {`/\*\*.*?\*/`, LiteralStringDoc, nil},
{`(class)(\s+)`, ByGroups(Keyword, Text), Push("classname")}, {`/\*.*?\*/`, CommentMultiline, nil},
{`(function)(\s*)(?=\()`, ByGroups(Keyword, Text), nil}, {`(->|::)(\s*)((?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*)`, ByGroups(Operator, Text, NameAttribute), nil},
{`(function)(\s+)(&?)(\s*)`, ByGroups(Keyword, Text, Operator, Text), Push("functionname")}, {`[~!%^&*+=|:.<>/@-]+`, Operator, nil},
{`(const)(\s+)((?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*)`, ByGroups(Keyword, Text, NameConstant), nil}, {`\?`, Operator, nil},
{`(and|E_PARSE|old_function|E_ERROR|or|as|E_WARNING|parent|eval|PHP_OS|break|exit|case|extends|PHP_VERSION|cfunction|FALSE|print|for|require|continue|foreach|require_once|declare|return|default|static|do|switch|die|stdClass|echo|else|TRUE|elseif|var|empty|if|xor|enddeclare|include|virtual|endfor|include_once|while|endforeach|global|endif|list|endswitch|new|endwhile|not|array|E_ALL|NULL|final|php_user_filter|interface|implements|public|private|protected|abstract|clone|try|catch|throw|this|use|namespace|trait|yield|finally)\b`, Keyword, nil}, {`[\[\]{}();,]+`, Punctuation, nil},
{`(true|false|null)\b`, KeywordConstant, nil}, {`(class)(\s+)`, ByGroups(Keyword, Text), Push("classname")},
Include("magicconstants"), {`(function)(\s*)(?=\()`, ByGroups(Keyword, Text), nil},
{`\$\{\$+(?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*\}`, NameVariable, nil}, {`(function)(\s+)(&?)(\s*)`, ByGroups(Keyword, Text, Operator, Text), Push("functionname")},
{`\$+(?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*`, NameVariable, nil}, {`(const)(\s+)((?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*)`, ByGroups(Keyword, Text, NameConstant), nil},
{`(?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*`, NameOther, nil}, {`(and|E_PARSE|old_function|E_ERROR|or|as|E_WARNING|parent|eval|PHP_OS|break|exit|case|extends|PHP_VERSION|cfunction|FALSE|print|for|require|continue|foreach|require_once|declare|return|default|static|do|switch|die|stdClass|echo|else|TRUE|elseif|var|empty|if|xor|enddeclare|include|virtual|endfor|include_once|while|endforeach|global|endif|list|endswitch|new|endwhile|not|array|E_ALL|NULL|final|php_user_filter|interface|implements|public|private|protected|abstract|clone|try|catch|throw|this|use|namespace|trait|yield|finally)\b`, Keyword, nil},
{`(\d+\.\d*|\d*\.\d+)(e[+-]?[0-9]+)?`, LiteralNumberFloat, nil}, {`(true|false|null)\b`, KeywordConstant, nil},
{`\d+e[+-]?[0-9]+`, LiteralNumberFloat, nil}, Include("magicconstants"),
{`0[0-7]+`, LiteralNumberOct, nil}, {`\$\{\$+(?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*\}`, NameVariable, nil},
{`0x[a-f0-9]+`, LiteralNumberHex, nil}, {`\$+(?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*`, NameVariable, nil},
{`\d+`, LiteralNumberInteger, nil}, {`(?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*`, NameOther, nil},
{`0b[01]+`, LiteralNumberBin, nil}, {`(\d+\.\d*|\d*\.\d+)(e[+-]?[0-9]+)?`, LiteralNumberFloat, nil},
{`'([^'\\]*(?:\\.[^'\\]*)*)'`, LiteralStringSingle, nil}, {`\d+e[+-]?[0-9]+`, LiteralNumberFloat, nil},
{"`([^`\\\\]*(?:\\\\.[^`\\\\]*)*)`", LiteralStringBacktick, nil}, {`0[0-7]+`, LiteralNumberOct, nil},
{`"`, LiteralStringDouble, Push("string")}, {`0x[a-f0-9_]+`, LiteralNumberHex, nil},
}, {`[\d_]+`, LiteralNumberInteger, nil},
"magicfuncs": { {`0b[01]+`, LiteralNumberBin, nil},
{Words(``, `\b`, `__construct`, `__destruct`, `__call`, `__callStatic`, `__get`, `__set`, `__isset`, `__unset`, `__sleep`, `__wakeup`, `__toString`, `__invoke`, `__set_state`, `__clone`, `__debugInfo`), NameFunctionMagic, nil}, {`'([^'\\]*(?:\\.[^'\\]*)*)'`, LiteralStringSingle, nil},
}, {"`([^`\\\\]*(?:\\\\.[^`\\\\]*)*)`", LiteralStringBacktick, nil},
"magicconstants": { {`"`, LiteralStringDouble, Push("string")},
{Words(``, `\b`, `__LINE__`, `__FILE__`, `__DIR__`, `__FUNCTION__`, `__CLASS__`, `__TRAIT__`, `__METHOD__`, `__NAMESPACE__`), NameConstant, nil}, },
}, "magicfuncs": {
"classname": { {Words(``, `\b`, `__construct`, `__destruct`, `__call`, `__callStatic`, `__get`, `__set`, `__isset`, `__unset`, `__sleep`, `__wakeup`, `__toString`, `__invoke`, `__set_state`, `__clone`, `__debugInfo`), NameFunctionMagic, nil},
{`(?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*`, NameClass, Pop(1)}, },
}, "magicconstants": {
"functionname": { {Words(``, `\b`, `__LINE__`, `__FILE__`, `__DIR__`, `__FUNCTION__`, `__CLASS__`, `__TRAIT__`, `__METHOD__`, `__NAMESPACE__`), NameConstant, nil},
Include("magicfuncs"), },
{`(?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*`, NameFunction, Pop(1)}, "classname": {
Default(Pop(1)), {`(?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*`, NameClass, Pop(1)},
}, },
"string": { "functionname": {
{`"`, LiteralStringDouble, Pop(1)}, Include("magicfuncs"),
{`[^{$"\\]+`, LiteralStringDouble, nil}, {`(?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*`, NameFunction, Pop(1)},
{`\\([nrt"$\\]|[0-7]{1,3}|x[0-9a-f]{1,2})`, LiteralStringEscape, nil}, Default(Pop(1)),
{`\$(?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*(\[\S+?\]|->(?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*)?`, LiteralStringInterpol, nil}, },
{`(\{\$\{)(.*?)(\}\})`, ByGroups(LiteralStringInterpol, UsingSelf("root"), LiteralStringInterpol), nil}, "string": {
{`(\{)(\$.*?)(\})`, ByGroups(LiteralStringInterpol, UsingSelf("root"), LiteralStringInterpol), nil}, {`"`, LiteralStringDouble, Pop(1)},
{`(\$\{)(\S+)(\})`, ByGroups(LiteralStringInterpol, NameVariable, LiteralStringInterpol), nil}, {`[^{$"\\]+`, LiteralStringDouble, nil},
{`[${\\]`, LiteralStringDouble, nil}, {`\\([nrt"$\\]|[0-7]{1,3}|x[0-9a-f]{1,2})`, LiteralStringEscape, nil},
}, {`\$(?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*(\[\S+?\]|->(?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*)?`, LiteralStringInterpol, nil},
{`(\{\$\{)(.*?)(\}\})`, ByGroups(LiteralStringInterpol, UsingSelf("root"), LiteralStringInterpol), nil},
{`(\{)(\$.*?)(\})`, ByGroups(LiteralStringInterpol, UsingSelf("root"), LiteralStringInterpol), nil},
{`(\$\{)(\S+)(\})`, ByGroups(LiteralStringInterpol, NameVariable, LiteralStringInterpol), nil},
{`[${\\]`, LiteralStringDouble, nil},
},
}
} }

View File

@ -9,26 +9,31 @@ import (
) )
// PHTML lexer is PHP in HTML. // PHTML lexer is PHP in HTML.
var PHTML = internal.Register(DelegatingLexer(h.HTML, MustNewLexer( var PHTML = internal.Register(DelegatingLexer(h.HTML, MustNewLazyLexer(
&Config{ &Config{
Name: "PHTML", Name: "PHTML",
Aliases: []string{"phtml"}, Aliases: []string{"phtml"},
Filenames: []string{"*.phtml"}, Filenames: []string{"*.phtml", "*.php", "*.php[345]", "*.inc"},
MimeTypes: []string{"application/x-php", "application/x-httpd-php", "application/x-httpd-php3", "application/x-httpd-php4", "application/x-httpd-php5"}, MimeTypes: []string{"application/x-php", "application/x-httpd-php", "application/x-httpd-php3", "application/x-httpd-php4", "application/x-httpd-php5", "text/x-php"},
DotAll: true, DotAll: true,
CaseInsensitive: true, CaseInsensitive: true,
EnsureNL: true, EnsureNL: true,
Priority: 2,
}, },
Rules{ phtmlRules,
"root": {
{`<\?(php)?`, CommentPreproc, Push("php")},
{`[^<]+`, Other, nil},
{`<`, Other, nil},
},
}.Merge(phpCommonRules),
).SetAnalyser(func(text string) float32 { ).SetAnalyser(func(text string) float32 {
if strings.Contains(text, "<?php") { if strings.Contains(text, "<?php") {
return 0.5 return 0.5
} }
return 0.0 return 0.0
}))) })))
func phtmlRules() Rules {
return Rules{
"root": {
{`<\?(php)?`, CommentPreproc, Push("php")},
{`[^<]+`, Other, nil},
{`<`, Other, nil},
},
}.Merge(phpCommonRules())
}

View File

@ -6,7 +6,7 @@ import (
) )
// D lexer. https://dlang.org/spec/lex.html // D lexer. https://dlang.org/spec/lex.html
var D = internal.Register(MustNewLexer( var D = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "D", Name: "D",
Aliases: []string{"d"}, Aliases: []string{"d"},
@ -14,7 +14,11 @@ var D = internal.Register(MustNewLexer(
MimeTypes: []string{"text/x-d"}, MimeTypes: []string{"text/x-d"},
EnsureNL: true, EnsureNL: true,
}, },
Rules{ dRules,
))
func dRules() Rules {
return Rules{
"root": { "root": {
{`[^\S\n]+`, Text, nil}, {`[^\S\n]+`, Text, nil},
@ -65,5 +69,5 @@ var D = internal.Register(MustNewLexer(
"import": { "import": {
{`[\w.]+\*?`, NameNamespace, Pop(1)}, {`[\w.]+\*?`, NameNamespace, Pop(1)},
}, },
}, }
)) }

View File

@ -6,7 +6,7 @@ import (
) )
// Dart lexer. // Dart lexer.
var Dart = internal.Register(MustNewLexer( var Dart = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "Dart", Name: "Dart",
Aliases: []string{"dart"}, Aliases: []string{"dart"},
@ -14,7 +14,11 @@ var Dart = internal.Register(MustNewLexer(
MimeTypes: []string{"text/x-dart"}, MimeTypes: []string{"text/x-dart"},
DotAll: true, DotAll: true,
}, },
Rules{ dartRules,
))
func dartRules() Rules {
return Rules{
"root": { "root": {
Include("string_literal"), Include("string_literal"),
{`#!(.*?)$`, CommentPreproc, nil}, {`#!(.*?)$`, CommentPreproc, nil},
@ -87,5 +91,5 @@ var Dart = internal.Register(MustNewLexer(
Include("string_common"), Include("string_common"),
{`(\$|\')+`, LiteralStringSingle, nil}, {`(\$|\')+`, LiteralStringSingle, nil},
}, },
}, }
)) }

View File

@ -6,7 +6,7 @@ import (
) )
// Diff lexer. // Diff lexer.
var Diff = internal.Register(MustNewLexer( var Diff = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "Diff", Name: "Diff",
Aliases: []string{"diff", "udiff"}, Aliases: []string{"diff", "udiff"},
@ -14,7 +14,11 @@ var Diff = internal.Register(MustNewLexer(
Filenames: []string{"*.diff", "*.patch"}, Filenames: []string{"*.diff", "*.patch"},
MimeTypes: []string{"text/x-diff", "text/x-patch"}, MimeTypes: []string{"text/x-diff", "text/x-patch"},
}, },
Rules{ diffRules,
))
func diffRules() Rules {
return Rules{
"root": { "root": {
{` .*\n`, Text, nil}, {` .*\n`, Text, nil},
{`\+.*\n`, GenericInserted, nil}, {`\+.*\n`, GenericInserted, nil},
@ -25,5 +29,5 @@ var Diff = internal.Register(MustNewLexer(
{`=.*\n`, GenericHeading, nil}, {`=.*\n`, GenericHeading, nil},
{`.*\n`, Text, nil}, {`.*\n`, Text, nil},
}, },
}, }
)) }

View File

@ -6,7 +6,7 @@ import (
) )
// Django/Jinja lexer. // Django/Jinja lexer.
var DjangoJinja = internal.Register(MustNewLexer( var DjangoJinja = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "Django/Jinja", Name: "Django/Jinja",
Aliases: []string{"django", "jinja"}, Aliases: []string{"django", "jinja"},
@ -14,7 +14,11 @@ var DjangoJinja = internal.Register(MustNewLexer(
MimeTypes: []string{"application/x-django-templating", "application/x-jinja"}, MimeTypes: []string{"application/x-django-templating", "application/x-jinja"},
DotAll: true, DotAll: true,
}, },
Rules{ djangoJinjaRules,
))
func djangoJinjaRules() Rules {
return Rules{
"root": { "root": {
{`[^{]+`, Other, nil}, {`[^{]+`, Other, nil},
{`\{\{`, CommentPreproc, Push("var")}, {`\{\{`, CommentPreproc, Push("var")},
@ -49,5 +53,5 @@ var DjangoJinja = internal.Register(MustNewLexer(
Include("varnames"), Include("varnames"),
{`.`, Punctuation, nil}, {`.`, Punctuation, nil},
}, },
}, }
)) }

View File

@ -8,7 +8,7 @@ import (
) )
// Docker lexer. // Docker lexer.
var Docker = internal.Register(MustNewLexer( var Docker = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "Docker", Name: "Docker",
Aliases: []string{"docker", "dockerfile"}, Aliases: []string{"docker", "dockerfile"},
@ -16,7 +16,11 @@ var Docker = internal.Register(MustNewLexer(
MimeTypes: []string{"text/x-dockerfile-config"}, MimeTypes: []string{"text/x-dockerfile-config"},
CaseInsensitive: true, CaseInsensitive: true,
}, },
Rules{ dockerRules,
))
func dockerRules() Rules {
return Rules{
"root": { "root": {
{`#.*`, Comment, nil}, {`#.*`, Comment, nil},
{`(ONBUILD)((?:\s*\\?\s*))`, ByGroups(Keyword, Using(b.Bash)), nil}, {`(ONBUILD)((?:\s*\\?\s*))`, ByGroups(Keyword, Using(b.Bash)), nil},
@ -27,5 +31,5 @@ var Docker = internal.Register(MustNewLexer(
{`((?:RUN|CMD|ENTRYPOINT|ENV|ARG|LABEL|ADD|COPY))`, Keyword, nil}, {`((?:RUN|CMD|ENTRYPOINT|ENV|ARG|LABEL|ADD|COPY))`, Keyword, nil},
{`(.*\\\n)*.+`, Using(b.Bash), nil}, {`(.*\\\n)*.+`, Using(b.Bash), nil},
}, },
}, }
)) }

View File

@ -6,7 +6,7 @@ import (
) )
// Dtd lexer. // Dtd lexer.
var Dtd = internal.Register(MustNewLexer( var Dtd = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "DTD", Name: "DTD",
Aliases: []string{"dtd"}, Aliases: []string{"dtd"},
@ -14,7 +14,11 @@ var Dtd = internal.Register(MustNewLexer(
MimeTypes: []string{"application/xml-dtd"}, MimeTypes: []string{"application/xml-dtd"},
DotAll: true, DotAll: true,
}, },
Rules{ dtdRules,
))
func dtdRules() Rules {
return Rules{
"root": { "root": {
Include("common"), Include("common"),
{`(<!ELEMENT)(\s+)(\S+)`, ByGroups(Keyword, Text, NameTag), Push("element")}, {`(<!ELEMENT)(\s+)(\S+)`, ByGroups(Keyword, Text, NameTag), Push("element")},
@ -65,5 +69,5 @@ var Dtd = internal.Register(MustNewLexer(
{`[^>\s|()?+*,]+`, NameAttribute, nil}, {`[^>\s|()?+*,]+`, NameAttribute, nil},
{`>`, Keyword, Pop(1)}, {`>`, Keyword, Pop(1)},
}, },
}, }
)) }

76
vendor/github.com/alecthomas/chroma/lexers/d/dylan.go generated vendored Normal file
View File

@ -0,0 +1,76 @@
package d
import (
. "github.com/alecthomas/chroma" // nolint
"github.com/alecthomas/chroma/lexers/internal"
)
// Dylan lexer.
var Dylan = internal.Register(MustNewLazyLexer(
&Config{
Name: "Dylan",
Aliases: []string{"dylan"},
Filenames: []string{"*.dylan", "*.dyl", "*.intr"},
MimeTypes: []string{"text/x-dylan"},
CaseInsensitive: true,
},
func() Rules {
return Rules{
"root": {
{`\s+`, Whitespace, nil},
{`//.*?\n`, CommentSingle, nil},
{`([a-z0-9-]+:)([ \t]*)(.*(?:\n[ \t].+)*)`, ByGroups(NameAttribute, Whitespace, LiteralString), nil},
Default(Push("code")),
},
"code": {
{`\s+`, Whitespace, nil},
{`//.*?\n`, CommentSingle, nil},
{`/\*`, CommentMultiline, Push("comment")},
{`"`, LiteralString, Push("string")},
{`'(\\.|\\[0-7]{1,3}|\\x[a-f0-9]{1,2}|[^\\\'\n])'`, LiteralStringChar, nil},
{`#b[01]+`, LiteralNumberBin, nil},
{`#o[0-7]+`, LiteralNumberOct, nil},
{`[-+]?(\d*\.\d+([ed][-+]?\d+)?|\d+(\.\d*)?e[-+]?\d+)`, LiteralNumberFloat, nil},
{`[-+]?\d+`, LiteralNumberInteger, nil},
{`#x[0-9a-f]+`, LiteralNumberHex, nil},
{`(\?\\?)([\w!&*<>|^$%@+~?/=-]+)(:)(token|name|variable|expression|body|case-body|\*)`,
ByGroups(Operator, NameVariable, Operator, NameBuiltin), nil},
{`(\?)(:)(token|name|variable|expression|body|case-body|\*)`,
ByGroups(Operator, Operator, NameVariable), nil},
{`(\?\\?)([\w!&*<>|^$%@+~?/=-]+)`, ByGroups(Operator, NameVariable), nil},
{`(=>|::|#\(|#\[|##|\?\?|\?=|\?|[(){}\[\],.;])`, Punctuation, nil},
{`:=`, Operator, nil},
{`#[tf]`, Literal, nil},
{`#"`, LiteralStringSymbol, Push("symbol")},
{`#[a-z0-9-]+`, Keyword, nil},
{`#(all-keys|include|key|next|rest)`, Keyword, nil},
{`[\w!&*<>|^$%@+~?/=-]+:`, KeywordConstant, nil},
{`<[\w!&*<>|^$%@+~?/=-]+>`, NameClass, nil},
{`\*[\w!&*<>|^$%@+~?/=-]+\*`, NameVariableGlobal, nil},
{`\$[\w!&*<>|^$%@+~?/=-]+`, NameConstant, nil},
{`(let|method|function)([ \t]+)([\w!&*<>|^$%@+~?/=-]+)`, ByGroups(NameBuiltin, Whitespace, NameVariable), nil},
{`(error|signal|return|break)`, NameException, nil},
{`(\\?)([\w!&*<>|^$%@+~?/=-]+)`, ByGroups(Operator, Name), nil},
},
"comment": {
{`[^*/]`, CommentMultiline, nil},
{`/\*`, CommentMultiline, Push()},
{`\*/`, CommentMultiline, Pop(1)},
{`[*/]`, CommentMultiline, nil},
},
"symbol": {
{`"`, LiteralStringSymbol, Pop(1)},
{`[^\\"]+`, LiteralStringSymbol, nil},
},
"string": {
{`"`, LiteralString, Pop(1)},
{`\\([\\abfnrtv"\']|x[a-f0-9]{2,4}|[0-7]{1,3})`, LiteralStringEscape, nil},
{`[^\\"\n]+`, LiteralString, nil},
{`\\\n`, LiteralString, nil},
{`\\`, LiteralString, nil},
},
}
},
))

View File

@ -6,14 +6,18 @@ import (
) )
// Ebnf lexer. // Ebnf lexer.
var Ebnf = internal.Register(MustNewLexer( var Ebnf = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "EBNF", Name: "EBNF",
Aliases: []string{"ebnf"}, Aliases: []string{"ebnf"},
Filenames: []string{"*.ebnf"}, Filenames: []string{"*.ebnf"},
MimeTypes: []string{"text/x-ebnf"}, MimeTypes: []string{"text/x-ebnf"},
}, },
Rules{ ebnfRules,
))
func ebnfRules() Rules {
return Rules{
"root": { "root": {
Include("whitespace"), Include("whitespace"),
Include("comment_start"), Include("comment_start"),
@ -47,5 +51,5 @@ var Ebnf = internal.Register(MustNewLexer(
"identifier": { "identifier": {
{`([a-zA-Z][\w \-]*)`, Keyword, nil}, {`([a-zA-Z][\w \-]*)`, Keyword, nil},
}, },
}, }
)) }

View File

@ -6,14 +6,18 @@ import (
) )
// Elixir lexer. // Elixir lexer.
var Elixir = internal.Register(MustNewLexer( var Elixir = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "Elixir", Name: "Elixir",
Aliases: []string{"elixir", "ex", "exs"}, Aliases: []string{"elixir", "ex", "exs"},
Filenames: []string{"*.ex", "*.exs"}, Filenames: []string{"*.ex", "*.exs"},
MimeTypes: []string{"text/x-elixir"}, MimeTypes: []string{"text/x-elixir"},
}, },
Rules{ elixirRules,
))
func elixirRules() Rules {
return Rules{
"root": { "root": {
{`\s+`, Text, nil}, {`\s+`, Text, nil},
{`#.*$`, CommentSingle, nil}, {`#.*$`, CommentSingle, nil},
@ -273,5 +277,5 @@ var Elixir = internal.Register(MustNewLexer(
{`\\.`, LiteralStringOther, nil}, {`\\.`, LiteralStringOther, nil},
{`'[a-zA-Z]*`, LiteralStringOther, Pop(1)}, {`'[a-zA-Z]*`, LiteralStringOther, Pop(1)},
}, },
}, }
)) }

View File

@ -6,14 +6,18 @@ import (
) )
// Elm lexer. // Elm lexer.
var Elm = internal.Register(MustNewLexer( var Elm = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "Elm", Name: "Elm",
Aliases: []string{"elm"}, Aliases: []string{"elm"},
Filenames: []string{"*.elm"}, Filenames: []string{"*.elm"},
MimeTypes: []string{"text/x-elm"}, MimeTypes: []string{"text/x-elm"},
}, },
Rules{ elmRules,
))
func elmRules() Rules {
return Rules{
"root": { "root": {
{`\{-`, CommentMultiline, Push("comment")}, {`\{-`, CommentMultiline, Push("comment")},
{`--.*`, CommentSingle, nil}, {`--.*`, CommentSingle, nil},
@ -55,5 +59,5 @@ var Elm = internal.Register(MustNewLexer(
{`\|\]`, NameEntity, Pop(1)}, {`\|\]`, NameEntity, Pop(1)},
{`.*\n`, NameEntity, nil}, {`.*\n`, NameEntity, nil},
}, },
}, }
)) }

View File

@ -522,14 +522,24 @@ var (
) )
// EmacsLisp lexer. // EmacsLisp lexer.
var EmacsLisp = internal.Register(TypeRemappingLexer(MustNewLexer( var EmacsLisp = internal.Register(TypeRemappingLexer(MustNewLazyLexer(
&Config{ &Config{
Name: "EmacsLisp", Name: "EmacsLisp",
Aliases: []string{"emacs", "elisp", "emacs-lisp"}, Aliases: []string{"emacs", "elisp", "emacs-lisp"},
Filenames: []string{"*.el"}, Filenames: []string{"*.el"},
MimeTypes: []string{"text/x-elisp", "application/x-elisp"}, MimeTypes: []string{"text/x-elisp", "application/x-elisp"},
}, },
Rules{ emacsLispRules,
), TypeMapping{
{NameVariable, NameFunction, emacsBuiltinFunction},
{NameVariable, NameBuiltin, emacsSpecialForms},
{NameVariable, NameException, emacsErrorKeywords},
{NameVariable, NameBuiltin, append(emacsBuiltinFunctionHighlighted, emacsMacros...)},
{NameVariable, KeywordPseudo, emacsLambdaListKeywords},
}))
func emacsLispRules() Rules {
return Rules{
"root": { "root": {
Default(Push("body")), Default(Push("body")),
}, },
@ -572,11 +582,5 @@ var EmacsLisp = internal.Register(TypeRemappingLexer(MustNewLexer(
{`\\\n`, LiteralString, nil}, {`\\\n`, LiteralString, nil},
{`"`, LiteralString, Pop(1)}, {`"`, LiteralString, Pop(1)},
}, },
}, }
), TypeMapping{ }
{NameVariable, NameFunction, emacsBuiltinFunction},
{NameVariable, NameBuiltin, emacsSpecialForms},
{NameVariable, NameException, emacsErrorKeywords},
{NameVariable, NameBuiltin, append(emacsBuiltinFunctionHighlighted, emacsMacros...)},
{NameVariable, KeywordPseudo, emacsLambdaListKeywords},
}))

View File

@ -6,14 +6,18 @@ import (
) )
// Erlang lexer. // Erlang lexer.
var Erlang = internal.Register(MustNewLexer( var Erlang = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "Erlang", Name: "Erlang",
Aliases: []string{"erlang"}, Aliases: []string{"erlang"},
Filenames: []string{"*.erl", "*.hrl", "*.es", "*.escript"}, Filenames: []string{"*.erl", "*.hrl", "*.es", "*.escript"},
MimeTypes: []string{"text/x-erlang"}, MimeTypes: []string{"text/x-erlang"},
}, },
Rules{ erlangRules,
))
func erlangRules() Rules {
return Rules{
"root": { "root": {
{`\s+`, Text, nil}, {`\s+`, Text, nil},
{`%.*\n`, Comment, nil}, {`%.*\n`, Comment, nil},
@ -62,5 +66,5 @@ var Erlang = internal.Register(MustNewLexer(
{`,`, Punctuation, Pop(1)}, {`,`, Punctuation, Pop(1)},
{`(?=\})`, Punctuation, Pop(1)}, {`(?=\})`, Punctuation, Pop(1)},
}, },
}, }
)) }

View File

@ -6,14 +6,18 @@ import (
) )
// Factor lexer. // Factor lexer.
var Factor = internal.Register(MustNewLexer( var Factor = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "Factor", Name: "Factor",
Aliases: []string{"factor"}, Aliases: []string{"factor"},
Filenames: []string{"*.factor"}, Filenames: []string{"*.factor"},
MimeTypes: []string{"text/x-factor"}, MimeTypes: []string{"text/x-factor"},
}, },
Rules{ factorRules,
))
func factorRules() Rules {
return Rules{
"root": { "root": {
{`#!.*$`, CommentPreproc, nil}, {`#!.*$`, CommentPreproc, nil},
Default(Push("base")), Default(Push("base")),
@ -111,5 +115,5 @@ var Factor = internal.Register(MustNewLexer(
{`;\s`, Keyword, Pop(1)}, {`;\s`, Keyword, Pop(1)},
{`\S+`, NameFunction, nil}, {`\S+`, NameFunction, nil},
}, },
}, }
)) }

View File

@ -6,18 +6,43 @@ import (
) )
// Fish lexer. // Fish lexer.
var Fish = internal.Register(MustNewLexer( var Fish = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "Fish", Name: "Fish",
Aliases: []string{"fish", "fishshell"}, Aliases: []string{"fish", "fishshell"},
Filenames: []string{"*.fish", "*.load"}, Filenames: []string{"*.fish", "*.load"},
MimeTypes: []string{"application/x-fish"}, MimeTypes: []string{"application/x-fish"},
}, },
Rules{ fishRules,
))
func fishRules() Rules {
keywords := []string{
`begin`, `end`, `if`, `else`, `while`, `break`, `for`, `return`, `function`, `block`,
`case`, `continue`, `switch`, `not`, `and`, `or`, `set`, `echo`, `exit`, `pwd`, `true`,
`false`, `cd`, `cdh`, `count`, `test`,
}
keywordsPattern := Words(`\b`, `\b`, keywords...)
builtins := []string{
`alias`, `bg`, `bind`, `breakpoint`, `builtin`, `argparse`, `abbr`, `string`, `command`,
`commandline`, `complete`, `contains`, `dirh`, `dirs`, `disown`, `emit`, `eval`, `exec`,
`fg`, `fish`, `fish_add_path`, `fish_breakpoint_prompt`, `fish_command_not_found`,
`fish_config`, `fish_git_prompt`, `fish_greeting`, `fish_hg_prompt`, `fish_indent`,
`fish_is_root_user`, `fish_key_reader`, `fish_mode_prompt`, `fish_opt`, `fish_pager`,
`fish_prompt`, `fish_right_prompt`, `fish_status_to_signal`, `fish_svn_prompt`,
`fish_title`, `fish_update_completions`, `fish_vcs_prompt`, `fishd`, `funced`,
`funcsave`, `functions`, `help`, `history`, `isatty`, `jobs`, `math`, `mimedb`, `nextd`,
`open`, `prompt_pwd`, `realpath`, `popd`, `prevd`, `psub`, `pushd`, `random`, `read`,
`set_color`, `source`, `status`, `suspend`, `trap`, `type`, `ulimit`, `umask`, `vared`,
`fc`, `getopts`, `hash`, `kill`, `printf`, `time`, `wait`,
}
return Rules{
"root": { "root": {
Include("basic"), Include("basic"),
Include("data"),
Include("interp"), Include("interp"),
Include("data"),
}, },
"interp": { "interp": {
{`\$\(\(`, Keyword, Push("math")}, {`\$\(\(`, Keyword, Push("math")},
@ -25,13 +50,20 @@ var Fish = internal.Register(MustNewLexer(
{`\$#?(\w+|.)`, NameVariable, nil}, {`\$#?(\w+|.)`, NameVariable, nil},
}, },
"basic": { "basic": {
{`\b(begin|end|if|else|while|break|for|in|return|function|block|case|continue|switch|not|and|or|set|echo|exit|pwd|true|false|cd|count|test)(\s*)\b`, ByGroups(Keyword, Text), nil}, {Words(`(?<=(?:^|\A|;|&&|\|\||\||`+keywordsPattern+`)\s*)`, `(?=;?\b)`, keywords...), Keyword, nil},
{`\b(alias|bg|bind|breakpoint|builtin|command|commandline|complete|contains|dirh|dirs|emit|eval|exec|fg|fish|fish_config|fish_indent|fish_pager|fish_prompt|fish_right_prompt|fish_update_completions|fishd|funced|funcsave|functions|help|history|isatty|jobs|math|mimedb|nextd|open|popd|prevd|psub|pushd|random|read|set_color|source|status|trap|type|ulimit|umask|vared|fc|getopts|hash|kill|printf|time|wait)\s*\b(?!\.)`, NameBuiltin, nil}, {`(?<=for\s+\S+\s+)in\b`, Keyword, nil},
{Words(`\b`, `\s*\b(?!\.)`, builtins...), NameBuiltin, nil},
{`#!.*\n`, CommentHashbang, nil},
{`#.*\n`, Comment, nil}, {`#.*\n`, Comment, nil},
{`\\[\w\W]`, LiteralStringEscape, nil}, {`\\[\w\W]`, LiteralStringEscape, nil},
{`(\b\w+)(\s*)(=)`, ByGroups(NameVariable, Text, Operator), nil}, {`(\b\w+)(\s*)(=)`, ByGroups(NameVariable, Text, Operator), nil},
{`[\[\]()=]`, Operator, nil}, {`[\[\]()={}]`, Operator, nil},
{`(?<=\[[^\]]+)\.\.|-(?=[^\[]+\])`, Operator, nil},
{`<<-?\s*(\'?)\\?(\w+)[\w\W]+?\2`, LiteralString, nil}, {`<<-?\s*(\'?)\\?(\w+)[\w\W]+?\2`, LiteralString, nil},
{`(?<=set\s+(?:--?[^\d\W][\w-]*\s+)?)\w+`, NameVariable, nil},
{`(?<=for\s+)\w[\w-]*(?=\s+in)`, NameVariable, nil},
{`(?<=function\s+)\w(?:[^\n])*?(?= *[-\n])`, NameFunction, nil},
{`(?<=(?:^|\b(?:and|or|sudo)\b|;|\|\||&&|\||\(|(?:\b\w+\s*=\S+\s)) *)\w[\w-]*`, NameFunction, nil},
}, },
"data": { "data": {
{`(?s)\$?"(\\\\|\\[0-7]+|\\.|[^"\\$])*"`, LiteralStringDouble, nil}, {`(?s)\$?"(\\\\|\\[0-7]+|\\.|[^"\\$])*"`, LiteralStringDouble, nil},
@ -39,10 +71,11 @@ var Fish = internal.Register(MustNewLexer(
{`(?s)\$'(\\\\|\\[0-7]+|\\.|[^'\\])*'`, LiteralStringSingle, nil}, {`(?s)\$'(\\\\|\\[0-7]+|\\.|[^'\\])*'`, LiteralStringSingle, nil},
{`(?s)'.*?'`, LiteralStringSingle, nil}, {`(?s)'.*?'`, LiteralStringSingle, nil},
{`;`, Punctuation, nil}, {`;`, Punctuation, nil},
{`&|\||\^|<|>`, Operator, nil}, {`&&|\|\||&|\||\^|<|>`, Operator, nil},
{`\s+`, Text, nil}, {`\s+`, Text, nil},
{`\d+(?= |\Z)`, LiteralNumber, nil}, {`\b\d+\b`, LiteralNumber, nil},
{"[^=\\s\\[\\]{}()$\"\\'`\\\\<&|;]+", Text, nil}, {`--?[^\d][\w-]*`, NameAttribute, nil},
{".+?", Text, nil},
}, },
"string": { "string": {
{`"`, LiteralStringDouble, Pop(1)}, {`"`, LiteralStringDouble, Pop(1)},
@ -61,5 +94,5 @@ var Fish = internal.Register(MustNewLexer(
{`\d+`, LiteralNumber, nil}, {`\d+`, LiteralNumber, nil},
Include("root"), Include("root"),
}, },
}, }
)) }

View File

@ -6,7 +6,7 @@ import (
) )
// Forth lexer. // Forth lexer.
var Forth = internal.Register(MustNewLexer( var Forth = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "Forth", Name: "Forth",
Aliases: []string{"forth"}, Aliases: []string{"forth"},
@ -14,7 +14,11 @@ var Forth = internal.Register(MustNewLexer(
MimeTypes: []string{"application/x-forth"}, MimeTypes: []string{"application/x-forth"},
CaseInsensitive: true, CaseInsensitive: true,
}, },
Rules{ forthRules,
))
func forthRules() Rules {
return Rules{
"root": { "root": {
{`\s+`, Text, nil}, {`\s+`, Text, nil},
{`\\.*?\n`, CommentSingle, nil}, {`\\.*?\n`, CommentSingle, nil},
@ -36,5 +40,5 @@ var Forth = internal.Register(MustNewLexer(
"stringdef": { "stringdef": {
{`[^"]+`, LiteralString, Pop(1)}, {`[^"]+`, LiteralString, Pop(1)},
}, },
}, }
)) }

View File

@ -6,7 +6,7 @@ import (
) )
// Fortran lexer. // Fortran lexer.
var Fortran = internal.Register(MustNewLexer( var Fortran = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "Fortran", Name: "Fortran",
Aliases: []string{"fortran"}, Aliases: []string{"fortran"},
@ -14,7 +14,11 @@ var Fortran = internal.Register(MustNewLexer(
MimeTypes: []string{"text/x-fortran"}, MimeTypes: []string{"text/x-fortran"},
CaseInsensitive: true, CaseInsensitive: true,
}, },
Rules{ fortranRules,
))
func fortranRules() Rules {
return Rules{
"root": { "root": {
{`^#.*\n`, CommentPreproc, nil}, {`^#.*\n`, CommentPreproc, nil},
{`!.*\n`, Comment, nil}, {`!.*\n`, Comment, nil},
@ -43,5 +47,5 @@ var Fortran = internal.Register(MustNewLexer(
{`[+-]?\d*\.\d+([ed][-+]?\d+)?(_[a-z]\w+)?`, LiteralNumberFloat, nil}, {`[+-]?\d*\.\d+([ed][-+]?\d+)?(_[a-z]\w+)?`, LiteralNumberFloat, nil},
{`[+-]?\d+\.\d*([ed][-+]?\d+)?(_[a-z]\w+)?`, LiteralNumberFloat, nil}, {`[+-]?\d+\.\d*([ed][-+]?\d+)?(_[a-z]\w+)?`, LiteralNumberFloat, nil},
}, },
}, }
)) }

View File

@ -0,0 +1,39 @@
package f
import (
. "github.com/alecthomas/chroma" // nolint
"github.com/alecthomas/chroma/lexers/internal"
)
// FortranFixed lexer.
var FortranFixed = internal.Register(MustNewLazyLexer(
&Config{
Name: "FortranFixed",
Aliases: []string{"fortranfixed"},
Filenames: []string{"*.f", "*.F"},
MimeTypes: []string{"text/x-fortran"},
NotMultiline: true,
CaseInsensitive: true,
},
func() Rules {
return Rules{
"root": {
{`[C*].*\n`, Comment, nil},
{`#.*\n`, CommentPreproc, nil},
{`[\t ]*!.*\n`, Comment, nil},
{`(.{5})`, NameLabel, Push("cont-char")},
{`.*\n`, Using(Fortran), nil},
},
"cont-char": {
{` `, Text, Push("code")},
{`0`, Comment, Push("code")},
{`.`, GenericStrong, Push("code")},
},
"code": {
{`(.{66})(.*)(\n)`, ByGroups(Using(Fortran), Comment, Text), Push("root")},
{`.*\n`, Using(Fortran), Push("root")},
Default(Push("root")),
},
}
},
))

View File

@ -6,14 +6,18 @@ import (
) )
// Fsharp lexer. // Fsharp lexer.
var Fsharp = internal.Register(MustNewLexer( var Fsharp = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "FSharp", Name: "FSharp",
Aliases: []string{"fsharp"}, Aliases: []string{"fsharp"},
Filenames: []string{"*.fs", "*.fsi"}, Filenames: []string{"*.fs", "*.fsi"},
MimeTypes: []string{"text/x-fsharp"}, MimeTypes: []string{"text/x-fsharp"},
}, },
Rules{ fsharpRules,
))
func fsharpRules() Rules {
return Rules{
"escape-sequence": { "escape-sequence": {
{`\\[\\"\'ntbrafv]`, LiteralStringEscape, nil}, {`\\[\\"\'ntbrafv]`, LiteralStringEscape, nil},
{`\\[0-9]{3}`, LiteralStringEscape, nil}, {`\\[0-9]{3}`, LiteralStringEscape, nil},
@ -90,5 +94,5 @@ var Fsharp = internal.Register(MustNewLexer(
{`"""B?`, LiteralString, Pop(1)}, {`"""B?`, LiteralString, Pop(1)},
{`"`, LiteralString, nil}, {`"`, LiteralString, nil},
}, },
}, }
)) }

View File

@ -6,14 +6,18 @@ import (
) )
// Gas lexer. // Gas lexer.
var Gas = internal.Register(MustNewLexer( var Gas = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "GAS", Name: "GAS",
Aliases: []string{"gas", "asm"}, Aliases: []string{"gas", "asm"},
Filenames: []string{"*.s", "*.S"}, Filenames: []string{"*.s", "*.S"},
MimeTypes: []string{"text/x-gas"}, MimeTypes: []string{"text/x-gas"},
}, },
Rules{ gasRules,
))
func gasRules() Rules {
return Rules{
"root": { "root": {
Include("whitespace"), Include("whitespace"),
{`(?:[a-zA-Z$_][\w$.@-]*|\.[\w$.@-]+):`, NameLabel, nil}, {`(?:[a-zA-Z$_][\w$.@-]*|\.[\w$.@-]+):`, NameLabel, nil},
@ -51,5 +55,5 @@ var Gas = internal.Register(MustNewLexer(
"punctuation": { "punctuation": {
{`[-*,.()\[\]!:]+`, Punctuation, nil}, {`[-*,.()\[\]!:]+`, Punctuation, nil},
}, },
}, }
)) }

View File

@ -6,14 +6,18 @@ import (
) )
// GDScript lexer. // GDScript lexer.
var GDScript = internal.Register(MustNewLexer( var GDScript = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "GDScript", Name: "GDScript",
Aliases: []string{"gdscript", "gd"}, Aliases: []string{"gdscript", "gd"},
Filenames: []string{"*.gd"}, Filenames: []string{"*.gd"},
MimeTypes: []string{"text/x-gdscript", "application/x-gdscript"}, MimeTypes: []string{"text/x-gdscript", "application/x-gdscript"},
}, },
Rules{ gdscriptRules,
))
func gdscriptRules() Rules {
return Rules{
"root": { "root": {
{`\n`, Text, nil}, {`\n`, Text, nil},
{`^(\s*)([rRuUbB]{,2})("""(?:.|\n)*?""")`, ByGroups(Text, LiteralStringAffix, LiteralStringDoc), nil}, {`^(\s*)([rRuUbB]{,2})("""(?:.|\n)*?""")`, ByGroups(Text, LiteralStringAffix, LiteralStringDoc), nil},
@ -120,5 +124,5 @@ var GDScript = internal.Register(MustNewLexer(
Include("strings-single"), Include("strings-single"),
{`\n`, LiteralStringSingle, nil}, {`\n`, LiteralStringSingle, nil},
}, },
}, }
)) }

View File

@ -7,14 +7,18 @@ import (
) )
// Genshi Text lexer. // Genshi Text lexer.
var GenshiText = internal.Register(MustNewLexer( var GenshiText = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "Genshi Text", Name: "Genshi Text",
Aliases: []string{"genshitext"}, Aliases: []string{"genshitext"},
Filenames: []string{}, Filenames: []string{},
MimeTypes: []string{"application/x-genshi-text", "text/x-genshi"}, MimeTypes: []string{"application/x-genshi-text", "text/x-genshi"},
}, },
Rules{ genshiTextRules,
))
func genshiTextRules() Rules {
return Rules{
"root": { "root": {
{`[^#$\s]+`, Other, nil}, {`[^#$\s]+`, Other, nil},
{`^(\s*)(##.*)$`, ByGroups(Text, Comment), nil}, {`^(\s*)(##.*)$`, ByGroups(Text, Comment), nil},
@ -33,11 +37,11 @@ var GenshiText = internal.Register(MustNewLexer(
{`(?<!\$)(\$\{)(.+?)(\})`, ByGroups(CommentPreproc, Using(Python), CommentPreproc), nil}, {`(?<!\$)(\$\{)(.+?)(\})`, ByGroups(CommentPreproc, Using(Python), CommentPreproc), nil},
{`(?<!\$)(\$)([a-zA-Z_][\w.]*)`, NameVariable, nil}, {`(?<!\$)(\$)([a-zA-Z_][\w.]*)`, NameVariable, nil},
}, },
}, }
)) }
// Html+Genshi lexer. // Html+Genshi lexer.
var GenshiHTMLTemplate = internal.Register(MustNewLexer( var GenshiHTMLTemplate = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "Genshi HTML", Name: "Genshi HTML",
Aliases: []string{"html+genshi", "html+kid"}, Aliases: []string{"html+genshi", "html+kid"},
@ -50,7 +54,7 @@ var GenshiHTMLTemplate = internal.Register(MustNewLexer(
)) ))
// Genshi lexer. // Genshi lexer.
var Genshi = internal.Register(MustNewLexer( var Genshi = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "Genshi", Name: "Genshi",
Aliases: []string{"genshi", "kid", "xml+genshi", "xml+kid"}, Aliases: []string{"genshi", "kid", "xml+genshi", "xml+kid"},
@ -62,53 +66,55 @@ var Genshi = internal.Register(MustNewLexer(
genshiMarkupRules, genshiMarkupRules,
)) ))
var genshiMarkupRules = Rules{ func genshiMarkupRules() Rules {
"root": { return Rules{
{`[^<$]+`, Other, nil}, "root": {
{`(<\?python)(.*?)(\?>)`, ByGroups(CommentPreproc, Using(Python), CommentPreproc), nil}, {`[^<$]+`, Other, nil},
{`<\s*(script|style)\s*.*?>.*?<\s*/\1\s*>`, Other, nil}, {`(<\?python)(.*?)(\?>)`, ByGroups(CommentPreproc, Using(Python), CommentPreproc), nil},
{`<\s*py:[a-zA-Z0-9]+`, NameTag, Push("pytag")}, {`<\s*(script|style)\s*.*?>.*?<\s*/\1\s*>`, Other, nil},
{`<\s*[a-zA-Z0-9:.]+`, NameTag, Push("tag")}, {`<\s*py:[a-zA-Z0-9]+`, NameTag, Push("pytag")},
Include("variable"), {`<\s*[a-zA-Z0-9:.]+`, NameTag, Push("tag")},
{`[<$]`, Other, nil}, Include("variable"),
}, {`[<$]`, Other, nil},
"pytag": { },
{`\s+`, Text, nil}, "pytag": {
{`[\w:-]+\s*=`, NameAttribute, Push("pyattr")}, {`\s+`, Text, nil},
{`/?\s*>`, NameTag, Pop(1)}, {`[\w:-]+\s*=`, NameAttribute, Push("pyattr")},
}, {`/?\s*>`, NameTag, Pop(1)},
"pyattr": { },
{`(")(.*?)(")`, ByGroups(LiteralString, Using(Python), LiteralString), Pop(1)}, "pyattr": {
{`(')(.*?)(')`, ByGroups(LiteralString, Using(Python), LiteralString), Pop(1)}, {`(")(.*?)(")`, ByGroups(LiteralString, Using(Python), LiteralString), Pop(1)},
{`[^\s>]+`, LiteralString, Pop(1)}, {`(')(.*?)(')`, ByGroups(LiteralString, Using(Python), LiteralString), Pop(1)},
}, {`[^\s>]+`, LiteralString, Pop(1)},
"tag": { },
{`\s+`, Text, nil}, "tag": {
{`py:[\w-]+\s*=`, NameAttribute, Push("pyattr")}, {`\s+`, Text, nil},
{`[\w:-]+\s*=`, NameAttribute, Push("attr")}, {`py:[\w-]+\s*=`, NameAttribute, Push("pyattr")},
{`/?\s*>`, NameTag, Pop(1)}, {`[\w:-]+\s*=`, NameAttribute, Push("attr")},
}, {`/?\s*>`, NameTag, Pop(1)},
"attr": { },
{`"`, LiteralString, Push("attr-dstring")}, "attr": {
{`'`, LiteralString, Push("attr-sstring")}, {`"`, LiteralString, Push("attr-dstring")},
{`[^\s>]*`, LiteralString, Pop(1)}, {`'`, LiteralString, Push("attr-sstring")},
}, {`[^\s>]*`, LiteralString, Pop(1)},
"attr-dstring": { },
{`"`, LiteralString, Pop(1)}, "attr-dstring": {
Include("strings"), {`"`, LiteralString, Pop(1)},
{`'`, LiteralString, nil}, Include("strings"),
}, {`'`, LiteralString, nil},
"attr-sstring": { },
{`'`, LiteralString, Pop(1)}, "attr-sstring": {
Include("strings"), {`'`, LiteralString, Pop(1)},
{`'`, LiteralString, nil}, Include("strings"),
}, {`'`, LiteralString, nil},
"strings": { },
{`[^"'$]+`, LiteralString, nil}, "strings": {
Include("variable"), {`[^"'$]+`, LiteralString, nil},
}, Include("variable"),
"variable": { },
{`(?<!\$)(\$\{)(.+?)(\})`, ByGroups(CommentPreproc, Using(Python), CommentPreproc), nil}, "variable": {
{`(?<!\$)(\$)([a-zA-Z_][\w\.]*)`, NameVariable, nil}, {`(?<!\$)(\$\{)(.+?)(\})`, ByGroups(CommentPreproc, Using(Python), CommentPreproc), nil},
}, {`(?<!\$)(\$)([a-zA-Z_][\w\.]*)`, NameVariable, nil},
},
}
} }

View File

@ -5,23 +5,27 @@ import (
"github.com/alecthomas/chroma/lexers/internal" "github.com/alecthomas/chroma/lexers/internal"
) )
var stepKeywords = `^(\s*)(하지만|조건|먼저|만일|만약|단|그리고|그러면|那麼|那么|而且|當|当|前提|假設|假设|假如|假定|但是|但し|並且|并且|同時|同时|もし|ならば|ただし|しかし|かつ|و |متى |لكن |عندما |ثم |بفرض |اذاً |כאשר |וגם |בהינתן |אזי |אז |אבל |Якщо |Унда |То |Припустимо, що |Припустимо |Онда |Но |Нехай |Лекин |Когато |Када |Кад |К тому же |И |Задато |Задати |Задате |Если |Допустим |Дадено |Ва |Бирок |Аммо |Али |Але |Агар |А |І |Și |És |Zatati |Zakładając |Zadato |Zadate |Zadano |Zadani |Zadan |Youse know when youse got |Youse know like when |Yna |Ya know how |Ya gotta |Y |Wun |Wtedy |When y'all |When |Wenn |WEN |Và |Ve |Und |Un |Thì |Then y'all |Then |Tapi |Tak |Tada |Tad |Så |Stel |Soit |Siis |Si |Sed |Se |Quando |Quand |Quan |Pryd |Pokud |Pokiaľ |Però |Pero |Pak |Oraz |Onda |Ond |Oletetaan |Og |Och |O zaman |Når |När |Niin |Nhưng |N |Mutta |Men |Mas |Maka |Majd |Mais |Maar |Ma |Lorsque |Lorsqu'|Kun |Kuid |Kui |Khi |Keď |Ketika |Když |Kaj |Kai |Kada |Kad |Jeżeli |Ja |Ir |I CAN HAZ |I |Ha |Givun |Givet |Given y'all |Given |Gitt |Gegeven |Gegeben sei |Fakat |Eğer ki |Etant donné |Et |Então |Entonces |Entao |En |Eeldades |E |Duota |Dun |Donitaĵo |Donat |Donada |Do |Diyelim ki |Dengan |Den youse gotta |De |Dato |Dar |Dann |Dan |Dado |Dacă |Daca |DEN |Când |Cuando |Cho |Cept |Cand |Cal |But y'all |But |Buh |Biết |Bet |BUT |Atès |Atunci |Atesa |Anrhegedig a |Angenommen |And y'all |And |An |Ama |Als |Alors |Allora |Ali |Aleshores |Ale |Akkor |Aber |AN |A také |A |\* )`
var featureKeywords = `^(기능|機能|功能|フィーチャ|خاصية|תכונה|Функціонал|Функционалност|Функционал|Фича|Особина|Могућност|Özellik|Właściwość|Tính năng|Trajto|Savybė|Požiadavka|Požadavek|Osobina|Ominaisuus|Omadus|OH HAI|Mogućnost|Mogucnost|Jellemző|Fīča|Funzionalità|Funktionalität|Funkcionalnost|Funkcionalitāte|Funcționalitate|Functionaliteit|Functionalitate|Funcionalitat|Funcionalidade|Fonctionnalité|Fitur|Feature|Egenskap|Egenskab|Crikey|Característica|Arwedd)(:)(.*)$`
var featureElementKeywords = `^(\s*)(시나리오 개요|시나리오|배경|背景|場景大綱|場景|场景大纲|场景|劇本大綱|劇本|剧本大纲|剧本|テンプレ|シナリオテンプレート|シナリオテンプレ|シナリオアウトライン|シナリオ|سيناريو مخطط|سيناريو|الخلفية|תרחיש|תבנית תרחיש|רקע|Тарих|Сценарій|Сценарио|Сценарий структураси|Сценарий|Структура сценарію|Структура сценарија|Структура сценария|Скица|Рамка на сценарий|Пример|Предыстория|Предистория|Позадина|Передумова|Основа|Концепт|Контекст|Założenia|Wharrimean is|Tình huống|The thing of it is|Tausta|Taust|Tapausaihio|Tapaus|Szenariogrundriss|Szenario|Szablon scenariusza|Stsenaarium|Struktura scenarija|Skica|Skenario konsep|Skenario|Situācija|Senaryo taslağı|Senaryo|Scénář|Scénario|Schema dello scenario|Scenārijs pēc parauga|Scenārijs|Scenár|Scenaro|Scenariusz|Scenariul de şablon|Scenariul de sablon|Scenariu|Scenario Outline|Scenario Amlinellol|Scenario|Scenarijus|Scenarijaus šablonas|Scenarij|Scenarie|Rerefons|Raamstsenaarium|Primer|Pozadí|Pozadina|Pozadie|Plan du scénario|Plan du Scénario|Osnova scénáře|Osnova|Náčrt Scénáře|Náčrt Scenáru|Mate|MISHUN SRSLY|MISHUN|Kịch bản|Konturo de la scenaro|Kontext|Konteksts|Kontekstas|Kontekst|Koncept|Khung tình huống|Khung kịch bản|Háttér|Grundlage|Geçmiş|Forgatókönyv vázlat|Forgatókönyv|Fono|Esquema do Cenário|Esquema do Cenario|Esquema del escenario|Esquema de l'escenari|Escenario|Escenari|Dis is what went down|Dasar|Contexto|Contexte|Contesto|Condiţii|Conditii|Cenário|Cenario|Cefndir|Bối cảnh|Blokes|Bakgrunn|Bakgrund|Baggrund|Background|B4|Antecedents|Antecedentes|All y'all|Achtergrond|Abstrakt Scenario|Abstract Scenario)(:)(.*)$`
var examplesKeywords = `^(\s*)(예|例子|例|サンプル|امثلة|דוגמאות|Сценарији|Примери|Приклади|Мисоллар|Значения|Örnekler|Voorbeelden|Variantai|Tapaukset|Scenarios|Scenariji|Scenarijai|Příklady|Példák|Príklady|Przykłady|Primjeri|Primeri|Piemēri|Pavyzdžiai|Paraugs|Juhtumid|Exemplos|Exemples|Exemplele|Exempel|Examples|Esempi|Enghreifftiau|Ekzemploj|Eksempler|Ejemplos|EXAMPLZ|Dữ liệu|Contoh|Cobber|Beispiele)(:)(.*)$`
// Gherkin lexer. // Gherkin lexer.
var Gherkin = internal.Register(MustNewLexer( var Gherkin = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "Gherkin", Name: "Gherkin",
Aliases: []string{"cucumber", "Cucumber", "gherkin", "Gherkin"}, Aliases: []string{"cucumber", "Cucumber", "gherkin", "Gherkin"},
Filenames: []string{"*.feature", "*.FEATURE"}, Filenames: []string{"*.feature", "*.FEATURE"},
MimeTypes: []string{"text/x-gherkin"}, MimeTypes: []string{"text/x-gherkin"},
}, },
Rules{ gherkinRules,
))
func gherkinRules() Rules {
stepKeywords := `^(\s*)(하지만|조건|먼저|만일|만약|단|그리고|그러면|那麼|那么|而且|當|当|前提|假設|假设|假如|假定|但是|但し|並且|并且|同時|同时|もし|ならば|ただし|しかし|かつ|و |متى |لكن |عندما |ثم |بفرض |اذاً |כאשר |וגם |בהינתן |אזי |אז |אבל |Якщо |Унда |То |Припустимо, що |Припустимо |Онда |Но |Нехай |Лекин |Когато |Када |Кад |К тому же |И |Задато |Задати |Задате |Если |Допустим |Дадено |Ва |Бирок |Аммо |Али |Але |Агар |А |І |Și |És |Zatati |Zakładając |Zadato |Zadate |Zadano |Zadani |Zadan |Youse know when youse got |Youse know like when |Yna |Ya know how |Ya gotta |Y |Wun |Wtedy |When y'all |When |Wenn |WEN |Và |Ve |Und |Un |Thì |Then y'all |Then |Tapi |Tak |Tada |Tad |Så |Stel |Soit |Siis |Si |Sed |Se |Quando |Quand |Quan |Pryd |Pokud |Pokiaľ |Però |Pero |Pak |Oraz |Onda |Ond |Oletetaan |Og |Och |O zaman |Når |När |Niin |Nhưng |N |Mutta |Men |Mas |Maka |Majd |Mais |Maar |Ma |Lorsque |Lorsqu'|Kun |Kuid |Kui |Khi |Keď |Ketika |Když |Kaj |Kai |Kada |Kad |Jeżeli |Ja |Ir |I CAN HAZ |I |Ha |Givun |Givet |Given y'all |Given |Gitt |Gegeven |Gegeben sei |Fakat |Eğer ki |Etant donné |Et |Então |Entonces |Entao |En |Eeldades |E |Duota |Dun |Donitaĵo |Donat |Donada |Do |Diyelim ki |Dengan |Den youse gotta |De |Dato |Dar |Dann |Dan |Dado |Dacă |Daca |DEN |Când |Cuando |Cho |Cept |Cand |Cal |But y'all |But |Buh |Biết |Bet |BUT |Atès |Atunci |Atesa |Anrhegedig a |Angenommen |And y'all |And |An |Ama |Als |Alors |Allora |Ali |Aleshores |Ale |Akkor |Aber |AN |A také |A |\* )`
featureKeywords := `^(기능|機能|功能|フィーチャ|خاصية|תכונה|Функціонал|Функционалност|Функционал|Фича|Особина|Могућност|Özellik|Właściwość|Tính năng|Trajto|Savybė|Požiadavka|Požadavek|Osobina|Ominaisuus|Omadus|OH HAI|Mogućnost|Mogucnost|Jellemző|Fīča|Funzionalità|Funktionalität|Funkcionalnost|Funkcionalitāte|Funcționalitate|Functionaliteit|Functionalitate|Funcionalitat|Funcionalidade|Fonctionnalité|Fitur|Feature|Egenskap|Egenskab|Crikey|Característica|Arwedd)(:)(.*)$`
featureElementKeywords := `^(\s*)(시나리오 개요|시나리오|배경|背景|場景大綱|場景|场景大纲|场景|劇本大綱|劇本|剧本大纲|剧本|テンプレ|シナリオテンプレート|シナリオテンプレ|シナリオアウトライン|シナリオ|سيناريو مخطط|سيناريو|الخلفية|תרחיש|תבנית תרחיש|רקע|Тарих|Сценарій|Сценарио|Сценарий структураси|Сценарий|Структура сценарію|Структура сценарија|Структура сценария|Скица|Рамка на сценарий|Пример|Предыстория|Предистория|Позадина|Передумова|Основа|Концепт|Контекст|Założenia|Wharrimean is|Tình huống|The thing of it is|Tausta|Taust|Tapausaihio|Tapaus|Szenariogrundriss|Szenario|Szablon scenariusza|Stsenaarium|Struktura scenarija|Skica|Skenario konsep|Skenario|Situācija|Senaryo taslağı|Senaryo|Scénář|Scénario|Schema dello scenario|Scenārijs pēc parauga|Scenārijs|Scenár|Scenaro|Scenariusz|Scenariul de şablon|Scenariul de sablon|Scenariu|Scenario Outline|Scenario Amlinellol|Scenario|Scenarijus|Scenarijaus šablonas|Scenarij|Scenarie|Rerefons|Raamstsenaarium|Primer|Pozadí|Pozadina|Pozadie|Plan du scénario|Plan du Scénario|Osnova scénáře|Osnova|Náčrt Scénáře|Náčrt Scenáru|Mate|MISHUN SRSLY|MISHUN|Kịch bản|Konturo de la scenaro|Kontext|Konteksts|Kontekstas|Kontekst|Koncept|Khung tình huống|Khung kịch bản|Háttér|Grundlage|Geçmiş|Forgatókönyv vázlat|Forgatókönyv|Fono|Esquema do Cenário|Esquema do Cenario|Esquema del escenario|Esquema de l'escenari|Escenario|Escenari|Dis is what went down|Dasar|Contexto|Contexte|Contesto|Condiţii|Conditii|Cenário|Cenario|Cefndir|Bối cảnh|Blokes|Bakgrunn|Bakgrund|Baggrund|Background|B4|Antecedents|Antecedentes|All y'all|Achtergrond|Abstrakt Scenario|Abstract Scenario)(:)(.*)$`
examplesKeywords := `^(\s*)(예|例子|例|サンプル|امثلة|דוגמאות|Сценарији|Примери|Приклади|Мисоллар|Значения|Örnekler|Voorbeelden|Variantai|Tapaukset|Scenarios|Scenariji|Scenarijai|Příklady|Példák|Príklady|Przykłady|Primjeri|Primeri|Piemēri|Pavyzdžiai|Paraugs|Juhtumid|Exemplos|Exemples|Exemplele|Exempel|Examples|Esempi|Enghreifftiau|Ekzemploj|Eksempler|Ejemplos|EXAMPLZ|Dữ liệu|Contoh|Cobber|Beispiele)(:)(.*)$`
return Rules{
"comments": { "comments": {
{`\s*#.*$`, Comment, nil}, {`\s*#.*$`, Comment, nil},
}, },
@ -114,5 +118,5 @@ var Gherkin = internal.Register(MustNewLexer(
{examplesKeywords, ByGroups(NameFunction, Keyword, Keyword, NameFunction), Push("examplesTable")}, {examplesKeywords, ByGroups(NameFunction, Keyword, Keyword, NameFunction), Push("examplesTable")},
{`(\s|.)`, NameFunction, nil}, {`(\s|.)`, NameFunction, nil},
}, },
}, }
)) }

View File

@ -6,14 +6,18 @@ import (
) )
// GLSL lexer. // GLSL lexer.
var GLSL = internal.Register(MustNewLexer( var GLSL = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "GLSL", Name: "GLSL",
Aliases: []string{"glsl"}, Aliases: []string{"glsl"},
Filenames: []string{"*.vert", "*.frag", "*.geo"}, Filenames: []string{"*.vert", "*.frag", "*.geo"},
MimeTypes: []string{"text/x-glslsrc"}, MimeTypes: []string{"text/x-glslsrc"},
}, },
Rules{ glslRules,
))
func glslRules() Rules {
return Rules{
"root": { "root": {
{`^#.*`, CommentPreproc, nil}, {`^#.*`, CommentPreproc, nil},
{`//.*`, CommentSingle, nil}, {`//.*`, CommentSingle, nil},
@ -33,5 +37,5 @@ var GLSL = internal.Register(MustNewLexer(
{`\.`, Punctuation, nil}, {`\.`, Punctuation, nil},
{`\s+`, Text, nil}, {`\s+`, Text, nil},
}, },
}, }
)) }

View File

@ -6,14 +6,18 @@ import (
) )
// Gnuplot lexer. // Gnuplot lexer.
var Gnuplot = internal.Register(MustNewLexer( var Gnuplot = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "Gnuplot", Name: "Gnuplot",
Aliases: []string{"gnuplot"}, Aliases: []string{"gnuplot"},
Filenames: []string{"*.plot", "*.plt"}, Filenames: []string{"*.plot", "*.plt"},
MimeTypes: []string{"text/x-gnuplot"}, MimeTypes: []string{"text/x-gnuplot"},
}, },
Rules{ gnuplotRules,
))
func gnuplotRules() Rules {
return Rules{
"root": { "root": {
Include("whitespace"), Include("whitespace"),
{`bind\b|bin\b|bi\b`, Keyword, Push("bind")}, {`bind\b|bin\b|bi\b`, Keyword, Push("bind")},
@ -113,5 +117,5 @@ var Gnuplot = internal.Register(MustNewLexer(
{`functions\b|function\b|functio\b|functi\b|funct\b|func\b|fun\b|fu\b|f\b|set\b|se\b|s\b|terminal\b|termina\b|termin\b|termi\b|term\b|ter\b|te\b|t\b|variables\b|variable\b|variabl\b|variab\b|varia\b|vari\b|var\b|va\b|v\b`, NameBuiltin, nil}, {`functions\b|function\b|functio\b|functi\b|funct\b|func\b|fun\b|fu\b|f\b|set\b|se\b|s\b|terminal\b|termina\b|termin\b|termi\b|term\b|ter\b|te\b|t\b|variables\b|variable\b|variabl\b|variab\b|varia\b|vari\b|var\b|va\b|v\b`, NameBuiltin, nil},
Include("genericargs"), Include("genericargs"),
}, },
}, }
)) }

View File

@ -9,7 +9,7 @@ import (
) )
// Go lexer. // Go lexer.
var Go = internal.Register(MustNewLexer( var Go = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "Go", Name: "Go",
Aliases: []string{"go", "golang"}, Aliases: []string{"go", "golang"},
@ -17,7 +17,19 @@ var Go = internal.Register(MustNewLexer(
MimeTypes: []string{"text/x-gosrc"}, MimeTypes: []string{"text/x-gosrc"},
EnsureNL: true, EnsureNL: true,
}, },
Rules{ goRules,
).SetAnalyser(func(text string) float32 {
if strings.Contains(text, "fmt.") && strings.Contains(text, "package ") {
return 0.5
}
if strings.Contains(text, "package ") {
return 0.1
}
return 0.0
}))
func goRules() Rules {
return Rules{
"root": { "root": {
{`\n`, Text, nil}, {`\n`, Text, nil},
{`\s+`, Text, nil}, {`\s+`, Text, nil},
@ -37,8 +49,8 @@ var Go = internal.Register(MustNewLexer(
{`\d+(\.\d+[eE][+\-]?\d+|\.\d*|[eE][+\-]?\d+)`, LiteralNumberFloat, nil}, {`\d+(\.\d+[eE][+\-]?\d+|\.\d*|[eE][+\-]?\d+)`, LiteralNumberFloat, nil},
{`\.\d+([eE][+\-]?\d+)?`, LiteralNumberFloat, nil}, {`\.\d+([eE][+\-]?\d+)?`, LiteralNumberFloat, nil},
{`0[0-7]+`, LiteralNumberOct, nil}, {`0[0-7]+`, LiteralNumberOct, nil},
{`0[xX][0-9a-fA-F]+`, LiteralNumberHex, nil}, {`0[xX][0-9a-fA-F_]+`, LiteralNumberHex, nil},
{`(0|[1-9][0-9]*)`, LiteralNumberInteger, nil}, {`(0|[1-9][0-9_]*)`, LiteralNumberInteger, nil},
{`'(\\['"\\abfnrtv]|\\x[0-9a-fA-F]{2}|\\[0-7]{1,3}|\\u[0-9a-fA-F]{4}|\\U[0-9a-fA-F]{8}|[^\\])'`, LiteralStringChar, nil}, {`'(\\['"\\abfnrtv]|\\x[0-9a-fA-F]{2}|\\[0-7]{1,3}|\\u[0-9a-fA-F]{4}|\\U[0-9a-fA-F]{8}|[^\\])'`, LiteralStringChar, nil},
{"(`)([^`]*)(`)", ByGroups(LiteralString, Using(TypeRemappingLexer(GoTextTemplate, TypeMapping{{Other, LiteralString, nil}})), LiteralString), nil}, {"(`)([^`]*)(`)", ByGroups(LiteralString, Using(TypeRemappingLexer(GoTextTemplate, TypeMapping{{Other, LiteralString, nil}})), LiteralString), nil},
{`"(\\\\|\\"|[^"])*"`, LiteralString, nil}, {`"(\\\\|\\"|[^"])*"`, LiteralString, nil},
@ -47,58 +59,52 @@ var Go = internal.Register(MustNewLexer(
{`[|^<>=!()\[\]{}.,;:]`, Punctuation, nil}, {`[|^<>=!()\[\]{}.,;:]`, Punctuation, nil},
{`[^\W\d]\w*`, NameOther, nil}, {`[^\W\d]\w*`, NameOther, nil},
}, },
},
).SetAnalyser(func(text string) float32 {
if strings.Contains(text, "fmt.") && strings.Contains(text, "package ") {
return 0.5
} }
if strings.Contains(text, "package ") {
return 0.1
}
return 0.0
}))
var goTemplateRules = Rules{
"root": {
{`{{[-]?`, CommentPreproc, Push("template")},
{`[^{]+`, Other, nil},
{`{`, Other, nil},
},
"template": {
{`[-]?}}`, CommentPreproc, Pop(1)},
{`/\*.*?\*/`, Comment, nil},
{`(?=}})`, CommentPreproc, Pop(1)}, // Terminate the pipeline
{`\(`, Operator, Push("subexpression")},
{`"(\\\\|\\"|[^"])*"`, LiteralString, nil},
Include("expression"),
},
"subexpression": {
{`\)`, Operator, Pop(1)},
Include("expression"),
},
"expression": {
{`\s+`, Whitespace, nil},
{`\(`, Operator, Push("subexpression")},
{`(range|if|else|while|with|template|end|true|false|nil|and|call|html|index|js|len|not|or|print|printf|println|urlquery|eq|ne|lt|le|gt|ge)\b`, Keyword, nil},
{`\||:=`, Operator, nil},
{`[$]?[^\W\d]\w*`, NameOther, nil},
{`[$]?\.(?:[^\W\d]\w*)?`, NameAttribute, nil},
{`"(\\\\|\\"|[^"])*"`, LiteralString, nil},
{`\d+i`, LiteralNumber, nil},
{`\d+\.\d*([Ee][-+]\d+)?i`, LiteralNumber, nil},
{`\.\d+([Ee][-+]\d+)?i`, LiteralNumber, nil},
{`\d+[Ee][-+]\d+i`, LiteralNumber, nil},
{`\d+(\.\d+[eE][+\-]?\d+|\.\d*|[eE][+\-]?\d+)`, LiteralNumberFloat, nil},
{`\.\d+([eE][+\-]?\d+)?`, LiteralNumberFloat, nil},
{`0[0-7]+`, LiteralNumberOct, nil},
{`0[xX][0-9a-fA-F]+`, LiteralNumberHex, nil},
{`(0|[1-9][0-9]*)`, LiteralNumberInteger, nil},
{`'(\\['"\\abfnrtv]|\\x[0-9a-fA-F]{2}|\\[0-7]{1,3}|\\u[0-9a-fA-F]{4}|\\U[0-9a-fA-F]{8}|[^\\])'`, LiteralStringChar, nil},
{"`[^`]*`", LiteralString, nil},
},
} }
var GoHTMLTemplate = internal.Register(DelegatingLexer(h.HTML, MustNewLexer( func goTemplateRules() Rules {
return Rules{
"root": {
{`{{(- )?/\*(.|\n)*?\*/( -)?}}`, CommentMultiline, nil},
{`{{[-]?`, CommentPreproc, Push("template")},
{`[^{]+`, Other, nil},
{`{`, Other, nil},
},
"template": {
{`[-]?}}`, CommentPreproc, Pop(1)},
{`(?=}})`, CommentPreproc, Pop(1)}, // Terminate the pipeline
{`\(`, Operator, Push("subexpression")},
{`"(\\\\|\\"|[^"])*"`, LiteralString, nil},
Include("expression"),
},
"subexpression": {
{`\)`, Operator, Pop(1)},
Include("expression"),
},
"expression": {
{`\s+`, Whitespace, nil},
{`\(`, Operator, Push("subexpression")},
{`(range|if|else|while|with|template|end|true|false|nil|and|call|html|index|js|len|not|or|print|printf|println|urlquery|eq|ne|lt|le|gt|ge)\b`, Keyword, nil},
{`\||:?=|,`, Operator, nil},
{`[$]?[^\W\d]\w*`, NameOther, nil},
{`\$|[$]?\.(?:[^\W\d]\w*)?`, NameAttribute, nil},
{`"(\\\\|\\"|[^"])*"`, LiteralString, nil},
{`-?\d+i`, LiteralNumber, nil},
{`-?\d+\.\d*([Ee][-+]\d+)?i`, LiteralNumber, nil},
{`\.\d+([Ee][-+]\d+)?i`, LiteralNumber, nil},
{`-?\d+[Ee][-+]\d+i`, LiteralNumber, nil},
{`-?\d+(\.\d+[eE][+\-]?\d+|\.\d*|[eE][+\-]?\d+)`, LiteralNumberFloat, nil},
{`-?\.\d+([eE][+\-]?\d+)?`, LiteralNumberFloat, nil},
{`-?0[0-7]+`, LiteralNumberOct, nil},
{`-?0[xX][0-9a-fA-F]+`, LiteralNumberHex, nil},
{`-?(0|[1-9][0-9]*)`, LiteralNumberInteger, nil},
{`'(\\['"\\abfnrtv]|\\x[0-9a-fA-F]{2}|\\[0-7]{1,3}|\\u[0-9a-fA-F]{4}|\\U[0-9a-fA-F]{8}|[^\\])'`, LiteralStringChar, nil},
{"`[^`]*`", LiteralString, nil},
},
}
}
var GoHTMLTemplate = internal.Register(DelegatingLexer(h.HTML, MustNewLazyLexer(
&Config{ &Config{
Name: "Go HTML Template", Name: "Go HTML Template",
Aliases: []string{"go-html-template"}, Aliases: []string{"go-html-template"},
@ -106,7 +112,7 @@ var GoHTMLTemplate = internal.Register(DelegatingLexer(h.HTML, MustNewLexer(
goTemplateRules, goTemplateRules,
))) )))
var GoTextTemplate = internal.Register(MustNewLexer( var GoTextTemplate = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "Go Text Template", Name: "Go Text Template",
Aliases: []string{"go-text-template"}, Aliases: []string{"go-text-template"},

View File

@ -6,13 +6,17 @@ import (
) )
// Go lexer. // Go lexer.
var Graphql = internal.Register(MustNewLexer( var Graphql = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "GraphQL", Name: "GraphQL",
Aliases: []string{"graphql", "graphqls", "gql"}, Aliases: []string{"graphql", "graphqls", "gql"},
Filenames: []string{"*.graphql", "*.graphqls"}, Filenames: []string{"*.graphql", "*.graphqls"},
}, },
Rules{ graphqlRules,
))
func graphqlRules() Rules {
return Rules{
"root": { "root": {
{`(query|mutation|subscription|fragment|scalar|implements|interface|union|enum|input|type)`, KeywordDeclaration, Push("type")}, {`(query|mutation|subscription|fragment|scalar|implements|interface|union|enum|input|type)`, KeywordDeclaration, Push("type")},
{`(on|extend|schema|directive|\.\.\.)`, KeywordDeclaration, nil}, {`(on|extend|schema|directive|\.\.\.)`, KeywordDeclaration, nil},
@ -41,5 +45,5 @@ var Graphql = internal.Register(MustNewLexer(
{`[^\W\d]\w*`, NameClass, Pop(1)}, {`[^\W\d]\w*`, NameClass, Pop(1)},
Include("root"), Include("root"),
}, },
}, }
)) }

47
vendor/github.com/alecthomas/chroma/lexers/g/groff.go generated vendored Normal file
View File

@ -0,0 +1,47 @@
package g
import (
. "github.com/alecthomas/chroma" // nolint
"github.com/alecthomas/chroma/lexers/internal"
)
// Groff lexer.
var Groff = internal.Register(MustNewLazyLexer(
&Config{
Name: "Groff",
Aliases: []string{"groff", "nroff", "man"},
Filenames: []string{"*.[1-9]", "*.1p", "*.3pm", "*.man"},
MimeTypes: []string{"application/x-troff", "text/troff"},
},
func() Rules {
return Rules{
"root": {
{`(\.)(\w+)`, ByGroups(Text, Keyword), Push("request")},
{`\.`, Punctuation, Push("request")},
{`[^\\\n]+`, Text, Push("textline")},
Default(Push("textline")),
},
"textline": {
Include("escapes"),
{`[^\\\n]+`, Text, nil},
{`\n`, Text, Pop(1)},
},
"escapes": {
{`\\"[^\n]*`, Comment, nil},
{`\\[fn]\w`, LiteralStringEscape, nil},
{`\\\(.{2}`, LiteralStringEscape, nil},
{`\\.\[.*\]`, LiteralStringEscape, nil},
{`\\.`, LiteralStringEscape, nil},
{`\\\n`, Text, Push("request")},
},
"request": {
{`\n`, Text, Pop(1)},
Include("escapes"),
{`"[^\n"]+"`, LiteralStringDouble, nil},
{`\d+`, LiteralNumber, nil},
{`\S+`, LiteralString, nil},
{`\s+`, Text, nil},
},
}
},
))

View File

@ -6,7 +6,7 @@ import (
) )
// Groovy lexer. // Groovy lexer.
var Groovy = internal.Register(MustNewLexer( var Groovy = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "Groovy", Name: "Groovy",
Aliases: []string{"groovy"}, Aliases: []string{"groovy"},
@ -14,7 +14,11 @@ var Groovy = internal.Register(MustNewLexer(
MimeTypes: []string{"text/x-groovy"}, MimeTypes: []string{"text/x-groovy"},
DotAll: true, DotAll: true,
}, },
Rules{ groovyRules,
))
func groovyRules() Rules {
return Rules{
"root": { "root": {
{`#!(.*?)$`, CommentPreproc, Push("base")}, {`#!(.*?)$`, CommentPreproc, Push("base")},
Default(Push("base")), Default(Push("base")),
@ -54,5 +58,5 @@ var Groovy = internal.Register(MustNewLexer(
"import": { "import": {
{`[\w.]+\*?`, NameNamespace, Pop(1)}, {`[\w.]+\*?`, NameNamespace, Pop(1)},
}, },
}, }
)) }

View File

@ -6,14 +6,18 @@ import (
) )
// Handlebars lexer. // Handlebars lexer.
var Handlebars = internal.Register(MustNewLexer( var Handlebars = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "Handlebars", Name: "Handlebars",
Aliases: []string{"handlebars"}, Aliases: []string{"handlebars", "hbs"},
Filenames: []string{"*.handlebars"}, Filenames: []string{"*.handlebars", "*.hbs"},
MimeTypes: []string{}, MimeTypes: []string{},
}, },
Rules{ handlebarsRules,
))
func handlebarsRules() Rules {
return Rules{
"root": { "root": {
{`[^{]+`, Other, nil}, {`[^{]+`, Other, nil},
{`\{\{!.*\}\}`, Comment, nil}, {`\{\{!.*\}\}`, Comment, nil},
@ -52,5 +56,5 @@ var Handlebars = internal.Register(MustNewLexer(
{`:?'(\\\\|\\'|[^'])*'`, LiteralStringSingle, nil}, {`:?'(\\\\|\\'|[^'])*'`, LiteralStringSingle, nil},
{`[0-9](\.[0-9]*)?(eE[+-][0-9])?[flFLdD]?|0[xX][0-9a-fA-F]+[Ll]?`, LiteralNumber, nil}, {`[0-9](\.[0-9]*)?(eE[+-][0-9])?[flFLdD]?|0[xX][0-9a-fA-F]+[Ll]?`, LiteralNumber, nil},
}, },
}, }
)) }

View File

@ -6,14 +6,18 @@ import (
) )
// Haskell lexer. // Haskell lexer.
var Haskell = internal.Register(MustNewLexer( var Haskell = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "Haskell", Name: "Haskell",
Aliases: []string{"haskell", "hs"}, Aliases: []string{"haskell", "hs"},
Filenames: []string{"*.hs"}, Filenames: []string{"*.hs"},
MimeTypes: []string{"text/x-haskell"}, MimeTypes: []string{"text/x-haskell"},
}, },
Rules{ haskellRules,
))
func haskellRules() Rules {
return Rules{
"root": { "root": {
{`\s+`, Text, nil}, {`\s+`, Text, nil},
{`--(?![!#$%&*+./<=>?@^|_~:\\]).*?$`, CommentSingle, nil}, {`--(?![!#$%&*+./<=>?@^|_~:\\]).*?$`, CommentSingle, nil},
@ -95,5 +99,5 @@ var Haskell = internal.Register(MustNewLexer(
{`\d+`, LiteralStringEscape, Pop(1)}, {`\d+`, LiteralStringEscape, Pop(1)},
{`\s+\\`, LiteralStringEscape, Pop(1)}, {`\s+\\`, LiteralStringEscape, Pop(1)},
}, },
}, }
)) }

View File

@ -6,7 +6,7 @@ import (
) )
// Haxe lexer. // Haxe lexer.
var Haxe = internal.Register(MustNewLexer( var Haxe = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "Haxe", Name: "Haxe",
Aliases: []string{"hx", "haxe", "hxsl"}, Aliases: []string{"hx", "haxe", "hxsl"},
@ -14,7 +14,11 @@ var Haxe = internal.Register(MustNewLexer(
MimeTypes: []string{"text/haxe", "text/x-haxe", "text/x-hx"}, MimeTypes: []string{"text/haxe", "text/x-haxe", "text/x-hx"},
DotAll: true, DotAll: true,
}, },
Rules{ haxeRules,
))
func haxeRules() Rules {
return Rules{
"root": { "root": {
Include("spaces"), Include("spaces"),
Include("meta"), Include("meta"),
@ -609,8 +613,8 @@ var Haxe = internal.Register(MustNewLexer(
{`\}`, Punctuation, Pop(1)}, {`\}`, Punctuation, Pop(1)},
{`,`, Punctuation, Push("#pop", "object")}, {`,`, Punctuation, Push("#pop", "object")},
}, },
}, }
)) }
func haxePreProcMutator(state *LexerState) error { func haxePreProcMutator(state *LexerState) error {
stack, ok := state.Get("haxe-pre-proc").([][]string) stack, ok := state.Get("haxe-pre-proc").([][]string)

View File

@ -6,14 +6,18 @@ import (
) )
// HCL lexer. // HCL lexer.
var HCL = internal.Register(MustNewLexer( var HCL = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "HCL", Name: "HCL",
Aliases: []string{"hcl"}, Aliases: []string{"hcl"},
Filenames: []string{"*.hcl"}, Filenames: []string{"*.hcl"},
MimeTypes: []string{"application/x-hcl"}, MimeTypes: []string{"application/x-hcl"},
}, },
Rules{ hclRules,
))
func hclRules() Rules {
return Rules{
"root": { "root": {
Include("string"), Include("string"),
Include("punctuation"), Include("punctuation"),
@ -65,5 +69,5 @@ var HCL = internal.Register(MustNewLexer(
{`\s+`, Text, nil}, {`\s+`, Text, nil},
{`\\\n`, Text, nil}, {`\\\n`, Text, nil},
}, },
}, }
)) }

View File

@ -6,14 +6,18 @@ import (
) )
// Hexdump lexer. // Hexdump lexer.
var Hexdump = internal.Register(MustNewLexer( var Hexdump = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "Hexdump", Name: "Hexdump",
Aliases: []string{"hexdump"}, Aliases: []string{"hexdump"},
Filenames: []string{}, Filenames: []string{},
MimeTypes: []string{}, MimeTypes: []string{},
}, },
Rules{ hexdumpRules,
))
func hexdumpRules() Rules {
return Rules{
"root": { "root": {
{`\n`, Text, nil}, {`\n`, Text, nil},
Include("offset"), Include("offset"),
@ -63,5 +67,5 @@ var Hexdump = internal.Register(MustNewLexer(
{`\s`, Text, nil}, {`\s`, Text, nil},
{`^\*`, Punctuation, nil}, {`^\*`, Punctuation, nil},
}, },
}, }
)) }

View File

@ -1,4 +1,4 @@
package lexers package h
import ( import (
. "github.com/alecthomas/chroma" // nolint . "github.com/alecthomas/chroma" // nolint
@ -6,14 +6,18 @@ import (
) )
// HLB lexer. // HLB lexer.
var HLB = internal.Register(MustNewLexer( var HLB = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "HLB", Name: "HLB",
Aliases: []string{"hlb"}, Aliases: []string{"hlb"},
Filenames: []string{"*.hlb"}, Filenames: []string{"*.hlb"},
MimeTypes: []string{}, MimeTypes: []string{},
}, },
Rules{ hlbRules,
))
func hlbRules() Rules {
return Rules{
"root": { "root": {
{`(#.*)`, ByGroups(CommentSingle), nil}, {`(#.*)`, ByGroups(CommentSingle), nil},
{`((\b(0(b|B|o|O|x|X)[a-fA-F0-9]+)\b)|(\b(0|[1-9][0-9]*)\b))`, ByGroups(LiteralNumber), nil}, {`((\b(0(b|B|o|O|x|X)[a-fA-F0-9]+)\b)|(\b(0|[1-9][0-9]*)\b))`, ByGroups(LiteralNumber), nil},
@ -50,5 +54,5 @@ var HLB = internal.Register(MustNewLexer(
{`(\n|\r|\r\n)`, Text, nil}, {`(\n|\r|\r\n)`, Text, nil},
{`.`, Text, nil}, {`.`, Text, nil},
}, },
}, }
)) }

View File

@ -8,7 +8,7 @@ import (
) )
// HTML lexer. // HTML lexer.
var HTML = internal.Register(MustNewLexer( var HTML = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "HTML", Name: "HTML",
Aliases: []string{"html"}, Aliases: []string{"html"},
@ -18,7 +18,11 @@ var HTML = internal.Register(MustNewLexer(
DotAll: true, DotAll: true,
CaseInsensitive: true, CaseInsensitive: true,
}, },
Rules{ htmlRules,
))
func htmlRules() Rules {
return Rules{
"root": { "root": {
{`[^<&]+`, Text, nil}, {`[^<&]+`, Text, nil},
{`&\S*?;`, NameEntity, nil}, {`&\S*?;`, NameEntity, nil},
@ -55,5 +59,5 @@ var HTML = internal.Register(MustNewLexer(
{`'.*?'`, LiteralString, Pop(1)}, {`'.*?'`, LiteralString, Pop(1)},
{`[^\s>]+`, LiteralString, Pop(1)}, {`[^\s>]+`, LiteralString, Pop(1)},
}, },
}, }
)) }

View File

@ -8,7 +8,7 @@ import (
) )
// HTTP lexer. // HTTP lexer.
var HTTP = internal.Register(httpBodyContentTypeLexer(MustNewLexer( var HTTP = internal.Register(httpBodyContentTypeLexer(MustNewLazyLexer(
&Config{ &Config{
Name: "HTTP", Name: "HTTP",
Aliases: []string{"http"}, Aliases: []string{"http"},
@ -17,7 +17,11 @@ var HTTP = internal.Register(httpBodyContentTypeLexer(MustNewLexer(
NotMultiline: true, NotMultiline: true,
DotAll: true, DotAll: true,
}, },
Rules{ httpRules,
)))
func httpRules() Rules {
return Rules{
"root": { "root": {
{`(GET|POST|PUT|DELETE|HEAD|OPTIONS|TRACE|PATCH|CONNECT)( +)([^ ]+)( +)(HTTP)(/)([12]\.[01])(\r?\n|\Z)`, ByGroups(NameFunction, Text, NameNamespace, Text, KeywordReserved, Operator, LiteralNumber, Text), Push("headers")}, {`(GET|POST|PUT|DELETE|HEAD|OPTIONS|TRACE|PATCH|CONNECT)( +)([^ ]+)( +)(HTTP)(/)([12]\.[01])(\r?\n|\Z)`, ByGroups(NameFunction, Text, NameNamespace, Text, KeywordReserved, Operator, LiteralNumber, Text), Push("headers")},
{`(HTTP)(/)([12]\.[01])( +)(\d{3})( +)([^\r\n]+)(\r?\n|\Z)`, ByGroups(KeywordReserved, Operator, LiteralNumber, Text, LiteralNumber, Text, NameException, Text), Push("headers")}, {`(HTTP)(/)([12]\.[01])( +)(\d{3})( +)([^\r\n]+)(\r?\n|\Z)`, ByGroups(KeywordReserved, Operator, LiteralNumber, Text, LiteralNumber, Text, NameException, Text), Push("headers")},
@ -30,17 +34,17 @@ var HTTP = internal.Register(httpBodyContentTypeLexer(MustNewLexer(
"content": { "content": {
{`.+`, EmitterFunc(httpContentBlock), nil}, {`.+`, EmitterFunc(httpContentBlock), nil},
}, },
}, }
))) }
func httpContentBlock(groups []string, lexer Lexer) Iterator { func httpContentBlock(groups []string, state *LexerState) Iterator {
tokens := []Token{ tokens := []Token{
{Generic, groups[0]}, {Generic, groups[0]},
} }
return Literator(tokens...) return Literator(tokens...)
} }
func httpHeaderBlock(groups []string, lexer Lexer) Iterator { func httpHeaderBlock(groups []string, state *LexerState) Iterator {
tokens := []Token{ tokens := []Token{
{Name, groups[1]}, {Name, groups[1]},
{Text, groups[2]}, {Text, groups[2]},
@ -52,7 +56,7 @@ func httpHeaderBlock(groups []string, lexer Lexer) Iterator {
return Literator(tokens...) return Literator(tokens...)
} }
func httpContinuousHeaderBlock(groups []string, lexer Lexer) Iterator { func httpContinuousHeaderBlock(groups []string, state *LexerState) Iterator {
tokens := []Token{ tokens := []Token{
{Text, groups[1]}, {Text, groups[1]},
{Literal, groups[2]}, {Literal, groups[2]},

View File

@ -6,14 +6,18 @@ import (
) )
// Hy lexer. // Hy lexer.
var Hy = internal.Register(MustNewLexer( var Hy = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "Hy", Name: "Hy",
Aliases: []string{"hylang"}, Aliases: []string{"hylang"},
Filenames: []string{"*.hy"}, Filenames: []string{"*.hy"},
MimeTypes: []string{"text/x-hy", "application/x-hy"}, MimeTypes: []string{"text/x-hy", "application/x-hy"},
}, },
Rules{ hyRules,
))
func hyRules() Rules {
return Rules{
"root": { "root": {
{`;.*$`, CommentSingle, nil}, {`;.*$`, CommentSingle, nil},
{`[,\s]+`, Text, nil}, {`[,\s]+`, Text, nil},
@ -47,5 +51,5 @@ var Hy = internal.Register(MustNewLexer(
{`(?<!\.)(self|None|Ellipsis|NotImplemented|False|True|cls)\b`, NameBuiltinPseudo, nil}, {`(?<!\.)(self|None|Ellipsis|NotImplemented|False|True|cls)\b`, NameBuiltinPseudo, nil},
{Words(`(?<!\.)`, `\b`, `ArithmeticError`, `AssertionError`, `AttributeError`, `BaseException`, `DeprecationWarning`, `EOFError`, `EnvironmentError`, `Exception`, `FloatingPointError`, `FutureWarning`, `GeneratorExit`, `IOError`, `ImportError`, `ImportWarning`, `IndentationError`, `IndexError`, `KeyError`, `KeyboardInterrupt`, `LookupError`, `MemoryError`, `NameError`, `NotImplemented`, `NotImplementedError`, `OSError`, `OverflowError`, `OverflowWarning`, `PendingDeprecationWarning`, `ReferenceError`, `RuntimeError`, `RuntimeWarning`, `StandardError`, `StopIteration`, `SyntaxError`, `SyntaxWarning`, `SystemError`, `SystemExit`, `TabError`, `TypeError`, `UnboundLocalError`, `UnicodeDecodeError`, `UnicodeEncodeError`, `UnicodeError`, `UnicodeTranslateError`, `UnicodeWarning`, `UserWarning`, `ValueError`, `VMSError`, `Warning`, `WindowsError`, `ZeroDivisionError`), NameException, nil}, {Words(`(?<!\.)`, `\b`, `ArithmeticError`, `AssertionError`, `AttributeError`, `BaseException`, `DeprecationWarning`, `EOFError`, `EnvironmentError`, `Exception`, `FloatingPointError`, `FutureWarning`, `GeneratorExit`, `IOError`, `ImportError`, `ImportWarning`, `IndentationError`, `IndexError`, `KeyError`, `KeyboardInterrupt`, `LookupError`, `MemoryError`, `NameError`, `NotImplemented`, `NotImplementedError`, `OSError`, `OverflowError`, `OverflowWarning`, `PendingDeprecationWarning`, `ReferenceError`, `RuntimeError`, `RuntimeWarning`, `StandardError`, `StopIteration`, `SyntaxError`, `SyntaxWarning`, `SystemError`, `SystemExit`, `TabError`, `TypeError`, `UnboundLocalError`, `UnicodeDecodeError`, `UnicodeEncodeError`, `UnicodeError`, `UnicodeTranslateError`, `UnicodeWarning`, `UserWarning`, `ValueError`, `VMSError`, `Warning`, `WindowsError`, `ZeroDivisionError`), NameException, nil},
}, },
}, }
)) }

View File

@ -6,14 +6,18 @@ import (
) )
// Idris lexer. // Idris lexer.
var Idris = internal.Register(MustNewLexer( var Idris = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "Idris", Name: "Idris",
Aliases: []string{"idris", "idr"}, Aliases: []string{"idris", "idr"},
Filenames: []string{"*.idr"}, Filenames: []string{"*.idr"},
MimeTypes: []string{"text/x-idris"}, MimeTypes: []string{"text/x-idris"},
}, },
Rules{ idrisRules,
))
func idrisRules() Rules {
return Rules{
"root": { "root": {
{`^(\s*)(%lib|link|flag|include|hide|freeze|access|default|logging|dynamic|name|error_handlers|language)`, ByGroups(Text, KeywordReserved), nil}, {`^(\s*)(%lib|link|flag|include|hide|freeze|access|default|logging|dynamic|name|error_handlers|language)`, ByGroups(Text, KeywordReserved), nil},
{`(\s*)(--(?![!#$%&*+./<=>?@^|_~:\\]).*?)$`, ByGroups(Text, CommentSingle), nil}, {`(\s*)(--(?![!#$%&*+./<=>?@^|_~:\\]).*?)$`, ByGroups(Text, CommentSingle), nil},
@ -76,5 +80,5 @@ var Idris = internal.Register(MustNewLexer(
{`\d+`, LiteralStringEscape, Pop(1)}, {`\d+`, LiteralStringEscape, Pop(1)},
{`\s+\\`, LiteralStringEscape, Pop(1)}, {`\s+\\`, LiteralStringEscape, Pop(1)},
}, },
}, }
)) }

View File

@ -6,7 +6,7 @@ import (
) )
// Igor lexer. // Igor lexer.
var Igor = internal.Register(MustNewLexer( var Igor = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "Igor", Name: "Igor",
Aliases: []string{"igor", "igorpro"}, Aliases: []string{"igor", "igorpro"},
@ -14,7 +14,11 @@ var Igor = internal.Register(MustNewLexer(
MimeTypes: []string{"text/ipf"}, MimeTypes: []string{"text/ipf"},
CaseInsensitive: true, CaseInsensitive: true,
}, },
Rules{ igorRules,
))
func igorRules() Rules {
return Rules{
"root": { "root": {
{`//.*$`, CommentSingle, nil}, {`//.*$`, CommentSingle, nil},
{`"([^"\\]|\\.)*"`, LiteralString, nil}, {`"([^"\\]|\\.)*"`, LiteralString, nil},
@ -28,5 +32,5 @@ var Igor = internal.Register(MustNewLexer(
{`.`, Text, nil}, {`.`, Text, nil},
{`\n|\r`, Text, nil}, {`\n|\r`, Text, nil},
}, },
}, }
)) }

View File

@ -6,14 +6,18 @@ import (
) )
// Ini lexer. // Ini lexer.
var Ini = internal.Register(MustNewLexer( var Ini = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "INI", Name: "INI",
Aliases: []string{"ini", "cfg", "dosini"}, Aliases: []string{"ini", "cfg", "dosini"},
Filenames: []string{"*.ini", "*.cfg", "*.inf", ".gitconfig", ".editorconfig"}, Filenames: []string{"*.ini", "*.cfg", "*.inf", ".gitconfig", ".editorconfig"},
MimeTypes: []string{"text/x-ini", "text/inf"}, MimeTypes: []string{"text/x-ini", "text/inf"},
}, },
Rules{ iniRules,
))
func iniRules() Rules {
return Rules{
"root": { "root": {
{`\s+`, Text, nil}, {`\s+`, Text, nil},
{`[;#].*`, CommentSingle, nil}, {`[;#].*`, CommentSingle, nil},
@ -21,5 +25,5 @@ var Ini = internal.Register(MustNewLexer(
{`(.*?)([ \t]*)(=)([ \t]*)(.*(?:\n[ \t].+)*)`, ByGroups(NameAttribute, Text, Operator, Text, LiteralString), nil}, {`(.*?)([ \t]*)(=)([ \t]*)(.*(?:\n[ \t].+)*)`, ByGroups(NameAttribute, Text, Operator, Text, LiteralString), nil},
{`(.+?)$`, NameAttribute, nil}, {`(.+?)$`, NameAttribute, nil},
}, },
}, }
)) }

View File

@ -6,14 +6,18 @@ import (
) )
// Io lexer. // Io lexer.
var Io = internal.Register(MustNewLexer( var Io = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "Io", Name: "Io",
Aliases: []string{"io"}, Aliases: []string{"io"},
Filenames: []string{"*.io"}, Filenames: []string{"*.io"},
MimeTypes: []string{"text/x-iosrc"}, MimeTypes: []string{"text/x-iosrc"},
}, },
Rules{ ioRules,
))
func ioRules() Rules {
return Rules{
"root": { "root": {
{`\n`, Text, nil}, {`\n`, Text, nil},
{`\s+`, Text, nil}, {`\s+`, Text, nil},
@ -36,5 +40,5 @@ var Io = internal.Register(MustNewLexer(
{`\+/`, CommentMultiline, Pop(1)}, {`\+/`, CommentMultiline, Pop(1)},
{`[+/]`, CommentMultiline, nil}, {`[+/]`, CommentMultiline, nil},
}, },
}, }
)) }

View File

@ -11,6 +11,19 @@ import (
"github.com/alecthomas/chroma" "github.com/alecthomas/chroma"
) )
var (
ignoredSuffixes = [...]string{
// Editor backups
"~", ".bak", ".old", ".orig",
// Debian and derivatives apt/dpkg backups
".dpkg-dist", ".dpkg-old",
// Red Hat and derivatives rpm backups
".rpmnew", ".rpmorig", ".rpmsave",
// Build system input/template files
".in",
}
)
// Registry of Lexers. // Registry of Lexers.
var Registry = struct { var Registry = struct {
Lexers chroma.Lexers Lexers chroma.Lexers
@ -93,6 +106,13 @@ func Match(filename string) chroma.Lexer {
for _, glob := range config.Filenames { for _, glob := range config.Filenames {
if fnmatch.Match(glob, filename, 0) { if fnmatch.Match(glob, filename, 0) {
matched = append(matched, lexer) matched = append(matched, lexer)
} else {
for _, suf := range &ignoredSuffixes {
if fnmatch.Match(glob+suf, filename, 0) {
matched = append(matched, lexer)
break
}
}
} }
} }
} }
@ -107,6 +127,13 @@ func Match(filename string) chroma.Lexer {
for _, glob := range config.AliasFilenames { for _, glob := range config.AliasFilenames {
if fnmatch.Match(glob, filename, 0) { if fnmatch.Match(glob, filename, 0) {
matched = append(matched, lexer) matched = append(matched, lexer)
} else {
for _, suf := range &ignoredSuffixes {
if fnmatch.Match(glob+suf, filename, 0) {
matched = append(matched, lexer)
break
}
}
} }
} }
} }
@ -146,16 +173,19 @@ func Register(lexer chroma.Lexer) chroma.Lexer {
return lexer return lexer
} }
// Used for the fallback lexer as well as the explicit plaintext lexer // PlaintextRules is used for the fallback lexer as well as the explicit
var PlaintextRules = chroma.Rules{ // plaintext lexer.
"root": []chroma.Rule{ func PlaintextRules() chroma.Rules {
{`.+`, chroma.Text, nil}, return chroma.Rules{
{`\n`, chroma.Text, nil}, "root": []chroma.Rule{
}, {`.+`, chroma.Text, nil},
{`\n`, chroma.Text, nil},
},
}
} }
// Fallback lexer if no other is found. // Fallback lexer if no other is found.
var Fallback chroma.Lexer = chroma.MustNewLexer(&chroma.Config{ var Fallback chroma.Lexer = chroma.MustNewLazyLexer(&chroma.Config{
Name: "fallback", Name: "fallback",
Filenames: []string{"*"}, Filenames: []string{"*"},
}, PlaintextRules) }, PlaintextRules)

View File

@ -6,14 +6,18 @@ import (
) )
// J lexer. // J lexer.
var J = internal.Register(MustNewLexer( var J = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "J", Name: "J",
Aliases: []string{"j"}, Aliases: []string{"j"},
Filenames: []string{"*.ijs"}, Filenames: []string{"*.ijs"},
MimeTypes: []string{"text/x-j"}, MimeTypes: []string{"text/x-j"},
}, },
Rules{ jRules,
))
func jRules() Rules {
return Rules{
"root": { "root": {
{`#!.*$`, CommentPreproc, nil}, {`#!.*$`, CommentPreproc, nil},
{`NB\..*`, CommentSingle, nil}, {`NB\..*`, CommentSingle, nil},
@ -69,5 +73,5 @@ var J = internal.Register(MustNewLexer(
{`''`, LiteralString, nil}, {`''`, LiteralString, nil},
{`'`, LiteralString, Pop(1)}, {`'`, LiteralString, Pop(1)},
}, },
}, }
)) }

View File

@ -6,15 +6,20 @@ import (
) )
// Java lexer. // Java lexer.
var Java = internal.Register(MustNewLexer( var Java = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "Java", Name: "Java",
Aliases: []string{"java"}, Aliases: []string{"java"},
Filenames: []string{"*.java"}, Filenames: []string{"*.java"},
MimeTypes: []string{"text/x-java"}, MimeTypes: []string{"text/x-java"},
DotAll: true, DotAll: true,
EnsureNL: true,
}, },
Rules{ javaRules,
))
func javaRules() Rules {
return Rules{
"root": { "root": {
{`[^\S\n]+`, Text, nil}, {`[^\S\n]+`, Text, nil},
{`//.*?\n`, CommentSingle, nil}, {`//.*?\n`, CommentSingle, nil},
@ -47,5 +52,5 @@ var Java = internal.Register(MustNewLexer(
"import": { "import": {
{`[\w.]+\*?`, NameNamespace, Pop(1)}, {`[\w.]+\*?`, NameNamespace, Pop(1)},
}, },
}, }
)) }

View File

@ -26,11 +26,11 @@ var JavascriptRules = Rules{
{`\A#! ?/.*?\n`, CommentHashbang, nil}, {`\A#! ?/.*?\n`, CommentHashbang, nil},
{`^(?=\s|/|<!--)`, Text, Push("slashstartsregex")}, {`^(?=\s|/|<!--)`, Text, Push("slashstartsregex")},
Include("commentsandwhitespace"), Include("commentsandwhitespace"),
{`(\.\d+|[0-9]+\.[0-9]*)([eE][-+]?[0-9]+)?`, LiteralNumberFloat, nil}, {`\d+(\.\d*|[eE][+\-]?\d+)`, LiteralNumberFloat, nil},
{`0[bB][01]+`, LiteralNumberBin, nil}, {`0[bB][01]+`, LiteralNumberBin, nil},
{`0[oO][0-7]+`, LiteralNumberOct, nil}, {`0[oO][0-7]+`, LiteralNumberOct, nil},
{`0[xX][0-9a-fA-F]+`, LiteralNumberHex, nil}, {`0[xX][0-9a-fA-F]+`, LiteralNumberHex, nil},
{`[0-9]+`, LiteralNumberInteger, nil}, {`[0-9_]+`, LiteralNumberInteger, nil},
{`\.\.\.|=>`, Punctuation, nil}, {`\.\.\.|=>`, Punctuation, nil},
{`\+\+|--|~|&&|\?|:|\|\||\\(?=\n)|(<<|>>>?|==?|!=?|[-<>+*%&|^/])=?`, Operator, Push("slashstartsregex")}, {`\+\+|--|~|&&|\?|:|\|\||\\(?=\n)|(<<|>>>?|==?|!=?|[-<>+*%&|^/])=?`, Operator, Push("slashstartsregex")},
{`[{(\[;,]`, Punctuation, Push("slashstartsregex")}, {`[{(\[;,]`, Punctuation, Push("slashstartsregex")},
@ -49,6 +49,7 @@ var JavascriptRules = Rules{
{"`", LiteralStringBacktick, Pop(1)}, {"`", LiteralStringBacktick, Pop(1)},
{`\\\\`, LiteralStringBacktick, nil}, {`\\\\`, LiteralStringBacktick, nil},
{"\\\\`", LiteralStringBacktick, nil}, {"\\\\`", LiteralStringBacktick, nil},
{"\\\\[^`\\\\]", LiteralStringBacktick, nil},
{`\$\{`, LiteralStringInterpol, Push("interp-inside")}, {`\$\{`, LiteralStringInterpol, Push("interp-inside")},
{`\$`, LiteralStringBacktick, nil}, {`\$`, LiteralStringBacktick, nil},
{"[^`\\\\$]+", LiteralStringBacktick, nil}, {"[^`\\\\$]+", LiteralStringBacktick, nil},
@ -60,11 +61,11 @@ var JavascriptRules = Rules{
} }
// Javascript lexer. // Javascript lexer.
var Javascript = internal.Register(MustNewLexer( var Javascript = internal.Register(MustNewLexer( // nolint: forbidigo
&Config{ &Config{
Name: "JavaScript", Name: "JavaScript",
Aliases: []string{"js", "javascript"}, Aliases: []string{"js", "javascript"},
Filenames: []string{"*.js", "*.jsm"}, Filenames: []string{"*.js", "*.jsm", "*.mjs"},
MimeTypes: []string{"application/javascript", "application/x-javascript", "text/x-javascript", "text/javascript"}, MimeTypes: []string{"application/javascript", "application/x-javascript", "text/x-javascript", "text/javascript"},
DotAll: true, DotAll: true,
EnsureNL: true, EnsureNL: true,

View File

@ -6,7 +6,7 @@ import (
) )
// JSON lexer. // JSON lexer.
var JSON = internal.Register(MustNewLexer( var JSON = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "JSON", Name: "JSON",
Aliases: []string{"json"}, Aliases: []string{"json"},
@ -15,7 +15,11 @@ var JSON = internal.Register(MustNewLexer(
NotMultiline: true, NotMultiline: true,
DotAll: true, DotAll: true,
}, },
Rules{ jsonRules,
))
func jsonRules() Rules {
return Rules{
"whitespace": { "whitespace": {
{`\s+`, Text, nil}, {`\s+`, Text, nil},
}, },
@ -51,5 +55,5 @@ var JSON = internal.Register(MustNewLexer(
"root": { "root": {
Include("value"), Include("value"),
}, },
}, }
)) }

View File

@ -8,7 +8,7 @@ import (
// JSX lexer. // JSX lexer.
// //
// This was generated from https://github.com/fcurella/jsx-lexer // This was generated from https://github.com/fcurella/jsx-lexer
var JSX = internal.Register(MustNewLexer( var JSX = internal.Register(MustNewLazyLexer(
&Config{ &Config{
Name: "react", Name: "react",
Aliases: []string{"jsx", "react"}, Aliases: []string{"jsx", "react"},
@ -16,7 +16,11 @@ var JSX = internal.Register(MustNewLexer(
MimeTypes: []string{"text/jsx", "text/typescript-jsx"}, MimeTypes: []string{"text/jsx", "text/typescript-jsx"},
DotAll: true, DotAll: true,
}, },
Rules{ jsxRules,
))
func jsxRules() Rules {
return Rules{
"commentsandwhitespace": { "commentsandwhitespace": {
{`\s+`, Text, nil}, {`\s+`, Text, nil},
{`<!--`, Comment, nil}, {`<!--`, Comment, nil},
@ -91,5 +95,5 @@ var JSX = internal.Register(MustNewLexer(
{`}`, Punctuation, Pop(1)}, {`}`, Punctuation, Pop(1)},
Include("root"), Include("root"),
}, },
}, }
)) }

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