Esempio n. 1
0
        protected void AdjustBlockingScoreForPastTyposTreatedAsFullFailures(
            IpHistory clientsIpHistory,
            IUserAccountController <TUserAccount> accountController,
            TUserAccount account,
            DateTime whenUtc,
            string correctPassword,
            byte[] phase1HashOfCorrectPassword)
        {
            double credit = 0d;

            if (clientsIpHistory == null)
            {
                return;
            }

            LoginAttemptSummaryForTypoAnalysis[] recentPotentialTypos = clientsIpHistory.RecentPotentialTypos.MostRecentFirst.ToArray();
            Encryption.IPrivateKey privateAccountLogKey = null;
            try
            {
                foreach (LoginAttemptSummaryForTypoAnalysis potentialTypo in recentPotentialTypos)
                {
                    bool usernameCorrect = potentialTypo.UsernameOrAccountId == account.UsernameOrAccountId;

                    if (usernameCorrect)
                    {
                        string passwordSubmittedInFailedAttempt = null;
                        if (account.GetType().Name == "SimulatedUserAccount")
                        {
                            passwordSubmittedInFailedAttempt = potentialTypo.EncryptedIncorrectPassword.Ciphertext;
                        }
                        else
                        {
                            if (privateAccountLogKey == null)
                            {
                                try
                                {
                                    privateAccountLogKey = accountController.DecryptPrivateAccountLogKey(account,
                                                                                                         phase1HashOfCorrectPassword);
                                }
                                catch (Exception)
                                {
                                    return;
                                }
                            }
                            try
                            {
                                passwordSubmittedInFailedAttempt =
                                    potentialTypo.EncryptedIncorrectPassword.Read(privateAccountLogKey);
                            }
                            catch (Exception)
                            {
                            }
                        }

                        if (passwordSubmittedInFailedAttempt != null)
                        {
                            bool passwordHadTypo =
                                EditDistance.Calculate(passwordSubmittedInFailedAttempt, correctPassword) <=
                                _options.MaxEditDistanceConsideredATypo;

                            if (passwordHadTypo)
                            {
                                credit += potentialTypo.Penalty.GetValue(_options.AccountCreditLimitHalfLife, whenUtc) *
                                          (1d - _options.PenaltyMulitiplierForTypo);
                            }
                        }
                    }

                    clientsIpHistory.RecentPotentialTypos.Remove(potentialTypo);
                }

                clientsIpHistory.CurrentBlockScore.SubtractInPlace(account.CreditHalfLife, credit, whenUtc);
            }
            finally
            {
                privateAccountLogKey?.Dispose();
            }
        }
        /// <summary>
        /// This analysis will examine the client IP's previous failed attempts to login to this account
        /// to determine if any failed attempts were due to typos.
        /// </summary>
        /// <param name="clientsIpHistory">Records of this client's previous attempts to examine.</param>
        /// <param name="accountController"></param>
        /// <param name="account">The account that the client is currently trying to login to.</param>
        /// <param name="whenUtc"></param>
        /// <param name="correctPassword">The correct password for this account.  (We can only know it because
        /// the client must have provided the correct one this loginAttempt.)</param>
        /// <param name="phase1HashOfCorrectPassword">The phase1 hash of that correct password (which we could
        /// recalculate from the information in the previous parameters, but doing so would be expensive.)</param>
        /// <returns></returns>
        protected void AdjustBlockingScoreForPastTyposTreatedAsFullFailures(
            IpHistory clientsIpHistory,
            IUserAccountController <TUserAccount> accountController,
            TUserAccount account,
            DateTime whenUtc,
            string correctPassword,
            byte[] phase1HashOfCorrectPassword)
        {
            double credit = 0d;

            if (clientsIpHistory == null)
            {
                return;
            }

            LoginAttemptSummaryForTypoAnalysis[] recentPotentialTypos = clientsIpHistory.RecentPotentialTypos.MostRecentFirst.ToArray();
            Encryption.IPrivateKey privateAccountLogKey = null;
            try
            {
                foreach (LoginAttemptSummaryForTypoAnalysis potentialTypo in recentPotentialTypos)
                {
                    bool usernameCorrect = potentialTypo.UsernameOrAccountId == account.UsernameOrAccountId;
                    //bool usernameHadTypo = !usernameCorrect &&
                    //    EditDistance.Calculate(potentialTypo.UsernameOrAccountId, account.UsernameOrAccountId) <=
                    //_options.MaxEditDistanceConsideredATypo;

                    if (usernameCorrect) // || usernameHadTypo
                    {
                        // Get the plaintext password from the previous login attempt
                        string passwordSubmittedInFailedAttempt = null;
                        if (account.GetType().Name == "SimulatedUserAccount")
                        {
                            passwordSubmittedInFailedAttempt = potentialTypo.EncryptedIncorrectPassword.Ciphertext;
                        }
                        else
                        {
                            if (privateAccountLogKey == null)
                            {
                                // Get the EC decryption key, which is stored encrypted with the Phase1 password hash
                                try
                                {
                                    privateAccountLogKey = accountController.DecryptPrivateAccountLogKey(account,
                                                                                                         phase1HashOfCorrectPassword);
                                }
                                catch (Exception)
                                {
                                    // There's a problem with the key that prevents us from decrypting it.  We won't be able to do this analysis.
                                    return;
                                }
                            }
                            // Now try to decrypt the incorrect password from the previous attempt and perform the typo analysis
                            try
                            {
                                // Attempt to decrypt the password.
                                passwordSubmittedInFailedAttempt =
                                    potentialTypo.EncryptedIncorrectPassword.Read(privateAccountLogKey);
                            }
                            catch (Exception)
                            {
                                // An exception is likely due to an incorrect key (perhaps outdated).
                                // Since we simply can't do anything with a record we can't Decrypt, we carry on
                                // as if nothing ever happened.  No.  Really.  Nothing to see here.
                            }
                        }

                        if (passwordSubmittedInFailedAttempt != null)
                        {
                            //bool passwordCorrect = passwordSubmittedInFailedAttempt == correctPassword;
                            bool passwordHadTypo = // !passwordCorrect &&
                                                   EditDistance.Calculate(passwordSubmittedInFailedAttempt, correctPassword) <=
                                                   _options.MaxEditDistanceConsideredATypo;

                            // Get credit for this nearly-correct attempt to counter penalty
                            if (passwordHadTypo)
                            {
                                credit += potentialTypo.Penalty.GetValue(_options.AccountCreditLimitHalfLife, whenUtc) *
                                          (1d - _options.PenaltyMulitiplierForTypo);
                            }
                        }
                    }

                    // Now that we know whether this past event was a typo or not, we no longer need to keep track
                    // of it (and we should remove it so we don't double credit it).
                    clientsIpHistory.RecentPotentialTypos.Remove(potentialTypo);
                }

                // Remove the amount to be credited from the block score due to the discovery of typos
                clientsIpHistory.CurrentBlockScore.SubtractInPlace(account.CreditHalfLife, credit, whenUtc);
            }
            finally
            {
                privateAccountLogKey?.Dispose();
            }
        }