summaryrefslogtreecommitdiff
path: root/board/cr50/dcrypto/trng.c
blob: a67d5ef766a8cc66155231108ef36b12c0ff187a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
/* Copyright 2015 The ChromiumOS Authors
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "internal.h"
#include "flash_log.h"
#include "dcrypto_regs.h"

/**
 * Define KEYMGR SHA/HMAC access structure.
 */
static volatile struct trng_reg *reg_trng = (void *)(GC_TRNG_BASE_ADDR);

/**
 * The H1 TRNG uses the collapse time of a ring oscillator (RO) that is
 * initialized in a 3x mode (three enable pulses) and eventually collapses
 * to a stable 1x mode as a result of accumulated jitter (thermal noise).
 * A Phase-Frequency Detector (PFD) compares the 3x RO to a reference
 * RO (1.5x) and captures the state of a counter that is incremented
 * from the reference RO. The resulting reference-cycles-to-collapse
 * distribution is log-normal, and truncation of the counter bits results in
 * a distribution that approaches uniform.
 *
 * TRNG_SAMPLE_BITS defines how many bits to use from the 16 bit counter
 * output coming from the analog unit. Entropy is highest in least significant
 * bits of counter. For FIPS-certified code use just Bit 0 - it provides
 * highest entropy, allows better security settings for TRNG and simplifies
 * implementation of continuous health tests.
 */
#ifndef TRNG_SAMPLE_BITS
#define TRNG_SAMPLE_BITS 1
#endif

/**
 * Attempts to read TRNG_EMPTY before reporting a stall. Practically data should
 * be available in less than 0x7ff cycles under normal conditions. 0x7ff was
 * chosen to match the hardware TRNG TIMEOUT_COUNTER. Test on boards with slow
 * TRNG before reducing this number.
 */
#define TRNG_EMPTY_COUNT 0x7ff

/**
 * Number of attempts to reset TRNG after stall is detected.
 */
#define TRNG_RESET_COUNT 16

void fips_init_trng(void)
{
	/*
	 * Most of the trng initialization requires high permissions. If RO has
	 * dropped the permission level, dont try to read or write these high
	 * permission registers because it will cause rolling reboots. RO
	 * should do the TRNG initialization before dropping the level.
	 *
	 * For Cr50 RO doesn't drop permission level and init_trng() is called
	 * by board_init() before dropping permissions.
	 */

	/**
	 * According to NIST SP 800-90B only vetted conditioning mechanism
	 * should be used for post-processing raw entropy.
	 * See SP 800-90B, 3.1.5.1 Using Vetted Conditioning Components.
	 * Use of non-vetted algorithms is governed in 3.1.5.2, but
	 * assumes conservative coefficient 0.85 for entropy estimate,
	 * which increase number of requests to TRNG to get desirable
	 * entropy and prevents from getting full entropy.
	 */
	reg_trng->post_processing  = 0;

	/**
	 * TRNG can return up to 16 bits at a time, but highest bits
	 * have lower entropy. Practically on Cr50 only 13 bits can be
	 * used - setting to higher value makes TRNG_EMPTY always set.
	 * Entropy assessed to be reasonable (one bit H > 0.85)
	 * for up to 8 bits [7..0].
	 * Time to get 32bit random is roughly 160/TRNG_SAMPLE_BITS us.
	 */
	reg_trng->slice_max_upper_limit = TRNG_SAMPLE_BITS - 1;

	/* lowest bit have highest entropy, so always start from it */
	reg_trng->slice_min_lower_limit = 0;

	/**
	 * Analog logic cannot create a value < 8 under normal operating
	 * conditions, but there's a chance that an attacker could coax
	 * them out.
	 * Bit 0 - Enable rejection for values outside of range specified
	 * by TRNG_ALLOWED_VALUES register
	 */
	reg_trng->secure_post_processing = 0x1;

	/**
	 * Since for FIPS settings we use TRNG_SAMPLE_BITS = 1,
	 * and take only bit 0 from internal 16 bit reading, no bias is
	 * created for bit 0 if allowed_min is set to 6, which
	 * actually means min accepted value is 8 (RTL adds +2).
	 * TRNG_ALLOWED_VALUES_MAX=0x04 (accept all values up to 2^16-1).
	 * So, range will be [8..65535], with probability for bit 0 and 1
	 * remaining 50/50.
	 */
	reg_trng->allowed_values = 0x26;

	reg_trng->timeout_counter = 0x7ff;
	reg_trng->timeout_max_try = 4;
	reg_trng->power_down_b = 1;
	reg_trng->go_event = 1;
}

