Skip to content

Commit

Permalink
Add digest to targets metadata directly (#170)
Browse files Browse the repository at this point in the history
* 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]>
  • Loading branch information
mnm678 and ethan-lowman-dd authored Jan 19, 2022
1 parent eac0a85 commit b072577
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 1 deletion.
24 changes: 24 additions & 0 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package client

import (
"bytes"
"encoding/hex"
"encoding/json"
"io"
"io/ioutil"
Expand Down Expand Up @@ -841,6 +842,29 @@ func (c *Client) Download(name string, dest Destination) (err error) {
return nil
}

func (c *Client) VerifyDigest(digest string, digestAlg string, length int64, path string) error {
localMeta, ok := c.targets[path]
if !ok {
return ErrUnknownTarget{Name: path, SnapshotVersion: c.snapshotVer}
}

actual := data.FileMeta{Length: length, Hashes: make(data.Hashes, 1)}
var err error
actual.Hashes[digestAlg], err = hex.DecodeString(digest)
if err != nil {
return err
}

if err := util.TargetFileMetaEqual(data.TargetFileMeta{FileMeta: actual}, localMeta); err != nil {
if e, ok := err.(util.ErrWrongLength); ok {
return ErrWrongSize{path, e.Actual, e.Expected}
}
return ErrDownloadFailed{path, err}
}

return nil
}

// Target returns the target metadata for a specific target if it
// exists, searching from top-level level targets then through
// all delegations. If it does not, ErrNotFound will be returned.
Expand Down
18 changes: 18 additions & 0 deletions client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1178,3 +1178,21 @@ func generateRepoFS(c *C, dir string, files map[string][]byte, consistentSnapsho
c.Assert(repo.Commit(), IsNil)
return repo
}

func (s *ClientSuite) TestVerifyDigest(c *C) {
digest := "sha256:bc11b176a293bb341a0f2d0d226f52e7fcebd186a7c4dfca5fc64f305f06b94c"
hash := "bc11b176a293bb341a0f2d0d226f52e7fcebd186a7c4dfca5fc64f305f06b94c"
size := int64(42)

c.Assert(s.repo.AddTargetsWithDigest(hash, "sha256", size, digest, nil), IsNil)
c.Assert(s.repo.Snapshot(), IsNil)
c.Assert(s.repo.Timestamp(), IsNil)
c.Assert(s.repo.Commit(), IsNil)
s.syncRemote(c)

client := s.newClient(c)
_, err := client.Update()
c.Assert(err, IsNil)

c.Assert(client.VerifyDigest(hash, "sha256", size, digest), IsNil)
}
35 changes: 34 additions & 1 deletion repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package tuf

import (
"bytes"
"encoding/hex"
"encoding/json"
"fmt"
"io"
Expand Down Expand Up @@ -703,6 +704,34 @@ func (r *Repo) AddTargets(paths []string, custom json.RawMessage) error {
return r.AddTargetsWithExpires(paths, custom, data.DefaultExpires("targets"))
}

func (r *Repo) AddTargetsWithDigest(digest string, digestAlg string, length int64, path string, custom json.RawMessage) error {
expires := data.DefaultExpires("targets")

// TODO: support delegated targets
t, err := r.topLevelTargets()
if err != nil {
return err
}

meta := data.FileMeta{Length: length, Hashes: make(data.Hashes, 1)}
meta.Hashes[digestAlg], err = hex.DecodeString(digest)
if err != nil {
return err
}

// If custom is provided, set custom, otherwise maintain existing custom
// metadata
if len(custom) > 0 {
meta.Custom = &custom
} else if t, ok := t.Targets[path]; ok {
meta.Custom = t.Custom
}

t.Targets[path] = data.TargetFileMeta{FileMeta: meta}

return r.writeTargetWithExpires(t, expires)
}

func (r *Repo) AddTargetWithExpires(path string, custom json.RawMessage, expires time.Time) error {
return r.AddTargetsWithExpires([]string{path}, custom, expires)
}
Expand Down Expand Up @@ -742,12 +771,16 @@ func (r *Repo) AddTargetsWithExpires(paths []string, custom json.RawMessage, exp
}); err != nil {
return err
}
return r.writeTargetWithExpires(t, expires)
}

func (r *Repo) writeTargetWithExpires(t *data.Targets, expires time.Time) error {
t.Expires = expires.Round(time.Second)
if !r.local.FileIsStaged("targets.json") {
t.Version++
}

err = r.setTopLevelMeta("targets.json", t)
err := r.setTopLevelMeta("targets.json", t)
if err == nil {
fmt.Println("Added/staged targets:")
for k := range t.Targets {
Expand Down
32 changes: 32 additions & 0 deletions repo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package tuf
import (
"crypto"
"crypto/rand"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
Expand Down Expand Up @@ -1795,3 +1796,34 @@ func (rs *RepoSuite) TestBadAddOrUpdateSignatures(c *C) {
}
checkSigIDs("root.json")
}

func (rs *RepoSuite) TestSignDigest(c *C) {
files := map[string][]byte{"foo.txt": []byte("foo")}
local := MemoryStore(make(map[string]json.RawMessage), files)
r, err := NewRepo(local)
c.Assert(err, IsNil)

genKey(c, r, "root")
genKey(c, r, "targets")
genKey(c, r, "snapshot")
genKey(c, r, "timestamp")

digest := "sha256:bc11b176a293bb341a0f2d0d226f52e7fcebd186a7c4dfca5fc64f305f06b94c"
hash := "bc11b176a293bb341a0f2d0d226f52e7fcebd186a7c4dfca5fc64f305f06b94c"
size := int64(42)

c.Assert(r.AddTargetsWithDigest(hash, "sha256", size, digest, nil), IsNil)
c.Assert(r.Snapshot(), IsNil)
c.Assert(r.Timestamp(), IsNil)
c.Assert(r.Commit(), IsNil)

digest_bytes, err := hex.DecodeString("bc11b176a293bb341a0f2d0d226f52e7fcebd186a7c4dfca5fc64f305f06b94c")
hex_digest_bytes := data.HexBytes(digest_bytes)
c.Assert(err, IsNil)

targets, err := r.topLevelTargets()
c.Assert(err, IsNil)
c.Assert(targets.Targets["sha256:bc11b176a293bb341a0f2d0d226f52e7fcebd186a7c4dfca5fc64f305f06b94c"].FileMeta.Length, Equals, size)
c.Assert(targets.Targets["sha256:bc11b176a293bb341a0f2d0d226f52e7fcebd186a7c4dfca5fc64f305f06b94c"].FileMeta.Hashes["sha256"], DeepEquals, hex_digest_bytes)

}

0 comments on commit b072577

Please sign in to comment.