diff options
author | Gaurav Shah <gauravsh@google.com> | 2010-02-28 19:18:24 -0800 |
---|---|---|
committer | Gaurav Shah <gauravsh@google.com> | 2010-02-28 19:18:24 -0800 |
commit | f67bcaa9a6d8ac8c31cb5650405f99c475c9ad5e (patch) | |
tree | e8fdb91825d0778f5dd7e7ce1c6cfef5bb2947f9 | |
parent | 8b95c703730da8a73219c50a6ddb668ea8fc414c (diff) | |
download | vboot-f67bcaa9a6d8ac8c31cb5650405f99c475c9ad5e.tar.gz |
Vboot Reference: Add functions to verify signed kernel images.
BUG=670
TEST=Adds kernel_image_test which tests the new functions.
The kernel image verification pretty much exactly mirror the already existing firmware image verification functions except with a few different/additional fields in a signed kernel image. The firmware signing key is the root key equivalent for kernel images.
This CL also moves the image verification tests to a different script. There's some additional cleanup of the code that I will be submitting separately after this and another pending patches get LGTMed and land.
Review URL: http://codereview.chromium.org/660161
-rw-r--r-- | include/kernel_image.h | 200 | ||||
-rw-r--r-- | tests/Makefile | 16 | ||||
-rw-r--r-- | tests/firmware_image_tests.c | 9 | ||||
-rw-r--r-- | tests/kernel_image_tests.c | 257 | ||||
-rwxr-xr-x | tests/run_image_verification_tests.sh | 100 | ||||
-rwxr-xr-x | tests/run_rsa_tests.sh | 28 | ||||
-rw-r--r-- | utils/Makefile | 6 | ||||
-rw-r--r-- | utils/kernel_image.c | 614 |
8 files changed, 1199 insertions, 31 deletions
diff --git a/include/kernel_image.h b/include/kernel_image.h new file mode 100644 index 00000000..c65c6143 --- /dev/null +++ b/include/kernel_image.h @@ -0,0 +1,200 @@ +/* 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. + */ + +#ifndef VBOOT_REFERENCE_KERNEL_IMAGE_H_ +#define VBOOT_REFERENCE_KERNEL_IMAGE_H_ + +#include <inttypes.h> + +#include "rsa.h" +#include "sha.h" + +#define KERNEL_MAGIC "CHROMEOS" +#define KERNEL_MAGIC_SIZE 8 + +/* Kernel config file options according to the Chrome OS drive map design. */ +typedef struct kconfig_options { + uint32_t version[2]; /* Configuration file version. */ + uint32_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 [kernel_data]. */ + uint8_t* kernel_data; /* Actual kernel data. */ + +} KernelImage; + +/* Allocate and return a new KernelImage structure. */ +KernelImage* KernelImageNew(void); + +/* Deep free the contents of [image]. */ +void KernelImageFree(KernelImage* image); + +/* Read kernel data from file named [input_file]. + * + * Returns a filled up KernelImage on success, NULL on error. + */ +KernelImage* ReadKernelImage(const char* input_file); + +/* Write kernel key header from [image] to an open file pointed by the + * file descriptor [fd]. + */ +void WriteKernelHeader(int fd, KernelImage* image); + +/* Write kernel config from [image] to an open file pointed by the + * file descriptor [fd]. + */ +void WriteKernelConfig(int fd, KernelImage* image); + +/* Write kernel data from [image] to a file named [input_file]. + * + * Return [image] on success, NULL on error. + */ +KernelImage* WriteKernelImage(const char* input_file, + KernelImage* image); + +/* Pretty print the contents of [image]. Only headers and metadata information + * is printed. + */ +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. */ + +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 VerifyFirmwareHeader(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, + int* 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_data_start, + int 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 + * 0 (inactive), then the [firmware_signing_key] is used to verify the signature + * of the signing key, else the check is skipped. + * + * Returns 0 on success, error code on failure. + */ +int VerifyKernelImage(const RSAPublicKey* firmware_signing_key, + const KernelImage* image, + int dev_mode); + + +/* Maps error codes from VerifyKernel*() to error description. */ +const char* VerifyKernelErrorString(int error); + +/* Add a kernel signing key signature to the key header to a kernel image + * [image] using the private key in file [firmware_key_file]. + * + * Return 1 on success, 0 on failure. + */ +int AddKernelKeySignature(KernelImage* image, const char* firmware_key_file); + +/* Add a kernel and kernel config signature to a kernel image [image] + * using the private signing key in file [kernel_sigining_key_file]. + * + * Return 1 on success, 0 on failure. + */ +int AddKernelSignature(KernelImage* image, const char* kernel_sigining_key_file, + int algorithm); + +#endif /* VBOOT_REFERENCE_KERNEL_IMAGE_H_ */ diff --git a/tests/Makefile b/tests/Makefile index 92e91a4d..57fecd7b 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -7,16 +7,20 @@ CFLAGS = -Wall -DNDEBUG INCLUDES ?= -I../include/ TOP ?= ../ -LIBS = $(TOP)/utils/firmware_image.o $(TOP)/crypto/libcrypto.a \ - $(TOP)/common/libcommon.a $(TOP)/utils/file_keys.o -lrt +LIBS = $(TOP)/utils/kernel_image.o $(TOP)/utils/firmware_image.o \ + $(TOP)/crypto/libcrypto.a $(TOP)/common/libcommon.a \ + $(TOP)/utils/file_keys.o -lrt -tests: firmware_image_tests sha_tests sha_benchmark rsa_verify_benchmark \ - rsa_padding_test +tests: firmware_image_tests kernel_image_tests sha_tests sha_benchmark \ + rsa_verify_benchmark rsa_padding_test sha_tests: sha_tests.c $(CC) $(CFLAGS) $(INCLUDES) $< -o $@ $(LIBS) -firmware_image_tests: firmware_image_tests.c +firmware_image_tests: firmware_image_tests.c $(LIBS) + $(CC) $(CFLAGS) $(INCLUDES) $< -o $@ $(LIBS) + +kernel_image_tests: kernel_image_tests.c $(LIBS) $(CC) $(CFLAGS) $(INCLUDES) $< -o $@ $(LIBS) sha_benchmark: sha_benchmark.c timer_utils.c @@ -30,4 +34,4 @@ rsa_verify_benchmark: rsa_verify_benchmark.c timer_utils.c clean: rm -f sha_tests sha_benchmark rsa_verify_benchmark \ - firmware_image_tests rsa_padding_test + firmware_image_tests kernel_image_tests rsa_padding_test diff --git a/tests/firmware_image_tests.c b/tests/firmware_image_tests.c index 11eb4760..ff7b509c 100644 --- a/tests/firmware_image_tests.c +++ b/tests/firmware_image_tests.c @@ -14,13 +14,18 @@ #include "sha_utility.h" #include "utility.h" +/* ANSI Color coding sequences. */ +#define COL_GREEN "\e[1;32m" +#define COL_RED "\e[0;31m]" +#define COL_STOP "\e[m" + int TEST_EQ(int result, int expected_result, char* testname) { if (result == expected_result) { - fprintf(stderr, "%s Test \e[1;32mSUCCEEDED\e[m\n", testname); + fprintf(stderr, "%s Test " COL_GREEN " PASSED\n" COL_STOP, testname); return 1; } else { - fprintf(stderr, "%s Test \e[0;31mFAILED\e[m\n", testname); + fprintf(stderr, "%s Test " COL_RED " FAILED\n" COL_STOP, testname); return 0; } } diff --git a/tests/kernel_image_tests.c b/tests/kernel_image_tests.c new file mode 100644 index 00000000..62ca5602 --- /dev/null +++ b/tests/kernel_image_tests.c @@ -0,0 +1,257 @@ +/* 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. + * + * Tests for kernel image library. + */ + +#include <stdio.h> +#include <stdlib.h> + +#include "file_keys.h" +#include "kernel_image.h" +#include "rsa_utility.h" +#include "sha_utility.h" +#include "utility.h" + +/* ANSI Color coding sequences. */ +#define COL_GREEN "\e[1;32m" +#define COL_RED "\e[0;31m" +#define COL_STOP "\e[m" + +int TEST_EQ(int result, int expected_result, char* testname) { + if (result == expected_result) { + fprintf(stderr, "%s Test " COL_GREEN " PASSED\n" COL_STOP, testname); + return 1; + } + else { + fprintf(stderr, "%s Test " COL_RED " FAILED\n" COL_STOP, testname); + return 0; + } +} + +KernelImage* GenerateTestKernelImage(int firmware_sign_algorithm, + int kernel_sign_algorithm, + uint8_t* kernel_sign_key, + int kernel_key_version, + int kernel_version, + int kernel_len) { + KernelImage* image = KernelImageNew(); + uint8_t* header_checksum; + DigestContext ctx; + + Memcpy(image->magic, KERNEL_MAGIC, KERNEL_MAGIC_SIZE); + image->header_version = 1; + image->firmware_sign_algorithm = firmware_sign_algorithm; + image->kernel_sign_algorithm = kernel_sign_algorithm; + image->kernel_key_version = kernel_key_version; + image->kernel_sign_key = (uint8_t*) Malloc( + RSAProcessedKeySize(image->kernel_sign_algorithm)); + Memcpy(image->kernel_sign_key, kernel_sign_key, + RSAProcessedKeySize(image->kernel_sign_algorithm)); + + /* Update correct header length. */ + image->header_len = (sizeof(image->header_version) + + sizeof(image->header_len) + + sizeof(image->firmware_sign_algorithm) + + sizeof(image->kernel_sign_algorithm) + + RSAProcessedKeySize(image->kernel_sign_algorithm) + + sizeof(image->kernel_key_version) + + sizeof(image->header_checksum)); + + /* Calculate SHA-512 digest on header and populate header_checksum. */ + DigestInit(&ctx, SHA512_DIGEST_ALGORITHM); + DigestUpdate(&ctx, (uint8_t*) &image->header_version, + sizeof(image->header_version)); + DigestUpdate(&ctx, (uint8_t*) &image->header_len, + sizeof(image->header_len)); + DigestUpdate(&ctx, (uint8_t*) &image->firmware_sign_algorithm, + sizeof(image->firmware_sign_algorithm)); + DigestUpdate(&ctx, (uint8_t*) &image->kernel_sign_algorithm, + sizeof(image->kernel_sign_algorithm)); + DigestUpdate(&ctx, (uint8_t*) &image->kernel_key_version, + sizeof(image->kernel_key_version)); + DigestUpdate(&ctx, image->kernel_sign_key, + RSAProcessedKeySize(image->kernel_sign_algorithm)); + header_checksum = DigestFinal(&ctx); + Memcpy(image->header_checksum, header_checksum, SHA512_DIGEST_SIZE); + Free(header_checksum); + + /* Populate kernel options and data with dummy data. */ + image->kernel_version = kernel_version; + image->options.version[0] = 1; + image->options.version[1] = 1; + image->options.kernel_len = kernel_len; + image->options.kernel_load_addr = 0; + image->options.kernel_entry_addr = 0; + image->kernel_key_signature = image->kernel_signature = NULL; + image->kernel_data = Malloc(kernel_len); + Memset(image->kernel_data, 'F', kernel_len); + + return image; +} + +#define DEV_MODE_ENABLED 1 +#define DEV_MODE_DISABLED 0 + +/* Normal Kernel Blob Verification Tests. */ +int VerifyKernelTest(uint8_t* kernel_blob, uint8_t* firmware_key_blob) { + int success = 1; + if (!TEST_EQ(VerifyKernel(firmware_key_blob, kernel_blob, DEV_MODE_ENABLED), + VERIFY_KERNEL_SUCCESS, + "Normal Kernel Blob Verification (Dev Mode)")) + success = 0; + + if (!TEST_EQ(VerifyKernel(firmware_key_blob, kernel_blob, DEV_MODE_DISABLED), + VERIFY_KERNEL_SUCCESS, + "Normal Kernel Blob Verification (Trusted)")) + success = 0; + return success; +} + + +/* Normal KernelImage Verification Tests. */ +int VerifyKernelImageTest(KernelImage* image, + RSAPublicKey* firmware_key) { + int success = 1; + if (!TEST_EQ(VerifyKernelImage(firmware_key, image, DEV_MODE_ENABLED), + VERIFY_KERNEL_SUCCESS, + "Normal KernelImage Verification (Dev Mode)")) + success = 0; + + if (!TEST_EQ(VerifyKernelImage(firmware_key, image, DEV_MODE_DISABLED), + VERIFY_KERNEL_SUCCESS, + "Normal KernelImage Verification (Trusted)")) + success = 0; + return success; +} + +/* Tampered KernelImage Verification Tests. */ +int VerifyKernelImageTamperTest(KernelImage* image, + RSAPublicKey* firmware_key) { + int success = 1; + fprintf(stderr, "[[Tampering with kernel config....]]\n"); + image->options.kernel_load_addr = 0xFFFF; + if (!TEST_EQ(VerifyKernelImage(firmware_key, image, DEV_MODE_ENABLED), + VERIFY_KERNEL_CONFIG_SIGNATURE_FAILED, + "KernelImage Config Tamper Verification (Dev Mode)")) + success = 0; + if (!TEST_EQ(VerifyKernelImage(firmware_key, image, DEV_MODE_DISABLED), + VERIFY_KERNEL_CONFIG_SIGNATURE_FAILED, + "KernelImage Config Tamper Verification (Trusted)")) + success = 0; + image->options.kernel_load_addr = 0; + + image->kernel_data[0] = 'T'; + if (!TEST_EQ(VerifyKernelImage(firmware_key, image, DEV_MODE_ENABLED), + VERIFY_KERNEL_SIGNATURE_FAILED, + "KernelImage Tamper Verification (Dev Mode)")) + success = 0; + if (!TEST_EQ(VerifyKernelImage(firmware_key, image, DEV_MODE_DISABLED), + VERIFY_KERNEL_SIGNATURE_FAILED, + "KernelImage Tamper Verification (Trusted)")) + success = 0; + image->kernel_data[0] = 'F'; + + + fprintf(stderr, "[[Tampering with kernel key signature...]]\n"); + image->kernel_key_signature[0] = 0xFF; + image->kernel_key_signature[1] = 0x00; + if (!TEST_EQ(VerifyKernelImage(firmware_key, image, DEV_MODE_ENABLED), + VERIFY_KERNEL_SUCCESS, + "KernelImage Key Signature Tamper Verification (Dev Mode)")) + success = 0; + if (!TEST_EQ(VerifyKernelImage(firmware_key, image, DEV_MODE_DISABLED), + VERIFY_KERNEL_KEY_SIGNATURE_FAILED, + "KernelImage Key Signature Tamper Verification (Trusted)")) + success = 0; + + return success; +} + +int main(int argc, char* argv[]) { + uint32_t len; + uint8_t* kernel_sign_key_buf = NULL; + uint8_t* firmware_key_blob = NULL; + uint8_t* kernel_blob = NULL; + KernelImage* image = NULL; + RSAPublicKey* firmware_key = NULL; + int error_code = 1; + char* tmp_kernelblob_file = ".tmpKernelBlob"; + + if(argc != 7) { + fprintf(stderr, "Usage: %s <firmware signing algorithm> " /* argv[1] */ + "<kernel signing algorithm> " /* argv[2] */ + "<firmware key> " /* argv[3] */ + "<processed firmware pubkey> " /* argv[4] */ + "<kernel signing key> " /* argv[5] */ + "<processed kernel signing key>\n", /* argv[6] */ + argv[0]); + return -1; + } + + /* Read verification keys and create a test image. */ + firmware_key = RSAPublicKeyFromFile(argv[4]); + firmware_key_blob = BufferFromFile(argv[4], &len); + kernel_sign_key_buf = BufferFromFile(argv[6], &len); + if (!firmware_key || !kernel_sign_key_buf || !kernel_sign_key_buf) { + error_code = 1; + goto failure; + } + + image = GenerateTestKernelImage(atoi(argv[1]), + atoi(argv[2]), + kernel_sign_key_buf, + 1, /* Kernel Key Version */ + 1, /* Kernel Version */ + 1000); /* Kernel Size */ + if (!image) { + error_code = 1; + goto failure; + } + + /* Generate and populate signatures. */ + if (!AddKernelKeySignature(image, argv[3])) { + fprintf(stderr, "Couldn't create key signature.\n"); + error_code = 1; + goto failure; + } + + if (!AddKernelSignature(image, argv[5], image->kernel_sign_algorithm)) { + fprintf(stderr, "Couldn't create firmware and preamble signature.\n"); + error_code = 1; + goto failure; + } + + /* Generate a firmware binary blob from image. + * + * TODO(gauravsh): Add a function to directly generate a binary + * blob buffer from a KernelImage instead of indirectly writing to a file + * and reading it into a buffer. + */ + if (!WriteKernelImage(tmp_kernelblob_file, image)) { + fprintf(stderr, "Couldn't create a temporary kernel blob file.\n"); + error_code = 1; + goto failure; + } + kernel_blob = BufferFromFile(tmp_kernelblob_file, &len); + + /* Test Kernel blob verify operations. */ + if (!VerifyKernelTest(kernel_blob, firmware_key_blob)) + error_code = 255; + + /* Test KernelImage verify operations. */ + if (!VerifyKernelImageTest(image, firmware_key)) + error_code = 255; + if (!VerifyKernelImageTamperTest(image, firmware_key)) + error_code = 255; + +failure: + Free(kernel_blob); + KernelImageFree(image); + Free(kernel_sign_key_buf); + Free(firmware_key_blob); + Free(firmware_key); + + return error_code; +} diff --git a/tests/run_image_verification_tests.sh b/tests/run_image_verification_tests.sh new file mode 100755 index 00000000..19feae23 --- /dev/null +++ b/tests/run_image_verification_tests.sh @@ -0,0 +1,100 @@ +#!/bin/bash + +# 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. + +# Run verified boot firmware and kernel verification tests. + +return_code=0 +hash_algos=( sha1 sha256 sha512 ) +key_lengths=( 1024 2048 4096 8192 ) +TEST_FILE=test_file +TEST_FILE_SIZE=1000000 + +COL_RED='\E[31;1m' +COL_GREEN='\E[32;1m' +COL_YELLOW='\E[33;1m' +COL_BLUE='\E[34;1m' +COL_STOP='\E[0;m' + +function test_firmware_verification { + algorithmcounter=0 + for keylen in ${key_lengths[@]} + do + for hashalgo in ${hash_algos[@]} + do + echo -e "For Root key ${COL_YELLOW}RSA-$keylen/$hashalgo${COL_STOP}:" + cd ${UTIL_DIR} && ${TEST_DIR}/firmware_image_tests $algorithmcounter \ + ${TEST_DIR}/testkeys/key_rsa8192.pem \ + ${TEST_DIR}/testkeys/key_rsa8192.keyb \ + ${TEST_DIR}/testkeys/key_rsa${keylen}.pem \ + ${TEST_DIR}/testkeys/key_rsa${keylen}.keyb + if [ $? -ne 0 ] + then + return_code=255 + fi + let algorithmcounter=algorithmcounter+1 + done + done +} + +function test_kernel_verification { +# Test for various combinations of firmware signing algorithm and +# kernel signing algorithm + firmware_algorithmcounter=0 + kernel_algorithmcounter=0 + for firmware_keylen in ${key_lengths[@]} + do + for firmware_hashalgo in ${hash_algos[@]} + do + let kernel_algorithmcounter=0 + for kernel_keylen in ${key_lengths[@]} + do + for kernel_hashalgo in ${hash_algos[@]} + do + echo -e "For ${COL_YELLOW}Firmware signing algorithm \ +RSA-${firmware_keylen}/${firmware_hashalgo}${COL_STOP} \ +and ${COL_YELLOW}Kernel signing algorithm RSA-${kernel_keylen}/\ +${kernel_hashalgo}${COL_STOP}" + cd ${UTIL_DIR} && ${TEST_DIR}/kernel_image_tests \ + $firmware_algorithmcounter $kernel_algorithmcounter \ + ${TEST_DIR}/testkeys/key_rsa${firmware_keylen}.pem \ + ${TEST_DIR}/testkeys/key_rsa${firmware_keylen}.keyb \ + ${TEST_DIR}/testkeys/key_rsa${kernel_keylen}.pem \ + ${TEST_DIR}/testkeys/key_rsa${kernel_keylen}.keyb + if [ $? -ne 0 ] + then + return_code=255 + fi + let kernel_algorithmcounter=kernel_algorithmcounter+1 + done + done + let firmware_algorithmcounter=firmware_algorithmcounter+1 + done + done +} + +# Determine script directory. +if [[ $0 == '/'* ]]; +then + SCRIPT_DIR="`dirname $0`" +elif [[ $0 == './'* ]]; +then + SCRIPT_DIR="`pwd`" +else + SCRIPT_DIR="`pwd`"/"`dirname $0`" +fi +UTIL_DIR=`dirname ${SCRIPT_DIR}`/utils +KEY_DIR=${SCRIPT_DIR}/testkeys +TEST_DIR=${SCRIPT_DIR}/ + +echo +echo "Testing high-level firmware image verification..." +test_firmware_verification + +echo +echo "Testing high-level kernel image verification..." +test_kernel_verification + +exit $return_code diff --git a/tests/run_rsa_tests.sh b/tests/run_rsa_tests.sh index 2196ddd7..3ca1504d 100755 --- a/tests/run_rsa_tests.sh +++ b/tests/run_rsa_tests.sh @@ -4,8 +4,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -# Run tests for cryptographic routine implementations - Message digests -# and RSA Signature verification. +# Run tests for RSA Signature verification. return_code=0 hash_algos=( sha1 sha256 sha512 ) @@ -52,23 +51,6 @@ function test_signatures { let algorithmcounter=algorithmcounter+1 done done -} - -function test_verification { - algorithmcounter=0 - for keylen in ${key_lengths[@]} - do - for hashalgo in ${hash_algos[@]} - do - echo -e "For ${COL_YELLOW}RSA-$keylen and $hashalgo${COL_STOP}:" - cd ${UTIL_DIR} && ${TEST_DIR}/firmware_image_tests $algorithmcounter \ - ${TEST_DIR}/testkeys/key_rsa8192.pem \ - ${TEST_DIR}/testkeys/key_rsa8192.keyb \ - ${TEST_DIR}/testkeys/key_rsa${keylen}.pem \ - ${TEST_DIR}/testkeys/key_rsa${keylen}.keyb - let algorithmcounter=algorithmcounter+1 - done - done echo -e "Peforming ${COL_YELLOW}PKCS #1 v1.5 Padding Tests${COL_STOP}..." ${TEST_DIR}/rsa_padding_test ${TEST_DIR}/testkeys/rsa_padding_test_pubkey.keyb } @@ -107,8 +89,12 @@ echo "Testing signature verification..." test_signatures echo -echo "Testing high-level image verification..." -test_verification +echo "Testing high-level firmware image verification..." +test_firmware_verification + +echo +echo "Testing high-level kernel image verification..." +test_kernel_verification echo echo "Cleaning up..." diff --git a/utils/Makefile b/utils/Makefile index e9b6b67a..d0f8ea59 100644 --- a/utils/Makefile +++ b/utils/Makefile @@ -12,7 +12,7 @@ LIBS = -lcrypto FIRMWARELIBS = $(TOP)/crypto/libcrypto.a $(TOP)/common/libcommon.a all: dumpRSAPublicKey verify_data signature_digest firmware_utility \ - file_keys.o firmware_image.o + file_keys.o firmware_image.o kernel_image.o dumpRSAPublicKey: dumpRSAPublicKey.c $(CC) $(CFLAGS) $(LIBS) $< -o $@ @@ -34,7 +34,9 @@ file_keys.o: file_keys.c firmware_image.o: firmware_image.c $(CC) $(CFLAGS) -ansi $(INCLUDES) -c $< -o $@ +kernel_image.o: kernel_image.c + $(CC) $(CFLAGS) -ansi $(INCLUDES) -c $< -o $@ clean: rm -f dumpRSAPublicKey verify_data signature_digest firmware_image.o \ - file_keys.o firmware_utility + kernel_image.o file_keys.o firmware_utility diff --git a/utils/kernel_image.c b/utils/kernel_image.c new file mode 100644 index 00000000..8f874ec8 --- /dev/null +++ b/utils/kernel_image.c @@ -0,0 +1,614 @@ +/* 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 generating and manipulating a verified boot kernel image. + */ + +#include "kernel_image.h" + +#include <fcntl.h> +#include <stdio.h> +#include <sys/types.h> +#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 KernelImage + * structure. */ +#define FIELD_LEN(field) (sizeof(((KernelImage*)0)->field)) + +KernelImage* KernelImageNew(void) { + KernelImage* image = (KernelImage*) Malloc(sizeof(KernelImage)); + if (image) { + image->kernel_sign_key = NULL; + image->kernel_key_signature = NULL; + image->config_signature = NULL; + image->kernel_signature = NULL; + image->kernel_data = NULL; + } + return image; +} + +void KernelImageFree(KernelImage* image) { + if (image) { + Free(image->kernel_sign_key); + Free(image->kernel_key_signature); + Free(image->config_signature); + Free(image->kernel_signature); + Free(image->kernel_data); + Free(image); + } +} + +KernelImage* ReadKernelImage(const char* input_file) { + uint32_t file_size; + int image_len = 0; /* Total size of the kernel image. */ + int header_len = 0; + int firmware_sign_key_len; + int kernel_key_signature_len; + int kernel_sign_key_len; + int kernel_signature_len; + uint8_t* kernel_buf; + MemcpyState st; + KernelImage* image = KernelImageNew(); + + if (!image) + return NULL; + + kernel_buf = BufferFromFile(input_file, &file_size); + image_len = file_size; + + st.remaining_len = image_len; + st.remaining_buf = kernel_buf; + + /* Read and compare magic bytes. */ + if (!StatefulMemcpy(&st, &image->magic, KERNEL_MAGIC_SIZE)) + goto parse_failure; + + if (SafeMemcmp(image->magic, KERNEL_MAGIC, KERNEL_MAGIC_SIZE)) { + fprintf(stderr, "Wrong Kernel Magic.\n"); + goto parse_failure; + } + StatefulMemcpy(&st, &image->header_version, FIELD_LEN(header_version)); + StatefulMemcpy(&st, &image->header_len, FIELD_LEN(header_len)); + StatefulMemcpy(&st, &image->firmware_sign_algorithm, + FIELD_LEN(firmware_sign_algorithm)); + StatefulMemcpy(&st, &image->kernel_sign_algorithm, + FIELD_LEN(kernel_sign_algorithm)); + + /* Valid Kernel Key signing algorithm. */ + if (image->firmware_sign_algorithm >= kNumAlgorithms) + goto parse_failure; + + /* Valid Kernel Signing Algorithm? */ + if (image->kernel_sign_algorithm >= kNumAlgorithms) + goto parse_failure; + + /* Compute size of pre-processed RSA public keys and signatures. */ + firmware_sign_key_len = RSAProcessedKeySize(image->firmware_sign_algorithm); + /* TODO(gauravsh): Make siglen_map track the signature length in number + * of bytes rather than 32-bit words. */ + kernel_key_signature_len = (siglen_map[image->firmware_sign_algorithm] * + sizeof(uint32_t)); + kernel_sign_key_len = RSAProcessedKeySize(image->kernel_sign_algorithm); + kernel_signature_len = (siglen_map[image->kernel_sign_algorithm] * + sizeof(uint32_t)); + + /* Check whether key header length is correct. */ + header_len = (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_len + + FIELD_LEN(header_checksum)); + + if (header_len != image->header_len) { + fprintf(stderr, "Header length mismatch. Got: %d, Expected: %d\n", + image->header_len, header_len); + goto parse_failure; + } + + /* Read pre-processed public half of the kernel signing key. */ + StatefulMemcpy(&st, &image->kernel_key_version, + FIELD_LEN(kernel_key_version)); + image->kernel_sign_key = (uint8_t*) Malloc(kernel_sign_key_len); + StatefulMemcpy(&st, image->kernel_sign_key, kernel_sign_key_len); + StatefulMemcpy(&st, image->header_checksum, FIELD_LEN(header_checksum)); + + /* Read key signature. */ + StatefulMemcpy(&st, image->kernel_key_signature, + FIELD_LEN(kernel_key_signature)); + + /* Read the kernel config. */ + StatefulMemcpy(&st, &image->kernel_version, FIELD_LEN(kernel_version)); + StatefulMemcpy(&st, &image->options.version, FIELD_LEN(options.version)); + StatefulMemcpy(&st, &image->options.kernel_len, + FIELD_LEN(options.kernel_len)); + StatefulMemcpy(&st, &image->options.kernel_load_addr, + FIELD_LEN(options.kernel_load_addr)); + StatefulMemcpy(&st, &image->options.kernel_entry_addr, + FIELD_LEN(options.kernel_entry_addr)); + + /* Read kernel config signature. */ + image->config_signature = (uint8_t*) Malloc(kernel_signature_len); + StatefulMemcpy(&st, image->config_signature, kernel_signature_len); + + image->kernel_signature = (uint8_t*) Malloc(kernel_signature_len); + StatefulMemcpy(&st, image->kernel_signature, kernel_signature_len); + + image->kernel_data = (uint8_t*) Malloc(image->options.kernel_len); + StatefulMemcpy(&st, image->kernel_data, image->options.kernel_len); + + if(st.remaining_len != 0) /* Overrun or underrun. */ + goto parse_failure; + + Free(kernel_buf); + return image; + +parse_failure: + Free(kernel_buf); + return NULL; +} + +void WriteKernelHeader(int fd, KernelImage* image) { + int kernel_sign_key_len; + write(fd, &image->header_version, FIELD_LEN(header_version)); + write(fd, &image->header_len, FIELD_LEN(header_len)); + write(fd, &image->firmware_sign_algorithm, + FIELD_LEN(firmware_sign_algorithm)); + write(fd, &image->kernel_sign_algorithm, + FIELD_LEN(kernel_sign_algorithm)); + write(fd, &image->kernel_key_version, FIELD_LEN(kernel_key_version)); + kernel_sign_key_len = (image->header_len - + FIELD_LEN(header_version) - + FIELD_LEN(header_len) - + FIELD_LEN(firmware_sign_algorithm) - + FIELD_LEN(kernel_sign_algorithm) - + FIELD_LEN(kernel_key_version) - + FIELD_LEN(header_checksum)); + write(fd, image->kernel_sign_key, kernel_sign_key_len); + write(fd, &image->header_checksum, FIELD_LEN(header_checksum)); +} + +void WriteKernelConfig(int fd, KernelImage* image) { + write(fd, &image->kernel_version, FIELD_LEN(kernel_version)); + write(fd, image->options.version, FIELD_LEN(options.version)); + write(fd, &image->options.kernel_len, FIELD_LEN(options.kernel_len)); + write(fd, &image->options.kernel_load_addr, + FIELD_LEN(options.kernel_load_addr)); + write(fd, &image->options.kernel_entry_addr, + FIELD_LEN(options.kernel_entry_addr)); +} + +KernelImage* WriteKernelImage(const char* input_file, + KernelImage* image) { + int fd; + int kernel_key_signature_len; + int kernel_signature_len; + if (!image) + return NULL; + if (-1 == (fd = creat(input_file, + S_IRUSR | S_IWUSR))) { /* Owner has R/W permissions. */ + fprintf(stderr, "Couldn't open file for writing.\n"); + return NULL; + } + + kernel_key_signature_len = (siglen_map[image->firmware_sign_algorithm] * + sizeof(uint32_t)); + kernel_signature_len = (siglen_map[image->kernel_sign_algorithm] * + sizeof(uint32_t)); + + write(fd, image->magic, FIELD_LEN(magic)); + WriteKernelHeader(fd, image); + write(fd, image->kernel_key_signature, kernel_key_signature_len); + WriteKernelConfig(fd, image); + write(fd, image->config_signature, kernel_signature_len); + write(fd, image->kernel_signature, kernel_signature_len); + write(fd, image->kernel_data, image->options.kernel_len); + + close(fd); + return image; +} + +void PrintKernelImage(const KernelImage* image) { + if (!image) + return; + + /* Print header. */ + printf("Header Length = %d\n" + "Firmware Signing key algorithm id = %d\n" + "Kernel Signing key algorithm id = %d\n" + "Kernel Signature Algorithm = %s\n" + "Kernel Key Version = %d\n\n", + image->header_len, + image->firmware_sign_algorithm, + image->kernel_sign_algorithm, + algo_strings[image->kernel_sign_algorithm], + image->kernel_key_version); + /* TODO(gauravsh): Output hash and key signature here? */ + /* Print preamble. */ + printf("Kernel Version = %d\n" + "Kernel Config Version = %d.%d\n" + "kernel Length = %d\n" + "Kernel Load Address = %" PRId64 "\n" + "Kernel Entry Address = %" PRId64 "\n\n", + image->kernel_version, + image->options.version[0], image->options.version[1], + image->options.kernel_len, + image->options.kernel_load_addr, + image->options.kernel_entry_addr); + /* 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); + 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, + int* kernel_len) { + uint32_t len, config_len; + config_len = (FIELD_LEN(kernel_version) + + FIELD_LEN(options.version)+ + FIELD_LEN(options.kernel_len) + + FIELD_LEN(options.kernel_load_addr) + + FIELD_LEN(options.kernel_entry_addr)); + 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)), + sizeof(len)); + *kernel_len = (int) len; + return 0; +} + +int VerifyKernelData(RSAPublicKey* kernel_sign_key, + const uint8_t* kernel_data_start, + int kernel_len, + int algorithm) { + int signature_len = siglen_map[algorithm] * sizeof(uint32_t); + if (!RSAVerifyBinary_f(NULL, kernel_sign_key, /* Key to use. */ + kernel_data_start + signature_len, /* Data to + * verify */ + kernel_len, /* Length of data. */ + kernel_data_start, /* Expected Signature */ + algorithm)) + return VERIFY_KERNEL_SIGNATURE_FAILED; + 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, 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] * sizeof(uint32_t); + kernel_key_signature_len = siglen_map[firmware_sign_algorithm] * + sizeof(uint32_t); + + /* 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))) + return error_code; /* AKA jump to recovery. */ + /* Only continue if kernel data verification succeeds. */ + kernel_ptr = (config_ptr + + FIELD_LEN(kernel_version) + + FIELD_LEN(options.version) + + FIELD_LEN(options.kernel_len) + + FIELD_LEN(options.kernel_entry_addr) + + FIELD_LEN(options.kernel_load_addr) + + kernel_signature_len); + + if ((error_code = VerifyKernelData(kernel_sign_key, kernel_ptr, kernel_len, + kernel_sign_algorithm))) + return error_code; /* AKA jump to recovery. */ + return 0; /* Success! */ +} + +int VerifyKernelImage(const RSAPublicKey* firmware_key, + const KernelImage* image, + const int dev_mode) { + RSAPublicKey* kernel_sign_key; + uint8_t* header_digest = NULL; + uint8_t* config_digest = NULL; + uint8_t* kernel_digest = NULL; + int kernel_sign_key_size; + int kernel_signature_size; + int error_code = 0; + DigestContext ctx; + + if (!image) + return VERIFY_KERNEL_INVALID_IMAGE; + + /* Verify kernel key signature on the key header if we + * 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 (image->firmware_sign_algorithm >= kNumAlgorithms) + return VERIFY_KERNEL_INVALID_ALGORITHM; + if (image->kernel_sign_algorithm >= kNumAlgorithms) + return VERIFY_KERNEL_INVALID_ALGORITHM; + + if (!dev_mode) { + DigestInit(&ctx, image->firmware_sign_algorithm); + DigestUpdate(&ctx, (uint8_t*) &image->header_version, + FIELD_LEN(header_version)); + DigestUpdate(&ctx, (uint8_t*) &image->header_len, + FIELD_LEN(header_len)); + DigestUpdate(&ctx, (uint8_t*) &image->firmware_sign_algorithm, + FIELD_LEN(firmware_sign_algorithm)); + DigestUpdate(&ctx, (uint8_t*) &image->kernel_sign_algorithm, + FIELD_LEN(kernel_sign_algorithm)); + DigestUpdate(&ctx, (uint8_t*) &image->kernel_key_version, + FIELD_LEN(kernel_key_version)); + DigestUpdate(&ctx, image->kernel_sign_key, + RSAProcessedKeySize(image->kernel_sign_algorithm)); + DigestUpdate(&ctx, image->header_checksum, + FIELD_LEN(header_checksum)); + header_digest = DigestFinal(&ctx); + if (!RSA_verify(firmware_key, image->kernel_key_signature, + siglen_map[image->firmware_sign_algorithm] * + sizeof(uint32_t), + image->firmware_sign_algorithm, + header_digest)) { + fprintf(stderr, "VerifyKernelImage(): Key signature check failed.\n"); + error_code = VERIFY_KERNEL_KEY_SIGNATURE_FAILED; + goto verify_failure; + } + } + + /* Get kernel signing key to verify the rest of the kernel. */ + kernel_sign_key_size = RSAProcessedKeySize(image->kernel_sign_algorithm); + kernel_sign_key = RSAPublicKeyFromBuf(image->kernel_sign_key, + kernel_sign_key_size); + kernel_signature_size = siglen_map[image->kernel_sign_algorithm] * + sizeof(uint32_t); + + /* Verify kernel config signature. */ + DigestInit(&ctx, image->kernel_sign_algorithm); + DigestUpdate(&ctx, (uint8_t*) &image->kernel_version, + FIELD_LEN(kernel_version)); + DigestUpdate(&ctx, (uint8_t*) &image->options.version, + FIELD_LEN(options.version)); + DigestUpdate(&ctx, (uint8_t*) &image->options.kernel_len, + FIELD_LEN(options.kernel_len)); + DigestUpdate(&ctx, (uint8_t*) &image->options.kernel_load_addr, + FIELD_LEN(options.kernel_load_addr)); + DigestUpdate(&ctx, (uint8_t*) &image->options.kernel_entry_addr, + FIELD_LEN(options.kernel_entry_addr)); + config_digest = DigestFinal(&ctx); + if (!RSA_verify(kernel_sign_key, image->config_signature, + kernel_signature_size, image->kernel_sign_algorithm, + config_digest)) { + error_code = VERIFY_KERNEL_CONFIG_SIGNATURE_FAILED; + goto verify_failure; + } + + /* Verify firmware signature. */ + kernel_digest = DigestBuf(image->kernel_data, + image->options.kernel_len, + image->kernel_sign_algorithm); + if(!RSA_verify(kernel_sign_key, image->kernel_signature, + kernel_signature_size, image->kernel_sign_algorithm, + kernel_digest)) { + error_code = VERIFY_KERNEL_SIGNATURE_FAILED; + goto verify_failure; + } + +verify_failure: + Free(kernel_digest); + Free(config_digest); + Free(header_digest); + return error_code; +} + +const char* VerifyKernelErrorString(int error) { + return kVerifyKernelErrors[error]; +} + +int AddKernelKeySignature(KernelImage* image, const char* firmware_key_file) { + int tmp_hdr_fd; + char* tmp_hdr_file = ".tmpKernelHdrFile"; + uint8_t* signature; + int signature_len = siglen_map[image->firmware_sign_algorithm] * + sizeof(uint32_t); + + if(-1 == (tmp_hdr_fd = creat(tmp_hdr_file, S_IRWXU))) { + fprintf(stderr, "Could not open temporary file for writing " + "kernel header.\n"); + return 0; + } + WriteKernelHeader(tmp_hdr_fd, image); + close(tmp_hdr_fd); + if (!(signature = SignatureFile(tmp_hdr_file, firmware_key_file, + image->firmware_sign_algorithm))) + return 0; + image->kernel_key_signature = Malloc(signature_len); + Memcpy(image->kernel_key_signature, signature, signature_len); + return 1; +} + +int AddKernelSignature(KernelImage* image, const char* kernel_signing_key_file, + int algorithm) { + int tmp_config_fd; + char* tmp_config_file = ".tmpConfigFile"; + int tmp_kernel_fd; + char* tmp_kernel_file = ".tmpKernelFile"; + uint8_t* config_signature; + uint8_t* kernel_signature; + int signature_len = siglen_map[algorithm] * sizeof(uint32_t); + + /* Write config to a file. */ + if(-1 == (tmp_config_fd = creat(tmp_config_file, S_IRWXU))) { + fprintf(stderr, "Could not open temporary file for writing " + "kernel config.\n"); + return 0; + } + WriteKernelConfig(tmp_config_fd, image); + close(tmp_config_fd); + if (!(config_signature = SignatureFile(tmp_config_file, + kernel_signing_key_file, + algorithm))) + return 0; + image->config_signature = (uint8_t*) Malloc(signature_len); + Memcpy(image->config_signature, config_signature, signature_len); + Free(config_signature); + + if (-1 == (tmp_kernel_fd = creat(tmp_kernel_file, S_IRWXU))) { + fprintf(stderr, "Could not open temporary file for writing " + "kernel.\n"); + return 0; + } + write(tmp_kernel_fd, image->kernel_data, image->options.kernel_len); + close(tmp_kernel_fd); + + if (!(kernel_signature = SignatureFile(tmp_kernel_file, + kernel_signing_key_file, + algorithm))) { + fprintf(stderr, "Could not compute signature on the kernel.\n"); + return 0; + } + image->kernel_signature = (uint8_t*) Malloc(signature_len); + Memcpy(image->kernel_signature, kernel_signature, signature_len); + Free(kernel_signature); + return 1; +} |