private static void CombinationCount(ReadablePassphraseGenerator generator)
        {
            Console.WriteLine();
            Console.WriteLine("Combination count:");

            foreach (var strength in RandomStrengths)
            {
                var combinations = generator.CalculateCombinations(strength);
                Console.WriteLine("  {0}: {1:E3} ({2:N2} bits)", strength, combinations.ToString(), combinations.EntropyBitsToString());
            }
            Console.WriteLine();

            var predefined = new PhraseStrength[]
            {
                PhraseStrength.Normal, PhraseStrength.NormalAnd, PhraseStrength.NormalSpeech, PhraseStrength.NormalEqual, PhraseStrength.NormalEqualAnd, PhraseStrength.NormalEqualSpeech, PhraseStrength.NormalRequired, PhraseStrength.NormalRequiredAnd, PhraseStrength.NormalRequiredSpeech,
                PhraseStrength.Strong, PhraseStrength.StrongAnd, PhraseStrength.StrongSpeech, PhraseStrength.StrongEqual, PhraseStrength.StrongEqualAnd, PhraseStrength.StrongEqualSpeech, PhraseStrength.StrongRequired, PhraseStrength.StrongRequiredAnd, PhraseStrength.StrongRequiredSpeech,
                PhraseStrength.Insane, PhraseStrength.InsaneAnd, PhraseStrength.InsaneSpeech, PhraseStrength.InsaneEqual, PhraseStrength.InsaneEqualAnd, PhraseStrength.InsaneEqualSpeech, PhraseStrength.InsaneRequired, PhraseStrength.InsaneRequiredAnd, PhraseStrength.InsaneRequiredSpeech,
            };

            for (int i = 0; i < predefined.Length; i++)
            {
                var strength     = predefined[i];
                var combinations = generator.CalculateCombinations(strength);
                Console.WriteLine("  {0}: {1:E3} ({2:N2} bits)", strength, combinations.ToString(), combinations.EntropyBitsToString());
                if ((i + 1) % 9 == 0)
                {
                    Console.WriteLine();
                }
            }
        }
 private static void GenerateSamples(PhraseStrength strength, ReadablePassphraseGenerator generator, int count)
 {
     Console.WriteLine();
     Console.WriteLine("Samples:");
     for (int i = 0; i < count; i++)
     {
         Console.WriteLine(generator.Generate(strength));
     }
 }
 private static void TestGeneration(ReadablePassphraseGenerator generator, PhraseStrength strength, int iterations)
 {
     Console.Write("Testing {0:N0} string phrases of strength {1}...", iterations, strength);
     for (int i = 0; i < iterations; i++)
     {
         generator.Generate(strength);
     }
     Console.WriteLine(" OK.");
 }
 private static void GenerateDelimiterSamples(PhraseStrength strength, ReadablePassphraseGenerator generator, int count, string delimiter)
 {
     Console.WriteLine();
     Console.WriteLine("Delimited Samples ('{0}'):", delimiter);
     for (int i = 0; i < count; i++)
     {
         Console.WriteLine(generator.Generate(strength, wordDelimiter: delimiter));
     }
 }
 private static void GenerateMutatedSamples(PhraseStrength strength, ReadablePassphraseGenerator generator, int count, IEnumerable <IMutator> mutators)
 {
     Console.WriteLine();
     Console.WriteLine("Mutated Samples:");
     for (int i = 0; i < count; i++)
     {
         var passphrase = generator.Generate(strength, mutators: mutators);
         Console.WriteLine(passphrase);
     }
 }
 /// <summary>
 /// Calculates the number of possible combinations of phrases based on the current dictionary and given phrase strength.
 /// </summary>
 public PhraseCombinations CalculateCombinations(PhraseStrength strength)
 {
     if (Clause.RandomMappings.ContainsKey(strength))
     {
         return(this.CalculateCombinations(Clause.RandomMappings[strength]));
     }
     else
     {
         return(this.CalculateCombinations(Clause.CreatePhraseDescription(strength, this.Randomness)));
     }
 }
 /// <summary>
 /// Generates a single phrase based on the given phrase strength in a UTF8 <c>byte[]</c>.
 /// This is slightly slower than <c>Generate()</c> and allows deterministic destruction of the data, but is still unencrypted.
 /// </summary>
 /// <param name="strength">One of the predefined <c>PhraseStrength</c> enumeration members.</param>
 /// <param name="includeSpacesBetweenWords">Include spaces between words (defaults to true).</param>
 public byte[] GenerateAsUtf8Bytes(PhraseStrength strength, bool includeSpacesBetweenWords)
 {
     return(GenerateAsUtf8Bytes(Clause.CreatePhraseDescription(strength, Randomness), includeSpacesBetweenWords));
 }
 /// <summary>
 /// Generates a single phrase based on the given phrase strength in a UTF8 <c>byte[]</c>.
 /// This is slightly slower than <c>Generate()</c> and allows deterministic destruction of the data, but is still unencrypted.
 /// </summary>
 public byte[] GenerateAsUtf8Bytes(PhraseStrength strength)
 {
     return(GenerateAsUtf8Bytes(Clause.CreatePhraseDescription(strength, Randomness), " "));
 }
 /// <summary>
 /// Generates a single phrase based on the given phrase strength in a <c>StringBuilder</c>.
 /// This is the fastest and least secure method.
 /// </summary>
 /// <param name="strength">One of the predefined <c>PhraseStrength</c> enumeration members (default: Random).</param>
 /// <param name="wordDelimiter">The string to place between each word in the passphrase (default: single space).</param>
 /// /// <param name="mutators">Applies one or more mutators to the passphrase after it is generated (default: none).</param>
 public String Generate(PhraseStrength strength = PhraseStrength.Random, string wordDelimiter = " ", IEnumerable <IMutator>?mutators = null)
 {
     return(Generate(Clause.CreatePhraseDescription(strength, Randomness), wordDelimiter, mutators));
 }
 /// <summary>
 /// Generates a single phrase as a <c>SecureString</c> based on the given phrase strength.
 /// This is the slowest and most secure method.
 /// </summary>
 /// <param name="strength">One of the predefined <c>PhraseStrength</c> enumeration members.</param>
 /// <param name="wordDelimiter">The string to place between each word in the passphrase.</param>
 public SecureString GenerateAsSecure(PhraseStrength strength, string wordDelimiter)
 {
     return(GenerateAsSecure(Clause.CreatePhraseDescription(strength, Randomness), wordDelimiter));
 }
 /// <summary>
 /// Generates a single phrase as a <c>SecureString</c> based on the given phrase strength.
 /// This is the slowest and most secure method.
 /// </summary>
 /// <param name="strength">One of the predefined <c>PhraseStrength</c> enumeration members.</param>
 /// <param name="includeSpacesBetweenWords">Include spaces between words (defaults to true).</param>
 public SecureString GenerateAsSecure(PhraseStrength strength, bool includeSpacesBetweenWords)
 {
     return(GenerateAsSecure(Clause.CreatePhraseDescription(strength, Randomness), includeSpacesBetweenWords));
 }
