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

Commit

Permalink
Includes SHA2-384 and SHA2-512 digests in the endorsement statement (#…
Browse files Browse the repository at this point in the history
…243)

* Included SHA2-384 and SHA2-512 digests in the endorsement statement
* Update CI command
* Fix after review
  • Loading branch information
rbehjati authored Sep 1, 2023
1 parent 242a431 commit 1d291ba
Show file tree
Hide file tree
Showing 9 changed files with 106 additions and 69 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ jobs:
- name: endorser-e2e
run: |
go run cmd/endorser/main.go \
--binary_digest=sha256:39051983bbb600bbfb91bd22ee4c976420f8f0c6a895fd083dcb0d153ddd5fd6 \
--binary_name=oak_echo_raw_enclave_app \
--provenance_uris=https://ent-server-62sa4xcfia-ew.a.run.app/raw/sha256:b28696a8341443e3ba433373c60fe1eba8d96f28c8aff6c5ee03d752dd3b399b \
--binary_path=testdata/binary \
--binary_name=stage0_bin \
--provenance_uris=https://ent-server-62sa4xcfia-ew.a.run.app/raw/sha2-256:94f2b47418b42dde64f678a9d348dde887bfe4deafc8b43f611240fee6cc750a \
--verification_options=testdata/skip_verification.textproto
# TODO(#113): Find a better, and easier-to-navigate way to report coverage.
Expand Down
10 changes: 5 additions & 5 deletions cmd/endorser/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ Example execution without provenances:

```bash
go run cmd/endorser/main.go \
--binary_digest=sha256:1234 \
--binary_name=binary \
--binary_path=testdata/binary \
--binary_name=stage0_bin \
--verification_options=testdata/skip_verification.textproto
```

