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.

Pages: 1 2

6 Responses to “Password Generator”

  1. Zack said

    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!

  2. Daniel said

    Here’s a solution in Python.

    import random
    import string
    
    def generate(lower, upper, digits, special):
        output = random.choices(string.ascii_lowercase, k=lower)
        output.extend(random.choices(string.ascii_uppercase, k=upper))
        output.extend(random.choices(string.digits, k=digits))
        output.extend(random.choices(string.punctuation, k=special))
        random.shuffle(output)
        return ''.join(output)
    
    print(generate(6, 6, 4, 2))
    

    Example output:

    HX,y8gIK6hO3Fj#y0h
    
  3. John Cowan said

    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.

  4. Mayer Goldberg said

    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)
    ;;; “59vGSsz
    p”
    ;;; > (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”

  5. Alex B said

    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.

    import secrets
    import string
    
    import pyperclip
    
    def password(length=12, copy_to_cb=False, *,
                 allow_lower=True, num_lower=1,
                 allow_upper=True, num_upper=1,
                 allow_digit=True, num_digit=1,
                 allow_symbol=True, num_symbol=1,
                 disallowed_symbols=None):
        """Generate a random password conforming to the input parameters"""
        SYMBOLS = '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'
    
        allowed_chars = ''
        allowed_chars += string.ascii_uppercase if allow_upper else ''
        allowed_chars += string.ascii_lowercase if allow_lower else ''
        allowed_chars += string.digits if allow_digit else ''
        allowed_chars += SYMBOLS if allow_symbol else ''
    
        if allow_symbol and disallowed_symbols is not None:
            for sym in disallowed_symbols:
                allowed_chars = allowed_chars.replace(sym, '')
    
        if allowed_chars == '':
            raise Exception(
                'Cannot forbid all character types')
    
        if not allow_lower:
            num_lower = 0
        if not allow_upper:
            num_upper = 0
        if not allow_digit:
            num_digit = 0
        if not allow_symbol:
            num_symbol = 0
    
        if num_lower+num_upper+num_digit+num_symbol > length:
            raise ValueError(
                'Length cannot be less than sum of character requirements')
    
        while True:
            cand = ''.join(secrets.choice(allowed_chars) for i in range(length))
            if (sum(c.islower() for c in cand) >= num_lower
                    and sum(c.isupper() for c in cand) >= num_upper
                    and sum(c.isdigit() for c in cand) >= num_digit
                    and sum(c in SYMBOLS for c in cand) >= num_symbol):
                if copy_to_cb:
                    pyperclip.copy(cand)
                    print('Password copied to clipboard')
                    return
                else:
                    return cand
    
  6. Patrik said

    (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))

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: