Como semear de forma sucinta, portátil e completa o PRNG mt19937?

112

Parece que vejo muitas respostas em que alguém sugere o uso <random>para gerar números aleatórios, geralmente junto com códigos como este:

std::random_device rd;  
std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(0, 5);
dis(gen);

Normalmente, isso substitui algum tipo de "abominação profana", como:

srand(time(NULL));
rand()%6;

Podemos criticar a maneira antiga argumentando que time(NULL)fornece baixa entropia, time(NULL)é previsível e o resultado final não é uniforme.

Mas tudo isso é verdade no novo jeito: ele apenas tem um verniz mais brilhante.

  • rd()retorna um único unsigned int. Isso tem pelo menos 16 bits e provavelmente 32. Isso não é suficiente para propagar os 19937 bits de estado do MT.

  • Usando std::mt19937 gen(rd());gen()(semeando com 32 bits e olhando para a primeira saída) não dá uma boa distribuição de saída. 7 e 13 nunca podem ser a primeira saída. Duas sementes produzem 0. Doze sementes produzem 1226181350. ( Link )

  • std::random_devicepode ser, e às vezes é, implementado como um PRNG simples com uma semente fixa. Portanto, pode produzir a mesma sequência em cada execução. ( Link ) Isso é ainda pior do que time(NULL).

Pior ainda, é muito fácil copiar e colar os trechos de código anteriores, apesar dos problemas que eles contêm. Algumas soluções para isso requerem a aquisição de bibliotecas maiores que podem não ser adequadas para todos.

Diante disso, minha pergunta é: Como alguém pode semear de forma sucinta, portátil e completa o PRNG mt19937 em C ++?

Dadas as questões acima, uma boa resposta:

  • Deve semear totalmente o mt19937 / mt19937_64.
  • Não pode contar apenas com std::random_deviceou time(NULL)como fonte de entropia.
  • Não deve contar com Boost ou outras bibliotecas.
  • Deve caber em um pequeno número de linhas, de forma que fique bem copiado e colado em uma resposta.

Pensamentos

  • Meu pensamento atual é que as saídas de std::random_devicepodem ser combinadas (talvez via XOR) com time(NULL)valores derivados da randomização do espaço de endereço e uma constante codificada (que pode ser definida durante a distribuição) para obter uma melhor chance de entropia.

  • std::random_device::entropy() não dá uma boa indicação do que std::random_devicepode ou não fazer.

Richard
fonte
24
@Fabien: O que há de portátil nisso? Esta é uma questão C ++, não uma questão Linux.
Lightness Races in Orbit
6
Meu pensamento pessoal era que talvez os valores pudessem ser extraídos de std::random_device, time(NULL)e endereços de função, e então XOR juntos para produzir uma espécie de fonte de entropia de melhor esforço.
Richard
5
Seria bom se houvesse uma função como does_random_device_actually_work () para que se pudesse pelo menos degradar normalmente, ou produzir avisos ou erros para o usuário.
4
A solução adequada não é curta, a solução curta não será adequada. Minha abordagem que uso em minha biblioteca seed11 é basicamente implementar std::random_deviceadequadamente nas plataformas em que você está planejando executar seu programa e fornecer uma função auxiliar que cria um gerador propagado ( seed11::make_seeded<std::mt19937>())
milleniumbug
5
À parte: seu segundo marcador não adiciona nada de novo. Não é surpreendente que você tenha encontrado algum valor que aparece 12 vezes. Você deve esperar que haja pouco mais de três valores que aparecem exatamente 12 vezes , assumindo que você tenha 2 ^ 32 amostras independentes e uniformemente aleatórias .

Respostas:

58

Eu argumentaria que a maior falha com std::random_deviceé que é permitido um fallback determinístico se nenhum CSPRNG estiver disponível. Isso por si só é uma boa razão para não propagar um PRNG usando std::random_device, uma vez que os bytes produzidos podem ser determinísticos. Infelizmente, não fornece uma API para descobrir quando isso acontece ou para solicitar falha em vez de números aleatórios de baixa qualidade.

Ou seja, não existe uma solução totalmente portátil : no entanto, existe uma abordagem decente e mínima. Você pode usar um envoltório mínimo em torno de um CSPRNG (definido como sysrandomabaixo) para propagar o PRNG.

janelas


Você pode confiar em CryptGenRandomum CSPRNG. Por exemplo, você pode usar o seguinte código:

