Wie verwende ich / dev / random oder urandom in C?

76

Ich möchte /dev/randomoder /dev/urandomin C verwenden. Wie kann ich das tun? Ich weiß nicht, wie ich mit ihnen in C umgehen soll. Wenn jemand weiß, sagen Sie mir bitte, wie. Vielen Dank.

Stojance
quelle
Lesen Sie diesen sehr informativen Artikel über einige häufige Vorbehalte, um diesen Weg zur (Pseudo-) Zufälligkeit einzuschlagen
Appas

Antworten:

107

Im Allgemeinen ist es besser, das Öffnen von Dateien zu vermeiden, um zufällige Daten zu erhalten, da das Verfahren so viele Fehlerquellen aufweist.

Auf den letzten Linux - Distributionen, die getrandomkann Systemaufruf verwendet wird Krypto sichere Zufallszahl zu bekommen, und es kann nicht umhin , wenn GRND_RANDOM sie nicht als Flag und die Lesemenge beträgt maximal 256 Byte festgelegt.

Seit Oktober 2017 haben OpenBSD, Darwin und Linux (mit -lbsd) eine Implementierung arc4random, die kryptosicher ist und nicht fehlschlagen kann. Das macht es zu einer sehr attraktiven Option:

char myRandomData[50];
arc4random_buf(myRandomData, sizeof myRandomData); // done!

Andernfalls können Sie die zufälligen Geräte so verwenden, als wären sie Dateien. Sie lesen von ihnen und erhalten zufällige Daten. Ich benutze open/ readhier, aber fopen/ freadwürde genauso gut funktionieren.

int randomData = open("/dev/urandom", O_RDONLY);
if (randomData < 0)
{
    // something went wrong
}
else
{
    char myRandomData[50];
    ssize_t result = read(randomData, myRandomData, sizeof myRandomData);
    if (result < 0)
    {
        // something went wrong
    }
}

Sie können viel mehr zufällige Bytes lesen, bevor Sie den Dateideskriptor schließen. / dev / urandom blockiert niemals und füllt immer so viele Bytes aus, wie Sie angefordert haben, es sei denn, der Systemaufruf wird durch ein Signal unterbrochen. Es gilt als kryptografisch sicher und sollte Ihr zufälliges Gerät sein.

/ dev / random ist kniffliger. Auf den meisten Plattformen kann es weniger Bytes zurückgeben, als Sie angefordert haben, und es kann blockieren, wenn nicht genügend Bytes verfügbar sind. Dies macht die Fehlerbehandlungsgeschichte komplexer:

int randomData = open("/dev/random", O_RDONLY);
if (randomData < 0)
{
    // something went wrong
}
else
{
    char myRandomData[50];
    size_t randomDataLen = 0;
    while (randomDataLen < sizeof myRandomData)
    {
        ssize_t result = read(randomData, myRandomData + randomDataLen, (sizeof myRandomData) - randomDataLen);
        if (result < 0)
        {
            // something went wrong
        }
        randomDataLen += result;
    }
    close(randomData);
}
zneak
quelle
14
@karim: Bitte lies niemals alle Bytes aus / dev / random. Tu es einfach nicht. Ihr Programm ist wahrscheinlich nicht der einzige Benutzer auf dem System, der zufällige Bytes benötigt.
Zan Lynx
1
@zneak, sorry, ich habe vergessen, dass Beiträge hier bearbeitet werden können. Danke für die Information. Bei Ihrer letzten Änderung wurde nur eine Überprüfung beim Lesen anstelle einer Schleife hinzugefügt. Ich habe es so geändert, dass es eine Schleife verwendet, sodass der Code jetzt entsprechend blockiert wird.
Morrog
2
@morrog Overeager-Rezensenten haben es abgelehnt, daher habe ich die Änderungen manuell vorgenommen. Entschuldigung, Sie werden nicht dafür gutgeschrieben.
Zneak
1
@CodesInChaos, ich zitiere die Linux-Manpage : "Wenn Sie sich nicht sicher sind, ob Sie / dev / random oder / dev / urandom verwenden sollen, möchten Sie wahrscheinlich Letzteres verwenden. In der Regel sollte / dev / urandom für alles außer für langlebige GPG / SSL / SSH-Schlüssel verwendet werden. "
Zneak
2
@zneak Betonung auf Langlebigkeit, da es für diejenigen nicht schadet, extra paranoid zu sein. Für normale Krypto ist die Verwendung /dev/urandomin Ordnung.
CodesInChaos
21

