C'è qualche pregiudizio in questa selezione casuale da un dizionario?

2

Sto generando password selezionando parole casuali da un dizionario e contando entropy=words_in_passphrase*log(dictionary_size)/log(2) . Questo algoritmo presuppone che le parole vengano selezionate dal dizionario con distribuzione uniforme.

Penso che il principio di progettazione sia valido, ma non sono sicuro dell'attuazione. Sto leggendo da /dev/urandom abbastanza byte per esprimere un indice nel dizionario @words , e convertire quei byte in un numero un po 'goffo.

Il codice appare per funzionare correttamente, ma ci sono dei problemi a Perl o / dev / urandom che distorcono i risultati? Sorgente completa.

my $entropy_per_word = log (@words) / log (2);

my $pw = "";
my $pw_entropy = 0;

# maybe /dev/random ?
open my $random_source, '<', "/dev/urandom" or die "Could not open /dev/urandom";

binmode $random_source or die $!;

my $random_buffer = "";

my $random_bytes = ceil ($entropy_per_word / 8);

die "Tried to read no random bytes." if ($random_bytes < 1);

while ($pw_entropy < $entropy)
{
    die "Could not read random bytes"
    if ($random_bytes !=
        read ($random_source, $random_buffer, $random_bytes));

    my @bytes = unpack 'C ' x $random_bytes, $random_buffer;

    my $n = 0;

    foreach (@bytes)
    {
        $n *= 256;
        $n += $_ * 1;
    }

    next if ($n >= @words); # don't use %, it will bias the randomness

    $pw .= ' ' if ("" ne $pw);

    foreach (split //, $words[$n])
    {
        # sprinkle some capitalization for good measure
        $_ = uc if (rand > 0.8);

        $pw .= $_;
    }

    $pw_entropy += $entropy_per_word;
}
    
posta spraff 22.10.2016 - 14:48
fonte

1 risposta

7

Hai ragione di usare /dev/urandom . Non utilizzare /dev/random : non è migliore di /dev/urandom , e in qualche modo è peggio. Questa pagina è un bel riassunto su questo argomento.

Il tuo calcolo di entropia è corretto. L'entropia è una misura di ciò che il processo di generazione della password può produrre, non di ciò che ha prodotto; quindi, se hai n parole nella tua lista e scegli ogni parola in modo casuale e uniforme, ottieni esattamente n bit di entropia per parola (logaritmo in base 2 qui) . L'entropia non si riduce se ti capita di scegliere più volte la stessa parola. Ulteriori spiegazioni dettagliate sono disponibili in questa risposta .

Il test del ciclo:

next if ($n >= @words); # don't use %, it will bias the randomness

è corretto. Hai evitato il bias indotto dall'operazione modulo. Tuttavia, qui c'è una leggera inefficienza, in quanto si genera e il numero integrale di byte . Ad esempio, se la tua lista contiene, per esempio, 1000 parole, allora produrrai sequenze di due byte, quindi un numero nell'intervallo 0..65535. Dovrai ciclare in media 65.536 volte prima di colpire un numero nell'intervallo 0..999. A seconda del contesto, questo può o non può importare; i computer moderni sono molto veloci e se la generazione della password è guidata dall'uomo (produci una singola password per un utente umano che la sta aspettando), il computer ha molta potenza di calcolo da risparmiare e le dozzine / centinaia di loop vinti essere persino visibili.

Se vuoi essere più efficiente, ci sono principalmente due miglioramenti che possono essere fatti:

  • Tronca la sequenza di byte alla lunghezza minima in bit. Ad esempio, se si dispone di un elenco di 1000 parole, la lunghezza minima del bit è 10 (perché 2 10 = 1024). Ciò comporterebbe il mascheramento (vale a dire la cancellazione) dei primi 6 bit del numero intero casuale a 16 bit. In questo caso specifico, ciò significherebbe che il looping avverrebbe solo nel 2,4% dei casi, cioè piuttosto raramente. Su una base più generale, il troncamento alla lunghezza di bit corretta assicura che il numero medio di istanze del ciclo non sia maggiore di 2.

  • Usa una combinazione di esclusione (quando il numero è troppo alto) e Riduzione modulo. Con un elenco di 1000 parole, ciò significherebbe generare un numero nell'intervallo 0..64999 e quindi ridurre il modulo 1000: la clausola next verrebbe attivata solo quando il numero intero a 16 bit cade nell'intervallo 65000..65535 (vale a dire non spesso affatto), e il modulo non indurrebbe un pregiudizio perché 65000 è un multiplo di 1000. Potresti voler trarre ispirazione dalla documentazione per Java java.util.Random.nextInt(int bound) metodo (la documentazione include un estratto di codice che è illuminante, questo è Java, ma il concetto si applica a anche altre lingue).

Vorrei sconsigliare le capitalizzazioni casuali: non danneggiano direttamente la sicurezza, ma consumano risorse di memoria umana per un guadagno di entropia relativamente mediocre. Cioè, se non usando quella lettera maiuscola, fai spazio nella tua mente per ricordare un paio di parole in più, allora questo è probabilmente un buon affare. Inoltre, avere una password minuscola rende più agevole l'immissione della password, soprattutto sugli smartphone.

    
risposta data 22.10.2016 - 15:54
fonte

Leggi altre domande sui tag