1// Copyright 2015 Keybase, Inc. All rights reserved. Use of
2// this source code is governed by the included BSD license.
3
4// This engine makes sure the user is logged in and unlocked.
5// It asks for a paper key if need be. It does not ask for a passphrase.
6
7package engine
8
9import (
10	"github.com/keybase/client/go/libkb"
11)
12
13// LoginWithPaperKey is an engine.
14type LoginWithPaperKey struct {
15	libkb.Contextified
16	username string
17}
18
19// NewLoginWithPaperKey creates a LoginWithPaperKey engine.
20// Uses the paperkey to log in and unlock LKS.
21func NewLoginWithPaperKey(g *libkb.GlobalContext, username string) *LoginWithPaperKey {
22	return &LoginWithPaperKey{
23		Contextified: libkb.NewContextified(g),
24		username:     username,
25	}
26}
27
28// Name is the unique engine name.
29func (e *LoginWithPaperKey) Name() string {
30	return "LoginWithPaperKey"
31}
32
33// GetPrereqs returns the engine prereqs.
34func (e *LoginWithPaperKey) Prereqs() Prereqs {
35	return Prereqs{}
36}
37
38// RequiredUIs returns the required UIs.
39func (e *LoginWithPaperKey) RequiredUIs() []libkb.UIKind {
40	return []libkb.UIKind{
41		libkb.LogUIKind,
42		libkb.SecretUIKind,
43	}
44}
45
46// SubConsumers returns the other UI consumers for this engine.
47func (e *LoginWithPaperKey) SubConsumers() []libkb.UIConsumer {
48	return []libkb.UIConsumer{}
49}
50
51// Run starts the engine.
52func (e *LoginWithPaperKey) Run(m libkb.MetaContext) (err error) {
53	var me *libkb.User
54	if e.username == "" {
55		me, err = libkb.LoadMe(libkb.NewLoadUserArgWithMetaContext(m).WithForceReload())
56		if err != nil {
57			return err
58		}
59	} else {
60		me, err = libkb.LoadUser(libkb.NewLoadUserArgWithMetaContext(m).WithForceReload().WithName(e.username))
61		if err != nil {
62			return err
63		}
64	}
65
66	if loggedIn, _ := isLoggedIn(m); loggedIn {
67		m.Debug("Already logged in with unlocked device keys")
68		return nil
69	}
70
71	// Prompts for a paper key.
72	m.Debug("No device keys available; getting paper key")
73	kp, err := findPaperKeys(m, me)
74	if err != nil {
75		return err
76	}
77
78	// Switch config file to our new user, and zero out the current active device.
79	if err = m.SwitchUser(me.GetNormalizedName()); err != nil {
80		return err
81	}
82
83	// Convert our paper keys into a provisional active device, to use for
84	// API session authentication. BAM! We're "logged in".
85	m = m.WithProvisioningKeyActiveDevice(kp, me.ToUserVersion())
86
87	// Get the LKS client half.
88	gen, clientLKS, err := fetchLKS(m, kp.EncryptionKey())
89	if err != nil {
90		return err
91	}
92	lks := libkb.NewLKSecWithClientHalf(clientLKS, gen, me.GetUID())
93	m.Debug("Got LKS client half")
94
95	// Get the LKS server half.
96	err = lks.Load(m)
97	if err != nil {
98		return err
99	}
100	m.Debug("Got LKS full")
101
102	secretStore := libkb.NewSecretStore(m, me.GetNormalizedName())
103	m.Debug("Got secret store")
104
105	// Extract the LKS secret
106	secret, err := lks.GetSecret(m)
107	if err != nil {
108		return err
109	}
110	m.Debug("Got LKS secret")
111
112	err = secretStore.StoreSecret(m, secret)
113	if err != nil {
114		return err
115	}
116	m.Debug("Stored secret with LKS from paperkey")
117
118	// Remove our provisional active device, and fall back to global device
119	m = m.WithGlobalActiveDevice()
120
121	// This could prompt but shouldn't because of the secret store.
122	if _, err = libkb.BootstrapActiveDeviceFromConfig(m, true); err != nil {
123		return err
124	}
125	m.Debug("Unlocked device keys")
126
127	m.Debug("LoginWithPaperkey success, sending login notification")
128	m.G().NotifyRouter.HandleLogin(m.Ctx(), string(m.G().Env.GetUsername()))
129	m.Debug("LoginWithPaperkey success, calling login hooks")
130	m.G().CallLoginHooks(m)
131
132	return nil
133}
134