embed the google vsa key, match against all signatures, match the subject digests

Signed-off-by: Ramon Petgrave <ramon.petgrave64@gmail.com>
This commit is contained in:
Ramon Petgrave
2024-06-18 22:18:25 +00:00
parent ead4e9bf4e
commit 13a74b5b4a
3 changed files with 96 additions and 20 deletions

View File

@@ -1 +1,10 @@
{"payload":"eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjEiLCJwcmVkaWNhdGVUeXBlIjoiaHR0cHM6Ly9zbHNhLmRldi92ZXJpZmljYXRpb25fc3VtbWFyeS92MSIsInByZWRpY2F0ZSI6eyJ0aW1lVmVyaWZpZWQiOiIyMDI0LTA2LTEyVDA3OjI0OjM0LjM1MTYwOFoiLCJ2ZXJpZmllciI6eyJpZCI6Imh0dHBzOi8vYmNpZC5jb3JwLmdvb2dsZS5jb20vdmVyaWZpZXIvYmNpZF9wYWNrYWdlX2VuZm9yY2VyL3YwLjEifSwidmVyaWZpY2F0aW9uUmVzdWx0IjoiUEFTU0VEIiwidmVyaWZpZWRMZXZlbHMiOlsiQkNJRF9MMSIsIlNMU0FfQlVJTERfTEVWRUxfMiJdLCJyZXNvdXJjZVVyaSI6ImdjZV9pbWFnZTovL2drZS1ub2RlLWltYWdlczpna2UtMTI2MTUtZ2tlMTQxODAwMC1jb3MtMTAxLTE3MTYyLTQ2My0yOS1jLWNncHYxLXByZSIsInBvbGljeSI6eyJ1cmkiOiJnb29nbGVmaWxlOi9nb29nbGVfc3JjL2ZpbGVzLzY0MjUxMzE5Mi9kZXBvdC9nb29nbGUzL3Byb2R1Y3Rpb24vc2VjdXJpdHkvYmNpZC9zb2Z0d2FyZS9nY2VfaW1hZ2UvZ2tlL3ZtX2ltYWdlcy5zd19wb2xpY3kudGV4dHByb3RvIn19LCJzdWJqZWN0IjpbeyJuYW1lIjoiXyIsImRpZ2VzdCI6eyJnY2VfaW1hZ2VfaWQiOiI4OTcwMDk1MDA1MzA2MDAwMDUzIn19XX0=","payloadType":"application/vnd.in-toto+json","signatures":[{"sig":"bmIy2gfnQt6oYpd0WbpQMtZcMRtmntDmyki+Be+2Z9qkboMVbi2RQAD1b5AWbBs7iAP8NZVJOI4R/4jOVYB/FA==","keyid":"keystore://76574:prod:vsa_signing_public_key"}]}
{
"payload": "eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjEiLCJwcmVkaWNhdGVUeXBlIjoiaHR0cHM6Ly9zbHNhLmRldi92ZXJpZmljYXRpb25fc3VtbWFyeS92MSIsInByZWRpY2F0ZSI6eyJ0aW1lVmVyaWZpZWQiOiIyMDI0LTA2LTEyVDA3OjI0OjM0LjM1MTYwOFoiLCJ2ZXJpZmllciI6eyJpZCI6Imh0dHBzOi8vYmNpZC5jb3JwLmdvb2dsZS5jb20vdmVyaWZpZXIvYmNpZF9wYWNrYWdlX2VuZm9yY2VyL3YwLjEifSwidmVyaWZpY2F0aW9uUmVzdWx0IjoiUEFTU0VEIiwidmVyaWZpZWRMZXZlbHMiOlsiQkNJRF9MMSIsIlNMU0FfQlVJTERfTEVWRUxfMiJdLCJyZXNvdXJjZVVyaSI6ImdjZV9pbWFnZTovL2drZS1ub2RlLWltYWdlczpna2UtMTI2MTUtZ2tlMTQxODAwMC1jb3MtMTAxLTE3MTYyLTQ2My0yOS1jLWNncHYxLXByZSIsInBvbGljeSI6eyJ1cmkiOiJnb29nbGVmaWxlOi9nb29nbGVfc3JjL2ZpbGVzLzY0MjUxMzE5Mi9kZXBvdC9nb29nbGUzL3Byb2R1Y3Rpb24vc2VjdXJpdHkvYmNpZC9zb2Z0d2FyZS9nY2VfaW1hZ2UvZ2tlL3ZtX2ltYWdlcy5zd19wb2xpY3kudGV4dHByb3RvIn19LCJzdWJqZWN0IjpbeyJuYW1lIjoiXyIsImRpZ2VzdCI6eyJnY2VfaW1hZ2VfaWQiOiI4OTcwMDk1MDA1MzA2MDAwMDUzIn19XX0=",
"payloadType": "application/vnd.in-toto+json",
"signatures": [
{
"sig": "bmIy2gfnQt6oYpd0WbpQMtZcMRtmntDmyki+Be+2Z9qkboMVbi2RQAD1b5AWbBs7iAP8NZVJOI4R/4jOVYB/FA==",
"keyid": "keystore://76574:prod:vsa_signing_public_key"
}
]
}

