1package libkb
2
3import (
4	sha512 "crypto/sha512"
5	json "encoding/json"
6	fmt "fmt"
7
8	keybase1 "github.com/keybase/client/go/protocol/keybase1"
9	jsonw "github.com/keybase/go-jsonw"
10)
11
12type resetLinkAndHash struct {
13	link keybase1.ResetLink
14	hash keybase1.SHA512
15}
16
17type unverifiedResetChain []resetLinkAndHash
18
19func importResetLinkAndHash(s string) (ret *resetLinkAndHash, err error) {
20	b := []byte(s)
21	hash := sha512.Sum512(b)
22	var link keybase1.ResetLink
23	err = json.Unmarshal(b, &link)
24	if err != nil {
25		return nil, err
26	}
27	ret = &resetLinkAndHash{
28		hash: hash[:],
29		link: link,
30	}
31	return ret, nil
32}
33
34func importResetChainFromServer(m MetaContext, jw *jsonw.Wrapper) (urc unverifiedResetChain, err error) {
35	defer m.VTrace(VLog1, "importResetChainFromServer", &err)()
36	if jw == nil || jw.IsNil() {
37		return nil, nil
38	}
39	var ret unverifiedResetChain
40	chainLen, err := jw.Len()
41	if err != nil {
42		return nil, err
43	}
44	for i := 0; i < chainLen; i++ {
45		s, err := jw.AtIndex(i).GetString()
46		if err != nil {
47			return nil, err
48		}
49		link, err := importResetLinkAndHash(s)
50		if err != nil {
51			return nil, err
52		}
53		ret = append(ret, *link)
54	}
55	return ret, nil
56}
57
58func parseV2LeafResetChainTail(jw *jsonw.Wrapper) (*MerkleResets, error) {
59	if jw == nil || jw.IsNil() {
60		return nil, nil
61	}
62	l, err := jw.Len()
63	if err != nil {
64		return nil, err
65	}
66	if l == 0 {
67		return nil, nil
68	}
69	if l != 2 {
70		return nil, MerkleClientError{m: "bad reset chain tail; expecting 2 items", t: merkleErrorBadResetChain}
71	}
72	var ct MerkleResetChainTail
73	if err := jw.AtIndex(0).UnmarshalAgain(&ct.Seqno); err != nil {
74		return nil, err
75	}
76	if err := jw.AtIndex(1).UnmarshalAgain(&ct.Hash); err != nil {
77		return nil, err
78	}
79	ret := &MerkleResets{chainTail: ct}
80	return ret, nil
81}
82
83type MerkleResetChainTail struct {
84	Seqno keybase1.Seqno
85	Hash  keybase1.SHA512
86}
87
88type MerkleResets struct {
89	chainTail MerkleResetChainTail
90	chain     []keybase1.ResetLink
91}
92
93func (mr *MerkleResets) verifyAndLoad(m MetaContext, urc unverifiedResetChain) (err error) {
94
95	// Don't even bother to do a CVTrace if the user hasn't reset at all
96	if mr == nil {
97		return nil
98	}
99
100	defer m.VTrace(VLog1, "MerkleResets#verifyAndLoad", &err)()
101
102	mkerr := func(f string, a ...interface{}) error {
103		return MerkleClientError{m: fmt.Sprintf(f, a...), t: merkleErrorBadResetChain}
104	}
105
106	hashEq := func(a, b keybase1.SHA512) bool {
107		if a == nil || b == nil {
108			return (a == nil && b == nil)
109		}
110		return a.Eq(b)
111	}
112
113	if int(mr.chainTail.Seqno) != len(urc) {
114		err = mkerr("bad reset chain length: %d != %d", int(mr.chainTail.Seqno), len(urc))
115		return err
116	}
117
118	// Verify the chain starting at the tail and going to the front.
119	curr := mr.chainTail.Hash
120	last := true
121	foundDelete := false
122	lastWasDelete := false
123
124	for i := len(urc) - 1; i >= 0; i-- {
125		resetSeqno := i + 1
126		link := urc[i].link
127		hash := urc[i].hash
128		if !hashEq(curr, hash) {
129			err = mkerr("hash chain mismatch at seqno %d", resetSeqno)
130			return err
131		}
132		if int(link.ResetSeqno) != resetSeqno {
133			err = mkerr("wrong seqno at seqno %d", resetSeqno)
134			return err
135		}
136		if link.Type == keybase1.ResetType_DELETE {
137			if last {
138				lastWasDelete = true
139			}
140			foundDelete = true
141		}
142		curr = link.Prev.Reset
143		last = false
144	}
145
146	// NOTE(max) 2018-03-19
147	// We should have checked that deletes were only visible at the end of the reset chain.
148	// However, there was a bug in the migrate script, and if you had an account that did
149	// several resets and then a delete, all were marked as deletes! This check isn't ideal, but
150	// it's good enough -- we just want to make sure that a delete is indeed a tombstone,
151	// and that if there are any deletes in the chain, then the last must be a delete.
152	if foundDelete && !lastWasDelete {
153		err = mkerr("found a delete that didn't tombstone the user")
154		return err
155	}
156
157	if curr != nil {
158		err = mkerr("expected first link in reset chain to have a null prev")
159		return err
160	}
161
162	// It all verified, now load it up, going front to back.
163	for _, e := range urc {
164		mr.chain = append(mr.chain, e.link)
165	}
166
167	return nil
168}
169