summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Zeuthen <zeuthen@google.com>2016-03-02 17:14:30 -0500
committerDavid Zeuthen <zeuthen@google.com>2016-03-09 16:44:21 -0500
commitf67c41ede7b456270494ac7607692c3a76f7c67d (patch)
treeb74d47473068f63dde49ee158b958ea5891f43be
parent5987e57e2dc0aa432fd41fd19668949e8787de82 (diff)
downloadbvb-f67c41ede7b456270494ac7607692c3a76f7c67d.tar.gz
Initial commit of Brillo Verified Boot.
This directory contains - The specification for the Brillo Verified Boot boot image format; and - A reference implementation (intended for use in boot loaders); and - tooling for generating and signing boot images; and - tooling for using dm-verity on the rootfs; and - unit tests for the above code. See the README file for more details. The code in the files bvb_rsa.[ch], bvb_sha*.[ch], bvb_sysdeps*.[ch] is based on code from the ChromiumOS vboot_reference project. BUG=26184998 BUG=26186855 TEST=Unit tests pass. Change-Id: Ie7fa495238e422d71e49e890eb8c58d99cf038b8
-rw-r--r--Android.mk80
-rw-r--r--README146
-rw-r--r--bvb_boot_image_header.h298
-rw-r--r--bvb_property.c176
-rw-r--r--bvb_property.h65
-rw-r--r--bvb_rsa.c276
-rw-r--r--bvb_rsa.h61
-rw-r--r--bvb_sha.h78
-rw-r--r--bvb_sha256.c310
-rw-r--r--bvb_sha512.c333
-rw-r--r--bvb_sysdeps.h122
-rw-r--r--bvb_sysdeps_stub.c84
-rw-r--r--bvb_unittest_util.h105
-rw-r--r--bvb_util.c114
-rw-r--r--bvb_util.h65
-rw-r--r--bvb_util_unittest.cc138
-rw-r--r--bvb_verify.c302
-rw-r--r--bvb_verify.h98
-rw-r--r--bvb_verify_unittest.cc469
-rwxr-xr-xbvbtool1083
-rw-r--r--bvbtool_unittest.cc406
-rw-r--r--test/dummy_initrd.binbin0 -> 131072 bytes
-rw-r--r--test/dummy_kernel.binbin0 -> 65536 bytes
-rw-r--r--test/dummy_rootfs.binbin0 -> 1049600 bytes
-rw-r--r--test/small_blob.binbin0 -> 21 bytes
-rw-r--r--test/testkey_rsa2048.pem27
-rw-r--r--test/testkey_rsa4096.pem51
-rw-r--r--test/testkey_rsa8192.pem99
28 files changed, 4986 insertions, 0 deletions
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..51800e5
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,80 @@
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(my-dir)
+
+bvb_common_cflags := \
+ -D_FILE_OFFSET_BITS=64 \
+ -D_POSIX_C_SOURCE=199309L \
+ -Wa,--noexecstack \
+ -Werror \
+ -Wall \
+ -Wextra \
+ -Wformat=2 \
+ -Wno-psabi \
+ -Wno-unused-parameter \
+ -ffunction-sections \
+ -fstack-protector-strong \
+ -fvisibility=hidden
+bvb_common_cppflags := \
+ -Wnon-virtual-dtor \
+ -fno-strict-aliasing
+bvb_common_ldflags := \
+ -Wl,--gc-sections
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := bvbtool
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_IS_HOST_MODULE := true
+LOCAL_MODULE := bvbtool
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libbvb_refimpl
+LOCAL_MODULE_HOST_OS := linux
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+LOCAL_CLANG := true
+LOCAL_CFLAGS := $(bvb_common_cflags) -DBVB_ENABLE_DEBUG
+LOCAL_LDFLAGS := $(bvb_common_ldflags)
+LOCAL_C_INCLUDES :=
+LOCAL_SRC_FILES := \
+ bvb_property.c \
+ bvb_rsa.c \
+ bvb_sha256.c \
+ bvb_sha512.c \
+ bvb_sysdeps_stub.c \
+ bvb_util.c \
+ bvb_verify.c
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libbvb_refimpl_unittest
+LOCAL_MODULE_HOST_OS := linux
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_CLANG := true
+LOCAL_CFLAGS := $(bvb_common_cflags)
+LOCAL_CPPFLAGS := $(bvb_common_cppflags)
+LOCAL_LDFLAGS := $(bvb_common_ldflags)
+LOCAL_C_INCLUDES := external/gtest/include
+LOCAL_STATIC_LIBRARIES := \
+ libbvb_refimpl \
+ libgmock_host \
+ libgtest_host
+LOCAL_SHARED_LIBRARIES := \
+ libchrome
+LOCAL_SRC_FILES := bvb_util_unittest.cc bvb_verify_unittest.cc bvbtool_unittest.cc
+LOCAL_LDLIBS_linux := -lrt
+include $(BUILD_HOST_NATIVE_TEST)
diff --git a/README b/README
new file mode 100644
index 0000000..6b40c26
--- /dev/null
+++ b/README
@@ -0,0 +1,146 @@
+This directory contains the specification for the Brillo Verified Boot
+boot image, a reference implementation for verified boot and A/B
+selection to be used in boot loaders, and a tool for generating and
+signing boot images.
+
+-- FILES AND DIRECTORIES
+
+ bvb_boot_image.h
+
+ The specification for a Brillo Boot Image as a C header file.
+
+ bvb_verify.[ch]
+
+ Code for cryptographically verifying a Brillo Boot Image.
+
+ bvb_property.[ch]
+
+ Code for looking up properties (key/value pairs) embedded in the
+ boot image.
+
+ bvb_sha.h, bvb_sha{256, 512}.c
+
+ Code for calculating SHA-256 and SHA-512.
+
+ bvb_rsa.h, bvb_rsa.c
+
+ Code for verifying RSA signatures.
+
+ bvb_util.[ch]
+
+ Various utility code.
+
+ bvb_sysdeps.h
+
+ Declarations of system dependencies expected to be provided by the
+ platform the reference implementation is used by. If the platform
+ provides the standard C runtime you can use bvb_sysdeps_stub.c.
+
+ bvb_sysdeps_stub.c
+
+ An implementation of system dependencies for the standard C runtime.
+
+ Android.mk
+
+ Build instructions for building the reference implementation and
+ associated unit tests.
+
+ bvb_*_unittest.cc
+
+ Unit tests for the reference implementation.
+
+ test/
+
+ Contains test data used in unit tests.
+
+ bvbtool
+
+ A tool written in Python for working with Brillo boot images.
+
+-- AUDIENCE AND PORTABILITY NOTES
+
+This code is intended to be used in bootloaders in devices running
+Brillo. The suggested approach is to copy the appropriate header and C
+files mentioned in the previous section into the boot loader and
+integrate as appropriate.
+
+The reference implementation will evolve over time so integration
+should be as non-invasive as possible. The intention is to keep the
+API of the reference implementation stable however it will be broken
+if necessary.
+
+As for portability, the reference implementation is intended to be
+highly portable, work on both little- and big-endian architectures and
+32- and 64-bit. It's also intended to work in non-standard
+environments without the standard C library and runtime.
+
+If the BVB_ENABLE_DEBUG preprocessor symbol is set, the code will
+include useful debug information and run-time checks. Production
+builds should not use this.
+
+-- COMPATIBILITY NOTES
+
+The Brillo Boot Image structure (as defined in bvb_boot_image.h)
+guarantees forwards- and backwards-compatibility provided the major
+version does not change.
+
+When backwards-compatible changes are made - for example, when a new
+field is added to BvbBootImageHeader - the minor version will be
+bumped. At the same time, the reference implementation will also be
+modified to test for the appropriate minor version before attempting
+to access the newly added field. This ensures that version 1.N of the
+reference implementation is able to read an old boot image header
+produced with version 1.M where N > M.
+
+The usual scenario is that the code parsing the BvbBootImageHeader
+rarely changes (it's usually in the firmware of a device and this
+firmware is rarely updated if ever), let's say it's fixed at version
+1.N. At the same time, the version of the bvbtool used to produce the
+boot image is rolling forward and is at version 1.M where M > N. The
+problem with this scenario is that version 1.M may easily and
+inadvertently introduce a seemingly compatible change that isn't. For
+example, consider if a new verification algorithm is added - in this
+case version 1.N of the reference implementation will fail at
+verification time when validating the |algorithm_field| of a 1.M image
+if it's set to the new algorithm.
+
+The best approach for dealing with this problem is to always used a
+pinned version of bvbtool (say, use version 1.N to generate images
+targeted for devices running version 1.N) for generating and signing
+images but sometimes this is not always possible nor
+desirable. Therefore, to avoid this compatibility problem, bvbtool is
+designed to always take such input as a command-line argument so it
+can be kept constant by the caller. In other words, as long as you
+keep your command-line options passed to the bvb tool the same, images
+produced by newer versions of bvb will continue to work on the same
+version of the reference implementation.
+
+-- BUILD SYSTEM INTEGRATION NOTES
+
+Brillo Verified Boot is enabled by the BOARD_BVB_ENABLE variable
+
+ BOARD_BVB_ENABLE := true
+
+By default, the algorithm SHA256_RSA4096 is used with a test key from
+this directory. This can be overriden by the BOARD_BVB_ALGORITHM and
+BOARD_BVB_KEY_PATH variables to use e.g. RSA-4096:
+
+ BOARD_BVB_ALGORITHM := SHA512_RSA4096
+ BOARD_BVB_KEY_PATH := /path/to/rsa_key_4096bits.pem
+
+Remember that the public part of this key needs to be embedded in the
+bootloader of the device expected to process resulting images. Use
+'bvbtool extract_public_key' to do this.
+
+To prevent rollback attakcs, the rollback index should be increased on
+a regular basis. The rollback index can be set with the
+BOARD_BVB_ROLLBACK_INDEX variable:
+
+ BOARD_BVB_ROLLBACK_INDEX := 5
+
+If this is not set, the rollback index defaults to 0.
+
+Additionally, the variables BOARD_BVB_MAKE_BOOT_IMAGE_ARGS and
+BOARD_BVB_SIGN_BOOT_IMAGE_ARGS can be used to specify additional
+options passed to respectively 'bvbtool make_boot_image' and 'bvbtool
+sign_boot_image'.
diff --git a/bvb_boot_image_header.h b/bvb_boot_image_header.h
new file mode 100644
index 0000000..69a2174
--- /dev/null
+++ b/bvb_boot_image_header.h
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef BVB_BOOT_IMAGE_HEADER_H_
+#define BVB_BOOT_IMAGE_HEADER_H_
+
+#include "bvb_sysdeps.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Size of the Brillo boot image header. */
+#define BVB_BOOT_IMAGE_HEADER_SIZE 8192
+
+/* Magic for the Brillo boot image header. */
+#define BVB_MAGIC "BVB0"
+#define BVB_MAGIC_LEN 4
+
+/* The current MAJOR and MINOR versions used - keep in sync with bvbtool. */
+#define BVB_MAJOR_VERSION 1
+#define BVB_MINOR_VERSION 0
+
+/* Maximum number of bytes in the kernel command-line before substitution. */
+#define BVB_KERNEL_CMDLINE_MAX_LEN 4096
+
+/* Algorithms that can be used in the Brillo boot image for
+ * verification. An algorithm consists of a hash type and a signature
+ * type.
+ *
+ * The data used to calculate the hash is the four blocks mentioned in
+ * the documentation for |BvbBootImageHeader| except for the data in
+ * the "Authentication data" block.
+ *
+ * For signatures with RSA keys, PKCS v1.5 padding is used. The public
+ * key data is stored in the auxilary data block, see
+ * |BvbRSAPublicKeyHeader| for the serialization format.
+ *
+ * Each algorithm type is described below:
+ *
+ * BVB_ALGORITHM_TYPE_NONE: There is no hash, no signature of the
+ * data, and no public key. The data cannot be verified. The fields
+ * |hash_size|, |signature_size|, and |public_key_size| must be zero.
+ *
+ * BVB_ALGORITHM_TYPE_SHA256_RSA2048: The hash function used is
+ * SHA-256, resulting in 32 bytes of hash digest data. This hash is
+ * signed with a 2048-bit RSA key. The field |hash_size| must be 32,
+ * |signature_size| must be 256, and the public key data must have
+ * |key_num_bits| set to 2048.
+ *
+ * BVB_ALGORITHM_TYPE_SHA256_RSA4096: Like above, but only with
+ * a 4096-bit RSA key and |signature_size| set to 512.
+ *
+ * BVB_ALGORITHM_TYPE_SHA256_RSA8192: Like above, but only with
+ * a 8192-bit RSA key and |signature_size| set to 1024.
+ *
+ * BVB_ALGORITHM_TYPE_SHA512_RSA2048: The hash function used is
+ * SHA-512, resulting in 64 bytes of hash digest data. This hash is
+ * signed with a 2048-bit RSA key. The field |hash_size| must be 64,
+ * |signature_size| must be 256, and the public key data must have
+ * |key_num_bits| set to 2048.
+ *
+ * BVB_ALGORITHM_TYPE_SHA512_RSA4096: Like above, but only with
+ * a 4096-bit RSA key and |signature_size| set to 512.
+ *
+ * BVB_ALGORITHM_TYPE_SHA512_RSA8192: Like above, but only with
+ * a 8192-bit RSA key and |signature_size| set to 1024.
+ */
+typedef enum {
+ BVB_ALGORITHM_TYPE_NONE,
+ BVB_ALGORITHM_TYPE_SHA256_RSA2048,
+ BVB_ALGORITHM_TYPE_SHA256_RSA4096,
+ BVB_ALGORITHM_TYPE_SHA256_RSA8192,
+ BVB_ALGORITHM_TYPE_SHA512_RSA2048,
+ BVB_ALGORITHM_TYPE_SHA512_RSA4096,
+ BVB_ALGORITHM_TYPE_SHA512_RSA8192,
+ _BVB_ALGORITHM_NUM_TYPES
+} BvbAlgorithmType;
+
+/* The header for a serialized RSA public key.
+ *
+ * The size of the key is given by |key_num_bits|, for example 2048
+ * for a RSA-2048 key. By definition, a RSA public key is the pair (n,
+ * e) where |n| is the modulus (which can be represented in
+ * |key_num_bits| bits) and |e| is the public exponent. The exponent
+ * is not stored since it's assumed to always be 65537.
+ *
+ * To optimize verification, the key block includes two precomputed
+ * values, |n0inv| (fits in 32 bits) and |rr| and can always be
+ * represented in |key_num_bits|.
+
+ * The value |n0inv| is the value -1/n[0] (mod 2^32). The value |rr|
+ * is (2^key_num_bits)^2 (mod n).
+ *
+ * Following this header is |key_num_bits| bits of |n|, then
+ * |key_num_bits| bits of |rr|. Both values are stored with most
+ * significant bit first. Each serialized number takes up
+ * |key_num_bits|/8 bytes.
+ *
+ * All fields in this struct are stored in network byte order when
+ * serialized. To generate a copy with fields swapped to native byte
+ * order, use the function bvb_rsa_public_key_header_to_host_byte_order().
+ *
+ * The bvb_RSAVerify() function expects a key in this serialized
+ * format.
+ *
+ * The 'bvbtool extract_public_key' command can be used to generate a
+ * serialized RSA public key.
+ */
+typedef struct BvbRSAPublicKeyHeader {
+ uint32_t key_num_bits;
+ uint32_t n0inv;
+} __attribute__((packed)) BvbRSAPublicKeyHeader;
+
+/* The header for a serialized property.
+ *
+ * Following this header is |key_num_bytes| bytes of key data,
+ * followed by a NUL byte, then |value_num_bytes| bytes of value data,
+ * followed by a NUL byte and then enough padding to make the combined
+ * size a multiple of 8.
+ *
+ * Headers with keys beginning with "brillo." are reserved for use in
+ * the Brillo project and must not be used by others. Well-known
+ * headers include
+ *
+ * brillo.device_tree: The property value is a device-tree blob.
+ */
+typedef struct BvbPropertyHeader {
+ uint64_t key_num_bytes;
+ uint64_t value_num_bytes;
+} __attribute__((packed)) BvbPropertyHeader;
+
+/* Binary format for header of the boot image used in Brillo.
+ *
+ * The Brillo boot image consists of four blocks:
+ *
+ * +-----------------------------------------+
+ * | Header data - fixed size |
+ * +-----------------------------------------+
+ * | Authentication data - variable size |
+ * +-----------------------------------------+
+ * | Auxilary data - variable size |
+ * +-----------------------------------------+
+ * | Payload data - variable size |
+ * +-----------------------------------------+
+ *
+ * The "Header data" block is described by this struct and is always
+ * |BVB_BOOT_IMAGE_HEADER_SIZE| bytes long.
+ *
+ * The "Authentication data" block is |authentication_data_block_size|
+ * bytes long and contains the hash and signature used to authenticate
+ * the boot image. The type of the hash and signature is defined by
+ * the |algorithm_type| field.
+ *
+ * The "Auxilary data" is |auxilary_data_block_size| bytes long and
+ * contains the auxilary data including the public key used to make
+ * the signature and properties.
+ *
+ * The public key is at offset |public_key_offset| with size
+ * |public_key_size| in this block. The size of the public key data is
+ * defined by the |algorithm_type| field. The format of the public key
+ * data is described in the |BvbRSAPublicKeyHeader| struct.
+ *
+ * The properties starts at |properties_offset| from the beginning of
+ * the "Auxliary Data" block and take up |properties_size| bytes. Each
+ * property is stored as |BvbPropertyHeader| with key, NUL, value,
+ * NUL, and padding following. The number of properties can be
+ * determined by walking this data until |properties_size| is
+ * exhausted.
+ *
+ * The "Payload data" block is |payload_data_block_size| bytes
+ * long. This is where the kernel, initramfs, and other data is
+ * stored.
+ *
+ * The size of each of the "Authentication data" and "Auxilary data"
+ * blocks must be divisible by 64. This is to ensure proper alignment.
+ *
+ * Properties are free-form key/value pairs stored in a part of the
+ * boot partition subject to the same integrity checks as the rest of
+ * the boot partition. See the documentation for |BvbPropertyHeader|
+ * for well-known properties. See bvb_property_lookup() and
+ * bvb_property_lookup_uint64() for convenience functions to look up
+ * property values.
+ *
+ * This struct is versioned, see the |header_version_major| and
+ * |header_version_minor| fields. Compatibility is guaranteed only
+ * within the same major version.
+ *
+ * All fields are stored in network byte order when serialized. To
+ * generate a copy with fields swapped to native byte order, use the
+ * function bvb_boot_image_header_to_host_byte_order().
+ *
+ * Before reading and/or using any of this data, you MUST verify it
+ * using bvb_verify_boot_image() and reject it unless it's signed by a
+ * known good public key.
+ */
+typedef struct BvbBootImageHeader {
+ /* 0: Four bytes equal to "BVB0" (BVB_MAGIC). */
+ uint8_t magic[BVB_MAGIC_LEN];
+ /* 4: The major version of the boot image header. */
+ uint32_t header_version_major;
+ /* 8: The minor version of the boot image header. */
+ uint32_t header_version_minor;
+
+ /* 12: The size of the signature block. */
+ uint64_t authentication_data_block_size;
+ /* 20: The size of the public key block. */
+ uint64_t auxilary_data_block_size;
+ /* 28: The size of the payload block. */
+ uint64_t payload_data_block_size;
+
+ /* 36: The verification algorithm used, see |BvbAlgorithmType| enum. */
+ uint32_t algorithm_type;
+
+ /* 40: Offset into the "Authentication data" block of hash data. */
+ uint64_t hash_offset;
+ /* 48: Length of the hash data. */
+ uint64_t hash_size;
+
+ /* 56: Offset into the "Authentication data" block of signature data. */
+ uint64_t signature_offset;
+ /* 64: Length of the signature data. */
+ uint64_t signature_size;
+
+ /* 72: Offset into the "Auxilary data" block of public key data. */
+ uint64_t public_key_offset;
+ /* 80: Length of the public key data. */
+ uint64_t public_key_size;
+
+ /* 88: Offset into the "Auxilary data" block of property data. */
+ uint64_t properties_offset;
+ /* 96: Length of property data. */
+ uint64_t properties_size;
+
+ /* 104: The rollback index which can be used to prevent rollback to
+ * older versions.
+ */
+ uint64_t rollback_index;
+
+ /* 112: Offset into the "Payload data" block of kernel image. */
+ uint64_t kernel_offset;
+ /* 120: Length of the kernel image. */
+ uint64_t kernel_size;
+
+ /* 128: Offset into the "Payload data" block of initial ramdisk. */
+ uint64_t initrd_offset;
+ /* 136: Length of the initial ramdisk. */
+ uint64_t initrd_size;
+
+ /* 144: Physical kernel load address. */
+ uint64_t kernel_addr;
+
+ /* 152: Physical initial ramdisk load address. */
+ uint64_t initrd_addr;
+
+ /* 160: The NUL-terminated kernel command-line string, passed to the
+ * Linux kernel.
+ *
+ * Limited substitution will be performed by the boot loader - the
+ * following variables are supported:
+ *
+ * $(ANDROID_SYSTEM_PARTUUID) - this place-holder will be replaced
+ * with the image UUID/GUID of the corresponding system_X image of
+ * the booted slot (where _X is the slot to boot).
+ *
+ * $(ANDROID_BOOT_PARTUUID) - this place-holder will be replaced
+ * with the image UUID/GUID of the boot image of the booted slot.
+ *
+ * For example, the snippet "root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)"
+ * in this field can be used to instruct the Linux kernel to use the
+ * system image in the booted slot as the root filesystem.
+ */
+ uint8_t kernel_cmdline[BVB_KERNEL_CMDLINE_MAX_LEN];
+
+ /* 4256: Padding to ensure struct is size BVB_BOOT_IMAGE_HEADER_SIZE
+ * bytes. This must be set to zeroes.
+ */
+ uint8_t reserved[3936];
+} __attribute__((packed)) BvbBootImageHeader;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BVB_BOOT_IMAGE_HEADER_H_ */
diff --git a/bvb_property.c b/bvb_property.c
new file mode 100644
index 0000000..8b3ac3d
--- /dev/null
+++ b/bvb_property.c
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "bvb_boot_image_header.h"
+#include "bvb_util.h"
+
+const char* bvb_lookup_property(const uint8_t* image_data, size_t image_size,
+ const char* key, size_t key_size,
+ size_t* out_value_size) {
+ const BvbBootImageHeader *header = NULL;
+ const char* ret = NULL;
+ const uint8_t* image_end;
+ const uint8_t* prop_start;
+ const uint8_t* prop_end;
+ const uint8_t* p;
+
+ if (out_value_size != NULL)
+ *out_value_size = 0;
+
+ if (image_data == NULL) {
+ bvb_debug("image_data is NULL\n.");
+ goto out;
+ }
+
+ if (key == NULL) {
+ bvb_debug("key is NULL\n.");
+ goto out;
+ }
+
+ if (image_size < sizeof(BvbBootImageHeader)) {
+ bvb_debug("Length is smaller than header.\n");
+ goto out;
+ }
+
+ // Ensure magic is correct.
+ if (bvb_memcmp(image_data, BVB_MAGIC, BVB_MAGIC_LEN) != 0) {
+ bvb_debug("Magic is incorrect.\n");
+ goto out;
+ }
+
+ if (key_size == 0)
+ key_size = bvb_strlen(key);
+
+ // Careful, not byteswapped - also ensure it's aligned properly.
+ bvb_assert_word_aligned(image_data);
+ header = (const BvbBootImageHeader *) image_data;
+ image_end = image_data + image_size;
+
+ prop_start = image_data + sizeof(BvbBootImageHeader) +
+ bvb_be64toh(header->authentication_data_block_size) +
+ bvb_be64toh(header->properties_offset);
+
+ prop_end = prop_start + bvb_be64toh(header->properties_size);
+
+ if (prop_start < image_data || prop_start > image_end ||
+ prop_end < image_data || prop_end > image_end ||
+ prop_end < prop_start) {
+ bvb_debug("Properties not inside passed-in data.\n");
+ goto out;
+ }
+
+ for (p = prop_start; p < prop_end; ) {
+ const BvbPropertyHeader *ph = (const BvbPropertyHeader *) p;
+ bvb_assert_word_aligned(ph);
+ uint64_t key_nb = bvb_be64toh(ph->key_num_bytes);
+ uint64_t value_nb = bvb_be64toh(ph->value_num_bytes);
+ uint64_t total = sizeof(BvbPropertyHeader) + 2 /* NUL bytes */
+ + key_nb + value_nb;
+ uint64_t remainder = total % 8;
+
+ if (remainder != 0)
+ total += 8 - remainder;
+
+ if (total + p < prop_start || total + p > prop_end) {
+ bvb_debug("Invalid data in properties array.\n");
+ goto out;
+ }
+ if (p[sizeof(BvbPropertyHeader) + key_nb] != 0) {
+ bvb_debug("No terminating NUL byte in key.\n");
+ goto out;
+ }
+ if (p[sizeof(BvbPropertyHeader) + key_nb + 1 + value_nb] != 0) {
+ bvb_debug("No terminating NUL byte in value.\n");
+ goto out;
+ }
+ if (key_size == key_nb) {
+ if (bvb_memcmp(p + sizeof(BvbPropertyHeader), key, key_size) == 0) {
+ ret = (const char *) (p + sizeof(BvbPropertyHeader) + key_nb + 1);
+ if (out_value_size != NULL)
+ *out_value_size = value_nb;
+ goto out;
+ }
+ }
+ p += total;
+ }
+
+out:
+ return ret;
+}
+
+int bvb_lookup_property_uint64(const uint8_t* image_data, size_t image_size,
+ const char* key, size_t key_size,
+ uint64_t* out_value) {
+ const char *value;
+ int ret = 0;
+ uint64_t parsed_val;
+ int base;
+ int n;
+
+ value = bvb_lookup_property(image_data, image_size, key, key_size, NULL);
+ if (value == NULL)
+ goto out;
+
+ base = 10;
+ if (bvb_memcmp(value, "0x", 2) == 0) {
+ base = 16;
+ value += 2;
+ }
+
+ parsed_val = 0;
+ for (n = 0; value[n] != '\0'; n++) {
+ int c = value[n];
+ int digit;
+
+ parsed_val *= base;
+
+ switch (base) {
+ case 10:
+ if (c >= '0' && c <= '9') {
+ digit = c - '0';
+ } else {
+ bvb_debug("Invalid digit.\n");
+ goto out;
+ }
+ break;
+
+ case 16:
+ if (c >= '0' && c <= '9') {
+ digit = c - '0';
+ } else if (c >= 'a' && c <= 'f') {
+ digit = c - 'a' + 10;
+ } else if (c >= 'A' && c <= 'F') {
+ digit = c - 'A' + 10;
+ } else {
+ bvb_debug("Invalid digit.\n");
+ goto out;
+ }
+ break;
+
+ default:
+ goto out;
+ }
+
+ parsed_val += digit;
+ }
+
+ ret = 1;
+ if (out_value != NULL)
+ *out_value = parsed_val;
+
+out:
+ return ret;
+}
diff --git a/bvb_property.h b/bvb_property.h
new file mode 100644
index 0000000..94b5b28
--- /dev/null
+++ b/bvb_property.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef BVB_PROPERTY_H_
+#define BVB_PROPERTY_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "bvb_boot_image_header.h"
+
+/* Convenience function for looking up the value for a property with
+ * name |key| in a Brillo boot image. If |key| is NUL-terminated,
+ * |key_size| may be set to 0.
+ *
+ * The |image_data| parameter must be a pointer to a Brillo Boot Image
+ * of size |image_size|.
+ *
+ * This function returns a pointer to the value inside the passed-in
+ * image or NULL if not found. Note that the value is always
+ * guaranteed to be followed by a NUL byte.
+ *
+ * If the value was found and |out_value_size| is not NULL, the size
+ * of the value is returned there.
+ *
+ * This function is O(n) in number of properties so if you need to
+ * look up a lot of values, you may want to build a more efficient
+ * lookup-table by manually walking all properties yourself.
+ *
+ * Before using this function, you MUST verify |image_data| with
+ * bvb_verify_boot_image() and reject it unless it's signed by a known
+ * good public key.
+ */
+const char* bvb_lookup_property(const uint8_t* image_data, size_t image_size,
+ const char* key, size_t key_size,
+ size_t* out_value_size);
+
+/* Like bvb_lookup_property() but parses the value as an unsigned
+ * 64-bit integer. Both decimal and hexadecimal representations
+ * (e.g. "0x2a") are supported. Returns 0 on failure and non-zero on
+ * success. On success, the parsed value is returned in |out_value|.
+ */
+int bvb_lookup_property_uint64(const uint8_t* image_data, size_t image_size,
+ const char* key, size_t key_size,
+ uint64_t* out_value);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BVB_PROPERTY_H_ */
diff --git a/bvb_rsa.c b/bvb_rsa.c
new file mode 100644
index 0000000..50517b8
--- /dev/null
+++ b/bvb_rsa.c
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Copyright (c) 2011 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 "bvb_rsa.h"
+#include "bvb_sha.h"
+#include "bvb_util.h"
+
+typedef struct Key {
+ unsigned int len; /* Length of n[] in number of uint32_t */
+ uint32_t n0inv; /* -1 / n[0] mod 2^32 */
+ uint32_t* n; /* modulus as little endian array */
+ uint32_t* rr; /* R^2 as little endian array */
+} Key;
+
+Key* parse_key_data(const uint8_t* data, size_t length) {
+ BvbRSAPublicKeyHeader h;
+ Key* key = NULL;
+ size_t expected_length;
+ unsigned int i;
+ const uint8_t* n;
+ const uint8_t *rr;
+
+ bvb_rsa_public_key_header_to_host_byte_order(
+ (const BvbRSAPublicKeyHeader *) data, &h);
+
+ if (!(h.key_num_bits == 2048 ||
+ h.key_num_bits == 4096 ||
+ h.key_num_bits == 8192)) {
+ bvb_debug("Unexpected key length.\n");
+ goto fail;
+ }
+
+ expected_length = sizeof(BvbRSAPublicKeyHeader) + 2*h.key_num_bits/8;
+ if (length != expected_length) {
+ bvb_debug("Key does not match expected length.\n");
+ goto fail;
+ }
+
+ n = data + sizeof(BvbRSAPublicKeyHeader);
+ rr = data + sizeof(BvbRSAPublicKeyHeader) + h.key_num_bits/8;
+
+ // Store n and rr following the key header so we only have to do one
+ // allocation.
+ key = (Key *) (bvb_malloc(sizeof(Key) + 2*h.key_num_bits/8));
+ if (key == NULL)
+ goto fail;
+
+ key->len = h.key_num_bits/32;
+ key->n0inv = h.n0inv;
+ key->n = (uint32_t *) (key + 1); // Skip ahead sizeof(Key) bytes.
+ key->rr = key->n + key->len;
+
+ // Crypto-code below (modpowF4() and friends) expects the key in
+ // little-endian format (rather than the format we're storing the
+ // key in), so convert it.
+ for (i = 0; i < key->len; i++) {
+ key->n[i] = bvb_be32toh(((uint32_t *) n)[key->len - i - 1]);
+ key->rr[i] = bvb_be32toh(((uint32_t *) rr)[key->len - i - 1]);
+ }
+ return key;
+
+fail:
+ if (key != NULL)
+ bvb_free(key);
+ return NULL;
+}
+
+void free_parsed_key(Key* key) {
+ bvb_free(key);
+}
+
+/* a[] -= mod */
+static void subM(const Key* key, uint32_t* a) {
+ int64_t A = 0;
+ uint32_t i;
+ for (i = 0; i < key->len; ++i) {
+ A += (uint64_t)a[i] - key->n[i];
+ a[i] = (uint32_t)A;
+ A >>= 32;
+ }
+}
+
+/* return a[] >= mod */
+static int geM(const Key* key, uint32_t *a) {
+ uint32_t i;
+ for (i = key->len; 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 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->len; ++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 Key* key,
+ uint32_t* c,
+ uint32_t* a,
+ uint32_t* b) {
+ uint32_t i;
+ for (i = 0; i < key->len; ++i) {
+ c[i] = 0;
+ }
+ for (i = 0; i < key->len; ++i) {
+ montMulAdd(key, c, a[i], b);
+ }
+}
+
+/* In-place public exponentiation. (65537}
+ * Input and output big-endian byte array in inout.
+ */
+static void modpowF4(const Key *key,
+ uint8_t* inout) {
+ uint32_t* a = (uint32_t*) bvb_malloc(key->len * sizeof(uint32_t));
+ uint32_t* aR = (uint32_t*) bvb_malloc(key->len * sizeof(uint32_t));
+ uint32_t* aaR = (uint32_t*) bvb_malloc(key->len * sizeof(uint32_t));
+ if (a == NULL || aR == NULL || aaR == NULL)
+ goto out;
+
+ 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->len; ++i) {
+ uint32_t tmp =
+ (inout[((key->len - 1 - i) * 4) + 0] << 24) |
+ (inout[((key->len - 1 - i) * 4) + 1] << 16) |
+ (inout[((key->len - 1 - i) * 4) + 2] << 8) |
+ (inout[((key->len - 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 (geM(key, aaa)) {
+ subM(key, aaa);
+ }
+
+ /* Convert to bigendian byte array */
+ for (i = (int)key->len - 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);
+ }
+
+out:
+ if (a != NULL)
+ bvb_free(a);
+ if (aR != NULL)
+ bvb_free(aR);
+ if (aaR != NULL)
+ bvb_free(aaR);
+}
+
+/* Verify a RSA PKCS1.5 signature against an expected hash.
+ * Returns 0 on failure, 1 on success.
+ */
+int bvb_rsa_verify(const uint8_t* key, size_t key_num_bytes,
+ const uint8_t* sig, size_t sig_num_bytes,
+ const uint8_t* hash, size_t hash_num_bytes,
+ const uint8_t* padding, size_t padding_num_bytes) {
+ uint8_t* buf = NULL;
+ Key* parsed_key = NULL;
+ int success = 0;
+
+ if (key == NULL || sig == NULL || hash == NULL || padding == NULL) {
+ bvb_debug("Invalid input.\n");
+ goto out;
+ }
+
+ parsed_key = parse_key_data(key, key_num_bytes);
+ if (parsed_key == NULL) {
+ bvb_debug("Error parsing key.\n");
+ goto out;
+ }
+
+ if (sig_num_bytes != (parsed_key->len * sizeof(uint32_t))) {
+ bvb_debug("Signature length does not match key length.\n");
+ goto out;
+ }
+
+ if (padding_num_bytes != sig_num_bytes - hash_num_bytes) {
+ bvb_debug("Padding length does not match hash and signature lengths.\n");
+ goto out;
+ }
+
+ buf = (uint8_t *) bvb_malloc(sig_num_bytes);
+ if (buf == NULL) {
+ bvb_debug("Error allocating %d bytes.\n", (int) sig_num_bytes);
+ goto out;
+ }
+ bvb_memcpy(buf, sig, sig_num_bytes);
+
+ modpowF4(parsed_key, buf);
+
+ /* Check padding bytes.
+ *
+ * Even though there are probably no timing issues here, we use
+ * bvb_safe_memcmp() just to be on the safe side.
+ */
+ if (bvb_safe_memcmp(buf, padding, padding_num_bytes)) {
+ bvb_debug("Padding check failed.\n");
+ goto out;
+ }
+
+ /* Check hash. */
+ if (bvb_safe_memcmp(buf + padding_num_bytes, hash, hash_num_bytes)) {
+ bvb_debug("Hash check failed.\n");
+ goto out;
+ }
+
+ success = 1;
+
+out:
+ if (parsed_key != NULL)
+ free_parsed_key(parsed_key);
+ if (buf != NULL)
+ bvb_free(buf);
+ return success;
+}
diff --git a/bvb_rsa.h b/bvb_rsa.h
new file mode 100644
index 0000000..517fd84
--- /dev/null
+++ b/bvb_rsa.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Copyright (c) 2011 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 BVB_RSA_H_
+#define BVB_RSA_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "bvb_sysdeps.h"
+
+/* Size of a RSA-2048 signature. */
+#define BVB_RSA2048_NUM_BYTES 256
+
+/* Size of a RSA-4096 signature. */
+#define BVB_RSA4096_NUM_BYTES 512
+
+/* Size of a RSA-8192 signature. */
+#define BVB_RSA8192_NUM_BYTES 1024
+
+/* Using the key given by |key_header|, verify a RSA signature |sig|
+ * of length |sig_num_bytes| against an expected |hash| of length
+ * |hash_num_bytes|. The padding to expect must be passed in using
+ * |padding| of length |padding_num_bytes|.
+ *
+ * The data in |key| must match the format defined in
+ * |BvbRSAPublicKeyHeader|, including the two large numbers
+ * following. The |key_num_bytes| must be the size of the entire
+ * serialized key.
+ *
+ * Returns zero if verification fails, non-zero otherwise.
+ */
+int bvb_rsa_verify(const uint8_t* key, size_t key_num_bytes,
+ const uint8_t* sig, size_t sig_num_bytes,
+ const uint8_t* hash, size_t hash_num_bytes,
+ const uint8_t* padding, size_t padding_num_bytes);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BVB_RSA_H_ */
diff --git a/bvb_sha.h b/bvb_sha.h
new file mode 100644
index 0000000..1b335dc
--- /dev/null
+++ b/bvb_sha.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef BVB_SHA_H_
+#define BVB_SHA_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "bvb_sysdeps.h"
+
+/* Size in bytes of a SHA-256 digest. */
+#define BVB_SHA256_DIGEST_SIZE 32
+
+/* Block size in bytes of a SHA-256 digest. */
+#define BVB_SHA256_BLOCK_SIZE 64
+
+/* Size in bytes of a SHA-512 digest. */
+#define BVB_SHA512_DIGEST_SIZE 64
+
+/* Block size in bytes of a SHA-512 digest. */
+#define BVB_SHA512_BLOCK_SIZE 128
+
+/* Data structure used for SHA-256. */
+typedef struct {
+ uint32_t h[8];
+ uint32_t tot_len;
+ uint32_t len;
+ uint8_t block[2 * BVB_SHA256_BLOCK_SIZE];
+ uint8_t buf[BVB_SHA256_DIGEST_SIZE]; /* Used for storing the final digest. */
+} BvbSHA256Ctx;
+
+/* Data structure used for SHA-512. */
+typedef struct {
+ uint64_t h[8];
+ uint32_t tot_len;
+ uint32_t len;
+ uint8_t block[2 * BVB_SHA512_BLOCK_SIZE];
+ uint8_t buf[BVB_SHA512_DIGEST_SIZE]; /* Used for storing the final digest. */
+} BvbSHA512Ctx;
+
+/* Initializes the SHA-256 context. */
+void bvb_sha256_init(BvbSHA256Ctx* ctx);
+
+/* Updates the SHA-256 context with |len| bytes from |data|. */
+void bvb_sha256_update(BvbSHA256Ctx* ctx, const uint8_t* data, uint32_t len);
+
+/* Returns the SHA-256 digest. */
+uint8_t* bvb_sha256_final(BvbSHA256Ctx* ctx);
+
+/* Initializes the SHA-512 context. */
+void bvb_sha512_init(BvbSHA512Ctx* ctx);
+
+/* Updates the SHA-512 context with |len| bytes from |data|. */
+void bvb_sha512_update(BvbSHA512Ctx* ctx, const uint8_t* data, uint32_t len);
+
+/* Returns the SHA-512 digest. */
+uint8_t* bvb_sha512_final(BvbSHA512Ctx* ctx);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BVB_SHA_H_ */
diff --git a/bvb_sha256.c b/bvb_sha256.c
new file mode 100644
index 0000000..53e5f6b
--- /dev/null
+++ b/bvb_sha256.c
@@ -0,0 +1,310 @@
+/* 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 "bvb_sha.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 bvb_sha256_init(BvbSHA256Ctx *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->len = 0;
+ ctx->tot_len = 0;
+}
+
+
+static void SHA256_transform(BvbSHA256Ctx* ctx, const uint8_t* message,
+ unsigned int block_nb) {
+ 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 bvb_sha256_update(BvbSHA256Ctx* ctx, const uint8_t* data, uint32_t len) {
+ unsigned int block_nb;
+ unsigned int new_len, rem_len, tmp_len;
+ const uint8_t *shifted_data;
+
+ tmp_len = BVB_SHA256_BLOCK_SIZE - ctx->len;
+ rem_len = len < tmp_len ? len : tmp_len;
+
+ bvb_memcpy(&ctx->block[ctx->len], data, rem_len);
+
+ if (ctx->len + len < BVB_SHA256_BLOCK_SIZE) {
+ ctx->len += len;
+ return;
+ }
+
+ new_len = len - rem_len;
+ block_nb = new_len / BVB_SHA256_BLOCK_SIZE;
+
+ shifted_data = data + rem_len;
+
+ SHA256_transform(ctx, ctx->block, 1);
+ SHA256_transform(ctx, shifted_data, block_nb);
+
+ rem_len = new_len % BVB_SHA256_BLOCK_SIZE;
+
+ bvb_memcpy(ctx->block, &shifted_data[block_nb << 6],
+ rem_len);
+
+ ctx->len = rem_len;
+ ctx->tot_len += (block_nb + 1) << 6;
+}
+
+uint8_t* bvb_sha256_final(BvbSHA256Ctx* ctx) {
+ unsigned int block_nb;
+ unsigned int pm_len;
+ unsigned int len_b;
+#ifndef UNROLL_LOOPS
+ int i;
+#endif
+
+ block_nb = (1 + ((BVB_SHA256_BLOCK_SIZE - 9)
+ < (ctx->len % BVB_SHA256_BLOCK_SIZE)));
+
+ len_b = (ctx->tot_len + ctx->len) << 3;
+ pm_len = block_nb << 6;
+
+ bvb_memset(ctx->block + ctx->len, 0, pm_len - ctx->len);
+ ctx->block[ctx->len] = 0x80;
+ UNPACK32(len_b, ctx->block + pm_len - 4);
+
+ SHA256_transform(ctx, ctx->block, block_nb);
+
+#ifndef UNROLL_LOOPS
+ for (i = 0 ; i < 8; i++) {
+ UNPACK32(ctx->h[i], &ctx->buf[i << 2]);
+ }
+#else
+ UNPACK32(ctx->h[0], &ctx->buf[ 0]);
+ UNPACK32(ctx->h[1], &ctx->buf[ 4]);
+ UNPACK32(ctx->h[2], &ctx->buf[ 8]);
+ UNPACK32(ctx->h[3], &ctx->buf[12]);
+ UNPACK32(ctx->h[4], &ctx->buf[16]);
+ UNPACK32(ctx->h[5], &ctx->buf[20]);
+ UNPACK32(ctx->h[6], &ctx->buf[24]);
+ UNPACK32(ctx->h[7], &ctx->buf[28]);
+#endif /* !UNROLL_LOOPS */
+
+ return ctx->buf;
+}
diff --git a/bvb_sha512.c b/bvb_sha512.c
new file mode 100644
index 0000000..0f1b385
--- /dev/null
+++ b/bvb_sha512.c
@@ -0,0 +1,333 @@
+/* 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 "bvb_sha.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 bvb_sha512_init(BvbSHA512Ctx *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->len = 0;
+ ctx->tot_len = 0;
+}
+
+
+static void SHA512_transform(BvbSHA512Ctx* ctx, const uint8_t* message,
+ unsigned int block_nb) {
+ 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 bvb_sha512_update(BvbSHA512Ctx* ctx, const uint8_t* data,
+ uint32_t len) {
+ unsigned int block_nb;
+ unsigned int new_len, rem_len, tmp_len;
+ const uint8_t* shifted_data;
+
+ tmp_len = BVB_SHA512_BLOCK_SIZE - ctx->len;
+ rem_len = len < tmp_len ? len : tmp_len;
+
+ bvb_memcpy(&ctx->block[ctx->len], data, rem_len);
+
+ if (ctx->len + len < BVB_SHA512_BLOCK_SIZE) {
+ ctx->len += len;
+ return;
+ }
+
+ new_len = len - rem_len;
+ block_nb = new_len / BVB_SHA512_BLOCK_SIZE;
+
+ shifted_data = data + rem_len;
+
+ SHA512_transform(ctx, ctx->block, 1);
+ SHA512_transform(ctx, shifted_data, block_nb);
+
+ rem_len = new_len % BVB_SHA512_BLOCK_SIZE;
+
+ bvb_memcpy(ctx->block, &shifted_data[block_nb << 7],
+ rem_len);
+
+ ctx->len = rem_len;
+ ctx->tot_len += (block_nb + 1) << 7;
+}
+
+uint8_t* bvb_sha512_final(BvbSHA512Ctx* ctx)
+{
+ unsigned int block_nb;
+ unsigned int pm_len;
+ unsigned int len_b;
+
+#ifndef UNROLL_LOOPS_SHA512
+ int i;
+#endif
+
+ block_nb = 1 + ((BVB_SHA512_BLOCK_SIZE - 17)
+ < (ctx->len % BVB_SHA512_BLOCK_SIZE));
+
+ len_b = (ctx->tot_len + ctx->len) << 3;
+ pm_len = block_nb << 7;
+
+ bvb_memset(ctx->block + ctx->len, 0, pm_len - ctx->len);
+ ctx->block[ctx->len] = 0x80;
+ UNPACK32(len_b, ctx->block + pm_len - 4);
+
+ SHA512_transform(ctx, ctx->block, block_nb);
+
+#ifdef UNROLL_LOOPS_SHA512
+ UNPACK64(ctx->h[0], &ctx->buf[ 0]);
+ UNPACK64(ctx->h[1], &ctx->buf[ 8]);
+ UNPACK64(ctx->h[2], &ctx->buf[16]);
+ UNPACK64(ctx->h[3], &ctx->buf[24]);
+ UNPACK64(ctx->h[4], &ctx->buf[32]);
+ UNPACK64(ctx->h[5], &ctx->buf[40]);
+ UNPACK64(ctx->h[6], &ctx->buf[48]);
+ UNPACK64(ctx->h[7], &ctx->buf[56]);
+#else
+ for (i = 0 ; i < 8; i++)
+ UNPACK64(ctx->h[i], &ctx->buf[i << 3]);
+#endif /* UNROLL_LOOPS_SHA512 */
+
+ return ctx->buf;
+}
diff --git a/bvb_sysdeps.h b/bvb_sysdeps.h
new file mode 100644
index 0000000..a4cb17f
--- /dev/null
+++ b/bvb_sysdeps.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef BVB_SYSDEPS_H_
+#define BVB_SYSDEPS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Change these includes to match your platform to bring in the
+ * equivalent types available in a normal C runtime, as well as
+ * printf()-format specifiers such as PRIx64.
+ */
+#include <stddef.h>
+#include <stdint.h>
+#include <inttypes.h>
+
+#ifdef BVB_ENABLE_DEBUG
+/* Aborts the program if |expr| is false.
+ *
+ * This has no effect unless BVB_ENABLE_DEBUG is defined.
+ */
+#define bvb_assert(expr) do { if (!(expr)) { \
+ bvb_error("assert fail: %s at %s:%d\n", \
+ #expr, __FILE__, __LINE__); }} while(0)
+#else
+#define bvb_assert(expr)
+#endif
+
+/* Size in bytes used for word-alignment.
+ *
+ * Change this to match your architecture - must be a power of two.
+ */
+#define BVB_WORD_ALIGNMENT_SIZE 8
+
+/* Aborts the program if |addr| is not word-aligned.
+ *
+ * This has no effect unless BVB_ENABLE_DEBUG is defined.
+ */
+#define bvb_assert_word_aligned(addr) \
+ bvb_assert((((uintptr_t) addr) & (BVB_WORD_ALIGNMENT_SIZE-1)) == 0)
+
+/* Compare |n| bytes in |src1| and |src2|.
+ *
+ * Returns an integer less than, equal to, or greater than zero if the
+ * first |n| bytes of |src1| is found, respectively, to be less than,
+ * to match, or be greater than the first |n| bytes of |src2|. */
+int bvb_memcmp(const void* src1, const void* src2, size_t n);
+
+/* Copy |n| bytes from |src| to |dest|. */
+void* bvb_memcpy(void* dest, const void* src, size_t n);
+
+/* Set |n| bytes starting at |s| to |c|. Returns |dest|. */
+void* bvb_memset(void* dest, const int c, size_t n);
+
+/* Compare |n| bytes starting at |s1| with |s2| and return 0 if they
+ * match, 1 if they don't. Returns 0 if |n|==0, since no bytes
+ * mismatched.
+ *
+ * Time taken to perform the comparison is only dependent on |n| and
+ * not on the relationship of the match between |s1| and |s2|.
+ *
+ * Note that unlike bvb_memcmp(), this only indicates inequality, not
+ * whether |s1| is less than or greater than |s2|.
+ */
+int bvb_safe_memcmp(const void* s1, const void* s2, size_t n);
+
+#ifdef BVB_ENABLE_DEBUG
+/* printf()-style function, used for diagnostics.
+ *
+ * This has no effect unless BVB_ENABLE_DEBUG is defined.
+ */
+void bvb_debug(const char* format, ...) __attribute__((format(printf, 1, 2)));
+#else
+static inline void bvb_debug(const char* format, ...)
+ __attribute__((format(printf, 1, 2)));
+static inline void bvb_debug(const char* format, ...) {}
+#endif
+
+/* Prints out a message (defined by |format|, printf()-style) and
+ * aborts the program or reboots the device.
+ *
+ * Unlike bvb_debug(), this function does not depend on BVB_ENABLE_DEBUG.
+ */
+void bvb_error(const char* format, ...) __attribute__((format(printf, 1, 2)));
+
+/* Allocates |size| bytes. Returns NULL if no memory is available,
+ * otherwise a pointer to the allocated memory.
+ *
+ * The memory is not initialized.
+ *
+ * The pointer returned is guaranteed to be word-aligned.
+ *
+ * The memory should be freed with bvb_free() when you are done with it.
+ */
+void* bvb_malloc(size_t size);
+
+/* Frees memory previously allocated with bvb_malloc(). */
+void bvb_free(void* ptr);
+
+/* Returns the lenght of |str|, excluding the terminating NUL-byte. */
+size_t bvb_strlen(const char* str);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BVB_SYSDEPS_H_ */
diff --git a/bvb_sysdeps_stub.c b/bvb_sysdeps_stub.c
new file mode 100644
index 0000000..c7cd076
--- /dev/null
+++ b/bvb_sysdeps_stub.c
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <endian.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "bvb_sysdeps.h"
+
+int bvb_memcmp(const void* src1, const void* src2, size_t n) {
+ return memcmp(src1, src2, n);
+}
+
+void* bvb_memcpy(void* dest, const void* src, size_t n) {
+ return memcpy(dest, src, (size_t)n);
+}
+
+void* bvb_memset(void* dest, const int c, size_t n) {
+ return memset(dest, c, n);
+}
+
+size_t bvb_strlen(const char* str) {
+ return strlen(str);
+}
+
+int bvb_safe_memcmp(const void* s1, const void* s2, size_t n) {
+ const unsigned char* us1 = s1;
+ const unsigned char* us2 = s2;
+ int result = 0;
+
+ if (0 == n)
+ return 0;
+
+ /*
+ * Code snippet without data-dependent branch due to Nate Lawson
+ * (nate@root.org) of Root Labs.
+ */
+ while (n--)
+ result |= *us1++ ^ *us2++;
+
+ return result != 0;
+}
+
+void bvb_error(const char* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ fprintf(stderr, "ERROR: ");
+ vfprintf(stderr, format, ap);
+ va_end(ap);
+ exit(1);
+}
+
+#ifdef BVB_ENABLE_DEBUG
+void bvb_debug(const char* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ fprintf(stderr, "DEBUG: ");
+ vfprintf(stderr, format, ap);
+ va_end(ap);
+}
+#endif
+
+void* bvb_malloc(size_t size) {
+ return malloc(size);
+}
+
+void bvb_free(void* ptr) {
+ free(ptr);
+}
diff --git a/bvb_unittest_util.h b/bvb_unittest_util.h
new file mode 100644
index 0000000..1735a01
--- /dev/null
+++ b/bvb_unittest_util.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef BVB_UNITTEST_UTIL_H_
+#define BVB_UNITTEST_UTIL_H_
+
+#include <gtest/gtest.h>
+
+/* Utility macro to run the command expressed by the printf()-style string
+ * |command_format| using the system(3) utility function. Will assert unless
+ * the command exits normally with exit status |expected_exit_status|.
+ */
+#define EXPECT_COMMAND(expected_exit_status, command_format, ...) do { \
+ int rc = system(base::StringPrintf(command_format, ## __VA_ARGS__).c_str()); \
+ EXPECT_TRUE(WIFEXITED(rc)); \
+ EXPECT_EQ(WEXITSTATUS(rc), expected_exit_status); \
+} while (0);
+
+/* Base-class used for unit test. */
+class BaseBvbToolTest : public ::testing::Test {
+public:
+ BaseBvbToolTest() {}
+
+protected:
+ virtual ~BaseBvbToolTest() {}
+
+ /* Generates a Brillo Boot Image, using bvbtoool. The generated boot
+ * image will written to disk, see the |boot_image_path| variable
+ * for its path and |boot_image_| for the content.
+ */
+ void GenerateBootImage(const std::string& algorithm,
+ const std::string& kernel_cmdline,
+ uint64_t rollback_index,
+ const base::FilePath& key_path,
+ const std::string& additional_options = "") {
+ boot_image_path_ = testdir_.Append("boot_brillo.img");
+ EXPECT_COMMAND(0,
+ "./bvbtool make_boot_image"
+ " --kernel %s"
+ " --initrd %s"
+ " --kernel_cmdline \"%s\""
+ " --rollback_index %" PRIu64
+ " %s "
+ " --output %s",
+ base::FilePath("test/dummy_kernel.bin").value().c_str(),
+ base::FilePath("test/dummy_initrd.bin").value().c_str(),
+ kernel_cmdline.c_str(),
+ rollback_index,
+ additional_options.c_str(),
+ boot_image_path_.value().c_str());
+ if (algorithm != "") {
+ EXPECT_COMMAND(0,
+ "./bvbtool sign_boot_image --key %s"
+ " --image %s --algorithm %s",
+ key_path.value().c_str(),
+ boot_image_path_.value().c_str(),
+ algorithm.c_str());
+ }
+ int64_t file_size;
+ ASSERT_TRUE(base::GetFileSize(boot_image_path_, &file_size));
+ boot_image_.resize(file_size);
+ ASSERT_TRUE(base::ReadFile(boot_image_path_,
+ reinterpret_cast<char*>(boot_image_.data()),
+ boot_image_.size()));
+ }
+
+ /* Create temporary directory to stash images in. */
+ virtual void SetUp() override {
+ base::FilePath ret;
+ char* buf = strdup("/tmp/bvb-refimpl-tests.XXXXXX");
+ ASSERT_TRUE(mkdtemp(buf) != nullptr);
+ testdir_ = base::FilePath(buf);
+ free(buf);
+ }
+
+ /* Nuke temporary directory. */
+ virtual void TearDown() override {
+ ASSERT_EQ(0U, testdir_.value().find("/tmp/bvb-refimpl-tests"));
+ ASSERT_TRUE(base::DeleteFile(testdir_, true /* recursive */));
+ }
+
+ /* Temporary directory created in SetUp(). */
+ base::FilePath testdir_;
+
+ /* Path to boot image generated with GenerateBootImage(). */
+ base::FilePath boot_image_path_;
+
+ /* Contents of the image generated with GenerateBootImage(). */
+ std::vector<uint8_t> boot_image_;
+};
+
+#endif /* BVB_UNITTEST_UTIL_H_ */
diff --git a/bvb_util.c b/bvb_util.c
new file mode 100644
index 0000000..fff8e96
--- /dev/null
+++ b/bvb_util.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include "bvb_util.h"
+
+uint32_t bvb_be32toh(uint32_t in) {
+ uint8_t* d = (uint8_t*) &in;
+ uint32_t ret;
+ ret = ((uint32_t) d[0]) << 24;
+ ret |= ((uint32_t) d[1]) << 16;
+ ret |= ((uint32_t) d[2]) << 8;
+ ret |= ((uint32_t) d[3]);
+ return ret;
+}
+
+uint64_t bvb_be64toh(uint64_t in) {
+ uint8_t* d = (uint8_t*) &in;
+ uint64_t ret;
+ ret = ((uint64_t) d[0]) << 56;
+ ret |= ((uint64_t) d[1]) << 48;
+ ret |= ((uint64_t) d[2]) << 40;
+ ret |= ((uint64_t) d[3]) << 32;
+ ret |= ((uint64_t) d[4]) << 24;
+ ret |= ((uint64_t) d[5]) << 16;
+ ret |= ((uint64_t) d[6]) << 8;
+ ret |= ((uint64_t) d[7]);
+ return ret;
+}
+
+void bvb_boot_image_header_to_host_byte_order(const BvbBootImageHeader* src,
+ BvbBootImageHeader* dest) {
+ bvb_memcpy(dest, src, sizeof(BvbBootImageHeader));
+
+ dest->header_version_major = bvb_be32toh(dest->header_version_major);
+ dest->header_version_minor = bvb_be32toh(dest->header_version_minor);
+
+ dest->authentication_data_block_size =
+ bvb_be64toh(dest->authentication_data_block_size);
+ dest->auxilary_data_block_size = bvb_be64toh(dest->auxilary_data_block_size);
+ dest->payload_data_block_size = bvb_be64toh(dest->payload_data_block_size);
+
+ dest->algorithm_type = bvb_be32toh(dest->algorithm_type);
+
+ dest->hash_offset = bvb_be64toh(dest->hash_offset);
+ dest->hash_size = bvb_be64toh(dest->hash_size);
+
+ dest->signature_offset = bvb_be64toh(dest->signature_offset);
+ dest->signature_size = bvb_be64toh(dest->signature_size);
+
+ dest->public_key_offset = bvb_be64toh(dest->public_key_offset);
+ dest->public_key_size = bvb_be64toh(dest->public_key_size);
+
+ dest->properties_offset = bvb_be64toh(dest->properties_offset);
+ dest->properties_size = bvb_be64toh(dest->properties_size);
+
+ dest->rollback_index = bvb_be64toh(dest->rollback_index);
+
+ dest->kernel_offset = bvb_be64toh(dest->kernel_offset);
+ dest->kernel_size = bvb_be64toh(dest->kernel_size);
+
+ dest->initrd_offset = bvb_be64toh(dest->initrd_offset);
+ dest->initrd_size = bvb_be64toh(dest->initrd_size);
+
+ dest->kernel_addr = bvb_be64toh(dest->kernel_addr);
+ dest->initrd_addr = bvb_be64toh(dest->initrd_addr);
+}
+
+void bvb_rsa_public_key_header_to_host_byte_order(
+ const BvbRSAPublicKeyHeader* src,
+ BvbRSAPublicKeyHeader* dest) {
+ bvb_memcpy(dest, src, sizeof(BvbRSAPublicKeyHeader));
+
+ dest->key_num_bits = bvb_be32toh(dest->key_num_bits);
+ dest->n0inv = bvb_be32toh(dest->n0inv);
+}
+
+int bvb_safe_add_to(uint64_t *value, uint64_t value_to_add) {
+ uint64_t original_value;
+
+ bvb_assert(value != NULL);
+
+ original_value = *value;
+
+ *value += value_to_add;
+ if (*value < original_value) {
+ bvb_debug("%s: overflow: 0x%016" PRIx64 " + 0x%016" PRIx64 "\n",
+ __FUNCTION__, original_value, value_to_add);
+ return 0;
+ }
+
+ return 1;
+}
+
+int bvb_safe_add(uint64_t* out_result, uint64_t a, uint64_t b) {
+ uint64_t dummy;
+ if (out_result == NULL)
+ out_result = &dummy;
+ *out_result = a;
+ return bvb_safe_add_to(out_result, b);
+}
diff --git a/bvb_util.h b/bvb_util.h
new file mode 100644
index 0000000..77331fc
--- /dev/null
+++ b/bvb_util.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef BVB_UTIL_H_
+#define BVB_UTIL_H_
+
+#include "bvb_boot_image_header.h"
+#include "bvb_rsa.h"
+#include "bvb_sysdeps.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Converts a 32-bit unsigned integer from big-endian to host byte order. */
+uint32_t bvb_be32toh(uint32_t in);
+
+/* Converts a 64-bit unsigned integer from big-endian to host byte order. */
+uint64_t bvb_be64toh(uint64_t in);
+
+/* Adds |value_to_add| to |value| with overflow protection.
+ *
+ * Returns zero if the addition overflows, non-zero otherwise. In
+ * either case, |value| is always modified.
+ */
+int bvb_safe_add_to(uint64_t *value, uint64_t value_to_add);
+
+/* Adds |a| and |b| with overflow protection, returning the value in
+ * |out_result|.
+ *
+ * It's permissible to pass NULL for |out_result| if you just want to
+ * check that the addition would not overflow.
+ *
+ * Returns zero if the addition overflows, non-zero otherwise.
+ */
+int bvb_safe_add(uint64_t *out_result, uint64_t a, uint64_t b);
+
+/* Copies |src| to |dest|, byte-swapping fields in the process. */
+void bvb_boot_image_header_to_host_byte_order(
+ const BvbBootImageHeader* src,
+ BvbBootImageHeader* dest);
+
+/* Copies |header| to |dest|, byte-swapping fields in the process. */
+void bvb_rsa_public_key_header_to_host_byte_order(
+ const BvbRSAPublicKeyHeader* src,
+ BvbRSAPublicKeyHeader* dest);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BVB_UTIL_H_ */
diff --git a/bvb_util_unittest.cc b/bvb_util_unittest.cc
new file mode 100644
index 0000000..851e2bd
--- /dev/null
+++ b/bvb_util_unittest.cc
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string.h>
+
+#include <gtest/gtest.h>
+
+#include "bvb_util.h"
+
+TEST(UtilTest, BootImageHeaderByteswap)
+{
+ BvbBootImageHeader h;
+ BvbBootImageHeader s;
+ uint32_t n32;
+ uint64_t n64;
+
+ n32 = 0x11223344;
+ n64 = 0x1122334455667788;
+
+ h.header_version_major = htobe32(n32); n32++;
+ h.header_version_minor = htobe32(n32); n32++;
+ h.authentication_data_block_size = htobe64(n64); n64++;
+ h.auxilary_data_block_size = htobe64(n64); n64++;
+ h.payload_data_block_size = htobe64(n64); n64++;
+ h.algorithm_type = htobe32(n32); n32++;
+ h.hash_offset = htobe64(n64); n64++;
+ h.hash_size = htobe64(n64); n64++;
+ h.signature_offset = htobe64(n64); n64++;
+ h.signature_size = htobe64(n64); n64++;
+ h.public_key_offset = htobe64(n64); n64++;
+ h.public_key_size = htobe64(n64); n64++;
+ h.properties_offset = htobe64(n64); n64++;
+ h.properties_size = htobe64(n64); n64++;
+ h.rollback_index = htobe64(n64); n64++;
+ h.kernel_offset = htobe64(n64); n64++;
+ h.kernel_size = htobe64(n64); n64++;
+ h.initrd_offset = htobe64(n64); n64++;
+ h.initrd_size = htobe64(n64); n64++;
+ h.kernel_addr = htobe64(n64); n64++;
+ h.initrd_addr = htobe64(n64); n64++;
+
+ bvb_boot_image_header_to_host_byte_order(&h, &s);
+
+ n32 = 0x11223344;
+ n64 = 0x1122334455667788;
+
+ EXPECT_EQ(n32, s.header_version_major); n32++;
+ EXPECT_EQ(n32, s.header_version_minor); n32++;
+ EXPECT_EQ(n64, s.authentication_data_block_size); n64++;
+ EXPECT_EQ(n64, s.auxilary_data_block_size); n64++;
+ EXPECT_EQ(n64, s.payload_data_block_size); n64++;
+ EXPECT_EQ(n32, s.algorithm_type); n32++;
+ EXPECT_EQ(n64, s.hash_offset); n64++;
+ EXPECT_EQ(n64, s.hash_size); n64++;
+ EXPECT_EQ(n64, s.signature_offset); n64++;
+ EXPECT_EQ(n64, s.signature_size); n64++;
+ EXPECT_EQ(n64, s.public_key_offset); n64++;
+ EXPECT_EQ(n64, s.public_key_size); n64++;
+ EXPECT_EQ(n64, s.properties_offset); n64++;
+ EXPECT_EQ(n64, s.properties_size); n64++;
+ EXPECT_EQ(n64, s.rollback_index); n64++;
+ EXPECT_EQ(n64, s.kernel_offset); n64++;
+ EXPECT_EQ(n64, s.kernel_size); n64++;
+ EXPECT_EQ(n64, s.initrd_offset); n64++;
+ EXPECT_EQ(n64, s.initrd_size); n64++;
+ EXPECT_EQ(n64, s.kernel_addr); n64++;
+ EXPECT_EQ(n64, s.initrd_addr); n64++;
+
+ // If new fields are added, the following will fail. This is to
+ // remind that byteswapping code (in bvb_util.c) and unittests for
+ // this should be updated.
+ static_assert(offsetof(BvbBootImageHeader, reserved) == 4256,
+ "Remember to unittest byteswapping of newly added fields");
+}
+
+TEST(UtilTest, RSAPublicKeyHeaderByteswap)
+{
+ BvbRSAPublicKeyHeader h;
+ BvbRSAPublicKeyHeader s;
+ uint32_t n32;
+ uint64_t n64;
+
+ n32 = 0x11223344;
+ n64 = 0x1122334455667788;
+
+ h.key_num_bits = htobe32(n32); n32++;
+ h.n0inv = htobe32(n32); n32++;
+
+ bvb_rsa_public_key_header_to_host_byte_order(&h, &s);
+
+ n32 = 0x11223344;
+ n64 = 0x1122334455667788;
+
+ EXPECT_EQ(n32, s.key_num_bits); n32++;
+ EXPECT_EQ(n32, s.n0inv); n32++;
+}
+
+TEST(UtilTest, SafeAddition) {
+ uint64_t value;
+ uint64_t pow2_60 = 1ULL << 60;
+
+ value = 2;
+ EXPECT_NE(0, bvb_safe_add_to(&value, 5));
+ EXPECT_EQ(7UL, value);
+
+ /* These should not overflow */
+ value = 1*pow2_60;
+ EXPECT_NE(0, bvb_safe_add_to(&value, 2*pow2_60));
+ EXPECT_EQ(3*pow2_60, value);
+ value = 7*pow2_60;
+ EXPECT_NE(0, bvb_safe_add_to(&value, 8*pow2_60));
+ EXPECT_EQ(15*pow2_60, value);
+ value = 9*pow2_60;
+ EXPECT_NE(0, bvb_safe_add_to(&value, 3*pow2_60));
+ EXPECT_EQ(12*pow2_60, value);
+ value = 0xfffffffffffffffcUL;
+ EXPECT_NE(0, bvb_safe_add_to(&value, 2));
+ EXPECT_EQ(0xfffffffffffffffeUL, value);
+
+ /* These should overflow. */
+ value = 8*pow2_60;
+ EXPECT_EQ(0, bvb_safe_add_to(&value, 8*pow2_60));
+ value = 0xfffffffffffffffcUL;
+ EXPECT_EQ(0, bvb_safe_add_to(&value, 4));
+}
diff --git a/bvb_verify.c b/bvb_verify.c
new file mode 100644
index 0000000..45b675e
--- /dev/null
+++ b/bvb_verify.c
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "bvb_rsa.h"
+#include "bvb_sha.h"
+#include "bvb_util.h"
+#include "bvb_verify.h"
+
+static const uint8_t padding_RSA2048_SHA256[BVB_RSA2048_NUM_BYTES - BVB_SHA256_DIGEST_SIZE] = {
+0x00,0x01,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x30,0x31,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x01,0x05,0x00,0x04,0x20
+};
+
+static const uint8_t padding_RSA4096_SHA256[BVB_RSA4096_NUM_BYTES - BVB_SHA256_DIGEST_SIZE] = {
+0x00,0x01,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x30,0x31,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x01,0x05,0x00,0x04,0x20
+};
+
+static const uint8_t padding_RSA8192_SHA256[BVB_RSA8192_NUM_BYTES - BVB_SHA256_DIGEST_SIZE] = {
+0x00,0x01,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x30,0x31,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x01,0x05,0x00,0x04,0x20
+};
+
+static const uint8_t padding_RSA2048_SHA512[BVB_RSA2048_NUM_BYTES - BVB_SHA512_DIGEST_SIZE] = {
+0x00,0x01,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x30,0x51,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x03,0x05,0x00,0x04,0x40
+};
+
+static const uint8_t padding_RSA4096_SHA512[BVB_RSA4096_NUM_BYTES - BVB_SHA512_DIGEST_SIZE] = {
+0x00,0x01,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x30,0x51,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x03,0x05,0x00,0x04,0x40
+};
+
+static const uint8_t padding_RSA8192_SHA512[BVB_RSA8192_NUM_BYTES - BVB_SHA512_DIGEST_SIZE] = {
+0x00,0x01,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x30,0x51,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x03,0x05,0x00,0x04,0x40
+};
+
+typedef struct {
+ const uint8_t *padding;
+ size_t padding_len;
+ size_t hash_len;
+} BvbAlgorithmData;
+
+static BvbAlgorithmData algorithm_data[_BVB_ALGORITHM_NUM_TYPES] = {
+ /* BVB_ALGORITHM_TYPE_NONE */
+ {
+ .padding = NULL,
+ .padding_len = 0,
+ .hash_len = 0
+ },
+ /* BVB_ALGORITHM_TYPE_SHA256_RSA2048 */
+ {
+ .padding = padding_RSA2048_SHA256,
+ .padding_len = sizeof(padding_RSA2048_SHA256),
+ .hash_len = BVB_SHA256_DIGEST_SIZE
+ },
+ /* BVB_ALGORITHM_TYPE_SHA256_RSA4096 */
+ {
+ .padding = padding_RSA4096_SHA256,
+ .padding_len = sizeof(padding_RSA4096_SHA256),
+ .hash_len = BVB_SHA256_DIGEST_SIZE
+ },
+ /* BVB_ALGORITHM_TYPE_SHA256_RSA8192 */
+ {
+ .padding = padding_RSA8192_SHA256,
+ .padding_len = sizeof(padding_RSA8192_SHA256),
+ .hash_len = BVB_SHA256_DIGEST_SIZE
+ },
+ /* BVB_ALGORITHM_TYPE_SHA512_RSA2048 */
+ {
+ .padding = padding_RSA2048_SHA512,
+ .padding_len = sizeof(padding_RSA2048_SHA512),
+ .hash_len = BVB_SHA512_DIGEST_SIZE
+ },
+ /* BVB_ALGORITHM_TYPE_SHA512_RSA4096 */
+ {
+ .padding = padding_RSA4096_SHA512,
+ .padding_len = sizeof(padding_RSA4096_SHA512),
+ .hash_len = BVB_SHA512_DIGEST_SIZE
+ },
+ /* BVB_ALGORITHM_TYPE_SHA512_RSA8192 */
+ {
+ .padding = padding_RSA8192_SHA512,
+ .padding_len = sizeof(padding_RSA8192_SHA512),
+ .hash_len = BVB_SHA512_DIGEST_SIZE
+ },
+};
+
+BvbVerifyResult bvb_verify_boot_image(const uint8_t* data,
+ size_t length,
+ const uint8_t** out_public_key_data,
+ size_t* out_public_key_length) {
+ BvbVerifyResult ret;
+ BvbBootImageHeader *h = NULL;
+ uint8_t* computed_hash;
+ BvbAlgorithmData* algorithm;
+ BvbSHA256Ctx sha256_ctx;
+ BvbSHA512Ctx sha512_ctx;
+ const uint8_t* header_block;
+ const uint8_t* authentication_block;
+ const uint8_t* auxilary_block;
+ const uint8_t* payload_block;
+ int verification_result;
+
+ ret = BVB_VERIFY_RESULT_INVALID_BOOT_IMAGE_HEADER;
+
+ if (out_public_key_data != NULL)
+ *out_public_key_data = NULL;
+ if (out_public_key_length != NULL)
+ *out_public_key_length = 0;
+
+ /* Ensure magic is correct. */
+ if (bvb_safe_memcmp(data, BVB_MAGIC, BVB_MAGIC_LEN) != 0) {
+ bvb_debug("Magic is incorrect.\n");
+ goto out;
+ }
+
+ /* Before we byteswap, ensure length is long enough. */
+ if (length < sizeof(BvbBootImageHeader)) {
+ bvb_debug("Length is smaller than header.\n");
+ goto out;
+ }
+ h = bvb_malloc(sizeof(BvbBootImageHeader));
+ if (h == NULL) {
+ bvb_debug("Error allocating byteswapped header.\n");
+ goto out;
+ }
+ bvb_boot_image_header_to_host_byte_order(
+ (const BvbBootImageHeader *) data, h);
+
+ /* Ensure we don't attempt to access any fields if the major version
+ * is not supported.
+ */
+ if (h->header_version_major > BVB_MAJOR_VERSION) {
+ bvb_debug("No support for version %d.\n", h->header_version_major);
+ goto out;
+ }
+
+ /* Ensure inner block sizes are multiple of 64. */
+ if ((h->authentication_data_block_size & 0x3f) != 0 ||
+ (h->auxilary_data_block_size & 0x3f) != 0) {
+ bvb_debug("Block size is not a multiple of 64.\n");
+ goto out;
+ }
+
+ /* Ensure block sizes all add up to at least |length|. */
+ uint64_t block_total = sizeof(BvbBootImageHeader);
+ if (!bvb_safe_add_to(&block_total, h->authentication_data_block_size) ||
+ !bvb_safe_add_to(&block_total, h->auxilary_data_block_size) ||
+ !bvb_safe_add_to(&block_total, h->payload_data_block_size)) {
+ bvb_debug("Overflow while computing size of boot image.\n");
+ goto out;
+ }
+ if (block_total > length) {
+ bvb_debug("Block sizes add up to more than given length.\n");
+ goto out;
+ }
+
+ /* Ensure passed in memory doesn't wrap. */
+ if (!bvb_safe_add(NULL, (uint64_t) data, length)) {
+ bvb_debug("Boot image location and length mismatch.\n");
+ goto out;
+ }
+
+ /* Ensure hash and signature are entirely in the Authentication data block. */
+ uint64_t hash_end;
+ if (!bvb_safe_add(&hash_end, h->hash_offset, h->hash_size) ||
+ hash_end > h->authentication_data_block_size) {
+ bvb_debug("Hash is not entirely in its block.\n");
+ goto out;
+ }
+ uint64_t signature_end;
+ if (!bvb_safe_add(&signature_end, h->signature_offset, h->signature_size) ||
+ signature_end > h->authentication_data_block_size) {
+ bvb_debug("Signature is not entirely in its block.\n");
+ goto out;
+ }
+
+ /* Ensure public key is entirely in the Auxilary data block. */
+ uint64_t pubkey_end;
+ if (!bvb_safe_add(&pubkey_end, h->public_key_offset, h->public_key_size) ||
+ pubkey_end > h->auxilary_data_block_size) {
+ bvb_debug("Public key is not entirely in its block.\n");
+ goto out;
+ }
+
+ /* Ensure kernel and initramfs are entirely in the Payload data
+ * block.
+ */
+ uint64_t kernel_end;
+ if (!bvb_safe_add(&kernel_end, h->kernel_offset, h->kernel_size) ||
+ kernel_end > h->payload_data_block_size) {
+ bvb_debug("Kernel is not entirely in its block.\n");
+ goto out;
+ }
+ if (h->initrd_size > 0) {
+ uint64_t initrd_end;
+ if (!bvb_safe_add(&initrd_end, h->initrd_offset, h->initrd_size) ||
+ initrd_end > h->payload_data_block_size) {
+ bvb_debug("Initrd is not entirely in its block.\n");
+ goto out;
+ }
+ }
+
+ /* Ensure algorithm field is supported. */
+ if (h->algorithm_type >= _BVB_ALGORITHM_NUM_TYPES) {
+ bvb_debug("Invalid or unknown algorithm.\n");
+ goto out;
+ }
+ algorithm = &algorithm_data[h->algorithm_type];
+
+ /* Bail early if there's no hash or signature. */
+ if (h->algorithm_type == BVB_ALGORITHM_TYPE_NONE) {
+ ret = BVB_VERIFY_RESULT_OK_NOT_SIGNED;
+ goto out;
+ }
+
+ /* Bail if the embedded hash size doesn't match the chosen algorithm. */
+ if (h->hash_size != algorithm->hash_len) {
+ bvb_debug("Embedded hash has wrong size.\n");
+ goto out;
+ }
+
+ /* No overflow checks needed from here-on after since all block
+ * sizes and offsets have been verified above.
+ */
+
+ header_block = data;
+ authentication_block = header_block + sizeof(BvbBootImageHeader);
+ auxilary_block = authentication_block + h->authentication_data_block_size;
+ payload_block = auxilary_block + h->auxilary_data_block_size;
+
+ switch (h->algorithm_type) {
+ /* Explicit fall-through: */
+ case BVB_ALGORITHM_TYPE_SHA256_RSA2048:
+ case BVB_ALGORITHM_TYPE_SHA256_RSA4096:
+ case BVB_ALGORITHM_TYPE_SHA256_RSA8192:
+ bvb_sha256_init(&sha256_ctx);
+ bvb_sha256_update(&sha256_ctx, header_block,
+ sizeof(BvbBootImageHeader));
+ bvb_sha256_update(&sha256_ctx, auxilary_block,
+ h->auxilary_data_block_size);
+ bvb_sha256_update(&sha256_ctx, payload_block,
+ h->payload_data_block_size);
+ computed_hash = bvb_sha256_final(&sha256_ctx);
+ break;
+ /* Explicit fall-through: */
+ case BVB_ALGORITHM_TYPE_SHA512_RSA2048:
+ case BVB_ALGORITHM_TYPE_SHA512_RSA4096:
+ case BVB_ALGORITHM_TYPE_SHA512_RSA8192:
+ bvb_sha512_init(&sha512_ctx);
+ bvb_sha512_update(&sha512_ctx, header_block,
+ sizeof(BvbBootImageHeader));
+ bvb_sha512_update(&sha512_ctx, auxilary_block,
+ h->auxilary_data_block_size);
+ bvb_sha512_update(&sha512_ctx, payload_block,
+ h->payload_data_block_size);
+ computed_hash = bvb_sha512_final(&sha512_ctx);
+ break;
+ default:
+ bvb_debug("Unknown algorithm %d.\n", h->algorithm_type);
+ goto out;
+ }
+
+ if (bvb_safe_memcmp(authentication_block + h->hash_offset,
+ computed_hash, h->hash_size) != 0) {
+ bvb_debug("Hash does not match!\n");
+ ret = BVB_VERIFY_RESULT_HASH_MISMATCH;
+ goto out;
+ }
+
+ verification_result = bvb_rsa_verify(
+ auxilary_block + h->public_key_offset, h->public_key_size,
+ authentication_block + h->signature_offset, h->signature_size,
+ authentication_block + h->hash_offset, h->hash_size,
+ algorithm->padding, algorithm->padding_len);
+
+ if (verification_result == 0) {
+ ret = BVB_VERIFY_RESULT_SIGNATURE_MISMATCH;
+ goto out;
+ }
+
+ if (out_public_key_data != NULL)
+ *out_public_key_data = auxilary_block + h->public_key_offset;
+ if (out_public_key_length != NULL)
+ *out_public_key_length = h->public_key_size;
+
+ ret = BVB_VERIFY_RESULT_OK;
+
+out:
+ if (h != NULL)
+ bvb_free(h);
+ return ret;
+}
diff --git a/bvb_verify.h b/bvb_verify.h
new file mode 100644
index 0000000..8a713e7
--- /dev/null
+++ b/bvb_verify.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef BVB_VERIFY_H_
+#define BVB_VERIFY_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "bvb_boot_image_header.h"
+
+/* Return codes used in bvb_verify_boot_image().
+ *
+ * BVB_VERIFY_RESULT_OK is returned if the boot image header is valid,
+ * the hash is correct and the signature is correct. Keep in mind that
+ * you still need to check that you know the public key used to sign
+ * the image, see bvb_verify_boot_image() for details.
+ *
+ * BVB_VERIFY_RESULT_OK_NOT_SIGNED is returned if the boot image
+ * header is valid but there is no signature or hash.
+ *
+ * BVB_VERIFY_INVALID_BOOT_IMAGE_HEADER is returned if the header of
+ * the boot image is invalid, for example, invalid magic or
+ * inconsistent data.
+ *
+ * BVB_VERIFY_HASH_MISMATCH is returned if the hash stored in the
+ * "Authentication data" block does not match the calculated hash.
+ *
+ * BVB_VERIFY_SIGNATURE_MISMATCH is returned if the signature stored
+ * in the "Authentication data" block is invalid or doesn't match the
+ * public key stored in the boot image.
+ */
+typedef enum {
+ BVB_VERIFY_RESULT_OK,
+ BVB_VERIFY_RESULT_OK_NOT_SIGNED,
+ BVB_VERIFY_RESULT_INVALID_BOOT_IMAGE_HEADER,
+ BVB_VERIFY_RESULT_HASH_MISMATCH,
+ BVB_VERIFY_RESULT_SIGNATURE_MISMATCH,
+} BvbVerifyResult;
+
+/*
+ * Checks that raw boot image at |data| of size |length| is a valid
+ * Brillo boot image. The complete contents of the boot image must be
+ * passed in. It's fine if |length| is bigger than the actual image,
+ * typically callers of this function will load the entire contents of
+ * the 'boot_a' or 'boot_b' partition and pass in its length (for
+ * example, 32 MiB).
+ *
+ * See the |BvbBootImageHeader| struct for information about the four
+ * blocks (header, authentication, auxilary, payload) that make up a
+ * boot image.
+ *
+ * If the function returns |BVB_VERIFY_RESULT_OK| and
+ * |out_public_key_data| is non-NULL, it will be set to point inside
+ * |data| for where the serialized public key data is stored and
+ * |out_public_key_length|, if non-NULL, will be set to the length of
+ * the public key data.
+ *
+ * See the |BvbVerifyResult| enum for possible return values.
+ *
+ * VERY IMPORTANT:
+ *
+ * 1. Even if |BVB_VERIFY_RESULT_OK| is returned, you still need to
+ * check that the public key embedded in the image matches a
+ * known key! You can use 'bvbtool extract_public_key' to extract
+ * the key at build time and compare it to what is returned in
+ * |out_public_key_data|.
+ *
+ * 2. You need to check the |rollback_index| field against a stored
+ * value in NVRAM and reject the boot image if the value in NVRAM
+ * is bigger than |rollback_index|. You must also update the
+ * value stored in NVRAM to the smallest value of
+ * |rollback_index| field from boot images in all bootable and
+ * authentic slots marked as GOOD.
+ */
+BvbVerifyResult bvb_verify_boot_image(
+ const uint8_t* data, size_t length,
+ const uint8_t** out_public_key_data, size_t* out_public_key_length);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BVB_VERIFY_H_ */
diff --git a/bvb_verify_unittest.cc b/bvb_verify_unittest.cc
new file mode 100644
index 0000000..faa4ae8
--- /dev/null
+++ b/bvb_verify_unittest.cc
@@ -0,0 +1,469 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <iostream>
+
+#include <endian.h>
+#include <inttypes.h>
+#include <string.h>
+
+#include <base/files/file_util.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+
+#include "bvb_unittest_util.h"
+#include "bvb_util.h"
+#include "bvb_verify.h"
+#include "bvb_property.h"
+
+class VerifyTest : public BaseBvbToolTest {
+public:
+ VerifyTest() {}
+
+protected:
+
+ // Helper function for ModificationDetection test. Modifies
+ // boot_image_ in a number of places in the sub-array at |offset| of
+ // size |length| and checks that bvb_verify_boot_image() returns
+ // |expected_result|.
+ bool test_modification(BvbVerifyResult expected_result,
+ size_t offset, size_t length);
+
+};
+
+TEST_F(VerifyTest, BootImageStructSize) {
+ EXPECT_EQ(8192UL, sizeof(BvbBootImageHeader));
+}
+
+TEST_F(VerifyTest, CheckSHA256RSA2048) {
+ GenerateBootImage("SHA256_RSA2048", "", 0,
+ base::FilePath("test/testkey_rsa2048.pem"));
+ EXPECT_EQ(BVB_VERIFY_RESULT_OK,
+ bvb_verify_boot_image(boot_image_.data(), boot_image_.size(),
+ NULL, NULL));
+}
+
+TEST_F(VerifyTest, CheckSHA256RSA4096) {
+ GenerateBootImage("SHA256_RSA4096", "", 0
+ , base::FilePath("test/testkey_rsa4096.pem"));
+ EXPECT_EQ(BVB_VERIFY_RESULT_OK,
+ bvb_verify_boot_image(boot_image_.data(), boot_image_.size(),
+ NULL, NULL));
+}
+
+TEST_F(VerifyTest, CheckSHA256RSA8192) {
+ GenerateBootImage("SHA256_RSA8192", "", 0,
+ base::FilePath("test/testkey_rsa8192.pem"));
+ EXPECT_EQ(BVB_VERIFY_RESULT_OK,
+ bvb_verify_boot_image(boot_image_.data(), boot_image_.size(),
+ NULL, NULL));
+}
+
+TEST_F(VerifyTest, CheckSHA512RSA2048) {
+ GenerateBootImage("SHA512_RSA2048", "", 0,
+ base::FilePath("test/testkey_rsa2048.pem"));
+ EXPECT_EQ(BVB_VERIFY_RESULT_OK,
+ bvb_verify_boot_image(boot_image_.data(), boot_image_.size(),
+ NULL, NULL));
+}
+
+TEST_F(VerifyTest, CheckSHA512RSA4096) {
+ GenerateBootImage("SHA512_RSA4096", "", 0,
+ base::FilePath("test/testkey_rsa4096.pem"));
+ EXPECT_EQ(BVB_VERIFY_RESULT_OK,
+ bvb_verify_boot_image(boot_image_.data(), boot_image_.size(),
+ NULL, NULL));
+}
+
+TEST_F(VerifyTest, CheckSHA512RSA8192) {
+ GenerateBootImage("SHA512_RSA8192", "", 0,
+ base::FilePath("test/testkey_rsa8192.pem"));
+ EXPECT_EQ(BVB_VERIFY_RESULT_OK,
+ bvb_verify_boot_image(boot_image_.data(), boot_image_.size(),
+ NULL, NULL));
+}
+
+TEST_F(VerifyTest, CheckBiggerLength) {
+ GenerateBootImage("SHA256_RSA2048", "", 0,
+ base::FilePath("test/testkey_rsa2048.pem"));
+ // Check that it's OK if we pass a bigger length than what the
+ // header indicates.
+ EXPECT_EQ(BVB_VERIFY_RESULT_OK,
+ bvb_verify_boot_image(boot_image_.data(),
+ boot_image_.size() + 8192,
+ NULL, NULL));
+}
+
+TEST_F(VerifyTest, CheckUnsigned) {
+ GenerateBootImage("", "", 0, base::FilePath(""));
+ EXPECT_EQ(BVB_VERIFY_RESULT_OK_NOT_SIGNED,
+ bvb_verify_boot_image(boot_image_.data(), boot_image_.size(),
+ NULL, NULL));
+}
+
+TEST_F(VerifyTest, BadMagic) {
+ GenerateBootImage("SHA256_RSA2048", "", 0,
+ base::FilePath("test/testkey_rsa2048.pem"));
+ boot_image_[0] = 'A';
+ EXPECT_EQ(BVB_VERIFY_RESULT_INVALID_BOOT_IMAGE_HEADER,
+ bvb_verify_boot_image(boot_image_.data(), boot_image_.size(),
+ NULL, NULL));
+}
+
+
+TEST_F(VerifyTest, MajorVersionCheck) {
+ GenerateBootImage("SHA256_RSA2048", "", 0,
+ base::FilePath("test/testkey_rsa2048.pem"));
+
+ BvbBootImageHeader *h =
+ reinterpret_cast<BvbBootImageHeader*>(boot_image_.data());
+ h->header_version_major = htobe32(1 + be32toh(h->header_version_major));
+ EXPECT_EQ(BVB_VERIFY_RESULT_INVALID_BOOT_IMAGE_HEADER,
+ bvb_verify_boot_image(boot_image_.data(), boot_image_.size(),
+ NULL, NULL));
+}
+
+
+TEST_F(VerifyTest, MinorVersionCheck) {
+ GenerateBootImage("", "", 0, base::FilePath(""));
+
+ BvbBootImageHeader *h =
+ reinterpret_cast<BvbBootImageHeader*>(boot_image_.data());
+ h->header_version_minor = htobe32(1 + be32toh(h->header_version_minor));
+ EXPECT_EQ(BVB_VERIFY_RESULT_OK_NOT_SIGNED,
+ bvb_verify_boot_image(boot_image_.data(), boot_image_.size(),
+ NULL, NULL));
+}
+
+TEST_F(VerifyTest, BlockSizesAddUpToLessThanLength) {
+ GenerateBootImage("SHA256_RSA2048", "", 0,
+ base::FilePath("test/testkey_rsa2048.pem"));
+
+ BvbBootImageHeader *h =
+ reinterpret_cast<BvbBootImageHeader*>(boot_image_.data());
+ BvbBootImageHeader backup = *h;
+
+ // Check that the sum of the three block lengths is less than passed
+ // in size. Use a size that's a multiple of 64 to avoid failure on
+ // earlier check.
+ uint64_t size = boot_image_.size() & (~0x3f);
+
+ h->authentication_data_block_size = htobe64(size);
+ EXPECT_EQ(BVB_VERIFY_RESULT_INVALID_BOOT_IMAGE_HEADER,
+ bvb_verify_boot_image(boot_image_.data(), boot_image_.size(),
+ NULL, NULL));
+ *h = backup;
+
+ h->auxilary_data_block_size = htobe64(size);
+ EXPECT_EQ(BVB_VERIFY_RESULT_INVALID_BOOT_IMAGE_HEADER,
+ bvb_verify_boot_image(boot_image_.data(), boot_image_.size(),
+ NULL, NULL));
+ *h = backup;
+
+ h->payload_data_block_size = htobe64(size);
+ EXPECT_EQ(BVB_VERIFY_RESULT_INVALID_BOOT_IMAGE_HEADER,
+ bvb_verify_boot_image(boot_image_.data(), boot_image_.size(),
+ NULL, NULL));
+ *h = backup;
+
+ // Overflow checks - choose overflow candidate so it's a multiple of
+ // 64 otherwise we'll fail on an earlier check.
+ size = 0xffffffffffffffc0UL;
+
+ h->authentication_data_block_size = htobe64(size);
+ EXPECT_EQ(BVB_VERIFY_RESULT_INVALID_BOOT_IMAGE_HEADER,
+ bvb_verify_boot_image(boot_image_.data(), boot_image_.size(),
+ NULL, NULL));
+ *h = backup;
+
+ h->auxilary_data_block_size = htobe64(size);
+ EXPECT_EQ(BVB_VERIFY_RESULT_INVALID_BOOT_IMAGE_HEADER,
+ bvb_verify_boot_image(boot_image_.data(), boot_image_.size(),
+ NULL, NULL));
+ *h = backup;
+
+ h->payload_data_block_size = htobe64(size);
+ EXPECT_EQ(BVB_VERIFY_RESULT_INVALID_BOOT_IMAGE_HEADER,
+ bvb_verify_boot_image(boot_image_.data(), boot_image_.size(),
+ NULL, NULL));
+ *h = backup;
+
+ EXPECT_EQ(BVB_VERIFY_RESULT_OK,
+ bvb_verify_boot_image(boot_image_.data(), boot_image_.size(),
+ NULL, NULL));
+}
+
+TEST_F(VerifyTest, BlockSizesMultipleOf64) {
+ GenerateBootImage("SHA256_RSA2048", "", 0,
+ base::FilePath("test/testkey_rsa2048.pem"));
+
+ BvbBootImageHeader *h =
+ reinterpret_cast<BvbBootImageHeader*>(boot_image_.data());
+ BvbBootImageHeader backup = *h;
+
+ h->authentication_data_block_size =
+ htobe32(be32toh(h->authentication_data_block_size) - 32);
+ EXPECT_EQ(BVB_VERIFY_RESULT_INVALID_BOOT_IMAGE_HEADER,
+ bvb_verify_boot_image(boot_image_.data(), boot_image_.size() - 32,
+ NULL, NULL));
+ *h = backup;
+
+ h->auxilary_data_block_size =
+ htobe32(be32toh(h->auxilary_data_block_size) - 32);
+ EXPECT_EQ(BVB_VERIFY_RESULT_INVALID_BOOT_IMAGE_HEADER,
+ bvb_verify_boot_image(boot_image_.data(), boot_image_.size() - 32,
+ NULL, NULL));
+ *h = backup;
+
+ EXPECT_EQ(BVB_VERIFY_RESULT_OK,
+ bvb_verify_boot_image(boot_image_.data(), boot_image_.size(),
+ NULL, NULL));
+}
+
+TEST_F(VerifyTest, HashOutOfBounds) {
+ GenerateBootImage("SHA256_RSA2048", "", 0,
+ base::FilePath("test/testkey_rsa2048.pem"));
+
+ BvbBootImageHeader *h =
+ reinterpret_cast<BvbBootImageHeader*>(boot_image_.data());
+
+ // Check we catch when hash data goes out of bounds.
+ h->hash_offset = htobe64(4);
+ h->hash_size = htobe64(be64toh(h->authentication_data_block_size));
+ EXPECT_EQ(BVB_VERIFY_RESULT_INVALID_BOOT_IMAGE_HEADER,
+ bvb_verify_boot_image(boot_image_.data(), boot_image_.size(),
+ NULL, NULL));
+
+ // Overflow checks.
+ h->hash_offset = htobe64(4);
+ h->hash_size = htobe64(0xfffffffffffffffeUL);
+ EXPECT_EQ(BVB_VERIFY_RESULT_INVALID_BOOT_IMAGE_HEADER,
+ bvb_verify_boot_image(boot_image_.data(), boot_image_.size(),
+ NULL, NULL));
+}
+
+TEST_F(VerifyTest, SignatureOutOfBounds) {
+ GenerateBootImage("SHA256_RSA2048", "", 0,
+ base::FilePath("test/testkey_rsa2048.pem"));
+
+ BvbBootImageHeader *h =
+ reinterpret_cast<BvbBootImageHeader*>(boot_image_.data());
+
+ // Check we catch when signature data goes out of bounds.
+ h->signature_offset = htobe64(4);
+ h->signature_size = htobe64(be64toh(h->authentication_data_block_size));
+ EXPECT_EQ(BVB_VERIFY_RESULT_INVALID_BOOT_IMAGE_HEADER,
+ bvb_verify_boot_image(boot_image_.data(), boot_image_.size(),
+ NULL, NULL));
+
+ // Overflow checks.
+ h->signature_offset = htobe64(4);
+ h->signature_size = htobe64(0xfffffffffffffffeUL);
+ EXPECT_EQ(BVB_VERIFY_RESULT_INVALID_BOOT_IMAGE_HEADER,
+ bvb_verify_boot_image(boot_image_.data(), boot_image_.size(),
+ NULL, NULL));
+}
+
+TEST_F(VerifyTest, PublicKeyOutOfBounds) {
+ GenerateBootImage("SHA256_RSA2048", "", 0,
+ base::FilePath("test/testkey_rsa2048.pem"));
+
+ BvbBootImageHeader *h =
+ reinterpret_cast<BvbBootImageHeader*>(boot_image_.data());
+
+ // Check we catch when public key data goes out of bounds.
+ h->public_key_offset = htobe64(4);
+ h->public_key_size = htobe64(be64toh(h->auxilary_data_block_size));
+ EXPECT_EQ(BVB_VERIFY_RESULT_INVALID_BOOT_IMAGE_HEADER,
+ bvb_verify_boot_image(boot_image_.data(), boot_image_.size(),
+ NULL, NULL));
+
+ // Overflow checks.
+ h->public_key_offset = htobe64(4);
+ h->public_key_size = htobe64(0xfffffffffffffffeUL);
+ EXPECT_EQ(BVB_VERIFY_RESULT_INVALID_BOOT_IMAGE_HEADER,
+ bvb_verify_boot_image(boot_image_.data(), boot_image_.size(),
+ NULL, NULL));
+}
+
+TEST_F(VerifyTest, KernelOutOfBounds) {
+ GenerateBootImage("SHA256_RSA2048", "", 0,
+ base::FilePath("test/testkey_rsa2048.pem"));
+
+ BvbBootImageHeader *h =
+ reinterpret_cast<BvbBootImageHeader*>(boot_image_.data());
+
+ // Check we catch when kernel data goes out of bounds.
+ h->kernel_offset = htobe64(4);
+ h->kernel_size = htobe64(be64toh(h->payload_data_block_size));
+ EXPECT_EQ(BVB_VERIFY_RESULT_INVALID_BOOT_IMAGE_HEADER,
+ bvb_verify_boot_image(boot_image_.data(), boot_image_.size(),
+ NULL, NULL));
+
+ // Overflow checks.
+ h->kernel_offset = htobe64(4);
+ h->kernel_size = htobe64(0xfffffffffffffffeUL);
+ EXPECT_EQ(BVB_VERIFY_RESULT_INVALID_BOOT_IMAGE_HEADER,
+ bvb_verify_boot_image(boot_image_.data(), boot_image_.size(),
+ NULL, NULL));
+}
+
+TEST_F(VerifyTest, InitrdOutOfBounds) {
+ GenerateBootImage("SHA256_RSA2048", "", 0,
+ base::FilePath("test/testkey_rsa2048.pem"));
+
+ BvbBootImageHeader *h =
+ reinterpret_cast<BvbBootImageHeader*>(boot_image_.data());
+
+ // Check we catch when initrd data goes out of bounds.
+ h->initrd_offset = htobe64(4);
+ h->initrd_size = htobe64(be64toh(h->payload_data_block_size));
+ EXPECT_EQ(BVB_VERIFY_RESULT_INVALID_BOOT_IMAGE_HEADER,
+ bvb_verify_boot_image(boot_image_.data(), boot_image_.size(),
+ NULL, NULL));
+
+ // Overflow checks.
+ h->initrd_offset = htobe64(4);
+ h->initrd_size = htobe64(0xfffffffffffffffeUL);
+ EXPECT_EQ(BVB_VERIFY_RESULT_INVALID_BOOT_IMAGE_HEADER,
+ bvb_verify_boot_image(boot_image_.data(), boot_image_.size(),
+ NULL, NULL));
+}
+
+TEST_F(VerifyTest, InvalidAlgorithmField) {
+ GenerateBootImage("SHA256_RSA2048", "", 0,
+ base::FilePath("test/testkey_rsa2048.pem"));
+
+ BvbBootImageHeader *h =
+ reinterpret_cast<BvbBootImageHeader*>(boot_image_.data());
+ BvbBootImageHeader backup = *h;
+
+ // Check we bail on unknown algorithm.
+ h->algorithm_type = htobe32(_BVB_ALGORITHM_NUM_TYPES);
+ EXPECT_EQ(BVB_VERIFY_RESULT_INVALID_BOOT_IMAGE_HEADER,
+ bvb_verify_boot_image(boot_image_.data(), boot_image_.size(),
+ NULL, NULL));
+ *h = backup;
+ EXPECT_EQ(BVB_VERIFY_RESULT_OK,
+ bvb_verify_boot_image(boot_image_.data(), boot_image_.size(),
+ NULL, NULL));
+}
+
+TEST_F(VerifyTest, PublicKeyBlockTooSmall) {
+ GenerateBootImage("SHA256_RSA2048", "", 0,
+ base::FilePath("test/testkey_rsa2048.pem"));
+
+ BvbBootImageHeader *h =
+ reinterpret_cast<BvbBootImageHeader*>(boot_image_.data());
+ BvbBootImageHeader backup = *h;
+
+ // Check we bail if the auxilary data block is too small.
+ uint64_t change = be64toh(h->auxilary_data_block_size) - 64;
+ h->auxilary_data_block_size = htobe64(change);
+ EXPECT_EQ(BVB_VERIFY_RESULT_INVALID_BOOT_IMAGE_HEADER,
+ bvb_verify_boot_image(boot_image_.data(),
+ boot_image_.size() - change,
+ NULL, NULL));
+ *h = backup;
+ EXPECT_EQ(BVB_VERIFY_RESULT_OK,
+ bvb_verify_boot_image(boot_image_.data(), boot_image_.size(),
+ NULL, NULL));
+}
+
+bool VerifyTest::test_modification(BvbVerifyResult expected_result,
+ size_t offset, size_t length) {
+ uint8_t *d = reinterpret_cast<uint8_t*>(boot_image_.data());
+ const int kNumCheckpoints = 16;
+
+ // Test |kNumCheckpoints| modifications in the start, middle, and
+ // end of given sub-array.
+ for (int n = 0; n <= kNumCheckpoints; n++) {
+ size_t o = std::min(length*n/kNumCheckpoints, length - 1) + offset;
+ d[o] ^= 0x80;
+ BvbVerifyResult result = bvb_verify_boot_image(boot_image_.data(),
+ boot_image_.size(),
+ NULL, NULL);
+ d[o] ^= 0x80;
+ if (result != expected_result)
+ return false;
+ }
+
+ return true;
+}
+
+TEST_F(VerifyTest, ModificationDetection) {
+ GenerateBootImage("SHA256_RSA2048", "", 0,
+ base::FilePath("test/testkey_rsa2048.pem"));
+
+ EXPECT_EQ(BVB_VERIFY_RESULT_OK,
+ bvb_verify_boot_image(boot_image_.data(), boot_image_.size(),
+ NULL, NULL));
+
+ BvbBootImageHeader h;
+ bvb_boot_image_header_to_host_byte_order(
+ reinterpret_cast<BvbBootImageHeader*>(boot_image_.data()), &h);
+
+ size_t header_block_offset = 0;
+ size_t authentication_block_offset = header_block_offset + sizeof(BvbBootImageHeader);
+ size_t auxilary_block_offset = authentication_block_offset + h.authentication_data_block_size;
+ size_t payload_block_offset = auxilary_block_offset + h.auxilary_data_block_size;
+
+ // Ensure we detect modification of the header data block. Do this
+ // in a field that's not validated so INVALID_BOOT_IMAGE_HEADER
+ // isn't returned.
+ EXPECT_TRUE(test_modification(BVB_VERIFY_RESULT_HASH_MISMATCH,
+ offsetof(BvbBootImageHeader, kernel_cmdline),
+ BVB_KERNEL_CMDLINE_MAX_LEN));
+ // Also check the |reserved| field.
+ EXPECT_TRUE(test_modification(BVB_VERIFY_RESULT_HASH_MISMATCH,
+ offsetof(BvbBootImageHeader, reserved),
+ sizeof(BvbBootImageHeader().reserved)));
+
+ // Ensure we detect modifications in the auxilary data block.
+ EXPECT_TRUE(test_modification(BVB_VERIFY_RESULT_HASH_MISMATCH,
+ auxilary_block_offset,
+ h.auxilary_data_block_size));
+
+ // Ensure we detect modifications in the payload key data block.
+ EXPECT_TRUE(test_modification(BVB_VERIFY_RESULT_HASH_MISMATCH,
+ payload_block_offset,
+ h.payload_data_block_size));
+
+ // Modifications in the hash part of the Authentication data block
+ // should also yield HASH_MISMATCH. This is because the hash check
+ // compares the calculated hash against the stored hash.
+ EXPECT_TRUE(test_modification(BVB_VERIFY_RESULT_HASH_MISMATCH,
+ authentication_block_offset + h.hash_offset,
+ h.hash_size));
+
+ // Modifications in the signature part of the Authentication data
+ // block, should not cause a hash mismatch ... but will cause a
+ // signature mismatch.
+ EXPECT_TRUE(test_modification(BVB_VERIFY_RESULT_SIGNATURE_MISMATCH,
+ authentication_block_offset +
+ h.signature_offset,
+ h.signature_size));
+
+ // Mofications outside the hash and signature parts of the
+ // Authentication data block are not detected. This is because it's
+ // not part of the hash calculation.
+ uint64_t offset = h.signature_offset + h.signature_size;
+ ASSERT_LT(h.hash_offset, h.signature_offset);
+ ASSERT_LT(offset + 1, h.authentication_data_block_size);
+ EXPECT_TRUE(test_modification(BVB_VERIFY_RESULT_OK,
+ authentication_block_offset + offset,
+ h.authentication_data_block_size - offset));
+}
diff --git a/bvbtool b/bvbtool
new file mode 100755
index 0000000..d91069d
--- /dev/null
+++ b/bvbtool
@@ -0,0 +1,1083 @@
+#!/usr/bin/env python
+
+# Copyright 2016, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Command-line tool for working with Brillo Verified Boot images."""
+
+import argparse
+import hashlib
+import os
+import struct
+import subprocess
+import sys
+
+import Crypto.PublicKey.RSA
+
+# Keep in sync with bvb_boot_image_header.h.
+BVB_VERSION_MAJOR = 1
+BVB_VERSION_MINOR = 0
+
+
+class Algorithm(object):
+ """Contains details about an algorithm.
+
+ See the bvb_boot_image_header.h file for more details about
+ algorithms.
+
+ The constant |ALGORITHMS| is a dictionary from human-readable
+ names (e.g 'SHA256_RSA2048') to instances of this class.
+
+ Attributes:
+ algorithm_type: Integer code corresponding to |BvbAlgorithmType|.
+ hash_num_bytes: Number of bytes used to store the hash.
+ signature_num_bytes: Number of bytes used to store the signature.
+ public_key_num_bytes: Number of bytes used to store the public key.
+ padding: Padding used for signature, if any.
+ """
+
+ def __init__(self, algorithm_type, hash_num_bytes, signature_num_bytes,
+ public_key_num_bytes, padding):
+ self.algorithm_type = algorithm_type
+ self.hash_num_bytes = hash_num_bytes
+ self.signature_num_bytes = signature_num_bytes
+ self.public_key_num_bytes = public_key_num_bytes
+ self.padding = padding
+
+# This must be kept in sync with bvb_verify.h.
+ALGORITHMS = {
+ 'NONE': Algorithm(
+ algorithm_type=0, # BVB_ALGORITHM_TYPE_NONE
+ hash_num_bytes=0,
+ signature_num_bytes=0,
+ public_key_num_bytes=0,
+ padding=[]),
+ 'SHA256_RSA2048': Algorithm(
+ algorithm_type=1, # BVB_ALGORITHM_TYPE_SHA256_RSA2048
+ hash_num_bytes=32,
+ signature_num_bytes=256,
+ public_key_num_bytes=8 + 2*2048/8,
+ padding=[
+ # PKCS1-v1_5 padding
+ 0x00, 0x01] + [0xff]*202 + [0x00] + [
+ # ASN.1 header
+ 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
+ 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
+ 0x00, 0x04, 0x20,
+ ]),
+ 'SHA256_RSA4096': Algorithm(
+ algorithm_type=2, # BVB_ALGORITHM_TYPE_SHA256_RSA4096
+ hash_num_bytes=32,
+ signature_num_bytes=512,
+ public_key_num_bytes=8 + 2*4096/8,
+ padding=[
+ # PKCS1-v1_5 padding
+ 0x00, 0x01] + [0xff]*458 + [0x00] + [
+ # ASN.1 header
+ 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
+ 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
+ 0x00, 0x04, 0x20,
+ ]),
+ 'SHA256_RSA8192': Algorithm(
+ algorithm_type=3, # BVB_ALGORITHM_TYPE_SHA256_RSA8192
+ hash_num_bytes=32,
+ signature_num_bytes=1024,
+ public_key_num_bytes=8 + 2*8192/8,
+ padding=[
+ # PKCS1-v1_5 padding
+ 0x00, 0x01] + [0xff]*970 + [0x00] + [
+ # ASN.1 header
+ 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
+ 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
+ 0x00, 0x04, 0x20,
+ ]),
+ 'SHA512_RSA2048': Algorithm(
+ algorithm_type=4, # BVB_ALGORITHM_TYPE_SHA512_RSA2048
+ hash_num_bytes=64,
+ signature_num_bytes=256,
+ public_key_num_bytes=8 + 2*2048/8,
+ padding=[
+ # PKCS1-v1_5 padding
+ 0x00, 0x01] + [0xff]*170 + [0x00] + [
+ # ASN.1 header
+ 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
+ 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
+ 0x00, 0x04, 0x40
+ ]),
+ 'SHA512_RSA4096': Algorithm(
+ algorithm_type=5, # BVB_ALGORITHM_TYPE_SHA512_RSA4096
+ hash_num_bytes=64,
+ signature_num_bytes=512,
+ public_key_num_bytes=8 + 2*4096/8,
+ padding=[
+ # PKCS1-v1_5 padding
+ 0x00, 0x01] + [0xff]*426 + [0x00] + [
+ # ASN.1 header
+ 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
+ 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
+ 0x00, 0x04, 0x40
+ ]),
+ 'SHA512_RSA8192': Algorithm(
+ algorithm_type=6, # BVB_ALGORITHM_TYPE_SHA512_RSA8192
+ hash_num_bytes=64,
+ signature_num_bytes=1024,
+ public_key_num_bytes=8 + 2*8192/8,
+ padding=[
+ # PKCS1-v1_5 padding
+ 0x00, 0x01] + [0xff]*938 + [0x00] + [
+ # ASN.1 header
+ 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
+ 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
+ 0x00, 0x04, 0x40
+ ]),
+}
+
+
+def round_to_multiple(number, size):
+ """Rounds a number up to nearest multiple of another number.
+
+ Args:
+ number: The number to round up.
+ size: The multiple to round up to.
+
+ Returns:
+ If |number| is a multiple of |size|, returns |number|, otherwise
+ returns |number| + |size|.
+ """
+ remainder = number % size
+ if remainder == 0:
+ return number
+ return number + size - remainder
+
+
+def round_to_pow2(number):
+ """Rounds a number up to the next power of 2.
+
+ Args:
+ number: The number to round up.
+
+ Returns:
+ If |number| is already a power of 2 then |number| is
+ returned. Otherwise the smallest power of 2 greater than |number|
+ is returned.
+ """
+ return 2**((number - 1).bit_length())
+
+
+def write_long(output, num_bits, value):
+ """Writes a long to an output stream using a given amount of bits.
+
+ This number is written big-endian, e.g. with the most significant
+ bit first.
+
+ Arguments:
+ output: The object to write the output to.
+ num_bits: The number of bits to write, e.g. 2048.
+ value: The value to write.
+ """
+ for bit_pos in range(num_bits, 0, -8):
+ octet = (value >> (bit_pos - 8)) & 0xff
+ output.write(struct.pack('!B', octet))
+
+
+def egcd(a, b):
+ """Calculate greatest common divisor of two numbers.
+
+ This implementation uses a recursive version of the extended
+ Euclidian algorithm.
+
+ Arguments:
+ a: First number.
+ b: Second number.
+
+ Returns:
+ A tuple (gcd, x, y) that where |gcd| is the greatest common
+ divisor of |a| and |b| and |a|*|x| + |b|*|y| = |gcd|.
+ """
+ if a == 0:
+ return (b, 0, 1)
+ else:
+ g, y, x = egcd(b % a, a)
+ return (g, x - (b // a) * y, y)
+
+
+def modinv(a, m):
+ """Calculate modular multiplicative inverse of |a| modulo |m|.
+
+ This calculates the number |x| such that |a| * |x| == 1 (modulo
+ |m|). This number only exists if |a| and |m| are co-prime - |None|
+ is returned if this isn't true.
+
+ Arguments:
+ a: The number to calculate a modular inverse of.
+ m: The modulo to use.
+
+ Returns:
+ The modular multiplicative inverse of |a| and |m| or |None| if
+ these numbers are not co-prime.
+ """
+ gcd, x, _ = egcd(a, m)
+ if gcd != 1:
+ return None # modular inverse does not exist
+ else:
+ return x % m
+
+
+def parse_number(string):
+ """Parse a string as a number.
+
+ This is just a short-hand for int(string, 0) suitable for use in the
+ |type| parameter of |ArgumentParser|'s add_argument() function. An
+ improvement to just using type=int is that this function supports
+ numbers in other bases, e.g. "0x1234".
+
+ Arguments:
+ string: The string to parse.
+
+ Returns:
+ The parsed integer.
+
+ Raises:
+ ValueError: If the number could not be parsed.
+ """
+ return int(string, 0)
+
+
+def write_rsa_key(output, key):
+ """Writes a public RSA key in |BvBRSAPublicKeyHeader| format.
+
+ This writes the |BvBRSAPublicKeyHeader| as well as the two large
+ numbers (|key_num_bits| bits long) following it.
+
+ Arguments:
+ output: The object to write the output to.
+ key: A Crypto.PublicKey.RSA object.
+ """
+ # key.e is exponent
+ # key.n is modulus
+ key_num_bits = key.size() + 1
+ # Calculate n0inv = -1/n[0] (mod 2^32)
+ b = 2L**32
+ n0inv = b - modinv(key.n, b)
+ # Calculate rr = r^2 (mod N), where r = 2^(# of key bits)
+ r = 2L**key.n.bit_length()
+ rrmodn = r * r % key.n
+ output.write(struct.pack('!II', key_num_bits, n0inv))
+ write_long(output, key_num_bits, key.n)
+ write_long(output, key_num_bits, rrmodn)
+
+
+def lookup_algorithm_by_type(alg_type):
+ """Looks up algorithm by type.
+
+ Arguments:
+ alg_type: The integer representing the type.
+
+ Returns:
+ A tuple with the algorithm name and an |Algorithm| instance.
+
+ Raises:
+ Exception: If the algorithm cannot be found
+ """
+ for alg_name in ALGORITHMS:
+ alg_data = ALGORITHMS[alg_name]
+ if alg_data.algorithm_type == alg_type:
+ return (alg_name, alg_data)
+ raise Exception('Unknown algorithm type %d' % alg_type)
+
+
+def add_property(encoded_props, key, value):
+ """Helper function to add a key/value pair to a bytearray.
+
+ The encoding specified in the |BvbPropertyHeader| in
+ bvb_boot_image_header.h is used.
+
+ Arguments:
+ encoded_props: A bytearray to append to.
+ key: The key to write.
+ value: The value to write.
+ """
+ encoded_props.extend(struct.pack('!QQ', len(key), len(value)))
+ encoded_props.extend(key)
+ encoded_props.append(0)
+ encoded_props.extend(value)
+ encoded_props.append(0)
+ num_bytes = 2 * 8 + len(key) + len(value) + 2
+ padding_bytes = (8 - num_bytes) & 7
+ for _ in range(padding_bytes):
+ encoded_props.append(0)
+
+
+class BvbIntegrityFooter(object):
+ """A class for parsing and writing Integrity Footers.
+
+ Integrity footers are stored at the end of filesystems where
+ dm-verity hashes have been added with the 'add_image_hashes'
+ command.
+
+ Attributes:
+ magic: Magic for identifying the footer, see |MAGIC|.
+ version_major: The major version of bvbtool that wrote the footer.
+ version_minor: The minor version of bvbtool that wrote the footer.
+ dm_verity_version: dm-verity version used.
+ image_size: Size of the image, after rounding up to |block_size|.
+ tree_offset: Offset of the hash tree in the file.
+ tree_size: Size of the tree.
+ data_block_size: Data block size
+ hash_block_size: Hash block size
+ hash_algorithm: Hash algorithm used.
+ salt_hex: Salt used, as a hex-string
+ root_hash_hex: Root hash, as a hex-string.
+ """
+
+ MAGIC = 'BVBi'
+ SIZE = 4096
+ RESERVED = 1968
+ FORMAT_STRING = ('!4s2L' # magic, 2 x version
+ 'L' # dm-verity version used
+ 'Q' # image size (bytes)
+ 'Q' # tree offset (bytes)
+ 'Q' # tree size (bytes)
+ 'L' # data block size (bytes)
+ 'L' # hash block size (bytes)
+ '32s' # hash algorithm used
+ '1024s' # salt, as a hex string
+ '1024s' + # root hash, as a hex string
+ str(RESERVED) + 'x') # padding for reserved bytes
+
+ def __init__(self, data=None):
+ """Initializes a new footer object.
+
+ Arguments:
+ data: If not None, must be a bytearray of size 4096.
+
+ Raises:
+ LookupError: If the given footer is malformed.
+ struct.error: If the given data has no footer.
+ """
+ assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
+
+ if data:
+ (self.magic, self.version_major, self.version_minor,
+ self.dm_verity_version, self.image_size, self.tree_offset,
+ self.tree_size, self.data_block_size, self.hash_block_size,
+ self.hash_algorithm, self.salt_hex,
+ self.root_hash_hex) = struct.unpack(self.FORMAT_STRING, data)
+ if self.magic != self.MAGIC:
+ raise LookupError('Given data does not look like a Brillo '
+ 'integrity footer.')
+ # Nuke NUL-bytes at the end.
+ self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
+ self.salt_hex = self.salt_hex.split('\0', 1)[0]
+ self.root_hash_hex = self.root_hash_hex.split('\0', 1)[0]
+ else:
+ self.magic = self.MAGIC
+ self.version_major = BVB_VERSION_MAJOR
+ self.version_minor = BVB_VERSION_MINOR
+ self.dm_verity_version = 0
+ self.image_size = 0
+ self.tree_offset = 0
+ self.tree_size = 0
+ self.data_block_size = 0
+ self.hash_block_size = 0
+ self.hash_algorithm = ''
+ self.salt_hex = ''
+ self.root_hash_hex = ''
+
+ def save(self, output):
+ """Serializes the header (4096 bytes) to disk.
+
+ Arguments:
+ output: The object to write the output to.
+ """
+ output.write(struct.pack(
+ self.FORMAT_STRING, self.magic, self.version_major, self.version_minor,
+ self.dm_verity_version, self.image_size, self.tree_offset,
+ self.tree_size, self.data_block_size, self.hash_block_size,
+ self.hash_algorithm, self.salt_hex, self.root_hash_hex))
+
+
+class BvbHeader(object):
+ """A class for parsing and writing Brillo Verified Boot headers.
+
+ Attributes:
+ The attributes correspond to the |BvBBootImageHeader| struct
+ defined in bvb_boot_image_header.h.
+ """
+
+ SIZE = 8192
+
+ # Keep in sync with |reserved| field of |BvbBootImageHeader|.
+ RESERVED = 3936
+
+ # Keep in sync with |BvbBootImageHeader|.
+ FORMAT_STRING = ('!4s2L' # magic, 2 x version
+ '3Q' # 3 x block size
+ 'L' # algorithm type
+ '2Q' # offset, size (hash)
+ '2Q' # offset, size (signature)
+ '2Q' # offset, size (public key)
+ '2Q' # offset, size (properties)
+ 'Q' # rollback_index
+ '2Q' # offset, size (kernel)
+ '2Q' # offset, size (initrd)
+ 'Q' # kernel load address
+ 'Q' # initrd load address
+ '4096s' + # cmdline
+ str(RESERVED) + 'x') # padding for reserved bytes
+
+ def __init__(self, data=None):
+ """Initializes a new header object.
+
+ Arguments:
+ data: If not None, must be a bytearray of size 8192.
+
+ Raises:
+ Exception: If the given data is malformed.
+ """
+ assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
+
+ if data:
+ (self.magic, self.header_version_major, self.header_version_minor,
+ self.authentication_data_block_size, self.auxilary_data_block_size,
+ self.payload_data_block_size, self.algorithm_type, self.hash_offset,
+ self.hash_size, self.signature_offset, self.signature_size,
+ self.public_key_offset, self.public_key_size, self.properties_offset,
+ self.properties_size, self.rollback_index, self.kernel_offset,
+ self.kernel_size, self.initrd_offset, self.initrd_size,
+ self.kernel_address, self.initrd_address,
+ self.kernel_cmdline) = struct.unpack(self.FORMAT_STRING, data)
+ # Nuke NUL-bytes at the end of the string.
+ self.kernel_cmdline = self.kernel_cmdline.split('\0', 1)[0]
+ if self.magic != 'BVB0':
+ raise Exception('Given image does not look like a Brillo boot image')
+ else:
+ self.magic = 'BVB0'
+ self.header_version_major = BVB_VERSION_MAJOR
+ self.header_version_minor = BVB_VERSION_MINOR
+ self.authentication_data_block_size = 0
+ self.auxilary_data_block_size = 0
+ self.payload_data_block_size = 0
+ self.algorithm_type = 0
+ self.hash_offset = 0
+ self.hash_size = 0
+ self.signature_offset = 0
+ self.signature_size = 0
+ self.public_key_offset = 0
+ self.public_key_size = 0
+ self.properties_offset = 0
+ self.properties_size = 0
+ self.rollback_index = 0
+ self.kernel_offset = 0
+ self.kernel_size = 0
+ self.initrd_offset = 0
+ self.initrd_size = 0
+ self.kernel_address = 0
+ self.initrd_address = 0
+ self.kernel_cmdline = ''
+
+ def save(self, output):
+ """Serializes the header (8192 bytes) to disk.
+
+ Arguments:
+ output: The object to write the output to.
+ """
+ output.write(struct.pack(
+ self.FORMAT_STRING, self.magic, self.header_version_major,
+ self.header_version_minor, self.authentication_data_block_size,
+ self.auxilary_data_block_size, self.payload_data_block_size,
+ self.algorithm_type, self.hash_offset, self.hash_size,
+ self.signature_offset, self.signature_size, self.public_key_offset,
+ self.public_key_size, self.properties_offset, self.properties_size,
+ self.rollback_index, self.kernel_offset, self.kernel_size,
+ self.initrd_offset, self.initrd_size, self.kernel_address,
+ self.initrd_address, self.kernel_cmdline))
+
+
+class BvbTool(object):
+ """Object for bvbtool."""
+
+ def __init__(self):
+ """Initiailzes the object."""
+ parser = argparse.ArgumentParser(usage="""bvbtool COMMAND [<args>]
+
+Commands:
+ version Prints out version of bvbtool.
+ make_boot_image Make boot image.
+ sign_boot_image Sign boot image.
+ info_boot_image Show information about boot image.
+ add_image_hashes Add hashes for integrity-checking to image.
+ info_image_hashes Show information about integrity-checking hashes.
+ extract_public_key Extract public key.
+
+""")
+ parser.add_argument('COMMAND', help='The command to run')
+ args = parser.parse_args(sys.argv[1:2])
+ if not hasattr(self, args.COMMAND):
+ print 'Unrecognized command'
+ parser.print_help()
+ sys.exit(1)
+ getattr(self, args.COMMAND)()
+
+ def version(self):
+ """Implements the 'version' command."""
+ print '%d.%d' % (BVB_VERSION_MAJOR, BVB_VERSION_MINOR)
+
+ def info_boot_image(self):
+ """Implements the 'info_boot_image' command."""
+ parser = argparse.ArgumentParser(
+ prog='bvbtool info_boot_image',
+ description='Show information about Brillo boot image.')
+ parser.add_argument('--image',
+ help='Brillo boot image to use',
+ type=argparse.FileType('rb'),
+ required=True)
+ parser.add_argument('--output',
+ help='Write info to file',
+ type=argparse.FileType('wt'),
+ default=sys.stdout)
+ args = parser.parse_args(sys.argv[2:])
+
+ h = BvbHeader(args.image.read(BvbHeader.SIZE))
+
+ (alg_name, _) = lookup_algorithm_by_type(h.algorithm_type)
+
+ o = args.output
+ o.write('Boot Image version: %d.%d\n' %
+ (h.header_version_major, h.header_version_minor))
+ o.write('Header Block: %d bytes\n' % BvbHeader.SIZE)
+ o.write('Authentication Block: %d bytes\n' %
+ h.authentication_data_block_size)
+ o.write('Auxilary Block: %d bytes\n' % h.auxilary_data_block_size)
+ o.write('Payload Block: %d bytes\n' % h.payload_data_block_size)
+ o.write('Algorithm: %s\n' % alg_name)
+ o.write('Rollback Index: %d\n' % h.rollback_index)
+ o.write('Kernel: %d bytes\n' % h.kernel_size)
+ o.write('Initrd: %d bytes\n' % h.initrd_size)
+ o.write('Kernel Load Address: 0x%08x\n' % h.kernel_address)
+ o.write('Initrd Load Address: 0x%08x\n' % h.initrd_address)
+ o.write('Kernel Cmdline: %s\n' % h.kernel_cmdline)
+
+ # Print properties.
+ o.write('Properties:\n')
+ authentication_block_offset = BvbHeader.SIZE
+ auxilary_block_offset = (
+ authentication_block_offset + h.authentication_data_block_size)
+ prop_start_offset = auxilary_block_offset + h.properties_offset
+ prop_end_offset = prop_start_offset + h.properties_size
+ args.image.seek(prop_start_offset)
+ num_printed = 0
+ while args.image.tell() < prop_end_offset:
+ (key_len, value_len) = struct.unpack('!2Q', args.image.read(16))
+ num_bytes = key_len + value_len + 2
+ padding_bytes = (8 - num_bytes) & 7
+ prop_data = args.image.read(num_bytes + padding_bytes)
+ key = prop_data[0:key_len]
+ # Avoid printing large property values (e.g. blobs).
+ if value_len >= 256:
+ o.write(' %s: (%d bytes)\n' % (key, value_len))
+ else:
+ value = prop_data[key_len + 1:key_len + 1 + value_len]
+ o.write(' %s: %s\n' % (key, repr(value)))
+ num_printed += 1
+ if num_printed == 0:
+ o.write(' (none)\n')
+
+ def make_boot_image(self):
+ """Implements the 'make_boot_image' command."""
+ parser = argparse.ArgumentParser(prog='bvbtool make_boot_image',
+ description='Make Brillo boot image.')
+
+ parser.add_argument('--kernel',
+ help='Path to kernel',
+ type=argparse.FileType('rb')) #, required=True)
+ parser.add_argument('--initrd',
+ help='Path to ramdisk',
+ type=argparse.FileType('rb'))
+ parser.add_argument('--kernel_address',
+ help='Kernel load address',
+ type=parse_number,
+ default=0x10008000)
+ parser.add_argument('--initrd_address',
+ help='Ramdisk load address',
+ type=parse_number,
+ default=0x11000000)
+ parser.add_argument('--kernel_cmdline',
+ help='Kernel command-line',
+ default='')
+ parser.add_argument('--rootfs_with_hashes',
+ help='Setup dm-verity for given rootfs',
+ type=argparse.FileType('rb'))
+ parser.add_argument('--rollback_index',
+ help='Rollback Index',
+ type=parse_number,
+ default=0)
+ parser.add_argument('--prop',
+ help='Add property',
+ metavar='KEY:VALUE',
+ action='append')
+ parser.add_argument('--prop_from_file',
+ help='Add property from file',
+ metavar='KEY:PATH',
+ action='append')
+ parser.add_argument('--output',
+ help='Output file name',
+ type=argparse.FileType('wb'),
+ required=True)
+ args = parser.parse_args(sys.argv[2:])
+
+ h = BvbHeader()
+
+ # Write header data block and leave ample room for hash, signature
+ # and public key.
+ h.authentication_data_block_size = 4096
+ h.kernel_offset = 0
+ h.kernel_size = os.fstat(args.kernel.fileno()).st_size
+ h.initrd_offset = 0
+ h.initrd_size = 0
+ if args.initrd:
+ h.initrd_offset = h.kernel_offset + h.kernel_size
+ h.initrd_size = os.fstat(args.initrd.fileno()).st_size
+ h.payload_data_block_size = h.kernel_size + h.initrd_size
+
+ # Generate properties blob.
+ encoded_props = bytearray()
+ if args.prop:
+ for prop in args.prop:
+ idx = prop.find(':')
+ if idx == -1:
+ sys.stderr.write('Malformed --property value %s.\n', prop)
+ sys.exit(1)
+ key = prop[0:idx]
+ value = prop[(idx + 1):]
+ add_property(encoded_props, key, value)
+ if args.prop_from_file:
+ for prop in args.prop_from_file:
+ idx = prop.find(':')
+ if idx == -1:
+ sys.stderr.write('Malformed --property value %s.\n', prop)
+ sys.exit(1)
+ key = prop[0:idx]
+ file_path = prop[(idx + 1):]
+ value = open(file_path, 'rb').read()
+ add_property(encoded_props, key, value)
+
+ # We'll store the properties at offset 0 in the Auxiliary data
+ # block. Make sure it's big enough to hold the biggest
+ # possible key.
+ h.auxilary_data_block_size = round_to_multiple(
+ len(encoded_props) + 4096, 64)
+ h.properties_offset = 0
+ h.properties_size = len(encoded_props)
+
+ h.rollback_index = args.rollback_index
+ h.kernel_address = args.kernel_address
+ h.initrd_address = args.initrd_address
+ h.kernel_cmdline = args.kernel_cmdline
+
+ # Setup dm-verity, if requested.
+ if args.rootfs_with_hashes:
+ args.rootfs_with_hashes.seek(0, os.SEEK_END)
+ image_size = args.rootfs_with_hashes.tell()
+ args.rootfs_with_hashes.seek(image_size - BvbIntegrityFooter.SIZE)
+ f = BvbIntegrityFooter(args.rootfs_with_hashes.read(
+ BvbIntegrityFooter.SIZE))
+ c = 'dm="1 vroot none ro 1,'
+ c += '0 ' # start
+ c += '%d ' % (f.image_size / 512) # size (# sectors)
+ c += 'verity %d ' % f.dm_verity_version # type and version
+ c += 'PARTUUID=$(ANDROID_SYSTEM_PARTUUID) ' # data_dev
+ c += 'PARTUUID=$(ANDROID_SYSTEM_PARTUUID) ' # hash_dev
+ c += '%d ' % f.data_block_size # data_block
+ c += '%d ' % f.hash_block_size # hash_block
+ c += '%d ' % (f.image_size / f.data_block_size) # #blocks
+ c += '%d ' % (f.image_size / f.data_block_size) # hash_offset
+ c += '%s ' % f.hash_algorithm # hash_alg
+ c += '%s ' % f.root_hash_hex # root_digest
+ c += '%s' % f.salt_hex # salt
+ c += '"'
+ if h.kernel_cmdline:
+ c += ' '
+ h.kernel_cmdline = c + h.kernel_cmdline
+
+ # Save the header.
+ h.save(args.output)
+
+ # Write Authentication data block, as zeroes.
+ args.output.write(struct.pack(str(h.authentication_data_block_size) + 'x'))
+
+ # Write Auxilary data block. First properties, then pad with zeroes.
+ args.output.write(encoded_props)
+ args.output.write(struct.pack(str(h.auxilary_data_block_size - len(
+ encoded_props)) + 'x'))
+
+ # Write Payload data block: kernel and initrd
+ args.output.write(args.kernel.read())
+ if args.initrd:
+ args.output.write(args.initrd.read())
+
+ def sign_boot_image(self):
+ """Implements the 'sign_boot_image' command."""
+ parser = argparse.ArgumentParser(prog='bvbtool sign_boot_image',
+ description='Sign Brillo boot image.')
+ group = parser.add_argument_group()
+ group.add_argument('--show_algorithms',
+ help='Show avaiable algorithms',
+ action='store_true')
+ group = parser.add_argument_group()
+ group.add_argument('--image',
+ help='Brillo boot image to sign',
+ type=argparse.FileType('rab+'))
+ group.add_argument('--key', help='Path to RSA private key file')
+ group.add_argument('--algorithm', help='Algorithm to use')
+ args = parser.parse_args(sys.argv[2:])
+
+ if args.show_algorithms:
+ algs = []
+ for alg_name in ALGORITHMS:
+ if alg_name != 'NONE':
+ algs.append(alg_name)
+ algs.sort()
+ for alg in algs:
+ print alg
+ sys.exit(0)
+
+ # Support 'NONE' to avoid conditionals in build systems if
+ # signing is not needed (this way they can always execute
+ # 'bvbtool sign_boot_image --algorithm NONE')
+ if args.algorithm == 'NONE':
+ sys.exit(0)
+
+ if not args.algorithm:
+ sys.stderr.write('Option --algorithm is required.\n')
+ sys.exit(1)
+ if not args.image:
+ sys.stderr.write('Option --image is required.\n')
+ sys.exit(1)
+ if not args.key:
+ sys.stderr.write('Option --key is required.\n')
+ sys.exit(1)
+
+ try:
+ alg = ALGORITHMS[args.algorithm]
+ except IndexError:
+ sys.stderr.write('Unknown algorithm %s.\n' % args.algorithm)
+ sys.exit(1)
+
+ h = BvbHeader(args.image.read(BvbHeader.SIZE))
+
+ if h.properties_offset != 0:
+ # This is just an implementation limitation which can be lifted later.
+ sys.stderr.write('Only support images with props at the top.\n')
+ sys.exit(1)
+
+ if h.authentication_data_block_size < (
+ alg.hash_num_bytes + alg.signature_num_bytes):
+ sys.stderr.write('Insufficient room for storing hash and signature.\n')
+ sys.exit(1)
+
+ if h.auxilary_data_block_size < alg.public_key_num_bytes:
+ sys.stderr.write('Insufficient room for storing public key.\n')
+ sys.exit(1)
+
+ authentication_data_block_offset = BvbHeader.SIZE
+ auxilary_data_block_offset = (
+ authentication_data_block_offset + h.authentication_data_block_size)
+
+ # Update header with hash type/offset/size, signature
+ # type/offset/size, and public key size.
+ h.algorithm_type = alg.algorithm_type
+ # Hash offset and size (in Authentication data block).
+ h.hash_offset = 0
+ h.hash_size = alg.hash_num_bytes
+ # Signature offset and size - it's stored right after the hash
+ # (in Authentication data block).
+ h.signature_offset = alg.hash_num_bytes
+ h.signature_size = alg.signature_num_bytes
+ # Public key offset and size - follows properties (in Auxilary data block).
+ h.public_key_offset = h.properties_size
+ h.public_key_size = alg.public_key_num_bytes
+
+ # Extract public key and insert it into "Auxilary data" block.
+ key = Crypto.PublicKey.RSA.importKey(open(args.key).read())
+ args.image.seek(auxilary_data_block_offset + h.properties_size)
+ write_rsa_key(args.image, key)
+
+ # Save the updated header.
+ args.image.seek(0)
+ h.save(args.image)
+
+ # Calculate the hash.
+ if args.algorithm[0:6] == 'SHA256':
+ ha = hashlib.sha256()
+ elif args.algorithm[0:6] == 'SHA512':
+ ha = hashlib.sha512()
+ else:
+ sys.stderr.write('Unsupported algorithm.\n')
+ sys.exit(1)
+ args.image.seek(0)
+ ha.update(args.image.read(BvbHeader.SIZE))
+ args.image.seek(auxilary_data_block_offset)
+ ha.update(args.image.read(h.auxilary_data_block_size))
+ ha.update(args.image.read(h.payload_data_block_size))
+ # Write the hash
+ args.image.seek(authentication_data_block_offset)
+ binary_hash = ha.digest()
+ args.image.write(binary_hash)
+
+ # Calculate the signature.
+ p = subprocess.Popen(
+ ['openssl', 'rsautl', '-sign', '-inkey', args.key, '-raw'],
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ padding_and_hash = str(bytearray(alg.padding)) + binary_hash
+ (pout, perr) = p.communicate(padding_and_hash)
+ retcode = p.wait()
+ if retcode != 0:
+ sys.stderr.write('Error signing: %s\n' % perr)
+ sys.exit(1)
+ # Write the signature.
+ args.image.seek(authentication_data_block_offset + alg.hash_num_bytes)
+ args.image.write(pout)
+
+ def extract_public_key(self):
+ """Implements the 'extract_public_key' command."""
+ parser = argparse.ArgumentParser(
+ prog='bvbtool extract_public_key',
+ description=
+ 'Extract public key and dump it in the format used by Brillo.')
+ parser.add_argument('--key',
+ help='Path to RSA private key file',
+ required=True)
+ parser.add_argument('--output',
+ help='Output file name',
+ type=argparse.FileType('wb'),
+ required=True)
+ args = parser.parse_args(sys.argv[2:])
+
+ key = Crypto.PublicKey.RSA.importKey(open(args.key).read())
+ write_rsa_key(args.output, key)
+
+ def info_image_hashes(self):
+ """Implements the 'info_boot_image' command."""
+ parser = argparse.ArgumentParser(
+ prog='bvbtool info_image_hashes',
+ description='Show information about integrity-checking hashes.')
+ parser.add_argument('--image',
+ help='Brillo boot image to use',
+ type=argparse.FileType('rb'),
+ required=True)
+ parser.add_argument('--output',
+ help='Write info to file',
+ type=argparse.FileType('wt'),
+ default=sys.stdout)
+ args = parser.parse_args(sys.argv[2:])
+
+ args.image.seek(0, os.SEEK_END)
+ image_size = args.image.tell()
+ args.image.seek(image_size - BvbIntegrityFooter.SIZE)
+ footer = BvbIntegrityFooter(args.image.read(BvbIntegrityFooter.SIZE))
+
+ o = args.output
+ o.write('Footer version: %d.%d\n' %
+ (footer.version_major, footer.version_minor))
+ o.write('Version of dm-verity: %d\n' % footer.dm_verity_version)
+ o.write('Image Size: %d bytes\n' % footer.image_size)
+ o.write('Tree Offset: %d\n' % footer.tree_offset)
+ o.write('Tree Size: %d bytes\n' % footer.tree_size)
+ o.write('Data Block Size: %d bytes\n' % footer.data_block_size)
+ o.write('Hash Block Size: %d bytes\n' % footer.hash_block_size)
+ o.write('Hash Algorithm: %s\n' % footer.hash_algorithm)
+ o.write('Salt: %s\n' % footer.salt_hex)
+ o.write('Root Hash: %s\n' % footer.root_hash_hex)
+
+ def add_image_hashes(self):
+ """Implements the 'add_image_hashes' command.
+
+ See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
+ more information about dm-verity and these hashes.
+ """
+ parser = argparse.ArgumentParser(
+ prog='bvbtool add_hashes_to_image',
+ description='Add hashes for integrity-checking to image.')
+ parser.add_argument('--image',
+ help='Brillo boot image to add hashes to',
+ type=argparse.FileType('rab+'))
+ parser.add_argument('--hash',
+ help='Hash algorithm to use (default: sha1)',
+ default='sha1')
+ parser.add_argument('--salt', help='Salt in hex (default: /dev/urandom)')
+ parser.add_argument('--block_size',
+ help='Block size (default: 4096)',
+ type=parse_number,
+ default=4096)
+ args = parser.parse_args(sys.argv[2:])
+
+ # If there's already a footer of ours, truncate it. This way
+ # 'bvbtool add_hashes_to_image' is idempotent modulo salts.
+ args.image.seek(0, os.SEEK_END)
+ image_size = args.image.tell()
+ args.image.seek(image_size - BvbIntegrityFooter.SIZE)
+ try:
+ footer = BvbIntegrityFooter(args.image.read(BvbIntegrityFooter.SIZE))
+ # Existing footer found. Just truncate.
+ image_size = footer.image_size
+ args.image.truncate(image_size)
+ except (LookupError, struct.error):
+ pass
+
+ # Ensure image is multiple of block_size
+ rounded_image_size = round_to_multiple(image_size, args.block_size)
+ if rounded_image_size > image_size:
+ args.image.write('\0' * (rounded_image_size - image_size))
+ image_size = rounded_image_size
+
+ tree_offset = image_size
+
+ digest_size = len(hashlib.new(name=args.hash).digest())
+ digest_padding = round_to_pow2(digest_size) - digest_size
+
+ if args.salt:
+ salt = args.salt.decode('hex')
+ else:
+ if args.salt is None:
+ # If salt is not explicitly specified, choose a hash
+ # that's the same size as the hash size.
+ hash_size = digest_size
+ salt = open('/dev/urandom').read(hash_size)
+ else:
+ salt = ''
+
+ # Hashes are stored upside down so we need to calcuate hash
+ # offsets in advance.
+ (hash_level_offsets, tree_size) = calc_hash_level_offsets(
+ image_size, args.block_size, digest_size + digest_padding)
+
+ # Make room for the tree.
+ args.image.truncate(image_size + tree_size)
+
+ # Generate the tree.
+ root_hash = generate_hash_tree(args.image, image_size, args.block_size,
+ args.hash, salt, digest_padding, tree_offset,
+ hash_level_offsets)
+
+ # Write footer with the root hash and other information.
+ footer = BvbIntegrityFooter()
+ footer.dm_verity_version = 1
+ footer.image_size = image_size
+ footer.tree_offset = tree_offset
+ footer.tree_size = tree_size
+ footer.data_block_size = args.block_size
+ footer.hash_block_size = args.block_size
+ footer.hash_algorithm = args.hash
+ footer.salt_hex = salt.encode('hex')
+ footer.root_hash_hex = root_hash.encode('hex')
+ args.image.seek(tree_offset + tree_size)
+ footer.save(args.image)
+
+
+def calc_hash_level_offsets(image_size, block_size, digest_size):
+ """Calculate the offsets of all the hash-levels in a Merkle-tree.
+
+ Arguments:
+ image_size: The size of the image to calculate a Merkle-tree for.
+ block_size: The block size, e.g. 4096.
+ digest_size: The size of each hash, e.g. 32 for SHA-256.
+
+ Returns:
+ A tuple where the first argument is an array of offsets and the
+ second is size of the tree, in bytes.
+ """
+ level_offsets = []
+ level_sizes = []
+ tree_size = 0
+
+ num_levels = 0
+ size = image_size
+ while size > block_size:
+ num_blocks = (size + block_size - 1) / block_size
+ level_size = round_to_multiple(num_blocks * digest_size, block_size)
+
+ level_sizes.append(level_size)
+ tree_size += level_size
+ num_levels += 1
+
+ size = level_size
+
+ for n in range(0, num_levels):
+ offset = 0
+ for m in range(n + 1, num_levels):
+ offset += level_sizes[m]
+ level_offsets.append(offset)
+
+ return (level_offsets, tree_size)
+
+
+def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
+ digest_padding, tree_offset, hash_level_offsets):
+ """Generates a Merkle-tree for a file.
+
+ Args:
+ image: The image, as a file.
+ image_size: The size of the image.
+ block_size: The block size, e.g. 4096.
+ hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'.
+ salt: The salt to use.
+ digest_padding: The padding for each digest.
+ tree_offset: The offset of where to store the Merkle tree in |image|.
+ hash_level_offsets: The offsets from calc_hash_level_offsets().
+
+ Returns:
+ The top-level hash.
+ """
+ hash_src_offset = 0
+ hash_src_size = image_size
+ level_num = 0
+ while hash_src_size > block_size:
+ level_output = ''
+ image.seek(hash_src_offset)
+ remaining = hash_src_size
+ while remaining > 0:
+ hasher = hashlib.new(name=hash_alg_name, string=salt)
+ data = image.read(min(remaining, block_size))
+ assert data
+ remaining -= len(data)
+ hasher.update(data)
+ if len(data) < block_size:
+ hasher.update('\0' * (block_size - len(data)))
+ level_output += hasher.digest()
+ if digest_padding > 0:
+ level_output += '\0' * digest_padding
+
+ padding_needed = (round_to_multiple(
+ len(level_output), block_size) - len(level_output))
+ level_output += '\0' * padding_needed
+
+ hash_dest_offset = hash_level_offsets[level_num] + tree_offset
+
+ image.seek(hash_dest_offset)
+ image.write(level_output)
+
+ hash_src_offset = hash_dest_offset
+ hash_src_size = len(level_output)
+
+ level_num += 1
+
+ hasher = hashlib.new(name=hash_alg_name, string=salt)
+ hasher.update(level_output)
+ return hasher.digest()
+
+
+if __name__ == '__main__':
+ BvbTool()
diff --git a/bvbtool_unittest.cc b/bvbtool_unittest.cc
new file mode 100644
index 0000000..d57f2ae
--- /dev/null
+++ b/bvbtool_unittest.cc
@@ -0,0 +1,406 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <iostream>
+
+#include <endian.h>
+#include <inttypes.h>
+#include <string.h>
+
+#include <base/files/file_util.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+
+#include "bvb_unittest_util.h"
+#include "bvb_util.h"
+#include "bvb_verify.h"
+#include "bvb_property.h"
+
+class BvbToolTest : public BaseBvbToolTest {
+public:
+ BvbToolTest() {}
+};
+
+// This test ensure that the version is increased in both
+// bvb_boot_image.h and the bvb tool.
+TEST_F(BvbToolTest, BvbVersionInSync)
+{
+ base::FilePath path = testdir_.Append("version.txt");
+ EXPECT_COMMAND(0,
+ "./bvbtool version > %s",
+ path.value().c_str());
+ std::string printed_version;
+ ASSERT_TRUE(base::ReadFileToString(path, &printed_version));
+ base::TrimWhitespaceASCII(printed_version, base::TRIM_ALL, &printed_version);
+ std::string expected_version = base::StringPrintf("%d.%d",
+ BVB_MAJOR_VERSION,
+ BVB_MINOR_VERSION);
+ EXPECT_EQ(printed_version, expected_version);
+}
+
+TEST_F(BvbToolTest, ExtractPublicKey)
+{
+ GenerateBootImage("SHA256_RSA2048", "", 0,
+ base::FilePath("test/testkey_rsa2048.pem"));
+
+ base::FilePath public_key_path = testdir_.Append("public_key.bin");
+ EXPECT_COMMAND(0,
+ "./bvbtool extract_public_key --key test/testkey_rsa2048.pem"
+ " --output %s",
+ public_key_path.value().c_str());
+
+ std::string key_data;
+ ASSERT_TRUE(base::ReadFileToString(public_key_path, &key_data));
+
+ BvbBootImageHeader h;
+ bvb_boot_image_header_to_host_byte_order(
+ reinterpret_cast<BvbBootImageHeader*>(boot_image_.data()), &h);
+ uint8_t *d = reinterpret_cast<uint8_t*>(boot_image_.data());
+ size_t auxilary_data_block_offset =
+ sizeof(BvbBootImageHeader) + h.authentication_data_block_size;
+ EXPECT_GT(h.auxilary_data_block_size, key_data.size());
+ EXPECT_EQ(0, memcmp(key_data.data(),
+ d + auxilary_data_block_offset + h.public_key_offset,
+ key_data.size()));
+}
+
+TEST_F(BvbToolTest, PayloadsAreCorrect)
+{
+ GenerateBootImage("SHA256_RSA2048", "", 0,
+ base::FilePath("test/testkey_rsa2048.pem"));
+
+ BvbBootImageHeader h;
+ bvb_boot_image_header_to_host_byte_order(
+ reinterpret_cast<BvbBootImageHeader*>(boot_image_.data()), &h);
+
+ uint8_t *d = reinterpret_cast<uint8_t*>(boot_image_.data()) +
+ sizeof(BvbBootImageHeader) +
+ h.authentication_data_block_size +
+ h.auxilary_data_block_size;
+
+ // Check that the kernel and initrd images are inserted correctly.
+ for (int n = 0; n < 2; n++) {
+ std::string paths[2] = {"test/dummy_kernel.bin",
+ "test/dummy_initrd.bin"};
+ base::FilePath path(paths[n]);
+
+ int64_t file_size;
+ std::vector<uint8_t> file_data;
+ ASSERT_TRUE(base::GetFileSize(path, &file_size));
+ file_data.resize(file_size);
+ ASSERT_TRUE(base::ReadFile(path,
+ reinterpret_cast<char*>(file_data.data()),
+ file_data.size()));
+
+ switch (n) {
+ case 0:
+ // kernel
+ EXPECT_EQ(65536U, file_data.size());
+ EXPECT_EQ(file_data.size(), h.kernel_size);
+ EXPECT_EQ(0, memcmp(file_data.data(),
+ d + h.kernel_offset,
+ file_data.size()));
+ break;
+ case 1:
+ // initrd
+ EXPECT_EQ(131072U, file_data.size());
+ EXPECT_EQ(file_data.size(), h.initrd_size);
+ EXPECT_EQ(0, memcmp(file_data.data(),
+ d + h.initrd_offset,
+ file_data.size()));
+ break;
+ default:
+ ASSERT_TRUE(false);
+ break;
+ }
+ }
+}
+
+TEST_F(BvbToolTest, CheckCmdline)
+{
+ std::string cmdline("init=/sbin/init ro x y z");
+ GenerateBootImage("SHA256_RSA2048", cmdline, 0,
+ base::FilePath("test/testkey_rsa2048.pem"));
+
+ BvbBootImageHeader h;
+ bvb_boot_image_header_to_host_byte_order(
+ reinterpret_cast<BvbBootImageHeader*>(boot_image_.data()), &h);
+
+ EXPECT_EQ(0, ::strcmp(cmdline.c_str(),
+ reinterpret_cast<const char*>(h.kernel_cmdline)));
+}
+
+TEST_F(BvbToolTest, CheckAddresses)
+{
+ GenerateBootImage("SHA256_RSA2048", "", 0,
+ base::FilePath("test/testkey_rsa2048.pem"),
+ "--kernel_addr 0x42 --initrd_addr 43");
+
+ BvbBootImageHeader h;
+ bvb_boot_image_header_to_host_byte_order(
+ reinterpret_cast<BvbBootImageHeader*>(boot_image_.data()), &h);
+
+ EXPECT_EQ(0x42U, h.kernel_addr);
+ EXPECT_EQ(43U, h.initrd_addr);
+}
+
+TEST_F(BvbToolTest, CheckProperties)
+{
+ GenerateBootImage("SHA256_RSA2048", "", 0,
+ base::FilePath("test/testkey_rsa2048.pem"),
+ "--prop foo:brillo "
+ "--prop bar:chromeos "
+ "--prop prisoner:24601 "
+ "--prop hexnumber:0xcafe "
+ "--prop hexnumber_capital:0xCAFE "
+ "--prop large_hexnumber:0xfedcba9876543210 "
+ "--prop larger_than_uint64:0xfedcba98765432101 "
+ "--prop almost_a_number:423x "
+ "--prop_from_file blob:test/small_blob.bin "
+ );
+
+ BvbBootImageHeader h;
+ bvb_boot_image_header_to_host_byte_order(
+ reinterpret_cast<BvbBootImageHeader*>(boot_image_.data()), &h);
+
+ EXPECT_EQ(BVB_VERIFY_RESULT_OK,
+ bvb_verify_boot_image(boot_image_.data(), boot_image_.size(),
+ nullptr, nullptr));
+
+ const char *s;
+ size_t len;
+ uint64_t val;
+
+ // Basic.
+ s = bvb_lookup_property(boot_image_.data(), boot_image_.size(),
+ "foo", 0, &len);
+ EXPECT_EQ(0, strcmp(s, "brillo"));
+ EXPECT_EQ(6U, len);
+ s = bvb_lookup_property(boot_image_.data(), boot_image_.size(),
+ "bar", 0, &len);
+ EXPECT_EQ(0, strcmp(s, "chromeos"));
+ EXPECT_EQ(8U, len);
+ s = bvb_lookup_property(boot_image_.data(), boot_image_.size(),
+ "non-existant", 0, &len);
+ EXPECT_EQ(0U, len);
+ EXPECT_EQ(NULL, s);
+
+ // Numbers.
+ EXPECT_NE(0, bvb_lookup_property_uint64(
+ boot_image_.data(), boot_image_.size(), "prisoner", 0, &val));
+ EXPECT_EQ(24601U, val);
+
+ EXPECT_NE(0, bvb_lookup_property_uint64(
+ boot_image_.data(), boot_image_.size(), "hexnumber", 0, &val));
+ EXPECT_EQ(0xcafeU, val);
+
+ EXPECT_NE(0, bvb_lookup_property_uint64(
+ boot_image_.data(), boot_image_.size(), "hexnumber_capital", 0, &val));
+ EXPECT_EQ(0xcafeU, val);
+
+ EXPECT_NE(0, bvb_lookup_property_uint64(
+ boot_image_.data(), boot_image_.size(), "large_hexnumber", 0, &val));
+ EXPECT_EQ(0xfedcba9876543210U, val);
+
+ // We could catch overflows and return an error ... but we currently don't.
+ EXPECT_NE(0, bvb_lookup_property_uint64(
+ boot_image_.data(), boot_image_.size(), "larger_than_uint64", 0, &val));
+ EXPECT_EQ(0xedcba98765432101U, val);
+
+ // Number-parsing failures.
+ EXPECT_EQ(0, bvb_lookup_property_uint64(
+ boot_image_.data(), boot_image_.size(), "foo", 0, &val));
+
+ EXPECT_EQ(0, bvb_lookup_property_uint64(
+ boot_image_.data(), boot_image_.size(), "almost_a_number", 0, &val));
+
+ // Blobs.
+ //
+ // test/small_blob.bin is 21 byte file full of NUL-bytes except for
+ // the string "brillo ftw!" at index 2 and '\n' at the last byte.
+ s = bvb_lookup_property(boot_image_.data(), boot_image_.size(),
+ "blob", 0, &len);
+ EXPECT_EQ(21U, len);
+ EXPECT_EQ(0, memcmp(s, "\0\0", 2));
+ EXPECT_EQ(0, memcmp(s + 2, "brillo ftw!", 11));
+ EXPECT_EQ(0, memcmp(s + 13, "\0\0\0\0\0\0\0", 7));
+ EXPECT_EQ('\n', s[20]);
+}
+
+TEST_F(BvbToolTest, CheckRollbackIndex)
+{
+ uint64_t rollback_index = 42;
+ GenerateBootImage("SHA256_RSA2048", "", rollback_index,
+ base::FilePath("test/testkey_rsa2048.pem"));
+
+ BvbBootImageHeader h;
+ bvb_boot_image_header_to_host_byte_order(
+ reinterpret_cast<BvbBootImageHeader*>(boot_image_.data()), &h);
+
+ EXPECT_EQ(rollback_index, h.rollback_index);
+}
+
+TEST_F(BvbToolTest, CheckPubkeyReturned)
+{
+ GenerateBootImage("SHA256_RSA2048", "", 0,
+ base::FilePath("test/testkey_rsa2048.pem"));
+
+ const uint8_t* pubkey = NULL;
+ size_t pubkey_length = 0;
+
+ EXPECT_EQ(BVB_VERIFY_RESULT_OK,
+ bvb_verify_boot_image(boot_image_.data(), boot_image_.size(),
+ &pubkey, &pubkey_length));
+
+ BvbBootImageHeader h;
+ bvb_boot_image_header_to_host_byte_order(
+ reinterpret_cast<BvbBootImageHeader*>(boot_image_.data()), &h);
+
+ EXPECT_EQ(pubkey_length, h.public_key_size);
+
+ const uint8_t* expected_pubkey = boot_image_.data() +
+ sizeof(BvbBootImageHeader) +
+ h.authentication_data_block_size +
+ h.public_key_offset;
+ EXPECT_EQ(pubkey, expected_pubkey);
+}
+
+TEST_F(BvbToolTest, Info)
+{
+ GenerateBootImage("SHA256_RSA2048", "foobar=cmdline test=42", 0,
+ base::FilePath("test/testkey_rsa2048.pem"),
+ "--prop foo:brillo "
+ "--prop bar:chromeos "
+ "--prop prisoner:24601 "
+ "--prop hexnumber:0xcafe "
+ "--prop hexnumber_capital:0xCAFE "
+ "--prop large_hexnumber:0xfedcba9876543210 "
+ "--prop larger_than_uint64:0xfedcba98765432101 "
+ "--prop almost_a_number:423x "
+ "--prop_from_file blob:test/small_blob.bin "
+ "--prop_from_file large_blob:test/dummy_kernel.bin");
+
+ base::FilePath info_path = testdir_.Append("info_output.txt");
+ EXPECT_COMMAND(0,
+ "./bvbtool info_boot_image --image %s --output %s",
+ boot_image_path_.value().c_str(),
+ info_path.value().c_str());
+
+ std::string info_data;
+ ASSERT_TRUE(base::ReadFileToString(info_path, &info_data));
+
+ ASSERT_EQ(
+ "Boot Image version: 1.0\n"
+ "Header Block: 8192 bytes\n"
+ "Authentication Block: 4096 bytes\n"
+ "Auxilary Block: 70080 bytes\n"
+ "Payload Block: 196608 bytes\n"
+ "Algorithm: SHA256_RSA2048\n"
+ "Rollback Index: 0\n"
+ "Kernel: 65536 bytes\n"
+ "Initrd: 131072 bytes\n"
+ "Kernel Load Address: 0x10008000\n"
+ "Initrd Load Address: 0x11000000\n"
+ "Kernel Cmdline: foobar=cmdline test=42\n"
+ "Properties:\n"
+ " foo: 'brillo'\n"
+ " bar: 'chromeos'\n"
+ " prisoner: '24601'\n"
+ " hexnumber: '0xcafe'\n"
+ " hexnumber_capital: '0xCAFE'\n"
+ " large_hexnumber: '0xfedcba9876543210'\n"
+ " larger_than_uint64: '0xfedcba98765432101'\n"
+ " almost_a_number: '423x'\n"
+ " blob: '\\x00\\x00brillo "
+ "ftw!\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\n'\n"
+ " large_blob: (65536 bytes)\n",
+ info_data);
+}
+
+TEST_F(BvbToolTest, ImageHashes) {
+ // test/dummy_rootfs.bin is a 1,049,600 byte (1025 Kib) file.
+ EXPECT_COMMAND(
+ 0, "cp test/dummy_rootfs.bin %s/rootfs.bin", testdir_.value().c_str());
+ EXPECT_COMMAND(
+ 0,
+ "./bvbtool add_image_hashes --salt d00df00d --image %s/rootfs.bin",
+ testdir_.value().c_str());
+
+ // We don't want to impose the requirement of having the
+ // veritysetup(1) command available on builders but leave it here so
+ // it can be manually enabled when making changes.
+ //
+ // (The fact that we end up with the correct root hash and tree size
+ // demonstrates correctness sufficiently well.)
+ if (false) {
+ EXPECT_COMMAND(0,
+ "veritysetup --no-superblock --format=1 --hash=sha1 "
+ "--data-block-size=4096 --hash-block-size=4096 "
+ "--salt=d00df00d "
+ "--data-blocks=257 "
+ "--hash-offset=1052672 "
+ "verify "
+ "%s/rootfs.bin %s/rootfs.bin "
+ "d0e3b9865f45fc66c1a64796dae1666647103f72",
+ testdir_.value().c_str(),
+ testdir_.value().c_str());
+ }
+
+ base::FilePath info_path = testdir_.Append("info_output.txt");
+ EXPECT_COMMAND(
+ 0,
+ "./bvbtool info_image_hashes --image %s/rootfs.bin --output %s",
+ testdir_.value().c_str(),
+ info_path.value().c_str());
+
+ std::string info_data;
+ ASSERT_TRUE(base::ReadFileToString(info_path, &info_data));
+
+ // Note that image size is rounded up to block size (4096).
+ ASSERT_EQ(
+ "Footer version: 1.0\n"
+ "Version of dm-verity: 1\n"
+ "Image Size: 1052672 bytes\n"
+ "Tree Offset: 1052672\n"
+ "Tree Size: 16384 bytes\n"
+ "Data Block Size: 4096 bytes\n"
+ "Hash Block Size: 4096 bytes\n"
+ "Hash Algorithm: sha1\n"
+ "Salt: d00df00d\n"
+ "Root Hash: d0e3b9865f45fc66c1a64796dae1666647103f72\n",
+ info_data);
+
+ // Check that bvbtool injects the directives for setting up the
+ // rootfs for the given integrity-checked file system BEFORE the
+ // user-supplied command-line.
+ GenerateBootImage("SHA256_RSA2048",
+ "some_option=42",
+ 0,
+ base::FilePath("test/testkey_rsa2048.pem"),
+ base::StringPrintf("--rootfs_with_hashes %s/rootfs.bin",
+ testdir_.value().c_str()));
+ BvbBootImageHeader h;
+ bvb_boot_image_header_to_host_byte_order(
+ reinterpret_cast<BvbBootImageHeader*>(boot_image_.data()), &h);
+
+ EXPECT_EQ(
+ "dm=\"1 vroot none ro 1,"
+ "0 2056 verity 1 PARTUUID=$(ANDROID_SYSTEM_PARTUUID) "
+ "PARTUUID=$(ANDROID_SYSTEM_PARTUUID) 4096 4096 257 257 sha1 "
+ "d0e3b9865f45fc66c1a64796dae1666647103f72 d00df00d\" "
+ "some_option=42",
+ std::string(reinterpret_cast<const char*>(h.kernel_cmdline)));
+}
diff --git a/test/dummy_initrd.bin b/test/dummy_initrd.bin
new file mode 100644
index 0000000..6d13e99
--- /dev/null
+++ b/test/dummy_initrd.bin
Binary files differ
diff --git a/test/dummy_kernel.bin b/test/dummy_kernel.bin
new file mode 100644
index 0000000..3666773
--- /dev/null
+++ b/test/dummy_kernel.bin
Binary files differ
diff --git a/test/dummy_rootfs.bin b/test/dummy_rootfs.bin
new file mode 100644
index 0000000..e2a36c2
--- /dev/null
+++ b/test/dummy_rootfs.bin
Binary files differ
diff --git a/test/small_blob.bin b/test/small_blob.bin
new file mode 100644
index 0000000..d712de6
--- /dev/null
+++ b/test/small_blob.bin
Binary files differ
diff --git a/test/testkey_rsa2048.pem b/test/testkey_rsa2048.pem
new file mode 100644
index 0000000..867dcff
--- /dev/null
+++ b/test/testkey_rsa2048.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAxlVR3TIkouAOvH79vaJTgFhpfvVKQIeVkFRZPVXK/zY0Gvrh
+4JAqGjJoW/PfrQv5sdD36qtHH3a+G5hLZ6Ni+t/mtfjucxZfuLGC3kmJ1T3XqEKZ
+gXXI2IR7vVSoImREvDQGEDyJwtHzLANlkbGg0cghVhWZSCAndO8BenalC2v94/rt
+DfkPekH6dgU3Sf40T0sBSeSY94mOzTaqOR2pfV1rWlLRdWmo33zeHBv52Rlbt0dM
+uXAureXWiHztkm5GCBC1dgM+CaxNtizNEgC91KcD0xuRCCM2WxH+r1lpszyIJDct
+YbrFmVEYl/kjQpafhy7Nsk1fqSTyRdriZSYmTQIDAQABAoIBAQC+kJgaCuX8wYAn
+SXWQ0fmdZlXnMNRpcF0a0pD0SAzGb1RdYBXMaXiqtyhiwc53PPxsCDdNecjayIMd
+jJVXPTwLhTruOgMS/bp3gcgWwV34UHV4LJXGOGAE+jbS0hbDBMiudOYmj6RmVshp
+z9G1zZCSQNMXHaWsEYkX59XpzzoB384nRul2QgEtwzUNR9XlpzgtJBLk3SACkvsN
+mQ/DW8IWHXLg8vLn1LzVJ2e3B16H4MoE2TCHxqfMgr03IDRRJogkenQuQsFhevYT
+o/mJyHSWavVgzMHG9I5m+eepF4Wyhj1Y4WyKAuMI+9dHAX/h7Lt8XFCQCh5DbkVG
+zGr34sWBAoGBAOs7n7YZqNaaguovfIdRRsxxZr1yJAyDsr6w3yGImDZYju4c4WY9
+5esO2kP3FA4p0c7FhQF5oOb1rBuHEPp36cpL4aGeK87caqTfq63WZAujoTZpr9Lp
+BRbkL7w/xG7jpQ/clpA8sHzHGQs/nelxoOtC7E118FiRgvD/jdhlMyL9AoGBANfX
+vyoN1pplfT2xR8QOjSZ+Q35S/+SAtMuBnHx3l0qH2bbBjcvM1MNDWjnRDyaYhiRu
+i+KA7tqfib09+XpB3g5D6Ov7ls/Ldx0S/VcmVWtia2HK8y8iLGtokoBZKQ5AaFX2
+iQU8+tC4h69GnJYQKqNwgCUzh8+gHX5Y46oDiTmRAoGAYpOx8lX+czB8/Da6MNrW
+mIZNT8atZLEsDs2ANEVRxDSIcTCZJId7+m1W+nRoaycLTWNowZ1+2ErLvR10+AGY
+b7Ys79Wg9idYaY9yGn9lnZsMzAiuLeyIvXcSqgjvAKlVWrhOQFOughvNWvFl85Yy
+oWSCMlPiTLtt7CCsCKsgKuECgYBgdIp6GZsIfkgclKe0hqgvRoeU4TR3gcjJlM9A
+lBTo+pKhaBectplx9RxR8AnsPobbqwcaHnIfAuKDzjk5mEvKZjClnFXF4HAHbyAF
+nRzZEy9XkWFhc80T5rRpZO7C7qdxmu2aiKixM3V3L3/0U58qULEDbubHMw9bEhAT
+PudI8QKBgHEEiMm/hr9T41hbQi/LYanWnlFw1ue+osKuF8bXQuxnnHNuFT/c+9/A
+vWhgqG6bOEHu+p/IPrYm4tBMYlwsyh4nXCyGgDJLbLIfzKwKAWCtH9LwnyDVhOow
+GH9shdR+sW3Ew97xef02KAH4VlNANEmBV4sQNqWWvsYrcFm2rOdL
+-----END RSA PRIVATE KEY-----
diff --git a/test/testkey_rsa4096.pem b/test/testkey_rsa4096.pem
new file mode 100644
index 0000000..26db5c3
--- /dev/null
+++ b/test/testkey_rsa4096.pem
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+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
+-----END RSA PRIVATE KEY-----
diff --git a/test/testkey_rsa8192.pem b/test/testkey_rsa8192.pem
new file mode 100644
index 0000000..a383428
--- /dev/null
+++ b/test/testkey_rsa8192.pem
@@ -0,0 +1,99 @@
+-----BEGIN RSA PRIVATE KEY-----
+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
+-----END RSA PRIVATE KEY-----