Comment semer le mt19937 PRNG de manière succincte, portable et complète?

112

Il me semble voir de nombreuses réponses dans lesquelles quelqu'un suggère d'utiliser <random>pour générer des nombres aléatoires, généralement avec un code comme celui-ci:

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

Habituellement, cela remplace une sorte d '«abomination impie» telle que:

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

Nous pourrions critiquer l'ancienne méthode en affirmant qu'elle time(NULL)fournit une faible entropie, qu'elle time(NULL)est prévisible et que le résultat final n'est pas uniforme.

Mais tout cela est vrai de la nouvelle façon: il a juste un placage plus brillant.

  • rd()renvoie un seul unsigned int. Cela a au moins 16 bits et probablement 32. Cela ne suffit pas pour amorcer les bits d'état 19937 de MT.

  • Utiliser std::mt19937 gen(rd());gen()(ensemencer avec 32 bits et regarder la première sortie) ne donne pas une bonne distribution de sortie. 7 et 13 ne peuvent jamais être la première sortie. Deux graines produisent 0. Douze graines produisent 1226181350. ( Lien )

  • std::random_devicepeut être, et est parfois, implémenté comme un simple PRNG avec une graine fixe. Il peut donc produire la même séquence à chaque exécution. ( Lien ) C'est encore pire que time(NULL).

Pire encore, il est très facile de copier et coller les extraits de code précédents, malgré les problèmes qu'ils contiennent. Certaines solutions à cela nécessitent l'acquisition de grandes bibliothèques qui peuvent ne pas convenir à tout le monde.

À la lumière de cela, ma question est: Comment peut-on semer succinctement, de manière portable et complète le PRNG mt19937 en C ++?

Compte tenu des problèmes ci-dessus, une bonne réponse:

  • Doit semer complètement le mt19937 / mt19937_64.
  • Ne peut pas compter uniquement sur std::random_deviceou time(NULL)comme source d'entropie.
  • Ne devrait pas compter sur Boost ou d'autres bibliothèques.
  • Doit tenir dans un petit nombre de lignes de sorte que cela semble bien copié-collé dans une réponse.

Pensées

  • Ma pensée actuelle est que les sorties de std::random_devicepeuvent être mélangées (peut-être via XOR) avec des time(NULL)valeurs dérivées de la randomisation de l'espace d'adressage et une constante codée en dur (qui pourrait être définie lors de la distribution) pour obtenir un meilleur effort d'entropie.

  • std::random_device::entropy() ne donne pas une bonne indication de ce qui std::random_devicepourrait ou ne pourrait pas faire.

Richard
la source
24
@Fabien: Qu'est-ce qui est portable à ce sujet? C'est une question C ++, pas une question Linux.
Courses de légèreté en orbite le
6
Ma pensée personnelle était que peut-être des valeurs pourraient être tirées de std::random_device, time(NULL)et des adresses de fonction, puis XORed ensemble pour produire une sorte de source d'entropie au mieux.
Richard
5
Ce serait bien s'il y avait une fonction telle que does_random_device_actually_work () afin que l'on puisse au moins dégrader gracieusement, ou produire des avertissements ou des erreurs pour l'utilisateur.
4
La solution appropriée n'est pas courte, la solution courte ne sera pas appropriée. Mon approche que j'utilise dans ma bibliothèque seed11 consiste essentiellement à implémenter std::random_devicecorrectement sur les plates-formes sur lesquelles vous prévoyez d'exécuter votre programme et à fournir une fonction d'assistance qui crée un générateur prédéfini ( seed11::make_seeded<std::mt19937>())
milleniumbug
5
A part: votre deuxième puce n'ajoute rien de nouveau. Il n'est pas surprenant que vous ayez trouvé une valeur qui apparaît 12 fois. Vous devez vous attendre à ce qu'il y ait un peu plus de trois valeurs qui apparaissent exactement 12 fois , en supposant que vous avez 2 ^ 32 échantillons indépendants et uniformément aléatoires .

Réponses:

58

Je dirais que le plus grand défaut std::random_deviceest le fait qu'il est autorisé à un repli déterministe si aucun CSPRNG n'est disponible. Ceci seul est une bonne raison de ne pas amorcer un PRNG en utilisant std::random_device, car les octets produits peuvent être déterministes. Il ne fournit malheureusement pas d'API pour savoir quand cela se produit, ou pour demander un échec au lieu de nombres aléatoires de mauvaise qualité.

