github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/kbfs/tlf/id.go (about) 1 // Copyright 2016 Keybase Inc. All rights reserved. 2 // Use of this source code is governed by a BSD 3 // license that can be found in the LICENSE file. 4 5 package tlf 6 7 import ( 8 "bytes" 9 "encoding" 10 "encoding/hex" 11 "fmt" 12 "strings" 13 14 "github.com/keybase/client/go/kbfs/kbfscrypto" 15 "github.com/keybase/client/go/protocol/keybase1" 16 "github.com/pkg/errors" 17 ) 18 19 const ( 20 // idByteLen is the number of bytes in a top-level folder ID 21 idByteLen = 16 22 // idSuffix is the last byte of a private top-level folder ID 23 idSuffix = 0x16 24 // pubIDSuffix is the last byte of a public top-level folder ID 25 pubIDSuffix = 0x17 26 // singleTeamIDSuffix is the last byte of a single-team top-level 27 // folder ID 28 singleTeamIDSuffix = 0x26 29 ) 30 31 // Type is the type of TLF represented by a particular ID (e.g., 32 // public, private, etc.) 33 type Type int 34 35 const ( 36 // Unknown is a placeholder type for when TLF type information is not 37 // available. It is the zero value of the type Type. 38 Unknown Type = iota 39 // Private represents a private TLF between one or more individual users. 40 Private 41 // Public represents a public TLF for one or more individual users. 42 Public 43 // SingleTeam represents a private TLF for a single Keybase team. 44 SingleTeam 45 ) 46 47 const ( 48 strPrivate = "private" 49 strPublic = "public" 50 strSingleTeam = "singleTeam" 51 strTeam = "team" 52 ) 53 54 func (t Type) String() string { 55 switch t { 56 case Private: 57 return strPrivate 58 case Public: 59 return strPublic 60 case SingleTeam: 61 return strSingleTeam 62 default: 63 return fmt.Sprintf("Unknown TLF type: %d", t) 64 } 65 } 66 67 // MarshalText implements the encoding.TextMarshaler interface for Type. 68 func (t Type) MarshalText() ([]byte, error) { 69 return []byte(t.String()), nil 70 } 71 72 // PathString returns the string representation of t, when they are used in a 73 // KBFS path. This is different from String() where this one returns 'team' 74 // instead of 'singleTeam' for SingleTeam. 75 func (t Type) PathString() string { 76 switch t { 77 case Private: 78 return strPrivate 79 case Public: 80 return strPublic 81 case SingleTeam: 82 return strTeam 83 default: 84 return fmt.Sprintf("Unknown TLF type: %d", t) 85 } 86 } 87 88 // FolderType returns the keybase1.FolderType corresponding to the 89 // given TLF type. 90 func (t Type) FolderType() keybase1.FolderType { 91 switch t { 92 case Private: 93 return keybase1.FolderType_PRIVATE 94 case Public: 95 return keybase1.FolderType_PUBLIC 96 case SingleTeam: 97 return keybase1.FolderType_TEAM 98 default: 99 return keybase1.FolderType_UNKNOWN 100 } 101 } 102 103 // ErrUnknownTLFType is returned by ParseTlfType when an unknown TLF type 104 // string is provided. 105 type ErrUnknownTLFType struct { 106 unknownType string 107 } 108 109 // Error implements the error interface. 110 func (e ErrUnknownTLFType) Error() string { 111 return "unknown TLF type: " + e.unknownType 112 } 113 114 // ParseTlfTypeFromPath parses str into a Type. 115 func ParseTlfTypeFromPath(str string) (Type, error) { 116 switch strings.ToLower(str) { 117 case strPrivate: 118 return Private, nil 119 case strPublic: 120 return Public, nil 121 case strTeam: 122 return SingleTeam, nil 123 default: 124 return Unknown, ErrUnknownTLFType{unknownType: str} 125 } 126 } 127 128 // TypeFromFolderType returns the Type corresponding to the given 129 // keybase1.FolderType. 130 func TypeFromFolderType(ft keybase1.FolderType) Type { 131 switch ft { 132 case keybase1.FolderType_PRIVATE: 133 return Private 134 case keybase1.FolderType_PUBLIC: 135 return Public 136 case keybase1.FolderType_TEAM: 137 return SingleTeam 138 default: 139 return Unknown 140 } 141 } 142 143 // KeyingType represents a TLF keying mode. It normally have the same values 144 // as Type. 145 type KeyingType Type 146 147 const ( 148 // UnknownKeying is a placeholder type for when TLF keying mode is unknown. 149 UnknownKeying = KeyingType(Unknown) 150 // PrivateKeying specifies the TLF keying mode used in classic private TLFs. 151 PrivateKeying = KeyingType(Private) 152 // PublicKeying specifies the TLF keying mode used in classic public TLFs. 153 PublicKeying = KeyingType(Public) 154 // TeamKeying specifies the TLF keying mode used for SingleTeam or 155 // implicit team backed TLFs. 156 TeamKeying = KeyingType(SingleTeam) 157 ) 158 159 // ToKeyingType converts Type t into a KeyingType. 160 func (t Type) ToKeyingType() KeyingType { 161 return KeyingType(t) 162 } 163 164 // String implements the fmt.Stringer interface. 165 func (t KeyingType) String() string { 166 switch t { 167 case PrivateKeying: 168 return "private keying" 169 case PublicKeying: 170 return "public keying" 171 case TeamKeying: 172 return "team keying" 173 default: 174 return fmt.Sprintf("Unknown TLF keying type: %d", t) 175 } 176 } 177 178 // ID is a top-level folder ID 179 type ID struct { 180 id [idByteLen]byte 181 } 182 183 var _ encoding.BinaryMarshaler = ID{} 184 var _ encoding.BinaryUnmarshaler = (*ID)(nil) 185 186 var _ encoding.TextMarshaler = ID{} 187 var _ encoding.TextUnmarshaler = (*ID)(nil) 188 189 // NullID is an empty ID 190 var NullID = ID{} 191 192 // Bytes returns the bytes of the TLF ID. 193 func (id ID) Bytes() []byte { 194 return id.id[:] 195 } 196 197 // String implements the fmt.Stringer interface for ID. 198 func (id ID) String() string { 199 return hex.EncodeToString(id.id[:]) 200 } 201 202 // MarshalBinary implements the encoding.BinaryMarshaler interface for ID. 203 func (id ID) MarshalBinary() (data []byte, err error) { 204 suffix := id.id[idByteLen-1] 205 if suffix != idSuffix && suffix != pubIDSuffix && 206 suffix != singleTeamIDSuffix { 207 return nil, errors.WithStack(InvalidIDError{id.String()}) 208 } 209 return id.id[:], nil 210 } 211 212 // UnmarshalBinary implements the encoding.BinaryUnmarshaler interface 213 // for ID. 214 func (id *ID) UnmarshalBinary(data []byte) error { 215 if len(data) != idByteLen { 216 return errors.WithStack( 217 InvalidIDError{hex.EncodeToString(data)}) 218 } 219 suffix := data[idByteLen-1] 220 if suffix != idSuffix && suffix != pubIDSuffix && 221 suffix != singleTeamIDSuffix { 222 return errors.WithStack( 223 InvalidIDError{hex.EncodeToString(data)}) 224 } 225 copy(id.id[:], data) 226 return nil 227 } 228 229 // MarshalText implements the encoding.TextMarshaler interface for ID. 230 func (id ID) MarshalText() ([]byte, error) { 231 bytes, err := id.MarshalBinary() 232 if err != nil { 233 return nil, err 234 } 235 return []byte(hex.EncodeToString(bytes)), nil 236 } 237 238 // UnmarshalText implements the encoding.TextUnmarshaler interface for 239 // ID. 240 func (id *ID) UnmarshalText(buf []byte) error { 241 s := string(buf) 242 bytes, err := hex.DecodeString(s) 243 if err != nil { 244 return errors.WithStack(InvalidIDError{s}) 245 } 246 return id.UnmarshalBinary(bytes) 247 } 248 249 // SafeType returns the type of TLF represented by this ID. If the ID 250 // isn't valid, it returns tlf.Unknown along with an error. 251 func (id ID) SafeType() (Type, error) { 252 switch id.id[idByteLen-1] { 253 case idSuffix: 254 return Private, nil 255 case pubIDSuffix: 256 return Public, nil 257 case singleTeamIDSuffix: 258 return SingleTeam, nil 259 default: 260 return Unknown, fmt.Errorf("Unknown ID suffix %x", id.id[idByteLen-1]) 261 } 262 } 263 264 // Type returns the type of TLF represented by this ID. 265 // 266 // Note that this function panics if the ID suffix is unknown, rather than 267 // returning tlf.Unknown. 268 func (id ID) Type() Type { 269 t, err := id.SafeType() 270 if err != nil { 271 panic(err) 272 } 273 return t 274 } 275 276 // ParseID parses a hex encoded ID. Returns NullID and an 277 // InvalidIDError on failure. 278 func ParseID(s string) (ID, error) { 279 var id ID 280 err := id.UnmarshalText([]byte(s)) 281 if err != nil { 282 return ID{}, err 283 } 284 return id, nil 285 } 286 287 // MakeRandomID makes a random ID using a cryptographically secure 288 // RNG. Returns NullID on failure. 289 func MakeRandomID(t Type) (ID, error) { 290 var idBytes [idByteLen]byte 291 err := kbfscrypto.RandRead(idBytes[:]) 292 if err != nil { 293 return NullID, err 294 } 295 switch t { 296 case Private: 297 idBytes[idByteLen-1] = idSuffix 298 case Public: 299 idBytes[idByteLen-1] = pubIDSuffix 300 case SingleTeam: 301 idBytes[idByteLen-1] = singleTeamIDSuffix 302 default: 303 panic(fmt.Sprintf("Unknown TLF type %d", t)) 304 } 305 var id ID 306 err = id.UnmarshalBinary(idBytes[:]) 307 if err != nil { 308 return NullID, err 309 } 310 return id, nil 311 } 312 313 // MakeIDFromTeam makes a deterministic TLF ID from a team ID and an epoch 314 // representing how many times a new TLF has been needed for this 315 // team. Returns NullID on failure. 316 func MakeIDFromTeam(t Type, tid keybase1.TeamID, epoch byte) (ID, error) { 317 idBytes := tid.ToBytes() 318 if len(idBytes) != idByteLen { 319 return NullID, errors.Errorf( 320 "The length of team ID %s doesn't match that of a TLF ID", tid) 321 } 322 323 idBytes[idByteLen-2] = epoch 324 325 switch t { 326 case Private: 327 if tid.IsPublic() { 328 return NullID, errors.Errorf( 329 "Cannot make a private TLF for a public team ID %s", tid) 330 } 331 idBytes[idByteLen-1] = idSuffix 332 case Public: 333 if !tid.IsPublic() { 334 return NullID, errors.Errorf( 335 "Cannot make a public TLF for a private team ID %s", tid) 336 } 337 idBytes[idByteLen-1] = pubIDSuffix 338 case SingleTeam: 339 if tid.IsPublic() { 340 return NullID, errors.Errorf( 341 "Cannot make a single-team TLF for a public team ID %s", tid) 342 } 343 idBytes[idByteLen-1] = singleTeamIDSuffix 344 default: 345 panic(fmt.Sprintf("Unknown TLF type %d", t)) 346 } 347 var id ID 348 err := id.UnmarshalBinary(idBytes) 349 if err != nil { 350 return NullID, err 351 } 352 return id, nil 353 } 354 355 // GetEpochFromTeamTLF returns 1) whether this ID matches the given 356 // team TID, and 2) if so, which epoch it is. 357 func (id ID) GetEpochFromTeamTLF(tid keybase1.TeamID) ( 358 matches bool, epoch byte, err error) { 359 tidBytes := tid.ToBytes() 360 if len(tidBytes) != idByteLen { 361 return false, 0, errors.Errorf( 362 "The length of team ID %s doesn't match that of a TLF ID", tid) 363 } 364 365 epochIndex := idByteLen - 2 366 if !bytes.Equal(tidBytes[:epochIndex], id.id[:epochIndex]) { 367 return false, 0, nil 368 } 369 370 return true, id.id[epochIndex], nil 371 }