Example #12
0
        private IEnumerable <string> SelectPhrases(IRandomNumberGenerator random, PhraseStrength strength, int phraseCount, bool includeSpaces, int minChars, int maxChars, NumericStyles whenNumeric, int numbersToAdd, AllUppercaseStyles whenUpper, int uppersToAdd)
        {
            if (minChars > maxChars)
            {
                yield break;
            }

            phraseCount = Math.Min(phraseCount, MaxCount);
            if (phraseCount <= 0)
            {
                yield break;
            }

            var sw        = System.Diagnostics.Stopwatch.StartNew();
            var generator = this.GetGenerator(random);
            int attempts  = 0;
            ICollection <IMutator> mutators = null;

            if (whenNumeric != NumericStyles.Never || whenUpper != AllUppercaseStyles.Never)
            {
                mutators = new List <IMutator>();
            }
            if (whenNumeric != NumericStyles.Never)
            {
                mutators.Add(new NumericMutator()
                {
                    When = whenNumeric, NumberOfNumbersToAdd = numbersToAdd
                });
            }
            if (whenUpper == AllUppercaseStyles.Anywhere)
            {
                mutators.Add(new UppercaseMutator()
                {
                    When = UppercaseStyles.Anywhere, NumberOfCharactersToCapitalise = uppersToAdd
                });
            }
            else if (whenUpper == AllUppercaseStyles.StartOfWord)
            {
                mutators.Add(new UppercaseMutator()
                {
                    When = UppercaseStyles.StartOfWord, NumberOfCharactersToCapitalise = uppersToAdd
                });
            }
            else if (whenUpper == AllUppercaseStyles.WholeWord)
            {
                mutators.Add(new UppercaseWordMutator()
                {
                    NumberOfWordsToCapitalise = uppersToAdd
                });
            }
            else if (whenUpper == AllUppercaseStyles.RunOfLetters)
            {
                mutators.Add(new UppercaseRunMutator()
                {
                    NumberOfRuns = uppersToAdd
                });
            }

            for (int c = 0; c < phraseCount; c++)
            {
                string candidate = "";
                do
                {
                    // Generate a phrase.
                    candidate = generator.Generate(strength, " ", mutators);

                    // Finally, remove spaces if required (as the mutators depend on whitespace to do their work).
                    if (!includeSpaces)
                    {
                        candidate = new string(candidate.Where(ch => ch != ' ').ToArray());
                    }
                    attempts++;

                    // Ensure the final phrase is within the min / max chars.
                } while (attempts < MaxAttemptsPerCount && (candidate.Length < minChars || candidate.Length > maxChars));
                if (attempts >= MaxAttemptsPerCount)
                {
                    candidate = "A passphrase could not be found matching your minimum and maximum length requirements";
                }

                // Yield the phrase and reset state.
                yield return(candidate);

                attempts = 0;
            }
            sw.Stop();

            var bytesRequested = (int)((random as Terninger.Random.CypherBasedPrngGenerator)?.BytesRequested).GetValueOrDefault();

            RandomService.LogPasswordStat("ReadablePassphrase", phraseCount, sw.Elapsed, bytesRequested, IPAddressHelpers.GetHostOrCacheIp(Request).AddressFamily, HttpContext.GetApiKeyId());
            if (!IpThrottlerService.HasAnyUsage(IPAddressHelpers.GetHostOrCacheIp(this.HttpContext.Request)))
            {
                RandomService.AddWebRequestEntropy(this.Request);
            }
            IpThrottlerService.IncrementUsage(IPAddressHelpers.GetHostOrCacheIp(this.HttpContext.Request), phraseCount);
        }
        private static void WriteStatisticsFor(ReadablePassphraseGenerator generator, PhraseStrength strength, int count, string filename)
        {
            Console.Write("Writing statistics to '{0}'...", filename);

            var wordHistogram           = new Dictionary <int, int>();
            var charHistogram           = new Dictionary <int, int>();
            var keepassQualityHistogram = new Dictionary <uint, int>();
            var combinations            = generator.CalculateCombinations(strength);

            for (int i = 0; i < count; i++)
            {
                var phrase = generator.Generate(strength);

                if (!charHistogram.ContainsKey(phrase.Length))
                {
                    charHistogram.Add(phrase.Length, 0);
                }
                charHistogram[phrase.Length] = charHistogram[phrase.Length] + 1;

                var wordCount = phrase.Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries).Length;
                if (!wordHistogram.ContainsKey(wordCount))
                {
                    wordHistogram.Add(wordCount, 0);
                }
                wordHistogram[wordCount] = wordHistogram[wordCount] + 1;

                var keePassQualityEst = KeePassLib.Cryptography.QualityEstimation.EstimatePasswordBits(phrase.ToCharArray());
                if (!keepassQualityHistogram.ContainsKey(keePassQualityEst))
                {
                    keepassQualityHistogram.Add(keePassQualityEst, 0);
                }
                keepassQualityHistogram[keePassQualityEst] = keepassQualityHistogram[keePassQualityEst] + 1;
            }

            using (var writer = new System.IO.StreamWriter(filename, false, Encoding.UTF8))
            {
                writer.WriteLine("Word histogram");
                for (int i = wordHistogram.Keys.Min(); i < wordHistogram.Keys.Max() + 1; i++)
                {
                    writer.WriteLine("{0},{1}", i, wordHistogram.ContainsKey(i) ? wordHistogram[i] : 0);
                }
                writer.WriteLine();

                writer.WriteLine("Character histogram");
                for (int i = charHistogram.Keys.Min(); i < charHistogram.Keys.Max() + 1; i++)
                {
                    writer.WriteLine("{0},{1}", i, charHistogram.ContainsKey(i) ? charHistogram[i] : 0);
                }
                writer.WriteLine();

                writer.WriteLine("KeePass Quality Estimate");
                for (uint i = keepassQualityHistogram.Keys.Min(); i < keepassQualityHistogram.Keys.Max() + 1; i++)
                {
                    writer.WriteLine("{0},{1}", i, keepassQualityHistogram.ContainsKey(i) ? keepassQualityHistogram[i] : 0);
                }
                writer.WriteLine();

                writer.WriteLine("Combination counts");
                writer.WriteLine("Min:,{0:E3},{1:N2}", combinations.Shortest, combinations.ShortestAsEntropyBits);
                writer.WriteLine("Max:,{0:E3},{1:N2}", combinations.Longest, combinations.LongestAsEntropyBits);
                writer.WriteLine("Avg:,{0:E3},{1:N2}", combinations.OptionalAverage, combinations.OptionalAverageAsEntropyBits);

                writer.WriteLine();
                writer.WriteLine("Samples:");
                for (int i = 0; i < 20; i++)
                {
                    writer.WriteLine(generator.Generate(strength));
                }
            }

            Console.WriteLine(" Done.");

            bool isFirst = !System.IO.File.Exists(AllStatsCharFilename);

            using (var writer = new System.IO.StreamWriter(AllStatsCharFilename, true, Encoding.UTF8))
            {
                if (isFirst)
                {
                    writer.WriteLine("Strength,Min,Max,Avg,Median,Samples");
                }
                writer.WriteLine("{0},{1},{2},{3},{4},{5}", strength.ToString(), charHistogram.Keys.Min(), charHistogram.Keys.Max(), GetAvg(charHistogram), GetMedian(charHistogram), count);
            }
        }
        private static void BenchmarkUtf8Generation(ReadablePassphraseGenerator generator, PhraseStrength strength, int iterations)
        {
            Console.WriteLine();
            Console.WriteLine("Benchmark of {0:N0} UTF8 phrases of strength {1}...", iterations, strength);
            var sw = System.Diagnostics.Stopwatch.StartNew();

            for (int i = 0; i < iterations; i++)
            {
                generator.GenerateAsUtf8Bytes(strength);
            }
            sw.Stop();
            Console.WriteLine("  in {0:N3}ms", sw.Elapsed.TotalMilliseconds);
        }
        private static void TestTextualParsing(ReadablePassphraseGenerator generator, PhraseStrength strength)
        {
            Console.WriteLine("Testing parsing of textual phrase strength {0}...", strength);
            var description = Clause.CreatePhraseDescription(strength, generator.Randomness);
            var sb          = new StringBuilder();

            foreach (var c in description)
            {
                c.ToStringBuilder(sb);
            }
            var textualDescription = sb.ToString();

            Console.Write("    Generation OK.");
            var customStrength = Clause.CreateCollectionFromTextString(textualDescription);

            Console.Write(" Parsing OK.");
            generator.Generate(customStrength);
            Console.WriteLine(" Passphrase OK.");
        }
 /// <summary>
 /// Generates a single phrase based on the given phrase strength in a UTF8 <c>byte[]</c>.
 /// This is slightly slower than <c>Generate()</c> and allows deterministic destruction of the data, but is still unencrypted.
 /// </summary>
 /// <param name="strength">One of the predefined <c>PhraseStrength</c> enumeration members.</param>
 /// <param name="wordDelimiter">The string to place between each word in the passphrase.</param>
 public byte[] GenerateAsUtf8Bytes(PhraseStrength strength, string wordDelimiter)
 {
     return(GenerateAsUtf8Bytes(Clause.CreatePhraseDescription(strength, Randomness), wordDelimiter));
 }
