...

Source file src/github.com/theupdateframework/go-tuf/client/client_test.go

Documentation: github.com/theupdateframework/go-tuf/client

     1  package client
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/ed25519"
     6  	"crypto/sha256"
     7  	"encoding/hex"
     8  	"encoding/json"
     9  	"errors"
    10  	"fmt"
    11  	"io"
    12  	"net"
    13  	"net/http"
    14  	"os"
    15  	"path/filepath"
    16  	"testing"
    17  	"time"
    18  
    19  	"github.com/secure-systems-lab/go-securesystemslib/cjson"
    20  	"github.com/stretchr/testify/assert"
    21  	tuf "github.com/theupdateframework/go-tuf"
    22  	"github.com/theupdateframework/go-tuf/data"
    23  	"github.com/theupdateframework/go-tuf/internal/sets"
    24  	"github.com/theupdateframework/go-tuf/pkg/keys"
    25  	"github.com/theupdateframework/go-tuf/sign"
    26  	"github.com/theupdateframework/go-tuf/util"
    27  	"github.com/theupdateframework/go-tuf/verify"
    28  	. "gopkg.in/check.v1"
    29  )
    30  
    31  // Hook up gocheck into the "go test" runner.
    32  func Test(t *testing.T) { TestingT(t) }
    33  
    34  type ClientSuite struct {
    35  	store        tuf.LocalStore
    36  	repo         *tuf.Repo
    37  	local        LocalStore
    38  	remote       RemoteStore
    39  	expiredTime  time.Time
    40  	keyIDs       map[string][]string
    41  	useFileStore bool
    42  	// Only used with FileStore
    43  	tmpDir string
    44  }
    45  
    46  var _ = Suite(&ClientSuite{useFileStore: false})
    47  var _ = Suite(&ClientSuite{useFileStore: true})
    48  
    49  func newFakeRemoteStore() *fakeRemoteStore {
    50  	return &fakeRemoteStore{
    51  		meta:    make(map[string]*fakeFile),
    52  		targets: make(map[string]*fakeFile),
    53  	}
    54  }
    55  
    56  type fakeRemoteStore struct {
    57  	meta    map[string]*fakeFile
    58  	targets map[string]*fakeFile
    59  }
    60  
    61  func (f *fakeRemoteStore) GetMeta(name string) (io.ReadCloser, int64, error) {
    62  	return f.get(name, f.meta)
    63  }
    64  
    65  func (f *fakeRemoteStore) GetTarget(path string) (io.ReadCloser, int64, error) {
    66  	return f.get(path, f.targets)
    67  }
    68  
    69  func (f *fakeRemoteStore) get(name string, store map[string]*fakeFile) (io.ReadCloser, int64, error) {
    70  	file, ok := store[name]
    71  	if !ok {
    72  		return nil, 0, ErrNotFound{name}
    73  	}
    74  	return file, file.size, nil
    75  }
    76  
    77  // These are helper methods for manipulating the internals of the Stores
    78  // because the set/delete methods are not part of the Interface, we need to
    79  // switch on the underlying implementation.
    80  // Also readMeta method is convenience for ease of testing.
    81  func (s *ClientSuite) setRemoteMeta(path string, data []byte) error {
    82  	switch impl := s.remote.(type) {
    83  	case *fakeRemoteStore:
    84  		impl.meta[path] = newFakeFile(data)
    85  		return nil
    86  	case *FileRemoteStore:
    87  		return impl.addMeta(path, data)
    88  	default:
    89  		return fmt.Errorf("non-supoprted RemoteStore, got %+v", impl)
    90  	}
    91  }
    92  
    93  func (s *ClientSuite) setRemoteTarget(path string, data []byte) error {
    94  	switch impl := s.remote.(type) {
    95  	case *fakeRemoteStore:
    96  		impl.targets[path] = newFakeFile(data)
    97  		return nil
    98  	case *FileRemoteStore:
    99  		return impl.addTarget(path, data)
   100  	default:
   101  		return fmt.Errorf("non-supoprted RemoteStore, got %+v", impl)
   102  	}
   103  }
   104  
   105  func (s *ClientSuite) deleteMeta(path string) error {
   106  	switch impl := s.remote.(type) {
   107  	case *fakeRemoteStore:
   108  		delete(impl.meta, path)
   109  		return nil
   110  	case *FileRemoteStore:
   111  		return impl.deleteMeta(path)
   112  	default:
   113  		return fmt.Errorf("non-supported RemoteStore, got %+v", impl)
   114  	}
   115  }
   116  
   117  func (s *ClientSuite) deleteTarget(path string) error {
   118  	switch impl := s.remote.(type) {
   119  	case *fakeRemoteStore:
   120  		delete(impl.targets, path)
   121  		return nil
   122  	case *FileRemoteStore:
   123  		return impl.deleteTarget(path)
   124  	default:
   125  		return fmt.Errorf("non-supported RemoteStore, got %+v", impl)
   126  	}
   127  }
   128  
   129  func (s *ClientSuite) readMeta(name string) ([]byte, error) {
   130  	stream, _, err := s.remote.GetMeta(name)
   131  	if err != nil {
   132  		return nil, err
   133  	}
   134  	return io.ReadAll(stream)
   135  }
   136  
   137  func newFakeFile(b []byte) *fakeFile {
   138  	return &fakeFile{buf: bytes.NewReader(b), size: int64(len(b))}
   139  }
   140  
   141  type fakeFile struct {
   142  	buf       *bytes.Reader
   143  	bytesRead int
   144  	size      int64
   145  }
   146  
   147  func (f *fakeFile) Read(p []byte) (int, error) {
   148  	n, err := f.buf.Read(p)
   149  	f.bytesRead += n
   150  	return n, err
   151  }
   152  
   153  func (f *fakeFile) Close() error {
   154  	f.buf.Seek(0, io.SeekStart)
   155  	return nil
   156  }
   157  
   158  var targetFiles = map[string][]byte{
   159  	"foo.txt": []byte("foo"),
   160  	"bar.txt": []byte("bar"),
   161  	"baz.txt": []byte("baz"),
   162  }
   163  
   164  func (s *ClientSuite) SetUpTest(c *C) {
   165  	s.store = tuf.MemoryStore(nil, targetFiles)
   166  
   167  	// create a valid repo containing foo.txt
   168  	var err error
   169  	s.repo, err = tuf.NewRepo(s.store)
   170  	c.Assert(err, IsNil)
   171  	// don't use consistent snapshots to make testing easier (consistent
   172  	// snapshots are tested explicitly elsewhere)
   173  	c.Assert(s.repo.Init(false), IsNil)
   174  	s.keyIDs = map[string][]string{
   175  		"root":      s.genKey(c, "root"),
   176  		"targets":   s.genKey(c, "targets"),
   177  		"snapshot":  s.genKey(c, "snapshot"),
   178  		"timestamp": s.genKey(c, "timestamp"),
   179  	}
   180  	c.Assert(s.repo.AddTarget("foo.txt", nil), IsNil)
   181  	c.Assert(s.repo.Snapshot(), IsNil)
   182  	c.Assert(s.repo.Timestamp(), IsNil)
   183  	c.Assert(s.repo.Commit(), IsNil)
   184  
   185  	// create a remote store containing valid repo files
   186  	if s.useFileStore {
   187  		s.remote, s.tmpDir, err = newTestFileStoreFS()
   188  		if err != nil {
   189  			c.Fatalf("failed to create new FileStore: %v", err)
   190  		}
   191  	} else {
   192  		s.remote = newFakeRemoteStore()
   193  	}
   194  	s.syncRemote(c)
   195  	for path, data := range targetFiles {
   196  		s.setRemoteTarget(path, data)
   197  	}
   198  
   199  	s.expiredTime = time.Now().Add(time.Hour)
   200  }
   201  
   202  func (s *ClientSuite) TearDownTest(c *C) {
   203  	if s.tmpDir != "" {
   204  		rmrf(s.tmpDir, c.Logf)
   205  	}
   206  }
   207  
   208  func (s *ClientSuite) genKey(c *C, role string) []string {
   209  	ids, err := s.repo.GenKey(role)
   210  	c.Assert(err, IsNil)
   211  	return ids
   212  }
   213  
   214  func (s *ClientSuite) genKeyExpired(c *C, role string) []string {
   215  	ids, err := s.repo.GenKeyWithExpires(role, s.expiredTime)
   216  	c.Assert(err, IsNil)
   217  	return ids
   218  }
   219  
   220  // withMetaExpired sets signed.IsExpired throughout the invocation of f so that
   221  // any metadata marked to expire at s.expiredTime will be expired (this avoids
   222  // the need to sleep in the tests).
   223  func (s *ClientSuite) withMetaExpired(f func()) {
   224  	e := verify.IsExpired
   225  	defer func() { verify.IsExpired = e }()
   226  	verify.IsExpired = func(t time.Time) bool {
   227  		return t.Unix() == s.expiredTime.Round(time.Second).Unix()
   228  	}
   229  	f()
   230  }
   231  
   232  func (s *ClientSuite) syncLocal(c *C) {
   233  	meta, err := s.store.GetMeta()
   234  	c.Assert(err, IsNil)
   235  	for k, v := range meta {
   236  		c.Assert(s.local.SetMeta(k, v), IsNil)
   237  	}
   238  }
   239  
   240  func (s *ClientSuite) syncRemote(c *C) {
   241  	meta, err := s.store.GetMeta()
   242  	c.Assert(err, IsNil)
   243  	for name, data := range meta {
   244  		if err := s.setRemoteMeta(name, data); err != nil {
   245  			panic(fmt.Sprintf("setMetadata failed: %v", err))
   246  		}
   247  	}
   248  }
   249  
   250  func (s *ClientSuite) addRemoteTarget(c *C, name string) {
   251  	c.Assert(s.repo.AddTarget(name, nil), IsNil)
   252  	c.Assert(s.repo.Snapshot(), IsNil)
   253  	c.Assert(s.repo.Timestamp(), IsNil)
   254  	c.Assert(s.repo.Commit(), IsNil)
   255  	s.syncRemote(c)
   256  }
   257  
   258  func (s *ClientSuite) rootMeta(c *C) []byte {
   259  	meta, err := s.repo.GetMeta()
   260  	c.Assert(err, IsNil)
   261  	rootMeta, ok := meta["root.json"]
   262  	c.Assert(ok, Equals, true)
   263  	return rootMeta
   264  }
   265  
   266  func (s *ClientSuite) newClient(c *C) *Client {
   267  	s.local = MemoryLocalStore()
   268  	client := NewClient(s.local, s.remote)
   269  	c.Assert(client.Init(s.rootMeta(c)), IsNil)
   270  	return client
   271  }
   272  
   273  func (s *ClientSuite) updatedClient(c *C) *Client {
   274  	client := s.newClient(c)
   275  	_, err := client.Update()
   276  	c.Assert(err, IsNil)
   277  	return client
   278  }
   279  
   280  func assertFile(c *C, file data.TargetFileMeta, name string) {
   281  	target, ok := targetFiles[name]
   282  	if !ok {
   283  		c.Fatalf("unknown target %s", name)
   284  	}
   285  
   286  	meta, err := util.GenerateTargetFileMeta(bytes.NewReader(target), file.HashAlgorithms()...)
   287  	c.Assert(err, IsNil)
   288  	c.Assert(util.TargetFileMetaEqual(file, meta), IsNil)
   289  }
   290  
   291  func assertFiles(c *C, files data.TargetFiles, names []string) {
   292  	c.Assert(files, HasLen, len(names))
   293  	for _, name := range names {
   294  		file, ok := files[name]
   295  		if !ok {
   296  			c.Fatalf("expected files to contain %s", name)
   297  		}
   298  
   299  		assertFile(c, file, name)
   300  	}
   301  }
   302  
   303  func assertWrongHash(c *C, err error) {
   304  	// just test the type of err rather using DeepEquals as it contains
   305  	// hashes we don't necessarily need to check.
   306  	e, ok := err.(ErrDownloadFailed)
   307  	if !ok {
   308  		c.Fatalf("expected err to have type ErrDownloadFailed, got %T", err)
   309  	}
   310  	if _, ok := e.Err.(util.ErrWrongHash); !ok {
   311  		c.Fatalf("expected err.Err to have type util.ErrWrongHash, got %T", err)
   312  	}
   313  }
   314  
   315  func (s *ClientSuite) assertErrExpired(c *C, err error, file string) {
   316  	decodeErr, ok := err.(ErrDecodeFailed)
   317  	if !ok {
   318  		c.Fatalf("expected err to have type ErrDecodeFailed, got %T", err)
   319  	}
   320  	c.Assert(decodeErr.File, Equals, file)
   321  	expiredErr, ok := decodeErr.Err.(verify.ErrExpired)
   322  	if !ok {
   323  		c.Fatalf("expected err.Err to have type signed.ErrExpired, got %T", err)
   324  	}
   325  	c.Assert(expiredErr.Expired.Unix(), Equals, s.expiredTime.Round(time.Second).Unix())
   326  }
   327  
   328  func (s *ClientSuite) TestInitAllowsExpired(c *C) {
   329  	s.genKeyExpired(c, "targets")
   330  	c.Assert(s.repo.Snapshot(), IsNil)
   331  	c.Assert(s.repo.Timestamp(), IsNil)
   332  	c.Assert(s.repo.Commit(), IsNil)
   333  	s.syncRemote(c)
   334  	client := NewClient(MemoryLocalStore(), s.remote)
   335  	bytes, err := s.readMeta("root.json")
   336  	c.Assert(err, IsNil)
   337  	s.withMetaExpired(func() {
   338  		c.Assert(client.Init(bytes), IsNil)
   339  	})
   340  }
   341  
   342  func (s *ClientSuite) TestInit(c *C) {
   343  	client := NewClient(MemoryLocalStore(), s.remote)
   344  	bytes, err := s.readMeta("root.json")
   345  	c.Assert(err, IsNil)
   346  	dataSigned := &data.Signed{}
   347  	c.Assert(json.Unmarshal(bytes, dataSigned), IsNil)
   348  	root := &data.Root{}
   349  	c.Assert(json.Unmarshal(dataSigned.Signed, root), IsNil)
   350  
   351  	// check Update() returns ErrNoRootKeys when uninitialized
   352  	_, err = client.Update()
   353  	c.Assert(err, Equals, ErrNoRootKeys)
   354  
   355  	// check Init() returns ErrRoleThreshold when the root's signature is
   356  	// invalid
   357  	// modify root and marshal without regenerating signatures
   358  	root.Version = root.Version + 1
   359  	rootBytes, err := json.Marshal(root)
   360  	c.Assert(err, IsNil)
   361  	dataSigned.Signed = rootBytes
   362  	dataBytes, err := json.Marshal(dataSigned)
   363  	c.Assert(err, IsNil)
   364  	c.Assert(client.Init(dataBytes), Equals, verify.ErrRoleThreshold{
   365  		Expected: 1, Actual: 0})
   366  
   367  	// check Update() does not return ErrNoRootKeys after initialization
   368  	c.Assert(client.Init(bytes), IsNil)
   369  	_, err = client.Update()
   370  	c.Assert(err, IsNil)
   371  }
   372  
   373  // This is a regression test for https://github.com/theupdateframework/go-tuf/issues/370
   374  // where a single invalid signature resulted in an early return.
   375  // Instead, the client should have continued and counted the number
   376  // of valid signatures, ignoring the incorrect one.
   377  func (s *ClientSuite) TestExtraRootSignaturesOnInit(c *C) {
   378  	client := NewClient(MemoryLocalStore(), s.remote)
   379  	bytes, err := s.readMeta("root.json")
   380  	c.Assert(err, IsNil)
   381  	dataSigned := &data.Signed{}
   382  	c.Assert(json.Unmarshal(bytes, dataSigned), IsNil)
   383  
   384  	// check Init() succeeds when an extra invalid signature was
   385  	// added to the root.
   386  	dataSigned.Signatures = append(dataSigned.Signatures,
   387  		data.Signature{
   388  			KeyID:     dataSigned.Signatures[0].KeyID,
   389  			Signature: make([]byte, ed25519.SignatureSize),
   390  		})
   391  	dataBytes, err := json.Marshal(dataSigned)
   392  	c.Assert(err, IsNil)
   393  	c.Assert(client.Init(dataBytes), IsNil)
   394  }
   395  
   396  func (s *ClientSuite) TestFirstUpdate(c *C) {
   397  	files, err := s.newClient(c).Update()
   398  	c.Assert(err, IsNil)
   399  	c.Assert(files, HasLen, 1)
   400  	assertFiles(c, files, []string{"foo.txt"})
   401  }
   402  
   403  func (s *ClientSuite) TestMissingRemoteMetadata(c *C) {
   404  	client := s.newClient(c)
   405  
   406  	s.deleteMeta("targets.json")
   407  	_, err := client.Update()
   408  	c.Assert(err, Equals, ErrMissingRemoteMetadata{"targets.json"})
   409  
   410  	s.deleteMeta("timestamp.json")
   411  	_, err = client.Update()
   412  	c.Assert(err, Equals, ErrMissingRemoteMetadata{"timestamp.json"})
   413  }
   414  
   415  func (s *ClientSuite) TestNoChangeUpdate(c *C) {
   416  	client := s.newClient(c)
   417  	_, err := client.Update()
   418  	c.Assert(err, IsNil)
   419  	_, err = client.Update()
   420  	c.Assert(err, IsNil)
   421  }
   422  
   423  func (s *ClientSuite) TestNewTimestamp(c *C) {
   424  	client := s.updatedClient(c)
   425  	version := client.timestampVer
   426  	c.Assert(version > 0, Equals, true)
   427  	c.Assert(s.repo.Timestamp(), IsNil)
   428  	s.syncRemote(c)
   429  	_, err := client.Update()
   430  	c.Assert(err, IsNil)
   431  	c.Assert(client.timestampVer > version, Equals, true)
   432  }
   433  
   434  func (s *ClientSuite) TestNewRoot(c *C) {
   435  	client := s.newClient(c)
   436  
   437  	// replace all keys
   438  	newKeyIDs := make(map[string][]string)
   439  	for role, ids := range s.keyIDs {
   440  		c.Assert(len(ids) > 0, Equals, true)
   441  		c.Assert(s.repo.RevokeKey(role, ids[0]), IsNil)
   442  		newKeyIDs[role] = s.genKey(c, role)
   443  	}
   444  
   445  	// update metadata
   446  	c.Assert(s.repo.Sign("targets.json"), IsNil)
   447  	c.Assert(s.repo.Snapshot(), IsNil)
   448  	c.Assert(s.repo.Timestamp(), IsNil)
   449  	c.Assert(s.repo.Commit(), IsNil)
   450  	s.syncRemote(c)
   451  
   452  	// check update gets new root version
   453  	c.Assert(client.getLocalMeta(), IsNil)
   454  	version := client.rootVer
   455  	c.Assert(version > 0, Equals, true)
   456  	_, err := client.Update()
   457  	c.Assert(err, IsNil)
   458  	c.Assert(client.rootVer > version, Equals, true)
   459  
   460  	// check old keys are not in db
   461  	for _, ids := range s.keyIDs {
   462  		c.Assert(len(ids) > 0, Equals, true)
   463  		for _, id := range ids {
   464  			_, err := client.db.GetVerifier(id)
   465  			c.Assert(err, NotNil)
   466  		}
   467  	}
   468  
   469  	// check new keys are in db
   470  	for name, ids := range newKeyIDs {
   471  		c.Assert(len(ids) > 0, Equals, true)
   472  		for _, id := range ids {
   473  			verifier, err := client.db.GetVerifier(id)
   474  			c.Assert(err, IsNil)
   475  			c.Assert(verifier.MarshalPublicKey().IDs(), DeepEquals, ids)
   476  		}
   477  		role := client.db.GetRole(name)
   478  		c.Assert(role, NotNil)
   479  		c.Assert(role.KeyIDs, DeepEquals, sets.StringSliceToSet(ids))
   480  	}
   481  }
   482  
   483  // This is a regression test for https://github.com/theupdateframework/go-tuf/issues/370
   484  // where a single invalid signature resulted in an early return.
   485  // Instead, the client should have continued and counted the number
   486  // of valid signatures, ignoring the incorrect one.
   487  func (s *ClientSuite) TestExtraSignaturesOnRootUpdate(c *C) {
   488  	client := s.newClient(c)
   489  
   490  	// Add an extra root key to update the root to a new version.
   491  	s.genKey(c, "root")
   492  	// update metadata
   493  	c.Assert(s.repo.Sign("targets.json"), IsNil)
   494  	c.Assert(s.repo.Snapshot(), IsNil)
   495  	c.Assert(s.repo.Timestamp(), IsNil)
   496  	c.Assert(s.repo.Commit(), IsNil)
   497  	s.syncRemote(c)
   498  
   499  	// Add an extra signature to the new remote root.
   500  	bytes, err := s.readMeta("root.json")
   501  	c.Assert(err, IsNil)
   502  	dataSigned := &data.Signed{}
   503  	c.Assert(json.Unmarshal(bytes, dataSigned), IsNil)
   504  	dataSigned.Signatures = append(dataSigned.Signatures,
   505  		data.Signature{
   506  			KeyID:     dataSigned.Signatures[0].KeyID,
   507  			Signature: make([]byte, ed25519.SignatureSize),
   508  		})
   509  	dataBytes, err := json.Marshal(dataSigned)
   510  	c.Assert(err, IsNil)
   511  	s.setRemoteMeta("root.json", dataBytes)
   512  	s.setRemoteMeta("2.root.json", dataBytes)
   513  
   514  	// check Update() succeeds when an extra invalid signature was
   515  	// added to the root.
   516  	_, err = client.Update()
   517  	c.Assert(err, IsNil)
   518  	c.Assert(client.rootVer, Equals, int64(2))
   519  }
   520  
   521  // startTUFRepoServer starts a HTTP server to serve a TUF Repo.
   522  func startTUFRepoServer(baseDir string, relPath string) (net.Listener, error) {
   523  	serverDir := filepath.Join(baseDir, relPath)
   524  	l, err := net.Listen("tcp", "127.0.0.1:0")
   525  	go http.Serve(l, http.FileServer(http.Dir(serverDir)))
   526  	return l, err
   527  }
   528  
   529  // newClientWithMeta creates new client and sets the root metadata for it.
   530  func newClientWithMeta(baseDir string, relPath string, serverAddr string) (*Client, error) {
   531  	initialStateDir := filepath.Join(baseDir, relPath)
   532  	opts := &HTTPRemoteOptions{
   533  		MetadataPath: "metadata",
   534  		TargetsPath:  "targets",
   535  	}
   536  
   537  	remote, err := HTTPRemoteStore(fmt.Sprintf("http://%s/", serverAddr), opts, nil)
   538  	if err != nil {
   539  		return nil, err
   540  	}
   541  	c := NewClient(MemoryLocalStore(), remote)
   542  	for _, m := range []string{"root.json", "snapshot.json", "timestamp.json", "targets.json"} {
   543  		if _, err := os.Stat(initialStateDir + "/" + m); err == nil {
   544  			metadataJSON, err := os.ReadFile(initialStateDir + "/" + m)
   545  			if err != nil {
   546  				return nil, err
   547  			}
   548  			c.local.SetMeta(m, metadataJSON)
   549  		}
   550  	}
   551  	return c, nil
   552  }
   553  
   554  func initRootTest(c *C, baseDir string) (*Client, func() error) {
   555  	l, err := startTUFRepoServer(baseDir, "server")
   556  	c.Assert(err, IsNil)
   557  	tufClient, err := newClientWithMeta(baseDir, "client/metadata/current", l.Addr().String())
   558  	c.Assert(err, IsNil)
   559  	return tufClient, l.Close
   560  }
   561  
   562  func (s *ClientSuite) TestUpdateRoots(c *C) {
   563  	var tests = []struct {
   564  		fixturePath      string
   565  		expectedError    error
   566  		expectedVersions map[string]int64
   567  	}{
   568  		// Succeeds when there is no root update.
   569  		{"testdata/Published1Time", nil, map[string]int64{"root": 1, "timestamp": 1, "snapshot": 1, "targets": 1}},
   570  		// Succeeds when client only has root.json
   571  		{"testdata/Published1Time_client_root_only", nil, map[string]int64{"root": 1, "timestamp": 1, "snapshot": 1, "targets": 1}},
   572  		// Succeeds updating root from version 1 to version 2.
   573  		{"testdata/Published2Times_keyrotated", nil, map[string]int64{"root": 2, "timestamp": 1, "snapshot": 1, "targets": 1}},
   574  		// Succeeds updating root from version 1 to version 2 when the client's initial root version is expired.
   575  		{"testdata/Published2Times_keyrotated_initialrootexpired", nil, map[string]int64{"root": 2, "timestamp": 1, "snapshot": 1, "targets": 1}},
   576  		// Succeeds updating root from version 1 to version 3 when versions 1 and 2 are expired.
   577  		{"testdata/Published3Times_keyrotated_initialrootsexpired", nil, map[string]int64{"root": 3, "timestamp": 1, "snapshot": 1, "targets": 1}},
   578  		// Succeeds updating root from version 2 to version 3.
   579  		{"testdata/Published3Times_keyrotated_initialrootsexpired_clientversionis2", nil, map[string]int64{"root": 3, "timestamp": 1, "snapshot": 1, "targets": 1}},
   580  		// Fails updating root from version 1 to version 3 when versions 1 and 3 are expired but version 2 is not expired.
   581  		{"testdata/Published3Times_keyrotated_latestrootexpired", ErrDecodeFailed{File: "root.json", Err: verify.ErrExpired{}}, map[string]int64{"root": 2, "timestamp": 1, "snapshot": 1, "targets": 1}},
   582  		// Fails updating root from version 1 to version 2 when old root 1 did not sign off on it (nth root didn't sign off n+1).
   583  		// TODO(asraa): This testcase should have revoked the old key!
   584  		// https://github.com/theupdateframework/go-tuf/issues/417
   585  		{"testdata/Published2Times_keyrotated_invalidOldRootSignature", nil, map[string]int64{}},
   586  		// Fails updating root from version 1 to version 2 when the new root 2 did not sign itself (n+1th root didn't sign off n+1)
   587  		{"testdata/Published2Times_keyrotated_invalidNewRootSignature", verify.ErrRoleThreshold{Expected: 1, Actual: 0}, map[string]int64{}},
   588  		// Fails updating root to 2.root.json when the value of the version field inside it is 1 (rollback attack prevention).
   589  		{"testdata/Published1Time_backwardRootVersion", verify.ErrWrongVersion(verify.ErrWrongVersion{Given: 1, Expected: 2}), map[string]int64{}},
   590  		// Fails updating root to 2.root.json when the value of the version field inside it is 3 (rollforward attack prevention).
   591  		{"testdata/Published3Times_keyrotated_forwardRootVersion", verify.ErrWrongVersion(verify.ErrWrongVersion{Given: 3, Expected: 2}), map[string]int64{}},
   592  		// Fails updating when there is no local trusted root.
   593  		{"testdata/Published1Time_client_no_root", errors.New("tuf: no root keys found in local meta store"), map[string]int64{}},
   594  
   595  		// snapshot role key rotation increase the snapshot and timestamp.
   596  		{"testdata/Published2Times_snapshot_keyrotated", nil, map[string]int64{"root": 2, "timestamp": 2, "snapshot": 2, "targets": 1}},
   597  		// targets role key rotation increase the snapshot, timestamp, and targets.
   598  		{"testdata/Published2Times_targets_keyrotated", nil, map[string]int64{"root": 2, "timestamp": 2, "snapshot": 2, "targets": 2}},
   599  		// timestamp role key rotation increase the timestamp.
   600  		{"testdata/Published2Times_timestamp_keyrotated", nil, map[string]int64{"root": 2, "timestamp": 2, "snapshot": 1, "targets": 1}},
   601  		//root file size > defaultRootDownloadLimit
   602  		{"testdata/Published2Times_roottoolarge", ErrMetaTooLarge{Name: "2.root.json", Size: defaultRootDownloadLimit + 1, MaxSize: defaultRootDownloadLimit}, map[string]int64{}},
   603  	}
   604  
   605  	for _, test := range tests {
   606  		tufClient, closer := initRootTest(c, test.fixturePath)
   607  		_, err := tufClient.Update()
   608  		if test.expectedError == nil {
   609  			c.Assert(err, IsNil)
   610  			// Check if the root.json is being saved in non-volatile storage.
   611  			tufClient.getLocalMeta()
   612  			versionMethods := map[string]int64{"root": tufClient.rootVer,
   613  				"timestamp": tufClient.timestampVer,
   614  				"snapshot":  tufClient.snapshotVer,
   615  				"targets":   tufClient.targetsVer}
   616  			for m, v := range test.expectedVersions {
   617  				assert.Equal(c, v, versionMethods[m])
   618  			}
   619  		} else {
   620  			// For backward compatibility, the update root returns
   621  			// ErrDecodeFailed that wraps the verify.ErrExpired.
   622  			if _, ok := test.expectedError.(ErrDecodeFailed); ok {
   623  				decodeErr, ok := err.(ErrDecodeFailed)
   624  				c.Assert(ok, Equals, true)
   625  				c.Assert(decodeErr.File, Equals, "root.json")
   626  				_, ok = decodeErr.Err.(verify.ErrExpired)
   627  				c.Assert(ok, Equals, true)
   628  			} else {
   629  				assert.Equal(c, test.expectedError, err)
   630  			}
   631  		}
   632  		closer()
   633  	}
   634  }
   635  
   636  func (s *ClientSuite) TestFastForwardAttackRecovery(c *C) {
   637  	var tests = []struct {
   638  		fixturePath       string
   639  		expectMetaDeleted map[string]bool
   640  	}{
   641  		// Each of the following test cases each has a two sets of TUF metadata:
   642  		// (1) client's initial, and (2) server's current.
   643  		// The naming format is PublishedTwiceMultiKeysadd_X_revoke_Y_threshold_Z_ROLE
   644  		// The client includes TUF metadata before key rotation for TUF ROLE with X keys.
   645  		// The server includes updated TUF metadata after key rotation. The
   646  		// rotation involves revoking Y keys from the initial keys.
   647  		// For each test, the TUF client's will be initialized to the client files.
   648  		// The test checks whether the client is  able to update itself properly.
   649  
   650  		// Fast-forward recovery is not needed if less than threshold keys are revoked.
   651  		{"testdata/PublishedTwiceMultiKeysadd_9_revoke_2_threshold_4_root",
   652  			map[string]bool{"root.json": false, "timestamp.json": false, "snapshot.json": false, "targets.json": false}},
   653  		{"testdata/PublishedTwiceMultiKeysadd_9_revoke_2_threshold_4_snapshot",
   654  			map[string]bool{"root.json": false, "timestamp.json": false, "snapshot.json": false, "targets.json": false}},
   655  		{"testdata/PublishedTwiceMultiKeysadd_9_revoke_2_threshold_4_targets",
   656  			map[string]bool{"root.json": false, "timestamp.json": false, "snapshot.json": false, "targets.json": false}},
   657  		{"testdata/PublishedTwiceMultiKeysadd_9_revoke_2_threshold_4_timestamp",
   658  			map[string]bool{"root.json": false, "timestamp.json": false, "snapshot.json": false, "targets.json": false}},
   659  
   660  		// Fast-forward recovery not needed if root keys are revoked, even when the threshold number of root keys are revoked.
   661  		{"testdata/PublishedTwiceMultiKeysadd_9_revoke_4_threshold_4_root",
   662  			map[string]bool{"root.json": false, "timestamp.json": false, "snapshot.json": false, "targets.json": false}},
   663  
   664  		// Delete snapshot and timestamp metadata if a threshold number of snapshot keys are revoked.
   665  		{"testdata/PublishedTwiceMultiKeysadd_9_revoke_4_threshold_4_snapshot",
   666  			map[string]bool{"root.json": false, "timestamp.json": true, "snapshot.json": true, "targets.json": false}},
   667  		// Delete targets and snapshot metadata if a threshold number of targets keys are revoked.
   668  		{"testdata/PublishedTwiceMultiKeysadd_9_revoke_4_threshold_4_targets",
   669  			map[string]bool{"root.json": false, "timestamp.json": false, "snapshot.json": true, "targets.json": true}},
   670  		// Delete timestamp metadata if a threshold number of timestamp keys are revoked.
   671  		{"testdata/PublishedTwiceMultiKeysadd_9_revoke_4_threshold_4_timestamp",
   672  			map[string]bool{"root.json": false, "timestamp.json": true, "snapshot.json": false, "targets.json": false}},
   673  	}
   674  	for _, test := range tests {
   675  		tufClient, closer := initRootTest(c, test.fixturePath)
   676  		c.Assert(tufClient.UpdateRoots(), IsNil)
   677  		m, err := tufClient.local.GetMeta()
   678  		c.Assert(err, IsNil)
   679  		for md, deleted := range test.expectMetaDeleted {
   680  			if deleted {
   681  				if _, ok := m[md]; ok {
   682  					c.Fatalf("Metadata %s is not deleted!", md)
   683  				}
   684  			} else {
   685  				if _, ok := m[md]; !ok {
   686  					c.Fatalf("Metadata %s deleted!", md)
   687  				}
   688  			}
   689  		}
   690  		closer()
   691  	}
   692  
   693  }
   694  
   695  func (s *ClientSuite) TestUpdateRace(c *C) {
   696  	// Tests race condition for the client update. You need to run the test with -race flag:
   697  	// go test -race
   698  	for i := 0; i < 2; i++ {
   699  		go func() {
   700  			c := NewClient(MemoryLocalStore(), newFakeRemoteStore())
   701  			c.Update()
   702  		}()
   703  	}
   704  }
   705  
   706  func (s *ClientSuite) TestNewTargets(c *C) {
   707  	client := s.newClient(c)
   708  	files, err := client.Update()
   709  	c.Assert(err, IsNil)
   710  	assertFiles(c, files, []string{"foo.txt"})
   711  
   712  	s.addRemoteTarget(c, "bar.txt")
   713  	s.addRemoteTarget(c, "baz.txt")
   714  
   715  	files, err = client.Update()
   716  	c.Assert(err, IsNil)
   717  	assertFiles(c, files, []string{"bar.txt", "baz.txt"})
   718  
   719  	// Adding the same exact file should not lead to an update
   720  	s.addRemoteTarget(c, "bar.txt")
   721  	files, err = client.Update()
   722  	c.Assert(err, IsNil)
   723  	c.Assert(files, HasLen, 0)
   724  }
   725  
   726  func (s *ClientSuite) TestNewTimestampKey(c *C) {
   727  	client := s.newClient(c)
   728  
   729  	// replace key
   730  	oldIDs := s.keyIDs["timestamp"]
   731  	c.Assert(s.repo.RevokeKey("timestamp", oldIDs[0]), IsNil)
   732  	newIDs := s.genKey(c, "timestamp")
   733  
   734  	// generate new snapshot (because root has changed) and timestamp
   735  	c.Assert(s.repo.Snapshot(), IsNil)
   736  	c.Assert(s.repo.Timestamp(), IsNil)
   737  	c.Assert(s.repo.Commit(), IsNil)
   738  	s.syncRemote(c)
   739  
   740  	// check update gets new root and timestamp
   741  	c.Assert(client.getLocalMeta(), IsNil)
   742  	rootVer := client.rootVer
   743  	timestampVer := client.timestampVer
   744  	_, err := client.Update()
   745  	c.Assert(err, IsNil)
   746  	c.Assert(client.rootVer > rootVer, Equals, true)
   747  	c.Assert(client.timestampVer > timestampVer, Equals, true)
   748  
   749  	// check key has been replaced in db
   750  	for _, oldID := range oldIDs {
   751  		_, err := client.db.GetVerifier(oldID)
   752  		c.Assert(err, NotNil)
   753  	}
   754  	for _, newID := range newIDs {
   755  		verifier, err := client.db.GetVerifier(newID)
   756  		c.Assert(err, IsNil)
   757  		c.Assert(verifier.MarshalPublicKey().IDs(), DeepEquals, newIDs)
   758  	}
   759  	role := client.db.GetRole("timestamp")
   760  	c.Assert(role, NotNil)
   761  	c.Assert(role.KeyIDs, DeepEquals, sets.StringSliceToSet(newIDs))
   762  }
   763  
   764  func (s *ClientSuite) TestNewSnapshotKey(c *C) {
   765  	client := s.newClient(c)
   766  
   767  	// replace key
   768  	oldIDs := s.keyIDs["snapshot"]
   769  	c.Assert(s.repo.RevokeKey("snapshot", oldIDs[0]), IsNil)
   770  	newIDs := s.genKey(c, "snapshot")
   771  
   772  	// generate new snapshot and timestamp
   773  	c.Assert(s.repo.Snapshot(), IsNil)
   774  	c.Assert(s.repo.Timestamp(), IsNil)
   775  	c.Assert(s.repo.Commit(), IsNil)
   776  	s.syncRemote(c)
   777  
   778  	// check update gets new root, snapshot and timestamp
   779  	c.Assert(client.getLocalMeta(), IsNil)
   780  	rootVer := client.rootVer
   781  	snapshotVer := client.snapshotVer
   782  	timestampVer := client.timestampVer
   783  	_, err := client.Update()
   784  	c.Assert(err, IsNil)
   785  	c.Assert(client.rootVer > rootVer, Equals, true)
   786  	c.Assert(client.snapshotVer > snapshotVer, Equals, true)
   787  	c.Assert(client.timestampVer > timestampVer, Equals, true)
   788  
   789  	// check key has been replaced in db
   790  	for _, oldID := range oldIDs {
   791  		_, err := client.db.GetVerifier(oldID)
   792  		c.Assert(err, NotNil)
   793  	}
   794  	for _, newID := range newIDs {
   795  		verifier, err := client.db.GetVerifier(newID)
   796  		c.Assert(err, IsNil)
   797  		c.Assert(verifier.MarshalPublicKey().IDs(), DeepEquals, newIDs)
   798  	}
   799  	role := client.db.GetRole("snapshot")
   800  	c.Assert(role, NotNil)
   801  	c.Assert(role.KeyIDs, DeepEquals, sets.StringSliceToSet(newIDs))
   802  }
   803  
   804  func (s *ClientSuite) TestNewTargetsKey(c *C) {
   805  	client := s.newClient(c)
   806  
   807  	// replace key
   808  	oldIDs := s.keyIDs["targets"]
   809  	c.Assert(s.repo.RevokeKey("targets", oldIDs[0]), IsNil)
   810  	newIDs := s.genKey(c, "targets")
   811  
   812  	// re-sign targets and generate new snapshot and timestamp
   813  	c.Assert(s.repo.Sign("targets.json"), IsNil)
   814  	c.Assert(s.repo.Snapshot(), IsNil)
   815  	c.Assert(s.repo.Timestamp(), IsNil)
   816  	c.Assert(s.repo.Commit(), IsNil)
   817  	s.syncRemote(c)
   818  
   819  	// check update gets new metadata
   820  	c.Assert(client.getLocalMeta(), IsNil)
   821  	rootVer := client.rootVer
   822  	targetsVer := client.targetsVer
   823  	snapshotVer := client.snapshotVer
   824  	timestampVer := client.timestampVer
   825  	_, err := client.Update()
   826  	c.Assert(err, IsNil)
   827  	c.Assert(client.rootVer > rootVer, Equals, true)
   828  	c.Assert(client.targetsVer > targetsVer, Equals, true)
   829  	c.Assert(client.snapshotVer > snapshotVer, Equals, true)
   830  	c.Assert(client.timestampVer > timestampVer, Equals, true)
   831  
   832  	// check key has been replaced in db
   833  	for _, oldID := range oldIDs {
   834  		_, err := client.db.GetVerifier(oldID)
   835  		c.Assert(err, NotNil)
   836  	}
   837  	for _, newID := range newIDs {
   838  		verifier, err := client.db.GetVerifier(newID)
   839  		c.Assert(err, IsNil)
   840  		c.Assert(verifier.MarshalPublicKey().IDs(), DeepEquals, newIDs)
   841  	}
   842  	role := client.db.GetRole("targets")
   843  	c.Assert(role, NotNil)
   844  	c.Assert(role.KeyIDs, DeepEquals, sets.StringSliceToSet(newIDs))
   845  }
   846  
   847  func (s *ClientSuite) TestOfflineSignatureFlow(c *C) {
   848  	client := s.newClient(c)
   849  
   850  	// replace key
   851  	oldIDs := s.keyIDs["targets"]
   852  	c.Assert(s.repo.RevokeKey("targets", oldIDs[0]), IsNil)
   853  	_ = s.genKey(c, "targets")
   854  
   855  	// re-sign targets using offline flow and generate new snapshot and timestamp
   856  	payload, err := s.repo.Payload("targets.json")
   857  	c.Assert(err, IsNil)
   858  	signed := data.Signed{Signed: payload}
   859  	_, err = s.repo.SignPayload("targets", &signed)
   860  	c.Assert(err, IsNil)
   861  	for _, sig := range signed.Signatures {
   862  		// This method checks that the signature verifies!
   863  		err = s.repo.AddOrUpdateSignature("targets.json", sig)
   864  		c.Assert(err, IsNil)
   865  	}
   866  	c.Assert(s.repo.Snapshot(), IsNil)
   867  	c.Assert(s.repo.Timestamp(), IsNil)
   868  	c.Assert(s.repo.Commit(), IsNil)
   869  	s.syncRemote(c)
   870  
   871  	// check update gets new metadata
   872  	c.Assert(client.getLocalMeta(), IsNil)
   873  	_, err = client.Update()
   874  	c.Assert(err, IsNil)
   875  }
   876  
   877  func (s *ClientSuite) TestLocalExpired(c *C) {
   878  	client := s.newClient(c)
   879  
   880  	// locally expired timestamp.json is ok
   881  	version := client.timestampVer
   882  	c.Assert(s.repo.TimestampWithExpires(s.expiredTime), IsNil)
   883  	s.syncLocal(c)
   884  	s.withMetaExpired(func() {
   885  		c.Assert(client.getLocalMeta(), IsNil)
   886  		c.Assert(client.timestampVer > version, Equals, true)
   887  	})
   888  
   889  	// locally expired snapshot.json is ok
   890  	version = client.snapshotVer
   891  	c.Assert(s.repo.SnapshotWithExpires(s.expiredTime), IsNil)
   892  	s.syncLocal(c)
   893  	s.withMetaExpired(func() {
   894  		c.Assert(client.getLocalMeta(), IsNil)
   895  		c.Assert(client.snapshotVer > version, Equals, true)
   896  	})
   897  
   898  	// locally expired targets.json is ok
   899  	version = client.targetsVer
   900  	c.Assert(s.repo.AddTargetWithExpires("foo.txt", nil, s.expiredTime), IsNil)
   901  	s.syncLocal(c)
   902  	s.withMetaExpired(func() {
   903  		c.Assert(client.getLocalMeta(), IsNil)
   904  		c.Assert(client.targetsVer > version, Equals, true)
   905  	})
   906  
   907  	// locally expired root.json is not ok
   908  	version = client.rootVer
   909  	s.genKeyExpired(c, "targets")
   910  	s.syncLocal(c)
   911  	s.withMetaExpired(func() {
   912  		err := client.getLocalMeta()
   913  		if _, ok := err.(verify.ErrExpired); !ok {
   914  			c.Fatalf("expected err to have type signed.ErrExpired, got %T", err)
   915  		}
   916  		c.Assert(client.rootVer, Equals, version)
   917  	})
   918  }
   919  
   920  func (s *ClientSuite) TestTimestampTooLarge(c *C) {
   921  	s.setRemoteMeta("timestamp.json", make([]byte, defaultTimestampDownloadLimit+1))
   922  	_, err := s.newClient(c).Update()
   923  	c.Assert(err, Equals, ErrMetaTooLarge{"timestamp.json", defaultTimestampDownloadLimit + 1, defaultTimestampDownloadLimit})
   924  }
   925  
   926  func (s *ClientSuite) TestUpdateLocalRootExpired(c *C) {
   927  	client := s.newClient(c)
   928  
   929  	// add soon to expire root.json to local storage
   930  	s.genKeyExpired(c, "timestamp")
   931  	c.Assert(s.repo.Snapshot(), IsNil)
   932  	c.Assert(s.repo.Timestamp(), IsNil)
   933  	c.Assert(s.repo.Commit(), IsNil)
   934  	s.syncLocal(c)
   935  
   936  	// add far expiring root.json to remote storage
   937  	s.genKey(c, "timestamp")
   938  	s.addRemoteTarget(c, "bar.txt")
   939  	c.Assert(s.repo.Snapshot(), IsNil)
   940  	c.Assert(s.repo.Timestamp(), IsNil)
   941  	c.Assert(s.repo.Commit(), IsNil)
   942  	s.syncRemote(c)
   943  
   944  	const expectedRootVersion = int64(3)
   945  
   946  	// check the update downloads the non expired remote root.json and
   947  	// restarts itself, thus successfully updating
   948  	s.withMetaExpired(func() {
   949  		err := client.getLocalMeta()
   950  		if _, ok := err.(verify.ErrExpired); !ok {
   951  			c.Fatalf("expected err to have type signed.ErrExpired, got %T", err)
   952  		}
   953  		_, err = client.Update()
   954  		c.Assert(err, IsNil)
   955  		c.Assert(client.rootVer, Equals, expectedRootVersion)
   956  	})
   957  }
   958  
   959  func (s *ClientSuite) TestUpdateRemoteExpired(c *C) {
   960  	client := s.updatedClient(c)
   961  
   962  	// expired remote metadata should always be rejected
   963  	c.Assert(s.repo.TimestampWithExpires(s.expiredTime), IsNil)
   964  	s.syncRemote(c)
   965  	s.withMetaExpired(func() {
   966  		_, err := client.Update()
   967  		s.assertErrExpired(c, err, "timestamp.json")
   968  	})
   969  
   970  	c.Assert(s.repo.SnapshotWithExpires(s.expiredTime), IsNil)
   971  	c.Assert(s.repo.Timestamp(), IsNil)
   972  	c.Assert(s.repo.Commit(), IsNil)
   973  	s.syncRemote(c)
   974  	s.withMetaExpired(func() {
   975  		_, err := client.Update()
   976  		s.assertErrExpired(c, err, "snapshot.json")
   977  	})
   978  
   979  	c.Assert(s.repo.AddTargetWithExpires("bar.txt", nil, s.expiredTime), IsNil)
   980  	c.Assert(s.repo.Snapshot(), IsNil)
   981  	c.Assert(s.repo.Timestamp(), IsNil)
   982  	s.syncRemote(c)
   983  	s.withMetaExpired(func() {
   984  		_, err := client.Update()
   985  		s.assertErrExpired(c, err, "targets.json")
   986  	})
   987  
   988  	s.genKeyExpired(c, "timestamp")
   989  	c.Assert(s.repo.RemoveTarget("bar.txt"), IsNil)
   990  	c.Assert(s.repo.Snapshot(), IsNil)
   991  	c.Assert(s.repo.Timestamp(), IsNil)
   992  	c.Assert(s.repo.Commit(), IsNil)
   993  	s.syncRemote(c)
   994  	s.withMetaExpired(func() {
   995  		_, err := client.Update()
   996  		s.assertErrExpired(c, err, "root.json")
   997  	})
   998  }
   999  
  1000  func (s *ClientSuite) TestUpdateLocalRootExpiredKeyChange(c *C) {
  1001  	client := s.newClient(c)
  1002  
  1003  	// add soon to expire root.json to local storage
  1004  	s.genKeyExpired(c, "timestamp")
  1005  	c.Assert(s.repo.Snapshot(), IsNil)
  1006  	c.Assert(s.repo.Timestamp(), IsNil)
  1007  	c.Assert(s.repo.Commit(), IsNil)
  1008  	s.syncLocal(c)
  1009  
  1010  	// replace all keys
  1011  	newKeyIDs := make(map[string][]string)
  1012  	for role, ids := range s.keyIDs {
  1013  		if role != "snapshot" && role != "timestamp" && role != "targets" {
  1014  			c.Assert(s.repo.RevokeKey(role, ids[0]), IsNil)
  1015  			newKeyIDs[role] = s.genKey(c, role)
  1016  		}
  1017  	}
  1018  
  1019  	// update metadata
  1020  	c.Assert(s.repo.Sign("targets.json"), IsNil)
  1021  	c.Assert(s.repo.Snapshot(), IsNil)
  1022  	c.Assert(s.repo.Timestamp(), IsNil)
  1023  	c.Assert(s.repo.Commit(), IsNil)
  1024  	s.syncRemote(c)
  1025  
  1026  	// check the update downloads the non expired remote root.json and
  1027  	// restarts itself, thus successfully updating
  1028  	s.withMetaExpired(func() {
  1029  		err := client.getLocalMeta()
  1030  		c.Assert(err, FitsTypeOf, verify.ErrExpired{})
  1031  
  1032  		_, err = client.Update()
  1033  		c.Assert(err, IsNil)
  1034  	})
  1035  }
  1036  
  1037  func (s *ClientSuite) TestUpdateMixAndMatchAttack(c *C) {
  1038  	// generate metadata with an explicit expires so we can make predictable changes
  1039  	expires := time.Now().Add(time.Hour)
  1040  	c.Assert(s.repo.AddTargetWithExpires("foo.txt", nil, expires), IsNil)
  1041  	c.Assert(s.repo.Snapshot(), IsNil)
  1042  	c.Assert(s.repo.Timestamp(), IsNil)
  1043  	c.Assert(s.repo.Commit(), IsNil)
  1044  	s.syncRemote(c)
  1045  	client := s.updatedClient(c)
  1046  
  1047  	// grab the remote targets.json
  1048  	oldTargets, err := s.readMeta("targets.json")
  1049  	if err != nil {
  1050  		c.Fatal("missing remote targets.json")
  1051  	}
  1052  
  1053  	// generate new remote metadata, but replace targets.json with the old one
  1054  	c.Assert(s.repo.AddTargetWithExpires("bar.txt", nil, expires), IsNil)
  1055  	c.Assert(s.repo.Snapshot(), IsNil)
  1056  	c.Assert(s.repo.Timestamp(), IsNil)
  1057  	c.Assert(s.repo.Commit(), IsNil)
  1058  	s.syncRemote(c)
  1059  	newTargets, err := s.readMeta("targets.json")
  1060  	if err != nil {
  1061  		c.Fatal("missing remote targets.json")
  1062  	}
  1063  	s.setRemoteMeta("targets.json", oldTargets)
  1064  
  1065  	// check update returns ErrWrongSize for targets.json
  1066  	_, err = client.Update()
  1067  	c.Assert(err, DeepEquals, ErrWrongSize{"targets.json", int64(len(oldTargets)), int64(len(newTargets))})
  1068  
  1069  	// do the same but keep the size the same
  1070  	c.Assert(s.repo.RemoveTargetWithExpires("foo.txt", expires), IsNil)
  1071  	c.Assert(s.repo.Snapshot(), IsNil)
  1072  	c.Assert(s.repo.Timestamp(), IsNil)
  1073  	s.syncRemote(c)
  1074  	s.setRemoteMeta("targets.json", oldTargets)
  1075  
  1076  	// check update returns ErrWrongHash
  1077  	_, err = client.Update()
  1078  	assertWrongHash(c, err)
  1079  }
  1080  
  1081  func (s *ClientSuite) TestUpdateReplayAttack(c *C) {
  1082  	client := s.updatedClient(c)
  1083  
  1084  	// grab the remote timestamp.json
  1085  	oldTimestamp, err := s.readMeta("timestamp.json")
  1086  	if err != nil {
  1087  		c.Fatal("missing remote timestamp.json")
  1088  	}
  1089  
  1090  	// generate a new timestamp and sync with the client
  1091  	version := client.timestampVer
  1092  	c.Assert(version > 0, Equals, true)
  1093  	c.Assert(s.repo.Timestamp(), IsNil)
  1094  	s.syncRemote(c)
  1095  	_, err = client.Update()
  1096  	c.Assert(err, IsNil)
  1097  	c.Assert(client.timestampVer > version, Equals, true)
  1098  
  1099  	// replace remote timestamp.json with the old one
  1100  	s.setRemoteMeta("timestamp.json", oldTimestamp)
  1101  
  1102  	// check update returns ErrLowVersion
  1103  	_, err = client.Update()
  1104  	c.Assert(err, DeepEquals, ErrDecodeFailed{
  1105  		File: "timestamp.json",
  1106  		Err: verify.ErrLowVersion{
  1107  			Actual:  version,
  1108  			Current: client.timestampVer,
  1109  		},
  1110  	})
  1111  }
  1112  
  1113  func (s *ClientSuite) TestUpdateForkTimestamp(c *C) {
  1114  	client := s.updatedClient(c)
  1115  
  1116  	// grab the remote timestamp.json
  1117  	oldTimestamp, err := s.readMeta("timestamp.json")
  1118  	if err != nil {
  1119  		c.Fatal("missing remote timestamp.json")
  1120  	}
  1121  
  1122  	// generate a new timestamp and sync with the client
  1123  	version := client.timestampVer
  1124  	c.Assert(version > 0, Equals, true)
  1125  	c.Assert(s.repo.Timestamp(), IsNil)
  1126  	s.syncRemote(c)
  1127  	_, err = client.Update()
  1128  	c.Assert(err, IsNil)
  1129  	newVersion := client.timestampVer
  1130  	c.Assert(newVersion > version, Equals, true)
  1131  
  1132  	// generate a new, different timestamp with the *same version*
  1133  	s.setRemoteMeta("timestamp.json", oldTimestamp)
  1134  	c.Assert(s.repo.Timestamp(), IsNil)
  1135  	c.Assert(client.timestampVer, Equals, newVersion) // double-check: same version?
  1136  	s.syncRemote(c)
  1137  
  1138  	oldMeta, err := client.local.GetMeta()
  1139  	c.Assert(err, IsNil)
  1140  	_, err = client.Update()
  1141  	c.Assert(err, IsNil) // no error: the targets.json version didn't change, so there was no update!
  1142  	// Client shouldn't update!
  1143  	newMeta, err := client.local.GetMeta()
  1144  	c.Assert(err, IsNil)
  1145  	c.Assert(oldMeta, DeepEquals, newMeta)
  1146  }
  1147  
  1148  func (s *ClientSuite) TestUpdateTamperedTargets(c *C) {
  1149  	client := s.newClient(c)
  1150  
  1151  	// get local targets.json
  1152  	meta, err := s.store.GetMeta()
  1153  	c.Assert(err, IsNil)
  1154  	targetsJSON, ok := meta["targets.json"]
  1155  	if !ok {
  1156  		c.Fatal("missing targets.json")
  1157  	}
  1158  
  1159  	type signedTargets struct {
  1160  		Signed     data.Targets     `json:"signed"`
  1161  		Signatures []data.Signature `json:"signatures"`
  1162  	}
  1163  	targets := &signedTargets{}
  1164  	c.Assert(json.Unmarshal(targetsJSON, targets), IsNil)
  1165  
  1166  	// update remote targets.json to have different content but same size
  1167  	targets.Signed.Type = "xxxxxxx"
  1168  	tamperedJSON, err := json.Marshal(targets)
  1169  	c.Assert(err, IsNil)
  1170  	s.store.SetMeta("targets.json", tamperedJSON)
  1171  	s.store.Commit(false, nil, nil)
  1172  	s.syncRemote(c)
  1173  	_, err = client.Update()
  1174  	assertWrongHash(c, err)
  1175  
  1176  	// update remote targets.json to have the wrong size
  1177  	targets.Signed.Type = "xxx"
  1178  	tamperedJSON, err = json.Marshal(targets)
  1179  	c.Assert(err, IsNil)
  1180  	s.store.SetMeta("targets.json", tamperedJSON)
  1181  	s.store.Commit(false, nil, nil)
  1182  	c.Assert(s.repo.Timestamp(), IsNil) // unless timestamp changes, the client doesn't even look at "targets.json"
  1183  	s.syncRemote(c)
  1184  	_, err = client.Update()
  1185  	c.Assert(err, DeepEquals, ErrWrongSize{"targets.json", int64(len(tamperedJSON)), int64(len(targetsJSON))})
  1186  }
  1187  
  1188  func (s *ClientSuite) TestUpdateHTTP(c *C) {
  1189  	tmp := c.MkDir()
  1190  
  1191  	// start file server
  1192  	addr, cleanup := startFileServer(c, tmp)
  1193  	defer cleanup()
  1194  
  1195  	for _, consistentSnapshot := range []bool{false, true} {
  1196  		dir := fmt.Sprintf("consistent-snapshot-%t", consistentSnapshot)
  1197  
  1198  		// generate repository
  1199  		repo := generateRepoFS(c, filepath.Join(tmp, dir), targetFiles, consistentSnapshot)
  1200  
  1201  		// initialize a client
  1202  		remote, err := HTTPRemoteStore(fmt.Sprintf("http://%s/%s/repository", addr, dir), nil, nil)
  1203  		c.Assert(err, IsNil)
  1204  		client := NewClient(MemoryLocalStore(), remote)
  1205  		rootMeta, err := repo.SignedMeta("root.json")
  1206  		c.Assert(err, IsNil)
  1207  		rootJsonBytes, err := json.Marshal(rootMeta)
  1208  		c.Assert(err, IsNil)
  1209  		c.Assert(client.Init(rootJsonBytes), IsNil)
  1210  
  1211  		// check update is ok
  1212  		targets, err := client.Update()
  1213  		c.Assert(err, IsNil)
  1214  		assertFiles(c, targets, []string{"foo.txt", "bar.txt", "baz.txt"})
  1215  
  1216  		// check can download files
  1217  		for name, data := range targetFiles {
  1218  			var dest testDestination
  1219  			c.Assert(client.Download(name, &dest), IsNil)
  1220  			c.Assert(dest.deleted, Equals, false)
  1221  			c.Assert(dest.String(), Equals, string(data))
  1222  		}
  1223  	}
  1224  }
  1225  
  1226  // TestRollbackSnapshot tests a rollback version of snapshot.
  1227  func (s *ClientSuite) TestRollbackSnapshot(c *C) {
  1228  	client := s.updatedClient(c)
  1229  
  1230  	// generate a new snapshot & timestamp v2 and sync with the client
  1231  	version := client.snapshotVer
  1232  	c.Assert(version > 0, Equals, true)
  1233  	c.Assert(s.repo.Snapshot(), IsNil)
  1234  	c.Assert(s.repo.Timestamp(), IsNil)
  1235  	c.Assert(s.repo.Commit(), IsNil)
  1236  	s.syncRemote(c)
  1237  	_, err := client.Update()
  1238  	c.Assert(err, IsNil)
  1239  	c.Assert(client.snapshotVer > version, Equals, true)
  1240  
  1241  	// replace remote snapshot.json with old version and timestamp again.
  1242  	s.repo.SetSnapshotVersion(version)
  1243  	c.Assert(s.repo.Snapshot(), IsNil)
  1244  	c.Assert(s.repo.Timestamp(), IsNil)
  1245  	c.Assert(s.repo.Commit(), IsNil)
  1246  	s.syncRemote(c)
  1247  
  1248  	// check update returns ErrLowVersion
  1249  	_, err = client.Update()
  1250  
  1251  	c.Assert(err, DeepEquals, verify.ErrLowVersion{
  1252  		Actual:  version,
  1253  		Current: client.snapshotVer,
  1254  	})
  1255  }
  1256  
  1257  func (s *ClientSuite) TestRollbackTopLevelTargets(c *C) {
  1258  	client := s.updatedClient(c)
  1259  
  1260  	// generate a new targets and sync with the client
  1261  	version := client.targetsVer
  1262  	c.Assert(version > 0, Equals, true)
  1263  	s.addRemoteTarget(c, "bar.txt")
  1264  	_, err := client.Update()
  1265  	c.Assert(err, IsNil)
  1266  	c.Assert(client.targetsVer > version, Equals, true)
  1267  
  1268  	// replace remote snapshot.json with old version and timestamp again.
  1269  	s.repo.SetTargetsVersion(version)
  1270  	c.Assert(s.repo.Snapshot(), IsNil)
  1271  	c.Assert(s.repo.Timestamp(), IsNil)
  1272  	c.Assert(s.repo.Commit(), IsNil)
  1273  	s.syncRemote(c)
  1274  
  1275  	// check update returns ErrLowVersion
  1276  	_, err = client.Update()
  1277  	c.Assert(err, DeepEquals, verify.ErrLowVersion{
  1278  		Actual:  version,
  1279  		Current: client.targetsVer,
  1280  	})
  1281  }
  1282  
  1283  func (s *ClientSuite) TestRollbackDelegatedTargets(c *C) {
  1284  	client := s.updatedClient(c)
  1285  	// add a delegation
  1286  	signer, err := keys.GenerateEd25519Key()
  1287  	c.Assert(err, IsNil)
  1288  	role := data.DelegatedRole{
  1289  		Name:      "role",
  1290  		KeyIDs:    signer.PublicData().IDs(),
  1291  		Paths:     []string{"bar.txt", "baz.txt"},
  1292  		Threshold: 1,
  1293  	}
  1294  	s.store.SaveSigner("role", signer)
  1295  	s.repo.AddDelegatedRole("targets", role, []*data.PublicKey{signer.PublicData()})
  1296  	s.repo.AddTargetToPreferredRole("bar.txt", nil, "role")
  1297  	c.Assert(s.repo.Snapshot(), IsNil)
  1298  	c.Assert(s.repo.Timestamp(), IsNil)
  1299  	c.Assert(s.repo.Commit(), IsNil)
  1300  	s.syncRemote(c)
  1301  
  1302  	// save v1 delegation
  1303  	meta, err := s.store.GetMeta()
  1304  	c.Assert(err, IsNil)
  1305  	oldRole, ok := meta["role.json"]
  1306  	if !ok {
  1307  		c.Fatal("missing role.json")
  1308  	}
  1309  	// update client and verify download delegated target
  1310  	_, err = client.Update()
  1311  	c.Assert(err, IsNil)
  1312  	var dest testDestination
  1313  	c.Assert(client.Download("bar.txt", &dest), IsNil)
  1314  
  1315  	// update delegation to v2
  1316  	s.repo.AddTargetToPreferredRole("baz.txt", nil, "role")
  1317  	c.Assert(s.repo.Snapshot(), IsNil)
  1318  	c.Assert(s.repo.Timestamp(), IsNil)
  1319  	c.Assert(s.repo.Commit(), IsNil)
  1320  	s.syncRemote(c)
  1321  
  1322  	// update client and verify download v2 delegated target
  1323  	_, err = client.Update()
  1324  	c.Assert(err, IsNil)
  1325  	c.Assert(dest.Delete(), IsNil)
  1326  	c.Assert(client.Download("baz.txt", &dest), IsNil)
  1327  
  1328  	// rollback role.json version.
  1329  	c.Assert(s.store.SetMeta("role.json", oldRole), IsNil)
  1330  	repo, err := tuf.NewRepo(s.store)
  1331  	c.Assert(err, IsNil)
  1332  	c.Assert(repo.Snapshot(), IsNil)
  1333  	c.Assert(repo.Timestamp(), IsNil)
  1334  	c.Assert(repo.Commit(), IsNil)
  1335  	s.syncRemote(c)
  1336  
  1337  	// check update returns ErrLowVersion
  1338  	_, err = client.Update()
  1339  	c.Assert(err, DeepEquals, verify.ErrLowVersion{
  1340  		Actual:  1,
  1341  		Current: 2,
  1342  	})
  1343  }
  1344  
  1345  type testDestination struct {
  1346  	bytes.Buffer
  1347  	deleted bool
  1348  }
  1349  
  1350  func (t *testDestination) Delete() error {
  1351  	t.deleted = true
  1352  	return nil
  1353  }
  1354  
  1355  func (s *ClientSuite) TestDownloadUnknownTarget(c *C) {
  1356  	client := s.updatedClient(c)
  1357  	var dest testDestination
  1358  	c.Assert(client.Download("nonexistent", &dest), Equals, ErrUnknownTarget{Name: "nonexistent", SnapshotVersion: 1})
  1359  	c.Assert(dest.deleted, Equals, true)
  1360  }
  1361  
  1362  func (s *ClientSuite) TestDownloadNoExist(c *C) {
  1363  	client := s.updatedClient(c)
  1364  	s.deleteTarget("foo.txt")
  1365  	var dest testDestination
  1366  	c.Assert(client.Download("foo.txt", &dest), Equals, ErrNotFound{"foo.txt"})
  1367  	c.Assert(dest.deleted, Equals, true)
  1368  }
  1369  
  1370  func (s *ClientSuite) TestDownloadOK(c *C) {
  1371  	client := s.updatedClient(c)
  1372  	// the filename is normalized if necessary
  1373  	for _, name := range []string{"/foo.txt", "foo.txt"} {
  1374  		var dest testDestination
  1375  		c.Assert(client.Download(name, &dest), IsNil)
  1376  		c.Assert(dest.deleted, Equals, false)
  1377  		c.Assert(dest.String(), Equals, "foo")
  1378  	}
  1379  }
  1380  
  1381  func (s *ClientSuite) TestDownloadWrongSize(c *C) {
  1382  	client := s.updatedClient(c)
  1383  	// Update with a file that's incorrect size.
  1384  	s.setRemoteTarget("foo.txt", []byte("wrong-size"))
  1385  	var dest testDestination
  1386  	c.Assert(client.Download("foo.txt", &dest), DeepEquals, ErrWrongSize{"foo.txt", 10, 3})
  1387  	c.Assert(dest.deleted, Equals, true)
  1388  }
  1389  
  1390  func (s *ClientSuite) TestDownloadTargetTooLong(c *C) {
  1391  	client := s.updatedClient(c)
  1392  	s.setRemoteTarget("foo.txt", []byte("foo-ooo"))
  1393  	var dest testDestination
  1394  	c.Assert(client.Download("foo.txt", &dest), DeepEquals, ErrWrongSize{"foo.txt", 7, 3})
  1395  	c.Assert(dest.deleted, Equals, true)
  1396  }
  1397  
  1398  func (s *ClientSuite) TestDownloadTargetTooShort(c *C) {
  1399  	client := s.updatedClient(c)
  1400  	s.setRemoteTarget("foo.txt", []byte("fo"))
  1401  	var dest testDestination
  1402  	c.Assert(client.Download("foo.txt", &dest), DeepEquals, ErrWrongSize{"foo.txt", 2, 3})
  1403  	c.Assert(dest.deleted, Equals, true)
  1404  }
  1405  
  1406  func (s *ClientSuite) TestDownloadTargetCorruptData(c *C) {
  1407  	client := s.updatedClient(c)
  1408  	s.setRemoteTarget("foo.txt", []byte("ooo"))
  1409  	var dest testDestination
  1410  	assertWrongHash(c, client.Download("foo.txt", &dest))
  1411  	c.Assert(dest.deleted, Equals, true)
  1412  }
  1413  
  1414  func (s *ClientSuite) TestAvailableTargets(c *C) {
  1415  	client := s.updatedClient(c)
  1416  	files, err := client.Targets()
  1417  	c.Assert(err, IsNil)
  1418  	assertFiles(c, files, []string{"foo.txt"})
  1419  
  1420  	s.addRemoteTarget(c, "bar.txt")
  1421  	s.addRemoteTarget(c, "baz.txt")
  1422  	_, err = client.Update()
  1423  	c.Assert(err, IsNil)
  1424  	files, err = client.Targets()
  1425  	c.Assert(err, IsNil)
  1426  	assertFiles(c, files, []string{"foo.txt", "bar.txt", "baz.txt"})
  1427  }
  1428  
  1429  func (s *ClientSuite) TestAvailableTarget(c *C) {
  1430  	client := s.updatedClient(c)
  1431  
  1432  	target, err := client.Target("foo.txt")
  1433  	c.Assert(err, IsNil)
  1434  	assertFile(c, target, "foo.txt")
  1435  
  1436  	target, err = client.Target("/foo.txt")
  1437  	c.Assert(err, IsNil)
  1438  	assertFile(c, target, "foo.txt")
  1439  
  1440  	_, err = client.Target("bar.txt")
  1441  	c.Assert(err, Equals, ErrNotFound{"bar.txt"})
  1442  
  1443  	_, err = client.Target("/bar.txt")
  1444  	c.Assert(err, Equals, ErrNotFound{"/bar.txt"})
  1445  }
  1446  
  1447  func (s *ClientSuite) TestUnknownKeyIDs(c *C) {
  1448  	// get local root.json
  1449  	meta, err := s.store.GetMeta()
  1450  	c.Assert(err, IsNil)
  1451  
  1452  	rootJSON, ok := meta["root.json"]
  1453  	c.Assert(ok, Equals, true)
  1454  
  1455  	var root struct {
  1456  		Signed     data.Root        `json:"signed"`
  1457  		Signatures []data.Signature `json:"signatures"`
  1458  	}
  1459  	c.Assert(json.Unmarshal(rootJSON, &root), IsNil)
  1460  
  1461  	// update remote root.json to add a new key with an unknown id
  1462  	signer, err := keys.GenerateEd25519Key()
  1463  	c.Assert(err, IsNil)
  1464  
  1465  	root.Signed.Keys["unknown-key-id"] = signer.PublicData()
  1466  
  1467  	// re-sign the root metadata, then commit it back into the store.
  1468  	signingKeys, err := s.store.GetSigners("root")
  1469  	c.Assert(err, IsNil)
  1470  
  1471  	signedRoot, err := sign.Marshal(root.Signed, signingKeys...)
  1472  	c.Assert(err, IsNil)
  1473  
  1474  	rootJSON, err = cjson.EncodeCanonical(signedRoot)
  1475  	c.Assert(err, IsNil)
  1476  
  1477  	s.store.SetMeta("root.json", rootJSON)
  1478  	s.store.Commit(false, nil, nil)
  1479  	s.syncRemote(c)
  1480  
  1481  	// FIXME(TUF-0.9) We need this for now because the client still uses
  1482  	// the TUF-0.9 update workflow, where we decide to update the root
  1483  	// metadata when we observe a new root through the snapshot.
  1484  	repo, err := tuf.NewRepo(s.store)
  1485  	c.Assert(err, IsNil)
  1486  	c.Assert(repo.Snapshot(), IsNil)
  1487  	c.Assert(repo.Timestamp(), IsNil)
  1488  	c.Assert(repo.Commit(), IsNil)
  1489  	s.syncRemote(c)
  1490  
  1491  	// Make sure the client can update with the unknown keyid.
  1492  	client := s.newClient(c)
  1493  	_, err = client.Update()
  1494  	c.Assert(err, IsNil)
  1495  }
  1496  
  1497  func generateRepoFS(c *C, dir string, files map[string][]byte, consistentSnapshot bool) *tuf.Repo {
  1498  	repo, err := tuf.NewRepo(tuf.FileSystemStore(dir, nil))
  1499  	c.Assert(err, IsNil)
  1500  	if !consistentSnapshot {
  1501  		c.Assert(repo.Init(false), IsNil)
  1502  	}
  1503  	for _, role := range []string{"root", "snapshot", "targets", "timestamp"} {
  1504  		_, err := repo.GenKey(role)
  1505  		c.Assert(err, IsNil)
  1506  	}
  1507  	for file, data := range files {
  1508  		path := filepath.Join(dir, "staged", "targets", file)
  1509  		c.Assert(os.MkdirAll(filepath.Dir(path), 0755), IsNil)
  1510  		c.Assert(os.WriteFile(path, data, 0644), IsNil)
  1511  		c.Assert(repo.AddTarget(file, nil), IsNil)
  1512  	}
  1513  	c.Assert(repo.Snapshot(), IsNil)
  1514  	c.Assert(repo.Timestamp(), IsNil)
  1515  	c.Assert(repo.Commit(), IsNil)
  1516  	return repo
  1517  }
  1518  
  1519  func (s *ClientSuite) TestVerifyDigest(c *C) {
  1520  	digest := "sha256:bc11b176a293bb341a0f2d0d226f52e7fcebd186a7c4dfca5fc64f305f06b94c"
  1521  	hash := "bc11b176a293bb341a0f2d0d226f52e7fcebd186a7c4dfca5fc64f305f06b94c"
  1522  	size := int64(42)
  1523  
  1524  	c.Assert(s.repo.AddTargetsWithDigest(hash, "sha256", size, digest, nil), IsNil)
  1525  	c.Assert(s.repo.Snapshot(), IsNil)
  1526  	c.Assert(s.repo.Timestamp(), IsNil)
  1527  	c.Assert(s.repo.Commit(), IsNil)
  1528  	s.syncRemote(c)
  1529  
  1530  	client := s.newClient(c)
  1531  	_, err := client.Update()
  1532  	c.Assert(err, IsNil)
  1533  
  1534  	c.Assert(client.VerifyDigest(hash, "sha256", size, digest), IsNil)
  1535  }
  1536  
  1537  type StateLessSuite struct{}
  1538  
  1539  var _ = Suite(&StateLessSuite{})
  1540  
  1541  func (s *StateLessSuite) TestRejectsMultiSignaturesSameKeyDifferentIDs(c *C) {
  1542  	// In this test Alice and Bob want to create a TUF repo
  1543  	// where a root key rotation would require both their signatures.
  1544  	// Alice uses an old version of Go-TUF where each key gets assigned several IDs.
  1545  	// Bob uses a modern version of Go-TUF that does not produce the same list of IDs for a same key.
  1546  	// This test checks that the TUF client
  1547  	// will not accept a root rotation
  1548  	// signed twice with Alice's key with different key IDs each time.
  1549  	// This test was failing with https://github.com/theupdateframework/go-tuf/tree/ac7b5d7bce18cca5a84a28b021bd6372f450b35b
  1550  	// because the signature verification code was assuming that the key IDs used in the metadata
  1551  	// were the same as the one the TUF library of the client would generate,
  1552  	// breaking the security of threshold signatures.
  1553  
  1554  	// The attack works just the same if Alice is malicious from the beginning
  1555  	// and convinces Bob to sign an initial "root.json"
  1556  	// with additional key IDs for her only key,
  1557  	// but this scenario show that the vulnerability can even impact situations
  1558  	// where Alice is not malicious at all,
  1559  	// she was simply using an old client and an attacker stole her key.
  1560  	// The purpose of threshold signatures in TUF is precisely
  1561  	// to make sure that an attacker cannot forge signatures
  1562  	// if they did not steal a large enough number of keys.
  1563  
  1564  	alice, err := keys.GenerateEd25519Key()
  1565  	if err != nil {
  1566  		panic(err)
  1567  	}
  1568  
  1569  	root := data.NewRoot()
  1570  	root.Version = 1
  1571  	root.Roles["root"] = &data.Role{
  1572  		KeyIDs:    []string{},
  1573  		Threshold: 2, // Note the threshold
  1574  	}
  1575  
  1576  	// reproduces how IDs were computed in
  1577  	// https://github.com/theupdateframework/go-tuf/blob/8e84384bebe3/data/types.go#L50
  1578  	oldTUFIDs := func(k *data.PublicKey) []string {
  1579  		bytes, _ := cjson.EncodeCanonical(k)
  1580  		digest := sha256.Sum256(bytes)
  1581  		ids := []string{hex.EncodeToString(digest[:])}
  1582  
  1583  		if k.Scheme != "" || len(k.Algorithms) != 0 {
  1584  			bytes, _ = cjson.EncodeCanonical(&data.PublicKey{
  1585  				Type:  k.Type,
  1586  				Value: k.Value,
  1587  			})
  1588  			digest = sha256.Sum256(bytes)
  1589  			ids = append(ids, hex.EncodeToString(digest[:]))
  1590  		}
  1591  
  1592  		return ids
  1593  	}
  1594  
  1595  	// Alice adds her key using an old version of go-tuf
  1596  	// which will use several IDs
  1597  	for _, keyID := range oldTUFIDs(alice.PublicData()) {
  1598  		root.Keys[keyID] = alice.PublicData()
  1599  		root.Roles["root"].KeyIDs = append(root.Roles["root"].KeyIDs, keyID)
  1600  	}
  1601  
  1602  	bob, err := keys.GenerateEd25519Key()
  1603  	if err != nil {
  1604  		panic(err)
  1605  	}
  1606  
  1607  	root.AddKey(bob.PublicData())
  1608  	root.Roles["root"].KeyIDs = append(
  1609  		root.Roles["root"].KeyIDs,
  1610  		bob.PublicData().IDs()...,
  1611  	)
  1612  
  1613  	// signer for the other roles, not important
  1614  	delegatedSigner, _ := keys.GenerateEd25519Key()
  1615  	root.AddKey(delegatedSigner.PublicData())
  1616  	for _, role := range []string{"targets", "snapshot", "timestamp"} {
  1617  		root.Roles[role] = &data.Role{
  1618  			KeyIDs:    delegatedSigner.PublicData().IDs(),
  1619  			Threshold: 1,
  1620  		}
  1621  	}
  1622  
  1623  	signedRoot, err := sign.Marshal(root, alice, bob)
  1624  	c.Assert(err, IsNil)
  1625  	rootJSON, err := json.Marshal(signedRoot)
  1626  	c.Assert(err, IsNil)
  1627  
  1628  	// producing evil root using only Alice's key
  1629  
  1630  	evilRoot := root
  1631  	evilRoot.Version = 2
  1632  
  1633  	canonical, err := cjson.EncodeCanonical(evilRoot)
  1634  	c.Assert(err, IsNil)
  1635  	sig, err := alice.SignMessage(canonical)
  1636  	c.Assert(err, IsNil)
  1637  	signedEvilRoot := &data.Signed{
  1638  		Signed:     canonical,
  1639  		Signatures: make([]data.Signature, 0),
  1640  	}
  1641  	for _, keyID := range oldTUFIDs(alice.PublicData()) {
  1642  		signedEvilRoot.Signatures = append(signedEvilRoot.Signatures, data.Signature{
  1643  			Signature: sig,
  1644  			KeyID:     keyID,
  1645  		})
  1646  	}
  1647  	evilRootJSON, err := json.Marshal(signedEvilRoot)
  1648  	c.Assert(err, IsNil)
  1649  
  1650  	// checking that client does not accept root rotation
  1651  	// to evil root
  1652  
  1653  	localStore := MemoryLocalStore()
  1654  	err = localStore.SetMeta("root.json", rootJSON)
  1655  	c.Assert(err, IsNil)
  1656  
  1657  	remoteStore := newFakeRemoteStore()
  1658  	remoteStore.meta["2.root.json"] = newFakeFile(evilRootJSON)
  1659  
  1660  	client := NewClient(localStore, remoteStore)
  1661  
  1662  	err = client.UpdateRoots()
  1663  	if err != nil {
  1664  		c.Assert(err, DeepEquals, verify.ErrRoleThreshold{Expected: 2, Actual: 1})
  1665  	} else {
  1666  		c.Fatalf("client returned no error when updating with evil root")
  1667  	}
  1668  }
  1669  

View as plain text