Skip to content

Commit a89474a

Browse files
zx2c4ardbiesheuvel
authored andcommitted
efi: stub: use random seed from EFI variable
EFI has a rather unique benefit that it has access to some limited non-volatile storage, where the kernel can store a random seed. Read that seed in EFISTUB and concatenate it with other seeds we wind up passing onward to the kernel in the configuration table. This is complementary to the current other two sources - previous bootloaders, and the EFI RNG protocol. Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com> [ardb: check for non-NULL RNG protocol pointer, call GetVariable() without buffer first to obtain the size] Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
1 parent 63ffb57 commit a89474a

File tree

1 file changed

+52
-16
lines changed

1 file changed

+52
-16
lines changed

drivers/firmware/efi/libstub/random.c

Lines changed: 52 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -69,21 +69,29 @@ efi_status_t efi_random_get_seed(void)
6969
efi_guid_t rng_table_guid = LINUX_EFI_RANDOM_SEED_TABLE_GUID;
7070
struct linux_efi_random_seed *prev_seed, *seed = NULL;
7171
int prev_seed_size = 0, seed_size = EFI_RANDOM_SEED_SIZE;
72+
unsigned long nv_seed_size = 0, offset = 0;
7273
efi_rng_protocol_t *rng = NULL;
7374
efi_status_t status;
7475

7576
status = efi_bs_call(locate_protocol, &rng_proto, NULL, (void **)&rng);
7677
if (status != EFI_SUCCESS)
78+
seed_size = 0;
79+
80+
// Call GetVariable() with a zero length buffer to obtain the size
81+
get_efi_var(L"RandomSeed", &rng_table_guid, NULL, &nv_seed_size, NULL);
82+
if (!seed_size && !nv_seed_size)
7783
return status;
7884

85+
seed_size += nv_seed_size;
86+
7987
/*
8088
* Check whether a seed was provided by a prior boot stage. In that
8189
* case, instead of overwriting it, let's create a new buffer that can
8290
* hold both, and concatenate the existing and the new seeds.
8391
* Note that we should read the seed size with caution, in case the
8492
* table got corrupted in memory somehow.
8593
*/
86-
prev_seed = get_efi_config_table(LINUX_EFI_RANDOM_SEED_TABLE_GUID);
94+
prev_seed = get_efi_config_table(rng_table_guid);
8795
if (prev_seed && prev_seed->size <= 512U) {
8896
prev_seed_size = prev_seed->size;
8997
seed_size += prev_seed_size;
@@ -102,25 +110,53 @@ efi_status_t efi_random_get_seed(void)
102110
goto err_warn;
103111
}
104112

105-
status = efi_call_proto(rng, get_rng, &rng_algo_raw,
106-
EFI_RANDOM_SEED_SIZE, seed->bits);
107-
108-
if (status == EFI_UNSUPPORTED)
109-
/*
110-
* Use whatever algorithm we have available if the raw algorithm
111-
* is not implemented.
112-
*/
113-
status = efi_call_proto(rng, get_rng, NULL,
113+
if (rng) {
114+
status = efi_call_proto(rng, get_rng, &rng_algo_raw,
114115
EFI_RANDOM_SEED_SIZE, seed->bits);
115116

116-
if (status != EFI_SUCCESS)
117+
if (status == EFI_UNSUPPORTED)
118+
/*
119+
* Use whatever algorithm we have available if the raw algorithm
120+
* is not implemented.
121+
*/
122+
status = efi_call_proto(rng, get_rng, NULL,
123+
EFI_RANDOM_SEED_SIZE, seed->bits);
124+
125+
if (status == EFI_SUCCESS)
126+
offset = EFI_RANDOM_SEED_SIZE;
127+
}
128+
129+
if (nv_seed_size) {
130+
status = get_efi_var(L"RandomSeed", &rng_table_guid, NULL,
131+
&nv_seed_size, seed->bits + offset);
132+
133+
if (status == EFI_SUCCESS)
134+
/*
135+
* We delete the seed here, and /hope/ that this causes
136+
* EFI to also zero out its representation on disk.
137+
* This is somewhat idealistic, but overwriting the
138+
* variable with zeros is likely just as fraught too.
139+
* TODO: in the future, maybe we can hash it forward
140+
* instead, and write a new seed.
141+
*/
142+
status = set_efi_var(L"RandomSeed", &rng_table_guid, 0,
143+
0, NULL);
144+
145+
if (status == EFI_SUCCESS)
146+
offset += nv_seed_size;
147+
else
148+
memzero_explicit(seed->bits + offset, nv_seed_size);
149+
}
150+
151+
if (!offset)
117152
goto err_freepool;
118153

119-
seed->size = seed_size;
120-
if (prev_seed_size)
121-
memcpy(seed->bits + EFI_RANDOM_SEED_SIZE, prev_seed->bits,
122-
prev_seed_size);
154+
if (prev_seed_size) {
155+
memcpy(seed->bits + offset, prev_seed->bits, prev_seed_size);
156+
offset += prev_seed_size;
157+
}
123158

159+
seed->size = offset;
124160
status = efi_bs_call(install_configuration_table, &rng_table_guid, seed);
125161
if (status != EFI_SUCCESS)
126162
goto err_freepool;
@@ -135,7 +171,7 @@ efi_status_t efi_random_get_seed(void)
135171
err_freepool:
136172
memzero_explicit(seed, struct_size(seed, bits, seed_size));
137173
efi_bs_call(free_pool, seed);
138-
efi_warn("Failed to obtain seed from EFI_RNG_PROTOCOL\n");
174+
efi_warn("Failed to obtain seed from EFI_RNG_PROTOCOL or EFI variable\n");
139175
err_warn:
140176
if (prev_seed)
141177
efi_warn("Retaining bootloader-supplied seed only");

0 commit comments

Comments
 (0)