Generador de contraseñas aleatorio simple de alta calidad

Me interesa crear un generador de contraseñas aleatorias de alta calidad (criptográfica) muy simple. ¿Hay una mejor manera de hacer esto?

import os, random, string length = 13 chars = string.ascii_letters + string.digits + '!@#$%^&*()' random.seed = (os.urandom(1024)) print ''.join(random.choice(chars) for i in range(length)) 

Lo difícil con las contraseñas es hacerlas lo suficientemente fuertes y aún así poder recordarlas. Si la contraseña no debe ser recordada por un ser humano, entonces no es realmente una contraseña.

os.urandom() Python: eso es bueno. Para cualquier propósito práctico (incluso la criptografía), la salida de os.urandom() es indistinguible de la verdadera alea. Luego lo usa como semilla al random , lo que es menos bueno: ese es un PRNG no criptográfico, y su salida puede mostrar alguna estructura que no se registrará en una herramienta de medición estadística, pero que puede ser explotada por un atacante inteligente. Debes trabajar con os.urandom() todo el tiempo. Para simplificar las cosas: elija un alfabeto de longitud 64, por ejemplo, letras (mayúsculas y minúsculas), dígitos y dos caracteres de puntuación adicionales (como ‘+’ y ‘/’). Luego, para cada carácter de contraseña, obtenga un byte de os.urandom() , reduzca el valor módulo 64 (esto es imparcial porque 64 divide 256) y use el resultado como índice en su matriz de chars .

Con un alfabeto de longitud 64, obtienes 6 bits de entropía por carácter (porque 2 6 = 64). Así, con 13 caracteres, obtienes 78 bits de entropía. En última instancia, esto no es fuerte en todos los casos, pero ya es muy fuerte (podría ser derrotado con un presupuesto que se contabilizará en meses y miles de millones de dólares, no solo millones).

XKCD tiene una gran explicación de por qué las contraseñas que crees que son fuertes no lo son .

http://xkcd.com/936/

A cualquier persona que entienda la teoría y la seguridad de la información y se encuentre en una discusión enfurecedora con alguien que no (posiblemente involucre un caso mixto), me disculpo sinceramente. – Randall Munroe

Y si no entiende las matemáticas detrás de lo que explica esta ilustración , no intente escribir nada que deba ser criptográficamente seguro, porque no lo será. Simplemente coloca el mouse hacia abajo y aléjate del teclado.

Hace solo dos días, Kragen Javier Sitaker publicó un progtwig para hacer esto en http://lists.canonical.org/pipermail/kragen-hacks/2011-September/000527.html (ya se ha ido, intente https://github.com / jesterpm / bin / blob / master / mkpasswd )

Genere una contraseña aleatoria y memorizable: http://xkcd.com/936/

Ejecución de ejemplo:

kragen en inexorable: ~ / devel / inexorable-misc $ ./mkpass.py 5 12 Su contraseña es “etapas residenciales guardadas por daños aprendidos”. Eso es equivalente a una clave de 60 bits.

Esa contraseña tardaría 2.5e + 03 CPU-años en descifrar mi barato Celeron E1200 de 2008, suponiendo un ataque fuera de línea en un hash de MS-Cache, que es el peor algoritmo de hashing de contraseña de uso común, un poco peor que incluso el simple MD5.

El algoritmo de hashing de contraseña más común en estos días es el MD5 iterado de FreeBSD; agrietar tal hash tomaría 5.2e + 06 CPU-años.

Pero una GPU moderna puede romperse unas 250 veces más rápido, de modo que el mismo MD5 iterado caería en 2e + 04 GPU-años.

Esa GPU cuesta alrededor de US $ 1.45 por día para ejecutarse en 2011, por lo que descifrar la contraseña costaría aproximadamente US $ 3e + 09.

