Password Generator
February 4, 2020
Many web sites require passwords that have particular combinations of various types of characters; for instance, a web site might require a password that has at least ten characters, including at least one each from the sets of lower-case letters, upper-case letters, digits, and special characters.
Your task is to write a program that generates passwords according to specifications that you define. When you are finished, you are welcome to read or run a suggested solution, or to post your own solution or discuss the exercise in the comments below.
Here is my take on it in Julia: https://pastebin.com/knT8W7zj
I defined just 3 sets of characters but if one requires special characters too, one can define a forth set too and incorporate it in the program. Personally I find that easier to remember passwords that are of larger length are more effective and practical. Cheers!
Here’s a solution in Python.
Example output:
Technically off-topic, but NIST has issued a new set of recommendations representing the state of the art in password choice: 8-64 characters, any Unicode character is valid (but normalize), check against known-bad and known-broken password databases, no password hints, no expiration. The idea is to make passwords both easier to remember and harder to break. There are other recommendations as well, like always allowing paste (to help password managers) and having an option to enable echoing while typing.
So hopefully code like this will eventually be obsolete.
I think the interface is more interesting than the code itself. Here’s mine (done in Chez Scheme):
The password-generator takes a string in a DSL for specifying passwords, and optionally, minimum and maximum lengths.
The password-generator can take categories of characters:
Any capital letter (e.g., A or M) represents capital letters
Any lowercase letter represents lowercase letters
Any digit represents digits
Thus AAA represents 3 capital letters. Optionally, I can specify the length in braces: A{2–5} represents anywhere between 2 and 5 capital letters.
A user-defined category is specified in single quotes: ‘@$’ means one of the @ or $ characters
What password-generator returns is a thunk that generates suggestions based on the specification. The suggested passwords are permuted
Special syntax exists for specifying an initial, unpermuted substring, in case the password, e.g., MUST begin with a lowercase character… etc.
Whitespaces are ignored in the specification string, and the range can be specified by any number of hyphens: E.g., —– would work just fine.
I implemented this some time ago using my parsing-combinator library (which is why I’m not including the code, because I’d have to upload the library too).
The way I use it is to define various generators based on the specific password syntax required by various sites, at work or on various online shops. Many sites & installations have their own idiosyncratic requirements for passwords, and I grew tired of having to think up a password that fit their specific nonsensical restrictions (e.g., requiring that there be exactly 2 digits or that the password starts with a lowercase character, actually makes it simpler and reduces the potential search space needed).
One amusing consequence of using this password-generator is that I discovered many platforms that publish one set of rules for their passwords, and actually implement a slightly different set of rules. :-)
;;; > (define q
;;; (password-generator
;;; “A{2–3} a{4–8} 9{1–2} ‘!$@+'{1–2}”
;;; ;; optional: minimum & maximum lengths
;;; 7 9))
;;; > (q)
;;; “59vGSszp”
;;; > (q)
;;; “vScH3r+eH”
;;; > (q)
;;; “0AshS+iv”
;;; > (q)
;;; “$t0z!hXkA”
;;; To specify initial, unpermuted elements, use an ! to separate
;;; the fixed from the permuted:
;;; > (define q (password-generator “a ! a{2-4}A{1-2}”))
;;; > (q)
;;; “eLza”
;;; > (q)
;;; “uUtq”
;;; > (q)
;;; “kxQp”
My go to password generator: Python 3 and uses the third-party pyperclip module (https://pypi.org/project/pyperclip/) to copy directly to the clipboard if required.
(define (passgen len)
(let*-values ([(genlist) (λ(x) (flatten (make-list len x)))]
[(s n l u)
(values
(genlist (range 35 46))
(genlist (range 48 57))
(genlist (range 65 90))
(genlist (range 97 122)))]
[(i->c->s) (λ(x) (map (compose string integer->char) (shuffle x)))]
[(lst) (for/fold
([acc ‘()])
([symbol (i->c->s s)]
[number (i->c->s n)]
[ll (i->c->s l)]
[ul (i->c->s u)])
(append (list symbol number ll ul) acc))]
[(password) (string-join (take lst len) “”)])
password))