diff --git a/vboot_firmware/include/kernel_image_fw.h b/vboot_firmware/include/kernel_image_fw.h index 03bcf858e2..db2550dbb1 100644 --- a/vboot_firmware/include/kernel_image_fw.h +++ b/vboot_firmware/include/kernel_image_fw.h @@ -125,30 +125,29 @@ int VerifyKernelData(RSAPublicKey* kernel_sign_key, * using the firmware public key [firmware_key_blob]. If [dev_mode] is 1 * (active), then key header verification is skipped. * - * Fills in a pointer to expected kernel data signature - * within [kernel_header_blob] in [expected_kernel_signature]. + * On success, fills in the fields of image with the kernel header and + * preamble fields. + * + * Note that pointers in the image point directly into the input + * kernel_header_blob. image->kernel_data is set to NULL, since it's not + * part of the header and preamble data itself. * * The signing key to use for kernel data verification is returned in * [kernel_sign_key], This must be free-d explicitly by the caller after use. - * The kernel signing algorithm is returned in [kernel_sign_algorithm] and its - * length in [kernel_len]. * * Returns 0 on success, error code on failure. */ int VerifyKernelHeader(const uint8_t* firmware_key_blob, const uint8_t* kernel_header_blob, + uint64_t kernel_header_blob_len, const int dev_mode, - const uint8_t** expected_kernel_signature, - RSAPublicKey** kernel_sign_key, - int* kernel_sign_algorithm, - uint64_t* kernel_len); + KernelImage *image, + RSAPublicKey** kernel_sign_key); /* Performs a chained verify of the kernel blob [kernel_blob]. If * [dev_mode] is 0 [inactive], then the pre-processed public signing key * [root_key_blob] is used to verify the signature of the signing key, * else the check is skipped. - * - * * Returns 0 on success, error code on failure. * * NOTE: The length of the kernel blob is derived from reading the fields diff --git a/vboot_firmware/lib/include/stateful_util.h b/vboot_firmware/lib/include/stateful_util.h index f8ad2dde2f..246aeabb63 100644 --- a/vboot_firmware/lib/include/stateful_util.h +++ b/vboot_firmware/lib/include/stateful_util.h @@ -22,6 +22,16 @@ typedef struct MemcpyState { uint8_t overrun; /* Flag set to 1 when an overrun occurs. */ } MemcpyState; +/* Skip [len] bytes only if there's enough data to skip according + * to [state]. + * On success, return a meaningless but non-NULL pointer and updates [state]. + * On failure, return NULL, set remaining_len in state to -1. + * + * Useful for iterating through a binary blob to populate a struct. After the + * first failure (buffer overrun), successive calls will always fail. + */ +void* StatefulSkip(MemcpyState* state, uint64_t len); + /* Copy [len] bytes into [dst] only if there's enough data to read according * to [state]. * On success, return [dst] and update [state]. diff --git a/vboot_firmware/lib/kernel_image_fw.c b/vboot_firmware/lib/kernel_image_fw.c index 6cfb1970f5..10c01b5027 100644 --- a/vboot_firmware/lib/kernel_image_fw.c +++ b/vboot_firmware/lib/kernel_image_fw.c @@ -10,6 +10,7 @@ #include "cryptolib.h" #include "rollback_index.h" +#include "stateful_util.h" #include "utility.h" /* Macro to determine the size of a field structure in the KernelImage @@ -187,61 +188,91 @@ int VerifyKernelData(RSAPublicKey* kernel_sign_key, int VerifyKernelHeader(const uint8_t* firmware_key_blob, const uint8_t* kernel_header_blob, + uint64_t kernel_header_blob_len, const int dev_mode, - const uint8_t** expected_kernel_signature, - RSAPublicKey** kernel_sign_key, - int* kernel_sign_algorithm, - uint64_t* kernel_len) { + KernelImage *image, + RSAPublicKey** kernel_sign_key) { int error_code; int firmware_sign_algorithm; /* Firmware signing key algorithm. */ + int kernel_sign_algorithm; /* Kernel signing key algorithm. */ int kernel_sign_key_len, kernel_key_signature_len, kernel_signature_len, header_len; + uint64_t kernel_len; const uint8_t* header_ptr = NULL; /* Pointer to key header. */ const uint8_t* preamble_ptr = NULL; /* Pointer to start of preamble. */ - const uint8_t* kernel_sign_key_ptr = NULL; /* Pointer to signing key. */ + MemcpyState st; - /* Note: All the offset calculations are based on struct FirmwareImage which - * is defined in include/firmware_image.h. */ + /* Note: All the offset calculations are based on struct KernelImage which + * is defined in include/kernel_image_fw.h. */ + st.remaining_buf = (void *)kernel_header_blob; + st.remaining_len = kernel_header_blob_len; + st.overrun = 0; - /* Compare magic bytes. */ - if (SafeMemcmp(kernel_header_blob, KERNEL_MAGIC, KERNEL_MAGIC_SIZE)) + /* Clear destination image struct */ + Memset(image, 0, sizeof(KernelImage)); + + /* Read and compare magic bytes. */ + StatefulMemcpy(&st, &image->magic, KERNEL_MAGIC_SIZE); + if (SafeMemcmp(image->magic, KERNEL_MAGIC, KERNEL_MAGIC_SIZE)) { return VERIFY_KERNEL_WRONG_MAGIC; + } + StatefulMemcpy(&st, &image->header_version, FIELD_LEN(header_version)); + StatefulMemcpy(&st, &image->header_len, FIELD_LEN(header_len)); + StatefulMemcpy(&st, &image->firmware_sign_algorithm, + FIELD_LEN(firmware_sign_algorithm)); + StatefulMemcpy(&st, &image->kernel_sign_algorithm, + FIELD_LEN(kernel_sign_algorithm)); + header_ptr = kernel_header_blob + KERNEL_MAGIC_SIZE; /* Only continue if header verification succeeds. */ if ((error_code = VerifyKernelKeyHeader(firmware_key_blob, header_ptr, dev_mode, &firmware_sign_algorithm, - kernel_sign_algorithm, + &kernel_sign_algorithm, &header_len))) { debug("VerifyKernelHeader: Kernel Key Header verification failed.\n"); return error_code; /* AKA jump to recovery. */ } - /* Parse signing key into RSAPublicKey structure since it is required multiple - * times. */ - kernel_sign_key_len = RSAProcessedKeySize(*kernel_sign_algorithm); - kernel_sign_key_ptr = header_ptr + (FIELD_LEN(header_version) + - FIELD_LEN(header_len) + - FIELD_LEN(firmware_sign_algorithm) + - FIELD_LEN(kernel_sign_algorithm) + - FIELD_LEN(kernel_key_version)); - *kernel_sign_key = RSAPublicKeyFromBuf(kernel_sign_key_ptr, + + /* Read pre-processed public half of the kernel signing key. */ + kernel_sign_key_len = RSAProcessedKeySize(kernel_sign_algorithm); + StatefulMemcpy(&st, &image->kernel_key_version, + FIELD_LEN(kernel_key_version)); + image->kernel_sign_key = (uint8_t*)st.remaining_buf; + StatefulSkip(&st, kernel_sign_key_len); + StatefulMemcpy(&st, image->header_checksum, FIELD_LEN(header_checksum)); + + /* Parse signing key into RSAPublicKey structure since it is + * required multiple times. */ + *kernel_sign_key = RSAPublicKeyFromBuf(image->kernel_sign_key, kernel_sign_key_len); - kernel_signature_len = siglen_map[*kernel_sign_algorithm]; + kernel_signature_len = siglen_map[kernel_sign_algorithm]; kernel_key_signature_len = siglen_map[firmware_sign_algorithm]; + image->kernel_key_signature = (uint8_t*)st.remaining_buf; + StatefulSkip(&st, kernel_signature_len); /* Only continue if preamble verification succeeds. */ - preamble_ptr = (header_ptr + header_len + kernel_key_signature_len); + /* TODO: should pass the remaining len into VerifyKernelPreamble() */ + preamble_ptr = (const uint8_t*)st.remaining_buf; if ((error_code = VerifyKernelPreamble(*kernel_sign_key, preamble_ptr, - *kernel_sign_algorithm, - kernel_len))) { + kernel_sign_algorithm, + &kernel_len))) { RSAPublicKeyFree(*kernel_sign_key); return error_code; /* AKA jump to recovery. */ } - *expected_kernel_signature = (preamble_ptr + - GetKernelPreambleLen(*kernel_sign_algorithm) - - kernel_signature_len); /* Skip beginning of - * preamble. */ + + /* Copy preamble fields */ + StatefulMemcpy(&st, &image->kernel_version, FIELD_LEN(kernel_version)); + StatefulMemcpy(&st, &image->kernel_len, FIELD_LEN(kernel_len)); + StatefulMemcpy(&st, &image->bootloader_offset, FIELD_LEN(bootloader_offset)); + StatefulMemcpy(&st, &image->bootloader_size, FIELD_LEN(bootloader_size)); + StatefulMemcpy(&st, &image->padded_header_size, + FIELD_LEN(padded_header_size)); + image->kernel_signature = (uint8_t*)st.remaining_buf; + StatefulSkip(&st, kernel_signature_len); + image->preamble_signature = (uint8_t*)st.remaining_buf; + return 0; } diff --git a/vboot_firmware/lib/stateful_util.c b/vboot_firmware/lib/stateful_util.c index 4727eab9a0..389961b372 100644 --- a/vboot_firmware/lib/stateful_util.c +++ b/vboot_firmware/lib/stateful_util.c @@ -12,6 +12,18 @@ #include #include +void* StatefulSkip(MemcpyState* state, uint64_t len) { + if (state->overrun) + return NULL; + if (len > state->remaining_len) { + state->overrun = 1; + return NULL; + } + state->remaining_buf += len; + state->remaining_len -= len; + return state; // have to return something non-NULL +} + void* StatefulMemcpy(MemcpyState* state, void* dst, uint64_t len) { if (state->overrun) diff --git a/vboot_firmware/linktest/main.c b/vboot_firmware/linktest/main.c index d6496b4153..d713ff7c28 100644 --- a/vboot_firmware/linktest/main.c +++ b/vboot_firmware/linktest/main.c @@ -26,7 +26,7 @@ int main(void) VerifyKernelKeyHeader(0, 0, 0, 0, 0, 0); VerifyKernelPreamble(0, 0, 0, 0); VerifyKernelData(0, 0, 0, 0, 0); - VerifyKernelHeader(0, 0, 0, 0, 0, 0, 0); + VerifyKernelHeader(0, 0, 0, 0, 0, 0); VerifyKernel(0, 0, 0); GetLogicalKernelVersion(0); VerifyKernelDriver_f(0, 0, 0, 0);