GETRANDOM relazione di syscall con lo stato del pool entropia del kernel

3

Ho capito che quando GETRANDOM viene chiamato con la maschera di bit 00, la fonte di entropia proviene da / dev / urandom e l'output CSPRNG sta bloccando finché il pool di entropia interna ha almeno 128 bit di entropia. Lo stato del pool è leggibile a /proc/sys/kernel/random/entropy_avail ? Se non è quello, dove posso trovarlo?

L'obiettivo qui è di essere in grado di impostare una soglia personalizzata (256 bit) per lo stato interno del pool di entropia prima di utilizzarla per generare chiavi, ecc.

    
posta maqp 22.06.2017 - 05:59
fonte

1 risposta

3

Sysctls e il pool di blocco.

Puoi aumentare il valore di kernel.random.read_wakeup_threshold sysctl. Questo sysctl modifica il comportamento del blocco del pool , forzando gli utenti del pool di blocchi ad attendere fino a quando la stima dell'entropia supera questo valore. Dalla pagina di manuale random(4) :

read_wakeup_threshold
    This file contains the number of bits of entropy required for
    waking up processes that sleep waiting for entropy from
    /dev/random.  The default is 64.

Si noti tuttavia che il comportamento di blocco che si descrive si applica solo al sistema all'avvio molto presto. Una volta che ha abbastanza entropia, cambierà in comportamento non bloccante indipendentemente da quanto bassa sia la stima corrente. Questo perché hai solo bisogno di un certo numero di bit di entropia una volta e puoi creare una quantità praticamente illimitata di dati pseudocasuali crittograficamente sicuri una volta che ce l'hai. La stima dell'entropia che scende non cambia questo fatto.

Risposta alla tua domanda esatta

Ora, è possibile modificare questa soglia iniziale per il pool non bloccante (se non si desidera imparare tutto il nocciolo, saltare alla fine di questa risposta)? Sospettavo che non lo fosse, ma non ne ero sicuro, quindi sono andato a consultare la documentazione più autorevole disponibile, la fonte. Il syscall getrandom(2) è definito nel driver di casualità del kernel. Si noti che questo è specifico per il kernel di Linux 4.14 (le principali modifiche al driver di casualità sono state fatte in 4.8).

Commenti aggiunti da me per chiarimenti.

getrandom ()

SYSCALL_DEFINE3(getrandom, char __user *, buf, size_t, count,
        unsigned int, flags)
{
    int ret;

    // If the flags bitmask is invalid, return an error.
    if (flags & ~(GRND_NONBLOCK|GRND_RANDOM))
        return -EINVAL;

    // Cap the requested size at the maximum value.
    if (count > INT_MAX)
        count = INT_MAX;

    // If the blocking pool is selected, read from it and return.
    // The _random_read() function will deal with blocking.
    if (flags & GRND_RANDOM)
        return _random_read(flags & GRND_NONBLOCK, buf, count);

    // At this point, we know the non-blocking pool was selected.

    // Is the CRNG ready? Evaluates true if it is not.
    if (!crng_ready()) {
        // If we want to bail out and not block, return -EAGAIN.
        if (flags & GRND_NONBLOCK)
            return -EAGAIN;

        // Otherwise, block until random bytes become available.
        ret = wait_for_random_bytes();
        if (unlikely(ret))
            return ret;
    }

    // Finally, read from the non-blocking pool and return.
    return urandom_read(NULL, buf, count, NULL);
}

OK, quindi la maggior parte di questo è abbastanza evidente, ma quali sono le funzioni crng_ready() e wait_for_random_bytes() per? Quest'ultimo è definito nello stesso file.

wait_for_random_bytes ()

int wait_for_random_bytes(void)
{
    // If crng_ready() returns true (which is likely), return 0.
    if (likely(crng_ready()))
        return 0;

    // Otherwise, wait until it does return true before returning.
    return wait_event_interruptible(crng_init_wait, crng_ready());
}
EXPORT_SYMBOL(wait_for_random_bytes);

Quindi ora sappiamo nella definizione di getrandom() che, se il pool non bloccante è selezionato, controllerà se crng_ready() restituisce true. Se non ritorna vero, allora aspetteremo, dormendo finché non lo farà. Cosa fa crng_ready() ? Si scopre che è definito come una semplice macro.

crng_ready ()

// If the crng_init variable is > 0 (which is likely), evaluate true.
#define crng_ready() (likely(crng_init > 0))

La variabile inizia come zero, ma ciò che importa è esattamente dove è impostato su 1. Sembra che ciò avvenga nella funzione crng_fast_load() , definito qui .

crng_fast_load ()

static int crng_fast_load(const char *cp, size_t len)
{
    unsigned long flags;
    char *p;

    // Enter the atomic section.
    if (!spin_trylock_irqsave(&primary_crng.lock, flags))
        return 0;

    // If crng_ready() is already true, leave the atomic section and return.
    if (crng_ready()) {
        spin_unlock_irqrestore(&primary_crng.lock, flags);
        return 0;
    }

    // Mix in the values at cp with the CRNG state. Increment crng_init_cnt
    // for each byte from cp that gets mixed in (up to len times).
    p = (unsigned char *) &primary_crng.state[4];
    while (len > 0 && crng_init_cnt < CRNG_INIT_CNT_THRESH) {
        p[crng_init_cnt % CHACHA20_KEY_SIZE] ^= *cp;
        cp++; crng_init_cnt++; len--;
    }

    // Leave the atomic section.
    spin_unlock_irqrestore(&primary_crng.lock, flags);

    // If crng_init_cnt is >= CRNG_INIT_CNT_THRESH, set crng_init to 1.
    if (crng_init_cnt >= CRNG_INIT_CNT_THRESH) {
        invalidate_batched_entropy();
        crng_init = 1;
        wake_up_interruptible(&crng_init_wait);
        pr_notice("random: fast init done\n");
    }
    return 1;
}

Da questo, vediamo che crng_init_cnt viene incrementato per ogni byte che crng_fast_load() assorbe. La funzione è chiamata all'inizio all'avvio in varie funzioni di raccolta di entropia per aggiungere il maggior numero possibile di dati al pool nelle fasi iniziali. Ci siamo quasi! L'ultima cosa da fare è definire il valore di CRNG_INIT_CNT_THRESH , definito qui .

CRNG_INIT_CNT_THRESH e CHACHA20_KEY_SIZE

#define CRNG_INIT_CNT_THRESH (2*CHACHA20_KEY_SIZE)

Quindi è doppio CHACHA20_KEY_SIZE . Questo è definito in un file di intestazione, crypto/chacha20.h .

#define CHACHA20_KEY_SIZE   32

Quindi CRNG_INIT_CNT_THRESH è 64. E c'è la tua risposta!

Recap

Vediamo dove tutto questo porta a:

  • CHACHA20_KEY_SIZE è hardcoded a 32.
  • CRNG_INIT_CNT_THRESH è doppio CHACHA20_KEY_SIZE , rendendolo 64.
  • crng_init_cnt viene incrementato per ogni byte della casualità raccolta in precedenza.
  • Quando vengono raccolti almeno 64 byte di casualità, crng_init è impostato su 1.
  • Quando crng_init è 1, crng_ready() valuta true.
  • Quando crng_ready() valuta true, getrandom() riprende e restituisce.

La quantità di entropia precoce richiesta prima che getrandom() riprenda e restituisca non è in realtà 128 bit. È già codificato a 64 byte (512 bit), il doppio della quantità desiderata.

    
risposta data 19.12.2017 - 11:35
fonte

Leggi altre domande sui tag