Autrement dit, il n'y a pas de solution complètement portable : cependant, il existe une approche décente et minimale. Vous pouvez utiliser un wrapper minimal autour d'un CSPRNG (défini comme sysrandomci-dessous) pour amorcer le PRNG.

les fenêtres


Vous pouvez compter sur CryptGenRandomun CSPRNG. Par exemple, vous pouvez utiliser le code suivant:

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


Sur de nombreux systèmes de type Unix, vous devez utiliser / dev / urandom lorsque cela est possible (bien que cela ne soit pas garanti sur les systèmes compatibles 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;
}

Autre


Si aucun CSPRNG n'est disponible, vous pouvez choisir de vous fier à std::random_device. Cependant, j'éviterais cela si possible, car divers compilateurs (notamment MinGW) l'implémentent avec un PRNG (en fait, produisant la même séquence à chaque fois pour alerter les humains que ce n'est pas correctement aléatoire).

Semis


Maintenant que nous avons nos pièces avec une surcharge minimale, nous pouvons générer les bits d'entropie aléatoire souhaités pour amorcer notre PRNG. L'exemple utilise (évidemment insuffisant) 32 bits pour amorcer le PRNG, et vous devez augmenter cette valeur (qui dépend de votre CSPRNG).

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

Comparaison pour booster


Nous pouvons voir des parallèles pour boost :: random_device (un vrai CSPRNG) après un rapide coup d'œil au code source . Boost utilise MS_DEF_PROVWindows, qui est le type de fournisseur pour PROV_RSA_FULL. La seule chose qui manque serait de vérifier le contexte cryptographique, ce qui peut être fait avec CRYPT_VERIFYCONTEXT. Sur * Nix, Boost utilise /dev/urandom. IE, cette solution est portable, bien testée et facile à utiliser.

Spécialisation Linux


Si vous êtes prêt à sacrifier la concision pour la sécurité, getrandomc'est un excellent choix sur Linux 3.17 et supérieur, et sur Solaris récent. getrandomse comporte de la même manière que /dev/urandom, sauf qu'il se bloque si le noyau n'a pas encore initialisé son CSPRNG après le démarrage. L'extrait de code suivant détecte si Linux getrandomest disponible, et si ce n'est pas le cas, il revient à /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


Il y a une dernière mise en garde: OpenBSD moderne n'en a pas /dev/urandom. Vous devriez utiliser getentropy à la place.

#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

d'autres pensées


Si vous avez besoin d'octets aléatoires sécurisés par cryptographie, vous devriez probablement remplacer le fstream par l'ouverture / lecture / fermeture sans tampon de POSIX. C'est parce que les deux basic_filebufet FILEcontiennent un tampon interne, qui sera alloué via un allocateur standard (et donc pas effacé de la mémoire).

Cela pourrait facilement être fait en changeant sysrandomen:

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;
}

Merci


Un merci spécial à Ben Voigt pour avoir signalé les FILEutilisations de lectures tamponnées et ne devraient donc pas être utilisées.

Je voudrais également remercier Peter Cordes pour avoir mentionné getrandom, et le manque d'OpenBSD /dev/urandom.

Alexander Huszagh
la source
11
C'est ce que j'ai fait dans le passé, mais la question, ou du moins une, est que WTF ne peut pas les rédacteurs de bibliothèques pour ces plates-formes faire cela pour nous? Je m'attends à ce que l'accès aux fichiers et les threads (par exemple) soient abstraits par les implémentations de bibliothèques, alors pourquoi ne pas générer de nombres aléatoires?
2
OP ici: Ce serait bien si cette réponse démontrait un peu mieux le semis. Autant que possible, j'espère des réponses qui génèrent du code copiable qui fait mieux le travail que le simple exemple que j'ai posté dans ma question sans nécessiter beaucoup d'interprétation technique ou de réflexion de la part du codeur.
Richard
4
Je pensais que ce /dev/randomserait le meilleur choix pour semer un RNG, mais apparemment, il /dev/urandomest toujours considéré comme sûr sur le plan informatique même s'il /dev/randomse bloquerait en raison de la faible entropie disponible, c'est donc urandomle choix recommandé pour tout sauf peut-être les tampons ponctuels. Voir aussi unix.stackexchange.com/questions/324209/… . Méfiez-vous des graines prévisibles urandomtrès tôt après le démarrage, cependant.
Peter Cordes
2
L' getrandom(2)appel système de Linux est comme l'ouverture et la lecture /dev/urandom, sauf qu'il bloquera si les sources aléatoires du noyau n'ont pas encore été initialisées. Je pense que cela vous évite le problème de l'aléatoire de faible qualité au démarrage précoce sans bloquer dans d'autres cas comme le /dev/randomfait.
Peter Cordes
1
@PeterCordes, bien sûr, et c'est une excellente option lorsqu'elle est disponible. Cependant, cela ne fonctionne pas sur BSD ou autres * Nix, ce qui /dev/urandomfonctionne généralement. La discussion de la liste de diffusion Python à ce sujet est quelque chose à laquelle je m'abonne généralement: bugs.python.org/issue27266
Alexander Huszagh
22

