blob: fd1e02fac937bba2e4dd8519e18e177875172af1 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <lib/crypto/entropy/jitterentropy_collector.h>
#include <kernel/cmdline.h>
#include <zircon/errors.h>
#include <fbl/atomic.h>
#ifndef JITTERENTROPY_MEM_SIZE
#define JITTERENTROPY_MEM_SIZE (64u * 1024u)
#endif
namespace crypto {
namespace entropy {
zx_status_t JitterentropyCollector::GetInstance(Collector** ptr) {
static JitterentropyCollector* instance = nullptr;
// Note: this would be fbl::atomic<bool>, except that fbl doesn't support
// that specialization.
static fbl::atomic<int> initialized = {0};
// Release-acquire ordering guarantees that, once a thread has stored a
// value in |initialized| with |memory_order_release|, any other thread that
// later loads |initialized| with |memory_order_acquire| will see the result
// that the first thread wrote to |instance|. In particular, any thread that
// reads |initialized| as 1 will see |instance| as having been initialized.
//
// Note that this doesn't protect against concurrent access: if two threads
// both enter GetInstance while |initialized| is still 0, they might both
// try to run the initialization code. That's why the comment in
// jitterentropy_collector.h requires that GetInstance() runs to completion
// first before concurrent calls are allowed.
if (!initialized.load(fbl::memory_order_acquire)) {
if (jent_entropy_init() != 0) {
// Initialization failed; keep instance == nullptr
instance = nullptr;
} else {
// TODO(andrewkrieger): after optimizing jitterentropy parameters
// (see ZX-1022), replace JITTERENTROPY_MEM_SIZE by the optimal
// size.
static uint8_t mem[JITTERENTROPY_MEM_SIZE];
static JitterentropyCollector collector(mem, sizeof(mem));
instance = &collector;
}
initialized.store(1, fbl::memory_order_release);
}
if (instance) {
*ptr = instance;
return ZX_OK;
} else {
*ptr = nullptr;
return ZX_ERR_NOT_SUPPORTED;
}
}
// TODO(ZX-1024): Test jitterentropy in different environments (especially on
// different platforms/architectures, and in multi-threaded mode). Ensure
// entropy estimate is safe enough.
// Testing shows that, with the default parameters below (bs=64, bc=512,
// ml=32, ll=1, raw=true), each byte of data contributes approximately
// 0.58 bits of min-entropy on the rpi3 and 0.5 bits on qemu-arm64. A safety
// factor of 0.9 gives us 0.50 * 0.9 * 1000 == 450 bits of entropy per 1000
// bytes of random data.
JitterentropyCollector::JitterentropyCollector(uint8_t* mem, size_t len)
: Collector("jitterentropy", /* entropy_per_1000_bytes */ 450) {
// TODO(ZX-1022): optimize default jitterentropy parameters, then update
// values here and in docs/kernel_cmdline.md.
uint32_t bs = cmdline_get_uint32("kernel.jitterentropy.bs", 64);
uint32_t bc = cmdline_get_uint32("kernel.jitterentropy.bc", 512);
mem_loops_ = cmdline_get_uint32("kernel.jitterentropy.ml", 32);
lfsr_loops_ = cmdline_get_uint32("kernel.jitterentropy.ll", 1);
use_raw_samples_ = cmdline_get_bool("kernel.jitterentropy.raw", true);
jent_entropy_collector_init(&ec_, mem, len, bs, bc, mem_loops_,
/* stir */ true);
}
size_t JitterentropyCollector::DrawEntropy(uint8_t* buf, size_t len) {
// TODO(ZX-1024): Test jitterentropy in multi-CPU environment. Disable
// interrupts, or otherwise ensure that jitterentropy still performs well in
// multi-threaded systems.
fbl::AutoLock guard(&lock_);
if (use_raw_samples_) {
for (size_t i = 0; i < len; i++) {
buf[i] = static_cast<uint8_t>(jent_lfsr_var_stat(&ec_, lfsr_loops_,
mem_loops_));
}
return len;
} else {
ssize_t err =jent_read_entropy(&ec_, reinterpret_cast<char*>(buf),
len);
return (err < 0) ? 0 : static_cast<size_t>(err);
}
}
} // namespace entropy
} // namespace crypto