예제 #1
0
        //public DecayingDouble AllFailures(TimeSpan halfLife) => AccountFailures.Add(halfLife, PasswordFailures);

        //public DecayingDouble AccountFailuresSubsetWithInfrequentPassword(TimeSpan halfLife) => AccountFailures.Subtract(halfLife, AccountFailuresSubsetWithFrequentPassword);
        //public DecayingDouble PasswordFailuresSubsetWithInfrequentPassword(TimeSpan halfLife) => PasswordFailures.Subtract(halfLife, PasswordFailuresSubsetWithFrequentPassword);
        //public DecayingDouble PasswordFailuresSubsetWithoutTypo(TimeSpan halfLife) => PasswordFailures.Subtract(halfLife, PasswordFailuresSubsetWithTypo);
        //public DecayingDouble PasswordFailuresSubsetWithoutEitherFrequentPasswordOrTypo(TimeSpan halfLife) => PasswordFailures.Subtract(halfLife, PasswordFailuresSubsetWithTypoAndFrequentPassword);

        /// <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="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>
        /// <returns></returns>
        public void AdjustBlockingScoreForPastTyposTreatedAsFullFailures(
            Simulator simulator,
            SimulatedUserAccount account,
            DateTime whenUtc,
            string correctPassword)
        {
            SimLoginAttemptSummaryForTypoAnalysis[] recentPotentialTypos =
                RecentPotentialTypos.MostRecentFirst.ToArray();
            foreach (SimLoginAttemptSummaryForTypoAnalysis potentialTypo in recentPotentialTypos)
            {
                if (account == null || potentialTypo.UsernameOrAccountId != account.UsernameOrAccountId)
                {
                    continue;
                }

                // Use an edit distance calculation to determine if it was a likely typo
                bool likelyTypo =
                    EditDistance.Calculate(potentialTypo.Password, correctPassword) <=
                    simulator._experimentalConfiguration.BlockingOptions.MaxEditDistanceConsideredATypo;

                TimeSpan       halfLife = simulator._experimentalConfiguration.BlockingOptions.BlockScoreHalfLife;
                DecayingDouble value    = new DecayingDouble(1d, potentialTypo.WhenUtc);
                // Add this to the list of changed attempts
                if (potentialTypo.WasPasswordFrequent)
                {
                    PasswordFailuresNoTypoFrequentPassword.SubtractInPlace(halfLife, value);
                    PasswordFailuresTypoFrequentPassword.AddInPlace(halfLife, value);
                }
                RecentPotentialTypos.Remove(potentialTypo);
            }
        }
예제 #2
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();
            }
        }
예제 #4
0
 public void EditDistanceTranspose()
 {
     Assert.Equal(0.8f, EditDistance.Calculate("Transposition", "Transopsition", new EditDistance.Costs(transpose: .8f)));
 }
예제 #5
0
 public void EditDistanceChangeCase()
 {
     Assert.Equal(0.8f, EditDistance.Calculate("CaseSensitive", "casesensitive", new EditDistance.Costs(caseChange: 0.4f)));
 }
예제 #6
0
 public void EditDistanceAdd()
 {
     Assert.Equal(0.4f, EditDistance.Calculate("dd", "Add", new EditDistance.Costs(add: 0.4f)));
     Assert.Equal(0.4f, EditDistance.Calculate("Add", "Add!", new EditDistance.Costs(add: 0.4f)));
 }
예제 #7
0
 public void EditDistanceDelete()
 {
     Assert.Equal(0.4f, EditDistance.Calculate("Deletion", "eletion", new EditDistance.Costs(delete: 0.4f)));
 }
예제 #8
0
 public void EditDistanceSubstitute()
 {
     Assert.Equal(0.75f, EditDistance.Calculate("Substitution", "Sabstitution!", new EditDistance.Costs(substitute: .25f, add: .5f)));
 }