mirror of
https://github.com/slsa-framework/slsa-verifier.git
synced 2026-05-17 05:56:37 +00:00
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:
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
12
verifiers/internal/vsa/keys/static.go
Normal file
12
verifiers/internal/vsa/keys/static.go
Normal 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,
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user