From b719942290f26984a7a7d323ae747009103ff8d3 Mon Sep 17 00:00:00 2001 From: Hayden B Date: Thu, 19 Oct 2023 05:01:41 -0700 Subject: [PATCH] Add method to get artifact hash for an entry (#1777) This can be used to get the entry's artifact hash without requiring unmarshaling the type. Signed-off-by: Hayden Blauzvern --- pkg/types/alpine/v0.0.1/entry.go | 7 +++++++ pkg/types/alpine/v0.0.1/entry_test.go | 9 +++++++++ pkg/types/cose/v0.0.1/entry.go | 7 +++++++ pkg/types/cose/v0.0.1/entry_test.go | 7 +++++++ pkg/types/dsse/v0.0.1/entry.go | 7 +++++++ pkg/types/dsse/v0.0.1/entry_test.go | 8 ++++++++ pkg/types/entries.go | 5 +++-- pkg/types/hashedrekord/hashedrekord_test.go | 4 ++++ pkg/types/hashedrekord/v0.0.1/entry.go | 7 +++++++ pkg/types/hashedrekord/v0.0.1/entry_test.go | 13 +++++++++++++ pkg/types/helm/v0.0.1/entry.go | 7 +++++++ pkg/types/helm/v0.0.1/entry_test.go | 6 ++++++ pkg/types/intoto/v0.0.1/entry.go | 7 +++++++ pkg/types/intoto/v0.0.1/entry_test.go | 8 ++++++++ pkg/types/intoto/v0.0.2/entry.go | 7 +++++++ pkg/types/intoto/v0.0.2/entry_test.go | 8 ++++++++ pkg/types/jar/v0.0.1/entry.go | 7 +++++++ pkg/types/jar/v0.0.1/entry_test.go | 6 ++++++ pkg/types/rekord/v0.0.1/entry.go | 7 +++++++ pkg/types/rekord/v0.0.1/entry_test.go | 9 +++++++++ pkg/types/rfc3161/v0.0.1/entry.go | 13 +++++++++++++ pkg/types/rfc3161/v0.0.1/entry_test.go | 6 ++++++ pkg/types/rpm/v0.0.1/entry.go | 7 +++++++ pkg/types/rpm/v0.0.1/entry_test.go | 6 ++++++ pkg/types/test_util.go | 4 ++++ pkg/types/tuf/v0.0.1/entry.go | 20 ++++++++++++++++++++ pkg/types/tuf/v0.0.1/entry_test.go | 9 ++++++++- 27 files changed, 208 insertions(+), 3 deletions(-) diff --git a/pkg/types/alpine/v0.0.1/entry.go b/pkg/types/alpine/v0.0.1/entry.go index cd3f20bf5..d4998bb05 100644 --- a/pkg/types/alpine/v0.0.1/entry.go +++ b/pkg/types/alpine/v0.0.1/entry.go @@ -362,6 +362,13 @@ func (v V001Entry) Verifiers() ([]pki.PublicKey, error) { return []pki.PublicKey{key}, nil } +func (v V001Entry) ArtifactHash() (string, error) { + if v.AlpineModel.Package == nil || v.AlpineModel.Package.Hash == nil || v.AlpineModel.Package.Hash.Value == nil || v.AlpineModel.Package.Hash.Algorithm == nil { + return "", errors.New("alpine v0.0.1 entry not initialized") + } + return strings.ToLower(fmt.Sprintf("%s:%s", *v.AlpineModel.Package.Hash.Algorithm, *v.AlpineModel.Package.Hash.Value)), nil +} + func (v V001Entry) Insertable() (bool, error) { if v.AlpineModel.Package == nil { return false, fmt.Errorf("missing package entry") diff --git a/pkg/types/alpine/v0.0.1/entry_test.go b/pkg/types/alpine/v0.0.1/entry_test.go index 03fd600ae..3df468935 100644 --- a/pkg/types/alpine/v0.0.1/entry_test.go +++ b/pkg/types/alpine/v0.0.1/entry_test.go @@ -18,6 +18,8 @@ package alpine import ( "bytes" "context" + "crypto/sha256" + "encoding/hex" "os" "reflect" "testing" @@ -177,6 +179,13 @@ func TestCrossFieldValidation(t *testing.T) { if ok, err := ei.Insertable(); ok || err == nil { t.Errorf("unexpected success calling Insertable on entry created from canonicalized content") } + hash, err := ei.ArtifactHash() + expectedHash := sha256.Sum256(dataBytes) + if err != nil { + t.Errorf("unexpected failure with ArtifactHash: %v", err) + } else if hash != "sha256:"+hex.EncodeToString(expectedHash[:]) { + t.Errorf("unexpected match with ArtifactHash: %s", hash) + } } verifiers, err := v.Verifiers() diff --git a/pkg/types/cose/v0.0.1/entry.go b/pkg/types/cose/v0.0.1/entry.go index d5a74b422..930afb451 100644 --- a/pkg/types/cose/v0.0.1/entry.go +++ b/pkg/types/cose/v0.0.1/entry.go @@ -359,6 +359,13 @@ func (v V001Entry) Verifiers() ([]pki.PublicKey, error) { return []pki.PublicKey{key}, nil } +func (v V001Entry) ArtifactHash() (string, error) { + if v.CoseObj.Data == nil || v.CoseObj.Data.PayloadHash == nil || v.CoseObj.Data.PayloadHash.Value == nil || v.CoseObj.Data.PayloadHash.Algorithm == nil { + return "", errors.New("cose v0.0.1 entry not initialized") + } + return strings.ToLower(fmt.Sprintf("%s:%s", *v.CoseObj.Data.PayloadHash.Algorithm, *v.CoseObj.Data.PayloadHash.Value)), nil +} + func (v V001Entry) Insertable() (bool, error) { if len(v.CoseObj.Message) == 0 { return false, errors.New("missing COSE Sign1 message") diff --git a/pkg/types/cose/v0.0.1/entry_test.go b/pkg/types/cose/v0.0.1/entry_test.go index f6b7a4107..7f60b3cc0 100644 --- a/pkg/types/cose/v0.0.1/entry_test.go +++ b/pkg/types/cose/v0.0.1/entry_test.go @@ -303,6 +303,13 @@ func TestV001Entry_Unmarshal(t *testing.T) { if ok, err := ei.Insertable(); ok || err == nil { t.Errorf("entry created from canonicalized entry should not also be insertable") } + hash, err := ei.ArtifactHash() + expectedHash := sha256.Sum256([]byte("hello")) + if err != nil { + t.Errorf("unexpected failure with ArtifactHash: %v", err) + } else if hash != "sha256:"+hex.EncodeToString(expectedHash[:]) { + t.Errorf("unexpected match with ArtifactHash: %s", hash) + } } } diff --git a/pkg/types/dsse/v0.0.1/entry.go b/pkg/types/dsse/v0.0.1/entry.go index ba41742b9..fb62a22f5 100644 --- a/pkg/types/dsse/v0.0.1/entry.go +++ b/pkg/types/dsse/v0.0.1/entry.go @@ -419,6 +419,13 @@ func (v V001Entry) Verifiers() ([]pki.PublicKey, error) { return keys, nil } +func (v V001Entry) ArtifactHash() (string, error) { + if v.DSSEObj.PayloadHash == nil || v.DSSEObj.PayloadHash.Algorithm == nil || v.DSSEObj.PayloadHash.Value == nil { + return "", errors.New("dsse v0.0.1 entry not initialized") + } + return strings.ToLower(fmt.Sprintf("%s:%s", *v.DSSEObj.PayloadHash.Algorithm, *v.DSSEObj.PayloadHash.Value)), nil +} + func (v V001Entry) Insertable() (bool, error) { if v.DSSEObj.ProposedContent == nil { return false, errors.New("missing proposed content") diff --git a/pkg/types/dsse/v0.0.1/entry_test.go b/pkg/types/dsse/v0.0.1/entry_test.go index e59e46fd5..77cfe66ea 100644 --- a/pkg/types/dsse/v0.0.1/entry_test.go +++ b/pkg/types/dsse/v0.0.1/entry_test.go @@ -306,6 +306,14 @@ func TestV001Entry_Unmarshal(t *testing.T) { t.Errorf("index keys from hydrated object do not match those generated from canonicalized (and re-hydrated) object: %v %v", got, canonicalIndexKeys) } + hash, err := canonicalV001.ArtifactHash() + expectedHash := sha256.Sum256([]byte(validPayload)) + if err != nil { + t.Errorf("unexpected failure with ArtifactHash: %v", err) + } else if hash != "sha256:"+hex.EncodeToString(expectedHash[:]) { + t.Errorf("unexpected match with ArtifactHash: %s", hash) + } + return nil } if err := uv(); (err != nil) != tt.wantErr { diff --git a/pkg/types/entries.go b/pkg/types/entries.go index 3bbf47e05..c2565968c 100644 --- a/pkg/types/entries.go +++ b/pkg/types/entries.go @@ -37,8 +37,9 @@ type EntryImpl interface { Canonicalize(ctx context.Context) ([]byte, error) // marshal the canonical entry to be put into the tlog Unmarshal(e models.ProposedEntry) error // unmarshal the abstract entry into the specific struct for this versioned type CreateFromArtifactProperties(context.Context, ArtifactProperties) (models.ProposedEntry, error) - Verifiers() ([]pki.PublicKey, error) - Insertable() (bool, error) // denotes whether the entry that was unmarshalled has the writeOnly fields required to validate and insert into the log + Verifiers() ([]pki.PublicKey, error) // list of keys or certificates that can verify an entry's signature + ArtifactHash() (string, error) // hex-encoded artifact hash prefixed with hash name, e.g. sha256:abcdef + Insertable() (bool, error) // denotes whether the entry that was unmarshalled has the writeOnly fields required to validate and insert into the log } // EntryWithAttestationImpl specifies the behavior of a versioned type that also stores attestations diff --git a/pkg/types/hashedrekord/hashedrekord_test.go b/pkg/types/hashedrekord/hashedrekord_test.go index aeb2975aa..e75ab2288 100644 --- a/pkg/types/hashedrekord/hashedrekord_test.go +++ b/pkg/types/hashedrekord/hashedrekord_test.go @@ -47,6 +47,10 @@ func (u UnmarshalFailsTester) Verifiers() ([]pki.PublicKey, error) { return nil, nil } +func (u UnmarshalFailsTester) ArtifactHash() (string, error) { + return "", nil +} + func TestRekordType(t *testing.T) { // empty to start if VersionMap.Count() != 0 { diff --git a/pkg/types/hashedrekord/v0.0.1/entry.go b/pkg/types/hashedrekord/v0.0.1/entry.go index 5fdef0286..5f0cb138a 100644 --- a/pkg/types/hashedrekord/v0.0.1/entry.go +++ b/pkg/types/hashedrekord/v0.0.1/entry.go @@ -257,6 +257,13 @@ func (v V001Entry) Verifiers() ([]pki.PublicKey, error) { return []pki.PublicKey{key}, nil } +func (v V001Entry) ArtifactHash() (string, error) { + if v.HashedRekordObj.Data == nil || v.HashedRekordObj.Data.Hash == nil || v.HashedRekordObj.Data.Hash.Value == nil || v.HashedRekordObj.Data.Hash.Algorithm == nil { + return "", errors.New("hashedrekord v0.0.1 entry not initialized") + } + return strings.ToLower(fmt.Sprintf("%s:%s", *v.HashedRekordObj.Data.Hash.Algorithm, *v.HashedRekordObj.Data.Hash.Value)), nil +} + func (v V001Entry) Insertable() (bool, error) { if v.HashedRekordObj.Signature == nil { return false, errors.New("missing signature property") diff --git a/pkg/types/hashedrekord/v0.0.1/entry_test.go b/pkg/types/hashedrekord/v0.0.1/entry_test.go index 6c9fb2f31..343712dc8 100644 --- a/pkg/types/hashedrekord/v0.0.1/entry_test.go +++ b/pkg/types/hashedrekord/v0.0.1/entry_test.go @@ -289,6 +289,13 @@ func TestCrossFieldValidation(t *testing.T) { if !ok || err != nil { t.Errorf("unexpected failure in testing insertable on valid entry: %v", err) } + + hash, err := v.ArtifactHash() + if err != nil { + t.Errorf("unexpected failure with ArtifactHash: %v", err) + } else if hash != "sha256:"+dataSHA { + t.Errorf("unexpected match with ArtifactHash: %s", hash) + } } b, err := v.Canonicalize(context.TODO()) @@ -313,6 +320,12 @@ func TestCrossFieldValidation(t *testing.T) { if !ok || err != nil { t.Errorf("unexpected failure in testing insertable on entry created from canonicalized content: %v", err) } + hash, err := ei.ArtifactHash() + if err != nil { + t.Errorf("unexpected failure with ArtifactHash: %v", err) + } else if hash != "sha256:"+dataSHA { + t.Errorf("unexpected match with ArtifactHash: %s", hash) + } } verifiers, err := v.Verifiers() diff --git a/pkg/types/helm/v0.0.1/entry.go b/pkg/types/helm/v0.0.1/entry.go index 9100a7d98..80e4395ae 100644 --- a/pkg/types/helm/v0.0.1/entry.go +++ b/pkg/types/helm/v0.0.1/entry.go @@ -360,6 +360,13 @@ func (v V001Entry) Verifiers() ([]pki.PublicKey, error) { return []pki.PublicKey{key}, nil } +func (v V001Entry) ArtifactHash() (string, error) { + if v.HelmObj.Chart == nil || v.HelmObj.Chart.Hash == nil || v.HelmObj.Chart.Hash.Algorithm == nil || v.HelmObj.Chart.Hash.Value == nil { + return "", errors.New("helm v0.0.1 entry not initialized") + } + return strings.ToLower(fmt.Sprintf("%s:%s", *v.HelmObj.Chart.Hash.Algorithm, *v.HelmObj.Chart.Hash.Value)), nil +} + func (v V001Entry) Insertable() (bool, error) { if v.HelmObj.PublicKey == nil { return false, errors.New("missing public key property") diff --git a/pkg/types/helm/v0.0.1/entry_test.go b/pkg/types/helm/v0.0.1/entry_test.go index d9f79eb98..510e1bd1a 100644 --- a/pkg/types/helm/v0.0.1/entry_test.go +++ b/pkg/types/helm/v0.0.1/entry_test.go @@ -209,6 +209,12 @@ func TestCrossFieldValidation(t *testing.T) { if ok, err := ei.Insertable(); ok || err == nil { t.Errorf("unexpected success calling Insertable on entry created from canonicalized content") } + hash, err := ei.ArtifactHash() + if err != nil { + t.Errorf("unexpected failure with ArtifactHash: %v", err) + } else if hash != "sha256:6dec7ea21e655d5796c1e214cfb75b73428b2abfa2e66c8f7bc64ff4a7b3b29f" { + t.Errorf("unexpected match with ArtifactHash: %s", hash) + } } verifiers, err := v.Verifiers() diff --git a/pkg/types/intoto/v0.0.1/entry.go b/pkg/types/intoto/v0.0.1/entry.go index ceb7371d5..e44c76717 100644 --- a/pkg/types/intoto/v0.0.1/entry.go +++ b/pkg/types/intoto/v0.0.1/entry.go @@ -364,6 +364,13 @@ func (v V001Entry) Verifiers() ([]pki.PublicKey, error) { return []pki.PublicKey{key}, nil } +func (v V001Entry) ArtifactHash() (string, error) { + if v.IntotoObj.Content == nil || v.IntotoObj.Content.PayloadHash == nil || v.IntotoObj.Content.PayloadHash.Algorithm == nil || v.IntotoObj.Content.PayloadHash.Value == nil { + return "", errors.New("hashedrekord v0.0.1 entry not initialized") + } + return strings.ToLower(fmt.Sprintf("%s:%s", *v.IntotoObj.Content.PayloadHash.Algorithm, *v.IntotoObj.Content.PayloadHash.Value)), nil +} + func (v V001Entry) Insertable() (bool, error) { if v.IntotoObj.Content == nil { return false, errors.New("missing content property") diff --git a/pkg/types/intoto/v0.0.1/entry_test.go b/pkg/types/intoto/v0.0.1/entry_test.go index 23eb54939..3fb61345f 100644 --- a/pkg/types/intoto/v0.0.1/entry_test.go +++ b/pkg/types/intoto/v0.0.1/entry_test.go @@ -336,6 +336,14 @@ func TestV001Entry_Unmarshal(t *testing.T) { t.Errorf("index keys from hydrated object do not match those generated from canonicalized (and re-hydrated) object: %v %v", got, canonicalIndexKeys) } + hash, err := canonicalV001.ArtifactHash() + expectedHash := sha256.Sum256([]byte(validPayload)) + if err != nil { + t.Errorf("unexpected failure with ArtifactHash: %v", err) + } else if hash != "sha256:"+hex.EncodeToString(expectedHash[:]) { + t.Errorf("unexpected match with ArtifactHash: %s", hash) + } + verifiers, err := v.Verifiers() if !tt.wantVerifierErr { if err != nil { diff --git a/pkg/types/intoto/v0.0.2/entry.go b/pkg/types/intoto/v0.0.2/entry.go index 69a9b4c14..0ae4a9d44 100644 --- a/pkg/types/intoto/v0.0.2/entry.go +++ b/pkg/types/intoto/v0.0.2/entry.go @@ -478,6 +478,13 @@ func (v V002Entry) Verifiers() ([]pki.PublicKey, error) { return keys, nil } +func (v V002Entry) ArtifactHash() (string, error) { + if v.IntotoObj.Content == nil || v.IntotoObj.Content.PayloadHash == nil || v.IntotoObj.Content.PayloadHash.Algorithm == nil || v.IntotoObj.Content.PayloadHash.Value == nil { + return "", errors.New("intoto v0.0.2 entry not initialized") + } + return strings.ToLower(fmt.Sprintf("%s:%s", *v.IntotoObj.Content.PayloadHash.Algorithm, *v.IntotoObj.Content.PayloadHash.Value)), nil +} + func (v V002Entry) Insertable() (bool, error) { if v.IntotoObj.Content == nil { return false, errors.New("missing content property") diff --git a/pkg/types/intoto/v0.0.2/entry_test.go b/pkg/types/intoto/v0.0.2/entry_test.go index cde69708e..f26518c6e 100644 --- a/pkg/types/intoto/v0.0.2/entry_test.go +++ b/pkg/types/intoto/v0.0.2/entry_test.go @@ -377,6 +377,14 @@ func TestV002Entry_Unmarshal(t *testing.T) { t.Errorf("index keys from hydrated object do not match those generated from canonicalized (and re-hydrated) object: %v %v", got, canonicalIndexKeys) } + hash, err := canonicalV002.ArtifactHash() + expectedHash := sha256.Sum256([]byte(validPayload)) + if err != nil { + t.Errorf("unexpected failure with ArtifactHash: %v", err) + } else if hash != "sha256:"+hex.EncodeToString(expectedHash[:]) { + t.Errorf("unexpected match with ArtifactHash: %s", hash) + } + verifiers, err := v.Verifiers() if !tt.wantVerifierErr { if err != nil { diff --git a/pkg/types/jar/v0.0.1/entry.go b/pkg/types/jar/v0.0.1/entry.go index 01fc6aced..ec25ad5bc 100644 --- a/pkg/types/jar/v0.0.1/entry.go +++ b/pkg/types/jar/v0.0.1/entry.go @@ -349,6 +349,13 @@ func (v V001Entry) Verifiers() ([]pki.PublicKey, error) { return []pki.PublicKey{key}, nil } +func (v V001Entry) ArtifactHash() (string, error) { + if v.JARModel.Archive == nil || v.JARModel.Archive.Hash == nil || v.JARModel.Archive.Hash.Value == nil || v.JARModel.Archive.Hash.Algorithm == nil { + return "", errors.New("jar v0.0.1 entry not initialized") + } + return strings.ToLower(fmt.Sprintf("%s:%s", *v.JARModel.Archive.Hash.Algorithm, *v.JARModel.Archive.Hash.Value)), nil +} + func (v V001Entry) Insertable() (bool, error) { if v.JARModel.Archive == nil { return false, errors.New("missing archive property") diff --git a/pkg/types/jar/v0.0.1/entry_test.go b/pkg/types/jar/v0.0.1/entry_test.go index 9a096b3a2..ad857ab0d 100644 --- a/pkg/types/jar/v0.0.1/entry_test.go +++ b/pkg/types/jar/v0.0.1/entry_test.go @@ -140,6 +140,12 @@ Hr/+CxFvaJWmpYqNkLDGRU+9orzh5hI2RrcuaQ== if ok, err := ei.Insertable(); ok || err == nil { t.Errorf("unexpected err from calling Insertable on entry created from canonicalized content") } + hash, err := ei.ArtifactHash() + if err != nil { + t.Errorf("unexpected failure with ArtifactHash: %v", err) + } else if hash != "sha256:4a9d0ab4e10597497ed6c4617c983c35fa9e964e75cdd6f9fae3a0da1929acc6" { + t.Errorf("unexpected match with ArtifactHash: %s", hash) + } } verifiers, err := v.Verifiers() diff --git a/pkg/types/rekord/v0.0.1/entry.go b/pkg/types/rekord/v0.0.1/entry.go index da0af234e..619f19f8e 100644 --- a/pkg/types/rekord/v0.0.1/entry.go +++ b/pkg/types/rekord/v0.0.1/entry.go @@ -450,6 +450,13 @@ func (v V001Entry) Verifiers() ([]pki.PublicKey, error) { return []pki.PublicKey{key}, nil } +func (v V001Entry) ArtifactHash() (string, error) { + if v.RekordObj.Data == nil || v.RekordObj.Data.Hash == nil || v.RekordObj.Data.Hash.Value == nil || v.RekordObj.Data.Hash.Algorithm == nil { + return "", errors.New("rekord v0.0.1 entry not initialized") + } + return strings.ToLower(fmt.Sprintf("%s:%s", *v.RekordObj.Data.Hash.Algorithm, *v.RekordObj.Data.Hash.Value)), nil +} + func (v V001Entry) Insertable() (bool, error) { if v.RekordObj.Signature == nil { return false, errors.New("missing signature property") diff --git a/pkg/types/rekord/v0.0.1/entry_test.go b/pkg/types/rekord/v0.0.1/entry_test.go index e236a50f2..7c2dee246 100644 --- a/pkg/types/rekord/v0.0.1/entry_test.go +++ b/pkg/types/rekord/v0.0.1/entry_test.go @@ -18,6 +18,8 @@ package rekord import ( "bytes" "context" + "crypto/sha256" + "encoding/hex" "os" "reflect" "testing" @@ -256,6 +258,13 @@ func TestCrossFieldValidation(t *testing.T) { if ok, err := ei.Insertable(); ok || err == nil { t.Errorf("unexpected success calling Insertable on entry created from canonicalized content") } + hash, err := ei.ArtifactHash() + expectedHash := sha256.Sum256(dataBytes) + if err != nil { + t.Errorf("unexpected failure with ArtifactHash: %v", err) + } else if hash != "sha256:"+hex.EncodeToString(expectedHash[:]) { + t.Errorf("unexpected match with ArtifactHash: %s", hash) + } } verifiers, err := v.Verifiers() diff --git a/pkg/types/rfc3161/v0.0.1/entry.go b/pkg/types/rfc3161/v0.0.1/entry.go index 9a6c04949..dfac12b1d 100644 --- a/pkg/types/rfc3161/v0.0.1/entry.go +++ b/pkg/types/rfc3161/v0.0.1/entry.go @@ -26,6 +26,7 @@ import ( "fmt" "os" "path/filepath" + "strings" "github.com/sigstore/rekor/pkg/pki" "github.com/sigstore/rekor/pkg/types/rfc3161" @@ -211,6 +212,18 @@ func (v V001Entry) Verifiers() ([]pki.PublicKey, error) { return nil, errors.New("Verifiers() does not support rfc3161 entry type") } +func (v V001Entry) ArtifactHash() (string, error) { + if v.Rfc3161Obj.Tsr == nil || v.Rfc3161Obj.Tsr.Content == nil { + return "", errors.New("rfc3161 v0.0.1 entry not initialized") + } + tsrDecoded, err := base64.StdEncoding.DecodeString(v.Rfc3161Obj.Tsr.Content.String()) + if err != nil { + return "", err + } + h := sha256.Sum256(tsrDecoded) + return strings.ToLower(fmt.Sprintf("sha256:%s", hex.EncodeToString(h[:]))), nil +} + func (v V001Entry) Insertable() (bool, error) { if v.Rfc3161Obj.Tsr == nil { return false, errors.New("missing tsr property") diff --git a/pkg/types/rfc3161/v0.0.1/entry_test.go b/pkg/types/rfc3161/v0.0.1/entry_test.go index 6c5457455..b115accb2 100644 --- a/pkg/types/rfc3161/v0.0.1/entry_test.go +++ b/pkg/types/rfc3161/v0.0.1/entry_test.go @@ -197,6 +197,12 @@ func TestCrossFieldValidation(t *testing.T) { if ok, err := ei.Insertable(); !ok || err != nil { t.Errorf("unexpected error calling insertable on entry created from canonicalized content") } + hash, err := ei.ArtifactHash() + if err != nil { + t.Errorf("unexpected failure with ArtifactHash: %v", err) + } else if hash != "sha256:6dbda791c778454d0baa96e1dec5fa2c50867fd22d119ebe75c60c513b254c91" { + t.Errorf("unexpected match with ArtifactHash: %s", hash) + } } } } diff --git a/pkg/types/rpm/v0.0.1/entry.go b/pkg/types/rpm/v0.0.1/entry.go index fd3b91ce7..d83527e09 100644 --- a/pkg/types/rpm/v0.0.1/entry.go +++ b/pkg/types/rpm/v0.0.1/entry.go @@ -382,6 +382,13 @@ func (v V001Entry) Verifiers() ([]pki.PublicKey, error) { return []pki.PublicKey{key}, nil } +func (v V001Entry) ArtifactHash() (string, error) { + if v.RPMModel.Package == nil || v.RPMModel.Package.Hash == nil || v.RPMModel.Package.Hash.Value == nil || v.RPMModel.Package.Hash.Algorithm == nil { + return "", errors.New("rpm v0.0.1 entry not initialized") + } + return strings.ToLower(fmt.Sprintf("%s:%s", *v.RPMModel.Package.Hash.Algorithm, *v.RPMModel.Package.Hash.Value)), nil +} + func (v V001Entry) Insertable() (bool, error) { if v.RPMModel.PublicKey == nil { return false, errors.New("missing publicKey property") diff --git a/pkg/types/rpm/v0.0.1/entry_test.go b/pkg/types/rpm/v0.0.1/entry_test.go index 9b1ec7bd0..539c21d36 100644 --- a/pkg/types/rpm/v0.0.1/entry_test.go +++ b/pkg/types/rpm/v0.0.1/entry_test.go @@ -194,6 +194,12 @@ func TestCrossFieldValidation(t *testing.T) { if ok, err := ei.Insertable(); ok || err == nil { t.Errorf("unexpected success calling Insertable on entry created from canonicalized content") } + hash, err := ei.ArtifactHash() + if err != nil { + t.Errorf("unexpected failure with ArtifactHash: %v", err) + } else if hash != "sha256:c8b0bc59708d74f53aab0089ac587d5c348d6ead143dab9f6d9c4b48c973bfd8" { + t.Errorf("unexpected match with ArtifactHash: %s", hash) + } } verifiers, err := v.Verifiers() diff --git a/pkg/types/test_util.go b/pkg/types/test_util.go index 529d71b79..0fada89a2 100644 --- a/pkg/types/test_util.go +++ b/pkg/types/test_util.go @@ -31,6 +31,10 @@ func (u BaseUnmarshalTester) NewEntry() EntryImpl { return &BaseUnmarshalTester{} } +func (u BaseUnmarshalTester) ArtifactHash() (string, error) { + return "", nil +} + func (u BaseUnmarshalTester) Verifiers() ([]pki.PublicKey, error) { return nil, nil } diff --git a/pkg/types/tuf/v0.0.1/entry.go b/pkg/types/tuf/v0.0.1/entry.go index be24fd6ed..608162eb4 100644 --- a/pkg/types/tuf/v0.0.1/entry.go +++ b/pkg/types/tuf/v0.0.1/entry.go @@ -385,6 +385,26 @@ func (v V001Entry) Verifiers() ([]pki.PublicKey, error) { return []pki.PublicKey{key}, nil } +func (v V001Entry) ArtifactHash() (string, error) { + if v.TufObj.Metadata == nil || v.TufObj.Metadata.Content == nil { + return "", errors.New("tuf v0.0.1 entry not initialized") + } + sigBytes, err := v.parseMetadataContent() + if err != nil { + return "", err + } + sig, err := ptuf.NewSignature(bytes.NewReader(sigBytes)) + if err != nil { + return "", err + } + metadata, err := sig.CanonicalValue() + if err != nil { + return "", err + } + metadataHash := sha256.Sum256(metadata) + return strings.ToLower(fmt.Sprintf("sha256:%s", hex.EncodeToString(metadataHash[:]))), nil +} + func (v V001Entry) Insertable() (bool, error) { if v.TufObj.Metadata == nil { return false, errors.New("missing metadata property") diff --git a/pkg/types/tuf/v0.0.1/entry_test.go b/pkg/types/tuf/v0.0.1/entry_test.go index 174bb8c69..92ca46aaa 100644 --- a/pkg/types/tuf/v0.0.1/entry_test.go +++ b/pkg/types/tuf/v0.0.1/entry_test.go @@ -239,10 +239,17 @@ func TestCrossFieldValidation(t *testing.T) { if err != nil { t.Errorf("unexpected err from Unmarshalling canonicalized entry for '%v': %v", tc.caseDesc, err) } - if _, err := types.UnmarshalEntry(pe); err != nil { + ei, err := types.UnmarshalEntry(pe) + if err != nil { t.Errorf("unexpected err from type-specific unmarshalling for '%v': %v", tc.caseDesc, err) } // Insertable on canonicalized content is variable so we skip testing it here + hash, err := ei.ArtifactHash() + if err != nil { + t.Errorf("unexpected failure with ArtifactHash: %v", err) + } else if hash != "sha256:c170ae288c93f56031639bac1ad085fc47918346f733b3d76b07a8124ebd24f9" { + t.Errorf("unexpected match with ArtifactHash: %s", hash) + } } verifiers, err := v.Verifiers()