firmware/coreboot: Subtree merged vboot

Signed-off-by: David Hendricks <dhendricks@fb.com>
This commit is contained in:
David Hendricks
2018-06-14 15:19:54 -07:00
1243 changed files with 109589 additions and 1 deletions

Submodule firmware/coreboot/3rdparty/vboot deleted from 392211f035

View File

@@ -0,0 +1,10 @@
/build
/build-au
/build-main
ID
scripts/newbitmaps/default_source/*.bmp
scripts/newbitmaps/images/out_*
scripts/newbitmaps/lib/*.pyc
scripts/newbitmaps/strings/font
scripts/newbitmaps/strings/*.png
scripts/newbitmaps/strings/localized_text/*/*.png

View File

@@ -0,0 +1,175 @@
# Copyright 2015 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.
# Minimal makefile capable of compiling futility to sign images
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libvboot_util-host
ifeq ($(HOST_OS),darwin)
LOCAL_CFLAGS += -DHAVE_MACOS
endif
# These are required to access large disks and files on 32-bit systems.
LOCAL_CFLAGS += -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64
LOCAL_C_INCLUDES += \
$(LOCAL_PATH)/firmware/include \
$(LOCAL_PATH)/firmware/lib/include \
$(LOCAL_PATH)/firmware/lib/cgptlib/include \
$(LOCAL_PATH)/firmware/lib/cryptolib/include \
$(LOCAL_PATH)/firmware/lib/tpm_lite/include \
$(LOCAL_PATH)/firmware/2lib/include \
$(LOCAL_PATH)/host/include \
$(LOCAL_PATH)/host/lib/include
# Firmware library sources needed by VbInit() call
VBINIT_SRCS = \
firmware/lib/crc8.c \
firmware/lib/utility.c \
firmware/lib/vboot_api_init.c \
firmware/lib/vboot_common_init.c \
firmware/lib/region-init.c \
# Additional firmware library sources needed by VbSelectFirmware() call
VBSF_SRCS = \
firmware/lib/cryptolib/padding.c \
firmware/lib/cryptolib/rsa.c \
firmware/lib/cryptolib/rsa_utility.c \
firmware/lib/cryptolib/sha1.c \
firmware/lib/cryptolib/sha256.c \
firmware/lib/cryptolib/sha512.c \
firmware/lib/cryptolib/sha_utility.c \
firmware/lib/stateful_util.c \
firmware/lib/vboot_common.c \
firmware/lib/region-fw.c \
# Additional firmware library sources needed by VbSelectAndLoadKernel() call
VBSLK_SRCS = \
firmware/lib/cgptlib/cgptlib.c \
firmware/lib/cgptlib/cgptlib_internal.c \
firmware/lib/cgptlib/crc32.c \
firmware/lib/gpt_misc.c \
firmware/lib/utility_string.c \
firmware/lib/vboot_api_kernel.c \
firmware/lib/vboot_audio.c \
firmware/lib/vboot_display.c \
firmware/lib/vboot_kernel.c \
firmware/lib/region-kernel.c \
VBINIT_SRCS += \
firmware/stub/tpm_lite_stub.c \
firmware/stub/utility_stub.c \
firmware/stub/vboot_api_stub_init.c \
firmware/stub/vboot_api_stub_region.c
VBSF_SRCS += \
firmware/stub/vboot_api_stub_sf.c
VBSLK_SRCS += \
firmware/stub/vboot_api_stub.c \
firmware/stub/vboot_api_stub_disk.c \
firmware/stub/vboot_api_stub_stream.c
UTILLIB_SRCS = \
cgpt/cgpt_create.c \
cgpt/cgpt_add.c \
cgpt/cgpt_boot.c \
cgpt/cgpt_show.c \
cgpt/cgpt_repair.c \
cgpt/cgpt_prioritize.c \
cgpt/cgpt_common.c \
futility/dump_kernel_config_lib.c \
host/lib/crossystem.c \
host/lib/file_keys.c \
host/lib/fmap.c \
host/lib/host_common.c \
host/lib/host_key.c \
host/lib/host_keyblock.c \
host/lib/host_misc.c \
host/lib/util_misc.c \
host/lib/host_signature.c \
host/lib/signature_digest.c
# host/arch/${HOST_ARCH}/lib/crossystem_arch.c \
LOCAL_SRC_FILES := \
$(VBINIT_SRCS) \
$(VBSF_SRCS) \
$(VBSLK_SRCS) \
$(UTILLIB_SRCS)
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_C_INCLUDES)
LOCAL_STATIC_LIBRARIES := libcrypto_static
include $(BUILD_HOST_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := futility-host
LOCAL_IS_HOST_MODULE := true
LOCAL_MODULE_CLASS := EXECUTABLES
generated_sources := $(call local-generated-sources-dir)
ifeq ($(HOST_OS),darwin)
LOCAL_CFLAGS += -DHAVE_MACOS
endif
# These are required to access large disks and files on 32-bit systems.
LOCAL_CFLAGS += -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64
FUTIL_STATIC_SRCS = \
futility/futility.c \
futility/cmd_dump_fmap.c \
futility/cmd_gbb_utility.c \
futility/misc.c
FUTIL_SRCS = \
${FUTIL_STATIC_SRCS} \
futility/cmd_dump_kernel_config.c \
futility/cmd_load_fmap.c \
futility/cmd_pcr.c \
futility/cmd_show.c \
futility/cmd_sign.c \
futility/cmd_vbutil_firmware.c \
futility/cmd_vbutil_kernel.c \
futility/cmd_vbutil_key.c \
futility/cmd_vbutil_keyblock.c \
futility/file_type.c \
futility/traversal.c \
futility/vb1_helper.c
# ${FUTIL_STATIC_WORKAROUND_SRCS:%.c=${BUILD}/%.o} \
LOCAL_SRC_FILES := \
$(FUTIL_SRCS) \
$(generated_sources)/futility_cmds.c: ${FUTIL_SRCS:%=${LOCAL_PATH}/%}
@echo making $< from ${FUTIL_SRCS}
@rm -f $@ $@_t $@_commands
@mkdir -p $(dir $@)
@grep -hoRE '^DECLARE_FUTIL_COMMAND\([^,]+' $^ \
| sed 's/DECLARE_FUTIL_COMMAND(\(.*\)/_CMD(\1)/' \
| sort >>$@_commands
@external/vboot_reference/scripts/getversion.sh >> $@_t
@echo '#define _CMD(NAME) extern const struct' \
'futil_cmd_t __cmd_##NAME;' >> $@_t
@cat $@_commands >> $@_t
@echo '#undef _CMD' >> $@_t
@echo '#define _CMD(NAME) &__cmd_##NAME,' >> $@_t
@echo 'const struct futil_cmd_t *const futil_cmds[] = {' >> $@_t
@cat $@_commands >> $@_t
@echo '0}; /* null-terminated */' >> $@_t
@echo '#undef _CMD' >> $@_t
@mv $@_t $@
@rm -f $@_commands
LOCAL_GENERATED_SOURCES := $(generated_sources)/futility_cmds.c
LOCAL_STATIC_LIBRARIES := libvboot_util-host
LOCAL_SHARED_LIBRARIES := libcrypto-host
include $(BUILD_HOST_EXECUTABLE)

View File

@@ -0,0 +1,27 @@
// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

1551
firmware/coreboot/3rdparty/vboot/Makefile vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,7 @@
[Hook Overrides]
branch_check: true
# We are using Linux style indentation with tabs
# The indentation is checked by checkpatch not the python script
tab_check: false

174
firmware/coreboot/3rdparty/vboot/README vendored Normal file
View File

@@ -0,0 +1,174 @@
This directory contains a reference implementation for Chrome OS
verified boot in firmware.
----------
Directory Structure
----------
The source is organized into distinct modules -
firmware/
Contains ONLY the code required by the BIOS to validate the secure boot
components. There shouldn't be any code in here that signs or generates
images. BIOS should require ONLY this directory to implement secure boot.
Refer to firmware/README for futher details.
cgpt/
Utility to read/write/modify GPT partitions. Similar to GNU parted or any
other GPT tool, but this has support for Chrome OS extensions.
host/
Miscellaneous functions needed by userland utilities.
futility/
The "firmware utility" tool, used to create, sign, and validate Chrome OS
images.
utility/
Random other utilities, not necesssarily related to verified boot as such.
tests/
User-land tests and benchmarks that test the reference implementation.
Please have a look at these if you'd like to understand how to use the
reference implementation.
build/
The output directory where the generated files will be placed, and where
tests are run.
scripts/
Tools and scripts used to generate and use new signing keypairs. These are
typically used only on a secure machine.
--------------------
Building and testing
--------------------
The suite can be built on the host or in the chroot environment.
Building on the host could fail if certain packages are not installed. If
there are host environment build problems due to missing .h files, try
researching what packages the files belong to and install the missing packages
before reporting a problem.
The commands are the more-or-less expected ones:
make
make runtests
make install [ DESTDIR=/usr/local ]
----------
Some useful utilities:
----------
futility vbutil_key Convert a public key into .vbpubk format
futility vbutil_keyblock Wrap a public key inside a signature and checksum
futility vbutil_firmware Create a .vblock with signature info for a
firmware image
futility vbutil_kernel Pack a kernel image, bootloader, and config into
a signed binary
dumpRSAPublicKey Dump RSA Public key (from a DER-encoded X509
certificate) in a format suitable for use by
RSAVerify* functions in crypto/.
verify_data.c Verify a given signature on a given file.
----------
Generating a signed firmware image:
----------
* Step 0: Build the tools, install them somewhere.
* Step 1: Generate RSA root and signing keys.
The root key is always 8192 bits.
$ openssl genrsa -F4 -out root_key.pem 8192
The signing key can be between 1024-8192 bits.
$ openssl genrsa -F4 -out signing_key.pem <1024|2048|4096|8192>
Note: The -F4 option must be specified to generate RSA keys with a public
exponent of 65535. RSA keys with 3 as a public exponent (the default)
won't work.
* Step 2: Generate pre-processed public versions of the above keys using
dumpRSAPublicKey. This utility expects an x509 certificate as
input, and emits an intermediate representation for further
processing.
$ openssl req -batch -new -x509 -key root_key.pem -out root_key.crt
$ openssl req -batch -new -x509 -key signing_key.pem -out signing_key.crt
$ dumpRSAPublicKey root_key.crt > root_key.keyb
$ dumpRSAPublicKey signing_key.crt > signing_key.keyb
************** TODO: STUFF PAST HERE IS OUT OF DATE ***************
At this point we have all the requisite keys needed to generate a signed
firmware image.
.pem RSA Public/Private Key Pair
.crt X509 Key Certificate
.keyb Pre-processed RSA Public Key
* Step 3: Use utility/firmware_utility to generate a signed firmare blob.
$ utility/firmware_utility --generate \
--root_key root_key.pem \
--firmware_sign_key signing_key.pem \
--firmware_sign_key_pub signing_key.keyb \
--firmware_sign_algorithm <algoid> \
--firmware_key_version 1 \
--firmware_version 1 \
--in <firmware blob file> \
--out <output file>
Where <algoid> is based on the signature algorithm to use for firmware
signining. The list of <algoid> specifications can be output by running
'utility/firmware_utility' without any arguments.
Note: --firmware_key_version and --firmware_version are part of a signed
image and are used to prevent rollbacks to older version. For testing,
they can just be set to valid values.
* Step 4: Verify that this image verifies.
$ utility/firmware_utility --verify \
--in <signed firmware image>
--root_key_pub root_key.keyb
Verification SUCCESS.
Note: The verification functions expects a pointer to the
pre-processed public root key as input. For testing purposes,
root_key.keyb can be stored in RW part of the firmware. For the
final firmware, this will be a fixed public key which cannot be
changed and must be stored in RO firmware.
----------
Generating a signed kernel image:
----------
The steps for generating a signed kernel image are similar to that of
a firmware image. Since verification is chained - RO firmware verifies
RW firmware which verifies the kernel, only the keys change. An additional
kernel signing key must be generated. The firmware signing generated above
is the root key equivalent for signed kernel images.

View File

@@ -0,0 +1,14 @@
# See http://dev.chromium.org/developers/contributing-code/watchlists for
# a description of this file's format.
# Please keep these keys in alphabetical order.
{
'WATCHLIST_DEFINITIONS': {
'all': {
'filepath': '.',
},
},
'WATCHLISTS': {
'all': ['gauravsh@chromium.org', 'rspangler@chromium.org', 'semenzato@chromium.org', 'wfrichar@chromium.org']
},
}

View File

@@ -0,0 +1,27 @@
// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,130 @@
# Copyright 2015 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.
# This Makefile normally builds in a 'build' subdir, but use
#
# make BUILD=<dir>
#
# to put the output somewhere else.
##############################################################################
# Configuration variables come first.
# Verbose? Use V=1
ifeq (${V},)
Q := @
endif
# Quiet? Use QUIET=1
ifeq (${QUIET},)
PRINTF := printf
else
PRINTF := :
endif
CC ?= gcc
LD = ${CC}
PKG_CONFIG ?= pkg-config
SRCDIR := $(shell pwd)
export SRCDIR
BUILD = ${SRCDIR}/build
export BUILD
KEYDIR = ${SRCDIR}/testkeys
CFLAGS = -Wall -Werror
# Create / use dependency files
CFLAGS += -MMD -MF $@.d
##############################################################################
# Create output directories if necessary. Do this via explicit shell commands
# so it happens before trying to generate/include dependencies.
_dir_create := $(shell [ -d ${BUILD} ] || mkdir -p ${BUILD}))
_keydir_create := $(shell [ -d ${KEYDIR} ] || mkdir -p ${KEYDIR}))
INC_PATH := $(shell ${PKG_CONFIG} --cflags libcrypto)
CFLAGS += ${INC_PATH}
CRYPTO_LIBS := $(shell ${PKG_CONFIG} --libs libcrypto)
LDLIBS += ${CRYPTO_LIBS}
##############################################################################
# Sources
LIBSRC = bdb.c host.c sha.c rsa.c
LIBOBJ = ${LIBSRC:%.c=${BUILD}/%.o}
BDBTESTSRC = bdb_test.c
BDBTESTOBJ = ${BDBTESTSRC:%.c=${BUILD}/%.o}
BDBTEST = ${BUILD}/bdb_test
BDBCREATESRC = bdb_create.c
BDBCREATEOBJ = ${BDBCREATESRC:%.c=${BUILD}/%.o}
BDBCREATE = ${BUILD}/bdb_create
DUMPRSASRC = dump_rsa.c
DUMPRSAOBJ = ${DUMPRSASRC:%.c=${BUILD}/%.o}
DUMPRSA = ${BUILD}/dump_rsa
ALL_OBJS = ${LIBOBJ} ${BDBTESTOBJ} ${BDBCREATEOBJ}
ALL_EXES = ${BDBTEST} ${BDBCREATE} ${DUMPRSA}
##############################################################################
# Targets
.PHONY: all
all: ${ALL_EXES}
.PHONY: clean
clean:
${Q}/bin/rm -rf ${BUILD}
.PHONY: bdb
bdb: ${BDBCREATE}
${Q}${BDBCREATE}
.PHONY: runtests
runtests: ${BDBTEST}
${Q}${BDBTEST}
.PHONY: testkeys
testkeys: ${DUMPRSA}
${Q}openssl genrsa -F4 -out ${KEYDIR}/bdbkey.pem 4096
${Q}openssl req -batch -new -x509 -key ${KEYDIR}/bdbkey.pem \
-out ${KEYDIR}/bdbkey.crt
${Q}${DUMPRSA} -cert ${KEYDIR}/bdbkey.crt > ${KEYDIR}/bdbkey.keyb
${Q}openssl genrsa -3 -out ${KEYDIR}/subkey.pem 3072
${Q}openssl req -batch -new -x509 -key ${KEYDIR}/subkey.pem \
-out ${KEYDIR}/subkey.crt
${Q}${DUMPRSA} -cert ${KEYDIR}/subkey.crt > ${KEYDIR}/subkey.keyb
${BDBTEST}: ${BDBTESTOBJ} ${LIBOBJ}
@$(PRINTF) " LD $(subst ${BUILD}/,,$@)\n"
${Q}${LD} -o ${BDBTEST} ${CFLAGS} $^ ${LIBS} ${LDLIBS}
${BDBCREATE}: ${BDBCREATEOBJ} ${LIBOBJ}
@$(PRINTF) " LD $(subst ${BUILD}/,,$@)\n"
${Q}${LD} -o ${BDBCREATE} ${CFLAGS} $^ ${LIBS} ${LDLIBS}
${DUMPRSA}: ${DUMPRSAOBJ} ${LIBOBJ}
@$(PRINTF) " LD $(subst ${BUILD}/,,$@)\n"
${Q}${LD} -o ${DUMPRSA} ${CFLAGS} $^ ${LIBS} ${LDLIBS}
##############################################################################
# Generic build rules. LIBS and OBJS can be overridden to tweak the generic
# rules for specific targets.
${BUILD}/%: ${BUILD}/%.o ${OBJS} ${LIBS}
@${PRINTF} " LD $(subst ${BUILD}/,,$@)\n"
${Q}${LD} -o $@ ${CFLAGS} ${LDFLAGS} $< ${OBJS} ${LIBS} ${LDLIBS}
${BUILD}/%.o: %.c
@${PRINTF} " CC $(subst ${BUILD}/,,$@)\n"
${Q}${CC} ${CFLAGS} ${INCLUDES} -c -o $@ $<
# Include generated dependencies
ALL_DEPS += ${ALL_OBJS:%.o=%.o.d}
-include ${ALL_DEPS}

View File

@@ -0,0 +1,30 @@
Boot Descriptor Block (BDB) library and utilities
Building:
---------
The host-side library and utilities requires OpenSSL.
Do 'make runtests' to ensure everything is working.
Generating a BDB:
-----------------
Edit the options in bdb_create.c. Then 'make bdb'.
In the next release, this will take a config file rather than
requiring recompilation each time. Also, the BDB header and data will
be signed in two separate steps, so that the private BDB key is not
required each time.
Revision History:
-----------------
v0.1.2 24-Nov-2015 Add support for RSA-3072B keys and signatures.
Add dump_rsa utility and 'make testkeys' to create
new keys.
Use a RSA-3072B (exponent 3) key for the subkey so
the exponent 3 code gets tested.
v0.1.1 17-Nov-2015 Add support for ECDSA-521 data types. Note that
only the data types are supported; there is not a
C implementation for ECDSA.
v0.1.0 15-Sep-2015 Initial version.

View File

@@ -0,0 +1,398 @@
/* Copyright (c) 2015 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.
*
* Boot descriptor block firmware functions
*/
#include <string.h>
#include "bdb.h"
/*****************************************************************************/
/**
* Check if string contains a null terminator.
*
* Bytes after the null terminator do not need to be null.
*
* @param s String to check
* @param size Size of string buffer in characters
* @return 1 if string has a null terminator, 0 if not
*/
int string_has_null(const char *s, size_t size)
{
for (; size; size--) {
if (*s++ == 0)
return 1;
}
return 0;
}
int bdb_check_header(const struct bdb_header *p, size_t size)
{
if (size < sizeof(*p) || size < p->struct_size)
return BDB_ERROR_BUF_SIZE;
if (p->struct_magic != BDB_HEADER_MAGIC)
return BDB_ERROR_STRUCT_MAGIC;
if (p->struct_major_version != BDB_HEADER_VERSION_MAJOR)
return BDB_ERROR_STRUCT_VERSION;
/* Note that minor version doesn't matter yet */
if (p->struct_size < sizeof(*p))
return BDB_ERROR_STRUCT_SIZE;
if (p->oem_area_0_size & 3)
return BDB_ERROR_OEM_AREA_SIZE; /* Not 32-bit aligned */
/*
* Make sure the BDB is at least big enough for us. At this point, all
* the caller may have loaded is this header We'll check if there's
* space for everything else after we load it.
*/
if (p->bdb_size < sizeof(*p))
return BDB_ERROR_BDB_SIZE;
/*
* The rest of the fields don't matter yet; we'll check them when we
* check the BDB itself.
*/
return BDB_SUCCESS;
}
int bdb_check_key(const struct bdb_key *p, size_t size)
{
size_t expect_key_size = 0;
if (size < sizeof(*p) || size < p->struct_size)
return BDB_ERROR_BUF_SIZE;
if (p->struct_magic != BDB_KEY_MAGIC)
return BDB_ERROR_STRUCT_MAGIC;
if (p->struct_major_version != BDB_KEY_VERSION_MAJOR)
return BDB_ERROR_STRUCT_VERSION;
/* Note that minor version doesn't matter yet */
if (!string_has_null(p->description, sizeof(p->description)))
return BDB_ERROR_DESCRIPTION;
/* We currently only support SHA-256 */
if (p->hash_alg != BDB_HASH_ALG_SHA256)
return BDB_ERROR_HASH_ALG;
/* Make sure signature algorithm and size are correct */
switch (p->sig_alg) {
case BDB_SIG_ALG_RSA4096:
expect_key_size = BDB_RSA4096_KEY_DATA_SIZE;
break;
case BDB_SIG_ALG_ECSDSA521:
expect_key_size = BDB_ECDSA521_KEY_DATA_SIZE;
break;
case BDB_SIG_ALG_RSA3072B:
expect_key_size = BDB_RSA3072B_KEY_DATA_SIZE;
break;
default:
return BDB_ERROR_SIG_ALG;
}
if (p->struct_size < sizeof(*p) + expect_key_size)
return BDB_ERROR_STRUCT_SIZE;
return BDB_SUCCESS;
}
int bdb_check_sig(const struct bdb_sig *p, size_t size)
{
size_t expect_sig_size = 0;
if (size < sizeof(*p) || size < p->struct_size)
return BDB_ERROR_BUF_SIZE;
if (p->struct_magic != BDB_SIG_MAGIC)
return BDB_ERROR_STRUCT_MAGIC;
if (p->struct_major_version != BDB_SIG_VERSION_MAJOR)
return BDB_ERROR_STRUCT_VERSION;
/* Note that minor version doesn't matter yet */
if (!string_has_null(p->description, sizeof(p->description)))
return BDB_ERROR_DESCRIPTION;
/* We currently only support SHA-256 */
if (p->hash_alg != BDB_HASH_ALG_SHA256)
return BDB_ERROR_HASH_ALG;
/* Make sure signature algorithm and size are correct */
switch (p->sig_alg) {
case BDB_SIG_ALG_RSA4096:
expect_sig_size = BDB_RSA4096_SIG_SIZE;
break;
case BDB_SIG_ALG_ECSDSA521:
expect_sig_size = BDB_ECDSA521_SIG_SIZE;
break;
case BDB_SIG_ALG_RSA3072B:
expect_sig_size = BDB_RSA3072B_SIG_SIZE;
break;
default:
return BDB_ERROR_SIG_ALG;
}
if (p->struct_size < sizeof(*p) + expect_sig_size)
return BDB_ERROR_STRUCT_SIZE;
return BDB_SUCCESS;
}
int bdb_check_data(const struct bdb_data *p, size_t size)
{
size_t need_size;
if (size < sizeof(*p) || size < p->signed_size)
return BDB_ERROR_BUF_SIZE;
if (p->struct_magic != BDB_DATA_MAGIC)
return BDB_ERROR_STRUCT_MAGIC;
if (p->struct_major_version != BDB_DATA_VERSION_MAJOR)
return BDB_ERROR_STRUCT_VERSION;
/* Note that minor version doesn't matter yet */
if (!string_has_null(p->description, sizeof(p->description)))
return BDB_ERROR_DESCRIPTION;
if (p->struct_size < sizeof(*p))
return BDB_ERROR_STRUCT_SIZE;
if (p->hash_entry_size < sizeof(struct bdb_hash))
return BDB_ERROR_HASH_ENTRY_SIZE;
/* Calculate expected size */
need_size = p->struct_size + p->num_hashes * p->hash_entry_size;
/* Make sure OEM area size doesn't cause wraparound */
if (need_size + p->oem_area_1_size < need_size)
return BDB_ERROR_OEM_AREA_SIZE;
if (p->oem_area_1_size & 3)
return BDB_ERROR_OEM_AREA_SIZE; /* Not 32-bit aligned */
need_size += p->oem_area_1_size;
if (p->signed_size < need_size)
return BDB_ERROR_SIGNED_SIZE;
return BDB_SUCCESS;
}
/*****************************************************************************/
const struct bdb_header *bdb_get_header(const void *buf)
{
return buf;
}
const struct bdb_key *bdb_get_bdbkey(const void *buf)
{
const struct bdb_header *h = bdb_get_header(buf);
const uint8_t *b8 = buf;
/* BDB key follows header */
return (const struct bdb_key *)(b8 + h->struct_size);
}
const void *bdb_get_oem_area_0(const void *buf)
{
const struct bdb_key *k = bdb_get_bdbkey(buf);
const uint8_t *b8 = (const uint8_t *)k;
/* OEM area 0 follows BDB key */
return b8 + k->struct_size;
}
const struct bdb_key *bdb_get_subkey(const void *buf)
{
const struct bdb_header *h = bdb_get_header(buf);
const uint8_t *b8 = bdb_get_oem_area_0(buf);
/* Subkey follows OEM area 0 */
return (const struct bdb_key *)(b8 + h->oem_area_0_size);
}
const struct bdb_sig *bdb_get_header_sig(const void *buf)
{
const struct bdb_header *h = bdb_get_header(buf);
const uint8_t *b8 = bdb_get_oem_area_0(buf);
/* Header signature starts after signed data */
return (const struct bdb_sig *)(b8 + h->signed_size);
}
const struct bdb_data *bdb_get_data(const void *buf)
{
const struct bdb_sig *s = bdb_get_header_sig(buf);
const uint8_t *b8 = (const uint8_t *)s;
/* Data follows header signature */
return (const struct bdb_data *)(b8 + s->struct_size);
}
const void *bdb_get_oem_area_1(const void *buf)
{
const struct bdb_data *p = bdb_get_data(buf);
const uint8_t *b8 = (const uint8_t *)p;
/* OEM area 1 follows BDB data */
return b8 + p->struct_size;
}
const struct bdb_hash *bdb_get_hash(const void *buf, enum bdb_data_type type)
{
const struct bdb_data *data = bdb_get_data(buf);
const uint8_t *b8 = bdb_get_oem_area_1(buf);
int i;
/* Hashes follow OEM area 0 */
b8 += data->oem_area_1_size;
/* Search for a matching hash */
for (i = 0; i < data->num_hashes; i++, b8 += data->hash_entry_size) {
const struct bdb_hash *h = (const struct bdb_hash *)b8;
if (h->type == type)
return h;
}
return NULL;
}
const struct bdb_sig *bdb_get_data_sig(const void *buf)
{
const struct bdb_data *data = bdb_get_data(buf);
const uint8_t *b8 = (const uint8_t *)data;
/* Data signature starts after signed data */
return (const struct bdb_sig *)(b8 + data->signed_size);
}
/*****************************************************************************/
int bdb_verify_sig(const struct bdb_key *key,
const struct bdb_sig *sig,
const uint8_t *digest)
{
/* Key and signature algorithms must match */
if (key->sig_alg != sig->sig_alg)
return BDB_ERROR_SIG_ALG;
switch (key->sig_alg) {
case BDB_SIG_ALG_RSA4096:
if (bdb_rsa4096_verify(key->key_data, sig->sig_data, digest))
return BDB_ERROR_VERIFY_SIG;
break;
case BDB_SIG_ALG_ECSDSA521:
if (bdb_ecdsa521_verify(key->key_data, sig->sig_data, digest))
return BDB_ERROR_VERIFY_SIG;
break;
case BDB_SIG_ALG_RSA3072B:
if (bdb_rsa3072b_verify(key->key_data, sig->sig_data, digest))
return BDB_ERROR_VERIFY_SIG;
break;
default:
return BDB_ERROR_VERIFY_SIG;
}
return BDB_SUCCESS;
}
int bdb_verify(const void *buf, size_t size, const uint8_t *bdb_key_digest)
{
const uint8_t *end = (const uint8_t *)buf + size;
const struct bdb_header *h;
const struct bdb_key *bdbkey, *subkey;
const struct bdb_sig *sig;
const struct bdb_data *data;
const void *oem;
uint8_t digest[BDB_SHA256_DIGEST_SIZE];
int bdb_digest_mismatch;
/* Make sure buffer doesn't wrap around address space */
if (end < (const uint8_t *)buf)
return BDB_ERROR_BUF_SIZE;
/*
* Check header now that we've actually loaded it. We can't guarantee
* this is the same header which was checked before.
*/
h = bdb_get_header(buf);
if (bdb_check_header(h, size))
return BDB_ERROR_HEADER;
/* Sanity-check BDB key */
bdbkey = bdb_get_bdbkey(buf);
if (bdb_check_key(bdbkey, end - (const uint8_t *)bdbkey))
return BDB_ERROR_BDBKEY;
/* Calculate BDB key digest and compare with expected */
if (bdb_sha256(digest, bdbkey, bdbkey->struct_size))
return BDB_ERROR_DIGEST;
bdb_digest_mismatch = memcmp(digest, bdb_key_digest, sizeof(digest));
/* Make sure OEM area 0 fits */
oem = bdb_get_oem_area_0(buf);
if (h->oem_area_0_size > end - (const uint8_t *)oem)
return BDB_ERROR_OEM_AREA_0;
/* Sanity-check subkey */
subkey = bdb_get_subkey(buf);
if (bdb_check_key(subkey, end - (const uint8_t *)subkey))
return BDB_ERROR_SUBKEY;
/* Make sure enough data was signed, and the signed data fits */
if (h->oem_area_0_size + subkey->struct_size > h->signed_size ||
h->signed_size > end - (const uint8_t *)oem)
return BDB_ERROR_BDB_SIGNED_SIZE;
/* Sanity-check header signature */
sig = bdb_get_header_sig(buf);
if (bdb_check_sig(sig, end - (const uint8_t *)sig))
return BDB_ERROR_HEADER_SIG;
/* Make sure it signed the right amount of data */
if (sig->signed_size != h->signed_size)
return BDB_ERROR_HEADER_SIG;
/* Calculate header digest and compare with expected signature */
if (bdb_sha256(digest, oem, h->signed_size))
return BDB_ERROR_DIGEST;
if (bdb_verify_sig(bdbkey, sig, digest))
return BDB_ERROR_HEADER_SIG;
/*
* Sanity-check data struct. This also checks that OEM area 1 and the
* hashes fit in the remaining buffer.
*/
data = bdb_get_data(buf);
if (bdb_check_data(data, end - (const uint8_t *)data))
return BDB_ERROR_DATA;
/* Sanity-check data signature */
sig = bdb_get_data_sig(buf);
if (bdb_check_sig(sig, end - (const uint8_t *)sig))
return BDB_ERROR_DATA_SIG;
if (sig->signed_size != data->signed_size)
return BDB_ERROR_DATA_SIG;
/* Calculate data digest and compare with expected signature */
if (bdb_sha256(digest, data, data->signed_size))
return BDB_ERROR_DIGEST;
if (bdb_verify_sig(subkey, sig, digest))
return BDB_ERROR_DATA_SIG;
/* Return success or success-other-than-BDB-key-mismatch */
return bdb_digest_mismatch ? BDB_GOOD_OTHER_THAN_KEY : BDB_SUCCESS;
}

View File

@@ -0,0 +1,181 @@
/* Copyright (c) 2015 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.
*
* Boot descriptor block firmware functions
*/
#ifndef VBOOT_REFERENCE_BDB_H_
#define VBOOT_REFERENCE_BDB_H_
#include <stdlib.h>
#include "bdb_struct.h"
/*****************************************************************************/
/*
Expected calling sequence:
Load and check just the header
bdb_check_header(buf, size);
Load and verify the entire BDB
bdb_verify(buf, size, bdb_key_hash, dev_mode_flag);
Check RW subkey version. If normal boot from primary BDB, roll forward
Check data version. If normal boot from primary BDB, roll forward
*/
/*****************************************************************************/
/* Codes for functions returning numeric error codes */
enum bdb_return_code {
/* Success */
BDB_SUCCESS = 0,
/* BDB key did not match hash, but other than that the BDB was
* fully verified. */
BDB_GOOD_OTHER_THAN_KEY = 1,
/* Other errors */
BDB_ERROR_UNKNOWN = 100,
/* Buffer size too small or wraps around */
BDB_ERROR_BUF_SIZE,
/* Bad fields in structures */
BDB_ERROR_STRUCT_MAGIC,
BDB_ERROR_STRUCT_VERSION,
BDB_ERROR_STRUCT_SIZE,
BDB_ERROR_SIGNED_SIZE,
BDB_ERROR_BDB_SIZE,
BDB_ERROR_OEM_AREA_SIZE,
BDB_ERROR_HASH_ENTRY_SIZE,
BDB_ERROR_HASH_ALG,
BDB_ERROR_SIG_ALG,
BDB_ERROR_DESCRIPTION,
/* Bad components of BDB in bdb_verify() */
BDB_ERROR_HEADER,
BDB_ERROR_BDBKEY,
BDB_ERROR_OEM_AREA_0,
BDB_ERROR_SUBKEY,
BDB_ERROR_BDB_SIGNED_SIZE,
BDB_ERROR_HEADER_SIG,
BDB_ERROR_DATA,
BDB_ERROR_DATA_SIG,
/* Other errors in bdb_verify() */
BDB_ERROR_DIGEST, /* Error calculating digest */
BDB_ERROR_VERIFY_SIG, /* Error verifying signature */
};
/*****************************************************************************/
/* Functions */
/**
* Sanity-check BDB structures.
*
* This checks for known version numbers, magic numbers, algorithms, etc. and
* ensures the sizes are consistent with those parameters.
*
* @param p Pointer to structure to check
* @param size Size of structure buffer
* @return 0 if success, non-zero error code if error.
*/
int bdb_check_header(const struct bdb_header *p, size_t size);
int bdb_check_key(const struct bdb_key *p, size_t size);
int bdb_check_sig(const struct bdb_sig *p, size_t size);
int bdb_check_data(const struct bdb_data *p, size_t size);
/**
* Verify the entire BDB
*
* @param buf Data to hash
* @param size Size of data in bytes
* @param bdb_key_digest Pointer to expected digest for BDB key.
* Must be BDB_SHA256_DIGEST_SIZE bytes long.
*
* @return 0 if success, non-zero error code if error. Note that error code
* BDB_GOOD_OTHER_THAN_KEY may still indicate an acceptable BDB if the Boot
* Verified fuse has not been set, or in developer mode.
*/
int bdb_verify(const void *buf, size_t size, const uint8_t *bdb_key_digest);
/**
* Functions to extract things from a verified BDB buffer.
*
* Do not call these externally until after bdb_verify()! These methods
* assume data structures have already been verified.
*
* @param buf Pointer to BDB buffer
* @param type Data type, for bdb_get_hash()
* @return A pointer to the requested data, or NULL if error / not present.
*/
const struct bdb_header *bdb_get_header(const void *buf);
const struct bdb_key *bdb_get_bdbkey(const void *buf);
const void *bdb_get_oem_area_0(const void *buf);
const struct bdb_key *bdb_get_subkey(const void *buf);
const struct bdb_sig *bdb_get_header_sig(const void *buf);
const struct bdb_data *bdb_get_data(const void *buf);
const void *bdb_get_oem_area_1(const void *buf);
const struct bdb_hash *bdb_get_hash(const void *buf, enum bdb_data_type type);
const struct bdb_sig *bdb_get_data_sig(const void *buf);
/*****************************************************************************/
/* Functions probably provided by the caller */
/**
* Calculate a SHA-256 digest of a buffer.
*
* @param digest Pointer to the digest buffer. Must be
* BDB_SHA256_DIGEST_SIZE bytes long.
* @param buf Data to hash
* @param size Size of data in bytes
* @return 0 if success, non-zero error code if error.
*/
__attribute__((weak))
int bdb_sha256(void *digest, const void *buf, size_t size);
/**
* Verify a RSA-4096 signed digest
*
* @param key_data Key data to use (BDB_RSA4096_KEY_DATA_SIZE bytes)
* @param sig_data Signature to verify (BDB_RSA4096_SIG_SIZE bytes)
* @param digest Digest of signed data (BDB_SHA256_DIGEST bytes)
* @return 0 if success, non-zero error code if error.
*/
__attribute__((weak))
int bdb_rsa4096_verify(const uint8_t *key_data,
const uint8_t *sig,
const uint8_t *digest);
/**
* Verify a RSA-3072B signed digest
*
* @param key_data Key data to use (BDB_RSA3072B_KEY_DATA_SIZE bytes)
* @param sig_data Signature to verify (BDB_RSA3072B_SIG_SIZE bytes)
* @param digest Digest of signed data (BDB_SHA256_DIGEST bytes)
* @return 0 if success, non-zero error code if error.
*/
__attribute__((weak))
int bdb_rsa3072b_verify(const uint8_t *key_data,
const uint8_t *sig,
const uint8_t *digest);
/**
* Verify a ECDSA-521 signed digest
*
* @param key_data Key data to use (BDB_ECDSA521_KEY_DATA_SIZE bytes)
* @param sig_data Signature to verify (BDB_ECDSA521_SIG_SIZE bytes)
* @param digest Digest of signed data (BDB_SHA256_DIGEST bytes)
* @return 0 if success, non-zero error code if error.
*/
__attribute__((weak))
int bdb_ecdsa521_verify(const uint8_t *key_data,
const uint8_t *sig,
const uint8_t *digest);
/*****************************************************************************/
#endif /* VBOOT_REFERENCE_BDB_H_ */

View File

@@ -0,0 +1,232 @@
/* Copyright (c) 2015 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.
*
* Create a BDB
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "bdb.h"
#include "host.h"
/* Parameters for creating a BDB hash entry */
struct create_hash {
/* File containing data */
const char *filename;
/* Type of data; enum bdb_data_type */
uint8_t type;
/* Address in RAM to load data. -1 means use default. */
uint64_t load_address;
/* Partition number containing data, or -1 to use the same partition as
* the BDB. */
uint8_t partition;
/*
* Offset of data from start of partition.
*
* TODO: if -1, append after BDB. But need to know how big the BDB
* is, and need to round up offset to 32-bit boundary.
*/
uint64_t offset;
};
/* Parameters for a key */
struct create_key {
/* Description */
const char *description;
/* Key version (not meaningful for BDB key) */
uint32_t key_version;
/* Public key filename (.keyb) */
const char *public_filename;
/* Private key filename (.pem) */
const char *private_filename;
};
struct create_params_2 {
/* Destination filename */
const char *filename;
/* Partition to contain the BDB */
uint8_t partition;
/* OEM area files. NULL means there is no data for that area. */
const char *oem_area_0_filename;
const char *oem_area_1_filename;
/* BDB key and subkey */
struct create_key bdbkey;
struct create_key subkey;
};
/*****************************************************************************/
/* FILL THIS IN WITH YOUR SOURCE DATA */
/*
* Creation parameters. Hash and num_hashes will be filled in automatically
* by create().
*/
struct bdb_create_params p = {
.bdb_load_address = 0x11223344,
.header_sig_description = "The header sig",
.data_sig_description = "The data sig",
.data_description = "Test BDB data",
.data_version = 3,
};
/* Additional parameters */
struct create_params_2 p2 = {
.filename = "build/bdb.bin",
.partition = 1,
.oem_area_0_filename = "testdata/oem0.bin",
.oem_area_1_filename = "testdata/oem1.bin",
.bdbkey = {
.description = "Test BDB key",
.key_version = 3,
.public_filename = "testkeys/bdbkey.keyb",
.private_filename = "testkeys/bdbkey.pem",
},
.subkey = {
.description = "Test Subkey",
.key_version = 4,
.public_filename = "testkeys/subkey.keyb",
.private_filename = "testkeys/subkey.pem",
},
};
/* List of hash entries, terminated by one with a NULL filename */
struct create_hash hash_entries[] = {
{
.filename = "testdata/sp-rw.bin",
.type = BDB_DATA_SP_RW,
.load_address = -1,
.partition = -1,
.offset = 0x10000,
},
{
.filename = "testdata/ap-rw.bin",
.type = BDB_DATA_AP_RW,
.load_address = 0x200000,
.partition = -1,
.offset = 0x28000,
},
{
.filename = NULL
},
};
/*****************************************************************************/
int create(void)
{
struct bdb_hash *hash;
struct bdb_header *h;
int i;
/* Count the number of hash entries */
for (p.num_hashes = 0; hash_entries[p.num_hashes].filename;
p.num_hashes++)
;
printf("Found %d hash entries\n", p.num_hashes);
/* Calculate hashes */
p.hash = hash = calloc(sizeof(struct bdb_hash), p.num_hashes);
for (i = 0; i < p.num_hashes; i++, hash++) {
const struct create_hash *he = hash_entries + i;
/* Read file and calculate size and hash */
uint8_t *buf = read_file(he->filename, &hash->size);
if (!buf)
return 1;
if (bdb_sha256(hash->digest, buf, hash->size)) {
fprintf(stderr, "Unable to calculate hash\n");
return 1;
}
free(buf);
hash->type = he->type;
hash->load_address = he->load_address;
hash->partition = he->partition == -1 ? p2.partition :
he->partition;
hash->offset = he->offset;
}
/* Read OEM data */
if (p2.oem_area_0_filename) {
p.oem_area_0 = read_file(p2.oem_area_0_filename,
&p.oem_area_0_size);
if (!p.oem_area_0)
return 1;
if (p.oem_area_0_size & 3) {
fprintf(stderr,
"OEM area 0 size isn't 32-bit aligned\n");
return 1;
}
}
if (p2.oem_area_1_filename) {
p.oem_area_1 = read_file(p2.oem_area_1_filename,
&p.oem_area_1_size);
if (!p.oem_area_1)
return 1;
if (p.oem_area_1_size & 3) {
fprintf(stderr,
"OEM area 1 size isn't 32-bit aligned\n");
return 1;
}
}
/* Load keys */
p.bdbkey = bdb_create_key(p2.bdbkey.public_filename,
p2.bdbkey.key_version,
p2.bdbkey.description);
p.subkey = bdb_create_key(p2.subkey.public_filename,
p2.subkey.key_version,
p2.subkey.description);
p.private_bdbkey = read_pem(p2.bdbkey.private_filename);
p.private_subkey = read_pem(p2.subkey.private_filename);
if (!p.bdbkey || !p.subkey || !p.private_bdbkey || !p.private_subkey) {
fprintf(stderr, "Unable to load keys\n");
return 1;
}
/* Create the BDB */
h = bdb_create(&p);
if (!h) {
fprintf(stderr, "Unable to create BDB\n");
return 1;
}
/* Write it */
if (write_file(p2.filename, h, h->bdb_size))
return 1;
/* Free keys and buffers */
free(p.bdbkey);
free(p.subkey);
RSA_free(p.private_bdbkey);
RSA_free(p.private_subkey);
free(h);
free(p.hash);
return 0;
}
/*****************************************************************************/
int main(void)
{
return create();
}

View File

@@ -0,0 +1,268 @@
/* Copyright (c) 2015 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.
*
* Boot descriptor block structures
*/
#ifndef VBOOT_REFERENCE_BDB_STRUCT_H_
#define VBOOT_REFERENCE_BDB_STRUCT_H_
#include <stdint.h>
/* Size of SHA256 digest in bytes */
#define BDB_SHA256_DIGEST_SIZE 32
/* Size of RSA4096 key data in bytes */
#define BDB_RSA4096_KEY_DATA_SIZE 1032
/* Size of RSA4096 signature in bytes */
#define BDB_RSA4096_SIG_SIZE 512
/* Size of ECDSA521 key data in bytes = ceil(521/8) * 2 */
#define BDB_ECDSA521_KEY_DATA_SIZE 132
/* Size of ECDSA521 signature in bytes = ceil(521/8) * 2 */
#define BDB_ECDSA521_SIG_SIZE 132
/* Size of RSA3072B key data in bytes */
#define BDB_RSA3072B_KEY_DATA_SIZE 776
/* Size of RSA3072B signature in bytes */
#define BDB_RSA3072B_SIG_SIZE 384
/*****************************************************************************/
/* Header for BDB */
/* Magic number for bdb_header.struct_magic */
#define BDB_HEADER_MAGIC 0x30426442
/* Current version of bdb_header struct */
#define BDB_HEADER_VERSION_MAJOR 1
#define BDB_HEADER_VERSION_MINOR 0
/* Expected size of bdb_header struct in bytes */
#define BDB_HEADER_EXPECTED_SIZE 32
struct bdb_header {
/* Magic number to identify struct = BDB_HEADER_MAGIC. */
uint32_t struct_magic;
/* Structure version = BDB_HEADER_VERSION{MAJOR,MINOR} */
uint8_t struct_major_version;
uint8_t struct_minor_version;
/* Size of structure in bytes */
uint16_t struct_size;
/* Recommended address in SP SRAM to load BDB. Set to -1 to use
* default address. */
uint64_t bdb_load_address;
/* Size of the entire BDB in bytes */
uint32_t bdb_size;
/* Number of bytes following the BDB key which are signed by the BDB
* header signature. */
uint32_t signed_size;
/* Size of OEM area 0 in bytes, or 0 if not present */
uint32_t oem_area_0_size;
/* Reserved; set 0 */
uint8_t reserved0[8];
} __attribute__((packed));
/*****************************************************************************/
/* Public key structure for BDB */
/* Magic number for bdb_key.struct_magic */
#define BDB_KEY_MAGIC 0x73334256
/* Current version of bdb_key struct */
#define BDB_KEY_VERSION_MAJOR 1
#define BDB_KEY_VERSION_MINOR 0
/* Supported hash algorithms */
enum bdb_hash_alg {
BDB_HASH_ALG_INVALID = 0, /* Not used; invalid */
BDB_HASH_ALG_SHA256 = 2, /* SHA-256 */
};
/* Supported signature algorithms */
enum bdb_sig_alg {
BDB_SIG_ALG_INVALID = 0, /* Not used; invalid */
BDB_SIG_ALG_RSA4096 = 3, /* RSA-4096, exponent 65537 */
BDB_SIG_ALG_ECSDSA521 = 5, /* ECDSA-521 */
BDB_SIG_ALG_RSA3072B = 7, /* RSA_3072, exponent 3 */
};
/*
* Expected size of bdb_key struct in bytes, not counting variable-length key
* data at end.
*/
#define BDB_KEY_EXPECTED_SIZE 80
struct bdb_key {
/* Magic number to identify struct = BDB_KEY_MAGIC. */
uint32_t struct_magic;
/* Structure version = BDB_KEY_VERSION{MAJOR,MINOR} */
uint8_t struct_major_version;
uint8_t struct_minor_version;
/* Size of structure in bytes, including variable-length key data */
uint16_t struct_size;
/* Hash algorithm (enum bdb_hash_alg) */
uint8_t hash_alg;
/* Signature algorithm (enum bdb_sig_alg) */
uint8_t sig_alg;
/* Reserved; set 0 */
uint8_t reserved0[2];
/* Key version */
uint32_t key_version;
/* Description; null-terminated ASCII */
char description[128];
/*
* Key data. Variable-length; size is struct_size -
* offset_of(bdb_key, key_data).
*/
uint8_t key_data[0];
} __attribute__((packed));
/*****************************************************************************/
/* Signature structure for BDB */
/* Magic number for bdb_sig.struct_magic */
#define BDB_SIG_MAGIC 0x6b334256
/* Current version of bdb_sig struct */
#define BDB_SIG_VERSION_MAJOR 1
#define BDB_SIG_VERSION_MINOR 0
struct bdb_sig {
/* Magic number to identify struct = BDB_SIG_MAGIC. */
uint32_t struct_magic;
/* Structure version = BDB_SIG_VERSION{MAJOR,MINOR} */
uint8_t struct_major_version;
uint8_t struct_minor_version;
/* Size of structure in bytes, including variable-length signature
* data. */
uint16_t struct_size;
/* Hash algorithm used for this signature (enum bdb_hash_alg) */
uint8_t hash_alg;
/* Signature algorithm (enum bdb_sig_alg) */
uint8_t sig_alg;
/* Reserved; set 0 */
uint8_t reserved0[2];
/* Number of bytes of data signed by this signature */
uint32_t signed_size;
/* Description; null-terminated ASCII */
char description[128];
/* Signature data. Variable-length; size is struct_size -
* offset_of(bdb_sig, sig_data). */
uint8_t sig_data[0];
} __attribute__((packed));
/*****************************************************************************/
/* Data structure for BDB */
/* Magic number for bdb_data.struct_magic */
#define BDB_DATA_MAGIC 0x31426442
/* Current version of bdb_sig struct */
#define BDB_DATA_VERSION_MAJOR 1
#define BDB_DATA_VERSION_MINOR 0
struct bdb_data {
/* Magic number to identify struct = BDB_DATA_MAGIC. */
uint32_t struct_magic;
/* Structure version = BDB_DATA_VERSION{MAJOR,MINOR} */
uint8_t struct_major_version;
uint8_t struct_minor_version;
/* Size of structure in bytes, NOT including hashes which follow. */
uint16_t struct_size;
/* Version of data (RW firmware) contained */
uint32_t data_version;
/* Size of OEM area 1 in bytes, or 0 if not present */
uint32_t oem_area_1_size;
/* Number of hashes which follow */
uint8_t num_hashes;
/* Size of each hash entry in bytes */
uint8_t hash_entry_size;
/* Reserved; set 0 */
uint8_t reserved0[2];
/* Number of bytes of data signed by the subkey, including this
* header */
uint32_t signed_size;
/* Reserved; set 0 */
uint8_t reserved1[8];
/* Description; null-terminated ASCII */
char description[128];
} __attribute__((packed));
/* Type of data for bdb_hash.type */
enum bdb_data_type {
/* Types of data for boot descriptor blocks */
BDB_DATA_SP_RW = 1, /* SP-RW firmware */
BDB_DATA_AP_RW = 2, /* AP-RW firmware */
BDB_DATA_MCU = 3, /* MCU firmware */
/* Types of data for kernel descriptor blocks */
BDB_DATA_KERNEL = 128, /* Kernel */
BDB_DATA_CMD_LINE = 129, /* Command line */
BDB_DATA_HEADER16 = 130, /* 16-bit vmlinuz header */
};
/* Hash entries which follow the structure */
struct bdb_hash {
/* Offset of data from start of partition */
uint64_t offset;
/* Size of data in bytes */
uint32_t size;
/* Partition number containing data */
uint8_t partition;
/* Type of data; enum bdb_data_type */
uint8_t type;
/* Reserved; set 0 */
uint8_t reserved0[2];
/* Address in RAM to load data. -1 means use default. */
uint64_t load_address;
/* SHA-256 hash digest */
uint8_t digest[BDB_SHA256_DIGEST_SIZE];
} __attribute__((packed));
/*****************************************************************************/
#endif /* VBOOT_REFERENCE_BDB_STRUCT_H_ */

View File

@@ -0,0 +1,492 @@
/* Copyright (c) 2015 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.
*
* Unit tests
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "bdb.h"
#include "host.h"
#define TEST_EQ(got, want) test_eq(got, want, #got, #want, __LINE__)
void test_eq(int got, int want, const char *gotstr, const char *wantstr,
int line)
{
if (got == want)
return;
fprintf(stderr, "Fail(%d): %s != %s\n"
"got: 0x%08x (%d)\n"
"wanted: 0x%08x (%d)\n",
line, gotstr, wantstr, got, got, want, want);
exit(1);
}
void check_header_tests(void)
{
struct bdb_header sgood = {
.struct_magic = BDB_HEADER_MAGIC,
.struct_major_version = BDB_HEADER_VERSION_MAJOR,
.struct_minor_version = BDB_HEADER_VERSION_MINOR,
.struct_size = sizeof(struct bdb_header),
.bdb_load_address = -1,
.bdb_size = 1024,
.signed_size = 512,
.oem_area_0_size = 256,
};
const size_t ssize = sgood.struct_size;
struct bdb_header s;
s = sgood;
TEST_EQ(bdb_check_header(&s, ssize), BDB_SUCCESS);
TEST_EQ(bdb_check_header(&s, ssize - 1), BDB_ERROR_BUF_SIZE);
s = sgood;
s.struct_size++;
TEST_EQ(bdb_check_header(&s, ssize), BDB_ERROR_BUF_SIZE);
s = sgood;
s.struct_size--;
TEST_EQ(bdb_check_header(&s, ssize), BDB_ERROR_STRUCT_SIZE);
s = sgood;
s.struct_magic++;
TEST_EQ(bdb_check_header(&s, ssize), BDB_ERROR_STRUCT_MAGIC);
s = sgood;
s.struct_major_version++;
TEST_EQ(bdb_check_header(&s, ssize), BDB_ERROR_STRUCT_VERSION);
s = sgood;
s.oem_area_0_size++;
TEST_EQ(bdb_check_header(&s, ssize), BDB_ERROR_OEM_AREA_SIZE);
s = sgood;
s.bdb_size = ssize - 1;
TEST_EQ(bdb_check_header(&s, ssize), BDB_ERROR_BDB_SIZE);
}
void check_key_tests(void)
{
struct bdb_key sgood = {
.struct_magic = BDB_KEY_MAGIC,
.struct_major_version = BDB_KEY_VERSION_MAJOR,
.struct_minor_version = BDB_KEY_VERSION_MINOR,
.struct_size = (sizeof(struct bdb_key) +
BDB_RSA4096_KEY_DATA_SIZE),
.hash_alg = BDB_HASH_ALG_SHA256,
.sig_alg = BDB_SIG_ALG_RSA4096,
.key_version = 1,
.description = "Test key",
};
const size_t ssize = sgood.struct_size;
struct bdb_key s;
s = sgood;
TEST_EQ(bdb_check_key(&s, ssize), BDB_SUCCESS);
TEST_EQ(bdb_check_key(&s, ssize - 1), BDB_ERROR_BUF_SIZE);
s = sgood;
s.struct_size++;
TEST_EQ(bdb_check_key(&s, ssize), BDB_ERROR_BUF_SIZE);
s = sgood;
s.struct_size--;
TEST_EQ(bdb_check_key(&s, ssize), BDB_ERROR_STRUCT_SIZE);
s = sgood;
s.struct_magic++;
TEST_EQ(bdb_check_key(&s, ssize), BDB_ERROR_STRUCT_MAGIC);
s = sgood;
s.struct_major_version++;
TEST_EQ(bdb_check_key(&s, ssize), BDB_ERROR_STRUCT_VERSION);
/* Description must contain a null */
s = sgood;
memset(s.description, 'x', sizeof(s.description));
TEST_EQ(bdb_check_key(&s, ssize), BDB_ERROR_DESCRIPTION);
/* Data AFTER the null is explicitly allowed, though */
s = sgood;
s.description[100] = 'x';
TEST_EQ(bdb_check_key(&s, ssize), BDB_SUCCESS);
/* Limited algorithm choices at present */
s = sgood;
s.hash_alg = BDB_HASH_ALG_INVALID;
TEST_EQ(bdb_check_key(&s, ssize), BDB_ERROR_HASH_ALG);
/* This works because ECDSA521 signatures are smaller than RSA4096 */
s = sgood;
s.sig_alg = BDB_SIG_ALG_ECSDSA521;
TEST_EQ(bdb_check_key(&s, ssize), BDB_SUCCESS);
s = sgood;
s.sig_alg = BDB_SIG_ALG_INVALID;
TEST_EQ(bdb_check_key(&s, ssize), BDB_ERROR_SIG_ALG);
}
void check_sig_tests(void)
{
struct bdb_sig sgood = {
.struct_magic = BDB_SIG_MAGIC,
.struct_major_version = BDB_SIG_VERSION_MAJOR,
.struct_minor_version = BDB_SIG_VERSION_MINOR,
.struct_size = sizeof(struct bdb_sig) + BDB_RSA4096_SIG_SIZE,
.hash_alg = BDB_HASH_ALG_SHA256,
.sig_alg = BDB_SIG_ALG_RSA4096,
.signed_size = 123,
.description = "Test sig",
};
const size_t ssize = sgood.struct_size;
struct bdb_sig s;
s = sgood;
TEST_EQ(bdb_check_sig(&s, ssize), BDB_SUCCESS);
TEST_EQ(bdb_check_sig(&s, ssize - 1), BDB_ERROR_BUF_SIZE);
s = sgood;
s.struct_size++;
TEST_EQ(bdb_check_sig(&s, ssize), BDB_ERROR_BUF_SIZE);
s = sgood;
s.struct_size--;
TEST_EQ(bdb_check_sig(&s, ssize), BDB_ERROR_STRUCT_SIZE);
s = sgood;
s.struct_magic++;
TEST_EQ(bdb_check_sig(&s, ssize), BDB_ERROR_STRUCT_MAGIC);
s = sgood;
s.struct_major_version++;
TEST_EQ(bdb_check_sig(&s, ssize), BDB_ERROR_STRUCT_VERSION);
/* Description must contain a null */
s = sgood;
memset(s.description, 'x', sizeof(s.description));
TEST_EQ(bdb_check_sig(&s, ssize), BDB_ERROR_DESCRIPTION);
/* Data AFTER the null is explicitly allowed, though */
s = sgood;
s.description[100] = 'x';
TEST_EQ(bdb_check_sig(&s, ssize), BDB_SUCCESS);
/* Limited algorithm choices at present */
s = sgood;
s.hash_alg = BDB_HASH_ALG_INVALID;
TEST_EQ(bdb_check_sig(&s, ssize), BDB_ERROR_HASH_ALG);
/* This works because ECDSA521 signatures are smaller than RSA4096 */
s = sgood;
s.sig_alg = BDB_SIG_ALG_ECSDSA521;
TEST_EQ(bdb_check_sig(&s, ssize), BDB_SUCCESS);
s = sgood;
s.sig_alg = BDB_SIG_ALG_INVALID;
TEST_EQ(bdb_check_sig(&s, ssize), BDB_ERROR_SIG_ALG);
}
void check_data_tests(void)
{
struct bdb_data sgood = {
.struct_magic = BDB_DATA_MAGIC,
.struct_major_version = BDB_DATA_VERSION_MAJOR,
.struct_minor_version = BDB_DATA_VERSION_MINOR,
.struct_size = sizeof(struct bdb_data),
.data_version = 1,
.oem_area_1_size = 256,
.num_hashes = 3,
.hash_entry_size = sizeof(struct bdb_hash),
.signed_size = 2048,
.description = "Test data",
};
const size_t ssize = sgood.signed_size;
struct bdb_data s;
s = sgood;
TEST_EQ(bdb_check_data(&s, ssize), BDB_SUCCESS);
TEST_EQ(bdb_check_data(&s, ssize - 1), BDB_ERROR_BUF_SIZE);
s = sgood;
s.struct_size--;
TEST_EQ(bdb_check_data(&s, ssize), BDB_ERROR_STRUCT_SIZE);
s = sgood;
s.struct_magic++;
TEST_EQ(bdb_check_data(&s, ssize), BDB_ERROR_STRUCT_MAGIC);
s = sgood;
s.struct_major_version++;
TEST_EQ(bdb_check_data(&s, ssize), BDB_ERROR_STRUCT_VERSION);
/* Description must contain a null */
s = sgood;
memset(s.description, 'x', sizeof(s.description));
TEST_EQ(bdb_check_data(&s, ssize), BDB_ERROR_DESCRIPTION);
/* Data AFTER the null is explicitly allowed, though */
s = sgood;
s.description[100] = 'x';
TEST_EQ(bdb_check_data(&s, ssize), BDB_SUCCESS);
s = sgood;
s.hash_entry_size--;
TEST_EQ(bdb_check_data(&s, ssize), BDB_ERROR_HASH_ENTRY_SIZE);
s = sgood;
s.oem_area_1_size++;
TEST_EQ(bdb_check_data(&s, ssize), BDB_ERROR_OEM_AREA_SIZE);
/* Check exact size needed */
s = sgood;
s.signed_size = sizeof(s) + s.num_hashes * sizeof(struct bdb_hash) +
s.oem_area_1_size;
TEST_EQ(bdb_check_data(&s, ssize), BDB_SUCCESS);
s.signed_size--;
TEST_EQ(bdb_check_data(&s, ssize), BDB_ERROR_SIGNED_SIZE);
/*
* TODO: Verify wraparound check works. That can only be tested on a
* platform where size_t is uint32_t, because otherwise a 32-bit
* oem_area_1_size can't cause wraparound.
*/
}
/**
* Test bdb_verify() and bdb_create()
*/
void check_bdb_verify(void)
{
uint8_t oem_area_0[32] = "Some OEM area.";
uint8_t oem_area_1[64] = "Some other OEM area.";
struct bdb_hash hash[2] = {
{
.offset = 0x10000,
.size = 0x18000,
.partition = 1,
.type = BDB_DATA_SP_RW,
.load_address = 0x100000,
.digest = {0x11, 0x11, 0x11, 0x10},
},
{
.offset = 0x28000,
.size = 0x20000,
.partition = 1,
.type = BDB_DATA_AP_RW,
.load_address = 0x200000,
.digest = {0x22, 0x22, 0x22, 0x20},
},
};
struct bdb_create_params p = {
.bdb_load_address = 0x11223344,
.oem_area_0 = oem_area_0,
.oem_area_0_size = sizeof(oem_area_0),
.oem_area_1 = oem_area_1,
.oem_area_1_size = sizeof(oem_area_1),
.header_sig_description = "The header sig",
.data_sig_description = "The data sig",
.data_description = "Test BDB data",
.data_version = 3,
.hash = hash,
.num_hashes = 2,
};
uint8_t bdbkey_digest[BDB_SHA256_DIGEST_SIZE];
struct bdb_header *hgood, *h;
size_t hsize;
/* Load keys */
p.bdbkey = bdb_create_key("testkeys/bdbkey.keyb", 100, "BDB key");
p.subkey = bdb_create_key("testkeys/subkey.keyb", 200, "Subkey");
p.private_bdbkey = read_pem("testkeys/bdbkey.pem");
p.private_subkey = read_pem("testkeys/subkey.pem");
if (!p.bdbkey || !p.subkey || !p.private_bdbkey || !p.private_subkey) {
fprintf(stderr, "Unable to load test keys\n");
exit(2);
}
bdb_sha256(bdbkey_digest, p.bdbkey, p.bdbkey->struct_size);
/* Create the test BDB */
hgood = bdb_create(&p);
if (!hgood) {
fprintf(stderr, "Unable to create test BDB\n");
exit(2);
}
hsize = hgood->bdb_size;
/* Allocate a copy we can mangle */
h = calloc(hsize, 1);
/* As created, it should pass */
memcpy(h, hgood, hsize);
TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_SUCCESS);
/* Mangle each component in turn */
memcpy(h, hgood, hsize);
h->struct_magic++;
TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_HEADER);
memcpy(h, hgood, hsize);
((struct bdb_key *)bdb_get_bdbkey(h))->struct_magic++;
TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_BDBKEY);
memcpy(h, hgood, hsize);
((struct bdb_key *)bdb_get_bdbkey(h))->key_version++;
TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_GOOD_OTHER_THAN_KEY);
memcpy(h, hgood, hsize);
h->oem_area_0_size += hsize;
TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_OEM_AREA_0);
memcpy(h, hgood, hsize);
((struct bdb_key *)bdb_get_subkey(h))->struct_magic++;
TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_SUBKEY);
memcpy(h, hgood, hsize);
((struct bdb_key *)bdb_get_subkey(h))->struct_size += 4;
TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_BDB_SIGNED_SIZE);
memcpy(h, hgood, hsize);
((struct bdb_sig *)bdb_get_header_sig(h))->struct_magic++;
TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_HEADER_SIG);
memcpy(h, hgood, hsize);
((struct bdb_sig *)bdb_get_header_sig(h))->signed_size--;
TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_HEADER_SIG);
memcpy(h, hgood, hsize);
((struct bdb_sig *)bdb_get_header_sig(h))->sig_data[0] ^= 0x42;
TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_HEADER_SIG);
/* Also make sure the header sig really covers all the fields */
memcpy(h, hgood, hsize);
((struct bdb_key *)bdb_get_subkey(h))->key_version++;
TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_HEADER_SIG);
memcpy(h, hgood, hsize);
((uint8_t *)bdb_get_oem_area_0(h))[0] ^= 0x42;
TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_HEADER_SIG);
memcpy(h, hgood, hsize);
((uint8_t *)bdb_get_oem_area_0(h))[p.oem_area_0_size - 1] ^= 0x24;
TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_HEADER_SIG);
/* Check data header */
memcpy(h, hgood, hsize);
((struct bdb_data *)bdb_get_data(h))->struct_magic++;
TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_DATA);
memcpy(h, hgood, hsize);
((struct bdb_sig *)bdb_get_data_sig(h))->struct_magic++;
TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_DATA_SIG);
memcpy(h, hgood, hsize);
((struct bdb_sig *)bdb_get_data_sig(h))->signed_size--;
TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_DATA_SIG);
memcpy(h, hgood, hsize);
((struct bdb_sig *)bdb_get_data_sig(h))->sig_data[0] ^= 0x42;
TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_DATA_SIG);
/* Also make sure the data sig really covers all the fields */
memcpy(h, hgood, hsize);
((struct bdb_data *)bdb_get_data(h))->data_version--;
TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_DATA_SIG);
memcpy(h, hgood, hsize);
((uint8_t *)bdb_get_oem_area_1(h))[0] ^= 0x42;
TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_DATA_SIG);
memcpy(h, hgood, hsize);
((uint8_t *)bdb_get_oem_area_1(h))[p.oem_area_1_size - 1] ^= 0x24;
TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_DATA_SIG);
memcpy(h, hgood, hsize);
((struct bdb_hash *)bdb_get_hash(h, BDB_DATA_SP_RW))->offset++;
TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_DATA_SIG);
memcpy(h, hgood, hsize);
((struct bdb_hash *)bdb_get_hash(h, BDB_DATA_AP_RW))->digest[0] ^= 0x96;
TEST_EQ(bdb_verify(h, hsize, bdbkey_digest), BDB_ERROR_DATA_SIG);
/*
* This is also a convenient place to test that all the parameters we
* fed into bdb_create() also worked. That also tests all the
* bdb_get_*() functions.
*/
memcpy(h, hgood, hsize);
TEST_EQ(h->bdb_load_address, p.bdb_load_address);
TEST_EQ(strcmp(bdb_get_bdbkey(h)->description,
p.bdbkey->description), 0);
TEST_EQ(bdb_get_bdbkey(h)->key_version, p.bdbkey->key_version);
TEST_EQ(h->oem_area_0_size, p.oem_area_0_size);
TEST_EQ(memcmp(bdb_get_oem_area_0(h), oem_area_0, sizeof(oem_area_0)),
0);
TEST_EQ(strcmp(bdb_get_subkey(h)->description, p.subkey->description),
0);
TEST_EQ(bdb_get_subkey(h)->key_version, p.subkey->key_version);
TEST_EQ(strcmp(bdb_get_header_sig(h)->description,
p.header_sig_description), 0);
TEST_EQ(strcmp(bdb_get_data(h)->description, p.data_description), 0);
TEST_EQ(bdb_get_data(h)->data_version, p.data_version);
TEST_EQ(bdb_get_data(h)->num_hashes, p.num_hashes);
TEST_EQ(bdb_get_data(h)->oem_area_1_size, p.oem_area_1_size);
TEST_EQ(memcmp(bdb_get_oem_area_1(h), oem_area_1, sizeof(oem_area_1)),
0);
TEST_EQ(strcmp(bdb_get_data_sig(h)->description,
p.data_sig_description), 0);
/* Test getting hash entries */
memcpy(h, hgood, hsize);
TEST_EQ(bdb_get_hash(h, BDB_DATA_SP_RW)->offset, hash[0].offset);
TEST_EQ(bdb_get_hash(h, BDB_DATA_AP_RW)->offset, hash[1].offset);
/* And a non-existent one */
TEST_EQ(bdb_get_hash(h, BDB_DATA_MCU)!=NULL, 0);
/*
* TODO: Verify wraparound checks works. That can only be tested on a
* platform where size_t is uint32_t, because otherwise a 32-bit
* oem_area_1_size can't cause wraparound.
*/
/* Free keys and buffers */
free(p.bdbkey);
free(p.subkey);
RSA_free(p.private_bdbkey);
RSA_free(p.private_subkey);
free(hgood);
free(h);
}
/*****************************************************************************/
int main(void)
{
printf("Running tests...\n");
check_header_tests();
check_key_tests();
check_sig_tests();
check_data_tests();
check_bdb_verify();
printf("All tests passed!\n");
return 0;
}

View 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.
*/
/*
* C port of DumpPublicKey.java from the Android Open source project with
* support for additional RSA key sizes. (platform/system/core,git/libmincrypt
* /tools/DumpPublicKey.java). Uses the OpenSSL X509 and BIGNUM library.
*/
#include <openssl/pem.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
/*
* Command line tool to extract RSA public keys from X.509 certificates and
* output a pre-processed version of keys for use by RSA verification routines.
*/
int check(RSA *key)
{
int public_exponent = BN_get_word(key->e);
int modulus = BN_num_bits(key->n);
if (public_exponent != 65537 && public_exponent != 3) {
fprintf(stderr, "WARNING: Non-standard public exponent %d.\n",
public_exponent);
}
if (modulus != 1024 && modulus != 2048 && modulus != 3072 &&
modulus != 4096 && modulus != 8192) {
fprintf(stderr, "WARNING: Non-standard modulus length = %d.\n",
modulus);
}
return 1;
}
/**
* Pre-processes and outputs RSA public key to standard output.
*/
void output(RSA *key)
{
BIGNUM *N;
BIGNUM *Big1 = NULL, *Big2 = NULL, *Big32 = NULL, *BigMinus1 = NULL;
BIGNUM *B = NULL;
BIGNUM *N0inv= NULL, *R = NULL, *RR = NULL, *RRTemp = NULL;
BIGNUM *NnumBits = NULL;
BIGNUM *n = NULL, *rr = NULL;
BN_CTX *bn_ctx = BN_CTX_new();
uint32_t n0invout;
int nwords, i;
N = key->n;
/* Output size of RSA key in 32-bit words */
nwords = BN_num_bits(N) / 32;
if (-1 == write(1, &nwords, sizeof(nwords)))
goto failure;
/* Initialize BIGNUMs */
Big1 = BN_new();
Big2 = BN_new();
Big32 = BN_new();
BigMinus1 = BN_new();
N0inv= BN_new();
R = BN_new();
RR = BN_new();
RRTemp = BN_new();
NnumBits = BN_new();
n = BN_new();
rr = BN_new();
BN_set_word(Big1, 1L);
BN_set_word(Big2, 2L);
BN_set_word(Big32, 32L);
BN_sub(BigMinus1, Big1, Big2);
B = BN_new();
BN_exp(B, Big2, Big32, bn_ctx); /* B = 2^32 */
/* Calculate and output N0inv = -1 / N[0] mod 2^32 */
BN_mod_inverse(N0inv, N, B, bn_ctx);
BN_sub(N0inv, B, N0inv);
n0invout = BN_get_word(N0inv);
if (-1 == write(1, &n0invout, sizeof(n0invout)))
goto failure;
/* Calculate R = 2^(# of key bits) */
BN_set_word(NnumBits, BN_num_bits(N));
BN_exp(R, Big2, NnumBits, bn_ctx);
/* Calculate RR = R^2 mod N */
BN_copy(RR, R);
BN_mul(RRTemp, RR, R, bn_ctx);
BN_mod(RR, RRTemp, N, bn_ctx);
/* Write out modulus as little endian array of integers. */
for (i = 0; i < nwords; ++i) {
uint32_t nout;
BN_mod(n, N, B, bn_ctx); /* n = N mod B */
nout = BN_get_word(n);
if (-1 == write(1, &nout, sizeof(nout)))
goto failure;
BN_rshift(N, N, 32); /* N = N/B */
}
/* Write R^2 as little endian array of integers. */
for (i = 0; i < nwords; ++i) {
uint32_t rrout;
BN_mod(rr, RR, B, bn_ctx); /* rr = RR mod B */
rrout = BN_get_word(rr);
if (-1 == write(1, &rrout, sizeof(rrout)))
goto failure;
BN_rshift(RR, RR, 32); /* RR = RR/B */
}
failure:
/* Free BIGNUMs. */
BN_free(Big1);
BN_free(Big2);
BN_free(Big32);
BN_free(BigMinus1);
BN_free(N0inv);
BN_free(R);
BN_free(RRTemp);
BN_free(NnumBits);
BN_free(n);
BN_free(rr);
}
int main(int argc, char* argv[]) {
int cert_mode = 0;
FILE* fp;
X509* cert = NULL;
RSA* pubkey = NULL;
EVP_PKEY* key;
char *progname;
if (argc != 3 ||
(strcmp(argv[1], "-cert") && strcmp(argv[1], "-pub"))) {
progname = strrchr(argv[0], '/');
if (progname)
progname++;
else
progname = argv[0];
fprintf(stderr, "Usage: %s <-cert | -pub> <file>\n", progname);
return -1;
}
if (!strcmp(argv[1], "-cert"))
cert_mode = 1;
fp = fopen(argv[2], "r");
if (!fp) {
fprintf(stderr, "Couldn't open file %s!\n", argv[2]);
return -1;
}
if (cert_mode) {
/* Read the certificate */
if (!PEM_read_X509(fp, &cert, NULL, NULL)) {
fprintf(stderr, "Couldn't read certificate.\n");
goto fail;
}
/* Get the public key from the certificate. */
key = X509_get_pubkey(cert);
/* Convert to a RSA_style key. */
if (!(pubkey = EVP_PKEY_get1_RSA(key))) {
fprintf(stderr, "Couldn't convert to RSA style key.\n");
goto fail;
}
} else {
/* Read the pubkey in .PEM format. */
if (!(pubkey = PEM_read_RSA_PUBKEY(fp, NULL, NULL, NULL))) {
fprintf(stderr, "Couldn't read public key file.\n");
goto fail;
}
}
if (check(pubkey)) {
output(pubkey);
}
fail:
X509_free(cert);
RSA_free(pubkey);
fclose(fp);
return 0;
}

View File

@@ -0,0 +1,17 @@
/* Copyright (c) 2015 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.
*
* Boot descriptor block firmware ECDSA stub
*/
#include <string.h>
#include "bdb.h"
int bdb_ecdsa521_verify(const uint8_t *key_data,
const uint8_t *sig,
const uint8_t *digest)
{
/* This is just a stub */
return BDB_ERROR_DIGEST;
}

View File

@@ -0,0 +1,347 @@
/* Copyright (c) 2015 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.
*
* Host functions for signing
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "bdb.h"
#include "host.h"
char *strzcpy(char *dest, const char *src, size_t size)
{
strncpy(dest, src, size);
dest[size - 1] = 0;
return dest;
}
uint8_t *read_file(const char *filename, uint32_t *size_ptr)
{
FILE *f;
uint8_t *buf;
long size;
*size_ptr = 0;
f = fopen(filename, "rb");
if (!f) {
fprintf(stderr, "Unable to open file %s\n", filename);
return NULL;
}
fseek(f, 0, SEEK_END);
size = ftell(f);
rewind(f);
if (size < 0 || size > UINT32_MAX) {
fclose(f);
return NULL;
}
buf = malloc(size);
if (!buf) {
fclose(f);
return NULL;
}
if (1 != fread(buf, size, 1, f)) {
fprintf(stderr, "Unable to read file %s\n", filename);
fclose(f);
free(buf);
return NULL;
}
fclose(f);
*size_ptr = size;
return buf;
}
int write_file(const char *filename, const void *buf, uint32_t size)
{
FILE *f = fopen(filename, "wb");
if (!f) {
fprintf(stderr, "Unable to open file %s\n", filename);
return 1;
}
if (1 != fwrite(buf, size, 1, f)) {
fprintf(stderr, "Unable to write to file %s\n", filename);
fclose(f);
unlink(filename); /* Delete any partial file */
return 1;
}
fclose(f);
return 0;
}
struct rsa_st *read_pem(const char *filename)
{
struct rsa_st *pem;
FILE *f;
/* Read private key */
f = fopen(filename, "rb");
if (!f) {
fprintf(stderr, "%s: unable to read key from %s\n",
__func__, filename);
return NULL;
}
pem = PEM_read_RSAPrivateKey(f, NULL, NULL, NULL);
fclose(f);
return pem;
}
struct bdb_key *bdb_create_key(const char *filename,
uint32_t key_version,
const char *desc)
{
uint32_t sig_alg;
size_t key_size = sizeof(struct bdb_key);
struct bdb_key *k;
uint8_t *kdata;
uint32_t kdata_size = 0;
/*
* Read key data. Somewhat lame assumption that we can determine the
* signature algorithm from the key size, but it's true right now.
*/
kdata = read_file(filename, &kdata_size);
if (kdata_size == BDB_RSA4096_KEY_DATA_SIZE) {
sig_alg = BDB_SIG_ALG_RSA4096;
} else if (kdata_size == BDB_RSA3072B_KEY_DATA_SIZE) {
sig_alg = BDB_SIG_ALG_RSA3072B;
} else {
fprintf(stderr, "%s: bad key size from %s\n",
__func__, filename);
return NULL;
}
key_size += kdata_size;
/* Allocate buffer */
k = (struct bdb_key *)calloc(key_size, 1);
if (!k) {
free(kdata);
return NULL;
}
k->struct_magic = BDB_KEY_MAGIC;
k->struct_major_version = BDB_KEY_VERSION_MAJOR;
k->struct_minor_version = BDB_KEY_VERSION_MINOR;
k->struct_size = key_size;
k->hash_alg = BDB_HASH_ALG_SHA256;
k->sig_alg = sig_alg;
k->key_version = key_version;
/* Copy description, if any */
if (desc)
strzcpy(k->description, desc, sizeof(k->description));
/* Copy key data */
memcpy(k->key_data, kdata, kdata_size);
free(kdata);
return k;
}
struct bdb_sig *bdb_create_sig(const void *data,
size_t size,
struct rsa_st *key,
uint32_t sig_alg,
const char *desc)
{
static const uint8_t info[] = {
0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
0x00, 0x04, 0x20
};
size_t sig_size = sizeof(struct bdb_sig);
uint8_t digest[sizeof(info) + BDB_SHA256_DIGEST_SIZE];
struct bdb_sig *sig;
if (size >= UINT32_MAX)
return NULL;
switch(sig_alg) {
case BDB_SIG_ALG_RSA4096:
sig_size += BDB_RSA4096_SIG_SIZE;
break;
case BDB_SIG_ALG_RSA3072B:
sig_size += BDB_RSA3072B_SIG_SIZE;
break;
default:
fprintf(stderr, "%s: bad signature algorithm %d\n",
__func__, sig_alg);
return NULL;
}
/* Allocate buffer */
sig = (struct bdb_sig *)calloc(sig_size, 1);
if (!sig)
return NULL;
sig->struct_magic = BDB_SIG_MAGIC;
sig->struct_major_version = BDB_SIG_VERSION_MAJOR;
sig->struct_minor_version = BDB_SIG_VERSION_MINOR;
sig->struct_size = sig_size;
sig->hash_alg = BDB_HASH_ALG_SHA256;
sig->sig_alg = sig_alg;
sig->signed_size = size;
/* Copy description, if any */
if (desc)
strzcpy(sig->description, desc, sizeof(sig->description));
/* Calculate info-padded digest */
memcpy(digest, info, sizeof(info));
if (bdb_sha256(digest + sizeof(info), data, size)) {
free(sig);
return NULL;
}
/* RSA-encrypt the signature */
if (RSA_private_encrypt(sizeof(digest),
digest,
sig->sig_data,
key,
RSA_PKCS1_PADDING) == -1) {
free(sig);
return NULL;
}
return sig;
}
struct bdb_header *bdb_create(struct bdb_create_params *p)
{
size_t bdb_size = 0;
size_t sig_size = sizeof(struct bdb_sig) + BDB_RSA4096_SIG_SIZE;
size_t hashes_size = sizeof(struct bdb_hash) * p->num_hashes;
uint8_t *buf, *bnext;
struct bdb_header *h;
struct bdb_sig *sig;
struct bdb_data *data;
const void *oem;
/* We can do some checks before we even allocate the buffer */
/* Make sure OEM sizes are aligned */
if ((p->oem_area_0_size & 3) || (p->oem_area_1_size & 3)) {
fprintf(stderr, "%s: OEM areas not 32-bit aligned\n",
__func__);
return NULL;
}
/* Hash count must fit in uint8_t */
if (p->num_hashes > 255) {
fprintf(stderr, "%s: too many hashes\n", __func__);
return NULL;
}
/* Calculate BDB size */
bdb_size = sizeof(struct bdb_header);
bdb_size += p->bdbkey->struct_size;
bdb_size += p->oem_area_0_size;
bdb_size += p->subkey->struct_size;
bdb_size += sig_size;
bdb_size += sizeof(struct bdb_data);
bdb_size += p->oem_area_1_size;
bdb_size += sizeof(struct bdb_hash) * p->num_hashes;
bdb_size += sig_size;
/* Make sure it fits */
if (bdb_size > UINT32_MAX) {
fprintf(stderr, "%s: BDB size > UINT32_MAX\n", __func__);
return NULL;
}
/* Allocate a buffer */
bnext = buf = calloc(bdb_size, 1);
if (!buf) {
fprintf(stderr, "%s: can't allocate buffer\n", __func__);
return NULL;
}
/* Fill in the header */
h = (struct bdb_header *)bnext;
h->struct_magic = BDB_HEADER_MAGIC;
h->struct_major_version = BDB_HEADER_VERSION_MAJOR;
h->struct_minor_version = BDB_HEADER_VERSION_MINOR;
h->struct_size = sizeof(*h);
h->bdb_load_address = p->bdb_load_address;
h->bdb_size = bdb_size;
h->signed_size = p->oem_area_0_size + p->subkey->struct_size;
h->oem_area_0_size = p->oem_area_0_size;
bnext += h->struct_size;
/* Copy BDB key */
memcpy(bnext, p->bdbkey, p->bdbkey->struct_size);
bnext += p->bdbkey->struct_size;
/* Copy OEM area 0 */
oem = bnext;
if (p->oem_area_0_size) {
memcpy(bnext, p->oem_area_0, p->oem_area_0_size);
bnext += p->oem_area_0_size;
}
/* Copy subkey */
memcpy(bnext, p->subkey, p->subkey->struct_size);
bnext += p->subkey->struct_size;
/*
* Create header signature using private BDB key.
*
* TODO: create the header signature in a totally separate step. That
* way, the private BDB key is not required each time a BDB is created.
*/
sig = bdb_create_sig(oem, h->signed_size, p->private_bdbkey,
p->bdbkey->sig_alg, p->header_sig_description);
memcpy(bnext, sig, sig->struct_size);
bnext += sig->struct_size;
/* Fill in the data */
data = (struct bdb_data *)bnext;
data->struct_magic = BDB_DATA_MAGIC;
data->struct_major_version = BDB_DATA_VERSION_MAJOR;
data->struct_minor_version = BDB_DATA_VERSION_MINOR;
data->struct_size = sizeof(struct bdb_data);
data->data_version = p->data_version;
data->oem_area_1_size = p->oem_area_1_size;
data->num_hashes = p->num_hashes;
data->hash_entry_size = sizeof(struct bdb_hash);
data->signed_size = data->struct_size + data->oem_area_1_size +
hashes_size;
if (p->data_description) {
strzcpy(data->description, p->data_description,
sizeof(data->description));
}
bnext += data->struct_size;
/* Copy OEM area 1 */
oem = bnext;
if (p->oem_area_1_size) {
memcpy(bnext, p->oem_area_1, p->oem_area_1_size);
bnext += p->oem_area_1_size;
}
/* Copy hashes */
memcpy(bnext, p->hash, hashes_size);
bnext += hashes_size;
/* Create data signature using private subkey */
sig = bdb_create_sig(data, data->signed_size, p->private_subkey,
p->subkey->sig_alg, p->data_sig_description);
memcpy(bnext, sig, sig->struct_size);
/* Return the BDB */
return h;
}

View File

@@ -0,0 +1,171 @@
/* Copyright (c) 2015 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.
*
* Boot descriptor block host functions
*/
#ifndef VBOOT_REFERENCE_BDB_HOST_H_
#define VBOOT_REFERENCE_BDB_HOST_H_
#include <stdlib.h>
#include <openssl/pem.h>
#include "bdb_struct.h"
/*****************************************************************************/
/*
Expected calling sequence:
Load and check just the header
bdb_check_header(buf, size);
Load and verify the entire BDB
bdb_verify(buf, size, bdb_key_hash, dev_mode_flag);
bdb_check_header() again - paranoia against bad storage devices
bdb_check_key() on BDB key
bdb_sha256() on BDB key
Compare with appropriate root key hash
If dev_mode_flag(), mismatch is not fatal
bdb_check_sig() on BDB header sig
bdb_sha256() on OEM area 1, RW subkey
bdb_rsa_verify() on digest using BDB key
bdb_check_key() on RW subkey
bdb_check_data() on RW data
bdb_check_sig() on data sig
bdb_sha256() on data, OEM area 1, hashes
bdb_rsa_verify() on digest using RW subkey
Check RW subkey version. If normal boot from primary BDB, roll forward
Check data version. If normal boot from primary BDB, roll forward
*/
/*****************************************************************************/
/* Codes for functions returning numeric error codes */
enum bdb_host_return_code {
/* All/any of bdb_return_code, and the following... */
/* Other errors */
BDB_ERROR_HOST = 200,
};
/*****************************************************************************/
/* Functions */
/**
* Like strncpy, but guaranteeing null termination
*/
char *strzcpy(char *dest, const char *src, size_t size);
/**
* Read a file.
*
* Caller must free() the returned buffer.
*
* @param filename Path to file
* @param size_ptr Destination for size of buffer
* @return A newly allocated buffer containing the data, or NULL if error.
*/
uint8_t *read_file(const char *filename, uint32_t *size_ptr);
/**
* Write a file.
*
* @param buf Data to write
* @param size Size of data in bytes
* @return 0 if success, non-zero error code if error.
*/
int write_file(const char *filename, const void *buf, uint32_t size);
/**
* Read a PEM from a file.
*
* Caller must free the PEM with RSA_free().
*
* @param filename Path to file
* @return A newly allocated PEM object, or NULL if error.
*/
struct rsa_st *read_pem(const char *filename);
/**
* Create a BDB public key object.
*
* Caller must free() the returned key.
*
* @param filename Path to file containing public key (.keyb)
* @param key_version Version for key
* @param desc Description. Optional; may be NULL.
* @return A newly allocated public key, or NULL if error.
*/
struct bdb_key *bdb_create_key(const char *filename,
uint32_t key_version,
const char *desc);
/**
* Create a BDB signature object.
*
* Caller must free() the returned signature.
*
* @param data Data to sign
* @param size Size of data in bytes
* @param key PEM key
* @param sig_alg Signature algorithm
* @param desc Description. Optional; may be NULL.
* @return A newly allocated signature, or NULL if error.
*/
struct bdb_sig *bdb_create_sig(const void *data,
size_t size,
struct rsa_st *key,
uint32_t sig_alg,
const char *desc);
struct bdb_create_params
{
/* Load address */
uint64_t bdb_load_address;
/* OEM areas. Size may be 0, in which case the buffer is ignored */
uint8_t *oem_area_0;
uint32_t oem_area_0_size;
uint8_t *oem_area_1;
uint32_t oem_area_1_size;
/* Public BDB key and subkey */
struct bdb_key *bdbkey;
struct bdb_key *subkey;
/* Private BDB key and subkey */
struct rsa_st *private_bdbkey;
struct rsa_st *private_subkey;
/* Descriptions for header and data signatures */
char *header_sig_description;
char *data_sig_description;
/* Data description and version */
char *data_description;
uint32_t data_version;
/* Data hashes and count */
struct bdb_hash *hash;
uint32_t num_hashes;
};
/**
* Create a new BDB
*
* Caller must free() returned object.
*
* @param p Creation parameters
* @return A newly allocated BDB, or NULL if error.
*/
struct bdb_header *bdb_create(struct bdb_create_params *p);
/*****************************************************************************/
#endif /* VBOOT_REFERENCE_BDB_HOST_H_ */

View File

@@ -0,0 +1,339 @@
/* Copyright (c) 2015 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.
*
* Boot descriptor block firmware RSA
*/
#include <string.h>
#include "bdb.h"
/* Public key structure in RAM */
struct public_key {
uint32_t arrsize; /* Size of n[] and rr[] arrays in elements */
uint32_t n0inv; /* -1 / n[0] mod 2^32 */
const uint32_t *n; /* Modulus as little endian array */
const uint32_t *rr; /* R^2 as little endian array */
};
/**
* a[] -= mod
*/
static void subM(const struct public_key *key, uint32_t *a)
{
int64_t A = 0;
uint32_t i;
for (i = 0; i < key->arrsize; ++i) {
A += (uint64_t)a[i] - key->n[i];
a[i] = (uint32_t)A;
A >>= 32;
}
}
/**
* Return a[] >= mod
*/
int vb2_mont_ge(const struct public_key *key, uint32_t *a)
{
uint32_t i;
for (i = key->arrsize; i;) {
--i;
if (a[i] < key->n[i])
return 0;
if (a[i] > key->n[i])
return 1;
}
return 1; /* equal */
}
/**
* Montgomery c[] += a * b[] / R % mod
*/
static void montMulAdd(const struct public_key *key,
uint32_t *c,
const uint32_t a,
const uint32_t *b)
{
uint64_t A = (uint64_t)a * b[0] + c[0];
uint32_t d0 = (uint32_t)A * key->n0inv;
uint64_t B = (uint64_t)d0 * key->n[0] + (uint32_t)A;
uint32_t i;
for (i = 1; i < key->arrsize; ++i) {
A = (A >> 32) + (uint64_t)a * b[i] + c[i];
B = (B >> 32) + (uint64_t)d0 * key->n[i] + (uint32_t)A;
c[i - 1] = (uint32_t)B;
}
A = (A >> 32) + (B >> 32);
c[i - 1] = (uint32_t)A;
if (A >> 32) {
subM(key, c);
}
}
/**
* Montgomery c[] = a[] * b[] / R % mod
*/
static void montMul(const struct public_key *key,
uint32_t *c,
const uint32_t *a,
const uint32_t *b)
{
uint32_t i;
for (i = 0; i < key->arrsize; ++i) {
c[i] = 0;
}
for (i = 0; i < key->arrsize; ++i) {
montMulAdd(key, c, a[i], b);
}
}
int vb2_safe_memcmp(const void *s1, const void *s2, size_t size)
{
const unsigned char *us1 = s1;
const unsigned char *us2 = s2;
int result = 0;
if (0 == size)
return 0;
/*
* Code snippet without data-dependent branch due to Nate Lawson
* (nate@root.org) of Root Labs.
*/
while (size--)
result |= *us1++ ^ *us2++;
return result != 0;
}
/*
* PKCS 1.5 padding (from the RSA PKCS#1 v2.1 standard)
*
* Depending on the RSA key size and hash function, the padding is calculated
* as follows:
*
* 0x00 || 0x01 || PS || 0x00 || T
*
* T: DER Encoded DigestInfo value which depends on the hash function used.
*
* SHA-256: (0x)30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 || H.
*
* Length(T) = 51 octets for SHA-256
*
* PS: octet string consisting of {Length(RSA Key) - Length(T) - 3} 0xFF
*/
static const uint8_t sha256_tail[] = {
0x00,0x30,0x31,0x30,0x0d,0x06,0x09,0x60,
0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x01,
0x05,0x00,0x04,0x20
};
int vb2_check_padding(const uint8_t *sig, const struct public_key *key,
uint32_t pad_size)
{
/* Determine padding to use depending on the signature type */
const uint32_t tail_size = sizeof(sha256_tail);
int result = 0;
int i;
/* First 2 bytes are always 0x00 0x01 */
result |= *sig++ ^ 0x00;
result |= *sig++ ^ 0x01;
/* Then 0xff bytes until the tail */
for (i = 0; i < pad_size - tail_size - 2; i++)
result |= *sig++ ^ 0xff;
/*
* Then the tail. Even though there are probably no timing issues
* here, we use vb2_safe_memcmp() just to be on the safe side.
*/
result |= vb2_safe_memcmp(sig, sha256_tail, tail_size);
return result ? BDB_ERROR_DIGEST : BDB_SUCCESS;
}
/* Array size for RSA4096 */
#define ARRSIZE4096 (4096 / 32)
/**
* In-place public exponentiation. (exponent 65537, key size 4096 bits)
*
* @param key Key to use in signing
* @param inout Input and output big-endian byte array
*/
static void modpowF4(const struct public_key *key, uint8_t *inout)
{
uint32_t a[ARRSIZE4096];
uint32_t aR[ARRSIZE4096];
uint32_t aaR[ARRSIZE4096];
uint32_t *aaa = aaR; /* Re-use location. */
int i;
/* Convert from big endian byte array to little endian word array. */
for (i = 0; i < ARRSIZE4096; ++i) {
uint32_t tmp =
(inout[((ARRSIZE4096 - 1 - i) * 4) + 0] << 24) |
(inout[((ARRSIZE4096 - 1 - i) * 4) + 1] << 16) |
(inout[((ARRSIZE4096 - 1 - i) * 4) + 2] << 8) |
(inout[((ARRSIZE4096 - 1 - i) * 4) + 3] << 0);
a[i] = tmp;
}
montMul(key, aR, a, key->rr); /* aR = a * RR / R mod M */
for (i = 0; i < 16; i+=2) {
montMul(key, aaR, aR, aR); /* aaR = aR * aR / R mod M */
montMul(key, aR, aaR, aaR); /* aR = aaR * aaR / R mod M */
}
montMul(key, aaa, aR, a); /* aaa = aR * a / R mod M */
/* Make sure aaa < mod; aaa is at most 1x mod too large. */
if (vb2_mont_ge(key, aaa)) {
subM(key, aaa);
}
/* Convert to bigendian byte array */
for (i = ARRSIZE4096 - 1; i >= 0; --i) {
uint32_t tmp = aaa[i];
*inout++ = (uint8_t)(tmp >> 24);
*inout++ = (uint8_t)(tmp >> 16);
*inout++ = (uint8_t)(tmp >> 8);
*inout++ = (uint8_t)(tmp >> 0);
}
}
int bdb_rsa4096_verify(const uint8_t *key_data,
const uint8_t *sig,
const uint8_t *digest)
{
const uint32_t *kdata32 = (const uint32_t *)key_data;
struct public_key key;
uint8_t sig_work[BDB_RSA4096_SIG_SIZE];
uint32_t pad_size;
int rv;
/* Unpack key */
if (kdata32[0] != ARRSIZE4096)
return BDB_ERROR_DIGEST; /* Wrong key size */
key.arrsize = kdata32[0];
key.n0inv = kdata32[1];
key.n = kdata32 + 2;
key.rr = kdata32 + 2 + key.arrsize;
/* Copy signature to work buffer */
memcpy(sig_work, sig, sizeof(sig_work));
modpowF4(&key, sig_work);
/*
* Check padding. Continue on to check the digest even if error to
* reduce the risk of timing based attacks.
*/
pad_size = key.arrsize * sizeof(uint32_t) - BDB_SHA256_DIGEST_SIZE;
rv = vb2_check_padding(sig_work, &key, pad_size);
/*
* Check digest. Even though there are probably no timing issues here,
* use vb2_safe_memcmp() just to be on the safe side. (That's also why
* we don't return before this check if the padding check failed.)
*/
if (vb2_safe_memcmp(sig_work + pad_size, digest,
BDB_SHA256_DIGEST_SIZE))
rv = BDB_ERROR_DIGEST;
return rv;
}
/* Array size for RSA3072B */
#define ARRSIZE3072B (3072 / 32)
/**
* In-place public exponentiation. (exponent 3, key size 3072 bits)
*
* @param key Key to use in signing
* @param inout Input and output big-endian byte array
*/
static void modpow3(const struct public_key *key, uint8_t *inout)
{
uint32_t a[ARRSIZE3072B];
uint32_t aR[ARRSIZE3072B];
uint32_t aaR[ARRSIZE3072B];
uint32_t *aaa = aR; /* Re-use location */
int i;
/* Convert from big endian byte array to little endian word array. */
for (i = 0; i < ARRSIZE3072B; ++i) {
uint32_t tmp =
(inout[((ARRSIZE3072B - 1 - i) * 4) + 0] << 24) |
(inout[((ARRSIZE3072B - 1 - i) * 4) + 1] << 16) |
(inout[((ARRSIZE3072B - 1 - i) * 4) + 2] << 8) |
(inout[((ARRSIZE3072B - 1 - i) * 4) + 3] << 0);
a[i] = tmp;
}
montMul(key, aR, a, key->rr); /* aR = a * RR / R mod M */
montMul(key, aaR, aR, aR); /* aaR = aR * aR / R mod M */
montMul(key, aaa, aaR, a); /* aaa = aaR * a / R mod M */
/* Make sure aaa < mod; aaa is at most 1x mod too large. */
if (vb2_mont_ge(key, aaa)) {
subM(key, aaa);
}
/* Convert to bigendian byte array */
for (i = ARRSIZE3072B - 1; i >= 0; --i) {
uint32_t tmp = aaa[i];
*inout++ = (uint8_t)(tmp >> 24);
*inout++ = (uint8_t)(tmp >> 16);
*inout++ = (uint8_t)(tmp >> 8);
*inout++ = (uint8_t)(tmp >> 0);
}
}
int bdb_rsa3072b_verify(const uint8_t *key_data,
const uint8_t *sig,
const uint8_t *digest)
{
const uint32_t *kdata32 = (const uint32_t *)key_data;
struct public_key key;
uint8_t sig_work[BDB_RSA3072B_SIG_SIZE];
uint32_t pad_size;
int rv;
/* Unpack key */
if (kdata32[0] != ARRSIZE3072B)
return BDB_ERROR_DIGEST; /* Wrong key size */
key.arrsize = kdata32[0];
key.n0inv = kdata32[1];
key.n = kdata32 + 2;
key.rr = kdata32 + 2 + key.arrsize;
/* Copy signature to work buffer */
memcpy(sig_work, sig, sizeof(sig_work));
modpow3(&key, sig_work);
/*
* Check padding. Continue on to check the digest even if error to
* reduce the risk of timing based attacks.
*/
pad_size = key.arrsize * sizeof(uint32_t) - BDB_SHA256_DIGEST_SIZE;
rv = vb2_check_padding(sig_work, &key, pad_size);
/*
* Check digest. Even though there are probably no timing issues here,
* use vb2_safe_memcmp() just to be on the safe side. (That's also why
* we don't return before this check if the padding check failed.)
*/
if (vb2_safe_memcmp(sig_work + pad_size, digest,
BDB_SHA256_DIGEST_SIZE))
rv = BDB_ERROR_DIGEST;
return rv;
}

View File

@@ -0,0 +1,210 @@
/* SHA-256 and SHA-512 implementation based on code by Oliver Gay
* <olivier.gay@a3.epfl.ch> under a BSD-style license. See below.
*/
/*
* FIPS 180-2 SHA-224/256/384/512 implementation
* Last update: 02/02/2007
* Issue date: 04/30/2005
*
* Copyright (C) 2005, 2007 Olivier Gay <olivier.gay@a3.epfl.ch>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <string.h>
#include "bdb.h"
#define VB2_SHA256_DIGEST_SIZE 32
#define VB2_SHA256_BLOCK_SIZE 64
struct vb2_sha256_context {
uint32_t h[8];
uint32_t total_size;
uint32_t size;
uint8_t block[2 * VB2_SHA256_BLOCK_SIZE];
};
#define SHFR(x, n) (x >> n)
#define ROTR(x, n) ((x >> n) | (x << ((sizeof(x) << 3) - n)))
#define ROTL(x, n) ((x << n) | (x >> ((sizeof(x) << 3) - n)))
#define CH(x, y, z) ((x & y) ^ (~x & z))
#define MAJ(x, y, z) ((x & y) ^ (x & z) ^ (y & z))
#define SHA256_F1(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22))
#define SHA256_F2(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25))
#define SHA256_F3(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHFR(x, 3))
#define SHA256_F4(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHFR(x, 10))
#define UNPACK32(x, str) \
{ \
*((str) + 3) = (uint8_t) ((x) ); \
*((str) + 2) = (uint8_t) ((x) >> 8); \
*((str) + 1) = (uint8_t) ((x) >> 16); \
*((str) + 0) = (uint8_t) ((x) >> 24); \
}
#define PACK32(str, x) \
{ \
*(x) = ((uint32_t) *((str) + 3) ) \
| ((uint32_t) *((str) + 2) << 8) \
| ((uint32_t) *((str) + 1) << 16) \
| ((uint32_t) *((str) + 0) << 24); \
}
#define SHA256_SCR(i) \
{ \
w[i] = SHA256_F4(w[i - 2]) + w[i - 7] \
+ SHA256_F3(w[i - 15]) + w[i - 16]; \
}
static const uint32_t sha256_h0[8] = {
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
};
static const uint32_t sha256_k[64] = {
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
};
/* SHA-256 implementation from verified boot library */
void vb2_sha256_init(struct vb2_sha256_context *ctx)
{
int i;
for (i = 0; i < 8; i++) {
ctx->h[i] = sha256_h0[i];
}
ctx->size = 0;
ctx->total_size = 0;
}
static void vb2_sha256_transform(struct vb2_sha256_context *ctx,
const uint8_t *message,
unsigned int block_nb)
{
/* Note that these arrays use 72*4=288 bytes of stack */
uint32_t w[64];
uint32_t wv[8];
uint32_t t1, t2;
const unsigned char *sub_block;
int i;
int j;
for (i = 0; i < (int) block_nb; i++) {
sub_block = message + (i << 6);
for (j = 0; j < 16; j++) {
PACK32(&sub_block[j << 2], &w[j]);
}
for (j = 16; j < 64; j++) {
SHA256_SCR(j);
}
for (j = 0; j < 8; j++) {
wv[j] = ctx->h[j];
}
for (j = 0; j < 64; j++) {
t1 = wv[7] + SHA256_F2(wv[4]) + CH(wv[4], wv[5], wv[6])
+ sha256_k[j] + w[j];
t2 = SHA256_F1(wv[0]) + MAJ(wv[0], wv[1], wv[2]);
wv[7] = wv[6];
wv[6] = wv[5];
wv[5] = wv[4];
wv[4] = wv[3] + t1;
wv[3] = wv[2];
wv[2] = wv[1];
wv[1] = wv[0];
wv[0] = t1 + t2;
}
for (j = 0; j < 8; j++) {
ctx->h[j] += wv[j];
}
}
}
void vb2_sha256_update(struct vb2_sha256_context *ctx,
const uint8_t *data,
uint32_t size)
{
unsigned int block_nb;
unsigned int new_size, rem_size, tmp_size;
const uint8_t *shifted_data;
tmp_size = VB2_SHA256_BLOCK_SIZE - ctx->size;
rem_size = size < tmp_size ? size : tmp_size;
memcpy(&ctx->block[ctx->size], data, rem_size);
if (ctx->size + size < VB2_SHA256_BLOCK_SIZE) {
ctx->size += size;
return;
}
new_size = size - rem_size;
block_nb = new_size / VB2_SHA256_BLOCK_SIZE;
shifted_data = data + rem_size;
vb2_sha256_transform(ctx, ctx->block, 1);
vb2_sha256_transform(ctx, shifted_data, block_nb);
rem_size = new_size % VB2_SHA256_BLOCK_SIZE;
memcpy(ctx->block, &shifted_data[block_nb << 6],
rem_size);
ctx->size = rem_size;
ctx->total_size += (block_nb + 1) << 6;
}
void vb2_sha256_finalize(struct vb2_sha256_context *ctx, uint8_t *digest)
{
unsigned int block_nb;
unsigned int pm_size;
unsigned int size_b;
int i;
block_nb = (1 + ((VB2_SHA256_BLOCK_SIZE - 9)
< (ctx->size % VB2_SHA256_BLOCK_SIZE)));
size_b = (ctx->total_size + ctx->size) << 3;
pm_size = block_nb << 6;
memset(ctx->block + ctx->size, 0, pm_size - ctx->size);
ctx->block[ctx->size] = 0x80;
UNPACK32(size_b, ctx->block + pm_size - 4);
vb2_sha256_transform(ctx, ctx->block, block_nb);
for (i = 0 ; i < 8; i++) {
UNPACK32(ctx->h[i], &digest[i << 2]);
}
}
int bdb_sha256(void *digest, const void *buf, size_t size)
{
struct vb2_sha256_context ctx;
vb2_sha256_init(&ctx);
vb2_sha256_update(&ctx, buf, size);
vb2_sha256_finalize(&ctx, digest);
return BDB_SUCCESS;
}

View File

@@ -0,0 +1 @@
This is a pretend ap-rw.bin image. Exciting.

View File

@@ -0,0 +1 @@
This is some OEM0 data.

View File

@@ -0,0 +1 @@
This is some OEM1 data of some sort

View File

@@ -0,0 +1 @@
This is a pretend sp-rw.bin image.

View File

@@ -0,0 +1,33 @@
-----BEGIN CERTIFICATE-----
MIIFtTCCA52gAwIBAgIJANitnQKymb5VMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGQwHhcNMTAwOTI5MTgxNjM4WhcNMTAxMDI5MTgxNjM4WjBF
MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
CgKCAgEAm5v71oqFynujT4FVq5lKaYxpmKfXdeBNKDmLzgu7fXLUKaEqTGEDsseE
5qyaaP+dmTnQKfne7G31zgf46//YEl+u5Gt/S4oAgYyvs3rjymzD5kVOLEAzgrIX
AwyhDFARRzAFWos43hypunHGvu4fDBAzZ3zGVulhjgAzD/gNjToVYCP7bj6kTaDx
1u9siCKdYN09vGwSUt9WuV+yort7kns/B8ArVxt3bFSjsAxuWel/dJyLwCMQ9XAx
dgWpg3RBUsK/KgekQybPLrhLYJn1AeOApwzJ4HoJSqU/1jCEaGrKA/KtCRXiurZz
6lBi7sElsigjBvEZH0iCmmRgH3Oi/cbpHIs1C6YHvCCbO90ntwgtDf0+2WJtFtbG
t5Do3CXri0tcsXBWqISSK3VzzjHH691BVwLuoBvF1XICMEjmq9aJ+MdbEe4E+GU8
TV9NnRnuYyOUoxeisyXiArUUI9+1qL6pIgulTlY2Ch51QZY5n2aYY97PtosNotbS
ylMrLvWXGiiQWxux12eOnB3c/3wNYWey8Km4cmOhEOYz7hLz2r1uIoC/SzM5wLnn
TEQmaiUDNV9R3Gj3E3xkpTq3UNSSPsV7k8lInMtWqzps6aTvBw1k6i6CUvWbEZqm
t/0bimQHOEdg3OrJjQpwTKSp4ouSyVu0IphDwy1yjKCfNWKRzrUCAwEAAaOBpzCB
pDAdBgNVHQ4EFgQUyBKBgFg+vONV1sbup7QtFa7DR78wdQYDVR0jBG4wbIAUyBKB
gFg+vONV1sbup7QtFa7DR7+hSaRHMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpT
b21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGSCCQDY
rZ0Cspm+VTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4ICAQA0wtlLEAKR
ctB41x/V10SMFIg5eLbDrUKQQT33BddrhFu0blc7U5jgXjqTYS80xIlOC0hXtN7D
Z478st3NAxjtvBKxNMWB9Ppz6+15UENnXNGLElhRPaeAbxBs7zVB64b8fY69EJRe
JOJNp6+c4WJsHWzxrmfHD0Wx18pJ877ThRi/ZH0QP2TjPc0gZT4szP1taoOJ7SXy
gO10WfPoF1GgI/VXhPLnk2zXpTlFdp+qyKOtDFxOOK/cVKdXAxDDDO9DAw6cvrEn
mPS2Zml9HI25/CrE00y+k4w7bqzNeGNzhSGPBvq5Yqnefc1dJSdDQZ3XLG9Fis4a
nVfuSTvP1MUrFEGEvuxRcA0rWPwQtYSHHs8ZnpT6eayTPcpDvWSihe4xUywirXTT
kbWgeABGQGaoAnFJYhjqBROGdVb4V3vbsjbCi8k2r4IIcqOzp6OIJxha2LvkZ+iu
f+OlMVAO/C1LbRsVQkfJp7NxEt6PVewQV5Kgnwlf+x7Q2tUfZfdpLd/EMtojv3BD
Ewx5X2yHGXcYZG/C1kNzyGTfg97/+55mtNlkTmo8elcPxlpnEuMXEv4JthnRy90x
ZLflcR9q0pOiV+n//KyQvfjH99JmRtVJGG8xlDEtRbJWjFQD/uSEBxeS0T6INrza
0WTaiIOZB1vMPe6CDYDWDzrFdQrD6HoWDQ==
-----END CERTIFICATE-----

Binary file not shown.

View File

@@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
MIIJKQIBAAKCAgEAm5v71oqFynujT4FVq5lKaYxpmKfXdeBNKDmLzgu7fXLUKaEq
TGEDsseE5qyaaP+dmTnQKfne7G31zgf46//YEl+u5Gt/S4oAgYyvs3rjymzD5kVO
LEAzgrIXAwyhDFARRzAFWos43hypunHGvu4fDBAzZ3zGVulhjgAzD/gNjToVYCP7
bj6kTaDx1u9siCKdYN09vGwSUt9WuV+yort7kns/B8ArVxt3bFSjsAxuWel/dJyL
wCMQ9XAxdgWpg3RBUsK/KgekQybPLrhLYJn1AeOApwzJ4HoJSqU/1jCEaGrKA/Kt
CRXiurZz6lBi7sElsigjBvEZH0iCmmRgH3Oi/cbpHIs1C6YHvCCbO90ntwgtDf0+
2WJtFtbGt5Do3CXri0tcsXBWqISSK3VzzjHH691BVwLuoBvF1XICMEjmq9aJ+Mdb
Ee4E+GU8TV9NnRnuYyOUoxeisyXiArUUI9+1qL6pIgulTlY2Ch51QZY5n2aYY97P
tosNotbSylMrLvWXGiiQWxux12eOnB3c/3wNYWey8Km4cmOhEOYz7hLz2r1uIoC/
SzM5wLnnTEQmaiUDNV9R3Gj3E3xkpTq3UNSSPsV7k8lInMtWqzps6aTvBw1k6i6C
UvWbEZqmt/0bimQHOEdg3OrJjQpwTKSp4ouSyVu0IphDwy1yjKCfNWKRzrUCAwEA
AQKCAgEAlbfvBu0g7UEoUEbQdtp2jjdbIlXbKL83fYxgx07ihkEFgUhfuj1doZX2
eTt5Fa1bpSHK95hCtJjX9/QTvH3dF1CYpY4IXFXbRspmAvoqUYl0swnbvRfId+eB
3J06Fu6ysRuzCvsJLCvH4mu2Hd5eYOz1iIy1CMpj4oyulJ7F6ywHhQkZ0WjUDRzd
kz+p3RHw+lHkJHaW6sWYW6OH7KsWqkmKy5pKGPWEYebN14UeZ8QRrdExZRxYJM5d
yICKKMCiWU6nP3k6wqGElh8b50Y6RibukcvsMN86MWftk9f6jbyxwjqr4iH8lEkY
HkpZ5f5QlqmnifZPhZnujz4kfh50oteC2QPQ0hrNYCDG75wuiNX/vINVfrKG0ddg
iQDFqyQyQirxCGQgy7Wto08KAzKt146ST28N+kdF/kY14ou5f5+GlWQJcnqdHd2p
R25MueXUsY3I63dULR6k02Y6M7Tzo39lYe0LV82+G0A3iGpI+eM7xw/sQDNb2sQs
jCcz7XPrfTomrVJaW1FkM8vM6eWhuhAyDFP+unz0aMnKrkUrarh4t9QpriiCjm3E
HV2Hc7t/Do/w+B3rywKy3PE2yO49eGz20um0JqWcAbGDZY2vDnyV+/xibxqaIZUo
saI/btlyvCv00812momkX/qWwS+1GHvyYYcpIg0XQbZY1TvEi8ECggEBAM6LTfVu
MKNwW/QdZ6pxKl/Oy8zlb1o8HET5hKCdhoMvpwlvpO2qSvlCxH3VZTmcXIXd+Mkd
e4OZrzeMLVxMd64xP10k2ui/O2/8G38xmpMGqZihc+LnY6JgajujfAQHljOgrAJL
xzO2Gk4oWX72oA6jqP8LZkRp/9acTWqBTKs6MOdrfn6I3k0urBB29+jcbqFAfgMx
hfcTKAOHYmg/SeEZDvKP6fRDJGMGXqJ4TaBXjsnhNGCjGmuCqJhxxIGCI/AVK10B
CjEboo9vACzNE1/JMxH8aT5up7e+7R/WoiJ5e3jlvSKmcO7KiR27JVsAlZeIddKd
LzG9KKZ8Yla0U3MCggEBAMDefKVTqSPaG7cmAQGtXrbBDLdCWIaT08v+kMw/drlq
NqLD+1ct098iFwRtKaYPERPKqNtxfJdkUMqWELBWV2Sq4Fi+JVXjGOUctP7Atd2x
6NJ9xHqQKQwKUv0/9jN5Oie9sFvsLwPAJNOJej1BrmvPZvc0CoMyOjkmxEhYu3qG
i26ZTSZSCTrbE8eAL0EJdH0gB7Ryuks8O+jEF7eXuZLZyN3AromISJtmLVlMFZ7m
+0sQnZQqwNF+BIrOgO+3R61jjNzCJbFo7frvRIlDSnrbmWp6sYns1cjhZiKCnO78
RgDiaJcuceqsalgBZi8/Fmam2IPeqhvTNg+5alCuWzcCggEAXFjglFmeGZVFJ9J1
5TkPzyJw8L2smdXCdfxyFjYYTFNkBc4LGdBIEUaPAAwHZEjK/XePoqwx61cthlKA
fYIbCKEwSX8O+X13H8zCpo4RJKeX8IxPeiYm4BTnqp6f9lVGDPNLtQMYn8BN5qAX
07KFQcZe6xm3seMK5nOgEXyaQPyVnQLs3bpoWm4BtKLcmRrlw+dH8DmWQjAoddt0
XlPdvm0rx7wcyH+0pynT6iSL4KMFTrIIbyS9zU/v/ajwSU9crh1o8/5hBi/q8OKa
W22dufgFg4ctryJejsMo1lFq0KssT5O4iuOMHtgjkk14mEWcnNIAjBiHX1/J6xY2
Cbo6jQKCAQBtvmt4e1kz8Ehy92n9NVQ+cyy0HklXEkiiu9BSmA4LRPefuBqNKaN0
ROaJ+z+GoO4br+ZTL4kwb8FU9Py8CfUib+TGOjPuYhFpVONcTfVuF2yeUTf6cYsZ
sco1Fi8WbPV9ZX8zXvoFjVCnGYP31SbVa6dwJCmTK4JbwMZRUEQlXOd74Dk5A9cC
qWPg0fyRajrhc9dOgzWj17tTIDlKm0fZ2phkLd5inayK2CIXvKZUy6PTu7medJFQ
4v7cqNJPFJ/xdkLR3psqDsXTUlBSNnrr24a5QuVA0QV4j2DZZC6+Acgneqz+0Uu6
t66vMuSdH620bV2n84wh1xXc7qkjDYMTAoIBAQC6DsTyBGNNI0/DGwAsae5Zri8w
T/SOER7Tc/PCgQyFUNsJJc/OmSy66PPiH2HzqLjl6/jeiJP++oCnfO6pNTq1Fjz4
Le2iS1szlcuJ9QLdtn2LTqORzdQVpka42X+o+NqJEdzkZb/N6eBA4PPQdTxHIiu1
WGBpDc5vGkpuzLm9SVCw/4SD84z+Nhs0pqOvwWhmQWCtl28fgqU4LMeOX1Wz5P8E
IledlgbCZh2KwXuv3BJdkawuwrSPsahnZmoJapx2dE+FkNl4equaBwImfLf5Qifj
IhIN5GueO9k/D2/7/XvW2qJ3Vy0z0xMMNiTVYufVpbh77Kn2ebKfROlkzMEU
-----END RSA PRIVATE KEY-----

View File

@@ -0,0 +1,26 @@
-----BEGIN CERTIFICATE-----
MIIEWzCCAsOgAwIBAgIJAKOPQrNCNRSqMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGQwHhcNMTUxMTIzMjA1MTI4WhcNMTUxMjIzMjA1MTI4WjBF
MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBoDANBgkqhkiG9w0BAQEFAAOCAY0AMIIB
iAKCAYEA1/JAfpirBAFZdm+m6AcX5QQt7vWDV6OAN4T5nWxAtedRb0XLeV9ICJJI
4CwxWKhvM4pe8pUwwldjoIHIs7viDjBnFVO6+41Gwdsp54AO9qpd//izjarwrOFq
QcZIn7KVOaYxTlMWlfIDOS3K0rr2adBuR/Yq4Nj61XevNsxRelojDLAQeamRia8t
hB3vR1LHQJlMT7R8VXy3AOHx+YcYKUFCgf5A7J+pdAaOIgkFKuakZZNDC+7D0Ykq
jPHadHKzT1SCJxILD/YkCUCOn5bdh7oQekCnhJgf3bwPRDaGEsJL3nnxhc8hx3tI
7DHwKmz9naz63S41Zqd+VvWam7Jttyb6lQjj7Y8TauXGAL3vEiI/u0iiVyw6b3c3
77/W4RWuRIiMAn6FAkUY6oxKB+67ZA1PoRLfiIxFGot1KEpTeLzdsyYx7fB1Scj+
DDzx3EaHQAz12E4anTozljvdAPMoZ6i9Dar3eZvA0KFsM5sdNy0Hp96PBeBNiw1O
XZYUrYgVAgEDo1AwTjAdBgNVHQ4EFgQUiwtVmJWqyIBZWV1xvn7iMbLeE30wHwYD
VR0jBBgwFoAUiwtVmJWqyIBZWV1xvn7iMbLeE30wDAYDVR0TBAUwAwEB/zANBgkq
hkiG9w0BAQsFAAOCAYEAJXgsYoQ7QDNf1GF0bQQKnSqIKMssecD8x66rrJQr1Yon
biJeUfN9N/pAca7CI/vjeTC1w6BlXEbUDNNwLKQYVfTOBdmbW1qDANMUP05PaiAG
cZHVKMZZrR/5+z+LWG157cP7HzHPGfw78LopVXUDZmd5fRD2d7MnqYCrswp/dORM
brHTwzpPhH75uNQmq6w2RowjrntDnKhGT0tSY57/OI+Gke3ch3XPPg3juhREaVUm
4ZXSCwajKmPBju+7adT46gY/LjAYv/rAEiUN5YHOBVHxHdoMOIqsf6VfPVc8USpg
5fsV7NWGNSuvpiA7xBIBKai3dZl8nztFHvjn7z4x6XrqQ1KTMBvnGONHAHopeUtj
a3WPd3GpBmLMpSfDWtcaSSuwOAgZro6vGqcHN4FybKASbtS0RynPRL42DBbLiarn
nA0hhR1YR03Kqc6hexrsg7zrjcNL6b8dzu6o+VxD30ecj68D7IliORtouJahvk5/
F8MyzvHvAa+K7Hb/IcJv
-----END CERTIFICATE-----

Binary file not shown.

View File

@@ -0,0 +1,39 @@
-----BEGIN RSA PRIVATE KEY-----
MIIG4wIBAAKCAYEA1/JAfpirBAFZdm+m6AcX5QQt7vWDV6OAN4T5nWxAtedRb0XL
eV9ICJJI4CwxWKhvM4pe8pUwwldjoIHIs7viDjBnFVO6+41Gwdsp54AO9qpd//iz
jarwrOFqQcZIn7KVOaYxTlMWlfIDOS3K0rr2adBuR/Yq4Nj61XevNsxRelojDLAQ
eamRia8thB3vR1LHQJlMT7R8VXy3AOHx+YcYKUFCgf5A7J+pdAaOIgkFKuakZZND
C+7D0YkqjPHadHKzT1SCJxILD/YkCUCOn5bdh7oQekCnhJgf3bwPRDaGEsJL3nnx
hc8hx3tI7DHwKmz9naz63S41Zqd+VvWam7Jttyb6lQjj7Y8TauXGAL3vEiI/u0ii
Vyw6b3c377/W4RWuRIiMAn6FAkUY6oxKB+67ZA1PoRLfiIxFGot1KEpTeLzdsyYx
7fB1Scj+DDzx3EaHQAz12E4anTozljvdAPMoZ6i9Dar3eZvA0KFsM5sdNy0Hp96P
BeBNiw1OXZYUrYgVAgEDAoIBgQCP9tWpuxytVjukSm9FWg/uAslJ+QI6bQAlA1ET
nYB5ROD02TJQ6jAFtttAHXY7GvTNBun3DiCBj5fAVoXNJ+wJdZoON9H9CNnWkhvv
qrSkcZP/+yJecfXIlkbWhDBqdw4mbsuJjLm5TAImHoc3J07xNZ7apByV5fyOT8ok
iDZRkWyzIAr7xmEGdMkCvp+E4dorEN2KeFLjqHoAlqFRBLrGK4Gr/tXzFRuirwls
BgNx7xhDt4IH9IKLsMcIoTxNocul86msFzeUDFXM/ruLFa34DBtT935WhGn+18+/
bYA+qM4GoC8N/4sSQtENQ8bX21aUHRs1MIOa82rJEmkCWW+Tgw8kYSSi8gVmd7Ly
QFp4XN17ReaIZZKBYJt/3XamcV2tcapxwqBNiGOLPQM4wnnuinkiZD7rXf1u/NFV
rHiVFu1+ORZaTX34+Vq9/wdj18E3cm1ghQsN3BQNzCE1qtulDcs5w2O1iZlP29wq
mtgeU7YHK7CzqNuUiPJWJardCWsCgcEA8VAXoPMSAJByCk3l92eDTOe8n5IMZ307
qXpGMlSUT+SctpgVdoN9iV+7bUQE636Hn37E5czANNpERIoVjFJRm0ioYW/9jOEN
B8zPnvwtLN1vVD1u5XNlumDGWGU1bQCdjgA07DtCM0S5xfhWOACCUJ4l4TDE3DAz
eNfnaXXP8UNfpL6gaWb/xZZwwTesS+hcX8zw14Gzn/GHkyGEtSMA+lezI5C37bzk
ao2pWr+W26HeDPwdG/38gBQceUujidJ9AoHBAOUW7AP8JbFTMUt0j1eO1UbAKty7
XZtURTX+EXK9sWTgeh3xlXpMU6K3U+sIQPsldCACjSeYr8lgGeTP54vZ9L6Zu30H
L2xC/kllafZhOjC5hC4iWaUgePMFiFeOb3prBDJd12ufUlqzydO4bvrKgi2fdAxL
vEtPFXs4U75RzqfXGdER7/0VOI68hS4GunqaiQ0UYPAE1mh+je5oJntP3fW8WRN1
KfuQdm5J+JjzQi4NmJAg6NxlB6wrxmMR8NgneQKBwQCg4A/AogwAYEwG3plPmleI
mn2/trLvqNJw/C7MOGLf7b3PEA5PAlOw6nzzgq3yVFpqVIND3dV4kYLYXA5djDZn
hcWWSqkIlgiv3d+/UsjIk5+NfknuTO58QIQ67iOeAGkJVXidfNbM2HvZUDl6qwGL
FBlAyy3oICJQj++bo9/2LOpt1Grw71UuZEsrenLdRZLqiKCPq80VS6+3a63OF1X8
OndtCyVJKJhHCRuR1Q89FpQIqBNn/qhVYr2mMm0GjFMCgcEAmLnyrVLDy4zLh6MK
Ol842dVx6HzpEjguI/62TH52Q0BRaUu4/DLibHo38gWAp25NaqxeGmXKhkARQzVF
B+ajKbvSU1ofnYH+25jxTut8IHutdBbmbhWl91kFj7RKUZytduk6R7+MPHfb4nr0
pzGsHmpNXYfS3N9jp3rifuE0b+S74Laf/g4ltH2uHq8m/GcGCLhAoAM5mv8JSZrE
UjU+o9LmDPjGp7WkSYalu0zWyV5ltWtF6ENacsfZl2FLOsT7AoHARP9ZNA7JMYMs
dbx2l7eLaTtdOeeSE9AxssmzXBsNHWklEx8DTI3MAFcIXPNesqnm++D/T5sXsIy6
kd3srldHGYHoh1IT5mOO7S8SlKLdYTIlsVIg+I4jpqmHTgk7DWDDpjYzkmJz3A29
q4HHmNx92SXNZD+mX0GnDdP9XDbT3tbt3ANuR1LoTcI3wAUMTN/NQiF8qj2NHzb1
CFOpBOJ9cuUgkWcntjqLJ6mUAMcQ8kF7Pyuhn46qDhY5ceVhrPt9
-----END RSA PRIVATE KEY-----

View File

@@ -0,0 +1,97 @@
/* 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.
*
* Utility for ChromeOS-specific GPT partitions, Please see corresponding .c
* files for more details.
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <uuid/uuid.h>
#include "cgpt.h"
#include "vboot_host.h"
const char* progname;
int GenerateGuid(Guid *newguid)
{
/* From libuuid */
uuid_generate(newguid->u.raw);
return CGPT_OK;
}
struct {
const char *name;
int (*fp)(int argc, char *argv[]);
const char *comment;
} cmds[] = {
{"create", cmd_create, "Create or reset GPT headers and tables"},
{"add", cmd_add, "Add, edit or remove a partition entry"},
{"show", cmd_show, "Show partition table and entries"},
{"repair", cmd_repair, "Repair damaged GPT headers and tables"},
{"boot", cmd_boot, "Edit the PMBR sector for legacy BIOSes"},
{"find", cmd_find, "Locate a partition by its GUID"},
{"prioritize", cmd_prioritize,
"Reorder the priority of all kernel partitions"},
{"legacy", cmd_legacy, "Switch between GPT and Legacy GPT"},
};
void Usage(void) {
int i;
printf("\nUsage: %s COMMAND [OPTIONS] DRIVE\n\n"
"Supported COMMANDs:\n\n",
progname);
for (i = 0; i < sizeof(cmds)/sizeof(cmds[0]); ++i) {
printf(" %-15s %s\n", cmds[i].name, cmds[i].comment);
}
printf("\nFor more detailed usage, use %s COMMAND -h\n\n", progname);
}
int main(int argc, char *argv[]) {
int i;
int match_count = 0;
int match_index = 0;
char* command;
progname = strrchr(argv[0], '/');
if (progname)
progname++;
else
progname = argv[0];
if (argc < 2) {
Usage();
return CGPT_FAILED;
}
// increment optind now, so that getopt skips argv[0] in command function
command = argv[optind++];
// Find the command to invoke.
for (i = 0; command && i < sizeof(cmds)/sizeof(cmds[0]); ++i) {
// exact match?
if (0 == strcmp(cmds[i].name, command)) {
match_index = i;
match_count = 1;
break;
}
// unique match?
else if (0 == strncmp(cmds[i].name, command, strlen(command))) {
match_index = i;
match_count++;
}
}
if (match_count == 1)
return cmds[match_index].fp(argc, argv);
// Couldn't find a single matching command.
Usage();
return CGPT_FAILED;
}

View File

@@ -0,0 +1,222 @@
// Copyright (c) 2012 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.
#ifndef VBOOT_REFERENCE_UTILITY_CGPT_CGPT_H_
#define VBOOT_REFERENCE_UTILITY_CGPT_CGPT_H_
#include <fcntl.h>
#ifndef HAVE_MACOS
#include <features.h>
#endif
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include "cgpt_endian.h"
#include "cgptlib.h"
#include "gpt.h"
struct legacy_partition {
uint8_t status;
uint8_t f_head;
uint8_t f_sect;
uint8_t f_cyl;
uint8_t type;
uint8_t l_head;
uint8_t l_sect;
uint8_t l_cyl;
uint32_t f_lba;
uint32_t num_sect;
} __attribute__((packed));
// syslinux uses this format:
struct pmbr {
uint8_t bootcode[424];
Guid boot_guid;
uint32_t disk_id;
uint8_t magic[2]; // 0x1d, 0x9a
struct legacy_partition part[4];
uint8_t sig[2]; // 0x55, 0xaa
} __attribute__((packed));
void PMBRToStr(struct pmbr *pmbr, char *str, unsigned int buflen);
// Handle to the drive storing the GPT.
struct drive {
uint64_t size; /* total size (in bytes) */
GptData gpt;
struct pmbr pmbr;
int fd; /* file descriptor */
};
// Opens a block device or file, loads raw GPT data from it.
// 'mode' should be O_RDONLY or O_RDWR.
// If 'drive_size' is 0, both the partitions and GPT structs reside on the same
// 'drive_path'.
// Otherwise, 'drive_size' is taken as the size of the device that all
// partitions will reside on, and 'drive_path' is where we store GPT structs.
//
// Returns CGPT_FAILED if any error happens.
// Returns CGPT_OK if success and information are stored in 'drive'. */
int DriveOpen(const char *drive_path, struct drive *drive, int mode,
uint64_t drive_size);
int DriveClose(struct drive *drive, int update_as_needed);
int CheckValid(const struct drive *drive);
/* Loads sectors from 'drive'.
* *buf is pointed to an allocated memory when returned, and should be
* freed.
*
* drive -- open drive.
* buf -- pointer to buffer pointer
* sector -- offset of starting sector (in sectors)
* sector_bytes -- bytes per sector
* sector_count -- number of sectors to load
*
* Returns CGPT_OK for successful. Aborts if any error occurs.
*/
int Load(struct drive *drive, uint8_t **buf,
const uint64_t sector,
const uint64_t sector_bytes,
const uint64_t sector_count);
/* Saves sectors to 'drive'.
*
* drive -- open drive
* buf -- pointer to buffer
* sector -- starting sector offset
* sector_bytes -- bytes per sector
* sector_count -- number of sector to save
*
* Returns CGPT_OK for successful, CGPT_FAILED for failed.
*/
int Save(struct drive *drive, const uint8_t *buf,
const uint64_t sector,
const uint64_t sector_bytes,
const uint64_t sector_count);
/* GUID conversion functions. Accepted format:
*
* "C12A7328-F81F-11D2-BA4B-00A0C93EC93B"
*
* At least GUID_STRLEN bytes should be reserved in 'str' (included the tailing
* '\0').
*/
#define GUID_STRLEN 37
int StrToGuid(const char *str, Guid *guid);
void GuidToStr(const Guid *guid, char *str, unsigned int buflen);
int GuidEqual(const Guid *guid1, const Guid *guid2);
int IsZero(const Guid *guid);
/* Constant global type values to compare against */
extern const Guid guid_chromeos_firmware;
extern const Guid guid_chromeos_kernel;
extern const Guid guid_chromeos_rootfs;
extern const Guid guid_linux_data;
extern const Guid guid_chromeos_reserved;
extern const Guid guid_efi;
extern const Guid guid_unused;
int ReadPMBR(struct drive *drive);
int WritePMBR(struct drive *drive);
/* Convert possibly unterminated UTF16 string to UTF8.
* Caller must prepare enough space for UTF8, which could be up to
* twice the byte length of UTF16 string plus the terminating '\0'.
*
* Return: CGPT_OK --- all character are converted successfully.
* CGPT_FAILED --- convert error, i.e. output buffer is too short.
*/
int UTF16ToUTF8(const uint16_t *utf16, unsigned int maxinput,
uint8_t *utf8, unsigned int maxoutput);
/* Convert null-terminated UTF8 string to UTF16.
* Caller must prepare enough space for UTF16, which is the byte length of UTF8
* plus the terminating 0x0000.
*
* Return: CGPT_OK --- all character are converted successfully.
* CGPT_FAILED --- convert error, i.e. output buffer is too short.
*/
int UTF8ToUTF16(const uint8_t *utf8, uint16_t *utf16, unsigned int maxoutput);
/* Helper functions for supported GPT types. */
int ResolveType(const Guid *type, char *buf);
int SupportedType(const char *name, Guid *type);
void PrintTypes(void);
void EntryDetails(GptEntry *entry, uint32_t index, int raw);
uint32_t GetNumberOfEntries(const struct drive *drive);
GptEntry *GetEntry(GptData *gpt, int secondary, uint32_t entry_index);
void SetRequired(struct drive *drive, int secondary, uint32_t entry_index,
int required);
int GetRequired(struct drive *drive, int secondary, uint32_t entry_index);
void SetLegacyBoot(struct drive *drive, int secondary, uint32_t entry_index,
int legacy_boot);
int GetLegacyBoot(struct drive *drive, int secondary, uint32_t entry_index);
void SetPriority(struct drive *drive, int secondary, uint32_t entry_index,
int priority);
int GetPriority(struct drive *drive, int secondary, uint32_t entry_index);
void SetTries(struct drive *drive, int secondary, uint32_t entry_index,
int tries);
int GetTries(struct drive *drive, int secondary, uint32_t entry_index);
void SetSuccessful(struct drive *drive, int secondary, uint32_t entry_index,
int success);
int GetSuccessful(struct drive *drive, int secondary, uint32_t entry_index);
void SetRaw(struct drive *drive, int secondary, uint32_t entry_index,
uint32_t raw);
void UpdateAllEntries(struct drive *drive);
uint8_t RepairHeader(GptData *gpt, const uint32_t valid_headers);
uint8_t RepairEntries(GptData *gpt, const uint32_t valid_entries);
void UpdateCrc(GptData *gpt);
int IsSynonymous(const GptHeader* a, const GptHeader* b);
int IsUnused(struct drive *drive, int secondary, uint32_t index);
int IsKernel(struct drive *drive, int secondary, uint32_t index);
// Optional. Applications that need this must provide an implementation.
//
// Explanation:
// Some external utilities need to manipulate the GPT, but don't create new
// partitions from scratch. The cgpt executable uses libuuid to provide this
// functionality, but we don't want to have to build or install a separate
// instance of that library just for the 32-bit static post-install tool,
// which doesn't need this function.
int GenerateGuid(Guid *newguid);
// For usage and error messages.
void Error(const char *format, ...);
void Warning(const char *format, ...);
// Command functions.
int check_int_parse(char option, const char *buf);
int check_int_limit(char option, int val, int low, int high);
int cmd_show(int argc, char *argv[]);
int cmd_repair(int argc, char *argv[]);
int cmd_create(int argc, char *argv[]);
int cmd_add(int argc, char *argv[]);
int cmd_boot(int argc, char *argv[]);
int cmd_find(int argc, char *argv[]);
int cmd_prioritize(int argc, char *argv[]);
int cmd_legacy(int argc, char *argv[]);
#define ARRAY_COUNT(array) (sizeof(array)/sizeof((array)[0]))
const char *GptError(int errnum);
// Size in chars of the GPT Entry's PartitionName field
#define GPT_PARTNAME_LEN 72
/* The standard "assert" macro goes away when NDEBUG is defined. This doesn't.
*/
#define require(A) do { \
if (!(A)) { \
fprintf(stderr, "condition (%s) failed at %s:%d\n", \
#A, __FILE__, __LINE__); \
exit(1); } \
} while (0)
#endif // VBOOT_REFERENCE_UTILITY_CGPT_CGPT_H_

View File

@@ -0,0 +1,332 @@
// Copyright (c) 2012 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.
#include <string.h>
#include "cgpt.h"
#include "cgpt_params.h"
#include "cgptlib_internal.h"
#include "utility.h"
#include "vboot_host.h"
static const char* DumpCgptAddParams(const CgptAddParams *params) {
static char buf[256];
char tmp[64];
buf[0] = 0;
snprintf(tmp, sizeof(tmp), "-i %d ", params->partition);
StrnAppend(buf, tmp, sizeof(buf));
if (params->label) {
snprintf(tmp, sizeof(tmp), "-l %s ", params->label);
StrnAppend(buf, tmp, sizeof(buf));
}
if (params->set_begin) {
snprintf(tmp, sizeof(tmp), "-b %llu ", (unsigned long long)params->begin);
StrnAppend(buf, tmp, sizeof(buf));
}
if (params->set_size) {
snprintf(tmp, sizeof(tmp), "-s %llu ", (unsigned long long)params->size);
StrnAppend(buf, tmp, sizeof(buf));
}
if (params->set_type) {
GuidToStr(&params->type_guid, tmp, sizeof(tmp));
StrnAppend(buf, "-t ", sizeof(buf));
StrnAppend(buf, tmp, sizeof(buf));
StrnAppend(buf, " ", sizeof(buf));
}
if (params->set_unique) {
GuidToStr(&params->unique_guid, tmp, sizeof(tmp));
StrnAppend(buf, "-u ", sizeof(buf));
StrnAppend(buf, tmp, sizeof(buf));
StrnAppend(buf, " ", sizeof(buf));
}
if (params->set_successful) {
snprintf(tmp, sizeof(tmp), "-S %d ", params->successful);
StrnAppend(buf, tmp, sizeof(buf));
}
if (params->set_tries) {
snprintf(tmp, sizeof(tmp), "-T %d ", params->tries);
StrnAppend(buf, tmp, sizeof(buf));
}
if (params->set_priority) {
snprintf(tmp, sizeof(tmp), "-P %d ", params->priority);
StrnAppend(buf, tmp, sizeof(buf));
}
if (params->set_required) {
snprintf(tmp, sizeof(tmp), "-R %d ", params->required);
StrnAppend(buf, tmp, sizeof(buf));
}
if (params->set_legacy_boot) {
snprintf(tmp, sizeof(tmp), "-B %d ", params->legacy_boot);
StrnAppend(buf, tmp, sizeof(buf));
}
if (params->set_raw) {
snprintf(tmp, sizeof(tmp), "-A 0x%x ", params->raw_value);
StrnAppend(buf, tmp, sizeof(buf));
}
StrnAppend(buf, "\n", sizeof(buf));
return buf;
}
// This is the implementation-specific helper function.
static int GptSetEntryAttributes(struct drive *drive,
uint32_t index,
CgptAddParams *params) {
GptEntry *entry;
entry = GetEntry(&drive->gpt, PRIMARY, index);
if (params->set_begin)
entry->starting_lba = params->begin;
if (params->set_size)
entry->ending_lba = entry->starting_lba + params->size - 1;
if (params->set_unique) {
memcpy(&entry->unique, &params->unique_guid, sizeof(Guid));
} else if (GuidIsZero(&entry->type)) {
if (CGPT_OK != GenerateGuid(&entry->unique)) {
Error("Unable to generate new GUID.\n");
return -1;
}
}
if (params->set_type)
memcpy(&entry->type, &params->type_guid, sizeof(Guid));
if (params->label) {
if (CGPT_OK != UTF8ToUTF16((uint8_t *)params->label, entry->name,
sizeof(entry->name) / sizeof(entry->name[0]))) {
Error("The label cannot be converted to UTF16.\n");
return -1;
}
}
return 0;
}
// This is an internal helper function which assumes no NULL args are passed.
// It sets the given attribute values for a single entry at the given index.
static int SetEntryAttributes(struct drive *drive,
uint32_t index,
CgptAddParams *params) {
if (params->set_raw) {
SetRaw(drive, PRIMARY, index, params->raw_value);
} else {
if (params->set_successful)
SetSuccessful(drive, PRIMARY, index, params->successful);
if (params->set_tries)
SetTries(drive, PRIMARY, index, params->tries);
if (params->set_priority)
SetPriority(drive, PRIMARY, index, params->priority);
if (params->set_legacy_boot)
SetLegacyBoot(drive, PRIMARY, index, params->legacy_boot);
if (params->set_required)
SetRequired(drive, PRIMARY, index, params->required);
}
// New partitions must specify type, begin, and size.
if (IsUnused(drive, PRIMARY, index)) {
if (!params->set_begin || !params->set_size || !params->set_type) {
Error("-t, -b, and -s options are required for new partitions\n");
return -1;
}
if (GuidIsZero(&params->type_guid)) {
Error("New partitions must have a type other than \"unused\"\n");
return -1;
}
}
return 0;
}
static int CgptCheckAddValidity(struct drive *drive) {
int gpt_retval;
if (GPT_SUCCESS != (gpt_retval = GptSanityCheck(&drive->gpt))) {
Error("GptSanityCheck() returned %d: %s\n",
gpt_retval, GptError(gpt_retval));
return -1;
}
if (CGPT_OK != CheckValid(drive)) {
Error("please run 'cgpt repair' before adding anything.\n");
return -1;
}
return 0;
}
static int CgptGetUnusedPartition(struct drive *drive, uint32_t *index,
CgptAddParams *params) {
uint32_t i;
uint32_t max_part = GetNumberOfEntries(drive);
if (params->partition) {
if (params->partition > max_part) {
Error("invalid partition number: %d\n", params->partition);
return -1;
}
*index = params->partition - 1;
return 0;
} else {
// Find next empty partition.
for (i = 0; i < max_part; i++) {
if (IsUnused(drive, PRIMARY, i)) {
params->partition = i + 1;
*index = i;
return 0;
}
}
Error("no unused partitions available\n");
return -1;
}
}
int CgptSetAttributes(CgptAddParams *params) {
struct drive drive;
if (params == NULL)
return CGPT_FAILED;
if (CGPT_OK != DriveOpen(params->drive_name, &drive, O_RDWR,
params->drive_size))
return CGPT_FAILED;
if (CgptCheckAddValidity(&drive)) {
goto bad;
}
if (params->partition == 0 ||
params->partition >= GetNumberOfEntries(&drive)) {
Error("invalid partition number: %d\n", params->partition);
goto bad;
}
SetEntryAttributes(&drive, params->partition - 1, params);
UpdateAllEntries(&drive);
// Write it all out.
return DriveClose(&drive, 1);
bad:
DriveClose(&drive, 0);
return CGPT_FAILED;
}
// This method gets the partition details such as the attributes, the
// guids of the partitions, etc. Input is the partition number or the
// unique id of the partition. Output is populated in the respective
// fields of params.
int CgptGetPartitionDetails(CgptAddParams *params) {
struct drive drive;
int result = CGPT_FAILED;
int index;
if (params == NULL)
return CGPT_FAILED;
if (CGPT_OK != DriveOpen(params->drive_name, &drive, O_RDWR,
params->drive_size))
return CGPT_FAILED;
if (CgptCheckAddValidity(&drive)) {
goto bad;
}
int max_part = GetNumberOfEntries(&drive);
if (params->partition > 0) {
if (params->partition >= max_part) {
Error("invalid partition number: %d\n", params->partition);
goto bad;
}
} else {
if (!params->set_unique) {
Error("either partition or unique_id must be specified\n");
goto bad;
}
for (index = 0; index < max_part; index++) {
GptEntry *entry = GetEntry(&drive.gpt, PRIMARY, index);
if (GuidEqual(&entry->unique, &params->unique_guid)) {
params->partition = index + 1;
break;
}
}
if (index >= max_part) {
Error("no partitions with the given unique id available\n");
goto bad;
}
}
index = params->partition - 1;
// GPT-specific code
GptEntry *entry = GetEntry(&drive.gpt, PRIMARY, index);
params->begin = entry->starting_lba;
params->size = entry->ending_lba - entry->starting_lba + 1;
memcpy(&params->type_guid, &entry->type, sizeof(Guid));
memcpy(&params->unique_guid, &entry->unique, sizeof(Guid));
params->raw_value = entry->attrs.fields.gpt_att;
params->successful = GetSuccessful(&drive, PRIMARY, index);
params->tries = GetTries(&drive, PRIMARY, index);
params->priority = GetPriority(&drive, PRIMARY, index);
result = CGPT_OK;
bad:
DriveClose(&drive, 0);
return result;
}
static int GptAdd(struct drive *drive, CgptAddParams *params, uint32_t index) {
GptEntry *entry, backup;
int rv;
entry = GetEntry(&drive->gpt, PRIMARY, index);
memcpy(&backup, entry, sizeof(backup));
if (SetEntryAttributes(drive, index, params) ||
GptSetEntryAttributes(drive, index, params)) {
memcpy(entry, &backup, sizeof(*entry));
return -1;
}
UpdateAllEntries(drive);
rv = CheckEntries((GptEntry*)drive->gpt.primary_entries,
(GptHeader*)drive->gpt.primary_header);
if (0 != rv) {
// If the modified entry is illegal, recover it and return error.
memcpy(entry, &backup, sizeof(*entry));
Error("%s\n", GptErrorText(rv));
Error(DumpCgptAddParams(params));
return -1;
}
return 0;
}
int CgptAdd(CgptAddParams *params) {
struct drive drive;
uint32_t index;
if (params == NULL)
return CGPT_FAILED;
if (CGPT_OK != DriveOpen(params->drive_name, &drive, O_RDWR,
params->drive_size))
return CGPT_FAILED;
if (CgptCheckAddValidity(&drive)) {
goto bad;
}
if (CgptGetUnusedPartition(&drive, &index, params)) {
goto bad;
}
if (GptAdd(&drive, params, index))
goto bad;
// Write it all out.
return DriveClose(&drive, 1);
bad:
DriveClose(&drive, 0);
return CGPT_FAILED;
}

View File

@@ -0,0 +1,152 @@
// Copyright (c) 2012 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.
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include "cgpt.h"
#include "cgpt_params.h"
#include "cgptlib_internal.h"
#include "vboot_host.h"
int CgptGetBootPartitionNumber(CgptBootParams *params) {
struct drive drive;
int gpt_retval= 0;
int retval;
if (params == NULL)
return CGPT_FAILED;
if (CGPT_OK != DriveOpen(params->drive_name, &drive, O_RDONLY,
params->drive_size))
return CGPT_FAILED;
if (GPT_SUCCESS != (gpt_retval = GptSanityCheck(&drive.gpt))) {
Error("GptSanityCheck() returned %d: %s\n",
gpt_retval, GptError(gpt_retval));
retval = CGPT_FAILED;
goto done;
}
if (CGPT_OK != ReadPMBR(&drive)) {
Error("Unable to read PMBR\n");
retval = CGPT_FAILED;
goto done;
}
char buf[GUID_STRLEN];
GuidToStr(&drive.pmbr.boot_guid, buf, sizeof(buf));
int numEntries = GetNumberOfEntries(&drive);
int i;
for(i = 0; i < numEntries; i++) {
GptEntry *entry = GetEntry(&drive.gpt, ANY_VALID, i);
if (GuidEqual(&entry->unique, &drive.pmbr.boot_guid)) {
params->partition = i + 1;
retval = CGPT_OK;
goto done;
}
}
Error("Didn't find any boot partition\n");
params->partition = 0;
retval = CGPT_FAILED;
done:
(void) DriveClose(&drive, 1);
return retval;
}
int CgptBoot(CgptBootParams *params) {
struct drive drive;
int retval = 1;
int gpt_retval= 0;
int mode = O_RDONLY;
if (params == NULL)
return CGPT_FAILED;
if (params->create_pmbr || params->partition || params->bootfile)
mode = O_RDWR;
if (CGPT_OK != DriveOpen(params->drive_name, &drive, mode,
params->drive_size)) {
return CGPT_FAILED;
}
if (CGPT_OK != ReadPMBR(&drive)) {
Error("Unable to read PMBR\n");
goto done;
}
if (params->create_pmbr) {
drive.pmbr.magic[0] = 0x1d;
drive.pmbr.magic[1] = 0x9a;
drive.pmbr.sig[0] = 0x55;
drive.pmbr.sig[1] = 0xaa;
memset(&drive.pmbr.part, 0, sizeof(drive.pmbr.part));
drive.pmbr.part[0].f_head = 0x00;
drive.pmbr.part[0].f_sect = 0x02;
drive.pmbr.part[0].f_cyl = 0x00;
drive.pmbr.part[0].type = 0xee;
drive.pmbr.part[0].l_head = 0xff;
drive.pmbr.part[0].l_sect = 0xff;
drive.pmbr.part[0].l_cyl = 0xff;
drive.pmbr.part[0].f_lba = htole32(1);
uint32_t max = 0xffffffff;
if (drive.gpt.streaming_drive_sectors < 0xffffffff)
max = drive.gpt.streaming_drive_sectors - 1;
drive.pmbr.part[0].num_sect = htole32(max);
}
if (params->partition) {
if (GPT_SUCCESS != (gpt_retval = GptSanityCheck(&drive.gpt))) {
Error("GptSanityCheck() returned %d: %s\n",
gpt_retval, GptError(gpt_retval));
goto done;
}
if (params->partition > GetNumberOfEntries(&drive)) {
Error("invalid partition number: %d\n", params->partition);
goto done;
}
uint32_t index = params->partition - 1;
GptEntry *entry = GetEntry(&drive.gpt, ANY_VALID, index);
memcpy(&drive.pmbr.boot_guid, &entry->unique, sizeof(Guid));
}
if (params->bootfile) {
int fd = open(params->bootfile, O_RDONLY);
if (fd < 0) {
Error("Can't read %s: %s\n", params->bootfile, strerror(errno));
goto done;
}
int n = read(fd, drive.pmbr.bootcode, sizeof(drive.pmbr.bootcode));
if (n < 1) {
Error("problem reading %s: %s\n", params->bootfile, strerror(errno));
close(fd);
goto done;
}
close(fd);
}
char buf[GUID_STRLEN];
GuidToStr(&drive.pmbr.boot_guid, buf, sizeof(buf));
printf("%s\n", buf);
// Write it all out, if needed.
if (mode == O_RDONLY || CGPT_OK == WritePMBR(&drive))
retval = 0;
done:
(void) DriveClose(&drive, 1);
return retval;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,127 @@
// Copyright (c) 2012 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.
#include <string.h>
#include "cgpt.h"
#include "cgptlib_internal.h"
#include "vboot_host.h"
static void AllocAndClear(uint8_t **buf, uint64_t size) {
if (*buf) {
memset(*buf, 0, size);
} else {
*buf = calloc(1, size);
if (!*buf) {
Error("Cannot allocate %u bytes.\n", size);
abort();
}
}
}
static int GptCreate(struct drive *drive, CgptCreateParams *params) {
// Allocate and/or erase the data.
// We cannot assume the GPT headers or entry arrays have been allocated
// by GptLoad() because those fields might have failed validation checks.
AllocAndClear(&drive->gpt.primary_header,
drive->gpt.sector_bytes * GPT_HEADER_SECTORS);
AllocAndClear(&drive->gpt.secondary_header,
drive->gpt.sector_bytes * GPT_HEADER_SECTORS);
drive->gpt.modified |= (GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1 |
GPT_MODIFIED_HEADER2 | GPT_MODIFIED_ENTRIES2);
// Initialize a blank set
if (!params->zap) {
GptHeader *h = (GptHeader *)drive->gpt.primary_header;
memcpy(h->signature, GPT_HEADER_SIGNATURE, GPT_HEADER_SIGNATURE_SIZE);
h->revision = GPT_HEADER_REVISION;
h->size = sizeof(GptHeader);
h->my_lba = GPT_PMBR_SECTORS; /* The second sector on drive. */
h->alternate_lba = drive->gpt.gpt_drive_sectors - GPT_HEADER_SECTORS;
if (CGPT_OK != GenerateGuid(&h->disk_uuid)) {
Error("Unable to generate new GUID.\n");
return -1;
}
/* Calculate number of entries */
h->size_of_entry = sizeof(GptEntry);
h->number_of_entries = MAX_NUMBER_OF_ENTRIES;
if (drive->gpt.flags & GPT_FLAG_EXTERNAL) {
// We might have smaller space for the GPT table. Scale accordingly.
//
// +------+------------+---------------+-----+--------------+-----------+
// | PMBR | Prim. Head | Prim. Entries | ... | Sec. Entries | Sec. Head |
// +------+------------+---------------+-----+--------------+-----------+
//
// Half the size of gpt_drive_sectors must be big enough to hold PMBR +
// GPT Header + Entries Table, though the secondary structures do not
// contain PMBR.
size_t required_headers_size =
(GPT_PMBR_SECTORS + GPT_HEADER_SECTORS) * drive->gpt.sector_bytes;
size_t min_entries_size = MIN_NUMBER_OF_ENTRIES * h->size_of_entry;
size_t required_min_size = required_headers_size + min_entries_size;
size_t half_size =
(drive->gpt.gpt_drive_sectors / 2) * drive->gpt.sector_bytes;
if (half_size < required_min_size) {
Error("Not enough space to store GPT structures. Required %d bytes.\n",
required_min_size * 2);
return -1;
}
size_t max_entries =
(half_size - required_headers_size) / h->size_of_entry;
if (h->number_of_entries > max_entries) {
h->number_of_entries = max_entries;
}
}
/* Then use number of entries to calculate entries_lba. */
h->entries_lba = h->my_lba + GPT_HEADER_SECTORS;
if (!(drive->gpt.flags & GPT_FLAG_EXTERNAL)) {
h->entries_lba += params->padding;
h->first_usable_lba = h->entries_lba + CalculateEntriesSectors(h,
drive->gpt.sector_bytes);
h->last_usable_lba =
(drive->gpt.streaming_drive_sectors - GPT_HEADER_SECTORS -
CalculateEntriesSectors(h, drive->gpt.sector_bytes) - 1);
} else {
h->first_usable_lba = params->padding;
h->last_usable_lba = (drive->gpt.streaming_drive_sectors - 1);
}
size_t entries_size = h->number_of_entries * h->size_of_entry;
AllocAndClear(&drive->gpt.primary_entries, entries_size);
AllocAndClear(&drive->gpt.secondary_entries, entries_size);
// Copy to secondary
RepairHeader(&drive->gpt, MASK_PRIMARY);
UpdateCrc(&drive->gpt);
}
return 0;
}
int CgptCreate(CgptCreateParams *params) {
struct drive drive;
if (params == NULL)
return CGPT_FAILED;
if (CGPT_OK != DriveOpen(params->drive_name, &drive, O_RDWR,
params->drive_size))
return CGPT_FAILED;
if (GptCreate(&drive, params))
goto bad;
// Write it all out
return DriveClose(&drive, 1);
bad:
DriveClose(&drive, 0);
return CGPT_FAILED;
}

View File

@@ -0,0 +1,53 @@
/* 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.
*/
#ifndef VBOOT_REFERENCE_UTILITY_CGPT_ENDIAN_H_
#define VBOOT_REFERENCE_UTILITY_CGPT_ENDIAN_H_
// Newer distros already have this. For those that don't, we add it here.
#ifndef HAVE_MACOS
#include <endian.h>
#endif
#ifndef le16toh
#ifndef HAVE_MACOS
# include <byteswap.h>
#endif
# if __BYTE_ORDER == __LITTLE_ENDIAN
# define htobe16(x) __bswap_16 (x)
# define htole16(x) (x)
# define be16toh(x) __bswap_16 (x)
# define le16toh(x) (x)
# define htobe32(x) __bswap_32 (x)
# define htole32(x) (x)
# define be32toh(x) __bswap_32 (x)
# define le32toh(x) (x)
# define htobe64(x) __bswap_64 (x)
# define htole64(x) (x)
# define be64toh(x) __bswap_64 (x)
# define le64toh(x) (x)
# else
# define htobe16(x) (x)
# define htole16(x) __bswap_16 (x)
# define be16toh(x) (x)
# define le16toh(x) __bswap_16 (x)
# define htobe32(x) (x)
# define htole32(x) __bswap_32 (x)
# define be32toh(x) (x)
# define le32toh(x) __bswap_32 (x)
# define htobe64(x) (x)
# define htole64(x) __bswap_64 (x)
# define be64toh(x) (x)
# define le64toh(x) __bswap_64 (x)
# endif
#endif
#endif // VBOOT_REFERENCE_UTILITY_CGPT_ENDIAN_H_

View File

@@ -0,0 +1,311 @@
// Copyright (c) 2012 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.
#include <ctype.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "cgpt.h"
#include "cgpt_nor.h"
#include "cgptlib_internal.h"
#include "vboot_host.h"
#define BUFSIZE 1024
// fill comparebuf with the data to be examined, returning true on success.
static int FillBuffer(CgptFindParams *params, int fd, uint64_t pos,
uint64_t count) {
uint8_t *bufptr = params->comparebuf;
if (-1 == lseek(fd, pos, SEEK_SET))
return 0;
// keep reading until done or error
while (count) {
ssize_t bytes_read = read(fd, bufptr, count);
// negative means error, 0 means (unexpected) EOF
if (bytes_read <= 0)
return 0;
count -= bytes_read;
bufptr += bytes_read;
}
return 1;
}
// check partition data content. return true for match, 0 for no match or error
static int match_content(CgptFindParams *params, struct drive *drive,
GptEntry *entry) {
uint64_t part_size;
if (!params->matchlen)
return 1;
// Ensure that the region we want to match against is inside the partition.
part_size = drive->gpt.sector_bytes *
(entry->ending_lba - entry->starting_lba + 1);
if (params->matchoffset + params->matchlen > part_size) {
return 0;
}
// Read the partition data.
if (!FillBuffer(params, drive->fd,
(drive->gpt.sector_bytes * entry->starting_lba) + params->matchoffset,
params->matchlen)) {
Error("unable to read partition data\n");
return 0;
}
// Compare it
if (0 == memcmp(params->matchbuf, params->comparebuf, params->matchlen)) {
return 1;
}
// Nope.
return 0;
}
// This needs to handle /dev/mmcblk0 -> /dev/mmcblk0p3, /dev/sda -> /dev/sda3
static void showmatch(CgptFindParams *params, char *filename,
int partnum, GptEntry *entry) {
char * format = "%s%d\n";
/*
* Follow convention from disk_name() in kernel block/partition-generic.c
* code:
* If the last digit of the device name is a number, add a 'p' between the
* device name and the partition number.
*/
if (isdigit(filename[strlen(filename) - 1]))
format = "%sp%d\n";
if (params->numeric) {
printf("%d\n", partnum);
} else {
if (params->show_fn) {
params->show_fn(params, filename, partnum, entry);
} else {
printf(format, filename, partnum);
}
}
if (params->verbose > 0)
EntryDetails(entry, partnum - 1, params->numeric);
}
// This handles the MTD devices. ChromeOS uses /dev/mtdX for kernel partitions,
// /dev/ubiblockX_0 for root partitions, and /dev/ubiX for stateful partition.
static void chromeos_mtd_show(CgptFindParams *params, char *filename,
int partnum, GptEntry *entry) {
if (GuidEqual(&guid_chromeos_kernel, &entry->type)) {
printf("/dev/mtd%d\n", partnum);
} else if (GuidEqual(&guid_chromeos_rootfs, &entry->type)) {
printf("/dev/ubiblock%d_0\n", partnum);
} else {
printf("/dev/ubi%d_0\n", partnum);
}
}
// This returns true if a GPT partition matches the search criteria. If a match
// isn't found (or if the file doesn't contain a GPT), it returns false. The
// filename and partition number that matched is left in a global, since we
// could have multiple hits.
static int gpt_search(CgptFindParams *params, struct drive *drive,
char *filename) {
int i;
GptEntry *entry;
int retval = 0;
char partlabel[GPT_PARTNAME_LEN];
if (GPT_SUCCESS != GptSanityCheck(&drive->gpt)) {
return 0;
}
for (i = 0; i < GetNumberOfEntries(drive); ++i) {
entry = GetEntry(&drive->gpt, ANY_VALID, i);
if (GuidIsZero(&entry->type))
continue;
int found = 0;
if ((params->set_unique && GuidEqual(&params->unique_guid, &entry->unique))
|| (params->set_type && GuidEqual(&params->type_guid, &entry->type))) {
found = 1;
} else if (params->set_label) {
if (CGPT_OK != UTF16ToUTF8(entry->name,
sizeof(entry->name) / sizeof(entry->name[0]),
(uint8_t *)partlabel, sizeof(partlabel))) {
Error("The label cannot be converted from UTF16, so abort.\n");
return 0;
}
if (!strncmp(params->label, partlabel, sizeof(partlabel)))
found = 1;
}
if (found && match_content(params, drive, entry)) {
params->hits++;
retval++;
showmatch(params, filename, i+1, entry);
if (!params->match_partnum)
params->match_partnum = i+1;
}
}
return retval;
}
static int do_search(CgptFindParams *params, char *fileName) {
int retval;
struct drive drive;
if (CGPT_OK != DriveOpen(fileName, &drive, O_RDONLY, params->drive_size))
return 0;
retval = gpt_search(params, &drive, fileName);
(void) DriveClose(&drive, 0);
return retval;
}
#define PROC_MTD "/proc/mtd"
#define PROC_PARTITIONS "/proc/partitions"
#define DEV_DIR "/dev"
#define SYS_BLOCK_DIR "/sys/block"
#define MAX_PARTITION_NAME_LEN 128
static const char *devdirs[] = { "/dev", "/devices", "/devfs", 0 };
// Given basename "foo", see if we can find a whole, real device by that name.
// This is copied from the logic in the linux utility 'findfs', although that
// does more exhaustive searching.
static char *is_wholedev(const char *basename) {
int i;
struct stat statbuf;
static char pathname[BUFSIZE]; // we'll return this.
char tmpname[BUFSIZE];
// It should be a block device under /dev/,
for (i = 0; devdirs[i]; i++) {
sprintf(pathname, "%s/%s", devdirs[i], basename);
if (0 != stat(pathname, &statbuf))
continue;
if (!S_ISBLK(statbuf.st_mode))
continue;
// It should have a symlink called /sys/block/*/device
sprintf(tmpname, "%s/%s/device", SYS_BLOCK_DIR, basename);
if (0 != lstat(tmpname, &statbuf))
continue;
if (!S_ISLNK(statbuf.st_mode))
continue;
// found it
return pathname;
}
return 0;
}
// This scans all the physical devices it can find, looking for a match. It
// returns true if any matches were found, false otherwise.
static int scan_real_devs(CgptFindParams *params) {
int found = 0;
char partname[MAX_PARTITION_NAME_LEN];
char partname_prev[MAX_PARTITION_NAME_LEN];
FILE *fp;
char *pathname;
fp = fopen(PROC_PARTITIONS, "re");
if (!fp) {
perror("can't read " PROC_PARTITIONS);
return found;
}
size_t line_length = 0;
char *line = NULL;
partname_prev[0] = '\0';
while (getline(&line, &line_length, fp) != -1) {
int ma, mi;
long long unsigned int sz;
if (sscanf(line, " %d %d %llu %127[^\n ]", &ma, &mi, &sz, partname) != 4)
continue;
/* Only check devices that have partitions under them.
* We can tell by checking that an entry like "sda" is immediately
* followed by one like "sda0". */
if (!strncmp(partname_prev, partname, strlen(partname_prev)) &&
strlen(partname_prev)) {
if ((pathname = is_wholedev(partname_prev))) {
if (do_search(params, pathname)) {
found++;
}
}
}
strcpy(partname_prev, partname);
}
fclose(fp);
fp = fopen(PROC_MTD, "re");
if (!fp) {
free(line);
return found;
}
while (getline(&line, &line_length, fp) != -1) {
uint64_t sz;
uint32_t erasesz;
char name[128];
// dev: size erasesize name
if (sscanf(line, "%64[^:]: %" PRIx64 " %x \"%127[^\"]\"",
partname, &sz, &erasesz, name) != 4)
continue;
if (strcmp(partname, "mtd0") == 0) {
char temp_dir[] = "/tmp/cgpt_find.XXXXXX";
if (params->drive_size == 0) {
if (GetMtdSize("/dev/mtd0", &params->drive_size) != 0) {
perror("GetMtdSize");
goto cleanup;
}
}
if (ReadNorFlash(temp_dir) != 0) {
perror("ReadNorFlash");
goto cleanup;
}
char nor_file[64];
if (snprintf(nor_file, sizeof(nor_file), "%s/rw_gpt", temp_dir) > 0) {
params->show_fn = chromeos_mtd_show;
if (do_search(params, nor_file)) {
found++;
}
params->show_fn = NULL;
}
RemoveDir(temp_dir);
break;
}
}
cleanup:
fclose(fp);
free(line);
return found;
}
void CgptFind(CgptFindParams *params) {
if (params == NULL)
return;
if (params->drive_name != NULL)
do_search(params, params->drive_name);
else
scan_real_devs(params);
}

View File

@@ -0,0 +1,61 @@
// Copyright (c) 2012 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.
#include <string.h>
#include "cgpt.h"
#include "cgptlib_internal.h"
#include "vboot_host.h"
int CgptLegacy(CgptLegacyParams *params) {
struct drive drive;
int gpt_retval;
GptHeader *h1, *h2;
if (params == NULL)
return CGPT_FAILED;
if (CGPT_OK != DriveOpen(params->drive_name, &drive, O_RDWR,
params->drive_size))
return CGPT_FAILED;
if (GPT_SUCCESS != (gpt_retval = GptSanityCheck(&drive.gpt))) {
Error("GptSanityCheck() returned %d: %s\n",
gpt_retval, GptError(gpt_retval));
return CGPT_FAILED;
}
h1 = (GptHeader *)drive.gpt.primary_header;
h2 = (GptHeader *)drive.gpt.secondary_header;
if (params->mode == CGPT_LEGACY_MODE_EFIPART) {
drive.gpt.ignored = MASK_NONE;
memcpy(h1->signature, GPT_HEADER_SIGNATURE, GPT_HEADER_SIGNATURE_SIZE);
memcpy(h2->signature, GPT_HEADER_SIGNATURE, GPT_HEADER_SIGNATURE_SIZE);
RepairEntries(&drive.gpt, MASK_SECONDARY);
drive.gpt.modified |= (GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1 |
GPT_MODIFIED_HEADER2);
} else if (params->mode == CGPT_LEGACY_MODE_IGNORE_PRIMARY) {
if (!(drive.gpt.valid_headers & MASK_SECONDARY) ||
!(drive.gpt.valid_entries & MASK_SECONDARY) ||
drive.gpt.ignored & MASK_SECONDARY) {
Error("Refusing to mark primary GPT ignored unless secondary is valid.");
return CGPT_FAILED;
}
memset(h1, 0, sizeof(*h1));
memcpy(h1->signature, GPT_HEADER_SIGNATURE_IGNORED,
GPT_HEADER_SIGNATURE_SIZE);
drive.gpt.modified |= GPT_MODIFIED_HEADER1;
} else {
memcpy(h1->signature, GPT_HEADER_SIGNATURE2, GPT_HEADER_SIGNATURE_SIZE);
memcpy(h2->signature, GPT_HEADER_SIGNATURE2, GPT_HEADER_SIGNATURE_SIZE);
memset(drive.gpt.primary_entries, 0, drive.gpt.sector_bytes);
drive.gpt.modified |= (GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1 |
GPT_MODIFIED_HEADER2);
}
UpdateCrc(&drive.gpt);
// Write it all out
return DriveClose(&drive, 1);
}

View File

@@ -0,0 +1,263 @@
/* Copyright 2015 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.
*/
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <ftw.h>
#include <inttypes.h>
#include <linux/major.h>
#include <stdbool.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "cgpt.h"
#include "cgpt_nor.h"
static const char FLASHROM_PATH[] = "/usr/sbin/flashrom";
// Obtain the MTD size from its sysfs node.
int GetMtdSize(const char *mtd_device, uint64_t *size) {
mtd_device = strrchr(mtd_device, '/');
if (mtd_device == NULL) {
errno = EINVAL;
return 1;
}
char *sysfs_name;
if (asprintf(&sysfs_name, "/sys/class/mtd%s/size", mtd_device) == -1) {
return 1;
}
FILE *fp = fopen(sysfs_name, "r");
free(sysfs_name);
if (fp == NULL) {
return 1;
}
int ret = (fscanf(fp, "%" PRIu64 "\n", size) != 1);
fclose(fp);
return ret;
}
int ForkExecV(const char *cwd, const char *const argv[]) {
pid_t pid = fork();
if (pid == -1) {
return -1;
}
int status = -1;
if (pid == 0) {
if (cwd && chdir(cwd) != 0) {
return -1;
}
execv(argv[0], (char *const *)argv);
// If this is reached, execv fails.
err(-1, "Cannot exec %s in %s.", argv[0], cwd);
} else {
if (waitpid(pid, &status, 0) != -1 && WIFEXITED(status))
return WEXITSTATUS(status);
}
return status;
}
int ForkExecL(const char *cwd, const char *cmd, ...) {
int argc;
va_list ap;
va_start(ap, cmd);
for (argc = 1; va_arg(ap, char *) != NULL; ++argc);
va_end(ap);
va_start(ap, cmd);
const char **argv = calloc(argc + 1, sizeof(char *));
if (argv == NULL) {
errno = ENOMEM;
va_end(ap);
return -1;
}
argv[0] = cmd;
int i;
for (i = 1; i < argc; ++i) {
argv[i] = va_arg(ap, char *);
}
va_end(ap);
int ret = ForkExecV(cwd, argv);
free(argv);
return ret;
}
static int read_write(int source_fd,
uint64_t size,
const char *src_name,
int idx) {
int ret = 1;
const int bufsize = 4096;
char *buf = malloc(bufsize);
if (buf == NULL) {
goto clean_exit;
}
ret++;
char *dest;
if (asprintf(&dest, "%s_%d", src_name, idx) == -1) {
goto free_buf;
}
ret++;
int dest_fd = open(dest, O_WRONLY | O_CLOEXEC | O_CREAT, 0600);
if (dest_fd < 0) {
goto free_dest;
}
ret++;
uint64_t copied = 0;
ssize_t nr_read;
ssize_t nr_write;
while (copied < size) {
size_t to_read = size - copied;
if (to_read > bufsize) {
to_read = bufsize;
}
nr_read = read(source_fd, buf, to_read);
if (nr_read < 0) {
goto close_dest_fd;
}
nr_write = 0;
while (nr_write < nr_read) {
ssize_t s = write(dest_fd, buf + nr_write, nr_read - nr_write);
if (s < 0) {
goto close_dest_fd;
}
nr_write += s;
}
copied += nr_read;
}
ret = 0;
close_dest_fd:
close(dest_fd);
free_dest:
free(dest);
free_buf:
free(buf);
clean_exit:
return ret;
}
static int split_gpt(const char *dir_name, const char *file_name) {
int ret = 1;
char *source;
if (asprintf(&source, "%s/%s", dir_name, file_name) == -1) {
goto clean_exit;
}
ret++;
int fd = open(source, O_RDONLY | O_CLOEXEC);
if (fd < 0) {
goto free_source;
}
ret++;
struct stat stat;
if (fstat(fd, &stat) != 0 || (stat.st_size & 1) != 0) {
goto close_fd;
}
uint64_t half_size = stat.st_size / 2;
ret++;
if (read_write(fd, half_size, source, 1) != 0 ||
read_write(fd, half_size, source, 2) != 0) {
goto close_fd;
}
ret = 0;
close_fd:
close(fd);
free_source:
free(source);
clean_exit:
return ret;
}
static int remove_file_or_dir(const char *fpath, const struct stat *sb,
int typeflag, struct FTW *ftwbuf) {
return remove(fpath);
}
int RemoveDir(const char *dir) {
return nftw(dir, remove_file_or_dir, 20, FTW_DEPTH | FTW_PHYS);
}
// Read RW_GPT from NOR flash to "rw_gpt" in a temp dir |temp_dir_template|.
// |temp_dir_template| is passed to mkdtemp() so it must satisfy all
// requirements by mkdtemp.
int ReadNorFlash(char *temp_dir_template) {
int ret = 0;
// Create a temp dir to work in.
ret++;
if (mkdtemp(temp_dir_template) == NULL) {
Error("Cannot create a temporary directory.\n");
return ret;
}
// Read RW_GPT section from NOR flash to "rw_gpt".
ret++;
int fd_flags = fcntl(1, F_GETFD);
// Close stdout on exec so that flashrom does not muck up cgpt's output.
if (0 != fcntl(1, F_SETFD, FD_CLOEXEC))
Warning("Can't stop flashrom from mucking up our output\n");
if (ForkExecL(temp_dir_template, FLASHROM_PATH, "-i", "RW_GPT:rw_gpt", "-r",
NULL) != 0) {
Error("Cannot exec flashrom to read from RW_GPT section.\n");
RemoveDir(temp_dir_template);
} else {
ret = 0;
}
// Restore stdout flags
if (0 != fcntl(1, F_SETFD, fd_flags))
Warning("Can't restore stdout flags\n");
return ret;
}
// Write "rw_gpt" back to NOR flash. We write the file in two parts for safety.
int WriteNorFlash(const char *dir) {
int ret = 0;
ret++;
if (split_gpt(dir, "rw_gpt") != 0) {
Error("Cannot split rw_gpt in two.\n");
return ret;
}
ret++;
int nr_fails = 0;
int fd_flags = fcntl(1, F_GETFD);
// Close stdout on exec so that flashrom does not muck up cgpt's output.
if (0 != fcntl(1, F_SETFD, FD_CLOEXEC))
Warning("Can't stop flashrom from mucking up our output\n");
if (ForkExecL(dir, FLASHROM_PATH, "-i", "RW_GPT_PRIMARY:rw_gpt_1",
"-w", "--fast-verify", NULL) != 0) {
Warning("Cannot write the 1st half of rw_gpt back with flashrom.\n");
nr_fails++;
}
if (ForkExecL(dir, FLASHROM_PATH, "-i", "RW_GPT_SECONDARY:rw_gpt_2",
"-w", "--fast-verify", NULL) != 0) {
Warning("Cannot write the 2nd half of rw_gpt back with flashrom.\n");
nr_fails++;
}
if (0 != fcntl(1, F_SETFD, fd_flags))
Warning("Can't restore stdout flags\n");
switch (nr_fails) {
case 0: ret = 0; break;
case 1: Warning("It might still be okay.\n"); break;
case 2: Error("Cannot write both parts back with flashrom.\n"); break;
}
return ret;
}

View File

@@ -0,0 +1,34 @@
/* Copyright 2015 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.
*
* This module provides some utility functions to use "flashrom" to read from
* and write to NOR flash.
*/
#ifndef VBOOT_REFERCENCE_CGPT_CGPT_NOR_H_
#define VBOOT_REFERCENCE_CGPT_CGPT_NOR_H_
// Obtain the MTD size from its sysfs node. |mtd_device| should point to
// a dev node such as /dev/mtd0. This function returns 0 on success.
int GetMtdSize(const char *mtd_device, uint64_t *size);
// Exec |argv| in |cwd|. Return -1 on error, or exit code on success. |argv|
// must be terminated with a NULL element as is required by execv().
int ForkExecV(const char *cwd, const char *const argv[]);
// Similar to ForkExecV but with a vararg instead of an array of pointers.
int ForkExecL(const char *cwd, const char *cmd, ...);
// Exec "rm" to remove |dir|.
int RemoveDir(const char *dir);
// Read RW_GPT from NOR flash to "rw_gpt" in a temp dir |temp_dir_template|.
// |temp_dir_template| is passed to mkdtemp() so it must satisfy all
// requirements by mkdtemp().
int ReadNorFlash(char *temp_dir_template);
// Write "rw_gpt" back to NOR flash. We write the file in two parts for safety.
int WriteNorFlash(const char *dir);
#endif // VBOOT_REFERCENCE_CGPT_CGPT_NOR_H_

View File

@@ -0,0 +1,213 @@
// Copyright (c) 2012 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.
#include <string.h>
#include "cgpt.h"
#include "cgptlib_internal.h"
#include "vboot_host.h"
//////////////////////////////////////////////////////////////////////////////
// We need a sorted list of priority groups, where each element in the list
// contains an unordered list of GPT partition numbers.
#define MAX_GROUPS 17 // 0-15, plus one "higher"
typedef struct {
int priority; // priority of this group
int num_parts; // number of partitions in this group
uint32_t *part; // array of partitions in this group
} group_t;
typedef struct {
int max_parts; // max number of partitions in any group
int num_groups; // number of non-empty groups
group_t group[MAX_GROUPS]; // array of groups
} group_list_t;
static group_list_t *NewGroupList(int max_p) {
int i;
group_list_t *gl = (group_list_t *)malloc(sizeof(group_list_t));
require(gl);
gl->max_parts = max_p;
gl->num_groups = 0;
// reserve space for the maximum number of partitions in every group
for (i=0; i<MAX_GROUPS; i++) {
gl->group[i].priority = -1;
gl->group[i].num_parts = 0;
gl->group[i].part = (uint32_t *)malloc(sizeof(uint32_t) * max_p);
require(gl->group[i].part);
}
return gl;
}
static void FreeGroups(group_list_t *gl) {
int i;
for (i=0; i<MAX_GROUPS; i++)
free(gl->group[i].part);
free(gl);
}
static void AddToGroup(group_list_t *gl, int priority, int partition) {
int i;
// See if I've already got a group with this priority
for (i=0; i<gl->num_groups; i++)
if (gl->group[i].priority == priority)
break;
if (i == gl->num_groups) {
// no, add a group
require(i < MAX_GROUPS);
gl->num_groups++;
gl->group[i].priority = priority;
}
// add the partition to it
int j = gl->group[i].num_parts;
gl->group[i].part[j] = partition;
gl->group[i].num_parts++;
}
static void ChangeGroup(group_list_t *gl, int old_priority, int new_priority) {
int i;
for (i=0; i<gl->num_groups; i++)
if (gl->group[i].priority == old_priority) {
gl->group[i].priority = new_priority;
break;
}
}
static void SortGroups(group_list_t *gl) {
int i, j;
group_t tmp;
// straight insertion sort is fast enough
for (i=1; i<gl->num_groups; i++) {
tmp = gl->group[i];
for (j=i; j && (gl->group[j-1].priority < tmp.priority); j--)
gl->group[j] = gl->group[j-1];
gl->group[j] = tmp;
}
}
int CgptPrioritize(CgptPrioritizeParams *params) {
struct drive drive;
int priority;
int gpt_retval;
uint32_t index;
uint32_t max_part;
int num_kernels;
int i,j;
group_list_t *groups;
if (params == NULL)
return CGPT_FAILED;
if (CGPT_OK != DriveOpen(params->drive_name, &drive, O_RDWR,
params->drive_size))
return CGPT_FAILED;
if (GPT_SUCCESS != (gpt_retval = GptSanityCheck(&drive.gpt))) {
Error("GptSanityCheck() returned %d: %s\n",
gpt_retval, GptError(gpt_retval));
return CGPT_FAILED;
}
if (CGPT_OK != CheckValid(&drive)) {
Error("please run 'cgpt repair' before reordering the priority.\n");
return CGPT_OK;
}
max_part = GetNumberOfEntries(&drive);
if (params->set_partition) {
if (params->set_partition < 1 || params->set_partition > max_part) {
Error("invalid partition number: %d (must be between 1 and %d\n",
params->set_partition, max_part);
goto bad;
}
index = params->set_partition - 1;
// it must be a kernel
if (!IsKernel(&drive, PRIMARY, index)) {
Error("partition %d is not a ChromeOS kernel\n", params->set_partition);
goto bad;
}
}
// How many kernel partitions do I have?
num_kernels = 0;
for (i = 0; i < max_part; i++) {
if (IsKernel(&drive, PRIMARY, i))
num_kernels++;
}
if (num_kernels) {
// Determine the current priority groups
groups = NewGroupList(num_kernels);
for (i = 0; i < max_part; i++) {
if (!IsKernel(&drive, PRIMARY, i))
continue;
priority = GetPriority(&drive, PRIMARY, i);
// Is this partition special?
if (params->set_partition && (i+1 == params->set_partition)) {
params->orig_priority = priority; // remember the original priority
if (params->set_friends)
AddToGroup(groups, priority, i); // we'll move them all later
else
AddToGroup(groups, 99, i); // move only this one
} else {
AddToGroup(groups, priority, i); // just remember
}
}
// If we're including friends, then change the original group priority
if (params->set_partition && params->set_friends) {
ChangeGroup(groups, params->orig_priority, 99);
}
// Sorting gives the new order. Now we just need to reassign the
// priorities.
SortGroups(groups);
// We'll never lower anything to zero, so if the last group is priority zero
// we can ignore it.
i = groups->num_groups;
if (groups->group[i-1].priority == 0)
groups->num_groups--;
// Where do we start?
if (params->max_priority)
priority = params->max_priority;
else
priority = groups->num_groups > 15 ? 15 : groups->num_groups;
// Figure out what the new values should be
for (i=0; i<groups->num_groups; i++) {
groups->group[i].priority = priority;
if (priority > 1)
priority--;
}
// Now apply the ranking to the GPT
for (i=0; i<groups->num_groups; i++)
for (j=0; j<groups->group[i].num_parts; j++)
SetPriority(&drive, PRIMARY,
groups->group[i].part[j], groups->group[i].priority);
FreeGroups(groups);
}
// Write it all out
UpdateAllEntries(&drive);
return DriveClose(&drive, 1);
bad:
(void) DriveClose(&drive, 0);
return CGPT_FAILED;
}

View File

@@ -0,0 +1,56 @@
// Copyright (c) 2012 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.
#include <string.h>
#include "cgpt.h"
#include "cgptlib_internal.h"
#include "vboot_host.h"
int CgptRepair(CgptRepairParams *params) {
struct drive drive;
if (params == NULL)
return CGPT_FAILED;
if (CGPT_OK != DriveOpen(params->drive_name, &drive, O_RDWR,
params->drive_size))
return CGPT_FAILED;
int gpt_retval = GptSanityCheck(&drive.gpt);
if (params->verbose)
printf("GptSanityCheck() returned %d: %s\n",
gpt_retval, GptError(gpt_retval));
GptHeader *header;
if (MASK_PRIMARY == drive.gpt.valid_headers ||
MASK_BOTH == drive.gpt.valid_headers) {
header = (GptHeader *)(drive.gpt.primary_header);
} else {
header = (GptHeader *)(drive.gpt.secondary_header);
}
if (MASK_PRIMARY == drive.gpt.valid_entries) {
free(drive.gpt.secondary_entries);
drive.gpt.secondary_entries =
malloc(header->size_of_entry * header->number_of_entries);
} else if (MASK_SECONDARY == drive.gpt.valid_entries) {
free(drive.gpt.primary_entries);
drive.gpt.primary_entries =
malloc(header->size_of_entry * header->number_of_entries);
}
GptRepair(&drive.gpt);
if (drive.gpt.modified & GPT_MODIFIED_HEADER1)
printf("Primary Header is updated.\n");
if (drive.gpt.modified & GPT_MODIFIED_ENTRIES1)
printf("Primary Entries is updated.\n");
if (drive.gpt.modified & GPT_MODIFIED_ENTRIES2)
printf("Secondary Entries is updated.\n");
if (drive.gpt.modified & GPT_MODIFIED_HEADER2)
printf("Secondary Header is updated.\n");
return DriveClose(&drive, 1);
}

View File

@@ -0,0 +1,392 @@
// Copyright (c) 2012 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.
#define __STDC_FORMAT_MACROS
#include <string.h>
#include "cgpt.h"
#include "cgptlib_internal.h"
#include "crc32.h"
#include "vboot_host.h"
/* Generate output like:
*
* [AB-CD-EF-01] for group = 1
* [ABCD-EF01] for group = 3 (low byte first)
*
* Needs (size*3-1+3) bytes of space in 'buf' (included the tailing '\0').
*/
#define BUFFER_SIZE(size) (size *3 - 1 + 3)
static short Uint8To2Chars(const uint8_t t) {
int h = t >> 4;
int l = t & 0xf;
h = (h >= 0xA) ? h - 0xA + 'A' : h + '0';
l = (l >= 0xA) ? l - 0xA + 'A' : l + '0';
return (h << 8) + l;
}
static void RawDump(const uint8_t *memory, const int size,
char *buf, int group) {
int i, outlen = 0;
buf[outlen++] = '[';
for (i = 0; i < size; ++i) {
short c2 = Uint8To2Chars(memory[i]);
buf[outlen++] = c2 >> 8;
buf[outlen++] = c2 & 0xff;
if (i != (size - 1) && ((i + 1) % group) == 0)
buf[outlen++] = '-';
}
buf[outlen++] = ']';
buf[outlen++] = '\0';
}
/* Output formatters */
#define TITLE_FMT "%12s%12s%8s %s\n"
#define GPT_FMT "%12"PRId64"%12"PRId64"%8s %s\n"
#define GPT_MORE "%12s%12s%8s ", "", "", ""
#define PARTITION_FMT "%12"PRId64"%12"PRId64"%8d %s\n"
#define PARTITION_MORE "%12s%12s%8s %s%s\n", "", "", ""
void PrintSignature(const char *indent, const char *sig, size_t n, int raw) {
size_t i;
printf("%sSig: ", indent);
if (!raw) {
printf("[");
for (i = 0; i < n; ++i)
printf("%c", sig[i]);
printf("]");
} else {
char *buf = malloc(BUFFER_SIZE(n));
RawDump((uint8_t *)sig, n, buf, 1);
printf("%s", buf);
free(buf);
}
printf("\n");
}
static void HeaderDetails(GptHeader *header, GptEntry *entries,
const char *indent, int raw) {
PrintSignature(indent, header->signature, sizeof(header->signature), raw);
printf("%sRev: 0x%08x\n", indent, header->revision);
printf("%sSize: %d\n", indent, header->size);
printf("%sHeader CRC: 0x%08x %s\n", indent, header->header_crc32,
(HeaderCrc(header) != header->header_crc32) ? "(INVALID)" : "");
printf("%sMy LBA: %lld\n", indent, (long long)header->my_lba);
printf("%sAlternate LBA: %lld\n", indent, (long long)header->alternate_lba);
printf("%sFirst LBA: %lld\n", indent, (long long)header->first_usable_lba);
printf("%sLast LBA: %lld\n", indent, (long long)header->last_usable_lba);
{ /* For disk guid */
char buf[GUID_STRLEN];
GuidToStr(&header->disk_uuid, buf, GUID_STRLEN);
printf("%sDisk UUID: %s\n", indent, buf);
}
printf("%sEntries LBA: %lld\n", indent, (long long)header->entries_lba);
printf("%sNumber of entries: %d\n", indent, header->number_of_entries);
printf("%sSize of entry: %d\n", indent, header->size_of_entry);
printf("%sEntries CRC: 0x%08x %s\n", indent, header->entries_crc32,
header->entries_crc32 !=
Crc32((const uint8_t *)entries,header->size_of_entry *
header->number_of_entries)
? "INVALID" : ""
);
}
void EntryDetails(GptEntry *entry, uint32_t index, int raw) {
char contents[256]; // scratch buffer for formatting output
uint8_t label[GPT_PARTNAME_LEN];
char type[GUID_STRLEN], unique[GUID_STRLEN];
int clen;
UTF16ToUTF8(entry->name, sizeof(entry->name) / sizeof(entry->name[0]),
label, sizeof(label));
require(snprintf(contents, sizeof(contents),
"Label: \"%s\"", label) < sizeof(contents));
printf(PARTITION_FMT, (uint64_t)entry->starting_lba,
(uint64_t)(entry->ending_lba - entry->starting_lba + 1),
index+1, contents);
if (!raw && CGPT_OK == ResolveType(&entry->type, type)) {
printf(PARTITION_MORE, "Type: ", type);
} else {
GuidToStr(&entry->type, type, GUID_STRLEN);
printf(PARTITION_MORE, "Type: ", type);
}
GuidToStr(&entry->unique, unique, GUID_STRLEN);
printf(PARTITION_MORE, "UUID: ", unique);
clen = 0;
if (!raw) {
if (GuidEqual(&guid_chromeos_kernel, &entry->type)) {
int tries = (entry->attrs.fields.gpt_att &
CGPT_ATTRIBUTE_TRIES_MASK) >>
CGPT_ATTRIBUTE_TRIES_OFFSET;
int successful = (entry->attrs.fields.gpt_att &
CGPT_ATTRIBUTE_SUCCESSFUL_MASK) >>
CGPT_ATTRIBUTE_SUCCESSFUL_OFFSET;
int priority = (entry->attrs.fields.gpt_att &
CGPT_ATTRIBUTE_PRIORITY_MASK) >>
CGPT_ATTRIBUTE_PRIORITY_OFFSET;
clen = snprintf(contents, sizeof(contents),
"priority=%d tries=%d successful=%d ",
priority, tries, successful);
}
if (entry->attrs.fields.required) {
clen += snprintf(contents + clen, sizeof(contents) - clen,
"required=%d ", entry->attrs.fields.required);
require(clen < sizeof(contents));
}
if (entry->attrs.fields.efi_ignore) {
clen += snprintf(contents + clen, sizeof(contents) - clen,
"efi_ignore=%d ", entry->attrs.fields.efi_ignore);
require(clen < sizeof(contents));
}
if (entry->attrs.fields.legacy_boot) {
clen += snprintf(contents + clen, sizeof(contents) - clen,
"legacy_boot=%d ", entry->attrs.fields.legacy_boot);
require(clen < sizeof(contents));
}
} else {
clen = snprintf(contents, sizeof(contents),
"[%x]", entry->attrs.fields.gpt_att);
}
require(clen < sizeof(contents));
if (clen)
printf(PARTITION_MORE, "Attr: ", contents);
}
void EntriesDetails(struct drive *drive, const int secondary, int raw) {
uint32_t i;
for (i = 0; i < GetNumberOfEntries(drive); ++i) {
GptEntry *entry;
entry = GetEntry(&drive->gpt, secondary, i);
if (GuidIsZero(&entry->type))
continue;
EntryDetails(entry, i, raw);
}
}
static int GptShow(struct drive *drive, CgptShowParams *params) {
int gpt_retval;
if (GPT_SUCCESS != (gpt_retval = GptSanityCheck(&drive->gpt))) {
Error("GptSanityCheck() returned %d: %s\n",
gpt_retval, GptError(gpt_retval));
return CGPT_FAILED;
}
if (params->partition) { // show single partition
if (params->partition > GetNumberOfEntries(drive)) {
Error("invalid partition number: %d\n", params->partition);
return CGPT_FAILED;
}
uint32_t index = params->partition - 1;
GptEntry *entry = GetEntry(&drive->gpt, ANY_VALID, index);
char buf[256]; // scratch buffer for string conversion
if (params->single_item) {
switch(params->single_item) {
case 'b':
printf("%" PRId64 "\n", entry->starting_lba);
break;
case 's': {
uint64_t size = 0;
// If these aren't actually defined, don't show anything
if (entry->ending_lba || entry->starting_lba)
size = entry->ending_lba - entry->starting_lba + 1;
printf("%" PRId64 "\n", size);
break;
}
case 't':
GuidToStr(&entry->type, buf, sizeof(buf));
printf("%s\n", buf);
break;
case 'u':
GuidToStr(&entry->unique, buf, sizeof(buf));
printf("%s\n", buf);
break;
case 'l':
UTF16ToUTF8(entry->name, sizeof(entry->name) / sizeof(entry->name[0]),
(uint8_t *)buf, sizeof(buf));
printf("%s\n", buf);
break;
case 'S':
printf("%d\n", GetSuccessful(drive, ANY_VALID, index));
break;
case 'T':
printf("%d\n", GetTries(drive, ANY_VALID, index));
break;
case 'P':
printf("%d\n", GetPriority(drive, ANY_VALID, index));
break;
case 'R':
printf("%d\n", GetRequired(drive, ANY_VALID, index));
break;
case 'B':
printf("%d\n", GetLegacyBoot(drive, ANY_VALID, index));
break;
case 'A':
printf("0x%x\n", entry->attrs.fields.gpt_att);
break;
}
} else {
printf(TITLE_FMT, "start", "size", "part", "contents");
EntryDetails(entry, index, params->numeric);
}
} else if (params->quick) { // show all partitions, quickly
uint32_t i;
GptEntry *entry;
char type[GUID_STRLEN];
for (i = 0; i < GetNumberOfEntries(drive); ++i) {
entry = GetEntry(&drive->gpt, ANY_VALID, i);
if (GuidIsZero(&entry->type))
continue;
if (!params->numeric && CGPT_OK == ResolveType(&entry->type, type)) {
} else {
GuidToStr(&entry->type, type, GUID_STRLEN);
}
printf(PARTITION_FMT, (uint64_t)entry->starting_lba,
(uint64_t)(entry->ending_lba - entry->starting_lba + 1),
i+1, type);
}
} else { // show all partitions
GptEntry *entries;
if (CGPT_OK != ReadPMBR(drive)) {
Error("Unable to read PMBR\n");
return CGPT_FAILED;
}
printf(TITLE_FMT, "start", "size", "part", "contents");
char buf[256]; // buffer for formatted PMBR content
PMBRToStr(&drive->pmbr, buf, sizeof(buf)); // will exit if buf is too small
printf(GPT_FMT, (uint64_t)0, (uint64_t)GPT_PMBR_SECTORS, "", buf);
if (drive->gpt.ignored & MASK_PRIMARY) {
printf(GPT_FMT, (uint64_t)GPT_PMBR_SECTORS,
(uint64_t)GPT_HEADER_SECTORS, "IGNORED", "Pri GPT header");
} else {
if (drive->gpt.valid_headers & MASK_PRIMARY) {
printf(GPT_FMT, (uint64_t)GPT_PMBR_SECTORS,
(uint64_t)GPT_HEADER_SECTORS, "", "Pri GPT header");
} else {
printf(GPT_FMT, (uint64_t)GPT_PMBR_SECTORS,
(uint64_t)GPT_HEADER_SECTORS, "INVALID", "Pri GPT header");
}
if (params->debug ||
((drive->gpt.valid_headers & MASK_PRIMARY) && params->verbose)) {
GptHeader *header;
char indent[64];
require(snprintf(indent, sizeof(indent), GPT_MORE) < sizeof(indent));
header = (GptHeader*)drive->gpt.primary_header;
entries = (GptEntry*)drive->gpt.primary_entries;
HeaderDetails(header, entries, indent, params->numeric);
}
GptHeader* primary_header = (GptHeader*)drive->gpt.primary_header;
printf(GPT_FMT, (uint64_t)primary_header->entries_lba,
(uint64_t)CalculateEntriesSectors(primary_header,
drive->gpt.sector_bytes),
drive->gpt.valid_entries & MASK_PRIMARY ? "" : "INVALID",
"Pri GPT table");
if (params->debug ||
(drive->gpt.valid_entries & MASK_PRIMARY))
EntriesDetails(drive, PRIMARY, params->numeric);
}
/****************************** Secondary *************************/
if (drive->gpt.ignored & MASK_SECONDARY) {
printf(GPT_FMT,
(uint64_t)(drive->gpt.gpt_drive_sectors - GPT_HEADER_SECTORS),
(uint64_t)GPT_HEADER_SECTORS, "IGNORED", "Sec GPT header");
} else {
GptHeader* secondary_header = (GptHeader*)drive->gpt.secondary_header;
printf(GPT_FMT, (uint64_t)secondary_header->entries_lba,
(uint64_t)CalculateEntriesSectors(secondary_header,
drive->gpt.sector_bytes),
drive->gpt.valid_entries & MASK_SECONDARY ? "" : "INVALID",
"Sec GPT table");
/* We show secondary table details if any of following is true.
* 1. in debug mode.
* 2. primary table is being ignored
* 3. only secondary is valid.
* 4. secondary is not identical to promary.
*/
if (params->debug || (drive->gpt.ignored & MASK_PRIMARY) ||
((drive->gpt.valid_entries & MASK_SECONDARY) &&
(!(drive->gpt.valid_entries & MASK_PRIMARY) ||
memcmp(drive->gpt.primary_entries, drive->gpt.secondary_entries,
secondary_header->number_of_entries *
secondary_header->size_of_entry)))) {
EntriesDetails(drive, SECONDARY, params->numeric);
}
if (drive->gpt.valid_headers & MASK_SECONDARY) {
printf(GPT_FMT,
(uint64_t)(drive->gpt.gpt_drive_sectors - GPT_HEADER_SECTORS),
(uint64_t)GPT_HEADER_SECTORS, "", "Sec GPT header");
} else {
printf(GPT_FMT, (uint64_t)GPT_PMBR_SECTORS,
(uint64_t)GPT_HEADER_SECTORS, "INVALID", "Sec GPT header");
}
/* We show secondary header if any of following is true:
* 1. in debug mode.
* 2. only secondary is valid.
* 3. secondary is not synonymous to primary and not ignored.
*/
if (params->debug ||
((drive->gpt.valid_headers & MASK_SECONDARY) &&
(!(drive->gpt.valid_headers & MASK_PRIMARY) ||
!IsSynonymous((GptHeader*)drive->gpt.primary_header,
(GptHeader*)drive->gpt.secondary_header)) &&
params->verbose)) {
GptHeader *header;
char indent[64];
require(snprintf(indent, sizeof(indent), GPT_MORE) < sizeof(indent));
header = (GptHeader*)drive->gpt.secondary_header;
entries = (GptEntry*)drive->gpt.secondary_entries;
HeaderDetails(header, entries, indent, params->numeric);
}
}
}
CheckValid(drive);
return CGPT_OK;
}
int CgptShow(CgptShowParams *params) {
struct drive drive;
if (params == NULL)
return CGPT_FAILED;
if (CGPT_OK != DriveOpen(params->drive_name, &drive, O_RDONLY,
params->drive_size))
return CGPT_FAILED;
if (GptShow(&drive, params))
return CGPT_FAILED;
DriveClose(&drive, 0);
return CGPT_OK;
}

View File

@@ -0,0 +1,196 @@
/* Copyright 2015 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.
*
* This utility wraps around "cgpt" execution to work with NAND. If the target
* device is an MTD device, this utility will read the GPT structures from
* FMAP, invokes "cgpt" on that, and writes the result back to NOR flash. */
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <limits.h>
#include <linux/major.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <sys/types.h>
#include <unistd.h>
#include "2sysincludes.h"
#include "2common.h"
#include "2sha.h"
#include "cgpt.h"
#include "cgpt_nor.h"
#include "file_keys.h"
// Check if cmdline |argv| has "-D". "-D" signifies that GPT structs are stored
// off device, and hence we should not wrap around cgpt.
static bool has_dash_D(int argc, const char *const argv[]) {
int i;
// We go from 2, because the second arg is a cgpt command such as "create".
for (i = 2; i < argc; ++i) {
if (strcmp("-D", argv[i]) == 0) {
return true;
}
}
return false;
}
// Check if |device_path| is an MTD device based on its major number being 90.
static bool is_mtd(const char *device_path) {
struct stat stat;
if (lstat(device_path, &stat) != 0) {
return false;
}
if (major(stat.st_rdev) != MTD_CHAR_MAJOR) {
return false;
}
return true;
}
// Return the element in |argv| that is an MTD device.
static const char *find_mtd_device(int argc, const char *const argv[]) {
int i;
for (i = 2; i < argc; ++i) {
if (is_mtd(argv[i])) {
return argv[i];
}
}
return NULL;
}
static int wrap_cgpt(int argc,
const char *const argv[],
const char *mtd_device) {
uint8_t original_hash[VB2_SHA1_DIGEST_SIZE];
uint8_t modified_hash[VB2_SHA1_DIGEST_SIZE];
int ret = 0;
// Create a temp dir to work in.
ret++;
char temp_dir[] = "/tmp/cgpt_wrapper.XXXXXX";
if (ReadNorFlash(temp_dir) != 0) {
return ret;
}
char rw_gpt_path[PATH_MAX];
if (snprintf(rw_gpt_path, sizeof(rw_gpt_path), "%s/rw_gpt", temp_dir) < 0) {
goto cleanup;
}
if (VB2_SUCCESS != DigestFile(rw_gpt_path, VB2_HASH_SHA1,
original_hash, sizeof(original_hash))) {
Error("Cannot compute original GPT digest.\n");
goto cleanup;
}
// Obtain the MTD size.
ret++;
uint64_t drive_size = 0;
if (GetMtdSize(mtd_device, &drive_size) != 0) {
Error("Cannot get the size of %s.\n", mtd_device);
goto cleanup;
}
// Launch cgpt on "rw_gpt" with -D size.
ret++;
const char** my_argv = calloc(argc + 2 + 1, sizeof(char *));
if (my_argv == NULL) {
errno = ENOMEM;
goto cleanup;
}
memcpy(my_argv, argv, sizeof(char *) * argc);
char *real_cgpt;
if (asprintf(&real_cgpt, "%s.bin", argv[0]) == -1) {
free(my_argv);
goto cleanup;
}
my_argv[0] = real_cgpt;
int i;
for (i = 2; i < argc; ++i) {
if (strcmp(my_argv[i], mtd_device) == 0) {
my_argv[i] = rw_gpt_path;
}
}
my_argv[argc] = "-D";
char size[32];
snprintf(size, sizeof(size), "%" PRIu64, drive_size);
my_argv[argc + 1] = size;
i = ForkExecV(NULL, my_argv);
free(real_cgpt);
free(my_argv);
if (i != 0) {
Error("Cannot exec cgpt to modify rw_gpt.\n");
goto cleanup;
}
// Write back "rw_gpt" to NOR flash in two chunks.
ret++;
if (VB2_SUCCESS == DigestFile(rw_gpt_path, VB2_HASH_SHA1,
modified_hash, sizeof(modified_hash))) {
if (memcmp(original_hash, modified_hash, VB2_SHA1_DIGEST_SIZE) != 0) {
ret = WriteNorFlash(temp_dir);
} else {
ret = 0;
}
}
cleanup:
RemoveDir(temp_dir);
return ret;
}
int main(int argc, const char *argv[]) {
char resolved_cgpt[PATH_MAX];
pid_t pid = getpid();
char exe_link[40];
int retval = 0;
if (argc < 1) {
return -1;
}
const char *orig_argv0 = argv[0];
snprintf(exe_link, sizeof(exe_link), "/proc/%d/exe", pid);
memset(resolved_cgpt, 0, sizeof(resolved_cgpt));
if (readlink(exe_link, resolved_cgpt, sizeof(resolved_cgpt) - 1) == -1) {
perror("readlink");
return -1;
}
argv[0] = resolved_cgpt;
if (argc > 2 && !has_dash_D(argc, argv)) {
const char *mtd_device = find_mtd_device(argc, argv);
if (mtd_device) {
retval = wrap_cgpt(argc, argv, mtd_device);
goto cleanup;
}
}
// Forward to cgpt as-is. Real cgpt has been renamed cgpt.bin.
char *real_cgpt;
if (asprintf(&real_cgpt, "%s.bin", argv[0]) == -1) {
retval = -1;
goto cleanup;
}
argv[0] = real_cgpt;
if (execv(argv[0], (char * const *)argv) == -1) {
err(-2, "execv(%s) failed", real_cgpt);
}
free(real_cgpt);
retval = -2;
cleanup:
argv[0] = orig_argv0;
return retval;
}

View File

@@ -0,0 +1,157 @@
// Copyright (c) 2012 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.
#include <getopt.h>
#include <string.h>
#include "cgpt.h"
#include "vboot_host.h"
extern const char* progname;
static void Usage(void)
{
printf("\nUsage: %s add [OPTIONS] DRIVE\n\n"
"Add, edit, or remove a partition entry.\n\n"
"Options:\n"
" -D NUM Size (in bytes) of the disk where partitions reside\n"
" default 0, meaning partitions and GPT structs are\n"
" both on DRIVE\n"
" -i NUM Specify partition (default is next available)\n"
" -b NUM Beginning sector\n"
" -s NUM Size in sectors\n"
" -t GUID Partition Type GUID\n"
" -u GUID Partition Unique ID\n"
" -l LABEL Label\n"
" -S NUM set Successful flag (0|1)\n"
" -T NUM set Tries flag (0-15)\n"
" -P NUM set Priority flag (0-15)\n"
" -R NUM set Required flag (0|1)\n"
" -B NUM set Legacy Boot flag (0|1)\n"
" -A NUM set raw 16-bit attribute value (bits 48-63)\n"
"\n"
"Use the -i option to modify an existing partition.\n"
"The -b, -s, and -t options must be given for new partitions.\n"
"\n", progname);
PrintTypes();
}
int cmd_add(int argc, char *argv[]) {
CgptAddParams params;
memset(&params, 0, sizeof(params));
int c;
int errorcnt = 0;
char *e = 0;
opterr = 0; // quiet, you
while ((c=getopt(argc, argv, ":hi:b:s:t:u:l:S:T:P:R:B:A:D:")) != -1)
{
switch (c)
{
case 'D':
params.drive_size = strtoull(optarg, &e, 0);
errorcnt += check_int_parse(c, e);
break;
case 'i':
params.partition = (uint32_t)strtoul(optarg, &e, 0);
errorcnt += check_int_parse(c, e);
break;
case 'b':
params.set_begin = 1;
params.begin = strtoull(optarg, &e, 0);
errorcnt += check_int_parse(c, e);
break;
case 's':
params.set_size = 1;
params.size = strtoull(optarg, &e, 0);
errorcnt += check_int_parse(c, e);
break;
case 't':
params.set_type = 1;
if (CGPT_OK != SupportedType(optarg, &params.type_guid) &&
CGPT_OK != StrToGuid(optarg, &params.type_guid)) {
Error("invalid argument to -%c: %s\n", c, optarg);
errorcnt++;
}
break;
case 'u':
params.set_unique = 1;
if (CGPT_OK != StrToGuid(optarg, &params.unique_guid)) {
Error("invalid argument to -%c: %s\n", c, optarg);
errorcnt++;
}
break;
case 'l':
params.label = optarg;
break;
case 'S':
params.set_successful = 1;
params.successful = (uint32_t)strtoul(optarg, &e, 0);
errorcnt += check_int_parse(c, e);
errorcnt += check_int_limit(c, params.successful, 0, 1);
break;
case 'T':
params.set_tries = 1;
params.tries = (uint32_t)strtoul(optarg, &e, 0);
errorcnt += check_int_parse(c, e);
errorcnt += check_int_limit(c, params.tries, 0, 15);
break;
case 'P':
params.set_priority = 1;
params.priority = (uint32_t)strtoul(optarg, &e, 0);
errorcnt += check_int_parse(c, e);
errorcnt += check_int_limit(c, params.priority, 0, 15);
break;
case 'R':
params.set_required = 1;
params.required = (uint32_t)strtoul(optarg, &e, 0);
errorcnt += check_int_parse(c, e);
errorcnt += check_int_limit(c, params.required, 0, 1);
break;
case 'B':
params.set_legacy_boot = 1;
params.legacy_boot = (uint32_t)strtoul(optarg, &e, 0);
errorcnt += check_int_parse(c, e);
errorcnt += check_int_limit(c, params.legacy_boot, 0, 1);
break;
case 'A':
params.set_raw = 1;
params.raw_value = strtoull(optarg, &e, 0);
errorcnt += check_int_parse(c, e);
break;
case 'h':
Usage();
return CGPT_OK;
case '?':
Error("unrecognized option: -%c\n", optopt);
errorcnt++;
break;
case ':':
Error("missing argument to -%c\n", optopt);
errorcnt++;
break;
default:
errorcnt++;
break;
}
}
if (errorcnt)
{
Usage();
return CGPT_FAILED;
}
if (optind >= argc)
{
Error("missing drive argument\n");
return CGPT_FAILED;
}
params.drive_name = argv[optind];
return CgptAdd(&params);
}

View File

@@ -0,0 +1,89 @@
// Copyright (c) 2012 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.
#include <getopt.h>
#include <string.h>
#include "cgpt.h"
#include "vboot_host.h"
extern const char* progname;
static void Usage(void)
{
printf("\nUsage: %s boot [OPTIONS] DRIVE\n\n"
"Edit the PMBR sector for legacy BIOSes\n\n"
"Options:\n"
" -D NUM Size (in bytes) of the disk where partitions reside\n"
" default 0, meaning partitions and GPT structs are\n"
" both on DRIVE\n"
" -i NUM Set bootable partition\n"
" -b FILE Install bootloader code in the PMBR\n"
" -p Create legacy PMBR partition table\n"
"\n"
"With no options, it will just print the PMBR boot guid\n"
"\n", progname);
}
int cmd_boot(int argc, char *argv[]) {
CgptBootParams params;
memset(&params, 0, sizeof(params));
int c;
int errorcnt = 0;
char *e = 0;
opterr = 0; // quiet, you
while ((c=getopt(argc, argv, ":hi:b:pD:")) != -1)
{
switch (c)
{
case 'D':
params.drive_size = strtoull(optarg, &e, 0);
errorcnt += check_int_parse(c, e);
break;
case 'i':
params.partition = (uint32_t)strtoul(optarg, &e, 0);
errorcnt += check_int_parse(c, e);
break;
case 'b':
params.bootfile = optarg;
break;
case 'p':
params.create_pmbr = 1;
break;
case 'h':
Usage();
return CGPT_OK;
case '?':
Error("unrecognized option: -%c\n", optopt);
errorcnt++;
break;
case ':':
Error("missing argument to -%c\n", optopt);
errorcnt++;
break;
default:
errorcnt++;
break;
}
}
if (errorcnt)
{
Usage();
return CGPT_FAILED;
}
if (optind >= argc) {
Error("missing drive argument\n");
return CGPT_FAILED;
}
params.drive_name = argv[optind];
return CgptBoot(&params);
}

View File

@@ -0,0 +1,81 @@
// Copyright (c) 2012 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.
#include <getopt.h>
#include <string.h>
#include "cgpt.h"
#include "vboot_host.h"
extern const char* progname;
static void Usage(void)
{
printf("\nUsage: %s create [OPTIONS] DRIVE\n\n"
"Create or reset an empty GPT.\n\n"
"Options:\n"
" -D NUM Size (in bytes) of the disk where partitions reside\n"
" default 0, meaning partitions and GPT structs are\n"
" both on DRIVE\n"
" -z Zero the sectors of the GPT table and entries\n"
" -p NUM Size (in blocks) of the disk to pad between the\n"
" primary GPT header and its entries, default 0\n"
"\n", progname);
}
int cmd_create(int argc, char *argv[]) {
CgptCreateParams params;
memset(&params, 0, sizeof(params));
int c;
int errorcnt = 0;
char *e = 0;
opterr = 0; // quiet, you
while ((c=getopt(argc, argv, ":hzp:D:")) != -1)
{
switch (c)
{
case 'D':
params.drive_size = strtoull(optarg, &e, 0);
errorcnt += check_int_parse(c, e);
break;
case 'z':
params.zap = 1;
break;
case 'p':
params.padding = strtoull(optarg, &e, 0);
errorcnt += check_int_parse(c, e);
break;
case 'h':
Usage();
return CGPT_OK;
case '?':
Error("unrecognized option: -%c\n", optopt);
errorcnt++;
break;
case ':':
Error("missing argument to -%c\n", optopt);
errorcnt++;
break;
default:
errorcnt++;
break;
}
}
if (errorcnt)
{
Usage();
return CGPT_FAILED;
}
if (optind >= argc) {
Usage();
return CGPT_FAILED;
}
params.drive_name = argv[optind];
return CgptCreate(&params);
}

View File

@@ -0,0 +1,182 @@
// Copyright (c) 2012 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.
#include <getopt.h>
#include <string.h>
#include "cgpt.h"
#include "vboot_host.h"
extern const char* progname;
static void Usage(void)
{
printf("\nUsage: %s find [OPTIONS] [DRIVE]\n\n"
"Find a partition by its UUID or label. With no specified DRIVE\n"
"it scans all physical drives.\n\n"
"Options:\n"
" -D NUM Size (in bytes) of the disk where partitions reside\n"
" default 0, meaning partitions and GPT structs are\n"
" both on DRIVE\n"
" -t GUID Search for Partition Type GUID\n"
" -u GUID Search for Partition Unique ID\n"
" -l LABEL Search for Label\n"
" -v Be verbose in displaying matches (repeatable)\n"
" -n Numeric output only\n"
" -1 Fail if more than one match is found\n"
" -M FILE"
" Matching partition data must also contain FILE content\n"
" -O NUM"
" Byte offset into partition to match content (default 0)\n"
"\n", progname);
PrintTypes();
}
// read a file into a buffer, return buffer and update size
static uint8_t *ReadFile(const char *filename, uint64_t *size) {
FILE *f;
uint8_t *buf;
long pos;
f = fopen(filename, "rb");
if (!f) {
return NULL;
}
fseek(f, 0, SEEK_END);
pos = ftell(f);
if (pos < 0) {
fclose(f);
return NULL;
}
*size = pos;
rewind(f);
buf = malloc(*size);
if (!buf) {
fclose(f);
return NULL;
}
if(1 != fread(buf, *size, 1, f)) {
fclose(f);
free(buf);
return NULL;
}
fclose(f);
return buf;
}
int cmd_find(int argc, char *argv[]) {
CgptFindParams params;
memset(&params, 0, sizeof(params));
int i;
int errorcnt = 0;
char *e = 0;
int c;
opterr = 0; // quiet, you
while ((c=getopt(argc, argv, ":hv1nt:u:l:M:O:D:")) != -1)
{
switch (c)
{
case 'D':
params.drive_size = strtoull(optarg, &e, 0);
errorcnt += check_int_parse(c, e);
break;
case 'v':
params.verbose++;
break;
case 'n':
params.numeric = 1;
break;
case '1':
params.oneonly = 1;
break;
case 'l':
params.set_label = 1;
params.label = optarg;
break;
case 't':
params.set_type = 1;
if (CGPT_OK != SupportedType(optarg, &params.type_guid) &&
CGPT_OK != StrToGuid(optarg, &params.type_guid)) {
Error("invalid argument to -%c: %s\n", c, optarg);
errorcnt++;
}
break;
case 'u':
params.set_unique = 1;
if (CGPT_OK != StrToGuid(optarg, &params.unique_guid)) {
Error("invalid argument to -%c: %s\n", c, optarg);
errorcnt++;
}
break;
case 'M':
params.matchbuf = ReadFile(optarg, &params.matchlen);
if (!params.matchbuf || !params.matchlen) {
Error("Unable to read from %s\n", optarg);
errorcnt++;
}
// Go ahead and allocate space for the comparison too
params.comparebuf = (uint8_t *)malloc(params.matchlen);
if (!params.comparebuf) {
Error("Unable to allocate %" PRIu64 "bytes for comparison buffer\n",
params.matchlen);
errorcnt++;
}
break;
case 'O':
params.matchoffset = strtoull(optarg, &e, 0);
errorcnt += check_int_parse(c, e);
break;
case 'h':
Usage();
return CGPT_OK;
case '?':
Error("unrecognized option: -%c\n", optopt);
errorcnt++;
break;
case ':':
Error("missing argument to -%c\n", optopt);
errorcnt++;
break;
default:
errorcnt++;
break;
}
}
if (!params.set_unique && !params.set_type && !params.set_label) {
Error("You must specify at least one of -t, -u, or -l\n");
errorcnt++;
}
if (errorcnt)
{
Usage();
return CGPT_FAILED;
}
if (optind < argc) {
for (i=optind; i<argc; i++) {
params.drive_name = argv[i];
CgptFind(&params);
}
} else {
CgptFind(&params);
}
if (params.oneonly && params.hits != 1) {
return CGPT_FAILED;
}
if (params.match_partnum) {
return CGPT_OK;
}
return CGPT_FAILED;
}

View File

@@ -0,0 +1,87 @@
// Copyright (c) 2012 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.
#include <getopt.h>
#include <string.h>
#include "cgpt.h"
#include "vboot_host.h"
extern const char* progname;
static void Usage(void)
{
printf("\nUsage: %s legacy [OPTIONS] DRIVE\n\n"
"Switch GPT header signature to \"CHROMEOS\".\n\n"
"Options:\n"
" -D NUM Size (in bytes) of the disk where partitions reside\n"
" default 0, meaning partitions and GPT structs are\n"
" both on DRIVE\n"
" -e Switch GPT header signature back to \"EFI PART\"\n"
" -p Switch primary GPT header signature to \"IGNOREME\"\n"
"\n", progname);
}
int cmd_legacy(int argc, char *argv[]) {
CgptLegacyParams params;
memset(&params, 0, sizeof(params));
int c;
char* e = 0;
int errorcnt = 0;
opterr = 0; // quiet, you
while ((c=getopt(argc, argv, ":hepD:")) != -1)
{
switch (c)
{
case 'D':
params.drive_size = strtoull(optarg, &e, 0);
errorcnt += check_int_parse(c, e);
break;
case 'e':
if (params.mode) {
Error("Incompatible flags, pick either -e or -p\n");
errorcnt++;
}
params.mode = CGPT_LEGACY_MODE_EFIPART;
break;
case 'p':
if (params.mode) {
Error("Incompatible flags, pick either -e or -p\n");
errorcnt++;
}
params.mode = CGPT_LEGACY_MODE_IGNORE_PRIMARY;
break;
case 'h':
Usage();
return CGPT_OK;
case '?':
Error("unrecognized option: -%c\n", optopt);
errorcnt++;
break;
case ':':
Error("missing argument to -%c\n", optopt);
errorcnt++;
break;
default:
errorcnt++;
break;
}
}
if (errorcnt)
{
Usage();
return CGPT_FAILED;
}
if (optind >= argc) {
Usage();
return CGPT_FAILED;
}
params.drive_name = argv[optind];
return CgptLegacy(&params);
}

View File

@@ -0,0 +1,106 @@
// Copyright (c) 2012 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.
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <uuid/uuid.h>
#include "cgpt.h"
#include "vboot_host.h"
extern const char* progname;
static void Usage(void)
{
printf("\nUsage: %s prioritize [OPTIONS] DRIVE\n\n"
"Reorder the priority of all active ChromeOS Kernel partitions.\n\n"
"Options:\n"
" -D NUM Size (in bytes) of the disk where partitions reside\n"
" default 0, meaning partitions and GPT structs are\n"
" both on DRIVE\n"
" -P NUM Highest priority to use in the new ordering. The\n"
" other partitions will be ranked in decreasing\n"
" priority while preserving their original order.\n"
" If necessary the lowest ranks will be coalesced.\n"
" No active kernels will be lowered to priority 0.\n"
" -i NUM Specify the partition to make the highest in the new\n"
" order.\n"
" -f Friends of the given partition (those with the same\n"
" starting priority) are also updated to the new\n"
" highest priority.\n"
"\n"
"With no options this will set the lowest active kernel to\n"
"priority 1 while maintaining the original order.\n"
"\n", progname);
}
int cmd_prioritize(int argc, char *argv[]) {
CgptPrioritizeParams params;
memset(&params, 0, sizeof(params));
int c;
int errorcnt = 0;
char *e = 0;
opterr = 0; // quiet, you
while ((c=getopt(argc, argv, ":hi:fP:D:")) != -1)
{
switch (c)
{
case 'D':
params.drive_size = strtoull(optarg, &e, 0);
errorcnt += check_int_parse(c, e);
break;
case 'i':
params.set_partition = (uint32_t)strtoul(optarg, &e, 0);
errorcnt += check_int_parse(c, e);
break;
case 'f':
params.set_friends = 1;
break;
case 'P':
params.max_priority = (int)strtol(optarg, &e, 0);
errorcnt += check_int_parse(c, e);
errorcnt += check_int_limit(c, params.max_priority, 1, 15);
break;
case 'h':
Usage();
return CGPT_OK;
case '?':
Error("unrecognized option: -%c\n", optopt);
errorcnt++;
break;
case ':':
Error("missing argument to -%c\n", optopt);
errorcnt++;
break;
default:
errorcnt++;
break;
}
}
if (errorcnt)
{
Usage();
return CGPT_FAILED;
}
if (params.set_friends && !params.set_partition) {
Error("the -f option is only useful with the -i option\n");
Usage();
return CGPT_FAILED;
}
if (optind >= argc) {
Error("missing drive argument\n");
return CGPT_FAILED;
}
params.drive_name = argv[optind];
return CgptPrioritize(&params);
}

View File

@@ -0,0 +1,71 @@
// Copyright (c) 2012 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.
#include <getopt.h>
#include <string.h>
#include "cgpt.h"
#include "vboot_host.h"
extern const char* progname;
static void Usage(void)
{
printf("\nUsage: %s repair [OPTIONS] DRIVE\n\n"
"Repair damaged GPT headers and tables.\n\n"
"Options:\n"
" -D NUM Size (in bytes) of the disk where partitions reside\n"
" default 0, meaning partitions and GPT structs are\n"
" both on DRIVE\n"
" -v Verbose\n"
"\n", progname);
}
int cmd_repair(int argc, char *argv[]) {
CgptRepairParams params;
memset(&params, 0, sizeof(params));
int c;
char* e = 0;
int errorcnt = 0;
opterr = 0; // quiet, you
while ((c=getopt(argc, argv, ":hvD:")) != -1)
{
switch (c)
{
case 'D':
params.drive_size = strtoull(optarg, &e, 0);
errorcnt += check_int_parse(c, e);
break;
case 'v':
params.verbose++;
break;
case 'h':
Usage();
return CGPT_OK;
case '?':
Error("unrecognized option: -%c\n", optopt);
errorcnt++;
break;
case ':':
Error("missing argument to -%c\n", optopt);
errorcnt++;
break;
default:
errorcnt++;
break;
}
}
if (errorcnt)
{
Usage();
return CGPT_FAILED;
}
params.drive_name = argv[optind];
return CgptRepair(&params);
}

View File

@@ -0,0 +1,121 @@
// Copyright (c) 2012 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.
#define __STDC_FORMAT_MACROS
#include <getopt.h>
#include <inttypes.h>
#include <string.h>
#include "cgpt.h"
#include "vboot_host.h"
extern const char* progname;
static void Usage(void)
{
printf("\nUsage: %s show [OPTIONS] DRIVE\n\n"
"Display the GPT table\n\n"
"Options:\n"
" -D NUM Size (in bytes) of the disk where partitions reside\n"
" default 0, meaning partitions and GPT structs are\n"
" both on DRIVE\n"
" -n Numeric output only\n"
" -v Verbose output\n"
" -q Quick output\n"
" -i NUM Show specified partition only - pick one of:\n"
" -b beginning sector\n"
" -s partition size\n"
" -t type guid\n"
" -u unique guid\n"
" -l label\n"
" -S Successful flag\n"
" -T Tries flag\n"
" -P Priority flag\n"
" -R Required flag\n"
" -B Legacy Boot flag\n"
" -A raw 16-bit attribute value (bits 48-63)\n"
" -d Debug output (including invalid headers)\n"
"\n", progname);
}
int cmd_show(int argc, char *argv[]) {
CgptShowParams params;
memset(&params, 0, sizeof(params));
int c;
int errorcnt = 0;
char *e = 0;
opterr = 0; // quiet, you
while ((c=getopt(argc, argv, ":hnvqi:bstulSTPRBAdD:")) != -1)
{
switch (c)
{
case 'D':
params.drive_size = strtoull(optarg, &e, 0);
errorcnt += check_int_parse(c, e);
break;
case 'n':
params.numeric = 1;
break;
case 'v':
params.verbose = 1;
break;
case 'q':
params.quick = 1;
break;
case 'i':
params.partition = (uint32_t)strtoul(optarg, &e, 0);
errorcnt += check_int_parse(c, e);
break;
case 'b':
case 's':
case 't':
case 'u':
case 'l':
case 'S':
case 'T':
case 'P':
case 'R':
case 'B':
case 'A':
params.single_item = c;
break;
case 'd':
params.debug = 1;
break;
case 'h':
Usage();
return CGPT_OK;
case '?':
Error("unrecognized option: -%c\n", optopt);
errorcnt++;
break;
case ':':
Error("missing argument to -%c\n", optopt);
errorcnt++;
break;
default:
errorcnt++;
break;
}
}
if (errorcnt)
{
Usage();
return CGPT_FAILED;
}
if (optind >= argc) {
Error("missing drive argument\n");
Usage();
return CGPT_FAILED;
}
params.drive_name = argv[optind];
return CgptShow(&params);
}

View File

@@ -0,0 +1,60 @@
#!/bin/sh
# Copyright 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.
# Tests emerging all the ebuilds that use vboot_reference either as an
# ebuild dependency or by checking out the code and compiling it in a
# different ebuild. This is meant to be run from the chroot as part of testing
# a new change in vboot_reference.
# Required ebuilds:
TEST_EBUILDS="
sys-boot/chromeos-bootimage
sys-boot/chromeos-u-boot
sys-boot/coreboot
sys-boot/depthcharge
chromeos-base/chromeos-cryptohome
chromeos-base/chromeos-ec
chromeos-base/chromeos-installer
chromeos-base/chromeos-initramfs
chromeos-base/chromeos-login
chromeos-base/update_engine
chromeos-base/vboot_reference
chromeos-base/verity
"
set -e
# Check running inside the chroot.
if [ ! -e /etc/cros_chroot_version ]; then
echo "You must run this inside the chroot." >&2
exit 1
fi
# Detect the target board.
if [ "x${BOARD}" == "x" ]; then
if [ -e ~/trunk/src/scripts/.default_board ]; then
BOARD="`cat ~/trunk/src/scripts/.default_board`"
else
echo "You must pass BOARD environment variable or set a default board." >&2
exit 1
fi
fi
VBOOT_REF_DIR="$(dirname "$0")"
echo "Running tests for board '${BOARD}' from ${VBOOT_REF_DIR}"
cd "${VBOOT_REF_DIR}"
echo "Running make runtests..."
make runtests -j32
echo "Removing build artifacts."
rm -rf build build-main
echo "Running emerge tests (runs cros_workon start)."
# Ignore errors about already working on those repos.
cros_workon-${BOARD} start ${TEST_EBUILDS} || true
emerge-${BOARD} ${TEST_EBUILDS}

View File

@@ -0,0 +1,208 @@
/* 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.
*
* Externally-callable APIs
* (Firmware portion)
*/
#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 "2tpm_bootmode.h"
int vb2api_secdata_check(const struct vb2_context *ctx)
{
return vb2_secdata_check_crc(ctx);
}
int vb2api_secdata_create(struct vb2_context *ctx)
{
return vb2_secdata_create(ctx);
}
void vb2api_fail(struct vb2_context *ctx, uint8_t reason, uint8_t subcode)
{
/* Initialize the vboot context if it hasn't been yet */
vb2_init_context(ctx);
vb2_fail(ctx, reason, subcode);
}
int vb2api_fw_phase1(struct vb2_context *ctx)
{
int rv;
/* Initialize the vboot context if it hasn't been yet */
vb2_init_context(ctx);
/* Initialize NV context */
vb2_nv_init(ctx);
/*
* Handle caller-requested reboot due to secdata. Do this before we
* even look at secdata. If we fail because of a reboot loop we'll be
* the first failure so will get to set the recovery reason.
*/
if (!(ctx->flags & VB2_CONTEXT_SECDATA_WANTS_REBOOT)) {
/* No reboot requested */
vb2_nv_set(ctx, VB2_NV_TPM_REQUESTED_REBOOT, 0);
} else if (vb2_nv_get(ctx, VB2_NV_TPM_REQUESTED_REBOOT)) {
/*
* Reboot requested... again. Fool me once, shame on you.
* Fool me twice, shame on me. Fail into recovery to avoid
* a reboot loop.
*/
vb2_fail(ctx, VB2_RECOVERY_RO_TPM_REBOOT, 0);
} else {
/* Reboot requested for the first time */
vb2_nv_set(ctx, VB2_NV_TPM_REQUESTED_REBOOT, 1);
return VB2_ERROR_API_PHASE1_SECDATA_REBOOT;
}
/* Initialize secure data */
rv = vb2_secdata_init(ctx);
if (rv)
vb2_fail(ctx, VB2_RECOVERY_SECDATA_INIT, rv);
/* Load and parse the GBB header */
rv = vb2_fw_parse_gbb(ctx);
if (rv)
vb2_fail(ctx, VB2_RECOVERY_GBB_HEADER, rv);
/*
* Check for recovery. Note that this function returns void, since any
* errors result in requesting recovery. That's also why we don't
* return error from failures in the preceding two steps; those
* failures simply cause us to detect recovery mode here.
*/
vb2_check_recovery(ctx);
/* Check for dev switch */
rv = vb2_check_dev_switch(ctx);
if (rv && !(ctx->flags & VB2_CONTEXT_RECOVERY_MODE)) {
/*
* Error in dev switch processing, and we weren't already
* headed for recovery mode. Reboot into recovery mode, since
* it's too late to handle those errors this boot, and we need
* to take a different path through the dev switch checking
* code in that case.
*/
vb2_fail(ctx, VB2_RECOVERY_DEV_SWITCH, rv);
return rv;
}
/* Return error if recovery is needed */
if (ctx->flags & VB2_CONTEXT_RECOVERY_MODE) {
/* Always clear RAM when entering recovery mode */
ctx->flags |= VB2_CONTEXT_CLEAR_RAM;
return VB2_ERROR_API_PHASE1_RECOVERY;
}
return VB2_SUCCESS;
}
int vb2api_fw_phase2(struct vb2_context *ctx)
{
int rv;
/*
* Use the slot from the last boot if this is a resume. Do not set
* VB2_SD_STATUS_CHOSE_SLOT so the try counter is not decremented on
* failure as we are explicitly not attempting to boot from a new slot.
*/
if (ctx->flags & VB2_CONTEXT_S3_RESUME) {
struct vb2_shared_data *sd = vb2_get_sd(ctx);
/* Set the current slot to the last booted slot */
sd->fw_slot = vb2_nv_get(ctx, VB2_NV_FW_TRIED);
/* Set context flag if we're using slot B */
if (sd->fw_slot)
ctx->flags |= VB2_CONTEXT_FW_SLOT_B;
return VB2_SUCCESS;
}
/* Always clear RAM when entering developer mode */
if (ctx->flags & VB2_CONTEXT_DEVELOPER_MODE)
ctx->flags |= VB2_CONTEXT_CLEAR_RAM;
/* Check for explicit request to clear TPM */
rv = vb2_check_tpm_clear(ctx);
if (rv) {
vb2_fail(ctx, VB2_RECOVERY_TPM_CLEAR_OWNER, rv);
return rv;
}
/* Decide which firmware slot to try this boot */
rv = vb2_select_fw_slot(ctx);
if (rv) {
vb2_fail(ctx, VB2_RECOVERY_FW_SLOT, rv);
return rv;
}
return VB2_SUCCESS;
}
int vb2api_extend_hash(struct vb2_context *ctx,
const void *buf,
uint32_t size)
{
struct vb2_shared_data *sd = vb2_get_sd(ctx);
struct vb2_digest_context *dc = (struct vb2_digest_context *)
(ctx->workbuf + sd->workbuf_hash_offset);
/* Must have initialized hash digest work area */
if (!sd->workbuf_hash_size)
return VB2_ERROR_API_EXTEND_HASH_WORKBUF;
/* Don't extend past the data we expect to hash */
if (!size || size > sd->hash_remaining_size)
return VB2_ERROR_API_EXTEND_HASH_SIZE;
sd->hash_remaining_size -= size;
if (dc->using_hwcrypto)
return vb2ex_hwcrypto_digest_extend(buf, size);
else
return vb2_digest_extend(dc, buf, size);
}
int vb2api_get_pcr_digest(struct vb2_context *ctx,
enum vb2_pcr_digest which_digest,
uint8_t *dest,
uint32_t *dest_size)
{
const uint8_t *digest;
uint32_t digest_size;
switch (which_digest) {
case BOOT_MODE_PCR:
digest = vb2_get_boot_state_digest(ctx);
digest_size = VB2_SHA1_DIGEST_SIZE;
break;
case HWID_DIGEST_PCR:
digest = vb2_get_sd(ctx)->gbb_hwid_digest;
digest_size = VB2_GBB_HWID_DIGEST_SIZE;
break;
default:
return VB2_ERROR_API_PCR_DIGEST;
}
if (digest == NULL || *dest_size < digest_size)
return VB2_ERROR_API_PCR_DIGEST_BUF;
memcpy(dest, digest, digest_size);
if (digest_size < *dest_size)
memset(dest + digest_size, 0, *dest_size - digest_size);
*dest_size = digest_size;
return VB2_SUCCESS;
}

View File

@@ -0,0 +1,105 @@
/* 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.
*
* Common functions between firmware and kernel verified boot.
* (Firmware portion)
*/
#include "2sysincludes.h"
#include "2common.h"
#include "2rsa.h"
#include "2sha.h"
int vb2_safe_memcmp(const void *s1, const void *s2, size_t size)
{
const unsigned char *us1 = s1;
const unsigned char *us2 = s2;
int result = 0;
if (0 == size)
return 0;
/*
* Code snippet without data-dependent branch due to Nate Lawson
* (nate@root.org) of Root Labs.
*/
while (size--)
result |= *us1++ ^ *us2++;
return result != 0;
}
int vb2_align(uint8_t **ptr, uint32_t *size, uint32_t align, uint32_t want_size)
{
uintptr_t p = (uintptr_t)*ptr;
uintptr_t offs = p & (align - 1);
if (offs) {
offs = align - offs;
if (*size < offs)
return VB2_ERROR_ALIGN_BIGGER_THAN_SIZE;
*ptr += offs;
*size -= offs;
}
if (*size < want_size)
return VB2_ERROR_ALIGN_SIZE;
return VB2_SUCCESS;
}
void vb2_workbuf_init(struct vb2_workbuf *wb, uint8_t *buf, uint32_t size)
{
wb->buf = buf;
wb->size = size;
/* Align the buffer so allocations will be aligned */
if (vb2_align(&wb->buf, &wb->size, VB2_WORKBUF_ALIGN, 0))
wb->size = 0;
}
void *vb2_workbuf_alloc(struct vb2_workbuf *wb, uint32_t size)
{
uint8_t *ptr = wb->buf;
/* Round up size to work buffer alignment */
size = vb2_wb_round_up(size);
if (size > wb->size)
return NULL;
wb->buf += size;
wb->size -= size;
return ptr;
}
void *vb2_workbuf_realloc(struct vb2_workbuf *wb,
uint32_t oldsize,
uint32_t newsize)
{
/*
* Just free and allocate to update the size. No need to move/copy
* memory, since the new pointer is guaranteed to be the same as the
* old one. The new allocation can fail, if the new size is too big.
*/
vb2_workbuf_free(wb, oldsize);
return vb2_workbuf_alloc(wb, newsize);
}
void vb2_workbuf_free(struct vb2_workbuf *wb, uint32_t size)
{
/* Round up size to work buffer alignment */
size = vb2_wb_round_up(size);
wb->buf -= size;
wb->size += size;
}
ptrdiff_t vb2_offset_of(const void *base, const void *ptr)
{
return (uintptr_t)ptr - (uintptr_t)base;
}

View File

@@ -0,0 +1,29 @@
/* 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.
*/
#include "2sysincludes.h"
#include "2crc8.h"
uint8_t vb2_crc8(const void *vptr, uint32_t size)
{
const uint8_t *data = vptr;
unsigned crc = 0;
uint32_t i, j;
/*
* Calculate CRC-8 directly. A table-based algorithm would be faster,
* but for only a few bytes it isn't worth the code size.
*/
for (j = size; j; j--, data++) {
crc ^= (*data << 8);
for(i = 8; i; i--) {
if (crc & 0x8000)
crc ^= (0x1070 << 3);
crc <<= 1;
}
}
return (uint8_t)(crc >> 8);
}

View File

@@ -0,0 +1,60 @@
/* Copyright 2016 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.
*/
#include "2sysincludes.h"
#include "2sha.h"
#include "2hmac.h"
int hmac(enum vb2_hash_algorithm alg,
const void *key, uint32_t key_size,
const void *msg, uint32_t msg_size,
uint8_t *mac, uint32_t mac_size)
{
uint32_t block_size;
uint32_t digest_size;
uint8_t k[VB2_MAX_BLOCK_SIZE];
uint8_t o_pad[VB2_MAX_BLOCK_SIZE];
uint8_t i_pad[VB2_MAX_BLOCK_SIZE];
uint8_t b[VB2_MAX_DIGEST_SIZE];
struct vb2_digest_context dc;
int i;
if (!key | !msg | !mac)
return -1;
digest_size = vb2_digest_size(alg);
block_size = vb2_hash_block_size(alg);
if (!digest_size || !block_size)
return -1;
if (mac_size < digest_size)
return -1;
if (key_size > block_size) {
vb2_digest_buffer((uint8_t *)key, key_size, alg, k, block_size);
key_size = digest_size;
} else {
memcpy(k, key, key_size);
}
if (key_size < block_size)
memset(k + key_size, 0, block_size - key_size);
for (i = 0; i < block_size; i++) {
o_pad[i] = 0x5c ^ k[i];
i_pad[i] = 0x36 ^ k[i];
}
vb2_digest_init(&dc, alg);
vb2_digest_extend(&dc, i_pad, block_size);
vb2_digest_extend(&dc, msg, msg_size);
vb2_digest_finalize(&dc, b, digest_size);
vb2_digest_init(&dc, alg);
vb2_digest_extend(&dc, o_pad, block_size);
vb2_digest_extend(&dc, b, digest_size);
vb2_digest_finalize(&dc, mac, mac_size);
return 0;
}

View File

@@ -0,0 +1,413 @@
/* 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"
int vb2_validate_gbb_signature(uint8_t *sig) {
const static uint8_t sig_xor[VB2_GBB_SIGNATURE_SIZE] =
VB2_GBB_XOR_SIGNATURE;
int i;
for (i = 0; i < VB2_GBB_SIGNATURE_SIZE; i++) {
if (sig[i] != (sig_xor[i] ^ VB2_GBB_XOR_CHARS[i]))
return VB2_ERROR_GBB_MAGIC;
}
return VB2_SUCCESS;
}
void vb2_workbuf_from_ctx(struct vb2_context *ctx, struct vb2_workbuf *wb)
{
vb2_workbuf_init(wb, ctx->workbuf + ctx->workbuf_used,
ctx->workbuf_size - ctx->workbuf_used);
}
void vb2_set_workbuf_used(struct vb2_context *ctx, uint32_t used)
{
ctx->workbuf_used = vb2_wb_round_up(used);
}
int vb2_read_gbb_header(struct vb2_context *ctx, struct vb2_gbb_header *gbb)
{
int rv;
/* Read the entire header */
rv = vb2ex_read_resource(ctx, VB2_RES_GBB, 0, gbb, sizeof(*gbb));
if (rv)
return rv;
/* Make sure it's really a GBB */
rv = vb2_validate_gbb_signature(gbb->signature);
if (rv)
return rv;
/* Check for compatible version */
if (gbb->major_version != VB2_GBB_MAJOR_VER)
return VB2_ERROR_GBB_VERSION;
/* Current code is not backwards-compatible to 1.1 headers or older */
if (gbb->minor_version < VB2_GBB_MINOR_VER)
return VB2_ERROR_GBB_TOO_OLD;
/*
* Header size should be at least as big as we expect. It could be
* bigger, if the header has grown.
*/
if (gbb->header_size < sizeof(*gbb))
return VB2_ERROR_GBB_HEADER_SIZE;
return VB2_SUCCESS;
}
void vb2_fail(struct vb2_context *ctx, uint8_t reason, uint8_t subcode)
{
struct vb2_shared_data *sd = vb2_get_sd(ctx);
/* If NV data hasn't been initialized, initialize it now */
if (!(sd->status & VB2_SD_STATUS_NV_INIT))
vb2_nv_init(ctx);
/* See if we were far enough in the boot process to choose a slot */
if (sd->status & VB2_SD_STATUS_CHOSE_SLOT) {
/* Boot failed */
vb2_nv_set(ctx, VB2_NV_FW_RESULT, VB2_FW_RESULT_FAILURE);
/* Use up remaining tries */
vb2_nv_set(ctx, VB2_NV_TRY_COUNT, 0);
/*
* Try the other slot next time. We'll alternate
* between slots, which may help if one or both slots is
* flaky.
*/
vb2_nv_set(ctx, VB2_NV_TRY_NEXT, 1 - sd->fw_slot);
/*
* If we didn't try the other slot last boot, or we tried it
* and it didn't fail, try it next boot.
*/
if (sd->last_fw_slot != 1 - sd->fw_slot ||
sd->last_fw_result != VB2_FW_RESULT_FAILURE)
return;
}
/*
* If we're still here, we failed before choosing a slot, or both
* this slot and the other slot failed in successive boots. So we
* need to go to recovery.
*
* Set a recovery reason and subcode only if they're not already set.
* If recovery is already requested, it's a more specific error code
* than later code is providing and we shouldn't overwrite it.
*/
VB2_DEBUG("Need recovery, reason: %#x / %#x\n", reason, subcode);
if (!vb2_nv_get(ctx, VB2_NV_RECOVERY_REQUEST)) {
vb2_nv_set(ctx, VB2_NV_RECOVERY_REQUEST, reason);
vb2_nv_set(ctx, VB2_NV_RECOVERY_SUBCODE, subcode);
}
}
int vb2_init_context(struct vb2_context *ctx)
{
struct vb2_shared_data *sd = vb2_get_sd(ctx);
/* Don't do anything if the context has already been initialized */
if (ctx->workbuf_used)
return VB2_SUCCESS;
/*
* Workbuf had better be big enough for our shared data struct and
* aligned. Not much we can do if it isn't; we'll die before we can
* store a recovery reason.
*/
if (ctx->workbuf_size < sizeof(*sd))
return VB2_ERROR_INITCTX_WORKBUF_SMALL;
if (!vb2_aligned(ctx->workbuf, VB2_WORKBUF_ALIGN))
return VB2_ERROR_INITCTX_WORKBUF_ALIGN;
/* Initialize the shared data at the start of the work buffer */
memset(sd, 0, sizeof(*sd));
ctx->workbuf_used = vb2_wb_round_up(sizeof(*sd));
return VB2_SUCCESS;
}
void vb2_check_recovery(struct vb2_context *ctx)
{
struct vb2_shared_data *sd = vb2_get_sd(ctx);
uint32_t reason = vb2_nv_get(ctx, VB2_NV_RECOVERY_REQUEST);
uint32_t subcode = vb2_nv_get(ctx, VB2_NV_RECOVERY_SUBCODE);
VB2_DEBUG("Recovery reason from previous boot: %#x / %#x\n",
reason, subcode);
/*
* Sets the current recovery request, unless there's already been a
* failure earlier in the boot process.
*/
if (!sd->recovery_reason)
sd->recovery_reason = reason;
/* Clear request and subcode so we don't get stuck in recovery mode */
vb2_nv_set(ctx, VB2_NV_RECOVERY_REQUEST, VB2_RECOVERY_NOT_REQUESTED);
vb2_nv_set(ctx, VB2_NV_RECOVERY_SUBCODE, VB2_RECOVERY_NOT_REQUESTED);
if (ctx->flags & VB2_CONTEXT_FORCE_RECOVERY_MODE) {
VB2_DEBUG("Recovery was requested manually\n");
if (subcode && !sd->recovery_reason)
/*
* Recovery was requested at 'broken' screen.
* Promote subcode to reason.
*/
sd->recovery_reason = subcode;
else
/* Recovery was forced. Override recovery reason */
sd->recovery_reason = VB2_RECOVERY_RO_MANUAL;
sd->flags |= VB2_SD_FLAG_MANUAL_RECOVERY;
}
/* If recovery reason is non-zero, tell caller we need recovery mode */
if (sd->recovery_reason) {
ctx->flags |= VB2_CONTEXT_RECOVERY_MODE;
VB2_DEBUG("We have a recovery request: %#x / %#x\n",
sd->recovery_reason,
vb2_nv_get(ctx, VB2_NV_RECOVERY_SUBCODE));
}
}
int vb2_fw_parse_gbb(struct vb2_context *ctx)
{
struct vb2_shared_data *sd = vb2_get_sd(ctx);
struct vb2_gbb_header *gbb;
struct vb2_workbuf wb;
int rv;
vb2_workbuf_from_ctx(ctx, &wb);
/* Read GBB into next chunk of work buffer */
gbb = vb2_workbuf_alloc(&wb, sizeof(*gbb));
if (!gbb)
return VB2_ERROR_GBB_WORKBUF;
rv = vb2_read_gbb_header(ctx, gbb);
if (rv)
return rv;
/* Extract the only things we care about at firmware time */
sd->gbb_flags = gbb->flags;
sd->gbb_rootkey_offset = gbb->rootkey_offset;
sd->gbb_rootkey_size = gbb->rootkey_size;
memcpy(sd->gbb_hwid_digest, gbb->hwid_digest, VB2_GBB_HWID_DIGEST_SIZE);
return VB2_SUCCESS;
}
int vb2_check_dev_switch(struct vb2_context *ctx)
{
struct vb2_shared_data *sd = vb2_get_sd(ctx);
uint32_t flags = 0;
uint32_t old_flags;
int is_dev = 0;
int use_secdata = 1;
int rv;
/* Read secure flags */
rv = vb2_secdata_get(ctx, VB2_SECDATA_FLAGS, &flags);
if (rv) {
if (ctx->flags & VB2_CONTEXT_RECOVERY_MODE) {
/*
* Recovery mode needs to check other ways developer
* mode can be enabled, so don't give up yet. But
* since we can't read secdata, assume dev mode was
* disabled.
*/
use_secdata = 0;
flags = 0;
} else {
/* Normal mode simply fails */
return rv;
}
}
old_flags = flags;
/* Handle dev disable request */
if (use_secdata && vb2_nv_get(ctx, VB2_NV_DISABLE_DEV_REQUEST)) {
flags &= ~VB2_SECDATA_FLAG_DEV_MODE;
/* Clear the request */
vb2_nv_set(ctx, VB2_NV_DISABLE_DEV_REQUEST, 0);
}
/*
* Check if we've been asked by the caller to disable dev mode. Note
* that hardware switch and GBB flag will take precedence over this.
*/
if (ctx->flags & VB2_DISABLE_DEVELOPER_MODE)
flags &= ~VB2_SECDATA_FLAG_DEV_MODE;
/* Check virtual dev switch */
if (flags & VB2_SECDATA_FLAG_DEV_MODE)
is_dev = 1;
/* Handle forcing dev mode via physical switch */
if (ctx->flags & VB2_CONTEXT_FORCE_DEVELOPER_MODE)
is_dev = 1;
/* Check if GBB is forcing dev mode */
if (sd->gbb_flags & VB2_GBB_FLAG_FORCE_DEV_SWITCH_ON)
is_dev = 1;
/* Handle whichever mode we end up in */
if (is_dev) {
/* Developer mode */
sd->flags |= VB2_SD_DEV_MODE_ENABLED;
ctx->flags |= VB2_CONTEXT_DEVELOPER_MODE;
flags |= VB2_SECDATA_FLAG_LAST_BOOT_DEVELOPER;
} else {
/* Normal mode */
flags &= ~VB2_SECDATA_FLAG_LAST_BOOT_DEVELOPER;
/*
* Disable dev_boot_* flags. This ensures they will be
* initially disabled if the user later transitions back into
* developer mode.
*/
vb2_nv_set(ctx, VB2_NV_DEV_BOOT_USB, 0);
vb2_nv_set(ctx, VB2_NV_DEV_BOOT_LEGACY, 0);
vb2_nv_set(ctx, VB2_NV_DEV_BOOT_SIGNED_ONLY, 0);
vb2_nv_set(ctx, VB2_NV_DEV_BOOT_FASTBOOT_FULL_CAP, 0);
vb2_nv_set(ctx, VB2_NV_DEV_DEFAULT_BOOT, 0);
vb2_nv_set(ctx, VB2_NV_FASTBOOT_UNLOCK_IN_FW, 0);
}
if (ctx->flags & VB2_CONTEXT_FORCE_WIPEOUT_MODE)
vb2_nv_set(ctx, VB2_NV_REQ_WIPEOUT, 1);
if (flags != old_flags) {
/*
* Just changed dev mode state. Clear TPM owner. This must be
* done here instead of simply passing a flag to
* vb2_check_tpm_clear(), because we don't want to update
* last_boot_developer and then fail to clear the TPM owner.
*
* Note that we do this even if we couldn't read secdata, since
* the TPM owner and secdata may be independent, and we want
* the owner to be cleared if *this boot* is different than the
* last one (perhaps due to GBB or hardware override).
*/
rv = vb2ex_tpm_clear_owner(ctx);
if (use_secdata) {
/* Check for failure to clear owner */
if (rv) {
/*
* Note that this truncates rv to 8 bit. Which
* is not as useful as the full error code, but
* we don't have NVRAM space to store the full
* 32-bit code.
*/
vb2_fail(ctx, VB2_RECOVERY_TPM_CLEAR_OWNER, rv);
return rv;
}
/* Save new flags */
rv = vb2_secdata_set(ctx, VB2_SECDATA_FLAGS, flags);
if (rv)
return rv;
}
}
return VB2_SUCCESS;
}
int vb2_check_tpm_clear(struct vb2_context *ctx)
{
int rv;
/* Check if we've been asked to clear the owner */
if (!vb2_nv_get(ctx, VB2_NV_CLEAR_TPM_OWNER_REQUEST))
return VB2_SUCCESS; /* No need to clear */
/* Request applies one time only */
vb2_nv_set(ctx, VB2_NV_CLEAR_TPM_OWNER_REQUEST, 0);
/* Try clearing */
rv = vb2ex_tpm_clear_owner(ctx);
if (rv) {
/*
* Note that this truncates rv to 8 bit. Which is not as
* useful as the full error code, but we don't have NVRAM space
* to store the full 32-bit code.
*/
vb2_fail(ctx, VB2_RECOVERY_TPM_CLEAR_OWNER, rv);
return rv;
}
/* Clear successful */
vb2_nv_set(ctx, VB2_NV_CLEAR_TPM_OWNER_DONE, 1);
return VB2_SUCCESS;
}
int vb2_select_fw_slot(struct vb2_context *ctx)
{
struct vb2_shared_data *sd = vb2_get_sd(ctx);
uint32_t tries;
/* Get result of last boot */
sd->last_fw_slot = vb2_nv_get(ctx, VB2_NV_FW_TRIED);
sd->last_fw_result = vb2_nv_get(ctx, VB2_NV_FW_RESULT);
/* Save to the previous result fields in NV storage */
vb2_nv_set(ctx, VB2_NV_FW_PREV_TRIED, sd->last_fw_slot);
vb2_nv_set(ctx, VB2_NV_FW_PREV_RESULT, sd->last_fw_result);
/* Clear result, since we don't know what will happen this boot */
vb2_nv_set(ctx, VB2_NV_FW_RESULT, VB2_FW_RESULT_UNKNOWN);
/* Get slot to try */
sd->fw_slot = vb2_nv_get(ctx, VB2_NV_TRY_NEXT);
/* Check try count */
tries = vb2_nv_get(ctx, VB2_NV_TRY_COUNT);
if (sd->last_fw_result == VB2_FW_RESULT_TRYING &&
sd->last_fw_slot == sd->fw_slot &&
tries == 0) {
/*
* We used up our last try on the previous boot, so fall back
* to the other slot this boot.
*/
sd->fw_slot = 1 - sd->fw_slot;
vb2_nv_set(ctx, VB2_NV_TRY_NEXT, sd->fw_slot);
}
if (tries > 0) {
/* Still trying this firmware */
vb2_nv_set(ctx, VB2_NV_FW_RESULT, VB2_FW_RESULT_TRYING);
/* Decrement non-zero try count, unless told not to */
if (!(ctx->flags & VB2_CONTEXT_NOFAIL_BOOT))
vb2_nv_set(ctx, VB2_NV_TRY_COUNT, tries - 1);
}
/* Store the slot we're trying */
vb2_nv_set(ctx, VB2_NV_FW_TRIED, sd->fw_slot);
/* Set context flag if we're using slot B */
if (sd->fw_slot)
ctx->flags |= VB2_CONTEXT_FW_SLOT_B;
/* Set status flag */
sd->status |= VB2_SD_STATUS_CHOSE_SLOT;
return VB2_SUCCESS;
}

View File

@@ -0,0 +1,433 @@
/* 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.
*/
/* Non-volatile storage routines */
#include "2sysincludes.h"
#include "2common.h"
#include "2crc8.h"
#include "2misc.h"
#include "2nvstorage.h"
#include "2nvstorage_fields.h"
static void vb2_nv_regen_crc(struct vb2_context *ctx)
{
const int offs = ctx->flags & VB2_CONTEXT_NVDATA_V2 ?
VB2_NV_OFFS_CRC_V2 : VB2_NV_OFFS_CRC_V1;
ctx->nvdata[offs] = vb2_crc8(ctx->nvdata, offs);
ctx->flags |= VB2_CONTEXT_NVDATA_CHANGED;
}
int vb2_nv_get_size(const struct vb2_context *ctx)
{
return ctx->flags & VB2_CONTEXT_NVDATA_V2 ?
VB2_NVDATA_SIZE_V2 : VB2_NVDATA_SIZE;
}
/**
* Check the CRC of the non-volatile storage context.
*
* Use this if reading from non-volatile storage may be flaky, and you want to
* retry reading it several times.
*
* This may be called before vb2_context_init().
*
* @param ctx Context pointer
* @return VB2_SUCCESS, or non-zero error code if error.
*/
int vb2_nv_check_crc(const struct vb2_context *ctx)
{
const uint8_t *p = ctx->nvdata;
const int offs = ctx->flags & VB2_CONTEXT_NVDATA_V2 ?
VB2_NV_OFFS_CRC_V2 : VB2_NV_OFFS_CRC_V1;
const int sig = ctx->flags & VB2_CONTEXT_NVDATA_V2 ?
VB2_NV_HEADER_SIGNATURE_V2 : VB2_NV_HEADER_SIGNATURE_V1;
/* Check header */
if (sig != (p[VB2_NV_OFFS_HEADER] & VB2_NV_HEADER_SIGNATURE_MASK))
return VB2_ERROR_NV_HEADER;
/* Check CRC */
if (vb2_crc8(p, offs) != p[offs])
return VB2_ERROR_NV_CRC;
return VB2_SUCCESS;
}
void vb2_nv_init(struct vb2_context *ctx)
{
const int sig = ctx->flags & VB2_CONTEXT_NVDATA_V2 ?
VB2_NV_HEADER_SIGNATURE_V2 : VB2_NV_HEADER_SIGNATURE_V1;
struct vb2_shared_data *sd = vb2_get_sd(ctx);
uint8_t *p = ctx->nvdata;
/* Check data for consistency */
if (vb2_nv_check_crc(ctx) != VB2_SUCCESS) {
/* Data is inconsistent (bad CRC or header); reset defaults */
memset(p, 0, VB2_NVDATA_SIZE_V2);
p[VB2_NV_OFFS_HEADER] = (sig |
VB2_NV_HEADER_FW_SETTINGS_RESET |
VB2_NV_HEADER_KERNEL_SETTINGS_RESET);
/* Regenerate CRC */
vb2_nv_regen_crc(ctx);
/*
* Set status flag.
*
* Note that early in some calling sequences, shared data may
* not be available. For example, if there is an error
* allocating the context work buffer, and we're trying to
* initialize non-volatile storage so we can write a recovery
* request. In that case, sd will be NULL. So while we don't
* usually need to check for that in other library functions,
* here we do.
*/
if (sd)
sd->status |= VB2_SD_STATUS_NV_REINIT;
/* TODO: unit test for status flag being set */
}
if (sd)
sd->status |= VB2_SD_STATUS_NV_INIT;
}
/* Macro for vb2_nv_get() single-bit settings to reduce duplicate code. */
#define GETBIT(offs, mask) (p[offs] & mask ? 1 : 0)
uint32_t vb2_nv_get(struct vb2_context *ctx, enum vb2_nv_param param)
{
const uint8_t *p = ctx->nvdata;
/*
* TODO: We could reduce the binary size for this code by #ifdef'ing
* out the params not used by firmware verification.
*/
switch (param) {
case VB2_NV_FIRMWARE_SETTINGS_RESET:
return GETBIT(VB2_NV_OFFS_HEADER,
VB2_NV_HEADER_FW_SETTINGS_RESET);
case VB2_NV_KERNEL_SETTINGS_RESET:
return GETBIT(VB2_NV_OFFS_HEADER,
VB2_NV_HEADER_KERNEL_SETTINGS_RESET);
case VB2_NV_DEBUG_RESET_MODE:
return GETBIT(VB2_NV_OFFS_BOOT, VB2_NV_BOOT_DEBUG_RESET);
case VB2_NV_TRY_NEXT:
return GETBIT(VB2_NV_OFFS_BOOT2, VB2_NV_BOOT2_TRY_NEXT);
case VB2_NV_TRY_COUNT:
return p[VB2_NV_OFFS_BOOT] & VB2_NV_BOOT_TRY_COUNT_MASK;
case VB2_NV_FW_TRIED:
return GETBIT(VB2_NV_OFFS_BOOT2, VB2_NV_BOOT2_TRIED);
case VB2_NV_FW_RESULT:
return p[VB2_NV_OFFS_BOOT2] & VB2_NV_BOOT2_RESULT_MASK;
case VB2_NV_FW_PREV_TRIED:
return GETBIT(VB2_NV_OFFS_BOOT2, VB2_NV_BOOT2_PREV_TRIED);
case VB2_NV_FW_PREV_RESULT:
return (p[VB2_NV_OFFS_BOOT2] & VB2_NV_BOOT2_PREV_RESULT_MASK)
>> VB2_NV_BOOT2_PREV_RESULT_SHIFT;
case VB2_NV_RECOVERY_REQUEST:
return p[VB2_NV_OFFS_RECOVERY];
case VB2_NV_RECOVERY_SUBCODE:
return p[VB2_NV_OFFS_RECOVERY_SUBCODE];
case VB2_NV_LOCALIZATION_INDEX:
return p[VB2_NV_OFFS_LOCALIZATION];
case VB2_NV_KERNEL_FIELD:
return p[VB2_NV_OFFS_KERNEL1] | (p[VB2_NV_OFFS_KERNEL2] << 8);
case VB2_NV_DEV_BOOT_USB:
return GETBIT(VB2_NV_OFFS_DEV, VB2_NV_DEV_FLAG_USB);
case VB2_NV_DEV_BOOT_LEGACY:
return GETBIT(VB2_NV_OFFS_DEV, VB2_NV_DEV_FLAG_LEGACY);
case VB2_NV_DEV_BOOT_SIGNED_ONLY:
return GETBIT(VB2_NV_OFFS_DEV, VB2_NV_DEV_FLAG_SIGNED_ONLY);
case VB2_NV_DEV_BOOT_FASTBOOT_FULL_CAP:
return GETBIT(VB2_NV_OFFS_DEV,
VB2_NV_DEV_FLAG_FASTBOOT_FULL_CAP);
case VB2_NV_DEV_DEFAULT_BOOT:
return (p[VB2_NV_OFFS_DEV] & VB2_NV_DEV_FLAG_DEFAULT_BOOT)
>> VB2_NV_DEV_DEFAULT_BOOT_SHIFT;
case VB2_NV_DEV_ENABLE_UDC:
return GETBIT(VB2_NV_OFFS_DEV, VB2_NV_DEV_FLAG_UDC);
case VB2_NV_DISABLE_DEV_REQUEST:
return GETBIT(VB2_NV_OFFS_BOOT, VB2_NV_BOOT_DISABLE_DEV);
case VB2_NV_OPROM_NEEDED:
return GETBIT(VB2_NV_OFFS_BOOT, VB2_NV_BOOT_OPROM_NEEDED);
case VB2_NV_BACKUP_NVRAM_REQUEST:
return GETBIT(VB2_NV_OFFS_BOOT, VB2_NV_BOOT_BACKUP_NVRAM);
case VB2_NV_CLEAR_TPM_OWNER_REQUEST:
return GETBIT(VB2_NV_OFFS_TPM, VB2_NV_TPM_CLEAR_OWNER_REQUEST);
case VB2_NV_CLEAR_TPM_OWNER_DONE:
return GETBIT(VB2_NV_OFFS_TPM, VB2_NV_TPM_CLEAR_OWNER_DONE);
case VB2_NV_TPM_REQUESTED_REBOOT:
return GETBIT(VB2_NV_OFFS_TPM, VB2_NV_TPM_REBOOTED);
case VB2_NV_REQ_WIPEOUT:
return GETBIT(VB2_NV_OFFS_HEADER , VB2_NV_HEADER_WIPEOUT);
case VB2_NV_FASTBOOT_UNLOCK_IN_FW:
return GETBIT(VB2_NV_OFFS_MISC, VB2_NV_MISC_UNLOCK_FASTBOOT);
case VB2_NV_BOOT_ON_AC_DETECT:
return GETBIT(VB2_NV_OFFS_MISC, VB2_NV_MISC_BOOT_ON_AC_DETECT);
case VB2_NV_TRY_RO_SYNC:
return GETBIT(VB2_NV_OFFS_MISC, VB2_NV_MISC_TRY_RO_SYNC);
case VB2_NV_BATTERY_CUTOFF_REQUEST:
return GETBIT(VB2_NV_OFFS_MISC, VB2_NV_MISC_BATTERY_CUTOFF);
case VB2_NV_KERNEL_MAX_ROLLFORWARD:
return (p[VB2_NV_OFFS_KERNEL_MAX_ROLLFORWARD1]
| (p[VB2_NV_OFFS_KERNEL_MAX_ROLLFORWARD2] << 8)
| (p[VB2_NV_OFFS_KERNEL_MAX_ROLLFORWARD3] << 16)
| (p[VB2_NV_OFFS_KERNEL_MAX_ROLLFORWARD4] << 24));
case VB2_NV_FW_MAX_ROLLFORWARD:
/* Field only present in V2 */
if (!(ctx->flags & VB2_CONTEXT_NVDATA_V2))
return VB2_FW_MAX_ROLLFORWARD_V1_DEFAULT;
return (p[VB2_NV_OFFS_FW_MAX_ROLLFORWARD1]
| (p[VB2_NV_OFFS_FW_MAX_ROLLFORWARD2] << 8)
| (p[VB2_NV_OFFS_FW_MAX_ROLLFORWARD3] << 16)
| (p[VB2_NV_OFFS_FW_MAX_ROLLFORWARD4] << 24));
}
/*
* Put default return outside the switch() instead of in default:, so
* that adding a new param will cause a compiler warning.
*/
return 0;
}
#undef GETBIT
/* Macro for vb2_nv_set() single-bit settings to reduce duplicate code. */
#define SETBIT(offs, mask) \
{ if (value) p[offs] |= mask; else p[offs] &= ~mask; }
void vb2_nv_set(struct vb2_context *ctx,
enum vb2_nv_param param,
uint32_t value)
{
uint8_t *p = ctx->nvdata;
/* If not changing the value, don't regenerate the CRC. */
if (vb2_nv_get(ctx, param) == value)
return;
/*
* TODO: We could reduce the binary size for this code by #ifdef'ing
* out the params not used by firmware verification.
*/
switch (param) {
case VB2_NV_FIRMWARE_SETTINGS_RESET:
SETBIT(VB2_NV_OFFS_HEADER, VB2_NV_HEADER_FW_SETTINGS_RESET);
break;
case VB2_NV_KERNEL_SETTINGS_RESET:
SETBIT(VB2_NV_OFFS_HEADER, VB2_NV_HEADER_KERNEL_SETTINGS_RESET);
break;
case VB2_NV_DEBUG_RESET_MODE:
SETBIT(VB2_NV_OFFS_BOOT, VB2_NV_BOOT_DEBUG_RESET);
break;
case VB2_NV_TRY_NEXT:
SETBIT(VB2_NV_OFFS_BOOT2, VB2_NV_BOOT2_TRY_NEXT);
break;
case VB2_NV_TRY_COUNT:
/* Clip to valid range. */
if (value > VB2_NV_BOOT_TRY_COUNT_MASK)
value = VB2_NV_BOOT_TRY_COUNT_MASK;
p[VB2_NV_OFFS_BOOT] &= ~VB2_NV_BOOT_TRY_COUNT_MASK;
p[VB2_NV_OFFS_BOOT] |= (uint8_t)value;
break;
case VB2_NV_FW_TRIED:
SETBIT(VB2_NV_OFFS_BOOT2, VB2_NV_BOOT2_TRIED);
break;
case VB2_NV_FW_RESULT:
/* Map out of range values to unknown */
if (value > VB2_NV_BOOT2_RESULT_MASK)
value = VB2_FW_RESULT_UNKNOWN;
p[VB2_NV_OFFS_BOOT2] &= ~VB2_NV_BOOT2_RESULT_MASK;
p[VB2_NV_OFFS_BOOT2] |= (uint8_t)value;
break;
case VB2_NV_FW_PREV_TRIED:
SETBIT(VB2_NV_OFFS_BOOT2, VB2_NV_BOOT2_PREV_TRIED);
break;
case VB2_NV_FW_PREV_RESULT:
/* Map out of range values to unknown */
if (value > VB2_NV_BOOT2_RESULT_MASK)
value = VB2_FW_RESULT_UNKNOWN;
p[VB2_NV_OFFS_BOOT2] &= ~VB2_NV_BOOT2_PREV_RESULT_MASK;
p[VB2_NV_OFFS_BOOT2] |=
(uint8_t)(value << VB2_NV_BOOT2_PREV_RESULT_SHIFT);
break;
case VB2_NV_RECOVERY_REQUEST:
/*
* Map values outside the valid range to the legacy reason,
* since we can't determine if we're called from kernel or user
* mode.
*/
if (value > 0xff)
value = VB2_RECOVERY_LEGACY;
p[VB2_NV_OFFS_RECOVERY] = (uint8_t)value;
break;
case VB2_NV_RECOVERY_SUBCODE:
p[VB2_NV_OFFS_RECOVERY_SUBCODE] = (uint8_t)value;
break;
case VB2_NV_LOCALIZATION_INDEX:
/* Map values outside the valid range to the default index. */
if (value > 0xFF)
value = 0;
p[VB2_NV_OFFS_LOCALIZATION] = (uint8_t)value;
break;
case VB2_NV_KERNEL_FIELD:
p[VB2_NV_OFFS_KERNEL1] = (uint8_t)(value);
p[VB2_NV_OFFS_KERNEL2] = (uint8_t)(value >> 8);
break;
case VB2_NV_DEV_BOOT_USB:
SETBIT(VB2_NV_OFFS_DEV, VB2_NV_DEV_FLAG_USB);
break;
case VB2_NV_DEV_BOOT_LEGACY:
SETBIT(VB2_NV_OFFS_DEV, VB2_NV_DEV_FLAG_LEGACY);
break;
case VB2_NV_DEV_BOOT_SIGNED_ONLY:
SETBIT(VB2_NV_OFFS_DEV, VB2_NV_DEV_FLAG_SIGNED_ONLY);
break;
case VB2_NV_DEV_BOOT_FASTBOOT_FULL_CAP:
SETBIT(VB2_NV_OFFS_DEV, VB2_NV_DEV_FLAG_FASTBOOT_FULL_CAP);
break;
case VB2_NV_DEV_DEFAULT_BOOT:
/* Map out of range values to disk */
if (value > (VB2_NV_DEV_FLAG_DEFAULT_BOOT >>
VB2_NV_DEV_DEFAULT_BOOT_SHIFT))
value = VB2_DEV_DEFAULT_BOOT_DISK;
p[VB2_NV_OFFS_DEV] &= ~VB2_NV_DEV_FLAG_DEFAULT_BOOT;
p[VB2_NV_OFFS_DEV] |=
(uint8_t)(value << VB2_NV_DEV_DEFAULT_BOOT_SHIFT);
break;
case VB2_NV_DEV_ENABLE_UDC:
SETBIT(VB2_NV_OFFS_DEV, VB2_NV_DEV_FLAG_UDC);
break;
case VB2_NV_DISABLE_DEV_REQUEST:
SETBIT(VB2_NV_OFFS_BOOT, VB2_NV_BOOT_DISABLE_DEV);
break;
case VB2_NV_OPROM_NEEDED:
SETBIT(VB2_NV_OFFS_BOOT, VB2_NV_BOOT_OPROM_NEEDED);
break;
case VB2_NV_BACKUP_NVRAM_REQUEST:
SETBIT(VB2_NV_OFFS_BOOT, VB2_NV_BOOT_BACKUP_NVRAM);
break;
case VB2_NV_CLEAR_TPM_OWNER_REQUEST:
SETBIT(VB2_NV_OFFS_TPM, VB2_NV_TPM_CLEAR_OWNER_REQUEST);
break;
case VB2_NV_CLEAR_TPM_OWNER_DONE:
SETBIT(VB2_NV_OFFS_TPM, VB2_NV_TPM_CLEAR_OWNER_DONE);
break;
case VB2_NV_TPM_REQUESTED_REBOOT:
SETBIT(VB2_NV_OFFS_TPM, VB2_NV_TPM_REBOOTED);
break;
case VB2_NV_REQ_WIPEOUT:
SETBIT(VB2_NV_OFFS_HEADER , VB2_NV_HEADER_WIPEOUT);
break;
case VB2_NV_FASTBOOT_UNLOCK_IN_FW:
SETBIT(VB2_NV_OFFS_MISC, VB2_NV_MISC_UNLOCK_FASTBOOT);
break;
case VB2_NV_BOOT_ON_AC_DETECT:
SETBIT(VB2_NV_OFFS_MISC, VB2_NV_MISC_BOOT_ON_AC_DETECT);
break;
case VB2_NV_TRY_RO_SYNC:
SETBIT(VB2_NV_OFFS_MISC, VB2_NV_MISC_TRY_RO_SYNC);
break;
case VB2_NV_BATTERY_CUTOFF_REQUEST:
SETBIT(VB2_NV_OFFS_MISC, VB2_NV_MISC_BATTERY_CUTOFF);
break;
case VB2_NV_KERNEL_MAX_ROLLFORWARD:
p[VB2_NV_OFFS_KERNEL_MAX_ROLLFORWARD1] = (uint8_t)(value);
p[VB2_NV_OFFS_KERNEL_MAX_ROLLFORWARD2] = (uint8_t)(value >> 8);
p[VB2_NV_OFFS_KERNEL_MAX_ROLLFORWARD3] = (uint8_t)(value >> 16);
p[VB2_NV_OFFS_KERNEL_MAX_ROLLFORWARD4] = (uint8_t)(value >> 24);
break;
case VB2_NV_FW_MAX_ROLLFORWARD:
/* Field only present in V2 */
if (!(ctx->flags & VB2_CONTEXT_NVDATA_V2))
return;
p[VB2_NV_OFFS_FW_MAX_ROLLFORWARD1] = (uint8_t)(value);
p[VB2_NV_OFFS_FW_MAX_ROLLFORWARD2] = (uint8_t)(value >> 8);
p[VB2_NV_OFFS_FW_MAX_ROLLFORWARD3] = (uint8_t)(value >> 16);
p[VB2_NV_OFFS_FW_MAX_ROLLFORWARD4] = (uint8_t)(value >> 24);
break;
}
/*
* Note there is no default case. This causes a compiler warning if
* a new param is added to the enum without adding support here.
*/
/* Need to regenerate CRC, since the value changed. */
vb2_nv_regen_crc(ctx);
}
#undef SETBIT

View File

@@ -0,0 +1,425 @@
/* 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.
*/
/*
* Implementation of RSA signature verification which uses a pre-processed key
* for computation. The code extends Android's RSA verification code to support
* multiple RSA key lengths and hash digest algorithms.
*/
#include "2sysincludes.h"
#include "2common.h"
#include "2rsa.h"
#include "2sha.h"
/**
* a[] -= mod
*/
static void subM(const struct vb2_public_key *key, uint32_t *a)
{
int64_t A = 0;
uint32_t i;
for (i = 0; i < key->arrsize; ++i) {
A += (uint64_t)a[i] - key->n[i];
a[i] = (uint32_t)A;
A >>= 32;
}
}
/**
* Return a[] >= mod
*/
int vb2_mont_ge(const struct vb2_public_key *key, uint32_t *a)
{
uint32_t i;
for (i = key->arrsize; i;) {
--i;
if (a[i] < key->n[i])
return 0;
if (a[i] > key->n[i])
return 1;
}
return 1; /* equal */
}
/**
* Montgomery c[] += a * b[] / R % mod
*/
static void montMulAdd(const struct vb2_public_key *key,
uint32_t *c,
const uint32_t a,
const uint32_t *b)
{
uint64_t A = (uint64_t)a * b[0] + c[0];
uint32_t d0 = (uint32_t)A * key->n0inv;
uint64_t B = (uint64_t)d0 * key->n[0] + (uint32_t)A;
uint32_t i;
for (i = 1; i < key->arrsize; ++i) {
A = (A >> 32) + (uint64_t)a * b[i] + c[i];
B = (B >> 32) + (uint64_t)d0 * key->n[i] + (uint32_t)A;
c[i - 1] = (uint32_t)B;
}
A = (A >> 32) + (B >> 32);
c[i - 1] = (uint32_t)A;
if (A >> 32) {
subM(key, c);
}
}
/**
* Montgomery c[] += 0 * b[] / R % mod
*/
static void montMulAdd0(const struct vb2_public_key *key,
uint32_t *c,
const uint32_t *b)
{
uint32_t d0 = c[0] * key->n0inv;
uint64_t B = (uint64_t)d0 * key->n[0] + c[0];
uint32_t i;
for (i = 1; i < key->arrsize; ++i) {
B = (B >> 32) + (uint64_t)d0 * key->n[i] + c[i];
c[i - 1] = (uint32_t)B;
}
c[i - 1] = B >> 32;
}
/**
* Montgomery c[] = a[] * b[] / R % mod
*/
static void montMul(const struct vb2_public_key *key,
uint32_t *c,
const uint32_t *a,
const uint32_t *b)
{
uint32_t i;
for (i = 0; i < key->arrsize; ++i) {
c[i] = 0;
}
for (i = 0; i < key->arrsize; ++i) {
montMulAdd(key, c, a[i], b);
}
}
/* Montgomery c[] = a[] * 1 / R % key. */
static void montMul1(const struct vb2_public_key *key,
uint32_t *c,
const uint32_t *a)
{
int i;
for (i = 0; i < key->arrsize; ++i)
c[i] = 0;
montMulAdd(key, c, 1, a);
for (i = 1; i < key->arrsize; ++i)
montMulAdd0(key, c, a);
}
/**
* In-place public exponentiation.
*
* @param key Key to use in signing
* @param inout Input and output big-endian byte array
* @param workbuf32 Work buffer; caller must verify this is
* (3 * key->arrsize) elements long.
* @param exp RSA public exponent: either 65537 (F4) or 3
*/
static void modpow(const struct vb2_public_key *key, uint8_t *inout,
uint32_t *workbuf32, int exp)
{
uint32_t *a = workbuf32;
uint32_t *aR = a + key->arrsize;
uint32_t *aaR = aR + key->arrsize;
uint32_t *aaa = aaR; /* Re-use location. */
int i;
/* Convert from big endian byte array to little endian word array. */
for (i = 0; i < (int)key->arrsize; ++i) {
uint32_t tmp =
(inout[((key->arrsize - 1 - i) * 4) + 0] << 24) |
(inout[((key->arrsize - 1 - i) * 4) + 1] << 16) |
(inout[((key->arrsize - 1 - i) * 4) + 2] << 8) |
(inout[((key->arrsize - 1 - i) * 4) + 3] << 0);
a[i] = tmp;
}
montMul(key, aR, a, key->rr); /* aR = a * RR / R mod M */
if (exp == 3) {
montMul(key, aaR, aR, aR); /* aaR = aR * aR / R mod M */
montMul(key, a, aaR, aR); /* a = aaR * aR / R mod M */
montMul1(key, aaa, a); /* aaa = a * 1 / R mod M */
} else {
/* Exponent 65537 */
for (i = 0; i < 16; i+=2) {
montMul(key, aaR, aR, aR); /* aaR = aR * aR / R mod M */
montMul(key, aR, aaR, aaR); /* aR = aaR * aaR / R mod M */
}
montMul(key, aaa, aR, a); /* aaa = aR * a / R mod M */
}
/* Make sure aaa < mod; aaa is at most 1x mod too large. */
if (vb2_mont_ge(key, aaa)) {
subM(key, aaa);
}
/* Convert to bigendian byte array */
for (i = (int)key->arrsize - 1; i >= 0; --i) {
uint32_t tmp = aaa[i];
*inout++ = (uint8_t)(tmp >> 24);
*inout++ = (uint8_t)(tmp >> 16);
*inout++ = (uint8_t)(tmp >> 8);
*inout++ = (uint8_t)(tmp >> 0);
}
}
static const uint8_t crypto_to_sig[] = {
VB2_SIG_RSA1024,
VB2_SIG_RSA1024,
VB2_SIG_RSA1024,
VB2_SIG_RSA2048,
VB2_SIG_RSA2048,
VB2_SIG_RSA2048,
VB2_SIG_RSA4096,
VB2_SIG_RSA4096,
VB2_SIG_RSA4096,
VB2_SIG_RSA8192,
VB2_SIG_RSA8192,
VB2_SIG_RSA8192,
VB2_SIG_RSA2048_EXP3,
VB2_SIG_RSA2048_EXP3,
VB2_SIG_RSA2048_EXP3,
VB2_SIG_RSA3072_EXP3,
VB2_SIG_RSA3072_EXP3,
VB2_SIG_RSA3072_EXP3,
};
/**
* Convert vb2_crypto_algorithm to vb2_signature_algorithm.
*
* @param algorithm Crypto algorithm (vb2_crypto_algorithm)
*
* @return The signature algorithm for that crypto algorithm, or
* VB2_SIG_INVALID if the crypto algorithm or its corresponding signature
* algorithm is invalid or not supported.
*/
enum vb2_signature_algorithm vb2_crypto_to_signature(uint32_t algorithm)
{
if (algorithm < ARRAY_SIZE(crypto_to_sig))
return crypto_to_sig[algorithm];
else
return VB2_SIG_INVALID;
}
uint32_t vb2_rsa_sig_size(enum vb2_signature_algorithm sig_alg)
{
switch (sig_alg) {
case VB2_SIG_RSA1024:
return 1024 / 8;
case VB2_SIG_RSA2048:
case VB2_SIG_RSA2048_EXP3:
return 2048 / 8;
case VB2_SIG_RSA3072_EXP3:
return 3072 / 8;
case VB2_SIG_RSA4096:
return 4096 / 8;
case VB2_SIG_RSA8192:
return 8192 / 8;
default:
return 0;
}
}
/**
* Return the exponent used by an RSA algorithm
*
* @param sig_alg Signature algorithm
* @return The exponent to use (3 or 65537(F4)), or 0 if error.
*/
static uint32_t vb2_rsa_exponent(enum vb2_signature_algorithm sig_alg)
{
switch (sig_alg) {
case VB2_SIG_RSA1024:
case VB2_SIG_RSA2048:
case VB2_SIG_RSA4096:
case VB2_SIG_RSA8192:
return 65537;
case VB2_SIG_RSA2048_EXP3:
case VB2_SIG_RSA3072_EXP3:
return 3;
default:
return 0;
}
}
uint32_t vb2_packed_key_size(enum vb2_signature_algorithm sig_alg)
{
uint32_t sig_size = vb2_rsa_sig_size(sig_alg);
if (!sig_size)
return 0;
/*
* Total size needed by a RSAPublicKey buffer is =
* 2 * key_len bytes for the n and rr arrays
* + sizeof len + sizeof n0inv.
*/
return 2 * sig_size + 2 * sizeof(uint32_t);
}
/*
* PKCS 1.5 padding (from the RSA PKCS#1 v2.1 standard)
*
* Depending on the RSA key size and hash function, the padding is calculated
* as follows:
*
* 0x00 || 0x01 || PS || 0x00 || T
*
* T: DER Encoded DigestInfo value which depends on the hash function used.
*
* SHA-1: (0x)30 21 30 09 06 05 2b 0e 03 02 1a 05 00 04 14 || H.
* SHA-256: (0x)30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 || H.
* SHA-512: (0x)30 51 30 0d 06 09 60 86 48 01 65 03 04 02 03 05 00 04 40 || H.
*
* Length(T) = 35 octets for SHA-1
* Length(T) = 51 octets for SHA-256
* Length(T) = 83 octets for SHA-512
*
* PS: octet string consisting of {Length(RSA Key) - Length(T) - 3} 0xFF
*/
static const uint8_t sha1_tail[] = {
0x00,0x30,0x21,0x30,0x09,0x06,0x05,0x2b,
0x0e,0x03,0x02,0x1a,0x05,0x00,0x04,0x14
};
static const uint8_t sha256_tail[] = {
0x00,0x30,0x31,0x30,0x0d,0x06,0x09,0x60,
0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x01,
0x05,0x00,0x04,0x20
};
static const uint8_t sha512_tail[] = {
0x00,0x30,0x51,0x30,0x0d,0x06,0x09,0x60,
0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x03,
0x05,0x00,0x04,0x40
};
int vb2_check_padding(const uint8_t *sig, const struct vb2_public_key *key)
{
/* Determine padding to use depending on the signature type */
uint32_t sig_size = vb2_rsa_sig_size(key->sig_alg);
uint32_t hash_size = vb2_digest_size(key->hash_alg);
uint32_t pad_size = sig_size - hash_size;
const uint8_t *tail;
uint32_t tail_size;
int result = 0;
int i;
if (!sig_size || !hash_size || hash_size > sig_size)
return VB2_ERROR_RSA_PADDING_SIZE;
switch (key->hash_alg) {
case VB2_HASH_SHA1:
tail = sha1_tail;
tail_size = sizeof(sha1_tail);
break;
case VB2_HASH_SHA256:
tail = sha256_tail;
tail_size = sizeof(sha256_tail);
break;
case VB2_HASH_SHA512:
tail = sha512_tail;
tail_size = sizeof(sha512_tail);
break;
default:
return VB2_ERROR_RSA_PADDING_ALGORITHM;
}
/* First 2 bytes are always 0x00 0x01 */
result |= *sig++ ^ 0x00;
result |= *sig++ ^ 0x01;
/* Then 0xff bytes until the tail */
for (i = 0; i < pad_size - tail_size - 2; i++)
result |= *sig++ ^ 0xff;
/*
* Then the tail. Even though there are probably no timing issues
* here, we use vb2_safe_memcmp() just to be on the safe side.
*/
result |= vb2_safe_memcmp(sig, tail, tail_size);
return result ? VB2_ERROR_RSA_PADDING : VB2_SUCCESS;
}
int vb2_rsa_verify_digest(const struct vb2_public_key *key,
uint8_t *sig,
const uint8_t *digest,
const struct vb2_workbuf *wb)
{
struct vb2_workbuf wblocal = *wb;
uint32_t *workbuf32;
uint32_t key_bytes;
int sig_size;
int pad_size;
int exp;
int rv;
if (!key || !sig || !digest)
return VB2_ERROR_RSA_VERIFY_PARAM;
sig_size = vb2_rsa_sig_size(key->sig_alg);
exp = vb2_rsa_exponent(key->sig_alg);
if (!sig_size || !exp) {
VB2_DEBUG("Invalid signature type!\n");
return VB2_ERROR_RSA_VERIFY_ALGORITHM;
}
/* Signature length should be same as key length */
key_bytes = key->arrsize * sizeof(uint32_t);
if (key_bytes != sig_size) {
VB2_DEBUG("Signature is of incorrect length!\n");
return VB2_ERROR_RSA_VERIFY_SIG_LEN;
}
workbuf32 = vb2_workbuf_alloc(&wblocal, 3 * key_bytes);
if (!workbuf32) {
VB2_DEBUG("ERROR - vboot2 work buffer too small!\n");
return VB2_ERROR_RSA_VERIFY_WORKBUF;
}
modpow(key, sig, workbuf32, exp);
vb2_workbuf_free(&wblocal, 3 * key_bytes);
/*
* Check padding. Only fail immediately if the padding size is bad.
* Otherwise, continue on to check the digest to reduce the risk of
* timing based attacks.
*/
rv = vb2_check_padding(sig, key);
if (rv == VB2_ERROR_RSA_PADDING_SIZE)
return rv;
/*
* Check digest. Even though there are probably no timing issues here,
* use vb2_safe_memcmp() just to be on the safe side. (That's also why
* we don't return before this check if the padding check failed.)
*/
pad_size = sig_size - vb2_digest_size(key->hash_alg);
if (vb2_safe_memcmp(sig + pad_size, digest, key_bytes - pad_size)) {
VB2_DEBUG("Digest check failed!\n");
if (!rv)
rv = VB2_ERROR_RSA_VERIFY_DIGEST;
}
return rv;
}

View File

@@ -0,0 +1,126 @@
/* 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.
*
* Secure storage APIs
*/
#include "2sysincludes.h"
#include "2common.h"
#include "2crc8.h"
#include "2misc.h"
#include "2secdata.h"
int vb2_secdata_check_crc(const struct vb2_context *ctx)
{
const struct vb2_secdata *sec =
(const struct vb2_secdata *)ctx->secdata;
/* Verify CRC */
if (sec->crc8 != vb2_crc8(sec, offsetof(struct vb2_secdata, crc8)))
return VB2_ERROR_SECDATA_CRC;
/* CRC(<000...00>) is 0, so check version as well (should never be 0) */
if (!sec->struct_version)
return VB2_ERROR_SECDATA_ZERO;
return VB2_SUCCESS;
}
int vb2_secdata_create(struct vb2_context *ctx)
{
struct vb2_secdata *sec = (struct vb2_secdata *)ctx->secdata;
/* Clear the entire struct */
memset(sec, 0, sizeof(*sec));
/* Set to current version */
sec->struct_version = VB2_SECDATA_VERSION;
/* Calculate initial CRC */
sec->crc8 = vb2_crc8(sec, offsetof(struct vb2_secdata, crc8));
ctx->flags |= VB2_CONTEXT_SECDATA_CHANGED;
return VB2_SUCCESS;
}
int vb2_secdata_init(struct vb2_context *ctx)
{
struct vb2_shared_data *sd = vb2_get_sd(ctx);
int rv;
rv = vb2_secdata_check_crc(ctx);
if (rv)
return rv;
/* Set status flag */
sd->status |= VB2_SD_STATUS_SECDATA_INIT;
/* TODO: unit test for that */
/* Read this now to make sure crossystem has it even in rec mode. */
rv = vb2_secdata_get(ctx, VB2_SECDATA_VERSIONS,
&sd->fw_version_secdata);
if (rv)
return rv;
return VB2_SUCCESS;
}
int vb2_secdata_get(struct vb2_context *ctx,
enum vb2_secdata_param param,
uint32_t *dest)
{
struct vb2_secdata *sec = (struct vb2_secdata *)ctx->secdata;
if (!(vb2_get_sd(ctx)->status & VB2_SD_STATUS_SECDATA_INIT))
return VB2_ERROR_SECDATA_GET_UNINITIALIZED;
switch(param) {
case VB2_SECDATA_FLAGS:
*dest = sec->flags;
return VB2_SUCCESS;
case VB2_SECDATA_VERSIONS:
*dest = sec->fw_versions;
return VB2_SUCCESS;
default:
return VB2_ERROR_SECDATA_GET_PARAM;
}
}
int vb2_secdata_set(struct vb2_context *ctx,
enum vb2_secdata_param param,
uint32_t value)
{
struct vb2_secdata *sec = (struct vb2_secdata *)ctx->secdata;
uint32_t now;
if (!(vb2_get_sd(ctx)->status & VB2_SD_STATUS_SECDATA_INIT))
return VB2_ERROR_SECDATA_SET_UNINITIALIZED;
/* If not changing the value, don't regenerate the CRC. */
if (vb2_secdata_get(ctx, param, &now) == VB2_SUCCESS && now == value)
return VB2_SUCCESS;
switch(param) {
case VB2_SECDATA_FLAGS:
/* Make sure flags is in valid range */
if (value > 0xff)
return VB2_ERROR_SECDATA_SET_FLAGS;
sec->flags = value;
break;
case VB2_SECDATA_VERSIONS:
sec->fw_versions = value;
break;
default:
return VB2_ERROR_SECDATA_SET_PARAM;
}
/* Regenerate CRC */
sec->crc8 = vb2_crc8(sec, offsetof(struct vb2_secdata, crc8));
ctx->flags |= VB2_CONTEXT_SECDATA_CHANGED;
return VB2_SUCCESS;
}

View File

@@ -0,0 +1,112 @@
/* Copyright 2015 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.
*
* Secure storage APIs - kernel version space
*/
#include "2sysincludes.h"
#include "2common.h"
#include "2crc8.h"
#include "2misc.h"
#include "2secdata.h"
int vb2_secdatak_check_crc(const struct vb2_context *ctx)
{
const struct vb2_secdatak *sec =
(const struct vb2_secdatak *)ctx->secdatak;
/* Verify CRC */
if (sec->crc8 != vb2_crc8(sec, offsetof(struct vb2_secdatak, crc8)))
return VB2_ERROR_SECDATAK_CRC;
return VB2_SUCCESS;
}
int vb2_secdatak_create(struct vb2_context *ctx)
{
struct vb2_secdatak *sec = (struct vb2_secdatak *)ctx->secdatak;
/* Clear the entire struct */
memset(sec, 0, sizeof(*sec));
/* Set to current version */
sec->struct_version = VB2_SECDATAK_VERSION;
/* Set UID */
sec->uid = VB2_SECDATAK_UID;
/* Calculate initial CRC */
sec->crc8 = vb2_crc8(sec, offsetof(struct vb2_secdatak, crc8));
ctx->flags |= VB2_CONTEXT_SECDATAK_CHANGED;
return VB2_SUCCESS;
}
int vb2_secdatak_init(struct vb2_context *ctx)
{
struct vb2_secdatak *sec = (struct vb2_secdatak *)ctx->secdatak;
struct vb2_shared_data *sd = vb2_get_sd(ctx);
int rv;
rv = vb2_secdatak_check_crc(ctx);
if (rv)
return rv;
/* Make sure the UID is correct */
if (sec->uid != VB2_SECDATAK_UID)
return VB2_ERROR_SECDATAK_UID;
/* Set status flag */
sd->status |= VB2_SD_STATUS_SECDATAK_INIT;
/* TODO: unit test for that */
return VB2_SUCCESS;
}
int vb2_secdatak_get(struct vb2_context *ctx,
enum vb2_secdatak_param param,
uint32_t *dest)
{
struct vb2_secdatak *sec = (struct vb2_secdatak *)ctx->secdatak;
if (!(vb2_get_sd(ctx)->status & VB2_SD_STATUS_SECDATAK_INIT))
return VB2_ERROR_SECDATAK_GET_UNINITIALIZED;
switch(param) {
case VB2_SECDATAK_VERSIONS:
*dest = sec->kernel_versions;
return VB2_SUCCESS;
default:
return VB2_ERROR_SECDATAK_GET_PARAM;
}
}
int vb2_secdatak_set(struct vb2_context *ctx,
enum vb2_secdatak_param param,
uint32_t value)
{
struct vb2_secdatak *sec = (struct vb2_secdatak *)ctx->secdatak;
uint32_t now;
if (!(vb2_get_sd(ctx)->status & VB2_SD_STATUS_SECDATAK_INIT))
return VB2_ERROR_SECDATAK_SET_UNINITIALIZED;
/* If not changing the value, don't regenerate the CRC. */
if (vb2_secdatak_get(ctx, param, &now) == VB2_SUCCESS && now == value)
return VB2_SUCCESS;
switch(param) {
case VB2_SECDATAK_VERSIONS:
sec->kernel_versions = value;
break;
default:
return VB2_ERROR_SECDATAK_SET_PARAM;
}
/* Regenerate CRC */
sec->crc8 = vb2_crc8(sec, offsetof(struct vb2_secdatak, crc8));
ctx->flags |= VB2_CONTEXT_SECDATAK_CHANGED;
return VB2_SUCCESS;
}

View File

@@ -0,0 +1,292 @@
/* 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.
*
* SHA-1 implementation largely based on libmincrypt in the the Android
* Open Source Project (platorm/system/core.git/libmincrypt/sha.c
*/
#include "2sysincludes.h"
#include "2common.h"
#include "2sha.h"
/*
* Some machines lack byteswap.h and endian.h. These have to use the
* slower code, even if they're little-endian.
*/
#if defined(HAVE_ENDIAN_H) && defined(HAVE_LITTLE_ENDIAN)
/*
* This version is about 28% faster than the generic version below,
* but assumes little-endianness.
*/
static uint32_t ror27(uint32_t val)
{
return (val >> 27) | (val << 5);
}
static uint32_t ror2(uint32_t val)
{
return (val >> 2) | (val << 30);
}
static uint32_t ror31(uint32_t val)
{
return (val >> 31) | (val << 1);
}
static void sha1_transform(struct vb2_sha1_context *ctx)
{
/* Note that this array uses 80*4=320 bytes of stack */
uint32_t W[80];
register uint32_t A, B, C, D, E;
int t;
A = ctx->state[0];
B = ctx->state[1];
C = ctx->state[2];
D = ctx->state[3];
E = ctx->state[4];
#define SHA_F1(A,B,C,D,E,t) \
E += ror27(A) + \
(W[t] = bswap_32(ctx->buf.w[t])) + \
(D^(B&(C^D))) + 0x5A827999; \
B = ror2(B);
for (t = 0; t < 15; t += 5) {
SHA_F1(A,B,C,D,E,t + 0);
SHA_F1(E,A,B,C,D,t + 1);
SHA_F1(D,E,A,B,C,t + 2);
SHA_F1(C,D,E,A,B,t + 3);
SHA_F1(B,C,D,E,A,t + 4);
}
SHA_F1(A,B,C,D,E,t + 0); /* 16th one, t == 15 */
#undef SHA_F1
#define SHA_F1(A,B,C,D,E,t) \
E += ror27(A) + \
(W[t] = ror31(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16])) + \
(D^(B&(C^D))) + 0x5A827999; \
B = ror2(B);
SHA_F1(E,A,B,C,D,t + 1);
SHA_F1(D,E,A,B,C,t + 2);
SHA_F1(C,D,E,A,B,t + 3);
SHA_F1(B,C,D,E,A,t + 4);
#undef SHA_F1
#define SHA_F2(A,B,C,D,E,t) \
E += ror27(A) + \
(W[t] = ror31(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16])) + \
(B^C^D) + 0x6ED9EBA1; \
B = ror2(B);
for (t = 20; t < 40; t += 5) {
SHA_F2(A,B,C,D,E,t + 0);
SHA_F2(E,A,B,C,D,t + 1);
SHA_F2(D,E,A,B,C,t + 2);
SHA_F2(C,D,E,A,B,t + 3);
SHA_F2(B,C,D,E,A,t + 4);
}
#undef SHA_F2
#define SHA_F3(A,B,C,D,E,t) \
E += ror27(A) + \
(W[t] = ror31(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16])) + \
((B&C)|(D&(B|C))) + 0x8F1BBCDC; \
B = ror2(B);
for (; t < 60; t += 5) {
SHA_F3(A,B,C,D,E,t + 0);
SHA_F3(E,A,B,C,D,t + 1);
SHA_F3(D,E,A,B,C,t + 2);
SHA_F3(C,D,E,A,B,t + 3);
SHA_F3(B,C,D,E,A,t + 4);
}
#undef SHA_F3
#define SHA_F4(A,B,C,D,E,t) \
E += ror27(A) + \
(W[t] = ror31(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16])) + \
(B^C^D) + 0xCA62C1D6; \
B = ror2(B);
for (; t < 80; t += 5) {
SHA_F4(A,B,C,D,E,t + 0);
SHA_F4(E,A,B,C,D,t + 1);
SHA_F4(D,E,A,B,C,t + 2);
SHA_F4(C,D,E,A,B,t + 3);
SHA_F4(B,C,D,E,A,t + 4);
}
#undef SHA_F4
ctx->state[0] += A;
ctx->state[1] += B;
ctx->state[2] += C;
ctx->state[3] += D;
ctx->state[4] += E;
}
void vb2_sha1_update(struct vb2_sha1_context *ctx,
const uint8_t *data,
uint32_t size)
{
int i = ctx->count % sizeof(ctx->buf);
const uint8_t *p = (const uint8_t*)data;
ctx->count += size;
while (size > sizeof(ctx->buf) - i) {
memcpy(&ctx->buf.b[i], p, sizeof(ctx->buf) - i);
size -= sizeof(ctx->buf) - i;
p += sizeof(ctx->buf) - i;
sha1_transform(ctx);
i = 0;
}
while (size--) {
ctx->buf.b[i++] = *p++;
if (i == sizeof(ctx->buf)) {
sha1_transform(ctx);
i = 0;
}
}
}
uint8_t *vb2_sha1_finalize(struct vb2_sha1_context *ctx)
{
uint32_t cnt = ctx->count * 8;
int i;
vb2_sha1_update(ctx, (uint8_t*)"\x80", 1);
while ((ctx->count % sizeof(ctx->buf)) != (sizeof(ctx->buf) - 8)) {
vb2_sha1_update(ctx, (uint8_t*)"\0", 1);
}
for (i = 0; i < 8; ++i) {
uint8_t tmp = cnt >> ((7 - i) * 8);
vb2_sha1_update(ctx, &tmp, 1);
}
for (i = 0; i < 5; i++) {
ctx->buf.w[i] = bswap_32(ctx->state[i]);
}
return ctx->buf.b;
}
#else /* #if defined(HAVE_ENDIAN_H) && defined(HAVE_LITTLE_ENDIAN) */
#define rol(bits, value) (((value) << (bits)) | ((value) >> (32 - (bits))))
static void sha1_transform(struct vb2_sha1_context *ctx)
{
/* Note that this array uses 80*4=320 bytes of stack */
uint32_t W[80];
uint32_t A, B, C, D, E;
uint8_t *p = ctx->buf;
int t;
for(t = 0; t < 16; ++t) {
uint32_t tmp = *p++ << 24;
tmp |= *p++ << 16;
tmp |= *p++ << 8;
tmp |= *p++;
W[t] = tmp;
}
for(; t < 80; t++) {
W[t] = rol(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
}
A = ctx->state[0];
B = ctx->state[1];
C = ctx->state[2];
D = ctx->state[3];
E = ctx->state[4];
for(t = 0; t < 80; t++) {
uint32_t tmp = rol(5,A) + E + W[t];
if (t < 20)
tmp += (D^(B&(C^D))) + 0x5A827999;
else if ( t < 40)
tmp += (B^C^D) + 0x6ED9EBA1;
else if ( t < 60)
tmp += ((B&C)|(D&(B|C))) + 0x8F1BBCDC;
else
tmp += (B^C^D) + 0xCA62C1D6;
E = D;
D = C;
C = rol(30,B);
B = A;
A = tmp;
}
ctx->state[0] += A;
ctx->state[1] += B;
ctx->state[2] += C;
ctx->state[3] += D;
ctx->state[4] += E;
}
void vb2_sha1_update(struct vb2_sha1_context *ctx,
const uint8_t *data,
uint32_t size)
{
int i = (int)(ctx->count % sizeof(ctx->buf));
const uint8_t* p = (const uint8_t*) data;
ctx->count += size;
while (size--) {
ctx->buf[i++] = *p++;
if (i == sizeof(ctx->buf)) {
sha1_transform(ctx);
i = 0;
}
}
}
void vb2_sha1_finalize(struct vb2_sha1_context *ctx, uint8_t *digest)
{
uint32_t cnt = ctx->count << 3;
int i;
vb2_sha1_update(ctx, (uint8_t*)"\x80", 1);
while ((ctx->count % sizeof(ctx->buf)) != (sizeof(ctx->buf) - 8)) {
vb2_sha1_update(ctx, (uint8_t*)"\0", 1);
}
for (i = 0; i < 8; ++i) {
uint8_t tmp = (uint8_t)((uint64_t)cnt >> ((7 - i) * 8));
vb2_sha1_update(ctx, &tmp, 1);
}
for (i = 0; i < 5; i++) {
uint32_t tmp = ctx->state[i];
*digest++ = (uint8_t)(tmp >> 24);
*digest++ = (uint8_t)(tmp >> 16);
*digest++ = (uint8_t)(tmp >> 8);
*digest++ = (uint8_t)(tmp >> 0);
}
}
#endif /* endianness */
void vb2_sha1_init(struct vb2_sha1_context *ctx)
{
ctx->state[0] = 0x67452301;
ctx->state[1] = 0xefcdab89;
ctx->state[2] = 0x98badcfe;
ctx->state[3] = 0x10325476;
ctx->state[4] = 0xc3d2e1f0;
ctx->count = 0;
}

View File

@@ -0,0 +1,334 @@
/* SHA-256 and SHA-512 implementation based on code by Oliver Gay
* <olivier.gay@a3.epfl.ch> under a BSD-style license. See below.
*/
/*
* FIPS 180-2 SHA-224/256/384/512 implementation
* Last update: 02/02/2007
* Issue date: 04/30/2005
*
* Copyright (C) 2005, 2007 Olivier Gay <olivier.gay@a3.epfl.ch>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "2sysincludes.h"
#include "2common.h"
#include "2sha.h"
#define SHFR(x, n) (x >> n)
#define ROTR(x, n) ((x >> n) | (x << ((sizeof(x) << 3) - n)))
#define ROTL(x, n) ((x << n) | (x >> ((sizeof(x) << 3) - n)))
#define CH(x, y, z) ((x & y) ^ (~x & z))
#define MAJ(x, y, z) ((x & y) ^ (x & z) ^ (y & z))
#define SHA256_F1(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22))
#define SHA256_F2(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25))
#define SHA256_F3(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHFR(x, 3))
#define SHA256_F4(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHFR(x, 10))
#define UNPACK32(x, str) \
{ \
*((str) + 3) = (uint8_t) ((x) ); \
*((str) + 2) = (uint8_t) ((x) >> 8); \
*((str) + 1) = (uint8_t) ((x) >> 16); \
*((str) + 0) = (uint8_t) ((x) >> 24); \
}
#define PACK32(str, x) \
{ \
*(x) = ((uint32_t) *((str) + 3) ) \
| ((uint32_t) *((str) + 2) << 8) \
| ((uint32_t) *((str) + 1) << 16) \
| ((uint32_t) *((str) + 0) << 24); \
}
/* Macros used for loops unrolling */
#define SHA256_SCR(i) \
{ \
w[i] = SHA256_F4(w[i - 2]) + w[i - 7] \
+ SHA256_F3(w[i - 15]) + w[i - 16]; \
}
#define SHA256_EXP(a, b, c, d, e, f, g, h, j) \
{ \
t1 = wv[h] + SHA256_F2(wv[e]) + CH(wv[e], wv[f], wv[g]) \
+ sha256_k[j] + w[j]; \
t2 = SHA256_F1(wv[a]) + MAJ(wv[a], wv[b], wv[c]); \
wv[d] += t1; \
wv[h] = t1 + t2; \
}
static const uint32_t sha256_h0[8] = {
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
};
static const uint32_t sha256_k[64] = {
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
};
/* SHA-256 implementation */
void vb2_sha256_init(struct vb2_sha256_context *ctx)
{
#ifndef UNROLL_LOOPS
int i;
for (i = 0; i < 8; i++) {
ctx->h[i] = sha256_h0[i];
}
#else
ctx->h[0] = sha256_h0[0]; ctx->h[1] = sha256_h0[1];
ctx->h[2] = sha256_h0[2]; ctx->h[3] = sha256_h0[3];
ctx->h[4] = sha256_h0[4]; ctx->h[5] = sha256_h0[5];
ctx->h[6] = sha256_h0[6]; ctx->h[7] = sha256_h0[7];
#endif /* !UNROLL_LOOPS */
ctx->size = 0;
ctx->total_size = 0;
}
static void vb2_sha256_transform(struct vb2_sha256_context *ctx,
const uint8_t *message,
unsigned int block_nb)
{
/* Note that these arrays use 72*4=288 bytes of stack */
uint32_t w[64];
uint32_t wv[8];
uint32_t t1, t2;
const unsigned char *sub_block;
int i;
#ifndef UNROLL_LOOPS
int j;
#endif
for (i = 0; i < (int) block_nb; i++) {
sub_block = message + (i << 6);
#ifndef UNROLL_LOOPS
for (j = 0; j < 16; j++) {
PACK32(&sub_block[j << 2], &w[j]);
}
for (j = 16; j < 64; j++) {
SHA256_SCR(j);
}
for (j = 0; j < 8; j++) {
wv[j] = ctx->h[j];
}
for (j = 0; j < 64; j++) {
t1 = wv[7] + SHA256_F2(wv[4]) + CH(wv[4], wv[5], wv[6])
+ sha256_k[j] + w[j];
t2 = SHA256_F1(wv[0]) + MAJ(wv[0], wv[1], wv[2]);
wv[7] = wv[6];
wv[6] = wv[5];
wv[5] = wv[4];
wv[4] = wv[3] + t1;
wv[3] = wv[2];
wv[2] = wv[1];
wv[1] = wv[0];
wv[0] = t1 + t2;
}
for (j = 0; j < 8; j++) {
ctx->h[j] += wv[j];
}
#else
PACK32(&sub_block[ 0], &w[ 0]); PACK32(&sub_block[ 4], &w[ 1]);
PACK32(&sub_block[ 8], &w[ 2]); PACK32(&sub_block[12], &w[ 3]);
PACK32(&sub_block[16], &w[ 4]); PACK32(&sub_block[20], &w[ 5]);
PACK32(&sub_block[24], &w[ 6]); PACK32(&sub_block[28], &w[ 7]);
PACK32(&sub_block[32], &w[ 8]); PACK32(&sub_block[36], &w[ 9]);
PACK32(&sub_block[40], &w[10]); PACK32(&sub_block[44], &w[11]);
PACK32(&sub_block[48], &w[12]); PACK32(&sub_block[52], &w[13]);
PACK32(&sub_block[56], &w[14]); PACK32(&sub_block[60], &w[15]);
SHA256_SCR(16); SHA256_SCR(17); SHA256_SCR(18); SHA256_SCR(19);
SHA256_SCR(20); SHA256_SCR(21); SHA256_SCR(22); SHA256_SCR(23);
SHA256_SCR(24); SHA256_SCR(25); SHA256_SCR(26); SHA256_SCR(27);
SHA256_SCR(28); SHA256_SCR(29); SHA256_SCR(30); SHA256_SCR(31);
SHA256_SCR(32); SHA256_SCR(33); SHA256_SCR(34); SHA256_SCR(35);
SHA256_SCR(36); SHA256_SCR(37); SHA256_SCR(38); SHA256_SCR(39);
SHA256_SCR(40); SHA256_SCR(41); SHA256_SCR(42); SHA256_SCR(43);
SHA256_SCR(44); SHA256_SCR(45); SHA256_SCR(46); SHA256_SCR(47);
SHA256_SCR(48); SHA256_SCR(49); SHA256_SCR(50); SHA256_SCR(51);
SHA256_SCR(52); SHA256_SCR(53); SHA256_SCR(54); SHA256_SCR(55);
SHA256_SCR(56); SHA256_SCR(57); SHA256_SCR(58); SHA256_SCR(59);
SHA256_SCR(60); SHA256_SCR(61); SHA256_SCR(62); SHA256_SCR(63);
wv[0] = ctx->h[0]; wv[1] = ctx->h[1];
wv[2] = ctx->h[2]; wv[3] = ctx->h[3];
wv[4] = ctx->h[4]; wv[5] = ctx->h[5];
wv[6] = ctx->h[6]; wv[7] = ctx->h[7];
SHA256_EXP(0,1,2,3,4,5,6,7, 0); SHA256_EXP(7,0,1,2,3,4,5,6, 1);
SHA256_EXP(6,7,0,1,2,3,4,5, 2); SHA256_EXP(5,6,7,0,1,2,3,4, 3);
SHA256_EXP(4,5,6,7,0,1,2,3, 4); SHA256_EXP(3,4,5,6,7,0,1,2, 5);
SHA256_EXP(2,3,4,5,6,7,0,1, 6); SHA256_EXP(1,2,3,4,5,6,7,0, 7);
SHA256_EXP(0,1,2,3,4,5,6,7, 8); SHA256_EXP(7,0,1,2,3,4,5,6, 9);
SHA256_EXP(6,7,0,1,2,3,4,5,10); SHA256_EXP(5,6,7,0,1,2,3,4,11);
SHA256_EXP(4,5,6,7,0,1,2,3,12); SHA256_EXP(3,4,5,6,7,0,1,2,13);
SHA256_EXP(2,3,4,5,6,7,0,1,14); SHA256_EXP(1,2,3,4,5,6,7,0,15);
SHA256_EXP(0,1,2,3,4,5,6,7,16); SHA256_EXP(7,0,1,2,3,4,5,6,17);
SHA256_EXP(6,7,0,1,2,3,4,5,18); SHA256_EXP(5,6,7,0,1,2,3,4,19);
SHA256_EXP(4,5,6,7,0,1,2,3,20); SHA256_EXP(3,4,5,6,7,0,1,2,21);
SHA256_EXP(2,3,4,5,6,7,0,1,22); SHA256_EXP(1,2,3,4,5,6,7,0,23);
SHA256_EXP(0,1,2,3,4,5,6,7,24); SHA256_EXP(7,0,1,2,3,4,5,6,25);
SHA256_EXP(6,7,0,1,2,3,4,5,26); SHA256_EXP(5,6,7,0,1,2,3,4,27);
SHA256_EXP(4,5,6,7,0,1,2,3,28); SHA256_EXP(3,4,5,6,7,0,1,2,29);
SHA256_EXP(2,3,4,5,6,7,0,1,30); SHA256_EXP(1,2,3,4,5,6,7,0,31);
SHA256_EXP(0,1,2,3,4,5,6,7,32); SHA256_EXP(7,0,1,2,3,4,5,6,33);
SHA256_EXP(6,7,0,1,2,3,4,5,34); SHA256_EXP(5,6,7,0,1,2,3,4,35);
SHA256_EXP(4,5,6,7,0,1,2,3,36); SHA256_EXP(3,4,5,6,7,0,1,2,37);
SHA256_EXP(2,3,4,5,6,7,0,1,38); SHA256_EXP(1,2,3,4,5,6,7,0,39);
SHA256_EXP(0,1,2,3,4,5,6,7,40); SHA256_EXP(7,0,1,2,3,4,5,6,41);
SHA256_EXP(6,7,0,1,2,3,4,5,42); SHA256_EXP(5,6,7,0,1,2,3,4,43);
SHA256_EXP(4,5,6,7,0,1,2,3,44); SHA256_EXP(3,4,5,6,7,0,1,2,45);
SHA256_EXP(2,3,4,5,6,7,0,1,46); SHA256_EXP(1,2,3,4,5,6,7,0,47);
SHA256_EXP(0,1,2,3,4,5,6,7,48); SHA256_EXP(7,0,1,2,3,4,5,6,49);
SHA256_EXP(6,7,0,1,2,3,4,5,50); SHA256_EXP(5,6,7,0,1,2,3,4,51);
SHA256_EXP(4,5,6,7,0,1,2,3,52); SHA256_EXP(3,4,5,6,7,0,1,2,53);
SHA256_EXP(2,3,4,5,6,7,0,1,54); SHA256_EXP(1,2,3,4,5,6,7,0,55);
SHA256_EXP(0,1,2,3,4,5,6,7,56); SHA256_EXP(7,0,1,2,3,4,5,6,57);
SHA256_EXP(6,7,0,1,2,3,4,5,58); SHA256_EXP(5,6,7,0,1,2,3,4,59);
SHA256_EXP(4,5,6,7,0,1,2,3,60); SHA256_EXP(3,4,5,6,7,0,1,2,61);
SHA256_EXP(2,3,4,5,6,7,0,1,62); SHA256_EXP(1,2,3,4,5,6,7,0,63);
ctx->h[0] += wv[0]; ctx->h[1] += wv[1];
ctx->h[2] += wv[2]; ctx->h[3] += wv[3];
ctx->h[4] += wv[4]; ctx->h[5] += wv[5];
ctx->h[6] += wv[6]; ctx->h[7] += wv[7];
#endif /* !UNROLL_LOOPS */
}
}
void vb2_sha256_update(struct vb2_sha256_context *ctx,
const uint8_t *data,
uint32_t size)
{
unsigned int block_nb;
unsigned int new_size, rem_size, tmp_size;
const uint8_t *shifted_data;
tmp_size = VB2_SHA256_BLOCK_SIZE - ctx->size;
rem_size = size < tmp_size ? size : tmp_size;
memcpy(&ctx->block[ctx->size], data, rem_size);
if (ctx->size + size < VB2_SHA256_BLOCK_SIZE) {
ctx->size += size;
return;
}
new_size = size - rem_size;
block_nb = new_size / VB2_SHA256_BLOCK_SIZE;
shifted_data = data + rem_size;
vb2_sha256_transform(ctx, ctx->block, 1);
vb2_sha256_transform(ctx, shifted_data, block_nb);
rem_size = new_size % VB2_SHA256_BLOCK_SIZE;
memcpy(ctx->block, &shifted_data[block_nb << 6],
rem_size);
ctx->size = rem_size;
ctx->total_size += (block_nb + 1) << 6;
}
void vb2_sha256_finalize(struct vb2_sha256_context *ctx, uint8_t *digest)
{
unsigned int block_nb;
unsigned int pm_size;
unsigned int size_b;
#ifndef UNROLL_LOOPS
int i;
#endif
block_nb = (1 + ((VB2_SHA256_BLOCK_SIZE - 9)
< (ctx->size % VB2_SHA256_BLOCK_SIZE)));
size_b = (ctx->total_size + ctx->size) << 3;
pm_size = block_nb << 6;
memset(ctx->block + ctx->size, 0, pm_size - ctx->size);
ctx->block[ctx->size] = 0x80;
UNPACK32(size_b, ctx->block + pm_size - 4);
vb2_sha256_transform(ctx, ctx->block, block_nb);
#ifndef UNROLL_LOOPS
for (i = 0 ; i < 8; i++) {
UNPACK32(ctx->h[i], &digest[i << 2]);
}
#else
UNPACK32(ctx->h[0], &digest[ 0]);
UNPACK32(ctx->h[1], &digest[ 4]);
UNPACK32(ctx->h[2], &digest[ 8]);
UNPACK32(ctx->h[3], &digest[12]);
UNPACK32(ctx->h[4], &digest[16]);
UNPACK32(ctx->h[5], &digest[20]);
UNPACK32(ctx->h[6], &digest[24]);
UNPACK32(ctx->h[7], &digest[28]);
#endif /* !UNROLL_LOOPS */
}
void vb2_sha256_extend(const uint8_t *from, const uint8_t *by, uint8_t *to)
{
struct vb2_sha256_context dc;
int i;
for (i = 0; i < 8; i++) {
PACK32(from, &dc.h[i]);
from += 4;
}
vb2_sha256_transform(&dc, by, 1);
for (i = 0; i < 8; i++) {
UNPACK32(dc.h[i], to);
to += 4;
}
}

View File

@@ -0,0 +1,346 @@
/* SHA-256 and SHA-512 implementation based on code by Oliver Gay
* <olivier.gay@a3.epfl.ch> under a BSD-style license. See below.
*/
/*
* FIPS 180-2 SHA-224/256/384/512 implementation
* Last update: 02/02/2007
* Issue date: 04/30/2005
*
* Copyright (C) 2005, 2007 Olivier Gay <olivier.gay@a3.epfl.ch>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "2sysincludes.h"
#include "2common.h"
#include "2sha.h"
#define SHFR(x, n) (x >> n)
#define ROTR(x, n) ((x >> n) | (x << ((sizeof(x) << 3) - n)))
#define ROTL(x, n) ((x << n) | (x >> ((sizeof(x) << 3) - n)))
#define CH(x, y, z) ((x & y) ^ (~x & z))
#define MAJ(x, y, z) ((x & y) ^ (x & z) ^ (y & z))
#define SHA512_F1(x) (ROTR(x, 28) ^ ROTR(x, 34) ^ ROTR(x, 39))
#define SHA512_F2(x) (ROTR(x, 14) ^ ROTR(x, 18) ^ ROTR(x, 41))
#define SHA512_F3(x) (ROTR(x, 1) ^ ROTR(x, 8) ^ SHFR(x, 7))
#define SHA512_F4(x) (ROTR(x, 19) ^ ROTR(x, 61) ^ SHFR(x, 6))
#define UNPACK32(x, str) \
{ \
*((str) + 3) = (uint8_t) ((x) ); \
*((str) + 2) = (uint8_t) ((x) >> 8); \
*((str) + 1) = (uint8_t) ((x) >> 16); \
*((str) + 0) = (uint8_t) ((x) >> 24); \
}
#define UNPACK64(x, str) \
{ \
*((str) + 7) = (uint8_t) x; \
*((str) + 6) = (uint8_t) ((uint64_t)x >> 8); \
*((str) + 5) = (uint8_t) ((uint64_t)x >> 16); \
*((str) + 4) = (uint8_t) ((uint64_t)x >> 24); \
*((str) + 3) = (uint8_t) ((uint64_t)x >> 32); \
*((str) + 2) = (uint8_t) ((uint64_t)x >> 40); \
*((str) + 1) = (uint8_t) ((uint64_t)x >> 48); \
*((str) + 0) = (uint8_t) ((uint64_t)x >> 56); \
}
#define PACK64(str, x) \
{ \
*(x) = ((uint64_t) *((str) + 7) ) \
| ((uint64_t) *((str) + 6) << 8) \
| ((uint64_t) *((str) + 5) << 16) \
| ((uint64_t) *((str) + 4) << 24) \
| ((uint64_t) *((str) + 3) << 32) \
| ((uint64_t) *((str) + 2) << 40) \
| ((uint64_t) *((str) + 1) << 48) \
| ((uint64_t) *((str) + 0) << 56); \
}
/* Macros used for loops unrolling */
#define SHA512_SCR(i) \
{ \
w[i] = SHA512_F4(w[i - 2]) + w[i - 7] \
+ SHA512_F3(w[i - 15]) + w[i - 16]; \
}
#define SHA512_EXP(a, b, c, d, e, f, g ,h, j) \
{ \
t1 = wv[h] + SHA512_F2(wv[e]) + CH(wv[e], wv[f], wv[g]) \
+ sha512_k[j] + w[j]; \
t2 = SHA512_F1(wv[a]) + MAJ(wv[a], wv[b], wv[c]); \
wv[d] += t1; \
wv[h] = t1 + t2; \
}
static const uint64_t sha512_h0[8] = {
0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL,
0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL,
0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL,
0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL
};
static const uint64_t sha512_k[80] = {
0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL,
0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL,
0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL,
0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL,
0xd807aa98a3030242ULL, 0x12835b0145706fbeULL,
0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL,
0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL,
0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL,
0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL,
0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL,
0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL,
0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL,
0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL,
0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL,
0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL,
0x06ca6351e003826fULL, 0x142929670a0e6e70ULL,
0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL,
0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL,
0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL,
0x81c2c92e47edaee6ULL, 0x92722c851482353bULL,
0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL,
0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL,
0xd192e819d6ef5218ULL, 0xd69906245565a910ULL,
0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL,
0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL,
0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL,
0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL,
0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL,
0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL,
0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL,
0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL,
0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL,
0xca273eceea26619cULL, 0xd186b8c721c0c207ULL,
0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL,
0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL,
0x113f9804bef90daeULL, 0x1b710b35131c471bULL,
0x28db77f523047d84ULL, 0x32caab7b40c72493ULL,
0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL,
0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL,
0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL
};
/* SHA-512 implementation */
void vb2_sha512_init(struct vb2_sha512_context *ctx)
{
#ifdef UNROLL_LOOPS_SHA512
ctx->h[0] = sha512_h0[0]; ctx->h[1] = sha512_h0[1];
ctx->h[2] = sha512_h0[2]; ctx->h[3] = sha512_h0[3];
ctx->h[4] = sha512_h0[4]; ctx->h[5] = sha512_h0[5];
ctx->h[6] = sha512_h0[6]; ctx->h[7] = sha512_h0[7];
#else
int i;
for (i = 0; i < 8; i++)
ctx->h[i] = sha512_h0[i];
#endif /* UNROLL_LOOPS_SHA512 */
ctx->size = 0;
ctx->total_size = 0;
}
static void vb2_sha512_transform(struct vb2_sha512_context *ctx,
const uint8_t *message,
unsigned int block_nb)
{
/* Note that these arrays use 88*8=704 bytes of stack */
uint64_t w[80];
uint64_t wv[8];
uint64_t t1, t2;
const uint8_t *sub_block;
int i, j;
for (i = 0; i < (int) block_nb; i++) {
sub_block = message + (i << 7);
#ifdef UNROLL_LOOPS_SHA512
PACK64(&sub_block[ 0], &w[ 0]);
PACK64(&sub_block[ 8], &w[ 1]);
PACK64(&sub_block[ 16], &w[ 2]);
PACK64(&sub_block[ 24], &w[ 3]);
PACK64(&sub_block[ 32], &w[ 4]);
PACK64(&sub_block[ 40], &w[ 5]);
PACK64(&sub_block[ 48], &w[ 6]);
PACK64(&sub_block[ 56], &w[ 7]);
PACK64(&sub_block[ 64], &w[ 8]);
PACK64(&sub_block[ 72], &w[ 9]);
PACK64(&sub_block[ 80], &w[10]);
PACK64(&sub_block[ 88], &w[11]);
PACK64(&sub_block[ 96], &w[12]);
PACK64(&sub_block[104], &w[13]);
PACK64(&sub_block[112], &w[14]);
PACK64(&sub_block[120], &w[15]);
SHA512_SCR(16); SHA512_SCR(17); SHA512_SCR(18); SHA512_SCR(19);
SHA512_SCR(20); SHA512_SCR(21); SHA512_SCR(22); SHA512_SCR(23);
SHA512_SCR(24); SHA512_SCR(25); SHA512_SCR(26); SHA512_SCR(27);
SHA512_SCR(28); SHA512_SCR(29); SHA512_SCR(30); SHA512_SCR(31);
SHA512_SCR(32); SHA512_SCR(33); SHA512_SCR(34); SHA512_SCR(35);
SHA512_SCR(36); SHA512_SCR(37); SHA512_SCR(38); SHA512_SCR(39);
SHA512_SCR(40); SHA512_SCR(41); SHA512_SCR(42); SHA512_SCR(43);
SHA512_SCR(44); SHA512_SCR(45); SHA512_SCR(46); SHA512_SCR(47);
SHA512_SCR(48); SHA512_SCR(49); SHA512_SCR(50); SHA512_SCR(51);
SHA512_SCR(52); SHA512_SCR(53); SHA512_SCR(54); SHA512_SCR(55);
SHA512_SCR(56); SHA512_SCR(57); SHA512_SCR(58); SHA512_SCR(59);
SHA512_SCR(60); SHA512_SCR(61); SHA512_SCR(62); SHA512_SCR(63);
SHA512_SCR(64); SHA512_SCR(65); SHA512_SCR(66); SHA512_SCR(67);
SHA512_SCR(68); SHA512_SCR(69); SHA512_SCR(70); SHA512_SCR(71);
SHA512_SCR(72); SHA512_SCR(73); SHA512_SCR(74); SHA512_SCR(75);
SHA512_SCR(76); SHA512_SCR(77); SHA512_SCR(78); SHA512_SCR(79);
wv[0] = ctx->h[0]; wv[1] = ctx->h[1];
wv[2] = ctx->h[2]; wv[3] = ctx->h[3];
wv[4] = ctx->h[4]; wv[5] = ctx->h[5];
wv[6] = ctx->h[6]; wv[7] = ctx->h[7];
j = 0;
do {
SHA512_EXP(0,1,2,3,4,5,6,7,j); j++;
SHA512_EXP(7,0,1,2,3,4,5,6,j); j++;
SHA512_EXP(6,7,0,1,2,3,4,5,j); j++;
SHA512_EXP(5,6,7,0,1,2,3,4,j); j++;
SHA512_EXP(4,5,6,7,0,1,2,3,j); j++;
SHA512_EXP(3,4,5,6,7,0,1,2,j); j++;
SHA512_EXP(2,3,4,5,6,7,0,1,j); j++;
SHA512_EXP(1,2,3,4,5,6,7,0,j); j++;
} while (j < 80);
ctx->h[0] += wv[0]; ctx->h[1] += wv[1];
ctx->h[2] += wv[2]; ctx->h[3] += wv[3];
ctx->h[4] += wv[4]; ctx->h[5] += wv[5];
ctx->h[6] += wv[6]; ctx->h[7] += wv[7];
#else
for (j = 0; j < 16; j++) {
PACK64(&sub_block[j << 3], &w[j]);
}
for (j = 16; j < 80; j++) {
SHA512_SCR(j);
}
for (j = 0; j < 8; j++) {
wv[j] = ctx->h[j];
}
for (j = 0; j < 80; j++) {
t1 = wv[7] + SHA512_F2(wv[4]) + CH(wv[4], wv[5], wv[6])
+ sha512_k[j] + w[j];
t2 = SHA512_F1(wv[0]) + MAJ(wv[0], wv[1], wv[2]);
wv[7] = wv[6];
wv[6] = wv[5];
wv[5] = wv[4];
wv[4] = wv[3] + t1;
wv[3] = wv[2];
wv[2] = wv[1];
wv[1] = wv[0];
wv[0] = t1 + t2;
}
for (j = 0; j < 8; j++)
ctx->h[j] += wv[j];
#endif /* UNROLL_LOOPS_SHA512 */
}
}
void vb2_sha512_update(struct vb2_sha512_context *ctx,
const uint8_t *data,
uint32_t size)
{
unsigned int block_nb;
unsigned int new_size, rem_size, tmp_size;
const uint8_t *shifted_data;
tmp_size = VB2_SHA512_BLOCK_SIZE - ctx->size;
rem_size = size < tmp_size ? size : tmp_size;
memcpy(&ctx->block[ctx->size], data, rem_size);
if (ctx->size + size < VB2_SHA512_BLOCK_SIZE) {
ctx->size += size;
return;
}
new_size = size - rem_size;
block_nb = new_size / VB2_SHA512_BLOCK_SIZE;
shifted_data = data + rem_size;
vb2_sha512_transform(ctx, ctx->block, 1);
vb2_sha512_transform(ctx, shifted_data, block_nb);
rem_size = new_size % VB2_SHA512_BLOCK_SIZE;
memcpy(ctx->block, &shifted_data[block_nb << 7],
rem_size);
ctx->size = rem_size;
ctx->total_size += (block_nb + 1) << 7;
}
void vb2_sha512_finalize(struct vb2_sha512_context *ctx, uint8_t *digest)
{
unsigned int block_nb;
unsigned int pm_size;
unsigned int size_b;
#ifndef UNROLL_LOOPS_SHA512
int i;
#endif
block_nb = 1 + ((VB2_SHA512_BLOCK_SIZE - 17)
< (ctx->size % VB2_SHA512_BLOCK_SIZE));
size_b = (ctx->total_size + ctx->size) << 3;
pm_size = block_nb << 7;
memset(ctx->block + ctx->size, 0, pm_size - ctx->size);
ctx->block[ctx->size] = 0x80;
UNPACK32(size_b, ctx->block + pm_size - 4);
vb2_sha512_transform(ctx, ctx->block, block_nb);
#ifdef UNROLL_LOOPS_SHA512
UNPACK64(ctx->h[0], &digest[ 0]);
UNPACK64(ctx->h[1], &digest[ 8]);
UNPACK64(ctx->h[2], &digest[16]);
UNPACK64(ctx->h[3], &digest[24]);
UNPACK64(ctx->h[4], &digest[32]);
UNPACK64(ctx->h[5], &digest[40]);
UNPACK64(ctx->h[6], &digest[48]);
UNPACK64(ctx->h[7], &digest[56]);
#else
for (i = 0 ; i < 8; i++)
UNPACK64(ctx->h[i], &digest[i << 3]);
#endif /* UNROLL_LOOPS_SHA512 */
}

View File

@@ -0,0 +1,217 @@
/* 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.
*
* Utility functions for message digest functions.
*/
#include "2sysincludes.h"
#include "2common.h"
#include "2sha.h"
#if VB2_SUPPORT_SHA1
#define CTH_SHA1 VB2_HASH_SHA1
#else
#define CTH_SHA1 VB2_HASH_INVALID
#endif
#if VB2_SUPPORT_SHA256
#define CTH_SHA256 VB2_HASH_SHA256
#else
#define CTH_SHA256 VB2_HASH_INVALID
#endif
#if VB2_SUPPORT_SHA512
#define CTH_SHA512 VB2_HASH_SHA512
#else
#define CTH_SHA512 VB2_HASH_INVALID
#endif
static const uint8_t crypto_to_hash[] = {
CTH_SHA1,
CTH_SHA256,
CTH_SHA512,
CTH_SHA1,
CTH_SHA256,
CTH_SHA512,
CTH_SHA1,
CTH_SHA256,
CTH_SHA512,
CTH_SHA1,
CTH_SHA256,
CTH_SHA512,
CTH_SHA1,
CTH_SHA256,
CTH_SHA512,
CTH_SHA1,
CTH_SHA256,
CTH_SHA512,
};
enum vb2_hash_algorithm vb2_crypto_to_hash(uint32_t algorithm)
{
if (algorithm < ARRAY_SIZE(crypto_to_hash))
return crypto_to_hash[algorithm];
else
return VB2_HASH_INVALID;
}
int vb2_digest_size(enum vb2_hash_algorithm hash_alg)
{
switch (hash_alg) {
#if VB2_SUPPORT_SHA1
case VB2_HASH_SHA1:
return VB2_SHA1_DIGEST_SIZE;
#endif
#if VB2_SUPPORT_SHA256
case VB2_HASH_SHA256:
return VB2_SHA256_DIGEST_SIZE;
#endif
#if VB2_SUPPORT_SHA512
case VB2_HASH_SHA512:
return VB2_SHA512_DIGEST_SIZE;
#endif
default:
return 0;
}
}
int vb2_hash_block_size(enum vb2_hash_algorithm alg)
{
switch (alg) {
#if VB2_SUPPORT_SHA1
case VB2_HASH_SHA1:
return VB2_SHA1_BLOCK_SIZE;
#endif
#if VB2_SUPPORT_SHA256
case VB2_HASH_SHA256:
return VB2_SHA256_BLOCK_SIZE;
#endif
#if VB2_SUPPORT_SHA512
case VB2_HASH_SHA512:
return VB2_SHA512_BLOCK_SIZE;
#endif
default:
return 0;
}
}
const char *vb2_get_hash_algorithm_name(enum vb2_hash_algorithm alg)
{
switch (alg) {
#if VB2_SUPPORT_SHA1
case VB2_HASH_SHA1:
return VB2_SHA1_ALG_NAME;
#endif
#if VB2_SUPPORT_SHA256
case VB2_HASH_SHA256:
return VB2_SHA256_ALG_NAME;
#endif
#if VB2_SUPPORT_SHA512
case VB2_HASH_SHA512:
return VB2_SHA512_ALG_NAME;
#endif
default:
return VB2_INVALID_ALG_NAME;
}
}
int vb2_digest_init(struct vb2_digest_context *dc,
enum vb2_hash_algorithm hash_alg)
{
dc->hash_alg = hash_alg;
dc->using_hwcrypto = 0;
switch (dc->hash_alg) {
#if VB2_SUPPORT_SHA1
case VB2_HASH_SHA1:
vb2_sha1_init(&dc->sha1);
return VB2_SUCCESS;
#endif
#if VB2_SUPPORT_SHA256
case VB2_HASH_SHA256:
vb2_sha256_init(&dc->sha256);
return VB2_SUCCESS;
#endif
#if VB2_SUPPORT_SHA512
case VB2_HASH_SHA512:
vb2_sha512_init(&dc->sha512);
return VB2_SUCCESS;
#endif
default:
return VB2_ERROR_SHA_INIT_ALGORITHM;
}
}
int vb2_digest_extend(struct vb2_digest_context *dc,
const uint8_t *buf,
uint32_t size)
{
switch (dc->hash_alg) {
#if VB2_SUPPORT_SHA1
case VB2_HASH_SHA1:
vb2_sha1_update(&dc->sha1, buf, size);
return VB2_SUCCESS;
#endif
#if VB2_SUPPORT_SHA256
case VB2_HASH_SHA256:
vb2_sha256_update(&dc->sha256, buf, size);
return VB2_SUCCESS;
#endif
#if VB2_SUPPORT_SHA512
case VB2_HASH_SHA512:
vb2_sha512_update(&dc->sha512, buf, size);
return VB2_SUCCESS;
#endif
default:
return VB2_ERROR_SHA_EXTEND_ALGORITHM;
}
}
int vb2_digest_finalize(struct vb2_digest_context *dc,
uint8_t *digest,
uint32_t digest_size)
{
if (digest_size < vb2_digest_size(dc->hash_alg))
return VB2_ERROR_SHA_FINALIZE_DIGEST_SIZE;
switch (dc->hash_alg) {
#if VB2_SUPPORT_SHA1
case VB2_HASH_SHA1:
vb2_sha1_finalize(&dc->sha1, digest);
return VB2_SUCCESS;
#endif
#if VB2_SUPPORT_SHA256
case VB2_HASH_SHA256:
vb2_sha256_finalize(&dc->sha256, digest);
return VB2_SUCCESS;
#endif
#if VB2_SUPPORT_SHA512
case VB2_HASH_SHA512:
vb2_sha512_finalize(&dc->sha512, digest);
return VB2_SUCCESS;
#endif
default:
return VB2_ERROR_SHA_FINALIZE_ALGORITHM;
}
}
int vb2_digest_buffer(const uint8_t *buf,
uint32_t size,
enum vb2_hash_algorithm hash_alg,
uint8_t *digest,
uint32_t digest_size)
{
struct vb2_digest_context dc;
int rv;
rv = vb2_digest_init(&dc, hash_alg);
if (rv)
return rv;
rv = vb2_digest_extend(&dc, buf, size);
if (rv)
return rv;
return vb2_digest_finalize(&dc, digest, digest_size);
}

View File

@@ -0,0 +1,60 @@
/* 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.
*
* Stub API implementations which should be implemented by the caller.
*/
#include <stdarg.h>
#include <stdio.h>
#include "2sysincludes.h"
#include "2api.h"
__attribute__((weak))
void vb2ex_printf(const char *func, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (func)
fprintf(stderr, "%s: ", func);
vfprintf(stderr, fmt, ap);
va_end(ap);
}
__attribute__((weak))
int vb2ex_tpm_clear_owner(struct vb2_context *ctx)
{
return VB2_ERROR_EX_TPM_CLEAR_OWNER_UNIMPLEMENTED;
}
__attribute__((weak))
int vb2ex_read_resource(struct vb2_context *ctx,
enum vb2_resource_index index,
uint32_t offset,
void *buf,
uint32_t size)
{
return VB2_ERROR_EX_READ_RESOURCE_UNIMPLEMENTED;
}
__attribute__((weak))
int vb2ex_hwcrypto_digest_init(enum vb2_hash_algorithm hash_alg,
uint32_t data_size)
{
return VB2_ERROR_EX_HWCRYPTO_UNSUPPORTED;
}
__attribute__((weak))
int vb2ex_hwcrypto_digest_extend(const uint8_t *buf,
uint32_t size)
{
return VB2_ERROR_SHA_EXTEND_ALGORITHM; /* Should not be called. */
}
__attribute__((weak))
int vb2ex_hwcrypto_digest_finalize(uint8_t *digest,
uint32_t digest_size)
{
return VB2_ERROR_SHA_FINALIZE_ALGORITHM; /* Should not be called. */
}

View File

@@ -0,0 +1,54 @@
/* Copyright 2015 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 updating the TPM state with the status of boot path.
*/
#include "2sysincludes.h"
#include "2common.h"
#include "2sha.h"
#include "2tpm_bootmode.h"
/*
* Input digests for PCR extend.
* These are calculated as:
* SHA1("|Developer_Mode||Recovery_Mode||Keyblock_Mode|").
* Developer_Mode can be 0 or 1.
* Recovery_Mode can be 0 or 1.
* Keyblock flags are defined in 2struct.h and assumed always 0 in recovery mode
* or 7 in non-recovery mode.
*
* We map them to Keyblock_Mode as follows:
* -----------------------------------------
* Keyblock Flags | Keyblock Mode
* -----------------------------------------
* 0 recovery mode | 0
* 7 Normal-signed firmware | 1
*/
const uint8_t kBootStateSHA1Digests[][VB2_SHA1_DIGEST_SIZE] = {
/* SHA1(0x00|0x00|0x01) */
{0x25, 0x47, 0xcc, 0x73, 0x6e, 0x95, 0x1f, 0xa4, 0x91, 0x98, 0x53, 0xc4,
0x3a, 0xe8, 0x90, 0x86, 0x1a, 0x3b, 0x32, 0x64},
/* SHA1(0x01|0x00|0x01) */
{0xc4, 0x2a, 0xc1, 0xc4, 0x6f, 0x1d, 0x4e, 0x21, 0x1c, 0x73, 0x5c, 0xc7,
0xdf, 0xad, 0x4f, 0xf8, 0x39, 0x11, 0x10, 0xe9},
/* SHA1(0x00|0x01|0x00) */
{0x62, 0x57, 0x18, 0x91, 0x21, 0x5b, 0x4e, 0xfc, 0x1c, 0xea, 0xb7, 0x44,
0xce, 0x59, 0xdd, 0x0b, 0x66, 0xea, 0x6f, 0x73},
/* SHA1(0x01|0x01|0x00) */
{0x47, 0xec, 0x8d, 0x98, 0x36, 0x64, 0x33, 0xdc, 0x00, 0x2e, 0x77, 0x21,
0xc9, 0xe3, 0x7d, 0x50, 0x67, 0x54, 0x79, 0x37},
};
const uint8_t *vb2_get_boot_state_digest(struct vb2_context *ctx)
{
int index = (ctx->flags & VB2_CONTEXT_RECOVERY_MODE ? 2 : 0) +
(ctx->flags & VB2_CONTEXT_DEVELOPER_MODE ? 1 : 0);
return kBootStateSHA1Digests[index];
}

View File

@@ -0,0 +1,716 @@
/* 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.
*/
/* APIs between calling firmware and vboot_reference
*
* General notes:
*
* TODO: split this file into a vboot_entry_points.h file which contains the
* entry points for the firmware to call vboot_reference, and a
* vboot_firmware_exports.h which contains the APIs to be implemented by the
* calling firmware and exported to vboot_reference.
*
* Notes:
* * Assumes this code is never called in the S3 resume path. TPM resume
* must be done elsewhere, and VB2_NV_DEBUG_RESET_MODE is ignored.
*/
#ifndef VBOOT_2_API_H_
#define VBOOT_2_API_H_
#include <stdint.h>
#include "2common.h"
#include "2crypto.h"
#include "2fw_hash_tags.h"
#include "2id.h"
#include "2recovery_reasons.h"
#include "2return_codes.h"
/*
* Size of non-volatile data used by vboot.
*
* If you only support non-volatile data format V1, then use VB2_NVDATA_SIZE.
* If you support V2, use VB2_NVDATA_SIZE_V2 and set context flag
* VB2_CONTEXT_NVDATA_V2.
*/
#define VB2_NVDATA_SIZE 16
#define VB2_NVDATA_SIZE_V2 64
/* Size of secure data spaces used by vboot */
#define VB2_SECDATA_SIZE 10
#define VB2_SECDATAK_SIZE 14
/*
* Recommended size of work buffer for firmware verification stage
*
* TODO: The recommended size really depends on which key algorithms are
* used. Should have a better / more accurate recommendation than this.
*/
#define VB2_WORKBUF_RECOMMENDED_SIZE (12 * 1024)
/*
* Recommended size of work buffer for kernel verification stage
*
* This is bigger because vboot 2.0 kernel preambles are usually padded to
* 64 KB.
*
* TODO: The recommended size really depends on which key algorithms are
* used. Should have a better / more accurate recommendation than this.
*/
#define VB2_KERNEL_WORKBUF_RECOMMENDED_SIZE (80 * 1024)
/* Recommended buffer size for vb2api_get_pcr_digest */
#define VB2_PCR_DIGEST_RECOMMENDED_SIZE 32
/* Flags for vb2_context.
*
* Unless otherwise noted, flags are set by verified boot and may be read (but
* not set or cleared) by the caller.
*/
enum vb2_context_flags {
/*
* Verified boot has changed nvdata[]. Caller must save nvdata[] back
* to its underlying storage, then may clear this flag.
*/
VB2_CONTEXT_NVDATA_CHANGED = (1 << 0),
/*
* Verified boot has changed secdata[]. Caller must save secdata[]
* back to its underlying storage, then may clear this flag.
*/
VB2_CONTEXT_SECDATA_CHANGED = (1 << 1),
/* Recovery mode is requested this boot */
VB2_CONTEXT_RECOVERY_MODE = (1 << 2),
/* Developer mode is requested this boot */
VB2_CONTEXT_DEVELOPER_MODE = (1 << 3),
/*
* Force recovery mode due to physical user request. Caller may set
* this flag when initializing the context.
*/
VB2_CONTEXT_FORCE_RECOVERY_MODE = (1 << 4),
/*
* Force developer mode enabled. Caller may set this flag when
* initializing the context.
*/
VB2_CONTEXT_FORCE_DEVELOPER_MODE = (1 << 5),
/* Using firmware slot B. If this flag is clear, using slot A. */
VB2_CONTEXT_FW_SLOT_B = (1 << 6),
/* RAM should be cleared by caller this boot */
VB2_CONTEXT_CLEAR_RAM = (1 << 7),
/* Wipeout by the app should be requested. */
VB2_CONTEXT_FORCE_WIPEOUT_MODE = (1 << 8),
/* Erase TPM developer mode state if it is enabled. */
VB2_DISABLE_DEVELOPER_MODE = (1 << 9),
/*
* Verified boot has changed secdatak[]. Caller must save secdatak[]
* back to its underlying storage, then may clear this flag.
*/
VB2_CONTEXT_SECDATAK_CHANGED = (1 << 10),
/*
* Allow kernel verification to roll forward the version in secdatak[].
* Caller may set this flag before calling vb2api_kernel_phase3().
*/
VB2_CONTEXT_ALLOW_KERNEL_ROLL_FORWARD = (1 << 11),
/*
* Boot optimistically: don't touch failure counters. Caller may set
* this flag when initializing the context.
*/
VB2_CONTEXT_NOFAIL_BOOT = (1 << 12),
/*
* Secdata is not ready this boot, but should be ready next boot. It
* would like to reboot. The decision whether to reboot or not must be
* deferred until vboot, because rebooting all the time before then
* could cause a device with malfunctioning secdata to get stuck in an
* unrecoverable crash loop.
*/
VB2_CONTEXT_SECDATA_WANTS_REBOOT = (1 << 13),
/*
* Boot is S3->S0 resume, not S5->S0 normal boot. Caller may set this
* flag when initializing the context.
*/
VB2_CONTEXT_S3_RESUME = (1 << 14),
/*
* System supports EC software sync. Caller may set this flag at any
* time before calling VbSelectAndLoadKernel().
*/
VB2_CONTEXT_EC_SYNC_SUPPORTED = (1 << 15),
/*
* EC software sync is slow to update; warning screen should be
* displayed. Caller may set this flag at any time before calling
* VbSelectAndLoadKernel().
*/
VB2_CONTEXT_EC_SYNC_SLOW = (1 << 16),
/*
* EC firmware supports early firmware selection; two EC images exist,
* and EC may have already verified and jumped to EC-RW prior to EC
* software sync.
*/
VB2_CONTEXT_EC_EFS = (1 << 17),
/*
* NV storage uses data format V2. Data is size VB2_NVDATA_SIZE_V2,
* not VB2_NVDATA_SIZE.
*
* Caller must set this flag when initializing the context to use V2.
* (Vboot cannot infer the data size from the data itself, because the
* data provided by the caller could be uninitialized.)
*/
VB2_CONTEXT_NVDATA_V2 = (1 << 18),
};
/*
* Context for firmware verification. Pass this to all vboot APIs.
*
* Caller may relocate this between calls to vboot APIs.
*/
struct vb2_context {
/**********************************************************************
* Fields which must be initialized by caller.
*/
/*
* Flags; see vb2_context_flags. Some flags may only be set by caller
* prior to calling vboot functions.
*/
uint32_t flags;
/*
* Work buffer, and length in bytes. Caller may relocate this between
* calls to vboot APIs; it contains no internal pointers. Caller must
* not examine the contents of this work buffer directly.
*/
uint8_t *workbuf;
uint32_t workbuf_size;
/*
* Non-volatile data. Caller must fill this from some non-volatile
* location. If the VB2_CONTEXT_NVDATA_CHANGED flag is set when a
* vb2api function returns, caller must save the data back to the
* non-volatile location and then clear the flag.
*/
uint8_t nvdata[VB2_NVDATA_SIZE_V2];
/*
* Secure data for firmware verification stage. Caller must fill this
* from some secure non-volatile location. If the
* VB2_CONTEXT_SECDATA_CHANGED flag is set when a function returns,
* caller must save the data back to the secure non-volatile location
* and then clear the flag.
*/
uint8_t secdata[VB2_SECDATA_SIZE];
/*
* Context pointer for use by caller. Verified boot never looks at
* this. Put context here if you need it for APIs that verified boot
* may call (vb2ex_...() functions).
*/
void *non_vboot_context;
/**********************************************************************
* Fields caller may examine after calling vb2api_fw_phase1(). Caller
* must set these fields to 0 before calling any vboot functions.
*/
/*
* Amount of work buffer used so far. Verified boot sub-calls use
* this to know where the unused work area starts. Caller may use
* this between calls to vboot APIs to know how much data must be
* copied when relocating the work buffer.
*/
uint32_t workbuf_used;
/**********************************************************************
* Fields caller must initialize before calling vb2api_kernel_phase1().
*/
/*
* Secure data for kernel verification stage. Caller must fill this
* from some secure non-volatile location. If the
* VB2_CONTEXT_SECDATAK_CHANGED flag is set when a function returns,
* caller must save the data back to the secure non-volatile location
* and then clear the flag.
*/
uint8_t secdatak[VB2_SECDATAK_SIZE];
};
/* Resource index for vb2ex_read_resource() */
enum vb2_resource_index {
/* Google binary block */
VB2_RES_GBB,
/*
* Firmware verified boot block (keyblock+preamble). Use
* VB2_CONTEXT_FW_SLOT_B to determine whether this refers to slot A or
* slot B; vboot will set that flag to the proper state before reading
* the vblock.
*/
VB2_RES_FW_VBLOCK,
/*
* Kernel verified boot block (keyblock+preamble) for the current
* kernel partition. Used only by vb2api_kernel_load_vblock().
* Contents are allowed to change between calls to that function (to
* allow multiple kernels to be examined).
*/
VB2_RES_KERNEL_VBLOCK,
};
/* Digest ID for vbapi_get_pcr_digest() */
enum vb2_pcr_digest {
/* Digest based on current developer and recovery mode flags */
BOOT_MODE_PCR,
/* SHA-256 hash digest of HWID, from GBB */
HWID_DIGEST_PCR,
};
/******************************************************************************
* APIs provided by verified boot.
*
* At a high level, call functions in the order described below. After each
* call, examine vb2_context.flags to determine whether nvdata or secdata
* needs to be written.
*
* If you need to cause the boot process to fail at any point, call
* vb2api_fail(). Then check vb2_context.flags to see what data needs to be
* written. Then reboot.
*
* Load nvdata from wherever you keep it.
*
* Load secdata from wherever you keep it.
*
* If it wasn't there at all (for example, this is the first boot
* of a new system in the factory), call vb2api_secdata_create()
* to initialize the data.
*
* If access to your storage is unreliable (reads/writes may
* contain corrupt data), you may call vb2api_secdata_check() to
* determine if the data was valid, and retry reading if it
* wasn't. (In that case, you should also read back and check the
* data after any time you write it, to make sure it was written
* correctly.)
*
* Call vb2api_fw_phase1(). At present, this nominally decides whether
* recovery mode is needed this boot.
*
* Call vb2api_fw_phase2(). At present, this nominally decides which
* firmware slot will be attempted (A or B).
*
* Call vb2api_fw_phase3(). At present, this nominally verifies the
* firmware keyblock and preamble.
*
* Lock down wherever you keep secdata. It should no longer be writable
* this boot.
*
* Verify the hash of each section of code/data you need to boot the RW
* firmware. For each section:
*
* Call vb2_init_hash() to see if the hash exists.
*
* Load the data for the section. Call vb2_extend_hash() on the
* data as you load it. You can load it all at once and make one
* call, or load and hash-extend a block at a time.
*
* Call vb2_check_hash() to see if the hash is valid.
*
* If it is valid, you may use the data and/or execute
* code from that section.
*
* If the hash was invalid, you must reboot.
*
* At this point, firmware verification is done, and vb2_context contains the
* kernel key needed to verify the kernel. That context should be preserved
* and passed on to kernel selection. The kernel selection process may be
* done by the same firmware image, or may be done by the RW firmware. The
* recommended order is:
*
* Load secdatak from wherever you keep it.
*
* If it wasn't there at all (for example, this is the first boot
* of a new system in the factory), call vb2api_secdatak_create()
* to initialize the data.
*
* If access to your storage is unreliable (reads/writes may
* contain corrupt data), you may call vb2api_secdatak_check() to
* determine if the data was valid, and retry reading if it
* wasn't. (In that case, you should also read back and check the
* data after any time you write it, to make sure it was written
* correctly.)
*
* Call vb2api_kernel_phase1(). At present, this decides which key to
* use to verify kernel data - the recovery key from the GBB, or the
* kernel subkey from the firmware verification stage.
*
* Kernel phase 2 is finding loading, and verifying the kernel partition.
*
* Find a boot device (you're on your own here).
*
* Call vb2api_load_kernel_vblock() for each kernel partition on the
* boot device, until one succeeds.
*
* When that succeeds, call vb2api_get_kernel_size() to determine where
* the kernel is located in the stream and how big it is. Load or map
* the kernel. (Again, you're on your own. This is the responsibility of
* the caller so that the caller can choose whether to allocate a buffer,
* load the kernel data into a predefined area of RAM, or directly map a
* kernel file into the address space. Note that technically it doesn't
* matter whether the kernel data is even in the same file or stream as
* the vblock, as long as the caller loads the right data.
*
* Call vb2api_verify_kernel_data() on the kernel data.
*
* If you ran out of kernels before finding a good one, call vb2api_fail()
* with an appropriate recovery reason.
*
* Set the VB2_CONTEXT_ALLOW_KERNEL_ROLL_FORWARD flag if the current
* kernel partition has the successful flag (that is, it's already known
* or assumed to be a functional kernel partition).
*
* Call vb2api_kernel_phase3(). This cleans up from kernel verification
* and updates the secure data if needed.
*
* Lock down wherever you keep secdatak. It should no longer be writable
* this boot.
*/
/**
* Sanity-check the contents of the secure storage context.
*
* Use this if reading from secure storage may be flaky, and you want to retry
* reading it several times.
*
* This may be called before vb2api_phase1().
*
* @param ctx Context pointer
* @return VB2_SUCCESS, or non-zero error code if error.
*/
int vb2api_secdata_check(const struct vb2_context *ctx);
/**
* Create fresh data in the secure storage context.
*
* Use this only when initializing the secure storage context on a new machine
* the first time it boots. Do NOT simply use this if vb2api_secdata_check()
* (or any other API in this library) fails; that could allow the secure data
* to be rolled back to an insecure state.
*
* This may be called before vb2api_phase1().
*
* @param ctx Context pointer
* @return VB2_SUCCESS, or non-zero error code if error.
*/
int vb2api_secdata_create(struct vb2_context *ctx);
/**
* Sanity-check the contents of the kernel version secure storage context.
*
* Use this if reading from secure storage may be flaky, and you want to retry
* reading it several times.
*
* This may be called before vb2api_phase1().
*
* @param ctx Context pointer
* @return VB2_SUCCESS, or non-zero error code if error.
*/
int vb2api_secdatak_check(const struct vb2_context *ctx);
/**
* Create fresh data in the kernel version secure storage context.
*
* Use this only when initializing the secure storage context on a new machine
* the first time it boots. Do NOT simply use this if vb2api_secdatak_check()
* (or any other API in this library) fails; that could allow the secure data
* to be rolled back to an insecure state.
*
* This may be called before vb2api_phase1().
*
* @param ctx Context pointer
* @return VB2_SUCCESS, or non-zero error code if error.
*/
int vb2api_secdatak_create(struct vb2_context *ctx);
/**
* Report firmware failure to vboot.
*
* This may be called before vb2api_phase1() to indicate errors in the boot
* process prior to the start of vboot.
*
* If this is called after vb2api_phase1(), on return, the calling firmware
* should check for updates to secdata and/or nvdata, then reboot.
*
* @param reason Recovery reason
* @param subcode Recovery subcode
*/
void vb2api_fail(struct vb2_context *ctx, uint8_t reason, uint8_t subcode);
/**
* Firmware selection, phase 1.
*
* If the returned error is VB2_ERROR_API_PHASE1_RECOVERY, the calling firmware
* should jump directly to recovery-mode firmware without rebooting.
*
* For other errors, the calling firmware should check for updates to secdata
* and/or nvdata, then reboot.
*
* @param ctx Vboot context
* @return VB2_SUCCESS, or error code on error.
*/
int vb2api_fw_phase1(struct vb2_context *ctx);
/**
* Firmware selection, phase 2.
*
* On error, the calling firmware should check for updates to secdata and/or
* nvdata, then reboot.
*
* @param ctx Vboot context
* @return VB2_SUCCESS, or error code on error.
*/
int vb2api_fw_phase2(struct vb2_context *ctx);
/**
* Firmware selection, phase 3.
*
* On error, the calling firmware should check for updates to secdata and/or
* nvdata, then reboot.
*
* On success, the calling firmware should lock down secdata before continuing
* with the boot process.
*
* @param ctx Vboot context
* @return VB2_SUCCESS, or error code on error.
*/
int vb2api_fw_phase3(struct vb2_context *ctx);
/**
* Same, but for new-style structs.
*/
int vb21api_fw_phase3(struct vb2_context *ctx);
/**
* Initialize hashing data for the specified tag.
*
* @param ctx Vboot context
* @param tag Tag to start hashing (enum vb2_hash_tag)
* @param size If non-null, expected size of data for tag will be
* stored here on output.
* @return VB2_SUCCESS, or error code on error.
*/
int vb2api_init_hash(struct vb2_context *ctx, uint32_t tag, uint32_t *size);
/**
* Same, but for new-style structs.
*/
int vb21api_init_hash(struct vb2_context *ctx,
const struct vb2_id *id,
uint32_t *size);
/**
* Extend the hash started by vb2api_init_hash() with additional data.
*
* (This is the same for both old and new style structs.)
*
* @param ctx Vboot context
* @param buf Data to hash
* @param size Size of data in bytes
* @return VB2_SUCCESS, or error code on error.
*/
int vb2api_extend_hash(struct vb2_context *ctx,
const void *buf,
uint32_t size);
/**
* Check the hash value started by vb2api_init_hash().
*
* @param ctx Vboot context
* @return VB2_SUCCESS, or error code on error.
*/
int vb2api_check_hash(struct vb2_context *ctx);
/**
* Same, but for new-style structs.
*/
int vb21api_check_hash(struct vb2_context *ctx);
/**
* Check the hash value started by vb2api_init_hash() while retrieving
* calculated digest.
*
* @param ctx Vboot context
* @param digest_out optional pointer to buffer to store digest
* @param digest_out_size optional size of buffer to store digest
* @return VB2_SUCCESS, or error code on error.
*/
int vb2api_check_hash_get_digest(struct vb2_context *ctx, void *digest_out,
uint32_t digest_out_size);
/**
* Get a PCR digest
*
* @param ctx Vboot context
* @param which_digest PCR index of the digest
* @param dest Destination where the digest is copied.
* Recommended size is VB2_PCR_DIGEST_RECOMMENDED_SIZE.
* @param dest_size IN: size of the buffer pointed by dest
* OUT: size of the copied digest
* @return VB2_SUCCESS, or error code on error
*/
int vb2api_get_pcr_digest(struct vb2_context *ctx,
enum vb2_pcr_digest which_digest,
uint8_t *dest,
uint32_t *dest_size);
/**
* Prepare for kernel verification stage.
*
* Must be called before other vb2api kernel functions.
*
* @param ctx Vboot context
* @return VB2_SUCCESS, or error code on error.
*/
int vb2api_kernel_phase1(struct vb2_context *ctx);
/**
* Load the verified boot block (vblock) for a kernel.
*
* This function may be called multiple times, to load and verify the
* vblocks from multiple kernel partitions.
*
* @param ctx Vboot context
* @param stream Kernel stream
* @return VB2_SUCCESS, or error code on error.
*/
int vb2api_load_kernel_vblock(struct vb2_context *ctx);
/**
* Get the size and offset of the kernel data for the most recent vblock.
*
* Valid after a successful call to vb2api_load_kernel_vblock().
*
* @param ctx Vboot context
* @param offset_ptr Destination for offset in bytes of kernel data as
* reported by vblock.
* @param size_ptr Destination for size of kernel data in bytes.
* @return VB2_SUCCESS, or error code on error.
*/
int vb2api_get_kernel_size(struct vb2_context *ctx,
uint32_t *offset_ptr,
uint32_t *size_ptr);
/**
* Verify kernel data using the previously loaded kernel vblock.
*
* Valid after a successful call to vb2api_load_kernel_vblock(). This allows
* the caller to load or map the kernel data, as appropriate, and pass the
* pointer to the kernel data into vboot.
*
* @param ctx Vboot context
* @param buf Pointer to kernel data
* @param size Size of kernel data in bytes
* @return VB2_SUCCESS, or error code on error.
*/
int vb2api_verify_kernel_data(struct vb2_context *ctx,
const void *buf,
uint32_t size);
/**
* Clean up after kernel verification.
*
* Call this after successfully loading a vblock and verifying kernel data,
* or if you've run out of boot devices and/or kernel partitions.
*
* This cleans up intermediate data structures in the vboot context, and
* updates the version in the secure data if necessary.
*/
int vb2api_kernel_phase3(struct vb2_context *ctx);
/*****************************************************************************/
/* APIs provided by the caller to verified boot */
/**
* Clear the TPM owner.
*
* @param ctx Vboot context
* @return VB2_SUCCESS, or error code on error.
*/
int vb2ex_tpm_clear_owner(struct vb2_context *ctx);
/**
* Read a verified boot resource.
*
* @param ctx Vboot context
* @param index Resource index to read
* @param offset Byte offset within resource to start at
* @param buf Destination for data
* @param size Amount of data to read
* @return VB2_SUCCESS, or error code on error.
*/
int vb2ex_read_resource(struct vb2_context *ctx,
enum vb2_resource_index index,
uint32_t offset,
void *buf,
uint32_t size);
/**
* Print debug output
*
* This should work like printf(). If func!=NULL, it will be a string with
* the current function name; that can be used to generate prettier debug
* output. If func==NULL, don't print any extra header/trailer so that this
* can be used to composite a bigger output string from several calls - for
* example, when doing a hex dump.
*
* @param func Function name generating output, or NULL.
* @param fmt Printf format string
*/
void vb2ex_printf(const char *func, const char *fmt, ...);
/**
* Initialize the hardware crypto engine to calculate a block-style digest.
*
* @param hash_alg Hash algorithm to use
* @param data_size Expected total size of data to hash
* @return VB2_SUCCESS, or non-zero error code (HWCRYPTO_UNSUPPORTED not fatal).
*/
int vb2ex_hwcrypto_digest_init(enum vb2_hash_algorithm hash_alg,
uint32_t data_size);
/**
* Extend the hash in the hardware crypto engine with another block of data.
*
* @param buf Next data block to hash
* @param size Length of data block in bytes
* @return VB2_SUCCESS, or non-zero error code.
*/
int vb2ex_hwcrypto_digest_extend(const uint8_t *buf, uint32_t size);
/**
* Finalize the digest in the hardware crypto engine and extract the result.
*
* @param digest Destination buffer for resulting digest
* @param digest_size Length of digest buffer in bytes
* @return VB2_SUCCESS, or non-zero error code.
*/
int vb2ex_hwcrypto_digest_finalize(uint8_t *digest, uint32_t digest_size);
#endif /* VBOOT_2_API_H_ */

View File

@@ -0,0 +1,225 @@
/* 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.
*
* Common functions between firmware and kernel verified boot.
*/
#ifndef VBOOT_REFERENCE_VBOOT_2COMMON_H_
#define VBOOT_REFERENCE_VBOOT_2COMMON_H_
#include "2api.h"
#include "2return_codes.h"
#include "2sha.h"
#include "2struct.h"
#include "2sysincludes.h"
struct vb2_public_key;
/*
* Return the greater of A and B. This is used in macros which calculate the
* required buffer size, so can't be turned into a static inline function.
*/
#ifndef VB2_MAX
#define VB2_MAX(A, B) ((A) > (B) ? (A) : (B))
#endif
/* Return the number of elements in an array */
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
#endif
/* Platform-dependent debug output macros. */
#if defined(VBOOT_DEBUG)
# define VB2_DEBUG(format, args...) vb2ex_printf(__func__, format, ## args)
# define VB2_DEBUG_RAW(format, args...) vb2ex_printf(NULL, format, ## args)
#else
# define VB2_DEBUG(format, args...)
# define VB2_DEBUG_RAW(format, args...)
#endif
/*
* Define test_mockable and for mocking functions when compiled for Chrome OS
* environment (that is, not for firmware).
*/
#ifndef test_mockable
#ifdef CHROMEOS_ENVIRONMENT
#define test_mockable __attribute__((weak))
#else
#define test_mockable
#endif
#endif
/*
* Alignment for work buffer pointers/allocations should be useful for any
* data type. When declaring workbuf buffers on the stack, the caller should
* use explicit alignment to avoid run-time errors. For example:
*
* int foo(void)
* {
* struct vb2_workbuf wb;
* uint8_t buf[NUM] __attribute__ ((aligned (VB2_WORKBUF_ALIGN)));
* wb.buf = buf;
* wb.size = sizeof(buf);
*/
/* We might get away with using __alignof__(void *), but since GCC defines a
* macro for us we'll be safe and use that. */
#define VB2_WORKBUF_ALIGN __BIGGEST_ALIGNMENT__
/**
* Round up a number to a multiple of VB2_WORKBUF_ALIGN
*
* @param v Number to round up
* @return The number, rounded up.
*/
static __inline uint32_t vb2_wb_round_up(uint32_t v)
{
return (v + VB2_WORKBUF_ALIGN - 1) & ~(VB2_WORKBUF_ALIGN - 1);
}
/* Work buffer */
struct vb2_workbuf {
uint8_t *buf;
uint32_t size;
};
/**
* Initialize a work buffer.
*
* @param wb Work buffer to init
* @param buf Pointer to work buffer data
* @param size Size of work buffer data in bytes
*/
void vb2_workbuf_init(struct vb2_workbuf *wb, uint8_t *buf, uint32_t size);
/**
* Allocate space in a work buffer.
*
* Note that the returned buffer will always be aligned to VB2_WORKBUF_ALIGN.
*
* The work buffer acts like a stack, and detailed tracking of allocs and frees
* is not done. The caller must track the size of each allocation and free via
* vb2_workbuf_free() in the reverse order they were allocated.
*
* An acceptable alternate workflow inside a function is to pass in a const
* work buffer, then make a local copy. Allocations done to the local copy
* then don't change the passed-in work buffer, and will effectively be freed
* when the local copy goes out of scope.
*
* @param wb Work buffer
* @param size Requested size in bytes
* @return A pointer to the allocated space, or NULL if error.
*/
void *vb2_workbuf_alloc(struct vb2_workbuf *wb, uint32_t size);
/**
* Reallocate space in a work buffer.
*
* Note that the returned buffer will always be aligned to VB2_WORKBUF_ALIGN.
* The work buffer acts like a stack, so this must only be done to the most
* recently allocated buffer.
*
* @param wb Work buffer
* @param oldsize Old allocation size in bytes
* @param newsize Requested size in bytes
* @return A pointer to the allocated space, or NULL if error.
*/
void *vb2_workbuf_realloc(struct vb2_workbuf *wb,
uint32_t oldsize,
uint32_t newsize);
/**
* Free the preceding allocation.
*
* Note that the work buffer acts like a stack, and detailed tracking of
* allocs and frees is not done. The caller must track the size of each
* allocation and free them in reverse order.
*
* @param wb Work buffer
* @param size Size of data to free
*/
void vb2_workbuf_free(struct vb2_workbuf *wb, uint32_t size);
/* Check if a pointer is aligned on an align-byte boundary */
#define vb2_aligned(ptr, align) (!(((uintptr_t)(ptr)) & ((align) - 1)))
/**
* Safer memcmp() for use in crypto.
*
* Compares the buffers to see if they are equal. Time taken to perform
* the comparison is dependent only on the size, not the relationship of
* the match between the buffers. Note that unlike memcmp(), this only
* indicates inequality, not which buffer is lesser.
*
* @param s1 First buffer
* @param s2 Second buffer
* @param size Number of bytes to compare
* @return 0 if match or size=0, non-zero if at least one byte mismatched.
*/
int vb2_safe_memcmp(const void *s1, const void *s2, size_t size);
/**
* Align a buffer and check its size.
*
* @param **ptr Pointer to pointer to align
* @param *size Points to size of buffer pointed to by *ptr
* @param align Required alignment (must be power of 2)
* @param want_size Required size
* @return VB2_SUCCESS, or non-zero if error.
*/
int vb2_align(uint8_t **ptr,
uint32_t *size,
uint32_t align,
uint32_t want_size);
/**
* Return offset of ptr from base.
*
* @param base Base pointer
* @param ptr Pointer at some offset from base
* @return The offset of ptr from base.
*/
ptrdiff_t vb2_offset_of(const void *base, const void *ptr);
/**
* Return expected signature size for a signature/hash algorithm pair
*
* @param sig_alg Signature algorithm
* @param hash_alg Hash algorithm
* @return The signature size, or zero if error / unsupported algorithm.
*/
uint32_t vb2_sig_size(enum vb2_signature_algorithm sig_alg,
enum vb2_hash_algorithm hash_alg);
/**
* Return a key ID for an unsigned hash algorithm.
*
* @param hash_alg Hash algorithm to return key for
* @return A pointer to the key ID for that hash algorithm with
* sig_alg=VB2_SIG_NONE, or NULL if error.
*/
const struct vb2_id *vb2_hash_id(enum vb2_hash_algorithm hash_alg);
/* Size of work buffer sufficient for vb2_verify_digest() worst case. */
#define VB2_VERIFY_DIGEST_WORKBUF_BYTES VB2_VERIFY_RSA_DIGEST_WORKBUF_BYTES
/* Size of work buffer sufficient for vb2_verify_data() worst case. */
#define VB2_VERIFY_DATA_WORKBUF_BYTES \
(VB2_SHA512_DIGEST_SIZE + \
VB2_MAX(VB2_VERIFY_DIGEST_WORKBUF_BYTES, \
sizeof(struct vb2_digest_context)))
/* Size of work buffer sufficient for vb2_verify_keyblock() worst case. */
#define VB2_KEY_BLOCK_VERIFY_WORKBUF_BYTES VB2_VERIFY_DATA_WORKBUF_BYTES
/* Size of work buffer sufficient for vb2_verify_fw_preamble() worst case. */
#define VB2_VERIFY_FIRMWARE_PREAMBLE_WORKBUF_BYTES VB2_VERIFY_DATA_WORKBUF_BYTES
/*
* Size of work buffer sufficient for vb2_verify_kernel_preamble() worst
* case.
*/
#define VB2_VERIFY_KERNEL_PREAMBLE_WORKBUF_BYTES VB2_VERIFY_DATA_WORKBUF_BYTES
#endif /* VBOOT_REFERENCE_VBOOT_2COMMON_H_ */

View File

@@ -0,0 +1,20 @@
/* 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.
*
* Very simple 8-bit CRC function.
*/
#ifndef VBOOT_REFERENCE_2_CRC8_H_
#define VBOOT_REFERENCE_2_CRC8_H_
/**
* Calculate CRC-8 of the data, using x^8 + x^2 + x + 1 polynomial.
*
* @param data Data to CRC
* @param size Size of data in bytes
* @return CRC-8 of the data.
*/
uint8_t vb2_crc8(const void *data, uint32_t size);
#endif /* VBOOT_REFERENCE_2_CRC8_H_ */

View File

@@ -0,0 +1,75 @@
/* 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.
*
* Crypto constants for verified boot
*/
#ifndef VBOOT_REFERENCE_VBOOT_2CRYPTO_H_
#define VBOOT_REFERENCE_VBOOT_2CRYPTO_H_
#include <stdint.h>
/* Verified boot crypto algorithms */
enum vb2_crypto_algorithm {
VB2_ALG_RSA1024_SHA1 = 0,
VB2_ALG_RSA1024_SHA256 = 1,
VB2_ALG_RSA1024_SHA512 = 2,
VB2_ALG_RSA2048_SHA1 = 3,
VB2_ALG_RSA2048_SHA256 = 4,
VB2_ALG_RSA2048_SHA512 = 5,
VB2_ALG_RSA4096_SHA1 = 6,
VB2_ALG_RSA4096_SHA256 = 7,
VB2_ALG_RSA4096_SHA512 = 8,
VB2_ALG_RSA8192_SHA1 = 9,
VB2_ALG_RSA8192_SHA256 = 10,
VB2_ALG_RSA8192_SHA512 = 11,
VB2_ALG_RSA2048_EXP3_SHA1 = 12,
VB2_ALG_RSA2048_EXP3_SHA256 = 13,
VB2_ALG_RSA2048_EXP3_SHA512 = 14,
VB2_ALG_RSA3072_EXP3_SHA1 = 15,
VB2_ALG_RSA3072_EXP3_SHA256 = 16,
VB2_ALG_RSA3072_EXP3_SHA512 = 17,
/* Number of algorithms */
VB2_ALG_COUNT
};
/* Algorithm types for signatures */
enum vb2_signature_algorithm {
/* Invalid or unsupported signature type */
VB2_SIG_INVALID = 0,
/*
* No signature algorithm. The digest is unsigned. See
* VB2_ID_NONE_* for key IDs to use with this algorithm.
*/
VB2_SIG_NONE = 1,
/* RSA algorithms of the given length in bits (1024-8192) */
VB2_SIG_RSA1024 = 2, /* Warning! This is likely to be deprecated! */
VB2_SIG_RSA2048 = 3,
VB2_SIG_RSA4096 = 4,
VB2_SIG_RSA8192 = 5,
VB2_SIG_RSA2048_EXP3 = 6,
VB2_SIG_RSA3072_EXP3 = 7,
/* Last index. Don't add anything below. */
VB2_SIG_ALG_COUNT,
};
/* Algorithm types for hash digests */
enum vb2_hash_algorithm {
/* Invalid or unsupported digest type */
VB2_HASH_INVALID = 0,
/* SHA-1. Warning: This is likely to be deprecated soon! */
VB2_HASH_SHA1 = 1,
/* SHA-256 and SHA-512 */
VB2_HASH_SHA256 = 2,
VB2_HASH_SHA512 = 3,
/* Last index. Don't add anything below. */
VB2_HASH_ALG_COUNT,
};
#endif /* VBOOT_REFERENCE_VBOOT_2CRYPTO_H_ */

View File

@@ -0,0 +1,40 @@
/* 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.
*
* Firmware hash tags for verified boot
*/
#ifndef VBOOT_REFERENCE_VBOOT_2FW_HASH_TAGS_H_
#define VBOOT_REFERENCE_VBOOT_2FW_HASH_TAGS_H_
#include <stdint.h>
/*
* Tags for types of hashable data.
*
* Note that not every firmware image will contain every tag.
*
* TODO: These are the ones that vboot specifically knows about given the
* current data structures. In the future, I'd really like the vboot preamble
* to contain an arbitrary list of tags and their hashes, so that we can hash
* ram init, main RW body, EC-RW for software sync, etc. all separately.
*/
enum vb2_hash_tag {
/* Invalid hash tag; never present in table */
VB2_HASH_TAG_INVALID = 0,
/* Firmware body */
VB2_HASH_TAG_FW_BODY = 1,
/* Kernel data key */
VB2_HASH_TAG_KERNEL_DATA_KEY = 2,
/*
* Tags over 0x40000000 are reserved for use by the calling firmware,
* which may associate them with arbitrary types of RW firmware data
* that it wants to track.
*/
VB2_HASH_TAG_CALLER_BASE = 0x40000000
};
#endif /* VBOOT_REFERENCE_VBOOT_2FW_HASH_TAGS_H_ */

View File

@@ -0,0 +1,29 @@
/* Copyright 2016 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.
*/
#ifndef VBOOT_REFERENCE_VBOOT_2HMAC_H_
#define VBOOT_REFERENCE_VBOOT_2HMAC_H_
#include <stdint.h>
#include "2crypto.h"
/**
* Compute HMAC
*
* @param alg Hash algorithm ID
* @param key HMAC key
* @param key_size HMAC key size
* @param msg Message to compute HMAC for
* @param msg_size Message size
* @param mac Computed message authentication code
* @param mac_size Size of the buffer pointed by <mac>
* @return
*/
int hmac(enum vb2_hash_algorithm alg,
const void *key, uint32_t key_size,
const void *msg, uint32_t msg_size,
uint8_t *mac, uint32_t mac_size);
#endif

View File

@@ -0,0 +1,28 @@
/* Copyright 2015 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.
*
* Key ID, used to quickly match keys with signatures. There's not a standard
* fingerprint for private keys, so we're using the sha1sum of the public key
* in our keyb format. Pretty much anything would work as long as it's
* resistant to collisions and easy to compare.
*/
#ifndef VBOOT_REFERENCE_VBOOT_2ID_H_
#define VBOOT_REFERENCE_VBOOT_2ID_H_
#include <stdint.h>
#define VB2_ID_NUM_BYTES 20
struct vb2_id {
uint8_t raw[VB2_ID_NUM_BYTES];
} __attribute__((packed));
#define EXPECTED_ID_SIZE VB2_ID_NUM_BYTES
/* IDs to use for "keys" with sig_alg==VB2_SIG_NONE */
#define VB2_ID_NONE_SHA1 {{0x00, 0x01,}}
#define VB2_ID_NONE_SHA256 {{0x02, 0x56,}}
#define VB2_ID_NONE_SHA512 {{0x05, 0x12,}}
#endif /* VBOOT_REFERENCE_VBOOT_2ID_H_ */

View File

@@ -0,0 +1,177 @@
/* 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
*/
#ifndef VBOOT_REFERENCE_VBOOT_2MISC_H_
#define VBOOT_REFERENCE_VBOOT_2MISC_H_
#include "2api.h"
struct vb2_gbb_header;
struct vb2_workbuf;
/**
* Get the shared data pointer from the vboot context
*
* @param ctx Vboot context
* @return The shared data pointer.
*/
static __inline struct vb2_shared_data *vb2_get_sd(struct vb2_context *ctx) {
return (struct vb2_shared_data *)ctx->workbuf;
}
/**
* Validate gbb signature (the magic number)
*
* @param sig Pointer to the signature bytes to validate
* @return VB2_SUCCESS if valid or non-zero if error.
*/
int vb2_validate_gbb_signature(uint8_t *sig);
/**
* Initialize a work buffer from the vboot context.
*
* This sets the work buffer to the unused portion of the context work buffer.
*
* @param ctx Vboot context
* @param wb Work buffer to initialize
*/
void vb2_workbuf_from_ctx(struct vb2_context *ctx, struct vb2_workbuf *wb);
/**
* Set the amount of work buffer used in the vboot context.
*
* This will round up to VB2_WORKBUF_ALIGN, so that the next allocation will
* be aligned as expected.
*
* @param ctx Vboot context
* @param used Number of bytes used
*/
void vb2_set_workbuf_used(struct vb2_context *ctx, uint32_t used);
/**
* Read the GBB header.
*
* @param ctx Vboot context
* @param gbb Destination for header
* @return VB2_SUCCESS, or non-zero if error.
*/
int vb2_read_gbb_header(struct vb2_context *ctx, struct vb2_gbb_header *gbb);
/**
* Handle vboot failure.
*
* If the failure occurred after choosing a firmware slot, and the other
* firmware slot is not known-bad, try the other firmware slot after reboot.
*
* If the failure occurred before choosing a firmware slot, or both slots have
* failed in successive boots, request recovery.
*
* @param reason Recovery reason
* @param subcode Recovery subcode
*/
void vb2_fail(struct vb2_context *ctx, uint8_t reason, uint8_t subcode);
/**
* Set up the verified boot context data, if not already set up.
*
* This uses ctx->workbuf_used=0 as a flag to indicate that the data has not
* yet been set up. Caller must set that before calling any vboot functions;
* see 2api.h.
*
* @param ctx Vboot context to initialize
* @return VB2_SUCCESS, or error code on error.
*/
int vb2_init_context(struct vb2_context *ctx);
/**
* Check for recovery reasons we can determine early in the boot process.
*
* On exit, check ctx->flags for VB2_CONTEXT_RECOVERY_MODE; if present, jump to
* the recovery path instead of continuing with normal boot. This is the only
* direct path to recovery mode. All other errors later in the boot process
* should induce a reboot instead of jumping to recovery, so that recovery mode
* starts from a consistent firmware state.
*
* @param ctx Vboot context
*/
void vb2_check_recovery(struct vb2_context *ctx);
/**
* Parse the GBB header.
*
* @param ctx Vboot context
* @return VB2_SUCCESS, or error code on error.
*/
int vb2_fw_parse_gbb(struct vb2_context *ctx);
/**
* Check developer switch position.
*
* @param ctx Vboot context
* @return VB2_SUCCESS, or error code on error.
*/
int vb2_check_dev_switch(struct vb2_context *ctx);
/**
* Check if we need to clear the TPM owner.
*
* @param ctx Vboot context
* @return VB2_SUCCESS, or error code on error.
*/
int vb2_check_tpm_clear(struct vb2_context *ctx);
/**
* Decide which firmware slot to try this boot.
*
* @param ctx Vboot context
* @return VB2_SUCCESS, or error code on error.
*/
int vb2_select_fw_slot(struct vb2_context *ctx);
/**
* Verify the firmware keyblock using the root key.
*
* After this call, the data key is stored in the work buffer.
*
* @param ctx Vboot context
* @return VB2_SUCCESS, or error code on error.
*/
int vb2_load_fw_keyblock(struct vb2_context *ctx);
int vb21_load_fw_keyblock(struct vb2_context *ctx);
/**
* Verify the firmware preamble using the data subkey from the keyblock.
*
* After this call, the preamble is stored in the work buffer.
*
* @param ctx Vboot context
* @return VB2_SUCCESS, or error code on error.
*/
int vb2_load_fw_preamble(struct vb2_context *ctx);
int vb21_load_fw_preamble(struct vb2_context *ctx);
/**
* Verify the kernel keyblock using the previously-loaded kernel key.
*
* After this call, the data key is stored in the work buffer.
*
* @param ctx Vboot context
* @return VB2_SUCCESS, or error code on error.
*/
int vb2_load_kernel_keyblock(struct vb2_context *ctx);
/**
* Verify the kernel preamble using the data subkey from the keyblock.
*
* After this call, the preamble is stored in the work buffer.
*
* @param ctx Vboot context
* @return VB2_SUCCESS, or error code on error.
*/
int vb2_load_kernel_preamble(struct vb2_context *ctx);
#endif /* VBOOT_REFERENCE_VBOOT_2MISC_H_ */

View File

@@ -0,0 +1,223 @@
/* 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.
*
* Non-volatile storage routines
*/
#ifndef VBOOT_REFERENCE_VBOOT_2NVSTORAGE_H_
#define VBOOT_REFERENCE_VBOOT_2NVSTORAGE_H_
struct vb2_context;
enum vb2_nv_param {
/*
* Parameter values have been reset to defaults (flag for firmware).
* 0=clear; 1=set.
*/
VB2_NV_FIRMWARE_SETTINGS_RESET = 0,
/*
* Parameter values have been reset to defaults (flag for kernel).
* 0=clear; 1=set.
*/
VB2_NV_KERNEL_SETTINGS_RESET,
/* Request debug reset on next S3->S0 transition. 0=clear; 1=set. */
VB2_NV_DEBUG_RESET_MODE,
/* Firmware slot to try next. 0=A, 1=B */
VB2_NV_TRY_NEXT,
/*
* Number of times to try booting RW firmware slot B before slot A.
* Valid range: 0-15.
*
* For VB2, number of times to try booting the slot indicated by
* VB2_NV_TRY_NEXT. On a 1->0 transition of try count, VB2_NV_TRY_NEXT
* will be set to the other slot.
*/
VB2_NV_TRY_COUNT,
/*
* Request recovery mode on next boot; see 2recovery_reason.h for
* currently defined reason codes. 8-bit value.
*/
VB2_NV_RECOVERY_REQUEST,
/*
* Localization index for screen bitmaps displayed by firmware.
* 8-bit value.
*/
VB2_NV_LOCALIZATION_INDEX,
/* Field reserved for kernel/user-mode use; 16-bit value. */
VB2_NV_KERNEL_FIELD,
/* Allow booting from USB in developer mode. 0=no, 1=yes. */
VB2_NV_DEV_BOOT_USB,
/* Allow booting of legacy OSes in developer mode. 0=no, 1=yes. */
VB2_NV_DEV_BOOT_LEGACY,
/* Only boot Google-signed images in developer mode. 0=no, 1=yes. */
VB2_NV_DEV_BOOT_SIGNED_ONLY,
/*
* Allow full fastboot capability in firmware in developer mode.
* 0=no, 1=yes.
*/
VB2_NV_DEV_BOOT_FASTBOOT_FULL_CAP,
/* Set default boot mode (see vb2_dev_default_boot) */
VB2_NV_DEV_DEFAULT_BOOT,
/* Enable USB Device Controller */
VB2_NV_DEV_ENABLE_UDC,
/*
* Set by userspace to request that RO firmware disable dev-mode on the
* next boot. This is likely only possible if the dev-switch is
* virtual.
*/
VB2_NV_DISABLE_DEV_REQUEST,
/*
* Set and cleared by vboot to request that the video Option ROM be
* loaded at boot time, so that BIOS screens can be displayed. 0=no,
* 1=yes.
*/
VB2_NV_OPROM_NEEDED,
/* Request that the firmware clear the TPM owner on the next boot. */
VB2_NV_CLEAR_TPM_OWNER_REQUEST,
/* Flag that TPM owner was cleared on request. */
VB2_NV_CLEAR_TPM_OWNER_DONE,
/* TPM requested a reboot already. */
VB2_NV_TPM_REQUESTED_REBOOT,
/* More details on recovery reason */
VB2_NV_RECOVERY_SUBCODE,
/* Request that NVRAM be backed up at next boot if possible. */
VB2_NV_BACKUP_NVRAM_REQUEST,
/* Firmware slot tried this boot (0=A, 1=B) */
VB2_NV_FW_TRIED,
/* Result of trying that firmware (see vb2_fw_result) */
VB2_NV_FW_RESULT,
/* Firmware slot tried previous boot (0=A, 1=B) */
VB2_NV_FW_PREV_TRIED,
/* Result of trying that firmware (see vb2_fw_result) */
VB2_NV_FW_PREV_RESULT,
/* Request wipeout of the device by the app. */
VB2_NV_REQ_WIPEOUT,
/* Fastboot: Unlock in firmware, 0=disabled, 1=enabled. */
VB2_NV_FASTBOOT_UNLOCK_IN_FW,
/* Boot system when AC detected (0=no, 1=yes). */
VB2_NV_BOOT_ON_AC_DETECT,
/*
* Try to update the EC-RO image after updating the EC-RW image
* (0=no, 1=yes).
*/
VB2_NV_TRY_RO_SYNC,
/* Cut off battery and shutdown on next boot. */
VB2_NV_BATTERY_CUTOFF_REQUEST,
/* Maximum kernel version to roll forward to */
VB2_NV_KERNEL_MAX_ROLLFORWARD,
/*** Fields only available in NV storage V2 ***/
/*
* Maximum firmware version to roll forward to. Returns
* VB2_MAX_ROLLFORWARD_MAX_V1_DEFAULT for V1.
*/
VB2_NV_FW_MAX_ROLLFORWARD,
};
/* Set default boot in developer mode */
enum vb2_dev_default_boot {
/* Default to boot from disk*/
VB2_DEV_DEFAULT_BOOT_DISK = 0,
/* Default to boot from USB */
VB2_DEV_DEFAULT_BOOT_USB = 1,
/* Default to boot legacy OS */
VB2_DEV_DEFAULT_BOOT_LEGACY = 2,
};
/* Firmware result codes for VB2_NV_FW_RESULT and VB2_NV_FW_PREV_RESULT */
enum vb2_fw_result {
/* Unknown */
VB2_FW_RESULT_UNKNOWN = 0,
/* Trying a new slot, but haven't reached success/failure */
VB2_FW_RESULT_TRYING = 1,
/* Successfully booted to the OS */
VB2_FW_RESULT_SUCCESS = 2,
/* Known failure */
VB2_FW_RESULT_FAILURE = 3,
};
/*
* Default value for VB2_NV_FIRMWARE_MAX_ROLLFORWARD on V1. This preserves the
* existing behavior that V1 systems will always roll forward the firmware
* version when possible.
*/
#define VB2_FW_MAX_ROLLFORWARD_V1_DEFAULT 0xfffffffe
/**
* Return the size of the non-volatile storage data in the context.
*
* This may be called before vb2_context_init(), but you must set
* VB2_CONTEXT_NVDATA_V2 if you support V2 record size.
*
* @param ctx Context pointer
* @return Size of the non-volatile storage data in bytes.
*/
int vb2_nv_get_size(const struct vb2_context *ctx);
/**
* Check the CRC of the non-volatile storage context.
*
* Use this if reading from non-volatile storage may be flaky, and you want to
* retry reading it several times.
*
* This may be called before vb2_context_init().
*
* @param ctx Context pointer
* @return VB2_SUCCESS, or non-zero error code if error.
*/
int vb2_nv_check_crc(const struct vb2_context *ctx);
/**
* Initialize the non-volatile storage context and verify its CRC.
*
* This may be called before vb2_context_init(), as long as:
*
* 1) The ctx structure has been cleared to 0.
* 2) Existing non-volatile data, if any, has been stored to ctx->nvdata[].
*
* This is to support using the non-volatile storage functions to request
* recovery if there is an error allocating the workbuf for the context. It
* also allows host-side code to use this library without setting up a bunch of
* extra context.
*
* @param ctx Context pointer
*/
void vb2_nv_init(struct vb2_context *ctx);
/**
* Read a non-volatile value.
*
* Valid only after calling vb2_nv_init().
*
* @param ctx Context pointer
* @param param Parameter to read
* @return The value of the parameter. If you somehow force an invalid
* parameter number, returns 0.
*/
uint32_t vb2_nv_get(struct vb2_context *ctx, enum vb2_nv_param param);
/**
* Write a non-volatile value.
*
* Ignores writes to unknown params. Valid only after calling vb2_nv_init().
* If this changes ctx->nvdata[], it will set VB2_CONTEXT_NVDATA_CHANGED in
* ctx->flags.
*
* @param ctx Context pointer
* @param param Parameter to write
* @param value New value
*/
void vb2_nv_set(struct vb2_context *ctx,
enum vb2_nv_param param,
uint32_t value);
#endif /* VBOOT_REFERENCE_VBOOT_2NVSTORAGE_H_ */

View File

@@ -0,0 +1,110 @@
/* Copyright 2015 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.
*
* Non-volatile storage bitfields
*/
#ifndef VBOOT_REFERENCE_VBOOT_2NVSTORAGE_FIELDS_H_
#define VBOOT_REFERENCE_VBOOT_2NVSTORAGE_FIELDS_H_
/*
* Constants for NV storage. We use this rather than structs and bitfields so
* the data format is consistent across platforms and compilers. Total NV
* storage size is:
*
* Version 1: VB2_NVDATA_SIZE = 16 bytes
* Version 2: VB2_NVDATA_SIZE_V2 = 64 bytes
*
* Unused bits/bytes must be set to 0.
*/
enum vb2_nv_offset {
/*** The following fields are present in all versions ***/
VB2_NV_OFFS_HEADER = 0,
VB2_NV_OFFS_BOOT = 1,
VB2_NV_OFFS_RECOVERY = 2,
VB2_NV_OFFS_LOCALIZATION = 3,
VB2_NV_OFFS_DEV = 4,
VB2_NV_OFFS_TPM = 5,
VB2_NV_OFFS_RECOVERY_SUBCODE = 6,
VB2_NV_OFFS_BOOT2 = 7,
VB2_NV_OFFS_MISC = 8,
VB2_NV_OFFS_KERNEL_MAX_ROLLFORWARD1 = 9, /* bits 0-7 of 32 */
VB2_NV_OFFS_KERNEL_MAX_ROLLFORWARD2 = 10, /* bits 8-15 of 32 */
VB2_NV_OFFS_KERNEL1 = 11, /* bits 0-7 of 16 */
VB2_NV_OFFS_KERNEL2 = 12, /* bits 8-15 of 16 */
VB2_NV_OFFS_KERNEL_MAX_ROLLFORWARD3 = 13, /* bits 16-23 of 32 */
VB2_NV_OFFS_KERNEL_MAX_ROLLFORWARD4 = 14, /* bits 24-31 of 32 */
/*
* CRC_V1 must be last field in V1. This byte can be reused in later
* versions.
*/
VB2_NV_OFFS_CRC_V1 = 15,
/* The following fields are only present in V2+ */
VB2_NV_OFFS_RESERVED_V2 = 15, /* Was CRC in V1 (unused = 0xff) */
VB2_NV_OFFS_FW_MAX_ROLLFORWARD1 = 16, /* bits 0-7 of 32 */
VB2_NV_OFFS_FW_MAX_ROLLFORWARD2 = 17, /* bits 8-15 of 32 */
VB2_NV_OFFS_FW_MAX_ROLLFORWARD3 = 18, /* bits 16-23 of 32 */
VB2_NV_OFFS_FW_MAX_ROLLFORWARD4 = 19, /* bits 24-31 of 32 */
/* CRC must be last field */
VB2_NV_OFFS_CRC_V2 = 63,
};
/* Fields in VB2_NV_OFFS_HEADER (unused = 0x04) */
#define VB2_NV_HEADER_WIPEOUT 0x08
#define VB2_NV_HEADER_KERNEL_SETTINGS_RESET 0x10
#define VB2_NV_HEADER_FW_SETTINGS_RESET 0x20
#define VB2_NV_HEADER_SIGNATURE_MASK 0xc3
/*
* Valid signature values. Note that V1 readers only looked at mask 0xc0, and
* expect it to have value 0x40. Versions != V1 must have the top two bits !=
* 0x40 so old readers will reject the data. Using all 1-bits or all 0-bits is
* also discouraged, because that is indistinguishable from all-erased data on
* some platforms.
*/
/* Version 1 = 16-byte record */
#define VB2_NV_HEADER_SIGNATURE_V1 0x40
/* Version 2 = 64-byte record */
#define VB2_NV_HEADER_SIGNATURE_V2 0x03
/* Fields in VB2_NV_OFFS_BOOT */
#define VB2_NV_BOOT_TRY_COUNT_MASK 0x0f
#define VB2_NV_BOOT_BACKUP_NVRAM 0x10
#define VB2_NV_BOOT_OPROM_NEEDED 0x20
#define VB2_NV_BOOT_DISABLE_DEV 0x40
#define VB2_NV_BOOT_DEBUG_RESET 0x80
/* Fields in VB2_NV_OFFS_BOOT2 (unused = 0x80) */
#define VB2_NV_BOOT2_RESULT_MASK 0x03
#define VB2_NV_BOOT2_TRIED 0x04
#define VB2_NV_BOOT2_TRY_NEXT 0x08
#define VB2_NV_BOOT2_PREV_RESULT_MASK 0x30
#define VB2_NV_BOOT2_PREV_RESULT_SHIFT 4 /* Number of bits to shift result */
#define VB2_NV_BOOT2_PREV_TRIED 0x40
/* Fields in VB2_NV_OFFS_DEV (unused = 0x80) */
#define VB2_NV_DEV_FLAG_USB 0x01
#define VB2_NV_DEV_FLAG_SIGNED_ONLY 0x02
#define VB2_NV_DEV_FLAG_LEGACY 0x04
#define VB2_NV_DEV_FLAG_FASTBOOT_FULL_CAP 0x08
#define VB2_NV_DEV_FLAG_DEFAULT_BOOT 0x30
#define VB2_NV_DEV_DEFAULT_BOOT_SHIFT 4 /* Number of bits to shift */
#define VB2_NV_DEV_FLAG_UDC 0x40
/* Fields in VB2_NV_OFFS_TPM (unused = 0xf8) */
#define VB2_NV_TPM_CLEAR_OWNER_REQUEST 0x01
#define VB2_NV_TPM_CLEAR_OWNER_DONE 0x02
#define VB2_NV_TPM_REBOOTED 0x04
/* Fields in VB2_NV_OFFS_MISC (unused = 0xf0) */
#define VB2_NV_MISC_UNLOCK_FASTBOOT 0x01
#define VB2_NV_MISC_BOOT_ON_AC_DETECT 0x02
#define VB2_NV_MISC_TRY_RO_SYNC 0x04
#define VB2_NV_MISC_BATTERY_CUTOFF 0x08
#endif /* VBOOT_REFERENCE_VBOOT_2NVSTORAGE_FIELDS_H_ */

View File

@@ -0,0 +1,247 @@
/* 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.
*
* Recovery reasons
*/
#ifndef VBOOT_REFERENCE_VBOOT_2RECOVERY_REASONS_H_
#define VBOOT_REFERENCE_VBOOT_2RECOVERY_REASONS_H_
/* Recovery reason codes */
enum vb2_nv_recovery {
/* Recovery not requested. */
VB2_RECOVERY_NOT_REQUESTED = 0x00,
/*
* Recovery requested from legacy utility. (Prior to the NV storage
* spec, recovery mode was a single bitfield; this value is reserved so
* that scripts which wrote 1 to the recovery field are distinguishable
* from scripts whch use the recovery reasons listed here.
*/
VB2_RECOVERY_LEGACY = 0x01,
/* User manually requested recovery via recovery button */
VB2_RECOVERY_RO_MANUAL = 0x02,
/*
* RW firmware failed signature check (neither RW firmware slot was
* valid)
*/
VB2_RECOVERY_RO_INVALID_RW = 0x03,
/* S3 resume failed */
VB2_RECOVERY_RO_S3_RESUME = 0x04,
/* TPM error in read-only firmware (deprecated) */
VB2_RECOVERY_DEP_RO_TPM_ERROR = 0x05,
/* Shared data error in read-only firmware */
VB2_RECOVERY_RO_SHARED_DATA = 0x06,
/* Test error from S3Resume() */
VB2_RECOVERY_RO_TEST_S3 = 0x07,
/* Test error from LoadFirmwareSetup() (deprecated) */
VB2_RECOVERY_RO_TEST_LFS = 0x08,
/* Test error from LoadFirmware() (deprecated) */
VB2_RECOVERY_RO_TEST_LF = 0x09,
/*
* RW firmware failed signature check (neither RW firmware slot was
* valid). Recovery reason is VB2_RECOVERY_RO_INVALID_RW_CHECK_MIN +
* the check value for the slot which came closest to validating; see
* VBSD_LF_CHECK_* in vboot_struct.h.
*/
VB2_RECOVERY_RO_INVALID_RW_CHECK_MIN = 0x10,
/* Latest tried RW firmware keyblock verification failed */
VB2_RECOVERY_FW_KEYBLOCK = 0x13,
/* Latest tried RW firmware key version too old */
VB2_RECOVERY_FW_KEY_ROLLBACK = 0x14,
/* Latest tried RW firmware preamble verification failed */
VB2_RECOVERY_FW_PREAMBLE = 0x16,
/* Latest tried RW firmware version too old */
VB2_RECOVERY_FW_ROLLBACK = 0x17,
/* Latest tried RW firmware body verification failed */
VB2_RECOVERY_FW_BODY = 0x1b,
/* Highest reason for failed RW firmware signature check */
VB2_RECOVERY_RO_INVALID_RW_CHECK_MAX = 0x1f,
/*
* Firmware boot failure outside of verified boot (RAM init, missing
* SSD, etc.).
*/
VB2_RECOVERY_RO_FIRMWARE = 0x20,
/*
* Recovery mode TPM initialization requires a system reboot. The
* system was already in recovery mode for some other reason when this
* happened.
*/
VB2_RECOVERY_RO_TPM_REBOOT = 0x21,
/* EC software sync - other error */
VB2_RECOVERY_EC_SOFTWARE_SYNC = 0x22,
/* EC software sync - unable to determine active EC image */
VB2_RECOVERY_EC_UNKNOWN_IMAGE = 0x23,
/* EC software sync - error obtaining EC image hash (deprecated) */
VB2_RECOVERY_DEP_EC_HASH = 0x24,
/* EC software sync - error obtaining expected EC image */
VB2_RECOVERY_EC_EXPECTED_IMAGE = 0x25,
/* EC software sync - error updating EC */
VB2_RECOVERY_EC_UPDATE = 0x26,
/* EC software sync - unable to jump to EC-RW */
VB2_RECOVERY_EC_JUMP_RW = 0x27,
/* EC software sync - unable to protect / unprotect EC-RW */
VB2_RECOVERY_EC_PROTECT = 0x28,
/* EC software sync - error obtaining expected EC hash */
VB2_RECOVERY_EC_EXPECTED_HASH = 0x29,
/* EC software sync - expected EC image doesn't match hash */
VB2_RECOVERY_EC_HASH_MISMATCH = 0x2a,
/* New error codes from VB2 */
/* TODO: may need to add strings for these in the original fwlib */
/* Secure data inititalization error */
VB2_RECOVERY_SECDATA_INIT = 0x2b,
/* GBB header is bad */
VB2_RECOVERY_GBB_HEADER = 0x2c,
/* Unable to clear TPM owner */
VB2_RECOVERY_TPM_CLEAR_OWNER = 0x2d,
/* Error determining/updating virtual dev switch */
VB2_RECOVERY_DEV_SWITCH = 0x2e,
/* Error determining firmware slot */
VB2_RECOVERY_FW_SLOT = 0x2f,
/* Error updating AUX firmware */
VB2_RECOVERY_AUX_FW_UPDATE = 0x30,
/* Unspecified/unknown error in read-only firmware */
VB2_RECOVERY_RO_UNSPECIFIED = 0x3f,
/*
* User manually requested recovery by pressing a key at developer
* warning screen
*/
VB2_RECOVERY_RW_DEV_SCREEN = 0x41,
/* No OS kernel detected */
VB2_RECOVERY_RW_NO_OS = 0x42,
/* OS kernel failed signature check */
VB2_RECOVERY_RW_INVALID_OS = 0x43,
/* TPM error in rewritable firmware (deprecated) */
VB2_RECOVERY_DEP_RW_TPM_ERROR = 0x44,
/* RW firmware in dev mode, but dev switch is off */
VB2_RECOVERY_RW_DEV_MISMATCH = 0x45,
/* Shared data error in rewritable firmware */
VB2_RECOVERY_RW_SHARED_DATA = 0x46,
/* Test error from LoadKernel() */
VB2_RECOVERY_RW_TEST_LK = 0x47,
/* No bootable disk found (deprecated)*/
VB2_RECOVERY_DEP_RW_NO_DISK = 0x48,
/* Rebooting did not correct TPM_E_FAIL or TPM_E_FAILEDSELFTEST */
VB2_RECOVERY_TPM_E_FAIL = 0x49,
/* TPM setup error in read-only firmware */
VB2_RECOVERY_RO_TPM_S_ERROR = 0x50,
/* TPM write error in read-only firmware */
VB2_RECOVERY_RO_TPM_W_ERROR = 0x51,
/* TPM lock error in read-only firmware */
VB2_RECOVERY_RO_TPM_L_ERROR = 0x52,
/* TPM update error in read-only firmware */
VB2_RECOVERY_RO_TPM_U_ERROR = 0x53,
/* TPM read error in rewritable firmware */
VB2_RECOVERY_RW_TPM_R_ERROR = 0x54,
/* TPM write error in rewritable firmware */
VB2_RECOVERY_RW_TPM_W_ERROR = 0x55,
/* TPM lock error in rewritable firmware */
VB2_RECOVERY_RW_TPM_L_ERROR = 0x56,
/* EC software sync unable to get EC image hash */
VB2_RECOVERY_EC_HASH_FAILED = 0x57,
/* EC software sync invalid image hash size */
VB2_RECOVERY_EC_HASH_SIZE = 0x58,
/* Unspecified error while trying to load kernel */
VB2_RECOVERY_LK_UNSPECIFIED = 0x59,
/* No bootable storage device in system */
VB2_RECOVERY_RW_NO_DISK = 0x5a,
/* No bootable kernel found on disk */
VB2_RECOVERY_RW_NO_KERNEL = 0x5b,
/* BCB related error in RW firmware */
VB2_RECOVERY_RW_BCB_ERROR = 0x5c,
/* New error codes from VB2 */
/* TODO: may need to add strings for these in the original fwlib */
/* Secure data inititalization error */
VB2_RECOVERY_SECDATAK_INIT = 0x5d,
/* Fastboot mode requested in firmware */
VB2_RECOVERY_FW_FASTBOOT = 0x5e,
/* Recovery hash space lock error in RO firmware */
VB2_RECOVERY_RO_TPM_REC_HASH_L_ERROR = 0x5f,
/* Unspecified/unknown error in rewritable firmware */
VB2_RECOVERY_RW_UNSPECIFIED = 0x7f,
/* DM-verity error */
VB2_RECOVERY_KE_DM_VERITY = 0x81,
/* Unspecified/unknown error in kernel */
VB2_RECOVERY_KE_UNSPECIFIED = 0xbf,
/* Recovery mode test from user-mode */
VB2_RECOVERY_US_TEST = 0xc1,
/* Recovery requested by user-mode via BCB */
VB2_RECOVERY_BCB_USER_MODE = 0xc2,
/* Fastboot mode requested by user-mode */
VB2_RECOVERY_US_FASTBOOT = 0xc3,
/* User requested recovery for training memory and rebooting. */
VB2_RECOVERY_TRAIN_AND_REBOOT = 0xc4,
/* Unspecified/unknown error in user-mode */
VB2_RECOVERY_US_UNSPECIFIED = 0xff,
};
#endif /* VBOOT_REFERENCE_VBOOT_2RECOVERY_REASONS_H_ */

View File

@@ -0,0 +1,827 @@
/* 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.
*/
#ifndef VBOOT_2_RETURN_CODES_H_
#define VBOOT_2_RETURN_CODES_H_
/*
* Return codes from verified boot functions.
*
* Note that other values may be passed through from vb2ex_*() calls; see
* the comment for VB2_ERROR_EX below.
*/
enum vb2_return_code {
/* Success - no error */
VB2_SUCCESS = 0,
/*
* All vboot2 error codes start at a large offset from zero, to reduce
* the risk of overlap with other error codes (TPM, etc.).
*/
VB2_ERROR_BASE = 0x10000000,
/* Unknown / unspecified error */
VB2_ERROR_UNKNOWN = VB2_ERROR_BASE + 1,
/* Mock error for testing */
VB2_ERROR_MOCK,
/**********************************************************************
* SHA errors
*/
VB2_ERROR_SHA = VB2_ERROR_BASE + 0x010000,
/* Bad algorithm in vb2_digest_init() */
VB2_ERROR_SHA_INIT_ALGORITHM,
/* Bad algorithm in vb2_digest_extend() */
VB2_ERROR_SHA_EXTEND_ALGORITHM,
/* Bad algorithm in vb2_digest_finalize() */
VB2_ERROR_SHA_FINALIZE_ALGORITHM,
/* Digest size buffer too small in vb2_digest_finalize() */
VB2_ERROR_SHA_FINALIZE_DIGEST_SIZE,
/**********************************************************************
* RSA errors
*/
VB2_ERROR_RSA = VB2_ERROR_BASE + 0x020000,
/* Padding mismatch in vb2_check_padding() */
VB2_ERROR_RSA_PADDING,
/* Bad algorithm in vb2_check_padding() */
VB2_ERROR_RSA_PADDING_ALGORITHM,
/* Null param passed to vb2_verify_digest() */
VB2_ERROR_RSA_VERIFY_PARAM,
/* Bad algorithm in vb2_verify_digest() */
VB2_ERROR_RSA_VERIFY_ALGORITHM,
/* Bad signature length in vb2_verify_digest() */
VB2_ERROR_RSA_VERIFY_SIG_LEN,
/* Work buffer too small in vb2_verify_digest() */
VB2_ERROR_RSA_VERIFY_WORKBUF,
/* Digest mismatch in vb2_verify_digest() */
VB2_ERROR_RSA_VERIFY_DIGEST,
/* Bad size calculation in vb2_check_padding() */
VB2_ERROR_RSA_PADDING_SIZE,
/**********************************************************************
* NV storage errors
*/
VB2_ERROR_NV = VB2_ERROR_BASE + 0x030000,
/* Bad header in vb2_nv_check_crc() */
VB2_ERROR_NV_HEADER,
/* Bad CRC in vb2_nv_check_crc() */
VB2_ERROR_NV_CRC,
/**********************************************************************
* Secure data storage errors
*/
VB2_ERROR_SECDATA = VB2_ERROR_BASE + 0x040000,
/* Bad CRC in vb2_secdata_check_crc() */
VB2_ERROR_SECDATA_CRC,
/* Secdata is all zeroes (uninitialized) in vb2_secdata_check_crc() */
VB2_ERROR_SECDATA_ZERO,
/* Invalid param in vb2_secdata_get() */
VB2_ERROR_SECDATA_GET_PARAM,
/* Invalid param in vb2_secdata_set() */
VB2_ERROR_SECDATA_SET_PARAM,
/* Invalid flags passed to vb2_secdata_set() */
VB2_ERROR_SECDATA_SET_FLAGS,
/* Called vb2_secdata_get() with uninitialized secdata */
VB2_ERROR_SECDATA_GET_UNINITIALIZED,
/* Called vb2_secdata_set() with uninitialized secdata */
VB2_ERROR_SECDATA_SET_UNINITIALIZED,
/* Bad CRC in vb2_secdatak_check_crc() */
VB2_ERROR_SECDATAK_CRC,
/* Bad struct version in vb2_secdatak_init() */
VB2_ERROR_SECDATAK_VERSION,
/* Bad uid in vb2_secdatak_init() */
VB2_ERROR_SECDATAK_UID,
/* Invalid param in vb2_secdatak_get() */
VB2_ERROR_SECDATAK_GET_PARAM,
/* Invalid param in vb2_secdatak_set() */
VB2_ERROR_SECDATAK_SET_PARAM,
/* Invalid flags passed to vb2_secdatak_set() */
VB2_ERROR_SECDATAK_SET_FLAGS,
/* Called vb2_secdatak_get() with uninitialized secdatak */
VB2_ERROR_SECDATAK_GET_UNINITIALIZED,
/* Called vb2_secdatak_set() with uninitialized secdatak */
VB2_ERROR_SECDATAK_SET_UNINITIALIZED,
/**********************************************************************
* Common code errors
*/
VB2_ERROR_COMMON = VB2_ERROR_BASE + 0x050000,
/* Buffer is smaller than alignment offset in vb2_align() */
VB2_ERROR_ALIGN_BIGGER_THAN_SIZE,
/* Buffer is smaller than request in vb2_align() */
VB2_ERROR_ALIGN_SIZE,
/* Parent wraps around in vb2_verify_member_inside() */
VB2_ERROR_INSIDE_PARENT_WRAPS,
/* Member wraps around in vb2_verify_member_inside() */
VB2_ERROR_INSIDE_MEMBER_WRAPS,
/* Member outside parent in vb2_verify_member_inside() */
VB2_ERROR_INSIDE_MEMBER_OUTSIDE,
/* Member data wraps around in vb2_verify_member_inside() */
VB2_ERROR_INSIDE_DATA_WRAPS,
/* Member data outside parent in vb2_verify_member_inside() */
VB2_ERROR_INSIDE_DATA_OUTSIDE,
/* Unsupported signature algorithm in vb2_unpack_key_buffer() */
VB2_ERROR_UNPACK_KEY_SIG_ALGORITHM, /* 0x150008 */
/* Bad key size in vb2_unpack_key_buffer() */
VB2_ERROR_UNPACK_KEY_SIZE,
/* Bad key alignment in vb2_unpack_key_buffer() */
VB2_ERROR_UNPACK_KEY_ALIGN,
/* Bad key array size in vb2_unpack_key_buffer() */
VB2_ERROR_UNPACK_KEY_ARRAY_SIZE,
/* Bad algorithm in vb2_verify_data() */
VB2_ERROR_VDATA_ALGORITHM,
/* Incorrect signature size for algorithm in vb2_verify_data() */
VB2_ERROR_VDATA_SIG_SIZE,
/* Data smaller than length of signed data in vb2_verify_data() */
VB2_ERROR_VDATA_NOT_ENOUGH_DATA,
/* Not enough work buffer for digest in vb2_verify_data() */
VB2_ERROR_VDATA_WORKBUF_DIGEST,
/* Not enough work buffer for hash temp data in vb2_verify_data() */
VB2_ERROR_VDATA_WORKBUF_HASHING, /* 0x150010 */
/*
* Bad digest size in vb2_verify_data() - probably because algorithm
* is bad.
*/
VB2_ERROR_VDATA_DIGEST_SIZE,
/* Unsupported hash algorithm in vb2_unpack_key_buffer() */
VB2_ERROR_UNPACK_KEY_HASH_ALGORITHM,
/* Member data overlaps member header */
VB2_ERROR_INSIDE_DATA_OVERLAP,
/* Unsupported packed key struct version */
VB2_ERROR_UNPACK_KEY_STRUCT_VERSION,
/*
* Buffer too small for total, fixed size, or description reported in
* common header, or member data checked via
* vb21_verify_common_member().
*/
VB2_ERROR_COMMON_TOTAL_SIZE,
VB2_ERROR_COMMON_FIXED_SIZE,
VB2_ERROR_COMMON_DESC_SIZE,
VB2_ERROR_COMMON_MEMBER_SIZE, /* 0x150018 */
/*
* Total, fixed, description, or member offset/size not a multiple of
* 32 bits.
*/
VB2_ERROR_COMMON_TOTAL_UNALIGNED,
VB2_ERROR_COMMON_FIXED_UNALIGNED,
VB2_ERROR_COMMON_DESC_UNALIGNED,
VB2_ERROR_COMMON_MEMBER_UNALIGNED,
/* Common struct description or member data wraps address space */
VB2_ERROR_COMMON_DESC_WRAPS,
VB2_ERROR_COMMON_MEMBER_WRAPS,
/* Common struct description is not null-terminated */
VB2_ERROR_COMMON_DESC_TERMINATOR,
/* Member data overlaps previous data */
VB2_ERROR_COMMON_MEMBER_OVERLAP, /* 0x150020 */
/* Signature bad magic number */
VB2_ERROR_SIG_MAGIC,
/* Signature incompatible version */
VB2_ERROR_SIG_VERSION,
/* Signature header doesn't fit */
VB2_ERROR_SIG_HEADER_SIZE,
/* Signature unsupported algorithm */
VB2_ERROR_SIG_ALGORITHM,
/* Signature bad size for algorithm */
VB2_ERROR_SIG_SIZE,
/* Wrong amount of data signed */
VB2_ERROR_VDATA_SIZE,
/* Digest mismatch */
VB2_ERROR_VDATA_VERIFY_DIGEST,
/* Key algorithm doesn't match signature algorithm */
VB2_ERROR_VDATA_ALGORITHM_MISMATCH,
/* Bad magic number in vb2_unpack_key_buffer() */
VB2_ERROR_UNPACK_KEY_MAGIC,
/* Null public key buffer passed to vb2_unpack_key_buffer() */
VB2_ERROR_UNPACK_KEY_BUFFER,
/**********************************************************************
* Keyblock verification errors (all in vb2_verify_keyblock())
*/
VB2_ERROR_KEYBLOCK = VB2_ERROR_BASE + 0x060000,
/* Data buffer too small for header */
VB2_ERROR_KEYBLOCK_TOO_SMALL_FOR_HEADER,
/* Magic number not present */
VB2_ERROR_KEYBLOCK_MAGIC,
/* Header version incompatible */
VB2_ERROR_KEYBLOCK_HEADER_VERSION,
/* Data buffer too small for keyblock */
VB2_ERROR_KEYBLOCK_SIZE,
/* Signature data offset outside keyblock */
VB2_ERROR_KEYBLOCK_SIG_OUTSIDE,
/* Signature signed more data than size of keyblock */
VB2_ERROR_KEYBLOCK_SIGNED_TOO_MUCH,
/* Signature signed less data than size of keyblock header */
VB2_ERROR_KEYBLOCK_SIGNED_TOO_LITTLE,
/* Signature invalid */
VB2_ERROR_KEYBLOCK_SIG_INVALID,
/* Data key outside keyblock */
VB2_ERROR_KEYBLOCK_DATA_KEY_OUTSIDE,
/* Data key outside signed part of keyblock */
VB2_ERROR_KEYBLOCK_DATA_KEY_UNSIGNED,
/* Signature signed wrong amount of data */
VB2_ERROR_KEYBLOCK_SIGNED_SIZE,
/* No signature matching key ID */
VB2_ERROR_KEYBLOCK_SIG_ID,
/**********************************************************************
* Preamble verification errors (all in vb2_verify_preamble())
*/
VB2_ERROR_PREAMBLE = VB2_ERROR_BASE + 0x070000,
/* Preamble data too small to contain header */
VB2_ERROR_PREAMBLE_TOO_SMALL_FOR_HEADER,
/* Header version incompatible */
VB2_ERROR_PREAMBLE_HEADER_VERSION,
/* Header version too old */
VB2_ERROR_PREAMBLE_HEADER_OLD,
/* Data buffer too small for preamble */
VB2_ERROR_PREAMBLE_SIZE,
/* Signature data offset outside preamble */
VB2_ERROR_PREAMBLE_SIG_OUTSIDE,
/* Signature signed more data than size of preamble */
VB2_ERROR_PREAMBLE_SIGNED_TOO_MUCH,
/* Signature signed less data than size of preamble header */
VB2_ERROR_PREAMBLE_SIGNED_TOO_LITTLE,
/* Signature invalid */
VB2_ERROR_PREAMBLE_SIG_INVALID,
/* Body signature outside preamble */
VB2_ERROR_PREAMBLE_BODY_SIG_OUTSIDE,
/* Kernel subkey outside preamble */
VB2_ERROR_PREAMBLE_KERNEL_SUBKEY_OUTSIDE,
/* Bad magic number */
VB2_ERROR_PREAMBLE_MAGIC,
/* Hash is signed */
VB2_ERROR_PREAMBLE_HASH_SIGNED,
/* Bootloader outside signed portion of body */
VB2_ERROR_PREAMBLE_BOOTLOADER_OUTSIDE,
/* Vmlinuz header outside signed portion of body */
VB2_ERROR_PREAMBLE_VMLINUZ_HEADER_OUTSIDE,
/**********************************************************************
* Misc higher-level code errors
*/
VB2_ERROR_MISC = VB2_ERROR_BASE + 0x080000,
/* Work buffer too small in vb2_init_context() */
VB2_ERROR_INITCTX_WORKBUF_SMALL,
/* Work buffer unaligned in vb2_init_context() */
VB2_ERROR_INITCTX_WORKBUF_ALIGN,
/* Work buffer too small in vb2_fw_parse_gbb() */
VB2_ERROR_GBB_WORKBUF,
/* Bad magic number in vb2_read_gbb_header() */
VB2_ERROR_GBB_MAGIC,
/* Incompatible version in vb2_read_gbb_header() */
VB2_ERROR_GBB_VERSION,
/* Old version in vb2_read_gbb_header() */
VB2_ERROR_GBB_TOO_OLD,
/* Header size too small in vb2_read_gbb_header() */
VB2_ERROR_GBB_HEADER_SIZE,
/* Work buffer too small for root key in vb2_load_fw_keyblock() */
VB2_ERROR_FW_KEYBLOCK_WORKBUF_ROOT_KEY,
/* Work buffer too small for header in vb2_load_fw_keyblock() */
VB2_ERROR_FW_KEYBLOCK_WORKBUF_HEADER,
/* Work buffer too small for keyblock in vb2_load_fw_keyblock() */
VB2_ERROR_FW_KEYBLOCK_WORKBUF,
/* Keyblock version out of range in vb2_load_fw_keyblock() */
VB2_ERROR_FW_KEYBLOCK_VERSION_RANGE,
/* Keyblock version rollback in vb2_load_fw_keyblock() */
VB2_ERROR_FW_KEYBLOCK_VERSION_ROLLBACK,
/* Missing firmware data key in vb2_load_fw_preamble() */
VB2_ERROR_FW_PREAMBLE2_DATA_KEY,
/* Work buffer too small for header in vb2_load_fw_preamble() */
VB2_ERROR_FW_PREAMBLE2_WORKBUF_HEADER,
/* Work buffer too small for preamble in vb2_load_fw_preamble() */
VB2_ERROR_FW_PREAMBLE2_WORKBUF,
/* Firmware version out of range in vb2_load_fw_preamble() */
VB2_ERROR_FW_PREAMBLE_VERSION_RANGE,
/* Firmware version rollback in vb2_load_fw_preamble() */
VB2_ERROR_FW_PREAMBLE_VERSION_ROLLBACK,
/* Not enough space in work buffer for resource object */
VB2_ERROR_READ_RESOURCE_OBJECT_BUF,
/* Work buffer too small for header in vb2_load_kernel_keyblock() */
VB2_ERROR_KERNEL_KEYBLOCK_WORKBUF_HEADER,
/* Work buffer too small for keyblock in vb2_load_kernel_keyblock() */
VB2_ERROR_KERNEL_KEYBLOCK_WORKBUF,
/* Keyblock version out of range in vb2_load_kernel_keyblock() */
VB2_ERROR_KERNEL_KEYBLOCK_VERSION_RANGE,
/* Keyblock version rollback in vb2_load_kernel_keyblock() */
VB2_ERROR_KERNEL_KEYBLOCK_VERSION_ROLLBACK,
/*
* Keyblock flags don't match current mode in
* vb2_load_kernel_keyblock().
*/
VB2_ERROR_KERNEL_KEYBLOCK_DEV_FLAG,
VB2_ERROR_KERNEL_KEYBLOCK_REC_FLAG,
/* Missing firmware data key in vb2_load_kernel_preamble() */
VB2_ERROR_KERNEL_PREAMBLE2_DATA_KEY,
/* Work buffer too small for header in vb2_load_kernel_preamble() */
VB2_ERROR_KERNEL_PREAMBLE2_WORKBUF_HEADER,
/* Work buffer too small for preamble in vb2_load_kernel_preamble() */
VB2_ERROR_KERNEL_PREAMBLE2_WORKBUF,
/* Kernel version out of range in vb2_load_kernel_preamble() */
VB2_ERROR_KERNEL_PREAMBLE_VERSION_RANGE,
/* Kernel version rollback in vb2_load_kernel_preamble() */
VB2_ERROR_KERNEL_PREAMBLE_VERSION_ROLLBACK,
/* Kernel preamble not loaded before calling vb2api_get_kernel_size() */
VB2_ERROR_API_GET_KERNEL_SIZE_PREAMBLE,
/* Unable to unpack kernel subkey in vb2_verify_vblock() */
VB2_ERROR_VBLOCK_KERNEL_SUBKEY,
/*
* Got a self-signed kernel in vb2_verify_vblock(), but need an
* officially signed one.
*/
VB2_ERROR_VBLOCK_SELF_SIGNED,
/* Invalid keyblock hash in vb2_verify_vblock() */
VB2_ERROR_VBLOCK_KEYBLOCK_HASH,
/* Invalid keyblock in vb2_verify_vblock() */
VB2_ERROR_VBLOCK_KEYBLOCK,
/* Wrong developer key hash in vb2_verify_vblock() */
VB2_ERROR_VBLOCK_DEV_KEY_HASH,
/* Work buffer too small in vb2_load_partition() */
VB2_ERROR_LOAD_PARTITION_WORKBUF,
/* Unable to read vblock in vb2_load_partition() */
VB2_ERROR_LOAD_PARTITION_READ_VBLOCK,
/* Unable to verify vblock in vb2_load_partition() */
VB2_ERROR_LOAD_PARTITION_VERIFY_VBLOCK,
/* Kernel body offset too large in vb2_load_partition() */
VB2_ERROR_LOAD_PARTITION_BODY_OFFSET,
/* Kernel body too big in vb2_load_partition() */
VB2_ERROR_LOAD_PARTITION_BODY_SIZE,
/* Unable to read kernel body in vb2_load_partition() */
VB2_ERROR_LOAD_PARTITION_READ_BODY,
/* Unable to unpack data key in vb2_load_partition() */
VB2_ERROR_LOAD_PARTITION_DATA_KEY,
/* Unable to verify body in vb2_load_partition() */
VB2_ERROR_LOAD_PARTITION_VERIFY_BODY,
/* Unable to get EC image hash in ec_sync_phase1() */
VB2_ERROR_EC_HASH_IMAGE,
/* Unable to get expected EC image hash in ec_sync_phase1() */
VB2_ERROR_EC_HASH_EXPECTED,
/* Expected and image hashes are different size in ec_sync_phase1() */
VB2_ERROR_EC_HASH_SIZE,
/**********************************************************************
* API-level errors
*/
VB2_ERROR_API = VB2_ERROR_BASE + 0x090000,
/* Bad tag in vb2api_init_hash() */
VB2_ERROR_API_INIT_HASH_TAG,
/* Preamble not present in vb2api_init_hash() */
VB2_ERROR_API_INIT_HASH_PREAMBLE,
/* Work buffer too small in vb2api_init_hash() */
VB2_ERROR_API_INIT_HASH_WORKBUF,
/* Missing firmware data key in vb2api_init_hash() */
VB2_ERROR_API_INIT_HASH_DATA_KEY,
/* Uninitialized work area in vb2api_extend_hash() */
VB2_ERROR_API_EXTEND_HASH_WORKBUF,
/* Too much data hashed in vb2api_extend_hash() */
VB2_ERROR_API_EXTEND_HASH_SIZE,
/* Preamble not present in vb2api_check_hash() */
VB2_ERROR_API_CHECK_HASH_PREAMBLE,
/* Uninitialized work area in vb2api_check_hash() */
VB2_ERROR_API_CHECK_HASH_WORKBUF,
/* Wrong amount of data hashed in vb2api_check_hash() */
VB2_ERROR_API_CHECK_HASH_SIZE,
/* Work buffer too small in vb2api_check_hash() */
VB2_ERROR_API_CHECK_HASH_WORKBUF_DIGEST,
/* Bad tag in vb2api_check_hash() */
VB2_ERROR_API_CHECK_HASH_TAG,
/* Missing firmware data key in vb2api_check_hash() */
VB2_ERROR_API_CHECK_HASH_DATA_KEY,
/* Signature size mismatch in vb2api_check_hash() */
VB2_ERROR_API_CHECK_HASH_SIG_SIZE,
/* Phase one needs recovery mode */
VB2_ERROR_API_PHASE1_RECOVERY,
/* Bad tag in vb2api_check_hash() */
VB2_ERROR_API_INIT_HASH_ID,
/* Signature mismatch in vb2api_check_hash() */
VB2_ERROR_API_CHECK_HASH_SIG,
/* Invalid enum vb2_pcr_digest requested to vb2api_get_pcr_digest */
VB2_ERROR_API_PCR_DIGEST,
/* Buffer size for the digest is too small for vb2api_get_pcr_digest */
VB2_ERROR_API_PCR_DIGEST_BUF,
/* Work buffer too small for recovery key in vb2api_kernel_phase1() */
VB2_ERROR_API_KPHASE1_WORKBUF_REC_KEY,
/* Firmware preamble not present for vb2api_kernel_phase1() */
VB2_ERROR_API_KPHASE1_PREAMBLE,
/* Wrong amount of kernel data in vb2api_verify_kernel_data() */
VB2_ERROR_API_VERIFY_KDATA_SIZE,
/* Kernel preamble not present for vb2api_verify_kernel_data() */
VB2_ERROR_API_VERIFY_KDATA_PREAMBLE,
/* Insufficient workbuf for hashing in vb2api_verify_kernel_data() */
VB2_ERROR_API_VERIFY_KDATA_WORKBUF,
/* Bad data key in vb2api_verify_kernel_data() */
VB2_ERROR_API_VERIFY_KDATA_KEY,
/* Phase one passing through secdata's request to reboot */
VB2_ERROR_API_PHASE1_SECDATA_REBOOT,
/* Digest buffer passed into vb2api_check_hash incorrect. */
VB2_ERROR_API_CHECK_DIGEST_SIZE,
/**********************************************************************
* Errors which may be generated by implementations of vb2ex functions.
* Implementation may also return its own specific errors, which should
* NOT be in the range VB2_ERROR_BASE...VB2_ERROR_MAX to avoid
* conflicting with future vboot2 error codes.
*/
VB2_ERROR_EX = VB2_ERROR_BASE + 0x0a0000,
/* Read resource not implemented */
VB2_ERROR_EX_READ_RESOURCE_UNIMPLEMENTED,
/* Resource index not found */
VB2_ERROR_EX_READ_RESOURCE_INDEX,
/* Size of resource not big enough for requested offset and/or size */
VB2_ERROR_EX_READ_RESOURCE_SIZE,
/* TPM clear owner failed */
VB2_ERROR_EX_TPM_CLEAR_OWNER,
/* TPM clear owner not implemented */
VB2_ERROR_EX_TPM_CLEAR_OWNER_UNIMPLEMENTED,
/* Hardware crypto engine doesn't support this algorithm (non-fatal) */
VB2_ERROR_EX_HWCRYPTO_UNSUPPORTED,
/**********************************************************************
* Errors generated by host library (non-firmware) start here.
*/
VB2_ERROR_HOST_BASE = 0x20000000,
/**********************************************************************
* Errors generated by host library misc functions
*/
VB2_ERROR_HOST_MISC = VB2_ERROR_HOST_BASE + 0x010000,
/* Unable to open file in read_file() */
VB2_ERROR_READ_FILE_OPEN,
/* Bad size in read_file() */
VB2_ERROR_READ_FILE_SIZE,
/* Unable to allocate buffer in read_file() */
VB2_ERROR_READ_FILE_ALLOC,
/* Unable to read data in read_file() */
VB2_ERROR_READ_FILE_DATA,
/* Unable to open file in write_file() */
VB2_ERROR_WRITE_FILE_OPEN,
/* Unable to write data in write_file() */
VB2_ERROR_WRITE_FILE_DATA,
/* Unable to convert string to struct vb_id */
VB2_ERROR_STR_TO_ID,
/**********************************************************************
* Errors generated by host library key functions
*/
VB2_ERROR_HOST_KEY = VB2_ERROR_HOST_BASE + 0x020000,
/* Unable to allocate key in vb2_private_key_read_pem() */
VB2_ERROR_READ_PEM_ALLOC,
/* Unable to open .pem file in vb2_private_key_read_pem() */
VB2_ERROR_READ_PEM_FILE_OPEN,
/* Bad RSA data from .pem file in vb2_private_key_read_pem() */
VB2_ERROR_READ_PEM_RSA,
/* Unable to set private key description */
VB2_ERROR_PRIVATE_KEY_SET_DESC,
/* Bad magic number in vb2_private_key_unpack() */
VB2_ERROR_UNPACK_PRIVATE_KEY_MAGIC,
/* Bad common header in vb2_private_key_unpack() */
VB2_ERROR_UNPACK_PRIVATE_KEY_HEADER,
/* Bad key data in vb2_private_key_unpack() */
VB2_ERROR_UNPACK_PRIVATE_KEY_DATA,
/* Bad struct version in vb2_private_key_unpack() */
VB2_ERROR_UNPACK_PRIVATE_KEY_STRUCT_VERSION,
/* Unable to allocate buffer in vb2_private_key_unpack() */
VB2_ERROR_UNPACK_PRIVATE_KEY_ALLOC,
/* Unable to unpack RSA key in vb2_private_key_unpack() */
VB2_ERROR_UNPACK_PRIVATE_KEY_RSA,
/* Unable to set description in vb2_private_key_unpack() */
VB2_ERROR_UNPACK_PRIVATE_KEY_DESC,
/* Bad bare hash key in vb2_private_key_unpack() */
VB2_ERROR_UNPACK_PRIVATE_KEY_HASH,
/* Unable to create RSA data in vb2_private_key_write() */
VB2_ERROR_PRIVATE_KEY_WRITE_RSA,
/* Unable to allocate packed key buffer in vb2_private_key_write() */
VB2_ERROR_PRIVATE_KEY_WRITE_ALLOC,
/* Unable to write file in vb2_private_key_write() */
VB2_ERROR_PRIVATE_KEY_WRITE_FILE,
/* Bad algorithm in vb2_private_key_hash() */
VB2_ERROR_PRIVATE_KEY_HASH,
/* Unable to determine key size in vb2_public_key_alloc() */
VB2_ERROR_PUBLIC_KEY_ALLOC_SIZE,
/* Unable to allocate buffer in vb2_public_key_alloc() */
VB2_ERROR_PUBLIC_KEY_ALLOC,
/* Unable to set public key description */
VB2_ERROR_PUBLIC_KEY_SET_DESC,
/* Unable to read key data in vb2_public_key_read_keyb() */
VB2_ERROR_READ_KEYB_DATA,
/* Wrong amount of data read in vb2_public_key_read_keyb() */
VB2_ERROR_READ_KEYB_SIZE,
/* Unable to allocate key buffer in vb2_public_key_read_keyb() */
VB2_ERROR_READ_KEYB_ALLOC,
/* Error unpacking RSA arrays in vb2_public_key_read_keyb() */
VB2_ERROR_READ_KEYB_UNPACK,
/* Unable to read key data in vb2_packed_key_read() */
VB2_ERROR_READ_PACKED_KEY_DATA,
/* Bad key data in vb2_packed_key_read() */
VB2_ERROR_READ_PACKED_KEY,
/* Unable to determine key size in vb2_public_key_pack() */
VB2_ERROR_PUBLIC_KEY_PACK_SIZE,
/* Bad hash algorithm in vb2_public_key_hash() */
VB2_ERROR_PUBLIC_KEY_HASH,
/* Bad key size in vb2_copy_packed_key() */
VB2_ERROR_COPY_KEY_SIZE,
/* Unable to convert back to vb1 crypto algorithm */
VB2_ERROR_VB1_CRYPTO_ALGORITHM,
/* Unable to allocate packed key */
VB2_ERROR_PACKED_KEY_ALLOC,
/* Unable to copy packed key */
VB2_ERROR_PACKED_KEY_COPY,
/**********************************************************************
* Errors generated by host library signature functions
*/
VB2_ERROR_HOST_SIG = VB2_ERROR_HOST_BASE + 0x030000,
/* Bad hash algorithm in vb2_digest_info() */
VB2_ERROR_DIGEST_INFO,
/*
* Unable to determine signature size for key algorithm in
* vb2_sig_size_for_key().
*/
VB2_ERROR_SIG_SIZE_FOR_KEY,
/* Bad signature size in vb2_sign_data() */
VB2_SIGN_DATA_SIG_SIZE,
/* Unable to get digest info in vb2_sign_data() */
VB2_SIGN_DATA_DIGEST_INFO,
/* Unable to get digest size in vb2_sign_data() */
VB2_SIGN_DATA_DIGEST_SIZE,
/* Unable to allocate digest buffer in vb2_sign_data() */
VB2_SIGN_DATA_DIGEST_ALLOC,
/* Unable to initialize digest in vb2_sign_data() */
VB2_SIGN_DATA_DIGEST_INIT,
/* Unable to extend digest in vb2_sign_data() */
VB2_SIGN_DATA_DIGEST_EXTEND,
/* Unable to finalize digest in vb2_sign_data() */
VB2_SIGN_DATA_DIGEST_FINALIZE,
/* RSA encrypt failed in vb2_sign_data() */
VB2_SIGN_DATA_RSA_ENCRYPT,
/* Not enough buffer space to hold signature in vb2_sign_object() */
VB2_SIGN_OBJECT_OVERFLOW,
/**********************************************************************
* Errors generated by host library keyblock functions
*/
VB2_ERROR_HOST_KEYBLOCK = VB2_ERROR_HOST_BASE + 0x040000,
/* Unable to determine signature sizes for vb2_create_keyblock() */
VB2_KEYBLOCK_CREATE_SIG_SIZE,
/* Unable to pack data key for vb2_create_keyblock() */
VB2_KEYBLOCK_CREATE_DATA_KEY,
/* Unable to allocate buffer in vb2_create_keyblock() */
VB2_KEYBLOCK_CREATE_ALLOC,
/* Unable to sign keyblock in vb2_create_keyblock() */
VB2_KEYBLOCK_CREATE_SIGN,
/**********************************************************************
* Errors generated by host library firmware preamble functions
*/
VB2_ERROR_HOST_FW_PREAMBLE = VB2_ERROR_HOST_BASE + 0x050000,
/* Unable to determine signature sizes for vb2_create_fw_preamble() */
VB2_FW_PREAMBLE_CREATE_SIG_SIZE,
/* Unable to allocate buffer in vb2_create_fw_preamble() */
VB2_FW_PREAMBLE_CREATE_ALLOC,
/* Unable to sign preamble in vb2_create_fw_preamble() */
VB2_FW_PREAMBLE_CREATE_SIGN,
/**********************************************************************
* Errors generated by unit test functions
*/
VB2_ERROR_UNIT_TEST = VB2_ERROR_HOST_BASE + 0x060000,
/* Unable to open an input file needed for a unit test */
VB2_ERROR_TEST_INPUT_FILE,
/**********************************************************************
* Highest non-zero error generated inside vboot library. Note that
* error codes passed through vboot when it calls external APIs may
* still be outside this range.
*/
VB2_ERROR_MAX = VB2_ERROR_BASE + 0x1fffffff,
};
#endif /* VBOOT_2_RETURN_CODES_H_ */

View File

@@ -0,0 +1,80 @@
/* 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.
*/
#ifndef VBOOT_REFERENCE_2RSA_H_
#define VBOOT_REFERENCE_2RSA_H_
#include "2crypto.h"
#include "2struct.h"
struct vb2_workbuf;
/* Public key structure in RAM */
struct vb2_public_key {
uint32_t arrsize; /* Length of n[] and rr[] in number of uint32_t */
uint32_t n0inv; /* -1 / n[0] mod 2^32 */
const uint32_t *n; /* Modulus as little endian array */
const uint32_t *rr; /* R^2 as little endian array */
enum vb2_signature_algorithm sig_alg; /* Signature algorithm */
enum vb2_hash_algorithm hash_alg; /* Hash algorithm */
const char *desc; /* Description */
uint32_t version; /* Key version */
const struct vb2_id *id; /* Key ID */
};
/**
* Convert vb2_crypto_algorithm to vb2_signature_algorithm.
*
* @param algorithm Crypto algorithm (vb2_crypto_algorithm)
*
* @return The signature algorithm for that crypto algorithm, or
* VB2_SIG_INVALID if the crypto algorithm or its corresponding signature
* algorithm is invalid or not supported.
*/
enum vb2_signature_algorithm vb2_crypto_to_signature(uint32_t algorithm);
/**
* Return the size of a RSA signature
*
* @param sig_alg Signature algorithm
* @return The size of the signature in bytes, or 0 if error.
*/
uint32_t vb2_rsa_sig_size(enum vb2_signature_algorithm sig_alg);
/**
* Return the size of a pre-processed RSA public key.
*
* @param sig_alg Signature algorithm
* @return The size of the preprocessed key in bytes, or 0 if error.
*/
uint32_t vb2_packed_key_size(enum vb2_signature_algorithm sig_alg);
/**
* Check pkcs 1.5 padding bytes
*
* @param sig Signature to verify
* @param key Key to take signature and hash algorithms from
* @return VB2_SUCCESS, or non-zero if error.
*/
int vb2_check_padding(const uint8_t *sig, const struct vb2_public_key *key);
/* Size of work buffer sufficient for vb2_rsa_verify_digest() worst case */
#define VB2_VERIFY_RSA_DIGEST_WORKBUF_BYTES (3 * 1024)
/**
* Verify a RSA PKCS1.5 signature against an expected hash digest.
*
* @param key Key to use in signature verification
* @param sig Signature to verify (destroyed in process)
* @param digest Digest of signed data
* @param wb Work buffer
* @return VB2_SUCCESS, or non-zero if error.
*/
int vb2_rsa_verify_digest(const struct vb2_public_key *key,
uint8_t *sig,
const uint8_t *digest,
const struct vb2_workbuf *wb);
#endif /* VBOOT_REFERENCE_2RSA_H_ */

View File

@@ -0,0 +1,219 @@
/* 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.
*
* Secure non-volatile storage routines
*/
#ifndef VBOOT_REFERENCE_VBOOT_SECDATA_H_
#define VBOOT_REFERENCE_VBOOT_SECDATA_H_
/*****************************************************************************/
/* Firmware version space */
/* Expected value of vb2_secdata.version */
#define VB2_SECDATA_VERSION 2
/* Flags for firmware space */
enum vb2_secdata_flags {
/*
* Last boot was developer mode. TPM ownership is cleared when
* transitioning to/from developer mode. Set/cleared by
* vb2_check_dev_switch().
*/
VB2_SECDATA_FLAG_LAST_BOOT_DEVELOPER = (1 << 0),
/*
* Virtual developer mode switch is on. Set/cleared by the
* keyboard-controlled dev screens in recovery mode. Cleared by
* vb2_check_dev_switch().
*/
VB2_SECDATA_FLAG_DEV_MODE = (1 << 1),
};
/* Secure data area (firmware space) */
struct vb2_secdata {
/* Struct version, for backwards compatibility */
uint8_t struct_version;
/* Flags; see vb2_secdata_flags */
uint8_t flags;
/* Firmware versions */
uint32_t fw_versions;
/* Reserved for future expansion */
uint8_t reserved[3];
/* CRC; must be last field in struct */
uint8_t crc8;
} __attribute__((packed));
/* Which param to get/set for vb2_secdata_get() / vb2_secdata_set() */
enum vb2_secdata_param {
/* Flags; see vb2_secdata_flags */
VB2_SECDATA_FLAGS = 0,
/* Firmware versions */
VB2_SECDATA_VERSIONS,
};
/*****************************************************************************/
/* Kernel version space */
/* Kernel space - KERNEL_NV_INDEX, locked with physical presence. */
#define VB2_SECDATAK_VERSION 2
#define VB2_SECDATAK_UID 0x4752574c /* 'GRWL' */
struct vb2_secdatak {
/* Struct version, for backwards compatibility */
uint8_t struct_version;
/* Unique ID to detect space redefinition */
uint32_t uid;
/* Kernel versions */
uint32_t kernel_versions;
/* Reserved for future expansion */
uint8_t reserved[3];
/* CRC; must be last field in struct */
uint8_t crc8;
} __attribute__((packed));
/* Which param to get/set for vb2_secdatak_get() / vb2_secdatak_set() */
enum vb2_secdatak_param {
/* Kernel versions */
VB2_SECDATAK_VERSIONS = 0,
};
/*****************************************************************************/
/* Firmware version space functions */
/**
* Check the CRC of the secure storage context.
*
* Use this if reading from secure storage may be flaky, and you want to retry
* reading it several times.
*
* This may be called before vb2_context_init().
*
* @param ctx Context pointer
* @return VB2_SUCCESS, or non-zero error code if error.
*/
int vb2_secdata_check_crc(const struct vb2_context *ctx);
/**
* Create fresh data in the secure storage context.
*
* Use this only when initializing the secure storage context on a new machine
* the first time it boots. Do NOT simply use this if vb2_secdata_check_crc()
* (or any other API in this library) fails; that could allow the secure data
* to be rolled back to an insecure state.
*
* This may be called before vb2_context_init().
*/
int vb2_secdata_create(struct vb2_context *ctx);
/**
* Initialize the secure storage context and verify its CRC.
*
* This must be called before vb2_secdata_get() or vb2_secdata_set().
*
* @param ctx Context pointer
* @return VB2_SUCCESS, or non-zero error code if error.
*/
int vb2_secdata_init(struct vb2_context *ctx);
/**
* Read a secure storage value.
*
* @param ctx Context pointer
* @param param Parameter to read
* @param dest Destination for value
* @return VB2_SUCCESS, or non-zero error code if error.
*/
int vb2_secdata_get(struct vb2_context *ctx,
enum vb2_secdata_param param,
uint32_t *dest);
/**
* Write a secure storage value.
*
* @param ctx Context pointer
* @param param Parameter to write
* @param value New value
* @return VB2_SUCCESS, or non-zero error code if error.
*/
int vb2_secdata_set(struct vb2_context *ctx,
enum vb2_secdata_param param,
uint32_t value);
/*****************************************************************************/
/* Kernel version space functions.
*
* These are separate functions so that they don't bloat the size of the early
* boot code which uses the firmware version space functions.
*/
/**
* Check the CRC of the kernel version secure storage context.
*
* Use this if reading from secure storage may be flaky, and you want to retry
* reading it several times.
*
* This may be called before vb2_context_init().
*
* @param ctx Context pointer
* @return VB2_SUCCESS, or non-zero error code if error.
*/
int vb2_secdatak_check_crc(const struct vb2_context *ctx);
/**
* Create fresh data in the secure storage context.
*
* Use this only when initializing the secure storage context on a new machine
* the first time it boots. Do NOT simply use this if vb2_secdatak_check_crc()
* (or any other API in this library) fails; that could allow the secure data
* to be rolled back to an insecure state.
*
* This may be called before vb2_context_init().
*/
int vb2_secdatak_create(struct vb2_context *ctx);
/**
* Initialize the secure storage context and verify its CRC.
*
* This must be called before vb2_secdatak_get() or vb2_secdatak_set().
*
* @param ctx Context pointer
* @return VB2_SUCCESS, or non-zero error code if error.
*/
int vb2_secdatak_init(struct vb2_context *ctx);
/**
* Read a secure storage value.
*
* @param ctx Context pointer
* @param param Parameter to read
* @param dest Destination for value
* @return VB2_SUCCESS, or non-zero error code if error.
*/
int vb2_secdatak_get(struct vb2_context *ctx,
enum vb2_secdatak_param param,
uint32_t *dest);
/**
* Write a secure storage value.
*
* @param ctx Context pointer
* @param param Parameter to write
* @param value New value
* @return VB2_SUCCESS, or non-zero error code if error.
*/
int vb2_secdatak_set(struct vb2_context *ctx,
enum vb2_secdatak_param param,
uint32_t value);
#endif /* VBOOT_REFERENCE_VBOOT_2SECDATA_H_ */

View File

@@ -0,0 +1,232 @@
/* 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.
*
* These APIs may be called by external firmware as well as vboot. External
* firmware must NOT include this header file directly; instead, define
* NEED_VB2_SHA_LIBRARY and include vb2api.h. This is permissible because the
* SHA library routines below don't interact with the rest of vboot.
*/
#ifndef VBOOT_REFERENCE_2SHA_H_
#define VBOOT_REFERENCE_2SHA_H_
#include "2crypto.h"
/* Hash algorithms may be disabled individually to save code space */
#ifndef VB2_SUPPORT_SHA1
#define VB2_SUPPORT_SHA1 1
#endif
#ifndef VB2_SUPPORT_SHA256
#define VB2_SUPPORT_SHA256 1
#endif
#ifndef VB2_SUPPORT_SHA512
#define VB2_SUPPORT_SHA512 1
#endif
/* These are set to the biggest values among the supported hash algorithms.
* They have to be updated as we add new hash algorithms */
#define VB2_MAX_DIGEST_SIZE VB2_SHA512_DIGEST_SIZE
#define VB2_MAX_BLOCK_SIZE VB2_SHA512_BLOCK_SIZE
#define VB2_INVALID_ALG_NAME "INVALID"
#define VB2_SHA1_DIGEST_SIZE 20
#define VB2_SHA1_BLOCK_SIZE 64
#define VB2_SHA1_ALG_NAME "SHA1"
/* Context structs for hash algorithms */
struct vb2_sha1_context {
uint32_t count;
uint32_t state[5];
#if defined(HAVE_ENDIAN_H) && defined(HAVE_LITTLE_ENDIAN)
union {
uint8_t b[VB2_SHA1_BLOCK_SIZE];
uint32_t w[VB2_SHA1_BLOCK_SIZE / sizeof(uint32_t)];
} buf;
#else
uint8_t buf[VB2_SHA1_BLOCK_SIZE];
#endif
};
#define VB2_SHA256_DIGEST_SIZE 32
#define VB2_SHA256_BLOCK_SIZE 64
#define VB2_SHA256_ALG_NAME "SHA256"
struct vb2_sha256_context {
uint32_t h[8];
uint32_t total_size;
uint32_t size;
uint8_t block[2 * VB2_SHA256_BLOCK_SIZE];
};
#define VB2_SHA512_DIGEST_SIZE 64
#define VB2_SHA512_BLOCK_SIZE 128
#define VB2_SHA512_ALG_NAME "SHA512"
struct vb2_sha512_context {
uint64_t h[8];
uint32_t total_size;
uint32_t size;
uint8_t block[2 * VB2_SHA512_BLOCK_SIZE];
};
/* Hash algorithm independent digest context; includes all of the above. */
struct vb2_digest_context {
/* Context union for all algorithms */
union {
#if VB2_SUPPORT_SHA1
struct vb2_sha1_context sha1;
#endif
#if VB2_SUPPORT_SHA256
struct vb2_sha256_context sha256;
#endif
#if VB2_SUPPORT_SHA512
struct vb2_sha512_context sha512;
#endif
};
/* Current hash algorithm */
enum vb2_hash_algorithm hash_alg;
/* 1 if digest is computed with vb2ex_hwcrypto routines, else 0 */
int using_hwcrypto;
};
/**
* Initialize a hash context.
*
* @param ctx Hash context
*/
void vb2_sha1_init(struct vb2_sha1_context *ctx);
void vb2_sha256_init(struct vb2_sha256_context *ctx);
void vb2_sha512_init(struct vb2_sha512_context *ctx);
/**
* Update (extend) a hash.
*
* @param ctx Hash context
* @param data Data to hash
* @param size Length of data in bytes
*/
void vb2_sha1_update(struct vb2_sha1_context *ctx,
const uint8_t *data,
uint32_t size);
void vb2_sha256_update(struct vb2_sha256_context *ctx,
const uint8_t *data,
uint32_t size);
void vb2_sha512_update(struct vb2_sha512_context *ctx,
const uint8_t *data,
uint32_t size);
/**
* Finalize a hash digest.
*
* @param ctx Hash context
* @param digest Destination for hash; must be VB_SHA*_DIGEST_SIZE bytes
*/
void vb2_sha1_finalize(struct vb2_sha1_context *ctx, uint8_t *digest);
void vb2_sha256_finalize(struct vb2_sha256_context *ctx, uint8_t *digest);
void vb2_sha512_finalize(struct vb2_sha512_context *ctx, uint8_t *digest);
/**
* Hash-extend data
*
* @param from Hash to be extended. It has to be the hash size.
* @param by Block to be extended by. It has to be the hash block size.
* @param to Destination for extended data
*/
void vb2_sha256_extend(const uint8_t *from, const uint8_t *by, uint8_t *to);
/**
* Convert vb2_crypto_algorithm to vb2_hash_algorithm.
*
* @param algorithm Crypto algorithm (vb2_crypto_algorithm)
*
* @return The hash algorithm for that crypto algorithm, or VB2_HASH_INVALID if
* the crypto algorithm or its corresponding hash algorithm is invalid or not
* supported.
*/
enum vb2_hash_algorithm vb2_crypto_to_hash(uint32_t algorithm);
/**
* Return the size of the digest for a hash algorithm.
*
* @param hash_alg Hash algorithm
* @return The size of the digest, or 0 if error.
*/
int vb2_digest_size(enum vb2_hash_algorithm hash_alg);
/**
* Return the block size of a hash algorithm.
*
* @param hash_alg Hash algorithm
* @return The block size of the algorithm, or 0 if error.
*/
int vb2_hash_block_size(enum vb2_hash_algorithm alg);
/**
* Return the name of a hash algorithm
*
* @param alg Hash algorithm ID
* @return String containing a hash name or VB2_INVALID_ALG_NAME
* if <alg> is invalid.
*/
const char *vb2_get_hash_algorithm_name(enum vb2_hash_algorithm alg);
/**
* Initialize a digest context for doing block-style digesting.
*
* @param dc Digest context
* @param hash_alg Hash algorithm
* @return VB2_SUCCESS, or non-zero on error.
*/
int vb2_digest_init(struct vb2_digest_context *dc,
enum vb2_hash_algorithm hash_alg);
/**
* Extend a digest's hash with another block of data.
*
* @param dc Digest context
* @param buf Data to hash
* @param size Length of data in bytes
* @return VB2_SUCCESS, or non-zero on error.
*/
int vb2_digest_extend(struct vb2_digest_context *dc,
const uint8_t *buf,
uint32_t size);
/**
* Finalize a digest and store the result.
*
* The destination digest should be at least vb2_digest_size(algorithm).
*
* @param dc Digest context
* @param digest Destination for digest
* @param digest_size Length of digest buffer in bytes.
* @return VB2_SUCCESS, or non-zero on error.
*/
int vb2_digest_finalize(struct vb2_digest_context *dc,
uint8_t *digest,
uint32_t digest_size);
/**
* Calculate the digest of a buffer and store the result.
*
* @param buf Data to hash
* @param size Length of data in bytes
* @param hash_alg Hash algorithm
* @param digest Destination for digest
* @param digest_size Length of digest buffer in bytes.
* @return VB2_SUCCESS, or non-zero on error.
*/
int vb2_digest_buffer(const uint8_t *buf,
uint32_t size,
enum vb2_hash_algorithm hash_alg,
uint8_t *digest,
uint32_t digest_size);
#endif /* VBOOT_REFERENCE_2SHA_H_ */

View File

@@ -0,0 +1,373 @@
/* 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.
*
* Data structure definitions for verified boot, for on-disk / in-eeprom
* data.
*/
#ifndef VBOOT_REFERENCE_VBOOT_2STRUCT_H_
#define VBOOT_REFERENCE_VBOOT_2STRUCT_H_
#include <stdint.h>
#include "2crypto.h"
/*
* Key block flags.
*
*The following flags set where the key is valid. Not used by firmware
* verification; only kernel verification.
*/
#define VB2_KEY_BLOCK_FLAG_DEVELOPER_0 0x01 /* Developer switch off */
#define VB2_KEY_BLOCK_FLAG_DEVELOPER_1 0x02 /* Developer switch on */
#define VB2_KEY_BLOCK_FLAG_RECOVERY_0 0x04 /* Not recovery mode */
#define VB2_KEY_BLOCK_FLAG_RECOVERY_1 0x08 /* Recovery mode */
#define VB2_GBB_HWID_DIGEST_SIZE 32
/****************************************************************************/
/* Flags for vb2_shared_data.flags */
enum vb2_shared_data_flags {
/* User has explicitly and physically requested recovery */
VB2_SD_FLAG_MANUAL_RECOVERY = (1 << 0),
/* Developer mode is enabled */
/* TODO: should have been VB2_SD_FLAG_DEV_MODE_ENABLED */
VB2_SD_DEV_MODE_ENABLED = (1 << 1),
/*
* TODO: might be nice to add flags for why dev mode is enabled - via
* gbb, virtual dev switch, or forced on for testing.
*/
/* Kernel keyblock was verified by signature (not just hash) */
VB2_SD_FLAG_KERNEL_SIGNED = (1 << 2),
/* Software sync needs to update EC-RO, EC-RW, or PD-RW respectively */
VB2_SD_FLAG_ECSYNC_EC_RO = (1 << 3),
VB2_SD_FLAG_ECSYNC_EC_RW = (1 << 4),
VB2_SD_FLAG_ECSYNC_PD_RW = (1 << 5),
/* Software sync says EC / PD running RW */
VB2_SD_FLAG_ECSYNC_EC_IN_RW = (1 << 6),
VB2_SD_FLAG_ECSYNC_PD_IN_RW = (1 << 7),
};
/* Flags for vb2_shared_data.status */
enum vb2_shared_data_status {
/* Reinitialized NV data due to invalid checksum */
VB2_SD_STATUS_NV_REINIT = (1 << 0),
/* NV data has been initialized */
VB2_SD_STATUS_NV_INIT = (1 << 1),
/* Secure data initialized */
VB2_SD_STATUS_SECDATA_INIT = (1 << 2),
/* Chose a firmware slot */
VB2_SD_STATUS_CHOSE_SLOT = (1 << 3),
/* Secure data kernel version space initialized */
VB2_SD_STATUS_SECDATAK_INIT = (1 << 4),
};
/*
* Data shared between vboot API calls. Stored at the start of the work
* buffer.
*/
struct vb2_shared_data {
/* Flags; see enum vb2_shared_data_flags */
uint32_t flags;
/* Flags from GBB header */
uint32_t gbb_flags;
/*
* Reason we are in recovery mode this boot (enum vb2_nv_recovery), or
* 0 if we aren't.
*/
uint32_t recovery_reason;
/* Firmware slot used last boot (0=A, 1=B) */
uint32_t last_fw_slot;
/* Result of last boot (enum vb2_fw_result) */
uint32_t last_fw_result;
/* Firmware slot used this boot */
uint32_t fw_slot;
/*
* Version for this slot (top 16 bits = key, lower 16 bits = firmware).
*
* TODO: Make this a union to allow getting/setting those versions
* separately?
*/
uint32_t fw_version;
/* Version stored in secdata (must be <= fw_version to boot). */
uint32_t fw_version_secdata;
/*
* Status flags for this boot; see enum vb2_shared_data_status. Status
* is "what we've done"; flags above are "decisions we've made".
*/
uint32_t status;
/**********************************************************************
* Data from kernel verification stage.
*
* TODO: shouldn't be part of the main struct, since that needlessly
* uses more memory during firmware verification.
*/
/*
* Version for the current kernel (top 16 bits = key, lower 16 bits =
* kernel preamble).
*
* TODO: Make this a union to allow getting/setting those versions
* separately?
*/
uint32_t kernel_version;
/* Kernel version from secdatak (must be <= kernel_version to boot) */
uint32_t kernel_version_secdatak;
/**********************************************************************
* Temporary variables used during firmware verification. These don't
* really need to persist through to the OS, but there's nowhere else
* we can put them.
*/
/* Root key offset and size from GBB header */
uint32_t gbb_rootkey_offset;
uint32_t gbb_rootkey_size;
/* HWID digest from GBB header */
uint8_t gbb_hwid_digest[VB2_GBB_HWID_DIGEST_SIZE];
/* Offset of preamble from start of vblock */
uint32_t vblock_preamble_offset;
/*
* Offset and size of packed data key in work buffer. Size is 0 if
* data key is not stored in the work buffer.
*/
uint32_t workbuf_data_key_offset;
uint32_t workbuf_data_key_size;
/*
* Offset and size of firmware preamble in work buffer. Size is 0 if
* preamble is not stored in the work buffer.
*/
uint32_t workbuf_preamble_offset;
uint32_t workbuf_preamble_size;
/*
* Offset and size of hash context in work buffer. Size is 0 if
* hash context is not stored in the work buffer.
*/
uint32_t workbuf_hash_offset;
uint32_t workbuf_hash_size;
/*
* Current tag we're hashing
*
* For new structs, this is the offset of the vb2_signature struct
* in the work buffer.
*
* TODO: rename to workbuf_hash_sig_offset when vboot1 structs are
* deprecated.
*/
uint32_t hash_tag;
/* Amount of data we still expect to hash */
uint32_t hash_remaining_size;
/**********************************************************************
* Temporary variables used during kernel verification. These don't
* really need to persist through to the OS, but there's nowhere else
* we can put them.
*
* TODO: make a union with the firmware verification temp variables,
* or make both of them workbuf-allocated sub-structs, so that we can
* overlap them so kernel variables don't bloat firmware verification
* stage memory requirements.
*/
/*
* Vboot1 shared data header. This data should eventually get folded
* directly into the kernel portion of this struct.
*/
struct VbSharedDataHeader *vbsd;
/*
* Offset and size of packed kernel key in work buffer. Size is 0 if
* subkey is not stored in the work buffer. Note that kernel key may
* be inside the firmware preamble.
*/
uint32_t workbuf_kernel_key_offset;
uint32_t workbuf_kernel_key_size;
/* GBB data and size */
struct vb2_gbb_header *gbb;
uint32_t gbb_size;
} __attribute__((packed));
/****************************************************************************/
/* Signature at start of the GBB
* Note that if you compile in the signature as is, you are likely to break any
* tools that search for the signature. */
#define VB2_GBB_SIGNATURE "$GBB"
#define VB2_GBB_SIGNATURE_SIZE 4
#define VB2_GBB_XOR_CHARS "****"
/* TODO: can we write a macro to produce this at compile time? */
#define VB2_GBB_XOR_SIGNATURE { 0x0e, 0x6d, 0x68, 0x68 }
/* VB2 GBB struct version */
#define VB2_GBB_MAJOR_VER 1
#define VB2_GBB_MINOR_VER 2
/* v1.2 - added fields for sha256 digest of the HWID */
/* Flags for vb2_gbb_header.flags */
enum vb2_gbb_flag {
/*
* Reduce the dev screen delay to 2 sec from 30 sec to speed up
* factory.
*/
VB2_GBB_FLAG_DEV_SCREEN_SHORT_DELAY = (1 << 0),
/*
* BIOS should load option ROMs from arbitrary PCI devices. We'll never
* enable this ourselves because it executes non-verified code, but if
* a customer wants to void their warranty and set this flag in the
* read-only flash, they should be able to do so.
*
* (TODO: Currently not supported. Mark as deprecated/unused?)
*/
VB2_GBB_FLAG_LOAD_OPTION_ROMS = (1 << 1),
/*
* The factory flow may need the BIOS to boot a non-ChromeOS kernel if
* the dev-switch is on. This flag allows that.
*
* (TODO: Currently not supported. Mark as deprecated/unused?)
*/
VB2_GBB_FLAG_ENABLE_ALTERNATE_OS = (1 << 2),
/*
* Force dev switch on, regardless of physical/keyboard dev switch
* position.
*/
VB2_GBB_FLAG_FORCE_DEV_SWITCH_ON = (1 << 3),
/* Allow booting from USB in dev mode even if dev_boot_usb=0. */
VB2_GBB_FLAG_FORCE_DEV_BOOT_USB = (1 << 4),
/* Disable firmware rollback protection. */
VB2_GBB_FLAG_DISABLE_FW_ROLLBACK_CHECK = (1 << 5),
/* Allow Enter key to trigger dev->tonorm screen transition */
VB2_GBB_FLAG_ENTER_TRIGGERS_TONORM = (1 << 6),
/* Allow booting Legacy OSes in dev mode even if dev_boot_legacy=0. */
VB2_GBB_FLAG_FORCE_DEV_BOOT_LEGACY = (1 << 7),
/* Allow booting using alternate keys for FAFT servo testing */
VB2_GBB_FLAG_FAFT_KEY_OVERIDE = (1 << 8),
/* Disable EC software sync */
VB2_GBB_FLAG_DISABLE_EC_SOFTWARE_SYNC = (1 << 9),
/* Default to booting legacy OS when dev screen times out */
VB2_GBB_FLAG_DEFAULT_DEV_BOOT_LEGACY = (1 << 10),
/* Disable PD software sync */
VB2_GBB_FLAG_DISABLE_PD_SOFTWARE_SYNC = (1 << 11),
/* Disable shutdown on lid closed */
VB2_GBB_FLAG_DISABLE_LID_SHUTDOWN = (1 << 12),
/*
* Allow full fastboot capability in firmware even if
* dev_boot_fastboot_full_cap=0.
*/
VB2_GBB_FLAG_FORCE_DEV_BOOT_FASTBOOT_FULL_CAP = (1 << 13),
/* Recovery mode always assumes manual recovery, even if EC_IN_RW=1 */
VB2_GBB_FLAG_FORCE_MANUAL_RECOVERY = (1 << 14),
/* Disable FWMP */
VB2_GBB_FLAG_DISABLE_FWMP = (1 << 15),
/* Enable USB Device Controller */
VB2_GBB_FLAG_ENABLE_UDC = (1 << 16),
};
struct vb2_gbb_header {
/* Fields present in version 1.1 */
uint8_t signature[VB2_GBB_SIGNATURE_SIZE]; /* VB2_GBB_SIGNATURE */
uint16_t major_version; /* See VB2_GBB_MAJOR_VER */
uint16_t minor_version; /* See VB2_GBB_MINOR_VER */
uint32_t header_size; /* Size of GBB header in bytes */
uint32_t flags; /* Flags (see enum vb2_gbb_flag) */
/* Offsets (from start of header) and sizes (in bytes) of components */
uint32_t hwid_offset; /* HWID */
uint32_t hwid_size;
uint32_t rootkey_offset; /* Root key */
uint32_t rootkey_size;
uint32_t bmpfv_offset; /* BMP FV; deprecated in current FW */
uint32_t bmpfv_size;
uint32_t recovery_key_offset; /* Recovery key */
uint32_t recovery_key_size;
/* Added in version 1.2 */
uint8_t hwid_digest[VB2_GBB_HWID_DIGEST_SIZE]; /* SHA-256 of HWID */
/* Pad to match EXPECETED_VB2_GBB_HEADER_SIZE. Initialize to 0. */
uint8_t pad[48];
} __attribute__((packed));
/* The GBB is used outside of vboot_reference, so this size is important. */
#define EXPECTED_VB2_GBB_HEADER_SIZE 128
/*
* Root key hash for Ryu devices only. Contains the hash of the root key.
* This will be embedded somewhere inside the RO part of the firmware, so that
* it can verify the GBB contains only the official root key.
*/
#define RYU_ROOT_KEY_HASH_MAGIC "RtKyHash"
#define RYU_ROOT_KEY_HASH_MAGIC_INVCASE "rTkYhASH"
#define RYU_ROOT_KEY_HASH_MAGIC_SIZE 8
#define RYU_ROOT_KEY_HASH_VERSION_MAJOR 1
#define RYU_ROOT_KEY_HASH_VERSION_MINOR 0
struct vb2_ryu_root_key_hash {
/* Magic number (RYU_ROOT_KEY_HASH_MAGIC) */
uint8_t magic[RYU_ROOT_KEY_HASH_MAGIC_SIZE];
/* Version of this struct */
uint16_t header_version_major;
uint16_t header_version_minor;
/*
* Length of this struct, in bytes, including any variable length data
* which follows (there is none, yet).
*/
uint32_t struct_size;
/*
* SHA-256 hash digest of the entire root key section from the GBB. If
* all 0 bytes, all root keys will be treated as if matching.
*/
uint8_t root_key_hash_digest[32];
};
#define EXPECTED_VB2_RYU_ROOT_KEY_HASH_SIZE 48
#endif /* VBOOT_REFERENCE_VBOOT_2STRUCT_H_ */

View File

@@ -0,0 +1,27 @@
/* 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.
*/
/*
* System includes for vboot reference library. With few exceptions, this is
* the ONLY place in firmware/ where system headers may be included via
* #include <...>, so that there's only one place that needs to be fixed up for
* platforms which don't have all the system includes.
*/
#ifndef VBOOT_REFERENCE_2_SYSINCLUDES_H_
#define VBOOT_REFERENCE_2_SYSINCLUDES_H_
#include <inttypes.h> /* For PRIu64 */
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#if defined(HAVE_ENDIAN_H) && defined(HAVE_LITTLE_ENDIAN)
#include <byteswap.h>
#include <memory.h>
#endif
#endif /* VBOOT_REFERENCE_2_SYSINCLUDES_H_ */

View File

@@ -0,0 +1,21 @@
/* Copyright 2015 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 updating the TPM state with the status of boot path.
*/
#ifndef VBOOT_REFERENCE_2TPM_BOOTMODE_H_
#define VBOOT_REFERENCE_2TPM_BOOTMODE_H_
#include "2api.h"
/**
* Return digest indicating the boot state
*
* @param ctx Vboot context
* @return Pointer to sha1 digest of size VB2_SHA1_DIGEST_SIZE
*/
const uint8_t *vb2_get_boot_state_digest(struct vb2_context *ctx);
#endif /* VBOOT_REFERENCE_2TPM_BOOTMODE_H_ */

View File

@@ -0,0 +1,47 @@
Here's what's what in the firmware/ directory.
bdb/
Code for managing Boot Descriptor Blocks (BDB).
include/
lib/
These are the original structures and APIs used in the earliest
Chromebooks and continuing through 2014. It never had a version as such to
begin with, but we now refer to this implementation as "vboot1" or
"vboot version 1.0".
linktest/
stub/
These are stubs used to link the vboot1 libraries into host-side test
executables so we can run some tests on the build machine instead of a
Chromebook.
2lib/
In 2014 we began work on a new vboot API. The first step was just a
refactoring and renaming of the verification API. The public functions and
external headers that are exported for use by the Chrome OS firmware (or
anything else that wants to use vboot) live in here. The internal
structures and implementations go elsewhere.
lib20/
This is an early implementation of the public (2lib/) API. It is
binary-compatible with vboot1, so although the interface details are
different, any existing on-device structures or signatures created by the
vboot1 tools can be validated using this implementation.
This was deployed slightly before it was ready. That's not a problem,
thanks to the binary compatibility, but this directory will be abandoned
Real Soon Now, except for the product support branches.
lib21/
This is where the current development of the second-generation vboot API
is taking place. It uses the public (2lib/) API, but will NOT be binary
compatible with vboot1 structs. Because of the early release of the lib20
stuff, we're actually calling this lib21.

View File

@@ -0,0 +1,27 @@
// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,30 @@
Boot Descriptor Block (BDB) library and utilities
Building:
---------
The host-side library and utilities requires OpenSSL.
Do 'make runtests' to ensure everything is working.
Generating a BDB:
-----------------
Edit the options in bdb_create.c. Then 'make bdb'.
In the next release, this will take a config file rather than
requiring recompilation each time. Also, the BDB header and data will
be signed in two separate steps, so that the private BDB key is not
required each time.
Revision History:
-----------------
v0.1.2 24-Nov-2015 Add support for RSA-3072B keys and signatures.
Add dump_rsa utility and 'make testkeys' to create
new keys.
Use a RSA-3072B (exponent 3) key for the subkey so
the exponent 3 code gets tested.
v0.1.1 17-Nov-2015 Add support for ECDSA-521 data types. Note that
only the data types are supported; there is not a
C implementation for ECDSA.
v0.1.0 15-Sep-2015 Initial version.

View File

@@ -0,0 +1,450 @@
/* Copyright 2015 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.
*
* Boot descriptor block firmware functions
*/
#include "2sysincludes.h"
#include "2common.h"
#include "2sha.h"
#include "bdb.h"
/*****************************************************************************/
/**
* Check if string contains a null terminator.
*
* Bytes after the null terminator do not need to be null.
*
* @param s String to check
* @param size Size of string buffer in characters
* @return 1 if string has a null terminator, 0 if not
*/
int string_has_null(const char *s, size_t size)
{
for (; size; size--) {
if (*s++ == 0)
return 1;
}
return 0;
}
int bdb_check_header(const struct bdb_header *p, size_t size)
{
if (size < sizeof(*p) || size < p->struct_size)
return BDB_ERROR_BUF_SIZE;
if (p->struct_magic != BDB_HEADER_MAGIC)
return BDB_ERROR_STRUCT_MAGIC;
if (p->struct_major_version != BDB_HEADER_VERSION_MAJOR)
return BDB_ERROR_STRUCT_VERSION;
/* Note that minor version doesn't matter yet */
if (p->struct_size < sizeof(*p))
return BDB_ERROR_STRUCT_SIZE;
if (p->oem_area_0_size & 3)
return BDB_ERROR_OEM_AREA_SIZE; /* Not 32-bit aligned */
/*
* Make sure the BDB is at least big enough for us. At this point, all
* the caller may have loaded is this header We'll check if there's
* space for everything else after we load it.
*/
if (p->bdb_size < sizeof(*p))
return BDB_ERROR_BDB_SIZE;
/*
* The rest of the fields don't matter yet; we'll check them when we
* check the BDB itself.
*/
return BDB_SUCCESS;
}
int bdb_check_key(const struct bdb_key *p, size_t size)
{
size_t expect_key_size = 0;
if (size < sizeof(*p) || size < p->struct_size)
return BDB_ERROR_BUF_SIZE;
if (p->struct_magic != BDB_KEY_MAGIC)
return BDB_ERROR_STRUCT_MAGIC;
if (p->struct_major_version != BDB_KEY_VERSION_MAJOR)
return BDB_ERROR_STRUCT_VERSION;
/* Note that minor version doesn't matter yet */
if (!string_has_null(p->description, sizeof(p->description)))
return BDB_ERROR_DESCRIPTION;
/* We currently only support SHA-256 */
if (p->hash_alg != BDB_HASH_ALG_SHA256)
return BDB_ERROR_HASH_ALG;
/* Make sure signature algorithm and size are correct */
switch (p->sig_alg) {
case BDB_SIG_ALG_RSA4096:
expect_key_size = BDB_RSA4096_KEY_DATA_SIZE;
break;
case BDB_SIG_ALG_ECSDSA521:
expect_key_size = BDB_ECDSA521_KEY_DATA_SIZE;
break;
case BDB_SIG_ALG_RSA3072B:
expect_key_size = BDB_RSA3072B_KEY_DATA_SIZE;
break;
default:
return BDB_ERROR_SIG_ALG;
}
if (p->struct_size < sizeof(*p) + expect_key_size)
return BDB_ERROR_STRUCT_SIZE;
return BDB_SUCCESS;
}
int bdb_check_sig(const struct bdb_sig *p, size_t size)
{
size_t expect_sig_size = 0;
if (size < sizeof(*p) || size < p->struct_size)
return BDB_ERROR_BUF_SIZE;
if (p->struct_magic != BDB_SIG_MAGIC)
return BDB_ERROR_STRUCT_MAGIC;
if (p->struct_major_version != BDB_SIG_VERSION_MAJOR)
return BDB_ERROR_STRUCT_VERSION;
/* Note that minor version doesn't matter yet */
if (!string_has_null(p->description, sizeof(p->description)))
return BDB_ERROR_DESCRIPTION;
/* We currently only support SHA-256 */
if (p->hash_alg != BDB_HASH_ALG_SHA256)
return BDB_ERROR_HASH_ALG;
/* Make sure signature algorithm and size are correct */
switch (p->sig_alg) {
case BDB_SIG_ALG_RSA4096:
expect_sig_size = BDB_RSA4096_SIG_SIZE;
break;
case BDB_SIG_ALG_ECSDSA521:
expect_sig_size = BDB_ECDSA521_SIG_SIZE;
break;
case BDB_SIG_ALG_RSA3072B:
expect_sig_size = BDB_RSA3072B_SIG_SIZE;
break;
default:
return BDB_ERROR_SIG_ALG;
}
if (p->struct_size < sizeof(*p) + expect_sig_size)
return BDB_ERROR_STRUCT_SIZE;
return BDB_SUCCESS;
}
int bdb_check_data(const struct bdb_data *p, size_t size)
{
size_t need_size;
if (size < sizeof(*p) || size < p->signed_size)
return BDB_ERROR_BUF_SIZE;
if (p->struct_magic != BDB_DATA_MAGIC)
return BDB_ERROR_STRUCT_MAGIC;
if (p->struct_major_version != BDB_DATA_VERSION_MAJOR)
return BDB_ERROR_STRUCT_VERSION;
/* Note that minor version doesn't matter yet */
if (!string_has_null(p->description, sizeof(p->description)))
return BDB_ERROR_DESCRIPTION;
if (p->struct_size < sizeof(*p))
return BDB_ERROR_STRUCT_SIZE;
if (p->hash_entry_size < sizeof(struct bdb_hash))
return BDB_ERROR_HASH_ENTRY_SIZE;
/* Calculate expected size */
need_size = p->struct_size + p->num_hashes * p->hash_entry_size;
/* Make sure OEM area size doesn't cause wraparound */
if (need_size + p->oem_area_1_size < need_size)
return BDB_ERROR_OEM_AREA_SIZE;
if (p->oem_area_1_size & 3)
return BDB_ERROR_OEM_AREA_SIZE; /* Not 32-bit aligned */
need_size += p->oem_area_1_size;
if (p->signed_size < need_size)
return BDB_ERROR_SIGNED_SIZE;
return BDB_SUCCESS;
}
/*****************************************************************************/
const struct bdb_header *bdb_get_header(const void *buf)
{
return buf;
}
uint32_t bdb_size_of(const void *buf)
{
return bdb_get_header(buf)->bdb_size;
}
const struct bdb_key *bdb_get_bdbkey(const void *buf)
{
const struct bdb_header *h = bdb_get_header(buf);
const uint8_t *b8 = buf;
/* BDB key follows header */
return (const struct bdb_key *)(b8 + h->struct_size);
}
const void *bdb_get_oem_area_0(const void *buf)
{
const struct bdb_key *k = bdb_get_bdbkey(buf);
const uint8_t *b8 = (const uint8_t *)k;
/* OEM area 0 follows BDB key */
return b8 + k->struct_size;
}
const struct bdb_key *bdb_get_datakey(const void *buf)
{
const struct bdb_header *h = bdb_get_header(buf);
const uint8_t *b8 = bdb_get_oem_area_0(buf);
/* datakey follows OEM area 0 */
return (const struct bdb_key *)(b8 + h->oem_area_0_size);
}
ptrdiff_t bdb_offset_of_datakey(const void *buf)
{
return vb2_offset_of(buf, bdb_get_datakey(buf));
}
const struct bdb_sig *bdb_get_header_sig(const void *buf)
{
const struct bdb_header *h = bdb_get_header(buf);
const uint8_t *b8 = bdb_get_oem_area_0(buf);
/* Header signature starts after signed data */
return (const struct bdb_sig *)(b8 + h->signed_size);
}
ptrdiff_t bdb_offset_of_header_sig(const void *buf)
{
return vb2_offset_of(buf, bdb_get_header_sig(buf));
}
const struct bdb_data *bdb_get_data(const void *buf)
{
const struct bdb_sig *s = bdb_get_header_sig(buf);
const uint8_t *b8 = (const uint8_t *)s;
/* Data follows header signature */
return (const struct bdb_data *)(b8 + s->struct_size);
}
ptrdiff_t bdb_offset_of_data(const void *buf)
{
return vb2_offset_of(buf, bdb_get_data(buf));
}
const void *bdb_get_oem_area_1(const void *buf)
{
const struct bdb_data *p = bdb_get_data(buf);
const uint8_t *b8 = (const uint8_t *)p;
/* OEM area 1 follows BDB data */
return b8 + p->struct_size;
}
static const void *bdb_get_hash(const void *buf)
{
const struct bdb_data *data = bdb_get_data(buf);
const uint8_t *b8 = bdb_get_oem_area_1(buf);
/* Hashes follow OEM area 0 */
return b8 + data->oem_area_1_size;
}
const struct bdb_hash *bdb_get_hash_by_type(const void *buf,
enum bdb_data_type type)
{
const struct bdb_data *data = bdb_get_data(buf);
const uint8_t *b8 = bdb_get_hash(buf);
int i;
/* Search for a matching hash */
for (i = 0; i < data->num_hashes; i++, b8 += data->hash_entry_size) {
const struct bdb_hash *h = (const struct bdb_hash *)b8;
if (h->type == type)
return h;
}
return NULL;
}
const struct bdb_hash *bdb_get_hash_by_index(const void *buf, int index)
{
const struct bdb_data *data = bdb_get_data(buf);
const uint8_t *p = bdb_get_hash(buf);
const struct bdb_hash *h = NULL;
int i;
/* Search for a matching hash */
for (i = 0; i < data->num_hashes; i++, p += data->hash_entry_size) {
if (i == index) {
h = (const struct bdb_hash *)p;
break;
}
}
return h;
}
const struct bdb_sig *bdb_get_data_sig(const void *buf)
{
const struct bdb_data *data = bdb_get_data(buf);
const uint8_t *b8 = (const uint8_t *)data;
/* Data signature starts after signed data */
return (const struct bdb_sig *)(b8 + data->signed_size);
}
/*****************************************************************************/
int bdb_verify_sig(const struct bdb_key *key,
const struct bdb_sig *sig,
const uint8_t *digest)
{
/* Key and signature algorithms must match */
if (key->sig_alg != sig->sig_alg)
return BDB_ERROR_SIG_ALG;
switch (key->sig_alg) {
case BDB_SIG_ALG_RSA4096:
if (bdb_rsa4096_verify(key->key_data, sig->sig_data, digest))
return BDB_ERROR_VERIFY_SIG;
break;
case BDB_SIG_ALG_ECSDSA521:
if (bdb_ecdsa521_verify(key->key_data, sig->sig_data, digest))
return BDB_ERROR_VERIFY_SIG;
break;
case BDB_SIG_ALG_RSA3072B:
if (bdb_rsa3072b_verify(key->key_data, sig->sig_data, digest))
return BDB_ERROR_VERIFY_SIG;
break;
default:
return BDB_ERROR_VERIFY_SIG;
}
return BDB_SUCCESS;
}
int bdb_verify(const void *buf, size_t size, const uint8_t *bdb_key_digest)
{
const uint8_t *end = (const uint8_t *)buf + size;
const struct bdb_header *h;
const struct bdb_key *bdbkey, *datakey;
const struct bdb_sig *sig;
const struct bdb_data *data;
const void *oem;
uint8_t digest[BDB_SHA256_DIGEST_SIZE];
int bdb_digest_mismatch = -1;
/* Make sure buffer doesn't wrap around address space */
if (end < (const uint8_t *)buf)
return BDB_ERROR_BUF_SIZE;
/*
* Check header now that we've actually loaded it. We can't guarantee
* this is the same header which was checked before.
*/
h = bdb_get_header(buf);
if (bdb_check_header(h, size))
return BDB_ERROR_HEADER;
/* Sanity-check BDB key */
bdbkey = bdb_get_bdbkey(buf);
if (bdb_check_key(bdbkey, end - (const uint8_t *)bdbkey))
return BDB_ERROR_BDBKEY;
/* Calculate BDB key digest and compare with expected */
if (vb2_digest_buffer((uint8_t *)bdbkey, bdbkey->struct_size,
VB2_HASH_SHA256, digest, BDB_SHA256_DIGEST_SIZE))
return BDB_ERROR_DIGEST;
if (bdb_key_digest)
bdb_digest_mismatch = memcmp(digest,
bdb_key_digest, sizeof(digest));
/* Make sure OEM area 0 fits */
oem = bdb_get_oem_area_0(buf);
if (h->oem_area_0_size > end - (const uint8_t *)oem)
return BDB_ERROR_OEM_AREA_0;
/* Sanity-check datakey */
datakey = bdb_get_datakey(buf);
if (bdb_check_key(datakey, end - (const uint8_t *)datakey))
return BDB_ERROR_DATAKEY;
/* Make sure enough data was signed, and the signed data fits */
if (h->oem_area_0_size + datakey->struct_size > h->signed_size ||
h->signed_size > end - (const uint8_t *)oem)
return BDB_ERROR_BDB_SIGNED_SIZE;
/* Sanity-check header signature */
sig = bdb_get_header_sig(buf);
if (bdb_check_sig(sig, end - (const uint8_t *)sig))
return BDB_ERROR_HEADER_SIG;
/* Make sure it signed the right amount of data */
if (sig->signed_size != h->signed_size)
return BDB_ERROR_HEADER_SIG;
/* Calculate header digest and compare with expected signature */
if (vb2_digest_buffer((uint8_t *)oem, h->signed_size,
VB2_HASH_SHA256, digest, BDB_SHA256_DIGEST_SIZE))
return BDB_ERROR_DIGEST;
if (bdb_verify_sig(bdbkey, sig, digest))
return BDB_ERROR_HEADER_SIG;
/*
* Sanity-check data struct. This also checks that OEM area 1 and the
* hashes fit in the remaining buffer.
*/
data = bdb_get_data(buf);
if (bdb_check_data(data, end - (const uint8_t *)data))
return BDB_ERROR_DATA;
/* Sanity-check data signature */
sig = bdb_get_data_sig(buf);
if (bdb_check_sig(sig, end - (const uint8_t *)sig))
return BDB_ERROR_DATA_CHECK_SIG;
if (sig->signed_size != data->signed_size)
return BDB_ERROR_DATA_SIGNED_SIZE;
/* Calculate data digest and compare with expected signature */
if (vb2_digest_buffer((uint8_t *)data, data->signed_size,
VB2_HASH_SHA256, digest, BDB_SHA256_DIGEST_SIZE))
return BDB_ERROR_DIGEST;
if (bdb_verify_sig(datakey, sig, digest))
return BDB_ERROR_DATA_SIG;
/* Return success or success-other-than-BDB-key-mismatch */
return bdb_digest_mismatch ? BDB_GOOD_OTHER_THAN_KEY : BDB_SUCCESS;
}

View File

@@ -0,0 +1,232 @@
/* Copyright 2015 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.
*
* Boot descriptor block firmware functions
*/
#ifndef VBOOT_REFERENCE_BDB_H_
#define VBOOT_REFERENCE_BDB_H_
#include <stdlib.h>
#include <stddef.h>
#include "bdb_struct.h"
/*****************************************************************************/
/*
Expected calling sequence:
Load and check just the header
bdb_check_header(buf, size);
Load and verify the entire BDB
bdb_verify(buf, size, bdb_key_hash, dev_mode_flag);
Check RW datakey version. If normal boot from primary BDB, roll forward
Check data version. If normal boot from primary BDB, roll forward
*/
/*****************************************************************************/
/* Codes for functions returning numeric error codes */
enum bdb_return_code {
/* Success */
BDB_SUCCESS = 0,
/* BDB key did not match hash, but other than that the BDB was
* fully verified. */
BDB_GOOD_OTHER_THAN_KEY = 1,
/* Function is not implemented, thus supposed to be not called */
BDB_ERROR_NOT_IMPLEMENTED,
/* Other errors */
BDB_ERROR_UNKNOWN = 100,
/* Buffer size too small or wraps around */
BDB_ERROR_BUF_SIZE,
/* Bad fields in structures */
BDB_ERROR_STRUCT_MAGIC,
BDB_ERROR_STRUCT_VERSION,
BDB_ERROR_STRUCT_SIZE,
BDB_ERROR_SIGNED_SIZE,
BDB_ERROR_BDB_SIZE,
BDB_ERROR_OEM_AREA_SIZE,
BDB_ERROR_HASH_ENTRY_SIZE,
BDB_ERROR_HASH_ALG,
BDB_ERROR_SIG_ALG,
BDB_ERROR_DESCRIPTION,
/* Bad components of BDB in bdb_verify() */
BDB_ERROR_HEADER,
BDB_ERROR_BDBKEY,
BDB_ERROR_OEM_AREA_0,
BDB_ERROR_DATAKEY,
BDB_ERROR_BDB_SIGNED_SIZE,
BDB_ERROR_HEADER_SIG,
BDB_ERROR_DATA,
BDB_ERROR_DATA_SIG,
BDB_ERROR_DATA_CHECK_SIG,
BDB_ERROR_DATA_SIGNED_SIZE,
/* Other errors in bdb_verify() */
BDB_ERROR_DIGEST, /* Error calculating digest */
BDB_ERROR_VERIFY_SIG, /* Error verifying signature */
/* Errors in vba_bdb_init */
BDB_ERROR_TRY_OTHER_SLOT,
BDB_ERROR_RECOVERY_REQUEST,
BDB_ERROR_NVM_INIT,
BDB_ERROR_NVM_WRITE,
BDB_ERROR_NVM_RW_HMAC,
BDB_ERROR_NVM_RW_INVALID_HMAC,
BDB_ERROR_NVM_INVALID_PARAMETER,
BDB_ERROR_NVM_INVALID_SECRET,
BDB_ERROR_NVM_RW_MAGIC,
BDB_ERROR_NVM_STRUCT_SIZE,
BDB_ERROR_NVM_WRITE_VERIFY,
BDB_ERROR_NVM_STRUCT_VERSION,
BDB_ERROR_NVM_VBE_READ,
BDB_ERROR_NVM_RW_BUFFER_SMALL,
BDB_ERROR_DECRYPT_BUC,
BDB_ERROR_ENCRYPT_BUC,
BDB_ERROR_WRITE_BUC,
BDB_ERROR_SECRET_TYPE,
BDB_ERROR_SECRET_BUC,
BDB_ERROR_SECRET_BOOT_VERIFIED,
BDB_ERROR_SECRET_BOOT_PATH,
BDB_ERROR_SECRET_BDB,
};
/*****************************************************************************/
/* Functions */
/**
* Sanity-check BDB structures.
*
* This checks for known version numbers, magic numbers, algorithms, etc. and
* ensures the sizes are consistent with those parameters.
*
* @param p Pointer to structure to check
* @param size Size of structure buffer
* @return 0 if success, non-zero error code if error.
*/
int bdb_check_header(const struct bdb_header *p, size_t size);
int bdb_check_key(const struct bdb_key *p, size_t size);
int bdb_check_sig(const struct bdb_sig *p, size_t size);
int bdb_check_data(const struct bdb_data *p, size_t size);
/**
* Verify the entire BDB
*
* @param buf Data to hash
* @param size Size of data in bytes
* @param bdb_key_digest Pointer to expected digest for BDB key.
* Must be BDB_SHA256_DIGEST_SIZE bytes long.
* If it's NULL, digest match will be skipped
* (and it'll be treated as 'mismatch').
*
* @return 0 if success, non-zero error code if error. Note that error code
* BDB_GOOD_OTHER_THAN_KEY may still indicate an acceptable BDB if the Boot
* Verified fuse has not been set, or in developer mode.
*/
int bdb_verify(const void *buf, size_t size, const uint8_t *bdb_key_digest);
/**
* Functions to extract things from a verified BDB buffer.
*
* Do not call these externally until after bdb_verify()! These methods
* assume data structures have already been verified.
*
* @param buf Pointer to BDB buffer
* @param type Data type, for bdb_get_hash()
* @return A pointer to the requested data, or NULL if error / not present.
*/
const struct bdb_header *bdb_get_header(const void *buf);
const struct bdb_key *bdb_get_bdbkey(const void *buf);
const void *bdb_get_oem_area_0(const void *buf);
const struct bdb_key *bdb_get_datakey(const void *buf);
const struct bdb_sig *bdb_get_header_sig(const void *buf);
const struct bdb_data *bdb_get_data(const void *buf);
const void *bdb_get_oem_area_1(const void *buf);
const struct bdb_hash *bdb_get_hash_by_type(const void *buf,
enum bdb_data_type type);
const struct bdb_hash *bdb_get_hash_by_index(const void *buf, int index);
const struct bdb_sig *bdb_get_data_sig(const void *buf);
/**
* Functions to calculate size of BDB components
*
* @param buf Pointer to BDB buffer
* @return Size of the component
*/
uint32_t bdb_size_of(const void *buf);
/**
* Functions to calculate offset of BDB components
*
* @param buf Pointer to BDB buffer
* @return Offset of the component
*/
ptrdiff_t bdb_offset_of_datakey(const void *buf);
ptrdiff_t bdb_offset_of_header_sig(const void *buf);
ptrdiff_t bdb_offset_of_data(const void *buf);
/*****************************************************************************/
/* Functions probably provided by the caller */
/**
* Calculate a SHA-256 digest of a buffer.
*
* @param digest Pointer to the digest buffer. Must be
* BDB_SHA256_DIGEST_SIZE bytes long.
* @param buf Data to hash
* @param size Size of data in bytes
* @return 0 if success, non-zero error code if error.
*/
int bdb_sha256(void *digest, const void *buf, size_t size);
/**
* Verify a RSA-4096 signed digest
*
* @param key_data Key data to use (BDB_RSA4096_KEY_DATA_SIZE bytes)
* @param sig_data Signature to verify (BDB_RSA4096_SIG_SIZE bytes)
* @param digest Digest of signed data (BDB_SHA256_DIGEST bytes)
* @return 0 if success, non-zero error code if error.
*/
int bdb_rsa4096_verify(const uint8_t *key_data,
const uint8_t *sig,
const uint8_t *digest);
/**
* Verify a RSA-3072B signed digest
*
* @param key_data Key data to use (BDB_RSA3072B_KEY_DATA_SIZE bytes)
* @param sig_data Signature to verify (BDB_RSA3072B_SIG_SIZE bytes)
* @param digest Digest of signed data (BDB_SHA256_DIGEST bytes)
* @return 0 if success, non-zero error code if error.
*/
int bdb_rsa3072b_verify(const uint8_t *key_data,
const uint8_t *sig,
const uint8_t *digest);
/**
* Verify a ECDSA-521 signed digest
*
* @param key_data Key data to use (BDB_ECDSA521_KEY_DATA_SIZE bytes)
* @param sig_data Signature to verify (BDB_ECDSA521_SIG_SIZE bytes)
* @param digest Digest of signed data (BDB_SHA256_DIGEST bytes)
* @return 0 if success, non-zero error code if error.
*/
int bdb_ecdsa521_verify(const uint8_t *key_data,
const uint8_t *sig,
const uint8_t *digest);
/*****************************************************************************/
#endif /* VBOOT_REFERENCE_BDB_H_ */

View File

@@ -0,0 +1,194 @@
/* Copyright 2016 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.
*/
#ifndef VBOOT_REFERENCE_FIRMWARE_BDB_BDB_API_H
#define VBOOT_REFERENCE_FIRMWARE_BDB_BDB_API_H
#include <stdint.h>
#include "vboot_register.h"
#include "nvm.h"
#include "secrets.h"
#include "bdb_flag.h"
struct vba_context {
/* Indicate which slot is being tried: 0 - primary, 1 - secondary */
uint8_t slot;
/* Defined by VBA_CONTEXT_FLAG_* in bdb_flag.h */
uint32_t flags;
/* BDB */
uint8_t *bdb;
/* Secrets */
struct bdb_secrets *secrets;
/* NVM-RW buffer */
struct nvmrw nvmrw;
};
/**
* Initialize vboot process
*
* @param ctx
* @return enum bdb_return_code
*/
int vba_bdb_init(struct vba_context *ctx);
/**
* Finalize vboot process
*
* @param ctx
* @return enum bdb_return_code
*/
int vba_bdb_finalize(struct vba_context *ctx);
/**
* Log failed boot attempt and reset the chip
*
* @param ctx
*/
void vba_bdb_fail(struct vba_context *ctx);
/**
* Update kernel and its data key version in NVM
*
* This is the function called from SP-RW, which receives a kernel version
* from an AP-RW after successful verification of a kernel.
*
* It checks whether the version in NVM-RW is older than the reported version
* or not. If so, it updates the version in NVM-RW.
*
* @param ctx
* @param kernel_data_key_version
* @param kernel_version
* @return BDB_SUCCESS or BDB_ERROR_*
*/
int vba_update_kernel_version(struct vba_context *ctx,
uint32_t kernel_data_key_version,
uint32_t kernel_version);
/**
* Write new boot unlock code to NVM-RW
*
* @param ctx
* @param new_buc New BUC to be written
* @return BDB_SUCCESS or BDB_ERROR_*
*/
int vba_update_buc(struct vba_context *ctx, uint8_t *new_buc);
/**
* Derive a secret
*
* This derives a new secret from a secret passed from SP-RO.
*
* @param ctx
* @param type Type of secret to derive
* @param buf Buffer containing data to derive secret from
* @param buf_size Size of <buf>
* @return BDB_SUCCESS or BDB_ERROR_*
*/
int vba_derive_secret(struct vba_context *ctx, enum bdb_secret_type type,
uint8_t *wsr, const uint8_t *buf, uint32_t buf_size);
/**
* Clear a secret
*
* @param ctx
* @param type Type of secret to clear
* @return BDB_SUCCESS or BDB_ERROR_*
*/
int vba_clear_secret(struct vba_context *ctx, enum bdb_secret_type type);
/**
* Extend secrets for SP-RO
*
* @param ctx struct vba_context
* @param bdb BDB
* @param wsr Pointer to working secret register contents
* @param extend Function to be called for extending a secret
* @return BDB_SUCCESS or BDB_ERROR_*
*/
typedef void (*f_extend)(const uint8_t *from, const uint8_t *by, uint8_t *to);
int vba_extend_secrets_ro(struct vba_context *ctx, const uint8_t *bdb,
uint8_t *wsr, f_extend extend);
/**
* Get vboot register value
*
* Implemented by each chip
*
* @param type Type of register to get
* @return Register value
*/
uint32_t vbe_get_vboot_register(enum vboot_register type);
/**
* Set vboot register value
*
* Implemented by each chip
*
* @param type Type of register to set
* @param val Value to set
*/
void vbe_set_vboot_register(enum vboot_register type, uint32_t val);
/**
* Reset the SoC
*
* Implemented by each chip. This is different from reboot (a.k.a. board reset,
* cold reset).
*/
void vbe_reset(void);
/**
* Read contents from Non-Volatile Memory
*
* Implemented by each chip.
*
* @param type Type of NVM
* @param buf Buffer where the data will be read to
* @param size Size of data to read
* @return Zero if success or non-zero otherwise
*/
int vbe_read_nvm(enum nvm_type type, uint8_t *buf, uint32_t size);
/**
* Write contents to Non-Volatile Memory
*
* Implemented by each chip.
*
* @param type Type of NVM
* @param buf Buffer where the data will be written from
* @param size Size of data to write
* @return Zero if success or non-zero otherwise
*/
int vbe_write_nvm(enum nvm_type type, void *buf, uint32_t size);
/**
* Encrypt data by AES-256
*
* @param msg Message to be encrypted
* @param len Length of <msg> in bytes
* @param key Key used for encryption
* @param out Buffer where encrypted message is stored
* @return BDB_SUCCESS or BDB_ERROR_*
*/
int vbe_aes256_encrypt(const uint8_t *msg, uint32_t len, const uint8_t *key,
uint8_t *out);
/**
* Decrypt data by AES-256
*
* @param msg Message to be decrypted
* @param len Length of <msg> in bytes
* @param key Key used for decryption
* @param out Buffer where decrypted message is stored
* @return BDB_SUCCESS or BDB_ERROR_*
*/
int vbe_aes256_decrypt(const uint8_t *msg, uint32_t len, const uint8_t *key,
uint8_t *out);
#endif

View File

@@ -0,0 +1,15 @@
/* Copyright 2016 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.
*/
#ifndef VBOOT_REFERENCE_FIRMWARE_BDB_BDB_FLAG_H
#define VBOOT_REFERENCE_FIRMWARE_BDB_BDB_FLAG_H
/* Indicate whether BDB key is verified */
#define VBA_CONTEXT_FLAG_BDB_KEY_EFUSED (1 << 0)
/* Indicate whether kernel data key is verified */
#define VBA_CONTEXT_FLAG_KERNEL_DATA_KEY_VERIFIED (1 << 1)
#endif

View File

@@ -0,0 +1,268 @@
/* Copyright (c) 2015 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.
*
* Boot descriptor block structures
*/
#ifndef VBOOT_REFERENCE_BDB_STRUCT_H_
#define VBOOT_REFERENCE_BDB_STRUCT_H_
#include <stdint.h>
/* Size of SHA256 digest in bytes */
#define BDB_SHA256_DIGEST_SIZE 32
/* Size of RSA4096 key data in bytes */
#define BDB_RSA4096_KEY_DATA_SIZE 1032
/* Size of RSA4096 signature in bytes */
#define BDB_RSA4096_SIG_SIZE 512
/* Size of ECDSA521 key data in bytes = ceil(521/8) * 2 */
#define BDB_ECDSA521_KEY_DATA_SIZE 132
/* Size of ECDSA521 signature in bytes = ceil(521/8) * 2 */
#define BDB_ECDSA521_SIG_SIZE 132
/* Size of RSA3072B key data in bytes */
#define BDB_RSA3072B_KEY_DATA_SIZE 776
/* Size of RSA3072B signature in bytes */
#define BDB_RSA3072B_SIG_SIZE 384
/*****************************************************************************/
/* Header for BDB */
/* Magic number for bdb_header.struct_magic */
#define BDB_HEADER_MAGIC 0x30426442
/* Current version of bdb_header struct */
#define BDB_HEADER_VERSION_MAJOR 1
#define BDB_HEADER_VERSION_MINOR 0
/* Expected size of bdb_header struct in bytes */
#define BDB_HEADER_EXPECTED_SIZE 32
struct bdb_header {
/* Magic number to identify struct = BDB_HEADER_MAGIC. */
uint32_t struct_magic;
/* Structure version = BDB_HEADER_VERSION{MAJOR,MINOR} */
uint8_t struct_major_version;
uint8_t struct_minor_version;
/* Size of structure in bytes */
uint16_t struct_size;
/* Recommended address in SP SRAM to load BDB. Set to -1 to use
* default address. */
uint64_t bdb_load_address;
/* Size of the entire BDB in bytes */
uint32_t bdb_size;
/* Number of bytes following the BDB key which are signed by the BDB
* header signature. */
uint32_t signed_size;
/* Size of OEM area 0 in bytes, or 0 if not present */
uint32_t oem_area_0_size;
/* Reserved; set 0 */
uint8_t reserved0[8];
} __attribute__((packed));
/*****************************************************************************/
/* Public key structure for BDB */
/* Magic number for bdb_key.struct_magic */
#define BDB_KEY_MAGIC 0x73334256
/* Current version of bdb_key struct */
#define BDB_KEY_VERSION_MAJOR 1
#define BDB_KEY_VERSION_MINOR 0
/* Supported hash algorithms */
enum bdb_hash_alg {
BDB_HASH_ALG_INVALID = 0, /* Not used; invalid */
BDB_HASH_ALG_SHA256 = 2, /* SHA-256 */
};
/* Supported signature algorithms */
enum bdb_sig_alg {
BDB_SIG_ALG_INVALID = 0, /* Not used; invalid */
BDB_SIG_ALG_RSA4096 = 3, /* RSA-4096, exponent 65537 */
BDB_SIG_ALG_ECSDSA521 = 5, /* ECDSA-521 */
BDB_SIG_ALG_RSA3072B = 7, /* RSA_3072, exponent 3 */
};
/*
* Expected size of bdb_key struct in bytes, not counting variable-length key
* data at end.
*/
#define BDB_KEY_EXPECTED_SIZE 80
struct bdb_key {
/* Magic number to identify struct = BDB_KEY_MAGIC. */
uint32_t struct_magic;
/* Structure version = BDB_KEY_VERSION{MAJOR,MINOR} */
uint8_t struct_major_version;
uint8_t struct_minor_version;
/* Size of structure in bytes, including variable-length key data */
uint16_t struct_size;
/* Hash algorithm (enum bdb_hash_alg) */
uint8_t hash_alg;
/* Signature algorithm (enum bdb_sig_alg) */
uint8_t sig_alg;
/* Reserved; set 0 */
uint8_t reserved0[2];
/* Key version */
uint32_t key_version;
/* Description; null-terminated ASCII */
char description[128];
/*
* Key data. Variable-length; size is struct_size -
* offset_of(bdb_key, key_data).
*/
uint8_t key_data[0];
} __attribute__((packed));
/*****************************************************************************/
/* Signature structure for BDB */
/* Magic number for bdb_sig.struct_magic */
#define BDB_SIG_MAGIC 0x6b334256
/* Current version of bdb_sig struct */
#define BDB_SIG_VERSION_MAJOR 1
#define BDB_SIG_VERSION_MINOR 0
struct bdb_sig {
/* Magic number to identify struct = BDB_SIG_MAGIC. */
uint32_t struct_magic;
/* Structure version = BDB_SIG_VERSION{MAJOR,MINOR} */
uint8_t struct_major_version;
uint8_t struct_minor_version;
/* Size of structure in bytes, including variable-length signature
* data. */
uint16_t struct_size;
/* Hash algorithm used for this signature (enum bdb_hash_alg) */
uint8_t hash_alg;
/* Signature algorithm (enum bdb_sig_alg) */
uint8_t sig_alg;
/* Reserved; set 0 */
uint8_t reserved0[2];
/* Number of bytes of data signed by this signature */
uint32_t signed_size;
/* Description; null-terminated ASCII */
char description[128];
/* Signature data. Variable-length; size is struct_size -
* offset_of(bdb_sig, sig_data). */
uint8_t sig_data[0];
} __attribute__((packed));
/*****************************************************************************/
/* Data structure for BDB */
/* Magic number for bdb_data.struct_magic */
#define BDB_DATA_MAGIC 0x31426442
/* Current version of bdb_sig struct */
#define BDB_DATA_VERSION_MAJOR 1
#define BDB_DATA_VERSION_MINOR 0
struct bdb_data {
/* Magic number to identify struct = BDB_DATA_MAGIC. */
uint32_t struct_magic;
/* Structure version = BDB_DATA_VERSION{MAJOR,MINOR} */
uint8_t struct_major_version;
uint8_t struct_minor_version;
/* Size of structure in bytes, NOT including hashes which follow. */
uint16_t struct_size;
/* Version of data (RW firmware) contained */
uint32_t data_version;
/* Size of OEM area 1 in bytes, or 0 if not present */
uint32_t oem_area_1_size;
/* Number of hashes which follow */
uint8_t num_hashes;
/* Size of each hash entry in bytes */
uint8_t hash_entry_size;
/* Reserved; set 0 */
uint8_t reserved0[2];
/* Number of bytes of data signed by the datakey, including this
* header */
uint32_t signed_size;
/* Reserved; set 0 */
uint8_t reserved1[8];
/* Description; null-terminated ASCII */
char description[128];
} __attribute__((packed));
/* Type of data for bdb_hash.type */
enum bdb_data_type {
/* Types of data for boot descriptor blocks */
BDB_DATA_SP_RW = 1, /* SP-RW firmware */
BDB_DATA_AP_RW = 2, /* AP-RW firmware */
BDB_DATA_MCU = 3, /* MCU firmware */
/* Types of data for kernel descriptor blocks */
BDB_DATA_KERNEL = 128, /* Kernel */
BDB_DATA_CMD_LINE = 129, /* Command line */
BDB_DATA_HEADER16 = 130, /* 16-bit vmlinuz header */
};
/* Hash entries which follow the structure */
struct bdb_hash {
/* Offset of data from start of partition */
uint64_t offset;
/* Size of data in bytes */
uint32_t size;
/* Partition number containing data */
uint8_t partition;
/* Type of data; enum bdb_data_type */
uint8_t type;
/* Reserved; set 0 */
uint8_t reserved0[2];
/* Address in RAM to load data. -1 means use default. */
uint64_t load_address;
/* SHA-256 hash digest */
uint8_t digest[BDB_SHA256_DIGEST_SIZE];
} __attribute__((packed));
/*****************************************************************************/
#endif /* VBOOT_REFERENCE_BDB_STRUCT_H_ */

View 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.
*/
/*
* C port of DumpPublicKey.java from the Android Open source project with
* support for additional RSA key sizes. (platform/system/core,git/libmincrypt
* /tools/DumpPublicKey.java). Uses the OpenSSL X509 and BIGNUM library.
*/
#include <openssl/pem.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
/*
* Command line tool to extract RSA public keys from X.509 certificates and
* output a pre-processed version of keys for use by RSA verification routines.
*/
int check(RSA *key)
{
int public_exponent = BN_get_word(key->e);
int modulus = BN_num_bits(key->n);
if (public_exponent != 65537 && public_exponent != 3) {
fprintf(stderr, "WARNING: Non-standard public exponent %d.\n",
public_exponent);
}
if (modulus != 1024 && modulus != 2048 && modulus != 3072 &&
modulus != 4096 && modulus != 8192) {
fprintf(stderr, "WARNING: Non-standard modulus length = %d.\n",
modulus);
}
return 1;
}
/**
* Pre-processes and outputs RSA public key to standard output.
*/
void output(RSA *key)
{
BIGNUM *N;
BIGNUM *Big1 = NULL, *Big2 = NULL, *Big32 = NULL, *BigMinus1 = NULL;
BIGNUM *B = NULL;
BIGNUM *N0inv= NULL, *R = NULL, *RR = NULL, *RRTemp = NULL;
BIGNUM *NnumBits = NULL;
BIGNUM *n = NULL, *rr = NULL;
BN_CTX *bn_ctx = BN_CTX_new();
uint32_t n0invout;
int nwords, i;
N = key->n;
/* Output size of RSA key in 32-bit words */
nwords = BN_num_bits(N) / 32;
if (-1 == write(1, &nwords, sizeof(nwords)))
goto failure;
/* Initialize BIGNUMs */
Big1 = BN_new();
Big2 = BN_new();
Big32 = BN_new();
BigMinus1 = BN_new();
N0inv= BN_new();
R = BN_new();
RR = BN_new();
RRTemp = BN_new();
NnumBits = BN_new();
n = BN_new();
rr = BN_new();
BN_set_word(Big1, 1L);
BN_set_word(Big2, 2L);
BN_set_word(Big32, 32L);
BN_sub(BigMinus1, Big1, Big2);
B = BN_new();
BN_exp(B, Big2, Big32, bn_ctx); /* B = 2^32 */
/* Calculate and output N0inv = -1 / N[0] mod 2^32 */
BN_mod_inverse(N0inv, N, B, bn_ctx);
BN_sub(N0inv, B, N0inv);
n0invout = BN_get_word(N0inv);
if (-1 == write(1, &n0invout, sizeof(n0invout)))
goto failure;
/* Calculate R = 2^(# of key bits) */
BN_set_word(NnumBits, BN_num_bits(N));
BN_exp(R, Big2, NnumBits, bn_ctx);
/* Calculate RR = R^2 mod N */
BN_copy(RR, R);
BN_mul(RRTemp, RR, R, bn_ctx);
BN_mod(RR, RRTemp, N, bn_ctx);
/* Write out modulus as little endian array of integers. */
for (i = 0; i < nwords; ++i) {
uint32_t nout;
BN_mod(n, N, B, bn_ctx); /* n = N mod B */
nout = BN_get_word(n);
if (-1 == write(1, &nout, sizeof(nout)))
goto failure;
BN_rshift(N, N, 32); /* N = N/B */
}
/* Write R^2 as little endian array of integers. */
for (i = 0; i < nwords; ++i) {
uint32_t rrout;
BN_mod(rr, RR, B, bn_ctx); /* rr = RR mod B */
rrout = BN_get_word(rr);
if (-1 == write(1, &rrout, sizeof(rrout)))
goto failure;
BN_rshift(RR, RR, 32); /* RR = RR/B */
}
failure:
/* Free BIGNUMs. */
BN_free(Big1);
BN_free(Big2);
BN_free(Big32);
BN_free(BigMinus1);
BN_free(N0inv);
BN_free(R);
BN_free(RRTemp);
BN_free(NnumBits);
BN_free(n);
BN_free(rr);
}
int main(int argc, char* argv[]) {
int cert_mode = 0;
FILE* fp;
X509* cert = NULL;
RSA* pubkey = NULL;
EVP_PKEY* key;
char *progname;
if (argc != 3 ||
(strcmp(argv[1], "-cert") && strcmp(argv[1], "-pub"))) {
progname = strrchr(argv[0], '/');
if (progname)
progname++;
else
progname = argv[0];
fprintf(stderr, "Usage: %s <-cert | -pub> <file>\n", progname);
return -1;
}
if (!strcmp(argv[1], "-cert"))
cert_mode = 1;
fp = fopen(argv[2], "r");
if (!fp) {
fprintf(stderr, "Couldn't open file %s!\n", argv[2]);
return -1;
}
if (cert_mode) {
/* Read the certificate */
if (!PEM_read_X509(fp, &cert, NULL, NULL)) {
fprintf(stderr, "Couldn't read certificate.\n");
goto fail;
}
/* Get the public key from the certificate. */
key = X509_get_pubkey(cert);
/* Convert to a RSA_style key. */
if (!(pubkey = EVP_PKEY_get1_RSA(key))) {
fprintf(stderr, "Couldn't convert to RSA style key.\n");
goto fail;
}
} else {
/* Read the pubkey in .PEM format. */
if (!(pubkey = PEM_read_RSA_PUBKEY(fp, NULL, NULL, NULL))) {
fprintf(stderr, "Couldn't read public key file.\n");
goto fail;
}
}
if (check(pubkey)) {
output(pubkey);
}
fail:
X509_free(cert);
RSA_free(pubkey);
fclose(fp);
return 0;
}

View File

@@ -0,0 +1,17 @@
/* Copyright (c) 2015 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.
*
* Boot descriptor block firmware ECDSA stub
*/
#include <string.h>
#include "bdb.h"
int bdb_ecdsa521_verify(const uint8_t *key_data,
const uint8_t *sig,
const uint8_t *digest)
{
/* This is just a stub */
return BDB_ERROR_DIGEST;
}

View File

@@ -0,0 +1,419 @@
/* Copyright 2015 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.
*
* Host functions for signing
*/
#include <unistd.h>
#include "2sysincludes.h"
#include "2common.h"
#include "2sha.h"
#include "bdb.h"
#include "host.h"
char *strzcpy(char *dest, const char *src, size_t size)
{
strncpy(dest, src, size);
dest[size - 1] = 0;
return dest;
}
uint8_t *read_file(const char *filename, uint32_t *size_ptr)
{
FILE *f;
uint8_t *buf;
long size;
*size_ptr = 0;
f = fopen(filename, "rb");
if (!f) {
fprintf(stderr, "Unable to open file %s\n", filename);
return NULL;
}
fseek(f, 0, SEEK_END);
size = ftell(f);
rewind(f);
if (size < 0 || size > UINT32_MAX) {
fclose(f);
return NULL;
}
buf = malloc(size);
if (!buf) {
fclose(f);
return NULL;
}
if (1 != fread(buf, size, 1, f)) {
fprintf(stderr, "Unable to read file %s\n", filename);
fclose(f);
free(buf);
return NULL;
}
fclose(f);
*size_ptr = size;
return buf;
}
int write_file(const char *filename, const void *buf, uint32_t size)
{
FILE *f = fopen(filename, "wb");
if (!f) {
fprintf(stderr, "Unable to open file %s\n", filename);
return 1;
}
if (1 != fwrite(buf, size, 1, f)) {
fprintf(stderr, "Unable to write to file %s\n", filename);
fclose(f);
unlink(filename); /* Delete any partial file */
return 1;
}
fclose(f);
return 0;
}
struct rsa_st *read_pem(const char *filename)
{
struct rsa_st *pem;
FILE *f;
/* Read private key */
f = fopen(filename, "rb");
if (!f) {
fprintf(stderr, "%s: unable to read key from %s\n",
__func__, filename);
return NULL;
}
pem = PEM_read_RSAPrivateKey(f, NULL, NULL, NULL);
fclose(f);
return pem;
}
struct bdb_key *bdb_create_key(const char *filename,
uint32_t key_version,
const char *desc)
{
uint32_t sig_alg;
size_t key_size = sizeof(struct bdb_key);
struct bdb_key *k;
uint8_t *kdata;
uint32_t kdata_size = 0;
/*
* Read key data. Somewhat lame assumption that we can determine the
* signature algorithm from the key size, but it's true right now.
*/
kdata = read_file(filename, &kdata_size);
if (kdata_size == BDB_RSA4096_KEY_DATA_SIZE) {
sig_alg = BDB_SIG_ALG_RSA4096;
} else if (kdata_size == BDB_RSA3072B_KEY_DATA_SIZE) {
sig_alg = BDB_SIG_ALG_RSA3072B;
} else {
fprintf(stderr, "%s: bad key size from %s\n",
__func__, filename);
free(kdata);
return NULL;
}
key_size += kdata_size;
/* Allocate buffer */
k = (struct bdb_key *)calloc(key_size, 1);
if (!k) {
free(kdata);
return NULL;
}
k->struct_magic = BDB_KEY_MAGIC;
k->struct_major_version = BDB_KEY_VERSION_MAJOR;
k->struct_minor_version = BDB_KEY_VERSION_MINOR;
k->struct_size = key_size;
k->hash_alg = BDB_HASH_ALG_SHA256;
k->sig_alg = sig_alg;
k->key_version = key_version;
/* Copy description, if any */
if (desc)
strzcpy(k->description, desc, sizeof(k->description));
/* Copy key data */
memcpy(k->key_data, kdata, kdata_size);
free(kdata);
return k;
}
struct bdb_sig *bdb_create_sig(const void *data,
size_t size,
struct rsa_st *key,
uint32_t sig_alg,
const char *desc)
{
static const uint8_t info[] = {
0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
0x00, 0x04, 0x20
};
size_t sig_size = sizeof(struct bdb_sig);
uint8_t digest[sizeof(info) + BDB_SHA256_DIGEST_SIZE];
struct bdb_sig *sig;
if (size >= UINT32_MAX)
return NULL;
switch(sig_alg) {
case BDB_SIG_ALG_RSA4096:
sig_size += BDB_RSA4096_SIG_SIZE;
break;
case BDB_SIG_ALG_RSA3072B:
sig_size += BDB_RSA3072B_SIG_SIZE;
break;
default:
fprintf(stderr, "%s: bad signature algorithm %d\n",
__func__, sig_alg);
return NULL;
}
/* Allocate buffer */
sig = (struct bdb_sig *)calloc(sig_size, 1);
if (!sig)
return NULL;
sig->struct_magic = BDB_SIG_MAGIC;
sig->struct_major_version = BDB_SIG_VERSION_MAJOR;
sig->struct_minor_version = BDB_SIG_VERSION_MINOR;
sig->struct_size = sig_size;
sig->hash_alg = BDB_HASH_ALG_SHA256;
sig->sig_alg = sig_alg;
sig->signed_size = size;
/* Copy description, if any */
if (desc)
strzcpy(sig->description, desc, sizeof(sig->description));
/* Calculate info-padded digest */
memcpy(digest, info, sizeof(info));
if (vb2_digest_buffer((uint8_t *)data, size,
VB2_HASH_SHA256,
digest + sizeof(info), BDB_SHA256_DIGEST_SIZE)) {
free(sig);
return NULL;
}
/* RSA-encrypt the signature */
if (RSA_private_encrypt(sizeof(digest),
digest,
sig->sig_data,
key,
RSA_PKCS1_PADDING) == -1) {
free(sig);
return NULL;
}
return sig;
}
int bdb_sign_datakey(uint8_t **bdb, struct rsa_st *key)
{
const struct bdb_header *header = bdb_get_header(*bdb);
const struct bdb_key *bdbkey = bdb_get_bdbkey(*bdb);
const void *oem = bdb_get_oem_area_0(*bdb);
const struct bdb_sig *sig = bdb_get_header_sig(*bdb);
struct bdb_sig *new_sig;
uint8_t *new_bdb, *src, *dst;
size_t len;
new_sig = bdb_create_sig(oem, header->signed_size,
key, bdbkey->sig_alg, NULL);
new_bdb = calloc(1, header->bdb_size
+ (new_sig->struct_size - sig->struct_size));
if (!new_bdb)
return BDB_ERROR_UNKNOWN;
/* copy up to sig */
src = *bdb;
dst = new_bdb;
len = bdb_offset_of_header_sig(*bdb);
memcpy(dst, src, len);
/* copy new sig */
src += len;
dst += len;
memcpy(dst, new_sig, new_sig->struct_size);
/* copy the rest */
src += sig->struct_size;
dst += new_sig->struct_size;
len = bdb_size_of(*bdb) - vb2_offset_of(*bdb, src);
memcpy(dst, src, len);
free(*bdb);
free(new_sig);
*bdb = new_bdb;
return BDB_SUCCESS;
}
int bdb_sign_data(uint8_t **bdb, struct rsa_st *key)
{
const struct bdb_key *datakey = bdb_get_datakey(*bdb);
const struct bdb_data *data = bdb_get_data(*bdb);
const uint64_t sig_offset = vb2_offset_of(*bdb, bdb_get_data_sig(*bdb));
struct bdb_sig *new_sig;
uint8_t *new_bdb;
new_sig = bdb_create_sig(data, data->signed_size,
key, datakey->sig_alg, NULL);
new_bdb = calloc(1, sig_offset + new_sig->struct_size);
if (!new_bdb)
return BDB_ERROR_UNKNOWN;
/* copy all data up to the data sig */
memcpy(new_bdb, *bdb, sig_offset);
/* copy the new signature */
memcpy(new_bdb + sig_offset, new_sig, new_sig->struct_size);
free(*bdb);
free(new_sig);
*bdb = new_bdb;
return BDB_SUCCESS;
}
struct bdb_header *bdb_create(struct bdb_create_params *p)
{
size_t bdb_size = 0;
size_t sig_size = sizeof(struct bdb_sig) + BDB_RSA4096_SIG_SIZE;
size_t hashes_size = sizeof(struct bdb_hash) * p->num_hashes;
uint8_t *buf, *bnext;
struct bdb_header *h;
struct bdb_sig *sig;
struct bdb_data *data;
const void *oem;
/* We can do some checks before we even allocate the buffer */
/* Make sure OEM sizes are aligned */
if ((p->oem_area_0_size & 3) || (p->oem_area_1_size & 3)) {
fprintf(stderr, "%s: OEM areas not 32-bit aligned\n",
__func__);
return NULL;
}
/* Hash count must fit in uint8_t */
if (p->num_hashes > 255) {
fprintf(stderr, "%s: too many hashes\n", __func__);
return NULL;
}
/* Calculate BDB size */
bdb_size = sizeof(struct bdb_header);
bdb_size += p->bdbkey->struct_size;
bdb_size += p->oem_area_0_size;
bdb_size += p->datakey->struct_size;
bdb_size += sig_size;
bdb_size += sizeof(struct bdb_data);
bdb_size += p->oem_area_1_size;
bdb_size += sizeof(struct bdb_hash) * p->num_hashes;
bdb_size += sig_size;
/* Make sure it fits */
if (bdb_size > UINT32_MAX) {
fprintf(stderr, "%s: BDB size > UINT32_MAX\n", __func__);
return NULL;
}
/* Allocate a buffer */
bnext = buf = calloc(bdb_size, 1);
if (!buf) {
fprintf(stderr, "%s: can't allocate buffer\n", __func__);
return NULL;
}
/* Fill in the header */
h = (struct bdb_header *)bnext;
h->struct_magic = BDB_HEADER_MAGIC;
h->struct_major_version = BDB_HEADER_VERSION_MAJOR;
h->struct_minor_version = BDB_HEADER_VERSION_MINOR;
h->struct_size = sizeof(*h);
h->bdb_load_address = p->bdb_load_address;
h->bdb_size = bdb_size;
h->signed_size = p->oem_area_0_size + p->datakey->struct_size;
h->oem_area_0_size = p->oem_area_0_size;
bnext += h->struct_size;
/* Copy BDB key */
memcpy(bnext, p->bdbkey, p->bdbkey->struct_size);
bnext += p->bdbkey->struct_size;
/* Copy OEM area 0 */
oem = bnext;
if (p->oem_area_0_size) {
memcpy(bnext, p->oem_area_0, p->oem_area_0_size);
bnext += p->oem_area_0_size;
}
/* Copy datakey */
memcpy(bnext, p->datakey, p->datakey->struct_size);
bnext += p->datakey->struct_size;
/*
* Create header signature using private BDB key.
*
* TODO: create the header signature in a totally separate step. That
* way, the private BDB key is not required each time a BDB is created.
*/
sig = bdb_create_sig(oem, h->signed_size, p->private_bdbkey,
p->bdbkey->sig_alg, p->header_sig_description);
memcpy(bnext, sig, sig->struct_size);
bnext += sig->struct_size;
/* Fill in the data */
data = (struct bdb_data *)bnext;
data->struct_magic = BDB_DATA_MAGIC;
data->struct_major_version = BDB_DATA_VERSION_MAJOR;
data->struct_minor_version = BDB_DATA_VERSION_MINOR;
data->struct_size = sizeof(struct bdb_data);
data->data_version = p->data_version;
data->oem_area_1_size = p->oem_area_1_size;
data->num_hashes = p->num_hashes;
data->hash_entry_size = sizeof(struct bdb_hash);
data->signed_size = data->struct_size + data->oem_area_1_size +
hashes_size;
if (p->data_description) {
strzcpy(data->description, p->data_description,
sizeof(data->description));
}
bnext += data->struct_size;
/* Copy OEM area 1 */
oem = bnext;
if (p->oem_area_1_size) {
memcpy(bnext, p->oem_area_1, p->oem_area_1_size);
bnext += p->oem_area_1_size;
}
/* Copy hashes */
memcpy(bnext, p->hash, hashes_size);
bnext += hashes_size;
/* Create data signature using private datakey */
sig = bdb_create_sig(data, data->signed_size, p->private_datakey,
p->datakey->sig_alg, p->data_sig_description);
memcpy(bnext, sig, sig->struct_size);
/* Return the BDB */
return h;
}

Some files were not shown because too many files have changed in this diff Show More