github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/engine/paperprovision.go (about) 1 // Copyright 2015 Keybase, Inc. All rights reserved. Use of 2 // this source code is governed by the included BSD license. 3 4 package engine 5 6 import ( 7 "errors" 8 "fmt" 9 10 "github.com/keybase/client/go/libkb" 11 keybase1 "github.com/keybase/client/go/protocol/keybase1" 12 ) 13 14 type PaperProvisionEngine struct { 15 libkb.Contextified 16 Username string 17 DeviceName string 18 PaperKey string 19 result error 20 lks *libkb.LKSec 21 User *libkb.User 22 perUserKeyring *libkb.PerUserKeyring 23 24 deviceWrapEng *DeviceWrap 25 } 26 27 func NewPaperProvisionEngine(g *libkb.GlobalContext, username, deviceName, 28 paperKey string) *PaperProvisionEngine { 29 return &PaperProvisionEngine{ 30 Contextified: libkb.NewContextified(g), 31 Username: username, 32 DeviceName: deviceName, 33 PaperKey: paperKey, 34 } 35 } 36 37 func (e *PaperProvisionEngine) Name() string { 38 return "PaperProvision" 39 } 40 41 func (e *PaperProvisionEngine) Prereqs() Prereqs { 42 return Prereqs{} 43 } 44 45 func (e *PaperProvisionEngine) RequiredUIs() []libkb.UIKind { 46 return []libkb.UIKind{ 47 libkb.ProvisionUIKind, 48 libkb.LogUIKind, 49 libkb.SecretUIKind, 50 libkb.LoginUIKind, 51 } 52 } 53 54 func (e *PaperProvisionEngine) Run(m libkb.MetaContext) (err error) { 55 defer m.Trace("PaperProvisionEngine#Run", &err)() 56 57 // clear out any existing session 58 err = m.LogoutKeepSecrets() 59 if err != nil { 60 m.Debug("error on logout: %+v", err) 61 } 62 63 m = m.WithNewProvisionalLoginContext() 64 65 // From this point on, if there's an error, we abort the 66 // transaction. 67 defer func() { 68 if err == nil { 69 m = m.CommitProvisionalLogin() 70 } 71 }() 72 73 // run the LoginLoadUser sub-engine to load a user 74 ueng := newLoginLoadUser(e.G(), e.Username) 75 if err = RunEngine2(m, ueng); err != nil { 76 return err 77 } 78 79 // make sure the user isn't already provisioned (can 80 // get here if usernameOrEmail is an email address 81 // for an already provisioned on this device user). 82 if ueng.User().HasCurrentDeviceInCurrentInstall() { 83 return libkb.DeviceAlreadyProvisionedError{} 84 } 85 e.User = ueng.User() 86 87 // Transform the paper key phrase into a key pair 88 bkarg := &PaperKeyGenArg{ 89 Passphrase: libkb.PaperKeyPhrase(e.PaperKey), 90 SkipPush: true, 91 } 92 bkeng := NewPaperKeyGen(e.G(), bkarg) 93 if err := RunEngine2(m, bkeng); err != nil { 94 return err 95 } 96 97 keys := bkeng.DeviceWithKeys() 98 99 // Make sure the key matches the logged in user 100 // use the KID to find the uid 101 uid, err := keys.Populate(m) 102 if err != nil { 103 return err 104 } 105 106 if uid.NotEqual(e.User.GetUID()) { 107 e.G().Log.Debug("paper key entered was for a different user") 108 return fmt.Errorf("paper key valid, but for %s, not %s", uid, e.User.GetUID()) 109 } 110 111 e.perUserKeyring, err = libkb.NewPerUserKeyring(e.G(), e.User.GetUID()) 112 if err != nil { 113 return err 114 } 115 116 // Make new device keys and sign them with this paper key 117 if err = e.paper(m, keys); err != nil { 118 return err 119 } 120 121 // Finish provisoning by calling SwitchConfigAndActiveDevice. we 122 // can't undo that, so do not error out after that. 123 if err := e.deviceWrapEng.SwitchConfigAndActiveDevice(m); err != nil { 124 return err 125 } 126 127 e.sendNotification(m) 128 return nil 129 130 } 131 132 // copied more or less from loginProvision.paper() 133 func (e *PaperProvisionEngine) paper(m libkb.MetaContext, keys *libkb.DeviceWithKeys) error { 134 // After obtaining login session, this will be called before the login state is released. 135 // It signs this new device with the paper key. 136 u := e.User 137 nn := u.GetNormalizedName() 138 uv := u.ToUserVersion() 139 140 // Set the active device to be a special paper key active device, which keeps 141 // a cached copy around for DeviceKeyGen, which requires it to be in memory. 142 // It also will establish a NIST so that API calls can proceed on behalf of the user. 143 m = m.WithProvisioningKeyActiveDevice(keys, uv) 144 if err := m.LoginContext().SetUsernameUserVersion(nn, uv); err != nil { 145 return err 146 } 147 148 // need lksec to store device keys locally 149 if err := e.fetchLKS(m, keys.EncryptionKey()); err != nil { 150 return err 151 } 152 153 if err := e.makeDeviceKeysWithSigner(m, keys.SigningKey()); err != nil { 154 return err 155 } 156 157 // Cache the paper keys globally now that we're logged in 158 m = m.WithGlobalActiveDevice() 159 m.ActiveDevice().CacheProvisioningKey(m, keys) 160 161 return nil 162 } 163 164 func (e *PaperProvisionEngine) sendNotification(m libkb.MetaContext) { 165 e.G().NotifyRouter.HandleLogin(m.Ctx(), string(e.G().Env.GetUsername())) 166 } 167 168 func (e *PaperProvisionEngine) SubConsumers() []libkb.UIConsumer { 169 return []libkb.UIConsumer{ 170 &loginLoadUser{}, 171 } 172 } 173 174 func (e *PaperProvisionEngine) Result() error { 175 return e.result 176 } 177 178 // copied from loginProvision 179 func (e *PaperProvisionEngine) fetchLKS(m libkb.MetaContext, encKey libkb.GenericKey) error { 180 gen, clientLKS, err := fetchLKS(m, encKey) 181 if err != nil { 182 return err 183 } 184 e.lks = libkb.NewLKSecWithClientHalf(clientLKS, gen, e.User.GetUID()) 185 return nil 186 } 187 188 // copied from loginProvision 189 // makeDeviceKeysWithSigner creates device keys given a signing 190 // key. 191 func (e *PaperProvisionEngine) makeDeviceKeysWithSigner(m libkb.MetaContext, signer libkb.GenericKey) error { 192 args, err := e.makeDeviceWrapArgs(m) 193 if err != nil { 194 return err 195 } 196 args.Signer = signer 197 args.IsEldest = false // just to be explicit 198 args.EldestKID = e.User.GetEldestKID() 199 200 return e.makeDeviceKeys(m, args) 201 } 202 203 // copied from loginProvision 204 // makeDeviceWrapArgs creates a base set of args for DeviceWrap. 205 // It ensures that LKSec is created. It also gets a new device 206 // name for this device. 207 func (e *PaperProvisionEngine) makeDeviceWrapArgs(m libkb.MetaContext) (*DeviceWrapArgs, error) { 208 if err := e.ensureLKSec(m); err != nil { 209 return nil, err 210 } 211 212 return &DeviceWrapArgs{ 213 Me: e.User, 214 DeviceName: e.DeviceName, 215 DeviceType: keybase1.DeviceTypeV2_DESKTOP, 216 Lks: e.lks, 217 PerUserKeyring: e.perUserKeyring, 218 }, nil 219 } 220 221 // Copied from loginProvision. makeDeviceKeys uses DeviceWrap to 222 // generate device keys and sets active device. 223 func (e *PaperProvisionEngine) makeDeviceKeys(m libkb.MetaContext, args *DeviceWrapArgs) error { 224 e.deviceWrapEng = NewDeviceWrap(m.G(), args) 225 return RunEngine2(m, e.deviceWrapEng) 226 } 227 228 // copied from loginProvision 229 // ensureLKSec ensures we have LKSec for saving device keys. 230 func (e *PaperProvisionEngine) ensureLKSec(m libkb.MetaContext) error { 231 if e.lks != nil { 232 return nil 233 } 234 235 pps, err := e.ppStream(m) 236 if err != nil { 237 return err 238 } 239 240 e.lks = libkb.NewLKSec(pps, e.User.GetUID()) 241 return nil 242 } 243 244 // copied from loginProvision 245 // ppStream gets the passphrase stream from the cache 246 func (e *PaperProvisionEngine) ppStream(m libkb.MetaContext) (*libkb.PassphraseStream, error) { 247 if m.LoginContext() == nil { 248 return nil, errors.New("loginProvision: ppStream() -> nil ctx.LoginContext") 249 } 250 cached := m.LoginContext().PassphraseStreamCache() 251 if cached == nil { 252 return nil, errors.New("loginProvision: ppStream() -> nil PassphraseStreamCache") 253 } 254 return cached.PassphraseStream(), nil 255 }