diff options
author | David Zeuthen <zeuthen@google.com> | 2016-06-10 12:14:52 -0400 |
---|---|---|
committer | David Zeuthen <zeuthen@google.com> | 2016-06-10 14:52:14 -0400 |
commit | 052652c8d3d4ebc9e3dace4045138a3bf45e4b7f (patch) | |
tree | a0ed7072a41dc99d2c1c5cc9bdf29648c8635de3 | |
parent | 34670530c36e398a80581db1c1266365ce20554f (diff) | |
download | bvb-052652c8d3d4ebc9e3dace4045138a3bf45e4b7f.tar.gz |
BUG=29099910
TEST=Unit tests pass.
Change-Id: Ifc8615752aa6d62e7e6b9037491daa140bb36765
31 files changed, 5296 insertions, 0 deletions
diff --git a/Android.mk b/Android.mk new file mode 100644 index 0000000..61588a9 --- /dev/null +++ b/Android.mk @@ -0,0 +1,108 @@ +# +# Copyright (C) 2016 The Android Open Source Project +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation +# files (the "Software"), to deal in the Software without +# restriction, including without limitation the rights to use, copy, +# modify, merge, publish, distribute, sublicense, and/or sell copies +# of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +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) -fno-stack-protector -DBVB_ENABLE_DEBUG -DBVB_REFIMPL_COMPILATION +LOCAL_LDFLAGS := $(bvb_common_ldflags) +LOCAL_C_INCLUDES := +LOCAL_SRC_FILES := \ + refimpl/bvb_property.c \ + refimpl/bvb_rsa.c \ + refimpl/bvb_sha256.c \ + refimpl/bvb_sha512.c \ + refimpl/bvb_util.c \ + refimpl/bvb_verify.c +include $(BUILD_HOST_STATIC_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := libbvb_refimpl_sysdeps +LOCAL_MODULE_HOST_OS := linux +LOCAL_MODULE_CLASS := STATIC_LIBRARIES +LOCAL_CLANG := true +LOCAL_CFLAGS := $(bvb_common_cflags) -DBVB_ENABLE_DEBUG -DBVB_REFIMPL_COMPILATION +LOCAL_LDFLAGS := $(bvb_common_ldflags) +LOCAL_C_INCLUDES := +LOCAL_SRC_FILES := \ + refimpl/bvb_sysdeps_stub.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 := $(LOCAL_PATH)/refimpl external/gtest/include +LOCAL_STATIC_LIBRARIES := \ + libbvb_refimpl \ + libbvb_refimpl_sysdeps \ + 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) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := bvb_refimpl_symbols_test +LOCAL_MODULE_TAGS := debug +LOCAL_ADDITIONAL_DEPENDENCIES := libbvb_refimpl +include $(BUILD_HOST_PREBUILT) @@ -0,0 +1,20 @@ +Copyright 2016, The Android Open Source Project + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. @@ -0,0 +1,144 @@ +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. + +-- LICENSE + +This code is made available under the MIT License. See the LICENSE +file for more information. + +-- FILES AND DIRECTORIES + + refimpl/ + + The reference implementation for boot image verification and A/B + selection is in the refimpl sub-directory. + + Part of this code is considered internal to the reference + implementation and should not be used outside it. This includes the + bvb_rsa.[ch] and bvb_sha.[ch] files. + + System dependencies expected to be provided by the platform is + defined in bvb_sysdeps.h. If the platform provides the standard C + runtime bvb_sysdeps_stub.c can be used. + + Android.mk + + Build instructions for building the reference implementation and + associated unit tests. + + bvb_util_unittest.cc, bvb_verify_unittest.cc, bvb_unittest_util.h + + Unit tests for the reference implementation. + + bvbtool_unittest.cc + + Unit tests for bvbtool. + + bvbtool + + A tool written in Python for working with Brillo boot images. + + test/ + + Contains test data used in unit tests. + +-- 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. + +The preprocessor symbol BVB_REFIMPL_COMPILATION should be set when +compiling the code. The code must be compiled into a separate library. + +Applications using the compiled library must only include the +refimpl/bvb_refimpl.h file (which will include all public interfaces) +and must not have the BVB_REFIMPL_COMPILATION preprocessor symbol +set. This is to ensure that internal code that may be change in the +future (for example refimpl/bvb_sha.[ch] and refimpl/bvb_rsa.[ch]) +will not be visible to application code. + +-- COMPATIBILITY NOTES + +The Brillo Boot Image structure (as defined in refimpl/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, +BOARD_BVB_SIGN_BOOT_IMAGE_ARGS, and BOARD_BVB_ADD_IMAGE_HASHES_ARGS +can be used to specify additional options passed to respectively +'bvbtool make_boot_image', 'bvbtool sign_boot_image', and 'bvbtool +add_image_hashes'. diff --git a/bvb_refimpl_symbols_test b/bvb_refimpl_symbols_test new file mode 100755 index 0000000..e1ec9a1 --- /dev/null +++ b/bvb_refimpl_symbols_test @@ -0,0 +1,48 @@ +#!/bin/bash + +# +# Copyright (C) 2016 The Android Open Source Project +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation +# files (the "Software"), to deal in the Software without +# restriction, including without limitation the rights to use, copy, +# modify, merge, publish, distribute, sublicense, and/or sell copies +# of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +# This shell-script checks the symbols in libbvb_refimpl.a and fails +# if a reference not starting with bvb_ is referenced. It's intended +# to catch mistakes where the standard C library is inadvertently +# used. + +set -e + +SYMBOLS_FILE=$(mktemp /tmp/libbvb_refimpl_symbols.XXXXXXXXXX) + +trap "rm -f '${SYMBOLS_FILE}'" EXIT + +readelf --symbols --wide "${ANDROID_HOST_OUT}/obj/STATIC_LIBRARIES/libbvb_refimpl_intermediates/libbvb_refimpl.a" | \ + awk '$7 == "UND" && $8 != "" {print $8}' | \ + grep -v ^bvb_ | \ + sort -u > "${SYMBOLS_FILE}" + +# If this file is non-empty, it means that the library is using +# symbols not starting with "bvb_". +if [ -s "${SYMBOLS_FILE}" ] ; then + echo "ERROR: $0: Unexpected symbols in libbvb_refimpl:" >&2 + cat "${SYMBOLS_FILE}" >&2 + exit 1 +fi diff --git a/bvb_unittest_util.h b/bvb_unittest_util.h new file mode 100644 index 0000000..f65420e --- /dev/null +++ b/bvb_unittest_util.h @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef BVB_UNITTEST_UTIL_H_ +#define BVB_UNITTEST_UTIL_H_ + +#include <inttypes.h> + +#include <gtest/gtest.h> + +#include <base/files/file_util.h> +#include <base/strings/string_util.h> +#include <base/strings/stringprintf.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_unittest.cc b/bvb_util_unittest.cc new file mode 100644 index 0000000..7227edb --- /dev/null +++ b/bvb_util_unittest.cc @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <string.h> + +#include <gtest/gtest.h> + +#include "bvb_refimpl.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_unittest.cc b/bvb_verify_unittest.cc new file mode 100644 index 0000000..ebabd8d --- /dev/null +++ b/bvb_verify_unittest.cc @@ -0,0 +1,475 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#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_refimpl.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)); +} @@ -0,0 +1,1091 @@ +#!/usr/bin/env python + +# Copyright 2016, The Android Open Source Project +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation +# files (the "Software"), to deal in the Software without +# restriction, including without limitation the rights to use, copy, +# modify, merge, publish, distribute, sublicense, and/or sell copies +# of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +"""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..020eb2c --- /dev/null +++ b/bvbtool_unittest.cc @@ -0,0 +1,412 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#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_refimpl.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/refimpl/bvb_boot_image_header.h b/refimpl/bvb_boot_image_header.h new file mode 100644 index 0000000..8e25f84 --- /dev/null +++ b/refimpl/bvb_boot_image_header.h @@ -0,0 +1,310 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined (BVB_INSIDE_BVB_REFIMPL_H) && !defined (BVB_REFIMPL_COMPILATION) +#error "Never include this file directly, include bvb_refimpl.h instead." +#endif + +#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/refimpl/bvb_property.c b/refimpl/bvb_property.c new file mode 100644 index 0000000..67aceb9 --- /dev/null +++ b/refimpl/bvb_property.c @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#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/refimpl/bvb_property.h b/refimpl/bvb_property.h new file mode 100644 index 0000000..30f936a --- /dev/null +++ b/refimpl/bvb_property.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined (BVB_INSIDE_BVB_REFIMPL_H) && !defined (BVB_REFIMPL_COMPILATION) +#error "Never include this file directly, include bvb_refimpl.h instead." +#endif + +#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/refimpl/bvb_refimpl.h b/refimpl/bvb_refimpl.h new file mode 100644 index 0000000..703d3e3 --- /dev/null +++ b/refimpl/bvb_refimpl.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef BVB_REFIMPL_H_ +#define BVB_REFIMPL_H_ + +/* The BVB_INSIDE_BVB_REFIMPL_H preprocessor symbol is used to enforce + * library users to include only this file. All public interfaces, and + * only public interfaces, must be included here. + */ + +#define BVB_INSIDE_BVB_REFIMPL_H +#include "bvb_boot_image_header.h" +#include "bvb_property.h" +#include "bvb_sysdeps.h" +#include "bvb_util.h" +#include "bvb_verify.h" +#undef BVB_INSIDE_BVB_REFIMPL_H + +#endif /* BVB_REFIMPL_H_ */ diff --git a/refimpl/bvb_rsa.c b/refimpl/bvb_rsa.c new file mode 100644 index 0000000..bda4683 --- /dev/null +++ b/refimpl/bvb_rsa.c @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* 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/refimpl/bvb_rsa.h b/refimpl/bvb_rsa.h new file mode 100644 index 0000000..1b1987f --- /dev/null +++ b/refimpl/bvb_rsa.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* 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. + */ + +#ifdef BVB_INSIDE_BVB_REFIMPL_H +#error "You can't include bvb_rsa.h in the public header bvb_refimpl.h." +#endif + +#ifndef BVB_REFIMPL_COMPILATION +#error "Never include this file, it may only be used from internal bvb code." +#endif + +#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/refimpl/bvb_sha.h b/refimpl/bvb_sha.h new file mode 100644 index 0000000..3b5964b --- /dev/null +++ b/refimpl/bvb_sha.h @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + +#ifdef BVB_INSIDE_BVB_REFIMPL_H +#error "You can't include bvb_sha.h in the public header bvb_refimpl.h." +#endif + +#ifndef BVB_REFIMPL_COMPILATION +#error "Never include this file, it may only be used from internal bvb code." +#endif + +#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/refimpl/bvb_sha256.c b/refimpl/bvb_sha256.c new file mode 100644 index 0000000..53e5f6b --- /dev/null +++ b/refimpl/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/refimpl/bvb_sha512.c b/refimpl/bvb_sha512.c new file mode 100644 index 0000000..0f1b385 --- /dev/null +++ b/refimpl/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/refimpl/bvb_sysdeps.h b/refimpl/bvb_sysdeps.h new file mode 100644 index 0000000..ed16f2b --- /dev/null +++ b/refimpl/bvb_sysdeps.h @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined (BVB_INSIDE_BVB_REFIMPL_H) && !defined (BVB_REFIMPL_COMPILATION) +#error "Never include this file directly, include bvb_refimpl.h instead." +#endif + +#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/refimpl/bvb_sysdeps_stub.c b/refimpl/bvb_sysdeps_stub.c new file mode 100644 index 0000000..88e3c41 --- /dev/null +++ b/refimpl/bvb_sysdeps_stub.c @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#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/refimpl/bvb_util.c b/refimpl/bvb_util.c new file mode 100644 index 0000000..f020cda --- /dev/null +++ b/refimpl/bvb_util.c @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + +#include "bvb_util.h" + +uint32_t bvb_be32toh(uint32_t in) { + uint8_t* d = (uint8_t*) ∈ + 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*) ∈ + 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/refimpl/bvb_util.h b/refimpl/bvb_util.h new file mode 100644 index 0000000..63c150b --- /dev/null +++ b/refimpl/bvb_util.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined (BVB_INSIDE_BVB_REFIMPL_H) && !defined (BVB_REFIMPL_COMPILATION) +#error "Never include this file directly, include bvb_refimpl.h instead." +#endif + +#ifndef BVB_UTIL_H_ +#define BVB_UTIL_H_ + +#include "bvb_boot_image_header.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/refimpl/bvb_verify.c b/refimpl/bvb_verify.c new file mode 100644 index 0000000..02758d0 --- /dev/null +++ b/refimpl/bvb_verify.c @@ -0,0 +1,311 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#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; + } + + uintptr_t data_ptr = (uintptr_t) data; + /* Ensure passed in memory doesn't wrap. */ + if (!bvb_safe_add(NULL, (uint64_t) data_ptr, 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/refimpl/bvb_verify.h b/refimpl/bvb_verify.h new file mode 100644 index 0000000..eaa9adf --- /dev/null +++ b/refimpl/bvb_verify.h @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined (BVB_INSIDE_BVB_REFIMPL_H) && !defined (BVB_REFIMPL_COMPILATION) +#error "Never include this file directly, include bvb_refimpl.h instead." +#endif + +#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/test/dummy_initrd.bin b/test/dummy_initrd.bin Binary files differnew file mode 100644 index 0000000..6d13e99 --- /dev/null +++ b/test/dummy_initrd.bin diff --git a/test/dummy_kernel.bin b/test/dummy_kernel.bin Binary files differnew file mode 100644 index 0000000..3666773 --- /dev/null +++ b/test/dummy_kernel.bin diff --git a/test/dummy_rootfs.bin b/test/dummy_rootfs.bin Binary files differnew file mode 100644 index 0000000..e2a36c2 --- /dev/null +++ b/test/dummy_rootfs.bin diff --git a/test/small_blob.bin b/test/small_blob.bin Binary files differnew file mode 100644 index 0000000..d712de6 --- /dev/null +++ b/test/small_blob.bin 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----- +MIIJKQIBAAKCAgEA2ASv49OEbH4NiT3CjNMSVeliyfEPXswWcqtEfCxlSpS1FisA +uwbvEwdTTPlkuSh6G4SYiNhnpCP5p0vcSg/3OhiuVKgV/rCtrDXaO60nvK/o0y83 +NNZRK2xaJ9eWBq9ruIDK+jC0sYWzTaqqwxY0Grjnx/r5CXerl5PrRK7PILzwgBHb +IwxHcblt1ntgR4cWVpO3wiqasEwBDDDYk4fw7W6LvjBb9qav3YB8RV6PkZNeRP64 +ggfuecq/MXNiWOPNxLzCER2hSr/+J32h9jWjXsrcVy8+8Mldhmr4r2an7c247aFf +upuFGtUJrpROO8/LXMl5gPfMpkqoatjTMRH59gJjKhot0RpmGxZBvb33TcBK5SdJ +X39Y4yct5clmDlI4Fjj7FutTP+b96aJeJVnYeUX/A0wmogBajsJRoRX5e/RcgZsY +RzXYLQXprQ81dBWjjovMJ9p8XeT6BNMFC7o6sklFL0fHDUE/l4BNP8G1u3Bfpzev +SCISRS71D4eS4oQB+RIPFBUkzomZ7rnEF3BwFeq+xmwfYrP0LRaH+1YeRauuMuRe +ke1TZl697a3mEjkNg8noa2wtpe7EWmaujJfXDWxJx/XEkjGLCe4z2qk3tkkY+A5g +Rcgzke8gVxC+eC2DJtbKYfkv4L8FMFJaEhwAp13MfC7FlYujO/BDLl7dANsCAwEA +AQKCAgAWoL8P/WsktjuSwb5sY/vKtgzcHH1Ar942GsysuTXPDy686LpF3R8T/jNy +n7k2UBAia8xSoWCR6BbRuHeV5oA+PLGeOpE7QaSfonB+yc+cy0x3Or3ssfqEsu/q +toGHp75/8DXS6WE0K04x94u1rdC9b9sPrrGBlWCLGzqM0kbuJfyHXdd3n2SofAUO +b5QRSgxD+2tHUpEroHqHnWJCaf4J0QegX45yktlfOYNK/PHLDQXV8ly/ejc32M4Y +Tv7hUtOOJTuq8VCg9OWZm2Zo1QuM9XEJTPCp5l3+o5vzO6yhk2gotDvD32CdA+3k +tLJRP54M1Sn+IXb1gGKN9rKAtGJbenWIPlNObhQgkbwG89Qd+5rfMXsiPv1Hl1tK ++tqwjD82/H3/ElaaMnwHCpeoGSp95OblAoBjzjMP2KsbvKSdL8O/rf1c3uOw9+DF +cth0SA8y3ZzI11gJtb2QMGUrCny5n4sPGGbc3x38NdLhwbkPKZy60OiT4g2kNpdY +dIitmAML2otttiF4AJM6AraPk8YVzkPLTksoL3azPBya5lIoDI2H3QvTtSvpXkXP +yKchsDSWYbdqfplqC/X0Djp2/Zd8jpN5I6+1aSmpTmbwx/JTllY1N89FRZLIdxoh +2k81LPiXhE6uRbjioJUlbnEWIpY2y2N2Clmxpjh0/IcXd1XImQKCAQEA7Zai+yjj +8xit24aO9Tf3mZBXBjSaDodjC2KS1yCcAIXp6S7aH0wZipyZpQjys3zaBQyMRYFG +bQqIfVAa6inWyDoofbAJHMu5BVcHFBPZvSS5YhDjc8XZ5dqSCxzIz9opIqAbm+b4 +aEV/3A3Jki5Dy8y/5j21GAK4Y4mqQOYzne7bDGi3Hyu041MGM4qfIcIkS5N1eHW4 +sDZJh6+K5tuxN5TX3nDZSpm9luNH8mLGgKAZ15b1LqXAtM5ycoBY9Hv082suPPom +O+r0ybdRX6nDSH8+11y2KiP2kdVIUHCGkwlqgrux5YZyjCZPwOvEPhzSoOS+vBiF +UVXA8idnxNLk1QKCAQEA6MIihDSXx+350fWqhQ/3Qc6gA/t2C15JwJ9+uFWA+gjd +c/hn5HcmnmBJN4R04nLG/aU9SQur87a4mnC/Mp9JIARjHlZ/WNT4U0sJyPEVRg5U +Z9VajAucWwi0JyJYCO1EMMy68Jp8qlTriK/L7nbD86JJ5ASxjojiN/0psK/Pk60F +Rr+shKPi3jRQ1BDjDtAxOfo4ctf/nFbUM4bY0FNPQMP7WesoSKU0NBCRR6d0d2tq +YflMjIQHx+N74P5jEdSCHTVGQm+dj47pUt3lLPLWc0bX1G/GekwXP4NUsR/70Hsi +bwxkNnK2TSGzkt2rcOnutP125rJu6WpV7SNrq9rm7wKCAQAfMROcnbWviKHqnDPQ +hdR/2K9UJTvEhInASOS2UZWpi+s1rez9BuSjigOx4wbaAZ4t44PW7C3uyt84dHfU +HkIQb3I5bg8ENMrJpK9NN33ykwuzkDwMSwFcZ+Gci97hSubzoMl/IkeiiN1MapL4 +GhLUgsD+3UMVL+Y9SymK8637IgyoCGdiND6/SXsa8SwLJo3VTjqx4eKpX7cvlSBL +RrRxc50TmwUsAhsd4CDl9YnSATLjVvJBeYlfM2tbFPaYwl1aR8v+PWkfnK0efm60 +fHki33HEnGteBPKuGq4vwVYpn6bYGwQz+f6335/A2DMfZHFSpjVURHPcRcHbCMla +0cUxAoIBAQC25eYNkO478mo+bBbEXJlkoqLmvjAyGrNFo48F9lpVH6Y0vNuWkXJN +PUgLUhAu6RYotjGENqG17rz8zt/PPY9Ok2P3sOx8t00y1mIn/hlDZXs55FM0fOMu +PZaiscAPs7HDzvyOmDah+fzi+ZD8H2M3DS2W+YE0iaeJa2vZJS2t02W0BGXiDI33 +IZDqMyLYvwwPjOnShJydEzXID4xLl0tNjzLxo3GSNA7jYqlmbtV8CXIc7rMSL6WV +ktIDKKJcnmpn3TcKeX6MEjaSIT82pNOS3fY3PmXuL+CMzfw8+u77Eecq78fHaTiL +P5JGM93F6mzi19EY0tmInUBMCWtQLcENAoIBAQCg0KaOkb8T36qzPrtgbfou0E2D +ufdpL1ugmD4edOFKQB5fDFQhLnSEVSJq3KUg4kWsXapQdsBd6kLdxS+K6MQrLBzr +4tf0c7UCF1AzWk6wXMExZ8mRb2RkGZYQB2DdyhFB3TPmnq9CW8JCq+6kxg/wkU4s +vM4JXzgcqVoSf42QJl+B9waeWhg0BTWx01lal4ds88HvEKmE0ik5GwiDbr7EvDDw +E6UbZtQcIoSTIIZDgYqVFfR2DAho3wXJRsOXh433lEJ8X7cCDzrngFbQnlKrpwML +Xgm0SIUc+Nf5poMM3rfLFK77t/ob4w+5PwRKcoSniyAxrHd6bwykYA8Vuydv +-----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----- |