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	"github.com/keybase/client/go/libkb"
8	keybase1 "github.com/keybase/client/go/protocol/keybase1"
9)
10
11type SaltpackSenderIdentifyArg struct {
12	publicKey        keybase1.KID
13	isAnon           bool
14	interactive      bool
15	forceRemoteCheck bool
16	reason           keybase1.IdentifyReason
17	userAssertion    string // optional
18}
19
20type SaltpackSenderIdentify struct {
21	libkb.Contextified
22	arg *SaltpackSenderIdentifyArg
23	res keybase1.SaltpackSender
24}
25
26func (e *SaltpackSenderIdentify) Name() string {
27	return "SaltpackSenderIdentify"
28}
29
30func NewSaltpackSenderIdentify(g *libkb.GlobalContext, arg *SaltpackSenderIdentifyArg) *SaltpackSenderIdentify {
31	return &SaltpackSenderIdentify{
32		Contextified: libkb.NewContextified(g),
33		arg:          arg,
34	}
35}
36
37// GetPrereqs returns the engine prereqs.
38func (e *SaltpackSenderIdentify) Prereqs() Prereqs {
39	return Prereqs{}
40}
41
42// RequiredUIs returns the required UIs.
43func (e *SaltpackSenderIdentify) RequiredUIs() []libkb.UIKind {
44	return []libkb.UIKind{}
45}
46
47// SubConsumers returns the other UI consumers for this engine.
48func (e *SaltpackSenderIdentify) SubConsumers() []libkb.UIConsumer {
49	return []libkb.UIConsumer{
50		&Identify2WithUID{},
51	}
52}
53
54func (e *SaltpackSenderIdentify) Run(m libkb.MetaContext) (err error) {
55	defer m.Trace("SaltpackSenderIdentify::Run", &err)()
56
57	if e.arg.isAnon {
58		e.res.SenderType = keybase1.SaltpackSenderType_ANONYMOUS
59		return
60	}
61
62	// Essentially nothing that this key/basics.json endpoint tells us is
63	// verifiable. Don't even retain the username it gives us here; we'll get
64	// it from LoadUser instead, where we check some of the server's work.
65	//
66	// Security note: This is an opportunity for the server to maliciously DOS
67	// us, by lying and saying a key doesn't exist. If we wanted to really
68	// prevent this, we could include a KID->UID index in the Merkle tree that
69	// we pin against the bitcoin blockchain, and trust that Someone Out There
70	// would audit it for consistency with the main body of the tree. File this
71	// one away in the Book of Things We Would Do With Infinite Time and Money.
72	var maybeUID keybase1.UID
73	_, maybeUID, err = libkb.KeyLookupKIDIncludingRevoked(m, e.arg.publicKey)
74	if _, ok := err.(libkb.NotFoundError); ok {
75		// The key in question might not be a Keybase key at all (for example,
76		// anything generated with the Python saltpack implementation, which
77		// isn't Keybase-aware). In that case we'll get this NotFoundError, and
78		// we can just report it as an unknown sender.
79		e.res.SenderType = keybase1.SaltpackSenderType_UNKNOWN
80		err = nil
81		return
82	} else if err != nil {
83		return
84	}
85
86	loadUserArg := libkb.NewLoadUserArgWithMetaContext(m).WithUID(maybeUID)
87	var user *libkb.User
88	user, err = libkb.LoadUser(loadUserArg)
89	if err != nil {
90		return
91	}
92
93	// Use the ComputedKeyFamily assembled by LoadUser to get the status of the
94	// key we started with. (This is where we'll detect corner cases like the
95	// server straight up lying about who owns a given key. An inconsistency
96	// like that will be an error here.)
97	var maybeSenderType *keybase1.SaltpackSenderType
98	maybeSenderType, err = user.GetComputedKeyFamily().GetSaltpackSenderTypeIfInactive(e.arg.publicKey)
99	if err != nil {
100		return
101	}
102
103	// At this point, since GetSaltpackSenderTypeOrActive has not returned an
104	// error, we can consider the UID/username returned by the server to be
105	// "mostly legit". It's still possible that the signing key might be
106	// revoked (this sort of thing is indicated by a non-nil sender type, which
107	// we check for now) or the identify that comes next could report a broken
108	// tracking statement, but those are states that we'll report to the user,
109	// as opposed to unexpected failures or corner case server lies.
110	e.res.Uid = user.GetUID()
111	e.res.Username = user.GetName()
112	e.res.Fullname, err = libkb.GetFullName(m, user.GetUID())
113	if err != nil {
114		return err
115	}
116	if maybeSenderType != nil {
117		e.res.SenderType = *maybeSenderType
118		return
119	}
120
121	// The key is active! This is the happy path. We'll do an identify and show
122	// it to the user, and the SenderType will follow from that.
123	err = e.identifySender(m)
124
125	return
126}
127
128func (e *SaltpackSenderIdentify) identifySender(m libkb.MetaContext) (err error) {
129	defer m.Trace("SaltpackDecrypt::identifySender", &err)()
130
131	var lin bool
132	var uid keybase1.UID
133	if lin, uid = isLoggedIn(m); lin && uid.Equal(e.res.Uid) {
134		e.res.SenderType = keybase1.SaltpackSenderType_SELF
135		if len(e.arg.userAssertion) == 0 {
136			m.Debug("| Sender is self")
137			return nil
138		}
139	}
140
141	iarg := keybase1.Identify2Arg{
142		Uid:                   e.res.Uid,
143		UseDelegateUI:         !e.arg.interactive,
144		AlwaysBlock:           e.arg.interactive,
145		ForceRemoteCheck:      e.arg.forceRemoteCheck,
146		NeedProofSet:          true,
147		NoErrorOnTrackFailure: true,
148		Reason:                e.arg.reason,
149		UserAssertion:         e.arg.userAssertion,
150		IdentifyBehavior:      keybase1.TLFIdentifyBehavior_SALTPACK,
151	}
152	eng := NewIdentify2WithUID(e.G(), &iarg)
153	if err = RunEngine2(m, eng); err != nil {
154		return err
155	}
156
157	if e.res.SenderType == keybase1.SaltpackSenderType_SELF {
158		// if we already know the sender type, then return now
159		return nil
160	}
161
162	switch eng.getTrackType() {
163	case identify2NoTrack:
164		e.res.SenderType = keybase1.SaltpackSenderType_NOT_TRACKED
165	case identify2TrackOK:
166		e.res.SenderType = keybase1.SaltpackSenderType_TRACKING_OK
167	case identify2TrackBroke:
168		e.res.SenderType = keybase1.SaltpackSenderType_TRACKING_BROKE
169	default:
170		panic("unexpected track type")
171	}
172	return nil
173}
174
175func (e *SaltpackSenderIdentify) Result() keybase1.SaltpackSender {
176	return e.res
177}
178