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	"fmt"
8	"io"
9
10	"github.com/keybase/client/go/libkb"
11	keybase1 "github.com/keybase/client/go/protocol/keybase1"
12	"github.com/keybase/saltpack"
13)
14
15type SaltpackEncryptArg struct {
16	Opts   keybase1.SaltpackEncryptOptions
17	Source io.Reader
18	Sink   io.WriteCloser
19}
20
21// SaltpackEncrypt encrypts data read from a source into a sink
22// for a set of users.  It will track them if necessary.
23type SaltpackEncrypt struct {
24	arg *SaltpackEncryptArg
25	me  keybase1.UID
26
27	newKeyfinderHook (func(arg libkb.SaltpackRecipientKeyfinderArg) libkb.SaltpackRecipientKeyfinderEngineInterface)
28
29	// keep track if an SBS recipient was used so callers can tell the user
30	UsedSBS      bool
31	SBSAssertion string
32
33	// Legacy encryption-only messages include a lot more information about
34	// receivers, and it's nice to keep the helpful errors working while those
35	// messages are still around.
36	visibleRecipientsForTesting bool
37}
38
39// NewSaltpackEncrypt creates a SaltpackEncrypt engine.
40func NewSaltpackEncrypt(arg *SaltpackEncryptArg, newKeyfinderHook func(arg libkb.SaltpackRecipientKeyfinderArg) libkb.SaltpackRecipientKeyfinderEngineInterface) *SaltpackEncrypt {
41	return &SaltpackEncrypt{
42		arg:              arg,
43		newKeyfinderHook: newKeyfinderHook,
44	}
45}
46
47// Name is the unique engine name.
48func (e *SaltpackEncrypt) Name() string {
49	return "SaltpackEncrypt"
50}
51
52// Prereqs returns the engine prereqs.
53func (e *SaltpackEncrypt) Prereqs() Prereqs {
54	return Prereqs{}
55}
56
57// RequiredUIs returns the required UIs.
58func (e *SaltpackEncrypt) RequiredUIs() []libkb.UIKind {
59	return []libkb.UIKind{
60		libkb.SecretUIKind,
61	}
62}
63
64// SubConsumers returns the other UI consumers for this engine.
65func (e *SaltpackEncrypt) SubConsumers() []libkb.UIConsumer {
66	// Note that potentially KeyfinderHook might return a different UIConsumer depending on its arguments,
67	// which might make this call problematic, but all the hooks currently in use are not doing that.
68	return []libkb.UIConsumer{
69		e.newKeyfinderHook(libkb.SaltpackRecipientKeyfinderArg{}),
70	}
71}
72
73func (e *SaltpackEncrypt) loadMe(m libkb.MetaContext) error {
74	loggedIn, uid, err := isLoggedInWithUIDAndError(m)
75	if err != nil && !e.arg.Opts.NoSelfEncrypt {
76		return err
77	}
78	if !loggedIn {
79		return nil
80	}
81	e.me = uid
82	return nil
83}
84
85// Run starts the engine.
86func (e *SaltpackEncrypt) Run(m libkb.MetaContext) (err error) {
87	defer m.Trace("SaltpackEncrypt::Run", &err)()
88
89	if err = e.loadMe(m); err != nil {
90		return err
91	}
92
93	if !(e.arg.Opts.UseEntityKeys || e.arg.Opts.UseDeviceKeys || e.arg.Opts.UsePaperKeys || e.arg.Opts.UseKBFSKeysOnlyForTesting) {
94		return fmt.Errorf("no key type for encryption was specified")
95	}
96
97	kfarg := libkb.SaltpackRecipientKeyfinderArg{
98		Recipients:        e.arg.Opts.Recipients,
99		TeamRecipients:    e.arg.Opts.TeamRecipients,
100		NoSelfEncrypt:     e.arg.Opts.NoSelfEncrypt,
101		UseEntityKeys:     e.arg.Opts.UseEntityKeys,
102		UsePaperKeys:      e.arg.Opts.UsePaperKeys,
103		UseDeviceKeys:     e.arg.Opts.UseDeviceKeys,
104		UseRepudiableAuth: e.arg.Opts.AuthenticityType == keybase1.AuthenticityType_REPUDIABLE,
105		NoForcePoll:       e.arg.Opts.NoForcePoll,
106	}
107
108	kf := e.newKeyfinderHook(kfarg)
109	if err := RunEngine2(m, kf); err != nil {
110		return err
111	}
112
113	var receivers []libkb.NaclDHKeyPublic
114	for _, KID := range kf.GetPublicKIDs() {
115		gk, err := libkb.ImportKeypairFromKID(KID)
116		if err != nil {
117			return err
118		}
119		kp, ok := gk.(libkb.NaclDHKeyPair)
120		if !ok {
121			return libkb.KeyCannotEncryptError{}
122		}
123		receivers = append(receivers, kp.Public)
124	}
125
126	var symmetricReceivers []saltpack.ReceiverSymmetricKey
127	for _, key := range kf.GetSymmetricKeys() {
128		symmetricReceivers = append(symmetricReceivers, saltpack.ReceiverSymmetricKey{
129			Key:        saltpack.SymmetricKey(key.Key),
130			Identifier: key.Identifier,
131		})
132	}
133
134	e.UsedSBS, e.SBSAssertion = kf.UsedUnresolvedSBSAssertion()
135
136	if e.UsedSBS {
137		actx := m.G().MakeAssertionContext(m)
138		expr, err := libkb.AssertionParse(actx, e.SBSAssertion)
139		if err == nil {
140			social, err := expr.ToSocialAssertion()
141			if err == nil && social.Service == "email" {
142				// email assertions are pretty ugly, so just return
143				// the "User" part for easier handling upstream.
144				e.SBSAssertion = social.User
145			}
146		}
147	}
148
149	// This flag determines whether saltpack is used in signcryption (false)
150	// vs encryption (true) format.
151	encryptionOnlyMode := false
152
153	var senderDH libkb.NaclDHKeyPair
154	if e.arg.Opts.AuthenticityType == keybase1.AuthenticityType_REPUDIABLE && !e.me.IsNil() {
155		encryptionOnlyMode = true
156		dhKey, err := m.G().ActiveDevice.EncryptionKeyWithUID(e.me)
157		if err != nil {
158			return err
159		}
160		dhKeypair, ok := dhKey.(libkb.NaclDHKeyPair)
161		if !ok || dhKeypair.Private == nil {
162			return libkb.KeyCannotEncryptError{}
163		}
164		senderDH = dhKeypair
165	}
166
167	var senderSigning libkb.NaclSigningKeyPair
168	if e.arg.Opts.AuthenticityType == keybase1.AuthenticityType_SIGNED && !e.me.IsNil() {
169		signingKey, err := m.G().ActiveDevice.SigningKeyWithUID(e.me)
170		if err != nil {
171			return err
172		}
173		signingKeypair, ok := signingKey.(libkb.NaclSigningKeyPair)
174		if !ok || signingKeypair.Private == nil {
175			//Perhaps a KeyCannotEncrypt error, although less accurate, would be more intuitive for the user.
176			return libkb.KeyCannotSignError{}
177		}
178		senderSigning = signingKeypair
179	}
180
181	if e.arg.Opts.AuthenticityType != keybase1.AuthenticityType_ANONYMOUS && e.me.IsNil() {
182		return libkb.NewLoginRequiredError("authenticating a message requires login. Either login or use --auth-type=anonymous")
183	}
184
185	saltpackVersion, err := libkb.SaltpackVersionFromArg(e.arg.Opts.SaltpackVersion)
186	if err != nil {
187		return err
188	}
189
190	encarg := libkb.SaltpackEncryptArg{
191		Source:             e.arg.Source,
192		Sink:               e.arg.Sink,
193		Receivers:          receivers,
194		Sender:             senderDH,
195		SenderSigning:      senderSigning,
196		Binary:             e.arg.Opts.Binary,
197		EncryptionOnlyMode: encryptionOnlyMode,
198		SymmetricReceivers: symmetricReceivers,
199		SaltpackVersion:    saltpackVersion,
200
201		VisibleRecipientsForTesting: e.visibleRecipientsForTesting,
202	}
203	return libkb.SaltpackEncrypt(m, &encarg)
204}
205