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 }