diff --git a/go.mod b/go.mod
index 41d54836b2..8aedb945bb 100644
--- a/go.mod
+++ b/go.mod
@@ -4,7 +4,7 @@ go 1.22.0
require (
github.com/docker/cli v27.3.1+incompatible
- github.com/go-git/go-git/v5 v5.12.0
+ github.com/go-git/go-git/v5 v5.13.1
github.com/go-logr/logr v1.4.2
github.com/golang-jwt/jwt/v4 v4.5.1
github.com/google/go-containerregistry v0.20.2
@@ -35,7 +35,7 @@ require (
contrib.go.opencensus.io/exporter/prometheus v0.4.2 // indirect
dario.cat/mergo v1.0.1 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
- github.com/ProtonMail/go-crypto v1.0.0 // indirect
+ github.com/ProtonMail/go-crypto v1.1.3 // indirect
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/blendle/zapdriver v1.3.1 // indirect
@@ -43,7 +43,7 @@ require (
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cloudflare/circl v1.5.0 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.15.1 // indirect
- github.com/cyphar/filepath-securejoin v0.3.4 // indirect
+ github.com/cyphar/filepath-securejoin v0.3.6 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/docker-credential-helpers v0.8.2 // indirect
@@ -53,7 +53,7 @@ require (
github.com/evanphx/json-patch/v5 v5.9.0 // indirect
github.com/fsnotify/fsnotify v1.8.0 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
- github.com/go-git/go-billy/v5 v5.6.0 // indirect
+ github.com/go-git/go-billy/v5 v5.6.1 // indirect
github.com/go-logr/zapr v1.3.0 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect
diff --git a/go.sum b/go.sum
index 396bd99ba4..e65f382116 100644
--- a/go.sum
+++ b/go.sum
@@ -42,8 +42,8 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
-github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78=
-github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
+github.com/ProtonMail/go-crypto v1.1.3 h1:nRBOetoydLeUb4nHajyO2bKqMLfWQ/ZPwkXqXxPxCFk=
+github.com/ProtonMail/go-crypto v1.1.3/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
@@ -63,7 +63,6 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/blendle/zapdriver v1.3.1 h1:C3dydBOWYRiOk+B8X9IVZ5IOe+7cl+tGOexN4QqHfpE=
github.com/blendle/zapdriver v1.3.1/go.mod h1:mdXfREi6u5MArG4j9fewC+FGnXaBR+T4Ox4J2u4eHCc=
-github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g=
github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw=
@@ -77,15 +76,14 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudevents/sdk-go/v2 v2.15.2 h1:54+I5xQEnI73RBhWHxbI1XJcqOFOVJN85vb41+8mHUc=
github.com/cloudevents/sdk-go/v2 v2.15.2/go.mod h1:lL7kSWAE/V8VI4Wh0jbL2v/jvqsm6tjmaQBSvxcv4uE=
-github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
github.com/cloudflare/circl v1.5.0 h1:hxIWksrX6XN5a1L2TI/h53AGPhNHoUBo+TD1ms9+pys=
github.com/cloudflare/circl v1.5.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/containerd/stargz-snapshotter/estargz v0.15.1 h1:eXJjw9RbkLFgioVaTG+G/ZW/0kEe2oEKCdS/ZxIyoCU=
github.com/containerd/stargz-snapshotter/estargz v0.15.1/go.mod h1:gr2RNwukQ/S9Nv33Lt6UC7xEx58C+LHRdoqbEKjz1Kk=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
-github.com/cyphar/filepath-securejoin v0.3.4 h1:VBWugsJh2ZxJmLFSM06/0qzQyiQX2Qs0ViKrUAcqdZ8=
-github.com/cyphar/filepath-securejoin v0.3.4/go.mod h1:8s/MCNJREmFK0H02MF6Ihv1nakJe4L/w3WZLHNkvlYM=
+github.com/cyphar/filepath-securejoin v0.3.6 h1:4d9N5ykBnSp5Xn2JkhocYDkOpURL/18CYMpo6xB9uWM=
+github.com/cyphar/filepath-securejoin v0.3.6/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
@@ -96,8 +94,8 @@ github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBi
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo=
github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
-github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU=
-github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
+github.com/elazarl/goproxy v1.2.3 h1:xwIyKHbaP5yfT6O9KIeYJR5549MXRQkoQMRXGztz8YQ=
+github.com/elazarl/goproxy v1.2.3/go.mod h1:YfEbZtqP4AetfO6d40vWchF3znWX7C7Vd6ZMfdL8z64=
github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU=
github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
@@ -113,16 +111,16 @@ github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE=
-github.com/gliderlabs/ssh v0.3.7/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8=
+github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
+github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
-github.com/go-git/go-billy/v5 v5.6.0 h1:w2hPNtoehvJIxR00Vb4xX94qHQi/ApZfX+nBE2Cjio8=
-github.com/go-git/go-billy/v5 v5.6.0/go.mod h1:sFDq7xD3fn3E0GOwUSZqHo9lrkmx8xJhA0ZrfvjBRGM=
+github.com/go-git/go-billy/v5 v5.6.1 h1:u+dcrgaguSSkbjzHwelEjc0Yj300NUevrrPphk/SoRA=
+github.com/go-git/go-billy/v5 v5.6.1/go.mod h1:0AsLr1z2+Uksi4NlElmMblP5rPcDZNRCD8ujZCRR2BE=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
-github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys=
-github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY=
+github.com/go-git/go-git/v5 v5.13.1 h1:DAQ9APonnlvSWpvolXWIuV6Q6zXy2wHbN4cVlNR5Q+M=
+github.com/go-git/go-git/v5 v5.13.1/go.mod h1:qryJB4cSBoq3FRoBRf5A77joojuBcmPJ0qu3XXXVixc=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
@@ -384,8 +382,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
-github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
-github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
+github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stvp/go-udp-testing v0.0.0-20201019212854-469649b16807/go.mod h1:7jxmlfBCDBXRzr0eAQJ48XC1hBu1np4CS5+cHEYfwpc=
github.com/tektoncd/pipeline v0.65.1 h1:7Ee/nqG+QWE25NGzwKZdFE0p5COb/aljfDysUFv8+0o=
github.com/tektoncd/pipeline v0.65.1/go.mod h1:V3cyfxxc7b3GLT2a13GX2mWA86qmxWhh4mOp4gfFQwQ=
@@ -397,7 +395,6 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
@@ -421,10 +418,7 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
-golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -459,8 +453,6 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
-golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -494,15 +486,10 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
-golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
-golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -525,8 +512,6 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -572,18 +557,10 @@ golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
-golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
-golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -593,9 +570,6 @@ 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.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
-golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -645,8 +619,6 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
-golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/pkg/git/git.go b/pkg/git/git.go
index 28942ebd2e..9d20a6affe 100644
--- a/pkg/git/git.go
+++ b/pkg/git/git.go
@@ -6,6 +6,7 @@ package git
import (
"context"
+ "errors"
"fmt"
"github.com/go-git/go-git/v5/config"
@@ -45,7 +46,7 @@ func ValidateGitURLExists(ctx context.Context, urlPath string) error {
// path doesn't exist, func will return `authentication required`,
// this is maybe misleading. So convert this error message to:
// `remote repository unreachable`
- if err == transport.ErrAuthenticationRequired {
+ if errors.Is(err, transport.ErrAuthenticationRequired) {
return fmt.Errorf("remote repository unreachable")
}
diff --git a/vendor/github.com/ProtonMail/go-crypto/ocb/ocb.go b/vendor/github.com/ProtonMail/go-crypto/ocb/ocb.go
index 1a6f73502e..5022285b44 100644
--- a/vendor/github.com/ProtonMail/go-crypto/ocb/ocb.go
+++ b/vendor/github.com/ProtonMail/go-crypto/ocb/ocb.go
@@ -18,8 +18,9 @@ import (
"crypto/cipher"
"crypto/subtle"
"errors"
- "github.com/ProtonMail/go-crypto/internal/byteutil"
"math/bits"
+
+ "github.com/ProtonMail/go-crypto/internal/byteutil"
)
type ocb struct {
@@ -153,7 +154,7 @@ func (o *ocb) crypt(instruction int, Y, nonce, adata, X []byte) []byte {
truncatedNonce := make([]byte, len(nonce))
copy(truncatedNonce, nonce)
truncatedNonce[len(truncatedNonce)-1] &= 192
- Ktop := make([]byte, blockSize)
+ var Ktop []byte
if bytes.Equal(truncatedNonce, o.reusableKtop.noncePrefix) {
Ktop = o.reusableKtop.Ktop
} else {
diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/armor/armor.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/armor/armor.go
index d7af9141e3..e0a677f284 100644
--- a/vendor/github.com/ProtonMail/go-crypto/openpgp/armor/armor.go
+++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/armor/armor.go
@@ -23,7 +23,7 @@ import (
// Headers
//
// base64-encoded Bytes
-// '=' base64 encoded checksum
+// '=' base64 encoded checksum (optional) not checked anymore
// -----END Type-----
//
// where Headers is a possibly empty sequence of Key: Value lines.
@@ -40,36 +40,15 @@ type Block struct {
var ArmorCorrupt error = errors.StructuralError("armor invalid")
-const crc24Init = 0xb704ce
-const crc24Poly = 0x1864cfb
-const crc24Mask = 0xffffff
-
-// crc24 calculates the OpenPGP checksum as specified in RFC 4880, section 6.1
-func crc24(crc uint32, d []byte) uint32 {
- for _, b := range d {
- crc ^= uint32(b) << 16
- for i := 0; i < 8; i++ {
- crc <<= 1
- if crc&0x1000000 != 0 {
- crc ^= crc24Poly
- }
- }
- }
- return crc
-}
-
var armorStart = []byte("-----BEGIN ")
var armorEnd = []byte("-----END ")
var armorEndOfLine = []byte("-----")
-// lineReader wraps a line based reader. It watches for the end of an armor
-// block and records the expected CRC value.
+// lineReader wraps a line based reader. It watches for the end of an armor block
type lineReader struct {
- in *bufio.Reader
- buf []byte
- eof bool
- crc uint32
- crcSet bool
+ in *bufio.Reader
+ buf []byte
+ eof bool
}
func (l *lineReader) Read(p []byte) (n int, err error) {
@@ -98,26 +77,9 @@ func (l *lineReader) Read(p []byte) (n int, err error) {
if len(line) == 5 && line[0] == '=' {
// This is the checksum line
- var expectedBytes [3]byte
- var m int
- m, err = base64.StdEncoding.Decode(expectedBytes[0:], line[1:])
- if m != 3 || err != nil {
- return
- }
- l.crc = uint32(expectedBytes[0])<<16 |
- uint32(expectedBytes[1])<<8 |
- uint32(expectedBytes[2])
-
- line, _, err = l.in.ReadLine()
- if err != nil && err != io.EOF {
- return
- }
- if !bytes.HasPrefix(line, armorEnd) {
- return 0, ArmorCorrupt
- }
+ // Don't check the checksum
l.eof = true
- l.crcSet = true
return 0, io.EOF
}
@@ -138,23 +100,14 @@ func (l *lineReader) Read(p []byte) (n int, err error) {
return
}
-// openpgpReader passes Read calls to the underlying base64 decoder, but keeps
-// a running CRC of the resulting data and checks the CRC against the value
-// found by the lineReader at EOF.
+// openpgpReader passes Read calls to the underlying base64 decoder.
type openpgpReader struct {
- lReader *lineReader
- b64Reader io.Reader
- currentCRC uint32
+ lReader *lineReader
+ b64Reader io.Reader
}
func (r *openpgpReader) Read(p []byte) (n int, err error) {
n, err = r.b64Reader.Read(p)
- r.currentCRC = crc24(r.currentCRC, p[:n])
-
- if err == io.EOF && r.lReader.crcSet && r.lReader.crc != uint32(r.currentCRC&crc24Mask) {
- return 0, ArmorCorrupt
- }
-
return
}
@@ -222,7 +175,6 @@ TryNextBlock:
}
p.lReader.in = r
- p.oReader.currentCRC = crc24Init
p.oReader.lReader = &p.lReader
p.oReader.b64Reader = base64.NewDecoder(base64.StdEncoding, &p.lReader)
p.Body = &p.oReader
diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/armor/encode.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/armor/encode.go
index 5b6e16c19d..112f98b835 100644
--- a/vendor/github.com/ProtonMail/go-crypto/openpgp/armor/encode.go
+++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/armor/encode.go
@@ -14,6 +14,23 @@ var blockEnd = []byte("\n=")
var newline = []byte("\n")
var armorEndOfLineOut = []byte("-----\n")
+const crc24Init = 0xb704ce
+const crc24Poly = 0x1864cfb
+
+// crc24 calculates the OpenPGP checksum as specified in RFC 4880, section 6.1
+func crc24(crc uint32, d []byte) uint32 {
+ for _, b := range d {
+ crc ^= uint32(b) << 16
+ for i := 0; i < 8; i++ {
+ crc <<= 1
+ if crc&0x1000000 != 0 {
+ crc ^= crc24Poly
+ }
+ }
+ }
+ return crc
+}
+
// writeSlices writes its arguments to the given Writer.
func writeSlices(out io.Writer, slices ...[]byte) (err error) {
for _, s := range slices {
@@ -99,15 +116,18 @@ func (l *lineBreaker) Close() (err error) {
//
// encoding -> base64 encoder -> lineBreaker -> out
type encoding struct {
- out io.Writer
- breaker *lineBreaker
- b64 io.WriteCloser
- crc uint32
- blockType []byte
+ out io.Writer
+ breaker *lineBreaker
+ b64 io.WriteCloser
+ crc uint32
+ crcEnabled bool
+ blockType []byte
}
func (e *encoding) Write(data []byte) (n int, err error) {
- e.crc = crc24(e.crc, data)
+ if e.crcEnabled {
+ e.crc = crc24(e.crc, data)
+ }
return e.b64.Write(data)
}
@@ -118,20 +138,21 @@ func (e *encoding) Close() (err error) {
}
e.breaker.Close()
- var checksumBytes [3]byte
- checksumBytes[0] = byte(e.crc >> 16)
- checksumBytes[1] = byte(e.crc >> 8)
- checksumBytes[2] = byte(e.crc)
+ if e.crcEnabled {
+ var checksumBytes [3]byte
+ checksumBytes[0] = byte(e.crc >> 16)
+ checksumBytes[1] = byte(e.crc >> 8)
+ checksumBytes[2] = byte(e.crc)
- var b64ChecksumBytes [4]byte
- base64.StdEncoding.Encode(b64ChecksumBytes[:], checksumBytes[:])
+ var b64ChecksumBytes [4]byte
+ base64.StdEncoding.Encode(b64ChecksumBytes[:], checksumBytes[:])
- return writeSlices(e.out, blockEnd, b64ChecksumBytes[:], newline, armorEnd, e.blockType, armorEndOfLine)
+ return writeSlices(e.out, blockEnd, b64ChecksumBytes[:], newline, armorEnd, e.blockType, armorEndOfLine)
+ }
+ return writeSlices(e.out, newline, armorEnd, e.blockType, armorEndOfLine)
}
-// Encode returns a WriteCloser which will encode the data written to it in
-// OpenPGP armor.
-func Encode(out io.Writer, blockType string, headers map[string]string) (w io.WriteCloser, err error) {
+func encode(out io.Writer, blockType string, headers map[string]string, checksum bool) (w io.WriteCloser, err error) {
bType := []byte(blockType)
err = writeSlices(out, armorStart, bType, armorEndOfLineOut)
if err != nil {
@@ -151,11 +172,27 @@ func Encode(out io.Writer, blockType string, headers map[string]string) (w io.Wr
}
e := &encoding{
- out: out,
- breaker: newLineBreaker(out, 64),
- crc: crc24Init,
- blockType: bType,
+ out: out,
+ breaker: newLineBreaker(out, 64),
+ blockType: bType,
+ crc: crc24Init,
+ crcEnabled: checksum,
}
e.b64 = base64.NewEncoder(base64.StdEncoding, e.breaker)
return e, nil
}
+
+// Encode returns a WriteCloser which will encode the data written to it in
+// OpenPGP armor.
+func Encode(out io.Writer, blockType string, headers map[string]string) (w io.WriteCloser, err error) {
+ return encode(out, blockType, headers, true)
+}
+
+// EncodeWithChecksumOption returns a WriteCloser which will encode the data written to it in
+// OpenPGP armor and provides the option to include a checksum.
+// When forming ASCII Armor, the CRC24 footer SHOULD NOT be generated,
+// unless interoperability with implementations that require the CRC24 footer
+// to be present is a concern.
+func EncodeWithChecksumOption(out io.Writer, blockType string, headers map[string]string, doChecksum bool) (w io.WriteCloser, err error) {
+ return encode(out, blockType, headers, doChecksum)
+}
diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/canonical_text.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/canonical_text.go
index a94f6150c4..5b40e1375d 100644
--- a/vendor/github.com/ProtonMail/go-crypto/openpgp/canonical_text.go
+++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/canonical_text.go
@@ -30,8 +30,12 @@ func writeCanonical(cw io.Writer, buf []byte, s *int) (int, error) {
if c == '\r' {
*s = 1
} else if c == '\n' {
- cw.Write(buf[start:i])
- cw.Write(newline)
+ if _, err := cw.Write(buf[start:i]); err != nil {
+ return 0, err
+ }
+ if _, err := cw.Write(newline); err != nil {
+ return 0, err
+ }
start = i + 1
}
case 1:
@@ -39,7 +43,9 @@ func writeCanonical(cw io.Writer, buf []byte, s *int) (int, error) {
}
}
- cw.Write(buf[start:])
+ if _, err := cw.Write(buf[start:]); err != nil {
+ return 0, err
+ }
return len(buf), nil
}
diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/ecdh/ecdh.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/ecdh/ecdh.go
index c895bad6bb..db8fb163b6 100644
--- a/vendor/github.com/ProtonMail/go-crypto/openpgp/ecdh/ecdh.go
+++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/ecdh/ecdh.go
@@ -163,13 +163,9 @@ func buildKey(pub *PublicKey, zb []byte, curveOID, fingerprint []byte, stripLead
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 {
+ if _, err := param.Write(fingerprint[:]); 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()
diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/ed25519/ed25519.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/ed25519/ed25519.go
new file mode 100644
index 0000000000..6abdf7c446
--- /dev/null
+++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/ed25519/ed25519.go
@@ -0,0 +1,115 @@
+// Package ed25519 implements the ed25519 signature algorithm for OpenPGP
+// as defined in the Open PGP crypto refresh.
+package ed25519
+
+import (
+ "crypto/subtle"
+ "io"
+
+ "github.com/ProtonMail/go-crypto/openpgp/errors"
+ ed25519lib "github.com/cloudflare/circl/sign/ed25519"
+)
+
+const (
+ // PublicKeySize is the size, in bytes, of public keys in this package.
+ PublicKeySize = ed25519lib.PublicKeySize
+ // SeedSize is the size, in bytes, of private key seeds.
+ // The private key representation used by RFC 8032.
+ SeedSize = ed25519lib.SeedSize
+ // SignatureSize is the size, in bytes, of signatures generated and verified by this package.
+ SignatureSize = ed25519lib.SignatureSize
+)
+
+type PublicKey struct {
+ // Point represents the elliptic curve point of the public key.
+ Point []byte
+}
+
+type PrivateKey struct {
+ PublicKey
+ // Key the private key representation by RFC 8032,
+ // encoded as seed | pub key point.
+ Key []byte
+}
+
+// NewPublicKey creates a new empty ed25519 public key.
+func NewPublicKey() *PublicKey {
+ return &PublicKey{}
+}
+
+// NewPrivateKey creates a new empty private key referencing the public key.
+func NewPrivateKey(key PublicKey) *PrivateKey {
+ return &PrivateKey{
+ PublicKey: key,
+ }
+}
+
+// Seed returns the ed25519 private key secret seed.
+// The private key representation by RFC 8032.
+func (pk *PrivateKey) Seed() []byte {
+ return pk.Key[:SeedSize]
+}
+
+// MarshalByteSecret returns the underlying 32 byte seed of the private key.
+func (pk *PrivateKey) MarshalByteSecret() []byte {
+ return pk.Seed()
+}
+
+// UnmarshalByteSecret computes the private key from the secret seed
+// and stores it in the private key object.
+func (sk *PrivateKey) UnmarshalByteSecret(seed []byte) error {
+ sk.Key = ed25519lib.NewKeyFromSeed(seed)
+ return nil
+}
+
+// GenerateKey generates a fresh private key with the provided randomness source.
+func GenerateKey(rand io.Reader) (*PrivateKey, error) {
+ publicKey, privateKey, err := ed25519lib.GenerateKey(rand)
+ if err != nil {
+ return nil, err
+ }
+ privateKeyOut := new(PrivateKey)
+ privateKeyOut.PublicKey.Point = publicKey[:]
+ privateKeyOut.Key = privateKey[:]
+ return privateKeyOut, nil
+}
+
+// Sign signs a message with the ed25519 algorithm.
+// priv MUST be a valid key! Check this with Validate() before use.
+func Sign(priv *PrivateKey, message []byte) ([]byte, error) {
+ return ed25519lib.Sign(priv.Key, message), nil
+}
+
+// Verify verifies an ed25519 signature.
+func Verify(pub *PublicKey, message []byte, signature []byte) bool {
+ return ed25519lib.Verify(pub.Point, message, signature)
+}
+
+// Validate checks if the ed25519 private key is valid.
+func Validate(priv *PrivateKey) error {
+ expectedPrivateKey := ed25519lib.NewKeyFromSeed(priv.Seed())
+ if subtle.ConstantTimeCompare(priv.Key, expectedPrivateKey) == 0 {
+ return errors.KeyInvalidError("ed25519: invalid ed25519 secret")
+ }
+ if subtle.ConstantTimeCompare(priv.PublicKey.Point, expectedPrivateKey[SeedSize:]) == 0 {
+ return errors.KeyInvalidError("ed25519: invalid ed25519 public key")
+ }
+ return nil
+}
+
+// ENCODING/DECODING signature:
+
+// WriteSignature encodes and writes an ed25519 signature to writer.
+func WriteSignature(writer io.Writer, signature []byte) error {
+ _, err := writer.Write(signature)
+ return err
+}
+
+// ReadSignature decodes an ed25519 signature from a reader.
+func ReadSignature(reader io.Reader) ([]byte, error) {
+ signature := make([]byte, SignatureSize)
+ if _, err := io.ReadFull(reader, signature); err != nil {
+ return nil, err
+ }
+ return signature, nil
+}
diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/ed448/ed448.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/ed448/ed448.go
new file mode 100644
index 0000000000..b11fb4fb17
--- /dev/null
+++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/ed448/ed448.go
@@ -0,0 +1,119 @@
+// Package ed448 implements the ed448 signature algorithm for OpenPGP
+// as defined in the Open PGP crypto refresh.
+package ed448
+
+import (
+ "crypto/subtle"
+ "io"
+
+ "github.com/ProtonMail/go-crypto/openpgp/errors"
+ ed448lib "github.com/cloudflare/circl/sign/ed448"
+)
+
+const (
+ // PublicKeySize is the size, in bytes, of public keys in this package.
+ PublicKeySize = ed448lib.PublicKeySize
+ // SeedSize is the size, in bytes, of private key seeds.
+ // The private key representation used by RFC 8032.
+ SeedSize = ed448lib.SeedSize
+ // SignatureSize is the size, in bytes, of signatures generated and verified by this package.
+ SignatureSize = ed448lib.SignatureSize
+)
+
+type PublicKey struct {
+ // Point represents the elliptic curve point of the public key.
+ Point []byte
+}
+
+type PrivateKey struct {
+ PublicKey
+ // Key the private key representation by RFC 8032,
+ // encoded as seed | public key point.
+ Key []byte
+}
+
+// NewPublicKey creates a new empty ed448 public key.
+func NewPublicKey() *PublicKey {
+ return &PublicKey{}
+}
+
+// NewPrivateKey creates a new empty private key referencing the public key.
+func NewPrivateKey(key PublicKey) *PrivateKey {
+ return &PrivateKey{
+ PublicKey: key,
+ }
+}
+
+// Seed returns the ed448 private key secret seed.
+// The private key representation by RFC 8032.
+func (pk *PrivateKey) Seed() []byte {
+ return pk.Key[:SeedSize]
+}
+
+// MarshalByteSecret returns the underlying seed of the private key.
+func (pk *PrivateKey) MarshalByteSecret() []byte {
+ return pk.Seed()
+}
+
+// UnmarshalByteSecret computes the private key from the secret seed
+// and stores it in the private key object.
+func (sk *PrivateKey) UnmarshalByteSecret(seed []byte) error {
+ sk.Key = ed448lib.NewKeyFromSeed(seed)
+ return nil
+}
+
+// GenerateKey generates a fresh private key with the provided randomness source.
+func GenerateKey(rand io.Reader) (*PrivateKey, error) {
+ publicKey, privateKey, err := ed448lib.GenerateKey(rand)
+ if err != nil {
+ return nil, err
+ }
+ privateKeyOut := new(PrivateKey)
+ privateKeyOut.PublicKey.Point = publicKey[:]
+ privateKeyOut.Key = privateKey[:]
+ return privateKeyOut, nil
+}
+
+// Sign signs a message with the ed448 algorithm.
+// priv MUST be a valid key! Check this with Validate() before use.
+func Sign(priv *PrivateKey, message []byte) ([]byte, error) {
+ // Ed448 is used with the empty string as a context string.
+ // See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-08#section-13.7
+ return ed448lib.Sign(priv.Key, message, ""), nil
+}
+
+// Verify verifies a ed448 signature
+func Verify(pub *PublicKey, message []byte, signature []byte) bool {
+ // Ed448 is used with the empty string as a context string.
+ // See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-08#section-13.7
+ return ed448lib.Verify(pub.Point, message, signature, "")
+}
+
+// Validate checks if the ed448 private key is valid
+func Validate(priv *PrivateKey) error {
+ expectedPrivateKey := ed448lib.NewKeyFromSeed(priv.Seed())
+ if subtle.ConstantTimeCompare(priv.Key, expectedPrivateKey) == 0 {
+ return errors.KeyInvalidError("ed448: invalid ed448 secret")
+ }
+ if subtle.ConstantTimeCompare(priv.PublicKey.Point, expectedPrivateKey[SeedSize:]) == 0 {
+ return errors.KeyInvalidError("ed448: invalid ed448 public key")
+ }
+ return nil
+}
+
+// ENCODING/DECODING signature:
+
+// WriteSignature encodes and writes an ed448 signature to writer.
+func WriteSignature(writer io.Writer, signature []byte) error {
+ _, err := writer.Write(signature)
+ return err
+}
+
+// ReadSignature decodes an ed448 signature from a reader.
+func ReadSignature(reader io.Reader) ([]byte, error) {
+ signature := make([]byte, SignatureSize)
+ if _, err := io.ReadFull(reader, signature); err != nil {
+ return nil, err
+ }
+ return signature, nil
+}
diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/errors/errors.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/errors/errors.go
index 17e2bcfed2..0eb3937b39 100644
--- a/vendor/github.com/ProtonMail/go-crypto/openpgp/errors/errors.go
+++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/errors/errors.go
@@ -9,6 +9,18 @@ import (
"strconv"
)
+var (
+ // ErrDecryptSessionKeyParsing is a generic error message for parsing errors in decrypted data
+ // to reduce the risk of oracle attacks.
+ ErrDecryptSessionKeyParsing = DecryptWithSessionKeyError("parsing error")
+ // ErrAEADTagVerification is returned if one of the tag verifications in SEIPDv2 fails
+ ErrAEADTagVerification error = DecryptWithSessionKeyError("AEAD tag verification failed")
+ // ErrMDCHashMismatch
+ ErrMDCHashMismatch error = SignatureError("MDC hash mismatch")
+ // ErrMDCMissing
+ ErrMDCMissing error = SignatureError("MDC packet not found")
+)
+
// A StructuralError is returned when OpenPGP data is found to be syntactically
// invalid.
type StructuralError string
@@ -17,6 +29,34 @@ func (s StructuralError) Error() string {
return "openpgp: invalid data: " + string(s)
}
+// A DecryptWithSessionKeyError is returned when a failure occurs when reading from symmetrically decrypted data or
+// an authentication tag verification fails.
+// Such an error indicates that the supplied session key is likely wrong or the data got corrupted.
+type DecryptWithSessionKeyError string
+
+func (s DecryptWithSessionKeyError) Error() string {
+ return "openpgp: decryption with session key failed: " + string(s)
+}
+
+// HandleSensitiveParsingError handles parsing errors when reading data from potentially decrypted data.
+// The function makes parsing errors generic to reduce the risk of oracle attacks in SEIPDv1.
+func HandleSensitiveParsingError(err error, decrypted bool) error {
+ if !decrypted {
+ // Data was not encrypted so we return the inner error.
+ return err
+ }
+ // The data is read from a stream that decrypts using a session key;
+ // therefore, we need to handle parsing errors appropriately.
+ // This is essential to mitigate the risk of oracle attacks.
+ if decError, ok := err.(*DecryptWithSessionKeyError); ok {
+ return decError
+ }
+ if decError, ok := err.(DecryptWithSessionKeyError); ok {
+ return decError
+ }
+ return ErrDecryptSessionKeyParsing
+}
+
// UnsupportedError indicates that, although the OpenPGP data is valid, it
// makes use of currently unimplemented features.
type UnsupportedError string
@@ -41,9 +81,6 @@ func (b SignatureError) Error() string {
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 {
@@ -58,6 +95,14 @@ func (ke keyExpiredError) Error() string {
return "openpgp: key expired"
}
+var ErrSignatureOlderThanKey error = signatureOlderThanKeyError(0)
+
+type signatureOlderThanKeyError int
+
+func (ske signatureOlderThanKeyError) Error() string {
+ return "openpgp: signature is older than the key"
+}
+
var ErrKeyExpired error = keyExpiredError(0)
type keyIncorrectError int
@@ -92,12 +137,24 @@ func (keyRevokedError) Error() string {
var ErrKeyRevoked error = keyRevokedError(0)
+type WeakAlgorithmError string
+
+func (e WeakAlgorithmError) Error() string {
+ return "openpgp: weak algorithms are rejected: " + string(e)
+}
+
type UnknownPacketTypeError uint8
func (upte UnknownPacketTypeError) Error() string {
return "openpgp: unknown packet type: " + strconv.Itoa(int(upte))
}
+type CriticalUnknownPacketTypeError uint8
+
+func (upte CriticalUnknownPacketTypeError) Error() string {
+ return "openpgp: unknown critical 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
@@ -114,3 +171,10 @@ type ErrDummyPrivateKey string
func (dke ErrDummyPrivateKey) Error() string {
return "openpgp: s2k GNU dummy key: " + string(dke)
}
+
+// ErrMalformedMessage results when the packet sequence is incorrect
+type ErrMalformedMessage string
+
+func (dke ErrMalformedMessage) Error() string {
+ return "openpgp: malformed message " + string(dke)
+}
diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/algorithm/cipher.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/algorithm/cipher.go
index 5760cff80e..c76a75bcda 100644
--- a/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/algorithm/cipher.go
+++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/algorithm/cipher.go
@@ -51,24 +51,14 @@ 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:
+ case AES192, TripleDES:
return 24
case AES256:
return 32
diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/curve_info.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/curve_info.go
index 35751034dd..0da2d0d852 100644
--- a/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/curve_info.go
+++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/curve_info.go
@@ -4,11 +4,14 @@ package ecc
import (
"bytes"
"crypto/elliptic"
+
"github.com/ProtonMail/go-crypto/bitcurves"
"github.com/ProtonMail/go-crypto/brainpool"
"github.com/ProtonMail/go-crypto/openpgp/internal/encoding"
)
+const Curve25519GenName = "Curve25519"
+
type CurveInfo struct {
GenName string
Oid *encoding.OID
@@ -42,19 +45,19 @@ var Curves = []CurveInfo{
},
{
// Curve25519
- GenName: "Curve25519",
+ GenName: Curve25519GenName,
Oid: encoding.NewOID([]byte{0x2B, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01}),
Curve: NewCurve25519(),
},
{
- // X448
+ // x448
GenName: "Curve448",
Oid: encoding.NewOID([]byte{0x2B, 0x65, 0x6F}),
Curve: NewX448(),
},
{
// Ed25519
- GenName: "Curve25519",
+ GenName: Curve25519GenName,
Oid: encoding.NewOID([]byte{0x2B, 0x06, 0x01, 0x04, 0x01, 0xDA, 0x47, 0x0F, 0x01}),
Curve: NewEd25519(),
},
diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/ed25519.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/ed25519.go
index 54a08a8a38..5a4c3a8596 100644
--- a/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/ed25519.go
+++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/ed25519.go
@@ -2,6 +2,7 @@
package ecc
import (
+ "bytes"
"crypto/subtle"
"io"
@@ -90,7 +91,14 @@ func (c *ed25519) GenerateEdDSA(rand io.Reader) (pub, priv []byte, err error) {
}
func getEd25519Sk(publicKey, privateKey []byte) ed25519lib.PrivateKey {
- return append(privateKey, publicKey...)
+ privateKeyCap, privateKeyLen, publicKeyLen := cap(privateKey), len(privateKey), len(publicKey)
+
+ if privateKeyCap >= privateKeyLen+publicKeyLen &&
+ bytes.Equal(privateKey[privateKeyLen:privateKeyLen+publicKeyLen], publicKey) {
+ return privateKey[:privateKeyLen+publicKeyLen]
+ }
+
+ return append(privateKey[:privateKeyLen:privateKeyLen], publicKey...)
}
func (c *ed25519) Sign(publicKey, privateKey, message []byte) (sig []byte, err error) {
diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/ed448.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/ed448.go
index 18cd80434b..b6edda7480 100644
--- a/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/ed448.go
+++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/ed448.go
@@ -2,6 +2,7 @@
package ecc
import (
+ "bytes"
"crypto/subtle"
"io"
@@ -84,7 +85,14 @@ func (c *ed448) GenerateEdDSA(rand io.Reader) (pub, priv []byte, err error) {
}
func getEd448Sk(publicKey, privateKey []byte) ed448lib.PrivateKey {
- return append(privateKey, publicKey...)
+ privateKeyCap, privateKeyLen, publicKeyLen := cap(privateKey), len(privateKey), len(publicKey)
+
+ if privateKeyCap >= privateKeyLen+publicKeyLen &&
+ bytes.Equal(privateKey[privateKeyLen:privateKeyLen+publicKeyLen], publicKey) {
+ return privateKey[:privateKeyLen+publicKeyLen]
+ }
+
+ return append(privateKey[:privateKeyLen:privateKeyLen], publicKey...)
}
func (c *ed448) Sign(publicKey, privateKey, message []byte) (sig []byte, err error) {
diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/key_generation.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/key_generation.go
index 0e71934cd9..77213f66be 100644
--- a/vendor/github.com/ProtonMail/go-crypto/openpgp/key_generation.go
+++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/key_generation.go
@@ -15,11 +15,15 @@ import (
"github.com/ProtonMail/go-crypto/openpgp/ecdh"
"github.com/ProtonMail/go-crypto/openpgp/ecdsa"
+ "github.com/ProtonMail/go-crypto/openpgp/ed25519"
+ "github.com/ProtonMail/go-crypto/openpgp/ed448"
"github.com/ProtonMail/go-crypto/openpgp/eddsa"
"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/packet"
+ "github.com/ProtonMail/go-crypto/openpgp/x25519"
+ "github.com/ProtonMail/go-crypto/openpgp/x448"
)
// NewEntity returns an Entity that contains a fresh RSA/RSA keypair with a
@@ -36,8 +40,10 @@ func NewEntity(name, comment, email string, config *packet.Config) (*Entity, err
return nil, err
}
primary := packet.NewSignerPrivateKey(creationTime, primaryPrivRaw)
- if config != nil && config.V5Keys {
- primary.UpgradeToV5()
+ if config.V6() {
+ if err := primary.UpgradeToV6(); err != nil {
+ return nil, err
+ }
}
e := &Entity{
@@ -45,9 +51,25 @@ func NewEntity(name, comment, email string, config *packet.Config) (*Entity, err
PrivateKey: primary,
Identities: make(map[string]*Identity),
Subkeys: []Subkey{},
+ Signatures: []*packet.Signature{},
+ }
+
+ if config.V6() {
+ // In v6 keys algorithm preferences should be stored in direct key signatures
+ selfSignature := createSignaturePacket(&primary.PublicKey, packet.SigTypeDirectSignature, config)
+ err = writeKeyProperties(selfSignature, creationTime, keyLifetimeSecs, config)
+ if err != nil {
+ return nil, err
+ }
+ err = selfSignature.SignDirectKeyBinding(&primary.PublicKey, primary, config)
+ if err != nil {
+ return nil, err
+ }
+ e.Signatures = append(e.Signatures, selfSignature)
+ e.SelfSignature = selfSignature
}
- err = e.addUserId(name, comment, email, config, creationTime, keyLifetimeSecs)
+ err = e.addUserId(name, comment, email, config, creationTime, keyLifetimeSecs, !config.V6())
if err != nil {
return nil, err
}
@@ -65,32 +87,19 @@ func NewEntity(name, comment, email string, config *packet.Config) (*Entity, err
func (t *Entity) AddUserId(name, comment, email string, config *packet.Config) error {
creationTime := config.Now()
keyLifetimeSecs := config.KeyLifetime()
- return t.addUserId(name, comment, email, config, creationTime, keyLifetimeSecs)
+ return t.addUserId(name, comment, email, config, creationTime, keyLifetimeSecs, !config.V6())
}
-func (t *Entity) addUserId(name, comment, email string, config *packet.Config, creationTime time.Time, keyLifetimeSecs uint32) error {
- uid := packet.NewUserId(name, comment, email)
- if uid == nil {
- return errors.InvalidArgumentError("user id field contained invalid characters")
- }
-
- if _, ok := t.Identities[uid.Id]; ok {
- return errors.InvalidArgumentError("user id exist")
- }
-
- primary := t.PrivateKey
-
- isPrimaryId := len(t.Identities) == 0
+func writeKeyProperties(selfSignature *packet.Signature, creationTime time.Time, keyLifetimeSecs uint32, config *packet.Config) error {
+ advertiseAead := config.AEAD() != nil
- selfSignature := createSignaturePacket(&primary.PublicKey, packet.SigTypePositiveCert, config)
selfSignature.CreationTime = creationTime
selfSignature.KeyLifetimeSecs = &keyLifetimeSecs
- selfSignature.IsPrimaryId = &isPrimaryId
selfSignature.FlagsValid = true
selfSignature.FlagSign = true
selfSignature.FlagCertify = true
selfSignature.SEIPDv1 = true // true by default, see 5.8 vs. 5.14
- selfSignature.SEIPDv2 = config.AEAD() != nil
+ selfSignature.SEIPDv2 = advertiseAead
// Set the PreferredHash for the SelfSignature from the packet.Config.
// If it is not the must-implement algorithm from rfc4880bis, append that.
@@ -119,18 +128,44 @@ func (t *Entity) addUserId(name, comment, email string, config *packet.Config, c
selfSignature.PreferredCompression = append(selfSignature.PreferredCompression, uint8(config.Compression()))
}
- // And for DefaultMode.
- modes := []uint8{uint8(config.AEAD().Mode())}
- if config.AEAD().Mode() != packet.AEADModeOCB {
- modes = append(modes, uint8(packet.AEADModeOCB))
+ if advertiseAead {
+ // Get the preferred AEAD mode from the packet.Config.
+ // If it is not the must-implement algorithm from rfc9580, append that.
+ modes := []uint8{uint8(config.AEAD().Mode())}
+ if config.AEAD().Mode() != packet.AEADModeOCB {
+ modes = append(modes, uint8(packet.AEADModeOCB))
+ }
+
+ // For preferred (AES256, GCM), we'll generate (AES256, GCM), (AES256, OCB), (AES128, GCM), (AES128, OCB)
+ for _, cipher := range selfSignature.PreferredSymmetric {
+ for _, mode := range modes {
+ selfSignature.PreferredCipherSuites = append(selfSignature.PreferredCipherSuites, [2]uint8{cipher, mode})
+ }
+ }
+ }
+ return nil
+}
+
+func (t *Entity) addUserId(name, comment, email string, config *packet.Config, creationTime time.Time, keyLifetimeSecs uint32, writeProperties bool) error {
+ uid := packet.NewUserId(name, comment, email)
+ if uid == nil {
+ return errors.InvalidArgumentError("user id field contained invalid characters")
+ }
+
+ if _, ok := t.Identities[uid.Id]; ok {
+ return errors.InvalidArgumentError("user id exist")
}
- // For preferred (AES256, GCM), we'll generate (AES256, GCM), (AES256, OCB), (AES128, GCM), (AES128, OCB)
- for _, cipher := range selfSignature.PreferredSymmetric {
- for _, mode := range modes {
- selfSignature.PreferredCipherSuites = append(selfSignature.PreferredCipherSuites, [2]uint8{cipher, mode})
+ primary := t.PrivateKey
+ isPrimaryId := len(t.Identities) == 0
+ selfSignature := createSignaturePacket(&primary.PublicKey, packet.SigTypePositiveCert, config)
+ if writeProperties {
+ err := writeKeyProperties(selfSignature, creationTime, keyLifetimeSecs, config)
+ if err != nil {
+ return err
}
}
+ selfSignature.IsPrimaryId = &isPrimaryId
// User ID binding signature
err := selfSignature.SignUserId(uid.Id, &primary.PublicKey, primary, config)
@@ -158,8 +193,10 @@ func (e *Entity) AddSigningSubkey(config *packet.Config) error {
}
sub := packet.NewSignerPrivateKey(creationTime, subPrivRaw)
sub.IsSubkey = true
- if config != nil && config.V5Keys {
- sub.UpgradeToV5()
+ if config.V6() {
+ if err := sub.UpgradeToV6(); err != nil {
+ return err
+ }
}
subkey := Subkey{
@@ -203,8 +240,10 @@ func (e *Entity) addEncryptionSubkey(config *packet.Config, creationTime time.Ti
}
sub := packet.NewDecrypterPrivateKey(creationTime, subPrivRaw)
sub.IsSubkey = true
- if config != nil && config.V5Keys {
- sub.UpgradeToV5()
+ if config.V6() {
+ if err := sub.UpgradeToV6(); err != nil {
+ return err
+ }
}
subkey := Subkey{
@@ -242,6 +281,11 @@ func newSigner(config *packet.Config) (signer interface{}, err error) {
}
return rsa.GenerateKey(config.Random(), bits)
case packet.PubKeyAlgoEdDSA:
+ if config.V6() {
+ // Implementations MUST NOT accept or generate v6 key material
+ // using the deprecated OIDs.
+ return nil, errors.InvalidArgumentError("EdDSALegacy cannot be used for v6 keys")
+ }
curve := ecc.FindEdDSAByGenName(string(config.CurveName()))
if curve == nil {
return nil, errors.InvalidArgumentError("unsupported curve")
@@ -263,6 +307,18 @@ func newSigner(config *packet.Config) (signer interface{}, err error) {
return nil, err
}
return priv, nil
+ case packet.PubKeyAlgoEd25519:
+ priv, err := ed25519.GenerateKey(config.Random())
+ if err != nil {
+ return nil, err
+ }
+ return priv, nil
+ case packet.PubKeyAlgoEd448:
+ priv, err := ed448.GenerateKey(config.Random())
+ if err != nil {
+ return nil, err
+ }
+ return priv, nil
default:
return nil, errors.InvalidArgumentError("unsupported public key algorithm")
}
@@ -285,6 +341,13 @@ func newDecrypter(config *packet.Config) (decrypter interface{}, err error) {
case packet.PubKeyAlgoEdDSA, packet.PubKeyAlgoECDSA:
fallthrough // When passing EdDSA or ECDSA, we generate an ECDH subkey
case packet.PubKeyAlgoECDH:
+ if config.V6() &&
+ (config.CurveName() == packet.Curve25519 ||
+ config.CurveName() == packet.Curve448) {
+ // Implementations MUST NOT accept or generate v6 key material
+ // using the deprecated OIDs.
+ return nil, errors.InvalidArgumentError("ECDH with Curve25519/448 legacy cannot be used for v6 keys")
+ }
var kdf = ecdh.KDF{
Hash: algorithm.SHA512,
Cipher: algorithm.AES256,
@@ -294,6 +357,10 @@ func newDecrypter(config *packet.Config) (decrypter interface{}, err error) {
return nil, errors.InvalidArgumentError("unsupported curve")
}
return ecdh.GenerateKey(config.Random(), curve, kdf)
+ case packet.PubKeyAlgoEd25519, packet.PubKeyAlgoX25519: // When passing Ed25519, we generate an x25519 subkey
+ return x25519.GenerateKey(config.Random())
+ case packet.PubKeyAlgoEd448, packet.PubKeyAlgoX448: // When passing Ed448, we generate an x448 subkey
+ return x448.GenerateKey(config.Random())
default:
return nil, errors.InvalidArgumentError("unsupported public key algorithm")
}
@@ -302,7 +369,7 @@ func newDecrypter(config *packet.Config) (decrypter interface{}, err error) {
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.
+// given bit size, using the given random source and pre-populated primes.
func generateRSAKeyWithPrimes(random io.Reader, nprimes int, bits int, prepopulatedPrimes []*big.Int) (*rsa.PrivateKey, error) {
priv := new(rsa.PrivateKey)
priv.E = 65537
diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/keys.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/keys.go
index 2d7b0cf373..a071353e2e 100644
--- a/vendor/github.com/ProtonMail/go-crypto/openpgp/keys.go
+++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/keys.go
@@ -6,6 +6,7 @@ package openpgp
import (
goerrors "errors"
+ "fmt"
"io"
"time"
@@ -24,11 +25,13 @@ var PrivateKeyType = "PGP PRIVATE KEY BLOCK"
// (which must be a signing key), one or more identities claimed by that key,
// and zero or more subkeys, which may be encryption keys.
type Entity struct {
- PrimaryKey *packet.PublicKey
- PrivateKey *packet.PrivateKey
- Identities map[string]*Identity // indexed by Identity.Name
- Revocations []*packet.Signature
- Subkeys []Subkey
+ PrimaryKey *packet.PublicKey
+ PrivateKey *packet.PrivateKey
+ Identities map[string]*Identity // indexed by Identity.Name
+ Revocations []*packet.Signature
+ Subkeys []Subkey
+ SelfSignature *packet.Signature // Direct-key self signature of the PrimaryKey (contains primary key properties in v6)
+ Signatures []*packet.Signature // all (potentially unverified) self-signatures, revocations, and third-party signatures
}
// An Identity represents an identity claimed by an Entity and zero or more
@@ -120,12 +123,12 @@ func shouldPreferIdentity(existingId, potentialNewId *Identity) bool {
// given Entity.
func (e *Entity) EncryptionKey(now time.Time) (Key, bool) {
// Fail to find any encryption key if the...
- i := e.PrimaryIdentity()
- if e.PrimaryKey.KeyExpired(i.SelfSignature, now) || // primary key has expired
- i.SelfSignature == nil || // user ID has no self-signature
- i.SelfSignature.SigExpired(now) || // user ID self-signature has expired
+ primarySelfSignature, primaryIdentity := e.PrimarySelfSignature()
+ if primarySelfSignature == nil || // no self-signature found
+ e.PrimaryKey.KeyExpired(primarySelfSignature, now) || // primary key has expired
e.Revoked(now) || // primary key has been revoked
- i.Revoked(now) { // user ID has been revoked
+ primarySelfSignature.SigExpired(now) || // user ID or or direct self-signature has expired
+ (primaryIdentity != nil && primaryIdentity.Revoked(now)) { // user ID has been revoked (for v4 keys)
return Key{}, false
}
@@ -152,9 +155,9 @@ func (e *Entity) EncryptionKey(now time.Time) (Key, bool) {
// If we don't have any subkeys for encryption and the primary key
// is marked as OK to encrypt with, then we can use it.
- if i.SelfSignature.FlagsValid && i.SelfSignature.FlagEncryptCommunications &&
+ if primarySelfSignature.FlagsValid && primarySelfSignature.FlagEncryptCommunications &&
e.PrimaryKey.PubKeyAlgo.CanEncrypt() {
- return Key{e, e.PrimaryKey, e.PrivateKey, i.SelfSignature, e.Revocations}, true
+ return Key{e, e.PrimaryKey, e.PrivateKey, primarySelfSignature, e.Revocations}, true
}
return Key{}, false
@@ -186,12 +189,12 @@ func (e *Entity) SigningKeyById(now time.Time, id uint64) (Key, bool) {
func (e *Entity) signingKeyByIdUsage(now time.Time, id uint64, flags int) (Key, bool) {
// Fail to find any signing key if the...
- i := e.PrimaryIdentity()
- if e.PrimaryKey.KeyExpired(i.SelfSignature, now) || // primary key has expired
- i.SelfSignature == nil || // user ID has no self-signature
- i.SelfSignature.SigExpired(now) || // user ID self-signature has expired
+ primarySelfSignature, primaryIdentity := e.PrimarySelfSignature()
+ if primarySelfSignature == nil || // no self-signature found
+ e.PrimaryKey.KeyExpired(primarySelfSignature, now) || // primary key has expired
e.Revoked(now) || // primary key has been revoked
- i.Revoked(now) { // user ID has been revoked
+ primarySelfSignature.SigExpired(now) || // user ID or direct self-signature has expired
+ (primaryIdentity != nil && primaryIdentity.Revoked(now)) { // user ID has been revoked (for v4 keys)
return Key{}, false
}
@@ -220,12 +223,12 @@ func (e *Entity) signingKeyByIdUsage(now time.Time, id uint64, flags int) (Key,
// If we don't have any subkeys for signing and the primary key
// is marked as OK to sign with, then we can use it.
- if i.SelfSignature.FlagsValid &&
- (flags&packet.KeyFlagCertify == 0 || i.SelfSignature.FlagCertify) &&
- (flags&packet.KeyFlagSign == 0 || i.SelfSignature.FlagSign) &&
+ if primarySelfSignature.FlagsValid &&
+ (flags&packet.KeyFlagCertify == 0 || primarySelfSignature.FlagCertify) &&
+ (flags&packet.KeyFlagSign == 0 || primarySelfSignature.FlagSign) &&
e.PrimaryKey.PubKeyAlgo.CanSign() &&
(id == 0 || e.PrimaryKey.KeyId == id) {
- return Key{e, e.PrimaryKey, e.PrivateKey, i.SelfSignature, e.Revocations}, true
+ return Key{e, e.PrimaryKey, e.PrivateKey, primarySelfSignature, e.Revocations}, true
}
// No keys with a valid Signing Flag or no keys matched the id passed in
@@ -259,7 +262,7 @@ func (e *Entity) EncryptPrivateKeys(passphrase []byte, config *packet.Config) er
var keysToEncrypt []*packet.PrivateKey
// Add entity private key to encrypt.
if e.PrivateKey != nil && !e.PrivateKey.Dummy() && !e.PrivateKey.Encrypted {
- keysToEncrypt = append(keysToEncrypt, e.PrivateKey)
+ keysToEncrypt = append(keysToEncrypt, e.PrivateKey)
}
// Add subkeys to encrypt.
@@ -271,7 +274,7 @@ func (e *Entity) EncryptPrivateKeys(passphrase []byte, config *packet.Config) er
return packet.EncryptPrivateKeys(keysToEncrypt, passphrase, config)
}
-// DecryptPrivateKeys decrypts all encrypted keys in the entitiy with the given passphrase.
+// DecryptPrivateKeys decrypts all encrypted keys in the entity with the given passphrase.
// Avoids recomputation of similar s2k key derivations. Public keys and dummy keys are ignored,
// and don't cause an error to be returned.
func (e *Entity) DecryptPrivateKeys(passphrase []byte) error {
@@ -284,7 +287,7 @@ func (e *Entity) DecryptPrivateKeys(passphrase []byte) error {
// Add subkeys to decrypt.
for _, sub := range e.Subkeys {
if sub.PrivateKey != nil && !sub.PrivateKey.Dummy() && sub.PrivateKey.Encrypted {
- keysToDecrypt = append(keysToDecrypt, sub.PrivateKey)
+ keysToDecrypt = append(keysToDecrypt, sub.PrivateKey)
}
}
return packet.DecryptPrivateKeys(keysToDecrypt, passphrase)
@@ -318,8 +321,7 @@ type EntityList []*Entity
func (el EntityList) KeysById(id uint64) (keys []Key) {
for _, e := range el {
if e.PrimaryKey.KeyId == id {
- ident := e.PrimaryIdentity()
- selfSig := ident.SelfSignature
+ selfSig, _ := e.PrimarySelfSignature()
keys = append(keys, Key{e, e.PrimaryKey, e.PrivateKey, selfSig, e.Revocations})
}
@@ -441,7 +443,6 @@ func readToNextPublicKey(packets *packet.Reader) (err error) {
return
} else if err != nil {
if _, ok := err.(errors.UnsupportedError); ok {
- err = nil
continue
}
return
@@ -479,6 +480,7 @@ func ReadEntity(packets *packet.Reader) (*Entity, error) {
}
var revocations []*packet.Signature
+ var directSignatures []*packet.Signature
EachPacket:
for {
p, err := packets.Next()
@@ -497,9 +499,7 @@ EachPacket:
if pkt.SigType == packet.SigTypeKeyRevocation {
revocations = append(revocations, pkt)
} else if pkt.SigType == packet.SigTypeDirectSignature {
- // TODO: RFC4880 5.2.1 permits signatures
- // directly on keys (eg. to bind additional
- // revocation keys).
+ directSignatures = append(directSignatures, pkt)
}
// Else, ignoring the signature as it does not follow anything
// we would know to attach it to.
@@ -522,12 +522,39 @@ EachPacket:
return nil, err
}
default:
- // we ignore unknown packets
+ // we ignore unknown packets.
}
}
- if len(e.Identities) == 0 {
- return nil, errors.StructuralError("entity without any identities")
+ if len(e.Identities) == 0 && e.PrimaryKey.Version < 6 {
+ return nil, errors.StructuralError(fmt.Sprintf("v%d entity without any identities", e.PrimaryKey.Version))
+ }
+
+ // An implementation MUST ensure that a valid direct-key signature is present before using a v6 key.
+ if e.PrimaryKey.Version == 6 {
+ if len(directSignatures) == 0 {
+ return nil, errors.StructuralError("v6 entity without a valid direct-key signature")
+ }
+ // Select main direct key signature.
+ var mainDirectKeySelfSignature *packet.Signature
+ for _, directSignature := range directSignatures {
+ if directSignature.SigType == packet.SigTypeDirectSignature &&
+ directSignature.CheckKeyIdOrFingerprint(e.PrimaryKey) &&
+ (mainDirectKeySelfSignature == nil ||
+ directSignature.CreationTime.After(mainDirectKeySelfSignature.CreationTime)) {
+ mainDirectKeySelfSignature = directSignature
+ }
+ }
+ if mainDirectKeySelfSignature == nil {
+ return nil, errors.StructuralError("no valid direct-key self-signature for v6 primary key found")
+ }
+ // Check that the main self-signature is valid.
+ err = e.PrimaryKey.VerifyDirectKeySignature(mainDirectKeySelfSignature)
+ if err != nil {
+ return nil, errors.StructuralError("invalid direct-key self-signature for v6 primary key")
+ }
+ e.SelfSignature = mainDirectKeySelfSignature
+ e.Signatures = directSignatures
}
for _, revocation := range revocations {
@@ -672,6 +699,12 @@ func (e *Entity) serializePrivate(w io.Writer, config *packet.Config, reSign boo
return err
}
}
+ for _, directSignature := range e.Signatures {
+ err := directSignature.Serialize(w)
+ if err != nil {
+ return err
+ }
+ }
for _, ident := range e.Identities {
err = ident.UserId.Serialize(w)
if err != nil {
@@ -738,6 +771,12 @@ func (e *Entity) Serialize(w io.Writer) error {
return err
}
}
+ for _, directSignature := range e.Signatures {
+ err := directSignature.Serialize(w)
+ if err != nil {
+ return err
+ }
+ }
for _, ident := range e.Identities {
err = ident.UserId.Serialize(w)
if err != nil {
@@ -840,3 +879,23 @@ func (e *Entity) RevokeSubkey(sk *Subkey, reason packet.ReasonForRevocation, rea
sk.Revocations = append(sk.Revocations, revSig)
return nil
}
+
+func (e *Entity) primaryDirectSignature() *packet.Signature {
+ return e.SelfSignature
+}
+
+// PrimarySelfSignature searches the entity for the self-signature that stores key preferences.
+// For V4 keys, returns the self-signature of the primary identity, and the identity.
+// For V6 keys, returns the latest valid direct-key self-signature, and no identity (nil).
+// This self-signature is to be used to check the key expiration,
+// algorithm preferences, and so on.
+func (e *Entity) PrimarySelfSignature() (*packet.Signature, *Identity) {
+ if e.PrimaryKey.Version == 6 {
+ return e.primaryDirectSignature(), nil
+ }
+ primaryIdentity := e.PrimaryIdentity()
+ if primaryIdentity == nil {
+ return nil, nil
+ }
+ return primaryIdentity.SelfSignature, primaryIdentity
+}
diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/aead_crypter.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/aead_crypter.go
index cee83bdc7a..2eecd062f5 100644
--- a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/aead_crypter.go
+++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/aead_crypter.go
@@ -88,17 +88,20 @@ func (ar *aeadDecrypter) Read(dst []byte) (n int, err error) {
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)
+ if len(cipherChunk) > 0 {
+ 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
@@ -116,6 +119,12 @@ func (ar *aeadDecrypter) Read(dst []byte) (n int, err error) {
// 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) {
+ if !ar.eof {
+ errChunk := ar.validateFinalTag(ar.peekedBytes)
+ if errChunk != nil {
+ return errChunk
+ }
+ }
return nil
}
@@ -138,7 +147,7 @@ func (ar *aeadDecrypter) openChunk(data []byte) ([]byte, error) {
nonce := ar.computeNextNonce()
plainChunk, err := ar.aead.Open(nil, nonce, chunk, adata)
if err != nil {
- return nil, err
+ return nil, errors.ErrAEADTagVerification
}
ar.bytesProcessed += len(plainChunk)
if err = ar.aeadCrypter.incrementIndex(); err != nil {
@@ -163,9 +172,8 @@ func (ar *aeadDecrypter) validateFinalTag(tag []byte) error {
// ... and total number of encrypted octets
adata = append(adata, amountBytes...)
nonce := ar.computeNextNonce()
- _, err := ar.aead.Open(nil, nonce, tag, adata)
- if err != nil {
- return err
+ if _, err := ar.aead.Open(nil, nonce, tag, adata); err != nil {
+ return errors.ErrAEADTagVerification
}
return nil
}
diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/compressed.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/compressed.go
index 2f5cad71da..0bcb38caca 100644
--- a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/compressed.go
+++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/compressed.go
@@ -8,9 +8,10 @@ import (
"compress/bzip2"
"compress/flate"
"compress/zlib"
- "github.com/ProtonMail/go-crypto/openpgp/errors"
"io"
"strconv"
+
+ "github.com/ProtonMail/go-crypto/openpgp/errors"
)
// Compressed represents a compressed OpenPGP packet. The decompressed contents
@@ -39,6 +40,37 @@ type CompressionConfig struct {
Level int
}
+// decompressionReader ensures that the whole compression packet is read.
+type decompressionReader struct {
+ compressed io.Reader
+ decompressed io.ReadCloser
+ readAll bool
+}
+
+func newDecompressionReader(r io.Reader, decompressor io.ReadCloser) *decompressionReader {
+ return &decompressionReader{
+ compressed: r,
+ decompressed: decompressor,
+ }
+}
+
+func (dr *decompressionReader) Read(data []byte) (n int, err error) {
+ if dr.readAll {
+ return 0, io.EOF
+ }
+ n, err = dr.decompressed.Read(data)
+ if err == io.EOF {
+ dr.readAll = true
+ // Close the decompressor.
+ if errDec := dr.decompressed.Close(); errDec != nil {
+ return n, errDec
+ }
+ // Consume all remaining data from the compressed packet.
+ consumeAll(dr.compressed)
+ }
+ return n, err
+}
+
func (c *Compressed) parse(r io.Reader) error {
var buf [1]byte
_, err := readFull(r, buf[:])
@@ -50,11 +82,15 @@ func (c *Compressed) parse(r io.Reader) error {
case 0:
c.Body = r
case 1:
- c.Body = flate.NewReader(r)
+ c.Body = newDecompressionReader(r, flate.NewReader(r))
case 2:
- c.Body, err = zlib.NewReader(r)
+ decompressor, err := zlib.NewReader(r)
+ if err != nil {
+ return err
+ }
+ c.Body = newDecompressionReader(r, decompressor)
case 3:
- c.Body = bzip2.NewReader(r)
+ c.Body = newDecompressionReader(r, io.NopCloser(bzip2.NewReader(r)))
default:
err = errors.UnsupportedError("unknown compression algorithm: " + strconv.Itoa(int(buf[0])))
}
diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/config.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/config.go
index 04994bec97..8bf8e6e51f 100644
--- a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/config.go
+++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/config.go
@@ -14,6 +14,34 @@ import (
"github.com/ProtonMail/go-crypto/openpgp/s2k"
)
+var (
+ defaultRejectPublicKeyAlgorithms = map[PublicKeyAlgorithm]bool{
+ PubKeyAlgoElGamal: true,
+ PubKeyAlgoDSA: true,
+ }
+ defaultRejectHashAlgorithms = map[crypto.Hash]bool{
+ crypto.MD5: true,
+ crypto.RIPEMD160: true,
+ }
+ defaultRejectMessageHashAlgorithms = map[crypto.Hash]bool{
+ crypto.SHA1: true,
+ crypto.MD5: true,
+ crypto.RIPEMD160: true,
+ }
+ defaultRejectCurves = map[Curve]bool{
+ CurveSecP256k1: true,
+ }
+)
+
+// A global feature flag to indicate v5 support.
+// Can be set via a build tag, e.g.: `go build -tags v5 ./...`
+// If the build tag is missing config_v5.go will set it to true.
+//
+// Disables parsing of v5 keys and v5 signatures.
+// These are non-standard entities, which in the crypto-refresh have been superseded
+// by v6 keys, v6 signatures and SEIPDv2 encrypted data, respectively.
+var V5Disabled = false
+
// Config collects a number of parameters along with sensible defaults.
// A nil *Config is valid and results in all default values.
type Config struct {
@@ -73,9 +101,16 @@ type Config struct {
// **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
+ // V6Keys configures version 6 key generation. If false, this package still
+ // supports version 6 keys, but produces version 4 keys.
+ V6Keys bool
+ // Minimum RSA key size allowed for key generation and message signing, verification and encryption.
+ MinRSABits uint16
+ // Reject insecure algorithms, only works with v2 api
+ RejectPublicKeyAlgorithms map[PublicKeyAlgorithm]bool
+ RejectHashAlgorithms map[crypto.Hash]bool
+ RejectMessageHashAlgorithms map[crypto.Hash]bool
+ RejectCurves map[Curve]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
@@ -104,12 +139,40 @@ type Config struct {
// might be no other way than to tolerate the missing MDC. Setting this flag, allows this
// mode of operation. It should be considered a measure of last resort.
InsecureAllowUnauthenticatedMessages bool
+ // InsecureAllowDecryptionWithSigningKeys allows decryption with keys marked as signing keys in the v2 API.
+ // This setting is potentially insecure, but it is needed as some libraries
+ // ignored key flags when selecting a key for encryption.
+ // Not relevant for the v1 API, as all keys were allowed in decryption.
+ InsecureAllowDecryptionWithSigningKeys bool
// KnownNotations is a map of Notation Data names to bools, which controls
// the notation names that are allowed to be present in critical Notation Data
// signature subpackets.
KnownNotations map[string]bool
// SignatureNotations is a list of Notations to be added to any signatures.
SignatureNotations []*Notation
+ // CheckIntendedRecipients controls, whether the OpenPGP Intended Recipient Fingerprint feature
+ // should be enabled for encryption and decryption.
+ // (See https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-intended-recipient-fingerpr).
+ // When the flag is set, encryption produces Intended Recipient Fingerprint signature sub-packets and decryption
+ // checks whether the key it was encrypted to is one of the included fingerprints in the signature.
+ // If the flag is disabled, no Intended Recipient Fingerprint sub-packets are created or checked.
+ // The default behavior, when the config or flag is nil, is to enable the feature.
+ CheckIntendedRecipients *bool
+ // CacheSessionKey controls if decryption should return the session key used for decryption.
+ // If the flag is set, the session key is cached in the message details struct.
+ CacheSessionKey bool
+ // CheckPacketSequence is a flag that controls if the pgp message reader should strictly check
+ // that the packet sequence conforms with the grammar mandated by rfc4880.
+ // The default behavior, when the config or flag is nil, is to check the packet sequence.
+ CheckPacketSequence *bool
+ // NonDeterministicSignaturesViaNotation is a flag to enable randomization of signatures.
+ // If true, a salt notation is used to randomize signatures generated by v4 and v5 keys
+ // (v6 signatures are always non-deterministic, by design).
+ // This protects EdDSA signatures from potentially leaking the secret key in case of faults (i.e. bitflips) which, in principle, could occur
+ // during the signing computation. It is added to signatures of any algo for simplicity, and as it may also serve as protection in case of
+ // weaknesses in the hash algo, potentially hindering e.g. some chosen-prefix attacks.
+ // The default behavior, when the config or flag is nil, is to enable the feature.
+ NonDeterministicSignaturesViaNotation *bool
}
func (c *Config) Random() io.Reader {
@@ -197,7 +260,7 @@ func (c *Config) S2K() *s2k.Config {
return nil
}
// for backwards compatibility
- if c != nil && c.S2KCount > 0 && c.S2KConfig == nil {
+ if c.S2KCount > 0 && c.S2KConfig == nil {
return &s2k.Config{
S2KCount: c.S2KCount,
}
@@ -233,6 +296,13 @@ func (c *Config) AllowUnauthenticatedMessages() bool {
return c.InsecureAllowUnauthenticatedMessages
}
+func (c *Config) AllowDecryptionWithSigningKeys() bool {
+ if c == nil {
+ return false
+ }
+ return c.InsecureAllowDecryptionWithSigningKeys
+}
+
func (c *Config) KnownNotation(notationName string) bool {
if c == nil {
return false
@@ -246,3 +316,95 @@ func (c *Config) Notations() []*Notation {
}
return c.SignatureNotations
}
+
+func (c *Config) V6() bool {
+ if c == nil {
+ return false
+ }
+ return c.V6Keys
+}
+
+func (c *Config) IntendedRecipients() bool {
+ if c == nil || c.CheckIntendedRecipients == nil {
+ return true
+ }
+ return *c.CheckIntendedRecipients
+}
+
+func (c *Config) RetrieveSessionKey() bool {
+ if c == nil {
+ return false
+ }
+ return c.CacheSessionKey
+}
+
+func (c *Config) MinimumRSABits() uint16 {
+ if c == nil || c.MinRSABits == 0 {
+ return 2047
+ }
+ return c.MinRSABits
+}
+
+func (c *Config) RejectPublicKeyAlgorithm(alg PublicKeyAlgorithm) bool {
+ var rejectedAlgorithms map[PublicKeyAlgorithm]bool
+ if c == nil || c.RejectPublicKeyAlgorithms == nil {
+ // Default
+ rejectedAlgorithms = defaultRejectPublicKeyAlgorithms
+ } else {
+ rejectedAlgorithms = c.RejectPublicKeyAlgorithms
+ }
+ return rejectedAlgorithms[alg]
+}
+
+func (c *Config) RejectHashAlgorithm(hash crypto.Hash) bool {
+ var rejectedAlgorithms map[crypto.Hash]bool
+ if c == nil || c.RejectHashAlgorithms == nil {
+ // Default
+ rejectedAlgorithms = defaultRejectHashAlgorithms
+ } else {
+ rejectedAlgorithms = c.RejectHashAlgorithms
+ }
+ return rejectedAlgorithms[hash]
+}
+
+func (c *Config) RejectMessageHashAlgorithm(hash crypto.Hash) bool {
+ var rejectedAlgorithms map[crypto.Hash]bool
+ if c == nil || c.RejectMessageHashAlgorithms == nil {
+ // Default
+ rejectedAlgorithms = defaultRejectMessageHashAlgorithms
+ } else {
+ rejectedAlgorithms = c.RejectMessageHashAlgorithms
+ }
+ return rejectedAlgorithms[hash]
+}
+
+func (c *Config) RejectCurve(curve Curve) bool {
+ var rejectedCurve map[Curve]bool
+ if c == nil || c.RejectCurves == nil {
+ // Default
+ rejectedCurve = defaultRejectCurves
+ } else {
+ rejectedCurve = c.RejectCurves
+ }
+ return rejectedCurve[curve]
+}
+
+func (c *Config) StrictPacketSequence() bool {
+ if c == nil || c.CheckPacketSequence == nil {
+ return true
+ }
+ return *c.CheckPacketSequence
+}
+
+func (c *Config) RandomizeSignaturesViaNotation() bool {
+ if c == nil || c.NonDeterministicSignaturesViaNotation == nil {
+ return true
+ }
+ return *c.NonDeterministicSignaturesViaNotation
+}
+
+// BoolPointer is a helper function to set a boolean pointer in the Config.
+// e.g., config.CheckPacketSequence = BoolPointer(true)
+func BoolPointer(value bool) *bool {
+ return &value
+}
diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/config_v5.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/config_v5.go
new file mode 100644
index 0000000000..f2415906b9
--- /dev/null
+++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/config_v5.go
@@ -0,0 +1,7 @@
+//go:build !v5
+
+package packet
+
+func init() {
+ V5Disabled = true
+}
diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/encrypted_key.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/encrypted_key.go
index eeff2902c1..b90bb28911 100644
--- a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/encrypted_key.go
+++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/encrypted_key.go
@@ -5,9 +5,11 @@
package packet
import (
+ "bytes"
"crypto"
"crypto/rsa"
"encoding/binary"
+ "encoding/hex"
"io"
"math/big"
"strconv"
@@ -16,32 +18,85 @@ import (
"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/x25519"
+ "github.com/ProtonMail/go-crypto/openpgp/x448"
)
-const encryptedKeyVersion = 3
-
// EncryptedKey represents a public-key encrypted session key. See RFC 4880,
// section 5.1.
type EncryptedKey struct {
- KeyId uint64
- Algo PublicKeyAlgorithm
- CipherFunc CipherFunction // only valid after a successful Decrypt for a v3 packet
- Key []byte // only valid after a successful Decrypt
+ Version int
+ KeyId uint64
+ KeyVersion int // v6
+ KeyFingerprint []byte // v6
+ Algo PublicKeyAlgorithm
+ CipherFunc CipherFunction // only valid after a successful Decrypt for a v3 packet
+ Key []byte // only valid after a successful Decrypt
encryptedMPI1, encryptedMPI2 encoding.Field
+ ephemeralPublicX25519 *x25519.PublicKey // used for x25519
+ ephemeralPublicX448 *x448.PublicKey // used for x448
+ encryptedSession []byte // used for x25519 and x448
}
func (e *EncryptedKey) parse(r io.Reader) (err error) {
- var buf [10]byte
- _, err = readFull(r, buf[:])
+ var buf [8]byte
+ _, err = readFull(r, buf[:versionSize])
if err != nil {
return
}
- if buf[0] != encryptedKeyVersion {
+ e.Version = int(buf[0])
+ if e.Version != 3 && e.Version != 6 {
return errors.UnsupportedError("unknown EncryptedKey version " + strconv.Itoa(int(buf[0])))
}
- e.KeyId = binary.BigEndian.Uint64(buf[1:9])
- e.Algo = PublicKeyAlgorithm(buf[9])
+ if e.Version == 6 {
+ //Read a one-octet size of the following two fields.
+ if _, err = readFull(r, buf[:1]); err != nil {
+ return
+ }
+ // The size may also be zero, and the key version and
+ // fingerprint omitted for an "anonymous recipient"
+ if buf[0] != 0 {
+ // non-anonymous case
+ _, err = readFull(r, buf[:versionSize])
+ if err != nil {
+ return
+ }
+ e.KeyVersion = int(buf[0])
+ if e.KeyVersion != 4 && e.KeyVersion != 6 {
+ return errors.UnsupportedError("unknown public key version " + strconv.Itoa(e.KeyVersion))
+ }
+ var fingerprint []byte
+ if e.KeyVersion == 6 {
+ fingerprint = make([]byte, fingerprintSizeV6)
+ } else if e.KeyVersion == 4 {
+ fingerprint = make([]byte, fingerprintSize)
+ }
+ _, err = readFull(r, fingerprint)
+ if err != nil {
+ return
+ }
+ e.KeyFingerprint = fingerprint
+ if e.KeyVersion == 6 {
+ e.KeyId = binary.BigEndian.Uint64(e.KeyFingerprint[:keyIdSize])
+ } else if e.KeyVersion == 4 {
+ e.KeyId = binary.BigEndian.Uint64(e.KeyFingerprint[fingerprintSize-keyIdSize : fingerprintSize])
+ }
+ }
+ } else {
+ _, err = readFull(r, buf[:8])
+ if err != nil {
+ return
+ }
+ e.KeyId = binary.BigEndian.Uint64(buf[:keyIdSize])
+ }
+
+ _, err = readFull(r, buf[:1])
+ if err != nil {
+ return
+ }
+ e.Algo = PublicKeyAlgorithm(buf[0])
+ var cipherFunction byte
switch e.Algo {
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly:
e.encryptedMPI1 = new(encoding.MPI)
@@ -68,26 +123,39 @@ func (e *EncryptedKey) parse(r io.Reader) (err error) {
if _, err = e.encryptedMPI2.ReadFrom(r); err != nil {
return
}
+ case PubKeyAlgoX25519:
+ e.ephemeralPublicX25519, e.encryptedSession, cipherFunction, err = x25519.DecodeFields(r, e.Version == 6)
+ if err != nil {
+ return
+ }
+ case PubKeyAlgoX448:
+ e.ephemeralPublicX448, e.encryptedSession, cipherFunction, err = x448.DecodeFields(r, e.Version == 6)
+ if err != nil {
+ return
+ }
+ }
+ if e.Version < 6 {
+ switch e.Algo {
+ case PubKeyAlgoX25519, PubKeyAlgoX448:
+ e.CipherFunc = CipherFunction(cipherFunction)
+ // Check for validiy is in the Decrypt method
+ }
}
+
_, err = consumeAll(r)
return
}
-func checksumKeyMaterial(key []byte) uint16 {
- var checksum uint16
- for _, v := range key {
- checksum += uint16(v)
- }
- return checksum
-}
-
// Decrypt decrypts an encrypted session key with the given private key. The
// private key must have been decrypted first.
// If config is nil, sensible defaults will be used.
func (e *EncryptedKey) Decrypt(priv *PrivateKey, config *Config) error {
- if e.KeyId != 0 && e.KeyId != priv.KeyId {
+ if e.Version < 6 && 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.Version == 6 && e.KeyVersion != 0 && !bytes.Equal(e.KeyFingerprint, priv.Fingerprint) {
+ return errors.InvalidArgumentError("cannot decrypt encrypted session key for key fingerprint " + hex.EncodeToString(e.KeyFingerprint) + " with private key fingerprint " + hex.EncodeToString(priv.Fingerprint))
+ }
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)))
}
@@ -113,52 +181,116 @@ func (e *EncryptedKey) Decrypt(priv *PrivateKey, config *Config) error {
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[:])
+ fp := priv.PublicKey.Fingerprint[:]
+ if priv.PublicKey.Version == 5 {
+ // For v5 the, the fingerprint must be restricted to 20 bytes
+ fp = fp[:20]
+ }
+ b, err = ecdh.Decrypt(priv.PrivateKey.(*ecdh.PrivateKey), vsG, m, oid, fp)
+ case PubKeyAlgoX25519:
+ b, err = x25519.Decrypt(priv.PrivateKey.(*x25519.PrivateKey), e.ephemeralPublicX25519, e.encryptedSession)
+ case PubKeyAlgoX448:
+ b, err = x448.Decrypt(priv.PrivateKey.(*x448.PrivateKey), e.ephemeralPublicX448, e.encryptedSession)
default:
err = errors.InvalidArgumentError("cannot decrypt encrypted session key with private key of type " + strconv.Itoa(int(priv.PubKeyAlgo)))
}
-
if err != nil {
return err
}
- e.CipherFunc = CipherFunction(b[0])
- if !e.CipherFunc.IsSupported() {
- return errors.UnsupportedError("unsupported encryption function")
- }
-
- e.Key = b[1 : len(b)-2]
- expectedChecksum := uint16(b[len(b)-2])<<8 | uint16(b[len(b)-1])
- checksum := checksumKeyMaterial(e.Key)
- if checksum != expectedChecksum {
- return errors.StructuralError("EncryptedKey checksum incorrect")
+ var key []byte
+ switch priv.PubKeyAlgo {
+ case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoElGamal, PubKeyAlgoECDH:
+ keyOffset := 0
+ if e.Version < 6 {
+ e.CipherFunc = CipherFunction(b[0])
+ keyOffset = 1
+ if !e.CipherFunc.IsSupported() {
+ return errors.UnsupportedError("unsupported encryption function")
+ }
+ }
+ key, err = decodeChecksumKey(b[keyOffset:])
+ if err != nil {
+ return err
+ }
+ case PubKeyAlgoX25519, PubKeyAlgoX448:
+ if e.Version < 6 {
+ switch e.CipherFunc {
+ case CipherAES128, CipherAES192, CipherAES256:
+ break
+ default:
+ return errors.StructuralError("v3 PKESK mandates AES as cipher function for x25519 and x448")
+ }
+ }
+ key = b[:]
+ default:
+ return errors.UnsupportedError("unsupported algorithm for decryption")
}
-
+ e.Key = key
return nil
}
// Serialize writes the encrypted key packet, e, to w.
func (e *EncryptedKey) Serialize(w io.Writer) error {
- var mpiLen int
+ var encodedLength int
switch e.Algo {
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly:
- mpiLen = int(e.encryptedMPI1.EncodedLength())
+ encodedLength = int(e.encryptedMPI1.EncodedLength())
case PubKeyAlgoElGamal:
- mpiLen = int(e.encryptedMPI1.EncodedLength()) + int(e.encryptedMPI2.EncodedLength())
+ encodedLength = int(e.encryptedMPI1.EncodedLength()) + int(e.encryptedMPI2.EncodedLength())
case PubKeyAlgoECDH:
- mpiLen = int(e.encryptedMPI1.EncodedLength()) + int(e.encryptedMPI2.EncodedLength())
+ encodedLength = int(e.encryptedMPI1.EncodedLength()) + int(e.encryptedMPI2.EncodedLength())
+ case PubKeyAlgoX25519:
+ encodedLength = x25519.EncodedFieldsLength(e.encryptedSession, e.Version == 6)
+ case PubKeyAlgoX448:
+ encodedLength = x448.EncodedFieldsLength(e.encryptedSession, e.Version == 6)
default:
return errors.InvalidArgumentError("don't know how to serialize encrypted key type " + strconv.Itoa(int(e.Algo)))
}
- err := serializeHeader(w, packetTypeEncryptedKey, 1 /* version */ +8 /* key id */ +1 /* algo */ +mpiLen)
+ packetLen := versionSize /* version */ + keyIdSize /* key id */ + algorithmSize /* algo */ + encodedLength
+ if e.Version == 6 {
+ packetLen = versionSize /* version */ + algorithmSize /* algo */ + encodedLength + keyVersionSize /* key version */
+ if e.KeyVersion == 6 {
+ packetLen += fingerprintSizeV6
+ } else if e.KeyVersion == 4 {
+ packetLen += fingerprintSize
+ }
+ }
+
+ err := serializeHeader(w, packetTypeEncryptedKey, packetLen)
if err != nil {
return err
}
- w.Write([]byte{encryptedKeyVersion})
- binary.Write(w, binary.BigEndian, e.KeyId)
- w.Write([]byte{byte(e.Algo)})
+ _, err = w.Write([]byte{byte(e.Version)})
+ if err != nil {
+ return err
+ }
+ if e.Version == 6 {
+ _, err = w.Write([]byte{byte(e.KeyVersion)})
+ if err != nil {
+ return err
+ }
+ // The key version number may also be zero,
+ // and the fingerprint omitted
+ if e.KeyVersion != 0 {
+ _, err = w.Write(e.KeyFingerprint)
+ if err != nil {
+ return err
+ }
+ }
+ } else {
+ // Write KeyID
+ err = binary.Write(w, binary.BigEndian, e.KeyId)
+ if err != nil {
+ return err
+ }
+ }
+ _, err = w.Write([]byte{byte(e.Algo)})
+ if err != nil {
+ return err
+ }
switch e.Algo {
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly:
@@ -176,34 +308,115 @@ func (e *EncryptedKey) Serialize(w io.Writer) error {
}
_, err := w.Write(e.encryptedMPI2.EncodedBytes())
return err
+ case PubKeyAlgoX25519:
+ err := x25519.EncodeFields(w, e.ephemeralPublicX25519, e.encryptedSession, byte(e.CipherFunc), e.Version == 6)
+ return err
+ case PubKeyAlgoX448:
+ err := x448.EncodeFields(w, e.ephemeralPublicX448, e.encryptedSession, byte(e.CipherFunc), e.Version == 6)
+ return err
default:
panic("internal error")
}
}
-// SerializeEncryptedKey serializes an encrypted key packet to w that contains
+// SerializeEncryptedKeyAEAD serializes an encrypted key packet to w that contains
// key, encrypted to pub.
+// If aeadSupported is set, PKESK v6 is used, otherwise v3.
+// Note: aeadSupported MUST match the value passed to SerializeSymmetricallyEncrypted.
// If config is nil, sensible defaults will be used.
-func SerializeEncryptedKey(w io.Writer, pub *PublicKey, cipherFunc CipherFunction, key []byte, config *Config) error {
- var buf [10]byte
- buf[0] = encryptedKeyVersion
- binary.BigEndian.PutUint64(buf[1:9], pub.KeyId)
- buf[9] = byte(pub.PubKeyAlgo)
-
- keyBlock := make([]byte, 1 /* cipher type */ +len(key)+2 /* checksum */)
- keyBlock[0] = byte(cipherFunc)
- copy(keyBlock[1:], key)
- checksum := checksumKeyMaterial(key)
- keyBlock[1+len(key)] = byte(checksum >> 8)
- keyBlock[1+len(key)+1] = byte(checksum)
+func SerializeEncryptedKeyAEAD(w io.Writer, pub *PublicKey, cipherFunc CipherFunction, aeadSupported bool, key []byte, config *Config) error {
+ return SerializeEncryptedKeyAEADwithHiddenOption(w, pub, cipherFunc, aeadSupported, key, false, config)
+}
+
+// SerializeEncryptedKeyAEADwithHiddenOption serializes an encrypted key packet to w that contains
+// key, encrypted to pub.
+// Offers the hidden flag option to indicated if the PKESK packet should include a wildcard KeyID.
+// If aeadSupported is set, PKESK v6 is used, otherwise v3.
+// Note: aeadSupported MUST match the value passed to SerializeSymmetricallyEncrypted.
+// If config is nil, sensible defaults will be used.
+func SerializeEncryptedKeyAEADwithHiddenOption(w io.Writer, pub *PublicKey, cipherFunc CipherFunction, aeadSupported bool, key []byte, hidden bool, config *Config) error {
+ var buf [36]byte // max possible header size is v6
+ lenHeaderWritten := versionSize
+ version := 3
+
+ if aeadSupported {
+ version = 6
+ }
+ // An implementation MUST NOT generate ElGamal v6 PKESKs.
+ if version == 6 && pub.PubKeyAlgo == PubKeyAlgoElGamal {
+ return errors.InvalidArgumentError("ElGamal v6 PKESK are not allowed")
+ }
+ // In v3 PKESKs, for x25519 and x448, mandate using AES
+ if version == 3 && (pub.PubKeyAlgo == PubKeyAlgoX25519 || pub.PubKeyAlgo == PubKeyAlgoX448) {
+ switch cipherFunc {
+ case CipherAES128, CipherAES192, CipherAES256:
+ break
+ default:
+ return errors.InvalidArgumentError("v3 PKESK mandates AES for x25519 and x448")
+ }
+ }
+
+ buf[0] = byte(version)
+
+ // If hidden is set, the key should be hidden
+ // An implementation MAY accept or use a Key ID of all zeros,
+ // or a key version of zero and no key fingerprint, to hide the intended decryption key.
+ // See Section 5.1.8. in the open pgp crypto refresh
+ if version == 6 {
+ if !hidden {
+ // A one-octet size of the following two fields.
+ buf[1] = byte(keyVersionSize + len(pub.Fingerprint))
+ // A one octet key version number.
+ buf[2] = byte(pub.Version)
+ lenHeaderWritten += keyVersionSize + 1
+ // The fingerprint of the public key
+ copy(buf[lenHeaderWritten:lenHeaderWritten+len(pub.Fingerprint)], pub.Fingerprint)
+ lenHeaderWritten += len(pub.Fingerprint)
+ } else {
+ // The size may also be zero, and the key version
+ // and fingerprint omitted for an "anonymous recipient"
+ buf[1] = 0
+ lenHeaderWritten += 1
+ }
+ } else {
+ if !hidden {
+ binary.BigEndian.PutUint64(buf[versionSize:(versionSize+keyIdSize)], pub.KeyId)
+ }
+ lenHeaderWritten += keyIdSize
+ }
+ buf[lenHeaderWritten] = byte(pub.PubKeyAlgo)
+ lenHeaderWritten += algorithmSize
+
+ var keyBlock []byte
+ switch pub.PubKeyAlgo {
+ case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoElGamal, PubKeyAlgoECDH:
+ lenKeyBlock := len(key) + 2
+ if version < 6 {
+ lenKeyBlock += 1 // cipher type included
+ }
+ keyBlock = make([]byte, lenKeyBlock)
+ keyOffset := 0
+ if version < 6 {
+ keyBlock[0] = byte(cipherFunc)
+ keyOffset = 1
+ }
+ encodeChecksumKey(keyBlock[keyOffset:], key)
+ case PubKeyAlgoX25519, PubKeyAlgoX448:
+ // algorithm is added in plaintext below
+ keyBlock = key
+ }
switch pub.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly:
- return serializeEncryptedKeyRSA(w, config.Random(), buf, pub.PublicKey.(*rsa.PublicKey), keyBlock)
+ return serializeEncryptedKeyRSA(w, config.Random(), buf[:lenHeaderWritten], pub.PublicKey.(*rsa.PublicKey), keyBlock)
case PubKeyAlgoElGamal:
- return serializeEncryptedKeyElGamal(w, config.Random(), buf, pub.PublicKey.(*elgamal.PublicKey), keyBlock)
+ return serializeEncryptedKeyElGamal(w, config.Random(), buf[:lenHeaderWritten], pub.PublicKey.(*elgamal.PublicKey), keyBlock)
case PubKeyAlgoECDH:
- return serializeEncryptedKeyECDH(w, config.Random(), buf, pub.PublicKey.(*ecdh.PublicKey), keyBlock, pub.oid, pub.Fingerprint)
+ return serializeEncryptedKeyECDH(w, config.Random(), buf[:lenHeaderWritten], pub.PublicKey.(*ecdh.PublicKey), keyBlock, pub.oid, pub.Fingerprint)
+ case PubKeyAlgoX25519:
+ return serializeEncryptedKeyX25519(w, config.Random(), buf[:lenHeaderWritten], pub.PublicKey.(*x25519.PublicKey), keyBlock, byte(cipherFunc), version)
+ case PubKeyAlgoX448:
+ return serializeEncryptedKeyX448(w, config.Random(), buf[:lenHeaderWritten], pub.PublicKey.(*x448.PublicKey), keyBlock, byte(cipherFunc), version)
case PubKeyAlgoDSA, PubKeyAlgoRSASignOnly:
return errors.InvalidArgumentError("cannot encrypt to public key of type " + strconv.Itoa(int(pub.PubKeyAlgo)))
}
@@ -211,14 +424,32 @@ func SerializeEncryptedKey(w io.Writer, pub *PublicKey, cipherFunc CipherFunctio
return errors.UnsupportedError("encrypting a key to public key of type " + strconv.Itoa(int(pub.PubKeyAlgo)))
}
-func serializeEncryptedKeyRSA(w io.Writer, rand io.Reader, header [10]byte, pub *rsa.PublicKey, keyBlock []byte) error {
+// SerializeEncryptedKey serializes an encrypted key packet to w that contains
+// key, encrypted to pub.
+// PKESKv6 is used if config.AEAD() is not nil.
+// If config is nil, sensible defaults will be used.
+// Deprecated: Use SerializeEncryptedKeyAEAD instead.
+func SerializeEncryptedKey(w io.Writer, pub *PublicKey, cipherFunc CipherFunction, key []byte, config *Config) error {
+ return SerializeEncryptedKeyAEAD(w, pub, cipherFunc, config.AEAD() != nil, key, config)
+}
+
+// SerializeEncryptedKeyWithHiddenOption serializes an encrypted key packet to w that contains
+// key, encrypted to pub. PKESKv6 is used if config.AEAD() is not nil.
+// The hidden option controls if the packet should be anonymous, i.e., omit key metadata.
+// If config is nil, sensible defaults will be used.
+// Deprecated: Use SerializeEncryptedKeyAEADwithHiddenOption instead.
+func SerializeEncryptedKeyWithHiddenOption(w io.Writer, pub *PublicKey, cipherFunc CipherFunction, key []byte, hidden bool, config *Config) error {
+ return SerializeEncryptedKeyAEADwithHiddenOption(w, pub, cipherFunc, config.AEAD() != nil, key, hidden, config)
+}
+
+func serializeEncryptedKeyRSA(w io.Writer, rand io.Reader, header []byte, pub *rsa.PublicKey, keyBlock []byte) error {
cipherText, err := rsa.EncryptPKCS1v15(rand, pub, keyBlock)
if err != nil {
return errors.InvalidArgumentError("RSA encryption failed: " + err.Error())
}
cipherMPI := encoding.NewMPI(cipherText)
- packetLen := 10 /* header length */ + int(cipherMPI.EncodedLength())
+ packetLen := len(header) /* header length */ + int(cipherMPI.EncodedLength())
err = serializeHeader(w, packetTypeEncryptedKey, packetLen)
if err != nil {
@@ -232,13 +463,13 @@ func serializeEncryptedKeyRSA(w io.Writer, rand io.Reader, header [10]byte, pub
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 []byte, pub *elgamal.PublicKey, keyBlock []byte) error {
c1, c2, err := elgamal.Encrypt(rand, pub, keyBlock)
if err != nil {
return errors.InvalidArgumentError("ElGamal encryption failed: " + err.Error())
}
- packetLen := 10 /* header length */
+ packetLen := len(header) /* header length */
packetLen += 2 /* mpi size */ + (c1.BitLen()+7)/8
packetLen += 2 /* mpi size */ + (c2.BitLen()+7)/8
@@ -257,7 +488,7 @@ func serializeEncryptedKeyElGamal(w io.Writer, rand io.Reader, header [10]byte,
return err
}
-func serializeEncryptedKeyECDH(w io.Writer, rand io.Reader, header [10]byte, pub *ecdh.PublicKey, keyBlock []byte, oid encoding.Field, fingerprint []byte) error {
+func serializeEncryptedKeyECDH(w io.Writer, rand io.Reader, header []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())
@@ -266,7 +497,7 @@ func serializeEncryptedKeyECDH(w io.Writer, rand io.Reader, header [10]byte, pub
g := encoding.NewMPI(vsG)
m := encoding.NewOID(c)
- packetLen := 10 /* header length */
+ packetLen := len(header) /* header length */
packetLen += int(g.EncodedLength()) + int(m.EncodedLength())
err = serializeHeader(w, packetTypeEncryptedKey, packetLen)
@@ -284,3 +515,70 @@ func serializeEncryptedKeyECDH(w io.Writer, rand io.Reader, header [10]byte, pub
_, err = w.Write(m.EncodedBytes())
return err
}
+
+func serializeEncryptedKeyX25519(w io.Writer, rand io.Reader, header []byte, pub *x25519.PublicKey, keyBlock []byte, cipherFunc byte, version int) error {
+ ephemeralPublicX25519, ciphertext, err := x25519.Encrypt(rand, pub, keyBlock)
+ if err != nil {
+ return errors.InvalidArgumentError("x25519 encryption failed: " + err.Error())
+ }
+
+ packetLen := len(header) /* header length */
+ packetLen += x25519.EncodedFieldsLength(ciphertext, version == 6)
+
+ err = serializeHeader(w, packetTypeEncryptedKey, packetLen)
+ if err != nil {
+ return err
+ }
+
+ _, err = w.Write(header[:])
+ if err != nil {
+ return err
+ }
+ return x25519.EncodeFields(w, ephemeralPublicX25519, ciphertext, cipherFunc, version == 6)
+}
+
+func serializeEncryptedKeyX448(w io.Writer, rand io.Reader, header []byte, pub *x448.PublicKey, keyBlock []byte, cipherFunc byte, version int) error {
+ ephemeralPublicX448, ciphertext, err := x448.Encrypt(rand, pub, keyBlock)
+ if err != nil {
+ return errors.InvalidArgumentError("x448 encryption failed: " + err.Error())
+ }
+
+ packetLen := len(header) /* header length */
+ packetLen += x448.EncodedFieldsLength(ciphertext, version == 6)
+
+ err = serializeHeader(w, packetTypeEncryptedKey, packetLen)
+ if err != nil {
+ return err
+ }
+
+ _, err = w.Write(header[:])
+ if err != nil {
+ return err
+ }
+ return x448.EncodeFields(w, ephemeralPublicX448, ciphertext, cipherFunc, version == 6)
+}
+
+func checksumKeyMaterial(key []byte) uint16 {
+ var checksum uint16
+ for _, v := range key {
+ checksum += uint16(v)
+ }
+ return checksum
+}
+
+func decodeChecksumKey(msg []byte) (key []byte, err error) {
+ key = msg[:len(msg)-2]
+ expectedChecksum := uint16(msg[len(msg)-2])<<8 | uint16(msg[len(msg)-1])
+ checksum := checksumKeyMaterial(key)
+ if checksum != expectedChecksum {
+ err = errors.StructuralError("session key checksum is incorrect")
+ }
+ return
+}
+
+func encodeChecksumKey(buffer []byte, key []byte) {
+ copy(buffer, key)
+ checksum := checksumKeyMaterial(key)
+ buffer[len(key)] = byte(checksum >> 8)
+ buffer[len(key)+1] = byte(checksum)
+}
diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/literal.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/literal.go
index 4be987609b..8a028c8a17 100644
--- a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/literal.go
+++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/literal.go
@@ -58,9 +58,9 @@ func (l *LiteralData) parse(r io.Reader) (err error) {
// on completion. The fileName is truncated to 255 bytes.
func SerializeLiteral(w io.WriteCloser, isBinary bool, fileName string, time uint32) (plaintext io.WriteCloser, err error) {
var buf [4]byte
- buf[0] = 't'
- if isBinary {
- buf[0] = 'b'
+ buf[0] = 'b'
+ if !isBinary {
+ buf[0] = 'u'
}
if len(fileName) > 255 {
fileName = fileName[:255]
diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/marker.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/marker.go
new file mode 100644
index 0000000000..1ee378ba3c
--- /dev/null
+++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/marker.go
@@ -0,0 +1,33 @@
+package packet
+
+import (
+ "io"
+
+ "github.com/ProtonMail/go-crypto/openpgp/errors"
+)
+
+type Marker struct{}
+
+const markerString = "PGP"
+
+// parse just checks if the packet contains "PGP".
+func (m *Marker) parse(reader io.Reader) error {
+ var buffer [3]byte
+ if _, err := io.ReadFull(reader, buffer[:]); err != nil {
+ return err
+ }
+ if string(buffer[:]) != markerString {
+ return errors.StructuralError("invalid marker packet")
+ }
+ return nil
+}
+
+// SerializeMarker writes a marker packet to writer.
+func SerializeMarker(writer io.Writer) error {
+ err := serializeHeader(writer, packetTypeMarker, len(markerString))
+ if err != nil {
+ return err
+ }
+ _, err = writer.Write([]byte(markerString))
+ return err
+}
diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/one_pass_signature.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/one_pass_signature.go
index 033fb2d7e8..f393c4063b 100644
--- a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/one_pass_signature.go
+++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/one_pass_signature.go
@@ -7,34 +7,37 @@ package packet
import (
"crypto"
"encoding/binary"
- "github.com/ProtonMail/go-crypto/openpgp/errors"
- "github.com/ProtonMail/go-crypto/openpgp/internal/algorithm"
"io"
"strconv"
+
+ "github.com/ProtonMail/go-crypto/openpgp/errors"
+ "github.com/ProtonMail/go-crypto/openpgp/internal/algorithm"
)
// OnePassSignature represents a one-pass signature packet. See RFC 4880,
// section 5.4.
type OnePassSignature struct {
- SigType SignatureType
- Hash crypto.Hash
- PubKeyAlgo PublicKeyAlgorithm
- KeyId uint64
- IsLast bool
+ Version int
+ SigType SignatureType
+ Hash crypto.Hash
+ PubKeyAlgo PublicKeyAlgorithm
+ KeyId uint64
+ IsLast bool
+ Salt []byte // v6 only
+ KeyFingerprint []byte // v6 only
}
-const onePassSignatureVersion = 3
-
func (ops *OnePassSignature) parse(r io.Reader) (err error) {
- var buf [13]byte
-
- _, err = readFull(r, buf[:])
+ var buf [8]byte
+ // Read: version | signature type | hash algorithm | public-key algorithm
+ _, err = readFull(r, buf[:4])
if err != nil {
return
}
- if buf[0] != onePassSignatureVersion {
- err = errors.UnsupportedError("one-pass-signature packet version " + strconv.Itoa(int(buf[0])))
+ if buf[0] != 3 && buf[0] != 6 {
+ return errors.UnsupportedError("one-pass-signature packet version " + strconv.Itoa(int(buf[0])))
}
+ ops.Version = int(buf[0])
var ok bool
ops.Hash, ok = algorithm.HashIdToHashWithSha1(buf[2])
@@ -44,15 +47,69 @@ func (ops *OnePassSignature) parse(r io.Reader) (err error) {
ops.SigType = SignatureType(buf[1])
ops.PubKeyAlgo = PublicKeyAlgorithm(buf[3])
- ops.KeyId = binary.BigEndian.Uint64(buf[4:12])
- ops.IsLast = buf[12] != 0
+
+ if ops.Version == 6 {
+ // Only for v6, a variable-length field containing the salt
+ _, err = readFull(r, buf[:1])
+ if err != nil {
+ return
+ }
+ saltLength := int(buf[0])
+ var expectedSaltLength int
+ expectedSaltLength, err = SaltLengthForHash(ops.Hash)
+ if err != nil {
+ return
+ }
+ if saltLength != expectedSaltLength {
+ err = errors.StructuralError("unexpected salt size for the given hash algorithm")
+ return
+ }
+ salt := make([]byte, expectedSaltLength)
+ _, err = readFull(r, salt)
+ if err != nil {
+ return
+ }
+ ops.Salt = salt
+
+ // Only for v6 packets, 32 octets of the fingerprint of the signing key.
+ fingerprint := make([]byte, 32)
+ _, err = readFull(r, fingerprint)
+ if err != nil {
+ return
+ }
+ ops.KeyFingerprint = fingerprint
+ ops.KeyId = binary.BigEndian.Uint64(ops.KeyFingerprint[:8])
+ } else {
+ _, err = readFull(r, buf[:8])
+ if err != nil {
+ return
+ }
+ ops.KeyId = binary.BigEndian.Uint64(buf[:8])
+ }
+
+ _, err = readFull(r, buf[:1])
+ if err != nil {
+ return
+ }
+ ops.IsLast = buf[0] != 0
return
}
// Serialize marshals the given OnePassSignature to w.
func (ops *OnePassSignature) Serialize(w io.Writer) error {
- var buf [13]byte
- buf[0] = onePassSignatureVersion
+ //v3 length 1+1+1+1+8+1 =
+ packetLength := 13
+ if ops.Version == 6 {
+ // v6 length 1+1+1+1+1+len(salt)+32+1 =
+ packetLength = 38 + len(ops.Salt)
+ }
+
+ if err := serializeHeader(w, packetTypeOnePassSignature, packetLength); err != nil {
+ return err
+ }
+
+ var buf [8]byte
+ buf[0] = byte(ops.Version)
buf[1] = uint8(ops.SigType)
var ok bool
buf[2], ok = algorithm.HashToHashIdWithSha1(ops.Hash)
@@ -60,14 +117,41 @@ func (ops *OnePassSignature) Serialize(w io.Writer) error {
return errors.UnsupportedError("hash type: " + strconv.Itoa(int(ops.Hash)))
}
buf[3] = uint8(ops.PubKeyAlgo)
- binary.BigEndian.PutUint64(buf[4:12], ops.KeyId)
- if ops.IsLast {
- buf[12] = 1
- }
- if err := serializeHeader(w, packetTypeOnePassSignature, len(buf)); err != nil {
+ _, err := w.Write(buf[:4])
+ if err != nil {
return err
}
- _, err := w.Write(buf[:])
+
+ if ops.Version == 6 {
+ // write salt for v6 signatures
+ _, err := w.Write([]byte{uint8(len(ops.Salt))})
+ if err != nil {
+ return err
+ }
+ _, err = w.Write(ops.Salt)
+ if err != nil {
+ return err
+ }
+
+ // write fingerprint v6 signatures
+ _, err = w.Write(ops.KeyFingerprint)
+ if err != nil {
+ return err
+ }
+ } else {
+ binary.BigEndian.PutUint64(buf[:8], ops.KeyId)
+ _, err := w.Write(buf[:8])
+ if err != nil {
+ return err
+ }
+ }
+
+ isLast := []byte{byte(0)}
+ if ops.IsLast {
+ isLast[0] = 1
+ }
+
+ _, err = w.Write(isLast)
return err
}
diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/opaque.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/opaque.go
index 4f8204079f..cef7c661d3 100644
--- a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/opaque.go
+++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/opaque.go
@@ -7,7 +7,6 @@ package packet
import (
"bytes"
"io"
- "io/ioutil"
"github.com/ProtonMail/go-crypto/openpgp/errors"
)
@@ -26,7 +25,7 @@ type OpaquePacket struct {
}
func (op *OpaquePacket) parse(r io.Reader) (err error) {
- op.Contents, err = ioutil.ReadAll(r)
+ op.Contents, err = io.ReadAll(r)
return
}
diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/packet.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/packet.go
index 4d86a7da82..1e92e22c97 100644
--- a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/packet.go
+++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/packet.go
@@ -311,12 +311,15 @@ const (
packetTypePrivateSubkey packetType = 7
packetTypeCompressed packetType = 8
packetTypeSymmetricallyEncrypted packetType = 9
+ packetTypeMarker packetType = 10
packetTypeLiteralData packetType = 11
+ packetTypeTrust packetType = 12
packetTypeUserId packetType = 13
packetTypePublicSubkey packetType = 14
packetTypeUserAttribute packetType = 17
packetTypeSymmetricallyEncryptedIntegrityProtected packetType = 18
packetTypeAEADEncrypted packetType = 20
+ packetPadding packetType = 21
)
// EncryptedDataPacket holds encrypted data. It is currently implemented by
@@ -328,7 +331,7 @@ type EncryptedDataPacket interface {
// Read reads a single OpenPGP packet from the given io.Reader. If there is an
// error parsing a packet, the whole packet is consumed from the input.
func Read(r io.Reader) (p Packet, err error) {
- tag, _, contents, err := readHeader(r)
+ tag, len, contents, err := readHeader(r)
if err != nil {
return
}
@@ -367,8 +370,93 @@ func Read(r io.Reader) (p Packet, err error) {
p = se
case packetTypeAEADEncrypted:
p = new(AEADEncrypted)
+ case packetPadding:
+ p = Padding(len)
+ case packetTypeMarker:
+ p = new(Marker)
+ case packetTypeTrust:
+ // Not implemented, just consume
+ err = errors.UnknownPacketTypeError(tag)
default:
+ // Packet Tags from 0 to 39 are critical.
+ // Packet Tags from 40 to 63 are non-critical.
+ if tag < 40 {
+ err = errors.CriticalUnknownPacketTypeError(tag)
+ } else {
+ err = errors.UnknownPacketTypeError(tag)
+ }
+ }
+ if p != nil {
+ err = p.parse(contents)
+ }
+ if err != nil {
+ consumeAll(contents)
+ }
+ return
+}
+
+// ReadWithCheck reads a single OpenPGP message packet from the given io.Reader. If there is an
+// error parsing a packet, the whole packet is consumed from the input.
+// ReadWithCheck additionally checks if the OpenPGP message packet sequence adheres
+// to the packet composition rules in rfc4880, if not throws an error.
+func ReadWithCheck(r io.Reader, sequence *SequenceVerifier) (p Packet, msgErr error, err error) {
+ tag, len, contents, err := readHeader(r)
+ if err != nil {
+ return
+ }
+ switch tag {
+ case packetTypeEncryptedKey:
+ msgErr = sequence.Next(ESKSymbol)
+ p = new(EncryptedKey)
+ case packetTypeSignature:
+ msgErr = sequence.Next(SigSymbol)
+ p = new(Signature)
+ case packetTypeSymmetricKeyEncrypted:
+ msgErr = sequence.Next(ESKSymbol)
+ p = new(SymmetricKeyEncrypted)
+ case packetTypeOnePassSignature:
+ msgErr = sequence.Next(OPSSymbol)
+ p = new(OnePassSignature)
+ case packetTypeCompressed:
+ msgErr = sequence.Next(CompSymbol)
+ p = new(Compressed)
+ case packetTypeSymmetricallyEncrypted:
+ msgErr = sequence.Next(EncSymbol)
+ p = new(SymmetricallyEncrypted)
+ case packetTypeLiteralData:
+ msgErr = sequence.Next(LDSymbol)
+ p = new(LiteralData)
+ case packetTypeSymmetricallyEncryptedIntegrityProtected:
+ msgErr = sequence.Next(EncSymbol)
+ se := new(SymmetricallyEncrypted)
+ se.IntegrityProtected = true
+ p = se
+ case packetTypeAEADEncrypted:
+ msgErr = sequence.Next(EncSymbol)
+ p = new(AEADEncrypted)
+ case packetPadding:
+ p = Padding(len)
+ case packetTypeMarker:
+ p = new(Marker)
+ case packetTypeTrust:
+ // Not implemented, just consume
err = errors.UnknownPacketTypeError(tag)
+ case packetTypePrivateKey,
+ packetTypePrivateSubkey,
+ packetTypePublicKey,
+ packetTypePublicSubkey,
+ packetTypeUserId,
+ packetTypeUserAttribute:
+ msgErr = sequence.Next(UnknownSymbol)
+ consumeAll(contents)
+ default:
+ // Packet Tags from 0 to 39 are critical.
+ // Packet Tags from 40 to 63 are non-critical.
+ if tag < 40 {
+ err = errors.CriticalUnknownPacketTypeError(tag)
+ } else {
+ err = errors.UnknownPacketTypeError(tag)
+ }
}
if p != nil {
err = p.parse(contents)
@@ -385,17 +473,17 @@ type SignatureType uint8
const (
SigTypeBinary SignatureType = 0x00
- SigTypeText = 0x01
- SigTypeGenericCert = 0x10
- SigTypePersonaCert = 0x11
- SigTypeCasualCert = 0x12
- SigTypePositiveCert = 0x13
- SigTypeSubkeyBinding = 0x18
- SigTypePrimaryKeyBinding = 0x19
- SigTypeDirectSignature = 0x1F
- SigTypeKeyRevocation = 0x20
- SigTypeSubkeyRevocation = 0x28
- SigTypeCertificationRevocation = 0x30
+ SigTypeText SignatureType = 0x01
+ SigTypeGenericCert SignatureType = 0x10
+ SigTypePersonaCert SignatureType = 0x11
+ SigTypeCasualCert SignatureType = 0x12
+ SigTypePositiveCert SignatureType = 0x13
+ SigTypeSubkeyBinding SignatureType = 0x18
+ SigTypePrimaryKeyBinding SignatureType = 0x19
+ SigTypeDirectSignature SignatureType = 0x1F
+ SigTypeKeyRevocation SignatureType = 0x20
+ SigTypeSubkeyRevocation SignatureType = 0x28
+ SigTypeCertificationRevocation SignatureType = 0x30
)
// PublicKeyAlgorithm represents the different public key system specified for
@@ -412,6 +500,11 @@ const (
PubKeyAlgoECDSA PublicKeyAlgorithm = 19
// https://www.ietf.org/archive/id/draft-koch-eddsa-for-openpgp-04.txt
PubKeyAlgoEdDSA PublicKeyAlgorithm = 22
+ // https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh
+ PubKeyAlgoX25519 PublicKeyAlgorithm = 25
+ PubKeyAlgoX448 PublicKeyAlgorithm = 26
+ PubKeyAlgoEd25519 PublicKeyAlgorithm = 27
+ PubKeyAlgoEd448 PublicKeyAlgorithm = 28
// Deprecated in RFC 4880, Section 13.5. Use key flags instead.
PubKeyAlgoRSAEncryptOnly PublicKeyAlgorithm = 2
@@ -422,7 +515,7 @@ const (
// key of the given type.
func (pka PublicKeyAlgorithm) CanEncrypt() bool {
switch pka {
- case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoElGamal, PubKeyAlgoECDH:
+ case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoElGamal, PubKeyAlgoECDH, PubKeyAlgoX25519, PubKeyAlgoX448:
return true
}
return false
@@ -432,7 +525,7 @@ func (pka PublicKeyAlgorithm) CanEncrypt() bool {
// sign a message.
func (pka PublicKeyAlgorithm) CanSign() bool {
switch pka {
- case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA, PubKeyAlgoECDSA, PubKeyAlgoEdDSA:
+ case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA, PubKeyAlgoECDSA, PubKeyAlgoEdDSA, PubKeyAlgoEd25519, PubKeyAlgoEd448:
return true
}
return false
@@ -512,6 +605,11 @@ func (mode AEADMode) TagLength() int {
return algorithm.AEADMode(mode).TagLength()
}
+// IsSupported returns true if the aead mode is supported from the library
+func (mode AEADMode) IsSupported() bool {
+ return algorithm.AEADMode(mode).TagLength() > 0
+}
+
// new returns a fresh instance of the given mode.
func (mode AEADMode) new(block cipher.Block) cipher.AEAD {
return algorithm.AEADMode(mode).New(block)
@@ -526,8 +624,17 @@ const (
KeySuperseded ReasonForRevocation = 1
KeyCompromised ReasonForRevocation = 2
KeyRetired ReasonForRevocation = 3
+ UserIDNotValid ReasonForRevocation = 32
+ Unknown ReasonForRevocation = 200
)
+func NewReasonForRevocation(value byte) ReasonForRevocation {
+ if value < 4 || value == 32 {
+ return ReasonForRevocation(value)
+ }
+ return Unknown
+}
+
// Curve is a mapping to supported ECC curves for key generation.
// See https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-06.html#name-curve-specific-wire-formats
type Curve string
@@ -549,3 +656,20 @@ type TrustLevel uint8
// TrustAmount represents a trust amount per RFC4880 5.2.3.13
type TrustAmount uint8
+
+const (
+ // versionSize is the length in bytes of the version value.
+ versionSize = 1
+ // algorithmSize is the length in bytes of the key algorithm value.
+ algorithmSize = 1
+ // keyVersionSize is the length in bytes of the key version value
+ keyVersionSize = 1
+ // keyIdSize is the length in bytes of the key identifier value.
+ keyIdSize = 8
+ // timestampSize is the length in bytes of encoded timestamps.
+ timestampSize = 4
+ // fingerprintSizeV6 is the length in bytes of the key fingerprint in v6.
+ fingerprintSizeV6 = 32
+ // fingerprintSize is the length in bytes of the key fingerprint.
+ fingerprintSize = 20
+)
diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/packet_sequence.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/packet_sequence.go
new file mode 100644
index 0000000000..55a8a56c2d
--- /dev/null
+++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/packet_sequence.go
@@ -0,0 +1,222 @@
+package packet
+
+// This file implements the pushdown automata (PDA) from PGPainless (Paul Schaub)
+// to verify pgp packet sequences. See Paul's blogpost for more details:
+// https://blog.jabberhead.tk/2022/10/26/implementing-packet-sequence-validation-using-pushdown-automata/
+import (
+ "fmt"
+
+ "github.com/ProtonMail/go-crypto/openpgp/errors"
+)
+
+func NewErrMalformedMessage(from State, input InputSymbol, stackSymbol StackSymbol) errors.ErrMalformedMessage {
+ return errors.ErrMalformedMessage(fmt.Sprintf("state %d, input symbol %d, stack symbol %d ", from, input, stackSymbol))
+}
+
+// InputSymbol defines the input alphabet of the PDA
+type InputSymbol uint8
+
+const (
+ LDSymbol InputSymbol = iota
+ SigSymbol
+ OPSSymbol
+ CompSymbol
+ ESKSymbol
+ EncSymbol
+ EOSSymbol
+ UnknownSymbol
+)
+
+// StackSymbol defines the stack alphabet of the PDA
+type StackSymbol int8
+
+const (
+ MsgStackSymbol StackSymbol = iota
+ OpsStackSymbol
+ KeyStackSymbol
+ EndStackSymbol
+ EmptyStackSymbol
+)
+
+// State defines the states of the PDA
+type State int8
+
+const (
+ OpenPGPMessage State = iota
+ ESKMessage
+ LiteralMessage
+ CompressedMessage
+ EncryptedMessage
+ ValidMessage
+)
+
+// transition represents a state transition in the PDA
+type transition func(input InputSymbol, stackSymbol StackSymbol) (State, []StackSymbol, bool, error)
+
+// SequenceVerifier is a pushdown automata to verify
+// PGP messages packet sequences according to rfc4880.
+type SequenceVerifier struct {
+ stack []StackSymbol
+ state State
+}
+
+// Next performs a state transition with the given input symbol.
+// If the transition fails a ErrMalformedMessage is returned.
+func (sv *SequenceVerifier) Next(input InputSymbol) error {
+ for {
+ stackSymbol := sv.popStack()
+ transitionFunc := getTransition(sv.state)
+ nextState, newStackSymbols, redo, err := transitionFunc(input, stackSymbol)
+ if err != nil {
+ return err
+ }
+ if redo {
+ sv.pushStack(stackSymbol)
+ }
+ for _, newStackSymbol := range newStackSymbols {
+ sv.pushStack(newStackSymbol)
+ }
+ sv.state = nextState
+ if !redo {
+ break
+ }
+ }
+ return nil
+}
+
+// Valid returns true if RDA is in a valid state.
+func (sv *SequenceVerifier) Valid() bool {
+ return sv.state == ValidMessage && len(sv.stack) == 0
+}
+
+func (sv *SequenceVerifier) AssertValid() error {
+ if !sv.Valid() {
+ return errors.ErrMalformedMessage("invalid message")
+ }
+ return nil
+}
+
+func NewSequenceVerifier() *SequenceVerifier {
+ return &SequenceVerifier{
+ stack: []StackSymbol{EndStackSymbol, MsgStackSymbol},
+ state: OpenPGPMessage,
+ }
+}
+
+func (sv *SequenceVerifier) popStack() StackSymbol {
+ if len(sv.stack) == 0 {
+ return EmptyStackSymbol
+ }
+ elemIndex := len(sv.stack) - 1
+ stackSymbol := sv.stack[elemIndex]
+ sv.stack = sv.stack[:elemIndex]
+ return stackSymbol
+}
+
+func (sv *SequenceVerifier) pushStack(stackSymbol StackSymbol) {
+ sv.stack = append(sv.stack, stackSymbol)
+}
+
+func getTransition(from State) transition {
+ switch from {
+ case OpenPGPMessage:
+ return fromOpenPGPMessage
+ case LiteralMessage:
+ return fromLiteralMessage
+ case CompressedMessage:
+ return fromCompressedMessage
+ case EncryptedMessage:
+ return fromEncryptedMessage
+ case ESKMessage:
+ return fromESKMessage
+ case ValidMessage:
+ return fromValidMessage
+ }
+ return nil
+}
+
+// fromOpenPGPMessage is the transition for the state OpenPGPMessage.
+func fromOpenPGPMessage(input InputSymbol, stackSymbol StackSymbol) (State, []StackSymbol, bool, error) {
+ if stackSymbol != MsgStackSymbol {
+ return 0, nil, false, NewErrMalformedMessage(OpenPGPMessage, input, stackSymbol)
+ }
+ switch input {
+ case LDSymbol:
+ return LiteralMessage, nil, false, nil
+ case SigSymbol:
+ return OpenPGPMessage, []StackSymbol{MsgStackSymbol}, false, nil
+ case OPSSymbol:
+ return OpenPGPMessage, []StackSymbol{OpsStackSymbol, MsgStackSymbol}, false, nil
+ case CompSymbol:
+ return CompressedMessage, nil, false, nil
+ case ESKSymbol:
+ return ESKMessage, []StackSymbol{KeyStackSymbol}, false, nil
+ case EncSymbol:
+ return EncryptedMessage, nil, false, nil
+ }
+ return 0, nil, false, NewErrMalformedMessage(OpenPGPMessage, input, stackSymbol)
+}
+
+// fromESKMessage is the transition for the state ESKMessage.
+func fromESKMessage(input InputSymbol, stackSymbol StackSymbol) (State, []StackSymbol, bool, error) {
+ if stackSymbol != KeyStackSymbol {
+ return 0, nil, false, NewErrMalformedMessage(ESKMessage, input, stackSymbol)
+ }
+ switch input {
+ case ESKSymbol:
+ return ESKMessage, []StackSymbol{KeyStackSymbol}, false, nil
+ case EncSymbol:
+ return EncryptedMessage, nil, false, nil
+ }
+ return 0, nil, false, NewErrMalformedMessage(ESKMessage, input, stackSymbol)
+}
+
+// fromLiteralMessage is the transition for the state LiteralMessage.
+func fromLiteralMessage(input InputSymbol, stackSymbol StackSymbol) (State, []StackSymbol, bool, error) {
+ switch input {
+ case SigSymbol:
+ if stackSymbol == OpsStackSymbol {
+ return LiteralMessage, nil, false, nil
+ }
+ case EOSSymbol:
+ if stackSymbol == EndStackSymbol {
+ return ValidMessage, nil, false, nil
+ }
+ }
+ return 0, nil, false, NewErrMalformedMessage(LiteralMessage, input, stackSymbol)
+}
+
+// fromLiteralMessage is the transition for the state CompressedMessage.
+func fromCompressedMessage(input InputSymbol, stackSymbol StackSymbol) (State, []StackSymbol, bool, error) {
+ switch input {
+ case SigSymbol:
+ if stackSymbol == OpsStackSymbol {
+ return CompressedMessage, nil, false, nil
+ }
+ case EOSSymbol:
+ if stackSymbol == EndStackSymbol {
+ return ValidMessage, nil, false, nil
+ }
+ }
+ return OpenPGPMessage, []StackSymbol{MsgStackSymbol}, true, nil
+}
+
+// fromEncryptedMessage is the transition for the state EncryptedMessage.
+func fromEncryptedMessage(input InputSymbol, stackSymbol StackSymbol) (State, []StackSymbol, bool, error) {
+ switch input {
+ case SigSymbol:
+ if stackSymbol == OpsStackSymbol {
+ return EncryptedMessage, nil, false, nil
+ }
+ case EOSSymbol:
+ if stackSymbol == EndStackSymbol {
+ return ValidMessage, nil, false, nil
+ }
+ }
+ return OpenPGPMessage, []StackSymbol{MsgStackSymbol}, true, nil
+}
+
+// fromValidMessage is the transition for the state ValidMessage.
+func fromValidMessage(input InputSymbol, stackSymbol StackSymbol) (State, []StackSymbol, bool, error) {
+ return 0, nil, false, NewErrMalformedMessage(ValidMessage, input, stackSymbol)
+}
diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/packet_unsupported.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/packet_unsupported.go
new file mode 100644
index 0000000000..2d714723cf
--- /dev/null
+++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/packet_unsupported.go
@@ -0,0 +1,24 @@
+package packet
+
+import (
+ "io"
+
+ "github.com/ProtonMail/go-crypto/openpgp/errors"
+)
+
+// UnsupportedPackage represents a OpenPGP packet with a known packet type
+// but with unsupported content.
+type UnsupportedPacket struct {
+ IncompletePacket Packet
+ Error errors.UnsupportedError
+}
+
+// Implements the Packet interface
+func (up *UnsupportedPacket) parse(read io.Reader) error {
+ err := up.IncompletePacket.parse(read)
+ if castedErr, ok := err.(errors.UnsupportedError); ok {
+ up.Error = castedErr
+ return nil
+ }
+ return err
+}
diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/padding.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/padding.go
new file mode 100644
index 0000000000..3b6a7045d1
--- /dev/null
+++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/padding.go
@@ -0,0 +1,26 @@
+package packet
+
+import (
+ "io"
+)
+
+// Padding type represents a Padding Packet (Tag 21).
+// The padding type is represented by the length of its padding.
+// see https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh#name-padding-packet-tag-21
+type Padding int
+
+// parse just ignores the padding content.
+func (pad Padding) parse(reader io.Reader) error {
+ _, err := io.CopyN(io.Discard, reader, int64(pad))
+ return err
+}
+
+// SerializePadding writes the padding to writer.
+func (pad Padding) SerializePadding(writer io.Writer, rand io.Reader) error {
+ err := serializeHeader(writer, packetPadding, int(pad))
+ if err != nil {
+ return err
+ }
+ _, err = io.CopyN(writer, rand, int64(pad))
+ return err
+}
diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/private_key.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/private_key.go
index 2fc4386437..f04e6c6b87 100644
--- a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/private_key.go
+++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/private_key.go
@@ -9,22 +9,28 @@ import (
"crypto"
"crypto/cipher"
"crypto/dsa"
- "crypto/rand"
"crypto/rsa"
"crypto/sha1"
+ "crypto/sha256"
+ "crypto/subtle"
+ "fmt"
"io"
- "io/ioutil"
"math/big"
"strconv"
"time"
"github.com/ProtonMail/go-crypto/openpgp/ecdh"
"github.com/ProtonMail/go-crypto/openpgp/ecdsa"
+ "github.com/ProtonMail/go-crypto/openpgp/ed25519"
+ "github.com/ProtonMail/go-crypto/openpgp/ed448"
"github.com/ProtonMail/go-crypto/openpgp/eddsa"
"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"
+ "github.com/ProtonMail/go-crypto/openpgp/x25519"
+ "github.com/ProtonMail/go-crypto/openpgp/x448"
+ "golang.org/x/crypto/hkdf"
)
// PrivateKey represents a possibly encrypted private key. See RFC 4880,
@@ -35,14 +41,14 @@ type PrivateKey struct {
encryptedData []byte
cipher CipherFunction
s2k func(out, in []byte)
- // An *{rsa|dsa|elgamal|ecdh|ecdsa|ed25519}.PrivateKey or
+ aead AEADMode // only relevant if S2KAEAD is enabled
+ // An *{rsa|dsa|elgamal|ecdh|ecdsa|ed25519|ed448}.PrivateKey or
// crypto.Signer/crypto.Decrypter (Decryptor RSA only).
- PrivateKey interface{}
- sha1Checksum bool
- iv []byte
+ PrivateKey interface{}
+ iv []byte
// Type of encryption of the S2K packet
- // Allowed values are 0 (Not encrypted), 254 (SHA1), or
+ // Allowed values are 0 (Not encrypted), 253 (AEAD), 254 (SHA1), or
// 255 (2-byte checksum)
s2kType S2KType
// Full parameters of the S2K packet
@@ -55,6 +61,8 @@ type S2KType uint8
const (
// S2KNON unencrypt
S2KNON S2KType = 0
+ // S2KAEAD use authenticated encryption
+ S2KAEAD S2KType = 253
// S2KSHA1 sha1 sum check
S2KSHA1 S2KType = 254
// S2KCHECKSUM sum check
@@ -103,6 +111,34 @@ func NewECDHPrivateKey(creationTime time.Time, priv *ecdh.PrivateKey) *PrivateKe
return pk
}
+func NewX25519PrivateKey(creationTime time.Time, priv *x25519.PrivateKey) *PrivateKey {
+ pk := new(PrivateKey)
+ pk.PublicKey = *NewX25519PublicKey(creationTime, &priv.PublicKey)
+ pk.PrivateKey = priv
+ return pk
+}
+
+func NewX448PrivateKey(creationTime time.Time, priv *x448.PrivateKey) *PrivateKey {
+ pk := new(PrivateKey)
+ pk.PublicKey = *NewX448PublicKey(creationTime, &priv.PublicKey)
+ pk.PrivateKey = priv
+ return pk
+}
+
+func NewEd25519PrivateKey(creationTime time.Time, priv *ed25519.PrivateKey) *PrivateKey {
+ pk := new(PrivateKey)
+ pk.PublicKey = *NewEd25519PublicKey(creationTime, &priv.PublicKey)
+ pk.PrivateKey = priv
+ return pk
+}
+
+func NewEd448PrivateKey(creationTime time.Time, priv *ed448.PrivateKey) *PrivateKey {
+ pk := new(PrivateKey)
+ pk.PublicKey = *NewEd448PublicKey(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 interface{}) *PrivateKey {
@@ -122,6 +158,14 @@ func NewSignerPrivateKey(creationTime time.Time, signer interface{}) *PrivateKey
pk.PublicKey = *NewEdDSAPublicKey(creationTime, &pubkey.PublicKey)
case eddsa.PrivateKey:
pk.PublicKey = *NewEdDSAPublicKey(creationTime, &pubkey.PublicKey)
+ case *ed25519.PrivateKey:
+ pk.PublicKey = *NewEd25519PublicKey(creationTime, &pubkey.PublicKey)
+ case ed25519.PrivateKey:
+ pk.PublicKey = *NewEd25519PublicKey(creationTime, &pubkey.PublicKey)
+ case *ed448.PrivateKey:
+ pk.PublicKey = *NewEd448PublicKey(creationTime, &pubkey.PublicKey)
+ case ed448.PrivateKey:
+ pk.PublicKey = *NewEd448PublicKey(creationTime, &pubkey.PublicKey)
default:
panic("openpgp: unknown signer type in NewSignerPrivateKey")
}
@@ -129,7 +173,7 @@ func NewSignerPrivateKey(creationTime time.Time, signer interface{}) *PrivateKey
return pk
}
-// NewDecrypterPrivateKey creates a PrivateKey from a *{rsa|elgamal|ecdh}.PrivateKey.
+// NewDecrypterPrivateKey creates a PrivateKey from a *{rsa|elgamal|ecdh|x25519|x448}.PrivateKey.
func NewDecrypterPrivateKey(creationTime time.Time, decrypter interface{}) *PrivateKey {
pk := new(PrivateKey)
switch priv := decrypter.(type) {
@@ -139,6 +183,10 @@ func NewDecrypterPrivateKey(creationTime time.Time, decrypter interface{}) *Priv
pk.PublicKey = *NewElGamalPublicKey(creationTime, &priv.PublicKey)
case *ecdh.PrivateKey:
pk.PublicKey = *NewECDHPublicKey(creationTime, &priv.PublicKey)
+ case *x25519.PrivateKey:
+ pk.PublicKey = *NewX25519PublicKey(creationTime, &priv.PublicKey)
+ case *x448.PrivateKey:
+ pk.PublicKey = *NewX448PublicKey(creationTime, &priv.PublicKey)
default:
panic("openpgp: unknown decrypter type in NewDecrypterPrivateKey")
}
@@ -152,6 +200,11 @@ func (pk *PrivateKey) parse(r io.Reader) (err error) {
return
}
v5 := pk.PublicKey.Version == 5
+ v6 := pk.PublicKey.Version == 6
+
+ if V5Disabled && v5 {
+ return errors.UnsupportedError("support for parsing v5 entities is disabled; build with `-tags v5` if needed")
+ }
var buf [1]byte
_, err = readFull(r, buf[:])
@@ -160,7 +213,7 @@ func (pk *PrivateKey) parse(r io.Reader) (err error) {
}
pk.s2kType = S2KType(buf[0])
var optCount [1]byte
- if v5 {
+ if v5 || (v6 && pk.s2kType != S2KNON) {
if _, err = readFull(r, optCount[:]); err != nil {
return
}
@@ -170,9 +223,9 @@ func (pk *PrivateKey) parse(r io.Reader) (err error) {
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")
+ case S2KSHA1, S2KCHECKSUM, S2KAEAD:
+ if (v5 || v6) && pk.s2kType == S2KCHECKSUM {
+ return errors.StructuralError(fmt.Sprintf("wrong s2k identifier for version %d", pk.Version))
}
_, err = readFull(r, buf[:])
if err != nil {
@@ -182,6 +235,29 @@ func (pk *PrivateKey) parse(r io.Reader) (err error) {
if pk.cipher != 0 && !pk.cipher.IsSupported() {
return errors.UnsupportedError("unsupported cipher function in private key")
}
+ // [Optional] If string-to-key usage octet was 253,
+ // a one-octet AEAD algorithm.
+ if pk.s2kType == S2KAEAD {
+ _, err = readFull(r, buf[:])
+ if err != nil {
+ return
+ }
+ pk.aead = AEADMode(buf[0])
+ if !pk.aead.IsSupported() {
+ return errors.UnsupportedError("unsupported aead mode in private key")
+ }
+ }
+
+ // [Optional] Only for a version 6 packet,
+ // and if string-to-key usage octet was 255, 254, or 253,
+ // an one-octet count of the following field.
+ if v6 {
+ _, err = readFull(r, buf[:])
+ if err != nil {
+ return
+ }
+ }
+
pk.s2kParams, err = s2k.ParseIntoParams(r)
if err != nil {
return
@@ -189,28 +265,43 @@ func (pk *PrivateKey) parse(r io.Reader) (err error) {
if pk.s2kParams.Dummy() {
return
}
+ if pk.s2kParams.Mode() == s2k.Argon2S2K && pk.s2kType != S2KAEAD {
+ return errors.StructuralError("using Argon2 S2K without AEAD is not allowed")
+ }
+ if pk.s2kParams.Mode() == s2k.SimpleS2K && pk.Version == 6 {
+ return errors.StructuralError("using Simple S2K with version 6 keys is not allowed")
+ }
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 {
+ var ivSize int
+ // If the S2K usage octet was 253, the IV is of the size expected by the AEAD mode,
+ // unless it's a version 5 key, in which case it's the size of the symmetric cipher's block size.
+ // For all other S2K modes, it's always the block size.
+ if !v5 && pk.s2kType == S2KAEAD {
+ ivSize = pk.aead.IvLength()
+ } else {
+ ivSize = pk.cipher.blockSize()
+ }
+
+ if ivSize == 0 {
return errors.UnsupportedError("unsupported cipher in private key: " + strconv.Itoa(int(pk.cipher)))
}
- pk.iv = make([]byte, blockSize)
+ pk.iv = make([]byte, ivSize)
_, err = readFull(r, pk.iv)
if err != nil {
return
}
+ if v5 && pk.s2kType == S2KAEAD {
+ pk.iv = pk.iv[:pk.aead.IvLength()]
+ }
}
var privateKeyData []byte
@@ -230,7 +321,7 @@ func (pk *PrivateKey) parse(r io.Reader) (err error) {
return
}
} else {
- privateKeyData, err = ioutil.ReadAll(r)
+ privateKeyData, err = io.ReadAll(r)
if err != nil {
return
}
@@ -239,16 +330,22 @@ func (pk *PrivateKey) parse(r io.Reader) (err error) {
if len(privateKeyData) < 2 {
return errors.StructuralError("truncated private key data")
}
- var sum uint16
- for i := 0; i < len(privateKeyData)-2; i++ {
- sum += uint16(privateKeyData[i])
- }
- if privateKeyData[len(privateKeyData)-2] != uint8(sum>>8) ||
- privateKeyData[len(privateKeyData)-1] != uint8(sum) {
- return errors.StructuralError("private key checksum failure")
+ if pk.Version != 6 {
+ // checksum
+ var sum uint16
+ for i := 0; i < len(privateKeyData)-2; i++ {
+ sum += uint16(privateKeyData[i])
+ }
+ if privateKeyData[len(privateKeyData)-2] != uint8(sum>>8) ||
+ privateKeyData[len(privateKeyData)-1] != uint8(sum) {
+ return errors.StructuralError("private key checksum failure")
+ }
+ privateKeyData = privateKeyData[:len(privateKeyData)-2]
+ return pk.parsePrivateKey(privateKeyData)
+ } else {
+ // No checksum
+ return pk.parsePrivateKey(privateKeyData)
}
- privateKeyData = privateKeyData[:len(privateKeyData)-2]
- return pk.parsePrivateKey(privateKeyData)
}
pk.encryptedData = privateKeyData
@@ -280,18 +377,59 @@ func (pk *PrivateKey) Serialize(w io.Writer) (err error) {
optional := bytes.NewBuffer(nil)
if pk.Encrypted || pk.Dummy() {
- optional.Write([]byte{uint8(pk.cipher)})
- if err := pk.s2kParams.Serialize(optional); err != nil {
+ // [Optional] If string-to-key usage octet was 255, 254, or 253,
+ // a one-octet symmetric encryption algorithm.
+ if _, err = optional.Write([]byte{uint8(pk.cipher)}); err != nil {
+ return
+ }
+ // [Optional] If string-to-key usage octet was 253,
+ // a one-octet AEAD algorithm.
+ if pk.s2kType == S2KAEAD {
+ if _, err = optional.Write([]byte{uint8(pk.aead)}); err != nil {
+ return
+ }
+ }
+
+ s2kBuffer := bytes.NewBuffer(nil)
+ if err := pk.s2kParams.Serialize(s2kBuffer); err != nil {
return err
}
+ // [Optional] Only for a version 6 packet, and if string-to-key
+ // usage octet was 255, 254, or 253, an one-octet
+ // count of the following field.
+ if pk.Version == 6 {
+ if _, err = optional.Write([]byte{uint8(s2kBuffer.Len())}); err != nil {
+ return
+ }
+ }
+ // [Optional] If string-to-key usage octet was 255, 254, or 253,
+ // a string-to-key (S2K) specifier. The length of the string-to-key specifier
+ // depends on its type
+ if _, err = io.Copy(optional, s2kBuffer); err != nil {
+ return
+ }
+
+ // IV
if pk.Encrypted {
- optional.Write(pk.iv)
+ if _, err = optional.Write(pk.iv); err != nil {
+ return
+ }
+ if pk.Version == 5 && pk.s2kType == S2KAEAD {
+ // Add padding for version 5
+ padding := make([]byte, pk.cipher.blockSize()-len(pk.iv))
+ if _, err = optional.Write(padding); err != nil {
+ return
+ }
+ }
}
}
- if pk.Version == 5 {
+ if pk.Version == 5 || (pk.Version == 6 && pk.s2kType != S2KNON) {
contents.Write([]byte{uint8(optional.Len())})
}
- io.Copy(contents, optional)
+
+ if _, err := io.Copy(contents, optional); err != nil {
+ return err
+ }
if !pk.Dummy() {
l := 0
@@ -303,8 +441,10 @@ func (pk *PrivateKey) Serialize(w io.Writer) (err error) {
return err
}
l = buf.Len()
- checksum := mod64kHash(buf.Bytes())
- buf.Write([]byte{byte(checksum >> 8), byte(checksum)})
+ if pk.Version != 6 {
+ checksum := mod64kHash(buf.Bytes())
+ buf.Write([]byte{byte(checksum >> 8), byte(checksum)})
+ }
priv = buf.Bytes()
} else {
priv, l = pk.encryptedData, len(pk.encryptedData)
@@ -370,6 +510,26 @@ func serializeECDHPrivateKey(w io.Writer, priv *ecdh.PrivateKey) error {
return err
}
+func serializeX25519PrivateKey(w io.Writer, priv *x25519.PrivateKey) error {
+ _, err := w.Write(priv.Secret)
+ return err
+}
+
+func serializeX448PrivateKey(w io.Writer, priv *x448.PrivateKey) error {
+ _, err := w.Write(priv.Secret)
+ return err
+}
+
+func serializeEd25519PrivateKey(w io.Writer, priv *ed25519.PrivateKey) error {
+ _, err := w.Write(priv.MarshalByteSecret())
+ return err
+}
+
+func serializeEd448PrivateKey(w io.Writer, priv *ed448.PrivateKey) error {
+ _, err := w.Write(priv.MarshalByteSecret())
+ return err
+}
+
// decrypt decrypts an encrypted private key using a decryption key.
func (pk *PrivateKey) decrypt(decryptionKey []byte) error {
if pk.Dummy() {
@@ -378,37 +538,51 @@ func (pk *PrivateKey) decrypt(decryptionKey []byte) error {
if !pk.Encrypted {
return nil
}
-
block := pk.cipher.new(decryptionKey)
- 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 data []byte
+ switch pk.s2kType {
+ case S2KAEAD:
+ aead := pk.aead.new(block)
+ additionalData, err := pk.additionalData()
+ if err != nil {
+ return err
}
- var sum uint16
- for i := 0; i < len(data)-2; i++ {
- sum += uint16(data[i])
+ // Decrypt the encrypted key material with aead
+ data, err = aead.Open(nil, pk.iv, pk.encryptedData, additionalData)
+ if err != nil {
+ return err
}
- if data[len(data)-2] != uint8(sum>>8) ||
- data[len(data)-1] != uint8(sum) {
- return errors.StructuralError("private key checksum failure")
+ case S2KSHA1, S2KCHECKSUM:
+ cfb := cipher.NewCFBDecrypter(block, pk.iv)
+ data = make([]byte, len(pk.encryptedData))
+ cfb.XORKeyStream(data, pk.encryptedData)
+ if pk.s2kType == S2KSHA1 {
+ 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]
}
- data = data[:len(data)-2]
+ default:
+ return errors.InvalidArgumentError("invalid s2k type")
}
err := pk.parsePrivateKey(data)
@@ -424,7 +598,6 @@ func (pk *PrivateKey) decrypt(decryptionKey []byte) error {
pk.s2k = nil
pk.Encrypted = false
pk.encryptedData = nil
-
return nil
}
@@ -440,6 +613,9 @@ func (pk *PrivateKey) decryptWithCache(passphrase []byte, keyCache *s2k.Cache) e
if err != nil {
return err
}
+ if pk.s2kType == S2KAEAD {
+ key = pk.applyHKDF(key)
+ }
return pk.decrypt(key)
}
@@ -454,11 +630,14 @@ func (pk *PrivateKey) Decrypt(passphrase []byte) error {
key := make([]byte, pk.cipher.KeySize())
pk.s2k(key, passphrase)
+ if pk.s2kType == S2KAEAD {
+ key = pk.applyHKDF(key)
+ }
return pk.decrypt(key)
}
// DecryptPrivateKeys decrypts all encrypted keys with the given config and passphrase.
-// Avoids recomputation of similar s2k key derivations.
+// Avoids recomputation of similar s2k key derivations.
func DecryptPrivateKeys(keys []*PrivateKey, passphrase []byte) error {
// Create a cache to avoid recomputation of key derviations for the same passphrase.
s2kCache := &s2k.Cache{}
@@ -474,7 +653,7 @@ func DecryptPrivateKeys(keys []*PrivateKey, passphrase []byte) error {
}
// encrypt encrypts an unencrypted private key.
-func (pk *PrivateKey) encrypt(key []byte, params *s2k.Params, cipherFunction CipherFunction) error {
+func (pk *PrivateKey) encrypt(key []byte, params *s2k.Params, s2kType S2KType, cipherFunction CipherFunction, rand io.Reader) error {
if pk.Dummy() {
return errors.ErrDummyPrivateKey("dummy key found")
}
@@ -485,7 +664,15 @@ func (pk *PrivateKey) encrypt(key []byte, params *s2k.Params, cipherFunction Cip
if len(key) != cipherFunction.KeySize() {
return errors.InvalidArgumentError("supplied encryption key has the wrong size")
}
-
+
+ if params.Mode() == s2k.Argon2S2K && s2kType != S2KAEAD {
+ return errors.InvalidArgumentError("using Argon2 S2K without AEAD is not allowed")
+ }
+ if params.Mode() != s2k.Argon2S2K && params.Mode() != s2k.IteratedSaltedS2K &&
+ params.Mode() != s2k.SaltedS2K { // only allowed for high-entropy passphrases
+ return errors.InvalidArgumentError("insecure S2K mode")
+ }
+
priv := bytes.NewBuffer(nil)
err := pk.serializePrivateKey(priv)
if err != nil {
@@ -497,35 +684,53 @@ func (pk *PrivateKey) encrypt(key []byte, params *s2k.Params, cipherFunction Cip
pk.s2k, err = pk.s2kParams.Function()
if err != nil {
return err
- }
+ }
privateKeyBytes := priv.Bytes()
- pk.sha1Checksum = true
+ pk.s2kType = s2kType
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)
+ switch s2kType {
+ case S2KAEAD:
+ if pk.aead == 0 {
+ return errors.StructuralError("aead mode is not set on key")
}
- priv.Write([]byte{uint8(sum >> 8), uint8(sum)})
+ aead := pk.aead.new(block)
+ additionalData, err := pk.additionalData()
+ if err != nil {
+ return err
+ }
+ pk.iv = make([]byte, aead.NonceSize())
+ _, err = io.ReadFull(rand, pk.iv)
+ if err != nil {
+ return err
+ }
+ // Decrypt the encrypted key material with aead
+ pk.encryptedData = aead.Seal(nil, pk.iv, privateKeyBytes, additionalData)
+ case S2KSHA1, S2KCHECKSUM:
+ pk.iv = make([]byte, pk.cipher.blockSize())
+ _, err = io.ReadFull(rand, pk.iv)
+ if err != nil {
+ return err
+ }
+ cfb := cipher.NewCFBEncrypter(block, pk.iv)
+ if s2kType == S2KSHA1 {
+ h := sha1.New()
+ h.Write(privateKeyBytes)
+ sum := h.Sum(nil)
+ privateKeyBytes = append(privateKeyBytes, sum...)
+ } else {
+ var sum uint16
+ for _, b := range privateKeyBytes {
+ sum += uint16(b)
+ }
+ privateKeyBytes = append(privateKeyBytes, []byte{uint8(sum >> 8), uint8(sum)}...)
+ }
+ pk.encryptedData = make([]byte, len(privateKeyBytes))
+ cfb.XORKeyStream(pk.encryptedData, privateKeyBytes)
+ default:
+ return errors.InvalidArgumentError("invalid s2k type for encryption")
}
- pk.encryptedData = make([]byte, len(privateKeyBytes))
- cfb.XORKeyStream(pk.encryptedData, privateKeyBytes)
pk.Encrypted = true
pk.PrivateKey = nil
return err
@@ -544,8 +749,15 @@ func (pk *PrivateKey) EncryptWithConfig(passphrase []byte, config *Config) error
return err
}
s2k(key, passphrase)
+ s2kType := S2KSHA1
+ if config.AEAD() != nil {
+ s2kType = S2KAEAD
+ pk.aead = config.AEAD().Mode()
+ pk.cipher = config.Cipher()
+ key = pk.applyHKDF(key)
+ }
// Encrypt the private key with the derived encryption key.
- return pk.encrypt(key, params, config.Cipher())
+ return pk.encrypt(key, params, s2kType, config.Cipher(), config.Random())
}
// EncryptPrivateKeys encrypts all unencrypted keys with the given config and passphrase.
@@ -564,7 +776,16 @@ func EncryptPrivateKeys(keys []*PrivateKey, passphrase []byte, config *Config) e
s2k(encryptionKey, passphrase)
for _, key := range keys {
if key != nil && !key.Dummy() && !key.Encrypted {
- err = key.encrypt(encryptionKey, params, config.Cipher())
+ s2kType := S2KSHA1
+ if config.AEAD() != nil {
+ s2kType = S2KAEAD
+ key.aead = config.AEAD().Mode()
+ key.cipher = config.Cipher()
+ derivedKey := key.applyHKDF(encryptionKey)
+ err = key.encrypt(derivedKey, params, s2kType, config.Cipher(), config.Random())
+ } else {
+ err = key.encrypt(encryptionKey, params, s2kType, config.Cipher(), config.Random())
+ }
if err != nil {
return err
}
@@ -581,7 +802,7 @@ func (pk *PrivateKey) Encrypt(passphrase []byte) error {
S2KMode: s2k.IteratedSaltedS2K,
S2KCount: 65536,
Hash: crypto.SHA256,
- } ,
+ },
DefaultCipher: CipherAES256,
}
return pk.EncryptWithConfig(passphrase, config)
@@ -601,6 +822,14 @@ func (pk *PrivateKey) serializePrivateKey(w io.Writer) (err error) {
err = serializeEdDSAPrivateKey(w, priv)
case *ecdh.PrivateKey:
err = serializeECDHPrivateKey(w, priv)
+ case *x25519.PrivateKey:
+ err = serializeX25519PrivateKey(w, priv)
+ case *x448.PrivateKey:
+ err = serializeX448PrivateKey(w, priv)
+ case *ed25519.PrivateKey:
+ err = serializeEd25519PrivateKey(w, priv)
+ case *ed448.PrivateKey:
+ err = serializeEd448PrivateKey(w, priv)
default:
err = errors.InvalidArgumentError("unknown private key type")
}
@@ -621,8 +850,18 @@ func (pk *PrivateKey) parsePrivateKey(data []byte) (err error) {
return pk.parseECDHPrivateKey(data)
case PubKeyAlgoEdDSA:
return pk.parseEdDSAPrivateKey(data)
+ case PubKeyAlgoX25519:
+ return pk.parseX25519PrivateKey(data)
+ case PubKeyAlgoX448:
+ return pk.parseX448PrivateKey(data)
+ case PubKeyAlgoEd25519:
+ return pk.parseEd25519PrivateKey(data)
+ case PubKeyAlgoEd448:
+ return pk.parseEd448PrivateKey(data)
+ default:
+ err = errors.StructuralError("unknown private key type")
+ return
}
- panic("impossible")
}
func (pk *PrivateKey) parseRSAPrivateKey(data []byte) (err error) {
@@ -743,6 +982,86 @@ func (pk *PrivateKey) parseECDHPrivateKey(data []byte) (err error) {
return nil
}
+func (pk *PrivateKey) parseX25519PrivateKey(data []byte) (err error) {
+ publicKey := pk.PublicKey.PublicKey.(*x25519.PublicKey)
+ privateKey := x25519.NewPrivateKey(*publicKey)
+ privateKey.PublicKey = *publicKey
+
+ privateKey.Secret = make([]byte, x25519.KeySize)
+
+ if len(data) != x25519.KeySize {
+ err = errors.StructuralError("wrong x25519 key size")
+ return err
+ }
+ subtle.ConstantTimeCopy(1, privateKey.Secret, data)
+ if err = x25519.Validate(privateKey); err != nil {
+ return err
+ }
+ pk.PrivateKey = privateKey
+ return nil
+}
+
+func (pk *PrivateKey) parseX448PrivateKey(data []byte) (err error) {
+ publicKey := pk.PublicKey.PublicKey.(*x448.PublicKey)
+ privateKey := x448.NewPrivateKey(*publicKey)
+ privateKey.PublicKey = *publicKey
+
+ privateKey.Secret = make([]byte, x448.KeySize)
+
+ if len(data) != x448.KeySize {
+ err = errors.StructuralError("wrong x448 key size")
+ return err
+ }
+ subtle.ConstantTimeCopy(1, privateKey.Secret, data)
+ if err = x448.Validate(privateKey); err != nil {
+ return err
+ }
+ pk.PrivateKey = privateKey
+ return nil
+}
+
+func (pk *PrivateKey) parseEd25519PrivateKey(data []byte) (err error) {
+ publicKey := pk.PublicKey.PublicKey.(*ed25519.PublicKey)
+ privateKey := ed25519.NewPrivateKey(*publicKey)
+ privateKey.PublicKey = *publicKey
+
+ if len(data) != ed25519.SeedSize {
+ err = errors.StructuralError("wrong ed25519 key size")
+ return err
+ }
+ err = privateKey.UnmarshalByteSecret(data)
+ if err != nil {
+ return err
+ }
+ err = ed25519.Validate(privateKey)
+ if err != nil {
+ return err
+ }
+ pk.PrivateKey = privateKey
+ return nil
+}
+
+func (pk *PrivateKey) parseEd448PrivateKey(data []byte) (err error) {
+ publicKey := pk.PublicKey.PublicKey.(*ed448.PublicKey)
+ privateKey := ed448.NewPrivateKey(*publicKey)
+ privateKey.PublicKey = *publicKey
+
+ if len(data) != ed448.SeedSize {
+ err = errors.StructuralError("wrong ed448 key size")
+ return err
+ }
+ err = privateKey.UnmarshalByteSecret(data)
+ if err != nil {
+ return err
+ }
+ err = ed448.Validate(privateKey)
+ if err != nil {
+ return err
+ }
+ pk.PrivateKey = privateKey
+ return nil
+}
+
func (pk *PrivateKey) parseEdDSAPrivateKey(data []byte) (err error) {
eddsaPub := pk.PublicKey.PublicKey.(*eddsa.PublicKey)
eddsaPriv := eddsa.NewPrivateKey(*eddsaPub)
@@ -767,6 +1086,41 @@ func (pk *PrivateKey) parseEdDSAPrivateKey(data []byte) (err error) {
return nil
}
+func (pk *PrivateKey) additionalData() ([]byte, error) {
+ additionalData := bytes.NewBuffer(nil)
+ // Write additional data prefix based on packet type
+ var packetByte byte
+ if pk.PublicKey.IsSubkey {
+ packetByte = 0xc7
+ } else {
+ packetByte = 0xc5
+ }
+ // Write public key to additional data
+ _, err := additionalData.Write([]byte{packetByte})
+ if err != nil {
+ return nil, err
+ }
+ err = pk.PublicKey.serializeWithoutHeaders(additionalData)
+ if err != nil {
+ return nil, err
+ }
+ return additionalData.Bytes(), nil
+}
+
+func (pk *PrivateKey) applyHKDF(inputKey []byte) []byte {
+ var packetByte byte
+ if pk.PublicKey.IsSubkey {
+ packetByte = 0xc7
+ } else {
+ packetByte = 0xc5
+ }
+ associatedData := []byte{packetByte, byte(pk.Version), byte(pk.cipher), byte(pk.aead)}
+ hkdfReader := hkdf.New(sha256.New, inputKey, []byte{}, associatedData)
+ encryptionKey := make([]byte, pk.cipher.KeySize())
+ _, _ = readFull(hkdfReader, encryptionKey)
+ return encryptionKey
+}
+
func validateDSAParameters(priv *dsa.PrivateKey) error {
p := priv.P // group prime
q := priv.Q // subgroup order
diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/public_key.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/public_key.go
index 3402b8c140..f8da781bbe 100644
--- a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/public_key.go
+++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/public_key.go
@@ -5,7 +5,6 @@
package packet
import (
- "crypto"
"crypto/dsa"
"crypto/rsa"
"crypto/sha1"
@@ -21,23 +20,24 @@ import (
"github.com/ProtonMail/go-crypto/openpgp/ecdh"
"github.com/ProtonMail/go-crypto/openpgp/ecdsa"
+ "github.com/ProtonMail/go-crypto/openpgp/ed25519"
+ "github.com/ProtonMail/go-crypto/openpgp/ed448"
"github.com/ProtonMail/go-crypto/openpgp/eddsa"
"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"
+ "github.com/ProtonMail/go-crypto/openpgp/x25519"
+ "github.com/ProtonMail/go-crypto/openpgp/x448"
)
-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
+ PublicKey interface{} // *rsa.PublicKey, *dsa.PublicKey, *ecdsa.PublicKey or *eddsa.PublicKey, *x25519.PublicKey, *x448.PublicKey, *ed25519.PublicKey, *ed448.PublicKey
Fingerprint []byte
KeyId uint64
IsSubkey bool
@@ -61,11 +61,19 @@ func (pk *PublicKey) UpgradeToV5() {
pk.setFingerprintAndKeyId()
}
+// UpgradeToV6 updates the version of the key to v6, and updates all necessary
+// fields.
+func (pk *PublicKey) UpgradeToV6() error {
+ pk.Version = 6
+ pk.setFingerprintAndKeyId()
+ return pk.checkV6Compatibility()
+}
+
// signingKey provides a convenient abstraction over signature verification
// for v3 and v4 public keys.
type signingKey interface {
SerializeForHash(io.Writer) error
- SerializeSignaturePrefix(io.Writer)
+ SerializeSignaturePrefix(io.Writer) error
serializeWithoutHeaders(io.Writer) error
}
@@ -174,6 +182,54 @@ func NewEdDSAPublicKey(creationTime time.Time, pub *eddsa.PublicKey) *PublicKey
return pk
}
+func NewX25519PublicKey(creationTime time.Time, pub *x25519.PublicKey) *PublicKey {
+ pk := &PublicKey{
+ Version: 4,
+ CreationTime: creationTime,
+ PubKeyAlgo: PubKeyAlgoX25519,
+ PublicKey: pub,
+ }
+
+ pk.setFingerprintAndKeyId()
+ return pk
+}
+
+func NewX448PublicKey(creationTime time.Time, pub *x448.PublicKey) *PublicKey {
+ pk := &PublicKey{
+ Version: 4,
+ CreationTime: creationTime,
+ PubKeyAlgo: PubKeyAlgoX448,
+ PublicKey: pub,
+ }
+
+ pk.setFingerprintAndKeyId()
+ return pk
+}
+
+func NewEd25519PublicKey(creationTime time.Time, pub *ed25519.PublicKey) *PublicKey {
+ pk := &PublicKey{
+ Version: 4,
+ CreationTime: creationTime,
+ PubKeyAlgo: PubKeyAlgoEd25519,
+ PublicKey: pub,
+ }
+
+ pk.setFingerprintAndKeyId()
+ return pk
+}
+
+func NewEd448PublicKey(creationTime time.Time, pub *ed448.PublicKey) *PublicKey {
+ pk := &PublicKey{
+ Version: 4,
+ CreationTime: creationTime,
+ PubKeyAlgo: PubKeyAlgoEd448,
+ PublicKey: pub,
+ }
+
+ pk.setFingerprintAndKeyId()
+ return pk
+}
+
func (pk *PublicKey) parse(r io.Reader) (err error) {
// RFC 4880, section 5.5.2
var buf [6]byte
@@ -181,12 +237,19 @@ func (pk *PublicKey) parse(r io.Reader) (err error) {
if err != nil {
return
}
- if buf[0] != 4 && buf[0] != 5 {
+
+ pk.Version = int(buf[0])
+ if pk.Version != 4 && pk.Version != 5 && pk.Version != 6 {
return errors.UnsupportedError("public key version " + strconv.Itoa(int(buf[0])))
}
- pk.Version = int(buf[0])
- if pk.Version == 5 {
+ if V5Disabled && pk.Version == 5 {
+ return errors.UnsupportedError("support for parsing v5 entities is disabled; build with `-tags v5` if needed")
+ }
+
+ if pk.Version >= 5 {
+ // Read the four-octet scalar octet count
+ // The count is not used in this implementation
var n [4]byte
_, err = readFull(r, n[:])
if err != nil {
@@ -195,6 +258,7 @@ func (pk *PublicKey) parse(r io.Reader) (err error) {
}
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])
+ // Ignore four-ocet length
switch pk.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly:
err = pk.parseRSA(r)
@@ -208,6 +272,14 @@ func (pk *PublicKey) parse(r io.Reader) (err error) {
err = pk.parseECDH(r)
case PubKeyAlgoEdDSA:
err = pk.parseEdDSA(r)
+ case PubKeyAlgoX25519:
+ err = pk.parseX25519(r)
+ case PubKeyAlgoX448:
+ err = pk.parseX448(r)
+ case PubKeyAlgoEd25519:
+ err = pk.parseEd25519(r)
+ case PubKeyAlgoEd448:
+ err = pk.parseEd448(r)
default:
err = errors.UnsupportedError("public key type: " + strconv.Itoa(int(pk.PubKeyAlgo)))
}
@@ -221,21 +293,44 @@ func (pk *PublicKey) parse(r io.Reader) (err error) {
func (pk *PublicKey) setFingerprintAndKeyId() {
// RFC 4880, section 12.2
- if pk.Version == 5 {
+ if pk.Version >= 5 {
fingerprint := sha256.New()
- pk.SerializeForHash(fingerprint)
+ if err := pk.SerializeForHash(fingerprint); err != nil {
+ // Should not happen for a hash.
+ panic(err)
+ }
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)
+ if err := pk.SerializeForHash(fingerprint); err != nil {
+ // Should not happen for a hash.
+ panic(err)
+ }
pk.Fingerprint = make([]byte, 20)
copy(pk.Fingerprint, fingerprint.Sum(nil))
pk.KeyId = binary.BigEndian.Uint64(pk.Fingerprint[12:20])
}
}
+func (pk *PublicKey) checkV6Compatibility() error {
+ // Implementations MUST NOT accept or generate version 6 key material using the deprecated OIDs.
+ switch pk.PubKeyAlgo {
+ case PubKeyAlgoECDH:
+ curveInfo := ecc.FindByOid(pk.oid)
+ if curveInfo == nil {
+ return errors.UnsupportedError(fmt.Sprintf("unknown oid: %x", pk.oid))
+ }
+ if curveInfo.GenName == ecc.Curve25519GenName {
+ return errors.StructuralError("cannot generate v6 key with deprecated OID: Curve25519Legacy")
+ }
+ case PubKeyAlgoEdDSA:
+ return errors.StructuralError("cannot generate v6 key with deprecated algorithm: EdDSALegacy")
+ }
+ return nil
+}
+
// 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) {
@@ -324,16 +419,17 @@ func (pk *PublicKey) parseECDSA(r io.Reader) (err error) {
if _, err = pk.oid.ReadFrom(r); err != nil {
return
}
- pk.p = new(encoding.MPI)
- if _, err = pk.p.ReadFrom(r); err != nil {
- return
- }
curveInfo := ecc.FindByOid(pk.oid)
if curveInfo == nil {
return errors.UnsupportedError(fmt.Sprintf("unknown oid: %x", pk.oid))
}
+ pk.p = new(encoding.MPI)
+ if _, err = pk.p.ReadFrom(r); err != nil {
+ return
+ }
+
c, ok := curveInfo.Curve.(ecc.ECDSACurve)
if !ok {
return errors.UnsupportedError(fmt.Sprintf("unsupported oid: %x", pk.oid))
@@ -353,6 +449,17 @@ func (pk *PublicKey) parseECDH(r io.Reader) (err error) {
if _, err = pk.oid.ReadFrom(r); err != nil {
return
}
+
+ curveInfo := ecc.FindByOid(pk.oid)
+ if curveInfo == nil {
+ return errors.UnsupportedError(fmt.Sprintf("unknown oid: %x", pk.oid))
+ }
+
+ if pk.Version == 6 && curveInfo.GenName == ecc.Curve25519GenName {
+ // Implementations MUST NOT accept or generate version 6 key material using the deprecated OIDs.
+ return errors.StructuralError("cannot read v6 key with deprecated OID: Curve25519Legacy")
+ }
+
pk.p = new(encoding.MPI)
if _, err = pk.p.ReadFrom(r); err != nil {
return
@@ -362,12 +469,6 @@ func (pk *PublicKey) parseECDH(r io.Reader) (err error) {
return
}
- curveInfo := ecc.FindByOid(pk.oid)
-
- if curveInfo == nil {
- return errors.UnsupportedError(fmt.Sprintf("unknown oid: %x", pk.oid))
- }
-
c, ok := curveInfo.Curve.(ecc.ECDHCurve)
if !ok {
return errors.UnsupportedError(fmt.Sprintf("unsupported oid: %x", pk.oid))
@@ -396,10 +497,16 @@ func (pk *PublicKey) parseECDH(r io.Reader) (err error) {
}
func (pk *PublicKey) parseEdDSA(r io.Reader) (err error) {
+ if pk.Version == 6 {
+ // Implementations MUST NOT accept or generate version 6 key material using the deprecated OIDs.
+ return errors.StructuralError("cannot generate v6 key with deprecated algorithm: EdDSALegacy")
+ }
+
pk.oid = new(encoding.OID)
if _, err = pk.oid.ReadFrom(r); err != nil {
return
}
+
curveInfo := ecc.FindByOid(pk.oid)
if curveInfo == nil {
return errors.UnsupportedError(fmt.Sprintf("unknown oid: %x", pk.oid))
@@ -435,75 +542,145 @@ func (pk *PublicKey) parseEdDSA(r io.Reader) (err error) {
return
}
+func (pk *PublicKey) parseX25519(r io.Reader) (err error) {
+ point := make([]byte, x25519.KeySize)
+ _, err = io.ReadFull(r, point)
+ if err != nil {
+ return
+ }
+ pub := &x25519.PublicKey{
+ Point: point,
+ }
+ pk.PublicKey = pub
+ return
+}
+
+func (pk *PublicKey) parseX448(r io.Reader) (err error) {
+ point := make([]byte, x448.KeySize)
+ _, err = io.ReadFull(r, point)
+ if err != nil {
+ return
+ }
+ pub := &x448.PublicKey{
+ Point: point,
+ }
+ pk.PublicKey = pub
+ return
+}
+
+func (pk *PublicKey) parseEd25519(r io.Reader) (err error) {
+ point := make([]byte, ed25519.PublicKeySize)
+ _, err = io.ReadFull(r, point)
+ if err != nil {
+ return
+ }
+ pub := &ed25519.PublicKey{
+ Point: point,
+ }
+ pk.PublicKey = pub
+ return
+}
+
+func (pk *PublicKey) parseEd448(r io.Reader) (err error) {
+ point := make([]byte, ed448.PublicKeySize)
+ _, err = io.ReadFull(r, point)
+ if err != nil {
+ return
+ }
+ pub := &ed448.PublicKey{
+ Point: point,
+ }
+ pk.PublicKey = pub
+ 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)
+ if err := pk.SerializeSignaturePrefix(w); err != nil {
+ return err
+ }
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) {
+func (pk *PublicKey) SerializeSignaturePrefix(w io.Writer) error {
var pLength = pk.algorithmSpecificByteCount()
- if pk.Version == 5 {
- pLength += 10 // version, timestamp (4), algorithm, key octet count (4).
- w.Write([]byte{
- 0x9A,
+ // version, timestamp, algorithm
+ pLength += versionSize + timestampSize + algorithmSize
+ if pk.Version >= 5 {
+ // key octet count (4).
+ pLength += 4
+ _, err := w.Write([]byte{
+ // When a v4 signature is made over a key, the hash data starts with the octet 0x99, followed by a two-octet length
+ // of the key, and then the body of the key packet. When a v6 signature is made over a key, the hash data starts
+ // with the salt, then octet 0x9B, followed by a four-octet length of the key, and then the body of the key packet.
+ 0x95 + byte(pk.Version),
byte(pLength >> 24),
byte(pLength >> 16),
byte(pLength >> 8),
byte(pLength),
})
- return
+ return err
}
- pLength += 6
- w.Write([]byte{0x99, byte(pLength >> 8), byte(pLength)})
+ if _, err := w.Write([]byte{0x99, byte(pLength >> 8), byte(pLength)}); err != nil {
+ return err
+ }
+ return nil
}
func (pk *PublicKey) Serialize(w io.Writer) (err error) {
- length := 6 // 6 byte header
+ length := uint32(versionSize + timestampSize + algorithmSize) // 6 byte header
length += pk.algorithmSpecificByteCount()
- if pk.Version == 5 {
+ if pk.Version >= 5 {
length += 4 // octet key count
}
packetType := packetTypePublicKey
if pk.IsSubkey {
packetType = packetTypePublicSubkey
}
- err = serializeHeader(w, packetType, length)
+ err = serializeHeader(w, packetType, int(length))
if err != nil {
return
}
return pk.serializeWithoutHeaders(w)
}
-func (pk *PublicKey) algorithmSpecificByteCount() int {
- length := 0
+func (pk *PublicKey) algorithmSpecificByteCount() uint32 {
+ length := uint32(0)
switch pk.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly:
- length += int(pk.n.EncodedLength())
- length += int(pk.e.EncodedLength())
+ length += uint32(pk.n.EncodedLength())
+ length += uint32(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())
+ length += uint32(pk.p.EncodedLength())
+ length += uint32(pk.q.EncodedLength())
+ length += uint32(pk.g.EncodedLength())
+ length += uint32(pk.y.EncodedLength())
case PubKeyAlgoElGamal:
- length += int(pk.p.EncodedLength())
- length += int(pk.g.EncodedLength())
- length += int(pk.y.EncodedLength())
+ length += uint32(pk.p.EncodedLength())
+ length += uint32(pk.g.EncodedLength())
+ length += uint32(pk.y.EncodedLength())
case PubKeyAlgoECDSA:
- length += int(pk.oid.EncodedLength())
- length += int(pk.p.EncodedLength())
+ length += uint32(pk.oid.EncodedLength())
+ length += uint32(pk.p.EncodedLength())
case PubKeyAlgoECDH:
- length += int(pk.oid.EncodedLength())
- length += int(pk.p.EncodedLength())
- length += int(pk.kdf.EncodedLength())
+ length += uint32(pk.oid.EncodedLength())
+ length += uint32(pk.p.EncodedLength())
+ length += uint32(pk.kdf.EncodedLength())
case PubKeyAlgoEdDSA:
- length += int(pk.oid.EncodedLength())
- length += int(pk.p.EncodedLength())
+ length += uint32(pk.oid.EncodedLength())
+ length += uint32(pk.p.EncodedLength())
+ case PubKeyAlgoX25519:
+ length += x25519.KeySize
+ case PubKeyAlgoX448:
+ length += x448.KeySize
+ case PubKeyAlgoEd25519:
+ length += ed25519.PublicKeySize
+ case PubKeyAlgoEd448:
+ length += ed448.PublicKeySize
default:
panic("unknown public key algorithm")
}
@@ -522,7 +699,7 @@ func (pk *PublicKey) serializeWithoutHeaders(w io.Writer) (err error) {
return
}
- if pk.Version == 5 {
+ if pk.Version >= 5 {
n := pk.algorithmSpecificByteCount()
if _, err = w.Write([]byte{
byte(n >> 24), byte(n >> 16), byte(n >> 8), byte(n),
@@ -580,6 +757,22 @@ func (pk *PublicKey) serializeWithoutHeaders(w io.Writer) (err error) {
}
_, err = w.Write(pk.p.EncodedBytes())
return
+ case PubKeyAlgoX25519:
+ publicKey := pk.PublicKey.(*x25519.PublicKey)
+ _, err = w.Write(publicKey.Point)
+ return
+ case PubKeyAlgoX448:
+ publicKey := pk.PublicKey.(*x448.PublicKey)
+ _, err = w.Write(publicKey.Point)
+ return
+ case PubKeyAlgoEd25519:
+ publicKey := pk.PublicKey.(*ed25519.PublicKey)
+ _, err = w.Write(publicKey.Point)
+ return
+ case PubKeyAlgoEd448:
+ publicKey := pk.PublicKey.(*ed448.PublicKey)
+ _, err = w.Write(publicKey.Point)
+ return
}
return errors.InvalidArgumentError("bad public-key algorithm")
}
@@ -589,6 +782,20 @@ func (pk *PublicKey) CanSign() bool {
return pk.PubKeyAlgo != PubKeyAlgoRSAEncryptOnly && pk.PubKeyAlgo != PubKeyAlgoElGamal && pk.PubKeyAlgo != PubKeyAlgoECDH
}
+// VerifyHashTag returns nil iff sig appears to be a plausible signature of the data
+// hashed into signed, based solely on its HashTag. signed is mutated by this call.
+func VerifyHashTag(signed hash.Hash, sig *Signature) (err error) {
+ 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")
+ }
+ return nil
+}
+
// 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) {
@@ -600,7 +807,8 @@ func (pk *PublicKey) VerifySignature(signed hash.Hash, sig *Signature) (err erro
}
signed.Write(sig.HashSuffix)
hashBytes := signed.Sum(nil)
- if sig.Version == 5 && (hashBytes[0] != sig.HashTag[0] || hashBytes[1] != sig.HashTag[1]) {
+ // see discussion https://github.com/ProtonMail/go-crypto/issues/107
+ if sig.Version >= 5 && (hashBytes[0] != sig.HashTag[0] || hashBytes[1] != sig.HashTag[1]) {
return errors.SignatureError("hash tag doesn't match")
}
@@ -639,6 +847,18 @@ func (pk *PublicKey) VerifySignature(signed hash.Hash, sig *Signature) (err erro
return errors.SignatureError("EdDSA verification failure")
}
return nil
+ case PubKeyAlgoEd25519:
+ ed25519PublicKey := pk.PublicKey.(*ed25519.PublicKey)
+ if !ed25519.Verify(ed25519PublicKey, hashBytes, sig.EdSig) {
+ return errors.SignatureError("Ed25519 verification failure")
+ }
+ return nil
+ case PubKeyAlgoEd448:
+ ed448PublicKey := pk.PublicKey.(*ed448.PublicKey)
+ if !ed448.Verify(ed448PublicKey, hashBytes, sig.EdSig) {
+ return errors.SignatureError("ed448 verification failure")
+ }
+ return nil
default:
return errors.SignatureError("Unsupported public key algorithm used in signature")
}
@@ -646,11 +866,8 @@ func (pk *PublicKey) VerifySignature(signed hash.Hash, sig *Signature) (err erro
// 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()
+func keySignatureHash(pk, signed signingKey, hashFunc hash.Hash) (h hash.Hash, err error) {
+ h = hashFunc
// RFC 4880, section 5.2.4
err = pk.SerializeForHash(h)
@@ -662,10 +879,28 @@ func keySignatureHash(pk, signed signingKey, hashFunc crypto.Hash) (h hash.Hash,
return
}
+// VerifyKeyHashTag returns nil iff sig appears to be a plausible signature over this
+// primary key and subkey, based solely on its HashTag.
+func (pk *PublicKey) VerifyKeyHashTag(signed *PublicKey, sig *Signature) error {
+ preparedHash, err := sig.PrepareVerify()
+ if err != nil {
+ return err
+ }
+ h, err := keySignatureHash(pk, signed, preparedHash)
+ if err != nil {
+ return err
+ }
+ return VerifyHashTag(h, sig)
+}
+
// 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)
+ preparedHash, err := sig.PrepareVerify()
+ if err != nil {
+ return err
+ }
+ h, err := keySignatureHash(pk, signed, preparedHash)
if err != nil {
return err
}
@@ -679,10 +914,14 @@ func (pk *PublicKey) VerifyKeySignature(signed *PublicKey, sig *Signature) error
if sig.EmbeddedSignature == nil {
return errors.StructuralError("signing subkey is missing cross-signature")
}
+ preparedHashEmbedded, err := sig.EmbeddedSignature.PrepareVerify()
+ if err != nil {
+ return err
+ }
// 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 {
+ if h, err = keySignatureHash(pk, signed, preparedHashEmbedded); err != nil {
return errors.StructuralError("error while hashing for cross-signature: " + err.Error())
}
if err := signed.VerifySignature(h, sig.EmbeddedSignature); err != nil {
@@ -693,32 +932,44 @@ func (pk *PublicKey) VerifyKeySignature(signed *PublicKey, sig *Signature) 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)
+func keyRevocationHash(pk signingKey, hashFunc hash.Hash) (err error) {
+ return pk.SerializeForHash(hashFunc)
+}
- return
+// VerifyRevocationHashTag returns nil iff sig appears to be a plausible signature
+// over this public key, based solely on its HashTag.
+func (pk *PublicKey) VerifyRevocationHashTag(sig *Signature) (err error) {
+ preparedHash, err := sig.PrepareVerify()
+ if err != nil {
+ return err
+ }
+ if err = keyRevocationHash(pk, preparedHash); err != nil {
+ return err
+ }
+ return VerifyHashTag(preparedHash, sig)
}
// 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)
+ preparedHash, err := sig.PrepareVerify()
if err != nil {
return err
}
- return pk.VerifySignature(h, sig)
+ if err = keyRevocationHash(pk, preparedHash); err != nil {
+ return err
+ }
+ return pk.VerifySignature(preparedHash, sig)
}
// VerifySubkeyRevocationSignature returns nil iff sig is a valid subkey revocation signature,
// made by this public key, of signed.
func (pk *PublicKey) VerifySubkeyRevocationSignature(sig *Signature, signed *PublicKey) (err error) {
- h, err := keySignatureHash(pk, signed, sig.Hash)
+ preparedHash, err := sig.PrepareVerify()
+ if err != nil {
+ return err
+ }
+ h, err := keySignatureHash(pk, signed, preparedHash)
if err != nil {
return err
}
@@ -727,15 +978,15 @@ func (pk *PublicKey) VerifySubkeyRevocationSignature(sig *Signature, signed *Pub
// 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()
+func userIdSignatureHash(id string, pk *PublicKey, h hash.Hash) (err error) {
// RFC 4880, section 5.2.4
- pk.SerializeSignaturePrefix(h)
- pk.serializeWithoutHeaders(h)
+ if err := pk.SerializeSignaturePrefix(h); err != nil {
+ return err
+ }
+ if err := pk.serializeWithoutHeaders(h); err != nil {
+ return err
+ }
var buf [5]byte
buf[0] = 0xb4
@@ -746,16 +997,51 @@ func userIdSignatureHash(id string, pk *PublicKey, hashFunc crypto.Hash) (h hash
h.Write(buf[:])
h.Write([]byte(id))
- return
+ return nil
+}
+
+// directKeySignatureHash returns a Hash of the message that needs to be signed.
+func directKeySignatureHash(pk *PublicKey, h hash.Hash) (err error) {
+ return pk.SerializeForHash(h)
+}
+
+// VerifyUserIdHashTag returns nil iff sig appears to be a plausible signature over this
+// public key and UserId, based solely on its HashTag
+func (pk *PublicKey) VerifyUserIdHashTag(id string, sig *Signature) (err error) {
+ preparedHash, err := sig.PrepareVerify()
+ if err != nil {
+ return err
+ }
+ err = userIdSignatureHash(id, pk, preparedHash)
+ if err != nil {
+ return err
+ }
+ return VerifyHashTag(preparedHash, sig)
}
// 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)
+ h, err := sig.PrepareVerify()
+ if err != nil {
+ return err
+ }
+ if err := userIdSignatureHash(id, pub, h); err != nil {
+ return err
+ }
+ return pk.VerifySignature(h, sig)
+}
+
+// VerifyDirectKeySignature returns nil iff sig is a valid signature, made by this
+// public key.
+func (pk *PublicKey) VerifyDirectKeySignature(sig *Signature) (err error) {
+ h, err := sig.PrepareVerify()
if err != nil {
return err
}
+ if err := directKeySignatureHash(pk, h); err != nil {
+ return err
+ }
return pk.VerifySignature(h, sig)
}
@@ -786,21 +1072,49 @@ func (pk *PublicKey) BitLength() (bitLength uint16, err error) {
bitLength = pk.p.BitLength()
case PubKeyAlgoEdDSA:
bitLength = pk.p.BitLength()
+ case PubKeyAlgoX25519:
+ bitLength = x25519.KeySize * 8
+ case PubKeyAlgoX448:
+ bitLength = x448.KeySize * 8
+ case PubKeyAlgoEd25519:
+ bitLength = ed25519.PublicKeySize * 8
+ case PubKeyAlgoEd448:
+ bitLength = ed448.PublicKeySize * 8
default:
err = errors.InvalidArgumentError("bad public-key algorithm")
}
return
}
+// Curve returns the used elliptic curve of this public key.
+// Returns an error if no elliptic curve is used.
+func (pk *PublicKey) Curve() (curve Curve, err error) {
+ switch pk.PubKeyAlgo {
+ case PubKeyAlgoECDSA, PubKeyAlgoECDH, PubKeyAlgoEdDSA:
+ curveInfo := ecc.FindByOid(pk.oid)
+ if curveInfo == nil {
+ return "", errors.UnsupportedError(fmt.Sprintf("unknown oid: %x", pk.oid))
+ }
+ curve = Curve(curveInfo.GenName)
+ case PubKeyAlgoEd25519, PubKeyAlgoX25519:
+ curve = Curve25519
+ case PubKeyAlgoEd448, PubKeyAlgoX448:
+ curve = Curve448
+ default:
+ err = errors.InvalidArgumentError("public key does not operate with an elliptic curve")
+ }
+ 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) {
+ if pk.CreationTime.Unix() > currentTime.Unix() {
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)
+ return currentTime.Unix() > expiry.Unix()
}
diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/reader.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/reader.go
index 10215fe5f2..dd84092392 100644
--- a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/reader.go
+++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/reader.go
@@ -10,6 +10,12 @@ import (
"github.com/ProtonMail/go-crypto/openpgp/errors"
)
+type PacketReader interface {
+ Next() (p Packet, err error)
+ Push(reader io.Reader) (err error)
+ Unread(p Packet)
+}
+
// Reader reads packets from an io.Reader and allows packets to be 'unread' so
// that they result from the next call to Next.
type Reader struct {
@@ -26,37 +32,81 @@ type Reader struct {
const maxReaders = 32
// Next returns the most recently unread Packet, or reads another packet from
-// the top-most io.Reader. Unknown packet types are skipped.
+// the top-most io.Reader. Unknown/unsupported/Marker packet types are skipped.
func (r *Reader) Next() (p Packet, err error) {
+ for {
+ p, err := r.read()
+ if err == io.EOF {
+ break
+ } else if err != nil {
+ if _, ok := err.(errors.UnknownPacketTypeError); ok {
+ continue
+ }
+ if _, ok := err.(errors.UnsupportedError); ok {
+ switch p.(type) {
+ case *SymmetricallyEncrypted, *AEADEncrypted, *Compressed, *LiteralData:
+ return nil, err
+ }
+ continue
+ }
+ return nil, err
+ } else {
+ //A marker packet MUST be ignored when received
+ switch p.(type) {
+ case *Marker:
+ continue
+ }
+ return p, nil
+ }
+ }
+ return nil, io.EOF
+}
+
+// Next returns the most recently unread Packet, or reads another packet from
+// the top-most io.Reader. Unknown/Marker packet types are skipped while unsupported
+// packets are returned as UnsupportedPacket type.
+func (r *Reader) NextWithUnsupported() (p Packet, err error) {
+ for {
+ p, err = r.read()
+ if err == io.EOF {
+ break
+ } else if err != nil {
+ if _, ok := err.(errors.UnknownPacketTypeError); ok {
+ continue
+ }
+ if casteErr, ok := err.(errors.UnsupportedError); ok {
+ return &UnsupportedPacket{
+ IncompletePacket: p,
+ Error: casteErr,
+ }, nil
+ }
+ return
+ } else {
+ //A marker packet MUST be ignored when received
+ switch p.(type) {
+ case *Marker:
+ continue
+ }
+ return
+ }
+ }
+ return nil, io.EOF
+}
+
+func (r *Reader) read() (p Packet, err error) {
if len(r.q) > 0 {
p = r.q[len(r.q)-1]
r.q = r.q[:len(r.q)-1]
return
}
-
for len(r.readers) > 0 {
p, err = Read(r.readers[len(r.readers)-1])
- if err == nil {
- return
- }
if err == io.EOF {
r.readers = r.readers[:len(r.readers)-1]
continue
}
- // TODO: Add strict mode that rejects unknown packets, instead of ignoring them.
- if _, ok := err.(errors.UnknownPacketTypeError); ok {
- continue
- }
- if _, ok := err.(errors.UnsupportedError); ok {
- switch p.(type) {
- case *SymmetricallyEncrypted, *AEADEncrypted, *Compressed, *LiteralData:
- return nil, err
- }
- continue
- }
- return nil, err
+ return p, err
}
-
return nil, io.EOF
}
@@ -84,3 +134,76 @@ func NewReader(r io.Reader) *Reader {
readers: []io.Reader{r},
}
}
+
+// CheckReader is similar to Reader but additionally
+// uses the pushdown automata to verify the read packet sequence.
+type CheckReader struct {
+ Reader
+ verifier *SequenceVerifier
+ fullyRead bool
+}
+
+// Next returns the most recently unread Packet, or reads another packet from
+// the top-most io.Reader. Unknown packet types are skipped.
+// If the read packet sequence does not conform to the packet composition
+// rules in rfc4880, it returns an error.
+func (r *CheckReader) Next() (p Packet, err error) {
+ if r.fullyRead {
+ return nil, io.EOF
+ }
+ if len(r.q) > 0 {
+ p = r.q[len(r.q)-1]
+ r.q = r.q[:len(r.q)-1]
+ return
+ }
+ var errMsg error
+ for len(r.readers) > 0 {
+ p, errMsg, err = ReadWithCheck(r.readers[len(r.readers)-1], r.verifier)
+ if errMsg != nil {
+ err = errMsg
+ return
+ }
+ if err == nil {
+ return
+ }
+ if err == io.EOF {
+ r.readers = r.readers[:len(r.readers)-1]
+ continue
+ }
+ //A marker packet MUST be ignored when received
+ switch p.(type) {
+ case *Marker:
+ continue
+ }
+ if _, ok := err.(errors.UnknownPacketTypeError); ok {
+ continue
+ }
+ if _, ok := err.(errors.UnsupportedError); ok {
+ switch p.(type) {
+ case *SymmetricallyEncrypted, *AEADEncrypted, *Compressed, *LiteralData:
+ return nil, err
+ }
+ continue
+ }
+ return nil, err
+ }
+ if errMsg = r.verifier.Next(EOSSymbol); errMsg != nil {
+ return nil, errMsg
+ }
+ if errMsg = r.verifier.AssertValid(); errMsg != nil {
+ return nil, errMsg
+ }
+ r.fullyRead = true
+ return nil, io.EOF
+}
+
+func NewCheckReader(r io.Reader) *CheckReader {
+ return &CheckReader{
+ Reader: Reader{
+ q: nil,
+ readers: []io.Reader{r},
+ },
+ verifier: NewSequenceVerifier(),
+ fullyRead: false,
+ }
+}
diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/recipient.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/recipient.go
new file mode 100644
index 0000000000..fb2e362e4a
--- /dev/null
+++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/recipient.go
@@ -0,0 +1,15 @@
+package packet
+
+// Recipient type represents a Intended Recipient Fingerprint subpacket
+// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh#name-intended-recipient-fingerpr
+type Recipient struct {
+ KeyVersion int
+ Fingerprint []byte
+}
+
+func (r *Recipient) Serialize() []byte {
+ packet := make([]byte, len(r.Fingerprint)+1)
+ packet[0] = byte(r.KeyVersion)
+ copy(packet[1:], r.Fingerprint)
+ return packet
+}
diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/signature.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/signature.go
index 80d0bb98e0..3a4b366d87 100644
--- a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/signature.go
+++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/signature.go
@@ -8,13 +8,17 @@ import (
"bytes"
"crypto"
"crypto/dsa"
+ "encoding/asn1"
"encoding/binary"
"hash"
"io"
+ "math/big"
"strconv"
"time"
"github.com/ProtonMail/go-crypto/openpgp/ecdsa"
+ "github.com/ProtonMail/go-crypto/openpgp/ed25519"
+ "github.com/ProtonMail/go-crypto/openpgp/ed448"
"github.com/ProtonMail/go-crypto/openpgp/eddsa"
"github.com/ProtonMail/go-crypto/openpgp/errors"
"github.com/ProtonMail/go-crypto/openpgp/internal/algorithm"
@@ -22,7 +26,8 @@ import (
)
const (
- // See RFC 4880, section 5.2.3.21 for details.
+ // First octet of key flags.
+ // See RFC 9580, section 5.2.3.29 for details.
KeyFlagCertify = 1 << iota
KeyFlagSign
KeyFlagEncryptCommunications
@@ -33,12 +38,30 @@ const (
KeyFlagGroupKey
)
-// Signature represents a signature. See RFC 4880, section 5.2.
+const (
+ // First octet of keyserver preference flags.
+ // See RFC 9580, section 5.2.3.25 for details.
+ _ = 1 << iota
+ _
+ _
+ _
+ _
+ _
+ _
+ KeyserverPrefNoModify
+)
+
+const SaltNotationName = "salt@notations.openpgpjs.org"
+
+// Signature represents a signature. See RFC 9580, section 5.2.
type Signature struct {
Version int
SigType SignatureType
PubKeyAlgo PublicKeyAlgorithm
Hash crypto.Hash
+ // salt contains a random salt value for v6 signatures
+ // See RFC 9580 Section 5.2.4.
+ salt []byte
// HashSuffix is extra data that is hashed in after the signed data.
HashSuffix []byte
@@ -57,6 +80,7 @@ type Signature struct {
DSASigR, DSASigS encoding.Field
ECDSASigR, ECDSASigS encoding.Field
EdDSASigR, EdDSASigS encoding.Field
+ EdSig []byte
// rawSubpackets contains the unparsed subpackets, in order.
rawSubpackets []outputSubpacket
@@ -72,31 +96,42 @@ type Signature struct {
SignerUserId *string
IsPrimaryId *bool
Notations []*Notation
+ IntendedRecipients []*Recipient
// TrustLevel and TrustAmount can be set by the signer to assert that
// the key is not only valid but also trustworthy at the specified
// level.
- // See RFC 4880, section 5.2.3.13 for details.
+ // See RFC 9580, section 5.2.3.21 for details.
TrustLevel TrustLevel
TrustAmount TrustAmount
// TrustRegularExpression can be used in conjunction with trust Signature
// packets to limit the scope of the trust that is extended.
- // See RFC 4880, section 5.2.3.14 for details.
+ // See RFC 9580, section 5.2.3.22 for details.
TrustRegularExpression *string
+ // KeyserverPrefsValid is set if any keyserver preferences were given. See RFC 9580, section
+ // 5.2.3.25 for details.
+ KeyserverPrefsValid bool
+ KeyserverPrefNoModify bool
+
+ // PreferredKeyserver can be set to a URI where the latest version of the
+ // key that this signature is made over can be found. See RFC 9580, section
+ // 5.2.3.26 for details.
+ PreferredKeyserver string
+
// 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.
+ // policy under which the signature was issued. See RFC 9580, section
+ // 5.2.3.28 for details.
PolicyURI string
- // FlagsValid is set if any flags were given. See RFC 4880, section
- // 5.2.3.21 for details.
+ // FlagsValid is set if any flags were given. See RFC 9580, section
+ // 5.2.3.29 for details.
FlagsValid bool
FlagCertify, FlagSign, FlagEncryptCommunications, FlagEncryptStorage, FlagSplitKey, FlagAuthenticate, FlagGroupKey bool
// RevocationReason is set if this signature has been revoked.
- // See RFC 4880, section 5.2.3.23 for details.
+ // See RFC 9580, section 5.2.3.31 for details.
RevocationReason *ReasonForRevocation
RevocationReasonText string
@@ -113,26 +148,57 @@ type Signature struct {
outSubpackets []outputSubpacket
}
+// VerifiableSignature internally keeps state if the
+// the signature has been verified before.
+type VerifiableSignature struct {
+ Valid *bool // nil if it has not been verified yet
+ Packet *Signature
+}
+
+// NewVerifiableSig returns a struct of type VerifiableSignature referencing the input signature.
+func NewVerifiableSig(signature *Signature) *VerifiableSignature {
+ return &VerifiableSignature{
+ Packet: signature,
+ }
+}
+
+// Salt returns the signature salt for v6 signatures.
+func (sig *Signature) Salt() []byte {
+ if sig == nil {
+ return nil
+ }
+ return sig.salt
+}
+
func (sig *Signature) parse(r io.Reader) (err error) {
- // RFC 4880, section 5.2.3
- var buf [5]byte
+ // RFC 9580, section 5.2.3
+ var buf [7]byte
_, err = readFull(r, buf[:1])
if err != nil {
return
}
- if buf[0] != 4 && buf[0] != 5 {
+ sig.Version = int(buf[0])
+ if sig.Version != 4 && sig.Version != 5 && sig.Version != 6 {
err = errors.UnsupportedError("signature packet version " + strconv.Itoa(int(buf[0])))
return
}
- sig.Version = int(buf[0])
- _, err = readFull(r, buf[:5])
+
+ if V5Disabled && sig.Version == 5 {
+ return errors.UnsupportedError("support for parsing v5 entities is disabled; build with `-tags v5` if needed")
+ }
+
+ if sig.Version == 6 {
+ _, err = readFull(r, buf[:7])
+ } else {
+ _, err = readFull(r, buf[:5])
+ }
if err != nil {
return
}
sig.SigType = SignatureType(buf[0])
sig.PubKeyAlgo = PublicKeyAlgorithm(buf[1])
switch sig.PubKeyAlgo {
- case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA, PubKeyAlgoECDSA, PubKeyAlgoEdDSA:
+ case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA, PubKeyAlgoECDSA, PubKeyAlgoEdDSA, PubKeyAlgoEd25519, PubKeyAlgoEd448:
default:
err = errors.UnsupportedError("public key algorithm " + strconv.Itoa(int(sig.PubKeyAlgo)))
return
@@ -150,7 +216,17 @@ func (sig *Signature) parse(r io.Reader) (err error) {
return errors.UnsupportedError("hash function " + strconv.Itoa(int(buf[2])))
}
- hashedSubpacketsLength := int(buf[3])<<8 | int(buf[4])
+ var hashedSubpacketsLength int
+ if sig.Version == 6 {
+ // For a v6 signature, a four-octet length is used.
+ hashedSubpacketsLength =
+ int(buf[3])<<24 |
+ int(buf[4])<<16 |
+ int(buf[5])<<8 |
+ int(buf[6])
+ } else {
+ hashedSubpacketsLength = int(buf[3])<<8 | int(buf[4])
+ }
hashedSubpackets := make([]byte, hashedSubpacketsLength)
_, err = readFull(r, hashedSubpackets)
if err != nil {
@@ -166,11 +242,21 @@ func (sig *Signature) parse(r io.Reader) (err error) {
return
}
- _, err = readFull(r, buf[:2])
+ if sig.Version == 6 {
+ _, err = readFull(r, buf[:4])
+ } else {
+ _, err = readFull(r, buf[:2])
+ }
+
if err != nil {
return
}
- unhashedSubpacketsLength := int(buf[0])<<8 | int(buf[1])
+ var unhashedSubpacketsLength uint32
+ if sig.Version == 6 {
+ unhashedSubpacketsLength = uint32(buf[0])<<24 | uint32(buf[1])<<16 | uint32(buf[2])<<8 | uint32(buf[3])
+ } else {
+ unhashedSubpacketsLength = uint32(buf[0])<<8 | uint32(buf[1])
+ }
unhashedSubpackets := make([]byte, unhashedSubpacketsLength)
_, err = readFull(r, unhashedSubpackets)
if err != nil {
@@ -186,6 +272,30 @@ func (sig *Signature) parse(r io.Reader) (err error) {
return
}
+ if sig.Version == 6 {
+ // Only for v6 signatures, a variable-length field containing the salt
+ _, err = readFull(r, buf[:1])
+ if err != nil {
+ return
+ }
+ saltLength := int(buf[0])
+ var expectedSaltLength int
+ expectedSaltLength, err = SaltLengthForHash(sig.Hash)
+ if err != nil {
+ return
+ }
+ if saltLength != expectedSaltLength {
+ err = errors.StructuralError("unexpected salt size for the given hash algorithm")
+ return
+ }
+ salt := make([]byte, expectedSaltLength)
+ _, err = readFull(r, salt)
+ if err != nil {
+ return
+ }
+ sig.salt = salt
+ }
+
switch sig.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
sig.RSASignature = new(encoding.MPI)
@@ -216,6 +326,16 @@ func (sig *Signature) parse(r io.Reader) (err error) {
if _, err = sig.EdDSASigS.ReadFrom(r); err != nil {
return
}
+ case PubKeyAlgoEd25519:
+ sig.EdSig, err = ed25519.ReadSignature(r)
+ if err != nil {
+ return
+ }
+ case PubKeyAlgoEd448:
+ sig.EdSig, err = ed448.ReadSignature(r)
+ if err != nil {
+ return
+ }
default:
panic("unreachable")
}
@@ -223,7 +343,7 @@ func (sig *Signature) parse(r io.Reader) (err error) {
}
// parseSignatureSubpackets parses subpackets of the main signature packet. See
-// RFC 4880, section 5.2.3.1.
+// RFC 9580, section 5.2.3.1.
func parseSignatureSubpackets(sig *Signature, subpackets []byte, isHashed bool) (err error) {
for len(subpackets) > 0 {
subpackets, err = parseSignatureSubpacket(sig, subpackets, isHashed)
@@ -244,6 +364,7 @@ type signatureSubpacketType uint8
const (
creationTimeSubpacket signatureSubpacketType = 2
signatureExpirationSubpacket signatureSubpacketType = 3
+ exportableCertSubpacket signatureSubpacketType = 4
trustSubpacket signatureSubpacketType = 5
regularExpressionSubpacket signatureSubpacketType = 6
keyExpirationSubpacket signatureSubpacketType = 9
@@ -252,6 +373,8 @@ const (
notationDataSubpacket signatureSubpacketType = 20
prefHashAlgosSubpacket signatureSubpacketType = 21
prefCompressionSubpacket signatureSubpacketType = 22
+ keyserverPrefsSubpacket signatureSubpacketType = 23
+ prefKeyserverSubpacket signatureSubpacketType = 24
primaryUserIdSubpacket signatureSubpacketType = 25
policyUriSubpacket signatureSubpacketType = 26
keyFlagsSubpacket signatureSubpacketType = 27
@@ -260,12 +383,13 @@ const (
featuresSubpacket signatureSubpacketType = 30
embeddedSignatureSubpacket signatureSubpacketType = 32
issuerFingerprintSubpacket signatureSubpacketType = 33
+ intendedRecipientSubpacket signatureSubpacketType = 35
prefCipherSuitesSubpacket signatureSubpacketType = 39
)
// parseSignatureSubpacket parses a single subpacket. len(subpacket) is >= 1.
func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (rest []byte, err error) {
- // RFC 4880, section 5.2.3.1
+ // RFC 9580, section 5.2.3.7
var (
length uint32
packetType signatureSubpacketType
@@ -323,19 +447,24 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r
t := binary.BigEndian.Uint32(subpacket)
sig.CreationTime = time.Unix(int64(t), 0)
case signatureExpirationSubpacket:
- // Signature expiration time, section 5.2.3.10
+ // Signature expiration time, section 5.2.3.18
if len(subpacket) != 4 {
err = errors.StructuralError("expiration subpacket with bad length")
return
}
sig.SigLifetimeSecs = new(uint32)
*sig.SigLifetimeSecs = binary.BigEndian.Uint32(subpacket)
+ case exportableCertSubpacket:
+ if subpacket[0] == 0 {
+ err = errors.UnsupportedError("signature with non-exportable certification")
+ return
+ }
case trustSubpacket:
if len(subpacket) != 2 {
err = errors.StructuralError("trust subpacket with bad length")
return
}
- // Trust level and amount, section 5.2.3.13
+ // Trust level and amount, section 5.2.3.21
sig.TrustLevel = TrustLevel(subpacket[0])
sig.TrustAmount = TrustAmount(subpacket[1])
case regularExpressionSubpacket:
@@ -343,7 +472,7 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r
err = errors.StructuralError("regexp subpacket with bad length")
return
}
- // Trust regular expression, section 5.2.3.14
+ // Trust regular expression, section 5.2.3.22
// RFC specifies the string should be null-terminated; remove a null byte from the end
if subpacket[len(subpacket)-1] != 0x00 {
err = errors.StructuralError("expected regular expression to be null-terminated")
@@ -352,7 +481,7 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r
trustRegularExpression := string(subpacket[:len(subpacket)-1])
sig.TrustRegularExpression = &trustRegularExpression
case keyExpirationSubpacket:
- // Key expiration time, section 5.2.3.6
+ // Key expiration time, section 5.2.3.13
if len(subpacket) != 4 {
err = errors.StructuralError("key expiration subpacket with bad length")
return
@@ -360,23 +489,25 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r
sig.KeyLifetimeSecs = new(uint32)
*sig.KeyLifetimeSecs = binary.BigEndian.Uint32(subpacket)
case prefSymmetricAlgosSubpacket:
- // Preferred symmetric algorithms, section 5.2.3.7
+ // Preferred symmetric algorithms, section 5.2.3.14
sig.PreferredSymmetric = make([]byte, len(subpacket))
copy(sig.PreferredSymmetric, subpacket)
case issuerSubpacket:
- // Issuer, section 5.2.3.5
- if sig.Version > 4 {
- err = errors.StructuralError("issuer subpacket found in v5 key")
+ // Issuer, section 5.2.3.12
+ if sig.Version > 4 && isHashed {
+ err = errors.StructuralError("issuer subpacket found in v6 key")
return
}
if len(subpacket) != 8 {
err = errors.StructuralError("issuer subpacket with bad length")
return
}
- sig.IssuerKeyId = new(uint64)
- *sig.IssuerKeyId = binary.BigEndian.Uint64(subpacket)
+ if sig.Version <= 4 {
+ sig.IssuerKeyId = new(uint64)
+ *sig.IssuerKeyId = binary.BigEndian.Uint64(subpacket)
+ }
case notationDataSubpacket:
- // Notation data, section 5.2.3.16
+ // Notation data, section 5.2.3.24
if len(subpacket) < 8 {
err = errors.StructuralError("notation data subpacket with bad length")
return
@@ -398,15 +529,27 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r
sig.Notations = append(sig.Notations, ¬ation)
case prefHashAlgosSubpacket:
- // Preferred hash algorithms, section 5.2.3.8
+ // Preferred hash algorithms, section 5.2.3.16
sig.PreferredHash = make([]byte, len(subpacket))
copy(sig.PreferredHash, subpacket)
case prefCompressionSubpacket:
- // Preferred compression algorithms, section 5.2.3.9
+ // Preferred compression algorithms, section 5.2.3.17
sig.PreferredCompression = make([]byte, len(subpacket))
copy(sig.PreferredCompression, subpacket)
+ case keyserverPrefsSubpacket:
+ // Keyserver preferences, section 5.2.3.25
+ sig.KeyserverPrefsValid = true
+ if len(subpacket) == 0 {
+ return
+ }
+ if subpacket[0]&KeyserverPrefNoModify != 0 {
+ sig.KeyserverPrefNoModify = true
+ }
+ case prefKeyserverSubpacket:
+ // Preferred keyserver, section 5.2.3.26
+ sig.PreferredKeyserver = string(subpacket)
case primaryUserIdSubpacket:
- // Primary User ID, section 5.2.3.19
+ // Primary User ID, section 5.2.3.27
if len(subpacket) != 1 {
err = errors.StructuralError("primary user id subpacket with bad length")
return
@@ -416,12 +559,11 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r
*sig.IsPrimaryId = true
}
case keyFlagsSubpacket:
- // Key flags, section 5.2.3.21
+ // Key flags, section 5.2.3.29
+ sig.FlagsValid = true
if len(subpacket) == 0 {
- err = errors.StructuralError("empty key flags subpacket")
return
}
- sig.FlagsValid = true
if subpacket[0]&KeyFlagCertify != 0 {
sig.FlagCertify = true
}
@@ -447,16 +589,16 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r
userId := string(subpacket)
sig.SignerUserId = &userId
case reasonForRevocationSubpacket:
- // Reason For Revocation, section 5.2.3.23
+ // Reason For Revocation, section 5.2.3.31
if len(subpacket) == 0 {
err = errors.StructuralError("empty revocation reason subpacket")
return
}
sig.RevocationReason = new(ReasonForRevocation)
- *sig.RevocationReason = ReasonForRevocation(subpacket[0])
+ *sig.RevocationReason = NewReasonForRevocation(subpacket[0])
sig.RevocationReasonText = string(subpacket[1:])
case featuresSubpacket:
- // Features subpacket, section 5.2.3.24 specifies a very general
+ // Features subpacket, section 5.2.3.32 specifies a very general
// mechanism for OpenPGP implementations to signal support for new
// features.
if len(subpacket) > 0 {
@@ -470,16 +612,13 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r
}
case embeddedSignatureSubpacket:
// Only usage is in signatures that cross-certify
- // signing subkeys. section 5.2.3.26 describes the
+ // signing subkeys. section 5.2.3.34 describes the
// format, with its usage described in section 11.1
if sig.EmbeddedSignature != nil {
err = errors.StructuralError("Cannot have multiple embedded signatures")
return
}
sig.EmbeddedSignature = new(Signature)
- // Embedded signatures are required to be v4 signatures see
- // section 12.1. However, we only parse v4 signatures in this
- // file anyway.
if err := sig.EmbeddedSignature.parse(bytes.NewBuffer(subpacket)); err != nil {
return nil, err
}
@@ -487,7 +626,7 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r
return nil, errors.StructuralError("cross-signature has unexpected type " + strconv.Itoa(int(sigType)))
}
case policyUriSubpacket:
- // Policy URI, section 5.2.3.20
+ // Policy URI, section 5.2.3.28
sig.PolicyURI = string(subpacket)
case issuerFingerprintSubpacket:
if len(subpacket) == 0 {
@@ -495,20 +634,31 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r
return
}
v, l := subpacket[0], len(subpacket[1:])
- if v == 5 && l != 32 || v != 5 && l != 20 {
+ 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 {
+ if v >= 5 {
*sig.IssuerKeyId = binary.BigEndian.Uint64(subpacket[1:9])
} else {
*sig.IssuerKeyId = binary.BigEndian.Uint64(subpacket[13:21])
}
+ case intendedRecipientSubpacket:
+ // Intended Recipient Fingerprint, section 5.2.3.36
+ if len(subpacket) < 1 {
+ return nil, errors.StructuralError("invalid intended recipient fingerpring length")
+ }
+ version, length := subpacket[0], len(subpacket[1:])
+ if version >= 5 && length != 32 || version < 5 && length != 20 {
+ return nil, errors.StructuralError("invalid fingerprint length")
+ }
+ fingerprint := make([]byte, length)
+ copy(fingerprint, subpacket[1:])
+ sig.IntendedRecipients = append(sig.IntendedRecipients, &Recipient{int(version), fingerprint})
case prefCipherSuitesSubpacket:
- // Preferred AEAD cipher suites
- // See https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-07.html#name-preferred-aead-ciphersuites
+ // Preferred AEAD cipher suites, section 5.2.3.15
if len(subpacket)%2 != 0 {
err = errors.StructuralError("invalid aead cipher suite length")
return
@@ -550,9 +700,16 @@ func (sig *Signature) CheckKeyIdOrFingerprint(pk *PublicKey) bool {
return sig.IssuerKeyId != nil && *sig.IssuerKeyId == pk.KeyId
}
+func (sig *Signature) CheckKeyIdOrFingerprintExplicit(fingerprint []byte, keyId uint64) bool {
+ if sig.IssuerFingerprint != nil && len(sig.IssuerFingerprint) >= 20 && fingerprint != nil {
+ return bytes.Equal(sig.IssuerFingerprint, fingerprint)
+ }
+ return sig.IssuerKeyId != nil && *sig.IssuerKeyId == keyId
+}
+
// serializeSubpacketLength marshals the given length into to.
func serializeSubpacketLength(to []byte, length int) int {
- // RFC 4880, Section 4.2.2.
+ // RFC 9580, Section 4.2.1.
if length < 192 {
to[0] = byte(length)
return 1
@@ -598,20 +755,19 @@ func serializeSubpackets(to []byte, subpackets []outputSubpacket, hashed bool) {
to = to[n:]
}
}
- return
}
// SigExpired returns whether sig is a signature that has expired or is created
// in the future.
func (sig *Signature) SigExpired(currentTime time.Time) bool {
- if sig.CreationTime.After(currentTime) {
+ if sig.CreationTime.Unix() > currentTime.Unix() {
return true
}
if sig.SigLifetimeSecs == nil || *sig.SigLifetimeSecs == 0 {
return false
}
expiry := sig.CreationTime.Add(time.Duration(*sig.SigLifetimeSecs) * time.Second)
- return currentTime.After(expiry)
+ return currentTime.Unix() > expiry.Unix()
}
// buildHashSuffix constructs the HashSuffix member of sig in preparation for signing.
@@ -635,20 +791,36 @@ func (sig *Signature) buildHashSuffix(hashedSubpackets []byte) (err error) {
uint8(sig.SigType),
uint8(sig.PubKeyAlgo),
uint8(hashId),
- uint8(len(hashedSubpackets) >> 8),
- uint8(len(hashedSubpackets)),
})
+ hashedSubpacketsLength := len(hashedSubpackets)
+ if sig.Version == 6 {
+ // v6 signatures store the length in 4 octets
+ hashedFields.Write([]byte{
+ uint8(hashedSubpacketsLength >> 24),
+ uint8(hashedSubpacketsLength >> 16),
+ uint8(hashedSubpacketsLength >> 8),
+ uint8(hashedSubpacketsLength),
+ })
+ } else {
+ hashedFields.Write([]byte{
+ uint8(hashedSubpacketsLength >> 8),
+ uint8(hashedSubpacketsLength),
+ })
+ }
+ lenPrefix := hashedFields.Len()
hashedFields.Write(hashedSubpackets)
- var l uint64 = uint64(6 + len(hashedSubpackets))
+ var l uint64 = uint64(lenPrefix + len(hashedSubpackets))
if sig.Version == 5 {
+ // v5 case
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})
+ // v4 and v6 case
+ hashedFields.Write([]byte{byte(sig.Version), 0xff})
hashedFields.Write([]byte{
uint8(l >> 24), uint8(l >> 16), uint8(l >> 8), uint8(l),
})
@@ -676,6 +848,67 @@ func (sig *Signature) signPrepareHash(h hash.Hash) (digest []byte, err error) {
return
}
+// PrepareSign must be called to create a hash object before Sign for v6 signatures.
+// The created hash object initially hashes a randomly generated salt
+// as required by v6 signatures. The generated salt is stored in sig. If the signature is not v6,
+// the method returns an empty hash object.
+// See RFC 9580 Section 5.2.4.
+func (sig *Signature) PrepareSign(config *Config) (hash.Hash, error) {
+ if !sig.Hash.Available() {
+ return nil, errors.UnsupportedError("hash function")
+ }
+ hasher := sig.Hash.New()
+ if sig.Version == 6 {
+ if sig.salt == nil {
+ var err error
+ sig.salt, err = SignatureSaltForHash(sig.Hash, config.Random())
+ if err != nil {
+ return nil, err
+ }
+ }
+ hasher.Write(sig.salt)
+ }
+ return hasher, nil
+}
+
+// SetSalt sets the signature salt for v6 signatures.
+// Assumes salt is generated correctly and checks if length matches.
+// If the signature is not v6, the method ignores the salt.
+// Use PrepareSign whenever possible instead of generating and
+// hashing the salt externally.
+// See RFC 9580 Section 5.2.4.
+func (sig *Signature) SetSalt(salt []byte) error {
+ if sig.Version == 6 {
+ expectedSaltLength, err := SaltLengthForHash(sig.Hash)
+ if err != nil {
+ return err
+ }
+ if salt == nil || len(salt) != expectedSaltLength {
+ return errors.InvalidArgumentError("unexpected salt size for the given hash algorithm")
+ }
+ sig.salt = salt
+ }
+ return nil
+}
+
+// PrepareVerify must be called to create a hash object before verifying v6 signatures.
+// The created hash object initially hashes the internally stored salt.
+// If the signature is not v6, the method returns an empty hash object.
+// See RFC 9580 Section 5.2.4.
+func (sig *Signature) PrepareVerify() (hash.Hash, error) {
+ if !sig.Hash.Available() {
+ return nil, errors.UnsupportedError("hash function")
+ }
+ hasher := sig.Hash.New()
+ if sig.Version == 6 {
+ if sig.salt == nil {
+ return nil, errors.StructuralError("v6 requires a salt for the hash to be signed")
+ }
+ hasher.Write(sig.salt)
+ }
+ return hasher, nil
+}
+
// Sign signs a message with a private key. The hash, h, must contain
// the hash of the message to be signed and will be mutated by this function.
// On success, the signature is stored in sig. Call Serialize to write it out.
@@ -686,6 +919,20 @@ func (sig *Signature) Sign(h hash.Hash, priv *PrivateKey, config *Config) (err e
}
sig.Version = priv.PublicKey.Version
sig.IssuerFingerprint = priv.PublicKey.Fingerprint
+ if sig.Version < 6 && config.RandomizeSignaturesViaNotation() {
+ sig.removeNotationsWithName(SaltNotationName)
+ salt, err := SignatureSaltForHash(sig.Hash, config.Random())
+ if err != nil {
+ return err
+ }
+ notation := Notation{
+ Name: SaltNotationName,
+ Value: salt,
+ IsCritical: false,
+ IsHumanReadable: false,
+ }
+ sig.Notations = append(sig.Notations, ¬ation)
+ }
sig.outSubpackets, err = sig.buildSubpackets(priv.PublicKey)
if err != nil {
return err
@@ -715,8 +962,16 @@ func (sig *Signature) Sign(h hash.Hash, priv *PrivateKey, config *Config) (err e
sig.DSASigS = new(encoding.MPI).SetBig(s)
}
case PubKeyAlgoECDSA:
- sk := priv.PrivateKey.(*ecdsa.PrivateKey)
- r, s, err := ecdsa.Sign(config.Random(), sk, digest)
+ var r, s *big.Int
+ if sk, ok := priv.PrivateKey.(*ecdsa.PrivateKey); ok {
+ r, s, err = ecdsa.Sign(config.Random(), sk, digest)
+ } else {
+ var b []byte
+ b, err = priv.PrivateKey.(crypto.Signer).Sign(config.Random(), digest, sig.Hash)
+ if err == nil {
+ r, s, err = unwrapECDSASig(b)
+ }
+ }
if err == nil {
sig.ECDSASigR = new(encoding.MPI).SetBig(r)
@@ -729,6 +984,18 @@ func (sig *Signature) Sign(h hash.Hash, priv *PrivateKey, config *Config) (err e
sig.EdDSASigR = encoding.NewMPI(r)
sig.EdDSASigS = encoding.NewMPI(s)
}
+ case PubKeyAlgoEd25519:
+ sk := priv.PrivateKey.(*ed25519.PrivateKey)
+ signature, err := ed25519.Sign(sk, digest)
+ if err == nil {
+ sig.EdSig = signature
+ }
+ case PubKeyAlgoEd448:
+ sk := priv.PrivateKey.(*ed448.PrivateKey)
+ signature, err := ed448.Sign(sk, digest)
+ if err == nil {
+ sig.EdSig = signature
+ }
default:
err = errors.UnsupportedError("public key algorithm: " + strconv.Itoa(int(sig.PubKeyAlgo)))
}
@@ -736,6 +1003,18 @@ func (sig *Signature) Sign(h hash.Hash, priv *PrivateKey, config *Config) (err e
return
}
+// unwrapECDSASig parses the two integer components of an ASN.1-encoded ECDSA signature.
+func unwrapECDSASig(b []byte) (r, s *big.Int, err error) {
+ var ecsdaSig struct {
+ R, S *big.Int
+ }
+ _, err = asn1.Unmarshal(b, &ecsdaSig)
+ if err != nil {
+ return
+ }
+ return ecsdaSig.R, ecsdaSig.S, nil
+}
+
// SignUserId computes a signature from priv, asserting that pub is a valid
// key for the identity id. On success, the signature is stored in sig. Call
// Serialize to write it out.
@@ -744,11 +1023,32 @@ func (sig *Signature) SignUserId(id string, pub *PublicKey, priv *PrivateKey, co
if priv.Dummy() {
return errors.ErrDummyPrivateKey("dummy key found")
}
- h, err := userIdSignatureHash(id, pub, sig.Hash)
+ prepareHash, err := sig.PrepareSign(config)
if err != nil {
return err
}
- return sig.Sign(h, priv, config)
+ if err := userIdSignatureHash(id, pub, prepareHash); err != nil {
+ return err
+ }
+ return sig.Sign(prepareHash, priv, config)
+}
+
+// SignDirectKeyBinding computes a signature from 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) SignDirectKeyBinding(pub *PublicKey, priv *PrivateKey, config *Config) error {
+ if priv.Dummy() {
+ return errors.ErrDummyPrivateKey("dummy key found")
+ }
+ prepareHash, err := sig.PrepareSign(config)
+ if err != nil {
+ return err
+ }
+ if err := directKeySignatureHash(pub, prepareHash); err != nil {
+ return err
+ }
+ return sig.Sign(prepareHash, priv, config)
}
// CrossSignKey computes a signature from signingKey on pub hashed using hashKey. On success,
@@ -756,7 +1056,11 @@ func (sig *Signature) SignUserId(id string, pub *PublicKey, priv *PrivateKey, co
// 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)
+ prepareHash, err := sig.PrepareSign(config)
+ if err != nil {
+ return err
+ }
+ h, err := keySignatureHash(hashKey, pub, prepareHash)
if err != nil {
return err
}
@@ -770,7 +1074,11 @@ func (sig *Signature) SignKey(pub *PublicKey, priv *PrivateKey, config *Config)
if priv.Dummy() {
return errors.ErrDummyPrivateKey("dummy key found")
}
- h, err := keySignatureHash(&priv.PublicKey, pub, sig.Hash)
+ prepareHash, err := sig.PrepareSign(config)
+ if err != nil {
+ return err
+ }
+ h, err := keySignatureHash(&priv.PublicKey, pub, prepareHash)
if err != nil {
return err
}
@@ -781,11 +1089,14 @@ func (sig *Signature) SignKey(pub *PublicKey, priv *PrivateKey, config *Config)
// 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)
+ prepareHash, err := sig.PrepareSign(config)
if err != nil {
return err
}
- return sig.Sign(h, priv, config)
+ if err := keyRevocationHash(pub, prepareHash); err != nil {
+ return err
+ }
+ return sig.Sign(prepareHash, priv, config)
}
// RevokeSubkey computes a subkey revocation signature of pub using priv.
@@ -802,7 +1113,7 @@ func (sig *Signature) Serialize(w io.Writer) (err error) {
if len(sig.outSubpackets) == 0 {
sig.outSubpackets = sig.rawSubpackets
}
- if sig.RSASignature == nil && sig.DSASigR == nil && sig.ECDSASigR == nil && sig.EdDSASigR == nil {
+ if sig.RSASignature == nil && sig.DSASigR == nil && sig.ECDSASigR == nil && sig.EdDSASigR == nil && sig.EdSig == nil {
return errors.InvalidArgumentError("Signature: need to call Sign, SignUserId or SignKey before Serialize")
}
@@ -819,16 +1130,24 @@ func (sig *Signature) Serialize(w io.Writer) (err error) {
case PubKeyAlgoEdDSA:
sigLength = int(sig.EdDSASigR.EncodedLength())
sigLength += int(sig.EdDSASigS.EncodedLength())
+ case PubKeyAlgoEd25519:
+ sigLength = ed25519.SignatureSize
+ case PubKeyAlgoEd448:
+ sigLength = ed448.SignatureSize
default:
panic("impossible")
}
+ hashedSubpacketsLen := subpacketsLength(sig.outSubpackets, true)
unhashedSubpacketsLen := subpacketsLength(sig.outSubpackets, false)
- length := len(sig.HashSuffix) - 6 /* trailer not included */ +
+ length := 4 + /* length of version|signature type|public-key algorithm|hash algorithm */
+ 2 /* length of hashed subpackets */ + hashedSubpacketsLen +
2 /* length of unhashed subpackets */ + unhashedSubpacketsLen +
2 /* hash tag */ + sigLength
- if sig.Version == 5 {
- length -= 4 // eight-octet instead of four-octet big endian
+ if sig.Version == 6 {
+ length += 4 + /* the two length fields are four-octet instead of two */
+ 1 + /* salt length */
+ len(sig.salt) /* length salt */
}
err = serializeHeader(w, packetTypeSignature, length)
if err != nil {
@@ -842,18 +1161,41 @@ func (sig *Signature) Serialize(w io.Writer) (err error) {
}
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]
+ var fields []byte
+ if sig.Version == 6 {
+ // v6 signatures use 4 octets for length
+ hashedSubpacketsLen :=
+ uint32(uint32(sig.HashSuffix[4])<<24) |
+ uint32(uint32(sig.HashSuffix[5])<<16) |
+ uint32(uint32(sig.HashSuffix[6])<<8) |
+ uint32(sig.HashSuffix[7])
+ fields = sig.HashSuffix[:8+hashedSubpacketsLen]
+ } else {
+ hashedSubpacketsLen := uint16(uint16(sig.HashSuffix[4])<<8) |
+ uint16(sig.HashSuffix[5])
+ fields = sig.HashSuffix[:6+hashedSubpacketsLen]
+
+ }
_, err = w.Write(fields)
if err != nil {
return
}
unhashedSubpacketsLen := subpacketsLength(sig.outSubpackets, false)
- unhashedSubpackets := make([]byte, 2+unhashedSubpacketsLen)
- unhashedSubpackets[0] = byte(unhashedSubpacketsLen >> 8)
- unhashedSubpackets[1] = byte(unhashedSubpacketsLen)
- serializeSubpackets(unhashedSubpackets[2:], sig.outSubpackets, false)
+ var unhashedSubpackets []byte
+ if sig.Version == 6 {
+ unhashedSubpackets = make([]byte, 4+unhashedSubpacketsLen)
+ unhashedSubpackets[0] = byte(unhashedSubpacketsLen >> 24)
+ unhashedSubpackets[1] = byte(unhashedSubpacketsLen >> 16)
+ unhashedSubpackets[2] = byte(unhashedSubpacketsLen >> 8)
+ unhashedSubpackets[3] = byte(unhashedSubpacketsLen)
+ serializeSubpackets(unhashedSubpackets[4:], sig.outSubpackets, false)
+ } else {
+ unhashedSubpackets = make([]byte, 2+unhashedSubpacketsLen)
+ unhashedSubpackets[0] = byte(unhashedSubpacketsLen >> 8)
+ unhashedSubpackets[1] = byte(unhashedSubpacketsLen)
+ serializeSubpackets(unhashedSubpackets[2:], sig.outSubpackets, false)
+ }
_, err = w.Write(unhashedSubpackets)
if err != nil {
@@ -864,6 +1206,18 @@ func (sig *Signature) serializeBody(w io.Writer) (err error) {
return
}
+ if sig.Version == 6 {
+ // write salt for v6 signatures
+ _, err = w.Write([]byte{uint8(len(sig.salt))})
+ if err != nil {
+ return
+ }
+ _, err = w.Write(sig.salt)
+ if err != nil {
+ return
+ }
+ }
+
switch sig.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
_, err = w.Write(sig.RSASignature.EncodedBytes())
@@ -882,6 +1236,10 @@ func (sig *Signature) serializeBody(w io.Writer) (err error) {
return
}
_, err = w.Write(sig.EdDSASigS.EncodedBytes())
+ case PubKeyAlgoEd25519:
+ err = ed25519.WriteSignature(w, sig.EdSig)
+ case PubKeyAlgoEd448:
+ err = ed448.WriteSignature(w, sig.EdSig)
default:
panic("impossible")
}
@@ -899,28 +1257,81 @@ type outputSubpacket struct {
func (sig *Signature) buildSubpackets(issuer PublicKey) (subpackets []outputSubpacket, err error) {
creationTime := make([]byte, 4)
binary.BigEndian.PutUint32(creationTime, uint32(sig.CreationTime.Unix()))
- subpackets = append(subpackets, outputSubpacket{true, creationTimeSubpacket, false, creationTime})
-
+ // Signature Creation Time
+ subpackets = append(subpackets, outputSubpacket{true, creationTimeSubpacket, true, creationTime})
+ // Signature Expiration Time
+ if sig.SigLifetimeSecs != nil && *sig.SigLifetimeSecs != 0 {
+ sigLifetime := make([]byte, 4)
+ binary.BigEndian.PutUint32(sigLifetime, *sig.SigLifetimeSecs)
+ subpackets = append(subpackets, outputSubpacket{true, signatureExpirationSubpacket, true, sigLifetime})
+ }
+ // Trust Signature
+ if sig.TrustLevel != 0 {
+ subpackets = append(subpackets, outputSubpacket{true, trustSubpacket, true, []byte{byte(sig.TrustLevel), byte(sig.TrustAmount)}})
+ }
+ // Regular Expression
+ if sig.TrustRegularExpression != nil {
+ // RFC specifies the string should be null-terminated; add a null byte to the end
+ subpackets = append(subpackets, outputSubpacket{true, regularExpressionSubpacket, true, []byte(*sig.TrustRegularExpression + "\000")})
+ }
+ // Key Expiration Time
+ if sig.KeyLifetimeSecs != nil && *sig.KeyLifetimeSecs != 0 {
+ keyLifetime := make([]byte, 4)
+ binary.BigEndian.PutUint32(keyLifetime, *sig.KeyLifetimeSecs)
+ subpackets = append(subpackets, outputSubpacket{true, keyExpirationSubpacket, true, keyLifetime})
+ }
+ // Preferred Symmetric Ciphers for v1 SEIPD
+ if len(sig.PreferredSymmetric) > 0 {
+ subpackets = append(subpackets, outputSubpacket{true, prefSymmetricAlgosSubpacket, false, sig.PreferredSymmetric})
+ }
+ // Issuer Key ID
if sig.IssuerKeyId != nil && sig.Version == 4 {
keyId := make([]byte, 8)
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, sig.Version == 5, contents})
+ // Notation Data
+ for _, notation := range sig.Notations {
+ subpackets = append(
+ subpackets,
+ outputSubpacket{
+ true,
+ notationDataSubpacket,
+ notation.IsCritical,
+ notation.getData(),
+ })
}
- if sig.SignerUserId != nil {
- subpackets = append(subpackets, outputSubpacket{true, signerUserIdSubpacket, false, []byte(*sig.SignerUserId)})
+ // Preferred Hash Algorithms
+ if len(sig.PreferredHash) > 0 {
+ subpackets = append(subpackets, outputSubpacket{true, prefHashAlgosSubpacket, false, sig.PreferredHash})
}
- if sig.SigLifetimeSecs != nil && *sig.SigLifetimeSecs != 0 {
- sigLifetime := make([]byte, 4)
- binary.BigEndian.PutUint32(sigLifetime, *sig.SigLifetimeSecs)
- subpackets = append(subpackets, outputSubpacket{true, signatureExpirationSubpacket, true, sigLifetime})
+ // Preferred Compression Algorithms
+ if len(sig.PreferredCompression) > 0 {
+ subpackets = append(subpackets, outputSubpacket{true, prefCompressionSubpacket, false, sig.PreferredCompression})
}
-
+ // Keyserver Preferences
+ // Keyserver preferences may only appear in self-signatures or certification signatures.
+ if sig.KeyserverPrefsValid {
+ var prefs byte
+ if sig.KeyserverPrefNoModify {
+ prefs |= KeyserverPrefNoModify
+ }
+ subpackets = append(subpackets, outputSubpacket{true, keyserverPrefsSubpacket, false, []byte{prefs}})
+ }
+ // Preferred Keyserver
+ if len(sig.PreferredKeyserver) > 0 {
+ subpackets = append(subpackets, outputSubpacket{true, prefKeyserverSubpacket, false, []uint8(sig.PreferredKeyserver)})
+ }
+ // Primary User ID
+ if sig.IsPrimaryId != nil && *sig.IsPrimaryId {
+ subpackets = append(subpackets, outputSubpacket{true, primaryUserIdSubpacket, false, []byte{1}})
+ }
+ // Policy URI
+ if len(sig.PolicyURI) > 0 {
+ subpackets = append(subpackets, outputSubpacket{true, policyUriSubpacket, false, []uint8(sig.PolicyURI)})
+ }
+ // Key Flags
// Key flags may only appear in self-signatures or certification signatures.
-
if sig.FlagsValid {
var flags byte
if sig.FlagCertify {
@@ -944,22 +1355,19 @@ func (sig *Signature) buildSubpackets(issuer PublicKey) (subpackets []outputSubp
if sig.FlagGroupKey {
flags |= KeyFlagGroupKey
}
- subpackets = append(subpackets, outputSubpacket{true, keyFlagsSubpacket, false, []byte{flags}})
+ subpackets = append(subpackets, outputSubpacket{true, keyFlagsSubpacket, true, []byte{flags}})
}
-
- for _, notation := range sig.Notations {
- subpackets = append(
- subpackets,
- outputSubpacket{
- true,
- notationDataSubpacket,
- notation.IsCritical,
- notation.getData(),
- })
+ // Signer's User ID
+ if sig.SignerUserId != nil {
+ subpackets = append(subpackets, outputSubpacket{true, signerUserIdSubpacket, false, []byte(*sig.SignerUserId)})
}
-
- // The following subpackets may only appear in self-signatures.
-
+ // Reason for Revocation
+ // Revocation reason appears only in revocation signatures and is serialized as per section 5.2.3.31.
+ if sig.RevocationReason != nil {
+ subpackets = append(subpackets, outputSubpacket{true, reasonForRevocationSubpacket, true,
+ append([]uint8{uint8(*sig.RevocationReason)}, []uint8(sig.RevocationReasonText)...)})
+ }
+ // Features
var features = byte(0x00)
if sig.SEIPDv1 {
features |= 0x01
@@ -967,46 +1375,36 @@ func (sig *Signature) buildSubpackets(issuer PublicKey) (subpackets []outputSubp
if sig.SEIPDv2 {
features |= 0x08
}
-
if features != 0x00 {
subpackets = append(subpackets, outputSubpacket{true, featuresSubpacket, false, []byte{features}})
}
-
- if sig.TrustLevel != 0 {
- subpackets = append(subpackets, outputSubpacket{true, trustSubpacket, true, []byte{byte(sig.TrustLevel), byte(sig.TrustAmount)}})
- }
-
- if sig.TrustRegularExpression != nil {
- // RFC specifies the string should be null-terminated; add a null byte to the end
- subpackets = append(subpackets, outputSubpacket{true, regularExpressionSubpacket, true, []byte(*sig.TrustRegularExpression + "\000")})
- }
-
- if sig.KeyLifetimeSecs != nil && *sig.KeyLifetimeSecs != 0 {
- keyLifetime := make([]byte, 4)
- binary.BigEndian.PutUint32(keyLifetime, *sig.KeyLifetimeSecs)
- subpackets = append(subpackets, outputSubpacket{true, keyExpirationSubpacket, true, keyLifetime})
- }
-
- if sig.IsPrimaryId != nil && *sig.IsPrimaryId {
- subpackets = append(subpackets, outputSubpacket{true, primaryUserIdSubpacket, false, []byte{1}})
- }
-
- if len(sig.PreferredSymmetric) > 0 {
- subpackets = append(subpackets, outputSubpacket{true, prefSymmetricAlgosSubpacket, false, sig.PreferredSymmetric})
- }
-
- if len(sig.PreferredHash) > 0 {
- subpackets = append(subpackets, outputSubpacket{true, prefHashAlgosSubpacket, false, sig.PreferredHash})
+ // Embedded Signature
+ // EmbeddedSignature appears only in subkeys capable of signing and is serialized as per section 5.2.3.34.
+ 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()})
}
-
- if len(sig.PreferredCompression) > 0 {
- subpackets = append(subpackets, outputSubpacket{true, prefCompressionSubpacket, false, sig.PreferredCompression})
+ // Issuer Fingerprint
+ if sig.IssuerFingerprint != nil {
+ contents := append([]uint8{uint8(issuer.Version)}, sig.IssuerFingerprint...)
+ subpackets = append(subpackets, outputSubpacket{true, issuerFingerprintSubpacket, sig.Version >= 5, contents})
}
-
- if len(sig.PolicyURI) > 0 {
- subpackets = append(subpackets, outputSubpacket{true, policyUriSubpacket, false, []uint8(sig.PolicyURI)})
+ // Intended Recipient Fingerprint
+ for _, recipient := range sig.IntendedRecipients {
+ subpackets = append(
+ subpackets,
+ outputSubpacket{
+ true,
+ intendedRecipientSubpacket,
+ false,
+ recipient.Serialize(),
+ })
}
-
+ // Preferred AEAD Ciphersuites
if len(sig.PreferredCipherSuites) > 0 {
serialized := make([]byte, len(sig.PreferredCipherSuites)*2)
for i, cipherSuite := range sig.PreferredCipherSuites {
@@ -1015,23 +1413,6 @@ func (sig *Signature) buildSubpackets(issuer PublicKey) (subpackets []outputSubp
}
subpackets = append(subpackets, outputSubpacket{true, prefCipherSuitesSubpacket, false, serialized})
}
-
- // 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{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
}
@@ -1073,8 +1454,6 @@ func (sig *Signature) AddMetadataToHashSuffix() {
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),
@@ -1082,3 +1461,49 @@ func (sig *Signature) AddMetadataToHashSuffix() {
})
sig.HashSuffix = suffix.Bytes()
}
+
+// SaltLengthForHash selects the required salt length for the given hash algorithm,
+// as per Table 23 (Hash algorithm registry) of the crypto refresh.
+// See RFC 9580 Section 9.5.
+func SaltLengthForHash(hash crypto.Hash) (int, error) {
+ switch hash {
+ case crypto.SHA256, crypto.SHA224, crypto.SHA3_256:
+ return 16, nil
+ case crypto.SHA384:
+ return 24, nil
+ case crypto.SHA512, crypto.SHA3_512:
+ return 32, nil
+ default:
+ return 0, errors.UnsupportedError("hash function not supported for V6 signatures")
+ }
+}
+
+// SignatureSaltForHash generates a random signature salt
+// with the length for the given hash algorithm.
+// See RFC 9580 Section 9.5.
+func SignatureSaltForHash(hash crypto.Hash, randReader io.Reader) ([]byte, error) {
+ saltLength, err := SaltLengthForHash(hash)
+ if err != nil {
+ return nil, err
+ }
+ salt := make([]byte, saltLength)
+ _, err = io.ReadFull(randReader, salt)
+ if err != nil {
+ return nil, err
+ }
+ return salt, nil
+}
+
+// removeNotationsWithName removes all notations in this signature with the given name.
+func (sig *Signature) removeNotationsWithName(name string) {
+ if sig == nil || sig.Notations == nil {
+ return
+ }
+ updatedNotations := make([]*Notation, 0, len(sig.Notations))
+ for _, notation := range sig.Notations {
+ if notation.Name != name {
+ updatedNotations = append(updatedNotations, notation)
+ }
+ }
+ sig.Notations = updatedNotations
+}
diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetric_key_encrypted.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetric_key_encrypted.go
index bac2b132ea..2812a1db88 100644
--- a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetric_key_encrypted.go
+++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetric_key_encrypted.go
@@ -7,11 +7,13 @@ package packet
import (
"bytes"
"crypto/cipher"
+ "crypto/sha256"
"io"
"strconv"
"github.com/ProtonMail/go-crypto/openpgp/errors"
"github.com/ProtonMail/go-crypto/openpgp/s2k"
+ "golang.org/x/crypto/hkdf"
)
// This is the largest session key that we'll support. Since at most 256-bit cipher
@@ -39,10 +41,21 @@ func (ske *SymmetricKeyEncrypted) parse(r io.Reader) error {
return err
}
ske.Version = int(buf[0])
- if ske.Version != 4 && ske.Version != 5 {
+ if ske.Version != 4 && ske.Version != 5 && ske.Version != 6 {
return errors.UnsupportedError("unknown SymmetricKeyEncrypted version")
}
+ if V5Disabled && ske.Version == 5 {
+ return errors.UnsupportedError("support for parsing v5 entities is disabled; build with `-tags v5` if needed")
+ }
+
+ if ske.Version > 5 {
+ // Scalar octet count
+ if _, err := readFull(r, buf[:]); err != nil {
+ return err
+ }
+ }
+
// Cipher function
if _, err := readFull(r, buf[:]); err != nil {
return err
@@ -52,7 +65,7 @@ func (ske *SymmetricKeyEncrypted) parse(r io.Reader) error {
return errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(buf[0])))
}
- if ske.Version == 5 {
+ if ske.Version >= 5 {
// AEAD mode
if _, err := readFull(r, buf[:]); err != nil {
return errors.StructuralError("cannot read AEAD octet from packet")
@@ -60,6 +73,13 @@ func (ske *SymmetricKeyEncrypted) parse(r io.Reader) error {
ske.Mode = AEADMode(buf[0])
}
+ if ske.Version > 5 {
+ // Scalar octet count
+ if _, err := readFull(r, buf[:]); err != nil {
+ return err
+ }
+ }
+
var err error
if ske.s2k, err = s2k.Parse(r); err != nil {
if _, ok := err.(errors.ErrDummyPrivateKey); ok {
@@ -68,7 +88,7 @@ func (ske *SymmetricKeyEncrypted) parse(r io.Reader) error {
return err
}
- if ske.Version == 5 {
+ if ske.Version >= 5 {
// AEAD IV
iv := make([]byte, ske.Mode.IvLength())
_, err := readFull(r, iv)
@@ -109,8 +129,8 @@ func (ske *SymmetricKeyEncrypted) Decrypt(passphrase []byte) ([]byte, CipherFunc
case 4:
plaintextKey, cipherFunc, err := ske.decryptV4(key)
return plaintextKey, cipherFunc, err
- case 5:
- plaintextKey, err := ske.decryptV5(key)
+ case 5, 6:
+ plaintextKey, err := ske.aeadDecrypt(ske.Version, key)
return plaintextKey, CipherFunction(0), err
}
err := errors.UnsupportedError("unknown SymmetricKeyEncrypted version")
@@ -136,9 +156,9 @@ func (ske *SymmetricKeyEncrypted) decryptV4(key []byte) ([]byte, CipherFunction,
return plaintextKey, cipherFunc, nil
}
-func (ske *SymmetricKeyEncrypted) decryptV5(key []byte) ([]byte, error) {
- adata := []byte{0xc3, byte(5), byte(ske.CipherFunc), byte(ske.Mode)}
- aead := getEncryptedKeyAeadInstance(ske.CipherFunc, ske.Mode, key, adata)
+func (ske *SymmetricKeyEncrypted) aeadDecrypt(version int, key []byte) ([]byte, error) {
+ adata := []byte{0xc3, byte(version), byte(ske.CipherFunc), byte(ske.Mode)}
+ aead := getEncryptedKeyAeadInstance(ske.CipherFunc, ske.Mode, key, adata, version)
plaintextKey, err := aead.Open(nil, ske.iv, ske.encryptedKey, adata)
if err != nil {
@@ -175,10 +195,22 @@ func SerializeSymmetricKeyEncrypted(w io.Writer, passphrase []byte, config *Conf
// the given passphrase. The returned session key must be passed to
// SerializeSymmetricallyEncrypted.
// If config is nil, sensible defaults will be used.
+// Deprecated: Use SerializeSymmetricKeyEncryptedAEADReuseKey instead.
func SerializeSymmetricKeyEncryptedReuseKey(w io.Writer, sessionKey []byte, passphrase []byte, config *Config) (err error) {
+ return SerializeSymmetricKeyEncryptedAEADReuseKey(w, sessionKey, passphrase, config.AEAD() != nil, config)
+}
+
+// SerializeSymmetricKeyEncryptedAEADReuseKey serializes a symmetric key packet to w.
+// The packet contains the given session key, encrypted by a key derived from
+// the given passphrase. The returned session key must be passed to
+// SerializeSymmetricallyEncrypted.
+// If aeadSupported is set, SKESK v6 is used, otherwise v4.
+// Note: aeadSupported MUST match the value passed to SerializeSymmetricallyEncrypted.
+// If config is nil, sensible defaults will be used.
+func SerializeSymmetricKeyEncryptedAEADReuseKey(w io.Writer, sessionKey []byte, passphrase []byte, aeadSupported bool, config *Config) (err error) {
var version int
- if config.AEAD() != nil {
- version = 5
+ if aeadSupported {
+ version = 6
} else {
version = 4
}
@@ -203,11 +235,15 @@ func SerializeSymmetricKeyEncryptedReuseKey(w io.Writer, sessionKey []byte, pass
switch version {
case 4:
packetLength = 2 /* header */ + len(s2kBytes) + 1 /* cipher type */ + keySize
- case 5:
+ case 5, 6:
ivLen := config.AEAD().Mode().IvLength()
tagLen := config.AEAD().Mode().TagLength()
packetLength = 3 + len(s2kBytes) + ivLen + keySize + tagLen
}
+ if version > 5 {
+ packetLength += 2 // additional octet count fields
+ }
+
err = serializeHeader(w, packetTypeSymmetricKeyEncrypted, packetLength)
if err != nil {
return
@@ -216,13 +252,22 @@ func SerializeSymmetricKeyEncryptedReuseKey(w io.Writer, sessionKey []byte, pass
// Symmetric Key Encrypted Version
buf := []byte{byte(version)}
+ if version > 5 {
+ // Scalar octet count
+ buf = append(buf, byte(3+len(s2kBytes)+config.AEAD().Mode().IvLength()))
+ }
+
// Cipher function
buf = append(buf, byte(cipherFunc))
- if version == 5 {
+ if version >= 5 {
// AEAD mode
buf = append(buf, byte(config.AEAD().Mode()))
}
+ if version > 5 {
+ // Scalar octet count
+ buf = append(buf, byte(len(s2kBytes)))
+ }
_, err = w.Write(buf)
if err != nil {
return
@@ -243,10 +288,10 @@ func SerializeSymmetricKeyEncryptedReuseKey(w io.Writer, sessionKey []byte, pass
if err != nil {
return
}
- case 5:
+ case 5, 6:
mode := config.AEAD().Mode()
- adata := []byte{0xc3, byte(5), byte(cipherFunc), byte(mode)}
- aead := getEncryptedKeyAeadInstance(cipherFunc, mode, keyEncryptingKey, adata)
+ adata := []byte{0xc3, byte(version), byte(cipherFunc), byte(mode)}
+ aead := getEncryptedKeyAeadInstance(cipherFunc, mode, keyEncryptingKey, adata, version)
// Sample iv using random reader
iv := make([]byte, config.AEAD().Mode().IvLength())
@@ -270,7 +315,17 @@ func SerializeSymmetricKeyEncryptedReuseKey(w io.Writer, sessionKey []byte, pass
return
}
-func getEncryptedKeyAeadInstance(c CipherFunction, mode AEADMode, inputKey, associatedData []byte) (aead cipher.AEAD) {
- blockCipher := c.new(inputKey)
+func getEncryptedKeyAeadInstance(c CipherFunction, mode AEADMode, inputKey, associatedData []byte, version int) (aead cipher.AEAD) {
+ var blockCipher cipher.Block
+ if version > 5 {
+ hkdfReader := hkdf.New(sha256.New, inputKey, []byte{}, associatedData)
+
+ encryptionKey := make([]byte, c.KeySize())
+ _, _ = readFull(hkdfReader, encryptionKey)
+
+ blockCipher = c.new(encryptionKey)
+ } else {
+ blockCipher = c.new(inputKey)
+ }
return mode.new(blockCipher)
}
diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted.go
index e9bbf0327e..0e898742cf 100644
--- a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted.go
+++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted.go
@@ -74,6 +74,10 @@ func (se *SymmetricallyEncrypted) Decrypt(c CipherFunction, key []byte) (io.Read
// SerializeSymmetricallyEncrypted serializes a symmetrically encrypted packet
// to w and returns a WriteCloser to which the to-be-encrypted packets can be
// written.
+// If aeadSupported is set to true, SEIPDv2 is used with the indicated CipherSuite.
+// Otherwise, SEIPDv1 is used with the indicated CipherFunction.
+// Note: aeadSupported MUST match the value passed to SerializeEncryptedKeyAEAD
+// and/or SerializeSymmetricKeyEncryptedAEADReuseKey.
// If config is nil, sensible defaults will be used.
func SerializeSymmetricallyEncrypted(w io.Writer, c CipherFunction, aeadSupported bool, cipherSuite CipherSuite, key []byte, config *Config) (Contents io.WriteCloser, err error) {
writeCloser := noOpCloser{w}
diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted_aead.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted_aead.go
index e96252c196..3957b2d53e 100644
--- a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted_aead.go
+++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted_aead.go
@@ -7,7 +7,9 @@ package packet
import (
"crypto/cipher"
"crypto/sha256"
+ "fmt"
"io"
+ "strconv"
"github.com/ProtonMail/go-crypto/openpgp/errors"
"golang.org/x/crypto/hkdf"
@@ -25,19 +27,19 @@ func (se *SymmetricallyEncrypted) parseAead(r io.Reader) error {
se.Cipher = CipherFunction(headerData[0])
// cipherFunc must have block size 16 to use AEAD
if se.Cipher.blockSize() != 16 {
- return errors.UnsupportedError("invalid aead cipher: " + string(se.Cipher))
+ return errors.UnsupportedError("invalid aead cipher: " + strconv.Itoa(int(se.Cipher)))
}
// Mode
se.Mode = AEADMode(headerData[1])
if se.Mode.TagLength() == 0 {
- return errors.UnsupportedError("unknown aead mode: " + string(se.Mode))
+ return errors.UnsupportedError("unknown aead mode: " + strconv.Itoa(int(se.Mode)))
}
// Chunk size
se.ChunkSizeByte = headerData[2]
if se.ChunkSizeByte > 16 {
- return errors.UnsupportedError("invalid aead chunk size byte: " + string(se.ChunkSizeByte))
+ return errors.UnsupportedError("invalid aead chunk size byte: " + strconv.Itoa(int(se.ChunkSizeByte)))
}
// Salt
@@ -62,8 +64,11 @@ func (se *SymmetricallyEncrypted) associatedData() []byte {
// decryptAead decrypts a V2 SEIPD packet (AEAD) as specified in
// https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-07.html#section-5.13.2
func (se *SymmetricallyEncrypted) decryptAead(inputKey []byte) (io.ReadCloser, error) {
- aead, nonce := getSymmetricallyEncryptedAeadInstance(se.Cipher, se.Mode, inputKey, se.Salt[:], se.associatedData())
+ if se.Cipher.KeySize() != len(inputKey) {
+ return nil, errors.StructuralError(fmt.Sprintf("invalid session key length for cipher: got %d bytes, but expected %d bytes", len(inputKey), se.Cipher.KeySize()))
+ }
+ aead, nonce := getSymmetricallyEncryptedAeadInstance(se.Cipher, se.Mode, inputKey, se.Salt[:], se.associatedData())
// Carry the first tagLen bytes
tagLen := se.Mode.TagLength()
peekedBytes := make([]byte, tagLen)
@@ -115,7 +120,7 @@ func serializeSymmetricallyEncryptedAead(ciphertext io.WriteCloser, cipherSuite
// Random salt
salt := make([]byte, aeadSaltSize)
- if _, err := rand.Read(salt); err != nil {
+ if _, err := io.ReadFull(rand, salt); err != nil {
return nil, err
}
diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted_mdc.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted_mdc.go
index fa26bebe38..8b18623684 100644
--- a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted_mdc.go
+++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted_mdc.go
@@ -148,7 +148,7 @@ const mdcPacketTagByte = byte(0x80) | 0x40 | 19
func (ser *seMDCReader) Close() error {
if ser.error {
- return errors.ErrMDCMissing
+ return errors.ErrMDCHashMismatch
}
for !ser.eof {
@@ -159,7 +159,7 @@ func (ser *seMDCReader) Close() error {
break
}
if err != nil {
- return errors.ErrMDCMissing
+ return errors.ErrMDCHashMismatch
}
}
@@ -172,7 +172,7 @@ func (ser *seMDCReader) Close() error {
// 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 errors.ErrMDCHashMismatch
}
return nil
}
@@ -237,9 +237,9 @@ func serializeSymmetricallyEncryptedMdc(ciphertext io.WriteCloser, c CipherFunct
block := c.new(key)
blockSize := block.BlockSize()
iv := make([]byte, blockSize)
- _, err = config.Random().Read(iv)
+ _, err = io.ReadFull(config.Random(), iv)
if err != nil {
- return
+ return nil, err
}
s, prefix := NewOCFBEncrypter(block, iv, OCFBNoResync)
_, err = ciphertext.Write(prefix)
diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/userattribute.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/userattribute.go
index 88ec72c6c4..63814ed132 100644
--- a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/userattribute.go
+++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/userattribute.go
@@ -9,7 +9,6 @@ import (
"image"
"image/jpeg"
"io"
- "io/ioutil"
)
const UserAttrImageSubpacket = 1
@@ -63,7 +62,7 @@ func NewUserAttribute(contents ...*OpaqueSubpacket) *UserAttribute {
func (uat *UserAttribute) parse(r io.Reader) (err error) {
// RFC 4880, section 5.13
- b, err := ioutil.ReadAll(r)
+ b, err := io.ReadAll(r)
if err != nil {
return
}
diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/userid.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/userid.go
index 614fbafd5e..3c7451a3c3 100644
--- a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/userid.go
+++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/userid.go
@@ -6,7 +6,6 @@ package packet
import (
"io"
- "io/ioutil"
"strings"
)
@@ -66,7 +65,7 @@ func NewUserId(name, comment, email string) *UserId {
func (uid *UserId) parse(r io.Reader) (err error) {
// RFC 4880, section 5.11
- b, err := ioutil.ReadAll(r)
+ b, err := io.ReadAll(r)
if err != nil {
return
}
diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/read.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/read.go
index 8499c73790..e6dd9b5fd3 100644
--- a/vendor/github.com/ProtonMail/go-crypto/openpgp/read.go
+++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/read.go
@@ -46,6 +46,7 @@ type MessageDetails struct {
DecryptedWith Key // the private key used to decrypt the message, if any.
IsSigned bool // true if the message is signed.
SignedByKeyId uint64 // the key id of the signer, if any.
+ SignedByFingerprint []byte // the key fingerprint of the signer, if any.
SignedBy *Key // the key of the signer, if available.
LiteralData *packet.LiteralData // the metadata of the contents
UnverifiedBody io.Reader // the contents of the message.
@@ -117,7 +118,7 @@ ParsePackets:
// This packet contains the decryption key encrypted to a public key.
md.EncryptedToKeyIds = append(md.EncryptedToKeyIds, p.KeyId)
switch p.Algo {
- case packet.PubKeyAlgoRSA, packet.PubKeyAlgoRSAEncryptOnly, packet.PubKeyAlgoElGamal, packet.PubKeyAlgoECDH:
+ case packet.PubKeyAlgoRSA, packet.PubKeyAlgoRSAEncryptOnly, packet.PubKeyAlgoElGamal, packet.PubKeyAlgoECDH, packet.PubKeyAlgoX25519, packet.PubKeyAlgoX448:
break
default:
continue
@@ -232,7 +233,7 @@ FindKey:
}
mdFinal, sensitiveParsingErr := readSignedMessage(packets, md, keyring, config)
if sensitiveParsingErr != nil {
- return nil, errors.StructuralError("parsing error")
+ return nil, errors.HandleSensitiveParsingError(sensitiveParsingErr, md.decrypted != nil)
}
return mdFinal, nil
}
@@ -270,13 +271,17 @@ FindLiteralData:
prevLast = true
}
- h, wrappedHash, err = hashForSignature(p.Hash, p.SigType)
+ h, wrappedHash, err = hashForSignature(p.Hash, p.SigType, p.Salt)
if err != nil {
md.SignatureError = err
}
md.IsSigned = true
+ if p.Version == 6 {
+ md.SignedByFingerprint = p.KeyFingerprint
+ }
md.SignedByKeyId = p.KeyId
+
if keyring != nil {
keys := keyring.KeysByIdUsage(p.KeyId, packet.KeyFlagSign)
if len(keys) > 0 {
@@ -292,7 +297,7 @@ FindLiteralData:
if md.IsSigned && md.SignatureError == nil {
md.UnverifiedBody = &signatureCheckReader{packets, h, wrappedHash, md, config}
} else if md.decrypted != nil {
- md.UnverifiedBody = checkReader{md}
+ md.UnverifiedBody = &checkReader{md, false}
} else {
md.UnverifiedBody = md.LiteralData.Body
}
@@ -300,12 +305,22 @@ FindLiteralData:
return md, nil
}
+func wrapHashForSignature(hashFunc hash.Hash, sigType packet.SignatureType) (hash.Hash, error) {
+ switch sigType {
+ case packet.SigTypeBinary:
+ return hashFunc, nil
+ case packet.SigTypeText:
+ return NewCanonicalTextHash(hashFunc), nil
+ }
+ return nil, errors.UnsupportedError("unsupported signature type: " + strconv.Itoa(int(sigType)))
+}
+
// hashForSignature returns a pair of hashes that can be used to verify a
// signature. The signature may specify that the contents of the signed message
// should be preprocessed (i.e. to normalize line endings). Thus this function
// returns two hashes. The second should be used to hash the message itself and
// performs any needed preprocessing.
-func hashForSignature(hashFunc crypto.Hash, sigType packet.SignatureType) (hash.Hash, hash.Hash, error) {
+func hashForSignature(hashFunc crypto.Hash, sigType packet.SignatureType, sigSalt []byte) (hash.Hash, hash.Hash, error) {
if _, ok := algorithm.HashToHashIdWithSha1(hashFunc); !ok {
return nil, nil, errors.UnsupportedError("unsupported hash function")
}
@@ -313,14 +328,19 @@ func hashForSignature(hashFunc crypto.Hash, sigType packet.SignatureType) (hash.
return nil, nil, errors.UnsupportedError("hash not available: " + strconv.Itoa(int(hashFunc)))
}
h := hashFunc.New()
-
+ if sigSalt != nil {
+ h.Write(sigSalt)
+ }
+ wrappedHash, err := wrapHashForSignature(h, sigType)
+ if err != nil {
+ return nil, nil, err
+ }
switch sigType {
case packet.SigTypeBinary:
- return h, h, nil
+ return h, wrappedHash, nil
case packet.SigTypeText:
- return h, NewCanonicalTextHash(h), nil
+ return h, wrappedHash, nil
}
-
return nil, nil, errors.UnsupportedError("unsupported signature type: " + strconv.Itoa(int(sigType)))
}
@@ -328,21 +348,27 @@ func hashForSignature(hashFunc crypto.Hash, sigType packet.SignatureType) (hash.
// it closes the ReadCloser from any SymmetricallyEncrypted packet to trigger
// MDC checks.
type checkReader struct {
- md *MessageDetails
+ md *MessageDetails
+ checked bool
}
-func (cr checkReader) Read(buf []byte) (int, error) {
+func (cr *checkReader) Read(buf []byte) (int, error) {
n, sensitiveParsingError := cr.md.LiteralData.Body.Read(buf)
if sensitiveParsingError == io.EOF {
+ if cr.checked {
+ // Only check once
+ return n, io.EOF
+ }
mdcErr := cr.md.decrypted.Close()
if mdcErr != nil {
return n, mdcErr
}
+ cr.checked = true
return n, io.EOF
}
if sensitiveParsingError != nil {
- return n, errors.StructuralError("parsing error")
+ return n, errors.HandleSensitiveParsingError(sensitiveParsingError, true)
}
return n, nil
@@ -366,6 +392,7 @@ func (scr *signatureCheckReader) Read(buf []byte) (int, error) {
scr.wrappedHash.Write(buf[:n])
}
+ readsDecryptedData := scr.md.decrypted != nil
if sensitiveParsingError == io.EOF {
var p packet.Packet
var readError error
@@ -384,7 +411,7 @@ func (scr *signatureCheckReader) Read(buf []byte) (int, error) {
key := scr.md.SignedBy
signatureError := key.PublicKey.VerifySignature(scr.h, sig)
if signatureError == nil {
- signatureError = checkSignatureDetails(key, sig, scr.config)
+ signatureError = checkMessageSignatureDetails(key, sig, scr.config)
}
scr.md.Signature = sig
scr.md.SignatureError = signatureError
@@ -408,16 +435,15 @@ func (scr *signatureCheckReader) Read(buf []byte) (int, error) {
// unsigned hash of its own. In order to check this we need to
// close that Reader.
if scr.md.decrypted != nil {
- mdcErr := scr.md.decrypted.Close()
- if mdcErr != nil {
- return n, mdcErr
+ if sensitiveParsingError := scr.md.decrypted.Close(); sensitiveParsingError != nil {
+ return n, errors.HandleSensitiveParsingError(sensitiveParsingError, true)
}
}
return n, io.EOF
}
if sensitiveParsingError != nil {
- return n, errors.StructuralError("parsing error")
+ return n, errors.HandleSensitiveParsingError(sensitiveParsingError, readsDecryptedData)
}
return n, nil
@@ -428,14 +454,13 @@ func (scr *signatureCheckReader) Read(buf []byte) (int, error) {
// if any, and a possible signature verification error.
// If the signer isn't known, ErrUnknownIssuer is returned.
func VerifyDetachedSignature(keyring KeyRing, signed, signature io.Reader, config *packet.Config) (sig *packet.Signature, signer *Entity, err error) {
- var expectedHashes []crypto.Hash
- return verifyDetachedSignature(keyring, signed, signature, expectedHashes, config)
+ return verifyDetachedSignature(keyring, signed, signature, nil, false, config)
}
// VerifyDetachedSignatureAndHash performs the same actions as
// VerifyDetachedSignature and checks that the expected hash functions were used.
func VerifyDetachedSignatureAndHash(keyring KeyRing, signed, signature io.Reader, expectedHashes []crypto.Hash, config *packet.Config) (sig *packet.Signature, signer *Entity, err error) {
- return verifyDetachedSignature(keyring, signed, signature, expectedHashes, config)
+ return verifyDetachedSignature(keyring, signed, signature, expectedHashes, true, config)
}
// CheckDetachedSignature takes a signed file and a detached signature and
@@ -443,25 +468,24 @@ func VerifyDetachedSignatureAndHash(keyring KeyRing, signed, signature io.Reader
// signature verification error. If the signer isn't known,
// ErrUnknownIssuer is returned.
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)
+ _, signer, err = verifyDetachedSignature(keyring, signed, signature, nil, false, config)
+ return
}
// 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) {
- _, signer, err = verifyDetachedSignature(keyring, signed, signature, expectedHashes, config)
+ _, signer, err = verifyDetachedSignature(keyring, signed, signature, expectedHashes, true, config)
return
}
-func verifyDetachedSignature(keyring KeyRing, signed, signature io.Reader, expectedHashes []crypto.Hash, config *packet.Config) (sig *packet.Signature, signer *Entity, err error) {
+func verifyDetachedSignature(keyring KeyRing, signed, signature io.Reader, expectedHashes []crypto.Hash, checkHashes bool, config *packet.Config) (sig *packet.Signature, signer *Entity, err error) {
var issuerKeyId uint64
var hashFunc crypto.Hash
var sigType packet.SignatureType
var keys []Key
var p packet.Packet
- expectedHashesLen := len(expectedHashes)
packets := packet.NewReader(signature)
for {
p, err = packets.Next()
@@ -483,16 +507,19 @@ func verifyDetachedSignature(keyring KeyRing, signed, signature io.Reader, expec
issuerKeyId = *sig.IssuerKeyId
hashFunc = sig.Hash
sigType = sig.SigType
-
- for i, expectedHash := range expectedHashes {
- if hashFunc == expectedHash {
- break
+ if checkHashes {
+ matchFound := false
+ // check for hashes
+ for _, expectedHash := range expectedHashes {
+ if hashFunc == expectedHash {
+ matchFound = true
+ break
+ }
}
- if i+1 == expectedHashesLen {
- return nil, nil, errors.StructuralError("hash algorithm mismatch with cleartext message headers")
+ if !matchFound {
+ return nil, nil, errors.StructuralError("hash algorithm or salt mismatch with cleartext message headers")
}
}
-
keys = keyring.KeysByIdUsage(issuerKeyId, packet.KeyFlagSign)
if len(keys) > 0 {
break
@@ -503,7 +530,11 @@ func verifyDetachedSignature(keyring KeyRing, signed, signature io.Reader, expec
panic("unreachable")
}
- h, wrappedHash, err := hashForSignature(hashFunc, sigType)
+ h, err := sig.PrepareVerify()
+ if err != nil {
+ return nil, nil, err
+ }
+ wrappedHash, err := wrapHashForSignature(h, sigType)
if err != nil {
return nil, nil, err
}
@@ -515,7 +546,7 @@ func verifyDetachedSignature(keyring KeyRing, signed, signature io.Reader, expec
for _, key := range keys {
err = key.PublicKey.VerifySignature(h, sig)
if err == nil {
- return sig, key.Entity, checkSignatureDetails(&key, sig, config)
+ return sig, key.Entity, checkMessageSignatureDetails(&key, sig, config)
}
}
@@ -533,7 +564,7 @@ func CheckArmoredDetachedSignature(keyring KeyRing, signed, signature io.Reader,
return CheckDetachedSignature(keyring, signed, body, config)
}
-// checkSignatureDetails returns an error if:
+// checkMessageSignatureDetails returns an error if:
// - The signature (or one of the binding signatures mentioned below)
// has a unknown critical notation data subpacket
// - The primary key of the signing entity is revoked
@@ -551,15 +582,11 @@ func CheckArmoredDetachedSignature(keyring KeyRing, signed, signature io.Reader,
// NOTE: The order of these checks is important, as the caller may choose to
// ignore ErrSignatureExpired or ErrKeyExpired errors, but should never
// ignore any other errors.
-//
-// TODO: Also return an error if:
-// - The primary key is expired according to a direct-key signature
-// - (For V5 keys only:) The direct-key signature (exists and) is expired
-func checkSignatureDetails(key *Key, signature *packet.Signature, config *packet.Config) error {
+func checkMessageSignatureDetails(key *Key, signature *packet.Signature, config *packet.Config) error {
now := config.Now()
- primaryIdentity := key.Entity.PrimaryIdentity()
+ primarySelfSignature, primaryIdentity := key.Entity.PrimarySelfSignature()
signedBySubKey := key.PublicKey != key.Entity.PrimaryKey
- sigsToCheck := []*packet.Signature{signature, primaryIdentity.SelfSignature}
+ sigsToCheck := []*packet.Signature{signature, primarySelfSignature}
if signedBySubKey {
sigsToCheck = append(sigsToCheck, key.SelfSignature, key.SelfSignature.EmbeddedSignature)
}
@@ -572,10 +599,10 @@ func checkSignatureDetails(key *Key, signature *packet.Signature, config *packet
}
if key.Entity.Revoked(now) || // primary key is revoked
(signedBySubKey && key.Revoked(now)) || // subkey is revoked
- primaryIdentity.Revoked(now) { // primary identity is revoked
+ (primaryIdentity != nil && primaryIdentity.Revoked(now)) { // primary identity is revoked for v4
return errors.ErrKeyRevoked
}
- if key.Entity.PrimaryKey.KeyExpired(primaryIdentity.SelfSignature, now) { // primary key is expired
+ if key.Entity.PrimaryKey.KeyExpired(primarySelfSignature, now) { // primary key is expired
return errors.ErrKeyExpired
}
if signedBySubKey {
diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/read_write_test_data.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/read_write_test_data.go
index db6dad5c0b..670d60226a 100644
--- a/vendor/github.com/ProtonMail/go-crypto/openpgp/read_write_test_data.go
+++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/read_write_test_data.go
@@ -26,6 +26,8 @@ const testKeys1And2PrivateHex = "9501d8044d3c5c10010400b1d13382944bd5aba23a43129
const dsaElGamalTestKeysHex = "9501e1044dfcb16a110400aa3e5c1a1f43dd28c2ffae8abf5cfce555ee874134d8ba0a0f7b868ce2214beddc74e5e1e21ded354a95d18acdaf69e5e342371a71fbb9093162e0c5f3427de413a7f2c157d83f5cd2f9d791256dc4f6f0e13f13c3302af27f2384075ab3021dff7a050e14854bbde0a1094174855fc02f0bae8e00a340d94a1f22b32e48485700a0cec672ac21258fb95f61de2ce1af74b2c4fa3e6703ff698edc9be22c02ae4d916e4fa223f819d46582c0516235848a77b577ea49018dcd5e9e15cff9dbb4663a1ae6dd7580fa40946d40c05f72814b0f88481207e6c0832c3bded4853ebba0a7e3bd8e8c66df33d5a537cd4acf946d1080e7a3dcea679cb2b11a72a33a2b6a9dc85f466ad2ddf4c3db6283fa645343286971e3dd700703fc0c4e290d45767f370831a90187e74e9972aae5bff488eeff7d620af0362bfb95c1a6c3413ab5d15a2e4139e5d07a54d72583914661ed6a87cce810be28a0aa8879a2dd39e52fb6fe800f4f181ac7e328f740cde3d09a05cecf9483e4cca4253e60d4429ffd679d9996a520012aad119878c941e3cf151459873bdfc2a9563472fe0303027a728f9feb3b864260a1babe83925ce794710cfd642ee4ae0e5b9d74cee49e9c67b6cd0ea5dfbb582132195a121356a1513e1bca73e5b80c58c7ccb4164453412f456c47616d616c2054657374204b65792031886204131102002205024dfcb16a021b03060b090807030206150802090a0b0416020301021e01021780000a091033af447ccd759b09fadd00a0b8fd6f5a790bad7e9f2dbb7632046dc4493588db009c087c6a9ba9f7f49fab221587a74788c00db4889ab00200009d0157044dfcb16a1004008dec3f9291205255ccff8c532318133a6840739dd68b03ba942676f9038612071447bf07d00d559c5c0875724ea16a4c774f80d8338b55fca691a0522e530e604215b467bbc9ccfd483a1da99d7bc2648b4318fdbd27766fc8bfad3fddb37c62b8ae7ccfe9577e9b8d1e77c1d417ed2c2ef02d52f4da11600d85d3229607943700030503ff506c94c87c8cab778e963b76cf63770f0a79bf48fb49d3b4e52234620fc9f7657f9f8d56c96a2b7c7826ae6b57ebb2221a3fe154b03b6637cea7e6d98e3e45d87cf8dc432f723d3d71f89c5192ac8d7290684d2c25ce55846a80c9a7823f6acd9bb29fa6cd71f20bc90eccfca20451d0c976e460e672b000df49466408d527affe0303027a728f9feb3b864260abd761730327bca2aaa4ea0525c175e92bf240682a0e83b226f97ecb2e935b62c9a133858ce31b271fa8eb41f6a1b3cd72a63025ce1a75ee4180dcc284884904181102000905024dfcb16a021b0c000a091033af447ccd759b09dd0b009e3c3e7296092c81bee5a19929462caaf2fff3ae26009e218c437a2340e7ea628149af1ec98ec091a43992b00200009501e1044dfcb1be1104009f61faa61aa43df75d128cbe53de528c4aec49ce9360c992e70c77072ad5623de0a3a6212771b66b39a30dad6781799e92608316900518ec01184a85d872365b7d2ba4bacfb5882ea3c2473d3750dc6178cc1cf82147fb58caa28b28e9f12f6d1efcb0534abed644156c91cca4ab78834268495160b2400bc422beb37d237c2300a0cac94911b6d493bda1e1fbc6feeca7cb7421d34b03fe22cec6ccb39675bb7b94a335c2b7be888fd3906a1125f33301d8aa6ec6ee6878f46f73961c8d57a3e9544d8ef2a2cbfd4d52da665b1266928cfe4cb347a58c412815f3b2d2369dec04b41ac9a71cc9547426d5ab941cccf3b18575637ccfb42df1a802df3cfe0a999f9e7109331170e3a221991bf868543960f8c816c28097e503fe319db10fb98049f3a57d7c80c420da66d56f3644371631fad3f0ff4040a19a4fedc2d07727a1b27576f75a4d28c47d8246f27071e12d7a8de62aad216ddbae6aa02efd6b8a3e2818cda48526549791ab277e447b3a36c57cefe9b592f5eab73959743fcc8e83cbefec03a329b55018b53eec196765ae40ef9e20521a603c551efe0303020950d53a146bf9c66034d00c23130cce95576a2ff78016ca471276e8227fb30b1ffbd92e61804fb0c3eff9e30b1a826ee8f3e4730b4d86273ca977b4164453412f456c47616d616c2054657374204b65792032886204131102002205024dfcb1be021b03060b090807030206150802090a0b0416020301021e01021780000a0910a86bf526325b21b22bd9009e34511620415c974750a20df5cb56b182f3b48e6600a0a9466cb1a1305a84953445f77d461593f1d42bc1b00200009d0157044dfcb1be1004009565a951da1ee87119d600c077198f1c1bceb0f7aa54552489298e41ff788fa8f0d43a69871f0f6f77ebdfb14a4260cf9fbeb65d5844b4272a1904dd95136d06c3da745dc46327dd44a0f16f60135914368c8039a34033862261806bb2c5ce1152e2840254697872c85441ccb7321431d75a747a4bfb1d2c66362b51ce76311700030503fc0ea76601c196768070b7365a200e6ddb09307f262d5f39eec467b5f5784e22abdf1aa49226f59ab37cb49969d8f5230ea65caf56015abda62604544ed526c5c522bf92bed178a078789f6c807b6d34885688024a5bed9e9f8c58d11d4b82487b44c5f470c5606806a0443b79cadb45e0f897a561a53f724e5349b9267c75ca17fe0303020950d53a146bf9c660bc5f4ce8f072465e2d2466434320c1e712272fafc20e342fe7608101580fa1a1a367e60486a7cd1246b7ef5586cf5e10b32762b710a30144f12dd17dd4884904181102000905024dfcb1be021b0c000a0910a86bf526325b21b2904c00a0b2b66b4b39ccffda1d10f3ea8d58f827e30a8b8e009f4255b2d8112a184e40cde43a34e8655ca7809370b0020000"
+const ed25519wX25519Key = "c54b0663877fe31b00000020f94da7bb48d60a61e567706a6587d0331999bb9d891a08242ead84543df895a3001972817b12be707e8d5f586ce61361201d344eb266a2c82fde6835762b65b0b7c2b1061f1b0a00000042058263877fe3030b090705150a0e080c021600029b03021e09222106cb186c4f0609a697e4d52dfa6c722b0c1f1e27c18a56708f6525ec27bad9acc905270902070200000000ad2820103e2d7d227ec0e6d7ce4471db36bfc97083253690271498a7ef0576c07faae14585b3b903b0127ec4fda2f023045a2ec76bcb4f9571a9651e14aee1137a1d668442c88f951e33c4ffd33fb9a17d511eed758fc6d9cc50cb5fd793b2039d5804c74b0663877fe319000000208693248367f9e5015db922f8f48095dda784987f2d5985b12fbad16caf5e4435004d600a4f794d44775c57a26e0feefed558e9afffd6ad0d582d57fb2ba2dcedb8c29b06181b0a0000002c050263877fe322a106cb186c4f0609a697e4d52dfa6c722b0c1f1e27c18a56708f6525ec27bad9acc9021b0c00000000defa20a6e9186d9d5935fc8fe56314cdb527486a5a5120f9b762a235a729f039010a56b89c658568341fbef3b894e9834ad9bc72afae2f4c9c47a43855e65f1cb0a3f77bbc5f61085c1f8249fe4e7ca59af5f0bcee9398e0fa8d76e522e1d8ab42bb0d"
+
const signedMessageHex = "a3019bc0cbccc0c4b8d8b74ee2108fe16ec6d3ca490cbe362d3f8333d3f352531472538b8b13d353b97232f352158c20943157c71c16064626063656269052062e4e01987e9b6fccff4b7df3a34c534b23e679cbec3bc0f8f6e64dfb4b55fe3f8efa9ce110ddb5cd79faf1d753c51aecfa669f7e7aa043436596cccc3359cb7dd6bbe9ecaa69e5989d9e57209571edc0b2fa7f57b9b79a64ee6e99ce1371395fee92fec2796f7b15a77c386ff668ee27f6d38f0baa6c438b561657377bf6acff3c5947befd7bf4c196252f1d6e5c524d0300"
const signedTextMessageHex = "a3019bc0cbccc8c4b8d8b74ee2108fe16ec6d36a250cbece0c178233d3f352531472538b8b13d35379b97232f352158ca0b4312f57c71c1646462606365626906a062e4e019811591798ff99bf8afee860b0d8a8c2a85c3387e3bcf0bb3b17987f2bbcfab2aa526d930cbfd3d98757184df3995c9f3e7790e36e3e9779f06089d4c64e9e47dd6202cb6e9bc73c5d11bb59fbaf89d22d8dc7cf199ddf17af96e77c5f65f9bbed56f427bd8db7af37f6c9984bf9385efaf5f184f986fb3e6adb0ecfe35bbf92d16a7aa2a344fb0bc52fb7624f0200"
@@ -160,18 +162,78 @@ 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
+// See OpenPGP crypto refresh Section A.3.
+const v6PrivKey = `-----BEGIN PGP PRIVATE KEY BLOCK-----
+
+xUsGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laMAGXKB
+exK+cH6NX1hs5hNhIB00TrJmosgv3mg1ditlsLfCsQYfGwoAAABCBYJjh3/jAwsJ
+BwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6
+2azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lwgyU2kCcUmKfvBXbAf6rh
+RYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaEQsiPlR4zxP/TP7mhfVEe
+7XWPxtnMUMtf15OyA51YBMdLBmOHf+MZAAAAIIaTJINn+eUBXbki+PSAld2nhJh/
+LVmFsS+60WyvXkQ1AE1gCk95TUR3XFeibg/u/tVY6a//1q0NWC1X+yui3O24wpsG
+GBsKAAAALAWCY4d/4wKbDCIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6
+2azJAAAAAAQBIKbpGG2dWTX8j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDE
+M0g12vYxoWM8Y81W+bHBw805I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUr
+k0mXubZvyl4GBg==
+-----END PGP PRIVATE KEY BLOCK-----`
+
+// See OpenPGP crypto refresh merge request:
+// https://gitlab.com/openpgp-wg/rfc4880bis/-/merge_requests/304
+const v6PrivKeyMsg = `-----BEGIN PGP MESSAGE-----
+
+wV0GIQYSyD8ecG9jCP4VGkF3Q6HwM3kOk+mXhIjR2zeNqZMIhRmHzxjV8bU/gXzO
+WgBM85PMiVi93AZfJfhK9QmxfdNnZBjeo1VDeVZheQHgaVf7yopqR6W1FT6NOrfS
+aQIHAgZhZBZTW+CwcW1g4FKlbExAf56zaw76/prQoN+bAzxpohup69LA7JW/Vp0l
+yZnuSj3hcFj0DfqLTGgr4/u717J+sPWbtQBfgMfG9AOIwwrUBqsFE9zW+f1zdlYo
+bhF30A+IitsxxA==
+-----END PGP MESSAGE-----`
+
+// See OpenPGP crypto refresh merge request:
+// https://gitlab.com/openpgp-wg/rfc4880bis/-/merge_requests/305
+const v6PrivKeyInlineSignMsg = `-----BEGIN PGP MESSAGE-----
-xA0DAQoWGTR7yYckZAIByxF1B21zZy50eHRfbIGSdGVzdMJ3BQEWCgAGBQJf
-bIGSACMiIQUZNHvJhyRkAl+Z3z7C4AAO2YhIkuH3s+pMlACRWVabVDQvAP9G
-y29VPonFXqi2zKkpZrvyvZxg+n5e8Nt9wNbuxeCd3QD/TtO2s+JvjrE4Siwv
-UQdl5MlBka1QSNbMq2Bz7XwNPg4=
-=6lbM
+wV0GIQYSyD8ecG9jCP4VGkF3Q6HwM3kOk+mXhIjR2zeNqZMIhRmHzxjV8bU/gXzO
+WgBM85PMiVi93AZfJfhK9QmxfdNnZBjeo1VDeVZheQHgaVf7yopqR6W1FT6NOrfS
+aQIHAgZhZBZTW+CwcW1g4FKlbExAf56zaw76/prQoN+bAzxpohup69LA7JW/Vp0l
+yZnuSj3hcFj0DfqLTGgr4/u717J+sPWbtQBfgMfG9AOIwwrUBqsFE9zW+f1zdlYo
+bhF30A+IitsxxA==
-----END PGP MESSAGE-----`
+// See https://gitlab.com/openpgp-wg/rfc4880bis/-/merge_requests/274
+// decryption password: "correct horse battery staple"
+const v6ArgonSealedPrivKey = `-----BEGIN PGP PRIVATE KEY BLOCK-----
+
+xYIGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laP9JgkC
+FARdb9ccngltHraRe25uHuyuAQQVtKipJ0+r5jL4dacGWSAheCWPpITYiyfyIOPS
+3gIDyg8f7strd1OB4+LZsUhcIjOMpVHgmiY/IutJkulneoBYwrEGHxsKAAAAQgWC
+Y4d/4wMLCQcFFQoOCAwCFgACmwMCHgkiIQbLGGxPBgmml+TVLfpscisMHx4nwYpW
+cI9lJewnutmsyQUnCQIHAgAAAACtKCAQPi19In7A5tfORHHbNr/JcIMlNpAnFJin
+7wV2wH+q4UWFs7kDsBJ+xP2i8CMEWi7Ha8tPlXGpZR4UruETeh1mhELIj5UeM8T/
+0z+5oX1RHu11j8bZzFDLX9eTsgOdWATHggZjh3/jGQAAACCGkySDZ/nlAV25Ivj0
+gJXdp4SYfy1ZhbEvutFsr15ENf0mCQIUBA5hhGgp2oaavg6mFUXcFMwBBBUuE8qf
+9Ock+xwusd+GAglBr5LVyr/lup3xxQvHXFSjjA2haXfoN6xUGRdDEHI6+uevKjVR
+v5oAxgu7eJpaXNjCmwYYGwoAAAAsBYJjh3/jApsMIiEGyxhsTwYJppfk1S36bHIr
+DB8eJ8GKVnCPZSXsJ7rZrMkAAAAABAEgpukYbZ1ZNfyP5WMUzbUnSGpaUSD5t2Ki
+Nacp8DkBClZRa2c3AMQzSDXa9jGhYzxjzVb5scHDzTkjyRZWRdTq8U6L4da+/+Kt
+ruh8m7Xo2ehSSFyWRSuTSZe5tm/KXgYG
+-----END PGP PRIVATE KEY BLOCK-----`
+
+const v4Key25519 = `-----BEGIN PGP PRIVATE KEY BLOCK-----
+
+xUkEZB3qzRto01j2k2pwN5ux9w70stPinAdXULLr20CRW7U7h2GSeACch0M+
+qzQg8yjFQ8VBvu3uwgKH9senoHmj72lLSCLTmhFKzQR0ZXN0wogEEBsIAD4F
+gmQd6s0ECwkHCAmQIf45+TuC+xMDFQgKBBYAAgECGQECmwMCHgEWIQSWEzMi
+jJUHvyIbVKIh/jn5O4L7EwAAUhaHNlgudvxARdPPETUzVgjuWi+YIz8w1xIb
+lHQMvIrbe2sGCQIethpWofd0x7DHuv/ciHg+EoxJ/Td6h4pWtIoKx0kEZB3q
+zRm4CyA7quliq7yx08AoOqHTuuCgvpkSdEhpp3pEyejQOgBo0p6ywIiLPllY
+0t+jpNspHpAGfXID6oqjpYuJw3AfVRBlwnQEGBsIACoFgmQd6s0JkCH+Ofk7
+gvsTApsMFiEElhMzIoyVB78iG1SiIf45+TuC+xMAAGgQuN9G73446ykvJ/mL
+sCZ7zGFId2gBd1EnG0FTC4npfOKpck0X8dngByrCxU8LDSfvjsEp/xDAiKsQ
+aU71tdtNBQ==
+=e7jT
+-----END PGP PRIVATE KEY BLOCK-----`
+
const keyWithExpiredCrossSig = `-----BEGIN PGP PUBLIC KEY BLOCK-----
xsDNBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv
@@ -272,3 +334,124 @@ AtNTq6ihLMD5v1d82ZC7tNatdlDMGWnIdvEMCv2GZcuIqDQ9rXWs49e7tq1NncLY
hz3tYjKhoFTKEIq3y3Pp
=h/aX
-----END PGP PUBLIC KEY BLOCK-----`
+
+const keyv5Test = `-----BEGIN PGP PRIVATE KEY BLOCK-----
+Comment: Bob's OpenPGP Transferable Secret Key
+
+lQVYBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv
+/seOXpgecTdOcVttfzC8ycIKrt3aQTiwOG/ctaR4Bk/t6ayNFfdUNxHWk4WCKzdz
+/56fW2O0F23qIRd8UUJp5IIlN4RDdRCtdhVQIAuzvp2oVy/LaS2kxQoKvph/5pQ/
+5whqsyroEWDJoSV0yOb25B/iwk/pLUFoyhDG9bj0kIzDxrEqW+7Ba8nocQlecMF3
+X5KMN5kp2zraLv9dlBBpWW43XktjcCZgMy20SouraVma8Je/ECwUWYUiAZxLIlMv
+9CurEOtxUw6N3RdOtLmYZS9uEnn5y1UkF88o8Nku890uk6BrewFzJyLAx5wRZ4F0
+qV/yq36UWQ0JB/AUGhHVPdFf6pl6eaxBwT5GXvbBUibtf8YI2og5RsgTWtXfU7eb
+SGXrl5ZMpbA6mbfhd0R8aPxWfmDWiIOhBufhMCvUHh1sApMKVZnvIff9/0Dca3wb
+vLIwa3T4CyshfT0AEQEAAQAL/RZqbJW2IqQDCnJi4Ozm++gPqBPiX1RhTWSjwxfM
+cJKUZfzLj414rMKm6Jh1cwwGY9jekROhB9WmwaaKT8HtcIgrZNAlYzANGRCM4TLK
+3VskxfSwKKna8l+s+mZglqbAjUg3wmFuf9Tj2xcUZYmyRm1DEmcN2ZzpvRtHgX7z
+Wn1mAKUlSDJZSQks0zjuMNbupcpyJokdlkUg2+wBznBOTKzgMxVNC9b2g5/tMPUs
+hGGWmF1UH+7AHMTaS6dlmr2ZBIyogdnfUqdNg5sZwsxSNrbglKP4sqe7X61uEAIQ
+bD7rT3LonLbhkrj3I8wilUD8usIwt5IecoHhd9HziqZjRCc1BUBkboUEoyedbDV4
+i4qfsFZ6CEWoLuD5pW7dEp0M+WeuHXO164Rc+LnH6i1VQrpb1Okl4qO6ejIpIjBI
+1t3GshtUu/mwGBBxs60KBX5g77mFQ9lLCRj8lSYqOsHRKBhUp4qM869VA+fD0BRP
+fqPT0I9IH4Oa/A3jYJcg622GwQYA1LhnP208Waf6PkQSJ6kyr8ymY1yVh9VBE/g6
+fRDYA+pkqKnw9wfH2Qho3ysAA+OmVOX8Hldg+Pc0Zs0e5pCavb0En8iFLvTA0Q2E
+LR5rLue9uD7aFuKFU/VdcddY9Ww/vo4k5p/tVGp7F8RYCFn9rSjIWbfvvZi1q5Tx
++akoZbga+4qQ4WYzB/obdX6SCmi6BndcQ1QdjCCQU6gpYx0MddVERbIp9+2SXDyL
+hpxjSyz+RGsZi/9UAshT4txP4+MZBgDfK3ZqtW+h2/eMRxkANqOJpxSjMyLO/FXN
+WxzTDYeWtHNYiAlOwlQZEPOydZFty9IVzzNFQCIUCGjQ/nNyhw7adSgUk3+BXEx/
+MyJPYY0BYuhLxLYcrfQ9nrhaVKxRJj25SVHj2ASsiwGJRZW4CC3uw40OYxfKEvNC
+mer/VxM3kg8qqGf9KUzJ1dVdAvjyx2Hz6jY2qWCyRQ6IMjWHyd43C4r3jxooYKUC
+YnstRQyb/gCSKahveSEjo07CiXMr88UGALwzEr3npFAsPW3osGaFLj49y1oRe11E
+he9gCHFm+fuzbXrWmdPjYU5/ZdqdojzDqfu4ThfnipknpVUM1o6MQqkjM896FHm8
+zbKVFSMhEP6DPHSCexMFrrSgN03PdwHTO6iBaIBBFqmGY01tmJ03SxvSpiBPON9P
+NVvy/6UZFedTq8A07OUAxO62YUSNtT5pmK2vzs3SAZJmbFbMh+NN204TRI72GlqT
+t5hcfkuv8hrmwPS/ZR6q312mKQ6w/1pqO9qitCFCb2IgQmFiYmFnZSA8Ym9iQG9w
+ZW5wZ3AuZXhhbXBsZT6JAc4EEwEKADgCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgEC
+F4AWIQTRpm4aI7GCyZgPeIz7/MgqAV5zMAUCXaWe+gAKCRD7/MgqAV5zMG9sC/9U
+2T3RrqEbw533FPNfEflhEVRIZ8gDXKM8hU6cqqEzCmzZT6xYTe6sv4y+PJBGXJFX
+yhj0g6FDkSyboM5litOcTupURObVqMgA/Y4UKERznm4fzzH9qek85c4ljtLyNufe
+doL2pp3vkGtn7eD0QFRaLLmnxPKQ/TlZKdLE1G3u8Uot8QHicaR6GnAdc5UXQJE3
+BiV7jZuDyWmZ1cUNwJkKL6oRtp+ZNDOQCrLNLecKHcgCqrpjSQG5oouba1I1Q6Vl
+sP44dhA1nkmLHtxlTOzpeHj4jnk1FaXmyasurrrI5CgU/L2Oi39DGKTH/A/cywDN
+4ZplIQ9zR8enkbXquUZvFDe+Xz+6xRXtb5MwQyWODB3nHw85HocLwRoIN9WdQEI+
+L8a/56AuOwhs8llkSuiITjR7r9SgKJC2WlAHl7E8lhJ3VDW3ELC56KH308d6mwOG
+ZRAqIAKzM1T5FGjMBhq7ZV0eqdEntBh3EcOIfj2M8rg1MzJv+0mHZOIjByawikad
+BVgEXaWc8gEMANYwv1xsYyunXYK0X1vY/rP1NNPvhLyLIE7NpK90YNBj+xS1ldGD
+bUdZqZeef2xJe8gMQg05DoD1DF3GipZ0Ies65beh+d5hegb7N4pzh0LzrBrVNHar
+29b5ExdI7i4iYD5TO6Vr/qTUOiAN/byqELEzAb+L+b2DVz/RoCm4PIp1DU9ewcc2
+WB38Ofqut3nLYA5tqJ9XvAiEQme+qAVcM3ZFcaMt4I4dXhDZZNg+D9LiTWcxdUPB
+leu8iwDRjAgyAhPzpFp+nWoqWA81uIiULWD1Fj+IVoY3ZvgivoYOiEFBJ9lbb4te
+g9m5UT/AaVDTWuHzbspVlbiVe+qyB77C2daWzNyx6UYBPLOo4r0t0c91kbNE5lgj
+Z7xz6los0N1U8vq91EFSeQJoSQ62XWavYmlCLmdNT6BNfgh4icLsT7Vr1QMX9jzn
+JtTPxdXytSdHvpSpULsqJ016l0dtmONcK3z9mj5N5z0k1tg1AH970TGYOe2aUcSx
+IRDMXDOPyzEfjwARAQABAAv9F2CwsjS+Sjh1M1vegJbZjei4gF1HHpEM0K0PSXsp
+SfVvpR4AoSJ4He6CXSMWg0ot8XKtDuZoV9jnJaES5UL9pMAD7JwIOqZm/DYVJM5h
+OASCh1c356/wSbFbzRHPtUdZO9Q30WFNJM5pHbCJPjtNoRmRGkf71RxtvHBzy7np
+Ga+W6U/NVKHw0i0CYwMI0YlKDakYW3Pm+QL+gHZFvngGweTod0f9l2VLLAmeQR/c
++EZs7lNumhuZ8mXcwhUc9JQIhOkpO+wreDysEFkAcsKbkQP3UDUsA1gFx9pbMzT0
+tr1oZq2a4QBtxShHzP/ph7KLpN+6qtjks3xB/yjTgaGmtrwM8tSe0wD1RwXS+/1o
+BHpXTnQ7TfeOGUAu4KCoOQLv6ELpKWbRBLWuiPwMdbGpvVFALO8+kvKAg9/r+/ny
+zM2GQHY+J3Jh5JxPiJnHfXNZjIKLbFbIPdSKNyJBuazXW8xIa//mEHMI5OcvsZBK
+clAIp7LXzjEjKXIwHwDcTn9pBgDpdOKTHOtJ3JUKx0rWVsDH6wq6iKV/FTVSY5jl
+zN+puOEsskF1Lfxn9JsJihAVO3yNsp6RvkKtyNlFazaCVKtDAmkjoh60XNxcNRqr
+gCnwdpbgdHP6v/hvZY54ZaJjz6L2e8unNEkYLxDt8cmAyGPgH2XgL7giHIp9jrsQ
+aS381gnYwNX6wE1aEikgtY91nqJjwPlibF9avSyYQoMtEqM/1UjTjB2KdD/MitK5
+fP0VpvuXpNYZedmyq4UOMwdkiNMGAOrfmOeT0olgLrTMT5H97Cn3Yxbk13uXHNu/
+ZUZZNe8s+QtuLfUlKAJtLEUutN33TlWQY522FV0m17S+b80xJib3yZVJteVurrh5
+HSWHAM+zghQAvCesg5CLXa2dNMkTCmZKgCBvfDLZuZbjFwnwCI6u/NhOY9egKuUf
+SA/je/RXaT8m5VxLYMxwqQXKApzD87fv0tLPlVIEvjEsaf992tFEFSNPcG1l/jpd
+5AVXw6kKuf85UkJtYR1x2MkQDrqY1QX/XMw00kt8y9kMZUre19aCArcmor+hDhRJ
+E3Gt4QJrD9z/bICESw4b4z2DbgD/Xz9IXsA/r9cKiM1h5QMtXvuhyfVeM01enhxM
+GbOH3gjqqGNKysx0UODGEwr6AV9hAd8RWXMchJLaExK9J5SRawSg671ObAU24SdY
+vMQ9Z4kAQ2+1ReUZzf3ogSMRZtMT+d18gT6L90/y+APZIaoArLPhebIAGq39HLmJ
+26x3z0WAgrpA1kNsjXEXkoiZGPLKIGoe3hqJAbYEGAEKACAWIQTRpm4aI7GCyZgP
+eIz7/MgqAV5zMAUCXaWc8gIbDAAKCRD7/MgqAV5zMOn/C/9ugt+HZIwX308zI+QX
+c5vDLReuzmJ3ieE0DMO/uNSC+K1XEioSIZP91HeZJ2kbT9nn9fuReuoff0T0Dief
+rbwcIQQHFFkrqSp1K3VWmUGp2JrUsXFVdjy/fkBIjTd7c5boWljv/6wAsSfiv2V0
+JSM8EFU6TYXxswGjFVfc6X97tJNeIrXL+mpSmPPqy2bztcCCHkWS5lNLWQw+R7Vg
+71Fe6yBSNVrqC2/imYG2J9zlowjx1XU63Wdgqp2Wxt0l8OmsB/W80S1fRF5G4SDH
+s9HXglXXqPsBRZJYfP+VStm9L5P/sKjCcX6WtZR7yS6G8zj/X767MLK/djANvpPd
+NVniEke6hM3CNBXYPAMhQBMWhCulcoz+0lxi8L34rMN+Dsbma96psdUrn7uLaB91
+6we0CTfF8qqm7BsVAgalon/UUiuMY80U3ueoj3okiSTiHIjD/YtpXSPioC8nMng7
+xqAY9Bwizt4FWgXuLm1a4+So4V9j1TRCXd12Uc2l2RNmgDE=
+=miES
+-----END PGP PRIVATE KEY BLOCK-----
+`
+
+const certv5Test = `-----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-----
+`
+
+const msgv5Test = `-----BEGIN PGP MESSAGE-----
+
+wcDMA3wvqk35PDeyAQv+PcQiLsoYTH30nJYQh3j3cJaO2+jErtVCrIQRIU0+
+rmgMddERYST4A9mA0DQIiTI4FQ0Lp440D3BWCgpq3LlNWewGzduaWwym5rN6
+cwHz5ccDqOcqbd9X0GXXGy/ZH/ljSgzuVMIytMAXKdF/vrRrVgH/+I7cxvm9
+HwnhjMN5dF0j4aEt996H2T7cbtzSr2GN9SWGW8Gyu7I8Zx73hgrGUI7gDiJB
+Afaff+P6hfkkHSGOItr94dde8J/7AUF4VEwwxdVVPvsNEFyvv6gRIbYtOCa2
+6RE6h1V/QTxW2O7zZgzWALrE2ui0oaYr9QuqQSssd9CdgExLfdPbI+3/ZAnE
+v31Idzpk3/6ILiakYHtXkElPXvf46mCNpobty8ysT34irF+fy3C1p3oGwAsx
+5VDV9OSFU6z5U+UPbSPYAy9rkc5ZssuIKxCER2oTvZ2L8Q5cfUvEUiJtRGGn
+CJlHrVDdp3FssKv2tlKgLkvxJLyoOjuEkj44H1qRk+D02FzmmUT/0sAHAYYx
+lTir6mjHeLpcGjn4waUuWIAJyph8SxUexP60bic0L0NBa6Qp5SxxijKsPIDb
+FPHxWwfJSDZRrgUyYT7089YFB/ZM4FHyH9TZcnxn0f0xIB7NS6YNDsxzN2zT
+EVEYf+De4qT/dQTsdww78Chtcv9JY9r2kDm77dk2MUGHL2j7n8jasbLtgA7h
+pn2DMIWLrGamMLWRmlwslolKr1sMV5x8w+5Ias6C33iBMl9phkg42an0gYmc
+byVJHvLO/XErtC+GNIJeMg==
+=liRq
+-----END PGP MESSAGE-----
+`
diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/s2k/s2k.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/s2k/s2k.go
index a43695964b..6871b84fc9 100644
--- a/vendor/github.com/ProtonMail/go-crypto/openpgp/s2k/s2k.go
+++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/s2k/s2k.go
@@ -87,10 +87,10 @@ func decodeCount(c uint8) int {
// encodeMemory converts the Argon2 "memory" in the range parallelism*8 to
// 2**31, inclusive, to an encoded memory. The return value is the
// octet that is actually stored in the GPG file. encodeMemory panics
-// if is not in the above range
+// if is not in the above range
// See OpenPGP crypto refresh Section 3.7.1.4.
func encodeMemory(memory uint32, parallelism uint8) uint8 {
- if memory < (8 * uint32(parallelism)) || memory > uint32(2147483648) {
+ if memory < (8*uint32(parallelism)) || memory > uint32(2147483648) {
panic("Memory argument memory is outside the required range")
}
@@ -199,8 +199,8 @@ func Generate(rand io.Reader, c *Config) (*Params, error) {
}
params = &Params{
- mode: SaltedS2K,
- hashId: hashId,
+ mode: SaltedS2K,
+ hashId: hashId,
}
} else { // Enforce IteratedSaltedS2K method otherwise
hashId, ok := algorithm.HashToHashId(c.hash())
@@ -211,7 +211,7 @@ func Generate(rand io.Reader, c *Config) (*Params, error) {
c.S2KMode = IteratedSaltedS2K
}
params = &Params{
- mode: IteratedSaltedS2K,
+ mode: IteratedSaltedS2K,
hashId: hashId,
countByte: c.EncodedCount(),
}
@@ -283,6 +283,9 @@ func ParseIntoParams(r io.Reader) (params *Params, err error) {
params.passes = buf[Argon2SaltSize]
params.parallelism = buf[Argon2SaltSize+1]
params.memoryExp = buf[Argon2SaltSize+2]
+ if err := validateArgon2Params(params); err != nil {
+ return nil, err
+ }
return params, nil
case GnuS2K:
// This is a GNU extension. See
@@ -300,15 +303,22 @@ func ParseIntoParams(r io.Reader) (params *Params, err error) {
return nil, errors.UnsupportedError("S2K function")
}
+func (params *Params) Mode() Mode {
+ return params.mode
+}
+
func (params *Params) Dummy() bool {
return params != nil && params.mode == GnuS2K
}
func (params *Params) salt() []byte {
switch params.mode {
- case SaltedS2K, IteratedSaltedS2K: return params.saltBytes[:8]
- case Argon2S2K: return params.saltBytes[:Argon2SaltSize]
- default: return nil
+ case SaltedS2K, IteratedSaltedS2K:
+ return params.saltBytes[:8]
+ case Argon2S2K:
+ return params.saltBytes[:Argon2SaltSize]
+ default:
+ return nil
}
}
@@ -405,3 +415,22 @@ func Serialize(w io.Writer, key []byte, rand io.Reader, passphrase []byte, c *Co
f(key, passphrase)
return nil
}
+
+// validateArgon2Params checks that the argon2 parameters are valid according to RFC9580.
+func validateArgon2Params(params *Params) error {
+ // The number of passes t and the degree of parallelism p MUST be non-zero.
+ if params.parallelism == 0 {
+ return errors.StructuralError("invalid argon2 params: parallelism is 0")
+ }
+ if params.passes == 0 {
+ return errors.StructuralError("invalid argon2 params: iterations is 0")
+ }
+
+ // The encoded memory size MUST be a value from 3+ceil(log2(p)) to 31,
+ // such that the decoded memory size m is a value from 8*p to 2^31.
+ if params.memoryExp > 31 || decodeMemory(params.memoryExp) < 8*uint32(params.parallelism) {
+ return errors.StructuralError("invalid argon2 params: memory is out of bounds")
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/s2k/s2k_cache.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/s2k/s2k_cache.go
index 25a4442dfb..616e0d12c6 100644
--- a/vendor/github.com/ProtonMail/go-crypto/openpgp/s2k/s2k_cache.go
+++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/s2k/s2k_cache.go
@@ -5,7 +5,7 @@ package s2k
// the same parameters.
type Cache map[Params][]byte
-// GetOrComputeDerivedKey tries to retrieve the key
+// GetOrComputeDerivedKey tries to retrieve the key
// for the given s2k parameters from the cache.
// If there is no hit, it derives the key with the s2k function from the passphrase,
// updates the cache, and returns the key.
diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/s2k/s2k_config.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/s2k/s2k_config.go
index b40be5228f..b93db1ab85 100644
--- a/vendor/github.com/ProtonMail/go-crypto/openpgp/s2k/s2k_config.go
+++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/s2k/s2k_config.go
@@ -50,9 +50,9 @@ type Config struct {
type Argon2Config struct {
NumberOfPasses uint8
DegreeOfParallelism uint8
- // The memory parameter for Argon2 specifies desired memory usage in kibibytes.
+ // Memory specifies the desired Argon2 memory usage in kibibytes.
// For example memory=64*1024 sets the memory cost to ~64 MB.
- Memory uint32
+ Memory uint32
}
func (c *Config) Mode() Mode {
@@ -115,7 +115,7 @@ func (c *Argon2Config) EncodedMemory() uint8 {
}
memory := c.Memory
- lowerBound := uint32(c.Parallelism())*8
+ lowerBound := uint32(c.Parallelism()) * 8
upperBound := uint32(2147483648)
switch {
diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/write.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/write.go
index 7fdd13a3dd..b0f6ef7b09 100644
--- a/vendor/github.com/ProtonMail/go-crypto/openpgp/write.go
+++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/write.go
@@ -76,7 +76,11 @@ func detachSign(w io.Writer, signer *Entity, message io.Reader, sigType packet.S
sig := createSignaturePacket(signingKey.PublicKey, sigType, config)
- h, wrappedHash, err := hashForSignature(sig.Hash, sig.SigType)
+ h, err := sig.PrepareSign(config)
+ if err != nil {
+ return
+ }
+ wrappedHash, err := wrapHashForSignature(h, sig.SigType)
if err != nil {
return
}
@@ -275,14 +279,28 @@ func writeAndSign(payload io.WriteCloser, candidateHashes []uint8, signed *Entit
return nil, errors.InvalidArgumentError("cannot encrypt because no candidate hash functions are compiled in. (Wanted " + name + " in this case.)")
}
+ var salt []byte
if signer != nil {
+ var opsVersion = 3
+ if signer.Version == 6 {
+ opsVersion = signer.Version
+ }
ops := &packet.OnePassSignature{
+ Version: opsVersion,
SigType: sigType,
Hash: hash,
PubKeyAlgo: signer.PubKeyAlgo,
KeyId: signer.KeyId,
IsLast: true,
}
+ if opsVersion == 6 {
+ ops.KeyFingerprint = signer.Fingerprint
+ salt, err = packet.SignatureSaltForHash(hash, config.Random())
+ if err != nil {
+ return nil, err
+ }
+ ops.Salt = salt
+ }
if err := ops.Serialize(payload); err != nil {
return nil, err
}
@@ -310,19 +328,19 @@ func writeAndSign(payload io.WriteCloser, candidateHashes []uint8, signed *Entit
}
if signer != nil {
- h, wrappedHash, err := hashForSignature(hash, sigType)
+ h, wrappedHash, err := hashForSignature(hash, sigType, salt)
if err != nil {
return nil, err
}
metadata := &packet.LiteralData{
- Format: 't',
+ Format: 'u',
FileName: hints.FileName,
Time: epochSeconds,
}
if hints.IsBinary {
metadata.Format = 'b'
}
- return signatureWriter{payload, literalData, hash, wrappedHash, h, signer, sigType, config, metadata}, nil
+ return signatureWriter{payload, literalData, hash, wrappedHash, h, salt, signer, sigType, config, metadata}, nil
}
return literalData, nil
}
@@ -380,15 +398,19 @@ func encrypt(keyWriter io.Writer, dataWriter io.Writer, to []*Entity, signed *En
return nil, errors.InvalidArgumentError("cannot encrypt a message to key id " + strconv.FormatUint(to[i].PrimaryKey.KeyId, 16) + " because it has no valid encryption keys")
}
- sig := to[i].PrimaryIdentity().SelfSignature
- if !sig.SEIPDv2 {
+ primarySelfSignature, _ := to[i].PrimarySelfSignature()
+ if primarySelfSignature == nil {
+ return nil, errors.InvalidArgumentError("entity without a self-signature")
+ }
+
+ if !primarySelfSignature.SEIPDv2 {
aeadSupported = false
}
- candidateCiphers = intersectPreferences(candidateCiphers, sig.PreferredSymmetric)
- candidateHashes = intersectPreferences(candidateHashes, sig.PreferredHash)
- candidateCipherSuites = intersectCipherSuites(candidateCipherSuites, sig.PreferredCipherSuites)
- candidateCompression = intersectPreferences(candidateCompression, sig.PreferredCompression)
+ candidateCiphers = intersectPreferences(candidateCiphers, primarySelfSignature.PreferredSymmetric)
+ candidateHashes = intersectPreferences(candidateHashes, primarySelfSignature.PreferredHash)
+ candidateCipherSuites = intersectCipherSuites(candidateCipherSuites, primarySelfSignature.PreferredCipherSuites)
+ candidateCompression = intersectPreferences(candidateCompression, primarySelfSignature.PreferredCompression)
}
// In the event that the intersection of supported algorithms is empty we use the ones
@@ -422,13 +444,19 @@ func encrypt(keyWriter io.Writer, dataWriter io.Writer, to []*Entity, signed *En
}
}
- symKey := make([]byte, cipher.KeySize())
+ var symKey []byte
+ if aeadSupported {
+ symKey = make([]byte, aeadCipherSuite.Cipher.KeySize())
+ } else {
+ symKey = make([]byte, cipher.KeySize())
+ }
+
if _, err := io.ReadFull(config.Random(), symKey); err != nil {
return nil, err
}
for _, key := range encryptKeys {
- if err := packet.SerializeEncryptedKey(keyWriter, key.PublicKey, cipher, symKey, config); err != nil {
+ if err := packet.SerializeEncryptedKeyAEAD(keyWriter, key.PublicKey, cipher, aeadSupported, symKey, config); err != nil {
return nil, err
}
}
@@ -465,13 +493,17 @@ func Sign(output io.Writer, signed *Entity, hints *FileHints, config *packet.Con
hashToHashId(crypto.SHA3_512),
}
defaultHashes := candidateHashes[0:1]
- preferredHashes := signed.PrimaryIdentity().SelfSignature.PreferredHash
+ primarySelfSignature, _ := signed.PrimarySelfSignature()
+ if primarySelfSignature == nil {
+ return nil, errors.StructuralError("signed entity has no self-signature")
+ }
+ preferredHashes := primarySelfSignature.PreferredHash
if len(preferredHashes) == 0 {
preferredHashes = defaultHashes
}
candidateHashes = intersectPreferences(candidateHashes, preferredHashes)
if len(candidateHashes) == 0 {
- return nil, errors.InvalidArgumentError("cannot sign because signing key shares no common algorithms with candidate hashes")
+ return nil, errors.StructuralError("cannot sign because signing key shares no common algorithms with candidate hashes")
}
return writeAndSign(noOpCloser{output}, candidateHashes, signed, hints, packet.SigTypeBinary, config)
@@ -486,6 +518,7 @@ type signatureWriter struct {
hashType crypto.Hash
wrappedHash hash.Hash
h hash.Hash
+ salt []byte // v6 only
signer *packet.PrivateKey
sigType packet.SignatureType
config *packet.Config
@@ -509,6 +542,10 @@ func (s signatureWriter) Close() error {
sig.Hash = s.hashType
sig.Metadata = s.metadata
+ if err := sig.SetSalt(s.salt); err != nil {
+ return err
+ }
+
if err := sig.Sign(s.h, s.signer, s.config); err != nil {
return err
}
diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/x25519/x25519.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/x25519/x25519.go
new file mode 100644
index 0000000000..38afcc74fa
--- /dev/null
+++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/x25519/x25519.go
@@ -0,0 +1,221 @@
+package x25519
+
+import (
+ "crypto/sha256"
+ "crypto/subtle"
+ "io"
+
+ "github.com/ProtonMail/go-crypto/openpgp/aes/keywrap"
+ "github.com/ProtonMail/go-crypto/openpgp/errors"
+ x25519lib "github.com/cloudflare/circl/dh/x25519"
+ "golang.org/x/crypto/hkdf"
+)
+
+const (
+ hkdfInfo = "OpenPGP X25519"
+ aes128KeySize = 16
+ // The size of a public or private key in bytes.
+ KeySize = x25519lib.Size
+)
+
+type PublicKey struct {
+ // Point represents the encoded elliptic curve point of the public key.
+ Point []byte
+}
+
+type PrivateKey struct {
+ PublicKey
+ // Secret represents the secret of the private key.
+ Secret []byte
+}
+
+// NewPrivateKey creates a new empty private key including the public key.
+func NewPrivateKey(key PublicKey) *PrivateKey {
+ return &PrivateKey{
+ PublicKey: key,
+ }
+}
+
+// Validate validates that the provided public key matches the private key.
+func Validate(pk *PrivateKey) (err error) {
+ var expectedPublicKey, privateKey x25519lib.Key
+ subtle.ConstantTimeCopy(1, privateKey[:], pk.Secret)
+ x25519lib.KeyGen(&expectedPublicKey, &privateKey)
+ if subtle.ConstantTimeCompare(expectedPublicKey[:], pk.PublicKey.Point) == 0 {
+ return errors.KeyInvalidError("x25519: invalid key")
+ }
+ return nil
+}
+
+// GenerateKey generates a new x25519 key pair.
+func GenerateKey(rand io.Reader) (*PrivateKey, error) {
+ var privateKey, publicKey x25519lib.Key
+ privateKeyOut := new(PrivateKey)
+ err := generateKey(rand, &privateKey, &publicKey)
+ if err != nil {
+ return nil, err
+ }
+ privateKeyOut.PublicKey.Point = publicKey[:]
+ privateKeyOut.Secret = privateKey[:]
+ return privateKeyOut, nil
+}
+
+func generateKey(rand io.Reader, privateKey *x25519lib.Key, publicKey *x25519lib.Key) error {
+ maxRounds := 10
+ isZero := true
+ for round := 0; isZero; round++ {
+ if round == maxRounds {
+ return errors.InvalidArgumentError("x25519: zero keys only, randomness source might be corrupt")
+ }
+ _, err := io.ReadFull(rand, privateKey[:])
+ if err != nil {
+ return err
+ }
+ isZero = constantTimeIsZero(privateKey[:])
+ }
+ x25519lib.KeyGen(publicKey, privateKey)
+ return nil
+}
+
+// Encrypt encrypts a sessionKey with x25519 according to
+// the OpenPGP crypto refresh specification section 5.1.6. The function assumes that the
+// sessionKey has the correct format and padding according to the specification.
+func Encrypt(rand io.Reader, publicKey *PublicKey, sessionKey []byte) (ephemeralPublicKey *PublicKey, encryptedSessionKey []byte, err error) {
+ var ephemeralPrivate, ephemeralPublic, staticPublic, shared x25519lib.Key
+ // Check that the input static public key has 32 bytes
+ if len(publicKey.Point) != KeySize {
+ err = errors.KeyInvalidError("x25519: the public key has the wrong size")
+ return
+ }
+ copy(staticPublic[:], publicKey.Point)
+ // Generate ephemeral keyPair
+ err = generateKey(rand, &ephemeralPrivate, &ephemeralPublic)
+ if err != nil {
+ return
+ }
+ // Compute shared key
+ ok := x25519lib.Shared(&shared, &ephemeralPrivate, &staticPublic)
+ if !ok {
+ err = errors.KeyInvalidError("x25519: the public key is a low order point")
+ return
+ }
+ // Derive the encryption key from the shared secret
+ encryptionKey := applyHKDF(ephemeralPublic[:], publicKey.Point[:], shared[:])
+ ephemeralPublicKey = &PublicKey{
+ Point: ephemeralPublic[:],
+ }
+ // Encrypt the sessionKey with aes key wrapping
+ encryptedSessionKey, err = keywrap.Wrap(encryptionKey, sessionKey)
+ return
+}
+
+// Decrypt decrypts a session key stored in ciphertext with the provided x25519
+// private key and ephemeral public key.
+func Decrypt(privateKey *PrivateKey, ephemeralPublicKey *PublicKey, ciphertext []byte) (encodedSessionKey []byte, err error) {
+ var ephemeralPublic, staticPrivate, shared x25519lib.Key
+ // Check that the input ephemeral public key has 32 bytes
+ if len(ephemeralPublicKey.Point) != KeySize {
+ err = errors.KeyInvalidError("x25519: the public key has the wrong size")
+ return
+ }
+ copy(ephemeralPublic[:], ephemeralPublicKey.Point)
+ subtle.ConstantTimeCopy(1, staticPrivate[:], privateKey.Secret)
+ // Compute shared key
+ ok := x25519lib.Shared(&shared, &staticPrivate, &ephemeralPublic)
+ if !ok {
+ err = errors.KeyInvalidError("x25519: the ephemeral public key is a low order point")
+ return
+ }
+ // Derive the encryption key from the shared secret
+ encryptionKey := applyHKDF(ephemeralPublicKey.Point[:], privateKey.PublicKey.Point[:], shared[:])
+ // Decrypt the session key with aes key wrapping
+ encodedSessionKey, err = keywrap.Unwrap(encryptionKey, ciphertext)
+ return
+}
+
+func applyHKDF(ephemeralPublicKey []byte, publicKey []byte, sharedSecret []byte) []byte {
+ inputKey := make([]byte, 3*KeySize)
+ // ephemeral public key | recipient public key | shared secret
+ subtle.ConstantTimeCopy(1, inputKey[:KeySize], ephemeralPublicKey)
+ subtle.ConstantTimeCopy(1, inputKey[KeySize:2*KeySize], publicKey)
+ subtle.ConstantTimeCopy(1, inputKey[2*KeySize:], sharedSecret)
+ hkdfReader := hkdf.New(sha256.New, inputKey, []byte{}, []byte(hkdfInfo))
+ encryptionKey := make([]byte, aes128KeySize)
+ _, _ = io.ReadFull(hkdfReader, encryptionKey)
+ return encryptionKey
+}
+
+func constantTimeIsZero(bytes []byte) bool {
+ isZero := byte(0)
+ for _, b := range bytes {
+ isZero |= b
+ }
+ return isZero == 0
+}
+
+// ENCODING/DECODING ciphertexts:
+
+// EncodeFieldsLength returns the length of the ciphertext encoding
+// given the encrypted session key.
+func EncodedFieldsLength(encryptedSessionKey []byte, v6 bool) int {
+ lenCipherFunction := 0
+ if !v6 {
+ lenCipherFunction = 1
+ }
+ return KeySize + 1 + len(encryptedSessionKey) + lenCipherFunction
+}
+
+// EncodeField encodes x25519 session key encryption fields as
+// ephemeral x25519 public key | follow byte length | cipherFunction (v3 only) | encryptedSessionKey
+// and writes it to writer.
+func EncodeFields(writer io.Writer, ephemeralPublicKey *PublicKey, encryptedSessionKey []byte, cipherFunction byte, v6 bool) (err error) {
+ lenAlgorithm := 0
+ if !v6 {
+ lenAlgorithm = 1
+ }
+ if _, err = writer.Write(ephemeralPublicKey.Point); err != nil {
+ return err
+ }
+ if _, err = writer.Write([]byte{byte(len(encryptedSessionKey) + lenAlgorithm)}); err != nil {
+ return err
+ }
+ if !v6 {
+ if _, err = writer.Write([]byte{cipherFunction}); err != nil {
+ return err
+ }
+ }
+ _, err = writer.Write(encryptedSessionKey)
+ return err
+}
+
+// DecodeField decodes a x25519 session key encryption as
+// ephemeral x25519 public key | follow byte length | cipherFunction (v3 only) | encryptedSessionKey.
+func DecodeFields(reader io.Reader, v6 bool) (ephemeralPublicKey *PublicKey, encryptedSessionKey []byte, cipherFunction byte, err error) {
+ var buf [1]byte
+ ephemeralPublicKey = &PublicKey{
+ Point: make([]byte, KeySize),
+ }
+ // 32 octets representing an ephemeral x25519 public key.
+ if _, err = io.ReadFull(reader, ephemeralPublicKey.Point); err != nil {
+ return nil, nil, 0, err
+ }
+ // A one-octet size of the following fields.
+ if _, err = io.ReadFull(reader, buf[:]); err != nil {
+ return nil, nil, 0, err
+ }
+ followingLen := buf[0]
+ // The one-octet algorithm identifier, if it was passed (in the case of a v3 PKESK packet).
+ if !v6 {
+ if _, err = io.ReadFull(reader, buf[:]); err != nil {
+ return nil, nil, 0, err
+ }
+ cipherFunction = buf[0]
+ followingLen -= 1
+ }
+ // The encrypted session key.
+ encryptedSessionKey = make([]byte, followingLen)
+ if _, err = io.ReadFull(reader, encryptedSessionKey); err != nil {
+ return nil, nil, 0, err
+ }
+ return ephemeralPublicKey, encryptedSessionKey, cipherFunction, nil
+}
diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/x448/x448.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/x448/x448.go
new file mode 100644
index 0000000000..65a082dabd
--- /dev/null
+++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/x448/x448.go
@@ -0,0 +1,229 @@
+package x448
+
+import (
+ "crypto/sha512"
+ "crypto/subtle"
+ "io"
+
+ "github.com/ProtonMail/go-crypto/openpgp/aes/keywrap"
+ "github.com/ProtonMail/go-crypto/openpgp/errors"
+ x448lib "github.com/cloudflare/circl/dh/x448"
+ "golang.org/x/crypto/hkdf"
+)
+
+const (
+ hkdfInfo = "OpenPGP X448"
+ aes256KeySize = 32
+ // The size of a public or private key in bytes.
+ KeySize = x448lib.Size
+)
+
+type PublicKey struct {
+ // Point represents the encoded elliptic curve point of the public key.
+ Point []byte
+}
+
+type PrivateKey struct {
+ PublicKey
+ // Secret represents the secret of the private key.
+ Secret []byte
+}
+
+// NewPrivateKey creates a new empty private key including the public key.
+func NewPrivateKey(key PublicKey) *PrivateKey {
+ return &PrivateKey{
+ PublicKey: key,
+ }
+}
+
+// Validate validates that the provided public key matches
+// the private key.
+func Validate(pk *PrivateKey) (err error) {
+ var expectedPublicKey, privateKey x448lib.Key
+ subtle.ConstantTimeCopy(1, privateKey[:], pk.Secret)
+ x448lib.KeyGen(&expectedPublicKey, &privateKey)
+ if subtle.ConstantTimeCompare(expectedPublicKey[:], pk.PublicKey.Point) == 0 {
+ return errors.KeyInvalidError("x448: invalid key")
+ }
+ return nil
+}
+
+// GenerateKey generates a new x448 key pair.
+func GenerateKey(rand io.Reader) (*PrivateKey, error) {
+ var privateKey, publicKey x448lib.Key
+ privateKeyOut := new(PrivateKey)
+ err := generateKey(rand, &privateKey, &publicKey)
+ if err != nil {
+ return nil, err
+ }
+ privateKeyOut.PublicKey.Point = publicKey[:]
+ privateKeyOut.Secret = privateKey[:]
+ return privateKeyOut, nil
+}
+
+func generateKey(rand io.Reader, privateKey *x448lib.Key, publicKey *x448lib.Key) error {
+ maxRounds := 10
+ isZero := true
+ for round := 0; isZero; round++ {
+ if round == maxRounds {
+ return errors.InvalidArgumentError("x448: zero keys only, randomness source might be corrupt")
+ }
+ _, err := io.ReadFull(rand, privateKey[:])
+ if err != nil {
+ return err
+ }
+ isZero = constantTimeIsZero(privateKey[:])
+ }
+ x448lib.KeyGen(publicKey, privateKey)
+ return nil
+}
+
+// Encrypt encrypts a sessionKey with x448 according to
+// the OpenPGP crypto refresh specification section 5.1.7. The function assumes that the
+// sessionKey has the correct format and padding according to the specification.
+func Encrypt(rand io.Reader, publicKey *PublicKey, sessionKey []byte) (ephemeralPublicKey *PublicKey, encryptedSessionKey []byte, err error) {
+ var ephemeralPrivate, ephemeralPublic, staticPublic, shared x448lib.Key
+ // Check that the input static public key has 56 bytes.
+ if len(publicKey.Point) != KeySize {
+ err = errors.KeyInvalidError("x448: the public key has the wrong size")
+ return nil, nil, err
+ }
+ copy(staticPublic[:], publicKey.Point)
+ // Generate ephemeral keyPair.
+ if err = generateKey(rand, &ephemeralPrivate, &ephemeralPublic); err != nil {
+ return nil, nil, err
+ }
+ // Compute shared key.
+ ok := x448lib.Shared(&shared, &ephemeralPrivate, &staticPublic)
+ if !ok {
+ err = errors.KeyInvalidError("x448: the public key is a low order point")
+ return nil, nil, err
+ }
+ // Derive the encryption key from the shared secret.
+ encryptionKey := applyHKDF(ephemeralPublic[:], publicKey.Point[:], shared[:])
+ ephemeralPublicKey = &PublicKey{
+ Point: ephemeralPublic[:],
+ }
+ // Encrypt the sessionKey with aes key wrapping.
+ encryptedSessionKey, err = keywrap.Wrap(encryptionKey, sessionKey)
+ if err != nil {
+ return nil, nil, err
+ }
+ return ephemeralPublicKey, encryptedSessionKey, nil
+}
+
+// Decrypt decrypts a session key stored in ciphertext with the provided x448
+// private key and ephemeral public key.
+func Decrypt(privateKey *PrivateKey, ephemeralPublicKey *PublicKey, ciphertext []byte) (encodedSessionKey []byte, err error) {
+ var ephemeralPublic, staticPrivate, shared x448lib.Key
+ // Check that the input ephemeral public key has 56 bytes.
+ if len(ephemeralPublicKey.Point) != KeySize {
+ err = errors.KeyInvalidError("x448: the public key has the wrong size")
+ return nil, err
+ }
+ copy(ephemeralPublic[:], ephemeralPublicKey.Point)
+ subtle.ConstantTimeCopy(1, staticPrivate[:], privateKey.Secret)
+ // Compute shared key.
+ ok := x448lib.Shared(&shared, &staticPrivate, &ephemeralPublic)
+ if !ok {
+ err = errors.KeyInvalidError("x448: the ephemeral public key is a low order point")
+ return nil, err
+ }
+ // Derive the encryption key from the shared secret.
+ encryptionKey := applyHKDF(ephemeralPublicKey.Point[:], privateKey.PublicKey.Point[:], shared[:])
+ // Decrypt the session key with aes key wrapping.
+ encodedSessionKey, err = keywrap.Unwrap(encryptionKey, ciphertext)
+ if err != nil {
+ return nil, err
+ }
+ return encodedSessionKey, nil
+}
+
+func applyHKDF(ephemeralPublicKey []byte, publicKey []byte, sharedSecret []byte) []byte {
+ inputKey := make([]byte, 3*KeySize)
+ // ephemeral public key | recipient public key | shared secret.
+ subtle.ConstantTimeCopy(1, inputKey[:KeySize], ephemeralPublicKey)
+ subtle.ConstantTimeCopy(1, inputKey[KeySize:2*KeySize], publicKey)
+ subtle.ConstantTimeCopy(1, inputKey[2*KeySize:], sharedSecret)
+ hkdfReader := hkdf.New(sha512.New, inputKey, []byte{}, []byte(hkdfInfo))
+ encryptionKey := make([]byte, aes256KeySize)
+ _, _ = io.ReadFull(hkdfReader, encryptionKey)
+ return encryptionKey
+}
+
+func constantTimeIsZero(bytes []byte) bool {
+ isZero := byte(0)
+ for _, b := range bytes {
+ isZero |= b
+ }
+ return isZero == 0
+}
+
+// ENCODING/DECODING ciphertexts:
+
+// EncodeFieldsLength returns the length of the ciphertext encoding
+// given the encrypted session key.
+func EncodedFieldsLength(encryptedSessionKey []byte, v6 bool) int {
+ lenCipherFunction := 0
+ if !v6 {
+ lenCipherFunction = 1
+ }
+ return KeySize + 1 + len(encryptedSessionKey) + lenCipherFunction
+}
+
+// EncodeField encodes x448 session key encryption fields as
+// ephemeral x448 public key | follow byte length | cipherFunction (v3 only) | encryptedSessionKey
+// and writes it to writer.
+func EncodeFields(writer io.Writer, ephemeralPublicKey *PublicKey, encryptedSessionKey []byte, cipherFunction byte, v6 bool) (err error) {
+ lenAlgorithm := 0
+ if !v6 {
+ lenAlgorithm = 1
+ }
+ if _, err = writer.Write(ephemeralPublicKey.Point); err != nil {
+ return err
+ }
+ if _, err = writer.Write([]byte{byte(len(encryptedSessionKey) + lenAlgorithm)}); err != nil {
+ return err
+ }
+ if !v6 {
+ if _, err = writer.Write([]byte{cipherFunction}); err != nil {
+ return err
+ }
+ }
+ if _, err = writer.Write(encryptedSessionKey); err != nil {
+ return err
+ }
+ return nil
+}
+
+// DecodeField decodes a x448 session key encryption as
+// ephemeral x448 public key | follow byte length | cipherFunction (v3 only) | encryptedSessionKey.
+func DecodeFields(reader io.Reader, v6 bool) (ephemeralPublicKey *PublicKey, encryptedSessionKey []byte, cipherFunction byte, err error) {
+ var buf [1]byte
+ ephemeralPublicKey = &PublicKey{
+ Point: make([]byte, KeySize),
+ }
+ // 56 octets representing an ephemeral x448 public key.
+ if _, err = io.ReadFull(reader, ephemeralPublicKey.Point); err != nil {
+ return nil, nil, 0, err
+ }
+ // A one-octet size of the following fields.
+ if _, err = io.ReadFull(reader, buf[:]); err != nil {
+ return nil, nil, 0, err
+ }
+ followingLen := buf[0]
+ // The one-octet algorithm identifier, if it was passed (in the case of a v3 PKESK packet).
+ if !v6 {
+ if _, err = io.ReadFull(reader, buf[:]); err != nil {
+ return nil, nil, 0, err
+ }
+ cipherFunction = buf[0]
+ followingLen -= 1
+ }
+ // The encrypted session key.
+ encryptedSessionKey = make([]byte, followingLen)
+ if _, err = io.ReadFull(reader, encryptedSessionKey); err != nil {
+ return nil, nil, 0, err
+ }
+ return ephemeralPublicKey, encryptedSessionKey, cipherFunction, nil
+}
diff --git a/vendor/github.com/cyphar/filepath-securejoin/CHANGELOG.md b/vendor/github.com/cyphar/filepath-securejoin/CHANGELOG.md
index 04b5685ab4..cb1252b53e 100644
--- a/vendor/github.com/cyphar/filepath-securejoin/CHANGELOG.md
+++ b/vendor/github.com/cyphar/filepath-securejoin/CHANGELOG.md
@@ -6,6 +6,35 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased] ##
+## [0.3.6] - 2024-12-17 ##
+
+### Compatibility ###
+- The minimum Go version requirement for `filepath-securejoin` is now Go 1.18
+ (we use generics internally).
+
+ For reference, `filepath-securejoin@v0.3.0` somewhat-arbitrarily bumped the
+ Go version requirement to 1.21.
+
+ While we did make some use of Go 1.21 stdlib features (and in principle Go
+ versions <= 1.21 are no longer even supported by upstream anymore), some
+ downstreams have complained that the version bump has meant that they have to
+ do workarounds when backporting fixes that use the new `filepath-securejoin`
+ API onto old branches. This is not an ideal situation, but since using this
+ library is probably better for most downstreams than a hand-rolled
+ workaround, we now have compatibility shims that allow us to build on older
+ Go versions.
+- Lower minimum version requirement for `golang.org/x/sys` to `v0.18.0` (we
+ need the wrappers for `fsconfig(2)`), which should also make backporting
+ patches to older branches easier.
+
+## [0.3.5] - 2024-12-06 ##
+
+### Fixed ###
+- `MkdirAll` will now no longer return an `EEXIST` error if two racing
+ processes are creating the same directory. We will still verify that the path
+ is a directory, but this will avoid spurious errors when multiple threads or
+ programs are trying to `MkdirAll` the same path. opencontainers/runc#4543
+
## [0.3.4] - 2024-10-09 ##
### Fixed ###
@@ -164,8 +193,10 @@ This is our first release of `github.com/cyphar/filepath-securejoin`,
containing a full implementation with a coverage of 93.5% (the only missing
cases are the error cases, which are hard to mocktest at the moment).
-[Unreleased]: https://github.com/cyphar/filepath-securejoin/compare/v0.3.4...HEAD
-[0.3.3]: https://github.com/cyphar/filepath-securejoin/compare/v0.3.3...v0.3.4
+[Unreleased]: https://github.com/cyphar/filepath-securejoin/compare/v0.3.6...HEAD
+[0.3.6]: https://github.com/cyphar/filepath-securejoin/compare/v0.3.5...v0.3.6
+[0.3.5]: https://github.com/cyphar/filepath-securejoin/compare/v0.3.4...v0.3.5
+[0.3.4]: https://github.com/cyphar/filepath-securejoin/compare/v0.3.3...v0.3.4
[0.3.3]: https://github.com/cyphar/filepath-securejoin/compare/v0.3.2...v0.3.3
[0.3.2]: https://github.com/cyphar/filepath-securejoin/compare/v0.3.1...v0.3.2
[0.3.1]: https://github.com/cyphar/filepath-securejoin/compare/v0.3.0...v0.3.1
diff --git a/vendor/github.com/cyphar/filepath-securejoin/VERSION b/vendor/github.com/cyphar/filepath-securejoin/VERSION
index 42045acae2..449d7e73a9 100644
--- a/vendor/github.com/cyphar/filepath-securejoin/VERSION
+++ b/vendor/github.com/cyphar/filepath-securejoin/VERSION
@@ -1 +1 @@
-0.3.4
+0.3.6
diff --git a/vendor/github.com/cyphar/filepath-securejoin/gocompat_errors_go120.go b/vendor/github.com/cyphar/filepath-securejoin/gocompat_errors_go120.go
new file mode 100644
index 0000000000..42452bbf9b
--- /dev/null
+++ b/vendor/github.com/cyphar/filepath-securejoin/gocompat_errors_go120.go
@@ -0,0 +1,18 @@
+//go:build linux && go1.20
+
+// Copyright (C) 2024 SUSE LLC. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package securejoin
+
+import (
+ "fmt"
+)
+
+// wrapBaseError is a helper that is equivalent to fmt.Errorf("%w: %w"), except
+// that on pre-1.20 Go versions only errors.Is() works properly (errors.Unwrap)
+// is only guaranteed to give you baseErr.
+func wrapBaseError(baseErr, extraErr error) error {
+ return fmt.Errorf("%w: %w", extraErr, baseErr)
+}
diff --git a/vendor/github.com/cyphar/filepath-securejoin/gocompat_errors_unsupported.go b/vendor/github.com/cyphar/filepath-securejoin/gocompat_errors_unsupported.go
new file mode 100644
index 0000000000..e7adca3fd1
--- /dev/null
+++ b/vendor/github.com/cyphar/filepath-securejoin/gocompat_errors_unsupported.go
@@ -0,0 +1,38 @@
+//go:build linux && !go1.20
+
+// Copyright (C) 2024 SUSE LLC. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package securejoin
+
+import (
+ "fmt"
+)
+
+type wrappedError struct {
+ inner error
+ isError error
+}
+
+func (err wrappedError) Is(target error) bool {
+ return err.isError == target
+}
+
+func (err wrappedError) Unwrap() error {
+ return err.inner
+}
+
+func (err wrappedError) Error() string {
+ return fmt.Sprintf("%v: %v", err.isError, err.inner)
+}
+
+// wrapBaseError is a helper that is equivalent to fmt.Errorf("%w: %w"), except
+// that on pre-1.20 Go versions only errors.Is() works properly (errors.Unwrap)
+// is only guaranteed to give you baseErr.
+func wrapBaseError(baseErr, extraErr error) error {
+ return wrappedError{
+ inner: baseErr,
+ isError: extraErr,
+ }
+}
diff --git a/vendor/github.com/cyphar/filepath-securejoin/gocompat_generics_go121.go b/vendor/github.com/cyphar/filepath-securejoin/gocompat_generics_go121.go
new file mode 100644
index 0000000000..ddd6fa9a41
--- /dev/null
+++ b/vendor/github.com/cyphar/filepath-securejoin/gocompat_generics_go121.go
@@ -0,0 +1,32 @@
+//go:build linux && go1.21
+
+// Copyright (C) 2024 SUSE LLC. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package securejoin
+
+import (
+ "slices"
+ "sync"
+)
+
+func slices_DeleteFunc[S ~[]E, E any](slice S, delFn func(E) bool) S {
+ return slices.DeleteFunc(slice, delFn)
+}
+
+func slices_Contains[S ~[]E, E comparable](slice S, val E) bool {
+ return slices.Contains(slice, val)
+}
+
+func slices_Clone[S ~[]E, E any](slice S) S {
+ return slices.Clone(slice)
+}
+
+func sync_OnceValue[T any](f func() T) func() T {
+ return sync.OnceValue(f)
+}
+
+func sync_OnceValues[T1, T2 any](f func() (T1, T2)) func() (T1, T2) {
+ return sync.OnceValues(f)
+}
diff --git a/vendor/github.com/cyphar/filepath-securejoin/gocompat_generics_unsupported.go b/vendor/github.com/cyphar/filepath-securejoin/gocompat_generics_unsupported.go
new file mode 100644
index 0000000000..f1e6fe7e71
--- /dev/null
+++ b/vendor/github.com/cyphar/filepath-securejoin/gocompat_generics_unsupported.go
@@ -0,0 +1,124 @@
+//go:build linux && !go1.21
+
+// Copyright (C) 2024 SUSE LLC. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package securejoin
+
+import (
+ "sync"
+)
+
+// These are very minimal implementations of functions that appear in Go 1.21's
+// stdlib, included so that we can build on older Go versions. Most are
+// borrowed directly from the stdlib, and a few are modified to be "obviously
+// correct" without needing to copy too many other helpers.
+
+// clearSlice is equivalent to the builtin clear from Go 1.21.
+// Copied from the Go 1.24 stdlib implementation.
+func clearSlice[S ~[]E, E any](slice S) {
+ var zero E
+ for i := range slice {
+ slice[i] = zero
+ }
+}
+
+// Copied from the Go 1.24 stdlib implementation.
+func slices_IndexFunc[S ~[]E, E any](s S, f func(E) bool) int {
+ for i := range s {
+ if f(s[i]) {
+ return i
+ }
+ }
+ return -1
+}
+
+// Copied from the Go 1.24 stdlib implementation.
+func slices_DeleteFunc[S ~[]E, E any](s S, del func(E) bool) S {
+ i := slices_IndexFunc(s, del)
+ if i == -1 {
+ return s
+ }
+ // Don't start copying elements until we find one to delete.
+ for j := i + 1; j < len(s); j++ {
+ if v := s[j]; !del(v) {
+ s[i] = v
+ i++
+ }
+ }
+ clearSlice(s[i:]) // zero/nil out the obsolete elements, for GC
+ return s[:i]
+}
+
+// Similar to the stdlib slices.Contains, except that we don't have
+// slices.Index so we need to use slices.IndexFunc for this non-Func helper.
+func slices_Contains[S ~[]E, E comparable](s S, v E) bool {
+ return slices_IndexFunc(s, func(e E) bool { return e == v }) >= 0
+}
+
+// Copied from the Go 1.24 stdlib implementation.
+func slices_Clone[S ~[]E, E any](s S) S {
+ // Preserve nil in case it matters.
+ if s == nil {
+ return nil
+ }
+ return append(S([]E{}), s...)
+}
+
+// Copied from the Go 1.24 stdlib implementation.
+func sync_OnceValue[T any](f func() T) func() T {
+ var (
+ once sync.Once
+ valid bool
+ p any
+ result T
+ )
+ g := func() {
+ defer func() {
+ p = recover()
+ if !valid {
+ panic(p)
+ }
+ }()
+ result = f()
+ f = nil
+ valid = true
+ }
+ return func() T {
+ once.Do(g)
+ if !valid {
+ panic(p)
+ }
+ return result
+ }
+}
+
+// Copied from the Go 1.24 stdlib implementation.
+func sync_OnceValues[T1, T2 any](f func() (T1, T2)) func() (T1, T2) {
+ var (
+ once sync.Once
+ valid bool
+ p any
+ r1 T1
+ r2 T2
+ )
+ g := func() {
+ defer func() {
+ p = recover()
+ if !valid {
+ panic(p)
+ }
+ }()
+ r1, r2 = f()
+ f = nil
+ valid = true
+ }
+ return func() (T1, T2) {
+ once.Do(g)
+ if !valid {
+ panic(p)
+ }
+ return r1, r2
+ }
+}
diff --git a/vendor/github.com/cyphar/filepath-securejoin/lookup_linux.go b/vendor/github.com/cyphar/filepath-securejoin/lookup_linux.go
index 290befa154..be81e498d7 100644
--- a/vendor/github.com/cyphar/filepath-securejoin/lookup_linux.go
+++ b/vendor/github.com/cyphar/filepath-securejoin/lookup_linux.go
@@ -12,7 +12,6 @@ import (
"os"
"path"
"path/filepath"
- "slices"
"strings"
"golang.org/x/sys/unix"
@@ -113,7 +112,7 @@ func (s *symlinkStack) push(dir *os.File, remainingPath, linkTarget string) erro
return nil
}
// Split the link target and clean up any "" parts.
- linkTargetParts := slices.DeleteFunc(
+ linkTargetParts := slices_DeleteFunc(
strings.Split(linkTarget, "/"),
func(part string) bool { return part == "" || part == "." })
diff --git a/vendor/github.com/cyphar/filepath-securejoin/mkdir_linux.go b/vendor/github.com/cyphar/filepath-securejoin/mkdir_linux.go
index b5f674524c..5e559bb7a8 100644
--- a/vendor/github.com/cyphar/filepath-securejoin/mkdir_linux.go
+++ b/vendor/github.com/cyphar/filepath-securejoin/mkdir_linux.go
@@ -11,7 +11,6 @@ import (
"fmt"
"os"
"path/filepath"
- "slices"
"strings"
"golang.org/x/sys/unix"
@@ -93,7 +92,7 @@ func MkdirAllHandle(root *os.File, unsafePath string, mode int) (_ *os.File, Err
}
remainingParts := strings.Split(remainingPath, string(filepath.Separator))
- if slices.Contains(remainingParts, "..") {
+ if slices_Contains(remainingParts, "..") {
// The path contained ".." components after the end of the "real"
// components. We could try to safely resolve ".." here but that would
// add a bunch of extra logic for something that it's not clear even
@@ -119,11 +118,20 @@ func MkdirAllHandle(root *os.File, unsafePath string, mode int) (_ *os.File, Err
// NOTE: mkdir(2) will not follow trailing symlinks, so we can safely
// create the final component without worrying about symlink-exchange
// attacks.
- if err := unix.Mkdirat(int(currentDir.Fd()), part, uint32(mode)); err != nil {
+ //
+ // If we get -EEXIST, it's possible that another program created the
+ // directory at the same time as us. In that case, just continue on as
+ // if we created it (if the created inode is not a directory, the
+ // following open call will fail).
+ if err := unix.Mkdirat(int(currentDir.Fd()), part, uint32(mode)); err != nil && !errors.Is(err, unix.EEXIST) {
err = &os.PathError{Op: "mkdirat", Path: currentDir.Name() + "/" + part, Err: err}
// Make the error a bit nicer if the directory is dead.
- if err2 := isDeadInode(currentDir); err2 != nil {
- err = fmt.Errorf("%w (%w)", err, err2)
+ if deadErr := isDeadInode(currentDir); deadErr != nil {
+ // TODO: Once we bump the minimum Go version to 1.20, we can use
+ // multiple %w verbs for this wrapping. For now we need to use a
+ // compatibility shim for older Go versions.
+ //err = fmt.Errorf("%w (%w)", err, deadErr)
+ err = wrapBaseError(err, deadErr)
}
return nil, err
}
diff --git a/vendor/github.com/cyphar/filepath-securejoin/openat2_linux.go b/vendor/github.com/cyphar/filepath-securejoin/openat2_linux.go
index ae3b381efe..f7a13e69ce 100644
--- a/vendor/github.com/cyphar/filepath-securejoin/openat2_linux.go
+++ b/vendor/github.com/cyphar/filepath-securejoin/openat2_linux.go
@@ -12,12 +12,11 @@ import (
"os"
"path/filepath"
"strings"
- "sync"
"golang.org/x/sys/unix"
)
-var hasOpenat2 = sync.OnceValue(func() bool {
+var hasOpenat2 = sync_OnceValue(func() bool {
fd, err := unix.Openat2(unix.AT_FDCWD, ".", &unix.OpenHow{
Flags: unix.O_PATH | unix.O_CLOEXEC,
Resolve: unix.RESOLVE_NO_SYMLINKS | unix.RESOLVE_IN_ROOT,
diff --git a/vendor/github.com/cyphar/filepath-securejoin/procfs_linux.go b/vendor/github.com/cyphar/filepath-securejoin/procfs_linux.go
index 8cc827d704..809a579cbd 100644
--- a/vendor/github.com/cyphar/filepath-securejoin/procfs_linux.go
+++ b/vendor/github.com/cyphar/filepath-securejoin/procfs_linux.go
@@ -12,7 +12,6 @@ import (
"os"
"runtime"
"strconv"
- "sync"
"golang.org/x/sys/unix"
)
@@ -54,7 +53,7 @@ func verifyProcRoot(procRoot *os.File) error {
return nil
}
-var hasNewMountApi = sync.OnceValue(func() bool {
+var hasNewMountApi = sync_OnceValue(func() bool {
// All of the pieces of the new mount API we use (fsopen, fsconfig,
// fsmount, open_tree) were added together in Linux 5.1[1,2], so we can
// just check for one of the syscalls and the others should also be
@@ -192,11 +191,11 @@ func doGetProcRoot() (*os.File, error) {
return procRoot, err
}
-var getProcRoot = sync.OnceValues(func() (*os.File, error) {
+var getProcRoot = sync_OnceValues(func() (*os.File, error) {
return doGetProcRoot()
})
-var hasProcThreadSelf = sync.OnceValue(func() bool {
+var hasProcThreadSelf = sync_OnceValue(func() bool {
return unix.Access("/proc/thread-self/", unix.F_OK) == nil
})
@@ -265,12 +264,20 @@ func procThreadSelf(procRoot *os.File, subpath string) (_ *os.File, _ procThread
Resolve: unix.RESOLVE_BENEATH | unix.RESOLVE_NO_XDEV | unix.RESOLVE_NO_MAGICLINKS,
})
if err != nil {
- return nil, nil, fmt.Errorf("%w: %w", errUnsafeProcfs, err)
+ // TODO: Once we bump the minimum Go version to 1.20, we can use
+ // multiple %w verbs for this wrapping. For now we need to use a
+ // compatibility shim for older Go versions.
+ //err = fmt.Errorf("%w: %w", errUnsafeProcfs, err)
+ return nil, nil, wrapBaseError(err, errUnsafeProcfs)
}
} else {
handle, err = openatFile(procRoot, threadSelf+subpath, unix.O_PATH|unix.O_NOFOLLOW|unix.O_CLOEXEC, 0)
if err != nil {
- return nil, nil, fmt.Errorf("%w: %w", errUnsafeProcfs, err)
+ // TODO: Once we bump the minimum Go version to 1.20, we can use
+ // multiple %w verbs for this wrapping. For now we need to use a
+ // compatibility shim for older Go versions.
+ //err = fmt.Errorf("%w: %w", errUnsafeProcfs, err)
+ return nil, nil, wrapBaseError(err, errUnsafeProcfs)
}
defer func() {
if Err != nil {
@@ -289,12 +296,17 @@ func procThreadSelf(procRoot *os.File, subpath string) (_ *os.File, _ procThread
return handle, runtime.UnlockOSThread, nil
}
-var hasStatxMountId = sync.OnceValue(func() bool {
+// STATX_MNT_ID_UNIQUE is provided in golang.org/x/sys@v0.20.0, but in order to
+// avoid bumping the requirement for a single constant we can just define it
+// ourselves.
+const STATX_MNT_ID_UNIQUE = 0x4000
+
+var hasStatxMountId = sync_OnceValue(func() bool {
var (
stx unix.Statx_t
// We don't care which mount ID we get. The kernel will give us the
// unique one if it is supported.
- wantStxMask uint32 = unix.STATX_MNT_ID_UNIQUE | unix.STATX_MNT_ID
+ wantStxMask uint32 = STATX_MNT_ID_UNIQUE | unix.STATX_MNT_ID
)
err := unix.Statx(-int(unix.EBADF), "/", 0, int(wantStxMask), &stx)
return err == nil && stx.Mask&wantStxMask != 0
@@ -310,7 +322,7 @@ func getMountId(dir *os.File, path string) (uint64, error) {
stx unix.Statx_t
// We don't care which mount ID we get. The kernel will give us the
// unique one if it is supported.
- wantStxMask uint32 = unix.STATX_MNT_ID_UNIQUE | unix.STATX_MNT_ID
+ wantStxMask uint32 = STATX_MNT_ID_UNIQUE | unix.STATX_MNT_ID
)
err := unix.Statx(int(dir.Fd()), path, unix.AT_EMPTY_PATH|unix.AT_SYMLINK_NOFOLLOW, int(wantStxMask), &stx)
diff --git a/vendor/github.com/go-git/go-git/v5/COMPATIBILITY.md b/vendor/github.com/go-git/go-git/v5/COMPATIBILITY.md
index ff0c22c896..ba1fb90ac5 100644
--- a/vendor/github.com/go-git/go-git/v5/COMPATIBILITY.md
+++ b/vendor/github.com/go-git/go-git/v5/COMPATIBILITY.md
@@ -11,7 +11,7 @@ compatibility status with go-git.
| `init` | `--bare` | ✅ | | |
| `init` | `--template`
`--separate-git-dir`
`--shared` | ❌ | | |
| `clone` | | ✅ | | - [PlainClone](_examples/clone/main.go) |
-| `clone` | Authentication:
- none
- access token
- username + password
- ssh | ✅ | | - [clone ssh](_examples/clone/auth/ssh/main.go)
- [clone access token](_examples/clone/auth/basic/access_token/main.go)
- [clone user + password](_examples/clone/auth/basic/username_password/main.go) |
+| `clone` | Authentication:
- none
- access token
- username + password
- ssh | ✅ | | - [clone ssh (private_key)](_examples/clone/auth/ssh/private_key/main.go)
- [clone ssh (ssh_agent)](_examples/clone/auth/ssh/ssh_agent/main.go)
- [clone access token](_examples/clone/auth/basic/access_token/main.go)
- [clone user + password](_examples/clone/auth/basic/username_password/main.go) |
| `clone` | `--progress`
`--single-branch`
`--depth`
`--origin`
`--recurse-submodules`
`--shared` | ✅ | | - [recurse submodules](_examples/clone/main.go)
- [progress](_examples/progress/main.go) |
## Basic snapshotting
@@ -34,6 +34,7 @@ compatibility status with go-git.
| `merge` | | ⚠️ (partial) | Fast-forward only | |
| `mergetool` | | ❌ | | |
| `stash` | | ❌ | | |
+| `sparse-checkout` | | ✅ | | - [sparse-checkout](_examples/sparse-checkout/main.go) |
| `tag` | | ✅ | | - [tag](_examples/tag/main.go)
- [tag create and push](_examples/tag-create-push/main.go) |
## Sharing and updating projects
diff --git a/vendor/github.com/go-git/go-git/v5/CONTRIBUTING.md b/vendor/github.com/go-git/go-git/v5/CONTRIBUTING.md
index fce25328a7..a5b01823bf 100644
--- a/vendor/github.com/go-git/go-git/v5/CONTRIBUTING.md
+++ b/vendor/github.com/go-git/go-git/v5/CONTRIBUTING.md
@@ -31,6 +31,13 @@ In order for a PR to be accepted it needs to pass a list of requirements:
- If the PR is a new feature, it has to come with a suite of unit tests, that tests the new functionality.
- In any case, all the PRs have to pass the personal evaluation of at least one of the maintainers of go-git.
+### Branches
+
+The `master` branch is currently used for maintaining the `v5` major release only. The accepted changes would
+be dependency bumps, bug fixes and small changes that aren't needed for `v6`. New development should target the
+`v6-exp` branch, and if agreed with at least one go-git maintainer, it can be back ported to `v5` by creating
+a new PR that targets `master`.
+
### Format of the commit message
Every commit message should describe what was changed, under which context and, if applicable, the GitHub issue it relates to:
diff --git a/vendor/github.com/go-git/go-git/v5/blame.go b/vendor/github.com/go-git/go-git/v5/blame.go
index 2a877dcdf9..e3cb39aec3 100644
--- a/vendor/github.com/go-git/go-git/v5/blame.go
+++ b/vendor/github.com/go-git/go-git/v5/blame.go
@@ -97,13 +97,10 @@ func Blame(c *object.Commit, path string) (*BlameResult, error) {
if err != nil {
return nil, err
}
- if finished == true {
+ if finished {
break
}
}
- if err != nil {
- return nil, err
- }
b.lineToCommit = make([]*object.Commit, finalLength)
for i := range needsMap {
@@ -309,8 +306,8 @@ func (b *blame) addBlames(curItems []*queueItem) (bool, error) {
for h := range hunks {
hLines := countLines(hunks[h].Text)
for hl := 0; hl < hLines; hl++ {
- switch {
- case hunks[h].Type == diffmatchpatch.DiffEqual:
+ switch hunks[h].Type {
+ case diffmatchpatch.DiffEqual:
prevl++
curl++
if curl == curItem.NeedsMap[need].Cur {
@@ -322,7 +319,7 @@ func (b *blame) addBlames(curItems []*queueItem) (bool, error) {
break out
}
}
- case hunks[h].Type == diffmatchpatch.DiffInsert:
+ case diffmatchpatch.DiffInsert:
curl++
if curl == curItem.NeedsMap[need].Cur {
// the line we want is added, it may have been added here (or by another parent), skip it for now
@@ -331,7 +328,7 @@ func (b *blame) addBlames(curItems []*queueItem) (bool, error) {
break out
}
}
- case hunks[h].Type == diffmatchpatch.DiffDelete:
+ case diffmatchpatch.DiffDelete:
prevl += hLines
continue out
default:
diff --git a/vendor/github.com/go-git/go-git/v5/config/config.go b/vendor/github.com/go-git/go-git/v5/config/config.go
index 6d41c15dcd..33f6e37d26 100644
--- a/vendor/github.com/go-git/go-git/v5/config/config.go
+++ b/vendor/github.com/go-git/go-git/v5/config/config.go
@@ -252,6 +252,7 @@ const (
extensionsSection = "extensions"
fetchKey = "fetch"
urlKey = "url"
+ pushurlKey = "pushurl"
bareKey = "bare"
worktreeKey = "worktree"
commentCharKey = "commentChar"
@@ -633,6 +634,7 @@ func (c *RemoteConfig) unmarshal(s *format.Subsection) error {
c.Name = c.raw.Name
c.URLs = append([]string(nil), c.raw.Options.GetAll(urlKey)...)
+ c.URLs = append(c.URLs, c.raw.Options.GetAll(pushurlKey)...)
c.Fetch = fetch
c.Mirror = c.raw.Options.Get(mirrorKey) == "true"
diff --git a/vendor/github.com/go-git/go-git/v5/internal/revision/scanner.go b/vendor/github.com/go-git/go-git/v5/internal/revision/scanner.go
index c46c21b795..2444f33ec2 100644
--- a/vendor/github.com/go-git/go-git/v5/internal/revision/scanner.go
+++ b/vendor/github.com/go-git/go-git/v5/internal/revision/scanner.go
@@ -43,6 +43,11 @@ func tokenizeExpression(ch rune, tokenType token, check runeCategoryValidator, r
return tokenType, string(data), nil
}
+// maxRevisionLength holds the maximum length that will be parsed for a
+// revision. Git itself doesn't enforce a max length, but rather leans on
+// the OS to enforce it via its ARG_MAX.
+const maxRevisionLength = 128 * 1024 // 128kb
+
var zeroRune = rune(0)
// scanner represents a lexical scanner.
@@ -52,7 +57,7 @@ type scanner struct {
// newScanner returns a new instance of scanner.
func newScanner(r io.Reader) *scanner {
- return &scanner{r: bufio.NewReader(r)}
+ return &scanner{r: bufio.NewReader(io.LimitReader(r, maxRevisionLength))}
}
// Scan extracts tokens and their strings counterpart
diff --git a/vendor/github.com/go-git/go-git/v5/options.go b/vendor/github.com/go-git/go-git/v5/options.go
index d7776dad5e..3cd0f952c3 100644
--- a/vendor/github.com/go-git/go-git/v5/options.go
+++ b/vendor/github.com/go-git/go-git/v5/options.go
@@ -416,6 +416,9 @@ type ResetOptions struct {
// the index (resetting it to the tree of Commit) and the working tree
// depending on Mode. If empty MixedReset is used.
Mode ResetMode
+ // Files, if not empty will constrain the reseting the index to only files
+ // specified in this list.
+ Files []string
}
// Validate validates the fields and sets the default values.
@@ -790,3 +793,26 @@ type PlainInitOptions struct {
// Validate validates the fields and sets the default values.
func (o *PlainInitOptions) Validate() error { return nil }
+
+var (
+ ErrNoRestorePaths = errors.New("you must specify path(s) to restore")
+)
+
+// RestoreOptions describes how a restore should be performed.
+type RestoreOptions struct {
+ // Marks to restore the content in the index
+ Staged bool
+ // Marks to restore the content of the working tree
+ Worktree bool
+ // List of file paths that will be restored
+ Files []string
+}
+
+// Validate validates the fields and sets the default values.
+func (o *RestoreOptions) Validate() error {
+ if len(o.Files) == 0 {
+ return ErrNoRestorePaths
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/format/gitignore/dir.go b/vendor/github.com/go-git/go-git/v5/plumbing/format/gitignore/dir.go
index aca5d0dbd2..92df5a3de7 100644
--- a/vendor/github.com/go-git/go-git/v5/plumbing/format/gitignore/dir.go
+++ b/vendor/github.com/go-git/go-git/v5/plumbing/format/gitignore/dir.go
@@ -64,6 +64,10 @@ func ReadPatterns(fs billy.Filesystem, path []string) (ps []Pattern, err error)
for _, fi := range fis {
if fi.IsDir() && fi.Name() != gitDir {
+ if NewMatcher(ps).Match(append(path, fi.Name()), true) {
+ continue
+ }
+
var subps []Pattern
subps, err = ReadPatterns(fs, append(path, fi.Name()))
if err != nil {
diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/format/index/decoder.go b/vendor/github.com/go-git/go-git/v5/plumbing/format/index/decoder.go
index 6778cf74ec..fc25d37022 100644
--- a/vendor/github.com/go-git/go-git/v5/plumbing/format/index/decoder.go
+++ b/vendor/github.com/go-git/go-git/v5/plumbing/format/index/decoder.go
@@ -24,8 +24,8 @@ var (
// ErrInvalidChecksum is returned by Decode if the SHA1 hash mismatch with
// the read content
ErrInvalidChecksum = errors.New("invalid checksum")
-
- errUnknownExtension = errors.New("unknown extension")
+ // ErrUnknownExtension is returned when an index extension is encountered that is considered mandatory
+ ErrUnknownExtension = errors.New("unknown extension")
)
const (
@@ -39,6 +39,7 @@ const (
// A Decoder reads and decodes index files from an input stream.
type Decoder struct {
+ buf *bufio.Reader
r io.Reader
hash hash.Hash
lastEntry *Entry
@@ -49,8 +50,10 @@ type Decoder struct {
// NewDecoder returns a new decoder that reads from r.
func NewDecoder(r io.Reader) *Decoder {
h := hash.New(hash.CryptoType)
+ buf := bufio.NewReader(r)
return &Decoder{
- r: io.TeeReader(r, h),
+ buf: buf,
+ r: io.TeeReader(buf, h),
hash: h,
extReader: bufio.NewReader(nil),
}
@@ -210,71 +213,75 @@ func (d *Decoder) readExtensions(idx *Index) error {
// count that they are not supported by jgit or libgit
var expected []byte
+ var peeked []byte
var err error
- var header [4]byte
+ // we should always be able to peek for 4 bytes (header) + 4 bytes (extlen) + final hash
+ // if this fails, we know that we're at the end of the index
+ peekLen := 4 + 4 + d.hash.Size()
+
for {
expected = d.hash.Sum(nil)
-
- var n int
- if n, err = io.ReadFull(d.r, header[:]); err != nil {
- if n == 0 {
- err = io.EOF
- }
-
+ peeked, err = d.buf.Peek(peekLen)
+ if len(peeked) < peekLen {
+ // there can't be an extension at this point, so let's bail out
break
}
+ if err != nil {
+ return err
+ }
- err = d.readExtension(idx, header[:])
+ err = d.readExtension(idx)
if err != nil {
- break
+ return err
}
}
- if err != errUnknownExtension {
+ return d.readChecksum(expected)
+}
+
+func (d *Decoder) readExtension(idx *Index) error {
+ var header [4]byte
+
+ if _, err := io.ReadFull(d.r, header[:]); err != nil {
return err
}
- return d.readChecksum(expected, header)
-}
+ r, err := d.getExtensionReader()
+ if err != nil {
+ return err
+ }
-func (d *Decoder) readExtension(idx *Index, header []byte) error {
switch {
- case bytes.Equal(header, treeExtSignature):
- r, err := d.getExtensionReader()
- if err != nil {
- return err
- }
-
+ case bytes.Equal(header[:], treeExtSignature):
idx.Cache = &Tree{}
d := &treeExtensionDecoder{r}
if err := d.Decode(idx.Cache); err != nil {
return err
}
- case bytes.Equal(header, resolveUndoExtSignature):
- r, err := d.getExtensionReader()
- if err != nil {
- return err
- }
-
+ case bytes.Equal(header[:], resolveUndoExtSignature):
idx.ResolveUndo = &ResolveUndo{}
d := &resolveUndoDecoder{r}
if err := d.Decode(idx.ResolveUndo); err != nil {
return err
}
- case bytes.Equal(header, endOfIndexEntryExtSignature):
- r, err := d.getExtensionReader()
- if err != nil {
- return err
- }
-
+ case bytes.Equal(header[:], endOfIndexEntryExtSignature):
idx.EndOfIndexEntry = &EndOfIndexEntry{}
d := &endOfIndexEntryDecoder{r}
if err := d.Decode(idx.EndOfIndexEntry); err != nil {
return err
}
default:
- return errUnknownExtension
+ // See https://git-scm.com/docs/index-format, which says:
+ // If the first byte is 'A'..'Z' the extension is optional and can be ignored.
+ if header[0] < 'A' || header[0] > 'Z' {
+ return ErrUnknownExtension
+ }
+
+ d := &unknownExtensionDecoder{r}
+ if err := d.Decode(); err != nil {
+ return err
+ }
}
return nil
@@ -290,11 +297,10 @@ func (d *Decoder) getExtensionReader() (*bufio.Reader, error) {
return d.extReader, nil
}
-func (d *Decoder) readChecksum(expected []byte, alreadyRead [4]byte) error {
+func (d *Decoder) readChecksum(expected []byte) error {
var h plumbing.Hash
- copy(h[:4], alreadyRead[:])
- if _, err := io.ReadFull(d.r, h[4:]); err != nil {
+ if _, err := io.ReadFull(d.r, h[:]); err != nil {
return err
}
@@ -476,3 +482,22 @@ func (d *endOfIndexEntryDecoder) Decode(e *EndOfIndexEntry) error {
_, err = io.ReadFull(d.r, e.Hash[:])
return err
}
+
+type unknownExtensionDecoder struct {
+ r *bufio.Reader
+}
+
+func (d *unknownExtensionDecoder) Decode() error {
+ var buf [1024]byte
+
+ for {
+ _, err := d.r.Read(buf[:])
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/format/index/encoder.go b/vendor/github.com/go-git/go-git/v5/plumbing/format/index/encoder.go
index fa2d814454..c232e03231 100644
--- a/vendor/github.com/go-git/go-git/v5/plumbing/format/index/encoder.go
+++ b/vendor/github.com/go-git/go-git/v5/plumbing/format/index/encoder.go
@@ -3,8 +3,11 @@ package index
import (
"bytes"
"errors"
+ "fmt"
"io"
+ "path"
"sort"
+ "strings"
"time"
"github.com/go-git/go-git/v5/plumbing/hash"
@@ -13,7 +16,7 @@ import (
var (
// EncodeVersionSupported is the range of supported index versions
- EncodeVersionSupported uint32 = 3
+ EncodeVersionSupported uint32 = 4
// ErrInvalidTimestamp is returned by Encode if a Index with a Entry with
// negative timestamp values
@@ -22,20 +25,25 @@ var (
// An Encoder writes an Index to an output stream.
type Encoder struct {
- w io.Writer
- hash hash.Hash
+ w io.Writer
+ hash hash.Hash
+ lastEntry *Entry
}
// NewEncoder returns a new encoder that writes to w.
func NewEncoder(w io.Writer) *Encoder {
h := hash.New(hash.CryptoType)
mw := io.MultiWriter(w, h)
- return &Encoder{mw, h}
+ return &Encoder{mw, h, nil}
}
// Encode writes the Index to the stream of the encoder.
func (e *Encoder) Encode(idx *Index) error {
- // TODO: support v4
+ return e.encode(idx, true)
+}
+
+func (e *Encoder) encode(idx *Index, footer bool) error {
+
// TODO: support extensions
if idx.Version > EncodeVersionSupported {
return ErrUnsupportedVersion
@@ -49,7 +57,10 @@ func (e *Encoder) Encode(idx *Index) error {
return err
}
- return e.encodeFooter()
+ if footer {
+ return e.encodeFooter()
+ }
+ return nil
}
func (e *Encoder) encodeHeader(idx *Index) error {
@@ -64,7 +75,7 @@ func (e *Encoder) encodeEntries(idx *Index) error {
sort.Sort(byName(idx.Entries))
for _, entry := range idx.Entries {
- if err := e.encodeEntry(entry); err != nil {
+ if err := e.encodeEntry(idx, entry); err != nil {
return err
}
entryLength := entryHeaderLength
@@ -73,7 +84,7 @@ func (e *Encoder) encodeEntries(idx *Index) error {
}
wrote := entryLength + len(entry.Name)
- if err := e.padEntry(wrote); err != nil {
+ if err := e.padEntry(idx, wrote); err != nil {
return err
}
}
@@ -81,7 +92,7 @@ func (e *Encoder) encodeEntries(idx *Index) error {
return nil
}
-func (e *Encoder) encodeEntry(entry *Entry) error {
+func (e *Encoder) encodeEntry(idx *Index, entry *Entry) error {
sec, nsec, err := e.timeToUint32(&entry.CreatedAt)
if err != nil {
return err
@@ -132,9 +143,68 @@ func (e *Encoder) encodeEntry(entry *Entry) error {
return err
}
+ switch idx.Version {
+ case 2, 3:
+ err = e.encodeEntryName(entry)
+ case 4:
+ err = e.encodeEntryNameV4(entry)
+ default:
+ err = ErrUnsupportedVersion
+ }
+
+ return err
+}
+
+func (e *Encoder) encodeEntryName(entry *Entry) error {
return binary.Write(e.w, []byte(entry.Name))
}
+func (e *Encoder) encodeEntryNameV4(entry *Entry) error {
+ name := entry.Name
+ l := 0
+ if e.lastEntry != nil {
+ dir := path.Dir(e.lastEntry.Name) + "/"
+ if strings.HasPrefix(entry.Name, dir) {
+ l = len(e.lastEntry.Name) - len(dir)
+ name = strings.TrimPrefix(entry.Name, dir)
+ } else {
+ l = len(e.lastEntry.Name)
+ }
+ }
+
+ e.lastEntry = entry
+
+ err := binary.WriteVariableWidthInt(e.w, int64(l))
+ if err != nil {
+ return err
+ }
+
+ return binary.Write(e.w, []byte(name+string('\x00')))
+}
+
+func (e *Encoder) encodeRawExtension(signature string, data []byte) error {
+ if len(signature) != 4 {
+ return fmt.Errorf("invalid signature length")
+ }
+
+ _, err := e.w.Write([]byte(signature))
+ if err != nil {
+ return err
+ }
+
+ err = binary.WriteUint32(e.w, uint32(len(data)))
+ if err != nil {
+ return err
+ }
+
+ _, err = e.w.Write(data)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
func (e *Encoder) timeToUint32(t *time.Time) (uint32, uint32, error) {
if t.IsZero() {
return 0, 0, nil
@@ -147,7 +217,11 @@ func (e *Encoder) timeToUint32(t *time.Time) (uint32, uint32, error) {
return uint32(t.Unix()), uint32(t.Nanosecond()), nil
}
-func (e *Encoder) padEntry(wrote int) error {
+func (e *Encoder) padEntry(idx *Index, wrote int) error {
+ if idx.Version == 4 {
+ return nil
+ }
+
padLen := 8 - wrote%8
_, err := e.w.Write(bytes.Repeat([]byte{'\x00'}, padLen))
diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/delta_index.go b/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/delta_index.go
index 07a61120e5..a60ec0b24d 100644
--- a/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/delta_index.go
+++ b/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/delta_index.go
@@ -32,19 +32,17 @@ func (idx *deltaIndex) findMatch(src, tgt []byte, tgtOffset int) (srcOffset, l i
return 0, -1
}
- if len(tgt) >= tgtOffset+s && len(src) >= blksz {
- h := hashBlock(tgt, tgtOffset)
- tIdx := h & idx.mask
- eIdx := idx.table[tIdx]
- if eIdx != 0 {
- srcOffset = idx.entries[eIdx]
- } else {
- return
- }
-
- l = matchLength(src, tgt, tgtOffset, srcOffset)
+ h := hashBlock(tgt, tgtOffset)
+ tIdx := h & idx.mask
+ eIdx := idx.table[tIdx]
+ if eIdx == 0 {
+ return
}
+ srcOffset = idx.entries[eIdx]
+
+ l = matchLength(src, tgt, tgtOffset, srcOffset)
+
return
}
diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/patch_delta.go b/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/patch_delta.go
index 960769c7c8..a9c6b9b56f 100644
--- a/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/patch_delta.go
+++ b/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/patch_delta.go
@@ -26,6 +26,13 @@ var (
const (
payload = 0x7f // 0111 1111
continuation = 0x80 // 1000 0000
+
+ // maxPatchPreemptionSize defines what is the max size of bytes to be
+ // premptively made available for a patch operation.
+ maxPatchPreemptionSize uint = 65536
+
+ // minDeltaSize defines the smallest size for a delta.
+ minDeltaSize = 4
)
type offset struct {
@@ -86,9 +93,13 @@ func ApplyDelta(target, base plumbing.EncodedObject, delta []byte) (err error) {
}
// PatchDelta returns the result of applying the modification deltas in delta to src.
-// An error will be returned if delta is corrupted (ErrDeltaLen) or an action command
+// An error will be returned if delta is corrupted (ErrInvalidDelta) or an action command
// is not copy from source or copy from delta (ErrDeltaCmd).
func PatchDelta(src, delta []byte) ([]byte, error) {
+ if len(src) == 0 || len(delta) < minDeltaSize {
+ return nil, ErrInvalidDelta
+ }
+
b := &bytes.Buffer{}
if err := patchDelta(b, src, delta); err != nil {
return nil, err
@@ -239,7 +250,9 @@ func patchDelta(dst *bytes.Buffer, src, delta []byte) error {
remainingTargetSz := targetSz
var cmd byte
- dst.Grow(int(targetSz))
+
+ growSz := min(targetSz, maxPatchPreemptionSize)
+ dst.Grow(int(growSz))
for {
if len(delta) == 0 {
return ErrInvalidDelta
@@ -403,6 +416,10 @@ func patchDeltaWriter(dst io.Writer, base io.ReaderAt, delta io.Reader,
// This must be called twice on the delta data buffer, first to get the
// expected source buffer size, and again to get the target buffer size.
func decodeLEB128(input []byte) (uint, []byte) {
+ if len(input) == 0 {
+ return 0, input
+ }
+
var num, sz uint
var b byte
for {
diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/format/pktline/scanner.go b/vendor/github.com/go-git/go-git/v5/plumbing/format/pktline/scanner.go
index fbb137de06..706d984ee0 100644
--- a/vendor/github.com/go-git/go-git/v5/plumbing/format/pktline/scanner.go
+++ b/vendor/github.com/go-git/go-git/v5/plumbing/format/pktline/scanner.go
@@ -140,6 +140,8 @@ func asciiHexToByte(b byte) (byte, error) {
return b - '0', nil
case b >= 'a' && b <= 'f':
return b - 'a' + 10, nil
+ case b >= 'A' && b <= 'F':
+ return b - 'A' + 10, nil
default:
return 0, ErrInvalidPktLen
}
diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/object/signature.go b/vendor/github.com/go-git/go-git/v5/plumbing/object/signature.go
index 91cf371f0c..f9c3d306bd 100644
--- a/vendor/github.com/go-git/go-git/v5/plumbing/object/signature.go
+++ b/vendor/github.com/go-git/go-git/v5/plumbing/object/signature.go
@@ -19,6 +19,7 @@ var (
// a PKCS#7 (S/MIME) signature.
x509SignatureFormat = signatureFormat{
[]byte("-----BEGIN CERTIFICATE-----"),
+ []byte("-----BEGIN SIGNED MESSAGE-----"),
}
// sshSignatureFormat is the format of an SSH signature.
diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/object/tree.go b/vendor/github.com/go-git/go-git/v5/plumbing/object/tree.go
index 0fd0e51398..2e1b789156 100644
--- a/vendor/github.com/go-git/go-git/v5/plumbing/object/tree.go
+++ b/vendor/github.com/go-git/go-git/v5/plumbing/object/tree.go
@@ -295,6 +295,7 @@ func (s TreeEntrySorter) Swap(i, j int) {
}
// Encode transforms a Tree into a plumbing.EncodedObject.
+// The tree entries must be sorted by name.
func (t *Tree) Encode(o plumbing.EncodedObject) (err error) {
o.SetType(plumbing.TreeObject)
w, err := o.Writer()
diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/filter.go b/vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/filter.go
new file mode 100644
index 0000000000..145fc711ca
--- /dev/null
+++ b/vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/filter.go
@@ -0,0 +1,76 @@
+package packp
+
+import (
+ "errors"
+ "fmt"
+ "github.com/go-git/go-git/v5/plumbing"
+ "net/url"
+ "strings"
+)
+
+var ErrUnsupportedObjectFilterType = errors.New("unsupported object filter type")
+
+// Filter values enable the partial clone capability which causes
+// the server to omit objects that match the filter.
+//
+// See [Git's documentation] for more details.
+//
+// [Git's documentation]: https://github.com/git/git/blob/e02ecfcc534e2021aae29077a958dd11c3897e4c/Documentation/rev-list-options.txt#L948
+type Filter string
+
+type BlobLimitPrefix string
+
+const (
+ BlobLimitPrefixNone BlobLimitPrefix = ""
+ BlobLimitPrefixKibi BlobLimitPrefix = "k"
+ BlobLimitPrefixMebi BlobLimitPrefix = "m"
+ BlobLimitPrefixGibi BlobLimitPrefix = "g"
+)
+
+// FilterBlobNone omits all blobs.
+func FilterBlobNone() Filter {
+ return "blob:none"
+}
+
+// FilterBlobLimit omits blobs of size at least n bytes (when prefix is
+// BlobLimitPrefixNone), n kibibytes (when prefix is BlobLimitPrefixKibi),
+// n mebibytes (when prefix is BlobLimitPrefixMebi) or n gibibytes (when
+// prefix is BlobLimitPrefixGibi). n can be zero, in which case all blobs
+// will be omitted.
+func FilterBlobLimit(n uint64, prefix BlobLimitPrefix) Filter {
+ return Filter(fmt.Sprintf("blob:limit=%d%s", n, prefix))
+}
+
+// FilterTreeDepth omits all blobs and trees whose depth from the root tree
+// is larger or equal to depth.
+func FilterTreeDepth(depth uint64) Filter {
+ return Filter(fmt.Sprintf("tree:%d", depth))
+}
+
+// FilterObjectType omits all objects which are not of the requested type t.
+// Supported types are TagObject, CommitObject, TreeObject and BlobObject.
+func FilterObjectType(t plumbing.ObjectType) (Filter, error) {
+ switch t {
+ case plumbing.TagObject:
+ fallthrough
+ case plumbing.CommitObject:
+ fallthrough
+ case plumbing.TreeObject:
+ fallthrough
+ case plumbing.BlobObject:
+ return Filter(fmt.Sprintf("object:type=%s", t.String())), nil
+ default:
+ return "", fmt.Errorf("%w: %s", ErrUnsupportedObjectFilterType, t.String())
+ }
+}
+
+// FilterCombine combines multiple Filter values together.
+func FilterCombine(filters ...Filter) Filter {
+ var escapedFilters []string
+
+ for _, filter := range filters {
+ escapedFilters = append(escapedFilters, url.QueryEscape(string(filter)))
+ }
+
+ return Filter(fmt.Sprintf("combine:%s", strings.Join(escapedFilters, "+")))
+}
diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/sideband/demux.go b/vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/sideband/demux.go
index 0116f962ef..01d95a3aba 100644
--- a/vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/sideband/demux.go
+++ b/vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/sideband/demux.go
@@ -114,7 +114,7 @@ func (d *Demuxer) nextPackData() ([]byte, error) {
size := len(content)
if size == 0 {
- return nil, nil
+ return nil, io.EOF
} else if size > d.max {
return nil, ErrMaxPackedExceeded
}
diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/srvresp.go b/vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/srvresp.go
index a9ddb538b2..d760ad6609 100644
--- a/vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/srvresp.go
+++ b/vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/srvresp.go
@@ -120,6 +120,9 @@ func (r *ServerResponse) decodeACKLine(line []byte) error {
}
sp := bytes.Index(line, []byte(" "))
+ if sp+41 > len(line) {
+ return fmt.Errorf("malformed ACK %q", line)
+ }
h := plumbing.NewHash(string(line[sp+1 : sp+41]))
r.ACKs = append(r.ACKs, h)
return nil
diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/ulreq.go b/vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/ulreq.go
index 344f8c7e3a..ef4e08a10a 100644
--- a/vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/ulreq.go
+++ b/vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/ulreq.go
@@ -17,6 +17,7 @@ type UploadRequest struct {
Wants []plumbing.Hash
Shallows []plumbing.Hash
Depth Depth
+ Filter Filter
}
// Depth values stores the desired depth of the requested packfile: see
diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/ulreq_encode.go b/vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/ulreq_encode.go
index c451e23164..8b19c0f674 100644
--- a/vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/ulreq_encode.go
+++ b/vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/ulreq_encode.go
@@ -132,6 +132,17 @@ func (e *ulReqEncoder) encodeDepth() stateFn {
return nil
}
+ return e.encodeFilter
+}
+
+func (e *ulReqEncoder) encodeFilter() stateFn {
+ if filter := e.data.Filter; filter != "" {
+ if err := e.pe.Encodef("filter %s\n", filter); err != nil {
+ e.err = fmt.Errorf("encoding filter %s: %s", filter, err)
+ return nil
+ }
+ }
+
return e.encodeFlush
}
diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/reference.go b/vendor/github.com/go-git/go-git/v5/plumbing/reference.go
index ddba930292..4daa341649 100644
--- a/vendor/github.com/go-git/go-git/v5/plumbing/reference.go
+++ b/vendor/github.com/go-git/go-git/v5/plumbing/reference.go
@@ -188,7 +188,7 @@ func (r ReferenceName) Validate() error {
isBranch := r.IsBranch()
isTag := r.IsTag()
- for _, part := range parts {
+ for i, part := range parts {
// rule 6
if len(part) == 0 {
return ErrInvalidReferenceName
@@ -205,7 +205,7 @@ func (r ReferenceName) Validate() error {
return ErrInvalidReferenceName
}
- if (isBranch || isTag) && strings.HasPrefix(part, "-") { // branches & tags can't start with -
+ if (isBranch || isTag) && strings.HasPrefix(part, "-") && (i == 2) { // branches & tags can't start with -
return ErrInvalidReferenceName
}
}
diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/transport/common.go b/vendor/github.com/go-git/go-git/v5/plumbing/transport/common.go
index b05437fbfc..fae1aa98ca 100644
--- a/vendor/github.com/go-git/go-git/v5/plumbing/transport/common.go
+++ b/vendor/github.com/go-git/go-git/v5/plumbing/transport/common.go
@@ -19,6 +19,7 @@ import (
"fmt"
"io"
"net/url"
+ "path/filepath"
"strconv"
"strings"
@@ -295,7 +296,11 @@ func parseFile(endpoint string) (*Endpoint, bool) {
return nil, false
}
- path := endpoint
+ path, err := filepath.Abs(endpoint)
+ if err != nil {
+ return nil, false
+ }
+
return &Endpoint{
Protocol: "file",
Path: path,
diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/transport/file/client.go b/vendor/github.com/go-git/go-git/v5/plumbing/transport/file/client.go
index 38714e2ad1..d921d0a5a4 100644
--- a/vendor/github.com/go-git/go-git/v5/plumbing/transport/file/client.go
+++ b/vendor/github.com/go-git/go-git/v5/plumbing/transport/file/client.go
@@ -7,6 +7,7 @@ import (
"io"
"os"
"path/filepath"
+ "runtime"
"strings"
"github.com/go-git/go-git/v5/plumbing/transport"
@@ -95,7 +96,23 @@ func (r *runner) Command(cmd string, ep *transport.Endpoint, auth transport.Auth
}
}
- return &command{cmd: execabs.Command(cmd, ep.Path)}, nil
+ return &command{cmd: execabs.Command(cmd, adjustPathForWindows(ep.Path))}, nil
+}
+
+func isDriveLetter(c byte) bool {
+ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
+}
+
+// On Windows, the path that results from a file: URL has a leading slash. This
+// has to be removed if there's a drive letter
+func adjustPathForWindows(p string) string {
+ if runtime.GOOS != "windows" {
+ return p
+ }
+ if len(p) >= 3 && p[0] == '/' && isDriveLetter(p[1]) && p[2] == ':' {
+ return p[1:]
+ }
+ return p
}
type command struct {
diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/transport/http/common.go b/vendor/github.com/go-git/go-git/v5/plumbing/transport/http/common.go
index 1c4ceee68d..120008db1c 100644
--- a/vendor/github.com/go-git/go-git/v5/plumbing/transport/http/common.go
+++ b/vendor/github.com/go-git/go-git/v5/plumbing/transport/http/common.go
@@ -430,11 +430,11 @@ func NewErr(r *http.Response) error {
switch r.StatusCode {
case http.StatusUnauthorized:
- return transport.ErrAuthenticationRequired
+ return fmt.Errorf("%w: %s", transport.ErrAuthenticationRequired, reason)
case http.StatusForbidden:
- return transport.ErrAuthorizationFailed
+ return fmt.Errorf("%w: %s", transport.ErrAuthorizationFailed, reason)
case http.StatusNotFound:
- return transport.ErrRepositoryNotFound
+ return fmt.Errorf("%w: %s", transport.ErrRepositoryNotFound, reason)
}
return plumbing.NewUnexpectedError(&Err{r, reason})
diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/transport/server/loader.go b/vendor/github.com/go-git/go-git/v5/plumbing/transport/server/loader.go
index e7e2b075e5..f03a91c6d1 100644
--- a/vendor/github.com/go-git/go-git/v5/plumbing/transport/server/loader.go
+++ b/vendor/github.com/go-git/go-git/v5/plumbing/transport/server/loader.go
@@ -40,8 +40,16 @@ func (l *fsLoader) Load(ep *transport.Endpoint) (storer.Storer, error) {
return nil, err
}
- if _, err := fs.Stat("config"); err != nil {
- return nil, transport.ErrRepositoryNotFound
+ var bare bool
+ if _, err := fs.Stat("config"); err == nil {
+ bare = true
+ }
+
+ if !bare {
+ // do not use git.GitDirName due to import cycle
+ if _, err := fs.Stat(".git"); err != nil {
+ return nil, transport.ErrRepositoryNotFound
+ }
}
return filesystem.NewStorage(fs, cache.NewObjectLRUDefault()), nil
diff --git a/vendor/github.com/go-git/go-git/v5/remote.go b/vendor/github.com/go-git/go-git/v5/remote.go
index 7cc0db9b7d..e2c734e751 100644
--- a/vendor/github.com/go-git/go-git/v5/remote.go
+++ b/vendor/github.com/go-git/go-git/v5/remote.go
@@ -9,6 +9,7 @@ import (
"time"
"github.com/go-git/go-billy/v5/osfs"
+
"github.com/go-git/go-git/v5/config"
"github.com/go-git/go-git/v5/internal/url"
"github.com/go-git/go-git/v5/plumbing"
@@ -82,7 +83,7 @@ func (r *Remote) String() string {
var fetch, push string
if len(r.c.URLs) > 0 {
fetch = r.c.URLs[0]
- push = r.c.URLs[0]
+ push = r.c.URLs[len(r.c.URLs)-1]
}
return fmt.Sprintf("%s\t%s (fetch)\n%[1]s\t%[3]s (push)", r.c.Name, fetch, push)
@@ -109,8 +110,8 @@ func (r *Remote) PushContext(ctx context.Context, o *PushOptions) (err error) {
return fmt.Errorf("remote names don't match: %s != %s", o.RemoteName, r.c.Name)
}
- if o.RemoteURL == "" {
- o.RemoteURL = r.c.URLs[0]
+ if o.RemoteURL == "" && len(r.c.URLs) > 0 {
+ o.RemoteURL = r.c.URLs[len(r.c.URLs)-1]
}
s, err := newSendPackSession(o.RemoteURL, o.Auth, o.InsecureSkipTLS, o.CABundle, o.ProxyOptions)
@@ -491,7 +492,18 @@ func (r *Remote) fetch(ctx context.Context, o *FetchOptions) (sto storer.Referen
}
if !updated && !updatedPrune {
- return remoteRefs, NoErrAlreadyUpToDate
+ // No references updated, but may have fetched new objects, check if we now have any of our wants
+ for _, hash := range req.Wants {
+ exists, _ := objectExists(r.s, hash)
+ if exists {
+ updated = true
+ break
+ }
+ }
+
+ if !updated {
+ return remoteRefs, NoErrAlreadyUpToDate
+ }
}
return remoteRefs, nil
@@ -878,17 +890,12 @@ func getHavesFromRef(
return nil
}
- // No need to load the commit if we know the remote already
- // has this hash.
- if remoteRefs[h] {
- haves[h] = true
- return nil
- }
-
commit, err := object.GetCommit(s, h)
if err != nil {
- // Ignore the error if this isn't a commit.
- haves[ref.Hash()] = true
+ if !errors.Is(err, plumbing.ErrObjectNotFound) {
+ // Ignore the error if this isn't a commit.
+ haves[ref.Hash()] = true
+ }
return nil
}
diff --git a/vendor/github.com/go-git/go-git/v5/repository.go b/vendor/github.com/go-git/go-git/v5/repository.go
index a57c7141f8..200098e7a0 100644
--- a/vendor/github.com/go-git/go-git/v5/repository.go
+++ b/vendor/github.com/go-git/go-git/v5/repository.go
@@ -956,7 +956,7 @@ func (r *Repository) clone(ctx context.Context, o *CloneOptions) error {
}
if o.RecurseSubmodules != NoRecurseSubmodules {
- if err := w.updateSubmodules(&SubmoduleUpdateOptions{
+ if err := w.updateSubmodules(ctx, &SubmoduleUpdateOptions{
RecurseSubmodules: o.RecurseSubmodules,
Depth: func() int {
if o.ShallowSubmodules {
@@ -1037,7 +1037,7 @@ func (r *Repository) setIsBare(isBare bool) error {
return r.Storer.SetConfig(cfg)
}
-func (r *Repository) updateRemoteConfigIfNeeded(o *CloneOptions, c *config.RemoteConfig, head *plumbing.Reference) error {
+func (r *Repository) updateRemoteConfigIfNeeded(o *CloneOptions, c *config.RemoteConfig, _ *plumbing.Reference) error {
if !o.SingleBranch {
return nil
}
diff --git a/vendor/github.com/go-git/go-git/v5/status.go b/vendor/github.com/go-git/go-git/v5/status.go
index 7f18e02278..d14f7e6572 100644
--- a/vendor/github.com/go-git/go-git/v5/status.go
+++ b/vendor/github.com/go-git/go-git/v5/status.go
@@ -4,6 +4,9 @@ import (
"bytes"
"fmt"
"path/filepath"
+
+ mindex "github.com/go-git/go-git/v5/utils/merkletrie/index"
+ "github.com/go-git/go-git/v5/utils/merkletrie/noder"
)
// Status represents the current status of a Worktree.
@@ -77,3 +80,69 @@ const (
Copied StatusCode = 'C'
UpdatedButUnmerged StatusCode = 'U'
)
+
+// StatusStrategy defines the different types of strategies when processing
+// the worktree status.
+type StatusStrategy int
+
+const (
+ // TODO: (V6) Review the default status strategy.
+ // TODO: (V6) Review the type used to represent Status, to enable lazy
+ // processing of statuses going direct to the backing filesystem.
+ defaultStatusStrategy = Empty
+
+ // Empty starts its status map from empty. Missing entries for a given
+ // path means that the file is untracked. This causes a known issue (#119)
+ // whereby unmodified files can be incorrectly reported as untracked.
+ //
+ // This can be used when returning the changed state within a modified Worktree.
+ // For example, to check whether the current worktree is clean.
+ Empty StatusStrategy = 0
+ // Preload goes through all existing nodes from the index and add them to the
+ // status map as unmodified. This is currently the most reliable strategy
+ // although it comes at a performance cost in large repositories.
+ //
+ // This method is recommended when fetching the status of unmodified files.
+ // For example, to confirm the status of a specific file that is either
+ // untracked or unmodified.
+ Preload StatusStrategy = 1
+)
+
+func (s StatusStrategy) new(w *Worktree) (Status, error) {
+ switch s {
+ case Preload:
+ return preloadStatus(w)
+ case Empty:
+ return make(Status), nil
+ }
+ return nil, fmt.Errorf("%w: %+v", ErrUnsupportedStatusStrategy, s)
+}
+
+func preloadStatus(w *Worktree) (Status, error) {
+ idx, err := w.r.Storer.Index()
+ if err != nil {
+ return nil, err
+ }
+
+ idxRoot := mindex.NewRootNode(idx)
+ nodes := []noder.Noder{idxRoot}
+
+ status := make(Status)
+ for len(nodes) > 0 {
+ var node noder.Noder
+ node, nodes = nodes[0], nodes[1:]
+ if node.IsDir() {
+ children, err := node.Children()
+ if err != nil {
+ return nil, err
+ }
+ nodes = append(nodes, children...)
+ continue
+ }
+ fs := status.File(node.Name())
+ fs.Worktree = Unmodified
+ fs.Staging = Unmodified
+ }
+
+ return status, nil
+}
diff --git a/vendor/github.com/go-git/go-git/v5/storage/filesystem/dotgit/dotgit.go b/vendor/github.com/go-git/go-git/v5/storage/filesystem/dotgit/dotgit.go
index 31c4694816..72c9ccfc14 100644
--- a/vendor/github.com/go-git/go-git/v5/storage/filesystem/dotgit/dotgit.go
+++ b/vendor/github.com/go-git/go-git/v5/storage/filesystem/dotgit/dotgit.go
@@ -72,6 +72,9 @@ var (
// ErrIsDir is returned when a reference file is attempting to be read,
// but the path specified is a directory.
ErrIsDir = errors.New("reference path is a directory")
+ // ErrEmptyRefFile is returned when a reference file is attempted to be read,
+ // but the file is empty
+ ErrEmptyRefFile = errors.New("ref file is empty")
)
// Options holds configuration for the storage.
@@ -249,7 +252,7 @@ func (d *DotGit) objectPacks() ([]plumbing.Hash, error) {
continue
}
- h := plumbing.NewHash(n[5 : len(n)-5]) //pack-(hash).pack
+ h := plumbing.NewHash(n[5 : len(n)-5]) // pack-(hash).pack
if h.IsZero() {
// Ignore files with badly-formatted names.
continue
@@ -661,18 +664,33 @@ func (d *DotGit) readReferenceFrom(rd io.Reader, name string) (ref *plumbing.Ref
return nil, err
}
+ if len(b) == 0 {
+ return nil, ErrEmptyRefFile
+ }
+
line := strings.TrimSpace(string(b))
return plumbing.NewReferenceFromStrings(name, line), nil
}
+// checkReferenceAndTruncate reads the reference from the given file, or the `pack-refs` file if
+// the file was empty. Then it checks that the old reference matches the stored reference and
+// truncates the file.
func (d *DotGit) checkReferenceAndTruncate(f billy.File, old *plumbing.Reference) error {
if old == nil {
return nil
}
+
ref, err := d.readReferenceFrom(f, old.Name().String())
+ if errors.Is(err, ErrEmptyRefFile) {
+ // This may happen if the reference is being read from a newly created file.
+ // In that case, try getting the reference from the packed refs file.
+ ref, err = d.packedRef(old.Name())
+ }
+
if err != nil {
return err
}
+
if ref.Hash() != old.Hash() {
return storage.ErrReferenceHasChanged
}
@@ -701,16 +719,16 @@ func (d *DotGit) SetRef(r, old *plumbing.Reference) error {
// Symbolic references are resolved and included in the output.
func (d *DotGit) Refs() ([]*plumbing.Reference, error) {
var refs []*plumbing.Reference
- var seen = make(map[plumbing.ReferenceName]bool)
- if err := d.addRefsFromRefDir(&refs, seen); err != nil {
+ seen := make(map[plumbing.ReferenceName]bool)
+ if err := d.addRefFromHEAD(&refs); err != nil {
return nil, err
}
- if err := d.addRefsFromPackedRefs(&refs, seen); err != nil {
+ if err := d.addRefsFromRefDir(&refs, seen); err != nil {
return nil, err
}
- if err := d.addRefFromHEAD(&refs); err != nil {
+ if err := d.addRefsFromPackedRefs(&refs, seen); err != nil {
return nil, err
}
@@ -815,7 +833,8 @@ func (d *DotGit) addRefsFromPackedRefsFile(refs *[]*plumbing.Reference, f billy.
}
func (d *DotGit) openAndLockPackedRefs(doCreate bool) (
- pr billy.File, err error) {
+ pr billy.File, err error,
+) {
var f billy.File
defer func() {
if err != nil && f != nil {
@@ -1020,7 +1039,7 @@ func (d *DotGit) readReferenceFile(path, name string) (ref *plumbing.Reference,
func (d *DotGit) CountLooseRefs() (int, error) {
var refs []*plumbing.Reference
- var seen = make(map[plumbing.ReferenceName]bool)
+ seen := make(map[plumbing.ReferenceName]bool)
if err := d.addRefsFromRefDir(&refs, seen); err != nil {
return 0, err
}
diff --git a/vendor/github.com/go-git/go-git/v5/storage/filesystem/index.go b/vendor/github.com/go-git/go-git/v5/storage/filesystem/index.go
index a19176f83d..a86ef3e2e5 100644
--- a/vendor/github.com/go-git/go-git/v5/storage/filesystem/index.go
+++ b/vendor/github.com/go-git/go-git/v5/storage/filesystem/index.go
@@ -48,7 +48,7 @@ func (s *IndexStorage) Index() (i *index.Index, err error) {
defer ioutil.CheckClose(f, &err)
- d := index.NewDecoder(bufio.NewReader(f))
+ d := index.NewDecoder(f)
err = d.Decode(idx)
return idx, err
}
diff --git a/vendor/github.com/go-git/go-git/v5/storage/filesystem/object.go b/vendor/github.com/go-git/go-git/v5/storage/filesystem/object.go
index e812fe934d..91b4aceae1 100644
--- a/vendor/github.com/go-git/go-git/v5/storage/filesystem/object.go
+++ b/vendor/github.com/go-git/go-git/v5/storage/filesystem/object.go
@@ -431,13 +431,13 @@ func (s *ObjectStorage) getFromUnpacked(h plumbing.Hash) (obj plumbing.EncodedOb
defer ioutil.CheckClose(w, &err)
- s.objectCache.Put(obj)
-
bufp := copyBufferPool.Get().(*[]byte)
buf := *bufp
_, err = io.CopyBuffer(w, r, buf)
copyBufferPool.Put(bufp)
+ s.objectCache.Put(obj)
+
return obj, err
}
diff --git a/vendor/github.com/go-git/go-git/v5/submodule.go b/vendor/github.com/go-git/go-git/v5/submodule.go
index 84f020dc72..afabb6acad 100644
--- a/vendor/github.com/go-git/go-git/v5/submodule.go
+++ b/vendor/github.com/go-git/go-git/v5/submodule.go
@@ -214,10 +214,10 @@ func (s *Submodule) update(ctx context.Context, o *SubmoduleUpdateOptions, force
return err
}
- return s.doRecursiveUpdate(r, o)
+ return s.doRecursiveUpdate(ctx, r, o)
}
-func (s *Submodule) doRecursiveUpdate(r *Repository, o *SubmoduleUpdateOptions) error {
+func (s *Submodule) doRecursiveUpdate(ctx context.Context, r *Repository, o *SubmoduleUpdateOptions) error {
if o.RecurseSubmodules == NoRecurseSubmodules {
return nil
}
@@ -236,7 +236,7 @@ func (s *Submodule) doRecursiveUpdate(r *Repository, o *SubmoduleUpdateOptions)
*new = *o
new.RecurseSubmodules--
- return l.Update(new)
+ return l.UpdateContext(ctx, new)
}
func (s *Submodule) fetchAndCheckout(
diff --git a/vendor/github.com/go-git/go-git/v5/utils/merkletrie/change.go b/vendor/github.com/go-git/go-git/v5/utils/merkletrie/change.go
index cc6dc89071..450feb4bac 100644
--- a/vendor/github.com/go-git/go-git/v5/utils/merkletrie/change.go
+++ b/vendor/github.com/go-git/go-git/v5/utils/merkletrie/change.go
@@ -1,12 +1,17 @@
package merkletrie
import (
+ "errors"
"fmt"
"io"
"github.com/go-git/go-git/v5/utils/merkletrie/noder"
)
+var (
+ ErrEmptyFileName = errors.New("empty filename in tree entry")
+)
+
// Action values represent the kind of things a Change can represent:
// insertion, deletions or modifications of files.
type Action int
@@ -121,6 +126,10 @@ func (l *Changes) AddRecursiveDelete(root noder.Path) error {
type noderToChangeFn func(noder.Path) Change // NewInsert or NewDelete
func (l *Changes) addRecursive(root noder.Path, ctor noderToChangeFn) error {
+ if root.String() == "" {
+ return ErrEmptyFileName
+ }
+
if !root.IsDir() {
l.Add(ctor(root))
return nil
diff --git a/vendor/github.com/go-git/go-git/v5/utils/merkletrie/difftree.go b/vendor/github.com/go-git/go-git/v5/utils/merkletrie/difftree.go
index 8090942ddb..4ef2d9907a 100644
--- a/vendor/github.com/go-git/go-git/v5/utils/merkletrie/difftree.go
+++ b/vendor/github.com/go-git/go-git/v5/utils/merkletrie/difftree.go
@@ -11,7 +11,7 @@ package merkletrie
// corresponding changes and move the iterators further over both
// trees.
//
-// The table bellow show all the possible comparison results, along
+// The table below shows all the possible comparison results, along
// with what changes should we produce and how to advance the
// iterators.
//
diff --git a/vendor/github.com/go-git/go-git/v5/utils/sync/bufio.go b/vendor/github.com/go-git/go-git/v5/utils/sync/bufio.go
index 5009ea8047..42f60f7ea1 100644
--- a/vendor/github.com/go-git/go-git/v5/utils/sync/bufio.go
+++ b/vendor/github.com/go-git/go-git/v5/utils/sync/bufio.go
@@ -13,7 +13,7 @@ var bufioReader = sync.Pool{
}
// GetBufioReader returns a *bufio.Reader that is managed by a sync.Pool.
-// Returns a bufio.Reader that is resetted with reader and ready for use.
+// Returns a bufio.Reader that is reset with reader and ready for use.
//
// After use, the *bufio.Reader should be put back into the sync.Pool
// by calling PutBufioReader.
diff --git a/vendor/github.com/go-git/go-git/v5/utils/sync/bytes.go b/vendor/github.com/go-git/go-git/v5/utils/sync/bytes.go
index dd06fc0bc6..c67b978375 100644
--- a/vendor/github.com/go-git/go-git/v5/utils/sync/bytes.go
+++ b/vendor/github.com/go-git/go-git/v5/utils/sync/bytes.go
@@ -35,7 +35,7 @@ func PutByteSlice(buf *[]byte) {
}
// GetBytesBuffer returns a *bytes.Buffer that is managed by a sync.Pool.
-// Returns a buffer that is resetted and ready for use.
+// Returns a buffer that is reset and ready for use.
//
// After use, the *bytes.Buffer should be put back into the sync.Pool
// by calling PutBytesBuffer.
diff --git a/vendor/github.com/go-git/go-git/v5/utils/sync/zlib.go b/vendor/github.com/go-git/go-git/v5/utils/sync/zlib.go
index c613885957..edf674d852 100644
--- a/vendor/github.com/go-git/go-git/v5/utils/sync/zlib.go
+++ b/vendor/github.com/go-git/go-git/v5/utils/sync/zlib.go
@@ -35,7 +35,7 @@ type ZLibReader struct {
}
// GetZlibReader returns a ZLibReader that is managed by a sync.Pool.
-// Returns a ZLibReader that is resetted using a dictionary that is
+// Returns a ZLibReader that is reset using a dictionary that is
// also managed by a sync.Pool.
//
// After use, the ZLibReader should be put back into the sync.Pool
@@ -58,7 +58,7 @@ func PutZlibReader(z ZLibReader) {
}
// GetZlibWriter returns a *zlib.Writer that is managed by a sync.Pool.
-// Returns a writer that is resetted with w and ready for use.
+// Returns a writer that is reset with w and ready for use.
//
// After use, the *zlib.Writer should be put back into the sync.Pool
// by calling PutZlibWriter.
diff --git a/vendor/github.com/go-git/go-git/v5/worktree.go b/vendor/github.com/go-git/go-git/v5/worktree.go
index ab11d42db8..8dfa50b1b3 100644
--- a/vendor/github.com/go-git/go-git/v5/worktree.go
+++ b/vendor/github.com/go-git/go-git/v5/worktree.go
@@ -25,11 +25,12 @@ import (
)
var (
- ErrWorktreeNotClean = errors.New("worktree is not clean")
- ErrSubmoduleNotFound = errors.New("submodule not found")
- ErrUnstagedChanges = errors.New("worktree contains unstaged changes")
- ErrGitModulesSymlink = errors.New(gitmodulesFile + " is a symlink")
- ErrNonFastForwardUpdate = errors.New("non-fast-forward update")
+ ErrWorktreeNotClean = errors.New("worktree is not clean")
+ ErrSubmoduleNotFound = errors.New("submodule not found")
+ ErrUnstagedChanges = errors.New("worktree contains unstaged changes")
+ ErrGitModulesSymlink = errors.New(gitmodulesFile + " is a symlink")
+ ErrNonFastForwardUpdate = errors.New("non-fast-forward update")
+ ErrRestoreWorktreeOnlyNotSupported = errors.New("worktree only is not supported")
)
// Worktree represents a git worktree.
@@ -139,7 +140,7 @@ func (w *Worktree) PullContext(ctx context.Context, o *PullOptions) error {
}
if o.RecurseSubmodules != NoRecurseSubmodules {
- return w.updateSubmodules(&SubmoduleUpdateOptions{
+ return w.updateSubmodules(ctx, &SubmoduleUpdateOptions{
RecurseSubmodules: o.RecurseSubmodules,
Auth: o.Auth,
})
@@ -148,13 +149,13 @@ func (w *Worktree) PullContext(ctx context.Context, o *PullOptions) error {
return nil
}
-func (w *Worktree) updateSubmodules(o *SubmoduleUpdateOptions) error {
+func (w *Worktree) updateSubmodules(ctx context.Context, o *SubmoduleUpdateOptions) error {
s, err := w.Submodules()
if err != nil {
return err
}
o.Init = true
- return s.Update(o)
+ return s.UpdateContext(ctx, o)
}
// Checkout switch branches or restore working tree files.
@@ -307,13 +308,13 @@ func (w *Worktree) ResetSparsely(opts *ResetOptions, dirs []string) error {
}
if opts.Mode == MixedReset || opts.Mode == MergeReset || opts.Mode == HardReset {
- if err := w.resetIndex(t, dirs); err != nil {
+ if err := w.resetIndex(t, dirs, opts.Files); err != nil {
return err
}
}
if opts.Mode == MergeReset || opts.Mode == HardReset {
- if err := w.resetWorktree(t); err != nil {
+ if err := w.resetWorktree(t, opts.Files); err != nil {
return err
}
}
@@ -321,20 +322,52 @@ func (w *Worktree) ResetSparsely(opts *ResetOptions, dirs []string) error {
return nil
}
+// Restore restores specified files in the working tree or stage with contents from
+// a restore source. If a path is tracked but does not exist in the restore,
+// source, it will be removed to match the source.
+//
+// If Staged and Worktree are true, then the restore source will be the index.
+// If only Staged is true, then the restore source will be HEAD.
+// If only Worktree is true or neither Staged nor Worktree are true, will
+// result in ErrRestoreWorktreeOnlyNotSupported because restoring the working
+// tree while leaving the stage untouched is not currently supported.
+//
+// Restore with no files specified will return ErrNoRestorePaths.
+func (w *Worktree) Restore(o *RestoreOptions) error {
+ if err := o.Validate(); err != nil {
+ return err
+ }
+
+ if o.Staged {
+ opts := &ResetOptions{
+ Files: o.Files,
+ }
+
+ if o.Worktree {
+ // If we are doing both Worktree and Staging then it is a hard reset
+ opts.Mode = HardReset
+ } else {
+ // If we are doing just staging then it is a mixed reset
+ opts.Mode = MixedReset
+ }
+
+ return w.Reset(opts)
+ }
+
+ return ErrRestoreWorktreeOnlyNotSupported
+}
+
// Reset the worktree to a specified state.
func (w *Worktree) Reset(opts *ResetOptions) error {
return w.ResetSparsely(opts, nil)
}
-func (w *Worktree) resetIndex(t *object.Tree, dirs []string) error {
+func (w *Worktree) resetIndex(t *object.Tree, dirs []string, files []string) error {
idx, err := w.r.Storer.Index()
- if len(dirs) > 0 {
- idx.SkipUnless(dirs)
- }
-
if err != nil {
return err
}
+
b := newIndexBuilder(idx)
changes, err := w.diffTreeWithStaging(t, true)
@@ -362,6 +395,13 @@ func (w *Worktree) resetIndex(t *object.Tree, dirs []string) error {
name = ch.From.String()
}
+ if len(files) > 0 {
+ contains := inFiles(files, name)
+ if !contains {
+ continue
+ }
+ }
+
b.Remove(name)
if e == nil {
continue
@@ -376,10 +416,25 @@ func (w *Worktree) resetIndex(t *object.Tree, dirs []string) error {
}
b.Write(idx)
+
+ if len(dirs) > 0 {
+ idx.SkipUnless(dirs)
+ }
+
return w.r.Storer.SetIndex(idx)
}
-func (w *Worktree) resetWorktree(t *object.Tree) error {
+func inFiles(files []string, v string) bool {
+ for _, s := range files {
+ if s == v {
+ return true
+ }
+ }
+
+ return false
+}
+
+func (w *Worktree) resetWorktree(t *object.Tree, files []string) error {
changes, err := w.diffStagingWithWorktree(true, false)
if err != nil {
return err
@@ -395,6 +450,25 @@ func (w *Worktree) resetWorktree(t *object.Tree) error {
if err := w.validChange(ch); err != nil {
return err
}
+
+ if len(files) > 0 {
+ file := ""
+ if ch.From != nil {
+ file = ch.From.String()
+ } else if ch.To != nil {
+ file = ch.To.String()
+ }
+
+ if file == "" {
+ continue
+ }
+
+ contains := inFiles(files, file)
+ if !contains {
+ continue
+ }
+ }
+
if err := w.checkoutChange(ch, t, b); err != nil {
return err
}
@@ -642,7 +716,7 @@ func (w *Worktree) checkoutChangeRegularFile(name string,
return err
}
- return w.addIndexFromFile(name, e.Hash, idx)
+ return w.addIndexFromFile(name, e.Hash, f.Mode, idx)
}
return nil
@@ -725,18 +799,13 @@ func (w *Worktree) addIndexFromTreeEntry(name string, f *object.TreeEntry, idx *
return nil
}
-func (w *Worktree) addIndexFromFile(name string, h plumbing.Hash, idx *indexBuilder) error {
+func (w *Worktree) addIndexFromFile(name string, h plumbing.Hash, mode filemode.FileMode, idx *indexBuilder) error {
idx.Remove(name)
fi, err := w.Filesystem.Lstat(name)
if err != nil {
return err
}
- mode, err := filemode.NewFromOSFileMode(fi.Mode())
- if err != nil {
- return err
- }
-
e := &index.Entry{
Hash: h,
Name: name,
@@ -1058,7 +1127,7 @@ func rmFileAndDirsIfEmpty(fs billy.Filesystem, name string) error {
dir := filepath.Dir(name)
for {
removed, err := removeDirIfEmpty(fs, dir)
- if err != nil {
+ if err != nil && !os.IsNotExist(err) {
return err
}
diff --git a/vendor/github.com/go-git/go-git/v5/worktree_commit.go b/vendor/github.com/go-git/go-git/v5/worktree_commit.go
index f62054bcb4..9b1988ae6b 100644
--- a/vendor/github.com/go-git/go-git/v5/worktree_commit.go
+++ b/vendor/github.com/go-git/go-git/v5/worktree_commit.go
@@ -5,6 +5,7 @@ import (
"errors"
"io"
"path"
+ "regexp"
"sort"
"strings"
@@ -23,6 +24,10 @@ var (
// ErrEmptyCommit occurs when a commit is attempted using a clean
// working tree, with no changes to be committed.
ErrEmptyCommit = errors.New("cannot create empty commit: clean working tree")
+
+ // characters to be removed from user name and/or email before using them to build a commit object
+ // See https://git-scm.com/docs/git-commit#_commit_information
+ invalidCharactersRe = regexp.MustCompile(`[<>\n]`)
)
// Commit stores the current contents of the index in a new commit along with
@@ -38,8 +43,6 @@ func (w *Worktree) Commit(msg string, opts *CommitOptions) (plumbing.Hash, error
}
}
- var treeHash plumbing.Hash
-
if opts.Amend {
head, err := w.r.Head()
if err != nil {
@@ -61,16 +64,34 @@ func (w *Worktree) Commit(msg string, opts *CommitOptions) (plumbing.Hash, error
return plumbing.ZeroHash, err
}
+ // First handle the case of the first commit in the repository being empty.
+ if len(opts.Parents) == 0 && len(idx.Entries) == 0 && !opts.AllowEmptyCommits {
+ return plumbing.ZeroHash, ErrEmptyCommit
+ }
+
h := &buildTreeHelper{
fs: w.Filesystem,
s: w.r.Storer,
}
- treeHash, err = h.BuildTree(idx, opts)
+ treeHash, err := h.BuildTree(idx, opts)
if err != nil {
return plumbing.ZeroHash, err
}
+ previousTree := plumbing.ZeroHash
+ if len(opts.Parents) > 0 {
+ parentCommit, err := w.r.CommitObject(opts.Parents[0])
+ if err != nil {
+ return plumbing.ZeroHash, err
+ }
+ previousTree = parentCommit.TreeHash
+ }
+
+ if treeHash == previousTree && !opts.AllowEmptyCommits {
+ return plumbing.ZeroHash, ErrEmptyCommit
+ }
+
commit, err := w.buildCommitObject(msg, opts, treeHash)
if err != nil {
return plumbing.ZeroHash, err
@@ -121,8 +142,8 @@ func (w *Worktree) updateHEAD(commit plumbing.Hash) error {
func (w *Worktree) buildCommitObject(msg string, opts *CommitOptions, tree plumbing.Hash) (plumbing.Hash, error) {
commit := &object.Commit{
- Author: *opts.Author,
- Committer: *opts.Committer,
+ Author: w.sanitize(*opts.Author),
+ Committer: w.sanitize(*opts.Committer),
Message: msg,
TreeHash: tree,
ParentHashes: opts.Parents,
@@ -148,6 +169,14 @@ func (w *Worktree) buildCommitObject(msg string, opts *CommitOptions, tree plumb
return w.r.Storer.SetEncodedObject(obj)
}
+func (w *Worktree) sanitize(signature object.Signature) object.Signature {
+ return object.Signature{
+ Name: invalidCharactersRe.ReplaceAllString(signature.Name, ""),
+ Email: invalidCharactersRe.ReplaceAllString(signature.Email, ""),
+ When: signature.When,
+ }
+}
+
type gpgSigner struct {
key *openpgp.Entity
cfg *packet.Config
@@ -175,10 +204,6 @@ type buildTreeHelper struct {
// BuildTree builds the tree objects and push its to the storer, the hash
// of the root tree is returned.
func (h *buildTreeHelper) BuildTree(idx *index.Index, opts *CommitOptions) (plumbing.Hash, error) {
- if len(idx.Entries) == 0 && (opts == nil || !opts.AllowEmptyCommits) {
- return plumbing.ZeroHash, ErrEmptyCommit
- }
-
const rootNode = ""
h.trees = map[string]*object.Tree{rootNode: {}}
h.entries = map[string]*object.TreeEntry{}
diff --git a/vendor/github.com/go-git/go-git/v5/worktree_linux.go b/vendor/github.com/go-git/go-git/v5/worktree_linux.go
index 6fcace2f93..f6b85fe3df 100644
--- a/vendor/github.com/go-git/go-git/v5/worktree_linux.go
+++ b/vendor/github.com/go-git/go-git/v5/worktree_linux.go
@@ -1,3 +1,4 @@
+//go:build linux
// +build linux
package git
@@ -21,6 +22,6 @@ func init() {
}
}
-func isSymlinkWindowsNonAdmin(err error) bool {
+func isSymlinkWindowsNonAdmin(_ error) bool {
return false
}
diff --git a/vendor/github.com/go-git/go-git/v5/worktree_status.go b/vendor/github.com/go-git/go-git/v5/worktree_status.go
index dd9b2439cf..6e72db9744 100644
--- a/vendor/github.com/go-git/go-git/v5/worktree_status.go
+++ b/vendor/github.com/go-git/go-git/v5/worktree_status.go
@@ -29,10 +29,23 @@ var (
// ErrGlobNoMatches in an AddGlob if the glob pattern does not match any
// files in the worktree.
ErrGlobNoMatches = errors.New("glob pattern did not match any files")
+ // ErrUnsupportedStatusStrategy occurs when an invalid StatusStrategy is used
+ // when processing the Worktree status.
+ ErrUnsupportedStatusStrategy = errors.New("unsupported status strategy")
)
// Status returns the working tree status.
func (w *Worktree) Status() (Status, error) {
+ return w.StatusWithOptions(StatusOptions{Strategy: defaultStatusStrategy})
+}
+
+// StatusOptions defines the options for Worktree.StatusWithOptions().
+type StatusOptions struct {
+ Strategy StatusStrategy
+}
+
+// StatusWithOptions returns the working tree status.
+func (w *Worktree) StatusWithOptions(o StatusOptions) (Status, error) {
var hash plumbing.Hash
ref, err := w.r.Head()
@@ -44,11 +57,14 @@ func (w *Worktree) Status() (Status, error) {
hash = ref.Hash()
}
- return w.status(hash)
+ return w.status(o.Strategy, hash)
}
-func (w *Worktree) status(commit plumbing.Hash) (Status, error) {
- s := make(Status)
+func (w *Worktree) status(ss StatusStrategy, commit plumbing.Hash) (Status, error) {
+ s, err := ss.new(w)
+ if err != nil {
+ return nil, err
+ }
left, err := w.diffCommitWithStaging(commit, false)
if err != nil {
@@ -488,7 +504,7 @@ func (w *Worktree) copyFileToStorage(path string) (hash plumbing.Hash, err error
return w.r.Storer.SetEncodedObject(obj)
}
-func (w *Worktree) fillEncodedObjectFromFile(dst io.Writer, path string, fi os.FileInfo) (err error) {
+func (w *Worktree) fillEncodedObjectFromFile(dst io.Writer, path string, _ os.FileInfo) (err error) {
src, err := w.Filesystem.Open(path)
if err != nil {
return err
@@ -503,7 +519,7 @@ func (w *Worktree) fillEncodedObjectFromFile(dst io.Writer, path string, fi os.F
return err
}
-func (w *Worktree) fillEncodedObjectFromSymlink(dst io.Writer, path string, fi os.FileInfo) error {
+func (w *Worktree) fillEncodedObjectFromSymlink(dst io.Writer, path string, _ os.FileInfo) error {
target, err := w.Filesystem.Readlink(path)
if err != nil {
return err
@@ -543,9 +559,11 @@ func (w *Worktree) doUpdateFileToIndex(e *index.Entry, filename string, h plumbi
return err
}
- if e.Mode.IsRegular() {
- e.Size = uint32(info.Size())
- }
+ // The entry size must always reflect the current state, otherwise
+ // it will cause go-git's Worktree.Status() to divert from "git status".
+ // The size of a symlink is the length of the path to the target.
+ // The size of Regular and Executable files is the size of the files.
+ e.Size = uint32(info.Size())
fillSystemInfo(e, info.Sys())
return nil
diff --git a/vendor/modules.txt b/vendor/modules.txt
index b01bdb0560..b7a1ffcaad 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -14,8 +14,8 @@ github.com/Microsoft/go-winio/internal/fs
github.com/Microsoft/go-winio/internal/socket
github.com/Microsoft/go-winio/internal/stringbuffer
github.com/Microsoft/go-winio/pkg/guid
-# github.com/ProtonMail/go-crypto v1.0.0
-## explicit; go 1.13
+# github.com/ProtonMail/go-crypto v1.1.3
+## explicit; go 1.17
github.com/ProtonMail/go-crypto/bitcurves
github.com/ProtonMail/go-crypto/brainpool
github.com/ProtonMail/go-crypto/eax
@@ -26,6 +26,8 @@ github.com/ProtonMail/go-crypto/openpgp/aes/keywrap
github.com/ProtonMail/go-crypto/openpgp/armor
github.com/ProtonMail/go-crypto/openpgp/ecdh
github.com/ProtonMail/go-crypto/openpgp/ecdsa
+github.com/ProtonMail/go-crypto/openpgp/ed25519
+github.com/ProtonMail/go-crypto/openpgp/ed448
github.com/ProtonMail/go-crypto/openpgp/eddsa
github.com/ProtonMail/go-crypto/openpgp/elgamal
github.com/ProtonMail/go-crypto/openpgp/errors
@@ -34,6 +36,8 @@ github.com/ProtonMail/go-crypto/openpgp/internal/ecc
github.com/ProtonMail/go-crypto/openpgp/internal/encoding
github.com/ProtonMail/go-crypto/openpgp/packet
github.com/ProtonMail/go-crypto/openpgp/s2k
+github.com/ProtonMail/go-crypto/openpgp/x25519
+github.com/ProtonMail/go-crypto/openpgp/x448
# github.com/antlr4-go/antlr/v4 v4.13.1
## explicit; go 1.22
github.com/antlr4-go/antlr/v4
@@ -72,8 +76,8 @@ github.com/cloudflare/circl/sign/ed448
## explicit; go 1.19
github.com/containerd/stargz-snapshotter/estargz
github.com/containerd/stargz-snapshotter/estargz/errorutil
-# github.com/cyphar/filepath-securejoin v0.3.4
-## explicit; go 1.21
+# github.com/cyphar/filepath-securejoin v0.3.6
+## explicit; go 1.18
github.com/cyphar/filepath-securejoin
# github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
## explicit
@@ -120,16 +124,16 @@ github.com/go-git/gcfg
github.com/go-git/gcfg/scanner
github.com/go-git/gcfg/token
github.com/go-git/gcfg/types
-# github.com/go-git/go-billy/v5 v5.6.0
-## explicit; go 1.20
+# github.com/go-git/go-billy/v5 v5.6.1
+## explicit; go 1.21
github.com/go-git/go-billy/v5
github.com/go-git/go-billy/v5/helper/chroot
github.com/go-git/go-billy/v5/helper/polyfill
github.com/go-git/go-billy/v5/memfs
github.com/go-git/go-billy/v5/osfs
github.com/go-git/go-billy/v5/util
-# github.com/go-git/go-git/v5 v5.12.0
-## explicit; go 1.19
+# github.com/go-git/go-git/v5 v5.13.1
+## explicit; go 1.21
github.com/go-git/go-git/v5
github.com/go-git/go-git/v5/config
github.com/go-git/go-git/v5/internal/path_util