A leitura de / dev / random não produz nenhum dado

19

Costumo usar o comando

cat /dev/urandom | strings --bytes 1 | tr -d '\n\t ' | head --bytes 32

para gerar senhas pseudo-aleatórias. Isso não funciona /dev/random.

Especificamente

  • cat /dev/urandom | strings --bytes 1 | tr -d '\n\t ' produz saída
  • cat /dev/random | strings --bytes 1 produz saída
  • cat /dev/random | strings --bytes 1 | tr -d '\n\t ' não produz saída

Nota: Ao usar, /dev/randomvocê pode ter que mexer o mouse ou pressionar as teclas (por exemplo, ctrl, shift, etc.) para gerar entropia.

Por que o último exemplo não funciona? Tem tralgum tipo de buffer interno grande que /dev/urandompreenche rapidamente, mas /dev/randomnão tem?

PS estou usando o CentOS 6.5

cat /proc/version
Linux version 2.6.32-431.3.1.el6.x86_64 ([email protected]) (gcc version 4.4.7 20120313 (Red Hat 4.4.7-4) (GCC) ) #1 SMP Fri Jan 3 21:39:27 UTC 2014
Aaron J Lang
fonte
qual é a sua distribuição, sua versão do kernel? no Cygwin, ambos retornam valores.
Kiwy
@Kiwy Ver edição.
Aaron J Lang
1
Você sabe pwgen, em particular pwgen -s?
MvG 12/02
2
A -smudança os torna menos memoráveis, mais verdadeiramente aleatórios. @Boyd: é makepasswd amplamente disponíveis além das distros baseadas em Debian? Do meu ponto de vista, o pwgen está disponível para o CentOS, enquanto o makepasswd não .
MvG 12/02
1
@BoydStephenSmithJr. Concordo com @ MvG que makepasswdnão está disponível em minha plataforma, obrigado de qualquer maneira
Aaron J Lang

Respostas:

27

Eventualmente.

Em:

cat /dev/random | strings --bytes 1 | tr -d '\n\t '

cat nunca será armazenado em buffer, mas é supérfluo de qualquer maneira, pois não há nada para concatenar aqui.

< /dev/random strings --bytes 1 | tr -d '\n\t '

stringsno entanto, como sua saída não é mais um terminal, sua saída será armazenada em buffer por blocos (de algo como 4 ou 8kB) em oposição a linhas quando a saída for para um terminal.

Portanto, ele só começará a escrever no stdout quando tiver acumulado 4kB de caracteres na saída, o que /dev/randomdemorará um pouco.

trA saída vai para um terminal (se você estiver executando isso em um prompt de shell em um terminal), portanto, ela armazenará sua saída em buffer. Como você está removendo o \n, ele nunca terá uma linha completa para gravar; portanto, ele gravará assim que um bloco completo for acumulado (como quando a saída não for para um terminal).

Portanto, tré provável que você não escreva nada até stringster lido o suficiente /dev/randompara escrever 8kB (2 blocos possivelmente muito mais) de dados (já que o primeiro bloco provavelmente conterá alguns caracteres de nova linha ou tabulação ou espaço).

Neste sistema em que estou testando, posso obter uma média de 3 bytes por segundo /dev/random(em vez de 12MiB /dev/urandom), portanto, no melhor cenário (os primeiros 4096 bytes /dev/randomsão todos imprimíveis), estamos falando 22 minutos antes trcomeça a produzir qualquer coisa. Mas é mais provável que demore horas (em um teste rápido, posso ver a stringsgravação de um bloco a cada 1 ou 2 blocos lidos, e os blocos de saída contêm cerca de 30% dos caracteres de nova linha, então eu esperaria que fosse necessário ler pelo menos 3 blocos antes trtem 4096 caracteres para saída).

Para evitar isso, você pode fazer:

< /dev/random stdbuf -o0 strings --bytes 1 | stdbuf -o0 tr -d '\n\t '

stdbuf é um comando GNU (também encontrado em alguns BSDs) que altera o buffer stdio de comandos por meio de um truque LD_PRELOAD.

Observe que, em vez de strings, você pode usar o tr -cd '[:graph:]'que também excluirá tabulação, nova linha e espaço.

Convém também corrigir o código do idioma Cpara evitar possíveis surpresas futuras com caracteres UTF-8.

