/// Performs an in place shuffle on a StringBuilder's array. /// The stack of randomly selected chars builds from the end of the array. /// The top of the stack tracks with i as i gets smaller. /// </summary> /// <param name="builder">string builder that gets shuffled</param> /// <param name="provider">random generator provider</param> private void shuffle(StringBuilder builder, CryptoRandomIndexProvider provider) { char copiedChar; for (int i = builder.Length - 1; i > 1; i--) { // Get a random number in the range of 0 to i. //processRandomIndex(provider, i, getBitsToShift(i + 1)); var randomIndex = provider.getRandomIndex(i - 1); // Copy the char at i making room above the stack for the randomly selected char. copiedChar = builder[i]; // Grab the char at that random position and place it on the stack. builder[i] = builder[randomIndex]; // Place the char that was at i in the position the random char came from. builder[randomIndex] = copiedChar; } }
/// <summary> /// Adds randomly selected chars from a selection pool to the string builder. /// </summary> /// <param name="pool">Selection pool</param> /// <param name="numberOfCharsToAdd">Number of chars to add</param> /// <param name="builder">string builder chars are added to</param> /// <param name="provider">random generator provider</param> /// <prram name="compensate">Flag to check if compensation is necessary</prram> private void addCharsToBuilder( string pool, int numberOfCharsToAdd, StringBuilder builder, CryptoRandomIndexProvider provider, bool compensate) { for (int i = 0; i < numberOfCharsToAdd; i++) { // load randomIndex with a random number. var randomIndex = provider.getRandomIndex(pool.Length - 1); // Discussion: The program starts by selecting one random character from each of the four sub-strings. This // ensures the secret generated will contain at least one character from each of the sub-strings. This, however // introduces a bias. Disproportionately more characters are generated from the shorter sub-strings than the // longer ones. The bias can be significant and increases with shorter pools and shorter generated secrets. The // bias is reduced by ignoring the next reference to each sub-string after the initial selections. The bias // becomes insignificant falling below three sigma when generating secrets of 64 characters or more. // The following block of code implements a trap that bypasses the second instance of a character being selected // from each of the four sub-strings. See discussion above. if (!allPoolsCompensated && compensate) { if (randomIndex <= numberPoolEnd) { if (!numberPoolCompensated) { numberPoolCompensated = true; numberOfPoolsCompensated++; i--; continue; } } else if (randomIndex <= lowerPoolEnd) { if (!lowerPoolCompensated) { lowerPoolCompensated = true; numberOfPoolsCompensated++; i--; continue; } } else if (randomIndex <= upperPoolEnd) { if (!upperPoolCompensated) { upperPoolCompensated = true; numberOfPoolsCompensated++; i--; continue; } } else if (true) // (randomSort <= specialPoolEnd) { if (!specialPoolCompensated) { specialPoolCompensated = true; numberOfPoolsCompensated++; i--; continue; } } } // Add the selected character to the builder. builder.Append(pool[randomIndex]); } }