bool acquire_context(HCRYPTPROV *ctx)
{
    if (!CryptAcquireContext(ctx, nullptr, nullptr, PROV_RSA_FULL, 0)) {
        return CryptAcquireContext(ctx, nullptr, nullptr, PROV_RSA_FULL, CRYPT_NEWKEYSET);
    }
    return true;
}


size_t sysrandom(void* dst, size_t dstlen)
{
    HCRYPTPROV ctx;
    if (!acquire_context(&ctx)) {
        throw std::runtime_error("Unable to initialize Win32 crypt library.");
    }

    BYTE* buffer = reinterpret_cast<BYTE*>(dst);
    if(!CryptGenRandom(ctx, dstlen, buffer)) {
        throw std::runtime_error("Unable to generate random bytes.");
    }

    if (!CryptReleaseContext(ctx, 0)) {
        throw std::runtime_error("Unable to release Win32 crypt library.");
    }

    return dstlen;
}

Unix-like


Em muitos sistemas semelhantes ao Unix, você deve usar / dev / urandom quando possível (embora não seja garantido que exista em sistemas compatíveis com POSIX).

size_t sysrandom(void* dst, size_t dstlen)
{
    char* buffer = reinterpret_cast<char*>(dst);
    std::ifstream stream("/dev/urandom", std::ios_base::binary | std::ios_base::in);
    stream.read(buffer, dstlen);

    return dstlen;
}

De outros


Se nenhum CSPRNG estiver disponível, você pode optar por confiar em std::random_device. No entanto, eu evitaria isso se possível, uma vez que vários compiladores (mais notavelmente, MinGW) o implementam como um PRNG (na verdade, produzindo a mesma sequência todas as vezes para alertar os humanos de que não é propriamente aleatório).

Semeando


Agora que temos nossas peças com overhead mínimo, podemos gerar os bits desejados de entropia aleatória para semear nosso PRNG. O exemplo usa 32 bits (obviamente insuficientes) para propagar o PRNG e você deve aumentar esse valor (que depende do seu CSPRNG).

std::uint_least32_t seed;    
sysrandom(&seed, sizeof(seed));
std::mt19937 gen(seed);

Comparação para Boost


Podemos ver paralelos com boost :: random_device (um verdadeiro CSPRNG) após uma rápida olhada no código-fonte . O Boost usa MS_DEF_PROVno Windows, que é o tipo de provedor para PROV_RSA_FULL. Falta apenas verificar o contexto criptográfico, o que pode ser feito com CRYPT_VERIFYCONTEXT. Em * Nix, Boost usa /dev/urandom. Ou seja, esta solução é portátil, bem testada e fácil de usar.

Especialização Linux


Se você está disposto a sacrificar a sucinta pela segurança, getrandomé uma excelente escolha no Linux 3.17 e superior e no Solaris recente. getrandomse comporta de forma idêntica /dev/urandom, exceto que bloqueia se o kernel ainda não inicializou seu CSPRNG após a inicialização. O snippet a seguir detecta se o Linux getrandomestá disponível e, caso não esteja, retorna ao /dev/urandom.

#if defined(__linux__) || defined(linux) || defined(__linux)
#   // Check the kernel version. `getrandom` is only Linux 3.17 and above.
#   include <linux/version.h>
#   if LINUX_VERSION_CODE >= KERNEL_VERSION(3,17,0)
#       define HAVE_GETRANDOM
#   endif
#endif

// also requires glibc 2.25 for the libc wrapper
#if defined(HAVE_GETRANDOM)
#   include <sys/syscall.h>
#   include <linux/random.h>

size_t sysrandom(void* dst, size_t dstlen)
{
    int bytes = syscall(SYS_getrandom, dst, dstlen, 0);
    if (bytes != dstlen) {
        throw std::runtime_error("Unable to read N bytes from CSPRNG.");
    }

    return dstlen;
}

#elif defined(_WIN32)

// Windows sysrandom here.

#else

// POSIX sysrandom here.

#endif

OpenBSD


Há uma advertência final: o OpenBSD moderno não tem /dev/urandom. Você deve usar getentropy em vez disso.

#if defined(__OpenBSD__)
#   define HAVE_GETENTROPY
#endif

#if defined(HAVE_GETENTROPY)
#   include <unistd.h>

size_t sysrandom(void* dst, size_t dstlen)
{
    int bytes = getentropy(dst, dstlen);
    if (bytes != dstlen) {
        throw std::runtime_error("Unable to read N bytes from CSPRNG.");
    }

    return dstlen;
}

