github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/engine/saltpack_decrypt.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 "bytes" 8 "io" 9 10 "github.com/keybase/client/go/kbcrypto" 11 "github.com/keybase/client/go/libkb" 12 keybase1 "github.com/keybase/client/go/protocol/keybase1" 13 "github.com/keybase/saltpack" 14 saltpackBasic "github.com/keybase/saltpack/basic" 15 ) 16 17 type SaltpackDecryptArg struct { 18 Source io.Reader 19 Sink io.WriteCloser 20 Opts keybase1.SaltpackDecryptOptions 21 } 22 23 // SaltpackDecrypt decrypts data read from a source into a sink. 24 type SaltpackDecrypt struct { 25 arg *SaltpackDecryptArg 26 res keybase1.SaltpackEncryptedMessageInfo 27 pnymResolver saltpack.SymmetricKeyResolver 28 } 29 30 // NewSaltpackDecrypt creates a SaltpackDecrypt engine. 31 func NewSaltpackDecrypt(arg *SaltpackDecryptArg, pnymResolver saltpack.SymmetricKeyResolver) *SaltpackDecrypt { 32 return &SaltpackDecrypt{ 33 arg: arg, 34 pnymResolver: pnymResolver, 35 } 36 } 37 38 // Name is the unique engine name. 39 func (e *SaltpackDecrypt) Name() string { 40 return "SaltpackDecrypt" 41 } 42 43 // GetPrereqs returns the engine prereqs. 44 func (e *SaltpackDecrypt) Prereqs() Prereqs { 45 return Prereqs{} 46 } 47 48 // RequiredUIs returns the required UIs. 49 func (e *SaltpackDecrypt) RequiredUIs() []libkb.UIKind { 50 return []libkb.UIKind{ 51 libkb.SaltpackUIKind, 52 libkb.SecretUIKind, 53 } 54 } 55 56 // SubConsumers returns the other UI consumers for this engine. 57 func (e *SaltpackDecrypt) SubConsumers() []libkb.UIConsumer { 58 return []libkb.UIConsumer{ 59 &SaltpackSenderIdentify{}, 60 } 61 } 62 63 func (e *SaltpackDecrypt) promptForDecrypt(m libkb.MetaContext, publicKey keybase1.KID, isAnon, signed bool) (err error) { 64 defer m.Trace("SaltpackDecrypt#promptForDecrypt", &err)() 65 66 spsiArg := SaltpackSenderIdentifyArg{ 67 isAnon: isAnon, 68 publicKey: publicKey, 69 interactive: e.arg.Opts.Interactive, 70 forceRemoteCheck: e.arg.Opts.ForceRemoteCheck, 71 reason: keybase1.IdentifyReason{ 72 Reason: "Identify who encrypted this message", 73 Type: keybase1.IdentifyReasonType_DECRYPT, 74 }, 75 } 76 77 spsiEng := NewSaltpackSenderIdentify(m.G(), &spsiArg) 78 if err = RunEngine2(m, spsiEng); err != nil { 79 return err 80 } 81 82 arg := keybase1.SaltpackPromptForDecryptArg{ 83 Sender: spsiEng.Result(), 84 SigningKID: publicKey, 85 Signed: signed, 86 } 87 e.res.Sender = arg.Sender 88 89 usedDelegateUI := false 90 if m.G().UIRouter != nil { 91 if ui, err := m.G().UIRouter.GetIdentifyUI(); err == nil && ui != nil { 92 usedDelegateUI = true 93 } 94 } 95 96 err = m.UIs().SaltpackUI.SaltpackPromptForDecrypt(m.Ctx(), arg, usedDelegateUI) 97 if err != nil { 98 return err 99 } 100 return err 101 } 102 103 func (e *SaltpackDecrypt) makeMessageInfo(me *libkb.User, mki *saltpack.MessageKeyInfo) { 104 if mki == nil || me == nil { 105 return 106 } 107 ckf := me.GetComputedKeyFamily() 108 for _, nr := range mki.NamedReceivers { 109 kid := keybase1.KIDFromRawKey(nr, byte(kbcrypto.KIDNaclDH)) 110 if dev, _ := ckf.GetDeviceForKID(kid); dev != nil { 111 edev := dev.ProtExport() 112 edev.EncryptKey = kid 113 e.res.Devices = append(e.res.Devices, *edev) 114 } 115 } 116 e.res.NumAnonReceivers = mki.NumAnonReceivers 117 e.res.ReceiverIsAnon = mki.ReceiverIsAnon 118 } 119 120 func addToKeyring(keyring *saltpackBasic.Keyring, key *libkb.NaclDHKeyPair) { 121 keyring.ImportBoxKey((*[libkb.NaclDHKeysize]byte)(&key.Public), (*[libkb.NaclDHKeysize]byte)(key.Private)) 122 } 123 124 // Used when decrypting with a paper key, as in that case there is no active device/user and we cannot rely on the 125 // pseudonym mechanism. 126 type nilPseudonymResolver struct{} 127 128 func (t *nilPseudonymResolver) ResolveKeys(identifiers [][]byte) ([]*saltpack.SymmetricKey, error) { 129 return make([]*saltpack.SymmetricKey, len(identifiers)), nil 130 } 131 132 // Run starts the engine. 133 func (e *SaltpackDecrypt) Run(m libkb.MetaContext) (err error) { 134 defer m.Trace("SaltpackDecrypt::Run", &err)() 135 136 // We don't load this in the --paperkey case. 137 var me *libkb.User 138 139 keyring := saltpackBasic.NewKeyring() 140 141 if e.arg.Opts.UsePaperKey { 142 // Prompt the user for a paper key. This doesn't require you to be 143 // logged in. 144 keypair, _, err := getPaperKey(m, nil, nil) 145 if err != nil { 146 return err 147 } 148 encryptionNaclKeyPair := keypair.EncryptionKey().(libkb.NaclDHKeyPair) 149 addToKeyring(keyring, &encryptionNaclKeyPair) 150 151 // If a paper key is used, we do not have PUK or an active session, so we cannot talk to the server to resolve pseudonym. 152 m.Debug("substituting the default PseudonymResolver as a paper key is being used for decryption") 153 e.pnymResolver = &nilPseudonymResolver{} 154 } else { 155 // This does require you to be logged in. 156 if !m.G().ActiveDevice.HaveKeys() { 157 return libkb.LoginRequiredError{} 158 } 159 160 // Only used in the makeMessageInfo call, which is helpful for old messages (one cannot encrypt messages with visible recipients any more). 161 me, err = libkb.LoadMe(libkb.NewLoadUserArgWithMetaContext(m)) 162 if err != nil { 163 return err 164 } 165 166 // Get the device encryption key and per user keys. 167 var key *libkb.NaclDHKeyPair 168 var err error 169 key, err = m.G().ActiveDevice.NaclEncryptionKey() 170 if err != nil { 171 return err 172 } 173 m.Debug("adding device key for decryption: %v", key.GetKID()) 174 addToKeyring(keyring, key) 175 176 perUserKeyring, err := m.G().GetPerUserKeyring(m.Ctx()) 177 if err != nil { 178 return err 179 } 180 pukGen := perUserKeyring.CurrentGeneration() 181 for i := 1; i <= int(pukGen); i++ { 182 key, err = perUserKeyring.GetEncryptionKeyByGeneration(m, keybase1.PerUserKeyGeneration(i)) 183 m.Debug("adding per user key at generation %v for decryption: %v", i, key.GetKID()) 184 if err != nil { 185 return err 186 } 187 addToKeyring(keyring, key) 188 } 189 } 190 191 // For DH mode. 192 hookMki := func(mki *saltpack.MessageKeyInfo) error { 193 kidToIdentify := libkb.BoxPublicKeyToKeybaseKID(mki.SenderKey) 194 return e.promptForDecrypt(m, kidToIdentify, mki.SenderIsAnon, false /* not signed */) 195 } 196 197 // For signcryption mode. 198 hookSenderSigningKey := func(senderSigningKey saltpack.SigningPublicKey) error { 199 kidToIdentify := libkb.SigningPublicKeyToKeybaseKID(senderSigningKey) 200 // See if the sender signing key is nil or all zeroes. 201 isAnon := false 202 signed := true 203 if senderSigningKey == nil || bytes.Equal(senderSigningKey.ToKID(), make([]byte, len(senderSigningKey.ToKID()))) { 204 isAnon = true 205 signed = false 206 } 207 return e.promptForDecrypt(m, kidToIdentify, isAnon, signed) 208 } 209 210 m.Debug("| SaltpackDecrypt") 211 var mki *saltpack.MessageKeyInfo 212 mki, err = libkb.SaltpackDecrypt(m, e.arg.Source, e.arg.Sink, keyring, hookMki, hookSenderSigningKey, e.pnymResolver) 213 214 if decErr, ok := err.(libkb.DecryptionError); ok && decErr.Cause.Err == saltpack.ErrNoDecryptionKey { 215 m.Debug("switching cause of libkb.DecryptionError from saltpack.ErrNoDecryptionKey to more specific libkb.NoDecryptionKeyError") 216 if e.arg.Opts.UsePaperKey { 217 return libkb.DecryptionError{Cause: libkb.ErrorCause{Err: libkb.NoDecryptionKeyError{Msg: "this message was not directly encrypted for the given paper key. In some cases, you might still be able to decrypt the message from a device provisioned with this key."}, StatusCode: libkb.SCDecryptionKeyNotFound}} 218 } 219 err = libkb.DecryptionError{Cause: libkb.ErrorCause{Err: libkb.NoDecryptionKeyError{Msg: "no suitable key found"}, StatusCode: libkb.SCDecryptionKeyNotFound}} 220 } 221 222 // Since messages recipients are never public any more, this is only meaningful for messages generated by 223 // very old clients (or potentially saltpack messages generated for a keybase user by some other app). 224 // It's ok if me is nil here. 225 e.makeMessageInfo(me, mki) 226 227 return err 228 } 229 230 func (e *SaltpackDecrypt) MessageInfo() keybase1.SaltpackEncryptedMessageInfo { 231 return e.res 232 }