Por que o dd de / dev / random fornece tamanhos de arquivo diferentes?

26

Estou executando o seguinte comando em um sistema ubuntu:

dd if=/dev/random of=rand bs=1K count=2

No entanto, toda vez que eu o executo, acabo com um arquivo de tamanho diferente. Por que é isso? Como posso gerar um arquivo de um determinado tamanho preenchido com dados aleatórios?

Daniel
fonte
11
/dev/randombloqueará se não houver entropia suficiente disponível para gerar o número de dígitos desejado. simplesmente leva tempo para reunir essa quantidade de "aleatoriedade" aleatória psuedo de alta qualidade ... Use /dev/urandomum valor "aleatório" menos aleatório ou verifique seu pool de entropia (em um loop e aguarde conforme necessário) ...
Pedro.Of
Veja também esta questão .
29412 Keith Thompson
3
basta adicionariflag=fullblock
frostschutz 23/03

Respostas:

31

Você está observando uma combinação do comportamento peculiar de ddcom o comportamento peculiar do Linux /dev/random. Ambos, a propósito, raramente são a ferramenta certa para o trabalho.

O Linux /dev/randomretorna dados com moderação. É baseado na suposição de que a entropia no gerador de números pseudo-aleatórios é extinta a uma taxa muito rápida. Como a coleta de nova entropia é lenta, /dev/randomnormalmente renuncia apenas a alguns bytes de cada vez.

ddé um programa antigo e irritadiço, inicialmente destinado a operar em dispositivos de fita. Quando você diz para ler um bloco de 1kB, ele tenta ler um bloco. Se a leitura retornar menos de 1024 bytes, é difícil, é tudo o que você obtém. Então dd if=/dev/random bs=1K count=2faz duas read(2)ligações. Como está lendo /dev/random, as duas readchamadas normalmente retornam apenas alguns bytes, em número variável, dependendo da entropia disponível. Consulte também Quando o dd é adequado para copiar dados? (ou, quando são lidos () e gravados () parciais)

A menos que você esteja projetando um instalador ou clonador do sistema operacional, nunca deve usar o /dev/randomLinux sempre /dev/urandom. A urandompágina de manual é um pouco enganadora; /dev/urandomé de fato adequado para criptografia, mesmo para gerar chaves de vida longa. A única restrição /dev/urandomé que ele deve ser fornecido com entropia suficiente; As distribuições Linux normalmente salvam a entropia entre as reinicializações, portanto, o único momento em que você pode não ter entropia suficiente é em uma nova instalação. A entropia não se esgota em termos práticos. Para obter mais informações, leia Um rand de / dev / urandom é seguro para uma chave de login? e Alimentação / dev / pool de entropia aleatória? .

A maioria dos usos de ddé melhor expressa com ferramentas como headou tail. Se você quiser 2kB de bytes aleatórios, execute

head -c 2k </dev/urandom >rand

Com os kernels Linux mais antigos, você pode se safar

dd if=/dev/urandom of=rand bs=1k count=2

porque /dev/urandomfelizmente retornou quantos bytes foram solicitados. Mas isso não é mais verdade desde o kernel 3.16, agora é limitado a 32 MB .

Em geral, quando você precisa usar ddpara extrair um número fixo de bytes e sua entrada não é proveniente de um arquivo regular ou dispositivo de bloco, você precisa ler byte a byte: dd bs=1 count=2048.

Gilles 'SO- parar de ser mau'
fonte
Obrigado pela dica de usar a cabeça em vez de dd. Isso me permite ainda usar / dev / random, se eu quiser. Embora o / dev / urandom provavelmente seja suficiente como você mencionou, é bom saber como usar o / dev / random, se necessário.
Daniel
em kernels desde 3,16 /dev/urandom retorna 32m porread() .
mikeserv
Como alternativa, se você precisar de um comando compatível com POSIX, poderá usar o truque aqui: unix.stackexchange.com/a/192114 dd if=/dev/urandom ibs=1k obs=1k | dd bs=1k count=2
Rufflewind
11

Em man 4 randomuma caixa RHEL 5:

Quando lido, o dispositivo / dev / random retornará apenas bytes aleatórios dentro do número estimado de bits de ruído no pool de entropia.

Eu recebo arquivos de tamanho 213 bytes nessa máquina. Voltar ao man 4 random:

Quando lido, o dispositivo / dev / urandom retornará quantos bytes forem solicitados.

Eu recebo 2048 bytes de cada chamada de dd if=/dev/urandom of=rand bs=1K count=2

Concluo que a diferença se deve à quantidade de entropia que sua máquina gera entre invocações de dd if=/dev/random ...

Bruce Ediger
fonte
Sim, praticamente, a menos que ele esteja em um aplicativo de criptografia real, o @Daniel deve usar / dev / urandom. Mas estou confuso sobre o porquê de dd if=/dev/random bs=1K count=2parar quando a piscina de entropia é aparentemente drenada. Nos documentos, ele deve bloquear até que haja mais entropia, para ddque o arquivo seja gravado lentamente, em vez de apenas despejar o pool atual e sair.
cjc
Eu me perguntei sobre isso também, mas é consistente no RHEL, Slackware 13.1 e um arco bastante atual. O RHEL era x86_64, os outros eram de 32 bits. Infelizmente, os documentos do dd estão no formato de informação GNU, por isso não li todos.
Bruce Ediger
Também é consistente no Gentoo também.
Matthew Scharley 29/02
4
@cjc: é porque quando você chama read(fd, mybuf, 1024)um FD de bloqueio, ele retorna assim que o dispositivo subjacente retorna alguns dados. Se houver 1024 bytes para serem lidos, isso retornará. Se houver apenas 201 bytes, ele retornará 201. Se houver 0 bytes disponíveis, ele bloqueará até que pelo menos um byte seja disponibilizado e retorne-os.
Warren Young
@WarrenYoung a leitura de / dev / random drena seu conteúdo? Eu presumo que sim.
Michael Martinez
5

