func (c Client) verifyRoot(role string, s *data.Signed, minVersion int) error { // this will confirm that the root has been signed by the old root role // with the root keys we bootstrapped with. // Still need to determine if there has been a root key update and // confirm signature with new root key logrus.Debug("verifying root with existing keys") rootRole, err := c.local.GetBaseRole(role) if err != nil { logrus.Debug("no previous root role loaded") return err } // Verify using the rootRole loaded from the known root.json if err = signed.Verify(s, rootRole, minVersion); err != nil { logrus.Debug("root did not verify with existing keys") return err } logrus.Debug("updating known root roles and keys") root, err := data.RootFromSigned(s) if err != nil { logrus.Error(err.Error()) return err } // replace the existing root.json with the new one (just in memory, we // have another validation step before we fully accept the new root) err = c.local.SetRoot(root) if err != nil { logrus.Error(err.Error()) return err } // Verify the new root again having loaded the rootRole out of this new // file (verifies self-referential integrity) // TODO(endophage): be more intelligent and only re-verify if we detect // there has been a change in root keys logrus.Debug("verifying root with updated keys") rootRole, err = c.local.GetBaseRole(role) if err != nil { logrus.Debug("root role with new keys not loaded") return err } err = signed.Verify(s, rootRole, minVersion) if err != nil { logrus.Debug("root did not verify with new keys") return err } logrus.Debug("successfully verified root") return nil }
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 }
// downloadTimestamp is responsible for downloading the timestamp.json // Timestamps are special in that we ALWAYS attempt to download and only // use cache if the download fails (and the cache is still valid). func (c *Client) downloadTimestamp() error { logrus.Debug("downloadTimestamp") role := data.RoleName("timestamp") // We may not have a cached timestamp if this is the first time // we're interacting with the repo. This will result in the // version being 0 var download bool old := &data.Signed{} version := 0 cachedTS, err := c.cache.GetMeta(role, maxSize) if err == nil { err := json.Unmarshal(cachedTS, old) if err == nil { ts, err := data.TimestampFromSigned(old) if err == nil { version = ts.Signed.Version } } else { old = nil } } // unlike root, targets and snapshot, always try and download timestamps // from remote, only using the cache one if we couldn't reach remote. raw, s, err := c.downloadSigned(role, maxSize, nil) if err != nil || len(raw) == 0 { if err, ok := err.(store.ErrMetaNotFound); ok { return err } if old == nil { if err == nil { // couldn't retrieve data from server and don't have valid // data in cache. return store.ErrMetaNotFound{} } return err } logrus.Debug("using cached timestamp") s = old } else { download = true } err = signed.Verify(s, role, version, c.keysDB) if err != nil { return err } logrus.Debug("successfully verified timestamp") if download { c.cache.SetMeta(role, raw) } ts, err := data.TimestampFromSigned(s) if err != nil { return err } c.local.SetTimestamp(ts) return nil }
// downloadTimestamp is responsible for downloading the timestamp.json // Timestamps are special in that we ALWAYS attempt to download and only // use cache if the download fails (and the cache is still valid). func (c *Client) downloadTimestamp() error { logrus.Debug("Downloading Timestamp...") role := data.CanonicalTimestampRole // We may not have a cached timestamp if this is the first time // we're interacting with the repo. This will result in the // version being 0 var ( saveToCache bool old *data.Signed version = 0 ) cachedTS, err := c.cache.GetMeta(role, maxSize) if err == nil { cached := &data.Signed{} err := json.Unmarshal(cachedTS, cached) if err == nil { ts, err := data.TimestampFromSigned(cached) if err == nil { version = ts.Signed.Version } old = cached } } // unlike root, targets and snapshot, always try and download timestamps // from remote, only using the cache one if we couldn't reach remote. raw, s, err := c.downloadSigned(role, maxSize, nil) if err != nil || len(raw) == 0 { if old == nil { if err == nil { // couldn't retrieve data from server and don't have valid // data in cache. return store.ErrMetaNotFound{Resource: data.CanonicalTimestampRole} } return err } logrus.Debug(err.Error()) logrus.Warn("Error while downloading remote metadata, using cached timestamp - this might not be the latest version available remotely") s = old } else { saveToCache = true } err = signed.Verify(s, role, version, c.keysDB) if err != nil { return err } logrus.Debug("successfully verified timestamp") if saveToCache { c.cache.SetMeta(role, raw) } ts, err := data.TimestampFromSigned(s) if err != nil { return err } c.local.SetTimestamp(ts) return nil }
// verifies that a timestamp is valid, and returned the SignedTimestamp object to add to the tuf repo func (c *Client) verifyTimestamp(s *data.Signed, minVersion int) (*data.SignedTimestamp, error) { timestampRole, err := c.local.GetBaseRole(data.CanonicalTimestampRole) if err != nil { logrus.Debug("no timestamp role loaded") return nil, err } if err := signed.Verify(s, timestampRole, minVersion); err != nil { return nil, err } return data.TimestampFromSigned(s) }
// This changes the root key func TestSwizzlerChangeRootKey(t *testing.T) { f, origMeta := createNewSwizzler(t) f.ChangeRootKey() tufRepo := tuf.NewRepo(f.CryptoService) // we want to test these in a specific order roles := []string{data.CanonicalRootRole, data.CanonicalTargetsRole, data.CanonicalSnapshotRole, data.CanonicalTimestampRole, "targets/a", "targets/a/b"} for _, role := range roles { origMeta := origMeta[role] newMeta, err := f.MetadataCache.GetMeta(role, -1) require.NoError(t, err) // the threshold for base roles is set in root switch role { case data.CanonicalRootRole: require.False(t, bytes.Equal(origMeta, newMeta)) origRoot, newRoot := &data.SignedRoot{}, &data.SignedRoot{} require.NoError(t, json.Unmarshal(origMeta, origRoot)) require.NoError(t, json.Unmarshal(newMeta, newRoot)) require.NotEqual(t, len(origRoot.Signed.Keys), len(newRoot.Signed.Keys)) var rootRole data.Role for r, origRole := range origRoot.Signed.Roles { newRole := newRoot.Signed.Roles[r] require.Len(t, origRole.KeyIDs, 1) require.Len(t, newRole.KeyIDs, 1) if r == data.CanonicalRootRole { require.NotEqual(t, origRole.KeyIDs[0], newRole.KeyIDs[0]) rootRole = data.Role{RootRole: *newRole, Name: data.CanonicalRootRole} } else { require.Equal(t, origRole.KeyIDs[0], newRole.KeyIDs[0]) } } require.NoError(t, tufRepo.SetRoot(newRoot)) signedThing, err := newRoot.ToSigned() require.NoError(t, err) newKey := newRoot.Signed.Keys[rootRole.KeyIDs[0]] require.NoError(t, signed.Verify(signedThing, data.BaseRole{Name: data.CanonicalRootRole, Keys: map[string]data.PublicKey{newKey.ID(): newKey}, Threshold: 1}, 1)) default: require.True(t, bytes.Equal(origMeta, newMeta), "bytes have changed for role %s", role) } } }
func (c Client) verifyRoot(role string, s *data.Signed, minVersion int) error { // this will confirm that the root has been signed by the old root role // as c.keysDB contains the root keys we bootstrapped with. // Still need to determine if there has been a root key update and // confirm signature with new root key logrus.Debug("verifying root with existing keys") err := signed.Verify(s, role, minVersion, c.keysDB) if err != nil { logrus.Debug("root did not verify with existing keys") return err } // This will cause keyDB to get updated, overwriting any keyIDs associated // with the roles in root.json logrus.Debug("updating known root roles and keys") root, err := data.RootFromSigned(s) if err != nil { logrus.Error(err.Error()) return err } err = c.local.SetRoot(root) if err != nil { logrus.Error(err.Error()) return err } // verify again now that the old keys have been replaced with the new keys. // TODO(endophage): be more intelligent and only re-verify if we detect // there has been a change in root keys logrus.Debug("verifying root with updated keys") err = signed.Verify(s, role, minVersion, c.keysDB) if err != nil { logrus.Debug("root did not verify with new keys") return err } logrus.Debug("successfully verified root") return nil }
func validateTargets(role string, roles map[string]storage.MetaUpdate, kdb *keys.KeyDB) (*data.SignedTargets, error) { // TODO: when delegations are being validated, validate parent // role exists for any delegation s := &data.Signed{} err := json.Unmarshal(roles[role].Data, s) if err != nil { return nil, fmt.Errorf("could not parse %s", role) } // 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 nil, err } t, err := data.TargetsFromSigned(s) if err != nil { return nil, err } if !data.ValidTUFType(t.Signed.Type, data.CanonicalTargetsRole) { return nil, fmt.Errorf("%s has wrong type", role) } return t, nil }
func validateTargets(role string, roles map[string]storage.MetaUpdate, repo *tuf.Repo) (*data.SignedTargets, error) { // TODO: when delegations are being validated, validate parent // role exists for any delegation s := &data.Signed{} err := json.Unmarshal(roles[role].Data, s) if err != nil { return nil, fmt.Errorf("could not parse %s", role) } // version specifically gets validated when writing to store to // better handle race conditions there. var targetOrDelgRole data.BaseRole if role == data.CanonicalTargetsRole { targetOrDelgRole, err = repo.GetBaseRole(role) if err != nil { logrus.Debugf("no %s role loaded", role) return nil, err } } else { delgRole, err := repo.GetDelegationRole(role) if err != nil { logrus.Debugf("no %s delegation role loaded", role) return nil, err } targetOrDelgRole = delgRole.BaseRole } if err := signed.Verify(s, targetOrDelgRole, 0); err != nil { return nil, err } t, err := data.TargetsFromSigned(s) if err != nil { return nil, err } if !data.ValidTUFType(t.Signed.Type, data.CanonicalTargetsRole) { return nil, fmt.Errorf("%s has wrong type", role) } return t, nil }
func (c Client) getTargetsFile(role string, keyIDs []string, snapshotMeta data.Files, consistent bool, threshold int) (*data.Signed, error) { // require role exists in snapshots roleMeta, ok := snapshotMeta[role] if !ok { return nil, ErrMissingMeta{role: role} } expectedSha256, ok := snapshotMeta[role].Hashes["sha256"] if !ok { return nil, ErrMissingMeta{role: role} } // try to get meta file from content addressed cache var download bool old := &data.Signed{} version := 0 raw, err := c.cache.GetMeta(role, roleMeta.Length) if err != nil || raw == nil { logrus.Debugf("Couldn't not find cached %s, must download", role) download = true } else { // file may have been tampered with on disk. Always check the hash! genHash := sha256.Sum256(raw) if !bytes.Equal(genHash[:], expectedSha256) { download = true } err := json.Unmarshal(raw, old) if err == nil { targ, err := data.TargetsFromSigned(old) if err == nil { version = targ.Signed.Version } else { download = true } } else { download = true } } size := snapshotMeta[role].Length var s *data.Signed if download { rolePath, err := c.RoleTargetsPath(role, hex.EncodeToString(expectedSha256), consistent) if err != nil { return nil, err } raw, s, err = c.downloadSigned(rolePath, size, expectedSha256) if err != nil { return nil, err } } else { logrus.Debug("using cached ", role) s = old } err = signed.Verify(s, role, version, c.keysDB) if err != nil { return nil, err } logrus.Debugf("successfully verified %s", role) if download { // if we error when setting meta, we should continue. err = c.cache.SetMeta(role, raw) if err != nil { logrus.Errorf("Failed to write snapshot to local cache: %s", err.Error()) } } return s, nil }
// 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 }
// verifies that a timestamp is valid, and returned the SignedTimestamp object to add to the tuf repo func (c *Client) verifyTimestamp(s *data.Signed, minVersion int, kdb *keys.KeyDB) (*data.SignedTimestamp, error) { if err := signed.Verify(s, data.CanonicalTimestampRole, minVersion, kdb); err != nil { return nil, err } return data.TimestampFromSigned(s) }
func (c Client) getTargetsFile(role string, snapshotMeta data.Files, consistent bool) (*data.Signed, error) { // require role exists in snapshots roleMeta, ok := snapshotMeta[role] if !ok { return nil, data.ErrMissingMeta{Role: role} } expectedSha256, ok := snapshotMeta[role].Hashes["sha256"] if !ok { return nil, data.ErrMissingMeta{Role: role} } // try to get meta file from content addressed cache var download bool old := &data.Signed{} version := 0 raw, err := c.cache.GetMeta(role, roleMeta.Length) if err != nil || raw == nil { logrus.Debugf("Couldn't not find cached %s, must download", role) download = true } else { // file may have been tampered with on disk. Always check the hash! genHash := sha256.Sum256(raw) if !bytes.Equal(genHash[:], expectedSha256) { download = true } err := json.Unmarshal(raw, old) if err == nil { targ, err := data.TargetsFromSigned(old, role) if err == nil { version = targ.Signed.Version } else { download = true } } else { download = true } } size := snapshotMeta[role].Length var s *data.Signed if download { raw, s, err = c.downloadSigned(role, size, expectedSha256) if err != nil { return nil, err } } else { logrus.Debug("using cached ", role) s = old } var targetOrDelgRole data.BaseRole if data.IsDelegation(role) { delgRole, err := c.local.GetDelegationRole(role) if err != nil { logrus.Debugf("no %s delegation role loaded", role) return nil, err } targetOrDelgRole = delgRole.BaseRole } else { targetOrDelgRole, err = c.local.GetBaseRole(role) if err != nil { logrus.Debugf("no %s role loaded", role) return nil, err } } if err = signed.Verify(s, targetOrDelgRole, version); err != nil { return nil, err } logrus.Debugf("successfully verified %s", role) if download { // if we error when setting meta, we should continue. err = c.cache.SetMeta(role, raw) if err != nil { logrus.Errorf("Failed to write %s to local cache: %s", role, err.Error()) } } return s, 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 }