github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/libkb/id_table.go (about)

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