mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2025-11-26 19:25:02 +00:00
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
This commit is contained in:
200
include/kernel_image.h
Normal file
200
include/kernel_image.h
Normal file
@@ -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_ */
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
257
tests/kernel_image_tests.c
Normal file
257
tests/kernel_image_tests.c
Normal file
@@ -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;
|
||||
}
|
||||
100
tests/run_image_verification_tests.sh
Executable file
100
tests/run_image_verification_tests.sh
Executable file
@@ -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
|
||||
@@ -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..."
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
614
utils/kernel_image.c
Normal file
614
utils/kernel_image.c
Normal file
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user