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

Commit

Permalink
Included SHA2-384 and SHA2-512 digests in the endorsement statement
Browse files Browse the repository at this point in the history
  • Loading branch information
rbehjati committed Sep 1, 2023
1 parent 242a431 commit 6d44fb4
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 44 deletions.
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
45 changes: 23 additions & 22 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,21 @@ func parseDateOrDefault(date string, value time.Time) (time.Time, error) {
return time.Parse(layout, date)
}

func parseDigest(input string) (*digest, error) {
func computeBinaryDigests(path string) (*intoto.DigestSet, 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])
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{
"sha256": 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
24 changes: 21 additions & 3 deletions internal/model/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,31 @@ type sigstoreBundle struct {
DSSEEnvelope *dsse.Envelope `json:"dsseEnvelope"`
}

// ValidatedProvenance wraps an intoto.Statement representing a valid SLSA provenance statement.
// A provenance statement is valid if it contains a single subject, with a SHA256 hash.
// ValidatedProvenance wraps an intoto.Statement representing a valid SLSA
// provenance statement. A provenance statement is valid if it contains a
// single subject, with a SHA256 hash.
type ValidatedProvenance struct {
// The field is private so that invalid instances cannot be created.
provenance intoto.Statement
}

// FindBinarySHA256Digest looks for a "sha256" or "aha2-256" entry in the input
// DigestSet, and returns its value, or an empty string if no such digest is
// found in the digest set.
func FindBinarySHA256Digest(digestSet intoto.DigestSet) string {
binarySHA256Digest, ok := digestSet["sha256"]
if ok {
return binarySHA256Digest
}

binarySHA256Digest, ok = digestSet["sha2-256"]
if ok {
return binarySHA256Digest
}

return ""
}

// NewValidatedProvenance validates the given provenance and returns an
// instance of ValidatedProvenance wrapping it if it is valid, or an error
// otherwise.
Expand All @@ -46,7 +64,7 @@ func NewValidatedProvenance(provenance intoto.Statement) (*ValidatedProvenance,

// GetBinarySHA256Digest returns the SHA256 digest of the subject.
func (p *ValidatedProvenance) GetBinarySHA256Digest() string {
return p.provenance.Subject[0].Digest["sha256"]
return FindBinarySHA256Digest(p.provenance.Subject[0].Digest)
}

// GetBinaryName returns the name of the subject.
Expand Down
11 changes: 7 additions & 4 deletions pkg/claims/endorsement.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,15 @@ import (
// together with `ClaimV1` as the predicate type in an in-toto statement.
const EndorsementV2 = "https://github.com/project-oak/transparent-release/endorsement/v2"

// VerifiedProvenanceSet encapsulates metadata about a non-empty list of verified provenances.
// VerifiedProvenanceSet encapsulates metadata about a non-empty list of
// verified provenances.
type VerifiedProvenanceSet struct {
// Name of the binary that all validated provenances agree on.
BinaryName string
// SHA256 digest of the binary that all validated provenances agree on.
BinaryDigest string
// DigestSet containing one or more digests of the binary. Must at least
// contain one SHA256 (or SHA2-256) entry that all validated provenances
// agree on.
Digests intoto.DigestSet
// Provenances is a possibly empty list of provenance metadata objects.
Provenances []ProvenanceData
}
Expand Down Expand Up @@ -127,7 +130,7 @@ func GenerateEndorsementStatement(validity ClaimValidity, provenances VerifiedPr

subject := intoto.Subject{
Name: provenances.BinaryName,
Digest: intoto.DigestSet{"sha256": provenances.BinaryDigest},
Digest: provenances.Digests,
}

statementHeader := intoto.StatementHeader{
Expand Down
Binary file added testdata/binary
Binary file not shown.

0 comments on commit 6d44fb4

Please sign in to comment.