Comencé a usar una contraseña generada de esta manera en lugar de una contraseña aleatoria de 9 caracteres ASCII para imprimir, que es igual de sólida. La afirmación de Munroe de que estas contraseñas son mucho más fáciles de memorizar es correcta. Sin embargo, todavía hay un problema: debido a que hay muchos menos bits de entropía por carácter (alrededor de 1.7 en lugar de 6.6) hay mucha redundancia en la contraseña, por lo que ataques como el ataque del canal de tiempo ssh (la Canción, El ataque de Wagner y Tian Herbivore, que aprendí de Bram Cohen en el Bagdad Café en la madrugada (una mañana, hace muchos años) y los ataques de grabación de audio en el teclado tienen muchas más posibilidades de capturar suficiente información para poder atacar la contraseña.

Mi medida para contrarrestar el ataque de Herbivore, que funciona bien con una contraseña de 9 caracteres, pero es extremadamente molesta con mi nueva contraseña, es escribir la contraseña con un retraso de medio segundo entre los caracteres, de modo que el canal de sincronización no contenga mucha información sobre el caracteres reales utilizados. Además, la longitud inferior de la contraseña de 9 caracteres inherentemente le da al enfoque de Herbivore mucha menos información para masticar.

Otras posibles contramedidas incluyen el uso del modo shell de Emacs, que le solicita la contraseña localmente cuando reconoce una solicitud de contraseña y luego envía toda la contraseña de una vez, y copia y pega la contraseña desde otro lugar.

Como era de esperar, esta contraseña también tarda un poco más en escribirse: unos 6 segundos en lugar de unos 3 segundos.

 #!/usr/bin/python # -*- coding: utf-8 -*- import random, itertools, os, sys def main(argv): try: nwords = int(argv[1]) except IndexError: return usage(argv[0]) try: nbits = int(argv[2]) except IndexError: nbits = 11 filename = os.path.join(os.environ['HOME'], 'devel', 'wordlist') wordlist = read_file(filename, nbits) if len(wordlist) != 2**nbits: sys.stderr.write("%r contains only %d words, not %d.\n" % (filename, len(wordlist), 2**nbits)) return 2 display_password(generate_password(nwords, wordlist), nwords, nbits) return 0 def usage(argv0): p = sys.stderr.write p("Usage: %s nwords [nbits]\n" % argv0) p("Generates a password of nwords words, each with nbits bits\n") p("of entropy, choosing words from the first entries in\n") p("$HOME/devel/wordlist, which should be in the same format as\n") p(", which is a text file\n") p("with one word per line, preceded by its frequency, most frequent\n") p("words first.\n") p("\nRecommended:\n") p(" %s 5 12\n" % argv0) p(" %s 6\n" % argv0) return 1 def read_file(filename, nbits): return [line.split()[1] for line in itertools.islice(open(filename), 2**nbits)] def generate_password(nwords, wordlist): choice = random.SystemRandom().choice return ' '.join(choice(wordlist) for ii in range(nwords)) def display_password(password, nwords, nbits): print 'Your password is "%s".' % password entropy = nwords * nbits print "That's equivalent to a %d-bit key." % entropy print # My Celeron E1200 # () # was released on January 20, 2008. Running it in 32-bit mode, # john --test () reports that it # can do 7303000 MD5 operations per second, but I'm pretty sure # that's a single-core number (I don't think John is # multithreaded) on a dual-core processor. t = years(entropy, 7303000 * 2) print "That password would take %.2g CPU-years to crack" % t print "on my inexpensive Celeron E1200 from 2008," print "assuming an offline attack on a MS-Cache hash," print "which is the worst password hashing algorithm in common use," print "slightly worse than even simple MD5." print t = years(entropy, 3539 * 2) print "The most common password-hashing algorithm these days is FreeBSD's" print "iterated MD5; cracking such a hash would take %.2g CPU-years." % t print # (As it happens, my own machines use Drepper's SHA-2-based # hashing algorithm that was developed to replace the one # mentioned above; I am assuming that it's at least as slow as the # MD5-crypt.) #  says a # Core 2 Duo U7600 can do 1.1 Mhash/s (of Bitcoin) at a 1.2GHz # clock with one thread. The Celeron in my machine that I # benchmarked is basically a Core 2 Duo with a smaller cache, so # I'm going to assume that it could probably do about 1.5Mhash/s. # All common password-hashing algorithms (the ones mentioned # above, the others implemented in John, and bcrypt, but not # scrypt) use very little memory and, I believe, should scale on # GPUs comparably to the SHA-256 used in Bitcoin. # The same mining-hardware comparison says a Radeon 5870 card can # do 393.46 Mhash/s for US$350. print "But a modern GPU can crack about 250 times as fast," print "so that same iterated MD5 would fall in %.1g GPU-years." % (t / 250) print # Suppose we depreciate the video card by Moore's law, # ie halving in value every 18 months. That's a loss of about # 0.13% in value every day; at US$350, that's about 44¢ per day, # or US$160 per GPU-year. If someone wanted your password as # quickly as possible, they could distribute the cracking job # across a network of millions of these cards. The cards # additionally use about 200 watts of power, which at 16¢/kWh # works out to 77¢ per day. If we assume an additional 20% # overhead, that's US$1.45/day or US$529/GPU-year. cost_per_day = 1.45 cost_per_crack = cost_per_day * 365 * t print "That GPU costs about US$%.2f per day to run in 2011," % cost_per_day print "so cracking the password would cost about US$%.1g." % cost_per_crack def years(entropy, crypts_per_second): return float(2**entropy) / crypts_per_second / 86400 / 365.2422 if __name__ == '__main__': sys.exit(main(sys.argv)) 

Implementando la solución @Thomas Pornin.

 import M2Crypto import string def random_password(length=10): chars = string.ascii_uppercase + string.digits + string.ascii_lowercase password = '' for i in range(length): password += chars[ord(M2Crypto.m2.rand_bytes(1)) % len(chars)] return password 

Otra implementación del método XKCD:

 #!/usr/bin/env python import random import re # apt-get install wbritish def randomWords(num, dictionary="/usr/share/dict/british-english"): r = random.SystemRandom() # ie preferably not pseudo-random f = open(dictionary, "r") count = 0 chosen = [] for i in range(num): chosen.append("") prog = re.compile("^[az]{5,9}$") # reasonable length, no proper nouns if(f): for word in f: if(prog.match(word)): for i in range(num): # generate all words in one pass thru file if(r.randint(0,count) == 0): chosen[i] = word.strip() count += 1 return(chosen) def genPassword(num=4): return(" ".join(randomWords(num))) if(__name__ == "__main__"): print genPassword() 

Salida de muestra:

 $ ./randompassword.py affluent afford scarlets twines $ ./randompassword.py speedboat ellipse further staffer 

Sé que esta pregunta se publicó en 2011, pero para aquellos que vienen ahora en 2014 y más allá, tengo una cosa que decir: RESISTE EL IMPULSO DE REINVENTAR LA RUEDA.

En estas situaciones, lo mejor que puede hacer es buscar software de código abierto, por ejemplo, restringir su búsqueda a los resultados de github. De lejos lo mejor que he encontrado:

https://github.com/redacted/XKCD-password-generator

No se puede confiar en el generador de números pseudoaleatorios de python cuando se genera una contraseña. No es necesariamente criptográficamente aleatorio. Está sembrando el generador de números pseudoaleatorios de os.urandom que es un buen comienzo. Pero luego dependes del generador de python después de eso.

Una mejor opción sería la clase random.SystemRandom() que toma números aleatorios de la misma fuente que urandom . De acuerdo con la documentación de Python que debería ser lo suficientemente buena para el uso criptográfico. La clase SystemRandom le brinda todo lo que hace la clase aleatoria principal, pero no necesita preocuparse por la pseudoaleatoriedad.

Código de ejemplo usando random.SystemRandom (para Python 2.6):

 import random, string length = 13 chars = string.ascii_letters + string.digits + '!@#$%^&*()' rnd = random.SystemRandom() print ''.join(rnd.choice(chars) for i in range(length)) 

Nota: su millaje puede variar: la documentación de Python dice que la disponibilidad aleatoria del sistema varía según el sistema operativo.

Teniendo en cuenta tu comentario,

Solo necesito poder generar contraseñas que sean más seguras que las que se me ocurren en la cabeza.

Parece que quieres usar tu progtwig para generar contraseñas, en lugar de solo escribirlo como un ejercicio. Es preferible utilizar una implementación existente, ya que si comete un error, la salida podría verse comprometida. Lea acerca de los ataques de generador de números aleatorios ; en particular, un conocido error RNG en Debian expuso las claves privadas SSL de las personas.

Así que en su lugar, considere usar pwgen . Ofrece varias opciones, que debe elegir dependiendo de para qué planea usar las contraseñas.

Implentando la solución de @Thomas Pornin: (no puedo comentar la respuesta inexacta de @Yossi)

 import string, os chars = string.letters + string.digits + '+/' assert 256 % len(chars) == 0 # non-biased later modulo PWD_LEN = 16 print ''.join(chars[ord(c) % len(chars)] for c in os.urandom(PWD_LEN)) 
 import random r = random.SystemRandom() def generate_password(words, top=2000, k=4, numbers=None, characters=None, first_upper=True): """Return a random password based on a sorted word list.""" elements = r.sample(words[:top], k) if numbers: elements.insert(r.randint(1, len(elements)), r.choice(numbers)) if characters: elements.insert(r.randint(1, len(elements)), r.choice(characters)) if first_upper: elements[0] = elements[0].title() return ''.join(elements) if __name__ == '__main__': with open('./google-10000-english-usa.txt') as f: words = [w.strip() for w in f] print(generate_password(words, numbers='0123456789', characters='!@#$%')) 
  • Genera contraseñas que puedes recordar
  • Utiliza os.urandom()
  • Maneja reglas del mundo real como agregar números, mayúsculas, caracteres.

Claro que se puede mejorar, pero esto es lo que uso.

De esa manera funciona. Está perfectamente bien. Si tenía reglas adicionales, como excluir palabras del diccionario, es posible que desee incluir esos filtros también, pero la probabilidad de generar aleatoriamente una palabra del diccionario con esa configuración es extremadamente pequeña.

Hay algunos problemas con su implementación:

 random.seed = (os.urandom(1024)) 

Esto no siembra el generador de números aleatorios; Reemplaza la función de seed con un bytestring. Necesitas llamar a seed , like, random.seed(…) .

 print ''.join(random.choice(chars) for i in range(length)) 

El PRNG predeterminado de Python es un Mersenne Twister, que no es un PRNG criptográficamente sólido, por lo que desconfío de usarlo con fines criptográficos. El módulo random incluye random.SystemRandom , que en al menos la mayoría de los sistemas * nix, debe usar un CSPRNG. Sin embargo

 random.choice(chars) 

… se implementa como …

 def choice(self, seq): """Choose a random element from a non-empty sequence.""" return seq[int(self.random() * len(seq))] # raises IndexError if seq is empty 

… En Python 2 . Desafortunadamente, la self.random aquí es una función de C, por lo que es difícil de ver; El olor del código aquí es que este código casi seguramente no se elige uniformemente. El código ha cambiado completamente en Python 3 y hace un trabajo mucho mejor para garantizar la uniformidad. Los documentos de Python 3 para randrange note,

randrange() en la versión 3.2: randrange() es más sofisticado en la producción de valores distribuidos equitativamente. Anteriormente utilizaba un estilo como int(random()*n) que podía producir distribuciones ligeramente desiguales.

randrange y choice ambos llaman al mismo método ( _randbelow ) debajo del capó.

En Python 3, la choice está bien; en Python 2, solo se acerca a una distribución uniforme, pero no lo garantiza. Ya que esto es criptográfico, me apoyo en el lado de la cerca “no arriesgarse” y me gustaría tener esa garantía.

Es fácil 🙂

 def codegenerator(): alphabet = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" pw_length = 8 mypw = "" for i in range(pw_length): next_index = random.randrange(len(alphabet)) mypw = mypw + alphabet[next_index] return mypw 

y el hacer

 print codegenerator() 

Gracias http://xkcd.com/936/

Construí mi propia respuesta de CLI al tema en cuestión (código fuente completo en la siguiente URL):

http://0netenv.blogspot.com/2016/08/password-generator-with-argparse.html

Escribió un generador de contraseñas usando argparse. ¡Espero que esto ayude a alguien (ya sea construyendo un generador de contraseñas o usando argparse)!

De cualquier manera, fue divertido construir!

 $ ./pwgen.py -h usage: pwgen.py [-h] [-c COUNT] [-a] [-l] [-n] [-s] [-u] [-p] Create a random password Special characters, numbers, UPPERCASE -"Oscar", and lowercase -"lima" to avoid confusion. Default options (no arguments): -c 16 -a Enjoy! --0NetEnv@gmail.com optional arguments: -h, --help show this help message and exit -c COUNT, --count COUNT password length -a, --all same as -l -n -s -u -l, --lower include lowercase characters -n, --number include 0-9 -s, --special include special characters -u, --upper include uppercase characters -p, --license print license and exit 

Aquí está el código:

 #!/usr/bin/env python2 # -*- coding: utf-8 -*- license = """ # pwgen -- the pseudo-random password generator # # This software is distributed under the MIT license. # # The MIT License (MIT) # # Copyright (c) 2016 0NetEnv 0netenv@gmail.com # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated # documentation files (the "Software"), to deal in the # Software without restriction, including without # limitation the rights to use, copy, modify, merge, # publish, distribute, sublicense, and/or sell copies # of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following # conditions: # # The above copyright notice and this permission notice # shall be included in all copies or substantial portions # of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF # ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED # TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT # SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR # IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. # # NOTE: # This software was tested on Slackware 14.2, Raspbian, & # Mac OS X 10.11 # """ import string import random import sys # first time using argparse library import argparse # wanted to change the formatting of the help menu a little bit, so used RawTextHelpFormatter directly from argparse import RawTextHelpFormatter typo = '' c = 16 counter = 0 line = '-' * 40 # CREATE FUNCTION for PWGEN def pwgen(z, t): # EMPTY SET OF CHARACTERS charsset = '' # UPPERCASE -"O" U = 'ABCDEFGHIJKLMNPQRSTUVWXYZ' # lowercase -"l" L = 'abcdefghijkmnopqrstuvwxyz' N = '0123456789' S = '!@#$%^&*?<>' # make sure we're using an integer, not a char/string z = int(z) for type in t: if 'u' in t: charsset = charsset + U if 'l' in t: charsset = charsset + L if 'n' in t: charsset = charsset + N if 's' in t: charsset = charsset + S if 'a' == t: charsset = charsset + U + L + N + S return ''.join(random.choice(charsset) for _ in range(0, int(z))) # GET ARGUMENTS using ARGPARSE parser = argparse.ArgumentParser(description='\n Create a random password\n\ Special characters, numbers, UPPERCASE -"Oscar",\n\ and lowercase -"lima" to avoid confusion.\n\ Default options (no arguments): -c 16 -a\n\ \t\tEnjoy! --0NetEnv@gmail.com', formatter_class=argparse.RawTextHelpFormatter) parser.add_argument("-c", "--count", dest="count", action="store", help="password length") parser.add_argument("-a", "--all", help="same as -l -n -s -u", action="store_true") parser.add_argument("-l", "--lower", help="include lowercase characters", action="store_true") parser.add_argument("-n", "--number", help="include 0-9", action="store_true") parser.add_argument("-s", "--special", help="include special characters", action="store_true") parser.add_argument("-u", "--upper", help="include uppercase characters", action="store_true") parser.add_argument("-p", "--license", help="print license and exit", action="store_true") # COLLECT ARGPARSE RESULTS results = args = parser.parse_args() # CHECK RESULTS # Check that a length was given. # If not, gripe and exit. if args.count == '0': print ("Input error:\nCannot create a zero length password.\nExiting") exit (0) # check character results and add to counter if # selection is made. if args.lower: typo = typo + 'l' counter = counter + 1 #print "lower" if args.number: typo = typo + 'n' counter = counter + 1 #print "number" if args.special: typo = typo + 's' counter = counter + 1 #print "special" if args.upper: typo = typo + 'u' counter = counter + 1 #print "upper" if args.all: typo = 'a' counter = counter + 1 #print "all" if args.license: print (license) exit (1) # CHECK COUNTER # Check our counter and see if we used any command line # options. We don't want to error out. # try it gracefully. If no arguments are given, # use defaults and tell the user. # args.count comes from argparse and by default requires # an input to '-c'. We want to get around that for the # sake of convenience. # Without further adieu, here's our if statement: if args.count: if counter == 0: typo = 'a' print ("defaulting to '--all'") print (line) print (pwgen(results.count,typo)) else: if counter == 0: typo = 'a' print ("defaulting to '--count 16 --all'") print (line) print (pwgen(c,typo)) print (line) #print typo 

Me encanta la lingüística, en mi enfoque creo pseudo palabras memorables con un alto nivel de entropía alternando consonantes y vocales.

  • No es susceptible a ataques de diccionario.
  • Pronunciable y por lo tanto buena oportunidad de ser memorable.
  • Contraseñas cortas con fuerza decente
  • Parámetro opcional para agregar un dígito aleatorio para compatibilidad (menos memorable, pero se ajusta a las aplicaciones creadas con el pensamiento de seguridad de la contraseña anterior, por ejemplo, que requiere un dígito)

Código Python:

 import random import string def make_pseudo_word(syllables=5, add_number=False): """Create decent memorable passwords. Alternate random consonants & vowels """ rnd = random.SystemRandom() s = string.ascii_lowercase vowels = 'aeiou' consonants = ''.join([x for x in s if x not in vowels]) pwd = ''.join([rnd.choice(consonants) + rnd.choice(vowels) for x in range(syllables)]).title() if add_number: pwd += str(rnd.choice(range(10))) return pwd >>> make_pseudo_word(syllables=5) 'Bidedatuci' >>> make_pseudo_word(syllables=5) 'Fobumehura' >>> make_pseudo_word(syllables=5) 'Seganiwasi' >>> make_pseudo_word(syllables=4) 'Dokibiqa' >>> make_pseudo_word(syllables=4) 'Lapoxuho' >>> make_pseudo_word(syllables=4) 'Qodepira' >>> make_pseudo_word(syllables=3) 'Minavo' >>> make_pseudo_word(syllables=3) 'Fiqone' >>> make_pseudo_word(syllables=3) 'Wiwohi' 

Contras:

  • para los hablantes de latín y alemán y los que están familiarizados con el inglés
  • uno debe usar vocales y consonantes del lenguaje predominante con los usuarios de la aplicación o grupo de enfoque y sintonizar

Aquí hay otra implementación (python 2; requeriría algunas reescrituras menores para que funcione en 3) que es mucho más rápida que las OJW, que parece recorrer el diccionario de cada palabra, a pesar del comentario / implicación en contrario. La sincronización del script de OJW en mi máquina, con un SSD de 80,000 IOP:

 real 0m3.264s user 0m1.768s sys 0m1.444s 

La siguiente secuencia de comandos carga todo el diccionario en una lista, luego selecciona palabras basándose en una selección aleatoria del valor del índice, utilizando la expresión regular de OJW para el filtrado.

Esto también genera 10 conjuntos de frase de contraseña, permite pasar parámetros de línea de comando para ajustar el número de palabras y agrega relleno de números y símbolos (también longitud ajustable).

Horas de muestra para este script:

 real 0m0.289s user 0m0.176s sys 0m0.108s 

Uso: xkcdpass-mod.py 2 4 (por ejemplo, estos son los valores predeterminados).

Imprime espacios en la salida para una fácil lectura, aunque casi nunca he encontrado un servicio en línea que permita usarlos, así que simplemente los ignoraría. Esto definitivamente se podría limpiar con argparse o getopt y permitir conmutadores para incluir espacios o no, incluyendo / excluyendo símbolos, mayúsculas, etc., además de algunas refactorizaciones adicionales, pero aún no he llegado a eso. Así que sin más preámbulos:

 #!/usr/bin/env python #Copyright AMH, 2013; dedicated to public domain. import os, re, sys, random from sys import argv def getargs(): if len(argv) == 3: numwords = argv[1] numpads = argv[2] return(numwords, numpads) elif len(argv) == 2: numwords = argv[1] numpads = 4 return (numwords, numpads) else: numwords = 2 numpads = 4 return (numwords, numpads) def dicopen(dictionary="/usr/share/dict/american-english"): f = open(dictionary, "r") dic = f.readlines() return dic def genPassword(numwords, numpads): r = random.SystemRandom() pads = '0123456789!@#$%^&*()' padding = [] words = dicopen() wordlist = [] for i in range (0,int(numpads)): padding.append(pads[r.randint(0,len(pads)-1)]) #initialize counter for only adding filtered words to passphrase j = 0 while (j < int(numwords)): inclusion_criteria = re.compile('^[az]{5,10}$') #Select a random number, then pull the word at that index value, rather than looping through the dictionary for each word current_word = words[r.randint(0,len(words)-1)].strip() #Only append matching words if inclusion_criteria.match(current_word): wordlist.append(current_word) j += 1 else: #Ignore non-matching words pass return(" ".join(wordlist)+' '+''.join(padding)) if(__name__ == "__main__"): for i in range (1,11): print "item "+str(i)+"\n"+genPassword(getargs()[0], getargs()[1]) 

Salida de muestra:

 [✗]─[user@machine]─[~/bin] └──╼ xkcdpass-mod.py item 1 digress basketball )%^) item 2 graves giant &118 item 3 impelled maniacs ^@%1 

