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	"crypto/hmac"
8	"crypto/sha256"
9	"encoding/hex"
10	"encoding/json"
11	"fmt"
12	"strings"
13	"time"
14
15	keybase1 "github.com/keybase/client/go/protocol/keybase1"
16	stellar1 "github.com/keybase/client/go/protocol/stellar1"
17	jsonw "github.com/keybase/go-jsonw"
18)
19
20type TypedChainLink interface {
21	GetRevocations() []keybase1.SigID
22	GetRevokeKids() []keybase1.KID
23	insertIntoTable(tab *IdentityTable)
24	GetSigID() keybase1.SigID
25	GetArmoredSig() string
26	markRevoked(l TypedChainLink)
27	ToDebugString() string
28	Type() string
29	ToDisplayString() string
30	IsRevocationIsh() bool
31	IsRevoked() bool
32	IsDirectlyRevoked() bool
33	GetRole() KeyRole
34	GetSeqno() keybase1.Seqno
35	GetCTime() time.Time
36	GetETime() time.Time
37	GetPGPFingerprint() *PGPFingerprint
38	GetPGPFullHash() string
39	GetKID() keybase1.KID
40	IsInCurrentFamily(u *User) bool
41	GetUsername() string
42	GetUID() keybase1.UID
43	GetDelegatedKid() keybase1.KID
44	GetMerkleHashMeta() (keybase1.HashMeta, error)
45	GetParentKid() keybase1.KID
46	VerifyReverseSig(ckf ComputedKeyFamily) error
47	GetMerkleSeqno() keybase1.Seqno
48	GetFirstAppearedMerkleSeqnoUnverified() keybase1.Seqno
49	GetDevice() *Device
50	DoOwnNewLinkFromServerNotifications(g *GlobalContext)
51	ToSigChainLocation() keybase1.SigChainLocation
52}
53
54//=========================================================================
55// GenericChainLink
56//
57
58type GenericChainLink struct {
59	*ChainLink
60}
61
62func (g *GenericChainLink) GetSigID() keybase1.SigID {
63	return g.unpacked.sigID
64}
65func (g *GenericChainLink) ToSigChainLocation() keybase1.SigChainLocation {
66	return g.ChainLink.ToSigChainLocation()
67}
68func (g *GenericChainLink) Type() string            { return "generic" }
69func (g *GenericChainLink) ToDisplayString() string { return "unknown" }
70func (g *GenericChainLink) insertIntoTable(tab *IdentityTable) {
71	tab.insertLink(g)
72}
73func (g *GenericChainLink) markRevoked(r TypedChainLink) {
74	g.revoked = true
75}
76func (g *GenericChainLink) ToDebugString() string {
77	return fmt.Sprintf("uid=%s, seq=%d, link=%s", g.Parent().uid, g.unpacked.seqno, g.id)
78}
79
80func (g *GenericChainLink) GetDelegatedKid() (kid keybase1.KID)          { return }
81func (g *GenericChainLink) GetParentKid() (kid keybase1.KID)             { return }
82func (g *GenericChainLink) VerifyReverseSig(ckf ComputedKeyFamily) error { return nil }
83func (g *GenericChainLink) IsRevocationIsh() bool                        { return false }
84func (g *GenericChainLink) GetRole() KeyRole                             { return DLGNone }
85func (g *GenericChainLink) IsRevoked() bool                              { return g.revoked }
86func (g *GenericChainLink) IsDirectlyRevoked() bool {
87	// Same as IsRevoked, but should not be overridden by subclasses (as
88	// TrackChainLink does with IsRevoked). E.g. if in the future
89	// SibkeyChainLink decides to return IsRevoked=true when the delegated
90	// sibkey has been revoked *by KID*, that could be fine, but
91	// IsDirectlyRevoked should still return false in that case.
92	return g.revoked
93}
94func (g *GenericChainLink) GetSeqno() keybase1.Seqno { return g.unpacked.seqno }
95func (g *GenericChainLink) GetPGPFingerprint() *PGPFingerprint {
96	return g.unpacked.pgpFingerprint
97}
98func (g *GenericChainLink) GetPGPFullHash() string { return "" }
99
100func (g *GenericChainLink) GetArmoredSig() string {
101	return g.unpacked.sig
102}
103func (g *GenericChainLink) GetUsername() string {
104	return g.unpacked.username
105}
106func (g *GenericChainLink) GetUID() keybase1.UID {
107	return g.unpacked.uid
108}
109
110func (g *GenericChainLink) GetDevice() *Device { return nil }
111
112func (g *GenericChainLink) extractPGPFullHash(loc string) string {
113	if jw := g.UnmarshalPayloadJSON().AtPath("body." + loc + ".full_hash"); !jw.IsNil() {
114		if ret, err := jw.GetString(); err == nil {
115			return ret
116		}
117	}
118	return ""
119}
120
121func (g *GenericChainLink) DoOwnNewLinkFromServerNotifications(glob *GlobalContext) {}
122
123func CanonicalProofName(t TypedChainLink) string {
124	return strings.ToLower(t.ToDisplayString())
125}
126
127//
128//=========================================================================
129
130//=========================================================================
131// Web of Trust
132
133type WotVouchChainLink struct {
134	GenericChainLink
135	ExpansionID string
136	Revocations []keybase1.SigID
137}
138
139func (cl *WotVouchChainLink) DoOwnNewLinkFromServerNotifications(g *GlobalContext) {}
140func (cl *WotVouchChainLink) Type() string                                         { return string(LinkTypeWotVouch) }
141
142var _ TypedChainLink = (*WotVouchChainLink)(nil)
143
144func ParseWotVouch(base GenericChainLink) (ret *WotVouchChainLink, err error) {
145	body := base.UnmarshalPayloadJSON()
146	expansionID, err := body.AtPath("body.wot_vouch").GetString()
147	if err != nil {
148		return nil, err
149	}
150	return &WotVouchChainLink{
151		GenericChainLink: base,
152		ExpansionID:      expansionID,
153		Revocations:      base.GetRevocations(),
154	}, nil
155}
156
157type WotReactChainLink struct {
158	GenericChainLink
159	ExpansionID string
160}
161
162func (cl *WotReactChainLink) DoOwnNewLinkFromServerNotifications(g *GlobalContext) {}
163func (cl *WotReactChainLink) Type() string                                         { return string(LinkTypeWotReact) }
164
165var _ TypedChainLink = (*WotReactChainLink)(nil)
166
167func ParseWotReact(base GenericChainLink) (ret *WotReactChainLink, err error) {
168	body := base.UnmarshalPayloadJSON()
169	expansionID, err := body.AtPath("body.wot_react").GetString()
170	if err != nil {
171		return nil, err
172	}
173	return &WotReactChainLink{
174		GenericChainLink: base,
175		ExpansionID:      expansionID,
176	}, nil
177}
178
179type sigExpansion struct {
180	Key string      `json:"key"`
181	Obj interface{} `json:"obj"`
182}
183
184// ExtractExpansionObj extracts the `obj` field from a sig expansion and verifies the
185// hash of the content matches the expected id. This is reusable beyond WotVouchChainLink.
186func ExtractExpansionObj(expansionID string, expansionJSON string) (expansionObj []byte, err error) {
187	var expansions map[string]sigExpansion
188	err = json.Unmarshal([]byte(expansionJSON), &expansions)
189	if err != nil {
190		return nil, err
191	}
192	expansion, ok := expansions[expansionID]
193	if !ok {
194		return nil, fmt.Errorf("expansion %s does not exist", expansionID)
195	}
196
197	// verify the hash of the expansion object payload matches the expension id
198	objBytes, err := json.Marshal(expansion.Obj)
199	if err != nil {
200		return nil, err
201	}
202	hmacKey, err := hex.DecodeString(expansion.Key)
203	if err != nil {
204		return nil, err
205	}
206	mac := hmac.New(sha256.New, hmacKey)
207	if _, err := mac.Write(objBytes); err != nil {
208		return nil, err
209	}
210	sum := mac.Sum(nil)
211	expectedID := hex.EncodeToString(sum)
212	if expectedID != expansionID {
213		return nil, fmt.Errorf("expansion id doesn't match expected value %s != %s", expansionID, expectedID)
214	}
215	return objBytes, nil
216}
217
218func EmbedExpansionObj(statement *jsonw.Wrapper) (expansion *jsonw.Wrapper, sum []byte, err error) {
219	outer := jsonw.NewDictionary()
220	inner := jsonw.NewDictionary()
221	if err := inner.SetKey("obj", statement); err != nil {
222		return nil, nil, err
223	}
224	randKey, err := RandBytes(16)
225	if err != nil {
226		return nil, nil, err
227	}
228	hexKey := hex.EncodeToString(randKey)
229	if err := inner.SetKey("key", jsonw.NewString(hexKey)); err != nil {
230		return nil, nil, err
231	}
232	marshaled, err := statement.Marshal()
233	if err != nil {
234		return nil, nil, err
235	}
236	mac := hmac.New(sha256.New, randKey)
237	if _, err := mac.Write(marshaled); err != nil {
238		return nil, nil, err
239	}
240	sum = mac.Sum(nil)
241	if err := outer.SetKey(hex.EncodeToString(sum), inner); err != nil {
242		return nil, nil, err
243	}
244	return outer, sum, nil
245}
246
247//=========================================================================
248// Remote, Web and Social
249//
250type RemoteProofChainLink interface {
251	TypedChainLink
252	DisplayPriorityKey() string
253	TableKey() string
254	LastWriterWins() bool
255	GetRemoteUsername() string
256	GetHostname() string
257	GetProtocol() string
258	DisplayCheck(m MetaContext, ui IdentifyUI, lcr LinkCheckResult) error
259	ToTrackingStatement(keybase1.ProofState) (*jsonw.Wrapper, error)
260	CheckDataJSON() *jsonw.Wrapper
261	ToIDString() string
262	ToKeyValuePair() (string, string)
263	ComputeTrackDiff(tl *TrackLookup) TrackDiff
264	GetProofType() keybase1.ProofType
265	ProofText() string
266}
267
268type WebProofChainLink struct {
269	GenericChainLink
270	protocol  string
271	hostname  string
272	proofText string
273}
274
275type SocialProofChainLink struct {
276	GenericChainLink
277	service   string
278	username  string
279	proofText string
280	// signifies a GENERIC_SOCIAL link from a parameterized proof
281	isGeneric bool
282}
283
284func (w *WebProofChainLink) DisplayPriorityKey() string {
285	return w.protocol
286}
287
288func (w *WebProofChainLink) TableKey() string {
289	if w.protocol == "https" {
290		return "http"
291	}
292	return w.protocol
293}
294
295func (w *WebProofChainLink) GetProofType() keybase1.ProofType {
296	if w.protocol == "dns" {
297		return keybase1.ProofType_DNS
298	}
299	return keybase1.ProofType_GENERIC_WEB_SITE
300}
301
302func (w *WebProofChainLink) ToTrackingStatement(state keybase1.ProofState) (*jsonw.Wrapper, error) {
303	ret := w.BaseToTrackingStatement(state)
304	remoteProofToTrackingStatement(w, ret)
305	return ret, nil
306}
307
308func (w *WebProofChainLink) DisplayCheck(m MetaContext, ui IdentifyUI, lcr LinkCheckResult) error {
309	return ui.FinishWebProofCheck(m, ExportRemoteProof(w), lcr.Export())
310}
311
312func (w *WebProofChainLink) Type() string { return "proof" }
313func (w *WebProofChainLink) insertIntoTable(tab *IdentityTable) {
314	remoteProofInsertIntoTable(w, tab)
315}
316func (w *WebProofChainLink) ToDisplayString() string {
317	return w.protocol + "://" + w.hostname
318}
319func (w *WebProofChainLink) LastWriterWins() bool      { return false }
320func (w *WebProofChainLink) GetRemoteUsername() string { return "" }
321func (w *WebProofChainLink) GetHostname() string       { return w.hostname }
322func (w *WebProofChainLink) GetProtocol() string       { return w.protocol }
323func (w *WebProofChainLink) ProofText() string         { return w.proofText }
324
325func (w *WebProofChainLink) CheckDataJSON() *jsonw.Wrapper {
326	ret := jsonw.NewDictionary()
327	if w.protocol == "dns" {
328		_ = ret.SetKey("protocol", jsonw.NewString(w.protocol))
329		_ = ret.SetKey("domain", jsonw.NewString(w.hostname))
330
331	} else {
332		_ = ret.SetKey("protocol", jsonw.NewString(w.protocol+":"))
333		_ = ret.SetKey("hostname", jsonw.NewString(w.hostname))
334	}
335	return ret
336}
337func (w *WebProofChainLink) ToIDString() string { return w.ToDisplayString() }
338func (w *WebProofChainLink) ToKeyValuePair() (string, string) {
339	return w.GetProtocol(), w.GetHostname()
340}
341
342func (w *WebProofChainLink) ComputeTrackDiff(tl *TrackLookup) (res TrackDiff) {
343
344	find := func(list []string) bool {
345		for _, e := range list {
346			if Cicmp(e, w.hostname) {
347				return true
348			}
349		}
350		return false
351	}
352	if find(tl.ids[w.protocol]) {
353		res = TrackDiffNone{}
354	} else if w.protocol == "https" && find(tl.ids["http"]) {
355		res = TrackDiffUpgraded{"http", "https"}
356	} else {
357		res = TrackDiffNew{}
358	}
359	return
360}
361
362func (s *SocialProofChainLink) DisplayPriorityKey() string {
363	return s.TableKey()
364}
365func (s *SocialProofChainLink) TableKey() string { return s.service }
366func (s *SocialProofChainLink) Type() string     { return "proof" }
367func (s *SocialProofChainLink) insertIntoTable(tab *IdentityTable) {
368	remoteProofInsertIntoTable(s, tab)
369}
370func (s *SocialProofChainLink) ToDisplayString() string {
371	return s.username + "@" + s.service
372}
373func (s *SocialProofChainLink) LastWriterWins() bool      { return true }
374func (s *SocialProofChainLink) GetRemoteUsername() string { return s.username }
375func (s *SocialProofChainLink) GetHostname() string       { return "" }
376func (s *SocialProofChainLink) GetProtocol() string       { return "" }
377func (s *SocialProofChainLink) ProofText() string         { return s.proofText }
378func (s *SocialProofChainLink) ToIDString() string        { return s.ToDisplayString() }
379func (s *SocialProofChainLink) ToKeyValuePair() (string, string) {
380	return s.service, s.username
381}
382func (s *SocialProofChainLink) GetService() string { return s.service }
383
384func (s *SocialProofChainLink) ToTrackingStatement(state keybase1.ProofState) (*jsonw.Wrapper, error) {
385	ret := s.BaseToTrackingStatement(state)
386	remoteProofToTrackingStatement(s, ret)
387	return ret, nil
388}
389
390func (s *SocialProofChainLink) ComputeTrackDiff(tl *TrackLookup) TrackDiff {
391	k, v := s.ToKeyValuePair()
392	if list, found := tl.ids[k]; !found || len(list) == 0 {
393		return TrackDiffNew{}
394	} else if expected := list[len(list)-1]; !Cicmp(expected, v) {
395		return TrackDiffClash{observed: v, expected: expected}
396	} else {
397		return TrackDiffNone{}
398	}
399}
400
401func (s *SocialProofChainLink) DisplayCheck(m MetaContext, ui IdentifyUI, lcr LinkCheckResult) error {
402	return ui.FinishSocialProofCheck(m, ExportRemoteProof(s), lcr.Export())
403}
404
405func (s *SocialProofChainLink) CheckDataJSON() *jsonw.Wrapper {
406	ret := jsonw.NewDictionary()
407	_ = ret.SetKey("username", jsonw.NewString(s.username))
408	_ = ret.SetKey("name", jsonw.NewString(s.service))
409	return ret
410}
411
412func (s *SocialProofChainLink) GetProofType() keybase1.ProofType {
413	if s.isGeneric {
414		return keybase1.ProofType_GENERIC_SOCIAL
415	}
416	return RemoteServiceTypes[s.service]
417}
418
419var _ RemoteProofChainLink = (*SocialProofChainLink)(nil)
420var _ RemoteProofChainLink = (*WebProofChainLink)(nil)
421
422func NewWebProofChainLink(b GenericChainLink, p, h, proofText string) *WebProofChainLink {
423	return &WebProofChainLink{b, p, h, proofText}
424}
425
426func NewSocialProofChainLink(b GenericChainLink, s, u, proofText string) *SocialProofChainLink {
427	_, found := RemoteServiceTypes[s]
428	return &SocialProofChainLink{
429		GenericChainLink: b,
430		service:          s,
431		username:         u,
432		proofText:        proofText,
433		isGeneric:        !found,
434	}
435}
436
437//=========================================================================
438
439// Can be used to either parse a proof `service` JSON block, or a
440// `remote_key_proof` JSON block in a tracking statement.
441type ServiceBlock struct {
442	social     bool
443	typ        string
444	id         string
445	proofState keybase1.ProofState
446	proofType  keybase1.ProofType
447}
448
449func (sb ServiceBlock) GetProofState() keybase1.ProofState { return sb.proofState }
450
451func (sb ServiceBlock) IsSocial() bool { return sb.social }
452
453func (sb ServiceBlock) ToIDString() string {
454	if sb.social {
455		return sb.id + "@" + sb.typ
456	}
457	return sb.typ + "://" + sb.id
458}
459
460func (sb ServiceBlock) ToKeyValuePair() (string, string) {
461	return sb.typ, sb.id
462}
463
464func (sb ServiceBlock) LastWriterWins() bool {
465	return sb.social
466}
467
468func (sb ServiceBlock) GetProofType() keybase1.ProofType { return sb.proofType }
469
470func ParseServiceBlock(jw *jsonw.Wrapper, pt keybase1.ProofType) (sb *ServiceBlock, err error) {
471	var social bool
472	var typ, id string
473
474	if prot, e1 := jw.AtKey("protocol").GetString(); e1 == nil {
475
476		var hostname string
477
478		jw.AtKey("hostname").GetStringVoid(&hostname, &e1)
479		if e1 == nil {
480			switch prot {
481			case "http:":
482				typ, id = "http", hostname
483			case "https:":
484				typ, id = "https", hostname
485			}
486		} else if domain, e2 := jw.AtKey("domain").GetString(); e2 == nil && prot == "dns" {
487			typ, id = "dns", domain
488		}
489	} else {
490
491		var e2 error
492
493		jw.AtKey("name").GetStringVoid(&typ, &e2)
494		jw.AtKey("username").GetStringVoid(&id, &e2)
495		if e2 != nil {
496			id, typ = "", ""
497		} else {
498			social = true
499		}
500	}
501
502	if len(typ) == 0 {
503		err = fmt.Errorf("Unrecognized Web proof @%s", jw.MarshalToDebug())
504	}
505	sb = &ServiceBlock{social: social, typ: typ, id: id, proofType: pt}
506	return
507}
508
509// To be used for signatures in a user's signature chain.
510func ParseWebServiceBinding(base GenericChainLink) (ret RemoteProofChainLink, err error) {
511	jw := base.UnmarshalPayloadJSON().AtKey("body").AtKey("service")
512	sptf := base.unpacked.proofText
513
514	if jw.IsNil() {
515		ret, err = ParseSelfSigChainLink(base)
516		if err != nil {
517			return nil, err
518		}
519	} else if sb, err := ParseServiceBlock(jw, keybase1.ProofType_NONE); err != nil {
520		err = fmt.Errorf("%s @%s", err, base.ToDebugString())
521		return nil, err
522	} else if sb.social {
523		ret = NewSocialProofChainLink(base, sb.typ, sb.id, sptf)
524	} else {
525		ret = NewWebProofChainLink(base, sb.typ, sb.id, sptf)
526	}
527
528	return ret, nil
529}
530
531func remoteProofInsertIntoTable(l RemoteProofChainLink, tab *IdentityTable) {
532	tab.insertLink(l)
533	tab.insertRemoteProof(l)
534}
535
536//
537//=========================================================================
538
539//=========================================================================
540// TrackChainLink
541//
542type TrackChainLink struct {
543	GenericChainLink
544	whomUsername  NormalizedUsername
545	whomUID       keybase1.UID
546	untrack       *UntrackChainLink
547	local         bool
548	tmpExpireTime time.Time // should only be relevant if local is set to true
549}
550
551func (l TrackChainLink) IsRemote() bool {
552	return !l.local
553}
554
555func ParseTrackChainLink(b GenericChainLink) (ret *TrackChainLink, err error) {
556	payload := b.UnmarshalPayloadJSON()
557	var tmp string
558	tmp, err = payload.AtPath("body.track.basics.username").GetString()
559	if err != nil {
560		err = fmt.Errorf("Bad track statement @%s: %s", b.ToDebugString(), err)
561		return
562	}
563	whomUsername := NewNormalizedUsername(tmp)
564
565	whomUID, err := GetUID(payload.AtPath("body.track.id"))
566	if err != nil {
567		err = fmt.Errorf("Bad track statement @%s: %s", b.ToDebugString(), err)
568		return
569	}
570
571	ret = &TrackChainLink{b, whomUsername, whomUID, nil, false, time.Time{}}
572	return
573}
574
575func (l *TrackChainLink) Type() string { return "track" }
576
577func (l *TrackChainLink) ToDisplayString() string {
578	return l.whomUsername.String()
579}
580
581func (l *TrackChainLink) GetTmpExpireTime() (ret time.Time) {
582	if l.local {
583		ret = l.tmpExpireTime
584	}
585	return ret
586}
587
588func (l *TrackChainLink) insertIntoTable(tab *IdentityTable) {
589	tab.insertLink(l)
590	tab.tracks[l.whomUsername] = append(tab.tracks[l.whomUsername], l)
591}
592
593type TrackedKey struct {
594	KID         keybase1.KID
595	Fingerprint *PGPFingerprint
596}
597
598func trackedKeyFromJSON(jw *jsonw.Wrapper) (TrackedKey, error) {
599	var ret TrackedKey
600	kid, err := GetKID(jw.AtKey("kid"))
601	if err != nil {
602		return TrackedKey{}, err
603	}
604	ret.KID = kid
605
606	// It's ok if key_fingerprint doesn't exist.  But if it does, then include it:
607	fp, err := GetPGPFingerprint(jw.AtKey("key_fingerprint"))
608	if err == nil && fp != nil {
609		ret.Fingerprint = fp
610	}
611	return ret, nil
612}
613
614func (l *TrackChainLink) GetTrackedKeys() ([]TrackedKey, error) {
615	// presumably order is important, so we'll only use the map as a set
616	// to deduplicate keys.
617	set := make(map[keybase1.KID]bool)
618
619	var res []TrackedKey
620
621	pgpKeysJSON := l.UnmarshalPayloadJSON().AtPath("body.track.pgp_keys")
622	if !pgpKeysJSON.IsNil() {
623		n, err := pgpKeysJSON.Len()
624		if err != nil {
625			return nil, err
626		}
627		for i := 0; i < n; i++ {
628			keyJSON := pgpKeysJSON.AtIndex(i)
629			tracked, err := trackedKeyFromJSON(keyJSON)
630			if err != nil {
631				return nil, err
632			}
633			if !set[tracked.KID] {
634				res = append(res, tracked)
635				set[tracked.KID] = true
636			}
637		}
638	}
639	return res, nil
640}
641
642func (l *TrackChainLink) GetEldestKID() (kid keybase1.KID, err error) {
643	keyJSON := l.UnmarshalPayloadJSON().AtPath("body.track.key")
644	if keyJSON.IsNil() {
645		return kid, nil
646	}
647	tracked, err := trackedKeyFromJSON(keyJSON)
648	if err != nil {
649		return kid, err
650	}
651	return tracked.KID, nil
652}
653
654func (l *TrackChainLink) GetTrackedUID() (keybase1.UID, error) {
655	return GetUID(l.UnmarshalPayloadJSON().AtPath("body.track.id"))
656}
657
658func (l *TrackChainLink) GetTrackedUsername() (NormalizedUsername, error) {
659	tmp, err := l.UnmarshalPayloadJSON().AtPath("body.track.basics.username").GetString()
660	if err != nil {
661		return NormalizedUsername(""), fmt.Errorf("no tracked username: %v", err)
662	}
663	return NewNormalizedUsername(tmp), err
664}
665
666func (l *TrackChainLink) IsRevoked() bool {
667	return l.revoked || l.untrack != nil
668}
669
670func (l *TrackChainLink) RemoteKeyProofs() *jsonw.Wrapper {
671	return l.UnmarshalPayloadJSON().AtPath("body.track.remote_proofs")
672}
673
674func (l *TrackChainLink) ToServiceBlocks() (ret []*ServiceBlock) {
675	w := l.RemoteKeyProofs()
676	ln, err := w.Len()
677	if err != nil {
678		return nil
679	}
680	for index := 0; index < ln; index++ {
681		proof := w.AtIndex(index).AtKey("remote_key_proof")
682		sb := convertTrackedProofToServiceBlock(l.G(), proof, index)
683		if sb != nil {
684			ret = append(ret, sb)
685		}
686	}
687	return ret
688}
689
690// Get the tail of the trackee's sigchain.
691func (l *TrackChainLink) GetTrackedLinkSeqno() (seqno keybase1.Seqno, err error) {
692	seqnoJSON := l.UnmarshalPayloadJSON().AtPath("body.track.seq_tail.seqno")
693	if seqnoJSON.IsNil() {
694		return seqno, nil
695	}
696	i64, err := seqnoJSON.GetInt64()
697	if err != nil {
698		return seqno, err
699	}
700	return keybase1.Seqno(i64), nil
701}
702
703// convertTrackedProofToServiceBlock will take a JSON stanza from a track statement, and convert it
704// to a ServiceBlock if it fails some important sanity checks. We check that the JSON stanza is
705// well-formed, and that it's not for a defunct proof type (like Coinbase). If all succeeds,
706// we output a service block that can entered into found-versus-tracked comparison logic.
707// The `index` provided is what index this JSON stanza is in the overall track statement.
708func convertTrackedProofToServiceBlock(g *GlobalContext, proof *jsonw.Wrapper, index int) (ret *ServiceBlock) {
709	var i, t int
710	var err error
711	i, err = proof.AtKey("state").GetInt()
712	if err != nil {
713		g.Log.Warning("Bad 'state' in track statement: %s", err)
714		return nil
715	}
716	t, err = proof.AtKey("proof_type").GetInt()
717	if err != nil {
718		g.Log.Warning("Bad 'proof_type' in track statement: %s", err)
719		return nil
720	}
721	proofType := keybase1.ProofType(t)
722	if isProofTypeDefunct(g, proofType) {
723		g.Log.Debug("Ignoring now defunct proof type %q at index=%d", proofType, index)
724		return nil
725	}
726	ret, err = ParseServiceBlock(proof.AtKey("check_data_json"), proofType)
727	if err != nil {
728		g.Log.Warning("Bad remote_key_proof.check_data_json: %s", err)
729		return nil
730	}
731
732	ret.proofState = keybase1.ProofState(i)
733	if ret.proofState != keybase1.ProofState_OK {
734		g.Log.Debug("Including broken proof at index=%d (proof state=%d)", index, ret.proofState)
735	}
736	return ret
737}
738
739func (l *TrackChainLink) DoOwnNewLinkFromServerNotifications(g *GlobalContext) {
740	g.Log.Debug("Post notification for new TrackChainLink")
741	g.NotifyRouter.HandleTrackingChanged(l.whomUID, l.whomUsername, true)
742}
743
744//
745//=========================================================================
746
747//=========================================================================
748// EldestChainLink
749//
750
751type EldestChainLink struct {
752	GenericChainLink
753	kid    keybase1.KID
754	device *Device
755}
756
757func ParseEldestChainLink(b GenericChainLink) (ret *EldestChainLink, err error) {
758	var kid keybase1.KID
759	var device *Device
760
761	payload := b.UnmarshalPayloadJSON()
762	if kid, err = GetKID(payload.AtPath("body.key.kid")); err != nil {
763		err = ChainLinkError{fmt.Sprintf("Bad eldest statement @%s: %s", b.ToDebugString(), err)}
764		return
765	}
766
767	if jw := payload.AtPath("body.device"); !jw.IsNil() {
768		if device, err = ParseDevice(jw, b.GetCTime()); err != nil {
769			return
770		}
771	}
772
773	ret = &EldestChainLink{b, kid, device}
774	return
775}
776
777func (s *EldestChainLink) GetDelegatedKid() keybase1.KID { return s.kid }
778func (s *EldestChainLink) GetRole() KeyRole              { return DLGSibkey }
779func (s *EldestChainLink) Type() string                  { return string(DelegationTypeEldest) }
780func (s *EldestChainLink) ToDisplayString() string       { return s.kid.String() }
781func (s *EldestChainLink) GetDevice() *Device            { return s.device }
782func (s *EldestChainLink) GetPGPFullHash() string        { return s.extractPGPFullHash("key") }
783func (s *EldestChainLink) insertIntoTable(tab *IdentityTable) {
784	tab.insertLink(s)
785}
786
787//
788//=========================================================================
789
790//=========================================================================
791// SibkeyChainLink
792//
793
794type SibkeyChainLink struct {
795	GenericChainLink
796	kid        keybase1.KID
797	device     *Device
798	reverseSig string
799}
800
801func ParseSibkeyChainLink(b GenericChainLink) (ret *SibkeyChainLink, err error) {
802	var kid keybase1.KID
803	var device *Device
804
805	payload := b.UnmarshalPayloadJSON()
806	if kid, err = GetKID(payload.AtPath("body.sibkey.kid")); err != nil {
807		err = ChainLinkError{fmt.Sprintf("Bad sibkey statement @%s: %s", b.ToDebugString(), err)}
808		return
809	}
810
811	var rs string
812	if rs, err = payload.AtPath("body.sibkey.reverse_sig").GetString(); err != nil {
813		err = ChainLinkError{fmt.Sprintf("Missing reverse_sig in sibkey delegation: @%s: %s",
814			b.ToDebugString(), err)}
815		return
816	}
817
818	if jw := payload.AtPath("body.device"); !jw.IsNil() {
819		if device, err = ParseDevice(jw, b.GetCTime()); err != nil {
820			return
821		}
822	}
823
824	ret = &SibkeyChainLink{b, kid, device, rs}
825	return
826}
827
828func (s *SibkeyChainLink) GetDelegatedKid() keybase1.KID { return s.kid }
829func (s *SibkeyChainLink) GetRole() KeyRole              { return DLGSibkey }
830func (s *SibkeyChainLink) Type() string                  { return string(DelegationTypeSibkey) }
831func (s *SibkeyChainLink) ToDisplayString() string       { return s.kid.String() }
832func (s *SibkeyChainLink) GetDevice() *Device            { return s.device }
833func (s *SibkeyChainLink) GetPGPFullHash() string        { return s.extractPGPFullHash("sibkey") }
834func (s *SibkeyChainLink) insertIntoTable(tab *IdentityTable) {
835	tab.insertLink(s)
836}
837
838//-------------------------------------
839
840func makeDeepCopy(w *jsonw.Wrapper) (ret *jsonw.Wrapper, err error) {
841	var b []byte
842	if b, err = w.Marshal(); err != nil {
843		return nil, err
844	}
845	return jsonw.Unmarshal(b)
846}
847
848//-------------------------------------
849
850// VerifyReverseSig checks a SibkeyChainLink's reverse signature using the ComputedKeyFamily provided.
851func (s *SibkeyChainLink) VerifyReverseSig(ckf ComputedKeyFamily) (err error) {
852	var key GenericKey
853
854	if key, err = ckf.FindKeyWithKIDUnsafe(s.GetDelegatedKid()); err != nil {
855		return err
856	}
857
858	return VerifyReverseSig(s.G(), key, "body.sibkey.reverse_sig", s.UnmarshalPayloadJSON(), s.reverseSig)
859}
860
861//
862//=========================================================================
863// SubkeyChainLink
864
865type SubkeyChainLink struct {
866	GenericChainLink
867	kid       keybase1.KID
868	parentKid keybase1.KID
869}
870
871func ParseSubkeyChainLink(b GenericChainLink) (ret *SubkeyChainLink, err error) {
872	var kid, pkid keybase1.KID
873	payload := b.UnmarshalPayloadJSON()
874	if kid, err = GetKID(payload.AtPath("body.subkey.kid")); err != nil {
875		err = ChainLinkError{fmt.Sprintf("Can't get KID for subkey @%s: %s", b.ToDebugString(), err)}
876	} else if pkid, err = GetKID(payload.AtPath("body.subkey.parent_kid")); err != nil {
877		err = ChainLinkError{fmt.Sprintf("Can't get parent_kid for subkey @%s: %s", b.ToDebugString(), err)}
878	} else {
879		ret = &SubkeyChainLink{b, kid, pkid}
880	}
881	return
882}
883
884func (s *SubkeyChainLink) Type() string                  { return string(DelegationTypeSubkey) }
885func (s *SubkeyChainLink) ToDisplayString() string       { return s.kid.String() }
886func (s *SubkeyChainLink) GetRole() KeyRole              { return DLGSubkey }
887func (s *SubkeyChainLink) GetDelegatedKid() keybase1.KID { return s.kid }
888func (s *SubkeyChainLink) GetParentKid() keybase1.KID    { return s.parentKid }
889func (s *SubkeyChainLink) insertIntoTable(tab *IdentityTable) {
890	tab.insertLink(s)
891}
892
893//
894//=========================================================================
895
896//=========================================================================
897// PerUserKeyChainLink
898
899type PerUserKeyChainLink struct {
900	GenericChainLink
901	// KID of the signing key derived from the per-user-secret.
902	sigKID keybase1.KID
903	// KID of the encryption key derived from the per-user-secret.
904	encKID     keybase1.KID
905	generation keybase1.PerUserKeyGeneration
906	reverseSig string
907}
908
909func ParsePerUserKeyChainLink(b GenericChainLink) (ret *PerUserKeyChainLink, err error) {
910	var sigKID, encKID keybase1.KID
911	var g int
912	var reverseSig string
913	section := b.UnmarshalPayloadJSON().AtPath("body.per_user_key")
914	if sigKID, err = GetKID(section.AtKey("signing_kid")); err != nil {
915		err = ChainLinkError{fmt.Sprintf("Can't get signing KID for per_user_secret: @%s: %s", b.ToDebugString(), err)}
916	} else if encKID, err = GetKID(section.AtKey("encryption_kid")); err != nil {
917		err = ChainLinkError{fmt.Sprintf("Can't get encryption KID for per_user_secret: @%s: %s", b.ToDebugString(), err)}
918	} else if g, err = section.AtKey("generation").GetInt(); err != nil {
919		err = ChainLinkError{fmt.Sprintf("Can't get generation for per_user_secret @%s: %s", b.ToDebugString(), err)}
920	} else if reverseSig, err = section.AtKey("reverse_sig").GetString(); err != nil {
921		err = ChainLinkError{fmt.Sprintf("Missing reverse_sig in per-user-key section: @%s: %s", b.ToDebugString(), err)}
922	} else {
923		ret = &PerUserKeyChainLink{b, sigKID, encKID, keybase1.PerUserKeyGeneration(g), reverseSig}
924	}
925	return ret, err
926}
927
928func (s *PerUserKeyChainLink) Type() string { return string(LinkTypePerUserKey) }
929func (s *PerUserKeyChainLink) ToDisplayString() string {
930	return s.sigKID.String() + " + " + s.encKID.String()
931}
932
933// Don't consider per-user-keys as normal delegations. Because they have
934// multiple kids and initially can't delegate further. They are handled
935// separately by the sigchain loader.
936func (s *PerUserKeyChainLink) GetRole() KeyRole                    { return DLGNone }
937func (s *PerUserKeyChainLink) GetDelegatedKid() (res keybase1.KID) { return }
938func (s *PerUserKeyChainLink) insertIntoTable(tab *IdentityTable) {
939	tab.insertLink(s)
940}
941
942func (s *PerUserKeyChainLink) ToPerUserKey() keybase1.PerUserKey {
943	return keybase1.PerUserKey{
944		Gen:         int(s.generation),
945		Seqno:       s.GetSeqno(),
946		SigKID:      s.sigKID,
947		EncKID:      s.encKID,
948		SignedByKID: s.GetKID(),
949	}
950}
951
952//-------------------------------------
953
954// VerifyReverseSig checks a SibkeyChainLink's reverse signature using the ComputedKeyFamily provided.
955func (s *PerUserKeyChainLink) VerifyReverseSig(_ ComputedKeyFamily) (err error) {
956	key, err := ImportNaclSigningKeyPairFromHex(s.sigKID.String())
957	if err != nil {
958		return fmt.Errorf("Invalid per-user signing KID: %s", s.sigKID)
959	}
960
961	return VerifyReverseSig(s.G(), key, "body.per_user_key.reverse_sig", s.UnmarshalPayloadJSON(), s.reverseSig)
962}
963
964//
965//=========================================================================
966// PGPUpdateChainLink
967//
968
969// PGPUpdateChainLink represents a chain link which marks a new version of a
970// PGP key as current. The KID and a new full hash are included in the
971// pgp_update section of the body.
972type PGPUpdateChainLink struct {
973	GenericChainLink
974	kid keybase1.KID
975}
976
977// ParsePGPUpdateChainLink creates a PGPUpdateChainLink from a GenericChainLink
978// and verifies that its pgp_update section contains a KID and full_hash
979func ParsePGPUpdateChainLink(b GenericChainLink) (ret *PGPUpdateChainLink, err error) {
980	var kid keybase1.KID
981
982	pgpUpdate := b.UnmarshalPayloadJSON().AtPath("body.pgp_update")
983
984	if pgpUpdate.IsNil() {
985		err = ChainLinkError{fmt.Sprintf("missing pgp_update section @%s", b.ToDebugString())}
986		return
987	}
988
989	if kid, err = GetKID(pgpUpdate.AtKey("kid")); err != nil {
990		err = ChainLinkError{fmt.Sprintf("Missing kid @%s: %s", b.ToDebugString(), err)}
991		return
992	}
993
994	ret = &PGPUpdateChainLink{b, kid}
995
996	if fh := ret.GetPGPFullHash(); fh == "" {
997		err = ChainLinkError{fmt.Sprintf("Missing full_hash @%s", b.ToDebugString())}
998		ret = nil
999		return
1000	}
1001
1002	return
1003}
1004
1005func (l *PGPUpdateChainLink) Type() string                       { return string(DelegationTypePGPUpdate) }
1006func (l *PGPUpdateChainLink) ToDisplayString() string            { return l.kid.String() }
1007func (l *PGPUpdateChainLink) GetPGPFullHash() string             { return l.extractPGPFullHash("pgp_update") }
1008func (l *PGPUpdateChainLink) insertIntoTable(tab *IdentityTable) { tab.insertLink(l) }
1009
1010//
1011//=========================================================================
1012//
1013
1014type DeviceChainLink struct {
1015	GenericChainLink
1016	device *Device
1017}
1018
1019func ParseDeviceChainLink(b GenericChainLink) (ret *DeviceChainLink, err error) {
1020	var dobj *Device
1021	if dobj, err = ParseDevice(b.UnmarshalPayloadJSON().AtPath("body.device"), b.GetCTime()); err != nil {
1022	} else {
1023		ret = &DeviceChainLink{b, dobj}
1024	}
1025	return
1026}
1027
1028func (s *DeviceChainLink) GetDevice() *Device { return s.device }
1029func (s *DeviceChainLink) insertIntoTable(tab *IdentityTable) {
1030	tab.insertLink(s)
1031}
1032
1033//
1034//=========================================================================
1035// WalletStellarChainLink
1036
1037type WalletStellarChainLink struct {
1038	GenericChainLink
1039	addressKID keybase1.KID
1040	reverseSig string
1041	address    string
1042	network    string
1043	name       string
1044}
1045
1046func ParseWalletStellarChainLink(b GenericChainLink) (ret *WalletStellarChainLink, err error) {
1047	ret = &WalletStellarChainLink{GenericChainLink: b}
1048	mkErr := func(format string, args ...interface{}) error {
1049		return ChainLinkError{fmt.Sprintf(format, args...) + fmt.Sprintf(" @%s", b.ToDebugString())}
1050	}
1051	bodyW := b.UnmarshalPayloadJSON()
1052	walletSection := bodyW.AtPath("body.wallet")
1053	walletKeySection := bodyW.AtPath("body.wallet_key")
1054	ret.addressKID, err = GetKID(walletKeySection.AtKey("kid"))
1055	if err != nil {
1056		return nil, mkErr("Can't get address KID: %v", err)
1057	}
1058	ret.reverseSig, err = walletKeySection.AtKey("reverse_sig").GetString()
1059	if err != nil {
1060		return nil, mkErr("Missing reverse_sig: %v", err)
1061	}
1062	ret.address, err = walletSection.AtKey("address").GetString()
1063	if err != nil {
1064		return nil, mkErr("Can't get address: %v", err)
1065	}
1066	ret.network, err = walletSection.AtKey("network").GetString()
1067	if err != nil {
1068		return nil, mkErr("Can't get address network: %v", err)
1069	}
1070	nameOption := walletSection.AtKey("name")
1071	if !nameOption.IsNil() {
1072		ret.name, err = nameOption.GetString()
1073		if err != nil {
1074			return nil, mkErr("Can't get account name: %v", err)
1075		}
1076	}
1077
1078	// Check the network and that the keys match.
1079	if ret.network != string(WalletNetworkStellar) {
1080		return nil, mkErr("Unsupported wallet network '%v'", ret.network)
1081	}
1082	accountKey, err := MakeNaclSigningKeyPairFromStellarAccountID(stellar1.AccountID(ret.address))
1083	if err != nil {
1084		return nil, mkErr("Invalid stellar account address: '%v'", ret.address)
1085	}
1086	if !ret.addressKID.Equal(accountKey.GetKID()) {
1087		return nil, mkErr("Mismatched wallet keys: '%v' <-/-> '%v", ret.addressKID, ret.address)
1088	}
1089
1090	return ret, nil
1091}
1092
1093func (s *WalletStellarChainLink) Type() string { return string(LinkTypeWalletStellar) }
1094func (s *WalletStellarChainLink) ToDisplayString() string {
1095	return fmt.Sprintf("%v %v %v %v", s.network, s.name, s.address, s.addressKID.String())
1096}
1097func (s *WalletStellarChainLink) insertIntoTable(tab *IdentityTable) {
1098	tab.insertLink(s)
1099	if tab.stellar == nil || tab.stellar.GetSeqno() <= s.GetSeqno() {
1100		tab.stellar = s
1101	}
1102}
1103
1104// VerifyReverseSig checks a SibkeyChainLink's reverse signature using the ComputedKeyFamily provided.
1105func (s *WalletStellarChainLink) VerifyReverseSig(_ ComputedKeyFamily) (err error) {
1106	key, err := ImportNaclSigningKeyPairFromHex(s.addressKID.String())
1107	if err != nil {
1108		return fmt.Errorf("Invalid wallet reverse signing KID: %s", s.addressKID)
1109	}
1110
1111	return VerifyReverseSig(s.G(), key, "body.wallet_key.reverse_sig", s.UnmarshalPayloadJSON(), s.reverseSig)
1112}
1113
1114func (s *WalletStellarChainLink) Display(m MetaContext, ui IdentifyUI) error {
1115	// First get an up to date user card, since hiding the Stellar address affects it.
1116	card, err := UserCard(m, s.GetUID(), true)
1117	if err != nil {
1118		m.Info("Could not get usercard, so skipping displaying stellar chain link: %s.", err)
1119		return nil
1120	}
1121	selfUID := m.G().Env.GetUID()
1122	if selfUID.IsNil() {
1123		m.G().Log.Warning("Could not get self UID for api")
1124	}
1125	if card.StellarHidden && !selfUID.Equal(s.GetUID()) {
1126		return nil
1127	}
1128	return ui.DisplayStellarAccount(m, keybase1.StellarAccount{
1129		AccountID:         s.address,
1130		FederationAddress: fmt.Sprintf("%s*keybase.io", s.GetUsername()),
1131		SigID:             s.GetSigID(),
1132		Hidden:            card.StellarHidden,
1133	})
1134}
1135
1136//
1137//=========================================================================
1138// UntrackChainLink
1139
1140type UntrackChainLink struct {
1141	GenericChainLink
1142	whomUsername NormalizedUsername
1143	whomUID      keybase1.UID
1144}
1145
1146func ParseUntrackChainLink(b GenericChainLink) (ret *UntrackChainLink, err error) {
1147	var tmp string
1148	payload := b.UnmarshalPayloadJSON()
1149	tmp, err = payload.AtPath("body.untrack.basics.username").GetString()
1150	if err != nil {
1151		err = fmt.Errorf("Bad track statement @%s: %s", b.ToDebugString(), err)
1152		return
1153	}
1154	whomUsername := NewNormalizedUsername(tmp)
1155
1156	whomUID, err := GetUID(payload.AtPath("body.untrack.id"))
1157	if err != nil {
1158		err = fmt.Errorf("Bad track statement @%s: %s", b.ToDebugString(), err)
1159		return
1160	}
1161
1162	ret = &UntrackChainLink{b, whomUsername, whomUID}
1163	return
1164}
1165
1166func (u *UntrackChainLink) insertIntoTable(tab *IdentityTable) {
1167	tab.insertLink(u)
1168	if list, found := tab.tracks[u.whomUsername]; !found {
1169		u.G().Log.Debug("| Useless untrack of %s; no previous tracking statement found",
1170			u.whomUsername)
1171	} else {
1172		for _, obj := range list {
1173			obj.untrack = u
1174		}
1175	}
1176}
1177
1178func (u *UntrackChainLink) ToDisplayString() string {
1179	return u.whomUsername.String()
1180}
1181
1182func (u *UntrackChainLink) Type() string { return "untrack" }
1183
1184func (u *UntrackChainLink) IsRevocationIsh() bool { return true }
1185
1186func (u *UntrackChainLink) DoOwnNewLinkFromServerNotifications(g *GlobalContext) {
1187	g.Log.Debug("Post notification for new UntrackChainLink")
1188	g.NotifyRouter.HandleTrackingChanged(u.whomUID, u.whomUsername, false)
1189}
1190
1191//
1192//=========================================================================
1193
1194//=========================================================================
1195// CryptocurrencyChainLink
1196
1197type CryptocurrencyChainLink struct {
1198	GenericChainLink
1199	pkhash  []byte
1200	address string
1201	typ     CryptocurrencyType
1202}
1203
1204func (c CryptocurrencyChainLink) GetAddress() string {
1205	return c.address
1206}
1207
1208func ParseCryptocurrencyChainLink(b GenericChainLink) (
1209	cl *CryptocurrencyChainLink, err error) {
1210
1211	jw := b.UnmarshalPayloadJSON().AtPath("body.cryptocurrency")
1212	var styp, addr string
1213	var pkhash []byte
1214
1215	jw.AtKey("type").GetStringVoid(&styp, &err)
1216	jw.AtKey("address").GetStringVoid(&addr, &err)
1217
1218	if err != nil {
1219		return
1220	}
1221
1222	var typ CryptocurrencyType
1223	typ, pkhash, err = CryptocurrencyParseAndCheck(addr)
1224	if err != nil {
1225		err = fmt.Errorf("At signature %s: %s", b.ToDebugString(), err)
1226		return
1227	}
1228	if styp != typ.String() {
1229		err = fmt.Errorf("Got %q type but wanted %q at: %s", styp, typ.String(), b.ToDebugString())
1230		return
1231	}
1232
1233	cl = &CryptocurrencyChainLink{b, pkhash, addr, typ}
1234	return
1235}
1236
1237func (c *CryptocurrencyChainLink) Type() string { return "cryptocurrency" }
1238
1239func (c *CryptocurrencyChainLink) ToDisplayString() string { return c.address }
1240
1241func (c *CryptocurrencyChainLink) insertIntoTable(tab *IdentityTable) {
1242	tab.insertLink(c)
1243	tab.cryptocurrency = append(tab.cryptocurrency, c)
1244}
1245
1246func (c CryptocurrencyChainLink) Display(m MetaContext, ui IdentifyUI) error {
1247	return ui.DisplayCryptocurrency(m, c.Export())
1248}
1249
1250//
1251//=========================================================================
1252
1253//=========================================================================
1254// RevokeChainLink
1255
1256type RevokeChainLink struct {
1257	GenericChainLink
1258	device *Device
1259}
1260
1261func ParseRevokeChainLink(b GenericChainLink) (ret *RevokeChainLink, err error) {
1262	var device *Device
1263	if jw := b.UnmarshalPayloadJSON().AtPath("body.device"); !jw.IsNil() {
1264		if device, err = ParseDevice(jw, b.GetCTime()); err != nil {
1265			return
1266		}
1267	}
1268	ret = &RevokeChainLink{b, device}
1269	return
1270}
1271
1272func (r *RevokeChainLink) Type() string { return "revoke" }
1273
1274func (r *RevokeChainLink) ToDisplayString() string {
1275	v := r.GetRevocations()
1276	list := make([]string, len(v))
1277	for i, s := range v {
1278		list[i] = s.String()
1279	}
1280	return strings.Join(list, ",")
1281}
1282
1283func (r *RevokeChainLink) IsRevocationIsh() bool { return true }
1284
1285func (r *RevokeChainLink) insertIntoTable(tab *IdentityTable) {
1286	tab.insertLink(r)
1287}
1288
1289func (r *RevokeChainLink) GetDevice() *Device { return r.device }
1290
1291//
1292//=========================================================================
1293
1294//=========================================================================
1295// SelfSigChainLink
1296
1297type SelfSigChainLink struct {
1298	GenericChainLink
1299	device *Device
1300}
1301
1302func (s *SelfSigChainLink) Type() string { return "self" }
1303
1304func (s *SelfSigChainLink) ToDisplayString() string { return s.unpacked.username }
1305
1306func (s *SelfSigChainLink) insertIntoTable(tab *IdentityTable) {
1307	tab.insertLink(s)
1308}
1309func (s *SelfSigChainLink) DisplayPriorityKey() string { return s.TableKey() }
1310func (s *SelfSigChainLink) TableKey() string           { return "keybase" }
1311func (s *SelfSigChainLink) LastWriterWins() bool       { return true }
1312func (s *SelfSigChainLink) GetRemoteUsername() string  { return s.GetUsername() }
1313func (s *SelfSigChainLink) GetHostname() string        { return "" }
1314func (s *SelfSigChainLink) GetProtocol() string        { return "" }
1315func (s *SelfSigChainLink) ProofText() string          { return "" }
1316
1317func (s *SelfSigChainLink) GetPGPFullHash() string { return s.extractPGPFullHash("key") }
1318
1319func (s *SelfSigChainLink) DisplayCheck(m MetaContext, ui IdentifyUI, lcr LinkCheckResult) error {
1320	return nil
1321}
1322
1323func (s *SelfSigChainLink) CheckDataJSON() *jsonw.Wrapper { return nil }
1324
1325func (s *SelfSigChainLink) ToTrackingStatement(keybase1.ProofState) (*jsonw.Wrapper, error) {
1326	return nil, nil
1327}
1328
1329func (s *SelfSigChainLink) ToIDString() string { return s.GetUsername() }
1330func (s *SelfSigChainLink) ToKeyValuePair() (string, string) {
1331	return s.TableKey(), s.GetUsername()
1332}
1333
1334func (s *SelfSigChainLink) ComputeTrackDiff(tl *TrackLookup) TrackDiff { return nil }
1335
1336func (s *SelfSigChainLink) GetProofType() keybase1.ProofType { return keybase1.ProofType_KEYBASE }
1337
1338func (s *SelfSigChainLink) ParseDevice() (err error) {
1339	if jw := s.UnmarshalPayloadJSON().AtPath("body.device"); !jw.IsNil() {
1340		s.device, err = ParseDevice(jw, s.GetCTime())
1341	}
1342	return err
1343}
1344
1345func (s *SelfSigChainLink) GetDevice() *Device {
1346	return s.device
1347}
1348
1349func ParseSelfSigChainLink(base GenericChainLink) (ret *SelfSigChainLink, err error) {
1350	ret = &SelfSigChainLink{base, nil}
1351	if err = ret.ParseDevice(); err != nil {
1352		ret = nil
1353	}
1354	return
1355}
1356
1357//
1358//=========================================================================
1359
1360//=========================================================================
1361
1362type IdentityTable struct {
1363	Contextified
1364	sigChain         *SigChain
1365	revocations      map[keybase1.SigIDMapKey]bool
1366	links            map[keybase1.SigIDMapKey]TypedChainLink
1367	remoteProofLinks *RemoteProofLinks
1368	tracks           map[NormalizedUsername][]*TrackChainLink
1369	Order            []TypedChainLink
1370	sigHints         *SigHints
1371	cryptocurrency   []*CryptocurrencyChainLink
1372	stellar          *WalletStellarChainLink
1373	checkResult      *CheckResult
1374	eldest           keybase1.KID
1375	hasStubs         bool
1376}
1377
1378func (idt *IdentityTable) GetActiveProofsFor(st ServiceType) (ret []RemoteProofChainLink) {
1379	return idt.remoteProofLinks.ForService(st)
1380}
1381
1382func (idt *IdentityTable) GetTrackMap() map[NormalizedUsername][]*TrackChainLink {
1383	return idt.tracks
1384}
1385
1386func (idt *IdentityTable) HasStubs() bool {
1387	return idt.hasStubs
1388}
1389
1390func (idt *IdentityTable) insertLink(l TypedChainLink) {
1391	idt.links[l.GetSigID().ToMapKey()] = l
1392	idt.Order = append(idt.Order, l)
1393	for _, rev := range l.GetRevocations() {
1394		idt.revocations[rev.ToMapKey()] = true
1395		if targ, found := idt.links[rev.ToMapKey()]; !found {
1396			idt.G().Log.Warning("Can't revoke signature %s @%s", rev, l.ToDebugString())
1397		} else {
1398			targ.markRevoked(l)
1399		}
1400	}
1401}
1402
1403func (idt *IdentityTable) MarkCheckResult(err ProofError) {
1404	idt.checkResult = NewNowCheckResult(idt.G(), err)
1405}
1406
1407func NewTypedChainLink(cl *ChainLink) (ret TypedChainLink, w Warning) {
1408	if ret = cl.typed; ret != nil {
1409		return
1410	}
1411
1412	base := GenericChainLink{cl}
1413
1414	s, err := cl.UnmarshalPayloadJSON().AtKey("body").AtKey("type").GetString()
1415	if len(s) == 0 || err != nil {
1416		err = fmt.Errorf("No type in signature @%s", base.ToDebugString())
1417	} else {
1418		switch s {
1419		case string(DelegationTypeEldest):
1420			ret, err = ParseEldestChainLink(base)
1421		case "web_service_binding":
1422			ret, err = ParseWebServiceBinding(base)
1423		case "track":
1424			ret, err = ParseTrackChainLink(base)
1425		case "untrack":
1426			ret, err = ParseUntrackChainLink(base)
1427		case "cryptocurrency":
1428			ret, err = ParseCryptocurrencyChainLink(base)
1429		case "revoke":
1430			ret, err = ParseRevokeChainLink(base)
1431		case string(DelegationTypeSibkey):
1432			ret, err = ParseSibkeyChainLink(base)
1433		case string(DelegationTypeSubkey):
1434			ret, err = ParseSubkeyChainLink(base)
1435		case string(DelegationTypePGPUpdate):
1436			ret, err = ParsePGPUpdateChainLink(base)
1437		case "per_user_key":
1438			ret, err = ParsePerUserKeyChainLink(base)
1439		case "device":
1440			ret, err = ParseDeviceChainLink(base)
1441		case string(LinkTypeWalletStellar):
1442			ret, err = ParseWalletStellarChainLink(base)
1443		case string(LinkTypeWotVouch):
1444			ret, err = ParseWotVouch(base)
1445		case string(LinkTypeWotReact):
1446			ret, err = ParseWotReact(base)
1447		default:
1448			err = fmt.Errorf("Unknown signature type %s @%s", s, base.ToDebugString())
1449		}
1450	}
1451
1452	if err != nil {
1453		w = ErrorToWarning(err)
1454		ret = &base
1455	}
1456
1457	cl.typed = ret
1458
1459	// Basically we never fail, since worse comes to worse, we treat
1460	// unknown signatures as "generic" and can still display them
1461	return ret, w
1462}
1463
1464func NewIdentityTable(m MetaContext, eldest keybase1.KID, sc *SigChain, h *SigHints) (*IdentityTable, error) {
1465	ret := &IdentityTable{
1466		Contextified:     NewContextified(m.G()),
1467		sigChain:         sc,
1468		revocations:      make(map[keybase1.SigIDMapKey]bool),
1469		links:            make(map[keybase1.SigIDMapKey]TypedChainLink),
1470		remoteProofLinks: NewRemoteProofLinks(m.G()),
1471		tracks:           make(map[NormalizedUsername][]*TrackChainLink),
1472		sigHints:         h,
1473		eldest:           eldest,
1474	}
1475	err := ret.populate(m)
1476	return ret, err
1477}
1478
1479func (idt *IdentityTable) populate(m MetaContext) (err error) {
1480	defer m.Trace("IdentityTable#populate", &err)()
1481
1482	var links []*ChainLink
1483	if links, err = idt.sigChain.GetCurrentSubchain(m, idt.eldest); err != nil {
1484		return err
1485	}
1486
1487	for _, link := range links {
1488		isBad, reason, err := link.IsBad()
1489		if err != nil {
1490			return err
1491		}
1492		if isBad {
1493			m.Debug("Ignoring bad chain link with linkID %s: %s", link.LinkID(), reason)
1494			continue
1495		}
1496		if link.IsStubbed() {
1497			idt.hasStubs = true
1498			continue
1499		}
1500
1501		tcl, w := NewTypedChainLink(link)
1502		if w != nil {
1503			w.Warn(idt.G())
1504		}
1505		// If it's an unknown link type, then it's OK to ignore it
1506		if tcl == nil {
1507			continue
1508		}
1509		tcl.insertIntoTable(idt)
1510		if link.isOwnNewLinkFromServer {
1511			link.isOwnNewLinkFromServer = false
1512			tcl.DoOwnNewLinkFromServerNotifications(idt.G())
1513		}
1514	}
1515	return nil
1516}
1517
1518func isProofTypeDefunct(g *GlobalContext, typ keybase1.ProofType) bool {
1519	switch typ {
1520	case keybase1.ProofType_COINBASE:
1521		return true
1522	default:
1523		return false
1524	}
1525}
1526
1527func (idt *IdentityTable) insertRemoteProof(link RemoteProofChainLink) {
1528
1529	if isProofTypeDefunct(idt.G(), link.GetProofType()) {
1530		idt.G().Log.Debug("Ignoring now-defunct proof: %s", link.ToDebugString())
1531		return
1532	}
1533
1534	// note that the links in the identity table have no ProofError state.
1535	idt.remoteProofLinks.Insert(link, nil)
1536}
1537
1538func (idt *IdentityTable) VerifySelfSig(nun NormalizedUsername, uid keybase1.UID) bool {
1539	list := idt.Order
1540	ln := len(list)
1541	for i := ln - 1; i >= 0; i-- {
1542		link := list[i]
1543
1544		if link.IsRevoked() {
1545			continue
1546		}
1547		if NewNormalizedUsername(link.GetUsername()).Eq(nun) && link.GetUID().Equal(uid) {
1548			idt.G().Log.Debug("| Found self-signature for %s @%s", string(nun),
1549				link.ToDebugString())
1550			return true
1551		}
1552	}
1553	return false
1554}
1555
1556func (idt *IdentityTable) GetTrackList() (ret []*TrackChainLink) {
1557	for _, v := range idt.tracks {
1558		for i := len(v) - 1; i >= 0; i-- {
1559			link := v[i]
1560			if !link.IsRevoked() {
1561				ret = append(ret, link)
1562				break
1563			}
1564		}
1565	}
1566	return
1567}
1568
1569func (idt *IdentityTable) TrackChainLinkFor(username NormalizedUsername, uid keybase1.UID) (*TrackChainLink, error) {
1570	list, found := idt.tracks[username]
1571	if !found {
1572		return nil, nil
1573	}
1574	for i := len(list) - 1; i >= 0; i-- {
1575		link := list[i]
1576		if link.IsRevoked() {
1577			// noop; continue on!
1578			continue
1579		}
1580		uid2, err := link.GetTrackedUID()
1581		if err != nil {
1582			return nil, fmt.Errorf("Bad tracking statement for %s: %s", username, err)
1583		}
1584		if uid.NotEqual(uid2) {
1585			return nil, fmt.Errorf("Bad UID in tracking statement for %s: %s != %s", username, uid, uid2)
1586		}
1587		return link, nil
1588	}
1589	return nil, nil
1590}
1591
1592func (idt *IdentityTable) ActiveCryptocurrency(family CryptocurrencyFamily) *CryptocurrencyChainLink {
1593	tab := idt.cryptocurrency
1594	for i := len(tab) - 1; i >= 0; i-- {
1595		link := tab[i]
1596		if link.typ.ToCryptocurrencyFamily() == family {
1597			if link.IsRevoked() {
1598				return nil
1599			}
1600			return link
1601		}
1602	}
1603	return nil
1604}
1605
1606func (idt *IdentityTable) AllActiveCryptocurrency() []CryptocurrencyChainLink {
1607	var ret []CryptocurrencyChainLink
1608	for _, link := range idt.cryptocurrency {
1609		if !link.IsRevoked() {
1610			ret = append(ret, *link)
1611		}
1612	}
1613	return ret
1614}
1615
1616func (idt *IdentityTable) HasActiveCryptocurrencyFamily(family CryptocurrencyFamily) bool {
1617	for _, link := range idt.AllActiveCryptocurrency() {
1618		if link.typ.ToCryptocurrencyFamily() == family {
1619			return true
1620		}
1621	}
1622	return false
1623}
1624
1625func (idt *IdentityTable) GetRevokedCryptocurrencyForTesting() []CryptocurrencyChainLink {
1626	ret := []CryptocurrencyChainLink{}
1627	for _, link := range idt.cryptocurrency {
1628		if link.IsRevoked() {
1629			ret = append(ret, *link)
1630		}
1631	}
1632	return ret
1633}
1634
1635// Return the active stellar public address for a user.
1636// Returns nil if there is none or it has not been loaded.
1637func (idt *IdentityTable) StellarAccountID() *stellar1.AccountID {
1638	// Return the account ID of the latest link with the network set to stellar.
1639	if idt.stellar == nil {
1640		return nil
1641	}
1642	link := idt.stellar
1643	if link.network == string(WalletNetworkStellar) {
1644		// Something should have already validated link.address as a stellar account ID.
1645		tmp := stellar1.AccountID(link.address)
1646		return &tmp
1647	}
1648	return nil
1649}
1650
1651func (idt *IdentityTable) Len() int {
1652	return len(idt.Order)
1653}
1654
1655type CheckCompletedListener interface {
1656	CCLCheckCompleted(lcr *LinkCheckResult)
1657}
1658
1659type IdentifyTableMode int
1660
1661const (
1662	IdentifyTableModePassive IdentifyTableMode = iota
1663	IdentifyTableModeActive  IdentifyTableMode = iota
1664)
1665
1666func (idt *IdentityTable) Identify(m MetaContext, is IdentifyState, forceRemoteCheck bool, ui IdentifyUI, ccl CheckCompletedListener, itm IdentifyTableMode) error {
1667	errs := make(chan error, len(is.res.ProofChecks))
1668	for _, lcr := range is.res.ProofChecks {
1669		go func(l *LinkCheckResult) {
1670			errs <- idt.identifyActiveProof(m, l, is, forceRemoteCheck, ui, ccl, itm)
1671		}(lcr)
1672	}
1673
1674	allAcc := idt.AllActiveCryptocurrency()
1675	for _, acc := range allAcc {
1676		if err := acc.Display(m, ui); err != nil {
1677			return err
1678		}
1679	}
1680
1681	if stellar := idt.stellar; stellar != nil {
1682		if err := stellar.Display(m, ui); err != nil {
1683			return err
1684		}
1685	}
1686
1687	for i := 0; i < len(is.res.ProofChecks); i++ {
1688		err := <-errs
1689		if err != nil {
1690			return err
1691		}
1692	}
1693
1694	return nil
1695}
1696
1697//=========================================================================
1698
1699func (idt *IdentityTable) identifyActiveProof(m MetaContext, lcr *LinkCheckResult, is IdentifyState, forceRemoteCheck bool, ui IdentifyUI, ccl CheckCompletedListener, itm IdentifyTableMode) error {
1700	idt.proofRemoteCheck(m, is.HasPreviousTrack(), forceRemoteCheck, lcr, itm)
1701	if ccl != nil {
1702		ccl.CCLCheckCompleted(lcr)
1703	}
1704	return lcr.link.DisplayCheck(m, ui, *lcr)
1705}
1706
1707type LinkCheckResult struct {
1708	hint *SigHint
1709	// The client checker fills this in with any knowledge it has about hint
1710	// metadata if it is able to derive it without server help. This value is
1711	// preferred to the plain old server-trust `hint`
1712	verifiedHint         *SigHint
1713	cached               *CheckResult
1714	err                  ProofError
1715	snoozedErr           ProofError
1716	diff                 TrackDiff
1717	remoteDiff           TrackDiff
1718	link                 RemoteProofChainLink
1719	trackedProofState    keybase1.ProofState
1720	tmpTrackedProofState keybase1.ProofState
1721	tmpTrackExpireTime   time.Time
1722	position             int
1723	torWarning           bool
1724}
1725
1726func (l LinkCheckResult) GetDiff() TrackDiff        { return l.diff }
1727func (l LinkCheckResult) GetError() error           { return l.err }
1728func (l LinkCheckResult) GetProofError() ProofError { return l.err }
1729func (l LinkCheckResult) GetHint() *SigHint {
1730	if l.verifiedHint != nil {
1731		return l.verifiedHint
1732	}
1733	return l.hint
1734}
1735func (l LinkCheckResult) GetCached() *CheckResult       { return l.cached }
1736func (l LinkCheckResult) GetPosition() int              { return l.position }
1737func (l LinkCheckResult) GetTorWarning() bool           { return l.torWarning }
1738func (l LinkCheckResult) GetLink() RemoteProofChainLink { return l.link }
1739func (l LinkCheckResult) GetRemoteDiff() TrackDiff      { return l.remoteDiff }
1740
1741// ComputeRemoteDiff takes as input three tracking results: the permanent track,
1742// the local temporary track, and the one it observed remotely. It favors the
1743// permanent track but will roll back to the temporary track if needs be.
1744func (idt *IdentityTable) ComputeRemoteDiff(tracked, trackedTmp, observed keybase1.ProofState) (ret TrackDiff) {
1745	idt.G().Log.Debug("+ ComputeRemoteDiff(%v,%v,%v)", tracked, trackedTmp, observed)
1746	if observed == tracked {
1747		ret = TrackDiffNone{}
1748	} else if observed == trackedTmp {
1749		ret = TrackDiffNoneViaTemporary{}
1750	} else if observed == keybase1.ProofState_OK {
1751		ret = TrackDiffRemoteWorking{tracked}
1752	} else if tracked == keybase1.ProofState_OK {
1753		ret = TrackDiffRemoteFail{observed}
1754	} else {
1755		ret = TrackDiffRemoteChanged{tracked, observed}
1756	}
1757	idt.G().Log.Debug("- ComputeRemoteDiff(%v,%v,%v) -> (%+v,(%T))", tracked, trackedTmp, observed, ret, ret)
1758	return ret
1759}
1760
1761func (idt *IdentityTable) proofRemoteCheck(m MetaContext, hasPreviousTrack, forceRemoteCheck bool, res *LinkCheckResult, itm IdentifyTableMode) {
1762	p := res.link
1763
1764	m.Debug("+ RemoteCheckProof %s", p.ToDebugString())
1765	doCache := false
1766	pvlHashUsed := keybase1.MerkleStoreKitHash("")
1767	sid := p.GetSigID()
1768
1769	defer func() {
1770
1771		if hasPreviousTrack {
1772			observedProofState := ProofErrorToState(res.err)
1773			res.remoteDiff = idt.ComputeRemoteDiff(res.trackedProofState, res.tmpTrackedProofState, observedProofState)
1774
1775			// If the remote diff only worked out because of the temporary track, then
1776			// also update the local diff (i.e., the difference between what we tracked
1777			// and what Keybase is saying) accordingly.
1778			if _, ok := res.remoteDiff.(TrackDiffNoneViaTemporary); ok {
1779				res.diff = res.remoteDiff
1780			}
1781		}
1782
1783		if doCache {
1784			m.Debug("| Caching results under key=%s pvlHash=%s", sid, pvlHashUsed)
1785			if cacheErr := idt.G().ProofCache.Put(sid, res, pvlHashUsed); cacheErr != nil {
1786				m.Warning("proof cache put error: %s", cacheErr)
1787			}
1788		}
1789
1790		m.Debug("- RemoteCheckProof %s", p.ToDebugString())
1791	}()
1792
1793	pvlSource := idt.G().GetPvlSource()
1794	if pvlSource == nil {
1795		res.err = NewProofError(keybase1.ProofStatus_MISSING_PVL, "no pvl source for proof verification")
1796		return
1797	}
1798	pvlU, err := pvlSource.GetLatestEntry(m)
1799	if err != nil {
1800		res.err = NewProofError(keybase1.ProofStatus_MISSING_PVL, "error getting pvl: %s", err)
1801		return
1802	}
1803	pvlHashUsed = pvlU.Hash
1804
1805	res.hint = idt.sigHints.Lookup(sid)
1806	if res.hint == nil {
1807		res.err = NewProofError(keybase1.ProofStatus_NO_HINT, "No server-given hint for sig=%s", sid)
1808		return
1809	}
1810
1811	var pc ProofChecker
1812
1813	// Call the Global context's version of what a proof checker is. We might want to stub it out
1814	// for the purposes of testing.
1815	pc, res.err = MakeProofChecker(m, m.G().GetProofServices(), p)
1816
1817	if res.err != nil || pc == nil {
1818		return
1819	}
1820
1821	if m.G().Env.GetTorMode().Enabled() {
1822		if e := pc.GetTorError(); e != nil {
1823			res.torWarning = true
1824		}
1825	}
1826
1827	if !forceRemoteCheck {
1828		res.cached = m.G().ProofCache.Get(sid, pvlU.Hash)
1829		m.Debug("| Proof cache lookup for %s: %+v", sid, res.cached)
1830		if res.cached != nil && res.cached.Freshness() == keybase1.CheckResultFreshness_FRESH {
1831			res.err = res.cached.Status
1832			res.verifiedHint = res.cached.VerifiedHint
1833			m.Debug("| Early exit after proofCache hit for %s", sid)
1834			return
1835		}
1836	}
1837
1838	// From this point on in the function, we'll be putting our results into
1839	// cache (in the defer above).
1840	doCache = true
1841
1842	// ProofCheckerModeActive or Passive mainly decides whether we need to reach out to
1843	// self-hosted services. We want to avoid so doing when the user is acting passively
1844	// (such as when receiving a message).
1845	pcm := ProofCheckerModePassive
1846	if (hasPreviousTrack && res.trackedProofState != keybase1.ProofState_NONE && res.trackedProofState != keybase1.ProofState_UNCHECKED) || itm == IdentifyTableModeActive {
1847		pcm = ProofCheckerModeActive
1848	}
1849	var hint SigHint
1850	if res.hint != nil {
1851		hint = *res.hint
1852	}
1853	res.verifiedHint, res.err = pc.CheckStatus(m, hint, pcm, pvlU)
1854
1855	// If no error than all good
1856	if res.err == nil {
1857		return
1858	}
1859
1860	// If the error was soft, and we had a cached successful result that wasn't rancid,
1861	// then it's OK to stifle the error message for now.  We just have to be certain
1862	// not to cache it.
1863	if ProofErrorIsSoft(res.err) && res.cached != nil && res.cached.Status == nil &&
1864		res.cached.Freshness() != keybase1.CheckResultFreshness_RANCID {
1865		m.Debug("| Got soft error (%s) but returning success (last seen at %s)",
1866			res.err.Error(), res.cached.Time)
1867		res.snoozedErr = res.err
1868		res.err = nil
1869		doCache = false
1870		return
1871	}
1872
1873	m.Debug("| Check status (%s) failed with error: %s", p.ToDebugString(), res.err.Error())
1874}
1875
1876// VerifyReverseSig checks reverse signature using the key provided.
1877// does not modify `payload`.
1878// `path` is the path to the reverse sig spot to null before checking.
1879func VerifyReverseSig(g *GlobalContext, key GenericKey, path string, payload *jsonw.Wrapper, reverseSig string) (err error) {
1880	var p1, p2 []byte
1881	if p1, _, err = key.VerifyStringAndExtract(g.Log, reverseSig); err != nil {
1882		err = ReverseSigError{fmt.Sprintf("Failed to verify/extract sig: %s", err)}
1883		return err
1884	}
1885
1886	if p1, err = jsonw.Canonicalize(p1); err != nil {
1887		err = ReverseSigError{fmt.Sprintf("Failed to canonicalize json: %s", err)}
1888		return err
1889	}
1890
1891	// Make a deep copy. It's dangerous to try to mutate this thing
1892	// since other goroutines might be accessing it at the same time.
1893	var jsonCopy *jsonw.Wrapper
1894	if jsonCopy, err = makeDeepCopy(payload); err != nil {
1895		err = ReverseSigError{fmt.Sprintf("Failed to copy payload json: %s", err)}
1896		return err
1897	}
1898
1899	err = jsonCopy.SetValueAtPath(path, jsonw.NewNil())
1900	if err != nil {
1901		return err
1902	}
1903	if p2, err = jsonCopy.Marshal(); err != nil {
1904		err = ReverseSigError{fmt.Sprintf("Can't remarshal JSON statement: %s", err)}
1905		return err
1906	}
1907
1908	eq := FastByteArrayEq(p1, p2)
1909
1910	if !eq {
1911		err = ReverseSigError{fmt.Sprintf("JSON mismatch: %s != %s",
1912			string(p1), string(p2))}
1913		return err
1914	}
1915	return nil
1916}
1917