3
\$\begingroup\$

I'm new to C# and I've just created a passwords generator. I'm not sure if my code is following best practices, etc.

The schematic of the program is simple. How many passwords of x length can be created with y characters, that's it. An example, following [a-z0-9], 2 to the power of 36 = 68719476736 generated passwords.

I would add a new pattern with special characters but I was not able to do this, is it possible? I tried to add and the IDE reported error, one issue could be because I used question mark as special character.

Is there any mistakes in the code?

Does my program make sense?

Is my code clean and readable?

What do you suggest for the future, what ideas do you have to improve the code?

Main.cs

using System;

namespace PasswordsGenerator
{
    class Password
    {
        static void Main(string[] args)
        {
            string firstPattern = "[a-z0-9]";
            string secondPattern = "[a-zA-Z0-9]";

            Console.WriteLine("Passwords must be up to 7 in length.");
            Console.WriteLine($"List of patterns\n 1 - {firstPattern}\n 2 - {secondPattern}");

            Console.Write("Enter password length: ");
            int passwordLength = Convert.ToInt32(Console.ReadLine());
            Console.Write("Enter pattern: ");
            int pattern = Convert.ToInt32(Console.ReadLine());

            UserInput randomPassword = new UserInput(passwordLength, pattern);
            randomPassword.DisplayPasswords();
        }
    }
}

UserInput.cs

using System;
using System.IO;

namespace PasswordsGenerator
{
    class UserInput : Password
    {
        public int passwordLength { get; set; }
        public int userPattern { get; set; }

        public UserInput(int PasswordLength,int Pattern)
        {
            passwordLength = PasswordLength;
            userPattern = Pattern;
        }

