From d81bb7831127b80dd76df7c989214900647a995b Mon Sep 17 00:00:00 2001 From: Kieran Miller Date: Mon, 1 Nov 2021 20:26:41 +0100 Subject: [PATCH 1/9] Implemented PKCS11 support. Signed-off-by: Kieran Miller --- .goreleaser.yml | 15 ++ Makefile | 3 + PKCS11.md | 89 ++++++++ cmd/cosign/cli/commands.go | 1 + cmd/cosign/cli/options/pkcs11_tool.go | 54 +++++ cmd/cosign/cli/pkcs11_tool.go | 80 +++++++ cmd/cosign/cli/pkcs11_tool_disabled.go | 29 +++ cmd/cosign/cli/pkcs11cli/commands.go | 196 ++++++++++++++++ cmd/cosign/cli/publickey/public_key.go | 31 ++- cmd/cosign/cli/sign/sign.go | 43 ++++ cmd/cosign/cli/verify/verify.go | 30 ++- cmd/cosign/cli/verify/verify_attestation.go | 28 ++- cmd/cosign/cli/verify/verify_blob.go | 28 ++- doc/cosign_pkcs11-tool.md | 25 +++ doc/cosign_pkcs11-tool_list-keys-uris.md | 31 +++ doc/cosign_pkcs11-tool_list-tokens.md | 29 +++ go.mod | 2 + go.sum | 3 + pkg/cosign/pkcs11key/disabled.go | 69 ++++++ pkg/cosign/pkcs11key/pkcs11key.go | 236 ++++++++++++++++++++ pkg/cosign/pkcs11key/util.go | 218 ++++++++++++++++++ 21 files changed, 1227 insertions(+), 13 deletions(-) create mode 100644 PKCS11.md create mode 100644 cmd/cosign/cli/options/pkcs11_tool.go create mode 100644 cmd/cosign/cli/pkcs11_tool.go create mode 100644 cmd/cosign/cli/pkcs11_tool_disabled.go create mode 100644 cmd/cosign/cli/pkcs11cli/commands.go create mode 100644 doc/cosign_pkcs11-tool.md create mode 100644 doc/cosign_pkcs11-tool_list-keys-uris.md create mode 100644 doc/cosign_pkcs11-tool_list-tokens.md create mode 100644 pkg/cosign/pkcs11key/disabled.go create mode 100644 pkg/cosign/pkcs11key/pkcs11key.go create mode 100644 pkg/cosign/pkcs11key/util.go diff --git a/.goreleaser.yml b/.goreleaser.yml index de3bb5ba448..57218db6c79 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -48,6 +48,21 @@ builds: env: - PKG_CONFIG_PATH="/usr/lib/x86_64-linux-gnu/pkgconfig/" +- id: linux-pkcs11key-amd64 + binary: cosign-linux-pkcs11key-amd64 + main: ./cmd/cosign + flags: + - -trimpath + mod_timestamp: '{{ .CommitTimestamp }}' + goos: + - linux + goarch: + - amd64 + ldflags: + - "{{ .Env.LDFLAGS }}" + tags: + - pkcs11key + - id: darwin-amd64 binary: cosign-darwin-amd64 no_unique_dist_dir: true diff --git a/Makefile b/Makefile index d20ed044fe3..29eb55bb004 100644 --- a/Makefile +++ b/Makefile @@ -61,6 +61,9 @@ cosign: $(SRCS) cosign-pivkey: $(SRCS) CGO_ENABLED=1 go build -trimpath -tags=pivkey -ldflags $(LDFLAGS) -o cosign ./cmd/cosign +cosign-pkcs11key: $(SRCS) + go build -trimpath -tags=pkcs11key -ldflags $(LDFLAGS) -o cosign ./cmd/cosign + GOLANGCI_LINT_DIR = $(shell pwd)/bin GOLANGCI_LINT_BIN = $(GOLANGCI_LINT_DIR)/golangci-lint golangci-lint: diff --git a/PKCS11.md b/PKCS11.md new file mode 100644 index 00000000000..4e924765bc1 --- /dev/null +++ b/PKCS11.md @@ -0,0 +1,89 @@ +# PKCS11 Tokens + +The `cosign` command line tool optionally supports PKCS11 tokens for signing. +This support is enabled through the [crypto11](https://github.com/ThalesIgnite/crypto11) and the [pkcs11](https://github.com/miekg/pkcs11) libraries, which are not included in the standard release. Use [`make cosign-pkcs11key`](https://github.com/sigstore/cosign/blob/a8d1cc1132d4a019a62ff515b9375c8c5b98a5c5/Makefile#L52), or `go build -tags=pkcs11key`, to build `cosign` with support for PKCS11 tokens. + +## Quick Start + +### Setup + +To get started, make sure you already have your PKCS11 module installed, and insert your PKCS11-compatible token. + +Then, run the command `cosign pkcs11-tool list-tokens` to get the slot id of your token, as follows : + +```shell +$ cosign pkcs11-tool list-tokens --module-path /usr/local/lib/libp11.so +Listing tokens of PKCS11 module '/usr/local/lib/libp11.so' +Token in slot 1 + Label: TokenLabel + Manufacturer: Token Manufacturer + Model: Token Model + S/N: 68800ca5c75e074c +``` + +Afterwards, run the command `cosign pkcs11-tool list-keys-uris` to retrieve the PKCS11 URI of the key you wish to use, as follows : + +```shell +$ cosign pkcs11-tool list-keys-uris --module-path /usr/local/lib/libp11.so --slot-id 1 --pin 1234 +Listing URIs of keys in slot '1' of PKCS11 module '/usr/local/lib/libp11.so' +Object 0 + Label: key_label_1 + ID: 4a8d2f6ed9c4152b260d6c74a1ae72fcfdc64b65 + URI: pkcs11:token=TokenLabel;slot-id=1;id=%4a%8d%2f%6e%d9%c4%15%2b%26%0d%6c%74%a1%ae%72%fc%fd%c6%4b%65?module-path=/usr/local/lib/libp11.so&pin-value=1234 +Object 1 + Label: key_label_2 + ID: 57b39235cc6dec404c2310d7e37d5cbb5f1bba70 + URI: pkcs11:token=TokenLabel;slot-id=1;id=%57%b3%92%35%cc%6d%ec%40%4c%23%10%d7%e3%7d%5c%bb%5f%1b%ba%70?module-path=/usr/local/lib/libp11.so&pin-value=1234 +``` + +You can also construct the PKCS11 URI of your key manually by providing the following URI components : + +* **module-path** : the absolute path to the PKCS11 module **(optional)** + +* **token** and/or **slot-id** : either or both of the PKCS11 token label and the PKCS11 slot id **(mandatory)** + +* **object** and/or **id** : either or both of the PKCS11 key label and the PKCS11 key id **(mandatory)** + +* **pin-value** : the PIN of the PKCS11 token **(optional)** + +If `module-path` is not present in the URI, `cosign` expects the PKCS11 module path to be set using the environment variable `COSIGN_PKCS11_MODULE_PATH`. If neither are set, `cosign` will fail. If both are set, `module-path` has priority over `COSIGN_PKCS11_MODULE_PATH` environment variable. + +If `pin-value` is not present in the URI, `cosign` expects the PIN to be set using the environment variable `COSIGN_PKCS11_PIN`. If it is not, `cosign` checks whether the PKCS11 token requires user login (flag CKF_LOGIN_REQUIRED set), and if so, `cosign` will invite the user to enter the PIN only during signing. If both `pin-value` and `COSIGN_PKCS11_PIN` environment variable are set, `pin-value` has priority over `COSIGN_PKCS11_PIN`. + +### Signing + +You can then use the normal `cosign` commands to sign images and blobs with your PKCS11 key. + +```shell +$ cosign sign --key "" gcr.io/dlorenc-vmtest2/demo +Pushing signature to: gcr.io/dlorenc-vmtest2/demo:sha256-410a07f17151ffffb513f942a01748dfdb921de915ea6427d61d60b0357c1dcd.sig +``` + +To verify, you can either use the PKCS11 token key directly: + +```shell +$ cosign verify --key "" gcr.io/dlorenc-vmtest2/demo +Verification for gcr.io/dlorenc-vmtest2/demo -- +The following checks were performed on each of these signatures: +- The cosign claims were validated +- The signatures were verified against the specified public key +- Any certificates were verified against the Fulcio roots. + +[{"critical":{"identity":{"docker-reference":"gcr.io/dlorenc-vmtest2/demo"},"image":{"docker-manifest-digest":"sha256:410a07f17151ffffb513f942a01748dfdb921de915ea6427d61d60b0357c1dcd"},"type":"cosign container image signature"},"optional":null}] +``` + +Or export the public key and verify against that: + +```shell +$ cosign public-key --key "" > pub.key + +$ cosign verify --key pub.key gcr.io/dlorenc-vmtest2/demo +Verification for gcr.io/dlorenc-vmtest2/demo -- +The following checks were performed on each of these signatures: +- The cosign claims were validated +- The signatures were verified against the specified public key +- Any certificates were verified against the Fulcio roots. + +[{"critical":{"identity":{"docker-reference":"gcr.io/dlorenc-vmtest2/demo"},"image":{"docker-manifest-digest":"sha256:410a07f17151ffffb513f942a01748dfdb921de915ea6427d61d60b0357c1dcd"},"type":"cosign container image signature"},"optional":null}] + +``` diff --git a/cmd/cosign/cli/commands.go b/cmd/cosign/cli/commands.go index 2b425e10bf4..75019f6062b 100644 --- a/cmd/cosign/cli/commands.go +++ b/cmd/cosign/cli/commands.go @@ -70,6 +70,7 @@ func New() *cobra.Command { cmd.AddCommand(Initialize()) cmd.AddCommand(Manifest()) cmd.AddCommand(PIVTool()) + cmd.AddCommand(PKCS11Tool()) cmd.AddCommand(Policy()) cmd.AddCommand(PublicKey()) cmd.AddCommand(Sign()) diff --git a/cmd/cosign/cli/options/pkcs11_tool.go b/cmd/cosign/cli/options/pkcs11_tool.go new file mode 100644 index 00000000000..d476e0dd7fc --- /dev/null +++ b/cmd/cosign/cli/options/pkcs11_tool.go @@ -0,0 +1,54 @@ +// +// Copyright 2021 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 options + +import ( + "github.com/spf13/cobra" +) + +// PKCS11ToolListTokens is the wrapper for `pkcs11-tool list-tokens` related options. +type PKCS11ToolListTokensOptions struct { + ModulePath string +} + +var _ Interface = (*PKCS11ToolListTokensOptions)(nil) + +// AddFlags implements Interface +func (o *PKCS11ToolListTokensOptions) AddFlags(cmd *cobra.Command) { + cmd.Flags().StringVar(&o.ModulePath, "module-path", "", + "absolute path to the PKCS11 module") +} + +// PKCS11ToolListKeysUrisOptions is the wrapper for `pkcs11-tool list-keys-uris` related options. +type PKCS11ToolListKeysUrisOptions struct { + ModulePath string + SlotId uint + Pin string +} + +var _ Interface = (*PKCS11ToolListKeysUrisOptions)(nil) + +// AddFlags implements Interface +func (o *PKCS11ToolListKeysUrisOptions) AddFlags(cmd *cobra.Command) { + cmd.Flags().StringVar(&o.ModulePath, "module-path", "", + "absolute path to the PKCS11 module") + + cmd.Flags().UintVar(&o.SlotId, "slot-id", 0, + "id of the PKCS11 slot, uses 0 if empty") + + cmd.Flags().StringVar(&o.Pin, "pin", "", + "pin of the PKCS11 slot, uses environment variable COSIGN_PKCS11_PIN if empty") +} diff --git a/cmd/cosign/cli/pkcs11_tool.go b/cmd/cosign/cli/pkcs11_tool.go new file mode 100644 index 00000000000..922652ecc81 --- /dev/null +++ b/cmd/cosign/cli/pkcs11_tool.go @@ -0,0 +1,80 @@ +//go:build pkcs11key +// +build pkcs11key + +// Copyright 2021 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 cli + +import ( + "github.com/spf13/cobra" + + "github.com/sigstore/cosign/cmd/cosign/cli/options" + "github.com/sigstore/cosign/cmd/cosign/cli/pkcs11cli" +) + +var pkcs11ToolForce bool + +func PKCS11Tool() *cobra.Command { + cmd := &cobra.Command{ + Use: "pkcs11-tool", + Short: "Provides utilities for retrieving information from a PKCS11 token.", + } + + cmd.AddCommand( + pkcs11ToolListTokens(), + PKCS11ToolListKeysUrisOptions(), + ) + + // TODO: drop -f in favor of --no-input only + // TODO: use the force flag. + cmd.PersistentFlags().BoolVarP(&pkcs11ToolForce, "no-input", "f", false, + "skip warnings and confirmations") + + return cmd +} + +func pkcs11ToolListTokens() *cobra.Command { + o := &options.PKCS11ToolListTokensOptions{} + + cmd := &cobra.Command{ + Use: "list-tokens", + Short: "list-tokens lists all PKCS11 tokens linked to a PKCS11 module", + Args: cobra.ExactArgs(0), + RunE: func(cmd *cobra.Command, args []string) error { + return pkcs11cli.ListTokensCmd(cmd.Context(), o.ModulePath) + }, + } + + o.AddFlags(cmd) + + return cmd +} + +func PKCS11ToolListKeysUrisOptions() *cobra.Command { + o := &options.PKCS11ToolListKeysUrisOptions{} + + cmd := &cobra.Command{ + Use: "list-keys-uris", + Short: "list-keys-uris lists URIs of all keys in a PKCS11 token", + Args: cobra.ExactArgs(0), + RunE: func(cmd *cobra.Command, args []string) error { + return pkcs11cli.ListKeysUrisCmd(cmd.Context(), o.ModulePath, o.SlotId, o.Pin) + }, + } + + o.AddFlags(cmd) + + return cmd +} diff --git a/cmd/cosign/cli/pkcs11_tool_disabled.go b/cmd/cosign/cli/pkcs11_tool_disabled.go new file mode 100644 index 00000000000..a073c37a380 --- /dev/null +++ b/cmd/cosign/cli/pkcs11_tool_disabled.go @@ -0,0 +1,29 @@ +//go:build !pkcs11key +// +build !pkcs11key + +// Copyright 2021 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 cli + +import ( + "github.com/spf13/cobra" +) + +func PKCS11Tool() *cobra.Command { + return &cobra.Command{ + Use: "pkcs11-tool", + Short: "This cosign was not built with pkcs11-tool support!", + } +} diff --git a/cmd/cosign/cli/pkcs11cli/commands.go b/cmd/cosign/cli/pkcs11cli/commands.go new file mode 100644 index 00000000000..fe6c353db31 --- /dev/null +++ b/cmd/cosign/cli/pkcs11cli/commands.go @@ -0,0 +1,196 @@ +//go:build pkcs11key +// +build pkcs11key + +// Copyright 2021 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 pkcs11cli + +import ( + "context" + "encoding/hex" + "flag" + "fmt" + "os" + "path/filepath" + + "github.com/ThalesIgnite/crypto11" + "github.com/miekg/pkcs11" + "github.com/pkg/errors" + "github.com/sigstore/cosign/pkg/cosign/pkcs11key" + "golang.org/x/term" +) + +func ListTokensCmd(_ context.Context, modulePath string) error { + if modulePath == "" || !filepath.IsAbs(modulePath) { + return flag.ErrHelp + } + + // Initialize PKCS11 module. + p := pkcs11.New(modulePath) + if p == nil { + return errors.New("failed to load PKCS11 module") + } + err := p.Initialize() + if err != nil { + return errors.Wrap(err, "initialize PKCS11 module") + } + defer p.Destroy() + defer p.Finalize() + + // Get all slots with a token, and print info. + slots, err := p.GetSlotList(true) + if err != nil { + return errors.Wrap(err, "get slot list of PKCS11 module") + } + fmt.Fprintf(os.Stdout, "Listing tokens of PKCS11 module '%s'\n", modulePath) + for _, slot := range slots { + tokenInfo, err := p.GetTokenInfo(slot) + if err != nil { + return errors.Wrap(err, "get token info") + } + + fmt.Fprintf(os.Stdout, "Token in slot %d\n", slot) + fmt.Fprintf(os.Stdout, "\tLabel: %s\n", tokenInfo.Label) + fmt.Fprintf(os.Stdout, "\tManufacturer: %s\n", tokenInfo.ManufacturerID) + fmt.Fprintf(os.Stdout, "\tModel: %s\n", tokenInfo.Model) + fmt.Fprintf(os.Stdout, "\tS/N: %s\n\n", tokenInfo.SerialNumber) + } + + return nil +} + +func ListKeysUrisCmd(_ context.Context, modulePath string, slotId uint, pin string) error { + if modulePath == "" || !filepath.IsAbs(modulePath) { + return flag.ErrHelp + } + + // Initialize PKCS11 module. + ctx := pkcs11.New(modulePath) + if ctx == nil { + return errors.New("failed to load PKCS11 module") + } + err := ctx.Initialize() + if err != nil { + return errors.Wrap(err, "initialize PKCS11 module") + } + defer ctx.Destroy() + defer ctx.Finalize() + + // Get token Info. + var tokenInfo pkcs11.TokenInfo + tokenInfo, err = ctx.GetTokenInfo(uint(slotId)) + if err != nil { + return errors.Wrap(err, "get token info") + } + + // If pin was not given, check COSIGN_PKCS11_PIN environment variable. + if pin == "" { + pin = os.Getenv("COSIGN_PKCS11_PIN") + + // If COSIGN_PKCS11_PIN was not set, check if CKF_LOGIN_REQUIRED is set in Token Info. + // If it is, ask the user for the PIN, otherwise, do not. + if pin == "" { + if tokenInfo.Flags&pkcs11.CKF_LOGIN_REQUIRED == pkcs11.CKF_LOGIN_REQUIRED { + fmt.Fprintf(os.Stderr, "Enter PIN for PKCS11 token '%s': ", tokenInfo.Label) + b, err := term.ReadPassword(0) + if err != nil { + return errors.Wrap(err, "get pin") + } + pin = string(b) + } + } + } + + // Open a new session to the token. + session, err := ctx.OpenSession(slotId, pkcs11.CKF_SERIAL_SESSION|pkcs11.CKF_RW_SESSION) + if err != nil { + return errors.Wrap(err, "open session") + } + defer ctx.CloseSession(session) + + // Login user. + err = ctx.Login(session, pkcs11.CKU_USER, pin) + if err != nil { + return errors.Wrap(err, "login") + } + defer ctx.Logout(session) + + // Look for private keys. + maxHandlePerFind := 20 + var handles []pkcs11.ObjectHandle + findAttributes := make(crypto11.AttributeSet) + err = findAttributes.Set(crypto11.CkaClass, pkcs11.CKO_PRIVATE_KEY) + if err != nil { + return errors.Wrap(err, "set attributes") + } + if err = ctx.FindObjectsInit(session, findAttributes.ToSlice()); err != nil { + return errors.Wrap(err, "init find objects") + } + newhandles, _, err := ctx.FindObjects(session, maxHandlePerFind) + if err != nil { + return errors.Wrap(err, "find objects") + } + for len(newhandles) > 0 { + handles = append(handles, newhandles...) + newhandles, _, err = ctx.FindObjects(session, maxHandlePerFind) + if err != nil { + return errors.Wrap(err, "find objects") + } + } + err = ctx.FindObjectsFinal(session) + if err != nil { + return errors.Wrap(err, "finalize find objects") + } + + // For each private key, get key label and key id then construct uri. + i := 0 + fmt.Fprintf(os.Stdout, "Listing URIs of keys in slot '%d' of PKCS11 module '%s'\n", slotId, modulePath) + for _, handle := range handles { + attributes := []*pkcs11.Attribute{ + pkcs11.NewAttribute(pkcs11.CKA_ID, nil), + pkcs11.NewAttribute(pkcs11.CKA_LABEL, nil), + } + if attributes, err = ctx.GetAttributeValue(session, handle, attributes); err != nil { + return errors.Wrap(err, "get attributes") + } + keyId := attributes[0].Value + keyLabel := attributes[1].Value + + // If the object has neither a key id nor a key label, we skip it. + if (keyId == nil || len(keyId) == 0) && (keyLabel == nil || len(keyLabel) == 0) { + continue + } + + slotIdInt := int(slotId) + pkcs11Uri := pkcs11key.NewPkcs11UriConfigFromInput(modulePath, &slotIdInt, tokenInfo.Label, keyLabel, keyId, pin) + pkcs11UriStr, err := pkcs11Uri.Construct() + if err != nil { + return errors.Wrap(err, "construct pkcs11 uri") + } + + fmt.Fprintf(os.Stdout, "Object %d\n", i) + if keyLabel != nil && len(keyLabel) != 0 { + fmt.Fprintf(os.Stdout, "\tLabel: %s\n", string(keyLabel)) + } + if keyId != nil && len(keyId) != 0 { + fmt.Fprintf(os.Stdout, "\tID: %s\n", hex.EncodeToString(keyId)) + } + fmt.Fprintf(os.Stdout, "\tURI: %s\n", pkcs11UriStr) + + i++ + } + + return nil +} diff --git a/cmd/cosign/cli/publickey/public_key.go b/cmd/cosign/cli/publickey/public_key.go index 528e5fc0289..de805834e94 100644 --- a/cmd/cosign/cli/publickey/public_key.go +++ b/cmd/cosign/cli/publickey/public_key.go @@ -20,11 +20,13 @@ import ( "fmt" "io" "os" + "strings" "github.com/pkg/errors" "github.com/sigstore/cosign/pkg/cosign" "github.com/sigstore/cosign/pkg/cosign/pivkey" + "github.com/sigstore/cosign/pkg/cosign/pkcs11key" sigs "github.com/sigstore/cosign/pkg/signature" "github.com/sigstore/sigstore/pkg/signature" signatureoptions "github.com/sigstore/sigstore/pkg/signature/options" @@ -45,11 +47,32 @@ func GetPublicKey(ctx context.Context, opts Pkopts, writer NamedWriter, pf cosig var k signature.PublicKeyProvider switch { case opts.KeyRef != "": - s, err := sigs.SignerFromKeyRef(ctx, opts.KeyRef, pf) - if err != nil { - return err + // If -key starts with pkcs11:, we assume it is a PKCS11 URI and use it to get the PKCS11 Key. + if strings.HasPrefix(opts.KeyRef, "pkcs11:") { + pkcs11UriConfig := pkcs11key.NewPkcs11UriConfig() + err := pkcs11UriConfig.Parse(opts.KeyRef) + if err != nil { + return errors.Wrap(err, "parsing pkcs11 uri") + } + + sk, err := pkcs11key.GetKeyWithUriConfig(pkcs11UriConfig, false) + if err != nil { + return errors.Wrap(err, "opening pkcs11 token key") + } + defer sk.Close() + + pk, err := sk.Verifier() + if err != nil { + return errors.Wrap(err, "initializing pkcs11 token verifier") + } + k = pk + } else { + s, err := sigs.SignerFromKeyRef(ctx, opts.KeyRef, pf) + if err != nil { + return err + } + k = s } - k = s case opts.Sk: sk, err := pivkey.GetKeyWithSlot(opts.Slot) if err != nil { diff --git a/cmd/cosign/cli/sign/sign.go b/cmd/cosign/cli/sign/sign.go index f163ec51a90..1bd6cbf52c8 100644 --- a/cmd/cosign/cli/sign/sign.go +++ b/cmd/cosign/cli/sign/sign.go @@ -40,6 +40,7 @@ import ( "github.com/sigstore/cosign/cmd/cosign/cli/options" "github.com/sigstore/cosign/pkg/cosign" "github.com/sigstore/cosign/pkg/cosign/pivkey" + "github.com/sigstore/cosign/pkg/cosign/pkcs11key" cremote "github.com/sigstore/cosign/pkg/cosign/remote" "github.com/sigstore/cosign/pkg/oci" ociempty "github.com/sigstore/cosign/pkg/oci/empty" @@ -326,6 +327,48 @@ func SignerFromKeyOpts(ctx context.Context, certPath string, ko KeyOpts) (*CertS }, nil case ko.KeyRef != "": + // If -key starts with pkcs11:, we assume it is a PKCS11 URI and use it to get the PKCS11 Key. + if strings.HasPrefix(ko.KeyRef, "pkcs11:") { + pkcs11UriConfig := pkcs11key.NewPkcs11UriConfig() + err := pkcs11UriConfig.Parse(ko.KeyRef) + if err != nil { + return nil, err + } + + // Since we'll be signing, we need to set askForPinIsNeeded to true + // because we need access to the private key. + sk, err := pkcs11key.GetKeyWithUriConfig(pkcs11UriConfig, true) + if err != nil { + return nil, err + } + + sv, err := sk.SignerVerifier() + if err != nil { + return nil, err + } + + // Handle the -cert flag. + // With PKCS11, we assume the certificate is in the same slot on the PKCS11 + // token as the private key. If it's not there, show a warning to the + // user. + certFromPKCS11, err := sk.Certificate() + var pemBytes []byte + if err != nil { + fmt.Fprintln(os.Stderr, "warning: no x509 certificate retrieved from the PKCS11 token") + } else { + pemBytes, err = cryptoutils.MarshalCertificateToPEM(certFromPKCS11) + if err != nil { + return nil, err + } + } + + return &CertSignVerifier{ + Cert: pemBytes, + SignerVerifier: sv, + close: sk.Close, + }, nil + } + k, err := sigs.SignerVerifierFromKeyRef(ctx, ko.KeyRef, ko.PassFunc) if err != nil { return nil, errors.Wrap(err, "reading key") diff --git a/cmd/cosign/cli/verify/verify.go b/cmd/cosign/cli/verify/verify.go index b1666c8e374..e6f475c0143 100644 --- a/cmd/cosign/cli/verify/verify.go +++ b/cmd/cosign/cli/verify/verify.go @@ -22,6 +22,7 @@ import ( "flag" "fmt" "os" + "strings" "github.com/google/go-containerregistry/pkg/name" "github.com/pkg/errors" @@ -31,6 +32,7 @@ import ( "github.com/sigstore/cosign/cmd/cosign/cli/sign" "github.com/sigstore/cosign/pkg/cosign" "github.com/sigstore/cosign/pkg/cosign/pivkey" + "github.com/sigstore/cosign/pkg/cosign/pkcs11key" "github.com/sigstore/cosign/pkg/oci" sigs "github.com/sigstore/cosign/pkg/signature" "github.com/sigstore/sigstore/pkg/signature" @@ -89,9 +91,31 @@ func (c *VerifyCommand) Exec(ctx context.Context, images []string) (err error) { // Keys are optional! var pubKey signature.Verifier if keyRef != "" { - pubKey, err = sigs.PublicKeyFromKeyRef(ctx, keyRef) - if err != nil { - return errors.Wrap(err, "loading public key") + // If -key starts with pkcs11:, we assume it is a PKCS11 URI and use it to get the PKCS11 Key. + if strings.HasPrefix(keyRef, "pkcs11:") { + pkcs11UriConfig := pkcs11key.NewPkcs11UriConfig() + err := pkcs11UriConfig.Parse(keyRef) + if err != nil { + return errors.Wrap(err, "parsing pkcs11 uri") + } + + // Since we'll be verifying a signature, we do not need to set askForPinIsNeeded to true + // because we only need access to the public key. + sk, err := pkcs11key.GetKeyWithUriConfig(pkcs11UriConfig, false) + if err != nil { + return errors.Wrap(err, "opening pkcs11 token key") + } + defer sk.Close() + + pubKey, err = sk.Verifier() + if err != nil { + return errors.Wrap(err, "initializing pkcs11 token verifier") + } + } else { + pubKey, err = sigs.PublicKeyFromKeyRef(ctx, keyRef) + if err != nil { + return errors.Wrap(err, "loading public key") + } } } else if c.Sk { sk, err := pivkey.GetKeyWithSlot(c.Slot) diff --git a/cmd/cosign/cli/verify/verify_attestation.go b/cmd/cosign/cli/verify/verify_attestation.go index d689d15d5dd..f8ace612707 100644 --- a/cmd/cosign/cli/verify/verify_attestation.go +++ b/cmd/cosign/cli/verify/verify_attestation.go @@ -23,10 +23,12 @@ import ( "fmt" "os" "path/filepath" + "strings" "github.com/google/go-containerregistry/pkg/name" "github.com/in-toto/in-toto-golang/in_toto" "github.com/pkg/errors" + "github.com/sigstore/cosign/pkg/cosign/pkcs11key" "github.com/sigstore/cosign/pkg/cosign/rego" "github.com/sigstore/cosign/cmd/cosign/cli/fulcio" @@ -80,9 +82,29 @@ func (c *VerifyAttestationCommand) Exec(ctx context.Context, images []string) (e // Keys are optional! if keyRef != "" { - co.SigVerifier, err = sigs.PublicKeyFromKeyRef(ctx, keyRef) - if err != nil { - return errors.Wrap(err, "loading public key") + // If -key starts with pkcs11:, we assume it is a PKCS11 URI and use it to get the PKCS11 Key. + if strings.HasPrefix(keyRef, "pkcs11:") { + pkcs11UriConfig := pkcs11key.NewPkcs11UriConfig() + err := pkcs11UriConfig.Parse(keyRef) + if err != nil { + return errors.Wrap(err, "parsing pkcs11 uri") + } + + sk, err := pkcs11key.GetKeyWithUriConfig(pkcs11UriConfig, false) + if err != nil { + return errors.Wrap(err, "opening pkcs11 token key") + } + defer sk.Close() + + co.SigVerifier, err = sk.Verifier() + if err != nil { + return errors.Wrap(err, "initializing pkcs11 token verifier") + } + } else { + co.SigVerifier, err = sigs.PublicKeyFromKeyRef(ctx, keyRef) + if err != nil { + return errors.Wrap(err, "loading public key") + } } } else if c.Sk { sk, err := pivkey.GetKeyWithSlot(c.Slot) diff --git a/cmd/cosign/cli/verify/verify_blob.go b/cmd/cosign/cli/verify/verify_blob.go index 2c2f826bce2..21e2d10c9cd 100644 --- a/cmd/cosign/cli/verify/verify_blob.go +++ b/cmd/cosign/cli/verify/verify_blob.go @@ -26,6 +26,7 @@ import ( "fmt" "io" "os" + "strings" "github.com/pkg/errors" @@ -35,6 +36,7 @@ import ( "github.com/sigstore/cosign/pkg/blob" "github.com/sigstore/cosign/pkg/cosign" "github.com/sigstore/cosign/pkg/cosign/pivkey" + "github.com/sigstore/cosign/pkg/cosign/pkcs11key" sigs "github.com/sigstore/cosign/pkg/signature" rekorClient "github.com/sigstore/rekor/pkg/client" "github.com/sigstore/sigstore/pkg/cryptoutils" @@ -60,9 +62,29 @@ func VerifyBlobCmd(ctx context.Context, ko sign.KeyOpts, certRef, sigRef, blobRe // Keys are optional! switch { case ko.KeyRef != "": - pubKey, err = sigs.PublicKeyFromKeyRef(ctx, ko.KeyRef) - if err != nil { - return errors.Wrap(err, "loading public key") + // If -key starts with pkcs11:, we assume it is a PKCS11 URI and use it to get the PKCS11 Key. + if strings.HasPrefix(ko.KeyRef, "pkcs11:") { + pkcs11UriConfig := pkcs11key.NewPkcs11UriConfig() + err := pkcs11UriConfig.Parse(ko.KeyRef) + if err != nil { + return errors.Wrap(err, "parsing pkcs11 uri") + } + + sk, err := pkcs11key.GetKeyWithUriConfig(pkcs11UriConfig, false) + if err != nil { + return errors.Wrap(err, "opening pkcs11 token key") + } + defer sk.Close() + + pubKey, err = sk.Verifier() + if err != nil { + return errors.Wrap(err, "initializing pkcs11 token verifier") + } + } else { + pubKey, err = sigs.PublicKeyFromKeyRef(ctx, ko.KeyRef) + if err != nil { + return errors.Wrap(err, "loading public key") + } } case ko.Sk: sk, err := pivkey.GetKeyWithSlot(ko.Slot) diff --git a/doc/cosign_pkcs11-tool.md b/doc/cosign_pkcs11-tool.md new file mode 100644 index 00000000000..11362b0a39f --- /dev/null +++ b/doc/cosign_pkcs11-tool.md @@ -0,0 +1,25 @@ +## cosign pkcs11-tool + +Provides utilities for retrieving information from a PKCS11 token + +### Options + +``` + -h, --help help for pkcs11-tool + -f, --no-input skip warnings and confirmations +``` + +### Options inherited from parent commands + +``` + --azure-container-registry-config string Path to the file containing Azure container registry configuration information. + --output-file string log output to a file + -d, --verbose log debug output +``` + +### SEE ALSO + +* [cosign](cosign.md) - +* [cosign pkcs11-tool list-tokens](cosign_pkcs11-tool_list-tokens.md) - list-tokens lists all PKCS11 tokens linked to a PKCS11 module +* [cosign pkcs11-tool list-keys-uris](cosign_pkcs11-tool_list-keys-uris.md) - list-keys-uris lists URIs of all keys in a PKCS11 token + diff --git a/doc/cosign_pkcs11-tool_list-keys-uris.md b/doc/cosign_pkcs11-tool_list-keys-uris.md new file mode 100644 index 00000000000..e4c8e2f4923 --- /dev/null +++ b/doc/cosign_pkcs11-tool_list-keys-uris.md @@ -0,0 +1,31 @@ +## cosign pkcs11-tool list-keys-uris + +lists URIs of all keys in a PKCS11 token + +``` +cosign pkcs11-tool list-keys-uris [flags] +``` + +### Options + +``` + -h, --help help for list-keys-uris + --module-path string absolute path to the PKCS11 module + --pin string pin of the PKCS11 slot, uses environment variable COSIGN_PKCS11_PIN if empty + --slot-id uint id of the PKCS11 slot, uses 0 if empty + +``` + +### Options inherited from parent commands + +``` + --azure-container-registry-config string Path to the file containing Azure container registry configuration information. + -f, --no-input skip warnings and confirmations + --output-file string log output to a file + -d, --verbose log debug output +``` + +### SEE ALSO + +* [cosign pkcs11-tool](cosign_pkcs11-tool.md) - Provides utilities for retrieving information from a PKCS11 token + diff --git a/doc/cosign_pkcs11-tool_list-tokens.md b/doc/cosign_pkcs11-tool_list-tokens.md new file mode 100644 index 00000000000..efe25101c45 --- /dev/null +++ b/doc/cosign_pkcs11-tool_list-tokens.md @@ -0,0 +1,29 @@ +## cosign pkcs11-tool list-tokens + +lists all PKCS11 tokens linked to a PKCS11 module + +``` +cosign pkcs11-tool list-tokens [flags] +``` + +### Options + +``` + -h, --help help for list-tokens + --module-path string absolute path to the PKCS11 module + +``` + +### Options inherited from parent commands + +``` + --azure-container-registry-config string Path to the file containing Azure container registry configuration information. + -f, --no-input skip warnings and confirmations + --output-file string log output to a file + -d, --verbose log debug output +``` + +### SEE ALSO + +* [cosign pkcs11-tool](cosign_pkcs11-tool.md) - Provides utilities for retrieving information from a PKCS11 token + diff --git a/go.mod b/go.mod index a7dff776fb7..0aa868aed31 100644 --- a/go.mod +++ b/go.mod @@ -33,6 +33,7 @@ require ( require ( cloud.google.com/go/kms v1.0.0 // indirect cuelang.org/go v0.4.0 + github.com/ThalesIgnite/crypto11 v1.2.4 github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect github.com/envoyproxy/protoc-gen-validate v0.6.1 // indirect @@ -41,6 +42,7 @@ require ( github.com/google/go-github/v39 v39.2.0 github.com/imdario/mergo v0.3.12 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect + github.com/miekg/pkcs11 v1.0.3 // indirect github.com/onsi/gomega v1.16.0 // indirect github.com/open-policy-agent/opa v0.34.0 github.com/prometheus/procfs v0.7.3 // indirect diff --git a/go.sum b/go.sum index b91637ad294..f3066e23275 100644 --- a/go.sum +++ b/go.sum @@ -203,6 +203,7 @@ github.com/ReneKroon/ttlcache/v2 v2.7.0/go.mod h1:mBxvsNY+BT8qLLd6CuAJubbKo6r0jh github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/ThalesIgnite/crypto11 v1.2.4 h1:3MebRK/U0mA2SmSthXAIZAdUA9w8+ZuKem2O6HuR1f8= github.com/ThalesIgnite/crypto11 v1.2.4/go.mod h1:ILDKtnCKiQ7zRoNxcp36Y1ZR8LBPmR2E23+wTQe/MlE= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= @@ -1242,6 +1243,7 @@ github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3N github.com/miekg/dns v1.1.17/go.mod h1:WgzbA6oji13JREwiNsRDNfl7jYdPnmz+VEuLrA+/48M= github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/miekg/pkcs11 v1.0.3-0.20190429190417-a667d056470f/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= +github.com/miekg/pkcs11 v1.0.3 h1:iMwmD7I5225wv84WxIG/bmxz9AXjWvTWIbM/TYHvWtw= github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= @@ -1614,6 +1616,7 @@ github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpP github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= github.com/tent/canonical-json-go v0.0.0-20130607151641-96e4ba3a7613 h1:iGnD/q9160NWqKZZ5vY4p0dMiYMRknzctfSkqA4nBDw= github.com/tent/canonical-json-go v0.0.0-20130607151641-96e4ba3a7613/go.mod h1:g6AnIpDSYMcphz193otpSIzN+11Rs+AAIIC6rm1enug= +github.com/thales-e-security/pool v0.0.2 h1:RAPs4q2EbWsTit6tpzuvTFlgFRJ3S8Evf5gtvVDbmPg= github.com/thales-e-security/pool v0.0.2/go.mod h1:qtpMm2+thHtqhLzTwgDBj/OuNnMpupY8mv0Phz0gjhU= github.com/theupdateframework/go-tuf v0.0.0-20210630170422-22a94818d17b/go.mod h1:L+uU/NRFK/7h0NYAnsmvsX9EghDB5QVCcHCIrK2h5nw= github.com/theupdateframework/go-tuf v0.0.0-20210722233521-90e262754396 h1:j4odVZMwglHp54CYsNHd0wls+lkQzxloQU9AQjQu0W4= diff --git a/pkg/cosign/pkcs11key/disabled.go b/pkg/cosign/pkcs11key/disabled.go new file mode 100644 index 00000000000..287f0882637 --- /dev/null +++ b/pkg/cosign/pkcs11key/disabled.go @@ -0,0 +1,69 @@ +//go:build !pkcs11key +// +build !pkcs11key + +// Copyright 2021 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 pkcs11key + +import ( + "context" + "crypto" + "crypto/x509" + "io" + + "github.com/pkg/errors" + "github.com/sigstore/sigstore/pkg/signature" +) + +// The empty struct is used so this file never imports piv-go which is +// dependent on cgo and will fail to build if imported. +type empty struct{} //nolint + +type Key struct{} + +func GetKeyWithUriConfig(config *Pkcs11UriConfig, askForPinIfNeeded bool) (*Key, error) { + return nil, errors.New("unimplemented") +} + +func (k *Key) Certificate() (*x509.Certificate, error) { + return nil, errors.New("unimplemented") +} + +func (k *Key) PublicKey(opts ...signature.PublicKeyOption) (crypto.PublicKey, error) { + return nil, errors.New("unimplemented") +} + +func (k *Key) VerifySignature(signature, message io.Reader, opts ...signature.VerifyOption) error { + return errors.New("unimplemented") +} + +func (k *Key) Verifier() (signature.Verifier, error) { + return nil, errors.New("unimplemented") +} + +func (k *Key) Sign(ctx context.Context, rawPayload []byte) ([]byte, []byte, error) { + return nil, nil, errors.New("unimplemented") +} + +func (k *Key) SignMessage(message io.Reader, opts ...signature.SignOption) ([]byte, error) { + return nil, errors.New("unimplemented") +} + +func (k *Key) SignerVerifier() (signature.SignerVerifier, error) { + return nil, errors.New("unimplemented") +} + +func (k *Key) Close() { +} diff --git a/pkg/cosign/pkcs11key/pkcs11key.go b/pkg/cosign/pkcs11key/pkcs11key.go new file mode 100644 index 00000000000..73127585bfa --- /dev/null +++ b/pkg/cosign/pkcs11key/pkcs11key.go @@ -0,0 +1,236 @@ +//go:build pkcs11key +// +build pkcs11key + +// Copyright 2021 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 pkcs11key + +import ( + "context" + "crypto" + "crypto/ecdsa" + "crypto/rand" + "crypto/rsa" + "crypto/sha256" + "crypto/x509" + "fmt" + "io" + "os" + + "github.com/ThalesIgnite/crypto11" + "github.com/miekg/pkcs11" + "github.com/pkg/errors" + "github.com/sigstore/sigstore/pkg/signature" + "golang.org/x/term" +) + +var ( + ContextNotInitialized error = errors.New("context not initialized") + SignerNotSet error = errors.New("signer not set") + CertNotSet error = errors.New("certificate not set") +) + +type Key struct { + ctx *crypto11.Context + signer crypto.Signer + cert *x509.Certificate +} + +func GetKeyWithUriConfig(config *Pkcs11UriConfig, askForPinIfNeeded bool) (*Key, error) { + conf := &crypto11.Config{ + Path: config.modulePath, + Pin: config.pin, + } + + // At least one of object and id must be specified. + if (config.keyLabel == nil || len(config.keyLabel) == 0) && (config.keyId == nil || len(config.keyId) == 0) { + return nil, errors.New("one of keyLabel and keyId must be set") + } + + // At least one of token and slot-id must be specified. + if config.tokenLabel == "" && config.slotId == nil { + return nil, errors.New("one of token and slot id must be set") + } + + // If no PIN was specified, and if askForPinIfNeeded is true, check to see if COSIGN_PKCS11_PIN env var is set. + if conf.Pin == "" && askForPinIfNeeded { + conf.Pin = os.Getenv("COSIGN_PKCS11_PIN") + + // If COSIGN_PKCS11_PIN not set, check to see if CKF_LOGIN_REQUIRED is set in Token Info. + // If it is, and if askForPinIfNeeded is true, ask the user for the PIN, otherwise, do not. + if conf.Pin == "" { + p := pkcs11.New(config.modulePath) + if p == nil { + return nil, errors.New("failed to load PKCS11 module") + } + err := p.Initialize() + if err != nil { + return nil, errors.Wrap(err, "initialize PKCS11 module") + } + + var tokenInfo pkcs11.TokenInfo + if config.slotId != nil { + tokenInfo, err = p.GetTokenInfo(uint(*config.slotId)) + if err != nil { + return nil, errors.Wrap(err, "get token info") + } + } else { + slots, err := p.GetSlotList(true) + if err != nil { + return nil, errors.Wrap(err, "get slot list of PKCS11 module") + } + + for _, slot := range slots { + currentTokenInfo, err := p.GetTokenInfo(slot) + if err != nil { + return nil, errors.Wrap(err, "get token info") + } + if currentTokenInfo.Label == config.tokenLabel { + tokenInfo = currentTokenInfo + break + } + } + } + + if tokenInfo.Flags&pkcs11.CKF_LOGIN_REQUIRED == pkcs11.CKF_LOGIN_REQUIRED { + fmt.Fprintf(os.Stderr, "Enter PIN for key '%s' in PKCS11 token '%s': ", config.keyLabel, config.tokenLabel) + b, err := term.ReadPassword(0) + if err != nil { + return nil, errors.Wrap(err, "get pin") + } + conf.Pin = string(b) + } + + p.Finalize() + p.Destroy() + } + } + + // We must set one slotId or tokenLabel, never both. + // slotId has priority over tokenLabel. + if config.slotId != nil { + conf.SlotNumber = config.slotId + } else if config.tokenLabel != "" { + conf.TokenLabel = config.tokenLabel + } + + ctx, err := crypto11.Configure(conf) + if err != nil { + return nil, err + } + + // If both keyId and keyLabel are set, keyId has priority. + var signer crypto11.Signer + if config.keyId != nil && len(config.keyId) != 0 { + signer, err = ctx.FindKeyPair(config.keyId, nil) + } else if config.keyLabel != nil && len(config.keyLabel) != 0 { + signer, err = ctx.FindKeyPair(nil, config.keyLabel) + } + if err != nil { + return nil, err + } + + // Key's corresponding cert might not exist, + // therefore, we do not fail if it is the case. + var cert *x509.Certificate + if config.keyId != nil && len(config.keyId) != 0 { + cert, _ = ctx.FindCertificate(config.keyId, nil, nil) + } else if config.keyLabel != nil && len(config.keyLabel) != 0 { + cert, _ = ctx.FindCertificate(nil, config.keyLabel, nil) + } + + return &Key{ctx: ctx, signer: signer, cert: cert}, nil +} + +func (k *Key) Certificate() (*x509.Certificate, error) { + return k.cert, nil +} + +func (k *Key) PublicKey(opts ...signature.PublicKeyOption) (crypto.PublicKey, error) { + return k.signer.Public(), nil +} + +func (k *Key) VerifySignature(signature, message io.Reader, opts ...signature.VerifyOption) error { + sig, err := io.ReadAll(signature) + if err != nil { + return errors.Wrap(err, "read signature") + } + msg, err := io.ReadAll(message) + if err != nil { + return errors.Wrap(err, "read message") + } + digest := sha256.Sum256(msg) + + switch kt := k.signer.Public().(type) { + case *ecdsa.PublicKey: + if ecdsa.VerifyASN1(kt, digest[:], sig) { + return nil + } + return errors.New("invalid ecdsa signature") + case *rsa.PublicKey: + return rsa.VerifyPKCS1v15(kt, crypto.SHA256, digest[:], sig) + } + + return fmt.Errorf("unsupported key type: %T", k.PublicKey) +} + +func (k *Key) Verifier() (signature.Verifier, error) { + if k.ctx == nil { + return nil, ContextNotInitialized + } + if k.signer == nil { + return nil, SignerNotSet + } + return k, nil +} + +func (k *Key) Sign(ctx context.Context, rawPayload []byte) ([]byte, []byte, error) { + h := sha256.Sum256(rawPayload) + sig, err := k.signer.Sign(rand.Reader, h[:], crypto.SHA256) + if err != nil { + return nil, nil, err + } + return sig, h[:], err +} + +func (k *Key) SignMessage(message io.Reader, opts ...signature.SignOption) ([]byte, error) { + h := sha256.New() + if _, err := io.Copy(h, message); err != nil { + return nil, err + } + sig, err := k.signer.Sign(rand.Reader, h.Sum(nil), crypto.SHA256) + if err != nil { + return nil, err + } + return sig, err +} + +func (k *Key) SignerVerifier() (signature.SignerVerifier, error) { + if k.ctx == nil { + return nil, ContextNotInitialized + } + if k.signer == nil { + return nil, SignerNotSet + } + return k, nil +} + +func (k *Key) Close() { + k.ctx.Close() + + k.signer = nil + k.cert = nil + k.ctx = nil +} diff --git a/pkg/cosign/pkcs11key/util.go b/pkg/cosign/pkcs11key/util.go new file mode 100644 index 00000000000..1e771a16f71 --- /dev/null +++ b/pkg/cosign/pkcs11key/util.go @@ -0,0 +1,218 @@ +// Copyright 2021 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 pkcs11key + +import ( + "bytes" + "encoding/hex" + "fmt" + "net/url" + "os" + "path/filepath" + "strconv" + "strings" + + "github.com/pkg/errors" +) + +func insertInto(s string, interval int, sep rune) string { + var buffer bytes.Buffer + before := interval - 1 + last := len(s) - 1 + for i, char := range s { + buffer.WriteRune(char) + if i%interval == before && i != last { + buffer.WriteRune(sep) + } + } + return buffer.String() +} + +type Pkcs11UriConfig struct { + uriPathAttributes url.Values + uriQueryAttributes url.Values + + modulePath string + slotId *int + tokenLabel string + keyLabel []byte + keyId []byte + pin string +} + +func NewPkcs11UriConfig() *Pkcs11UriConfig { + return &Pkcs11UriConfig{ + uriPathAttributes: make(url.Values), + uriQueryAttributes: make(url.Values), + } +} + +func NewPkcs11UriConfigFromInput(modulePath string, slotId *int, tokenLabel string, keyLabel []byte, keyId []byte, pin string) *Pkcs11UriConfig { + return &Pkcs11UriConfig{ + uriPathAttributes: make(url.Values), + uriQueryAttributes: make(url.Values), + modulePath: modulePath, + slotId: slotId, + tokenLabel: tokenLabel, + keyLabel: keyLabel, + keyId: keyId, + pin: pin, + } +} + +func (conf *Pkcs11UriConfig) Parse(uriString string) error { + var slotId *int + var pin string + + uri, err := url.Parse(uriString) + if err != nil { + return errors.Wrap(err, "parse uri") + } + if uri.Scheme != "pkcs11" { + return errors.New("invalid uri: not a PKCS11 uri") + } + + // Semicolons are no longer valid separators, therefore, + // we need to replace all occurrences of ";" with "&" + // in uri.Opaque and uri.RawQuery before passing them to url.ParseQuery(). + uri.Opaque = strings.ReplaceAll(uri.Opaque, ";", "&") + uriPathAttributes, err := url.ParseQuery(uri.Opaque) + if err != nil { + return errors.Wrap(err, "parse uri path") + } + uri.RawQuery = strings.ReplaceAll(uri.RawQuery, ";", "&") + uriQueryAttributes, err := url.ParseQuery(uri.RawQuery) + if err != nil { + return errors.Wrap(err, "parse uri query") + } + + modulePath := uriQueryAttributes.Get("module-path") + pinValue := uriQueryAttributes.Get("pin-value") + tokenLabel := uriPathAttributes.Get("token") + slotIdStr := uriPathAttributes.Get("slot-id") + keyLabel := uriPathAttributes.Get("object") + keyId := uriPathAttributes.Get("id") + + // At least one of token and slot-id must be specified. + if tokenLabel == "" && slotIdStr == "" { + return errors.New("invalid uri: one of token and slot-id must be set") + } + + // slot-id, if specified, should be a number. + if slotIdStr != "" { + slot, err := strconv.Atoi(slotIdStr) + if err != nil { + return fmt.Errorf("invalid uri: slot-id '%s' is not a valid number", slotIdStr) + } + slotId = &slot + } + + // If pin-value is specified, take it as it is. + if pinValue != "" { + pin = pinValue + } + + // module-path should be specified and should point to the absolute path of the PKCS11 module. + // If it is not, COSIGN_PKCS11_MODULE_PATH environment variable must be set. + if modulePath == "" { + modulePath = os.Getenv("COSIGN_PKCS11_MODULE_PATH") + if modulePath == "" { + return errors.New("invalid uri: module-path or COSIGN_PKCS11_MODULE_PATH must be set to the absolute path of the PKCS11 module") + } + } + if !filepath.IsAbs(modulePath) { + return errors.New("invalid uri: module-path or COSIGN_PKCS11_MODULE_PATH does not point to an absolute path") + } + info, err := os.Stat(modulePath) + if err != nil { + return errors.Wrap(err, "access module-path or COSIGN_PKCS11_MODULE_PATH") + } + if !info.Mode().IsRegular() { + return errors.New("invalid uri: module-path or COSIGN_PKCS11_MODULE_PATH does not point to a regular file") + } + + // At least one of object and id must be specified. + if keyLabel == "" && keyId == "" { + return errors.New("invalid uri: one of object and id must be set") + } + + conf.uriPathAttributes = uriPathAttributes + conf.uriQueryAttributes = uriQueryAttributes + conf.modulePath = modulePath + conf.tokenLabel = tokenLabel + conf.slotId = slotId + conf.keyLabel = []byte(keyLabel) + conf.keyId = []byte(keyId) // url.ParseQuery() already calls url.QueryUnescape() on the id, so we only need to cast the result into byte array + conf.pin = pin + + return nil +} + +func (conf *Pkcs11UriConfig) Construct() (string, error) { + + uriString := "" + + // module-path should be specified and should point to the absolute path of the PKCS11 module. + if conf.modulePath == "" { + return uriString, errors.New("module path must be set to the absolute path of the PKCS11 module") + } + if !filepath.IsAbs(conf.modulePath) { + return uriString, errors.New("module path does not point to an absolute path") + } + info, err := os.Stat(conf.modulePath) + if err != nil { + return uriString, errors.Wrap(err, "access module path") + } + if !info.Mode().IsRegular() { + return uriString, errors.New("module path does not point to a regular file") + } + + // At least one of keyLabel and keyId must be specified. + if (conf.keyLabel == nil || len(conf.keyLabel) == 0) && (conf.keyId == nil || len(conf.keyId) == 0) { + return uriString, errors.New("one of keyLabel and keyId must be set") + } + + // At least one of tokenLabel and slotId must be specified. + if conf.tokenLabel == "" && conf.slotId == nil { + return uriString, errors.New("one of tokenLabel and slotId must be set") + } + + // Construct the URI. + uriString = "pkcs11:" + // We set either of tokenLabel and slotId. + if conf.tokenLabel != "" { + uriString += "token=" + conf.tokenLabel + } + if conf.slotId != nil { + uriString += ";slot-id=" + fmt.Sprintf("%d", *conf.slotId) + } + // If both keyLabel and keyId are set, keyId has priority. + if conf.keyId != nil && len(conf.keyId) != 0 { + keyIdStr := hex.EncodeToString(conf.keyId) + + // Need to percent escape the keyid, we do it manually. + keyIdStr = insertInto(keyIdStr, 2, '%') + keyIdStr = "%" + keyIdStr + uriString += ";id=" + keyIdStr + } else if conf.keyLabel != nil && len(conf.keyLabel) != 0 { + uriString += ";object=" + string(conf.keyLabel) + } + uriString += "?module-path=" + conf.modulePath + if conf.pin != "" { + uriString += "&pin-value" + conf.pin + } + + return uriString, nil +} From c4b18f76a38808d3d981779a4329dccf402d9304 Mon Sep 17 00:00:00 2001 From: Kieran Miller Date: Tue, 2 Nov 2021 12:05:41 +0100 Subject: [PATCH 2/9] Fixed lint and doc issues, and added pkcs11key flag to verify.sh. Signed-off-by: Kieran Miller --- PKCS11.md | 6 +- cmd/cosign/cli/options/pkcs11_tool.go | 4 +- cmd/cosign/cli/pkcs11_tool.go | 2 +- cmd/cosign/cli/pkcs11cli/commands.go | 20 +++---- cmd/cosign/cli/publickey/public_key.go | 2 +- cmd/cosign/cli/sign/sign.go | 2 +- cmd/cosign/cli/verify/verify.go | 2 +- cmd/cosign/cli/verify/verify_attestation.go | 2 +- cmd/cosign/cli/verify/verify_blob.go | 2 +- cmd/help/verify.sh | 4 +- doc/cosign_pkcs11-tool_list-keys-uris.md | 2 +- pkg/cosign/pkcs11key/disabled.go | 2 +- pkg/cosign/pkcs11key/pkcs11key.go | 30 +++++----- pkg/cosign/pkcs11key/util.go | 64 ++++++++++----------- 14 files changed, 72 insertions(+), 72 deletions(-) diff --git a/PKCS11.md b/PKCS11.md index 4e924765bc1..3a6c664d9ad 100644 --- a/PKCS11.md +++ b/PKCS11.md @@ -7,7 +7,7 @@ This support is enabled through the [crypto11](https://github.com/ThalesIgnite/c ### Setup -To get started, make sure you already have your PKCS11 module installed, and insert your PKCS11-compatible token. +To get started, make sure you already have your PKCS11 module installed, and insert your PKCS11-compatible token. Then, run the command `cosign pkcs11-tool list-tokens` to get the slot id of your token, as follows : @@ -59,7 +59,7 @@ $ cosign sign --key "" gcr.io/dlorenc-vmtest2/demo Pushing signature to: gcr.io/dlorenc-vmtest2/demo:sha256-410a07f17151ffffb513f942a01748dfdb921de915ea6427d61d60b0357c1dcd.sig ``` -To verify, you can either use the PKCS11 token key directly: +To verify, you can either use the PKCS11 token key directly: ```shell $ cosign verify --key "" gcr.io/dlorenc-vmtest2/demo @@ -72,7 +72,7 @@ The following checks were performed on each of these signatures: [{"critical":{"identity":{"docker-reference":"gcr.io/dlorenc-vmtest2/demo"},"image":{"docker-manifest-digest":"sha256:410a07f17151ffffb513f942a01748dfdb921de915ea6427d61d60b0357c1dcd"},"type":"cosign container image signature"},"optional":null}] ``` -Or export the public key and verify against that: +Or export the public key and verify against that: ```shell $ cosign public-key --key "" > pub.key diff --git a/cmd/cosign/cli/options/pkcs11_tool.go b/cmd/cosign/cli/options/pkcs11_tool.go index d476e0dd7fc..b18aacfa945 100644 --- a/cmd/cosign/cli/options/pkcs11_tool.go +++ b/cmd/cosign/cli/options/pkcs11_tool.go @@ -35,7 +35,7 @@ func (o *PKCS11ToolListTokensOptions) AddFlags(cmd *cobra.Command) { // PKCS11ToolListKeysUrisOptions is the wrapper for `pkcs11-tool list-keys-uris` related options. type PKCS11ToolListKeysUrisOptions struct { ModulePath string - SlotId uint + SlotID uint Pin string } @@ -46,7 +46,7 @@ func (o *PKCS11ToolListKeysUrisOptions) AddFlags(cmd *cobra.Command) { cmd.Flags().StringVar(&o.ModulePath, "module-path", "", "absolute path to the PKCS11 module") - cmd.Flags().UintVar(&o.SlotId, "slot-id", 0, + cmd.Flags().UintVar(&o.SlotID, "slot-id", 0, "id of the PKCS11 slot, uses 0 if empty") cmd.Flags().StringVar(&o.Pin, "pin", "", diff --git a/cmd/cosign/cli/pkcs11_tool.go b/cmd/cosign/cli/pkcs11_tool.go index 922652ecc81..42733e53486 100644 --- a/cmd/cosign/cli/pkcs11_tool.go +++ b/cmd/cosign/cli/pkcs11_tool.go @@ -70,7 +70,7 @@ func PKCS11ToolListKeysUrisOptions() *cobra.Command { Short: "list-keys-uris lists URIs of all keys in a PKCS11 token", Args: cobra.ExactArgs(0), RunE: func(cmd *cobra.Command, args []string) error { - return pkcs11cli.ListKeysUrisCmd(cmd.Context(), o.ModulePath, o.SlotId, o.Pin) + return pkcs11cli.ListKeysUrisCmd(cmd.Context(), o.ModulePath, o.SlotID, o.Pin) }, } diff --git a/cmd/cosign/cli/pkcs11cli/commands.go b/cmd/cosign/cli/pkcs11cli/commands.go index fe6c353db31..3370ce99e62 100644 --- a/cmd/cosign/cli/pkcs11cli/commands.go +++ b/cmd/cosign/cli/pkcs11cli/commands.go @@ -71,7 +71,7 @@ func ListTokensCmd(_ context.Context, modulePath string) error { return nil } -func ListKeysUrisCmd(_ context.Context, modulePath string, slotId uint, pin string) error { +func ListKeysUrisCmd(_ context.Context, modulePath string, SlotID uint, pin string) error { if modulePath == "" || !filepath.IsAbs(modulePath) { return flag.ErrHelp } @@ -90,7 +90,7 @@ func ListKeysUrisCmd(_ context.Context, modulePath string, slotId uint, pin stri // Get token Info. var tokenInfo pkcs11.TokenInfo - tokenInfo, err = ctx.GetTokenInfo(uint(slotId)) + tokenInfo, err = ctx.GetTokenInfo(uint(SlotID)) if err != nil { return errors.Wrap(err, "get token info") } @@ -114,7 +114,7 @@ func ListKeysUrisCmd(_ context.Context, modulePath string, slotId uint, pin stri } // Open a new session to the token. - session, err := ctx.OpenSession(slotId, pkcs11.CKF_SERIAL_SESSION|pkcs11.CKF_RW_SESSION) + session, err := ctx.OpenSession(SlotID, pkcs11.CKF_SERIAL_SESSION|pkcs11.CKF_RW_SESSION) if err != nil { return errors.Wrap(err, "open session") } @@ -156,7 +156,7 @@ func ListKeysUrisCmd(_ context.Context, modulePath string, slotId uint, pin stri // For each private key, get key label and key id then construct uri. i := 0 - fmt.Fprintf(os.Stdout, "Listing URIs of keys in slot '%d' of PKCS11 module '%s'\n", slotId, modulePath) + fmt.Fprintf(os.Stdout, "Listing URIs of keys in slot '%d' of PKCS11 module '%s'\n", SlotID, modulePath) for _, handle := range handles { attributes := []*pkcs11.Attribute{ pkcs11.NewAttribute(pkcs11.CKA_ID, nil), @@ -165,16 +165,16 @@ func ListKeysUrisCmd(_ context.Context, modulePath string, slotId uint, pin stri if attributes, err = ctx.GetAttributeValue(session, handle, attributes); err != nil { return errors.Wrap(err, "get attributes") } - keyId := attributes[0].Value + keyID := attributes[0].Value keyLabel := attributes[1].Value // If the object has neither a key id nor a key label, we skip it. - if (keyId == nil || len(keyId) == 0) && (keyLabel == nil || len(keyLabel) == 0) { + if (keyID == nil || len(keyID) == 0) && (keyLabel == nil || len(keyLabel) == 0) { continue } - slotIdInt := int(slotId) - pkcs11Uri := pkcs11key.NewPkcs11UriConfigFromInput(modulePath, &slotIdInt, tokenInfo.Label, keyLabel, keyId, pin) + SlotIDInt := int(SlotID) + pkcs11Uri := pkcs11key.NewPkcs11UriConfigFromInput(modulePath, &SlotIDInt, tokenInfo.Label, keyLabel, keyID, pin) pkcs11UriStr, err := pkcs11Uri.Construct() if err != nil { return errors.Wrap(err, "construct pkcs11 uri") @@ -184,8 +184,8 @@ func ListKeysUrisCmd(_ context.Context, modulePath string, slotId uint, pin stri if keyLabel != nil && len(keyLabel) != 0 { fmt.Fprintf(os.Stdout, "\tLabel: %s\n", string(keyLabel)) } - if keyId != nil && len(keyId) != 0 { - fmt.Fprintf(os.Stdout, "\tID: %s\n", hex.EncodeToString(keyId)) + if keyID != nil && len(keyID) != 0 { + fmt.Fprintf(os.Stdout, "\tID: %s\n", hex.EncodeToString(keyID)) } fmt.Fprintf(os.Stdout, "\tURI: %s\n", pkcs11UriStr) diff --git a/cmd/cosign/cli/publickey/public_key.go b/cmd/cosign/cli/publickey/public_key.go index de805834e94..e5d4b9c3c85 100644 --- a/cmd/cosign/cli/publickey/public_key.go +++ b/cmd/cosign/cli/publickey/public_key.go @@ -55,7 +55,7 @@ func GetPublicKey(ctx context.Context, opts Pkopts, writer NamedWriter, pf cosig return errors.Wrap(err, "parsing pkcs11 uri") } - sk, err := pkcs11key.GetKeyWithUriConfig(pkcs11UriConfig, false) + sk, err := pkcs11key.GetKeyWithURIConfig(pkcs11UriConfig, false) if err != nil { return errors.Wrap(err, "opening pkcs11 token key") } diff --git a/cmd/cosign/cli/sign/sign.go b/cmd/cosign/cli/sign/sign.go index 1bd6cbf52c8..9bc1cbbdfec 100644 --- a/cmd/cosign/cli/sign/sign.go +++ b/cmd/cosign/cli/sign/sign.go @@ -337,7 +337,7 @@ func SignerFromKeyOpts(ctx context.Context, certPath string, ko KeyOpts) (*CertS // Since we'll be signing, we need to set askForPinIsNeeded to true // because we need access to the private key. - sk, err := pkcs11key.GetKeyWithUriConfig(pkcs11UriConfig, true) + sk, err := pkcs11key.GetKeyWithURIConfig(pkcs11UriConfig, true) if err != nil { return nil, err } diff --git a/cmd/cosign/cli/verify/verify.go b/cmd/cosign/cli/verify/verify.go index e6f475c0143..b0fef6a4f6b 100644 --- a/cmd/cosign/cli/verify/verify.go +++ b/cmd/cosign/cli/verify/verify.go @@ -101,7 +101,7 @@ func (c *VerifyCommand) Exec(ctx context.Context, images []string) (err error) { // Since we'll be verifying a signature, we do not need to set askForPinIsNeeded to true // because we only need access to the public key. - sk, err := pkcs11key.GetKeyWithUriConfig(pkcs11UriConfig, false) + sk, err := pkcs11key.GetKeyWithURIConfig(pkcs11UriConfig, false) if err != nil { return errors.Wrap(err, "opening pkcs11 token key") } diff --git a/cmd/cosign/cli/verify/verify_attestation.go b/cmd/cosign/cli/verify/verify_attestation.go index f8ace612707..e27db3a8e68 100644 --- a/cmd/cosign/cli/verify/verify_attestation.go +++ b/cmd/cosign/cli/verify/verify_attestation.go @@ -90,7 +90,7 @@ func (c *VerifyAttestationCommand) Exec(ctx context.Context, images []string) (e return errors.Wrap(err, "parsing pkcs11 uri") } - sk, err := pkcs11key.GetKeyWithUriConfig(pkcs11UriConfig, false) + sk, err := pkcs11key.GetKeyWithURIConfig(pkcs11UriConfig, false) if err != nil { return errors.Wrap(err, "opening pkcs11 token key") } diff --git a/cmd/cosign/cli/verify/verify_blob.go b/cmd/cosign/cli/verify/verify_blob.go index 21e2d10c9cd..788c8152c28 100644 --- a/cmd/cosign/cli/verify/verify_blob.go +++ b/cmd/cosign/cli/verify/verify_blob.go @@ -70,7 +70,7 @@ func VerifyBlobCmd(ctx context.Context, ko sign.KeyOpts, certRef, sigRef, blobRe return errors.Wrap(err, "parsing pkcs11 uri") } - sk, err := pkcs11key.GetKeyWithUriConfig(pkcs11UriConfig, false) + sk, err := pkcs11key.GetKeyWithURIConfig(pkcs11UriConfig, false) if err != nil { return errors.Wrap(err, "opening pkcs11 token key") } diff --git a/cmd/help/verify.sh b/cmd/help/verify.sh index 148e66f556f..0e09addbf80 100755 --- a/cmd/help/verify.sh +++ b/cmd/help/verify.sh @@ -18,8 +18,8 @@ set -e # Verify that generated Markdown docs are up-to-date. tmpdir=$(mktemp -d) -go run -tags pivkey,cgo cmd/help/main.go --dir "$tmpdir" +go run -tags pivkey,pkcs11key,cgo cmd/help/main.go --dir "$tmpdir" echo "###########################################" -echo "If diffs are found, run: go run -tags pivkey,cgo ./cmd/help/" +echo "If diffs are found, run: go run -tags pivkey,pkcs11key,cgo ./cmd/help/" echo "###########################################" diff -Naur "$tmpdir" doc/ diff --git a/doc/cosign_pkcs11-tool_list-keys-uris.md b/doc/cosign_pkcs11-tool_list-keys-uris.md index e4c8e2f4923..b563d6b3518 100644 --- a/doc/cosign_pkcs11-tool_list-keys-uris.md +++ b/doc/cosign_pkcs11-tool_list-keys-uris.md @@ -12,7 +12,7 @@ cosign pkcs11-tool list-keys-uris [flags] -h, --help help for list-keys-uris --module-path string absolute path to the PKCS11 module --pin string pin of the PKCS11 slot, uses environment variable COSIGN_PKCS11_PIN if empty - --slot-id uint id of the PKCS11 slot, uses 0 if empty + --slot-id int id of the PKCS11 slot, uses 0 if empty ``` diff --git a/pkg/cosign/pkcs11key/disabled.go b/pkg/cosign/pkcs11key/disabled.go index 287f0882637..b993d21fc7f 100644 --- a/pkg/cosign/pkcs11key/disabled.go +++ b/pkg/cosign/pkcs11key/disabled.go @@ -33,7 +33,7 @@ type empty struct{} //nolint type Key struct{} -func GetKeyWithUriConfig(config *Pkcs11UriConfig, askForPinIfNeeded bool) (*Key, error) { +func GetKeyWithURIConfig(config *Pkcs11UriConfig, askForPinIfNeeded bool) (*Key, error) { return nil, errors.New("unimplemented") } diff --git a/pkg/cosign/pkcs11key/pkcs11key.go b/pkg/cosign/pkcs11key/pkcs11key.go index 73127585bfa..5e2a696242c 100644 --- a/pkg/cosign/pkcs11key/pkcs11key.go +++ b/pkg/cosign/pkcs11key/pkcs11key.go @@ -48,19 +48,19 @@ type Key struct { cert *x509.Certificate } -func GetKeyWithUriConfig(config *Pkcs11UriConfig, askForPinIfNeeded bool) (*Key, error) { +func GetKeyWithURIConfig(config *Pkcs11UriConfig, askForPinIfNeeded bool) (*Key, error) { conf := &crypto11.Config{ Path: config.modulePath, Pin: config.pin, } // At least one of object and id must be specified. - if (config.keyLabel == nil || len(config.keyLabel) == 0) && (config.keyId == nil || len(config.keyId) == 0) { - return nil, errors.New("one of keyLabel and keyId must be set") + if (config.keyLabel == nil || len(config.keyLabel) == 0) && (config.keyID == nil || len(config.keyID) == 0) { + return nil, errors.New("one of keyLabel and keyID must be set") } // At least one of token and slot-id must be specified. - if config.tokenLabel == "" && config.slotId == nil { + if config.tokenLabel == "" && config.slotID == nil { return nil, errors.New("one of token and slot id must be set") } @@ -81,8 +81,8 @@ func GetKeyWithUriConfig(config *Pkcs11UriConfig, askForPinIfNeeded bool) (*Key, } var tokenInfo pkcs11.TokenInfo - if config.slotId != nil { - tokenInfo, err = p.GetTokenInfo(uint(*config.slotId)) + if config.slotID != nil { + tokenInfo, err = p.GetTokenInfo(uint(*config.slotID)) if err != nil { return nil, errors.Wrap(err, "get token info") } @@ -118,10 +118,10 @@ func GetKeyWithUriConfig(config *Pkcs11UriConfig, askForPinIfNeeded bool) (*Key, } } - // We must set one slotId or tokenLabel, never both. - // slotId has priority over tokenLabel. - if config.slotId != nil { - conf.SlotNumber = config.slotId + // We must set one SlotID or tokenLabel, never both. + // SlotID has priority over tokenLabel. + if config.slotID != nil { + conf.SlotNumber = config.slotID } else if config.tokenLabel != "" { conf.TokenLabel = config.tokenLabel } @@ -131,10 +131,10 @@ func GetKeyWithUriConfig(config *Pkcs11UriConfig, askForPinIfNeeded bool) (*Key, return nil, err } - // If both keyId and keyLabel are set, keyId has priority. + // If both keyID and keyLabel are set, keyID has priority. var signer crypto11.Signer - if config.keyId != nil && len(config.keyId) != 0 { - signer, err = ctx.FindKeyPair(config.keyId, nil) + if config.keyID != nil && len(config.keyID) != 0 { + signer, err = ctx.FindKeyPair(config.keyID, nil) } else if config.keyLabel != nil && len(config.keyLabel) != 0 { signer, err = ctx.FindKeyPair(nil, config.keyLabel) } @@ -145,8 +145,8 @@ func GetKeyWithUriConfig(config *Pkcs11UriConfig, askForPinIfNeeded bool) (*Key, // Key's corresponding cert might not exist, // therefore, we do not fail if it is the case. var cert *x509.Certificate - if config.keyId != nil && len(config.keyId) != 0 { - cert, _ = ctx.FindCertificate(config.keyId, nil, nil) + if config.keyID != nil && len(config.keyID) != 0 { + cert, _ = ctx.FindCertificate(config.keyID, nil, nil) } else if config.keyLabel != nil && len(config.keyLabel) != 0 { cert, _ = ctx.FindCertificate(nil, config.keyLabel, nil) } diff --git a/pkg/cosign/pkcs11key/util.go b/pkg/cosign/pkcs11key/util.go index 1e771a16f71..cbc971cf777 100644 --- a/pkg/cosign/pkcs11key/util.go +++ b/pkg/cosign/pkcs11key/util.go @@ -45,10 +45,10 @@ type Pkcs11UriConfig struct { uriQueryAttributes url.Values modulePath string - slotId *int + slotID *int tokenLabel string keyLabel []byte - keyId []byte + keyID []byte pin string } @@ -59,21 +59,21 @@ func NewPkcs11UriConfig() *Pkcs11UriConfig { } } -func NewPkcs11UriConfigFromInput(modulePath string, slotId *int, tokenLabel string, keyLabel []byte, keyId []byte, pin string) *Pkcs11UriConfig { +func NewPkcs11UriConfigFromInput(modulePath string, slotID *int, tokenLabel string, keyLabel []byte, keyID []byte, pin string) *Pkcs11UriConfig { return &Pkcs11UriConfig{ uriPathAttributes: make(url.Values), uriQueryAttributes: make(url.Values), modulePath: modulePath, - slotId: slotId, + slotID: slotID, tokenLabel: tokenLabel, keyLabel: keyLabel, - keyId: keyId, + keyID: keyID, pin: pin, } } func (conf *Pkcs11UriConfig) Parse(uriString string) error { - var slotId *int + var slotID *int var pin string uri, err := url.Parse(uriString) @@ -101,22 +101,22 @@ func (conf *Pkcs11UriConfig) Parse(uriString string) error { modulePath := uriQueryAttributes.Get("module-path") pinValue := uriQueryAttributes.Get("pin-value") tokenLabel := uriPathAttributes.Get("token") - slotIdStr := uriPathAttributes.Get("slot-id") + slotIDStr := uriPathAttributes.Get("slot-id") keyLabel := uriPathAttributes.Get("object") - keyId := uriPathAttributes.Get("id") + keyID := uriPathAttributes.Get("id") // At least one of token and slot-id must be specified. - if tokenLabel == "" && slotIdStr == "" { + if tokenLabel == "" && slotIDStr == "" { return errors.New("invalid uri: one of token and slot-id must be set") } // slot-id, if specified, should be a number. - if slotIdStr != "" { - slot, err := strconv.Atoi(slotIdStr) + if slotIDStr != "" { + slot, err := strconv.Atoi(slotIDStr) if err != nil { - return fmt.Errorf("invalid uri: slot-id '%s' is not a valid number", slotIdStr) + return fmt.Errorf("invalid uri: slot-id '%s' is not a valid number", slotIDStr) } - slotId = &slot + slotID = &slot } // If pin-value is specified, take it as it is. @@ -144,7 +144,7 @@ func (conf *Pkcs11UriConfig) Parse(uriString string) error { } // At least one of object and id must be specified. - if keyLabel == "" && keyId == "" { + if keyLabel == "" && keyID == "" { return errors.New("invalid uri: one of object and id must be set") } @@ -152,9 +152,9 @@ func (conf *Pkcs11UriConfig) Parse(uriString string) error { conf.uriQueryAttributes = uriQueryAttributes conf.modulePath = modulePath conf.tokenLabel = tokenLabel - conf.slotId = slotId + conf.slotID = slotID conf.keyLabel = []byte(keyLabel) - conf.keyId = []byte(keyId) // url.ParseQuery() already calls url.QueryUnescape() on the id, so we only need to cast the result into byte array + conf.keyID = []byte(keyID) // url.ParseQuery() already calls url.QueryUnescape() on the id, so we only need to cast the result into byte array conf.pin = pin return nil @@ -179,33 +179,33 @@ func (conf *Pkcs11UriConfig) Construct() (string, error) { return uriString, errors.New("module path does not point to a regular file") } - // At least one of keyLabel and keyId must be specified. - if (conf.keyLabel == nil || len(conf.keyLabel) == 0) && (conf.keyId == nil || len(conf.keyId) == 0) { - return uriString, errors.New("one of keyLabel and keyId must be set") + // At least one of keyLabel and keyID must be specified. + if (conf.keyLabel == nil || len(conf.keyLabel) == 0) && (conf.keyID == nil || len(conf.keyID) == 0) { + return uriString, errors.New("one of keyLabel and keyID must be set") } - // At least one of tokenLabel and slotId must be specified. - if conf.tokenLabel == "" && conf.slotId == nil { - return uriString, errors.New("one of tokenLabel and slotId must be set") + // At least one of tokenLabel and slotID must be specified. + if conf.tokenLabel == "" && conf.slotID == nil { + return uriString, errors.New("one of tokenLabel and slotID must be set") } // Construct the URI. uriString = "pkcs11:" - // We set either of tokenLabel and slotId. + // We set either of tokenLabel and SlotID. if conf.tokenLabel != "" { uriString += "token=" + conf.tokenLabel } - if conf.slotId != nil { - uriString += ";slot-id=" + fmt.Sprintf("%d", *conf.slotId) + if conf.slotID != nil { + uriString += ";slot-id=" + fmt.Sprintf("%d", *conf.slotID) } - // If both keyLabel and keyId are set, keyId has priority. - if conf.keyId != nil && len(conf.keyId) != 0 { - keyIdStr := hex.EncodeToString(conf.keyId) + // If both keyLabel and keyID are set, keyID has priority. + if conf.keyID != nil && len(conf.keyID) != 0 { + keyIDStr := hex.EncodeToString(conf.keyID) - // Need to percent escape the keyid, we do it manually. - keyIdStr = insertInto(keyIdStr, 2, '%') - keyIdStr = "%" + keyIdStr - uriString += ";id=" + keyIdStr + // Need to percent escape the keyID, we do it manually. + keyIDStr = insertInto(keyIDStr, 2, '%') + keyIDStr = "%" + keyIDStr + uriString += ";id=" + keyIDStr } else if conf.keyLabel != nil && len(conf.keyLabel) != 0 { uriString += ";object=" + string(conf.keyLabel) } From e69e62258d724857e9bd7eeb57b8a5ffc268cc66 Mon Sep 17 00:00:00 2001 From: Kieran Miller Date: Tue, 2 Nov 2021 12:46:51 +0100 Subject: [PATCH 3/9] Added missing = sign Signed-off-by: Kieran Miller --- pkg/cosign/pkcs11key/util.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/cosign/pkcs11key/util.go b/pkg/cosign/pkcs11key/util.go index cbc971cf777..bd45d8e8bd7 100644 --- a/pkg/cosign/pkcs11key/util.go +++ b/pkg/cosign/pkcs11key/util.go @@ -211,7 +211,7 @@ func (conf *Pkcs11UriConfig) Construct() (string, error) { } uriString += "?module-path=" + conf.modulePath if conf.pin != "" { - uriString += "&pin-value" + conf.pin + uriString += "&pin-value=" + conf.pin } return uriString, nil From 881d2cbd9f2e8d8681fba4d855ace3cc99294f00 Mon Sep 17 00:00:00 2001 From: Kieran Miller Date: Wed, 3 Nov 2021 12:48:32 +0100 Subject: [PATCH 4/9] Improved PKCS11 URI parsing. Added PKCS11 tests. Signed-off-by: Kieran Miller --- Makefile | 2 +- cmd/cosign/cli/pkcs11cli/commands.go | 138 ++++--- pkg/cosign/pkcs11key/pkcs11key.go | 124 +++--- pkg/cosign/pkcs11key/util.go | 184 +++++---- test/pkcs11_test.go | 541 +++++++++++++++++++++++++++ 5 files changed, 824 insertions(+), 165 deletions(-) create mode 100644 test/pkcs11_test.go diff --git a/Makefile b/Makefile index 29eb55bb004..f9d428aed30 100644 --- a/Makefile +++ b/Makefile @@ -62,7 +62,7 @@ cosign-pivkey: $(SRCS) CGO_ENABLED=1 go build -trimpath -tags=pivkey -ldflags $(LDFLAGS) -o cosign ./cmd/cosign cosign-pkcs11key: $(SRCS) - go build -trimpath -tags=pkcs11key -ldflags $(LDFLAGS) -o cosign ./cmd/cosign + CGO_ENABLED=1 go build -trimpath -tags=pkcs11key -ldflags $(LDFLAGS) -o cosign ./cmd/cosign GOLANGCI_LINT_DIR = $(shell pwd)/bin GOLANGCI_LINT_BIN = $(GOLANGCI_LINT_DIR)/golangci-lint diff --git a/cmd/cosign/cli/pkcs11cli/commands.go b/cmd/cosign/cli/pkcs11cli/commands.go index 3370ce99e62..2d0e8946d52 100644 --- a/cmd/cosign/cli/pkcs11cli/commands.go +++ b/cmd/cosign/cli/pkcs11cli/commands.go @@ -25,74 +25,82 @@ import ( "os" "path/filepath" - "github.com/ThalesIgnite/crypto11" "github.com/miekg/pkcs11" "github.com/pkg/errors" "github.com/sigstore/cosign/pkg/cosign/pkcs11key" "golang.org/x/term" ) -func ListTokensCmd(_ context.Context, modulePath string) error { +type Token struct { + Slot uint + TokenInfo pkcs11.TokenInfo +} + +type KeyInfo struct { + KeyLabel []byte + KeyID []byte + KeyURI string +} + +func GetTokens(_ context.Context, modulePath string) ([]Token, error) { if modulePath == "" || !filepath.IsAbs(modulePath) { - return flag.ErrHelp + return nil, flag.ErrHelp } + var tokens []Token + // Initialize PKCS11 module. p := pkcs11.New(modulePath) if p == nil { - return errors.New("failed to load PKCS11 module") + return nil, errors.New("failed to load PKCS11 module") } err := p.Initialize() if err != nil { - return errors.Wrap(err, "initialize PKCS11 module") + return nil, errors.Wrap(err, "initialize PKCS11 module") } defer p.Destroy() defer p.Finalize() - // Get all slots with a token, and print info. + // Get list of all slots with a token, and get info of each. slots, err := p.GetSlotList(true) if err != nil { - return errors.Wrap(err, "get slot list of PKCS11 module") + return nil, errors.Wrap(err, "get slot list") } - fmt.Fprintf(os.Stdout, "Listing tokens of PKCS11 module '%s'\n", modulePath) for _, slot := range slots { tokenInfo, err := p.GetTokenInfo(slot) if err != nil { - return errors.Wrap(err, "get token info") + continue } - - fmt.Fprintf(os.Stdout, "Token in slot %d\n", slot) - fmt.Fprintf(os.Stdout, "\tLabel: %s\n", tokenInfo.Label) - fmt.Fprintf(os.Stdout, "\tManufacturer: %s\n", tokenInfo.ManufacturerID) - fmt.Fprintf(os.Stdout, "\tModel: %s\n", tokenInfo.Model) - fmt.Fprintf(os.Stdout, "\tS/N: %s\n\n", tokenInfo.SerialNumber) + tokens = append(tokens, Token{Slot: slot, TokenInfo: tokenInfo}) } - return nil + return tokens, nil } -func ListKeysUrisCmd(_ context.Context, modulePath string, SlotID uint, pin string) error { +func GetKeysInfo(_ context.Context, modulePath string, slotID uint, pin string) ([]KeyInfo, error) { if modulePath == "" || !filepath.IsAbs(modulePath) { - return flag.ErrHelp + return nil, flag.ErrHelp } + var keysInfo []KeyInfo + // Initialize PKCS11 module. ctx := pkcs11.New(modulePath) if ctx == nil { - return errors.New("failed to load PKCS11 module") + return nil, errors.New("failed to load PKCS11 module") } err := ctx.Initialize() if err != nil { - return errors.Wrap(err, "initialize PKCS11 module") + return nil, errors.Wrap(err, "initialize PKCS11 module") } defer ctx.Destroy() defer ctx.Finalize() // Get token Info. var tokenInfo pkcs11.TokenInfo - tokenInfo, err = ctx.GetTokenInfo(uint(SlotID)) + tokenInfo, err = ctx.GetTokenInfo(uint(slotID)) if err != nil { - return errors.Wrap(err, "get token info") + return nil, errors.Wrap(err, "get token info") } // If pin was not given, check COSIGN_PKCS11_PIN environment variable. @@ -106,7 +114,7 @@ func ListKeysUrisCmd(_ context.Context, modulePath string, SlotID uint, pin stri fmt.Fprintf(os.Stderr, "Enter PIN for PKCS11 token '%s': ", tokenInfo.Label) b, err := term.ReadPassword(0) if err != nil { - return errors.Wrap(err, "get pin") + return nil, errors.Wrap(err, "get pin") } pin = string(b) } @@ -114,82 +122,120 @@ func ListKeysUrisCmd(_ context.Context, modulePath string, SlotID uint, pin stri } // Open a new session to the token. - session, err := ctx.OpenSession(SlotID, pkcs11.CKF_SERIAL_SESSION|pkcs11.CKF_RW_SESSION) + session, err := ctx.OpenSession(slotID, pkcs11.CKF_SERIAL_SESSION|pkcs11.CKF_RW_SESSION) if err != nil { - return errors.Wrap(err, "open session") + return nil, errors.Wrap(err, "open session") } defer ctx.CloseSession(session) // Login user. err = ctx.Login(session, pkcs11.CKU_USER, pin) if err != nil { - return errors.Wrap(err, "login") + return nil, errors.Wrap(err, "login") } defer ctx.Logout(session) // Look for private keys. maxHandlePerFind := 20 var handles []pkcs11.ObjectHandle - findAttributes := make(crypto11.AttributeSet) - err = findAttributes.Set(crypto11.CkaClass, pkcs11.CKO_PRIVATE_KEY) - if err != nil { - return errors.Wrap(err, "set attributes") + findAttributes := []*pkcs11.Attribute{ + pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_PRIVATE_KEY), } - if err = ctx.FindObjectsInit(session, findAttributes.ToSlice()); err != nil { - return errors.Wrap(err, "init find objects") + if err = ctx.FindObjectsInit(session, findAttributes); err != nil { + return nil, errors.Wrap(err, "init find objects") } newhandles, _, err := ctx.FindObjects(session, maxHandlePerFind) if err != nil { - return errors.Wrap(err, "find objects") + return nil, errors.Wrap(err, "find objects") } for len(newhandles) > 0 { handles = append(handles, newhandles...) newhandles, _, err = ctx.FindObjects(session, maxHandlePerFind) if err != nil { - return errors.Wrap(err, "find objects") + return nil, errors.Wrap(err, "find objects") } } err = ctx.FindObjectsFinal(session) if err != nil { - return errors.Wrap(err, "finalize find objects") + return nil, errors.Wrap(err, "finalize find objects") } // For each private key, get key label and key id then construct uri. - i := 0 - fmt.Fprintf(os.Stdout, "Listing URIs of keys in slot '%d' of PKCS11 module '%s'\n", SlotID, modulePath) for _, handle := range handles { + var keyInfo KeyInfo + attributes := []*pkcs11.Attribute{ pkcs11.NewAttribute(pkcs11.CKA_ID, nil), pkcs11.NewAttribute(pkcs11.CKA_LABEL, nil), } if attributes, err = ctx.GetAttributeValue(session, handle, attributes); err != nil { - return errors.Wrap(err, "get attributes") + return nil, errors.Wrap(err, "get attributes") } keyID := attributes[0].Value keyLabel := attributes[1].Value + slotIDInt := int(slotID) // If the object has neither a key id nor a key label, we skip it. if (keyID == nil || len(keyID) == 0) && (keyLabel == nil || len(keyLabel) == 0) { continue } - SlotIDInt := int(SlotID) - pkcs11Uri := pkcs11key.NewPkcs11UriConfigFromInput(modulePath, &SlotIDInt, tokenInfo.Label, keyLabel, keyID, pin) + // Construct the PKCS11 URI. + pkcs11Uri := pkcs11key.NewPkcs11UriConfigFromInput(modulePath, &slotIDInt, tokenInfo.Label, keyLabel, keyID, pin) pkcs11UriStr, err := pkcs11Uri.Construct() if err != nil { - return errors.Wrap(err, "construct pkcs11 uri") + return nil, errors.Wrap(err, "construct pkcs11 uri") } - fmt.Fprintf(os.Stdout, "Object %d\n", i) if keyLabel != nil && len(keyLabel) != 0 { - fmt.Fprintf(os.Stdout, "\tLabel: %s\n", string(keyLabel)) + keyInfo.KeyLabel = keyLabel } if keyID != nil && len(keyID) != 0 { - fmt.Fprintf(os.Stdout, "\tID: %s\n", hex.EncodeToString(keyID)) + keyInfo.KeyID = keyID } - fmt.Fprintf(os.Stdout, "\tURI: %s\n", pkcs11UriStr) + keyInfo.KeyURI = pkcs11UriStr + keysInfo = append(keysInfo, keyInfo) + } + + return keysInfo, nil +} + +func ListTokensCmd(ctx context.Context, modulePath string) error { - i++ + tokens, err := GetTokens(ctx, modulePath) + if err != nil { + return err + } + + fmt.Fprintf(os.Stdout, "\nListing tokens of PKCS11 module '%s'\n", modulePath) + for _, token := range tokens { + fmt.Fprintf(os.Stdout, "Token in slot %d\n", token.Slot) + fmt.Fprintf(os.Stdout, "\tLabel: %s\n", token.TokenInfo.Label) + fmt.Fprintf(os.Stdout, "\tManufacturer: %s\n", token.TokenInfo.ManufacturerID) + fmt.Fprintf(os.Stdout, "\tModel: %s\n", token.TokenInfo.Model) + fmt.Fprintf(os.Stdout, "\tS/N: %s\n\n", token.TokenInfo.SerialNumber) + } + + return nil +} + +func ListKeysUrisCmd(ctx context.Context, modulePath string, slotID uint, pin string) error { + + keysInfo, err := GetKeysInfo(ctx, modulePath, slotID, pin) + if err != nil { + return err + } + + fmt.Fprintf(os.Stdout, "\nListing URIs of keys in slot '%d' of PKCS11 module '%s'\n", slotID, modulePath) + for i, keyInfo := range keysInfo { + fmt.Fprintf(os.Stdout, "Object %d\n", i) + if keyInfo.KeyLabel != nil && len(keyInfo.KeyLabel) != 0 { + fmt.Fprintf(os.Stdout, "\tLabel: %s\n", string(keyInfo.KeyLabel)) + } + if keyInfo.KeyID != nil && len(keyInfo.KeyID) != 0 { + fmt.Fprintf(os.Stdout, "\tID: %s\n", hex.EncodeToString(keyInfo.KeyID)) + } + fmt.Fprintf(os.Stdout, "\tURI: %s\n", keyInfo.KeyURI) } return nil diff --git a/pkg/cosign/pkcs11key/pkcs11key.go b/pkg/cosign/pkcs11key/pkcs11key.go index 5e2a696242c..0905cc2e1d2 100644 --- a/pkg/cosign/pkcs11key/pkcs11key.go +++ b/pkg/cosign/pkcs11key/pkcs11key.go @@ -28,6 +28,7 @@ import ( "fmt" "io" "os" + "path/filepath" "github.com/ThalesIgnite/crypto11" "github.com/miekg/pkcs11" @@ -50,20 +51,32 @@ type Key struct { func GetKeyWithURIConfig(config *Pkcs11UriConfig, askForPinIfNeeded bool) (*Key, error) { conf := &crypto11.Config{ - Path: config.modulePath, - Pin: config.pin, + Path: config.ModulePath, + Pin: config.Pin, } // At least one of object and id must be specified. - if (config.keyLabel == nil || len(config.keyLabel) == 0) && (config.keyID == nil || len(config.keyID) == 0) { + if (config.KeyLabel == nil || len(config.KeyLabel) == 0) && (config.KeyID == nil || len(config.KeyID) == 0) { return nil, errors.New("one of keyLabel and keyID must be set") } // At least one of token and slot-id must be specified. - if config.tokenLabel == "" && config.slotID == nil { + if config.TokenLabel == "" && config.SlotID == nil { return nil, errors.New("one of token and slot id must be set") } + // modulePath must be specified and must point to the absolute path of the PKCS11 module. + if !filepath.IsAbs(config.ModulePath) { + return nil, errors.New("modulePath does not point to an absolute path") + } + info, err := os.Stat(config.ModulePath) + if err != nil { + return nil, errors.Wrap(err, "access modulePath") + } + if !info.Mode().IsRegular() { + return nil, errors.New("modulePath does not point to a regular file") + } + // If no PIN was specified, and if askForPinIfNeeded is true, check to see if COSIGN_PKCS11_PIN env var is set. if conf.Pin == "" && askForPinIfNeeded { conf.Pin = os.Getenv("COSIGN_PKCS11_PIN") @@ -71,59 +84,74 @@ func GetKeyWithURIConfig(config *Pkcs11UriConfig, askForPinIfNeeded bool) (*Key, // If COSIGN_PKCS11_PIN not set, check to see if CKF_LOGIN_REQUIRED is set in Token Info. // If it is, and if askForPinIfNeeded is true, ask the user for the PIN, otherwise, do not. if conf.Pin == "" { - p := pkcs11.New(config.modulePath) - if p == nil { - return nil, errors.New("failed to load PKCS11 module") - } - err := p.Initialize() - if err != nil { - return nil, errors.Wrap(err, "initialize PKCS11 module") - } - var tokenInfo pkcs11.TokenInfo - if config.slotID != nil { - tokenInfo, err = p.GetTokenInfo(uint(*config.slotID)) - if err != nil { - return nil, errors.Wrap(err, "get token info") + askForPinIfNeededFunc := func() error { + p := pkcs11.New(config.ModulePath) + if p == nil { + return errors.New("failed to load PKCS11 module") } - } else { - slots, err := p.GetSlotList(true) + err := p.Initialize() if err != nil { - return nil, errors.Wrap(err, "get slot list of PKCS11 module") + return errors.Wrap(err, "initialize PKCS11 module") } + defer p.Destroy() + defer p.Finalize() - for _, slot := range slots { - currentTokenInfo, err := p.GetTokenInfo(slot) + var tokenInfo pkcs11.TokenInfo + bTokenFound := false + if config.SlotID != nil { + tokenInfo, err = p.GetTokenInfo(uint(*config.SlotID)) + if err != nil { + return errors.Wrap(err, "get token info") + } + } else { + slots, err := p.GetSlotList(true) if err != nil { - return nil, errors.Wrap(err, "get token info") + return errors.Wrap(err, "get slot list of PKCS11 module") } - if currentTokenInfo.Label == config.tokenLabel { - tokenInfo = currentTokenInfo - break + + for _, slot := range slots { + currentTokenInfo, err := p.GetTokenInfo(slot) + if err != nil { + return errors.Wrap(err, "get token info") + } + if currentTokenInfo.Label == config.TokenLabel { + tokenInfo = currentTokenInfo + bTokenFound = true + break + } + } + + if !bTokenFound { + return fmt.Errorf("could not find a slot for the token '%s'", config.TokenLabel) } } - } - if tokenInfo.Flags&pkcs11.CKF_LOGIN_REQUIRED == pkcs11.CKF_LOGIN_REQUIRED { - fmt.Fprintf(os.Stderr, "Enter PIN for key '%s' in PKCS11 token '%s': ", config.keyLabel, config.tokenLabel) - b, err := term.ReadPassword(0) - if err != nil { - return nil, errors.Wrap(err, "get pin") + if tokenInfo.Flags&pkcs11.CKF_LOGIN_REQUIRED == pkcs11.CKF_LOGIN_REQUIRED { + fmt.Fprintf(os.Stderr, "Enter PIN for key '%s' in PKCS11 token '%s': ", config.KeyLabel, config.TokenLabel) + b, err := term.ReadPassword(0) + if err != nil { + return errors.Wrap(err, "get pin") + } + conf.Pin = string(b) } - conf.Pin = string(b) - } - p.Finalize() - p.Destroy() + return nil + }() + + err := askForPinIfNeededFunc + if err != nil { + return nil, err + } } } // We must set one SlotID or tokenLabel, never both. // SlotID has priority over tokenLabel. - if config.slotID != nil { - conf.SlotNumber = config.slotID - } else if config.tokenLabel != "" { - conf.TokenLabel = config.tokenLabel + if config.SlotID != nil { + conf.SlotNumber = config.SlotID + } else if config.TokenLabel != "" { + conf.TokenLabel = config.TokenLabel } ctx, err := crypto11.Configure(conf) @@ -133,10 +161,10 @@ func GetKeyWithURIConfig(config *Pkcs11UriConfig, askForPinIfNeeded bool) (*Key, // If both keyID and keyLabel are set, keyID has priority. var signer crypto11.Signer - if config.keyID != nil && len(config.keyID) != 0 { - signer, err = ctx.FindKeyPair(config.keyID, nil) - } else if config.keyLabel != nil && len(config.keyLabel) != 0 { - signer, err = ctx.FindKeyPair(nil, config.keyLabel) + if config.KeyID != nil && len(config.KeyID) != 0 { + signer, err = ctx.FindKeyPair(config.KeyID, nil) + } else if config.KeyLabel != nil && len(config.KeyLabel) != 0 { + signer, err = ctx.FindKeyPair(nil, config.KeyLabel) } if err != nil { return nil, err @@ -145,10 +173,10 @@ func GetKeyWithURIConfig(config *Pkcs11UriConfig, askForPinIfNeeded bool) (*Key, // Key's corresponding cert might not exist, // therefore, we do not fail if it is the case. var cert *x509.Certificate - if config.keyID != nil && len(config.keyID) != 0 { - cert, _ = ctx.FindCertificate(config.keyID, nil, nil) - } else if config.keyLabel != nil && len(config.keyLabel) != 0 { - cert, _ = ctx.FindCertificate(nil, config.keyLabel, nil) + if config.KeyID != nil && len(config.KeyID) != 0 { + cert, _ = ctx.FindCertificate(config.KeyID, nil, nil) + } else if config.KeyLabel != nil && len(config.KeyLabel) != 0 { + cert, _ = ctx.FindCertificate(nil, config.KeyLabel, nil) } return &Key{ctx: ctx, signer: signer, cert: cert}, nil diff --git a/pkg/cosign/pkcs11key/util.go b/pkg/cosign/pkcs11key/util.go index bd45d8e8bd7..52c62754ea6 100644 --- a/pkg/cosign/pkcs11key/util.go +++ b/pkg/cosign/pkcs11key/util.go @@ -16,17 +16,18 @@ package pkcs11key import ( "bytes" - "encoding/hex" "fmt" "net/url" "os" - "path/filepath" "strconv" "strings" "github.com/pkg/errors" ) +var pathAttrValueChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~:[]@!$'()*+,=&" +var queryAttrValueChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~:[]@!$'()*+,=/?|" + func insertInto(s string, interval int, sep rune) string { var buffer bytes.Buffer before := interval - 1 @@ -40,16 +41,66 @@ func insertInto(s string, interval int, sep rune) string { return buffer.String() } +func percentEncode(input []byte) string { + if input == nil || len(input) == 0 { + return "" + } + + var stringBuilder strings.Builder + for i := 0; i < len(input); i++ { + stringBuilder.WriteByte('%') + stringBuilder.WriteString(fmt.Sprintf("%.2x", input[i])) + } + + return stringBuilder.String() +} + +func EncodeURIComponent(uriString string, isForPath bool, usePercenttEncoding bool) (string, error) { + var stringBuilder strings.Builder + var encodedUriString string + var allowedChars string + + if isForPath { + allowedChars = pathAttrValueChars + } else { + allowedChars = queryAttrValueChars + } + + for i := 0; i < len(uriString); i++ { + allowedChar := false + + for j := 0; j < len(allowedChars); j++ { + if uriString[i] == allowedChars[j] { + allowedChar = true + break + } + } + + if allowedChar { + stringBuilder.WriteByte(uriString[i]) + } else { + if usePercenttEncoding { + stringBuilder.WriteString(percentEncode([]byte{uriString[i]})) + } else { + return "", errors.New("string contains an invalid character") + } + } + } + + encodedUriString = stringBuilder.String() + return encodedUriString, nil +} + type Pkcs11UriConfig struct { uriPathAttributes url.Values uriQueryAttributes url.Values - modulePath string - slotID *int - tokenLabel string - keyLabel []byte - keyID []byte - pin string + ModulePath string + SlotID *int + TokenLabel string + KeyLabel []byte + KeyID []byte + Pin string } func NewPkcs11UriConfig() *Pkcs11UriConfig { @@ -63,12 +114,12 @@ func NewPkcs11UriConfigFromInput(modulePath string, slotID *int, tokenLabel stri return &Pkcs11UriConfig{ uriPathAttributes: make(url.Values), uriQueryAttributes: make(url.Values), - modulePath: modulePath, - slotID: slotID, - tokenLabel: tokenLabel, - keyLabel: keyLabel, - keyID: keyID, - pin: pin, + ModulePath: modulePath, + SlotID: slotID, + TokenLabel: tokenLabel, + KeyLabel: keyLabel, + KeyID: keyID, + Pin: pin, } } @@ -97,7 +148,6 @@ func (conf *Pkcs11UriConfig) Parse(uriString string) error { if err != nil { return errors.Wrap(err, "parse uri query") } - modulePath := uriQueryAttributes.Get("module-path") pinValue := uriQueryAttributes.Get("pin-value") tokenLabel := uriPathAttributes.Get("token") @@ -132,16 +182,6 @@ func (conf *Pkcs11UriConfig) Parse(uriString string) error { return errors.New("invalid uri: module-path or COSIGN_PKCS11_MODULE_PATH must be set to the absolute path of the PKCS11 module") } } - if !filepath.IsAbs(modulePath) { - return errors.New("invalid uri: module-path or COSIGN_PKCS11_MODULE_PATH does not point to an absolute path") - } - info, err := os.Stat(modulePath) - if err != nil { - return errors.Wrap(err, "access module-path or COSIGN_PKCS11_MODULE_PATH") - } - if !info.Mode().IsRegular() { - return errors.New("invalid uri: module-path or COSIGN_PKCS11_MODULE_PATH does not point to a regular file") - } // At least one of object and id must be specified. if keyLabel == "" && keyID == "" { @@ -150,68 +190,72 @@ func (conf *Pkcs11UriConfig) Parse(uriString string) error { conf.uriPathAttributes = uriPathAttributes conf.uriQueryAttributes = uriQueryAttributes - conf.modulePath = modulePath - conf.tokenLabel = tokenLabel - conf.slotID = slotID - conf.keyLabel = []byte(keyLabel) - conf.keyID = []byte(keyID) // url.ParseQuery() already calls url.QueryUnescape() on the id, so we only need to cast the result into byte array - conf.pin = pin + conf.ModulePath = modulePath + conf.TokenLabel = tokenLabel + conf.SlotID = slotID + conf.KeyLabel = []byte(keyLabel) + conf.KeyID = []byte(keyID) // url.ParseQuery() already calls url.QueryUnescape() on the id, so we only need to cast the result into byte array + conf.Pin = pin return nil } func (conf *Pkcs11UriConfig) Construct() (string, error) { - uriString := "" + var modulePath, pinValue, tokenLabel, slotID, keyID, keyLabel string + var err error + + uriString := "pkcs11:" // module-path should be specified and should point to the absolute path of the PKCS11 module. - if conf.modulePath == "" { - return uriString, errors.New("module path must be set to the absolute path of the PKCS11 module") - } - if !filepath.IsAbs(conf.modulePath) { - return uriString, errors.New("module path does not point to an absolute path") - } - info, err := os.Stat(conf.modulePath) - if err != nil { - return uriString, errors.Wrap(err, "access module path") - } - if !info.Mode().IsRegular() { - return uriString, errors.New("module path does not point to a regular file") + if conf.ModulePath == "" { + return "", errors.New("module path must be set to the absolute path of the PKCS11 module") } // At least one of keyLabel and keyID must be specified. - if (conf.keyLabel == nil || len(conf.keyLabel) == 0) && (conf.keyID == nil || len(conf.keyID) == 0) { - return uriString, errors.New("one of keyLabel and keyID must be set") + if (conf.KeyLabel == nil || len(conf.KeyLabel) == 0) && (conf.KeyID == nil || len(conf.KeyID) == 0) { + return "", errors.New("one of keyLabel and keyID must be set") } // At least one of tokenLabel and slotID must be specified. - if conf.tokenLabel == "" && conf.slotID == nil { - return uriString, errors.New("one of tokenLabel and slotID must be set") + if conf.TokenLabel == "" && conf.SlotID == nil { + return "", errors.New("one of tokenLabel and slotID must be set") } // Construct the URI. - uriString = "pkcs11:" - // We set either of tokenLabel and SlotID. - if conf.tokenLabel != "" { - uriString += "token=" + conf.tokenLabel - } - if conf.slotID != nil { - uriString += ";slot-id=" + fmt.Sprintf("%d", *conf.slotID) - } - // If both keyLabel and keyID are set, keyID has priority. - if conf.keyID != nil && len(conf.keyID) != 0 { - keyIDStr := hex.EncodeToString(conf.keyID) - - // Need to percent escape the keyID, we do it manually. - keyIDStr = insertInto(keyIDStr, 2, '%') - keyIDStr = "%" + keyIDStr - uriString += ";id=" + keyIDStr - } else if conf.keyLabel != nil && len(conf.keyLabel) != 0 { - uriString += ";object=" + string(conf.keyLabel) - } - uriString += "?module-path=" + conf.modulePath - if conf.pin != "" { - uriString += "&pin-value=" + conf.pin + if conf.TokenLabel != "" { + tokenLabel, err = EncodeURIComponent(conf.TokenLabel, true, true) + if err != nil { + return "", errors.Wrap(err, "encode token label") + } + uriString += "token=" + tokenLabel + } + if conf.SlotID != nil { + slotID = fmt.Sprintf("%d", *conf.SlotID) + uriString += ";slot-id=" + slotID + } + if conf.KeyID != nil && len(conf.KeyID) != 0 { + keyID = percentEncode(conf.KeyID) + uriString += ";id=" + keyID + } + if conf.KeyLabel != nil && len(conf.KeyLabel) != 0 { + keyLabel, err = EncodeURIComponent(string(conf.KeyLabel), true, true) + if err != nil { + return "", errors.Wrap(err, "encode key label") + } + uriString += ";object=" + keyLabel + } + modulePath, err = EncodeURIComponent(conf.ModulePath, false, true) + if err != nil { + return "", errors.Wrap(err, "encode module path") + } + uriString += "?module-path=" + modulePath + if conf.Pin != "" { + pinValue, err = EncodeURIComponent(conf.Pin, false, true) + if err != nil { + return "", errors.Wrap(err, "encode pin") + } + uriString += "&pin-value=" + pinValue } return uriString, nil diff --git a/test/pkcs11_test.go b/test/pkcs11_test.go new file mode 100644 index 00000000000..f0d9bacf6f0 --- /dev/null +++ b/test/pkcs11_test.go @@ -0,0 +1,541 @@ +// Copyright 2021 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. + +//go:build !pkcs11keydisabled && softhsm +// +build !pkcs11keydisabled,softhsm + +// DANGER +// This test requires SoftHSMv2 to be installed. An initialized token should already exist. +// This test will import an RSA key pair, using the specified token label. +// By default, the test assumes the following : +// - The SoftHSMv2 library is located at "/usr/local/lib/softhsm/libsofthsm2.so" +// - The initialized token has the label "My Token" +// - The initialized token has the pin "1234" +// - The test will import the key pair using the key label "My Key" +// These values can be overriden using the following environment variable : +// - SOFTHSM_LIB +// - SOFTHSM_TOKENLABEL +// - SOFTHSM_PIN +// - SOFTHSM_KEYLABEL +// By default, the test makes use of the following SoftHSMv2 configuration files : +// - /etc/softhsm2.conf +// - /etc/softhsm.conf +// These values can be overriden using the following environment variable : +// - SOFTHSM2_CONF +// - SOFTHSM_CONF + +package test + +import ( + "bytes" + "context" + "crypto/rsa" + "crypto/x509" + "encoding/hex" + "encoding/pem" + "fmt" + "io/ioutil" + "math/big" + "os" + "strings" + "testing" + + // Import the functions directly for testing. + + "github.com/miekg/pkcs11" + . "github.com/sigstore/cosign/cmd/cosign/cli/pkcs11cli" + "github.com/sigstore/cosign/pkg/cosign/pkcs11key" + "github.com/stretchr/testify/require" +) + +var ( + modulePath = "/usr/local/lib/softhsm/libsofthsm2.so" + tokenLabel = "My Token" + pin = "1234" + keyLabel = "My Key" + + keyID = "355d2d0b569a2a0169e46b82e172cf99aca41400" + uri = "" +) + +func init() { + if x := os.Getenv("SOFTHSM_LIB"); x != "" { + modulePath = x + } + if x := os.Getenv("SOFTHSM_TOKENLABEL"); x != "" { + tokenLabel = x + } + if x := os.Getenv("SOFTHSM_PIN"); x != "" { + pin = x + } + if x := os.Getenv("SOFTHSM_KEYLABEL"); x != "" { + keyLabel = x + } + if x := os.Getenv("SOFTHSM_CONF"); x == "" { + os.Setenv("SOFTHSM_CONF", "/etc/softhsm.conf") + } + if x := os.Getenv("SOFTHSM2_CONF"); x == "" { + os.Setenv("SOFTHSM2_CONF", "/etc/softhsm2.conf") + } + + keyIDBytes, _ := hex.DecodeString(keyID) + pkcs11UriConfig := pkcs11key.NewPkcs11UriConfigFromInput(modulePath, nil, tokenLabel, []byte(keyLabel), keyIDBytes, pin) + uri, _ = pkcs11UriConfig.Construct() +} + +func TestParsePKCS11URI(t *testing.T) { + _ = context.Background() + + uriString := "pkcs11:" + uriString += "library-manufacturer=manufacturer;library-description=description;library-version=1;" + uriString += "slot-manufacturer=manufacturer;slot-description=description;slot-id=1;" + uriString += "manufacturer=manufacturer;model=model;serial=12345678;token=token%20label;" + uriString += "type=private;object=key%20label;id=%6b%65%79%5f%69%64" + uriString += "?" + uriString += "module-path=/path/to/some%20folder/libmodule.so&module-name=libmodule.so&" + uriString += "pin-value=1234&pin-source=/path/to/pinfile" + + pkcs11UriConfig := pkcs11key.NewPkcs11UriConfig() + must(pkcs11UriConfig.Parse(uriString), t) + require.Equal(t, pkcs11UriConfig.KeyID, []byte("key_id")) + require.Equal(t, pkcs11UriConfig.KeyLabel, []byte("key label")) + require.Equal(t, pkcs11UriConfig.ModulePath, "/path/to/some folder/libmodule.so") + require.Equal(t, pkcs11UriConfig.Pin, "1234") + require.Equal(t, *pkcs11UriConfig.SlotID, 1) + require.Equal(t, pkcs11UriConfig.TokenLabel, "token label") +} + +func TestConstructPKCS11URI(t *testing.T) { + _ = context.Background() + + uri := "pkcs11:token=token%20label;slot-id=1;id=%6b%65%79%5f%69%64;object=key%20label" + uri += "?" + uri += "module-path=/path/to/some%20folder/libmodule.so&pin-value=1234" + + slotID := 1 + pkcs11UriConfig := pkcs11key.NewPkcs11UriConfigFromInput("/path/to/some folder/libmodule.so", &slotID, "token label", []byte("key label"), []byte("key_id"), "1234") + uriString, err := pkcs11UriConfig.Construct() + require.NoError(t, err) + require.Equal(t, uri, uriString) +} + +func TestListTokensCmdCmd(t *testing.T) { + ctx := context.Background() + + tokens, err := GetTokens(ctx, modulePath) + if err != nil { + t.Fatal(err) + } + + bTokenFound := false + for _, token := range tokens { + if token.TokenInfo.Label == tokenLabel { + bTokenFound = true + break + } + } + + if !bTokenFound { + t.Fatalf("token with label '%s' not found", tokenLabel) + } +} + +func TestListKeysUrisCmd(t *testing.T) { + ctx := context.Background() + + tokens, err := GetTokens(ctx, modulePath) + if err != nil { + t.Fatal(err) + } + + bTokenFound := false + var slotID uint + for _, token := range tokens { + if token.TokenInfo.Label == tokenLabel { + bTokenFound = true + slotID = token.Slot + break + } + } + if !bTokenFound { + t.Fatalf("token with label '%s' not found", tokenLabel) + } + + err = importKey(slotID) + if err != nil { + t.Fatal(err) + } + defer deleteKey(slotID) + + keysInfo, err := GetKeysInfo(ctx, modulePath, slotID, pin) + if err != nil { + t.Fatal(err) + } + + bKeyFound := false + for _, keyInfo := range keysInfo { + if hex.EncodeToString(keyInfo.KeyID) == keyID && string(keyInfo.KeyLabel) == keyLabel { + foundUriConfig := pkcs11key.NewPkcs11UriConfig() + err = foundUriConfig.Parse(keyInfo.KeyURI) + if err != nil { + t.Fatal(err) + } + + uriConfig := pkcs11key.NewPkcs11UriConfig() + err = uriConfig.Parse(uri) + if err != nil { + t.Fatal(err) + } + + if foundUriConfig.TokenLabel == uriConfig.TokenLabel && + bytes.Compare(foundUriConfig.KeyID, uriConfig.KeyID) == 0 && + bytes.Compare(foundUriConfig.KeyLabel, uriConfig.KeyLabel) == 0 && + foundUriConfig.ModulePath == uriConfig.ModulePath && + foundUriConfig.Pin == uriConfig.Pin { + bKeyFound = true + } + + break + } + } + + if !bKeyFound { + t.Fatalf("key not found") + } +} + +func TestSignAndVerify(t *testing.T) { + ctx := context.Background() + + tokens, err := GetTokens(ctx, modulePath) + if err != nil { + t.Fatal(err) + } + + bTokenFound := false + var slotID uint + for _, token := range tokens { + if token.TokenInfo.Label == tokenLabel { + bTokenFound = true + slotID = token.Slot + break + } + } + if !bTokenFound { + t.Fatalf("token with label '%s' not found", tokenLabel) + } + + err = importKey(slotID) + if err != nil { + t.Fatal(err) + } + defer deleteKey(slotID) + + pkcs11UriConfig := pkcs11key.NewPkcs11UriConfig() + err = pkcs11UriConfig.Parse(uri) + if err != nil { + t.Fatal(err) + } + + sk, err := pkcs11key.GetKeyWithURIConfig(pkcs11UriConfig, true) + if err != nil { + t.Fatal(err) + } + defer sk.Close() + + sv, err := sk.SignerVerifier() + if err != nil { + t.Fatal(err) + } + + sig, err := sv.SignMessage(bytes.NewReader([]byte("hello, world!"))) + if err != nil { + t.Fatal(err) + } + + err = sv.VerifySignature(bytes.NewReader(sig), bytes.NewReader([]byte("hello, world!"))) + if err != nil { + t.Fatal(err) + } +} + +var newPublicKeyAttrs = []*pkcs11.Attribute{ + pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_PUBLIC_KEY), + pkcs11.NewAttribute(pkcs11.CKA_TOKEN, true), + pkcs11.NewAttribute(pkcs11.CKA_PRIVATE, false), + pkcs11.NewAttribute(pkcs11.CKA_VERIFY, true), +} + +var newPrivateKeyAttrs = []*pkcs11.Attribute{ + pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_PRIVATE_KEY), + pkcs11.NewAttribute(pkcs11.CKA_TOKEN, true), + pkcs11.NewAttribute(pkcs11.CKA_PRIVATE, true), + pkcs11.NewAttribute(pkcs11.CKA_SENSITIVE, true), + pkcs11.NewAttribute(pkcs11.CKA_EXTRACTABLE, false), + pkcs11.NewAttribute(pkcs11.CKA_SIGN, true), +} + +func rsaImportAttrs(priv *rsa.PrivateKey) (pubAttrs, privAttrs []*pkcs11.Attribute) { + pubAttrs = []*pkcs11.Attribute{ + pkcs11.NewAttribute(pkcs11.CKA_PUBLIC_EXPONENT, big.NewInt(int64(priv.E)).Bytes()), + pkcs11.NewAttribute(pkcs11.CKA_MODULUS, priv.N.Bytes()), + } + privAttrs = []*pkcs11.Attribute{ + pkcs11.NewAttribute(pkcs11.CKA_PUBLIC_EXPONENT, big.NewInt(int64(priv.E)).Bytes()), + pkcs11.NewAttribute(pkcs11.CKA_MODULUS, priv.N.Bytes()), + pkcs11.NewAttribute(pkcs11.CKA_PRIVATE_EXPONENT, priv.D.Bytes()), + pkcs11.NewAttribute(pkcs11.CKA_PRIME_1, priv.Primes[0].Bytes()), + pkcs11.NewAttribute(pkcs11.CKA_PRIME_2, priv.Primes[1].Bytes()), + pkcs11.NewAttribute(pkcs11.CKA_EXPONENT_1, priv.Precomputed.Dp.Bytes()), + pkcs11.NewAttribute(pkcs11.CKA_EXPONENT_2, priv.Precomputed.Dq.Bytes()), + pkcs11.NewAttribute(pkcs11.CKA_COEFFICIENT, priv.Precomputed.Qinv.Bytes()), + } + return +} + +func attrConcat(attrSets ...[]*pkcs11.Attribute) []*pkcs11.Attribute { + ret := make([]*pkcs11.Attribute, 0) + for _, attrs := range attrSets { + ret = append(ret, attrs...) + } + return ret +} + +func initPKCS11(modulePath string) (*pkcs11.Ctx, error) { + ctx := pkcs11.New(modulePath) + if ctx == nil { + return nil, fmt.Errorf("unable to load PKCS#11 module") + } + + err := ctx.Initialize() + if err != nil { + return nil, fmt.Errorf("unable to initialize PKCS#11 module") + } + + return ctx, nil +} + +func importKey(slotID uint) error { + var pemBytes []byte + var priv interface{} + + ctx, err := initPKCS11(modulePath) + if err != nil { + return err + } + defer func() { + ctx.Finalize() + ctx.Destroy() + }() + + keyIDBytes, err := hex.DecodeString(keyID) + if err != nil { + return err + } + keyLabelBytes := []byte(keyLabel) + + r := strings.NewReader(rsaPrivKey) + pemBytes, err = ioutil.ReadAll(r) + if err != nil { + return fmt.Errorf("unable to read pem") + } + block, _ := pem.Decode(pemBytes) + if block == nil { + return fmt.Errorf("unable to decode pem") + } + priv, err = x509.ParsePKCS8PrivateKey(block.Bytes) + if err != nil { + return fmt.Errorf("unable to parse pem") + } + privKey, ok := priv.(*rsa.PrivateKey) + if !ok { + return fmt.Errorf("unable to load key") + } + + session, err := ctx.OpenSession(slotID, pkcs11.CKF_SERIAL_SESSION|pkcs11.CKF_RW_SESSION) + if err != nil { + return fmt.Errorf("unable to open session") + } + defer ctx.CloseSession(session) + err = ctx.Login(session, pkcs11.CKU_USER, pin) + if err != nil { + return fmt.Errorf("unable to login") + } + defer ctx.Logout(session) + + keyType := pkcs11.CKK_RSA + pubTypeAttrs, privTypeAttrs := rsaImportAttrs(privKey) + commonAttrs := []*pkcs11.Attribute{ + pkcs11.NewAttribute(pkcs11.CKA_KEY_TYPE, keyType), + pkcs11.NewAttribute(pkcs11.CKA_ID, keyIDBytes), + pkcs11.NewAttribute(pkcs11.CKA_LABEL, keyLabelBytes), + } + pubAttrs := attrConcat(commonAttrs, newPublicKeyAttrs, pubTypeAttrs) + privAttrs := attrConcat(commonAttrs, newPrivateKeyAttrs, privTypeAttrs) + pubHandle, err := ctx.CreateObject(session, pubAttrs) + if err != nil { + return fmt.Errorf("unable to create public key") + } + _, err = ctx.CreateObject(session, privAttrs) + if err != nil { + ctx.DestroyObject(session, pubHandle) + return fmt.Errorf("unable to create private key") + } + + return nil +} + +func deleteKey(slotID uint) error { + var handles []pkcs11.ObjectHandle + + ctx, err := initPKCS11(modulePath) + if err != nil { + return err + } + defer func() { + ctx.Finalize() + ctx.Destroy() + }() + + keyIDBytes, err := hex.DecodeString(keyID) + if err != nil { + return err + } + keyLabelBytes := []byte(keyLabel) + + session, err := ctx.OpenSession(slotID, pkcs11.CKF_SERIAL_SESSION|pkcs11.CKF_RW_SESSION) + if err != nil { + return fmt.Errorf("unable to open session") + } + defer ctx.CloseSession(session) + err = ctx.Login(session, pkcs11.CKU_USER, pin) + if err != nil { + return fmt.Errorf("unable to login") + } + defer ctx.Logout(session) + + maxHandlePerFind := 20 + publicAttrs := []*pkcs11.Attribute{ + pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_PUBLIC_KEY), + pkcs11.NewAttribute(pkcs11.CKA_ID, keyIDBytes), + pkcs11.NewAttribute(pkcs11.CKA_LABEL, keyLabelBytes), + } + if err = ctx.FindObjectsInit(session, publicAttrs); err != nil { + return fmt.Errorf("unable to initialize find objects") + } + handles, _, err = ctx.FindObjects(session, maxHandlePerFind) + if err != nil { + return fmt.Errorf("unable to find objects") + } + err = ctx.FindObjectsFinal(session) + if err != nil { + return fmt.Errorf("unable to finalize find objects") + } + if len(handles) == 1 { + ctx.DestroyObject(session, handles[0]) + if err != nil { + return fmt.Errorf("unable to destroy public key") + } + } + + privAttrs := []*pkcs11.Attribute{ + pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_PRIVATE_KEY), + pkcs11.NewAttribute(pkcs11.CKA_ID, keyIDBytes), + pkcs11.NewAttribute(pkcs11.CKA_LABEL, keyLabelBytes), + } + if err = ctx.FindObjectsInit(session, privAttrs); err != nil { + return fmt.Errorf("unable to initialize find objects") + } + handles, _, err = ctx.FindObjects(session, maxHandlePerFind) + if err != nil { + return fmt.Errorf("unable to find objects") + } + err = ctx.FindObjectsFinal(session) + if err != nil { + return fmt.Errorf("unable to finalize find objects") + } + if len(handles) == 1 { + ctx.DestroyObject(session, handles[0]) + if err != nil { + return fmt.Errorf("unable to destroy private key") + } + } + + return nil +} + +func must(err error, t *testing.T) { + t.Helper() + if err != nil { + t.Fatal(err) + } +} + +func mustErr(err error, t *testing.T) { + t.Helper() + if err == nil { + t.Fatal("expected error") + } +} + +const rsaPrivKey = `-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDZJZ44vB04D2wm +xz+3upmuWelrTWcceVC2v6fkBo9dIR9IejolFY+CsMF1Rc5LGXG3XStQHRrbmq1w +UxC8jsIOK7gI2xI9IOwCgyaQun3J+1VQc6eZxHLGQfTTNq7Vx67VOG8V8d3RhN7L +BvAMT5U55254bUgH0KVx5C1ybLcX6BdGaABCunh7tV+thgwEZSbr2/t0Tf8QneMr +eHojTKZp7/d90TH8KF+/FiPWJWWv5OVhjpZPwTWqUgL+6pgrMKUSWD/92JSDIZe6 +UjxASE4JgnJWMQUhkerJ7j5P16gjdAwJAAt5m3L6wdfVQG2aZ9CJzUowk12ly4Dc +Dx73/UufAgMBAAECggEAF8iA/eHMqXk29UBZgDwV3PzIDhKaOoonBv0S3GzDgwW/ +sWaBu9ISt9O4PKn6oEsXI2g2+D1X1bmpSWYvrRdNtdOgAohMBRn3/4Zx0OQ8JsU6 +YOdp8fOMRp6uu/t/RrbqNTxLHnIxQ2N0K3SFEjQdOgxZEyOVAhYeKM0/FQtHOnzj +WoyZHT8pV3mr6WnxBw/4u/1Ahfau7fs6aVJLECc9jGF/6e7aQeb+yEeLrHayml8e +sbBx4l/1LqU/2S7SQrWtQ+fi+/MlgxvLh0XC7tTPP6I3cTetyMZime9EwwDiPebX +PLUgo8Kf/sHzd/25G9M3Yz+UCLemcPSMUjBUQTPtYQKBgQD8lnpjekyeOjNCdRVP +5w6h1wGN4aC4bCksZ89HKpHc44+3AjDT/aVviory+CyOj05qbXDdpNnNh+jl5llM +yDw15WIvSsXFx3UQ467VVrBKm7vr+k1LGgLJ2fSFbZUTyLvwW4NpP26KDW6SitZ8 +B9lkepTZ0G4Eao51VgidHsulKQKBgQDcFJNAIctqUWDli4tA5L0G5tiypcAA7iIZ +0h2YK+7eOU2f3r8aaywbPhcRn+cKlrf3iV4BCZAv59WEJqq1HOlzU92jkmZspYPq +8kSZLaaiDIBw+vwV4prHDSdZFEY+hHq5eULPIgVm/M474JcghetkVt8pG3ee+Dml +o6zUrZr7hwKBgQDCiXbrpObbuoF+PsTSTGfFl923k74ALDWt4KoQ6qV61bz7O3G1 +5BYFiVOo/CD9Dzxa1b1mx6+ED5f9cOL4MwPEks2DFPircgoknucpomGWpMkgXyAm +pnrdUcN0/Egj+6db4G+eoN8W7m9p6Ap3bmgtbge0lkYVmqfrkP6DXJOFuQKBgHA/ +hkMFeYyGaRdqruGwSMEGaKvlYiKXUok8459DeReavn61y16cHujeKEHy/pImATqd +s3Zv/DyS0BIQ7qxlTKRnt/m/p8HuQXRJkLdX009/dNsrB/vZkfvIN7N1ZcZpJ3cF +5A9lWMAIXN+pUythYofQzw1WVxKbpDtZWcM3sH5tAoGBAMHgZdtmIyllx/1BbYSg +Emxj3LekvZL0e7afeod9f977ZETt/imaejnJNnGOPeSbtLSPfhwonLEp+5XmICzt +lJZAF8iP2m1n9h8sZga5rZQ0JgiwVNFNwde4sp1pD5UcFrYepHRxKPo50eJi3rhR +SwNAKWa96qm5o8BaQu/aRMRu +-----END PRIVATE KEY-----` + +const rsaCert = `-----BEGIN CERTIFICATE----- +MIIDazCCAlOgAwIBAgIUL7BdF7HSUwEAdqElJjVLQYd2OekwDQYJKoZIhvcNAQEL +BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM +GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMTExMDIxNzM3MzJaFw0zMTEw +MzExNzM3MzJaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw +HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQDZJZ44vB04D2wmxz+3upmuWelrTWcceVC2v6fkBo9d +IR9IejolFY+CsMF1Rc5LGXG3XStQHRrbmq1wUxC8jsIOK7gI2xI9IOwCgyaQun3J ++1VQc6eZxHLGQfTTNq7Vx67VOG8V8d3RhN7LBvAMT5U55254bUgH0KVx5C1ybLcX +6BdGaABCunh7tV+thgwEZSbr2/t0Tf8QneMreHojTKZp7/d90TH8KF+/FiPWJWWv +5OVhjpZPwTWqUgL+6pgrMKUSWD/92JSDIZe6UjxASE4JgnJWMQUhkerJ7j5P16gj +dAwJAAt5m3L6wdfVQG2aZ9CJzUowk12ly4DcDx73/UufAgMBAAGjUzBRMB0GA1Ud +DgQWBBRokgD44sdsSGQEQcbJ3vrCrXTIcTAfBgNVHSMEGDAWgBRokgD44sdsSGQE +QcbJ3vrCrXTIcTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQA8 ++CpbIGi4ycCcSeomBzGVXsTFgFutqqvh3BFQ1u6bPlV7hIlFd11zzgWBeKKxREJn +z3SipT1qGX+uP4iVhUux94f2rQCV25mJNRKft2phAUylMr+laiO7IkHFB1zzJTfz +Bi9gm55HGvGCIdSWFkLZ/MUNCMj3WtPrUYl5jqFgDDmCpLctmPoN4vxSa0of3apv +ILH8jSsN5XbL8G1hsT/IGlRRbzoiLCKgCp6e6TjZSq/Y+JWGyw/+sZJMI8Mg4Mje +054uJhD29xmbfxdYxrMWLAFb6yoWVbDJPdECFf9uwOXyDZ8bGd48frTdUU3Rb+m3 +5Hue2g5US98p2jnJiv75 +-----END CERTIFICATE-----` From 0a76bf25c8ff3d95d228d3dcd8577d1774ee06e2 Mon Sep 17 00:00:00 2001 From: Kieran Miller Date: Wed, 3 Nov 2021 13:52:04 +0100 Subject: [PATCH 5/9] Fixed typo. Signed-off-by: Kieran Miller --- test/pkcs11_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/pkcs11_test.go b/test/pkcs11_test.go index f0d9bacf6f0..997f0d3abd3 100644 --- a/test/pkcs11_test.go +++ b/test/pkcs11_test.go @@ -130,7 +130,7 @@ func TestConstructPKCS11URI(t *testing.T) { require.Equal(t, uri, uriString) } -func TestListTokensCmdCmd(t *testing.T) { +func TestListTokensCmd(t *testing.T) { ctx := context.Background() tokens, err := GetTokens(ctx, modulePath) From c7fea55d6126d7bd83fa1923a4cdeb324d7bafb4 Mon Sep 17 00:00:00 2001 From: Kieran Miller Date: Thu, 4 Nov 2021 12:38:27 +0100 Subject: [PATCH 6/9] Resolve go.mod and go.sum conflicts Signed-off-by: Kieran Miller --- go.mod | 33 +++++++++++++++-------------- go.sum | 65 +++++++++++++++++++++++++++++++++------------------------- 2 files changed, 53 insertions(+), 45 deletions(-) diff --git a/go.mod b/go.mod index 0aa868aed31..a75b7ad4e96 100644 --- a/go.mod +++ b/go.mod @@ -4,24 +4,32 @@ go 1.16 require ( cloud.google.com/go/storage v1.18.2 + cuelang.org/go v0.4.0 github.com/cyberphone/json-canonicalization v0.0.0-20210823021906-dc406ceaf94b - github.com/go-openapi/runtime v0.20.0 + github.com/go-openapi/runtime v0.21.0 github.com/go-openapi/strfmt v0.21.0 github.com/go-openapi/swag v0.19.15 github.com/go-piv/piv-go v1.9.0 github.com/google/certificate-transparency-go v1.1.2-0.20210728111105-5f7e9ba4be3d github.com/google/go-cmp v0.5.6 github.com/google/go-containerregistry v0.6.1-0.20210922191434-34b7f00d7a60 + github.com/google/go-containerregistry/pkg/authn/k8schain v0.0.0-20211102215614-dd49079bb93d + github.com/google/go-github/v39 v39.2.0 github.com/google/trillian v1.3.14-0.20210713114448-df474653733c github.com/in-toto/in-toto-golang v0.3.3 - github.com/manifoldco/promptui v0.8.0 + github.com/manifoldco/promptui v0.9.0 + github.com/open-policy-agent/opa v0.34.0 github.com/pkg/errors v0.9.1 + github.com/secure-systems-lab/go-securesystemslib v0.1.0 github.com/sigstore/fulcio v0.1.2-0.20210831152525-42f7422734bb github.com/sigstore/rekor v0.3.0 - github.com/sigstore/sigstore v0.0.0-20211005102407-3ab959fb2809 + github.com/sigstore/sigstore v1.0.0 github.com/spf13/cobra v1.2.1 github.com/stretchr/testify v1.7.0 + github.com/tent/canonical-json-go v0.0.0-20130607151641-96e4ba3a7613 github.com/theupdateframework/go-tuf v0.0.0-20210722233521-90e262754396 + github.com/xanzy/go-gitlab v0.51.1 + golang.org/x/oauth2 v0.0.0-20211028175245-ba495a64dcb5 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 google.golang.org/api v0.60.0 k8s.io/api v0.21.4 @@ -31,30 +39,21 @@ require ( ) require ( - cloud.google.com/go/kms v1.0.0 // indirect - cuelang.org/go v0.4.0 + cloud.google.com/go/kms v1.1.0 // indirect github.com/ThalesIgnite/crypto11 v1.2.4 github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect - github.com/envoyproxy/protoc-gen-validate v0.6.1 // indirect + github.com/envoyproxy/protoc-gen-validate v0.6.2 // indirect github.com/form3tech-oss/jwt-go v3.2.5+incompatible // indirect - github.com/google/go-containerregistry/pkg/authn/k8schain v0.0.0-20211004163346-9ae11fe20941 - github.com/google/go-github/v39 v39.2.0 github.com/imdario/mergo v0.3.12 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect - github.com/miekg/pkcs11 v1.0.3 // indirect + github.com/miekg/pkcs11 v1.0.3 github.com/onsi/gomega v1.16.0 // indirect - github.com/open-policy-agent/opa v0.34.0 github.com/prometheus/procfs v0.7.3 // indirect - github.com/secure-systems-lab/go-securesystemslib v0.1.0 - github.com/tent/canonical-json-go v0.0.0-20130607151641-96e4ba3a7613 github.com/urfave/cli v1.22.5 // indirect - github.com/xanzy/go-gitlab v0.51.1 - go.opentelemetry.io/contrib v0.24.0 // indirect - go.opentelemetry.io/proto/otlp v0.9.0 // indirect + go.opentelemetry.io/contrib v1.1.0 // indirect + go.opentelemetry.io/proto/otlp v0.10.0 // indirect golang.org/x/mod v0.5.1 // indirect - golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1 - golang.org/x/text v0.3.7 // indirect golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect k8s.io/klog/v2 v2.20.0 // indirect k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b // indirect diff --git a/go.sum b/go.sum index f3066e23275..fc34ac21ce9 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,7 @@ bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxo bazil.org/fuse v0.0.0-20180421153158-65cc252bf669/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= bitbucket.org/creachadair/shell v0.0.6 h1:reJflDbKqnlnqb4Oo2pQ1/BqmY/eCWcNGHrIUO8qIzc= bitbucket.org/creachadair/shell v0.0.6/go.mod h1:8Qqi/cYk7vPnsOePHroKXDJYmb5x7ENhtiFtfZq8K+M= +bou.ke/monkey v1.0.2/go.mod h1:OqickVX3tNx6t33n1xvtTtu85YN5s6cKwVug+oHMaIA= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= @@ -47,8 +48,8 @@ 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.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= cloud.google.com/go/firestore v1.5.0/go.mod h1:c4nNYR1qdq7eaZ+jSc5fonrQN2k3M7sWATcYTiakjEo= -cloud.google.com/go/kms v1.0.0 h1:YkIeqPXqTAlwXk3Z2/WG0d6h1tqJQjU354WftjEoP9E= -cloud.google.com/go/kms v1.0.0/go.mod h1:nhUehi+w7zht2XrUfvTRNpxrfayBHqP4lu2NSywui/0= +cloud.google.com/go/kms v1.1.0 h1:1yc4rLqCkVDS9Zvc7m+3mJ47kw0Uo5Q5+sMjcmUVUeM= +cloud.google.com/go/kms v1.1.0/go.mod h1:WdbppnCDMDpOvoYBMn1+gNmOeEoZYqAv+HeuKARGCXI= 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= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -562,8 +563,8 @@ github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021 h1: github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.3.0-java/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v0.6.1 h1:4CF52PCseTFt4bE+Yk3dIpdVi7XWuPVMhPtm4FaIJPM= -github.com/envoyproxy/protoc-gen-validate v0.6.1/go.mod h1:txg5va2Qkip90uYoSKH+nkAAmXrb2j3iq4FLwdrCbXQ= +github.com/envoyproxy/protoc-gen-validate v0.6.2 h1:JiO+kJTpmYGjEodY7O1Zk8oZcNz1+f30UtwtXoFUPzE= +github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws= github.com/etcd-io/gofail v0.0.0-20190801230047-ad7f989257ca/go.mod h1:49H/RkXP8pKaZy4h0d+NW16rSLhyVBt4o6VLJbmOqDE= github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= github.com/evanphx/json-patch v4.9.0+incompatible h1:kLcOMZeuLAJvL2BPWLMIj5oaZQobrkAqrL+WFZwQses= @@ -680,8 +681,9 @@ github.com/go-openapi/loads v0.19.5/go.mod h1:dswLCAdonkRufe/gSUC3gN8nTSaB9uaS2e github.com/go-openapi/loads v0.19.6/go.mod h1:brCsvE6j8mnbmGBh103PT/QLHfbyDxA4hsKvYBNEGVc= github.com/go-openapi/loads v0.19.7/go.mod h1:brCsvE6j8mnbmGBh103PT/QLHfbyDxA4hsKvYBNEGVc= github.com/go-openapi/loads v0.20.0/go.mod h1:2LhKquiE513rN5xC6Aan6lYOSddlL8Mp20AW9kpviM4= -github.com/go-openapi/loads v0.20.2 h1:z5p5Xf5wujMxS1y8aP+vxwW5qYT2zdJBbXKmQUG3lcc= github.com/go-openapi/loads v0.20.2/go.mod h1:hTVUotJ+UonAMMZsvakEgmWKgtulweO9vYP2bQYKA/o= +github.com/go-openapi/loads v0.21.0 h1:jYtUO4wwP7psAweisP/MDoOpdzsYEESdoPcsWjHDR68= +github.com/go-openapi/loads v0.21.0/go.mod h1:rHYve9nZrQ4CJhyeIIFJINGCg1tQpx2yJrrNo8sf1ws= github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64= github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4= @@ -690,8 +692,8 @@ github.com/go-openapi/runtime v0.19.16/go.mod h1:5P9104EJgYcizotuXhEuUrzVc+j1RiS github.com/go-openapi/runtime v0.19.24/go.mod h1:Lm9YGCeecBnUUkFTxPC4s1+lwrkJ0pthx8YvyjCfkgk= github.com/go-openapi/runtime v0.19.29/go.mod h1:BvrQtn6iVb2QmiVXRsFAm6ZCAZBpbVKFfN6QWCp582M= github.com/go-openapi/runtime v0.19.31/go.mod h1:BvrQtn6iVb2QmiVXRsFAm6ZCAZBpbVKFfN6QWCp582M= -github.com/go-openapi/runtime v0.20.0 h1:DEV4oYH28MqakaabtbxH0cjvlzFegi/15kfUVCfiZW0= -github.com/go-openapi/runtime v0.20.0/go.mod h1:2WnLRxMiOUWNN0UZskSkxW0+WXdfB1KmqRKCFH+ZWYk= +github.com/go-openapi/runtime v0.21.0 h1:giZ8eT26R+/rx6RX2MkYjZPY8vPYVKDhP/mOazrQHzM= +github.com/go-openapi/runtime v0.21.0/go.mod h1:aQg+kaIQEn+A2CRSY1TxbM8+sT9g2V3aLc1FbIAnbbs= github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= @@ -703,8 +705,9 @@ github.com/go-openapi/spec v0.19.8/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHK github.com/go-openapi/spec v0.19.15/go.mod h1:+81FIL1JwC5P3/Iuuozq3pPE9dXdIEGxFutcFKaVbmU= github.com/go-openapi/spec v0.20.0/go.mod h1:+81FIL1JwC5P3/Iuuozq3pPE9dXdIEGxFutcFKaVbmU= github.com/go-openapi/spec v0.20.1/go.mod h1:93x7oh+d+FQsmsieroS4cmR3u0p/ywH649a3qwC9OsQ= -github.com/go-openapi/spec v0.20.3 h1:uH9RQ6vdyPSs2pSy9fL8QPspDF2AMIMPtmK5coSSjtQ= github.com/go-openapi/spec v0.20.3/go.mod h1:gG4F8wdEDN+YPBMVnzE85Rbhf+Th2DTvA9nFPQ5AYEg= +github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M= +github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= @@ -737,8 +740,9 @@ github.com/go-openapi/validate v0.19.10/go.mod h1:RKEZTUWDkxKQxN2jDT7ZnZi2bhZlbN github.com/go-openapi/validate v0.19.12/go.mod h1:Rzou8hA/CBw8donlS6WNEUQupNvUZ0waH08tGe6kAQ4= github.com/go-openapi/validate v0.19.15/go.mod h1:tbn/fdOwYHgrhPBzidZfJC2MIVvs9GA7monOmWBbeCI= github.com/go-openapi/validate v0.20.1/go.mod h1:b60iJT+xNNLfaQJUqLI7946tYiFEOuE9E4k54HpKcJ0= -github.com/go-openapi/validate v0.20.2 h1:AhqDegYV3J3iQkMPJSXkvzymHKMTw0BST3RK3hTT4ts= github.com/go-openapi/validate v0.20.2/go.mod h1:e7OJoKNgd0twXZwIn0A43tHbvIcr/rZIVCbJBpTUoY0= +github.com/go-openapi/validate v0.20.3 h1:GZPPhhKSZrE8HjB4eEkoYAZmoWA4+tCemSgINH1/vKw= +github.com/go-openapi/validate v0.20.3/go.mod h1:goDdqVGiigM3jChcrYJxD2joalke3ZXeftD16byIjA4= github.com/go-piv/piv-go v1.9.0 h1:P6j2gjfP7zO7T3nCk/jwCgsvFRwB8shEqAJ4q85jgXc= github.com/go-piv/piv-go v1.9.0/go.mod h1:NZ2zmjVkfFaL/CF8cVQ/pXdXtuj110zEKGdJM6fJZZM= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= @@ -752,6 +756,7 @@ github.com/go-playground/validator v9.31.0+incompatible h1:UA72EPEogEnq76ehGdEDp github.com/go-playground/validator v9.31.0+incompatible/go.mod h1:yrEkQXlcI+PugkyDjY2bRrL/UBU4f3rvrgkN3V8JEig= github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= +github.com/go-rod/rod v0.101.8/go.mod h1:N/zlT53CfSpq74nb6rOR0K8UF0SPUPBmzBnArrms+mY= 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= @@ -895,8 +900,8 @@ github.com/google/go-containerregistry v0.5.1/go.mod h1:Ct15B4yir3PLOP5jsy0GNeYV github.com/google/go-containerregistry v0.5.2-0.20210609162550-f0ce2270b3b4/go.mod h1:R5WRYyTdQqTchlBhX4q+WICGh8HQIL5wDFoFZv7Jq6Q= github.com/google/go-containerregistry v0.6.1-0.20210922191434-34b7f00d7a60 h1:s8OO2miHh5fz8QvhZMOYwCOO+CyqLTOGu4QjngHQDd8= github.com/google/go-containerregistry v0.6.1-0.20210922191434-34b7f00d7a60/go.mod h1:Ux9OZd84tVJ5TebUQoBIZLWoiglvBbVMRYy+79iU03Q= -github.com/google/go-containerregistry/pkg/authn/k8schain v0.0.0-20211004163346-9ae11fe20941 h1:HvYo6qVxHm1j8XJWZmv7+GtekUkxMsDBtxIWNVNnTds= -github.com/google/go-containerregistry/pkg/authn/k8schain v0.0.0-20211004163346-9ae11fe20941/go.mod h1:j3IqhBG3Ox1NXmmhbWU4UmiHVAf2dUgB7le1Ch7JZQ0= +github.com/google/go-containerregistry/pkg/authn/k8schain v0.0.0-20211102215614-dd49079bb93d h1:oZig6KY9zrvhksCb/dKA4VqiaOESYlxGK74N6bs3Ans= +github.com/google/go-containerregistry/pkg/authn/k8schain v0.0.0-20211102215614-dd49079bb93d/go.mod h1:j3IqhBG3Ox1NXmmhbWU4UmiHVAf2dUgB7le1Ch7JZQ0= github.com/google/go-github/v27 v27.0.6/go.mod h1:/0Gr8pJ55COkmv+S/yPKCczSkUPIM/LnFyubufRNIS0= github.com/google/go-github/v28 v28.1.1/go.mod h1:bsqJWQX05omyWVmc00nEUql9mhQyv38lDZ8kPZcQVoM= github.com/google/go-github/v39 v39.2.0 h1:rNNM311XtPOz5rDdsJXAp2o8F67X9FnROXTvto3aSnQ= @@ -1072,7 +1077,7 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo= github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= -github.com/iancoleman/strcase v0.0.0-20180726023541-3605ed457bf7/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE= +github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.4/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= @@ -1129,8 +1134,6 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1 github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a h1:FaWFmfWdAUKbSCtOU2QjDaorUexogfaMgbipgYATUMU= -github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU= github.com/juju/ratelimit v1.0.1/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= @@ -1184,9 +1187,7 @@ github.com/lib/pq v1.10.1 h1:6VXZrLU0jHBYyAqrSPa+MgPfnSvTPuMgK+k0o5kVFWo= github.com/lib/pq v1.10.1/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= -github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a h1:weJVJJRzAJBFRlAiJQROKQs8oC9vOxvm4rZmBBk0ONw= -github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= -github.com/lyft/protoc-gen-star v0.5.1/go.mod h1:9toiA3cC7z5uVbODF7kEQ91Xn7XNFkVUl+SrEe+ZORU= +github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= @@ -1202,8 +1203,8 @@ github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7 github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/manifoldco/promptui v0.8.0 h1:R95mMF+McvXZQ7j1g8ucVZE1gLP3Sv6j9vlF9kyRqQo= -github.com/manifoldco/promptui v0.8.0/go.mod h1:n4zTdgP0vr0S3w7/O/g98U+e0gwLScEXGwov2nIKuGQ= +github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA= +github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg= github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= @@ -1524,8 +1525,8 @@ github.com/sigstore/fulcio v0.1.2-0.20210831152525-42f7422734bb/go.mod h1:LznI5A github.com/sigstore/rekor v0.3.0 h1:OBEvo/Rv8NKKtiWq0WRHgXFpVPe1fGiqz93dfBh/Myo= github.com/sigstore/rekor v0.3.0/go.mod h1:cL9B3+/gp3BG+/bhkSHBA3MQZMten5xM6BhJYd5b5zU= github.com/sigstore/sigstore v0.0.0-20210713222344-1fee53516622/go.mod h1:aOSeNrlcHsfUD8Q1hwWd8KloNqBnxEZlu4k47cFg5rg= -github.com/sigstore/sigstore v0.0.0-20211005102407-3ab959fb2809 h1:TOJFXiYjA1ZNQersM/yPDvQV03kco9xFxw9r1LRuJ2Y= -github.com/sigstore/sigstore v0.0.0-20211005102407-3ab959fb2809/go.mod h1:5ZdSfwXq/9WSzar9eVfYWXqK7hvdPhnwbr1UcSCe3o0= +github.com/sigstore/sigstore v1.0.0 h1:yQUDL9euUBOL2eVrlTtLW5kNtt5YdGrLElf+PFE7P4A= +github.com/sigstore/sigstore v1.0.0/go.mod h1:IVOe2lNKO5KEEj6GW58CnpwqcFQ8H+2RZQCKDwphta8= 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= @@ -1557,7 +1558,6 @@ github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2 github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= -github.com/spf13/afero v1.3.4/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= @@ -1688,6 +1688,11 @@ github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1: github.com/yashtewari/glob-intersection v0.0.0-20180916065949-5c77d914dd0b h1:vVRagRXf67ESqAb72hG2C/ZwI8NtJF2u2V76EsuOHGY= github.com/yashtewari/glob-intersection v0.0.0-20180916065949-5c77d914dd0b/go.mod h1:HptNXiXVDcJjXe9SqMd0v2FsL9f8dz4GnXgltU6q/co= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= +github.com/ysmood/goob v0.3.0/go.mod h1:S3lq113Y91y1UBf1wj1pFOxeahvfKkCk6mTWTWbDdWs= +github.com/ysmood/got v0.15.1/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/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= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -1763,8 +1768,8 @@ go.opencensus.io v0.22.6/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc= -go.opentelemetry.io/contrib v0.24.0 h1:bVBRFM9tOQkTlf45xEmr29hNyASQeYGrJf4iLzn2WOg= -go.opentelemetry.io/contrib v0.24.0/go.mod h1:EH4yDYeNoaTqn/8yCWQmfNB78VHfGX2Jt2bvnvzBlGM= +go.opentelemetry.io/contrib v1.1.0 h1:4Qm+uYIJaixSSMCnQ+Iao7j3S92URWtlhU5z4sQxs28= +go.opentelemetry.io/contrib v1.1.0/go.mod h1:EH4yDYeNoaTqn/8yCWQmfNB78VHfGX2Jt2bvnvzBlGM= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0 h1:sO4WKdPAudZGKPcpZT4MJn6JaDmpyLrMPDGGyA1SttE= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E= go.opentelemetry.io/otel v0.20.0 h1:eaP0Fqu7SXHwvjiqDq83zImeehOHX8doTvU9AwXON8g= @@ -1784,8 +1789,8 @@ go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4 go.opentelemetry.io/otel/trace v0.20.0 h1:1DL6EXUdcg95gukhuRRvLDO/4X5THh/5dIV52lqtnbw= go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v0.9.0 h1:C0g6TWmQYvjKRnljRULLWUVJGy8Uvu0NEL/5frY2/t4= -go.opentelemetry.io/proto/otlp v0.9.0/go.mod h1:1vKfU9rv61e9EVGthD1zNvUbiwPcimSsOPU9brfSHJg= +go.opentelemetry.io/proto/otlp v0.10.0 h1:n7brgtEbDvXEgGyKKo8SobKT1e9FewlDtXzkVP5djoE= +go.opentelemetry.io/proto/otlp v0.10.0/go.mod h1:zG20xCK0szZ1xdokeSOwEcmlXu+x9kkdRe6N1DhKcfU= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= @@ -1899,6 +1904,7 @@ golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449/go.mod h1:s0Qsj1ACt9ePp/hM golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38= golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1973,6 +1979,7 @@ golang.org/x/net v0.0.0-20210505214959-0714010a04ed/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b h1:eB48h3HiRycXNy8E0Gf5e0hv7YT6Kt14L/D73G1fuwo= golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -1998,8 +2005,9 @@ golang.org/x/oauth2 v0.0.0-20210615190721-d04028783cf1/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1 h1:B333XXssMuKQeBwiNODx4TupZy7bf4sxFZnN2ZOcvUE= golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211028175245-ba495a64dcb5 h1:v79phzBz03tsVCUTbvTBmmC3CUXF5mKYt7DA4ZVldpM= +golang.org/x/oauth2 v0.0.0-20211028175245-ba495a64dcb5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -2135,6 +2143,7 @@ golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210909193231-528a39cd75f3/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -2404,9 +2413,9 @@ google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEc google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210917145530-b395a37504d4/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210921142501-181ce0d877f6/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211016002631-37fc39342514/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211018162055-cf77aa76bad2/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211021150943-2b146023228c h1:FqrtZMB5Wr+/RecOM3uPJNPfWR8Upb5hAPnt7PU6i4k= google.golang.org/genproto v0.0.0-20211021150943-2b146023228c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= From 76f857e99e2fe6260820e5f8c31b74000b7cfcc6 Mon Sep 17 00:00:00 2001 From: Kieran Miller Date: Thu, 4 Nov 2021 12:44:50 +0100 Subject: [PATCH 7/9] Fix go.mod indentation Signed-off-by: Kieran Miller --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index f3aa668c509..a75b7ad4e96 100644 --- a/go.mod +++ b/go.mod @@ -40,7 +40,7 @@ require ( require ( cloud.google.com/go/kms v1.1.0 // indirect - github.com/ThalesIgnite/crypto11 v1.2.4 + github.com/ThalesIgnite/crypto11 v1.2.4 github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect github.com/envoyproxy/protoc-gen-validate v0.6.2 // indirect From 89c31301ef30320f8bf26a50a3097b867dd4350e Mon Sep 17 00:00:00 2001 From: Kieran Miller Date: Fri, 5 Nov 2021 12:49:52 +0100 Subject: [PATCH 8/9] Fixed docgen and lint issues. Signed-off-by: Kieran Miller --- doc/cosign.md | 1 + doc/cosign_pkcs11-tool.md | 4 ++-- doc/cosign_pkcs11-tool_list-keys-uris.md | 13 ++++++------ doc/cosign_pkcs11-tool_list-tokens.md | 9 ++++---- pkg/cosign/pkcs11key/disabled.go | 2 +- pkg/cosign/pkcs11key/pkcs11key.go | 10 ++++----- pkg/cosign/pkcs11key/util.go | 27 +++++------------------- 7 files changed, 24 insertions(+), 42 deletions(-) diff --git a/doc/cosign.md b/doc/cosign.md index ebfc9188ea1..1d270624151 100644 --- a/doc/cosign.md +++ b/doc/cosign.md @@ -26,6 +26,7 @@ cosign clean * [cosign initialize](cosign_initialize.md) - Initializes SigStore root to retrieve trusted certificate and key targets for verification. * [cosign manifest](cosign_manifest.md) - Provides utilities for discovering images in and performing operations on Kubernetes manifests * [cosign piv-tool](cosign_piv-tool.md) - Provides utilities for managing a hardware token +* [cosign pkcs11-tool](cosign_pkcs11-tool.md) - Provides utilities for retrieving information from a PKCS11 token. * [cosign policy](cosign_policy.md) - subcommand to manage a keyless policy. * [cosign public-key](cosign_public-key.md) - Gets a public key from the key-pair. * [cosign sign](cosign_sign.md) - Sign the supplied container image. diff --git a/doc/cosign_pkcs11-tool.md b/doc/cosign_pkcs11-tool.md index 11362b0a39f..291f68749e8 100644 --- a/doc/cosign_pkcs11-tool.md +++ b/doc/cosign_pkcs11-tool.md @@ -1,6 +1,6 @@ ## cosign pkcs11-tool -Provides utilities for retrieving information from a PKCS11 token +Provides utilities for retrieving information from a PKCS11 token. ### Options @@ -20,6 +20,6 @@ Provides utilities for retrieving information from a PKCS11 token ### SEE ALSO * [cosign](cosign.md) - -* [cosign pkcs11-tool list-tokens](cosign_pkcs11-tool_list-tokens.md) - list-tokens lists all PKCS11 tokens linked to a PKCS11 module * [cosign pkcs11-tool list-keys-uris](cosign_pkcs11-tool_list-keys-uris.md) - list-keys-uris lists URIs of all keys in a PKCS11 token +* [cosign pkcs11-tool list-tokens](cosign_pkcs11-tool_list-tokens.md) - list-tokens lists all PKCS11 tokens linked to a PKCS11 module diff --git a/doc/cosign_pkcs11-tool_list-keys-uris.md b/doc/cosign_pkcs11-tool_list-keys-uris.md index b563d6b3518..92362ae81b4 100644 --- a/doc/cosign_pkcs11-tool_list-keys-uris.md +++ b/doc/cosign_pkcs11-tool_list-keys-uris.md @@ -1,6 +1,6 @@ ## cosign pkcs11-tool list-keys-uris -lists URIs of all keys in a PKCS11 token +list-keys-uris lists URIs of all keys in a PKCS11 token ``` cosign pkcs11-tool list-keys-uris [flags] @@ -9,11 +9,10 @@ cosign pkcs11-tool list-keys-uris [flags] ### Options ``` - -h, --help help for list-keys-uris - --module-path string absolute path to the PKCS11 module - --pin string pin of the PKCS11 slot, uses environment variable COSIGN_PKCS11_PIN if empty - --slot-id int id of the PKCS11 slot, uses 0 if empty - + -h, --help help for list-keys-uris + --module-path string absolute path to the PKCS11 module + --pin string pin of the PKCS11 slot, uses environment variable COSIGN_PKCS11_PIN if empty + --slot-id uint id of the PKCS11 slot, uses 0 if empty ``` ### Options inherited from parent commands @@ -27,5 +26,5 @@ cosign pkcs11-tool list-keys-uris [flags] ### SEE ALSO -* [cosign pkcs11-tool](cosign_pkcs11-tool.md) - Provides utilities for retrieving information from a PKCS11 token +* [cosign pkcs11-tool](cosign_pkcs11-tool.md) - Provides utilities for retrieving information from a PKCS11 token. diff --git a/doc/cosign_pkcs11-tool_list-tokens.md b/doc/cosign_pkcs11-tool_list-tokens.md index efe25101c45..67a1c2b5a3d 100644 --- a/doc/cosign_pkcs11-tool_list-tokens.md +++ b/doc/cosign_pkcs11-tool_list-tokens.md @@ -1,6 +1,6 @@ ## cosign pkcs11-tool list-tokens -lists all PKCS11 tokens linked to a PKCS11 module +list-tokens lists all PKCS11 tokens linked to a PKCS11 module ``` cosign pkcs11-tool list-tokens [flags] @@ -9,9 +9,8 @@ cosign pkcs11-tool list-tokens [flags] ### Options ``` - -h, --help help for list-tokens - --module-path string absolute path to the PKCS11 module - + -h, --help help for list-tokens + --module-path string absolute path to the PKCS11 module ``` ### Options inherited from parent commands @@ -25,5 +24,5 @@ cosign pkcs11-tool list-tokens [flags] ### SEE ALSO -* [cosign pkcs11-tool](cosign_pkcs11-tool.md) - Provides utilities for retrieving information from a PKCS11 token +* [cosign pkcs11-tool](cosign_pkcs11-tool.md) - Provides utilities for retrieving information from a PKCS11 token. diff --git a/pkg/cosign/pkcs11key/disabled.go b/pkg/cosign/pkcs11key/disabled.go index b993d21fc7f..99b09c01146 100644 --- a/pkg/cosign/pkcs11key/disabled.go +++ b/pkg/cosign/pkcs11key/disabled.go @@ -21,9 +21,9 @@ import ( "context" "crypto" "crypto/x509" + "errors" "io" - "github.com/pkg/errors" "github.com/sigstore/sigstore/pkg/signature" ) diff --git a/pkg/cosign/pkcs11key/pkcs11key.go b/pkg/cosign/pkcs11key/pkcs11key.go index 0905cc2e1d2..e2417999c95 100644 --- a/pkg/cosign/pkcs11key/pkcs11key.go +++ b/pkg/cosign/pkcs11key/pkcs11key.go @@ -56,7 +56,7 @@ func GetKeyWithURIConfig(config *Pkcs11UriConfig, askForPinIfNeeded bool) (*Key, } // At least one of object and id must be specified. - if (config.KeyLabel == nil || len(config.KeyLabel) == 0) && (config.KeyID == nil || len(config.KeyID) == 0) { + if len(config.KeyLabel) == 0 && len(config.KeyID) == 0 { return nil, errors.New("one of keyLabel and keyID must be set") } @@ -161,9 +161,9 @@ func GetKeyWithURIConfig(config *Pkcs11UriConfig, askForPinIfNeeded bool) (*Key, // If both keyID and keyLabel are set, keyID has priority. var signer crypto11.Signer - if config.KeyID != nil && len(config.KeyID) != 0 { + if len(config.KeyID) != 0 { signer, err = ctx.FindKeyPair(config.KeyID, nil) - } else if config.KeyLabel != nil && len(config.KeyLabel) != 0 { + } else if len(config.KeyLabel) != 0 { signer, err = ctx.FindKeyPair(nil, config.KeyLabel) } if err != nil { @@ -173,9 +173,9 @@ func GetKeyWithURIConfig(config *Pkcs11UriConfig, askForPinIfNeeded bool) (*Key, // Key's corresponding cert might not exist, // therefore, we do not fail if it is the case. var cert *x509.Certificate - if config.KeyID != nil && len(config.KeyID) != 0 { + if len(config.KeyID) != 0 { cert, _ = ctx.FindCertificate(config.KeyID, nil, nil) - } else if config.KeyLabel != nil && len(config.KeyLabel) != 0 { + } else if len(config.KeyLabel) != 0 { cert, _ = ctx.FindCertificate(nil, config.KeyLabel, nil) } diff --git a/pkg/cosign/pkcs11key/util.go b/pkg/cosign/pkcs11key/util.go index 52c62754ea6..0376937c684 100644 --- a/pkg/cosign/pkcs11key/util.go +++ b/pkg/cosign/pkcs11key/util.go @@ -15,7 +15,6 @@ package pkcs11key import ( - "bytes" "fmt" "net/url" "os" @@ -28,21 +27,8 @@ import ( var pathAttrValueChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~:[]@!$'()*+,=&" var queryAttrValueChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~:[]@!$'()*+,=/?|" -func insertInto(s string, interval int, sep rune) string { - var buffer bytes.Buffer - before := interval - 1 - last := len(s) - 1 - for i, char := range s { - buffer.WriteRune(char) - if i%interval == before && i != last { - buffer.WriteRune(sep) - } - } - return buffer.String() -} - func percentEncode(input []byte) string { - if input == nil || len(input) == 0 { + if len(input) == 0 { return "" } @@ -57,7 +43,6 @@ func percentEncode(input []byte) string { func EncodeURIComponent(uriString string, isForPath bool, usePercenttEncoding bool) (string, error) { var stringBuilder strings.Builder - var encodedUriString string var allowedChars string if isForPath { @@ -87,8 +72,7 @@ func EncodeURIComponent(uriString string, isForPath bool, usePercenttEncoding bo } } - encodedUriString = stringBuilder.String() - return encodedUriString, nil + return stringBuilder.String(), nil } type Pkcs11UriConfig struct { @@ -201,7 +185,6 @@ func (conf *Pkcs11UriConfig) Parse(uriString string) error { } func (conf *Pkcs11UriConfig) Construct() (string, error) { - var modulePath, pinValue, tokenLabel, slotID, keyID, keyLabel string var err error @@ -213,7 +196,7 @@ func (conf *Pkcs11UriConfig) Construct() (string, error) { } // At least one of keyLabel and keyID must be specified. - if (conf.KeyLabel == nil || len(conf.KeyLabel) == 0) && (conf.KeyID == nil || len(conf.KeyID) == 0) { + if len(conf.KeyLabel) == 0 && len(conf.KeyID) == 0 { return "", errors.New("one of keyLabel and keyID must be set") } @@ -234,11 +217,11 @@ func (conf *Pkcs11UriConfig) Construct() (string, error) { slotID = fmt.Sprintf("%d", *conf.SlotID) uriString += ";slot-id=" + slotID } - if conf.KeyID != nil && len(conf.KeyID) != 0 { + if len(conf.KeyID) != 0 { keyID = percentEncode(conf.KeyID) uriString += ";id=" + keyID } - if conf.KeyLabel != nil && len(conf.KeyLabel) != 0 { + if len(conf.KeyLabel) != 0 { keyLabel, err = EncodeURIComponent(string(conf.KeyLabel), true, true) if err != nil { return "", errors.Wrap(err, "encode key label") From 22a0f3a3685f58fa9446c56ee3602ad5e0838c3a Mon Sep 17 00:00:00 2001 From: Kieran Miller Date: Sat, 6 Nov 2021 00:37:23 +0000 Subject: [PATCH 9/9] Reduce duplication of code by moving PKCS11 URI detection down. Signed-off-by: Kieran Miller --- cmd/cosign/cli/publickey/public_key.go | 34 ++++---------- cmd/cosign/cli/sign/sign.go | 46 ++++++------------- cmd/cosign/cli/verify/verify.go | 33 +++----------- cmd/cosign/cli/verify/verify_attestation.go | 31 +++---------- cmd/cosign/cli/verify/verify_blob.go | 31 +++---------- pkg/cosign/pkcs11key/util.go | 4 ++ pkg/signature/keys.go | 49 +++++++++++++++++++-- test/pkcs11_test.go | 7 ++- 8 files changed, 98 insertions(+), 137 deletions(-) diff --git a/cmd/cosign/cli/publickey/public_key.go b/cmd/cosign/cli/publickey/public_key.go index e5d4b9c3c85..24e8f94ee08 100644 --- a/cmd/cosign/cli/publickey/public_key.go +++ b/cmd/cosign/cli/publickey/public_key.go @@ -20,7 +20,6 @@ import ( "fmt" "io" "os" - "strings" "github.com/pkg/errors" @@ -47,32 +46,15 @@ func GetPublicKey(ctx context.Context, opts Pkopts, writer NamedWriter, pf cosig var k signature.PublicKeyProvider switch { case opts.KeyRef != "": - // If -key starts with pkcs11:, we assume it is a PKCS11 URI and use it to get the PKCS11 Key. - if strings.HasPrefix(opts.KeyRef, "pkcs11:") { - pkcs11UriConfig := pkcs11key.NewPkcs11UriConfig() - err := pkcs11UriConfig.Parse(opts.KeyRef) - if err != nil { - return errors.Wrap(err, "parsing pkcs11 uri") - } - - sk, err := pkcs11key.GetKeyWithURIConfig(pkcs11UriConfig, false) - if err != nil { - return errors.Wrap(err, "opening pkcs11 token key") - } - defer sk.Close() - - pk, err := sk.Verifier() - if err != nil { - return errors.Wrap(err, "initializing pkcs11 token verifier") - } - k = pk - } else { - s, err := sigs.SignerFromKeyRef(ctx, opts.KeyRef, pf) - if err != nil { - return err - } - k = s + s, err := sigs.SignerFromKeyRef(ctx, opts.KeyRef, pf) + if err != nil { + return err + } + pkcs11Key, ok := s.(*pkcs11key.Key) + if ok { + defer pkcs11Key.Close() } + k = s case opts.Sk: sk, err := pivkey.GetKeyWithSlot(opts.Slot) if err != nil { diff --git a/cmd/cosign/cli/sign/sign.go b/cmd/cosign/cli/sign/sign.go index 9bc1cbbdfec..7adb5f1d81e 100644 --- a/cmd/cosign/cli/sign/sign.go +++ b/cmd/cosign/cli/sign/sign.go @@ -327,31 +327,18 @@ func SignerFromKeyOpts(ctx context.Context, certPath string, ko KeyOpts) (*CertS }, nil case ko.KeyRef != "": - // If -key starts with pkcs11:, we assume it is a PKCS11 URI and use it to get the PKCS11 Key. - if strings.HasPrefix(ko.KeyRef, "pkcs11:") { - pkcs11UriConfig := pkcs11key.NewPkcs11UriConfig() - err := pkcs11UriConfig.Parse(ko.KeyRef) - if err != nil { - return nil, err - } - - // Since we'll be signing, we need to set askForPinIsNeeded to true - // because we need access to the private key. - sk, err := pkcs11key.GetKeyWithURIConfig(pkcs11UriConfig, true) - if err != nil { - return nil, err - } - - sv, err := sk.SignerVerifier() - if err != nil { - return nil, err - } + k, err := sigs.SignerVerifierFromKeyRef(ctx, ko.KeyRef, ko.PassFunc) + if err != nil { + return nil, errors.Wrap(err, "reading key") + } - // Handle the -cert flag. - // With PKCS11, we assume the certificate is in the same slot on the PKCS11 - // token as the private key. If it's not there, show a warning to the - // user. - certFromPKCS11, err := sk.Certificate() + // Handle the -cert flag + // With PKCS11, we assume the certificate is in the same slot on the PKCS11 + // token as the private key. If it's not there, show a warning to the + // user. + pkcs11Key, ok := k.(*pkcs11key.Key) + if ok { + certFromPKCS11, err := pkcs11Key.Certificate() var pemBytes []byte if err != nil { fmt.Fprintln(os.Stderr, "warning: no x509 certificate retrieved from the PKCS11 token") @@ -364,20 +351,13 @@ func SignerFromKeyOpts(ctx context.Context, certPath string, ko KeyOpts) (*CertS return &CertSignVerifier{ Cert: pemBytes, - SignerVerifier: sv, - close: sk.Close, + SignerVerifier: k, + close: pkcs11Key.Close, }, nil } - - k, err := sigs.SignerVerifierFromKeyRef(ctx, ko.KeyRef, ko.PassFunc) - if err != nil { - return nil, errors.Wrap(err, "reading key") - } - certSigner := &CertSignVerifier{ SignerVerifier: k, } - // Handle the -cert flag if certPath == "" { return certSigner, nil } diff --git a/cmd/cosign/cli/verify/verify.go b/cmd/cosign/cli/verify/verify.go index b0fef6a4f6b..3271716a96b 100644 --- a/cmd/cosign/cli/verify/verify.go +++ b/cmd/cosign/cli/verify/verify.go @@ -22,7 +22,6 @@ import ( "flag" "fmt" "os" - "strings" "github.com/google/go-containerregistry/pkg/name" "github.com/pkg/errors" @@ -91,31 +90,13 @@ func (c *VerifyCommand) Exec(ctx context.Context, images []string) (err error) { // Keys are optional! var pubKey signature.Verifier if keyRef != "" { - // If -key starts with pkcs11:, we assume it is a PKCS11 URI and use it to get the PKCS11 Key. - if strings.HasPrefix(keyRef, "pkcs11:") { - pkcs11UriConfig := pkcs11key.NewPkcs11UriConfig() - err := pkcs11UriConfig.Parse(keyRef) - if err != nil { - return errors.Wrap(err, "parsing pkcs11 uri") - } - - // Since we'll be verifying a signature, we do not need to set askForPinIsNeeded to true - // because we only need access to the public key. - sk, err := pkcs11key.GetKeyWithURIConfig(pkcs11UriConfig, false) - if err != nil { - return errors.Wrap(err, "opening pkcs11 token key") - } - defer sk.Close() - - pubKey, err = sk.Verifier() - if err != nil { - return errors.Wrap(err, "initializing pkcs11 token verifier") - } - } else { - pubKey, err = sigs.PublicKeyFromKeyRef(ctx, keyRef) - if err != nil { - return errors.Wrap(err, "loading public key") - } + pubKey, err = sigs.PublicKeyFromKeyRef(ctx, keyRef) + if err != nil { + return errors.Wrap(err, "loading public key") + } + pkcs11Key, ok := pubKey.(*pkcs11key.Key) + if ok { + defer pkcs11Key.Close() } } else if c.Sk { sk, err := pivkey.GetKeyWithSlot(c.Slot) diff --git a/cmd/cosign/cli/verify/verify_attestation.go b/cmd/cosign/cli/verify/verify_attestation.go index e27db3a8e68..72aee484147 100644 --- a/cmd/cosign/cli/verify/verify_attestation.go +++ b/cmd/cosign/cli/verify/verify_attestation.go @@ -23,7 +23,6 @@ import ( "fmt" "os" "path/filepath" - "strings" "github.com/google/go-containerregistry/pkg/name" "github.com/in-toto/in-toto-golang/in_toto" @@ -82,29 +81,13 @@ func (c *VerifyAttestationCommand) Exec(ctx context.Context, images []string) (e // Keys are optional! if keyRef != "" { - // If -key starts with pkcs11:, we assume it is a PKCS11 URI and use it to get the PKCS11 Key. - if strings.HasPrefix(keyRef, "pkcs11:") { - pkcs11UriConfig := pkcs11key.NewPkcs11UriConfig() - err := pkcs11UriConfig.Parse(keyRef) - if err != nil { - return errors.Wrap(err, "parsing pkcs11 uri") - } - - sk, err := pkcs11key.GetKeyWithURIConfig(pkcs11UriConfig, false) - if err != nil { - return errors.Wrap(err, "opening pkcs11 token key") - } - defer sk.Close() - - co.SigVerifier, err = sk.Verifier() - if err != nil { - return errors.Wrap(err, "initializing pkcs11 token verifier") - } - } else { - co.SigVerifier, err = sigs.PublicKeyFromKeyRef(ctx, keyRef) - if err != nil { - return errors.Wrap(err, "loading public key") - } + co.SigVerifier, err = sigs.PublicKeyFromKeyRef(ctx, keyRef) + if err != nil { + return errors.Wrap(err, "loading public key") + } + pkcs11Key, ok := co.SigVerifier.(*pkcs11key.Key) + if ok { + defer pkcs11Key.Close() } } else if c.Sk { sk, err := pivkey.GetKeyWithSlot(c.Slot) diff --git a/cmd/cosign/cli/verify/verify_blob.go b/cmd/cosign/cli/verify/verify_blob.go index 861f67f65a7..8b7bb749dd3 100644 --- a/cmd/cosign/cli/verify/verify_blob.go +++ b/cmd/cosign/cli/verify/verify_blob.go @@ -26,7 +26,6 @@ import ( "fmt" "io" "os" - "strings" "github.com/go-openapi/runtime" "github.com/pkg/errors" @@ -91,29 +90,13 @@ func VerifyBlobCmd(ctx context.Context, ko sign.KeyOpts, certRef, sigRef, blobRe // Keys are optional! switch { case ko.KeyRef != "": - // If -key starts with pkcs11:, we assume it is a PKCS11 URI and use it to get the PKCS11 Key. - if strings.HasPrefix(ko.KeyRef, "pkcs11:") { - pkcs11UriConfig := pkcs11key.NewPkcs11UriConfig() - err := pkcs11UriConfig.Parse(ko.KeyRef) - if err != nil { - return errors.Wrap(err, "parsing pkcs11 uri") - } - - sk, err := pkcs11key.GetKeyWithURIConfig(pkcs11UriConfig, false) - if err != nil { - return errors.Wrap(err, "opening pkcs11 token key") - } - defer sk.Close() - - pubKey, err = sk.Verifier() - if err != nil { - return errors.Wrap(err, "initializing pkcs11 token verifier") - } - } else { - pubKey, err = sigs.PublicKeyFromKeyRef(ctx, ko.KeyRef) - if err != nil { - return errors.Wrap(err, "loading public key") - } + pubKey, err = sigs.PublicKeyFromKeyRef(ctx, ko.KeyRef) + if err != nil { + return errors.Wrap(err, "loading public key") + } + pkcs11Key, ok := pubKey.(*pkcs11key.Key) + if ok { + defer pkcs11Key.Close() } case ko.Sk: sk, err := pivkey.GetKeyWithSlot(ko.Slot) diff --git a/pkg/cosign/pkcs11key/util.go b/pkg/cosign/pkcs11key/util.go index 0376937c684..296f91f519a 100644 --- a/pkg/cosign/pkcs11key/util.go +++ b/pkg/cosign/pkcs11key/util.go @@ -24,6 +24,10 @@ import ( "github.com/pkg/errors" ) +const ( + ReferenceScheme = "pkcs11:" +) + var pathAttrValueChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~:[]@!$'()*+,=&" var queryAttrValueChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~:[]@!$'()*+,=/?|" diff --git a/pkg/signature/keys.go b/pkg/signature/keys.go index e6d019b9cd1..f284da301b3 100644 --- a/pkg/signature/keys.go +++ b/pkg/signature/keys.go @@ -28,6 +28,7 @@ import ( "github.com/sigstore/cosign/pkg/cosign/git" "github.com/sigstore/cosign/pkg/cosign/git/gitlab" "github.com/sigstore/cosign/pkg/cosign/kubernetes" + "github.com/sigstore/cosign/pkg/cosign/pkcs11key" "github.com/sigstore/sigstore/pkg/cryptoutils" "github.com/sigstore/sigstore/pkg/signature" "github.com/sigstore/sigstore/pkg/signature/kms" @@ -86,7 +87,28 @@ func SignerVerifierFromKeyRef(ctx context.Context, keyRef string, pf cosign.Pass } } - if strings.HasPrefix(keyRef, kubernetes.KeyReference) { + switch { + case strings.HasPrefix(keyRef, pkcs11key.ReferenceScheme): + pkcs11UriConfig := pkcs11key.NewPkcs11UriConfig() + err := pkcs11UriConfig.Parse(keyRef) + if err != nil { + return nil, errors.Wrap(err, "parsing pkcs11 uri") + } + + // Since we'll be signing, we need to set askForPinIsNeeded to true + // because we need access to the private key. + sk, err := pkcs11key.GetKeyWithURIConfig(pkcs11UriConfig, true) + if err != nil { + return nil, errors.Wrap(err, "opening pkcs11 token key") + } + + sv, err := sk.SignerVerifier() + if err != nil { + return nil, errors.Wrap(err, "initializing pkcs11 token signer verifier") + } + + return sv, nil + case strings.HasPrefix(keyRef, kubernetes.KeyReference): s, err := kubernetes.GetKeyPairSecret(ctx, keyRef) if err != nil { return nil, err @@ -95,7 +117,7 @@ func SignerVerifierFromKeyRef(ctx context.Context, keyRef string, pf cosign.Pass if len(s.Data) > 0 { return cosign.LoadECDSAPrivateKey(s.Data["cosign.key"], s.Data["cosign.password"]) } - } else if strings.HasPrefix(keyRef, gitlab.ReferenceScheme) { + case strings.HasPrefix(keyRef, gitlab.ReferenceScheme): split := strings.Split(keyRef, "://") if len(split) < 2 { @@ -115,6 +137,7 @@ func SignerVerifierFromKeyRef(ctx context.Context, keyRef string, pf cosign.Pass } return cosign.LoadECDSAPrivateKey([]byte(pk), []byte(pass)) + default: } return loadKey(keyRef, pf) @@ -132,7 +155,27 @@ func PublicKeyFromKeyRef(ctx context.Context, keyRef string) (signature.Verifier } } - if strings.HasPrefix(keyRef, gitlab.ReferenceScheme) { + if strings.HasPrefix(keyRef, pkcs11key.ReferenceScheme) { + pkcs11UriConfig := pkcs11key.NewPkcs11UriConfig() + err := pkcs11UriConfig.Parse(keyRef) + if err != nil { + return nil, errors.Wrap(err, "parsing pkcs11 uri") + } + + // Since we'll be verifying a signature, we do not need to set askForPinIsNeeded to true + // because we only need access to the public key. + sk, err := pkcs11key.GetKeyWithURIConfig(pkcs11UriConfig, false) + if err != nil { + return nil, errors.Wrap(err, "opening pkcs11 token key") + } + + v, err := sk.Verifier() + if err != nil { + return nil, errors.Wrap(err, "initializing pkcs11 token verifier") + } + + return v, nil + } else if strings.HasPrefix(keyRef, gitlab.ReferenceScheme) { split := strings.Split(keyRef, "://") if len(split) < 2 { diff --git a/test/pkcs11_test.go b/test/pkcs11_test.go index 997f0d3abd3..93fb063da46 100644 --- a/test/pkcs11_test.go +++ b/test/pkcs11_test.go @@ -259,12 +259,17 @@ func TestSignAndVerify(t *testing.T) { t.Fatal(err) } + v, err := sk.Verifier() + if err != nil { + t.Fatal(err) + } + sig, err := sv.SignMessage(bytes.NewReader([]byte("hello, world!"))) if err != nil { t.Fatal(err) } - err = sv.VerifySignature(bytes.NewReader(sig), bytes.NewReader([]byte("hello, world!"))) + err = v.VerifySignature(bytes.NewReader(sig), bytes.NewReader([]byte("hello, world!"))) if err != nil { t.Fatal(err) }