1// Copyright 2015 Keybase, Inc. All rights reserved. Use of 2// this source code is governed by the included BSD license. 3 4package keybase1 5 6import ( 7 "bytes" 8 "crypto/hmac" 9 "crypto/sha256" 10 "crypto/sha512" 11 "crypto/subtle" 12 "encoding/base64" 13 "encoding/binary" 14 "encoding/hex" 15 "encoding/json" 16 "errors" 17 "fmt" 18 "math" 19 "reflect" 20 "regexp" 21 "sort" 22 "strconv" 23 "strings" 24 "time" 25 26 "github.com/keybase/client/go/kbtime" 27 "github.com/keybase/go-framed-msgpack-rpc/rpc" 28 jsonw "github.com/keybase/go-jsonw" 29) 30 31const ( 32 UID_LEN = 16 33 UID_SUFFIX = 0x00 34 UID_SUFFIX_2 = 0x19 35 UID_SUFFIX_HEX = "00" 36 UID_SUFFIX_2_HEX = "19" 37 TEAMID_LEN = 16 38 TEAMID_PRIVATE_SUFFIX = 0x24 39 TEAMID_PRIVATE_SUFFIX_HEX = "24" 40 TEAMID_PUBLIC_SUFFIX = 0x2e 41 TEAMID_PUBLIC_SUFFIX_HEX = "2e" 42 SUB_TEAMID_PRIVATE_SUFFIX = 0x25 43 SUB_TEAMID_PRIVATE_SUFFIX_HEX = "25" 44 SUB_TEAMID_PUBLIC_SUFFIX = 0x2f 45 SUB_TEAMID_PUBLIC_SUFFIX_HEX = "2f" 46 PUBLIC_UID = "ffffffffffffffffffffffffffffff00" 47) 48 49// UID for the special "public" user. 50var PublicUID = UID(PUBLIC_UID) 51 52const ( 53 SIG_ID_LEN = 32 54 SIG_ID_SUFFIX = 0x0f 55 SIG_SHORT_ID_BYTES = 27 56 SigIDQueryMin = 8 57) 58 59const ( 60 DeviceIDLen = 16 61 DeviceIDSuffix = 0x18 62 DeviceIDSuffixHex = "18" 63) 64 65const ( 66 KidLen = 35 // bytes 67 KidSuffix = 0x0a // a byte 68 KidVersion = 0x1 69) 70 71const redactedReplacer = "[REDACTED]" 72 73func Unquote(data []byte) string { 74 return strings.Trim(string(data), "\"") 75} 76 77func Quote(s string) []byte { 78 return []byte("\"" + s + "\"") 79} 80 81func UnquoteBytes(data []byte) []byte { 82 return bytes.Trim(data, "\"") 83} 84 85func KIDFromSlice(b []byte) KID { 86 return KID(hex.EncodeToString(b)) 87} 88 89func (b BinaryKID) ToKID() KID { 90 return KIDFromSlice([]byte(b)) 91} 92 93func (k KID) ToBinaryKID() BinaryKID { 94 return BinaryKID(k.ToBytes()) 95} 96 97func (b BinaryKID) Equal(c BinaryKID) bool { 98 return bytes.Equal([]byte(b), []byte(c)) 99} 100 101func KIDFromStringChecked(s string) (KID, error) { 102 103 // It's OK to have a 0-length KID. That means, no such key 104 // (or NULL kid). 105 if len(s) == 0 { 106 return KID(""), nil 107 } 108 109 b, err := hex.DecodeString(s) 110 if err != nil { 111 return KID(""), err 112 } 113 114 if len(b) != KidLen { 115 return KID(""), fmt.Errorf("KID wrong length; wanted %d but got %d bytes", 116 KidLen, len(b)) 117 } 118 if b[len(b)-1] != KidSuffix { 119 return KID(""), fmt.Errorf("Bad KID suffix: got 0x%02x, wanted 0x%02x", 120 b[len(b)-1], KidSuffix) 121 } 122 if b[0] != KidVersion { 123 return KID(""), fmt.Errorf("Bad KID version; got 0x%02x but wanted 0x%02x", 124 b[0], KidVersion) 125 } 126 return KID(s), nil 127} 128 129func HashMetaFromString(s string) (ret HashMeta, err error) { 130 // TODO: Should we add similar handling to other types? 131 if s == "null" { 132 return nil, nil 133 } 134 b, err := hex.DecodeString(s) 135 if err != nil { 136 return ret, err 137 } 138 return HashMeta(b), nil 139} 140 141func cieq(s string, t string) bool { 142 return strings.ToLower(s) == strings.ToLower(t) 143} 144 145func KBFSRootHashFromString(s string) (ret KBFSRootHash, err error) { 146 if s == "null" { 147 return nil, nil 148 } 149 b, err := base64.StdEncoding.DecodeString(s) 150 if err != nil { 151 return ret, err 152 } 153 return KBFSRootHash(b), nil 154} 155 156func (h KBFSRootHash) String() string { 157 return hex.EncodeToString(h) 158} 159 160func (h KBFSRootHash) Eq(h2 KBFSRootHash) bool { 161 return hmac.Equal(h[:], h2[:]) 162} 163 164func (h HashMeta) String() string { 165 return hex.EncodeToString(h) 166} 167 168func (h HashMeta) Eq(h2 HashMeta) bool { 169 return hmac.Equal(h[:], h2[:]) 170} 171 172func (h *HashMeta) UnmarshalJSON(b []byte) error { 173 hm, err := HashMetaFromString(Unquote(b)) 174 if err != nil { 175 return err 176 } 177 *h = hm 178 return nil 179} 180 181func (h *KBFSRootHash) UnmarshalJSON(b []byte) error { 182 rh, err := KBFSRootHashFromString(Unquote(b)) 183 if err != nil { 184 return err 185 } 186 *h = rh 187 return nil 188} 189 190func SHA512FromString(s string) (ret SHA512, err error) { 191 if s == "null" { 192 return nil, nil 193 } 194 b, err := hex.DecodeString(s) 195 if err != nil { 196 return ret, err 197 } 198 if len(b) != 64 { 199 return nil, fmt.Errorf("Wanted a 64-byte SHA512, but got %d bytes", len(b)) 200 } 201 return SHA512(b), nil 202} 203 204func (s SHA512) String() string { 205 return hex.EncodeToString(s) 206} 207 208func (s SHA512) Eq(s2 SHA512) bool { 209 return hmac.Equal(s[:], s2[:]) 210} 211 212func (s *SHA512) UnmarshalJSON(b []byte) error { 213 tmp, err := SHA512FromString(Unquote(b)) 214 if err != nil { 215 return err 216 } 217 *s = tmp 218 return nil 219} 220 221func (t *ResetType) UnmarshalJSON(b []byte) error { 222 var err error 223 s := strings.TrimSpace(string(b)) 224 var ret ResetType 225 switch s { 226 case "\"reset\"", "1": 227 ret = ResetType_RESET 228 case "\"delete\"", "2": 229 ret = ResetType_DELETE 230 default: 231 err = fmt.Errorf("Bad reset type: %s", s) 232 } 233 *t = ret 234 return err 235} 236 237func (l *LeaseID) UnmarshalJSON(b []byte) error { 238 decoded, err := hex.DecodeString(Unquote(b)) 239 if err != nil { 240 return err 241 } 242 *l = LeaseID(hex.EncodeToString(decoded)) 243 return nil 244} 245 246func (h HashMeta) MarshalJSON() ([]byte, error) { 247 return Quote(h.String()), nil 248} 249 250func KIDFromString(s string) KID { 251 // there are no validations for KIDs (length, suffixes) 252 return KID(s) 253} 254 255func (k KID) IsValid() bool { 256 return len(k) > 0 257} 258 259func (k KID) String() string { 260 return string(k) 261} 262 263func (k KID) IsNil() bool { 264 return len(k) == 0 265} 266 267func (k KID) Exists() bool { 268 return !k.IsNil() 269} 270 271func (k KID) Equal(v KID) bool { 272 return k == v 273} 274 275func (k KID) NotEqual(v KID) bool { 276 return !k.Equal(v) 277} 278 279func (k KID) SecureEqual(v KID) bool { 280 return hmac.Equal(k.ToBytes(), v.ToBytes()) 281} 282 283func (k KID) Match(q string, exact bool) bool { 284 if k.IsNil() { 285 return false 286 } 287 288 if exact { 289 return cieq(k.String(), q) 290 } 291 292 if strings.HasPrefix(k.String(), strings.ToLower(q)) { 293 return true 294 } 295 if strings.HasPrefix(k.ToShortIDString(), q) { 296 return true 297 } 298 return false 299} 300 301func (k KID) ToBytes() []byte { 302 b, err := hex.DecodeString(string(k)) 303 if err != nil { 304 return nil 305 } 306 return b 307} 308 309func (k KID) GetKeyType() byte { 310 raw := k.ToBytes() 311 if len(raw) < 2 { 312 return 0 313 } 314 return raw[1] 315} 316 317func (k KID) ToShortIDString() string { 318 return encode(k.ToBytes()[0:12]) 319} 320 321func (k KID) ToJsonw() *jsonw.Wrapper { 322 if k.IsNil() { 323 return jsonw.NewNil() 324 } 325 return jsonw.NewString(string(k)) 326} 327 328func (k KID) IsIn(list []KID) bool { 329 for _, h := range list { 330 if h.Equal(k) { 331 return true 332 } 333 } 334 return false 335} 336 337func PGPFingerprintFromString(s string) (ret PGPFingerprint, err error) { 338 b, err := hex.DecodeString(s) 339 if err != nil { 340 return 341 } 342 copy(ret[:], b[:]) 343 return 344} 345 346func (p *PGPFingerprint) String() string { 347 return hex.EncodeToString(p[:]) 348} 349 350func (p PGPFingerprint) MarshalJSON() ([]byte, error) { 351 return Quote(p.String()), nil 352} 353 354func (p *PGPFingerprint) UnmarshalJSON(b []byte) error { 355 tmp, err := PGPFingerprintFromString(Unquote(b)) 356 if err != nil { 357 return err 358 } 359 *p = tmp 360 return nil 361} 362 363func DeviceIDFromBytes(b [DeviceIDLen]byte) DeviceID { 364 return DeviceID(hex.EncodeToString(b[:])) 365} 366 367func (d DeviceID) ToBytes(out []byte) error { 368 tmp, err := hex.DecodeString(string(d)) 369 if err != nil { 370 return err 371 } 372 if len(tmp) != DeviceIDLen { 373 return fmt.Errorf("Bad device ID; wanted %d bytes but got %d", DeviceIDLen, len(tmp)) 374 } 375 if len(out) != DeviceIDLen { 376 return fmt.Errorf("Need to output to a slice with %d bytes", DeviceIDLen) 377 } 378 copy(out[:], tmp) 379 return nil 380} 381 382func DeviceIDFromSlice(b []byte) (DeviceID, error) { 383 if len(b) != DeviceIDLen { 384 return "", fmt.Errorf("invalid byte slice for DeviceID: len == %d, expected %d", len(b), DeviceIDLen) 385 } 386 var x [DeviceIDLen]byte 387 copy(x[:], b) 388 return DeviceIDFromBytes(x), nil 389} 390 391func LinkIDFromByte32(b [32]byte) LinkID { 392 return LinkID(hex.EncodeToString(b[:])) 393} 394 395func DeviceIDFromString(s string) (DeviceID, error) { 396 if len(s) != hex.EncodedLen(DeviceIDLen) { 397 return "", fmt.Errorf("Bad Device ID length: %d", len(s)) 398 } 399 suffix := s[len(s)-2:] 400 if suffix != DeviceIDSuffixHex { 401 return "", fmt.Errorf("Bad suffix byte: %s", suffix) 402 } 403 return DeviceID(s), nil 404} 405 406func (d DeviceID) String() string { 407 return string(d) 408} 409 410func (d DeviceID) IsNil() bool { 411 return len(d) == 0 412} 413 414func (d DeviceID) Exists() bool { 415 return !d.IsNil() 416} 417 418func (d DeviceID) Eq(d2 DeviceID) bool { 419 return d == d2 420} 421 422func (t TeamID) Eq(t2 TeamID) bool { 423 return t == t2 424} 425 426func (l LinkID) Eq(l2 LinkID) bool { 427 return l == l2 428} 429 430func (l LinkID) IsNil() bool { 431 return len(l) == 0 432} 433 434func (l LinkID) String() string { 435 return string(l) 436} 437 438func NilTeamID() TeamID { return TeamID("") } 439 440func (s Seqno) Eq(s2 Seqno) bool { 441 return s == s2 442} 443 444func (s Seqno) String() string { 445 return fmt.Sprintf("%d", s) 446} 447 448func UIDFromString(s string) (UID, error) { 449 if len(s) != hex.EncodedLen(UID_LEN) { 450 return "", fmt.Errorf("Bad UID '%s'; must be %d bytes long", s, UID_LEN) 451 } 452 suffix := s[len(s)-2:] 453 if suffix != UID_SUFFIX_HEX && suffix != UID_SUFFIX_2_HEX { 454 return "", fmt.Errorf("Bad UID '%s': must end in 0x%x or 0x%x", s, UID_SUFFIX, UID_SUFFIX_2) 455 } 456 return UID(s), nil 457} 458 459func UIDFromSlice(b []byte) (UID, error) { 460 return UIDFromString(hex.EncodeToString(b)) 461} 462 463// Used by unit tests. 464func MakeTestUID(n uint32) UID { 465 b := make([]byte, 8) 466 binary.LittleEndian.PutUint32(b, n) 467 s := hex.EncodeToString(b) 468 c := 2*UID_LEN - len(UID_SUFFIX_HEX) - len(s) 469 s += strings.Repeat("0", c) + UID_SUFFIX_HEX 470 uid, err := UIDFromString(s) 471 if err != nil { 472 panic(err) 473 } 474 return uid 475} 476 477func (u UID) String() string { 478 return string(u) 479} 480 481func (u UID) ToBytes() []byte { 482 b, err := hex.DecodeString(string(u)) 483 if err != nil { 484 return nil 485 } 486 return b 487} 488 489func (u UID) IsNil() bool { 490 return len(u) == 0 491} 492 493func (u UID) Exists() bool { 494 return !u.IsNil() 495} 496 497func (u UID) Equal(v UID) bool { 498 return u == v 499} 500 501func (u UID) NotEqual(v UID) bool { 502 return !u.Equal(v) 503} 504 505func (u UID) Less(v UID) bool { 506 return u < v 507} 508 509func (u UID) AsUserOrTeam() UserOrTeamID { 510 return UserOrTeamID(u) 511} 512 513func TeamIDFromString(s string) (TeamID, error) { 514 if len(s) != hex.EncodedLen(TEAMID_LEN) { 515 return "", fmt.Errorf("Bad TeamID '%s'; must be %d bytes long", s, TEAMID_LEN) 516 } 517 suffix := s[len(s)-2:] 518 switch suffix { 519 case TEAMID_PRIVATE_SUFFIX_HEX, TEAMID_PUBLIC_SUFFIX_HEX, 520 SUB_TEAMID_PRIVATE_SUFFIX_HEX, SUB_TEAMID_PUBLIC_SUFFIX_HEX: 521 return TeamID(s), nil 522 } 523 return "", fmt.Errorf("Bad TeamID '%s': must end in one of [0x%x, 0x%x, 0x%x, 0x%x]", 524 s, TEAMID_PRIVATE_SUFFIX_HEX, TEAMID_PUBLIC_SUFFIX_HEX, SUB_TEAMID_PRIVATE_SUFFIX, SUB_TEAMID_PUBLIC_SUFFIX) 525} 526 527func UserOrTeamIDFromString(s string) (UserOrTeamID, error) { 528 UID, errUser := UIDFromString(s) 529 if errUser == nil { 530 return UID.AsUserOrTeam(), nil 531 } 532 teamID, errTeam := TeamIDFromString(s) 533 if errTeam == nil { 534 return teamID.AsUserOrTeam(), nil 535 } 536 return "", fmt.Errorf("Bad UserOrTeamID: could not parse %s as a UID (err = %v) or team id (err = %v)", s, errUser, errTeam) 537} 538 539// Used by unit tests. 540func MakeTestTeamID(n uint32, public bool) TeamID { 541 b := make([]byte, 8) 542 binary.LittleEndian.PutUint32(b, n) 543 s := hex.EncodeToString(b) 544 useSuffix := TEAMID_PRIVATE_SUFFIX_HEX 545 if public { 546 useSuffix = TEAMID_PUBLIC_SUFFIX_HEX 547 } 548 c := 2*TEAMID_LEN - len(useSuffix) - len(s) 549 s += strings.Repeat("0", c) + useSuffix 550 tid, err := TeamIDFromString(s) 551 if err != nil { 552 panic(err) 553 } 554 return tid 555} 556 557// Used by unit tests. 558func MakeTestSubTeamID(n uint32, public bool) TeamID { 559 b := make([]byte, 8) 560 binary.LittleEndian.PutUint32(b, n) 561 s := hex.EncodeToString(b) 562 useSuffix := SUB_TEAMID_PRIVATE_SUFFIX_HEX 563 if public { 564 useSuffix = SUB_TEAMID_PUBLIC_SUFFIX_HEX 565 } 566 c := 2*TEAMID_LEN - len(useSuffix) - len(s) 567 s += strings.Repeat("0", c) + useSuffix 568 tid, err := TeamIDFromString(s) 569 if err != nil { 570 panic(err) 571 } 572 return tid 573} 574 575// Can panic if invalid 576func (t TeamID) IsSubTeam() bool { 577 suffix := t[len(t)-2:] 578 switch suffix { 579 case SUB_TEAMID_PRIVATE_SUFFIX_HEX, SUB_TEAMID_PUBLIC_SUFFIX_HEX: 580 return true 581 } 582 return false 583} 584 585func (t TeamID) IsRootTeam() bool { 586 return !t.IsSubTeam() 587} 588 589func (t TeamID) IsPublic() bool { 590 suffix := t[len(t)-2:] 591 switch suffix { 592 case TEAMID_PUBLIC_SUFFIX_HEX, SUB_TEAMID_PUBLIC_SUFFIX_HEX: 593 return true 594 } 595 return false 596} 597 598func (t TeamID) String() string { 599 return string(t) 600} 601 602func (t TeamID) ToBytes() []byte { 603 b, err := hex.DecodeString(string(t)) 604 if err != nil { 605 return nil 606 } 607 return b 608} 609 610func (t TeamID) IsNil() bool { 611 return len(t) == 0 612} 613 614func (t TeamID) Exists() bool { 615 return !t.IsNil() 616} 617 618func (t TeamID) Equal(v TeamID) bool { 619 return t == v 620} 621 622func (t TeamID) NotEqual(v TeamID) bool { 623 return !t.Equal(v) 624} 625 626func (t TeamID) Less(v TeamID) bool { 627 return t < v 628} 629 630func (t TeamID) AsUserOrTeam() UserOrTeamID { 631 return UserOrTeamID(t) 632} 633 634const ptrSize = 4 << (^uintptr(0) >> 63) // stolen from runtime/internal/sys 635 636// Size implements the cache.Measurable interface. 637func (t TeamID) Size() int { 638 return len(t) + ptrSize 639} 640 641func (s SigID) IsNil() bool { 642 return len(s) == 0 643} 644 645func (s SigID) Exists() bool { 646 return !s.IsNil() 647} 648 649func (s SigID) String() string { return string(s) } 650 651func (s SigID) ToDisplayString(verbose bool) string { 652 if verbose { 653 return string(s) 654 } 655 return fmt.Sprintf("%s...", s[0:SigIDQueryMin]) 656} 657 658func (s SigID) PrefixMatch(q string, exact bool) bool { 659 if s.IsNil() { 660 return false 661 } 662 663 if exact { 664 return cieq(string(s), q) 665 } 666 667 if strings.HasPrefix(strings.ToLower(string(s)), strings.ToLower(q)) { 668 return true 669 } 670 671 return false 672} 673 674func SigIDFromString(s string) (SigID, error) { 675 // Add 1 extra byte for the suffix 676 blen := SIG_ID_LEN + 1 677 if len(s) != hex.EncodedLen(blen) { 678 return "", fmt.Errorf("Invalid SigID string length: %d, expected %d", len(s), hex.EncodedLen(blen)) 679 } 680 s = strings.ToLower(s) 681 // Throw the outcome away, but we're checking that we can decode the value as hex 682 _, err := hex.DecodeString(s) 683 if err != nil { 684 return "", err 685 } 686 return SigID(s), nil 687} 688 689func (s SigID) ToBytes() []byte { 690 b, err := hex.DecodeString(string(s)) 691 if err != nil { 692 return nil 693 } 694 return b[0:SIG_ID_LEN] 695} 696 697func (s SigID) StripSuffix() SigIDBase { 698 l := hex.EncodedLen(SIG_ID_LEN) 699 if len(s) == l { 700 return SigIDBase(string(s)) 701 } 702 return SigIDBase(string(s[0:l])) 703} 704 705func (s SigID) Eq(t SigID) bool { 706 b := s.ToBytes() 707 c := t.ToBytes() 708 if b == nil || c == nil { 709 return false 710 } 711 return hmac.Equal(b, c) 712} 713 714type SigIDMapKey string 715 716// ToMapKey returns the string representation (hex-encoded) of a SigID with the hardcoded 0x0f suffix 717// (for backward comptability with on-disk storage). 718func (s SigID) ToMapKey() SigIDMapKey { 719 return SigIDMapKey(s.StripSuffix().ToSigIDLegacy().String()) 720} 721 722func (s SigID) ToMediumID() string { 723 return encode(s.ToBytes()) 724} 725 726func (s SigID) ToShortID() string { 727 return encode(s.ToBytes()[0:SIG_SHORT_ID_BYTES]) 728} 729 730// SigIDBase is a 64-character long hex encoding of the SHA256 of a signature, without 731// any suffix. You get a SigID by adding either a 0f or a 22 suffix. 732type SigIDBase string 733 734func (s SigIDBase) String() string { 735 return string(s) 736} 737 738func SigIDBaseFromBytes(b [SIG_ID_LEN]byte) SigIDBase { 739 s := hex.EncodeToString(b[:]) 740 return SigIDBase(s) 741} 742 743// MarshalJSON output the SigIDBase as a full SigID to be compatible 744// with legacy versions of the app. 745func (s SigIDBase) MarshalJSON() ([]byte, error) { 746 return Quote(s.ToSigIDLegacy().String()), nil 747} 748 749// UnmarshalJSON will accept either a SigID or a SigIDBase, and can 750// strip off the suffix. 751func (s *SigIDBase) UnmarshalJSON(b []byte) error { 752 tmp := Unquote(b) 753 754 l := hex.EncodedLen(SIG_ID_LEN) 755 if len(tmp) == l { 756 base, err := SigIDBaseFromString(tmp) 757 if err != nil { 758 return err 759 } 760 *s = base 761 return nil 762 } 763 764 // If we didn't get a sigID the right size, try to strip off the suffix. 765 sigID, err := SigIDFromString(tmp) 766 if err != nil { 767 return err 768 } 769 *s = sigID.StripSuffix() 770 return nil 771} 772 773func SigIDBaseFromSlice(b []byte) (SigIDBase, error) { 774 var buf [32]byte 775 if len(b) != len(buf) { 776 return "", errors.New("need a SHA256 hash, got something the wrong length") 777 } 778 copy(buf[:], b[:]) 779 return SigIDBaseFromBytes(buf), nil 780} 781 782func SigIDBaseFromString(s string) (SigIDBase, error) { 783 b, err := hex.DecodeString(s) 784 if err != nil { 785 return "", err 786 } 787 return SigIDBaseFromSlice(b) 788} 789 790func (s SigIDBase) EqSigID(t SigID) bool { 791 return cieq(s.String(), t.StripSuffix().String()) 792} 793 794// SigIDSuffixParameters specify how to turn a 64-character SigIDBase into a 66-character SigID, 795// via the two suffixes. In the future, there might be a third, 38, in use. 796type SigIDSuffixParameters struct { 797 IsUserSig bool // true for user, false for team 798 IsWalletStellar bool // exceptional sig type for backwards compatibility 799 SigVersion SigVersion // 1,2 or 3 supported now 800} 801 802func SigIDSuffixParametersFromTypeAndVersion(typ string, vers SigVersion) SigIDSuffixParameters { 803 return SigIDSuffixParameters{ 804 IsUserSig: !strings.HasPrefix(typ, "teams."), 805 IsWalletStellar: (typ == "wallet.stellar"), 806 SigVersion: vers, 807 } 808} 809 810func (s SigIDSuffixParameters) String() string { 811 if s.IsWalletStellar && s.SigVersion == 2 { 812 return "22" 813 } 814 if s.IsUserSig { 815 return "0f" 816 } 817 switch s.SigVersion { 818 case 2: 819 return "22" 820 case 3: 821 return "38" 822 default: 823 return "0f" 824 } 825} 826 827func (s SigIDBase) ToSigID(p SigIDSuffixParameters) SigID { 828 return SigID(string(s) + p.String()) 829} 830 831// ToSigIDLegacy does what all of Keybase used to do, which is to always assign a 0x0f 832// suffix to SigIDBases to get SigIDs. 833func (s SigIDBase) ToSigIDLegacy() SigID { 834 return s.ToSigID(SigIDSuffixParameters{IsUserSig: true, IsWalletStellar: false, SigVersion: 1}) 835} 836 837func (s SigIDBase) Eq(t SigIDBase) bool { 838 return cieq(string(s), string(t)) 839} 840 841func (s SigIDBase) ToBytes() []byte { 842 x, err := hex.DecodeString(string(s)) 843 if err != nil { 844 return nil 845 } 846 return x 847} 848 849func encode(b []byte) string { 850 return strings.TrimRight(base64.URLEncoding.EncodeToString(b), "=") 851} 852 853func FromTime(t Time) time.Time { 854 if t == 0 { 855 return time.Time{} 856 } 857 // t is in millisecond. 858 tSec := int64(t) / 1000 859 tNanoSecOffset := (int64(t) - tSec*1000) * 1000000 860 return time.Unix(tSec, tNanoSecOffset) 861} 862 863func (t Time) Time() time.Time { 864 return FromTime(t) 865} 866 867func (t Time) UnixSeconds() int64 { 868 return t.Time().Unix() 869} 870 871func ToTime(t time.Time) Time { 872 if t.IsZero() { 873 return 0 874 } 875 876 return Time(t.Unix()*1000 + (int64(t.Nanosecond()) / 1000000)) 877} 878 879func ToTimePtr(t *time.Time) *Time { 880 if t == nil { 881 return nil 882 } 883 ret := ToTime(*t) 884 return &ret 885} 886 887func TimeFromSeconds(seconds int64) Time { 888 return Time(seconds * 1000) 889} 890 891func (t Time) IsZero() bool { return t == 0 } 892func (t Time) After(t2 Time) bool { return t > t2 } 893func (t Time) Before(t2 Time) bool { return t < t2 } 894 895func FormatTime(t Time) string { 896 layout := "2006-01-02 15:04:05 MST" 897 return FromTime(t).Format(layout) 898} 899 900func FromUnixTime(u UnixTime) time.Time { 901 if u == 0 { 902 return time.Time{} 903 } 904 return time.Unix(int64(u), 0) 905} 906 907func (u UnixTime) Time() time.Time { 908 return FromUnixTime(u) 909} 910 911func (u UnixTime) UnixSeconds() int64 { 912 return int64(u) 913} 914 915func ToUnixTime(t time.Time) UnixTime { 916 if t.IsZero() { 917 return 0 918 } 919 return UnixTime(t.Unix()) 920} 921 922func UnixTimeFromSeconds(seconds int64) UnixTime { 923 return UnixTime(seconds) 924} 925 926func (u UnixTime) IsZero() bool { return u == UnixTime(0) } 927func (u UnixTime) After(u2 UnixTime) bool { return u > u2 } 928func (u UnixTime) Before(u2 UnixTime) bool { return u < u2 } 929func FormatUnixTime(u UnixTime) string { 930 layout := "2006-01-02 15:04:05 MST" 931 return FromUnixTime(u).Format(layout) 932} 933 934func (s Status) Error() string { 935 if s.Code == int(StatusCode_SCOk) { 936 return "" 937 } 938 return fmt.Sprintf("%s (%s/%d)", s.Desc, s.Name, s.Code) 939} 940 941func (s Status) GoError() error { 942 if s.Code == int(StatusCode_SCOk) { 943 return nil 944 } 945 return fmt.Errorf(s.Error()) 946} 947 948func (s InstallStatus) String() string { 949 switch s { 950 case InstallStatus_UNKNOWN: 951 return "Unknown" 952 case InstallStatus_ERROR: 953 return "Error" 954 case InstallStatus_NOT_INSTALLED: 955 return "Not Installed" 956 case InstallStatus_INSTALLED: 957 return "Installed" 958 } 959 return "" 960} 961 962func (s InstallAction) String() string { 963 switch s { 964 case InstallAction_UNKNOWN: 965 return "Unknown" 966 case InstallAction_NONE: 967 return "None" 968 case InstallAction_UPGRADE: 969 return "Upgrade" 970 case InstallAction_REINSTALL: 971 return "Re-Install" 972 case InstallAction_INSTALL: 973 return "Install" 974 } 975 return "" 976} 977 978func (s ServiceStatus) NeedsInstall() bool { 979 return s.InstallAction == InstallAction_INSTALL || 980 s.InstallAction == InstallAction_REINSTALL || 981 s.InstallAction == InstallAction_UPGRADE 982} 983 984func (k *KID) UnmarshalJSON(b []byte) error { 985 kid, err := KIDFromStringChecked(Unquote(b)) 986 if err != nil { 987 return err 988 } 989 *k = KID(kid) 990 return nil 991} 992 993func (u *UID) UnmarshalJSON(b []byte) error { 994 uid, err := UIDFromString(Unquote(b)) 995 if err != nil { 996 return err 997 } 998 *u = uid 999 return nil 1000} 1001 1002func (u *UserOrTeamID) UnmarshalJSON(b []byte) error { 1003 utid, err := UserOrTeamIDFromString(Unquote(b)) 1004 if err != nil { 1005 return err 1006 } 1007 *u = utid 1008 return nil 1009} 1010 1011// Size implements the cache.Measurable interface. 1012func (u UID) Size() int { 1013 return len(u) + ptrSize 1014} 1015 1016func (k *KID) MarshalJSON() ([]byte, error) { 1017 return Quote(k.String()), nil 1018} 1019 1020func (u *UID) MarshalJSON() ([]byte, error) { 1021 return Quote(u.String()), nil 1022} 1023 1024func (u *UserOrTeamID) MarshalJSON() ([]byte, error) { 1025 return Quote(u.String()), nil 1026} 1027 1028// Size implements the keybase/kbfs/cache.Measurable interface. 1029func (k *KID) Size() int { 1030 if k == nil { 1031 return 0 1032 } 1033 return len(*k) + ptrSize 1034} 1035 1036func (s *SigID) UnmarshalJSON(b []byte) error { 1037 sigID, err := SigIDFromString(Unquote(b)) 1038 if err != nil { 1039 return err 1040 } 1041 *s = sigID 1042 return nil 1043} 1044 1045func (s *SigID) MarshalJSON() ([]byte, error) { 1046 return Quote(s.String()), nil 1047} 1048 1049func (f Folder) ToString() string { 1050 prefix := "<unrecognized>" 1051 switch f.FolderType { 1052 case FolderType_PRIVATE: 1053 prefix = "private" 1054 case FolderType_PUBLIC: 1055 prefix = "public" 1056 case FolderType_TEAM: 1057 prefix = "team" 1058 } 1059 return prefix + "/" + f.Name 1060} 1061 1062func (f Folder) String() string { 1063 return f.ToString() 1064} 1065 1066func (f FolderHandle) ToString() string { 1067 prefix := "<unrecognized>" 1068 switch f.FolderType { 1069 case FolderType_PRIVATE: 1070 prefix = "private" 1071 case FolderType_PUBLIC: 1072 prefix = "public" 1073 case FolderType_TEAM: 1074 prefix = "team" 1075 } 1076 return prefix + "/" + f.Name 1077} 1078 1079func (f FolderHandle) String() string { 1080 return f.ToString() 1081} 1082 1083func (t TrackToken) String() string { 1084 return string(t) 1085} 1086 1087func KIDFromRawKey(b []byte, keyType byte) KID { 1088 tmp := []byte{KidVersion, keyType} 1089 tmp = append(tmp, b...) 1090 tmp = append(tmp, byte(KidSuffix)) 1091 return KIDFromSlice(tmp) 1092} 1093 1094type APIStatus interface { 1095 Status() Status 1096} 1097 1098type Error struct { 1099 code StatusCode 1100 message string 1101} 1102 1103func NewError(code StatusCode, message string) *Error { 1104 if code == StatusCode_SCOk { 1105 return nil 1106 } 1107 return &Error{code: code, message: message} 1108} 1109 1110func FromError(err error) *Error { 1111 return &Error{code: StatusCode_SCGeneric, message: err.Error()} 1112} 1113 1114func StatusOK(desc string) Status { 1115 if desc == "" { 1116 desc = "OK" 1117 } 1118 return Status{Code: int(StatusCode_SCOk), Name: "OK", Desc: desc} 1119} 1120 1121func StatusFromCode(code StatusCode, message string) Status { 1122 if code == StatusCode_SCOk { 1123 return StatusOK(message) 1124 } 1125 return NewError(code, message).Status() 1126} 1127 1128func (e *Error) Error() string { 1129 return e.message 1130} 1131 1132func (e *Error) Status() Status { 1133 return Status{Code: int(e.code), Name: "ERROR", Desc: e.message} 1134} 1135 1136func (t ClientType) String() string { 1137 switch t { 1138 case ClientType_CLI: 1139 return "command-line client" 1140 case ClientType_KBFS: 1141 return "KBFS" 1142 case ClientType_GUI_MAIN: 1143 return "desktop" 1144 case ClientType_GUI_HELPER: 1145 return "desktop helper" 1146 default: 1147 return "other" 1148 } 1149} 1150 1151func (m MerkleTreeID) Number() int { 1152 return int(m) 1153} 1154 1155func (m MerkleTreeID) String() string { 1156 return strconv.Itoa(int(m)) 1157} 1158 1159func (r BlockReference) String() string { 1160 return fmt.Sprintf("%s,%s", r.Bid.BlockHash, hex.EncodeToString(r.Nonce[:])) 1161} 1162 1163func (r BlockReferenceCount) String() string { 1164 return fmt.Sprintf("%s,%d", r.Ref.String(), r.LiveCount) 1165} 1166 1167func (sa SocialAssertion) String() string { 1168 if sa.Service == "email" { 1169 return fmt.Sprintf("[%s]@email", sa.User) 1170 } 1171 return fmt.Sprintf("%s@%s", sa.User, sa.Service) 1172} 1173 1174func (sa SocialAssertion) TeamInviteType() string { 1175 return string(sa.Service) 1176} 1177 1178func (sa SocialAssertion) TeamInviteName() TeamInviteName { 1179 return TeamInviteName(sa.User) 1180} 1181 1182func (a GetArg) GetEndpoint() string { 1183 return a.Endpoint 1184} 1185 1186func (a GetArg) GetHTTPArgs() []StringKVPair { 1187 return a.Args 1188} 1189 1190func (a GetArg) GetHttpStatuses() []int { 1191 return a.HttpStatus 1192} 1193 1194func (a GetArg) GetAppStatusCodes() []int { 1195 return a.AppStatusCode 1196} 1197 1198func (a GetWithSessionArg) GetEndpoint() string { 1199 return a.Endpoint 1200} 1201 1202func (a GetWithSessionArg) GetHTTPArgs() []StringKVPair { 1203 return a.Args 1204} 1205 1206func (a GetWithSessionArg) GetHttpStatuses() []int { 1207 return a.HttpStatus 1208} 1209 1210func (a GetWithSessionArg) GetAppStatusCodes() []int { 1211 return a.AppStatusCode 1212} 1213 1214func (a PostArg) GetEndpoint() string { 1215 return a.Endpoint 1216} 1217 1218func (a PostArg) GetHTTPArgs() []StringKVPair { 1219 return a.Args 1220} 1221 1222func (a PostArg) GetHttpStatuses() []int { 1223 return a.HttpStatus 1224} 1225 1226func (a PostArg) GetAppStatusCodes() []int { 1227 return a.AppStatusCode 1228} 1229 1230func (a PostJSONArg) GetEndpoint() string { 1231 return a.Endpoint 1232} 1233 1234func (a PostJSONArg) GetHTTPArgs() []StringKVPair { 1235 return a.Args 1236} 1237 1238func (a PostJSONArg) GetHttpStatuses() []int { 1239 return a.HttpStatus 1240} 1241 1242func (a PostJSONArg) GetAppStatusCodes() []int { 1243 return a.AppStatusCode 1244} 1245 1246func (a DeleteArg) GetEndpoint() string { 1247 return a.Endpoint 1248} 1249 1250func (a DeleteArg) GetHTTPArgs() []StringKVPair { 1251 return a.Args 1252} 1253 1254func (a DeleteArg) GetHttpStatuses() []int { 1255 return a.HttpStatus 1256} 1257 1258func (a DeleteArg) GetAppStatusCodes() []int { 1259 return a.AppStatusCode 1260} 1261 1262// ToStatusAble is something that can be coerced into a status. Some error types 1263// in your application might want this. 1264type ToStatusAble interface { 1265 ToStatus() Status 1266} 1267 1268// WrapError is a generic method that converts a Go Error into a RPC error status object. 1269// If the error is itself a Status object to being with, then it will just return that 1270// status object. If it is something that can be made into a Status object via the 1271// ToStatusAble interface, then we'll try that. Otherwise, we'll just make a generic 1272// Error type. 1273func WrapError(e error) interface{} { 1274 1275 if e == nil { 1276 return nil 1277 } 1278 1279 if ee, ok := e.(ToStatusAble); ok { 1280 tmp := ee.ToStatus() 1281 return &tmp 1282 } 1283 1284 if status, ok := e.(*Status); ok { 1285 return status 1286 } 1287 1288 if status, ok := e.(Status); ok { 1289 return status 1290 } 1291 1292 return Status{ 1293 Name: "GENERIC", 1294 Code: int(StatusCode_SCGeneric), 1295 Desc: e.Error(), 1296 } 1297} 1298 1299// WrapError should function as a valid WrapErrorFunc as used by the RPC library. 1300var _ rpc.WrapErrorFunc = WrapError 1301 1302// ErrorUnwrapper is converter that take a Status object off the wire and convert it 1303// into an Error that Go can understand, and you can discriminate on in your code. 1304// Though status object can act as Go errors, you can further convert them into 1305// typed errors via the Upcaster function if specified. An Upcaster takes a Status 1306// and returns something that obeys the Error interface, but can be anything your 1307// program needs. 1308type ErrorUnwrapper struct { 1309 Upcaster func(status Status) error 1310} 1311 1312// MakeArg just makes a dummy object that we can unmarshal into, as needed by the 1313// underlying RPC library. 1314func (eu ErrorUnwrapper) MakeArg() interface{} { 1315 return &Status{} 1316} 1317 1318// UnwrapError takes an incoming RPC object, attempts to coerce it into a Status 1319// object, and then Upcasts via the Upcaster or just returns if not was provided. 1320func (eu ErrorUnwrapper) UnwrapError(arg interface{}) (appError, dispatchError error) { 1321 targ, ok := arg.(*Status) 1322 if !ok { 1323 dispatchError = errors.New("Error converting status to keybase1.Status object") 1324 return nil, dispatchError 1325 } 1326 if targ == nil { 1327 return nil, nil 1328 } 1329 if targ.Code == int(StatusCode_SCOk) { 1330 return nil, nil 1331 } 1332 1333 if eu.Upcaster != nil { 1334 appError = eu.Upcaster(*targ) 1335 } else { 1336 appError = *targ 1337 } 1338 return appError, nil 1339} 1340 1341// Assert that Status can function as an error object. 1342var _ error = Status{} 1343 1344// Assert that our ErrorUnwrapper fits the RPC error unwrapper spec. 1345var _ rpc.ErrorUnwrapper = ErrorUnwrapper{} 1346 1347func (t TLFID) String() string { 1348 return string(t) 1349} 1350 1351func (t TLFID) IsNil() bool { 1352 return len(t) == 0 1353} 1354 1355func (t TLFID) Exists() bool { 1356 return !t.IsNil() 1357} 1358 1359func (t TLFID) ToBytes() []byte { 1360 b, err := hex.DecodeString(string(t)) 1361 if err != nil { 1362 return nil 1363 } 1364 return b 1365} 1366 1367func (t TLFID) Eq(u TLFID) bool { 1368 return t == u 1369} 1370 1371func (b TLFIdentifyBehavior) UnblockThenForceIDTable() bool { 1372 switch b { 1373 case TLFIdentifyBehavior_GUI_PROFILE: 1374 return true 1375 default: 1376 return false 1377 } 1378} 1379 1380func (b TLFIdentifyBehavior) AlwaysRunIdentify() bool { 1381 switch b { 1382 case TLFIdentifyBehavior_CHAT_CLI, 1383 TLFIdentifyBehavior_CHAT_GUI, 1384 TLFIdentifyBehavior_SALTPACK, 1385 TLFIdentifyBehavior_KBFS_CHAT, 1386 TLFIdentifyBehavior_GUI_PROFILE: 1387 return true 1388 default: 1389 return false 1390 } 1391} 1392 1393func (b TLFIdentifyBehavior) CanUseUntrackedFastPath() bool { 1394 switch b { 1395 case TLFIdentifyBehavior_CHAT_GUI, 1396 TLFIdentifyBehavior_FS_GUI, 1397 TLFIdentifyBehavior_SALTPACK, 1398 TLFIdentifyBehavior_RESOLVE_AND_CHECK: 1399 return true 1400 default: 1401 // TLFIdentifyBehavior_DEFAULT_KBFS, for filesystem activity that 1402 // doesn't have any other UI to report errors with. 1403 return false 1404 } 1405} 1406 1407func (b TLFIdentifyBehavior) WarningInsteadOfErrorOnBrokenTracks() bool { 1408 switch b { 1409 case TLFIdentifyBehavior_CHAT_GUI, 1410 TLFIdentifyBehavior_FS_GUI: 1411 // The chat GUI is specifically exempted from broken 1412 // track errors, because people need to be able to use it to ask each other 1413 // about the fact that proofs are broken. 1414 return true 1415 default: 1416 return false 1417 } 1418} 1419 1420func (b TLFIdentifyBehavior) NotifyGUIAboutBreaks() bool { 1421 switch b { 1422 case TLFIdentifyBehavior_FS_GUI: 1423 // Technically chat needs this too but is done in go/chat by itself and 1424 // doesn't use this. So we only put FS_GUI here. 1425 return true 1426 default: 1427 return false 1428 } 1429} 1430 1431func (b TLFIdentifyBehavior) SkipUserCard() bool { 1432 switch b { 1433 case TLFIdentifyBehavior_CHAT_GUI, 1434 TLFIdentifyBehavior_FS_GUI, 1435 TLFIdentifyBehavior_RESOLVE_AND_CHECK: 1436 // We don't need to bother loading a user card in these cases. 1437 return true 1438 default: 1439 return false 1440 } 1441} 1442 1443func (b TLFIdentifyBehavior) AllowCaching() bool { 1444 switch b { 1445 case TLFIdentifyBehavior_RESOLVE_AND_CHECK: 1446 // We Don't want to use any internal ID2 caching for ResolveAndCheck. 1447 return false 1448 default: 1449 return true 1450 } 1451} 1452 1453func (b TLFIdentifyBehavior) AllowDeletedUsers() bool { 1454 switch b { 1455 case TLFIdentifyBehavior_RESOLVE_AND_CHECK: 1456 // ResolveAndCheck is OK with deleted users 1457 return true 1458 default: 1459 return false 1460 } 1461} 1462 1463// All of the chat modes want to prevent tracker popups. 1464func (b TLFIdentifyBehavior) ShouldSuppressTrackerPopups() bool { 1465 switch b { 1466 case TLFIdentifyBehavior_CHAT_GUI, 1467 TLFIdentifyBehavior_FS_GUI, 1468 TLFIdentifyBehavior_CHAT_CLI, 1469 TLFIdentifyBehavior_KBFS_REKEY, 1470 TLFIdentifyBehavior_KBFS_QR, 1471 TLFIdentifyBehavior_SALTPACK, 1472 TLFIdentifyBehavior_RESOLVE_AND_CHECK, 1473 TLFIdentifyBehavior_KBFS_CHAT, 1474 TLFIdentifyBehavior_KBFS_INIT: 1475 // These are identifies that either happen without user interaction at 1476 // all, or happen while you're staring at some Keybase UI that can 1477 // report errors on its own. No popups needed. 1478 return true 1479 default: 1480 // TLFIdentifyBehavior_DEFAULT_KBFS, for filesystem activity that 1481 // doesn't have any other UI to report errors with. 1482 return false 1483 } 1484} 1485 1486// SkipExternalChecks indicates we do not want to run any external proof checkers in 1487// identify modes that yield true. 1488func (b TLFIdentifyBehavior) SkipExternalChecks() bool { 1489 switch b { 1490 case TLFIdentifyBehavior_KBFS_QR, 1491 TLFIdentifyBehavior_KBFS_REKEY: 1492 return true 1493 default: 1494 return false 1495 } 1496} 1497 1498// ShouldRefreshChatView indicates that when the identify is complete, we 1499// should update the chat system's view of the computed track breaks (also 1500// affects username coloring in the GUI). 1501func (b TLFIdentifyBehavior) ShouldRefreshChatView() bool { 1502 switch b { 1503 case TLFIdentifyBehavior_GUI_PROFILE, TLFIdentifyBehavior_CLI: 1504 return true 1505 default: 1506 return false 1507 } 1508} 1509 1510func (c CanonicalTLFNameAndIDWithBreaks) Eq(r CanonicalTLFNameAndIDWithBreaks) bool { 1511 if c.CanonicalName != r.CanonicalName { 1512 return false 1513 } 1514 if c.TlfID != r.TlfID { 1515 return false 1516 } 1517 if len(c.Breaks.Breaks) != len(r.Breaks.Breaks) { 1518 return false 1519 } 1520 1521 m := make(map[string]bool) 1522 for _, b := range c.Breaks.Breaks { 1523 m[b.User.Username] = true 1524 } 1525 for _, b := range r.Breaks.Breaks { 1526 if !m[b.User.Username] { 1527 return false 1528 } 1529 } 1530 1531 return true 1532} 1533 1534func (c CanonicalTlfName) String() string { 1535 return string(c) 1536} 1537 1538func (u UserPlusKeys) GetUID() UID { 1539 return u.Uid 1540} 1541 1542func (u UserPlusKeys) GetName() string { 1543 return u.Username 1544} 1545 1546func (u UserPlusKeys) GetStatus() StatusCode { 1547 return u.Status 1548} 1549 1550func (u UserPlusKeysV2AllIncarnations) GetRemoteTrack(uid UID) *RemoteTrack { 1551 ret, ok := u.Current.RemoteTracks[uid] 1552 if !ok { 1553 return nil 1554 } 1555 return &ret 1556} 1557 1558func (u UserPlusAllKeys) GetUID() UID { 1559 return u.Base.GetUID() 1560} 1561 1562func (u UserPlusAllKeys) GetName() string { 1563 return u.Base.GetName() 1564} 1565 1566func (u UserPlusAllKeys) GetStatus() StatusCode { 1567 return u.Base.GetStatus() 1568} 1569 1570func (u UserPlusAllKeys) GetDeviceID(kid KID) (ret DeviceID, err error) { 1571 for _, dk := range u.Base.DeviceKeys { 1572 if dk.KID.Equal(kid) { 1573 return dk.DeviceID, nil 1574 } 1575 } 1576 return ret, fmt.Errorf("no device key for kid") 1577} 1578 1579func (u UserPlusAllKeys) Export() *User { 1580 return &User{Uid: u.GetUID(), Username: u.GetName()} 1581} 1582 1583func (u UserVersionVector) Equal(u2 UserVersionVector) bool { 1584 if u2.Id == 0 || u.Id == 0 || u2.Id != u.Id { 1585 return false 1586 } 1587 if u2.SigHints == 0 || u.SigHints == 0 || u2.SigHints != u.SigHints { 1588 return false 1589 } 1590 if u2.SigChain == 0 || u.SigChain == 0 || u2.SigChain != u.SigChain { 1591 return false 1592 } 1593 return true 1594} 1595 1596func ToDurationMsec(d time.Duration) DurationMsec { 1597 return DurationMsec(d / time.Millisecond) 1598} 1599 1600func (d DurationMsec) Duration() time.Duration { 1601 return time.Duration(d) * time.Millisecond 1602} 1603 1604func ToDurationSec(d time.Duration) DurationSec { 1605 return DurationSec(d / time.Second) 1606} 1607 1608func (d DurationSec) Duration() time.Duration { 1609 return time.Duration(d) * time.Second 1610} 1611 1612func (u UserPlusAllKeys) FindDevice(d DeviceID) *PublicKey { 1613 for _, k := range u.Base.DeviceKeys { 1614 if k.DeviceID.Eq(d) { 1615 return &k 1616 } 1617 } 1618 return nil 1619} 1620 1621func (u UserPlusKeysV2) GetUID() UID { 1622 return u.Uid 1623} 1624 1625func (u UserPlusKeysV2) GetName() string { 1626 return u.Username 1627} 1628 1629func (u UserPlusKeysV2) GetStatus() StatusCode { 1630 return u.Status 1631} 1632 1633func (u UserPlusKeysV2AllIncarnations) ExportToSimpleUser() User { 1634 return User{Uid: u.GetUID(), Username: u.GetName()} 1635} 1636 1637func (u UserPlusKeysV2AllIncarnations) FindDevice(d DeviceID) *PublicKeyV2NaCl { 1638 for _, k := range u.Current.DeviceKeys { 1639 if k.DeviceID.Eq(d) { 1640 return &k 1641 } 1642 } 1643 return nil 1644} 1645 1646func (u UserPlusKeysV2AllIncarnations) GetUID() UID { 1647 return u.Current.GetUID() 1648} 1649 1650func (u UserPlusKeysV2AllIncarnations) GetName() string { 1651 return u.Current.GetName() 1652} 1653 1654func (u UserPlusKeysV2AllIncarnations) GetStatus() StatusCode { 1655 return u.Current.GetStatus() 1656} 1657 1658func (u UserPlusKeysV2AllIncarnations) AllIncarnations() (ret []UserPlusKeysV2) { 1659 ret = append(ret, u.Current) 1660 ret = append(ret, u.PastIncarnations...) 1661 return ret 1662} 1663 1664func (u UserPlusKeys) FindKID(needle KID) *PublicKey { 1665 for _, k := range u.DeviceKeys { 1666 if k.KID.Equal(needle) { 1667 return &k 1668 } 1669 } 1670 return nil 1671} 1672 1673// FindKID finds the Key and user incarnation that most recently used this KID. 1674// It is possible for users to use the same KID across incarnations (though definitely 1675// not condoned or encouraged). In that case, we'll give the most recent use. 1676func (u UserPlusKeysV2AllIncarnations) FindKID(kid KID) (*UserPlusKeysV2, *PublicKeyV2NaCl) { 1677 ret, ok := u.Current.DeviceKeys[kid] 1678 if ok { 1679 return &u.Current, &ret 1680 } 1681 for i := len(u.PastIncarnations) - 1; i >= 0; i-- { 1682 prev := u.PastIncarnations[i] 1683 ret, ok = prev.DeviceKeys[kid] 1684 if ok { 1685 return &prev, &ret 1686 } 1687 } 1688 return nil, nil 1689} 1690 1691// HasKID returns true if u has the given KID in any of its incarnations. 1692// Useful for deciding if we should repoll a stale UPAK in the UPAK loader. 1693func (u UserPlusKeysV2AllIncarnations) HasKID(kid KID) bool { 1694 incarnation, _ := u.FindKID(kid) 1695 return (incarnation != nil) 1696} 1697 1698func (u UserPlusKeysV2) FindDeviceKey(needle KID) *PublicKeyV2NaCl { 1699 for _, k := range u.DeviceKeys { 1700 if k.Base.Kid.Equal(needle) { 1701 return &k 1702 } 1703 } 1704 return nil 1705} 1706 1707func (u UserPlusKeysV2) FindSigningDeviceKey(d DeviceID) *PublicKeyV2NaCl { 1708 for _, k := range u.DeviceKeys { 1709 if k.DeviceID.Eq(d) && k.Base.IsSibkey { 1710 return &k 1711 } 1712 } 1713 return nil 1714} 1715 1716func (u UserPlusKeysV2) FindSigningDeviceKID(d DeviceID) (KID, string) { 1717 key := u.FindSigningDeviceKey(d) 1718 if key == nil { 1719 return KID(""), "" 1720 } 1721 return key.Base.Kid, key.DeviceDescription 1722} 1723 1724func (u UserPlusKeysV2) FindEncryptionDeviceKeyFromSigningKID(parent KID) *PublicKeyV2NaCl { 1725 for _, k := range u.DeviceKeys { 1726 if !k.Base.IsSibkey && k.Parent != nil && k.Parent.Equal(parent) { 1727 return &k 1728 } 1729 } 1730 return nil 1731} 1732 1733func (u UserPlusKeysV2) FindEncryptionKIDFromSigningKID(parent KID) KID { 1734 key := u.FindEncryptionDeviceKeyFromSigningKID(parent) 1735 if key == nil { 1736 return KID("") 1737 } 1738 return key.Base.Kid 1739} 1740 1741func (u UserPlusKeysV2) FindEncryptionKIDFromDeviceID(deviceID DeviceID) KID { 1742 signingKID, _ := u.FindSigningDeviceKID(deviceID) 1743 if signingKID.IsNil() { 1744 return KID("") 1745 } 1746 return u.FindEncryptionKIDFromSigningKID(signingKID) 1747} 1748 1749func (s ChatConversationID) String() string { 1750 return hex.EncodeToString(s) 1751} 1752 1753func (s ChatConversationID) Bytes() []byte { 1754 return s 1755} 1756 1757// IsOlderThan returns true if any of the versions of u are older than v 1758func (u UserPlusAllKeys) IsOlderThan(v UserPlusAllKeys) bool { 1759 if u.Base.Uvv.SigChain < v.Base.Uvv.SigChain { 1760 return true 1761 } 1762 if u.Base.Uvv.Id < v.Base.Uvv.Id { 1763 return true 1764 } 1765 return false 1766} 1767 1768// IsOlderThan returns true if any of the versions of u are older than v 1769func (u UserPlusKeysV2AllIncarnations) IsOlderThan(v UserPlusKeysV2AllIncarnations) bool { 1770 if u.Uvv.SigChain < v.Uvv.SigChain { 1771 return true 1772 } 1773 if u.Uvv.Id < v.Uvv.Id { 1774 return true 1775 } 1776 if u.Uvv.CachedAt < v.Uvv.CachedAt { 1777 return true 1778 } 1779 return false 1780} 1781 1782func (u UserPlusKeysV2AllIncarnations) AllDeviceNames() []string { 1783 var names []string 1784 1785 for _, k := range u.Current.DeviceKeys { 1786 if k.DeviceDescription != "" { 1787 names = append(names, k.DeviceDescription) 1788 } 1789 } 1790 for _, v := range u.PastIncarnations { 1791 for _, k := range v.DeviceKeys { 1792 if k.DeviceDescription != "" { 1793 names = append(names, k.DeviceDescription) 1794 } 1795 } 1796 } 1797 1798 return names 1799} 1800 1801func (ut UserOrTeamID) String() string { 1802 return string(ut) 1803} 1804 1805func (ut UserOrTeamID) ToBytes() []byte { 1806 b, err := hex.DecodeString(string(ut)) 1807 if err != nil { 1808 return nil 1809 } 1810 return b 1811} 1812 1813func (ut UserOrTeamID) IsNil() bool { 1814 return len(ut) == 0 1815} 1816 1817func (ut UserOrTeamID) Exists() bool { 1818 return !ut.IsNil() 1819} 1820 1821func (ut UserOrTeamID) Equal(v UserOrTeamID) bool { 1822 return ut == v 1823} 1824 1825func (ut UserOrTeamID) NotEqual(v UserOrTeamID) bool { 1826 return !ut.Equal(v) 1827} 1828 1829func (ut UserOrTeamID) Less(v UserOrTeamID) bool { 1830 return ut < v 1831} 1832 1833func (ut UserOrTeamID) AsUser() (UID, error) { 1834 if !ut.IsUser() { 1835 return UID(""), errors.New("ID is not a UID") 1836 } 1837 return UID(ut), nil 1838} 1839 1840func (ut UserOrTeamID) AsUserOrBust() UID { 1841 uid, err := ut.AsUser() 1842 if err != nil { 1843 panic(err) 1844 } 1845 return uid 1846} 1847 1848func (ut UserOrTeamID) IsPublic() bool { 1849 if ut.IsUser() { 1850 return true 1851 } 1852 return ut.AsTeamOrBust().IsPublic() 1853} 1854 1855func (ut UserOrTeamID) AsTeam() (TeamID, error) { 1856 if !ut.IsTeamOrSubteam() { 1857 return TeamID(""), fmt.Errorf("ID is not a team ID (%s)", ut) 1858 } 1859 return TeamID(ut), nil 1860} 1861 1862func (ut UserOrTeamID) AsTeamOrBust() TeamID { 1863 tid, err := ut.AsTeam() 1864 if err != nil { 1865 panic(err) 1866 } 1867 return tid 1868} 1869 1870func (ut UserOrTeamID) Compare(ut2 UserOrTeamID) int { 1871 return strings.Compare(string(ut), string(ut2)) 1872} 1873 1874func (ut UserOrTeamID) IsUser() bool { 1875 i := idSchema{ 1876 length: UID_LEN, 1877 magicSuffixes: map[byte]bool{UID_SUFFIX: true, UID_SUFFIX_2: true}, 1878 typeHint: "user id", 1879 } 1880 return i.check(string(ut)) == nil 1881} 1882 1883func (ut UserOrTeamID) IsTeam() bool { 1884 i := idSchema{ 1885 length: TEAMID_LEN, 1886 magicSuffixes: map[byte]bool{TEAMID_PRIVATE_SUFFIX: true, TEAMID_PUBLIC_SUFFIX: true}, 1887 typeHint: "team id", 1888 } 1889 return i.check(string(ut)) == nil 1890} 1891 1892func (ut UserOrTeamID) IsSubteam() bool { 1893 i := idSchema{ 1894 length: TEAMID_LEN, 1895 magicSuffixes: map[byte]bool{SUB_TEAMID_PRIVATE_SUFFIX: true, SUB_TEAMID_PUBLIC_SUFFIX: true}, 1896 typeHint: "subteam id", 1897 } 1898 return i.check(string(ut)) == nil 1899} 1900 1901func (ut UserOrTeamID) IsTeamOrSubteam() bool { 1902 return ut.IsTeam() || ut.IsSubteam() 1903} 1904 1905func (ut UserOrTeamID) IsValidID() bool { 1906 return ut.IsUser() || ut.IsTeamOrSubteam() 1907} 1908 1909// Preconditions: 1910// -first four bits (in Little Endian) of UserOrTeamID are 1911// independent and uniformly distributed 1912// -UserOrTeamID must have an even number of bits, or this will always 1913// return 0 1914// Returns a number in [0, shardCount) which can be treated as roughly 1915// uniformly distributed. Used for things that need to shard by user. 1916func (ut UserOrTeamID) GetShard(shardCount int) (int, error) { 1917 if !ut.IsValidID() { 1918 return 0, fmt.Errorf("Bad ID, does not match any known valid type") 1919 } 1920 bytes, err := hex.DecodeString(string(ut)) 1921 if err != nil { 1922 return 0, err 1923 } 1924 // LittleEndian.Uint32 truncates to obtain 4 bytes from the buffer 1925 n := binary.LittleEndian.Uint32(bytes) 1926 return int(n % uint32(shardCount)), nil 1927} 1928 1929// Size implements the cache.Measurable interface. 1930func (ut UserOrTeamID) Size() int { 1931 return len(ut) + ptrSize 1932} 1933 1934func (m *MaskB64) UnmarshalJSON(b []byte) error { 1935 unquoted := UnquoteBytes(b) 1936 if len(unquoted) == 0 { 1937 return nil 1938 } 1939 dbuf := make([]byte, base64.StdEncoding.DecodedLen(len(unquoted))) 1940 n, err := base64.StdEncoding.Decode(dbuf, unquoted) 1941 if err != nil { 1942 return err 1943 } 1944 *m = MaskB64(dbuf[:n]) 1945 return nil 1946} 1947 1948func (m *MaskB64) MarshalJSON() ([]byte, error) { 1949 s := Quote(base64.StdEncoding.EncodeToString([]byte(*m))) 1950 return []byte(s), nil 1951} 1952 1953func PublicKeyV1FromPGPKeyV2(keyV2 PublicKeyV2PGPSummary) PublicKey { 1954 return PublicKey{ 1955 KID: keyV2.Base.Kid, 1956 PGPFingerprint: hex.EncodeToString(keyV2.Fingerprint[:]), 1957 PGPIdentities: keyV2.Identities, 1958 IsSibkey: keyV2.Base.IsSibkey, 1959 IsEldest: keyV2.Base.IsEldest, 1960 CTime: keyV2.Base.CTime, 1961 ETime: keyV2.Base.ETime, 1962 IsRevoked: (keyV2.Base.Revocation != nil), 1963 } 1964} 1965 1966func PublicKeyV1FromDeviceKeyV2(keyV2 PublicKeyV2NaCl) PublicKey { 1967 parentID := "" 1968 if keyV2.Parent != nil { 1969 parentID = string(*keyV2.Parent) 1970 } 1971 return PublicKey{ 1972 KID: keyV2.Base.Kid, 1973 IsSibkey: keyV2.Base.IsSibkey, 1974 IsEldest: keyV2.Base.IsEldest, 1975 ParentID: parentID, 1976 DeviceID: keyV2.DeviceID, 1977 DeviceDescription: keyV2.DeviceDescription, 1978 DeviceType: keyV2.DeviceType, 1979 CTime: keyV2.Base.CTime, 1980 ETime: keyV2.Base.ETime, 1981 IsRevoked: (keyV2.Base.Revocation != nil), 1982 } 1983} 1984 1985const ( 1986 DeviceTypeV2_NONE DeviceTypeV2 = "none" 1987 DeviceTypeV2_PAPER DeviceTypeV2 = "backup" 1988 DeviceTypeV2_DESKTOP DeviceTypeV2 = "desktop" 1989 DeviceTypeV2_MOBILE DeviceTypeV2 = "mobile" 1990) 1991 1992func (d DeviceTypeV2) String() string { 1993 return string(d) 1994} 1995 1996func StringToDeviceTypeV2(s string) (d DeviceTypeV2, err error) { 1997 deviceType := DeviceTypeV2(s) 1998 switch deviceType { 1999 case DeviceTypeV2_NONE, DeviceTypeV2_DESKTOP, DeviceTypeV2_MOBILE, DeviceTypeV2_PAPER: 2000 //pass 2001 default: 2002 return DeviceTypeV2_NONE, fmt.Errorf("Unknown DeviceType: %s", deviceType) 2003 } 2004 return deviceType, nil 2005} 2006 2007// defaults to Desktop 2008func (dt *DeviceTypeV2) ToDeviceType() DeviceType { 2009 if *dt == DeviceTypeV2_MOBILE { 2010 return DeviceType_MOBILE 2011 } 2012 return DeviceType_DESKTOP 2013} 2014 2015func RevokedKeyV1FromDeviceKeyV2(keyV2 PublicKeyV2NaCl) RevokedKey { 2016 return RevokedKey{ 2017 Key: PublicKeyV1FromDeviceKeyV2(keyV2), 2018 Time: KeybaseTime{ 2019 Unix: keyV2.Base.Revocation.Time, 2020 Chain: keyV2.Base.Revocation.PrevMerkleRootSigned.Seqno, 2021 }, 2022 By: keyV2.Base.Revocation.SigningKID, 2023 } 2024} 2025 2026// UPKV2 should supersede UPAK eventually, but lots of older code requires 2027// UPAK. This is a simple converter function. 2028func UPAKFromUPKV2AI(uV2 UserPlusKeysV2AllIncarnations) UserPlusAllKeys { 2029 // Convert the PGP keys. 2030 var pgpKeysV1 []PublicKey 2031 for _, keyV2 := range uV2.Current.PGPKeys { 2032 pgpKeysV1 = append(pgpKeysV1, PublicKeyV1FromPGPKeyV2(keyV2)) 2033 } 2034 2035 // Convert the device keys. 2036 var deviceKeysV1 []PublicKey 2037 var revokedDeviceKeysV1 []RevokedKey 2038 var resets []ResetSummary 2039 for _, keyV2 := range uV2.Current.DeviceKeys { 2040 if keyV2.Base.Revocation != nil { 2041 revokedDeviceKeysV1 = append(revokedDeviceKeysV1, RevokedKeyV1FromDeviceKeyV2(keyV2)) 2042 } else { 2043 deviceKeysV1 = append(deviceKeysV1, PublicKeyV1FromDeviceKeyV2(keyV2)) 2044 } 2045 } 2046 sort.Slice(deviceKeysV1, func(i, j int) bool { return deviceKeysV1[i].KID < deviceKeysV1[j].KID }) 2047 sort.Slice(revokedDeviceKeysV1, func(i, j int) bool { return revokedDeviceKeysV1[i].Key.KID < revokedDeviceKeysV1[j].Key.KID }) 2048 2049 // Assemble the deleted device keys from past incarnations. 2050 var deletedDeviceKeysV1 []PublicKey 2051 for _, incarnation := range uV2.PastIncarnations { 2052 for _, keyV2 := range incarnation.DeviceKeys { 2053 deletedDeviceKeysV1 = append(deletedDeviceKeysV1, PublicKeyV1FromDeviceKeyV2(keyV2)) 2054 } 2055 if reset := incarnation.Reset; reset != nil { 2056 resets = append(resets, *reset) 2057 } 2058 } 2059 sort.Slice(deletedDeviceKeysV1, func(i, j int) bool { return deletedDeviceKeysV1[i].KID < deletedDeviceKeysV1[j].KID }) 2060 2061 // List and sort the remote tracks. Note that they *must* be sorted. 2062 var remoteTracks []RemoteTrack 2063 for _, track := range uV2.Current.RemoteTracks { 2064 remoteTracks = append(remoteTracks, track) 2065 } 2066 sort.Slice(remoteTracks, func(i, j int) bool { return remoteTracks[i].Username < remoteTracks[j].Username }) 2067 2068 // Apart from all the key mangling above, everything else is just naming 2069 // and layout changes. Assemble the final UPAK. 2070 return UserPlusAllKeys{ 2071 Base: UserPlusKeys{ 2072 Uid: uV2.Current.Uid, 2073 Username: uV2.Current.Username, 2074 EldestSeqno: uV2.Current.EldestSeqno, 2075 Status: uV2.Current.Status, 2076 DeviceKeys: deviceKeysV1, 2077 RevokedDeviceKeys: revokedDeviceKeysV1, 2078 DeletedDeviceKeys: deletedDeviceKeysV1, 2079 PGPKeyCount: len(pgpKeysV1), 2080 Uvv: uV2.Uvv, 2081 PerUserKeys: uV2.Current.PerUserKeys, 2082 Resets: resets, 2083 }, 2084 PGPKeys: pgpKeysV1, 2085 RemoteTracks: remoteTracks, 2086 } 2087} 2088 2089func (u UserVersionPercentForm) String() string { 2090 return string(u) 2091} 2092 2093func NewUserVersion(uid UID, eldestSeqno Seqno) UserVersion { 2094 return UserVersion{ 2095 Uid: uid, 2096 EldestSeqno: eldestSeqno, 2097 } 2098} 2099 2100func (u UserVersion) PercentForm() UserVersionPercentForm { 2101 return UserVersionPercentForm(u.String()) 2102} 2103 2104func (u UserVersion) String() string { 2105 return fmt.Sprintf("%s%%%d", u.Uid, u.EldestSeqno) 2106} 2107 2108func (u UserVersion) Eq(v UserVersion) bool { 2109 return u.Uid.Equal(v.Uid) && u.EldestSeqno.Eq(v.EldestSeqno) 2110} 2111 2112func (u UserVersion) TeamInviteName() TeamInviteName { 2113 return TeamInviteName(u.PercentForm()) 2114} 2115 2116func (u UserVersion) IsNil() bool { 2117 return u.Uid.IsNil() 2118} 2119 2120type ByUserVersionID []UserVersion 2121 2122func (b ByUserVersionID) Len() int { return len(b) } 2123func (b ByUserVersionID) Swap(i, j int) { b[i], b[j] = b[j], b[i] } 2124func (b ByUserVersionID) Less(i, j int) bool { 2125 return b[i].String() < b[j].String() 2126} 2127 2128func (k CryptKey) Material() Bytes32 { 2129 return k.Key 2130} 2131 2132func (k CryptKey) Generation() int { 2133 return k.KeyGeneration 2134} 2135 2136func (k TeamApplicationKey) Material() Bytes32 { 2137 return k.Key 2138} 2139 2140func (k TeamApplicationKey) Generation() int { 2141 return int(k.KeyGeneration) 2142} 2143 2144func (t TeamMembers) AllUIDs() []UID { 2145 m := make(map[UID]bool) 2146 for _, u := range t.Owners { 2147 m[u.Uid] = true 2148 } 2149 for _, u := range t.Admins { 2150 m[u.Uid] = true 2151 } 2152 for _, u := range t.Writers { 2153 m[u.Uid] = true 2154 } 2155 for _, u := range t.Readers { 2156 m[u.Uid] = true 2157 } 2158 for _, u := range t.Bots { 2159 m[u.Uid] = true 2160 } 2161 for _, u := range t.RestrictedBots { 2162 m[u.Uid] = true 2163 } 2164 var all []UID 2165 for u := range m { 2166 all = append(all, u) 2167 } 2168 return all 2169} 2170 2171func (t TeamMembers) AllUserVersions() []UserVersion { 2172 m := make(map[UID]UserVersion) 2173 for _, u := range t.Owners { 2174 m[u.Uid] = u 2175 } 2176 for _, u := range t.Admins { 2177 m[u.Uid] = u 2178 } 2179 for _, u := range t.Writers { 2180 m[u.Uid] = u 2181 } 2182 for _, u := range t.Readers { 2183 m[u.Uid] = u 2184 } 2185 for _, u := range t.Bots { 2186 m[u.Uid] = u 2187 } 2188 for _, u := range t.RestrictedBots { 2189 m[u.Uid] = u 2190 } 2191 var all []UserVersion 2192 for _, uv := range m { 2193 all = append(all, uv) 2194 } 2195 return all 2196} 2197 2198func (s TeamMemberStatus) IsActive() bool { 2199 return s == TeamMemberStatus_ACTIVE 2200} 2201 2202func (s TeamMemberStatus) IsReset() bool { 2203 return s == TeamMemberStatus_RESET 2204} 2205 2206func (s TeamMemberStatus) IsDeleted() bool { 2207 return s == TeamMemberStatus_DELETED 2208} 2209 2210func FilterInactiveReadersWriters(arg []TeamMemberDetails) (ret []TeamMemberDetails) { 2211 for _, v := range arg { 2212 if v.Status.IsActive() || (v.Role != TeamRole_READER && v.Role != TeamRole_WRITER) { 2213 ret = append(ret, v) 2214 } 2215 } 2216 return ret 2217} 2218 2219func (t TeamName) IsNil() bool { 2220 return len(t.Parts) == 0 2221} 2222 2223// underscores allowed, just not first or doubled 2224var namePartRxx = regexp.MustCompile(`^([a-zA-Z0-9][a-zA-Z0-9_]?)+$`) 2225var implicitRxxString = fmt.Sprintf("^%s[0-9a-f]{%d}$", ImplicitTeamPrefix, ImplicitSuffixLengthBytes*2) 2226var implicitNameRxx = regexp.MustCompile(implicitRxxString) 2227 2228const ImplicitTeamPrefix = "__keybase_implicit_team__" 2229const ImplicitSuffixLengthBytes = 16 2230 2231func stringToTeamNamePart(s string) TeamNamePart { 2232 return TeamNamePart(strings.ToLower(s)) 2233} 2234 2235func rootTeamNameFromString(s string) (TeamName, error) { 2236 if implicitNameRxx.MatchString(s) { 2237 return TeamName{Parts: []TeamNamePart{stringToTeamNamePart(s)}}, nil 2238 } 2239 if err := validatePart(s); err != nil { 2240 return TeamName{}, err 2241 } 2242 return TeamName{Parts: []TeamNamePart{stringToTeamNamePart(s)}}, nil 2243} 2244 2245func validatePart(s string) (err error) { 2246 if len(s) == 0 { 2247 return errors.New("team names cannot be empty") 2248 } 2249 if !(len(s) >= 2 && len(s) <= 16) { 2250 return errors.New("team names must be between 2 and 16 characters long") 2251 } 2252 if !namePartRxx.MatchString(s) { 2253 return errors.New("Keybase team names must be letters (a-z), numbers, and underscores. Also, they can't start with underscores or use double underscores, to avoid confusion.") 2254 } 2255 return nil 2256} 2257 2258func TeamNameFromString(s string) (TeamName, error) { 2259 ret := TeamName{} 2260 2261 s = strings.ToLower(s) 2262 parts := strings.Split(s, ".") 2263 if len(parts) == 0 { 2264 return ret, errors.New("team names cannot be empty") 2265 } 2266 if len(parts) == 1 { 2267 return rootTeamNameFromString(s) 2268 } 2269 tmp := make([]TeamNamePart, len(parts)) 2270 for i, part := range parts { 2271 err := validatePart(part) 2272 if err != nil { 2273 return TeamName{}, fmt.Errorf("Could not parse name as team; bad name component %q: %s", part, err.Error()) 2274 } 2275 tmp[i] = stringToTeamNamePart(part) 2276 } 2277 return TeamName{Parts: tmp}, nil 2278} 2279 2280func (p TeamNamePart) String() string { 2281 return string(p) 2282} 2283 2284func (t TeamName) AssertEqString(s string) error { 2285 tmp, err := TeamNameFromString(s) 2286 if err != nil { 2287 return err 2288 } 2289 if !t.Eq(tmp) { 2290 return fmt.Errorf("Team equality check failed: %s != %s", t.String(), s) 2291 } 2292 return nil 2293} 2294 2295func (t TeamName) String() string { 2296 tmp := make([]string, len(t.Parts)) 2297 for i, p := range t.Parts { 2298 tmp[i] = strings.ToLower(string(p)) 2299 } 2300 return strings.Join(tmp, ".") 2301} 2302 2303func (t TeamName) Eq(t2 TeamName) bool { 2304 return t.String() == t2.String() 2305} 2306 2307func (t TeamName) IsRootTeam() bool { 2308 return len(t.Parts) == 1 2309} 2310 2311func (t TeamName) ToPrivateTeamID() TeamID { 2312 return t.ToTeamID(false) 2313} 2314 2315func (t TeamName) ToPublicTeamID() TeamID { 2316 return t.ToTeamID(true) 2317} 2318 2319// Get the top level team id for this team name. 2320// Only makes sense for non-sub teams. 2321// The first 15 bytes of the sha256 of the lowercase team name, 2322// followed by the byte 0x24, encoded as hex. 2323func (t TeamName) ToTeamID(public bool) TeamID { 2324 low := strings.ToLower(t.String()) 2325 sum := sha256.Sum256([]byte(low)) 2326 var useSuffix byte = TEAMID_PRIVATE_SUFFIX 2327 if public { 2328 useSuffix = TEAMID_PUBLIC_SUFFIX 2329 } 2330 bs := append(sum[:15], useSuffix) 2331 res, err := TeamIDFromString(hex.EncodeToString(bs)) 2332 if err != nil { 2333 panic(err) 2334 } 2335 return res 2336} 2337 2338// Return a new team name with the part added to the end. 2339// For example {foo.bar}.Append(baz) -> {foo.bar.baz} 2340func (t TeamName) Append(part string) (t3 TeamName, err error) { 2341 t2 := t.DeepCopy() 2342 t2.Parts = append(t2.Parts, TeamNamePart(part)) 2343 t3, err = TeamNameFromString(t2.String()) 2344 return t3, err 2345} 2346 2347func (t TeamName) LastPart() TeamNamePart { 2348 return t.Parts[len(t.Parts)-1] 2349} 2350 2351func (t TeamName) RootAncestorName() TeamName { 2352 if len(t.Parts) == 0 { 2353 // this should never happen 2354 return TeamName{} 2355 } 2356 return TeamName{ 2357 Parts: t.Parts[:1], 2358 } 2359} 2360 2361func (t TeamName) RootID() TeamID { 2362 return t.RootAncestorName().ToTeamID(false) 2363} 2364 2365func (t TeamName) Parent() (TeamName, error) { 2366 if len(t.Parts) == 0 { 2367 return t, fmt.Errorf("empty team name") 2368 } 2369 if t.IsRootTeam() { 2370 return t, fmt.Errorf("root team has no parent") 2371 } 2372 return TeamName{ 2373 Parts: t.Parts[:len(t.Parts)-1], 2374 }, nil 2375} 2376 2377func (t TeamName) SwapLastPart(newLast string) (TeamName, error) { 2378 parent, err := t.Parent() 2379 if err != nil { 2380 return t, err 2381 } 2382 return parent.Append(newLast) 2383} 2384 2385func (t TeamName) IsImplicit() bool { 2386 return strings.HasPrefix(t.String(), ImplicitTeamPrefix) 2387} 2388 2389// The number of parts in a team name. 2390// Root teams have 1. 2391func (t TeamName) Depth() int { 2392 return len(t.Parts) 2393} 2394 2395func (t TeamName) IsAncestorOf(other TeamName) bool { 2396 depth := t.Depth() 2397 if depth >= other.Depth() { 2398 return false 2399 } 2400 2401 for i := 0; i < depth; i++ { 2402 if !other.Parts[i].Eq(t.Parts[i]) { 2403 return false 2404 } 2405 } 2406 2407 return true 2408} 2409 2410func (t TeamNamePart) Eq(t2 TeamNamePart) bool { 2411 return string(t) == string(t2) 2412} 2413 2414func (u UserPlusKeys) ToUserVersion() UserVersion { 2415 return UserVersion{ 2416 Uid: u.Uid, 2417 EldestSeqno: u.EldestSeqno, 2418 } 2419} 2420 2421func (u UserPlusKeysV2) ToUserVersion() UserVersion { 2422 return UserVersion{ 2423 Uid: u.Uid, 2424 EldestSeqno: u.EldestSeqno, 2425 } 2426} 2427 2428func (u UserPlusKeysV2AllIncarnations) ToUserVersion() UserVersion { 2429 return u.Current.ToUserVersion() 2430} 2431 2432func (u UserPlusKeysV2AllIncarnations) GetPerUserKeyAtSeqno(uv UserVersion, seqno Seqno, merkleSeqno Seqno) (*PerUserKey, error) { 2433 incarnations := u.AllIncarnations() 2434 for _, incarnation := range incarnations { 2435 if incarnation.EldestSeqno == uv.EldestSeqno { 2436 if incarnation.Reset != nil && incarnation.Reset.MerkleRoot.Seqno <= merkleSeqno { 2437 return nil, nil 2438 } 2439 if len(incarnation.PerUserKeys) == 0 { 2440 return nil, nil 2441 } 2442 for i := range incarnation.PerUserKeys { 2443 perUserKey := incarnation.PerUserKeys[len(incarnation.PerUserKeys)-1-i] 2444 if perUserKey.Seqno <= seqno { 2445 return &perUserKey, nil 2446 } 2447 } 2448 return nil, fmt.Errorf("didn't find per user key at seqno %d for uv %v", seqno, uv) 2449 } 2450 } 2451 return nil, fmt.Errorf("didn't find uv %v in upak", uv) 2452} 2453 2454// Can return nil. 2455func (u UserPlusKeysV2) GetLatestPerUserKey() *PerUserKey { 2456 if len(u.PerUserKeys) > 0 { 2457 return &u.PerUserKeys[len(u.PerUserKeys)-1] 2458 } 2459 return nil 2460} 2461 2462// Can return nil. 2463func (u UserPlusKeysV2) GetPerUserKeyByGen(gen PerUserKeyGeneration) *PerUserKey { 2464 genint := int(gen) 2465 if genint <= 0 || genint > len(u.PerUserKeys) { 2466 return nil 2467 } 2468 puk := u.PerUserKeys[genint-1] 2469 if puk.Gen != genint { 2470 // The PerUserKeys field of this object is malformed 2471 return nil 2472 } 2473 return &puk 2474} 2475 2476func (s PerTeamKeySeed) ToBytes() []byte { return s[:] } 2477 2478func (s PerTeamKeySeed) IsZero() bool { 2479 var tmp PerTeamKeySeed 2480 return hmac.Equal(s[:], tmp[:]) 2481} 2482 2483func PerTeamKeySeedFromBytes(b []byte) (PerTeamKeySeed, error) { 2484 var ret PerTeamKeySeed 2485 if len(b) != len(ret) { 2486 return PerTeamKeySeed{}, fmt.Errorf("decrypt yielded a bad-sized team secret: %d != %d", len(b), len(ret)) 2487 } 2488 copy(ret[:], b) 2489 return ret, nil 2490} 2491 2492func (s SigChainLocation) Eq(s2 SigChainLocation) bool { 2493 return s.Seqno == s2.Seqno && s.SeqType == s2.SeqType 2494} 2495 2496func (s SigChainLocation) LessThanOrEqualTo(s2 SigChainLocation) bool { 2497 return s.SeqType == s2.SeqType && s.Seqno <= s2.Seqno 2498} 2499 2500func (s SigChainLocation) Comparable(s2 SigChainLocation) error { 2501 if s.SeqType != s2.SeqType { 2502 return fmt.Errorf("mismatched seqtypes: %v != %v", s.SeqType, s2.SeqType) 2503 } 2504 return nil 2505} 2506 2507func (s SigChainLocation) Sub1() SigChainLocation { 2508 return SigChainLocation{ 2509 Seqno: s.Seqno - 1, 2510 SeqType: s.SeqType, 2511 } 2512} 2513 2514func (r TeamRole) IsAdminOrAbove() bool { 2515 return r.IsOrAbove(TeamRole_ADMIN) 2516} 2517 2518func (r TeamRole) IsWriterOrAbove() bool { 2519 return r.IsOrAbove(TeamRole_WRITER) 2520} 2521 2522func (r TeamRole) IsReaderOrAbove() bool { 2523 return r.IsOrAbove(TeamRole_READER) 2524} 2525 2526func (r TeamRole) IsBotOrAbove() bool { 2527 return r.IsOrAbove(TeamRole_BOT) 2528} 2529 2530func (r TeamRole) IsRestrictedBotOrAbove() bool { 2531 return r.IsOrAbove(TeamRole_RESTRICTEDBOT) 2532} 2533 2534func (r TeamRole) IsBotLike() bool { 2535 switch r { 2536 case TeamRole_BOT, TeamRole_RESTRICTEDBOT: 2537 return true 2538 } 2539 return false 2540} 2541 2542func (r TeamRole) IsRestrictedBot() bool { 2543 return r == TeamRole_RESTRICTEDBOT 2544} 2545 2546func (r TeamRole) teamRoleForOrderingOnly() int { 2547 switch r { 2548 case TeamRole_NONE: 2549 return 0 2550 case TeamRole_RESTRICTEDBOT: 2551 return 1 2552 case TeamRole_BOT: 2553 return 2 2554 case TeamRole_READER, 2555 TeamRole_WRITER, 2556 TeamRole_ADMIN, 2557 TeamRole_OWNER: 2558 return int(r) + 2 2559 default: 2560 return 0 2561 } 2562} 2563 2564func (r TeamRole) IsOrAbove(min TeamRole) bool { 2565 return r.teamRoleForOrderingOnly() >= min.teamRoleForOrderingOnly() 2566} 2567 2568func (r TeamRole) HumanString() string { 2569 if r.IsRestrictedBot() { 2570 return "restricted bot" 2571 } 2572 return strings.ToLower(r.String()) 2573} 2574 2575type idSchema struct { 2576 length int 2577 magicSuffixes map[byte]bool 2578 typeHint string 2579} 2580 2581func (i idSchema) check(s string) error { 2582 xs, err := hex.DecodeString(s) 2583 if err != nil { 2584 return err 2585 } 2586 if len(xs) != i.length { 2587 return fmt.Errorf("%s: Wrong ID length (got %d)", i.typeHint, len(xs)) 2588 } 2589 suffix := xs[len(xs)-1] 2590 if !i.magicSuffixes[suffix] { 2591 return fmt.Errorf("%s: Incorrect suffix byte (got 0x%x)", i.typeHint, suffix) 2592 } 2593 return nil 2594} 2595 2596func TeamInviteIDFromString(s string) (TeamInviteID, error) { 2597 if err := (idSchema{16, map[byte]bool{0x27: true}, "team invite ID"}).check(s); err != nil { 2598 return TeamInviteID(""), err 2599 } 2600 return TeamInviteID(s), nil 2601} 2602 2603func (i TeamInviteID) Eq(i2 TeamInviteID) bool { 2604 return string(i) == string(i2) 2605} 2606 2607func (t TeamInviteType) String() (string, error) { 2608 c, err := t.C() 2609 if err != nil { 2610 return "", err 2611 } 2612 switch c { 2613 case TeamInviteCategory_KEYBASE: 2614 return "keybase", nil 2615 case TeamInviteCategory_EMAIL: 2616 return "email", nil 2617 case TeamInviteCategory_PHONE: 2618 return "phone", nil 2619 case TeamInviteCategory_SBS: 2620 return string(t.Sbs()), nil 2621 case TeamInviteCategory_SEITAN: 2622 return "seitan_invite_token", nil 2623 case TeamInviteCategory_UNKNOWN: 2624 return t.Unknown(), nil 2625 } 2626 2627 return "", nil 2628} 2629 2630func (a TeamInviteType) Eq(b TeamInviteType) bool { 2631 ac, err := a.C() 2632 if err != nil { 2633 return false 2634 } 2635 bc, err := b.C() 2636 if err != nil { 2637 return false 2638 } 2639 if ac != bc { 2640 return false 2641 } 2642 2643 switch ac { 2644 case TeamInviteCategory_KEYBASE: 2645 return true 2646 case TeamInviteCategory_EMAIL, TeamInviteCategory_PHONE: 2647 return true 2648 case TeamInviteCategory_SBS: 2649 return a.Sbs() == b.Sbs() 2650 case TeamInviteCategory_UNKNOWN: 2651 return a.Unknown() == b.Unknown() 2652 } 2653 2654 return false 2655} 2656 2657func (t TeamInvite) KeybaseUserVersion() (UserVersion, error) { 2658 category, err := t.Type.C() 2659 if err != nil { 2660 return UserVersion{}, err 2661 } 2662 if category != TeamInviteCategory_KEYBASE { 2663 return UserVersion{}, errors.New("KeybaseUserVersion: invalid invite category, must be keybase") 2664 } 2665 2666 return ParseUserVersion(UserVersionPercentForm(t.Name)) 2667} 2668 2669// TeamMaxUsesInfinite is a value for max_uses field which makes team invite 2670// multiple use, with infinite number of uses. 2671const TeamMaxUsesInfinite = TeamInviteMaxUses(-1) 2672 2673func NewTeamInviteFiniteUses(maxUses int) (v TeamInviteMaxUses, err error) { 2674 if maxUses <= 0 { 2675 return v, errors.New("non-infinite uses with nonpositive maxUses") 2676 } 2677 return TeamInviteMaxUses(maxUses), nil 2678} 2679 2680func (e *TeamInviteMaxUses) IsNotNilAndValid() bool { 2681 return e != nil && (*e > 0 || *e == TeamMaxUsesInfinite) 2682} 2683 2684func max(a, b int) int { 2685 if a >= b { 2686 return a 2687 } 2688 return b 2689} 2690 2691func (ti TeamInvite) UsesLeftString(alreadyUsed int) string { 2692 if ti.IsInfiniteUses() { 2693 return "unlimited uses left" 2694 } 2695 var maxUses int 2696 if ti.MaxUses == nil { 2697 maxUses = 1 2698 } else { 2699 maxUses = int(*ti.MaxUses) 2700 } 2701 return formatItems("use", "uses", max(maxUses-alreadyUsed, 0)) 2702} 2703 2704func (ti TeamInvite) IsInfiniteUses() bool { 2705 return ti.MaxUses != nil && *ti.MaxUses == TeamMaxUsesInfinite 2706} 2707 2708func (ti TeamInvite) IsUsedUp(alreadyUsed int) bool { 2709 maxUses := ti.MaxUses 2710 if maxUses == nil { 2711 return alreadyUsed >= 1 2712 } 2713 if *maxUses == TeamMaxUsesInfinite { 2714 return false 2715 } 2716 return alreadyUsed >= int(*maxUses) 2717} 2718 2719func (ti TeamInvite) IsExpired(now time.Time) bool { 2720 if ti.Etime == nil { 2721 return false 2722 } 2723 etime := FromUnixTime(*ti.Etime) 2724 return now.After(etime) 2725} 2726 2727func formatItems(singular string, plural string, count int) string { 2728 if count == 1 { 2729 return "1 " + singular 2730 } 2731 return fmt.Sprintf("%d %s", count, plural) 2732} 2733 2734// ComputeValidity is used for invitelinks, but is accurate for other invites as well. 2735// It computes whether the invite is still valid (i.e., if it can still be used), 2736// and a short description of when it was invalidated or under what conditions it can 2737// be later invalidated. 2738func (md TeamInviteMetadata) ComputeValidity(now time.Time, 2739 userLog map[UserVersion][]UserLogPoint) (isValid bool, validityDescription string) { 2740 2741 isInvalid := false 2742 invalidationAction := "" 2743 var invalidationTime *time.Time 2744 var usedInviteCount int 2745 code, _ := md.Status.Code() 2746 switch code { 2747 case TeamInviteMetadataStatusCode_ACTIVE: 2748 isExpired := md.Invite.IsExpired(now) 2749 if isExpired { 2750 expireTime := md.Invite.Etime.Time() 2751 invalidationTime = &expireTime 2752 } 2753 usedInvites := md.UsedInvites 2754 // If this is an old-style invite that was completed; it wouldn't be ACTIVE anymore, 2755 // so we can assume len(usedInvites) is correct, since it should be empty (implying 2756 // the invite is not UsedUp. 2757 isUsedUp := md.Invite.IsUsedUp(len(usedInvites)) 2758 if isUsedUp { 2759 // implies usedInvites is nonempty 2760 usedInvites := md.UsedInvites 2761 teamUserLogPoint := usedInvites[len(usedInvites)-1] 2762 logPoint := userLog[teamUserLogPoint.Uv][teamUserLogPoint.LogPoint] 2763 usedUpTime := logPoint.SigMeta.Time.Time() 2764 if invalidationTime == nil || usedUpTime.Before(*invalidationTime) { 2765 invalidationTime = &usedUpTime 2766 } 2767 } 2768 if isExpired || isUsedUp { 2769 isInvalid = true 2770 invalidationAction = "Expired" 2771 } 2772 usedInviteCount = len(usedInvites) 2773 case TeamInviteMetadataStatusCode_OBSOLETE: 2774 isInvalid = true 2775 invalidationAction = "Obsoleted" 2776 // no invalidation time for obsoletes 2777 usedInviteCount = 0 2778 case TeamInviteMetadataStatusCode_CANCELLED: 2779 isInvalid = true 2780 invalidationAction = "Cancelled" 2781 cancelTime := md.Status.Cancelled().TeamSigMeta.SigMeta.Time.Time() 2782 invalidationTime = &cancelTime 2783 usedInviteCount = len(md.UsedInvites) 2784 case TeamInviteMetadataStatusCode_COMPLETED: 2785 isInvalid = true 2786 invalidationAction = "Completed" 2787 completeTime := md.Status.Completed().TeamSigMeta.SigMeta.Time.Time() 2788 invalidationTime = &completeTime 2789 usedInviteCount = 1 2790 default: 2791 return false, fmt.Sprintf("unknown invite status %v", code) 2792 } 2793 2794 if isInvalid { 2795 ret := "" 2796 ret += invalidationAction 2797 if invalidationTime != nil { 2798 invalidationDeltaFormatted := kbtime.RelTime(*invalidationTime, now, "", "") 2799 ret += " " + invalidationDeltaFormatted + " ago" 2800 } 2801 return false, ret 2802 } 2803 2804 if md.Invite.Etime == nil && md.Invite.IsInfiniteUses() { 2805 return true, "Does not expire" 2806 } 2807 2808 ret := "Expires" 2809 if md.Invite.Etime != nil { 2810 expirationTimeConverted := FromUnixTime(*md.Invite.Etime) 2811 expirationDeltaFormatted := kbtime.RelTime(expirationTimeConverted, now, "", "") 2812 ret += " in " + expirationDeltaFormatted 2813 } 2814 if md.Invite.Etime != nil && !md.Invite.IsInfiniteUses() { 2815 ret += " or" 2816 } 2817 if !md.Invite.IsInfiniteUses() { 2818 ret += " after " + md.Invite.UsesLeftString(usedInviteCount) 2819 } 2820 return true, ret 2821} 2822 2823func (m MemberInfo) TeamName() (TeamName, error) { 2824 return TeamNameFromString(m.FqName) 2825} 2826 2827func (i ImplicitTeamUserSet) NumTotalUsers() int { 2828 return len(i.KeybaseUsers) + len(i.UnresolvedUsers) 2829} 2830 2831func (i ImplicitTeamUserSet) List() string { 2832 var names []string 2833 names = append(names, i.KeybaseUsers...) 2834 for _, u := range i.UnresolvedUsers { 2835 names = append(names, u.String()) 2836 } 2837 sort.Strings(names) 2838 return strings.Join(names, ",") 2839} 2840 2841func (n ImplicitTeamDisplayName) String() string { 2842 name := n.Writers.List() 2843 2844 if n.Readers.NumTotalUsers() > 0 { 2845 name += "#" + n.Readers.List() 2846 } 2847 2848 return name 2849} 2850 2851func (c *ImplicitTeamConflictInfo) IsConflict() bool { 2852 return c != nil && c.Generation > ConflictGeneration(0) 2853} 2854 2855const ( 2856 // LockIDVersion0 is the first ever version for lock ID format. 2857 LockIDVersion0 byte = iota 2858) 2859 2860// LockIDFromBytes takes the first 8 bytes of the sha512 over data, overwrites 2861// first byte with the version byte, then interprets it as int64 using big 2862// endian, and returns the value as LockID. 2863func LockIDFromBytes(data []byte) LockID { 2864 sum := sha512.Sum512(data) 2865 sum[0] = LockIDVersion0 2866 return LockID(binary.BigEndian.Uint64(sum[:8])) 2867} 2868 2869// MDPriority is the type for the priority field of a metadata put. mdserver 2870// prioritizes MD writes with higher priority when multiple happen at the same 2871// time, for the same TLF. 2872const ( 2873 // MDPriorityDefault is the priority of zero. It's implicitly used by all 2874 // old clients, and has lowest priority. 2875 MDPriorityDefault MDPriority = 0 2876 // MDPriorityNormal is the priority used for normal KBFS metadata writes. 2877 MDPriorityNormal = 8 2878 // MDPriorityGit is the priority used for metadata writes triggered by git 2879 // remote helpers. 2880 MDPriorityGit = 32 2881) 2882 2883// IsValid returns true is p is a valid MDPriority, or false otherwise. 2884func (p MDPriority) IsValid() bool { 2885 return p < 256 && p >= 0 2886} 2887 2888func (t TLFVisibility) Eq(r TLFVisibility) bool { 2889 return int(t) == int(r) 2890} 2891 2892func ParseUserVersion(s UserVersionPercentForm) (res UserVersion, err error) { 2893 parts := strings.Split(string(s), "%") 2894 if len(parts) == 1 { 2895 // NOTE: We have to keep it the way it is, even though we 2896 // never save UIDs without EldestSeqno anywhere. There may be 2897 // team chain which have UVs encoded with default eldest=1 in 2898 // the wild. 2899 2900 // default to seqno 1 2901 parts = append(parts, "1") 2902 } 2903 if len(parts) != 2 { 2904 return res, fmt.Errorf("invalid user version: %s", s) 2905 } 2906 uid, err := UIDFromString(parts[0]) 2907 if err != nil { 2908 return res, err 2909 } 2910 eldestSeqno, err := strconv.ParseInt(parts[1], 10, 64) 2911 if err != nil { 2912 return res, fmt.Errorf("invalid eldest seqno: %s", err) 2913 } 2914 return UserVersion{ 2915 Uid: uid, 2916 EldestSeqno: Seqno(eldestSeqno), 2917 }, nil 2918} 2919 2920func (p StringKVPair) BoolValue() bool { 2921 i, err := strconv.ParseBool(p.Value) 2922 if err != nil { 2923 return false 2924 } 2925 return i 2926} 2927 2928func (p StringKVPair) IntValue() int { 2929 i, err := strconv.Atoi(p.Value) 2930 if err != nil { 2931 return 0 2932 } 2933 return i 2934} 2935 2936func (r *GitRepoResult) GetIfOk() (res GitRepoInfo, err error) { 2937 state, err := r.State() 2938 if err != nil { 2939 return res, err 2940 } 2941 switch state { 2942 case GitRepoResultState_ERR: 2943 return res, fmt.Errorf(r.Err()) 2944 case GitRepoResultState_OK: 2945 return r.Ok(), nil 2946 } 2947 return res, fmt.Errorf("git repo unknown error") 2948} 2949 2950func (r GitRepoInfo) FullName() string { 2951 switch r.Folder.FolderType { 2952 case FolderType_PRIVATE: 2953 return string(r.LocalMetadata.RepoName) 2954 case FolderType_TEAM: 2955 return r.Folder.Name + "/" + string(r.LocalMetadata.RepoName) 2956 default: 2957 return "<repo type error>" 2958 } 2959} 2960 2961func (req *TeamChangeReq) AddUVWithRole(uv UserVersion, role TeamRole, 2962 botSettings *TeamBotSettings) error { 2963 if !role.IsRestrictedBot() && botSettings != nil { 2964 return fmt.Errorf("Unexpected botSettings for role %v", role) 2965 } 2966 switch role { 2967 case TeamRole_RESTRICTEDBOT: 2968 if botSettings == nil { 2969 return fmt.Errorf("Cannot add a RESTRICTEDBOT with nil TeamBotSettings") 2970 } 2971 if req.RestrictedBots == nil { 2972 req.RestrictedBots = make(map[UserVersion]TeamBotSettings) 2973 } 2974 req.RestrictedBots[uv] = *botSettings 2975 case TeamRole_BOT: 2976 req.Bots = append(req.Bots, uv) 2977 case TeamRole_READER: 2978 req.Readers = append(req.Readers, uv) 2979 case TeamRole_WRITER: 2980 req.Writers = append(req.Writers, uv) 2981 case TeamRole_ADMIN: 2982 req.Admins = append(req.Admins, uv) 2983 case TeamRole_OWNER: 2984 req.Owners = append(req.Owners, uv) 2985 default: 2986 return fmt.Errorf("Unexpected role: %v", role) 2987 } 2988 return nil 2989} 2990 2991func (req *TeamChangeReq) RestrictedBotUVs() (ret []UserVersion) { 2992 for uv := range req.RestrictedBots { 2993 ret = append(ret, uv) 2994 } 2995 return ret 2996} 2997 2998// CompleteInviteID adds to the `completed_invites` field, and signals that the 2999// invite can never be used again. It's used for SBS, Keybase, SeitanV1, and 3000// SeitanV2 invites. 3001func (req *TeamChangeReq) CompleteInviteID(inviteID TeamInviteID, uv UserVersionPercentForm) { 3002 if req.CompletedInvites == nil { 3003 req.CompletedInvites = make(map[TeamInviteID]UserVersionPercentForm) 3004 } 3005 req.CompletedInvites[inviteID] = uv 3006} 3007 3008// UseInviteID adds to the `used_invites` field. It is used for SeitanInvitelink invites, 3009// which can be used multiple times. 3010func (req *TeamChangeReq) UseInviteID(inviteID TeamInviteID, uv UserVersionPercentForm) { 3011 req.UsedInvites = append(req.UsedInvites, TeamUsedInvite{InviteID: inviteID, Uv: uv}) 3012} 3013 3014func (req *TeamChangeReq) GetAllAdds() (ret []UserVersion) { 3015 ret = append(ret, req.RestrictedBotUVs()...) 3016 ret = append(ret, req.Bots...) 3017 ret = append(ret, req.Readers...) 3018 ret = append(ret, req.Writers...) 3019 ret = append(ret, req.Admins...) 3020 ret = append(ret, req.Owners...) 3021 return ret 3022} 3023 3024func TotalNumberOfCommits(refs []GitRefMetadata) (total int) { 3025 for _, ref := range refs { 3026 total += len(ref.Commits) 3027 } 3028 return total 3029} 3030 3031func RefNames(refs []GitRefMetadata) string { 3032 names := make([]string, len(refs)) 3033 for i, ref := range refs { 3034 names[i] = ref.RefName 3035 } 3036 return strings.Join(names, ", ") 3037} 3038 3039func TeamEncryptedKBFSKeysetHashFromString(s string) TeamEncryptedKBFSKeysetHash { 3040 return TeamEncryptedKBFSKeysetHash(s) 3041} 3042 3043func TeamEncryptedKBFSKeysetHashFromBytes(s []byte) TeamEncryptedKBFSKeysetHash { 3044 return TeamEncryptedKBFSKeysetHashFromString(hex.EncodeToString(s)) 3045} 3046 3047func (e TeamEncryptedKBFSKeysetHash) String() string { 3048 return string(e) 3049} 3050 3051func (e TeamEncryptedKBFSKeysetHash) Bytes() []byte { 3052 return []byte(e.String()) 3053} 3054 3055func (e TeamEncryptedKBFSKeysetHash) SecureEqual(l TeamEncryptedKBFSKeysetHash) bool { 3056 return hmac.Equal(e.Bytes(), l.Bytes()) 3057} 3058 3059func (r ResetLink) Summarize() ResetSummary { 3060 return ResetSummary{ 3061 Ctime: r.Ctime, 3062 MerkleRoot: r.MerkleRoot, 3063 ResetSeqno: r.ResetSeqno, 3064 Type: r.Type, 3065 } 3066} 3067 3068func (f AvatarFormat) String() string { 3069 return string(f) 3070} 3071 3072func (u AvatarUrl) String() string { 3073 return string(u) 3074} 3075 3076func MakeAvatarURL(u string) AvatarUrl { 3077 return AvatarUrl(u) 3078} 3079 3080func (b Bytes32) IsBlank() bool { 3081 var blank Bytes32 3082 return (subtle.ConstantTimeCompare(b[:], blank[:]) == 1) 3083} 3084 3085func (i Identify2ResUPK2) ExportToV1() Identify2Res { 3086 return Identify2Res{ 3087 Upk: UPAKFromUPKV2AI(i.Upk).Base, 3088 IdentifiedAt: i.IdentifiedAt, 3089 TrackBreaks: i.TrackBreaks, 3090 } 3091} 3092 3093func (path Path) String() string { 3094 pathType, err := path.PathType() 3095 if err != nil { 3096 return "" 3097 } 3098 switch pathType { 3099 case PathType_KBFS: 3100 return path.Kbfs().Path 3101 case PathType_KBFS_ARCHIVED: 3102 return path.KbfsArchived().Path 3103 case PathType_LOCAL: 3104 return path.Local() 3105 default: 3106 return "" 3107 } 3108} 3109 3110func (se *SelectorEntry) UnmarshalJSON(b []byte) error { 3111 if err := json.Unmarshal(b, &se.Index); err == nil { 3112 se.IsIndex = true 3113 return nil 3114 } 3115 3116 if err := json.Unmarshal(b, &se.Key); err == nil { 3117 se.IsKey = true 3118 return nil 3119 } 3120 3121 m := make(map[string]bool) 3122 if err := json.Unmarshal(b, &m); err != nil { 3123 return fmt.Errorf("invalid selector (not dict)") 3124 } 3125 ok1, ok2 := m["all"] 3126 if ok1 && ok2 { 3127 se.IsAll = true 3128 return nil 3129 } 3130 ok1, ok2 = m["contents"] 3131 if ok1 && ok2 { 3132 se.IsContents = true 3133 return nil 3134 } 3135 return fmt.Errorf("invalid selector (not recognized)") 3136} 3137 3138func (p PhoneNumber) String() string { 3139 return string(p) 3140} 3141 3142var nonDigits = regexp.MustCompile("[^\\d]") 3143 3144func PhoneNumberToAssertionValue(phoneNumber string) string { 3145 return nonDigits.ReplaceAllString(phoneNumber, "") 3146} 3147 3148func (p PhoneNumber) AssertionValue() string { 3149 return PhoneNumberToAssertionValue(p.String()) 3150} 3151 3152func (d TeamData) ID() TeamID { 3153 return d.Chain.Id 3154} 3155 3156func (d TeamData) IsPublic() bool { 3157 return d.Chain.Public 3158} 3159 3160func (d FastTeamData) ID() TeamID { 3161 return d.Chain.ID 3162} 3163 3164func (d FastTeamData) IsPublic() bool { 3165 return d.Chain.Public 3166} 3167 3168func (d HiddenTeamChain) ID() TeamID { 3169 return d.Id 3170} 3171 3172func (d HiddenTeamChain) IsPublic() bool { 3173 return d.Public 3174} 3175 3176func (d HiddenTeamChain) Summary() string { 3177 type pair struct { 3178 g PerTeamKeyGeneration 3179 q Seqno 3180 stubbed bool 3181 } 3182 var arr []pair 3183 for g, q := range d.ReaderPerTeamKeys { 3184 var full bool 3185 if d.Inner != nil { 3186 _, full = d.Inner[q] 3187 } 3188 arr = append(arr, pair{g: g, q: q, stubbed: !full}) 3189 } 3190 sort.Slice(arr, func(i, j int) bool { return arr[i].g < arr[j].g }) 3191 return fmt.Sprintf("{Team:%s, Last:%d, ReaderPerTeamKeys: %+v}", d.Id, d.Last, arr) 3192} 3193 3194func (f FullName) String() string { 3195 return string(f) 3196} 3197 3198func (h BoxSummaryHash) String() string { 3199 return string(h) 3200} 3201 3202func (r BoxAuditAttemptResult) IsOK() bool { 3203 switch r { 3204 case BoxAuditAttemptResult_OK_VERIFIED, BoxAuditAttemptResult_OK_NOT_ATTEMPTED_ROLE, BoxAuditAttemptResult_OK_NOT_ATTEMPTED_OPENTEAM, BoxAuditAttemptResult_OK_NOT_ATTEMPTED_SUBTEAM: 3205 return true 3206 default: 3207 return false 3208 } 3209} 3210 3211func (a BoxAuditAttempt) String() string { 3212 ret := fmt.Sprintf("%s", a.Result) 3213 if a.Error != nil { 3214 ret += fmt.Sprintf("\t(error: %s)", *a.Error) 3215 } 3216 if a.Rotated { 3217 ret += "\t(team rotated)" 3218 } 3219 return ret 3220} 3221 3222func (c ContactComponent) ValueString() string { 3223 switch { 3224 case c.Email != nil: 3225 return string(*c.Email) 3226 case c.PhoneNumber != nil: 3227 return string(*c.PhoneNumber) 3228 default: 3229 return "" 3230 } 3231} 3232 3233func (c ContactComponent) AssertionType() string { 3234 switch { 3235 case c.Email != nil: 3236 return "email" 3237 case c.PhoneNumber != nil: 3238 return "phone" 3239 default: 3240 return "" 3241 } 3242} 3243 3244func (c ContactComponent) FormatDisplayLabel(addLabel bool) string { 3245 if addLabel && c.Label != "" { 3246 return fmt.Sprintf("%s (%s)", c.ValueString(), c.Label) 3247 } 3248 return c.ValueString() 3249} 3250 3251func (fct FolderConflictType) MarshalText() ([]byte, error) { 3252 switch fct { 3253 case FolderConflictType_NONE: 3254 return []byte("none"), nil 3255 case FolderConflictType_IN_CONFLICT: 3256 return []byte("in conflict"), nil 3257 case FolderConflictType_IN_CONFLICT_AND_STUCK: 3258 return []byte("in conflict and stuck"), nil 3259 default: 3260 return []byte(fmt.Sprintf("unknown conflict type: %d", fct)), nil 3261 } 3262} 3263 3264func (fct *FolderConflictType) UnmarshalText(text []byte) error { 3265 switch string(text) { 3266 case "none": 3267 *fct = FolderConflictType_NONE 3268 case "in conflict": 3269 *fct = FolderConflictType_IN_CONFLICT 3270 case "in conflict and stuck": 3271 *fct = FolderConflictType_IN_CONFLICT_AND_STUCK 3272 default: 3273 return errors.New(fmt.Sprintf("Unknown conflict type: %s", text)) 3274 } 3275 return nil 3276} 3277 3278func (h *HiddenTeamChain) Tail() *HiddenTeamChainLink { 3279 last := h.Last 3280 if last == Seqno(0) { 3281 return nil 3282 } 3283 ret, ok := h.Inner[last] 3284 if !ok { 3285 return nil 3286 } 3287 return &ret 3288} 3289 3290func (h *HiddenTeamChain) TailTriple() *LinkTriple { 3291 last := h.Last 3292 if last == Seqno(0) { 3293 return nil 3294 } 3295 link, ok := h.Outer[last] 3296 if !ok { 3297 return nil 3298 } 3299 return &LinkTriple{ 3300 Seqno: last, 3301 LinkID: link, 3302 SeqType: SeqType_TEAM_PRIVATE_HIDDEN, 3303 } 3304} 3305 3306func (s Signer) UserVersion() UserVersion { 3307 return UserVersion{ 3308 Uid: s.U, 3309 EldestSeqno: s.E, 3310 } 3311} 3312 3313func (p PerTeamSeedCheck) Hash() (*PerTeamSeedCheckPostImage, error) { 3314 if p.Version != PerTeamSeedCheckVersion_V1 { 3315 return nil, errors.New("can only handle PerTeamKeySeedCheck V1") 3316 } 3317 ret := sha256.Sum256(p.Value[:]) 3318 return &PerTeamSeedCheckPostImage{ 3319 Version: PerTeamSeedCheckVersion_V1, 3320 Value: PerTeamSeedCheckValuePostImage(ret[:]), 3321 }, nil 3322} 3323 3324func (p PerTeamSeedCheckPostImage) Eq(p2 PerTeamSeedCheckPostImage) bool { 3325 return (p.Version == p2.Version) && hmac.Equal(p.Value[:], p2.Value[:]) 3326} 3327 3328func (r HiddenTeamChainRatchetSet) Flat() []LinkTripleAndTime { 3329 if r.Ratchets == nil { 3330 return nil 3331 } 3332 var ret []LinkTripleAndTime 3333 for _, v := range r.Ratchets { 3334 ret = append(ret, v) 3335 } 3336 return ret 3337} 3338 3339func (r HiddenTeamChainRatchetSet) IsEmpty() bool { 3340 return r.Ratchets == nil || len(r.Ratchets) == 0 3341} 3342 3343func (r HiddenTeamChainRatchetSet) Max() Seqno { 3344 var ret Seqno 3345 if r.Ratchets == nil { 3346 return ret 3347 } 3348 for _, v := range r.Ratchets { 3349 if v.Triple.Seqno > ret { 3350 ret = v.Triple.Seqno 3351 } 3352 } 3353 return ret 3354} 3355 3356func (r HiddenTeamChainRatchetSet) MaxTriple() *LinkTriple { 3357 if r.Ratchets == nil { 3358 return nil 3359 } 3360 var out LinkTriple 3361 for _, v := range r.Ratchets { 3362 if v.Triple.Seqno > out.Seqno { 3363 out = v.Triple 3364 } 3365 } 3366 return &out 3367} 3368 3369func (r *HiddenTeamChain) MaxTriple() *LinkTriple { 3370 tail := r.TailTriple() 3371 rat := r.RatchetSet.MaxTriple() 3372 if rat == nil && tail == nil { 3373 return nil 3374 } 3375 if rat == nil { 3376 return tail 3377 } 3378 if tail == nil { 3379 return rat 3380 } 3381 if tail.Seqno > rat.Seqno { 3382 return tail 3383 } 3384 return rat 3385} 3386 3387func (r *HiddenTeamChainRatchetSet) init() { 3388 if r.Ratchets == nil { 3389 r.Ratchets = make(map[RatchetType]LinkTripleAndTime) 3390 } 3391} 3392 3393func (r *HiddenTeamChainRatchetSet) Merge(r2 HiddenTeamChainRatchetSet) (updated bool) { 3394 r.init() 3395 if r2.Ratchets == nil { 3396 return false 3397 } 3398 for k, v := range r2.Ratchets { 3399 if r.Add(k, v) { 3400 updated = true 3401 } 3402 } 3403 return updated 3404} 3405 3406func (r *HiddenTeamChainRatchetSet) Add(t RatchetType, v LinkTripleAndTime) (changed bool) { 3407 r.init() 3408 found, ok := r.Ratchets[t] 3409 if (v.Triple.SeqType == SeqType_TEAM_PRIVATE_HIDDEN) && (!ok || v.Triple.Seqno > found.Triple.Seqno) { 3410 r.Ratchets[t] = v 3411 changed = true 3412 } 3413 return changed 3414} 3415 3416func (r LinkTripleAndTime) Clashes(r2 LinkTripleAndTime) bool { 3417 l1 := r.Triple 3418 l2 := r2.Triple 3419 return (l1.Seqno == l2.Seqno && l1.SeqType == l2.SeqType && !l1.LinkID.Eq(l2.LinkID)) 3420} 3421 3422func (r MerkleRootV2) Eq(s MerkleRootV2) bool { 3423 return r.Seqno == s.Seqno && r.HashMeta.Eq(s.HashMeta) 3424} 3425 3426func (d *HiddenTeamChain) GetLastCommittedSeqno() Seqno { 3427 if d == nil { 3428 return 0 3429 } 3430 return d.LastCommittedSeqno 3431} 3432 3433func (d *HiddenTeamChain) GetOuter() map[Seqno]LinkID { 3434 if d == nil { 3435 return nil 3436 } 3437 return d.Outer 3438} 3439 3440func (d *HiddenTeamChain) PopulateLastFull() { 3441 if d == nil { 3442 return 3443 } 3444 if d.LastFull != Seqno(0) { 3445 return 3446 } 3447 for i := Seqno(1); i <= d.Last; i++ { 3448 _, found := d.Inner[i] 3449 if !found { 3450 break 3451 } 3452 d.LastFull = i 3453 } 3454} 3455 3456func (d *HiddenTeamChain) LastFullPopulateIfUnset() Seqno { 3457 if d == nil { 3458 return Seqno(0) 3459 } 3460 if d.LastFull == Seqno(0) { 3461 d.PopulateLastFull() 3462 } 3463 return d.LastFull 3464} 3465 3466func (d *HiddenTeamChain) Merge(newData HiddenTeamChain) (updated bool, err error) { 3467 3468 for seqno, link := range newData.Outer { 3469 existing, ok := d.Outer[seqno] 3470 if ok && !existing.Eq(link) { 3471 return false, fmt.Errorf("bad merge since at seqno %d, link clash: %s != %s", seqno, existing, link) 3472 } 3473 if ok { 3474 continue 3475 } 3476 d.Outer[seqno] = link 3477 updated = true 3478 if seqno > d.Last { 3479 d.Last = seqno 3480 } 3481 } 3482 3483 for q, i := range newData.Inner { 3484 _, found := d.Inner[q] 3485 if found { 3486 continue 3487 } 3488 d.Inner[q] = i 3489 if ptk, ok := i.Ptk[PTKType_READER]; ok { 3490 d.ReaderPerTeamKeys[ptk.Ptk.Gen] = q 3491 } 3492 3493 // If we previously loaded full links up to d.LastFull, but this is d.LastFull+1, 3494 // then we can safely bump the pointer one foward. 3495 if q == d.LastFull+Seqno(1) { 3496 d.LastFull = q 3497 } 3498 updated = true 3499 } 3500 if newData.Last > d.Last { 3501 d.Last = newData.Last 3502 } 3503 3504 if newData.LastCommittedSeqno > d.LastCommittedSeqno { 3505 d.LastCommittedSeqno = newData.LastCommittedSeqno 3506 updated = true 3507 } 3508 3509 for k, v := range newData.LastPerTeamKeys { 3510 existing, ok := d.LastPerTeamKeys[k] 3511 if !ok || existing < v { 3512 d.LastPerTeamKeys[k] = v 3513 } 3514 } 3515 3516 for k, v := range newData.MerkleRoots { 3517 existing, ok := d.MerkleRoots[k] 3518 if ok && !existing.Eq(v) { 3519 return false, fmt.Errorf("bad merge since at seqno %d, merkle root clash: %+v != %+v", k, existing, v) 3520 } 3521 if ok { 3522 continue 3523 } 3524 d.MerkleRoots[k] = v 3525 updated = true 3526 } 3527 3528 if d.RatchetSet.Merge(newData.RatchetSet) { 3529 updated = true 3530 } 3531 3532 for k := range d.LinkReceiptTimes { 3533 if k <= newData.LastCommittedSeqno { 3534 // This link has been committed to the blind tree, no need to keep 3535 // track of it any more 3536 delete(d.LinkReceiptTimes, k) 3537 updated = true 3538 } 3539 } 3540 3541 for k, v := range newData.LinkReceiptTimes { 3542 if _, found := d.LinkReceiptTimes[k]; !found { 3543 if d.LinkReceiptTimes == nil { 3544 d.LinkReceiptTimes = make(map[Seqno]Time) 3545 } 3546 d.LinkReceiptTimes[k] = v 3547 updated = true 3548 } 3549 } 3550 3551 return updated, nil 3552} 3553 3554func (h HiddenTeamChain) HasSeqno(s Seqno) bool { 3555 _, found := h.Outer[s] 3556 return found 3557} 3558 3559func NewHiddenTeamChain(id TeamID) *HiddenTeamChain { 3560 return &HiddenTeamChain{ 3561 Id: id, 3562 Subversion: 1, // We are now on Version 1.1 3563 LastPerTeamKeys: make(map[PTKType]Seqno), 3564 ReaderPerTeamKeys: make(map[PerTeamKeyGeneration]Seqno), 3565 Outer: make(map[Seqno]LinkID), 3566 Inner: make(map[Seqno]HiddenTeamChainLink), 3567 MerkleRoots: make(map[Seqno]MerkleRootV2), 3568 } 3569} 3570 3571func (h *HiddenTeamChain) Tombstone() (changed bool) { 3572 if h.Tombstoned { 3573 return false 3574 } 3575 h.LastPerTeamKeys = make(map[PTKType]Seqno) 3576 h.ReaderPerTeamKeys = make(map[PerTeamKeyGeneration]Seqno) 3577 h.Outer = make(map[Seqno]LinkID) 3578 h.Inner = make(map[Seqno]HiddenTeamChainLink) 3579 h.Tombstoned = true 3580 return true 3581} 3582 3583func (h *HiddenTeamChain) Freeze() (changed bool) { 3584 if h.Frozen { 3585 return false 3586 } 3587 h.LastPerTeamKeys = make(map[PTKType]Seqno) 3588 h.ReaderPerTeamKeys = make(map[PerTeamKeyGeneration]Seqno) 3589 h.Inner = make(map[Seqno]HiddenTeamChainLink) 3590 newOuter := make(map[Seqno]LinkID) 3591 if h.Last != Seqno(0) { 3592 newOuter[h.Last] = h.Outer[h.Last] 3593 } 3594 h.Outer = newOuter 3595 h.Frozen = true 3596 return true 3597} 3598 3599func (h HiddenTeamChain) LastReaderPerTeamKeyLinkID() (ret LinkID) { 3600 seqno, ok := h.LastPerTeamKeys[PTKType_READER] 3601 if !ok { 3602 return ret 3603 } 3604 tmp, ok := h.Outer[seqno] 3605 if !ok { 3606 return ret 3607 } 3608 return tmp 3609} 3610 3611func (h *HiddenTeamChain) GetReaderPerTeamKeyAtGeneration(g PerTeamKeyGeneration) (ret PerTeamKey, found bool) { 3612 if h == nil { 3613 return ret, false 3614 } 3615 q, ok := h.ReaderPerTeamKeys[g] 3616 if !ok { 3617 return ret, false 3618 } 3619 inner, ok := h.Inner[q] 3620 if !ok { 3621 return ret, false 3622 } 3623 key, ok := inner.Ptk[PTKType_READER] 3624 if !ok { 3625 return ret, false 3626 } 3627 return key.Ptk, true 3628} 3629 3630func (h *HiddenTeamChain) MaxReaderPerTeamKey() *PerTeamKey { 3631 if h == nil { 3632 return nil 3633 } 3634 seqno, ok := h.LastPerTeamKeys[PTKType_READER] 3635 if !ok { 3636 return nil 3637 } 3638 inner, ok := h.Inner[seqno] 3639 if !ok { 3640 return nil 3641 } 3642 ptk, ok := inner.Ptk[PTKType_READER] 3643 if !ok { 3644 return nil 3645 } 3646 return &ptk.Ptk 3647} 3648 3649func (h *HiddenTeamChain) MaxReaderPerTeamKeyGeneration() PerTeamKeyGeneration { 3650 k := h.MaxReaderPerTeamKey() 3651 if k == nil { 3652 return PerTeamKeyGeneration(0) 3653 } 3654 return k.Gen 3655} 3656 3657func (h *HiddenTeamChain) KeySummary() string { 3658 if h == nil { 3659 return "Ø" 3660 } 3661 return fmt.Sprintf("{last:%d, lastPerTeamKeys:%+v, readerPerTeamKeys: %+v}", h.Last, h.LastPerTeamKeys, h.ReaderPerTeamKeys) 3662} 3663 3664func (h *HiddenTeamChain) LinkAndKeySummary() string { 3665 if h == nil { 3666 return "empty" 3667 } 3668 ks := h.KeySummary() 3669 return fmt.Sprintf("{nOuterlinks: %d, nInnerLinks:%d, keys:%s}", len(h.Outer), len(h.Inner), ks) 3670} 3671 3672func (h *TeamData) KeySummary() string { 3673 if h == nil { 3674 return "Ø" 3675 } 3676 var p []PerTeamKeyGeneration 3677 for k := range h.PerTeamKeySeedsUnverified { 3678 p = append(p, k) 3679 } 3680 m := make(map[PerTeamKeyGeneration]bool) 3681 for _, v := range h.ReaderKeyMasks { 3682 for k := range v { 3683 m[k] = true 3684 } 3685 } 3686 var r []PerTeamKeyGeneration 3687 for k := range m { 3688 r = append(r, k) 3689 } 3690 return fmt.Sprintf("{ptksu:%v, rkms:%v, sigchain:%s}", p, r, h.Chain.KeySummary()) 3691} 3692 3693func (s TeamSigChainState) UserRole(user UserVersion) TeamRole { 3694 points := s.UserLog[user] 3695 if len(points) == 0 { 3696 return TeamRole_NONE 3697 } 3698 role := points[len(points)-1].Role 3699 return role 3700} 3701 3702func (s TeamSigChainState) GetUserLastJoinTime(user UserVersion) (time Time, err error) { 3703 if s.UserRole(user) == TeamRole_NONE { 3704 return 0, fmt.Errorf("In GetUserLastJoinTime: User %s is not a member of team %v", user.Uid, s.Id) 3705 } 3706 // Look for the latest join event, i.e. the latest transition from a role NONE to a different valid one. 3707 points := s.UserLog[user] 3708 for i := len(points) - 1; i > -1; i-- { 3709 if points[i].Role == TeamRole_NONE { 3710 // this is the last time in the sigchain this user has role none 3711 // (note that it cannot be the last link in the chain, otherwise the 3712 // user would have role NONE), so the link after this one is the one 3713 // where they joined the team last. 3714 return points[i+1].SigMeta.Time, nil 3715 } 3716 } 3717 // If the user never had role none, they joined at the time of their first 3718 // UserLog entry (they need to have at least one, else again their role would be 3719 // NONE). 3720 return points[0].SigMeta.Time, nil 3721} 3722 3723// GetUserLastRoleChangeTime returns the time of the last role change for user 3724// in team. If the user left the team as a last change, the time of such leave 3725// event is returned. If the user was never in the team, then this function 3726// returns time=0 and wasMember=false. 3727func (s TeamSigChainState) GetUserLastRoleChangeTime(user UserVersion) (time Time, wasMember bool) { 3728 points := s.UserLog[user] 3729 if len(points) == 0 { 3730 return 0, false 3731 } 3732 return points[len(points)-1].SigMeta.Time, true 3733} 3734 3735func (s TeamSigChainState) KeySummary() string { 3736 var v []PerTeamKeyGeneration 3737 for k := range s.PerTeamKeys { 3738 v = append(v, k) 3739 } 3740 return fmt.Sprintf("{maxPTK:%d, ptk:%v}", s.MaxPerTeamKeyGeneration, v) 3741} 3742 3743func (s TeamSigChainState) HasAnyStubbedLinks() bool { 3744 for _, v := range s.StubbedLinks { 3745 if v { 3746 return true 3747 } 3748 } 3749 return false 3750} 3751 3752func (s TeamSigChainState) ListSubteams() (res []TeamIDAndName) { 3753 type Entry struct { 3754 ID TeamID 3755 Name TeamName 3756 // Seqno of the last cached rename of this team 3757 Seqno Seqno 3758 } 3759 // Use a map to deduplicate names. If there is a subteam name 3760 // collision, take the one with the latest (parent) seqno 3761 // modifying its name. 3762 // A collision could occur if you were removed from a team 3763 // and miss its renaming or deletion to stubbing. 3764 resMap := make(map[string] /*TeamName*/ Entry) 3765 for subteamID, points := range s.SubteamLog { 3766 if len(points) == 0 { 3767 // this should never happen 3768 continue 3769 } 3770 lastPoint := points[len(points)-1] 3771 if lastPoint.Name.IsNil() { 3772 // the subteam has been deleted 3773 continue 3774 } 3775 entry := Entry{ 3776 ID: subteamID, 3777 Name: lastPoint.Name, 3778 Seqno: lastPoint.Seqno, 3779 } 3780 existing, ok := resMap[entry.Name.String()] 3781 replace := !ok || (entry.Seqno >= existing.Seqno) 3782 if replace { 3783 resMap[entry.Name.String()] = entry 3784 } 3785 } 3786 for _, entry := range resMap { 3787 res = append(res, TeamIDAndName{ 3788 Id: entry.ID, 3789 Name: entry.Name, 3790 }) 3791 } 3792 return res 3793} 3794 3795func (s TeamSigChainState) GetAllUVs() (res []UserVersion) { 3796 for uv := range s.UserLog { 3797 if s.UserRole(uv) != TeamRole_NONE { 3798 res = append(res, uv) 3799 } 3800 } 3801 return res 3802} 3803 3804func (s TeamSigChainState) ActiveInvites() (ret []TeamInvite) { 3805 for _, md := range s.InviteMetadatas { 3806 if code, err := md.Status.Code(); err == nil && 3807 code == TeamInviteMetadataStatusCode_ACTIVE { 3808 ret = append(ret, md.Invite) 3809 } 3810 } 3811 return ret 3812} 3813 3814func (h *HiddenTeamChain) IsStale() bool { 3815 if h == nil { 3816 return false 3817 } 3818 max := h.RatchetSet.Max() 3819 if max < h.LatestSeqnoHint { 3820 max = h.LatestSeqnoHint 3821 } 3822 if max == Seqno(0) { 3823 return false 3824 } 3825 _, fresh := h.Outer[max] 3826 return !fresh 3827} 3828 3829func (k TeamEphemeralKey) Ctime() Time { 3830 typ, err := k.KeyType() 3831 if err != nil { 3832 return 0 3833 } 3834 switch typ { 3835 case TeamEphemeralKeyType_TEAM: 3836 return k.Team().Metadata.Ctime 3837 case TeamEphemeralKeyType_TEAMBOT: 3838 return k.Teambot().Metadata.Ctime 3839 default: 3840 return 0 3841 } 3842} 3843 3844func (k TeamEphemeralKeyBoxed) Ctime() Time { 3845 typ, err := k.KeyType() 3846 if err != nil { 3847 return 0 3848 } 3849 switch typ { 3850 case TeamEphemeralKeyType_TEAM: 3851 return k.Team().Metadata.Ctime 3852 case TeamEphemeralKeyType_TEAMBOT: 3853 return k.Teambot().Metadata.Ctime 3854 default: 3855 return 0 3856 } 3857} 3858 3859func (k TeamEphemeralKey) Generation() EkGeneration { 3860 typ, err := k.KeyType() 3861 if err != nil { 3862 return 0 3863 } 3864 switch typ { 3865 case TeamEphemeralKeyType_TEAM: 3866 return k.Team().Metadata.Generation 3867 case TeamEphemeralKeyType_TEAMBOT: 3868 return k.Teambot().Metadata.Generation 3869 default: 3870 return 0 3871 } 3872} 3873 3874func (k TeamEphemeralKey) Material() Bytes32 { 3875 typ, err := k.KeyType() 3876 if err != nil { 3877 return [32]byte{} 3878 } 3879 switch typ { 3880 case TeamEphemeralKeyType_TEAM: 3881 return k.Team().Seed 3882 case TeamEphemeralKeyType_TEAMBOT: 3883 return k.Teambot().Seed 3884 default: 3885 return [32]byte{} 3886 } 3887} 3888 3889func (k TeamEphemeralKeyBoxed) Generation() EkGeneration { 3890 typ, err := k.KeyType() 3891 if err != nil { 3892 return 0 3893 } 3894 switch typ { 3895 case TeamEphemeralKeyType_TEAM: 3896 return k.Team().Metadata.Generation 3897 case TeamEphemeralKeyType_TEAMBOT: 3898 return k.Teambot().Metadata.Generation 3899 default: 3900 return 0 3901 } 3902} 3903 3904func (k TeamEphemeralKeyType) IsTeambot() bool { 3905 return k == TeamEphemeralKeyType_TEAMBOT 3906} 3907 3908func (k TeamEphemeralKeyType) IsTeam() bool { 3909 return k == TeamEphemeralKeyType_TEAM 3910} 3911 3912// IsLimited returns if the network is considered limited based on the type. 3913func (s MobileNetworkState) IsLimited() bool { 3914 switch s { 3915 case MobileNetworkState_WIFI, MobileNetworkState_NOTAVAILABLE: 3916 return false 3917 default: 3918 return true 3919 } 3920} 3921 3922func (k TeambotKey) Generation() int { 3923 return int(k.Metadata.Generation) 3924} 3925 3926func (k TeambotKey) Material() Bytes32 { 3927 return k.Seed 3928} 3929 3930func (r APIUserSearchResult) GetStringIDForCompare() string { 3931 switch { 3932 case r.Contact != nil: 3933 return fmt.Sprintf("%s%s", r.Contact.DisplayName, r.Contact.DisplayLabel) 3934 case r.Imptofu != nil: 3935 return fmt.Sprintf("%s%s", r.Imptofu.PrettyName, r.Imptofu.Label) 3936 case r.Keybase != nil: 3937 return r.Keybase.Username 3938 default: 3939 return "" 3940 } 3941} 3942 3943func NewPathWithKbfsPath(path string) Path { 3944 return NewPathWithKbfs(KBFSPath{Path: path}) 3945} 3946 3947func (p PerTeamKey) Equal(q PerTeamKey) bool { 3948 return p.EncKID.Equal(q.EncKID) && p.SigKID.Equal(q.SigKID) 3949} 3950 3951func (b BotToken) IsNil() bool { 3952 return len(b) == 0 3953} 3954 3955func (b BotToken) Exists() bool { 3956 return !b.IsNil() 3957} 3958 3959func (b BotToken) String() string { 3960 return string(b) 3961} 3962 3963var botTokenRxx = regexp.MustCompile(`^[a-zA-Z0-9_-]{32}$`) 3964 3965func NewBotToken(s string) (BotToken, error) { 3966 if !botTokenRxx.MatchString(s) { 3967 return BotToken(""), errors.New("bad bot token") 3968 } 3969 return BotToken(s), nil 3970} 3971 3972func (b BadgeConversationInfo) IsEmpty() bool { 3973 return b.UnreadMessages == 0 && b.BadgeCount == 0 3974} 3975 3976func (s *TeamBotSettings) Eq(o *TeamBotSettings) bool { 3977 return reflect.DeepEqual(s, o) 3978} 3979 3980func (s *TeamBotSettings) ConvIDAllowed(strCID string) bool { 3981 if s == nil { 3982 return true 3983 } 3984 for _, strConvID := range s.Convs { 3985 if strCID == strConvID { 3986 return true 3987 } 3988 } 3989 return len(s.Convs) == 0 3990} 3991 3992func (b UserBlockedBody) Summarize() UserBlockedSummary { 3993 ret := UserBlockedSummary{ 3994 Blocker: b.Username, 3995 Blocks: make(map[string][]UserBlockState), 3996 } 3997 for _, block := range b.Blocks { 3998 if block.Chat != nil { 3999 ret.Blocks[block.Username] = append(ret.Blocks[block.Username], UserBlockState{UserBlockType_CHAT, *block.Chat}) 4000 } 4001 if block.Follow != nil { 4002 ret.Blocks[block.Username] = append(ret.Blocks[block.Username], UserBlockState{UserBlockType_FOLLOW, *block.Follow}) 4003 } 4004 } 4005 return ret 4006} 4007 4008func FilterMembersDetails(membMap map[string]struct{}, details []TeamMemberDetails) (res []TeamMemberDetails) { 4009 res = []TeamMemberDetails{} 4010 for _, member := range details { 4011 if _, ok := membMap[member.Username]; ok { 4012 res = append(res, member) 4013 } 4014 } 4015 return res 4016} 4017 4018func FilterTeamDetailsForMembers(usernames []string, details TeamDetails) TeamDetails { 4019 membMap := make(map[string]struct{}) 4020 for _, username := range usernames { 4021 membMap[username] = struct{}{} 4022 } 4023 res := details.DeepCopy() 4024 res.Members.Owners = FilterMembersDetails(membMap, res.Members.Owners) 4025 res.Members.Admins = FilterMembersDetails(membMap, res.Members.Admins) 4026 res.Members.Writers = FilterMembersDetails(membMap, res.Members.Writers) 4027 res.Members.Readers = FilterMembersDetails(membMap, res.Members.Readers) 4028 res.Members.Bots = FilterMembersDetails(membMap, res.Members.Bots) 4029 res.Members.RestrictedBots = FilterMembersDetails(membMap, res.Members.RestrictedBots) 4030 return res 4031} 4032 4033func (b FeaturedBot) DisplayName() string { 4034 if b.BotAlias == "" { 4035 return b.BotUsername 4036 } 4037 return fmt.Sprintf("%s (%s)", b.BotAlias, b.BotUsername) 4038} 4039 4040func (b FeaturedBot) Owner() string { 4041 if b.OwnerTeam != nil { 4042 return *b.OwnerTeam 4043 } 4044 if b.OwnerUser != nil { 4045 return *b.OwnerUser 4046 } 4047 return "" 4048} 4049 4050func (b FeaturedBot) Eq(o FeaturedBot) bool { 4051 return b.BotAlias == o.BotAlias && 4052 b.Description == o.Description && 4053 b.ExtendedDescription == o.ExtendedDescription && 4054 b.BotUsername == o.BotUsername && 4055 b.Owner() == o.Owner() 4056} 4057 4058func (a SearchArg) String() string { 4059 // Don't leak user's query string 4060 return fmt.Sprintf("Limit: %d, Offset: %d", a.Limit, a.Offset) 4061} 4062func (a SearchLocalArg) String() string { 4063 // Don't leak user's query string 4064 return fmt.Sprintf("Limit: %d, SkipCache: %v", a.Limit, a.SkipCache) 4065} 4066 4067func (b FeaturedBotsRes) Eq(o FeaturedBotsRes) bool { 4068 if len(b.Bots) != len(o.Bots) { 4069 return false 4070 } 4071 for i, bot := range b.Bots { 4072 if !bot.Eq(o.Bots[i]) { 4073 return false 4074 } 4075 } 4076 return true 4077} 4078 4079// Redact modifies the given ClientDetails struct 4080func (d *ClientDetails) Redact() { 4081 tmp := fmt.Sprintf("%v", d.Argv) 4082 re := regexp.MustCompile(`\b(chat|fs|encrypt|git|accept-invite|wallet\s+send|wallet\s+import|passphrase\s+check)\b`) 4083 if mtch := re.FindString(tmp); len(mtch) > 0 { 4084 d.Argv = []string{d.Argv[0], mtch, redactedReplacer} 4085 } 4086 4087 for i, arg := range d.Argv { 4088 if strings.Contains(arg, "paperkey") && i+1 < len(d.Argv) && !strings.HasPrefix(d.Argv[i+1], "-") { 4089 d.Argv[i+1] = redactedReplacer 4090 } 4091 } 4092} 4093 4094func (s UserSummarySet) Usernames() (ret []string) { 4095 for _, x := range s.Users { 4096 ret = append(ret, x.Username) 4097 } 4098 return ret 4099} 4100 4101func (x InstrumentationStat) AppendStat(y InstrumentationStat) InstrumentationStat { 4102 x.Mtime = ToTime(time.Now()) 4103 x.NumCalls += y.NumCalls 4104 x.TotalDur += y.TotalDur 4105 if y.MaxDur > x.MaxDur { 4106 x.MaxDur = y.MaxDur 4107 } 4108 if y.MinDur < x.MinDur { 4109 x.MinDur = y.MinDur 4110 } 4111 4112 x.TotalSize += y.TotalSize 4113 if y.MaxSize > x.MaxSize { 4114 x.MaxSize = y.MaxSize 4115 } 4116 if y.MinSize < x.MinSize { 4117 x.MinSize = y.MinSize 4118 } 4119 4120 x.AvgDur = x.TotalDur / DurationMsec(x.NumCalls) 4121 x.AvgSize = x.TotalSize / int64(x.NumCalls) 4122 return x 4123} 4124 4125func (e TeamSearchExport) Hash() string { 4126 l := make([]TeamSearchItem, 0, len(e.Items)) 4127 for _, item := range e.Items { 4128 l = append(l, item) 4129 } 4130 sort.Slice(l, func(i, j int) bool { 4131 return l[i].Id.Less(l[j].Id) 4132 }) 4133 hasher := sha256.New() 4134 for _, team := range l { 4135 log := math.Floor(math.Log10(float64(team.MemberCount))) 4136 rounder := int(math.Pow(10, log)) 4137 value := (team.MemberCount / rounder) * rounder 4138 hasher.Write(team.Id.ToBytes()) 4139 hasher.Write([]byte(fmt.Sprintf("%d", value))) 4140 } 4141 for _, id := range e.Suggested { 4142 hasher.Write(id.ToBytes()) 4143 } 4144 return hex.EncodeToString(hasher.Sum(nil)) 4145} 4146 4147// web-of-trust 4148// In order of descending quality. 4149// Keep in sync with: 4150// - server helpers/wot.ts 4151// - gui WebOfTrustVerificationType 4152const ( 4153 UsernameVerificationType_IN_PERSON = "in_person" 4154 UsernameVerificationType_VIDEO = "video" 4155 UsernameVerificationType_AUDIO = "audio" 4156 UsernameVerificationType_PROOFS = "proofs" 4157 UsernameVerificationType_OTHER_CHAT = "other_chat" 4158 UsernameVerificationType_FAMILIAR = "familiar" 4159 UsernameVerificationType_OTHER = "other" 4160) 4161 4162var UsernameVerificationTypeMap = map[string]UsernameVerificationType{ 4163 "in_person": UsernameVerificationType_IN_PERSON, 4164 "proofs": UsernameVerificationType_PROOFS, 4165 "video": UsernameVerificationType_VIDEO, 4166 "audio": UsernameVerificationType_AUDIO, 4167 "other_chat": UsernameVerificationType_OTHER_CHAT, 4168 "familiar": UsernameVerificationType_FAMILIAR, 4169 "other": UsernameVerificationType_OTHER, 4170} 4171 4172func (fsc FolderSyncConfig) Equal(other FolderSyncConfig) bool { 4173 if fsc.Mode != other.Mode { 4174 return false 4175 } 4176 if len(fsc.Paths) != len(other.Paths) { 4177 return false 4178 } 4179 for i, p := range fsc.Paths { 4180 if p != other.Paths[i] { 4181 return false 4182 } 4183 } 4184 return true 4185} 4186 4187func (t SeitanIKeyInvitelink) String() string { 4188 return string(t) 4189} 4190 4191// UserRolePairsHaveOwner check if a list of UserRolePair has user with role 4192// OWNER. 4193func UserRolePairsHaveOwner(users []UserRolePair) bool { 4194 for _, urp := range users { 4195 if urp.Role == TeamRole_OWNER { 4196 return true 4197 } 4198 } 4199 return false 4200} 4201 4202func (e EmailAddress) String() string { 4203 return string(e) 4204} 4205 4206func NewTeamSigMeta(sigMeta SignatureMetadata, uv UserVersion) TeamSignatureMetadata { 4207 return TeamSignatureMetadata{SigMeta: sigMeta, Uv: uv} 4208} 4209 4210func NewTeamInviteMetadata(invite TeamInvite, teamSigMeta TeamSignatureMetadata) TeamInviteMetadata { 4211 return TeamInviteMetadata{ 4212 Invite: invite, 4213 TeamSigMeta: teamSigMeta, 4214 Status: NewTeamInviteMetadataStatusWithActive(), 4215 } 4216} 4217 4218func (a AnnotatedTeam) ToLegacyTeamDetails() TeamDetails { 4219 var members TeamMembersDetails 4220 for _, member := range a.Members { 4221 switch member.Role { 4222 case TeamRole_RESTRICTEDBOT: 4223 members.RestrictedBots = append(members.RestrictedBots, member) 4224 case TeamRole_BOT: 4225 members.Bots = append(members.Bots, member) 4226 case TeamRole_READER: 4227 members.Readers = append(members.Readers, member) 4228 case TeamRole_WRITER: 4229 members.Writers = append(members.Writers, member) 4230 case TeamRole_ADMIN: 4231 members.Admins = append(members.Admins, member) 4232 case TeamRole_OWNER: 4233 members.Owners = append(members.Owners, member) 4234 } 4235 } 4236 4237 annotatedActiveInvites := make(map[TeamInviteID]AnnotatedTeamInvite) 4238 for _, annotatedInvite := range a.Invites { 4239 code, _ := annotatedInvite.InviteMetadata.Status.Code() 4240 if code != TeamInviteMetadataStatusCode_ACTIVE { 4241 continue 4242 } 4243 annotatedActiveInvites[annotatedInvite.InviteMetadata.Invite.Id] = annotatedInvite 4244 } 4245 4246 return TeamDetails{ 4247 Name: a.Name, 4248 Members: members, 4249 KeyGeneration: a.KeyGeneration, 4250 AnnotatedActiveInvites: annotatedActiveInvites, 4251 Settings: a.Settings, 4252 Showcase: a.Showcase, 4253 } 4254} 4255