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	"github.com/keybase/client/go/gregor"
8	keybase1 "github.com/keybase/client/go/protocol/keybase1"
9)
10
11type IdentifyState struct {
12	Contextified
13	res      *IdentifyOutcome
14	u        *User
15	track    *TrackLookup
16	tmpTrack *TrackLookup
17}
18
19func NewIdentifyStateWithGregorItem(g *GlobalContext, item gregor.Item, u *User) IdentifyState {
20	res := NewIdentifyOutcome(g, u.GetNormalizedName(), u.GetUID(), u.GetCurrentEldestSeqno())
21	res.ResponsibleGregorItem = item
22	return IdentifyState{Contextified: NewContextified(g), res: res, u: u}
23}
24
25func (s *IdentifyState) SetTrackLookup(t *TrackChainLink) {
26	s.track = NewTrackLookup(s.G(), t)
27	if s.res != nil {
28		s.res.TrackUsed = s.track
29	}
30}
31
32func (s *IdentifyState) SetTmpTrackLookup(t *TrackChainLink) {
33	s.tmpTrack = NewTrackLookup(s.G(), t)
34}
35
36func (s *IdentifyState) TrackLookup() *TrackLookup {
37	return s.track
38}
39
40func (s *IdentifyState) HasPreviousTrack() bool {
41	return s.track != nil
42}
43
44func (s *IdentifyState) Result() *IdentifyOutcome {
45	return s.res
46}
47
48func (s *IdentifyState) TmpTrackLookup() *TrackLookup {
49	return s.tmpTrack
50}
51
52func (s *IdentifyState) computeRevokedProofs(rhook func(TrackIDComponent, TrackDiff)) {
53	if s.track == nil {
54		return
55	}
56
57	found := s.res.TrackSet()
58
59	tracked := s.track.set
60
61	// These are the proofs that user previously tracked that
62	// are not in the current profile:
63	diff := tracked.Subtract(*found)
64
65	for _, e := range diff {
66		if e.GetProofState() != keybase1.ProofState_OK {
67			continue
68		}
69
70		// A proof that was previously tracked as GOOD
71		// is missing, so it has been REVOKED.
72		revokedDetail := ExportTrackIDComponentToRevokedProof(e)
73		var td TrackDiff
74		if s.tmpTrack == nil {
75			td = &TrackDiffRevoked{e}
76		} else {
77			// There is a snoozed track in s.tmpTrack.
78			// The user could have snoozed the revoked proof already.
79			// Check s.tmpTrack to see if that is the case.
80			if s.tmpTrack.set.HasMember(e) {
81				// proof was in snooze, too, so mark it as revoked.
82				td = &TrackDiffRevoked{e}
83			} else {
84				// proof wasn't in snooze, so revoked proof already snoozed.
85				td = &TrackDiffSnoozedRevoked{e}
86				revokedDetail.Snoozed = true
87			}
88		}
89		if td != nil {
90			s.res.Revoked = append(s.res.Revoked, td)
91			if rhook != nil {
92				rhook(e, td)
93			}
94		}
95		s.res.RevokedDetails = append(s.res.RevokedDetails, revokedDetail)
96	}
97}
98
99func (s *IdentifyState) initResultList() {
100	idt := s.u.IDTable()
101	if idt == nil {
102		return
103	}
104	activeProofs := idt.remoteProofLinks.Active()
105	s.res.ProofChecks = make([]*LinkCheckResult, len(activeProofs))
106	for i, p := range activeProofs {
107		s.res.ProofChecks[i] = &LinkCheckResult{link: p, trackedProofState: keybase1.ProofState_NONE, position: i}
108	}
109}
110
111func (s *IdentifyState) computeTrackDiffs() {
112	if s.track == nil {
113		return
114	}
115
116	s.G().Log.Debug("| with tracking %v", s.track.set)
117	for _, c := range s.res.ProofChecks {
118		c.diff = c.link.ComputeTrackDiff(s.track)
119		c.trackedProofState = s.track.GetProofState(c.link.ToIDString())
120		if s.tmpTrack != nil {
121			c.tmpTrackedProofState = s.tmpTrack.GetProofState(c.link.ToIDString())
122			c.tmpTrackExpireTime = s.tmpTrack.GetTmpExpireTime()
123		}
124	}
125}
126
127func (s *IdentifyState) Precompute(dhook func(keybase1.IdentifyKey) error, rhook func(TrackIDComponent, TrackDiff)) {
128	s.computeKeyDiffs(dhook)
129	s.initResultList()
130	s.computeTrackDiffs()
131	s.computeRevokedProofs(rhook)
132}
133
134func (s *IdentifyState) getLastDelegationSig(kid keybase1.KID) (ret keybase1.SigID) {
135	ckf := s.u.GetComputedKeyFamily()
136	if ckf == nil {
137		return ret
138	}
139	cki := ckf.getCkiUnchecked(kid)
140	if cki == nil {
141		return ret
142	}
143	dels := cki.DelegationsList
144	if len(dels) == 0 {
145		return ret
146	}
147	return dels[len(dels)-1].SigID
148}
149
150func (s *IdentifyState) computeKeyDiffs(dhook func(keybase1.IdentifyKey) error) {
151	mapify := func(v []keybase1.KID) map[keybase1.KID]bool {
152		ret := make(map[keybase1.KID]bool)
153		for _, k := range v {
154			ret[k] = true
155		}
156		return ret
157	}
158
159	display := func(kid keybase1.KID, diff TrackDiff) {
160		k := keybase1.IdentifyKey{
161			TrackDiff: ExportTrackDiff(diff),
162		}
163		k.KID = kid
164		if fp, ok := s.u.GetKeyFamily().kid2pgp[kid]; ok {
165			k.PGPFingerprint = fp[:]
166		}
167
168		// Get the last signature chronologically that delegated to
169		// this key.
170		k.SigID = s.getLastDelegationSig(kid)
171
172		// Anything other than a no difference here should be displayed to
173		// the user.
174		if diff != nil {
175			k.BreaksTracking = diff.BreaksTracking()
176		}
177		err := dhook(k)
178		if err != nil {
179			s.G().Log.Debug("computeKeyDiffs: dhook error: %+v", err)
180		}
181	}
182
183	// first check the eldest key
184	observedEldest := s.u.GetEldestKID()
185	if s.track != nil {
186		trackedEldest := s.track.GetEldestKID()
187		if observedEldest.NotEqual(trackedEldest) ||
188			s.u.GetCurrentEldestSeqno() > s.track.GetTrackedLinkSeqno() {
189			diff := TrackDiffNewEldest{tracked: trackedEldest, observed: observedEldest}
190			s.res.KeyDiffs = append(s.res.KeyDiffs, diff)
191			display(observedEldest, diff)
192		}
193	}
194
195	found := s.u.GetActivePGPKIDs(true)
196	foundMap := mapify(found)
197	var tracked []keybase1.KID
198	if s.track != nil {
199		for _, trackedKey := range s.track.GetTrackedKeys() {
200			tracked = append(tracked, trackedKey.KID)
201		}
202	}
203	trackedMap := mapify(tracked)
204
205	for _, kid := range found {
206		var diff TrackDiff
207		if s.track != nil && !trackedMap[kid] {
208			diff = TrackDiffNew{}
209			s.res.KeyDiffs = append(s.res.KeyDiffs, diff)
210		} else if s.track != nil {
211			diff = TrackDiffNone{}
212		}
213		display(kid, diff)
214	}
215
216	for _, kid := range tracked {
217		if !foundMap[kid] {
218			fp := s.u.GetKeyFamily().kid2pgp[kid]
219			diff := TrackDiffRevoked{fp}
220			s.res.KeyDiffs = append(s.res.KeyDiffs, diff)
221			// the identify outcome should know that this
222			// key was revoked, as well as there being
223			// a KeyDiff:
224			s.res.Revoked = append(s.res.Revoked, diff)
225			display(kid, diff)
226		}
227	}
228}
229