From 6d2e57793e93b2499b62563a48ac60678ad68be7 Mon Sep 17 00:00:00 2001 From: Hayden Blauzvern Date: Tue, 29 Mar 2022 21:01:03 +0000 Subject: [PATCH] Add intermediate CA implementation with KMS-backed signer This CA implementation will use an on-disk certificate chain and a remote KMS signer to sign certificates. There is validation on server startup that the provided chain matches the provided key. I've also added a utility to generate the intermediate certificate by calling GCP CA Service. This will be used to set up Fulcio. This also refactors the code to add an intermediate CA struct that implements the common methods. This makes it simple to add new intermediate CA types, with each only needing to provide a method to fetch a signer and certificate chain. Signed-off-by: Hayden Blauzvern --- cmd/app/serve.go | 16 +- go.mod | 4 +- go.sum | 74 +++++- pkg/ca/fileca/fileca.go | 50 +--- pkg/ca/fileca/fileca_test.go | 4 +- pkg/ca/fileca/load.go | 98 +------- pkg/ca/fileca/watch_test.go | 8 +- .../fetch_ca_cert/fetch_ca_cert.go | 169 +++++++++++++ pkg/ca/intermediateca/intermediateca.go | 124 ++++++++++ pkg/ca/intermediateca/intermediateca_test.go | 151 ++++++++++++ pkg/ca/kmsca/kmsca.go | 67 ++++++ pkg/ca/kmsca/kmsca_test.go | 102 ++++++++ pkg/test/cert_utils.go | 225 ++++++++++++++++++ 13 files changed, 940 insertions(+), 152 deletions(-) create mode 100644 pkg/ca/intermediateca/fetch_ca_cert/fetch_ca_cert.go create mode 100644 pkg/ca/intermediateca/intermediateca.go create mode 100644 pkg/ca/intermediateca/intermediateca_test.go create mode 100644 pkg/ca/kmsca/kmsca.go create mode 100644 pkg/ca/kmsca/kmsca_test.go create mode 100644 pkg/test/cert_utils.go diff --git a/cmd/app/serve.go b/cmd/app/serve.go index e5b6ef3b3..b107a778f 100644 --- a/cmd/app/serve.go +++ b/cmd/app/serve.go @@ -31,6 +31,7 @@ import ( "github.com/sigstore/fulcio/pkg/ca/ephemeralca" "github.com/sigstore/fulcio/pkg/ca/fileca" googlecav1 "github.com/sigstore/fulcio/pkg/ca/googleca/v1" + "github.com/sigstore/fulcio/pkg/ca/kmsca" "github.com/sigstore/fulcio/pkg/ca/x509ca" "github.com/sigstore/fulcio/pkg/config" "github.com/sigstore/fulcio/pkg/ctl" @@ -53,7 +54,7 @@ func newServeCmd() *cobra.Command { cmd.Flags().StringVarP(&serveCmdConfigFilePath, "config", "c", "", "config file containing all settings") cmd.Flags().String("log_type", "dev", "logger type to use (dev/prod)") - cmd.Flags().String("ca", "", "googleca | pkcs11ca | fileca | ephemeralca (for testing)") + cmd.Flags().String("ca", "", "googleca | pkcs11ca | fileca | kmsca | ephemeralca (for testing)") cmd.Flags().String("aws-hsm-root-ca-path", "", "Path to root CA on disk (only used with AWS HSM)") cmd.Flags().String("gcp_private_ca_parent", "", "private ca parent: /projects//locations// (only used with --ca googleca)") cmd.Flags().String("hsm-caroot-id", "", "HSM ID for Root CA (only used with --ca pkcs11ca)") @@ -64,6 +65,8 @@ func newServeCmd() *cobra.Command { cmd.Flags().String("fileca-key", "", "Path to CA encrypted private key") cmd.Flags().String("fileca-key-passwd", "", "Password to decrypt CA private key") cmd.Flags().Bool("fileca-watch", true, "Watch filesystem for updates") + cmd.Flags().String("kms-key", "", "KMS key resource path") + cmd.Flags().String("cert-chain-path", "", "Path to PEM-encoded CA certificate chain") cmd.Flags().String("host", "0.0.0.0", "The host on which to serve requests") cmd.Flags().String("port", "8080", "The port on which to serve requests") @@ -101,7 +104,6 @@ func runServeCmd(cmd *cobra.Command, args []string) { // There's a MarkDeprecated function in cobra/pflags, but it doesn't use log.Logger log.Logger.Warn("gcp_private_ca_version is deprecated and will soon be removed; please remove it") } - case "fileca": if !viper.IsSet("fileca-cert") { log.Logger.Fatal("fileca-cert must be set to certificate path when using fileca") @@ -112,7 +114,13 @@ func runServeCmd(cmd *cobra.Command, args []string) { if !viper.IsSet("fileca-key-passwd") { log.Logger.Fatal("fileca-key-passwd must be set to encryption password for private key file when using fileca") } - + case "kmsca": + if !viper.IsSet("kms-key") { + log.Logger.Fatal("kms-key must be set when using kmsca") + } + if !viper.IsSet("cert-chain-path") { + log.Logger.Fatal("cert-chain-path must be set when using kmsca") + } case "ephemeralca": // this is a no-op since this is a self-signed in-memory CA for testing default: @@ -153,6 +161,8 @@ func runServeCmd(cmd *cobra.Command, args []string) { baseca, err = fileca.NewFileCA(certFile, keyFile, keyPass, watch) case "ephemeralca": baseca, err = ephemeralca.NewEphemeralCA() + case "kmsca": + baseca, err = kmsca.NewKmsCA(cmd.Context(), viper.GetString("kms-key"), viper.GetString("cert-chain-path")) default: err = fmt.Errorf("invalid value for configured CA: %v", baseca) } diff --git a/go.mod b/go.mod index 17f3b98cc..9398a4d9c 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/prometheus/client_golang v1.12.1 github.com/prometheus/client_model v0.2.0 github.com/prometheus/common v0.33.0 - github.com/sigstore/sigstore v1.2.1-0.20220328200116-ef48ee800626 + github.com/sigstore/sigstore v1.2.1-0.20220330193110-d7475aecf1db github.com/spf13/cobra v1.4.0 github.com/spf13/viper v1.10.1 github.com/stretchr/testify v1.7.1 @@ -31,3 +31,5 @@ require ( gopkg.in/square/go-jose.v2 v2.6.0 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b ) + +replace github.com/sigstore/sigstore v1.2.1-0.20220330193110-d7475aecf1db => github.com/haydentherapper/sigstore v1.1.1-0.20220331234123-c28e9d036bd4 diff --git a/go.sum b/go.sum index a8c3ee0a8..623197d0f 100644 --- a/go.sum +++ b/go.sum @@ -45,7 +45,9 @@ cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7 cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY= cloud.google.com/go/iam v0.1.0/go.mod h1:vcUNEa0pEm0qRVpmWepWaFMIAI8/hjB9mO8rNCJtF6c= +cloud.google.com/go/iam v0.1.1 h1:4CapQyNFjiksks1/x7jsvsygFPhihslYk5GptIrlX68= cloud.google.com/go/iam v0.1.1/go.mod h1:CKqrcnI/suGpybEHxZ7BMehL0oA4LpdyJdUlTl9jVMw= +cloud.google.com/go/kms v1.4.0 h1:iElbfoE61VeLhnZcGOltqL8HIly8Nhbe5t6JlH9GXjo= cloud.google.com/go/kms v1.4.0/go.mod h1:fajBHndQ+6ubNw6Ss2sSd+SWvjL26RNo/dr7uxsnnOA= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= @@ -62,24 +64,36 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7 filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmGfU= filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go v62.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go v63.0.0+incompatible h1:whPsa+jCHQSo5wGMPNLw4bz8q9Co2+vnXHzXGctoTaQ= +github.com/Azure/azure-sdk-for-go v63.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= +github.com/Azure/go-autorest/autorest v0.11.24 h1:1fIGgHKqVm54KIPT+q8Zmd1QlVsmHqeUGso5qm2BqqE= github.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc= github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= +github.com/Azure/go-autorest/autorest/adal v0.9.18 h1:kLnPsRjzZZUF3K5REu/Kc+qMQrvuza2bwSnNdhmzLfQ= github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= +github.com/Azure/go-autorest/autorest/azure/auth v0.5.11 h1:P6bYXFoao05z5uhOQzbC3Qd8JqF3jUoocoTeIxkp2cA= github.com/Azure/go-autorest/autorest/azure/auth v0.5.11/go.mod h1:84w/uV8E37feW2NCJ08uT9VBfjfUHpgLVnG2InYD6cg= +github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 h1:0W/yGmFdTIT77fvdlGZ0LMISoLHFJ7Tx4U0yeB+uFs4= github.com/Azure/go-autorest/autorest/azure/cli v0.4.5/go.mod h1:ADQAXrkgm7acgWVUNamOgh8YNrv4p27l3Wc55oVfpzg= +github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk= github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk= github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= +github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac= github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= @@ -118,6 +132,7 @@ github.com/PaesslerAG/jsonpath v0.1.1 h1:c1/AToHQMVsduPAa4Vh6xp2U0evy4t8SWp8imEs github.com/PaesslerAG/jsonpath v0.1.1/go.mod h1:lVboNxFGal/VwW6d9JzIy56bUsYAP6tH/x80vjnCseY= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/ReneKroon/ttlcache/v2 v2.11.0 h1:OvlcYFYi941SBN3v9dsDcC2N8vRxyHcCmJb3Vl4QMoM= github.com/ReneKroon/ttlcache/v2 v2.11.0/go.mod h1:mBxvsNY+BT8qLLd6CuAJubbKo6r0jh3nb5et22bbfGY= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= github.com/ThalesIgnite/crypto11 v1.2.5 h1:1IiIIEqYmBvUYFeMnHqRft4bwf/O36jryEUpY+9ef8E= @@ -133,12 +148,15 @@ github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hC github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-metrics v0.3.9/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= +github.com/armon/go-metrics v0.3.10 h1:FR+drcQStOe+32sYyJYyZ7FIdgoGGBnwLl+flodp8Uo= github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= -github.com/aws/aws-sdk-go v1.43.27/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= +github.com/aws/aws-sdk-go v1.43.28 h1:HrBUf2pYEMRB3GDkSa/bZ2lkZIe8gSUOz/IEupG1Te0= +github.com/aws/aws-sdk-go v1.43.28/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= github.com/beeker1121/goque v1.0.3-0.20191103205551-d618510128af/go.mod h1:84CWnaDz4g1tEVnFLnuBigmGK15oPohy0RfvSN8d4eg= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= @@ -159,6 +177,7 @@ github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8n github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= +github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M= github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -317,6 +336,7 @@ github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11 github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= github.com/docker/cli v20.10.12+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= @@ -364,13 +384,16 @@ github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqL github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/flynn/go-docopt v0.0.0-20140912013429-f6dd2ebbb31e/go.mod h1:HyVoz1Mz5Co8TFO8EupIdlcpwShBmY98dkT2xeHkvEI= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= +github.com/frankban/quicktest v1.13.0 h1:yNZif1OkDfNoDfb9zZa9aXIpejNR4F23Wely0c+Qdqk= github.com/frankban/quicktest v1.13.0/go.mod h1:qLE0fzW0VuyUAJgPU19zByoIr0HtCHN/r/VLSOOIySU= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= @@ -413,7 +436,7 @@ github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTM github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= github.com/go-redis/redis/v8 v8.11.4/go.mod h1:2Z2wHZXdQpCDXEGzqMockDpNyYvi2l4Pxt6RJr792+w= -github.com/go-rod/rod v0.104.2/go.mod h1:SVRIBdiq0sXXDdQxISmWuY8g1kLIUT+Pgk4VZSbOM1s= +github.com/go-rod/rod v0.104.4/go.mod h1:trmrxxg+qUodIIQiYeyJbW5ZMo0FSajmdEGw2tHzlM4= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= @@ -460,6 +483,7 @@ github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXP github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= +github.com/golang-jwt/jwt/v4 v4.2.0 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8eU= github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -498,6 +522,7 @@ github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -570,45 +595,59 @@ github.com/hashicorp/consul/api v1.12.0/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUo github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v1.0.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v1.1.0 h1:QsGcniKx5/LuX2eYoeL+Np3UKYPNaN7YKpTh29h8rbw= github.com/hashicorp/go-hclog v1.1.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-kms-wrapping/entropy v0.1.0/go.mod h1:d1g9WGtAunDNpek8jUIEJnBlbgKS1N2Q61QkHiZyR1g= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-plugin v1.4.3 h1:DXmvivbWD5qdiBts9TpBC7BYL1Aia5sxbRgQB+v6UZM= github.com/hashicorp/go-plugin v1.4.3/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= +github.com/hashicorp/go-retryablehttp v0.7.0 h1:eu1EI/mbirUgP5C8hVsTNaGZreBDlYiwC1FZWkvQPQ4= github.com/hashicorp/go-retryablehttp v0.7.0/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= +github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-secure-stdlib/base62 v0.1.1/go.mod h1:EdWO6czbmthiwZ3/PUsDV+UD1D5IRU4ActiaWGwt0Yw= github.com/hashicorp/go-secure-stdlib/mlock v0.1.1/go.mod h1:zq93CJChV6L9QTfGKtfBxKqD7BqqXx5O04A/ns2p5+I= +github.com/hashicorp/go-secure-stdlib/mlock v0.1.2 h1:p4AKXPPS24tO8Wc8i1gLvSKdmkiSY5xuju57czJ/IJQ= github.com/hashicorp/go-secure-stdlib/mlock v0.1.2/go.mod h1:zq93CJChV6L9QTfGKtfBxKqD7BqqXx5O04A/ns2p5+I= github.com/hashicorp/go-secure-stdlib/parseutil v0.1.1/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= +github.com/hashicorp/go-secure-stdlib/parseutil v0.1.2 h1:Tz6v3Jb2DRnDCfifRSjYKG0m8dLdNq6bcDkB41en7nw= github.com/hashicorp/go-secure-stdlib/parseutil v0.1.2/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= github.com/hashicorp/go-secure-stdlib/password v0.1.1/go.mod h1:9hH302QllNwu1o2TGYtSk8I8kTAN0ca1EHpwhm5Mmzo= github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U= +github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts= github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4= github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.1/go.mod h1:l8slYwnJA26yBz+ErHpp2IRCLr0vuOMGBORIz4rRiAs= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.4.0 h1:aAQzgqIrRKRa7w75CKpbBxYsmUoPjzVm1W59ca1L0J4= github.com/hashicorp/go-version v1.4.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -623,10 +662,15 @@ github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOn github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= +github.com/hashicorp/vault/api v1.5.0 h1:Bp6yc2bn7CWkOrVIzFT/Qurzx528bdavF3nz590eu28= github.com/hashicorp/vault/api v1.5.0/go.mod h1:LkMdrZnWNrFaQyYYazWVn7KshilfDidgVBq6YiTq/bM= +github.com/hashicorp/vault/sdk v0.4.1 h1:3SaHOJY687jY1fnB61PtL0cOkKItphrbLmux7T92HBo= github.com/hashicorp/vault/sdk v0.4.1/go.mod h1:aZ3fNuL5VNydQk8GcLJ2TV8YCRVvyaakYkhZRoVuhj0= github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= +github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 h1:xixZ2bWeofWV68J+x6AzmKuVM/JWCQwkWm6GW/MUR6I= github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= +github.com/haydentherapper/sigstore v1.1.1-0.20220331234123-c28e9d036bd4 h1:B26iYm6TTPB6a0uTSINIF6N8i1+kTrgUzvZsBtxr/rs= +github.com/haydentherapper/sigstore v1.1.1-0.20220331234123-c28e9d036bd4/go.mod h1:dzF7JvyOyg6kcwz7UxS05jGdqXxVg17JUQaQ36nvwZg= github.com/honeycombio/beeline-go v1.1.1 h1:sU8r4ae34uEL3/CguSl8Mr+Asz9DL1nfH9Wwk85Pc7U= github.com/honeycombio/beeline-go v1.1.1/go.mod h1:kN0cfUGBMfA87DyCYbiiLoSzWsnw3bluZvNEWtatHxk= github.com/honeycombio/libhoney-go v1.15.2 h1:5NGcjOxZZma13dmzNcl3OtGbF1hECA0XHJNHEb2t2ck= @@ -677,10 +721,13 @@ github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0f github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmhodges/clock v0.0.0-20160418191101-880ee4c33548 h1:dYTbLf4m0a5u0KLmPfB6mgxbcV7588bOCx79hxa5Sr4= github.com/jmhodges/clock v0.0.0-20160418191101-880ee4c33548/go.mod h1:hGT6jSUVzF6no3QaDSMLGLEHtHSBSefs+MgcDWnmhmo= @@ -758,6 +805,7 @@ github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVc github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= @@ -768,6 +816,7 @@ github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2y github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= @@ -793,10 +842,13 @@ github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= @@ -807,6 +859,7 @@ github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGg github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= @@ -832,6 +885,7 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= @@ -878,6 +932,7 @@ github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqi github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= @@ -885,6 +940,7 @@ github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhEC github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -952,6 +1008,7 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig= @@ -967,8 +1024,6 @@ github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNX github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sigstore/sigstore v1.2.1-0.20220328200116-ef48ee800626 h1:ocuw0kEgUifM2NLm63T4vILh0YatZ8bD5LPxaqaTx9c= -github.com/sigstore/sigstore v1.2.1-0.20220328200116-ef48ee800626/go.mod h1:iujhJMoAxFbvcOKdVp5+HoPei9pg+2e1udEs8QSzuXE= github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= @@ -1020,6 +1075,7 @@ github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -1078,9 +1134,9 @@ github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/ysmood/goob v0.3.1/go.mod h1:S3lq113Y91y1UBf1wj1pFOxeahvfKkCk6mTWTWbDdWs= -github.com/ysmood/got v0.16.2/go.mod h1:pE1l4LOwOBhQg6A/8IAatkGp7uZjnalzrZolnlhhMgY= -github.com/ysmood/gotrace v0.2.2/go.mod h1:TzhIG7nHDry5//eYZDYcTzuJLYQIkykJzCRIo4/dzQM= -github.com/ysmood/gson v0.6.4/go.mod h1:3Kzs5zDl21g5F/BlLTNcuAGAYLKt2lV5G8D1zF3RNmg= +github.com/ysmood/got v0.19.1/go.mod h1:pE1l4LOwOBhQg6A/8IAatkGp7uZjnalzrZolnlhhMgY= +github.com/ysmood/gotrace v0.4.0/go.mod h1:TzhIG7nHDry5//eYZDYcTzuJLYQIkykJzCRIo4/dzQM= +github.com/ysmood/gson v0.7.0/go.mod h1:3Kzs5zDl21g5F/BlLTNcuAGAYLKt2lV5G8D1zF3RNmg= github.com/ysmood/leakless v0.7.0/go.mod h1:R8iAXPRaG97QJwqxs74RdwzcRHT1SWCGTNqY8q0JvMQ= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -1302,6 +1358,7 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1440,6 +1497,7 @@ golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 h1:GZokNIeuVkl3aZHJchRrr13WCsols02MLUcz1U9is6M= golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/pkg/ca/fileca/fileca.go b/pkg/ca/fileca/fileca.go index 57621c2f2..7f65e243c 100644 --- a/pkg/ca/fileca/fileca.go +++ b/pkg/ca/fileca/fileca.go @@ -16,24 +16,16 @@ package fileca import ( - "context" "crypto" - "crypto/rand" "crypto/x509" - "sync" "github.com/fsnotify/fsnotify" "github.com/sigstore/fulcio/pkg/ca" - "github.com/sigstore/fulcio/pkg/ca/x509ca" - "github.com/sigstore/fulcio/pkg/challenges" - "github.com/sigstore/sigstore/pkg/cryptoutils" + "github.com/sigstore/fulcio/pkg/ca/intermediateca" ) type fileCA struct { - sync.RWMutex - - certs []*x509.Certificate - key crypto.Signer + intermediateca.IntermediateCA } // NewFileCA returns a file backed certificate authority. Expects paths to a @@ -43,7 +35,7 @@ func NewFileCA(certPath, keyPath, keyPass string, watch bool) (ca.CertificateAut var fca fileCA var err error - fca.certs, fca.key, err = loadKeyPair(certPath, keyPath, keyPass) + fca.Certs, fca.Signer, err = loadKeyPair(certPath, keyPath, keyPass) if err != nil { return nil, err } @@ -68,43 +60,13 @@ func NewFileCA(certPath, keyPath, keyPass string, watch bool) (ca.CertificateAut return &fca, err } -func (fca *fileCA) updateX509KeyPair(certs []*x509.Certificate, key crypto.Signer) { +func (fca *fileCA) updateX509KeyPair(certs []*x509.Certificate, signer crypto.Signer) { fca.Lock() defer fca.Unlock() // NB: We use the RWLock to unsure a reading thread can't get a mismatching // cert / key pair by reading the attributes halfway through the update // below. - fca.certs = certs - fca.key = key -} - -func (fca *fileCA) getX509KeyPair() ([]*x509.Certificate, crypto.Signer) { - fca.RLock() - defer fca.RUnlock() - return fca.certs, fca.key -} - -// CreateCertificate issues code signing certificates -func (fca *fileCA) CreateCertificate(_ context.Context, subject *challenges.ChallengeResult) (*ca.CodeSigningCertificate, error) { - cert, err := x509ca.MakeX509(subject) - if err != nil { - return nil, err - } - - certChain, privateKey := fca.getX509KeyPair() - - finalCertBytes, err := x509.CreateCertificate(rand.Reader, cert, certChain[0], subject.PublicKey, privateKey) - if err != nil { - return nil, err - } - - return ca.CreateCSCFromDER(subject, finalCertBytes, certChain) -} - -func (fca *fileCA) Root(ctx context.Context) ([]byte, error) { - fca.RLock() - defer fca.RUnlock() - - return cryptoutils.MarshalCertificatesToPEM(fca.certs) + fca.Certs = certs + fca.Signer = signer } diff --git a/pkg/ca/fileca/fileca_test.go b/pkg/ca/fileca/fileca_test.go index d8dd267eb..49f29b5ba 100644 --- a/pkg/ca/fileca/fileca_test.go +++ b/pkg/ca/fileca/fileca_test.go @@ -57,7 +57,7 @@ func TestCertUpdate(t *testing.T) { t.Fatal(`Bad CA type`) } - _, key := fca.getX509KeyPair() + key := fca.Signer if _, ok = key.(ed25519.PrivateKey); !ok { t.Error(`first key should have been an ed25519 key`) } @@ -68,7 +68,7 @@ func TestCertUpdate(t *testing.T) { } fca.updateX509KeyPair(cert, key) - _, key = fca.getX509KeyPair() + key = fca.Signer if _, ok = key.(*ecdsa.PrivateKey); !ok { t.Fatal(`file CA should have been updated with ecdsa key`) diff --git a/pkg/ca/fileca/load.go b/pkg/ca/fileca/load.go index a2bd961b4..ed8cbf638 100644 --- a/pkg/ca/fileca/load.go +++ b/pkg/ca/fileca/load.go @@ -18,71 +18,30 @@ package fileca import ( "bytes" "crypto" - "crypto/ecdsa" - "crypto/ed25519" - "crypto/rsa" "crypto/x509" "errors" + "os" + "path/filepath" + "github.com/sigstore/fulcio/pkg/ca/intermediateca" + "github.com/sigstore/sigstore/pkg/cryptoutils" "go.step.sm/crypto/pemutil" ) func loadKeyPair(certPath, keyPath, keyPass string) ([]*x509.Certificate, crypto.Signer, error) { - var ( certs []*x509.Certificate err error key crypto.Signer ) - // NB: certs are ordered from leaf at certs[0] to root at - // certs[len(certs)-1] - certs, err = pemutil.ReadCertificateBundle(certPath) + data, err := os.ReadFile(filepath.Clean(certPath)) if err != nil { return nil, nil, err } - - // Verify certificate chain - { - roots := x509.NewCertPool() - roots.AddCert(certs[len(certs)-1]) - - intermediates := x509.NewCertPool() - if len(certs) > 2 { - for _, intermediate := range certs[1 : len(certs)-1] { - intermediates.AddCert(intermediate) - } - } - - opts := x509.VerifyOptions{ - Roots: roots, - Intermediates: intermediates, - KeyUsages: []x509.ExtKeyUsage{ - x509.ExtKeyUsageCodeSigning, - }, - } - if _, err := certs[0].Verify(opts); err != nil { - return nil, nil, err - } - - if !certs[0].IsCA { - return nil, nil, errors.New(`fileca: certificate is not a CA`) - } - - // If using an intermediate, verify that code signing extended key - // usage is set to satify extended key usage chainging - if len(certs) > 1 { - var hasExtKeyUsageCodeSigning bool - for _, extKeyUsage := range certs[0].ExtKeyUsage { - if extKeyUsage == x509.ExtKeyUsageCodeSigning { - hasExtKeyUsageCodeSigning = true - break - } - } - if !hasExtKeyUsageCodeSigning { - return nil, nil, errors.New(`fileca: certificate must have extended key usage code signing set to sign code signing certificates`) - } - } + certs, err = cryptoutils.LoadCertificatesFromPEM(bytes.NewReader(data)) + if err != nil { + return nil, nil, err } { @@ -98,46 +57,9 @@ func loadKeyPair(certPath, keyPath, keyPass string) ([]*x509.Certificate, crypto } } - if !valid(certs[0], key) { - return nil, nil, errors.New(`fileca: certificate public key and private key don't match`) + if err := intermediateca.VerifyCertChain(certs, key); err != nil { + return nil, nil, err } return certs, key, nil } - -func valid(cert *x509.Certificate, key crypto.Signer) bool { - if cert == nil || key == nil { - return false - } - - switch pub := cert.PublicKey.(type) { - case *rsa.PublicKey: - priv, ok := key.(*rsa.PrivateKey) - if !ok { - return false - } - if pub.N.Cmp(priv.N) != 0 { - return false - } - case *ecdsa.PublicKey: - priv, ok := key.(*ecdsa.PrivateKey) - if !ok { - return false - } - if pub.X.Cmp(priv.X) != 0 || pub.Y.Cmp(priv.Y) != 0 { - return false - } - case ed25519.PublicKey: - priv, ok := key.(ed25519.PrivateKey) - if !ok { - return false - } - if !bytes.Equal(priv.Public().(ed25519.PublicKey), pub) { - return false - } - default: - return false - } - - return true -} diff --git a/pkg/ca/fileca/watch_test.go b/pkg/ca/fileca/watch_test.go index 6ebd65927..ea75893b7 100644 --- a/pkg/ca/fileca/watch_test.go +++ b/pkg/ca/fileca/watch_test.go @@ -36,17 +36,13 @@ func cp(src, dst string) error { } func TestIOWatch(t *testing.T) { - dir, err := os.MkdirTemp("", "fileca") - if err != nil { - t.Fatal(`Failed to create temp dir`) - } - // defer os.RemoveAll(dir) + dir := t.TempDir() keyPath := filepath.Join(dir, "key.pem") certPath := filepath.Join(dir, "cert.pem") // Copy initial certs into place - err = cp("testdata/ed25519-key.pem", keyPath) + err := cp("testdata/ed25519-key.pem", keyPath) if err != nil { t.Fatal(`Couldn't copy test data to temp file`) } diff --git a/pkg/ca/intermediateca/fetch_ca_cert/fetch_ca_cert.go b/pkg/ca/intermediateca/fetch_ca_cert/fetch_ca_cert.go new file mode 100644 index 000000000..a2e307635 --- /dev/null +++ b/pkg/ca/intermediateca/fetch_ca_cert/fetch_ca_cert.go @@ -0,0 +1,169 @@ +// Copyright 2022 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package main + +import ( + "context" + "crypto" + "crypto/x509" + "errors" + "flag" + "log" + "os" + "time" + + privateca "cloud.google.com/go/security/privateca/apiv1" + "github.com/sigstore/sigstore/pkg/cryptoutils" + privatecapb "google.golang.org/genproto/googleapis/cloud/security/privateca/v1" + "google.golang.org/protobuf/types/known/durationpb" + + // Register the provider-specific plugins + "github.com/sigstore/sigstore/pkg/signature/kms" + _ "github.com/sigstore/sigstore/pkg/signature/kms/aws" + _ "github.com/sigstore/sigstore/pkg/signature/kms/azure" + _ "github.com/sigstore/sigstore/pkg/signature/kms/gcp" + _ "github.com/sigstore/sigstore/pkg/signature/kms/hashivault" +) + +/* +To run: +go run pkg/ca/intermediateca/update/fetch_ca_cert.go \ + --kms-key="gcpkms://projects//locations//keyRings//cryptoKeys//versions/1" \ + --gcp-ca-parent="projects//locations//caPools/" \ + --output="chain.crt.pem" + +You must have the permissions to read the KMS key, and create a certificate in the CA pool. +*/ + +var ( + gcpCaParent = flag.String("gcp-ca-parent", "", "Resource path to GCP CA Service CA") + kmsKey = flag.String("kms-key", "", "Resource path to KMS key, starting with gcpkms://, awskms://, azurekms:// or hashivault://") + outputPath = flag.String("output", "", "Path to the output file") +) + +func fetchCACertificate(ctx context.Context, parent, kmsKey string, client *privateca.CertificateAuthorityClient) ([]*x509.Certificate, error) { + kmsSigner, err := kms.Get(ctx, kmsKey, crypto.SHA256) + if err != nil { + return nil, err + } + signer, _, err := kmsSigner.CryptoSigner(ctx, func(err error) {}) + if err != nil { + return nil, err + } + + pemPubKey, err := cryptoutils.MarshalPublicKeyToPEM(signer.Public()) + if err != nil { + return nil, err + } + + isCa := true + // default value of 0 for int32 + var maxIssuerPathLength int32 + + csr := &privatecapb.CreateCertificateRequest{ + Parent: parent, + Certificate: &privatecapb.Certificate{ + // Default to a very large lifetime - CA Service will truncate the + // lifetime to be no longer than the root's lifetime. + // 20 years (24 hours * 365 days * 20) + Lifetime: durationpb.New(time.Hour * 24 * 365 * 20), + CertificateConfig: &privatecapb.Certificate_Config{ + Config: &privatecapb.CertificateConfig{ + PublicKey: &privatecapb.PublicKey{ + Format: privatecapb.PublicKey_PEM, + Key: pemPubKey, + }, + X509Config: &privatecapb.X509Parameters{ + KeyUsage: &privatecapb.KeyUsage{ + BaseKeyUsage: &privatecapb.KeyUsage_KeyUsageOptions{ + CertSign: true, + CrlSign: true, + }, + ExtendedKeyUsage: &privatecapb.KeyUsage_ExtendedKeyUsageOptions{ + CodeSigning: true, + }, + }, + CaOptions: &privatecapb.X509Parameters_CaOptions{ + IsCa: &isCa, + MaxIssuerPathLength: &maxIssuerPathLength, + }, + }, + SubjectConfig: &privatecapb.CertificateConfig_SubjectConfig{ + Subject: &privatecapb.Subject{ + CommonName: "sigstore-intermediate", + Organization: "sigstore.dev", + }, + }, + }, + }, + }, + } + + resp, err := client.CreateCertificate(ctx, csr) + if err != nil { + return nil, err + } + + var pemCerts []string + pemCerts = append(pemCerts, resp.PemCertificate) + pemCerts = append(pemCerts, resp.PemCertificateChain...) + + var parsedCerts []*x509.Certificate + for _, c := range pemCerts { + certs, err := cryptoutils.UnmarshalCertificatesFromPEM([]byte(c)) + if err != nil { + return nil, err + } + if len(certs) != 1 { + return nil, errors.New("unexpected number of certificates returned") + } + parsedCerts = append(parsedCerts, certs[0]) + } + + return parsedCerts, nil +} + +func main() { + flag.Parse() + + if *gcpCaParent == "" { + log.Fatal("gcp-ca-parent must be set") + } + if *kmsKey == "" { + log.Fatal("kms-key must be set") + } + if *outputPath == "" { + log.Fatal("output must be set") + } + + client, err := privateca.NewCertificateAuthorityClient(context.Background()) + if err != nil { + log.Fatal(err) + } + parsedCerts, err := fetchCACertificate(context.Background(), *gcpCaParent, *kmsKey, client) + if err != nil { + log.Fatal(err) + } + pemCerts, err := cryptoutils.MarshalCertificatesToPEM(parsedCerts) + if err != nil { + log.Fatal(err) + } + + err = os.WriteFile(*outputPath, pemCerts, 0600) + if err != nil { + log.Fatal(err) + } +} diff --git a/pkg/ca/intermediateca/intermediateca.go b/pkg/ca/intermediateca/intermediateca.go new file mode 100644 index 000000000..894c4616c --- /dev/null +++ b/pkg/ca/intermediateca/intermediateca.go @@ -0,0 +1,124 @@ +// Copyright 2022 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package intermediateca + +import ( + "context" + "crypto" + "crypto/rand" + "crypto/x509" + "errors" + "sync" + + "github.com/sigstore/fulcio/pkg/ca" + "github.com/sigstore/fulcio/pkg/ca/x509ca" + "github.com/sigstore/fulcio/pkg/challenges" + "github.com/sigstore/sigstore/pkg/cryptoutils" +) + +type IntermediateCA struct { + sync.RWMutex + + // certs is a chain of certificates from intermediate to root + Certs []*x509.Certificate + Signer crypto.Signer +} + +func (ica *IntermediateCA) CreateCertificate(ctx context.Context, challenge *challenges.ChallengeResult) (*ca.CodeSigningCertificate, error) { + cert, err := x509ca.MakeX509(challenge) + if err != nil { + return nil, err + } + + certChain, privateKey := ica.getX509KeyPair() + + finalCertBytes, err := x509.CreateCertificate(rand.Reader, cert, certChain[0], challenge.PublicKey, privateKey) + if err != nil { + return nil, err + } + + return ca.CreateCSCFromDER(challenge, finalCertBytes, certChain) +} + +func (ica *IntermediateCA) Root(ctx context.Context) ([]byte, error) { + ica.RLock() + defer ica.RUnlock() + + return cryptoutils.MarshalCertificatesToPEM(ica.Certs) +} + +func (ica *IntermediateCA) getX509KeyPair() ([]*x509.Certificate, crypto.Signer) { + ica.RLock() + defer ica.RUnlock() + + return ica.Certs, ica.Signer +} + +func VerifyCertChain(certs []*x509.Certificate, signer crypto.Signer) error { + if len(certs) == 0 { + return errors.New("certificate chain must contain at least one certificate") + } + + roots := x509.NewCertPool() + roots.AddCert(certs[len(certs)-1]) + + intermediates := x509.NewCertPool() + if len(certs) > 1 { + for _, intermediate := range certs[1 : len(certs)-1] { + intermediates.AddCert(intermediate) + } + } + + opts := x509.VerifyOptions{ + Roots: roots, + Intermediates: intermediates, + KeyUsages: []x509.ExtKeyUsage{ + x509.ExtKeyUsageCodeSigning, + }, + } + if _, err := certs[0].Verify(opts); err != nil { + return err + } + + if !certs[0].IsCA { + return errors.New("certificate is not a CA") + } + + // If using an intermediate, verify that code signing extended key + // usage is set to satify extended key usage chainging + if len(certs) > 1 { + var hasExtKeyUsageCodeSigning bool + for _, extKeyUsage := range certs[0].ExtKeyUsage { + if extKeyUsage == x509.ExtKeyUsageCodeSigning { + hasExtKeyUsageCodeSigning = true + break + } + } + if !hasExtKeyUsageCodeSigning { + return errors.New(`certificate must have extended key usage code signing set to sign code signing certificates`) + } + } + + if err := cryptoutils.EqualKeys(certs[0].PublicKey, signer.Public()); err != nil { + return err + } + + if err := cryptoutils.ValidatePubKey(signer.Public()); err != nil { + return err + } + + return nil +} diff --git a/pkg/ca/intermediateca/intermediateca_test.go b/pkg/ca/intermediateca/intermediateca_test.go new file mode 100644 index 000000000..da9226f86 --- /dev/null +++ b/pkg/ca/intermediateca/intermediateca_test.go @@ -0,0 +1,151 @@ +// Copyright 2022 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package intermediateca + +import ( + "context" + "crypto/x509" + "reflect" + "strings" + "testing" + + "github.com/sigstore/fulcio/pkg/test" + "github.com/sigstore/sigstore/pkg/cryptoutils" + "github.com/sigstore/sigstore/pkg/signature" +) + +func TestIntermediateCARoot(t *testing.T) { + signer, _, err := signature.NewDefaultECDSASignerVerifier() + if err != nil { + t.Fatalf("unexpected error generating signer: %v", err) + } + + rootCert, rootKey, _ := test.GenerateRootCa() + subCert, _, _ := test.GenerateSubordinateCa(rootCert, rootKey) + certChain := []*x509.Certificate{subCert, rootCert} + pemChain, err := cryptoutils.MarshalCertificatesToPEM(certChain) + if err != nil { + t.Fatalf("unexpected error marshalling cert chain: %v", err) + } + + ica := IntermediateCA{ + Certs: certChain, + Signer: signer, + } + + rootBytes, err := ica.Root(context.TODO()) + if err != nil { + t.Fatalf("unexpected error reading root: %v", err) + } + + if !reflect.DeepEqual(pemChain, rootBytes) { + t.Fatal("expected cert chains to be equivalent") + } +} + +func TestIntermediateCAGetX509KeyPair(t *testing.T) { + signer, _, err := signature.NewDefaultECDSASignerVerifier() + if err != nil { + t.Fatalf("unexpected error generating signer: %v", err) + } + + rootCert, rootKey, _ := test.GenerateRootCa() + subCert, _, _ := test.GenerateSubordinateCa(rootCert, rootKey) + certChain := []*x509.Certificate{subCert, rootCert} + + ica := IntermediateCA{ + Certs: certChain, + Signer: signer, + } + + foundCertChain, foundSigner := ica.getX509KeyPair() + + if !reflect.DeepEqual(certChain, foundCertChain) { + t.Fatal("expected cert chains to be equivalent") + } + + if err := cryptoutils.EqualKeys(signer.Public(), foundSigner.Public()); err != nil { + t.Fatalf("expected keys to be equivalent, expected %v, got %v, error %v", signer.Public(), foundSigner.Public(), err) + } +} + +func TestIntermediateCAVerifyCertChain(t *testing.T) { + rootCert, rootKey, _ := test.GenerateRootCa() + subCert, subKey, _ := test.GenerateSubordinateCa(rootCert, rootKey) + leafCert, _, _ := test.GenerateLeafCert("subject", "oidc-issuer", subCert, subKey) + + err := VerifyCertChain([]*x509.Certificate{subCert, rootCert}, subKey) + if err != nil { + t.Fatalf("unexpected error verifying cert chain: %v", err) + } + + // Handles single certifiacte in chain + err = VerifyCertChain([]*x509.Certificate{rootCert}, rootKey) + if err != nil { + t.Fatalf("unexpected error verifying single cert chain: %v", err) + } + + // Handles multiple intermediates + subCert2, subKey2, _ := test.GenerateSubordinateCa(subCert, subKey) + err = VerifyCertChain([]*x509.Certificate{subCert2, subCert, rootCert}, subKey2) + if err != nil { + t.Fatalf("unexpected error verifying cert chain: %v", err) + } + + // Failure: Certificate is not a CA certificate + err = VerifyCertChain([]*x509.Certificate{leafCert}, nil) + if err == nil || !strings.Contains(err.Error(), "certificate is not a CA") { + t.Fatalf("expected error with non-CA cert: %v", err) + } + + // Failure: Certificate missing EKU + // Note that the wrong EKU will be caught by x509.Verify + invalidSubCert, invalidSubKey, _ := test.GenerateSubordinateCaWithoutEKU(rootCert, rootKey) + err = VerifyCertChain([]*x509.Certificate{invalidSubCert, rootCert}, invalidSubKey) + if err == nil || !strings.Contains(err.Error(), "certificate must have extended key usage code signing") { + t.Fatalf("expected error verifying cert chain without EKU: %v", err) + } + + // Failure: Invalid chain + rootCert2, _, _ := test.GenerateRootCa() + err = VerifyCertChain([]*x509.Certificate{subCert, rootCert2}, subKey) + if err == nil || !strings.Contains(err.Error(), "certificate signed by unknown authority") { + t.Fatalf("expected error verifying cert chain: %v", err) + } + + // Failure: Different signer with different key + signer, _, err := signature.NewDefaultECDSASignerVerifier() + if err != nil { + t.Fatalf("expected error generating signer: %v", err) + } + err = VerifyCertChain([]*x509.Certificate{subCert, rootCert}, signer) + if err == nil || !strings.Contains(err.Error(), "public keys are not equal") { + t.Fatalf("expected error verifying cert with mismatched public keys: %v", err) + } + + // Failure: Weak key + weakSubCert, weakSubKey, _ := test.GenerateWeakSubordinateCa(rootCert, rootKey) + err = VerifyCertChain([]*x509.Certificate{weakSubCert, rootCert}, weakSubKey) + if err == nil || !strings.Contains(err.Error(), "unsupported ec curve") { + t.Fatalf("expected error verifying weak cert chain: %v", err) + } + + // Failure: Empty chain + err = VerifyCertChain([]*x509.Certificate{}, weakSubKey) + if err == nil || !strings.Contains(err.Error(), "certificate chain must contain at least one certificate") { + t.Fatalf("expected error verifying with empty chain: %v", err) + } +} diff --git a/pkg/ca/kmsca/kmsca.go b/pkg/ca/kmsca/kmsca.go new file mode 100644 index 000000000..9caf17385 --- /dev/null +++ b/pkg/ca/kmsca/kmsca.go @@ -0,0 +1,67 @@ +// Copyright 2022 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package kmsca + +import ( + "bytes" + "context" + "crypto" + "os" + "path/filepath" + + "github.com/sigstore/fulcio/pkg/ca" + "github.com/sigstore/fulcio/pkg/ca/intermediateca" + "github.com/sigstore/sigstore/pkg/cryptoutils" + "github.com/sigstore/sigstore/pkg/signature/kms" + + // Register the provider-specific plugins + _ "github.com/sigstore/sigstore/pkg/signature/kms/aws" + _ "github.com/sigstore/sigstore/pkg/signature/kms/azure" + _ "github.com/sigstore/sigstore/pkg/signature/kms/gcp" + _ "github.com/sigstore/sigstore/pkg/signature/kms/hashivault" +) + +type kmsCA struct { + intermediateca.IntermediateCA +} + +func NewKmsCA(ctx context.Context, kmsKey, certPath string) (ca.CertificateAuthority, error) { + var ica kmsCA + + kmsSigner, err := kms.Get(ctx, kmsKey, crypto.SHA256) + if err != nil { + return nil, err + } + signer, _, err := kmsSigner.CryptoSigner(ctx, func(err error) {}) + if err != nil { + return nil, err + } + ica.Signer = signer + + data, err := os.ReadFile(filepath.Clean(certPath)) + if err != nil { + return nil, err + } + ica.Certs, err = cryptoutils.LoadCertificatesFromPEM(bytes.NewReader(data)) + if err != nil { + return nil, err + } + if err := intermediateca.VerifyCertChain(ica.Certs, ica.Signer); err != nil { + return nil, err + } + + return &ica, nil +} diff --git a/pkg/ca/kmsca/kmsca_test.go b/pkg/ca/kmsca/kmsca_test.go new file mode 100644 index 000000000..4966abd77 --- /dev/null +++ b/pkg/ca/kmsca/kmsca_test.go @@ -0,0 +1,102 @@ +// Copyright 2022 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package kmsca + +import ( + "context" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/x509" + "os" + "path/filepath" + "reflect" + "strings" + "testing" + + "github.com/sigstore/fulcio/pkg/test" + "github.com/sigstore/sigstore/pkg/cryptoutils" + "github.com/sigstore/sigstore/pkg/signature/kms/fake" + _ "github.com/sigstore/sigstore/pkg/signature/kms/fake" +) + +func TestNewKmsCA(t *testing.T) { + dir := t.TempDir() + certPath := filepath.Join(dir, "cert.pem") + + rootCert, rootKey, _ := test.GenerateRootCa() + + priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Fatalf("error generating ecdsa private key: %v", err) + } + + subCert, _ := test.GenerateSubordinateCaWithPublicKey(priv.Public(), rootCert, rootKey) + + pemChain, err := cryptoutils.MarshalCertificatesToPEM([]*x509.Certificate{subCert, rootCert}) + if err != nil { + t.Fatalf("error marshalling cert chain: %v", err) + } + err = os.WriteFile(certPath, pemChain, 0600) + if err != nil { + t.Fatalf("error writing pem chain: %v", err) + } + + ca, err := NewKmsCA(context.WithValue(context.TODO(), fake.KmsCtxKey{}, priv), "fakekms://key", certPath) + if err != nil { + t.Fatalf("unexpected error creating KMS CA: %v", err) + } + + // Expect certificate chain from Root matches provided certificate chain + rootBytes, err := ca.Root(context.TODO()) + if err != nil { + t.Fatalf("error fetching root: %v", err) + } + if !reflect.DeepEqual(rootBytes, pemChain) { + t.Fatal("cert chains do not match") + } + + // Expect signer and certificate's public keys match + ica := ca.(*kmsCA) + if err := cryptoutils.EqualKeys(ica.Signer.Public(), priv.Public()); err != nil { + t.Fatalf("keys between CA and signer do not match: %v", err) + } + if !reflect.DeepEqual(ica.Certs, []*x509.Certificate{subCert, rootCert}) { + t.Fatalf("expected certificate chains to match: %v", err) + } + + // Failure: Mismatch between signer and certificate key + otherPriv, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + ca, err = NewKmsCA(context.WithValue(context.TODO(), fake.KmsCtxKey{}, otherPriv), "fakekms://key", certPath) + if err == nil || !strings.Contains(err.Error(), "ecdsa public keys are not equal") { + t.Fatalf("expected error with mismatched public keys, got %v", err) + } + + // Failure: Invalid certificate chain + otherRootCert, _, _ := test.GenerateRootCa() + pemChain, err = cryptoutils.MarshalCertificatesToPEM([]*x509.Certificate{subCert, otherRootCert}) + if err != nil { + t.Fatalf("error marshalling cert chain: %v", err) + } + err = os.WriteFile(certPath, pemChain, 0600) + if err != nil { + t.Fatalf("error writing pem chain: %v", err) + } + ca, err = NewKmsCA(context.WithValue(context.TODO(), fake.KmsCtxKey{}, priv), "fakekms://key", certPath) + if err == nil || !strings.Contains(err.Error(), "certificate signed by unknown authority") { + t.Fatalf("expected error with invalid certificate chain, got %v", err) + } +} diff --git a/pkg/test/cert_utils.go b/pkg/test/cert_utils.go new file mode 100644 index 000000000..db5116065 --- /dev/null +++ b/pkg/test/cert_utils.go @@ -0,0 +1,225 @@ +// Copyright 2022 The Sigstore Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package test + +import ( + "crypto" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "math/big" + "time" +) + +/* +To use: + +rootCert, rootKey, _ := GenerateRootCa() +subCert, subKey, _ := GenerateSubordinateCa(rootCert, rootKey) +leafCert, _, _ := GenerateLeafCert("subject", "oidc-issuer", subCert, subKey) + +roots := x509.NewCertPool() +subs := x509.NewCertPool() +roots.AddCert(rootCert) +subs.AddCert(subCert) +opts := x509.VerifyOptions{ + Roots: roots, + Intermediates: subs, + KeyUsages: []x509.ExtKeyUsage{ + x509.ExtKeyUsageCodeSigning, + }, +} +_, err := leafCert.Verify(opts) +*/ + +func createCertificate(template *x509.Certificate, parent *x509.Certificate, pub interface{}, priv crypto.Signer) (*x509.Certificate, error) { + certBytes, err := x509.CreateCertificate(rand.Reader, template, parent, pub, priv) + if err != nil { + return nil, err + } + + cert, err := x509.ParseCertificate(certBytes) + if err != nil { + return nil, err + } + return cert, nil +} + +func GenerateRootCa() (*x509.Certificate, *ecdsa.PrivateKey, error) { + rootTemplate := &x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ + CommonName: "sigstore", + Organization: []string{"sigstore.dev"}, + }, + NotBefore: time.Now().Add(-5 * time.Minute), + NotAfter: time.Now().Add(5 * time.Hour), + KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, + BasicConstraintsValid: true, + IsCA: true, + } + + priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + return nil, nil, err + } + + cert, err := createCertificate(rootTemplate, rootTemplate, &priv.PublicKey, priv) + if err != nil { + return nil, nil, err + } + + return cert, priv, nil +} + +func GenerateSubordinateCa(rootTemplate *x509.Certificate, rootPriv crypto.Signer) (*x509.Certificate, *ecdsa.PrivateKey, error) { + subTemplate := &x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ + CommonName: "sigstore-sub", + Organization: []string{"sigstore.dev"}, + }, + NotBefore: time.Now().Add(-2 * time.Minute), + NotAfter: time.Now().Add(2 * time.Hour), + KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageCodeSigning}, + BasicConstraintsValid: true, + IsCA: true, + } + + priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + return nil, nil, err + } + + cert, err := createCertificate(subTemplate, rootTemplate, &priv.PublicKey, rootPriv) + if err != nil { + return nil, nil, err + } + + return cert, priv, nil +} + +// GenerateSubordinateCaWithPublicKey takes a public key to sign instead of generating a keypair. +func GenerateSubordinateCaWithPublicKey(pub crypto.PublicKey, rootTemplate *x509.Certificate, rootPriv crypto.Signer) (*x509.Certificate, error) { + subTemplate := &x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ + CommonName: "sigstore-sub", + Organization: []string{"sigstore.dev"}, + }, + NotBefore: time.Now().Add(-2 * time.Minute), + NotAfter: time.Now().Add(2 * time.Hour), + KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageCodeSigning}, + BasicConstraintsValid: true, + IsCA: true, + } + + cert, err := createCertificate(subTemplate, rootTemplate, pub, rootPriv) + if err != nil { + return nil, err + } + + return cert, nil +} + +func GenerateWeakSubordinateCa(rootTemplate *x509.Certificate, rootPriv crypto.Signer) (*x509.Certificate, *ecdsa.PrivateKey, error) { + subTemplate := &x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ + CommonName: "sigstore-sub", + Organization: []string{"sigstore.dev"}, + }, + NotBefore: time.Now().Add(-2 * time.Minute), + NotAfter: time.Now().Add(2 * time.Hour), + KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageCodeSigning}, + BasicConstraintsValid: true, + IsCA: true, + } + + priv, err := ecdsa.GenerateKey(elliptic.P224(), rand.Reader) + if err != nil { + return nil, nil, err + } + + cert, err := createCertificate(subTemplate, rootTemplate, &priv.PublicKey, rootPriv) + if err != nil { + return nil, nil, err + } + + return cert, priv, nil +} + +func GenerateSubordinateCaWithoutEKU(rootTemplate *x509.Certificate, rootPriv crypto.Signer) (*x509.Certificate, *ecdsa.PrivateKey, error) { + subTemplate := &x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ + CommonName: "sigstore-sub", + Organization: []string{"sigstore.dev"}, + }, + NotBefore: time.Now().Add(-2 * time.Minute), + NotAfter: time.Now().Add(2 * time.Hour), + KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, + BasicConstraintsValid: true, + IsCA: true, + } + + priv, err := ecdsa.GenerateKey(elliptic.P224(), rand.Reader) + if err != nil { + return nil, nil, err + } + + cert, err := createCertificate(subTemplate, rootTemplate, &priv.PublicKey, rootPriv) + if err != nil { + return nil, nil, err + } + + return cert, priv, nil +} + +func GenerateLeafCert(subject string, oidcIssuer string, parentTemplate *x509.Certificate, parentPriv crypto.Signer) (*x509.Certificate, *ecdsa.PrivateKey, error) { + certTemplate := &x509.Certificate{ + SerialNumber: big.NewInt(1), + EmailAddresses: []string{subject}, + NotBefore: time.Now().Add(-1 * time.Minute), + NotAfter: time.Now().Add(time.Hour), + KeyUsage: x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageCodeSigning}, + IsCA: false, + ExtraExtensions: []pkix.Extension{{ + // OID for OIDC Issuer extension + Id: asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 1}, + Critical: false, + Value: []byte(oidcIssuer), + }}, + } + + priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + return nil, nil, err + } + + cert, err := createCertificate(certTemplate, parentTemplate, &priv.PublicKey, parentPriv) + if err != nil { + return nil, nil, err + } + + return cert, priv, nil +}