1// Copyright 2017 Keybase, Inc. All rights reserved. Use of
2// this source code is governed by the included BSD license.
3
4// PerUserKeyRoll creates a new per-user-key for the active user.
5// This can be the first per-user-key for the user.
6package engine
7
8import (
9	"fmt"
10
11	"github.com/keybase/client/go/libkb"
12	"github.com/keybase/client/go/protocol/keybase1"
13)
14
15// PerUserKeyRoll is an engine.
16type PerUserKeyRoll struct {
17	libkb.Contextified
18	args      *PerUserKeyRollArgs
19	DidNewKey bool
20}
21
22type PerUserKeyRollArgs struct {
23	Me *libkb.User // optional
24}
25
26// NewPerUserKeyRoll creates a PerUserKeyRoll engine.
27func NewPerUserKeyRoll(g *libkb.GlobalContext, args *PerUserKeyRollArgs) *PerUserKeyRoll {
28	return &PerUserKeyRoll{
29		args:         args,
30		Contextified: libkb.NewContextified(g),
31	}
32}
33
34// Name is the unique engine name.
35func (e *PerUserKeyRoll) Name() string {
36	return "PerUserKeyRoll"
37}
38
39// GetPrereqs returns the engine prereqs.
40func (e *PerUserKeyRoll) Prereqs() Prereqs {
41	return Prereqs{
42		Device: true,
43	}
44}
45
46// RequiredUIs returns the required UIs.
47func (e *PerUserKeyRoll) RequiredUIs() []libkb.UIKind {
48	return []libkb.UIKind{}
49}
50
51// SubConsumers returns the other UI consumers for this engine.
52func (e *PerUserKeyRoll) SubConsumers() []libkb.UIConsumer {
53	return []libkb.UIConsumer{}
54}
55
56// Run starts the engine.
57func (e *PerUserKeyRoll) Run(mctx libkb.MetaContext) (err error) {
58	defer mctx.Trace("PerUserKeyRoll", &err)()
59	return retryOnEphemeralRace(mctx, e.inner)
60}
61
62func (e *PerUserKeyRoll) inner(mctx libkb.MetaContext) error {
63	var err error
64
65	uid := mctx.G().GetMyUID()
66	if uid.IsNil() {
67		return libkb.NoUIDError{}
68	}
69
70	me := e.args.Me
71	if me == nil {
72		mctx.Debug("PerUserKeyRoll load self")
73
74		loadArg := libkb.NewLoadUserArgWithMetaContext(mctx).
75			WithUID(uid).
76			WithSelf(true).
77			WithPublicKeyOptional()
78		me, err = libkb.LoadUser(loadArg)
79		if err != nil {
80			return err
81		}
82	}
83	meUPAK := me.ExportToUserPlusAllKeys()
84
85	sigKey, err := mctx.ActiveDevice().SigningKey()
86	if err != nil {
87		return fmt.Errorf("signing key not found: (%v)", err)
88	}
89	encKey, err := mctx.ActiveDevice().EncryptionKey()
90	if err != nil {
91		return fmt.Errorf("encryption key not found: (%v)", err)
92	}
93
94	pukring, err := mctx.G().GetPerUserKeyring(mctx.Ctx())
95	if err != nil {
96		return err
97	}
98	err = pukring.Sync(mctx)
99	if err != nil {
100		return err
101	}
102
103	// Generation of the new key
104	gen := pukring.CurrentGeneration() + keybase1.PerUserKeyGeneration(1)
105	mctx.Debug("PerUserKeyRoll creating gen: %v", gen)
106
107	pukSeed, err := libkb.GeneratePerUserKeySeed()
108	if err != nil {
109		return err
110	}
111
112	var pukPrev *libkb.PerUserKeyPrev
113	if gen > 1 {
114		pukPrevInner, err := pukring.PreparePrev(mctx, pukSeed, gen)
115		if err != nil {
116			return err
117		}
118		pukPrev = &pukPrevInner
119	}
120
121	pukReceivers, err := e.getPukReceivers(mctx, &meUPAK)
122	if err != nil {
123		return err
124	}
125	if len(pukReceivers) == 0 {
126		return fmt.Errorf("no receivers")
127	}
128
129	// Create boxes of the new per-user-key
130	pukBoxes, err := pukring.PrepareBoxesForDevices(mctx,
131		pukSeed, gen, pukReceivers, encKey)
132	if err != nil {
133		return err
134	}
135
136	mctx.Debug("PerUserKeyRoll make sigs")
137	sig, err := libkb.PerUserKeyProofReverseSigned(mctx, me, pukSeed, gen, sigKey)
138	if err != nil {
139		return err
140	}
141	// Seqno when the per-user-key will be signed in.
142	pukSeqno := sig.Seqno
143
144	payload := make(libkb.JSONPayload)
145	payload["sigs"] = []libkb.JSONPayload{sig.Payload}
146
147	mctx.Debug("PerUserKeyRoll pukBoxes:%v pukPrev:%v for generation %v",
148		len(pukBoxes), pukPrev != nil, gen)
149	libkb.AddPerUserKeyServerArg(payload, gen, pukBoxes, pukPrev)
150
151	ekLib := mctx.G().GetEKLib()
152	var myUserEKBox *keybase1.UserEkBoxed
153	var newUserEKMetadata *keybase1.UserEkMetadata
154	if ekLib != nil {
155		merkleRoot, err := mctx.G().GetMerkleClient().FetchRootFromServer(mctx, libkb.EphemeralKeyMerkleFreshness)
156		if err != nil {
157			return err
158		}
159		sig, boxes, newMetadata, myBox, err := ekLib.PrepareNewUserEK(mctx, *merkleRoot, pukSeed)
160		if err != nil {
161			return err
162		}
163		// If there are no active deviceEKs, we can't publish this key. This
164		// should mostly only come up in tests.
165		if len(boxes) > 0 {
166			myUserEKBox = myBox
167			newUserEKMetadata = &newMetadata
168			userEKSection := make(libkb.JSONPayload)
169			userEKSection["sig"] = sig
170			userEKSection["boxes"] = boxes
171			payload["user_ek"] = userEKSection
172		} else {
173			mctx.Debug("skipping userEK publishing, there are no valid deviceEKs")
174		}
175	}
176
177	mctx.Debug("PerUserKeyRoll post")
178	_, err = mctx.G().API.PostJSON(mctx, libkb.APIArg{
179		Endpoint:    "key/multi",
180		SessionType: libkb.APISessionTypeREQUIRED,
181		JSONPayload: payload,
182	})
183	if err != nil {
184		return err
185	}
186	if err = libkb.MerkleCheckPostedUserSig(mctx, uid, pukSeqno, sig.LinkID); err != nil {
187		return err
188	}
189	e.DidNewKey = true
190
191	// Add the per-user-key locally
192	err = pukring.AddKey(mctx, gen, pukSeqno, pukSeed)
193	if err != nil {
194		return err
195	}
196
197	// Add the new userEK box to local storage, if it was created above.
198	if myUserEKBox != nil {
199		err = mctx.G().GetUserEKBoxStorage().Put(mctx, newUserEKMetadata.Generation, *myUserEKBox)
200		if err != nil {
201			mctx.Error("error while saving userEK box: %s", err)
202		}
203	}
204
205	mctx.G().UserChanged(mctx.Ctx(), uid)
206	return nil
207}
208
209// Get the receivers of the new per-user-key boxes.
210// Includes all the user's device subkeys.
211func (e *PerUserKeyRoll) getPukReceivers(mctx libkb.MetaContext, meUPAK *keybase1.UserPlusAllKeys) (res []libkb.NaclDHKeyPair, err error) {
212	for _, dk := range meUPAK.Base.DeviceKeys {
213		if !dk.IsSibkey && !dk.IsRevoked {
214			receiver, err := libkb.ImportNaclDHKeyPairFromHex(dk.KID.String())
215			if err != nil {
216				return res, err
217			}
218			res = append(res, receiver)
219		}
220	}
221	return res, nil
222}
223