Stéphane Chazelas
fonte
uau explicação impressionante.
Kiwy
2
Surpreendente! Ótima explicação e solução. Eu sempre usei cat'inutilmente' porque nunca gostei de redirecionar o stdin no final de um pipeline, agora posso 'salvar um processo' e ainda ter comandos legíveis. Minha solução final foi< /dev/random stdbuf -o0 tr -Cd '[:graph:]' | stdbuf -o0 head --bytes 32
Aaron J Lang
@AaronJLang, bom argumento [:graph:]. Eu tinha esquecido disso.
Stéphane Chazelas
@AaronJLang, você não precisa do stdbufpara head -c32a menos que você deseja permitir que ele para gravar os dados assim que o tem (como em vários pedaços em vez de um pedaço 32byte assim que ele está com eles)
Stéphane Chazelas
2
Na minha opinião, / dev / urandom é amplamente suficiente para o uso do autor. Se alguém tiver curiosidade de saber como, especificamente, o urandom funciona comparado ao aleatório, sugiro ler os comentários na parte superior do driver em drivers / char / random.c da árvore de fontes do kernel. Os comentários mencionam uma análise do PRNG e sua implementação. Felizmente, este artigo responderá à pergunta "o quão aleatório ou aleatório é o aleatório comparado ao aleatório?" Disponível aqui: eprint.iacr.org/2012/251.pdf
etherfish
5

A geração de números aleatórios para muitos aplicativos de segurança requer entropia suficiente - medidas de entropia quão imprevisível é a aleatoriedade. Um processador determinístico não pode gerar entropia; portanto, a entropia deve vir de fora - de um componente de hardware com comportamento não determinístico ou de outros fatores suficientemente difíceis de reproduzir, como o tempo das ações do usuário (é aí que mexe o mouse entra). Uma vez que a entropia suficiente estiver disponível, a criptografia pode ser usada para gerar um fluxo praticamente ilimitado de números aleatórios.

O Linux trabalha acumulando entropia em um pool e depois usando criptografia para produzir números aleatórios aceitáveis ​​através /dev/randome /dev/urandom. A diferença é que /dev/randomse aplica um cálculo de entropia extremamente conservador que reduz a estimativa da entropia no pool para cada byte gerado, enquanto /dev/urandomque não se preocupa com a quantidade de entropia no pool.

Se a estimativa de entropia no pool for muito baixa, /dev/randombloqueia até que mais entropia possa ser acumulada. Isso pode prejudicar gravemente a taxa na qual /dev/randompode produzir saída. É isso que você está observando aqui. Não tem nada a ver com tr; mas stringslê a saída com buffer, portanto, ele precisa ler um buffer completo (alguns KB) /dev/randomapenas para produzir pelo menos um byte de entrada.

/dev/urandomé perfeitamente aceitável para gerar uma chave criptográfica , porque a entropia não diminui de maneira perceptível. (Se você mantiver sua máquina funcionando por mais tempo do que o universo existia, não poderá negligenciar essas considerações, mas, caso contrário, você será bom.) Há apenas um caso em que /dev/urandomnão é bom: um sistema recém-instalado que não possui ainda não tinha tempo para gerar entropia ou um sistema recém-inicializado que inicializa a partir de mídia somente leitura.

Eliminar stringsda sua cadeia de inicialização provavelmente acelerará seu processo. Abaixo tr, filtraremos os caracteres não imprimíveis:

</dev/random LC_ALL=C tr -dc '!-~'

Mas você pode usar /dev/urandomaqui , desde que tome cuidado para não gerar senhas em um sistema que não teve tempo para acumular entropia suficiente. Você pode verificar o nível do pool de entropia do Linux /proc/sys/kernel/random/entropy_avail(se você usar /dev/random, a figura neste arquivo será conservadora, possivelmente muito).

Gilles 'SO- parar de ser mau'
fonte
1

Você deve usar /dev/urandompara obter números aleatórios (pseudo) de alta qualidade e /dev/randomsomente quando precisar absolutamente de números aleatórios realmente imprevisíveis. Um invasor abaixo dos recursos da NSA terá muita dificuldade para quebrar /dev/urandom(e não se esqueça da criptografia de mangueira de borracha ). O kernel preenche um buffer com bytes "realmente aleatórios", é isso que /dev/randomdá. Infelizmente, a taxa na qual esses são gerados é baixa, portanto, ler muito a partir de /dev/random vai parar de esperar pela aleatoriedade.

Você pode considerar o uso do random.org ou seu gerador de senhas , ou um dos muitos geradores aleatórios de senhas que estão flutuando, dê uma olhada, por exemplo, nesta página para obter algumas dicas de linha de comando (não que eu recomende todas elas , mas eles devem lhe dar idéias) ou você pode usar algo como mkpasswd(1)(aqui na parte do Fedora 19 expect-5.45-8.fc19.x86_64).

vonbrand
fonte