github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/engine/crypto.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 "sync" 8 9 "github.com/keybase/client/go/kbcrypto" 10 "github.com/keybase/client/go/libkb" 11 keybase1 "github.com/keybase/client/go/protocol/keybase1" 12 "golang.org/x/crypto/nacl/box" 13 "golang.org/x/net/context" 14 ) 15 16 // getKeyMu synchronizes all accesses to the need to pull in pinentries/secret keys 17 // for this user. 18 var getKeyMu sync.Mutex 19 20 // GetMySecretKey uses ActiveDevice to get a secret key for the current user. 21 // 22 // It used to have functionality to load the user and prompt for a passphrase to 23 // unlock the keys, but that is outdated now. Either you are logged in and 24 // have your device keys cached, or you aren't. 25 // 26 // If the key isn't found in the ActiveDevice cache, this will return LoginRequiredError. 27 func GetMySecretKey(ctx context.Context, g *libkb.GlobalContext, secretKeyType libkb.SecretKeyType, reason string) (libkb.GenericKey, error) { 28 key, err := g.ActiveDevice.KeyByType(secretKeyType) 29 if err != nil { 30 if _, ok := err.(libkb.NotFoundError); ok { 31 g.Log.CDebugf(ctx, "GetMySecretKey: no device key of type %s in ActiveDevice, returning LoginRequiredError", secretKeyType) 32 return nil, libkb.LoginRequiredError{Context: "GetMySecretKey"} 33 } 34 g.Log.CDebugf(ctx, "GetMySecretKey(%s), unexpected error: %s", secretKeyType, err) 35 return nil, err 36 } 37 return key, nil 38 } 39 40 // GetMySecretKeyWithUID is like GetMySecretKey but returns an error if uid is not active. 41 func GetMySecretKeyWithUID(ctx context.Context, g *libkb.GlobalContext, uid keybase1.UID, secretKeyType libkb.SecretKeyType, reason string) (libkb.GenericKey, error) { 42 key, err := g.ActiveDevice.KeyByTypeWithUID(uid, secretKeyType) 43 if err != nil { 44 if _, ok := err.(libkb.NotFoundError); ok { 45 g.Log.CDebugf(ctx, "GetMySecretKeyWithUID: no device key of type %s in ActiveDevice, returning LoginRequiredError", secretKeyType) 46 return nil, libkb.LoginRequiredError{Context: "GetMySecretKey"} 47 } 48 g.Log.CDebugf(ctx, "GetMySecretKeyWithUID(%s), unexpected error: %s", secretKeyType, err) 49 return nil, err 50 } 51 return key, nil 52 } 53 54 // SignED25519 signs the given message with the current user's private 55 // signing key. 56 func SignED25519(ctx context.Context, g *libkb.GlobalContext, arg keybase1.SignED25519Arg) (ret keybase1.ED25519SignatureInfo, err error) { 57 signingKey, err := GetMySecretKey(ctx, g, libkb.DeviceSigningKeyType, arg.Reason) 58 if err != nil { 59 return 60 } 61 62 kp, ok := signingKey.(libkb.NaclSigningKeyPair) 63 if !ok || kp.Private == nil { 64 err = libkb.KeyCannotSignError{} 65 return 66 } 67 68 sig := kp.Private.Sign(arg.Msg) 69 publicKey := kp.Public 70 ret = keybase1.ED25519SignatureInfo{ 71 Sig: keybase1.ED25519Signature(sig), 72 PublicKey: keybase1.ED25519PublicKey(publicKey), 73 } 74 return 75 } 76 77 // SignED25519ForKBFS signs the given message with the current user's private 78 // signing key on behalf of KBFS. 79 func SignED25519ForKBFS(ctx context.Context, g *libkb.GlobalContext, arg keybase1.SignED25519ForKBFSArg) ( 80 ret keybase1.ED25519SignatureInfo, err error) { 81 signingKey, err := GetMySecretKey(ctx, g, libkb.DeviceSigningKeyType, arg.Reason) 82 if err != nil { 83 return 84 } 85 86 kp, ok := signingKey.(libkb.NaclSigningKeyPair) 87 if !ok || kp.Private == nil { 88 err = libkb.KeyCannotSignError{} 89 return 90 } 91 92 var sigInfo kbcrypto.NaclSigInfo 93 sigInfo, err = kp.SignV2(arg.Msg, kbcrypto.SignaturePrefixKBFS) 94 if err != nil { 95 return 96 } 97 publicKey := kp.Public 98 ret = keybase1.ED25519SignatureInfo{ 99 Sig: keybase1.ED25519Signature(sigInfo.Sig), 100 PublicKey: keybase1.ED25519PublicKey(publicKey), 101 } 102 return 103 } 104 105 // SignToString signs the given message with the current user's private 106 // signing key and outputs the serialized NaclSigInfo string. 107 func SignToString(ctx context.Context, g *libkb.GlobalContext, arg keybase1.SignToStringArg) (sig string, err error) { 108 signingKey, err := GetMySecretKey(ctx, g, libkb.DeviceSigningKeyType, arg.Reason) 109 if err != nil { 110 return 111 } 112 113 kp, ok := signingKey.(libkb.NaclSigningKeyPair) 114 if !ok || kp.Private == nil { 115 err = libkb.KeyCannotSignError{} 116 return 117 } 118 119 sig, _, err = kp.SignToString(arg.Msg) 120 return 121 } 122 123 // UnboxBytes32 decrypts the given message with the current user's 124 // private encryption key and the given nonce and peer public key. 125 func UnboxBytes32(ctx context.Context, g *libkb.GlobalContext, arg keybase1.UnboxBytes32Arg) (bytes32 keybase1.Bytes32, err error) { 126 encryptionKey, err := GetMySecretKey(ctx, g, libkb.DeviceEncryptionKeyType, arg.Reason) 127 if err != nil { 128 return 129 } 130 131 return unboxBytes32(encryptionKey, arg.EncryptedBytes32, arg.Nonce, arg.PeersPublicKey) 132 } 133 134 // UnboxBytes32Any will decrypt any of the KID, ciphertext, nonce 135 // bundles in arg.Bundles. Key preference order: cached device keys, 136 // cached paper keys, local device key, user-entered paper key. 137 // It returns the KID and bundle index along with the plaintext. 138 func UnboxBytes32Any(m libkb.MetaContext, getSecretUI func() libkb.SecretUI, arg keybase1.UnboxBytes32AnyArg) (res keybase1.UnboxAnyRes, err error) { 139 defer m.Trace("UnboxBytes32Any", &err)() 140 141 // find a matching secret key for a bundle in arg.Bundles 142 key, index, err := getMatchingSecretKey(m, getSecretUI, arg) 143 if err != nil { 144 return res, err 145 } 146 147 // decrypt the bundle's ciphertext 148 plaintext, err := unboxBytes32(key, arg.Bundles[index].Ciphertext, arg.Bundles[index].Nonce, arg.Bundles[index].PublicKey) 149 if err != nil { 150 return res, err 151 } 152 153 // return plaintext, kid, and index 154 res.Plaintext = plaintext 155 res.Kid = key.GetKID() 156 res.Index = index 157 158 return res, nil 159 } 160 161 func unboxBytes32(encryptionKey libkb.GenericKey, ciphertext keybase1.EncryptedBytes32, nonce keybase1.BoxNonce, peerPubKey keybase1.BoxPublicKey) (bytes32 keybase1.Bytes32, err error) { 162 kp, ok := encryptionKey.(libkb.NaclDHKeyPair) 163 if !ok { 164 err = libkb.KeyCannotDecryptError{} 165 return 166 } 167 if kp.Private == nil { 168 err = libkb.NoSecretKeyError{} 169 return 170 } 171 172 decryptedData, ok := box.Open(nil, ciphertext[:], (*[24]byte)(&nonce), (*[32]byte)(&peerPubKey), (*[32]byte)(kp.Private)) 173 if !ok { 174 err = libkb.DecryptionError{} 175 return 176 } 177 178 if len(decryptedData) != len(bytes32) { 179 err = libkb.DecryptionError{} 180 return 181 } 182 183 copy(bytes32[:], decryptedData) 184 return 185 186 } 187 188 func getMatchingSecretKey(m libkb.MetaContext, getSecretUI func() libkb.SecretUI, arg keybase1.UnboxBytes32AnyArg) (key libkb.GenericKey, index int, err error) { 189 // first check cached keys 190 key, index, err = matchingCachedKey(m, arg) 191 if err != nil { 192 return nil, 0, err 193 } 194 if key != nil { 195 return key, index, nil 196 } 197 198 m.Debug("getMatchingSecretKey: acquiring lock") 199 getKeyMu.Lock() 200 defer func() { 201 getKeyMu.Unlock() 202 m.Debug("getMatchingSecretKey: lock released") 203 }() 204 m.Debug("getMatchingSecretKey: lock acquired") 205 206 // check cache after acquiring lock 207 key, index, err = matchingCachedKey(m, arg) 208 if err != nil { 209 return nil, 0, err 210 } 211 if key != nil { 212 return key, index, nil 213 } 214 m.Debug("getMatchingSecretKey: no matching cached device key found") 215 216 // load the user 217 me, err := libkb.LoadMe(libkb.NewLoadUserArgWithMetaContext(m)) 218 if err != nil { 219 return nil, 0, err 220 } 221 222 // need secretUI now: 223 secretUI := getSecretUI() 224 225 // check the device key for this user 226 key, index, err = matchingDeviceKey(m, secretUI, arg, me) 227 if err != nil { 228 return nil, 0, err 229 } 230 if key != nil { 231 return key, index, nil 232 } 233 m.Debug("getMatchingSecretKey: no matching device key found") 234 235 if !arg.PromptPaper { 236 m.Debug("UnboxBytes32Any/getMatchingSecretKey: not checking paper keys (promptPaper == false)") 237 return nil, 0, libkb.NoSecretKeyError{} 238 } 239 240 // check the paper keys for this user 241 key, index, err = matchingPaperKey(m, secretUI, arg, me) 242 if err != nil { 243 return nil, 0, err 244 } 245 if key != nil { 246 return key, index, nil 247 } 248 249 return nil, 0, libkb.NoSecretKeyError{} 250 } 251 252 // check cached keys for arg.Bundles match. 253 func matchingCachedKey(m libkb.MetaContext, arg keybase1.UnboxBytes32AnyArg) (key libkb.GenericKey, index int, err error) { 254 // check device key first 255 dkey, err := m.ActiveDevice().EncryptionKey() 256 if err == nil && dkey != nil { 257 if n, ok := kidMatch(dkey, arg.Bundles); ok { 258 return dkey, n, nil 259 } 260 } 261 262 device := m.ActiveDevice().ProvisioningKey(m) 263 if device != nil { 264 pkey := device.EncryptionKey() 265 if n, ok := kidMatch(pkey, arg.Bundles); ok { 266 return pkey, n, nil 267 } 268 } 269 return nil, 0, nil 270 } 271 272 // check device key for arg.Bundles match. 273 func matchingDeviceKey(m libkb.MetaContext, secretUI libkb.SecretUI, arg keybase1.UnboxBytes32AnyArg, me *libkb.User) (key libkb.GenericKey, index int, err error) { 274 ekey, err := me.GetDeviceSubkey() 275 if err == nil { 276 if n, ok := kidMatch(ekey, arg.Bundles); ok { 277 // unlock this key 278 parg := libkb.SecretKeyPromptArg{ 279 Ska: libkb.SecretKeyArg{ 280 Me: me, 281 KeyType: libkb.DeviceEncryptionKeyType, 282 }, 283 SecretUI: secretUI, 284 Reason: arg.Reason, 285 UseCancelCache: true, 286 } 287 key, err := m.G().Keyrings.GetSecretKeyWithPrompt(m, parg) 288 if err != nil { 289 return nil, 0, err 290 } 291 return key, n, nil 292 } 293 294 m.Debug("matchingDeviceKey: no match found for ekey in arg.Bundles") 295 logNoMatch(m, ekey, arg.Bundles) 296 } else { 297 m.Debug("matchingDeviceKey: ignoring error getting device subkey: %s", err) 298 } 299 300 return nil, 0, nil 301 } 302 303 // check all the user's paper keys for arg.Bundles match 304 func matchingPaperKey(m libkb.MetaContext, secretUI libkb.SecretUI, arg keybase1.UnboxBytes32AnyArg, me *libkb.User) (key libkb.GenericKey, index int, err error) { 305 cki := me.GetComputedKeyInfos() 306 if cki == nil { 307 return nil, 0, nil 308 } 309 var matchingPaper []*libkb.Device 310 for _, pdev := range cki.PaperDevices() { 311 enckey, err := me.GetComputedKeyFamily().GetEncryptionSubkeyForDevice(pdev.ID) 312 if err != nil { 313 return nil, 0, err 314 } 315 if _, ok := kidMatch(enckey, arg.Bundles); ok { 316 m.Debug("matching paper key: %s", *pdev.Description) 317 matchingPaper = append(matchingPaper, pdev) 318 } 319 } 320 if len(matchingPaper) == 0 { 321 m.Debug("no matching paper keys found") 322 return nil, 0, nil 323 } 324 325 phrase, err := libkb.GetPaperKeyForCryptoPassphrase(m, secretUI, arg.Reason, matchingPaper) 326 if err != nil { 327 return nil, 0, err 328 } 329 paperPhrase, err := libkb.NewPaperKeyPhraseCheckVersion(m, phrase) 330 if err != nil { 331 return nil, 0, err 332 } 333 334 bkarg := &PaperKeyGenArg{ 335 Passphrase: paperPhrase, 336 SkipPush: true, 337 } 338 bkeng := NewPaperKeyGen(m.G(), bkarg) 339 if err := RunEngine2(m, bkeng); err != nil { 340 return nil, 0, err 341 } 342 343 // find the index for the key they entered (and make sure the key they entered matches) 344 if n, ok := kidMatch(bkeng.EncKey(), arg.Bundles); ok { 345 m.ActiveDevice().CacheProvisioningKey(m, bkeng.DeviceWithKeys()) 346 return bkeng.EncKey(), n, nil 347 } 348 349 return nil, 0, nil 350 } 351 352 func kidMatch(key libkb.GenericKey, bundles []keybase1.CiphertextBundle) (int, bool) { 353 if key == nil { 354 return -1, false 355 } 356 kid := key.GetKID() 357 for i, bundle := range bundles { 358 if kid.Equal(bundle.Kid) { 359 return i, true 360 } 361 } 362 return -1, false 363 } 364 365 func logNoMatch(m libkb.MetaContext, key libkb.GenericKey, bundles []keybase1.CiphertextBundle) { 366 if key == nil { 367 m.Debug("logNoMatch: key is nil") 368 return 369 } 370 kid := key.GetKID() 371 m.Debug("logNoMatch: desired kid: %s", kid) 372 for i, bundle := range bundles { 373 m.Debug("logNoMatch: kid %d: %s (%v)", i, bundle.Kid, kid.Equal(bundle.Kid)) 374 } 375 }