From e405a20fc5136199be20d8a94006a4a2a4eae9b1 Mon Sep 17 00:00:00 2001 From: chaosinthecrd Date: Mon, 29 Jan 2024 18:41:57 +0000 Subject: [PATCH] adding logic so policy signature can be checked against constraints Signed-off-by: chaosinthecrd --- policy/policy.go | 30 ++++-------------------- policy/step.go | 27 ++++++++++++++++++++++ verify.go | 59 ++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 88 insertions(+), 28 deletions(-) diff --git a/policy/policy.go b/policy/policy.go index 3eddcd94..799a2c0b 100644 --- a/policy/policy.go +++ b/policy/policy.go @@ -236,35 +236,13 @@ func (step Step) checkFunctionaries(verifiedStatements []source.VerifiedCollecti } for _, verifier := range verifiedStatement.Verifiers { - verifierID, err := verifier.KeyID() - if err != nil { - log.Debugf("(policy) skipping verifier: could not get key id: %w", err) - continue - } - for _, functionary := range step.Functionaries { - if functionary.PublicKeyID != "" && functionary.PublicKeyID == verifierID { - collections = append(collections, verifiedStatement) - break - } - - x509Verifier, ok := verifier.(*cryptoutil.X509Verifier) - if !ok { - log.Debugf("(policy) skipping verifier: verifier with ID %v is not a public key verifier or a x509 verifier", verifierID) - continue - } - - if len(functionary.CertConstraint.Roots) == 0 { - log.Debugf("(policy) skipping verifier: verifier with ID %v is an x509 verifier, but step %v does not have any truested roots", verifierID, step) - continue - } - - if err := functionary.CertConstraint.Check(x509Verifier, trustBundles); err != nil { - log.Debugf("(policy) skipping verifier: verifier with ID %v doesn't meet certificate constraint: %w", verifierID, err) + if err := functionary.Validate(verifier, trustBundles); err != nil { + log.Debugf("(policy) skipping verifier: %w", err) continue + } else { + collections = append(collections, verifiedStatement) } - - collections = append(collections, verifiedStatement) } } } diff --git a/policy/step.go b/policy/step.go index b1b63ef0..ea451bf8 100644 --- a/policy/step.go +++ b/policy/step.go @@ -19,6 +19,7 @@ import ( "strings" "github.com/in-toto/go-witness/attestation" + "github.com/in-toto/go-witness/cryptoutil" "github.com/in-toto/go-witness/source" ) @@ -80,6 +81,32 @@ type RejectedCollection struct { Reason error } +func (f Functionary) Validate(verifier cryptoutil.Verifier, trustBundles map[string]TrustBundle) error { + verifierID, err := verifier.KeyID() + if err != nil { + return fmt.Errorf("could not get key id: %w", err) + } + + if f.PublicKeyID != "" && f.PublicKeyID == verifierID { + return nil + } + + x509Verifier, ok := verifier.(*cryptoutil.X509Verifier) + if !ok { + return fmt.Errorf("verifier with ID %v is not a public key verifier or a x509 verifier", verifierID) + } + + if len(f.CertConstraint.Roots) == 0 { + return fmt.Errorf("verifier with ID %v is an x509 verifier, but no trusted roots provided in functionary", verifierID) + } + + if err := f.CertConstraint.Check(x509Verifier, trustBundles); err != nil { + return fmt.Errorf("verifier with ID %v doesn't meet certificate constraint: %w", verifierID, err) + } + + return nil +} + // validateAttestations will test each collection against to ensure the expected attestations // appear in the collection as well as that any rego policies pass for the step. func (s Step) validateAttestations(verifiedCollections []source.VerifiedCollection) StepResult { diff --git a/verify.go b/verify.go index a0d2bdab..0e32b64e 100644 --- a/verify.go +++ b/verify.go @@ -17,12 +17,14 @@ package witness import ( "context" "crypto/x509" + "encoding/base64" "encoding/json" "fmt" "io" "github.com/in-toto/go-witness/cryptoutil" "github.com/in-toto/go-witness/dsse" + "github.com/in-toto/go-witness/log" "github.com/in-toto/go-witness/policy" "github.com/in-toto/go-witness/source" "github.com/in-toto/go-witness/timestamp" @@ -97,10 +99,12 @@ func Verify(ctx context.Context, policyEnvelope dsse.Envelope, policyVerifiers [ opt(&vo) } - if _, err := vo.policyEnvelope.Verify(dsse.VerifyWithVerifiers(vo.policyVerifiers...), dsse.VerifyWithTimestampVerifiers(vo.policyTimestampAuthorities...), dsse.VerifyWithRoots(vo.policyCARoots...), dsse.VerifyWithIntermediates(vo.policyCAIntermediates...)); err != nil { - return nil, fmt.Errorf("could not verify policy: %w", err) + if err := verifyPolicySignature(ctx, vo); err != nil { + return nil, fmt.Errorf("failed to verify policy signature: %w", err) } + log.Info("Policy signature verification passed") + pol := policy.Policy{} if err := json.Unmarshal(vo.policyEnvelope.Payload, &pol); err != nil { return nil, fmt.Errorf("failed to unmarshal policy from envelope: %w", err) @@ -154,3 +158,54 @@ func Verify(ctx context.Context, policyEnvelope dsse.Envelope, policyVerifiers [ return accepted, nil } + +func verifyPolicySignature(ctx context.Context, vo verifyOptions) error { + passedPolicyVerifiers, err := vo.policyEnvelope.Verify(dsse.VerifyWithVerifiers(vo.policyVerifiers...), dsse.VerifyWithTimestampVerifiers(vo.policyTimestampAuthorities...), dsse.VerifyWithRoots(vo.policyCARoots...), dsse.VerifyWithIntermediates(vo.policyCAIntermediates...)) + if err != nil { + return fmt.Errorf("could not verify policy: %w", err) + } + + var passed bool + for _, verifier := range passedPolicyVerifiers { + kid, err := verifier.Verifier.KeyID() + if err != nil { + return fmt.Errorf("could not get verifier key id: %w", err) + } + v, ok := verifier.Verifier.(*cryptoutil.X509Verifier) + if !ok { + log.Debug("Policy Verifier %s is not an X509Verifier, continuing...", kid) + } + + rootIDs := make([]string, 0) + trustBundle := make(map[string]policy.TrustBundle) + for _, root := range vo.policyCARoots { + id := base64.StdEncoding.EncodeToString(root.Raw) + rootIDs = append(rootIDs, id) + trustBundle[id] = policy.TrustBundle{ + Root: root, + } + } + + f := policy.Functionary{ + Type: "root", + CertConstraint: policy.CertConstraint{ + Roots: rootIDs, + CommonName: "*", + }, + } + + err = f.Validate(v, trustBundle) + if err != nil { + log.Debugf("Policy Verifier %s failed failed to match supplied constraints: %w, continuing...", err, kid) + continue + } + + passed = true + } + + if !passed { + return fmt.Errorf("no policy verifiers passed verification") + } else { + return nil + } +}