github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/engine/pgp_encrypt.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 "errors" 8 "io" 9 10 "github.com/keybase/client/go/libkb" 11 keybase1 "github.com/keybase/client/go/protocol/keybase1" 12 "github.com/keybase/go-crypto/openpgp/armor" 13 ) 14 15 type PGPEncryptArg struct { 16 Recips []string // user assertions 17 Source io.Reader 18 Sink io.WriteCloser 19 NoSign bool 20 NoSelf bool 21 BinaryOutput bool 22 KeyQuery string 23 } 24 25 // PGPEncrypt encrypts data read from a source into a sink 26 // for a set of users. It will track them if necessary. 27 type PGPEncrypt struct { 28 arg *PGPEncryptArg 29 me *libkb.User 30 warnings libkb.HashSecurityWarnings 31 libkb.Contextified 32 } 33 34 // NewPGPEncrypt creates a PGPEncrypt engine. 35 func NewPGPEncrypt(g *libkb.GlobalContext, arg *PGPEncryptArg) *PGPEncrypt { 36 return &PGPEncrypt{ 37 arg: arg, 38 Contextified: libkb.NewContextified(g), 39 } 40 } 41 42 // Name is the unique engine name. 43 func (e *PGPEncrypt) Name() string { 44 return "PGPEncrypt" 45 } 46 47 // GetPrereqs returns the engine prereqs. 48 func (e *PGPEncrypt) Prereqs() Prereqs { 49 return Prereqs{} 50 } 51 52 // RequiredUIs returns the required UIs. 53 func (e *PGPEncrypt) RequiredUIs() []libkb.UIKind { 54 // context.SecretKeyPromptArg requires SecretUI 55 return []libkb.UIKind{ 56 libkb.SecretUIKind, 57 libkb.PgpUIKind, 58 } 59 } 60 61 // SubConsumers returns the other UI consumers for this engine. 62 func (e *PGPEncrypt) SubConsumers() []libkb.UIConsumer { 63 return []libkb.UIConsumer{ 64 &PGPKeyfinder{}, 65 &ResolveThenIdentify2{}, 66 } 67 } 68 69 // Run starts the engine. 70 func (e *PGPEncrypt) Run(m libkb.MetaContext) error { 71 // verify valid options based on logged in state: 72 ok, uid := isLoggedIn(m) 73 74 if !ok { 75 // not logged in. this is fine, unless they requested signing the message. 76 if !e.arg.NoSign { 77 return libkb.LoginRequiredError{Context: "you must be logged in to sign"} 78 } 79 80 // or trying to encrypt for self 81 if !e.arg.NoSelf { 82 return libkb.LoginRequiredError{Context: "you must be logged in to encrypt for yourself (or use --no-self flag)"} 83 } 84 } else { 85 me, err := libkb.LoadMeByMetaContextAndUID(m, uid) 86 if err != nil { 87 return err 88 } 89 e.me = me 90 } 91 92 var mykey *libkb.PGPKeyBundle 93 var signer *libkb.PGPKeyBundle 94 if !e.arg.NoSign { 95 ska := libkb.SecretKeyArg{ 96 Me: e.me, 97 KeyType: libkb.PGPKeyType, 98 KeyQuery: e.arg.KeyQuery, 99 } 100 key, err := e.G().Keyrings.GetSecretKeyWithPrompt(m, m.SecretKeyPromptArg(ska, "command-line signature")) 101 if err != nil { 102 return err 103 } 104 105 var ok bool 106 mykey, ok = key.(*libkb.PGPKeyBundle) 107 if !ok { 108 return errors.New("Can only sign with PGP keys") 109 } 110 signer = mykey 111 } 112 113 usernames, err := e.verifyUsers(m, e.arg.Recips, ok) 114 if err != nil { 115 return err 116 } 117 118 kfarg := &PGPKeyfinderArg{ 119 Usernames: usernames, 120 } 121 122 kf := NewPGPKeyfinder(e.G(), kfarg) 123 if err := RunEngine2(m, kf); err != nil { 124 return err 125 } 126 uplus := kf.UsersPlusKeys() 127 128 var writer io.WriteCloser 129 if e.arg.BinaryOutput { 130 writer = e.arg.Sink 131 } else { 132 aw, err := armor.Encode(e.arg.Sink, "PGP MESSAGE", libkb.PGPArmorHeaders) 133 if err != nil { 134 return err 135 } 136 writer = aw 137 } 138 139 ks := newKeyset() 140 e.warnings = libkb.HashSecurityWarnings{} 141 142 if mykey != nil { 143 if w := mykey.SecurityWarnings( 144 libkb.HashSecurityWarningOurIdentityHash, 145 ); len(w) > 0 { 146 e.warnings = append(e.warnings, w...) 147 } 148 } 149 150 for _, up := range uplus { 151 for _, k := range up.Keys { 152 if len(k.Entity.Revocations)+len(k.Entity.UnverifiedRevocations) > 0 { 153 continue 154 } 155 156 if w := k.SecurityWarnings( 157 libkb.HashSecurityWarningRecipientsIdentityHash, 158 ); len(w) > 0 { 159 e.warnings = append(e.warnings, w...) 160 } 161 162 ks.Add(k) 163 } 164 } 165 166 if len(e.arg.Recips) > 0 && len(ks.keys) == 0 { 167 return errors.New("Cannot encrypt - recipient does not have a non-revoked key.") 168 } 169 170 if !e.arg.NoSelf { 171 if mykey == nil { 172 // need to load the public key for the logged in user 173 mykey, err = e.loadSelfKey() 174 if err != nil { 175 return err 176 } 177 } 178 179 // mykey could still be nil 180 if mykey != nil { 181 ks.Add(mykey) 182 } 183 } 184 185 for _, warning := range e.warnings.Strings() { 186 if err := m.UIs().PgpUI.OutputPGPWarning(m.Ctx(), keybase1.OutputPGPWarningArg{ 187 Warning: warning, 188 }); err != nil { 189 return err 190 } 191 } 192 193 recipients := ks.Sorted() 194 if err := libkb.PGPEncrypt(e.arg.Source, writer, signer, recipients); err != nil { 195 return err 196 } 197 if !e.arg.BinaryOutput { 198 return e.arg.Sink.Close() 199 } 200 return nil 201 } 202 203 func (e *PGPEncrypt) loadSelfKey() (*libkb.PGPKeyBundle, error) { 204 me, err := libkb.LoadMe(libkb.NewLoadUserArg(e.G())) 205 if err != nil { 206 return nil, err 207 } 208 209 keys := me.FilterActivePGPKeys(true, e.arg.KeyQuery) 210 if len(keys) == 0 { 211 return nil, libkb.NoKeyError{Msg: "No PGP key found for encrypting for self (add a PGP key or use --no-self flag)"} 212 } 213 return keys[0], nil 214 } 215 216 func (e *PGPEncrypt) verifyUsers(m libkb.MetaContext, assertions []string, loggedIn bool) ([]string, error) { 217 var names []string 218 for _, userAssert := range assertions { 219 arg := keybase1.Identify2Arg{ 220 UserAssertion: userAssert, 221 Reason: keybase1.IdentifyReason{ 222 Type: keybase1.IdentifyReasonType_ENCRYPT, 223 }, 224 AlwaysBlock: true, 225 IdentifyBehavior: keybase1.TLFIdentifyBehavior_CLI, 226 } 227 eng := NewResolveThenIdentify2(e.G(), &arg) 228 if err := RunEngine2(m, eng); err != nil { 229 return nil, libkb.IdentifyFailedError{Assertion: userAssert, Reason: err.Error()} 230 } 231 res, err := eng.Result(m) 232 if err != nil { 233 return nil, err 234 } 235 names = append(names, res.Upk.GetName()) 236 } 237 return names, nil 238 } 239 240 // keyset maintains a set of pgp keys, preserving insertion order. 241 type keyset struct { 242 index []keybase1.KID 243 keys map[keybase1.KID]*libkb.PGPKeyBundle 244 } 245 246 // newKeyset creates an empty keyset. 247 func newKeyset() *keyset { 248 return &keyset{keys: make(map[keybase1.KID]*libkb.PGPKeyBundle)} 249 } 250 251 // Add adds bundle to the keyset. If a key already exists, it 252 // will be ignored. 253 func (k *keyset) Add(bundle *libkb.PGPKeyBundle) { 254 kid := bundle.GetKID() 255 if _, ok := k.keys[kid]; ok { 256 return 257 } 258 k.keys[kid] = bundle 259 k.index = append(k.index, kid) 260 } 261 262 // Sorted returns the unique keys in insertion order. 263 func (k *keyset) Sorted() []*libkb.PGPKeyBundle { 264 var sorted []*libkb.PGPKeyBundle 265 for _, kid := range k.index { 266 sorted = append(sorted, k.keys[kid]) 267 } 268 return sorted 269 }