Anders Bergh diseñó a la inversa el OS X Password Assistant y escribió una utilidad de línea de comandos que hace el mismo trabajo. Desde su fuente puede descubrir que toda la generación de contraseñas se realiza mediante una función de biblioteca no documentada patentada SFPWAPasswordSuggest()
del marco de SecurityFoundation .
Aunque la función es propietaria y no existe documentación oficial de Apple, puede jugar con la función en sí o con la utilidad de línea de comandos que la rodea para averiguar qué está produciendo. Allister Banks ya lo hizo en 2015 , asumiendo que para OS X Yosemite, y surgió un fragmento de código que produce prácticamente los mismos resultados (excepto algunos las preferencias de los extraños autores, como limitar el rango de números aleatorios disponibles).
En pocas palabras, SFPWAPasswordSuggest()
escoge dos palabras al azar de un vocabulario e inserta un número aleatorio y un carácter especial (en ese orden) entre ellas. La longitud del número aleatorio es tal que la longitud total de la cadena será exactamente la requerida, y siempre habrá un único carácter especial. Con respecto al vocabulario, OS X de Yosemite a High Sierra usa /System/Library/Frameworks/SecurityInterface.framework/Resources/pwa_dict_en.gz
, 287 kb en tamaño y con 83935 palabras en inglés de longitud entre 2 y 24, para eso:
$ gzcat pwa_dict_en.gz | python2 -c 'import sys
> sys.stdin.read(512)
> word_counts = dict()
> for num_of_letters_index in range(64):
> value = int(sys.stdin.read(8).strip(), 16)
> if value:
> word_counts[num_of_letters_index] = value
> print "Distribution:", word_counts'
Distribution: {1: 26, 2: 191, 3: 1229, 4: 3170, 5: 5591, 6: 8913, 7: 12452, 8: 13462, 9: 12163, 10: 9820, 11: 7007, 12: 4516, 13: 2704, 14: 1429, 15: 691, 16: 329, 17: 150, 18: 61, 19: 21, 20: 5, 21: 3, 22: 1, 24: 1}
$
Aquí se explica cómo puede analizar el archivo . En otras versiones de OS X, la función puede diseñarse de manera algo diferente; usa dtruss ./sf-pwgen
para calcularlo.
Tenga en cuenta que la GUI limita la longitud de su contraseña generada con el número 31. Por lo que puedo ver, la función no produce un error para solicitudes más largas, pero no se garantiza que funcione con contraseñas de más de 31 caracteres. En realidad, para contraseñas de más de 67 caracteres, es casi seguro que falle; como se puede ver arriba, el vocabulario no presenta suficientes palabras de suficiente longitud, y el algoritmo de generación no maneja bien este caso, simplemente devuelve una breve frase de contraseña que consta de solo números y un carácter especial:
$ ./sf-pwgen -c 1 -l 68
2814154076!
$
EDIT 24.01.2018: en nombre de Alex Recuenco :
Cálculo de la entropía
Siguiendo la solución @ximaera.
import math
x = {1: 26, 2: 191, 3: 1229, 4: 3170, 5: 5591, 6: 8913, 7: 12452, 8: 13462, 9: 12163, 10: 9820, 11: 7007, 12: 4516, 13: 2704, 14: 1429, 15: 691, 16: 329, 17: 150, 18: 61, 19: 21, 20: 5, 21: 3, 22: 1, 24: 1}
def entropy(pass_length, n_symbols = 30):
combinations = 0
for key, value in x.items():
for key2, value2 in x.items():
if (key + key2) < (pass_length - 1):
combinations += value * value2 * n_symbols * (10 ** (pass_length - key - key2 - 1))
# last value
return {'combinations': combinations, 'entropy': math.log2(combinations)}
print(entropy(31))
Que cuando lo ejecutas:
> {'combinations': 1124445877165765109161692550890600, 'entropy': 109.79284135298234}
110 bits de entropía, máximo ... Pensé que sería mejor por alguna razón. La entropía de una contraseña de solo caracteres numéricos de longitud 30 es aproximadamente 100