github.com/keybase/client/go@v0.0.0-20240520164431-4f512a4c85a3/client/cmd_pgp_gen.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 client 5 6 import ( 7 "fmt" 8 9 "golang.org/x/net/context" 10 11 "github.com/keybase/cli" 12 "github.com/keybase/client/go/engine" 13 "github.com/keybase/client/go/libcmdline" 14 "github.com/keybase/client/go/libkb" 15 "github.com/keybase/client/go/protocol/keybase1" 16 "github.com/keybase/go-framed-msgpack-rpc/rpc" 17 ) 18 19 type CmdPGPGen struct { 20 libkb.Contextified 21 arg engine.PGPKeyImportEngineArg 22 } 23 24 var SmallKey = 1024 25 26 func (v *CmdPGPGen) ParseArgv(ctx *cli.Context) (err error) { 27 nargs := len(ctx.Args()) 28 if nargs != 0 { 29 err = fmt.Errorf("pgp gen takes 0 args") 30 } else { 31 g := libkb.PGPGenArg{} 32 g.PGPUids = ctx.StringSlice("pgp-uid") 33 v.arg.DoExport = !ctx.Bool("no-export") 34 v.arg.AllowMulti = ctx.Bool("multi") 35 if ctx.Bool("debug") { 36 g.PrimaryBits = SmallKey 37 g.SubkeyBits = SmallKey 38 } 39 v.arg.Gen = &g 40 } 41 return err 42 } 43 44 // Why use CreatePGPIDs rather than MakeAllIds? 45 func (v *CmdPGPGen) Run() (err error) { 46 protocols := []rpc.Protocol{ 47 NewSecretUIProtocol(v.G()), 48 } 49 cli, err := GetPGPClient(v.G()) 50 if err != nil { 51 return err 52 } 53 user, err := GetUserClient(v.G()) 54 if err != nil { 55 return err 56 } 57 58 if err = RegisterProtocolsWithContext(protocols, v.G()); err != nil { 59 return err 60 } 61 62 // Prompt for user IDs if none given on command line 63 if len(v.arg.Gen.PGPUids) == 0 { 64 if err = v.propmptPGPIDs(); err != nil { 65 return err 66 } 67 } else if err = v.arg.Gen.CreatePGPIDs(); err != nil { 68 return err 69 } 70 71 passphraseState, err := user.LoadPassphraseState(context.TODO(), 0) 72 if err != nil { 73 return err 74 } 75 if passphraseState == keybase1.PassphraseState_KNOWN { 76 v.arg.PushSecret, err = v.G().UI.GetTerminalUI().PromptYesNo(PromptDescriptorPGPGenPushSecret, "Push an encrypted copy of your new secret key to the Keybase.io server?", libkb.PromptDefaultYes) 77 if err != nil { 78 return err 79 } 80 } 81 if v.arg.DoExport { 82 v.arg.ExportEncrypted, err = v.G().UI.GetTerminalUI().PromptYesNo(PromptDescriptorPGPGenEncryptSecret, "When exporting to the GnuPG keychain, encrypt private keys with a passphrase?", libkb.PromptDefaultYes) 83 if err != nil { 84 return err 85 } 86 } 87 88 err = cli.PGPKeyGen(context.TODO(), v.arg.Export()) 89 err = AddPGPMultiInstructions(err) 90 return err 91 } 92 93 var CheckRealName = libkb.Checker{ 94 F: func(s string) bool { 95 nameID, err := libkb.ParseIdentity(s) 96 if err != nil { 97 return false 98 } 99 return len(nameID.Username) > 0 && len(nameID.Comment) == 0 && len(nameID.Email) == 0 100 }, 101 Hint: "for example: \"Ned Snowben\"", 102 } 103 104 var CheckOptionalEmail = libkb.Checker{ 105 F: func(s string) bool { 106 if len(s) == 0 { 107 return true 108 } 109 return libkb.CheckEmail.F(s) 110 }, 111 Hint: libkb.CheckEmail.Hint, 112 } 113 114 func (v *CmdPGPGen) propmptPGPIDs() (err error) { 115 id := libkb.Identity{} 116 prompt := "Enter your real name, which will be publicly visible in your new key" 117 id.Username, err = PromptWithChecker(PromptDescriptorPGPGenEnterID, v.G().UI.GetTerminalUI(), prompt, false, CheckRealName) 118 if err != nil { 119 return 120 } 121 // Email required for primary ID 122 prompt = "Enter a public email address for your key" 123 id.Email, err = PromptWithChecker(PromptDescriptorPGPGenEnterID, v.G().UI.GetTerminalUI(), prompt, false, libkb.CheckEmail) 124 if err != nil { 125 return 126 } 127 v.arg.Gen.Ids = append(v.arg.Gen.Ids, id) 128 129 emailsSeen := make(map[string]struct{}) 130 131 emailsSeen[id.Email] = struct{}{} 132 133 idAdditional := libkb.Identity{ 134 Username: id.Username, 135 } 136 prompt = "Enter another email address (or <enter> when done)" 137 for { 138 idAdditional.Email, err = PromptWithChecker(PromptDescriptorPGPGenEnterID, v.G().UI.GetTerminalUI(), prompt, false, CheckOptionalEmail) 139 if err != nil || len(idAdditional.Email) == 0 { 140 break 141 } 142 143 // Make sure it hasn't been added already 144 if _, ok := emailsSeen[idAdditional.Email]; ok { 145 v.G().Log.Warning("Email already applied to this key") 146 continue 147 } 148 149 emailsSeen[idAdditional.Email] = struct{}{} 150 v.arg.Gen.Ids = append(v.arg.Gen.Ids, idAdditional) 151 } 152 153 return 154 } 155 156 func AddPGPMultiInstructions(err error) error { 157 if err == nil { 158 return nil 159 } 160 if kee, ok := err.(libkb.KeyExistsError); ok { 161 return fmt.Errorf("You already have a PGP key registered (%s)\n"+ 162 "Specify the `--multi` flag to override this check", 163 kee.Key.ToQuads()) 164 } 165 // Not the right type. Return it as is. 166 return err 167 } 168 169 func NewCmdPGPGen(cl *libcmdline.CommandLine, g *libkb.GlobalContext) cli.Command { 170 return cli.Command{ 171 Name: "gen", 172 Usage: "Generate a new PGP key and write to local secret keychain", 173 Flags: []cli.Flag{ 174 cli.BoolFlag{ 175 Name: "d, debug", 176 Usage: "Generate small keys for debugging.", 177 }, 178 cli.StringSliceFlag{ 179 Name: "pgp-uid", 180 Usage: "Specify custom PGP uid(s).", 181 Value: &cli.StringSlice{}, 182 }, 183 cli.BoolFlag{ 184 Name: "multi", 185 Usage: "Allow multiple PGP keys.", 186 }, 187 cli.BoolFlag{ 188 Name: "no-export", 189 Usage: "Disable exporting of new keys to GPG keychain.", 190 }, 191 }, 192 Description: `"keybase pgp gen" generates a new PGP key for this account. 193 In all cases, it signs the public key with an existing device key, 194 and pushes the signature to the server. Thus, the user will have a 195 publicly-visible "PGP device" after running this operation. 196 197 The secret half of the PGP key is written by default to the user's 198 local Keybase keychain and encrypted with the "local key security" 199 (LKS) protocol. (For more information, try 'keybase help keyring'). 200 201 Also, by default, the public **and secret** halves of the new PGP key 202 are exported to the local GnuPG keyring, if one is found. You can 203 specify "--no-export" to stop the export of the newly generated key 204 to the GnuPG keyring. 205 206 On subsequent secret key accesses --- say for PGP decryption or 207 for signing --- access to the local GnuPG keyring is not required. 208 Rather, keybase will access the secret PGP key in its own local keychain. 209 210 By default, the secret half of the PGP key is never exported off 211 of the local system, but users have a choice via terminal prompt 212 to select storage of their encrypted secret PGP key on the Keybase 213 servers.`, 214 Action: func(c *cli.Context) { 215 cl.ChooseCommand(&CmdPGPGen{Contextified: libkb.NewContextified(g)}, "gen", c) 216 }, 217 } 218 } 219 220 func (v *CmdPGPGen) GetUsage() libkb.Usage { 221 return libkb.Usage{ 222 Config: true, 223 KbKeyring: true, 224 API: true, 225 } 226 }