/
githubmirror
/
client
Обзор
Документация
Войти
/
githubmirror
/
client
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
go/engine/signup.go
484 строки
13 KB
zoom-ua
gofumpt (#28710)
17 дек 2025, 17:16
Не верифицирован
17 дек 2025, 17:16
cda510b
Код
Авторство
О чём код?
// Copyright 2015 Keybase, Inc. All rights reserved. Use of // this source code is governed by the included BSD license. package engine import ( "encoding/base64" "fmt" "github.com/keybase/client/go/libkb" "github.com/keybase/client/go/protocol/keybase1" triplesec "github.com/keybase/go-triplesec" ) // For password-less signups, number of bytes that are randomly generated // and then encoded with base64 to be used as user's passphrase. const randomPassphraseLen = 16 type SignupEngine struct { pwsalt []byte ppStream *libkb.PassphraseStream tsec libkb.Triplesec uid keybase1.UID me *libkb.User signingKey libkb.GenericKey encryptionKey libkb.NaclDHKeyPair arg *SignupEngineRunArg lks *libkb.LKSec perUserKeyring *libkb.PerUserKeyring // Created after provisioning. Sent to paperkey gen. paperKey *libkb.PaperKeyPhrase } var _ Engine2 = (*SignupEngine)(nil) type SignupEngineRunArg struct { Username string Email string InviteCode string Passphrase string GenerateRandomPassphrase bool StoreSecret bool DeviceName string DeviceType keybase1.DeviceType SkipGPG bool SkipMail bool SkipPaper bool GenPGPBatch bool // if true, generate and push a pgp key to the server (no interaction) VerifyEmail bool // Bot signups have random PWs, no device keys, an eldest paper key, and return a paper key via // the main flow; you need to supply a bot token to signup with them. BotToken keybase1.BotToken // Used in tests for reproducible key generation naclSigningKeyPair libkb.NaclKeyPair naclEncryptionKeyPair libkb.NaclKeyPair } func NewSignupEngine(g *libkb.GlobalContext, arg *SignupEngineRunArg) *SignupEngine { return &SignupEngine{ arg: arg, } } func (s *SignupEngine) Name() string { return "Signup" } func (s *SignupEngine) RequiredUIs() []libkb.UIKind { return nil } func (s *SignupEngine) Prereqs() Prereqs { return Prereqs{} } func (s *SignupEngine) SubConsumers() []libkb.UIConsumer { if s.arg.BotToken.Exists() { return nil } return []libkb.UIConsumer{ &GPGImportKeyEngine{}, &DeviceWrap{}, &PaperKeyPrimary{}, } } func (s *SignupEngine) GetMe() *libkb.User { return s.me } func (s *SignupEngine) PaperKey() *libkb.PaperKeyPhrase { return s.paperKey } func (s *SignupEngine) Run(m libkb.MetaContext) (err error) { defer m.Trace("SignupEngine#Run", &err)() if err = m.LogoutKeepSecrets(); err != nil { return err } // StoreSecret is required if we are doing NOPW if !s.arg.StoreSecret && s.arg.GenerateRandomPassphrase && s.arg.BotToken.IsNil() { return fmt.Errorf("cannot SignUp with StoreSecret=false and GenerateRandomPassphrase=true") } // check if secret store works if s.arg.StoreSecret { if ss := m.G().SecretStore(); ss != nil { if s.arg.GenerateRandomPassphrase && !ss.IsPersistent() { // IsPersistent returns true if SecretStoreLocked is // disk-backed, and false if it's only memory backed. return SecretStoreNotFunctionalError{err: fmt.Errorf("persistent secret store is required for no-passphrase signup")} } err = ss.PrimeSecretStores(m) if err != nil { return SecretStoreNotFunctionalError{err} } } else if s.arg.GenerateRandomPassphrase { return SecretStoreNotFunctionalError{err: fmt.Errorf("secret store is required for no-passphrase signup but wasn't found")} } else { m.Debug("There is no secret store, but we are continuing because this is not a NOPW") } } m = m.WithNewProvisionalLoginContext() if err = s.genPassphraseStream(m, s.arg.Passphrase, s.arg.GenerateRandomPassphrase); err != nil { return err } if s.arg.BotToken.Exists() && s.arg.InviteCode == "" { s.arg.InviteCode, err = libkb.GetInvitationCode(m) if err != nil { return err } } if err = s.join(m, *s.arg); err != nil { return err } s.perUserKeyring, err = libkb.NewPerUserKeyring(m.G(), s.uid) if err != nil { return err } err = s.registerDevice(m, s.arg.DeviceName, s.arg.GenerateRandomPassphrase) if err != nil { return err } if s.arg.BotToken.IsNil() { m.Info("Signed up and provisioned a device.") } // After we are provisioned, do not fail the signup process. Everything // else happening here is optional. if !s.arg.SkipPaper { if err = s.genPaperKeys(m); err != nil { m.Warning("Paper key was not generated. Failed with an error: %s", err) } } // GenPGPBatch can be set in devel CLI to generate // a pgp key and push it to the server without any // user interaction to make testing easier. if s.arg.GenPGPBatch { if err = s.genPGPBatch(m); err != nil { m.Warning("genPGPBatch failed with an error: %s", err) } } if err := s.doGPG(m); err != nil { // We don't care if GPG import fails, continue with the signup process // because it's too late anyway. Failing here would leave a signed up // and logged in user in a weird state where their GUI does not know // they are logged in, and also other processes (CreateWallet) will not // run. m.Warning("Attempt at importing PGP keys from GPG failed with: %s", err) } m = m.CommitProvisionalLogin() // signup complete, notify anyone interested. m.G().NotifyRouter.HandleSignup(m.Ctx(), s.arg.Username) // For instance, setup gregor and friends... m.G().CallLoginHooks(m) m.G().GetStellar().CreateWalletSoft(m.Ctx()) return nil } func (s *SignupEngine) doGPG(m libkb.MetaContext) error { if s.arg.SkipGPG { return nil } // only desktop potentially has gpg, so if not desktop then // bail out if s.arg.DeviceType != keybase1.DeviceType_DESKTOP { return nil } if wantsGPG, err := s.checkGPG(m); err != nil { return err } else if wantsGPG { if err := s.addGPG(m, true, true); err != nil { return fmt.Errorf("addGPG error: %s", err) } } return nil } func (s *SignupEngine) genRandomPassphrase(m libkb.MetaContext) (string, error) { str, err := libkb.RandBytes(randomPassphraseLen) if err != nil { return "", err } return base64.StdEncoding.EncodeToString(str), nil } func (s *SignupEngine) genPassphraseStream(m libkb.MetaContext, passphrase string, randomPW bool) error { if randomPW { if len(passphrase) != 0 { return fmt.Errorf("Tried to generate random passphrase but also provided passphrase argument") } var err error passphrase, err = s.genRandomPassphrase(m) if err != nil { return err } } if len(passphrase) < libkb.MinPassphraseLength { return libkb.PassphraseError{Msg: fmt.Sprintf("Passphrase must be at least %d characters", libkb.MinPassphraseLength)} } salt, err := libkb.RandBytes(triplesec.SaltLen) if err != nil { return err } s.pwsalt = salt s.tsec, s.ppStream, err = libkb.StretchPassphrase(m.G(), passphrase, salt) if err != nil { return err } return nil } func (s *SignupEngine) join(m libkb.MetaContext, arg SignupEngineRunArg) error { m.Debug("SignupEngine#join") joinEngine := NewSignupJoinEngine(m.G()) pdpkda5kid, err := s.ppStream.PDPKA5KID() if err != nil { return err } jarg := SignupJoinEngineRunArg{ Username: arg.Username, Email: arg.Email, InviteCode: arg.InviteCode, PWHash: s.ppStream.PWHash(), PWSalt: s.pwsalt, RandomPW: arg.GenerateRandomPassphrase, SkipMail: arg.SkipMail, PDPKA5KID: pdpkda5kid, VerifyEmail: arg.VerifyEmail, BotToken: arg.BotToken, } res := joinEngine.Run(m, jarg) if res.Err != nil { return res } s.ppStream.SetGeneration(res.PpGen) m.LoginContext().CreateStreamCache(s.tsec, s.ppStream) s.uid = res.UV.Uid luArg := libkb.NewLoadUserArgWithMetaContext(m).WithSelf(true).WithUID(res.UV.Uid).WithPublicKeyOptional() user, err := libkb.LoadUser(luArg) if err != nil { return err } s.me = user return nil } func (s *SignupEngine) generateEldestPaperKey(m libkb.MetaContext, args *DeviceWrapArgs) (err error) { tmp, err := libkb.MakePaperKeyPhrase(libkb.PaperKeyVersion) if err != nil { return err } s.paperKey = &tmp kgarg := &PaperKeyGenArg{ Passphrase: tmp, Me: s.me, SkipPush: true, } eng := NewPaperKeyGen(m.G(), kgarg) err = RunEngine2(m, eng) if err != nil { return err } args.naclSigningKeyPair = eng.SigKey().(libkb.NaclKeyPair) args.naclEncryptionKeyPair = eng.EncKey() args.DeviceName = s.paperKey.Prefix() args.DeviceType = keybase1.DeviceTypeV2_PAPER args.DeviceID = eng.DeviceID() return nil } func (s *SignupEngine) registerDevice(m libkb.MetaContext, deviceName string, randomPw bool) error { m.Debug("SignupEngine#registerDevice") s.lks = libkb.NewLKSec(s.ppStream, s.uid) args := &DeviceWrapArgs{ Me: s.me, DeviceName: libkb.CheckDeviceName.Transform(deviceName), Lks: s.lks, IsEldest: true, naclSigningKeyPair: s.arg.naclSigningKeyPair, naclEncryptionKeyPair: s.arg.naclEncryptionKeyPair, } switch s.arg.DeviceType { case keybase1.DeviceType_DESKTOP: args.DeviceType = keybase1.DeviceTypeV2_DESKTOP case keybase1.DeviceType_MOBILE: args.DeviceType = keybase1.DeviceTypeV2_MOBILE default: if s.arg.BotToken.IsNil() { return fmt.Errorf("unknown device type: %v", s.arg.DeviceType) } } if s.arg.BotToken.Exists() { err := s.generateEldestPaperKey(m, args) if err != nil { return err } } if !libkb.CheckDeviceName.F(args.DeviceName) { m.Debug("invalid device name supplied: %s", args.DeviceName) return libkb.DeviceBadNameError{} } eng := NewDeviceWrap(m.G(), args) err := RunEngine2(m, eng) if err != nil { m.Warning("Failed to provision device: %s", err) if ssErr := s.storeSecretForRecovery(m); ssErr != nil { m.Warning("Failed to store secrets for recovery: %s", ssErr) } return err } if err := eng.SwitchConfigAndActiveDevice(m); err != nil { return err } s.signingKey = eng.SigningKey() s.encryptionKey = eng.EncryptionKey() did := eng.DeviceID() if err := m.LoginContext().LocalSession().SetDeviceProvisioned(did); err != nil { // this isn't a fatal error, session will stay in memory... m.Warning("error saving session file: %s", err) } s.storeSecret(m, randomPw) m.Debug("registered new device: %s", m.G().Env.GetDeviceID()) m.Debug("eldest kid: %s", s.me.GetEldestKID()) return nil } func (s *SignupEngine) storeSecret(m libkb.MetaContext, randomPw bool) { defer m.Trace("SignupEngine#storeSecret", nil)() // Create the secret store as late as possible here, as the username may // change during the signup process. if !s.arg.StoreSecret { m.Debug("not storing secret; disabled") return } w := libkb.StoreSecretAfterLoginWithLKSWithOptions(m, s.me.GetNormalizedName(), s.lks, &libkb.SecretStoreOptions{RandomPw: randomPw}) if w != nil { m.Warning("StoreSecret error: %s", w) } } func (s *SignupEngine) storeSecretForRecovery(m libkb.MetaContext) (err error) { defer m.Trace("SignupEngine#storeSecretForRecovery", &err)() if !s.arg.GenerateRandomPassphrase { m.Debug("Not GenerateRandomPassphrase - skipping storeSecretForRecovery") return nil } username := s.me.GetNormalizedName() err = libkb.StorePwhashEddsaPassphraseStream(m, username, s.ppStream) if err != nil { return err } return nil } func (s *SignupEngine) genPaperKeys(m libkb.MetaContext) error { m.Debug("SignupEngine#genPaperKeys") // Load me again so that keys will be up to date. var err error s.me, err = libkb.LoadUser(libkb.NewLoadUserArgWithMetaContext(m).WithSelf(true).WithUID(s.me.GetUID()).WithPublicKeyOptional()) if err != nil { return err } args := &PaperKeyPrimaryArgs{ Me: s.me, SigningKey: s.signingKey, EncryptionKey: s.encryptionKey, PerUserKeyring: s.perUserKeyring, } eng := NewPaperKeyPrimary(m.G(), args) return RunEngine2(m, eng) } func (s *SignupEngine) checkGPG(m libkb.MetaContext) (bool, error) { eng := NewGPGImportKeyEngine(m.G(), nil) return eng.WantsGPG(m) } func (s *SignupEngine) addGPG(m libkb.MetaContext, allowMulti bool, hasProvisionedDevice bool) (err error) { defer m.Trace(fmt.Sprintf("SignupEngine.addGPG(signingKey: %v)", s.signingKey), &err)() arg := GPGImportKeyArg{Signer: s.signingKey, AllowMulti: allowMulti, Me: s.me, Lks: s.lks, HasProvisionedDevice: hasProvisionedDevice} eng := NewGPGImportKeyEngine(m.G(), &arg) if err = RunEngine2(m, eng); err != nil { return err } if s.signingKey == nil { s.signingKey = eng.LastKey() } return nil } func (s *SignupEngine) genPGPBatch(m libkb.MetaContext) error { m.Debug("SignupEngine#genPGPBatch") gen := libkb.PGPGenArg{ PrimaryBits: 1024, SubkeyBits: 1024, } // genPGPBatch should never be run in production, but if there's // a bug or a mistunderstanding in the future, generate a good key. if m.G().Env.GetRunMode() != libkb.DevelRunMode { gen.PrimaryBits = 4096 gen.SubkeyBits = 4096 } gen.AddDefaultUID(m.G()) tsec, sgen := m.LoginContext().PassphraseStreamCache().TriplesecAndGeneration() eng := NewPGPKeyImportEngine(m.G(), PGPKeyImportEngineArg{ Gen: &gen, PushSecret: true, Lks: s.lks, NoSave: true, PreloadTsec: tsec, PreloadStreamGen: sgen, }) return RunEngine2(m, eng) }