Skip to content

Commit

Permalink
refactor: move gitoid code to cyrptoutil, use digestvalue everywhere
Browse files Browse the repository at this point in the history
When the functionality to calculate gitoids was added, there was a bit
of tech debt incurred since they didn't implement hash.Hash. This
remedies this with an admitedly hacky implementation of hash.Hash that
wraps the gitoid code. This also standardizes our cryptoutil fucntions
around the DigestValue struct that was added around this time to
differentiate between gitoids and regular hash functions.
  • Loading branch information
mikhailswift committed Oct 27, 2023
1 parent cc25097 commit ab4f36c
Show file tree
Hide file tree
Showing 16 changed files with 134 additions and 68 deletions.
4 changes: 2 additions & 2 deletions attestation/aws-iid/aws-iid.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ func init() {

type Attestor struct {
ec2metadata.EC2InstanceIdentityDocument
hashes []crypto.Hash
hashes []cryptoutil.DigestValue
session session.Session
conf *aws.Config
RawIID string `json:"rawiid"`
Expand Down Expand Up @@ -195,7 +195,7 @@ func (a *Attestor) Verify() error {
}

func (a *Attestor) Subjects() map[string]cryptoutil.DigestSet {
hashes := []crypto.Hash{crypto.SHA256}
hashes := []cryptoutil.DigestValue{{Hash: crypto.SHA256}}
subjects := make(map[string]cryptoutil.DigestSet)
if ds, err := cryptoutil.CalculateDigestSetFromBytes([]byte(a.EC2InstanceIdentityDocument.InstanceID), hashes); err == nil {
subjects[fmt.Sprintf("instanceid:%s", a.EC2InstanceIdentityDocument.InstanceID)] = ds
Expand Down
3 changes: 1 addition & 2 deletions attestation/commandrun/tracing_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ package commandrun

import (
"bytes"
"crypto"
"fmt"
"os"
"os/exec"
Expand All @@ -42,7 +41,7 @@ type ptraceContext struct {
mainProgram string
processes map[int]*ProcessInfo
exitCode int
hash []crypto.Hash
hash []cryptoutil.DigestValue
environmentBlockList map[string]struct{}
}

Expand Down
10 changes: 5 additions & 5 deletions attestation/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func WithContext(ctx context.Context) AttestationContextOption {
}
}

func WithHashes(hashes []crypto.Hash) AttestationContextOption {
func WithHashes(hashes []cryptoutil.DigestValue) AttestationContextOption {
return func(ctx *AttestationContext) {
if len(hashes) > 0 {
ctx.hashes = hashes
Expand All @@ -83,7 +83,7 @@ type AttestationContext struct {
ctx context.Context
attestors []Attestor
workingDir string
hashes []crypto.Hash
hashes []cryptoutil.DigestValue
completedAttestors []CompletedAttestor
products map[string]Product
materials map[string]cryptoutil.DigestSet
Expand All @@ -104,7 +104,7 @@ func NewContext(attestors []Attestor, opts ...AttestationContextOption) (*Attest
ctx: context.Background(),
attestors: attestors,
workingDir: wd,
hashes: []crypto.Hash{crypto.SHA256},
hashes: []cryptoutil.DigestValue{{Hash: crypto.SHA256}, {Hash: crypto.SHA256, GitOID: true}, {Hash: crypto.SHA1, GitOID: true}},
materials: make(map[string]cryptoutil.DigestSet),
products: make(map[string]Product),
}
Expand Down Expand Up @@ -222,8 +222,8 @@ func (ctx *AttestationContext) WorkingDir() string {
return ctx.workingDir
}

func (ctx *AttestationContext) Hashes() []crypto.Hash {
hashes := make([]crypto.Hash, len(ctx.hashes))
func (ctx *AttestationContext) Hashes() []cryptoutil.DigestValue {
hashes := make([]cryptoutil.DigestValue, len(ctx.hashes))
copy(hashes, ctx.hashes)
return hashes
}
Expand Down
29 changes: 1 addition & 28 deletions attestation/file/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,18 @@
package file

import (
"crypto"
"io/fs"
"os"
"path/filepath"

"github.com/edwarnicke/gitoid"
"github.com/testifysec/go-witness/cryptoutil"
"github.com/testifysec/go-witness/log"
)

// recordArtifacts will walk basePath and record the digests of each file with each of the functions in hashes.
// If file already exists in baseArtifacts and the two artifacts are equal the artifact will not be in the
// returned map of artifacts.
func RecordArtifacts(basePath string, baseArtifacts map[string]cryptoutil.DigestSet, hashes []crypto.Hash, visitedSymlinks map[string]struct{}) (map[string]cryptoutil.DigestSet, error) {
func RecordArtifacts(basePath string, baseArtifacts map[string]cryptoutil.DigestSet, hashes []cryptoutil.DigestValue, visitedSymlinks map[string]struct{}) (map[string]cryptoutil.DigestSet, error) {
artifacts := make(map[string]cryptoutil.DigestSet)
err := filepath.Walk(basePath, func(path string, info fs.FileInfo, err error) error {
if err != nil {
Expand Down Expand Up @@ -80,31 +78,6 @@ func RecordArtifacts(basePath string, baseArtifacts map[string]cryptoutil.Digest
return err
}

fileReader, err := os.Open(path)
if err != nil {
return err
}

goidSha1, err := gitoid.New(fileReader)
if err != nil {
return err
}

goidSha256, err := gitoid.New(fileReader, gitoid.WithSha256())
if err != nil {
return err
}

artifact[cryptoutil.DigestValue{
Hash: crypto.SHA1,
GitOID: true,
}] = goidSha1.URI()

artifact[cryptoutil.DigestValue{
Hash: crypto.SHA256,
GitOID: true,
}] = goidSha256.URI()

if shouldRecord(relPath, artifact, baseArtifacts) {
artifacts[relPath] = artifact
}
Expand Down
6 changes: 3 additions & 3 deletions attestation/file/file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,13 @@ func TestBrokenSymlink(t *testing.T) {
symTestDir := filepath.Join(dir, "symTestDir")
require.NoError(t, os.Symlink(testDir, symTestDir))

_, err := RecordArtifacts(dir, map[string]cryptoutil.DigestSet{}, []crypto.Hash{crypto.SHA256}, map[string]struct{}{})
_, err := RecordArtifacts(dir, map[string]cryptoutil.DigestSet{}, []cryptoutil.DigestValue{{Hash: crypto.SHA256}}, map[string]struct{}{})
require.NoError(t, err)

// remove the symlinks and make sure we don't get an error back
require.NoError(t, os.RemoveAll(testDir))
require.NoError(t, os.RemoveAll(testFile))
_, err = RecordArtifacts(dir, map[string]cryptoutil.DigestSet{}, []crypto.Hash{crypto.SHA256}, map[string]struct{}{})
_, err = RecordArtifacts(dir, map[string]cryptoutil.DigestSet{}, []cryptoutil.DigestValue{{Hash: crypto.SHA256}}, map[string]struct{}{})
require.NoError(t, err)
}

Expand All @@ -58,6 +58,6 @@ func TestSymlinkCycle(t *testing.T) {
require.NoError(t, os.Symlink(dir, symTestDir))

// if a symlink cycle weren't properly handled this would be an infinite loop
_, err := RecordArtifacts(dir, map[string]cryptoutil.DigestSet{}, []crypto.Hash{crypto.SHA256}, map[string]struct{}{})
_, err := RecordArtifacts(dir, map[string]cryptoutil.DigestSet{}, []cryptoutil.DigestValue{{Hash: crypto.SHA256}}, map[string]struct{}{})
require.NoError(t, err)
}
2 changes: 1 addition & 1 deletion attestation/gcp-iit/gcp-iit.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ func (a *Attestor) getInstanceData() {

func (a *Attestor) Subjects() map[string]cryptoutil.DigestSet {
subjects := make(map[string]cryptoutil.DigestSet)
hashes := []crypto.Hash{crypto.SHA256}
hashes := []cryptoutil.DigestValue{{Hash: crypto.SHA256}}
if ds, err := cryptoutil.CalculateDigestSetFromBytes([]byte(a.InstanceID), hashes); err == nil {
subjects[fmt.Sprintf("instanceid:%v", a.InstanceID)] = ds
} else {
Expand Down
2 changes: 1 addition & 1 deletion attestation/git/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ func (a *Attestor) Attest(ctx *attestation.AttestationContext) error {

func (a *Attestor) Subjects() map[string]cryptoutil.DigestSet {
subjects := make(map[string]cryptoutil.DigestSet)
hashes := []crypto.Hash{crypto.SHA256}
hashes := []cryptoutil.DigestValue{{Hash: crypto.SHA256}}

subjectName := fmt.Sprintf("commithash:%v", a.CommitHash)
subjects[subjectName] = cryptoutil.DigestSet{
Expand Down
2 changes: 1 addition & 1 deletion attestation/github/github.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ func (a *Attestor) Attest(ctx *attestation.AttestationContext) error {

func (a *Attestor) Subjects() map[string]cryptoutil.DigestSet {
subjects := make(map[string]cryptoutil.DigestSet)
hashes := []crypto.Hash{crypto.SHA256}
hashes := []cryptoutil.DigestValue{{Hash: crypto.SHA256}}
if pipelineSubj, err := cryptoutil.CalculateDigestSetFromBytes([]byte(a.PipelineUrl), hashes); err == nil {
subjects[fmt.Sprintf("pipelineurl:%v", a.PipelineUrl)] = pipelineSubj
} else {
Expand Down
2 changes: 1 addition & 1 deletion attestation/gitlab/gitlab.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ func (a *Attestor) Attest(ctx *attestation.AttestationContext) error {

func (a *Attestor) Subjects() map[string]cryptoutil.DigestSet {
subjects := make(map[string]cryptoutil.DigestSet)
hashes := []crypto.Hash{crypto.SHA256}
hashes := []cryptoutil.DigestValue{{Hash: crypto.SHA256}}
if ds, err := cryptoutil.CalculateDigestSetFromBytes([]byte(a.PipelineUrl), hashes); err == nil {
subjects[fmt.Sprintf("pipelineurl:%v", a.PipelineUrl)] = ds
} else {
Expand Down
2 changes: 1 addition & 1 deletion attestation/maven/maven.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ func (a *Attestor) Attest(ctx *attestation.AttestationContext) error {

func (a *Attestor) Subjects() map[string]cryptoutil.DigestSet {
subjects := make(map[string]cryptoutil.DigestSet)
hashes := []crypto.Hash{crypto.SHA256}
hashes := []cryptoutil.DigestValue{{Hash: crypto.SHA256}}
projectSubject := fmt.Sprintf("project:%v/%v@%v", a.GroupId, a.ArtifactId, a.Version)
if ds, err := cryptoutil.CalculateDigestSetFromBytes([]byte(projectSubject), hashes); err == nil {
subjects[projectSubject] = ds
Expand Down
2 changes: 1 addition & 1 deletion attestation/oci/oci.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ func (a *Attestor) parseMaifest(ctx *attestation.AttestationContext) error {
}

func (a *Attestor) Subjects() map[string]cryptoutil.DigestSet {
hashes := []crypto.Hash{crypto.SHA256}
hashes := []cryptoutil.DigestValue{{Hash: crypto.SHA256}}
subj := make(map[string]cryptoutil.DigestSet)
subj[fmt.Sprintf("manifestdigest:%s", a.ManifestDigest[cryptoutil.DigestValue{Hash: crypto.SHA256}])] = a.ManifestDigest
subj[fmt.Sprintf("tardigest:%s", a.TarDigest[cryptoutil.DigestValue{Hash: crypto.SHA256}])] = a.TarDigest
Expand Down
2 changes: 1 addition & 1 deletion attestation/oci/oci_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ func TestAttestor_Attest(t *testing.T) {
t.Fatal(err)
}

hashes := []crypto.Hash{crypto.SHA256}
hashes := []cryptoutil.DigestValue{{Hash: crypto.SHA256}}

tarDigest, err := cryptoutil.CalculateDigestSetFromBytes([]byte(decoded), hashes)
if err != nil {
Expand Down
12 changes: 4 additions & 8 deletions attestation/policyverify/policyverify.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,23 +173,19 @@ func (a *Attestor) Attest(ctx *attestation.AttestationContext) error {
return fmt.Errorf("failed to verify policy: %w", err)
}

a.VerificationSummary, err = verificationSummaryFromResults(a.policyEnvelope, policyResult, accepted)
a.VerificationSummary, err = verificationSummaryFromResults(ctx, a.policyEnvelope, policyResult, accepted)
if err != nil {
return fmt.Errorf("failed to generate verification summary: %w", err)
}

return policyErr
}

func calculateDigest(b []byte) (cryptoutil.DigestSet, error) {
return cryptoutil.CalculateDigestSetFromBytes(b, []crypto.Hash{crypto.SHA256})
}

func verificationSummaryFromResults(policyEnvelope dsse.Envelope, policyResult policy.PolicyResult, accepted bool) (slsa.VerificationSummary, error) {
func verificationSummaryFromResults(ctx *attestation.AttestationContext, policyEnvelope dsse.Envelope, policyResult policy.PolicyResult, accepted bool) (slsa.VerificationSummary, error) {
inputAttestations := make([]slsa.ResourceDescriptor, 0, len(policyResult.EvidenceByStep))
for _, input := range policyResult.EvidenceByStep {
for _, attestation := range input {
digest, err := calculateDigest(attestation.Envelope.Payload)
digest, err := cryptoutil.CalculateDigestSetFromBytes(attestation.Envelope.Payload, ctx.Hashes())
if err != nil {
log.Debugf("failed to calculate evidence hash: %v", err)
continue
Expand All @@ -202,7 +198,7 @@ func verificationSummaryFromResults(policyEnvelope dsse.Envelope, policyResult p
}
}

policyDigest, err := calculateDigest(policyEnvelope.Payload)
policyDigest, err := cryptoutil.CalculateDigestSetFromBytes(policyEnvelope.Payload, ctx.Hashes())
if err != nil {
return slsa.VerificationSummary{}, fmt.Errorf("failed to calculate policy digest: %w", err)
}
Expand Down
4 changes: 2 additions & 2 deletions attestation/product/product_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import (
)

func TestFromDigestMap(t *testing.T) {
testDigest, err := cryptoutil.CalculateDigestSetFromBytes([]byte("test"), []crypto.Hash{crypto.SHA256})
testDigest, err := cryptoutil.CalculateDigestSetFromBytes([]byte("test"), []cryptoutil.DigestValue{{Hash: crypto.SHA256}})
assert.NoError(t, err)
testDigestSet := make(map[string]cryptoutil.DigestSet)
testDigestSet["test"] = testDigest
Expand All @@ -56,7 +56,7 @@ func TestAttestorRunType(t *testing.T) {

func TestAttestorAttest(t *testing.T) {
a := New()
testDigest, err := cryptoutil.CalculateDigestSetFromBytes([]byte("test"), []crypto.Hash{crypto.SHA256})
testDigest, err := cryptoutil.CalculateDigestSetFromBytes([]byte("test"), []cryptoutil.DigestValue{{Hash: crypto.SHA256}})
if err != nil {
t.Errorf("Failed to calculate digest set from bytes: %v", err)
}
Expand Down
35 changes: 24 additions & 11 deletions cryptoutil/digestset.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,14 @@ type DigestValue struct {
GitOID bool
}

func (dv DigestValue) New() hash.Hash {
if dv.GitOID {
return &gitoidHasher{hash: dv.Hash, buf: &bytes.Buffer{}}
}

return dv.Hash.New()
}

type DigestSet map[DigestValue]string

func HashToString(h crypto.Hash) (string, error) {
Expand Down Expand Up @@ -142,13 +150,13 @@ func NewDigestSet(digestsByName map[string]string) (DigestSet, error) {
return ds, nil
}

func CalculateDigestSet(r io.Reader, hashes []crypto.Hash) (DigestSet, error) {
func CalculateDigestSet(r io.Reader, digestValues []DigestValue) (DigestSet, error) {
digestSet := make(DigestSet)
writers := []io.Writer{}
hashfuncs := map[crypto.Hash]hash.Hash{}
for _, hash := range hashes {
hashfunc := hash.New()
hashfuncs[hash] = hashfunc
hashfuncs := map[DigestValue]hash.Hash{}
for _, digestValue := range digestValues {
hashfunc := digestValue.New()
hashfuncs[digestValue] = hashfunc
writers = append(writers, hashfunc)
}

Expand All @@ -157,21 +165,26 @@ func CalculateDigestSet(r io.Reader, hashes []crypto.Hash) (DigestSet, error) {
return digestSet, err
}

for hash, hashfunc := range hashfuncs {
digestValue := DigestValue{
Hash: hash,
GitOID: false,
for digestValue, hashfunc := range hashfuncs {
// gitoids are somewhat special... we're using a custom implementation of hash.Hash
// to wrap the gitoid library. Sum will return a gitoid URI, so we don't want to hex
// encode it as it's already a string with a hex encoded hash.
if digestValue.GitOID {
digestSet[digestValue] = string(hashfunc.Sum(nil))
continue
}

digestSet[digestValue] = string(HexEncode(hashfunc.Sum(nil)))
}

return digestSet, nil
}

func CalculateDigestSetFromBytes(data []byte, hashes []crypto.Hash) (DigestSet, error) {
func CalculateDigestSetFromBytes(data []byte, hashes []DigestValue) (DigestSet, error) {
return CalculateDigestSet(bytes.NewReader(data), hashes)
}

func CalculateDigestSetFromFile(path string, hashes []crypto.Hash) (DigestSet, error) {
func CalculateDigestSetFromFile(path string, hashes []DigestValue) (DigestSet, error) {
file, err := os.Open(path)
if err != nil {
return DigestSet{}, err
Expand Down
Loading

0 comments on commit ab4f36c

Please sign in to comment.