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