-
Notifications
You must be signed in to change notification settings - Fork 9
Allow provenance-less endorsement generation #237
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Intermediate Comments :)
internal/endorser/endorser.go
Outdated
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What are reference values here? Are they inside the VerificationOptions
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. Updated the comment. I meant VerificationOptions.
internal/endorser/endorser.go
Outdated
// 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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: what is a "provenance reference" and is it the same as a "reference provenance" in line 53?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. I meant "reference provenance".
} | ||
|
||
message ProvenanceReferenceValues { | ||
bool must_have_build_command = 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure, but I think generally all fields to be optional is preferred. Happy to discuss offline!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you are right. The guidelines say:
Make all fields optional or repeated. You never know how long a message type is going to last and whether someone will be forced to fill in your required field with an empty string or zero in four years when it’s no longer logically required but the proto still says it is.
But I think for primitive fields it should be fine. Even though I have not made them explicitly optional, I can leave them unset.
Making them optional causes additional an unnecessary indirection, which does not look right.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In proto3, everything is optional by default.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed optional from other fields too. Here is the change in the generated Go code. Some oneof annotation and a oneof wrapper are removed. I am not sure if it has any serious impact on serialization.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the review.
} | ||
|
||
message ProvenanceReferenceValues { | ||
bool must_have_build_command = 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you are right. The guidelines say:
Make all fields optional or repeated. You never know how long a message type is going to last and whether someone will be forced to fill in your required field with an empty string or zero in four years when it’s no longer logically required but the proto still says it is.
But I think for primitive fields it should be fine. Even though I have not made them explicitly optional, I can leave them unset.
Making them optional causes additional an unnecessary indirection, which does not look right.
internal/endorser/endorser.go
Outdated
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. Updated the comment. I meant VerificationOptions.
internal/endorser/endorser.go
Outdated
// 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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. I meant "reference provenance".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I might be missing something on how SkipProvenanceVerification
and the list of provenances interact. Could you have a look and let me know?
internal/endorser/endorser.go
Outdated
// VerificationOptions to verify them. If more than one provenance statements | ||
// are provided the endorsement statement is generated only if the provenance | ||
// statements are consistent and valid according to the input | ||
// VerificationOptions. If no provenances are provided, a provenance-less |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A design question: might it happen, that I accidentially endorse something if I forget to add a provenance? Do we want to make this more explict? E.g., make GenerateEndorsement
private but expose two methods GenerateEndorsementWithProvenance
and GenerateEndorsementWithoutProvenance
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is being handled by the VerificationOptions. It requires explicitly setting skip provenance verification. If that is not set, at least one provenance is required.
So I'd say those accidents are unlikely.
internal/endorser/endorser.go
Outdated
// endorsement is generated, if the input VerificationOptions does not contain | ||
// a reference provenance. | ||
func GenerateEndorsement(binaryName, binaryDigest string, verOpt *prover.VerificationOptions, validityDuration claims.ClaimValidity, provenances []ParsedProvenance) (*intoto.Statement, error) { | ||
if (verOpt.GetSkipProvenanceVerification() == nil) && (verOpt.GetReferenceProvenance() == nil) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just checking: Don't you get from oneof
in the proto definition that exactly one of verOpt
is not nil?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No. I think this is a shortcoming of Go. I was able to create an instance where both options are null, so I had to add this check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, thanks! I'd have expected...
@@ -73,22 +83,35 @@ 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 { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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.
internal/endorser/endorser_test.go
Outdated
@@ -69,7 +71,67 @@ func TestGenerateEndorsement_SingleValidEndorsement(t *testing.T) { | |||
testutil.AssertEq(t, "notAfter date", predicate.Validity.NotAfter, &nextWeek) | |||
} | |||
|
|||
func TestLoadAndVerifyProvenances_MultipleValidEndorsement(t *testing.T) { | |||
func TestGenerateEndorsement_NoProvenance(t *testing.T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens if you give provenances and also set SkipProvenanceVerification
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a few tests and updated the documentation on GenerateEndorsement
. It generates an endorsement with the given provenances as evidence, if the provenances are consistent and match the given subject.
Let me know if you expect some other behavior.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmm... I am not sure I would expect "unverified provenances" as evidence, but actually: why not?
However, is it clear afterwards if the provenances were verified? Or does that not really matter?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They are not totally unverified. But they are not verified against a set of reference values. Actually this is similar to providing verification options with an empty ReferenceProvenance
. Except that the latter enforces having some provenances. Clarified this, and renamed SkipVerification
to EndorseProvenanceLess
to be clear that we are not skipping the entire verification logic.
However, is it clear afterwards if the provenances were verified? Or does that not really matter?
Currently we don't include the verification options in the endorsement, but I think it is a good idea to include it. As an extension. Created #238.
@@ -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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
@@ -32,6 +32,7 @@ import ( | |||
"github.com/project-oak/transparent-release/internal/verification" | |||
"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" |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
internal/endorser/endorser_test.go
Outdated
binaryHash = "d059c38cea82047ad316a1c6c6fbd13ecf7a0abdcc375463920bd25bf5c142cc" | ||
binaryName = "oak_functions_freestanding_bin" | ||
errorBinaryDigest = "do not contain the actual binary SHA256 digest" | ||
binaryHash = "d059c38cea82047ad316a1c6c6fbd13ecf7a0abdcc375463920bd25bf5c142cc" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
binaryHash = "d059c38cea82047ad316a1c6c6fbd13ecf7a0abdcc375463920bd25bf5c142cc" | |
binaryDigestSha256 = "d059c38cea82047ad316a1c6c6fbd13ecf7a0abdcc375463920bd25bf5c142cc" |
for consistency
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
@@ -65,7 +65,7 @@ linters: | |||
- noctx | |||
- nolintlint | |||
- nonamedreturns | |||
- nosnakecase | |||
# - nosnakecase |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove?
There was a problem hiding this comment.
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.
@@ -68,6 +68,25 @@ func TestNotAfterBeforeNotBeforeEndorsement(t *testing.T) { | |||
} | |||
} | |||
|
|||
func TestGenerateProvenanceLessEndorsement(t *testing.T) { | |||
newNotBefore := time.Now().AddDate(0, 0, 1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ideally the tests (and the functions under tests) would not rely on the current time, but would accept a reference time from the outside. This will make them easier to test (without having to depend on relative times), and also avoid actual bugs (TOCTOU and related issues, if multiple functions get their own time.Now()
independently at slightly different times).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the endorsement to be valid the notBefore time should be after the issued
time, which uses the current time. For the test to pass, we have to use the current time here.
// 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). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought we were using the numeric multihash id somewhere else (from https://github.com/multiformats/multicodec/blob/master/table.csv ).
in particular sha256
is ambiguous; it should be sha2-256
or something like that, to be unambiguos -- but the numeric id solves that problem (without having to actual implement full multihash -- I don't think we should do that for now).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not in this repo. This repo follows the in-toto and SLSA style of using digests. We can change that in the future, but that is out of the scope of this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the reviews.
internal/endorser/endorser.go
Outdated
// VerificationOptions to verify them. If more than one provenance statements | ||
// are provided the endorsement statement is generated only if the provenance | ||
// statements are consistent and valid according to the input | ||
// VerificationOptions. If no provenances are provided, a provenance-less |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is being handled by the VerificationOptions. It requires explicitly setting skip provenance verification. If that is not set, at least one provenance is required.
So I'd say those accidents are unlikely.
internal/endorser/endorser.go
Outdated
// endorsement is generated, if the input VerificationOptions does not contain | ||
// a reference provenance. | ||
func GenerateEndorsement(binaryName, binaryDigest string, verOpt *prover.VerificationOptions, validityDuration claims.ClaimValidity, provenances []ParsedProvenance) (*intoto.Statement, error) { | ||
if (verOpt.GetSkipProvenanceVerification() == nil) && (verOpt.GetReferenceProvenance() == nil) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No. I think this is a shortcoming of Go. I was able to create an instance where both options are null, so I had to add this check.
@@ -73,22 +83,35 @@ 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 { |
There was a problem hiding this comment.
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.
internal/endorser/endorser_test.go
Outdated
@@ -69,7 +71,67 @@ func TestGenerateEndorsement_SingleValidEndorsement(t *testing.T) { | |||
testutil.AssertEq(t, "notAfter date", predicate.Validity.NotAfter, &nextWeek) | |||
} | |||
|
|||
func TestLoadAndVerifyProvenances_MultipleValidEndorsement(t *testing.T) { | |||
func TestGenerateEndorsement_NoProvenance(t *testing.T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a few tests and updated the documentation on GenerateEndorsement
. It generates an endorsement with the given provenances as evidence, if the provenances are consistent and match the given subject.
Let me know if you expect some other behavior.
@@ -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 |
There was a problem hiding this comment.
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.
@@ -65,7 +65,7 @@ linters: | |||
- noctx | |||
- nolintlint | |||
- nonamedreturns | |||
- nosnakecase | |||
# - nosnakecase |
There was a problem hiding this comment.
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.
internal/endorser/endorser_test.go
Outdated
binaryHash = "d059c38cea82047ad316a1c6c6fbd13ecf7a0abdcc375463920bd25bf5c142cc" | ||
binaryName = "oak_functions_freestanding_bin" | ||
errorBinaryDigest = "do not contain the actual binary SHA256 digest" | ||
binaryHash = "d059c38cea82047ad316a1c6c6fbd13ecf7a0abdcc375463920bd25bf5c142cc" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
@@ -68,6 +68,25 @@ func TestNotAfterBeforeNotBeforeEndorsement(t *testing.T) { | |||
} | |||
} | |||
|
|||
func TestGenerateProvenanceLessEndorsement(t *testing.T) { | |||
newNotBefore := time.Now().AddDate(0, 0, 1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the endorsement to be valid the notBefore time should be after the issued
time, which uses the current time. For the test to pass, we have to use the current time here.
// 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). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not in this repo. This repo follows the in-toto and SLSA style of using digests. We can change that in the future, but that is out of the scope of this PR.
@@ -32,6 +32,7 @@ import ( | |||
"github.com/project-oak/transparent-release/internal/verification" | |||
"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" |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, added a couple more comments, where I wasn't 100% sure.
internal/endorser/endorser.go
Outdated
// endorsement is generated, if the input VerificationOptions does not contain | ||
// a reference provenance. | ||
func GenerateEndorsement(binaryName, binaryDigest string, verOpt *prover.VerificationOptions, validityDuration claims.ClaimValidity, provenances []ParsedProvenance) (*intoto.Statement, error) { | ||
if (verOpt.GetSkipProvenanceVerification() == nil) && (verOpt.GetReferenceProvenance() == nil) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, thanks! I'd have expected...
internal/endorser/endorser_test.go
Outdated
@@ -69,7 +71,67 @@ func TestGenerateEndorsement_SingleValidEndorsement(t *testing.T) { | |||
testutil.AssertEq(t, "notAfter date", predicate.Validity.NotAfter, &nextWeek) | |||
} | |||
|
|||
func TestLoadAndVerifyProvenances_MultipleValidEndorsement(t *testing.T) { | |||
func TestGenerateEndorsement_NoProvenance(t *testing.T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmm... I am not sure I would expect "unverified provenances" as evidence, but actually: why not?
However, is it clear afterwards if the provenances were verified? Or does that not really matter?
@@ -73,22 +83,35 @@ 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 { |
There was a problem hiding this comment.
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
.
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 { | ||
func verifyProvenances(referenceValues *prover.ProvenanceReferenceValues, provenances []model.ProvenanceIR) error { |
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
Fixes #236 and #234
GenerateEndorsement
. This is required to enable provenance-less endorsement generation.