public void GeneratePassword_NoSpecialChars_Checked()
        {
            _policy = new PasswordPolicy(8, 8) { SpecialCharacters = null };

             string password = PasswordGenerator.Generate(_policy);

             char[] special = new PasswordPolicy(1, 2).SpecialCharacters.ToCharArray();

             foreach(char ch in password)
             {
            Assert.False(special.Contains(ch));
             }
        }
        public void GeneratePassword_EdgeCases_Passes()
        {
            Assert.Throws<ArgumentException>(() =>
             {
            _policy = new PasswordPolicy(0, 0);
            PasswordGenerator.Generate(_policy);
             });

             Assert.Throws<ArgumentException>(() =>
             {
            _policy = new PasswordPolicy(-1, 2);
            PasswordGenerator.Generate(_policy);
             });

             Assert.Throws<ArgumentException>(() =>
             {
            _policy = new PasswordPolicy(3, 2);
            PasswordGenerator.Generate(_policy);
             });
        }
        /// <summary>
        /// Generates a random password.
        /// </summary>
        /// <param name="policy">
        /// Password generation policy
        /// </param>
        /// <returns>
        /// Randomly generated password.
        /// </returns>
        /// <remarks>
        /// The length of the generated password will be determined at
        /// random and it will fall with the range determined by the
        /// function parameters.
        /// </remarks>
        public static string Generate(PasswordPolicy policy)
        {
            if (policy == null) throw new ArgumentNullException(nameof(policy));

             // Create a local array containing supported password characters
             // grouped by types. You can remove character groups from this
             // array, but doing so will weaken the password strength.
             char[][] charGroups = policy.ToCharGroups();

             // Use this array to track the number of unused characters in each
             // character group.
             int[] charsLeftInGroup = new int[charGroups.Length];

             // Initially, all characters in each group are not used.
             for (int i = 0; i < charsLeftInGroup.Length; i++)
            charsLeftInGroup[i] = charGroups[i].Length;

             // Use this array to track (iterate through) unused character groups.
             int[] leftGroupsOrder = new int[charGroups.Length];

             // Initially, all character groups are not used.
             for (int i = 0; i < leftGroupsOrder.Length; i++)
            leftGroupsOrder[i] = i;

             // This array will hold password characters.
             char[] password = null;

             // Allocate appropriate memory for the password.
             if (policy.MinLength < policy.MaxLength)
            password = new char[Generator.GetRandomInt(policy.MinLength, policy.MaxLength + 1)];
             else
            password = new char[policy.MinLength];

             // Index of the next character to be added to password.
             int nextCharIdx;

             // Index of the next character group to be processed.
             int nextGroupIdx;

             // Index which will be used to track not processed character groups.
             int nextLeftGroupsOrderIdx;

             // Index of the last non-processed character in a group.
             int lastCharIdx;

             // Index of the last non-processed group.
             int lastLeftGroupsOrderIdx = leftGroupsOrder.Length - 1;

             // Generate password characters one at a time.
             for (int i = 0; i < password.Length; i++)
             {
            // If only one character group remained unprocessed, process it;
            // otherwise, pick a random character group from the unprocessed
            // group list. To allow a special character to appear in the
            // first position, increment the second parameter of the Next
            // function call by one, i.e. lastLeftGroupsOrderIdx + 1.
            if (lastLeftGroupsOrderIdx == 0)
               nextLeftGroupsOrderIdx = 0;
            else
               nextLeftGroupsOrderIdx = Generator.GetRandomInt(0,
                                                    lastLeftGroupsOrderIdx);

            // Get the actual index of the character group, from which we will
            // pick the next character.
            nextGroupIdx = leftGroupsOrder[nextLeftGroupsOrderIdx];

            // Get the index of the last unprocessed characters in this group.
            lastCharIdx = charsLeftInGroup[nextGroupIdx] - 1;

            // If only one unprocessed character is left, pick it; otherwise,
            // get a random character from the unused character list.
            if (lastCharIdx == 0)
               nextCharIdx = 0;
            else
               nextCharIdx = Generator.GetRandomInt(0, lastCharIdx + 1);

            // Add this character to the password.
            password[i] = charGroups[nextGroupIdx][nextCharIdx];

            // If we processed the last character in this group, start over.
            if (lastCharIdx == 0)
               charsLeftInGroup[nextGroupIdx] =
                                         charGroups[nextGroupIdx].Length;
            // There are more unprocessed characters left.
            else
            {
               // Swap processed character with the last unprocessed character
               // so that we don't pick it until we process all characters in
               // this group.
               if (lastCharIdx != nextCharIdx)
               {
                  char temp = charGroups[nextGroupIdx][lastCharIdx];
                  charGroups[nextGroupIdx][lastCharIdx] =
                              charGroups[nextGroupIdx][nextCharIdx];
                  charGroups[nextGroupIdx][nextCharIdx] = temp;
               }
               // Decrement the number of unprocessed characters in
               // this group.
               charsLeftInGroup[nextGroupIdx]--;
            }

            // If we processed the last group, start all over.
            if (lastLeftGroupsOrderIdx == 0)
               lastLeftGroupsOrderIdx = leftGroupsOrder.Length - 1;
            // There are more unprocessed groups left.
            else
            {
               // Swap processed group with the last unprocessed group
               // so that we don't pick it until we process all groups.
               if (lastLeftGroupsOrderIdx != nextLeftGroupsOrderIdx)
               {
                  int temp = leftGroupsOrder[lastLeftGroupsOrderIdx];
                  leftGroupsOrder[lastLeftGroupsOrderIdx] =
                              leftGroupsOrder[nextLeftGroupsOrderIdx];
                  leftGroupsOrder[nextLeftGroupsOrderIdx] = temp;
               }
               // Decrement the number of unprocessed groups.
               lastLeftGroupsOrderIdx--;
            }
             }

             // Convert password characters into a string and return the result.
             return new string(password);
        }
 public void GeneratePassword_MaxLength_OutOfMemory()
 {
     var policy = new PasswordPolicy(int.MaxValue, int.MaxValue);
      Assert.Throws<OutOfMemoryException>(() => PasswordGenerator.Generate(policy));
 }
 public PasswordGeneratorTest()
 {
     _policy = new PasswordPolicy(8, 12);
 }