github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/protocol/keybase1/extras.go (about) 1 // Copyright 2015 Keybase, Inc. All rights reserved. Use of 2 // this source code is governed by the included BSD license. 3 4 package keybase1 5 6 import ( 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 31 const ( 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. 50 var PublicUID = UID(PUBLIC_UID) 51 52 const ( 53 SIG_ID_LEN = 32 54 SIG_ID_SUFFIX = 0x0f 55 SIG_SHORT_ID_BYTES = 27 56 SigIDQueryMin = 8 57 ) 58 59 const ( 60 DeviceIDLen = 16 61 DeviceIDSuffix = 0x18 62 DeviceIDSuffixHex = "18" 63 ) 64 65 const ( 66 KidLen = 35 // bytes 67 KidSuffix = 0x0a // a byte 68 KidVersion = 0x1 69 ) 70 71 const redactedReplacer = "[REDACTED]" 72 73 func Unquote(data []byte) string { 74 return strings.Trim(string(data), "\"") 75 } 76 77 func Quote(s string) []byte { 78 return []byte("\"" + s + "\"") 79 } 80 81 func UnquoteBytes(data []byte) []byte { 82 return bytes.Trim(data, "\"") 83 } 84 85 func KIDFromSlice(b []byte) KID { 86 return KID(hex.EncodeToString(b)) 87 } 88 89 func (b BinaryKID) ToKID() KID { 90 return KIDFromSlice([]byte(b)) 91 } 92 93 func (k KID) ToBinaryKID() BinaryKID { 94 return BinaryKID(k.ToBytes()) 95 } 96 97 func (b BinaryKID) Equal(c BinaryKID) bool { 98 return bytes.Equal([]byte(b), []byte(c)) 99 } 100 101 func 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 129 func 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 141 func cieq(s string, t string) bool { 142 return strings.EqualFold(s, t) 143 } 144 145 func 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 156 func (h KBFSRootHash) String() string { 157 return hex.EncodeToString(h) 158 } 159 160 func (h KBFSRootHash) Eq(h2 KBFSRootHash) bool { 161 return hmac.Equal(h[:], h2[:]) 162 } 163 164 func (h HashMeta) String() string { 165 return hex.EncodeToString(h) 166 } 167 168 func (h HashMeta) Eq(h2 HashMeta) bool { 169 return hmac.Equal(h[:], h2[:]) 170 } 171 172 func (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 181 func (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 190 func 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 204 func (s SHA512) String() string { 205 return hex.EncodeToString(s) 206 } 207 208 func (s SHA512) Eq(s2 SHA512) bool { 209 return hmac.Equal(s[:], s2[:]) 210 } 211 212 func (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 221 func (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 237 func (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 246 func (h HashMeta) MarshalJSON() ([]byte, error) { 247 return Quote(h.String()), nil 248 } 249 250 func KIDFromString(s string) KID { 251 // there are no validations for KIDs (length, suffixes) 252 return KID(s) 253 } 254 255 func (k KID) IsValid() bool { 256 return len(k) > 0 257 } 258 259 func (k KID) String() string { 260 return string(k) 261 } 262 263 func (k KID) IsNil() bool { 264 return len(k) == 0 265 } 266 267 func (k KID) Exists() bool { 268 return !k.IsNil() 269 } 270 271 func (k KID) Equal(v KID) bool { 272 return k == v 273 } 274 275 func (k KID) NotEqual(v KID) bool { 276 return !k.Equal(v) 277 } 278 279 func (k KID) SecureEqual(v KID) bool { 280 return hmac.Equal(k.ToBytes(), v.ToBytes()) 281 } 282 283 func (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 301 func (k KID) ToBytes() []byte { 302 b, err := hex.DecodeString(string(k)) 303 if err != nil { 304 return nil 305 } 306 return b 307 } 308 309 func (k KID) GetKeyType() byte { 310 raw := k.ToBytes() 311 if len(raw) < 2 { 312 return 0 313 } 314 return raw[1] 315 } 316 317 func (k KID) ToShortIDString() string { 318 return encode(k.ToBytes()[0:12]) 319 } 320 321 func (k KID) ToJsonw() *jsonw.Wrapper { 322 if k.IsNil() { 323 return jsonw.NewNil() 324 } 325 return jsonw.NewString(string(k)) 326 } 327 328 func (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 337 func 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 346 func (p *PGPFingerprint) String() string { 347 return hex.EncodeToString(p[:]) 348 } 349 350 func (p PGPFingerprint) MarshalJSON() ([]byte, error) { 351 return Quote(p.String()), nil 352 } 353 354 func (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 363 func DeviceIDFromBytes(b [DeviceIDLen]byte) DeviceID { 364 return DeviceID(hex.EncodeToString(b[:])) 365 } 366 367 func (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 382 func 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 391 func LinkIDFromByte32(b [32]byte) LinkID { 392 return LinkID(hex.EncodeToString(b[:])) 393 } 394 395 func 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 406 func (d DeviceID) String() string { 407 return string(d) 408 } 409 410 func (d DeviceID) IsNil() bool { 411 return len(d) == 0 412 } 413 414 func (d DeviceID) Exists() bool { 415 return !d.IsNil() 416 } 417 418 func (d DeviceID) Eq(d2 DeviceID) bool { 419 return d == d2 420 } 421 422 func (t TeamID) Eq(t2 TeamID) bool { 423 return t == t2 424 } 425 426 func (l LinkID) Eq(l2 LinkID) bool { 427 return l == l2 428 } 429 430 func (l LinkID) IsNil() bool { 431 return len(l) == 0 432 } 433 434 func (l LinkID) String() string { 435 return string(l) 436 } 437 438 func NilTeamID() TeamID { return TeamID("") } 439 440 func (s Seqno) Eq(s2 Seqno) bool { 441 return s == s2 442 } 443 444 func (s Seqno) String() string { 445 return fmt.Sprintf("%d", s) 446 } 447 448 func 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 459 func UIDFromSlice(b []byte) (UID, error) { 460 return UIDFromString(hex.EncodeToString(b)) 461 } 462 463 // Used by unit tests. 464 func 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 477 func (u UID) String() string { 478 return string(u) 479 } 480 481 func (u UID) ToBytes() []byte { 482 b, err := hex.DecodeString(string(u)) 483 if err != nil { 484 return nil 485 } 486 return b 487 } 488 489 func (u UID) IsNil() bool { 490 return len(u) == 0 491 } 492 493 func (u UID) Exists() bool { 494 return !u.IsNil() 495 } 496 497 func (u UID) Equal(v UID) bool { 498 return u == v 499 } 500 501 func (u UID) NotEqual(v UID) bool { 502 return !u.Equal(v) 503 } 504 505 func (u UID) Less(v UID) bool { 506 return u < v 507 } 508 509 func (u UID) AsUserOrTeam() UserOrTeamID { 510 return UserOrTeamID(u) 511 } 512 513 func 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 527 func 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. 540 func 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. 558 func 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 576 func (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 585 func (t TeamID) IsRootTeam() bool { 586 return !t.IsSubTeam() 587 } 588 589 func (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 598 func (t TeamID) String() string { 599 return string(t) 600 } 601 602 func (t TeamID) ToBytes() []byte { 603 b, err := hex.DecodeString(string(t)) 604 if err != nil { 605 return nil 606 } 607 return b 608 } 609 610 func (t TeamID) IsNil() bool { 611 return len(t) == 0 612 } 613 614 func (t TeamID) Exists() bool { 615 return !t.IsNil() 616 } 617 618 func (t TeamID) Equal(v TeamID) bool { 619 return t == v 620 } 621 622 func (t TeamID) NotEqual(v TeamID) bool { 623 return !t.Equal(v) 624 } 625 626 func (t TeamID) Less(v TeamID) bool { 627 return t < v 628 } 629 630 func (t TeamID) AsUserOrTeam() UserOrTeamID { 631 return UserOrTeamID(t) 632 } 633 634 const ptrSize = 4 << (^uintptr(0) >> 63) // stolen from runtime/internal/sys 635 636 // Size implements the cache.Measurable interface. 637 func (t TeamID) Size() int { 638 return len(t) + ptrSize 639 } 640 641 func (s SigID) IsNil() bool { 642 return len(s) == 0 643 } 644 645 func (s SigID) Exists() bool { 646 return !s.IsNil() 647 } 648 649 func (s SigID) String() string { return string(s) } 650 651 func (s SigID) ToDisplayString(verbose bool) string { 652 if verbose { 653 return string(s) 654 } 655 return fmt.Sprintf("%s...", s[0:SigIDQueryMin]) 656 } 657 658 func (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 674 func 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 689 func (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 697 func (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 705 func (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 714 type 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). 718 func (s SigID) ToMapKey() SigIDMapKey { 719 return SigIDMapKey(s.StripSuffix().ToSigIDLegacy().String()) 720 } 721 722 func (s SigID) ToMediumID() string { 723 return encode(s.ToBytes()) 724 } 725 726 func (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. 732 type SigIDBase string 733 734 func (s SigIDBase) String() string { 735 return string(s) 736 } 737 738 func 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. 745 func (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. 751 func (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 773 func 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 782 func 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 790 func (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. 796 type 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 802 func 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 810 func (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 827 func (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. 833 func (s SigIDBase) ToSigIDLegacy() SigID { 834 return s.ToSigID(SigIDSuffixParameters{IsUserSig: true, IsWalletStellar: false, SigVersion: 1}) 835 } 836 837 func (s SigIDBase) Eq(t SigIDBase) bool { 838 return cieq(string(s), string(t)) 839 } 840 841 func (s SigIDBase) ToBytes() []byte { 842 x, err := hex.DecodeString(string(s)) 843 if err != nil { 844 return nil 845 } 846 return x 847 } 848 849 func encode(b []byte) string { 850 return strings.TrimRight(base64.URLEncoding.EncodeToString(b), "=") 851 } 852 853 func 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 863 func (t Time) Time() time.Time { 864 return FromTime(t) 865 } 866 867 func (t Time) UnixSeconds() int64 { 868 return t.Time().Unix() 869 } 870 871 func 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 879 func ToTimePtr(t *time.Time) *Time { 880 if t == nil { 881 return nil 882 } 883 ret := ToTime(*t) 884 return &ret 885 } 886 887 func TimeFromSeconds(seconds int64) Time { 888 return Time(seconds * 1000) 889 } 890 891 func (t Time) IsZero() bool { return t == 0 } 892 func (t Time) After(t2 Time) bool { return t > t2 } 893 func (t Time) Before(t2 Time) bool { return t < t2 } 894 895 func FormatTime(t Time) string { 896 layout := "2006-01-02 15:04:05 MST" 897 return FromTime(t).Format(layout) 898 } 899 900 func FromUnixTime(u UnixTime) time.Time { 901 if u == 0 { 902 return time.Time{} 903 } 904 return time.Unix(int64(u), 0) 905 } 906 907 func (u UnixTime) Time() time.Time { 908 return FromUnixTime(u) 909 } 910 911 func (u UnixTime) UnixSeconds() int64 { 912 return int64(u) 913 } 914 915 func ToUnixTime(t time.Time) UnixTime { 916 if t.IsZero() { 917 return 0 918 } 919 return UnixTime(t.Unix()) 920 } 921 922 func UnixTimeFromSeconds(seconds int64) UnixTime { 923 return UnixTime(seconds) 924 } 925 926 func (u UnixTime) IsZero() bool { return u == UnixTime(0) } 927 func (u UnixTime) After(u2 UnixTime) bool { return u > u2 } 928 func (u UnixTime) Before(u2 UnixTime) bool { return u < u2 } 929 func FormatUnixTime(u UnixTime) string { 930 layout := "2006-01-02 15:04:05 MST" 931 return FromUnixTime(u).Format(layout) 932 } 933 934 func (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 941 func (s Status) GoError() error { 942 if s.Code == int(StatusCode_SCOk) { 943 return nil 944 } 945 return fmt.Errorf(s.Error()) 946 } 947 948 func (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 962 func (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 978 func (s ServiceStatus) NeedsInstall() bool { 979 return s.InstallAction == InstallAction_INSTALL || 980 s.InstallAction == InstallAction_REINSTALL || 981 s.InstallAction == InstallAction_UPGRADE 982 } 983 984 func (k *KID) UnmarshalJSON(b []byte) error { 985 kid, err := KIDFromStringChecked(Unquote(b)) 986 if err != nil { 987 return err 988 } 989 *k = kid 990 return nil 991 } 992 993 func (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 1002 func (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. 1012 func (u UID) Size() int { 1013 return len(u) + ptrSize 1014 } 1015 1016 func (k *KID) MarshalJSON() ([]byte, error) { 1017 return Quote(k.String()), nil 1018 } 1019 1020 func (u *UID) MarshalJSON() ([]byte, error) { 1021 return Quote(u.String()), nil 1022 } 1023 1024 func (u *UserOrTeamID) MarshalJSON() ([]byte, error) { 1025 return Quote(u.String()), nil 1026 } 1027 1028 // Size implements the keybase/kbfs/cache.Measurable interface. 1029 func (k *KID) Size() int { 1030 if k == nil { 1031 return 0 1032 } 1033 return len(*k) + ptrSize 1034 } 1035 1036 func (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 1045 func (s *SigID) MarshalJSON() ([]byte, error) { 1046 return Quote(s.String()), nil 1047 } 1048 1049 func (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 1062 func (f Folder) String() string { 1063 return f.ToString() 1064 } 1065 1066 func (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 1079 func (f FolderHandle) String() string { 1080 return f.ToString() 1081 } 1082 1083 func (t TrackToken) String() string { 1084 return string(t) 1085 } 1086 1087 func 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 1094 type APIStatus interface { 1095 Status() Status 1096 } 1097 1098 type Error struct { 1099 code StatusCode 1100 message string 1101 } 1102 1103 func NewError(code StatusCode, message string) *Error { 1104 if code == StatusCode_SCOk { 1105 return nil 1106 } 1107 return &Error{code: code, message: message} 1108 } 1109 1110 func FromError(err error) *Error { 1111 return &Error{code: StatusCode_SCGeneric, message: err.Error()} 1112 } 1113 1114 func StatusOK(desc string) Status { 1115 if desc == "" { 1116 desc = "OK" 1117 } 1118 return Status{Code: int(StatusCode_SCOk), Name: "OK", Desc: desc} 1119 } 1120 1121 func StatusFromCode(code StatusCode, message string) Status { 1122 if code == StatusCode_SCOk { 1123 return StatusOK(message) 1124 } 1125 return NewError(code, message).Status() 1126 } 1127 1128 func (e *Error) Error() string { 1129 return e.message 1130 } 1131 1132 func (e *Error) Status() Status { 1133 return Status{Code: int(e.code), Name: "ERROR", Desc: e.message} 1134 } 1135 1136 func (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 1151 func (m MerkleTreeID) Number() int { 1152 return int(m) 1153 } 1154 1155 func (m MerkleTreeID) String() string { 1156 return strconv.Itoa(int(m)) 1157 } 1158 1159 func (r BlockReference) String() string { 1160 return fmt.Sprintf("%s,%s", r.Bid.BlockHash, hex.EncodeToString(r.Nonce[:])) 1161 } 1162 1163 func (r BlockReferenceCount) String() string { 1164 return fmt.Sprintf("%s,%d", r.Ref.String(), r.LiveCount) 1165 } 1166 1167 func (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 1174 func (sa SocialAssertion) TeamInviteType() string { 1175 return string(sa.Service) 1176 } 1177 1178 func (sa SocialAssertion) TeamInviteName() TeamInviteName { 1179 return TeamInviteName(sa.User) 1180 } 1181 1182 func (a GetArg) GetEndpoint() string { 1183 return a.Endpoint 1184 } 1185 1186 func (a GetArg) GetHTTPArgs() []StringKVPair { 1187 return a.Args 1188 } 1189 1190 func (a GetArg) GetHttpStatuses() []int { 1191 return a.HttpStatus 1192 } 1193 1194 func (a GetArg) GetAppStatusCodes() []int { 1195 return a.AppStatusCode 1196 } 1197 1198 func (a GetWithSessionArg) GetEndpoint() string { 1199 return a.Endpoint 1200 } 1201 1202 func (a GetWithSessionArg) GetHTTPArgs() []StringKVPair { 1203 return a.Args 1204 } 1205 1206 func (a GetWithSessionArg) GetHttpStatuses() []int { 1207 return a.HttpStatus 1208 } 1209 1210 func (a GetWithSessionArg) GetAppStatusCodes() []int { 1211 return a.AppStatusCode 1212 } 1213 1214 func (a PostArg) GetEndpoint() string { 1215 return a.Endpoint 1216 } 1217 1218 func (a PostArg) GetHTTPArgs() []StringKVPair { 1219 return a.Args 1220 } 1221 1222 func (a PostArg) GetHttpStatuses() []int { 1223 return a.HttpStatus 1224 } 1225 1226 func (a PostArg) GetAppStatusCodes() []int { 1227 return a.AppStatusCode 1228 } 1229 1230 func (a PostJSONArg) GetEndpoint() string { 1231 return a.Endpoint 1232 } 1233 1234 func (a PostJSONArg) GetHTTPArgs() []StringKVPair { 1235 return a.Args 1236 } 1237 1238 func (a PostJSONArg) GetHttpStatuses() []int { 1239 return a.HttpStatus 1240 } 1241 1242 func (a PostJSONArg) GetAppStatusCodes() []int { 1243 return a.AppStatusCode 1244 } 1245 1246 func (a DeleteArg) GetEndpoint() string { 1247 return a.Endpoint 1248 } 1249 1250 func (a DeleteArg) GetHTTPArgs() []StringKVPair { 1251 return a.Args 1252 } 1253 1254 func (a DeleteArg) GetHttpStatuses() []int { 1255 return a.HttpStatus 1256 } 1257 1258 func (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. 1264 type 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. 1273 func 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. 1300 var _ 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. 1308 type 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. 1314 func (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. 1320 func (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. 1342 var _ error = Status{} 1343 1344 // Assert that our ErrorUnwrapper fits the RPC error unwrapper spec. 1345 var _ rpc.ErrorUnwrapper = ErrorUnwrapper{} 1346 1347 func (t TLFID) String() string { 1348 return string(t) 1349 } 1350 1351 func (t TLFID) IsNil() bool { 1352 return len(t) == 0 1353 } 1354 1355 func (t TLFID) Exists() bool { 1356 return !t.IsNil() 1357 } 1358 1359 func (t TLFID) ToBytes() []byte { 1360 b, err := hex.DecodeString(string(t)) 1361 if err != nil { 1362 return nil 1363 } 1364 return b 1365 } 1366 1367 func (t TLFID) Eq(u TLFID) bool { 1368 return t == u 1369 } 1370 1371 func (b TLFIdentifyBehavior) UnblockThenForceIDTable() bool { 1372 switch b { 1373 case TLFIdentifyBehavior_GUI_PROFILE: 1374 return true 1375 default: 1376 return false 1377 } 1378 } 1379 1380 func (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 1393 func (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 1407 func (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 1420 func (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 1431 func (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 1443 func (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 1453 func (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. 1464 func (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. 1488 func (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). 1501 func (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 1510 func (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 1534 func (c CanonicalTlfName) String() string { 1535 return string(c) 1536 } 1537 1538 func (u UserPlusKeys) GetUID() UID { 1539 return u.Uid 1540 } 1541 1542 func (u UserPlusKeys) GetName() string { 1543 return u.Username 1544 } 1545 1546 func (u UserPlusKeys) GetStatus() StatusCode { 1547 return u.Status 1548 } 1549 1550 func (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 1558 func (u UserPlusAllKeys) GetUID() UID { 1559 return u.Base.GetUID() 1560 } 1561 1562 func (u UserPlusAllKeys) GetName() string { 1563 return u.Base.GetName() 1564 } 1565 1566 func (u UserPlusAllKeys) GetStatus() StatusCode { 1567 return u.Base.GetStatus() 1568 } 1569 1570 func (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 1579 func (u UserPlusAllKeys) Export() *User { 1580 return &User{Uid: u.GetUID(), Username: u.GetName()} 1581 } 1582 1583 func (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 1596 func ToDurationMsec(d time.Duration) DurationMsec { 1597 return DurationMsec(d / time.Millisecond) 1598 } 1599 1600 func (d DurationMsec) Duration() time.Duration { 1601 return time.Duration(d) * time.Millisecond 1602 } 1603 1604 func ToDurationSec(d time.Duration) DurationSec { 1605 return DurationSec(d / time.Second) 1606 } 1607 1608 func (d DurationSec) Duration() time.Duration { 1609 return time.Duration(d) * time.Second 1610 } 1611 1612 func (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 1621 func (u UserPlusKeysV2) GetUID() UID { 1622 return u.Uid 1623 } 1624 1625 func (u UserPlusKeysV2) GetName() string { 1626 return u.Username 1627 } 1628 1629 func (u UserPlusKeysV2) GetStatus() StatusCode { 1630 return u.Status 1631 } 1632 1633 func (u UserPlusKeysV2AllIncarnations) ExportToSimpleUser() User { 1634 return User{Uid: u.GetUID(), Username: u.GetName()} 1635 } 1636 1637 func (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 1646 func (u UserPlusKeysV2AllIncarnations) GetUID() UID { 1647 return u.Current.GetUID() 1648 } 1649 1650 func (u UserPlusKeysV2AllIncarnations) GetName() string { 1651 return u.Current.GetName() 1652 } 1653 1654 func (u UserPlusKeysV2AllIncarnations) GetStatus() StatusCode { 1655 return u.Current.GetStatus() 1656 } 1657 1658 func (u UserPlusKeysV2AllIncarnations) AllIncarnations() (ret []UserPlusKeysV2) { 1659 ret = append(ret, u.Current) 1660 ret = append(ret, u.PastIncarnations...) 1661 return ret 1662 } 1663 1664 func (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. 1676 func (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. 1693 func (u UserPlusKeysV2AllIncarnations) HasKID(kid KID) bool { 1694 incarnation, _ := u.FindKID(kid) 1695 return (incarnation != nil) 1696 } 1697 1698 func (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 1707 func (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 1716 func (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 1724 func (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 1733 func (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 1741 func (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 1749 func (s ChatConversationID) String() string { 1750 return hex.EncodeToString(s) 1751 } 1752 1753 func (s ChatConversationID) Bytes() []byte { 1754 return s 1755 } 1756 1757 // IsOlderThan returns true if any of the versions of u are older than v 1758 func (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 1769 func (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 1782 func (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 1801 func (ut UserOrTeamID) String() string { 1802 return string(ut) 1803 } 1804 1805 func (ut UserOrTeamID) ToBytes() []byte { 1806 b, err := hex.DecodeString(string(ut)) 1807 if err != nil { 1808 return nil 1809 } 1810 return b 1811 } 1812 1813 func (ut UserOrTeamID) IsNil() bool { 1814 return len(ut) == 0 1815 } 1816 1817 func (ut UserOrTeamID) Exists() bool { 1818 return !ut.IsNil() 1819 } 1820 1821 func (ut UserOrTeamID) Equal(v UserOrTeamID) bool { 1822 return ut == v 1823 } 1824 1825 func (ut UserOrTeamID) NotEqual(v UserOrTeamID) bool { 1826 return !ut.Equal(v) 1827 } 1828 1829 func (ut UserOrTeamID) Less(v UserOrTeamID) bool { 1830 return ut < v 1831 } 1832 1833 func (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 1840 func (ut UserOrTeamID) AsUserOrBust() UID { 1841 uid, err := ut.AsUser() 1842 if err != nil { 1843 panic(err) 1844 } 1845 return uid 1846 } 1847 1848 func (ut UserOrTeamID) IsPublic() bool { 1849 if ut.IsUser() { 1850 return true 1851 } 1852 return ut.AsTeamOrBust().IsPublic() 1853 } 1854 1855 func (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 1862 func (ut UserOrTeamID) AsTeamOrBust() TeamID { 1863 tid, err := ut.AsTeam() 1864 if err != nil { 1865 panic(err) 1866 } 1867 return tid 1868 } 1869 1870 func (ut UserOrTeamID) Compare(ut2 UserOrTeamID) int { 1871 return strings.Compare(string(ut), string(ut2)) 1872 } 1873 1874 func (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 1883 func (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 1892 func (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 1901 func (ut UserOrTeamID) IsTeamOrSubteam() bool { 1902 return ut.IsTeam() || ut.IsSubteam() 1903 } 1904 1905 func (ut UserOrTeamID) IsValidID() bool { 1906 return ut.IsUser() || ut.IsTeamOrSubteam() 1907 } 1908 1909 // Preconditions: 1910 // 1911 // -first four bits (in Little Endian) of UserOrTeamID are 1912 // independent and uniformly distributed 1913 // -UserOrTeamID must have an even number of bits, or this will always 1914 // return 0 1915 // 1916 // Returns a number in [0, shardCount) which can be treated as roughly 1917 // uniformly distributed. Used for things that need to shard by user. 1918 func (ut UserOrTeamID) GetShard(shardCount int) (int, error) { 1919 if !ut.IsValidID() { 1920 return 0, fmt.Errorf("Bad ID, does not match any known valid type") 1921 } 1922 bytes, err := hex.DecodeString(string(ut)) 1923 if err != nil { 1924 return 0, err 1925 } 1926 // LittleEndian.Uint32 truncates to obtain 4 bytes from the buffer 1927 n := binary.LittleEndian.Uint32(bytes) 1928 return int(n % uint32(shardCount)), nil 1929 } 1930 1931 // Size implements the cache.Measurable interface. 1932 func (ut UserOrTeamID) Size() int { 1933 return len(ut) + ptrSize 1934 } 1935 1936 func (m *MaskB64) UnmarshalJSON(b []byte) error { 1937 unquoted := UnquoteBytes(b) 1938 if len(unquoted) == 0 { 1939 return nil 1940 } 1941 dbuf := make([]byte, base64.StdEncoding.DecodedLen(len(unquoted))) 1942 n, err := base64.StdEncoding.Decode(dbuf, unquoted) 1943 if err != nil { 1944 return err 1945 } 1946 *m = MaskB64(dbuf[:n]) 1947 return nil 1948 } 1949 1950 func (m *MaskB64) MarshalJSON() ([]byte, error) { 1951 s := Quote(base64.StdEncoding.EncodeToString([]byte(*m))) 1952 return s, nil 1953 } 1954 1955 func PublicKeyV1FromPGPKeyV2(keyV2 PublicKeyV2PGPSummary) PublicKey { 1956 return PublicKey{ 1957 KID: keyV2.Base.Kid, 1958 PGPFingerprint: hex.EncodeToString(keyV2.Fingerprint[:]), 1959 PGPIdentities: keyV2.Identities, 1960 IsSibkey: keyV2.Base.IsSibkey, 1961 IsEldest: keyV2.Base.IsEldest, 1962 CTime: keyV2.Base.CTime, 1963 ETime: keyV2.Base.ETime, 1964 IsRevoked: (keyV2.Base.Revocation != nil), 1965 } 1966 } 1967 1968 func PublicKeyV1FromDeviceKeyV2(keyV2 PublicKeyV2NaCl) PublicKey { 1969 parentID := "" 1970 if keyV2.Parent != nil { 1971 parentID = string(*keyV2.Parent) 1972 } 1973 return PublicKey{ 1974 KID: keyV2.Base.Kid, 1975 IsSibkey: keyV2.Base.IsSibkey, 1976 IsEldest: keyV2.Base.IsEldest, 1977 ParentID: parentID, 1978 DeviceID: keyV2.DeviceID, 1979 DeviceDescription: keyV2.DeviceDescription, 1980 DeviceType: keyV2.DeviceType, 1981 CTime: keyV2.Base.CTime, 1982 ETime: keyV2.Base.ETime, 1983 IsRevoked: (keyV2.Base.Revocation != nil), 1984 } 1985 } 1986 1987 const ( 1988 DeviceTypeV2_NONE DeviceTypeV2 = "none" 1989 DeviceTypeV2_PAPER DeviceTypeV2 = "backup" 1990 DeviceTypeV2_DESKTOP DeviceTypeV2 = "desktop" 1991 DeviceTypeV2_MOBILE DeviceTypeV2 = "mobile" 1992 ) 1993 1994 func (d DeviceTypeV2) String() string { 1995 return string(d) 1996 } 1997 1998 func StringToDeviceTypeV2(s string) (d DeviceTypeV2, err error) { 1999 deviceType := DeviceTypeV2(s) 2000 switch deviceType { 2001 case DeviceTypeV2_NONE, DeviceTypeV2_DESKTOP, DeviceTypeV2_MOBILE, DeviceTypeV2_PAPER: 2002 // pass 2003 default: 2004 return DeviceTypeV2_NONE, fmt.Errorf("Unknown DeviceType: %s", deviceType) 2005 } 2006 return deviceType, nil 2007 } 2008 2009 // defaults to Desktop 2010 func (dt *DeviceTypeV2) ToDeviceType() DeviceType { 2011 if *dt == DeviceTypeV2_MOBILE { 2012 return DeviceType_MOBILE 2013 } 2014 return DeviceType_DESKTOP 2015 } 2016 2017 func RevokedKeyV1FromDeviceKeyV2(keyV2 PublicKeyV2NaCl) RevokedKey { 2018 return RevokedKey{ 2019 Key: PublicKeyV1FromDeviceKeyV2(keyV2), 2020 Time: KeybaseTime{ 2021 Unix: keyV2.Base.Revocation.Time, 2022 Chain: keyV2.Base.Revocation.PrevMerkleRootSigned.Seqno, 2023 }, 2024 By: keyV2.Base.Revocation.SigningKID, 2025 } 2026 } 2027 2028 // UPKV2 should supersede UPAK eventually, but lots of older code requires 2029 // UPAK. This is a simple converter function. 2030 func UPAKFromUPKV2AI(uV2 UserPlusKeysV2AllIncarnations) UserPlusAllKeys { 2031 // Convert the PGP keys. 2032 var pgpKeysV1 []PublicKey 2033 for _, keyV2 := range uV2.Current.PGPKeys { 2034 pgpKeysV1 = append(pgpKeysV1, PublicKeyV1FromPGPKeyV2(keyV2)) 2035 } 2036 2037 // Convert the device keys. 2038 var deviceKeysV1 []PublicKey 2039 var revokedDeviceKeysV1 []RevokedKey 2040 var resets []ResetSummary 2041 for _, keyV2 := range uV2.Current.DeviceKeys { 2042 if keyV2.Base.Revocation != nil { 2043 revokedDeviceKeysV1 = append(revokedDeviceKeysV1, RevokedKeyV1FromDeviceKeyV2(keyV2)) 2044 } else { 2045 deviceKeysV1 = append(deviceKeysV1, PublicKeyV1FromDeviceKeyV2(keyV2)) 2046 } 2047 } 2048 sort.Slice(deviceKeysV1, func(i, j int) bool { return deviceKeysV1[i].KID < deviceKeysV1[j].KID }) 2049 sort.Slice(revokedDeviceKeysV1, func(i, j int) bool { return revokedDeviceKeysV1[i].Key.KID < revokedDeviceKeysV1[j].Key.KID }) 2050 2051 // Assemble the deleted device keys from past incarnations. 2052 var deletedDeviceKeysV1 []PublicKey 2053 for _, incarnation := range uV2.PastIncarnations { 2054 for _, keyV2 := range incarnation.DeviceKeys { 2055 deletedDeviceKeysV1 = append(deletedDeviceKeysV1, PublicKeyV1FromDeviceKeyV2(keyV2)) 2056 } 2057 if reset := incarnation.Reset; reset != nil { 2058 resets = append(resets, *reset) 2059 } 2060 } 2061 sort.Slice(deletedDeviceKeysV1, func(i, j int) bool { return deletedDeviceKeysV1[i].KID < deletedDeviceKeysV1[j].KID }) 2062 2063 // List and sort the remote tracks. Note that they *must* be sorted. 2064 var remoteTracks []RemoteTrack 2065 for _, track := range uV2.Current.RemoteTracks { 2066 remoteTracks = append(remoteTracks, track) 2067 } 2068 sort.Slice(remoteTracks, func(i, j int) bool { return remoteTracks[i].Username < remoteTracks[j].Username }) 2069 2070 // Apart from all the key mangling above, everything else is just naming 2071 // and layout changes. Assemble the final UPAK. 2072 return UserPlusAllKeys{ 2073 Base: UserPlusKeys{ 2074 Uid: uV2.Current.Uid, 2075 Username: uV2.Current.Username, 2076 EldestSeqno: uV2.Current.EldestSeqno, 2077 Status: uV2.Current.Status, 2078 DeviceKeys: deviceKeysV1, 2079 RevokedDeviceKeys: revokedDeviceKeysV1, 2080 DeletedDeviceKeys: deletedDeviceKeysV1, 2081 PGPKeyCount: len(pgpKeysV1), 2082 Uvv: uV2.Uvv, 2083 PerUserKeys: uV2.Current.PerUserKeys, 2084 Resets: resets, 2085 }, 2086 PGPKeys: pgpKeysV1, 2087 RemoteTracks: remoteTracks, 2088 } 2089 } 2090 2091 func (u UserVersionPercentForm) String() string { 2092 return string(u) 2093 } 2094 2095 func NewUserVersion(uid UID, eldestSeqno Seqno) UserVersion { 2096 return UserVersion{ 2097 Uid: uid, 2098 EldestSeqno: eldestSeqno, 2099 } 2100 } 2101 2102 func (u UserVersion) PercentForm() UserVersionPercentForm { 2103 return UserVersionPercentForm(u.String()) 2104 } 2105 2106 func (u UserVersion) String() string { 2107 return fmt.Sprintf("%s%%%d", u.Uid, u.EldestSeqno) 2108 } 2109 2110 func (u UserVersion) Eq(v UserVersion) bool { 2111 return u.Uid.Equal(v.Uid) && u.EldestSeqno.Eq(v.EldestSeqno) 2112 } 2113 2114 func (u UserVersion) TeamInviteName() TeamInviteName { 2115 return TeamInviteName(u.PercentForm()) 2116 } 2117 2118 func (u UserVersion) IsNil() bool { 2119 return u.Uid.IsNil() 2120 } 2121 2122 type ByUserVersionID []UserVersion 2123 2124 func (b ByUserVersionID) Len() int { return len(b) } 2125 func (b ByUserVersionID) Swap(i, j int) { b[i], b[j] = b[j], b[i] } 2126 func (b ByUserVersionID) Less(i, j int) bool { 2127 return b[i].String() < b[j].String() 2128 } 2129 2130 func (k CryptKey) Material() Bytes32 { 2131 return k.Key 2132 } 2133 2134 func (k CryptKey) Generation() int { 2135 return k.KeyGeneration 2136 } 2137 2138 func (k TeamApplicationKey) Material() Bytes32 { 2139 return k.Key 2140 } 2141 2142 func (k TeamApplicationKey) Generation() int { 2143 return int(k.KeyGeneration) 2144 } 2145 2146 func (t TeamMembers) AllUIDs() []UID { 2147 m := make(map[UID]bool) 2148 for _, u := range t.Owners { 2149 m[u.Uid] = true 2150 } 2151 for _, u := range t.Admins { 2152 m[u.Uid] = true 2153 } 2154 for _, u := range t.Writers { 2155 m[u.Uid] = true 2156 } 2157 for _, u := range t.Readers { 2158 m[u.Uid] = true 2159 } 2160 for _, u := range t.Bots { 2161 m[u.Uid] = true 2162 } 2163 for _, u := range t.RestrictedBots { 2164 m[u.Uid] = true 2165 } 2166 var all []UID 2167 for u := range m { 2168 all = append(all, u) 2169 } 2170 return all 2171 } 2172 2173 func (t TeamMembers) AllUserVersions() []UserVersion { 2174 m := make(map[UID]UserVersion) 2175 for _, u := range t.Owners { 2176 m[u.Uid] = u 2177 } 2178 for _, u := range t.Admins { 2179 m[u.Uid] = u 2180 } 2181 for _, u := range t.Writers { 2182 m[u.Uid] = u 2183 } 2184 for _, u := range t.Readers { 2185 m[u.Uid] = u 2186 } 2187 for _, u := range t.Bots { 2188 m[u.Uid] = u 2189 } 2190 for _, u := range t.RestrictedBots { 2191 m[u.Uid] = u 2192 } 2193 var all []UserVersion 2194 for _, uv := range m { 2195 all = append(all, uv) 2196 } 2197 return all 2198 } 2199 2200 func (s TeamMemberStatus) IsActive() bool { 2201 return s == TeamMemberStatus_ACTIVE 2202 } 2203 2204 func (s TeamMemberStatus) IsReset() bool { 2205 return s == TeamMemberStatus_RESET 2206 } 2207 2208 func (s TeamMemberStatus) IsDeleted() bool { 2209 return s == TeamMemberStatus_DELETED 2210 } 2211 2212 func FilterInactiveReadersWriters(arg []TeamMemberDetails) (ret []TeamMemberDetails) { 2213 for _, v := range arg { 2214 if v.Status.IsActive() || (v.Role != TeamRole_READER && v.Role != TeamRole_WRITER) { 2215 ret = append(ret, v) 2216 } 2217 } 2218 return ret 2219 } 2220 2221 func (t TeamName) IsNil() bool { 2222 return len(t.Parts) == 0 2223 } 2224 2225 // underscores allowed, just not first or doubled 2226 var namePartRxx = regexp.MustCompile(`^([a-zA-Z0-9][a-zA-Z0-9_]?)+$`) 2227 var implicitRxxString = fmt.Sprintf("^%s[0-9a-f]{%d}$", ImplicitTeamPrefix, ImplicitSuffixLengthBytes*2) 2228 var implicitNameRxx = regexp.MustCompile(implicitRxxString) 2229 2230 const ImplicitTeamPrefix = "__keybase_implicit_team__" 2231 const ImplicitSuffixLengthBytes = 16 2232 2233 func stringToTeamNamePart(s string) TeamNamePart { 2234 return TeamNamePart(strings.ToLower(s)) 2235 } 2236 2237 func rootTeamNameFromString(s string) (TeamName, error) { 2238 if implicitNameRxx.MatchString(s) { 2239 return TeamName{Parts: []TeamNamePart{stringToTeamNamePart(s)}}, nil 2240 } 2241 if err := validatePart(s); err != nil { 2242 return TeamName{}, err 2243 } 2244 return TeamName{Parts: []TeamNamePart{stringToTeamNamePart(s)}}, nil 2245 } 2246 2247 func validatePart(s string) (err error) { 2248 if len(s) == 0 { 2249 return errors.New("team names cannot be empty") 2250 } 2251 if !(len(s) >= 2 && len(s) <= 16) { 2252 return errors.New("team names must be between 2 and 16 characters long") 2253 } 2254 if !namePartRxx.MatchString(s) { 2255 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.") 2256 } 2257 return nil 2258 } 2259 2260 func TeamNameFromString(s string) (TeamName, error) { 2261 ret := TeamName{} 2262 2263 s = strings.ToLower(s) 2264 parts := strings.Split(s, ".") 2265 if len(parts) == 0 { 2266 return ret, errors.New("team names cannot be empty") 2267 } 2268 if len(parts) == 1 { 2269 return rootTeamNameFromString(s) 2270 } 2271 tmp := make([]TeamNamePart, len(parts)) 2272 for i, part := range parts { 2273 err := validatePart(part) 2274 if err != nil { 2275 return TeamName{}, fmt.Errorf("Could not parse name as team; bad name component %q: %s", part, err.Error()) 2276 } 2277 tmp[i] = stringToTeamNamePart(part) 2278 } 2279 return TeamName{Parts: tmp}, nil 2280 } 2281 2282 func (p TeamNamePart) String() string { 2283 return string(p) 2284 } 2285 2286 func (t TeamName) AssertEqString(s string) error { 2287 tmp, err := TeamNameFromString(s) 2288 if err != nil { 2289 return err 2290 } 2291 if !t.Eq(tmp) { 2292 return fmt.Errorf("Team equality check failed: %s != %s", t.String(), s) 2293 } 2294 return nil 2295 } 2296 2297 func (t TeamName) String() string { 2298 tmp := make([]string, len(t.Parts)) 2299 for i, p := range t.Parts { 2300 tmp[i] = strings.ToLower(string(p)) 2301 } 2302 return strings.Join(tmp, ".") 2303 } 2304 2305 func (t TeamName) Eq(t2 TeamName) bool { 2306 return t.String() == t2.String() 2307 } 2308 2309 func (t TeamName) IsRootTeam() bool { 2310 return len(t.Parts) == 1 2311 } 2312 2313 func (t TeamName) ToPrivateTeamID() TeamID { 2314 return t.ToTeamID(false) 2315 } 2316 2317 func (t TeamName) ToPublicTeamID() TeamID { 2318 return t.ToTeamID(true) 2319 } 2320 2321 // Get the top level team id for this team name. 2322 // Only makes sense for non-sub teams. 2323 // The first 15 bytes of the sha256 of the lowercase team name, 2324 // followed by the byte 0x24, encoded as hex. 2325 func (t TeamName) ToTeamID(public bool) TeamID { 2326 low := strings.ToLower(t.String()) 2327 sum := sha256.Sum256([]byte(low)) 2328 var useSuffix byte = TEAMID_PRIVATE_SUFFIX 2329 if public { 2330 useSuffix = TEAMID_PUBLIC_SUFFIX 2331 } 2332 bs := append(sum[:15], useSuffix) 2333 res, err := TeamIDFromString(hex.EncodeToString(bs)) 2334 if err != nil { 2335 panic(err) 2336 } 2337 return res 2338 } 2339 2340 // Return a new team name with the part added to the end. 2341 // For example {foo.bar}.Append(baz) -> {foo.bar.baz} 2342 func (t TeamName) Append(part string) (t3 TeamName, err error) { 2343 t2 := t.DeepCopy() 2344 t2.Parts = append(t2.Parts, TeamNamePart(part)) 2345 t3, err = TeamNameFromString(t2.String()) 2346 return t3, err 2347 } 2348 2349 func (t TeamName) LastPart() TeamNamePart { 2350 return t.Parts[len(t.Parts)-1] 2351 } 2352 2353 func (t TeamName) RootAncestorName() TeamName { 2354 if len(t.Parts) == 0 { 2355 // this should never happen 2356 return TeamName{} 2357 } 2358 return TeamName{ 2359 Parts: t.Parts[:1], 2360 } 2361 } 2362 2363 func (t TeamName) RootID() TeamID { 2364 return t.RootAncestorName().ToTeamID(false) 2365 } 2366 2367 func (t TeamName) Parent() (TeamName, error) { 2368 if len(t.Parts) == 0 { 2369 return t, fmt.Errorf("empty team name") 2370 } 2371 if t.IsRootTeam() { 2372 return t, fmt.Errorf("root team has no parent") 2373 } 2374 return TeamName{ 2375 Parts: t.Parts[:len(t.Parts)-1], 2376 }, nil 2377 } 2378 2379 func (t TeamName) SwapLastPart(newLast string) (TeamName, error) { 2380 parent, err := t.Parent() 2381 if err != nil { 2382 return t, err 2383 } 2384 return parent.Append(newLast) 2385 } 2386 2387 func (t TeamName) IsImplicit() bool { 2388 return strings.HasPrefix(t.String(), ImplicitTeamPrefix) 2389 } 2390 2391 // The number of parts in a team name. 2392 // Root teams have 1. 2393 func (t TeamName) Depth() int { 2394 return len(t.Parts) 2395 } 2396 2397 func (t TeamName) IsAncestorOf(other TeamName) bool { 2398 depth := t.Depth() 2399 if depth >= other.Depth() { 2400 return false 2401 } 2402 2403 for i := 0; i < depth; i++ { 2404 if !other.Parts[i].Eq(t.Parts[i]) { 2405 return false 2406 } 2407 } 2408 2409 return true 2410 } 2411 2412 func (t TeamNamePart) Eq(t2 TeamNamePart) bool { 2413 return string(t) == string(t2) 2414 } 2415 2416 func (u UserPlusKeys) ToUserVersion() UserVersion { 2417 return UserVersion{ 2418 Uid: u.Uid, 2419 EldestSeqno: u.EldestSeqno, 2420 } 2421 } 2422 2423 func (u UserPlusKeysV2) ToUserVersion() UserVersion { 2424 return UserVersion{ 2425 Uid: u.Uid, 2426 EldestSeqno: u.EldestSeqno, 2427 } 2428 } 2429 2430 func (u UserPlusKeysV2AllIncarnations) ToUserVersion() UserVersion { 2431 return u.Current.ToUserVersion() 2432 } 2433 2434 func (u UserPlusKeysV2AllIncarnations) GetPerUserKeyAtSeqno(uv UserVersion, seqno Seqno, merkleSeqno Seqno) (*PerUserKey, error) { 2435 incarnations := u.AllIncarnations() 2436 for _, incarnation := range incarnations { 2437 if incarnation.EldestSeqno == uv.EldestSeqno { 2438 if incarnation.Reset != nil && incarnation.Reset.MerkleRoot.Seqno <= merkleSeqno { 2439 return nil, nil 2440 } 2441 if len(incarnation.PerUserKeys) == 0 { 2442 return nil, nil 2443 } 2444 for i := range incarnation.PerUserKeys { 2445 perUserKey := incarnation.PerUserKeys[len(incarnation.PerUserKeys)-1-i] 2446 if perUserKey.Seqno <= seqno { 2447 return &perUserKey, nil 2448 } 2449 } 2450 return nil, fmt.Errorf("didn't find per user key at seqno %d for uv %v", seqno, uv) 2451 } 2452 } 2453 return nil, fmt.Errorf("didn't find uv %v in upak", uv) 2454 } 2455 2456 // Can return nil. 2457 func (u UserPlusKeysV2) GetLatestPerUserKey() *PerUserKey { 2458 if len(u.PerUserKeys) > 0 { 2459 return &u.PerUserKeys[len(u.PerUserKeys)-1] 2460 } 2461 return nil 2462 } 2463 2464 // Can return nil. 2465 func (u UserPlusKeysV2) GetPerUserKeyByGen(gen PerUserKeyGeneration) *PerUserKey { 2466 genint := int(gen) 2467 if genint <= 0 || genint > len(u.PerUserKeys) { 2468 return nil 2469 } 2470 puk := u.PerUserKeys[genint-1] 2471 if puk.Gen != genint { 2472 // The PerUserKeys field of this object is malformed 2473 return nil 2474 } 2475 return &puk 2476 } 2477 2478 func (s PerTeamKeySeed) ToBytes() []byte { return s[:] } 2479 2480 func (s PerTeamKeySeed) IsZero() bool { 2481 var tmp PerTeamKeySeed 2482 return hmac.Equal(s[:], tmp[:]) 2483 } 2484 2485 func PerTeamKeySeedFromBytes(b []byte) (PerTeamKeySeed, error) { 2486 var ret PerTeamKeySeed 2487 if len(b) != len(ret) { 2488 return PerTeamKeySeed{}, fmt.Errorf("decrypt yielded a bad-sized team secret: %d != %d", len(b), len(ret)) 2489 } 2490 copy(ret[:], b) 2491 return ret, nil 2492 } 2493 2494 func (s SigChainLocation) Eq(s2 SigChainLocation) bool { 2495 return s.Seqno == s2.Seqno && s.SeqType == s2.SeqType 2496 } 2497 2498 func (s SigChainLocation) LessThanOrEqualTo(s2 SigChainLocation) bool { 2499 return s.SeqType == s2.SeqType && s.Seqno <= s2.Seqno 2500 } 2501 2502 func (s SigChainLocation) Comparable(s2 SigChainLocation) error { 2503 if s.SeqType != s2.SeqType { 2504 return fmt.Errorf("mismatched seqtypes: %v != %v", s.SeqType, s2.SeqType) 2505 } 2506 return nil 2507 } 2508 2509 func (s SigChainLocation) Sub1() SigChainLocation { 2510 return SigChainLocation{ 2511 Seqno: s.Seqno - 1, 2512 SeqType: s.SeqType, 2513 } 2514 } 2515 2516 func (r TeamRole) IsAdminOrAbove() bool { 2517 return r.IsOrAbove(TeamRole_ADMIN) 2518 } 2519 2520 func (r TeamRole) IsWriterOrAbove() bool { 2521 return r.IsOrAbove(TeamRole_WRITER) 2522 } 2523 2524 func (r TeamRole) IsReaderOrAbove() bool { 2525 return r.IsOrAbove(TeamRole_READER) 2526 } 2527 2528 func (r TeamRole) IsBotOrAbove() bool { 2529 return r.IsOrAbove(TeamRole_BOT) 2530 } 2531 2532 func (r TeamRole) IsRestrictedBotOrAbove() bool { 2533 return r.IsOrAbove(TeamRole_RESTRICTEDBOT) 2534 } 2535 2536 func (r TeamRole) IsBotLike() bool { 2537 switch r { 2538 case TeamRole_BOT, TeamRole_RESTRICTEDBOT: 2539 return true 2540 } 2541 return false 2542 } 2543 2544 func (r TeamRole) IsRestrictedBot() bool { 2545 return r == TeamRole_RESTRICTEDBOT 2546 } 2547 2548 func (r TeamRole) teamRoleForOrderingOnly() int { 2549 switch r { 2550 case TeamRole_NONE: 2551 return 0 2552 case TeamRole_RESTRICTEDBOT: 2553 return 1 2554 case TeamRole_BOT: 2555 return 2 2556 case TeamRole_READER, 2557 TeamRole_WRITER, 2558 TeamRole_ADMIN, 2559 TeamRole_OWNER: 2560 return int(r) + 2 2561 default: 2562 return 0 2563 } 2564 } 2565 2566 func (r TeamRole) IsOrAbove(min TeamRole) bool { 2567 return r.teamRoleForOrderingOnly() >= min.teamRoleForOrderingOnly() 2568 } 2569 2570 func (r TeamRole) HumanString() string { 2571 if r.IsRestrictedBot() { 2572 return "restricted bot" 2573 } 2574 return strings.ToLower(r.String()) 2575 } 2576 2577 type idSchema struct { 2578 length int 2579 magicSuffixes map[byte]bool 2580 typeHint string 2581 } 2582 2583 func (i idSchema) check(s string) error { 2584 xs, err := hex.DecodeString(s) 2585 if err != nil { 2586 return err 2587 } 2588 if len(xs) != i.length { 2589 return fmt.Errorf("%s: Wrong ID length (got %d)", i.typeHint, len(xs)) 2590 } 2591 suffix := xs[len(xs)-1] 2592 if !i.magicSuffixes[suffix] { 2593 return fmt.Errorf("%s: Incorrect suffix byte (got 0x%x)", i.typeHint, suffix) 2594 } 2595 return nil 2596 } 2597 2598 func TeamInviteIDFromString(s string) (TeamInviteID, error) { 2599 if err := (idSchema{16, map[byte]bool{0x27: true}, "team invite ID"}).check(s); err != nil { 2600 return TeamInviteID(""), err 2601 } 2602 return TeamInviteID(s), nil 2603 } 2604 2605 func (i TeamInviteID) Eq(i2 TeamInviteID) bool { 2606 return string(i) == string(i2) 2607 } 2608 2609 func (t TeamInviteType) String() (string, error) { 2610 c, err := t.C() 2611 if err != nil { 2612 return "", err 2613 } 2614 switch c { 2615 case TeamInviteCategory_KEYBASE: 2616 return "keybase", nil 2617 case TeamInviteCategory_EMAIL: 2618 return "email", nil 2619 case TeamInviteCategory_PHONE: 2620 return "phone", nil 2621 case TeamInviteCategory_SBS: 2622 return string(t.Sbs()), nil 2623 case TeamInviteCategory_SEITAN: 2624 return "seitan_invite_token", nil 2625 case TeamInviteCategory_UNKNOWN: 2626 return t.Unknown(), nil 2627 } 2628 2629 return "", nil 2630 } 2631 2632 func (a TeamInviteType) Eq(b TeamInviteType) bool { 2633 ac, err := a.C() 2634 if err != nil { 2635 return false 2636 } 2637 bc, err := b.C() 2638 if err != nil { 2639 return false 2640 } 2641 if ac != bc { 2642 return false 2643 } 2644 2645 switch ac { 2646 case TeamInviteCategory_KEYBASE: 2647 return true 2648 case TeamInviteCategory_EMAIL, TeamInviteCategory_PHONE: 2649 return true 2650 case TeamInviteCategory_SBS: 2651 return a.Sbs() == b.Sbs() 2652 case TeamInviteCategory_UNKNOWN: 2653 return a.Unknown() == b.Unknown() 2654 } 2655 2656 return false 2657 } 2658 2659 func (t TeamInvite) KeybaseUserVersion() (UserVersion, error) { 2660 category, err := t.Type.C() 2661 if err != nil { 2662 return UserVersion{}, err 2663 } 2664 if category != TeamInviteCategory_KEYBASE { 2665 return UserVersion{}, errors.New("KeybaseUserVersion: invalid invite category, must be keybase") 2666 } 2667 2668 return ParseUserVersion(UserVersionPercentForm(t.Name)) 2669 } 2670 2671 // TeamMaxUsesInfinite is a value for max_uses field which makes team invite 2672 // multiple use, with infinite number of uses. 2673 const TeamMaxUsesInfinite = TeamInviteMaxUses(-1) 2674 2675 func NewTeamInviteFiniteUses(maxUses int) (v TeamInviteMaxUses, err error) { 2676 if maxUses <= 0 { 2677 return v, errors.New("non-infinite uses with nonpositive maxUses") 2678 } 2679 return TeamInviteMaxUses(maxUses), nil 2680 } 2681 2682 func (e *TeamInviteMaxUses) IsNotNilAndValid() bool { 2683 return e != nil && (*e > 0 || *e == TeamMaxUsesInfinite) 2684 } 2685 2686 func max(a, b int) int { 2687 if a >= b { 2688 return a 2689 } 2690 return b 2691 } 2692 2693 func (ti TeamInvite) UsesLeftString(alreadyUsed int) string { 2694 if ti.IsInfiniteUses() { 2695 return "unlimited uses left" 2696 } 2697 var maxUses int 2698 if ti.MaxUses == nil { 2699 maxUses = 1 2700 } else { 2701 maxUses = int(*ti.MaxUses) 2702 } 2703 return formatItems("use", "uses", max(maxUses-alreadyUsed, 0)) 2704 } 2705 2706 func (ti TeamInvite) IsInfiniteUses() bool { 2707 return ti.MaxUses != nil && *ti.MaxUses == TeamMaxUsesInfinite 2708 } 2709 2710 func (ti TeamInvite) IsUsedUp(alreadyUsed int) bool { 2711 maxUses := ti.MaxUses 2712 if maxUses == nil { 2713 return alreadyUsed >= 1 2714 } 2715 if *maxUses == TeamMaxUsesInfinite { 2716 return false 2717 } 2718 return alreadyUsed >= int(*maxUses) 2719 } 2720 2721 func (ti TeamInvite) IsExpired(now time.Time) bool { 2722 if ti.Etime == nil { 2723 return false 2724 } 2725 etime := FromUnixTime(*ti.Etime) 2726 return now.After(etime) 2727 } 2728 2729 func formatItems(singular string, plural string, count int) string { 2730 if count == 1 { 2731 return "1 " + singular 2732 } 2733 return fmt.Sprintf("%d %s", count, plural) 2734 } 2735 2736 // ComputeValidity is used for invitelinks, but is accurate for other invites as well. 2737 // It computes whether the invite is still valid (i.e., if it can still be used), 2738 // and a short description of when it was invalidated or under what conditions it can 2739 // be later invalidated. 2740 func (md TeamInviteMetadata) ComputeValidity(now time.Time, 2741 userLog map[UserVersion][]UserLogPoint) (isValid bool, validityDescription string) { 2742 2743 isInvalid := false 2744 invalidationAction := "" 2745 var invalidationTime *time.Time 2746 var usedInviteCount int 2747 code, _ := md.Status.Code() 2748 switch code { 2749 case TeamInviteMetadataStatusCode_ACTIVE: 2750 isExpired := md.Invite.IsExpired(now) 2751 if isExpired { 2752 expireTime := md.Invite.Etime.Time() 2753 invalidationTime = &expireTime 2754 } 2755 usedInvites := md.UsedInvites 2756 // If this is an old-style invite that was completed; it wouldn't be ACTIVE anymore, 2757 // so we can assume len(usedInvites) is correct, since it should be empty (implying 2758 // the invite is not UsedUp. 2759 isUsedUp := md.Invite.IsUsedUp(len(usedInvites)) 2760 if isUsedUp { 2761 // implies usedInvites is nonempty 2762 usedInvites := md.UsedInvites 2763 teamUserLogPoint := usedInvites[len(usedInvites)-1] 2764 logPoint := userLog[teamUserLogPoint.Uv][teamUserLogPoint.LogPoint] 2765 usedUpTime := logPoint.SigMeta.Time.Time() 2766 if invalidationTime == nil || usedUpTime.Before(*invalidationTime) { 2767 invalidationTime = &usedUpTime 2768 } 2769 } 2770 if isExpired || isUsedUp { 2771 isInvalid = true 2772 invalidationAction = "Expired" 2773 } 2774 usedInviteCount = len(usedInvites) 2775 case TeamInviteMetadataStatusCode_OBSOLETE: 2776 isInvalid = true 2777 invalidationAction = "Obsoleted" 2778 // no invalidation time for obsoletes 2779 usedInviteCount = 0 2780 case TeamInviteMetadataStatusCode_CANCELLED: 2781 isInvalid = true 2782 invalidationAction = "Cancelled" 2783 cancelTime := md.Status.Cancelled().TeamSigMeta.SigMeta.Time.Time() 2784 invalidationTime = &cancelTime 2785 usedInviteCount = len(md.UsedInvites) 2786 case TeamInviteMetadataStatusCode_COMPLETED: 2787 isInvalid = true 2788 invalidationAction = "Completed" 2789 completeTime := md.Status.Completed().TeamSigMeta.SigMeta.Time.Time() 2790 invalidationTime = &completeTime 2791 usedInviteCount = 1 2792 default: 2793 return false, fmt.Sprintf("unknown invite status %v", code) 2794 } 2795 2796 if isInvalid { 2797 ret := "" 2798 ret += invalidationAction 2799 if invalidationTime != nil { 2800 invalidationDeltaFormatted := kbtime.RelTime(*invalidationTime, now, "", "") 2801 ret += " " + invalidationDeltaFormatted + " ago" 2802 } 2803 return false, ret 2804 } 2805 2806 if md.Invite.Etime == nil && md.Invite.IsInfiniteUses() { 2807 return true, "Does not expire" 2808 } 2809 2810 ret := "Expires" 2811 if md.Invite.Etime != nil { 2812 expirationTimeConverted := FromUnixTime(*md.Invite.Etime) 2813 expirationDeltaFormatted := kbtime.RelTime(expirationTimeConverted, now, "", "") 2814 ret += " in " + expirationDeltaFormatted 2815 } 2816 if md.Invite.Etime != nil && !md.Invite.IsInfiniteUses() { 2817 ret += " or" 2818 } 2819 if !md.Invite.IsInfiniteUses() { 2820 ret += " after " + md.Invite.UsesLeftString(usedInviteCount) 2821 } 2822 return true, ret 2823 } 2824 2825 func (m MemberInfo) TeamName() (TeamName, error) { 2826 return TeamNameFromString(m.FqName) 2827 } 2828 2829 func (i ImplicitTeamUserSet) NumTotalUsers() int { 2830 return len(i.KeybaseUsers) + len(i.UnresolvedUsers) 2831 } 2832 2833 func (i ImplicitTeamUserSet) List() string { 2834 var names []string 2835 names = append(names, i.KeybaseUsers...) 2836 for _, u := range i.UnresolvedUsers { 2837 names = append(names, u.String()) 2838 } 2839 sort.Strings(names) 2840 return strings.Join(names, ",") 2841 } 2842 2843 func (n ImplicitTeamDisplayName) String() string { 2844 name := n.Writers.List() 2845 2846 if n.Readers.NumTotalUsers() > 0 { 2847 name += "#" + n.Readers.List() 2848 } 2849 2850 return name 2851 } 2852 2853 func (c *ImplicitTeamConflictInfo) IsConflict() bool { 2854 return c != nil && c.Generation > ConflictGeneration(0) 2855 } 2856 2857 const ( 2858 // LockIDVersion0 is the first ever version for lock ID format. 2859 LockIDVersion0 byte = iota 2860 ) 2861 2862 // LockIDFromBytes takes the first 8 bytes of the sha512 over data, overwrites 2863 // first byte with the version byte, then interprets it as int64 using big 2864 // endian, and returns the value as LockID. 2865 func LockIDFromBytes(data []byte) LockID { 2866 sum := sha512.Sum512(data) 2867 sum[0] = LockIDVersion0 2868 return LockID(binary.BigEndian.Uint64(sum[:8])) 2869 } 2870 2871 // MDPriority is the type for the priority field of a metadata put. mdserver 2872 // prioritizes MD writes with higher priority when multiple happen at the same 2873 // time, for the same TLF. 2874 const ( 2875 // MDPriorityDefault is the priority of zero. It's implicitly used by all 2876 // old clients, and has lowest priority. 2877 MDPriorityDefault MDPriority = 0 2878 // MDPriorityNormal is the priority used for normal KBFS metadata writes. 2879 MDPriorityNormal = 8 2880 // MDPriorityGit is the priority used for metadata writes triggered by git 2881 // remote helpers. 2882 MDPriorityGit = 32 2883 ) 2884 2885 // IsValid returns true is p is a valid MDPriority, or false otherwise. 2886 func (p MDPriority) IsValid() bool { 2887 return p < 256 && p >= 0 2888 } 2889 2890 func (t TLFVisibility) Eq(r TLFVisibility) bool { 2891 return int(t) == int(r) 2892 } 2893 2894 func ParseUserVersion(s UserVersionPercentForm) (res UserVersion, err error) { 2895 parts := strings.Split(string(s), "%") 2896 if len(parts) == 1 { 2897 // NOTE: We have to keep it the way it is, even though we 2898 // never save UIDs without EldestSeqno anywhere. There may be 2899 // team chain which have UVs encoded with default eldest=1 in 2900 // the wild. 2901 2902 // default to seqno 1 2903 parts = append(parts, "1") 2904 } 2905 if len(parts) != 2 { 2906 return res, fmt.Errorf("invalid user version: %s", s) 2907 } 2908 uid, err := UIDFromString(parts[0]) 2909 if err != nil { 2910 return res, err 2911 } 2912 eldestSeqno, err := strconv.ParseInt(parts[1], 10, 64) 2913 if err != nil { 2914 return res, fmt.Errorf("invalid eldest seqno: %s", err) 2915 } 2916 return UserVersion{ 2917 Uid: uid, 2918 EldestSeqno: Seqno(eldestSeqno), 2919 }, nil 2920 } 2921 2922 func (p StringKVPair) BoolValue() bool { 2923 i, err := strconv.ParseBool(p.Value) 2924 if err != nil { 2925 return false 2926 } 2927 return i 2928 } 2929 2930 func (p StringKVPair) IntValue() int { 2931 i, err := strconv.Atoi(p.Value) 2932 if err != nil { 2933 return 0 2934 } 2935 return i 2936 } 2937 2938 func (r *GitRepoResult) GetIfOk() (res GitRepoInfo, err error) { 2939 state, err := r.State() 2940 if err != nil { 2941 return res, err 2942 } 2943 switch state { 2944 case GitRepoResultState_ERR: 2945 return res, fmt.Errorf(r.Err()) 2946 case GitRepoResultState_OK: 2947 return r.Ok(), nil 2948 } 2949 return res, fmt.Errorf("git repo unknown error") 2950 } 2951 2952 func (r GitRepoInfo) FullName() string { 2953 switch r.Folder.FolderType { 2954 case FolderType_PRIVATE: 2955 return string(r.LocalMetadata.RepoName) 2956 case FolderType_TEAM: 2957 return r.Folder.Name + "/" + string(r.LocalMetadata.RepoName) 2958 default: 2959 return "<repo type error>" 2960 } 2961 } 2962 2963 func (req *TeamChangeReq) AddUVWithRole(uv UserVersion, role TeamRole, 2964 botSettings *TeamBotSettings) error { 2965 if !role.IsRestrictedBot() && botSettings != nil { 2966 return fmt.Errorf("Unexpected botSettings for role %v", role) 2967 } 2968 switch role { 2969 case TeamRole_RESTRICTEDBOT: 2970 if botSettings == nil { 2971 return fmt.Errorf("Cannot add a RESTRICTEDBOT with nil TeamBotSettings") 2972 } 2973 if req.RestrictedBots == nil { 2974 req.RestrictedBots = make(map[UserVersion]TeamBotSettings) 2975 } 2976 req.RestrictedBots[uv] = *botSettings 2977 case TeamRole_BOT: 2978 req.Bots = append(req.Bots, uv) 2979 case TeamRole_READER: 2980 req.Readers = append(req.Readers, uv) 2981 case TeamRole_WRITER: 2982 req.Writers = append(req.Writers, uv) 2983 case TeamRole_ADMIN: 2984 req.Admins = append(req.Admins, uv) 2985 case TeamRole_OWNER: 2986 req.Owners = append(req.Owners, uv) 2987 default: 2988 return fmt.Errorf("Unexpected role: %v", role) 2989 } 2990 return nil 2991 } 2992 2993 func (req *TeamChangeReq) RestrictedBotUVs() (ret []UserVersion) { 2994 for uv := range req.RestrictedBots { 2995 ret = append(ret, uv) 2996 } 2997 return ret 2998 } 2999 3000 // CompleteInviteID adds to the `completed_invites` field, and signals that the 3001 // invite can never be used again. It's used for SBS, Keybase, SeitanV1, and 3002 // SeitanV2 invites. 3003 func (req *TeamChangeReq) CompleteInviteID(inviteID TeamInviteID, uv UserVersionPercentForm) { 3004 if req.CompletedInvites == nil { 3005 req.CompletedInvites = make(map[TeamInviteID]UserVersionPercentForm) 3006 } 3007 req.CompletedInvites[inviteID] = uv 3008 } 3009 3010 // UseInviteID adds to the `used_invites` field. It is used for SeitanInvitelink invites, 3011 // which can be used multiple times. 3012 func (req *TeamChangeReq) UseInviteID(inviteID TeamInviteID, uv UserVersionPercentForm) { 3013 req.UsedInvites = append(req.UsedInvites, TeamUsedInvite{InviteID: inviteID, Uv: uv}) 3014 } 3015 3016 func (req *TeamChangeReq) GetAllAdds() (ret []UserVersion) { 3017 ret = append(ret, req.RestrictedBotUVs()...) 3018 ret = append(ret, req.Bots...) 3019 ret = append(ret, req.Readers...) 3020 ret = append(ret, req.Writers...) 3021 ret = append(ret, req.Admins...) 3022 ret = append(ret, req.Owners...) 3023 return ret 3024 } 3025 3026 func TotalNumberOfCommits(refs []GitRefMetadata) (total int) { 3027 for _, ref := range refs { 3028 total += len(ref.Commits) 3029 } 3030 return total 3031 } 3032 3033 func RefNames(refs []GitRefMetadata) string { 3034 names := make([]string, len(refs)) 3035 for i, ref := range refs { 3036 names[i] = ref.RefName 3037 } 3038 return strings.Join(names, ", ") 3039 } 3040 3041 func TeamEncryptedKBFSKeysetHashFromString(s string) TeamEncryptedKBFSKeysetHash { 3042 return TeamEncryptedKBFSKeysetHash(s) 3043 } 3044 3045 func TeamEncryptedKBFSKeysetHashFromBytes(s []byte) TeamEncryptedKBFSKeysetHash { 3046 return TeamEncryptedKBFSKeysetHashFromString(hex.EncodeToString(s)) 3047 } 3048 3049 func (e TeamEncryptedKBFSKeysetHash) String() string { 3050 return string(e) 3051 } 3052 3053 func (e TeamEncryptedKBFSKeysetHash) Bytes() []byte { 3054 return []byte(e.String()) 3055 } 3056 3057 func (e TeamEncryptedKBFSKeysetHash) SecureEqual(l TeamEncryptedKBFSKeysetHash) bool { 3058 return hmac.Equal(e.Bytes(), l.Bytes()) 3059 } 3060 3061 func (r ResetLink) Summarize() ResetSummary { 3062 return ResetSummary{ 3063 Ctime: r.Ctime, 3064 MerkleRoot: r.MerkleRoot, 3065 ResetSeqno: r.ResetSeqno, 3066 Type: r.Type, 3067 } 3068 } 3069 3070 func (f AvatarFormat) String() string { 3071 return string(f) 3072 } 3073 3074 func (u AvatarUrl) String() string { 3075 return string(u) 3076 } 3077 3078 func MakeAvatarURL(u string) AvatarUrl { 3079 return AvatarUrl(u) 3080 } 3081 3082 func (b Bytes32) IsBlank() bool { 3083 var blank Bytes32 3084 return (subtle.ConstantTimeCompare(b[:], blank[:]) == 1) 3085 } 3086 3087 func (i Identify2ResUPK2) ExportToV1() Identify2Res { 3088 return Identify2Res{ 3089 Upk: UPAKFromUPKV2AI(i.Upk).Base, 3090 IdentifiedAt: i.IdentifiedAt, 3091 TrackBreaks: i.TrackBreaks, 3092 } 3093 } 3094 3095 func (path Path) String() string { 3096 pathType, err := path.PathType() 3097 if err != nil { 3098 return "" 3099 } 3100 switch pathType { 3101 case PathType_KBFS: 3102 return path.Kbfs().Path 3103 case PathType_KBFS_ARCHIVED: 3104 return path.KbfsArchived().Path 3105 case PathType_LOCAL: 3106 return path.Local() 3107 default: 3108 return "" 3109 } 3110 } 3111 3112 func (se *SelectorEntry) UnmarshalJSON(b []byte) error { 3113 if err := json.Unmarshal(b, &se.Index); err == nil { 3114 se.IsIndex = true 3115 return nil 3116 } 3117 3118 if err := json.Unmarshal(b, &se.Key); err == nil { 3119 se.IsKey = true 3120 return nil 3121 } 3122 3123 m := make(map[string]bool) 3124 if err := json.Unmarshal(b, &m); err != nil { 3125 return fmt.Errorf("invalid selector (not dict)") 3126 } 3127 ok1, ok2 := m["all"] 3128 if ok1 && ok2 { 3129 se.IsAll = true 3130 return nil 3131 } 3132 ok1, ok2 = m["contents"] 3133 if ok1 && ok2 { 3134 se.IsContents = true 3135 return nil 3136 } 3137 return fmt.Errorf("invalid selector (not recognized)") 3138 } 3139 3140 func (p PhoneNumber) String() string { 3141 return string(p) 3142 } 3143 3144 var nonDigits = regexp.MustCompile(`[^\d]`) 3145 3146 func PhoneNumberToAssertionValue(phoneNumber string) string { 3147 return nonDigits.ReplaceAllString(phoneNumber, "") 3148 } 3149 3150 func (p PhoneNumber) AssertionValue() string { 3151 return PhoneNumberToAssertionValue(p.String()) 3152 } 3153 3154 func (d TeamData) ID() TeamID { 3155 return d.Chain.Id 3156 } 3157 3158 func (d TeamData) IsPublic() bool { 3159 return d.Chain.Public 3160 } 3161 3162 func (d FastTeamData) ID() TeamID { 3163 return d.Chain.ID 3164 } 3165 3166 func (d FastTeamData) IsPublic() bool { 3167 return d.Chain.Public 3168 } 3169 3170 func (d HiddenTeamChain) ID() TeamID { 3171 return d.Id 3172 } 3173 3174 func (d HiddenTeamChain) IsPublic() bool { 3175 return d.Public 3176 } 3177 3178 func (d HiddenTeamChain) Summary() string { 3179 type pair struct { 3180 g PerTeamKeyGeneration 3181 q Seqno 3182 stubbed bool 3183 } 3184 var arr []pair 3185 for g, q := range d.ReaderPerTeamKeys { 3186 var full bool 3187 if d.Inner != nil { 3188 _, full = d.Inner[q] 3189 } 3190 arr = append(arr, pair{g: g, q: q, stubbed: !full}) 3191 } 3192 sort.Slice(arr, func(i, j int) bool { return arr[i].g < arr[j].g }) 3193 return fmt.Sprintf("{Team:%s, Last:%d, ReaderPerTeamKeys: %+v}", d.Id, d.Last, arr) 3194 } 3195 3196 func (f FullName) String() string { 3197 return string(f) 3198 } 3199 3200 func (h BoxSummaryHash) String() string { 3201 return string(h) 3202 } 3203 3204 func (r BoxAuditAttemptResult) IsOK() bool { 3205 switch r { 3206 case BoxAuditAttemptResult_OK_VERIFIED, BoxAuditAttemptResult_OK_NOT_ATTEMPTED_ROLE, BoxAuditAttemptResult_OK_NOT_ATTEMPTED_OPENTEAM, BoxAuditAttemptResult_OK_NOT_ATTEMPTED_SUBTEAM: 3207 return true 3208 default: 3209 return false 3210 } 3211 } 3212 3213 func (a BoxAuditAttempt) String() string { 3214 ret := a.Result.String() 3215 if a.Error != nil { 3216 ret += fmt.Sprintf("\t(error: %s)", *a.Error) 3217 } 3218 if a.Rotated { 3219 ret += "\t(team rotated)" 3220 } 3221 return ret 3222 } 3223 3224 func (c ContactComponent) ValueString() string { 3225 switch { 3226 case c.Email != nil: 3227 return string(*c.Email) 3228 case c.PhoneNumber != nil: 3229 return string(*c.PhoneNumber) 3230 default: 3231 return "" 3232 } 3233 } 3234 3235 func (c ContactComponent) AssertionType() string { 3236 switch { 3237 case c.Email != nil: 3238 return "email" 3239 case c.PhoneNumber != nil: 3240 return "phone" 3241 default: 3242 return "" 3243 } 3244 } 3245 3246 func (c ContactComponent) FormatDisplayLabel(addLabel bool) string { 3247 if addLabel && c.Label != "" { 3248 return fmt.Sprintf("%s (%s)", c.ValueString(), c.Label) 3249 } 3250 return c.ValueString() 3251 } 3252 3253 func (fct FolderConflictType) MarshalText() ([]byte, error) { 3254 switch fct { 3255 case FolderConflictType_NONE: 3256 return []byte("none"), nil 3257 case FolderConflictType_IN_CONFLICT: 3258 return []byte("in conflict"), nil 3259 case FolderConflictType_IN_CONFLICT_AND_STUCK: 3260 return []byte("in conflict and stuck"), nil 3261 default: 3262 return []byte(fmt.Sprintf("unknown conflict type: %d", fct)), nil 3263 } 3264 } 3265 3266 func (fct *FolderConflictType) UnmarshalText(text []byte) error { 3267 switch string(text) { 3268 case "none": 3269 *fct = FolderConflictType_NONE 3270 case "in conflict": 3271 *fct = FolderConflictType_IN_CONFLICT 3272 case "in conflict and stuck": 3273 *fct = FolderConflictType_IN_CONFLICT_AND_STUCK 3274 default: 3275 return fmt.Errorf("Unknown conflict type: %s", text) 3276 } 3277 return nil 3278 } 3279 3280 func (h *HiddenTeamChain) Tail() *HiddenTeamChainLink { 3281 last := h.Last 3282 if last == Seqno(0) { 3283 return nil 3284 } 3285 ret, ok := h.Inner[last] 3286 if !ok { 3287 return nil 3288 } 3289 return &ret 3290 } 3291 3292 func (h *HiddenTeamChain) TailTriple() *LinkTriple { 3293 last := h.Last 3294 if last == Seqno(0) { 3295 return nil 3296 } 3297 link, ok := h.Outer[last] 3298 if !ok { 3299 return nil 3300 } 3301 return &LinkTriple{ 3302 Seqno: last, 3303 LinkID: link, 3304 SeqType: SeqType_TEAM_PRIVATE_HIDDEN, 3305 } 3306 } 3307 3308 func (s Signer) UserVersion() UserVersion { 3309 return UserVersion{ 3310 Uid: s.U, 3311 EldestSeqno: s.E, 3312 } 3313 } 3314 3315 func (p PerTeamSeedCheck) Hash() (*PerTeamSeedCheckPostImage, error) { 3316 if p.Version != PerTeamSeedCheckVersion_V1 { 3317 return nil, errors.New("can only handle PerTeamKeySeedCheck V1") 3318 } 3319 ret := sha256.Sum256(p.Value[:]) 3320 return &PerTeamSeedCheckPostImage{ 3321 Version: PerTeamSeedCheckVersion_V1, 3322 Value: PerTeamSeedCheckValuePostImage(ret[:]), 3323 }, nil 3324 } 3325 3326 func (p PerTeamSeedCheckPostImage) Eq(p2 PerTeamSeedCheckPostImage) bool { 3327 return (p.Version == p2.Version) && hmac.Equal(p.Value[:], p2.Value[:]) 3328 } 3329 3330 func (r HiddenTeamChainRatchetSet) Flat() []LinkTripleAndTime { 3331 if r.Ratchets == nil { 3332 return nil 3333 } 3334 var ret []LinkTripleAndTime 3335 for _, v := range r.Ratchets { 3336 ret = append(ret, v) 3337 } 3338 return ret 3339 } 3340 3341 func (r HiddenTeamChainRatchetSet) IsEmpty() bool { 3342 return r.Ratchets == nil || len(r.Ratchets) == 0 3343 } 3344 3345 func (r HiddenTeamChainRatchetSet) Max() Seqno { 3346 var ret Seqno 3347 if r.Ratchets == nil { 3348 return ret 3349 } 3350 for _, v := range r.Ratchets { 3351 if v.Triple.Seqno > ret { 3352 ret = v.Triple.Seqno 3353 } 3354 } 3355 return ret 3356 } 3357 3358 func (r HiddenTeamChainRatchetSet) MaxTriple() *LinkTriple { 3359 if r.Ratchets == nil { 3360 return nil 3361 } 3362 var out LinkTriple 3363 for _, v := range r.Ratchets { 3364 if v.Triple.Seqno > out.Seqno { 3365 out = v.Triple 3366 } 3367 } 3368 return &out 3369 } 3370 3371 func (r *HiddenTeamChain) MaxTriple() *LinkTriple { 3372 tail := r.TailTriple() 3373 rat := r.RatchetSet.MaxTriple() 3374 if rat == nil && tail == nil { 3375 return nil 3376 } 3377 if rat == nil { 3378 return tail 3379 } 3380 if tail == nil { 3381 return rat 3382 } 3383 if tail.Seqno > rat.Seqno { 3384 return tail 3385 } 3386 return rat 3387 } 3388 3389 func (r *HiddenTeamChainRatchetSet) init() { 3390 if r.Ratchets == nil { 3391 r.Ratchets = make(map[RatchetType]LinkTripleAndTime) 3392 } 3393 } 3394 3395 func (r *HiddenTeamChainRatchetSet) Merge(r2 HiddenTeamChainRatchetSet) (updated bool) { 3396 r.init() 3397 if r2.Ratchets == nil { 3398 return false 3399 } 3400 for k, v := range r2.Ratchets { 3401 if r.Add(k, v) { 3402 updated = true 3403 } 3404 } 3405 return updated 3406 } 3407 3408 func (r *HiddenTeamChainRatchetSet) Add(t RatchetType, v LinkTripleAndTime) (changed bool) { 3409 r.init() 3410 found, ok := r.Ratchets[t] 3411 if (v.Triple.SeqType == SeqType_TEAM_PRIVATE_HIDDEN) && (!ok || v.Triple.Seqno > found.Triple.Seqno) { 3412 r.Ratchets[t] = v 3413 changed = true 3414 } 3415 return changed 3416 } 3417 3418 func (r LinkTripleAndTime) Clashes(r2 LinkTripleAndTime) bool { 3419 l1 := r.Triple 3420 l2 := r2.Triple 3421 return (l1.Seqno == l2.Seqno && l1.SeqType == l2.SeqType && !l1.LinkID.Eq(l2.LinkID)) 3422 } 3423 3424 func (r MerkleRootV2) Eq(s MerkleRootV2) bool { 3425 return r.Seqno == s.Seqno && r.HashMeta.Eq(s.HashMeta) 3426 } 3427 3428 func (d *HiddenTeamChain) GetLastCommittedSeqno() Seqno { 3429 if d == nil { 3430 return 0 3431 } 3432 return d.LastCommittedSeqno 3433 } 3434 3435 func (d *HiddenTeamChain) GetOuter() map[Seqno]LinkID { 3436 if d == nil { 3437 return nil 3438 } 3439 return d.Outer 3440 } 3441 3442 func (d *HiddenTeamChain) PopulateLastFull() { 3443 if d == nil { 3444 return 3445 } 3446 if d.LastFull != Seqno(0) { 3447 return 3448 } 3449 for i := Seqno(1); i <= d.Last; i++ { 3450 _, found := d.Inner[i] 3451 if !found { 3452 break 3453 } 3454 d.LastFull = i 3455 } 3456 } 3457 3458 func (d *HiddenTeamChain) LastFullPopulateIfUnset() Seqno { 3459 if d == nil { 3460 return Seqno(0) 3461 } 3462 if d.LastFull == Seqno(0) { 3463 d.PopulateLastFull() 3464 } 3465 return d.LastFull 3466 } 3467 3468 func (d *HiddenTeamChain) Merge(newData HiddenTeamChain) (updated bool, err error) { 3469 3470 for seqno, link := range newData.Outer { 3471 existing, ok := d.Outer[seqno] 3472 if ok && !existing.Eq(link) { 3473 return false, fmt.Errorf("bad merge since at seqno %d, link clash: %s != %s", seqno, existing, link) 3474 } 3475 if ok { 3476 continue 3477 } 3478 d.Outer[seqno] = link 3479 updated = true 3480 if seqno > d.Last { 3481 d.Last = seqno 3482 } 3483 } 3484 3485 for q, i := range newData.Inner { 3486 _, found := d.Inner[q] 3487 if found { 3488 continue 3489 } 3490 d.Inner[q] = i 3491 if ptk, ok := i.Ptk[PTKType_READER]; ok { 3492 d.ReaderPerTeamKeys[ptk.Ptk.Gen] = q 3493 } 3494 3495 // If we previously loaded full links up to d.LastFull, but this is d.LastFull+1, 3496 // then we can safely bump the pointer one foward. 3497 if q == d.LastFull+Seqno(1) { 3498 d.LastFull = q 3499 } 3500 updated = true 3501 } 3502 if newData.Last > d.Last { 3503 d.Last = newData.Last 3504 } 3505 3506 if newData.LastCommittedSeqno > d.LastCommittedSeqno { 3507 d.LastCommittedSeqno = newData.LastCommittedSeqno 3508 updated = true 3509 } 3510 3511 for k, v := range newData.LastPerTeamKeys { 3512 existing, ok := d.LastPerTeamKeys[k] 3513 if !ok || existing < v { 3514 d.LastPerTeamKeys[k] = v 3515 } 3516 } 3517 3518 for k, v := range newData.MerkleRoots { 3519 existing, ok := d.MerkleRoots[k] 3520 if ok && !existing.Eq(v) { 3521 return false, fmt.Errorf("bad merge since at seqno %d, merkle root clash: %+v != %+v", k, existing, v) 3522 } 3523 if ok { 3524 continue 3525 } 3526 d.MerkleRoots[k] = v 3527 updated = true 3528 } 3529 3530 if d.RatchetSet.Merge(newData.RatchetSet) { 3531 updated = true 3532 } 3533 3534 for k := range d.LinkReceiptTimes { 3535 if k <= newData.LastCommittedSeqno { 3536 // This link has been committed to the blind tree, no need to keep 3537 // track of it any more 3538 delete(d.LinkReceiptTimes, k) 3539 updated = true 3540 } 3541 } 3542 3543 for k, v := range newData.LinkReceiptTimes { 3544 if _, found := d.LinkReceiptTimes[k]; !found { 3545 if d.LinkReceiptTimes == nil { 3546 d.LinkReceiptTimes = make(map[Seqno]Time) 3547 } 3548 d.LinkReceiptTimes[k] = v 3549 updated = true 3550 } 3551 } 3552 3553 return updated, nil 3554 } 3555 3556 func (h HiddenTeamChain) HasSeqno(s Seqno) bool { 3557 _, found := h.Outer[s] 3558 return found 3559 } 3560 3561 func NewHiddenTeamChain(id TeamID) *HiddenTeamChain { 3562 return &HiddenTeamChain{ 3563 Id: id, 3564 Subversion: 1, // We are now on Version 1.1 3565 LastPerTeamKeys: make(map[PTKType]Seqno), 3566 ReaderPerTeamKeys: make(map[PerTeamKeyGeneration]Seqno), 3567 Outer: make(map[Seqno]LinkID), 3568 Inner: make(map[Seqno]HiddenTeamChainLink), 3569 MerkleRoots: make(map[Seqno]MerkleRootV2), 3570 } 3571 } 3572 3573 func (h *HiddenTeamChain) Tombstone() (changed bool) { 3574 if h.Tombstoned { 3575 return false 3576 } 3577 h.LastPerTeamKeys = make(map[PTKType]Seqno) 3578 h.ReaderPerTeamKeys = make(map[PerTeamKeyGeneration]Seqno) 3579 h.Outer = make(map[Seqno]LinkID) 3580 h.Inner = make(map[Seqno]HiddenTeamChainLink) 3581 h.Tombstoned = true 3582 return true 3583 } 3584 3585 func (h *HiddenTeamChain) Freeze() (changed bool) { 3586 if h.Frozen { 3587 return false 3588 } 3589 h.LastPerTeamKeys = make(map[PTKType]Seqno) 3590 h.ReaderPerTeamKeys = make(map[PerTeamKeyGeneration]Seqno) 3591 h.Inner = make(map[Seqno]HiddenTeamChainLink) 3592 newOuter := make(map[Seqno]LinkID) 3593 if h.Last != Seqno(0) { 3594 newOuter[h.Last] = h.Outer[h.Last] 3595 } 3596 h.Outer = newOuter 3597 h.Frozen = true 3598 return true 3599 } 3600 3601 func (h HiddenTeamChain) LastReaderPerTeamKeyLinkID() (ret LinkID) { 3602 seqno, ok := h.LastPerTeamKeys[PTKType_READER] 3603 if !ok { 3604 return ret 3605 } 3606 tmp, ok := h.Outer[seqno] 3607 if !ok { 3608 return ret 3609 } 3610 return tmp 3611 } 3612 3613 func (h *HiddenTeamChain) GetReaderPerTeamKeyAtGeneration(g PerTeamKeyGeneration) (ret PerTeamKey, found bool) { 3614 if h == nil { 3615 return ret, false 3616 } 3617 q, ok := h.ReaderPerTeamKeys[g] 3618 if !ok { 3619 return ret, false 3620 } 3621 inner, ok := h.Inner[q] 3622 if !ok { 3623 return ret, false 3624 } 3625 key, ok := inner.Ptk[PTKType_READER] 3626 if !ok { 3627 return ret, false 3628 } 3629 return key.Ptk, true 3630 } 3631 3632 func (h *HiddenTeamChain) MaxReaderPerTeamKey() *PerTeamKey { 3633 if h == nil { 3634 return nil 3635 } 3636 seqno, ok := h.LastPerTeamKeys[PTKType_READER] 3637 if !ok { 3638 return nil 3639 } 3640 inner, ok := h.Inner[seqno] 3641 if !ok { 3642 return nil 3643 } 3644 ptk, ok := inner.Ptk[PTKType_READER] 3645 if !ok { 3646 return nil 3647 } 3648 return &ptk.Ptk 3649 } 3650 3651 func (h *HiddenTeamChain) MaxReaderPerTeamKeyGeneration() PerTeamKeyGeneration { 3652 k := h.MaxReaderPerTeamKey() 3653 if k == nil { 3654 return PerTeamKeyGeneration(0) 3655 } 3656 return k.Gen 3657 } 3658 3659 func (h *HiddenTeamChain) KeySummary() string { 3660 if h == nil { 3661 return "Ø" 3662 } 3663 return fmt.Sprintf("{last:%d, lastPerTeamKeys:%+v, readerPerTeamKeys: %+v}", h.Last, h.LastPerTeamKeys, h.ReaderPerTeamKeys) 3664 } 3665 3666 func (h *HiddenTeamChain) LinkAndKeySummary() string { 3667 if h == nil { 3668 return "empty" 3669 } 3670 ks := h.KeySummary() 3671 return fmt.Sprintf("{nOuterlinks: %d, nInnerLinks:%d, keys:%s}", len(h.Outer), len(h.Inner), ks) 3672 } 3673 3674 func (h *TeamData) KeySummary() string { 3675 if h == nil { 3676 return "Ø" 3677 } 3678 var p []PerTeamKeyGeneration 3679 for k := range h.PerTeamKeySeedsUnverified { 3680 p = append(p, k) 3681 } 3682 m := make(map[PerTeamKeyGeneration]bool) 3683 for _, v := range h.ReaderKeyMasks { 3684 for k := range v { 3685 m[k] = true 3686 } 3687 } 3688 var r []PerTeamKeyGeneration 3689 for k := range m { 3690 r = append(r, k) 3691 } 3692 return fmt.Sprintf("{ptksu:%v, rkms:%v, sigchain:%s}", p, r, h.Chain.KeySummary()) 3693 } 3694 3695 func (s TeamSigChainState) UserRole(user UserVersion) TeamRole { 3696 points := s.UserLog[user] 3697 if len(points) == 0 { 3698 return TeamRole_NONE 3699 } 3700 role := points[len(points)-1].Role 3701 return role 3702 } 3703 3704 func (s TeamSigChainState) GetUserLastJoinTime(user UserVersion) (time Time, err error) { 3705 if s.UserRole(user) == TeamRole_NONE { 3706 return 0, fmt.Errorf("In GetUserLastJoinTime: User %s is not a member of team %v", user.Uid, s.Id) 3707 } 3708 // Look for the latest join event, i.e. the latest transition from a role NONE to a different valid one. 3709 points := s.UserLog[user] 3710 for i := len(points) - 1; i > -1; i-- { 3711 if points[i].Role == TeamRole_NONE { 3712 // this is the last time in the sigchain this user has role none 3713 // (note that it cannot be the last link in the chain, otherwise the 3714 // user would have role NONE), so the link after this one is the one 3715 // where they joined the team last. 3716 return points[i+1].SigMeta.Time, nil 3717 } 3718 } 3719 // If the user never had role none, they joined at the time of their first 3720 // UserLog entry (they need to have at least one, else again their role would be 3721 // NONE). 3722 return points[0].SigMeta.Time, nil 3723 } 3724 3725 // GetUserLastRoleChangeTime returns the time of the last role change for user 3726 // in team. If the user left the team as a last change, the time of such leave 3727 // event is returned. If the user was never in the team, then this function 3728 // returns time=0 and wasMember=false. 3729 func (s TeamSigChainState) GetUserLastRoleChangeTime(user UserVersion) (time Time, wasMember bool) { 3730 points := s.UserLog[user] 3731 if len(points) == 0 { 3732 return 0, false 3733 } 3734 return points[len(points)-1].SigMeta.Time, true 3735 } 3736 3737 func (s TeamSigChainState) KeySummary() string { 3738 var v []PerTeamKeyGeneration 3739 for k := range s.PerTeamKeys { 3740 v = append(v, k) 3741 } 3742 return fmt.Sprintf("{maxPTK:%d, ptk:%v}", s.MaxPerTeamKeyGeneration, v) 3743 } 3744 3745 func (s TeamSigChainState) HasAnyStubbedLinks() bool { 3746 for _, v := range s.StubbedLinks { 3747 if v { 3748 return true 3749 } 3750 } 3751 return false 3752 } 3753 3754 func (s TeamSigChainState) ListSubteams() (res []TeamIDAndName) { 3755 type Entry struct { 3756 ID TeamID 3757 Name TeamName 3758 // Seqno of the last cached rename of this team 3759 Seqno Seqno 3760 } 3761 // Use a map to deduplicate names. If there is a subteam name 3762 // collision, take the one with the latest (parent) seqno 3763 // modifying its name. 3764 // A collision could occur if you were removed from a team 3765 // and miss its renaming or deletion to stubbing. 3766 resMap := make(map[string] /*TeamName*/ Entry) 3767 for subteamID, points := range s.SubteamLog { 3768 if len(points) == 0 { 3769 // this should never happen 3770 continue 3771 } 3772 lastPoint := points[len(points)-1] 3773 if lastPoint.Name.IsNil() { 3774 // the subteam has been deleted 3775 continue 3776 } 3777 entry := Entry{ 3778 ID: subteamID, 3779 Name: lastPoint.Name, 3780 Seqno: lastPoint.Seqno, 3781 } 3782 existing, ok := resMap[entry.Name.String()] 3783 replace := !ok || (entry.Seqno >= existing.Seqno) 3784 if replace { 3785 resMap[entry.Name.String()] = entry 3786 } 3787 } 3788 for _, entry := range resMap { 3789 res = append(res, TeamIDAndName{ 3790 Id: entry.ID, 3791 Name: entry.Name, 3792 }) 3793 } 3794 return res 3795 } 3796 3797 func (s TeamSigChainState) GetAllUVs() (res []UserVersion) { 3798 for uv := range s.UserLog { 3799 if s.UserRole(uv) != TeamRole_NONE { 3800 res = append(res, uv) 3801 } 3802 } 3803 return res 3804 } 3805 3806 func (s TeamSigChainState) ActiveInvites() (ret []TeamInvite) { 3807 for _, md := range s.InviteMetadatas { 3808 if code, err := md.Status.Code(); err == nil && 3809 code == TeamInviteMetadataStatusCode_ACTIVE { 3810 ret = append(ret, md.Invite) 3811 } 3812 } 3813 return ret 3814 } 3815 3816 func (h *HiddenTeamChain) IsStale() bool { 3817 if h == nil { 3818 return false 3819 } 3820 max := h.RatchetSet.Max() 3821 if max < h.LatestSeqnoHint { 3822 max = h.LatestSeqnoHint 3823 } 3824 if max == Seqno(0) { 3825 return false 3826 } 3827 _, fresh := h.Outer[max] 3828 return !fresh 3829 } 3830 3831 func (k TeamEphemeralKey) Ctime() Time { 3832 typ, err := k.KeyType() 3833 if err != nil { 3834 return 0 3835 } 3836 switch typ { 3837 case TeamEphemeralKeyType_TEAM: 3838 return k.Team().Metadata.Ctime 3839 case TeamEphemeralKeyType_TEAMBOT: 3840 return k.Teambot().Metadata.Ctime 3841 default: 3842 return 0 3843 } 3844 } 3845 3846 func (k TeamEphemeralKeyBoxed) Ctime() Time { 3847 typ, err := k.KeyType() 3848 if err != nil { 3849 return 0 3850 } 3851 switch typ { 3852 case TeamEphemeralKeyType_TEAM: 3853 return k.Team().Metadata.Ctime 3854 case TeamEphemeralKeyType_TEAMBOT: 3855 return k.Teambot().Metadata.Ctime 3856 default: 3857 return 0 3858 } 3859 } 3860 3861 func (k TeamEphemeralKey) Generation() EkGeneration { 3862 typ, err := k.KeyType() 3863 if err != nil { 3864 return 0 3865 } 3866 switch typ { 3867 case TeamEphemeralKeyType_TEAM: 3868 return k.Team().Metadata.Generation 3869 case TeamEphemeralKeyType_TEAMBOT: 3870 return k.Teambot().Metadata.Generation 3871 default: 3872 return 0 3873 } 3874 } 3875 3876 func (k TeamEphemeralKey) Material() Bytes32 { 3877 typ, err := k.KeyType() 3878 if err != nil { 3879 return [32]byte{} 3880 } 3881 switch typ { 3882 case TeamEphemeralKeyType_TEAM: 3883 return k.Team().Seed 3884 case TeamEphemeralKeyType_TEAMBOT: 3885 return k.Teambot().Seed 3886 default: 3887 return [32]byte{} 3888 } 3889 } 3890 3891 func (k TeamEphemeralKeyBoxed) Generation() EkGeneration { 3892 typ, err := k.KeyType() 3893 if err != nil { 3894 return 0 3895 } 3896 switch typ { 3897 case TeamEphemeralKeyType_TEAM: 3898 return k.Team().Metadata.Generation 3899 case TeamEphemeralKeyType_TEAMBOT: 3900 return k.Teambot().Metadata.Generation 3901 default: 3902 return 0 3903 } 3904 } 3905 3906 func (k TeamEphemeralKeyType) IsTeambot() bool { 3907 return k == TeamEphemeralKeyType_TEAMBOT 3908 } 3909 3910 func (k TeamEphemeralKeyType) IsTeam() bool { 3911 return k == TeamEphemeralKeyType_TEAM 3912 } 3913 3914 // IsLimited returns if the network is considered limited based on the type. 3915 func (s MobileNetworkState) IsLimited() bool { 3916 switch s { 3917 case MobileNetworkState_WIFI, MobileNetworkState_NOTAVAILABLE: 3918 return false 3919 default: 3920 return true 3921 } 3922 } 3923 3924 func (k TeambotKey) Generation() int { 3925 return int(k.Metadata.Generation) 3926 } 3927 3928 func (k TeambotKey) Material() Bytes32 { 3929 return k.Seed 3930 } 3931 3932 func (r APIUserSearchResult) GetStringIDForCompare() string { 3933 switch { 3934 case r.Contact != nil: 3935 return fmt.Sprintf("%s%s", r.Contact.DisplayName, r.Contact.DisplayLabel) 3936 case r.Imptofu != nil: 3937 return fmt.Sprintf("%s%s", r.Imptofu.PrettyName, r.Imptofu.Label) 3938 case r.Keybase != nil: 3939 return r.Keybase.Username 3940 default: 3941 return "" 3942 } 3943 } 3944 3945 func NewPathWithKbfsPath(path string) Path { 3946 return NewPathWithKbfs(KBFSPath{Path: path}) 3947 } 3948 3949 func (p PerTeamKey) Equal(q PerTeamKey) bool { 3950 return p.EncKID.Equal(q.EncKID) && p.SigKID.Equal(q.SigKID) 3951 } 3952 3953 func (b BotToken) IsNil() bool { 3954 return len(b) == 0 3955 } 3956 3957 func (b BotToken) Exists() bool { 3958 return !b.IsNil() 3959 } 3960 3961 func (b BotToken) String() string { 3962 return string(b) 3963 } 3964 3965 var botTokenRxx = regexp.MustCompile(`^[a-zA-Z0-9_-]{32}$`) 3966 3967 func NewBotToken(s string) (BotToken, error) { 3968 if !botTokenRxx.MatchString(s) { 3969 return BotToken(""), errors.New("bad bot token") 3970 } 3971 return BotToken(s), nil 3972 } 3973 3974 func (b BadgeConversationInfo) IsEmpty() bool { 3975 return b.UnreadMessages == 0 && b.BadgeCount == 0 3976 } 3977 3978 func (s *TeamBotSettings) Eq(o *TeamBotSettings) bool { 3979 return reflect.DeepEqual(s, o) 3980 } 3981 3982 func (s *TeamBotSettings) ConvIDAllowed(strCID string) bool { 3983 if s == nil { 3984 return true 3985 } 3986 for _, strConvID := range s.Convs { 3987 if strCID == strConvID { 3988 return true 3989 } 3990 } 3991 return len(s.Convs) == 0 3992 } 3993 3994 func (b UserBlockedBody) Summarize() UserBlockedSummary { 3995 ret := UserBlockedSummary{ 3996 Blocker: b.Username, 3997 Blocks: make(map[string][]UserBlockState), 3998 } 3999 for _, block := range b.Blocks { 4000 if block.Chat != nil { 4001 ret.Blocks[block.Username] = append(ret.Blocks[block.Username], UserBlockState{UserBlockType_CHAT, *block.Chat}) 4002 } 4003 if block.Follow != nil { 4004 ret.Blocks[block.Username] = append(ret.Blocks[block.Username], UserBlockState{UserBlockType_FOLLOW, *block.Follow}) 4005 } 4006 } 4007 return ret 4008 } 4009 4010 func FilterMembersDetails(membMap map[string]struct{}, details []TeamMemberDetails) (res []TeamMemberDetails) { 4011 res = []TeamMemberDetails{} 4012 for _, member := range details { 4013 if _, ok := membMap[member.Username]; ok { 4014 res = append(res, member) 4015 } 4016 } 4017 return res 4018 } 4019 4020 func FilterTeamDetailsForMembers(usernames []string, details TeamDetails) TeamDetails { 4021 membMap := make(map[string]struct{}) 4022 for _, username := range usernames { 4023 membMap[username] = struct{}{} 4024 } 4025 res := details.DeepCopy() 4026 res.Members.Owners = FilterMembersDetails(membMap, res.Members.Owners) 4027 res.Members.Admins = FilterMembersDetails(membMap, res.Members.Admins) 4028 res.Members.Writers = FilterMembersDetails(membMap, res.Members.Writers) 4029 res.Members.Readers = FilterMembersDetails(membMap, res.Members.Readers) 4030 res.Members.Bots = FilterMembersDetails(membMap, res.Members.Bots) 4031 res.Members.RestrictedBots = FilterMembersDetails(membMap, res.Members.RestrictedBots) 4032 return res 4033 } 4034 4035 func (b FeaturedBot) DisplayName() string { 4036 if b.BotAlias == "" { 4037 return b.BotUsername 4038 } 4039 return fmt.Sprintf("%s (%s)", b.BotAlias, b.BotUsername) 4040 } 4041 4042 func (b FeaturedBot) Owner() string { 4043 if b.OwnerTeam != nil { 4044 return *b.OwnerTeam 4045 } 4046 if b.OwnerUser != nil { 4047 return *b.OwnerUser 4048 } 4049 return "" 4050 } 4051 4052 func (b FeaturedBot) Eq(o FeaturedBot) bool { 4053 return b.BotAlias == o.BotAlias && 4054 b.Description == o.Description && 4055 b.ExtendedDescription == o.ExtendedDescription && 4056 b.BotUsername == o.BotUsername && 4057 b.Owner() == o.Owner() 4058 } 4059 4060 func (a SearchArg) String() string { 4061 // Don't leak user's query string 4062 return fmt.Sprintf("Limit: %d, Offset: %d", a.Limit, a.Offset) 4063 } 4064 func (a SearchLocalArg) String() string { 4065 // Don't leak user's query string 4066 return fmt.Sprintf("Limit: %d, SkipCache: %v", a.Limit, a.SkipCache) 4067 } 4068 4069 func (b FeaturedBotsRes) Eq(o FeaturedBotsRes) bool { 4070 if len(b.Bots) != len(o.Bots) { 4071 return false 4072 } 4073 for i, bot := range b.Bots { 4074 if !bot.Eq(o.Bots[i]) { 4075 return false 4076 } 4077 } 4078 return true 4079 } 4080 4081 // Redact modifies the given ClientDetails struct 4082 func (d *ClientDetails) Redact() { 4083 tmp := fmt.Sprintf("%v", d.Argv) 4084 re := regexp.MustCompile(`\b(chat|fs|encrypt|git|accept-invite|wallet\s+send|wallet\s+import|passphrase\s+check)\b`) 4085 if mtch := re.FindString(tmp); len(mtch) > 0 { 4086 d.Argv = []string{d.Argv[0], mtch, redactedReplacer} 4087 } 4088 4089 for i, arg := range d.Argv { 4090 if strings.Contains(arg, "paperkey") && i+1 < len(d.Argv) && !strings.HasPrefix(d.Argv[i+1], "-") { 4091 d.Argv[i+1] = redactedReplacer 4092 } 4093 } 4094 } 4095 4096 func (s UserSummarySet) Usernames() (ret []string) { 4097 for _, x := range s.Users { 4098 ret = append(ret, x.Username) 4099 } 4100 return ret 4101 } 4102 4103 func (x InstrumentationStat) AppendStat(y InstrumentationStat) InstrumentationStat { 4104 x.Mtime = ToTime(time.Now()) 4105 x.NumCalls += y.NumCalls 4106 x.TotalDur += y.TotalDur 4107 if y.MaxDur > x.MaxDur { 4108 x.MaxDur = y.MaxDur 4109 } 4110 if y.MinDur < x.MinDur { 4111 x.MinDur = y.MinDur 4112 } 4113 4114 x.TotalSize += y.TotalSize 4115 if y.MaxSize > x.MaxSize { 4116 x.MaxSize = y.MaxSize 4117 } 4118 if y.MinSize < x.MinSize { 4119 x.MinSize = y.MinSize 4120 } 4121 4122 x.AvgDur = x.TotalDur / DurationMsec(x.NumCalls) 4123 x.AvgSize = x.TotalSize / int64(x.NumCalls) 4124 return x 4125 } 4126 4127 func (e TeamSearchExport) Hash() string { 4128 l := make([]TeamSearchItem, 0, len(e.Items)) 4129 for _, item := range e.Items { 4130 l = append(l, item) 4131 } 4132 sort.Slice(l, func(i, j int) bool { 4133 return l[i].Id.Less(l[j].Id) 4134 }) 4135 hasher := sha256.New() 4136 for _, team := range l { 4137 log := math.Floor(math.Log10(float64(team.MemberCount))) 4138 rounder := int(math.Pow(10, log)) 4139 value := (team.MemberCount / rounder) * rounder 4140 hasher.Write(team.Id.ToBytes()) 4141 hasher.Write([]byte(fmt.Sprintf("%d", value))) 4142 } 4143 for _, id := range e.Suggested { 4144 hasher.Write(id.ToBytes()) 4145 } 4146 return hex.EncodeToString(hasher.Sum(nil)) 4147 } 4148 4149 // web-of-trust 4150 // In order of descending quality. 4151 // Keep in sync with: 4152 // - server helpers/wot.ts 4153 // - gui WebOfTrustVerificationType 4154 const ( 4155 UsernameVerificationType_IN_PERSON = "in_person" 4156 UsernameVerificationType_VIDEO = "video" 4157 UsernameVerificationType_AUDIO = "audio" 4158 UsernameVerificationType_PROOFS = "proofs" 4159 UsernameVerificationType_OTHER_CHAT = "other_chat" 4160 UsernameVerificationType_FAMILIAR = "familiar" 4161 UsernameVerificationType_OTHER = "other" 4162 ) 4163 4164 var UsernameVerificationTypeMap = map[string]UsernameVerificationType{ 4165 "in_person": UsernameVerificationType_IN_PERSON, 4166 "proofs": UsernameVerificationType_PROOFS, 4167 "video": UsernameVerificationType_VIDEO, 4168 "audio": UsernameVerificationType_AUDIO, 4169 "other_chat": UsernameVerificationType_OTHER_CHAT, 4170 "familiar": UsernameVerificationType_FAMILIAR, 4171 "other": UsernameVerificationType_OTHER, 4172 } 4173 4174 func (fsc FolderSyncConfig) Equal(other FolderSyncConfig) bool { 4175 if fsc.Mode != other.Mode { 4176 return false 4177 } 4178 if len(fsc.Paths) != len(other.Paths) { 4179 return false 4180 } 4181 for i, p := range fsc.Paths { 4182 if p != other.Paths[i] { 4183 return false 4184 } 4185 } 4186 return true 4187 } 4188 4189 func (t SeitanIKeyInvitelink) String() string { 4190 return string(t) 4191 } 4192 4193 // UserRolePairsHaveOwner check if a list of UserRolePair has user with role 4194 // OWNER. 4195 func UserRolePairsHaveOwner(users []UserRolePair) bool { 4196 for _, urp := range users { 4197 if urp.Role == TeamRole_OWNER { 4198 return true 4199 } 4200 } 4201 return false 4202 } 4203 4204 func (e EmailAddress) String() string { 4205 return string(e) 4206 } 4207 4208 func NewTeamSigMeta(sigMeta SignatureMetadata, uv UserVersion) TeamSignatureMetadata { 4209 return TeamSignatureMetadata{SigMeta: sigMeta, Uv: uv} 4210 } 4211 4212 func NewTeamInviteMetadata(invite TeamInvite, teamSigMeta TeamSignatureMetadata) TeamInviteMetadata { 4213 return TeamInviteMetadata{ 4214 Invite: invite, 4215 TeamSigMeta: teamSigMeta, 4216 Status: NewTeamInviteMetadataStatusWithActive(), 4217 } 4218 } 4219 4220 func (a AnnotatedTeam) ToLegacyTeamDetails() TeamDetails { 4221 var members TeamMembersDetails 4222 for _, member := range a.Members { 4223 switch member.Role { 4224 case TeamRole_RESTRICTEDBOT: 4225 members.RestrictedBots = append(members.RestrictedBots, member) 4226 case TeamRole_BOT: 4227 members.Bots = append(members.Bots, member) 4228 case TeamRole_READER: 4229 members.Readers = append(members.Readers, member) 4230 case TeamRole_WRITER: 4231 members.Writers = append(members.Writers, member) 4232 case TeamRole_ADMIN: 4233 members.Admins = append(members.Admins, member) 4234 case TeamRole_OWNER: 4235 members.Owners = append(members.Owners, member) 4236 } 4237 } 4238 4239 annotatedActiveInvites := make(map[TeamInviteID]AnnotatedTeamInvite) 4240 for _, annotatedInvite := range a.Invites { 4241 code, _ := annotatedInvite.InviteMetadata.Status.Code() 4242 if code != TeamInviteMetadataStatusCode_ACTIVE { 4243 continue 4244 } 4245 annotatedActiveInvites[annotatedInvite.InviteMetadata.Invite.Id] = annotatedInvite 4246 } 4247 4248 return TeamDetails{ 4249 Name: a.Name, 4250 Members: members, 4251 KeyGeneration: a.KeyGeneration, 4252 AnnotatedActiveInvites: annotatedActiveInvites, 4253 Settings: a.Settings, 4254 Showcase: a.Showcase, 4255 } 4256 }