An experiment in generating readable passwords

Best

I’m quite passionate about security as an issue for developers. It’s a highly complex area, and should be an integral part of any good software development team. Because of this I can’t make a post about passwords without putting a few disclaimers against it. Mainly, I’m writing this post because I enjoyed the engineering aspects of the problem. I don’t suggest this as a silver bullet for all password generators – nor do I even suggest a password generator is the best way to design the user sign-on experience. Depending on your use case you may find it more appropriate to consider (for example) email verification with two factor authentication.

The problem

Ok, with that out of the way, we had an interesting problem a while ago that required me to do some research. My business stakeholders required an algorithm that could produce a strong password that could be given to someone over the phone, for a single use. This case existed because we had an important group of users who were not tech-savvy (typically an older generation), and it was believed that we would need to guide them through sign-in over the phone. A password reset link was removed as an option and so we had to provide our call centre with the ability to generate good passwords. Once the user had logged in they would be prompted to reset the password, making it essentially a single-use password.

What makes a strong password, and why do we need one?

I may go into this in more detail in a future post, but generally speaking the longer and more complex a password is the better from a technical point of view. Generally speaking at least 8 characters is good, a combination of characters is great (e.g. upper or lower case characters, with some numbers and/or symbols). Variance in length is good as well, as it broadens the pool of possible passwords. Generally speaking the longer the password the better, adding characters quickly increases the possible permutations of passwords.

We need strong passwords to ensure that a malicious attacker does not brute force or crack passwords from a (stolen) database. Passwords should always be salted and hashed so that they cannot be reversed, but given enough time this can be overcome.

We typically don’t worry too much about the case where the one time password is cracked (although this is important), but we should maintain a strong password policy to protect user passwords whilst they are in our care. After all, we know users will re-use them and we don’t want our system to be the weak point that leads to a breach in another system.

Our problem and password policy are at odds!

The inherit problem we have here is that we want a strong (and thus complex) password policy, but we also want something friendly enough that our user can verbally give the password to someone over the phone with clarity and ease. These two concepts at first glance appear contradictory.

We quickly decided that we didn’t want to support common words or phrases. We wanted our passwords to convey that we took our own policy seriously, and did not encourage weak passwords (e.g. Password1).

Open Syllables

After some research I turned towards the idea of using open syllables. The full definition of syllables is beyond the scope of what I’m talking about here, but open syllables were of interest to me because of the way they can be used together. In short an open syllable is one that allows for additional syllables to follow in the same word. For example “RE” is an open syllable, and is used in “Return”, “Regrow” and “Retain”. Closed syllables can be used in the same word – but in English this is not the norm. “Etch” is a good example of a closed syllable, as there are not many (any?) words that include that syllable in the beginning or end of the word.

I focused on sourcing a list of open syllables, and then on producing a simple algorithm that would string them together.

The implementation

public class PasswordGenerator
{
	private Random random = new Random();
	private TextInfo textInfo = new CultureInfo("en-US", false).TextInfo;
	private List syllables = new List
			{
				"BA", "BE", "BI", "BO", "BU", "BY", "DA", "DE", "DI", "DO", "DU", "DY", "FA", "FE", "FI", "FO",
				"FU", "FY", "GA", "GE", "GI", "GO", "GU", "GY", "HA", "HE", "HI", "HO", "HU", "HY", "JA", "JE", "JI",
				"JO", "JU", "JY", "KA", "KE", "KI", "KO", "KU", "KY", "LA", "LE", "LI", "LO", "LU", "LY", "MA", "ME",
				"MI", "MO", "MU", "MY", "NA", "NE", "NI", "NO", "NU", "NY", "PA", "PE", "PI", "PO", "PU", "PY", "RA",
				"RE", "RI", "RO", "RU", "RY", "SA", "SE", "SI", "SO", "SU", "SY", "TA", "TE", "TI", "TO", "TU", "TY",
				"VA", "VE", "VI", "VO", "VU", "VY", "BRA", "BRE", "BRI", "BRO", "BRU", "BRY", "DRA", "DRE", "DRI", "DRO",
				"DRU", "DRY", "FRA", "FRE", "FRI", "FRO", "FRU", "FRY", "GRA", "GRE", "GRI", "GRO", "GRU", "GRY",
				"PRA", "PRE", "PRI", "PRO", "PRU", "PRY", "STA", "STE", "STI", "STO", "STU", "STY", "TRA", "TRE",
			};
 
	public string GeneratePassword()
	{
		var word = String.Join("", syllables.OrderBy(x => Guid.NewGuid()).Take(3).ToArray());
		var number = random.Next(0, 10000).ToString().PadLeft(4, '0');
 
		return textInfo.ToTitleCase(word.ToLower()) + number;
	}
}

These are examples of the passwords generated:

Lotara1955 Sobeka7973 Laproste3033 Brimuhy2080 Nurola9025
Bofogo1064 Tefyba1938 Dragrefru3956 Tekidre1986 Drajele7994
Mubripo7958 Latuva3952 Pifuja2069 Stestybra5014 Kikesi1919
Frodrume4012 Haryfy2049 Tistyre3975 Mabrugru1949 Brisify7998
Pohyde3949 Grogamy8918 Rurubi1015 Myvoli0971 Grodyja5064
Nahyvy2045 Bebratra7052 Kofafro4929 Broprapi1014 Pibrupra8978
Kastygra8060 Prigustu1952 Nuprify9977 Tumoho4089 Retrado1936
Kistesti2043 Probridry3965 Grodagro5085 Runiny6989 Katrasa6019
Rebigu0961 Grivipe2082 Pravofra4065 Kekire1964 Depriba1965
Tirety8988 Pagubi0057 Dodadru2024 Dapibe5928 Munyko1027
Bodragry2046 Frodely0943 Hakubro4950 Lavevo7027 Tesustu3928
Sukifri2016 Drovuly2039 Mofune1913 Tradrajo5017 Rasyhu7058
Tatuge4947 Myvary8065 Sohysa1048 Stokoly8096 Pinupy9085
Storogu6012 Sistipro4966 Myrudra4080 Fogyga2039 Grarike2068
Grelami2025 Fodimo5043 Pojydu2011 Stegepro2012 Dragomo0023
Mybafre1994 Hoprafre2072 Lemopra5957 Hobrupra1969 Gretuja2069
Hevyty2087 Pravydy1987 Profygu1995 Fijibru1913 Pasusy7058
Hipihi7082 Ralobi2037 Rufrere6061 Dofroku9082 Hetive2072
Dopuse1072 Sodristo1069 Legropri2080 Kostify2091 Masoka1914
Tugihi2047 Brohadu1976 Nulydri1937 Droveso2047 Lojidre1919

Complexity

The above algorithm will generate passwords of between 10 and 13 characters, and uses upper and lower case characters, and numbers. For a blind brute force attack this could mean being forced to cover 2×1023 possible permutations.

Of course, a smart attacker might be able to obtain some sample passwords, and use this to narrow the range of passwords. You could quickly identify the first character is always a capital, followed by lower case characters, followed by 4 numbers. This makes the complexity closer to 2×1022 – still pretty good.

A very good attacker (perhaps one who has read this blog) has the best bet – but still has to search 1.8×1010. This range is definitely within range of brute force attacks – but I’m satisfied that this is sufficiently complex for a one-time, short-lived password. If we required we could fairly easily boost the actual complexity up, through:

  • Extending the range of numbers
  • Randomising the position of the capital letter
  • Adding additional syllables
  • Adding a symbol at the end of the password
  • Or many other minor alterations.

Image Credit: Ryan Hyde

Leave a Reply

Your email address will not be published. Required fields are marked *