Dans un sens, cela ne peut pas être fait de manière portative. Autrement dit, on peut concevoir une plate-forme entièrement déterministe valide exécutant C ++ (par exemple, un simulateur qui marche l'horloge de la machine de manière déterministe, et avec des E / S "déterminées") dans laquelle il n'y a aucune source d'aléa pour amorcer un PRNG.

einpoklum
la source
1
@kbelder: 1. Qui a dit que l'utilisateur était une personne? 2. Tous les programmes n'ont pas d'interaction avec l'utilisateur et vous ne pouvez certainement pas supposer qu'il y a toujours un utilisateur autour ...
einpoklum
8
J'apprécie cette réponse, mais j'ai également le sentiment qu'un programme devrait faire un effort raisonnable.
Richard
3
@Richard D'accord, mais le problème est que les rédacteurs de la norme C ++ doivent (ou du moins faire de leur mieux pour) s'adapter à ce genre de situations bizarres. C'est pourquoi vous obtenez ce genre de définitions standard insipides, où vous pourriez obtenir des résultats décents, mais le compilateur peut toujours être conforme aux normes même s'il rend quelque chose qui n'a aucune valeur fonctionnelle. - Ainsi, vos restrictions ("courtes et ne peuvent pas s'appuyer sur d'autres bibliothèques") excluent toute réponse, car vous avez effectivement besoin d'une casse spéciale plate-forme par plate-forme / compilateur par compilateur. (par exemple, ce que Boost fait si bien.)
RM
2
@Richard ce qu'il explique, cependant, c'est que vous obtenez ce que vous obtenez dans la norme car il n'y a pas de moyen portable de faire mieux. Si vous voulez faire mieux (ce qui est un objectif noble), vous devrez accepter une plus ou moins grande quantité d'abomination :)
hobbs
1
@Richard: Parfois, il suffit d'accepter qu'il est possible de faire une implémentation C ++ conforme aux standards, ce qui n'est pas utile. Puisque les implémentations que les gens utilisent pour tout ce qui compte, sont conçues pour être utiles, vous devez parfois vivre avec des arguments comme "toute implémentation sensée fera quelque chose de raisonnable". J'aurais espéré que ce std::random_deviceserait dans cette catégorie, mais apparemment ce n'est pas le cas si certaines implémentations réelles utilisent un PRNG à graines fixes! Cela va bien au-delà de l'argument d'einpoklum.
Peter Cordes
14

Vous pouvez utiliser a std::seed_seqet le remplir au moins à la taille d'état requise pour le générateur en utilisant la méthode d'Alexander Huszagh pour obtenir l'entropie:

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);
}

S'il y avait un moyen approprié de remplir ou de créer une SeedSequence à partir d'un UniformRandomBitGenerator dans la bibliothèque standard, utiliser std::random_devicecorrectement pour l'amorçage serait beaucoup plus simple.

monstre à cliquet
la source
1
seed_seq a des problèmes cependant, pcg-random.org/posts/developing-a-seed_seq-alternative.html
etarion
Il n'y a rien dans le standard C ++ ou autre pour garantir que le générateur de nombres aléatoires utilisera le tableau entier lorsque vous amorcez à partir de seed_seq. Cette méthode conduira à un échec si vous utilisez le rng pour une simulation scientifique, et évidemment aussi pour la cryptographie. Le seul cas d'utilisation pour cela sera de randomiser un jeu vidéo, mais là, ce serait exagéré.
Kostas
5

L'implémentation sur laquelle je travaille tire parti de la state_sizepropriété du mt19937PRNG pour décider du nombre de graines à fournir lors de l'initialisation:

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});
}

Je pense qu'il y a place à amélioration parce que cela std::random_device::result_typepourrait différer de la std::mt19937::result_typetaille et de la gamme, ce qui devrait vraiment être pris en compte.