Por que ddsoltar dados? ... Gilles fez esta pergunta interessante sobre dd:
quando o dd é adequado para copiar dados? (ou, quando são lidos () e escritos () parciais)
Aqui está um trecho dessa pergunta:

    * ... não é difícil colocar dd em falha; por exemplo, tente este código: **
        yes | dd of=out bs=1024k count=10
    e verifique o tamanho do arquivo de saída (é provável que esteja bem abaixo de 10 MB).


Além do meu comentário (no final da sua pergunta), algo assim é muito interessante de assistir ... Ele captura seus bytes no arquivo $trnd. Escolhi semi-arbitrariamente bs = 8

Mova o mouse e observe-o acelerar.
Com o meu computador ocioso (AFK e nenhuma atividade de rede) e depois de esgotar o pool de entropia, demorou 2 horas e 12 minutos para coletar apenas 1192 bytes, momento em que o cancelei.

Então, comigo movendo o mouse continuamente, foram necessários 1 minuto e 15 segundos relativamente mais curtos para coletar o mesmo número de bytes.

Isso mostra claramente que a coleta de entropia não se baseia na velocidade da CPU, mas sim em eventos aleatórios , e que meu sistema Ubuntu usa o mouse como um dos fatores aleatórios significativos .

get=2048
trnd=/tmp/$USER.rnd; >"$trnd"
while (( $(wc -c <"$trnd") < $get )) ;do
    dd if=/dev/random bs=8 count=1 2>/dev/null >>"$trnd"
    echo -n "itt: $((i+=1))  ct: "; wc -c <"$trnd"
done
truncate -s $get "$trnd"
echo -e "\nfinal count: "; wc -c <"$trnd"
Peter.O
fonte
1

ddfoi projetado para bloqueio - geralmente é a melhor ferramenta à sua disposição para leitura de entradas de tamanho variável, se você precisar fazer isso imediatamente, porque ddnão armazenará as leituras atuais em algum futuro write() (a menos que você a configure explicitamente dessa maneira com mais obs do que ibs) , mas fará write()tudo o que ler assim que read()for (e, opcionalmente, o processará) .

Aqui estão algumas definições importantes :

  • ibs=expr
    • Especifique o tamanho do bloco de entrada, em bytes, por (o padrão é 512) .expr
  • obs=expr
    • Especifique o tamanho do bloco de saída, em bytes, por (o padrão é 512) .expr
  • bs=expr
    • Defina os tamanhos dos blocos de entrada e saída como exprbytes, substituindo ibs=e obs=. Se nenhuma conversão diferente de sync, noerrore notruncfor especificada, cada bloco de entrada deve ser copiado para a saída como um único bloco sem agregar blocos curtos.

Então você vê, quando ibse obsé definido em conjunto, como bsentão ibstem precedência - mas, caso contrário, se você é específico, pode obsou cbsnão.

Aqui está um exemplo em que ibsé mais importante. Você pode fazer algo assim se quiser acompanhar quanto tempo a /dev/randompiscina enche ...

dd "ibs=$size" conv=sync "count=$lmt" \ 
    if=/dev/random of="$somefile"

Enquanto if='s meta é legível em tudo, que vai sempre resultar no mesmo arquivo de saída de tamanho, porque ddvai synchronize blocos leia-in on nulos. Em outras palavras, se dd read()s para um bloco de entrada de $((size=10)) $((count=5))vezes e o read()arquivo retornar 2 bytes, 8 bytes, 12 bytes, 2 bytes e 4 bytes, ddgravarão em seu arquivo externo algo como

 2 read bytes 8NULs \
 8 read bytes 2NULs \
10 read bytes 0NULs \
 4 read bytes 6NULs \
 4 read bytes 6NULs

... porque dd, por padrão, não demora. Portanto, se você precisar acompanhar o fluxo e delimitar as gravações de algum outro processo, ddé a ferramenta para você.

Se você estiver gravando apenas uma quantidade de dados em um arquivo regular, ao contrário de outras declarações feitas aqui, também poderá usá dd-lo - e com bastante facilidade - mas precisará de mais de um e um fator de bloqueio confiável .

Por exemplo, se você fez:

{   dd ibs="$size" obs="${size}x$block_factor" |
    dd bs="${size}x$blockfactor" "count=$lmt"
}  <infile >outfile

... o primeiro ddarmazenaria em buffer quantos ibs="$size"blocos de entrada fossem necessários para preencher pelo menos um obs="${size}x$block_factor"bloco de saída para cada write()canal no tubo entre ele e o segundo dd. Isso significa que o segundo ddpode limitar a saída de forma confiável, count="$lmt"porque todos os write()s que o primeiro faz corresponderão ao seu tamanho de bloco de E / S - independentemente de quantos read()s o primeiro dddeve fazer para fazê-lo.

E que é assim que você pode usar ddpara tubos confiável ler ou outros tipos de arquivos especiais - com apenas um pouco de matemática.

mikeserv
fonte