Password Generator
February 4, 2020
The hard part of this exercise is figuring out what set of features to include; you want something general-purpose enough to handle the requirements of various web sites. The scheme I came up with is a function that takes four parameters — the counts of lower-case letters, upper-case letters, digits and special characters — and returns an appropriate password:
(define (pgen lower upper digit special) (let ((lowers (genrand lower "abcdefghijklmnopqrstuvwxyz")) (uppers (genrand upper "ABCDEFGHIJKLMNOPQRSTUVWXYZ")) (digits (genrand digit "0123456789")) (specials (genrand special "!@#$%^&*()"))) (list->string (shuffle (append lowers uppers digits specials)))))
(define (genrand count chars) (let ((len (string-length chars))) (do ((k count (- k 1)) (ps (list) (cons (string-ref chars (randint len)) ps))) ((zero? k) ps))))
The function generates characters according to each of the four requested counts, then scrambles them. Here’s an example:
> (pgen 5 2 3 0) "w3Dtk7p5Ew"
You can run the program at https://ideone.com/rOmqzE. Of all the servers and web sites that I use, all use some sort of scheme as described above and none accepts passwords of the “correct horse battery staple” variety.
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))