Example #17
0
        static bool ParseCommandLine(string[] args)
        {
            for (int i = 0; i < args.Length; i++)
            {
                var arg = args[i].ToLower().Trim();
                if (arg.StartsWith("-") || arg.StartsWith("--") || arg.StartsWith("/"))
                {
                    arg = arg.Replace("--", "").Replace("-", "").Replace("/", "");
                }

                if (arg == "c" || arg == "count")
                {
                    if (!Int32.TryParse(args[i + 1].Trim(), out count))
                    {
                        Console.WriteLine("Unable to parse number '{0}' for 'count' option.", args[i + 1]);
                        return(false);
                    }
                    i++;
                }
                else if (arg == "s" || arg == "strength")
                {
                    if (!Enum.GetNames(typeof(PhraseStrength)).Select(x => x.ToLower()).Contains(args[i + 1]))
                    {
                        Console.WriteLine("Unknown 'strength' option '{0}'.", args[i + 1]);
                        return(false);
                    }
                    strength = (PhraseStrength)Enum.Parse(typeof(PhraseStrength), args[i + 1], true);
                    i++;
                }
                else if (arg == "spaces")
                {
                    bool includeSpaces;
                    if (!Boolean.TryParse(args[i + 1], out includeSpaces))
                    {
                        Console.WriteLine("Invalid boolean '{0}' for 'spaces' option.", args[i + 1]);
                        return(false);
                    }
                    else if (includeSpaces)
                    {
                        wordSeparator = " ";
                    }
                    else if (!includeSpaces)
                    {
                        wordSeparator = "";
                    }
                    i++;
                }
                else if (arg == "separator")
                {
                    wordSeparator = args[i + 1];
                    i++;
                }
                else if (arg == "d" || arg == "dict")
                {
                    var customDictionaryPath = args[i + 1];
                    loaderArguments = "file=" + customDictionaryPath;
                    if (!System.IO.File.Exists(customDictionaryPath))
                    {
                        Console.WriteLine("Unable to find file '{0}' for 'dict' option.", customDictionaryPath);
                        return(false);
                    }
                    i++;
                }
                else if (arg == "l" || arg == "loaderdll")
                {
                    loaderDll = args[i + 1];
                    if (!System.IO.File.Exists(loaderDll))
                    {
                        Console.WriteLine("Unable to find file '{0}' for 'loaderdll' option.", loaderDll);
                        return(false);
                    }
                    i++;
                }
                else if (arg == "t" || arg == "loadertype")
                {
                    loaderType = args[i + 1];
                    i++;
                }
                else if (arg == "a" || arg == "loaderargs")
                {
                    loaderArguments = args[i + 1];
                    i++;
                }
                else if (arg == "n" || arg == "nongrammar")
                {
                    if (!Int32.TryParse(args[i + 1].Trim(), out anyLength))
                    {
                        Console.WriteLine("Unable to parse number '{0}' for 'nongrammar' option.", args[i + 1]);
                        return(false);
                    }
                    i++;
                }
                else if (arg == "p" || arg == "phrase")
                {
                    customPhrasePath = args[i + 1];
                    if (!System.IO.File.Exists(customPhrasePath))
                    {
                        Console.WriteLine("Unable to find file '{0}' for 'phrase' option.", args[i + 1]);
                        return(false);
                    }
                    try
                    {
                        phraseDescription = ReadablePassphrase.PhraseDescription.Clause.CreateCollectionFromTextString(System.IO.File.ReadAllText(customPhrasePath));
                    }
                    catch (PhraseDescriptionParseException ex)
                    {
                        Console.WriteLine("Unable to parse file '{0}' for 'phrase' option:", args[i + 1]);
                        Console.WriteLine("  {0}", ex.Message);
                        if (ex.InnerException != null)
                        {
                            Console.WriteLine("  {0}: {1}", ex.InnerException.GetType().Name, ex.InnerException.Message);
                        }
                    }
                    i++;
                }
                else if (arg == "min")
                {
                    if (!Int32.TryParse(args[i + 1].Trim(), out minLength))
                    {
                        Console.WriteLine("Unable to parse number '{0}' for 'min' option.", args[i + 1]);
                        return(false);
                    }
                    i++;
                }
                else if (arg == "max")
                {
                    if (!Int32.TryParse(args[i + 1].Trim(), out maxLength))
                    {
                        Console.WriteLine("Unable to parse number '{0}' for 'max' option.", args[i + 1]);
                        return(false);
                    }
                    i++;
                }
                else if (arg == "m" || arg == "stdMutators")
                {
                    applyStandardMutators = true;
                }
                else if (arg == "m2" || arg == "altMutators")
                {
                    applyAlternativeMutators = true;
                }
                else if (arg == "mutnumeric")
                {
                    if (!Enum.GetNames(typeof(NumericStyles)).Select(x => x.ToLower()).Contains(args[i + 1]))
                    {
                        Console.WriteLine("Unknown 'mutNumeric' option '{0}'.", args[i + 1]);
                        return(false);
                    }
                    numericStyle = (NumericStyles)Enum.Parse(typeof(NumericStyles), args[i + 1], true);
                    i++;
                }
                else if (arg == "mutnumericcount")
                {
                    if (!Int32.TryParse(args[i + 1].Trim(), out numericCount))
                    {
                        Console.WriteLine("Unable to parse number '{0}' for 'mutNumericCount' option.", args[i + 1]);
                        return(false);
                    }
                    i++;
                }
                else if (arg == "mutupper")
                {
                    if (!Enum.GetNames(typeof(AllUppercaseStyles)).Select(x => x.ToLower()).Contains(args[i + 1]))
                    {
                        Console.WriteLine("Unknown 'mutUpper' option '{0}'.", args[i + 1]);
                        return(false);
                    }
                    upperStyle = (AllUppercaseStyles)Enum.Parse(typeof(AllUppercaseStyles), args[i + 1], true);
                    i++;
                }
                else if (arg == "mutuppercount")
                {
                    if (!Int32.TryParse(args[i + 1].Trim(), out upperCount))
                    {
                        Console.WriteLine("Unable to parse number '{0}' for 'mutUpperCount' option.", args[i + 1]);
                        return(false);
                    }
                    i++;
                }
                else if (arg == "mutconstant")
                {
                    if (!Enum.GetNames(typeof(ConstantStyles)).Select(x => x.ToLower()).Contains(args[i + 1]))
                    {
                        Console.WriteLine("Unknown 'mutConstant' option '{0}'.", args[i + 1]);
                        return(false);
                    }
                    constantStyle = (ConstantStyles)Enum.Parse(typeof(ConstantStyles), args[i + 1], true);
                    i++;
                }
                else if (arg == "mutconstantvalue")
                {
                    constantValue = args[i + 1];
                    i++;
                }
                else if (arg == "q" || arg == "quiet")
                {
                    quiet = true;
                }
                else if (arg == "h" || arg == "help")
                {
                    PrintUsage();
                    Environment.Exit(0);
                }
                else
                {
                    Console.WriteLine("Unknown argument '{0}'.", arg);
                    return(false);
                }
            }

            return(true);
        }
 /// <summary>
 /// Generates a single phrase as a <c>SecureString</c> based on the given phrase strength.
 /// This is the slowest and most secure method.
 /// </summary>
 public SecureString GenerateAsSecure(PhraseStrength strength)
 {
     return(GenerateAsSecure(Clause.CreatePhraseDescription(strength, Randomness), " "));
 }
