github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/engine/pgp_import_key.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 // 7 // engine.PGPKeyImportEngine is a class for optionally generating PGP keys, 8 // and pushing them into the keybase sigchain via the Delegator. 9 // 10 11 import ( 12 "bytes" 13 "errors" 14 "strings" 15 16 "github.com/keybase/client/go/libkb" 17 keybase1 "github.com/keybase/client/go/protocol/keybase1" 18 ) 19 20 type PGPKeyImportEngine struct { 21 me *libkb.User 22 bundle *libkb.PGPKeyBundle 23 arg PGPKeyImportEngineArg 24 epk string 25 del *libkb.Delegator 26 libkb.Contextified 27 } 28 29 type PGPKeyImportEngineArg struct { 30 Gen *libkb.PGPGenArg 31 Pregen *libkb.PGPKeyBundle 32 SigningKey libkb.GenericKey 33 Me *libkb.User 34 Lks *libkb.LKSec 35 NoSave bool 36 PushSecret bool 37 OnlySave bool 38 AllowMulti bool 39 DoExport bool // export to GPG keychain? 40 ExportEncrypted bool // encrypt secret key before exporting to GPG? 41 DoUnlock bool 42 GPGFallback bool 43 PreloadTsec libkb.Triplesec 44 PreloadStreamGen libkb.PassphraseGeneration 45 } 46 47 func NewPGPKeyImportEngineFromBytes(g *libkb.GlobalContext, key []byte, pushPrivate bool) (eng *PGPKeyImportEngine, err error) { 48 var bundle *libkb.PGPKeyBundle 49 var w *libkb.Warnings 50 if libkb.IsArmored(key) { 51 bundle, w, err = libkb.ReadPrivateKeyFromString(string(key)) 52 } else { 53 bundle, w, err = libkb.ReadOneKeyFromBytes(key) 54 } 55 if err != nil { 56 return 57 } 58 w.Warn(g) 59 arg := PGPKeyImportEngineArg{ 60 Pregen: bundle, 61 PushSecret: pushPrivate, 62 AllowMulti: true, 63 DoExport: false, 64 DoUnlock: true, 65 } 66 eng = NewPGPKeyImportEngine(g, arg) 67 return 68 } 69 70 func (e *PGPKeyImportEngine) loadMe(m libkb.MetaContext) (err error) { 71 if e.me = e.arg.Me; e.me != nil { 72 return 73 } 74 e.me, err = libkb.LoadMe(libkb.NewLoadUserArgWithMetaContext(m).WithPublicKeyOptional()) 75 return err 76 } 77 78 func (e *PGPKeyImportEngine) generateKey(m libkb.MetaContext) (err error) { 79 gen := e.arg.Gen 80 if err = gen.CreatePGPIDs(); err != nil { 81 return 82 } 83 e.bundle, err = libkb.GeneratePGPKeyBundle(m.G(), *gen, m.UIs().LogUI) 84 return 85 } 86 87 func (e *PGPKeyImportEngine) saveLKS(m libkb.MetaContext) (err error) { 88 89 defer m.Trace("PGPKeyImportEngine::saveLKS", &err)() 90 91 lks := e.arg.Lks 92 if lks == nil { 93 lks, err = libkb.NewLKSecForEncrypt(m, m.UIs().SecretUI, e.me.GetUID()) 94 if err != nil { 95 return err 96 } 97 } 98 _, err = libkb.WriteLksSKBToKeyring(m, e.bundle, lks) 99 return 100 } 101 102 var ErrKeyGenArgNoDefNoCustom = errors.New("invalid args: NoDefPGPUid set, but no custom PGPUids") 103 104 func NewPGPKeyImportEngine(g *libkb.GlobalContext, arg PGPKeyImportEngineArg) *PGPKeyImportEngine { 105 return &PGPKeyImportEngine{arg: arg, Contextified: libkb.NewContextified(g)} 106 } 107 108 func (e *PGPKeyImportEngine) Name() string { 109 return "PGPKeyImportEngine" 110 } 111 112 func (e *PGPKeyImportEngine) Prereqs() Prereqs { 113 return Prereqs{} 114 } 115 116 func (e *PGPKeyImportEngine) RequiredUIs() []libkb.UIKind { 117 return []libkb.UIKind{ 118 libkb.LogUIKind, 119 libkb.SecretUIKind, 120 } 121 } 122 123 func (e *PGPKeyImportEngine) SubConsumers() []libkb.UIConsumer { 124 return nil 125 } 126 127 func (e *PGPKeyImportEngine) init() (err error) { 128 if e.arg.Gen != nil { 129 err = e.arg.Gen.Init() 130 } 131 return err 132 } 133 134 func (e *PGPKeyImportEngine) testExisting() (err error) { 135 return PGPCheckMulti(e.me, e.arg.AllowMulti) 136 } 137 138 // checkPregenPrivate makes sure that the pregenerated key is a 139 // private key. 140 func (e *PGPKeyImportEngine) checkPregenPrivate() error { 141 if e.arg.Pregen == nil { 142 return nil 143 } 144 if e.arg.Pregen.HasSecretKey() || e.arg.GPGFallback { 145 return nil 146 } 147 return libkb.NoSecretKeyError{} 148 } 149 150 func (e *PGPKeyImportEngine) checkExistingKey(m libkb.MetaContext) error { 151 // Check if we have a public key that matches 152 pgps := e.me.GetActivePGPKeys(false) 153 for _, key := range pgps { 154 if e.GetKID() != key.GetKID() { 155 continue 156 } 157 158 e.G().Log.Info("Key %s already exists. Only importing the private key.", e.GetKID()) 159 e.arg.OnlySave = true 160 break 161 } 162 163 return nil 164 } 165 166 func (e *PGPKeyImportEngine) Run(m libkb.MetaContext) (err error) { 167 defer m.Trace("PGPKeyImportEngine::Run", &err)() 168 169 if err = e.init(); err != nil { 170 return err 171 } 172 173 if err = e.loadMe(m); err != nil { 174 switch err.(type) { 175 case libkb.SelfNotFoundError: 176 return libkb.LoginRequiredError{} 177 default: 178 return err 179 } 180 } 181 182 if e.arg.PushSecret { 183 if err = e.checkRandomPassword(m); err != nil { 184 return err 185 } 186 } 187 188 if err = e.checkPregenPrivate(); err != nil { 189 return err 190 } 191 192 if !e.arg.OnlySave { 193 if err = e.testExisting(); err != nil { 194 return err 195 } 196 197 if err = e.loadDelegator(m); err != nil { 198 switch err.(type) { 199 case libkb.NoUsernameError: 200 return libkb.LoginRequiredError{} 201 default: 202 return err 203 } 204 } 205 } 206 207 if err = e.generate(m); err != nil { 208 return err 209 } 210 211 if err = e.unlock(m); err != nil { 212 return err 213 } 214 215 if err := e.checkExistingKey(m); err != nil { 216 return err 217 } 218 219 if err = e.saveKey(m); err != nil { 220 return err 221 } 222 223 if !e.arg.OnlySave { 224 if err = e.push(m); err != nil { 225 return err 226 } 227 if err = e.exportToGPG(m); err != nil { 228 return GPGExportingError{err, true /* inPGPGen */} 229 } 230 } else if e.arg.PushSecret { 231 if err = e.pushSecretOnly(m); err != nil { 232 return err 233 } 234 } 235 236 return nil 237 } 238 239 func (e *PGPKeyImportEngine) checkRandomPassword(mctx libkb.MetaContext) error { 240 passphraseState, err := libkb.LoadPassphraseState(mctx) 241 if err != nil { 242 return err 243 } 244 if passphraseState == keybase1.PassphraseState_RANDOM { 245 return libkb.NewPushSecretWithoutPasswordError("You need to set your password first before uploading secret keys") 246 } 247 return nil 248 } 249 250 // clonePGPKeyBundle returns an approximate deep copy of PGPKeyBundle 251 // by exporting and re-importing PGPKeyBundle. If PGP key contains 252 // something that is not supported by either go-crypto exporter or 253 // importer, that information will be lost. 254 func clonePGPKeyBundle(bundle *libkb.PGPKeyBundle) (*libkb.PGPKeyBundle, error) { 255 var buf bytes.Buffer 256 if err := bundle.SerializePrivate(&buf); err != nil { 257 return nil, err 258 } 259 res, _, err := libkb.ReadOneKeyFromBytes(buf.Bytes()) 260 if err != nil { 261 return nil, err 262 } 263 return res, nil 264 } 265 266 func (e *PGPKeyImportEngine) exportToGPG(m libkb.MetaContext) (err error) { 267 if !e.arg.DoExport || e.arg.Pregen != nil { 268 m.Debug("| Skipping export to GPG") 269 return nil 270 } 271 gpg := e.G().GetGpgClient() 272 273 ok, err := gpg.CanExec(m) 274 if err != nil { 275 m.Debug("Not saving new key to GPG. Error in gpg.CanExec(): %s", err) 276 // libkb/util_*.go:canExec() can return generic errors, just ignore them 277 // in this situation since export to gpg is on by default in the client 278 // pgp gen command. 279 return nil 280 } 281 if !ok { 282 m.Debug("Not saving new key to GPG since no gpg install was found") 283 return nil 284 } 285 286 exportedBundle := e.bundle 287 288 if e.arg.ExportEncrypted { 289 m.Debug("Encrypting key with passphrase before exporting") 290 desc := "Exporting key to GPG keychain. Enter passphrase to protect the key. Secure passphrases have at least 8 characters." 291 pRes, err := GetPGPExportPassphrase(m, m.UIs().SecretUI, desc) 292 if err != nil { 293 return err 294 } 295 // Avoid mutating e.bundle. 296 if exportedBundle, err = clonePGPKeyBundle(e.bundle); err != nil { 297 return err 298 } 299 if err = libkb.EncryptPGPKey(exportedBundle.Entity, pRes.Passphrase); err != nil { 300 return err 301 } 302 } 303 304 // If key is encrypted, use batch mode in gpg so it does not ask 305 // for passphrase to re-encrypt to its internal representation. 306 err = gpg.ExportKey(m, *exportedBundle, true /* private */, e.arg.ExportEncrypted /* batch */) 307 if err == nil { 308 m.UIs().LogUI.Info("Exported new key to the local GPG keychain") 309 } 310 return err 311 } 312 313 func (e *PGPKeyImportEngine) unlock(m libkb.MetaContext) (err error) { 314 defer m.Trace("PGPKeyImportEngine::unlock", &err)() 315 if e.arg.Pregen == nil || !e.arg.DoUnlock || !e.arg.Pregen.HasSecretKey() { 316 m.Debug("| short circuit unlock function") 317 } else { 318 err = e.arg.Pregen.Unlock(m, "import into private keychain", m.UIs().SecretUI) 319 } 320 return err 321 } 322 323 func (e *PGPKeyImportEngine) loadDelegator(m libkb.MetaContext) (err error) { 324 325 e.del = &libkb.Delegator{ 326 ExistingKey: e.arg.SigningKey, 327 Me: e.me, 328 Expire: libkb.KeyExpireIn, 329 DelegationType: libkb.DelegationTypeSibkey, 330 Contextified: libkb.NewContextified(e.G()), 331 } 332 333 return e.del.LoadSigningKey(m, m.UIs().SecretUI) 334 } 335 336 func (e *PGPKeyImportEngine) generate(m libkb.MetaContext) (err error) { 337 defer m.Trace("PGP::Generate", &err)() 338 339 m.Debug("| GenerateKey") 340 if e.arg.Pregen != nil { 341 e.bundle = e.arg.Pregen 342 } else if e.arg.Gen == nil { 343 err = libkb.InternalError{Msg: "PGPKeyImportEngine: need either Gen or Pregen"} 344 return 345 } else if err = e.generateKey(m); err != nil { 346 return 347 } 348 return 349 } 350 351 func (e *PGPKeyImportEngine) saveKey(m libkb.MetaContext) (err error) { 352 defer m.Trace("PGP::saveKey", &err)() 353 354 m.Debug("| WriteKey (hasSecret = %v)", e.bundle.HasSecretKey()) 355 if !e.arg.NoSave && e.bundle.HasSecretKey() { 356 if err = e.saveLKS(m); err != nil { 357 return 358 } 359 } 360 361 if e.arg.PushSecret { 362 if err = e.prepareSecretPush(m); err != nil { 363 return 364 } 365 } 366 return 367 } 368 369 func (e *PGPKeyImportEngine) prepareSecretPush(m libkb.MetaContext) error { 370 var tsec libkb.Triplesec 371 var gen libkb.PassphraseGeneration 372 if e.arg.PreloadTsec != nil && e.arg.PreloadStreamGen > 0 { 373 tsec = e.arg.PreloadTsec 374 gen = e.arg.PreloadStreamGen 375 } else { 376 var err error 377 tsec, gen, err = libkb.GetTriplesecMaybePrompt(m) 378 if err != nil { 379 return err 380 } 381 } 382 383 skb, err := e.bundle.ToServerSKB(m.G(), tsec, gen) 384 if err != nil { 385 return err 386 } 387 e.epk, err = skb.ArmoredEncode() 388 389 return err 390 } 391 392 func (e *PGPKeyImportEngine) push(m libkb.MetaContext) (err error) { 393 defer m.Trace("PGP#Push", &err)() 394 if e.arg.GPGFallback { 395 e.bundle.GPGFallbackKey = libkb.NewGPGKey( 396 m.G(), 397 e.bundle.GetFingerprintP(), 398 e.bundle.GetKID(), 399 m.UIs().GPGUI, 400 m.UIs().ClientType) 401 } 402 e.del.NewKey = e.bundle 403 e.del.EncodedPrivateKey = e.epk 404 if err = e.del.Run(m); err != nil { 405 return err 406 } 407 408 m.UIs().LogUI.Info("Generated new PGP key:") 409 d := e.bundle.VerboseDescription() 410 for _, line := range strings.Split(d, "\n") { 411 m.UIs().LogUI.Info(" %s", line) 412 } 413 414 return nil 415 } 416 417 func (e *PGPKeyImportEngine) pushSecretOnly(m libkb.MetaContext) (err error) { 418 defer m.Trace("PGP#PushSecretOnly", &err)() 419 420 m.UIs().LogUI.Info("Only pushing encrypted private key to Keybase server") 421 422 hargs := libkb.HTTPArgs{ 423 "private_key": libkb.S{Val: e.epk}, 424 } 425 arg := libkb.APIArg{ 426 Endpoint: "key/add", 427 SessionType: libkb.APISessionTypeREQUIRED, 428 Args: hargs, 429 } 430 _, err = m.G().API.Post(m, arg) 431 if err != nil { 432 return err 433 } 434 435 m.UIs().LogUI.Info("Success! Pushed encrypted private key") 436 return nil 437 } 438 439 func PGPCheckMulti(me *libkb.User, allowMulti bool) (err error) { 440 if allowMulti { 441 return 442 } 443 if pgps := me.GetActivePGPKeys(false); len(pgps) > 0 { 444 err = libkb.KeyExistsError{Key: pgps[0].GetFingerprintP()} 445 } 446 return 447 } 448 449 func (e *PGPKeyImportEngine) GetKID() (kid keybase1.KID) { 450 if e.bundle == nil { 451 return kid 452 } 453 return e.bundle.GetKID() 454 }