Expand All @@ -30,9 +30,9 @@ Example execution with a provenance URI from ent (for simplicity we pass in

```bash
go run cmd/endorser/main.go \
--binary_digest=sha256:39051983bbb600bbfb91bd22ee4c976420f8f0c6a895fd083dcb0d153ddd5fd6 \
--binary_name=oak_echo_raw_enclave_app \
--provenance_uris=https://ent-server-62sa4xcfia-ew.a.run.app/raw/sha256:b28696a8341443e3ba433373c60fe1eba8d96f28c8aff6c5ee03d752dd3b399b \
--binary_path=testdata/binary \
--binary_name=stage0_bin \
--provenance_uris=https://ent-server-62sa4xcfia-ew.a.run.app/raw/sha2-256:94f2b47418b42dde64f678a9d348dde887bfe4deafc8b43f611240fee6cc750a \
--verification_options=testdata/skip_verification.textproto
```

Expand Down
46 changes: 23 additions & 23 deletions cmd/endorser/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,19 @@
package main

import (
"crypto/sha256"
"crypto/sha512"
"encoding/hex"
"encoding/json"
"flag"
"fmt"
"log"
"os"
"strings"
"time"

"github.com/project-oak/transparent-release/internal/endorser"
"github.com/project-oak/transparent-release/pkg/claims"
"github.com/project-oak/transparent-release/pkg/intoto"
)

// ISO 8601 layout for representing input dates.
Expand All @@ -42,13 +45,8 @@ func (f *provenanceURIsFlag) Set(value string) error {
return nil
}

type digest struct {
alg string
value string
}

type inputOptions struct {
binaryDigest string
binaryPath string
binaryName string
verificationOptions string
endorsementPath string
Expand All @@ -58,8 +56,8 @@ type inputOptions struct {
}

func (i *inputOptions) init() {
flag.StringVar(&i.binaryDigest, "binary_digest", "",
"Digest of the binary to endorse, of the form alg:value. Accepted values for alg include sha256, and sha2-256")
flag.StringVar(&i.binaryPath, "binary_path", "",
"Location of the binary in the local file system. This is required for computing various digests.")
flag.StringVar(&i.binaryName, "binary_name", "",
"Name of the binary to endorse. Should match the name in provenances, if provenance URIs are provided.")
flag.StringVar(&i.verificationOptions, "verification_options", "",
Expand All @@ -78,7 +76,7 @@ func main() {
opt := inputOptions{}
opt.init()

digest, err := parseDigest(opt.binaryDigest)
digests, err := computeBinaryDigests(opt.binaryPath)
if err != nil {
log.Fatalf("Failed parsing binaryDigest: %v", err)
}
Expand All @@ -98,7 +96,7 @@ func main() {
log.Fatalf("Failed loading provenances: %v", err)
}

endorsement, err := endorser.GenerateEndorsement(opt.binaryName, digest.value, verOpts, *validity, provenances)
endorsement, err := endorser.GenerateEndorsement(opt.binaryName, *digests, verOpts, *validity, provenances)
if err != nil {
log.Fatalf("Failed generating endorsement statement %v", err)
}
Expand Down Expand Up @@ -147,18 +145,20 @@ func parseDateOrDefault(date string, value time.Time) (time.Time, error) {
return time.Parse(layout, date)
}

func parseDigest(input string) (*digest, error) {
// We expect the input to be of the ALG:VALUE form.
parts := strings.Split(input, ":")
if len(parts) != 2 {
return nil, fmt.Errorf("got %s, want ALG:VALUE format", input)
}
if !strings.EqualFold("sha256", parts[0]) && !strings.EqualFold("sha2-256", parts[0]) {
return nil, fmt.Errorf("unrecognized hash algorithm (%q), must be one of sha256 or sha2-256", parts[0])
func computeBinaryDigests(path string) (*intoto.DigestSet, error) {
bytes, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("failed to read bytes from path %q", path)
}
digest := digest{
alg: parts[0],
value: parts[1],

sum256 := sha256.Sum256(bytes)
sum512 := sha512.Sum512(bytes)
sum384 := sha512.Sum384(bytes)

digestSet := intoto.DigestSet{
"sha2-256": hex.EncodeToString(sum256[:]),
"sha2-512": hex.EncodeToString(sum512[:]),
"sha2-384": hex.EncodeToString(sum384[:]),
}
return &digest, nil
return &digestSet, nil
}
21 changes: 11 additions & 10 deletions internal/endorser/endorser.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,11 @@ type ParsedProvenance struct {
// 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 *pb.VerificationOptions, validityDuration claims.ClaimValidity, provenances []ParsedProvenance) (*intoto.Statement, error) {
func GenerateEndorsement(binaryName string, digests intoto.DigestSet, verOpt *pb.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)
verifiedProvenances, err := verifyAndSummarizeProvenances(binaryName, digests, verOpt, provenances)
if err != nil {
return nil, fmt.Errorf("could not verify and summarize provenances: %v", err)
}
Expand All @@ -76,8 +76,8 @@ func GenerateEndorsement(binaryName, binaryDigest string, verOpt *pb.Verificatio
// 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).
// (4) Provenances match but don't match the input binary name or digest.
func verifyAndSummarizeProvenances(binaryName, binaryDigest string, verOpt *pb.VerificationOptions, provenances []ParsedProvenance) (*claims.VerifiedProvenanceSet, error) {
// (4) Provenances match but don't match the input binary name or digests.
func verifyAndSummarizeProvenances(binaryName string, digests intoto.DigestSet, verOpt *pb.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 @@ -92,10 +92,11 @@ func verifyAndSummarizeProvenances(binaryName, binaryDigest string, verOpt *pb.V
var errs error
if len(provenanceIRs) > 0 {
errs = multierr.Append(verifyConsistency(provenanceIRs), verifyProvenances(verOpt.GetReferenceProvenance(), provenanceIRs))
binarySHA256Digest := model.FindBinarySHA256Digest(digests)

if provenanceIRs[0].BinarySHA256Digest() != binaryDigest {
if provenanceIRs[0].BinarySHA256Digest() != binarySHA256Digest {
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))
provenanceIRs[0].BinarySHA256Digest(), binarySHA256Digest))
}
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)",
Expand All @@ -104,13 +105,13 @@ func verifyAndSummarizeProvenances(binaryName, binaryDigest string, verOpt *pb.V
}

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

verifiedProvenances := claims.VerifiedProvenanceSet{
BinaryDigest: binaryDigest,
BinaryName: binaryName,
Provenances: provenancesData,
Digests: digests,
BinaryName: binaryName,
Provenances: provenancesData,
}

return &verifiedProvenances, nil
Expand Down
51 changes: 32 additions & 19 deletions internal/endorser/endorser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ func createProvenanceList(t *testing.T, paths []string) []ParsedProvenance {

func TestGenerateEndorsement_InvalidVerificationOptions(t *testing.T) {
verOpts := &pb.VerificationOptions{}
_, err := GenerateEndorsement(binaryName, binaryDigestSha256, verOpts, createClaimValidity(7), []ParsedProvenance{})
digests := map[string]string{"sha2-256": binaryDigestSha256}
_, err := GenerateEndorsement(binaryName, digests, 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:")
}
Expand All @@ -73,25 +74,26 @@ func TestGenerateEndorsement_NoProvenance_EndorseProvenanceLess(t *testing.T) {
EndorseProvenanceLess: &pb.EndorseProvenanceLess{},
},
}
statement, err := GenerateEndorsement(binaryName, binaryDigestSha256, verOpts, createClaimValidity(7), []ParsedProvenance{})
digests := map[string]string{"sha2-256": binaryDigestSha256}
statement, err := GenerateEndorsement(binaryName, digests, 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"], binaryDigestSha256)
testutil.AssertEq(t, "binary hash", statement.Subject[0].Digest["sha2-256"], 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 load verification options: %v", err)
}
statement, err = GenerateEndorsement(binaryName, binaryDigestSha256, verOpts, createClaimValidity(7), []ParsedProvenance{})
statement, err = GenerateEndorsement(binaryName, digests, 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"], binaryDigestSha256)
testutil.AssertEq(t, "binary hash", statement.Subject[0].Digest["sha2-256"], binaryDigestSha256)
testutil.AssertEq(t, "binary name", statement.Subject[0].Name, binaryName)
}

Expand All @@ -103,12 +105,13 @@ func TestGenerateEndorsement_SingleProvenance_EndorseProvenanceLess(t *testing.T
}
provenances := createProvenanceList(t, []string{"../../testdata/slsa_v02_provenance.json"})

statement, err := GenerateEndorsement(binaryName, binaryDigestSha256, verOpts, createClaimValidity(7), provenances)
digests := map[string]string{"sha2-256": binaryDigestSha256}
statement, err := GenerateEndorsement(binaryName, digests, 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 hash", statement.Subject[0].Digest["sha2-256"], binaryDigestSha256)
testutil.AssertEq(t, "binary name", statement.Subject[0].Name, binaryName)

predicate := statement.Predicate.(claims.ClaimPredicate)
Expand All @@ -124,7 +127,8 @@ func TestGenerateEndorsement_SingleInvalidProvenance_EndorseProvenanceLess(t *te

provenances := createProvenanceList(t, []string{"../../testdata/slsa_v02_provenance.json"})

_, err := GenerateEndorsement(binaryName+"_diff", binaryDigestSha256, verOpts, createClaimValidity(7), provenances)
digests := map[string]string{"sha2-256": binaryDigestSha256}
_, err := GenerateEndorsement(binaryName+"_diff", digests, 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")
}
Expand All @@ -137,12 +141,14 @@ func TestLoadAndVerifyProvenances_MultipleValidProvenances_EndorseProvenanceLess
EndorseProvenanceLess: &pb.EndorseProvenanceLess{},
},
}
statement, err := GenerateEndorsement(binaryName, binaryDigestSha256, verOpts, createClaimValidity(7), provenances)

digests := map[string]string{"sha2-256": binaryDigestSha256}
statement, err := GenerateEndorsement(binaryName, digests, 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 hash", statement.Subject[0].Digest["sha2-256"], binaryDigestSha256)
testutil.AssertEq(t, "binary name", statement.Subject[0].Name, binaryName)

predicate := statement.Predicate.(claims.ClaimPredicate)
Expand All @@ -159,7 +165,8 @@ func TestLoadAndVerify_MultipleInconsistentProvenances_EndorseProvenanceLess(t *
}

// Provenances each contain a (different) given reference binary SHA256 digest value, but are inconsistent.
_, err := GenerateEndorsement(binaryName, binaryDigestSha256, verOpts, createClaimValidity(3), provenances)
digests := map[string]string{"sha2-256": binaryDigestSha256}
_, err := GenerateEndorsement(binaryName, digests, verOpts, createClaimValidity(3), provenances)
if err == nil || !strings.Contains(err.Error(), errorInconsistentProvenances) {
t.Fatalf("got %q, want error message containing %q,", err, errorInconsistentProvenances)
}
Expand All @@ -174,12 +181,13 @@ func TestGenerateEndorsement_SingleValidProvenance(t *testing.T) {
t.Fatalf("Could not load verification options: %v", err)
}

statement, err := GenerateEndorsement(binaryName, binaryDigestSha256, verOpt, validity, provenances)
digests := map[string]string{"sha2-256": binaryDigestSha256}
statement, err := GenerateEndorsement(binaryName, digests, verOpt, validity, provenances)
if err != nil {
t.Fatalf("Could not generate endorsement from %q: %v", provenances[0].SourceMetadata.URI, err)
}

testutil.AssertEq(t, "binary hash", statement.Subject[0].Digest["sha256"], binaryDigestSha256)
testutil.AssertEq(t, "binary hash", statement.Subject[0].Digest["sha2-256"], binaryDigestSha256)
testutil.AssertEq(t, "binary name", statement.Subject[0].Name, binaryName)

predicate := statement.Predicate.(claims.ClaimPredicate)
Expand All @@ -196,13 +204,14 @@ func TestLoadAndVerifyProvenances_MultipleValidProvenances(t *testing.T) {
ReferenceProvenance: &pb.ProvenanceReferenceValues{},
},
}
provenanceSet, err := verifyAndSummarizeProvenances(binaryName, binaryDigestSha256, verOpts, provenances)
digests := map[string]string{"sha2-256": binaryDigestSha256}
provenanceSet, err := verifyAndSummarizeProvenances(binaryName, digests, 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, binaryDigestSha256)
testutil.AssertEq(t, "binary hash", provenanceSet.Digests["sha2-256"], digests["sha2-256"])
}

func TestLoadProvenances_FailingSingleRemoteProvenanceEndorsement(t *testing.T) {
Expand All @@ -222,8 +231,9 @@ func TestLoadAndVerifyProvenances_ConsistentNotVerified(t *testing.T) {
},
}

digest := map[string]string{"sha2-256": binaryDigestSha256 + "diff"}
// Provenances do not contain the given reference binary SHA256 digest value, but are consistent.
_, err := verifyAndSummarizeProvenances(binaryName, binaryDigestSha256+"_diff", verOpts, provenances)
_, err := verifyAndSummarizeProvenances(binaryName, digest, verOpts, provenances)
if err == nil || !strings.Contains(err.Error(), errorBinaryDigest) {
t.Fatalf("got %q, want error message containing %q,", err, errorBinaryDigest)
}
Expand All @@ -239,7 +249,8 @@ func TestLoadAndVerify_InconsistentVerified(t *testing.T) {
}

// Provenances each contain a (different) given reference binary SHA256 digest value, but are inconsistent.
_, err := verifyAndSummarizeProvenances(binaryName, binaryDigestSha256, &verOpt, provenances)
digests := map[string]string{"sha2-256": binaryDigestSha256}
_, err := verifyAndSummarizeProvenances(binaryName, digests, &verOpt, provenances)
if err == nil || !strings.Contains(err.Error(), errorInconsistentProvenances) {
t.Fatalf("got %q, want error message containing %q,", err, errorInconsistentProvenances)
}
Expand All @@ -254,7 +265,8 @@ func TestLoadAndVerify_InconsistentNotVerified(t *testing.T) {
},
}

_, err := verifyAndSummarizeProvenances(binaryName, binaryDigestSha256+"_diff", verOpt, provenances)
digests := map[string]string{"sha2-256": binaryDigestSha256 + "_diff"}
_, err := verifyAndSummarizeProvenances(binaryName, digests, verOpt, provenances)
if err == nil || !strings.Contains(err.Error(), errorBinaryDigest) {
t.Fatalf("got %q, want error message containing %q,", err, errorBinaryDigest)
}
Expand All @@ -272,7 +284,8 @@ func TestLoadAndVerifyProvenances_NotVerified(t *testing.T) {
t.Fatalf("Could not load verification options: %v", err)
}

_, err = verifyAndSummarizeProvenances(binaryName, "a_different_digest", verOpts, provenances)
digests := map[string]string{"sha2-256": "a_different_digest"}
_, err = verifyAndSummarizeProvenances(binaryName, digests, verOpts, provenances)
if err == nil || !strings.Contains(err.Error(), errorBinaryDigest) {
t.Fatalf("got %q, want error message containing %q,", err, errorBinaryDigest)
}
Expand Down
Loading

0 comments on commit 1d291ba

Please sign in to comment.