mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2026-01-07 16:11:43 +00:00
Depthcharge currently asks EC whether recovery was requested manually or not without verifying EC is in RO or not. If EC-RW is compromised, recovery switch state can be spoofed. This patch makes Depthcharge check EC_IN_RW to determine whether EC is in RO or not. Only if it's in RO and it says recovery button was pressed at boot, we proceed to the recovery process. All other recovery requests including manual recovery requested by a (compromised) host will end up with 'broken' screen. BUG=b:66516882 BRANCH=none TEST=Boot Fizz. make runtests. Change-Id: I01d2df05fe22e79bbc949f5cb83db605147667b3 Signed-off-by: Daisuke Nojiri <dnojiri@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/693008 Reviewed-by: Randall Spangler <rspangler@chromium.org>
227 lines
5.9 KiB
C
227 lines
5.9 KiB
C
/* Copyright (c) 2013 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.
|
|
*
|
|
* Common functions between firmware and kernel verified boot.
|
|
* (Firmware portion)
|
|
*/
|
|
|
|
#include "sysincludes.h"
|
|
#include "2sysincludes.h"
|
|
|
|
#include "2common.h"
|
|
#include "2rsa.h"
|
|
#include "2sha.h"
|
|
#include "vboot_api.h"
|
|
#include "vboot_common.h"
|
|
#include "utility.h"
|
|
|
|
const char *kVbootErrors[VBOOT_ERROR_MAX] = {
|
|
"Success.",
|
|
"Key block invalid.",
|
|
"Key block signature failed.",
|
|
"Key block hash failed.",
|
|
"Public key invalid.",
|
|
"Preamble invalid.",
|
|
"Preamble signature check failed.",
|
|
"Shared data invalid."
|
|
};
|
|
|
|
uint64_t OffsetOf(const void *base, const void *ptr)
|
|
{
|
|
return (uint64_t)(size_t)ptr - (uint64_t)(size_t)base;
|
|
}
|
|
|
|
/* Helper functions to get data pointed to by a public key or signature. */
|
|
|
|
uint8_t *GetPublicKeyData(VbPublicKey *key)
|
|
{
|
|
return (uint8_t *)key + key->key_offset;
|
|
}
|
|
|
|
const uint8_t *GetPublicKeyDataC(const VbPublicKey *key)
|
|
{
|
|
return (const uint8_t *)key + key->key_offset;
|
|
}
|
|
|
|
uint8_t *GetSignatureData(VbSignature *sig)
|
|
{
|
|
return (uint8_t *)sig + sig->sig_offset;
|
|
}
|
|
|
|
const uint8_t *GetSignatureDataC(const VbSignature *sig)
|
|
{
|
|
return (const uint8_t *)sig + sig->sig_offset;
|
|
}
|
|
|
|
/*
|
|
* Helper functions to verify the data pointed to by a subfield is inside
|
|
* the parent data. Returns 0 if inside, 1 if error.
|
|
*/
|
|
|
|
int VerifyMemberInside(const void *parent, uint64_t parent_size,
|
|
const void *member, uint64_t member_size,
|
|
uint64_t member_data_offset,
|
|
uint64_t member_data_size)
|
|
{
|
|
uint64_t end = OffsetOf(parent, member);
|
|
|
|
if (end > parent_size)
|
|
return 1;
|
|
|
|
if (UINT64_MAX - end < member_size)
|
|
return 1; /* Detect wraparound in integer math */
|
|
if (end + member_size > parent_size)
|
|
return 1;
|
|
|
|
if (UINT64_MAX - end < member_data_offset)
|
|
return 1;
|
|
end += member_data_offset;
|
|
if (end > parent_size)
|
|
return 1;
|
|
|
|
if (UINT64_MAX - end < member_data_size)
|
|
return 1;
|
|
if (end + member_data_size > parent_size)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int VerifyPublicKeyInside(const void *parent, uint64_t parent_size,
|
|
const VbPublicKey *key)
|
|
{
|
|
return VerifyMemberInside(parent, parent_size,
|
|
key, sizeof(VbPublicKey),
|
|
key->key_offset, key->key_size);
|
|
}
|
|
|
|
int VerifySignatureInside(const void *parent, uint64_t parent_size,
|
|
const VbSignature *sig)
|
|
{
|
|
return VerifyMemberInside(parent, parent_size,
|
|
sig, sizeof(VbSignature),
|
|
sig->sig_offset, sig->sig_size);
|
|
}
|
|
|
|
void PublicKeyInit(VbPublicKey *key, uint8_t *key_data, uint64_t key_size)
|
|
{
|
|
key->key_offset = OffsetOf(key, key_data);
|
|
key->key_size = key_size;
|
|
key->algorithm = VB2_ALG_COUNT; /* Key not present yet */
|
|
key->key_version = 0;
|
|
}
|
|
|
|
int PublicKeyCopy(VbPublicKey *dest, const VbPublicKey *src)
|
|
{
|
|
if (dest->key_size < src->key_size)
|
|
return 1;
|
|
|
|
dest->key_size = src->key_size;
|
|
dest->algorithm = src->algorithm;
|
|
dest->key_version = src->key_version;
|
|
memcpy(GetPublicKeyData(dest), GetPublicKeyDataC(src), src->key_size);
|
|
return 0;
|
|
}
|
|
|
|
int VbGetKernelVmlinuzHeader(const VbKernelPreambleHeader *preamble,
|
|
uint64_t *vmlinuz_header_address,
|
|
uint64_t *vmlinuz_header_size)
|
|
{
|
|
*vmlinuz_header_address = 0;
|
|
*vmlinuz_header_size = 0;
|
|
if (preamble->header_version_minor > 0) {
|
|
/*
|
|
* Set header and size only if the preamble header version is >
|
|
* 2.1 as they don't exist in version 2.0 (Note that we don't
|
|
* need to check header_version_major; if that's not 2 then
|
|
* VerifyKernelPreamble() would have already failed.
|
|
*/
|
|
*vmlinuz_header_address = preamble->vmlinuz_header_address;
|
|
*vmlinuz_header_size = preamble->vmlinuz_header_size;
|
|
}
|
|
return VBOOT_SUCCESS;
|
|
}
|
|
|
|
int VbKernelHasFlags(const VbKernelPreambleHeader *preamble)
|
|
{
|
|
if (preamble->header_version_minor > 1)
|
|
return VBOOT_SUCCESS;
|
|
|
|
return VBOOT_KERNEL_PREAMBLE_NO_FLAGS;
|
|
}
|
|
|
|
int VerifyVmlinuzInsideKBlob(uint64_t kblob, uint64_t kblob_size,
|
|
uint64_t header, uint64_t header_size)
|
|
{
|
|
uint64_t end = header-kblob;
|
|
if (end > kblob_size)
|
|
return VBOOT_PREAMBLE_INVALID;
|
|
if (UINT64_MAX - end < header_size)
|
|
return VBOOT_PREAMBLE_INVALID;
|
|
if (end + header_size > kblob_size)
|
|
return VBOOT_PREAMBLE_INVALID;
|
|
|
|
return VBOOT_SUCCESS;
|
|
}
|
|
|
|
uint64_t VbSharedDataReserve(VbSharedDataHeader *header, uint64_t size)
|
|
{
|
|
if (!header || size > header->data_size - header->data_used) {
|
|
VB2_DEBUG("VbSharedData buffer out of space.\n");
|
|
return 0; /* Not initialized, or not enough space left. */
|
|
}
|
|
|
|
uint64_t offs = header->data_used;
|
|
VB2_DEBUG("VbSharedDataReserve %d bytes at %d\n", (int)size, (int)offs);
|
|
|
|
header->data_used += size;
|
|
return offs;
|
|
}
|
|
|
|
int VbSharedDataSetKernelKey(VbSharedDataHeader *header, const VbPublicKey *src)
|
|
{
|
|
VbPublicKey *kdest;
|
|
|
|
if (!header)
|
|
return VBOOT_SHARED_DATA_INVALID;
|
|
if (!src)
|
|
return VBOOT_PUBLIC_KEY_INVALID;
|
|
|
|
kdest = &header->kernel_subkey;
|
|
|
|
VB2_DEBUG("Saving kernel subkey to shared data: size %d, algo %d\n",
|
|
vb2_rsa_sig_size(vb2_crypto_to_signature(src->algorithm)),
|
|
(int)src->algorithm);
|
|
|
|
/* Attempt to allocate space for key, if it hasn't been allocated yet */
|
|
if (!header->kernel_subkey_data_offset) {
|
|
header->kernel_subkey_data_offset =
|
|
VbSharedDataReserve(header, src->key_size);
|
|
if (!header->kernel_subkey_data_offset)
|
|
return VBOOT_SHARED_DATA_INVALID;
|
|
header->kernel_subkey_data_size = src->key_size;
|
|
}
|
|
|
|
/* Copy the kernel sign key blob into the destination buffer */
|
|
PublicKeyInit(kdest,
|
|
(uint8_t *)header + header->kernel_subkey_data_offset,
|
|
header->kernel_subkey_data_size);
|
|
|
|
return PublicKeyCopy(kdest, src);
|
|
}
|
|
|
|
int vb2_allow_recovery(uint32_t flags)
|
|
{
|
|
/* In dev mode, unconditionally allowed. */
|
|
if (flags & VBSD_BOOT_DEV_SWITCH_ON)
|
|
return 1;
|
|
|
|
/* If EC is in RW, it implies recovery wasn't manually requested. */
|
|
if (!VbExTrustEC(0))
|
|
return 0;
|
|
|
|
/* Now we confidently check the recovery switch state at boot */
|
|
return !!(flags & VBSD_BOOT_REC_SWITCH_ON);
|
|
}
|