Y yendo por la "grapa de batería de caballo correcta" (CHBS) completa, sin relleno:

 ┌─[user@machine]─[~/bin] └──╼ xkcdpass-mod.py 4 0 item 1 superseded warred nighthawk rotary item 2 idealize chirruping gabbing vegan item 3 wriggling contestant hiccoughs instanced 

According to https://www.grc.com/haystack.htm , for all practical purposes, assuming 100 trillion guesses per second (ie, 100 TH/s) the shorter version would take about 50-60 million centuries to crack; the full CHBS = 1.24 hundred trillion trillion centuries; adding padding to that, 15.51 trillion trillion trillion centuries.

Even enlisting the entire Bitcoin mining network (~2500 TH/s as of this writing), the short version would still likely take 250-300 million years to break, which is probably secure enough for most purposes.

 import uuid print('Your new password is: {0}').format(uuid.uuid4()) 

A little bit off topic, but I made this, using also TKinter. Hope it can helps:

 import os, random, string from tkinter import * def createPwd(): try: length = int(e1.get()) except ValueError: return chars = string.ascii_letters + string.digits + '!@#$%^&*()?\/' random.seed = (os.urandom(1024)) e2.config(state=NORMAL) e2.delete(0,'end') e2.insert(0,''.join(random.choice(chars) for i in range(length))) e2.config(state="readonly") mainWindow = Tk() mainWindow.title('Password generator') mainWindow.resizable(0,0) f0 = Frame(mainWindow) f0.pack(side=TOP,pady=5,padx=5,fill=X,expand=1) Label(f0,text="Length: ",anchor=E).grid(row=0,column=0,sticky=E) e1 = Entry(f0) e1.insert(0,'12') e1.grid(row=0,column=1) btn = Button(f0,text="Generate") btn['command'] = lambda: createPwd() btn.grid(row=0,column=2,rowspan=1,padx=10,ipadx=10) Label(f0,text="Generated password: ",anchor=E).grid(row=1,column=0,sticky=E) e2 = Entry(f0) e2.grid(row=1,column=1) createPwd() #starting main window mainWindow.mainloop() 