Une note sur std :: random_device .

Selon la C++11(/14/17)(les) norme (s):

26.5.6 Classe random_device [ rand.device ]

2 Si les limitations d'implémentation empêchent de générer des nombres aléatoires non déterministes, l'implémentation peut utiliser un moteur de nombres aléatoires.

Cela signifie que l'implémentation ne peut générer des valeurs déterministes que si elle est empêchée de générer des valeurs non déterministes par une certaine limitation.

Le MinGWcompilateur sur Windowsne fournit pas de valeurs non déterministes à partir de son std::random_device, bien qu'elles soient facilement disponibles à partir du système d'exploitation. Je considère donc cela comme un bogue et peu probable comme un phénomène courant dans les implémentations et les plates-formes.

Galik
la source
1
Cela peut remplir l'état MT, mais repose toujours uniquement sur std::random_device, et est donc vulnérable aux problèmes qui en découlent.
Richard
1
Je pense que je les ai énoncés assez clairement dans la question. Heureux de clarifier / discuter, cependant.
Richard
2
@Richard Existe-t-il de vrais systèmes qui n'implémentent pas réellement un système raisonnable std::random_device? Je sais que la norme permet une PRNGsolution de secours, mais je pense que c'est juste pour se couvrir car il est difficile d'exiger que chaque appareil qui l'utilise C++ait une source aléatoire non déterministe. Et s'ils ne le font pas, que pourriez-vous faire à ce sujet de toute façon?
Galik
5
@AlexanderHuszagh Je ne suis pas si sûr. Mon intention est de rendre ma "solution portable" dépendante de l'appareil, car si l'appareil prend en charge des générateurs non déterministes, il en va de même std::random_device. Je pense que c'est l'esprit de la norme. J'ai donc cherché et je ne peux trouver MinGWque ce qui est cassé à cet égard. Personne ne semble signaler ce problème avec quoi que ce soit d'autre que j'ai trouvé. Donc, dans ma bibliothèque, j'ai simplement marqué MinGWcomme non pris en charge. S'il y avait un problème plus large, je le repenserais. Je ne vois tout simplement pas la preuve de cela pour le moment.
Galik
5
Je suis vraiment déçu que MinGW ruine std::random_devicepour tout le monde en le rendant disponible sous une forme qui ne fournit pas les capacités aléatoires de la plate-forme. Les implémentations de faible qualité vont à l'encontre de l'objectif de l'API existant. Il serait préférable que l'OMI ne la mette pas en œuvre du tout jusqu'à ce qu'elle fonctionne. (Ou mieux, si l'API fournissait un moyen de demander un échec si un caractère aléatoire de haute qualité n'était pas disponible, MinGW pourrait donc éviter de causer des risques de sécurité tout en donnant des graines différentes pour les jeux ou autre.)
Peter Cordes
2

Il n'y a rien de mal à semer en utilisant le temps, en supposant que vous n'en ayez pas besoin pour être sécurisé (et vous n'avez pas dit que c'était nécessaire). L'idée est que vous pouvez utiliser le hachage pour corriger le caractère non aléatoire. J'ai trouvé que cela fonctionne correctement dans tous les cas, y compris et en particulier pour les simulations de Monte Carlo lourdes.

Une caractéristique intéressante de cette approche est qu'elle se généralise à l'initialisation à partir d'autres ensembles de graines pas vraiment aléatoires. Par exemple, si vous souhaitez que chaque thread ait son propre RNG (pour la sécurité des threads), vous pouvez simplement l'initialiser en fonction de l'ID de thread haché.

Ce qui suit est un SSCCE , distillé de ma base de code (pour plus de simplicité; certaines structures de support OO ont élidé):

#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
la source
1
Je suis d'accord avec vous pour dire que le semis avec le temps est probablement assez bon dans la pratique, si vous n'en avez pas besoin pour être sûr. Mais je ne peux pas être d'accord avec le reste de votre réponse. Semer avec le hash du temps n'est pas meilleur que semer avec le temps lui-même.
DW
@DW Empiriquement, c'est beaucoup mieux. La raison en est que le hachage est discontinu et couvre une plage de valeurs beaucoup plus large (essayez ceci vous-même: amorcez avec 1et 2et observez que la séquence de flottants générés par eux prend un certain temps à vraiment diverger).
imallett le
Je ne vois pas pourquoi c'est important. Nous ne fonctionnons qu'avec une seule graine à la fois. L'espace des valeurs possibles pour la graine (l'entropie de la graine) est le même dans tous les cas - le hachage n'augmente pas l'entropie. Peut-être pourriez-vous modifier la question pour expliquer pourquoi le hachage est meilleur?
DW
0

