github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/libkb/track.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  	"errors"
     8  	"fmt"
     9  	"time"
    10  
    11  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
    12  	jsonw "github.com/keybase/go-jsonw"
    13  )
    14  
    15  var ErrTrackingExpired = errors.New("Local track expired")
    16  
    17  // Can be a ProofLinkWithState, one of the identities listed in a
    18  // tracking statement, or a PGP Fingerprint!
    19  type TrackIDComponent interface {
    20  	ToIDString() string
    21  	ToKeyValuePair() (string, string)
    22  	GetProofState() keybase1.ProofState
    23  	LastWriterWins() bool
    24  	GetProofType() keybase1.ProofType
    25  }
    26  
    27  type TrackSet struct {
    28  	ids      map[string]TrackIDComponent
    29  	services map[string]bool
    30  }
    31  
    32  func NewTrackSet() *TrackSet {
    33  	return &TrackSet{
    34  		ids:      make(map[string]TrackIDComponent),
    35  		services: make(map[string]bool),
    36  	}
    37  }
    38  
    39  func (ts TrackSet) Add(t TrackIDComponent) {
    40  	ts.ids[t.ToIDString()] = t
    41  	if t.LastWriterWins() {
    42  		k, _ := t.ToKeyValuePair()
    43  		ts.services[k] = true
    44  	}
    45  }
    46  
    47  func (ts TrackSet) GetProofState(id string) keybase1.ProofState {
    48  	ret := keybase1.ProofState_NONE
    49  	if obj := ts.ids[id]; obj != nil {
    50  		ret = obj.GetProofState()
    51  	}
    52  	return ret
    53  }
    54  
    55  func (ts TrackSet) Subtract(b TrackSet) (out []TrackIDComponent) {
    56  	for _, c := range ts.ids {
    57  		if !b.HasMember(c) {
    58  			out = append(out, c)
    59  		}
    60  	}
    61  	return
    62  }
    63  
    64  func (ts TrackSet) HasMember(t TrackIDComponent) bool {
    65  	var found bool
    66  
    67  	// For LastWriterWins like social networks, then it just matters
    68  	// that there is some proof for the service.  For non-last-writer-wins,
    69  	// like HTTPS and DNS, then the full proof needs to show up in A.
    70  	if t.LastWriterWins() {
    71  		k, _ := t.ToKeyValuePair()
    72  		_, found = ts.services[k]
    73  	} else {
    74  		_, found = ts.ids[t.ToIDString()]
    75  	}
    76  	return found
    77  }
    78  
    79  func (ts TrackSet) LenEq(b TrackSet) bool {
    80  	return len(ts.ids) == len(b.ids)
    81  }
    82  
    83  // =====================================================================
    84  
    85  type TrackInstructions struct {
    86  	Local  bool
    87  	Remote bool
    88  }
    89  
    90  // =====================================================================
    91  
    92  type TrackSummary struct {
    93  	time     time.Time
    94  	isRemote bool
    95  	username string
    96  }
    97  
    98  func (s TrackSummary) IsRemote() bool      { return s.isRemote }
    99  func (s TrackSummary) GetCTime() time.Time { return s.time }
   100  func (s TrackSummary) Username() string    { return s.username }
   101  
   102  // =====================================================================
   103  
   104  type TrackLookup struct {
   105  	Contextified
   106  	link         *TrackChainLink     // The original chain link that I signed
   107  	set          *TrackSet           // The total set of tracked identities
   108  	ids          map[string][]string // A http -> [foo.com, boo.com] lookup
   109  	trackerSeqno keybase1.Seqno      // The seqno in the tracker's sighcain
   110  }
   111  
   112  func (l TrackLookup) ToSummary() TrackSummary {
   113  	ret := TrackSummary{
   114  		time:     l.GetCTime(),
   115  		isRemote: l.IsRemote(),
   116  	}
   117  	return ret
   118  }
   119  
   120  func (l TrackLookup) GetProofState(id string) keybase1.ProofState {
   121  	return l.set.GetProofState(id)
   122  }
   123  
   124  func (l TrackLookup) GetTrackerSeqno() keybase1.Seqno {
   125  	return l.trackerSeqno
   126  }
   127  
   128  func (l TrackLookup) GetTrackedKeys() []TrackedKey {
   129  	ret, err := l.link.GetTrackedKeys()
   130  	if err != nil {
   131  		l.G().Log.Warning("Error in lookup of tracked PGP fingerprints: %s", err)
   132  	}
   133  	return ret
   134  }
   135  
   136  func (l TrackLookup) GetEldestKID() keybase1.KID {
   137  	ret, err := l.link.GetEldestKID()
   138  	if err != nil {
   139  		l.G().Log.Warning("Error in lookup of eldest KID: %s", err)
   140  	}
   141  	return ret
   142  }
   143  
   144  func (l TrackLookup) GetTrackedLinkSeqno() keybase1.Seqno {
   145  	ret, err := l.link.GetTrackedLinkSeqno()
   146  	if err != nil {
   147  		l.G().Log.Warning("Error in lookup of tracked link's seqno: %s", err)
   148  	}
   149  	return ret
   150  }
   151  
   152  func (l TrackLookup) GetTmpExpireTime() (ret time.Time) {
   153  	return l.link.GetTmpExpireTime()
   154  }
   155  
   156  func (l TrackLookup) IsRemote() bool {
   157  	return l.link.IsRemote()
   158  }
   159  
   160  type TrackDiff interface {
   161  	BreaksTracking() bool
   162  	ToDisplayString() string
   163  	ToDisplayMarkup() *Markup
   164  	IsSameAsTracked() bool
   165  	GetTrackDiffType() keybase1.TrackDiffType
   166  }
   167  
   168  type TrackDiffUpgraded struct {
   169  	prev, curr string
   170  }
   171  
   172  func (t TrackDiffUpgraded) IsSameAsTracked() bool {
   173  	return false
   174  }
   175  
   176  func (t TrackDiffUpgraded) BreaksTracking() bool {
   177  	return false
   178  }
   179  func (t TrackDiffUpgraded) ToDisplayString() string {
   180  	return "Upgraded from " + t.prev + " to " + t.curr
   181  }
   182  func (t TrackDiffUpgraded) GetPrev() string { return t.prev }
   183  func (t TrackDiffUpgraded) GetCurr() string { return t.curr }
   184  func (t TrackDiffUpgraded) ToDisplayMarkup() *Markup {
   185  	return NewMarkup(t.ToDisplayString())
   186  }
   187  func (t TrackDiffUpgraded) GetTrackDiffType() keybase1.TrackDiffType {
   188  	return keybase1.TrackDiffType_UPGRADED
   189  }
   190  
   191  type TrackDiffNone struct{}
   192  
   193  func (t TrackDiffNone) BreaksTracking() bool {
   194  	return false
   195  }
   196  func (t TrackDiffNone) IsSameAsTracked() bool {
   197  	return true
   198  }
   199  
   200  func (t TrackDiffNone) ToDisplayString() string {
   201  	return "followed"
   202  }
   203  func (t TrackDiffNone) ToDisplayMarkup() *Markup {
   204  	return NewMarkup(t.ToDisplayString())
   205  }
   206  func (t TrackDiffNone) GetTrackDiffType() keybase1.TrackDiffType {
   207  	return keybase1.TrackDiffType_NONE
   208  }
   209  
   210  type TrackDiffNoneViaTemporary struct{}
   211  
   212  func (t TrackDiffNoneViaTemporary) BreaksTracking() bool     { return false }
   213  func (t TrackDiffNoneViaTemporary) IsSameAsTracked() bool    { return true }
   214  func (t TrackDiffNoneViaTemporary) ToDisplayString() string  { return "snoozed" }
   215  func (t TrackDiffNoneViaTemporary) ToDisplayMarkup() *Markup { return NewMarkup(t.ToDisplayString()) }
   216  func (t TrackDiffNoneViaTemporary) GetTrackDiffType() keybase1.TrackDiffType {
   217  	return keybase1.TrackDiffType_NONE_VIA_TEMPORARY
   218  }
   219  
   220  type TrackDiffNew struct{}
   221  
   222  func (t TrackDiffNew) BreaksTracking() bool {
   223  	return false
   224  }
   225  func (t TrackDiffNew) IsSameAsTracked() bool {
   226  	return false
   227  }
   228  
   229  type TrackDiffClash struct {
   230  	observed, expected string
   231  }
   232  
   233  func (t TrackDiffNew) ToDisplayString() string {
   234  	return "new"
   235  }
   236  func (t TrackDiffNew) ToDisplayMarkup() *Markup {
   237  	return NewMarkup(t.ToDisplayString())
   238  }
   239  func (t TrackDiffNew) GetTrackDiffType() keybase1.TrackDiffType {
   240  	return keybase1.TrackDiffType_NEW
   241  }
   242  
   243  func (t TrackDiffClash) BreaksTracking() bool {
   244  	return true
   245  }
   246  
   247  func (t TrackDiffClash) ToDisplayString() string {
   248  	return "CHANGED from \"" + t.expected + "\""
   249  }
   250  func (t TrackDiffClash) IsSameAsTracked() bool {
   251  	return false
   252  }
   253  func (t TrackDiffClash) ToDisplayMarkup() *Markup {
   254  	return NewMarkup(t.ToDisplayString())
   255  }
   256  func (t TrackDiffClash) GetTrackDiffType() keybase1.TrackDiffType {
   257  	return keybase1.TrackDiffType_CLASH
   258  }
   259  
   260  type TrackDiffRevoked struct {
   261  	idc TrackIDComponent
   262  }
   263  
   264  func (t TrackDiffRevoked) BreaksTracking() bool {
   265  	return true
   266  }
   267  func (t TrackDiffRevoked) ToDisplayString() string {
   268  	return "Deleted proof: " + t.idc.ToIDString()
   269  }
   270  func (t TrackDiffRevoked) IsSameAsTracked() bool {
   271  	return false
   272  }
   273  func (t TrackDiffRevoked) ToDisplayMarkup() *Markup {
   274  	return NewMarkup(t.ToDisplayString())
   275  }
   276  func (t TrackDiffRevoked) GetTrackDiffType() keybase1.TrackDiffType {
   277  	return keybase1.TrackDiffType_REVOKED
   278  }
   279  
   280  type TrackDiffSnoozedRevoked struct {
   281  	idc TrackIDComponent
   282  }
   283  
   284  func (t TrackDiffSnoozedRevoked) BreaksTracking() bool {
   285  	return false
   286  }
   287  func (t TrackDiffSnoozedRevoked) ToDisplayString() string {
   288  	return "Deleted proof: " + t.idc.ToIDString() + " (snoozed)"
   289  }
   290  func (t TrackDiffSnoozedRevoked) IsSameAsTracked() bool {
   291  	return true
   292  }
   293  func (t TrackDiffSnoozedRevoked) ToDisplayMarkup() *Markup {
   294  	return NewMarkup(t.ToDisplayString())
   295  }
   296  func (t TrackDiffSnoozedRevoked) GetTrackDiffType() keybase1.TrackDiffType {
   297  	return keybase1.TrackDiffType_NONE_VIA_TEMPORARY
   298  }
   299  
   300  type TrackDiffRemoteFail struct {
   301  	observed keybase1.ProofState
   302  }
   303  
   304  func (t TrackDiffRemoteFail) BreaksTracking() bool {
   305  	return true
   306  }
   307  func (t TrackDiffRemoteFail) ToDisplayString() string {
   308  	return "remote failed"
   309  }
   310  func (t TrackDiffRemoteFail) ToDisplayMarkup() *Markup {
   311  	return NewMarkup(t.ToDisplayString())
   312  }
   313  func (t TrackDiffRemoteFail) GetTrackDiffType() keybase1.TrackDiffType {
   314  	return keybase1.TrackDiffType_REMOTE_FAIL
   315  }
   316  func (t TrackDiffRemoteFail) IsSameAsTracked() bool {
   317  	return false
   318  }
   319  
   320  type TrackDiffRemoteWorking struct {
   321  	tracked keybase1.ProofState
   322  }
   323  
   324  func (t TrackDiffRemoteWorking) BreaksTracking() bool {
   325  	return false
   326  }
   327  func (t TrackDiffRemoteWorking) ToDisplayString() string {
   328  	return "newly working"
   329  }
   330  func (t TrackDiffRemoteWorking) ToDisplayMarkup() *Markup {
   331  	return NewMarkup(t.ToDisplayString())
   332  }
   333  func (t TrackDiffRemoteWorking) GetTrackDiffType() keybase1.TrackDiffType {
   334  	return keybase1.TrackDiffType_REMOTE_WORKING
   335  }
   336  func (t TrackDiffRemoteWorking) IsSameAsTracked() bool {
   337  	return false
   338  }
   339  
   340  type TrackDiffRemoteChanged struct {
   341  	tracked, observed keybase1.ProofState
   342  }
   343  
   344  func (t TrackDiffRemoteChanged) BreaksTracking() bool {
   345  	return false
   346  }
   347  func (t TrackDiffRemoteChanged) ToDisplayString() string {
   348  	return "changed"
   349  }
   350  func (t TrackDiffRemoteChanged) ToDisplayMarkup() *Markup {
   351  	return NewMarkup(t.ToDisplayString())
   352  }
   353  func (t TrackDiffRemoteChanged) GetTrackDiffType() keybase1.TrackDiffType {
   354  	return keybase1.TrackDiffType_REMOTE_CHANGED
   355  }
   356  func (t TrackDiffRemoteChanged) IsSameAsTracked() bool {
   357  	return false
   358  }
   359  
   360  type TrackDiffNewEldest struct {
   361  	tracked  keybase1.KID
   362  	observed keybase1.KID
   363  }
   364  
   365  func (t TrackDiffNewEldest) BreaksTracking() bool {
   366  	return true
   367  }
   368  func (t TrackDiffNewEldest) IsSameAsTracked() bool {
   369  	return false
   370  }
   371  func (t TrackDiffNewEldest) GetTrackDiffType() keybase1.TrackDiffType {
   372  	return keybase1.TrackDiffType_NEW_ELDEST
   373  }
   374  func (t TrackDiffNewEldest) ToDisplayString() string {
   375  	if t.tracked.IsNil() {
   376  		return fmt.Sprintf("No key when followed; established new eldest key %s", t.observed)
   377  	}
   378  	if t.tracked.Equal(t.observed) {
   379  		return fmt.Sprintf("Account reset! Old key was %s; new key is the same", t.tracked)
   380  	}
   381  	return fmt.Sprintf("Account reset! Old key was %s; new key is %s", t.tracked, t.observed)
   382  }
   383  func (t TrackDiffNewEldest) ToDisplayMarkup() *Markup {
   384  	return NewMarkup(t.ToDisplayString())
   385  }
   386  
   387  func NewTrackLookup(g *GlobalContext, link *TrackChainLink) *TrackLookup {
   388  	sbs := link.ToServiceBlocks()
   389  	set := NewTrackSet()
   390  	ids := make(map[string][]string)
   391  	for _, sb := range sbs {
   392  		set.Add(sb)
   393  		k, v := sb.ToKeyValuePair()
   394  		ids[k] = append(ids[k], v)
   395  	}
   396  	ret := &TrackLookup{Contextified: NewContextified(g), link: link, set: set, ids: ids, trackerSeqno: link.GetSeqno()}
   397  	return ret
   398  }
   399  
   400  func (l *TrackLookup) GetCTime() time.Time {
   401  	return l.link.GetCTime()
   402  }
   403  
   404  // =====================================================================
   405  
   406  func LocalTrackDBKey(tracker, trackee keybase1.UID, expireLocal bool) DbKey {
   407  	key := fmt.Sprintf("%s-%s", tracker, trackee)
   408  	if expireLocal {
   409  		key += "-expires"
   410  	}
   411  	return DbKey{Typ: DBLocalTrack, Key: key}
   412  }
   413  
   414  // =====================================================================
   415  
   416  func localTrackChainLinkFor(m MetaContext, tracker, trackee keybase1.UID, localExpires bool) (ret *TrackChainLink, err error) {
   417  	data, _, err := m.G().LocalDb.GetRaw(LocalTrackDBKey(tracker, trackee, localExpires))
   418  	if err != nil {
   419  		m.Debug("| DB lookup failed")
   420  		return nil, err
   421  	}
   422  	if len(data) == 0 {
   423  		m.Debug("| No local track found")
   424  		return nil, nil
   425  	}
   426  
   427  	cl := &ChainLink{Contextified: NewContextified(m.G())}
   428  	if err = cl.UnpackLocal(data); err != nil {
   429  		m.Debug("| unpack local failed -> %s", err)
   430  		return nil, err
   431  	}
   432  
   433  	var linkETime time.Time
   434  
   435  	if localExpires {
   436  		linkETime = cl.GetCTime().Add(m.G().Env.GetLocalTrackMaxAge())
   437  
   438  		m.Debug("| local track created %s, expires: %s, it is now %s", cl.GetCTime(), linkETime.String(), m.G().Clock().Now())
   439  
   440  		if linkETime.Before(m.G().Clock().Now()) {
   441  			m.Debug("| expired local track, deleting")
   442  			_ = removeLocalTrack(m, tracker, trackee, true)
   443  			return nil, ErrTrackingExpired
   444  		}
   445  	}
   446  
   447  	base := GenericChainLink{cl}
   448  	ret, err = ParseTrackChainLink(base)
   449  	if ret != nil && err == nil {
   450  		ret.local = true
   451  		ret.tmpExpireTime = linkETime
   452  	}
   453  
   454  	return ret, err
   455  }
   456  
   457  func LocalTrackChainLinkFor(m MetaContext, tracker, trackee keybase1.UID) (ret *TrackChainLink, err error) {
   458  	return localTrackChainLinkFor(m, tracker, trackee, false)
   459  }
   460  
   461  func LocalTmpTrackChainLinkFor(m MetaContext, tracker, trackee keybase1.UID) (ret *TrackChainLink, err error) {
   462  	return localTrackChainLinkFor(m, tracker, trackee, true)
   463  }
   464  
   465  func StoreLocalTrack(m MetaContext, tracker keybase1.UID, trackee keybase1.UID, expiringLocal bool, statement *jsonw.Wrapper) error {
   466  	m.Debug("| StoreLocalTrack, expiring = %v", expiringLocal)
   467  	err := m.G().LocalDb.Put(LocalTrackDBKey(tracker, trackee, expiringLocal), nil, statement)
   468  	if err == nil {
   469  		m.G().IdentifyDispatch.NotifyTrackingSuccess(m, trackee)
   470  	}
   471  	return err
   472  }
   473  
   474  func removeLocalTrack(m MetaContext, tracker keybase1.UID, trackee keybase1.UID, expiringLocal bool) error {
   475  	m.Debug("| RemoveLocalTrack, expiring = %v", expiringLocal)
   476  	return m.G().LocalDb.Delete(LocalTrackDBKey(tracker, trackee, expiringLocal))
   477  }
   478  
   479  func RemoveLocalTracks(m MetaContext, tracker keybase1.UID, trackee keybase1.UID) error {
   480  	e1 := removeLocalTrack(m, tracker, trackee, false)
   481  	e2 := removeLocalTrack(m, tracker, trackee, true)
   482  	return PickFirstError(e1, e2)
   483  }