This is a simple small program addressed to people whome can’t figure out a secure passwords for there own public accounts.

Just run the program on a command console and pass in a bunch of letters that seems familiar to you, and it will generate a sequence of symbols based on what you’ve inserted.

of course, the program does not support multiple sequences generation.

You can download the code from my github pull: https://github.com/abdechahidely/python_password_generator

 from string import ascii_lowercase, ascii_uppercase, digits, punctuation from random import randint, choice, shuffle from math import ceil from re import finditer lower_cases = ascii_lowercase upper_cases = ascii_uppercase lower_upper = dict(zip(lower_cases, upper_cases)) upper_lower = dict(zip(upper_cases, lower_cases)) punctuations = '#$%&@!?.' space = ' ' class PunctOrDigit(): def __init__(self, number_of_punctuations, number_of_digits): self.puncts = number_of_punctuations self.digits = number_of_digits self.dupl_puncts = self.puncts self.dupl_digits = self.digits def PorD(self): symbol_type = choice('pd') if symbol_type == 'p': if self.puncts == 0: return 'd' else: self.puncts -= 1 return symbol_type if symbol_type == 'd': if self.digits == 0: return 'p' else: self.digits -= 1 return symbol_type def reset(self): self.puncts = self.dupl_puncts self.digits = self.dupl_digits def is_empty(text): for symbol in text: if symbol != space: return False return True def contain_unauthorized_symbols(text): for symbol in text: if symbol in punctuation or symbol in digits: return True return False def user_input(): user_input = input('-- Sentence to transform: ') while is_empty(user_input) or len(user_input) < 8 or contain_unauthorized_symbols(user_input): user_input = input('-- Sentence to transform: ') return user_input def number_of_punctuations(text): return ceil(len(text) / 2) - 3 def number_of_digits(text): return ceil(len(text) / 2) - 2 def total_symbols(text): return (number_of_digits(text) + number_of_punctuations(text), number_of_punctuations(text), number_of_digits(text)) def positions_to_change(text): pos_objct = PunctOrDigit(number_of_punctuations(text), number_of_digits(text)) positions = {} while len(positions) < total_symbols(text)[0]: i = randint(0,len(text)-1) while i in positions: i = randint(0,len(text)-1) positions[i] = pos_objct.PorD() pos_objct.reset() return positions def random_switch(letter): if letter in lower_cases: switch_or_pass = choice('sp') if switch_or_pass == 's': return lower_upper[letter] else: return letter if letter in upper_cases: switch_or_pass = choice('sp') if switch_or_pass == 's': return upper_lower[letter] else: return letter def repeated(text): reps = {} for letter in set(list(text)): indexs = [w.start() for w in finditer(letter, text)] if letter != ' ': if len(indexs) != 1: reps[letter] = indexs return reps def not_repeated(text): reps = {} for letter in set(list(text)): indexs = [w.start() for w in finditer(letter, text)] if letter != ' ': if len(indexs) == 1: reps[letter] = indexs return reps def generator(text, positions_to_change): rep = repeated(text) not_rep = not_repeated(text) text = list(text) for x in text: x_pos = text.index(x) if x not in positions_to_change: text[x_pos] = random_switch(x) for x in rep: for pos in rep[x]: if pos in positions_to_change: if positions_to_change[pos] == 'p': shuffle(list(punctuations)) text[pos] = choice(punctuations) if positions_to_change[pos] == 'd': shuffle(list(digits)) text[pos] = choice(digits) for x in not_rep: for pos in not_rep[x]: if pos in positions_to_change: if positions_to_change[pos] == 'p': shuffle(list(punctuations)) text[pos] = choice(punctuations) if positions_to_change[pos] == 'd': shuffle(list(digits)) text[pos] = choice(digits) text = ''.join(text) return text if __name__ == '__main__': x = user_input() print(generator(x, positions_to_change(x))) 

