diff options
Diffstat (limited to 'pkg/signature/kms/gcp/signer.go')
-rw-r--r-- | pkg/signature/kms/gcp/signer.go | 195 |
1 files changed, 195 insertions, 0 deletions
diff --git a/pkg/signature/kms/gcp/signer.go b/pkg/signature/kms/gcp/signer.go new file mode 100644 index 0000000..445f9d9 --- /dev/null +++ b/pkg/signature/kms/gcp/signer.go @@ -0,0 +1,195 @@ +// +// 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 gcp + +import ( + "context" + "crypto" + "fmt" + "hash/crc32" + "io" + + "github.com/sigstore/sigstore/pkg/signature" + "github.com/sigstore/sigstore/pkg/signature/options" + "google.golang.org/api/option" +) + +var gcpSupportedHashFuncs = []crypto.Hash{ + crypto.SHA256, + crypto.SHA512, + crypto.SHA384, +} + +// SignerVerifier creates and verifies digital signatures over a message using GCP KMS service +type SignerVerifier struct { + defaultCtx context.Context + client *gcpClient +} + +// LoadSignerVerifier generates signatures using the specified key object in GCP KMS and hash algorithm. +// +// It also can verify signatures locally using the public key. hashFunc must not be crypto.Hash(0). +func LoadSignerVerifier(defaultCtx context.Context, referenceStr string, opts ...option.ClientOption) (*SignerVerifier, error) { + g := &SignerVerifier{ + defaultCtx: defaultCtx, + } + + var err error + g.client, err = newGCPClient(defaultCtx, referenceStr, opts...) + if err != nil { + return nil, err + } + + return g, nil +} + +// SignMessage signs the provided message using GCP KMS. If the message is provided, +// this method will compute the digest according to the hash function specified +// when the Signer was created. +// +// SignMessage recognizes the following Options listed in order of preference: +// +// - WithContext() +// +// - WithDigest() +// +// - WithCryptoSignerOpts() +// +// All other options are ignored if specified. +func (g *SignerVerifier) SignMessage(message io.Reader, opts ...signature.SignOption) ([]byte, error) { + ctx := context.Background() + var digest []byte + var signerOpts crypto.SignerOpts + var err error + + signerOpts, err = g.client.getHashFunc() + if err != nil { + return nil, fmt.Errorf("getting fetching default hash function: %w", err) + } + + for _, opt := range opts { + opt.ApplyContext(&ctx) + opt.ApplyDigest(&digest) + opt.ApplyCryptoSignerOpts(&signerOpts) + } + + digest, hf, err := signature.ComputeDigestForSigning(message, signerOpts.HashFunc(), gcpSupportedHashFuncs, opts...) + if err != nil { + return nil, err + } + + crc32cHasher := crc32.New(crc32.MakeTable(crc32.Castagnoli)) + _, err = crc32cHasher.Write(digest) + if err != nil { + return nil, err + } + + return g.client.sign(ctx, digest, hf, crc32cHasher.Sum32()) +} + +// PublicKey returns the public key that can be used to verify signatures created by +// this signer. If the caller wishes to specify the context to use to obtain +// the public key, pass option.WithContext(desiredCtx). +// +// All other options are ignored if specified. +func (g *SignerVerifier) PublicKey(opts ...signature.PublicKeyOption) (crypto.PublicKey, error) { + ctx := context.Background() + for _, opt := range opts { + opt.ApplyContext(&ctx) + } + + return g.client.public(ctx) +} + +// VerifySignature verifies the signature for the given message. Unless provided +// in an option, the digest of the message will be computed using the hash function specified +// when the SignerVerifier was created. +// +// This function returns nil if the verification succeeded, and an error message otherwise. +// +// This function recognizes the following Options listed in order of preference: +// +// - WithDigest() +// +// All other options are ignored if specified. +func (g *SignerVerifier) VerifySignature(signature, message io.Reader, opts ...signature.VerifyOption) error { + return g.client.verify(signature, message, opts...) +} + +// CreateKey attempts to create a new key in Vault with the specified algorithm. +func (g *SignerVerifier) CreateKey(ctx context.Context, algorithm string) (crypto.PublicKey, error) { + return g.client.createKey(ctx, algorithm) +} + +type cryptoSignerWrapper struct { + ctx context.Context + hashFunc crypto.Hash + sv *SignerVerifier + errFunc func(error) +} + +func (c cryptoSignerWrapper) Public() crypto.PublicKey { + pk, err := c.sv.PublicKey(options.WithContext(c.ctx)) + if err != nil && c.errFunc != nil { + c.errFunc(err) + } + return pk +} + +func (c cryptoSignerWrapper) Sign(_ io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) { + hashFunc := c.hashFunc + if opts != nil { + hashFunc = opts.HashFunc() + } + gcpOptions := []signature.SignOption{ + options.WithContext(c.ctx), + options.WithDigest(digest), + options.WithCryptoSignerOpts(hashFunc), + } + + return c.sv.SignMessage(nil, gcpOptions...) +} + +// CryptoSigner returns a crypto.Signer object that uses the underlying SignerVerifier, along with a crypto.SignerOpts object +// that allows the KMS to be used in APIs that only accept the standard golang objects +func (g *SignerVerifier) CryptoSigner(ctx context.Context, errFunc func(error)) (crypto.Signer, crypto.SignerOpts, error) { + defaultHf, err := g.client.getHashFunc() + if err != nil { + return nil, nil, fmt.Errorf("getting fetching default hash function: %w", err) + } + + csw := &cryptoSignerWrapper{ + ctx: ctx, + sv: g, + hashFunc: defaultHf, + errFunc: errFunc, + } + + return csw, defaultHf, nil +} + +// SupportedAlgorithms returns the list of algorithms supported by the GCP KMS service +func (g *SignerVerifier) SupportedAlgorithms() (result []string) { + for k := range algorithmMap { + result = append(result, k) + } + return +} + +// DefaultAlgorithm returns the default algorithm for the GCP KMS service +func (g *SignerVerifier) DefaultAlgorithm() string { + return AlgorithmECDSAP256SHA256 +} |