...

Source file src/github.com/sigstore/cosign/v2/pkg/signature/keys.go

Documentation: github.com/sigstore/cosign/v2/pkg/signature

     1  // Copyright 2021 The Sigstore Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package signature
    16  
    17  import (
    18  	"context"
    19  	"crypto"
    20  	"errors"
    21  	"fmt"
    22  	"strings"
    23  
    24  	"github.com/sigstore/cosign/v2/pkg/blob"
    25  	"github.com/sigstore/cosign/v2/pkg/cosign"
    26  	"github.com/sigstore/cosign/v2/pkg/cosign/git"
    27  	"github.com/sigstore/cosign/v2/pkg/cosign/git/gitlab"
    28  	"github.com/sigstore/cosign/v2/pkg/cosign/kubernetes"
    29  	"github.com/sigstore/cosign/v2/pkg/cosign/pkcs11key"
    30  	"github.com/sigstore/sigstore/pkg/cryptoutils"
    31  	"github.com/sigstore/sigstore/pkg/signature"
    32  
    33  	"github.com/sigstore/sigstore/pkg/signature/kms"
    34  )
    35  
    36  // LoadPublicKey is a wrapper for VerifierForKeyRef, hardcoding SHA256 as the hash algorithm
    37  func LoadPublicKey(ctx context.Context, keyRef string) (verifier signature.Verifier, err error) {
    38  	return VerifierForKeyRef(ctx, keyRef, crypto.SHA256)
    39  }
    40  
    41  // VerifierForKeyRef parses the given keyRef, loads the key and returns an appropriate
    42  // verifier using the provided hash algorithm
    43  func VerifierForKeyRef(ctx context.Context, keyRef string, hashAlgorithm crypto.Hash) (verifier signature.Verifier, err error) {
    44  	// The key could be plaintext, in a file, at a URL, or in KMS.
    45  	var perr *kms.ProviderNotFoundError
    46  	kmsKey, err := kms.Get(ctx, keyRef, hashAlgorithm)
    47  	switch {
    48  	case err == nil:
    49  		// KMS specified
    50  		return kmsKey, nil
    51  	case errors.As(err, &perr):
    52  		// We can ignore ProviderNotFoundError; that just means the keyRef
    53  		// didn't match any of the KMS schemes.
    54  	default:
    55  		// But other errors indicate something more insidious; pass those
    56  		// through.
    57  		return nil, err
    58  	}
    59  
    60  	raw, err := blob.LoadFileOrURL(keyRef)
    61  
    62  	if err != nil {
    63  		return nil, err
    64  	}
    65  
    66  	// PEM encoded file.
    67  	pubKey, err := cryptoutils.UnmarshalPEMToPublicKey(raw)
    68  	if err != nil {
    69  		return nil, fmt.Errorf("pem to public key: %w", err)
    70  	}
    71  
    72  	return signature.LoadVerifier(pubKey, hashAlgorithm)
    73  }
    74  
    75  func loadKey(keyPath string, pf cosign.PassFunc) (signature.SignerVerifier, error) {
    76  	kb, err := blob.LoadFileOrURL(keyPath)
    77  	if err != nil {
    78  		return nil, err
    79  	}
    80  	pass := []byte{}
    81  	if pf != nil {
    82  		pass, err = pf(false)
    83  		if err != nil {
    84  			return nil, err
    85  		}
    86  	}
    87  	return cosign.LoadPrivateKey(kb, pass)
    88  }
    89  
    90  // LoadPublicKeyRaw loads a verifier from a PEM-encoded public key
    91  func LoadPublicKeyRaw(raw []byte, hashAlgorithm crypto.Hash) (signature.Verifier, error) {
    92  	pub, err := cryptoutils.UnmarshalPEMToPublicKey(raw)
    93  	if err != nil {
    94  		return nil, err
    95  	}
    96  	return signature.LoadVerifier(pub, hashAlgorithm)
    97  }
    98  
    99  func SignerFromKeyRef(ctx context.Context, keyRef string, pf cosign.PassFunc) (signature.Signer, error) {
   100  	return SignerVerifierFromKeyRef(ctx, keyRef, pf)
   101  }
   102  
   103  func SignerVerifierFromKeyRef(ctx context.Context, keyRef string, pf cosign.PassFunc) (signature.SignerVerifier, error) {
   104  	switch {
   105  	case strings.HasPrefix(keyRef, pkcs11key.ReferenceScheme):
   106  		pkcs11UriConfig := pkcs11key.NewPkcs11UriConfig()
   107  		err := pkcs11UriConfig.Parse(keyRef)
   108  		if err != nil {
   109  			return nil, fmt.Errorf("parsing pkcs11 uri: %w", err)
   110  		}
   111  
   112  		// Since we'll be signing, we need to set askForPinIsNeeded to true
   113  		// because we need access to the private key.
   114  		sk, err := pkcs11key.GetKeyWithURIConfig(pkcs11UriConfig, true)
   115  		if err != nil {
   116  			return nil, fmt.Errorf("opening pkcs11 token key: %w", err)
   117  		}
   118  
   119  		sv, err := sk.SignerVerifier()
   120  		if err != nil {
   121  			return nil, fmt.Errorf("initializing pkcs11 token signer verifier: %w", err)
   122  		}
   123  
   124  		return sv, nil
   125  	case strings.HasPrefix(keyRef, kubernetes.KeyReference):
   126  		s, err := kubernetes.GetKeyPairSecret(ctx, keyRef)
   127  		if err != nil {
   128  			return nil, err
   129  		}
   130  
   131  		if len(s.Data) > 0 {
   132  			return cosign.LoadPrivateKey(s.Data["cosign.key"], s.Data["cosign.password"])
   133  		}
   134  	case strings.HasPrefix(keyRef, gitlab.ReferenceScheme):
   135  		split := strings.Split(keyRef, "://")
   136  
   137  		if len(split) < 2 {
   138  			return nil, errors.New("could not parse scheme, use <scheme>://<ref> format")
   139  		}
   140  
   141  		provider, targetRef := split[0], split[1]
   142  
   143  		pk, err := git.GetProvider(provider).GetSecret(ctx, targetRef, "COSIGN_PRIVATE_KEY")
   144  		if err != nil {
   145  			return nil, err
   146  		}
   147  
   148  		pass, err := git.GetProvider(provider).GetSecret(ctx, targetRef, "COSIGN_PASSWORD")
   149  		if err != nil {
   150  			return nil, err
   151  		}
   152  
   153  		return cosign.LoadPrivateKey([]byte(pk), []byte(pass))
   154  	}
   155  
   156  	if strings.Contains(keyRef, "://") {
   157  		sv, err := kms.Get(ctx, keyRef, crypto.SHA256)
   158  		if err == nil {
   159  			return sv, nil
   160  		}
   161  		var e *kms.ProviderNotFoundError
   162  		if !errors.As(err, &e) {
   163  			return nil, fmt.Errorf("kms get: %w", err)
   164  		}
   165  		// ProviderNotFoundError is okay; loadKey handles other URL schemes
   166  	}
   167  
   168  	return loadKey(keyRef, pf)
   169  }
   170  
   171  func PublicKeyFromKeyRef(ctx context.Context, keyRef string) (signature.Verifier, error) {
   172  	return PublicKeyFromKeyRefWithHashAlgo(ctx, keyRef, crypto.SHA256)
   173  }
   174  
   175  func PublicKeyFromKeyRefWithHashAlgo(ctx context.Context, keyRef string, hashAlgorithm crypto.Hash) (signature.Verifier, error) {
   176  	if strings.HasPrefix(keyRef, kubernetes.KeyReference) {
   177  		s, err := kubernetes.GetKeyPairSecret(ctx, keyRef)
   178  		if err != nil {
   179  			return nil, err
   180  		}
   181  
   182  		if len(s.Data) > 0 {
   183  			return LoadPublicKeyRaw(s.Data["cosign.pub"], hashAlgorithm)
   184  		}
   185  	}
   186  
   187  	if strings.HasPrefix(keyRef, pkcs11key.ReferenceScheme) {
   188  		pkcs11UriConfig := pkcs11key.NewPkcs11UriConfig()
   189  		err := pkcs11UriConfig.Parse(keyRef)
   190  		if err != nil {
   191  			return nil, fmt.Errorf("parsing pkcs11 uri): %w", err)
   192  		}
   193  
   194  		// Since we'll be verifying a signature, we do not need to set askForPinIsNeeded to true
   195  		// because we only need access to the public key.
   196  		sk, err := pkcs11key.GetKeyWithURIConfig(pkcs11UriConfig, false)
   197  		if err != nil {
   198  			return nil, fmt.Errorf("opening pkcs11 token key: %w", err)
   199  		}
   200  
   201  		v, err := sk.Verifier()
   202  		if err != nil {
   203  			return nil, fmt.Errorf("initializing pkcs11 token verifier: %w", err)
   204  		}
   205  
   206  		return v, nil
   207  	} else if strings.HasPrefix(keyRef, gitlab.ReferenceScheme) {
   208  		split := strings.Split(keyRef, "://")
   209  
   210  		if len(split) < 2 {
   211  			return nil, errors.New("could not parse scheme, use <scheme>://<ref> format")
   212  		}
   213  
   214  		provider, targetRef := split[0], split[1]
   215  
   216  		pubKey, err := git.GetProvider(provider).GetSecret(ctx, targetRef, "COSIGN_PUBLIC_KEY")
   217  		if err != nil {
   218  			return nil, err
   219  		}
   220  
   221  		if len(pubKey) > 0 {
   222  			return LoadPublicKeyRaw([]byte(pubKey), hashAlgorithm)
   223  		}
   224  	}
   225  
   226  	return VerifierForKeyRef(ctx, keyRef, hashAlgorithm)
   227  }
   228  
   229  func PublicKeyPem(key signature.PublicKeyProvider, pkOpts ...signature.PublicKeyOption) ([]byte, error) {
   230  	pub, err := key.PublicKey(pkOpts...)
   231  	if err != nil {
   232  		return nil, err
   233  	}
   234  	return cryptoutils.MarshalPublicKeyToPEM(pub)
   235  }
   236  

View as plain text