#endif

outros pensamentos


Se você precisa de bytes aleatórios criptograficamente seguros, você provavelmente deve substituir o fstream pelo open / read / close sem buffer do POSIX. Isso ocorre porque basic_filebufe FILEcontém um buffer interno, que será alocado por meio de um alocador padrão (e, portanto, não apagado da memória).

Isso pode ser feito facilmente mudando sysrandompara:

size_t sysrandom(void* dst, size_t dstlen)
{
    int fd = open("/dev/urandom", O_RDONLY);
    if (fd == -1) {
        throw std::runtime_error("Unable to open /dev/urandom.");
    }
    if (read(fd, dst, dstlen) != dstlen) {
        close(fd);
        throw std::runtime_error("Unable to read N bytes from CSPRNG.");
    }

    close(fd);
    return dstlen;
}

obrigado


Agradecimentos especiais a Ben Voigt por apontar o FILEuso de leituras em buffer e, portanto, não deve ser usado.

Eu também gostaria de agradecer a Peter Cordes por mencionar getrandom, e a falta do OpenBSD /dev/urandom.

Alexander Huszagh
fonte
11
Isso é o que eu fiz no passado, mas a, ou pelo menos uma questão é WTF, os escritores de biblioteca para essas plataformas não podem fazer isso por nós? Espero que o acesso ao arquivo e os threads (por exemplo) sejam abstraídos por implementações de biblioteca, então por que não a geração de números aleatórios?
2
OP aqui: Seria bom se esta resposta demonstrasse a semeadura um pouco melhor. Tanto quanto possível, estou esperando por respostas que gerem código copiável e que faça o trabalho melhor do que o exemplo simples que postei em minha pergunta, sem exigir muita interpretação técnica ou reflexão por parte do codificador.
Richard
4
Achei que /dev/randomseria a melhor escolha para semear um RNG, mas aparentemente /dev/urandomainda é considerado computacionalmente seguro mesmo quando /dev/randombloquearia por causa da baixa entropia disponível, então urandomé a escolha recomendada para tudo, exceto talvez blocos únicos. Consulte também unix.stackexchange.com/questions/324209/… . No urandomentanto, tome cuidado com as sementes previsíveis logo após a inicialização.
Peter Cordes
2
A getrandom(2)chamada de sistema do Linux é como abrir e ler /dev/urandom, exceto que irá bloquear se as fontes de aleatoriedade do kernel ainda não foram inicializadas. Acho que isso o salva do problema de aleatoriedade de baixa qualidade da inicialização inicial sem bloquear em outros casos, como /dev/randomfaz.
Peter Cordes
1
@PeterCordes, com certeza, e essa é uma ótima opção quando disponível. No entanto, ele não funciona em BSD ou outros * Nixes, que /dev/urandomgeralmente funcionam. A discussão da lista de e-mails do Python sobre isso é algo que geralmente eu assino: bugs.python.org/issue27266
Alexander Huszagh
22

Em certo sentido, isso não pode ser feito de forma portátil. Ou seja, pode-se conceber uma plataforma totalmente determinística válida rodando C ++ (digamos, um simulador que acelera o relógio da máquina deterministicamente, e com E / S "determinada") na qual não há fonte de aleatoriedade para semear um PRNG.

einpoklum
fonte
1
@kbelder: 1. Quem disse que o usuário é uma pessoa? 2. Nem todos os programas têm interação com o usuário e você certamente não pode assumir que sempre há um usuário por perto ...
einpoklum
8
Agradeço esta resposta, mas também sinto que um programa deve fazer uma tentativa razoável de melhor esforço.
Richard
3
@Richard concorda, mas a questão é que os escritores do padrão C ++ precisam (ou pelo menos tentar o que têm de melhor para) acomodar esse tipo de situação bizarra. É por isso que você obtém esses tipos de definições padrão insossas, onde você pode obter resultados decentes, mas o compilador ainda pode ser compatível com os padrões, mesmo que devolva algo funcionalmente inútil. - Portanto, suas restrições ("curto e não pode depender de outras bibliotecas") excluem qualquer resposta, já que você efetivamente precisa de uma caixa especial plataforma por plataforma / compilador por compilador. (por exemplo, o que Boost faz tão bem.)
RM
2
@Richard, o que isso explica, entretanto, é que você obtém o que obtém no padrão porque não existe uma maneira portátil de fazer melhor. Se você quiser fazer melhor (que é um objetivo nobre), terá que aceitar uma quantidade maior ou menor de abominação :)
hobbs
1
@Richard: Às vezes você só tem que aceitar que é possível fazer uma implementação C ++ compatível com os padrões que não é útil. Já que as implementações que as pessoas usam para qualquer coisa importante são projetadas para serem úteis, às vezes você tem que conviver com argumentos como "qualquer implementação sã fará algo razoável". Eu esperava que std::random_deviceestivesse nessa categoria, mas aparentemente não é se algumas implementações reais usam um PRNG de semente fixa! Isso vai muito além do argumento de einpoklum.
Peter Cordes
14

