diff --git a/cmd/verifier/main.go b/cmd/verifier/main.go index 5afb42fc..7febd355 100644 --- a/cmd/verifier/main.go +++ b/cmd/verifier/main.go @@ -21,7 +21,8 @@ import ( "os" "github.com/project-oak/transparent-release/internal/model" - "github.com/project-oak/transparent-release/internal/verification" + "github.com/project-oak/transparent-release/internal/verifier" + prover "github.com/project-oak/transparent-release/pkg/proto/verification" ) func main() { @@ -44,9 +45,9 @@ func main() { log.Fatalf("couldn't map from %s to internal representation: %v", validatedProvenance, err) } - provenanceVerifier := verification.ProvenanceIRVerifier{ + provenanceVerifier := verifier.ProvenanceIRVerifier{ Got: provenanceIR, - Want: &verification.ReferenceValues{}, + Want: &prover.ProvenanceReferenceValues{}, } if err := provenanceVerifier.Verify(); err != nil { diff --git a/docs/development-guidelines.md b/docs/development-guidelines.md index 73854884..253a7fd8 100644 --- a/docs/development-guidelines.md +++ b/docs/development-guidelines.md @@ -23,3 +23,27 @@ You need to have: - Format files: `./scripts/formatting.sh` - Check linting: `./scripts/linting.sh` - Additional checks: `go vet ./...` + +## Using protocol buffers + +See instructions for compiling protocol buffers in the +[original guide](https://protobuf.dev/getting-started/gotutorial/#compiling-protocol-buffers). Here +is a summary: + +1. If you haven’t installed the compiler, [download the package](https://protobuf.dev/downloads) and + follow the instructions in the README. + +2. Run the following command to install the Go protocol buffers plugin: + +```bash +go install google.golang.org/protobuf/cmd/protoc-gen-go@latest +``` + +3. Run the compiler to generate the go code. Specify the source directory (where your application’s + source code lives – the current directory is used if you don’t provide a value), the destination + directory (where you want the generated code to go; often the same as $SRC_DIR), and the path to + your .proto: + +```bash +protoc -I=$SRC_DIR --go_out=$DST_DIR $SRC_DIR/provenance_verification.proto +``` diff --git a/golangci-linters.yaml b/golangci-linters.yaml index c92adba8..d7f6021a 100644 --- a/golangci-linters.yaml +++ b/golangci-linters.yaml @@ -65,7 +65,7 @@ linters: - noctx - nolintlint - nonamedreturns - - nosnakecase + # - nosnakecase - nosprintfhostport # Might be nice to enable it later. # - paralleltest @@ -160,7 +160,7 @@ linters: # - noctx # - nolintlint # - nonamedreturns - # - nosnakecase + - nosnakecase # - nosprintfhostport - paralleltest # - prealloc diff --git a/internal/endorser/endorser.go b/internal/endorser/endorser.go index 1fd77034..855ed0c9 100644 --- a/internal/endorser/endorser.go +++ b/internal/endorser/endorser.go @@ -29,9 +29,10 @@ import ( "go.uber.org/multierr" "github.com/project-oak/transparent-release/internal/model" - "github.com/project-oak/transparent-release/internal/verification" + "github.com/project-oak/transparent-release/internal/verifier" "github.com/project-oak/transparent-release/pkg/claims" "github.com/project-oak/transparent-release/pkg/intoto" + prover "github.com/project-oak/transparent-release/pkg/proto/verification" ) // ParsedProvenance contains a provenance in the internal ProvenanceIR format, @@ -43,12 +44,23 @@ 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 *verification.ReferenceValues, 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 +// VerificationOptions to verify them. If one or more provenance statements +// are provided, an endorsement statement is generated only if the +// provenance(s) are valid with respect to the ReferenceProvenance in +// VerificationOptions, if it contains one. If more than one provenance are +// provided, they are in addition checked to be consistent (i.e., that they have +// the same subject). If these conditions hold, the input provenances are +// included as evidence in the generated endorsement. If no provenances are +// provided, a provenance-less endorsement is generated, only if the input +// VerificationOptions has the EndorseProvenanceLess field set. An error is +// returned in all other cases. +func GenerateEndorsement(binaryName, binaryDigest string, verOpt *prover.VerificationOptions, validityDuration claims.ClaimValidity, provenances []ParsedProvenance) (*intoto.Statement, error) { + if (verOpt.GetEndorseProvenanceLess() == nil) && (verOpt.GetReferenceProvenance() == nil) { + return nil, fmt.Errorf("invalid VerificationOptions: exactly one of EndorseProvenanceLess and ReferenceProvenance must be set") + } + verifiedProvenances, err := verifyAndSummarizeProvenances(binaryName, binaryDigest, verOpt, provenances) if err != nil { return nil, fmt.Errorf("could not verify and summarize provenances: %v", err) } @@ -56,13 +68,16 @@ func GenerateEndorsement(referenceValues *verification.ReferenceValues, validity 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 VerificationOptions does not have +// its EndorseProvenanceLess field set. +// (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 *verification.ReferenceValues, provenances []ParsedProvenance) (*claims.VerifiedProvenanceSet, error) { - if len(provenances) == 0 { +// (4) Provenances match but don't match the input binary name or digest. +func verifyAndSummarizeProvenances(binaryName, binaryDigest string, verOpt *prover.VerificationOptions, provenances []ParsedProvenance) (*claims.VerifiedProvenanceSet, error) { + if len(provenances) == 0 && verOpt.GetEndorseProvenanceLess() == nil { return nil, fmt.Errorf("at least one provenance file must be provided") } @@ -73,25 +88,44 @@ func verifyAndSummarizeProvenances(referenceValues *verification.ReferenceValues provenancesData = append(provenancesData, p.SourceMetadata) } - errs := multierr.Append(verifyConsistency(provenanceIRs), verifyProvenances(referenceValues, provenanceIRs)) + var errs error + if len(provenanceIRs) > 0 { + errs = multierr.Append(verifyConsistency(provenanceIRs), verifyProvenances(verOpt.GetReferenceProvenance(), provenanceIRs)) + + 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 *verification.ReferenceValues, provenances []model.ProvenanceIR) error { +// verifyProvenances verifies the given list of provenances against the given +// ProvenanceReferenceValues. An error is returned if verification fails for +// one of them. No verification is performed if the provided +// ProvenanceReferenceValues is nil. +func verifyProvenances(referenceValues *prover.ProvenanceReferenceValues, provenances []model.ProvenanceIR) error { var errs error + if referenceValues == nil { + return nil + } for index := range provenances { - provenanceVerifier := verification.ProvenanceIRVerifier{ + provenanceVerifier := verifier.ProvenanceIRVerifier{ Got: &provenances[index], Want: referenceValues, } diff --git a/internal/endorser/endorser_test.go b/internal/endorser/endorser_test.go index fe92fed9..7956c718 100644 --- a/internal/endorser/endorser_test.go +++ b/internal/endorser/endorser_test.go @@ -22,78 +22,188 @@ import ( "time" "github.com/project-oak/transparent-release/internal/testutil" - "github.com/project-oak/transparent-release/internal/verification" "github.com/project-oak/transparent-release/pkg/claims" + prover "github.com/project-oak/transparent-release/pkg/proto/verification" + "google.golang.org/protobuf/encoding/prototext" ) const ( - binaryHash = "d059c38cea82047ad316a1c6c6fbd13ecf7a0abdcc375463920bd25bf5c142cc" - binaryName = "oak_functions_freestanding_bin" - errorBinaryDigest = "do not contain the actual binary SHA256 digest" + binaryDigestSha256 = "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) { +func createClaimValidity(days int) claims.ClaimValidity { tomorrow := time.Now().AddDate(0, 0, 1) - nextWeek := time.Now().AddDate(0, 0, 7) - validity := claims.ClaimValidity{ + nextWeek := time.Now().AddDate(0, 0, days) + return claims.ClaimValidity{ NotBefore: &tomorrow, NotAfter: &nextWeek, } +} - tempPath, err := copyToTemp("../../testdata/slsa_v02_provenance.json") - if err != nil { - t.Fatalf("Could not load provenance: %v", err) +func createProvenanceList(t *testing.T, paths []string) []ParsedProvenance { + tempURIs := make([]string, 0, len(paths)) + for _, p := range paths { + tempPath, err := copyToTemp(p) + if err != nil { + t.Fatalf("Could not load provenance: %v", err) + } + tempURIs = append(tempURIs, "file://"+tempPath) } - tempURI := "file://" + tempPath - provenances, err := LoadProvenances([]string{tempURI}) + provenances, err := LoadProvenances(tempURIs) if err != nil { t.Fatalf("Could not load provenances: %v", err) } + return provenances +} + +func TestGenerateEndorsement_InvalidVerificationOptions(t *testing.T) { + verOpts := &prover.VerificationOptions{} + _, err := GenerateEndorsement(binaryName, binaryDigestSha256, verOpts, createClaimValidity(7), []ParsedProvenance{}) + if err == nil || !strings.Contains(err.Error(), "invalid VerificationOptions") { + t.Fatalf("got %q, want error message containing %q,", err, "invalid VerificationOptions:") + } +} - referenceValues, err := verification.LoadReferenceValuesFromFile("../../testdata/reference_values.toml") +func TestGenerateEndorsement_NoProvenance_EndorseProvenanceLess(t *testing.T) { + verOpts := &prover.VerificationOptions{ + // Allow provenance-less endorsement generation. + Option: &prover.VerificationOptions_EndorseProvenanceLess{ + EndorseProvenanceLess: &prover.EndorseProvenanceLess{}, + }, + } + statement, err := GenerateEndorsement(binaryName, binaryDigestSha256, verOpts, createClaimValidity(7), []ParsedProvenance{}) if err != nil { - t.Fatalf("Could not load reference values: %v", err) + t.Fatalf("Could not generate provenance-less endorsement: %v", err) } - statement, err := GenerateEndorsement(referenceValues, validity, provenances) + testutil.AssertEq(t, "binary hash", statement.Subject[0].Digest["sha256"], binaryDigestSha256) + testutil.AssertEq(t, "binary name", statement.Subject[0].Name, binaryName) + + // Repeat the same with verification options loaded from file. + verOpts, err = loadTextprotoVerificationOptions("../../testdata/skip_verification.textproto") if err != nil { - t.Fatalf("Could not generate endorsement from %q: %v", provenances[0].SourceMetadata.URI, err) + t.Fatalf("Could not load verification options: %v", err) + } + statement, err = GenerateEndorsement(binaryName, binaryDigestSha256, verOpts, createClaimValidity(7), []ParsedProvenance{}) + if err != nil { + t.Fatalf("Could not generate provenance-less endorsement: %v", err) } - testutil.AssertEq(t, "binary hash", statement.Subject[0].Digest["sha256"], binaryHash) + testutil.AssertEq(t, "binary hash", statement.Subject[0].Digest["sha256"], binaryDigestSha256) + testutil.AssertEq(t, "binary name", statement.Subject[0].Name, binaryName) +} + +func TestGenerateEndorsement_SingleProvenance_EndorseProvenanceLess(t *testing.T) { + verOpts := &prover.VerificationOptions{ + Option: &prover.VerificationOptions_EndorseProvenanceLess{ + EndorseProvenanceLess: &prover.EndorseProvenanceLess{}, + }, + } + provenances := createProvenanceList(t, []string{"../../testdata/slsa_v02_provenance.json"}) + + statement, err := GenerateEndorsement(binaryName, binaryDigestSha256, verOpts, createClaimValidity(7), provenances) + if err != nil { + t.Fatalf("Could not generate provenance-less endorsement: %v", err) + } + + testutil.AssertEq(t, "binary hash", statement.Subject[0].Digest["sha256"], binaryDigestSha256) testutil.AssertEq(t, "binary name", statement.Subject[0].Name, binaryName) predicate := statement.Predicate.(claims.ClaimPredicate) + testutil.AssertEq(t, "evidence length", len(predicate.Evidence), 1) +} + +func TestGenerateEndorsement_SingleInvalidProvenance_EndorseProvenanceLess(t *testing.T) { + verOpts := &prover.VerificationOptions{ + Option: &prover.VerificationOptions_EndorseProvenanceLess{ + EndorseProvenanceLess: &prover.EndorseProvenanceLess{}, + }, + } - testutil.AssertEq(t, "notBefore date", predicate.Validity.NotBefore, &tomorrow) - testutil.AssertEq(t, "notAfter date", predicate.Validity.NotAfter, &nextWeek) + provenances := createProvenanceList(t, []string{"../../testdata/slsa_v02_provenance.json"}) + + _, err := GenerateEndorsement(binaryName+"_diff", binaryDigestSha256, verOpts, createClaimValidity(7), provenances) + if err == nil || !strings.Contains(err.Error(), "does not match the given binary name") { + t.Fatalf("got %q, want error message containing %q,", err, "does not match the given binary name") + } } -func TestLoadAndVerifyProvenances_MultipleValidEndorsement(t *testing.T) { - tempPath1, err := copyToTemp("../../testdata/slsa_v02_provenance.json") +func TestLoadAndVerifyProvenances_MultipleValidProvenances_EndorseProvenanceLess(t *testing.T) { + provenances := createProvenanceList(t, []string{"../../testdata/slsa_v02_provenance.json", "../../testdata/slsa_v02_provenance.json"}) + verOpts := &prover.VerificationOptions{ + Option: &prover.VerificationOptions_EndorseProvenanceLess{ + EndorseProvenanceLess: &prover.EndorseProvenanceLess{}, + }, + } + statement, err := GenerateEndorsement(binaryName, binaryDigestSha256, verOpts, createClaimValidity(7), provenances) if err != nil { - t.Fatalf("Could not load provenance: %v", err) + t.Fatalf("Could not generate provenance-less endorsement: %v", err) + } + + testutil.AssertEq(t, "binary hash", statement.Subject[0].Digest["sha256"], binaryDigestSha256) + testutil.AssertEq(t, "binary name", statement.Subject[0].Name, binaryName) + + predicate := statement.Predicate.(claims.ClaimPredicate) + testutil.AssertEq(t, "evidence length", len(predicate.Evidence), 2) +} + +func TestLoadAndVerify_MultipleInconsistentProvenances_EndorseProvenanceLess(t *testing.T) { + provenances := createProvenanceList(t, []string{"../../testdata/slsa_v02_provenance.json", "../../testdata/different_slsa_v02_provenance.json"}) + + verOpts := &prover.VerificationOptions{ + Option: &prover.VerificationOptions_EndorseProvenanceLess{ + EndorseProvenanceLess: &prover.EndorseProvenanceLess{}, + }, } - tempPath2, err := copyToTemp("../../testdata/slsa_v02_provenance.json") + + // Provenances each contain a (different) given reference binary SHA256 digest value, but are inconsistent. + _, err := GenerateEndorsement(binaryName, binaryDigestSha256, verOpts, createClaimValidity(3), provenances) + if err == nil || !strings.Contains(err.Error(), errorInconsistentProvenances) { + t.Fatalf("got %q, want error message containing %q,", err, errorInconsistentProvenances) + } +} + +func TestGenerateEndorsement_SingleValidProvenance(t *testing.T) { + provenances := createProvenanceList(t, []string{"../../testdata/slsa_v02_provenance.json"}) + validity := createClaimValidity(7) + + verOpt, err := loadTextprotoVerificationOptions("../../testdata/reference_values.textproto") if err != nil { - t.Fatalf("Could not load provenance: %v", err) + t.Fatalf("Could not load verification options: %v", err) } - provenances, err := LoadProvenances([]string{"file://" + tempPath1, "file://" + tempPath2}) + + statement, err := GenerateEndorsement(binaryName, binaryDigestSha256, verOpt, validity, provenances) if err != nil { - t.Fatalf("Could not load provenances: %v", err) + t.Fatalf("Could not generate endorsement from %q: %v", provenances[0].SourceMetadata.URI, err) } - referenceValues := verification.ReferenceValues{ - // Make sure we pick the correct binary hash if there are several reference values. - BinarySHA256Digests: []string{binaryHash + "_diff", binaryHash}, + testutil.AssertEq(t, "binary hash", statement.Subject[0].Digest["sha256"], binaryDigestSha256) + testutil.AssertEq(t, "binary name", statement.Subject[0].Name, binaryName) + + predicate := statement.Predicate.(claims.ClaimPredicate) + + testutil.AssertEq(t, "notBefore date", predicate.Validity.NotBefore, validity.NotBefore) + testutil.AssertEq(t, "notAfter date", predicate.Validity.NotAfter, validity.NotAfter) +} + +func TestLoadAndVerifyProvenances_MultipleValidProvenances(t *testing.T) { + provenances := createProvenanceList(t, []string{"../../testdata/slsa_v02_provenance.json", "../../testdata/slsa_v02_provenance.json"}) + + verOpts := &prover.VerificationOptions{ + Option: &prover.VerificationOptions_ReferenceProvenance{ + ReferenceProvenance: &prover.ProvenanceReferenceValues{}, + }, } - provenanceSet, err := verifyAndSummarizeProvenances(&referenceValues, provenances) + provenanceSet, err := verifyAndSummarizeProvenances(binaryName, binaryDigestSha256, verOpts, provenances) if err != nil { t.Fatalf("Could not generate endorsement from %q: %v", provenances[0].SourceMetadata.URI, err) } testutil.AssertEq(t, "binary name", provenanceSet.BinaryName, binaryName) - testutil.AssertEq(t, "binary hash", provenanceSet.BinaryDigest, binaryHash) + testutil.AssertEq(t, "binary hash", provenanceSet.BinaryDigest, binaryDigestSha256) } func TestLoadProvenances_FailingSingleRemoteProvenanceEndorsement(t *testing.T) { @@ -105,110 +215,70 @@ func TestLoadProvenances_FailingSingleRemoteProvenanceEndorsement(t *testing.T) } func TestLoadAndVerifyProvenances_ConsistentNotVerified(t *testing.T) { - tempPath1, err := copyToTemp("../../testdata/slsa_v02_provenance.json") - if err != nil { - t.Fatalf("Could not load provenance: %v", err) - } + provenances := createProvenanceList(t, []string{"../../testdata/slsa_v02_provenance.json", "../../testdata/slsa_v02_provenance.json"}) - provenances, err := LoadProvenances([]string{"file://" + tempPath1, "file://" + tempPath1}) - if err != nil { - t.Fatalf("Could not load provenances: %v", err) - } - referenceValues := verification.ReferenceValues{ - BinarySHA256Digests: []string{binaryHash + "_diff"}, + verOpts := &prover.VerificationOptions{ + Option: &prover.VerificationOptions_ReferenceProvenance{ + ReferenceProvenance: &prover.ProvenanceReferenceValues{}, + }, } // Provenances do not contain the given reference binary SHA256 digest value, but are consistent. - _, err = verifyAndSummarizeProvenances(&referenceValues, provenances) + _, err := verifyAndSummarizeProvenances(binaryName, binaryDigestSha256+"_diff", verOpts, provenances) if err == nil || !strings.Contains(err.Error(), errorBinaryDigest) { t.Fatalf("got %q, want error message containing %q,", err, errorBinaryDigest) } } func TestLoadAndVerify_InconsistentVerified(t *testing.T) { - tempPath1, err := copyToTemp("../../testdata/slsa_v02_provenance.json") - if err != nil { - t.Fatalf("Could not load provenance: %v", err) - } + provenances := createProvenanceList(t, []string{"../../testdata/slsa_v02_provenance.json", "../../testdata/different_slsa_v02_provenance.json"}) - tempPath2, err := copyToTemp("../../testdata/different_slsa_v02_provenance.json") - if err != nil { - t.Fatalf("Could not load provenance: %v", err) - } - - provenances, err := LoadProvenances([]string{"file://" + tempPath1, "file://" + tempPath2}) - if err != nil { - t.Fatalf("Could not load provenances: %v", err) - } - referenceValues := verification.ReferenceValues{ - BinarySHA256Digests: []string{"e8e05d1d09af8952919bf6ab38e0cc5a6414ee2b5e21f4765b12421c5db0037e", binaryHash}, + verOpt := prover.VerificationOptions{ + Option: &prover.VerificationOptions_ReferenceProvenance{ + 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, binaryDigestSha256, &verOpt, provenances) + if err == nil || !strings.Contains(err.Error(), errorInconsistentProvenances) { + t.Fatalf("got %q, want error message containing %q,", err, errorInconsistentProvenances) } } func TestLoadAndVerify_InconsistentNotVerified(t *testing.T) { - tempPath1, err := copyToTemp("../../testdata/slsa_v02_provenance.json") - if err != nil { - t.Fatalf("Could not load provenance: %v", err) - } + provenances := createProvenanceList(t, []string{"../../testdata/slsa_v02_provenance.json", "../../testdata/different_slsa_v02_provenance.json"}) - tempPath2, err := copyToTemp("../../testdata/different_slsa_v02_provenance.json") - if err != nil { - t.Fatalf("Could not load provenance: %v", err) - } - - provenances, err := LoadProvenances([]string{"file://" + tempPath1, "file://" + tempPath2}) - if err != nil { - t.Fatalf("Could not load provenances: %v", err) - } - referenceValues := verification.ReferenceValues{ - BinarySHA256Digests: []string{binaryHash + "_diff"}, + verOpt := &prover.VerificationOptions{ + Option: &prover.VerificationOptions_ReferenceProvenance{ + ReferenceProvenance: &prover.ProvenanceReferenceValues{}, + }, } - _, err = verifyAndSummarizeProvenances(&referenceValues, provenances) + _, err := verifyAndSummarizeProvenances(binaryName, binaryDigestSha256+"_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) } } func TestLoadAndVerifyProvenances_NotVerified(t *testing.T) { - tempPath1, err := copyToTemp("../../testdata/slsa_v02_provenance.json") - if err != nil { - t.Fatalf("Could not load provenance: %v", err) - } + provenances := createProvenanceList(t, []string{"../../testdata/slsa_v02_provenance.json"}) - provenances, err := LoadProvenances([]string{"file://" + tempPath1}) - if err != nil { - t.Fatalf("Could not load provenances: %v", err) - } - referenceValues, err := verification.LoadReferenceValuesFromFile("../../testdata/different_reference_values.toml") + verOpts, err := loadTextprotoVerificationOptions("../../testdata/different_reference_values.textproto") if err != nil { - t.Fatalf("Could not load reference values: %v", err) + t.Fatalf("Could not load verification options: %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) } @@ -234,3 +304,15 @@ func copyToTemp(path string) (string, error) { return tmpfile.Name(), nil } + +func loadTextprotoVerificationOptions(path string) (*prover.VerificationOptions, error) { + bytes, err := os.ReadFile(path) + if err != nil { + return nil, fmt.Errorf("reading provenance verification options from %q: %v", path, err) + } + var opt prover.VerificationOptions + if err := prototext.Unmarshal(bytes, &opt); err != nil { + return nil, fmt.Errorf("unmarshal bytes to VerificationOptions: %v", err) + } + return &opt, nil +} diff --git a/internal/verification/reference.go b/internal/verification/reference.go deleted file mode 100644 index adef45ea..00000000 --- a/internal/verification/reference.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2023 The Project Oak Authors -// -// 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 verification - -import ( - "fmt" - - "github.com/pelletier/go-toml" -) - -// ReferenceValues specify expected values to verify provenances against. -type ReferenceValues struct { - // Allow list of binary digests. - BinarySHA256Digests []string `toml:"binary_sha256_digests"` - // If true, expect that the provenance has a non-empty build command. - WantBuildCmds bool `toml:"want_build_cmds"` - // Allow list of builder image digests that are trusted for building the binary. - BuilderImageSHA256Digests []string `toml:"builder_image_sha256_digests"` - // The URI of the repo holding the resources the binary is built from. - RepoURI string `toml:"repo_uri"` - // Allow list of builders trusted to build the binary. - TrustedBuilders []string `toml:"trusted_builders"` -} - -// LoadReferenceValuesFromFile loads reference values from a toml file in the given path and returns an instance of ReferenceValues. -func LoadReferenceValuesFromFile(path string) (*ReferenceValues, error) { - tomlTree, err := toml.LoadFile(path) - if err != nil { - return nil, fmt.Errorf("couldn't load toml file: %v", err) - } - - referenceValues := ReferenceValues{} - if err := tomlTree.Unmarshal(&referenceValues); err != nil { - return nil, fmt.Errorf("couldn't unmarshal toml file: %v", err) - } - - return &referenceValues, nil -} diff --git a/internal/verification/reference_test.go b/internal/verification/reference_test.go deleted file mode 100644 index 8fa0b852..00000000 --- a/internal/verification/reference_test.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2023 The Project Oak Authors -// -// 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 verification - -import ( - "path/filepath" - "testing" - - "github.com/project-oak/transparent-release/internal/testutil" -) - -const ( - testdataPath = "../../testdata/" -) - -func TestParseReferenceValues(t *testing.T) { - path := filepath.Join(testdataPath, "reference_values.toml") - referenceValues, err := LoadReferenceValuesFromFile(path) - if err != nil { - t.Fatalf("couldn't load reference values file: %v", err) - } - - testutil.AssertEq(t, "binary digests[0]", referenceValues.BinarySHA256Digests[0], "d059c38cea82047ad316a1c6c6fbd13ecf7a0abdcc375463920bd25bf5c142cc") - testutil.AssertEq(t, "want build cmd", referenceValues.WantBuildCmds, false) - testutil.AssertEq(t, "builder image digests[0]", referenceValues.BuilderImageSHA256Digests[0], "9e2ba52487d945504d250de186cb4fe2e3ba023ed2921dd6ac8b97ed43e76af9") -} diff --git a/internal/verification/verifier.go b/internal/verifier/verifier.go similarity index 66% rename from internal/verification/verifier.go rename to internal/verifier/verifier.go index 2e59258d..bd986c86 100644 --- a/internal/verification/verifier.go +++ b/internal/verifier/verifier.go @@ -13,12 +13,13 @@ // limitations under the License. // Package verification provides a function for verifying a SLSA provenance file. -package verification +package verifier import ( "fmt" "github.com/project-oak/transparent-release/internal/model" + prover "github.com/project-oak/transparent-release/pkg/proto/verification" "go.uber.org/multierr" ) @@ -26,18 +27,13 @@ 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 *ReferenceValues + 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 { 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()) @@ -57,29 +53,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 { - if v.Want.BinarySHA256Digests == nil { - return nil - } - - gotBinarySHA256Digest := v.Got.BinarySHA256Digest() - - for _, wantBinarySHA256Digest := range v.Want.BinarySHA256Digests { - if wantBinarySHA256Digest == gotBinarySHA256Digest { - return nil - } - } - return fmt.Errorf("the reference binary SHA256 digests (%v) do not contain the actual binary SHA256 digest (%v)", - v.Want.BinarySHA256Digests, - gotBinarySHA256Digest) -} - // 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 { - if v.Got.HasBuildCmd() && v.Want.WantBuildCmds { + if v.Want.GetMustHaveBuildCommand() && v.Got.HasBuildCmd() { if buildCmd, err := v.Got.BuildCmd(); err != nil || len(buildCmd) == 0 { return fmt.Errorf("no build cmd found") } @@ -90,7 +67,10 @@ func (v *ProvenanceIRVerifier) verifyBuildCmd() error { // verifyBuilderImageDigest verifies that the builder image digest in the Got // provenance matches a builder image digest in the Want reference values. func (v *ProvenanceIRVerifier) verifyBuilderImageDigest() error { - if !v.Got.HasBuilderImageSHA256Digest() || v.Want.BuilderImageSHA256Digests == nil { + referenceDigests := v.Want.GetReferenceBuilderImageDigests() + if !v.Got.HasBuilderImageSHA256Digest() || referenceDigests == nil { + // A valid provenance that is missing a builder image digest passes the + // verification. return nil } @@ -99,50 +79,71 @@ func (v *ProvenanceIRVerifier) verifyBuilderImageDigest() error { return fmt.Errorf("no builder image digest set") } - for _, wantBuilderImageSHA256Digest := range v.Want.BuilderImageSHA256Digests { - if wantBuilderImageSHA256Digest == gotBuilderImageDigest { - return nil - } + if err := verifySHA256Digest(gotBuilderImageDigest, referenceDigests); err != nil { + return fmt.Errorf("verifying builder image SHA256 digest: %v", err) } - - return fmt.Errorf("the reference builder image digests (%v) do not contain the actual builder image digest (%v)", - v.Want.BuilderImageSHA256Digests, - gotBuilderImageDigest) + return nil } // 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 { - var errs error - - if !v.Got.HasRepoURI() || v.Want.RepoURI == "" { + referenceRepoURI := v.Want.GetReferenceRepoUri() + if referenceRepoURI == "" { return nil } - if v.Got.RepoURI() != v.Want.RepoURI { - multierr.AppendInto(&errs, fmt.Errorf("the URI from the provenance (%v) is different from the repo URI (%v)", v.Got.RepoURI(), v.Want.RepoURI)) + if !v.Got.HasRepoURI() { + return fmt.Errorf("no repo URI in the provenance, but want (%v)", referenceRepoURI) } - return errs + if v.Got.RepoURI() != referenceRepoURI { + return fmt.Errorf("the repo URI from the provenance (%v) is different from the repo URI (%v)", v.Got.RepoURI(), referenceRepoURI) + } + + return nil } // verifyTrustedBuilder verifies that the given trusted builder matches a trusted builder in the reference values. func (v *ProvenanceIRVerifier) verifyTrustedBuilder() error { - if !v.Got.HasTrustedBuilder() || v.Want.TrustedBuilders == nil { + referenceBuilders := v.Want.GetReferenceBuilders() + if referenceBuilders == nil { return nil } + gotTrustedBuilder, err := v.Got.TrustedBuilder() if err != nil { return fmt.Errorf("no trusted builder set") } - for _, wantTrustedBuilder := range v.Want.TrustedBuilders { + for _, wantTrustedBuilder := range referenceBuilders.GetValues() { if wantTrustedBuilder == gotTrustedBuilder { return nil } } return fmt.Errorf("the reference trusted builders (%v) do not contain the actual trusted builder (%v)", - v.Want.TrustedBuilders, + referenceBuilders.GetValues(), gotTrustedBuilder) } + +// verifySHA256Digest verifies that a given SHA256 is among the given digests. +func verifySHA256Digest(got string, want *prover.Digests) error { + if want == nil { + return nil + } + + sha256Digests, ok := want.GetDigests()["sha256"] + if !ok { + return nil + } + + for _, wantBinarySHA256Digest := range sha256Digests.GetValues() { + if wantBinarySHA256Digest == got { + return nil + } + } + return fmt.Errorf("the reference SHA256 digests (%v) do not contain the actual SHA256 digest (%v)", + sha256Digests.GetValues(), + got) +} diff --git a/internal/verification/verifier_test.go b/internal/verifier/verifier_test.go similarity index 74% rename from internal/verification/verifier_test.go rename to internal/verifier/verifier_test.go index ae3a0b3e..6ecb7f14 100644 --- a/internal/verification/verifier_test.go +++ b/internal/verifier/verifier_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package verification +package verifier import ( "fmt" @@ -21,6 +21,7 @@ import ( "github.com/project-oak/transparent-release/internal/model" slsav02 "github.com/project-oak/transparent-release/pkg/intoto/slsa_provenance/v0.2" + prover "github.com/project-oak/transparent-release/pkg/proto/verification" ) const ( @@ -32,21 +33,12 @@ func TestVerify_HasNoValues(t *testing.T) { // There are no optional fields set apart from the binary digest and the build type. got := model.NewProvenanceIR(binarySHA256Digest, slsav02.GenericSLSABuildType, binaryName) - want := ReferenceValues{ - // We ask for all the optional values in the reference values. - WantBuildCmds: true, - BuilderImageSHA256Digests: []string{"builder_image_digest"}, - RepoURI: "some_repo_uri", - TrustedBuilders: []string{"some_trusted_builder"}, - } - verifier := ProvenanceIRVerifier{ Got: got, - Want: &want, + Want: &prover.ProvenanceReferenceValues{}, } // We don't expect any verification to happen. - if err := verifier.Verify(); err != nil { t.Fatalf("verify failed, got %v", err) } @@ -55,8 +47,8 @@ func TestVerify_HasNoValues(t *testing.T) { func TestVerify_NeedsCanHaveHasBuildCmd(t *testing.T) { got := model.NewProvenanceIR(binarySHA256Digest, slsav02.GenericSLSABuildType, binaryName, model.WithBuildCmd([]string{"build cmd"})) - want := ReferenceValues{ - WantBuildCmds: true, + want := prover.ProvenanceReferenceValues{ + MustHaveBuildCommand: true, } verifier := ProvenanceIRVerifier{ @@ -73,8 +65,8 @@ func TestVerify_NeedsCannotHaveDoesNotHaveBuildCmd(t *testing.T) { // No buildCmd is set in the provenance. got := model.NewProvenanceIR(binarySHA256Digest, slsav02.GenericSLSABuildType, binaryName) - want := ReferenceValues{ - WantBuildCmds: true, + want := prover.ProvenanceReferenceValues{ + MustHaveBuildCommand: true, } verifier := ProvenanceIRVerifier{ @@ -91,8 +83,8 @@ func TestVerify_NeedsCannotHaveHasEmptyBuildCmd(t *testing.T) { // The build command is empty. got := model.NewProvenanceIR(binarySHA256Digest, slsav02.GenericSLSABuildType, binaryName, model.WithBuildCmd([]string{})) // And the reference values ask for a build cmd. - want := ReferenceValues{ - WantBuildCmds: true, + want := prover.ProvenanceReferenceValues{ + MustHaveBuildCommand: true, } verifier := ProvenanceIRVerifier{ @@ -110,8 +102,8 @@ func TestVerify_DoesNotNeedCannotHaveHasEmptyBuildCmd(t *testing.T) { // The build command is empty. got := model.NewProvenanceIR(binarySHA256Digest, slsav02.GenericSLSABuildType, binaryName, model.WithBuildCmd([]string{})) // But the reference values do not ask for a build cmd. - want := ReferenceValues{ - WantBuildCmds: false, + want := prover.ProvenanceReferenceValues{ + MustHaveBuildCommand: false, } verifier := ProvenanceIRVerifier{ @@ -128,8 +120,10 @@ func TestVerify_DoesNotNeedCannotHaveHasEmptyBuildCmd(t *testing.T) { func TestVerify_NeedsHasBuilderImageDigest(t *testing.T) { builderDigest := "9e2ba52487d945504d250de186cb4fe2e3ba023ed2921dd6ac8b97ed43e76af9" got := model.NewProvenanceIR(binarySHA256Digest, slsav02.GenericSLSABuildType, binaryName, model.WithBuilderImageSHA256Digest(builderDigest)) - want := ReferenceValues{ - BuilderImageSHA256Digests: []string{"some_other_digest", builderDigest}, + want := prover.ProvenanceReferenceValues{ + ReferenceBuilderImageDigests: &prover.Digests{ + Digests: map[string]*prover.StringAllowList{"sha256": {Values: []string{"some_other_digest", builderDigest}}}, + }, } verifier := ProvenanceIRVerifier{ @@ -145,8 +139,10 @@ func TestVerify_NeedsHasBuilderImageDigest(t *testing.T) { func TestVerify_NeedsDoesNotHaveBuilderImageDigest(t *testing.T) { builderDigest := "9e2ba52487d945504d250de186cb4fe2e3ba023ed2921dd6ac8b97ed43e76af9" got := model.NewProvenanceIR(binarySHA256Digest, slsav02.GenericSLSABuildType, binaryName, model.WithBuilderImageSHA256Digest(builderDigest)) - want := ReferenceValues{ - BuilderImageSHA256Digests: []string{"some_other_digest", "and_some_other"}, + want := prover.ProvenanceReferenceValues{ + ReferenceBuilderImageDigests: &prover.Digests{ + Digests: map[string]*prover.StringAllowList{"sha256": {Values: []string{"some_other_digest", "and_some_other"}}}, + }, } verifier := ProvenanceIRVerifier{ @@ -154,8 +150,8 @@ func TestVerify_NeedsDoesNotHaveBuilderImageDigest(t *testing.T) { Want: &want, } - wantErr := fmt.Sprintf("the reference builder image digests (%v) do not contain the actual builder image digest (%v)", - want.BuilderImageSHA256Digests, + wantErr := fmt.Sprintf("the reference SHA256 digests (%v) do not contain the actual SHA256 digest (%v)", + want.GetReferenceBuilderImageDigests().GetDigests()["sha256"].GetValues(), builderDigest) if err := verifier.Verify(); err == nil || !strings.Contains(err.Error(), wantErr) { t.Fatalf("got %q, want error message containing %q,", err, wantErr) @@ -164,8 +160,10 @@ func TestVerify_NeedsDoesNotHaveBuilderImageDigest(t *testing.T) { func TestVerify_NeedsHasEmptyBuilderImageDigest(t *testing.T) { got := model.NewProvenanceIR(binarySHA256Digest, slsav02.GenericSLSABuildType, binaryName, model.WithBuilderImageSHA256Digest("")) - want := ReferenceValues{ - BuilderImageSHA256Digests: []string{"some_digest"}, + want := prover.ProvenanceReferenceValues{ + ReferenceBuilderImageDigests: &prover.Digests{ + Digests: map[string]*prover.StringAllowList{"sha256": {Values: []string{"some_digest"}}}, + }, } verifier := ProvenanceIRVerifier{ @@ -173,8 +171,8 @@ func TestVerify_NeedsHasEmptyBuilderImageDigest(t *testing.T) { Want: &want, } - wantErr := fmt.Sprintf("the reference builder image digests (%v) do not contain the actual builder image digest (%v)", - want.BuilderImageSHA256Digests, + wantErr := fmt.Sprintf("the reference SHA256 digests (%v) do not contain the actual SHA256 digest (%v)", + want.GetReferenceBuilderImageDigests().GetDigests()["sha256"].GetValues(), "") if err := verifier.Verify(); err == nil || !strings.Contains(err.Error(), wantErr) { t.Fatalf("got %q, want error message containing %q,", err, wantErr) @@ -184,13 +182,11 @@ func TestVerify_NeedsHasEmptyBuilderImageDigest(t *testing.T) { func TestVerify_DoesNotNeedHasEmptyBuilderImageDigest(t *testing.T) { builderImageSHA256Digest := "" got := model.NewProvenanceIR(binarySHA256Digest, slsav02.GenericSLSABuildType, binaryName, model.WithBuilderImageSHA256Digest(builderImageSHA256Digest)) - want := ReferenceValues{ - // We do not check for the builder image digest. - } verifier := ProvenanceIRVerifier{ - Got: got, - Want: &want, + Got: got, + // We do not check for the builder image digest. + Want: &prover.ProvenanceReferenceValues{}, } if err := verifier.Verify(); err != nil { @@ -201,8 +197,8 @@ func TestVerify_DoesNotNeedHasEmptyBuilderImageDigest(t *testing.T) { func TestVerify_HasWantedRepoURI(t *testing.T) { got := model.NewProvenanceIR(binarySHA256Digest, slsav02.GenericSLSABuildType, binaryName, model.WithRepoURI("https://github.com/project-oak/transparent-release")) - want := ReferenceValues{ - RepoURI: "https://github.com/project-oak/transparent-release", + want := prover.ProvenanceReferenceValues{ + ReferenceRepoUri: "https://github.com/project-oak/transparent-release", } verifier := ProvenanceIRVerifier{ @@ -221,8 +217,8 @@ func TestVerify_HasWrongRepoURI(t *testing.T) { got := model.NewProvenanceIR(binarySHA256Digest, slsav02.GenericSLSABuildType, binaryName, model.WithRepoURI(wrongURI)) - want := ReferenceValues{ - RepoURI: "github.com/project-oak/transparent-release", + want := prover.ProvenanceReferenceValues{ + ReferenceRepoUri: "github.com/project-oak/transparent-release", } verifier := ProvenanceIRVerifier{ @@ -230,9 +226,9 @@ func TestVerify_HasWrongRepoURI(t *testing.T) { Want: &want, } - wantErr := fmt.Sprintf("the URI from the provenance (%v) is different from the repo URI (%v)", + wantErr := fmt.Sprintf("the repo URI from the provenance (%v) is different from the repo URI (%v)", wrongURI, - want.RepoURI, + want.GetReferenceRepoUri(), ) if err := verifier.Verify(); err == nil || !strings.Contains(err.Error(), wantErr) { t.Fatalf("got %q, want error message containing %q,", err, wantErr) @@ -242,8 +238,8 @@ func TestVerify_HasWrongRepoURI(t *testing.T) { func TestVerify_HasNoRepoURIs(t *testing.T) { // We have no repo URIs in the provenance. got := model.NewProvenanceIR(binarySHA256Digest, slsav02.GenericSLSABuildType, binaryName) - want := ReferenceValues{ - RepoURI: "github.com/project-oak/transparent-release", + want := prover.ProvenanceReferenceValues{ + ReferenceRepoUri: "github.com/project-oak/transparent-release", } verifier := ProvenanceIRVerifier{ @@ -251,9 +247,11 @@ func TestVerify_HasNoRepoURIs(t *testing.T) { Want: &want, } - // verfy succeeds because there are no references to any repo URI to match against - if err := verifier.Verify(); err != nil { - t.Fatalf("verify failed, got %v", err) + wantErr := fmt.Sprintf("no repo URI in the provenance, but want (%v)", + want.GetReferenceRepoUri(), + ) + if err := verifier.Verify(); err == nil || !strings.Contains(err.Error(), wantErr) { + t.Fatalf("got %q, want error message containing %q,", err, wantErr) } } @@ -261,8 +259,10 @@ func TestVerify_NeedsHasTrustedBuilder(t *testing.T) { trustedBuilder := "https://github.com/slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@refs/tags/v1.2.0" got := model.NewProvenanceIR(binarySHA256Digest, slsav02.GenericSLSABuildType, binaryName, model.WithTrustedBuilder(trustedBuilder)) - want := ReferenceValues{ - TrustedBuilders: []string{trustedBuilder}, + want := prover.ProvenanceReferenceValues{ + ReferenceBuilders: &prover.StringAllowList{ + Values: []string{trustedBuilder}, + }, } verifier := ProvenanceIRVerifier{ @@ -279,8 +279,10 @@ func TestVerify_NeedsDoesNotHaveTrustedBuilder(t *testing.T) { trustedBuilder := "https://github.com/slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@refs/tags/v1.2.0" got := model.NewProvenanceIR(binarySHA256Digest, slsav02.GenericSLSABuildType, binaryName, model.WithTrustedBuilder(trustedBuilder)) - want := ReferenceValues{ - TrustedBuilders: []string{"other_" + trustedBuilder, "another_" + trustedBuilder}, + want := prover.ProvenanceReferenceValues{ + ReferenceBuilders: &prover.StringAllowList{ + Values: []string{"other_" + trustedBuilder, "another_" + trustedBuilder}, + }, } verifier := ProvenanceIRVerifier{ @@ -289,7 +291,7 @@ func TestVerify_NeedsDoesNotHaveTrustedBuilder(t *testing.T) { } wantErr := fmt.Sprintf("the reference trusted builders (%v) do not contain the actual trusted builder (%v)", - want.TrustedBuilders, + want.GetReferenceBuilders().GetValues(), trustedBuilder) if err := verifier.Verify(); err == nil || !strings.Contains(err.Error(), wantErr) { t.Fatalf("got %q, want error message containing %q,", err, wantErr) @@ -299,8 +301,10 @@ func TestVerify_NeedsDoesNotHaveTrustedBuilder(t *testing.T) { func TestVerify_NeedsHasEmptyTrustedBuilder(t *testing.T) { got := model.NewProvenanceIR(binarySHA256Digest, slsav02.GenericSLSABuildType, binaryName, model.WithTrustedBuilder("")) - want := ReferenceValues{ - TrustedBuilders: []string{"other_trusted_builder", "another_trusted_builder"}, + want := prover.ProvenanceReferenceValues{ + ReferenceBuilders: &prover.StringAllowList{ + Values: []string{"other_trusted_builder", "another_trusted_builder"}, + }, } verifier := ProvenanceIRVerifier{ @@ -309,7 +313,7 @@ func TestVerify_NeedsHasEmptyTrustedBuilder(t *testing.T) { } wantErr := fmt.Sprintf("the reference trusted builders (%v) do not contain the actual trusted builder (%v)", - want.TrustedBuilders, + want.GetReferenceBuilders().GetValues(), "") if err := verifier.Verify(); err == nil || !strings.Contains(err.Error(), wantErr) { t.Fatalf("got %q, want error message containing %q,", err, wantErr) @@ -319,13 +323,10 @@ func TestVerify_NeedsHasEmptyTrustedBuilder(t *testing.T) { func TestVerify_DoesNotNeedHasEmptyTrustedBuilder(t *testing.T) { got := model.NewProvenanceIR(binarySHA256Digest, slsav02.GenericSLSABuildType, binaryName, model.WithTrustedBuilder("")) - want := ReferenceValues{ - // We do not check the trusted builder. - } - verifier := ProvenanceIRVerifier{ - Got: got, - Want: &want, + Got: got, + // We do not check the trusted builder. + Want: &prover.ProvenanceReferenceValues{}, } if err := verifier.Verify(); err != nil { diff --git a/pkg/claims/endorsement.go b/pkg/claims/endorsement.go index ffc24f2c..4f482bdc 100644 --- a/pkg/claims/endorsement.go +++ b/pkg/claims/endorsement.go @@ -33,7 +33,7 @@ type VerifiedProvenanceSet struct { BinaryName string // SHA256 digest of the binary that all validated provenances agree on. BinaryDigest string - // Provenances contains metadata about provenances + // Provenances is a possibly empty list of provenance metadata objects. Provenances []ProvenanceData } diff --git a/pkg/claims/endorsement_test.go b/pkg/claims/endorsement_test.go index ce9fcc76..d9ff357a 100644 --- a/pkg/claims/endorsement_test.go +++ b/pkg/claims/endorsement_test.go @@ -68,6 +68,25 @@ func TestNotAfterBeforeNotBeforeEndorsement(t *testing.T) { } } +func TestGenerateProvenanceLessEndorsement(t *testing.T) { + newNotBefore := time.Now().AddDate(0, 0, 1) + newNotAfter := time.Now().AddDate(0, 0, 3) + + validity := ClaimValidity{ + NotBefore: &newNotBefore, + NotAfter: &newNotAfter, + } + + provenances := VerifiedProvenanceSet{ + BinaryName: "SomeBinary", + BinaryDigest: "813841dda3818d616aa3e706e49d0286dc825c5dbad4a75cfb37b91ba412238b", + } + endorsement := GenerateEndorsementStatement(validity, provenances) + if err := validateClaim(*endorsement); err != nil { + t.Fatalf("Invalid endorsement: %v", err) + } +} + // Helper function for creating new test cases from the hard-coded one. func tweakValidity(t *testing.T, daysAddedToNotBefore, daysAddedToNotAfter int) []byte { examplePath := "../../schema/claim/v1/example.json" diff --git a/pkg/proto/verification/provenance_verification.pb.go b/pkg/proto/verification/provenance_verification.pb.go new file mode 100644 index 00000000..6c5df13d --- /dev/null +++ b/pkg/proto/verification/provenance_verification.pb.go @@ -0,0 +1,520 @@ +// Copyright 2023 The Project Oak Authors +// +// 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. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.31.0 +// protoc v3.21.12 +// source: proto/provenance_verification.proto + +package verification + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// Options for verifying provenances when generating an endorsement statement. +type VerificationOptions struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to Option: + // + // *VerificationOptions_EndorseProvenanceLess + // *VerificationOptions_ReferenceProvenance + Option isVerificationOptions_Option `protobuf_oneof:"option"` +} + +func (x *VerificationOptions) Reset() { + *x = VerificationOptions{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_provenance_verification_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *VerificationOptions) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*VerificationOptions) ProtoMessage() {} + +func (x *VerificationOptions) ProtoReflect() protoreflect.Message { + mi := &file_proto_provenance_verification_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use VerificationOptions.ProtoReflect.Descriptor instead. +func (*VerificationOptions) Descriptor() ([]byte, []int) { + return file_proto_provenance_verification_proto_rawDescGZIP(), []int{0} +} + +func (m *VerificationOptions) GetOption() isVerificationOptions_Option { + if m != nil { + return m.Option + } + return nil +} + +func (x *VerificationOptions) GetEndorseProvenanceLess() *EndorseProvenanceLess { + if x, ok := x.GetOption().(*VerificationOptions_EndorseProvenanceLess); ok { + return x.EndorseProvenanceLess + } + return nil +} + +func (x *VerificationOptions) GetReferenceProvenance() *ProvenanceReferenceValues { + if x, ok := x.GetOption().(*VerificationOptions_ReferenceProvenance); ok { + return x.ReferenceProvenance + } + return nil +} + +type isVerificationOptions_Option interface { + isVerificationOptions_Option() +} + +type VerificationOptions_EndorseProvenanceLess struct { + // Allows generating endorsements without any provenances as evidence. + // If one or more provenances are provided, the presence of this value + // is the same as having an empty reference provenance. + EndorseProvenanceLess *EndorseProvenanceLess `protobuf:"bytes,1,opt,name=endorse_provenance_less,json=endorseProvenanceLess,proto3,oneof"` +} + +type VerificationOptions_ReferenceProvenance struct { + ReferenceProvenance *ProvenanceReferenceValues `protobuf:"bytes,2,opt,name=reference_provenance,json=referenceProvenance,proto3,oneof"` +} + +func (*VerificationOptions_EndorseProvenanceLess) isVerificationOptions_Option() {} + +func (*VerificationOptions_ReferenceProvenance) isVerificationOptions_Option() {} + +// A singleton value to allow provenance-less endorsement generation. +type EndorseProvenanceLess struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *EndorseProvenanceLess) Reset() { + *x = EndorseProvenanceLess{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_provenance_verification_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *EndorseProvenanceLess) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EndorseProvenanceLess) ProtoMessage() {} + +func (x *EndorseProvenanceLess) ProtoReflect() protoreflect.Message { + mi := &file_proto_provenance_verification_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use EndorseProvenanceLess.ProtoReflect.Descriptor instead. +func (*EndorseProvenanceLess) Descriptor() ([]byte, []int) { + return file_proto_provenance_verification_proto_rawDescGZIP(), []int{1} +} + +// A collection of reference values for verifying provenance statements. +type ProvenanceReferenceValues struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + MustHaveBuildCommand bool `protobuf:"varint,1,opt,name=must_have_build_command,json=mustHaveBuildCommand,proto3" json:"must_have_build_command,omitempty"` + ReferenceBuilderImageDigests *Digests `protobuf:"bytes,3,opt,name=reference_builder_image_digests,json=referenceBuilderImageDigests,proto3" json:"reference_builder_image_digests,omitempty"` + ReferenceRepoUri string `protobuf:"bytes,4,opt,name=reference_repo_uri,json=referenceRepoUri,proto3" json:"reference_repo_uri,omitempty"` + ReferenceBuilders *StringAllowList `protobuf:"bytes,5,opt,name=reference_builders,json=referenceBuilders,proto3" json:"reference_builders,omitempty"` +} + +func (x *ProvenanceReferenceValues) Reset() { + *x = ProvenanceReferenceValues{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_provenance_verification_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ProvenanceReferenceValues) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProvenanceReferenceValues) ProtoMessage() {} + +func (x *ProvenanceReferenceValues) ProtoReflect() protoreflect.Message { + mi := &file_proto_provenance_verification_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProvenanceReferenceValues.ProtoReflect.Descriptor instead. +func (*ProvenanceReferenceValues) Descriptor() ([]byte, []int) { + return file_proto_provenance_verification_proto_rawDescGZIP(), []int{2} +} + +func (x *ProvenanceReferenceValues) GetMustHaveBuildCommand() bool { + if x != nil { + return x.MustHaveBuildCommand + } + return false +} + +func (x *ProvenanceReferenceValues) GetReferenceBuilderImageDigests() *Digests { + if x != nil { + return x.ReferenceBuilderImageDigests + } + return nil +} + +func (x *ProvenanceReferenceValues) GetReferenceRepoUri() string { + if x != nil { + return x.ReferenceRepoUri + } + return "" +} + +func (x *ProvenanceReferenceValues) GetReferenceBuilders() *StringAllowList { + if x != nil { + return x.ReferenceBuilders + } + return nil +} + +// An allow list of digests, represented as a mapping from cryptographic hash +// function names, to their allow listed values. +type Digests struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Keys are cryptographic hash algorithms (e.g., sha256). + // Values are acceptable digests. + Digests map[string]*StringAllowList `protobuf:"bytes,1,rep,name=digests,proto3" json:"digests,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *Digests) Reset() { + *x = Digests{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_provenance_verification_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Digests) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Digests) ProtoMessage() {} + +func (x *Digests) ProtoReflect() protoreflect.Message { + mi := &file_proto_provenance_verification_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Digests.ProtoReflect.Descriptor instead. +func (*Digests) Descriptor() ([]byte, []int) { + return file_proto_provenance_verification_proto_rawDescGZIP(), []int{3} +} + +func (x *Digests) GetDigests() map[string]*StringAllowList { + if x != nil { + return x.Digests + } + return nil +} + +// An allow list of string values. +type StringAllowList struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Values []string `protobuf:"bytes,1,rep,name=values,proto3" json:"values,omitempty"` +} + +func (x *StringAllowList) Reset() { + *x = StringAllowList{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_provenance_verification_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StringAllowList) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StringAllowList) ProtoMessage() {} + +func (x *StringAllowList) ProtoReflect() protoreflect.Message { + mi := &file_proto_provenance_verification_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StringAllowList.ProtoReflect.Descriptor instead. +func (*StringAllowList) Descriptor() ([]byte, []int) { + return file_proto_provenance_verification_proto_rawDescGZIP(), []int{4} +} + +func (x *StringAllowList) GetValues() []string { + if x != nil { + return x.Values + } + return nil +} + +var File_proto_provenance_verification_proto protoreflect.FileDescriptor + +var file_proto_provenance_verification_proto_rawDesc = []byte{ + 0x0a, 0x23, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x6e, 0x61, 0x6e, + 0x63, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1a, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x76, + 0x31, 0x22, 0xf8, 0x01, 0x0a, 0x13, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x6b, 0x0a, 0x17, 0x65, 0x6e, 0x64, + 0x6f, 0x72, 0x73, 0x65, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x5f, + 0x6c, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x76, 0x65, 0x72, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x6e, + 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x64, 0x6f, 0x72, 0x73, 0x65, 0x50, + 0x72, 0x6f, 0x76, 0x65, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x4c, 0x65, 0x73, 0x73, 0x48, 0x00, 0x52, + 0x15, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x73, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x65, 0x6e, 0x61, 0x6e, + 0x63, 0x65, 0x4c, 0x65, 0x73, 0x73, 0x12, 0x6a, 0x0a, 0x14, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, + 0x6e, 0x63, 0x65, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x65, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x66, 0x65, + 0x72, 0x65, 0x6e, 0x63, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x48, 0x00, 0x52, 0x13, 0x72, + 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x65, 0x6e, 0x61, 0x6e, + 0x63, 0x65, 0x42, 0x08, 0x0a, 0x06, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x17, 0x0a, 0x15, + 0x45, 0x6e, 0x64, 0x6f, 0x72, 0x73, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x65, 0x6e, 0x61, 0x6e, 0x63, + 0x65, 0x4c, 0x65, 0x73, 0x73, 0x22, 0xc8, 0x02, 0x0a, 0x19, 0x50, 0x72, 0x6f, 0x76, 0x65, 0x6e, + 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x73, 0x12, 0x35, 0x0a, 0x17, 0x6d, 0x75, 0x73, 0x74, 0x5f, 0x68, 0x61, 0x76, 0x65, + 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x6d, 0x75, 0x73, 0x74, 0x48, 0x61, 0x76, 0x65, 0x42, 0x75, + 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x6a, 0x0a, 0x1f, 0x72, 0x65, + 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x5f, + 0x69, 0x6d, 0x61, 0x67, 0x65, 0x5f, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x73, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x73, 0x52, 0x1c, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, + 0x6e, 0x63, 0x65, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x44, + 0x69, 0x67, 0x65, 0x73, 0x74, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, + 0x6e, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x10, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x70, + 0x6f, 0x55, 0x72, 0x69, 0x12, 0x5a, 0x0a, 0x12, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, + 0x65, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x2b, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, + 0x70, 0x72, 0x6f, 0x76, 0x65, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, + 0x72, 0x69, 0x6e, 0x67, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x11, 0x72, + 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x73, + 0x22, 0xbe, 0x01, 0x0a, 0x07, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x73, 0x12, 0x4a, 0x0a, 0x07, + 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, + 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, + 0x76, 0x65, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x67, 0x65, 0x73, + 0x74, 0x73, 0x2e, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x07, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x73, 0x1a, 0x67, 0x0a, 0x0c, 0x44, 0x69, 0x67, 0x65, + 0x73, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x41, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x76, 0x65, 0x72, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x6e, 0x61, + 0x6e, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x41, 0x6c, 0x6c, + 0x6f, 0x77, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x22, 0x29, 0x0a, 0x0f, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x41, 0x6c, 0x6c, 0x6f, 0x77, + 0x4c, 0x69, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x42, 0x14, 0x5a, 0x12, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_proto_provenance_verification_proto_rawDescOnce sync.Once + file_proto_provenance_verification_proto_rawDescData = file_proto_provenance_verification_proto_rawDesc +) + +func file_proto_provenance_verification_proto_rawDescGZIP() []byte { + file_proto_provenance_verification_proto_rawDescOnce.Do(func() { + file_proto_provenance_verification_proto_rawDescData = protoimpl.X.CompressGZIP(file_proto_provenance_verification_proto_rawDescData) + }) + return file_proto_provenance_verification_proto_rawDescData +} + +var file_proto_provenance_verification_proto_msgTypes = make([]protoimpl.MessageInfo, 6) +var file_proto_provenance_verification_proto_goTypes = []interface{}{ + (*VerificationOptions)(nil), // 0: verification.provenance.v1.VerificationOptions + (*EndorseProvenanceLess)(nil), // 1: verification.provenance.v1.EndorseProvenanceLess + (*ProvenanceReferenceValues)(nil), // 2: verification.provenance.v1.ProvenanceReferenceValues + (*Digests)(nil), // 3: verification.provenance.v1.Digests + (*StringAllowList)(nil), // 4: verification.provenance.v1.StringAllowList + nil, // 5: verification.provenance.v1.Digests.DigestsEntry +} +var file_proto_provenance_verification_proto_depIdxs = []int32{ + 1, // 0: verification.provenance.v1.VerificationOptions.endorse_provenance_less:type_name -> verification.provenance.v1.EndorseProvenanceLess + 2, // 1: verification.provenance.v1.VerificationOptions.reference_provenance:type_name -> verification.provenance.v1.ProvenanceReferenceValues + 3, // 2: verification.provenance.v1.ProvenanceReferenceValues.reference_builder_image_digests:type_name -> verification.provenance.v1.Digests + 4, // 3: verification.provenance.v1.ProvenanceReferenceValues.reference_builders:type_name -> verification.provenance.v1.StringAllowList + 5, // 4: verification.provenance.v1.Digests.digests:type_name -> verification.provenance.v1.Digests.DigestsEntry + 4, // 5: verification.provenance.v1.Digests.DigestsEntry.value:type_name -> verification.provenance.v1.StringAllowList + 6, // [6:6] is the sub-list for method output_type + 6, // [6:6] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name +} + +func init() { file_proto_provenance_verification_proto_init() } +func file_proto_provenance_verification_proto_init() { + if File_proto_provenance_verification_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_proto_provenance_verification_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*VerificationOptions); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_provenance_verification_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EndorseProvenanceLess); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_provenance_verification_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ProvenanceReferenceValues); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_provenance_verification_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Digests); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_provenance_verification_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StringAllowList); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_proto_provenance_verification_proto_msgTypes[0].OneofWrappers = []interface{}{ + (*VerificationOptions_EndorseProvenanceLess)(nil), + (*VerificationOptions_ReferenceProvenance)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_proto_provenance_verification_proto_rawDesc, + NumEnums: 0, + NumMessages: 6, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_proto_provenance_verification_proto_goTypes, + DependencyIndexes: file_proto_provenance_verification_proto_depIdxs, + MessageInfos: file_proto_provenance_verification_proto_msgTypes, + }.Build() + File_proto_provenance_verification_proto = out.File + file_proto_provenance_verification_proto_rawDesc = nil + file_proto_provenance_verification_proto_goTypes = nil + file_proto_provenance_verification_proto_depIdxs = nil +} diff --git a/proto/provenance_verification.proto b/proto/provenance_verification.proto new file mode 100644 index 00000000..9aee362b --- /dev/null +++ b/proto/provenance_verification.proto @@ -0,0 +1,54 @@ +// Copyright 2023 The Project Oak Authors +// +// 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. + +syntax = "proto3"; + +package verification.provenance.v1; + +option go_package = "proto/verification"; + +// Options for verifying provenances when generating an endorsement statement. +message VerificationOptions { + oneof option { + // Allows generating endorsements without any provenances as evidence. + // If one or more provenances are provided, the presence of this value + // is the same as having an empty reference provenance. + EndorseProvenanceLess endorse_provenance_less = 1; + ProvenanceReferenceValues reference_provenance = 2; + } +} + +// A singleton value to allow provenance-less endorsement generation. +message EndorseProvenanceLess{} + +// A collection of reference values for verifying provenance statements. +message ProvenanceReferenceValues { + bool must_have_build_command = 1; + Digests reference_builder_image_digests = 3; + string reference_repo_uri = 4; + StringAllowList reference_builders = 5; +} + +// An allow list of digests, represented as a mapping from cryptographic hash +// function names, to their allow listed values. +message Digests { + // Keys are cryptographic hash algorithms (e.g., sha256). + // Values are acceptable digests. + map digests = 1; +} + +// An allow list of string values. +message StringAllowList { + repeated string values = 1; +} diff --git a/testdata/different_reference_values.textproto b/testdata/different_reference_values.textproto new file mode 100644 index 00000000..f83ccb01 --- /dev/null +++ b/testdata/different_reference_values.textproto @@ -0,0 +1,9 @@ +reference_provenance: { + reference_builder_image_digests: { + digests: { + key: "sha256" + value: { values: ["another_different_digest"]} + } + } + reference_repo_uri: "some_different_repo" +} diff --git a/testdata/different_reference_values.toml b/testdata/different_reference_values.toml deleted file mode 100644 index 43551b23..00000000 --- a/testdata/different_reference_values.toml +++ /dev/null @@ -1,4 +0,0 @@ -binary_sha256_digests = ["a_different_digest"] -want_build_cmds = true -builder_image_sha256_digests = ["another_different_digest"] -repo_uri = "some_different_repo" diff --git a/testdata/reference_values.textproto b/testdata/reference_values.textproto new file mode 100644 index 00000000..5168b5de --- /dev/null +++ b/testdata/reference_values.textproto @@ -0,0 +1,13 @@ +reference_provenance: { + must_have_build_command: false + reference_builder_image_digests: { + digests: { + key: "sha256" + value: { values: ["9e2ba52487d945504d250de186cb4fe2e3ba023ed2921dd6ac8b97ed43e76af9"]} + } + } + reference_repo_uri: "git+https://github.com/project-oak/oak@refs/heads/main" + reference_builders: { + values: ["https://github.com/slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@refs/tags/v1.2.0"] + } +} diff --git a/testdata/reference_values.toml b/testdata/reference_values.toml deleted file mode 100644 index 33ee7848..00000000 --- a/testdata/reference_values.toml +++ /dev/null @@ -1,5 +0,0 @@ -binary_sha256_digests = ["d059c38cea82047ad316a1c6c6fbd13ecf7a0abdcc375463920bd25bf5c142cc"] -want_build_cmds = false -builder_image_sha256_digests = ["9e2ba52487d945504d250de186cb4fe2e3ba023ed2921dd6ac8b97ed43e76af9"] -repo_uri = "git+https://github.com/project-oak/oak@refs/heads/main" -trusted_builders = ["https://github.com/slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@refs/tags/v1.2.0"] diff --git a/testdata/skip_verification.textproto b/testdata/skip_verification.textproto new file mode 100644 index 00000000..a9d21bc6 --- /dev/null +++ b/testdata/skip_verification.textproto @@ -0,0 +1 @@ +endorse_provenance_less{}