summaryrefslogtreecommitdiffstats
path: root/src/interface/efi/efi_rng.c
blob: 66b37fe893cf42d1b9d1b156b326957355218b04 (plain) (blame)
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
/*
 * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 *
 * You can also choose to distribute this program under the terms of
 * the Unmodified Binary Distribution Licence (as given in the file
 * COPYING.UBDL), provided that you have satisfied its requirements.
 */

FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
FILE_SECBOOT ( PERMITTED );

#include <errno.h>
#include <ipxe/entropy.h>
#include <ipxe/crc32.h>
#include <ipxe/efi/efi.h>
#include <ipxe/efi/Protocol/Rng.h>

/** @file
 *
 * EFI random number generator protocol entropy source
 *
 */

struct entropy_source efirng_entropy __entropy_source ( ENTROPY_NORMAL );

/** Random number generator protocol */
static EFI_RNG_PROTOCOL *efirng;
EFI_REQUEST_PROTOCOL ( EFI_RNG_PROTOCOL, &efirng );

/** Minimum number of bytes to request from RNG
 *
 * The UEFI spec states (for no apparently good reason) that "When a
 * Deterministic Random Bit Generator (DRBG) is used on the output of
 * a (raw) entropy source, its security level must be at least 256
 * bits."  The EDK2 codebase (mis)interprets this to mean that the
 * call to GetRNG() should fail if given a buffer less than 32 bytes.
 *
 * Incidentally, nothing in the EFI RNG protocol provides any way to
 * report the actual amount of entropy returned by GetRNG().
 */
#define EFIRNG_LEN 32

/** Maximum number of times to attempting requesting data from RNG
 *
 * The UEFI spec allows GetRNG() to return EFI_NOT_READY, which is not
 * a particularly helpful error status since there is nothing that can
 * sensibly be done except to retry immediately.  We retry failed
 * calls to GetRNG() (for any reason) up to this number of times.
 */
#define EFIRNG_MAX_RETRY 16

/**
 * Enable entropy gathering
 *
 * @ret rc		Return status code
 */
static int efirng_enable ( void ) {

	/* Check for RNG protocol support */
	if ( ! efirng ) {
		DBGC ( &efirng, "EFIRNG has no RNG protocol\n" );
		return -ENOTSUP;
	}

	/* Nothing in the EFI specification provides any clue as to
	 * how much entropy will be returned by GetRNG().  Make a
	 * totally uninformed (and conservative guess) that each
	 * sample will contain at least one bit of entropy.
	 */
	entropy_init ( &efirng_entropy, MIN_ENTROPY ( 1.0 ) );

	return 0;
}

/**
 * Get noise sample from RNG protocol
 *
 * @ret noise		Noise sample
 * @ret rc		Return status code
 */
static int efirng_get_noise ( noise_sample_t *noise ) {
	uint8_t buf[EFIRNG_LEN];
	unsigned int i;
	EFI_STATUS efirc;
	int rc;

	/* Sanity check */
	assert ( efirng != NULL );

	/* Get random bytes, retrying if needed */
	for ( i = 0 ; i < EFIRNG_MAX_RETRY ; i++ ) {

		/* Get the minimum allowed number of random bytes */
		if ( ( efirc = efirng->GetRNG ( efirng, NULL, sizeof ( buf ),
						buf ) ) != 0 ) {
			rc = -EEFI ( efirc );
			continue;
		}

		/* Reduce random bytes to a single noise sample.  This
		 * seems like overkill, but we have no way of knowing
		 * how much entropy is actually present in the bytes
		 * returned by the RNG protocol.
		 */
		*noise = crc32_le ( 0, buf, sizeof ( buf ) );
		return 0;
	}

	DBGC ( &efirng, "ENTROPY could not read from RNG: %s\n",
	       strerror ( rc ) );
	return rc;
}

/** EFI random number generator protocol entropy source */
struct entropy_source efirng_entropy __entropy_source ( ENTROPY_NORMAL ) = {
	.name = "efirng",
	.enable = efirng_enable,
	.get_noise = efirng_get_noise,
};