Here is my random password generator after researching this topic:

 `import os, random, string #Generate Random Password UPP = random.SystemRandom().choice(string.ascii_uppercase) LOW1 = random.SystemRandom().choice(string.ascii_lowercase) LOW2 = random.SystemRandom().choice(string.ascii_lowercase) LOW3 = random.SystemRandom().choice(string.ascii_lowercase) DIG1 = random.SystemRandom().choice(string.digits) DIG2 = random.SystemRandom().choice(string.digits) DIG3 = random.SystemRandom().choice(string.digits) SPEC = random.SystemRandom().choice('!@#$%^&*()') PWD = None PWD = UPP + LOW1 + LOW2 + LOW3 + DIG1 + DIG2 + DIG3 + SPEC PWD = ''.join(random.sample(PWD,len(PWD))) print(PWD)` 

This will generate a random password with 1 random uppercase letter, 3 random lowercase letters, 3 random digits, and 1 random special character–this can be adjusted as needed. Then it combines each random character and creates a random order. I don’t know if this is considered “high quality”, but it gets the job done.

Base64 let us encode binary data in a human readable/writable mode with no data loss.

 import os random_bytes=os.urandom(12) secret=random_bytes.encode("base64") 

My solution based on @Thomas Pornin’s answer (Updated)

 import os, string def get_pass(password_len=12): new_password=None symbols='+!' chars=string.ascii_lowercase+\ string.ascii_uppercase+\ string.digits+\ symbols while new_password is None or \ new_password[0] in string.digits or \ new_password[0] in symbols: new_password=''.join([chars[ord(os.urandom(1)) % len(chars)] \ for i in range(password_len)]) return new_password print(get_pass()) 

This function returns a random password (without a number or a symbol in beginning of the password).

This is more for fun than anything. Scores favorably in passwordmeter.com but impossible to remember.

 #!/usr/bin/ruby puts (33..126).map{|x| ('a'..'z').include?(x.chr.downcase) ? (0..9).to_a.shuffle[0].to_s + x.chr : x.chr}.uniq.shuffle[0..41].join[0..41]