Skip to content
This repository has been archived by the owner on Apr 9, 2024. It is now read-only.

Commit

Permalink
Simplify verifier, and explicitly pass binary name and digest to Gene…
Browse files Browse the repository at this point in the history
…rateEndorsement
  • Loading branch information
rbehjati committed Jul 12, 2023
1 parent 303fa54 commit 91f9f37
Show file tree
Hide file tree
Showing 9 changed files with 158 additions and 247 deletions.
2 changes: 1 addition & 1 deletion cmd/verifier/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func main() {

provenanceVerifier := verification.ProvenanceIRVerifier{
Got: provenanceIR,
Want: &prover.VerificationOptions{},
Want: &prover.ProvenanceReferenceValues{},
}

if err := provenanceVerifier.Verify(); err != nil {
Expand Down
49 changes: 33 additions & 16 deletions internal/endorser/endorser.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,26 +44,31 @@ type ParsedProvenance struct {
SourceMetadata claims.ProvenanceData
}

// GenerateEndorsement generates an endorsement statement for the given validity duration, using
// the given provenances as evidence and reference values to verify them. At least one provenance
// must be provided. The endorsement statement is generated only if the provenance statements are
// valid.
func GenerateEndorsement(referenceValues *prover.VerificationOptions, validityDuration claims.ClaimValidity, provenances []ParsedProvenance) (*intoto.Statement, error) {
verifiedProvenances, err := verifyAndSummarizeProvenances(referenceValues, provenances)
// GenerateEndorsement generates an endorsement statement for the given binary
// and the given validity duration, using the given provenances as evidence and
// reference values to verify them. If more than one provenance statements are
// provided the endorsement statement is generated only if the provenance
// statements are valid. If no provenances are provided, a provenance-less
// endorsement is generated, if the input verification options does not contain
// a reference provenance.
func GenerateEndorsement(binaryName, binaryDigest string, verOpt *prover.VerificationOptions, validityDuration claims.ClaimValidity, provenances []ParsedProvenance) (*intoto.Statement, error) {
verifiedProvenances, err := verifyAndSummarizeProvenances(binaryName, binaryDigest, verOpt, provenances)
if err != nil {
return nil, fmt.Errorf("could not verify and summarize provenances: %v", err)
}

return claims.GenerateEndorsementStatement(validityDuration, *verifiedProvenances), nil
}

// Returns an instance of claims.VerifiedProvenanceSet, containing metadata about a set of verified
// provenances, or an error. An error is returned if any of the following conditions is met:
// (1) The list of provenances is empty,
// (2) Any of the provenances is invalid (see verifyProvenances for details on validity),
// Returns an instance of claims.VerifiedProvenanceSet, containing metadata
// about a set of verified provenances, or an error. An error is returned if
// any of the following conditions is met:
// (1) The list of provenances is empty, but verification options contains a
// nonempty provenance reference.
// (2) Any of the provenances is invalid (see verifyProvenances for details),
// (3) Provenances do not match (e.g., have different binary names).
func verifyAndSummarizeProvenances(referenceValues *prover.VerificationOptions, provenances []ParsedProvenance) (*claims.VerifiedProvenanceSet, error) {
if len(provenances) == 0 {
func verifyAndSummarizeProvenances(binaryName, binaryDigest string, verOpt *prover.VerificationOptions, provenances []ParsedProvenance) (*claims.VerifiedProvenanceSet, error) {
if len(provenances) == 0 && verOpt.GetReferenceProvenance() != nil {
return nil, fmt.Errorf("at least one provenance file must be provided")
}

Expand All @@ -74,22 +79,34 @@ func verifyAndSummarizeProvenances(referenceValues *prover.VerificationOptions,
provenancesData = append(provenancesData, p.SourceMetadata)
}

errs := multierr.Append(verifyConsistency(provenanceIRs), verifyProvenances(referenceValues, provenanceIRs))
errs := multierr.Append(verifyConsistency(provenanceIRs), verifyProvenances(verOpt.GetReferenceProvenance(), provenanceIRs))

if len(provenanceIRs) > 0 {
if provenanceIRs[0].BinarySHA256Digest() != binaryDigest {
errs = multierr.Append(errs, fmt.Errorf("the binary digest in the provenance (%q) does not match the given binary digest (%q)",
provenanceIRs[0].BinarySHA256Digest(), binaryDigest))
}
if provenanceIRs[0].BinaryName() != binaryName {
errs = multierr.Append(errs, fmt.Errorf("the binary name in the provenance (%q) does not match the given binary name (%q)",
provenanceIRs[0].BinaryName(), binaryName))
}
}

if errs != nil {
return nil, fmt.Errorf("failed while verifying of provenances: %v", errs)
}

verifiedProvenances := claims.VerifiedProvenanceSet{
BinaryDigest: provenanceIRs[0].BinarySHA256Digest(),
BinaryName: provenanceIRs[0].BinaryName(),
BinaryDigest: binaryDigest,
BinaryName: binaryName,
Provenances: provenancesData,
}

return &verifiedProvenances, nil
}

// verifyProvenances verifies the given list of provenances. An error is returned if verification fails for one of them.
func verifyProvenances(referenceValues *prover.VerificationOptions, provenances []model.ProvenanceIR) error {
func verifyProvenances(referenceValues *prover.ProvenanceReferenceValues, provenances []model.ProvenanceIR) error {
var errs error
for index := range provenances {
provenanceVerifier := verification.ProvenanceIRVerifier{
Expand Down
74 changes: 27 additions & 47 deletions internal/endorser/endorser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,10 @@ import (
)

const (
binaryHash = "d059c38cea82047ad316a1c6c6fbd13ecf7a0abdcc375463920bd25bf5c142cc"
binaryName = "oak_functions_freestanding_bin"
errorBinaryDigest = "do not contain the actual SHA256 digest"
binaryHash = "d059c38cea82047ad316a1c6c6fbd13ecf7a0abdcc375463920bd25bf5c142cc"
binaryName = "oak_functions_freestanding_bin"
errorBinaryDigest = "does not match the given binary digest"
errorInconsistentProvenances = "provenances are not consistent"
)

func TestGenerateEndorsement_SingleValidEndorsement(t *testing.T) {
Expand All @@ -51,12 +52,12 @@ func TestGenerateEndorsement_SingleValidEndorsement(t *testing.T) {
t.Fatalf("Could not load provenances: %v", err)
}

referenceValues, err := loadTextprotoVerificationOptions("../../testdata/reference_values.textproto")
verOpt, err := loadTextprotoVerificationOptions("../../testdata/reference_values.textproto")
if err != nil {
t.Fatalf("Could not load reference values: %v", err)
}

statement, err := GenerateEndorsement(referenceValues, validity, provenances)
statement, err := GenerateEndorsement(binaryName, binaryHash, verOpt, validity, provenances)
if err != nil {
t.Fatalf("Could not generate endorsement from %q: %v", provenances[0].SourceMetadata.URI, err)
}
Expand Down Expand Up @@ -86,13 +87,13 @@ func TestLoadAndVerifyProvenances_MultipleValidEndorsement(t *testing.T) {

referenceValues := prover.VerificationOptions{
ReferenceProvenance: &prover.ProvenanceReferenceValues{
// Make sure we pick the correct binary hash if there are several reference values.
ReferenceBinaryDigests: &prover.Digests{
Digests: map[string]*prover.StringAllowList{"sha256": {Values: []string{binaryHash + "_diff", binaryHash}}},
},
// // Make sure we pick the correct binary hash if there are several reference values.
// ReferenceBinaryDigests: &prover.Digests{
// Digests: map[string]*prover.StringAllowList{"sha256": {Values: []string{binaryHash + "_diff", binaryHash}}},
// },
},
}
provenanceSet, err := verifyAndSummarizeProvenances(&referenceValues, provenances)
provenanceSet, err := verifyAndSummarizeProvenances(binaryName, binaryHash, &referenceValues, provenances)
if err != nil {
t.Fatalf("Could not generate endorsement from %q: %v", provenances[0].SourceMetadata.URI, err)
}
Expand All @@ -119,16 +120,12 @@ func TestLoadAndVerifyProvenances_ConsistentNotVerified(t *testing.T) {
if err != nil {
t.Fatalf("Could not load provenances: %v", err)
}
referenceValues := prover.VerificationOptions{
ReferenceProvenance: &prover.ProvenanceReferenceValues{
ReferenceBinaryDigests: &prover.Digests{
Digests: map[string]*prover.StringAllowList{"sha256": {Values: []string{binaryHash + "_diff"}}},
},
},
verOpts := prover.VerificationOptions{
ReferenceProvenance: &prover.ProvenanceReferenceValues{},
}

// Provenances do not contain the given reference binary SHA256 digest value, but are consistent.
_, err = verifyAndSummarizeProvenances(&referenceValues, provenances)
_, err = verifyAndSummarizeProvenances(binaryName, binaryHash+"_diff", &verOpts, provenances)
if err == nil || !strings.Contains(err.Error(), errorBinaryDigest) {
t.Fatalf("got %q, want error message containing %q,", err, errorBinaryDigest)
}
Expand All @@ -149,19 +146,14 @@ func TestLoadAndVerify_InconsistentVerified(t *testing.T) {
if err != nil {
t.Fatalf("Could not load provenances: %v", err)
}
referenceValues := prover.VerificationOptions{
ReferenceProvenance: &prover.ProvenanceReferenceValues{
ReferenceBinaryDigests: &prover.Digests{
Digests: map[string]*prover.StringAllowList{"sha256": {Values: []string{"e8e05d1d09af8952919bf6ab38e0cc5a6414ee2b5e21f4765b12421c5db0037e", binaryHash}}},
},
},
verOpt := prover.VerificationOptions{
ReferenceProvenance: &prover.ProvenanceReferenceValues{},
}

// Provenances each contain a (different) given reference binary SHA256 digest value, but are inconsistent.
_, err = verifyAndSummarizeProvenances(&referenceValues, provenances)
want := "provenances are not consistent"
if err == nil || !strings.Contains(err.Error(), want) {
t.Fatalf("got %q, want error message containing %q,", err, want)
_, err = verifyAndSummarizeProvenances(binaryName, binaryHash, &verOpt, provenances)
if err == nil || !strings.Contains(err.Error(), errorInconsistentProvenances) {
t.Fatalf("got %q, want error message containing %q,", err, errorInconsistentProvenances)
}
}

Expand All @@ -180,22 +172,17 @@ func TestLoadAndVerify_InconsistentNotVerified(t *testing.T) {
if err != nil {
t.Fatalf("Could not load provenances: %v", err)
}
referenceValues := prover.VerificationOptions{
ReferenceProvenance: &prover.ProvenanceReferenceValues{
ReferenceBinaryDigests: &prover.Digests{
Digests: map[string]*prover.StringAllowList{"sha256": {Values: []string{binaryHash + "_diff"}}},
},
},
verOpt := prover.VerificationOptions{
ReferenceProvenance: &prover.ProvenanceReferenceValues{},
}

_, err = verifyAndSummarizeProvenances(&referenceValues, provenances)
_, err = verifyAndSummarizeProvenances(binaryName, binaryHash+"_diff", &verOpt, provenances)
if err == nil || !strings.Contains(err.Error(), errorBinaryDigest) {
t.Fatalf("got %q, want error message containing %q,", err, errorBinaryDigest)
}

want2 := "provenances are not consistent"
if err == nil || !strings.Contains(err.Error(), want2) {
t.Fatalf("got %q, want error message containing %q,", err, want2)
if err == nil || !strings.Contains(err.Error(), errorInconsistentProvenances) {
t.Fatalf("got %q, want error message containing %q,", err, errorInconsistentProvenances)
}
}

Expand All @@ -209,24 +196,17 @@ func TestLoadAndVerifyProvenances_NotVerified(t *testing.T) {
if err != nil {
t.Fatalf("Could not load provenances: %v", err)
}
// referenceValues, err := verification.LoadReferenceValuesFromFile("../../testdata/different_reference_values.toml")
referenceValues, err := loadTextprotoVerificationOptions("../../testdata/different_reference_values.textproto")
verOpts, err := loadTextprotoVerificationOptions("../../testdata/different_reference_values.textproto")
if err != nil {
t.Fatalf("Could not load reference values: %v", err)
}

_, err = verifyAndSummarizeProvenances(referenceValues, provenances)

_, err = verifyAndSummarizeProvenances(binaryName, "a_different_digest", verOpts, provenances)
if err == nil || !strings.Contains(err.Error(), errorBinaryDigest) {
t.Fatalf("got %q, want error message containing %q,", err, errorBinaryDigest)
}

want := "failed to verify binary SHA256 digest"
if err == nil || !strings.Contains(err.Error(), want) {
t.Fatalf("got %q, want error message containing %q,", err, want)
}

want = "is different from the repo URI"
want := "is different from the repo URI"
if err == nil || !strings.Contains(err.Error(), want) {
t.Fatalf("got %q, want error message containing %q,", err, want)
}
Expand Down
30 changes: 5 additions & 25 deletions internal/verification/verifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,22 +27,14 @@ import (
// all non-empty fields in got using fields in the reference values. Empty fields will not be verified.
type ProvenanceIRVerifier struct {
Got *model.ProvenanceIR
Want *prover.VerificationOptions
Want *prover.ProvenanceReferenceValues
}

// Verify verifies an instance of ProvenanceIRVerifier by comparing its Got and Want fields.
// Verify checks fields, which (i) are set in Got, i.e., GotHasX is true, and (ii) are set in Want.
func (v *ProvenanceIRVerifier) Verify() error {
if v.Want.GetReferenceProvenance() == nil {
return nil
}

var errs error

if err := v.verifyBinarySHA256Digest(); err != nil {
multierr.AppendInto(&errs, fmt.Errorf("failed to verify binary SHA256 digest: %v", err))
}

// Verify HasBuildCmd.
multierr.AppendInto(&errs, v.verifyBuildCmd())

Expand All @@ -62,22 +54,10 @@ func (v *ProvenanceIRVerifier) Verify() error {
return errs
}

// verifyBinarySHA256Digest verifies that the binary SHA256 Got in this
// verifier is among the Wanted digests.
func (v *ProvenanceIRVerifier) verifyBinarySHA256Digest() error {
referenceDigests := v.Want.GetReferenceProvenance().GetReferenceBinaryDigests()
gotBinarySHA256Digest := v.Got.BinarySHA256Digest()

if err := verifySHA256Digest(gotBinarySHA256Digest, referenceDigests); err != nil {
return fmt.Errorf("verifying binary SHA356 digest: %v", err)
}
return nil
}

// verifyBuildCmd verifies the build cmd. Returns an error if a build command is
// needed in the Want reference values, but is not present in the Got provenance.
func (v *ProvenanceIRVerifier) verifyBuildCmd() error {
mustHaveBuildCommand := v.Want.GetReferenceProvenance().GetMustHaveBuildCommand()
mustHaveBuildCommand := v.Want.GetMustHaveBuildCommand()
if mustHaveBuildCommand && v.Got.HasBuildCmd() {
if buildCmd, err := v.Got.BuildCmd(); err != nil || len(buildCmd) == 0 {
return fmt.Errorf("no build cmd found")
Expand All @@ -95,7 +75,7 @@ func (v *ProvenanceIRVerifier) verifyBuilderImageDigest() error {
return nil
}

referenceDigests := v.Want.GetReferenceProvenance().GetReferenceBuilderImageDigests()
referenceDigests := v.Want.GetReferenceBuilderImageDigests()
gotBuilderImageDigest, err := v.Got.BuilderImageSHA256Digest()
if err != nil && referenceDigests != nil {
return fmt.Errorf("no builder image digest set")
Expand All @@ -110,7 +90,7 @@ func (v *ProvenanceIRVerifier) verifyBuilderImageDigest() error {
// verifyRepoURI verifies that the Git URI in the Got provenance
// is the same as the repo URI in the Want reference values.
func (v *ProvenanceIRVerifier) verifyRepoURI() error {
referenceRepoURI := v.Want.GetReferenceProvenance().GetReferenceRepoUri()
referenceRepoURI := v.Want.GetReferenceRepoUri()
if referenceRepoURI == "" {
return nil
}
Expand All @@ -128,7 +108,7 @@ func (v *ProvenanceIRVerifier) verifyRepoURI() error {

// verifyTrustedBuilder verifies that the given trusted builder matches a trusted builder in the reference values.
func (v *ProvenanceIRVerifier) verifyTrustedBuilder() error {
referenceRepoURI := v.Want.GetReferenceProvenance().GetReferenceBuilders()
referenceRepoURI := v.Want.GetReferenceBuilders()
if referenceRepoURI == nil {
return nil
}
Expand Down
Loading

0 comments on commit 91f9f37

Please sign in to comment.