Files
OpenCellular/firmware/lib21/misc.c
Julius Werner 187f069f89 vboot2: Add more precise recovery reasons to firmware verification
vboot1 kept track of an internal "LoadFirmware() check" value for both
firmware slots and encoded the value for the slot that managed to go
further in the verification flow into a special range of recovery
reasons. vboot2 instead uses the generic "invalid RW" reason for all
firmware verification failures and communicates further information
through the subcode.

While the subcode may be good enough for developers, it's difficult to
communicate failure reasons to "normal" users (like non-firmware
developers) on the TAB screen. Currently we just display a couple of
numbers that people won't know how to interpret and "RW firmware failed
signature check" for any verification error (including rollback, which
might be the most commonly encountered in practice).

Since our recovery reason space is big enough (and we don't reuse old
numbers anyway), we might as well reuse the more precise numbers (and
strings) from vboot1 to communicate the failure reason, even if we don't
implement its "which slot came further" algorithm. This patch translates
the most common/useful VBSD_LF_CHECK numbers into plain VB2_RECOVERY
reasons and uses them where appropriate.

CQ-DEPEND=CL:248400
BRANCH=veyron
BUG=None
TEST=make runtests VBOOT2=1
test_that my_jerry firmware_CorruptBothFwSigAB
firmware_CorruptBothFwBodyAB firmware_RollbackFirmware
(Confirmed that matched recovery reasons are the more precise ones in
the 0x10-0x1F range.)

Change-Id: I51ecf1b820d1faa40405cb84377380d6f3f6ca1d
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/248392
Reviewed-by: Bill Richardson <wfrichar@chromium.org>
2015-02-12 00:41:33 +00:00

240 lines
6.5 KiB
C

