1// Copyright 2015 Keybase, Inc. All rights reserved. Use of
2// this source code is governed by the included BSD license.
3
4package engine
5
6import (
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
17type 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.
24type SaltpackDecrypt struct {
25	arg          *SaltpackDecryptArg
26	res          keybase1.SaltpackEncryptedMessageInfo
27	pnymResolver saltpack.SymmetricKeyResolver
28}
29
30// NewSaltpackDecrypt creates a SaltpackDecrypt engine.
31func 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.
39func (e *SaltpackDecrypt) Name() string {
40	return "SaltpackDecrypt"
41}
42
43// GetPrereqs returns the engine prereqs.
44func (e *SaltpackDecrypt) Prereqs() Prereqs {
45	return Prereqs{}
46}
47
48// RequiredUIs returns the required UIs.
49func (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.
57func (e *SaltpackDecrypt) SubConsumers() []libkb.UIConsumer {
58	return []libkb.UIConsumer{
59		&SaltpackSenderIdentify{},
60	}
61}
62
63func (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
103func (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
120func 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.
126type nilPseudonymResolver struct{}
127
128func (t *nilPseudonymResolver) ResolveKeys(identifiers [][]byte) ([]*saltpack.SymmetricKey, error) {
129	return make([]*saltpack.SymmetricKey, len(identifiers)), nil
130}
131
132// Run starts the engine.
133func (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
230func (e *SaltpackDecrypt) MessageInfo() keybase1.SaltpackEncryptedMessageInfo {
231	return e.res
232}
233