A class that represents a double that decays over time using a half life, to borrow a concept from radioactive decay. A value that is assigned a value 1d and a half life of one day will have value 0.5d after one day, .25d after two days, and so on. More generally, if you assign the value x, after e half lives it's value will be x/(2^e). Since a double that decays with time will have a constantly-changing real value, it should never be used as the key into a Dictionary or kept in a HashSet. It should not be compared for exact equality.
 public void SubtractInPlace()
 {
     DecayingDouble d = new DecayingDouble(4, FirstDayOfCentury);
     // One day later the double should be 2, so subtracting 1 should yield 1
     d.SubtractInPlace(OneDay, 1, OneDayLater);
     Assert.InRange(d.ValueAtTimeOfLastUpdate, .99999, 1.000001);
 }
Exemple #2
0
 public IpHistory(
     IPAddress address,
     BlockingAlgorithmOptions options)
 {
     Address = address;
     CurrentBlockScore = new DecayingDouble();
     RecentPotentialTypos =
         new SmallCapacityConstrainedSet<LoginAttemptSummaryForTypoAnalysis>(options.NumberOfFailuresToTrackForGoingBackInTimeToIdentifyTypos);
 }
 public void AddInPlace()
 {
     DecayingDouble d = new DecayingDouble(4, FirstDayOfCentury);
     // Two days later the double should be 1, so adding 1 should yield 4
     d.AddInPlace(OneDay, 3, TwoDaysLater);
     Assert.InRange(d.ValueAtTimeOfLastUpdate, 3.99999, 4.000001);
     double shouldBeVeryCloseTo1 = d.GetValue(OneDay, FourDaysLater);
     Assert.InRange(shouldBeVeryCloseTo1, .99999, 1.000001);
 }
 public DecayingDouble Add(TimeSpan halfLife, DecayingDouble amountToAdd)
 {
     if (!LastUpdatedUtc.HasValue)
     {
         return new DecayingDouble(ValueAtTimeOfLastUpdate + amountToAdd.ValueAtTimeOfLastUpdate, amountToAdd.LastUpdatedUtc);
     }
     else if (!amountToAdd.LastUpdatedUtc.HasValue)
     {
         return new DecayingDouble(ValueAtTimeOfLastUpdate + amountToAdd.ValueAtTimeOfLastUpdate, LastUpdatedUtc);
     }
     else if (LastUpdatedUtc.Value > amountToAdd.LastUpdatedUtc.Value)
     {
         return new DecayingDouble(
                 ValueAtTimeOfLastUpdate + amountToAdd.GetValue(halfLife, LastUpdatedUtc.Value),
                 LastUpdatedUtc.Value);
     }
     else
     {
         return new DecayingDouble(amountToAdd.ValueAtTimeOfLastUpdate + GetValue(halfLife, amountToAdd.LastUpdatedUtc.Value), amountToAdd.LastUpdatedUtc.Value);
     }
 }
 public DecayingDouble Add(TimeSpan halfLife, DecayingDouble amountToAdd)
 {
     if (!LastUpdatedUtc.HasValue)
     {
         return(new DecayingDouble(ValueAtTimeOfLastUpdate + amountToAdd.ValueAtTimeOfLastUpdate, amountToAdd.LastUpdatedUtc));
     }
     else if (!amountToAdd.LastUpdatedUtc.HasValue)
     {
         return(new DecayingDouble(ValueAtTimeOfLastUpdate + amountToAdd.ValueAtTimeOfLastUpdate, LastUpdatedUtc));
     }
     else if (LastUpdatedUtc.Value > amountToAdd.LastUpdatedUtc.Value)
     {
         return(new DecayingDouble(
                    ValueAtTimeOfLastUpdate + amountToAdd.GetValue(halfLife, LastUpdatedUtc.Value),
                    LastUpdatedUtc.Value));
     }
     else
     {
         return(new DecayingDouble(amountToAdd.ValueAtTimeOfLastUpdate + GetValue(halfLife, amountToAdd.LastUpdatedUtc.Value), amountToAdd.LastUpdatedUtc.Value));
     }
 }
 public DecayingDouble Subtract(TimeSpan halfLife, DecayingDouble amountToRemove)
 {
     if (!LastUpdatedUtc.HasValue)
     {
         return(new DecayingDouble(ValueAtTimeOfLastUpdate - amountToRemove.ValueAtTimeOfLastUpdate, amountToRemove.LastUpdatedUtc));
     }
     else if (!amountToRemove.LastUpdatedUtc.HasValue)
     {
         return(new DecayingDouble(ValueAtTimeOfLastUpdate - amountToRemove.ValueAtTimeOfLastUpdate, LastUpdatedUtc));
     }
     else if (LastUpdatedUtc.Value > amountToRemove.LastUpdatedUtc.Value)
     {
         return
             (new DecayingDouble(
                  ValueAtTimeOfLastUpdate - amountToRemove.GetValue(halfLife, LastUpdatedUtc.Value),
                  LastUpdatedUtc.Value));
     }
     else
     {
         return(new DecayingDouble(amountToRemove.ValueAtTimeOfLastUpdate - GetValue(halfLife, amountToRemove.LastUpdatedUtc.Value), amountToRemove.LastUpdatedUtc.Value));
     }
 }
 public DecayingDouble Subtract(TimeSpan halfLife, DecayingDouble amountToRemove)
 {
     if (!LastUpdatedUtc.HasValue)
     {
         return new DecayingDouble(ValueAtTimeOfLastUpdate - amountToRemove.ValueAtTimeOfLastUpdate, amountToRemove.LastUpdatedUtc);
     }
     else if (!amountToRemove.LastUpdatedUtc.HasValue)
     {
         return new DecayingDouble(ValueAtTimeOfLastUpdate - amountToRemove.ValueAtTimeOfLastUpdate, LastUpdatedUtc);
     } else if (LastUpdatedUtc.Value > amountToRemove.LastUpdatedUtc.Value)
     {
         return
             new DecayingDouble(
                 ValueAtTimeOfLastUpdate - amountToRemove.GetValue(halfLife, LastUpdatedUtc.Value),
                 LastUpdatedUtc.Value);
     }
     else
     {
         return new DecayingDouble(amountToRemove.ValueAtTimeOfLastUpdate - GetValue(halfLife, amountToRemove.LastUpdatedUtc.Value), amountToRemove.LastUpdatedUtc.Value);
     }
 }
 public void SubtractInPlace(TimeSpan halfLife, DecayingDouble amountToSubtract)
     => SubtractInPlace(halfLife, amountToSubtract.ValueAtTimeOfLastUpdate, amountToSubtract.LastUpdatedUtc);
 public void AddInPlace(TimeSpan halfLife, DecayingDouble amountToAdd)
     => AddInPlace(halfLife, amountToAdd.ValueAtTimeOfLastUpdate, amountToAdd.LastUpdatedUtc);
        //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);
            }
        }
 public void SubtractInPlace(TimeSpan halfLife, DecayingDouble amountToSubtract)
 => SubtractInPlace(halfLife, amountToSubtract.ValueAtTimeOfLastUpdate, amountToSubtract.LastUpdatedUtc);
 public void AddInPlace(TimeSpan halfLife, DecayingDouble amountToAdd)
 => AddInPlace(halfLife, amountToAdd.ValueAtTimeOfLastUpdate, amountToAdd.LastUpdatedUtc);
        public void UpdateSimulatorState(Simulator simulator, SimIpHistory ipHistory)
        {
            IsRepeatFailure = !IsPasswordValid && (
                (SimAccount == null)
                    ? simulator._recentIncorrectPasswords.AddMember(UserNameOrAccountId + "\n" + Password)
                    : simulator._userAccountController.AddIncorrectPhaseTwoHash(SimAccount, Password, TimeOfAttemptUtc)
            );

            int passwordsHeightOnBinomialLadder = (IsPasswordValid || IsRepeatFailure)
                ? simulator._binomialLadderFilter.GetHeight(Password)
                : simulator._binomialLadderFilter.Step(Password);

            IsFrequentlyGuessedPassword = passwordsHeightOnBinomialLadder >=
                                          simulator._experimentalConfiguration.BlockingOptions.BinomialLadderFrequencyThreshdold_T;

            DeviceCookieHadPriorSuccessfulLoginForThisAccount = SimAccount != null &&
                simulator._userAccountController.HasClientWithThisHashedCookieSuccessfullyLoggedInBefore(SimAccount, CookieProvidedByBrowser);

            if (SimAccount != null && IsPasswordValid)
            {
                // Determine if any of the outcomes for login attempts from the client IP for this request were the result of typos,
                // as this might impact our decision about whether or not to block this client IP in response to its past behaviors.
                ipHistory.AdjustBlockingScoreForPastTyposTreatedAsFullFailures(simulator, SimAccount, TimeOfAttemptUtc,
                    Password);
                simulator._userAccountController.RecordHashOfDeviceCookieUsedDuringSuccessfulLoginBackground(
                    SimAccount, CookieProvidedByBrowser, TimeOfAttemptUtc);
                // Clear the count of consecutive failures
                SimAccount.ConsecutiveIncorrectAttempts.SetValue(0, this.TimeOfAttemptUtc);
            }
            else if (SimAccount != null && !IsRepeatFailure)
            {
                // Add the the account's consecutive failure count
                SimAccount.ConsecutiveIncorrectAttempts.AddInPlace(
                    simulator._experimentalConfiguration.BlockingOptions.BlockScoreHalfLife, 1d,
                    this.TimeOfAttemptUtc);
                // Increase the max consecutive faiulre count if the current consecutive failure count exceeds it
                if (SimAccount.ConsecutiveIncorrectAttempts.GetValue(
                        simulator._experimentalConfiguration.BlockingOptions.BlockScoreHalfLife)
                    >
                    SimAccount.MaxConsecutiveIncorrectAttempts.GetValue(
                        simulator._experimentalConfiguration.BlockingOptions.BlockScoreHalfLife))
                    SimAccount.MaxConsecutiveIncorrectAttempts.SetValue(SimAccount.ConsecutiveIncorrectAttempts);
            }

            if (!IsPasswordValid && !IsRepeatFailure && SimAccount != null)
            {
                // This attempt is a non-repeat failure and could be a typo.  Store it in the ste of potential typos.
                ipHistory.RecentPotentialTypos.Add(new SimLoginAttemptSummaryForTypoAnalysis()
                {
                    WhenUtc = TimeOfAttemptUtc,
                    Password = Password,
                    UsernameOrAccountId = UserNameOrAccountId,
                    WasPasswordFrequent = IsFrequentlyGuessedPassword
                });
            }


            DecayingDouble decayingOneFromThisInstant = new DecayingDouble(1, TimeOfAttemptUtc);
            TimeSpan halfLife = simulator._experimentalConfiguration.BlockingOptions.BlockScoreHalfLife;
            if (IsPasswordValid)
            {
                ipHistory.SuccessfulLogins.AddInPlace(halfLife, decayingOneFromThisInstant);
            } else if (SimAccount == null)
            {
                if (IsRepeatFailure)
                {
                    if (IsFrequentlyGuessedPassword)
                        ipHistory.RepeatAccountFailuresFrequentPassword.AddInPlace(halfLife, decayingOneFromThisInstant);
                    else
                        ipHistory.RepeatAccountFailuresInfrequentPassword.AddInPlace(halfLife, decayingOneFromThisInstant);
                }
                else
                {
                    if (IsFrequentlyGuessedPassword)
                        ipHistory.AccountFailuresFrequentPassword.AddInPlace(halfLife, decayingOneFromThisInstant);
                    else
                        ipHistory.AccountFailuresInfrequentPassword.AddInPlace(halfLife, decayingOneFromThisInstant);
                }
            }
            else
            {
                if (IsRepeatFailure)
                {
                    if (IsFrequentlyGuessedPassword)
                        ipHistory.RepeatPasswordFailuresNoTypoFrequentPassword.AddInPlace(halfLife, decayingOneFromThisInstant);
                    else
                        ipHistory.RepeatPasswordFailuresNoTypoInfrequentPassword.AddInPlace(halfLife, decayingOneFromThisInstant);
                }
                else
                {
                    if (IsFrequentlyGuessedPassword)
                        ipHistory.PasswordFailuresNoTypoFrequentPassword.AddInPlace(halfLife, decayingOneFromThisInstant);
                    else
                        ipHistory.PasswordFailuresNoTypoInfrequentPassword.AddInPlace(halfLife, decayingOneFromThisInstant);
                }
            }

        }
 public void SetValue(DecayingDouble source)
 {
     LastUpdatedUtc = source.LastUpdatedUtc;
     ValueAtTimeOfLastUpdate = source.ValueAtTimeOfLastUpdate;
 }
 public void SetValue(DecayingDouble source)
 {
     LastUpdatedUtc          = source.LastUpdatedUtc;
     ValueAtTimeOfLastUpdate = source.ValueAtTimeOfLastUpdate;
 }