1package libkb
2
3import (
4	"fmt"
5	"time"
6
7	keybase1 "github.com/keybase/client/go/protocol/keybase1"
8)
9
10// leafSlot is a simple accessor that takes the given leaf, and the private/public type,
11// and returns the corresponding object.
12func leafSlot(leaf *MerkleGenericLeaf, typ keybase1.SeqType) *MerkleTriple {
13	if leaf == nil {
14		return nil
15	}
16	switch typ {
17	case keybase1.SeqType_PUBLIC:
18		return leaf.Public
19	case keybase1.SeqType_SEMIPRIVATE:
20		return leaf.Private
21	default:
22		return nil
23	}
24}
25
26// merkleSearchComparator is a comparator used to determine if the given leaf/root has overshot
27// the mark, or no. If overshoot, then return a true, otherwrise return a false. And error will
28// abort the attempt
29type merkleSearchComparator func(leaf *MerkleGenericLeaf, root *MerkleRoot) (bool, error)
30
31// lookup the max merkle root seqno from the server, or use a cached version if
32// possible (and it's less than a minute old).
33func lookupMaxMerkleSeqno(m MetaContext) (ret keybase1.Seqno, err error) {
34	defer m.Trace("lookupMaxMerkleSeqno", &err)()
35	cli := m.G().GetMerkleClient()
36	mr, err := cli.FetchRootFromServer(m, time.Minute)
37	if err != nil {
38		return ret, err
39	}
40	lastp := mr.Seqno()
41	if lastp == nil {
42		return ret, MerkleClientError{"unexpected nil max merkle seqno", merkleErrorBadRoot}
43	}
44	return *lastp, nil
45}
46
47// findFirstLeafWithComparer finds the first chronological leaf in the merkle tree for which
48// comparer(leaf, root) is true, where (leaf, root) is an historical (leaf,root) pair for this
49// id. It's assumed that that false values are all earlier in history than the true values. We're
50// looking for the point of transition. Start this search from the given prevRootSeqno, not from merkle seqno 1.
51// The algorithm is to jump forward, in exponentially larger jumps, until we find a root that has a leaf
52// that makes comparer true. Then, to binary search to find the exact [a,b] pair in which a makes the
53// comparer false, and b makes it true. The leaf and root that correspond to b are returned.
54func findFirstLeafWithComparer(m MetaContext, id keybase1.UserOrTeamID, comparator merkleSearchComparator, prevRootSeqno keybase1.Seqno) (leaf *MerkleGenericLeaf, root *MerkleRoot, err error) {
55	defer m.Trace(fmt.Sprintf("findFirstLeafWithComparer(%s,%d)", id, prevRootSeqno), &err)()
56
57	cli := m.G().GetMerkleClient()
58
59	if !cli.CanExamineHistoricalRoot(m, prevRootSeqno) {
60		return nil, nil, MerkleClientError{"can't operate on old merkle sequence number", merkleErrorOldTree}
61	}
62
63	low := prevRootSeqno
64	last, err := lookupMaxMerkleSeqno(m)
65	if err != nil {
66		return nil, nil, err
67	}
68	m.Debug("found max merkle root: %d", last)
69	var hi keybase1.Seqno
70	inc := keybase1.Seqno(1)
71
72	// First bump the hi pointer up to a merkle root that overshoots (or is equal to)
73	// the request chainSeqno. Don't go any higher than the last known Merkle seqno.
74	var found bool
75	var final bool
76	for hi = low + 1; !found && !final; hi += inc {
77		if hi > last {
78			hi = last
79			final = true
80		}
81		m.Debug("FFLWC: Expontential forward jump: trying %d", hi)
82		leaf, root, err = cli.LookupLeafAtSeqno(m, id, hi)
83		if err != nil {
84			return nil, nil, err
85		}
86		found, err = comparator(leaf, root)
87		if err != nil {
88			return nil, nil, err
89		}
90		if found || final {
91			// Still make sure we `break` so we don't wind up incrementing
92			// hi if we don't have to.
93			break
94		}
95		inc *= 2
96	}
97
98	if !found {
99		return nil, nil, MerkleClientError{fmt.Sprintf("given link can't be found even as high as Merkle Root %d", hi), merkleErrorNotFound}
100	}
101
102	m.Debug("FFLWC: Stopped at hi bookend; binary searching in [%d,%d]", low, hi)
103
104	// Now binary search between prevRootSeqno and the hi we just got
105	// to find the exact transition. Note that if we never enter this loop,
106	// we'll still have set leaf and root in the above loop. Interestingly, this is
107	// the most common case, since for most signatures, the next merkle root will
108	// contain the signature. In those cases, we don't even go into this loop
109	// (since hi = low + 1).
110	for hi-low > 1 {
111		mid := (hi + low) / 2
112		tmpLeaf, tmpRoot, err := cli.LookupLeafAtSeqno(m, id, mid)
113		if err != nil {
114			return nil, nil, err
115		}
116		found, err := comparator(tmpLeaf, tmpRoot)
117		if err != nil {
118			return nil, nil, err
119		}
120		if found {
121			hi = mid
122			leaf = tmpLeaf
123			root = tmpRoot
124		} else {
125			low = mid
126		}
127		m.Debug("FFLWC: Binary search: after update range is [%d,%d]", low, hi)
128	}
129	m.Debug("FFLWC: settling at final seqno: %d", hi)
130
131	return leaf, root, nil
132}
133
134// FindNextMerkleRootAfterRevoke loads the user for the given UID, and find the
135// next merkle root after the given key revocation happens. It uses the
136// parameter arg.Prev to figure out where to start looking and then keeps
137// searching forward until finding a leaf that matches arg.Loc.
138func FindNextMerkleRootAfterRevoke(m MetaContext, arg keybase1.FindNextMerkleRootAfterRevokeArg) (res keybase1.NextMerkleRootRes, err error) {
139
140	defer m.Trace(fmt.Sprintf("FindNextMerkleRootAfterRevoke(%+v)", arg), &err)()
141
142	var u *User
143	u, err = LoadUser(NewLoadUserArgWithMetaContext(m).WithUID(arg.Uid).WithPublicKeyOptional())
144	if err != nil {
145		return res, err
146	}
147
148	comparer := func(leaf *MerkleGenericLeaf, root *MerkleRoot) (bool, error) {
149		slot := leafSlot(leaf, keybase1.SeqType_PUBLIC)
150		if slot == nil {
151			return false, MerkleClientError{fmt.Sprintf("leaf at root %d returned with nil public part", *root.Seqno()), merkleErrorBadLeaf}
152		}
153		m.Debug("Comprator at Merkle root %d: found chain location is %d; searching for %d", *root.Seqno(), slot.Seqno, arg.Loc.Seqno)
154		return (slot.Seqno >= arg.Loc.Seqno), nil
155	}
156
157	leaf, root, err := findFirstLeafWithComparer(m, arg.Uid.AsUserOrTeam(), comparer, arg.Prev.Seqno)
158	if err != nil {
159		return res, err
160	}
161	if leaf == nil || root == nil {
162		return res, MerkleClientError{"no suitable leaf found", merkleErrorNoUpdates}
163	}
164	sigID := u.GetSigIDFromSeqno(leaf.Public.Seqno)
165	if sigID.IsNil() {
166		return res, MerkleClientError{fmt.Sprintf("unknown seqno in sigchain: %d", arg.Loc.Seqno), merkleErrorBadSeqno}
167	}
168	if !sigID.StripSuffix().Eq(leaf.Public.SigID) {
169		return res, MerkleClientError{fmt.Sprintf("sigID sent down by server didn't match: %s != %s", sigID.String(), string(leaf.Public.SigID)), merkleErrorBadSigID}
170	}
171	res.Res = &keybase1.MerkleRootV2{
172		HashMeta: root.HashMeta(),
173		Seqno:    *root.Seqno(),
174	}
175	m.Debug("res.Res: %+v", *res.Res)
176	return res, nil
177}
178
179func FindNextMerkleRootAfterReset(m MetaContext, arg keybase1.FindNextMerkleRootAfterResetArg) (res keybase1.NextMerkleRootRes, err error) {
180	defer m.Trace(fmt.Sprintf("FindNextMerkleRootAfterReset(%+v)", arg), &err)()
181
182	comparer := func(leaf *MerkleGenericLeaf, root *MerkleRoot) (bool, error) {
183		user := leaf.userExtras
184		if user == nil {
185			return false, MerkleClientError{fmt.Sprintf("for root %d, expected a user leaf, didn't get one", *root.Seqno()), merkleErrorBadLeaf}
186		}
187		if user.resets == nil {
188			return false, nil
189		}
190		return (user.resets.chainTail.Seqno >= arg.ResetSeqno), nil
191	}
192	leaf, root, err := findFirstLeafWithComparer(m, arg.Uid.AsUserOrTeam(), comparer, arg.Prev.Seqno)
193	if err != nil {
194		return res, err
195	}
196	if leaf == nil || root == nil {
197		return res, MerkleClientError{"no suitable leaf found", merkleErrorNoUpdates}
198	}
199	res.Res = &keybase1.MerkleRootV2{
200		HashMeta: root.HashMeta(),
201		Seqno:    *root.Seqno(),
202	}
203	m.Debug("res.Res: %+v", *res.Res)
204	return res, nil
205}
206
207func FindNextMerkleRootAfterTeamRemoval(m MetaContext, arg keybase1.FindNextMerkleRootAfterTeamRemovalArg) (res keybase1.NextMerkleRootRes, err error) {
208	defer m.Trace(fmt.Sprintf("FindNextMerkleRootAfterTeamRemoval(%+v)", arg), &err)()
209	comparer := func(leaf *MerkleGenericLeaf, root *MerkleRoot) (bool, error) {
210		var trip *MerkleTriple
211		if arg.IsPublic {
212			trip = leaf.Public
213		} else {
214			trip = leaf.Private
215		}
216		if trip == nil {
217			return false, MerkleClientError{fmt.Sprintf("No leaf found for team %v", arg.Team), merkleErrorNotFound}
218		}
219		return (trip.Seqno >= arg.TeamSigchainSeqno), nil
220	}
221	leaf, root, err := findFirstLeafWithComparer(m, arg.Team.AsUserOrTeam(), comparer, arg.Prev.Seqno)
222	if err != nil {
223		return res, err
224	}
225	if leaf == nil || root == nil {
226		return res, MerkleClientError{"no suitable leaf found", merkleErrorNoUpdates}
227	}
228	res.Res = &keybase1.MerkleRootV2{
229		HashMeta: root.HashMeta(),
230		Seqno:    *root.Seqno(),
231	}
232	m.Debug("res.Res: %+v", *res.Res)
233	return res, nil
234}
235
236func VerifyMerkleRootAndKBFS(m MetaContext, arg keybase1.VerifyMerkleRootAndKBFSArg) (err error) {
237
238	defer m.Trace(fmt.Sprintf("VerifyMerkleRootAndKBFS(%+v)", arg), &err)()
239
240	var mr *MerkleRoot
241	mr, err = m.G().GetMerkleClient().LookupRootAtSeqno(m, arg.Root.Seqno)
242	if err != nil {
243		return nil
244	}
245	if mr == nil {
246		return MerkleClientError{"no merkle root found", merkleErrorNotFound}
247	}
248
249	if !mr.HashMeta().Eq(arg.Root.HashMeta) {
250		return MerkleClientError{"wrong hash meta", merkleErrorHashMeta}
251	}
252
253	var received keybase1.KBFSRootHash
254	switch arg.ExpectedKBFSRoot.TreeID {
255	case keybase1.MerkleTreeID_KBFS_PUBLIC:
256		received = mr.payload.unpacked.Body.Kbfs.Public.Root
257	case keybase1.MerkleTreeID_KBFS_PRIVATE:
258		received = mr.payload.unpacked.Body.Kbfs.Private.Root
259	case keybase1.MerkleTreeID_KBFS_PRIVATETEAM:
260		received = mr.payload.unpacked.Body.Kbfs.PrivateTeam.Root
261	default:
262		return MerkleClientError{"unknown KBFS tree ID", merkleErrorKBFSBadTree}
263	}
264
265	if received == nil || arg.ExpectedKBFSRoot.Root == nil {
266		if received != nil || arg.ExpectedKBFSRoot.Root != nil {
267			return MerkleClientError{"KBFS hash mismatch; nil hash", merkleErrorKBFSMismatch}
268		}
269		return nil
270	}
271	if !received.Eq(arg.ExpectedKBFSRoot.Root) {
272		return MerkleClientError{"KBFS hash mismatch", merkleErrorKBFSMismatch}
273	}
274
275	return nil
276}
277
278// Verify that the given link has been posted to the merkle tree.
279// Used to detect a malicious server silently dropping sigchain link posts.
280func MerkleCheckPostedUserSig(mctx MetaContext, uid keybase1.UID,
281	seqno keybase1.Seqno, linkID LinkID) (err error) {
282	defer mctx.Trace(fmt.Sprintf("MerkleCheckPostedUserSig(%v, %v, %v)", uid, seqno, linkID.String()), &err)()
283	for _, forcePoll := range []bool{false, true} {
284		upak, _, err := mctx.G().GetUPAKLoader().LoadV2(
285			NewLoadUserArgWithMetaContext(mctx).WithPublicKeyOptional().
286				WithUID(uid).WithForcePoll(forcePoll))
287		if err != nil {
288			return err
289		}
290		if foundLinkID, found := upak.SeqnoLinkIDs[seqno]; found {
291			if foundLinkID.Eq(linkID.Export()) {
292				return nil
293			}
294		}
295	}
296	return fmt.Errorf("sigchain link not found at seqno %v", seqno)
297}
298