/* 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.
*
* Misc functions which need access to vb2_context but are not public APIs
*/
#include "2sysincludes.h"
#include "2api.h"
#include "2common.h"
#include "2misc.h"
#include "2nvstorage.h"
#include "2secdata.h"
#include "2sha.h"
#include "2rsa.h"
#include "vb2_common.h"
/**
* Read an object with a common struct header from a verified boot resource.
*
* On success, an object buffer will be allocated in the work buffer, the
* object will be stored into the buffer, and *buf_ptr will point to the
* object.
*
* @param ctx Vboot context
* @param index Resource index to read
* @param offset Byte offset within resource to start at
* @param buf_ptr Destination for object pointer
* @return VB2_SUCCESS, or error code on error.
*/
int vb2_read_resource_object(struct vb2_context *ctx,
enum vb2_resource_index index,
uint32_t offset,
struct vb2_workbuf *wb,
void **buf_ptr)
{
struct vb2_struct_common c;
void *buf;
int rv;
*buf_ptr = NULL;
/* Read the common header */
rv = vb2ex_read_resource(ctx, index, offset, &c, sizeof(c));
if (rv)
return rv;
/* Allocate a buffer for the object, now that we know how big it is */
buf = vb2_workbuf_alloc(wb, c.total_size);
if (!buf)
return VB2_ERROR_READ_RESOURCE_OBJECT_BUF;
/* Read the object */
rv = vb2ex_read_resource(ctx, index, offset, buf, c.total_size);
if (rv) {
vb2_workbuf_free(wb, c.total_size);
return rv;
}
/* Save the pointer */
*buf_ptr = buf;
return VB2_SUCCESS;
}
int vb2_load_fw_keyblock(struct vb2_context *ctx)
{
struct vb2_shared_data *sd = vb2_get_sd(ctx);
struct vb2_workbuf wb;
uint8_t *key_data;
uint32_t key_size;
struct vb2_packed_key *packed_key;
struct vb2_public_key root_key;
struct vb2_keyblock *kb;
int rv;
vb2_workbuf_from_ctx(ctx, &wb);
/* Read the root key */
key_size = sd->gbb_rootkey_size;
key_data = vb2_workbuf_alloc(&wb, key_size);
if (!key_data)
return VB2_ERROR_FW_KEYBLOCK_WORKBUF_ROOT_KEY;
rv = vb2ex_read_resource(ctx, VB2_RES_GBB, sd->gbb_rootkey_offset,
key_data, key_size);
if (rv)
return rv;
/* Unpack the root key */
rv = vb2_unpack_key(&root_key, key_data, key_size);
if (rv)
return rv;
/*
* Load the firmware keyblock common header into the work buffer after
* the root key.
*/
rv = vb2_read_resource_object(ctx, VB2_RES_FW_VBLOCK, 0, &wb,
(void **)&kb);
if (rv)
return rv;
/* Verify the keyblock */
rv = vb2_verify_keyblock(kb, kb->c.total_size, &root_key, &wb);
if (rv) {
vb2_fail(ctx, VB2_RECOVERY_FW_KEYBLOCK, rv);
return rv;
}
/* Preamble follows the keyblock in the vblock */
sd->vblock_preamble_offset = kb->c.total_size;
packed_key = (struct vb2_packed_key *)((uint8_t *)kb + kb->key_offset);
/* Key version is the upper 16 bits of the composite firmware version */
if (packed_key->key_version > 0xffff)
rv = VB2_ERROR_FW_KEYBLOCK_VERSION_RANGE;
if (!rv && packed_key->key_version < (sd->fw_version_secdata >> 16))
rv = VB2_ERROR_FW_KEYBLOCK_VERSION_ROLLBACK;
if (rv) {
vb2_fail(ctx, VB2_RECOVERY_FW_KEY_ROLLBACK, rv);
return rv;
}
sd->fw_version = packed_key->key_version << 16;
/*
* Save the data key in the work buffer. This overwrites the root key
* we read above. That's ok, because now that we have the data key we
* no longer need the root key.
*
* Use memmove() instead of memcpy(). In theory, the destination will
* never overlap with the source because the root key is likely to be
* at least as large as the data key, but there's no harm here in being
* paranoid.
*/
memmove(key_data, packed_key, packed_key->c.total_size);
packed_key = (struct vb2_packed_key *)key_data;
/* Save the packed key offset and size */
sd->workbuf_data_key_offset = vb2_offset_of(ctx->workbuf, key_data);
sd->workbuf_data_key_size = packed_key->c.total_size;
/* Data key will persist in the workbuf after we return */
ctx->workbuf_used = sd->workbuf_data_key_offset +
sd->workbuf_data_key_size;
return VB2_SUCCESS;
}
int vb2_load_fw_preamble(struct vb2_context *ctx)
{
struct vb2_shared_data *sd = vb2_get_sd(ctx);
struct vb2_workbuf wb;
uint8_t *key_data = ctx->workbuf + sd->workbuf_data_key_offset;
uint32_t key_size = sd->workbuf_data_key_size;
struct vb2_public_key data_key;
/* Preamble goes in the next unused chunk of work buffer */
struct vb2_fw_preamble *pre;
int rv;
vb2_workbuf_from_ctx(ctx, &wb);
/* Unpack the firmware data key */
if (!sd->workbuf_data_key_size)
return VB2_ERROR_FW_PREAMBLE2_DATA_KEY;
rv = vb2_unpack_key(&data_key, key_data, key_size);
if (rv)
return rv;
/* Load the firmware preamble */
rv = vb2_read_resource_object(ctx, VB2_RES_FW_VBLOCK,
sd->vblock_preamble_offset, &wb,
(void **)&pre);
if (rv)
return rv;
/* Work buffer now contains the data subkey data and the preamble */
/* Verify the preamble */
rv = vb2_verify_fw_preamble(pre, pre->c.total_size, &data_key, &wb);
if (rv) {
vb2_fail(ctx, VB2_RECOVERY_FW_PREAMBLE, rv);
return rv;
}
/* Move the preamble down now that the data key is no longer used */
memmove(key_data, pre, pre->c.total_size);
pre = (struct vb2_fw_preamble *)key_data;
/* Data key is now gone */
sd->workbuf_data_key_offset = sd->workbuf_data_key_size = 0;
/*
* Firmware version is the lower 16 bits of the composite firmware
* version.
*/
if (pre->fw_version > 0xffff)
rv = VB2_ERROR_FW_PREAMBLE_VERSION_RANGE;
/* Combine with the key version from vb2_load_fw_keyblock() */
sd->fw_version |= pre->fw_version;
if (!rv && sd->fw_version < sd->fw_version_secdata)
rv = VB2_ERROR_FW_PREAMBLE_VERSION_ROLLBACK;
if (rv) {
vb2_fail(ctx, VB2_RECOVERY_FW_ROLLBACK, rv);
return rv;
}
/*
* If this is a newer version than in secure storage, and we
* successfully booted the same slot last boot, roll forward the
* version in secure storage.
*/
if (sd->fw_version > sd->fw_version_secdata &&
sd->last_fw_slot == sd->fw_slot &&
sd->last_fw_result == VB2_FW_RESULT_SUCCESS) {
sd->fw_version_secdata = sd->fw_version;
rv = vb2_secdata_set(ctx, VB2_SECDATA_VERSIONS, sd->fw_version);
if (rv)
return rv;
}
/* Keep track of where we put the preamble */
sd->workbuf_preamble_offset = vb2_offset_of(ctx->workbuf, pre);
sd->workbuf_preamble_size = pre->c.total_size;
/* Preamble will persist in work buffer after we return */
ctx->workbuf_used = sd->workbuf_preamble_offset +
sd->workbuf_preamble_size;
return VB2_SUCCESS;
}