View File

@@ -0,0 +1,12 @@
package keys
// GoogleVSASigningPublicKey is the public key used to verify Google VSA signatures.
const GoogleVSASigningPublicKey = `-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeGa6ZCZn0q6WpaUwJrSk+PPYEsca
3Xkk3UrxvbQtoZzTmq0zIYq+4QQl0YBedSyy+XcwAMaUWTouTrB05WhYtg==
-----END PUBLIC KEY-----`
// AttestorKeys is a map of Attestor IDs to their public keys.
var AttestorKeys = map[string]string{
"keystore://76574:prod:vsa_signing_public_key": GoogleVSASigningPublicKey,
}

View File

@@ -4,6 +4,7 @@ import (
"context"
"crypto"
"fmt"
"strings"
"github.com/secure-systems-lab/go-securesystemslib/dsse"
sigstoreBundle "github.com/sigstore/sigstore-go/pkg/bundle"
@@ -12,6 +13,7 @@ import (
sigstoreDSSE "github.com/sigstore/sigstore/pkg/signature/dsse"
serrors "github.com/slsa-framework/slsa-verifier/v2/errors"
"github.com/slsa-framework/slsa-verifier/v2/options"
vsaKeys "github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/vsa/keys"
vsa10 "github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/vsa/v1.0"
"github.com/slsa-framework/slsa-verifier/v2/verifiers/utils"
)
@@ -56,28 +58,37 @@ func VerifyVSA(ctx context.Context,
return nil, nil, nil
}
// verifyEnvelopeSignature verifies the signatures of the envelope, requiring at least one signature to be valid.
func verifyEnvelopeSignature(ctx context.Context, sigstoreEnvelope *sigstoreBundle.Envelope) error {
pubKeyBytes := []byte(`-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeGa6ZCZn0q6WpaUwJrSk+PPYEsca
3Xkk3UrxvbQtoZzTmq0zIYq+4QQl0YBedSyy+XcwAMaUWTouTrB05WhYtg==
-----END PUBLIC KEY-----`)
keyID := "keystore://76574:prod:vsa_signing_public_key"
pubKey, err := sigstoreCryptoUtils.UnmarshalPEMToPublicKey(pubKeyBytes)
if err != nil {
return fmt.Errorf("%w: %w", serrors.ErrorInvalidPublicKey, err)
// assemble an "adapter" for each of the signatures and their KeyID
var verifierAdapters []dsse.Verifier
for _, signature := range sigstoreEnvelope.Envelope.Signatures {
keyID := signature.KeyID
pubKeyString, ok := vsaKeys.AttestorKeys[keyID]
if !ok {
continue
}
pubKey, err := sigstoreCryptoUtils.UnmarshalPEMToPublicKey([]byte(pubKeyString))
if err != nil {
return fmt.Errorf("%w: %w", serrors.ErrorInvalidPublicKey, err)
}
signatureVerifier, err := sigstoreSignature.LoadVerifier(pubKey, crypto.SHA256)
if err != nil {
return fmt.Errorf("%w: loading sigstore DSSE envolope verifier %w", serrors.ErrorInvalidPublicKey, err)
}
verifierAdapter := &sigstoreDSSE.VerifierAdapter{
SignatureVerifier: signatureVerifier,
Pub: pubKey,
PubKeyID: keyID, // "keystore://76574:prod:vsa_signing_public_key"
}
verifierAdapters = append(verifierAdapters, verifierAdapter)
}
signatureVerifier, err := sigstoreSignature.LoadVerifier(pubKey, crypto.SHA256)
// create the envelope verifier with all adapters
envelopeVerifier, err := dsse.NewEnvelopeVerifier(verifierAdapters...)
if err != nil {
return fmt.Errorf("%w: loading sigstore DSSE envolope verifier %w", serrors.ErrorInvalidPublicKey, err)
}
envelopeVerifier, err := dsse.NewEnvelopeVerifier(&sigstoreDSSE.VerifierAdapter{
SignatureVerifier: signatureVerifier,
Pub: pubKey,
PubKeyID: keyID, // "keystore://76574:prod:vsa_signing_public_key"
})
if err != nil {
return fmt.Errorf("%w: creating verifier %w", serrors.ErrorInvalidPublicKey, err)
return fmt.Errorf("%w: creating sigstore DSSE envelope verifier %w", serrors.ErrorInvalidPublicKey, err)
}
// verify the envelope
_, err = envelopeVerifier.Verify(ctx, sigstoreEnvelope.Envelope)
if err != nil {
return fmt.Errorf("%w: verifying envelope %w", serrors.ErrorInvalidPublicKey, err)
@@ -85,7 +96,51 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeGa6ZCZn0q6WpaUwJrSk+PPYEsca
return nil
}
// matchExpectedValues checks if the expected values are present in the VSA.
func matchExpectedValues(vsa *vsa10.VSA, vsaOpts *options.VSAOpts) error {
// TODO: implement this function
if err := matchExepectedSubjectDigests(vsa, vsaOpts); err != nil {
return err
}
// TODO: match other expected values
return nil
}
// matchExepectedSubjectDigests checks if the expected subject digests are present in the VSA.
func matchExepectedSubjectDigests(vsa *vsa10.VSA, vsaOpts *options.VSAOpts) error {
// collect all digests from the VSA, so we can efficiently search, e.g.:
// {
// "sha256": {
// "abc": true,
// "def": true,
// },
// "gce_image_id": {
// "123": true,
// "456": true,
// }
// }
allVSASubjectDigests := make(map[string]map[string]bool)
for _, subject := range vsa.Subject {
for digestType, digestValue := range subject.Digest {
if _, ok := allVSASubjectDigests[digestType]; !ok {
allVSASubjectDigests[digestType] = make(map[string]bool)
}
allVSASubjectDigests[digestType][digestValue] = true
}
}
// search for the expected digests in the VSA
for _, expectedDigest := range vsaOpts.ExpectedDigests {
parts := strings.SplitN(expectedDigest, ":", 2)
if len(parts) != 2 {
return fmt.Errorf("%w: expected digest %s is not in the format <digest type>:<digest value>", serrors.ErrorInvalidDssePayload, expectedDigest)
}
digestType := parts[0]
digestValue := parts[1]
if _, ok := allVSASubjectDigests[digestType]; !ok {
return fmt.Errorf("%w: expected digest not found: %s", serrors.ErrorInvalidDssePayload, expectedDigest)
}
if _, ok := allVSASubjectDigests[digestType][digestValue]; !ok {
return fmt.Errorf("%w: expected digest not found: %s", serrors.ErrorInvalidDssePayload, expectedDigest)
}
}
return nil
}