4 #include "missing_efi.h"
5 #include "random-seed.h"
10 #define RANDOM_MAX_SIZE_MIN (32U)
11 #define RANDOM_MAX_SIZE_MAX (32U*1024U)
13 static const EFI_GUID rng_protocol_guid
= EFI_RNG_PROTOCOL_GUID
;
15 /* SHA256 gives us 256/8=32 bytes */
16 #define HASH_VALUE_SIZE 32
18 static EFI_STATUS
acquire_rng(UINTN size
, VOID
**ret
) {
19 _cleanup_freepool_ VOID
*data
= NULL
;
20 EFI_RNG_PROTOCOL
*rng
;
23 /* Try to acquire the specified number of bytes from the UEFI RNG */
25 err
= LibLocateProtocol((EFI_GUID
*) &rng_protocol_guid
, (VOID
**) &rng
);
27 Print(L
"Failed to acquire RNG protocol: %r\n", err
);
31 /* Print(L"RNG protocol not available.\n"); */
32 return EFI_UNSUPPORTED
;
35 data
= AllocatePool(size
);
39 err
= uefi_call_wrapper(rng
->GetRNG
, 3, rng
, NULL
, size
, data
);
41 Print(L
"Failed to acquire RNG data: %r\n", err
);
45 *ret
= TAKE_PTR(data
);
49 static VOID
hash_once(
53 const VOID
*system_token
,
54 UINTN system_token_size
,
56 UINT8 ret
[static HASH_VALUE_SIZE
]) {
58 /* This hashes together:
60 * 1. The contents of the old seed file
61 * 2. Some random data acquired from the UEFI RNG (optional)
62 * 3. Some 'system token' the installer installed as EFI variable (optional)
65 * And writes the result to the specified buffer.
68 struct sha256_ctx hash
;
70 sha256_init_ctx(&hash
);
71 sha256_process_bytes(old_seed
, size
, &hash
);
73 sha256_process_bytes(rng
, size
, &hash
);
74 if (system_token_size
> 0)
75 sha256_process_bytes(system_token
, system_token_size
, &hash
);
76 sha256_process_bytes(&counter
, sizeof(counter
), &hash
);
77 sha256_finish_ctx(&hash
, ret
);
80 static EFI_STATUS
hash_many(
84 const VOID
*system_token
,
85 UINTN system_token_size
,
90 _cleanup_freepool_ VOID
*output
= NULL
;
93 /* Hashes the specified parameters in counter mode, generating n hash values, with the counter in the
94 * range counter_start…counter_start+n-1. */
96 output
= AllocatePool(n
* HASH_VALUE_SIZE
);
100 for (i
= 0; i
< n
; i
++)
101 hash_once(old_seed
, rng
, size
,
102 system_token
, system_token_size
,
104 (UINT8
*) output
+ (i
* HASH_VALUE_SIZE
));
106 *ret
= TAKE_PTR(output
);
110 static EFI_STATUS
mangle_random_seed(
111 const VOID
*old_seed
,
114 const VOID
*system_token
,
115 UINTN system_token_size
,
117 VOID
**ret_for_kernel
) {
119 _cleanup_freepool_ VOID
*new_seed
= NULL
, *for_kernel
= NULL
;
123 /* This takes the old seed file contents, an (optional) random number acquired from the UEFI RNG, an
124 * (optional) system 'token' installed once by the OS installer in an EFI variable, and hashes them
125 * together in counter mode, generating a new seed (to replace the file on disk) and the seed for the
126 * kernel. To keep things simple, the new seed and kernel data have the same size as the old seed and
129 n
= (size
+ HASH_VALUE_SIZE
- 1) / HASH_VALUE_SIZE
;
131 /* Begin hashing in counter mode at counter 0 for the new seed for the disk */
132 err
= hash_many(old_seed
, rng
, size
, system_token
, system_token_size
, 0, n
, &new_seed
);
136 /* Continue counting at 'n' for the seed for the kernel */
137 err
= hash_many(old_seed
, rng
, size
, system_token
, system_token_size
, n
, n
, &for_kernel
);
141 *ret_new_seed
= TAKE_PTR(new_seed
);
142 *ret_for_kernel
= TAKE_PTR(for_kernel
);
147 EFI_STATUS
acquire_system_token(VOID
**ret
, UINTN
*ret_size
) {
148 _cleanup_freepool_ CHAR8
*data
= NULL
;
152 err
= efivar_get_raw(&loader_guid
, L
"LoaderSystemToken", &data
, &size
);
153 if (EFI_ERROR(err
)) {
154 if (err
!= EFI_NOT_FOUND
)
155 Print(L
"Failed to read LoaderSystemToken EFI variable: %r", err
);
160 Print(L
"System token too short, ignoring.");
161 return EFI_NOT_FOUND
;
164 *ret
= TAKE_PTR(data
);
170 static VOID
validate_sha256(void) {
173 /* Let's validate our SHA256 implementation. We stole it from glibc, and converted it to UEFI
174 * style. We better check whether it does the right stuff. We use the simpler test vectors from the
175 * SHA spec. Note that we strip this out in optimization builds. */
177 static const struct {
179 uint8_t hash
[HASH_VALUE_SIZE
];
182 { 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea,
183 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23,
184 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c,
185 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad }},
188 { 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14,
189 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24,
190 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c,
191 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55 }},
193 { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
194 { 0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06, 0x38, 0xb8,
195 0xe5, 0xc0, 0x26, 0x93, 0x0c, 0x3e, 0x60, 0x39,
196 0xa3, 0x3c, 0xe4, 0x59, 0x64, 0xff, 0x21, 0x67,
197 0xf6, 0xec, 0xed, 0xd4, 0x19, 0xdb, 0x06, 0xc1 }},
199 { "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
200 { 0xcf, 0x5b, 0x16, 0xa7, 0x78, 0xaf, 0x83, 0x80,
201 0x03, 0x6c, 0xe5, 0x9e, 0x7b, 0x04, 0x92, 0x37,
202 0x0b, 0x24, 0x9b, 0x11, 0xe8, 0xf0, 0x7a, 0x51,
203 0xaf, 0xac, 0x45, 0x03, 0x7a, 0xfe, 0xe9, 0xd1 }},
208 for (i
= 0; i
< ELEMENTSOF(array
); i
++) {
209 struct sha256_ctx hash
;
210 uint8_t result
[HASH_VALUE_SIZE
];
212 sha256_init_ctx(&hash
);
213 sha256_process_bytes(array
[i
].string
, strlena((const CHAR8
*) array
[i
].string
), &hash
);
214 sha256_finish_ctx(&hash
, result
);
216 if (CompareMem(result
, array
[i
].hash
, HASH_VALUE_SIZE
) != 0) {
217 Print(L
"SHA256 failed validation.\n");
218 uefi_call_wrapper(BS
->Stall
, 1, 120 * 1000 * 1000);
223 Print(L
"SHA256 validated\n");
227 EFI_STATUS
process_random_seed(EFI_FILE
*root_dir
, RandomSeedMode mode
) {
228 _cleanup_freepool_ VOID
*seed
= NULL
, *new_seed
= NULL
, *rng
= NULL
, *for_kernel
= NULL
, *system_token
= NULL
;
229 _cleanup_(FileHandleClosep
) EFI_FILE_HANDLE handle
= NULL
;
230 UINTN size
, rsize
, wsize
, system_token_size
= 0;
231 _cleanup_freepool_ EFI_FILE_INFO
*info
= NULL
;
236 if (mode
== RANDOM_SEED_OFF
) {
237 /* Print(L"Random seed handling turned off.\n"); */
238 return EFI_NOT_FOUND
;
241 /* Let's better be safe than sorry, and for now disable this logic in SecureBoot mode, so that we
242 * don't credit a random seed that is not authenticated. */
243 if (secure_boot_enabled()) {
244 /* Print(L"Not loading random seed, because we are in SecureBoot mode.\n"); */
245 return EFI_NOT_FOUND
;
248 /* Get some system specific seed that the installer might have placed in an EFI variable. We include
249 * it in our hash. This is protection against golden master image sloppiness, and it remains on the
250 * system, even when disk images are duplicated or swapped out. */
251 err
= acquire_system_token(&system_token
, &system_token_size
);
252 if (mode
!= RANDOM_SEED_ALWAYS
) {
253 /* if (err == EFI_NOT_FOUND) */
254 /* Print(L"Not loading random seed, because no system token is set.\n"); */
256 return err
; /* in all other error cases we already logged */
259 err
= uefi_call_wrapper(root_dir
->Open
, 5, root_dir
, &handle
, L
"\\loader\\random-seed", EFI_FILE_MODE_READ
|EFI_FILE_MODE_WRITE
, 0ULL);
260 if (EFI_ERROR(err
)) {
261 if (err
!= EFI_NOT_FOUND
)
262 Print(L
"Failed to open random seed file: %r\n", err
);
264 /* Print(L"Not loading random seed, because there is none.\n"); */
269 info
= LibFileInfo(handle
);
273 size
= info
->FileSize
;
274 if (size
< RANDOM_MAX_SIZE_MIN
) {
275 Print(L
"Random seed file is too short?\n");
276 return EFI_INVALID_PARAMETER
;
279 if (size
> RANDOM_MAX_SIZE_MAX
) {
280 Print(L
"Random seed file is too large?\n");
281 return EFI_INVALID_PARAMETER
;
284 seed
= AllocatePool(size
);
289 err
= uefi_call_wrapper(handle
->Read
, 3, handle
, &rsize
, seed
);
290 if (EFI_ERROR(err
)) {
291 Print(L
"Failed to read random seed file: %r\n", err
);
295 Print(L
"Short read on random seed file\n");
296 return EFI_PROTOCOL_ERROR
;
299 err
= uefi_call_wrapper(handle
->SetPosition
, 2, handle
, 0);
300 if (EFI_ERROR(err
)) {
301 Print(L
"Failed to seek to beginning of random seed file: %r\n", err
);
305 /* Request some random data from the UEFI RNG. We don't need this to work safely, but it's a good
306 * idea to use it because it helps us for cases where users mistakenly include a random seed in
307 * golden master images that are replicated many times. */
308 (VOID
) acquire_rng(size
, &rng
); /* It's fine if this fails */
310 /* Calculate new random seed for the disk and what to pass to the kernel */
311 err
= mangle_random_seed(seed
, rng
, size
, system_token
, system_token_size
, &new_seed
, &for_kernel
);
315 /* Update the random seed on disk before we use it */
317 err
= uefi_call_wrapper(handle
->Write
, 3, handle
, &wsize
, new_seed
);
318 if (EFI_ERROR(err
)) {
319 Print(L
"Failed to write random seed file: %r\n", err
);
323 Print(L
"Short write on random seed file\n");
324 return EFI_PROTOCOL_ERROR
;
327 err
= uefi_call_wrapper(handle
->Flush
, 1, handle
);
328 if (EFI_ERROR(err
)) {
329 Print(L
"Failed to flush random seed file: %r\n");
333 /* We are good to go */
334 err
= efivar_set_raw(&loader_guid
, L
"LoaderRandomSeed", for_kernel
, size
, FALSE
);
335 if (EFI_ERROR(err
)) {
336 Print(L
"Failed to write random seed to EFI variable: %r\n", err
);