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 "errors" 8 "fmt" 9 "time" 10 11 keybase1 "github.com/keybase/client/go/protocol/keybase1" 12 jsonw "github.com/keybase/go-jsonw" 13) 14 15var 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! 19type TrackIDComponent interface { 20 ToIDString() string 21 ToKeyValuePair() (string, string) 22 GetProofState() keybase1.ProofState 23 LastWriterWins() bool 24 GetProofType() keybase1.ProofType 25} 26 27type TrackSet struct { 28 ids map[string]TrackIDComponent 29 services map[string]bool 30} 31 32func NewTrackSet() *TrackSet { 33 return &TrackSet{ 34 ids: make(map[string]TrackIDComponent), 35 services: make(map[string]bool), 36 } 37} 38 39func (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 47func (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 55func (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 64func (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 79func (ts TrackSet) LenEq(b TrackSet) bool { 80 return len(ts.ids) == len(b.ids) 81} 82 83//===================================================================== 84 85type TrackInstructions struct { 86 Local bool 87 Remote bool 88} 89 90//===================================================================== 91 92type TrackSummary struct { 93 time time.Time 94 isRemote bool 95 username string 96} 97 98func (s TrackSummary) IsRemote() bool { return s.isRemote } 99func (s TrackSummary) GetCTime() time.Time { return s.time } 100func (s TrackSummary) Username() string { return s.username } 101 102//===================================================================== 103 104type 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 112func (l TrackLookup) ToSummary() TrackSummary { 113 ret := TrackSummary{ 114 time: l.GetCTime(), 115 isRemote: l.IsRemote(), 116 } 117 return ret 118} 119 120func (l TrackLookup) GetProofState(id string) keybase1.ProofState { 121 return l.set.GetProofState(id) 122} 123 124func (l TrackLookup) GetTrackerSeqno() keybase1.Seqno { 125 return l.trackerSeqno 126} 127 128func (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 136func (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 144func (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 152func (l TrackLookup) GetTmpExpireTime() (ret time.Time) { 153 return l.link.GetTmpExpireTime() 154} 155 156func (l TrackLookup) IsRemote() bool { 157 return l.link.IsRemote() 158} 159 160type TrackDiff interface { 161 BreaksTracking() bool 162 ToDisplayString() string 163 ToDisplayMarkup() *Markup 164 IsSameAsTracked() bool 165 GetTrackDiffType() keybase1.TrackDiffType 166} 167 168type TrackDiffUpgraded struct { 169 prev, curr string 170} 171 172func (t TrackDiffUpgraded) IsSameAsTracked() bool { 173 return false 174} 175 176func (t TrackDiffUpgraded) BreaksTracking() bool { 177 return false 178} 179func (t TrackDiffUpgraded) ToDisplayString() string { 180 return "Upgraded from " + t.prev + " to " + t.curr 181} 182func (t TrackDiffUpgraded) GetPrev() string { return t.prev } 183func (t TrackDiffUpgraded) GetCurr() string { return t.curr } 184func (t TrackDiffUpgraded) ToDisplayMarkup() *Markup { 185 return NewMarkup(t.ToDisplayString()) 186} 187func (t TrackDiffUpgraded) GetTrackDiffType() keybase1.TrackDiffType { 188 return keybase1.TrackDiffType_UPGRADED 189} 190 191type TrackDiffNone struct{} 192 193func (t TrackDiffNone) BreaksTracking() bool { 194 return false 195} 196func (t TrackDiffNone) IsSameAsTracked() bool { 197 return true 198} 199 200func (t TrackDiffNone) ToDisplayString() string { 201 return "followed" 202} 203func (t TrackDiffNone) ToDisplayMarkup() *Markup { 204 return NewMarkup(t.ToDisplayString()) 205} 206func (t TrackDiffNone) GetTrackDiffType() keybase1.TrackDiffType { 207 return keybase1.TrackDiffType_NONE 208} 209 210type TrackDiffNoneViaTemporary struct{} 211 212func (t TrackDiffNoneViaTemporary) BreaksTracking() bool { return false } 213func (t TrackDiffNoneViaTemporary) IsSameAsTracked() bool { return true } 214func (t TrackDiffNoneViaTemporary) ToDisplayString() string { return "snoozed" } 215func (t TrackDiffNoneViaTemporary) ToDisplayMarkup() *Markup { return NewMarkup(t.ToDisplayString()) } 216func (t TrackDiffNoneViaTemporary) GetTrackDiffType() keybase1.TrackDiffType { 217 return keybase1.TrackDiffType_NONE_VIA_TEMPORARY 218} 219 220type TrackDiffNew struct{} 221 222func (t TrackDiffNew) BreaksTracking() bool { 223 return false 224} 225func (t TrackDiffNew) IsSameAsTracked() bool { 226 return false 227} 228 229type TrackDiffClash struct { 230 observed, expected string 231} 232 233func (t TrackDiffNew) ToDisplayString() string { 234 return "new" 235} 236func (t TrackDiffNew) ToDisplayMarkup() *Markup { 237 return NewMarkup(t.ToDisplayString()) 238} 239func (t TrackDiffNew) GetTrackDiffType() keybase1.TrackDiffType { 240 return keybase1.TrackDiffType_NEW 241} 242 243func (t TrackDiffClash) BreaksTracking() bool { 244 return true 245} 246 247func (t TrackDiffClash) ToDisplayString() string { 248 return "CHANGED from \"" + t.expected + "\"" 249} 250func (t TrackDiffClash) IsSameAsTracked() bool { 251 return false 252} 253func (t TrackDiffClash) ToDisplayMarkup() *Markup { 254 return NewMarkup(t.ToDisplayString()) 255} 256func (t TrackDiffClash) GetTrackDiffType() keybase1.TrackDiffType { 257 return keybase1.TrackDiffType_CLASH 258} 259 260type TrackDiffRevoked struct { 261 idc TrackIDComponent 262} 263 264func (t TrackDiffRevoked) BreaksTracking() bool { 265 return true 266} 267func (t TrackDiffRevoked) ToDisplayString() string { 268 return "Deleted proof: " + t.idc.ToIDString() 269} 270func (t TrackDiffRevoked) IsSameAsTracked() bool { 271 return false 272} 273func (t TrackDiffRevoked) ToDisplayMarkup() *Markup { 274 return NewMarkup(t.ToDisplayString()) 275} 276func (t TrackDiffRevoked) GetTrackDiffType() keybase1.TrackDiffType { 277 return keybase1.TrackDiffType_REVOKED 278} 279 280type TrackDiffSnoozedRevoked struct { 281 idc TrackIDComponent 282} 283 284func (t TrackDiffSnoozedRevoked) BreaksTracking() bool { 285 return false 286} 287func (t TrackDiffSnoozedRevoked) ToDisplayString() string { 288 return "Deleted proof: " + t.idc.ToIDString() + " (snoozed)" 289} 290func (t TrackDiffSnoozedRevoked) IsSameAsTracked() bool { 291 return true 292} 293func (t TrackDiffSnoozedRevoked) ToDisplayMarkup() *Markup { 294 return NewMarkup(t.ToDisplayString()) 295} 296func (t TrackDiffSnoozedRevoked) GetTrackDiffType() keybase1.TrackDiffType { 297 return keybase1.TrackDiffType_NONE_VIA_TEMPORARY 298} 299 300type TrackDiffRemoteFail struct { 301 observed keybase1.ProofState 302} 303 304func (t TrackDiffRemoteFail) BreaksTracking() bool { 305 return true 306} 307func (t TrackDiffRemoteFail) ToDisplayString() string { 308 return "remote failed" 309} 310func (t TrackDiffRemoteFail) ToDisplayMarkup() *Markup { 311 return NewMarkup(t.ToDisplayString()) 312} 313func (t TrackDiffRemoteFail) GetTrackDiffType() keybase1.TrackDiffType { 314 return keybase1.TrackDiffType_REMOTE_FAIL 315} 316func (t TrackDiffRemoteFail) IsSameAsTracked() bool { 317 return false 318} 319 320type TrackDiffRemoteWorking struct { 321 tracked keybase1.ProofState 322} 323 324func (t TrackDiffRemoteWorking) BreaksTracking() bool { 325 return false 326} 327func (t TrackDiffRemoteWorking) ToDisplayString() string { 328 return "newly working" 329} 330func (t TrackDiffRemoteWorking) ToDisplayMarkup() *Markup { 331 return NewMarkup(t.ToDisplayString()) 332} 333func (t TrackDiffRemoteWorking) GetTrackDiffType() keybase1.TrackDiffType { 334 return keybase1.TrackDiffType_REMOTE_WORKING 335} 336func (t TrackDiffRemoteWorking) IsSameAsTracked() bool { 337 return false 338} 339 340type TrackDiffRemoteChanged struct { 341 tracked, observed keybase1.ProofState 342} 343 344func (t TrackDiffRemoteChanged) BreaksTracking() bool { 345 return false 346} 347func (t TrackDiffRemoteChanged) ToDisplayString() string { 348 return "changed" 349} 350func (t TrackDiffRemoteChanged) ToDisplayMarkup() *Markup { 351 return NewMarkup(t.ToDisplayString()) 352} 353func (t TrackDiffRemoteChanged) GetTrackDiffType() keybase1.TrackDiffType { 354 return keybase1.TrackDiffType_REMOTE_CHANGED 355} 356func (t TrackDiffRemoteChanged) IsSameAsTracked() bool { 357 return false 358} 359 360type TrackDiffNewEldest struct { 361 tracked keybase1.KID 362 observed keybase1.KID 363} 364 365func (t TrackDiffNewEldest) BreaksTracking() bool { 366 return true 367} 368func (t TrackDiffNewEldest) IsSameAsTracked() bool { 369 return false 370} 371func (t TrackDiffNewEldest) GetTrackDiffType() keybase1.TrackDiffType { 372 return keybase1.TrackDiffType_NEW_ELDEST 373} 374func (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} 383func (t TrackDiffNewEldest) ToDisplayMarkup() *Markup { 384 return NewMarkup(t.ToDisplayString()) 385} 386 387func 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 400func (l *TrackLookup) GetCTime() time.Time { 401 return l.link.GetCTime() 402} 403 404//===================================================================== 405 406func 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 416func 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 457func LocalTrackChainLinkFor(m MetaContext, tracker, trackee keybase1.UID) (ret *TrackChainLink, err error) { 458 return localTrackChainLinkFor(m, tracker, trackee, false) 459} 460 461func LocalTmpTrackChainLinkFor(m MetaContext, tracker, trackee keybase1.UID) (ret *TrackChainLink, err error) { 462 return localTrackChainLinkFor(m, tracker, trackee, true) 463} 464 465func 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 474func 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 479func 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} 484