// downloadTargets is responsible for downloading any targets file // including delegates roles. func (c *Client) downloadTargets(role string) error { role = data.RoleName(role) // this will really only do something for base targets role if c.local.Snapshot == nil { return ErrMissingMeta{role: role} } snap := c.local.Snapshot.Signed root := c.local.Root.Signed r := c.keysDB.GetRole(role) if r == nil { return fmt.Errorf("Invalid role: %s", role) } keyIDs := r.KeyIDs s, err := c.getTargetsFile(role, keyIDs, snap.Meta, root.ConsistentSnapshot, r.Threshold) if err != nil { logrus.Error("Error getting targets file:", err) return err } t, err := data.TargetsFromSigned(s) if err != nil { return err } err = c.local.SetTargets(role, t) if err != nil { return err } return nil }
// checkRoot determines if the hash, and size are still those reported // in the snapshot file. It will also check the expiry, however, if the // hash and size in snapshot are unchanged but the root file has expired, // there is little expectation that the situation can be remedied. func (c Client) checkRoot() error { role := data.RoleName("root") size := c.local.Snapshot.Signed.Meta[role].Length hashSha256 := c.local.Snapshot.Signed.Meta[role].Hashes["sha256"] raw, err := c.cache.GetMeta("root", size) if err != nil { return err } hash := sha256.Sum256(raw) if !bytes.Equal(hash[:], hashSha256) { return fmt.Errorf("Cached root sha256 did not match snapshot root sha256") } if int64(len(raw)) != size { return fmt.Errorf("Cached root size did not match snapshot size") } root := &data.SignedRoot{} err = json.Unmarshal(raw, root) if err != nil { return ErrCorruptedCache{file: "root.json"} } if signed.IsExpired(root.Signed.Expires) { return tuf.ErrLocalRootExpired{} } return nil }
// check the snapshot is present. If it is, the hierarchy // of the update is OK. This seems like a simplistic check // but is completely sufficient for all possible use cases: // 1. the user is updating only the snapshot. // 2. the user is updating a targets (incl. delegations) or // root metadata. This requires they also provide a new // snapshot. // N.B. users should never be updating timestamps. The server // always handles timestamping. If the user does send a // timestamp, the server will replace it on next // GET timestamp.jsonshould it detect the current // snapshot has a different hash to the one in the timestamp. func hierarchyOK(roles map[string]storage.MetaUpdate) error { snapshotRole := data.RoleName(data.CanonicalSnapshotRole) if _, ok := roles[snapshotRole]; !ok { return errors.New("snapshot missing from update") } 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 }
func checkSnapshotEntries(role string, oldSnap, snap *data.SignedSnapshot, roles map[string]storage.MetaUpdate) error { snapshotRole := data.RoleName(data.CanonicalSnapshotRole) timestampRole := data.RoleName(data.CanonicalTimestampRole) // just in case for r, update := range roles { if r == snapshotRole || r == timestampRole { continue } m, ok := snap.Signed.Meta[r] if !ok { return fmt.Errorf("snapshot missing metadata for %s", r) } if int64(len(update.Data)) != m.Length { return fmt.Errorf("snapshot has incorrect length for %s", r) } if !checkHashes(m, update.Data) { return fmt.Errorf("snapshot has incorrect hashes for %s", r) } } return 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 }
// downloadRoot is responsible for downloading the root.json func (c *Client) downloadRoot() error { role := data.RoleName("root") size := maxSize var expectedSha256 []byte if c.local.Snapshot != nil { size = c.local.Snapshot.Signed.Meta[role].Length expectedSha256 = c.local.Snapshot.Signed.Meta[role].Hashes["sha256"] } // if we're bootstrapping we may not have a cached root, an // error will result in the "previous root version" being // interpreted as 0. var download bool var err error var cachedRoot []byte old := &data.Signed{} version := 0 if expectedSha256 != nil { // can only trust cache if we have an expected sha256 to trust cachedRoot, err = c.cache.GetMeta(role, size) } if cachedRoot == nil || err != nil { logrus.Debug("didn't find a cached root, must download") download = true } else { hash := sha256.Sum256(cachedRoot) if !bytes.Equal(hash[:], expectedSha256) { logrus.Debug("cached root's hash didn't match expected, must download") download = true } err := json.Unmarshal(cachedRoot, old) if err == nil { root, err := data.RootFromSigned(old) if err == nil { version = root.Signed.Version } else { logrus.Debug("couldn't parse Signed part of cached root, must download") download = true } } else { logrus.Debug("couldn't parse cached root, must download") download = true } } var s *data.Signed var raw []byte if download { raw, s, err = c.downloadSigned(role, size, expectedSha256) if err != nil { return err } } else { logrus.Debug("using cached root") s = old } if err := c.verifyRoot(role, s, version); err != nil { return err } if download { logrus.Debug("caching downloaded root") // Now that we have accepted new root, write it to cache if err = c.cache.SetMeta(role, raw); err != nil { logrus.Errorf("Failed to write root to local cache: %s", err.Error()) } } return nil }
// validateUpload checks that the updates being pushed // are semantically correct and the signatures are correct func validateUpdate(gun string, updates []storage.MetaUpdate, store storage.MetaStore) error { kdb := keys.NewDB() repo := tuf.NewRepo(kdb, nil) rootRole := data.RoleName(data.CanonicalRootRole) targetsRole := data.RoleName(data.CanonicalTargetsRole) snapshotRole := data.RoleName(data.CanonicalSnapshotRole) // check that the necessary roles are present: roles := make(map[string]storage.MetaUpdate) for _, v := range updates { roles[v.Role] = v } if err := hierarchyOK(roles); err != nil { logrus.Error("ErrBadHierarchy: ", err.Error()) return ErrBadHierarchy{msg: err.Error()} } logrus.Debug("Successfully validated hierarchy") var root *data.SignedRoot oldRootJSON, err := store.GetCurrent(gun, rootRole) if _, ok := err.(*storage.ErrNotFound); err != nil && !ok { // problem with storage. No expectation we can // write if we can't read so bail. logrus.Error("error reading previous root: ", err.Error()) return err } if rootUpdate, ok := roles[rootRole]; ok { // if root is present, validate its integrity, possibly // against a previous root if root, err = validateRoot(gun, oldRootJSON, rootUpdate.Data); err != nil { logrus.Error("ErrBadRoot: ", err.Error()) return ErrBadRoot{msg: err.Error()} } // setting root will update keys db if err = repo.SetRoot(root); err != nil { logrus.Error("ErrValidation: ", err.Error()) return ErrValidation{msg: err.Error()} } logrus.Debug("Successfully validated root") } else { if oldRootJSON == nil { return ErrValidation{msg: "no pre-existing root and no root provided in update."} } parsedOldRoot := &data.SignedRoot{} if err := json.Unmarshal(oldRootJSON, parsedOldRoot); err != nil { return ErrValidation{msg: "pre-existing root is corrupted and no root provided in update."} } if err = repo.SetRoot(parsedOldRoot); err != nil { logrus.Error("ErrValidation: ", err.Error()) return ErrValidation{msg: err.Error()} } } // TODO: validate delegated targets roles. var t *data.SignedTargets if _, ok := roles[targetsRole]; ok { if t, err = validateTargets(targetsRole, roles, kdb); err != nil { logrus.Error("ErrBadTargets: ", err.Error()) return ErrBadTargets{msg: err.Error()} } repo.SetTargets(targetsRole, t) } logrus.Debug("Successfully validated targets") var oldSnap *data.SignedSnapshot oldSnapJSON, err := store.GetCurrent(gun, snapshotRole) if _, ok := err.(*storage.ErrNotFound); err != nil && !ok { // problem with storage. No expectation we can // write if we can't read so bail. logrus.Error("error reading previous snapshot: ", err.Error()) return err } else if err == nil { oldSnap = &data.SignedSnapshot{} if err := json.Unmarshal(oldSnapJSON, oldSnap); err != nil { oldSnap = nil } } if err := validateSnapshot(snapshotRole, oldSnap, roles[snapshotRole], roles, kdb); err != nil { logrus.Error("ErrBadSnapshot: ", err.Error()) return ErrBadSnapshot{msg: err.Error()} } logrus.Debug("Successfully validated snapshot") return nil }
// checkRoot returns true if no rotation, or a valid // rotation has taken place, and the threshold number of signatures // are valid. func checkRoot(oldRoot, newRoot *data.SignedRoot) error { rootRole := data.RoleName(data.CanonicalRootRole) targetsRole := data.RoleName(data.CanonicalTargetsRole) snapshotRole := data.RoleName(data.CanonicalSnapshotRole) timestampRole := data.RoleName(data.CanonicalTimestampRole) var oldRootRole *data.RootRole newRootRole, ok := newRoot.Signed.Roles[rootRole] if !ok { return errors.New("new root is missing role entry for root role") } oldThreshold := 1 rotation := false oldKeys := map[string]data.PublicKey{} newKeys := map[string]data.PublicKey{} if oldRoot != nil { // check for matching root key IDs oldRootRole = oldRoot.Signed.Roles[rootRole] oldThreshold = oldRootRole.Threshold for _, kid := range oldRootRole.KeyIDs { k, ok := oldRoot.Signed.Keys[kid] if !ok { // if the key itself wasn't contained in the root // we're skipping it because it could never have // been used to validate this root. continue } oldKeys[kid] = data.NewPublicKey(k.Algorithm(), k.Public()) } // super simple check for possible rotation rotation = len(oldKeys) != len(newRootRole.KeyIDs) } // if old and new had the same number of keys, iterate // to see if there's a difference. for _, kid := range newRootRole.KeyIDs { k, ok := newRoot.Signed.Keys[kid] if !ok { // if the key itself wasn't contained in the root // we're skipping it because it could never have // been used to validate this root. continue } newKeys[kid] = data.NewPublicKey(k.Algorithm(), k.Public()) if oldRoot != nil { if _, ok := oldKeys[kid]; !ok { // if there is any difference in keys, a key rotation may have // occurred. rotation = true } } } newSigned, err := newRoot.ToSigned() if err != nil { return err } if rotation { err = signed.VerifyRoot(newSigned, oldThreshold, oldKeys) if err != nil { return fmt.Errorf("rotation detected and new root was not signed with at least %d old keys", oldThreshold) } } err = signed.VerifyRoot(newSigned, newRootRole.Threshold, newKeys) if err != nil { return err } root, err := data.RootFromSigned(newSigned) if err != nil { return err } // at a minimum, check the 4 required roles are present for _, r := range []string{rootRole, targetsRole, snapshotRole, timestampRole} { role, ok := root.Signed.Roles[r] if !ok { return fmt.Errorf("missing required %s role from root", r) } if role.Threshold < 1 { return fmt.Errorf("%s role has invalid threshold", r) } if len(role.KeyIDs) < role.Threshold { return fmt.Errorf("%s role has insufficient number of keys", r) } } return nil }