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

Includes SHA2-384 and SHA2-512 digests in the endorsement statement #243

Merged
merged 4 commits into from
Sep 1, 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
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
Loading