1// Copyright 2015 Keybase, Inc. All rights reserved. Use of
2// this source code is governed by the included BSD license.
3
4package libkb
5
6import (
7	"sync"
8
9	keybase1 "github.com/keybase/client/go/protocol/keybase1"
10)
11
12// SpecialKeyRing holds blessed keys, like the one Keybase uses to sign
13// its Merkle Root.
14type SpecialKeyRing struct {
15	Contextified
16
17	sync.Mutex
18
19	// Cache of keys that are used in verifying the root
20	keys map[keybase1.KID]GenericKey
21
22	// The only ones allowed for this purpose
23	validKIDs map[keybase1.KID]bool
24}
25
26// NewSpecialKeyRing allocates a new SpecialKeyRing with the given
27// vector of KIDs. For NaCl keys, it will actually import those
28// keys into the Keyring.
29func NewSpecialKeyRing(v []keybase1.KID, g *GlobalContext) *SpecialKeyRing {
30	ret := &SpecialKeyRing{
31		keys:         make(map[keybase1.KID]GenericKey),
32		validKIDs:    make(map[keybase1.KID]bool),
33		Contextified: NewContextified(g),
34	}
35	for _, kid := range v {
36		if key, _ := ImportKeypairFromKID(kid); key != nil {
37			ret.keys[kid] = key
38		}
39		ret.validKIDs[kid] = true
40	}
41	return ret
42
43}
44
45// IsValidKID returns if this KID is valid (blessed) according to this Keyring
46func (sk *SpecialKeyRing) IsValidKID(kid keybase1.KID) bool {
47	val, found := sk.validKIDs[kid]
48	return val && found
49}
50
51func LoadPGPKeyFromLocalDB(k keybase1.KID, g *GlobalContext) (*PGPKeyBundle, error) {
52	dbobj, err := g.LocalDb.Get(DbKey{
53		Typ: DBPGPKey,
54		Key: k.String(),
55	})
56	if err != nil {
57		return nil, err
58	}
59	if dbobj == nil {
60		return nil, nil
61	}
62	kb, w, err := GetOneKey(dbobj)
63	w.Warn(g)
64	return kb, err
65}
66
67// Load takes a blessed KID and returns, if possible, the GenericKey
68// associated with that KID, for signature verification. If the key isn't
69// found in memory or on disk (in the case of PGP), then it will attempt
70// to fetch the key from the keybase server.
71func (sk *SpecialKeyRing) Load(m MetaContext, kid keybase1.KID) (GenericKey, error) {
72	sk.Lock()
73	defer sk.Unlock()
74	m.Debug("+ SpecialKeyRing.Load(%s)", kid)
75
76	if !sk.IsValidKID(kid) {
77		err := UnknownSpecialKIDError{kid}
78		return nil, err
79	}
80
81	if key, found := sk.keys[kid]; found {
82		m.Debug("- SpecialKeyRing.Load(%s) -> hit inmem cache", kid)
83		return key, nil
84	}
85
86	key, err := LoadPGPKeyFromLocalDB(kid, m.G())
87
88	if err != nil || key == nil {
89
90		m.Debug("| Load(%s) going to network", kid)
91		var res *APIRes
92		res, err = sk.G().API.Get(m, APIArg{
93			Endpoint:    "key/special",
94			SessionType: APISessionTypeNONE,
95			Args: HTTPArgs{
96				"kid": S{kid.String()},
97			},
98		})
99		var w *Warnings
100		if err == nil {
101			key, w, err = GetOneKey(res.Body.AtKey("bundle"))
102		}
103		if err == nil {
104			w.Warn(sk.G())
105
106			if e2 := key.StoreToLocalDb(m.G()); e2 != nil {
107				m.Warning("Failed to store key: %s", e2)
108			}
109		}
110	} else {
111		m.Debug("| Load(%s) hit DB-backed cache", kid)
112	}
113
114	if err == nil && key != nil {
115		sk.keys[kid] = key
116	}
117
118	m.Debug("- SpecialKeyRing.Load(%s)", kid)
119
120	return key, err
121}
122