Es gibt andere genaue Antworten oben. Ich musste allerdings einen FILE*Stream verwenden. Folgendes habe ich getan ...

int byte_count = 64;
char data[64];
FILE *fp;
fp = fopen("/dev/urandom", "r");
fread(&data, 1, byte_count, fp);
fclose(fp);
Dustin Kirkland
quelle
1
Ein int kann direkt gelesen werden, indem einfach der int-Zeiger in einen char-Zeiger umgewandelt wird. fread((char*)(&myInt),sizeof(myInt),1,fp)
Azeem Bande-Ali
@ AzeemBande-Ali: Warum verwendest du nicht stattdessen fread ((int *) (& myInt), sizeof (myInt), 1, fp)? Ich meine eine Besetzung von int *?
Larry
4
In keinem Fall sollte eine Umwandlung in C-Code verwendet werden, fread () nimmt eine Leere * an, also machen Sie einfach fread (& myInt, ...);
Nr.
Warum brauchen Sie byte_count? Es ist unbenutzt.
CalculatorFeline
@CalculatorFeline Der byte_count hier ist etwas verwirrend, op wollte vielleicht zunächst, dass jeder Index die gleiche
Bytelänge hat
17

Öffnen Sie einfach die Datei zum Lesen und lesen Sie dann die Daten. In C ++ 11 möchten Sie möglicherweise verwenden, std::random_devicedas plattformübergreifenden Zugriff auf solche Geräte bietet.

Tronic
quelle
Es scheint, dass std::random_devicees nicht in den Standard von 2011 geschafft hat. Es erscheint im N3797-Entwurf .
Keith Thompson
2
Sieht so aus, als hätte ich es am Ende in C ++ 11 geschafft. std::random_device
Legends2k
1
Das Problem ist, dass std::random_devicees in C ++ und nicht in C ist und das OP gefragt hat, wie es verwendet werden soll /dev/randomoder /dev/urandomnicht, std::random_deviceobwohl es eine gute Wahl ist std::random_deviceund Vorteile hat, ist es einfach nicht das, wonach das OP gefragt hat
Nfagie Yansaneh
8

Zneak ist 100% korrekt. Es ist auch sehr üblich, einen Puffer mit Zufallszahlen zu lesen, der etwas größer ist als der, den Sie beim Start benötigen. Sie können dann ein Array in den Speicher einfügen oder es zur späteren Wiederverwendung in Ihre eigene Datei schreiben.

Eine typische Implementierung der oben genannten:

typedef struct prandom {
     struct prandom *prev;
     int64_t number;
     struct prandom *next;
} prandom_t;

Dies ähnelt mehr oder weniger einem Band, das sich nur vorwärts bewegt und bei Bedarf von einem anderen Thread auf magische Weise aufgefüllt werden kann. Es gibt eine Menge von Dienstleistungen , die große Datei Dumps von nichts als Zufallszahlen liefern , die mit vielen stärkeren Generatoren erzeugt werden , wie zum Beispiel:

  • Radioaktiver Zerfall
  • Optisches Verhalten (Photonen treffen auf einen halbtransparenten Spiegel)
  • Atmosphärisches Geräusch (nicht so stark wie oben)
  • Farmen von betrunkenen Affen, die auf Tastaturen tippen und Mäuse bewegen (Scherz)

Verwenden Sie keine "vorverpackte" Entropie für kryptografische Samen , falls dies nicht selbstverständlich ist. Diese Sätze eignen sich gut für Simulationen, überhaupt nicht zum Generieren von Schlüsseln und dergleichen.

Wenn Sie viele Zahlen für so etwas wie eine Monte-Carlo-Simulation benötigen, ist es viel besser, sie so verfügbar zu haben, dass read () nicht blockiert.

Denken Sie jedoch daran, dass die Zufälligkeit einer Zahl ebenso deterministisch ist wie die Komplexität, mit der sie generiert wird. /dev/randomund /dev/urandomsind praktisch, aber nicht so stark wie die Verwendung eines HRNG (oder das Herunterladen eines großen Speicherauszugs von einem HRNG). Erwähnenswert ist auch, dass das /dev/random Nachfüllen über Entropie erfolgt , sodass es je nach den Umständen für eine Weile blockieren kann.

