VBoot Reference: Refactor Pass 1: Split {firmware|kernel}_image

This CL refactors verified boot firmware and kernel image functions into firmware and userland portions. Data Types and Functions that need to be a part of the final firmware implementation reside in files with "_fw" suffix - firmware_image_fw.{c|h} and kernel_image_fw.{c|h}.

Also some Makefile cleanups.

Review URL: http://codereview.chromium.org/1599001
This commit is contained in:
Gaurav Shah
2010-03-30 18:56:07 -07:00
parent 091dfdf425
commit ed9c96a7aa
14 changed files with 1127 additions and 1058 deletions

View File

@@ -8,9 +8,27 @@
#include "utility.h" #include "utility.h"
#include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
void error(const char *format, ...) {
va_list ap;
va_start(ap, format);
fprintf(stderr, "ERROR: ");
vfprintf(stderr, format, ap);
va_end(ap);
exit(1);
}
void debug(const char *format, ...) {
va_list ap;
va_start(ap, format);
fprintf(stderr, "WARNING: ");
vfprintf(stderr, format, ap);
va_end(ap);
}
void* Malloc(size_t size) { void* Malloc(size_t size) {
void* p = malloc(size); void* p = malloc(size);
if (!p) { if (!p) {

View File

@@ -2,53 +2,14 @@
* Use of this source code is governed by a BSD-style license that can be * Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file. * found in the LICENSE file.
* *
* Data structure and API definitions for a verified boot firmware image. * API definitions for a verified boot firmware image.
* (Userland Portion)
*/ */
#ifndef VBOOT_REFERENCE_FIRMWARE_IMAGE_H_ #ifndef VBOOT_REFERENCE_FIRMWARE_IMAGE_H_
#define VBOOT_REFERENCE_FIRMWARE_IMAGE_H_ #define VBOOT_REFERENCE_FIRMWARE_IMAGE_H_
#include <inttypes.h> #include "firmware_image_fw.h"
#include "rsa.h"
#include "sha.h"
#define FIRMWARE_MAGIC "CHROMEOS"
#define FIRMWARE_MAGIC_SIZE 8
#define FIRMWARE_PREAMBLE_SIZE 8
/* RSA 8192 and SHA-512. */
#define ROOT_SIGNATURE_ALGORITHM 11
#define ROOT_SIGNATURE_ALGORITHM_STRING "11"
typedef struct FirmwareImage {
uint8_t magic[FIRMWARE_MAGIC_SIZE];
/* Key Header */
uint16_t header_len; /* Length of the header. */
uint16_t firmware_sign_algorithm; /* Signature algorithm used by the signing
* key. */
uint16_t firmware_key_version; /* Key Version# for preventing rollbacks. */
uint8_t* firmware_sign_key; /* Pre-processed public half of signing key. */
uint8_t header_checksum[SHA512_DIGEST_SIZE]; /* SHA-512 hash of the header.*/
uint8_t firmware_key_signature[RSA8192NUMBYTES]; /* Signature of the header
* above. */
/* Firmware Preamble. */
uint16_t firmware_version; /* Firmware Version# for preventing rollbacks.*/
uint64_t firmware_len; /* Length of the rest of the R/W firmware data. */
uint8_t preamble[FIRMWARE_PREAMBLE_SIZE]; /* Remaining preamble data.*/
uint8_t* preamble_signature; /* Signature over the preamble. */
/* The firmware signature comes first as it may allow us to parallelize
* the firmware data fetch and RSA public operation.
*/
uint8_t* firmware_signature; /* Signature on the Preamble +
[firmware_data]. */
uint8_t* firmware_data; /* Rest of firmware data */
} FirmwareImage;
/* Allocate and return a new FirmwareImage structure. */ /* Allocate and return a new FirmwareImage structure. */
FirmwareImage* FirmwareImageNew(void); FirmwareImage* FirmwareImageNew(void);
@@ -100,77 +61,11 @@ uint8_t* GetFirmwareBlob(const FirmwareImage* image, uint64_t* blob_len);
int WriteFirmwareImage(const char* input_file, int WriteFirmwareImage(const char* input_file,
const FirmwareImage* image); const FirmwareImage* image);
/* Pretty print the contents of [image]. Only headers and metadata information /* Pretty print the contents of [image]. Only headers and metadata information
* is printed. * is printed.
*/ */
void PrintFirmwareImage(const FirmwareImage* image); void PrintFirmwareImage(const FirmwareImage* image);
/* Error Codes for VerifyFirmware* family of functions. */
#define VERIFY_FIRMWARE_SUCCESS 0
#define VERIFY_FIRMWARE_INVALID_IMAGE 1
#define VERIFY_FIRMWARE_ROOT_SIGNATURE_FAILED 2
#define VERIFY_FIRMWARE_INVALID_ALGORITHM 3
#define VERIFY_FIRMWARE_PREAMBLE_SIGNATURE_FAILED 4
#define VERIFY_FIRMWARE_SIGNATURE_FAILED 5
#define VERIFY_FIRMWARE_WRONG_MAGIC 6
#define VERIFY_FIRMWARE_WRONG_HEADER_CHECKSUM 7
#define VERIFY_FIRMWARE_KEY_ROLLBACK 8
#define VERIFY_FIRMWARE_VERSION_ROLLBACK 9
#define VERIFY_FIRMWARE_MAX 10 /* Total number of error codes. */
extern char* kVerifyFirmwareErrors[VERIFY_FIRMWARE_MAX];
/* Checks for the sanity of the firmware header pointed by [header_blob].
*
* On success, put signature algorithm in [algorithm], header length
* in [header_len], and return 0.
* Else, return error code on failure.
*/
int VerifyFirmwareHeader(const uint8_t* root_key_blob,
const uint8_t* header_blob,
int* algorithm,
int* header_len);
/* Checks the preamble signature on firmware preamble pointed by
* [preamble_blob] using the signing key [sign_key].
*
* On success, put firmware length into [firmware_len], and return 0.
* Else, return error code on failure.
*/
int VerifyFirmwarePreamble(RSAPublicKey* sign_key,
const uint8_t* preamble_blob,
int algorithm,
uint64_t* firmware_len);
/* Checks the signature on the preamble + firmware data at
* [preamble_start] and [firmware_data_start].
* The length of the actual firmware data is firmware_len and it is assumed to
* be prepended with the signature whose size depends on the signature_algorithm
* [algorithm]. This signature also covers the preamble data (but not the
* preamble signature itself).
*
* Return 0 on success, error code on failure.
*/
int VerifyFirmwareData(RSAPublicKey* sign_key,
const uint8_t* preamble_start,
const uint8_t* firmware_data_start,
uint64_t firmware_len,
int algorithm);
/* Performs a chained verify of the firmware blob [firmware_blob].
*
* Returns 0 on success, error code on failure.
*
* NOTE: The length of the firmware blob is derived from reading the fields
* in the first few bytes of the buffer. This might look risky but in firmware
* land, the start address of the firmware_blob will always be fixed depending
* on the memory map on the particular platform. In addition, the signature on
* length itself is checked early in the verification process for extra safety.
*/
int VerifyFirmware(const uint8_t* root_key_blob,
const uint8_t* firmware_blob);
/* Performs a chained verify of the firmware [image]. /* Performs a chained verify of the firmware [image].
* *
* Returns 0 on success, error code on failure. * Returns 0 on success, error code on failure.
@@ -195,26 +90,4 @@ int AddFirmwareKeySignature(FirmwareImage* image, const char* root_key_file);
*/ */
int AddFirmwareSignature(FirmwareImage* image, const char* signing_key_file); int AddFirmwareSignature(FirmwareImage* image, const char* signing_key_file);
/* Returns the logical version of a firmware blob which is calculated as
* (firmware_key_version << 16 | firmware_version). */
uint32_t GetLogicalFirmwareVersion(uint8_t* firmware_blob);
#define BOOT_FIRMWARE_A_CONTINUE 1
#define BOOT_FIRMWARE_B_CONTINUE 2
#define BOOT_FIRMWARE_RECOVERY_CONTINUE 3
/* This function is the driver used by the RO firmware to
* determine which copy of the firmware to boot from. It performs
* the requisite rollback index checking, including updating them,
* if required.
*
* Returns the code path to follow. It is one of:
* BOOT_FIRMWARE_A_CONTINUE Boot from Firmware A
* BOOT_FIRMWARE_B_CONTINUE Boot from Firmware B
* BOOT_FIRMWARE_RECOVERY_CONTINUE Jump to recovery mode
*/
int VerifyFirmwareDriver_f(uint8_t* root_key_blob,
uint8_t* firmwareA,
uint8_t* firmwareB);
#endif /* VBOOT_REFERENCE_FIRMWARE_IMAGE_H_ */ #endif /* VBOOT_REFERENCE_FIRMWARE_IMAGE_H_ */

142
include/firmware_image_fw.h Normal file
View File

@@ -0,0 +1,142 @@
/* Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*
* Data structure and API definitions for a verified boot firmware image.
* (Firmware Portion)
*/
#ifndef VBOOT_REFERENCE_FIRMWARE_IMAGE_FW_H_
#define VBOOT_REFERENCE_FIRMWARE_IMAGE_FW_H_
#include <stdint.h>
#include "rsa.h"
#include "sha.h"
#define FIRMWARE_MAGIC "CHROMEOS"
#define FIRMWARE_MAGIC_SIZE 8
#define FIRMWARE_PREAMBLE_SIZE 8
/* RSA 8192 and SHA-512. */
#define ROOT_SIGNATURE_ALGORITHM 11
#define ROOT_SIGNATURE_ALGORITHM_STRING "11"
typedef struct FirmwareImage {
uint8_t magic[FIRMWARE_MAGIC_SIZE];
/* Key Header */
uint16_t header_len; /* Length of the header. */
uint16_t firmware_sign_algorithm; /* Signature algorithm used by the signing
* key. */
uint16_t firmware_key_version; /* Key Version# for preventing rollbacks. */
uint8_t* firmware_sign_key; /* Pre-processed public half of signing key. */
uint8_t header_checksum[SHA512_DIGEST_SIZE]; /* SHA-512 hash of the header.*/
uint8_t firmware_key_signature[RSA8192NUMBYTES]; /* Signature of the header
* above. */
/* Firmware Preamble. */
uint16_t firmware_version; /* Firmware Version# for preventing rollbacks.*/
uint64_t firmware_len; /* Length of the rest of the R/W firmware data. */
uint8_t preamble[FIRMWARE_PREAMBLE_SIZE]; /* Remaining preamble data.*/
uint8_t* preamble_signature; /* Signature over the preamble. */
/* The firmware signature comes first as it may allow us to parallelize
* the firmware data fetch and RSA public operation.
*/
uint8_t* firmware_signature; /* Signature on the Preamble +
[firmware_data]. */
uint8_t* firmware_data; /* Rest of firmware data */
} FirmwareImage;
/* Error Codes for VerifyFirmware* family of functions. */
#define VERIFY_FIRMWARE_SUCCESS 0
#define VERIFY_FIRMWARE_INVALID_IMAGE 1
#define VERIFY_FIRMWARE_ROOT_SIGNATURE_FAILED 2
#define VERIFY_FIRMWARE_INVALID_ALGORITHM 3
#define VERIFY_FIRMWARE_PREAMBLE_SIGNATURE_FAILED 4
#define VERIFY_FIRMWARE_SIGNATURE_FAILED 5
#define VERIFY_FIRMWARE_WRONG_MAGIC 6
#define VERIFY_FIRMWARE_WRONG_HEADER_CHECKSUM 7
#define VERIFY_FIRMWARE_KEY_ROLLBACK 8
#define VERIFY_FIRMWARE_VERSION_ROLLBACK 9
#define VERIFY_FIRMWARE_MAX 10 /* Total number of error codes. */
extern char* kVerifyFirmwareErrors[VERIFY_FIRMWARE_MAX];
/* Checks for the sanity of the firmware header pointed by [header_blob].
*
* On success, put signature algorithm in [algorithm], header length
* in [header_len], and return 0.
* Else, return error code on failure.
*/
int VerifyFirmwareHeader(const uint8_t* root_key_blob,
const uint8_t* header_blob,
int* algorithm,
int* header_len);
/* Checks the preamble signature on firmware preamble pointed by
* [preamble_blob] using the signing key [sign_key].
*
* On success, put firmware length into [firmware_len], and return 0.
* Else, return error code on failure.
*/
int VerifyFirmwarePreamble(RSAPublicKey* sign_key,
const uint8_t* preamble_blob,
int algorithm,
uint64_t* firmware_len);
/* Checks the signature on the preamble + firmware data at
* [preamble_start] and [firmware_data_start].
* The length of the actual firmware data is firmware_len and it is assumed to
* be prepended with the signature whose size depends on the signature_algorithm
* [algorithm]. This signature also covers the preamble data (but not the
* preamble signature itself).
*
* Return 0 on success, error code on failure.
*/
int VerifyFirmwareData(RSAPublicKey* sign_key,
const uint8_t* preamble_start,
const uint8_t* firmware_data_start,
uint64_t firmware_len,
int algorithm);
/* Performs a chained verify of the firmware blob [firmware_blob].
*
* Returns 0 on success, error code on failure.
*
* NOTE: The length of the firmware blob is derived from reading the fields
* in the first few bytes of the buffer. This might look risky but in firmware
* land, the start address of the firmware_blob will always be fixed depending
* on the memory map on the particular platform. In addition, the signature on
* length itself is checked early in the verification process for extra safety.
*/
int VerifyFirmware(const uint8_t* root_key_blob,
const uint8_t* firmware_blob);
/* Returns the logical version of a firmware blob which is calculated as
* (firmware_key_version << 16 | firmware_version). */
uint32_t GetLogicalFirmwareVersion(uint8_t* firmware_blob);
#define BOOT_FIRMWARE_A_CONTINUE 1
#define BOOT_FIRMWARE_B_CONTINUE 2
#define BOOT_FIRMWARE_RECOVERY_CONTINUE 3
/* This function is the driver used by the RO firmware to
* determine which copy of the firmware to boot from. It performs
* the requisite rollback index checking, including updating them,
* if required.
*
* Returns the code path to follow. It is one of:
* BOOT_FIRMWARE_A_CONTINUE Boot from Firmware A
* BOOT_FIRMWARE_B_CONTINUE Boot from Firmware B
* BOOT_FIRMWARE_RECOVERY_CONTINUE Jump to recovery mode
*/
int VerifyFirmwareDriver_f(uint8_t* root_key_blob,
uint8_t* firmwareA,
uint8_t* firmwareB);
#endif /* VBOOT_REFERENCE_FIRMWARE_IMAGE_FW_H_ */

View File

@@ -2,73 +2,14 @@
* Use of this source code is governed by a BSD-style license that can be * Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file. * found in the LICENSE file.
* *
* Data structure and API definitions for a verified boot kernel image. * API definitions for a generating and manipulating verified boot kernel images.
* (Userland portion.)
*/ */
#ifndef VBOOT_REFERENCE_KERNEL_IMAGE_H_ #ifndef VBOOT_REFERENCE_KERNEL_IMAGE_H_
#define VBOOT_REFERENCE_KERNEL_IMAGE_H_ #define VBOOT_REFERENCE_KERNEL_IMAGE_H_
#include <inttypes.h> #include "kernel_image_fw.h"
#include "rsa.h"
#include "sha.h"
#define KERNEL_MAGIC "CHROMEOS"
#define KERNEL_MAGIC_SIZE 8
#define KERNEL_CMD_LINE_SIZE 4096
#define DEV_MODE_ENABLED 1
#define DEV_MODE_DISABLED 0
/* Kernel config file options according to the Chrome OS drive map design. */
typedef struct kconfig_options {
uint32_t version[2]; /* Configuration file version. */
uint8_t cmd_line[KERNEL_CMD_LINE_SIZE]; /* Kernel command line option string
* terminated by a NULL character. */
uint64_t kernel_len; /* Size of the kernel. */
uint64_t kernel_load_addr; /* Load address in memory for the kernel image */
uint64_t kernel_entry_addr; /* Address to jump to after kernel is loaded. */
} kconfig_options;
typedef struct KernelImage {
uint8_t magic[KERNEL_MAGIC_SIZE];
/* Key header */
uint16_t header_version; /* Header version. */
uint16_t header_len; /* Length of the header. */
uint16_t firmware_sign_algorithm; /* Signature algorithm used by the firmware
* signing key (used to sign this kernel
* header. */
uint16_t kernel_sign_algorithm; /* Signature algorithm used by the kernel
* signing key. */
uint16_t kernel_key_version; /* Key Version# for preventing rollbacks. */
uint8_t* kernel_sign_key; /* Pre-processed public half of signing key. */
/* TODO(gauravsh): Do we need a choice of digest algorithms for the header
* checksum? */
uint8_t header_checksum[SHA512_DIGEST_SIZE]; /* SHA-512 Crytographic hash of
* the concatenation of the
* header fields, i.e.
* [header_len,
* firmware_sign_algorithm,
* sign_algorithm, sign_key,
* key_version] */
uint8_t* kernel_key_signature; /* Signature of the header above. */
uint16_t kernel_version; /* Kernel Version# for preventing rollbacks. */
kconfig_options options; /* Other kernel/bootloader options. */
uint8_t* config_signature; /* Signature of the kernel config file. */
/* The kernel signature comes first as it may allow us to parallelize
* the kernel data fetch and RSA public key operation.
*/
uint8_t* kernel_signature; /* Signature on the concatenation of
* [kernel_version], [options] and
* [kernel_data]. */
uint8_t* kernel_data; /* Actual kernel data. */
} KernelImage;
/* Allocate and return a new KernelImage structure. */ /* Allocate and return a new KernelImage structure. */
KernelImage* KernelImageNew(void); KernelImage* KernelImageNew(void);
@@ -125,79 +66,6 @@ int WriteKernelImage(const char* input_file,
*/ */
void PrintKernelImage(const KernelImage* image); void PrintKernelImage(const KernelImage* image);
/* Error Codes for VerifyFirmware. */
#define VERIFY_KERNEL_SUCCESS 0
#define VERIFY_KERNEL_INVALID_IMAGE 1
#define VERIFY_KERNEL_KEY_SIGNATURE_FAILED 2
#define VERIFY_KERNEL_INVALID_ALGORITHM 3
#define VERIFY_KERNEL_CONFIG_SIGNATURE_FAILED 4
#define VERIFY_KERNEL_SIGNATURE_FAILED 5
#define VERIFY_KERNEL_WRONG_MAGIC 6
#define VERIFY_KERNEL_MAX 7 /* Generic catch-all. */
extern char* kVerifyKernelErrors[VERIFY_KERNEL_MAX];
/* Checks for the sanity of the kernel header pointed by [kernel_header_blob].
* If [dev_mode] is enabled, also checks the firmware key signature using the
* pre-processed public firmware signing key [firmware_sign_key_blob].
*
* On success, put firmware signature algorithm in [firmware_algorithm],
* kernel signature algorithm in [kernel_algorithm], kernel header
* length in [header_len], and return 0.
* Else, return error code on failure.
*/
int VerifyKernelHeader(const uint8_t* firmware_sign_key_blob,
const uint8_t* kernel_header_blob,
const int dev_mode,
int* firmware_algorithm,
int* kernel_algorithm,
int* header_len);
/* Checks the kernel config (analogous to preamble for firmware) signature on
* kernel config pointed by [kernel_config_blob] using the signing key
* [kernel_sign_key].
*
* On success, put kernel length into [kernel_len], and return 0.
* Else, return error code on failure.
*/
int VerifyKernelConfig(RSAPublicKey* kernel_sign_key,
const uint8_t* kernel_config_blob,
int algorithm,
uint64_t* kernel_len);
/* Checks the signature on the kernel data at location [kernel_data_start].
* The length of the actual kernel data is kernel _len and it is assumed to
* be prepended with the signature whose size depends on the signature_algorithm
* [algorithm].
*
* Return 0 on success, error code on failure.
*/
int VerifyKernelData(RSAPublicKey* kernel_sign_key,
const uint8_t* kernel_config_start,
const uint8_t* kernel_data_start,
uint64_t kernel_len,
int algorithm);
/* 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.
*
* TODO(gauravsh): Does the dev mode only effect the R/W firmware verification,
* or kernel verification, or both?
*
* Returns 0 on success, error code on failure.
*
* NOTE: The length of the kernel blob is derived from reading the fields
* in the first few bytes of the buffer. This might look risky but in firmware
* land, the start address of the kernel_blob will always be fixed depending
* on the memory map on the particular platform. In addition, the signature on
* length itself is checked early in the verification process for extra safety.
*/
int VerifyKernel(const uint8_t* signing_key_blob,
const uint8_t* kernel_blob,
const int dev_mode);
/* Performs a chained verify of the kernel [image]. If [dev_mode] is /* Performs a chained verify of the kernel [image]. If [dev_mode] is
* 0 (inactive), then the [firmware_signing_key] is used to verify the signature * 0 (inactive), then the [firmware_signing_key] is used to verify the signature
* of the signing key, else the check is skipped. * of the signing key, else the check is skipped.
@@ -227,44 +95,6 @@ int AddKernelKeySignature(KernelImage* image, const char* firmware_key_file);
int AddKernelSignature(KernelImage* image, int AddKernelSignature(KernelImage* image,
const char* kernel_sigining_key_file); const char* kernel_sigining_key_file);
/* Returns the logical version of a kernel blob which is calculated as
* (kernel_key_version << 16 | kernel_version). */
uint32_t GetLogicalKernelVersion(uint8_t* kernel_blob);
#define BOOT_KERNEL_A_CONTINUE 1
#define BOOT_KERNEL_B_CONTINUE 2
#define BOOT_KERNEL_RECOVERY_CONTINUE 3
/* Contains information about the kernel paritition
* gleaned from the GPT partition table.
*
* Based on the Chromium OS Drive Map design document by
* rspangler@chromium.org.
*
*/
typedef struct kernel_entry {
uint8_t* kernel_blob; /* Pointer to actual kernel. */
uint8_t boot_priority; /* 15 = highest, 1 = lowest, 0 = not bootable. */
uint8_t boot_tries_remaining; /* Used when boot_priority = 0. */
uint8_t boot_success_flag; /* Set to 1 on successful boot by AU. */
} kernel_entry;
void PrintKernelEntry(kernel_entry* entry); void PrintKernelEntry(kernel_entry* entry);
/* This function is the driver used by the RW firmware to
* determine which copy of the kernel to boot from. It performs
* the requisite priority and remaining tries checking for a specific
* kernel partition, does rollback index checking, including updating
* if required.
*
* Returns the code path to follow. It is one of:
* BOOT_KERNEL_A_CONTINUE Boot from Kenrel A
* BOOT_KERNEL_B_CONTINUE Boot from Kernel B
* BOOT_KERNEL_RECOVERY_CONTINUE Jump to recovery mode
*/
int VerifyKernelDriver_f(uint8_t* firmware_key_blob,
kernel_entry* kernelA,
kernel_entry* kernelB,
int dev_mode);
#endif /* VBOOT_REFERENCE_KERNEL_IMAGE_H_ */ #endif /* VBOOT_REFERENCE_KERNEL_IMAGE_H_ */

183
include/kernel_image_fw.h Normal file
View File

@@ -0,0 +1,183 @@
/* Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*
* Data structure and API definitions for a verified boot kernel image.
* (Firmware Portion)
*/
#ifndef VBOOT_REFERENCE_KERNEL_IMAGE_FW_H_
#define VBOOT_REFERENCE_KERNEL_IMAGE_FW_H_
#include <stdint.h>
#include "rsa.h"
#include "sha.h"
#define KERNEL_MAGIC "CHROMEOS"
#define KERNEL_MAGIC_SIZE 8
#define KERNEL_CMD_LINE_SIZE 4096
#define DEV_MODE_ENABLED 1
#define DEV_MODE_DISABLED 0
/* Kernel config file options according to the Chrome OS drive map design. */
typedef struct kconfig_options {
uint32_t version[2]; /* Configuration file version. */
uint8_t cmd_line[KERNEL_CMD_LINE_SIZE]; /* Kernel command line option string
* terminated by a NULL character. */
uint64_t kernel_len; /* Size of the kernel. */
uint64_t kernel_load_addr; /* Load address in memory for the kernel image */
uint64_t kernel_entry_addr; /* Address to jump to after kernel is loaded. */
} kconfig_options;
typedef struct KernelImage {
uint8_t magic[KERNEL_MAGIC_SIZE];
/* Key header */
uint16_t header_version; /* Header version. */
uint16_t header_len; /* Length of the header. */
uint16_t firmware_sign_algorithm; /* Signature algorithm used by the firmware
* signing key (used to sign this kernel
* header. */
uint16_t kernel_sign_algorithm; /* Signature algorithm used by the kernel
* signing key. */
uint16_t kernel_key_version; /* Key Version# for preventing rollbacks. */
uint8_t* kernel_sign_key; /* Pre-processed public half of signing key. */
/* TODO(gauravsh): Do we need a choice of digest algorithms for the header
* checksum? */
uint8_t header_checksum[SHA512_DIGEST_SIZE]; /* SHA-512 Crytographic hash of
* the concatenation of the
* header fields, i.e.
* [header_len,
* firmware_sign_algorithm,
* sign_algorithm, sign_key,
* key_version] */
uint8_t* kernel_key_signature; /* Signature of the header above. */
uint16_t kernel_version; /* Kernel Version# for preventing rollbacks. */
kconfig_options options; /* Other kernel/bootloader options. */
uint8_t* config_signature; /* Signature of the kernel config file. */
/* The kernel signature comes first as it may allow us to parallelize
* the kernel data fetch and RSA public key operation.
*/
uint8_t* kernel_signature; /* Signature on the concatenation of
* [kernel_version], [options] and
* [kernel_data]. */
uint8_t* kernel_data; /* Actual kernel data. */
} KernelImage;
/* Error Codes for VerifyFirmware. */
#define VERIFY_KERNEL_SUCCESS 0
#define VERIFY_KERNEL_INVALID_IMAGE 1
#define VERIFY_KERNEL_KEY_SIGNATURE_FAILED 2
#define VERIFY_KERNEL_INVALID_ALGORITHM 3
#define VERIFY_KERNEL_CONFIG_SIGNATURE_FAILED 4
#define VERIFY_KERNEL_SIGNATURE_FAILED 5
#define VERIFY_KERNEL_WRONG_MAGIC 6
#define VERIFY_KERNEL_MAX 7 /* Generic catch-all. */
extern char* kVerifyKernelErrors[VERIFY_KERNEL_MAX];
/* Checks for the sanity of the kernel header pointed by [kernel_header_blob].
* If [dev_mode] is enabled, also checks the firmware key signature using the
* pre-processed public firmware signing key [firmware_sign_key_blob].
*
* On success, put firmware signature algorithm in [firmware_algorithm],
* kernel signature algorithm in [kernel_algorithm], kernel header
* length in [header_len], and return 0.
* Else, return error code on failure.
*/
int VerifyKernelHeader(const uint8_t* firmware_sign_key_blob,
const uint8_t* kernel_header_blob,
const int dev_mode,
int* firmware_algorithm,
int* kernel_algorithm,
int* header_len);
/* Checks the kernel config (analogous to preamble for firmware) signature on
* kernel config pointed by [kernel_config_blob] using the signing key
* [kernel_sign_key].
*
* On success, put kernel length into [kernel_len], and return 0.
* Else, return error code on failure.
*/
int VerifyKernelConfig(RSAPublicKey* kernel_sign_key,
const uint8_t* kernel_config_blob,
int algorithm,
uint64_t* kernel_len);
/* Checks the signature on the kernel data at location [kernel_data_start].
* The length of the actual kernel data is kernel _len and it is assumed to
* be prepended with the signature whose size depends on the signature_algorithm
* [algorithm].
*
* Return 0 on success, error code on failure.
*/
int VerifyKernelData(RSAPublicKey* kernel_sign_key,
const uint8_t* kernel_config_start,
const uint8_t* kernel_data_start,
uint64_t kernel_len,
int algorithm);
/* 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.
*
* TODO(gauravsh): Does the dev mode only effect the R/W firmware verification,
* or kernel verification, or both?
*
* Returns 0 on success, error code on failure.
*
* NOTE: The length of the kernel blob is derived from reading the fields
* in the first few bytes of the buffer. This might look risky but in firmware
* land, the start address of the kernel_blob will always be fixed depending
* on the memory map on the particular platform. In addition, the signature on
* length itself is checked early in the verification process for extra safety.
*/
int VerifyKernel(const uint8_t* signing_key_blob,
const uint8_t* kernel_blob,
const int dev_mode);
/* Returns the logical version of a kernel blob which is calculated as
* (kernel_key_version << 16 | kernel_version). */
uint32_t GetLogicalKernelVersion(uint8_t* kernel_blob);
#define BOOT_KERNEL_A_CONTINUE 1
#define BOOT_KERNEL_B_CONTINUE 2
#define BOOT_KERNEL_RECOVERY_CONTINUE 3
/* Contains information about the kernel paritition
* gleaned from the GPT partition table.
*
* Based on the Chromium OS Drive Map design document by
* rspangler@chromium.org.
*
*/
typedef struct kernel_entry {
uint8_t* kernel_blob; /* Pointer to actual kernel. */
uint8_t boot_priority; /* 15 = highest, 1 = lowest, 0 = not bootable. */
uint8_t boot_tries_remaining; /* Used when boot_priority = 0. */
uint8_t boot_success_flag; /* Set to 1 on successful boot by AU. */
} kernel_entry;
/* This function is the driver used by the RW firmware to
* determine which copy of the kernel to boot from. It performs
* the requisite priority and remaining tries checking for a specific
* kernel partition, does rollback index checking, including updating
* if required.
*
* Returns the code path to follow. It is one of:
* BOOT_KERNEL_A_CONTINUE Boot from Kenrel A
* BOOT_KERNEL_B_CONTINUE Boot from Kernel B
* BOOT_KERNEL_RECOVERY_CONTINUE Jump to recovery mode
*/
int VerifyKernelDriver_f(uint8_t* firmware_key_blob,
kernel_entry* kernelA,
kernel_entry* kernelB,
int dev_mode);
#endif /* VBOOT_REFERENCE_KERNEL_IMAGE_FW_H_ */

View File

@@ -16,41 +16,6 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#define POSSIBLY_UNUSED __attribute__((unused))
#ifdef __STRICT_ANSI__
#define INLINE
#else
#define INLINE inline
#endif
/* Outputs an error message and quits the program.
*/
POSSIBLY_UNUSED
static void error(const char *format, ...) {
va_list ap;
va_start(ap, format);
fprintf(stderr, "ERROR: ");
vfprintf(stderr, format, ap);
va_end(ap);
exit(1);
}
/* Outputs a warning and continues.
*/
POSSIBLY_UNUSED
static void warning(const char *format, ...) {
va_list ap;
va_start(ap, format);
fprintf(stderr, "WARNING: ");
vfprintf(stderr, format, ap);
va_end(ap);
}
#define assert(expr) do { if (!(expr)) { \
error("assert fail: %s at %s:%d\n", \
#expr, __FILE__, __LINE__); }} while(0)
/* Call this first. /* Call this first.
*/ */
void TlclLibinit(void); void TlclLibinit(void);

View File

@@ -10,9 +10,20 @@
#ifndef VBOOT_REFERENCE_UTILITY_H_ #ifndef VBOOT_REFERENCE_UTILITY_H_
#define VBOOT_REFERENCE_UTILITY_H_ #define VBOOT_REFERENCE_UTILITY_H_
#include <inttypes.h> #include <stdint.h>
#include <string.h> #include <string.h>
/* Outputs an error message and quits. */
void error(const char *format, ...);
/* Outputs debug/warning messages. */
void debug(const char *format, ...);
#define assert(expr) do { if (!(expr)) { \
error("assert fail: %s at %s:%d\n", \
#expr, __FILE__, __LINE__); }} while(0)
/* Combine [msw] and [lsw] uint16s to a uint32_t with its [msw] and /* Combine [msw] and [lsw] uint16s to a uint32_t with its [msw] and
* [lsw] forming the most and least signficant 16-bit words. * [lsw] forming the most and least signficant 16-bit words.
*/ */

View File

@@ -8,26 +8,31 @@ INCLUDES ?= -I../include/
TOP ?= ../ TOP ?= ../
BASE_LIBS = $(TOP)/crypto/libcrypto.a $(TOP)/common/libcommon.a BASE_LIBS = $(TOP)/crypto/libcrypto.a $(TOP)/common/libcommon.a
IMAGE_LIBS = $(TOP)/utils/firmware_image.o $(TOP)/utils/kernel_image.o IMAGE_LIBS = $(TOP)/utils/firmware_image.o \
$(TOP)/utils/firmware_image_fw.o \
$(TOP)/utils/kernel_image.o \
$(TOP)/utils/kernel_image_fw.o
UTIL_LIBS = $(TOP)/utils/file_keys.o $(TOP)/utils/signature_digest.o UTIL_LIBS = $(TOP)/utils/file_keys.o $(TOP)/utils/signature_digest.o
LIBS = $(IMAGE_LIBS) $(UTIL_LIBS) -lcrypto $(BASE_LIBS) LIBS = $(IMAGE_LIBS) $(UTIL_LIBS) -lcrypto $(BASE_LIBS)
tests: big_firmware_tests \ TEST_BINS = big_firmware_tests \
big_kernel_tests \ big_kernel_tests \
firmware_image_tests \ firmware_image_tests \
firmware_rollback_tests \ firmware_rollback_tests \
firmware_splicing_tests \ firmware_splicing_tests \
firmware_verify_benchmark \ firmware_verify_benchmark \
kernel_image_tests \ kernel_image_tests \
kernel_rollback_tests \ kernel_rollback_tests \
kernel_splicing_tests \ kernel_splicing_tests \
kernel_verify_benchmark \ kernel_verify_benchmark \
rsa_padding_test \ rsa_padding_test \
rsa_verify_benchmark \ rsa_verify_benchmark \
sha_benchmark \ sha_benchmark \
sha_tests \ sha_tests \
verify_firmware_fuzz_driver \ verify_firmware_fuzz_driver \
verify_kernel_fuzz_driver verify_kernel_fuzz_driver
all: $(TEST_BINS)
big_firmware_tests: big_firmware_tests.c rollback_index_mock.c test_common.c big_firmware_tests: big_firmware_tests.c rollback_index_mock.c test_common.c
$(CC) $(CFLAGS) $(INCLUDES) $^ -o $@ $(LIBS) $(CC) $(CFLAGS) $(INCLUDES) $^ -o $@ $(LIBS)
@@ -87,19 +92,4 @@ verify_kernel_fuzz_driver: verify_kernel_fuzz_driver.c rollback_index_mock.c
$(CC) $(CFLAGS) $(INCLUDES) $^ -o $@ $(LIBS) $(CC) $(CFLAGS) $(INCLUDES) $^ -o $@ $(LIBS)
clean: clean:
rm -f big_firmware_tests \ rm -f $(TEST_BINS)
big_kernel_tests \
firmware_image_tests \
firmware_rollback_tests \
firmware_splicing_tests \
firmware_verify_benchmark \
kernel_image_tests \
kernel_rollback_tests \
kernel_splicing_tests \
kernel_verify_benchmark \
rsa_padding_test \
rsa_verify_benchmark \
sha_benchmark \
sha_tests \
verify_firmware_fuzz_driver \
verify_kernel_fuzz_driver

View File

@@ -8,43 +8,53 @@ CFLAGS ?= -Wall -DNDEBUG -O3 -Werror
INCLUDES ?= -I../include/ INCLUDES ?= -I../include/
TOP ?= ../ TOP ?= ../
LIBS = firmware_image.o kernel_image.o signature_digest.o file_keys.o \ LIBS = file_keys.o \
rollback_index.o firmware_image.o \
firmware_image_fw.o \
kernel_image.o \
kernel_image_fw.o \
rollback_index.o \
signature_digest.o
FIRMWARELIBS = $(TOP)/crypto/libcrypto.a $(TOP)/common/libcommon.a FIRMWARELIBS = $(TOP)/crypto/libcrypto.a $(TOP)/common/libcommon.a
all: dumpRSAPublicKey verify_data file_keys.o signature_digest.o \ TARGET_BINS = $(LIBS) \
firmware_image.o kernel_image.o signature_digest.o \ dumpRSAPublicKey \
signature_digest_utility firmware_utility kernel_utility \ firmware_utility \
rollback_index.o kernel_utility \
signature_digest_utility \
verify_data
all: $(TARGET_BINS)
.c.o:
$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
dumpRSAPublicKey: dumpRSAPublicKey.c dumpRSAPublicKey: dumpRSAPublicKey.c
$(CC) $(CFLAGS) $< -o $@ -lcrypto $(CC) $(CFLAGS) $< -o $@ -lcrypto
verify_data: verify_data.c $(LIBS) $(FIRMWARELIBS) firmware_image_fw.o: firmware_image_fw.c
$(CC) $(CFLAGS) $(INCLUDES) $< -o $@ $(LIBS) $(FIRMWARELIBS) -lcrypto $(CC) $(CFLAGS) -ansi $(INCLUDES) -c $^ -o $@
signature_digest_utility: signature_digest_utility.c $(LIBS) $(FIRMWARELIBS)
$(CC) $(CFLAGS) $(INCLUDES) $< -o $@ $(LIBS) $(FIRMWARELIBS) -lcrypto
firmware_utility: firmware_utility.cc $(LIBS) $(FIRMWARELIBS) firmware_utility: firmware_utility.cc $(LIBS) $(FIRMWARELIBS)
$(CXX) $(CFLAGS) $(INCLUDES) -ggdb -D__STDC_LIMIT_MACROS $< \ $(CXX) $(CFLAGS) $(INCLUDES) -ggdb -D__STDC_LIMIT_MACROS $< \
-o $@ $(FIRMWARELIBS) $(LIBS) $(TOP)/common/libcommon.a \ -o $@ $(FIRMWARELIBS) $(LIBS) $(TOP)/common/libcommon.a \
-lcrypto -lcrypto
kernel_image_fw.o: kernel_image_fw.c
$(CC) $(CFLAGS) -ansi $(INCLUDES) -c $< -o $@
kernel_utility: kernel_utility.cc $(LIBS) $(FIRMWARELIBS) kernel_utility: kernel_utility.cc $(LIBS) $(FIRMWARELIBS)
$(CXX) $(CFLAGS) $(INCLUDES) -ggdb -D__STDC_LIMIT_MACROS $< \ $(CXX) $(CFLAGS) $(INCLUDES) -ggdb -D__STDC_LIMIT_MACROS $< \
-o $@ $(FIRMWARELIBS) $(LIBS) $(TOP)/common/libcommon.a \ -o $@ $(FIRMWARELIBS) $(LIBS) $(TOP)/common/libcommon.a \
-lcrypto -lcrypto
.c.o: signature_digest_utility: signature_digest_utility.c $(LIBS) $(FIRMWARELIBS)
$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@ $(CC) $(CFLAGS) $(INCLUDES) $< -o $@ $(LIBS) $(FIRMWARELIBS) -lcrypto
firmware_image.o: firmware_image.c verify_data: verify_data.c $(LIBS) $(FIRMWARELIBS)
$(CC) -ansi $(CFLAGS) $(INCLUDES) -c $< -o $@ $(CC) $(CFLAGS) $(INCLUDES) $< -o $@ $(LIBS) $(FIRMWARELIBS) -lcrypto
kernel_image.o: kernel_image.c
$(CC) -ansi $(CFLAGS) $(INCLUDES) -c $< -o $@
clean: clean:
rm -f dumpRSAPublicKey verify_data signature_digest firmware_utility \ rm -f $(TARGET_BINS) $(LIBS)
kernel_utility signature_digest_utility $(LIBS)

View File

@@ -8,7 +8,7 @@
* /tools/DumpPublicKey.java). Uses the OpenSSL X509 and BIGNUM library. * /tools/DumpPublicKey.java). Uses the OpenSSL X509 and BIGNUM library.
*/ */
#include <inttypes.h> #include <stdint.h>
#include <openssl/bn.h> #include <openssl/bn.h>
#include <openssl/evp.h> #include <openssl/evp.h>
#include <openssl/pem.h> #include <openssl/pem.h>

View File

@@ -9,14 +9,12 @@
#include <fcntl.h> #include <fcntl.h>
#include <limits.h> #include <limits.h>
#include <stdio.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
#include "file_keys.h" #include "file_keys.h"
#include "padding.h" #include "padding.h"
#include "rollback_index.h"
#include "rsa_utility.h" #include "rsa_utility.h"
#include "sha_utility.h" #include "sha_utility.h"
#include "signature_digest.h" #include "signature_digest.h"
@@ -71,7 +69,7 @@ FirmwareImage* ReadFirmwareImage(const char* input_file) {
/* Read and compare magic bytes. */ /* Read and compare magic bytes. */
StatefulMemcpy(&st, &image->magic, FIRMWARE_MAGIC_SIZE); StatefulMemcpy(&st, &image->magic, FIRMWARE_MAGIC_SIZE);
if (SafeMemcmp(image->magic, FIRMWARE_MAGIC, FIRMWARE_MAGIC_SIZE)) { if (SafeMemcmp(image->magic, FIRMWARE_MAGIC, FIRMWARE_MAGIC_SIZE)) {
fprintf(stderr, "Wrong Firmware Magic.\n"); debug("Wrong Firmware Magic.\n");
Free(firmware_buf); Free(firmware_buf);
return NULL; return NULL;
} }
@@ -92,8 +90,8 @@ FirmwareImage* ReadFirmwareImage(const char* input_file) {
/* Check whether the header length is correct. */ /* Check whether the header length is correct. */
header_len = GetFirmwareHeaderLen(image); header_len = GetFirmwareHeaderLen(image);
if (header_len != image->header_len) { if (header_len != image->header_len) {
fprintf(stderr, "Header length mismatch. Got: %d Expected: %d\n", debug("Header length mismatch. Got: %d Expected: %d\n",
image->header_len, header_len); image->header_len, header_len);
Free(firmware_buf); Free(firmware_buf);
return NULL; return NULL;
} }
@@ -109,7 +107,7 @@ FirmwareImage* ReadFirmwareImage(const char* input_file) {
CalculateFirmwareHeaderChecksum(image, header_checksum); CalculateFirmwareHeaderChecksum(image, header_checksum);
if (SafeMemcmp(header_checksum, image->header_checksum, if (SafeMemcmp(header_checksum, image->header_checksum,
FIELD_LEN(header_checksum))) { FIELD_LEN(header_checksum))) {
fprintf(stderr, "Invalid firmware header checksum!\n"); debug("Invalid firmware header checksum!\n");
Free(firmware_buf); Free(firmware_buf);
return NULL; return NULL;
} }
@@ -271,17 +269,17 @@ int WriteFirmwareImage(const char* input_file,
if (!image) if (!image)
return 0; return 0;
if (-1 == (fd = creat(input_file, S_IRWXU))) { if (-1 == (fd = creat(input_file, S_IRWXU))) {
fprintf(stderr, "Couldn't open file for writing.\n"); debug("Couldn't open file for writing.\n");
return 0; return 0;
} }
firmware_blob = GetFirmwareBlob(image, &blob_len); firmware_blob = GetFirmwareBlob(image, &blob_len);
if (!firmware_blob) { if (!firmware_blob) {
fprintf(stderr, "Couldn't create firmware blob from FirmwareImage.\n"); debug("Couldn't create firmware blob from FirmwareImage.\n");
return 0; return 0;
} }
if (blob_len != write(fd, firmware_blob, blob_len)) { if (blob_len != write(fd, firmware_blob, blob_len)) {
fprintf(stderr, "Couldn't write Firmware Image to file: %s\n", input_file); debug("Couldn't write Firmware Image to file: %s\n", input_file);
Free(firmware_blob); Free(firmware_blob);
close(fd); close(fd);
return 0; return 0;
@@ -296,7 +294,7 @@ void PrintFirmwareImage(const FirmwareImage* image) {
return; return;
/* Print header. */ /* Print header. */
printf("Header Length = %d\n" debug("Header Length = %d\n"
"Firmware Signature Algorithm = %s\n" "Firmware Signature Algorithm = %s\n"
"Firmware Key Version = %d\n\n", "Firmware Key Version = %d\n\n",
image->header_len, image->header_len,
@@ -304,201 +302,13 @@ void PrintFirmwareImage(const FirmwareImage* image) {
image->firmware_key_version); image->firmware_key_version);
/* TODO(gauravsh): Output hash and key signature here? */ /* TODO(gauravsh): Output hash and key signature here? */
/* Print preamble. */ /* Print preamble. */
printf("Firmware Version = %d\n" debug("Firmware Version = %d\n"
"Firmware Length = %" PRIu64 "\n\n", "Firmware Length = %" PRIu64 "\n\n",
image->firmware_version, image->firmware_version,
image->firmware_len); image->firmware_len);
/* Output key signature here? */ /* Output key signature here? */
} }
char* kVerifyFirmwareErrors[VERIFY_FIRMWARE_MAX] = {
"Success.",
"Invalid Image.",
"Root Key Signature Failed.",
"Invalid Verification Algorithm.",
"Preamble Signature Failed.",
"Firmware Signature Failed.",
"Wrong Firmware Magic.",
"Invalid Firmware Header Checksum.",
"Firmware Signing Key Rollback.",
"Firmware Version Rollback."
};
int VerifyFirmwareHeader(const uint8_t* root_key_blob,
const uint8_t* header_blob,
int* algorithm,
int* header_len) {
int firmware_sign_key_len;
int root_key_len;
uint16_t hlen, algo;
uint8_t* header_checksum = NULL;
/* Base Offset for the header_checksum field. Actual offset is
* this + firmware_sign_key_len. */
int base_header_checksum_offset = (FIELD_LEN(header_len) +
FIELD_LEN(firmware_sign_algorithm) +
FIELD_LEN(firmware_key_version));
root_key_len = RSAProcessedKeySize(ROOT_SIGNATURE_ALGORITHM);
Memcpy(&hlen, header_blob, sizeof(hlen));
Memcpy(&algo,
header_blob + FIELD_LEN(firmware_sign_algorithm),
sizeof(algo));
if (algo >= kNumAlgorithms)
return VERIFY_FIRMWARE_INVALID_ALGORITHM;
*algorithm = (int) algo;
firmware_sign_key_len = RSAProcessedKeySize(*algorithm);
/* Verify that header len is correct. */
if (hlen != (base_header_checksum_offset +
firmware_sign_key_len +
FIELD_LEN(header_checksum)))
return VERIFY_FIRMWARE_INVALID_IMAGE;
*header_len = (int) hlen;
/* Verify if the hash of the header is correct. */
header_checksum = DigestBuf(header_blob,
*header_len - FIELD_LEN(header_checksum),
SHA512_DIGEST_ALGORITHM);
if (SafeMemcmp(header_checksum,
header_blob + (base_header_checksum_offset +
firmware_sign_key_len),
FIELD_LEN(header_checksum))) {
Free(header_checksum);
return VERIFY_FIRMWARE_WRONG_HEADER_CHECKSUM;
}
Free(header_checksum);
/* Root key signature on the firmware signing key is always checked
* irrespective of dev mode. */
if (!RSAVerifyBinary_f(root_key_blob, NULL, /* Key to use */
header_blob, /* Data to verify */
*header_len, /* Length of data */
header_blob + *header_len, /* Expected Signature */
ROOT_SIGNATURE_ALGORITHM))
return VERIFY_FIRMWARE_ROOT_SIGNATURE_FAILED;
return 0;
}
int VerifyFirmwarePreamble(RSAPublicKey* firmware_sign_key,
const uint8_t* preamble_blob,
int algorithm,
uint64_t* firmware_len) {
uint64_t len;
int preamble_len;
uint16_t firmware_version;
Memcpy(&firmware_version, preamble_blob, sizeof(firmware_version));
preamble_len = (FIELD_LEN(firmware_version) +
FIELD_LEN(firmware_len) +
FIELD_LEN(preamble));
if (!RSAVerifyBinary_f(NULL, firmware_sign_key, /* Key to use */
preamble_blob, /* Data to verify */
preamble_len, /* Length of data */
preamble_blob + preamble_len, /* Expected Signature */
algorithm))
return VERIFY_FIRMWARE_PREAMBLE_SIGNATURE_FAILED;
Memcpy(&len, preamble_blob + FIELD_LEN(firmware_version),
sizeof(len));
*firmware_len = len;
return 0;
}
int VerifyFirmwareData(RSAPublicKey* firmware_sign_key,
const uint8_t* preamble_start,
const uint8_t* firmware_data_start,
uint64_t firmware_len,
int algorithm) {
int signature_len = siglen_map[algorithm];
uint8_t* digest;
DigestContext ctx;
/* Since the firmware signature is over the preamble and the firmware data,
* which does not form a contiguous region of memory, we calculate the
* message digest ourselves. */
DigestInit(&ctx, algorithm);
DigestUpdate(&ctx, preamble_start, GetFirmwarePreambleLen());
DigestUpdate(&ctx, firmware_data_start + signature_len, firmware_len);
digest = DigestFinal(&ctx);
if (!RSAVerifyBinaryWithDigest_f(
NULL, firmware_sign_key, /* Key to use. */
digest, /* Digest of the data to verify. */
firmware_data_start, /* Expected Signature */
algorithm)) {
Free(digest);
return VERIFY_FIRMWARE_SIGNATURE_FAILED;
}
Free(digest);
return 0;
}
int VerifyFirmware(const uint8_t* root_key_blob,
const uint8_t* firmware_blob) {
int error_code = 0;
int algorithm; /* Signing key algorithm. */
RSAPublicKey* firmware_sign_key = NULL;
int firmware_sign_key_len, signature_len, header_len;
uint64_t firmware_len;
const uint8_t* header_ptr = NULL; /* Pointer to header. */
const uint8_t* firmware_sign_key_ptr = NULL; /* Pointer to signing key. */
const uint8_t* preamble_ptr = NULL; /* Pointer to preamble block. */
const uint8_t* firmware_ptr = NULL; /* Pointer to firmware signature/data. */
/* Note: All the offset calculations are based on struct FirmwareImage which
* is defined in include/firmware_image.h. */
/* Compare magic bytes. */
if (SafeMemcmp(firmware_blob, FIRMWARE_MAGIC, FIRMWARE_MAGIC_SIZE))
return VERIFY_FIRMWARE_WRONG_MAGIC;
header_ptr = firmware_blob + FIRMWARE_MAGIC_SIZE;
/* Only continue if header verification succeeds. */
if ((error_code = VerifyFirmwareHeader(root_key_blob, header_ptr,
&algorithm, &header_len)))
return error_code; /* AKA jump to revovery. */
/* Parse signing key into RSAPublicKey structure since it is required multiple
* times. */
firmware_sign_key_len = RSAProcessedKeySize(algorithm);
firmware_sign_key_ptr = header_ptr + (FIELD_LEN(header_len) +
FIELD_LEN(firmware_sign_algorithm) +
FIELD_LEN(firmware_key_version));
firmware_sign_key = RSAPublicKeyFromBuf(firmware_sign_key_ptr,
firmware_sign_key_len);
signature_len = siglen_map[algorithm];
/* Only continue if preamble verification succeeds. */
preamble_ptr = (header_ptr + header_len +
FIELD_LEN(firmware_key_signature));
if ((error_code = VerifyFirmwarePreamble(firmware_sign_key, preamble_ptr,
algorithm,
&firmware_len))) {
RSAPublicKeyFree(firmware_sign_key);
fprintf(stderr, "Couldn't verify Firmware preamble.\n");
return error_code; /* AKA jump to recovery. */
}
/* Only continue if firmware data verification succeeds. */
firmware_ptr = (preamble_ptr +
GetFirmwarePreambleLen() +
signature_len);
if ((error_code = VerifyFirmwareData(firmware_sign_key, preamble_ptr,
firmware_ptr,
firmware_len,
algorithm))) {
RSAPublicKeyFree(firmware_sign_key);
fprintf(stderr, "Couldn't verify Firmware data.\n");
return error_code; /* AKA jump to recovery. */
}
RSAPublicKeyFree(firmware_sign_key);
return 0; /* Success! */
}
int VerifyFirmwareImage(const RSAPublicKey* root_key, int VerifyFirmwareImage(const RSAPublicKey* root_key,
const FirmwareImage* image) { const FirmwareImage* image) {
RSAPublicKey* firmware_sign_key = NULL; RSAPublicKey* firmware_sign_key = NULL;
@@ -662,114 +472,3 @@ int AddFirmwareSignature(FirmwareImage* image, const char* signing_key_file) {
Free(preamble_blob); Free(preamble_blob);
return 1; return 1;
} }
uint32_t GetLogicalFirmwareVersion(uint8_t* firmware_blob) {
uint16_t firmware_key_version;
uint16_t firmware_version;
uint16_t firmware_sign_algorithm;
int firmware_sign_key_len;
Memcpy(&firmware_sign_algorithm,
firmware_blob + (FIELD_LEN(magic) + /* Offset to field. */
FIELD_LEN(header_len)),
sizeof(firmware_sign_algorithm));
Memcpy(&firmware_key_version,
firmware_blob + (FIELD_LEN(magic) + /* Offset to field. */
FIELD_LEN(header_len) +
FIELD_LEN(firmware_sign_algorithm)),
sizeof(firmware_key_version));
if (firmware_sign_algorithm >= kNumAlgorithms)
return 0;
firmware_sign_key_len = RSAProcessedKeySize(firmware_sign_algorithm);
Memcpy(&firmware_version,
firmware_blob + (FIELD_LEN(magic) + /* Offset to field. */
FIELD_LEN(header_len) +
FIELD_LEN(firmware_key_version) +
firmware_sign_key_len +
FIELD_LEN(header_checksum) +
FIELD_LEN(firmware_key_signature)),
sizeof(firmware_version));
return CombineUint16Pair(firmware_key_version, firmware_version);
}
int VerifyFirmwareDriver_f(uint8_t* root_key_blob,
uint8_t* firmwareA,
uint8_t* firmwareB) {
/* Contains the logical firmware version (32-bit) which is calculated as
* (firmware_key_version << 16 | firmware_version) where
* [firmware_key_version] [firmware_version] are both 16-bit.
*/
uint32_t firmwareA_lversion, firmwareB_lversion;
uint8_t firmwareA_is_verified = 0; /* Whether firmwareA verify succeeded. */
uint32_t min_lversion; /* Minimum of firmware A and firmware lversion. */
uint32_t stored_lversion; /* Stored logical version in the TPM. */
/* Initialize the TPM since we'll be reading the rollback indices. */
SetupTPM();
/* We get the key versions by reading directly from the image blobs without
* any additional (expensive) sanity checking on the blob since it's faster to
* outright reject a firmware with an older firmware key version. A malformed
* or corrupted firmware blob will still fail when VerifyFirmware() is called
* on it.
*/
firmwareA_lversion = GetLogicalFirmwareVersion(firmwareA);
firmwareB_lversion = GetLogicalFirmwareVersion(firmwareB);
min_lversion = Min(firmwareA_lversion, firmwareB_lversion);
stored_lversion = CombineUint16Pair(GetStoredVersion(FIRMWARE_KEY_VERSION),
GetStoredVersion(FIRMWARE_VERSION));
/* Always try FirmwareA first. */
if (VERIFY_FIRMWARE_SUCCESS == VerifyFirmware(root_key_blob, firmwareA))
firmwareA_is_verified = 1;
if (firmwareA_is_verified && (stored_lversion < firmwareA_lversion)) {
/* Stored version may need to be updated but only if FirmwareB
* is successfully verified and has a logical version greater than
* the stored logical version. */
if (stored_lversion < firmwareB_lversion) {
if (VERIFY_FIRMWARE_SUCCESS == VerifyFirmware(root_key_blob, firmwareB)) {
WriteStoredVersion(FIRMWARE_KEY_VERSION,
(uint16_t) (min_lversion >> 16));
WriteStoredVersion(FIRMWARE_VERSION,
(uint16_t) (min_lversion & 0x00FFFF));
stored_lversion = min_lversion; /* Update stored version as it's used
* later. */
}
}
}
/* Lock Firmware TPM rollback indices from further writes. */
/* TODO(gauravsh): Figure out if these can be combined into one
* 32-bit location since we seem to always use them together. This can help
* us minimize the number of NVRAM writes/locks (which are limited over flash
* memory lifetimes.
*/
LockStoredVersion(FIRMWARE_KEY_VERSION);
LockStoredVersion(FIRMWARE_VERSION);
/* Determine which firmware (if any) to jump to.
*
* We always attempt to jump to FirmwareA first. If verification of FirmwareA
* fails, we try FirmwareB. In all cases, if the firmware successfully
* verified but is a rollback, we jump to recovery.
*
* Note: This means that if FirmwareA verified successfully and is a
* rollback, then no attempt is made to check FirmwareB. We still jump to
* recovery. FirmwareB is only used as a backup in case FirmwareA gets
* corrupted. Since newer firmware updates are always written to A,
* the case where firmware A is verified but a rollback should not occur in
* normal operation.
*/
if (firmwareA_is_verified) {
if (stored_lversion <= firmwareA_lversion)
return BOOT_FIRMWARE_A_CONTINUE;
} else {
/* If FirmwareA was not valid, then we skipped over the
* check to update the rollback indices and a Verify of FirmwareB wasn't
* attempted.
* If FirmwareB is not a rollback, then we attempt to do the verification.
*/
if (stored_lversion <= firmwareB_lversion &&
(VERIFY_FIRMWARE_SUCCESS == VerifyFirmware(root_key_blob, firmwareB)))
return BOOT_FIRMWARE_B_CONTINUE;
}
/* D'oh: No bootable firmware. */
return BOOT_FIRMWARE_RECOVERY_CONTINUE;
}

323
utils/firmware_image_fw.c Normal file
View File

@@ -0,0 +1,323 @@
/* Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*
* Functions for verifying a verified boot firmware image.
* (Firmware Portion)
*/
#include "firmware_image_fw.h"
#include "padding.h"
#include "rollback_index.h"
#include "rsa_utility.h"
#include "sha_utility.h"
#include "utility.h"
/* Macro to determine the size of a field structure in the FirmwareImage
* structure. */
#define FIELD_LEN(field) (sizeof(((FirmwareImage*)0)->field))
char* kVerifyFirmwareErrors[VERIFY_FIRMWARE_MAX] = {
"Success.",
"Invalid Image.",
"Root Key Signature Failed.",
"Invalid Verification Algorithm.",
"Preamble Signature Failed.",
"Firmware Signature Failed.",
"Wrong Firmware Magic.",
"Invalid Firmware Header Checksum.",
"Firmware Signing Key Rollback.",
"Firmware Version Rollback."
};
int VerifyFirmwareHeader(const uint8_t* root_key_blob,
const uint8_t* header_blob,
int* algorithm,
int* header_len) {
int firmware_sign_key_len;
int root_key_len;
uint16_t hlen, algo;
uint8_t* header_checksum = NULL;
/* Base Offset for the header_checksum field. Actual offset is
* this + firmware_sign_key_len. */
int base_header_checksum_offset = (FIELD_LEN(header_len) +
FIELD_LEN(firmware_sign_algorithm) +
FIELD_LEN(firmware_key_version));
root_key_len = RSAProcessedKeySize(ROOT_SIGNATURE_ALGORITHM);
Memcpy(&hlen, header_blob, sizeof(hlen));
Memcpy(&algo,
header_blob + FIELD_LEN(firmware_sign_algorithm),
sizeof(algo));
if (algo >= kNumAlgorithms)
return VERIFY_FIRMWARE_INVALID_ALGORITHM;
*algorithm = (int) algo;
firmware_sign_key_len = RSAProcessedKeySize(*algorithm);
/* Verify that header len is correct. */
if (hlen != (base_header_checksum_offset +
firmware_sign_key_len +
FIELD_LEN(header_checksum)))
return VERIFY_FIRMWARE_INVALID_IMAGE;
*header_len = (int) hlen;
/* Verify if the hash of the header is correct. */
header_checksum = DigestBuf(header_blob,
*header_len - FIELD_LEN(header_checksum),
SHA512_DIGEST_ALGORITHM);
if (SafeMemcmp(header_checksum,
header_blob + (base_header_checksum_offset +
firmware_sign_key_len),
FIELD_LEN(header_checksum))) {
Free(header_checksum);
return VERIFY_FIRMWARE_WRONG_HEADER_CHECKSUM;
}
Free(header_checksum);
/* Root key signature on the firmware signing key is always checked
* irrespective of dev mode. */
if (!RSAVerifyBinary_f(root_key_blob, NULL, /* Key to use */
header_blob, /* Data to verify */
*header_len, /* Length of data */
header_blob + *header_len, /* Expected Signature */
ROOT_SIGNATURE_ALGORITHM))
return VERIFY_FIRMWARE_ROOT_SIGNATURE_FAILED;
return 0;
}
int VerifyFirmwarePreamble(RSAPublicKey* firmware_sign_key,
const uint8_t* preamble_blob,
int algorithm,
uint64_t* firmware_len) {
uint64_t len;
int preamble_len;
uint16_t firmware_version;
Memcpy(&firmware_version, preamble_blob, sizeof(firmware_version));
preamble_len = (FIELD_LEN(firmware_version) +
FIELD_LEN(firmware_len) +
FIELD_LEN(preamble));
if (!RSAVerifyBinary_f(NULL, firmware_sign_key, /* Key to use */
preamble_blob, /* Data to verify */
preamble_len, /* Length of data */
preamble_blob + preamble_len, /* Expected Signature */
algorithm))
return VERIFY_FIRMWARE_PREAMBLE_SIGNATURE_FAILED;
Memcpy(&len, preamble_blob + FIELD_LEN(firmware_version),
sizeof(len));
*firmware_len = len;
return 0;
}
int VerifyFirmwareData(RSAPublicKey* firmware_sign_key,
const uint8_t* preamble_start,
const uint8_t* firmware_data_start,
uint64_t firmware_len,
int algorithm) {
int signature_len = siglen_map[algorithm];
uint8_t* digest;
DigestContext ctx;
/* Since the firmware signature is over the preamble and the firmware data,
* which does not form a contiguous region of memory, we calculate the
* message digest ourselves. */
DigestInit(&ctx, algorithm);
DigestUpdate(&ctx, preamble_start,
(FIELD_LEN(firmware_version) +
FIELD_LEN(firmware_len) +
FIELD_LEN(preamble)));
DigestUpdate(&ctx, firmware_data_start + signature_len, firmware_len);
digest = DigestFinal(&ctx);
if (!RSAVerifyBinaryWithDigest_f(
NULL, firmware_sign_key, /* Key to use. */
digest, /* Digest of the data to verify. */
firmware_data_start, /* Expected Signature */
algorithm)) {
Free(digest);
return VERIFY_FIRMWARE_SIGNATURE_FAILED;
}
Free(digest);
return 0;
}
int VerifyFirmware(const uint8_t* root_key_blob,
const uint8_t* firmware_blob) {
int error_code = 0;
int algorithm; /* Signing key algorithm. */
RSAPublicKey* firmware_sign_key = NULL;
int firmware_sign_key_len, signature_len, header_len;
uint64_t firmware_len;
const uint8_t* header_ptr = NULL; /* Pointer to header. */
const uint8_t* firmware_sign_key_ptr = NULL; /* Pointer to signing key. */
const uint8_t* preamble_ptr = NULL; /* Pointer to preamble block. */
const uint8_t* firmware_ptr = NULL; /* Pointer to firmware signature/data. */
/* Note: All the offset calculations are based on struct FirmwareImage which
* is defined in include/firmware_image.h. */
/* Compare magic bytes. */
if (SafeMemcmp(firmware_blob, FIRMWARE_MAGIC, FIRMWARE_MAGIC_SIZE))
return VERIFY_FIRMWARE_WRONG_MAGIC;
header_ptr = firmware_blob + FIRMWARE_MAGIC_SIZE;
/* Only continue if header verification succeeds. */
if ((error_code = VerifyFirmwareHeader(root_key_blob, header_ptr,
&algorithm, &header_len)))
return error_code; /* AKA jump to revovery. */
/* Parse signing key into RSAPublicKey structure since it is required multiple
* times. */
firmware_sign_key_len = RSAProcessedKeySize(algorithm);
firmware_sign_key_ptr = header_ptr + (FIELD_LEN(header_len) +
FIELD_LEN(firmware_sign_algorithm) +
FIELD_LEN(firmware_key_version));
firmware_sign_key = RSAPublicKeyFromBuf(firmware_sign_key_ptr,
firmware_sign_key_len);
signature_len = siglen_map[algorithm];
/* Only continue if preamble verification succeeds. */
preamble_ptr = (header_ptr + header_len +
FIELD_LEN(firmware_key_signature));
if ((error_code = VerifyFirmwarePreamble(firmware_sign_key, preamble_ptr,
algorithm,
&firmware_len))) {
RSAPublicKeyFree(firmware_sign_key);
debug("Couldn't verify Firmware preamble.\n");
return error_code; /* AKA jump to recovery. */
}
/* Only continue if firmware data verification succeeds. */
firmware_ptr = (preamble_ptr +
(FIELD_LEN(firmware_version) + /* Skip the preamble. */
FIELD_LEN(firmware_len) +
FIELD_LEN(preamble)) +
signature_len);
if ((error_code = VerifyFirmwareData(firmware_sign_key, preamble_ptr,
firmware_ptr,
firmware_len,
algorithm))) {
RSAPublicKeyFree(firmware_sign_key);
debug("Couldn't verify Firmware data.\n");
return error_code; /* AKA jump to recovery. */
}
RSAPublicKeyFree(firmware_sign_key);
return 0; /* Success! */
}
uint32_t GetLogicalFirmwareVersion(uint8_t* firmware_blob) {
uint16_t firmware_key_version;
uint16_t firmware_version;
uint16_t firmware_sign_algorithm;
int firmware_sign_key_len;
Memcpy(&firmware_sign_algorithm,
firmware_blob + (FIELD_LEN(magic) + /* Offset to field. */
FIELD_LEN(header_len)),
sizeof(firmware_sign_algorithm));
Memcpy(&firmware_key_version,
firmware_blob + (FIELD_LEN(magic) + /* Offset to field. */
FIELD_LEN(header_len) +
FIELD_LEN(firmware_sign_algorithm)),
sizeof(firmware_key_version));
if (firmware_sign_algorithm >= kNumAlgorithms)
return 0;
firmware_sign_key_len = RSAProcessedKeySize(firmware_sign_algorithm);
Memcpy(&firmware_version,
firmware_blob + (FIELD_LEN(magic) + /* Offset to field. */
FIELD_LEN(header_len) +
FIELD_LEN(firmware_key_version) +
firmware_sign_key_len +
FIELD_LEN(header_checksum) +
FIELD_LEN(firmware_key_signature)),
sizeof(firmware_version));
return CombineUint16Pair(firmware_key_version, firmware_version);
}
int VerifyFirmwareDriver_f(uint8_t* root_key_blob,
uint8_t* firmwareA,
uint8_t* firmwareB) {
/* Contains the logical firmware version (32-bit) which is calculated as
* (firmware_key_version << 16 | firmware_version) where
* [firmware_key_version] [firmware_version] are both 16-bit.
*/
uint32_t firmwareA_lversion, firmwareB_lversion;
uint8_t firmwareA_is_verified = 0; /* Whether firmwareA verify succeeded. */
uint32_t min_lversion; /* Minimum of firmware A and firmware lversion. */
uint32_t stored_lversion; /* Stored logical version in the TPM. */
/* Initialize the TPM since we'll be reading the rollback indices. */
SetupTPM();
/* We get the key versions by reading directly from the image blobs without
* any additional (expensive) sanity checking on the blob since it's faster to
* outright reject a firmware with an older firmware key version. A malformed
* or corrupted firmware blob will still fail when VerifyFirmware() is called
* on it.
*/
firmwareA_lversion = GetLogicalFirmwareVersion(firmwareA);
firmwareB_lversion = GetLogicalFirmwareVersion(firmwareB);
min_lversion = Min(firmwareA_lversion, firmwareB_lversion);
stored_lversion = CombineUint16Pair(GetStoredVersion(FIRMWARE_KEY_VERSION),
GetStoredVersion(FIRMWARE_VERSION));
/* Always try FirmwareA first. */
if (VERIFY_FIRMWARE_SUCCESS == VerifyFirmware(root_key_blob, firmwareA))
firmwareA_is_verified = 1;
if (firmwareA_is_verified && (stored_lversion < firmwareA_lversion)) {
/* Stored version may need to be updated but only if FirmwareB
* is successfully verified and has a logical version greater than
* the stored logical version. */
if (stored_lversion < firmwareB_lversion) {
if (VERIFY_FIRMWARE_SUCCESS == VerifyFirmware(root_key_blob, firmwareB)) {
WriteStoredVersion(FIRMWARE_KEY_VERSION,
(uint16_t) (min_lversion >> 16));
WriteStoredVersion(FIRMWARE_VERSION,
(uint16_t) (min_lversion & 0x00FFFF));
stored_lversion = min_lversion; /* Update stored version as it's used
* later. */
}
}
}
/* Lock Firmware TPM rollback indices from further writes. */
/* TODO(gauravsh): Figure out if these can be combined into one
* 32-bit location since we seem to always use them together. This can help
* us minimize the number of NVRAM writes/locks (which are limited over flash
* memory lifetimes.
*/
LockStoredVersion(FIRMWARE_KEY_VERSION);
LockStoredVersion(FIRMWARE_VERSION);
/* Determine which firmware (if any) to jump to.
*
* We always attempt to jump to FirmwareA first. If verification of FirmwareA
* fails, we try FirmwareB. In all cases, if the firmware successfully
* verified but is a rollback, we jump to recovery.
*
* Note: This means that if FirmwareA verified successfully and is a
* rollback, then no attempt is made to check FirmwareB. We still jump to
* recovery. FirmwareB is only used as a backup in case FirmwareA gets
* corrupted. Since newer firmware updates are always written to A,
* the case where firmware A is verified but a rollback should not occur in
* normal operation.
*/
if (firmwareA_is_verified) {
if (stored_lversion <= firmwareA_lversion)
return BOOT_FIRMWARE_A_CONTINUE;
} else {
/* If FirmwareA was not valid, then we skipped over the
* check to update the rollback indices and a Verify of FirmwareB wasn't
* attempted.
* If FirmwareB is not a rollback, then we attempt to do the verification.
*/
if (stored_lversion <= firmwareB_lversion &&
(VERIFY_FIRMWARE_SUCCESS == VerifyFirmware(root_key_blob, firmwareB)))
return BOOT_FIRMWARE_B_CONTINUE;
}
/* D'oh: No bootable firmware. */
return BOOT_FIRMWARE_RECOVERY_CONTINUE;
}

View File

@@ -3,6 +3,7 @@
* found in the LICENSE file. * found in the LICENSE file.
* *
* Functions for generating and manipulating a verified boot kernel image. * Functions for generating and manipulating a verified boot kernel image.
* (Userland portion)
*/ */
#include "kernel_image.h" #include "kernel_image.h"
@@ -75,7 +76,7 @@ KernelImage* ReadKernelImage(const char* input_file) {
StatefulMemcpy(&st, &image->magic, KERNEL_MAGIC_SIZE); StatefulMemcpy(&st, &image->magic, KERNEL_MAGIC_SIZE);
if (SafeMemcmp(image->magic, KERNEL_MAGIC, KERNEL_MAGIC_SIZE)) { if (SafeMemcmp(image->magic, KERNEL_MAGIC, KERNEL_MAGIC_SIZE)) {
fprintf(stderr, "Wrong Kernel Magic.\n"); debug("Wrong Kernel Magic.\n");
Free(kernel_buf); Free(kernel_buf);
return NULL; return NULL;
} }
@@ -107,7 +108,7 @@ KernelImage* ReadKernelImage(const char* input_file) {
/* Check whether key header length is correct. */ /* Check whether key header length is correct. */
header_len = GetKernelHeaderLen(image); header_len = GetKernelHeaderLen(image);
if (header_len != image->header_len) { if (header_len != image->header_len) {
fprintf(stderr, "Header length mismatch. Got: %d, Expected: %d\n", debug("Header length mismatch. Got: %d, Expected: %d\n",
image->header_len, header_len); image->header_len, header_len);
Free(kernel_buf); Free(kernel_buf);
return NULL; return NULL;
@@ -124,7 +125,7 @@ KernelImage* ReadKernelImage(const char* input_file) {
CalculateKernelHeaderChecksum(image, header_checksum); CalculateKernelHeaderChecksum(image, header_checksum);
if (SafeMemcmp(header_checksum, image->header_checksum, if (SafeMemcmp(header_checksum, image->header_checksum,
FIELD_LEN(header_checksum))) { FIELD_LEN(header_checksum))) {
fprintf(stderr, "Invalid kernel header checksum!\n"); debug("Invalid kernel header checksum!\n");
Free(kernel_buf); Free(kernel_buf);
return NULL; return NULL;
} }
@@ -307,17 +308,17 @@ int WriteKernelImage(const char* input_file,
if (!image) if (!image)
return 0; return 0;
if (-1 == (fd = creat(input_file, S_IRWXU))) { if (-1 == (fd = creat(input_file, S_IRWXU))) {
fprintf(stderr, "Couldn't open file for writing kernel image: %s\n", debug("Couldn't open file for writing kernel image: %s\n",
input_file); input_file);
return 0; return 0;
} }
kernel_blob = GetKernelBlob(image, &blob_len); kernel_blob = GetKernelBlob(image, &blob_len);
if (!kernel_blob) { if (!kernel_blob) {
fprintf(stderr, "Couldn't create kernel blob from KernelImage.\n"); debug("Couldn't create kernel blob from KernelImage.\n");
return 0; return 0;
} }
if (blob_len != write(fd, kernel_blob, blob_len)) { if (blob_len != write(fd, kernel_blob, blob_len)) {
fprintf(stderr, "Couldn't write Kernel Image to file: %s\n", debug("Couldn't write Kernel Image to file: %s\n",
input_file); input_file);
Free(kernel_blob); Free(kernel_blob);
@@ -361,212 +362,6 @@ void PrintKernelImage(const KernelImage* image) {
/* TODO(gauravsh): Output kernel signature here? */ /* TODO(gauravsh): Output kernel signature here? */
} }
char* kVerifyKernelErrors[VERIFY_KERNEL_MAX] = {
"Success.",
"Invalid Image.",
"Kernel Key Signature Failed.",
"Invalid Kernel Verification Algorithm.",
"Config Signature Failed.",
"Kernel Signature Failed.",
"Wrong Kernel Magic.",
};
int VerifyKernelHeader(const uint8_t* firmware_key_blob,
const uint8_t* header_blob,
const int dev_mode,
int* firmware_algorithm,
int* kernel_algorithm,
int* kernel_header_len) {
int kernel_sign_key_len;
int firmware_sign_key_len;
uint16_t header_version, header_len;
uint16_t firmware_sign_algorithm, kernel_sign_algorithm;
uint8_t* header_checksum = NULL;
/* Base Offset for the header_checksum field. Actual offset is
* this + kernel_sign_key_len. */
int base_header_checksum_offset = (FIELD_LEN(header_version) +
FIELD_LEN(header_len) +
FIELD_LEN(firmware_sign_algorithm) +
FIELD_LEN(kernel_sign_algorithm) +
FIELD_LEN(kernel_key_version));
Memcpy(&header_version, header_blob, sizeof(header_version));
Memcpy(&header_len, header_blob + FIELD_LEN(header_version),
sizeof(header_len));
Memcpy(&firmware_sign_algorithm,
header_blob + (FIELD_LEN(header_version) +
FIELD_LEN(header_len)),
sizeof(firmware_sign_algorithm));
Memcpy(&kernel_sign_algorithm,
header_blob + (FIELD_LEN(header_version) +
FIELD_LEN(header_len) +
FIELD_LEN(firmware_sign_algorithm)),
sizeof(kernel_sign_algorithm));
/* TODO(gauravsh): Make this return two different error types depending
* on whether the firmware or kernel signing algorithm is invalid. */
if (firmware_sign_algorithm >= kNumAlgorithms)
return VERIFY_KERNEL_INVALID_ALGORITHM;
if (kernel_sign_algorithm >= kNumAlgorithms)
return VERIFY_KERNEL_INVALID_ALGORITHM;
*firmware_algorithm = (int) firmware_sign_algorithm;
*kernel_algorithm = (int) kernel_sign_algorithm;
kernel_sign_key_len = RSAProcessedKeySize(kernel_sign_algorithm);
firmware_sign_key_len = RSAProcessedKeySize(firmware_sign_algorithm);
/* Verify if header len is correct? */
if (header_len != (base_header_checksum_offset +
kernel_sign_key_len +
FIELD_LEN(header_checksum))) {
fprintf(stderr, "VerifyKernelHeader: Header length mismatch\n");
return VERIFY_KERNEL_INVALID_IMAGE;
}
*kernel_header_len = (int) header_len;
/* Verify if the hash of the header is correct. */
header_checksum = DigestBuf(header_blob,
header_len - FIELD_LEN(header_checksum),
SHA512_DIGEST_ALGORITHM);
if (SafeMemcmp(header_checksum,
header_blob + (base_header_checksum_offset +
kernel_sign_key_len),
FIELD_LEN(header_checksum))) {
Free(header_checksum);
fprintf(stderr, "VerifyKernelHeader: Invalid header hash\n");
return VERIFY_KERNEL_INVALID_IMAGE;
}
Free(header_checksum);
/* Verify kernel key signature unless we are in dev mode. */
if (!dev_mode) {
if (!RSAVerifyBinary_f(firmware_key_blob, NULL, /* Key to use */
header_blob, /* Data to verify */
header_len, /* Length of data */
header_blob + header_len, /* Expected Signature */
firmware_sign_algorithm))
return VERIFY_KERNEL_KEY_SIGNATURE_FAILED;
}
return 0;
}
int VerifyKernelConfig(RSAPublicKey* kernel_sign_key,
const uint8_t* config_blob,
int algorithm,
uint64_t* kernel_len) {
uint64_t len;
int config_len;
config_len = GetKernelConfigLen(NULL);
if (!RSAVerifyBinary_f(NULL, kernel_sign_key, /* Key to use */
config_blob, /* Data to verify */
config_len, /* Length of data */
config_blob + config_len, /* Expected Signature */
algorithm))
return VERIFY_KERNEL_CONFIG_SIGNATURE_FAILED;
Memcpy(&len,
config_blob + (FIELD_LEN(kernel_version) + FIELD_LEN(options.version) +
FIELD_LEN(options.cmd_line)),
sizeof(len));
*kernel_len = len;
return 0;
}
int VerifyKernelData(RSAPublicKey* kernel_sign_key,
const uint8_t* kernel_config_start,
const uint8_t* kernel_data_start,
uint64_t kernel_len,
int algorithm) {
int signature_len = siglen_map[algorithm];
uint8_t* digest;
DigestContext ctx;
/* Since the kernel signature is computed over the kernel version, options
* and data, which does not form a contiguous region of memory, we calculate
* the message digest ourselves. */
DigestInit(&ctx, algorithm);
DigestUpdate(&ctx, kernel_config_start, GetKernelConfigLen());
DigestUpdate(&ctx, kernel_data_start + signature_len, kernel_len);
digest = DigestFinal(&ctx);
if (!RSAVerifyBinaryWithDigest_f(
NULL, kernel_sign_key, /* Key to use. */
digest, /* Digest of the data to verify. */
kernel_data_start, /* Expected Signature */
algorithm)) {
Free(digest);
return VERIFY_KERNEL_SIGNATURE_FAILED;
}
Free(digest);
return 0;
}
int VerifyKernel(const uint8_t* firmware_key_blob,
const uint8_t* kernel_blob,
const int dev_mode) {
int error_code;
int firmware_sign_algorithm; /* Firmware signing key algorithm. */
int kernel_sign_algorithm; /* Kernel Signing key algorithm. */
RSAPublicKey* kernel_sign_key;
int kernel_sign_key_len, kernel_key_signature_len, kernel_signature_len,
header_len;
uint64_t kernel_len;
const uint8_t* header_ptr; /* Pointer to header. */
const uint8_t* kernel_sign_key_ptr; /* Pointer to signing key. */
const uint8_t* config_ptr; /* Pointer to kernel config block. */
const uint8_t* kernel_ptr; /* Pointer to kernel signature/data. */
/* Note: All the offset calculations are based on struct FirmwareImage which
* is defined in include/firmware_image.h. */
/* Compare magic bytes. */
if (SafeMemcmp(kernel_blob, KERNEL_MAGIC, KERNEL_MAGIC_SIZE))
return VERIFY_KERNEL_WRONG_MAGIC;
header_ptr = kernel_blob + KERNEL_MAGIC_SIZE;
/* Only continue if header verification succeeds. */
if ((error_code = VerifyKernelHeader(firmware_key_blob, header_ptr, dev_mode,
&firmware_sign_algorithm,
&kernel_sign_algorithm, &header_len))) {
fprintf(stderr, "VerifyKernel: Kernel 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,
kernel_sign_key_len);
kernel_signature_len = siglen_map[kernel_sign_algorithm];
kernel_key_signature_len = siglen_map[firmware_sign_algorithm];
/* Only continue if config verification succeeds. */
config_ptr = (header_ptr + header_len + kernel_key_signature_len);
if ((error_code = VerifyKernelConfig(kernel_sign_key, config_ptr,
kernel_sign_algorithm,
&kernel_len))) {
RSAPublicKeyFree(kernel_sign_key);
return error_code; /* AKA jump to recovery. */
}
/* Only continue if kernel data verification succeeds. */
kernel_ptr = (config_ptr +
GetKernelConfigLen() + /* Skip config block/signature. */
kernel_signature_len);
if ((error_code = VerifyKernelData(kernel_sign_key, config_ptr, kernel_ptr,
kernel_len,
kernel_sign_algorithm))) {
RSAPublicKeyFree(kernel_sign_key);
return error_code; /* AKA jump to recovery. */
}
RSAPublicKeyFree(kernel_sign_key);
return 0; /* Success! */
}
int VerifyKernelImage(const RSAPublicKey* firmware_key, int VerifyKernelImage(const RSAPublicKey* firmware_key,
const KernelImage* image, const KernelImage* image,
@@ -617,7 +412,7 @@ int VerifyKernelImage(const RSAPublicKey* firmware_key,
siglen_map[image->firmware_sign_algorithm], siglen_map[image->firmware_sign_algorithm],
image->firmware_sign_algorithm, image->firmware_sign_algorithm,
header_digest)) { header_digest)) {
fprintf(stderr, "VerifyKernelImage(): Key signature check failed.\n"); debug("VerifyKernelImage(): Key signature check failed.\n");
error_code = VERIFY_KERNEL_KEY_SIGNATURE_FAILED; error_code = VERIFY_KERNEL_KEY_SIGNATURE_FAILED;
goto verify_failure; goto verify_failure;
} }
@@ -723,7 +518,7 @@ int AddKernelSignature(KernelImage* image,
GetKernelConfigLen(), GetKernelConfigLen(),
kernel_signing_key_file, kernel_signing_key_file,
image->kernel_sign_algorithm))) { image->kernel_sign_algorithm))) {
fprintf(stderr, "Could not compute signature on the kernel config.\n"); debug("Could not compute signature on the kernel config.\n");
Free(config_blob); Free(config_blob);
return 0; return 0;
} }
@@ -745,7 +540,7 @@ int AddKernelSignature(KernelImage* image,
image->kernel_sign_algorithm))) { image->kernel_sign_algorithm))) {
Free(config_blob); Free(config_blob);
Free(kernel_buf); Free(kernel_buf);
fprintf(stderr, "Could not compute signature on the kernel.\n"); debug("Could not compute signature on the kernel.\n");
return 0; return 0;
} }
image->kernel_signature = (uint8_t*) Malloc(signature_len); image->kernel_signature = (uint8_t*) Malloc(signature_len);
@@ -756,146 +551,8 @@ int AddKernelSignature(KernelImage* image,
return 1; return 1;
} }
uint32_t GetLogicalKernelVersion(uint8_t* kernel_blob) {
uint8_t* kernel_ptr;
uint16_t kernel_key_version;
uint16_t kernel_version;
uint16_t firmware_sign_algorithm;
uint16_t kernel_sign_algorithm;
int kernel_key_signature_len;
int kernel_sign_key_len;
kernel_ptr = kernel_blob + (FIELD_LEN(magic) +
FIELD_LEN(header_version) +
FIELD_LEN(header_len));
Memcpy(&firmware_sign_algorithm, kernel_ptr, sizeof(firmware_sign_algorithm));
kernel_ptr += FIELD_LEN(firmware_sign_algorithm);
Memcpy(&kernel_sign_algorithm, kernel_ptr, sizeof(kernel_sign_algorithm));
kernel_ptr += FIELD_LEN(kernel_sign_algorithm);
Memcpy(&kernel_key_version, kernel_ptr, sizeof(kernel_key_version));
if (firmware_sign_algorithm >= kNumAlgorithms)
return 0;
if (kernel_sign_algorithm >= kNumAlgorithms)
return 0;
kernel_key_signature_len = siglen_map[firmware_sign_algorithm];
kernel_sign_key_len = RSAProcessedKeySize(kernel_sign_algorithm);
kernel_ptr += (FIELD_LEN(kernel_key_version) +
kernel_sign_key_len +
FIELD_LEN(header_checksum) +
kernel_key_signature_len);
Memcpy(&kernel_version, kernel_ptr, sizeof(kernel_version));
return CombineUint16Pair(kernel_key_version, kernel_version);
}
void PrintKernelEntry(kernel_entry* entry) { void PrintKernelEntry(kernel_entry* entry) {
fprintf(stderr, "Boot Priority = %d\n", entry->boot_priority); debug("Boot Priority = %d\n", entry->boot_priority);
fprintf(stderr, "Boot Tries Remaining = %d\n", entry->boot_tries_remaining); debug("Boot Tries Remaining = %d\n", entry->boot_tries_remaining);
fprintf(stderr, "Boot Success Flag = %d\n", entry->boot_success_flag); debug("Boot Success Flag = %d\n", entry->boot_success_flag);
}
int VerifyKernelDriver_f(uint8_t* firmware_key_blob,
kernel_entry* kernelA,
kernel_entry* kernelB,
int dev_mode) {
int i;
/* Contains the logical kernel version (32-bit) which is calculated as
* (kernel_key_version << 16 | kernel_version) where
* [kernel_key_version], [firmware_version] are both 16-bit.
*/
uint32_t kernelA_lversion, kernelB_lversion;
uint32_t min_lversion; /* Minimum of kernel A and kernel B lversion. */
uint32_t stored_lversion; /* Stored logical version in the TPM. */
kernel_entry* try_kernel[2]; /* Kernel in try order. */
int try_kernel_which[2]; /* Which corresponding kernel in the try order */
uint32_t try_kernel_lversion[2]; /* Their logical versions. */
/* [kernel_to_boot] will eventually contain the boot path to follow
* and is returned to the caller. Initially, we set it to recovery. If
* a valid bootable kernel is found, it will be set to that. */
int kernel_to_boot = BOOT_KERNEL_RECOVERY_CONTINUE;
/* The TPM must already have be initialized, so no need to call SetupTPM(). */
/* We get the key versions by reading directly from the image blobs without
* any additional (expensive) sanity checking on the blob since it's faster to
* outright reject a kernel with an older kernel key version. A malformed
* or corrupted kernel blob will still fail when VerifyKernel() is called
* on it.
*/
kernelA_lversion = GetLogicalKernelVersion(kernelA->kernel_blob);
kernelB_lversion = GetLogicalKernelVersion(kernelB->kernel_blob);
min_lversion = Min(kernelA_lversion, kernelB_lversion);
stored_lversion = CombineUint16Pair(GetStoredVersion(KERNEL_KEY_VERSION),
GetStoredVersion(KERNEL_VERSION));
/* TODO(gauravsh): The kernel entries kernelA and kernelB come from the
* partition table - verify its signature/checksum before proceeding
* further. */
/* The logic for deciding which kernel to boot from is taken from the
* the Chromium OS Drive Map design document.
*
* We went to consider the kernels in their according to their boot
* priority attribute value.
*/
if (kernelA->boot_priority >= kernelB->boot_priority) {
try_kernel[0] = kernelA;
try_kernel_which[0] = BOOT_KERNEL_A_CONTINUE;
try_kernel_lversion[0] = kernelA_lversion;
try_kernel[1] = kernelB;
try_kernel_which[1] = BOOT_KERNEL_B_CONTINUE;
try_kernel_lversion[1] = kernelB_lversion;
} else {
try_kernel[0] = kernelB;
try_kernel_which[0] = BOOT_KERNEL_B_CONTINUE;
try_kernel_lversion[0] = kernelB_lversion;
try_kernel[1] = kernelA;
try_kernel_which[1] = BOOT_KERNEL_A_CONTINUE;
try_kernel_lversion[1] = kernelA_lversion;
}
/* TODO(gauravsh): Changes to boot_tries_remaining and boot_priority
* below should be propagated to partition table. This will be added
* once the firmware parition table parsing code is in. */
for (i = 0; i < 2; i++) {
if ((try_kernel[i]->boot_success_flag ||
try_kernel[i]->boot_tries_remaining) &&
(VERIFY_KERNEL_SUCCESS == VerifyKernel(firmware_key_blob,
try_kernel[i]->kernel_blob,
dev_mode))) {
if (try_kernel[i]->boot_tries_remaining > 0)
try_kernel[i]->boot_tries_remaining--;
if (stored_lversion > try_kernel_lversion[i])
continue; /* Rollback: I am afraid I can't let you do that Dave. */
if (i == 0 && (stored_lversion < try_kernel_lversion[1])) {
/* The higher priority kernel is valid and bootable, See if we
* need to update the stored version for rollback prevention. */
if (VERIFY_KERNEL_SUCCESS == VerifyKernel(firmware_key_blob,
try_kernel[1]->kernel_blob,
dev_mode)) {
WriteStoredVersion(KERNEL_KEY_VERSION,
(uint16_t) (min_lversion >> 16));
WriteStoredVersion(KERNEL_VERSION,
(uint16_t) (min_lversion & 0xFFFF));
stored_lversion = min_lversion; /* Update stored version as it's
* used later. */
}
}
kernel_to_boot = try_kernel_which[i];
break; /* We found a valid kernel. */
}
try_kernel[i]->boot_priority = 0;
} /* for loop. */
/* Lock Kernel TPM rollback indices from further writes.
* TODO(gauravsh): Figure out if these can be combined into one
* 32-bit location since we seem to always use them together. This can help
* us minimize the number of NVRAM writes/locks (which are limited over flash
* memory lifetimes.
*/
LockStoredVersion(KERNEL_KEY_VERSION);
LockStoredVersion(KERNEL_VERSION);
return kernel_to_boot;
} }

368
utils/kernel_image_fw.c Normal file
View File

@@ -0,0 +1,368 @@
/* Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*
* Functions for verifying a verified boot kernel image.
* (Firmware portion)
*/
#include "kernel_image_fw.h"
#include "padding.h"
#include "rollback_index.h"
#include "rsa_utility.h"
#include "sha_utility.h"
#include "utility.h"
/* Macro to determine the size of a field structure in the KernelImage
* structure. */
#define FIELD_LEN(field) (sizeof(((KernelImage*)0)->field))
#define KERNEL_CONFIG_FIELD_LEN (FIELD_LEN(kernel_version) + FIELD_LEN(options.version) + \
FIELD_LEN(options.cmd_line) + \
FIELD_LEN(options.kernel_len) + \
FIELD_LEN(options.kernel_load_addr) + \
FIELD_LEN(options.kernel_entry_addr))
char* kVerifyKernelErrors[VERIFY_KERNEL_MAX] = {
"Success.",
"Invalid Image.",
"Kernel Key Signature Failed.",
"Invalid Kernel Verification Algorithm.",
"Config Signature Failed.",
"Kernel Signature Failed.",
"Wrong Kernel Magic.",
};
int VerifyKernelHeader(const uint8_t* firmware_key_blob,
const uint8_t* header_blob,
const int dev_mode,
int* firmware_algorithm,
int* kernel_algorithm,
int* kernel_header_len) {
int kernel_sign_key_len;
int firmware_sign_key_len;
uint16_t header_version, header_len;
uint16_t firmware_sign_algorithm, kernel_sign_algorithm;
uint8_t* header_checksum = NULL;
/* Base Offset for the header_checksum field. Actual offset is
* this + kernel_sign_key_len. */
int base_header_checksum_offset = (FIELD_LEN(header_version) +
FIELD_LEN(header_len) +
FIELD_LEN(firmware_sign_algorithm) +
FIELD_LEN(kernel_sign_algorithm) +
FIELD_LEN(kernel_key_version));
Memcpy(&header_version, header_blob, sizeof(header_version));
Memcpy(&header_len, header_blob + FIELD_LEN(header_version),
sizeof(header_len));
Memcpy(&firmware_sign_algorithm,
header_blob + (FIELD_LEN(header_version) +
FIELD_LEN(header_len)),
sizeof(firmware_sign_algorithm));
Memcpy(&kernel_sign_algorithm,
header_blob + (FIELD_LEN(header_version) +
FIELD_LEN(header_len) +
FIELD_LEN(firmware_sign_algorithm)),
sizeof(kernel_sign_algorithm));
/* TODO(gauravsh): Make this return two different error types depending
* on whether the firmware or kernel signing algorithm is invalid. */
if (firmware_sign_algorithm >= kNumAlgorithms)
return VERIFY_KERNEL_INVALID_ALGORITHM;
if (kernel_sign_algorithm >= kNumAlgorithms)
return VERIFY_KERNEL_INVALID_ALGORITHM;
*firmware_algorithm = (int) firmware_sign_algorithm;
*kernel_algorithm = (int) kernel_sign_algorithm;
kernel_sign_key_len = RSAProcessedKeySize(kernel_sign_algorithm);
firmware_sign_key_len = RSAProcessedKeySize(firmware_sign_algorithm);
/* Verify if header len is correct? */
if (header_len != (base_header_checksum_offset +
kernel_sign_key_len +
FIELD_LEN(header_checksum))) {
debug("VerifyKernelHeader: Header length mismatch\n");
return VERIFY_KERNEL_INVALID_IMAGE;
}
*kernel_header_len = (int) header_len;
/* Verify if the hash of the header is correct. */
header_checksum = DigestBuf(header_blob,
header_len - FIELD_LEN(header_checksum),
SHA512_DIGEST_ALGORITHM);
if (SafeMemcmp(header_checksum,
header_blob + (base_header_checksum_offset +
kernel_sign_key_len),
FIELD_LEN(header_checksum))) {
Free(header_checksum);
debug("VerifyKernelHeader: Invalid header hash\n");
return VERIFY_KERNEL_INVALID_IMAGE;
}
Free(header_checksum);
/* Verify kernel key signature unless we are in dev mode. */
if (!dev_mode) {
if (!RSAVerifyBinary_f(firmware_key_blob, NULL, /* Key to use */
header_blob, /* Data to verify */
header_len, /* Length of data */
header_blob + header_len, /* Expected Signature */
firmware_sign_algorithm))
return VERIFY_KERNEL_KEY_SIGNATURE_FAILED;
}
return 0;
}
int VerifyKernelConfig(RSAPublicKey* kernel_sign_key,
const uint8_t* config_blob,
int algorithm,
uint64_t* kernel_len) {
uint64_t len;
if (!RSAVerifyBinary_f(NULL, kernel_sign_key, /* Key to use */
config_blob, /* Data to verify */
KERNEL_CONFIG_FIELD_LEN, /* Length of data */
config_blob + KERNEL_CONFIG_FIELD_LEN, /* Expected
* Signature */
algorithm))
return VERIFY_KERNEL_CONFIG_SIGNATURE_FAILED;
Memcpy(&len,
config_blob + (FIELD_LEN(kernel_version) + FIELD_LEN(options.version) +
FIELD_LEN(options.cmd_line)),
sizeof(len));
*kernel_len = len;
return 0;
}
int VerifyKernelData(RSAPublicKey* kernel_sign_key,
const uint8_t* kernel_config_start,
const uint8_t* kernel_data_start,
uint64_t kernel_len,
int algorithm) {
int signature_len = siglen_map[algorithm];
uint8_t* digest;
DigestContext ctx;
/* Since the kernel signature is computed over the kernel version, options
* and data, which does not form a contiguous region of memory, we calculate
* the message digest ourselves. */
DigestInit(&ctx, algorithm);
DigestUpdate(&ctx, kernel_config_start, KERNEL_CONFIG_FIELD_LEN);
DigestUpdate(&ctx, kernel_data_start + signature_len, kernel_len);
digest = DigestFinal(&ctx);
if (!RSAVerifyBinaryWithDigest_f(
NULL, kernel_sign_key, /* Key to use. */
digest, /* Digest of the data to verify. */
kernel_data_start, /* Expected Signature */
algorithm)) {
Free(digest);
return VERIFY_KERNEL_SIGNATURE_FAILED;
}
Free(digest);
return 0;
}
int VerifyKernel(const uint8_t* firmware_key_blob,
const uint8_t* kernel_blob,
const int dev_mode) {
int error_code;
int firmware_sign_algorithm; /* Firmware signing key algorithm. */
int kernel_sign_algorithm; /* Kernel Signing key algorithm. */
RSAPublicKey* kernel_sign_key;
int kernel_sign_key_len, kernel_key_signature_len, kernel_signature_len,
header_len;
uint64_t kernel_len;
const uint8_t* header_ptr; /* Pointer to header. */
const uint8_t* kernel_sign_key_ptr; /* Pointer to signing key. */
const uint8_t* config_ptr; /* Pointer to kernel config block. */
const uint8_t* kernel_ptr; /* Pointer to kernel signature/data. */
/* Note: All the offset calculations are based on struct FirmwareImage which
* is defined in include/firmware_image.h. */
/* Compare magic bytes. */
if (SafeMemcmp(kernel_blob, KERNEL_MAGIC, KERNEL_MAGIC_SIZE))
return VERIFY_KERNEL_WRONG_MAGIC;
header_ptr = kernel_blob + KERNEL_MAGIC_SIZE;
/* Only continue if header verification succeeds. */
if ((error_code = VerifyKernelHeader(firmware_key_blob, header_ptr, dev_mode,
&firmware_sign_algorithm,
&kernel_sign_algorithm, &header_len))) {
debug("VerifyKernel: Kernel 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,
kernel_sign_key_len);
kernel_signature_len = siglen_map[kernel_sign_algorithm];
kernel_key_signature_len = siglen_map[firmware_sign_algorithm];
/* Only continue if config verification succeeds. */
config_ptr = (header_ptr + header_len + kernel_key_signature_len);
if ((error_code = VerifyKernelConfig(kernel_sign_key, config_ptr,
kernel_sign_algorithm,
&kernel_len))) {
RSAPublicKeyFree(kernel_sign_key);
return error_code; /* AKA jump to recovery. */
}
/* Only continue if kernel data verification succeeds. */
kernel_ptr = (config_ptr +
KERNEL_CONFIG_FIELD_LEN + /* Skip config block/signature. */
kernel_signature_len);
if ((error_code = VerifyKernelData(kernel_sign_key, config_ptr, kernel_ptr,
kernel_len,
kernel_sign_algorithm))) {
RSAPublicKeyFree(kernel_sign_key);
return error_code; /* AKA jump to recovery. */
}
RSAPublicKeyFree(kernel_sign_key);
return 0; /* Success! */
}
uint32_t GetLogicalKernelVersion(uint8_t* kernel_blob) {
uint8_t* kernel_ptr;
uint16_t kernel_key_version;
uint16_t kernel_version;
uint16_t firmware_sign_algorithm;
uint16_t kernel_sign_algorithm;
int kernel_key_signature_len;
int kernel_sign_key_len;
kernel_ptr = kernel_blob + (FIELD_LEN(magic) +
FIELD_LEN(header_version) +
FIELD_LEN(header_len));
Memcpy(&firmware_sign_algorithm, kernel_ptr, sizeof(firmware_sign_algorithm));
kernel_ptr += FIELD_LEN(firmware_sign_algorithm);
Memcpy(&kernel_sign_algorithm, kernel_ptr, sizeof(kernel_sign_algorithm));
kernel_ptr += FIELD_LEN(kernel_sign_algorithm);
Memcpy(&kernel_key_version, kernel_ptr, sizeof(kernel_key_version));
if (firmware_sign_algorithm >= kNumAlgorithms)
return 0;
if (kernel_sign_algorithm >= kNumAlgorithms)
return 0;
kernel_key_signature_len = siglen_map[firmware_sign_algorithm];
kernel_sign_key_len = RSAProcessedKeySize(kernel_sign_algorithm);
kernel_ptr += (FIELD_LEN(kernel_key_version) +
kernel_sign_key_len +
FIELD_LEN(header_checksum) +
kernel_key_signature_len);
Memcpy(&kernel_version, kernel_ptr, sizeof(kernel_version));
return CombineUint16Pair(kernel_key_version, kernel_version);
}
int VerifyKernelDriver_f(uint8_t* firmware_key_blob,
kernel_entry* kernelA,
kernel_entry* kernelB,
int dev_mode) {
int i;
/* Contains the logical kernel version (32-bit) which is calculated as
* (kernel_key_version << 16 | kernel_version) where
* [kernel_key_version], [firmware_version] are both 16-bit.
*/
uint32_t kernelA_lversion, kernelB_lversion;
uint32_t min_lversion; /* Minimum of kernel A and kernel B lversion. */
uint32_t stored_lversion; /* Stored logical version in the TPM. */
kernel_entry* try_kernel[2]; /* Kernel in try order. */
int try_kernel_which[2]; /* Which corresponding kernel in the try order */
uint32_t try_kernel_lversion[2]; /* Their logical versions. */
/* [kernel_to_boot] will eventually contain the boot path to follow
* and is returned to the caller. Initially, we set it to recovery. If
* a valid bootable kernel is found, it will be set to that. */
int kernel_to_boot = BOOT_KERNEL_RECOVERY_CONTINUE;
/* The TPM must already have be initialized, so no need to call SetupTPM(). */
/* We get the key versions by reading directly from the image blobs without
* any additional (expensive) sanity checking on the blob since it's faster to
* outright reject a kernel with an older kernel key version. A malformed
* or corrupted kernel blob will still fail when VerifyKernel() is called
* on it.
*/
kernelA_lversion = GetLogicalKernelVersion(kernelA->kernel_blob);
kernelB_lversion = GetLogicalKernelVersion(kernelB->kernel_blob);
min_lversion = Min(kernelA_lversion, kernelB_lversion);
stored_lversion = CombineUint16Pair(GetStoredVersion(KERNEL_KEY_VERSION),
GetStoredVersion(KERNEL_VERSION));
/* TODO(gauravsh): The kernel entries kernelA and kernelB come from the
* partition table - verify its signature/checksum before proceeding
* further. */
/* The logic for deciding which kernel to boot from is taken from the
* the Chromium OS Drive Map design document.
*
* We went to consider the kernels in their according to their boot
* priority attribute value.
*/
if (kernelA->boot_priority >= kernelB->boot_priority) {
try_kernel[0] = kernelA;
try_kernel_which[0] = BOOT_KERNEL_A_CONTINUE;
try_kernel_lversion[0] = kernelA_lversion;
try_kernel[1] = kernelB;
try_kernel_which[1] = BOOT_KERNEL_B_CONTINUE;
try_kernel_lversion[1] = kernelB_lversion;
} else {
try_kernel[0] = kernelB;
try_kernel_which[0] = BOOT_KERNEL_B_CONTINUE;
try_kernel_lversion[0] = kernelB_lversion;
try_kernel[1] = kernelA;
try_kernel_which[1] = BOOT_KERNEL_A_CONTINUE;
try_kernel_lversion[1] = kernelA_lversion;
}
/* TODO(gauravsh): Changes to boot_tries_remaining and boot_priority
* below should be propagated to partition table. This will be added
* once the firmware parition table parsing code is in. */
for (i = 0; i < 2; i++) {
if ((try_kernel[i]->boot_success_flag ||
try_kernel[i]->boot_tries_remaining) &&
(VERIFY_KERNEL_SUCCESS == VerifyKernel(firmware_key_blob,
try_kernel[i]->kernel_blob,
dev_mode))) {
if (try_kernel[i]->boot_tries_remaining > 0)
try_kernel[i]->boot_tries_remaining--;
if (stored_lversion > try_kernel_lversion[i])
continue; /* Rollback: I am afraid I can't let you do that Dave. */
if (i == 0 && (stored_lversion < try_kernel_lversion[1])) {
/* The higher priority kernel is valid and bootable, See if we
* need to update the stored version for rollback prevention. */
if (VERIFY_KERNEL_SUCCESS == VerifyKernel(firmware_key_blob,
try_kernel[1]->kernel_blob,
dev_mode)) {
WriteStoredVersion(KERNEL_KEY_VERSION,
(uint16_t) (min_lversion >> 16));
WriteStoredVersion(KERNEL_VERSION,
(uint16_t) (min_lversion & 0xFFFF));
stored_lversion = min_lversion; /* Update stored version as it's
* used later. */
}
}
kernel_to_boot = try_kernel_which[i];
break; /* We found a valid kernel. */
}
try_kernel[i]->boot_priority = 0;
} /* for loop. */
/* Lock Kernel TPM rollback indices from further writes.
* TODO(gauravsh): Figure out if these can be combined into one
* 32-bit location since we seem to always use them together. This can help
* us minimize the number of NVRAM writes/locks (which are limited over flash
* memory lifetimes.
*/
LockStoredVersion(KERNEL_KEY_VERSION);
LockStoredVersion(KERNEL_VERSION);
return kernel_to_boot;
}