        public void DisplayPasswords()
        {
            string charactersFirstPattern = "abcdefghijklmnopqrstuvwxyz0123456789";
            string charactersSecondPattern = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
            
            try
            {
                //Block for password length 1
                if (passwordLength == 1 && userPattern == 1)
                {
                    for (int i = 0; i < charactersFirstPattern.Length; i++)
                    {
                        string char1 = Convert.ToString(charactersFirstPattern[i]);

                        string result = char1;

                        string path = @"C:\Users\Username\Desktop\passwords.txt";

                        using (StreamWriter sw = File.AppendText(path))
                        {
                            sw.WriteLine("New password: " + result);
                        }

                        Console.WriteLine("New password: " + result);
                    }
                }
                else if (passwordLength == 1 && userPattern == 2)
                {
                    for (int i = 0; i < charactersSecondPattern.Length; i++)
                    {
                        string char1 = Convert.ToString(charactersSecondPattern[i]);

                        string result = char1;

                        string path = @"C:\Users\Username\Desktop\passwords.txt";

                        using (StreamWriter sw = File.AppendText(path))
                        {
                            sw.WriteLine("New password: " + result);
                        }

                        Console.WriteLine("New password: " + result);
                    }
                }

                //Block password length 2
                else if (passwordLength == 2 && userPattern == 1)
                {
                    for (int i = 0; i < charactersFirstPattern.Length; i++)
                    {
                        string char1 = Convert.ToString(charactersFirstPattern[i]);
                        for (int j = 0; j < charactersFirstPattern.Length; j++)
                        {
                            string char2 = Convert.ToString(charactersFirstPattern[j]);

                            string result = char1 + char2;

                            string path = @"C:\Users\Username\Desktop\passwords.txt";

                            using (StreamWriter sw = File.AppendText(path))
                            {
                                sw.WriteLine("New password: " + result);
                            }

                            Console.WriteLine("New password: " + result);
                        }
                    }
                }
                else if (passwordLength == 2 && userPattern == 2)
                {
                    for (int i = 0; i < charactersSecondPattern.Length; i++)
                    {
                        string char1 = Convert.ToString(charactersSecondPattern[i]);
                        for (int j = 0; j < charactersSecondPattern.Length; j++)
                        {
                            string char2 = Convert.ToString(charactersSecondPattern[j]);

                            string result = char1 + char2;

                            string path = @"C:\Users\Username\Desktop\passwords.txt";

                            using (StreamWriter sw = File.AppendText(path))
                            {
                                sw.WriteLine("New password: " + result);
                            }

                            Console.WriteLine("New password: " + result);
                        }
                    }
                }

                //Block for password length 3
                else if (passwordLength == 3 && userPattern == 1)
                {
                    for (int i = 0; i < charactersFirstPattern.Length; i++)
                    {
                        string char1 = Convert.ToString(charactersFirstPattern[i]);
                        for (int j = 0; j < charactersFirstPattern.Length; j++)
                        {
                            string char2 = Convert.ToString(charactersFirstPattern[j]);
                            for (int k = 0; k < charactersFirstPattern.Length; k++)
                            {
                                string char3 = Convert.ToString(charactersFirstPattern[k]);

                                string result = char1 + char2 + char3;

                                string path = @"C:\Users\Username\Desktop\passwords.txt";

                                using (StreamWriter sw = File.AppendText(path))
                                {
                                    sw.WriteLine("New password: " + result);
                                }

                                Console.WriteLine("New password: " + result);
                            }
                        }
                    }
                }
                else if (passwordLength == 3 && userPattern == 2)
                {
                    for (int i = 0; i < charactersSecondPattern.Length; i++)
                    {
                        string char1 = Convert.ToString(charactersSecondPattern[i]);
                        for (int j = 0; j < charactersSecondPattern.Length; j++)
                        {
                            string char2 = Convert.ToString(charactersSecondPattern[j]);
                            for (int k = 0; k < charactersSecondPattern.Length; k++)
                            {
                                string char3 = Convert.ToString(charactersSecondPattern[k]);

                                string result = char1 + char2 + char3;

                                string path = @"C:\Users\Username\Desktop\passwords.txt";

                                using (StreamWriter sw = File.AppendText(path))
                                {
                                    sw.WriteLine("New password: " + result);
                                }

                                Console.WriteLine("New password: " + result);
                            }
                        }
                    }
                }

                //Block for password length 4
                else if (passwordLength == 4 && userPattern == 1)
                {
                    for (int i = 0; i < charactersFirstPattern.Length; i++)
                    {
                        string char1 = Convert.ToString(charactersFirstPattern[i]);
                        for (int j = 0; j < charactersFirstPattern.Length; j++)
                        {
                            string char2 = Convert.ToString(charactersFirstPattern[j]);
                            for (int k = 0; k < charactersFirstPattern.Length; k++)
                            {
                                string char3 = Convert.ToString(charactersFirstPattern[k]);
                                for (int l = 0; l < charactersFirstPattern.Length; l++)
                                {
                                    string char4 = Convert.ToString(charactersFirstPattern[l]);

                                    string result = char1 + char2 + char3 + char4;

                                    string path = @"C:\Users\Username\Desktop\passwords.txt";

                                    using (StreamWriter sw = File.AppendText(path))
                                    {
                                        sw.WriteLine("New password: " + result);
                                    }

                                    Console.WriteLine("New password: " + result);
                                }
                            }
                        }
                    }
                }
                else if (passwordLength == 4 && userPattern == 2)
                {
                    for (int i = 0; i < charactersSecondPattern.Length; i++)
                    {
                        string char1 = Convert.ToString(charactersSecondPattern[i]);
                        for (int j = 0; j < charactersSecondPattern.Length; j++)
                        {
                            string char2 = Convert.ToString(charactersSecondPattern[j]);
                            for (int k = 0; k < charactersSecondPattern.Length; k++)
                            {
                                string char3 = Convert.ToString(charactersSecondPattern[k]);
                                for (int l = 0; l < charactersSecondPattern.Length; l++)
                                {
                                    string char4 = Convert.ToString(charactersSecondPattern[l]);

                                    string result = char1 + char2 + char3 + char4;

                                    string path = @"C:\Users\Username\Desktop\passwords.txt";

                                    using (StreamWriter sw = File.AppendText(path))
                                    {
                                        sw.WriteLine("New password: " + result);
                                    }

                                    Console.WriteLine("New password: " + result);
                                }
                            }
                        }
                    }
                }

                //Block for password length 5
                else if (passwordLength == 5 && userPattern == 1)
                {
                    for (int i = 0; i < charactersFirstPattern.Length; i++)
                    {
                        string char1 = Convert.ToString(charactersFirstPattern[i]);
                        for (int j = 0; j < charactersFirstPattern.Length; j++)
                        {
                            string char2 = Convert.ToString(charactersFirstPattern[j]);
                            for (int k = 0; k < charactersFirstPattern.Length; k++)
                            {
                                string char3 = Convert.ToString(charactersFirstPattern[k]);
                                for (int l = 0; l < charactersFirstPattern.Length; l++)
                                {
                                    string char4 = Convert.ToString(charactersFirstPattern[l]);
                                    for (int m = 0; m < charactersFirstPattern.Length; m++)
                                    {
                                        string char5 = Convert.ToString(charactersFirstPattern[m]);

                                        string result = char1 + char2 + char3 + char4 + char5;

                                        string path = @"C:\Users\Username\Desktop\passwords.txt";

                                        using (StreamWriter sw = File.AppendText(path))
                                        {
                                            sw.WriteLine("New password: " + result);
                                        }

                                        Console.WriteLine("New password: " + result);
                                    }
                                }
                            }
                        }
                    }
                }
                else if (passwordLength == 5 && userPattern == 2)
                {
                    for (int i = 0; i < charactersSecondPattern.Length; i++)
                    {
                        string char1 = Convert.ToString(charactersSecondPattern[i]);
                        for (int j = 0; j < charactersSecondPattern.Length; j++)
                        {
                            string char2 = Convert.ToString(charactersSecondPattern[j]);
                            for (int k = 0; k < charactersSecondPattern.Length; k++)
                            {
                                string char3 = Convert.ToString(charactersSecondPattern[k]);
                                for (int l = 0; l < charactersSecondPattern.Length; l++)
                                {
                                    string char4 = Convert.ToString(charactersSecondPattern[l]);
                                    for (int m = 0; m < charactersSecondPattern.Length; m++)
                                    {
                                        string char5 = Convert.ToString(charactersSecondPattern[m]);

                                        string result = char1 + char2 + char3 + char4 + char5;

                                        string path = @"C:\Users\Username\Desktop\passwords.txt";

                                        using (StreamWriter sw = File.AppendText(path))
                                        {
                                            sw.WriteLine("New password: " + result);
                                        }

                                        Console.WriteLine("New password: " + result);
                                    }
                                }
                            }
                        }
                    }
                }

                //Block for password length 6
                else if (passwordLength == 6 && userPattern == 1)
                {
                    for (int i = 0; i < charactersFirstPattern.Length; i++)
                    {
                        string char1 = Convert.ToString(charactersFirstPattern[i]);
                        for (int j = 0; j < charactersFirstPattern.Length; j++)
                        {
                            string char2 = Convert.ToString(charactersFirstPattern[j]);
                            for (int k = 0; k < charactersFirstPattern.Length; k++)
                            {
                                string char3 = Convert.ToString(charactersFirstPattern[k]);
                                for (int l = 0; l < charactersFirstPattern.Length; l++)
                                {
                                    string char4 = Convert.ToString(charactersFirstPattern[l]);
                                    for (int m = 0; m < charactersFirstPattern.Length; m++)
                                    {
                                        string char5 = Convert.ToString(charactersFirstPattern[m]);
                                        for (int n = 0; n < charactersFirstPattern.Length; n++)
                                        {
                                            string char6 = Convert.ToString(charactersFirstPattern[n]);

                                            string result = char1 + char2 + char3 + char4 + char5 + char6;

                                            string path = @"C:\Users\Username\Desktop\passwords.txt";

                                            using (StreamWriter sw = File.AppendText(path))
                                            {
                                                sw.WriteLine("New password: " + result);
                                            }

                                            Console.WriteLine("New password: " + result);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                else if (passwordLength == 6 && userPattern == 2)
                {
                    for (int i = 0; i < charactersSecondPattern.Length; i++)
                    {
                        string char1 = Convert.ToString(charactersSecondPattern[i]);
                        for (int j = 0; j < charactersSecondPattern.Length; j++)
                        {
                            string char2 = Convert.ToString(charactersSecondPattern[j]);
                            for (int k = 0; k < charactersSecondPattern.Length; k++)
                            {
                                string char3 = Convert.ToString(charactersSecondPattern[k]);
                                for (int l = 0; l < charactersSecondPattern.Length; l++)
                                {
                                    string char4 = Convert.ToString(charactersSecondPattern[l]);
                                    for (int m = 0; m < charactersSecondPattern.Length; m++)
                                    {
                                        string char5 = Convert.ToString(charactersSecondPattern[m]);
                                        for (int n = 0; n < charactersSecondPattern.Length; n++)
                                        {
                                            string char6 = Convert.ToString(charactersSecondPattern[n]);

                                            string result = char1 + char2 + char3 + char4 + char5 + char6;

                                            string path = @"C:\Users\Username\Desktop\passwords.txt";

                                            using (StreamWriter sw = File.AppendText(path))
                                            {
                                                sw.WriteLine("New password: " + result);
                                            }

                                            Console.WriteLine("New password: " + result);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }

                //Block for password length 7
                else if (passwordLength == 7 && userPattern == 1)
                {
                    for (int i = 0; i < charactersFirstPattern.Length; i++)
                    {
                        string char1 = Convert.ToString(charactersFirstPattern[i]);
                        for (int j = 0; j < charactersFirstPattern.Length; j++)
                        {
                            string char2 = Convert.ToString(charactersFirstPattern[j]);
                            for (int k = 0; k < charactersFirstPattern.Length; k++)
                            {
                                string char3 = Convert.ToString(charactersFirstPattern[k]);
                                for (int l = 0; l < charactersFirstPattern.Length; l++)
                                {
                                    string char4 = Convert.ToString(charactersFirstPattern[l]);
                                    for (int m = 0; m < charactersFirstPattern.Length; m++)
                                    {
                                        string char5 = Convert.ToString(charactersFirstPattern[m]);
                                        for (int n = 0; n < charactersFirstPattern.Length; n++)
                                        {
                                            string char6 = Convert.ToString(charactersFirstPattern[n]);
                                            for (int o = 0; o < charactersFirstPattern.Length; o++)
                                            {
                                                string char7 = Convert.ToString(charactersFirstPattern[o]);

                                                string result = char1 + char2 + char3 + char4 + char5 + char6 + char7;

                                                string path = @"C:\Users\Username\Desktop\passwords.txt";

                                                using (StreamWriter sw = File.AppendText(path))
                                                {
                                                    sw.WriteLine("New password: " + result);
                                                }

                                                Console.WriteLine("New password: " + result);
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                else if (passwordLength == 7 && userPattern == 2)
                {
                    for (int i = 0; i < charactersSecondPattern.Length; i++)
                    {
                        string char1 = Convert.ToString(charactersSecondPattern[i]);
                        for (int j = 0; j < charactersSecondPattern.Length; j++)
                        {
                            string char2 = Convert.ToString(charactersSecondPattern[j]);
                            for (int k = 0; k < charactersSecondPattern.Length; k++)
                            {
                                string char3 = Convert.ToString(charactersSecondPattern[k]);
                                for (int l = 0; l < charactersSecondPattern.Length; l++)
                                {
                                    string char4 = Convert.ToString(charactersSecondPattern[l]);
                                    for (int m = 0; m < charactersSecondPattern.Length; m++)
                                    {
                                        string char5 = Convert.ToString(charactersSecondPattern[m]);
                                        for (int n = 0; n < charactersSecondPattern.Length; n++)
                                        {
                                            string char6 = Convert.ToString(charactersSecondPattern[n]);
                                            for (int o = 0; o < charactersSecondPattern.Length; o++)
                                            {
                                                string char7 = Convert.ToString(charactersSecondPattern[o]);

                                                string result = char1 + char2 + char3 + char4 + char5 + char6 + char7;

                                                string path = @"C:\Users\Username\Desktop\passwords.txt";

                                                using (StreamWriter sw = File.AppendText(path))
                                                {
                                                    sw.WriteLine("New password: " + result);
                                                }

                                                Console.WriteLine("New password: " + result);
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                else
                {
                    Console.WriteLine("Whoops. You can only use a password up to 7 and has only 2 patterns.");
                }
            }
            catch (FormatException)
            {
                Console.WriteLine("Input was not in a valid format. Use integers.");
            }
            finally
            {
                Console.WriteLine("End of the program.");
            }
        }
    }
}
\$\endgroup\$
1
  • 3
    \$\begingroup\$ "How many passwords of x length can be created with y characters" is not a password generator. What is it supposed to do? I would be astounded if 13 else ifs and 7 nested fors turns out to be a good idea. \$\endgroup\$
    – Teepeemm
    May 16, 2021 at 3:36

2 Answers 2

3
+25
\$\begingroup\$

Q) Does my program make sense? A) NO.

Q) Is my code clean and readable? A) NO.

Q) What do you suggest for the future, what ideas do you have to improve the code? A) Glad you asked.

You are guilty of repetitive chunks of code, and some of your naming could be improved. The biggest problem I see is that what I expect to be a password generator does too many things beyond generating a password, namely console writes and file writing.

Here are a few of my favorite guiding principles of good coding:

You violate each once of the above. It's easy to see where it repeats codes to violate DRY. Because the DisplayPasswords method will (1) Generate passwords, (2) writes them to a file, and (3) displays them at the console, it violates the SRP. Least Astonishment is violated because I would expect DisplayPasswords to simply display passwords that have already been generated; from its name, I would not expect it to also write passwords to a file.

If I were to write a password generator, it would be a static class. There would be a static method named GetRandomPassword. This method will do only 1 thing: generate a password. Here I use the .NET naming guideline of Action Verb + Noun. The int pattern idea needs to be abandoned, as it only makes sense to you. I would have something along these lines:

public const  string AlphaNumericCharacterSet =  "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

public static string GetRandomPassword(int length, string acceptableCharacters = AlphaNumericCharacterSet)
{ . . . }

This method will only do 1 thing: return a password of length length composed or randomly generated characters from acceptableCharacters. If I would like to invoke special characters, I could call it:

string password = GetRandomPassword(7, AlphaNumericCharacterSet + "!$%&");

Anyone can now call your password generator to get a random password. Person A can write a UI to display 100 passwords to the console. Person B can write a method to write 10_000 passwords to a file.

Rather than having one monster method consume 90% of your project, the better choice is to have smaller, modular methods that (1) are easier to debug and (2) can be reused. Getting a random password would be such as example.

Here is my twist on just generating passwords:

public static class PasswordGenerator
{
    public const int MinimumPasswordLength = 7;
    public const int MaximumPasswordLength = 36;
    public const int DefaultPasswordLength = MinimumPasswordLength;

    public const string LowerAlpha = "abcdefghijklmnopqrstuvwxyz";
    public const string UpperAlpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    public const string Digits = "0123456789";
    public const string SpecialCharacters = "!$$%&*";

    public const string AlphaNumeric = LowerAlpha + UpperAlpha + Digits;
    public const string AllCharacters = LowerAlpha + UpperAlpha + Digits + SpecialCharacters;

    private static Random _random = new Random();

    public static string GetRandomPassword(int length = DefaultPasswordLength, string characterSet = AllCharacters)
    {
        if (length < MinimumPasswordLength || length > MaximumPasswordLength)
        {
            throw new ArgumentOutOfRangeException(nameof(length), $"'{nameof(length)}' must be between {MinimumPasswordLength} and {MaximumPasswordLength}.");
        }
        if (string.IsNullOrWhiteSpace(characterSet))
        {
            throw new ArgumentNullException(nameof(characterSet));
        }
        var password = new char[length];
        for (var i = 0; i < length; i++)
        {
            password[i] = characterSet[_random.Next(characterSet.Length)];
        }
        return new string(password);
    }

    public static string GetRandomPassword(int minLength, int maxLength, string characterSet = AllCharacters)
    {
        if (minLength < MinimumPasswordLength)
        {
            throw new ArgumentOutOfRangeException(nameof(minLength), $"'{nameof(minLength)}' cannot be less than {MinimumPasswordLength}.");
        }
        if (maxLength > MaximumPasswordLength)
        {
            throw new ArgumentOutOfRangeException(nameof(maxLength), $"'{nameof(maxLength)}' cannot be greater than {MaximumPasswordLength}.");
        }
        if (maxLength < minLength)
        {
            throw new ArgumentOutOfRangeException(nameof(maxLength), $"'{nameof(maxLength)}' cannot be less than '{nameof(minLength)}'.");
        }
        // Our method uses inclusive min as does Random.Next.
        // Our method uses inclusive max BUT Random.Next uses an exclusive max.
        // We know minlength is at least MinimumPasswordLength, so we employ a tiny trick 
        // of subtracting 1 and adding it back later.
        var length = _random.Next(minLength - 1, maxLength) + 1;
        return GetRandomPassword(length, characterSet);
    }
}

There are many ways you can use it then. Some examples:

var password1 = PasswordGenerator.GetRandomPassword();
var password2 = PasswordGenerator.GetRandomPassword(8, PasswordGenerator.LowerAlpha + PasswordGenerator.Digits);

If you want to generate a large number of them, and you don't mind spending the memory on an array, you can use:

var passwords = Enumerable.Range(0, 10_000).Select(x => PasswordGenerator.GetRandomPassword(7, 12)).ToArray();

And now you can use 2 different methods to either display the array contents to the console, or to write to a file.

If you wanted an large array of 1 million, you may not want to consume that much memory, so you can use a loop:

using (StreamWriter sw = File.AppendText(path))
{
    var password = PasswordGenerator.GetRandomPassword();
    sw.WriteLine(password);
    Console.WriteLine(password);
}

By having your methods be modular in design and honoring SRP, you have greater flexibility in what you can do. More importantly, others can read your code easier. And sometimes that other person is YOU 6 months from now trying to remember what you did last week.

\$\endgroup\$
1
  • 2
    \$\begingroup\$ I, also, was confused by pattern, which seems like it should hold a regular expression. acceptableCharacters or alphabet is a much better name. \$\endgroup\$
    – Neil
    May 18, 2021 at 16:11
6
\$\begingroup\$

Problem Size

This is an exponential problem in passwordLength. Your on-screen output and file output is going to take a maximum of 62^7 = 3,521,614,606,208 words, each "New password: " + 7 + "\n", minimum 22 bytes in UTF-8, that's over 70 TiB. I would choose another format to express it in, since this is unlikely to complete. Maybe with a subset of characters?

Output

One should know your target audience. That being said, as a UNIX user, I would expect to see stdout be the only thing that gets written to. I would question the use of a file at all; you can always redirect to a file if needed. Prefixing "New password: " is unlikely to be useful for automation.

You noted that you tried to have special characters, which is great. Maybe IDE assumes a different character encoding; see the absolute minimum about character sets. Passwords are characters, too, and some password hosts have different capabilities.

Repetition

You seem to have a lot of repetition in your code. For example, for (int i = 0; i < charactersSecondPattern.Length; i++) is repeated several times in slightly different, more and more complicated contexts. Note that generating one character is independent of the next; maybe you could use recursion to generate each part of the password, something like (in hand-wave),

words[] F(characters, n) {
    if(n == 1) return { characters };
    for(a in characters) set <- { a, F(characters, n - 1); }
    return set;
}

Note that this set also has an exponential increase in resources, so may be intractable in practice. On the flip side, that is why a good password is good.

Generator?

This code in this question I would describe as a password enumerator; it doesn't really help with choosing a password, just transforms the problem into another form. I would say a password generator chooses a password automatically, perhaps of this list of passwords. This involves randomization.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.