github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/engine/saltpack_user_keyfinder.go (about) 1 // Copyright 2015 Keybase, Inc. All rights reserved. Use of 2 // this source code is governed by the included BSD license. 3 4 package engine 5 6 import ( 7 "fmt" 8 9 "github.com/keybase/client/go/libkb" 10 11 keybase1 "github.com/keybase/client/go/protocol/keybase1" 12 ) 13 14 // SaltpackUserKeyfinder is an engine to find Per User Keys (PUK). Users can also be loaded by assertions, possibly tracking them if necessary. 15 // This engine does not find per team keys, which capability is implemented by SaltpackRecipientKeyfinder in the saltpackKeyHelpers package. 16 type SaltpackUserKeyfinder struct { 17 Arg libkb.SaltpackRecipientKeyfinderArg 18 RecipientEntityKeyMap map[keybase1.UserOrTeamID]([]keybase1.KID) 19 RecipientDeviceAndPaperKeyMap map[keybase1.UID]([]keybase1.KID) 20 UsingSBS bool 21 SBSAssertion string 22 } 23 24 var _ libkb.Engine2 = (*SaltpackUserKeyfinder)(nil) 25 var _ libkb.SaltpackRecipientKeyfinderEngineInterface = (*SaltpackUserKeyfinder)(nil) 26 27 // NewSaltpackUserKeyfinderAsInterface creates a SaltpackUserKeyfinder engine. 28 func NewSaltpackUserKeyfinderAsInterface(arg libkb.SaltpackRecipientKeyfinderArg) libkb.SaltpackRecipientKeyfinderEngineInterface { 29 return NewSaltpackUserKeyfinder(arg) 30 } 31 32 func NewSaltpackUserKeyfinder(arg libkb.SaltpackRecipientKeyfinderArg) *SaltpackUserKeyfinder { 33 return &SaltpackUserKeyfinder{ 34 Arg: arg, 35 RecipientEntityKeyMap: make(map[keybase1.UserOrTeamID]([]keybase1.KID)), 36 RecipientDeviceAndPaperKeyMap: make(map[keybase1.UID]([]keybase1.KID)), 37 } 38 } 39 40 // Name is the unique engine name. 41 func (e *SaltpackUserKeyfinder) Name() string { 42 return "SaltpackUserKeyfinder" 43 } 44 45 // Prereqs returns the engine prereqs. 46 func (e *SaltpackUserKeyfinder) Prereqs() Prereqs { 47 return Prereqs{} 48 } 49 50 // RequiredUIs returns the required UIs. 51 func (e *SaltpackUserKeyfinder) RequiredUIs() []libkb.UIKind { 52 return []libkb.UIKind{} 53 } 54 55 // SubConsumers returns the other UI consumers for this engine. 56 func (e *SaltpackUserKeyfinder) SubConsumers() []libkb.UIConsumer { 57 return []libkb.UIConsumer{} 58 } 59 60 func (e *SaltpackUserKeyfinder) GetPublicKIDs() []keybase1.KID { 61 var r []keybase1.KID 62 for _, keys := range e.RecipientDeviceAndPaperKeyMap { 63 r = append(r, keys...) 64 } 65 for _, keys := range e.RecipientEntityKeyMap { 66 r = append(r, keys...) 67 } 68 69 return r 70 } 71 72 func (e *SaltpackUserKeyfinder) GetSymmetricKeys() []libkb.SaltpackReceiverSymmetricKey { 73 return []libkb.SaltpackReceiverSymmetricKey{} 74 } 75 76 func (e *SaltpackUserKeyfinder) UsedUnresolvedSBSAssertion() (bool, string) { 77 return e.UsingSBS, e.SBSAssertion 78 } 79 80 func (e *SaltpackUserKeyfinder) Run(m libkb.MetaContext) (err error) { 81 defer m.Trace("SaltpackUserKeyfinder#Run", &err)() 82 83 if len(e.Arg.TeamRecipients) != 0 { 84 m.Debug("tried to use SaltpackUserKeyfinder for a team. This should never happen") 85 return fmt.Errorf("cannot find keys for teams") 86 } 87 88 err = e.AddOwnKeysIfNeeded(m) 89 if err != nil { 90 return err 91 } 92 93 err = e.identifyAndAddRecipients(m) 94 if err != nil { 95 return err 96 } 97 return nil 98 } 99 100 func (e *SaltpackUserKeyfinder) AddOwnKeysIfNeeded(m libkb.MetaContext) error { 101 if e.Arg.NoSelfEncrypt { 102 return nil 103 } 104 if !m.ActiveDevice().Valid() { 105 return libkb.NewLoginRequiredError("need to be logged in or use --no-self-encrypt") 106 } 107 arg := libkb.NewLoadUserArgWithMetaContext(m).WithUID(m.ActiveDevice().UID()).WithForcePoll(!e.Arg.NoForcePoll) 108 upak, _, err := m.G().GetUPAKLoader().LoadV2(arg) 109 if err != nil { 110 return err 111 } 112 return e.AddUserRecipient(m, &upak.Current) 113 } 114 115 // identifyAndAddRecipients adds the KID corresponding to each recipient to the recipientMap 116 func (e *SaltpackUserKeyfinder) identifyAndAddRecipients(m libkb.MetaContext) error { 117 for _, u := range e.Arg.Recipients { 118 // TODO make these lookups in parallel (maybe using sync.WaitGroup) 119 upk, err := e.IdentifyUser(m, u) // For existing users 120 switch { 121 case err == nil: 122 // nothing to do here 123 case libkb.IsIdentifyProofError(err): 124 return fmt.Errorf("Cannot encrypt for %v as their account has changed since you last followed them (it might have been compromised!): please review their identity (with `keybase follow %v`) and then try again (err = %v)", u, u, err) 125 case libkb.IsNotFoundError(err) || libkb.IsResolutionError(err): 126 return fmt.Errorf("Cannot find keys for %v: it is not an assertion for a registered user (err = %v)", u, err) 127 default: 128 return fmt.Errorf("Error while adding keys for %v: %v", u, err) 129 } 130 err = e.AddUserRecipient(m, upk) 131 if err != nil { 132 return err 133 } 134 } 135 return nil 136 } 137 138 func (e *SaltpackUserKeyfinder) IdentifyUser(m libkb.MetaContext, user string) (upk *keybase1.UserPlusKeysV2, err error) { 139 140 Arg := keybase1.Identify2Arg{ 141 UserAssertion: user, 142 Reason: keybase1.IdentifyReason{ 143 Type: keybase1.IdentifyReasonType_ENCRYPT, 144 }, 145 AlwaysBlock: true, 146 IdentifyBehavior: keybase1.TLFIdentifyBehavior_SALTPACK, 147 } 148 eng := NewResolveThenIdentify2(m.G(), &Arg) 149 if err := RunEngine2(m, eng); err != nil { 150 return nil, err 151 } 152 153 engRes, err := eng.Result(m) 154 if err != nil { 155 return nil, err 156 } 157 158 return &engRes.Upk.Current, nil 159 } 160 161 func (e *SaltpackUserKeyfinder) hasRecipientDeviceOrPaperKeys(id keybase1.UID) bool { 162 _, ok := e.RecipientDeviceAndPaperKeyMap[id] 163 return ok 164 } 165 166 func (e *SaltpackUserKeyfinder) hasRecipientEntityKeys(id keybase1.UserOrTeamID) bool { 167 _, ok := e.RecipientEntityKeyMap[id] 168 return ok 169 } 170 171 func (e *SaltpackUserKeyfinder) AddUserRecipient(m libkb.MetaContext, upk *keybase1.UserPlusKeysV2) error { 172 err := e.AddDeviceAndPaperKeys(m, upk) 173 err2 := e.AddPUK(m, upk) 174 175 // If we managed to add at least one key for upk, we are happy. 176 if (!(e.Arg.UseDeviceKeys || e.Arg.UsePaperKeys) || err != nil) && (!e.Arg.UseEntityKeys || err2 != nil) { 177 return libkb.PickFirstError(err, err2) 178 } 179 return nil 180 } 181 182 func (e *SaltpackUserKeyfinder) isPaperEncryptionKey(key *keybase1.PublicKeyV2NaCl, deviceKeys *(map[keybase1.KID]keybase1.PublicKeyV2NaCl)) bool { 183 return libkb.KIDIsDeviceEncrypt(key.Base.Kid) && key.Parent != nil && (*deviceKeys)[*key.Parent].DeviceType == keybase1.DeviceTypeV2_PAPER 184 } 185 186 // AddPUK returns no error if it adds at least one key (or no paper keys and device keys were requested), otherwise it returns a libkb.NoNaClEncryptionKeyError 187 func (e *SaltpackUserKeyfinder) AddDeviceAndPaperKeys(m libkb.MetaContext, upk *keybase1.UserPlusKeysV2) error { 188 if !e.Arg.UsePaperKeys && !e.Arg.UseDeviceKeys { 189 // No need to add anything 190 return nil 191 } 192 193 if e.hasRecipientDeviceOrPaperKeys(upk.Uid) { 194 // This user's keys were already added 195 return nil 196 } 197 198 var keys []keybase1.KID 199 200 hasPaperKey := false 201 hasDeviceKey := false 202 hasPUK := len(upk.PerUserKeys) > 0 203 204 for KID, key := range upk.DeviceKeys { 205 // Note: for Nacl encryption keys, the DeviceType field is not set, so we need to look at the "parent" signing key 206 if e.isPaperEncryptionKey(&key, &upk.DeviceKeys) { 207 hasPaperKey = true 208 if e.Arg.UsePaperKeys { 209 keys = append(keys, KID) 210 m.Debug("adding user %v's paper key", upk.Username) 211 } 212 } 213 214 if libkb.KIDIsDeviceEncrypt(KID) && !e.isPaperEncryptionKey(&key, &upk.DeviceKeys) { 215 hasDeviceKey = true 216 if e.Arg.UseDeviceKeys { 217 keys = append(keys, KID) 218 m.Debug("adding user %v's device key", upk.Username) 219 } 220 } 221 } 222 223 e.RecipientDeviceAndPaperKeyMap[upk.Uid] = keys 224 225 if len(keys) == 0 { 226 m.Debug("did not add any device or paper keys for %v", upk.Username) 227 return libkb.NoNaClEncryptionKeyError{ 228 Username: upk.Username, 229 HasPGPKey: len(upk.PGPKeys) > 0, 230 HasPUK: hasPUK, 231 HasDeviceKey: hasDeviceKey, 232 HasPaperKey: hasPaperKey, 233 } 234 } 235 236 return nil 237 } 238 239 // AddPUK returns no error unless the user has no PUK, in which case it returns a libkb.NoNaClEncryptionKeyError 240 func (e *SaltpackUserKeyfinder) AddPUK(m libkb.MetaContext, upk *keybase1.UserPlusKeysV2) error { 241 if !e.Arg.UseEntityKeys { 242 // No need to add anything 243 return nil 244 } 245 246 if e.hasRecipientEntityKeys(upk.Uid.AsUserOrTeam()) { 247 // This user's keys were already added 248 return nil 249 } 250 251 hasPUK := len(upk.PerUserKeys) > 0 252 253 if !hasPUK { 254 hasPaperKey := false 255 hasDeviceKey := false 256 for KID, key := range upk.DeviceKeys { 257 if e.isPaperEncryptionKey(&key, &upk.DeviceKeys) { 258 hasPaperKey = true 259 } 260 if libkb.KIDIsDeviceEncrypt(KID) && !e.isPaperEncryptionKey(&key, &upk.DeviceKeys) { 261 hasDeviceKey = true 262 } 263 } 264 265 m.Debug("did not add any per user keys for %v", upk.Username) 266 return libkb.NoNaClEncryptionKeyError{ 267 Username: upk.Username, 268 HasPGPKey: len(upk.PGPKeys) > 0, 269 HasPUK: hasPUK, 270 HasDeviceKey: hasDeviceKey, 271 HasPaperKey: hasPaperKey, 272 } 273 } 274 275 // We ensured above that the user has a PUK, so the loop below will be executed at least once 276 maxGen := -1 277 var lk keybase1.KID 278 for _, k := range upk.PerUserKeys { 279 if k.Gen > maxGen { 280 maxGen = k.Gen 281 lk = k.EncKID 282 } 283 } 284 if lk == "" { 285 panic("This should never happen, user has a PUK with a nil KID") 286 } 287 288 m.Debug("adding user %v's latest per user key", upk.Username) 289 e.RecipientEntityKeyMap[upk.Uid.AsUserOrTeam()] = []keybase1.KID{lk} 290 291 return nil 292 }