iPXE
efi_entropy.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or (at your option) any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017  * 02110-1301, USA.
00018  *
00019  * You can also choose to distribute this program under the terms of
00020  * the Unmodified Binary Distribution Licence (as given in the file
00021  * COPYING.UBDL), provided that you have satisfied its requirements.
00022  */
00023 
00024 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00025 
00026 #include <errno.h>
00027 #include <ipxe/entropy.h>
00028 #include <ipxe/crc32.h>
00029 #include <ipxe/profile.h>
00030 #include <ipxe/efi/efi.h>
00031 #include <ipxe/efi/Protocol/Rng.h>
00032 
00033 /** @file
00034  *
00035  * EFI entropy source
00036  *
00037  */
00038 
00039 /** Random number generator protocol */
00040 static EFI_RNG_PROTOCOL *efirng;
00041 EFI_REQUEST_PROTOCOL ( EFI_RNG_PROTOCOL, &efirng );
00042 
00043 /** Minimum number of bytes to request from RNG
00044  *
00045  * The UEFI spec states (for no apparently good reason) that "When a
00046  * Deterministic Random Bit Generator (DRBG) is used on the output of
00047  * a (raw) entropy source, its security level must be at least 256
00048  * bits."  The EDK2 codebase (mis)interprets this to mean that the
00049  * call to GetRNG() should fail if given a buffer less than 32 bytes.
00050  *
00051  * Incidentally, nothing in the EFI RNG protocol provides any way to
00052  * report the actual amount of entropy returned by GetRNG().
00053  */
00054 #define EFI_ENTROPY_RNG_LEN 32
00055 
00056 /** Time (in 100ns units) to delay waiting for timer tick
00057  *
00058  * In theory, UEFI allows us to specify a trigger time of zero to
00059  * simply wait for the next timer tick.  In practice, specifying zero
00060  * seems to often return immediately, which produces almost no
00061  * entropy.  Specify a delay of 1000ns to try to force an existent
00062  * delay.
00063  */
00064 #define EFI_ENTROPY_TRIGGER_TIME 10
00065 
00066 /** Event used to wait for timer tick */
00067 static EFI_EVENT tick;
00068 
00069 /**
00070  * Enable entropy gathering
00071  *
00072  * @ret rc              Return status code
00073  */
00074 static int efi_entropy_enable ( void ) {
00075         EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
00076         EFI_STATUS efirc;
00077         int rc;
00078 
00079         DBGC ( &tick, "ENTROPY %s RNG protocol\n",
00080                ( efirng ? "has" : "has no" ) );
00081 
00082         /* Drop to TPL_APPLICATION to allow timer tick event to take place */
00083         bs->RestoreTPL ( TPL_APPLICATION );
00084 
00085         /* Create timer tick event */
00086         if ( ( efirc = bs->CreateEvent ( EVT_TIMER, TPL_NOTIFY, NULL, NULL,
00087                                          &tick ) ) != 0 ) {
00088                 rc = -EEFI ( efirc );
00089                 DBGC ( &tick, "ENTROPY could not create event: %s\n",
00090                        strerror ( rc ) );
00091                 return rc;
00092         }
00093 
00094         return 0;
00095 }
00096 
00097 /**
00098  * Disable entropy gathering
00099  *
00100  */
00101 static void efi_entropy_disable ( void ) {
00102         EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
00103 
00104         /* Close timer tick event */
00105         bs->CloseEvent ( tick );
00106 
00107         /* Return to TPL_CALLBACK */
00108         bs->RaiseTPL ( TPL_CALLBACK );
00109 }
00110 
00111 /**
00112  * Wait for a timer tick
00113  *
00114  * @ret low             CPU profiling low-order bits, or negative error
00115  */
00116 static int efi_entropy_tick ( void ) {
00117         EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
00118         UINTN index;
00119         uint16_t low;
00120         EFI_STATUS efirc;
00121         int rc;
00122 
00123         /* Wait for next timer tick */
00124         if ( ( efirc = bs->SetTimer ( tick, TimerRelative,
00125                                       EFI_ENTROPY_TRIGGER_TIME ) ) != 0 ) {
00126                 rc = -EEFI ( efirc );
00127                 DBGC ( &tick, "ENTROPY could not set timer: %s\n",
00128                        strerror ( rc ) );
00129                 return rc;
00130         }
00131         if ( ( efirc = bs->WaitForEvent ( 1, &tick, &index ) ) != 0 ) {
00132                 rc = -EEFI ( efirc );
00133                 DBGC ( &tick, "ENTROPY could not wait for timer tick: %s\n",
00134                        strerror ( rc ) );
00135                 return rc;
00136         }
00137 
00138         /* Get current CPU profiling timestamp low-order bits */
00139         low = profile_timestamp();
00140 
00141         return low;
00142 }
00143 
00144 /**
00145  * Get noise sample from timer ticks
00146  *
00147  * @ret noise           Noise sample
00148  * @ret rc              Return status code
00149  */
00150 static int efi_get_noise_ticks ( noise_sample_t *noise ) {
00151         int before;
00152         int after;
00153         int rc;
00154 
00155         /* Wait for a timer tick */
00156         before = efi_entropy_tick();
00157         if ( before < 0 ) {
00158                 rc = before;
00159                 return rc;
00160         }
00161 
00162         /* Wait for another timer tick */
00163         after = efi_entropy_tick();
00164         if ( after < 0 ) {
00165                 rc = after;
00166                 return rc;
00167         }
00168 
00169         /* Use TSC delta as noise sample */
00170         *noise = ( after - before );
00171 
00172         return 0;
00173 }
00174 
00175 /**
00176  * Get noise sample from RNG protocol
00177  *
00178  * @ret noise           Noise sample
00179  * @ret rc              Return status code
00180  */
00181 static int efi_get_noise_rng ( noise_sample_t *noise ) {
00182         uint8_t buf[EFI_ENTROPY_RNG_LEN];
00183         EFI_STATUS efirc;
00184         int rc;
00185 
00186         /* Fail if we have no EFI RNG protocol */
00187         if ( ! efirng )
00188                 return -ENOTSUP;
00189 
00190         /* Get the minimum allowed number of random bytes */
00191         if ( ( efirc = efirng->GetRNG ( efirng, NULL, EFI_ENTROPY_RNG_LEN,
00192                                         buf ) ) != 0 ) {
00193                 rc = -EEFI ( efirc );
00194                 DBGC ( &tick, "ENTROPY could not read from RNG: %s\n",
00195                        strerror ( rc ) );
00196                 return rc;
00197         }
00198 
00199         /* Reduce random bytes to a single noise sample.  This seems
00200          * like overkill, but we have no way of knowing how much
00201          * entropy is actually present in the bytes returned by the
00202          * RNG protocol.
00203          */
00204         *noise = crc32_le ( 0, buf, sizeof ( buf ) );
00205 
00206         return 0;
00207 }
00208 
00209 /**
00210  * Get noise sample
00211  *
00212  * @ret noise           Noise sample
00213  * @ret rc              Return status code
00214  */
00215 static int efi_get_noise ( noise_sample_t *noise ) {
00216         int rc;
00217 
00218         /* Try RNG first, falling back to timer ticks */
00219         if ( ( ( rc = efi_get_noise_rng ( noise ) ) != 0 ) &&
00220              ( ( rc = efi_get_noise_ticks ( noise ) ) != 0 ) )
00221                 return rc;
00222 
00223         return 0;
00224 }
00225 
00226 PROVIDE_ENTROPY_INLINE ( efi, min_entropy_per_sample );
00227 PROVIDE_ENTROPY ( efi, entropy_enable, efi_entropy_enable );
00228 PROVIDE_ENTROPY ( efi, entropy_disable, efi_entropy_disable );
00229 PROVIDE_ENTROPY ( efi, get_noise, efi_get_noise );