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

Allow provenance-less endorsement generation #237

Merged
merged 13 commits into from
Jul 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions cmd/verifier/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -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 {
Expand Down
24 changes: 24 additions & 0 deletions docs/development-guidelines.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI this is mostly solved by bazel and / or nix. Especially since the instructions here refer to latest, which will stop working at some point.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. We used to use bazel in this repo, and it caused a lot of problems. I think this is fine for now.


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
```
4 changes: 2 additions & 2 deletions golangci-linters.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ linters:
- noctx
- nolintlint
- nonamedreturns
- nosnakecase
# - nosnakecase

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The template kept these as commented out. I think this is for convenience to be able to enable or disable them easily.

- nosprintfhostport
# Might be nice to enable it later.
# - paralleltest
Expand Down Expand Up @@ -160,7 +160,7 @@ linters:
# - noctx
# - nolintlint
# - nonamedreturns
# - nosnakecase
- nosnakecase
# - nosprintfhostport
- paralleltest
# - prealloc
Expand Down
72 changes: 53 additions & 19 deletions internal/endorser/endorser.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having two different packages with the same name but in different locations in the same repo seems problematic. Maybe one of them should be renamed at some point

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, but I cannot think of a better name. I don't like long names with underscore in them.
Do you have any suggestions?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed the other verification into verifier. The only files in it now are verifier.go and verifier_test.go so I think that makes sense.

)

// ParsedProvenance contains a provenance in the internal ProvenanceIR format,
Expand All @@ -43,26 +44,40 @@ 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)
}

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")
}

Expand All @@ -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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am trying to understand what happens if we have multiple provenances but SkipProvenanceVerification.

IIUC the control flow lands here, but then GetReferenceProvenance would be nil? Is this okay with verifyProvenances?

On a higher level: What do we expect to happen then?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We expect the provenances to be consistent, but they are not verified against a set of reference values. Added tests to cover these scenarios.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, sounds good!

Just to double check: In the GetReferenceProvenance case, verifyProvenances will get nil for GetReferenceProvenance.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. Yes. verifyProvenances is called with a nil. Added a check to immediately return if the input ProvenanceReferenceValues is nil.

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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add a comment what happens if prover.ProvenanceReferenceValues == nil?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

var errs error
if referenceValues == nil {
return nil
}
for index := range provenances {
provenanceVerifier := verification.ProvenanceIRVerifier{
provenanceVerifier := verifier.ProvenanceIRVerifier{
Got: &provenances[index],
Want: referenceValues,
}
Expand Down
Loading
Loading