Você pode usar um std::seed_seqe preenchê-lo até pelo menos o tamanho de estado necessário para o gerador usando o método de Alexander Huszagh para obter a entropia:

size_t sysrandom(void* dst, size_t dstlen); //from Alexander Huszagh answer above

void foo(){

    std::array<std::mt19937::UIntType, std::mt19937::state_size> state;
    sysrandom(state.begin(), state.length*sizeof(std::mt19937::UIntType));
    std::seed_seq s(state.begin(), state.end());

    std::mt19937 g;
    g.seed(s);
}

Se houvesse uma maneira adequada de preencher ou criar um SeedSequence a partir de um UniformRandomBitGenerator na biblioteca padrão, o uso std::random_devicepara propagação correta seria muito mais simples.

aberração da catraca
fonte
Não há nada no padrão C ++ ou em qualquer lugar que garanta que o gerador de números aleatórios usará todo o array quando você semear a partir de seed_seq. Este método levará ao fracasso se você estiver usando o rng para uma simulação científica e, obviamente, também para criptografia. Praticamente o único caso de uso para isso será randomizar um videogame, mas aí seria um exagero.
Kostas
5

A implementação na qual estou trabalhando aproveita a state_sizepropriedade do mt19937PRNG para decidir quantas sementes fornecer na inicialização:

using Generator = std::mt19937;

inline
auto const& random_data()
{
    thread_local static std::array<typename Generator::result_type, Generator::state_size> data;
    thread_local static std::random_device rd;

    std::generate(std::begin(data), std::end(data), std::ref(rd));

    return data;
}

inline
Generator& random_generator()
{
    auto const& data = random_data();

    thread_local static std::seed_seq seeds(std::begin(data), std::end(data));
    thread_local static Generator gen{seeds};

    return gen;
}

template<typename Number>
Number random_number(Number from, Number to)
{
    using Distribution = typename std::conditional
    <
        std::is_integral<Number>::value,
        std::uniform_int_distribution<Number>,
        std::uniform_real_distribution<Number>
    >::type;

    thread_local static Distribution dist;

    return dist(random_generator(), typename Distribution::param_type{from, to});
}

Eu acho que há espaço para melhorias porque std::random_device::result_typepodem ser diferentes std::mt19937::result_typeem tamanho e alcance, então isso realmente deve ser levado em consideração.

Uma nota sobre std :: random_device .

De acordo com o C++11(/14/17)(s) padrão (ões):

26.5.6 Classe random_device [ rand.device ]

2 Se as limitações de implementação impedirem a geração de números aleatórios não determinísticos, a implementação pode empregar um mecanismo de número aleatório.

Isso significa que a implementação só pode gerar valores determinísticos se for impedida de gerar valores não determinísticos por alguma limitação.

O MinGWcompilador Windowsnotoriamente não fornece valores não determinísticos de seu std::random_device, apesar de estarem facilmente disponíveis no sistema operacional. Portanto, considero isso um bug e provavelmente não uma ocorrência comum em implementações e plataformas.

