func validateSnapshot(role string, oldSnap *data.SignedSnapshot, snapUpdate storage.MetaUpdate, roles map[string]storage.MetaUpdate, kdb *keys.KeyDB) error { s := &data.Signed{} err := json.Unmarshal(snapUpdate.Data, s) if err != nil { return errors.New("could not parse snapshot") } // version specifically gets validated when writing to store to // better handle race conditions there. if err := signed.Verify(s, role, 0, kdb); err != nil { return err } snap, err := data.SnapshotFromSigned(s) if err != nil { return errors.New("could not parse snapshot") } if !data.ValidTUFType(snap.Signed.Type, data.CanonicalSnapshotRole) { return errors.New("snapshot has wrong type") } err = checkSnapshotEntries(role, oldSnap, snap, roles) if err != nil { return err } return nil }
func TestValidateTargetsModifiedHash(t *testing.T) { repo, cs, err := testutils.EmptyRepo("docker.com/notary") assert.NoError(t, err) store := storage.NewMemStorage() r, tg, sn, ts, err := testutils.Sign(repo) assert.NoError(t, err) snap, err := data.SnapshotFromSigned(sn) assert.NoError(t, err) snap.Signed.Meta["targets"].Hashes["sha256"][0] = snap.Signed.Meta["targets"].Hashes["sha256"][0] ^ 0xff sn, err = snap.ToSigned() assert.NoError(t, err) root, targets, snapshot, timestamp, err := getUpdates(r, tg, sn, ts) assert.NoError(t, err) updates := []storage.MetaUpdate{root, targets, snapshot, timestamp} copyTimestampKey(t, repo, store, "testGUN") _, err = validateUpdate(cs, "testGUN", updates, store) assert.Error(t, err) assert.IsType(t, validation.ErrBadSnapshot{}, err) }
func TestValidateTargetsModifiedHash(t *testing.T) { gun := "docker.com/notary" repo, cs, err := testutils.EmptyRepo(gun) require.NoError(t, err) store := storage.NewMemStorage() r, tg, sn, ts, err := testutils.Sign(repo) require.NoError(t, err) snap, err := data.SnapshotFromSigned(sn) require.NoError(t, err) snap.Signed.Meta["targets"].Hashes["sha256"][0] = snap.Signed.Meta["targets"].Hashes["sha256"][0] ^ 0xff sn, err = snap.ToSigned() require.NoError(t, err) root, targets, snapshot, timestamp, err := getUpdates(r, tg, sn, ts) require.NoError(t, err) updates := []storage.MetaUpdate{root, targets, snapshot, timestamp} serverCrypto := testutils.CopyKeys(t, cs, data.CanonicalTimestampRole) _, err = validateUpdate(serverCrypto, gun, updates, store) require.Error(t, err) require.IsType(t, validation.ErrBadSnapshot{}, err) }
func TestValidateTargetsModifiedHash(t *testing.T) { _, repo, _ := testutils.EmptyRepo() store := storage.NewMemStorage() r, tg, sn, ts, err := testutils.Sign(repo) assert.NoError(t, err) snap, err := data.SnapshotFromSigned(sn) assert.NoError(t, err) snap.Signed.Meta["targets"].Hashes["sha256"][0] = snap.Signed.Meta["targets"].Hashes["sha256"][0] ^ 0xff sn, err = snap.ToSigned() assert.NoError(t, err) root, targets, snapshot, timestamp, err := testutils.Serialize(r, tg, sn, ts) assert.NoError(t, err) updates := []storage.MetaUpdate{ { Role: "root", Version: 1, Data: root, }, { Role: "targets", Version: 1, Data: targets, }, { Role: "snapshot", Version: 1, Data: snapshot, }, { Role: "timestamp", Version: 1, Data: timestamp, }, } err = validateUpdate("testGUN", updates, store) assert.Error(t, err) assert.IsType(t, ErrBadSnapshot{}, err) }
func (rb *repoBuilder) loadSnapshot(content []byte, minVersion int, allowExpired bool) error { roleName := data.CanonicalSnapshotRole snapshotRole, err := rb.repo.Root.BuildBaseRole(roleName) if err != nil { // this should never happen, since it's already been validated return err } signedObj, err := rb.bytesToSignedAndValidateSigs(snapshotRole, content) if err != nil { return err } signedSnapshot, err := data.SnapshotFromSigned(signedObj) if err != nil { return err } if err := signed.VerifyVersion(&(signedSnapshot.Signed.SignedCommon), minVersion); err != nil { return err } if !allowExpired { // check must go at the end because all other validation should pass if err := signed.VerifyExpiry(&(signedSnapshot.Signed.SignedCommon), roleName); err != nil { return err } } // at this point, the only thing left to validate is existing checksums - we can use // this snapshot to bootstrap the next builder if needed - and we don't need to do // the 2-value assignment since we've already validated the signedSnapshot, which MUST // have root metadata rootMeta := signedSnapshot.Signed.Meta[data.CanonicalRootRole] rb.nextRootChecksum = &rootMeta if err := rb.validateChecksumsFromSnapshot(signedSnapshot); err != nil { return err } rb.repo.Snapshot = signedSnapshot return nil }
func TestValidateSnapshotGenerateWithPrev(t *testing.T) { gun := "docker.com/notary" repo, cs, err := testutils.EmptyRepo(gun) require.NoError(t, err) store := storage.NewMemStorage() snapRole, err := repo.GetBaseRole(data.CanonicalSnapshotRole) require.NoError(t, err) for _, k := range snapRole.Keys { err := store.SetKey(gun, data.CanonicalSnapshotRole, k.Algorithm(), k.Public()) require.NoError(t, err) } r, tg, sn, ts, err := testutils.Sign(repo) require.NoError(t, err) root, targets, snapshot, _, err := getUpdates(r, tg, sn, ts) require.NoError(t, err) updates := []storage.MetaUpdate{root, targets} // set the current snapshot in the store manually so we find it when generating // the next version store.UpdateCurrent(gun, snapshot) prev, err := data.SnapshotFromSigned(sn) require.NoError(t, err) serverCrypto := testutils.CopyKeys(t, cs, data.CanonicalTimestampRole, data.CanonicalSnapshotRole) updates, err = validateUpdate(serverCrypto, gun, updates, store) require.NoError(t, err) for _, u := range updates { if u.Role == data.CanonicalSnapshotRole { curr := &data.SignedSnapshot{} err = json.Unmarshal(u.Data, curr) require.NoError(t, err) require.Equal(t, prev.Signed.Version+1, curr.Signed.Version) require.Equal(t, u.Version, curr.Signed.Version) } } }
func TestValidateSnapshotGenerateWithPrev(t *testing.T) { kdb, repo, cs, err := testutils.EmptyRepo("docker.com/notary") assert.NoError(t, err) store := storage.NewMemStorage() snapRole := kdb.GetRole(data.CanonicalSnapshotRole) for _, id := range snapRole.KeyIDs { k := kdb.GetKey(id) assert.NotNil(t, k) err := store.SetKey("testGUN", data.CanonicalSnapshotRole, k.Algorithm(), k.Public()) assert.NoError(t, err) } r, tg, sn, ts, err := testutils.Sign(repo) assert.NoError(t, err) root, targets, snapshot, _, err := getUpdates(r, tg, sn, ts) assert.NoError(t, err) updates := []storage.MetaUpdate{root, targets} // set the current snapshot in the store manually so we find it when generating // the next version store.UpdateCurrent("testGUN", snapshot) prev, err := data.SnapshotFromSigned(sn) assert.NoError(t, err) copyTimestampKey(t, kdb, store, "testGUN") updates, err = validateUpdate(cs, "testGUN", updates, store) assert.NoError(t, err) for _, u := range updates { if u.Role == data.CanonicalSnapshotRole { curr := &data.SignedSnapshot{} err = json.Unmarshal(u.Data, curr) assert.Equal(t, prev.Signed.Version+1, curr.Signed.Version) assert.Equal(t, u.Version, curr.Signed.Version) } } }
// downloadSnapshot is responsible for downloading the snapshot.json func (c *Client) downloadSnapshot() error { logrus.Debug("downloadSnapshot") role := data.RoleName("snapshot") if c.local.Timestamp == nil { return ErrMissingMeta{role: "snapshot"} } size := c.local.Timestamp.Signed.Meta[role].Length expectedSha256, ok := c.local.Timestamp.Signed.Meta[role].Hashes["sha256"] if !ok { return ErrMissingMeta{role: "snapshot"} } var download bool old := &data.Signed{} version := 0 raw, err := c.cache.GetMeta(role, size) if raw == nil || err != nil { logrus.Debug("no snapshot in cache, must download") download = true } else { // file may have been tampered with on disk. Always check the hash! genHash := sha256.Sum256(raw) if !bytes.Equal(genHash[:], expectedSha256) { logrus.Debug("hash of snapshot in cache did not match expected hash, must download") download = true } err := json.Unmarshal(raw, old) if err == nil { snap, err := data.TimestampFromSigned(old) if err == nil { version = snap.Signed.Version } else { logrus.Debug("Could not parse Signed part of snapshot, must download") download = true } } else { logrus.Debug("Could not parse snapshot, must download") download = true } } var s *data.Signed if download { raw, s, err = c.downloadSigned(role, size, expectedSha256) if err != nil { return err } } else { logrus.Debug("using cached snapshot") s = old } err = signed.Verify(s, role, version, c.keysDB) if err != nil { return err } logrus.Debug("successfully verified snapshot") snap, err := data.SnapshotFromSigned(s) if err != nil { return err } c.local.SetSnapshot(snap) if download { err = c.cache.SetMeta(role, raw) if err != nil { logrus.Errorf("Failed to write snapshot to local cache: %s", err.Error()) } } return nil }
// downloadSnapshot is responsible for downloading the snapshot.json func (c *Client) downloadSnapshot() error { logrus.Debug("Downloading Snapshot...") role := data.CanonicalSnapshotRole if c.local.Timestamp == nil { return tuf.ErrNotLoaded{Role: data.CanonicalTimestampRole} } size := c.local.Timestamp.Signed.Meta[role].Length expectedHashes := c.local.Timestamp.Signed.Meta[role].Hashes if len(expectedHashes) == 0 { return data.ErrMissingMeta{Role: data.CanonicalSnapshotRole} } var download bool old := &data.Signed{} version := 0 raw, err := c.cache.GetMeta(role, size) if raw == nil || err != nil { logrus.Debug("no snapshot in cache, must download") download = true } else { // file may have been tampered with on disk. Always check the hash! if err := data.CheckHashes(raw, expectedHashes); err != nil { logrus.Debug("hash of snapshot in cache did not match expected hash, must download") download = true } err := json.Unmarshal(raw, old) if err == nil { snap, err := data.SnapshotFromSigned(old) if err == nil { version = snap.Signed.Version } else { logrus.Debug("Could not parse Signed part of snapshot, must download") download = true } } else { logrus.Debug("Could not parse snapshot, must download") download = true } } var s *data.Signed if download { raw, s, err = c.downloadSigned(role, size, expectedHashes) if err != nil { return err } } else { logrus.Debug("using cached snapshot") s = old } snapshotRole, err := c.local.GetBaseRole(role) if err != nil { logrus.Debug("no snapshot role loaded") return err } err = signed.Verify(s, snapshotRole, version) if err != nil { return err } logrus.Debug("successfully verified snapshot") snap, err := data.SnapshotFromSigned(s) if err != nil { return err } c.local.SetSnapshot(snap) if download { err = c.cache.SetMeta(role, raw) if err != nil { logrus.Errorf("Failed to write snapshot to local cache: %s", err.Error()) } } return nil }