uint64_t read_rand(void)
{
	uint32_t empty_count = 0;
	uint32_t reset_count = 0;

#ifdef CRYPTO_TEST_SETUP
	/* Do we need to simulate error? */
	if (fips_break_cmd == FIPS_BREAK_TRNG)
		return (uint64_t)1UL << 32; /* Valid result, but value = 0. */
#endif

	/**
	 * Make sure we never hang in the loop - try at max TRNG_RESET_COUNT
	 * reset attempts, then return error
	 */
	while (reg_trng->empty && (reset_count < TRNG_RESET_COUNT)) {
		if ((reg_trng->fsm_state & GC_TRNG_FSM_STATE_FSM_IDLE_MASK) ||
		    empty_count > TRNG_EMPTY_COUNT) {
			/* TRNG timed out, restart */
			reg_trng->stop_work = 1;
			empty_count = 0;
			reset_count++;
			reg_trng->go_event = 1;
#ifdef CONFIG_FLASH_LOG
			/**
			 * Log stall only first time. Placing it after TRNG
			 * go_event increase a chance to get random in case
			 * of slow TRNG.
			 */
			if (reset_count == 1)
				fips_vtable->flash_log_add_event(
					FE_LOG_TRNG_STALL, 0, NULL);
#endif
		}
		empty_count++;
	}
	/**
	 * High 32-bits set to zero in case of error;
	 * otherwise value >> 32 == 1
	 */
	return (uint64_t)reg_trng->read_data |
	       ((uint64_t)(reset_count < TRNG_RESET_COUNT) << 32);
}

/* Local switch to test command. Enable when work on it. */
#ifndef CRYPTO_TEST_CMD_RAND
#define CRYPTO_TEST_CMD_RAND 0
#endif

#if !defined(SECTION_IS_RO) && defined(CRYPTO_TEST_SETUP)
#include "console.h"
#include "endian.h"
#include "extension.h"
#include "timer.h"
#include "watchdog.h"

#if CRYPTO_TEST_CMD_RAND
static void  print_rand_stat(uint32_t *histogram, size_t size)
{
	struct pair {
		uint32_t value;
		uint32_t count;
	};
	struct pair min;
	struct pair max;
	size_t count;

	min.count = ~0;
	max.count = 0;
	max.value = ~0;
	min.value = ~0;

	for (count = 0; count < size; count++) {
		if (histogram[count] > max.count) {
			max.count = histogram[count];
			max.value = count;
		}
		if (histogram[count] < min.count) {
			min.count = histogram[count];
			min.value = count;
		}
	}

	ccprintf("min %d(%d), max %d(%d)", min.count, min.value,
		 max.count, max.value);

	for (count = 0; count < size; count++) {
		if (!(count % 8)) {
			ccprintf("\n");
			cflush();
		}
		ccprintf(" %6d", histogram[count]);
	}
	ccprintf("\n");
}

/* histogram at byte level */
static uint32_t histogram[256];
/* histogram at level of TRNG samples */
static uint32_t histogram_trng[1 << TRNG_SAMPLE_BITS];

