public override State Advance(TwoFactorAuth owner)
            {
                var validAnswers = new[] { Ui.Answer.Check, Ui.Answer.Resend, Ui.Answer.Email };
                var answer       = owner._ui.AskToWaitForOob(owner._settings.Devices[_deviceIndex].Name,
                                                             owner._settings.Email,
                                                             validAnswers);

                switch (answer)
                {
                case Ui.Answer.Check:
                    return(Check(owner));

                case Ui.Answer.Resend:
                    Client.AuthSendPush(owner._clientInfo,
                                        owner._settings.Devices[_deviceIndex].Id,
                                        owner._settings.TransactionId,
                                        owner._rest);
                    return(this);

                case Ui.Answer.Email:
                    Client.AuthSendEmail(owner._clientInfo,
                                         owner._settings.Email,
                                         owner._settings.TransactionId,
                                         owner._rest);
                    return(new WaitForEmail());
                }

                throw new InternalErrorException($"Invalid answer '{answer}'");
            }
            public override State Advance(TwoFactorAuth owner)
            {
                var names        = owner._settings.Devices.Select(i => i.Name).ToArray();
                var validAnswers = Enumerable.Range(0, owner._settings.Devices.Length)
                                   .Select(i => Ui.Answer.Device0 + i)
                                   .Concat(new[] { Ui.Answer.Email })
                                   .ToArray();
                var answer = owner._ui.AskToChooseOob(names, owner._settings.Email, validAnswers);

                if (answer == Ui.Answer.Email)
                {
                    Client.AuthSendEmail(owner._clientInfo,
                                         owner._settings.Email,
                                         owner._settings.TransactionId,
                                         owner._rest);
                    return(new WaitForEmail());
                }

                var deviceIndex = answer - Ui.Answer.Device0;

                if (deviceIndex >= 0 && deviceIndex < owner._settings.Devices.Length)
                {
                    Client.AuthSendPush(owner._clientInfo,
                                        owner._settings.Devices[deviceIndex].Id,
                                        owner._settings.TransactionId,
                                        owner._rest);
                    return(new WaitForOob(deviceIndex));
                }

                throw new InternalErrorException($"Invalid answer '{answer}'");
            }
            // TODO: Shared code for most states. It's not really good that it's in the base class.
            protected State Check(TwoFactorAuth owner)
            {
                var result = Client.AuthCheck(owner._clientInfo,
                                              owner._settings.TransactionId,
                                              owner._rest);

                return(new Done(result));
            }
 public override State Advance(TwoFactorAuth owner)
 {
     Client.AuthSendPush(owner._clientInfo,
                         owner._settings.Devices[_deviceIndex].Id,
                         owner._settings.TransactionId,
                         owner._rest);
     return(new WaitForOob(_deviceIndex));
 }
 public override State Advance(TwoFactorAuth owner)
 {
     Client.AuthSendEmail(owner._clientInfo,
                          owner._settings.Email,
                          owner._settings.TransactionId,
                          owner._rest);
     return(new WaitForEmail());
 }
            // TODO: Shared code for most states. It's not really good that it's in the base class.
            protected State Check(TwoFactorAuth owner)
            {
                var result = Client.AuthCheck(owner._clientInfo,
                                              owner._settings.TransactionId,
                                              owner._rest);

                if (result == null)
                {
                    return(new Failure("Failed"));
                }
                return(new Done(result));
            }
 public override State Advance(TwoFactorAuth owner) => throw new NotImplementedException();
 public abstract State Advance(TwoFactorAuth owner);
 public virtual State Advance(TwoFactorAuth owner)
 {
     throw new InternalErrorException("Unreachable code");
 }
        public static Account[] OpenVault(string username,
                                          string password,
                                          Ui ui,
                                          ISecureStorage storage,
                                          IRestTransport transport)
        {
            var rest = new RestClient(transport);

            // Step 1: Register a new deice or use the existing one from the previous run.
            var deviceInfo = LoadDeviceInfo(storage) ?? RegisterNewDevice("truekey-sharp", rest);

            // Step 2: Parse the token to decode OTP information.
            var otpInfo = Util.ParseClientToken(deviceInfo.Token);

            // Step 3: Validate the OTP info to make sure it's got only the
            //         things we support at the moment.
            Util.ValidateOtpInfo(otpInfo);

            // Store the token and ID for the next time.
            StoreDeviceInfo(deviceInfo, storage);

            // Bundle up everything in one place
            var clientInfo = new ClientInfo(username, "truekey-sharp", deviceInfo, otpInfo);

            // Step 4: Auth step 1 gives us a transaction id to pass along to the next step.
            var transactionId = AuthStep1(clientInfo, rest);

            // Step 5: Auth step 2 gives us the instructions on what to do next. For a new client that
            //         would be some form of second factor auth. For a known client that would be a
            //         pair of OAuth tokens.
            var whatsNext = AuthStep2(clientInfo, password, transactionId, rest);

            // The device is trusted if it's already authenticated at this point and
            // no second factor is needed.
            var isTrusted = whatsNext.IsAuthenticated;

            // Step 6: Auth FSM -- walk through all the auth steps until we're done.
            var oauthToken = TwoFactorAuth.Start(clientInfo, whatsNext, ui, rest);

            // Step 7: Save this device as trusted not to repeat the two factor dance next times.
            if (!isTrusted)
            {
                SaveDeviceAsTrusted(clientInfo, transactionId, oauthToken, rest);
            }

            // Step 8: Get the vault from the server.
            var encryptedVault = GetVault(oauthToken, rest);

            // Step 9: Compute the master key.
            var masterKey = Util.DecryptMasterKey(password,
                                                  encryptedVault.MasterKeySalt,
                                                  encryptedVault.EncryptedMasterKey);

            // Step 10: Decrypt the accounts.
            var accounts = encryptedVault
                           .EncryptedAccounts
                           .Select(i => new Account(id: i.Id,
                                                    name: i.Name,
                                                    username: i.Username,
                                                    password: Util.Decrypt(masterKey, i.EncryptedPassword).ToUtf8(),
                                                    url: i.Url,
                                                    note: Util.Decrypt(masterKey, i.EncryptedNote).ToUtf8()))
                           .ToArray();

            return(accounts);
        }