github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/kbfsmd/key_bundle.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/kbfscrypto" 11 "github.com/keybase/client/go/protocol/keybase1" 12 "github.com/keybase/go-codec/codec" 13 ) 14 15 // TLFCryptKeyInfo is a per-device key half entry in the 16 // TLF{Writer,Reader}KeyBundleV{2,3}. 17 type TLFCryptKeyInfo struct { 18 ClientHalf kbfscrypto.EncryptedTLFCryptKeyClientHalf 19 ServerHalfID kbfscrypto.TLFCryptKeyServerHalfID 20 EPubKeyIndex int `codec:"i,omitempty"` 21 22 codec.UnknownFieldSetHandler 23 } 24 25 // DevicePublicKeys is a set of a user's devices (identified by the 26 // corresponding device CryptPublicKey). 27 type DevicePublicKeys map[kbfscrypto.CryptPublicKey]bool 28 29 // Equals returns whether both sets of keys are equal. 30 func (dpk DevicePublicKeys) Equals(other DevicePublicKeys) bool { 31 if len(dpk) != len(other) { 32 return false 33 } 34 35 for k := range dpk { 36 if !other[k] { 37 return false 38 } 39 } 40 41 return true 42 } 43 44 // UserDevicePublicKeys is a map from users to that user's set of devices. 45 type UserDevicePublicKeys map[keybase1.UID]DevicePublicKeys 46 47 // RemoveKeylessUsersForTest returns a new UserDevicePublicKeys objects with 48 // all the users with an empty DevicePublicKeys removed. 49 func (udpk UserDevicePublicKeys) RemoveKeylessUsersForTest() UserDevicePublicKeys { 50 udpkRemoved := make(UserDevicePublicKeys) 51 for u, dpk := range udpk { 52 if len(dpk) > 0 { 53 udpkRemoved[u] = dpk 54 } 55 } 56 return udpkRemoved 57 } 58 59 // Equals returns whether both sets of users are equal, and they all 60 // have corresponding equal sets of keys. 61 func (udpk UserDevicePublicKeys) Equals(other UserDevicePublicKeys) bool { 62 if len(udpk) != len(other) { 63 return false 64 } 65 66 for u, dpk := range udpk { 67 if !dpk.Equals(other[u]) { 68 return false 69 } 70 } 71 72 return true 73 } 74 75 // DeviceKeyServerHalves is a map from a user devices (identified by the 76 // corresponding device CryptPublicKey) to corresponding key server 77 // halves. 78 type DeviceKeyServerHalves map[kbfscrypto.CryptPublicKey]kbfscrypto.TLFCryptKeyServerHalf 79 80 // UserDeviceKeyServerHalves maps a user's keybase UID to their 81 // DeviceServerHalves map. 82 type UserDeviceKeyServerHalves map[keybase1.UID]DeviceKeyServerHalves 83 84 // MergeUsers returns a UserDeviceKeyServerHalves that contains all 85 // the users in serverHalves and other, which must be disjoint. This 86 // isn't a deep copy. 87 func (serverHalves UserDeviceKeyServerHalves) MergeUsers( 88 other UserDeviceKeyServerHalves) (UserDeviceKeyServerHalves, error) { 89 merged := make(UserDeviceKeyServerHalves, 90 len(serverHalves)+len(other)) 91 for uid, deviceServerHalves := range serverHalves { 92 merged[uid] = deviceServerHalves 93 } 94 for uid, deviceServerHalves := range other { 95 if _, ok := merged[uid]; ok { 96 return nil, fmt.Errorf( 97 "user %s is in both UserDeviceKeyServerHalves", 98 uid) 99 } 100 merged[uid] = deviceServerHalves 101 } 102 return merged, nil 103 } 104 105 // splitTLFCryptKey splits the given TLFCryptKey into two parts -- the 106 // client-side part (which is encrypted with the given keys), and the 107 // server-side part, which will be uploaded to the server. 108 func splitTLFCryptKey(uid keybase1.UID, 109 tlfCryptKey kbfscrypto.TLFCryptKey, 110 ePrivKey kbfscrypto.TLFEphemeralPrivateKey, ePubIndex int, 111 pubKey kbfscrypto.CryptPublicKey) ( 112 TLFCryptKeyInfo, kbfscrypto.TLFCryptKeyServerHalf, error) { 113 // * create a new random server half 114 // * mask it with the key to get the client half 115 // * encrypt the client half 116 var serverHalf kbfscrypto.TLFCryptKeyServerHalf 117 serverHalf, err := kbfscrypto.MakeRandomTLFCryptKeyServerHalf() 118 if err != nil { 119 return TLFCryptKeyInfo{}, kbfscrypto.TLFCryptKeyServerHalf{}, err 120 } 121 122 clientHalf := kbfscrypto.MaskTLFCryptKey(serverHalf, tlfCryptKey) 123 124 var encryptedClientHalf kbfscrypto.EncryptedTLFCryptKeyClientHalf 125 encryptedClientHalf, err = 126 kbfscrypto.EncryptTLFCryptKeyClientHalf(ePrivKey, pubKey, clientHalf) 127 if err != nil { 128 return TLFCryptKeyInfo{}, kbfscrypto.TLFCryptKeyServerHalf{}, err 129 } 130 131 var serverHalfID kbfscrypto.TLFCryptKeyServerHalfID 132 serverHalfID, err = 133 kbfscrypto.MakeTLFCryptKeyServerHalfID(uid, pubKey, serverHalf) 134 if err != nil { 135 return TLFCryptKeyInfo{}, kbfscrypto.TLFCryptKeyServerHalf{}, err 136 } 137 138 clientInfo := TLFCryptKeyInfo{ 139 ClientHalf: encryptedClientHalf, 140 ServerHalfID: serverHalfID, 141 EPubKeyIndex: ePubIndex, 142 } 143 return clientInfo, serverHalf, nil 144 } 145 146 // DeviceServerHalfRemovalInfo is a map from a device's crypt public 147 // key to a list of server halves to remove. 148 type DeviceServerHalfRemovalInfo map[kbfscrypto.CryptPublicKey][]kbfscrypto.TLFCryptKeyServerHalfID 149 150 // UserServerHalfRemovalInfo contains a map from devices (identified 151 // by its crypt public key) to a list of IDs for key server halves to 152 // remove (one per key generation). For logging purposes, it also 153 // contains a bool indicating whether all of the user's devices were 154 // removed. 155 type UserServerHalfRemovalInfo struct { 156 UserRemoved bool 157 DeviceServerHalfIDs DeviceServerHalfRemovalInfo 158 } 159 160 // addGeneration merges the keys in genInfo (which must be one per 161 // device) into ri. genInfo must have the same UserRemoved value and 162 // keys as ri. 163 func (ri UserServerHalfRemovalInfo) addGeneration( 164 uid keybase1.UID, genInfo UserServerHalfRemovalInfo) error { 165 if ri.UserRemoved != genInfo.UserRemoved { 166 return fmt.Errorf( 167 "UserRemoved=%t != generation UserRemoved=%t for user %s", 168 ri.UserRemoved, genInfo.UserRemoved, uid) 169 } 170 171 if len(ri.DeviceServerHalfIDs) != len(genInfo.DeviceServerHalfIDs) { 172 return fmt.Errorf( 173 "device count=%d != generation device count=%d for user %s", 174 len(ri.DeviceServerHalfIDs), 175 len(genInfo.DeviceServerHalfIDs), uid) 176 } 177 178 idCount := -1 179 for key, serverHalfIDs := range genInfo.DeviceServerHalfIDs { 180 if idCount == -1 { 181 idCount = len(ri.DeviceServerHalfIDs[key]) 182 } else { 183 localIDCount := len(ri.DeviceServerHalfIDs[key]) 184 if localIDCount != idCount { 185 return fmt.Errorf( 186 "expected %d keys, got %d for user %s and device %s", 187 idCount, localIDCount, uid, key) 188 } 189 } 190 191 if len(serverHalfIDs) != 1 { 192 return fmt.Errorf( 193 "expected exactly one key, got %d for user %s and device %s", 194 len(serverHalfIDs), uid, key) 195 } 196 if _, ok := ri.DeviceServerHalfIDs[key]; !ok { 197 return fmt.Errorf( 198 "no generation info for user %s and device %s", 199 uid, key) 200 } 201 ri.DeviceServerHalfIDs[key] = append( 202 ri.DeviceServerHalfIDs[key], serverHalfIDs[0]) 203 } 204 205 return nil 206 } 207 208 // ServerHalfRemovalInfo is a map from users and devices to a list of 209 // server half IDs to remove from the server. 210 type ServerHalfRemovalInfo map[keybase1.UID]UserServerHalfRemovalInfo 211 212 // AddGeneration merges the keys in genInfo (which must be one per 213 // device) into info. genInfo must have the same users as info. 214 func (info ServerHalfRemovalInfo) AddGeneration( 215 genInfo ServerHalfRemovalInfo) error { 216 if len(info) != len(genInfo) { 217 return fmt.Errorf( 218 "user count=%d != generation user count=%d", 219 len(info), len(genInfo)) 220 } 221 222 for uid, removalInfo := range genInfo { 223 if _, ok := info[uid]; !ok { 224 return fmt.Errorf("no generation info for user %s", uid) 225 } 226 err := info[uid].addGeneration(uid, removalInfo) 227 if err != nil { 228 return err 229 } 230 } 231 return nil 232 } 233 234 // MergeUsers returns a ServerHalfRemovalInfo that contains all the 235 // users in info and other, which must be disjoint. This isn't a deep 236 // copy. 237 func (info ServerHalfRemovalInfo) MergeUsers( 238 other ServerHalfRemovalInfo) (ServerHalfRemovalInfo, error) { 239 merged := make(ServerHalfRemovalInfo, len(info)+len(other)) 240 for uid, removalInfo := range info { 241 merged[uid] = removalInfo 242 } 243 for uid, removalInfo := range other { 244 if _, ok := merged[uid]; ok { 245 return nil, fmt.Errorf( 246 "user %s is in both ServerHalfRemovalInfos", 247 uid) 248 } 249 merged[uid] = removalInfo 250 } 251 return merged, nil 252 }