Voici ma propre tentative à la question:

#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;
}

L'idée ici est d'utiliser XOR pour combiner de nombreuses sources potentielles d'entropie (temps rapide, temps lent, std::random-deviceemplacements de variables statiques, emplacements de tas, emplacements de fonctions, emplacements de bibliothèque, valeurs spécifiques au programme) afin de tenter au mieux d'initialiser le mt19937. Tant qu'au moins une fois la source est "bonne", le résultat sera au moins aussi "bon".

Cette réponse n'est pas aussi courte qu'il serait préférable et peut contenir une ou plusieurs erreurs de logique. Je considère donc que c'est un travail en cours. Veuillez commenter si vous avez des commentaires.

Richard
la source
3
Les adresses peuvent avoir très peu de caractère aléatoire. Vous avez toujours les mêmes allocations, donc sur des systèmes embarqués plus petits où vous avez accès à toute la mémoire, il est probable que vous obteniez les mêmes résultats à chaque fois. Je dirais que c'est probablement assez bon pour un gros système, mais pourrait faire de la merde sur un microcontrôleur.
meneldal
1
Je suppose que cela &i ^ &myseeddevrait avoir beaucoup moins d'entropie que l'un ou l'autre seul, car les deux sont des objets avec une durée de stockage statique dans la même unité de traduction et donc susceptibles d'être assez proches les uns des autres. Et vous ne semblez pas réellement utiliser la valeur spéciale de l'initialisation de myseed?
aschepler
7
La conversion de pointeurs désaffectés en ints est un comportement indéfini; faites-le tant qu'il existe encore. ^est un horrible combinateur de hachage; si deux valeurs ont toutes les deux beaucoup d'entropie, mais peu comparées l'une à l'autre, cela la supprime. +est généralement meilleur (car x + x ne brûle qu'un bit d'entropie dans x, tandis que x ^ x les brûle tous). La fonction n'est pas la sécurité, je soupçonne ( rd())
Yakk - Adam Nevraumont
2
Oh et +je veux dire sur non signé ( +sur signé est UB-appât). Bien que ce soient des cas UB quelque peu ridicules, vous avez dit portable.
Pensez
1
@meneldal: même sur un PC à pleine puissance, bien que les allocations puissent obtenir différents emplacements physiques (en fonction de l'état de la machine externe au processus), les pointeurs sont abstraits par l'espace d'adressage virtuel du processus et sont probablement hautement répétables, en particulier ASLR n'est pas en vigueur.
Ben Voigt
0
  • Utilisez getentropy () pour amorcer un générateur de nombres pseudo-aléatoires (PRNG).
  • Utilisez getrandom () si vous voulez des valeurs aléatoires (au lieu de, disons, /dev/urandomou /dev/random).

Ils sont disponibles sur les systèmes modernes de type UNIX, tels que Linux, Solaris et OpenBSD.

Dan Anderson
la source
-2

Une plate-forme donnée peut avoir une source d'entropie, telle que /dev/random. Nanosecondes depuis l'époque avecstd::chrono::high_resolution_clock::now() est probablement la meilleure graine de la bibliothèque standard.

J'ai précédemment utilisé quelque chose comme (uint64_t)( time(NULL)*CLOCKS_PER_SEC + clock() )pour obtenir plus de bits d'entropie pour les applications qui ne sont pas critiques pour la sécurité.

Davislor
la source
2
Vous devriez vraiment l'utiliser /dev/urandom, surtout dans un cas comme celui-ci. /dev/randomblocs, et souvent sans bonnes raisons de le faire ([insérer une longue explication sur le nombre de systèmes d'exploitation différents qui estiment le caractère aléatoire des octets produits par / dev / random]).
Alexander Huszagh
2
@AlexanderHuszagh C'est vrai, même si j'ai dû coder sur des systèmes qui /dev/urandomn'existaient pas, et l'alternative au blocage était le déterminisme. Une boîte pourrait avoir /dev/hwrngou /dev/hw_randomaussi, ce qui devrait être encore mieux.
Davislor
D'accord, j'ai dit, "tel que /dev/random", et cela semble avoir déclenché une guerre sainte à propos de /dev/randomversus /dev/urandomsur Linux que je n'avais pas l'intention quand j'ai donné cet exemple ..
Davislor