forked from in-toto/go-witness
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Loading status checks…
review of AWS KMS signer and adding scrappy implementation of GCP Signer
- needs cleanup and testing Signed-off-by: chaosinthecrd <[email protected]>
1 parent
b8eee30
commit eda08d5
Showing
7 changed files
with
961 additions
and
65 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
module github.com/in-toto/go-witness/signer/kms/gcp | ||
|
||
replace github.com/in-toto/go-witness => ../../../ | ||
|
||
go 1.19 | ||
|
||
require ( | ||
cloud.google.com/go/kms v1.15.5 | ||
github.com/in-toto/go-witness v0.0.0-00010101000000-000000000000 | ||
github.com/jellydator/ttlcache/v3 v3.1.1 | ||
github.com/sigstore/sigstore v1.6.4 | ||
google.golang.org/protobuf v1.32.0 | ||
) | ||
|
||
require ( | ||
cloud.google.com/go/compute v1.23.3 // indirect | ||
cloud.google.com/go/compute/metadata v0.2.3 // indirect | ||
cloud.google.com/go/iam v1.1.5 // indirect | ||
github.com/felixge/httpsnoop v1.0.4 // indirect | ||
github.com/go-logr/logr v1.3.0 // indirect | ||
github.com/go-logr/stdr v1.2.2 // indirect | ||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect | ||
github.com/golang/protobuf v1.5.3 // indirect | ||
github.com/google/go-containerregistry v0.17.0 // indirect | ||
github.com/google/s2a-go v0.1.7 // indirect | ||
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect | ||
github.com/googleapis/gax-go/v2 v2.12.0 // indirect | ||
github.com/letsencrypt/boulder v0.0.0-20230907030200-6d76a0f91e1e // indirect | ||
github.com/opencontainers/go-digest v1.0.0 // indirect | ||
github.com/theupdateframework/go-tuf v0.5.2 // indirect | ||
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect | ||
go.opencensus.io v0.24.0 // indirect | ||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 // indirect | ||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 // indirect | ||
go.opentelemetry.io/otel v1.21.0 // indirect | ||
go.opentelemetry.io/otel/metric v1.21.0 // indirect | ||
go.opentelemetry.io/otel/trace v1.21.0 // indirect | ||
golang.org/x/crypto v0.17.0 // indirect | ||
golang.org/x/net v0.19.0 // indirect | ||
golang.org/x/oauth2 v0.15.0 // indirect | ||
golang.org/x/sync v0.5.0 // indirect | ||
golang.org/x/sys v0.15.0 // indirect | ||
golang.org/x/term v0.15.0 // indirect | ||
golang.org/x/text v0.14.0 // indirect | ||
golang.org/x/time v0.5.0 // indirect | ||
google.golang.org/api v0.154.0 // indirect | ||
google.golang.org/appengine v1.6.8 // indirect | ||
google.golang.org/genproto v0.0.0-20231120223509-83a465c0220f // indirect | ||
google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f // indirect | ||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231127180814-3a041ad873d4 // indirect | ||
google.golang.org/grpc v1.59.0 // indirect | ||
gopkg.in/go-jose/go-jose.v2 v2.6.1 // indirect | ||
gopkg.in/yaml.v3 v3.0.1 // indirect | ||
) |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,208 @@ | ||
// Copyright 2023 The Witness Contributors | ||
// | ||
// 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/in-toto/go-witness/cryptoutil" | ||
"github.com/in-toto/go-witness/log" | ||
kms "github.com/in-toto/go-witness/signer/kms" | ||
) | ||
|
||
var gcpSupportedHashFuncs = []crypto.Hash{ | ||
crypto.SHA256, | ||
crypto.SHA512, | ||
crypto.SHA384, | ||
} | ||
|
||
// SignerVerifier is a cryptoutil.SignerVerifier that uses the AWS Key Management Service | ||
type SignerVerifier struct { | ||
client *gcpClient | ||
hashFunc crypto.Hash | ||
} | ||
|
||
// LoadSignerVerifier generates signatures using the specified key object in AWS KMS and hash algorithm. | ||
func LoadSignerVerifier(ctx context.Context, ksp *kms.KMSSignerProvider) (*SignerVerifier, error) { | ||
g := &SignerVerifier{} | ||
|
||
var err error | ||
g.client, err = newGCPClient(ctx, ksp) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
for _, hashFunc := range gcpSupportedHashFuncs { | ||
if hashFunc == ksp.HashFunc { | ||
g.hashFunc = ksp.HashFunc | ||
} | ||
} | ||
|
||
if g.hashFunc == 0 { | ||
return nil, fmt.Errorf("unsupported hash function: %v", ksp.HashFunc) | ||
} | ||
|
||
return g, nil | ||
} | ||
|
||
// NOTE: This might be all wrong but setting it like so for now | ||
// | ||
// KeyID returns the key identifier for the key used by this signer. | ||
func (g *SignerVerifier) KeyID() (string, error) { | ||
return g.client.keyID, nil | ||
} | ||
|
||
// Sign 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. | ||
func (g *SignerVerifier) Sign(message io.Reader) ([]byte, error) { | ||
var err error | ||
ctx := context.Background() | ||
var digest []byte | ||
|
||
var signerOpts crypto.SignerOpts | ||
signerOpts, err = g.client.getHashFunc() | ||
if err != nil { | ||
return nil, fmt.Errorf("getting fetching default hash function: %w", err) | ||
} | ||
|
||
hf := signerOpts.HashFunc() | ||
|
||
digest, _, err = cryptoutil.ComputeDigestForSigning(message, hf, gcpSupportedHashFuncs) | ||
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()) | ||
} | ||
|
||
// Verifier returns a cryptoutil.Verifier that can be used to verify signatures created by this signer. | ||
func (g *SignerVerifier) Verifier() (cryptoutil.Verifier, error) { | ||
return g, nil | ||
} | ||
|
||
// PublicKey returns the public key that can be used to verify signatures created by | ||
// this signer. | ||
func (g *SignerVerifier) PublicKey(ctx context.Context) (crypto.PublicKey, error) { | ||
return g.client.public(ctx) | ||
} | ||
|
||
// Bytes returns the bytes of the public key that can be used to verify signatures created by the signer. | ||
func (g *SignerVerifier) Bytes() ([]byte, error) { | ||
ckv, err := g.client.getCKV() | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to get KMS key version: %w", err) | ||
} | ||
|
||
return cryptoutil.PublicPemBytes(ckv.PublicKey) | ||
} | ||
|
||
// VerifySignature verifies the signature for the given message, returning | ||
// nil if the verification succeeded, and an error message otherwise. | ||
func (g *SignerVerifier) Verify(message io.Reader, sig []byte) (err error) { | ||
var digest []byte | ||
|
||
var signerOpts crypto.SignerOpts | ||
signerOpts, err = g.client.getHashFunc() | ||
if err != nil { | ||
return fmt.Errorf("getting hash func: %w", err) | ||
} | ||
hf := signerOpts.HashFunc() | ||
|
||
digest, _, err = cryptoutil.ComputeDigestForVerifying(message, hf, gcpSupportedHashFuncs) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = g.client.verify(digest, sig) | ||
if err != nil { | ||
log.Info(err.Error()) | ||
} | ||
|
||
return err | ||
} | ||
|
||
// NOTE:Wondering if this should exist, at least for now | ||
// | ||
// CreateKey attempts to create a new key in Vault with the specified algorithm. | ||
func (a *SignerVerifier) CreateKey(ctx context.Context, algorithm string) (crypto.PublicKey, error) { | ||
return a.client.createKey(ctx, algorithm) | ||
} | ||
|
||
type cryptoSignerWrapper struct { | ||
ctx context.Context | ||
hashFunc crypto.Hash | ||
sv *SignerVerifier | ||
errFunc func(error) | ||
} | ||
|
||
func (c *cryptoSignerWrapper) Public() crypto.PublicKey { | ||
ctx := context.Background() | ||
|
||
pk, err := c.sv.PublicKey(ctx) | ||
if err != nil && c.errFunc != nil { | ||
c.errFunc(err) | ||
} | ||
return pk | ||
} | ||
|
||
func (c *cryptoSignerWrapper) Sign(message io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) { | ||
if opts != nil { | ||
c.hashFunc = opts.HashFunc() | ||
} | ||
|
||
return c.sv.Sign(message) | ||
} | ||
|
||
// 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 AWS KMS service | ||
func (*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 | ||
} |