github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/kbfsmd/key_bundle_v2.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 kbfsmd 6 7 import ( 8 "fmt" 9 10 "github.com/keybase/client/go/kbfs/kbfscodec" 11 "github.com/keybase/client/go/kbfs/kbfscrypto" 12 "github.com/keybase/client/go/protocol/keybase1" 13 "github.com/keybase/go-codec/codec" 14 "github.com/pkg/errors" 15 ) 16 17 // EPubKeyLocationV2 represents the location of a user's ephemeral 18 // public key. Note that for V2, a reader ePubKey can be in either the 19 // writers array (if rekeyed normally) or the readers array (if 20 // rekeyed by a reader), but a writer ePubKey can be in either array 21 // also; if a reader whose ePubKey is in the readers array is 22 // promoted, then the reader becomes a writer whose ePubKey is still 23 // in the readers array. 24 type EPubKeyLocationV2 int 25 26 const ( 27 // WriterEPubKeys means the ephemeral public key is in the 28 // writers array. 29 WriterEPubKeys EPubKeyLocationV2 = 1 30 // ReaderEPubKeys means the ephemeral public key is in the 31 // writers array. 32 ReaderEPubKeys EPubKeyLocationV2 = 2 33 ) 34 35 func (t EPubKeyLocationV2) String() string { 36 switch t { 37 case WriterEPubKeys: 38 return "WriterEPubKeys" 39 case ReaderEPubKeys: 40 return "ReaderEPubKeys" 41 default: 42 return fmt.Sprintf("EPubKeyLocationV2(%d)", t) 43 } 44 } 45 46 // GetEphemeralPublicKeyInfoV2 encapsulates all the ugly logic needed to 47 // deal with the "negative hack" from 48 // RootMetadataV2.UpdateKeyGeneration. 49 func GetEphemeralPublicKeyInfoV2(info TLFCryptKeyInfo, 50 wkb TLFWriterKeyBundleV2, rkb TLFReaderKeyBundleV2) ( 51 keyLocation EPubKeyLocationV2, index int, 52 ePubKey kbfscrypto.TLFEphemeralPublicKey, err error) { 53 var publicKeys kbfscrypto.TLFEphemeralPublicKeys 54 if info.EPubKeyIndex >= 0 { 55 index = info.EPubKeyIndex 56 publicKeys = wkb.TLFEphemeralPublicKeys 57 keyLocation = WriterEPubKeys 58 } else { 59 index = -1 - info.EPubKeyIndex 60 publicKeys = rkb.TLFReaderEphemeralPublicKeys 61 keyLocation = ReaderEPubKeys 62 } 63 keyCount := len(publicKeys) 64 if index >= keyCount { 65 return EPubKeyLocationV2(0), 66 0, kbfscrypto.TLFEphemeralPublicKey{}, 67 fmt.Errorf("Invalid key in %s with index %d >= %d", 68 keyLocation, index, keyCount) 69 } 70 71 return keyLocation, index, publicKeys[index], nil 72 } 73 74 // DeviceKeyInfoMapV2 is a map from a user devices (identified by the 75 // KID of the corresponding device CryptPublicKey) to the 76 // TLF's symmetric secret key information. 77 type DeviceKeyInfoMapV2 map[keybase1.KID]TLFCryptKeyInfo 78 79 func (dkimV2 DeviceKeyInfoMapV2) fillInDeviceInfos( 80 uid keybase1.UID, tlfCryptKey kbfscrypto.TLFCryptKey, 81 ePrivKey kbfscrypto.TLFEphemeralPrivateKey, ePubIndex int, 82 updatedDeviceKeys DevicePublicKeys) ( 83 serverHalves DeviceKeyServerHalves, err error) { 84 serverHalves = make(DeviceKeyServerHalves, len(updatedDeviceKeys)) 85 // TODO: parallelize 86 for k := range updatedDeviceKeys { 87 // Skip existing entries, and only fill in new ones. 88 if _, ok := dkimV2[k.KID()]; ok { 89 continue 90 } 91 92 clientInfo, serverHalf, err := splitTLFCryptKey( 93 uid, tlfCryptKey, ePrivKey, ePubIndex, k) 94 if err != nil { 95 return nil, err 96 } 97 98 dkimV2[k.KID()] = clientInfo 99 serverHalves[k] = serverHalf 100 } 101 102 return serverHalves, nil 103 } 104 105 func (dkimV2 DeviceKeyInfoMapV2) toPublicKeys() DevicePublicKeys { 106 publicKeys := make(DevicePublicKeys, len(dkimV2)) 107 for kid := range dkimV2 { 108 publicKeys[kbfscrypto.MakeCryptPublicKey(kid)] = true 109 } 110 return publicKeys 111 } 112 113 // UserDeviceKeyInfoMapV2 maps a user's keybase UID to their 114 // DeviceKeyInfoMapV2. 115 type UserDeviceKeyInfoMapV2 map[keybase1.UID]DeviceKeyInfoMapV2 116 117 // ToPublicKeys converts this object to a UserDevicePublicKeys object. 118 func (udkimV2 UserDeviceKeyInfoMapV2) ToPublicKeys() UserDevicePublicKeys { 119 publicKeys := make(UserDevicePublicKeys, len(udkimV2)) 120 for u, dkimV2 := range udkimV2 { 121 publicKeys[u] = dkimV2.toPublicKeys() 122 } 123 return publicKeys 124 } 125 126 // RemoveDevicesNotIn removes any info for any device that is not 127 // contained in the given map of users and devices. 128 func (udkimV2 UserDeviceKeyInfoMapV2) RemoveDevicesNotIn( 129 updatedUserKeys UserDevicePublicKeys) ServerHalfRemovalInfo { 130 removalInfo := make(ServerHalfRemovalInfo) 131 for uid, dkim := range udkimV2 { 132 userRemoved := false 133 deviceServerHalfIDs := make(DeviceServerHalfRemovalInfo) 134 if deviceKeys, ok := updatedUserKeys[uid]; ok { 135 for kid, info := range dkim { 136 key := kbfscrypto.MakeCryptPublicKey(kid) 137 if !deviceKeys[key] { 138 delete(dkim, kid) 139 deviceServerHalfIDs[key] = append( 140 deviceServerHalfIDs[key], 141 info.ServerHalfID) 142 } 143 } 144 145 if len(deviceServerHalfIDs) == 0 { 146 continue 147 } 148 } else { 149 // The user was completely removed, which 150 // shouldn't happen but might as well make it 151 // work just in case. 152 userRemoved = true 153 for kid, info := range dkim { 154 key := kbfscrypto.MakeCryptPublicKey(kid) 155 deviceServerHalfIDs[key] = append( 156 deviceServerHalfIDs[key], 157 info.ServerHalfID) 158 } 159 160 delete(udkimV2, uid) 161 } 162 163 removalInfo[uid] = UserServerHalfRemovalInfo{ 164 UserRemoved: userRemoved, 165 DeviceServerHalfIDs: deviceServerHalfIDs, 166 } 167 } 168 169 return removalInfo 170 } 171 172 // FillInUserInfos fills in this map from the given info. 173 func (udkimV2 UserDeviceKeyInfoMapV2) FillInUserInfos( 174 newIndex int, updatedUserKeys UserDevicePublicKeys, 175 ePrivKey kbfscrypto.TLFEphemeralPrivateKey, 176 tlfCryptKey kbfscrypto.TLFCryptKey) ( 177 serverHalves UserDeviceKeyServerHalves, err error) { 178 serverHalves = make(UserDeviceKeyServerHalves, len(updatedUserKeys)) 179 for u, updatedDeviceKeys := range updatedUserKeys { 180 if _, ok := udkimV2[u]; !ok { 181 udkimV2[u] = DeviceKeyInfoMapV2{} 182 } 183 184 deviceServerHalves, err := udkimV2[u].fillInDeviceInfos( 185 u, tlfCryptKey, ePrivKey, newIndex, 186 updatedDeviceKeys) 187 if err != nil { 188 return nil, err 189 } 190 if len(deviceServerHalves) > 0 { 191 serverHalves[u] = deviceServerHalves 192 } 193 } 194 return serverHalves, nil 195 } 196 197 // All section references below are to https://keybase.io/docs/crypto/kbfs 198 // (version 1.8). 199 200 // TLFWriterKeyBundleV2 is a bundle of all the writer keys for a top-level 201 // folder. 202 type TLFWriterKeyBundleV2 struct { 203 // Maps from each writer to their crypt key bundle. 204 WKeys UserDeviceKeyInfoMapV2 205 206 // M_f as described in § 4.1.1. 207 TLFPublicKey kbfscrypto.TLFPublicKey `codec:"pubKey"` 208 209 // M_e as described in § 4.1.1. Because devices can be added 210 // into the key generation after it is initially created (so 211 // those devices can get access to existing data), we track 212 // multiple ephemeral public keys; the one used by a 213 // particular device is specified by EPubKeyIndex in its 214 // TLFCryptoKeyInfo struct. 215 TLFEphemeralPublicKeys kbfscrypto.TLFEphemeralPublicKeys `codec:"ePubKey"` 216 217 codec.UnknownFieldSetHandler 218 } 219 220 // IsWriter returns true if the given user device is in the writer set. 221 func (wkb TLFWriterKeyBundleV2) IsWriter(user keybase1.UID, deviceKey kbfscrypto.CryptPublicKey) bool { 222 _, ok := wkb.WKeys[user][deviceKey.KID()] 223 return ok 224 } 225 226 // TLFWriterKeyGenerationsV2 stores a slice of TLFWriterKeyBundleV2, 227 // where the last element is the current generation. 228 type TLFWriterKeyGenerationsV2 []TLFWriterKeyBundleV2 229 230 // LatestKeyGeneration returns the current key generation for this TLF. 231 func (wkg TLFWriterKeyGenerationsV2) LatestKeyGeneration() KeyGen { 232 return KeyGen(len(wkg)) 233 } 234 235 // IsWriter returns whether or not the user+device is an authorized writer 236 // for the latest generation. 237 func (wkg TLFWriterKeyGenerationsV2) IsWriter(user keybase1.UID, deviceKey kbfscrypto.CryptPublicKey) bool { 238 keyGen := wkg.LatestKeyGeneration() 239 if keyGen < 1 { 240 return false 241 } 242 return wkg[keyGen-1].IsWriter(user, deviceKey) 243 } 244 245 // ToTLFWriterKeyBundleV3 converts a TLFWriterKeyGenerationsV2 to a TLFWriterKeyBundleV3. 246 func (wkg TLFWriterKeyGenerationsV2) ToTLFWriterKeyBundleV3( 247 codec kbfscodec.Codec, 248 tlfCryptKeyGetter func() ([]kbfscrypto.TLFCryptKey, error)) ( 249 TLFWriterKeyBundleV2, TLFWriterKeyBundleV3, error) { 250 keyGen := wkg.LatestKeyGeneration() 251 if keyGen < FirstValidKeyGen { 252 return TLFWriterKeyBundleV2{}, TLFWriterKeyBundleV3{}, 253 errors.New("No key generations to convert") 254 } 255 256 // Copy the latest UserDeviceKeyInfoMap. 257 wkbV2 := wkg[keyGen-FirstValidKeyGen] 258 ePubKeyCount := len(wkbV2.TLFEphemeralPublicKeys) 259 udkimV3, err := writerUDKIMV2ToV3(codec, wkbV2.WKeys, ePubKeyCount) 260 if err != nil { 261 return TLFWriterKeyBundleV2{}, TLFWriterKeyBundleV3{}, err 262 } 263 wkbV3 := TLFWriterKeyBundleV3{ 264 Keys: udkimV3, 265 TLFEphemeralPublicKeys: make( 266 kbfscrypto.TLFEphemeralPublicKeys, ePubKeyCount), 267 TLFPublicKey: wkbV2.TLFPublicKey, 268 } 269 // Copy all of the TLFEphemeralPublicKeys at this generation. 270 copy(wkbV3.TLFEphemeralPublicKeys[:], wkbV2.TLFEphemeralPublicKeys) 271 272 if keyGen > FirstValidKeyGen { 273 // Fetch all of the TLFCryptKeys. 274 keys, err := tlfCryptKeyGetter() 275 if err != nil { 276 return TLFWriterKeyBundleV2{}, TLFWriterKeyBundleV3{}, err 277 } 278 // Sanity check. 279 if len(keys) != int(keyGen) { 280 return TLFWriterKeyBundleV2{}, TLFWriterKeyBundleV3{}, 281 fmt.Errorf("expected %d keys, found %d", keyGen, len(keys)) 282 } 283 // Save the current key. 284 currKey := keys[len(keys)-1] 285 // Get rid of the most current generation as that's in the UserDeviceKeyInfoMap already. 286 keys = keys[:len(keys)-1] 287 // Encrypt the historic keys with the current key. 288 wkbV3.EncryptedHistoricTLFCryptKeys, err = kbfscrypto.EncryptTLFCryptKeys(codec, keys, currKey) 289 if err != nil { 290 return TLFWriterKeyBundleV2{}, TLFWriterKeyBundleV3{}, err 291 } 292 } 293 294 return wkbV2, wkbV3, nil 295 } 296 297 // TLFReaderKeyBundleV2 stores all the reader keys with reader 298 // permissions on a TLF. 299 type TLFReaderKeyBundleV2 struct { 300 RKeys UserDeviceKeyInfoMapV2 301 302 // M_e as described in § 4.1.1. Because devices can be added 303 // into the key generation after it is initially created (so 304 // those devices can get access to existing data), we track 305 // multiple ephemeral public keys; the one used by a 306 // particular device is specified by EPubKeyIndex in its 307 // TLFCryptoKeyInfo struct. This list is needed so a reader 308 // rekey doesn't modify the writer metadata. 309 TLFReaderEphemeralPublicKeys kbfscrypto.TLFEphemeralPublicKeys `codec:"readerEPubKey,omitempty"` 310 311 codec.UnknownFieldSetHandler 312 } 313 314 // IsReader returns true if the given user device is in the reader set. 315 func (trb TLFReaderKeyBundleV2) IsReader(user keybase1.UID, deviceKey kbfscrypto.CryptPublicKey) bool { 316 _, ok := trb.RKeys[user][deviceKey.KID()] 317 return ok 318 } 319 320 // TLFReaderKeyGenerationsV2 stores a slice of TLFReaderKeyBundleV2, 321 // where the last element is the current generation. 322 type TLFReaderKeyGenerationsV2 []TLFReaderKeyBundleV2 323 324 // LatestKeyGeneration returns the current key generation for this TLF. 325 func (rkg TLFReaderKeyGenerationsV2) LatestKeyGeneration() KeyGen { 326 return KeyGen(len(rkg)) 327 } 328 329 // IsReader returns whether or not the user+device is an authorized reader 330 // for the latest generation. 331 func (rkg TLFReaderKeyGenerationsV2) IsReader(user keybase1.UID, deviceKey kbfscrypto.CryptPublicKey) bool { 332 keyGen := rkg.LatestKeyGeneration() 333 if keyGen < 1 { 334 return false 335 } 336 return rkg[keyGen-1].IsReader(user, deviceKey) 337 } 338 339 // ToTLFReaderKeyBundleV3 converts a TLFReaderKeyGenerationsV2 to a TLFReaderkeyBundleV3. 340 func (rkg TLFReaderKeyGenerationsV2) ToTLFReaderKeyBundleV3( 341 codec kbfscodec.Codec, wkb TLFWriterKeyBundleV2) ( 342 TLFReaderKeyBundleV3, error) { 343 keyGen := rkg.LatestKeyGeneration() 344 if keyGen < FirstValidKeyGen { 345 return TLFReaderKeyBundleV3{}, errors.New("No key generations to convert") 346 } 347 348 rkbV3 := TLFReaderKeyBundleV3{ 349 Keys: make(UserDeviceKeyInfoMapV3), 350 } 351 352 // Copy the latest UserDeviceKeyInfoMap. 353 rkb := rkg[keyGen-FirstValidKeyGen] 354 355 // Copy all of the TLFReaderEphemeralPublicKeys. 356 rkbV3.TLFEphemeralPublicKeys = make(kbfscrypto.TLFEphemeralPublicKeys, 357 len(rkb.TLFReaderEphemeralPublicKeys)) 358 copy(rkbV3.TLFEphemeralPublicKeys[:], rkb.TLFReaderEphemeralPublicKeys) 359 360 // Track a mapping of old writer ephemeral pubkey index to new 361 // reader ephemeral pubkey index. 362 pubKeyIndicesMap := make(map[int]int) 363 364 // We need to copy these in a slightly annoying way to work around 365 // the negative index hack. In V3 readers always have their ePubKey 366 // in the TLFReaderEphemeralPublicKeys list. In V2 they only do if 367 // the index is negative. Otherwise it's in the writer's list. 368 for uid, dkim := range rkb.RKeys { 369 dkimV3 := make(DeviceKeyInfoMapV3) 370 for kid, info := range dkim { 371 var infoCopy TLFCryptKeyInfo 372 err := kbfscodec.Update(codec, &infoCopy, info) 373 if err != nil { 374 return TLFReaderKeyBundleV3{}, err 375 } 376 377 keyLocation, index, ePubKey, err := 378 GetEphemeralPublicKeyInfoV2(info, wkb, rkb) 379 if err != nil { 380 return TLFReaderKeyBundleV3{}, err 381 } 382 383 switch keyLocation { 384 case WriterEPubKeys: 385 // Map the old index in the writer list to a new index 386 // at the end of the reader list. 387 newIndex, ok := pubKeyIndicesMap[index] 388 if !ok { 389 rkbV3.TLFEphemeralPublicKeys = 390 append(rkbV3.TLFEphemeralPublicKeys, ePubKey) 391 // TODO: This index depends on 392 // map iteration order, which 393 // varies. Impose a consistent 394 // order on these indices. 395 newIndex = len(rkbV3.TLFEphemeralPublicKeys) - 1 396 pubKeyIndicesMap[index] = newIndex 397 } 398 infoCopy.EPubKeyIndex = newIndex 399 case ReaderEPubKeys: 400 // Use the real index in the reader list. 401 infoCopy.EPubKeyIndex = index 402 default: 403 return TLFReaderKeyBundleV3{}, fmt.Errorf("Unknown key location %s", keyLocation) 404 } 405 dkimV3[kbfscrypto.MakeCryptPublicKey(kid)] = infoCopy 406 } 407 rkbV3.Keys[uid] = dkimV3 408 } 409 return rkbV3, nil 410 }