Skip to content

Commit b072577

Browse files
Add digest to targets metadata directly (#170)
* Add digest to targets metadata directly This commit allows users of go-tuf to sign oci images or other non-local targets by directly providing the hash and length of these artifacts. Signed-off-by: Marina Moore <[email protected]> * dynamically determine hash algorithm in AddDigestTarget Signed-off-by: Marina Moore <[email protected]> * Add verification of digest signatures Signed-off-by: Marina Moore <[email protected]> * Update digest delegation based on pr feedback Signed-off-by: Marina Moore <[email protected]> * fix formatting Signed-off-by: Marina Moore <[email protected]> * add client test for VerifyDigest Signed-off-by: Marina Moore <[email protected]> * allow verifydigest to have a path Signed-off-by: Marina Moore <[email protected]> * Update repo.go Co-authored-by: Ethan Lowman <[email protected]> * Add and verify non-oci digests Signed-off-by: Marina Moore <[email protected]> * fix test Signed-off-by: Marina Moore <[email protected]> * fix go vet errors Signed-off-by: Marina Moore <[email protected]> * AddDigestTargets -> AddTargetsWithDigest Signed-off-by: Marina Moore <[email protected]> * AddDigestTargets -> AddTargetsWithDigest Signed-off-by: Marina Moore <[email protected]> * fix go vet Signed-off-by: Marina Moore <[email protected]> * fix static check Signed-off-by: Marina Moore <[email protected]> * update function signatures for VerifyDigest, AddTargetsWithDigest Signed-off-by: Marina Moore <[email protected]> * use topLevelTargets() instead of targets() Signed-off-by: Marina Moore <[email protected]> * bug fix Signed-off-by: Marina Moore <[email protected]> Co-authored-by: Ethan Lowman <[email protected]>
1 parent eac0a85 commit b072577

File tree

4 files changed

+108
-1
lines changed

4 files changed

+108
-1
lines changed

client/client.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package client
22

33
import (
44
"bytes"
5+
"encoding/hex"
56
"encoding/json"
67
"io"
78
"io/ioutil"
@@ -841,6 +842,29 @@ func (c *Client) Download(name string, dest Destination) (err error) {
841842
return nil
842843
}
843844

845+
func (c *Client) VerifyDigest(digest string, digestAlg string, length int64, path string) error {
846+
localMeta, ok := c.targets[path]
847+
if !ok {
848+
return ErrUnknownTarget{Name: path, SnapshotVersion: c.snapshotVer}
849+
}
850+
851+
actual := data.FileMeta{Length: length, Hashes: make(data.Hashes, 1)}
852+
var err error
853+
actual.Hashes[digestAlg], err = hex.DecodeString(digest)
854+
if err != nil {
855+
return err
856+
}
857+
858+
if err := util.TargetFileMetaEqual(data.TargetFileMeta{FileMeta: actual}, localMeta); err != nil {
859+
if e, ok := err.(util.ErrWrongLength); ok {
860+
return ErrWrongSize{path, e.Actual, e.Expected}
861+
}
862+
return ErrDownloadFailed{path, err}
863+
}
864+
865+
return nil
866+
}
867+
844868
// Target returns the target metadata for a specific target if it
845869
// exists, searching from top-level level targets then through
846870
// all delegations. If it does not, ErrNotFound will be returned.

client/client_test.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1178,3 +1178,21 @@ func generateRepoFS(c *C, dir string, files map[string][]byte, consistentSnapsho
11781178
c.Assert(repo.Commit(), IsNil)
11791179
return repo
11801180
}
1181+
1182+
func (s *ClientSuite) TestVerifyDigest(c *C) {
1183+
digest := "sha256:bc11b176a293bb341a0f2d0d226f52e7fcebd186a7c4dfca5fc64f305f06b94c"
1184+
hash := "bc11b176a293bb341a0f2d0d226f52e7fcebd186a7c4dfca5fc64f305f06b94c"
1185+
size := int64(42)
1186+
1187+
c.Assert(s.repo.AddTargetsWithDigest(hash, "sha256", size, digest, nil), IsNil)
1188+
c.Assert(s.repo.Snapshot(), IsNil)
1189+
c.Assert(s.repo.Timestamp(), IsNil)
1190+
c.Assert(s.repo.Commit(), IsNil)
1191+
s.syncRemote(c)
1192+
1193+
client := s.newClient(c)
1194+
_, err := client.Update()
1195+
c.Assert(err, IsNil)
1196+
1197+
c.Assert(client.VerifyDigest(hash, "sha256", size, digest), IsNil)
1198+
}

repo.go

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package tuf
22

33
import (
44
"bytes"
5+
"encoding/hex"
56
"encoding/json"
67
"fmt"
78
"io"
@@ -703,6 +704,34 @@ func (r *Repo) AddTargets(paths []string, custom json.RawMessage) error {
703704
return r.AddTargetsWithExpires(paths, custom, data.DefaultExpires("targets"))
704705
}
705706

707+
func (r *Repo) AddTargetsWithDigest(digest string, digestAlg string, length int64, path string, custom json.RawMessage) error {
708+
expires := data.DefaultExpires("targets")
709+
710+
// TODO: support delegated targets
711+
t, err := r.topLevelTargets()
712+
if err != nil {
713+
return err
714+
}
715+
716+
meta := data.FileMeta{Length: length, Hashes: make(data.Hashes, 1)}
717+
meta.Hashes[digestAlg], err = hex.DecodeString(digest)
718+
if err != nil {
719+
return err
720+
}
721+
722+
// If custom is provided, set custom, otherwise maintain existing custom
723+
// metadata
724+
if len(custom) > 0 {
725+
meta.Custom = &custom
726+
} else if t, ok := t.Targets[path]; ok {
727+
meta.Custom = t.Custom
728+
}
729+
730+
t.Targets[path] = data.TargetFileMeta{FileMeta: meta}
731+
732+
return r.writeTargetWithExpires(t, expires)
733+
}
734+
706735
func (r *Repo) AddTargetWithExpires(path string, custom json.RawMessage, expires time.Time) error {
707736
return r.AddTargetsWithExpires([]string{path}, custom, expires)
708737
}
@@ -742,12 +771,16 @@ func (r *Repo) AddTargetsWithExpires(paths []string, custom json.RawMessage, exp
742771
}); err != nil {
743772
return err
744773
}
774+
return r.writeTargetWithExpires(t, expires)
775+
}
776+
777+
func (r *Repo) writeTargetWithExpires(t *data.Targets, expires time.Time) error {
745778
t.Expires = expires.Round(time.Second)
746779
if !r.local.FileIsStaged("targets.json") {
747780
t.Version++
748781
}
749782

750-
err = r.setTopLevelMeta("targets.json", t)
783+
err := r.setTopLevelMeta("targets.json", t)
751784
if err == nil {
752785
fmt.Println("Added/staged targets:")
753786
for k := range t.Targets {

repo_test.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package tuf
33
import (
44
"crypto"
55
"crypto/rand"
6+
"encoding/hex"
67
"encoding/json"
78
"errors"
89
"fmt"
@@ -1795,3 +1796,34 @@ func (rs *RepoSuite) TestBadAddOrUpdateSignatures(c *C) {
17951796
}
17961797
checkSigIDs("root.json")
17971798
}
1799+
1800+
func (rs *RepoSuite) TestSignDigest(c *C) {
1801+
files := map[string][]byte{"foo.txt": []byte("foo")}
1802+
local := MemoryStore(make(map[string]json.RawMessage), files)
1803+
r, err := NewRepo(local)
1804+
c.Assert(err, IsNil)
1805+
1806+
genKey(c, r, "root")
1807+
genKey(c, r, "targets")
1808+
genKey(c, r, "snapshot")
1809+
genKey(c, r, "timestamp")
1810+
1811+
digest := "sha256:bc11b176a293bb341a0f2d0d226f52e7fcebd186a7c4dfca5fc64f305f06b94c"
1812+
hash := "bc11b176a293bb341a0f2d0d226f52e7fcebd186a7c4dfca5fc64f305f06b94c"
1813+
size := int64(42)
1814+
1815+
c.Assert(r.AddTargetsWithDigest(hash, "sha256", size, digest, nil), IsNil)
1816+
c.Assert(r.Snapshot(), IsNil)
1817+
c.Assert(r.Timestamp(), IsNil)
1818+
c.Assert(r.Commit(), IsNil)
1819+
1820+
digest_bytes, err := hex.DecodeString("bc11b176a293bb341a0f2d0d226f52e7fcebd186a7c4dfca5fc64f305f06b94c")
1821+
hex_digest_bytes := data.HexBytes(digest_bytes)
1822+
c.Assert(err, IsNil)
1823+
1824+
targets, err := r.topLevelTargets()
1825+
c.Assert(err, IsNil)
1826+
c.Assert(targets.Targets["sha256:bc11b176a293bb341a0f2d0d226f52e7fcebd186a7c4dfca5fc64f305f06b94c"].FileMeta.Length, Equals, size)
1827+
c.Assert(targets.Targets["sha256:bc11b176a293bb341a0f2d0d226f52e7fcebd186a7c4dfca5fc64f305f06b94c"].FileMeta.Hashes["sha256"], DeepEquals, hex_digest_bytes)
1828+
1829+
}

0 commit comments

Comments
 (0)