Vboot Reference: Add the "real" reference firmware verification function (VerifyFirmware).
The old VerifyFirmware function (now called VerifyFirmwareImage) works on the FirmwareImage structure. This CL adds a verification function which can be used directly on packed binary verified boot firmware blobs. This function can be used as the reference implementation for verified boot in firmware. In addition, all functions that work on FirmwareImage structure have been renames to distinguish them from those which work on binary firmware blobs.
In addition, this adds some new crypto utility functions and refactors old ones.
BUG=670
TEST=Added tests for the new function and they pass.
Review URL: http://codereview.chromium.org/650105
diff --git a/utils/Makefile b/utils/Makefile
index c1967fc..c0de74e 100644
--- a/utils/Makefile
+++ b/utils/Makefile
@@ -3,33 +3,36 @@
# found in the LICENSE file.
CC ?= gcc
-CFLAGS = -Wall -ggdb -DNDEBUG
+CFLAGS = -Wall -DNDEBUG
INCLUDES ?= -I../include/
TOP ?= ../
LIBS = -lcrypto
FIRMWARELIBS = $(TOP)/crypto/libcrypto.a $(TOP)/common/libcommon.a
-all: dumpRSAPublicKey verify_data signature_digest firmware_utility
+all: dumpRSAPublicKey verify_data signature_digest firmware_utility \
+ file_keys.o firmware_image.o
dumpRSAPublicKey: dumpRSAPublicKey.c
$(CC) $(CFLAGS) $(LIBS) $< -o $@
-verify_data: verify_data.c file_keys.o
- $(CC) $(CFLAGS) $(INCLUDES) $< -o $@ file_keys.o $(FIRMWARELIBS)
+verify_data: verify_data.c file_keys.c
+ $(CC) $(CFLAGS) $(INCLUDES) $^ -o $@ $(FIRMWARELIBS)
signature_digest: signature_digest.c
$(CC) $(CFLAGS) $(INCLUDES) $< -o $@ $(FIRMWARELIBS)
-firmware_utility: firmware_utility.c firmware_image.o file_keys.o
- $(CC) $(CFLAGS) $(INCLUDES) $< -o $@ firmware_image.o $(FIRMWARELIBS)
+firmware_utility: firmware_utility.c firmware_image.o file_keys.c
+ $(CC) $(CFLAGS) $(INCLUDES) $^ -o $@ $(FIRMWARELIBS)
+# Used by tests.
file_keys.o: file_keys.c
$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
firmware_image.o: firmware_image.c
- $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
+ $(CC) $(CFLAGS) -ansi $(INCLUDES) -c $< -o $@
clean:
- rm -f dumpRSAPublicKey verify_data signature_digest firmware_image.o file_keys.o
+ rm -f dumpRSAPublicKey verify_data signature_digest firmware_image.o \
+ file_keys.o
diff --git a/utils/file_keys.c b/utils/file_keys.c
index 8a8a2cb..bcba749 100644
--- a/utils/file_keys.c
+++ b/utils/file_keys.c
@@ -15,6 +15,7 @@
#include <sys/types.h>
#include <unistd.h>
+#include "padding.h"
#include "rsa_utility.h"
#include "utility.h"
@@ -55,3 +56,41 @@
Free(buf);
return key;
}
+
+uint8_t* SignatureFile(char* input_file, char* key_file, int algorithm) {
+ char* sign_utility = "./sign_data.sh";
+ char* cmd; /* Command line to invoke. */
+ int cmd_len;
+ FILE* cmd_out; /* File descriptor to command output. */
+ uint8_t* signature = NULL;
+ int signature_size = siglen_map[algorithm] * sizeof(uint32_t);
+
+ /* Build command line:
+ * sign_data.sh <algorithm> <key file> <input file>
+ */
+ cmd_len = (strlen(sign_utility) + 1 + /* +1 for space. */
+ 2 + 1 + /* For [algorithm]. */
+ strlen(key_file) + 1 + /* +1 for space. */
+ strlen(input_file) +
+ 1); /* For the trailing '\0'. */
+ cmd = (char*) Malloc(cmd_len);
+ snprintf(cmd, cmd_len, "%s %d %s %s", sign_utility, algorithm, key_file,
+ input_file);
+ cmd_out = popen(cmd, "r");
+ Free(cmd);
+ if (!cmd_out) {
+ fprintf(stderr, "Couldn't execute: %s\n", cmd);
+ return NULL;
+ }
+
+ signature = (uint8_t*) Malloc(signature_size);
+ if (fread(signature, signature_size, 1, cmd_out) != 1) {
+ fprintf(stderr, "Couldn't read signature.\n");
+ pclose(cmd_out);
+ Free(signature);
+ return NULL;
+ }
+
+ pclose(cmd_out);
+ return signature;
+}
diff --git a/utils/firmware_image.c b/utils/firmware_image.c
index ae89490..07aa8bd 100644
--- a/utils/firmware_image.c
+++ b/utils/firmware_image.c
@@ -14,11 +14,15 @@
#include <sys/stat.h>
#include <unistd.h>
+#include "file_keys.h"
#include "padding.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))
FirmwareImage* FirmwareImageNew(void) {
FirmwareImage* fw = (FirmwareImage*) Malloc(sizeof(FirmwareImage));
@@ -33,9 +37,8 @@
Free(image->firmware_data);
}
-
-FirmwareImage* ReadFirmware(const char* input_file,
- FirmwareImage* image) {
+FirmwareImage* ReadFirmwareImage(const char* input_file,
+ FirmwareImage* image) {
int fd;
struct stat fd_stat;
@@ -78,7 +81,7 @@
if (!StatefulMemcpy(&st, &image->magic, FIRMWARE_MAGIC_SIZE))
goto parse_failure;
- if (!SafeMemcmp(image->magic, FIRMWARE_MAGIC, FIRMWARE_MAGIC_SIZE)) {
+ if (SafeMemcmp(image->magic, FIRMWARE_MAGIC, FIRMWARE_MAGIC_SIZE)) {
fprintf(stderr, "Wrong Firmware Magic.\n");
goto parse_failure;
}
@@ -99,7 +102,7 @@
/* Check whether the header length is correct. */
header_len = (sizeof(image->header_len) + sizeof(image->sign_algorithm) +
sizeof(image->key_version) +
- sizeof(image->header_hash));
+ sizeof(image->header_checksum));
if (header_len != image->header_len) {
fprintf(stderr, "Header length mismatch.");
goto parse_failure;
@@ -109,7 +112,7 @@
image->sign_key = (uint8_t*) Malloc(sign_key_len);
StatefulMemcpy(&st, image->sign_key, sign_key_len);
StatefulMemcpy(&st, &image->key_version, sizeof(image->key_version));
- StatefulMemcpy(&st, image->header_hash, sizeof(image->header_hash));
+ StatefulMemcpy(&st, image->header_checksum, sizeof(image->header_checksum));
/* Read key signature. */
StatefulMemcpy(&st, image->key_signature, sizeof(image->key_signature));
@@ -147,10 +150,10 @@
sign_key_len = (image->header_len - sizeof(image->header_len) -
sizeof(image->sign_algorithm) -
sizeof(image->key_version) -
- sizeof(image->header_hash));
+ sizeof(image->header_checksum));
write(fd, image->sign_key, sign_key_len);
write(fd, &image->key_version, sizeof(image->key_version));
- write(fd, &image->header_hash, sizeof(image->header_hash));
+ write(fd, &image->header_checksum, sizeof(image->header_checksum));
}
void WriteFirmwarePreamble(int fd, FirmwareImage* image) {
@@ -160,31 +163,29 @@
write(fd, image->preamble, sizeof(image->preamble));
}
+FirmwareImage* WriteFirmwareImage(const char* input_file,
+ FirmwareImage* image) {
+ int fd;
+ int signature_len;
-FirmwareImage* WriteFirmware(const char* input_file,
- FirmwareImage* image) {
- int fd;
- int signature_len;
+ if (!image)
+ return NULL;
+ if (-1 == (fd = creat(input_file, S_IRWXU))) {
+ fprintf(stderr, "Couldn't open file for writing.\n");
+ return NULL;
+ }
- if (!image)
- return NULL;
+ write(fd, image->magic, sizeof(image->magic));
+ WriteFirmwareHeader(fd, image);
+ write(fd, image->key_signature, sizeof(image->key_signature));
+ signature_len = siglen_map[image->sign_algorithm] * sizeof(uint32_t);
+ WriteFirmwarePreamble(fd, image);
+ write(fd, image->preamble_signature, signature_len);
+ write(fd, image->firmware_signature, signature_len);
+ write(fd, image->firmware_data, image->firmware_len);
- if (-1 == (fd = open(input_file, O_WRONLY))) {
- fprintf(stderr, "Couldn't open file for writing.\n");
- return NULL;
- }
-
- write(fd, &image->magic, sizeof(image->magic));
- WriteFirmwareHeader(fd, image);
- write(fd, image->key_signature, sizeof(image->key_signature));
- signature_len = siglen_map[image->sign_algorithm] * sizeof(uint32_t);
- WriteFirmwarePreamble(fd, image);
- write(fd, image->preamble_signature, signature_len);
- write(fd, image->firmware_signature, signature_len);
- write(fd, image->firmware_data, image->firmware_len);
-
- close(fd);
- return image;
+ close(fd);
+ return image;
}
void PrintFirmware(const FirmwareImage* image) {
@@ -209,9 +210,169 @@
/* Output key signature here? */
}
-int VerifyFirmware(const RSAPublicKey* root_key,
- const FirmwareImage* image,
+char* kVerifyFirmwareErrors[VERIFY_FIRMWARE_MAX] = {
+ "Success.",
+ "Invalid Image.",
+ "Root Key Signature Failed.",
+ "Invalid Verification Algorithm.",
+ "Preamble Signature Failed.",
+ "Firmware Signature Failed.",
+ "Wrong Firmware Magic.",
+};
+
+int VerifyFirmwareHeader(const uint8_t* root_key_blob,
+ const uint8_t* header_blob,
+ const int dev_mode,
+ int* algorithm,
+ int* header_len) {
+ int 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 + sign_key_len. */
+ int base_header_checksum_offset = (FIELD_LEN(header_len) +
+ FIELD_LEN(sign_algorithm) +
+ FIELD_LEN(key_version));
+
+
+ root_key_len = RSAProcessedKeySize(ROOT_SIGNATURE_ALGORITHM);
+ Memcpy(&hlen, header_blob, sizeof(hlen));
+ Memcpy(&algo,
+ header_blob + FIELD_LEN(sign_algorithm),
+ sizeof(algo));
+ if (algo >= kNumAlgorithms)
+ return VERIFY_FIRMWARE_INVALID_ALGORITHM;
+ *algorithm = (int) algo;
+ sign_key_len = RSAProcessedKeySize(*algorithm);
+
+ /* Verify if header len is correct? */
+ if (hlen != (base_header_checksum_offset +
+ 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 + sign_key_len),
+ FIELD_LEN(header_checksum))) {
+ Free(header_checksum);
+ return VERIFY_FIRMWARE_INVALID_IMAGE;
+ }
+ Free(header_checksum);
+
+ /* Verify root key signature unless we are in dev mode. */
+ if (!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* sign_key,
+ const uint8_t* preamble_blob,
+ int algorithm,
+ int* firmware_len) {
+ uint32_t len;
+ int preamble_len;
+ preamble_len = (FIELD_LEN(firmware_version) +
+ FIELD_LEN(firmware_len) +
+ FIELD_LEN(preamble));
+ if (!RSAVerifyBinary_f(NULL, 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 = (int) len;
+ return 0;
+}
+
+int VerifyFirmwareData(RSAPublicKey* sign_key,
+ const uint8_t* firmware_data_start,
+ int firmware_len,
+ int algorithm) {
+ int signature_len = siglen_map[algorithm] * sizeof(uint32_t);
+ if (!RSAVerifyBinary_f(NULL, sign_key, /* Key to use. */
+ firmware_data_start + signature_len, /* Data to
+ * verify */
+ firmware_len, /* Length of data. */
+ firmware_data_start, /* Expected Signature */
+ algorithm))
+ return VERIFY_FIRMWARE_SIGNATURE_FAILED;
+ return 0;
+}
+
+int VerifyFirmware(const uint8_t* root_key_blob,
+ const uint8_t* firmware_blob,
const int dev_mode) {
+ int error_code;
+ int algorithm; /* Signing key algorithm. */
+ RSAPublicKey* sign_key;
+ int sign_key_len, signature_len, header_len, firmware_len;
+ const uint8_t* header_ptr; /* Pointer to header. */
+ const uint8_t* sign_key_ptr; /* Pointer to signing key. */
+ const uint8_t* preamble_ptr; /* Pointer to preamble block. */
+ const uint8_t* firmware_ptr; /* 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, dev_mode,
+ &algorithm, &header_len)))
+ return error_code; /* AKA jump to revovery. */
+
+ /* Parse signing key into RSAPublicKey structure since it is required multiple
+ * times. */
+ sign_key_len = RSAProcessedKeySize(algorithm);
+ sign_key_ptr = header_ptr + (FIELD_LEN(header_len) +
+ FIELD_LEN(sign_algorithm));
+ sign_key = RSAPublicKeyFromBuf(sign_key_ptr, sign_key_len);
+ signature_len = siglen_map[algorithm] * sizeof(uint32_t);
+
+ /* Only continue if preamble verification succeeds. */
+ preamble_ptr = (header_ptr + header_len +
+ FIELD_LEN(key_signature));
+ if ((error_code = VerifyFirmwarePreamble(sign_key, preamble_ptr, algorithm,
+ &firmware_len)))
+ return error_code; /* AKA jump to recovery. */
+
+ /* Only continue if firmware data verification succeeds. */
+ firmware_ptr = (preamble_ptr +
+ FIELD_LEN(firmware_version) +
+ FIELD_LEN(firmware_len) +
+ FIELD_LEN(preamble) +
+ signature_len);
+
+ if ((error_code = VerifyFirmwareData(sign_key, firmware_ptr, firmware_len,
+ algorithm)))
+ return error_code; /* AKA jump to recovery. */
+
+ return 0; /* Success! */
+}
+
+int VerifyFirmwareImage(const RSAPublicKey* root_key,
+ const FirmwareImage* image,
+ const int dev_mode) {
RSAPublicKey* sign_key;
uint8_t* header_digest = NULL;
uint8_t* preamble_digest = NULL;
@@ -222,10 +383,15 @@
DigestContext ctx;
if (!image)
- return VERIFY_INVALID_IMAGE;
+ return VERIFY_FIRMWARE_INVALID_IMAGE;
/* Verify root key signature on the sign key header if we
- * are not in dev mode. */
+ * are not in dev mode.
+ *
+ * TODO(gauravsh): Add additional sanity checks here for:
+ * 1) verifying the header length is correct.
+ * 2) header_checksum is correct.
+ */
if (!dev_mode) {
DigestInit(&ctx, ROOT_SIGNATURE_ALGORITHM);
DigestUpdate(&ctx, (uint8_t*) &image->header_len,
@@ -236,14 +402,14 @@
RSAProcessedKeySize(image->sign_algorithm));
DigestUpdate(&ctx, (uint8_t*) &image->key_version,
sizeof(image->key_version));
- DigestUpdate(&ctx, image->header_hash,
- sizeof(image->header_hash));
+ DigestUpdate(&ctx, image->header_checksum,
+ sizeof(image->header_checksum));
header_digest = DigestFinal(&ctx);
if (!RSA_verify(root_key, image->key_signature,
sizeof(image->key_signature),
ROOT_SIGNATURE_ALGORITHM,
header_digest)) {
- error_code = VERIFY_ROOT_SIGNATURE_FAILED;
+ error_code = VERIFY_FIRMWARE_ROOT_SIGNATURE_FAILED;
goto verify_failure;
}
}
@@ -255,7 +421,7 @@
signature_size = siglen_map[image->sign_algorithm] * sizeof(uint32_t);
if (image->sign_algorithm >= kNumAlgorithms)
- return VERIFY_INVALID_ALGORITHM;
+ return VERIFY_FIRMWARE_INVALID_ALGORITHM;
/* Verify firmware preamble signature. */
DigestInit(&ctx, image->sign_algorithm);
@@ -269,7 +435,7 @@
if (!RSA_verify(sign_key, image->preamble_signature,
signature_size, image->sign_algorithm,
preamble_digest)) {
- error_code = VERIFY_PREAMBLE_SIGNATURE_FAILED;
+ error_code = VERIFY_FIRMWARE_PREAMBLE_SIGNATURE_FAILED;
goto verify_failure;
}
@@ -291,52 +457,6 @@
return error_code;
}
-char* kVerifyFirmwareErrors[VERIFY_MAX] = {
- "Success.",
- "Invalid Image.",
- "Root Key Signature Failed.",
- "Invalid Verification Algorithm.",
- "Preamble Signature Failed.",
- "Firmware Signature Failed.",
-};
-
-uint8_t* SignatureFile(char* input_file, char* key_file, int algorithm) {
- char* sign_utility = "./sign_data.sh";
- char* cmd; /* Command line to invoke. */
- int cmd_len;
- FILE* cmd_out; /* File descriptor to command output. */
- uint8_t* signature = NULL;
- int signature_size = siglen_map[algorithm] * sizeof(uint32_t);
-
- /* Build command line:
- * sign_data.sh <algorithm> <key file> <input file>
- */
- cmd_len = (strlen(sign_utility) + 1 + /* +1 for space. */
- 2 + 1 + /* For [algorithm]. */
- strlen(key_file) + 1 + /* +1 for space. */
- strlen(input_file) +
- 1); /* For the trailing '\0'. */
- cmd = (char*) Malloc(cmd_len);
- snprintf(cmd, cmd_len, "%s %d %s %s", sign_utility, algorithm, key_file,
- input_file);
- cmd_out = popen(cmd, "r");
- Free(cmd);
- if (!cmd_out) {
- fprintf(stderr, "Couldn't execute: %s\n", cmd);
- return NULL;
- }
-
- signature = (uint8_t*) Malloc(signature_size);
- if (fread(signature, signature_size, 1, cmd_out) != 1) {
- fprintf(stderr, "Couldn't read signature.\n");
- pclose(cmd_out);
- Free(signature);
- return NULL;
- }
-
- pclose(cmd_out);
- return signature;
-}
int AddKeySignature(FirmwareImage* image, char* root_key_file) {
int tmp_hdr_fd;
@@ -371,7 +491,7 @@
/* Write preamble to a file. */
if(-1 == (tmp_preamble_fd = creat(tmp_preamble_file, S_IRWXU))) {
fprintf(stderr, "Could not open temporary file for writing "
- "firmware praemble.\n");
+ "firmware preamble.\n");
return 0;
}
WriteFirmwarePreamble(tmp_preamble_fd, image);
diff --git a/utils/signature_digest.c b/utils/signature_digest.c
index 8dcd8c9..100f7ec 100644
--- a/utils/signature_digest.c
+++ b/utils/signature_digest.c
@@ -21,7 +21,7 @@
#include "sha.h"
#include "sha_utility.h"
-uint8_t* prepend_digestinfo(int algorithm, uint8_t* digest) {
+uint8_t* PrependDigestInfo(int algorithm, uint8_t* digest) {
const int digest_size = hash_size_map[algorithm];
const int digestinfo_size = digestinfo_size_map[algorithm];
const uint8_t* digestinfo = hash_digestinfo_map[algorithm];
@@ -55,7 +55,7 @@
if (!(digest = DigestFile(argv[2], algorithm)))
goto failure;
- info_digest = prepend_digestinfo(algorithm, digest);
+ info_digest = PrependDigestInfo(algorithm, digest);
write(1, info_digest, hash_size_map[algorithm] +
digestinfo_size_map[algorithm]);
diff --git a/utils/verify_data.c b/utils/verify_data.c
index 5cb33f4..c58b6fb 100644
--- a/utils/verify_data.c
+++ b/utils/verify_data.c
@@ -22,6 +22,11 @@
#include "rsa_utility.h"
#include "verify_data.h"
+/* ANSI Color coding sequences. */
+#define COL_GREEN "\e[1;32m"
+#define COL_RED "\e[0;31m]"
+#define COL_STOP "\e[m"
+
uint8_t* read_signature(char* input_file, int len) {
int i, sigfd;
uint8_t* signature = NULL;
@@ -46,7 +51,6 @@
return signature;
}
-
int main(int argc, char* argv[]) {
int i, algorithm, sig_len;
int return_code = 1; /* Default to error. */
@@ -80,10 +84,11 @@
goto failure;
if(RSA_verify(key, signature, sig_len, algorithm, digest)) {
return_code = 0;
- fprintf(stderr, "Signature Verification SUCCEEDED.\n");
- }
- else {
- fprintf(stderr, "Signature Verification FAILED!\n");
+ fprintf(stderr, "Signature Verification "
+ COL_GREEN "SUCCEEDED" COL_STOP "\n");
+ } else {
+ fprintf(stderr, "Signature Verification "
+ COL_RED "FAILED" COL_STOP "\n");
}
failure: