// Init initializes a local repository. // // The latest root.json is fetched from remote storage, verified using rootKeys // and threshold, and then saved in local storage. It is expected that rootKeys // were securely distributed with the software being updated. func (c *Client) Init(rootKeys []*data.Key, threshold int) error { if len(rootKeys) < threshold { return ErrInsufficientKeys } rootJSON, err := c.downloadMetaUnsafe("root.json") if err != nil { return err } c.db = keys.NewDB() rootKeyIDs := make([]string, len(rootKeys)) for i, key := range rootKeys { id := key.ID() rootKeyIDs[i] = id if err := c.db.AddKey(id, key); err != nil { return err } } role := &data.Role{Threshold: threshold, KeyIDs: rootKeyIDs} if err := c.db.AddRole("root", role); err != nil { return err } if err := c.decodeRoot(rootJSON); err != nil { return err } return c.local.SetMeta("root.json", rootJSON) }
func (r *Repo) db() (*keys.DB, error) { db := keys.NewDB() root, err := r.root() if err != nil { return nil, err } for id, k := range root.Keys { if err := db.AddKey(id, k); err != nil { return nil, err } } for name, role := range root.Roles { if err := db.AddRole(name, role); err != nil { return nil, err } } return db, nil }
// getLocalMeta decodes and verifies metadata from local storage. // // The verification of local files is purely for consistency, if an attacker // has compromised the local storage, there is no guarantee it can be trusted. func (c *Client) getLocalMeta() error { meta, err := c.local.GetMeta() if err != nil { return err } if rootJSON, ok := meta["root.json"]; ok { // unmarshal root.json without verifying as we need the root // keys first s := &data.Signed{} if err := json.Unmarshal(rootJSON, s); err != nil { return err } root := &data.Root{} if err := json.Unmarshal(s.Signed, root); err != nil { return err } db := keys.NewDB() for id, k := range root.Keys { if err := db.AddKey(id, k); err != nil { return err } } for name, role := range root.Roles { if err := db.AddRole(name, role); err != nil { return err } } if err := signed.Verify(s, "root", 0, db); err != nil { return err } c.consistentSnapshot = root.ConsistentSnapshot c.db = db } else { return ErrNoRootKeys } if snapshotJSON, ok := meta["snapshot.json"]; ok { snapshot := &data.Snapshot{} if err := signed.UnmarshalTrusted(snapshotJSON, snapshot, "snapshot", c.db); err != nil { return err } c.snapshotVer = snapshot.Version } if targetsJSON, ok := meta["targets.json"]; ok { targets := &data.Targets{} if err := signed.UnmarshalTrusted(targetsJSON, targets, "targets", c.db); err != nil { return err } c.targetsVer = targets.Version c.targets = targets.Targets } if timestampJSON, ok := meta["timestamp.json"]; ok { timestamp := &data.Timestamp{} if err := signed.UnmarshalTrusted(timestampJSON, timestamp, "timestamp", c.db); err != nil { return err } c.timestampVer = timestamp.Version } c.localMeta = meta return nil }
func (VerifySuite) Test(c *C) { type test struct { name string keys []*data.Key roles map[string]*data.Role s *data.Signed ver int exp *time.Time typ string role string err error mut func(*test) } expiredTime := time.Now().Add(-time.Hour) minVer := 10 tests := []test{ { name: "no signatures", mut: func(t *test) { t.s.Signatures = []data.Signature{} }, err: ErrNoSignatures, }, { name: "unknown role", role: "foo", err: ErrUnknownRole, }, { name: "wrong signature method", mut: func(t *test) { t.s.Signatures[0].Method = "foo" }, err: ErrWrongMethod, }, { name: "signature wrong length", mut: func(t *test) { t.s.Signatures[0].Signature = []byte{0} }, err: ErrInvalid, }, { name: "key missing from role", mut: func(t *test) { t.roles["root"].KeyIDs = nil }, err: ErrRoleThreshold, }, { name: "invalid signature", mut: func(t *test) { t.s.Signatures[0].Signature = make([]byte, ed25519.SignatureSize) }, err: ErrInvalid, }, { name: "not enough signatures", mut: func(t *test) { t.roles["root"].Threshold = 2 }, err: ErrRoleThreshold, }, { name: "exactly enough signatures", }, { name: "more than enough signatures", mut: func(t *test) { k, _ := keys.NewKey() Sign(t.s, k.SerializePrivate()) t.keys = append(t.keys, k.Serialize()) t.roles["root"].KeyIDs = append(t.roles["root"].KeyIDs, k.ID) }, }, { name: "duplicate key id", mut: func(t *test) { t.roles["root"].Threshold = 2 t.s.Signatures = append(t.s.Signatures, t.s.Signatures[0]) }, err: ErrRoleThreshold, }, { name: "unknown key", mut: func(t *test) { k, _ := keys.NewKey() Sign(t.s, k.Serialize()) }, }, { name: "unknown key below threshold", mut: func(t *test) { k, _ := keys.NewKey() Sign(t.s, k.Serialize()) t.roles["root"].Threshold = 2 }, err: ErrRoleThreshold, }, { name: "unknown keys in db", mut: func(t *test) { k, _ := keys.NewKey() Sign(t.s, k.Serialize()) t.keys = append(t.keys, k.Serialize()) }, }, { name: "unknown keys in db below threshold", mut: func(t *test) { k, _ := keys.NewKey() Sign(t.s, k.Serialize()) t.keys = append(t.keys, k.Serialize()) t.roles["root"].Threshold = 2 }, err: ErrRoleThreshold, }, { name: "wrong type", typ: "bar", err: ErrWrongType, }, { name: "low version", ver: minVer - 1, err: ErrLowVersion{minVer - 1, minVer}, }, { name: "expired", exp: &expiredTime, err: ErrExpired{expiredTime}, }, } for _, t := range tests { if t.role == "" { t.role = "root" } if t.ver == 0 { t.ver = minVer } if t.exp == nil { expires := time.Now().Add(time.Hour) t.exp = &expires } if t.typ == "" { t.typ = t.role } if t.keys == nil && t.s == nil { k, _ := keys.NewKey() t.s, _ = Marshal(&signedMeta{Type: t.typ, Version: t.ver, Expires: *t.exp}, k.SerializePrivate()) t.keys = []*data.Key{k.Serialize()} } if t.roles == nil { t.roles = map[string]*data.Role{ "root": &data.Role{ KeyIDs: []string{t.keys[0].ID()}, Threshold: 1, }, } } if t.mut != nil { t.mut(&t) } db := keys.NewDB() for _, k := range t.keys { err := db.AddKey(k.ID(), k) c.Assert(err, IsNil) } for n, r := range t.roles { err := db.AddRole(n, r) c.Assert(err, IsNil) } err := Verify(t.s, t.role, minVer, db) if e, ok := t.err.(ErrExpired); ok { assertErrExpired(c, err, e) } else { c.Assert(err, DeepEquals, t.err, Commentf("name = %s", t.name)) } } }