Galik
fonte
1
Isso pode preencher o estado de MT, mas ainda depende exclusivamente dele std::random_devicee, portanto, é vulnerável a problemas decorrentes dele.
Richard
1
Acho que os declarei com bastante clareza na pergunta. Fico feliz em esclarecer / discutir, no entanto.
Richard
2
@Richard Existe algum sistema real que não implementa realmente um razoável std::random_device? Eu sei que o padrão permite um PRNGretrocesso, mas sinto que é apenas para se proteger, pois é difícil exigir que cada dispositivo que usa C++tenha uma fonte aleatória não determinística. E se não o fizerem, o que você poderia fazer a respeito?
Galik
5
@AlexanderHuszagh Não tenho tanta certeza. Minha intenção é tornar minha "solução portátil" dependente do dispositivo, porque se o dispositivo suporta geradores não determinísticos, então deveria std::random_device. Eu acredito que esse é o espírito do padrão. Então, eu procurei e só consigo encontrar MinGWque está quebrado nesse aspecto. Ninguém parece estar relatando esse problema com qualquer outra coisa que eu encontrei. Portanto, em minha biblioteca, simplesmente marquei MinGWcomo não compatível. Se houvesse um problema mais amplo, eu o repensaria. Eu simplesmente não vejo a evidência disso agora.
Galik
5
Estou realmente desapontado que o MinGW está arruinando std::random_devicepara todos ao torná-lo disponível em um formato que não oferece os recursos de aleatoriedade da plataforma. Implementações de baixa qualidade anulam o propósito da API existente. Seria melhor IMO se eles simplesmente não implementassem até que eles estivessem funcionando. (Ou melhor, se a API fornecesse uma maneira de solicitar falha se a aleatoriedade de alta qualidade não estivesse disponível, então o MinGW poderia evitar causar riscos de segurança enquanto ainda fornecia sementes diferentes para jogos ou qualquer outra coisa.)
Peter Cordes
2

Não há nada de errado em semear usando o tempo, supondo que você não precise dele para ser seguro (e você não disse que isso era necessário). A ideia é que você pode usar o hash para corrigir a não aleatoriedade. Descobri que isso funciona adequadamente em todos os casos, incluindo e em particular para simulações pesadas de Monte Carlo.

Um bom recurso dessa abordagem é que ela generaliza para a inicialização a partir de outros conjuntos de sementes não realmente aleatórios. Por exemplo, se você deseja que cada thread tenha seu próprio RNG (para threadsafety), você pode apenas inicializar com base no ID do thread em hash.

O seguinte é um SSCCE , destilado da minha base de código (para simplificar; algumas estruturas de suporte OO eliminadas):

#include <cstdint> //`uint32_t`
#include <functional> //`std::hash`
#include <random> //`std::mt19937`
#include <iostream> //`std::cout`

static std::mt19937 rng;

static void seed(uint32_t seed) {
    rng.seed(static_cast<std::mt19937::result_type>(seed));
}
static void seed() {
    uint32_t t = static_cast<uint32_t>( time(nullptr) );
    std::hash<uint32_t> hasher; size_t hashed=hasher(t);
    seed( static_cast<uint32_t>(hashed) );
}

int main(int /*argc*/, char* /*argv*/[]) {
    seed();
    std::uniform_int_distribution<> dis(0, 5);
    std::cout << dis(rng);
}
Imallett
fonte
1
Eu concordo com seu ponto de que semear com o tempo provavelmente é bom o suficiente na prática, se você não precisar que seja seguro. Mas não posso concordar com o resto da sua resposta. Semear com o hash do tempo não é melhor do que semear com o próprio tempo.
DW
@DW Empiricamente, é muito melhor. A razão é que o hash é descontínuo e abrange uma faixa muito mais ampla de valores (tente você mesmo: semeie com 1e 2e observe que a sequência de flutuações gerada por eles leva um tempo para realmente divergir).
imallett
Não vejo por que isso importa. Estamos executando apenas uma única semente por vez. O espaço de valores possíveis para a semente (a entropia da semente) é o mesmo de qualquer maneira - o hashing não aumenta a entropia. Talvez você possa editar a pergunta para explicar por que o hash é melhor.
DW
0

Aqui está minha própria tentativa de resolver a questão:

#include <random>
#include <chrono>
#include <cstdint>
#include <algorithm>
#include <functional>
#include <iostream>

uint32_t LilEntropy(){
  //Gather many potential forms of entropy and XOR them
  const  uint32_t my_seed = 1273498732; //Change during distribution
  static uint32_t i = 0;        
  static std::random_device rd; 
  const auto hrclock = std::chrono::high_resolution_clock::now().time_since_epoch().count();
  const auto sclock  = std::chrono::system_clock::now().time_since_epoch().count();
  auto *heap         = malloc(1);
  const auto mash = my_seed + rd() + hrclock + sclock + (i++) +
    reinterpret_cast<intptr_t>(heap)    + reinterpret_cast<intptr_t>(&hrclock) +
    reinterpret_cast<intptr_t>(&i)      + reinterpret_cast<intptr_t>(&malloc)  +
    reinterpret_cast<intptr_t>(&LilEntropy);
  free(heap);
  return mash;
}

