github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/engine/kex2_provisioner.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 "fmt" 8 "time" 9 10 "github.com/keybase/client/go/kex2" 11 "github.com/keybase/client/go/libkb" 12 "github.com/keybase/client/go/msgpack" 13 keybase1 "github.com/keybase/client/go/protocol/keybase1" 14 "github.com/keybase/go-framed-msgpack-rpc/rpc" 15 jsonw "github.com/keybase/go-jsonw" 16 "golang.org/x/net/context" 17 ) 18 19 // Kex2Provisioner is an engine. 20 type Kex2Provisioner struct { 21 libkb.Contextified 22 secret kex2.Secret 23 secretCh chan kex2.Secret 24 me *libkb.User 25 signingKey libkb.GenericKey 26 encryptionKey libkb.NaclDHKeyPair 27 pps keybase1.PassphraseStream 28 provisioneeDeviceName string 29 provisioneeDeviceType keybase1.DeviceTypeV2 30 mctx libkb.MetaContext 31 proof *jsonw.Wrapper 32 } 33 34 // Kex2Provisioner implements kex2.Provisioner interface. 35 var _ kex2.Provisioner = (*Kex2Provisioner)(nil) 36 37 // NewKex2Provisioner creates a Kex2Provisioner engine. 38 func NewKex2Provisioner(g *libkb.GlobalContext, secret kex2.Secret, pps *libkb.PassphraseStream) *Kex2Provisioner { 39 e := &Kex2Provisioner{ 40 Contextified: libkb.NewContextified(g), 41 secret: secret, 42 secretCh: make(chan kex2.Secret), 43 } 44 if pps != nil { 45 e.pps = pps.Export() 46 } 47 48 return e 49 } 50 51 // Name is the unique engine name. 52 func (e *Kex2Provisioner) Name() string { 53 return "Kex2Provisioner" 54 } 55 56 // GetPrereqs returns the engine prereqs. 57 func (e *Kex2Provisioner) Prereqs() Prereqs { 58 return Prereqs{Device: true} 59 } 60 61 // RequiredUIs returns the required UIs. 62 func (e *Kex2Provisioner) RequiredUIs() []libkb.UIKind { 63 return []libkb.UIKind{ 64 libkb.SecretUIKind, 65 libkb.ProvisionUIKind, 66 } 67 } 68 69 // SubConsumers returns the other UI consumers for this engine. 70 func (e *Kex2Provisioner) SubConsumers() []libkb.UIConsumer { 71 return nil 72 } 73 74 // Run starts the provisioner engine. 75 func (e *Kex2Provisioner) Run(m libkb.MetaContext) error { 76 // The guard is acquired later, after the potentially long pause by the user. 77 defer m.G().LocalSigchainGuard().Clear(m.Ctx(), "Kex2Provisioner") 78 79 // before starting provisioning, need to load some information: 80 if err := e.loadMe(); err != nil { 81 return err 82 } 83 if err := m.ActiveDevice().ClearPassphraseStreamCacheIfOutdated(m); err != nil { 84 return err 85 } 86 if err := e.loadSecretKeys(m); err != nil { 87 return err 88 } 89 90 // get current passphrase stream if necessary: 91 if e.pps.PassphraseStream == nil { 92 m.Debug("kex2 provisioner needs passphrase stream, getting it via GetPassphraseStreamStored") 93 pps, err := libkb.GetPassphraseStreamStored(m) 94 if err != nil { 95 return err 96 } 97 e.pps = pps.Export() 98 } 99 100 // Go's context.Context needed by some kex2 callback functions 101 m = m.EnsureCtx() 102 e.mctx = m 103 104 deviceID := m.G().Env.GetDeviceID() 105 106 // all set: start provisioner 107 karg := kex2.KexBaseArg{ 108 Ctx: m.Ctx(), 109 LogCtx: newKex2LogContext(m.G()), 110 Mr: libkb.NewKexRouter(m), 111 DeviceID: deviceID, 112 Secret: e.secret, 113 SecretChannel: e.secretCh, 114 Timeout: 60 * time.Minute, 115 } 116 parg := kex2.ProvisionerArg{ 117 KexBaseArg: karg, 118 Provisioner: e, 119 HelloTimeout: 15 * time.Second, 120 } 121 if err := kex2.RunProvisioner(parg); err != nil { 122 return err 123 } 124 m.G().LocalSigchainGuard().Clear(m.Ctx(), "Kex2Provisioner") 125 126 // successfully provisioned the other device 127 sarg := keybase1.ProvisionerSuccessArg{ 128 DeviceName: e.provisioneeDeviceName, 129 DeviceType: e.provisioneeDeviceType, 130 } 131 return m.UIs().ProvisionUI.ProvisionerSuccess(context.Background(), sarg) 132 } 133 134 func (e *Kex2Provisioner) loadSecretKeys(m libkb.MetaContext) (err error) { 135 // get signing key (including secret key) 136 ska1 := libkb.SecretKeyArg{ 137 Me: e.me, 138 KeyType: libkb.DeviceSigningKeyType, 139 } 140 e.signingKey, err = m.G().Keyrings.GetSecretKeyWithPrompt(m, m.SecretKeyPromptArg(ska1, "new device install")) 141 if err != nil { 142 return err 143 } 144 145 // get encryption key (including secret key) 146 ska2 := libkb.SecretKeyArg{ 147 Me: e.me, 148 KeyType: libkb.DeviceEncryptionKeyType, 149 } 150 encryptionKeyGeneric, err := e.G().Keyrings.GetSecretKeyWithPrompt(m, m.SecretKeyPromptArg(ska2, "new device install")) 151 if err != nil { 152 return err 153 } 154 var ok bool 155 e.encryptionKey, ok = encryptionKeyGeneric.(libkb.NaclDHKeyPair) 156 if !ok { 157 return fmt.Errorf("Unexpected encryption key type") 158 } 159 return nil 160 } 161 162 // AddSecret inserts a received secret into the provisioner's 163 // secret channel. 164 func (e *Kex2Provisioner) AddSecret(s kex2.Secret) { 165 e.secretCh <- s 166 } 167 168 // GetLogFactory implements GetLogFactory in kex2.Provisioner. 169 func (e *Kex2Provisioner) GetLogFactory() rpc.LogFactory { 170 return rpc.NewSimpleLogFactory(e.G().Log, nil) 171 } 172 173 // GetNetworkInstrumenter implements GetNetworkInstrumenter in kex2.Provisioner. 174 func (e *Kex2Provisioner) GetNetworkInstrumenter() rpc.NetworkInstrumenterStorage { 175 return e.G().RemoteNetworkInstrumenterStorage 176 } 177 178 // GetHelloArg implements GetHelloArg in kex2.Provisioner. 179 func (e *Kex2Provisioner) GetHelloArg() (arg keybase1.HelloArg, err error) { 180 181 // Pull the metaContext out of the this object, since we can't pass it through the 182 // kex2/provisioner interface 183 m := e.mctx 184 185 defer m.Trace("Kex2Provisioner#GetHelloArg()", &err)() 186 187 _ = m.UIs().ProvisionUI.DisplaySecretExchanged(context.Background(), 0) 188 189 // get a session token that device Y can use 190 mctx := libkb.NewMetaContextBackground(e.G()) 191 tokener, err := libkb.NewSessionTokener(mctx) 192 if err != nil { 193 return arg, err 194 } 195 token, csrf := tokener.Tokens() 196 197 // generate a skeleton key proof 198 sigBody, err := e.skeletonProof(m) 199 if err != nil { 200 return arg, err 201 } 202 203 // return the HelloArg 204 arg = keybase1.HelloArg{ 205 Uid: e.me.GetUID(), 206 Pps: e.pps, 207 Token: keybase1.SessionToken(token), 208 Csrf: keybase1.CsrfToken(csrf), 209 SigBody: sigBody, 210 } 211 return arg, nil 212 } 213 214 // GetHello2Arg implements GetHello2Arg in kex2.Provisioner. 215 func (e *Kex2Provisioner) GetHello2Arg() (arg2 keybase1.Hello2Arg, err error) { 216 // Pull the metaContext out of the this object, since we can't pass it through the 217 // kex2/provisioner interface 218 m := e.mctx 219 220 defer m.Trace("Kex2Provisioner#GetHello2Arg", &err)() 221 222 var arg1 keybase1.HelloArg 223 arg1, err = e.GetHelloArg() 224 if err != nil { 225 return arg2, err 226 } 227 228 arg2 = keybase1.Hello2Arg{ 229 Uid: arg1.Uid, 230 Token: arg1.Token, 231 Csrf: arg1.Csrf, 232 SigBody: arg1.SigBody, 233 } 234 return arg2, nil 235 } 236 237 // CounterSign implements CounterSign in kex2.Provisioner. 238 func (e *Kex2Provisioner) CounterSign(input keybase1.HelloRes) (sig []byte, err error) { 239 m := e.mctx 240 defer m.Trace("Kex2Provisioner#CounterSign", &err)() 241 242 jw, err := jsonw.Unmarshal([]byte(input)) 243 if err != nil { 244 return nil, err 245 } 246 247 // check the reverse signature and put the values from the provisionee into 248 // e.proof 249 if err = e.checkReverseSig(jw); err != nil { 250 m.Debug("provisioner failed to verify reverse sig: %s", err) 251 return nil, err 252 } 253 m.Debug("provisioner verified reverse sig") 254 255 // remember some device information for ProvisionUI.ProvisionerSuccess() 256 if err = e.rememberDeviceInfo(e.proof); err != nil { 257 return nil, err 258 } 259 260 // sign the whole thing with provisioner's signing key 261 s, _, _, err := libkb.SignJSON(e.proof, e.signingKey) 262 if err != nil { 263 return nil, err 264 } 265 266 return []byte(s), nil 267 } 268 269 // CounterSign2 implements CounterSign in kex2.Provisioner. 270 func (e *Kex2Provisioner) CounterSign2(input keybase1.Hello2Res) (output keybase1.DidCounterSign2Arg, err error) { 271 272 m := e.mctx 273 274 defer m.Trace("Kex2Provisioner#CounterSign2", &err)() 275 var key libkb.GenericKey 276 key, err = libkb.ImportKeypairFromKID(input.EncryptionKey) 277 if err != nil { 278 return output, err 279 } 280 281 output.Sig, err = e.CounterSign(input.SigPayload) 282 if err != nil { 283 return output, err 284 } 285 286 var ppsPacked []byte 287 ppsPacked, err = msgpack.Encode(e.pps) 288 if err != nil { 289 return output, err 290 } 291 output.PpsEncrypted, err = key.EncryptToString(ppsPacked, nil) 292 293 // Sync the PUK, if the pukring is nil, we don't have a PUK and have 294 // nothing to box. We also can't make a userEKBox which is signed by the 295 // PUK. 296 pukring, err := e.syncPUK(m) 297 if err != nil || pukring == nil { 298 return output, err 299 } 300 301 output.PukBox, err = e.makePukBox(m, pukring, key) 302 if err != nil { 303 return output, err 304 } 305 306 userEKBoxStorage := m.G().GetUserEKBoxStorage() 307 if input.DeviceEkKID.Exists() && userEKBoxStorage != nil { 308 // If we error out here the provisionee will create it's own keys later 309 // but we shouldn't fail kex. 310 userEKBox, ekErr := makeUserEKBoxForProvisionee(m, input.DeviceEkKID) 311 if ekErr != nil { 312 userEKBox = nil 313 m.Debug("Unable to makeUserEKBox %v", ekErr) 314 } 315 output.UserEkBox = userEKBox 316 } else { 317 m.Debug("Skipping userEKBox generation empty KID or storage. KID: %v, storage: %v", input.DeviceEkKID, userEKBoxStorage) 318 } 319 320 return output, nil 321 } 322 323 // skeletonProof generates a partial key proof structure that device Y can 324 // fill in. When verifying the reverse signature we fill in the values from Y 325 // to check the reverse signature 326 func (e *Kex2Provisioner) skeletonProof(m libkb.MetaContext) (sigBody string, err error) { 327 328 // Set the local sigchain guard to tell background tasks 329 // to stay off the sigchain while we do this. 330 // This is released at the end of Kex2Provisioner#Run 331 e.G().LocalSigchainGuard().Set(context.TODO(), "Kex2Provisioner") 332 333 // reload the self user to make sure it is fresh 334 // (this fixes TestProvisionWithRevoke [CORE-5631, CORE-5636]) 335 if err := e.loadMe(); err != nil { 336 return "", err 337 } 338 339 delg := libkb.Delegator{ 340 ExistingKey: e.signingKey, 341 Me: e.me, 342 DelegationType: libkb.DelegationTypeSibkey, 343 Expire: libkb.NaclEdDSAExpireIn, 344 Contextified: libkb.NewContextified(e.G()), 345 } 346 347 e.proof, err = libkb.KeyProof(m, delg) 348 if err != nil { 349 return "", err 350 } 351 body, err := e.proof.Marshal() 352 if err != nil { 353 return "", err 354 } 355 return string(body), nil 356 } 357 358 // checkReverseSig verifies that the reverse sig in jw is valid and matches 359 // e.proof. The provisionee is only allowed to pass the following fields to the 360 // provisioner: 361 // body.device 362 // body.sibkey.kid 363 // The values at these paths in the json reserialized and are inserted into the 364 // skeleton proof that we initially passed to the provisionee so we can ensure 365 // no other values were added when verifying the signature. 366 func (e *Kex2Provisioner) checkReverseSig(jw *jsonw.Wrapper) error { 367 kid, err := jw.AtPath("body.sibkey.kid").GetString() 368 if err != nil { 369 return err 370 } 371 372 keypair, err := libkb.ImportKeypairFromKID(keybase1.KIDFromString(kid)) 373 if err != nil { 374 return err 375 } 376 377 revsig, err := jw.AtPath("body.sibkey.reverse_sig").GetString() 378 if err != nil { 379 return err 380 } 381 382 // set reverse_sig to nil to verify it: 383 err = e.proof.SetValueAtPath("body.sibkey.reverse_sig", jsonw.NewNil()) 384 if err != nil { 385 return err 386 } 387 388 // Copy known fields that provisionee set into e.proof 389 deviceWrapper := jw.AtPath("body.device") 390 // NOTE the time value is dropped during Export, value here is arbitrary. 391 device, err := libkb.ParseDevice(deviceWrapper, time.Now()) 392 if err != nil { 393 return err 394 } 395 dw, err := device.Export(libkb.LinkType(libkb.DelegationTypeSibkey)) 396 if err != nil { 397 return err 398 } 399 err = e.proof.SetValueAtPath("body.device", dw) 400 if err != nil { 401 return err 402 } 403 err = e.proof.SetValueAtPath("body.sibkey.kid", jsonw.NewString(kid)) 404 if err != nil { 405 return err 406 } 407 408 msg, err := e.proof.Marshal() 409 if err != nil { 410 return err 411 } 412 _, err = keypair.VerifyString(e.G().Log, revsig, msg) 413 if err != nil { 414 return err 415 } 416 417 // put reverse_sig back in 418 return e.proof.SetValueAtPath("body.sibkey.reverse_sig", jsonw.NewString(revsig)) 419 } 420 421 // rememberDeviceInfo saves the device name and type in 422 // Kex2Provisioner for later use. 423 func (e *Kex2Provisioner) rememberDeviceInfo(jw *jsonw.Wrapper) error { 424 name, err := jw.AtPath("body.device.name").GetString() 425 if err != nil { 426 return err 427 } 428 e.provisioneeDeviceName = name 429 430 dtype, err := jw.AtPath("body.device.type").GetString() 431 if err != nil { 432 return err 433 } 434 e.provisioneeDeviceType, err = keybase1.StringToDeviceTypeV2(dtype) 435 436 return err 437 } 438 439 // Returns nil if there are no per-user-keys. 440 func (e *Kex2Provisioner) syncPUK(m libkb.MetaContext) (*libkb.PerUserKeyring, error) { 441 pukring, err := e.G().GetPerUserKeyring(m.Ctx()) 442 if err != nil { 443 return nil, err 444 } 445 if err = pukring.Sync(m); err != nil { 446 return nil, err 447 } 448 if !pukring.HasAnyKeys() { 449 return nil, nil 450 } 451 return pukring, nil 452 } 453 454 func (e *Kex2Provisioner) makePukBox(m libkb.MetaContext, pukring *libkb.PerUserKeyring, receiverKeyGeneric libkb.GenericKey) (*keybase1.PerUserKeyBox, error) { 455 receiverKey, ok := receiverKeyGeneric.(libkb.NaclDHKeyPair) 456 if !ok { 457 return nil, fmt.Errorf("Unexpected receiver key type") 458 } 459 460 pukBox, err := pukring.PrepareBoxForNewDevice(m, 461 receiverKey, // receiver key: provisionee enc 462 e.encryptionKey) // sender key: this device enc 463 return &pukBox, err 464 } 465 466 func (e *Kex2Provisioner) loadMe() error { 467 var err error 468 e.me, err = libkb.LoadMe(libkb.NewLoadUserArg(e.G())) 469 return err 470 }