github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/engine/pgp_export_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 "fmt" 15 16 "github.com/keybase/client/go/kbcrypto" 17 "github.com/keybase/client/go/libkb" 18 keybase1 "github.com/keybase/client/go/protocol/keybase1" 19 ) 20 21 type queryType int 22 23 const ( 24 unset queryType = iota 25 fingerprint 26 kid 27 either 28 ) 29 30 type PGPKeyExportEngine struct { 31 libkb.Contextified 32 arg keybase1.PGPQuery 33 encrypted bool 34 qtype queryType 35 res []keybase1.KeyInfo 36 me *libkb.User 37 } 38 39 func (e *PGPKeyExportEngine) Prereqs() Prereqs { 40 return Prereqs{ 41 Device: true, 42 } 43 } 44 45 func (e *PGPKeyExportEngine) Name() string { 46 return "PGPKeyExportEngine" 47 } 48 49 func (e *PGPKeyExportEngine) RequiredUIs() []libkb.UIKind { 50 return []libkb.UIKind{ 51 libkb.SecretUIKind, 52 } 53 } 54 55 func (e *PGPKeyExportEngine) SubConsumers() []libkb.UIConsumer { 56 return nil 57 } 58 59 func (e *PGPKeyExportEngine) Results() []keybase1.KeyInfo { 60 return e.res 61 } 62 63 func NewPGPKeyExportEngine(g *libkb.GlobalContext, arg keybase1.PGPExportArg) *PGPKeyExportEngine { 64 return &PGPKeyExportEngine{ 65 arg: arg.Options, 66 qtype: either, 67 encrypted: arg.Encrypted, 68 Contextified: libkb.NewContextified(g), 69 } 70 } 71 72 func NewPGPKeyExportByKIDEngine(g *libkb.GlobalContext, arg keybase1.PGPExportByKIDArg) *PGPKeyExportEngine { 73 return &PGPKeyExportEngine{ 74 arg: arg.Options, 75 qtype: kid, 76 encrypted: arg.Encrypted, 77 Contextified: libkb.NewContextified(g), 78 } 79 } 80 81 func NewPGPKeyExportByFingerprintEngine(g *libkb.GlobalContext, arg keybase1.PGPExportByFingerprintArg) *PGPKeyExportEngine { 82 return &PGPKeyExportEngine{ 83 arg: arg.Options, 84 qtype: fingerprint, 85 encrypted: arg.Encrypted, 86 Contextified: libkb.NewContextified(g), 87 } 88 } 89 90 func (e *PGPKeyExportEngine) pushRes(fp libkb.PGPFingerprint, key string, desc string) { 91 e.res = append(e.res, keybase1.KeyInfo{ 92 Fingerprint: fp.String(), 93 Key: key, 94 Desc: desc, 95 }) 96 } 97 98 func (e *PGPKeyExportEngine) queryMatch(k libkb.GenericKey) bool { 99 if len(e.arg.Query) == 0 { 100 return true 101 } 102 var match bool 103 switch e.qtype { 104 case either: 105 match = libkb.KeyMatchesQuery(k, e.arg.Query, e.arg.ExactMatch) 106 case fingerprint: 107 if fp := libkb.GetPGPFingerprintFromGenericKey(k); fp != nil { 108 match = fp.Match(e.arg.Query, e.arg.ExactMatch) 109 } 110 case kid: 111 match = k.GetKID().Match(e.arg.Query, e.arg.ExactMatch) 112 } 113 return match 114 } 115 116 func (e *PGPKeyExportEngine) exportPublic() (err error) { 117 keys := e.me.GetActivePGPKeys(false) 118 for _, k := range keys { 119 fp := k.GetFingerprintP() 120 s, err := k.Encode() 121 if fp == nil || err != nil { 122 continue 123 } 124 if !e.queryMatch(k) { 125 continue 126 } 127 e.pushRes(*fp, s, k.VerboseDescription()) 128 } 129 return 130 } 131 132 func (e *PGPKeyExportEngine) exportSecret(m libkb.MetaContext) error { 133 ska := libkb.SecretKeyArg{ 134 Me: e.me, 135 KeyType: libkb.PGPKeyType, 136 KeyQuery: e.arg.Query, 137 ExactMatch: e.arg.ExactMatch, 138 } 139 key, skb, err := m.G().Keyrings.GetSecretKeyAndSKBWithPrompt(m, m.SecretKeyPromptArg(ska, "key export")) 140 if err != nil { 141 if _, ok := err.(libkb.NoSecretKeyError); ok { 142 // if no secret key found, don't return an error, just let 143 // the result be empty 144 return nil 145 } 146 return err 147 } 148 fp := libkb.GetPGPFingerprintFromGenericKey(key) 149 if fp == nil { 150 return kbcrypto.BadKeyError{Msg: "no fingerprint found"} 151 } 152 153 if !e.queryMatch(key) { 154 return nil 155 } 156 157 if _, ok := key.(*libkb.PGPKeyBundle); !ok { 158 return kbcrypto.BadKeyError{Msg: "Expected a PGP key"} 159 } 160 161 raw := skb.RawUnlockedKey() 162 if raw == nil { 163 return kbcrypto.BadKeyError{Msg: "can't get raw representation of key"} 164 } 165 166 if e.encrypted { 167 // Make encrypted PGP key bundle using provided passphrase. 168 // Key will be reimported from bytes so we don't mutate SKB. 169 raw, err = e.encryptKey(m, raw) 170 if err != nil { 171 return err 172 } 173 } 174 175 ret, err := libkb.PGPKeyRawToArmored(raw, true) 176 if err != nil { 177 return err 178 } 179 180 e.pushRes(*fp, ret, "") 181 182 return nil 183 } 184 185 func GetPGPExportPassphrase(m libkb.MetaContext, ui libkb.SecretUI, desc string) (keybase1.GetPassphraseRes, error) { 186 pRes, err := libkb.GetSecret(m, ui, "PGP key passphrase", desc, "", false) 187 if err != nil { 188 return keybase1.GetPassphraseRes{}, err 189 } 190 191 desc = "Please reenter your passphrase for confirmation" 192 pRes2, err := libkb.GetSecret(m, ui, "PGP key passphrase", desc, "", false) 193 if err != nil { 194 return keybase1.GetPassphraseRes{}, err 195 } 196 if pRes.Passphrase != pRes2.Passphrase { 197 return keybase1.GetPassphraseRes{}, errors.New("Passphrase mismatch") 198 } 199 200 return pRes, nil 201 } 202 203 func (e *PGPKeyExportEngine) encryptKey(m libkb.MetaContext, raw []byte) ([]byte, error) { 204 entity, _, err := libkb.ReadOneKeyFromBytes(raw) 205 if err != nil { 206 return nil, err 207 } 208 209 if entity.PrivateKey == nil { 210 return nil, kbcrypto.BadKeyError{Msg: "No secret part in PGP key."} 211 } 212 213 desc := "Enter passphrase to protect your PGP key. Secure passphrases have at least 8 characters." 214 pRes, err := GetPGPExportPassphrase(m, m.UIs().SecretUI, desc) 215 if err != nil { 216 return nil, err 217 } 218 219 if err = libkb.EncryptPGPKey(entity.Entity, pRes.Passphrase); err != nil { 220 return nil, err 221 } 222 223 var buf bytes.Buffer 224 if err = entity.SerializePrivate(&buf); err != nil { 225 return nil, err 226 } 227 228 return buf.Bytes(), nil 229 } 230 231 func (e *PGPKeyExportEngine) loadMe(m libkb.MetaContext) (err error) { 232 e.me, err = libkb.LoadMe(libkb.NewLoadUserArgWithMetaContext(m).WithPublicKeyOptional()) 233 return 234 } 235 236 func (e *PGPKeyExportEngine) Run(m libkb.MetaContext) (err error) { 237 defer m.Trace("PGPKeyExportEngine::Run", &err)() 238 239 if e.qtype == unset { 240 return fmt.Errorf("PGPKeyExportEngine: query type not set") 241 } 242 243 if err = e.loadMe(m); err != nil { 244 return 245 } 246 247 if e.arg.Secret { 248 err = e.exportSecret(m) 249 } else { 250 err = e.exportPublic() 251 } 252 253 return 254 }