From b8ff397674fb98c1d7eea864e7fa571369675131 Mon Sep 17 00:00:00 2001 From: Randall Spangler Date: Wed, 27 Aug 2014 13:34:35 -0700 Subject: vboot: Add system-level test for LoadKernel() This creates a disk image and verifies a kernel can be loaded from it. It is roughly analogous to vb2_firmware_tests.sh, but at the kernel step instead of the firmware step. This will get more interesting in the near future, with the upcoming addition of a streaming API to read the kernel. BUG=chromium:408265 BRANCH=none TEST=make runtests Change-Id: Icc9e6d0e318c4bd38fc9ab1ad704da99232822e1 Signed-off-by: Randall Spangler Reviewed-on: https://chromium-review.googlesource.com/214508 Reviewed-by: Daisuke Nojiri --- Makefile | 4 +- futility/cmd_verify_kernel.c | 141 +++++++++++++++++++++++++++++++++++++++++++ tests/load_kernel_tests.sh | 73 ++++++++++++++++++++++ 3 files changed, 217 insertions(+), 1 deletion(-) create mode 100644 futility/cmd_verify_kernel.c create mode 100755 tests/load_kernel_tests.sh diff --git a/Makefile b/Makefile index 300d317c..d50cc4ff 100644 --- a/Makefile +++ b/Makefile @@ -543,7 +543,8 @@ FUTIL_SRCS = \ futility/cmd_vbutil_firmware.c \ futility/cmd_vbutil_kernel.c \ futility/cmd_vbutil_key.c \ - futility/cmd_vbutil_keyblock.c + futility/cmd_vbutil_keyblock.c \ + futility/cmd_verify_kernel.c ifneq (${VBOOT2},) FUTIL_SRCS += \ @@ -1086,6 +1087,7 @@ runcgpttests: test_setup .PHONY: runtestscripts runtestscripts: test_setup genfuzztestcases + tests/load_kernel_tests.sh tests/run_cgpt_tests.sh ${BUILD_RUN}/cgpt/cgpt tests/run_cgpt_tests.sh ${BUILD_RUN}/cgpt/cgpt -N=512,32,1,3 tests/run_preamble_tests.sh diff --git a/futility/cmd_verify_kernel.c b/futility/cmd_verify_kernel.c new file mode 100644 index 00000000..8c413427 --- /dev/null +++ b/futility/cmd_verify_kernel.c @@ -0,0 +1,141 @@ +/* Copyright (c) 2014 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. + * + * Routines for verifying a kernel or disk image + */ + +#include +#include +#include + +#include "host_common.h" +#include "util_misc.h" +#include "vboot_common.h" +#include "vboot_api.h" +#include "vboot_kernel.h" +#include "futility.h" + +static uint8_t *diskbuf; + +static uint8_t shared_data[VB_SHARED_DATA_MIN_SIZE]; +static VbSharedDataHeader *shared = (VbSharedDataHeader *)shared_data; +static VbNvContext nvc; + +static LoadKernelParams params; +static VbCommonParams cparams; + +VbError_t VbExDiskRead(VbExDiskHandle_t handle, uint64_t lba_start, + uint64_t lba_count, void *buffer) +{ + if (handle != (VbExDiskHandle_t)1) + return VBERROR_UNKNOWN; + if (lba_start > params.ending_lba) + return VBERROR_UNKNOWN; + if (lba_start + lba_count > params.ending_lba + 1) + return VBERROR_UNKNOWN; + + memcpy(buffer, diskbuf + lba_start * 512, lba_count * 512); + return VBERROR_SUCCESS; +} + +VbError_t VbExDiskWrite(VbExDiskHandle_t handle, uint64_t lba_start, + uint64_t lba_count, const void *buffer) +{ + if (handle != (VbExDiskHandle_t)1) + return VBERROR_UNKNOWN; + if (lba_start > params.ending_lba) + return VBERROR_UNKNOWN; + if (lba_start + lba_count > params.ending_lba + 1) + return VBERROR_UNKNOWN; + + memcpy(diskbuf + lba_start * 512, buffer, lba_count * 512); + return VBERROR_SUCCESS; +} + +int do_verify_kernel(int argc, char *argv[]) +{ + VbPublicKey *kernkey; + uint64_t disk_bytes = 0; + int rv; + + const char *progname = strrchr(argv[0], '/'); + if (progname) + progname++; + else + progname = argv[0]; + + if (argc < 3) { + fprintf(stderr, + "usage: %s \n", progname); + return 1; + } + + /* Load disk file */ + /* TODO: better to nmap() in the long run */ + diskbuf = ReadFile(argv[1], &disk_bytes); + if (!diskbuf) { + fprintf(stderr, "Can't read disk file %s\n", argv[1]); + return 1; + } + + /* Read public key */ + kernkey = PublicKeyRead(argv[2]); + if (!kernkey) { + fprintf(stderr, "Can't read key file %s\n", argv[2]); + return 1; + } + + /* Set up shared data blob */ + VbSharedDataInit(shared, sizeof(shared_data)); + VbSharedDataSetKernelKey(shared, kernkey); + /* TODO: optional TPM current kernel version */ + + /* Set up params */ + params.shared_data_blob = shared_data; + params.shared_data_size = sizeof(shared_data); + params.disk_handle = (VbExDiskHandle_t)1; + params.bytes_per_lba = 512; + params.ending_lba = disk_bytes / 512 - 1; + + params.kernel_buffer_size = 16 * 1024 * 1024; + params.kernel_buffer = malloc(params.kernel_buffer_size); + if (!params.kernel_buffer) { + fprintf(stderr, "Can't allocate kernel buffer\n"); + return 1; + } + + /* GBB and cparams only needed by LoadKernel() in recovery mode */ + params.gbb_data = NULL; + params.gbb_size = 0; + + /* TODO: optional dev-mode flag */ + params.boot_flags = 0; + + /* + * LoadKernel() cares only about VBNV_DEV_BOOT_SIGNED_ONLY, and only in + * dev mode. So just use defaults. + */ + VbNvSetup(&nvc); + params.nv_context = &nvc; + + /* Try loading kernel */ + rv = LoadKernel(¶ms, &cparams); + if (rv != VBERROR_SUCCESS) { + fprintf(stderr, "LoadKernel() failed with code %d\n", rv); + return 1; + } + + printf("Found a good kernel.\n"); + printf("Partition number: %d\n", (int)params.partition_number); + printf("Bootloader address: 0x%" PRIx64 "\n", + params.bootloader_address); + + /* TODO: print other things (partition GUID, nv_context, shared_data) */ + + printf("Yaay!\n"); + return 0; +} + +DECLARE_FUTIL_COMMAND(verify_kernel, do_verify_kernel, + "Verifies a kernel / disk image"); diff --git a/tests/load_kernel_tests.sh b/tests/load_kernel_tests.sh new file mode 100755 index 00000000..74e91e40 --- /dev/null +++ b/tests/load_kernel_tests.sh @@ -0,0 +1,73 @@ +#!/bin/bash + +# Copyright (c) 2014 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. +# +# End-to-end test for vboot2 kernel verification + +# Load common constants and variables. +. "$(dirname "$0")/common.sh" + +set -e + +echo 'Creating test kernel' + +# Run tests in a dedicated directory for easy cleanup or debugging. +DIR="${TEST_DIR}/load_kernel_test_dir" +[ -d "$DIR" ] || mkdir -p "$DIR" +echo "Testing kernel verification in $DIR" +cd "$DIR" + +# Dummy kernel data +echo "hi there" > "dummy_config.txt" +dd if=/dev/urandom bs=16384 count=1 of="dummy_bootloader.bin" +dd if=/dev/urandom bs=32768 count=1 of="dummy_kernel.bin" + +# Pack kernel data key using original vboot utilities. +${BIN_DIR}/vbutil_key --pack datakey.test \ + --key ${TESTKEY_DIR}/key_rsa2048.keyb --algorithm 4 + +# Keyblock with kernel data key is signed by kernel subkey +# Flags=5 means dev=0 rec=0 +${BIN_DIR}/vbutil_keyblock --pack keyblock.test \ + --datapubkey datakey.test \ + --flags 5 \ + --signprivate ${SCRIPT_DIR}/devkeys/kernel_subkey.vbprivk + +# Kernel preamble is signed with the kernel data key +${BIN_DIR}/futility vbutil_kernel \ + --pack "kernel.test" \ + --keyblock "keyblock.test" \ + --signprivate ${TESTKEY_DIR}/key_rsa2048.sha256.vbprivk \ + --version 1 \ + --arch arm \ + --vmlinuz "dummy_kernel.bin" \ + --bootloader "dummy_bootloader.bin" \ + --config "dummy_config.txt" + +echo 'Verifying test kernel using vbutil_kernel' + +# Verify the kernel +${BIN_DIR}/futility vbutil_kernel \ + --verify "kernel.test" \ + --signpubkey ${SCRIPT_DIR}/devkeys/kernel_subkey.vbpubk + +happy 'Kernel verification succeeded' + +# Now create a dummy disk image +echo 'Creating test disk image' +dd if=/dev/zero of=disk.test bs=1024 count=1024 +cgpt create disk.test +cgpt add -i 1 -S 1 -P 1 -b 64 -s 960 -t kernel -l kernelA disk.test +cgpt show disk.test + +# And insert the kernel into it +dd if=kernel.test of=disk.test bs=512 seek=64 conv=notrunc + +# And verify it using futility +echo 'Verifying test disk image' +${BIN_DIR}/futility verify_kernel disk.test \ + ${SCRIPT_DIR}/devkeys/kernel_subkey.vbpubk + +happy 'Image verification succeeded' -- cgit v1.2.1