Example #19
0
        private IEnumerable <string> SelectPhrases(WordDictionary dictionary, IRandomNumberGenerator random, PhraseStrength strength, int phraseCount, bool includeSpaces, int minChars, int maxChars, NumericStyles whenNumeric, int numbersToAdd, AllUppercaseStyles whenUpper, int uppersToAdd)
        {
            if (minChars > maxChars)
            {
                yield break;
            }

            phraseCount = Math.Min(phraseCount, MaxCount);
            if (phraseCount <= 0)
            {
                yield break;
            }

            var sw        = System.Diagnostics.Stopwatch.StartNew();
            var generator = this.GetGenerator(dictionary, random);
            int attempts  = 0;

            // Setup mutators.
            ICollection <IMutator> mutators = null;

            if (whenNumeric != NumericStyles.Never || whenUpper != AllUppercaseStyles.Never)
            {
                mutators = new List <IMutator>();
            }
            if (whenNumeric != NumericStyles.Never)
            {
                mutators.Add(new NumericMutator()
                {
                    When = whenNumeric, NumberOfNumbersToAdd = numbersToAdd
                });
            }
            if (whenUpper == AllUppercaseStyles.Anywhere)
            {
                mutators.Add(new UppercaseMutator()
                {
                    When = UppercaseStyles.Anywhere, NumberOfCharactersToCapitalise = uppersToAdd
                });
            }
            else if (whenUpper == AllUppercaseStyles.StartOfWord)
            {
                mutators.Add(new UppercaseMutator()
                {
                    When = UppercaseStyles.StartOfWord, NumberOfCharactersToCapitalise = uppersToAdd
                });
            }
            else if (whenUpper == AllUppercaseStyles.WholeWord)
            {
                mutators.Add(new UppercaseWordMutator()
                {
                    NumberOfWordsToCapitalise = uppersToAdd
                });
            }
            else if (whenUpper == AllUppercaseStyles.RunOfLetters)
            {
                mutators.Add(new UppercaseRunMutator()
                {
                    NumberOfRuns = uppersToAdd
                });
            }

            for (int c = 0; c < phraseCount; c++)
            {
                string candidate = "";
                do
                {
                    // Generate a phrase.
                    candidate = generator.Generate(strength, " ", mutators);

                    // Finally, remove spaces if required (as the mutators depend on whitespace to do their work).
                    if (!includeSpaces)
                    {
                        candidate = new string(candidate.Where(ch => ch != ' ').ToArray());
                    }
                    attempts++;

                    // Ensure the final phrase is within the min / max chars.
                } while (attempts < MaxAttemptsPerCount && (candidate.Length < minChars || candidate.Length > maxChars));
                if (attempts >= MaxAttemptsPerCount)
                {
                    candidate = "A passphrase could not be found matching your minimum and maximum length requirements";
                }

                // Yield the phrase and reset state.
                yield return(candidate);

                attempts = 0;
            }
            sw.Stop();

            PostSelectionAction("ReadablePassphrase", phraseCount, sw.Elapsed, random);
        }
 private static void GenerateSamples(PhraseStrength strength, ReadablePassphraseGenerator generator)
 {
     GenerateSamples(strength, generator, 20);
 }