//Fully seed the mt19937 engine using as much entropy as we can get our
//hands on
void SeedGenerator(std::mt19937 &mt){
  std::uint_least32_t seed_data[std::mt19937::state_size];
  std::generate_n(seed_data, std::mt19937::state_size, std::ref(LilEntropy));
  std::seed_seq q(std::begin(seed_data), std::end(seed_data));
  mt.seed(q);
}

int main(){
  std::mt19937 mt;
  SeedGenerator(mt);

  for(int i=0;i<100;i++)
    std::cout<<mt()<<std::endl;
}

A ideia aqui é usar XOR para combinar muitas fontes potenciais de entropia (tempo rápido, tempo lento, std::random-devicelocais de variáveis ​​estáticas, locais de heap, locais de função, locais de biblioteca, valores específicos do programa) para fazer um melhor esforço na tentativa de inicializar o mt19937. Contanto que pelo menos uma vez a fonte seja "boa", o resultado será pelo menos "bom".

Essa resposta não é tão curta quanto seria preferível e pode conter um ou mais erros de lógica. Portanto, estou considerando um trabalho em andamento. Por favor, comente se você tiver feedback.

Richard
fonte
3
Os endereços podem ter muito pouca aleatoriedade. Você sempre tem as mesmas alocações, portanto, em sistemas embarcados menores, onde você tem acesso a toda a memória, é provável que obtenha os mesmos resultados todas as vezes. Eu diria que provavelmente é bom o suficiente para um grande sistema, mas pode fazer merda em um microcontrolador.
meneldal
1
Eu diria que &i ^ &myseeddeveria ter consideravelmente menos entropia do que qualquer um deles sozinho, uma vez que ambos são objetos com duração de armazenamento estático na mesma unidade de tradução e, portanto, provavelmente estão bem próximos. E você não parece realmente usar o valor especial da inicialização de myseed?
aschepler
7
Converter ponteiros desalocados em ints é um comportamento indefinido; faça enquanto ainda existe. ^é um combinador de hash horrível; se dois valores tiverem muita entropia, mas pouco comparados um ao outro, ele a remove. +geralmente é melhor (já que x + x queima apenas 1 bit de entropia em x, enquanto x ^ x queima todos eles). A função não é segura, suspeito ( rd())
Yakk - Adam Nevraumont
2
Ah, e com +isso quero dizer sem sinal ( +com sinalizado é UB-isca). Embora esses sejam casos UB um tanto ridículos, você disse portátil. Considere também obter o endereço de uma função como um valor integral, se possível (incerto se for?)
Yakk - Adam Nevraumont
1
@meneldal: Mesmo em um PC de força total, embora as alocações possam obter diferentes locais físicos (dependendo do estado da máquina externa ao processo), os ponteiros são abstraídos pelo espaço de endereço virtual do processo e provavelmente altamente repetíveis, particularmente é ASLR não está em vigor.
Ben Voigt
0
  • Use getentropy () para propagar um gerador de números pseudo-aleatórios (PRNG).
  • Use getrandom () se quiser valores aleatórios (em vez de, digamos, /dev/urandomou /dev/random).

Eles estão disponíveis em sistemas modernos do tipo UNIX, como Linux, Solaris e OpenBSD.

Dan Anderson
fonte
-2

Uma determinada plataforma pode ter uma fonte de entropia, como /dev/random. Nanossegundos desde a época com std::chrono::high_resolution_clock::now()é provavelmente a melhor semente na Biblioteca Padrão.

Eu já usei algo como (uint64_t)( time(NULL)*CLOCKS_PER_SEC + clock() )obter mais bits de entropia para aplicativos que não são críticos para a segurança.

Davislor
fonte
2
Você realmente deve usar /dev/urandom, especialmente em um caso como este. /dev/randomblocos, e muitas vezes sem boas razões para fazê-lo ([insira uma longa explicação sobre quantos sistemas operacionais diferentes estimam a aleatoriedade dos bytes produzidos por / dev / random]).
Alexander Huszagh
2
@AlexanderHuszagh Verdade, embora eu tenha tido que codificar em sistemas onde /dev/urandomnão existiam, e a alternativa ao bloqueio era o determinismo. Uma caixa pode ter /dev/hwrngou /dev/hw_randomtambém, o que deve ser ainda melhor.
Davislor
Ok, eu disse "como /dev/random" e isso parece ter desencadeado uma guerra santa sobre o /dev/randomcontra /dev/urandomno Linux que eu não pretendia quando dei aquele exemplo ..
Davislor