static int command_rand(int argc, char **argv)
{
	int count = 1000; /* Default number of cycles. */
	uint32_t val = 0, bits = 0;

	if (argc == 2)
		count = strtoi(argv[1], NULL, 10);

	memset(histogram, 0, sizeof(histogram));
	memset(histogram_trng, 0, sizeof(histogram_trng));
	ccprintf("Retrieving %d 32-bit random words.\n", count);
	while (count-- > 0) {
		uint64_t rnd;
		uint32_t rvalue;
		size_t size;

		rnd = fips_trng_rand32();
		if (!rand_valid(rnd)) {
			ccprintf("Failed reading TRNG.\n");
			return EC_ERROR_HW_INTERNAL;
		}
		rvalue = (uint32_t)rnd;
		/* update byte-level histogram */
		for (size = 0; size < sizeof(rvalue); size++)
			histogram[((uint8_t *)&rvalue)[size]]++;

		/* update histogram on TRNG sample size level */
		val = (val | (rvalue << bits)) & ((1 << TRNG_SAMPLE_BITS) - 1);
		rvalue >>= TRNG_SAMPLE_BITS - bits;
		bits += 32;
		while (bits >= TRNG_SAMPLE_BITS) {
			histogram_trng[val]++;
			val = rvalue & ((1 << TRNG_SAMPLE_BITS) - 1);
			rvalue >>= TRNG_SAMPLE_BITS;
			bits -= TRNG_SAMPLE_BITS;
		};

		if (!(count % 10000))
			watchdog_reload();
	}
	ccprintf("Byte-level histogram:\n");
	print_rand_stat(histogram, ARRAY_SIZE(histogram));
	ccprintf("\nSample-level (%d bits) histogram:\n", TRNG_SAMPLE_BITS);
	print_rand_stat(histogram_trng, ARRAY_SIZE(histogram_trng));

	return EC_SUCCESS;
}
DECLARE_SAFE_CONSOLE_COMMAND(rand, command_rand, NULL, NULL);

#endif /* CRYPTO_TEST_CMD_RAND */

/* For testing we need unchecked values from TRNG. */
static bool raw_rand_bytes(void *buffer, size_t len)
{
	size_t random_togo = 0;
	size_t buffer_index = 0;
	uint32_t random_value;
	uint8_t *buf = (uint8_t *) buffer;

	/*
	 * Retrieve random numbers in 4 byte quantities and pack as many bytes
	 * as needed into 'buffer'. If len is not divisible by 4, the
	 * remaining random bytes get dropped.
	 */
	while (buffer_index < len) {
		if (!random_togo) {
			uint64_t rnd = read_rand();

			if (!rand_valid(rnd))
				return false;

			random_value = (uint32_t)rnd;
			random_togo = sizeof(random_value);
		}
		buf[buffer_index++] = random_value >>
			((random_togo-- - 1) * 8);
	}
	return true;
}

/*
 * This extension command is similar to TPM2_GetRandom, but made
 * available for CRYPTO_TEST = 1 which disables TPM.
 * Command structure, shared out of band with the test driver running
 * on the host:
 *
 * field     |    size  |                  note
 * =========================================================================
 * text_len  |    2     | the number of random bytes to generate, big endian
 * type      |    1     | 0 - TRNG, 1 = FIPS TRNG, 2 = FIPS DRBG
 *           |          | 3 - TRNG after restart
 *           |          | other values reserved for extensions
 */
static enum vendor_cmd_rc trng_test(enum vendor_cmd_cc code, void *buf,
				    size_t input_size, size_t *response_size)
{
	uint16_t text_len;
	uint8_t *cmd = buf;
	uint8_t op_type = 0;

	if (input_size != sizeof(text_len) + 1) {
		*response_size = 0;
		return VENDOR_RC_BOGUS_ARGS;
	}

	text_len = be16toh(*(uint16_t *)cmd);
	op_type = cmd[sizeof(text_len)];

	if (text_len > *response_size) {
		*response_size = 0;
		return VENDOR_RC_BOGUS_ARGS;
	}

	switch (op_type) {
	case 3:
		/* Power down LDO, wait 1ms, power up. */
		reg_trng->power_down_b = 0;
		udelay(1000);
		reg_trng->power_down_b = 1;
		reg_trng->go_event = 1;
		/* Fall through */
	case 0:
		if (!raw_rand_bytes(buf, text_len))
			return VENDOR_RC_INTERNAL_ERROR;
		break;
	case 1:
		if (!fips_trng_bytes(buf, text_len))
			return VENDOR_RC_INTERNAL_ERROR;
		break;
	case 2:
		if (!fips_rand_bytes(buf, text_len))
			return VENDOR_RC_INTERNAL_ERROR;
		break;

	default:
		return VENDOR_RC_BOGUS_ARGS;
	}
	*response_size = text_len;
	return VENDOR_RC_SUCCESS;
}

DECLARE_VENDOR_COMMAND(VENDOR_CC_TRNG_TEST, trng_test);

#endif /* CRYPTO_TEST_SETUP */