Import code from system/bvb repo and relicense to MIT.

BUG=29099910
TEST=Unit tests pass.

Change-Id: Ifc8615752aa6d62e7e6b9037491daa140bb36765
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)
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..d21621a
--- /dev/null
+++ b/LICENSE
@@ -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.
diff --git a/README b/README
new file mode 100644
index 0000000..d230039
--- /dev/null
+++ b/README
@@ -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));
+}
diff --git a/bvbtool b/bvbtool
new file mode 100755
index 0000000..2e52857
--- /dev/null
+++ b/bvbtool
@@ -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*) &in;
+  uint32_t ret;
+  ret  = ((uint32_t) d[0]) << 24;
+  ret |= ((uint32_t) d[1]) << 16;
+  ret |= ((uint32_t) d[2]) << 8;
+  ret |= ((uint32_t) d[3]);
+  return ret;
+}
+
+uint64_t bvb_be64toh(uint64_t in) {
+  uint8_t* d = (uint8_t*) &in;
+  uint64_t ret;
+  ret  = ((uint64_t) d[0]) << 56;
+  ret |= ((uint64_t) d[1]) << 48;
+  ret |= ((uint64_t) d[2]) << 40;
+  ret |= ((uint64_t) d[3]) << 32;
+  ret |= ((uint64_t) d[4]) << 24;
+  ret |= ((uint64_t) d[5]) << 16;
+  ret |= ((uint64_t) d[6]) << 8;
+  ret |= ((uint64_t) d[7]);
+  return ret;
+}
+
+void bvb_boot_image_header_to_host_byte_order(const BvbBootImageHeader* src,
+                                              BvbBootImageHeader* dest) {
+  bvb_memcpy(dest, src, sizeof(BvbBootImageHeader));
+
+  dest->header_version_major = bvb_be32toh(dest->header_version_major);
+  dest->header_version_minor = bvb_be32toh(dest->header_version_minor);
+
+  dest->authentication_data_block_size =
+      bvb_be64toh(dest->authentication_data_block_size);
+  dest->auxilary_data_block_size = bvb_be64toh(dest->auxilary_data_block_size);
+  dest->payload_data_block_size = bvb_be64toh(dest->payload_data_block_size);
+
+  dest->algorithm_type = bvb_be32toh(dest->algorithm_type);
+
+  dest->hash_offset = bvb_be64toh(dest->hash_offset);
+  dest->hash_size = bvb_be64toh(dest->hash_size);
+
+  dest->signature_offset = bvb_be64toh(dest->signature_offset);
+  dest->signature_size = bvb_be64toh(dest->signature_size);
+
+  dest->public_key_offset = bvb_be64toh(dest->public_key_offset);
+  dest->public_key_size = bvb_be64toh(dest->public_key_size);
+
+  dest->properties_offset = bvb_be64toh(dest->properties_offset);
+  dest->properties_size = bvb_be64toh(dest->properties_size);
+
+  dest->rollback_index = bvb_be64toh(dest->rollback_index);
+
+  dest->kernel_offset = bvb_be64toh(dest->kernel_offset);
+  dest->kernel_size = bvb_be64toh(dest->kernel_size);
+
+  dest->initrd_offset = bvb_be64toh(dest->initrd_offset);
+  dest->initrd_size = bvb_be64toh(dest->initrd_size);
+
+  dest->kernel_addr = bvb_be64toh(dest->kernel_addr);
+  dest->initrd_addr = bvb_be64toh(dest->initrd_addr);
+}
+
+void bvb_rsa_public_key_header_to_host_byte_order(
+    const BvbRSAPublicKeyHeader* src,
+    BvbRSAPublicKeyHeader* dest) {
+  bvb_memcpy(dest, src, sizeof(BvbRSAPublicKeyHeader));
+
+  dest->key_num_bits = bvb_be32toh(dest->key_num_bits);
+  dest->n0inv = bvb_be32toh(dest->n0inv);
+}
+
+int bvb_safe_add_to(uint64_t *value, uint64_t value_to_add) {
+  uint64_t original_value;
+
+  bvb_assert(value != NULL);
+
+  original_value = *value;
+
+  *value += value_to_add;
+  if (*value < original_value) {
+    bvb_debug("%s: overflow: 0x%016" PRIx64 " + 0x%016" PRIx64 "\n",
+              __FUNCTION__, original_value, value_to_add);
+    return 0;
+  }
+
+  return 1;
+}
+
+int bvb_safe_add(uint64_t* out_result, uint64_t a, uint64_t b) {
+  uint64_t dummy;
+  if (out_result == NULL)
+    out_result = &dummy;
+  *out_result = a;
+  return bvb_safe_add_to(out_result, b);
+}
diff --git a/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
new file mode 100644
index 0000000..6d13e99
--- /dev/null
+++ b/test/dummy_initrd.bin
Binary files differ
diff --git a/test/dummy_kernel.bin b/test/dummy_kernel.bin
new file mode 100644
index 0000000..3666773
--- /dev/null
+++ b/test/dummy_kernel.bin
Binary files differ
diff --git a/test/dummy_rootfs.bin b/test/dummy_rootfs.bin
new file mode 100644
index 0000000..e2a36c2
--- /dev/null
+++ b/test/dummy_rootfs.bin
Binary files differ
diff --git a/test/small_blob.bin b/test/small_blob.bin
new file mode 100644
index 0000000..d712de6
--- /dev/null
+++ b/test/small_blob.bin
Binary files differ
diff --git a/test/testkey_rsa2048.pem b/test/testkey_rsa2048.pem
new file mode 100644
index 0000000..867dcff
--- /dev/null
+++ b/test/testkey_rsa2048.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAxlVR3TIkouAOvH79vaJTgFhpfvVKQIeVkFRZPVXK/zY0Gvrh
+4JAqGjJoW/PfrQv5sdD36qtHH3a+G5hLZ6Ni+t/mtfjucxZfuLGC3kmJ1T3XqEKZ
+gXXI2IR7vVSoImREvDQGEDyJwtHzLANlkbGg0cghVhWZSCAndO8BenalC2v94/rt
+DfkPekH6dgU3Sf40T0sBSeSY94mOzTaqOR2pfV1rWlLRdWmo33zeHBv52Rlbt0dM
+uXAureXWiHztkm5GCBC1dgM+CaxNtizNEgC91KcD0xuRCCM2WxH+r1lpszyIJDct
+YbrFmVEYl/kjQpafhy7Nsk1fqSTyRdriZSYmTQIDAQABAoIBAQC+kJgaCuX8wYAn
+SXWQ0fmdZlXnMNRpcF0a0pD0SAzGb1RdYBXMaXiqtyhiwc53PPxsCDdNecjayIMd
+jJVXPTwLhTruOgMS/bp3gcgWwV34UHV4LJXGOGAE+jbS0hbDBMiudOYmj6RmVshp
+z9G1zZCSQNMXHaWsEYkX59XpzzoB384nRul2QgEtwzUNR9XlpzgtJBLk3SACkvsN
+mQ/DW8IWHXLg8vLn1LzVJ2e3B16H4MoE2TCHxqfMgr03IDRRJogkenQuQsFhevYT
+o/mJyHSWavVgzMHG9I5m+eepF4Wyhj1Y4WyKAuMI+9dHAX/h7Lt8XFCQCh5DbkVG
+zGr34sWBAoGBAOs7n7YZqNaaguovfIdRRsxxZr1yJAyDsr6w3yGImDZYju4c4WY9
+5esO2kP3FA4p0c7FhQF5oOb1rBuHEPp36cpL4aGeK87caqTfq63WZAujoTZpr9Lp
+BRbkL7w/xG7jpQ/clpA8sHzHGQs/nelxoOtC7E118FiRgvD/jdhlMyL9AoGBANfX
+vyoN1pplfT2xR8QOjSZ+Q35S/+SAtMuBnHx3l0qH2bbBjcvM1MNDWjnRDyaYhiRu
+i+KA7tqfib09+XpB3g5D6Ov7ls/Ldx0S/VcmVWtia2HK8y8iLGtokoBZKQ5AaFX2
+iQU8+tC4h69GnJYQKqNwgCUzh8+gHX5Y46oDiTmRAoGAYpOx8lX+czB8/Da6MNrW
+mIZNT8atZLEsDs2ANEVRxDSIcTCZJId7+m1W+nRoaycLTWNowZ1+2ErLvR10+AGY
+b7Ys79Wg9idYaY9yGn9lnZsMzAiuLeyIvXcSqgjvAKlVWrhOQFOughvNWvFl85Yy
+oWSCMlPiTLtt7CCsCKsgKuECgYBgdIp6GZsIfkgclKe0hqgvRoeU4TR3gcjJlM9A
+lBTo+pKhaBectplx9RxR8AnsPobbqwcaHnIfAuKDzjk5mEvKZjClnFXF4HAHbyAF
+nRzZEy9XkWFhc80T5rRpZO7C7qdxmu2aiKixM3V3L3/0U58qULEDbubHMw9bEhAT
+PudI8QKBgHEEiMm/hr9T41hbQi/LYanWnlFw1ue+osKuF8bXQuxnnHNuFT/c+9/A
+vWhgqG6bOEHu+p/IPrYm4tBMYlwsyh4nXCyGgDJLbLIfzKwKAWCtH9LwnyDVhOow
+GH9shdR+sW3Ew97xef02KAH4VlNANEmBV4sQNqWWvsYrcFm2rOdL
+-----END RSA PRIVATE KEY-----
diff --git a/test/testkey_rsa4096.pem b/test/testkey_rsa4096.pem
new file mode 100644
index 0000000..26db5c3
--- /dev/null
+++ b/test/testkey_rsa4096.pem
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+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
+-----END RSA PRIVATE KEY-----
diff --git a/test/testkey_rsa8192.pem b/test/testkey_rsa8192.pem
new file mode 100644
index 0000000..a383428
--- /dev/null
+++ b/test/testkey_rsa8192.pem
@@ -0,0 +1,99 @@
+-----BEGIN RSA PRIVATE KEY-----
+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
+-----END RSA PRIVATE KEY-----