Tim Post
quelle
2
Das Herunterladen von "großen Datei-Dumps mit nichts als Zufallszahlen" ist ein schrecklicher Rat für kryptografische Zwecke. Es wird jemand anderes gebeten, den Startwert für Ihre Funktionen bereitzustellen, und diese Dienste scheinen diese Daten unverschlüsselt über das Internet zu übertragen. Bitte tu das nicht.
Dequis
@dequis habe ich geklärt. Ich sehe kein Problem darin, sie zum Ausführen großer Simulationen zu verwenden. Ich dachte, es wäre vernünftig , sie nicht für keygen / etc zu verwenden, aber es lohnt sich, auf den Punkt seltsam spezifisch zu sein. Die Frage war unternehmensunabhängig, daher kam es mir wirklich nicht in den Sinn, so spezifisch zu sein, aber ein guter Punkt.
Tim Post
6

Die Antwort von zneak deckt es einfach ab, aber die Realität ist komplizierter. Zum Beispiel müssen Sie überlegen, ob / dev / {u} random wirklich das Zufallszahlengerät ist. Ein solches Szenario kann auftreten, wenn Ihr Computer kompromittiert und die Geräte durch Symlinks zu / dev / zero oder eine Datei mit geringer Dichte ersetzt wurden. In diesem Fall ist der Zufallsstrom jetzt vollständig vorhersehbar.

Der einfachste Weg (zumindest unter Linux und FreeBSD) besteht darin, einen ioctl-Aufruf auf dem Gerät auszuführen, der nur erfolgreich ist, wenn das Gerät ein Zufallsgenerator ist:

int data;
int result = ioctl(fd, RNDGETENTCNT, &data); 
// Upon success data now contains amount of entropy available in bits

Wenn dies vor dem ersten Lesen des zufälligen Geräts durchgeführt wird, gibt es eine faire Wette, dass Sie das zufällige Gerät haben. Die Antwort von @ zneak kann also besser wie folgt erweitert werden:

int randomData = open("/dev/random", O_RDONLY);
int entropy;
int result = ioctl(randomData, RNDGETENTCNT, &entropy);

if (!result) {
   // Error - /dev/random isn't actually a random device
   return;
}

if (entropy < sizeof(int) * 8) {
    // Error - there's not enough bits of entropy in the random device to fill the buffer
    return;
}

int myRandomInteger;
size_t randomDataLen = 0;
while (randomDataLen < sizeof myRandomInteger)
{
    ssize_t result = read(randomData, ((char*)&myRandomInteger) + randomDataLen, (sizeof myRandomInteger) - randomDataLen);
    if (result < 0)
    {
        // error, unable to read /dev/random 
    }
    randomDataLen += result;
}
close(randomData);

Der Insane Coding-Blog hat dies und andere Fallstricke vor nicht allzu langer Zeit behandelt. Ich empfehle dringend, den gesamten Artikel zu lesen. Ich muss ihnen die Ehre erweisen, woher diese Lösung stammt.

Bearbeitet, um hinzuzufügen (25.07.2014) ... Zufälligerweise habe
ich gestern Abend gelesen, dass Linux im Rahmen der LibReSSL-Bemühungen anscheinend einen GetRandom () -Syscall erhält . Zum Zeitpunkt des Schreibens gibt es kein Wort darüber, wann es in einer allgemeinen Kernel-Version verfügbar sein wird. Dies wäre jedoch die bevorzugte Schnittstelle, um kryptografisch sichere Zufallsdaten zu erhalten, da alle Fallstricke beseitigt werden, die der Zugriff über Dateien bietet. Siehe auch die mögliche Implementierung von LibReSSL .

Chris J.
quelle
2
Ein Angreifer mit genügend Leistung, um / dev / random oder / dev / urandom durch etwas anderes zu ersetzen, verfügt normalerweise auch über genügend Leistung, um ein Kernelmodul zu laden, um jeden Versuch zu vermasseln, festzustellen, ob es sich um ein zufälliges Gerät handelt oder nicht.
Zneak
Die Manpage sagt, getrandom()wurde in Kernel 3.17 eingeführt. Stock Ubuntu 16.04 hat es also nicht ab dem 17.01.2018. Führen Sie es uname -ain einem Terminal aus, um Ihre Kernelversion zu überprüfen.
Erapert