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