AVISO: Este projeto migrou para o repositório https://github.com/ramalho/dadoware
Neste notebook comecei com uma grande lista palavras da língua portuguesa, que fui sucessivamente filtrando com diversos critérios até chegar próximo da quantidade necessária para o método diceware. Então embaralhei as 7808
palavras restantes e fatiei a lista para ficar com a quantidade exata: 7776
. No último passo, gerei o arquivo 7776palavras.txt com linhas de "11111 abaci"
a "66666 zurpa"
.
Para começar, confirmei a quantidade de palavras necessárias para usar 5 dados na escolha de palavras:
In [1]:
6**5
Out[1]:
O ponto de partida foi uma lista 326.716
palavras da língua portuguesa que compilei a partir de várias fontes. Aqui eu leio o arquivo, verifico que são todas palavras únicas e exibo uma amostra com as 10 primeiras e as 10 últimas:
In [2]:
completa = [lin.strip() for lin in open('palavras.txt').readlines()]
len(completa), len(completa) == len(set(completa))
Out[2]:
In [3]:
completa[:10], completa[-10:]
Out[3]:
O primeiro filtro foi pegar só palavras de até 5 caracteres:
In [4]:
ate5 = [p for p in completa if len(p) <= 5]
len(ate5)
Out[4]:
Em seguida, fiquei apenas com as palavras formadas exclusivamente por letras minúsculas de "a" a "z", sem acentos ou hífens:
In [5]:
import string
so_ascii = []
for palavra in ate5:
if all(c in string.ascii_lowercase for c in palavra):
so_ascii.append(palavra)
len(so_ascii)
Out[5]:
In [6]:
so_ascii[:10], so_ascii[-10:]
Out[6]:
Examinando a lista obtida até agora, percebi que haviam muitas palavras que eram apenas plurais, como "acres", "botas" etc. Com este código eu removi as palavras que são iguais a outras palavras apenas com um "s" colado no final. Por exemplo, removi "abios" porque a lista já tem a palavra "abio".
In [7]:
ocorrencias = set(so_ascii)
singulares = []
print('Plurais removidas:')
for pal in so_ascii:
if pal[-1] == 's' and pal[:-1] in ocorrencias:
print(pal, end=' ')
else:
singulares.append(pal)
In [8]:
len(singulares)
Out[8]:
Estamos chegando perto das 7776
palavras que precisamos. Aqui fiquei só com as palavras de 4 ou 5 letras:
In [9]:
tam4ou5 = [p for p in singulares if len(p) in (4,5)]
len(tam4ou5)
Out[9]:
In [10]:
sobrando = len(tam4ou5) - 6**5
sobrando
Out[10]:
Ainda precisamos descartar 74 palavras. Procurei na Web uma lista de palavrões, e achei nesta página. Carreguei as palavras, convertendo em minúsculas e criando um set
. A notação {… for … in …}
é uma set comprehension e produz um objeto set
. O resultado foi um conjunto de 301
palavrões.
In [11]:
palavroes = {lin.strip().lower() for lin in open('palavroes.txt', encoding='latin1').readlines()}
In [12]:
len(palavroes)
Out[12]:
Transformando a lista tam4ou5
em um set
fica muito fácil remover os palavrões fazendo apenas uma subtração entre conjuntos. O resultado eu converti em list
novamente, para poder embaralhar a seguir.
In [13]:
limpas = list(set(tam4ou5) - palavroes)
len(limpas), len(limpas) - 6**5
Out[13]:
Ainda temos 32
palavras a mais. Na diagramação que fiz depois, a palavra "mamum" causou problemas por ter 3 "m" e então fica mais larga que as demais. Vamos todas as palavras com mais de 2 "m":
In [14]:
import collections
mmm = []
for palavra in limpas:
if collections.Counter(palavra)['m'] > 1:
mmm.append(palavra)
mmm
Out[14]:
Ok, vamos tirar esta da lista:
In [15]:
limpas.remove('mamum')
len(limpas), len(limpas) - 6**5
Out[15]:
Ainda sobram 31
. Para ficar só com a quantidade certa eu embaralhei a lista limpas
e peguei uma fatia com as 6**5
palavras iniciais da lista.
In [16]:
import random
random.shuffle(limpas)
final = sorted(limpas[:6**5])
len(final), final[:10], final[-10:]
Out[16]:
Vamos conferir mais uma vez se temos a quantidade certa de palavras e não temos nenhuma repetida:
In [17]:
len(final), len(final) == len(set(final))
Out[17]:
Com a lista final
em mãos, agora falta só gerar as chaves de 11111
a 66666
para criar um índice fácil de usar com 5 dados de 6 faces.
In [18]:
import itertools
dados5 = list(''.join(dados) for dados in itertools.product('123456', repeat=5))
len(dados5)
Out[18]:
In [19]:
pares = list(zip(dados5, final))
pares[:10], pares[-10:]
Out[19]:
O último passo é escrever o arquivo 7776palavras.txt. Usando o comando wc
no shell confiro se o arquivo tem o número esperado de linhas:
In [20]:
with open('7776palavras.txt', 'wt', encoding='ascii') as saida:
for par in pares:
saida.write('%s %s\n' % par)
!wc '7776palavras.txt'
Carregando esse arquivo no LibreOffice, criei dois PDF: 18 páginas A4, 36 páginas A5.
Basta imprimir, juntar um dado de 6 faces e gerar senhas seguras sem usar o computador.