1// Copyright 2017 The Fuchsia Authors
2//
3// Use of this source code is governed by a MIT-style
4// license that can be found in the LICENSE file or at
5// https://opensource.org/licenses/MIT
6
7#include <lib/crypto/entropy/quality_test.h>
8
9#include <dev/hw_rng.h>
10#include <kernel/cmdline.h>
11#include <inttypes.h>
12#include <lib/crypto/entropy/collector.h>
13#include <lib/crypto/entropy/hw_rng_collector.h>
14#include <lib/crypto/entropy/jitterentropy_collector.h>
15#include <lk/init.h>
16#include <string.h>
17#include <platform.h>
18#include <vm/vm_object_paged.h>
19#include <zircon/types.h>
20
21namespace crypto {
22
23namespace entropy {
24
25#if ENABLE_ENTROPY_COLLECTOR_TEST
26
27#ifndef ENTROPY_COLLECTOR_TEST_MAXLEN
28#define ENTROPY_COLLECTOR_TEST_MAXLEN (1024u * 1024u)
29#endif
30
31namespace {
32
33uint8_t entropy_buf[ENTROPY_COLLECTOR_TEST_MAXLEN];
34size_t entropy_len;
35
36} // namespace
37
38fbl::RefPtr<VmObject> entropy_vmo;
39bool entropy_was_lost = false;
40
41static void SetupEntropyVmo(uint level) {
42    if (VmObjectPaged::Create(PMM_ALLOC_FLAG_ANY, 0u, entropy_len, &entropy_vmo) != ZX_OK) {
43        printf("entropy-boot-test: Failed to create entropy_vmo (data lost)\n");
44        entropy_was_lost = true;
45        return;
46    }
47    size_t actual;
48    if (entropy_vmo->Write(entropy_buf, 0, entropy_len, &actual) != ZX_OK) {
49        printf("entropy-boot-test: Failed to write to entropy_vmo (data lost)\n");
50        entropy_was_lost = true;
51        return;
52    }
53    if (actual < entropy_len) {
54        printf("entropy-boot-test: partial write to entropy_vmo (data lost)\n");
55        entropy_was_lost = true;
56        return;
57    }
58    constexpr const char *name = "debug/entropy.bin";
59    if (entropy_vmo->set_name(name, strlen(name)) != ZX_OK) {
60        // The name is needed because devmgr uses it to add the VMO as a file in
61        // the /boot filesystem.
62        printf("entropy-boot-test: could not name entropy_vmo (data lost)\n");
63        entropy_was_lost = true;
64        return;
65    }
66}
67
68// Run the entropy collector test.
69void EarlyBootTest() {
70    const char* src_name = cmdline_get("kernel.entropy-test.src");
71    if (!src_name) {
72        src_name = "";
73    }
74
75    entropy::Collector* collector = nullptr;
76    entropy::Collector* candidate;
77    char candidate_name[ZX_MAX_NAME_LEN];
78
79    // TODO(andrewkrieger): find a nicer way to enumerate all entropy collectors
80    if (HwRngCollector::GetInstance(&candidate) == ZX_OK) {
81        candidate->get_name(candidate_name, sizeof(candidate_name));
82        if (strncmp(candidate_name, src_name, ZX_MAX_NAME_LEN) == 0) {
83            collector = candidate;
84        }
85    }
86    if (!collector &&
87        JitterentropyCollector::GetInstance(&candidate) == ZX_OK) {
88        candidate->get_name(candidate_name, sizeof(candidate_name));
89        if (strncmp(candidate_name, src_name, ZX_MAX_NAME_LEN) == 0) {
90            collector = candidate;
91        }
92    }
93
94    // TODO(andrewkrieger): add other entropy collectors.
95
96    if (!collector) {
97        printf("entropy-boot-test: unrecognized source \"%s\"\n", src_name);
98        printf("entropy-boot-test: skipping test.\n");
99        return;
100    }
101
102    entropy_len = cmdline_get_uint64("kernel.entropy-test.len", sizeof(entropy_buf));
103    if (entropy_len > sizeof(entropy_buf)) {
104        entropy_len = sizeof(entropy_buf);
105        printf("entropy-boot-test: only recording %zu bytes (try defining "
106               "ENTROPY_COLLECTOR_TEST_MAXLEN)\n", sizeof(entropy_buf));
107    }
108
109    zx_time_t start = current_time();
110    size_t result = collector->DrawEntropy(entropy_buf, entropy_len);
111    zx_time_t end = current_time();
112
113    if (result < entropy_len) {
114        printf("entropy-boot-test: source only returned %zu bytes.\n", result);
115        entropy_len = result;
116    } else {
117        printf("entropy-boot-test: successful draw in %" PRIu64 " nanoseconds.\n", end - start);
118    }
119}
120
121
122#else // ENABLE_ENTROPY_COLLECTOR_TEST
123
124void EarlyBootTest() {
125}
126
127#endif // ENABLE_ENTROPY_COLLECTOR_TEST
128
129} // namespace entropy
130
131} // namespace crypto
132
133#if ENABLE_ENTROPY_COLLECTOR_TEST
134LK_INIT_HOOK(setup_entropy_vmo, crypto::entropy::SetupEntropyVmo,
135             LK_INIT_LEVEL_VM + 1);
136#endif
137