/// <summary> /// Get a benign login attempt to simulate /// </summary> /// <returns></returns> public SimulatedLoginAttempt BenignLoginAttempt(DateTime eventTimeUtc) { // If there is a benign login attempt already scheduled to occur by now, // send it instaed lock (ScheduledBenignAttempts) { if (ScheduledBenignAttempts.Count > 0 && ScheduledBenignAttempts.First().TimeOfAttemptUtc < eventTimeUtc) { SimulatedLoginAttempt result = ScheduledBenignAttempts.First(); ScheduledBenignAttempts.Remove(result); return(result); } } string mistake = ""; //1. Pick a user at random SimulatedUserAccount account = _simAccounts.BenignAccountSelector.GetItemByWeightedRandom(); //2. Deal with cookies string cookie; // Add a new cookie if there are no cookies, or with if we haven't reached the max number of cookies and lose a roll of the dice if (account.Cookies.Count == 0 || (account.Cookies.Count < _experimentalConfiguration.MaxCookiesPerUserAccount && StrongRandomNumberGenerator.GetFraction() > _experimentalConfiguration.ChanceOfCoookieReUse)) { // We'll use the decimal represenation of a 64-bit unsigned integer as our cookie cookie = StrongRandomNumberGenerator.Get64Bits().ToString(); account.Cookies.Add(cookie); } else { // Use one of the user's existing cookies selected at random cookie = account.Cookies.ToArray()[(int)StrongRandomNumberGenerator.Get32Bits(account.Cookies.Count)]; } //Console.WriteLine("The user currently has " + account.Cookies.Count + " cookies. Using: " + cookie); //3. Choose an IP address for the login // 1/3 of times login with the primary IP address, otherwise, choose an IP randomly from the benign IP pool IPAddress clientIp; if (account.ClientAddresses.Count == 0 || (account.ClientAddresses.Count < _experimentalConfiguration.MaxIpPerUserAccount && StrongRandomNumberGenerator.GetFraction() < _experimentalConfiguration.ChanceOfIpReUse)) { // Use a new IP for the user account.ClientAddresses.Add(clientIp = _ipPool.GetNewRandomBenignIp()); } else { // Use one of the user's existing IP Addresses selected at random clientIp = account.ClientAddresses.ToArray()[(int)StrongRandomNumberGenerator.Get32Bits(account.ClientAddresses.Count)]; } string password = account.Password; // // Add benign failures // An automated client begins a string of login attempts using an old (stale) password if (StrongRandomNumberGenerator.GetFraction() < _experimentalConfiguration.ChanceOfLongRepeatOfStalePassword) { // To cause this client to be out of date, we'll change the password here. string newPassword = _simPasswords.GetPasswordFromWeightedDistribution(); _userAccountController.SetPassword(account, newPassword, account.Password); mistake += "StalePassword"; // Schedule all the future failed attempts a fixed distance aparat lock (ScheduledBenignAttempts) { double additionalMistakes = 0; DateTime currentTimeUtc = eventTimeUtc; for (additionalMistakes = 1; additionalMistakes < _experimentalConfiguration.LengthOfLongRepeatOfOldPassword; additionalMistakes++) { currentTimeUtc = currentTimeUtc.AddSeconds(_experimentalConfiguration.MinutesBetweenLongRepeatOfOldPassword); ScheduledBenignAttempts.Add(new SimulatedLoginAttempt( account, password, false, false, clientIp, cookie, mistake, currentTimeUtc)); } for (uint correctLogins = 1; correctLogins < _experimentalConfiguration.LengthOfLongRepeatOfOldPassword; correctLogins++) { currentTimeUtc = currentTimeUtc.AddSeconds(_experimentalConfiguration.MinutesBetweenLongRepeatOfOldPassword); ScheduledBenignAttempts.Add(new SimulatedLoginAttempt( account, newPassword, false, false, clientIp, cookie, mistake, currentTimeUtc)); } } } // The benign user may mistype her password causing a typo if (StrongRandomNumberGenerator.GetFraction() < _experimentalConfiguration.ChanceOfBenignPasswordTypo) { mistake += "Typo"; // Typos tend to come in clusters, and are hopefully followed by a correct login // Add additional typos to the schedule of future benign attempts and then a submission of the correct password lock (ScheduledBenignAttempts) { double additionalMistakes = 0; while (StrongRandomNumberGenerator.GetFraction() < _experimentalConfiguration.ChanceOfRepeatTypo) { ScheduledBenignAttempts.Add(new SimulatedLoginAttempt( account, AddTypoToPassword(password), false, false, clientIp, cookie, mistake, eventTimeUtc.AddSeconds(_experimentalConfiguration.DelayBetweenRepeatBenignErrorsInSeconds * ++additionalMistakes))); } // Add a correct login after the string of typos ScheduledBenignAttempts.Add(new SimulatedLoginAttempt( account, password, false, false, clientIp, cookie, "", eventTimeUtc.AddSeconds( _experimentalConfiguration.DelayBetweenRepeatBenignErrorsInSeconds * (1 + additionalMistakes)))); } // Put the typo into the password for the first typo failure, to be returned by this function. password = AddTypoToPassword(password); } // The benign user may mistakenly use a password for another of her accounts, which we draw from same distribution // we used to generate user account passwords if (StrongRandomNumberGenerator.GetFraction() < _experimentalConfiguration.ChanceOfAccidentallyUsingAnotherAccountPassword) { mistake += "WrongPassword"; // Choices of the wrong account password may come in clusters, and are hopefully followed by a correct login // Add additional typos to the schedule of future benign attempts and then a submission of the correct password lock (ScheduledBenignAttempts) { double additionalMistakes = 0; while (StrongRandomNumberGenerator.GetFraction() < _experimentalConfiguration.ChanceOfRepeatUseOfPasswordFromAnotherAccount) { ScheduledBenignAttempts.Add(new SimulatedLoginAttempt( account, _simPasswords.GetPasswordFromWeightedDistribution(), false, false, clientIp, cookie, mistake, eventTimeUtc.AddSeconds(_experimentalConfiguration.DelayBetweenRepeatBenignErrorsInSeconds * ++additionalMistakes))); } // Add a correct login after mistakes ScheduledBenignAttempts.Add(new SimulatedLoginAttempt( account, password, false, false, clientIp, cookie, "", eventTimeUtc.AddSeconds( _experimentalConfiguration.DelayBetweenRepeatBenignErrorsInSeconds * (additionalMistakes + 1)))); } // Make the current request have the wrong password password = _simPasswords.GetPasswordFromWeightedDistribution(); } // The benign user may mistype her account name, and land on someone else's account name if (StrongRandomNumberGenerator.GetFraction() < _experimentalConfiguration.ChanceOfBenignAccountNameTypoResultingInAValidUserName) { mistake += "WrongAccountName"; // Choices of the wrong account password may come in clusters, and are hopefully followed by a correct login // Add additional typos to the schedule of future benign attempts and then a submission of the correct password lock (ScheduledBenignAttempts) { double additionalMistakes = 0; while (StrongRandomNumberGenerator.GetFraction() < _experimentalConfiguration.ChanceOfRepeatWrongAccountName) { ScheduledBenignAttempts.Add(new SimulatedLoginAttempt( _simAccounts.GetBenignAccountAtRandomUniform(), password, false, false, clientIp, cookie, mistake, eventTimeUtc.AddSeconds( _experimentalConfiguration.DelayBetweenRepeatBenignErrorsInSeconds * ++additionalMistakes))); } // Add a correct login after mistakes ScheduledBenignAttempts.Add(new SimulatedLoginAttempt( account, password, false, false, clientIp, cookie, "", eventTimeUtc.AddSeconds( _experimentalConfiguration.DelayBetweenRepeatBenignErrorsInSeconds * (additionalMistakes + 1)))); // Make the current request have the wrong account name account = _simAccounts.GetBenignAccountAtRandomUniform(); } } return(new SimulatedLoginAttempt(account, password, false, false, clientIp, cookie, mistake, eventTimeUtc)); }