Project import
diff --git a/avb/.clang-format b/avb/.clang-format
new file mode 100644
index 0000000..51ff646
--- /dev/null
+++ b/avb/.clang-format
@@ -0,0 +1,26 @@
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+BasedOnStyle: Google
+AllowShortFunctionsOnASingleLine: Empty
+AllowShortIfStatementsOnASingleLine: true
+AllowShortLoopsOnASingleLine: false
+BinPackArguments: false
+BinPackParameters: false
+CommentPragmas: NOLINT:.*
+DerivePointerAlignment: false
+PointerAlignment: Left
+TabWidth: 2
diff --git a/avb/Android.mk b/avb/Android.mk
new file mode 100644
index 0000000..533c2d8
--- /dev/null
+++ b/avb/Android.mk
@@ -0,0 +1,212 @@
+#
+# 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.
+#
+
+LOCAL_PATH := $(my-dir)
+
+avb_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
+avb_common_cppflags := \
+ -Wnon-virtual-dtor \
+ -fno-strict-aliasing
+avb_common_ldflags := \
+ -Wl,--gc-sections
+avb_lib_common_files := \
+ libavb/avb_chain_partition_descriptor.c \
+ libavb/avb_crc32.c \
+ libavb/avb_crypto.c \
+ libavb/avb_descriptor.c \
+ libavb/avb_footer.c \
+ libavb/avb_hash_descriptor.c \
+ libavb/avb_hashtree_descriptor.c \
+ libavb/avb_kernel_cmdline_descriptor.c \
+ libavb/avb_property_descriptor.c \
+ libavb/avb_rsa.c \
+ libavb/avb_sha256.c \
+ libavb/avb_sha512.c \
+ libavb/avb_slot_verify.c \
+ libavb/avb_util.c \
+ libavb/avb_vbmeta_image.c
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := avbtool
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_REQUIRED_MODULES := fec
+LOCAL_IS_HOST_MODULE := true
+LOCAL_MODULE := avbtool
+include $(BUILD_PREBUILT)
+
+# Build libavb for the target (for e.g. fs_mgr usage).
+include $(CLEAR_VARS)
+LOCAL_MODULE := libavb
+LOCAL_MODULE_HOST_OS := linux
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
+LOCAL_CLANG := true
+LOCAL_CFLAGS := $(avb_common_cflags) -DAVB_ENABLE_DEBUG -DAVB_COMPILATION
+LOCAL_LDFLAGS := $(avb_common_ldflags)
+LOCAL_SRC_FILES := \
+ $(avb_lib_common_files) \
+ libavb/avb_sysdeps_posix.c
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libavb_32
+LOCAL_MODULE_HOST_OS := linux
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
+LOCAL_MULTILIB := 32
+LOCAL_CLANG := false
+LOCAL_CFLAGS := $(avb_common_cflags) -DAVB_ENABLE_DEBUG -DAVB_COMPILATION -fno-unwind-tables -std=c99
+LOCAL_LDFLAGS := $(avb_common_ldflags)
+LOCAL_SRC_FILES := $(avb_lib_common_files)
+include $(BUILD_STATIC_LIBRARY)
+
+# Build libavb for the host (for unit tests).
+include $(CLEAR_VARS)
+LOCAL_MODULE := libavb_host
+LOCAL_MODULE_HOST_OS := linux
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+LOCAL_CLANG := true
+LOCAL_CFLAGS := $(avb_common_cflags) -fno-stack-protector -DAVB_ENABLE_DEBUG -DAVB_COMPILATION
+LOCAL_LDFLAGS := $(avb_common_ldflags)
+LOCAL_SRC_FILES := \
+ $(avb_lib_common_files)
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+# Build libavb_ab for the host (for unit tests).
+include $(CLEAR_VARS)
+LOCAL_MODULE := libavb_ab_host
+LOCAL_REQUIRED_MODULES := libavb_host
+LOCAL_MODULE_HOST_OS := linux
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+LOCAL_CLANG := true
+LOCAL_CFLAGS := $(avb_common_cflags) -fno-stack-protector -DAVB_ENABLE_DEBUG -DAVB_COMPILATION
+LOCAL_LDFLAGS := $(avb_common_ldflags)
+LOCAL_SRC_FILES := \
+ libavb_ab/avb_ab_flow.c
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+# Build libavb_atx for the host (for unit tests).
+include $(CLEAR_VARS)
+LOCAL_MODULE := libavb_atx_host
+LOCAL_REQUIRED_MODULES := libavb_host
+LOCAL_MODULE_HOST_OS := linux
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+LOCAL_CLANG := true
+LOCAL_CFLAGS := $(avb_common_cflags) -fno-stack-protector -DAVB_ENABLE_DEBUG -DAVB_COMPILATION
+LOCAL_LDFLAGS := $(avb_common_ldflags)
+LOCAL_SRC_FILES := \
+ libavb_atx/avb_atx_validate.c
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libavb_host_sysdeps
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
+LOCAL_MODULE_HOST_OS := linux
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+LOCAL_CLANG := true
+LOCAL_CFLAGS := $(avb_common_cflags) -DAVB_ENABLE_DEBUG -DAVB_COMPILATION
+LOCAL_LDFLAGS := $(avb_common_ldflags)
+LOCAL_SRC_FILES := \
+ libavb/avb_sysdeps_posix.c
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libavb_host_unittest
+LOCAL_REQUIRED_MODULES := simg2img img2simg avbtool
+LOCAL_MODULE_HOST_OS := linux
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_CLANG := true
+LOCAL_CFLAGS := $(avb_common_cflags) -DAVB_ENABLE_DEBUG -DAVB_COMPILATION
+LOCAL_CPPFLAGS := $(avb_common_cppflags)
+LOCAL_LDFLAGS := $(avb_common_ldflags)
+LOCAL_STATIC_LIBRARIES := \
+ libavb_host \
+ libavb_host_sysdeps \
+ libavb_ab_host \
+ libavb_atx_host \
+ libgmock_host \
+ libgtest_host
+LOCAL_SHARED_LIBRARIES := \
+ libchrome \
+ libcrypto
+LOCAL_SRC_FILES := \
+ test/avb_ab_flow_unittest.cc \
+ test/avb_atx_validate_unittest.cc \
+ test/avb_slot_verify_unittest.cc \
+ test/avb_unittest_util.cc \
+ test/avb_util_unittest.cc \
+ test/avb_vbmeta_image_unittest.cc \
+ test/avbtool_unittest.cc \
+ test/fake_avb_ops.cc
+LOCAL_LDLIBS_linux := -lrt
+include $(BUILD_HOST_NATIVE_TEST)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libavb_host_user_code_test
+LOCAL_MODULE_HOST_OS := linux
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_CLANG := true
+LOCAL_CFLAGS := $(avb_common_cflags)
+LOCAL_CPPFLAGS := $(avb_common_cppflags)
+LOCAL_LDFLAGS := $(avb_common_ldflags)
+LOCAL_SRC_FILES := test/user_code_test.cc
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := bootctrl.avb
+LOCAL_MODULE_RELATIVE_PATH := hw
+LOCAL_REQUIRED_MODULES := libavb
+LOCAL_SRC_FILES := \
+ boot_control/boot_control_avb.c \
+ boot_control/avb_ops_device.c \
+ libavb_ab/avb_ab_flow.c \
+ libavb/avb_sysdeps_posix.c
+LOCAL_CLANG := true
+LOCAL_CFLAGS := $(avb_common_cflags) -DAVB_COMPILATION
+LOCAL_LDFLAGS := $(avb_common_ldflags)
+LOCAL_SHARED_LIBRARIES := libbase libcutils
+LOCAL_STATIC_LIBRARIES := libfs_mgr libavb
+LOCAL_POST_INSTALL_CMD := \
+ $(hide) mkdir -p $(TARGET_OUT_SHARED_LIBRARIES)/hw && \
+ ln -sf bootctrl.avb.so $(TARGET_OUT_SHARED_LIBRARIES)/hw/bootctrl.default.so
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := libavb_host_symbols_test
+LOCAL_MODULE_TAGS := debug
+LOCAL_ADDITIONAL_DEPENDENCIES := libavb_ab_host
+include $(BUILD_HOST_PREBUILT)
diff --git a/avb/LICENSE b/avb/LICENSE
new file mode 100644
index 0000000..d21621a
--- /dev/null
+++ b/avb/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/avb/MODULE_LICENSE_ANDROID b/avb/MODULE_LICENSE_ANDROID
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/avb/MODULE_LICENSE_ANDROID
diff --git a/avb/PREUPLOAD.cfg b/avb/PREUPLOAD.cfg
new file mode 100644
index 0000000..61948d2
--- /dev/null
+++ b/avb/PREUPLOAD.cfg
@@ -0,0 +1,5 @@
+[Builtin Hooks]
+clang_format = true
+
+[Builtin Hooks Options]
+clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc
diff --git a/avb/README b/avb/README
new file mode 100644
index 0000000..1439785
--- /dev/null
+++ b/avb/README
@@ -0,0 +1,307 @@
+This directory contains avbtool, libavb, and libavb_ab.
+
+The main job of avbtool is to create vbmeta.img which is the
+top-level object for verified boot. This image is designed to go into
+the vbmeta partition (or, if using A/B, the slot in question
+e.g. vbmeta_a or vbmeta_b) and be of minimal size (for out-of-band
+updates). The vbmeta image is cryptographically signed and contains
+verification data (e.g. cryptographic digests) for verifying boot.img,
+system.img, and other partitions/images.
+
+The vbmeta image can also contain references to other partitions where
+verification data is stored as well as a public key indicating who
+should sign the verification data. This indirection provides
+delegation, that is, it allows a 3rd party to control content on a given
+partition by including the public key said 3rd party is using to sign
+the data with, in vbmeta.img. By design, this authority can be easily
+revoked by simply updating vbmeta.img with new descriptors for the
+partition in question.
+
+Storing signed verification data on other images - for example
+boot.img and system.img - is also done with avbtool.
+
+In addition to avbtool, a library - libavb - is provided. This library
+performs all verification on the device side e.g. it starts by loading
+the vbmeta partition, checks the signature, and then goes on to load
+the boot partition for verification.
+
+The libavb library is intended to be used in both boot loaders and
+inside Android. It has a simple abstraction for system dependencies
+(see libavb/avb_sysdeps.h) as well as operations that the boot loader
+or OS is expected to implement (see libavb/avb_ops.h). The main entry
+point for verification is avb_slot_verify().
+
+It is expected that most devices will use A/B (e.g. multiple copies of
+the OS in separate so-called 'slots') in addition to AVB. While
+managing A/B metadata and associated metadata (e.g. managing
+stored_rollback_index[n] locations) is outside the scope of libavb,
+enough interfaces are exposed so the boot loader can integrate its A/B
+stack with libavb. In particular avb_slot_verify() takes a
+|slot_suffix| parameter and its result struct |AvbSlotVerifyData|
+convey the rollback indexes in the image that was verified.
+
+AVB also includes an A/B implementation that boot loaders may
+optionally use. This implementation is in the libavb_ab library and
+integrates with image verification including updating the
+stored_rollback_index[n] locations on the device as needed. The
+bootloader can use this through the avb_ab_flow() function. This
+library is built on top of libavb.
+
+In libavb_ab, A/B metadata is stored in the 'misc' partition using a
+format private to libavb_ab in the location on 'misc' reserved for
+this. For more information about the misc.img file format see the
+bootable/recovery/bootloader.h file. A/B metadata can be written to
+misc.img using the set_ab_metadata sub-command of avbtool. A/B
+metadata is comprised of (for each slot) a priority field (0 to 15),
+number of tries remaining for attempting to boot the slot (0 to 7),
+and a flag to indicate whether the slot has successfully booted.
+
+A/B metadata integrity is provided by a simple magic marker and a
+CRC-32 checksum. If invalid A/B metadata is detected, the behavior is
+to reset the A/B metadata to a known state where both slots are given
+seven boot tries.
+
+An implementation of a boot_control HAL using AVB-specific A/B
+metadata is also provided.
+
+Android Things has specific requirements and validation logic for the
+vbmeta public key. An extension is provided in libavb_atx which performs
+this validation as an implementatio of libavb's public key validation
+operation (see avb_validate_vbmeta_public_key in avb_ops.h).
+
+-- FILES AND DIRECTORIES
+
+ libavb/
+
+ An implementation of image verification. This code is designed to
+ be highly portable so it can be used in as many contexts as
+ possible. This code requires a C99-compliant C compiler.
+
+ Part of this code is considered internal to the implementation and
+ should not be used outside it. For example, this applies to the
+ avb_rsa.[ch] and avb_sha.[ch] files.
+
+ System dependencies expected to be provided by the platform is
+ defined in avb_sysdeps.h. If the platform provides the standard C
+ runtime avb_sysdeps_posix.c can be used.
+
+ libavb_ab/
+
+ An A/B implementation for use in boot loaders. This is built on top
+ of libavb.
+
+ libavb_atx/
+
+ An Android Things Extension for validating public key metadata.
+
+ boot_control/
+
+ An implemementation of the Android boot_control HAL for use with
+ boot loaders using libavb_ab.
+
+ Android.mk
+
+ Build instructions for building libavb (a static library for use on
+ the device), host-side libraries (for unit tests), and unit tests.
+
+ avbtool
+
+ A tool written in Python for working with images related to
+ verified boot.
+
+ test/
+
+ Contains unit tests for abvtool, libavb, libavb_ab, and libavb_atx.
+
+ examples/uefi/
+
+ Contains the source-code for a UEFI-based boot-loader utilizing
+ libavb/ and libavb_ab/.
+
+-- AUDIENCE AND PORTABILITY NOTES
+
+This code is intended to be used in bootloaders in devices running
+Android. 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 libavb/ and libavb_ab/ codebase will evolve over time so
+integration should be as non-invasive as possible. The intention is to
+keep the API of the library stable however it will be broken if
+necessary.
+
+As for portability, the library 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 AVB_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 AVB_COMPILATION should be set only when
+compiling the libraries. The code must be compiled into a separate
+libraries.
+
+Applications using the compiled libavb library must only include the
+libavb/libavb.h file (which will include all public interfaces) and
+must not have the AVB_COMPILATION preprocessor symbol set. This is to
+ensure that internal code that may be change in the future (for
+example avb_sha.[ch] and avb_rsa.[ch]) will not be visible to
+application code.
+
+Applications using the compiled libavb_ab library must only include
+the libavb_ab/libavb_ab.h file for similar reasons.
+
+-- COMPATIBILITY NOTES
+
+The VBMeta structure (as defined in libavb/avb_vbmeta_header.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 AvbVBMetaImageHeader - the minor version will be
+bumped. At the same time, libavb 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 library is able to read an
+old vbmeta image header produced with version 1.M where N > M.
+
+The usual scenario is that the code parsing the AvbVBMetaImageHeader
+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 avbtool used to produce the
+vbmeta 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 avbtool (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, avbtool 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 avb tool the same, images
+produced by newer versions of avb will continue to work on the same
+version of the reference implementation.
+
+-- USAGE
+
+The content for the vbmeta partition can be generated as follows:
+
+ $ avbtool make_vbmeta_image \
+ --output OUTPUT \
+ [--algorithm ALGORITHM] [--key /path/to/key_used_for_signing_or_pub_key] \
+ [--public_key_metadata /path/to/pkmd.bin] [--rollback_index NUMBER] \
+ [--include_descriptors_from_footer /path/to/image.bin] \
+ [--setup_rootfs_from_kernel /path/to/image.bin] \
+ [--chain_partition part_name:rollback_index_location:/path/to/key1.bin] \
+ [--signing_helper /path/to/external/signer]
+
+An integrity footer containing the hash for an entire partition can be
+added to an existing image as follows:
+
+ $ avbtool add_hash_footer \
+ --image IMAGE \
+ --partition_name PARTNAME --partition_size SIZE \
+ [--algorithm ALGORITHM] [--key /path/to/key_used_for_signing_or_pub_key] \
+ [--public_key_metadata /path/to/pkmd.bin] [--rollback_index NUMBER] \
+ [--hash_algorithm HASH_ALG] [--salt HEX] \
+ [--include_descriptors_from_footer /path/to/image.bin] \
+ [--setup_rootfs_from_kernel /path/to/image.bin] \
+ [--signing_helper /path/to/external/signer]
+
+An integrity footer containing the root digest and salt for a hashtree
+for a partition can be added to an existing image as follows. The
+hashtree is also appended to the image.
+
+ $ avbtool add_hashtree_footer \
+ --image IMAGE \
+ --partition_name PARTNAME --partition_size SIZE \
+ [--algorithm ALGORITHM] [--key /path/to/key_used_for_signing_or_pub_key] \
+ [--public_key_metadata /path/to/pkmd.bin] [--rollback_index NUMBER] \
+ [--hash_algorithm HASH_ALG] [--salt HEX] [--block_size SIZE] \
+ [--include_descriptors_from_footer /path/to/image.bin] \
+ [--setup_rootfs_from_kernel /path/to/image.bin] \
+ [--generate_fec] [--fec_num_roots FEC_NUM_ROOTS] \
+ [--signing_helper /path/to/external/signer]
+
+The integrity footer on an image can be removed from an image. The
+hashtree can optionally be kept in place.
+
+ $ avbtool erase_footer --image IMAGE [--keep_hashtree]
+
+To calculate the maximum size of an image that will fit in a partition
+of a given size after having used the 'avbtool add_hashtree_footer'
+command on it, use the --calc_max_image_size option:
+
+ $ avbtool add_hashtree_footer --partition_size $((10*1024*1024)) \
+ --calc_max_image_size
+ 10330112
+
+The --signing_helper option can be used in 'make_vbmeta_image', 'add_hash_footer'
+and 'add_hashtree_footer' commands to specify any external program for signing
+hashes. The data to sign (including padding e.g. PKCS1-v1.5) is fed via STDIN
+and the signed data is returned via STDOUT. If --signing_helper is present in
+a command line, the --key option need only contain a public key. Arguments for
+a signing helper are 'algorithm' and 'public key'. If the signing helper exits
+with a non-zero exit code, it means failure.
+
+Here's an example invocation:
+
+ /path/to/my_signing_program SHA256_RSA2048 /path/to/publickey.pem
+
+-- BUILD SYSTEM INTEGRATION NOTES
+
+Android Verified Boot is enabled by the BOARD_AVB_ENABLE variable
+
+ BOARD_AVB_ENABLE := true
+
+This will make the build system create vbmeta.img which will contain a
+hash descriptor for boot.img, a hashtree descriptor for system.img, a
+kernel-cmdline descriptor for setting up dm-verity for system.img and
+append a hash-tree to system.img.
+
+By default, the algorithm SHA256_RSA4096 is used with a test key from
+this directory. This can be overriden by the BOARD_AVB_ALGORITHM and
+BOARD_AVB_KEY_PATH variables to use e.g. a 4096-bit RSA key and
+SHA-512:
+
+ BOARD_AVB_ALGORITHM := SHA512_RSA4096
+ BOARD_AVB_KEY_PATH := /path/to/rsa_key_4096bits.pem
+
+Remember that the public part of this key needs to be available to the
+bootloader of the device expected to verify resulting images. Use
+'avbtool extract_public_key' to extract the key in the expected format
+("AVB_pk" in the following). If the device is using a different root
+of trust than AVB_pk the --public_key_metadata option can be used to
+embed a blob ("AVB_pkmd" in the following) that can be used to
+e.g. derive AVB_pk. Both AVB_pk and AVB_pkmd is passed to the
+bootloader for validation as part of verifying a slot.
+
+To prevent rollback attacks, the rollback index should be increased on
+a regular basis. The rollback index can be set with the
+BOARD_AVB_ROLLBACK_INDEX variable:
+
+ BOARD_AVB_ROLLBACK_INDEX := 5
+
+If this is not set, the rollback index defaults to 0.
+
+The variable BOARD_AVB_MAKE_VBMETA_IMAGE_ARGS can be used to specify
+additional options passed to 'avbtool make_vbmeta_image'. Typical
+options to be used here include '--prop', '--prop_from_file', and
+'--chain_partition'.
+
+The variable BOARD_AVBTOOL_BOOT_ADD_HASH_FOOTER_ARGS can be used to
+specify additional options passed to 'avbtool add_hash_footer' for
+boot.img. Typical options to be used here include '--hash_algorithm'
+and '--salt'.
+
+The variable BOARD_AVBTOOL_SYSTEM_ADD_HASHTREE_FOOTER_ARGS can be used
+to specify additional options passed to 'avbtool add_hashtree_footer'
+for systems.img. Typical options to be used here include
+'--hash_algorithm', '--salt', and '--block_size'.
diff --git a/avb/avbtool b/avb/avbtool
new file mode 100755
index 0000000..b62bd03
--- /dev/null
+++ b/avb/avbtool
@@ -0,0 +1,3077 @@
+#!/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 Android Verified Boot images."""
+
+import argparse
+import binascii
+import bisect
+import hashlib
+import os
+import struct
+import subprocess
+import sys
+import tempfile
+import time
+
+import Crypto.PublicKey.RSA
+
+# Keep in sync with avb_vbmeta_header.h.
+AVB_VERSION_MAJOR = 1
+AVB_VERSION_MINOR = 0
+AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED = 1
+
+class AvbError(Exception):
+ """Application-specific errors.
+
+ These errors represent issues for which a stack-trace should not be
+ presented.
+
+ Attributes:
+ message: Error message.
+ """
+
+ def __init__(self, message):
+ Exception.__init__(self, message)
+
+
+class Algorithm(object):
+ """Contains details about an algorithm.
+
+ See the avb_vbmeta_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 |AvbAlgorithmType|.
+ 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 the avb_crypto.h file.
+#
+# The PKC1-v1.5 padding is a blob of binary DER of ASN.1 and is
+# obtained from section 5.2.2 of RFC 4880.
+ALGORITHMS = {
+ 'NONE': Algorithm(
+ algorithm_type=0, # AVB_ALGORITHM_TYPE_NONE
+ hash_num_bytes=0,
+ signature_num_bytes=0,
+ public_key_num_bytes=0,
+ padding=[]),
+ 'SHA256_RSA2048': Algorithm(
+ algorithm_type=1, # AVB_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, # AVB_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, # AVB_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, # AVB_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, # AVB_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, # AVB_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 encode_long(num_bits, value):
+ """Encodes a long to a bytearray() using a given amount of bits.
+
+ This number is written big-endian, e.g. with the most significant
+ bit first.
+
+ Arguments:
+ num_bits: The number of bits to write, e.g. 2048.
+ value: The value to write.
+
+ Returns:
+ A bytearray() with the encoded long.
+ """
+ ret = bytearray()
+ for bit_pos in range(num_bits, 0, -8):
+ octet = (value >> (bit_pos - 8)) & 0xff
+ ret.extend(struct.pack('!B', octet))
+ return ret
+
+
+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 |AvbRSAPublicKeyHeader| format.
+
+ This writes the |AvbRSAPublicKeyHeader| 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 encode_rsa_key(key):
+ """Encodes a public RSA key in |AvbRSAPublicKeyHeader| format.
+
+ This creates a |AvbRSAPublicKeyHeader| as well as the two large
+ numbers (|key_num_bits| bits long) following it.
+
+ Arguments:
+ key: A Crypto.PublicKey.RSA object.
+
+ Returns:
+ A bytearray() with the |AvbRSAPublicKeyHeader|.
+ """
+ ret = bytearray()
+ # 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
+ ret.extend(struct.pack('!II', key_num_bits, n0inv))
+ ret.extend(encode_long(key_num_bits, key.n))
+ ret.extend(encode_long(key_num_bits, rrmodn))
+ return ret
+
+
+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 AvbError('Unknown algorithm type {}'.format(alg_type))
+
+
+def raw_sign(signing_helper, algorithm_name, key_path, raw_data_to_sign):
+ """Computes a raw RSA signature using |signing_helper| or openssl.
+
+ Arguments:
+ signing_helper: Program which signs a hash and returns the signature.
+ algorithm_name: The algorithm name as per the ALGORITHMS dict.
+ key_path: Path to the private key file. Must be PEM format.
+ raw_data_to_sign: Data to sign (bytearray or str expected).
+
+ Returns:
+ A bytearray containing the signature.
+
+ Raises:
+ Exception: If an error occurs.
+ """
+ p = None
+ if signing_helper is not None:
+ p = subprocess.Popen(
+ [signing_helper, algorithm_name, key_path],
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ else:
+ p = subprocess.Popen(
+ ['openssl', 'rsautl', '-sign', '-inkey', key_path, '-raw'],
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ (pout, perr) = p.communicate(str(raw_data_to_sign))
+ retcode = p.wait()
+ if retcode != 0:
+ raise AvbError('Error signing: {}'.format(perr))
+ return bytearray(pout)
+
+
+class ImageChunk(object):
+ """Data structure used for representing chunks in Android sparse files.
+
+ Attributes:
+ chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
+ chunk_offset: Offset in the sparse file where this chunk begins.
+ output_offset: Offset in de-sparsified file where output begins.
+ output_size: Number of bytes in output.
+ input_offset: Offset in sparse file for data if TYPE_RAW otherwise None.
+ fill_data: Blob with data to fill if TYPE_FILL otherwise None.
+ """
+
+ FORMAT = '<2H2I'
+ TYPE_RAW = 0xcac1
+ TYPE_FILL = 0xcac2
+ TYPE_DONT_CARE = 0xcac3
+ TYPE_CRC32 = 0xcac4
+
+ def __init__(self, chunk_type, chunk_offset, output_offset, output_size,
+ input_offset, fill_data):
+ """Initializes an ImageChunk object.
+
+ Arguments:
+ chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
+ chunk_offset: Offset in the sparse file where this chunk begins.
+ output_offset: Offset in de-sparsified file.
+ output_size: Number of bytes in output.
+ input_offset: Offset in sparse file if TYPE_RAW otherwise None.
+ fill_data: Blob with data to fill if TYPE_FILL otherwise None.
+
+ Raises:
+ ValueError: If data is not well-formed.
+ """
+ self.chunk_type = chunk_type
+ self.chunk_offset = chunk_offset
+ self.output_offset = output_offset
+ self.output_size = output_size
+ self.input_offset = input_offset
+ self.fill_data = fill_data
+ # Check invariants.
+ if self.chunk_type == self.TYPE_RAW:
+ if self.fill_data is not None:
+ raise ValueError('RAW chunk cannot have fill_data set.')
+ if not self.input_offset:
+ raise ValueError('RAW chunk must have input_offset set.')
+ elif self.chunk_type == self.TYPE_FILL:
+ if self.fill_data is None:
+ raise ValueError('FILL chunk must have fill_data set.')
+ if self.input_offset:
+ raise ValueError('FILL chunk cannot have input_offset set.')
+ elif self.chunk_type == self.TYPE_DONT_CARE:
+ if self.fill_data is not None:
+ raise ValueError('DONT_CARE chunk cannot have fill_data set.')
+ if self.input_offset:
+ raise ValueError('DONT_CARE chunk cannot have input_offset set.')
+ else:
+ raise ValueError('Invalid chunk type')
+
+
+class ImageHandler(object):
+ """Abstraction for image I/O with support for Android sparse images.
+
+ This class provides an interface for working with image files that
+ may be using the Android Sparse Image format. When an instance is
+ constructed, we test whether it's an Android sparse file. If so,
+ operations will be on the sparse file by interpreting the sparse
+ format, otherwise they will be directly on the file. Either way the
+ operations do the same.
+
+ For reading, this interface mimics a file object - it has seek(),
+ tell(), and read() methods. For writing, only truncation
+ (truncate()) and appending is supported (append_raw() and
+ append_dont_care()). Additionally, data can only be written in units
+ of the block size.
+
+ Attributes:
+ is_sparse: Whether the file being operated on is sparse.
+ block_size: The block size, typically 4096.
+ image_size: The size of the unsparsified file.
+ """
+ # See system/core/libsparse/sparse_format.h for details.
+ MAGIC = 0xed26ff3a
+ HEADER_FORMAT = '<I4H4I'
+
+ # These are formats and offset of just the |total_chunks| and
+ # |total_blocks| fields.
+ NUM_CHUNKS_AND_BLOCKS_FORMAT = '<II'
+ NUM_CHUNKS_AND_BLOCKS_OFFSET = 16
+
+ def __init__(self, image_filename):
+ """Initializes an image handler.
+
+ Arguments:
+ image_filename: The name of the file to operate on.
+
+ Raises:
+ ValueError: If data in the file is invalid.
+ """
+ self._image_filename = image_filename
+ self._read_header()
+
+ def _read_header(self):
+ """Initializes internal data structures used for reading file.
+
+ This may be called multiple times and is typically called after
+ modifying the file (e.g. appending, truncation).
+
+ Raises:
+ ValueError: If data in the file is invalid.
+ """
+ self.is_sparse = False
+ self.block_size = 4096
+ self._file_pos = 0
+ self._image = open(self._image_filename, 'r+b')
+ self._image.seek(0, os.SEEK_END)
+ self.image_size = self._image.tell()
+
+ self._image.seek(0, os.SEEK_SET)
+ header_bin = self._image.read(struct.calcsize(self.HEADER_FORMAT))
+ (magic, major_version, minor_version, file_hdr_sz, chunk_hdr_sz,
+ block_size, self._num_total_blocks, self._num_total_chunks,
+ _) = struct.unpack(self.HEADER_FORMAT, header_bin)
+ if magic != self.MAGIC:
+ # Not a sparse image, our job here is done.
+ return
+ if not (major_version == 1 and minor_version == 0):
+ raise ValueError('Encountered sparse image format version {}.{} but '
+ 'only 1.0 is supported'.format(major_version,
+ minor_version))
+ if file_hdr_sz != struct.calcsize(self.HEADER_FORMAT):
+ raise ValueError('Unexpected file_hdr_sz value {}.'.
+ format(file_hdr_sz))
+ if chunk_hdr_sz != struct.calcsize(ImageChunk.FORMAT):
+ raise ValueError('Unexpected chunk_hdr_sz value {}.'.
+ format(chunk_hdr_sz))
+
+ self.block_size = block_size
+
+ # Build an list of chunks by parsing the file.
+ self._chunks = []
+
+ # Find the smallest offset where only "Don't care" chunks
+ # follow. This will be the size of the content in the sparse
+ # image.
+ offset = 0
+ output_offset = 0
+ for _ in xrange(1, self._num_total_chunks + 1):
+ chunk_offset = self._image.tell()
+
+ header_bin = self._image.read(struct.calcsize(ImageChunk.FORMAT))
+ (chunk_type, _, chunk_sz, total_sz) = struct.unpack(ImageChunk.FORMAT,
+ header_bin)
+ data_sz = total_sz - struct.calcsize(ImageChunk.FORMAT)
+
+ if chunk_type == ImageChunk.TYPE_RAW:
+ if data_sz != (chunk_sz * self.block_size):
+ raise ValueError('Raw chunk input size ({}) does not match output '
+ 'size ({})'.
+ format(data_sz, chunk_sz*self.block_size))
+ self._chunks.append(ImageChunk(ImageChunk.TYPE_RAW,
+ chunk_offset,
+ output_offset,
+ chunk_sz*self.block_size,
+ self._image.tell(),
+ None))
+ self._image.read(data_sz)
+
+ elif chunk_type == ImageChunk.TYPE_FILL:
+ if data_sz != 4:
+ raise ValueError('Fill chunk should have 4 bytes of fill, but this '
+ 'has {}'.format(data_sz))
+ fill_data = self._image.read(4)
+ self._chunks.append(ImageChunk(ImageChunk.TYPE_FILL,
+ chunk_offset,
+ output_offset,
+ chunk_sz*self.block_size,
+ None,
+ fill_data))
+ elif chunk_type == ImageChunk.TYPE_DONT_CARE:
+ if data_sz != 0:
+ raise ValueError('Don\'t care chunk input size is non-zero ({})'.
+ format(data_sz))
+ self._chunks.append(ImageChunk(ImageChunk.TYPE_DONT_CARE,
+ chunk_offset,
+ output_offset,
+ chunk_sz*self.block_size,
+ None,
+ None))
+ elif chunk_type == ImageChunk.TYPE_CRC32:
+ if data_sz != 4:
+ raise ValueError('CRC32 chunk should have 4 bytes of CRC, but '
+ 'this has {}'.format(data_sz))
+ self._image.read(4)
+ else:
+ raise ValueError('Unknown chunk type {}'.format(chunk_type))
+
+ offset += chunk_sz
+ output_offset += chunk_sz*self.block_size
+
+ # Record where sparse data end.
+ self._sparse_end = self._image.tell()
+
+ # Now that we've traversed all chunks, sanity check.
+ if self._num_total_blocks != offset:
+ raise ValueError('The header said we should have {} output blocks, '
+ 'but we saw {}'.format(self._num_total_blocks, offset))
+ junk_len = len(self._image.read())
+ if junk_len > 0:
+ raise ValueError('There were {} bytes of extra data at the end of the '
+ 'file.'.format(junk_len))
+
+ # Assign |image_size|.
+ self.image_size = output_offset
+
+ # This is used when bisecting in read() to find the initial slice.
+ self._chunk_output_offsets = [i.output_offset for i in self._chunks]
+
+ self.is_sparse = True
+
+ def _update_chunks_and_blocks(self):
+ """Helper function to update the image header.
+
+ The the |total_chunks| and |total_blocks| fields in the header
+ will be set to value of the |_num_total_blocks| and
+ |_num_total_chunks| attributes.
+
+ """
+ self._image.seek(self.NUM_CHUNKS_AND_BLOCKS_OFFSET, os.SEEK_SET)
+ self._image.write(struct.pack(self.NUM_CHUNKS_AND_BLOCKS_FORMAT,
+ self._num_total_blocks,
+ self._num_total_chunks))
+
+ def append_dont_care(self, num_bytes):
+ """Appends a DONT_CARE chunk to the sparse file.
+
+ The given number of bytes must be a multiple of the block size.
+
+ Arguments:
+ num_bytes: Size in number of bytes of the DONT_CARE chunk.
+ """
+ assert num_bytes % self.block_size == 0
+
+ if not self.is_sparse:
+ self._image.seek(0, os.SEEK_END)
+ # This is more efficient that writing NUL bytes since it'll add
+ # a hole on file systems that support sparse files (native
+ # sparse, not Android sparse).
+ self._image.truncate(self._image.tell() + num_bytes)
+ self._read_header()
+ return
+
+ self._num_total_chunks += 1
+ self._num_total_blocks += num_bytes / self.block_size
+ self._update_chunks_and_blocks()
+
+ self._image.seek(self._sparse_end, os.SEEK_SET)
+ self._image.write(struct.pack(ImageChunk.FORMAT,
+ ImageChunk.TYPE_DONT_CARE,
+ 0, # Reserved
+ num_bytes / self.block_size,
+ struct.calcsize(ImageChunk.FORMAT)))
+ self._read_header()
+
+ def append_raw(self, data):
+ """Appends a RAW chunk to the sparse file.
+
+ The length of the given data must be a multiple of the block size.
+
+ Arguments:
+ data: Data to append.
+ """
+ assert len(data) % self.block_size == 0
+
+ if not self.is_sparse:
+ self._image.seek(0, os.SEEK_END)
+ self._image.write(data)
+ self._read_header()
+ return
+
+ self._num_total_chunks += 1
+ self._num_total_blocks += len(data) / self.block_size
+ self._update_chunks_and_blocks()
+
+ self._image.seek(self._sparse_end, os.SEEK_SET)
+ self._image.write(struct.pack(ImageChunk.FORMAT,
+ ImageChunk.TYPE_RAW,
+ 0, # Reserved
+ len(data) / self.block_size,
+ len(data) +
+ struct.calcsize(ImageChunk.FORMAT)))
+ self._image.write(data)
+ self._read_header()
+
+ def append_fill(self, fill_data, size):
+ """Appends a fill chunk to the sparse file.
+
+ The total length of the fill data must be a multiple of the block size.
+
+ Arguments:
+ fill_data: Fill data to append - must be four bytes.
+ size: Number of chunk - must be a multiple of four and the block size.
+ """
+ assert len(fill_data) == 4
+ assert size % 4 == 0
+ assert size % self.block_size == 0
+
+ if not self.is_sparse:
+ self._image.seek(0, os.SEEK_END)
+ self._image.write(fill_data * (size/4))
+ self._read_header()
+ return
+
+ self._num_total_chunks += 1
+ self._num_total_blocks += size / self.block_size
+ self._update_chunks_and_blocks()
+
+ self._image.seek(self._sparse_end, os.SEEK_SET)
+ self._image.write(struct.pack(ImageChunk.FORMAT,
+ ImageChunk.TYPE_FILL,
+ 0, # Reserved
+ size / self.block_size,
+ 4 + struct.calcsize(ImageChunk.FORMAT)))
+ self._image.write(fill_data)
+ self._read_header()
+
+ def seek(self, offset):
+ """Sets the cursor position for reading from unsparsified file.
+
+ Arguments:
+ offset: Offset to seek to from the beginning of the file.
+ """
+ self._file_pos = offset
+
+ def read(self, size):
+ """Reads data from the unsparsified file.
+
+ This method may return fewer than |size| bytes of data if the end
+ of the file was encountered.
+
+ The file cursor for reading is advanced by the number of bytes
+ read.
+
+ Arguments:
+ size: Number of bytes to read.
+
+ Returns:
+ The data.
+
+ """
+ if not self.is_sparse:
+ self._image.seek(self._file_pos)
+ data = self._image.read(size)
+ self._file_pos += len(data)
+ return data
+
+ # Iterate over all chunks.
+ chunk_idx = bisect.bisect_right(self._chunk_output_offsets,
+ self._file_pos) - 1
+ data = bytearray()
+ to_go = size
+ while to_go > 0:
+ chunk = self._chunks[chunk_idx]
+ chunk_pos_offset = self._file_pos - chunk.output_offset
+ chunk_pos_to_go = min(chunk.output_size - chunk_pos_offset, to_go)
+
+ if chunk.chunk_type == ImageChunk.TYPE_RAW:
+ self._image.seek(chunk.input_offset + chunk_pos_offset)
+ data.extend(self._image.read(chunk_pos_to_go))
+ elif chunk.chunk_type == ImageChunk.TYPE_FILL:
+ all_data = chunk.fill_data*(chunk_pos_to_go/len(chunk.fill_data) + 2)
+ offset_mod = chunk_pos_offset % len(chunk.fill_data)
+ data.extend(all_data[offset_mod:(offset_mod + chunk_pos_to_go)])
+ else:
+ assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
+ data.extend('\0' * chunk_pos_to_go)
+
+ to_go -= chunk_pos_to_go
+ self._file_pos += chunk_pos_to_go
+ chunk_idx += 1
+ # Generate partial read in case of EOF.
+ if chunk_idx >= len(self._chunks):
+ break
+
+ return data
+
+ def tell(self):
+ """Returns the file cursor position for reading from unsparsified file.
+
+ Returns:
+ The file cursor position for reading.
+ """
+ return self._file_pos
+
+ def truncate(self, size):
+ """Truncates the unsparsified file.
+
+ Arguments:
+ size: Desired size of unsparsified file.
+
+ Raises:
+ ValueError: If desired size isn't a multiple of the block size.
+ """
+ if not self.is_sparse:
+ self._image.truncate(size)
+ self._read_header()
+ return
+
+ if size % self.block_size != 0:
+ raise ValueError('Cannot truncate to a size which is not a multiple '
+ 'of the block size')
+
+ if size == self.image_size:
+ # Trivial where there's nothing to do.
+ return
+ elif size < self.image_size:
+ chunk_idx = bisect.bisect_right(self._chunk_output_offsets, size) - 1
+ chunk = self._chunks[chunk_idx]
+ if chunk.output_offset != size:
+ # Truncation in the middle of a trunk - need to keep the chunk
+ # and modify it.
+ chunk_idx_for_update = chunk_idx + 1
+ num_to_keep = size - chunk.output_offset
+ assert num_to_keep % self.block_size == 0
+ if chunk.chunk_type == ImageChunk.TYPE_RAW:
+ truncate_at = (chunk.chunk_offset +
+ struct.calcsize(ImageChunk.FORMAT) + num_to_keep)
+ data_sz = num_to_keep
+ elif chunk.chunk_type == ImageChunk.TYPE_FILL:
+ truncate_at = (chunk.chunk_offset +
+ struct.calcsize(ImageChunk.FORMAT) + 4)
+ data_sz = 4
+ else:
+ assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
+ truncate_at = chunk.chunk_offset + struct.calcsize(ImageChunk.FORMAT)
+ data_sz = 0
+ chunk_sz = num_to_keep/self.block_size
+ total_sz = data_sz + struct.calcsize(ImageChunk.FORMAT)
+ self._image.seek(chunk.chunk_offset)
+ self._image.write(struct.pack(ImageChunk.FORMAT,
+ chunk.chunk_type,
+ 0, # Reserved
+ chunk_sz,
+ total_sz))
+ chunk.output_size = num_to_keep
+ else:
+ # Truncation at trunk boundary.
+ truncate_at = chunk.chunk_offset
+ chunk_idx_for_update = chunk_idx
+
+ self._num_total_chunks = chunk_idx_for_update
+ self._num_total_blocks = 0
+ for i in range(0, chunk_idx_for_update):
+ self._num_total_blocks += self._chunks[i].output_size / self.block_size
+ self._update_chunks_and_blocks()
+ self._image.truncate(truncate_at)
+
+ # We've modified the file so re-read all data.
+ self._read_header()
+ else:
+ # Truncating to grow - just add a DONT_CARE section.
+ self.append_dont_care(size - self.image_size)
+
+
+class AvbDescriptor(object):
+ """Class for AVB descriptor.
+
+ See the |AvbDescriptor| C struct for more information.
+
+ Attributes:
+ tag: The tag identifying what kind of descriptor this is.
+ data: The data in the descriptor.
+ """
+
+ SIZE = 16
+ FORMAT_STRING = ('!QQ') # tag, num_bytes_following (descriptor header)
+
+ def __init__(self, data):
+ """Initializes a new property descriptor.
+
+ Arguments:
+ data: If not None, must be a bytearray().
+
+ Raises:
+ LookupError: If the given descriptor is malformed.
+ """
+ assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
+
+ if data:
+ (self.tag, num_bytes_following) = (
+ struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
+ self.data = data[self.SIZE:self.SIZE + num_bytes_following]
+ else:
+ self.tag = None
+ self.data = None
+
+ def print_desc(self, o):
+ """Print the descriptor.
+
+ Arguments:
+ o: The object to write the output to.
+ """
+ o.write(' Unknown descriptor:\n')
+ o.write(' Tag: {}\n'.format(self.tag))
+ if len(self.data) < 256:
+ o.write(' Data: {} ({} bytes)\n'.format(
+ repr(str(self.data)), len(self.data)))
+ else:
+ o.write(' Data: {} bytes\n'.format(len(self.data)))
+
+ def encode(self):
+ """Serializes the descriptor.
+
+ Returns:
+ A bytearray() with the descriptor data.
+ """
+ num_bytes_following = len(self.data)
+ nbf_with_padding = round_to_multiple(num_bytes_following, 8)
+ padding_size = nbf_with_padding - num_bytes_following
+ desc = struct.pack(self.FORMAT_STRING, self.tag, nbf_with_padding)
+ padding = struct.pack(str(padding_size) + 'x')
+ ret = desc + self.data + padding
+ return bytearray(ret)
+
+
+class AvbPropertyDescriptor(AvbDescriptor):
+ """A class for property descriptors.
+
+ See the |AvbPropertyDescriptor| C struct for more information.
+
+ Attributes:
+ key: The key.
+ value: The key.
+ """
+
+ TAG = 0
+ SIZE = 32
+ FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
+ 'Q' # key size (bytes)
+ 'Q') # value size (bytes)
+
+ def __init__(self, data=None):
+ """Initializes a new property descriptor.
+
+ Arguments:
+ data: If not None, must be a bytearray of size |SIZE|.
+
+ Raises:
+ LookupError: If the given descriptor is malformed.
+ """
+ AvbDescriptor.__init__(self, None)
+ assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
+
+ if data:
+ (tag, num_bytes_following, key_size,
+ value_size) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
+ expected_size = round_to_multiple(
+ self.SIZE - 16 + key_size + 1 + value_size + 1, 8)
+ if tag != self.TAG or num_bytes_following != expected_size:
+ raise LookupError('Given data does not look like a property '
+ 'descriptor.')
+ self.key = data[self.SIZE:(self.SIZE + key_size)]
+ self.value = data[(self.SIZE + key_size + 1):(self.SIZE + key_size + 1 +
+ value_size)]
+ else:
+ self.key = ''
+ self.value = ''
+
+ def print_desc(self, o):
+ """Print the descriptor.
+
+ Arguments:
+ o: The object to write the output to.
+ """
+ if len(self.value) < 256:
+ o.write(' Prop: {} -> {}\n'.format(self.key, repr(str(self.value))))
+ else:
+ o.write(' Prop: {} -> ({} bytes)\n'.format(self.key, len(self.value)))
+
+ def encode(self):
+ """Serializes the descriptor.
+
+ Returns:
+ A bytearray() with the descriptor data.
+ """
+ num_bytes_following = self.SIZE + len(self.key) + len(self.value) + 2 - 16
+ nbf_with_padding = round_to_multiple(num_bytes_following, 8)
+ padding_size = nbf_with_padding - num_bytes_following
+ desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
+ len(self.key), len(self.value))
+ padding = struct.pack(str(padding_size) + 'x')
+ ret = desc + self.key + '\0' + self.value + '\0' + padding
+ return bytearray(ret)
+
+
+class AvbHashtreeDescriptor(AvbDescriptor):
+ """A class for hashtree descriptors.
+
+ See the |AvbHashtreeDescriptor| C struct for more information.
+
+ Attributes:
+ 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
+ fec_num_roots: Number of roots used for FEC (0 if FEC is not used).
+ fec_offset: Offset of FEC data (0 if FEC is not used).
+ fec_size: Size of FEC data (0 if FEC is not used).
+ hash_algorithm: Hash algorithm used.
+ partition_name: Partition name.
+ salt: Salt used.
+ root_digest: Root digest.
+ """
+
+ TAG = 1
+ RESERVED = 64
+ SIZE = 116 + RESERVED
+ FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
+ '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)
+ 'L' # FEC number of roots
+ 'Q' # FEC offset (bytes)
+ 'Q' # FEC size (bytes)
+ '32s' # hash algorithm used
+ 'L' # partition name (bytes)
+ 'L' # salt length (bytes)
+ 'L' + # root digest length (bytes)
+ str(RESERVED) + 's') # reserved
+
+ def __init__(self, data=None):
+ """Initializes a new hashtree descriptor.
+
+ Arguments:
+ data: If not None, must be a bytearray of size |SIZE|.
+
+ Raises:
+ LookupError: If the given descriptor is malformed.
+ """
+ AvbDescriptor.__init__(self, None)
+ assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
+
+ if data:
+ (tag, num_bytes_following, self.dm_verity_version, self.image_size,
+ self.tree_offset, self.tree_size, self.data_block_size,
+ self.hash_block_size, self.fec_num_roots, self.fec_offset, self.fec_size,
+ self.hash_algorithm, partition_name_len, salt_len,
+ root_digest_len, _) = struct.unpack(self.FORMAT_STRING,
+ data[0:self.SIZE])
+ expected_size = round_to_multiple(
+ self.SIZE - 16 + partition_name_len + salt_len + root_digest_len, 8)
+ if tag != self.TAG or num_bytes_following != expected_size:
+ raise LookupError('Given data does not look like a hashtree '
+ 'descriptor.')
+ # Nuke NUL-bytes at the end.
+ self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
+ o = 0
+ self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
+ partition_name_len)])
+ # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
+ self.partition_name.decode('utf-8')
+ o += partition_name_len
+ self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
+ o += salt_len
+ self.root_digest = data[(self.SIZE + o):(self.SIZE + o + root_digest_len)]
+ if root_digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
+ raise LookupError('root_digest_len doesn\'t match hash algorithm')
+
+ else:
+ 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.fec_num_roots = 0
+ self.fec_offset = 0
+ self.fec_size = 0
+ self.hash_algorithm = ''
+ self.partition_name = ''
+ self.salt = bytearray()
+ self.root_digest = bytearray()
+
+ def print_desc(self, o):
+ """Print the descriptor.
+
+ Arguments:
+ o: The object to write the output to.
+ """
+ o.write(' Hashtree descriptor:\n')
+ o.write(' Version of dm-verity: {}\n'.format(self.dm_verity_version))
+ o.write(' Image Size: {} bytes\n'.format(self.image_size))
+ o.write(' Tree Offset: {}\n'.format(self.tree_offset))
+ o.write(' Tree Size: {} bytes\n'.format(self.tree_size))
+ o.write(' Data Block Size: {} bytes\n'.format(
+ self.data_block_size))
+ o.write(' Hash Block Size: {} bytes\n'.format(
+ self.hash_block_size))
+ o.write(' FEC num roots: {}\n'.format(self.fec_num_roots))
+ o.write(' FEC offset: {}\n'.format(self.fec_offset))
+ o.write(' FEC size: {} bytes\n'.format(self.fec_size))
+ o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
+ o.write(' Partition Name: {}\n'.format(self.partition_name))
+ o.write(' Salt: {}\n'.format(str(self.salt).encode(
+ 'hex')))
+ o.write(' Root Digest: {}\n'.format(str(
+ self.root_digest).encode('hex')))
+
+ def encode(self):
+ """Serializes the descriptor.
+
+ Returns:
+ A bytearray() with the descriptor data.
+ """
+ encoded_name = self.partition_name.encode('utf-8')
+ num_bytes_following = (self.SIZE + len(encoded_name) + len(self.salt) +
+ len(self.root_digest) - 16)
+ nbf_with_padding = round_to_multiple(num_bytes_following, 8)
+ padding_size = nbf_with_padding - num_bytes_following
+ desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
+ self.dm_verity_version, self.image_size,
+ self.tree_offset, self.tree_size, self.data_block_size,
+ self.hash_block_size, self.fec_num_roots,
+ self.fec_offset, self.fec_size, self.hash_algorithm,
+ len(encoded_name), len(self.salt), len(self.root_digest),
+ self.RESERVED*'\0')
+ padding = struct.pack(str(padding_size) + 'x')
+ ret = desc + encoded_name + self.salt + self.root_digest + padding
+ return bytearray(ret)
+
+
+class AvbHashDescriptor(AvbDescriptor):
+ """A class for hash descriptors.
+
+ See the |AvbHashDescriptor| C struct for more information.
+
+ Attributes:
+ image_size: Image size, in bytes.
+ hash_algorithm: Hash algorithm used.
+ partition_name: Partition name.
+ salt: Salt used.
+ digest: The hash value of salt and data combined.
+ """
+
+ TAG = 2
+ RESERVED = 64
+ SIZE = 68 + RESERVED
+ FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
+ 'Q' # image size (bytes)
+ '32s' # hash algorithm used
+ 'L' # partition name (bytes)
+ 'L' # salt length (bytes)
+ 'L' + # digest length (bytes)
+ str(RESERVED) + 's') # reserved
+
+ def __init__(self, data=None):
+ """Initializes a new hash descriptor.
+
+ Arguments:
+ data: If not None, must be a bytearray of size |SIZE|.
+
+ Raises:
+ LookupError: If the given descriptor is malformed.
+ """
+ AvbDescriptor.__init__(self, None)
+ assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
+
+ if data:
+ (tag, num_bytes_following, self.image_size, self.hash_algorithm,
+ partition_name_len, salt_len,
+ digest_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
+ expected_size = round_to_multiple(
+ self.SIZE - 16 + partition_name_len + salt_len + digest_len, 8)
+ if tag != self.TAG or num_bytes_following != expected_size:
+ raise LookupError('Given data does not look like a hash ' 'descriptor.')
+ # Nuke NUL-bytes at the end.
+ self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
+ o = 0
+ self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
+ partition_name_len)])
+ # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
+ self.partition_name.decode('utf-8')
+ o += partition_name_len
+ self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
+ o += salt_len
+ self.digest = data[(self.SIZE + o):(self.SIZE + o + digest_len)]
+ if digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
+ raise LookupError('digest_len doesn\'t match hash algorithm')
+
+ else:
+ self.image_size = 0
+ self.hash_algorithm = ''
+ self.partition_name = ''
+ self.salt = bytearray()
+ self.digest = bytearray()
+
+ def print_desc(self, o):
+ """Print the descriptor.
+
+ Arguments:
+ o: The object to write the output to.
+ """
+ o.write(' Hash descriptor:\n')
+ o.write(' Image Size: {} bytes\n'.format(self.image_size))
+ o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
+ o.write(' Partition Name: {}\n'.format(self.partition_name))
+ o.write(' Salt: {}\n'.format(str(self.salt).encode(
+ 'hex')))
+ o.write(' Digest: {}\n'.format(str(self.digest).encode(
+ 'hex')))
+
+ def encode(self):
+ """Serializes the descriptor.
+
+ Returns:
+ A bytearray() with the descriptor data.
+ """
+ encoded_name = self.partition_name.encode('utf-8')
+ num_bytes_following = (
+ self.SIZE + len(encoded_name) + len(self.salt) + len(self.digest) - 16)
+ nbf_with_padding = round_to_multiple(num_bytes_following, 8)
+ padding_size = nbf_with_padding - num_bytes_following
+ desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
+ self.image_size, self.hash_algorithm, len(encoded_name),
+ len(self.salt), len(self.digest), self.RESERVED*'\0')
+ padding = struct.pack(str(padding_size) + 'x')
+ ret = desc + encoded_name + self.salt + self.digest + padding
+ return bytearray(ret)
+
+
+class AvbKernelCmdlineDescriptor(AvbDescriptor):
+ """A class for kernel command-line descriptors.
+
+ See the |AvbKernelCmdlineDescriptor| C struct for more information.
+
+ Attributes:
+ flags: Flags.
+ kernel_cmdline: The kernel command-line.
+ """
+
+ TAG = 3
+ SIZE = 24
+ FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
+ 'L' # flags
+ 'L') # cmdline length (bytes)
+
+ FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED = (1 << 0)
+ FLAGS_USE_ONLY_IF_HASHTREE_DISABLED = (1 << 1)
+
+ def __init__(self, data=None):
+ """Initializes a new kernel cmdline descriptor.
+
+ Arguments:
+ data: If not None, must be a bytearray of size |SIZE|.
+
+ Raises:
+ LookupError: If the given descriptor is malformed.
+ """
+ AvbDescriptor.__init__(self, None)
+ assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
+
+ if data:
+ (tag, num_bytes_following, self.flags, kernel_cmdline_length) = (
+ struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
+ expected_size = round_to_multiple(self.SIZE - 16 + kernel_cmdline_length,
+ 8)
+ if tag != self.TAG or num_bytes_following != expected_size:
+ raise LookupError('Given data does not look like a kernel cmdline '
+ 'descriptor.')
+ # Nuke NUL-bytes at the end.
+ self.kernel_cmdline = str(data[self.SIZE:(self.SIZE +
+ kernel_cmdline_length)])
+ # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
+ self.kernel_cmdline.decode('utf-8')
+ else:
+ self.flags = 0
+ self.kernel_cmdline = ''
+
+ def print_desc(self, o):
+ """Print the descriptor.
+
+ Arguments:
+ o: The object to write the output to.
+ """
+ o.write(' Kernel Cmdline descriptor:\n')
+ o.write(' Flags: {}\n'.format(self.flags))
+ o.write(' Kernel Cmdline: {}\n'.format(repr(
+ self.kernel_cmdline)))
+
+ def encode(self):
+ """Serializes the descriptor.
+
+ Returns:
+ A bytearray() with the descriptor data.
+ """
+ encoded_str = self.kernel_cmdline.encode('utf-8')
+ num_bytes_following = (self.SIZE + len(encoded_str) - 16)
+ nbf_with_padding = round_to_multiple(num_bytes_following, 8)
+ padding_size = nbf_with_padding - num_bytes_following
+ desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
+ self.flags, len(encoded_str))
+ padding = struct.pack(str(padding_size) + 'x')
+ ret = desc + encoded_str + padding
+ return bytearray(ret)
+
+
+class AvbChainPartitionDescriptor(AvbDescriptor):
+ """A class for chained partition descriptors.
+
+ See the |AvbChainPartitionDescriptor| C struct for more information.
+
+ Attributes:
+ rollback_index_location: The rollback index location to use.
+ partition_name: Partition name.
+ public_key: Bytes for the public key.
+ """
+
+ TAG = 4
+ RESERVED = 64
+ SIZE = 28 + RESERVED
+ FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
+ 'L' # rollback_index_location
+ 'L' # partition_name_size (bytes)
+ 'L' + # public_key_size (bytes)
+ str(RESERVED) + 's') # reserved
+
+ def __init__(self, data=None):
+ """Initializes a new chain partition descriptor.
+
+ Arguments:
+ data: If not None, must be a bytearray of size |SIZE|.
+
+ Raises:
+ LookupError: If the given descriptor is malformed.
+ """
+ AvbDescriptor.__init__(self, None)
+ assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
+
+ if data:
+ (tag, num_bytes_following, self.rollback_index_location,
+ partition_name_len,
+ public_key_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
+ expected_size = round_to_multiple(
+ self.SIZE - 16 + partition_name_len + public_key_len, 8)
+ if tag != self.TAG or num_bytes_following != expected_size:
+ raise LookupError('Given data does not look like a chain partition '
+ 'descriptor.')
+ o = 0
+ self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
+ partition_name_len)])
+ # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
+ self.partition_name.decode('utf-8')
+ o += partition_name_len
+ self.public_key = data[(self.SIZE + o):(self.SIZE + o + public_key_len)]
+
+ else:
+ self.rollback_index_location = 0
+ self.partition_name = ''
+ self.public_key = bytearray()
+
+ def print_desc(self, o):
+ """Print the descriptor.
+
+ Arguments:
+ o: The object to write the output to.
+ """
+ o.write(' Chain Partition descriptor:\n')
+ o.write(' Partition Name: {}\n'.format(self.partition_name))
+ o.write(' Rollback Index Location: {}\n'.format(
+ self.rollback_index_location))
+ # Just show the SHA1 of the key, for size reasons.
+ hexdig = hashlib.sha1(self.public_key).hexdigest()
+ o.write(' Public key (sha1): {}\n'.format(hexdig))
+
+ def encode(self):
+ """Serializes the descriptor.
+
+ Returns:
+ A bytearray() with the descriptor data.
+ """
+ encoded_name = self.partition_name.encode('utf-8')
+ num_bytes_following = (
+ self.SIZE + len(encoded_name) + len(self.public_key) - 16)
+ nbf_with_padding = round_to_multiple(num_bytes_following, 8)
+ padding_size = nbf_with_padding - num_bytes_following
+ desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
+ self.rollback_index_location, len(encoded_name),
+ len(self.public_key), self.RESERVED*'\0')
+ padding = struct.pack(str(padding_size) + 'x')
+ ret = desc + encoded_name + self.public_key + padding
+ return bytearray(ret)
+
+
+DESCRIPTOR_CLASSES = [
+ AvbPropertyDescriptor, AvbHashtreeDescriptor, AvbHashDescriptor,
+ AvbKernelCmdlineDescriptor, AvbChainPartitionDescriptor
+]
+
+
+def parse_descriptors(data):
+ """Parses a blob of data into descriptors.
+
+ Arguments:
+ data: A bytearray() with encoded descriptors.
+
+ Returns:
+ A list of instances of objects derived from AvbDescriptor. For
+ unknown descriptors, the class AvbDescriptor is used.
+ """
+ o = 0
+ ret = []
+ while o < len(data):
+ tag, nb_following = struct.unpack('!2Q', data[o:o + 16])
+ if tag < len(DESCRIPTOR_CLASSES):
+ c = DESCRIPTOR_CLASSES[tag]
+ else:
+ c = AvbDescriptor
+ ret.append(c(bytearray(data[o:o + 16 + nb_following])))
+ o += 16 + nb_following
+ return ret
+
+
+class AvbFooter(object):
+ """A class for parsing and writing footers.
+
+ Footers are stored at the end of partitions and point to where the
+ AvbVBMeta blob is located. They also contain the original size of
+ the image before AVB information was added.
+
+ Attributes:
+ magic: Magic for identifying the footer, see |MAGIC|.
+ version_major: The major version of avbtool that wrote the footer.
+ version_minor: The minor version of avbtool that wrote the footer.
+ original_image_size: Original image size.
+ vbmeta_offset: Offset of where the AvbVBMeta blob is stored.
+ vbmeta_size: Size of the AvbVBMeta blob.
+ """
+
+ MAGIC = 'AVBf'
+ SIZE = 64
+ RESERVED = 28
+ FORMAT_STRING = ('!4s2L' # magic, 2 x version.
+ 'Q' # Original image size.
+ 'Q' # Offset of VBMeta blob.
+ 'Q' + # Size of VBMeta blob.
+ 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.original_image_size, self.vbmeta_offset,
+ self.vbmeta_size) = struct.unpack(self.FORMAT_STRING, data)
+ if self.magic != self.MAGIC:
+ raise LookupError('Given data does not look like a AVB footer.')
+ else:
+ self.magic = self.MAGIC
+ self.version_major = AVB_VERSION_MAJOR
+ self.version_minor = AVB_VERSION_MINOR
+ self.original_image_size = 0
+ self.vbmeta_offset = 0
+ self.vbmeta_size = 0
+
+ def encode(self):
+ """Gets a string representing the binary encoding of the footer.
+
+ Returns:
+ A bytearray() with a binary representation of the footer.
+ """
+ return struct.pack(self.FORMAT_STRING, self.magic, self.version_major,
+ self.version_minor, self.original_image_size,
+ self.vbmeta_offset, self.vbmeta_size)
+
+
+class AvbVBMetaHeader(object):
+ """A class for parsing and writing AVB vbmeta images.
+
+ Attributes:
+ The attributes correspond to the |AvbVBMetaHeader| struct
+ defined in avb_vbmeta_header.h.
+ """
+
+ SIZE = 256
+
+ # Keep in sync with |reserved| field of |AvbVBMetaImageHeader|.
+ RESERVED = 132
+
+ # Keep in sync with |AvbVBMetaImageHeader|.
+ FORMAT_STRING = ('!4s2L' # magic, 2 x version
+ '2Q' # 2 x block size
+ 'L' # algorithm type
+ '2Q' # offset, size (hash)
+ '2Q' # offset, size (signature)
+ '2Q' # offset, size (public key)
+ '2Q' # offset, size (public key metadata)
+ '2Q' # offset, size (descriptors)
+ 'Q' # rollback_index
+ 'L' + # flags
+ 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.auxiliary_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.public_key_metadata_offset,
+ self.public_key_metadata_size, self.descriptors_offset,
+ self.descriptors_size,
+ self.rollback_index,
+ self.flags) = struct.unpack(self.FORMAT_STRING, data)
+ # Nuke NUL-bytes at the end of the string.
+ if self.magic != 'AVB0':
+ raise AvbError('Given image does not look like a vbmeta image.')
+ else:
+ self.magic = 'AVB0'
+ self.header_version_major = AVB_VERSION_MAJOR
+ self.header_version_minor = AVB_VERSION_MINOR
+ self.authentication_data_block_size = 0
+ self.auxiliary_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.public_key_metadata_offset = 0
+ self.public_key_metadata_size = 0
+ self.descriptors_offset = 0
+ self.descriptors_size = 0
+ self.rollback_index = 0
+ self.flags = 0
+
+ def save(self, output):
+ """Serializes the header (256 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.auxiliary_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.public_key_metadata_offset, self.public_key_metadata_size,
+ self.descriptors_offset, self.descriptors_size, self.rollback_index,
+ self.flags))
+
+ def encode(self):
+ """Serializes the header (256) to a bytearray().
+
+ Returns:
+ A bytearray() with the encoded header.
+ """
+ return struct.pack(self.FORMAT_STRING, self.magic,
+ self.header_version_major, self.header_version_minor,
+ self.authentication_data_block_size,
+ self.auxiliary_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.public_key_metadata_offset,
+ self.public_key_metadata_size, self.descriptors_offset,
+ self.descriptors_size, self.rollback_index, self.flags)
+
+
+class Avb(object):
+ """Business logic for avbtool command-line tool."""
+
+ # Keep in sync with avb_ab_flow.h.
+ AB_FORMAT_NO_CRC = '!4sBB2xBBBxBBBx12x'
+ AB_MAGIC = '\0AB0'
+ AB_MAJOR_VERSION = 1
+ AB_MINOR_VERSION = 0
+ AB_MISC_METADATA_OFFSET = 2048
+
+ # Constants for maximum metadata size. These are used to give
+ # meaningful errors if the value passed in via --partition_size is
+ # too small and when --calc_max_image_size is used. We use
+ # conservative figures.
+ MAX_VBMETA_SIZE = 64 * 1024
+ MAX_FOOTER_SIZE = 4096
+
+ def erase_footer(self, image_filename, keep_hashtree):
+ """Implements the 'erase_footer' command.
+
+ Arguments:
+ image_filename: File to erase a footer from.
+ keep_hashtree: If True, keep the hashtree and FEC around.
+
+ Raises:
+ AvbError: If there's no footer in the image.
+ """
+
+ image = ImageHandler(image_filename)
+
+ (footer, _, descriptors, _) = self._parse_image(image)
+
+ if not footer:
+ raise AvbError('Given image does not have a footer.')
+
+ new_image_size = None
+ if not keep_hashtree:
+ new_image_size = footer.original_image_size
+ else:
+ # If requested to keep the hashtree, search for a hashtree
+ # descriptor to figure out the location and size of the hashtree
+ # and FEC.
+ for desc in descriptors:
+ if isinstance(desc, AvbHashtreeDescriptor):
+ # The hashtree is always just following the main data so the
+ # new size is easily derived.
+ new_image_size = desc.tree_offset + desc.tree_size
+ # If the image has FEC codes, also keep those.
+ if desc.fec_offset > 0:
+ fec_end = desc.fec_offset + desc.fec_size
+ new_image_size = max(new_image_size, fec_end)
+ break
+ if not new_image_size:
+ raise AvbError('Requested to keep hashtree but no hashtree '
+ 'descriptor was found.')
+
+ # And cut...
+ image.truncate(new_image_size)
+
+ def set_ab_metadata(self, misc_image, slot_data):
+ """Implements the 'set_ab_metadata' command.
+
+ The |slot_data| argument must be of the form 'A_priority:A_tries_remaining:
+ A_successful_boot:B_priority:B_tries_remaining:B_successful_boot'.
+
+ Arguments:
+ misc_image: The misc image to write to.
+ slot_data: Slot data as a string
+
+ Raises:
+ AvbError: If slot data is malformed.
+ """
+ tokens = slot_data.split(':')
+ if len(tokens) != 6:
+ raise AvbError('Malformed slot data "{}".'.format(slot_data))
+ a_priority = int(tokens[0])
+ a_tries_remaining = int(tokens[1])
+ a_success = True if int(tokens[2]) != 0 else False
+ b_priority = int(tokens[3])
+ b_tries_remaining = int(tokens[4])
+ b_success = True if int(tokens[5]) != 0 else False
+
+ ab_data_no_crc = struct.pack(self.AB_FORMAT_NO_CRC,
+ self.AB_MAGIC,
+ self.AB_MAJOR_VERSION, self.AB_MINOR_VERSION,
+ a_priority, a_tries_remaining, a_success,
+ b_priority, b_tries_remaining, b_success)
+ # Force CRC to be unsigned, see https://bugs.python.org/issue4903 for why.
+ crc_value = binascii.crc32(ab_data_no_crc) & 0xffffffff
+ ab_data = ab_data_no_crc + struct.pack('!I', crc_value)
+ misc_image.seek(self.AB_MISC_METADATA_OFFSET)
+ misc_image.write(ab_data)
+
+ def info_image(self, image_filename, output):
+ """Implements the 'info_image' command.
+
+ Arguments:
+ image_filename: Image file to get information from (file object).
+ output: Output file to write human-readable information to (file object).
+ """
+
+ image = ImageHandler(image_filename)
+
+ o = output
+
+ (footer, header, descriptors, image_size) = self._parse_image(image)
+
+ if footer:
+ o.write('Footer version: {}.{}\n'.format(footer.version_major,
+ footer.version_minor))
+ o.write('Image size: {} bytes\n'.format(image_size))
+ o.write('Original image size: {} bytes\n'.format(
+ footer.original_image_size))
+ o.write('VBMeta offset: {}\n'.format(footer.vbmeta_offset))
+ o.write('VBMeta size: {} bytes\n'.format(footer.vbmeta_size))
+ o.write('--\n')
+
+ (alg_name, _) = lookup_algorithm_by_type(header.algorithm_type)
+
+ o.write('VBMeta image version: {}.{}{}\n'.format(
+ header.header_version_major, header.header_version_minor,
+ ' (Sparse)' if image.is_sparse else ''))
+ o.write('Header Block: {} bytes\n'.format(AvbVBMetaHeader.SIZE))
+ o.write('Authentication Block: {} bytes\n'.format(
+ header.authentication_data_block_size))
+ o.write('Auxiliary Block: {} bytes\n'.format(
+ header.auxiliary_data_block_size))
+ o.write('Algorithm: {}\n'.format(alg_name))
+ o.write('Rollback Index: {}\n'.format(header.rollback_index))
+ o.write('Flags: {}\n'.format(header.flags))
+
+ # Print descriptors.
+ num_printed = 0
+ o.write('Descriptors:\n')
+ for desc in descriptors:
+ desc.print_desc(o)
+ num_printed += 1
+ if num_printed == 0:
+ o.write(' (none)\n')
+
+ def _parse_image(self, image):
+ """Gets information about an image.
+
+ The image can either be a vbmeta or an image with a footer.
+
+ Arguments:
+ image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
+
+ Returns:
+ A tuple where the first argument is a AvbFooter (None if there
+ is no footer on the image), the second argument is a
+ AvbVBMetaHeader, the third argument is a list of
+ AvbDescriptor-derived instances, and the fourth argument is the
+ size of |image|.
+ """
+ assert isinstance(image, ImageHandler)
+ footer = None
+ image.seek(image.image_size - AvbFooter.SIZE)
+ try:
+ footer = AvbFooter(image.read(AvbFooter.SIZE))
+ except (LookupError, struct.error):
+ # Nope, just seek back to the start.
+ image.seek(0)
+
+ vbmeta_offset = 0
+ if footer:
+ vbmeta_offset = footer.vbmeta_offset
+
+ image.seek(vbmeta_offset)
+ h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
+
+ auth_block_offset = vbmeta_offset + AvbVBMetaHeader.SIZE
+ aux_block_offset = auth_block_offset + h.authentication_data_block_size
+ desc_start_offset = aux_block_offset + h.descriptors_offset
+ image.seek(desc_start_offset)
+ descriptors = parse_descriptors(image.read(h.descriptors_size))
+
+ return footer, h, descriptors, image.image_size
+
+ def _get_cmdline_descriptors_for_dm_verity(self, image):
+ """Generate kernel cmdline descriptors for dm-verity.
+
+ Arguments:
+ image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
+
+ Returns:
+ A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
+ instructions. There is one for when hashtree is not disabled and one for
+ when it is.
+
+ Raises:
+ AvbError: If |image| doesn't have a hashtree descriptor.
+
+ """
+
+ (_, _, descriptors, _) = self._parse_image(image)
+
+ ht = None
+ for desc in descriptors:
+ if isinstance(desc, AvbHashtreeDescriptor):
+ ht = desc
+ break
+
+ if not ht:
+ raise AvbError('No hashtree descriptor in given image')
+
+ c = 'dm="1 vroot none ro 1,'
+ c += '0' # start
+ c += ' {}'.format((ht.image_size / 512)) # size (# sectors)
+ c += ' verity {}'.format(ht.dm_verity_version) # type and version
+ c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # data_dev
+ c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # hash_dev
+ c += ' {}'.format(ht.data_block_size) # data_block
+ c += ' {}'.format(ht.hash_block_size) # hash_block
+ c += ' {}'.format(ht.image_size / ht.data_block_size) # #blocks
+ c += ' {}'.format(ht.image_size / ht.data_block_size) # hash_offset
+ c += ' {}'.format(ht.hash_algorithm) # hash_alg
+ c += ' {}'.format(str(ht.root_digest).encode('hex')) # root_digest
+ c += ' {}'.format(str(ht.salt).encode('hex')) # salt
+ if ht.fec_num_roots > 0:
+ c += ' 10' # number of optional args
+ c += ' restart_on_corruption'
+ c += ' ignore_zero_blocks'
+ c += ' use_fec_from_device PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
+ c += ' fec_roots {}'.format(ht.fec_num_roots)
+ # Note that fec_blocks is the size that FEC covers, *not* the
+ # size of the FEC data. Since we use FEC for everything up until
+ # the FEC data, it's the same as the offset.
+ c += ' fec_blocks {}'.format(ht.fec_offset/ht.data_block_size)
+ c += ' fec_start {}'.format(ht.fec_offset/ht.data_block_size)
+ else:
+ c += ' 2' # number of optional args
+ c += ' restart_on_corruption'
+ c += ' ignore_zero_blocks'
+ c += '" root=0xfd00'
+
+ # Now that we have the command-line, generate the descriptor.
+ desc = AvbKernelCmdlineDescriptor()
+ desc.kernel_cmdline = c
+ desc.flags = (
+ AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
+
+ # The descriptor for when hashtree verification is disabled is a lot
+ # simpler - we just set the root to the partition.
+ desc_no_ht = AvbKernelCmdlineDescriptor()
+ desc_no_ht.kernel_cmdline = 'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
+ desc_no_ht.flags = (
+ AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED)
+
+ return [desc, desc_no_ht]
+
+ def make_vbmeta_image(self, output, chain_partitions, algorithm_name,
+ key_path, public_key_metadata_path, rollback_index,
+ flags, props, props_from_file, kernel_cmdlines,
+ setup_rootfs_from_kernel,
+ include_descriptors_from_image, signing_helper):
+ """Implements the 'make_vbmeta_image' command.
+
+ Arguments:
+ output: File to write the image to.
+ chain_partitions: List of partitions to chain.
+ algorithm_name: Name of algorithm to use.
+ key_path: Path to key to use or None.
+ public_key_metadata_path: Path to public key metadata or None.
+ rollback_index: The rollback index to use.
+ flags: Flags value to use in the image.
+ props: Properties to insert (list of strings of the form 'key:value').
+ props_from_file: Properties to insert (list of strings 'key:<path>').
+ kernel_cmdlines: Kernel cmdlines to insert (list of strings).
+ setup_rootfs_from_kernel: None or file to generate from.
+ include_descriptors_from_image: List of file objects with descriptors.
+ signing_helper: Program which signs a hash and return signature.
+
+ Raises:
+ AvbError: If a chained partition is malformed.
+ """
+
+ descriptors = []
+
+ # Insert chained partition descriptors.
+ if chain_partitions:
+ for cp in chain_partitions:
+ cp_tokens = cp.split(':')
+ if len(cp_tokens) != 3:
+ raise AvbError('Malformed chained partition "{}".'.format(cp))
+ desc = AvbChainPartitionDescriptor()
+ desc.partition_name = cp_tokens[0]
+ desc.rollback_index_location = int(cp_tokens[1])
+ if desc.rollback_index_location < 1:
+ raise AvbError('Rollback index location must be 1 or larger.')
+ file_path = cp_tokens[2]
+ desc.public_key = open(file_path, 'rb').read()
+ descriptors.append(desc)
+
+ vbmeta_blob = self._generate_vbmeta_blob(
+ algorithm_name, key_path, public_key_metadata_path, descriptors,
+ rollback_index, flags, props, props_from_file, kernel_cmdlines,
+ setup_rootfs_from_kernel,
+ include_descriptors_from_image, signing_helper)
+
+ # Write entire vbmeta blob (header, authentication, auxiliary).
+ output.seek(0)
+ output.write(vbmeta_blob)
+
+ def _generate_vbmeta_blob(self, algorithm_name, key_path,
+ public_key_metadata_path, descriptors,
+ rollback_index, flags, props, props_from_file,
+ kernel_cmdlines,
+ setup_rootfs_from_kernel,
+ include_descriptors_from_image, signing_helper):
+ """Generates a VBMeta blob.
+
+ This blob contains the header (struct AvbVBMetaHeader), the
+ authentication data block (which contains the hash and signature
+ for the header and auxiliary block), and the auxiliary block
+ (which contains descriptors, the public key used, and other data).
+
+ The |key| parameter can |None| only if the |algorithm_name| is
+ 'NONE'.
+
+ Arguments:
+ algorithm_name: The algorithm name as per the ALGORITHMS dict.
+ key_path: The path to the .pem file used to sign the blob.
+ public_key_metadata_path: Path to public key metadata or None.
+ descriptors: A list of descriptors to insert or None.
+ rollback_index: The rollback index to use.
+ flags: Flags to use in the image.
+ props: Properties to insert (List of strings of the form 'key:value').
+ props_from_file: Properties to insert (List of strings 'key:<path>').
+ kernel_cmdlines: Kernel cmdlines to insert (list of strings).
+ setup_rootfs_from_kernel: None or file to generate
+ dm-verity kernel cmdline from.
+ include_descriptors_from_image: List of file objects for which
+ to insert descriptors from.
+ signing_helper: Program which signs a hash and return signature.
+
+ Returns:
+ A bytearray() with the VBMeta blob.
+
+ Raises:
+ Exception: If the |algorithm_name| is not found, if no key has
+ been given and the given algorithm requires one, or the key is
+ of the wrong size.
+
+ """
+ try:
+ alg = ALGORITHMS[algorithm_name]
+ except KeyError:
+ raise AvbError('Unknown algorithm with name {}'.format(algorithm_name))
+
+ # Descriptors.
+ encoded_descriptors = bytearray()
+ if descriptors:
+ for desc in descriptors:
+ encoded_descriptors.extend(desc.encode())
+
+ # Add properties.
+ if props:
+ for prop in props:
+ idx = prop.find(':')
+ if idx == -1:
+ raise AvbError('Malformed property "{}".'.format(prop))
+ desc = AvbPropertyDescriptor()
+ desc.key = prop[0:idx]
+ desc.value = prop[(idx + 1):]
+ encoded_descriptors.extend(desc.encode())
+ if props_from_file:
+ for prop in props_from_file:
+ idx = prop.find(':')
+ if idx == -1:
+ raise AvbError('Malformed property "{}".'.format(prop))
+ desc = AvbPropertyDescriptor()
+ desc.key = prop[0:idx]
+ desc.value = prop[(idx + 1):]
+ file_path = prop[(idx + 1):]
+ desc.value = open(file_path, 'rb').read()
+ encoded_descriptors.extend(desc.encode())
+
+ # Add AvbKernelCmdline descriptor for dm-verity, if requested.
+ if setup_rootfs_from_kernel:
+ image_handler = ImageHandler(
+ setup_rootfs_from_kernel.name)
+ cmdline_desc = self._get_cmdline_descriptors_for_dm_verity(image_handler)
+ encoded_descriptors.extend(cmdline_desc[0].encode())
+ encoded_descriptors.extend(cmdline_desc[1].encode())
+
+ # Add kernel command-lines.
+ if kernel_cmdlines:
+ for i in kernel_cmdlines:
+ desc = AvbKernelCmdlineDescriptor()
+ desc.kernel_cmdline = i
+ encoded_descriptors.extend(desc.encode())
+
+ # Add descriptors from other images.
+ if include_descriptors_from_image:
+ for image in include_descriptors_from_image:
+ image_handler = ImageHandler(image.name)
+ (_, _, image_descriptors, _) = self._parse_image(image_handler)
+ for desc in image_descriptors:
+ encoded_descriptors.extend(desc.encode())
+
+ # Load public key metadata blob, if requested.
+ pkmd_blob = []
+ if public_key_metadata_path:
+ with open(public_key_metadata_path) as f:
+ pkmd_blob = f.read()
+
+ key = None
+ encoded_key = bytearray()
+ if alg.public_key_num_bytes > 0:
+ if not key_path:
+ raise AvbError('Key is required for algorithm {}'.format(
+ algorithm_name))
+ key = Crypto.PublicKey.RSA.importKey(open(key_path).read())
+ encoded_key = encode_rsa_key(key)
+ if len(encoded_key) != alg.public_key_num_bytes:
+ raise AvbError('Key is wrong size for algorithm {}'.format(
+ algorithm_name))
+
+ h = AvbVBMetaHeader()
+
+ # For the Auxiliary data block, descriptors are stored at offset 0,
+ # followed by the public key, followed by the public key metadata blob.
+ h.auxiliary_data_block_size = round_to_multiple(
+ len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64)
+ h.descriptors_offset = 0
+ h.descriptors_size = len(encoded_descriptors)
+ h.public_key_offset = h.descriptors_size
+ h.public_key_size = len(encoded_key)
+ h.public_key_metadata_offset = h.public_key_offset + h.public_key_size
+ h.public_key_metadata_size = len(pkmd_blob)
+
+ # For the Authentication data block, the hash is first and then
+ # the signature.
+ h.authentication_data_block_size = round_to_multiple(
+ alg.hash_num_bytes + alg.signature_num_bytes, 64)
+ h.algorithm_type = alg.algorithm_type
+ 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
+
+ h.rollback_index = rollback_index
+ h.flags = flags
+
+ # Generate Header data block.
+ header_data_blob = h.encode()
+
+ # Generate Auxiliary data block.
+ aux_data_blob = bytearray()
+ aux_data_blob.extend(encoded_descriptors)
+ aux_data_blob.extend(encoded_key)
+ aux_data_blob.extend(pkmd_blob)
+ padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob)
+ aux_data_blob.extend('\0' * padding_bytes)
+
+ # Calculate the hash.
+ binary_hash = bytearray()
+ binary_signature = bytearray()
+ if algorithm_name != 'NONE':
+ if algorithm_name[0:6] == 'SHA256':
+ ha = hashlib.sha256()
+ elif algorithm_name[0:6] == 'SHA512':
+ ha = hashlib.sha512()
+ else:
+ raise AvbError('Unsupported algorithm {}.'.format(algorithm_name))
+ ha.update(header_data_blob)
+ ha.update(aux_data_blob)
+ binary_hash.extend(ha.digest())
+
+ # Calculate the signature.
+ padding_and_hash = str(bytearray(alg.padding)) + binary_hash
+ binary_signature.extend(raw_sign(signing_helper, algorithm_name, key_path,
+ padding_and_hash))
+
+ # Generate Authentication data block.
+ auth_data_blob = bytearray()
+ auth_data_blob.extend(binary_hash)
+ auth_data_blob.extend(binary_signature)
+ padding_bytes = h.authentication_data_block_size - len(auth_data_blob)
+ auth_data_blob.extend('\0' * padding_bytes)
+
+ return header_data_blob + auth_data_blob + aux_data_blob
+
+ def extract_public_key(self, key_path, output):
+ """Implements the 'extract_public_key' command.
+
+ Arguments:
+ key_path: The path to a RSA private key file.
+ output: The file to write to.
+ """
+ key = Crypto.PublicKey.RSA.importKey(open(key_path).read())
+ write_rsa_key(output, key)
+
+ def add_hash_footer(self, image_filename, partition_size, partition_name,
+ hash_algorithm, salt, algorithm_name, key_path,
+ public_key_metadata_path, rollback_index, props,
+ props_from_file, kernel_cmdlines,
+ setup_rootfs_from_kernel,
+ include_descriptors_from_image, signing_helper):
+ """Implementation of the add_hash_footer on unsparse images.
+
+ Arguments:
+ image_filename: File to add the footer to.
+ partition_size: Size of partition.
+ partition_name: Name of partition (without A/B suffix).
+ hash_algorithm: Hash algorithm to use.
+ salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
+ algorithm_name: Name of algorithm to use.
+ key_path: Path to key to use or None.
+ public_key_metadata_path: Path to public key metadata or None.
+ rollback_index: Rollback index.
+ props: Properties to insert (List of strings of the form 'key:value').
+ props_from_file: Properties to insert (List of strings 'key:<path>').
+ kernel_cmdlines: Kernel cmdlines to insert (list of strings).
+ setup_rootfs_from_kernel: None or file to generate
+ dm-verity kernel cmdline from.
+ include_descriptors_from_image: List of file objects for which
+ to insert descriptors from.
+ signing_helper: Program which signs a hash and return signature.
+
+ Raises:
+ AvbError: If an argument is incorrect.
+ """
+ image = ImageHandler(image_filename)
+
+ if partition_size % image.block_size != 0:
+ raise AvbError('Partition size of {} is not a multiple of the image '
+ 'block size {}.'.format(partition_size,
+ image.block_size))
+
+ # If there's already a footer, truncate the image to its original
+ # size. This way 'avbtool add_hash_footer' is idempotent (modulo
+ # salts).
+ image.seek(image.image_size - AvbFooter.SIZE)
+ try:
+ footer = AvbFooter(image.read(AvbFooter.SIZE))
+ # Existing footer found. Just truncate.
+ original_image_size = footer.original_image_size
+ image.truncate(footer.original_image_size)
+ except (LookupError, struct.error):
+ original_image_size = image.image_size
+
+ # If anything goes wrong from here-on, restore the image back to
+ # its original size.
+ try:
+ # First, calculate the maximum image size such that an image
+ # this size + metadata (footer + vbmeta struct) fits in
+ # |partition_size|.
+ max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE
+ max_image_size = partition_size - max_metadata_size
+
+ # If image size exceeds the maximum image size, fail.
+ if image.image_size > max_image_size:
+ raise AvbError('Image size of {} exceeds maximum image '
+ 'size of {} in order to fit in a partition '
+ 'size of {}.'.format(image.image_size, max_image_size,
+ partition_size))
+
+ digest_size = len(hashlib.new(name=hash_algorithm).digest())
+ if salt:
+ salt = salt.decode('hex')
+ else:
+ if 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 = ''
+
+ hasher = hashlib.new(name=hash_algorithm, string=salt)
+ # TODO(zeuthen): might want to read this in chunks to avoid
+ # memory pressure, then again, this is only supposed to be used
+ # on kernel/initramfs partitions. Possible optimization.
+ image.seek(0)
+ hasher.update(image.read(image.image_size))
+ digest = hasher.digest()
+
+ h_desc = AvbHashDescriptor()
+ h_desc.image_size = image.image_size
+ h_desc.hash_algorithm = hash_algorithm
+ h_desc.partition_name = partition_name
+ h_desc.salt = salt
+ h_desc.digest = digest
+
+ # Flags are only allowed on top-level vbmeta struct.
+ flags = 0
+
+ # Generate the VBMeta footer.
+ vbmeta_blob = self._generate_vbmeta_blob(
+ algorithm_name, key_path, public_key_metadata_path, [h_desc],
+ rollback_index, flags, props, props_from_file, kernel_cmdlines,
+ setup_rootfs_from_kernel,
+ include_descriptors_from_image, signing_helper)
+
+ # If the image isn't sparse, its size might not be a multiple of
+ # the block size. This will screw up padding later so just grow it.
+ if image.image_size % image.block_size != 0:
+ assert not image.is_sparse
+ padding_needed = image.block_size - (image.image_size%image.block_size)
+ image.truncate(image.image_size + padding_needed)
+
+ # The append_raw() method requires content with size being a
+ # multiple of |block_size| so add padding as needed. Also record
+ # where this is written to since we'll need to put that in the
+ # footer.
+ vbmeta_offset = image.image_size
+ padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
+ len(vbmeta_blob))
+ vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
+ image.append_raw(vbmeta_blob_with_padding)
+ vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
+
+ # Now insert a DONT_CARE chunk with enough bytes such that the
+ # final Footer block is at the end of partition_size..
+ image.append_dont_care(partition_size - vbmeta_end_offset -
+ 1*image.block_size)
+
+ # Generate the Footer that tells where the VBMeta footer
+ # is. Also put enough padding in the front of the footer since
+ # we'll write out an entire block.
+ footer = AvbFooter()
+ footer.original_image_size = original_image_size
+ footer.vbmeta_offset = vbmeta_offset
+ footer.vbmeta_size = len(vbmeta_blob)
+ footer_blob = footer.encode()
+ footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
+ footer_blob)
+ image.append_raw(footer_blob_with_padding)
+
+ except:
+ # Truncate back to original size, then re-raise
+ image.truncate(original_image_size)
+ raise
+
+ def add_hashtree_footer(self, image_filename, partition_size, partition_name,
+ generate_fec, fec_num_roots, hash_algorithm,
+ block_size, salt, algorithm_name, key_path,
+ public_key_metadata_path, rollback_index,
+ props, props_from_file, kernel_cmdlines,
+ setup_rootfs_from_kernel,
+ include_descriptors_from_image,
+ calc_max_image_size, signing_helper):
+ """Implements the 'add_hashtree_footer' command.
+
+ See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
+ more information about dm-verity and these hashes.
+
+ Arguments:
+ image_filename: File to add the footer to.
+ partition_size: Size of partition.
+ partition_name: Name of partition (without A/B suffix).
+ generate_fec: If True, generate FEC codes.
+ fec_num_roots: Number of roots for FEC.
+ hash_algorithm: Hash algorithm to use.
+ block_size: Block size to use.
+ salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
+ algorithm_name: Name of algorithm to use.
+ key_path: Path to key to use or None.
+ public_key_metadata_path: Path to public key metadata or None.
+ rollback_index: Rollback index.
+ props: Properties to insert (List of strings of the form 'key:value').
+ props_from_file: Properties to insert (List of strings 'key:<path>').
+ kernel_cmdlines: Kernel cmdlines to insert (list of strings).
+ setup_rootfs_from_kernel: None or file to generate
+ dm-verity kernel cmdline from.
+ include_descriptors_from_image: List of file objects for which
+ to insert descriptors from.
+ calc_max_image_size: Don't store the hashtree or footer - instead
+ calculate the maximum image size leaving enough room for hashtree
+ and metadata with the given |partition_size|.
+ signing_helper: Program which signs a hash and return signature.
+
+ Raises:
+ AvbError: If an argument is incorrect.
+ """
+ digest_size = len(hashlib.new(name=hash_algorithm).digest())
+ digest_padding = round_to_pow2(digest_size) - digest_size
+
+ # First, calculate the maximum image size such that an image
+ # this size + the hashtree + metadata (footer + vbmeta struct)
+ # fits in |partition_size|. We use very conservative figures for
+ # metadata.
+ (_, max_tree_size) = calc_hash_level_offsets(
+ partition_size, block_size, digest_size + digest_padding)
+ max_fec_size = 0
+ if generate_fec:
+ max_fec_size = calc_fec_data_size(partition_size, fec_num_roots)
+ max_metadata_size = (max_fec_size + max_tree_size +
+ self.MAX_VBMETA_SIZE +
+ self.MAX_FOOTER_SIZE)
+ max_image_size = partition_size - max_metadata_size
+
+ # If we're asked to only calculate the maximum image size, we're done.
+ if calc_max_image_size:
+ print '{}'.format(max_image_size)
+ return
+
+ image = ImageHandler(image_filename)
+
+ if partition_size % image.block_size != 0:
+ raise AvbError('Partition size of {} is not a multiple of the image '
+ 'block size {}.'.format(partition_size,
+ image.block_size))
+
+ # If there's already a footer, truncate the image to its original
+ # size. This way 'avbtool add_hashtree_footer' is idempotent
+ # (modulo salts).
+ image.seek(image.image_size - AvbFooter.SIZE)
+ try:
+ footer = AvbFooter(image.read(AvbFooter.SIZE))
+ # Existing footer found. Just truncate.
+ original_image_size = footer.original_image_size
+ image.truncate(footer.original_image_size)
+ except (LookupError, struct.error):
+ original_image_size = image.image_size
+
+ # If anything goes wrong from here-on, restore the image back to
+ # its original size.
+ try:
+ # Ensure image is multiple of block_size.
+ rounded_image_size = round_to_multiple(image.image_size, block_size)
+ if rounded_image_size > image.image_size:
+ image.append_raw('\0' * (rounded_image_size - image.image_size))
+
+ # If image size exceeds the maximum image size, fail.
+ if image.image_size > max_image_size:
+ raise AvbError('Image size of {} exceeds maximum image '
+ 'size of {} in order to fit in a partition '
+ 'size of {}.'.format(image.image_size, max_image_size,
+ partition_size))
+
+ if salt:
+ salt = salt.decode('hex')
+ else:
+ if 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 calculate hash
+ # offsets in advance.
+ (hash_level_offsets, tree_size) = calc_hash_level_offsets(
+ image.image_size, block_size, digest_size + digest_padding)
+
+ # If the image isn't sparse, its size might not be a multiple of
+ # the block size. This will screw up padding later so just grow it.
+ if image.image_size % image.block_size != 0:
+ assert not image.is_sparse
+ padding_needed = image.block_size - (image.image_size%image.block_size)
+ image.truncate(image.image_size + padding_needed)
+
+ # Generate the tree and add padding as needed.
+ tree_offset = image.image_size
+ root_digest, hash_tree = generate_hash_tree(image, image.image_size,
+ block_size,
+ hash_algorithm, salt,
+ digest_padding,
+ hash_level_offsets,
+ tree_size)
+
+ # Generate HashtreeDescriptor with details about the tree we
+ # just generated.
+ ht_desc = AvbHashtreeDescriptor()
+ ht_desc.dm_verity_version = 1
+ ht_desc.image_size = image.image_size
+ ht_desc.tree_offset = tree_offset
+ ht_desc.tree_size = tree_size
+ ht_desc.data_block_size = block_size
+ ht_desc.hash_block_size = block_size
+ ht_desc.hash_algorithm = hash_algorithm
+ ht_desc.partition_name = partition_name
+ ht_desc.salt = salt
+ ht_desc.root_digest = root_digest
+
+ # Write the hash tree
+ padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
+ len(hash_tree))
+ hash_tree_with_padding = hash_tree + '\0'*padding_needed
+ image.append_raw(hash_tree_with_padding)
+ len_hashtree_and_fec = len(hash_tree_with_padding)
+
+ # Generate FEC codes, if requested.
+ if generate_fec:
+ fec_data = generate_fec_data(image_filename, fec_num_roots)
+ padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
+ len(fec_data))
+ fec_data_with_padding = fec_data + '\0'*padding_needed
+ fec_offset = image.image_size
+ image.append_raw(fec_data_with_padding)
+ len_hashtree_and_fec += len(fec_data_with_padding)
+ # Update the hashtree descriptor.
+ ht_desc.fec_num_roots = fec_num_roots
+ ht_desc.fec_offset = fec_offset
+ ht_desc.fec_size = len(fec_data)
+
+ # Flags are only allowed on top-level vbmeta struct.
+ flags = 0
+
+ # Generate the VBMeta footer and add padding as needed.
+ vbmeta_offset = tree_offset + len_hashtree_and_fec
+ vbmeta_blob = self._generate_vbmeta_blob(
+ algorithm_name, key_path, public_key_metadata_path, [ht_desc],
+ rollback_index, flags, props, props_from_file, kernel_cmdlines,
+ setup_rootfs_from_kernel,
+ include_descriptors_from_image, signing_helper)
+ padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
+ len(vbmeta_blob))
+ vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
+ image.append_raw(vbmeta_blob_with_padding)
+
+ # Now insert a DONT_CARE chunk with enough bytes such that the
+ # final Footer block is at the end of partition_size..
+ image.append_dont_care(partition_size - image.image_size -
+ 1*image.block_size)
+
+ # Generate the Footer that tells where the VBMeta footer
+ # is. Also put enough padding in the front of the footer since
+ # we'll write out an entire block.
+ footer = AvbFooter()
+ footer.original_image_size = original_image_size
+ footer.vbmeta_offset = vbmeta_offset
+ footer.vbmeta_size = len(vbmeta_blob)
+ footer_blob = footer.encode()
+ footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
+ footer_blob)
+ image.append_raw(footer_blob_with_padding)
+
+ except:
+ # Truncate back to original size, then re-raise.
+ image.truncate(original_image_size)
+ raise
+
+ def make_atx_certificate(self, output, authority_key_path, subject_key,
+ subject_key_version, subject,
+ is_intermediate_authority, signing_helper):
+ """Implements the 'make_atx_certificate' command.
+
+ Android Things certificates are required for Android Things public key
+ metadata. They chain the vbmeta signing key for a particular product back to
+ a fused, permanent root key. These certificates are fixed-length and fixed-
+ format with the explicit goal of not parsing ASN.1 in bootloader code.
+
+ Arguments:
+ output: Certificate will be written to this file on success.
+ authority_key_path: A PEM file path with the authority private key.
+ If None, then a certificate will be created without a
+ signature. The signature can be created out-of-band
+ and appended.
+ subject_key: A PEM or DER subject public key.
+ subject_key_version: A 64-bit version value. If this is None, the number
+ of seconds since the epoch is used.
+ subject: A subject identifier. For Product Signing Key certificates this
+ should be the same Product ID found in the permanent attributes.
+ is_intermediate_authority: True if the certificate is for an intermediate
+ authority.
+ signing_helper: Program which signs a hash and returns the signature.
+ """
+ signed_data = bytearray()
+ signed_data.extend(struct.pack('<I', 1)) # Format Version
+ signed_data.extend(
+ encode_rsa_key(Crypto.PublicKey.RSA.importKey(subject_key)))
+ hasher = hashlib.sha256()
+ hasher.update(subject)
+ signed_data.extend(hasher.digest())
+ usage = 'com.google.android.things.vboot'
+ if is_intermediate_authority:
+ usage += '.ca'
+ hasher = hashlib.sha256()
+ hasher.update(usage)
+ signed_data.extend(hasher.digest())
+ if not subject_key_version:
+ subject_key_version = int(time.time())
+ signed_data.extend(struct.pack('<Q', subject_key_version))
+ signature = bytearray()
+ if authority_key_path:
+ padding_and_hash = bytearray()
+ algorithm_name = None
+ hasher = None
+ if is_intermediate_authority:
+ hasher = hashlib.sha512()
+ algorithm_name = 'SHA512_RSA4096'
+ else:
+ hasher = hashlib.sha256()
+ algorithm_name = 'SHA256_RSA2048'
+ padding_and_hash.extend(ALGORITHMS[algorithm_name].padding)
+ hasher.update(signed_data)
+ padding_and_hash.extend(hasher.digest())
+ signature.extend(raw_sign(signing_helper, algorithm_name,
+ authority_key_path, padding_and_hash))
+ output.write(signed_data)
+ output.write(signature)
+
+ def make_atx_permanent_attributes(self, output, root_authority_key,
+ product_id):
+ """Implements the 'make_atx_permanent_attributes' command.
+
+ Android Things permanent attributes are designed to be permanent for a
+ particular product and a hash of these attributes should be fused into
+ hardware to enforce this.
+
+ Arguments:
+ output: Attributes will be written to this file on success.
+ root_authority_key: A PEM or DER public key for the root authority.
+ product_id: A 16-byte Product ID.
+
+ Raises:
+ AvbError: If an argument is incorrect.
+ """
+ if len(product_id) != 16:
+ raise AvbError('Invalid Product ID length.')
+ output.write(struct.pack('<I', 1)) # Format Version
+ write_rsa_key(output, Crypto.PublicKey.RSA.importKey(root_authority_key))
+ output.write(product_id)
+
+ def make_atx_metadata(self, output, intermediate_key_certificate,
+ product_key_certificate, google_key_version):
+ """Implements the 'make_atx_metadata' command.
+
+ Android Things metadata are included in vbmeta images to facilitate
+ verification. The output of this command can be used as the
+ public_key_metadata argument to other commands.
+
+ Arguments:
+ output: Metadata will be written to this file on success.
+ intermediate_key_certificate: A certificate file as output by
+ make_atx_certificate with
+ is_intermediate_authority set to true.
+ product_key_certificate: A certificate file as output by
+ make_atx_certificate with
+ is_intermediate_authority set to false.
+ google_key_version: The version of the Google Signing Key used in the
+ associated vbmeta image.
+
+ Raises:
+ AvbError: If an argument is incorrect.
+ """
+ if len(intermediate_key_certificate) != 1108:
+ raise AvbError('Invalid intermediate key certificate length.')
+ if len(product_key_certificate) != 852:
+ raise AvbError('Invalid product key certificate length.')
+ output.write(struct.pack('<I', 1)) # Format Version
+ output.write(intermediate_key_certificate)
+ output.write(product_key_certificate)
+ output.write(struct.pack('<Q', google_key_version))
+
+
+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
+
+
+# See system/extras/libfec/include/fec/io.h for these definitions.
+FEC_FOOTER_FORMAT = '<LLLLLQ32s'
+FEC_MAGIC = 0xfecfecfe
+
+
+def calc_fec_data_size(image_size, num_roots):
+ """Calculates how much space FEC data will take.
+
+ Args:
+ image_size: The size of the image.
+ num_roots: Number of roots.
+
+ Returns:
+ The number of bytes needed for FEC for an image of the given size
+ and with the requested number of FEC roots.
+
+ Raises:
+ ValueError: If output from the 'fec' tool is invalid.
+
+ """
+ p = subprocess.Popen(
+ ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ (pout, perr) = p.communicate()
+ retcode = p.wait()
+ if retcode != 0:
+ raise ValueError('Error invoking fec: {}'.format(perr))
+ return int(pout)
+
+
+def generate_fec_data(image_filename, num_roots):
+ """Generate FEC codes for an image.
+
+ Args:
+ image_filename: The filename of the image.
+ num_roots: Number of roots.
+
+ Returns:
+ The FEC data blob.
+
+ Raises:
+ ValueError: If output from the 'fec' tool is invalid.
+ """
+ fec_tmpfile = tempfile.NamedTemporaryFile()
+ subprocess.check_call(
+ ['fec', '--encode', '--roots', str(num_roots), image_filename,
+ fec_tmpfile.name],
+ stderr=open(os.devnull))
+ fec_data = fec_tmpfile.read()
+ footer_size = struct.calcsize(FEC_FOOTER_FORMAT)
+ footer_data = fec_data[-footer_size:]
+ (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT,
+ footer_data)
+ if magic != FEC_MAGIC:
+ raise ValueError('Unexpected magic in FEC footer')
+ return fec_data[0:fec_size]
+
+
+def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
+ digest_padding, hash_level_offsets, tree_size):
+ """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.
+ hash_level_offsets: The offsets from calc_hash_level_offsets().
+ tree_size: The size of the tree, in number of bytes.
+
+ Returns:
+ A tuple where the first element is the top-level hash and the
+ second element is the hash-tree.
+ """
+ hash_ret = bytearray(tree_size)
+ hash_src_offset = 0
+ hash_src_size = image_size
+ level_num = 0
+ while hash_src_size > block_size:
+ level_output = ''
+ remaining = hash_src_size
+ while remaining > 0:
+ hasher = hashlib.new(name=hash_alg_name, string=salt)
+ # Only read from the file for the first level - for subsequent
+ # levels, access the array we're building.
+ if level_num == 0:
+ image.seek(hash_src_offset + hash_src_size - remaining)
+ data = image.read(min(remaining, block_size))
+ else:
+ offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining
+ data = hash_ret[offset:offset + block_size]
+ hasher.update(data)
+
+ remaining -= len(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
+
+ # Copy level-output into resulting tree.
+ offset = hash_level_offsets[level_num]
+ hash_ret[offset:offset + len(level_output)] = level_output
+
+ # Continue on to the next level.
+ 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(), hash_ret
+
+
+class AvbTool(object):
+ """Object for avbtool command-line tool."""
+
+ def __init__(self):
+ """Initializer method."""
+ self.avb = Avb()
+
+ def _add_common_args(self, sub_parser):
+ """Adds arguments used by several sub-commands.
+
+ Arguments:
+ sub_parser: The parser to add arguments to.
+ """
+ sub_parser.add_argument('--algorithm',
+ help='Algorithm to use (default: NONE)',
+ metavar='ALGORITHM',
+ default='NONE')
+ sub_parser.add_argument('--key',
+ help='Path to RSA private key file',
+ metavar='KEY',
+ required=False)
+ sub_parser.add_argument('--signing_helper',
+ help='Path to helper used for signing',
+ metavar='APP',
+ default=None,
+ required=False)
+ sub_parser.add_argument('--public_key_metadata',
+ help='Path to public key metadata file',
+ metavar='KEY_METADATA',
+ required=False)
+ sub_parser.add_argument('--rollback_index',
+ help='Rollback Index',
+ type=parse_number,
+ default=0)
+ sub_parser.add_argument('--prop',
+ help='Add property',
+ metavar='KEY:VALUE',
+ action='append')
+ sub_parser.add_argument('--prop_from_file',
+ help='Add property from file',
+ metavar='KEY:PATH',
+ action='append')
+ sub_parser.add_argument('--kernel_cmdline',
+ help='Add kernel cmdline',
+ metavar='CMDLINE',
+ action='append')
+ # TODO(zeuthen): the --setup_rootfs_from_kernel option used to be called
+ # --generate_dm_verity_cmdline_from_hashtree. Remove support for the latter
+ # at some future point.
+ sub_parser.add_argument('--setup_rootfs_from_kernel',
+ '--generate_dm_verity_cmdline_from_hashtree',
+ metavar='IMAGE',
+ help='Adds kernel cmdline to set up IMAGE',
+ type=argparse.FileType('rb'))
+ sub_parser.add_argument('--include_descriptors_from_image',
+ help='Include descriptors from image',
+ metavar='IMAGE',
+ action='append',
+ type=argparse.FileType('rb'))
+
+ def run(self, argv):
+ """Command-line processor.
+
+ Arguments:
+ argv: Pass sys.argv from main.
+ """
+ parser = argparse.ArgumentParser()
+ subparsers = parser.add_subparsers(title='subcommands')
+
+ sub_parser = subparsers.add_parser('version',
+ help='Prints version of avbtool.')
+ sub_parser.set_defaults(func=self.version)
+
+ sub_parser = subparsers.add_parser('extract_public_key',
+ help='Extract public key.')
+ sub_parser.add_argument('--key',
+ help='Path to RSA private key file',
+ required=True)
+ sub_parser.add_argument('--output',
+ help='Output file name',
+ type=argparse.FileType('wb'),
+ required=True)
+ sub_parser.set_defaults(func=self.extract_public_key)
+
+ sub_parser = subparsers.add_parser('make_vbmeta_image',
+ help='Makes a vbmeta image.')
+ sub_parser.add_argument('--output',
+ help='Output file name',
+ type=argparse.FileType('wb'),
+ required=True)
+ self._add_common_args(sub_parser)
+ sub_parser.add_argument('--chain_partition',
+ help='Allow signed integrity-data for partition',
+ metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
+ action='append')
+ sub_parser.add_argument('--flags',
+ help='VBMeta flags',
+ type=parse_number,
+ default=0)
+ sub_parser.add_argument('--set_hashtree_disabled_flag',
+ help='Set the HASHTREE_DISABLED flag',
+ action='store_true')
+ sub_parser.set_defaults(func=self.make_vbmeta_image)
+
+ sub_parser = subparsers.add_parser('add_hash_footer',
+ help='Add hashes and footer to image.')
+ sub_parser.add_argument('--image',
+ help='Image to add hashes to',
+ type=argparse.FileType('rab+'))
+ sub_parser.add_argument('--partition_size',
+ help='Partition size',
+ type=parse_number,
+ required=True)
+ sub_parser.add_argument('--partition_name',
+ help='Partition name',
+ required=True)
+ sub_parser.add_argument('--hash_algorithm',
+ help='Hash algorithm to use (default: sha256)',
+ default='sha256')
+ sub_parser.add_argument('--salt',
+ help='Salt in hex (default: /dev/urandom)')
+ self._add_common_args(sub_parser)
+ sub_parser.set_defaults(func=self.add_hash_footer)
+
+ sub_parser = subparsers.add_parser('add_hashtree_footer',
+ help='Add hashtree and footer to image.')
+ sub_parser.add_argument('--image',
+ help='Image to add hashtree to',
+ type=argparse.FileType('rab+'))
+ sub_parser.add_argument('--partition_size',
+ help='Partition size',
+ type=parse_number,
+ required=True)
+ sub_parser.add_argument('--partition_name',
+ help='Partition name',
+ default=None)
+ sub_parser.add_argument('--hash_algorithm',
+ help='Hash algorithm to use (default: sha1)',
+ default='sha1')
+ sub_parser.add_argument('--salt',
+ help='Salt in hex (default: /dev/urandom)')
+ sub_parser.add_argument('--block_size',
+ help='Block size (default: 4096)',
+ type=parse_number,
+ default=4096)
+ sub_parser.add_argument('--generate_fec',
+ help='Add forward-error-correction codes',
+ action='store_true')
+ sub_parser.add_argument('--fec_num_roots',
+ help='Number of roots for FEC (default: 2)',
+ type=parse_number,
+ default=2)
+ sub_parser.add_argument('--calc_max_image_size',
+ help=('Don\'t store the hashtree or footer - '
+ 'instead calculate the maximum image size '
+ 'leaving enough room for hashtree '
+ 'and metadata with the given partition '
+ 'size.'),
+ action='store_true')
+ self._add_common_args(sub_parser)
+ sub_parser.set_defaults(func=self.add_hashtree_footer)
+
+ sub_parser = subparsers.add_parser('erase_footer',
+ help='Erase footer from an image.')
+ sub_parser.add_argument('--image',
+ help='Image with a footer',
+ type=argparse.FileType('rwb+'),
+ required=True)
+ sub_parser.add_argument('--keep_hashtree',
+ help='Keep the hashtree and FEC in the image',
+ action='store_true')
+ sub_parser.set_defaults(func=self.erase_footer)
+
+ sub_parser = subparsers.add_parser(
+ 'info_image',
+ help='Show information about vbmeta or footer.')
+ sub_parser.add_argument('--image',
+ help='Image to show information about',
+ type=argparse.FileType('rb'),
+ required=True)
+ sub_parser.add_argument('--output',
+ help='Write info to file',
+ type=argparse.FileType('wt'),
+ default=sys.stdout)
+ sub_parser.set_defaults(func=self.info_image)
+
+ sub_parser = subparsers.add_parser('set_ab_metadata',
+ help='Set A/B metadata.')
+ sub_parser.add_argument('--misc_image',
+ help=('The misc image to modify. If the image does '
+ 'not exist, it will be created.'),
+ type=argparse.FileType('r+b'),
+ required=True)
+ sub_parser.add_argument('--slot_data',
+ help=('Slot data of the form "priority", '
+ '"tries_remaining", "sucessful_boot" for '
+ 'slot A followed by the same for slot B, '
+ 'separated by colons. The default value '
+ 'is 15:7:0:14:7:0.'),
+ default='15:7:0:14:7:0')
+ sub_parser.set_defaults(func=self.set_ab_metadata)
+
+ sub_parser = subparsers.add_parser(
+ 'make_atx_certificate',
+ help='Create an Android Things eXtension (ATX) certificate.')
+ sub_parser.add_argument('--output',
+ help='Write certificate to file',
+ type=argparse.FileType('wb'),
+ default=sys.stdout)
+ sub_parser.add_argument('--subject',
+ help=('Path to subject file'),
+ type=argparse.FileType('rb'),
+ required=True)
+ sub_parser.add_argument('--subject_key',
+ help=('Path to subject RSA public key file'),
+ type=argparse.FileType('rb'),
+ required=True)
+ sub_parser.add_argument('--subject_key_version',
+ help=('Version of the subject key'),
+ type=parse_number,
+ required=False)
+ sub_parser.add_argument('--subject_is_intermediate_authority',
+ help=('Generate an intermediate authority '
+ 'certificate'),
+ action='store_true')
+ sub_parser.add_argument('--authority_key',
+ help='Path to authority RSA private key file',
+ required=False)
+ sub_parser.add_argument('--signing_helper',
+ help='Path to helper used for signing',
+ metavar='APP',
+ default=None,
+ required=False)
+ sub_parser.set_defaults(func=self.make_atx_certificate)
+
+ sub_parser = subparsers.add_parser(
+ 'make_atx_permanent_attributes',
+ help='Create Android Things eXtension (ATX) permanent attributes.')
+ sub_parser.add_argument('--output',
+ help='Write attributes to file',
+ type=argparse.FileType('wb'),
+ default=sys.stdout)
+ sub_parser.add_argument('--root_authority_key',
+ help='Path to authority RSA public key file',
+ type=argparse.FileType('rb'),
+ required=True)
+ sub_parser.add_argument('--product_id',
+ help=('Path to Product ID file'),
+ type=argparse.FileType('rb'),
+ required=True)
+ sub_parser.set_defaults(func=self.make_atx_permanent_attributes)
+
+ sub_parser = subparsers.add_parser(
+ 'make_atx_metadata',
+ help='Create Android Things eXtension (ATX) metadata.')
+ sub_parser.add_argument('--output',
+ help='Write metadata to file',
+ type=argparse.FileType('wb'),
+ default=sys.stdout)
+ sub_parser.add_argument('--intermediate_key_certificate',
+ help='Path to intermediate key certificate file',
+ type=argparse.FileType('rb'),
+ required=True)
+ sub_parser.add_argument('--product_key_certificate',
+ help='Path to product key certificate file',
+ type=argparse.FileType('rb'),
+ required=True)
+ sub_parser.add_argument('--google_key_version',
+ help=('Version of the Google signing key'),
+ type=parse_number,
+ default=0)
+ sub_parser.set_defaults(func=self.make_atx_metadata)
+
+ args = parser.parse_args(argv[1:])
+ try:
+ args.func(args)
+ except AvbError as e:
+ sys.stderr.write('{}: {}\n'.format(argv[0], e.message))
+ sys.exit(1)
+
+ def version(self, _):
+ """Implements the 'version' sub-command."""
+ print '{}.{}'.format(AVB_VERSION_MAJOR, AVB_VERSION_MINOR)
+
+ def extract_public_key(self, args):
+ """Implements the 'extract_public_key' sub-command."""
+ self.avb.extract_public_key(args.key, args.output)
+
+ def make_vbmeta_image(self, args):
+ """Implements the 'make_vbmeta_image' sub-command."""
+ if args.set_hashtree_disabled_flag:
+ args.flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED
+ self.avb.make_vbmeta_image(args.output, args.chain_partition,
+ args.algorithm, args.key,
+ args.public_key_metadata, args.rollback_index,
+ args.flags, args.prop, args.prop_from_file,
+ args.kernel_cmdline,
+ args.setup_rootfs_from_kernel,
+ args.include_descriptors_from_image, args.signing_helper)
+
+ def add_hash_footer(self, args):
+ """Implements the 'add_hash_footer' sub-command."""
+ self.avb.add_hash_footer(args.image.name, args.partition_size,
+ args.partition_name, args.hash_algorithm,
+ args.salt, args.algorithm, args.key,
+ args.public_key_metadata, args.rollback_index,
+ args.prop, args.prop_from_file,
+ args.kernel_cmdline,
+ args.setup_rootfs_from_kernel,
+ args.include_descriptors_from_image, args.signing_helper)
+
+ def add_hashtree_footer(self, args):
+ """Implements the 'add_hashtree_footer' sub-command."""
+ self.avb.add_hashtree_footer(args.image.name if args.image else None,
+ args.partition_size,
+ args.partition_name,
+ args.generate_fec, args.fec_num_roots,
+ args.hash_algorithm, args.block_size,
+ args.salt, args.algorithm, args.key,
+ args.public_key_metadata,
+ args.rollback_index, args.prop,
+ args.prop_from_file,
+ args.kernel_cmdline,
+ args.setup_rootfs_from_kernel,
+ args.include_descriptors_from_image,
+ args.calc_max_image_size, args.signing_helper)
+
+ def erase_footer(self, args):
+ """Implements the 'erase_footer' sub-command."""
+ self.avb.erase_footer(args.image.name, args.keep_hashtree)
+
+ def set_ab_metadata(self, args):
+ """Implements the 'set_ab_metadata' sub-command."""
+ self.avb.set_ab_metadata(args.misc_image, args.slot_data)
+
+ def info_image(self, args):
+ """Implements the 'info_image' sub-command."""
+ self.avb.info_image(args.image.name, args.output)
+
+ def make_atx_certificate(self, args):
+ """Implements the 'make_atx_certificate' sub-command."""
+ self.avb.make_atx_certificate(args.output, args.authority_key,
+ args.subject_key.read(),
+ args.subject_key_version,
+ args.subject.read(),
+ args.subject_is_intermediate_authority,
+ args.signing_helper)
+
+ def make_atx_permanent_attributes(self, args):
+ """Implements the 'make_atx_permanent_attributes' sub-command."""
+ self.avb.make_atx_permanent_attributes(args.output,
+ args.root_authority_key.read(),
+ args.product_id.read())
+
+ def make_atx_metadata(self, args):
+ """Implements the 'make_atx_metadata' sub-command."""
+ self.avb.make_atx_metadata(args.output,
+ args.intermediate_key_certificate.read(),
+ args.product_key_certificate.read(),
+ args.google_key_version)
+
+
+if __name__ == '__main__':
+ tool = AvbTool()
+ tool.run(sys.argv)
diff --git a/avb/boot_control/avb_ops_device.c b/avb/boot_control/avb_ops_device.c
new file mode 100644
index 0000000..fbee76c
--- /dev/null
+++ b/avb/boot_control/avb_ops_device.c
@@ -0,0 +1,248 @@
+/*
+ * 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 <errno.h>
+#include <fcntl.h>
+#include <linux/fs.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <cutils/properties.h>
+#include <fs_mgr.h>
+
+#include "avb_ops_device.h"
+
+/* Open the appropriate fstab file and fallback to /fstab.device if
+ * that's what's being used.
+ */
+static struct fstab* open_fstab(void) {
+ char propbuf[PROPERTY_VALUE_MAX];
+ char fstab_name[PROPERTY_VALUE_MAX + 32];
+ struct fstab* fstab;
+
+ property_get("ro.hardware", propbuf, "");
+ snprintf(fstab_name, sizeof(fstab_name), "/fstab.%s", propbuf);
+ fstab = fs_mgr_read_fstab(fstab_name);
+ if (fstab != NULL) {
+ return fstab;
+ }
+
+ fstab = fs_mgr_read_fstab("/fstab.device");
+ return fstab;
+}
+
+static int open_partition(const char* name, int flags) {
+ char* path;
+ int fd;
+ struct fstab* fstab;
+ struct fstab_rec* record;
+
+ /* We can't use fs_mgr to look up |name| because fstab doesn't list
+ * every slot partition (it uses the slotselect option to mask the
+ * suffix) and |slot| is expected to be of that form, e.g. boot_a.
+ *
+ * We can however assume that there's an entry for the /misc mount
+ * point and use that to get the device file for the misc
+ * partition. From there we'll assume that a by-name scheme is used
+ * so we can just replace the trailing "misc" by the given |name|,
+ * e.g.
+ *
+ * /dev/block/platform/soc.0/7824900.sdhci/by-name/misc ->
+ * /dev/block/platform/soc.0/7824900.sdhci/by-name/boot_a
+ *
+ * If needed, it's possible to relax this assumption in the future
+ * by trawling /sys/block looking for the appropriate sibling of
+ * misc and then finding an entry in /dev matching the sysfs entry.
+ */
+
+ fstab = open_fstab();
+ if (fstab == NULL) {
+ return -1;
+ }
+ record = fs_mgr_get_entry_for_mount_point(fstab, "/misc");
+ if (record == NULL) {
+ fs_mgr_free_fstab(fstab);
+ return -1;
+ }
+ if (strcmp(name, "misc") == 0) {
+ path = strdup(record->blk_device);
+ } else {
+ size_t trimmed_len, name_len;
+ const char* end_slash = strrchr(record->blk_device, '/');
+ if (end_slash == NULL) {
+ fs_mgr_free_fstab(fstab);
+ return -1;
+ }
+ trimmed_len = end_slash - record->blk_device + 1;
+ name_len = strlen(name);
+ path = calloc(trimmed_len + name_len + 1, 1);
+ strncpy(path, record->blk_device, trimmed_len);
+ strncpy(path + trimmed_len, name, name_len);
+ }
+ fs_mgr_free_fstab(fstab);
+
+ fd = open(path, flags);
+ free(path);
+
+ return fd;
+}
+
+static AvbIOResult read_from_partition(AvbOps* ops,
+ const char* partition,
+ int64_t offset,
+ size_t num_bytes,
+ void* buffer,
+ size_t* out_num_read) {
+ int fd;
+ off_t where;
+ ssize_t num_read;
+ AvbIOResult ret;
+
+ fd = open_partition(partition, O_RDONLY);
+ if (fd == -1) {
+ avb_errorv("Error opening \"", partition, "\" partition.\n", NULL);
+ ret = AVB_IO_RESULT_ERROR_IO;
+ goto out;
+ }
+
+ where = lseek(fd, offset, SEEK_SET);
+ if (where == -1) {
+ avb_error("Error seeking to offset.\n");
+ ret = AVB_IO_RESULT_ERROR_IO;
+ goto out;
+ }
+ if (where != offset) {
+ avb_error("Error seeking to offset.\n");
+ ret = AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION;
+ goto out;
+ }
+
+ /* On Linux, we never get partial reads from block devices (except
+ * for EOF).
+ */
+ num_read = read(fd, buffer, num_bytes);
+ if (num_read == -1) {
+ avb_error("Error reading data.\n");
+ ret = AVB_IO_RESULT_ERROR_IO;
+ goto out;
+ }
+ if (out_num_read != NULL) {
+ *out_num_read = num_read;
+ }
+
+ ret = AVB_IO_RESULT_OK;
+
+out:
+ if (fd != -1) {
+ if (close(fd) != 0) {
+ avb_error("Error closing file descriptor.\n");
+ }
+ }
+ return ret;
+}
+
+static AvbIOResult write_to_partition(AvbOps* ops,
+ const char* partition,
+ int64_t offset,
+ size_t num_bytes,
+ const void* buffer) {
+ int fd;
+ off_t where;
+ ssize_t num_written;
+ AvbIOResult ret;
+
+ fd = open_partition(partition, O_WRONLY);
+ if (fd == -1) {
+ avb_errorv("Error opening \"", partition, "\" partition.\n", NULL);
+ ret = AVB_IO_RESULT_ERROR_IO;
+ goto out;
+ }
+
+ where = lseek(fd, offset, SEEK_SET);
+ if (where == -1) {
+ avb_error("Error seeking to offset.\n");
+ ret = AVB_IO_RESULT_ERROR_IO;
+ goto out;
+ }
+ if (where != offset) {
+ avb_error("Error seeking to offset.\n");
+ ret = AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION;
+ goto out;
+ }
+
+ /* On Linux, we never get partial writes on block devices. */
+ num_written = write(fd, buffer, num_bytes);
+ if (num_written == -1) {
+ avb_error("Error writing data.\n");
+ ret = AVB_IO_RESULT_ERROR_IO;
+ goto out;
+ }
+
+ ret = AVB_IO_RESULT_OK;
+
+out:
+ if (fd != -1) {
+ if (close(fd) != 0) {
+ avb_error("Error closing file descriptor.\n");
+ }
+ }
+ return ret;
+}
+
+AvbABOps* avb_ops_device_new(void) {
+ AvbABOps* ab_ops;
+
+ ab_ops = calloc(1, sizeof(AvbABOps));
+ if (ab_ops == NULL) {
+ avb_error("Error allocating memory for AvbABOps.\n");
+ goto out;
+ }
+
+ ab_ops->ops = calloc(1, sizeof(AvbOps));
+ if (ab_ops->ops == NULL) {
+ avb_error("Error allocating memory for AvbOps.\n");
+ free(ab_ops);
+ goto out;
+ }
+
+ /* We only need these operations since that's all what is being used
+ * by the A/B routines.
+ */
+ ab_ops->ops->read_from_partition = read_from_partition;
+ ab_ops->ops->write_to_partition = write_to_partition;
+ ab_ops->read_ab_metadata = avb_ab_data_read;
+ ab_ops->write_ab_metadata = avb_ab_data_write;
+
+out:
+ return ab_ops;
+}
+
+void avb_ops_device_free(AvbABOps* ab_ops) {
+ free(ab_ops->ops);
+ free(ab_ops);
+}
diff --git a/avb/boot_control/avb_ops_device.h b/avb/boot_control/avb_ops_device.h
new file mode 100644
index 0000000..ad7bddb
--- /dev/null
+++ b/avb/boot_control/avb_ops_device.h
@@ -0,0 +1,40 @@
+/*
+ * 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 AVB_OPS_DEVICE_H_
+#define AVB_OPS_DEVICE_H_
+
+#include <libavb_ab/libavb_ab.h>
+
+/* Allocates an AvbOps instance suitable for use on the
+ * device. Returns NULL on OOM.
+ *
+ * Free with avb_ops_device_free().
+ */
+AvbABOps* avb_ops_device_new(void);
+
+/* Frees an AvbOps instance previously allocated with avb_ops_device_new(). */
+void avb_ops_device_free(AvbABOps* ab_ops);
+
+#endif /* AVB_OPS_DEVICE_H_ */
diff --git a/avb/boot_control/boot_control_avb.c b/avb/boot_control/boot_control_avb.c
new file mode 100644
index 0000000..8775d09
--- /dev/null
+++ b/avb/boot_control/boot_control_avb.c
@@ -0,0 +1,160 @@
+/*
+ * 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 <errno.h>
+#include <string.h>
+
+#include <cutils/properties.h>
+#include <hardware/boot_control.h>
+#include <hardware/hardware.h>
+
+#include "avb_ops_device.h"
+
+static AvbABOps* ab_ops = NULL;
+
+static void module_init(boot_control_module_t* module) {
+ if (ab_ops != NULL) {
+ return;
+ }
+
+ ab_ops = avb_ops_device_new();
+ if (ab_ops == NULL) {
+ avb_error("Unable to allocate AvbOps instance.\n");
+ }
+}
+
+static unsigned int module_getNumberSlots(boot_control_module_t* module) {
+ return 2;
+}
+
+static unsigned int module_getCurrentSlot(boot_control_module_t* module) {
+ char propbuf[PROPERTY_VALUE_MAX];
+
+ property_get("ro.boot.slot_suffix", propbuf, "");
+ if (strcmp(propbuf, "_a") == 0) {
+ return 0;
+ } else if (strcmp(propbuf, "_b") == 0) {
+ return 1;
+ } else {
+ avb_errorv("Unexpected slot suffix '", propbuf, "'.\n", NULL);
+ return 0;
+ }
+ return 0;
+}
+
+static int module_markBootSuccessful(boot_control_module_t* module) {
+ if (avb_ab_mark_slot_successful(ab_ops, module_getCurrentSlot(module)) ==
+ AVB_IO_RESULT_OK) {
+ return 0;
+ } else {
+ return -EIO;
+ }
+}
+
+static int module_setActiveBootSlot(boot_control_module_t* module,
+ unsigned int slot) {
+ if (avb_ab_mark_slot_active(ab_ops, slot) == AVB_IO_RESULT_OK) {
+ return 0;
+ } else {
+ return -EIO;
+ }
+}
+
+static int module_setSlotAsUnbootable(struct boot_control_module* module,
+ unsigned int slot) {
+ if (avb_ab_mark_slot_unbootable(ab_ops, slot) == AVB_IO_RESULT_OK) {
+ return 0;
+ } else {
+ return -EIO;
+ }
+}
+
+static int module_isSlotBootable(struct boot_control_module* module,
+ unsigned int slot) {
+ AvbABData ab_data;
+ bool is_bootable;
+
+ avb_assert(slot < 2);
+
+ if (avb_ab_data_read(ab_ops, &ab_data) != AVB_IO_RESULT_OK) {
+ return -EIO;
+ }
+
+ is_bootable = (ab_data.slots[slot].priority > 0) &&
+ (ab_data.slots[slot].successful_boot ||
+ (ab_data.slots[slot].tries_remaining > 0));
+
+ return is_bootable ? 1 : 0;
+}
+
+static int module_isSlotMarkedSuccessful(struct boot_control_module* module,
+ unsigned int slot) {
+ AvbABData ab_data;
+ bool is_marked_successful;
+
+ avb_assert(slot < 2);
+
+ if (avb_ab_data_read(ab_ops, &ab_data) != AVB_IO_RESULT_OK) {
+ return -EIO;
+ }
+
+ is_marked_successful = ab_data.slots[slot].successful_boot;
+
+ return is_marked_successful ? 1 : 0;
+}
+
+static const char* module_getSuffix(boot_control_module_t* module,
+ unsigned int slot) {
+ static const char* suffix[2] = {"_a", "_b"};
+ if (slot >= 2) {
+ return NULL;
+ }
+ return suffix[slot];
+}
+
+static struct hw_module_methods_t module_methods = {
+ .open = NULL,
+};
+
+boot_control_module_t HAL_MODULE_INFO_SYM = {
+ .common =
+ {
+ .tag = HARDWARE_MODULE_TAG,
+ .module_api_version = BOOT_CONTROL_MODULE_API_VERSION_0_1,
+ .hal_api_version = HARDWARE_HAL_API_VERSION,
+ .id = BOOT_CONTROL_HARDWARE_MODULE_ID,
+ .name = "AVB implementation of boot_control HAL",
+ .author = "The Android Open Source Project",
+ .methods = &module_methods,
+ },
+ .init = module_init,
+ .getNumberSlots = module_getNumberSlots,
+ .getCurrentSlot = module_getCurrentSlot,
+ .markBootSuccessful = module_markBootSuccessful,
+ .setActiveBootSlot = module_setActiveBootSlot,
+ .setSlotAsUnbootable = module_setSlotAsUnbootable,
+ .isSlotBootable = module_isSlotBootable,
+ .getSuffix = module_getSuffix,
+ .isSlotMarkedSuccessful = module_isSlotMarkedSuccessful,
+};
diff --git a/avb/examples/uefi/Makefile b/avb/examples/uefi/Makefile
new file mode 100644
index 0000000..eae589f
--- /dev/null
+++ b/avb/examples/uefi/Makefile
@@ -0,0 +1,98 @@
+#
+# Copyright (C) 2017 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.
+#
+
+# TODO(zeuthen): This is not invoked by Android build system and is
+# intended to aid users to locally build the EFI application. It would
+# be nice to add gnu-efi/ to external and rewrite this as an
+# Android.mk file.
+
+EFI_CC = gcc
+EFI_LD = ld
+EFI_OBJCOPY = objcopy
+AVB = ../..
+GNUEFI = /usr/include/efi
+
+EFI_SRC_FILES = \
+ main.c \
+ uefi_avb_sysdeps.c \
+ uefi_avb_ops.c \
+ uefi_avb_util.c \
+ uefi_avb_boot.c \
+ $(AVB)/libavb/avb_chain_partition_descriptor.c \
+ $(AVB)/libavb/avb_crc32.c \
+ $(AVB)/libavb/avb_crypto.c \
+ $(AVB)/libavb/avb_descriptor.c \
+ $(AVB)/libavb/avb_footer.c \
+ $(AVB)/libavb/avb_hash_descriptor.c \
+ $(AVB)/libavb/avb_hashtree_descriptor.c \
+ $(AVB)/libavb/avb_kernel_cmdline_descriptor.c \
+ $(AVB)/libavb/avb_property_descriptor.c \
+ $(AVB)/libavb/avb_rsa.c \
+ $(AVB)/libavb/avb_sha256.c \
+ $(AVB)/libavb/avb_sha512.c \
+ $(AVB)/libavb/avb_slot_verify.c \
+ $(AVB)/libavb/avb_util.c \
+ $(AVB)/libavb/avb_vbmeta_image.c \
+ $(AVB)/libavb_ab/avb_ab_flow.c
+
+EFI_OBJ_FILES = $(notdir $(patsubst %.S,%.o,$(patsubst %.c,%.o,$(EFI_SRC_FILES))))
+EFI_TARGET = avb_bootloader.efi
+EFI_SHARED_OBJ = $(patsubst %.efi,%.so,$(EFI_TARGET))
+
+EFI_CFLAGS = \
+ -DAVB_COMPILATION -std=gnu99 \
+ -I$(ANDROID_BUILD_TOP)/system/core/mkbootimg/ \
+ -I$(AVB) \
+ -I$(GNUEFI) \
+ -I$(GNUEFI)/x86_64 \
+ -I$(GNUEFI)/protocol \
+ -fno-stack-protector -fpic \
+ -fshort-wchar -mno-red-zone -Wall \
+ -DEFI_FUNCTION_WRAPPER
+
+EFI_LDFLAGS = \
+ -nostdlib -znocombreloc -T /usr/lib/elf_x86_64_efi.lds -shared \
+ -Bsymbolic -L /usr/lib/ /usr/lib/crt0-efi-x86_64.o \
+ /usr/lib/elf_x86_64_efi.lds
+
+EFI_LDLIBS = -lefi -lgnuefi
+
+EFI_OBJCOPY_SECTIONS = -j .text -j .sdata -j .data -j .dynamic \
+ -j .dynsym -j .rel -j .rela -j .reloc
+
+EFI_OBJCOPY_TARGET = --target=efi-app-x86_64
+
+all: $(EFI_TARGET)
+
+$(EFI_OBJ_FILES): $(EFI_SRC_FILES)
+ $(EFI_CC) $(EFI_CFLAGS) -c $(EFI_SRC_FILES)
+
+$(EFI_SHARED_OBJ): $(EFI_OBJ_FILES)
+ $(EFI_LD) $(EFI_LDFLAGS) $(EFI_OBJ_FILES) -o $@ $(EFI_LDLIBS)
+
+$(EFI_TARGET): $(EFI_SHARED_OBJ)
+ $(EFI_OBJCOPY) $(EFI_OBJCOPY_SECTIONS) $(EFI_OBJCOPY_TARGET) $^ $@
+
+clean:
+ rm -f $(EFI_OBJ_FILES) $(EFI_SHARED_OBJ) $(EFI_TARGET) *~
diff --git a/avb/examples/uefi/main.c b/avb/examples/uefi/main.c
new file mode 100644
index 0000000..8d9c0ee
--- /dev/null
+++ b/avb/examples/uefi/main.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2017 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 <efi.h>
+#include <efilib.h>
+
+#include <libavb_ab/libavb_ab.h>
+
+#include "uefi_avb_boot.h"
+#include "uefi_avb_ops.h"
+
+EFI_STATUS EFIAPI efi_main(EFI_HANDLE ImageHandle,
+ EFI_SYSTEM_TABLE* SystemTable) {
+ AvbOps* ops;
+ AvbABFlowResult ab_result;
+ AvbSlotVerifyData* slot_data;
+ UEFIAvbBootKernelResult boot_result;
+ const char* requested_partitions[] = {"boot", NULL};
+ bool unlocked = true;
+
+ InitializeLib(ImageHandle, SystemTable);
+
+ avb_print("UEFI AVB-based bootloader\n");
+
+ ops = uefi_avb_ops_new(ImageHandle);
+ if (ops == NULL) {
+ avb_fatal("Error allocating AvbOps.\n");
+ }
+
+ if (ops->read_is_device_unlocked(ops, &unlocked) != AVB_IO_RESULT_OK) {
+ avb_fatal("Error determining whether device is unlocked.\n");
+ }
+ avb_printv("read_is_device_unlocked() ops returned that device is ",
+ unlocked ? "UNLOCKED" : "LOCKED",
+ "\n",
+ NULL);
+
+ ab_result = avb_ab_flow(ops->ab_ops,
+ requested_partitions,
+ unlocked /* allow_verification_error */,
+ &slot_data);
+ avb_printv("avb_ab_flow() returned ",
+ avb_ab_flow_result_to_string(ab_result),
+ "\n",
+ NULL);
+ switch (ab_result) {
+ case AVB_AB_FLOW_RESULT_OK:
+ case AVB_AB_FLOW_RESULT_OK_WITH_VERIFICATION_ERROR:
+ avb_printv("slot_suffix: ", slot_data->ab_suffix, "\n", NULL);
+ avb_printv("cmdline: ", slot_data->cmdline, "\n", NULL);
+ /* Pass 'skip_initramfs' since we're not booting into recovery mode. */
+ boot_result =
+ uefi_avb_boot_kernel(ImageHandle, slot_data, "skip_initramfs");
+ avb_fatalv("uefi_avb_boot_kernel() failed with error ",
+ uefi_avb_boot_kernel_result_to_string(boot_result),
+ "\n",
+ NULL);
+ avb_slot_verify_data_free(slot_data);
+ break;
+ case AVB_AB_FLOW_RESULT_ERROR_OOM:
+ avb_fatal("OOM error while doing A/B select flow.\n");
+ break;
+ case AVB_AB_FLOW_RESULT_ERROR_IO:
+ avb_fatal("I/O error while doing A/B select flow.\n");
+ break;
+ case AVB_AB_FLOW_RESULT_ERROR_NO_BOOTABLE_SLOTS:
+ avb_fatal("No bootable slots - enter repair mode\n");
+ break;
+ }
+ uefi_avb_ops_free(ops);
+
+ return EFI_SUCCESS;
+}
diff --git a/avb/examples/uefi/uefi_avb_boot.c b/avb/examples/uefi/uefi_avb_boot.c
new file mode 100644
index 0000000..800c9a1
--- /dev/null
+++ b/avb/examples/uefi/uefi_avb_boot.c
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2017 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 <efi.h>
+#include <efilib.h>
+
+#include "bootimg.h"
+
+#include "uefi_avb_boot.h"
+#include "uefi_avb_util.h"
+
+/* See Documentation/x86/boot.txt for this struct and more information
+ * about the boot/handover protocol.
+ */
+
+#define SETUP_MAGIC 0x53726448 /* "HdrS" */
+
+struct SetupHeader {
+ UINT8 boot_sector[0x01f1];
+ UINT8 setup_secs;
+ UINT16 root_flags;
+ UINT32 sys_size;
+ UINT16 ram_size;
+ UINT16 video_mode;
+ UINT16 root_dev;
+ UINT16 signature;
+ UINT16 jump;
+ UINT32 header;
+ UINT16 version;
+ UINT16 su_switch;
+ UINT16 setup_seg;
+ UINT16 start_sys;
+ UINT16 kernel_ver;
+ UINT8 loader_id;
+ UINT8 load_flags;
+ UINT16 movesize;
+ UINT32 code32_start;
+ UINT32 ramdisk_start;
+ UINT32 ramdisk_len;
+ UINT32 bootsect_kludge;
+ UINT16 heap_end;
+ UINT8 ext_loader_ver;
+ UINT8 ext_loader_type;
+ UINT32 cmd_line_ptr;
+ UINT32 ramdisk_max;
+ UINT32 kernel_alignment;
+ UINT8 relocatable_kernel;
+ UINT8 min_alignment;
+ UINT16 xloadflags;
+ UINT32 cmdline_size;
+ UINT32 hardware_subarch;
+ UINT64 hardware_subarch_data;
+ UINT32 payload_offset;
+ UINT32 payload_length;
+ UINT64 setup_data;
+ UINT64 pref_address;
+ UINT32 init_size;
+ UINT32 handover_offset;
+} __attribute__((packed));
+
+#ifdef __x86_64__
+typedef VOID (*handover_f)(VOID* image,
+ EFI_SYSTEM_TABLE* table,
+ struct SetupHeader* setup);
+static inline VOID linux_efi_handover(EFI_HANDLE image,
+ struct SetupHeader* setup) {
+ handover_f handover;
+
+ asm volatile("cli");
+ handover =
+ (handover_f)((UINTN)setup->code32_start + 512 + setup->handover_offset);
+ handover(image, ST, setup);
+}
+#else
+typedef VOID (*handover_f)(VOID* image,
+ EFI_SYSTEM_TABLE* table,
+ struct SetupHeader* setup)
+ __attribute__((regparm(0)));
+static inline VOID linux_efi_handover(EFI_HANDLE image,
+ struct SetupHeader* setup) {
+ handover_f handover;
+
+ handover = (handover_f)((UINTN)setup->code32_start + setup->handover_offset);
+ handover(image, ST, setup);
+}
+#endif
+
+static size_t round_up(size_t value, size_t size) {
+ size_t ret = value + size - 1;
+ ret /= size;
+ ret *= size;
+ return ret;
+}
+
+UEFIAvbBootKernelResult uefi_avb_boot_kernel(EFI_HANDLE efi_image_handle,
+ AvbSlotVerifyData* slot_data,
+ const char* cmdline_extra) {
+ UEFIAvbBootKernelResult ret;
+ const boot_img_hdr* header;
+ EFI_STATUS err;
+ UINT8* kernel_buf = NULL;
+ UINT8* initramfs_buf = NULL;
+ UINT8* cmdline_utf8 = NULL;
+ AvbPartitionData* boot;
+ size_t offset;
+ uint64_t total_size;
+ size_t initramfs_size;
+ size_t cmdline_first_len;
+ size_t cmdline_second_len;
+ size_t cmdline_extra_len;
+ size_t cmdline_utf8_len;
+ struct SetupHeader* image_setup;
+ struct SetupHeader* setup;
+ EFI_PHYSICAL_ADDRESS addr;
+
+ if (slot_data->num_loaded_partitions != 1) {
+ avb_error("No boot partition.\n");
+ ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_PARTITION_INVALID_FORMAT;
+ goto out;
+ }
+
+ boot = &slot_data->loaded_partitions[0];
+ if (avb_strcmp(boot->partition_name, "boot") != 0) {
+ avb_errorv(
+ "Unexpected partition name '", boot->partition_name, "'.\n", NULL);
+ ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_PARTITION_INVALID_FORMAT;
+ goto out;
+ }
+
+ header = (const boot_img_hdr*)boot->data;
+
+ /* Check boot image header magic field. */
+ if (avb_memcmp(BOOT_MAGIC, header->magic, BOOT_MAGIC_SIZE)) {
+ avb_error("Wrong boot image header magic.\n");
+ ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_PARTITION_INVALID_FORMAT;
+ goto out;
+ }
+
+ /* Sanity check header. */
+ total_size = header->kernel_size;
+ if (!avb_safe_add_to(&total_size, header->ramdisk_size) ||
+ !avb_safe_add_to(&total_size, header->second_size)) {
+ avb_error("Overflow while adding sizes of kernel and initramfs.\n");
+ ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_PARTITION_INVALID_FORMAT;
+ goto out;
+ }
+ if (total_size > boot->data_size) {
+ avb_error("Invalid kernel/initramfs sizes.\n");
+ ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_PARTITION_INVALID_FORMAT;
+ goto out;
+ }
+
+ /* The kernel has to be in its own specific memory pool. */
+ err = uefi_call_wrapper(BS->AllocatePool,
+ NUM_ARGS_ALLOCATE_POOL,
+ EfiLoaderCode,
+ header->kernel_size,
+ &kernel_buf);
+ if (EFI_ERROR(err)) {
+ avb_error("Could not allocate kernel buffer.\n");
+ ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_OOM;
+ goto out;
+ }
+ avb_memcpy(kernel_buf, boot->data + header->page_size, header->kernel_size);
+
+ /* Ditto for the initrd. */
+ initramfs_buf = NULL;
+ initramfs_size = header->ramdisk_size + header->second_size;
+ if (initramfs_size > 0) {
+ err = uefi_call_wrapper(BS->AllocatePool,
+ NUM_ARGS_ALLOCATE_POOL,
+ EfiLoaderCode,
+ initramfs_size,
+ &initramfs_buf);
+ if (EFI_ERROR(err)) {
+ avb_error("Could not allocate initrd buffer.\n");
+ ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_OOM;
+ goto out;
+ }
+ /* Concatente the first and second initramfs. */
+ offset = header->page_size;
+ offset += round_up(header->kernel_size, header->page_size);
+ avb_memcpy(initramfs_buf, boot->data + offset, header->ramdisk_size);
+ offset += round_up(header->ramdisk_size, header->page_size);
+ avb_memcpy(initramfs_buf, boot->data + offset, header->second_size);
+ }
+
+ /* Prepare the command-line. */
+ cmdline_first_len = avb_strlen((const char*)header->cmdline);
+ cmdline_second_len = avb_strlen(slot_data->cmdline);
+ cmdline_extra_len = cmdline_extra != NULL ? avb_strlen(cmdline_extra) : 0;
+ if (cmdline_extra_len > 0) {
+ cmdline_extra_len += 1;
+ }
+ cmdline_utf8_len =
+ cmdline_first_len + 1 + cmdline_second_len + 1 + cmdline_extra_len;
+ err = uefi_call_wrapper(BS->AllocatePool,
+ NUM_ARGS_ALLOCATE_POOL,
+ EfiLoaderCode,
+ cmdline_utf8_len,
+ &cmdline_utf8);
+ if (EFI_ERROR(err)) {
+ avb_error("Could not allocate kernel cmdline.\n");
+ ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_OOM;
+ goto out;
+ }
+ offset = 0;
+ avb_memcpy(cmdline_utf8, header->cmdline, cmdline_first_len);
+ offset += cmdline_first_len;
+ cmdline_utf8[offset] = ' ';
+ offset += 1;
+ avb_memcpy(cmdline_utf8 + offset, slot_data->cmdline, cmdline_second_len);
+ offset += cmdline_second_len;
+ if (cmdline_extra_len > 0) {
+ cmdline_utf8[offset] = ' ';
+ avb_memcpy(cmdline_utf8 + offset + 1, cmdline_extra, cmdline_extra_len - 1);
+ offset += cmdline_extra_len;
+ }
+ cmdline_utf8[offset] = '\0';
+ offset += 1;
+ avb_assert(offset == cmdline_utf8_len);
+
+ /* Now set up the EFI handover. */
+ image_setup = (struct SetupHeader*)kernel_buf;
+ if (image_setup->signature != 0xAA55 || image_setup->header != SETUP_MAGIC) {
+ avb_error("Wrong kernel header magic.\n");
+ ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_KERNEL_INVALID_FORMAT;
+ goto out;
+ }
+
+ if (image_setup->version < 0x20b) {
+ avb_error("Wrong version.\n");
+ ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_KERNEL_INVALID_FORMAT;
+ goto out;
+ }
+
+ if (!image_setup->relocatable_kernel) {
+ avb_error("Kernel is not relocatable.\n");
+ ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_KERNEL_INVALID_FORMAT;
+ goto out;
+ }
+
+ addr = 0x3fffffff;
+ err = uefi_call_wrapper(BS->AllocatePages,
+ 4,
+ AllocateMaxAddress,
+ EfiLoaderData,
+ EFI_SIZE_TO_PAGES(0x4000),
+ &addr);
+ if (EFI_ERROR(err)) {
+ avb_error("Could not allocate setup buffer.\n");
+ ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_OOM;
+ goto out;
+ }
+ setup = (struct SetupHeader*)(UINTN)addr;
+ avb_memset(setup, '\0', 0x4000);
+ avb_memcpy(setup, image_setup, sizeof(struct SetupHeader));
+ setup->loader_id = 0xff;
+ setup->code32_start =
+ ((uintptr_t)kernel_buf) + (image_setup->setup_secs + 1) * 512;
+ setup->cmd_line_ptr = (uintptr_t)cmdline_utf8;
+
+ setup->ramdisk_start = (uintptr_t)initramfs_buf;
+ setup->ramdisk_len = (uintptr_t)initramfs_size;
+
+ /* Jump to the kernel. */
+ linux_efi_handover(efi_image_handle, setup);
+
+ ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_START_KERNEL;
+out:
+ return ret;
+}
+
+const char* uefi_avb_boot_kernel_result_to_string(
+ UEFIAvbBootKernelResult result) {
+ const char* ret = NULL;
+
+ switch (result) {
+ case UEFI_AVB_BOOT_KERNEL_RESULT_OK:
+ ret = "OK";
+ break;
+ case UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_OOM:
+ ret = "ERROR_OEM";
+ break;
+ case UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_IO:
+ ret = "ERROR_IO";
+ break;
+ case UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_PARTITION_INVALID_FORMAT:
+ ret = "ERROR_PARTITION_INVALID_FORMAT";
+ break;
+ case UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_KERNEL_INVALID_FORMAT:
+ ret = "ERROR_KERNEL_INVALID_FORMAT";
+ break;
+ case UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_START_KERNEL:
+ ret = "ERROR_START_KERNEL";
+ break;
+ /* Do not add a 'default:' case here because of -Wswitch. */
+ }
+
+ if (ret == NULL) {
+ avb_error("Unknown UEFIAvbBootKernelResult value.\n");
+ ret = "(unknown)";
+ }
+
+ return ret;
+}
diff --git a/avb/examples/uefi/uefi_avb_boot.h b/avb/examples/uefi/uefi_avb_boot.h
new file mode 100644
index 0000000..d2b8b38
--- /dev/null
+++ b/avb/examples/uefi/uefi_avb_boot.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 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 UEFI_AVB_BOOT_H_
+#define UEFI_AVB_BOOT_H_
+
+#include <efi.h>
+#include <libavb/libavb.h>
+
+/* Return codes used in uefi_avb_boot_kernel().
+ *
+ * Use uefi_avb_boot_kernel_result_to_string to get a textual
+ * representation usable for error/debug output.
+ */
+typedef enum {
+ UEFI_AVB_BOOT_KERNEL_RESULT_OK,
+ UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_OOM,
+ UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_IO,
+ UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_PARTITION_INVALID_FORMAT,
+ UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_KERNEL_INVALID_FORMAT,
+ UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_START_KERNEL
+} UEFIAvbBootKernelResult;
+
+/* Jumps to the Linux kernel embedded in the Android boot image which
+ * has been loaded in |slot_data|. This also supports setting up the
+ * initramfs from the boot image. The command-line to be passed to the
+ * kernel will be the concatenation of
+ *
+ * 1. the cmdline in the Android boot image; and
+ * 2. the cmdline in |slot_data|; and
+ * 3. |cmdline_extra| if not NULL
+ *
+ * in that order with spaces inserted as necessary.
+ *
+ * This function only returns if an error occurs.
+ */
+UEFIAvbBootKernelResult uefi_avb_boot_kernel(EFI_HANDLE efi_image_handle,
+ AvbSlotVerifyData* slot_data,
+ const char* cmdline_extra);
+
+/* Get a textual representation of |result|. */
+const char* uefi_avb_boot_kernel_result_to_string(
+ UEFIAvbBootKernelResult result);
+
+#endif /* UEFI_AVB_BOOT_H_ */
diff --git a/avb/examples/uefi/uefi_avb_ops.c b/avb/examples/uefi/uefi_avb_ops.c
new file mode 100644
index 0000000..3f22d03
--- /dev/null
+++ b/avb/examples/uefi/uefi_avb_ops.c
@@ -0,0 +1,631 @@
+/*
+ * Copyright (C) 2017 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 <efi.h>
+#include <efilib.h>
+
+#include <libavb_ab/libavb_ab.h>
+
+#include "uefi_avb_ops.h"
+#include "uefi_avb_util.h"
+
+#include <efi.h>
+#include <efilib.h>
+
+/* GPT related constants. */
+#define GPT_REVISION 0x00010000
+#define GPT_MAGIC "EFI PART"
+#define GPT_MIN_SIZE 92
+#define GPT_ENTRIES_LBA 2
+#define AVB_BLOCK_SIZE 512
+#define ENTRIES_PER_BLOCK 4
+#define ENTRY_NAME_LEN 36
+#define MAX_GPT_ENTRIES 128
+
+typedef struct {
+ uint8_t signature[8];
+ uint32_t revision;
+ uint32_t header_size;
+ uint32_t header_crc32;
+ uint32_t reserved;
+ uint64_t header_lba;
+ uint64_t alternate_header_lba;
+ uint64_t first_usable_lba;
+ uint64_t last_usable_lba;
+ uint8_t disk_guid[16];
+ uint64_t entry_lba;
+ uint32_t entry_count;
+ uint32_t entry_size;
+ uint32_t entry_crc32;
+ uint8_t reserved2[420];
+} GPTHeader;
+
+typedef struct {
+ uint8_t type_GUID[16];
+ uint8_t unique_GUID[16];
+ uint64_t first_lba;
+ uint64_t last_lba;
+ uint64_t flags;
+ uint16_t name[ENTRY_NAME_LEN];
+} GPTEntry;
+
+static EFI_STATUS find_partition_entry_by_name(IN EFI_BLOCK_IO* block_io,
+ const char* partition_name,
+ GPTEntry** entry_buf) {
+ EFI_STATUS err;
+ GPTHeader* gpt_header = NULL;
+ GPTEntry all_gpt_entries[MAX_GPT_ENTRIES];
+ uint16_t* partition_name_ucs2 = NULL;
+ size_t partition_name_bytes;
+ size_t partition_name_ucs2_capacity;
+ size_t partition_name_ucs2_len;
+
+ gpt_header = (GPTHeader*)avb_malloc(sizeof(GPTHeader));
+ if (gpt_header == NULL) {
+ avb_error("Could not allocate for GPT header\n");
+ return EFI_NOT_FOUND;
+ }
+
+ *entry_buf = (GPTEntry*)avb_malloc(sizeof(GPTEntry) * ENTRIES_PER_BLOCK);
+ if (entry_buf == NULL) {
+ avb_error("Could not allocate for partition entry\n");
+ avb_free(gpt_header);
+ return EFI_NOT_FOUND;
+ }
+
+ err = uefi_call_wrapper(block_io->ReadBlocks,
+ NUM_ARGS_READ_BLOCKS,
+ block_io,
+ block_io->Media->MediaId,
+ 1,
+ sizeof(GPTHeader),
+ gpt_header);
+ if (EFI_ERROR(err)) {
+ avb_error("Could not ReadBlocks for gpt header\n");
+ avb_free(gpt_header);
+ avb_free(*entry_buf);
+ *entry_buf = NULL;
+ return EFI_NOT_FOUND;
+ }
+
+ partition_name_bytes = avb_strlen(partition_name);
+ partition_name_ucs2_capacity = sizeof(uint16_t) * (partition_name_bytes + 1);
+ partition_name_ucs2 = avb_calloc(partition_name_ucs2_capacity);
+ if (partition_name_ucs2 == NULL) {
+ avb_error("Could not allocate for ucs2 partition name\n");
+ avb_free(gpt_header);
+ avb_free(*entry_buf);
+ *entry_buf = NULL;
+ return EFI_NOT_FOUND;
+ }
+ if (!uefi_avb_utf8_to_ucs2((const uint8_t*)partition_name,
+ partition_name_bytes,
+ partition_name_ucs2,
+ partition_name_ucs2_capacity,
+ NULL)) {
+ avb_error("Could not convert partition name to UCS-2\n");
+ avb_free(gpt_header);
+ avb_free(partition_name_ucs2);
+ avb_free(*entry_buf);
+ *entry_buf = NULL;
+ return EFI_NOT_FOUND;
+ }
+ partition_name_ucs2_len = StrLen(partition_name_ucs2);
+
+ /* Block-aligned bytes for entries. */
+ UINTN entries_num_bytes =
+ block_io->Media->BlockSize * (MAX_GPT_ENTRIES / ENTRIES_PER_BLOCK);
+
+ err = uefi_call_wrapper(block_io->ReadBlocks,
+ NUM_ARGS_READ_BLOCKS,
+ block_io,
+ block_io->Media->MediaId,
+ GPT_ENTRIES_LBA,
+ entries_num_bytes,
+ &all_gpt_entries);
+ if (EFI_ERROR(err)) {
+ avb_error("Could not ReadBlocks for GPT header\n");
+ avb_free(gpt_header);
+ avb_free(partition_name_ucs2);
+ avb_free(*entry_buf);
+ *entry_buf = NULL;
+ return EFI_NOT_FOUND;
+ }
+
+ /* Find matching partition name. */
+ for (int n = 0; n < gpt_header->entry_count; n++) {
+ if ((partition_name_ucs2_len == StrLen(all_gpt_entries[n].name)) &&
+ avb_memcmp(all_gpt_entries[n].name,
+ partition_name_ucs2,
+ partition_name_ucs2_len * 2) == 0) {
+ avb_memcpy((*entry_buf), &all_gpt_entries[n], sizeof(GPTEntry));
+ avb_free(partition_name_ucs2);
+ avb_free(gpt_header);
+ return EFI_SUCCESS;
+ }
+ }
+
+ avb_free(partition_name_ucs2);
+ avb_free(gpt_header);
+ avb_free(*entry_buf);
+ *entry_buf = NULL;
+ return EFI_NOT_FOUND;
+}
+
+static AvbIOResult read_from_partition(AvbOps* ops,
+ const char* partition_name,
+ int64_t offset_from_partition,
+ size_t num_bytes,
+ void* buf,
+ size_t* out_num_read) {
+ EFI_STATUS err;
+ GPTEntry* partition_entry;
+ uint64_t partition_size;
+ UEFIAvbOpsData* data = (UEFIAvbOpsData*)ops->user_data;
+
+ avb_assert(partition_name != NULL);
+ avb_assert(buf != NULL);
+ avb_assert(out_num_read != NULL);
+
+ err = find_partition_entry_by_name(
+ data->block_io, partition_name, &partition_entry);
+ if (EFI_ERROR(err)) {
+ return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
+ }
+
+ partition_size = (partition_entry->last_lba - partition_entry->first_lba) *
+ data->block_io->Media->BlockSize;
+
+ if (offset_from_partition < 0) {
+ if ((-offset_from_partition) > partition_size) {
+ avb_error("Offset outside range.\n");
+ avb_free(partition_entry);
+ return AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION;
+ }
+ offset_from_partition = partition_size - (-offset_from_partition);
+ }
+
+ /* Check if num_bytes goes beyond partition end. If so, don't read beyond
+ * this boundary -- do a partial I/O instead.
+ */
+ if (num_bytes > partition_size - offset_from_partition)
+ *out_num_read = partition_size - offset_from_partition;
+ else
+ *out_num_read = num_bytes;
+
+ err = uefi_call_wrapper(
+ data->disk_io->ReadDisk,
+ 5,
+ data->disk_io,
+ data->block_io->Media->MediaId,
+ (partition_entry->first_lba * data->block_io->Media->BlockSize) +
+ offset_from_partition,
+ *out_num_read,
+ buf);
+ if (EFI_ERROR(err)) {
+ avb_error("Could not read from Disk.\n");
+ *out_num_read = 0;
+ avb_free(partition_entry);
+ return AVB_IO_RESULT_ERROR_IO;
+ }
+
+ avb_free(partition_entry);
+ return AVB_IO_RESULT_OK;
+}
+
+static AvbIOResult write_to_partition(AvbOps* ops,
+ const char* partition_name,
+ int64_t offset_from_partition,
+ size_t num_bytes,
+ const void* buf) {
+ EFI_STATUS err;
+ GPTEntry* partition_entry;
+ uint64_t partition_size;
+ UEFIAvbOpsData* data = (UEFIAvbOpsData*)ops->user_data;
+
+ avb_assert(partition_name != NULL);
+ avb_assert(buf != NULL);
+
+ err = find_partition_entry_by_name(
+ data->block_io, partition_name, &partition_entry);
+ if (EFI_ERROR(err)) {
+ return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
+ }
+
+ partition_size = (partition_entry->last_lba - partition_entry->first_lba) *
+ data->block_io->Media->BlockSize;
+
+ if (offset_from_partition < 0) {
+ if ((-offset_from_partition) > partition_size) {
+ avb_error("Offset outside range.\n");
+ avb_free(partition_entry);
+ return AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION;
+ }
+ offset_from_partition = partition_size - (-offset_from_partition);
+ }
+
+ /* Check if num_bytes goes beyond partition end. If so, error out -- no
+ * partial I/O.
+ */
+ if (num_bytes > partition_size - offset_from_partition) {
+ avb_error("Cannot write beyond partition boundary.\n");
+ avb_free(partition_entry);
+ return AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION;
+ }
+
+ err = uefi_call_wrapper(
+ data->disk_io->WriteDisk,
+ 5,
+ data->disk_io,
+ data->block_io->Media->MediaId,
+ (partition_entry->first_lba * data->block_io->Media->BlockSize) +
+ offset_from_partition,
+ num_bytes,
+ buf);
+
+ if (EFI_ERROR(err)) {
+ avb_error("Could not write to Disk.\n");
+ avb_free(partition_entry);
+ return AVB_IO_RESULT_ERROR_IO;
+ }
+
+ avb_free(partition_entry);
+ return AVB_IO_RESULT_OK;
+}
+
+/* Helper method to get the parent path to the current |walker| path
+ * given the initial path, |init|. Resulting path is stored in |next|.
+ * Caller is responsible for freeing |next|. Stores allocated bytes
+ * for |next| in |out_bytes|. Returns EFI_SUCCESS on success.
+ */
+static EFI_STATUS walk_path(IN EFI_DEVICE_PATH* init,
+ IN EFI_DEVICE_PATH* walker,
+ OUT EFI_DEVICE_PATH** next,
+ OUT UINTN* out_bytes) {
+ /* Number of bytes from initial path to current walker. */
+ UINTN walker_bytes = (uint8_t*)NextDevicePathNode(walker) - (uint8_t*)init;
+ *out_bytes = sizeof(EFI_DEVICE_PATH) + walker_bytes;
+
+ *next = (EFI_DEVICE_PATH*)avb_malloc(*out_bytes);
+ if (*next == NULL) {
+ *out_bytes = 0;
+ return EFI_NOT_FOUND;
+ }
+
+ /* Copy in the previous paths. */
+ avb_memcpy((*next), init, walker_bytes);
+ /* Copy in the new ending of the path. */
+ avb_memcpy(
+ (uint8_t*)(*next) + walker_bytes, EndDevicePath, sizeof(EFI_DEVICE_PATH));
+ return EFI_SUCCESS;
+}
+
+/* Helper method to validate a GPT header, |gpth|.
+ *
+ * @return EFI_STATUS EFI_SUCCESS on success.
+ */
+static EFI_STATUS validate_gpt(const IN GPTHeader* gpth) {
+ if (avb_memcmp(gpth->signature, GPT_MAGIC, sizeof(gpth->signature)) != 0) {
+ avb_error("GPT signature does not match.\n");
+ return EFI_NOT_FOUND;
+ }
+ /* Make sure GPT header bytes are within minimun and block size. */
+ if (gpth->header_size < GPT_MIN_SIZE) {
+ avb_error("GPT header too small.\n");
+ return EFI_NOT_FOUND;
+ }
+ if (gpth->header_size > AVB_BLOCK_SIZE) {
+ avb_error("GPT header too big.\n");
+ return EFI_NOT_FOUND;
+ }
+
+ GPTHeader gpth_tmp = {{0}};
+ avb_memcpy(&gpth_tmp, gpth, sizeof(GPTHeader));
+ uint32_t gpt_header_crc = gpth_tmp.header_crc32;
+ gpth_tmp.header_crc32 = 0;
+ uint32_t gpt_header_crc_calc =
+ CalculateCrc((uint8_t*)&gpth_tmp, gpth_tmp.header_size);
+
+ if (gpt_header_crc != gpt_header_crc_calc) {
+ avb_error("GPT header crc invalid.\n");
+ return EFI_NOT_FOUND;
+ }
+
+ if (gpth->revision != GPT_REVISION) {
+ avb_error("GPT header wrong revision.\n");
+ return EFI_NOT_FOUND;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/* Queries |disk_handle| for a |block_io| device and the corresponding
+ * path, |block_path|. The |block_io| device is found by iteratively
+ * querying parent devices and checking for a GPT Header. This
+ * ensures the resulting |block_io| device is the top level block
+ * device having access to partition entries. Returns EFI_STATUS
+ * EFI_NOT_FOUND on failure, EFI_SUCCESS otherwise.
+ */
+static EFI_STATUS get_disk_block_io(IN EFI_HANDLE* block_handle,
+ OUT EFI_BLOCK_IO** block_io,
+ OUT EFI_DISK_IO** disk_io,
+ OUT EFI_DEVICE_PATH** io_path) {
+ EFI_STATUS err;
+ EFI_HANDLE disk_handle;
+ UINTN path_bytes;
+ EFI_DEVICE_PATH* disk_path;
+ EFI_DEVICE_PATH* walker_path;
+ EFI_DEVICE_PATH* init_path;
+ GPTHeader gpt_header = {{0}};
+ init_path = DevicePathFromHandle(block_handle);
+
+ if (!init_path) {
+ return EFI_NOT_FOUND;
+ }
+
+ walker_path = init_path;
+ while (!IsDevicePathEnd(walker_path)) {
+ walker_path = NextDevicePathNode(walker_path);
+
+ err = walk_path(init_path, walker_path, &(*io_path), &path_bytes);
+ if (EFI_ERROR(err)) {
+ avb_error("Cannot walk device path.\n");
+ return EFI_NOT_FOUND;
+ }
+
+ disk_path = (EFI_DEVICE_PATH*)avb_malloc(path_bytes);
+ avb_memcpy(disk_path, *io_path, path_bytes);
+ err = uefi_call_wrapper(BS->LocateDevicePath,
+ NUM_ARGS_LOCATE_DEVICE_PATH,
+ &BlockIoProtocol,
+ &(*io_path),
+ &block_handle);
+ if (EFI_ERROR(err)) {
+ avb_free(*io_path);
+ avb_free(disk_path);
+ continue;
+ }
+ err = uefi_call_wrapper(BS->LocateDevicePath,
+ NUM_ARGS_LOCATE_DEVICE_PATH,
+ &DiskIoProtocol,
+ &disk_path,
+ &disk_handle);
+ if (EFI_ERROR(err)) {
+ avb_error("LocateDevicePath, DISK_IO_PROTOCOL.\n");
+ avb_free(*io_path);
+ avb_free(disk_path);
+ continue;
+ }
+
+ /* Handle Block and Disk I/O. Attempt to get handle on device,
+ * must be Block/Disk Io type.
+ */
+ err = uefi_call_wrapper(BS->HandleProtocol,
+ NUM_ARGS_HANDLE_PROTOCOL,
+ block_handle,
+ &BlockIoProtocol,
+ (VOID**)&(*block_io));
+ if (EFI_ERROR(err)) {
+ avb_error("Cannot get handle on block device.\n");
+ avb_free(*io_path);
+ avb_free(disk_path);
+ continue;
+ }
+ err = uefi_call_wrapper(BS->HandleProtocol,
+ NUM_ARGS_HANDLE_PROTOCOL,
+ disk_handle,
+ &DiskIoProtocol,
+ (VOID**)&(*disk_io));
+ if (EFI_ERROR(err)) {
+ avb_error("Cannot get handle on disk device.\n");
+ avb_free(*io_path);
+ avb_free(disk_path);
+ continue;
+ }
+
+ if ((*block_io)->Media->LogicalPartition ||
+ !(*block_io)->Media->MediaPresent) {
+ avb_error("Logical partion or No Media Present, continue...\n");
+ avb_free(*io_path);
+ avb_free(disk_path);
+ continue;
+ }
+
+ err = uefi_call_wrapper((*block_io)->ReadBlocks,
+ NUM_ARGS_READ_BLOCKS,
+ (*block_io),
+ (*block_io)->Media->MediaId,
+ 1,
+ sizeof(GPTHeader),
+ &gpt_header);
+
+ if (EFI_ERROR(err)) {
+ avb_error("ReadBlocks, Block Media error.\n");
+ avb_free(*io_path);
+ avb_free(disk_path);
+ continue;
+ }
+
+ err = validate_gpt(&gpt_header);
+ if (EFI_ERROR(err)) {
+ avb_error("Invalid GPTHeader\n");
+ avb_free(*io_path);
+ avb_free(disk_path);
+ continue;
+ }
+
+ return EFI_SUCCESS;
+ }
+
+ (*block_io) = NULL;
+ return EFI_NOT_FOUND;
+}
+
+static AvbIOResult validate_vbmeta_public_key(
+ AvbOps* ops,
+ const uint8_t* public_key_data,
+ size_t public_key_length,
+ const uint8_t* public_key_metadata,
+ size_t public_key_metadata_length,
+ bool* out_key_is_trusted) {
+ /* For now we just allow any key. */
+ if (out_key_is_trusted != NULL) {
+ *out_key_is_trusted = true;
+ }
+ avb_debug("TODO: implement validate_vbmeta_public_key().\n");
+ return AVB_IO_RESULT_OK;
+}
+
+static AvbIOResult read_rollback_index(AvbOps* ops,
+ size_t rollback_index_slot,
+ uint64_t* out_rollback_index) {
+ /* For now we always return 0 as the stored rollback index. */
+ avb_debug("TODO: implement read_rollback_index().\n");
+ if (out_rollback_index != NULL) {
+ *out_rollback_index = 0;
+ }
+ return AVB_IO_RESULT_OK;
+}
+
+static AvbIOResult write_rollback_index(AvbOps* ops,
+ size_t rollback_index_slot,
+ uint64_t rollback_index) {
+ /* For now this is a no-op. */
+ avb_debug("TODO: implement write_rollback_index().\n");
+ return AVB_IO_RESULT_OK;
+}
+
+static AvbIOResult read_is_device_unlocked(AvbOps* ops, bool* out_is_unlocked) {
+ /* For now we always return that the device is unlocked. */
+ avb_debug("TODO: implement read_is_device_unlocked().\n");
+ *out_is_unlocked = true;
+ return AVB_IO_RESULT_OK;
+}
+
+static void set_hex(char* buf, uint8_t value) {
+ char hex_digits[17] = "0123456789abcdef";
+ buf[0] = hex_digits[value >> 4];
+ buf[1] = hex_digits[value & 0x0f];
+}
+
+static AvbIOResult get_unique_guid_for_partition(AvbOps* ops,
+ const char* partition,
+ char* guid_buf,
+ size_t guid_buf_size) {
+ EFI_STATUS err;
+ GPTEntry* partition_entry;
+ UEFIAvbOpsData* data = (UEFIAvbOpsData*)ops->user_data;
+
+ avb_assert(partition != NULL);
+ avb_assert(guid_buf != NULL);
+
+ err =
+ find_partition_entry_by_name(data->block_io, partition, &partition_entry);
+ if (EFI_ERROR(err)) {
+ avb_error("Error getting unique GUID for partition.\n");
+ return AVB_IO_RESULT_ERROR_IO;
+ }
+
+ if (guid_buf_size < 37) {
+ avb_error("GUID buffer size too small.\n");
+ return AVB_IO_RESULT_ERROR_IO;
+ }
+
+ /* The GUID encoding is somewhat peculiar in terms of byte order. It
+ * is what it is.
+ */
+ set_hex(guid_buf + 0, partition_entry->unique_GUID[3]);
+ set_hex(guid_buf + 2, partition_entry->unique_GUID[2]);
+ set_hex(guid_buf + 4, partition_entry->unique_GUID[1]);
+ set_hex(guid_buf + 6, partition_entry->unique_GUID[0]);
+ guid_buf[8] = '-';
+ set_hex(guid_buf + 9, partition_entry->unique_GUID[5]);
+ set_hex(guid_buf + 11, partition_entry->unique_GUID[4]);
+ guid_buf[13] = '-';
+ set_hex(guid_buf + 14, partition_entry->unique_GUID[7]);
+ set_hex(guid_buf + 16, partition_entry->unique_GUID[6]);
+ guid_buf[18] = '-';
+ set_hex(guid_buf + 19, partition_entry->unique_GUID[8]);
+ set_hex(guid_buf + 21, partition_entry->unique_GUID[9]);
+ guid_buf[23] = '-';
+ set_hex(guid_buf + 24, partition_entry->unique_GUID[10]);
+ set_hex(guid_buf + 26, partition_entry->unique_GUID[11]);
+ set_hex(guid_buf + 28, partition_entry->unique_GUID[12]);
+ set_hex(guid_buf + 30, partition_entry->unique_GUID[13]);
+ set_hex(guid_buf + 32, partition_entry->unique_GUID[14]);
+ set_hex(guid_buf + 34, partition_entry->unique_GUID[15]);
+ guid_buf[36] = '\0';
+ return AVB_IO_RESULT_OK;
+}
+
+AvbOps* uefi_avb_ops_new(EFI_HANDLE app_image) {
+ UEFIAvbOpsData* data;
+ EFI_STATUS err;
+ EFI_LOADED_IMAGE* loaded_app_image = NULL;
+ EFI_GUID loaded_image_protocol = LOADED_IMAGE_PROTOCOL;
+
+ data = avb_calloc(sizeof(UEFIAvbOpsData));
+ data->ops.user_data = data;
+
+ data->efi_image_handle = app_image;
+ err = uefi_call_wrapper(BS->HandleProtocol,
+ NUM_ARGS_HANDLE_PROTOCOL,
+ app_image,
+ &loaded_image_protocol,
+ (VOID**)&loaded_app_image);
+ if (EFI_ERROR(err)) {
+ avb_error("HandleProtocol, LOADED_IMAGE_PROTOCOL.\n");
+ return 0;
+ }
+
+ /* Get parent device disk and block I/O. */
+ err = get_disk_block_io(loaded_app_image->DeviceHandle,
+ &data->block_io,
+ &data->disk_io,
+ &data->path);
+ if (EFI_ERROR(err)) {
+ avb_error("Could not acquire block or disk device handle.\n");
+ return 0;
+ }
+
+ data->ops.ab_ops = &data->ab_ops;
+ data->ops.read_from_partition = read_from_partition;
+ data->ops.write_to_partition = write_to_partition;
+ data->ops.validate_vbmeta_public_key = validate_vbmeta_public_key;
+ data->ops.read_rollback_index = read_rollback_index;
+ data->ops.write_rollback_index = write_rollback_index;
+ data->ops.read_is_device_unlocked = read_is_device_unlocked;
+ data->ops.get_unique_guid_for_partition = get_unique_guid_for_partition;
+
+ data->ab_ops.ops = &data->ops;
+ data->ab_ops.read_ab_metadata = avb_ab_data_read;
+ data->ab_ops.write_ab_metadata = avb_ab_data_write;
+
+ return &data->ops;
+}
+
+void uefi_avb_ops_free(AvbOps* ops) {
+ UEFIAvbOpsData* data = ops->user_data;
+ avb_free(data);
+}
diff --git a/avb/examples/uefi/uefi_avb_ops.h b/avb/examples/uefi/uefi_avb_ops.h
new file mode 100644
index 0000000..80d13ee
--- /dev/null
+++ b/avb/examples/uefi/uefi_avb_ops.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017 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 UEFI_AVB_OPS_H_
+#define UEFI_AVB_OPS_H_
+
+#include <efi.h>
+#include <libavb_ab/libavb_ab.h>
+
+/* The |user_data| member of AvbOps points to a struct of this type. */
+typedef struct UEFIAvbOpsData {
+ AvbOps ops;
+ AvbABOps ab_ops;
+
+ EFI_HANDLE efi_image_handle;
+ EFI_DEVICE_PATH* path;
+ EFI_BLOCK_IO* block_io;
+ EFI_DISK_IO* disk_io;
+} UEFIAvbOpsData;
+
+/* Returns an AvbOps for use with UEFI. */
+AvbOps* uefi_avb_ops_new(EFI_HANDLE app_image);
+
+/* Frees the AvbOps allocated with uefi_avb_ops_new(). */
+void uefi_avb_ops_free(AvbOps* ops);
+
+#endif /* UEFI_AVB_OPS_H_ */
diff --git a/avb/examples/uefi/uefi_avb_sysdeps.c b/avb/examples/uefi/uefi_avb_sysdeps.c
new file mode 100644
index 0000000..4a41c4f
--- /dev/null
+++ b/avb/examples/uefi/uefi_avb_sysdeps.c
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2017 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 <efi.h>
+#include <efilib.h>
+#include <efistdarg.h>
+
+#include <libavb/libavb.h>
+
+#include "uefi_avb_util.h"
+
+int avb_memcmp(const void* src1, const void* src2, size_t n) {
+ return (int)CompareMem((VOID*)src1, (VOID*)src2, (UINTN)n);
+}
+
+int avb_strcmp(const char* s1, const char* s2) {
+ return (int)strcmpa((CHAR8*)s1, (CHAR8*)s2);
+}
+
+void* avb_memcpy(void* dest, const void* src, size_t n) {
+ CopyMem(dest, (VOID*)src, (UINTN)n);
+ return dest;
+}
+
+void* avb_memset(void* dest, const int c, size_t n) {
+ SetMem(dest, (UINTN)n, (UINT8)c);
+ return dest;
+}
+
+void avb_print(const char* message) {
+ size_t utf8_num_bytes = avb_strlen(message) + 1;
+ size_t max_ucs2_bytes = utf8_num_bytes * 2;
+ uint16_t* message_ucs2 = (uint16_t*)avb_calloc(max_ucs2_bytes);
+ if (message_ucs2 == NULL) {
+ return;
+ }
+ if (uefi_avb_utf8_to_ucs2((const uint8_t*)message,
+ utf8_num_bytes,
+ message_ucs2,
+ max_ucs2_bytes,
+ NULL)) {
+ Print(message_ucs2);
+ }
+ avb_free(message_ucs2);
+}
+
+void avb_printv(const char* message, ...) {
+ va_list ap;
+
+ va_start(ap, message);
+ do {
+ avb_print(message);
+ message = va_arg(ap, const char*);
+ } while (message != NULL);
+ va_end(ap);
+}
+
+void avb_abort(void) {
+ avb_print("\nABORTING...\n");
+ uefi_call_wrapper(BS->Stall, 1, 5 * 1000 * 1000);
+ uefi_call_wrapper(BS->Exit, 4, NULL, EFI_NOT_FOUND, 0, NULL);
+ while (true) {
+ ;
+ }
+}
+
+void* avb_malloc_(size_t size) {
+ EFI_STATUS err;
+ void* x;
+
+ err = uefi_call_wrapper(
+ BS->AllocatePool, 3, EfiBootServicesData, (UINTN)size, &x);
+ if (EFI_ERROR(err)) {
+ return NULL;
+ }
+
+ return x;
+}
+
+void avb_free(void* ptr) {
+ EFI_STATUS err;
+ err = uefi_call_wrapper(BS->FreePool, 1, ptr);
+
+ if (EFI_ERROR(err)) {
+ Print(L"Warning: Bad avb_free: %r\n", err);
+ uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
+ }
+}
+
+size_t avb_strlen(const char* str) {
+ return strlena((CHAR8*)str);
+}
diff --git a/avb/examples/uefi/uefi_avb_util.c b/avb/examples/uefi/uefi_avb_util.c
new file mode 100644
index 0000000..1d59af7
--- /dev/null
+++ b/avb/examples/uefi/uefi_avb_util.c
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 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 "uefi_avb_util.h"
+
+bool uefi_avb_utf8_to_ucs2(const uint8_t* utf8_data,
+ size_t utf8_num_bytes,
+ uint16_t* ucs2_data,
+ size_t ucs2_data_capacity_num_bytes,
+ size_t* out_ucs2_data_num_bytes) {
+ uint32_t i8 = 0;
+ uint32_t i2 = 0;
+
+ do {
+ if (i2 >= ucs2_data_capacity_num_bytes) {
+ break;
+ }
+
+ // cannot represent this character, too long
+ if ((utf8_data[i8] & 0xF8) == 0xF0) {
+ return false;
+ } else if ((utf8_data[i8] & 0xF0) == 0xE0) {
+ ucs2_data[i2] = (uint16_t)((uint16_t)utf8_data[i8] << 12) |
+ (uint16_t)(((uint16_t)utf8_data[i8 + 1] << 6) & 0x0FC0) |
+ (uint16_t)((uint16_t)utf8_data[i8 + 2] & 0x003F);
+ i8 += 3;
+ } else if ((utf8_data[i8] & 0xE0) == 0XC0) {
+ ucs2_data[i2] = (uint16_t)(((uint16_t)utf8_data[i8] << 6) & 0x07C0) |
+ (uint16_t)((uint16_t)utf8_data[i8 + 1] & 0x003F);
+ i8 += 2;
+ } else if (!(utf8_data[i8] >> 7)) {
+ ucs2_data[i2] = (uint16_t)((uint16_t)utf8_data[i8] & 0x00FF);
+ i8++;
+ } else {
+ // invalid utf-8
+ return false;
+ }
+ i2++;
+ } while (i8 < utf8_num_bytes);
+
+ if (out_ucs2_data_num_bytes != NULL) {
+ *out_ucs2_data_num_bytes = i2 * 2;
+ }
+ return true;
+}
diff --git a/avb/examples/uefi/uefi_avb_util.h b/avb/examples/uefi/uefi_avb_util.h
new file mode 100644
index 0000000..6a70557
--- /dev/null
+++ b/avb/examples/uefi/uefi_avb_util.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+// NOTE: See avb_sysdeps.h
+
+#ifndef UEFI_AVB_UTIL_H_
+#define UEFI_AVB_UTIL_H_
+
+#include <libavb/libavb.h>
+
+/* Converts UTF-8 to UCS-2. Returns |false| if invalid UTF-8 or if a
+ * rune cannot be represented as UCS-2.
+ */
+bool uefi_avb_utf8_to_ucs2(const uint8_t* utf8_data,
+ size_t utf8_num_bytes,
+ uint16_t* ucs2_data,
+ size_t ucs2_data_capacity_num_bytes,
+ size_t* out_ucs2_data_num_bytes);
+
+#endif /* UEFI_AVB_UTIL_H_ */
diff --git a/avb/libavb/avb_chain_partition_descriptor.c b/avb/libavb/avb_chain_partition_descriptor.c
new file mode 100644
index 0000000..3f14232
--- /dev/null
+++ b/avb/libavb/avb_chain_partition_descriptor.c
@@ -0,0 +1,64 @@
+/*
+ * 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 "avb_chain_partition_descriptor.h"
+#include "avb_util.h"
+
+bool avb_chain_partition_descriptor_validate_and_byteswap(
+ const AvbChainPartitionDescriptor* src, AvbChainPartitionDescriptor* dest) {
+ uint64_t expected_size;
+
+ avb_memcpy(dest, src, sizeof(AvbChainPartitionDescriptor));
+
+ if (!avb_descriptor_validate_and_byteswap((const AvbDescriptor*)src,
+ (AvbDescriptor*)dest))
+ return false;
+
+ if (dest->parent_descriptor.tag != AVB_DESCRIPTOR_TAG_CHAIN_PARTITION) {
+ avb_error("Invalid tag for chain partition descriptor.\n");
+ return false;
+ }
+
+ dest->rollback_index_location = avb_be32toh(dest->rollback_index_location);
+ dest->partition_name_len = avb_be32toh(dest->partition_name_len);
+ dest->public_key_len = avb_be32toh(dest->public_key_len);
+
+ if (dest->rollback_index_location < 1) {
+ avb_error("Invalid rollback index location value.\n");
+ return false;
+ }
+
+ /* Check that partition_name and public_key are fully contained. */
+ expected_size = sizeof(AvbChainPartitionDescriptor) - sizeof(AvbDescriptor);
+ if (!avb_safe_add_to(&expected_size, dest->partition_name_len) ||
+ !avb_safe_add_to(&expected_size, dest->public_key_len)) {
+ avb_error("Overflow while adding up sizes.\n");
+ return false;
+ }
+ if (expected_size > dest->parent_descriptor.num_bytes_following) {
+ avb_error("Descriptor payload size overflow.\n");
+ return false;
+ }
+ return true;
+}
diff --git a/avb/libavb/avb_chain_partition_descriptor.h b/avb/libavb/avb_chain_partition_descriptor.h
new file mode 100644
index 0000000..f2c9250
--- /dev/null
+++ b/avb/libavb/avb_chain_partition_descriptor.h
@@ -0,0 +1,72 @@
+/*
+ * 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(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION)
+#error "Never include this file directly, include libavb.h instead."
+#endif
+
+#ifndef AVB_CHAIN_PARTITION_DESCRIPTOR_H_
+#define AVB_CHAIN_PARTITION_DESCRIPTOR_H_
+
+#include "avb_descriptor.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* A descriptor containing a pointer to signed integrity data stored
+ * on another partition. The descriptor contains the partition name in
+ * question (without the A/B suffix), the public key used to sign the
+ * integrity data, and rollback index location to use for rollback
+ * protection.
+ *
+ * Following this struct are |partition_name_len| bytes of the
+ * partition name (UTF-8 encoded) and |public_key_len| bytes of the
+ * public key.
+ *
+ * The |reserved| field is for future expansion and must be set to NUL
+ * bytes.
+ */
+typedef struct AvbChainPartitionDescriptor {
+ AvbDescriptor parent_descriptor;
+ uint32_t rollback_index_location;
+ uint32_t partition_name_len;
+ uint32_t public_key_len;
+ uint8_t reserved[64];
+} AVB_ATTR_PACKED AvbChainPartitionDescriptor;
+
+/* Copies |src| to |dest| and validates, byte-swapping fields in the
+ * process if needed. Returns true if valid, false if invalid.
+ *
+ * Data following the struct is not validated nor copied.
+ */
+bool avb_chain_partition_descriptor_validate_and_byteswap(
+ const AvbChainPartitionDescriptor* src,
+ AvbChainPartitionDescriptor* dest) AVB_ATTR_WARN_UNUSED_RESULT;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_CHAIN_PARTITION_DESCRIPTOR_H_ */
diff --git a/avb/libavb/avb_crc32.c b/avb/libavb/avb_crc32.c
new file mode 100644
index 0000000..9abed54
--- /dev/null
+++ b/avb/libavb/avb_crc32.c
@@ -0,0 +1,113 @@
+/*-
+ * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or
+ * code or tables extracted from it, as desired without restriction.
+ */
+
+/*
+ * First, the polynomial itself and its table of feedback terms. The
+ * polynomial is
+ * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
+ *
+ * Note that we take it "backwards" and put the highest-order term in
+ * the lowest-order bit. The X^32 term is "implied"; the LSB is the
+ * X^31 term, etc. The X^0 term (usually shown as "+1") results in
+ * the MSB being 1
+ *
+ * Note that the usual hardware shift register implementation, which
+ * is what we're using (we're merely optimizing it by doing eight-bit
+ * chunks at a time) shifts bits into the lowest-order term. In our
+ * implementation, that means shifting towards the right. Why do we
+ * do it this way? Because the calculated CRC must be transmitted in
+ * order from highest-order term to lowest-order term. UARTs transmit
+ * characters in order from LSB to MSB. By storing the CRC this way
+ * we hand it to the UART in the order low-byte to high-byte; the UART
+ * sends each low-bit to hight-bit; and the result is transmission bit
+ * by bit from highest- to lowest-order term without requiring any bit
+ * shuffling on our part. Reception works similarly
+ *
+ * The feedback terms table consists of 256, 32-bit entries. Notes
+ *
+ * The table can be generated at runtime if desired; code to do so
+ * is shown later. It might not be obvious, but the feedback
+ * terms simply represent the results of eight shift/xor opera
+ * tions for all combinations of data and CRC register values
+ *
+ * The values must be right-shifted by eight bits by the "updcrc
+ * logic; the shift must be unsigned (bring in zeroes). On some
+ * hardware you could probably optimize the shift in assembler by
+ * using byte-swap instructions
+ * polynomial $edb88320
+ *
+ *
+ * CRC32 code derived from work by Gary S. Brown.
+ */
+
+#include "avb_sysdeps.h"
+
+/* Code taken from FreeBSD 8 */
+
+static uint32_t crc32_tab[] = {
+ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
+ 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+ 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
+ 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+ 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+ 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+ 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
+ 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+ 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
+ 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+ 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
+ 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+ 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
+ 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+ 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+ 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+ 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
+ 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+ 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
+ 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
+ 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+ 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
+ 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+ 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+ 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+ 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
+ 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+ 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
+ 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+ 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
+ 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+ 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
+ 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+ 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+ 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+ 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
+ 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+ 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
+ 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
+ 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+ 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d};
+
+/*
+ * A function that calculates the CRC-32 based on the table above is
+ * given below for documentation purposes. An equivalent implementation
+ * of this function that's actually used in the kernel can be found
+ * in sys/libkern.h, where it can be inlined.
+ */
+
+static uint32_t crc32(uint32_t crc_in, const uint8_t* buf, int size) {
+ const uint8_t* p = buf;
+ uint32_t crc;
+
+ crc = crc_in ^ ~0U;
+ while (size--)
+ crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
+ return crc ^ ~0U;
+}
+
+uint32_t avb_crc32(const uint8_t* buf, size_t size) {
+ return crc32(0, buf, size);
+}
diff --git a/avb/libavb/avb_crypto.c b/avb/libavb/avb_crypto.c
new file mode 100644
index 0000000..a428443
--- /dev/null
+++ b/avb/libavb/avb_crypto.c
@@ -0,0 +1,373 @@
+/*
+ * 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 "avb_crypto.h"
+#include "avb_rsa.h"
+#include "avb_sha.h"
+#include "avb_util.h"
+
+/* NOTE: The PKC1-v1.5 padding is a blob of binary DER of ASN.1 and is
+ * obtained from section 5.2.2 of RFC 4880.
+ */
+
+static const uint8_t
+ padding_RSA2048_SHA256[AVB_RSA2048_NUM_BYTES - AVB_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[AVB_RSA4096_NUM_BYTES - AVB_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[AVB_RSA8192_NUM_BYTES - AVB_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[AVB_RSA2048_NUM_BYTES - AVB_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[AVB_RSA4096_NUM_BYTES - AVB_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[AVB_RSA8192_NUM_BYTES - AVB_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};
+
+static AvbAlgorithmData algorithm_data[_AVB_ALGORITHM_NUM_TYPES] = {
+ /* AVB_ALGORITHM_TYPE_NONE */
+ {.padding = NULL, .padding_len = 0, .hash_len = 0},
+ /* AVB_ALGORITHM_TYPE_SHA256_RSA2048 */
+ {.padding = padding_RSA2048_SHA256,
+ .padding_len = sizeof(padding_RSA2048_SHA256),
+ .hash_len = AVB_SHA256_DIGEST_SIZE},
+ /* AVB_ALGORITHM_TYPE_SHA256_RSA4096 */
+ {.padding = padding_RSA4096_SHA256,
+ .padding_len = sizeof(padding_RSA4096_SHA256),
+ .hash_len = AVB_SHA256_DIGEST_SIZE},
+ /* AVB_ALGORITHM_TYPE_SHA256_RSA8192 */
+ {.padding = padding_RSA8192_SHA256,
+ .padding_len = sizeof(padding_RSA8192_SHA256),
+ .hash_len = AVB_SHA256_DIGEST_SIZE},
+ /* AVB_ALGORITHM_TYPE_SHA512_RSA2048 */
+ {.padding = padding_RSA2048_SHA512,
+ .padding_len = sizeof(padding_RSA2048_SHA512),
+ .hash_len = AVB_SHA512_DIGEST_SIZE},
+ /* AVB_ALGORITHM_TYPE_SHA512_RSA4096 */
+ {.padding = padding_RSA4096_SHA512,
+ .padding_len = sizeof(padding_RSA4096_SHA512),
+ .hash_len = AVB_SHA512_DIGEST_SIZE},
+ /* AVB_ALGORITHM_TYPE_SHA512_RSA8192 */
+ {.padding = padding_RSA8192_SHA512,
+ .padding_len = sizeof(padding_RSA8192_SHA512),
+ .hash_len = AVB_SHA512_DIGEST_SIZE},
+};
+
+const AvbAlgorithmData* avb_get_algorithm_data(AvbAlgorithmType algorithm) {
+ if (algorithm >= AVB_ALGORITHM_TYPE_NONE &&
+ algorithm < _AVB_ALGORITHM_NUM_TYPES) {
+ return &algorithm_data[algorithm];
+ }
+ return NULL;
+}
+
+bool avb_rsa_public_key_header_validate_and_byteswap(
+ const AvbRSAPublicKeyHeader* src, AvbRSAPublicKeyHeader* dest) {
+ avb_memcpy(dest, src, sizeof(AvbRSAPublicKeyHeader));
+
+ dest->key_num_bits = avb_be32toh(dest->key_num_bits);
+ dest->n0inv = avb_be32toh(dest->n0inv);
+
+ return true;
+}
diff --git a/avb/libavb/avb_crypto.h b/avb/libavb/avb_crypto.h
new file mode 100644
index 0000000..7e8d7e2
--- /dev/null
+++ b/avb/libavb/avb_crypto.h
@@ -0,0 +1,165 @@
+/*
+ * 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(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION)
+#error "Never include this file directly, include libavb.h instead."
+#endif
+
+#ifndef AVB_CRYPTO_H_
+#define AVB_CRYPTO_H_
+
+#include "avb_sysdeps.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Size of a RSA-2048 signature. */
+#define AVB_RSA2048_NUM_BYTES 256
+
+/* Size of a RSA-4096 signature. */
+#define AVB_RSA4096_NUM_BYTES 512
+
+/* Size of a RSA-8192 signature. */
+#define AVB_RSA8192_NUM_BYTES 1024
+
+/* Size in bytes of a SHA-256 digest. */
+#define AVB_SHA256_DIGEST_SIZE 32
+
+/* Size in bytes of a SHA-512 digest. */
+#define AVB_SHA512_DIGEST_SIZE 64
+
+/* Algorithms that can be used in the vbmeta image for
+ * verification. An algorithm consists of a hash type and a signature
+ * type.
+ *
+ * The data used to calculate the hash is the three blocks mentioned
+ * in the documentation for |AvbVBMetaImageHeader| 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 auxiliary data block, see
+ * |AvbRSAPublicKeyHeader| for the serialization format.
+ *
+ * Each algorithm type is described below:
+ *
+ * AVB_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.
+ *
+ * AVB_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.
+ *
+ * AVB_ALGORITHM_TYPE_SHA256_RSA4096: Like above, but only with
+ * a 4096-bit RSA key and |signature_size| set to 512.
+ *
+ * AVB_ALGORITHM_TYPE_SHA256_RSA8192: Like above, but only with
+ * a 8192-bit RSA key and |signature_size| set to 1024.
+ *
+ * AVB_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.
+ *
+ * AVB_ALGORITHM_TYPE_SHA512_RSA4096: Like above, but only with
+ * a 4096-bit RSA key and |signature_size| set to 512.
+ *
+ * AVB_ALGORITHM_TYPE_SHA512_RSA8192: Like above, but only with
+ * a 8192-bit RSA key and |signature_size| set to 1024.
+ */
+typedef enum {
+ AVB_ALGORITHM_TYPE_NONE,
+ AVB_ALGORITHM_TYPE_SHA256_RSA2048,
+ AVB_ALGORITHM_TYPE_SHA256_RSA4096,
+ AVB_ALGORITHM_TYPE_SHA256_RSA8192,
+ AVB_ALGORITHM_TYPE_SHA512_RSA2048,
+ AVB_ALGORITHM_TYPE_SHA512_RSA4096,
+ AVB_ALGORITHM_TYPE_SHA512_RSA8192,
+ _AVB_ALGORITHM_NUM_TYPES
+} AvbAlgorithmType;
+
+/* Holds algorithm-specific data. The |padding| is needed by avb_rsa_verify. */
+typedef struct {
+ const uint8_t* padding;
+ size_t padding_len;
+ size_t hash_len;
+} AvbAlgorithmData;
+
+/* Provides algorithm-specific data for a given |algorithm|. Returns NULL if
+ * |algorithm| is invalid.
+ */
+const AvbAlgorithmData* avb_get_algorithm_data(AvbAlgorithmType algorithm)
+ AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* 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 avb_rsa_public_key_header_validate_and_byteswap().
+ *
+ * The avb_rsa_verify() function expects a key in this serialized
+ * format.
+ *
+ * The 'avbtool extract_public_key' command can be used to generate a
+ * serialized RSA public key.
+ */
+typedef struct AvbRSAPublicKeyHeader {
+ uint32_t key_num_bits;
+ uint32_t n0inv;
+} AVB_ATTR_PACKED AvbRSAPublicKeyHeader;
+
+/* Copies |src| to |dest| and validates, byte-swapping fields in the
+ * process if needed. Returns true if valid, false if invalid.
+ */
+bool avb_rsa_public_key_header_validate_and_byteswap(
+ const AvbRSAPublicKeyHeader* src,
+ AvbRSAPublicKeyHeader* dest) AVB_ATTR_WARN_UNUSED_RESULT;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_CRYPTO_H_ */
diff --git a/avb/libavb/avb_descriptor.c b/avb/libavb/avb_descriptor.c
new file mode 100644
index 0000000..4f8e925
--- /dev/null
+++ b/avb/libavb/avb_descriptor.c
@@ -0,0 +1,160 @@
+/*
+ * 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 "avb_descriptor.h"
+#include "avb_util.h"
+#include "avb_vbmeta_image.h"
+
+bool avb_descriptor_validate_and_byteswap(const AvbDescriptor* src,
+ AvbDescriptor* dest) {
+ dest->tag = avb_be64toh(src->tag);
+ dest->num_bytes_following = avb_be64toh(src->num_bytes_following);
+
+ if ((dest->num_bytes_following & 0x07) != 0) {
+ avb_error("Descriptor size is not divisible by 8.\n");
+ return false;
+ }
+ return true;
+}
+
+bool avb_descriptor_foreach(const uint8_t* image_data,
+ size_t image_size,
+ AvbDescriptorForeachFunc foreach_func,
+ void* user_data) {
+ const AvbVBMetaImageHeader* header = NULL;
+ bool ret = false;
+ const uint8_t* image_end;
+ const uint8_t* desc_start;
+ const uint8_t* desc_end;
+ const uint8_t* p;
+
+ if (image_data == NULL) {
+ avb_error("image_data is NULL\n.");
+ goto out;
+ }
+
+ if (foreach_func == NULL) {
+ avb_error("foreach_func is NULL\n.");
+ goto out;
+ }
+
+ if (image_size < sizeof(AvbVBMetaImageHeader)) {
+ avb_error("Length is smaller than header.\n");
+ goto out;
+ }
+
+ /* Ensure magic is correct. */
+ if (avb_memcmp(image_data, AVB_MAGIC, AVB_MAGIC_LEN) != 0) {
+ avb_error("Magic is incorrect.\n");
+ goto out;
+ }
+
+ /* Careful, not byteswapped - also ensure it's aligned properly. */
+ avb_assert_aligned(image_data);
+ header = (const AvbVBMetaImageHeader*)image_data;
+ image_end = image_data + image_size;
+
+ desc_start = image_data + sizeof(AvbVBMetaImageHeader) +
+ avb_be64toh(header->authentication_data_block_size) +
+ avb_be64toh(header->descriptors_offset);
+
+ desc_end = desc_start + avb_be64toh(header->descriptors_size);
+
+ if (desc_start < image_data || desc_start > image_end ||
+ desc_end < image_data || desc_end > image_end || desc_end < desc_start) {
+ avb_error("Descriptors not inside passed-in data.\n");
+ goto out;
+ }
+
+ for (p = desc_start; p < desc_end;) {
+ const AvbDescriptor* dh = (const AvbDescriptor*)p;
+ avb_assert_aligned(dh);
+ uint64_t nb_following = avb_be64toh(dh->num_bytes_following);
+ uint64_t nb_total = sizeof(AvbDescriptor) + nb_following;
+
+ if ((nb_total & 7) != 0) {
+ avb_error("Invalid descriptor length.\n");
+ goto out;
+ }
+
+ if (nb_total + p < desc_start || nb_total + p > desc_end) {
+ avb_error("Invalid data in descriptors array.\n");
+ goto out;
+ }
+
+ if (foreach_func(dh, user_data) == 0) {
+ goto out;
+ }
+
+ p += nb_total;
+ }
+
+ ret = true;
+
+out:
+ return ret;
+}
+
+static bool count_descriptors(const AvbDescriptor* descriptor,
+ void* user_data) {
+ size_t* num_descriptors = user_data;
+ *num_descriptors += 1;
+ return true;
+}
+
+typedef struct {
+ size_t descriptor_number;
+ const AvbDescriptor** descriptors;
+} SetDescriptorData;
+
+static bool set_descriptors(const AvbDescriptor* descriptor, void* user_data) {
+ SetDescriptorData* data = user_data;
+ data->descriptors[data->descriptor_number++] = descriptor;
+ return true;
+}
+
+const AvbDescriptor** avb_descriptor_get_all(const uint8_t* image_data,
+ size_t image_size,
+ size_t* out_num_descriptors) {
+ size_t num_descriptors = 0;
+ SetDescriptorData data;
+
+ avb_descriptor_foreach(
+ image_data, image_size, count_descriptors, &num_descriptors);
+
+ data.descriptor_number = 0;
+ data.descriptors =
+ avb_calloc(sizeof(const AvbDescriptor*) * (num_descriptors + 1));
+ if (data.descriptors == NULL) {
+ return NULL;
+ }
+ avb_descriptor_foreach(image_data, image_size, set_descriptors, &data);
+ avb_assert(data.descriptor_number == num_descriptors);
+
+ if (out_num_descriptors != NULL) {
+ *out_num_descriptors = num_descriptors;
+ }
+
+ return data.descriptors;
+}
diff --git a/avb/libavb/avb_descriptor.h b/avb/libavb/avb_descriptor.h
new file mode 100644
index 0000000..5d0f0c6
--- /dev/null
+++ b/avb/libavb/avb_descriptor.h
@@ -0,0 +1,131 @@
+/*
+ * 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(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION)
+#error "Never include this file directly, include libavb.h instead."
+#endif
+
+#ifndef AVB_DESCRIPTOR_H_
+#define AVB_DESCRIPTOR_H_
+
+#include "avb_sysdeps.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Well-known descriptor tags.
+ *
+ * AVB_DESCRIPTOR_TAG_PROPERTY: see |AvbPropertyDescriptor| struct.
+ * AVB_DESCRIPTOR_TAG_HASHTREE: see |AvbHashtreeDescriptor| struct.
+ * AVB_DESCRIPTOR_TAG_HASH: see |AvbHashDescriptor| struct.
+ * AVB_DESCRIPTOR_TAG_KERNEL_CMDLINE: see |AvbKernelCmdlineDescriptor| struct.
+ * AVB_DESCRIPTOR_TAG_CHAIN_PARTITION: see |AvbChainPartitionDescriptor| struct.
+ */
+typedef enum {
+ AVB_DESCRIPTOR_TAG_PROPERTY,
+ AVB_DESCRIPTOR_TAG_HASHTREE,
+ AVB_DESCRIPTOR_TAG_HASH,
+ AVB_DESCRIPTOR_TAG_KERNEL_CMDLINE,
+ AVB_DESCRIPTOR_TAG_CHAIN_PARTITION,
+} AvbDescriptorTag;
+
+/* The header for a serialized descriptor.
+ *
+ * A descriptor always have two fields, a |tag| (denoting its type,
+ * see the |AvbDescriptorTag| enumeration) and the size of the bytes
+ * following, |num_bytes_following|.
+ *
+ * For padding, |num_bytes_following| is always a multiple of 8.
+ */
+typedef struct AvbDescriptor {
+ uint64_t tag;
+ uint64_t num_bytes_following;
+} AVB_ATTR_PACKED AvbDescriptor;
+
+/* Copies |src| to |dest| and validates, byte-swapping fields in the
+ * process if needed. Returns true if valid, false if invalid.
+ *
+ * Data following the struct is not validated nor copied.
+ */
+bool avb_descriptor_validate_and_byteswap(
+ const AvbDescriptor* src, AvbDescriptor* dest) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Signature for callback function used in avb_descriptor_foreach().
+ * The passed in descriptor is given by |descriptor| and the
+ * |user_data| passed to avb_descriptor_foreach() function is in
+ * |user_data|. Return true to continue iterating, false to stop
+ * iterating.
+ *
+ * Note that |descriptor| points into the image passed to
+ * avb_descriptor_foreach() - all fields need to be byteswapped!
+ */
+typedef bool AvbDescriptorForeachFunc(const AvbDescriptor* descriptor,
+ void* user_data);
+
+/* Convenience function to iterate over all descriptors in an vbmeta
+ * image.
+ *
+ * The function given by |foreach_func| will be called for each
+ * descriptor. The given function should return true to continue
+ * iterating, false to stop.
+ *
+ * The |user_data| parameter will be passed to |foreach_func|.
+ *
+ * Returns false if the iteration was short-circuited, that is if
+ * an invocation of |foreach_func| returned false.
+ *
+ * Before using this function, you MUST verify |image_data| with
+ * avb_vbmeta_image_verify() and reject it unless it's signed by a known
+ * good public key. Additionally, |image_data| must be word-aligned.
+ */
+bool avb_descriptor_foreach(const uint8_t* image_data,
+ size_t image_size,
+ AvbDescriptorForeachFunc foreach_func,
+ void* user_data);
+
+/* Gets all descriptors in a vbmeta image.
+ *
+ * The return value is a NULL-pointer terminated array of
+ * AvbDescriptor pointers. Free with avb_free() when you are done with
+ * it. If |out_num_descriptors| is non-NULL, the number of descriptors
+ * will be returned there.
+ *
+ * Note that each AvbDescriptor pointer in the array points into
+ * |image_data| - all fields need to be byteswapped!
+ *
+ * Before using this function, you MUST verify |image_data| with
+ * avb_vbmeta_image_verify() and reject it unless it's signed by a known
+ * good public key. Additionally, |image_data| must be word-aligned.
+ */
+const AvbDescriptor** avb_descriptor_get_all(const uint8_t* image_data,
+ size_t image_size,
+ size_t* out_num_descriptors)
+ AVB_ATTR_WARN_UNUSED_RESULT;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_DESCRIPTOR_H_ */
diff --git a/avb/libavb/avb_footer.c b/avb/libavb/avb_footer.c
new file mode 100644
index 0000000..5f6ed71
--- /dev/null
+++ b/avb/libavb/avb_footer.c
@@ -0,0 +1,54 @@
+/*
+ * 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 "avb_footer.h"
+#include "avb_util.h"
+
+bool avb_footer_validate_and_byteswap(const AvbFooter* src, AvbFooter* dest) {
+ avb_memcpy(dest, src, sizeof(AvbFooter));
+
+ dest->version_major = avb_be32toh(dest->version_major);
+ dest->version_minor = avb_be32toh(dest->version_minor);
+
+ dest->original_image_size = avb_be64toh(dest->original_image_size);
+ dest->vbmeta_offset = avb_be64toh(dest->vbmeta_offset);
+ dest->vbmeta_size = avb_be64toh(dest->vbmeta_size);
+
+ /* Check that magic is correct. */
+ if (avb_safe_memcmp(dest->magic, AVB_FOOTER_MAGIC, AVB_FOOTER_MAGIC_LEN) !=
+ 0) {
+ avb_error("Footer magic is incorrect.\n");
+ return false;
+ }
+
+ /* Ensure we don't attempt to access any fields if the footer major
+ * version is not supported.
+ */
+ if (dest->version_major > AVB_FOOTER_MAJOR_VERSION) {
+ avb_error("No support for footer version.\n");
+ return false;
+ }
+
+ return true;
+}
diff --git a/avb/libavb/avb_footer.h b/avb/libavb/avb_footer.h
new file mode 100644
index 0000000..6607c75
--- /dev/null
+++ b/avb/libavb/avb_footer.h
@@ -0,0 +1,86 @@
+/*
+ * 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(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION)
+#error "Never include this file directly, include libavb.h instead."
+#endif
+
+#ifndef AVB_FOOTER_H_
+#define AVB_FOOTER_H_
+
+#include "avb_sysdeps.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Magic for the footer. */
+#define AVB_FOOTER_MAGIC "AVBf"
+#define AVB_FOOTER_MAGIC_LEN 4
+
+/* Size of the footer. */
+#define AVB_FOOTER_SIZE 64
+
+/* The current MAJOR and MINOR versions used - keep in sync with avbtool. */
+#define AVB_FOOTER_MAJOR_VERSION 1
+#define AVB_FOOTER_MINOR_VERSION 0
+
+/* The struct used as a footer used on partitions, used to find the
+ * AvbVBMetaImageHeader struct. This struct is always stored at the
+ * end of a partition.
+ */
+typedef struct AvbFooter {
+ /* 0: Four bytes equal to "AVBf" (AVB_FOOTER_MAGIC). */
+ uint8_t magic[AVB_FOOTER_MAGIC_LEN];
+ /* 4: The major version of the footer struct. */
+ uint32_t version_major;
+ /* 8: The minor version of the footer struct. */
+ uint32_t version_minor;
+
+ /* 12: The original size of the image on the partition. */
+ uint64_t original_image_size;
+
+ /* 20: The offset of the |AvbVBMetaImageHeader| struct. */
+ uint64_t vbmeta_offset;
+
+ /* 28: The size of the vbmeta block (header + auth + aux blocks). */
+ uint64_t vbmeta_size;
+
+ /* 36: Padding to ensure struct is size AVB_FOOTER_SIZE bytes. This
+ * must be set to zeroes.
+ */
+ uint8_t reserved[28];
+} AVB_ATTR_PACKED AvbFooter;
+
+/* Copies |src| to |dest| and validates, byte-swapping fields in the
+ * process if needed. Returns true if valid, false if invalid.
+ */
+bool avb_footer_validate_and_byteswap(const AvbFooter* src, AvbFooter* dest)
+ AVB_ATTR_WARN_UNUSED_RESULT;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_FOOTER_H_ */
diff --git a/avb/libavb/avb_hash_descriptor.c b/avb/libavb/avb_hash_descriptor.c
new file mode 100644
index 0000000..2e427de
--- /dev/null
+++ b/avb/libavb/avb_hash_descriptor.c
@@ -0,0 +1,61 @@
+/*
+ * 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 "avb_hash_descriptor.h"
+#include "avb_util.h"
+
+bool avb_hash_descriptor_validate_and_byteswap(const AvbHashDescriptor* src,
+ AvbHashDescriptor* dest) {
+ uint64_t expected_size;
+
+ avb_memcpy(dest, src, sizeof(AvbHashDescriptor));
+
+ if (!avb_descriptor_validate_and_byteswap((const AvbDescriptor*)src,
+ (AvbDescriptor*)dest))
+ return false;
+
+ if (dest->parent_descriptor.tag != AVB_DESCRIPTOR_TAG_HASH) {
+ avb_error("Invalid tag for hash descriptor.\n");
+ return false;
+ }
+
+ dest->image_size = avb_be64toh(dest->image_size);
+ dest->partition_name_len = avb_be32toh(dest->partition_name_len);
+ dest->salt_len = avb_be32toh(dest->salt_len);
+ dest->digest_len = avb_be32toh(dest->digest_len);
+
+ /* Check that partition_name, salt, and digest are fully contained. */
+ expected_size = sizeof(AvbHashDescriptor) - sizeof(AvbDescriptor);
+ if (!avb_safe_add_to(&expected_size, dest->partition_name_len) ||
+ !avb_safe_add_to(&expected_size, dest->salt_len) ||
+ !avb_safe_add_to(&expected_size, dest->digest_len)) {
+ avb_error("Overflow while adding up sizes.\n");
+ return false;
+ }
+ if (expected_size > dest->parent_descriptor.num_bytes_following) {
+ avb_error("Descriptor payload size overflow.\n");
+ return false;
+ }
+ return true;
+}
diff --git a/avb/libavb/avb_hash_descriptor.h b/avb/libavb/avb_hash_descriptor.h
new file mode 100644
index 0000000..2668118
--- /dev/null
+++ b/avb/libavb/avb_hash_descriptor.h
@@ -0,0 +1,73 @@
+/*
+ * 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(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION)
+#error "Never include this file directly, include libavb.h instead."
+#endif
+
+#ifndef AVB_HASH_DESCRIPTOR_H_
+#define AVB_HASH_DESCRIPTOR_H_
+
+#include "avb_descriptor.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* A descriptor containing information about hash for an image.
+ *
+ * This descriptor is typically used for boot partitions to verify the
+ * entire kernel+initramfs image before executing it.
+ *
+ * Following this struct are |partition_name_len| bytes of the
+ * partition name (UTF-8 encoded), |salt_len| bytes of salt, and then
+ * |digest_len| bytes of the digest.
+ *
+ * The |reserved| field is for future expansion and must be set to NUL
+ * bytes.
+ */
+typedef struct AvbHashDescriptor {
+ AvbDescriptor parent_descriptor;
+ uint64_t image_size;
+ uint8_t hash_algorithm[32];
+ uint32_t partition_name_len;
+ uint32_t salt_len;
+ uint32_t digest_len;
+ uint8_t reserved[64];
+} AVB_ATTR_PACKED AvbHashDescriptor;
+
+/* Copies |src| to |dest| and validates, byte-swapping fields in the
+ * process if needed. Returns true if valid, false if invalid.
+ *
+ * Data following the struct is not validated nor copied.
+ */
+bool avb_hash_descriptor_validate_and_byteswap(const AvbHashDescriptor* src,
+ AvbHashDescriptor* dest)
+ AVB_ATTR_WARN_UNUSED_RESULT;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_HASH_DESCRIPTOR_H_ */
diff --git a/avb/libavb/avb_hashtree_descriptor.c b/avb/libavb/avb_hashtree_descriptor.c
new file mode 100644
index 0000000..b961e47
--- /dev/null
+++ b/avb/libavb/avb_hashtree_descriptor.c
@@ -0,0 +1,69 @@
+/*
+ * 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 "avb_hashtree_descriptor.h"
+#include "avb_util.h"
+
+bool avb_hashtree_descriptor_validate_and_byteswap(
+ const AvbHashtreeDescriptor* src, AvbHashtreeDescriptor* dest) {
+ uint64_t expected_size;
+
+ avb_memcpy(dest, src, sizeof(AvbHashtreeDescriptor));
+
+ if (!avb_descriptor_validate_and_byteswap((const AvbDescriptor*)src,
+ (AvbDescriptor*)dest))
+ return false;
+
+ if (dest->parent_descriptor.tag != AVB_DESCRIPTOR_TAG_HASHTREE) {
+ avb_error("Invalid tag for hashtree descriptor.\n");
+ return false;
+ }
+
+ dest->dm_verity_version = avb_be32toh(dest->dm_verity_version);
+ dest->image_size = avb_be64toh(dest->image_size);
+ dest->tree_offset = avb_be64toh(dest->tree_offset);
+ dest->tree_size = avb_be64toh(dest->tree_size);
+ dest->data_block_size = avb_be32toh(dest->data_block_size);
+ dest->hash_block_size = avb_be32toh(dest->hash_block_size);
+ dest->fec_num_roots = avb_be32toh(dest->fec_num_roots);
+ dest->fec_offset = avb_be64toh(dest->fec_offset);
+ dest->fec_size = avb_be64toh(dest->fec_size);
+ dest->partition_name_len = avb_be32toh(dest->partition_name_len);
+ dest->salt_len = avb_be32toh(dest->salt_len);
+ dest->root_digest_len = avb_be32toh(dest->root_digest_len);
+
+ /* Check that partition_name, salt, and root_digest are fully contained. */
+ expected_size = sizeof(AvbHashtreeDescriptor) - sizeof(AvbDescriptor);
+ if (!avb_safe_add_to(&expected_size, dest->partition_name_len) ||
+ !avb_safe_add_to(&expected_size, dest->salt_len) ||
+ !avb_safe_add_to(&expected_size, dest->root_digest_len)) {
+ avb_error("Overflow while adding up sizes.\n");
+ return false;
+ }
+ if (expected_size > dest->parent_descriptor.num_bytes_following) {
+ avb_error("Descriptor payload size overflow.\n");
+ return false;
+ }
+ return true;
+}
diff --git a/avb/libavb/avb_hashtree_descriptor.h b/avb/libavb/avb_hashtree_descriptor.h
new file mode 100644
index 0000000..a5aafbf
--- /dev/null
+++ b/avb/libavb/avb_hashtree_descriptor.h
@@ -0,0 +1,83 @@
+/*
+ * 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(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION)
+#error "Never include this file directly, include libavb.h instead."
+#endif
+
+#ifndef AVB_HASHTREE_DESCRIPTOR_H_
+#define AVB_HASHTREE_DESCRIPTOR_H_
+
+#include "avb_descriptor.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* A descriptor containing information about a dm-verity hashtree.
+ *
+ * Hash-trees are used to verify large partitions typically containing
+ * file systems. See
+ * https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for more
+ * information about dm-verity.
+ *
+ * Following this struct are |partition_name_len| bytes of the
+ * partition name (UTF-8 encoded), |salt_len| bytes of salt, and then
+ * |root_digest_len| bytes of the root digest.
+ *
+ * The |reserved| field is for future expansion and must be set to NUL
+ * bytes.
+ */
+typedef struct AvbHashtreeDescriptor {
+ AvbDescriptor parent_descriptor;
+ uint32_t dm_verity_version;
+ uint64_t image_size;
+ uint64_t tree_offset;
+ uint64_t tree_size;
+ uint32_t data_block_size;
+ uint32_t hash_block_size;
+ uint32_t fec_num_roots;
+ uint64_t fec_offset;
+ uint64_t fec_size;
+ uint8_t hash_algorithm[32];
+ uint32_t partition_name_len;
+ uint32_t salt_len;
+ uint32_t root_digest_len;
+ uint8_t reserved[64];
+} AVB_ATTR_PACKED AvbHashtreeDescriptor;
+
+/* Copies |src| to |dest| and validates, byte-swapping fields in the
+ * process if needed. Returns true if valid, false if invalid.
+ *
+ * Data following the struct is not validated nor copied.
+ */
+bool avb_hashtree_descriptor_validate_and_byteswap(
+ const AvbHashtreeDescriptor* src,
+ AvbHashtreeDescriptor* dest) AVB_ATTR_WARN_UNUSED_RESULT;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_HASHTREE_DESCRIPTOR_H_ */
diff --git a/avb/libavb/avb_kernel_cmdline_descriptor.c b/avb/libavb/avb_kernel_cmdline_descriptor.c
new file mode 100644
index 0000000..67521f2
--- /dev/null
+++ b/avb/libavb/avb_kernel_cmdline_descriptor.c
@@ -0,0 +1,58 @@
+/*
+ * 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 "avb_kernel_cmdline_descriptor.h"
+#include "avb_util.h"
+
+bool avb_kernel_cmdline_descriptor_validate_and_byteswap(
+ const AvbKernelCmdlineDescriptor* src, AvbKernelCmdlineDescriptor* dest) {
+ uint64_t expected_size;
+
+ avb_memcpy(dest, src, sizeof(AvbKernelCmdlineDescriptor));
+
+ if (!avb_descriptor_validate_and_byteswap((const AvbDescriptor*)src,
+ (AvbDescriptor*)dest))
+ return false;
+
+ if (dest->parent_descriptor.tag != AVB_DESCRIPTOR_TAG_KERNEL_CMDLINE) {
+ avb_error("Invalid tag for kernel cmdline descriptor.\n");
+ return false;
+ }
+
+ dest->flags = avb_be32toh(dest->flags);
+ dest->kernel_cmdline_length = avb_be32toh(dest->kernel_cmdline_length);
+
+ /* Check that kernel_cmdline is fully contained. */
+ expected_size = sizeof(AvbKernelCmdlineDescriptor) - sizeof(AvbDescriptor);
+ if (!avb_safe_add_to(&expected_size, dest->kernel_cmdline_length)) {
+ avb_error("Overflow while adding up sizes.\n");
+ return false;
+ }
+ if (expected_size > dest->parent_descriptor.num_bytes_following) {
+ avb_error("Descriptor payload size overflow.\n");
+ return false;
+ }
+
+ return true;
+}
diff --git a/avb/libavb/avb_kernel_cmdline_descriptor.h b/avb/libavb/avb_kernel_cmdline_descriptor.h
new file mode 100644
index 0000000..6908b3b
--- /dev/null
+++ b/avb/libavb/avb_kernel_cmdline_descriptor.h
@@ -0,0 +1,81 @@
+/*
+ * 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(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION)
+#error "Never include this file directly, include libavb.h instead."
+#endif
+
+#ifndef AVB_KERNEL_CMDLINE_DESCRIPTOR_H_
+#define AVB_KERNEL_CMDLINE_DESCRIPTOR_H_
+
+#include "avb_descriptor.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Flags for kernel command-line descriptors.
+ *
+ * AVB_KERNEL_CMDLINE_FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED: The
+ * cmdline will only be applied if hashtree verification is not
+ * disabled (cf. AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED).
+ *
+ * AVB_KERNEL_CMDLINE_FLAGS_USE_ONLY_IF_HASHTREE_DISABLED: The cmdline
+ * will only be applied if hashtree verification is disabled
+ * (cf. AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED).
+ */
+typedef enum {
+ AVB_KERNEL_CMDLINE_FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED = (1 << 0),
+ AVB_KERNEL_CMDLINE_FLAGS_USE_ONLY_IF_HASHTREE_DISABLED = (1 << 1)
+} AvbKernelCmdlineFlags;
+
+/* A descriptor containing information to be appended to the kernel
+ * command-line.
+ *
+ * The |flags| field contains flags from the AvbKernelCmdlineFlags
+ * enumeration.
+ *
+ * Following this struct are |kernel_cmdline_len| bytes with the
+ * kernel command-line (UTF-8 encoded).
+ */
+typedef struct AvbKernelCmdlineDescriptor {
+ AvbDescriptor parent_descriptor;
+ uint32_t flags;
+ uint32_t kernel_cmdline_length;
+} AVB_ATTR_PACKED AvbKernelCmdlineDescriptor;
+
+/* Copies |src| to |dest| and validates, byte-swapping fields in the
+ * process if needed. Returns true if valid, false if invalid.
+ *
+ * Data following the struct is not validated nor copied.
+ */
+bool avb_kernel_cmdline_descriptor_validate_and_byteswap(
+ const AvbKernelCmdlineDescriptor* src,
+ AvbKernelCmdlineDescriptor* dest) AVB_ATTR_WARN_UNUSED_RESULT;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_KERNEL_CMDLINE_DESCRIPTOR_H_ */
diff --git a/avb/libavb/avb_ops.h b/avb/libavb/avb_ops.h
new file mode 100644
index 0000000..908c66c
--- /dev/null
+++ b/avb/libavb/avb_ops.h
@@ -0,0 +1,214 @@
+/*
+ * 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(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION)
+#error "Never include this file directly, include libavb.h instead."
+#endif
+
+#ifndef AVB_OPS_H_
+#define AVB_OPS_H_
+
+#include "avb_sysdeps.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Return codes used for I/O operations.
+ *
+ * AVB_IO_RESULT_OK is returned if the requested operation was
+ * successful.
+ *
+ * AVB_IO_RESULT_ERROR_IO is returned if the underlying hardware (disk
+ * or other subsystem) encountered an I/O error.
+ *
+ * AVB_IO_RESULT_ERROR_OOM is returned if unable to allocate memory.
+ *
+ * AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION is returned if the requested
+ * partition does not exist.
+ *
+ * AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION is returned if the
+ * range of bytes requested to be read or written is outside the range
+ * of the partition.
+ */
+typedef enum {
+ AVB_IO_RESULT_OK,
+ AVB_IO_RESULT_ERROR_OOM,
+ AVB_IO_RESULT_ERROR_IO,
+ AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION,
+ AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION
+} AvbIOResult;
+
+struct AvbOps;
+typedef struct AvbOps AvbOps;
+
+/* Forward-declaration of operations in libavb_ab. */
+struct AvbABOps;
+
+/* Forward-declaration of operations in libavb_atx. */
+struct AvbAtxOps;
+
+/* High-level operations/functions/methods that are platform
+ * dependent.
+ */
+struct AvbOps {
+ /* This pointer can be used by the application/bootloader using
+ * libavb and is typically used in each operation to get a pointer
+ * to platform-specific resources. It cannot be used by libraries.
+ */
+ void* user_data;
+
+ /* If libavb_ab is used, this should point to the
+ * AvbABOps. Otherwise it must be set to NULL.
+ */
+ struct AvbABOps* ab_ops;
+
+ /* If libavb_atx is used, this should point to the
+ * AvbAtxOps. Otherwise it must be set to NULL.
+ */
+ struct AvbAtxOps* atx_ops;
+
+ /* Reads |num_bytes| from offset |offset| from partition with name
+ * |partition| (NUL-terminated UTF-8 string). If |offset| is
+ * negative, its absolute value should be interpreted as the number
+ * of bytes from the end of the partition.
+ *
+ * This function returns AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION if
+ * there is no partition with the given name,
+ * AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION if the requested
+ * |offset| is outside the partition, and AVB_IO_RESULT_ERROR_IO if
+ * there was an I/O error from the underlying I/O subsystem. If the
+ * operation succeeds as requested AVB_IO_RESULT_OK is returned and
+ * the data is available in |buffer|.
+ *
+ * The only time partial I/O may occur is if reading beyond the end
+ * of the partition. In this case the value returned in
+ * |out_num_read| may be smaller than |num_bytes|.
+ */
+ AvbIOResult (*read_from_partition)(AvbOps* ops,
+ const char* partition,
+ int64_t offset,
+ size_t num_bytes,
+ void* buffer,
+ size_t* out_num_read);
+
+ /* Writes |num_bytes| from |bffer| at offset |offset| to partition
+ * with name |partition| (NUL-terminated UTF-8 string). If |offset|
+ * is negative, its absolute value should be interpreted as the
+ * number of bytes from the end of the partition.
+ *
+ * This function returns AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION if
+ * there is no partition with the given name,
+ * AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION if the requested
+ * byterange goes outside the partition, and AVB_IO_RESULT_ERROR_IO
+ * if there was an I/O error from the underlying I/O subsystem. If
+ * the operation succeeds as requested AVB_IO_RESULT_OK is
+ * returned.
+ *
+ * This function never does any partial I/O, it either transfers all
+ * of the requested bytes or returns an error.
+ */
+ AvbIOResult (*write_to_partition)(AvbOps* ops,
+ const char* partition,
+ int64_t offset,
+ size_t num_bytes,
+ const void* buffer);
+
+ /* Checks if the given public key used to sign the 'vbmeta'
+ * partition is trusted. Boot loaders typically compare this with
+ * embedded key material generated with 'avbtool
+ * extract_public_key'.
+ *
+ * The public key is in the array pointed to by |public_key_data|
+ * and is of |public_key_length| bytes.
+ *
+ * If there is no public key metadata (set with the avbtool option
+ * --public_key_metadata) then |public_key_metadata| will be set to
+ * NULL. Otherwise this field points to the data which is
+ * |public_key_metadata_length| bytes long.
+ *
+ * If AVB_IO_RESULT_OK is returned then |out_is_trusted| is set -
+ * true if trusted or false if untrusted.
+ */
+ AvbIOResult (*validate_vbmeta_public_key)(AvbOps* ops,
+ const uint8_t* public_key_data,
+ size_t public_key_length,
+ const uint8_t* public_key_metadata,
+ size_t public_key_metadata_length,
+ bool* out_is_trusted);
+
+ /* Gets the rollback index corresponding to the location given by
+ * |rollback_index_location|. The value is returned in
+ * |out_rollback_index|. Returns AVB_IO_RESULT_OK if the rollback
+ * index was retrieved, otherwise an error code.
+ *
+ * A device may have a limited amount of rollback index locations (say,
+ * one or four) so may error out if |rollback_index_location| exceeds
+ * this number.
+ */
+ AvbIOResult (*read_rollback_index)(AvbOps* ops,
+ size_t rollback_index_location,
+ uint64_t* out_rollback_index);
+
+ /* Sets the rollback index corresponding to the location given by
+ * |rollback_index_location| to |rollback_index|. Returns
+ * AVB_IO_RESULT_OK if the rollback index was set, otherwise an
+ * error code.
+ *
+ * A device may have a limited amount of rollback index locations (say,
+ * one or four) so may error out if |rollback_index_location| exceeds
+ * this number.
+ */
+ AvbIOResult (*write_rollback_index)(AvbOps* ops,
+ size_t rollback_index_location,
+ uint64_t rollback_index);
+
+ /* Gets whether the device is unlocked. The value is returned in
+ * |out_is_unlocked| (true if unlocked, false otherwise). Returns
+ * AVB_IO_RESULT_OK if the state was retrieved, otherwise an error
+ * code.
+ */
+ AvbIOResult (*read_is_device_unlocked)(AvbOps* ops, bool* out_is_unlocked);
+
+ /* Gets the unique partition GUID for a partition with name in
+ * |partition| (NUL-terminated UTF-8 string). The GUID is copied as
+ * a string into |guid_buf| of size |guid_buf_size| and will be NUL
+ * terminated. The string must be lower-case and properly
+ * hyphenated. For example:
+ *
+ * 527c1c6d-6361-4593-8842-3c78fcd39219
+ *
+ * Returns AVB_IO_RESULT_OK on success, otherwise an error code.
+ */
+ AvbIOResult (*get_unique_guid_for_partition)(AvbOps* ops,
+ const char* partition,
+ char* guid_buf,
+ size_t guid_buf_size);
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_OPS_H_ */
diff --git a/avb/libavb/avb_property_descriptor.c b/avb/libavb/avb_property_descriptor.c
new file mode 100644
index 0000000..7eba2c0
--- /dev/null
+++ b/avb/libavb/avb_property_descriptor.c
@@ -0,0 +1,185 @@
+/*
+ * 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 "avb_property_descriptor.h"
+#include "avb_util.h"
+
+bool avb_property_descriptor_validate_and_byteswap(
+ const AvbPropertyDescriptor* src, AvbPropertyDescriptor* dest) {
+ uint64_t expected_size;
+
+ avb_memcpy(dest, src, sizeof(AvbPropertyDescriptor));
+
+ if (!avb_descriptor_validate_and_byteswap((const AvbDescriptor*)src,
+ (AvbDescriptor*)dest))
+ return false;
+
+ if (dest->parent_descriptor.tag != AVB_DESCRIPTOR_TAG_PROPERTY) {
+ avb_error("Invalid tag for property descriptor.\n");
+ return false;
+ }
+
+ dest->key_num_bytes = avb_be64toh(dest->key_num_bytes);
+ dest->value_num_bytes = avb_be64toh(dest->value_num_bytes);
+
+ /* Check that key and value are fully contained. */
+ expected_size = sizeof(AvbPropertyDescriptor) - sizeof(AvbDescriptor) + 2;
+ if (!avb_safe_add_to(&expected_size, dest->key_num_bytes) ||
+ !avb_safe_add_to(&expected_size, dest->value_num_bytes)) {
+ avb_error("Overflow while adding up sizes.\n");
+ return false;
+ }
+ if (expected_size > dest->parent_descriptor.num_bytes_following) {
+ avb_error("Descriptor payload size overflow.\n");
+ return false;
+ }
+
+ return true;
+}
+
+typedef struct {
+ const char* key;
+ size_t key_size;
+ const char* ret_value;
+ size_t ret_value_size;
+} PropertyIteratorData;
+
+static bool property_lookup_desc_foreach(const AvbDescriptor* header,
+ void* user_data) {
+ PropertyIteratorData* data = (PropertyIteratorData*)user_data;
+ AvbPropertyDescriptor prop_desc;
+ const uint8_t* p;
+ bool ret = true;
+
+ if (header->tag != AVB_DESCRIPTOR_TAG_PROPERTY) {
+ goto out;
+ }
+
+ if (!avb_property_descriptor_validate_and_byteswap(
+ (const AvbPropertyDescriptor*)header, &prop_desc)) {
+ goto out;
+ }
+
+ p = (const uint8_t*)header;
+ if (p[sizeof(AvbPropertyDescriptor) + prop_desc.key_num_bytes] != 0) {
+ avb_error("No terminating NUL byte in key.\n");
+ goto out;
+ }
+
+ if (data->key_size == prop_desc.key_num_bytes) {
+ if (avb_memcmp(p + sizeof(AvbPropertyDescriptor),
+ data->key,
+ data->key_size) == 0) {
+ data->ret_value = (const char*)(p + sizeof(AvbPropertyDescriptor) +
+ prop_desc.key_num_bytes + 1);
+ data->ret_value_size = prop_desc.value_num_bytes;
+ /* Stop iterating. */
+ ret = false;
+ goto out;
+ }
+ }
+
+out:
+ return ret;
+}
+
+const char* avb_property_lookup(const uint8_t* image_data,
+ size_t image_size,
+ const char* key,
+ size_t key_size,
+ size_t* out_value_size) {
+ PropertyIteratorData data;
+
+ if (key_size == 0) {
+ key_size = avb_strlen(key);
+ }
+
+ data.key = key;
+ data.key_size = key_size;
+
+ if (avb_descriptor_foreach(
+ image_data, image_size, property_lookup_desc_foreach, &data) == 0) {
+ if (out_value_size != NULL) {
+ *out_value_size = data.ret_value_size;
+ }
+ return data.ret_value;
+ }
+
+ if (out_value_size != NULL) {
+ *out_value_size = 0;
+ }
+ return NULL;
+}
+
+bool avb_property_lookup_uint64(const uint8_t* image_data,
+ size_t image_size,
+ const char* key,
+ size_t key_size,
+ uint64_t* out_value) {
+ const char* value;
+ bool ret = false;
+ uint64_t parsed_val;
+ int base;
+ int n;
+
+ value = avb_property_lookup(image_data, image_size, key, key_size, NULL);
+ if (value == NULL) {
+ goto out;
+ }
+
+ base = 10;
+ if (avb_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;
+
+ if (c >= '0' && c <= '9') {
+ digit = c - '0';
+ } else if (base == 16 && c >= 'a' && c <= 'f') {
+ digit = c - 'a' + 10;
+ } else if (base == 16 && c >= 'A' && c <= 'F') {
+ digit = c - 'A' + 10;
+ } else {
+ avb_error("Invalid digit.\n");
+ goto out;
+ }
+
+ parsed_val += digit;
+ }
+
+ ret = true;
+ if (out_value != NULL) {
+ *out_value = parsed_val;
+ }
+
+out:
+ return ret;
+}
diff --git a/avb/libavb/avb_property_descriptor.h b/avb/libavb/avb_property_descriptor.h
new file mode 100644
index 0000000..a2fef69
--- /dev/null
+++ b/avb/libavb/avb_property_descriptor.h
@@ -0,0 +1,107 @@
+/*
+ * 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(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION)
+#error "Never include this file directly, include libavb.h instead."
+#endif
+
+#ifndef AVB_PROPERTY_DESCRIPTOR_H_
+#define AVB_PROPERTY_DESCRIPTOR_H_
+
+#include "avb_descriptor.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* A descriptor for properties (free-form key/value pairs).
+ *
+ * Following this struct are |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.
+ */
+typedef struct AvbPropertyDescriptor {
+ AvbDescriptor parent_descriptor;
+ uint64_t key_num_bytes;
+ uint64_t value_num_bytes;
+} AVB_ATTR_PACKED AvbPropertyDescriptor;
+
+/* Copies |src| to |dest| and validates, byte-swapping fields in the
+ * process if needed. Returns true if valid, false if invalid.
+ *
+ * Data following the struct is not validated nor copied.
+ */
+bool avb_property_descriptor_validate_and_byteswap(
+ const AvbPropertyDescriptor* src,
+ AvbPropertyDescriptor* dest) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Convenience function for looking up the value for a property with
+ * name |key| in a vbmeta image. If |key_size| is 0, |key| must be
+ * NUL-terminated.
+ *
+ * The |image_data| parameter must be a pointer to a vbmeta 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 descriptors 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 descriptors using
+ * avb_descriptor_foreach().
+ *
+ * Before using this function, you MUST verify |image_data| with
+ * avb_vbmeta_image_verify() and reject it unless it's signed by a
+ * known good public key.
+ */
+const char* avb_property_lookup(const uint8_t* image_data,
+ size_t image_size,
+ const char* key,
+ size_t key_size,
+ size_t* out_value_size)
+ AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Like avb_property_lookup() but parses the intial portions of the
+ * value as an unsigned 64-bit integer. Both decimal and hexadecimal
+ * representations (e.g. "0x2a") are supported. Returns false on
+ * failure and true on success. On success, the parsed value is
+ * returned in |out_value|.
+ */
+bool avb_property_lookup_uint64(const uint8_t* image_data,
+ size_t image_size,
+ const char* key,
+ size_t key_size,
+ uint64_t* out_value)
+ AVB_ATTR_WARN_UNUSED_RESULT;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_PROPERTY_DESCRIPTOR_H_ */
diff --git a/avb/libavb/avb_rsa.c b/avb/libavb/avb_rsa.c
new file mode 100644
index 0000000..dcecc16
--- /dev/null
+++ b/avb/libavb/avb_rsa.c
@@ -0,0 +1,299 @@
+/*
+ * 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 libmincrypt RSA verification code to
+ * support multiple RSA key lengths and hash digest algorithms.
+ */
+
+#include "avb_rsa.h"
+#include "avb_sha.h"
+#include "avb_util.h"
+#include "avb_vbmeta_image.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 array (host-byte order) */
+ uint32_t* rr; /* R^2 as array (host-byte order) */
+} Key;
+
+Key* parse_key_data(const uint8_t* data, size_t length) {
+ AvbRSAPublicKeyHeader h;
+ Key* key = NULL;
+ size_t expected_length;
+ unsigned int i;
+ const uint8_t* n;
+ const uint8_t* rr;
+
+ if (!avb_rsa_public_key_header_validate_and_byteswap(
+ (const AvbRSAPublicKeyHeader*)data, &h)) {
+ avb_error("Invalid key.\n");
+ goto fail;
+ }
+
+ if (!(h.key_num_bits == 2048 || h.key_num_bits == 4096 ||
+ h.key_num_bits == 8192)) {
+ avb_error("Unexpected key length.\n");
+ goto fail;
+ }
+
+ expected_length = sizeof(AvbRSAPublicKeyHeader) + 2 * h.key_num_bits / 8;
+ if (length != expected_length) {
+ avb_error("Key does not match expected length.\n");
+ goto fail;
+ }
+
+ n = data + sizeof(AvbRSAPublicKeyHeader);
+ rr = data + sizeof(AvbRSAPublicKeyHeader) + h.key_num_bits / 8;
+
+ /* Store n and rr following the key header so we only have to do one
+ * allocation.
+ */
+ key = (Key*)(avb_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] = avb_be32toh(((uint32_t*)n)[key->len - i - 1]);
+ key->rr[i] = avb_be32toh(((uint32_t*)rr)[key->len - i - 1]);
+ }
+ return key;
+
+fail:
+ if (key != NULL) {
+ avb_free(key);
+ }
+ return NULL;
+}
+
+void free_parsed_key(Key* key) {
+ avb_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*)avb_malloc(key->len * sizeof(uint32_t));
+ uint32_t* aR = (uint32_t*)avb_malloc(key->len * sizeof(uint32_t));
+ uint32_t* aaR = (uint32_t*)avb_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) {
+ avb_free(a);
+ }
+ if (aR != NULL) {
+ avb_free(aR);
+ }
+ if (aaR != NULL) {
+ avb_free(aaR);
+ }
+}
+
+/* Verify a RSA PKCS1.5 signature against an expected hash.
+ * Returns false on failure, true on success.
+ */
+bool avb_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;
+ bool success = false;
+
+ if (key == NULL || sig == NULL || hash == NULL || padding == NULL) {
+ avb_error("Invalid input.\n");
+ goto out;
+ }
+
+ parsed_key = parse_key_data(key, key_num_bytes);
+ if (parsed_key == NULL) {
+ avb_error("Error parsing key.\n");
+ goto out;
+ }
+
+ if (sig_num_bytes != (parsed_key->len * sizeof(uint32_t))) {
+ avb_error("Signature length does not match key length.\n");
+ goto out;
+ }
+
+ if (padding_num_bytes != sig_num_bytes - hash_num_bytes) {
+ avb_error("Padding length does not match hash and signature lengths.\n");
+ goto out;
+ }
+
+ buf = (uint8_t*)avb_malloc(sig_num_bytes);
+ if (buf == NULL) {
+ avb_error("Error allocating memory.\n");
+ goto out;
+ }
+ avb_memcpy(buf, sig, sig_num_bytes);
+
+ modpowF4(parsed_key, buf);
+
+ /* Check padding bytes.
+ *
+ * Even though there are probably no timing issues here, we use
+ * avb_safe_memcmp() just to be on the safe side.
+ */
+ if (avb_safe_memcmp(buf, padding, padding_num_bytes)) {
+ avb_error("Padding check failed.\n");
+ goto out;
+ }
+
+ /* Check hash. */
+ if (avb_safe_memcmp(buf + padding_num_bytes, hash, hash_num_bytes)) {
+ avb_error("Hash check failed.\n");
+ goto out;
+ }
+
+ success = true;
+
+out:
+ if (parsed_key != NULL) {
+ free_parsed_key(parsed_key);
+ }
+ if (buf != NULL) {
+ avb_free(buf);
+ }
+ return success;
+}
diff --git a/avb/libavb/avb_rsa.h b/avb/libavb/avb_rsa.h
new file mode 100644
index 0000000..c2dcf47
--- /dev/null
+++ b/avb/libavb/avb_rsa.h
@@ -0,0 +1,73 @@
+/*
+ * 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 AVB_INSIDE_LIBAVB_H
+#error "You can't include avb_rsa.h in the public header libavb.h."
+#endif
+
+#ifndef AVB_COMPILATION
+#error "Never include this file, it may only be used from internal avb code."
+#endif
+
+#ifndef AVB_RSA_H_
+#define AVB_RSA_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "avb_crypto.h"
+#include "avb_sysdeps.h"
+
+/* Using the key given by |key|, 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
+ * |AvbRSAPublicKeyHeader|, including the two large numbers
+ * following. The |key_num_bytes| must be the size of the entire
+ * serialized key.
+ *
+ * Returns false if verification fails, true otherwise.
+ */
+bool avb_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) AVB_ATTR_WARN_UNUSED_RESULT;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_RSA_H_ */
diff --git a/avb/libavb/avb_sha.h b/avb/libavb/avb_sha.h
new file mode 100644
index 0000000..c5a6a4c
--- /dev/null
+++ b/avb/libavb/avb_sha.h
@@ -0,0 +1,90 @@
+/*
+ * 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 AVB_INSIDE_LIBAVB_H
+#error "You can't include avb_sha.h in the public header libavb.h."
+#endif
+
+#ifndef AVB_COMPILATION
+#error "Never include this file, it may only be used from internal avb code."
+#endif
+
+#ifndef AVB_SHA_H_
+#define AVB_SHA_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "avb_crypto.h"
+#include "avb_sysdeps.h"
+
+/* Block size in bytes of a SHA-256 digest. */
+#define AVB_SHA256_BLOCK_SIZE 64
+
+
+/* Block size in bytes of a SHA-512 digest. */
+#define AVB_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 * AVB_SHA256_BLOCK_SIZE];
+ uint8_t buf[AVB_SHA256_DIGEST_SIZE]; /* Used for storing the final digest. */
+} AvbSHA256Ctx;
+
+/* Data structure used for SHA-512. */
+typedef struct {
+ uint64_t h[8];
+ uint32_t tot_len;
+ uint32_t len;
+ uint8_t block[2 * AVB_SHA512_BLOCK_SIZE];
+ uint8_t buf[AVB_SHA512_DIGEST_SIZE]; /* Used for storing the final digest. */
+} AvbSHA512Ctx;
+
+/* Initializes the SHA-256 context. */
+void avb_sha256_init(AvbSHA256Ctx* ctx);
+
+/* Updates the SHA-256 context with |len| bytes from |data|. */
+void avb_sha256_update(AvbSHA256Ctx* ctx, const uint8_t* data, uint32_t len);
+
+/* Returns the SHA-256 digest. */
+uint8_t* avb_sha256_final(AvbSHA256Ctx* ctx) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Initializes the SHA-512 context. */
+void avb_sha512_init(AvbSHA512Ctx* ctx);
+
+/* Updates the SHA-512 context with |len| bytes from |data|. */
+void avb_sha512_update(AvbSHA512Ctx* ctx, const uint8_t* data, uint32_t len);
+
+/* Returns the SHA-512 digest. */
+uint8_t* avb_sha512_final(AvbSHA512Ctx* ctx) AVB_ATTR_WARN_UNUSED_RESULT;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_SHA_H_ */
diff --git a/avb/libavb/avb_sha256.c b/avb/libavb/avb_sha256.c
new file mode 100644
index 0000000..cdd143a
--- /dev/null
+++ b/avb/libavb/avb_sha256.c
@@ -0,0 +1,390 @@
+/* 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 "avb_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 avb_sha256_init(AvbSHA256Ctx* 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(AvbSHA256Ctx* 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 avb_sha256_update(AvbSHA256Ctx* 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 = AVB_SHA256_BLOCK_SIZE - ctx->len;
+ rem_len = len < tmp_len ? len : tmp_len;
+
+ avb_memcpy(&ctx->block[ctx->len], data, rem_len);
+
+ if (ctx->len + len < AVB_SHA256_BLOCK_SIZE) {
+ ctx->len += len;
+ return;
+ }
+
+ new_len = len - rem_len;
+ block_nb = new_len / AVB_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 % AVB_SHA256_BLOCK_SIZE;
+
+ avb_memcpy(ctx->block, &shifted_data[block_nb << 6], rem_len);
+
+ ctx->len = rem_len;
+ ctx->tot_len += (block_nb + 1) << 6;
+}
+
+uint8_t* avb_sha256_final(AvbSHA256Ctx* ctx) {
+ unsigned int block_nb;
+ unsigned int pm_len;
+ unsigned int len_b;
+#ifndef UNROLL_LOOPS
+ int i;
+#endif
+
+ block_nb =
+ (1 + ((AVB_SHA256_BLOCK_SIZE - 9) < (ctx->len % AVB_SHA256_BLOCK_SIZE)));
+
+ len_b = (ctx->tot_len + ctx->len) << 3;
+ pm_len = block_nb << 6;
+
+ avb_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/avb/libavb/avb_sha512.c b/avb/libavb/avb_sha512.c
new file mode 100644
index 0000000..8df6319
--- /dev/null
+++ b/avb/libavb/avb_sha512.c
@@ -0,0 +1,388 @@
+/* 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 "avb_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 avb_sha512_init(AvbSHA512Ctx* 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(AvbSHA512Ctx* 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 avb_sha512_update(AvbSHA512Ctx* 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 = AVB_SHA512_BLOCK_SIZE - ctx->len;
+ rem_len = len < tmp_len ? len : tmp_len;
+
+ avb_memcpy(&ctx->block[ctx->len], data, rem_len);
+
+ if (ctx->len + len < AVB_SHA512_BLOCK_SIZE) {
+ ctx->len += len;
+ return;
+ }
+
+ new_len = len - rem_len;
+ block_nb = new_len / AVB_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 % AVB_SHA512_BLOCK_SIZE;
+
+ avb_memcpy(ctx->block, &shifted_data[block_nb << 7], rem_len);
+
+ ctx->len = rem_len;
+ ctx->tot_len += (block_nb + 1) << 7;
+}
+
+uint8_t* avb_sha512_final(AvbSHA512Ctx* ctx) {
+ unsigned int block_nb;
+ unsigned int pm_len;
+ unsigned int len_b;
+
+#ifndef UNROLL_LOOPS_SHA512
+ int i;
+#endif
+
+ block_nb =
+ 1 + ((AVB_SHA512_BLOCK_SIZE - 17) < (ctx->len % AVB_SHA512_BLOCK_SIZE));
+
+ len_b = (ctx->tot_len + ctx->len) << 3;
+ pm_len = block_nb << 7;
+
+ avb_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/avb/libavb/avb_slot_verify.c b/avb/libavb/avb_slot_verify.c
new file mode 100644
index 0000000..296d831
--- /dev/null
+++ b/avb/libavb/avb_slot_verify.c
@@ -0,0 +1,1091 @@
+/*
+ * 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 "avb_slot_verify.h"
+#include "avb_chain_partition_descriptor.h"
+#include "avb_footer.h"
+#include "avb_hash_descriptor.h"
+#include "avb_kernel_cmdline_descriptor.h"
+#include "avb_sha.h"
+#include "avb_util.h"
+#include "avb_vbmeta_image.h"
+
+/* Maximum allow length (in bytes) of a partition name, including
+ * ab_suffix.
+ */
+#define PART_NAME_MAX_SIZE 32
+
+/* Maximum number of partitions that can be loaded with avb_slot_verify(). */
+#define MAX_NUMBER_OF_LOADED_PARTITIONS 32
+
+/* Maximum number of vbmeta images that can be loaded with avb_slot_verify(). */
+#define MAX_NUMBER_OF_VBMETA_IMAGES 32
+
+/* Maximum size of a vbmeta image - 64 KiB. */
+#define VBMETA_MAX_SIZE (64 * 1024)
+
+/* Helper function to see if we should continue with verification in
+ * allow_verification_error=true mode if something goes wrong. See the
+ * comments for the avb_slot_verify() function for more information.
+ */
+static inline bool result_should_continue(AvbSlotVerifyResult result) {
+ switch (result) {
+ case AVB_SLOT_VERIFY_RESULT_ERROR_OOM:
+ case AVB_SLOT_VERIFY_RESULT_ERROR_IO:
+ case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA:
+ return false;
+
+ case AVB_SLOT_VERIFY_RESULT_OK:
+ case AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION:
+ case AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX:
+ case AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED:
+ return true;
+ }
+
+ return false;
+}
+
+static AvbSlotVerifyResult load_and_verify_hash_partition(
+ AvbOps* ops,
+ const char* const* requested_partitions,
+ const char* ab_suffix,
+ bool allow_verification_error,
+ const AvbDescriptor* descriptor,
+ AvbSlotVerifyData* slot_data) {
+ AvbHashDescriptor hash_desc;
+ const uint8_t* desc_partition_name = NULL;
+ const uint8_t* desc_salt;
+ const uint8_t* desc_digest;
+ char part_name[PART_NAME_MAX_SIZE];
+ AvbSlotVerifyResult ret;
+ AvbIOResult io_ret;
+ uint8_t* image_buf = NULL;
+ size_t part_num_read;
+ uint8_t* digest;
+ size_t digest_len;
+ const char* found;
+
+ if (!avb_hash_descriptor_validate_and_byteswap(
+ (const AvbHashDescriptor*)descriptor, &hash_desc)) {
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+ goto out;
+ }
+
+ desc_partition_name =
+ ((const uint8_t*)descriptor) + sizeof(AvbHashDescriptor);
+ desc_salt = desc_partition_name + hash_desc.partition_name_len;
+ desc_digest = desc_salt + hash_desc.salt_len;
+
+ if (!avb_validate_utf8(desc_partition_name, hash_desc.partition_name_len)) {
+ avb_error("Partition name is not valid UTF-8.\n");
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+ goto out;
+ }
+
+ if (!avb_str_concat(part_name,
+ sizeof part_name,
+ (const char*)desc_partition_name,
+ hash_desc.partition_name_len,
+ ab_suffix,
+ avb_strlen(ab_suffix))) {
+ avb_error("Partition name and suffix does not fit.\n");
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+ goto out;
+ }
+
+ image_buf = avb_malloc(hash_desc.image_size);
+ if (image_buf == NULL) {
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+ goto out;
+ }
+
+ io_ret = ops->read_from_partition(ops,
+ part_name,
+ 0 /* offset */,
+ hash_desc.image_size,
+ image_buf,
+ &part_num_read);
+ if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+ goto out;
+ } else if (io_ret != AVB_IO_RESULT_OK) {
+ avb_errorv(part_name, ": Error loading data from partition.\n", NULL);
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO;
+ goto out;
+ }
+ if (part_num_read != hash_desc.image_size) {
+ avb_errorv(part_name, ": Read fewer than requested bytes.\n", NULL);
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO;
+ goto out;
+ }
+
+ if (avb_strcmp((const char*)hash_desc.hash_algorithm, "sha256") == 0) {
+ AvbSHA256Ctx sha256_ctx;
+ avb_sha256_init(&sha256_ctx);
+ avb_sha256_update(&sha256_ctx, desc_salt, hash_desc.salt_len);
+ avb_sha256_update(&sha256_ctx, image_buf, hash_desc.image_size);
+ digest = avb_sha256_final(&sha256_ctx);
+ digest_len = AVB_SHA256_DIGEST_SIZE;
+ } else if (avb_strcmp((const char*)hash_desc.hash_algorithm, "sha512") == 0) {
+ AvbSHA512Ctx sha512_ctx;
+ avb_sha512_init(&sha512_ctx);
+ avb_sha512_update(&sha512_ctx, desc_salt, hash_desc.salt_len);
+ avb_sha512_update(&sha512_ctx, image_buf, hash_desc.image_size);
+ digest = avb_sha512_final(&sha512_ctx);
+ digest_len = AVB_SHA512_DIGEST_SIZE;
+ } else {
+ avb_errorv(part_name, ": Unsupported hash algorithm.\n", NULL);
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+ goto out;
+ }
+
+ if (digest_len != hash_desc.digest_len) {
+ avb_errorv(
+ part_name, ": Digest in descriptor not of expected size.\n", NULL);
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+ goto out;
+ }
+
+ if (avb_safe_memcmp(digest, desc_digest, digest_len) != 0) {
+ avb_errorv(part_name,
+ ": Hash of data does not match digest in descriptor.\n",
+ NULL);
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION;
+ goto out;
+ }
+
+ ret = AVB_SLOT_VERIFY_RESULT_OK;
+
+out:
+
+ if (ret == AVB_SLOT_VERIFY_RESULT_OK || result_should_continue(ret)) {
+ /* If this is the requested partition, copy to slot_data. */
+ found = avb_strv_find_str(requested_partitions,
+ (const char*)desc_partition_name,
+ hash_desc.partition_name_len);
+ if (found != NULL) {
+ AvbPartitionData* loaded_partition;
+ if (slot_data->num_loaded_partitions == MAX_NUMBER_OF_LOADED_PARTITIONS) {
+ avb_errorv(part_name, ": Too many loaded partitions.\n", NULL);
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+ goto fail;
+ }
+ loaded_partition =
+ &slot_data->loaded_partitions[slot_data->num_loaded_partitions++];
+ loaded_partition->partition_name = avb_strdup(found);
+ loaded_partition->data_size = hash_desc.image_size;
+ loaded_partition->data = image_buf;
+ image_buf = NULL;
+ }
+ }
+
+fail:
+ if (image_buf != NULL) {
+ avb_free(image_buf);
+ }
+ return ret;
+}
+
+static AvbSlotVerifyResult load_and_verify_vbmeta(
+ AvbOps* ops,
+ const char* const* requested_partitions,
+ const char* ab_suffix,
+ bool allow_verification_error,
+ AvbVBMetaImageFlags toplevel_vbmeta_flags,
+ int rollback_index_location,
+ const char* partition_name,
+ size_t partition_name_len,
+ const uint8_t* expected_public_key,
+ size_t expected_public_key_length,
+ AvbSlotVerifyData* slot_data,
+ AvbAlgorithmType* out_algorithm_type) {
+ char full_partition_name[PART_NAME_MAX_SIZE];
+ AvbSlotVerifyResult ret;
+ AvbIOResult io_ret;
+ size_t vbmeta_offset;
+ size_t vbmeta_size;
+ uint8_t* vbmeta_buf = NULL;
+ size_t vbmeta_num_read;
+ AvbVBMetaVerifyResult vbmeta_ret;
+ const uint8_t* pk_data;
+ size_t pk_len;
+ AvbVBMetaImageHeader vbmeta_header;
+ uint64_t stored_rollback_index;
+ const AvbDescriptor** descriptors = NULL;
+ size_t num_descriptors;
+ size_t n;
+ int is_main_vbmeta;
+ AvbVBMetaData* vbmeta_image_data = NULL;
+
+ ret = AVB_SLOT_VERIFY_RESULT_OK;
+
+ avb_assert(slot_data != NULL);
+
+ is_main_vbmeta = (avb_strcmp(partition_name, "vbmeta") == 0);
+
+ if (!avb_validate_utf8((const uint8_t*)partition_name, partition_name_len)) {
+ avb_error("Partition name is not valid UTF-8.\n");
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+ goto out;
+ }
+
+ /* Construct full partition name. */
+ if (!avb_str_concat(full_partition_name,
+ sizeof full_partition_name,
+ partition_name,
+ partition_name_len,
+ ab_suffix,
+ avb_strlen(ab_suffix))) {
+ avb_error("Partition name and suffix does not fit.\n");
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+ goto out;
+ }
+
+ avb_debugv("Loading vbmeta struct from partition '",
+ full_partition_name,
+ "'.\n",
+ NULL);
+
+ /* If we're loading from the main vbmeta partition, the vbmeta
+ * struct is in the beginning. Otherwise we have to locate it via a
+ * footer.
+ */
+ if (is_main_vbmeta) {
+ vbmeta_offset = 0;
+ vbmeta_size = VBMETA_MAX_SIZE;
+ } else {
+ uint8_t footer_buf[AVB_FOOTER_SIZE];
+ size_t footer_num_read;
+ AvbFooter footer;
+
+ io_ret = ops->read_from_partition(ops,
+ full_partition_name,
+ -AVB_FOOTER_SIZE,
+ AVB_FOOTER_SIZE,
+ footer_buf,
+ &footer_num_read);
+ if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+ goto out;
+ } else if (io_ret != AVB_IO_RESULT_OK) {
+ avb_errorv(full_partition_name, ": Error loading footer.\n", NULL);
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO;
+ goto out;
+ }
+ avb_assert(footer_num_read == AVB_FOOTER_SIZE);
+
+ if (!avb_footer_validate_and_byteswap((const AvbFooter*)footer_buf,
+ &footer)) {
+ avb_errorv(full_partition_name, ": Error validating footer.\n", NULL);
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+ goto out;
+ }
+
+ /* Basic footer sanity check since the data is untrusted. */
+ if (footer.vbmeta_size > VBMETA_MAX_SIZE) {
+ avb_errorv(
+ full_partition_name, ": Invalid vbmeta size in footer.\n", NULL);
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+ goto out;
+ }
+
+ vbmeta_offset = footer.vbmeta_offset;
+ vbmeta_size = footer.vbmeta_size;
+ }
+
+ vbmeta_buf = avb_malloc(vbmeta_size);
+ if (vbmeta_buf == NULL) {
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+ goto out;
+ }
+
+ io_ret = ops->read_from_partition(ops,
+ full_partition_name,
+ vbmeta_offset,
+ vbmeta_size,
+ vbmeta_buf,
+ &vbmeta_num_read);
+ if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+ goto out;
+ } else if (io_ret != AVB_IO_RESULT_OK) {
+ avb_errorv(full_partition_name, ": Error loading vbmeta data.\n", NULL);
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO;
+ goto out;
+ }
+ avb_assert(vbmeta_num_read <= vbmeta_size);
+
+ /* Check if the image is properly signed and get the public key used
+ * to sign the image.
+ */
+ vbmeta_ret =
+ avb_vbmeta_image_verify(vbmeta_buf, vbmeta_num_read, &pk_data, &pk_len);
+ switch (vbmeta_ret) {
+ case AVB_VBMETA_VERIFY_RESULT_OK:
+ avb_assert(pk_data != NULL && pk_len > 0);
+ break;
+
+ case AVB_VBMETA_VERIFY_RESULT_OK_NOT_SIGNED:
+ case AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH:
+ case AVB_VBMETA_VERIFY_RESULT_SIGNATURE_MISMATCH:
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION;
+ avb_errorv(full_partition_name,
+ ": Error verifying vbmeta image: ",
+ avb_vbmeta_verify_result_to_string(vbmeta_ret),
+ "\n",
+ NULL);
+ if (!allow_verification_error) {
+ goto out;
+ }
+ break;
+
+ case AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER:
+ /* No way to continue this case. */
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+ avb_errorv(full_partition_name,
+ ": Error verifying vbmeta image: invalid vbmeta header\n",
+ NULL);
+ goto out;
+ }
+
+ /* Byteswap the header. */
+ avb_vbmeta_image_header_to_host_byte_order((AvbVBMetaImageHeader*)vbmeta_buf,
+ &vbmeta_header);
+
+ /* If we're the toplevel, assign flags so they'll be passed down. */
+ if (is_main_vbmeta) {
+ toplevel_vbmeta_flags = (AvbVBMetaImageFlags)vbmeta_header.flags;
+ } else {
+ if (vbmeta_header.flags != 0) {
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+ avb_errorv(full_partition_name,
+ ": chained vbmeta image has non-zero flags\n",
+ NULL);
+ goto out;
+ }
+ }
+
+ /* Check if key used to make signature matches what is expected. */
+ if (pk_data != NULL) {
+ if (expected_public_key != NULL) {
+ avb_assert(!is_main_vbmeta);
+ if (expected_public_key_length != pk_len ||
+ avb_safe_memcmp(expected_public_key, pk_data, pk_len) != 0) {
+ avb_errorv(full_partition_name,
+ ": Public key used to sign data does not match key in chain "
+ "partition descriptor.\n",
+ NULL);
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED;
+ if (!allow_verification_error) {
+ goto out;
+ }
+ }
+ } else {
+ bool key_is_trusted = false;
+ const uint8_t* pk_metadata = NULL;
+ size_t pk_metadata_len = 0;
+
+ if (vbmeta_header.public_key_metadata_size > 0) {
+ pk_metadata = vbmeta_buf + sizeof(AvbVBMetaImageHeader) +
+ vbmeta_header.authentication_data_block_size +
+ vbmeta_header.public_key_metadata_offset;
+ pk_metadata_len = vbmeta_header.public_key_metadata_size;
+ }
+
+ avb_assert(is_main_vbmeta);
+ io_ret = ops->validate_vbmeta_public_key(
+ ops, pk_data, pk_len, pk_metadata, pk_metadata_len, &key_is_trusted);
+ if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+ goto out;
+ } else if (io_ret != AVB_IO_RESULT_OK) {
+ avb_errorv(full_partition_name,
+ ": Error while checking public key used to sign data.\n",
+ NULL);
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO;
+ goto out;
+ }
+ if (!key_is_trusted) {
+ avb_errorv(full_partition_name,
+ ": Public key used to sign data rejected.\n",
+ NULL);
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED;
+ if (!allow_verification_error) {
+ goto out;
+ }
+ }
+ }
+ }
+
+ /* Check rollback index. */
+ io_ret = ops->read_rollback_index(
+ ops, rollback_index_location, &stored_rollback_index);
+ if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+ goto out;
+ } else if (io_ret != AVB_IO_RESULT_OK) {
+ avb_errorv(full_partition_name,
+ ": Error getting rollback index for location.\n",
+ NULL);
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO;
+ goto out;
+ }
+ if (vbmeta_header.rollback_index < stored_rollback_index) {
+ avb_errorv(
+ full_partition_name,
+ ": Image rollback index is less than the stored rollback index.\n",
+ NULL);
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX;
+ if (!allow_verification_error) {
+ goto out;
+ }
+ }
+
+ /* Copy vbmeta to vbmeta_images before recursing. */
+ if (is_main_vbmeta) {
+ avb_assert(slot_data->num_vbmeta_images == 0);
+ } else {
+ avb_assert(slot_data->num_vbmeta_images > 0);
+ }
+ if (slot_data->num_vbmeta_images == MAX_NUMBER_OF_VBMETA_IMAGES) {
+ avb_errorv(full_partition_name, ": Too many vbmeta images.\n", NULL);
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+ goto out;
+ }
+ vbmeta_image_data = &slot_data->vbmeta_images[slot_data->num_vbmeta_images++];
+ vbmeta_image_data->partition_name = avb_strdup(partition_name);
+ vbmeta_image_data->vbmeta_data = vbmeta_buf;
+ /* Note that |vbmeta_buf| is actually |vbmeta_num_read| bytes long
+ * and this includes data past the end of the image. Pass the
+ * actual size of the vbmeta image. Also, no need to use
+ * avb_safe_add() since the header has already been verified.
+ */
+ vbmeta_image_data->vbmeta_size =
+ sizeof(AvbVBMetaImageHeader) +
+ vbmeta_header.authentication_data_block_size +
+ vbmeta_header.auxiliary_data_block_size;
+ vbmeta_image_data->verify_result = vbmeta_ret;
+
+ /* Now go through all descriptors and take the appropriate action:
+ *
+ * - hash descriptor: Load data from partition, calculate hash, and
+ * checks that it matches what's in the hash descriptor.
+ *
+ * - hashtree descriptor: Do nothing since verification happens
+ * on-the-fly from within the OS.
+ *
+ * - chained partition descriptor: Load the footer, load the vbmeta
+ * image, verify vbmeta image (includes rollback checks, hash
+ * checks, bail on chained partitions).
+ */
+ descriptors =
+ avb_descriptor_get_all(vbmeta_buf, vbmeta_num_read, &num_descriptors);
+ for (n = 0; n < num_descriptors; n++) {
+ AvbDescriptor desc;
+
+ if (!avb_descriptor_validate_and_byteswap(descriptors[n], &desc)) {
+ avb_errorv(full_partition_name, ": Descriptor is invalid.\n", NULL);
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+ goto out;
+ }
+
+ switch (desc.tag) {
+ case AVB_DESCRIPTOR_TAG_HASH: {
+ AvbSlotVerifyResult sub_ret;
+ sub_ret = load_and_verify_hash_partition(ops,
+ requested_partitions,
+ ab_suffix,
+ allow_verification_error,
+ descriptors[n],
+ slot_data);
+ if (sub_ret != AVB_SLOT_VERIFY_RESULT_OK) {
+ ret = sub_ret;
+ if (!allow_verification_error || !result_should_continue(ret)) {
+ goto out;
+ }
+ }
+ } break;
+
+ case AVB_DESCRIPTOR_TAG_CHAIN_PARTITION: {
+ AvbSlotVerifyResult sub_ret;
+ AvbChainPartitionDescriptor chain_desc;
+ const uint8_t* chain_partition_name;
+ const uint8_t* chain_public_key;
+
+ /* Only allow CHAIN_PARTITION descriptors in the main vbmeta image. */
+ if (!is_main_vbmeta) {
+ avb_errorv(full_partition_name,
+ ": Encountered chain descriptor not in main image.\n",
+ NULL);
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+ goto out;
+ }
+
+ if (!avb_chain_partition_descriptor_validate_and_byteswap(
+ (AvbChainPartitionDescriptor*)descriptors[n], &chain_desc)) {
+ avb_errorv(full_partition_name,
+ ": Chain partition descriptor is invalid.\n",
+ NULL);
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+ goto out;
+ }
+
+ chain_partition_name = ((const uint8_t*)descriptors[n]) +
+ sizeof(AvbChainPartitionDescriptor);
+ chain_public_key = chain_partition_name + chain_desc.partition_name_len;
+
+ sub_ret = load_and_verify_vbmeta(ops,
+ requested_partitions,
+ ab_suffix,
+ allow_verification_error,
+ toplevel_vbmeta_flags,
+ chain_desc.rollback_index_location,
+ (const char*)chain_partition_name,
+ chain_desc.partition_name_len,
+ chain_public_key,
+ chain_desc.public_key_len,
+ slot_data,
+ NULL /* out_algorithm_type */);
+ if (sub_ret != AVB_SLOT_VERIFY_RESULT_OK) {
+ ret = sub_ret;
+ if (!result_should_continue(ret)) {
+ goto out;
+ }
+ }
+ } break;
+
+ case AVB_DESCRIPTOR_TAG_KERNEL_CMDLINE: {
+ const uint8_t* kernel_cmdline;
+ AvbKernelCmdlineDescriptor kernel_cmdline_desc;
+ bool apply_cmdline;
+
+ if (!avb_kernel_cmdline_descriptor_validate_and_byteswap(
+ (AvbKernelCmdlineDescriptor*)descriptors[n],
+ &kernel_cmdline_desc)) {
+ avb_errorv(full_partition_name,
+ ": Kernel cmdline descriptor is invalid.\n",
+ NULL);
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+ goto out;
+ }
+
+ kernel_cmdline = ((const uint8_t*)descriptors[n]) +
+ sizeof(AvbKernelCmdlineDescriptor);
+
+ if (!avb_validate_utf8(kernel_cmdline,
+ kernel_cmdline_desc.kernel_cmdline_length)) {
+ avb_errorv(full_partition_name,
+ ": Kernel cmdline is not valid UTF-8.\n",
+ NULL);
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+ goto out;
+ }
+
+ /* Compare the flags for top-level VBMeta struct with flags in
+ * the command-line descriptor so command-line snippets only
+ * intended for a certain mode (dm-verity enabled/disabled)
+ * are skipped if applicable.
+ */
+ apply_cmdline = true;
+ if (toplevel_vbmeta_flags & AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED) {
+ if (kernel_cmdline_desc.flags &
+ AVB_KERNEL_CMDLINE_FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED) {
+ apply_cmdline = false;
+ }
+ } else {
+ if (kernel_cmdline_desc.flags &
+ AVB_KERNEL_CMDLINE_FLAGS_USE_ONLY_IF_HASHTREE_DISABLED) {
+ apply_cmdline = false;
+ }
+ }
+
+ if (apply_cmdline) {
+ if (slot_data->cmdline == NULL) {
+ slot_data->cmdline =
+ avb_calloc(kernel_cmdline_desc.kernel_cmdline_length + 1);
+ if (slot_data->cmdline == NULL) {
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+ goto out;
+ }
+ avb_memcpy(slot_data->cmdline,
+ kernel_cmdline,
+ kernel_cmdline_desc.kernel_cmdline_length);
+ } else {
+ /* new cmdline is: <existing_cmdline> + ' ' + <newcmdline> + '\0' */
+ size_t orig_size = avb_strlen(slot_data->cmdline);
+ size_t new_size =
+ orig_size + 1 + kernel_cmdline_desc.kernel_cmdline_length + 1;
+ char* new_cmdline = avb_calloc(new_size);
+ if (new_cmdline == NULL) {
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+ goto out;
+ }
+ avb_memcpy(new_cmdline, slot_data->cmdline, orig_size);
+ new_cmdline[orig_size] = ' ';
+ avb_memcpy(new_cmdline + orig_size + 1,
+ kernel_cmdline,
+ kernel_cmdline_desc.kernel_cmdline_length);
+ avb_free(slot_data->cmdline);
+ slot_data->cmdline = new_cmdline;
+ }
+ }
+ } break;
+
+ /* Explicit fall-through */
+ case AVB_DESCRIPTOR_TAG_PROPERTY:
+ case AVB_DESCRIPTOR_TAG_HASHTREE:
+ /* Do nothing. */
+ break;
+ }
+ }
+
+ if (rollback_index_location >= AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS) {
+ avb_errorv(
+ full_partition_name, ": Invalid rollback_index_location.\n", NULL);
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+ goto out;
+ }
+
+ slot_data->rollback_indexes[rollback_index_location] =
+ vbmeta_header.rollback_index;
+
+ if (out_algorithm_type != NULL) {
+ *out_algorithm_type = (AvbAlgorithmType)vbmeta_header.algorithm_type;
+ }
+
+out:
+ /* If |vbmeta_image_data| isn't NULL it means that it adopted
+ * |vbmeta_buf| so in that case don't free it here.
+ */
+ if (vbmeta_image_data == NULL) {
+ if (vbmeta_buf != NULL) {
+ avb_free(vbmeta_buf);
+ }
+ }
+ if (descriptors != NULL) {
+ avb_free(descriptors);
+ }
+ return ret;
+}
+
+#define NUM_GUIDS 3
+
+/* Substitutes all variables (e.g. $(ANDROID_SYSTEM_PARTUUID)) with
+ * values. Returns NULL on OOM, otherwise the cmdline with values
+ * replaced.
+ */
+static char* sub_cmdline(AvbOps* ops,
+ const char* cmdline,
+ const char* ab_suffix) {
+ const char* part_name_str[NUM_GUIDS] = {"system", "boot", "vbmeta"};
+ const char* replace_str[NUM_GUIDS] = {"$(ANDROID_SYSTEM_PARTUUID)",
+ "$(ANDROID_BOOT_PARTUUID)",
+ "$(ANDROID_VBMETA_PARTUUID)"};
+ char* ret = NULL;
+ AvbIOResult io_ret;
+
+ /* Replace unique partition GUIDs */
+ for (size_t n = 0; n < NUM_GUIDS; n++) {
+ char part_name[PART_NAME_MAX_SIZE];
+ char guid_buf[37];
+ char* new_ret;
+
+ if (!avb_str_concat(part_name,
+ sizeof part_name,
+ part_name_str[n],
+ avb_strlen(part_name_str[n]),
+ ab_suffix,
+ avb_strlen(ab_suffix))) {
+ avb_error("Partition name and suffix does not fit.\n");
+ goto fail;
+ }
+
+ io_ret = ops->get_unique_guid_for_partition(
+ ops, part_name, guid_buf, sizeof guid_buf);
+ if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
+ return NULL;
+ } else if (io_ret != AVB_IO_RESULT_OK) {
+ avb_error("Error getting unique GUID for partition.\n");
+ goto fail;
+ }
+
+ if (ret == NULL) {
+ new_ret = avb_replace(cmdline, replace_str[n], guid_buf);
+ } else {
+ new_ret = avb_replace(ret, replace_str[n], guid_buf);
+ }
+ if (new_ret == NULL) {
+ goto fail;
+ }
+ ret = new_ret;
+ }
+
+ return ret;
+
+fail:
+ if (ret != NULL) {
+ avb_free(ret);
+ }
+ return NULL;
+}
+
+static int cmdline_append_option(AvbSlotVerifyData* slot_data,
+ const char* key,
+ const char* value) {
+ size_t offset, key_len, value_len;
+ char* new_cmdline;
+
+ key_len = avb_strlen(key);
+ value_len = avb_strlen(value);
+
+ offset = 0;
+ if (slot_data->cmdline != NULL) {
+ offset = avb_strlen(slot_data->cmdline);
+ if (offset > 0) {
+ offset += 1;
+ }
+ }
+
+ new_cmdline = avb_calloc(offset + key_len + value_len + 2);
+ if (new_cmdline == NULL) {
+ return 0;
+ }
+ if (offset > 0) {
+ avb_memcpy(new_cmdline, slot_data->cmdline, offset - 1);
+ new_cmdline[offset - 1] = ' ';
+ }
+ avb_memcpy(new_cmdline + offset, key, key_len);
+ new_cmdline[offset + key_len] = '=';
+ avb_memcpy(new_cmdline + offset + key_len + 1, value, value_len);
+ if (slot_data->cmdline != NULL) {
+ avb_free(slot_data->cmdline);
+ }
+ slot_data->cmdline = new_cmdline;
+
+ return 1;
+}
+
+static int cmdline_append_uint64_base10(AvbSlotVerifyData* slot_data,
+ const char* key,
+ uint64_t value) {
+ const int MAX_DIGITS = 32;
+ char rev_digits[MAX_DIGITS];
+ char digits[MAX_DIGITS];
+ size_t n, num_digits;
+
+ for (num_digits = 0; num_digits < MAX_DIGITS - 1;) {
+ rev_digits[num_digits++] = (value % 10) + '0';
+ value /= 10;
+ if (value == 0) {
+ break;
+ }
+ }
+
+ for (n = 0; n < num_digits; n++) {
+ digits[n] = rev_digits[num_digits - 1 - n];
+ }
+ digits[n] = '\0';
+
+ return cmdline_append_option(slot_data, key, digits);
+}
+
+static int cmdline_append_hex(AvbSlotVerifyData* slot_data,
+ const char* key,
+ const uint8_t* data,
+ size_t data_len) {
+ char hex_digits[17] = "0123456789abcdef";
+ char* hex_data;
+ int ret;
+ size_t n;
+
+ hex_data = avb_malloc(data_len * 2 + 1);
+ if (hex_data == NULL) {
+ return 0;
+ }
+
+ for (n = 0; n < data_len; n++) {
+ hex_data[n * 2] = hex_digits[data[n] >> 4];
+ hex_data[n * 2 + 1] = hex_digits[data[n] & 0x0f];
+ }
+ hex_data[n * 2] = '\0';
+
+ ret = cmdline_append_option(slot_data, key, hex_data);
+ avb_free(hex_data);
+ return ret;
+}
+
+AvbSlotVerifyResult avb_slot_verify(AvbOps* ops,
+ const char* const* requested_partitions,
+ const char* ab_suffix,
+ bool allow_verification_error,
+ AvbSlotVerifyData** out_data) {
+ AvbSlotVerifyResult ret;
+ AvbSlotVerifyData* slot_data = NULL;
+ AvbAlgorithmType algorithm_type = AVB_ALGORITHM_TYPE_NONE;
+ AvbIOResult io_ret;
+
+ if (out_data != NULL) {
+ *out_data = NULL;
+ }
+
+ slot_data = avb_calloc(sizeof(AvbSlotVerifyData));
+ if (slot_data == NULL) {
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+ goto fail;
+ }
+ slot_data->vbmeta_images =
+ avb_calloc(sizeof(AvbVBMetaData) * MAX_NUMBER_OF_VBMETA_IMAGES);
+ if (slot_data->vbmeta_images == NULL) {
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+ goto fail;
+ }
+ slot_data->loaded_partitions =
+ avb_calloc(sizeof(AvbPartitionData) * MAX_NUMBER_OF_LOADED_PARTITIONS);
+ if (slot_data->loaded_partitions == NULL) {
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+ goto fail;
+ }
+
+ ret = load_and_verify_vbmeta(ops,
+ requested_partitions,
+ ab_suffix,
+ allow_verification_error,
+ 0, /* toplevel_vbmeta_flags */
+ 0 /* rollback_index_location */,
+ "vbmeta",
+ avb_strlen("vbmeta"),
+ NULL /* expected_public_key */,
+ 0 /* expected_public_key_length */,
+ slot_data,
+ &algorithm_type);
+ if (!allow_verification_error && ret != AVB_SLOT_VERIFY_RESULT_OK) {
+ goto fail;
+ }
+
+ /* If things check out, mangle the kernel command-line as needed. */
+ if (result_should_continue(ret)) {
+ /* Fill in |ab_suffix| field. */
+ slot_data->ab_suffix = avb_strdup(ab_suffix);
+ if (slot_data->ab_suffix == NULL) {
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+ goto fail;
+ }
+
+ /* Add androidboot.vbmeta.device option. */
+ if (!cmdline_append_option(slot_data,
+ "androidboot.vbmeta.device",
+ "PARTUUID=$(ANDROID_VBMETA_PARTUUID)")) {
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+ goto fail;
+ }
+
+ /* Substitute $(ANDROID_SYSTEM_PARTUUID) and friends. */
+ if (slot_data->cmdline != NULL) {
+ char* new_cmdline = sub_cmdline(ops, slot_data->cmdline, ab_suffix);
+ if (new_cmdline == NULL) {
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+ goto fail;
+ }
+ avb_free(slot_data->cmdline);
+ slot_data->cmdline = new_cmdline;
+ }
+
+ /* Add androidboot.slot_suffix, if applicable. */
+ if (avb_strlen(ab_suffix) > 0) {
+ if (!cmdline_append_option(
+ slot_data, "androidboot.slot_suffix", ab_suffix)) {
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+ goto fail;
+ }
+ }
+
+ /* Set androidboot.avb.device_state to "locked" or "unlocked". */
+ bool is_device_unlocked;
+ io_ret = ops->read_is_device_unlocked(ops, &is_device_unlocked);
+ if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+ goto fail;
+ } else if (io_ret != AVB_IO_RESULT_OK) {
+ avb_error("Error getting device state.\n");
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO;
+ goto fail;
+ }
+ if (!cmdline_append_option(slot_data,
+ "androidboot.vbmeta.device_state",
+ is_device_unlocked ? "unlocked" : "locked")) {
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+ goto fail;
+ }
+
+ /* Set androidboot.vbmeta.{hash_alg, size, digest} - use same hash
+ * function as is used to sign vbmeta.
+ */
+ switch (algorithm_type) {
+ /* Explicit fallthrough. */
+ case AVB_ALGORITHM_TYPE_NONE:
+ case AVB_ALGORITHM_TYPE_SHA256_RSA2048:
+ case AVB_ALGORITHM_TYPE_SHA256_RSA4096:
+ case AVB_ALGORITHM_TYPE_SHA256_RSA8192: {
+ AvbSHA256Ctx ctx;
+ size_t n, total_size = 0;
+ avb_sha256_init(&ctx);
+ for (n = 0; n < slot_data->num_vbmeta_images; n++) {
+ avb_sha256_update(&ctx,
+ slot_data->vbmeta_images[n].vbmeta_data,
+ slot_data->vbmeta_images[n].vbmeta_size);
+ total_size += slot_data->vbmeta_images[n].vbmeta_size;
+ }
+ if (!cmdline_append_option(
+ slot_data, "androidboot.vbmeta.hash_alg", "sha256") ||
+ !cmdline_append_uint64_base10(
+ slot_data, "androidboot.vbmeta.size", total_size) ||
+ !cmdline_append_hex(slot_data,
+ "androidboot.vbmeta.digest",
+ avb_sha256_final(&ctx),
+ AVB_SHA256_DIGEST_SIZE)) {
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+ goto fail;
+ }
+ } break;
+ /* Explicit fallthrough. */
+ case AVB_ALGORITHM_TYPE_SHA512_RSA2048:
+ case AVB_ALGORITHM_TYPE_SHA512_RSA4096:
+ case AVB_ALGORITHM_TYPE_SHA512_RSA8192: {
+ AvbSHA512Ctx ctx;
+ size_t n, total_size = 0;
+ avb_sha512_init(&ctx);
+ for (n = 0; n < slot_data->num_vbmeta_images; n++) {
+ avb_sha512_update(&ctx,
+ slot_data->vbmeta_images[n].vbmeta_data,
+ slot_data->vbmeta_images[n].vbmeta_size);
+ total_size += slot_data->vbmeta_images[n].vbmeta_size;
+ }
+ if (!cmdline_append_option(
+ slot_data, "androidboot.vbmeta.hash_alg", "sha512") ||
+ !cmdline_append_uint64_base10(
+ slot_data, "androidboot.vbmeta.size", total_size) ||
+ !cmdline_append_hex(slot_data,
+ "androidboot.vbmeta.digest",
+ avb_sha512_final(&ctx),
+ AVB_SHA512_DIGEST_SIZE)) {
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+ goto fail;
+ }
+ } break;
+ case _AVB_ALGORITHM_NUM_TYPES:
+ avb_assert_not_reached();
+ break;
+ }
+
+ if (out_data != NULL) {
+ *out_data = slot_data;
+ } else {
+ avb_slot_verify_data_free(slot_data);
+ }
+ }
+
+ if (!allow_verification_error) {
+ avb_assert(ret == AVB_SLOT_VERIFY_RESULT_OK);
+ }
+
+ return ret;
+
+fail:
+ if (slot_data != NULL) {
+ avb_slot_verify_data_free(slot_data);
+ }
+ return ret;
+}
+
+void avb_slot_verify_data_free(AvbSlotVerifyData* data) {
+ if (data->ab_suffix != NULL) {
+ avb_free(data->ab_suffix);
+ }
+ if (data->cmdline != NULL) {
+ avb_free(data->cmdline);
+ }
+ if (data->vbmeta_images != NULL) {
+ size_t n;
+ for (n = 0; n < data->num_vbmeta_images; n++) {
+ AvbVBMetaData* vbmeta_image = &data->vbmeta_images[n];
+ if (vbmeta_image->partition_name != NULL) {
+ avb_free(vbmeta_image->partition_name);
+ }
+ if (vbmeta_image->vbmeta_data != NULL) {
+ avb_free(vbmeta_image->vbmeta_data);
+ }
+ }
+ avb_free(data->vbmeta_images);
+ }
+ if (data->loaded_partitions != NULL) {
+ size_t n;
+ for (n = 0; n < data->num_loaded_partitions; n++) {
+ AvbPartitionData* loaded_partition = &data->loaded_partitions[n];
+ if (loaded_partition->partition_name != NULL) {
+ avb_free(loaded_partition->partition_name);
+ }
+ if (loaded_partition->data != NULL) {
+ avb_free(loaded_partition->data);
+ }
+ }
+ avb_free(data->loaded_partitions);
+ }
+ avb_free(data);
+}
+
+const char* avb_slot_verify_result_to_string(AvbSlotVerifyResult result) {
+ const char* ret = NULL;
+
+ switch (result) {
+ case AVB_SLOT_VERIFY_RESULT_OK:
+ ret = "OK";
+ break;
+ case AVB_SLOT_VERIFY_RESULT_ERROR_OOM:
+ ret = "ERROR_OOM";
+ break;
+ case AVB_SLOT_VERIFY_RESULT_ERROR_IO:
+ ret = "ERROR_IO";
+ break;
+ case AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION:
+ ret = "ERROR_VERIFICATION";
+ break;
+ case AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX:
+ ret = "ERROR_ROLLBACK_INDEX";
+ break;
+ case AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED:
+ ret = "ERROR_PUBLIC_KEY_REJECTED";
+ break;
+ case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA:
+ ret = "ERROR_INVALID_METADATA";
+ break;
+ /* Do not add a 'default:' case here because of -Wswitch. */
+ }
+
+ if (ret == NULL) {
+ avb_error("Unknown AvbSlotVerifyResult value.\n");
+ ret = "(unknown)";
+ }
+
+ return ret;
+}
diff --git a/avb/libavb/avb_slot_verify.h b/avb/libavb/avb_slot_verify.h
new file mode 100644
index 0000000..93c12ba
--- /dev/null
+++ b/avb/libavb/avb_slot_verify.h
@@ -0,0 +1,242 @@
+/*
+ * 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(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION)
+#error "Never include this file directly, include libavb.h instead."
+#endif
+
+#ifndef AVB_SLOT_VERIFY_H_
+#define AVB_SLOT_VERIFY_H_
+
+#include "avb_ops.h"
+#include "avb_vbmeta_image.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Return codes used in avb_slot_verify(), see that function for
+ * documentation for each field.
+ *
+ * Use avb_slot_verify_result_to_string() to get a textual
+ * representation usable for error/debug output.
+ */
+typedef enum {
+ AVB_SLOT_VERIFY_RESULT_OK,
+ AVB_SLOT_VERIFY_RESULT_ERROR_OOM,
+ AVB_SLOT_VERIFY_RESULT_ERROR_IO,
+ AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION,
+ AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX,
+ AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED,
+ AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA
+} AvbSlotVerifyResult;
+
+/* Get a textual representation of |result|. */
+const char* avb_slot_verify_result_to_string(AvbSlotVerifyResult result);
+
+/* Maximum number of rollback index locations supported. */
+#define AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS 32
+
+/* AvbPartitionData contains data loaded from partitions when using
+ * avb_slot_verify(). The |partition_name| field contains the name of
+ * the partition (without A/B suffix), |data| points to the loaded
+ * data which is |data_size| bytes long.
+ *
+ * Note that this is strictly less than the partition size - it's only
+ * the image stored there, not the entire partition nor any of the
+ * metadata.
+ */
+typedef struct {
+ char* partition_name;
+ uint8_t* data;
+ size_t data_size;
+} AvbPartitionData;
+
+/* AvbVBMetaData contains a vbmeta struct loaded from a partition when
+ * using avb_slot_verify(). The |partition_name| field contains the
+ * name of the partition (without A/B suffix), |vbmeta_data| points to
+ * the loaded data which is |vbmeta_size| bytes long.
+ *
+ * The |verify_result| field contains the result of
+ * avb_vbmeta_image_verify() on the data. This is guaranteed to be
+ * AVB_VBMETA_VERIFY_RESULT_OK for all vbmeta images if
+ * avb_slot_verify() returns AVB_SLOT_VERIFY_RESULT_OK.
+ *
+ * You can use avb_descriptor_get_all(), avb_descriptor_foreach(), and
+ * avb_vbmeta_image_header_to_host_byte_order() with this data.
+ */
+typedef struct {
+ char* partition_name;
+ uint8_t* vbmeta_data;
+ size_t vbmeta_size;
+ AvbVBMetaVerifyResult verify_result;
+} AvbVBMetaData;
+
+/* AvbSlotVerifyData contains data needed to boot a particular slot
+ * and is returned by avb_slot_verify() if partitions in a slot are
+ * successfully verified.
+ *
+ * All data pointed to by this struct - including data in each item in
+ * the |partitions| array - will be freed when the
+ * avb_slot_verify_data_free() function is called.
+ *
+ * The |ab_suffix| field is the copy of the of |ab_suffix| field
+ * passed to avb_slot_verify(). It is the A/B suffix of the slot.
+ *
+ * The VBMeta images that were checked are available in the
+ * |vbmeta_images| field. The field |num_vbmeta_images| contains the
+ * number of elements in this array. The first element -
+ * vbmeta_images[0] - is guaranteed to be from the "vbmeta" partition
+ * in the requested slot.
+ *
+ * The partitions loaded and verified from from the slot are
+ * accessible in the |loaded_partitions| array. The field
+ * |num_loaded_partitions| contains the number of elements in this
+ * array. The order of partitions in this array may not necessarily be
+ * the same order as in the passed-in |requested_partitions| array.
+ *
+ * Rollback indexes for the verified slot are stored in the
+ * |rollback_indexes| field. Note that avb_slot_verify() will NEVER
+ * modify stored_rollback_index[n] locations e.g. it will never use
+ * the write_rollback_index() AvbOps operation. Instead it is the job
+ * of the caller of avb_slot_verify() to do this based on e.g. A/B
+ * policy and other factors. See libavb_ab/avb_ab_flow.c for an
+ * example of how to do this.
+ *
+ * The |cmdline| field is a NUL-terminated string in UTF-8 resulting
+ * from concatenating all |AvbKernelCmdlineDescriptor| and then
+ * performing proper substitution of the variables
+ * $(ANDROID_SYSTEM_PARTUUID), $(ANDROID_BOOT_PARTUUID), and
+ * $(ANDROID_VBMETA_PARTUUID) using the
+ * get_unique_guid_for_partition() operation in |AvbOps|.
+ *
+ * Additionally, the |cmdline| field will have the following kernel
+ * command-line options set:
+ *
+ * androidboot.vbmeta.device_state: set to "locked" or "unlocked"
+ * depending on the result of the result of AvbOps's
+ * read_is_unlocked() function.
+ *
+ * androidboot.slot_suffix: If |ab_suffix| as passed into
+ * avb_slot_verify() is non-empty, this variable will be set to its
+ * value.
+ *
+ * androidboot.vbmeta.{hash_alg, size, digest}: Will be set to
+ * the digest of all images in |vbmeta_images|.
+ *
+ * androidboot.vbmeta.device: This is set to the value
+ * PARTUUID=$(ANDROID_VBMETA_PARTUUID) before substitution so it
+ * will end up pointing to the vbmeta partition for the verified
+ * slot.
+ *
+ * This struct may grow in the future without it being considered an
+ * ABI break.
+ */
+typedef struct {
+ char* ab_suffix;
+ AvbVBMetaData* vbmeta_images;
+ size_t num_vbmeta_images;
+ AvbPartitionData* loaded_partitions;
+ size_t num_loaded_partitions;
+ char* cmdline;
+ uint64_t rollback_indexes[AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS];
+} AvbSlotVerifyData;
+
+/* Frees a |AvbSlotVerifyData| including all data it points to. */
+void avb_slot_verify_data_free(AvbSlotVerifyData* data);
+
+/* Performs a full verification of the slot identified by |ab_suffix|
+ * and load the contents of the partitions whose name is in the
+ * NULL-terminated string array |requested_partitions| (each partition
+ * must use hash verification). If not using A/B, pass an empty string
+ * (e.g. "", not NULL) for |ab_suffix|.
+ *
+ * Typically the |requested_partitions| array only contains a single
+ * item for the boot partition, 'boot'.
+ *
+ * Verification includes loading data from the 'vbmeta', all hash
+ * partitions, and possibly other partitions (with |ab_suffix|
+ * appended), inspecting rollback indexes, and checking if the public
+ * key used to sign the data is acceptable. The functions in |ops|
+ * will be used to do this.
+ *
+ * If |out_data| is not NULL, it will be set to a newly allocated
+ * |AvbSlotVerifyData| struct containing all the data needed to
+ * actually boot the slot. This data structure should be freed with
+ * avb_slot_verify_data_free() when you are done with it. See below
+ * for when this is returned.
+ *
+ * If |allow_verification_error| is false this function will bail out
+ * as soon as an error is encountered and |out_data| is set only if
+ * AVB_SLOT_VERIFY_RESULT_OK is returned.
+ *
+ * Otherwise if |allow_verification_error| is true the function will
+ * continue verification efforts and |out_data| is also set if
+ * AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED,
+ * AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION, or
+ * AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX is returned. It is
+ * undefined which error is returned if more than one distinct error
+ * is encountered. It is guaranteed that AVB_SLOT_VERIFY_RESULT_OK is
+ * returned if, and only if, there are no errors. This mode is needed
+ * to boot valid but unverified slots when the device is unlocked.
+ *
+ * Also note that |out_data| is never set if
+ * AVB_SLOT_VERIFY_RESULT_ERROR_OOM, AVB_SLOT_VERIFY_RESULT_ERROR_IO,
+ * or AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA is returned.
+ *
+ * AVB_SLOT_VERIFY_RESULT_OK is returned if everything is verified
+ * correctly and all public keys are accepted.
+ *
+ * AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED is returned if
+ * everything is verified correctly out but one or more public keys
+ * are not accepted. This includes the case where integrity data is
+ * not signed.
+ *
+ * AVB_SLOT_VERIFY_RESULT_ERROR_OOM is returned if unable to
+ * allocate memory.
+ *
+ * AVB_SLOT_VERIFY_RESULT_ERROR_IO is returned if an I/O error
+ * occurred while trying to load data or get a rollback index.
+ *
+ * AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION is returned if the data
+ * did not verify, e.g. the digest didn't match or signature checks
+ * failed.
+ *
+ * AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX is returned if a
+ * rollback index was less than its stored value.
+ *
+ * AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA is returned if some
+ * of the metadata is invalid or inconsistent.
+ */
+AvbSlotVerifyResult avb_slot_verify(AvbOps* ops,
+ const char* const* requested_partitions,
+ const char* ab_suffix,
+ bool allow_verification_error,
+ AvbSlotVerifyData** out_data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_SLOT_VERIFY_H_ */
diff --git a/avb/libavb/avb_sysdeps.h b/avb/libavb/avb_sysdeps.h
new file mode 100644
index 0000000..aea837a
--- /dev/null
+++ b/avb/libavb/avb_sysdeps.h
@@ -0,0 +1,118 @@
+/*
+ * 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(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION)
+#error "Never include this file directly, include libavb.h instead."
+#endif
+
+#ifndef AVB_SYSDEPS_H_
+#define AVB_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. At least things
+ * like uint8_t, uint64_t, and bool (with |false|, |true| keywords)
+ * must be present.
+ */
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+/* If you don't have gcc or clang, these attribute macros may need to
+ * be adjusted.
+ */
+#define AVB_ATTR_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
+#define AVB_ATTR_PACKED __attribute__((packed))
+#define AVB_ATTR_NO_RETURN __attribute__((noreturn))
+#define AVB_ATTR_SENTINEL __attribute__((__sentinel__))
+
+/* Size in bytes used for alignment. */
+#ifdef __LP64__
+#define AVB_ALIGNMENT_SIZE 8
+#else
+#define AVB_ALIGNMENT_SIZE 4
+#endif
+
+/* 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 avb_memcmp(const void* src1,
+ const void* src2,
+ size_t n) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Compare two strings.
+ *
+ * Return an integer less than, equal to, or greater than zero if |s1|
+ * is found, respectively, to be less than, to match, or be greater
+ * than |s2|.
+ */
+int avb_strcmp(const char* s1, const char* s2);
+
+/* Copy |n| bytes from |src| to |dest|. */
+void* avb_memcpy(void* dest, const void* src, size_t n);
+
+/* Set |n| bytes starting at |s| to |c|. Returns |dest|. */
+void* avb_memset(void* dest, const int c, size_t n);
+
+/* Prints out a message. The string passed must be a NUL-terminated
+ * UTF-8 string.
+ */
+void avb_print(const char* message);
+
+/* Prints out a vector of strings. Each argument must point to a
+ * NUL-terminated UTF-8 string and NULL should be the last argument.
+ */
+void avb_printv(const char* message, ...) AVB_ATTR_SENTINEL;
+
+/* Aborts the program or reboots the device. */
+void avb_abort(void) AVB_ATTR_NO_RETURN;
+
+/* 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 avb_free() when you are done with it.
+ */
+void* avb_malloc_(size_t size) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Frees memory previously allocated with avb_malloc(). */
+void avb_free(void* ptr);
+
+/* Returns the lenght of |str|, excluding the terminating NUL-byte. */
+size_t avb_strlen(const char* str) AVB_ATTR_WARN_UNUSED_RESULT;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_SYSDEPS_H_ */
diff --git a/avb/libavb/avb_sysdeps_posix.c b/avb/libavb/avb_sysdeps_posix.c
new file mode 100644
index 0000000..ea40d08
--- /dev/null
+++ b/avb/libavb/avb_sysdeps_posix.c
@@ -0,0 +1,78 @@
+/*
+ * 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 "avb_sysdeps.h"
+
+int avb_memcmp(const void* src1, const void* src2, size_t n) {
+ return memcmp(src1, src2, n);
+}
+
+void* avb_memcpy(void* dest, const void* src, size_t n) {
+ return memcpy(dest, src, n);
+}
+
+void* avb_memset(void* dest, const int c, size_t n) {
+ return memset(dest, c, n);
+}
+
+int avb_strcmp(const char* s1, const char* s2) {
+ return strcmp(s1, s2);
+}
+
+size_t avb_strlen(const char* str) {
+ return strlen(str);
+}
+
+void avb_abort(void) {
+ abort();
+}
+
+void avb_print(const char* message) {
+ fprintf(stderr, "%s", message);
+}
+
+void avb_printv(const char* message, ...) {
+ va_list ap;
+ const char* m;
+
+ va_start(ap, message);
+ for (m = message; m != NULL; m = va_arg(ap, const char*)) {
+ fprintf(stderr, "%s", m);
+ }
+ va_end(ap);
+}
+
+void* avb_malloc_(size_t size) {
+ return malloc(size);
+}
+
+void avb_free(void* ptr) {
+ free(ptr);
+}
diff --git a/avb/libavb/avb_util.c b/avb/libavb/avb_util.c
new file mode 100644
index 0000000..5a57b62
--- /dev/null
+++ b/avb/libavb/avb_util.c
@@ -0,0 +1,338 @@
+/*
+ * 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 "avb_util.h"
+
+uint32_t avb_be32toh(uint32_t in) {
+ uint8_t* d = (uint8_t*)∈
+ uint32_t ret;
+ ret = ((uint32_t)d[0]) << 24;
+ ret |= ((uint32_t)d[1]) << 16;
+ ret |= ((uint32_t)d[2]) << 8;
+ ret |= ((uint32_t)d[3]);
+ return ret;
+}
+
+uint64_t avb_be64toh(uint64_t in) {
+ uint8_t* d = (uint8_t*)∈
+ uint64_t ret;
+ ret = ((uint64_t)d[0]) << 56;
+ ret |= ((uint64_t)d[1]) << 48;
+ ret |= ((uint64_t)d[2]) << 40;
+ ret |= ((uint64_t)d[3]) << 32;
+ ret |= ((uint64_t)d[4]) << 24;
+ ret |= ((uint64_t)d[5]) << 16;
+ ret |= ((uint64_t)d[6]) << 8;
+ ret |= ((uint64_t)d[7]);
+ return ret;
+}
+
+/* Converts a 32-bit unsigned integer from host to big-endian byte order. */
+uint32_t avb_htobe32(uint32_t in) {
+ union {
+ uint32_t word;
+ uint8_t bytes[4];
+ } ret;
+ ret.bytes[0] = (in >> 24) & 0xff;
+ ret.bytes[1] = (in >> 16) & 0xff;
+ ret.bytes[2] = (in >> 8) & 0xff;
+ ret.bytes[3] = in & 0xff;
+ return ret.word;
+}
+
+/* Converts a 64-bit unsigned integer from host to big-endian byte order. */
+uint64_t avb_htobe64(uint64_t in) {
+ union {
+ uint64_t word;
+ uint8_t bytes[8];
+ } ret;
+ ret.bytes[0] = (in >> 56) & 0xff;
+ ret.bytes[1] = (in >> 48) & 0xff;
+ ret.bytes[2] = (in >> 40) & 0xff;
+ ret.bytes[3] = (in >> 32) & 0xff;
+ ret.bytes[4] = (in >> 24) & 0xff;
+ ret.bytes[5] = (in >> 16) & 0xff;
+ ret.bytes[6] = (in >> 8) & 0xff;
+ ret.bytes[7] = in & 0xff;
+ return ret.word;
+}
+
+int avb_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;
+}
+
+bool avb_safe_add_to(uint64_t* value, uint64_t value_to_add) {
+ uint64_t original_value;
+
+ avb_assert(value != NULL);
+
+ original_value = *value;
+
+ *value += value_to_add;
+ if (*value < original_value) {
+ avb_error("Overflow when adding values.\n");
+ return false;
+ }
+
+ return true;
+}
+
+bool avb_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 avb_safe_add_to(out_result, b);
+}
+
+bool avb_validate_utf8(const uint8_t* data, size_t num_bytes) {
+ size_t n;
+ unsigned int num_cc;
+
+ for (n = 0, num_cc = 0; n < num_bytes; n++) {
+ uint8_t c = data[n];
+
+ if (num_cc > 0) {
+ if ((c & (0x80 | 0x40)) == 0x80) {
+ /* 10xx xxxx */
+ } else {
+ goto fail;
+ }
+ num_cc--;
+ } else {
+ if (c < 0x80) {
+ num_cc = 0;
+ } else if ((c & (0x80 | 0x40 | 0x20)) == (0x80 | 0x40)) {
+ /* 110x xxxx */
+ num_cc = 1;
+ } else if ((c & (0x80 | 0x40 | 0x20 | 0x10)) == (0x80 | 0x40 | 0x20)) {
+ /* 1110 xxxx */
+ num_cc = 2;
+ } else if ((c & (0x80 | 0x40 | 0x20 | 0x10 | 0x08)) ==
+ (0x80 | 0x40 | 0x20 | 0x10)) {
+ /* 1111 0xxx */
+ num_cc = 3;
+ } else {
+ goto fail;
+ }
+ }
+ }
+
+ if (num_cc != 0) {
+ goto fail;
+ }
+
+ return true;
+
+fail:
+ return false;
+}
+
+bool avb_str_concat(char* buf,
+ size_t buf_size,
+ const char* str1,
+ size_t str1_len,
+ const char* str2,
+ size_t str2_len) {
+ uint64_t combined_len;
+
+ if (!avb_safe_add(&combined_len, str1_len, str2_len)) {
+ avb_error("Overflow when adding string sizes.\n");
+ return false;
+ }
+
+ if (combined_len > buf_size - 1) {
+ avb_error("Insufficient buffer space.\n");
+ return false;
+ }
+
+ avb_memcpy(buf, str1, str1_len);
+ avb_memcpy(buf + str1_len, str2, str2_len);
+ buf[combined_len] = '\0';
+
+ return true;
+}
+
+void* avb_malloc(size_t size) {
+ void* ret = avb_malloc_(size);
+ if (ret == NULL) {
+ avb_error("Failed to allocate memory.\n");
+ return NULL;
+ }
+ return ret;
+}
+
+void* avb_calloc(size_t size) {
+ void* ret = avb_malloc(size);
+ if (ret == NULL) {
+ return NULL;
+ }
+
+ avb_memset(ret, '\0', size);
+ return ret;
+}
+
+char* avb_strdup(const char* str) {
+ size_t len = avb_strlen(str);
+ char* ret = avb_malloc(len + 1);
+ if (ret == NULL) {
+ return NULL;
+ }
+
+ avb_memcpy(ret, str, len);
+ ret[len] = '\0';
+
+ return ret;
+}
+
+const char* avb_strstr(const char* haystack, const char* needle) {
+ size_t n, m;
+
+ /* Look through |haystack| and check if the first character of
+ * |needle| matches. If so, check the rest of |needle|.
+ */
+ for (n = 0; haystack[n] != '\0'; n++) {
+ if (haystack[n] != needle[0]) {
+ continue;
+ }
+
+ for (m = 1;; m++) {
+ if (needle[m] == '\0') {
+ return haystack + n;
+ }
+
+ if (haystack[n + m] != needle[m]) {
+ break;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+const char* avb_strv_find_str(const char* const* strings,
+ const char* str,
+ size_t str_size) {
+ size_t n;
+ for (n = 0; strings[n] != NULL; n++) {
+ if (avb_strlen(strings[n]) == str_size &&
+ avb_memcmp(strings[n], str, str_size) == 0) {
+ return strings[n];
+ }
+ }
+ return NULL;
+}
+
+char* avb_replace(const char* str, const char* search, const char* replace) {
+ char* ret = NULL;
+ size_t ret_len = 0;
+ size_t search_len, replace_len;
+ const char* str_after_last_replace;
+
+ search_len = avb_strlen(search);
+ replace_len = avb_strlen(replace);
+
+ str_after_last_replace = str;
+ while (*str != '\0') {
+ const char* s;
+ size_t num_before;
+ size_t num_new;
+
+ s = avb_strstr(str, search);
+ if (s == NULL) {
+ break;
+ }
+
+ num_before = s - str;
+
+ if (ret == NULL) {
+ num_new = num_before + replace_len + 1;
+ ret = avb_malloc(num_new);
+ if (ret == NULL) {
+ goto out;
+ }
+ avb_memcpy(ret, str, num_before);
+ avb_memcpy(ret + num_before, replace, replace_len);
+ ret[num_new - 1] = '\0';
+ ret_len = num_new - 1;
+ } else {
+ char* new_str;
+ num_new = ret_len + num_before + replace_len + 1;
+ new_str = avb_malloc(num_new);
+ if (ret == NULL) {
+ goto out;
+ }
+ avb_memcpy(new_str, ret, ret_len);
+ avb_memcpy(new_str + ret_len, str, num_before);
+ avb_memcpy(new_str + ret_len + num_before, replace, replace_len);
+ new_str[num_new - 1] = '\0';
+ avb_free(ret);
+ ret = new_str;
+ ret_len = num_new - 1;
+ }
+
+ str = s + search_len;
+ str_after_last_replace = str;
+ }
+
+ if (ret == NULL) {
+ ret = avb_strdup(str_after_last_replace);
+ if (ret == NULL) {
+ goto out;
+ }
+ } else {
+ size_t num_remaining = avb_strlen(str_after_last_replace);
+ size_t num_new = ret_len + num_remaining + 1;
+ char* new_str = avb_malloc(num_new);
+ if (ret == NULL) {
+ goto out;
+ }
+ avb_memcpy(new_str, ret, ret_len);
+ avb_memcpy(new_str + ret_len, str_after_last_replace, num_remaining);
+ new_str[num_new - 1] = '\0';
+ avb_free(ret);
+ ret = new_str;
+ ret_len = num_new - 1;
+ }
+
+out:
+ return ret;
+}
diff --git a/avb/libavb/avb_util.h b/avb/libavb/avb_util.h
new file mode 100644
index 0000000..352829a
--- /dev/null
+++ b/avb/libavb/avb_util.h
@@ -0,0 +1,240 @@
+/*
+ * 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(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION)
+#error "Never include this file directly, include libavb.h instead."
+#endif
+
+#ifndef AVB_UTIL_H_
+#define AVB_UTIL_H_
+
+#include "avb_sysdeps.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define AVB_STRINGIFY(x) #x
+#define AVB_TO_STRING(x) AVB_STRINGIFY(x)
+
+#ifdef AVB_ENABLE_DEBUG
+/* Aborts the program if |expr| is false.
+ *
+ * This has no effect unless AVB_ENABLE_DEBUG is defined.
+ */
+#define avb_assert(expr) \
+ do { \
+ if (!(expr)) { \
+ avb_fatal("assert fail: " #expr "\n"); \
+ } \
+ } while (0)
+#else
+#define avb_assert(expr)
+#endif
+
+/* Aborts the program if reached.
+ *
+ * This has no effect unless AVB_ENABLE_DEBUG is defined.
+ */
+#ifdef AVB_ENABLE_DEBUG
+#define avb_assert_not_reached() \
+ do { \
+ avb_fatal("assert_not_reached()\n"); \
+ } while (0)
+#else
+#define avb_assert_not_reached()
+#endif
+
+/* Aborts the program if |addr| is not word-aligned.
+ *
+ * This has no effect unless AVB_ENABLE_DEBUG is defined.
+ */
+#define avb_assert_aligned(addr) \
+ avb_assert((((uintptr_t)addr) & (AVB_ALIGNMENT_SIZE - 1)) == 0)
+
+#ifdef AVB_ENABLE_DEBUG
+/* Print functions, used for diagnostics.
+ *
+ * These have no effect unless AVB_ENABLE_DEBUG is defined.
+ */
+#define avb_debug(message) \
+ do { \
+ avb_print(__FILE__ ":" AVB_TO_STRING(__LINE__) ": DEBUG: "); \
+ avb_print(message); \
+ } while (0)
+#define avb_debugv(message, ...) \
+ do { \
+ avb_print(__FILE__ ":" AVB_TO_STRING(__LINE__) ": DEBUG: "); \
+ avb_printv(message, ##__VA_ARGS__); \
+ } while (0)
+#else
+#define avb_debug(message)
+#define avb_debugv(message, ...)
+#endif
+
+/* Prints out a message. This is typically used if a runtime-error
+ * occurs.
+ */
+#define avb_error(message) \
+ do { \
+ avb_print(__FILE__ ":" AVB_TO_STRING(__LINE__) ": ERROR: "); \
+ avb_print(message); \
+ } while (0)
+#define avb_errorv(message, ...) \
+ do { \
+ avb_print(__FILE__ ":" AVB_TO_STRING(__LINE__) ": ERROR: "); \
+ avb_printv(message, ##__VA_ARGS__); \
+ } while (0)
+
+/* Prints out a message and calls avb_abort().
+ */
+#define avb_fatal(message) \
+ do { \
+ avb_print(__FILE__ ":" AVB_TO_STRING(__LINE__) ": FATAL: "); \
+ avb_print(message); \
+ avb_abort(); \
+ } while (0)
+#define avb_fatalv(message, ...) \
+ do { \
+ avb_print(__FILE__ ":" AVB_TO_STRING(__LINE__) ": FATAL: "); \
+ avb_printv(message, ##__VA_ARGS__); \
+ avb_abort(); \
+ } while (0)
+
+/* Converts a 32-bit unsigned integer from big-endian to host byte order. */
+uint32_t avb_be32toh(uint32_t in) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Converts a 64-bit unsigned integer from big-endian to host byte order. */
+uint64_t avb_be64toh(uint64_t in) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Converts a 32-bit unsigned integer from host to big-endian byte order. */
+uint32_t avb_htobe32(uint32_t in) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Converts a 64-bit unsigned integer from host to big-endian byte order. */
+uint64_t avb_htobe64(uint64_t in) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* 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 avb_memcmp(), this only indicates inequality, not
+ * whether |s1| is less than or greater than |s2|.
+ */
+int avb_safe_memcmp(const void* s1,
+ const void* s2,
+ size_t n) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Adds |value_to_add| to |value| with overflow protection.
+ *
+ * Returns false if the addition overflows, true otherwise. In either
+ * case, |value| is always modified.
+ */
+bool avb_safe_add_to(uint64_t* value,
+ uint64_t value_to_add) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* 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 false if the addition overflows, true otherwise.
+ */
+bool avb_safe_add(uint64_t* out_result,
+ uint64_t a,
+ uint64_t b) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Checks if |num_bytes| data at |data| is a valid UTF-8
+ * string. Returns true if valid UTF-8, false otherwise.
+ */
+bool avb_validate_utf8(const uint8_t* data,
+ size_t num_bytes) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Concatenates |str1| (of |str1_len| bytes) and |str2| (of |str2_len|
+ * bytes) and puts the result in |buf| which holds |buf_size|
+ * bytes. The result is also guaranteed to be NUL terminated. Fail if
+ * there is not enough room in |buf| for the resulting string plus
+ * terminating NUL byte.
+ *
+ * Returns true if the operation succeeds, false otherwise.
+ */
+bool avb_str_concat(char* buf,
+ size_t buf_size,
+ const char* str1,
+ size_t str1_len,
+ const char* str2,
+ size_t str2_len);
+
+/* Like avb_malloc_() but prints a error using avb_error() if memory
+ * allocation fails.
+ */
+void* avb_malloc(size_t size) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Like avb_malloc() but sets the memory with zeroes. */
+void* avb_calloc(size_t size) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Duplicates a NUL-terminated string. Returns NULL on OOM. */
+char* avb_strdup(const char* str) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Finds the first occurrence of |needle| in the string |haystack|
+ * where both strings are NUL-terminated strings. The terminating NUL
+ * bytes are not compared.
+ *
+ * Returns NULL if not found, otherwise points into |haystack| for the
+ * first occurrence of |needle|.
+ */
+const char* avb_strstr(const char* haystack,
+ const char* needle) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Finds the first occurrence of |str| in the NULL-terminated string
+ * array |strings|. Each element in |strings| must be
+ * NUL-terminated. The string given by |str| need not be
+ * NUL-terminated but its size must be given in |str_size|.
+ *
+ * Returns NULL if not found, otherwise points into |strings| for the
+ * first occurrence of |str|.
+ */
+const char* avb_strv_find_str(const char* const* strings,
+ const char* str,
+ size_t str_size);
+
+/* Replaces all occurrences of |search| with |replace| in |str|.
+ *
+ * Returns a newly allocated string or NULL if out of memory.
+ */
+char* avb_replace(const char* str,
+ const char* search,
+ const char* replace) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Calculates the CRC-32 for data in |buf| of size |buf_size|. */
+uint32_t avb_crc32(const uint8_t* buf, size_t buf_size);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_UTIL_H_ */
diff --git a/avb/libavb/avb_vbmeta_image.c b/avb/libavb/avb_vbmeta_image.c
new file mode 100644
index 0000000..61b1eda
--- /dev/null
+++ b/avb/libavb/avb_vbmeta_image.c
@@ -0,0 +1,294 @@
+/*
+ * 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 "avb_vbmeta_image.h"
+#include "avb_crypto.h"
+#include "avb_rsa.h"
+#include "avb_sha.h"
+#include "avb_util.h"
+
+AvbVBMetaVerifyResult avb_vbmeta_image_verify(
+ const uint8_t* data,
+ size_t length,
+ const uint8_t** out_public_key_data,
+ size_t* out_public_key_length) {
+ AvbVBMetaVerifyResult ret;
+ AvbVBMetaImageHeader h;
+ uint8_t* computed_hash;
+ const AvbAlgorithmData* algorithm;
+ AvbSHA256Ctx sha256_ctx;
+ AvbSHA512Ctx sha512_ctx;
+ const uint8_t* header_block;
+ const uint8_t* authentication_block;
+ const uint8_t* auxiliary_block;
+ int verification_result;
+
+ ret = AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_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 (avb_safe_memcmp(data, AVB_MAGIC, AVB_MAGIC_LEN) != 0) {
+ avb_error("Magic is incorrect.\n");
+ goto out;
+ }
+
+ /* Before we byteswap, ensure length is long enough. */
+ if (length < sizeof(AvbVBMetaImageHeader)) {
+ avb_error("Length is smaller than header.\n");
+ goto out;
+ }
+ avb_vbmeta_image_header_to_host_byte_order((const AvbVBMetaImageHeader*)data,
+ &h);
+
+ /* Ensure we don't attempt to access any fields if the major version
+ * is not supported.
+ */
+ if (h.header_version_major > AVB_MAJOR_VERSION) {
+ avb_error("No support for given major version.\n");
+ goto out;
+ }
+
+ /* Ensure inner block sizes are multiple of 64. */
+ if ((h.authentication_data_block_size & 0x3f) != 0 ||
+ (h.auxiliary_data_block_size & 0x3f) != 0) {
+ avb_error("Block size is not a multiple of 64.\n");
+ goto out;
+ }
+
+ /* Ensure block sizes all add up to at most |length|. */
+ uint64_t block_total = sizeof(AvbVBMetaImageHeader);
+ if (!avb_safe_add_to(&block_total, h.authentication_data_block_size) ||
+ !avb_safe_add_to(&block_total, h.auxiliary_data_block_size)) {
+ avb_error("Overflow while computing size of boot image.\n");
+ goto out;
+ }
+ if (block_total > length) {
+ avb_error("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 (!avb_safe_add(NULL, (uint64_t)data_ptr, length)) {
+ avb_error("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 (!avb_safe_add(&hash_end, h.hash_offset, h.hash_size) ||
+ hash_end > h.authentication_data_block_size) {
+ avb_error("Hash is not entirely in its block.\n");
+ goto out;
+ }
+ uint64_t signature_end;
+ if (!avb_safe_add(&signature_end, h.signature_offset, h.signature_size) ||
+ signature_end > h.authentication_data_block_size) {
+ avb_error("Signature is not entirely in its block.\n");
+ goto out;
+ }
+
+ /* Ensure public key is entirely in the Auxiliary data block. */
+ uint64_t pubkey_end;
+ if (!avb_safe_add(&pubkey_end, h.public_key_offset, h.public_key_size) ||
+ pubkey_end > h.auxiliary_data_block_size) {
+ avb_error("Public key is not entirely in its block.\n");
+ goto out;
+ }
+
+ /* Ensure public key metadata (if set) is entirely in the Auxiliary
+ * data block. */
+ if (h.public_key_metadata_size > 0) {
+ uint64_t pubkey_md_end;
+ if (!avb_safe_add(&pubkey_md_end,
+ h.public_key_metadata_offset,
+ h.public_key_metadata_size) ||
+ pubkey_md_end > h.auxiliary_data_block_size) {
+ avb_error("Public key metadata is not entirely in its block.\n");
+ goto out;
+ }
+ }
+
+ /* Bail early if there's no hash or signature. */
+ if (h.algorithm_type == AVB_ALGORITHM_TYPE_NONE) {
+ ret = AVB_VBMETA_VERIFY_RESULT_OK_NOT_SIGNED;
+ goto out;
+ }
+
+ /* Ensure algorithm field is supported. */
+ algorithm = avb_get_algorithm_data(h.algorithm_type);
+ if (!algorithm) {
+ avb_error("Invalid or unknown algorithm.\n");
+ goto out;
+ }
+
+ /* Bail if the embedded hash size doesn't match the chosen algorithm. */
+ if (h.hash_size != algorithm->hash_len) {
+ avb_error("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(AvbVBMetaImageHeader);
+ auxiliary_block = authentication_block + h.authentication_data_block_size;
+
+ switch (h.algorithm_type) {
+ /* Explicit fall-through: */
+ case AVB_ALGORITHM_TYPE_SHA256_RSA2048:
+ case AVB_ALGORITHM_TYPE_SHA256_RSA4096:
+ case AVB_ALGORITHM_TYPE_SHA256_RSA8192:
+ avb_sha256_init(&sha256_ctx);
+ avb_sha256_update(
+ &sha256_ctx, header_block, sizeof(AvbVBMetaImageHeader));
+ avb_sha256_update(
+ &sha256_ctx, auxiliary_block, h.auxiliary_data_block_size);
+ computed_hash = avb_sha256_final(&sha256_ctx);
+ break;
+ /* Explicit fall-through: */
+ case AVB_ALGORITHM_TYPE_SHA512_RSA2048:
+ case AVB_ALGORITHM_TYPE_SHA512_RSA4096:
+ case AVB_ALGORITHM_TYPE_SHA512_RSA8192:
+ avb_sha512_init(&sha512_ctx);
+ avb_sha512_update(
+ &sha512_ctx, header_block, sizeof(AvbVBMetaImageHeader));
+ avb_sha512_update(
+ &sha512_ctx, auxiliary_block, h.auxiliary_data_block_size);
+ computed_hash = avb_sha512_final(&sha512_ctx);
+ break;
+ default:
+ avb_error("Unknown algorithm.\n");
+ goto out;
+ }
+
+ if (avb_safe_memcmp(authentication_block + h.hash_offset,
+ computed_hash,
+ h.hash_size) != 0) {
+ avb_error("Hash does not match!\n");
+ ret = AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH;
+ goto out;
+ }
+
+ verification_result =
+ avb_rsa_verify(auxiliary_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 = AVB_VBMETA_VERIFY_RESULT_SIGNATURE_MISMATCH;
+ goto out;
+ }
+
+ if (h.public_key_size > 0) {
+ if (out_public_key_data != NULL) {
+ *out_public_key_data = auxiliary_block + h.public_key_offset;
+ }
+ if (out_public_key_length != NULL) {
+ *out_public_key_length = h.public_key_size;
+ }
+ }
+
+ ret = AVB_VBMETA_VERIFY_RESULT_OK;
+
+out:
+ return ret;
+}
+
+void avb_vbmeta_image_header_to_host_byte_order(const AvbVBMetaImageHeader* src,
+ AvbVBMetaImageHeader* dest) {
+ avb_memcpy(dest, src, sizeof(AvbVBMetaImageHeader));
+
+ dest->header_version_major = avb_be32toh(dest->header_version_major);
+ dest->header_version_minor = avb_be32toh(dest->header_version_minor);
+
+ dest->authentication_data_block_size =
+ avb_be64toh(dest->authentication_data_block_size);
+ dest->auxiliary_data_block_size =
+ avb_be64toh(dest->auxiliary_data_block_size);
+
+ dest->algorithm_type = avb_be32toh(dest->algorithm_type);
+
+ dest->hash_offset = avb_be64toh(dest->hash_offset);
+ dest->hash_size = avb_be64toh(dest->hash_size);
+
+ dest->signature_offset = avb_be64toh(dest->signature_offset);
+ dest->signature_size = avb_be64toh(dest->signature_size);
+
+ dest->public_key_offset = avb_be64toh(dest->public_key_offset);
+ dest->public_key_size = avb_be64toh(dest->public_key_size);
+
+ dest->public_key_metadata_offset =
+ avb_be64toh(dest->public_key_metadata_offset);
+ dest->public_key_metadata_size = avb_be64toh(dest->public_key_metadata_size);
+
+ dest->descriptors_offset = avb_be64toh(dest->descriptors_offset);
+ dest->descriptors_size = avb_be64toh(dest->descriptors_size);
+
+ dest->rollback_index = avb_be64toh(dest->rollback_index);
+ dest->flags = avb_be32toh(dest->flags);
+}
+
+const char* avb_vbmeta_verify_result_to_string(AvbVBMetaVerifyResult result) {
+ const char* ret = NULL;
+
+ switch (result) {
+ case AVB_VBMETA_VERIFY_RESULT_OK:
+ ret = "OK";
+ break;
+ case AVB_VBMETA_VERIFY_RESULT_OK_NOT_SIGNED:
+ ret = "OK_NOT_SIGNED";
+ break;
+ case AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER:
+ ret = "INVALID_VBMETA_HEADER";
+ break;
+ case AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH:
+ ret = "HASH_MISMATCH";
+ break;
+ case AVB_VBMETA_VERIFY_RESULT_SIGNATURE_MISMATCH:
+ ret = "SIGNATURE_MISMATCH";
+ break;
+ /* Do not add a 'default:' case here because of -Wswitch. */
+ }
+
+ if (ret == NULL) {
+ avb_error("Unknown AvbVBMetaVerifyResult value.\n");
+ ret = "(unknown)";
+ }
+
+ return ret;
+}
diff --git a/avb/libavb/avb_vbmeta_image.h b/avb/libavb/avb_vbmeta_image.h
new file mode 100644
index 0000000..832773a
--- /dev/null
+++ b/avb/libavb/avb_vbmeta_image.h
@@ -0,0 +1,267 @@
+/*
+ * 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(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION)
+#error "Never include this file directly, include libavb.h instead."
+#endif
+
+#ifndef AVB_VBMETA_IMAGE_H_
+#define AVB_VBMETA_IMAGE_H_
+
+#include "avb_sysdeps.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "avb_crypto.h"
+#include "avb_descriptor.h"
+
+/* Size of the vbmeta image header. */
+#define AVB_VBMETA_IMAGE_HEADER_SIZE 256
+
+/* Magic for the vbmeta image header. */
+#define AVB_MAGIC "AVB0"
+#define AVB_MAGIC_LEN 4
+
+/* The current MAJOR and MINOR versions used - keep in sync with avbtool. */
+#define AVB_MAJOR_VERSION 1
+#define AVB_MINOR_VERSION 0
+
+/* Flags for the vbmeta image.
+ *
+ * AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED: If this flag is set,
+ * hashtree image verification will be disabled.
+ */
+typedef enum {
+ AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED = (1 << 0)
+} AvbVBMetaImageFlags;
+
+/* Binary format for header of the vbmeta image.
+ *
+ * The vbmeta image consists of three blocks:
+ *
+ * +-----------------------------------------+
+ * | Header data - fixed size |
+ * +-----------------------------------------+
+ * | Authentication data - variable size |
+ * +-----------------------------------------+
+ * | Auxiliary data - variable size |
+ * +-----------------------------------------+
+ *
+ * The "Header data" block is described by this struct and is always
+ * |AVB_VBMETA_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 vbmeta image. The type of the hash and signature is defined by
+ * the |algorithm_type| field.
+ *
+ * The "Auxiliary data" is |auxiliary_data_block_size| bytes long and
+ * contains the auxiliary data including the public key used to make
+ * the signature and descriptors.
+ *
+ * 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 |AvbRSAPublicKeyHeader| struct.
+ *
+ * The descriptors starts at |descriptors_offset| from the beginning
+ * of the "Auxiliary Data" block and take up |descriptors_size|
+ * bytes. Each descriptor is stored as a |AvbDescriptor| with tag and
+ * number of bytes following. The number of descriptors can be
+ * determined by walking this data until |descriptors_size| is
+ * exhausted.
+ *
+ * The size of each of the "Authentication data" and "Auxiliary data"
+ * blocks must be divisible by 64. This is to ensure proper alignment.
+ *
+ * Descriptors are free-form blocks stored in a part of the vbmeta
+ * image subject to the same integrity checks as the rest of the
+ * image. See the documentation for |AvbDescriptor| for well-known
+ * descriptors. See avb_descriptor_foreach() for a convenience
+ * function to iterate over descriptors.
+ *
+ * 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 avb_vbmeta_image_header_to_host_byte_order().
+ *
+ * Before reading and/or using any of this data, you MUST verify it
+ * using avb_vbmeta_image_verify() and reject it unless it's signed by
+ * a known good public key.
+ */
+typedef struct AvbVBMetaImageHeader {
+ /* 0: Four bytes equal to "AVB0" (AVB_MAGIC). */
+ uint8_t magic[AVB_MAGIC_LEN];
+ /* 4: The major version of the vbmeta image header. */
+ uint32_t header_version_major;
+ /* 8: The minor version of the vbmeta 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 auxiliary data block. */
+ uint64_t auxiliary_data_block_size;
+
+ /* 28: The verification algorithm used, see |AvbAlgorithmType| enum. */
+ uint32_t algorithm_type;
+
+ /* 32: Offset into the "Authentication data" block of hash data. */
+ uint64_t hash_offset;
+ /* 40: Length of the hash data. */
+ uint64_t hash_size;
+
+ /* 48: Offset into the "Authentication data" block of signature data. */
+ uint64_t signature_offset;
+ /* 56: Length of the signature data. */
+ uint64_t signature_size;
+
+ /* 64: Offset into the "Auxiliary data" block of public key data. */
+ uint64_t public_key_offset;
+ /* 72: Length of the public key data. */
+ uint64_t public_key_size;
+
+ /* 80: Offset into the "Auxiliary data" block of public key metadata. */
+ uint64_t public_key_metadata_offset;
+ /* 88: Length of the public key metadata. Must be set to zero if there
+ * is no public key metadata.
+ */
+ uint64_t public_key_metadata_size;
+
+ /* 96: Offset into the "Auxiliary data" block of descriptor data. */
+ uint64_t descriptors_offset;
+ /* 104: Length of descriptor data. */
+ uint64_t descriptors_size;
+
+ /* 112: The rollback index which can be used to prevent rollback to
+ * older versions.
+ */
+ uint64_t rollback_index;
+
+ /* 120: Flags from the AvbVBMetaImageFlags enumeration. This must be
+ * set to zero if the vbmeta image is not a top-level image.
+ */
+ uint32_t flags;
+
+ /* 124: Padding to ensure struct is size AVB_VBMETA_IMAGE_HEADER_SIZE
+ * bytes. This must be set to zeroes.
+ */
+ uint8_t reserved[132];
+} AVB_ATTR_PACKED AvbVBMetaImageHeader;
+
+/* Copies |src| to |dest|, byte-swapping fields in the process.
+ *
+ * Make sure you've verified |src| using avb_vbmeta_image_verify()
+ * before accessing the data and/or using this function.
+ */
+void avb_vbmeta_image_header_to_host_byte_order(const AvbVBMetaImageHeader* src,
+ AvbVBMetaImageHeader* dest);
+
+/* Return codes used in avb_vbmeta_image_verify().
+ *
+ * AVB_VBMETA_VERIFY_RESULT_OK is returned if the vbmeta 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 avb_vbmeta_image_verify() for details.
+ *
+ * AVB_VBMETA_VERIFY_RESULT_OK_NOT_SIGNED is returned if the vbmeta
+ * image header is valid but there is no signature or hash.
+ *
+ * AVB_VERIFY_INVALID_VBMETA_HEADER is returned if the header of
+ * the vbmeta image is invalid, for example, invalid magic or
+ * inconsistent data.
+ *
+ * AVB_VERIFY_HASH_MISMATCH is returned if the hash stored in the
+ * "Authentication data" block does not match the calculated hash.
+ *
+ * AVB_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 vbmeta image.
+ */
+typedef enum {
+ AVB_VBMETA_VERIFY_RESULT_OK,
+ AVB_VBMETA_VERIFY_RESULT_OK_NOT_SIGNED,
+ AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER,
+ AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH,
+ AVB_VBMETA_VERIFY_RESULT_SIGNATURE_MISMATCH,
+} AvbVBMetaVerifyResult;
+
+/* Get a textual representation of |result|. */
+const char* avb_vbmeta_verify_result_to_string(AvbVBMetaVerifyResult result);
+
+/* Checks that vbmeta image at |data| of size |length| is a valid
+ * vbmeta image. The complete contents of the vbmeta 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 'vbmeta_a' or 'vbmeta_b' partition and pass in its length (for
+ * example, 1 MiB).
+ *
+ * See the |AvbVBMetaImageHeader| struct for information about the
+ * three blocks (header, authentication, auxiliary) that make up a
+ * vbmeta image.
+ *
+ * If the function returns |AVB_VBMETA_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. If there is no public key in the metadata then
+ * |out_public_key_data| is set to NULL.
+ *
+ * See the |AvbVBMetaVerifyResult| enum for possible return values.
+ *
+ * VERY IMPORTANT:
+ *
+ * 1. Even if |AVB_VBMETA_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 'avbtool extract_public_key'
+ * to extract the key (at build time, then store it along your
+ * code) 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 vbmeta 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.
+ *
+ * This is a low-level function to only verify the vbmeta data - you
+ * are likely looking for avb_slot_verify() instead for verifying
+ * integrity data for a whole set of partitions.
+ */
+AvbVBMetaVerifyResult avb_vbmeta_image_verify(
+ const uint8_t* data,
+ size_t length,
+ const uint8_t** out_public_key_data,
+ size_t* out_public_key_length) AVB_ATTR_WARN_UNUSED_RESULT;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_VBMETA_IMAGE_H_ */
diff --git a/avb/libavb/libavb.h b/avb/libavb/libavb.h
new file mode 100644
index 0000000..b74307c
--- /dev/null
+++ b/avb/libavb/libavb.h
@@ -0,0 +1,49 @@
+/*
+ * 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 LIBAVB_H_
+#define LIBAVB_H_
+
+/* The AVB_INSIDE_LIBAVB_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 AVB_INSIDE_LIBAVB_H
+#include "avb_chain_partition_descriptor.h"
+#include "avb_crypto.h"
+#include "avb_descriptor.h"
+#include "avb_footer.h"
+#include "avb_hash_descriptor.h"
+#include "avb_hashtree_descriptor.h"
+#include "avb_kernel_cmdline_descriptor.h"
+#include "avb_ops.h"
+#include "avb_property_descriptor.h"
+#include "avb_slot_verify.h"
+#include "avb_sysdeps.h"
+#include "avb_util.h"
+#include "avb_vbmeta_image.h"
+#undef AVB_INSIDE_LIBAVB_H
+
+#endif /* LIBAVB_H_ */
diff --git a/avb/libavb_ab/avb_ab_flow.c b/avb/libavb_ab/avb_ab_flow.c
new file mode 100644
index 0000000..006e617
--- /dev/null
+++ b/avb/libavb_ab/avb_ab_flow.c
@@ -0,0 +1,515 @@
+/*
+ * 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 "avb_ab_flow.h"
+
+bool avb_ab_data_verify_and_byteswap(const AvbABData* src, AvbABData* dest) {
+ /* Ensure magic is correct. */
+ if (avb_safe_memcmp(src->magic, AVB_AB_MAGIC, AVB_AB_MAGIC_LEN) != 0) {
+ avb_error("Magic is incorrect.\n");
+ return false;
+ }
+
+ avb_memcpy(dest, src, sizeof(AvbABData));
+ dest->crc32 = avb_be32toh(dest->crc32);
+
+ /* Ensure we don't attempt to access any fields if the major version
+ * is not supported.
+ */
+ if (dest->version_major > AVB_AB_MAJOR_VERSION) {
+ avb_error("No support for given major version.\n");
+ return false;
+ }
+
+ /* Bail if CRC32 doesn't match. */
+ if (dest->crc32 !=
+ avb_crc32((const uint8_t*)dest, sizeof(AvbABData) - sizeof(uint32_t))) {
+ avb_error("CRC32 does not match.\n");
+ return false;
+ }
+
+ return true;
+}
+
+void avb_ab_data_update_crc_and_byteswap(const AvbABData* src,
+ AvbABData* dest) {
+ avb_memcpy(dest, src, sizeof(AvbABData));
+ dest->crc32 = avb_htobe32(
+ avb_crc32((const uint8_t*)dest, sizeof(AvbABData) - sizeof(uint32_t)));
+}
+
+void avb_ab_data_init(AvbABData* data) {
+ avb_memset(data, '\0', sizeof(AvbABData));
+ avb_memcpy(data->magic, AVB_AB_MAGIC, AVB_AB_MAGIC_LEN);
+ data->version_major = AVB_AB_MAJOR_VERSION;
+ data->version_minor = AVB_AB_MINOR_VERSION;
+ data->slots[0].priority = AVB_AB_MAX_PRIORITY;
+ data->slots[0].tries_remaining = AVB_AB_MAX_TRIES_REMAINING;
+ data->slots[0].successful_boot = 0;
+ data->slots[1].priority = AVB_AB_MAX_PRIORITY - 1;
+ data->slots[1].tries_remaining = AVB_AB_MAX_TRIES_REMAINING;
+ data->slots[1].successful_boot = 0;
+}
+
+/* The AvbABData struct is stored 2048 bytes into the 'misc' partition
+ * following the 'struct bootloader_message' field. The struct is
+ * compatible with the guidelines in bootable/recovery/bootloader.h -
+ * e.g. it is stored in the |slot_suffix| field, starts with a
+ * NUL-byte, and is 32 bytes long.
+ */
+#define AB_METADATA_MISC_PARTITION_OFFSET 2048
+
+AvbIOResult avb_ab_data_read(AvbABOps* ab_ops, AvbABData* data) {
+ AvbOps* ops = ab_ops->ops;
+ AvbABData serialized;
+ AvbIOResult io_ret;
+ size_t num_bytes_read;
+
+ io_ret = ops->read_from_partition(ops,
+ "misc",
+ AB_METADATA_MISC_PARTITION_OFFSET,
+ sizeof(AvbABData),
+ &serialized,
+ &num_bytes_read);
+ if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
+ return AVB_IO_RESULT_ERROR_OOM;
+ } else if (io_ret != AVB_IO_RESULT_OK ||
+ num_bytes_read != sizeof(AvbABData)) {
+ avb_error("Error reading A/B metadata.\n");
+ return AVB_IO_RESULT_ERROR_IO;
+ }
+
+ if (!avb_ab_data_verify_and_byteswap(&serialized, data)) {
+ avb_error(
+ "Error validating A/B metadata from disk. "
+ "Resetting and writing new A/B metadata to disk.\n");
+ avb_ab_data_init(data);
+ return avb_ab_data_write(ab_ops, data);
+ }
+
+ return AVB_IO_RESULT_OK;
+}
+
+AvbIOResult avb_ab_data_write(AvbABOps* ab_ops, const AvbABData* data) {
+ AvbOps* ops = ab_ops->ops;
+ AvbABData serialized;
+ AvbIOResult io_ret;
+
+ avb_ab_data_update_crc_and_byteswap(data, &serialized);
+ io_ret = ops->write_to_partition(ops,
+ "misc",
+ AB_METADATA_MISC_PARTITION_OFFSET,
+ sizeof(AvbABData),
+ &serialized);
+ if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
+ return AVB_IO_RESULT_ERROR_OOM;
+ } else if (io_ret != AVB_IO_RESULT_OK) {
+ avb_error("Error writing A/B metadata.\n");
+ return AVB_IO_RESULT_ERROR_IO;
+ }
+ return AVB_IO_RESULT_OK;
+}
+
+static bool slot_is_bootable(AvbABSlotData* slot) {
+ return slot->priority > 0 &&
+ (slot->successful_boot || (slot->tries_remaining > 0));
+}
+
+static void slot_set_unbootable(AvbABSlotData* slot) {
+ slot->priority = 0;
+ slot->tries_remaining = 0;
+ slot->successful_boot = 0;
+}
+
+/* Ensure all unbootable and/or illegal states are marked as the
+ * canonical 'unbootable' state, e.g. priority=0, tries_remaining=0,
+ * and successful_boot=0.
+ */
+static void slot_normalize(AvbABSlotData* slot) {
+ if (slot->priority > 0) {
+ if (slot->tries_remaining == 0 && !slot->successful_boot) {
+ /* We've exhausted all tries -> unbootable. */
+ slot_set_unbootable(slot);
+ }
+ if (slot->tries_remaining > 0 && slot->successful_boot) {
+ /* Illegal state - avb_ab_mark_slot_successful() will clear
+ * tries_remaining when setting successful_boot.
+ */
+ slot_set_unbootable(slot);
+ }
+ } else {
+ slot_set_unbootable(slot);
+ }
+}
+
+static const char* slot_suffixes[2] = {"_a", "_b"};
+
+/* Helper function to load metadata - returns AVB_IO_RESULT_OK on
+ * success, error code otherwise.
+ */
+static AvbIOResult load_metadata(AvbABOps* ab_ops,
+ AvbABData* ab_data,
+ AvbABData* ab_data_orig) {
+ AvbIOResult io_ret;
+
+ io_ret = ab_ops->read_ab_metadata(ab_ops, ab_data);
+ if (io_ret != AVB_IO_RESULT_OK) {
+ avb_error("I/O error while loading A/B metadata.\n");
+ return io_ret;
+ }
+ *ab_data_orig = *ab_data;
+
+ /* Ensure data is normalized, e.g. illegal states will be marked as
+ * unbootable and all unbootable states are represented with
+ * (priority=0, tries_remaining=0, successful_boot=0).
+ */
+ slot_normalize(&ab_data->slots[0]);
+ slot_normalize(&ab_data->slots[1]);
+ return AVB_IO_RESULT_OK;
+}
+
+/* Writes A/B metadata to disk only if it has changed - returns
+ * AVB_IO_RESULT_OK on success, error code otherwise.
+ */
+static AvbIOResult save_metadata_if_changed(AvbABOps* ab_ops,
+ AvbABData* ab_data,
+ AvbABData* ab_data_orig) {
+ if (avb_safe_memcmp(ab_data, ab_data_orig, sizeof(AvbABData)) != 0) {
+ avb_debug("Writing A/B metadata to disk.\n");
+ return ab_ops->write_ab_metadata(ab_ops, ab_data);
+ }
+ return AVB_IO_RESULT_OK;
+}
+
+AvbABFlowResult avb_ab_flow(AvbABOps* ab_ops,
+ const char* const* requested_partitions,
+ bool allow_verification_error,
+ AvbSlotVerifyData** out_data) {
+ AvbOps* ops = ab_ops->ops;
+ AvbSlotVerifyData* slot_data[2] = {NULL, NULL};
+ AvbSlotVerifyData* data = NULL;
+ AvbABFlowResult ret;
+ AvbABData ab_data, ab_data_orig;
+ size_t slot_index_to_boot, n;
+ AvbIOResult io_ret;
+ bool saw_and_allowed_verification_error = false;
+
+ io_ret = load_metadata(ab_ops, &ab_data, &ab_data_orig);
+ if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
+ ret = AVB_AB_FLOW_RESULT_ERROR_OOM;
+ goto out;
+ } else if (io_ret != AVB_IO_RESULT_OK) {
+ ret = AVB_AB_FLOW_RESULT_ERROR_IO;
+ goto out;
+ }
+
+ /* Validate all bootable slots. */
+ for (n = 0; n < 2; n++) {
+ if (slot_is_bootable(&ab_data.slots[n])) {
+ AvbSlotVerifyResult verify_result;
+ bool set_slot_unbootable = false;
+
+ verify_result = avb_slot_verify(ops,
+ requested_partitions,
+ slot_suffixes[n],
+ allow_verification_error,
+ &slot_data[n]);
+ switch (verify_result) {
+ case AVB_SLOT_VERIFY_RESULT_ERROR_OOM:
+ ret = AVB_AB_FLOW_RESULT_ERROR_OOM;
+ goto out;
+
+ case AVB_SLOT_VERIFY_RESULT_ERROR_IO:
+ ret = AVB_AB_FLOW_RESULT_ERROR_IO;
+ goto out;
+
+ case AVB_SLOT_VERIFY_RESULT_OK:
+ break;
+
+ case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA:
+ /* Even with |allow_verification_error| this is game over. */
+ set_slot_unbootable = true;
+ break;
+
+ /* explicit fallthrough. */
+ case AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION:
+ case AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX:
+ case AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED:
+ if (allow_verification_error) {
+ /* Do nothing since we allow this. */
+ avb_debugv("Allowing slot ",
+ slot_suffixes[n],
+ " which verified "
+ "with result ",
+ avb_slot_verify_result_to_string(verify_result),
+ " because |allow_verification_error| is true.\n",
+ NULL);
+ saw_and_allowed_verification_error = true;
+ } else {
+ set_slot_unbootable = true;
+ }
+ break;
+ }
+
+ if (set_slot_unbootable) {
+ avb_errorv("Error verifying slot ",
+ slot_suffixes[n],
+ " with result ",
+ avb_slot_verify_result_to_string(verify_result),
+ " - setting unbootable.\n",
+ NULL);
+ slot_set_unbootable(&ab_data.slots[n]);
+ }
+ }
+ }
+
+ if (slot_is_bootable(&ab_data.slots[0]) &&
+ slot_is_bootable(&ab_data.slots[1])) {
+ if (ab_data.slots[1].priority > ab_data.slots[0].priority) {
+ slot_index_to_boot = 1;
+ } else {
+ slot_index_to_boot = 0;
+ }
+ } else if (slot_is_bootable(&ab_data.slots[0])) {
+ slot_index_to_boot = 0;
+ } else if (slot_is_bootable(&ab_data.slots[1])) {
+ slot_index_to_boot = 1;
+ } else {
+ /* No bootable slots! */
+ avb_error("No bootable slots found.\n");
+ ret = AVB_AB_FLOW_RESULT_ERROR_NO_BOOTABLE_SLOTS;
+ goto out;
+ }
+
+ /* Update stored rollback index such that the stored rollback index
+ * is the largest value supporting all currently bootable slots. Do
+ * this for every rollback index location.
+ */
+ for (n = 0; n < AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS; n++) {
+ uint64_t rollback_index_value = 0;
+
+ if (slot_data[0] != NULL && slot_data[1] != NULL) {
+ uint64_t a_rollback_index = slot_data[0]->rollback_indexes[n];
+ uint64_t b_rollback_index = slot_data[1]->rollback_indexes[n];
+ rollback_index_value =
+ (a_rollback_index < b_rollback_index ? a_rollback_index
+ : b_rollback_index);
+ } else if (slot_data[0] != NULL) {
+ rollback_index_value = slot_data[0]->rollback_indexes[n];
+ } else if (slot_data[1] != NULL) {
+ rollback_index_value = slot_data[1]->rollback_indexes[n];
+ }
+
+ if (rollback_index_value != 0) {
+ uint64_t current_rollback_index_value;
+ io_ret = ops->read_rollback_index(ops, n, ¤t_rollback_index_value);
+ if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
+ ret = AVB_AB_FLOW_RESULT_ERROR_OOM;
+ goto out;
+ } else if (io_ret != AVB_IO_RESULT_OK) {
+ avb_error("Error getting rollback index for slot.\n");
+ ret = AVB_AB_FLOW_RESULT_ERROR_IO;
+ goto out;
+ }
+ if (current_rollback_index_value != rollback_index_value) {
+ io_ret = ops->write_rollback_index(ops, n, rollback_index_value);
+ if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
+ ret = AVB_AB_FLOW_RESULT_ERROR_OOM;
+ goto out;
+ } else if (io_ret != AVB_IO_RESULT_OK) {
+ avb_error("Error setting stored rollback index.\n");
+ ret = AVB_AB_FLOW_RESULT_ERROR_IO;
+ goto out;
+ }
+ }
+ }
+ }
+
+ /* Finally, select this slot. */
+ avb_assert(slot_data[slot_index_to_boot] != NULL);
+ data = slot_data[slot_index_to_boot];
+ slot_data[slot_index_to_boot] = NULL;
+ if (saw_and_allowed_verification_error) {
+ avb_assert(allow_verification_error);
+ ret = AVB_AB_FLOW_RESULT_OK_WITH_VERIFICATION_ERROR;
+ } else {
+ ret = AVB_AB_FLOW_RESULT_OK;
+ }
+
+ /* ... and decrement tries remaining, if applicable. */
+ if (!ab_data.slots[slot_index_to_boot].successful_boot &&
+ ab_data.slots[slot_index_to_boot].tries_remaining > 0) {
+ ab_data.slots[slot_index_to_boot].tries_remaining -= 1;
+ }
+
+out:
+ io_ret = save_metadata_if_changed(ab_ops, &ab_data, &ab_data_orig);
+ if (io_ret != AVB_IO_RESULT_OK) {
+ if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
+ ret = AVB_AB_FLOW_RESULT_ERROR_OOM;
+ } else {
+ ret = AVB_AB_FLOW_RESULT_ERROR_IO;
+ }
+ if (data != NULL) {
+ avb_slot_verify_data_free(data);
+ data = NULL;
+ }
+ }
+
+ for (n = 0; n < 2; n++) {
+ if (slot_data[n] != NULL) {
+ avb_slot_verify_data_free(slot_data[n]);
+ }
+ }
+
+ if (out_data != NULL) {
+ *out_data = data;
+ } else {
+ if (data != NULL) {
+ avb_slot_verify_data_free(data);
+ }
+ }
+
+ return ret;
+}
+
+AvbIOResult avb_ab_mark_slot_active(AvbABOps* ab_ops,
+ unsigned int slot_number) {
+ AvbABData ab_data, ab_data_orig;
+ unsigned int other_slot_number;
+ AvbIOResult ret;
+
+ avb_assert(slot_number < 2);
+
+ ret = load_metadata(ab_ops, &ab_data, &ab_data_orig);
+ if (ret != AVB_IO_RESULT_OK) {
+ goto out;
+ }
+
+ /* Make requested slot top priority, unsuccessful, and with max tries. */
+ ab_data.slots[slot_number].priority = AVB_AB_MAX_PRIORITY;
+ ab_data.slots[slot_number].tries_remaining = AVB_AB_MAX_TRIES_REMAINING;
+ ab_data.slots[slot_number].successful_boot = 0;
+
+ /* Ensure other slot doesn't have as high a priority. */
+ other_slot_number = 1 - slot_number;
+ if (ab_data.slots[other_slot_number].priority == AVB_AB_MAX_PRIORITY) {
+ ab_data.slots[other_slot_number].priority = AVB_AB_MAX_PRIORITY - 1;
+ }
+
+ ret = AVB_IO_RESULT_OK;
+
+out:
+ if (ret == AVB_IO_RESULT_OK) {
+ ret = save_metadata_if_changed(ab_ops, &ab_data, &ab_data_orig);
+ }
+ return ret;
+}
+
+AvbIOResult avb_ab_mark_slot_unbootable(AvbABOps* ab_ops,
+ unsigned int slot_number) {
+ AvbABData ab_data, ab_data_orig;
+ AvbIOResult ret;
+
+ avb_assert(slot_number < 2);
+
+ ret = load_metadata(ab_ops, &ab_data, &ab_data_orig);
+ if (ret != AVB_IO_RESULT_OK) {
+ goto out;
+ }
+
+ slot_set_unbootable(&ab_data.slots[slot_number]);
+
+ ret = AVB_IO_RESULT_OK;
+
+out:
+ if (ret == AVB_IO_RESULT_OK) {
+ ret = save_metadata_if_changed(ab_ops, &ab_data, &ab_data_orig);
+ }
+ return ret;
+}
+
+AvbIOResult avb_ab_mark_slot_successful(AvbABOps* ab_ops,
+ unsigned int slot_number) {
+ AvbABData ab_data, ab_data_orig;
+ AvbIOResult ret;
+
+ avb_assert(slot_number < 2);
+
+ ret = load_metadata(ab_ops, &ab_data, &ab_data_orig);
+ if (ret != AVB_IO_RESULT_OK) {
+ goto out;
+ }
+
+ if (!slot_is_bootable(&ab_data.slots[slot_number])) {
+ avb_error("Cannot mark unbootable slot as successful.\n");
+ ret = AVB_IO_RESULT_OK;
+ goto out;
+ }
+
+ ab_data.slots[slot_number].tries_remaining = 0;
+ ab_data.slots[slot_number].successful_boot = 1;
+
+ ret = AVB_IO_RESULT_OK;
+
+out:
+ if (ret == AVB_IO_RESULT_OK) {
+ ret = save_metadata_if_changed(ab_ops, &ab_data, &ab_data_orig);
+ }
+ return ret;
+}
+
+const char* avb_ab_flow_result_to_string(AvbABFlowResult result) {
+ const char* ret = NULL;
+
+ switch (result) {
+ case AVB_AB_FLOW_RESULT_OK:
+ ret = "OK";
+ break;
+
+ case AVB_AB_FLOW_RESULT_OK_WITH_VERIFICATION_ERROR:
+ ret = "OK_WITH_VERIFICATION_ERROR";
+ break;
+
+ case AVB_AB_FLOW_RESULT_ERROR_OOM:
+ ret = "ERROR_OOM";
+ break;
+
+ case AVB_AB_FLOW_RESULT_ERROR_IO:
+ ret = "ERROR_IO";
+ break;
+
+ case AVB_AB_FLOW_RESULT_ERROR_NO_BOOTABLE_SLOTS:
+ ret = "ERROR_NO_BOOTABLE_SLOTS";
+ break;
+ /* Do not add a 'default:' case here because of -Wswitch. */
+ }
+
+ if (ret == NULL) {
+ avb_error("Unknown AvbABFlowResult value.\n");
+ ret = "(unknown)";
+ }
+
+ return ret;
+}
diff --git a/avb/libavb_ab/avb_ab_flow.h b/avb/libavb_ab/avb_ab_flow.h
new file mode 100644
index 0000000..6f6a3d2
--- /dev/null
+++ b/avb/libavb_ab/avb_ab_flow.h
@@ -0,0 +1,249 @@
+/*
+ * 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(AVB_INSIDE_LIBAVB_AB_H) && !defined(AVB_COMPILATION)
+#error \
+ "Never include this file directly, include libavb_ab/libavb_ab.h instead."
+#endif
+
+#ifndef AVB_AB_FLOW_H_
+#define AVB_AB_FLOW_H_
+
+#include "avb_ab_ops.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Magic for the A/B struct when serialized. */
+#define AVB_AB_MAGIC "\0AB0"
+#define AVB_AB_MAGIC_LEN 4
+
+/* Versioning for the on-disk A/B metadata - keep in sync with avbtool. */
+#define AVB_AB_MAJOR_VERSION 1
+#define AVB_AB_MINOR_VERSION 0
+
+/* Size of AvbABData struct. */
+#define AVB_AB_DATA_SIZE 32
+
+/* Maximum values for slot data */
+#define AVB_AB_MAX_PRIORITY 15
+#define AVB_AB_MAX_TRIES_REMAINING 7
+
+/* Struct used for recording per-slot metadata.
+ *
+ * When serialized, data is stored in network byte-order.
+ */
+typedef struct AvbABSlotData {
+ /* Slot priority. Valid values range from 0 to AVB_AB_MAX_PRIORITY,
+ * both inclusive with 1 being the lowest and AVB_AB_MAX_PRIORITY
+ * being the highest. The special value 0 is used to indicate the
+ * slot is unbootable.
+ */
+ uint8_t priority;
+
+ /* Number of times left attempting to boot this slot ranging from 0
+ * to AVB_AB_MAX_TRIES_REMAINING.
+ */
+ uint8_t tries_remaining;
+
+ /* Non-zero if this slot has booted successfully, 0 otherwise. */
+ uint8_t successful_boot;
+
+ /* Reserved for future use. */
+ uint8_t reserved[1];
+} AVB_ATTR_PACKED AvbABSlotData;
+
+/* Struct used for recording A/B metadata.
+ *
+ * When serialized, data is stored in network byte-order.
+ */
+typedef struct AvbABData {
+ /* Magic number used for identification - see AVB_AB_MAGIC. */
+ uint8_t magic[AVB_AB_MAGIC_LEN];
+
+ /* Version of on-disk struct - see AVB_AB_{MAJOR,MINOR}_VERSION. */
+ uint8_t version_major;
+ uint8_t version_minor;
+
+ /* Padding to ensure |slots| field start eight bytes in. */
+ uint8_t reserved1[2];
+
+ /* Per-slot metadata. */
+ AvbABSlotData slots[2];
+
+ /* Reserved for future use. */
+ uint8_t reserved2[12];
+
+ /* CRC32 of all 28 bytes preceding this field. */
+ uint32_t crc32;
+} AVB_ATTR_PACKED AvbABData;
+
+/* Copies |src| to |dest|, byte-swapping fields in the
+ * process. Returns false if the data is invalid (e.g. wrong magic,
+ * wrong CRC32 etc.), true otherwise.
+ */
+bool avb_ab_data_verify_and_byteswap(const AvbABData* src, AvbABData* dest);
+
+/* Copies |src| to |dest|, byte-swapping fields in the process. Also
+ * updates the |crc32| field in |dest|.
+ */
+void avb_ab_data_update_crc_and_byteswap(const AvbABData* src, AvbABData* dest);
+
+/* Initializes |data| such that it has two slots and both slots have
+ * maximum tries remaining. The CRC is not set.
+ */
+void avb_ab_data_init(AvbABData* data);
+
+/* Reads A/B metadata from the 'misc' partition using |ops|. Returned
+ * data is properly byteswapped. Returns AVB_IO_RESULT_OK on
+ * success, error code otherwise.
+ *
+ * If the data read from disk is invalid (e.g. wrong magic or CRC
+ * checksum failure), the metadata will be reset using
+ * avb_ab_data_init() and then written to disk.
+ */
+AvbIOResult avb_ab_data_read(AvbABOps* ab_ops, AvbABData* data);
+
+/* Writes A/B metadata to the 'misc' partition using |ops|. This will
+ * byteswap and update the CRC as needed. Returns AVB_IO_RESULT_OK on
+ * success, error code otherwise.
+ */
+AvbIOResult avb_ab_data_write(AvbABOps* ab_ops, const AvbABData* data);
+
+/* Return codes used in avb_ab_flow(), see that function for
+ * documentation of each value.
+ */
+typedef enum {
+ AVB_AB_FLOW_RESULT_OK,
+ AVB_AB_FLOW_RESULT_OK_WITH_VERIFICATION_ERROR,
+ AVB_AB_FLOW_RESULT_ERROR_OOM,
+ AVB_AB_FLOW_RESULT_ERROR_IO,
+ AVB_AB_FLOW_RESULT_ERROR_NO_BOOTABLE_SLOTS
+} AvbABFlowResult;
+
+/* Get a textual representation of |result|. */
+const char* avb_ab_flow_result_to_string(AvbABFlowResult result);
+
+/* High-level function to select a slot to boot. The following
+ * algorithm is used:
+ *
+ * 1. A/B metadata is loaded and validated using the
+ * read_ab_metadata() operation. Typically this means it's read from
+ * the 'misc' partition and if it's invalid then it's reset using
+ * avb_ab_data_init() and this reset metadata is returned.
+ *
+ * 2. All bootable slots listed in the A/B metadata are verified using
+ * avb_slot_verify(). If a slot is invalid or if it fails verification
+ * (and |allow_verification_error| is false, see below), it will be
+ * marked as unbootable in the A/B metadata and the metadata will be
+ * saved to disk before returning.
+ *
+ * 3. If there are no bootable slots, the value
+ * AVB_AB_FLOW_RESULT_ERROR_NO_BOOTABLE_SLOTS is returned.
+ *
+ * 4. For each bootable slot, the Stored Rollback Indexes are updated
+ * such that for each rollback index location, the Stored Rollback
+ * Index is the largest number smaller than or equal to the Rollback
+ * Index of each slot.
+ *
+ * 5. The bootable slot with the highest priority is selected and
+ * returned in |out_data|. If this slot is already marked as
+ * successful, the A/B metadata is not modified. However, if the slot
+ * is not marked as bootable its |tries_remaining| count is
+ * decremented and the A/B metadata is saved to disk before returning.
+ * In either case the value AVB_AB_FLOW_RESULT_OK is returning.
+ *
+ * The partitions to load is given in |requested_partitions| as a
+ * NULL-terminated array of NUL-terminated strings. Typically the
+ * |requested_partitions| array only contains a single item for the
+ * boot partition, 'boot'.
+ *
+ * If the device is unlocked (and _only_ if it's unlocked), true
+ * should be passed in the |allow_verification_error| parameter. This
+ * will allow considering slots as verified even when
+ * avb_slot_verify() returns
+ * AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED,
+ * AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION, or
+ * AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX for the slot in
+ * question.
+ *
+ * If a slot was selected and it verified then AVB_AB_FLOW_RESULT_OK
+ * is returned.
+ *
+ * If a slot was selected but it didn't verify then
+ * AVB_AB_FLOW_RESULT_OK_WITH_VERIFICATION_ERROR is returned. This can
+ * only happen when |allow_verification_error| is true.
+ *
+ * If an I/O operation - such as loading/saving metadata or checking
+ * rollback indexes - fail, the value AVB_AB_FLOW_RESULT_ERROR_IO is
+ * returned.
+ *
+ * If memory allocation fails, AVB_AB_FLOW_RESULT_ERROR_OOM is
+ * returned.
+ *
+ * Reasonable behavior for handling AVB_AB_FLOW_RESULT_ERROR_NO_BOOTABLE_SLOTS
+ * is to initiate device repair (which is device-dependent).
+ */
+AvbABFlowResult avb_ab_flow(AvbABOps* ab_ops,
+ const char* const* requested_partitions,
+ bool allow_verification_error,
+ AvbSlotVerifyData** out_data);
+
+/* Marks the slot with the given slot number as active. Returns
+ * AVB_IO_RESULT_OK on success, error code otherwise.
+ *
+ * This function is typically used by the OS updater when completing
+ * an update. It can also used by the firmware for implementing the
+ * "set_active" command.
+ */
+AvbIOResult avb_ab_mark_slot_active(AvbABOps* ab_ops, unsigned int slot_number);
+
+/* Marks the slot with the given slot number as unbootable. Returns
+ * AVB_IO_RESULT_OK on success, error code otherwise.
+ *
+ * This function is typically used by the OS updater before writing to
+ * a slot.
+ */
+AvbIOResult avb_ab_mark_slot_unbootable(AvbABOps* ab_ops,
+ unsigned int slot_number);
+
+/* Marks the slot with the given slot number as having booted
+ * successfully. Returns AVB_IO_RESULT_OK on success, error code
+ * otherwise.
+ *
+ * Calling this on an unbootable slot is an error - AVB_IO_RESULT_OK
+ * will be returned yet the function will have no side-effects.
+ *
+ * This function is typically used by the OS updater after having
+ * confirmed that the slot works as intended.
+ */
+AvbIOResult avb_ab_mark_slot_successful(AvbABOps* ab_ops,
+ unsigned int slot_number);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_AB_FLOW_H_ */
diff --git a/avb/libavb_ab/avb_ab_ops.h b/avb/libavb_ab/avb_ab_ops.h
new file mode 100644
index 0000000..8d8fde7
--- /dev/null
+++ b/avb/libavb_ab/avb_ab_ops.h
@@ -0,0 +1,79 @@
+/*
+ * 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(AVB_INSIDE_LIBAVB_AB_H) && !defined(AVB_COMPILATION)
+#error \
+ "Never include this file directly, include libavb_ab/libavb_ab.h instead."
+#endif
+
+#ifndef AVB_AB_OPS_H_
+#define AVB_AB_OPS_H_
+
+#include <libavb/libavb.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct AvbABOps;
+typedef struct AvbABOps AvbABOps;
+
+struct AvbABData;
+
+/* High-level operations/functions/methods for A/B that are platform
+ * dependent.
+ */
+struct AvbABOps {
+ /* Operations from libavb. */
+ AvbOps* ops;
+
+ /* Reads A/B metadata from persistent storage. Returned data is
+ * properly byteswapped. Returns AVB_IO_RESULT_OK on success, error
+ * code otherwise.
+ *
+ * If the data read is invalid (e.g. wrong magic or CRC checksum
+ * failure), the metadata shoule be reset using avb_ab_data_init()
+ * and then written to persistent storage.
+ *
+ * Implementations will typically want to use avb_ab_data_read()
+ * here to use the 'misc' partition for persistent storage.
+ */
+ AvbIOResult (*read_ab_metadata)(AvbABOps* ab_ops, struct AvbABData* data);
+
+ /* Writes A/B metadata to persistent storage. This will byteswap and
+ * update the CRC as needed. Returns AVB_IO_RESULT_OK on success,
+ * error code otherwise.
+ *
+ * Implementations will typically want to use avb_ab_data_write()
+ * here to use the 'misc' partition for persistent storage.
+ */
+ AvbIOResult (*write_ab_metadata)(AvbABOps* ab_ops,
+ const struct AvbABData* data);
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_AB_OPS_H_ */
diff --git a/avb/libavb_ab/libavb_ab.h b/avb/libavb_ab/libavb_ab.h
new file mode 100644
index 0000000..0dcf3e9
--- /dev/null
+++ b/avb/libavb_ab/libavb_ab.h
@@ -0,0 +1,40 @@
+/*
+ * 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 LIBAVB_AB_H_
+#define LIBAVB_AB_H_
+
+#include <libavb/libavb.h>
+
+/* The AVB_INSIDE_LIBAVB_AB_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 AVB_INSIDE_LIBAVB_AB_H
+#include "avb_ab_flow.h"
+#include "avb_ab_ops.h"
+#undef AVB_INSIDE_LIBAVB_AB_H
+
+#endif /* LIBAVB_AB_H_ */
diff --git a/avb/libavb_atx/avb_atx_ops.h b/avb/libavb_atx/avb_atx_ops.h
new file mode 100644
index 0000000..e05ce99
--- /dev/null
+++ b/avb/libavb_atx/avb_atx_ops.h
@@ -0,0 +1,68 @@
+/*
+ * 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(AVB_INSIDE_LIBAVB_ATX_H) && !defined(AVB_COMPILATION)
+#error \
+ "Never include this file directly, include libavb_atx/libavb_atx.h instead."
+#endif
+
+#ifndef AVB_ATX_OPS_H_
+#define AVB_ATX_OPS_H_
+
+#include <libavb/libavb.h>
+
+#include "libavb_atx/avb_atx_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct AvbAtxOps;
+typedef struct AvbAtxOps AvbAtxOps;
+
+/* An extension to AvbOps required by avb_atx_validate_vbmeta_public_key(). */
+struct AvbAtxOps {
+ /* Operations from libavb. */
+ AvbOps* ops;
+
+ /* Reads permanent |attributes| data. There are no restrictions on where this
+ * data is stored. On success, returns AVB_IO_RESULT_OK and populates
+ * |attributes|.
+ */
+ AvbIOResult (*read_permanent_attributes)(
+ AvbAtxOps* atx_ops, AvbAtxPermanentAttributes* attributes);
+
+ /* Reads a |hash| of permanent attributes. This hash MUST be retrieved from a
+ * permanently read-only location (e.g. fuses) when a device is LOCKED. On
+ * success, returned AVB_IO_RESULT_OK and populates |hash|.
+ */
+ AvbIOResult (*read_permanent_attributes_hash)(
+ AvbAtxOps* atx_ops, uint8_t hash[AVB_SHA256_DIGEST_SIZE]);
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_ATX_OPS_H_ */
diff --git a/avb/libavb_atx/avb_atx_types.h b/avb/libavb_atx/avb_atx_types.h
new file mode 100644
index 0000000..ae1b438
--- /dev/null
+++ b/avb/libavb_atx/avb_atx_types.h
@@ -0,0 +1,88 @@
+/*
+ * 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(AVB_INSIDE_LIBAVB_ATX_H) && !defined(AVB_COMPILATION)
+#error \
+ "Never include this file directly, include libavb_atx/libavb_atx.h instead."
+#endif
+
+#ifndef AVB_ATX_TYPES_H_
+#define AVB_ATX_TYPES_H_
+
+#include <libavb/libavb.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Size in bytes of an Android Things product ID. */
+#define AVB_ATX_PRODUCT_ID_SIZE 16
+
+/* Size in bytes of a serialized public key with a 2048-bit modulus. */
+#define AVB_ATX_PUBLIC_KEY_SIZE_2048 (sizeof(AvbRSAPublicKeyHeader) + 512)
+
+/* Size in bytes of a serialized public key with a 4096-bit modulus. */
+#define AVB_ATX_PUBLIC_KEY_SIZE_4096 (sizeof(AvbRSAPublicKeyHeader) + 1024)
+
+/* Data structure of Android Things permanent attributes. */
+typedef struct AvbAtxPermanentAttributes {
+ uint32_t version;
+ uint8_t product_root_public_key[AVB_ATX_PUBLIC_KEY_SIZE_4096];
+ uint8_t product_id[AVB_ATX_PRODUCT_ID_SIZE];
+} AVB_ATTR_PACKED AvbAtxPermanentAttributes;
+
+/* Data structure of signed fields in an Android Things certificate. */
+typedef struct AvbAtxCertificateSignedData {
+ uint32_t version;
+ uint8_t public_key[AVB_ATX_PUBLIC_KEY_SIZE_2048];
+ uint8_t subject[AVB_SHA256_DIGEST_SIZE];
+ uint8_t usage[AVB_SHA256_DIGEST_SIZE];
+ uint64_t key_version;
+} AVB_ATTR_PACKED AvbAtxCertificateSignedData;
+
+/* Data structure of a certificate signed by a 4096-bit key. */
+typedef struct AvbAtxCertificate4096 {
+ AvbAtxCertificateSignedData signed_data;
+ uint8_t signature[AVB_RSA4096_NUM_BYTES];
+} AVB_ATTR_PACKED AvbAtxCertificate4096;
+
+/* Data structure of a certificate signed by a 2048-bit key. */
+typedef struct AvbAtxCertificate2048 {
+ AvbAtxCertificateSignedData signed_data;
+ uint8_t signature[AVB_RSA2048_NUM_BYTES];
+} AVB_ATTR_PACKED AvbAtxCertificate2048;
+
+/* Data structure of Android Things public key metadata in vbmeta. */
+typedef struct AvbAtxPublicKeyMetadata {
+ uint32_t version;
+ AvbAtxCertificate4096 product_intermediate_key_certificate;
+ AvbAtxCertificate2048 product_signing_key_certificate;
+ uint64_t google_signing_key_version;
+} AVB_ATTR_PACKED AvbAtxPublicKeyMetadata;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_ATX_TYPES_H_ */
diff --git a/avb/libavb_atx/avb_atx_validate.c b/avb/libavb_atx/avb_atx_validate.c
new file mode 100644
index 0000000..7a29d66
--- /dev/null
+++ b/avb/libavb_atx/avb_atx_validate.c
@@ -0,0 +1,269 @@
+/*
+ * 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 "libavb_atx/avb_atx_validate.h"
+
+#include "libavb/avb_rsa.h"
+#include "libavb/avb_sha.h"
+#include "libavb/avb_sysdeps.h"
+#include "libavb/avb_util.h"
+
+/* Computes the SHA256 |hash| of |length| bytes of |data|. */
+static void sha256(const uint8_t* data,
+ uint32_t length,
+ uint8_t hash[AVB_SHA256_DIGEST_SIZE]) {
+ AvbSHA256Ctx context;
+ avb_sha256_init(&context);
+ avb_sha256_update(&context, data, length);
+ uint8_t* tmp = avb_sha256_final(&context);
+ avb_memcpy(hash, tmp, AVB_SHA256_DIGEST_SIZE);
+}
+
+/* Computes the SHA512 |hash| of |length| bytes of |data|. */
+static void sha512(const uint8_t* data,
+ uint32_t length,
+ uint8_t hash[AVB_SHA512_DIGEST_SIZE]) {
+ AvbSHA512Ctx context;
+ avb_sha512_init(&context);
+ avb_sha512_update(&context, data, length);
+ uint8_t* tmp = avb_sha512_final(&context);
+ avb_memcpy(hash, tmp, AVB_SHA512_DIGEST_SIZE);
+}
+
+/* Computes the SHA256 |hash| of a NUL-terminated |str|. */
+static void sha256_str(const char* str, uint8_t hash[AVB_SHA256_DIGEST_SIZE]) {
+ sha256((const uint8_t*)str, avb_strlen(str), hash);
+}
+
+/* Verifies structure and |expected_hash| of permanent |attributes|. */
+static bool verify_permanent_attributes(
+ const AvbAtxPermanentAttributes* attributes,
+ uint8_t expected_hash[AVB_SHA256_DIGEST_SIZE]) {
+ uint8_t hash[AVB_SHA256_DIGEST_SIZE];
+
+ if (attributes->version != 1) {
+ avb_error("Unsupported permanent attributes version.\n");
+ return false;
+ }
+ sha256((const uint8_t*)attributes, sizeof(AvbAtxPermanentAttributes), hash);
+ if (0 != avb_safe_memcmp(hash, expected_hash, AVB_SHA256_DIGEST_SIZE)) {
+ avb_error("Invalid permanent attributes.\n");
+ return false;
+ }
+ return true;
+}
+
+/* Verifies signature and fields of a PIK certificate. */
+static bool verify_pik_certificate(
+ AvbAtxCertificate4096* certificate,
+ uint8_t authority[AVB_ATX_PUBLIC_KEY_SIZE_4096],
+ uint64_t minimum_version) {
+ const AvbAlgorithmData* algorithm_data;
+ uint8_t certificate_hash[AVB_SHA512_DIGEST_SIZE];
+ uint8_t expected_usage[AVB_SHA256_DIGEST_SIZE];
+
+ if (certificate->signed_data.version != 1) {
+ avb_error("Unsupported PIK certificate format.\n");
+ return false;
+ }
+ algorithm_data = avb_get_algorithm_data(AVB_ALGORITHM_TYPE_SHA512_RSA4096);
+ sha512((const uint8_t*)&certificate->signed_data,
+ sizeof(AvbAtxCertificateSignedData),
+ certificate_hash);
+ if (!avb_rsa_verify(authority,
+ AVB_ATX_PUBLIC_KEY_SIZE_4096,
+ certificate->signature,
+ AVB_RSA4096_NUM_BYTES,
+ certificate_hash,
+ AVB_SHA512_DIGEST_SIZE,
+ algorithm_data->padding,
+ algorithm_data->padding_len)) {
+ avb_error("Invalid PIK certificate signature.\n");
+ return false;
+ }
+ sha256_str("com.google.android.things.vboot.ca", expected_usage);
+ if (0 != avb_safe_memcmp(certificate->signed_data.usage,
+ expected_usage,
+ AVB_SHA256_DIGEST_SIZE)) {
+ avb_error("Invalid PIK certificate usage.\n");
+ return false;
+ }
+ if (certificate->signed_data.key_version < minimum_version) {
+ avb_error("PIK rollback detected.\n");
+ return false;
+ }
+ return true;
+}
+
+/* Verifies signature and fields of a PSK certificate. */
+static bool verify_psk_certificate(
+ AvbAtxCertificate2048* certificate,
+ uint8_t authority[AVB_ATX_PUBLIC_KEY_SIZE_2048],
+ uint64_t minimum_version,
+ uint8_t product_id[AVB_ATX_PRODUCT_ID_SIZE]) {
+ const AvbAlgorithmData* algorithm_data;
+ uint8_t certificate_hash[AVB_SHA256_DIGEST_SIZE];
+ uint8_t expected_subject[AVB_SHA256_DIGEST_SIZE];
+ uint8_t expected_usage[AVB_SHA256_DIGEST_SIZE];
+ if (certificate->signed_data.version != 1) {
+ avb_error("Unsupported PSK certificate format.\n");
+ return false;
+ }
+ algorithm_data = avb_get_algorithm_data(AVB_ALGORITHM_TYPE_SHA256_RSA2048);
+ sha256((const uint8_t*)&certificate->signed_data,
+ sizeof(AvbAtxCertificateSignedData),
+ certificate_hash);
+ if (!avb_rsa_verify(authority,
+ AVB_ATX_PUBLIC_KEY_SIZE_2048,
+ certificate->signature,
+ AVB_RSA2048_NUM_BYTES,
+ certificate_hash,
+ AVB_SHA256_DIGEST_SIZE,
+ algorithm_data->padding,
+ algorithm_data->padding_len)) {
+ avb_error("Invalid PSK certificate signature.\n");
+ return false;
+ }
+ sha256(product_id, AVB_ATX_PRODUCT_ID_SIZE, expected_subject);
+ if (0 != avb_safe_memcmp(certificate->signed_data.subject,
+ expected_subject,
+ AVB_SHA256_DIGEST_SIZE)) {
+ avb_error("Product ID mismatch.\n");
+ return false;
+ }
+ sha256_str("com.google.android.things.vboot", expected_usage);
+ if (0 != avb_safe_memcmp(certificate->signed_data.usage,
+ expected_usage,
+ AVB_SHA256_DIGEST_SIZE)) {
+ avb_error("Invalid PSK certificate usage.\n");
+ return false;
+ }
+ if (certificate->signed_data.key_version < minimum_version) {
+ avb_error("PSK rollback detected.\n");
+ return false;
+ }
+ return true;
+}
+
+AvbIOResult avb_atx_validate_vbmeta_public_key(
+ AvbOps* ops,
+ const uint8_t* public_key_data,
+ size_t public_key_length,
+ const uint8_t* public_key_metadata,
+ size_t public_key_metadata_length,
+ bool* out_is_trusted) {
+ AvbIOResult result = AVB_IO_RESULT_OK;
+ AvbAtxPermanentAttributes permanent_attributes;
+ uint8_t permanent_attributes_hash[AVB_SHA256_DIGEST_SIZE];
+ AvbAtxPublicKeyMetadata metadata;
+ uint64_t minimum_version;
+
+ /* Be pessimistic so we can exit early without having to remember to clear. */
+ *out_is_trusted = false;
+
+ /* Read and verify permanent attributes. */
+ result = ops->atx_ops->read_permanent_attributes(ops->atx_ops,
+ &permanent_attributes);
+ if (result != AVB_IO_RESULT_OK) {
+ avb_error("Failed to read permanent attributes.\n");
+ return result;
+ }
+ result = ops->atx_ops->read_permanent_attributes_hash(
+ ops->atx_ops, permanent_attributes_hash);
+ if (result != AVB_IO_RESULT_OK) {
+ avb_error("Failed to read permanent attributes hash.\n");
+ return result;
+ }
+ if (!verify_permanent_attributes(&permanent_attributes,
+ permanent_attributes_hash)) {
+ return AVB_IO_RESULT_OK;
+ }
+
+ /* Sanity check public key metadata. */
+ if (public_key_metadata_length != sizeof(AvbAtxPublicKeyMetadata)) {
+ avb_error("Invalid public key metadata.\n");
+ return AVB_IO_RESULT_OK;
+ }
+ avb_memcpy(&metadata, public_key_metadata, sizeof(AvbAtxPublicKeyMetadata));
+ if (metadata.version != 1) {
+ avb_error("Unsupported public key metadata.\n");
+ return AVB_IO_RESULT_OK;
+ }
+
+ /* Verify the PIK certificate. */
+ result = ops->read_rollback_index(
+ ops, AVB_ATX_PIK_VERSION_LOCATION, &minimum_version);
+ if (result != AVB_IO_RESULT_OK) {
+ avb_error("Failed to read PIK minimum version.\n");
+ return result;
+ }
+ if (!verify_pik_certificate(&metadata.product_intermediate_key_certificate,
+ permanent_attributes.product_root_public_key,
+ minimum_version)) {
+ return AVB_IO_RESULT_OK;
+ }
+
+ /* Verify the PSK certificate. */
+ result = ops->read_rollback_index(
+ ops, AVB_ATX_PSK_VERSION_LOCATION, &minimum_version);
+ if (result != AVB_IO_RESULT_OK) {
+ avb_error("Failed to read PSK minimum version.\n");
+ return result;
+ }
+ if (!verify_psk_certificate(
+ &metadata.product_signing_key_certificate,
+ metadata.product_intermediate_key_certificate.signed_data.public_key,
+ minimum_version,
+ permanent_attributes.product_id)) {
+ return AVB_IO_RESULT_OK;
+ }
+
+ /* Verify the PSK is the same key that verified vbmeta. */
+ if (public_key_length != AVB_ATX_PUBLIC_KEY_SIZE_2048) {
+ avb_error("Public key length mismatch.\n");
+ return AVB_IO_RESULT_OK;
+ }
+ if (0 != avb_safe_memcmp(
+ metadata.product_signing_key_certificate.signed_data.public_key,
+ public_key_data,
+ AVB_ATX_PUBLIC_KEY_SIZE_2048)) {
+ avb_error("Public key mismatch.\n");
+ return AVB_IO_RESULT_OK;
+ }
+
+ /* Verify the GSK minimum version. */
+ result = ops->read_rollback_index(
+ ops, AVB_ATX_GSK_VERSION_LOCATION, &minimum_version);
+ if (result != AVB_IO_RESULT_OK) {
+ avb_error("Failed to read GSK minimum version.\n");
+ return result;
+ }
+ if (metadata.google_signing_key_version < minimum_version) {
+ avb_error("Google signing key rollback detected.\n");
+ return AVB_IO_RESULT_OK;
+ }
+
+ *out_is_trusted = true;
+ return AVB_IO_RESULT_OK;
+}
diff --git a/avb/libavb_atx/avb_atx_validate.h b/avb/libavb_atx/avb_atx_validate.h
new file mode 100644
index 0000000..07c154e
--- /dev/null
+++ b/avb/libavb_atx/avb_atx_validate.h
@@ -0,0 +1,82 @@
+/*
+ * 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(AVB_INSIDE_LIBAVB_ATX_H) && !defined(AVB_COMPILATION)
+#error \
+ "Never include this file directly, include libavb_atx/libavb_atx.h instead."
+#endif
+
+#ifndef AVB_ATX_VALIDATE_H_
+#define AVB_ATX_VALIDATE_H_
+
+#include "libavb_atx/avb_atx_ops.h"
+#include "libavb_atx/avb_atx_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Rollback index locations for Android Things key versions. */
+#define AVB_ATX_PIK_VERSION_LOCATION 0x1000
+#define AVB_ATX_PSK_VERSION_LOCATION 0x1001
+#define AVB_ATX_GSK_VERSION_LOCATION 0x1002
+
+/* An implementation of validate_vbmeta_public_key for Android Things. See
+ * libavb/avb_ops.h for details on validate_vbmeta_public_key in general. This
+ * implementation uses the metadata expected with Android Things vbmeta images
+ * to perform validation on the public key. The ATX ops must be implemented.
+ * That is, |ops->atx_ops| must be valid.
+ *
+ * There are a multiple values that need verification:
+ * - Permanent Product Attributes: A hash of these attributes is fused into
+ * hardware. Consistency is checked.
+ * - Product Root Key (PRK): This key is provided in permanent attributes and
+ * is the root authority for all Android Things
+ * products.
+ * - Product Intermediate Key (PIK): This key is a rotated intermediary. It is
+ * certified by the PRK.
+ * - Product Signing Key (PSK): This key is a rotated authority for a specific
+ * Android Things product. It is certified by a
+ * PIK and must match |public_key_data|.
+ * - Google Signing Key (GSK): This key is a rotated authority for system and
+ * boot partitions built by Google. The key is
+ * already verified as part of vbmeta so only the
+ * key version needs verification here.
+ * - Product ID: This value is provided in permanent attributes and is unique
+ * to a specific Android Things product. This value must match
+ * the subject of the PSK certificate.
+ */
+AvbIOResult avb_atx_validate_vbmeta_public_key(
+ AvbOps* ops,
+ const uint8_t* public_key_data,
+ size_t public_key_length,
+ const uint8_t* public_key_metadata,
+ size_t public_key_metadata_length,
+ bool* out_is_trusted);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_ATX_VALIDATE_H_ */
diff --git a/avb/libavb_atx/libavb_atx.h b/avb/libavb_atx/libavb_atx.h
new file mode 100644
index 0000000..839c0af
--- /dev/null
+++ b/avb/libavb_atx/libavb_atx.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 LIBAVB_ATX_H_
+#define LIBAVB_ATX_H_
+
+#include <libavb/libavb.h>
+
+/* The AVB_INSIDE_LIBAVB_ATX_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 AVB_INSIDE_LIBAVB_ATX_H
+#include "avb_atx_ops.h"
+#include "avb_atx_types.h"
+#include "avb_atx_validate.h"
+#undef AVB_INSIDE_LIBAVB_ATX_H
+
+#endif /* LIBAVB_ATX_H_ */
diff --git a/avb/test/avb_ab_flow_unittest.cc b/avb/test/avb_ab_flow_unittest.cc
new file mode 100644
index 0000000..e13fccb
--- /dev/null
+++ b/avb/test/avb_ab_flow_unittest.cc
@@ -0,0 +1,1321 @@
+/*
+ * 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 <map>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <libavb_ab/libavb_ab.h>
+
+#include "avb_unittest_util.h"
+#include "fake_avb_ops.h"
+
+namespace avb {
+
+static_assert(sizeof(AvbABSlotData) == 4, "AvbABSlotData has wrong size");
+static_assert(sizeof(AvbABData) == AVB_AB_DATA_SIZE,
+ "AvbABData has wrong size");
+static_assert(offsetof(AvbABData, slots) % 8 == 0,
+ "AvbABData slots member has wrong offset");
+
+TEST(ABTest, InitData) {
+ AvbABData data;
+ avb_ab_data_init(&data);
+ EXPECT_EQ(0,
+ strncmp(reinterpret_cast<const char*>(data.magic),
+ AVB_AB_MAGIC,
+ AVB_AB_MAGIC_LEN));
+ EXPECT_EQ(AVB_AB_MAX_PRIORITY, data.slots[0].priority);
+ EXPECT_EQ(AVB_AB_MAX_TRIES_REMAINING, data.slots[0].tries_remaining);
+ EXPECT_EQ(0, data.slots[0].successful_boot);
+ EXPECT_EQ(AVB_AB_MAX_PRIORITY - 1, data.slots[1].priority);
+ EXPECT_EQ(AVB_AB_MAX_TRIES_REMAINING, data.slots[1].tries_remaining);
+ EXPECT_EQ(0, data.slots[1].successful_boot);
+ EXPECT_EQ(uint32_t(0), data.crc32);
+}
+
+TEST(ABTest, DataSerialization) {
+ AvbABData data;
+ AvbABData serialized;
+ AvbABData restored;
+
+ avb_ab_data_init(&data);
+ EXPECT_EQ(uint32_t(0), data.crc32);
+ avb_ab_data_update_crc_and_byteswap(&data, &serialized);
+ EXPECT_NE(uint32_t(0), serialized.crc32);
+ EXPECT_TRUE(avb_ab_data_verify_and_byteswap(&serialized, &restored));
+ EXPECT_EQ(std::string(reinterpret_cast<const char*>(data.magic), 4),
+ std::string(reinterpret_cast<const char*>(restored.magic), 4));
+ EXPECT_EQ(data.version_major, restored.version_major);
+ EXPECT_EQ(data.version_minor, restored.version_minor);
+ EXPECT_EQ(0,
+ memcmp(reinterpret_cast<void*>(data.slots),
+ reinterpret_cast<void*>(restored.slots),
+ sizeof(AvbABSlotData) * 2));
+}
+
+TEST(ABTest, CatchBadCRC) {
+ AvbABData data;
+ AvbABData serialized;
+ AvbABData restored;
+
+ avb_ab_data_init(&data);
+ avb_ab_data_update_crc_and_byteswap(&data, &serialized);
+ serialized.crc32 += 1;
+ EXPECT_FALSE(avb_ab_data_verify_and_byteswap(&serialized, &restored));
+}
+
+TEST(ABTest, CatchUnsupportedMajorVersion) {
+ AvbABData data;
+ AvbABData serialized;
+ AvbABData restored;
+
+ avb_ab_data_init(&data);
+ data.version_major += 1;
+ avb_ab_data_update_crc_and_byteswap(&data, &serialized);
+ EXPECT_FALSE(avb_ab_data_verify_and_byteswap(&serialized, &restored));
+}
+
+TEST(ABTest, SupportSameMajorFutureMinorVersion) {
+ AvbABData data;
+ AvbABData serialized;
+ AvbABData restored;
+
+ avb_ab_data_init(&data);
+ data.version_minor += 1;
+ avb_ab_data_update_crc_and_byteswap(&data, &serialized);
+ EXPECT_TRUE(avb_ab_data_verify_and_byteswap(&serialized, &restored));
+}
+
+#define MISC_PART_SIZE 8 * 1024
+
+// These values are kept short since they are used in SetMD() and it's
+// helpful if the information for a slot fits in one 80-character
+// line.
+enum SlotValidity {
+ SV_OK, // Slot is valid and verified.
+ SV_INV, // Slot is invalid.
+ SV_UNV, // Slot is valid but unverified.
+};
+
+class AvbABFlowTest : public BaseAvbToolTest {
+ public:
+ AvbABFlowTest() {}
+
+ virtual void SetUp() override {
+ BaseAvbToolTest::SetUp();
+ ops_.set_partition_dir(testdir_);
+ ops_.set_stored_rollback_indexes({{0, 0}, {1, 0}, {2, 0}, {3, 0}});
+ ops_.set_stored_is_device_unlocked(false);
+
+ // Create large enough 'misc' partition and initialize it with
+ // zeroes.
+ std::vector<uint8_t> misc;
+ misc.resize(MISC_PART_SIZE);
+ base::FilePath misc_path = testdir_.Append("misc.img");
+ EXPECT_EQ(misc.size(),
+ static_cast<const size_t>(
+ base::WriteFile(misc_path,
+ reinterpret_cast<const char*>(misc.data()),
+ misc.size())));
+
+ // We're going to use this key for all images.
+ ops_.set_expected_public_key(
+ PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem")));
+ }
+
+ void GenerateSlot(unsigned int slot_number,
+ SlotValidity slot_validity,
+ uint64_t rollback_boot,
+ uint64_t rollback_odm) {
+ std::string boot_name = "boot_a.img";
+ std::string vbmeta_name = "vbmeta_a.img";
+ std::string odm_name = "odm_a.img";
+ if (slot_number > 0) {
+ boot_name = "boot_b.img";
+ vbmeta_name = "vbmeta_b.img";
+ odm_name = "odm_b.img";
+ }
+
+ // If asked to make an invalid slot, just generate 1MiB garbage
+ // for each the three images in the slot.
+ if (slot_validity == SV_INV) {
+ GenerateImage(boot_name, 1024 * 1024);
+ GenerateImage(vbmeta_name, 1024 * 1024);
+ GenerateImage(odm_name, 1024 * 1024);
+ return;
+ }
+
+ const size_t boot_partition_size = 16 * 1024 * 1024;
+ const size_t boot_image_size = 5 * 1024 * 1024;
+ base::FilePath boot_path = GenerateImage(boot_name, boot_image_size);
+ EXPECT_COMMAND(0,
+ "./avbtool add_hash_footer"
+ " --image %s"
+ " --rollback_index %" PRIu64
+ " --partition_name boot"
+ " --partition_size %zd"
+ " --salt deadbeef",
+ boot_path.value().c_str(),
+ rollback_boot,
+ boot_partition_size);
+
+ const size_t odm_partition_size = 512 * 1024;
+ const size_t odm_image_size = 80 * 1024;
+ base::FilePath odm_path = GenerateImage(odm_name, odm_image_size);
+ EXPECT_COMMAND(0,
+ "./avbtool add_hashtree_footer"
+ " --image %s"
+ " --rollback_index %" PRIu64
+ " --partition_name odm"
+ " --partition_size %zd"
+ " --salt deadbeef"
+ " --algorithm SHA512_RSA4096 "
+ " --key test/data/testkey_rsa4096.pem",
+ odm_path.value().c_str(),
+ rollback_odm,
+ odm_partition_size);
+
+ base::FilePath pk_path = testdir_.Append("testkey_rsa4096.avbpubkey");
+ EXPECT_COMMAND(
+ 0,
+ "./avbtool extract_public_key --key test/data/testkey_rsa4096.pem"
+ " --output %s",
+ pk_path.value().c_str());
+
+ // If requested to make the image unverified, just use another key
+ // in the chain_partition descriptor since this will cause
+ // avb_slot_verify() to return ERROR_PUBLIC_KEY_REJECTED.
+ if (slot_validity == SV_UNV) {
+ pk_path = GenerateImage("dummy.avbpubkey", 32);
+ }
+
+ GenerateVBMetaImage(vbmeta_name,
+ "SHA256_RSA2048",
+ rollback_boot,
+ base::FilePath("test/data/testkey_rsa2048.pem"),
+ base::StringPrintf("--include_descriptors_from_image %s"
+ " --chain_partition odm:1:%s",
+ boot_path.value().c_str(),
+ pk_path.value().c_str()));
+ }
+
+ void SetMD(int a_pri,
+ int a_tries,
+ bool a_success,
+ SlotValidity a_slot_validity,
+ uint64_t a_rollback_boot,
+ uint64_t a_rollback_odm,
+ int b_pri,
+ int b_tries,
+ bool b_success,
+ SlotValidity b_slot_validity,
+ uint64_t b_rollback_boot,
+ uint64_t b_rollback_odm,
+ const std::map<size_t, uint64_t>& stored_rollback_indexes) {
+ AvbABData data;
+ avb_ab_data_init(&data);
+ data.slots[0].priority = a_pri;
+ data.slots[0].tries_remaining = a_tries;
+ data.slots[0].successful_boot = (a_success ? 1 : 0);
+ data.slots[1].priority = b_pri;
+ data.slots[1].tries_remaining = b_tries;
+ data.slots[1].successful_boot = (b_success ? 1 : 0);
+ EXPECT_EQ(AVB_IO_RESULT_OK,
+ ops_.avb_ab_ops()->write_ab_metadata(ops_.avb_ab_ops(), &data));
+ GenerateSlot(0, a_slot_validity, a_rollback_boot, a_rollback_odm);
+ GenerateSlot(1, b_slot_validity, b_rollback_boot, b_rollback_odm);
+ ops_.set_stored_rollback_indexes(stored_rollback_indexes);
+ }
+
+ std::map<size_t, uint64_t> MakeRollbackIndexes(uint64_t slot_0_value,
+ uint64_t slot_1_value) {
+ return std::map<size_t, uint64_t>{{0, slot_0_value}, {1, slot_1_value}};
+ }
+
+ FakeAvbOps ops_;
+};
+
+#define ExpMD(a_pri, \
+ a_tries, \
+ a_success, \
+ b_pri, \
+ b_tries, \
+ b_success, \
+ stored_rollback_indexes) \
+ do { \
+ AvbABData data; \
+ EXPECT_EQ(AVB_IO_RESULT_OK, \
+ ops_.avb_ab_ops()->read_ab_metadata(ops_.avb_ab_ops(), &data)); \
+ EXPECT_EQ(a_pri, data.slots[0].priority); \
+ EXPECT_EQ(a_tries, data.slots[0].tries_remaining); \
+ EXPECT_EQ(a_success ? 1 : 0, data.slots[0].successful_boot); \
+ EXPECT_EQ(b_pri, data.slots[1].priority); \
+ EXPECT_EQ(b_tries, data.slots[1].tries_remaining); \
+ EXPECT_EQ(b_success ? 1 : 0, data.slots[1].successful_boot); \
+ EXPECT_EQ(stored_rollback_indexes, ops_.get_stored_rollback_indexes()); \
+ } while (0);
+
+TEST_F(AvbABFlowTest, MetadataReadAndWrite) {
+ AvbABData data;
+ AvbABData loaded;
+
+ // First load from an uninitialized 'misc' partition. This should
+ // not fail and just returned initialized data.
+ EXPECT_EQ(AVB_IO_RESULT_OK, avb_ab_data_read(ops_.avb_ab_ops(), &loaded));
+ EXPECT_EQ(AVB_AB_MAX_PRIORITY, loaded.slots[0].priority);
+ EXPECT_EQ(AVB_AB_MAX_TRIES_REMAINING, loaded.slots[0].tries_remaining);
+ EXPECT_EQ(0, loaded.slots[0].successful_boot);
+ EXPECT_EQ(AVB_AB_MAX_PRIORITY - 1, loaded.slots[1].priority);
+ EXPECT_EQ(AVB_AB_MAX_TRIES_REMAINING, loaded.slots[1].tries_remaining);
+ EXPECT_EQ(0, loaded.slots[1].successful_boot);
+
+ // Then initialize and save well-known A/B metadata and check we
+ // read back the same thing.
+ avb_ab_data_init(&data);
+ data.slots[0].priority = 2;
+ data.slots[0].tries_remaining = 3;
+ EXPECT_EQ(AVB_IO_RESULT_OK, avb_ab_data_write(ops_.avb_ab_ops(), &data));
+ EXPECT_EQ(AVB_IO_RESULT_OK, avb_ab_data_read(ops_.avb_ab_ops(), &loaded));
+ EXPECT_EQ(2, loaded.slots[0].priority);
+ EXPECT_EQ(3, loaded.slots[0].tries_remaining);
+}
+
+TEST_F(AvbABFlowTest, EverythingIsValid) {
+ AvbSlotVerifyData* data;
+ const char* requested_partitions[] = {"boot", NULL};
+
+ SetMD(14,
+ 0,
+ 1,
+ SV_OK,
+ 0,
+ 0, // A: pri, tries, success, slot_validity, RIs
+ 15,
+ 0,
+ 1,
+ SV_OK,
+ 0,
+ 0, // B: pri, tries, success, slot_validity, RIs
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+ EXPECT_EQ(AVB_AB_FLOW_RESULT_OK,
+ avb_ab_flow(ops_.avb_ab_ops(),
+ requested_partitions,
+ false /* allow_verification_error */,
+ &data));
+ ExpMD(14,
+ 0,
+ 1, // A: pri, tries, successful
+ 15,
+ 0,
+ 1, // B: pri, tries, successful
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+ ASSERT_NE(nullptr, data);
+ EXPECT_EQ("_b", std::string(data->ab_suffix));
+ avb_slot_verify_data_free(data);
+
+ // Also check the other slot.
+ SetMD(15,
+ 0,
+ 1,
+ SV_OK,
+ 0,
+ 0, // A: pri, tries, success, slot_validity, RIs
+ 14,
+ 0,
+ 1,
+ SV_OK,
+ 0,
+ 0, // B: pri, tries, success, slot_validity, RIs
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+ EXPECT_EQ(AVB_AB_FLOW_RESULT_OK,
+ avb_ab_flow(ops_.avb_ab_ops(),
+ requested_partitions,
+ false /* allow_verification_error */,
+ &data));
+ ExpMD(15,
+ 0,
+ 1, // A: pri, tries, successful
+ 14,
+ 0,
+ 1, // B: pri, tries, successful
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+ ASSERT_NE(nullptr, data);
+ EXPECT_EQ("_a", std::string(data->ab_suffix));
+ avb_slot_verify_data_free(data);
+}
+
+TEST_F(AvbABFlowTest, NoBootableSlots) {
+ AvbSlotVerifyData* data;
+ const char* requested_partitions[] = {"boot", NULL};
+
+ SetMD(0,
+ 0,
+ 0,
+ SV_OK,
+ 0,
+ 0, // A: pri, tries, success, slot_validity, RIs
+ 0,
+ 0,
+ 0,
+ SV_OK,
+ 0,
+ 0, // B: pri, tries, success, slot_validity, RIs
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+ EXPECT_EQ(AVB_AB_FLOW_RESULT_ERROR_NO_BOOTABLE_SLOTS,
+ avb_ab_flow(ops_.avb_ab_ops(),
+ requested_partitions,
+ false /* allow_verification_error */,
+ &data));
+ ExpMD(0,
+ 0,
+ 0, // A: pri, tries, successful
+ 0,
+ 0,
+ 0, // B: pri, tries, successful
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+ ASSERT_EQ(nullptr, data);
+}
+
+TEST_F(AvbABFlowTest, TriesRemainingDecreasing) {
+ AvbSlotVerifyData* data;
+ const char* requested_partitions[] = {"boot", NULL};
+
+ SetMD(15,
+ 3,
+ 0,
+ SV_OK,
+ 0,
+ 0, // A: pri, tries, success, slot_validity, RIs
+ 0,
+ 0,
+ 0,
+ SV_OK,
+ 0,
+ 0, // B: pri, tries, success, slot_validity, RIs
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+
+ EXPECT_EQ(AVB_AB_FLOW_RESULT_OK,
+ avb_ab_flow(ops_.avb_ab_ops(),
+ requested_partitions,
+ false /* allow_verification_error */,
+ &data));
+ ExpMD(15,
+ 2,
+ 0, // A: pri, tries, successful
+ 0,
+ 0,
+ 0, // B: pri, tries, successful
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+ ASSERT_NE(nullptr, data);
+ EXPECT_EQ("_a", std::string(data->ab_suffix));
+ avb_slot_verify_data_free(data);
+
+ // Keep counting down...
+ EXPECT_EQ(AVB_AB_FLOW_RESULT_OK,
+ avb_ab_flow(ops_.avb_ab_ops(),
+ requested_partitions,
+ false /* allow_verification_error */,
+ &data));
+ ExpMD(15,
+ 1,
+ 0, // A: pri, tries, successful
+ 0,
+ 0,
+ 0, // B: pri, tries, successful
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+ ASSERT_NE(nullptr, data);
+ EXPECT_EQ("_a", std::string(data->ab_suffix));
+ avb_slot_verify_data_free(data);
+
+ // Last try...
+ EXPECT_EQ(AVB_AB_FLOW_RESULT_OK,
+ avb_ab_flow(ops_.avb_ab_ops(),
+ requested_partitions,
+ false /* allow_verification_error */,
+ &data));
+ ExpMD(15,
+ 0,
+ 0, // A: pri, tries, successful
+ 0,
+ 0,
+ 0, // B: pri, tries, successful
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+ ASSERT_NE(nullptr, data);
+ EXPECT_EQ("_a", std::string(data->ab_suffix));
+ avb_slot_verify_data_free(data);
+
+ // And we're out of tries. At this point, (15, 0, 0) is normalized
+ // to (0, 0, 0) so expect that.
+ EXPECT_EQ(AVB_AB_FLOW_RESULT_ERROR_NO_BOOTABLE_SLOTS,
+ avb_ab_flow(ops_.avb_ab_ops(),
+ requested_partitions,
+ false /* allow_verification_error */,
+ &data));
+ ExpMD(0,
+ 0,
+ 0, // A: pri, tries, successful
+ 0,
+ 0,
+ 0, // B: pri, tries, successful
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+ ASSERT_EQ(nullptr, data);
+}
+
+TEST_F(AvbABFlowTest, TryingThenFallback) {
+ AvbSlotVerifyData* data;
+ const char* requested_partitions[] = {"boot", NULL};
+
+ SetMD(15,
+ 2,
+ 0,
+ SV_OK,
+ 0,
+ 0, // A: pri, tries, success, slot_validity, RIs
+ 14,
+ 0,
+ 1,
+ SV_OK,
+ 0,
+ 0, // B: pri, tries, success, slot_validity, RIs
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+ EXPECT_EQ(AVB_AB_FLOW_RESULT_OK,
+ avb_ab_flow(ops_.avb_ab_ops(),
+ requested_partitions,
+ false /* allow_verification_error */,
+ &data));
+ ExpMD(15,
+ 1,
+ 0, // A: pri, tries, successful
+ 14,
+ 0,
+ 1, // B: pri, tries, successful
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+ ASSERT_NE(nullptr, data);
+ EXPECT_EQ("_a", std::string(data->ab_suffix));
+ avb_slot_verify_data_free(data);
+
+ // Last try...
+ EXPECT_EQ(AVB_AB_FLOW_RESULT_OK,
+ avb_ab_flow(ops_.avb_ab_ops(),
+ requested_partitions,
+ false /* allow_verification_error */,
+ &data));
+ ExpMD(15,
+ 0,
+ 0, // A: pri, tries, successful
+ 14,
+ 0,
+ 1, // B: pri, tries, successful
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+ ASSERT_NE(nullptr, data);
+ EXPECT_EQ("_a", std::string(data->ab_suffix));
+ avb_slot_verify_data_free(data);
+
+ // And we're out of tries. Check we fall back to slot B.
+ EXPECT_EQ(AVB_AB_FLOW_RESULT_OK,
+ avb_ab_flow(ops_.avb_ab_ops(),
+ requested_partitions,
+ false /* allow_verification_error */,
+ &data));
+ ExpMD(0,
+ 0,
+ 0, // A: pri, tries, successful
+ 14,
+ 0,
+ 1, // B: pri, tries, successful
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+ ASSERT_NE(nullptr, data);
+ EXPECT_EQ("_b", std::string(data->ab_suffix));
+ avb_slot_verify_data_free(data);
+}
+
+TEST_F(AvbABFlowTest, TriesRemainingNotDecreasingIfNotPriority) {
+ AvbSlotVerifyData* data;
+ const char* requested_partitions[] = {"boot", NULL};
+
+ SetMD(15,
+ 0,
+ 1,
+ SV_OK,
+ 0,
+ 0, // A: pri, tries, success, slot_validity, RIs
+ 14,
+ 7,
+ 0,
+ SV_OK,
+ 0,
+ 0, // B: pri, tries, success, slot_validity, RIs
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+ EXPECT_EQ(AVB_AB_FLOW_RESULT_OK,
+ avb_ab_flow(ops_.avb_ab_ops(),
+ requested_partitions,
+ false /* allow_verification_error */,
+ &data));
+ ExpMD(15,
+ 0,
+ 1, // A: pri, tries, successful
+ 14,
+ 7,
+ 0, // B: pri, tries, successful
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+ ASSERT_NE(nullptr, data);
+ EXPECT_EQ("_a", std::string(data->ab_suffix));
+ avb_slot_verify_data_free(data);
+}
+
+TEST_F(AvbABFlowTest, InvalidSlotIsMarkedAsSuch) {
+ AvbSlotVerifyData* data;
+ const char* requested_partitions[] = {"boot", NULL};
+
+ // Slot A is invalid.
+ SetMD(15,
+ 0,
+ 1,
+ SV_INV,
+ 0,
+ 0, // A: pri, tries, success, slot_validity, RIs
+ 14,
+ 0,
+ 1,
+ SV_OK,
+ 0,
+ 0, // B: pri, tries, success, slot_validity, RIs
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+ EXPECT_EQ(AVB_AB_FLOW_RESULT_OK,
+ avb_ab_flow(ops_.avb_ab_ops(),
+ requested_partitions,
+ false /* allow_verification_error */,
+ &data));
+ ExpMD(0,
+ 0,
+ 0, // A: pri, tries, successful
+ 14,
+ 0,
+ 1, // B: pri, tries, successful
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+ ASSERT_NE(nullptr, data);
+ EXPECT_EQ("_b", std::string(data->ab_suffix));
+ avb_slot_verify_data_free(data);
+
+ // Slot B is invalid.
+ SetMD(15,
+ 0,
+ 1,
+ SV_OK,
+ 0,
+ 0, // A: pri, tries, success, slot_validity, RIs
+ 14,
+ 0,
+ 1,
+ SV_INV,
+ 0,
+ 0, // B: pri, tries, success, slot_validity, RIs
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+ EXPECT_EQ(AVB_AB_FLOW_RESULT_OK,
+ avb_ab_flow(ops_.avb_ab_ops(),
+ requested_partitions,
+ false /* allow_verification_error */,
+ &data));
+ ExpMD(15,
+ 0,
+ 1, // A: pri, tries, successful
+ 0,
+ 0,
+ 0, // B: pri, tries, successful
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+ ASSERT_NE(nullptr, data);
+ EXPECT_EQ("_a", std::string(data->ab_suffix));
+ avb_slot_verify_data_free(data);
+
+ // Both slots are invalid.
+ SetMD(15,
+ 0,
+ 1,
+ SV_INV,
+ 0,
+ 0, // A: pri, tries, success, slot_validity, RIs
+ 14,
+ 0,
+ 1,
+ SV_INV,
+ 0,
+ 0, // B: pri, tries, success, slot_validity, RIs
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+ EXPECT_EQ(AVB_AB_FLOW_RESULT_ERROR_NO_BOOTABLE_SLOTS,
+ avb_ab_flow(ops_.avb_ab_ops(),
+ requested_partitions,
+ false /* allow_verification_error */,
+ &data));
+ ExpMD(0,
+ 0,
+ 0, // A: pri, tries, successful
+ 0,
+ 0,
+ 0, // B: pri, tries, successful
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+ ASSERT_EQ(nullptr, data);
+}
+
+TEST_F(AvbABFlowTest, UnverifiedSlotIsMarkedAsSuch) {
+ AvbSlotVerifyData* data;
+ const char* requested_partitions[] = {"boot", NULL};
+
+ // Slot A fails verification.
+ SetMD(15,
+ 0,
+ 1,
+ SV_UNV,
+ 0,
+ 0, // A: pri, tries, success, slot_validity, RIs
+ 14,
+ 0,
+ 1,
+ SV_OK,
+ 0,
+ 0, // B: pri, tries, success, slot_validity, RIs
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+ EXPECT_EQ(AVB_AB_FLOW_RESULT_OK,
+ avb_ab_flow(ops_.avb_ab_ops(),
+ requested_partitions,
+ false /* allow_verification_error */,
+ &data));
+ ExpMD(0,
+ 0,
+ 0, // A: pri, tries, successful
+ 14,
+ 0,
+ 1, // B: pri, tries, successful
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+ ASSERT_NE(nullptr, data);
+ EXPECT_EQ("_b", std::string(data->ab_suffix));
+ avb_slot_verify_data_free(data);
+
+ // Slot B fails verification.
+ SetMD(15,
+ 0,
+ 1,
+ SV_OK,
+ 0,
+ 0, // A: pri, tries, success, slot_validity, RIs
+ 14,
+ 0,
+ 1,
+ SV_UNV,
+ 0,
+ 0, // B: pri, tries, success, slot_validity, RIs
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+ EXPECT_EQ(AVB_AB_FLOW_RESULT_OK,
+ avb_ab_flow(ops_.avb_ab_ops(),
+ requested_partitions,
+ false /* allow_verification_error */,
+ &data));
+ ExpMD(15,
+ 0,
+ 1, // A: pri, tries, successful
+ 0,
+ 0,
+ 0, // B: pri, tries, successful
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+ ASSERT_NE(nullptr, data);
+ EXPECT_EQ("_a", std::string(data->ab_suffix));
+ avb_slot_verify_data_free(data);
+
+ // Both slots fail verification.
+ SetMD(15,
+ 0,
+ 1,
+ SV_UNV,
+ 0,
+ 0, // A: pri, tries, success, slot_validity, RIs
+ 14,
+ 0,
+ 1,
+ SV_UNV,
+ 0,
+ 0, // B: pri, tries, success, slot_validity, RIs
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+ EXPECT_EQ(AVB_AB_FLOW_RESULT_ERROR_NO_BOOTABLE_SLOTS,
+ avb_ab_flow(ops_.avb_ab_ops(),
+ requested_partitions,
+ false /* allow_verification_error */,
+ &data));
+ ExpMD(0,
+ 0,
+ 0, // A: pri, tries, successful
+ 0,
+ 0,
+ 0, // B: pri, tries, successful
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+ ASSERT_EQ(nullptr, data);
+}
+
+TEST_F(AvbABFlowTest, RollbackIndexFailures) {
+ AvbSlotVerifyData* data;
+ const char* requested_partitions[] = {"boot", NULL};
+
+ // Slot A rollback index failure for 'boot'.
+ SetMD(15,
+ 0,
+ 1,
+ SV_OK,
+ 0,
+ 2, // A: pri, tries, success, slot_validity, RIs
+ 14,
+ 0,
+ 1,
+ SV_OK,
+ 2,
+ 2, // B: pri, tries, success, slot_validity, RIs
+ MakeRollbackIndexes(2, 2)); // stored_rollback_indexes
+ EXPECT_EQ(AVB_AB_FLOW_RESULT_OK,
+ avb_ab_flow(ops_.avb_ab_ops(),
+ requested_partitions,
+ false /* allow_verification_error */,
+ &data));
+ ExpMD(0,
+ 0,
+ 0, // A: pri, tries, successful
+ 14,
+ 0,
+ 1, // B: pri, tries, successful
+ MakeRollbackIndexes(2, 2)); // stored_rollback_indexes
+ ASSERT_NE(nullptr, data);
+ EXPECT_EQ("_b", std::string(data->ab_suffix));
+ avb_slot_verify_data_free(data);
+
+ // Slot A rollback index failure for 'odm'.
+ SetMD(15,
+ 0,
+ 1,
+ SV_OK,
+ 2,
+ 0, // A: pri, tries, success, slot_validity, RIs
+ 14,
+ 0,
+ 1,
+ SV_OK,
+ 2,
+ 2, // B: pri, tries, success, slot_validity, RIs
+ MakeRollbackIndexes(2, 2)); // stored_rollback_indexes
+ EXPECT_EQ(AVB_AB_FLOW_RESULT_OK,
+ avb_ab_flow(ops_.avb_ab_ops(),
+ requested_partitions,
+ false /* allow_verification_error */,
+ &data));
+ ExpMD(0,
+ 0,
+ 0, // A: pri, tries, successful
+ 14,
+ 0,
+ 1, // B: pri, tries, successful
+ MakeRollbackIndexes(2, 2)); // stored_rollback_indexes
+ ASSERT_NE(nullptr, data);
+ EXPECT_EQ("_b", std::string(data->ab_suffix));
+ avb_slot_verify_data_free(data);
+}
+
+TEST_F(AvbABFlowTest, StoredRollbackIndexBumped) {
+ AvbSlotVerifyData* data;
+ const char* requested_partitions[] = {"boot", NULL};
+
+ SetMD(15,
+ 0,
+ 1,
+ SV_OK,
+ 3,
+ 3, // A: pri, tries, success, slot_validity, RIs
+ 14,
+ 0,
+ 1,
+ SV_OK,
+ 3,
+ 3, // B: pri, tries, success, slot_validity, RIs
+ MakeRollbackIndexes(2, 2)); // stored_rollback_indexes
+ EXPECT_EQ(AVB_AB_FLOW_RESULT_OK,
+ avb_ab_flow(ops_.avb_ab_ops(),
+ requested_partitions,
+ false /* allow_verification_error */,
+ &data));
+ ExpMD(15,
+ 0,
+ 1, // A: pri, tries, successful
+ 14,
+ 0,
+ 1, // B: pri, tries, successful
+ MakeRollbackIndexes(3, 3)); // stored_rollback_indexes
+ ASSERT_NE(nullptr, data);
+ EXPECT_EQ("_a", std::string(data->ab_suffix));
+ avb_slot_verify_data_free(data);
+
+ // The case where different partitions have different rollback
+ // index values.
+ SetMD(15,
+ 0,
+ 1,
+ SV_OK,
+ 4,
+ 9, // A: pri, tries, success, slot_validity, RIs
+ 14,
+ 0,
+ 1,
+ SV_OK,
+ 5,
+ 7, // B: pri, tries, success, slot_validity, RIs
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+ EXPECT_EQ(AVB_AB_FLOW_RESULT_OK,
+ avb_ab_flow(ops_.avb_ab_ops(),
+ requested_partitions,
+ false /* allow_verification_error */,
+ &data));
+ ExpMD(15,
+ 0,
+ 1, // A: pri, tries, successful
+ 14,
+ 0,
+ 1, // B: pri, tries, successful
+ MakeRollbackIndexes(4, 7)); // stored_rollback_indexes
+ ASSERT_NE(nullptr, data);
+ EXPECT_EQ("_a", std::string(data->ab_suffix));
+ avb_slot_verify_data_free(data);
+
+ // If the slot with the low RI fails verification (or is invalid),
+ // check that these low Rollback Indexs are not taken into account
+ // after marking it as unbootable.
+ SetMD(15,
+ 0,
+ 1,
+ SV_INV,
+ 4,
+ 9, // A: pri, tries, success, slot_validity, RIs
+ 14,
+ 0,
+ 1,
+ SV_OK,
+ 5,
+ 7, // B: pri, tries, success, slot_validity, RIs
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+ EXPECT_EQ(AVB_AB_FLOW_RESULT_OK,
+ avb_ab_flow(ops_.avb_ab_ops(),
+ requested_partitions,
+ false /* allow_verification_error */,
+ &data));
+ ExpMD(0,
+ 0,
+ 0, // A: pri, tries, successful
+ 14,
+ 0,
+ 1, // B: pri, tries, successful
+ MakeRollbackIndexes(5, 7)); // stored_rollback_indexes
+ ASSERT_NE(nullptr, data);
+ EXPECT_EQ("_b", std::string(data->ab_suffix));
+ avb_slot_verify_data_free(data);
+}
+
+TEST_F(AvbABFlowTest, MarkSlotActive) {
+ SetMD(15,
+ 0,
+ 1,
+ SV_INV,
+ 0,
+ 0, // A: pri, tries, success, slot_validity, RIs
+ 11,
+ 0,
+ 1,
+ SV_OK,
+ 0,
+ 0, // B: pri, tries, success, slot_validity, RIs
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+ EXPECT_EQ(AVB_IO_RESULT_OK, avb_ab_mark_slot_active(ops_.avb_ab_ops(), 0));
+ ExpMD(15,
+ 7,
+ 0, // A: pri, tries, successful
+ 11,
+ 0,
+ 1, // B: pri, tries, successful
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+
+ // Note how priority of slot A is altered to make room for newly
+ // activated slot.
+ SetMD(15,
+ 0,
+ 1,
+ SV_INV,
+ 0,
+ 0, // A: pri, tries, success, slot_validity, RIs
+ 14,
+ 0,
+ 1,
+ SV_OK,
+ 0,
+ 0, // B: pri, tries, success, slot_validity, RIs
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+ EXPECT_EQ(AVB_IO_RESULT_OK, avb_ab_mark_slot_active(ops_.avb_ab_ops(), 1));
+ ExpMD(14,
+ 0,
+ 1, // A: pri, tries, successful
+ 15,
+ 7,
+ 0, // B: pri, tries, successful
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+}
+
+TEST_F(AvbABFlowTest, MarkSlotUnbootable) {
+ SetMD(15,
+ 0,
+ 1,
+ SV_INV,
+ 0,
+ 0, // A: pri, tries, success, slot_validity, RIs
+ 11,
+ 0,
+ 1,
+ SV_OK,
+ 0,
+ 0, // B: pri, tries, success, slot_validity, RIs
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+ EXPECT_EQ(AVB_IO_RESULT_OK,
+ avb_ab_mark_slot_unbootable(ops_.avb_ab_ops(), 0));
+ ExpMD(0,
+ 0,
+ 0, // A: pri, tries, successful
+ 11,
+ 0,
+ 1, // B: pri, tries, successful
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+
+ SetMD(15,
+ 0,
+ 1,
+ SV_INV,
+ 0,
+ 0, // A: pri, tries, success, slot_validity, RIs
+ 14,
+ 0,
+ 1,
+ SV_OK,
+ 0,
+ 0, // B: pri, tries, success, slot_validity, RIs
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+ EXPECT_EQ(AVB_IO_RESULT_OK,
+ avb_ab_mark_slot_unbootable(ops_.avb_ab_ops(), 1));
+ ExpMD(15,
+ 0,
+ 1, // A: pri, tries, successful
+ 0,
+ 0,
+ 0, // B: pri, tries, successful
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+}
+
+TEST_F(AvbABFlowTest, MarkSlotSuccessful) {
+ SetMD(15,
+ 5,
+ 0,
+ SV_INV,
+ 0,
+ 0, // A: pri, tries, success, slot_validity, RIs
+ 11,
+ 3,
+ 0,
+ SV_OK,
+ 0,
+ 0, // B: pri, tries, success, slot_validity, RIs
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+ EXPECT_EQ(AVB_IO_RESULT_OK,
+ avb_ab_mark_slot_successful(ops_.avb_ab_ops(), 0));
+ ExpMD(15,
+ 0,
+ 1, // A: pri, tries, successful
+ 11,
+ 3,
+ 0, // B: pri, tries, successful
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+
+ SetMD(15,
+ 5,
+ 0,
+ SV_INV,
+ 0,
+ 0, // A: pri, tries, success, slot_validity, RIs
+ 14,
+ 0,
+ 1,
+ SV_OK,
+ 0,
+ 0, // B: pri, tries, success, slot_validity, RIs
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+ EXPECT_EQ(AVB_IO_RESULT_OK,
+ avb_ab_mark_slot_successful(ops_.avb_ab_ops(), 1));
+ ExpMD(15,
+ 5,
+ 0, // A: pri, tries, successful
+ 14,
+ 0,
+ 1, // B: pri, tries, successful
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+
+ // Marking an unbootable slot (A) as successful won't work (it's a
+ // programmer error to do so)... notice however that the unbootable
+ // slot is normalized in the process.
+ SetMD(0,
+ 3,
+ 2,
+ SV_INV,
+ 0,
+ 0, // A: pri, tries, success, slot_validity, RIs
+ 14,
+ 0,
+ 1,
+ SV_OK,
+ 0,
+ 0, // B: pri, tries, success, slot_validity, RIs
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+ EXPECT_EQ(AVB_IO_RESULT_OK,
+ avb_ab_mark_slot_successful(ops_.avb_ab_ops(), 0));
+ ExpMD(0,
+ 0,
+ 0, // A: pri, tries, successful
+ 14,
+ 0,
+ 1, // B: pri, tries, successful
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+}
+
+static AvbABData my_serialized_data;
+
+static AvbIOResult my_write_ab_metadata(AvbABOps* ops,
+ const struct AvbABData* data) {
+ avb_ab_data_update_crc_and_byteswap(data, &my_serialized_data);
+ return AVB_IO_RESULT_OK;
+}
+
+static AvbIOResult my_read_ab_metadata(AvbABOps* ops, struct AvbABData* data) {
+ if (!avb_ab_data_verify_and_byteswap(&my_serialized_data, data)) {
+ avb_error(
+ "Error validating A/B metadata from persistent storage. "
+ "Resetting and writing new A/B metadata to persistent storage.\n");
+ avb_ab_data_init(data);
+ return my_write_ab_metadata(ops, data);
+ }
+ return AVB_IO_RESULT_OK;
+}
+
+TEST_F(AvbABFlowTest, OtherMetadataStorage) {
+ AvbSlotVerifyData* data;
+ const char* requested_partitions[] = {"boot", NULL};
+
+ // Use our own A/B storage routines (see above).
+ ops_.avb_ab_ops()->read_ab_metadata = my_read_ab_metadata;
+ ops_.avb_ab_ops()->write_ab_metadata = my_write_ab_metadata;
+
+ SetMD(14,
+ 0,
+ 1,
+ SV_OK,
+ 0,
+ 0, // A: pri, tries, success, slot_validity, RIs
+ 15,
+ 0,
+ 1,
+ SV_OK,
+ 0,
+ 0, // B: pri, tries, success, slot_validity, RIs
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+ EXPECT_EQ(AVB_AB_FLOW_RESULT_OK,
+ avb_ab_flow(ops_.avb_ab_ops(),
+ requested_partitions,
+ false /* allow_verification_error */,
+ &data));
+ ExpMD(14,
+ 0,
+ 1, // A: pri, tries, successful
+ 15,
+ 0,
+ 1, // B: pri, tries, successful
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+ ASSERT_NE(nullptr, data);
+ EXPECT_EQ("_b", std::string(data->ab_suffix));
+ avb_slot_verify_data_free(data);
+
+ // Also check the other slot.
+ SetMD(15,
+ 0,
+ 1,
+ SV_OK,
+ 0,
+ 0, // A: pri, tries, success, slot_validity, RIs
+ 14,
+ 0,
+ 1,
+ SV_OK,
+ 0,
+ 0, // B: pri, tries, success, slot_validity, RIs
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+ EXPECT_EQ(AVB_AB_FLOW_RESULT_OK,
+ avb_ab_flow(ops_.avb_ab_ops(),
+ requested_partitions,
+ false /* allow_verification_error */,
+ &data));
+ ExpMD(15,
+ 0,
+ 1, // A: pri, tries, successful
+ 14,
+ 0,
+ 1, // B: pri, tries, successful
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+ ASSERT_NE(nullptr, data);
+ EXPECT_EQ("_a", std::string(data->ab_suffix));
+ avb_slot_verify_data_free(data);
+
+ // Check that 'misc' hasn't been written to at all.
+ std::string misc_data;
+ base::FilePath misc_path = testdir_.Append("misc.img");
+ ASSERT_TRUE(base::ReadFileToString(misc_path, &misc_data));
+ EXPECT_EQ(size_t(MISC_PART_SIZE), misc_data.size());
+ for (size_t n = 0; n < misc_data.size(); n++) {
+ ASSERT_EQ(uint8_t(misc_data[n]), 0);
+ }
+}
+
+TEST_F(AvbABFlowTest, UnlockedUnverifiedSlot) {
+ AvbSlotVerifyData* data;
+ const char* requested_partitions[] = {"boot", NULL};
+
+ SetMD(14,
+ 0,
+ 1,
+ SV_OK,
+ 0,
+ 0, // A: pri, tries, success, slot_validity, RIs
+ 15,
+ 0,
+ 1,
+ SV_UNV,
+ 0,
+ 0, // B: pri, tries, success, slot_validity, RIs
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+ EXPECT_EQ(AVB_AB_FLOW_RESULT_OK_WITH_VERIFICATION_ERROR,
+ avb_ab_flow(ops_.avb_ab_ops(),
+ requested_partitions,
+ true /* allow_verification_error */,
+ &data));
+ ExpMD(14,
+ 0,
+ 1, // A: pri, tries, successful
+ 15,
+ 0,
+ 1, // B: pri, tries, successful
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+ ASSERT_NE(nullptr, data);
+ EXPECT_EQ("_b", std::string(data->ab_suffix));
+ avb_slot_verify_data_free(data);
+
+ // Also check the other slot.
+ SetMD(15,
+ 0,
+ 1,
+ SV_UNV,
+ 0,
+ 0, // A: pri, tries, success, slot_validity, RIs
+ 14,
+ 0,
+ 1,
+ SV_OK,
+ 0,
+ 0, // B: pri, tries, success, slot_validity, RIs
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+ EXPECT_EQ(AVB_AB_FLOW_RESULT_OK_WITH_VERIFICATION_ERROR,
+ avb_ab_flow(ops_.avb_ab_ops(),
+ requested_partitions,
+ true /* allow_verification_error */,
+ &data));
+ ExpMD(15,
+ 0,
+ 1, // A: pri, tries, successful
+ 14,
+ 0,
+ 1, // B: pri, tries, successful
+ MakeRollbackIndexes(0, 0)); // stored_rollback_indexes
+ ASSERT_NE(nullptr, data);
+ EXPECT_EQ("_a", std::string(data->ab_suffix));
+ avb_slot_verify_data_free(data);
+}
+
+TEST_F(AvbABFlowTest, AvbtoolMetadataGeneratorEmptyFile) {
+ AvbABData data;
+
+ base::FilePath misc_path = testdir_.Append("misc.img");
+ EXPECT_COMMAND(0,
+ "./avbtool set_ab_metadata"
+ " --misc_image %s"
+ " --slot_data 13:3:0:11:2:1",
+ misc_path.value().c_str());
+
+ EXPECT_EQ(AVB_IO_RESULT_OK,
+ ops_.avb_ab_ops()->read_ab_metadata(ops_.avb_ab_ops(), &data));
+ EXPECT_EQ(13, data.slots[0].priority);
+ EXPECT_EQ(3, data.slots[0].tries_remaining);
+ EXPECT_EQ(0, data.slots[0].successful_boot);
+ EXPECT_EQ(11, data.slots[1].priority);
+ EXPECT_EQ(2, data.slots[1].tries_remaining);
+ EXPECT_EQ(1, data.slots[1].successful_boot);
+}
+
+TEST_F(AvbABFlowTest, AvbtoolMetadataGeneratorExistingFile) {
+ AvbABData data;
+ size_t n;
+
+ size_t misc_size = 1024 * 1024;
+ base::FilePath misc_path = GenerateImage("misc.img", misc_size);
+ EXPECT_COMMAND(0,
+ "./avbtool set_ab_metadata"
+ " --misc_image %s"
+ " --slot_data 12:2:1:10:5:0",
+ misc_path.value().c_str());
+
+ EXPECT_EQ(AVB_IO_RESULT_OK,
+ ops_.avb_ab_ops()->read_ab_metadata(ops_.avb_ab_ops(), &data));
+ EXPECT_EQ(12, data.slots[0].priority);
+ EXPECT_EQ(2, data.slots[0].tries_remaining);
+ EXPECT_EQ(1, data.slots[0].successful_boot);
+ EXPECT_EQ(10, data.slots[1].priority);
+ EXPECT_EQ(5, data.slots[1].tries_remaining);
+ EXPECT_EQ(0, data.slots[1].successful_boot);
+
+ std::string misc_data;
+ ASSERT_TRUE(base::ReadFileToString(misc_path, &misc_data));
+ EXPECT_EQ(misc_size, misc_data.size());
+ for (n = 0; n < 2048; n++) {
+ ASSERT_EQ(uint8_t(misc_data[n]), uint8_t(n));
+ }
+ for (n = 2048 + 32; n < misc_data.size(); n++) {
+ ASSERT_EQ(uint8_t(misc_data[n]), uint8_t(n));
+ }
+}
+
+} // namespace avb
diff --git a/avb/test/avb_atx_validate_unittest.cc b/avb/test/avb_atx_validate_unittest.cc
new file mode 100644
index 0000000..6efa856
--- /dev/null
+++ b/avb/test/avb_atx_validate_unittest.cc
@@ -0,0 +1,689 @@
+/*
+ * 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 <stdio.h>
+#include <string.h>
+
+#include <base/files/file_util.h>
+#include <gtest/gtest.h>
+#include <openssl/objects.h>
+#include <openssl/pem.h>
+#include <openssl/rsa.h>
+#include <openssl/sha.h>
+
+#include <libavb_atx/libavb_atx.h>
+
+#include "avb_unittest_util.h"
+#include "fake_avb_ops.h"
+
+namespace {
+
+const char kMetadataPath[] = "test/data/atx_metadata.bin";
+const char kPermanentAttributesPath[] =
+ "test/data/atx_permanent_attributes.bin";
+const char kPRKPrivateKeyPath[] = "test/data/testkey_rsa4096.pem";
+const char kPIKPrivateKeyPath[] = "test/data/testkey_rsa2048.pem";
+
+class ScopedRSA {
+ public:
+ ScopedRSA(const char* pem_key_path) {
+ FILE* file = fopen(pem_key_path, "r");
+ rsa_ = PEM_read_RSAPrivateKey(file, nullptr, nullptr, nullptr);
+ fclose(file);
+ }
+
+ ~ScopedRSA() {
+ if (rsa_) {
+ RSA_free(rsa_);
+ }
+ }
+
+ // PKCS #1 v1.5 signature using SHA512 if |rsa_| is 4096 bits, SHA256
+ // otherwise. Returns true on success.
+ bool Sign(const void* data_to_sign, size_t length, uint8_t signature[]) {
+ bool use_sha512 = (RSA_size(rsa_) == AVB_RSA4096_NUM_BYTES);
+ uint8_t digest[AVB_SHA512_DIGEST_SIZE];
+ unsigned int digest_size =
+ use_sha512 ? AVB_SHA512_DIGEST_SIZE : AVB_SHA256_DIGEST_SIZE;
+ const unsigned char* data_to_sign_buf =
+ reinterpret_cast<const unsigned char*>(data_to_sign);
+ if (use_sha512) {
+ SHA512(data_to_sign_buf, length, digest);
+ } else {
+ SHA256(data_to_sign_buf, length, digest);
+ }
+ unsigned int signature_length = 0;
+ return (1 == RSA_sign(use_sha512 ? NID_sha512 : NID_sha256,
+ digest,
+ digest_size,
+ signature,
+ &signature_length,
+ rsa_));
+ }
+
+ private:
+ RSA* rsa_;
+};
+
+} /* namespace */
+
+namespace avb {
+
+class AvbAtxValidateTest : public ::testing::Test, public FakeAvbOpsDelegate {
+ public:
+ ~AvbAtxValidateTest() override {}
+
+ void SetUp() override {
+ ReadDefaultData();
+ ops_.set_delegate(this);
+ ops_.set_permanent_attributes(attributes_);
+ ops_.set_stored_rollback_indexes({{AVB_ATX_PIK_VERSION_LOCATION, 0},
+ {AVB_ATX_PSK_VERSION_LOCATION, 0},
+ {AVB_ATX_GSK_VERSION_LOCATION, 0}});
+ }
+
+ // FakeAvbOpsDelegate methods.
+ AvbIOResult read_from_partition(const char* partition,
+ int64_t offset,
+ size_t num_bytes,
+ void* buffer,
+ size_t* out_num_read) override {
+ // Expect method not used.
+ return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
+ }
+
+ AvbIOResult write_to_partition(const char* partition,
+ int64_t offset,
+ size_t num_bytes,
+ const void* buffer) override {
+ // Expect method not used.
+ return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
+ }
+
+ AvbIOResult validate_vbmeta_public_key(AvbOps* ops,
+ const uint8_t* public_key_data,
+ size_t public_key_length,
+ const uint8_t* public_key_metadata,
+ size_t public_key_metadata_length,
+ bool* out_key_is_trusted) override {
+ // Expect method not used.
+ return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
+ }
+
+ AvbIOResult read_rollback_index(AvbOps* ops,
+ size_t rollback_index_slot,
+ uint64_t* out_rollback_index) override {
+ if ((fail_read_pik_rollback_index_ &&
+ rollback_index_slot == AVB_ATX_PIK_VERSION_LOCATION) ||
+ (fail_read_psk_rollback_index_ &&
+ rollback_index_slot == AVB_ATX_PSK_VERSION_LOCATION) ||
+ (fail_read_gsk_rollback_index_ &&
+ rollback_index_slot == AVB_ATX_GSK_VERSION_LOCATION)) {
+ return AVB_IO_RESULT_ERROR_IO;
+ }
+ return ops_.read_rollback_index(
+ ops, rollback_index_slot, out_rollback_index);
+ }
+
+ AvbIOResult write_rollback_index(AvbOps* ops,
+ size_t rollback_index_slot,
+ uint64_t rollback_index) override {
+ // Expect method not used.
+ return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
+ }
+
+ AvbIOResult read_is_device_unlocked(AvbOps* ops,
+ bool* out_is_device_unlocked) override {
+ // Expect method not used.
+ return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
+ }
+
+ AvbIOResult get_unique_guid_for_partition(AvbOps* ops,
+ const char* partition,
+ char* guid_buf,
+ size_t guid_buf_size) override {
+ // Expect method not used.
+ return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
+ }
+
+ AvbIOResult read_permanent_attributes(
+ AvbAtxPermanentAttributes* attributes) override {
+ if (fail_read_permanent_attributes_) {
+ return AVB_IO_RESULT_ERROR_IO;
+ }
+ return ops_.read_permanent_attributes(attributes);
+ }
+
+ AvbIOResult read_permanent_attributes_hash(
+ uint8_t hash[AVB_SHA256_DIGEST_SIZE]) override {
+ if (fail_read_permanent_attributes_hash_) {
+ return AVB_IO_RESULT_ERROR_IO;
+ }
+ return ops_.read_permanent_attributes_hash(hash);
+ }
+
+ protected:
+ virtual AvbIOResult Validate(bool* is_trusted) {
+ return avb_atx_validate_vbmeta_public_key(
+ ops_.avb_ops(),
+ metadata_.product_signing_key_certificate.signed_data.public_key,
+ AVB_ATX_PUBLIC_KEY_SIZE_2048,
+ reinterpret_cast<const uint8_t*>(&metadata_),
+ sizeof(metadata_),
+ is_trusted);
+ }
+
+ void SignPIKCertificate() {
+ memset(metadata_.product_intermediate_key_certificate.signature,
+ 0,
+ AVB_RSA4096_NUM_BYTES);
+ ScopedRSA key(kPRKPrivateKeyPath);
+ ASSERT_TRUE(
+ key.Sign(&metadata_.product_intermediate_key_certificate.signed_data,
+ sizeof(AvbAtxCertificateSignedData),
+ metadata_.product_intermediate_key_certificate.signature));
+ }
+
+ void SignPSKCertificate() {
+ memset(metadata_.product_signing_key_certificate.signature,
+ 0,
+ AVB_RSA2048_NUM_BYTES);
+ ScopedRSA key(kPIKPrivateKeyPath);
+ ASSERT_TRUE(key.Sign(&metadata_.product_signing_key_certificate.signed_data,
+ sizeof(AvbAtxCertificateSignedData),
+ metadata_.product_signing_key_certificate.signature));
+ }
+
+ FakeAvbOps ops_;
+ AvbAtxPermanentAttributes attributes_;
+ AvbAtxPublicKeyMetadata metadata_;
+ bool fail_read_permanent_attributes_{false};
+ bool fail_read_permanent_attributes_hash_{false};
+ bool fail_read_pik_rollback_index_{false};
+ bool fail_read_psk_rollback_index_{false};
+ bool fail_read_gsk_rollback_index_{false};
+
+ private:
+ void ReadDefaultData() {
+ std::string tmp;
+ ASSERT_TRUE(base::ReadFileToString(base::FilePath(kMetadataPath), &tmp));
+ ASSERT_EQ(tmp.size(), sizeof(AvbAtxPublicKeyMetadata));
+ memcpy(&metadata_, tmp.data(), tmp.size());
+ ASSERT_TRUE(
+ base::ReadFileToString(base::FilePath(kPermanentAttributesPath), &tmp));
+ ASSERT_EQ(tmp.size(), sizeof(AvbAtxPermanentAttributes));
+ memcpy(&attributes_, tmp.data(), tmp.size());
+ }
+};
+
+TEST_F(AvbAtxValidateTest, Success) {
+ bool is_trusted = false;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_TRUE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, SuccessAfterNewSign) {
+ std::string old_pik_sig(
+ reinterpret_cast<char*>(
+ metadata_.product_intermediate_key_certificate.signature),
+ AVB_RSA4096_NUM_BYTES);
+ std::string old_psk_sig(
+ reinterpret_cast<char*>(
+ metadata_.product_signing_key_certificate.signature),
+ AVB_RSA2048_NUM_BYTES);
+ SignPIKCertificate();
+ SignPSKCertificate();
+ std::string new_pik_sig(
+ reinterpret_cast<char*>(
+ metadata_.product_intermediate_key_certificate.signature),
+ AVB_RSA4096_NUM_BYTES);
+ std::string new_psk_sig(
+ reinterpret_cast<char*>(
+ metadata_.product_signing_key_certificate.signature),
+ AVB_RSA2048_NUM_BYTES);
+ EXPECT_EQ(old_pik_sig, new_pik_sig);
+ EXPECT_EQ(old_psk_sig, new_psk_sig);
+ bool is_trusted = false;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_TRUE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, FailReadPermamentAttributes) {
+ fail_read_permanent_attributes_ = true;
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_ERROR_IO, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, FailReadPermamentAttributesHash) {
+ fail_read_permanent_attributes_hash_ = true;
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_ERROR_IO, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, UnsupportedPermanentAttributesVersion) {
+ attributes_.version = 25;
+ ops_.set_permanent_attributes(attributes_);
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, PermanentAttributesHashMismatch) {
+ ops_.set_permanent_attributes_hash("bad_hash");
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+// A fixture with parameterized metadata length.
+class AvbAtxValidateTestWithMetadataLength
+ : public AvbAtxValidateTest,
+ public ::testing::WithParamInterface<size_t> {
+ protected:
+ AvbIOResult Validate(bool* is_trusted) override {
+ return avb_atx_validate_vbmeta_public_key(
+ ops_.avb_ops(),
+ metadata_.product_signing_key_certificate.signed_data.public_key,
+ AVB_ATX_PUBLIC_KEY_SIZE_2048,
+ reinterpret_cast<const uint8_t*>(&metadata_),
+ GetParam(),
+ is_trusted);
+ }
+};
+
+TEST_P(AvbAtxValidateTestWithMetadataLength, InvalidMetadataLength) {
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+// Test a bunch of invalid metadata length values.
+INSTANTIATE_TEST_CASE_P(P,
+ AvbAtxValidateTestWithMetadataLength,
+ ::testing::Values(0,
+ 1,
+ sizeof(AvbAtxPublicKeyMetadata) - 1,
+ sizeof(AvbAtxPublicKeyMetadata) + 1,
+ -1));
+
+TEST_F(AvbAtxValidateTest, UnsupportedMetadataVersion) {
+ metadata_.version = 25;
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, FailReadPIKRollbackIndex) {
+ fail_read_pik_rollback_index_ = true;
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_ERROR_IO, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, UnsupportedPIKCertificateVersion) {
+ metadata_.product_intermediate_key_certificate.signed_data.version = 25;
+ SignPIKCertificate();
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, BadPIKCert_ModifiedSubjectPublicKey) {
+ metadata_.product_intermediate_key_certificate.signed_data.public_key[0] ^= 1;
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, BadPIKCert_ModifiedSubject) {
+ metadata_.product_intermediate_key_certificate.signed_data.subject[0] ^= 1;
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, BadPIKCert_ModifiedUsage) {
+ metadata_.product_intermediate_key_certificate.signed_data.usage[0] ^= 1;
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, BadPIKCert_ModifiedKeyVersion) {
+ metadata_.product_intermediate_key_certificate.signed_data.key_version ^= 1;
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, BadPIKCert_BadSignature) {
+ metadata_.product_intermediate_key_certificate.signature[0] ^= 1;
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, PIKCertSubjectIgnored) {
+ metadata_.product_intermediate_key_certificate.signed_data.subject[0] ^= 1;
+ SignPIKCertificate();
+ bool is_trusted = false;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_TRUE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, PIKCertUnexpectedUsage) {
+ metadata_.product_intermediate_key_certificate.signed_data.usage[0] ^= 1;
+ SignPIKCertificate();
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, PIKRollback) {
+ ops_.set_stored_rollback_indexes(
+ {{AVB_ATX_PIK_VERSION_LOCATION,
+ metadata_.product_intermediate_key_certificate.signed_data.key_version +
+ 1},
+ {AVB_ATX_PSK_VERSION_LOCATION, 0},
+ {AVB_ATX_GSK_VERSION_LOCATION, 0}});
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, FailReadPSKRollbackIndex) {
+ fail_read_psk_rollback_index_ = true;
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_ERROR_IO, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, UnsupportedPSKCertificateVersion) {
+ metadata_.product_signing_key_certificate.signed_data.version = 25;
+ SignPSKCertificate();
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, BadPSKCert_ModifiedSubjectPublicKey) {
+ metadata_.product_signing_key_certificate.signed_data.public_key[0] ^= 1;
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, BadPSKCert_ModifiedSubject) {
+ metadata_.product_signing_key_certificate.signed_data.subject[0] ^= 1;
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, BadPSKCert_ModifiedUsage) {
+ metadata_.product_signing_key_certificate.signed_data.usage[0] ^= 1;
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, BadPSKCert_ModifiedKeyVersion) {
+ metadata_.product_signing_key_certificate.signed_data.key_version ^= 1;
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, BadPSKCert_BadSignature) {
+ metadata_.product_signing_key_certificate.signature[0] ^= 1;
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, PSKCertUnexpectedSubject) {
+ metadata_.product_signing_key_certificate.signed_data.subject[0] ^= 1;
+ SignPSKCertificate();
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, PSKCertUnexpectedUsage) {
+ metadata_.product_signing_key_certificate.signed_data.usage[0] ^= 1;
+ SignPSKCertificate();
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, PSKRollback) {
+ ops_.set_stored_rollback_indexes(
+ {{AVB_ATX_PIK_VERSION_LOCATION, 0},
+ {AVB_ATX_PSK_VERSION_LOCATION,
+ metadata_.product_signing_key_certificate.signed_data.key_version + 1},
+ {AVB_ATX_GSK_VERSION_LOCATION, 0}});
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+// A fixture with parameterized public key length.
+class AvbAtxValidateTestWithPublicKeyLength
+ : public AvbAtxValidateTest,
+ public ::testing::WithParamInterface<size_t> {
+ protected:
+ AvbIOResult Validate(bool* is_trusted) override {
+ return avb_atx_validate_vbmeta_public_key(
+ ops_.avb_ops(),
+ metadata_.product_signing_key_certificate.signed_data.public_key,
+ GetParam(),
+ reinterpret_cast<const uint8_t*>(&metadata_),
+ sizeof(metadata_),
+ is_trusted);
+ }
+};
+
+TEST_P(AvbAtxValidateTestWithPublicKeyLength, InvalidPublicKeyLength) {
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+// Test a bunch of invalid public key length values.
+INSTANTIATE_TEST_CASE_P(P,
+ AvbAtxValidateTestWithPublicKeyLength,
+ ::testing::Values(0,
+ 1,
+ AVB_ATX_PUBLIC_KEY_SIZE_2048 - 1,
+ AVB_ATX_PUBLIC_KEY_SIZE_2048 + 1,
+ AVB_ATX_PUBLIC_KEY_SIZE_4096,
+ -1));
+
+TEST_F(AvbAtxValidateTest, PSKMismatch) {
+ uint8_t bad_key[AVB_ATX_PUBLIC_KEY_SIZE_2048] = {};
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_OK,
+ avb_atx_validate_vbmeta_public_key(
+ ops_.avb_ops(),
+ bad_key,
+ AVB_ATX_PUBLIC_KEY_SIZE_2048,
+ reinterpret_cast<const uint8_t*>(&metadata_),
+ sizeof(metadata_),
+ &is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, FailReadGSKRollbackIndex) {
+ fail_read_gsk_rollback_index_ = true;
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_ERROR_IO, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, GSKRollback) {
+ ops_.set_stored_rollback_indexes(
+ {{AVB_ATX_PIK_VERSION_LOCATION, 0},
+ {AVB_ATX_PSK_VERSION_LOCATION, 0},
+ {AVB_ATX_GSK_VERSION_LOCATION,
+ metadata_.google_signing_key_version + 1}});
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+// A fixture for testing avb_slot_verify() with ATX.
+class AvbAtxSlotVerifyTest : public BaseAvbToolTest, public FakeAvbOpsDelegate {
+ public:
+ ~AvbAtxSlotVerifyTest() override = default;
+
+ void SetUp() override {
+ BaseAvbToolTest::SetUp();
+ ReadAtxDefaultData();
+ ops_.set_partition_dir(testdir_);
+ ops_.set_delegate(this);
+ ops_.set_permanent_attributes(attributes_);
+ ops_.set_stored_rollback_indexes({{0, 0},
+ {1, 0},
+ {2, 0},
+ {3, 0},
+ {AVB_ATX_PIK_VERSION_LOCATION, 0},
+ {AVB_ATX_PSK_VERSION_LOCATION, 0},
+ {AVB_ATX_GSK_VERSION_LOCATION, 0}});
+ ops_.set_stored_is_device_unlocked(false);
+ }
+
+ // FakeAvbOpsDelegate methods. All forward to FakeAvbOps default except for
+ // validate_vbmeta_public_key().
+ AvbIOResult read_from_partition(const char* partition,
+ int64_t offset,
+ size_t num_bytes,
+ void* buffer,
+ size_t* out_num_read) override {
+ return ops_.read_from_partition(
+ partition, offset, num_bytes, buffer, out_num_read);
+ }
+
+ AvbIOResult write_to_partition(const char* partition,
+ int64_t offset,
+ size_t num_bytes,
+ const void* buffer) override {
+ return ops_.write_to_partition(partition, offset, num_bytes, buffer);
+ }
+
+ AvbIOResult validate_vbmeta_public_key(AvbOps* ops,
+ const uint8_t* public_key_data,
+ size_t public_key_length,
+ const uint8_t* public_key_metadata,
+ size_t public_key_metadata_length,
+ bool* out_key_is_trusted) override {
+ // Send to ATX implementation.
+ ++num_atx_calls_;
+ return avb_atx_validate_vbmeta_public_key(ops_.avb_ops(),
+ public_key_data,
+ public_key_length,
+ public_key_metadata,
+ public_key_metadata_length,
+ out_key_is_trusted);
+ }
+
+ AvbIOResult read_rollback_index(AvbOps* ops,
+ size_t rollback_index_slot,
+ uint64_t* out_rollback_index) override {
+ return ops_.read_rollback_index(
+ ops, rollback_index_slot, out_rollback_index);
+ }
+
+ AvbIOResult write_rollback_index(AvbOps* ops,
+ size_t rollback_index_slot,
+ uint64_t rollback_index) override {
+ return ops_.write_rollback_index(ops, rollback_index_slot, rollback_index);
+ }
+
+ AvbIOResult read_is_device_unlocked(AvbOps* ops,
+ bool* out_is_device_unlocked) override {
+ return ops_.read_is_device_unlocked(ops, out_is_device_unlocked);
+ }
+
+ AvbIOResult get_unique_guid_for_partition(AvbOps* ops,
+ const char* partition,
+ char* guid_buf,
+ size_t guid_buf_size) override {
+ return ops_.get_unique_guid_for_partition(
+ ops, partition, guid_buf, guid_buf_size);
+ }
+
+ AvbIOResult read_permanent_attributes(
+ AvbAtxPermanentAttributes* attributes) override {
+ return ops_.read_permanent_attributes(attributes);
+ }
+
+ AvbIOResult read_permanent_attributes_hash(
+ uint8_t hash[AVB_SHA256_DIGEST_SIZE]) override {
+ return ops_.read_permanent_attributes_hash(hash);
+ }
+
+ protected:
+ FakeAvbOps ops_;
+ AvbAtxPermanentAttributes attributes_;
+ int num_atx_calls_ = 0;
+
+ private:
+ void ReadAtxDefaultData() {
+ std::string tmp;
+ ASSERT_TRUE(
+ base::ReadFileToString(base::FilePath(kPermanentAttributesPath), &tmp));
+ ASSERT_EQ(tmp.size(), sizeof(AvbAtxPermanentAttributes));
+ memcpy(&attributes_, tmp.data(), tmp.size());
+ }
+};
+
+TEST_F(AvbAtxSlotVerifyTest, SlotVerifyWithAtx) {
+ std::string metadata_option = "--public_key_metadata=";
+ metadata_option += kMetadataPath;
+ GenerateVBMetaImage("vbmeta_a.img",
+ "SHA256_RSA2048",
+ 0,
+ base::FilePath("test/data/testkey_rsa2048.pem"),
+ metadata_option);
+
+ ops_.set_expected_public_key(
+ PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem")));
+
+ AvbSlotVerifyData* slot_data = NULL;
+ const char* requested_partitions[] = {"boot", NULL};
+ EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_OK,
+ avb_slot_verify(ops_.avb_ops(),
+ requested_partitions,
+ "_a",
+ false /* allow_verification_error */,
+ &slot_data));
+ EXPECT_NE(nullptr, slot_data);
+ avb_slot_verify_data_free(slot_data);
+ EXPECT_EQ(1, num_atx_calls_);
+}
+
+} // namespace avb
diff --git a/avb/test/avb_slot_verify_unittest.cc b/avb/test/avb_slot_verify_unittest.cc
new file mode 100644
index 0000000..51cfb23
--- /dev/null
+++ b/avb/test/avb_slot_verify_unittest.cc
@@ -0,0 +1,1243 @@
+/*
+ * 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 <base/files/file_util.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+
+#include "avb_unittest_util.h"
+#include "fake_avb_ops.h"
+
+namespace avb {
+
+class AvbSlotVerifyTest : public BaseAvbToolTest {
+ public:
+ AvbSlotVerifyTest() {}
+
+ virtual void SetUp() override {
+ BaseAvbToolTest::SetUp();
+ ops_.set_partition_dir(testdir_);
+ ops_.set_stored_rollback_indexes({{0, 0}, {1, 0}, {2, 0}, {3, 0}});
+ ops_.set_stored_is_device_unlocked(false);
+ }
+
+ void CmdlineWithHashtreeVerification(bool hashtree_verification_on);
+
+ FakeAvbOps ops_;
+};
+
+TEST_F(AvbSlotVerifyTest, Basic) {
+ GenerateVBMetaImage("vbmeta_a.img",
+ "SHA256_RSA2048",
+ 0,
+ base::FilePath("test/data/testkey_rsa2048.pem"));
+
+ ops_.set_expected_public_key(
+ PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem")));
+
+ AvbSlotVerifyData* slot_data = NULL;
+ const char* requested_partitions[] = {"boot", NULL};
+ EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_OK,
+ avb_slot_verify(ops_.avb_ops(),
+ requested_partitions,
+ "_a",
+ false /* allow_verification_error */,
+ &slot_data));
+ EXPECT_NE(nullptr, slot_data);
+ EXPECT_EQ(
+ "androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta_a "
+ "androidboot.slot_suffix=_a androidboot.vbmeta.device_state=locked "
+ "androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=1152 "
+ "androidboot.vbmeta.digest="
+ "4161a7e655eabe16c3fe714de5d43736e7c0a190cf08d36c946d2509ce071e4d",
+ std::string(slot_data->cmdline));
+ avb_slot_verify_data_free(slot_data);
+}
+
+TEST_F(AvbSlotVerifyTest, BasicSha512) {
+ GenerateVBMetaImage("vbmeta_a.img",
+ "SHA512_RSA2048",
+ 0,
+ base::FilePath("test/data/testkey_rsa2048.pem"));
+
+ ops_.set_expected_public_key(
+ PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem")));
+
+ AvbSlotVerifyData* slot_data = NULL;
+ const char* requested_partitions[] = {"boot", NULL};
+ EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_OK,
+ avb_slot_verify(ops_.avb_ops(),
+ requested_partitions,
+ "_a",
+ false /* allow_verification_error */,
+ &slot_data));
+ EXPECT_NE(nullptr, slot_data);
+ EXPECT_EQ(
+ "androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta_a "
+ "androidboot.slot_suffix=_a androidboot.vbmeta.device_state=locked "
+ "androidboot.vbmeta.hash_alg=sha512 androidboot.vbmeta.size=1152 "
+ "androidboot.vbmeta.digest="
+ "cb913d2f1a884f4e04c1db5bb181f3133fd16ac02fb367a20ef0776c0b07b3656ad1f081"
+ "e01932cf70f38b8960877470b448f1588dff022808387cc52fa77e77",
+ std::string(slot_data->cmdline));
+ avb_slot_verify_data_free(slot_data);
+}
+
+TEST_F(AvbSlotVerifyTest, BasicUnlocked) {
+ GenerateVBMetaImage("vbmeta_a.img",
+ "SHA256_RSA2048",
+ 0,
+ base::FilePath("test/data/testkey_rsa2048.pem"));
+
+ ops_.set_expected_public_key(
+ PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem")));
+
+ ops_.set_stored_is_device_unlocked(true);
+
+ AvbSlotVerifyData* slot_data = NULL;
+ const char* requested_partitions[] = {"boot", NULL};
+ EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_OK,
+ avb_slot_verify(ops_.avb_ops(),
+ requested_partitions,
+ "_a",
+ false /* allow_verification_error */,
+ &slot_data));
+ EXPECT_NE(nullptr, slot_data);
+ EXPECT_EQ(
+ "androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta_a "
+ "androidboot.slot_suffix=_a androidboot.vbmeta.device_state=unlocked "
+ "androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=1152 "
+ "androidboot.vbmeta.digest="
+ "4161a7e655eabe16c3fe714de5d43736e7c0a190cf08d36c946d2509ce071e4d",
+ std::string(slot_data->cmdline));
+ avb_slot_verify_data_free(slot_data);
+}
+
+TEST_F(AvbSlotVerifyTest, SlotDataIsCorrect) {
+ GenerateVBMetaImage("vbmeta_a.img",
+ "SHA256_RSA2048",
+ 0,
+ base::FilePath("test/data/testkey_rsa2048.pem"));
+
+ ops_.set_expected_public_key(
+ PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem")));
+
+ AvbSlotVerifyData* slot_data = NULL;
+ const char* requested_partitions[] = {"boot", NULL};
+ EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_OK,
+ avb_slot_verify(ops_.avb_ops(),
+ requested_partitions,
+ "_a",
+ false /* allow_verification_error */,
+ &slot_data));
+ EXPECT_NE(nullptr, slot_data);
+ avb_slot_verify_data_free(slot_data);
+}
+
+TEST_F(AvbSlotVerifyTest, WrongPublicKey) {
+ GenerateVBMetaImage("vbmeta_a.img",
+ "SHA256_RSA2048",
+ 0,
+ base::FilePath("test/data/testkey_rsa2048.pem"));
+
+ AvbSlotVerifyData* slot_data = NULL;
+ const char* requested_partitions[] = {"boot", NULL};
+ EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED,
+ avb_slot_verify(ops_.avb_ops(),
+ requested_partitions,
+ "_a",
+ false /* allow_verification_error */,
+ &slot_data));
+ EXPECT_EQ(nullptr, slot_data);
+ EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED,
+ avb_slot_verify(ops_.avb_ops(),
+ requested_partitions,
+ "_a",
+ true /* allow_verification_error */,
+ &slot_data));
+ EXPECT_NE(nullptr, slot_data);
+ avb_slot_verify_data_free(slot_data);
+}
+
+TEST_F(AvbSlotVerifyTest, NoImage) {
+ const char* requested_partitions[] = {"boot", NULL};
+ AvbSlotVerifyData* slot_data = NULL;
+ EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_ERROR_IO,
+ avb_slot_verify(ops_.avb_ops(),
+ requested_partitions,
+ "_a",
+ false /* allow_verification_error */,
+ &slot_data));
+ EXPECT_EQ(nullptr, slot_data);
+}
+
+TEST_F(AvbSlotVerifyTest, UnsignedVBMeta) {
+ GenerateVBMetaImage("vbmeta_a.img", "", 0, base::FilePath(""));
+
+ AvbSlotVerifyData* slot_data = NULL;
+ const char* requested_partitions[] = {"boot", NULL};
+ EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION,
+ avb_slot_verify(ops_.avb_ops(),
+ requested_partitions,
+ "_a",
+ false /* allow_verification_error */,
+ &slot_data));
+ EXPECT_EQ(nullptr, slot_data);
+ EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION,
+ avb_slot_verify(ops_.avb_ops(),
+ requested_partitions,
+ "_a",
+ true /* allow_verification_error */,
+ &slot_data));
+ EXPECT_NE(nullptr, slot_data);
+ avb_slot_verify_data_free(slot_data);
+}
+
+TEST_F(AvbSlotVerifyTest, CorruptedImage) {
+ GenerateVBMetaImage("vbmeta_a.img",
+ "SHA256_RSA2048",
+ 0,
+ base::FilePath("test/data/testkey_rsa2048.pem"));
+
+ // Corrupt four bytes of data in the end of the image. Since the aux
+ // data is at the end and this data is signed, this will change the
+ // value of the computed hash.
+ uint8_t corrupt_data[4] = {0xff, 0xff, 0xff, 0xff};
+ EXPECT_EQ(AVB_IO_RESULT_OK,
+ ops_.avb_ops()->write_to_partition(ops_.avb_ops(),
+ "vbmeta_a",
+ -4, // offset from end
+ sizeof corrupt_data,
+ corrupt_data));
+
+ AvbSlotVerifyData* slot_data = NULL;
+ const char* requested_partitions[] = {"boot", NULL};
+ EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION,
+ avb_slot_verify(ops_.avb_ops(),
+ requested_partitions,
+ "_a",
+ false /* allow_verification_error */,
+ &slot_data));
+ EXPECT_EQ(nullptr, slot_data);
+ EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION,
+ avb_slot_verify(ops_.avb_ops(),
+ requested_partitions,
+ "_a",
+ true /* allow_verification_error */,
+ &slot_data));
+ EXPECT_NE(nullptr, slot_data);
+ avb_slot_verify_data_free(slot_data);
+}
+
+TEST_F(AvbSlotVerifyTest, CorruptedMetadata) {
+ GenerateVBMetaImage("vbmeta_a.img",
+ "SHA256_RSA2048",
+ 0,
+ base::FilePath("test/data/testkey_rsa2048.pem"));
+
+ // Corrupt four bytes of data in the beginning of the image. Unlike
+ // the CorruptedImage test-case above (which is valid metadata) this
+ // will make the metadata invalid and render the slot unbootable
+ // even if the device is unlocked. Specifically no AvbSlotVerifyData
+ // is returned.
+ uint8_t corrupt_data[4] = {0xff, 0xff, 0xff, 0xff};
+ EXPECT_EQ(AVB_IO_RESULT_OK,
+ ops_.avb_ops()->write_to_partition(ops_.avb_ops(),
+ "vbmeta_a",
+ 0, // offset: beginning
+ sizeof corrupt_data,
+ corrupt_data));
+
+ AvbSlotVerifyData* slot_data = NULL;
+ const char* requested_partitions[] = {"boot", NULL};
+ EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA,
+ avb_slot_verify(ops_.avb_ops(),
+ requested_partitions,
+ "_a",
+ false /* allow_verification_error */,
+ &slot_data));
+ EXPECT_EQ(nullptr, slot_data);
+}
+
+TEST_F(AvbSlotVerifyTest, RollbackIndex) {
+ GenerateVBMetaImage("vbmeta_a.img",
+ "SHA256_RSA2048",
+ 42,
+ base::FilePath("test/data/testkey_rsa2048.pem"));
+
+ ops_.set_expected_public_key(
+ PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem")));
+
+ AvbSlotVerifyData* slot_data = NULL;
+ const char* requested_partitions[] = {"boot", NULL};
+
+ // First try with 42 as the stored rollback index - this should
+ // succeed since the image rollback index is 42 (as set above).
+ ops_.set_stored_rollback_indexes({{0, 42}});
+ EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_OK,
+ avb_slot_verify(ops_.avb_ops(),
+ requested_partitions,
+ "_a",
+ false /* allow_verification_error */,
+ &slot_data));
+ EXPECT_NE(nullptr, slot_data);
+ avb_slot_verify_data_free(slot_data);
+
+ // Then try with 43 for the stored rollback index - this should fail
+ // because the image has rollback index 42 which is less than 43.
+ ops_.set_stored_rollback_indexes({{0, 43}});
+ EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX,
+ avb_slot_verify(ops_.avb_ops(),
+ requested_partitions,
+ "_a",
+ false /* allow_verification_error */,
+ &slot_data));
+ EXPECT_EQ(nullptr, slot_data);
+ EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX,
+ avb_slot_verify(ops_.avb_ops(),
+ requested_partitions,
+ "_a",
+ true /* allow_verification_error */,
+ &slot_data));
+ EXPECT_NE(nullptr, slot_data);
+ avb_slot_verify_data_free(slot_data);
+}
+
+TEST_F(AvbSlotVerifyTest, HashDescriptorInVBMeta) {
+ const size_t boot_partition_size = 16 * 1024 * 1024;
+ const size_t boot_image_size = 5 * 1024 * 1024;
+ base::FilePath boot_path = GenerateImage("boot_a.img", boot_image_size);
+
+ EXPECT_COMMAND(
+ 0,
+ "./avbtool add_hash_footer"
+ " --image %s"
+ " --rollback_index 0"
+ " --partition_name boot"
+ " --partition_size %zd"
+ " --kernel_cmdline 'cmdline in hash footer $(ANDROID_SYSTEM_PARTUUID)'"
+ " --salt deadbeef",
+ boot_path.value().c_str(),
+ boot_partition_size);
+
+ GenerateVBMetaImage(
+ "vbmeta_a.img",
+ "SHA256_RSA2048",
+ 4,
+ base::FilePath("test/data/testkey_rsa2048.pem"),
+ base::StringPrintf(
+ "--include_descriptors_from_image %s"
+ " --kernel_cmdline 'cmdline in vbmeta $(ANDROID_BOOT_PARTUUID)'",
+ boot_path.value().c_str()));
+
+ EXPECT_EQ(
+ "VBMeta image version: 1.0\n"
+ "Header Block: 256 bytes\n"
+ "Authentication Block: 320 bytes\n"
+ "Auxiliary Block: 896 bytes\n"
+ "Algorithm: SHA256_RSA2048\n"
+ "Rollback Index: 4\n"
+ "Flags: 0\n"
+ "Descriptors:\n"
+ " Kernel Cmdline descriptor:\n"
+ " Flags: 0\n"
+ " Kernel Cmdline: 'cmdline in vbmeta "
+ "$(ANDROID_BOOT_PARTUUID)'\n"
+ " Hash descriptor:\n"
+ " Image Size: 5242880 bytes\n"
+ " Hash Algorithm: sha256\n"
+ " Partition Name: boot\n"
+ " Salt: deadbeef\n"
+ " Digest: "
+ "184cb36243adb8b87d2d8c4802de32125fe294ec46753d732144ee65df68a23d\n"
+ " Kernel Cmdline descriptor:\n"
+ " Flags: 0\n"
+ " Kernel Cmdline: 'cmdline in hash footer "
+ "$(ANDROID_SYSTEM_PARTUUID)'\n",
+ InfoImage(vbmeta_image_path_));
+
+ EXPECT_COMMAND(0,
+ "./avbtool erase_footer"
+ " --image %s",
+ boot_path.value().c_str());
+
+ // With no footer, 'avbtool info_image' should fail (exit status 1).
+ EXPECT_COMMAND(
+ 1, "./avbtool info_image --image %s", boot_path.value().c_str());
+
+ ops_.set_expected_public_key(
+ PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem")));
+
+ AvbSlotVerifyData* slot_data = NULL;
+ const char* requested_partitions[] = {"boot", NULL};
+ EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_OK,
+ avb_slot_verify(ops_.avb_ops(),
+ requested_partitions,
+ "_a",
+ false /* allow_verification_error */,
+ &slot_data));
+ EXPECT_NE(nullptr, slot_data);
+
+ // Now verify the slot data. The vbmeta data should match our
+ // vbmeta_image_ member.
+ EXPECT_EQ(size_t(1), slot_data->num_vbmeta_images);
+ EXPECT_EQ("vbmeta", std::string(slot_data->vbmeta_images[0].partition_name));
+ EXPECT_EQ(slot_data->vbmeta_images[0].vbmeta_size, vbmeta_image_.size());
+ EXPECT_EQ(0,
+ memcmp(vbmeta_image_.data(),
+ slot_data->vbmeta_images[0].vbmeta_data,
+ slot_data->vbmeta_images[0].vbmeta_size));
+
+ // The boot image data should match what is generated above with
+ // GenerateImage().
+ EXPECT_EQ(size_t(1), slot_data->num_loaded_partitions);
+ EXPECT_EQ("boot",
+ std::string(slot_data->loaded_partitions[0].partition_name));
+ EXPECT_EQ(boot_image_size, slot_data->loaded_partitions[0].data_size);
+ for (size_t n = 0; n < slot_data->loaded_partitions[0].data_size; n++) {
+ EXPECT_EQ(slot_data->loaded_partitions[0].data[n], uint8_t(n));
+ }
+
+ // This should match the two cmdlines with a space (U+0020) between
+ // them and the $(ANDROID_SYSTEM_PARTUUID) and
+ // $(ANDROID_BOOT_PARTUUID) variables replaced.
+ EXPECT_EQ(
+ "cmdline in vbmeta 1234-fake-guid-for:boot_a cmdline in hash footer "
+ "1234-fake-guid-for:system_a "
+ "androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta_a "
+ "androidboot.slot_suffix=_a androidboot.vbmeta.device_state=locked "
+ "androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=1472 "
+ "androidboot.vbmeta.digest="
+ "34cdb59b955aa35d4da97701f304fabf7392eecca8c50ff1a0b7b6e1c9aaa1b8",
+ std::string(slot_data->cmdline));
+ EXPECT_EQ(4UL, slot_data->rollback_indexes[0]);
+ for (size_t n = 1; n < AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS; n++) {
+ EXPECT_EQ(0UL, slot_data->rollback_indexes[n]);
+ }
+ avb_slot_verify_data_free(slot_data);
+}
+
+TEST_F(AvbSlotVerifyTest, HashDescriptorInVBMetaCorruptBoot) {
+ size_t boot_partition_size = 16 * 1024 * 1024;
+ base::FilePath boot_path = GenerateImage("boot_a.img", 5 * 1024 * 1024);
+ const char* requested_partitions[] = {"boot", NULL};
+
+ EXPECT_COMMAND(0,
+ "./avbtool add_hash_footer"
+ " --image %s"
+ " --rollback_index 0"
+ " --partition_name boot"
+ " --partition_size %zd"
+ " --salt deadbeef",
+ boot_path.value().c_str(),
+ boot_partition_size);
+
+ GenerateVBMetaImage("vbmeta_a.img",
+ "SHA256_RSA2048",
+ 0,
+ base::FilePath("test/data/testkey_rsa2048.pem"),
+ base::StringPrintf("--include_descriptors_from_image %s",
+ boot_path.value().c_str()));
+
+ EXPECT_COMMAND(0,
+ "./avbtool erase_footer"
+ " --image %s",
+ boot_path.value().c_str());
+
+ ops_.set_expected_public_key(
+ PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem")));
+
+ // So far, so good.
+ AvbSlotVerifyData* slot_data = NULL;
+ EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_OK,
+ avb_slot_verify(ops_.avb_ops(),
+ requested_partitions,
+ "_a",
+ false /* allow_verification_error */,
+ &slot_data));
+ EXPECT_NE(nullptr, slot_data);
+ avb_slot_verify_data_free(slot_data);
+
+ // Now corrupt boot_a.img and expect verification error.
+ uint8_t corrupt_data[4] = {0xff, 0xff, 0xff, 0xff};
+ EXPECT_EQ(AVB_IO_RESULT_OK,
+ ops_.avb_ops()->write_to_partition(ops_.avb_ops(),
+ "boot_a",
+ 1024 * 1024, // offset: 1 MiB
+ sizeof corrupt_data,
+ corrupt_data));
+
+ EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION,
+ avb_slot_verify(ops_.avb_ops(),
+ requested_partitions,
+ "_a",
+ false /* allow_verification_error */,
+ &slot_data));
+ EXPECT_EQ(nullptr, slot_data);
+ EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION,
+ avb_slot_verify(ops_.avb_ops(),
+ requested_partitions,
+ "_a",
+ true /* allow_verification_error */,
+ &slot_data));
+ EXPECT_NE(nullptr, slot_data);
+ avb_slot_verify_data_free(slot_data);
+}
+
+TEST_F(AvbSlotVerifyTest, HashDescriptorInChainedPartition) {
+ size_t boot_partition_size = 16 * 1024 * 1024;
+ const size_t boot_image_size = 5 * 1024 * 1024;
+ base::FilePath boot_path = GenerateImage("boot_a.img", boot_image_size);
+ const char* requested_partitions[] = {"boot", NULL};
+
+ EXPECT_COMMAND(0,
+ "./avbtool add_hash_footer"
+ " --image %s"
+ " --kernel_cmdline 'cmdline2 in hash footer'"
+ " --rollback_index 12"
+ " --partition_name boot"
+ " --partition_size %zd"
+ " --algorithm SHA256_RSA4096"
+ " --key test/data/testkey_rsa4096.pem"
+ " --salt deadbeef",
+ boot_path.value().c_str(),
+ boot_partition_size);
+
+ base::FilePath pk_path = testdir_.Append("testkey_rsa4096.avbpubkey");
+ EXPECT_COMMAND(
+ 0,
+ "./avbtool extract_public_key --key test/data/testkey_rsa4096.pem"
+ " --output %s",
+ pk_path.value().c_str());
+
+ GenerateVBMetaImage(
+ "vbmeta_a.img",
+ "SHA256_RSA2048",
+ 11,
+ base::FilePath("test/data/testkey_rsa2048.pem"),
+ base::StringPrintf("--chain_partition boot:1:%s"
+ " --kernel_cmdline 'cmdline2 in vbmeta'",
+ pk_path.value().c_str()));
+
+ EXPECT_EQ(
+ "VBMeta image version: 1.0\n"
+ "Header Block: 256 bytes\n"
+ "Authentication Block: 320 bytes\n"
+ "Auxiliary Block: 1728 bytes\n"
+ "Algorithm: SHA256_RSA2048\n"
+ "Rollback Index: 11\n"
+ "Flags: 0\n"
+ "Descriptors:\n"
+ " Chain Partition descriptor:\n"
+ " Partition Name: boot\n"
+ " Rollback Index Location: 1\n"
+ " Public key (sha1): "
+ "2597c218aae470a130f61162feaae70afd97f011\n"
+ " Kernel Cmdline descriptor:\n"
+ " Flags: 0\n"
+ " Kernel Cmdline: 'cmdline2 in vbmeta'\n",
+ InfoImage(vbmeta_image_path_));
+
+ EXPECT_EQ(
+ "Footer version: 1.0\n"
+ "Image size: 16777216 bytes\n"
+ "Original image size: 5242880 bytes\n"
+ "VBMeta offset: 5242880\n"
+ "VBMeta size: 2112 bytes\n"
+ "--\n"
+ "VBMeta image version: 1.0\n"
+ "Header Block: 256 bytes\n"
+ "Authentication Block: 576 bytes\n"
+ "Auxiliary Block: 1280 bytes\n"
+ "Algorithm: SHA256_RSA4096\n"
+ "Rollback Index: 12\n"
+ "Flags: 0\n"
+ "Descriptors:\n"
+ " Hash descriptor:\n"
+ " Image Size: 5242880 bytes\n"
+ " Hash Algorithm: sha256\n"
+ " Partition Name: boot\n"
+ " Salt: deadbeef\n"
+ " Digest: "
+ "184cb36243adb8b87d2d8c4802de32125fe294ec46753d732144ee65df68a23d\n"
+ " Kernel Cmdline descriptor:\n"
+ " Flags: 0\n"
+ " Kernel Cmdline: 'cmdline2 in hash footer'\n",
+ InfoImage(boot_path));
+
+ ops_.set_expected_public_key(
+ PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem")));
+
+ AvbSlotVerifyData* slot_data = NULL;
+ EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_OK,
+ avb_slot_verify(ops_.avb_ops(),
+ requested_partitions,
+ "_a",
+ false /* allow_verification_error */,
+ &slot_data));
+ EXPECT_NE(nullptr, slot_data);
+
+ // Now verify the slot data. We should have two vbmeta
+ // structs. Verify both of them. Note that the A/B suffix isn't
+ // appended.
+ EXPECT_EQ(size_t(2), slot_data->num_vbmeta_images);
+ EXPECT_EQ("vbmeta", std::string(slot_data->vbmeta_images[0].partition_name));
+ EXPECT_EQ(slot_data->vbmeta_images[0].vbmeta_size, vbmeta_image_.size());
+ EXPECT_EQ(0,
+ memcmp(vbmeta_image_.data(),
+ slot_data->vbmeta_images[0].vbmeta_data,
+ slot_data->vbmeta_images[0].vbmeta_size));
+ // And for the second vbmeta struct we check that the descriptors
+ // match the info_image output from above.
+ EXPECT_EQ("boot", std::string(slot_data->vbmeta_images[1].partition_name));
+ const AvbDescriptor** descriptors =
+ avb_descriptor_get_all(slot_data->vbmeta_images[1].vbmeta_data,
+ slot_data->vbmeta_images[1].vbmeta_size,
+ NULL);
+ EXPECT_NE(nullptr, descriptors);
+ AvbHashDescriptor hash_desc;
+ EXPECT_EQ(true,
+ avb_hash_descriptor_validate_and_byteswap(
+ ((AvbHashDescriptor*)descriptors[0]), &hash_desc));
+ const uint8_t* desc_end = reinterpret_cast<const uint8_t*>(descriptors[0]) +
+ sizeof(AvbHashDescriptor);
+ uint64_t o = 0;
+ EXPECT_EQ("boot",
+ std::string(reinterpret_cast<const char*>(desc_end + o),
+ hash_desc.partition_name_len));
+ o += hash_desc.partition_name_len;
+ EXPECT_EQ("deadbeef", mem_to_hexstring(desc_end + o, hash_desc.salt_len));
+ o += hash_desc.salt_len;
+ EXPECT_EQ("184cb36243adb8b87d2d8c4802de32125fe294ec46753d732144ee65df68a23d",
+ mem_to_hexstring(desc_end + o, hash_desc.digest_len));
+ AvbKernelCmdlineDescriptor cmdline_desc;
+ EXPECT_EQ(true,
+ avb_kernel_cmdline_descriptor_validate_and_byteswap(
+ ((AvbKernelCmdlineDescriptor*)descriptors[1]), &cmdline_desc));
+ desc_end = reinterpret_cast<const uint8_t*>(descriptors[1]) +
+ sizeof(AvbKernelCmdlineDescriptor);
+ EXPECT_EQ("cmdline2 in hash footer",
+ std::string(reinterpret_cast<const char*>(desc_end),
+ cmdline_desc.kernel_cmdline_length));
+ avb_free(descriptors);
+
+ // The boot image data should match what is generated above with
+ // GenerateImage().
+ EXPECT_EQ(size_t(1), slot_data->num_loaded_partitions);
+ EXPECT_EQ("boot",
+ std::string(slot_data->loaded_partitions[0].partition_name));
+ EXPECT_EQ(boot_image_size, slot_data->loaded_partitions[0].data_size);
+ for (size_t n = 0; n < slot_data->loaded_partitions[0].data_size; n++) {
+ EXPECT_EQ(slot_data->loaded_partitions[0].data[n], uint8_t(n));
+ }
+
+ // This should match the two cmdlines with a space (U+0020) between them.
+ EXPECT_EQ(
+ "cmdline2 in hash footer cmdline2 in vbmeta "
+ "androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta_a "
+ "androidboot.slot_suffix=_a androidboot.vbmeta.device_state=locked "
+ "androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=4416 "
+ "androidboot.vbmeta.digest="
+ "4a45faa9adfeb94e9154fe682c11fef1a1a3d829b67cbf1a12ac7f0aa4f8e2e4",
+ std::string(slot_data->cmdline));
+ EXPECT_EQ(11UL, slot_data->rollback_indexes[0]);
+ EXPECT_EQ(12UL, slot_data->rollback_indexes[1]);
+ for (size_t n = 2; n < AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS; n++) {
+ EXPECT_EQ(0UL, slot_data->rollback_indexes[n]);
+ }
+ avb_slot_verify_data_free(slot_data);
+}
+
+TEST_F(AvbSlotVerifyTest, HashDescriptorInChainedPartitionCorruptBoot) {
+ size_t boot_partition_size = 16 * 1024 * 1024;
+ base::FilePath boot_path = GenerateImage("boot_a.img", 5 * 1024 * 1024);
+ const char* requested_partitions[] = {"boot", NULL};
+
+ EXPECT_COMMAND(0,
+ "./avbtool add_hash_footer"
+ " --image %s"
+ " --rollback_index 0"
+ " --partition_name boot"
+ " --partition_size %zd"
+ " --algorithm SHA256_RSA4096"
+ " --key test/data/testkey_rsa4096.pem"
+ " --salt deadbeef",
+ boot_path.value().c_str(),
+ boot_partition_size);
+
+ base::FilePath pk_path = testdir_.Append("testkey_rsa4096.avbpubkey");
+ EXPECT_COMMAND(
+ 0,
+ "./avbtool extract_public_key --key test/data/testkey_rsa4096.pem"
+ " --output %s",
+ pk_path.value().c_str());
+
+ GenerateVBMetaImage("vbmeta_a.img",
+ "SHA256_RSA2048",
+ 0,
+ base::FilePath("test/data/testkey_rsa2048.pem"),
+ base::StringPrintf("--chain_partition boot:1:%s",
+ pk_path.value().c_str()));
+
+ ops_.set_expected_public_key(
+ PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem")));
+
+ AvbSlotVerifyData* slot_data = NULL;
+ EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_OK,
+ avb_slot_verify(ops_.avb_ops(),
+ requested_partitions,
+ "_a",
+ false /* allow_verification_error */,
+ &slot_data));
+ EXPECT_NE(nullptr, slot_data);
+ avb_slot_verify_data_free(slot_data);
+
+ // Now corrupt boot_a.img and expect verification error.
+ uint8_t corrupt_data[4] = {0xff, 0xff, 0xff, 0xff};
+ EXPECT_EQ(AVB_IO_RESULT_OK,
+ ops_.avb_ops()->write_to_partition(ops_.avb_ops(),
+ "boot_a",
+ 1024 * 1024, // offset: 1 MiB
+ sizeof corrupt_data,
+ corrupt_data));
+
+ EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION,
+ avb_slot_verify(ops_.avb_ops(),
+ requested_partitions,
+ "_a",
+ false /* allow_verification_error */,
+ &slot_data));
+ EXPECT_EQ(nullptr, slot_data);
+ EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION,
+ avb_slot_verify(ops_.avb_ops(),
+ requested_partitions,
+ "_a",
+ true /* allow_verification_error */,
+ &slot_data));
+ EXPECT_NE(nullptr, slot_data);
+ avb_slot_verify_data_free(slot_data);
+}
+
+TEST_F(AvbSlotVerifyTest, HashDescriptorInChainedPartitionKeyMismatch) {
+ size_t boot_partition_size = 16 * 1024 * 1024;
+ base::FilePath boot_path = GenerateImage("boot_a.img", 5 * 1024 * 1024);
+ const char* requested_partitions[] = {"boot", NULL};
+
+ // Use different key to sign vbmeta in boot_a (we use the 8192 bit
+ // key) than what's in the chained partition descriptor (which is
+ // the 4096 bit key) and expect
+ // AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED.
+
+ EXPECT_COMMAND(0,
+ "./avbtool add_hash_footer"
+ " --image %s"
+ " --rollback_index 0"
+ " --partition_name boot"
+ " --partition_size %zd"
+ " --algorithm SHA256_RSA8192"
+ " --key test/data/testkey_rsa8192.pem"
+ " --salt deadbeef",
+ boot_path.value().c_str(),
+ boot_partition_size);
+
+ base::FilePath pk_path = testdir_.Append("testkey_rsa4096.avbpubkey");
+ EXPECT_COMMAND(
+ 0,
+ "./avbtool extract_public_key --key test/data/testkey_rsa4096.pem"
+ " --output %s",
+ pk_path.value().c_str());
+
+ GenerateVBMetaImage("vbmeta_a.img",
+ "SHA256_RSA2048",
+ 0,
+ base::FilePath("test/data/testkey_rsa2048.pem"),
+ base::StringPrintf("--chain_partition boot:1:%s",
+ pk_path.value().c_str()));
+
+ ops_.set_expected_public_key(
+ PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem")));
+
+ AvbSlotVerifyData* slot_data = NULL;
+ EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED,
+ avb_slot_verify(ops_.avb_ops(),
+ requested_partitions,
+ "_a",
+ false /* allow_verification_error */,
+ &slot_data));
+ EXPECT_EQ(nullptr, slot_data);
+ EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED,
+ avb_slot_verify(ops_.avb_ops(),
+ requested_partitions,
+ "_a",
+ true /* allow_verification_error */,
+ &slot_data));
+ EXPECT_NE(nullptr, slot_data);
+ avb_slot_verify_data_free(slot_data);
+}
+
+TEST_F(AvbSlotVerifyTest, HashDescriptorInChainedPartitionRollbackIndexFail) {
+ size_t boot_partition_size = 16 * 1024 * 1024;
+ base::FilePath boot_path = GenerateImage("boot_a.img", 5 * 1024 * 1024);
+ const char* requested_partitions[] = {"boot", NULL};
+
+ EXPECT_COMMAND(0,
+ "./avbtool add_hash_footer"
+ " --image %s"
+ " --rollback_index 10"
+ " --partition_name boot"
+ " --partition_size %zd"
+ " --algorithm SHA256_RSA4096"
+ " --key test/data/testkey_rsa4096.pem"
+ " --salt deadbeef",
+ boot_path.value().c_str(),
+ boot_partition_size);
+
+ base::FilePath pk_path = testdir_.Append("testkey_rsa4096.avbpubkey");
+ EXPECT_COMMAND(
+ 0,
+ "./avbtool extract_public_key --key test/data/testkey_rsa4096.pem"
+ " --output %s",
+ pk_path.value().c_str());
+
+ GenerateVBMetaImage("vbmeta_a.img",
+ "SHA256_RSA2048",
+ 110,
+ base::FilePath("test/data/testkey_rsa2048.pem"),
+ base::StringPrintf("--chain_partition boot:1:%s",
+ pk_path.value().c_str()));
+
+ ops_.set_expected_public_key(
+ PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem")));
+
+ AvbSlotVerifyData* slot_data = NULL;
+
+ // Both images (vbmeta_a and boot_a) have rollback index 10 and 11
+ // so it should work if the stored rollback indexes are 0 and 0.
+ ops_.set_stored_rollback_indexes({{0, 0}, {1, 0}});
+ EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_OK,
+ avb_slot_verify(ops_.avb_ops(),
+ requested_partitions,
+ "_a",
+ false /* allow_verification_error */,
+ &slot_data));
+ EXPECT_NE(nullptr, slot_data);
+ avb_slot_verify_data_free(slot_data);
+
+ // Check failure if we set the stored rollback index of the chained
+ // partition to 20 (see AvbSlotVerifyTest.RollbackIndex above
+ // where we test rollback index checks for the vbmeta partition).
+ ops_.set_stored_rollback_indexes({{0, 0}, {1, 20}});
+ EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX,
+ avb_slot_verify(ops_.avb_ops(),
+ requested_partitions,
+ "_a",
+ false /* allow_verification_error */,
+ &slot_data));
+ EXPECT_EQ(nullptr, slot_data);
+ EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX,
+ avb_slot_verify(ops_.avb_ops(),
+ requested_partitions,
+ "_a",
+ true /* allow_verification_error */,
+ &slot_data));
+ EXPECT_NE(nullptr, slot_data);
+ avb_slot_verify_data_free(slot_data);
+
+ // Check failure if there is no rollback index slot 1 - in that case
+ // we expect an I/O error since ops->read_rollback_index() will
+ // fail.
+ ops_.set_stored_rollback_indexes({{0, 0}});
+ EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_ERROR_IO,
+ avb_slot_verify(ops_.avb_ops(),
+ requested_partitions,
+ "_a",
+ false /* allow_verification_error */,
+ &slot_data));
+ EXPECT_EQ(nullptr, slot_data);
+}
+
+TEST_F(AvbSlotVerifyTest, ChainedPartitionNoSlots) {
+ size_t boot_partition_size = 16 * 1024 * 1024;
+ const size_t boot_image_size = 5 * 1024 * 1024;
+ base::FilePath boot_path = GenerateImage("boot.img", boot_image_size);
+ const char* requested_partitions[] = {"boot", NULL};
+
+ EXPECT_COMMAND(0,
+ "./avbtool add_hash_footer"
+ " --image %s"
+ " --kernel_cmdline 'cmdline2 in hash footer'"
+ " --rollback_index 12"
+ " --partition_name boot"
+ " --partition_size %zd"
+ " --algorithm SHA256_RSA4096"
+ " --key test/data/testkey_rsa4096.pem"
+ " --salt deadbeef",
+ boot_path.value().c_str(),
+ boot_partition_size);
+
+ base::FilePath pk_path = testdir_.Append("testkey_rsa4096.avbpubkey");
+ EXPECT_COMMAND(
+ 0,
+ "./avbtool extract_public_key --key test/data/testkey_rsa4096.pem"
+ " --output %s",
+ pk_path.value().c_str());
+
+ GenerateVBMetaImage(
+ "vbmeta.img",
+ "SHA256_RSA2048",
+ 11,
+ base::FilePath("test/data/testkey_rsa2048.pem"),
+ base::StringPrintf("--chain_partition boot:1:%s"
+ " --kernel_cmdline 'cmdline2 in vbmeta'",
+ pk_path.value().c_str()));
+
+ EXPECT_EQ(
+ "VBMeta image version: 1.0\n"
+ "Header Block: 256 bytes\n"
+ "Authentication Block: 320 bytes\n"
+ "Auxiliary Block: 1728 bytes\n"
+ "Algorithm: SHA256_RSA2048\n"
+ "Rollback Index: 11\n"
+ "Flags: 0\n"
+ "Descriptors:\n"
+ " Chain Partition descriptor:\n"
+ " Partition Name: boot\n"
+ " Rollback Index Location: 1\n"
+ " Public key (sha1): "
+ "2597c218aae470a130f61162feaae70afd97f011\n"
+ " Kernel Cmdline descriptor:\n"
+ " Flags: 0\n"
+ " Kernel Cmdline: 'cmdline2 in vbmeta'\n",
+ InfoImage(vbmeta_image_path_));
+
+ ops_.set_expected_public_key(
+ PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem")));
+
+ AvbSlotVerifyData* slot_data = NULL;
+ EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_OK,
+ avb_slot_verify(ops_.avb_ops(),
+ requested_partitions,
+ "",
+ false /* allow_verification_error */,
+ &slot_data));
+ EXPECT_NE(nullptr, slot_data);
+
+ // Now verify the slot data. The first vbmeta data should match our
+ // vbmeta_image_ member and the second one should be for the 'boot'
+ // partition.
+ EXPECT_EQ(size_t(2), slot_data->num_vbmeta_images);
+ EXPECT_EQ("vbmeta", std::string(slot_data->vbmeta_images[0].partition_name));
+ EXPECT_EQ(slot_data->vbmeta_images[0].vbmeta_size, vbmeta_image_.size());
+ EXPECT_EQ(0,
+ memcmp(vbmeta_image_.data(),
+ slot_data->vbmeta_images[0].vbmeta_data,
+ slot_data->vbmeta_images[0].vbmeta_size));
+ EXPECT_EQ("boot", std::string(slot_data->vbmeta_images[1].partition_name));
+
+ // The boot image data should match what is generated above with
+ // GenerateImage().
+ EXPECT_EQ(size_t(1), slot_data->num_loaded_partitions);
+ EXPECT_EQ("boot",
+ std::string(slot_data->loaded_partitions[0].partition_name));
+ EXPECT_EQ(boot_image_size, slot_data->loaded_partitions[0].data_size);
+ for (size_t n = 0; n < slot_data->loaded_partitions[0].data_size; n++) {
+ EXPECT_EQ(slot_data->loaded_partitions[0].data[n], uint8_t(n));
+ }
+
+ // This should match the two cmdlines with a space (U+0020) between
+ // them - note that androidboot.slot_suffix is not set since we
+ // don't have any slots in this setup.
+ EXPECT_EQ(
+ "cmdline2 in hash footer cmdline2 in vbmeta "
+ "androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta "
+ "androidboot.vbmeta.device_state=locked "
+ "androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=4416 "
+ "androidboot.vbmeta.digest="
+ "4a45faa9adfeb94e9154fe682c11fef1a1a3d829b67cbf1a12ac7f0aa4f8e2e4",
+ std::string(slot_data->cmdline));
+ EXPECT_EQ(11UL, slot_data->rollback_indexes[0]);
+ EXPECT_EQ(12UL, slot_data->rollback_indexes[1]);
+ for (size_t n = 2; n < AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS; n++) {
+ EXPECT_EQ(0UL, slot_data->rollback_indexes[n]);
+ }
+ avb_slot_verify_data_free(slot_data);
+}
+
+TEST_F(AvbSlotVerifyTest, PartitionsOtherThanBoot) {
+ const size_t foo_partition_size = 16 * 1024 * 1024;
+ const size_t bar_partition_size = 32 * 1024 * 1024;
+ const size_t foo_image_size = 5 * 1024 * 1024;
+ const size_t bar_image_size = 10 * 1024 * 1024;
+ base::FilePath foo_path = GenerateImage("foo_a.img", foo_image_size);
+ base::FilePath bar_path = GenerateImage("bar_a.img", bar_image_size);
+
+ EXPECT_COMMAND(0,
+ "./avbtool add_hash_footer"
+ " --image %s"
+ " --partition_name foo"
+ " --partition_size %zd"
+ " --salt deadbeef",
+ foo_path.value().c_str(),
+ foo_partition_size);
+
+ EXPECT_COMMAND(0,
+ "./avbtool add_hash_footer"
+ " --image %s"
+ " --partition_name bar"
+ " --partition_size %zd"
+ " --salt deadbeef",
+ bar_path.value().c_str(),
+ bar_partition_size);
+
+ GenerateVBMetaImage("vbmeta_a.img",
+ "SHA256_RSA2048",
+ 4,
+ base::FilePath("test/data/testkey_rsa2048.pem"),
+ base::StringPrintf("--include_descriptors_from_image %s"
+ " --include_descriptors_from_image %s",
+ foo_path.value().c_str(),
+ bar_path.value().c_str()));
+
+ EXPECT_EQ(
+ "VBMeta image version: 1.0\n"
+ "Header Block: 256 bytes\n"
+ "Authentication Block: 320 bytes\n"
+ "Auxiliary Block: 896 bytes\n"
+ "Algorithm: SHA256_RSA2048\n"
+ "Rollback Index: 4\n"
+ "Flags: 0\n"
+ "Descriptors:\n"
+ " Hash descriptor:\n"
+ " Image Size: 5242880 bytes\n"
+ " Hash Algorithm: sha256\n"
+ " Partition Name: foo\n"
+ " Salt: deadbeef\n"
+ " Digest: "
+ "184cb36243adb8b87d2d8c4802de32125fe294ec46753d732144ee65df68a23d\n"
+ " Hash descriptor:\n"
+ " Image Size: 10485760 bytes\n"
+ " Hash Algorithm: sha256\n"
+ " Partition Name: bar\n"
+ " Salt: deadbeef\n"
+ " Digest: "
+ "baea4bbd261d0edf4d1fe5e6e5a36976c291eeba66b6a46fa81dba691327a727\n",
+ InfoImage(vbmeta_image_path_));
+
+ ops_.set_expected_public_key(
+ PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem")));
+
+ AvbSlotVerifyData* slot_data = NULL;
+ const char* requested_partitions[] = {"foo", "bar", NULL};
+ EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_OK,
+ avb_slot_verify(ops_.avb_ops(),
+ requested_partitions,
+ "_a",
+ false /* allow_verification_error */,
+ &slot_data));
+ EXPECT_NE(nullptr, slot_data);
+
+ // Now verify the slot data. The vbmeta data should match our
+ // vbmeta_image_ member.
+ EXPECT_EQ(size_t(1), slot_data->num_vbmeta_images);
+ EXPECT_EQ("vbmeta", std::string(slot_data->vbmeta_images[0].partition_name));
+ EXPECT_EQ(slot_data->vbmeta_images[0].vbmeta_size, vbmeta_image_.size());
+ EXPECT_EQ(0,
+ memcmp(vbmeta_image_.data(),
+ slot_data->vbmeta_images[0].vbmeta_data,
+ slot_data->vbmeta_images[0].vbmeta_size));
+
+ // The 'foo' and 'bar' image data should match what is generated
+ // above with GenerateImage().
+ EXPECT_EQ(size_t(2), slot_data->num_loaded_partitions);
+ EXPECT_EQ("foo", std::string(slot_data->loaded_partitions[0].partition_name));
+ EXPECT_EQ(foo_image_size, slot_data->loaded_partitions[0].data_size);
+ for (size_t n = 0; n < slot_data->loaded_partitions[0].data_size; n++) {
+ EXPECT_EQ(slot_data->loaded_partitions[0].data[n], uint8_t(n));
+ }
+ EXPECT_EQ("bar", std::string(slot_data->loaded_partitions[1].partition_name));
+ EXPECT_EQ(bar_image_size, slot_data->loaded_partitions[1].data_size);
+ for (size_t n = 0; n < slot_data->loaded_partitions[1].data_size; n++) {
+ EXPECT_EQ(slot_data->loaded_partitions[1].data[n], uint8_t(n));
+ }
+
+ avb_slot_verify_data_free(slot_data);
+}
+
+TEST_F(AvbSlotVerifyTest, PublicKeyMetadata) {
+ base::FilePath md_path = GenerateImage("md.bin", 1536);
+
+ GenerateVBMetaImage(
+ "vbmeta_a.img",
+ "SHA256_RSA2048",
+ 0,
+ base::FilePath("test/data/testkey_rsa2048.pem"),
+ base::StringPrintf("--public_key_metadata %s", md_path.value().c_str()));
+
+ ops_.set_expected_public_key(
+ PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem")));
+
+ std::string md_data;
+ ASSERT_TRUE(base::ReadFileToString(md_path, &md_data));
+ ops_.set_expected_public_key_metadata(md_data);
+
+ AvbSlotVerifyData* slot_data = NULL;
+ const char* requested_partitions[] = {"boot", NULL};
+ EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_OK,
+ avb_slot_verify(ops_.avb_ops(),
+ requested_partitions,
+ "_a",
+ false /* allow_verification_error */,
+ &slot_data));
+ EXPECT_NE(nullptr, slot_data);
+ EXPECT_EQ(
+ "androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta_a "
+ "androidboot.slot_suffix=_a androidboot.vbmeta.device_state=locked "
+ "androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=2688 "
+ "androidboot.vbmeta.digest="
+ "5edcaa54f40382ee6a2fc3b86cdf383348b35ed07955e83ea32d84b69a97eaa0",
+ std::string(slot_data->cmdline));
+ avb_slot_verify_data_free(slot_data);
+}
+
+void AvbSlotVerifyTest::CmdlineWithHashtreeVerification(
+ bool hashtree_verification_on) {
+ const size_t rootfs_size = 1028 * 1024;
+ const size_t partition_size = 1536 * 1024;
+
+ // Generate a 1028 KiB file with known content.
+ std::vector<uint8_t> rootfs;
+ rootfs.resize(rootfs_size);
+ for (size_t n = 0; n < rootfs_size; n++)
+ rootfs[n] = uint8_t(n);
+ base::FilePath rootfs_path = testdir_.Append("rootfs.bin");
+ EXPECT_EQ(rootfs_size,
+ static_cast<const size_t>(
+ base::WriteFile(rootfs_path,
+ reinterpret_cast<const char*>(rootfs.data()),
+ rootfs.size())));
+
+ EXPECT_COMMAND(0,
+ "./avbtool add_hashtree_footer --salt d00df00d --image %s "
+ "--partition_size %d --partition_name foobar "
+ "--algorithm SHA256_RSA2048 "
+ "--key test/data/testkey_rsa2048.pem",
+ rootfs_path.value().c_str(),
+ (int)partition_size);
+
+ // Check that we correctly generate dm-verity kernel cmdline
+ // snippets, if requested.
+ GenerateVBMetaImage(
+ "vbmeta_a.img",
+ "SHA256_RSA2048",
+ 4,
+ base::FilePath("test/data/testkey_rsa2048.pem"),
+ base::StringPrintf("--setup_rootfs_from_kernel %s "
+ "--kernel_cmdline should_be_in_both=1 "
+ "--algorithm SHA256_RSA2048 "
+ "--flags %d ",
+ rootfs_path.value().c_str(),
+ hashtree_verification_on
+ ? 0
+ : AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED));
+
+ EXPECT_EQ(
+ base::StringPrintf(
+ "VBMeta image version: 1.0\n"
+ "Header Block: 256 bytes\n"
+ "Authentication Block: 320 bytes\n"
+ "Auxiliary Block: 896 bytes\n"
+ "Algorithm: SHA256_RSA2048\n"
+ "Rollback Index: 4\n"
+ "Flags: %d\n"
+ "Descriptors:\n"
+ " Kernel Cmdline descriptor:\n"
+ " Flags: 1\n"
+ " Kernel Cmdline: 'dm=\"1 vroot none ro 1,0 2056 verity "
+ "1 PARTUUID=$(ANDROID_SYSTEM_PARTUUID) "
+ "PARTUUID=$(ANDROID_SYSTEM_PARTUUID) 4096 4096 257 257 sha1 "
+ "e811611467dcd6e8dc4324e45f706c2bdd51db67 d00df00d 2 "
+ "restart_on_corruption ignore_zero_blocks\" root=0xfd00'\n"
+ " Kernel Cmdline descriptor:\n"
+ " Flags: 2\n"
+ " Kernel Cmdline: "
+ "'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'\n"
+ " Kernel Cmdline descriptor:\n"
+ " Flags: 0\n"
+ " Kernel Cmdline: 'should_be_in_both=1'\n",
+ hashtree_verification_on ? 0
+ : AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED),
+ InfoImage(vbmeta_image_path_));
+
+ ops_.set_expected_public_key(
+ PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem")));
+
+ // Check that avb_slot_verify() picks the cmdline decsriptors based
+ // on their flags value.
+ AvbSlotVerifyData* slot_data = NULL;
+ const char* requested_partitions[] = {"boot", NULL};
+ EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_OK,
+ avb_slot_verify(ops_.avb_ops(),
+ requested_partitions,
+ "_a",
+ false /* allow_verification_error */,
+ &slot_data));
+ EXPECT_NE(nullptr, slot_data);
+ if (hashtree_verification_on) {
+ EXPECT_EQ(
+ "dm=\"1 vroot none ro 1,0 2056 verity 1 "
+ "PARTUUID=1234-fake-guid-for:system_a "
+ "PARTUUID=1234-fake-guid-for:system_a 4096 4096 257 257 sha1 "
+ "e811611467dcd6e8dc4324e45f706c2bdd51db67 d00df00d 2 "
+ "restart_on_corruption ignore_zero_blocks\" root=0xfd00 "
+ "should_be_in_both=1 "
+ "androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta_a "
+ "androidboot.slot_suffix=_a androidboot.vbmeta.device_state=locked "
+ "androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=1472 "
+ "androidboot.vbmeta.digest="
+ "32572f7a89fb44cc76e9e3adbc0cb5272d0604578b2179729aa52d2bba4061c3",
+ std::string(slot_data->cmdline));
+ } else {
+ EXPECT_EQ(
+ "root=PARTUUID=1234-fake-guid-for:system_a should_be_in_both=1 "
+ "androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta_a "
+ "androidboot.slot_suffix=_a androidboot.vbmeta.device_state=locked "
+ "androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=1472 "
+ "androidboot.vbmeta.digest="
+ "bb630c27d09e10117092d4444ea57a99bf44101c46cf5424cfb86928a0873249",
+ std::string(slot_data->cmdline));
+ }
+ avb_slot_verify_data_free(slot_data);
+}
+
+TEST_F(AvbSlotVerifyTest, CmdlineWithHashtreeVerificationOff) {
+ CmdlineWithHashtreeVerification(false);
+}
+
+TEST_F(AvbSlotVerifyTest, CmdlineWithHashtreeVerificationOn) {
+ CmdlineWithHashtreeVerification(true);
+}
+
+} // namespace avb
diff --git a/avb/test/avb_unittest_util.cc b/avb/test/avb_unittest_util.cc
new file mode 100644
index 0000000..e065db8
--- /dev/null
+++ b/avb/test/avb_unittest_util.cc
@@ -0,0 +1,35 @@
+/*
+ * 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 "avb_unittest_util.h"
+
+std::string mem_to_hexstring(const uint8_t* data, size_t len) {
+ std::string ret;
+ char digits[17] = "0123456789abcdef";
+ for (size_t n = 0; n < len; n++) {
+ ret.push_back(digits[data[n] >> 4]);
+ ret.push_back(digits[data[n] & 0x0f]);
+ }
+ return ret;
+}
diff --git a/avb/test/avb_unittest_util.h b/avb/test/avb_unittest_util.h
new file mode 100644
index 0000000..e93e68c
--- /dev/null
+++ b/avb/test/avb_unittest_util.h
@@ -0,0 +1,166 @@
+/*
+ * 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 AVB_UNITTEST_UTIL_H_
+#define AVB_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>
+
+// Encodes |len| bytes of |data| as a lower-case hex-string.
+std::string mem_to_hexstring(const uint8_t* data, size_t len);
+
+/* 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);
+
+namespace avb {
+
+/* Base-class used for unit test. */
+class BaseAvbToolTest : public ::testing::Test {
+ public:
+ BaseAvbToolTest() {}
+
+ protected:
+ virtual ~BaseAvbToolTest() {}
+
+ /* Generates a vbmeta image, using avbtoool, with file name
+ * |image_name|. The generated vbmeta image will written to disk,
+ * see the |vbmeta_image_path_| variable for its path and
+ * |vbmeta_image_| for the content.
+ */
+ void GenerateVBMetaImage(const std::string& image_name,
+ const std::string& algorithm,
+ uint64_t rollback_index,
+ const base::FilePath& key_path,
+ const std::string& additional_options = "") {
+ std::string signing_options;
+ if (algorithm == "") {
+ signing_options = " --algorithm NONE ";
+ } else {
+ signing_options = std::string(" --algorithm ") + algorithm + " --key " +
+ key_path.value() + " ";
+ }
+ vbmeta_image_path_ = testdir_.Append(image_name);
+ EXPECT_COMMAND(0,
+ "./avbtool make_vbmeta_image"
+ " --rollback_index %" PRIu64
+ " %s %s "
+ " --output %s",
+ rollback_index,
+ additional_options.c_str(),
+ signing_options.c_str(),
+ vbmeta_image_path_.value().c_str());
+ int64_t file_size;
+ ASSERT_TRUE(base::GetFileSize(vbmeta_image_path_, &file_size));
+ vbmeta_image_.resize(file_size);
+ ASSERT_TRUE(base::ReadFile(vbmeta_image_path_,
+ reinterpret_cast<char*>(vbmeta_image_.data()),
+ vbmeta_image_.size()));
+ }
+
+ /* Generate a file with name |file_name| of size |image_size| with
+ * known content (0x00 0x01 0x02 .. 0xff 0x00 0x01 ..).
+ */
+ base::FilePath GenerateImage(const std::string file_name, size_t image_size) {
+ std::vector<uint8_t> image;
+ image.resize(image_size);
+ for (size_t n = 0; n < image_size; n++) {
+ image[n] = uint8_t(n);
+ }
+ base::FilePath image_path = testdir_.Append(file_name);
+ EXPECT_EQ(image_size,
+ static_cast<const size_t>(
+ base::WriteFile(image_path,
+ reinterpret_cast<const char*>(image.data()),
+ image.size())));
+ return image_path;
+ }
+
+ /* Returns the output of 'avbtool info_image' for a given image. */
+ std::string InfoImage(const base::FilePath& image_path) {
+ base::FilePath tmp_path = testdir_.Append("info_output.txt");
+ EXPECT_COMMAND(0,
+ "./avbtool info_image --image %s --output %s",
+ image_path.value().c_str(),
+ tmp_path.value().c_str());
+ std::string info_data;
+ EXPECT_TRUE(base::ReadFileToString(tmp_path, &info_data));
+ return info_data;
+ }
+
+ /* Returns public key in AVB format for a .pem key */
+ std::string PublicKeyAVB(const base::FilePath& key_path) {
+ base::FilePath tmp_path = testdir_.Append("public_key.bin");
+ EXPECT_COMMAND(0,
+ "./avbtool extract_public_key --key %s"
+ " --output %s",
+ key_path.value().c_str(),
+ tmp_path.value().c_str());
+ std::string key_data;
+ EXPECT_TRUE(base::ReadFileToString(tmp_path, &key_data));
+ return key_data;
+ }
+
+ /* Create temporary directory to stash images in. */
+ virtual void SetUp() override {
+ base::FilePath ret;
+ char* buf = strdup("/tmp/libavb-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/libavb-tests"));
+ ASSERT_TRUE(base::DeleteFile(testdir_, true /* recursive */));
+ }
+
+ /* Temporary directory created in SetUp(). */
+ base::FilePath testdir_;
+
+ /* Path to vbmeta image generated with GenerateVBMetaImage(). */
+ base::FilePath vbmeta_image_path_;
+
+ /* Contents of the image generated with GenerateVBMetaImage(). */
+ std::vector<uint8_t> vbmeta_image_;
+};
+
+} // namespace avb
+
+#endif /* AVB_UNITTEST_UTIL_H_ */
diff --git a/avb/test/avb_util_unittest.cc b/avb/test/avb_util_unittest.cc
new file mode 100644
index 0000000..0c8c375
--- /dev/null
+++ b/avb/test/avb_util_unittest.cc
@@ -0,0 +1,496 @@
+/*
+ * 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 <libavb/libavb.h>
+
+TEST(UtilTest, RSAPublicKeyHeaderByteswap) {
+ AvbRSAPublicKeyHeader h;
+ AvbRSAPublicKeyHeader s;
+ uint32_t n32;
+ uint64_t n64;
+
+ n32 = 0x11223344;
+ n64 = 0x1122334455667788;
+
+ h.key_num_bits = htobe32(n32);
+ n32++;
+ h.n0inv = htobe32(n32);
+ n32++;
+
+ EXPECT_NE(0, avb_rsa_public_key_header_validate_and_byteswap(&h, &s));
+
+ n32 = 0x11223344;
+ n64 = 0x1122334455667788;
+
+ EXPECT_EQ(n32, s.key_num_bits);
+ n32++;
+ EXPECT_EQ(n32, s.n0inv);
+ n32++;
+}
+
+TEST(UtilTest, FooterByteswap) {
+ AvbFooter h;
+ AvbFooter s;
+ AvbFooter other;
+ AvbFooter bad;
+ uint64_t n64;
+
+ n64 = 0x1122334455667788;
+
+ memcpy(h.magic, AVB_FOOTER_MAGIC, AVB_FOOTER_MAGIC_LEN);
+ h.version_major = htobe32(AVB_FOOTER_MAJOR_VERSION);
+ h.version_minor = htobe32(AVB_FOOTER_MINOR_VERSION);
+ h.original_image_size = htobe64(n64);
+ n64++;
+ h.vbmeta_offset = htobe64(n64);
+ n64++;
+ h.vbmeta_size = htobe64(n64);
+ n64++;
+
+ EXPECT_NE(0, avb_footer_validate_and_byteswap(&h, &s));
+
+ n64 = 0x1122334455667788;
+
+ EXPECT_EQ((uint32_t)AVB_FOOTER_MAJOR_VERSION, s.version_major);
+ EXPECT_EQ((uint32_t)AVB_FOOTER_MINOR_VERSION, s.version_minor);
+ EXPECT_EQ(n64, s.original_image_size);
+ n64++;
+ EXPECT_EQ(n64, s.vbmeta_offset);
+ n64++;
+ EXPECT_EQ(n64, s.vbmeta_size);
+ n64++;
+
+ // Check that the struct still validates if minor is bigger than
+ // what we expect.
+ other = h;
+ h.version_minor = htobe32(AVB_FOOTER_MINOR_VERSION + 1);
+ EXPECT_NE(0, avb_footer_validate_and_byteswap(&other, &s));
+
+ // Check for bad magic.
+ bad = h;
+ bad.magic[0] = 'x';
+ EXPECT_EQ(0, avb_footer_validate_and_byteswap(&bad, &s));
+
+ // Check for bad major version.
+ bad = h;
+ bad.version_major = htobe32(AVB_FOOTER_MAJOR_VERSION + 1);
+ EXPECT_EQ(0, avb_footer_validate_and_byteswap(&bad, &s));
+}
+
+TEST(UtilTest, KernelCmdlineDescriptorByteswap) {
+ AvbKernelCmdlineDescriptor h;
+ AvbKernelCmdlineDescriptor s;
+ AvbKernelCmdlineDescriptor bad;
+ uint64_t nbf;
+ uint32_t n32;
+
+ // Specify 40 bytes of data past the end of the descriptor struct.
+ nbf = 40 + sizeof(AvbKernelCmdlineDescriptor) - sizeof(AvbDescriptor);
+ h.parent_descriptor.num_bytes_following = htobe64(nbf);
+ h.parent_descriptor.tag = htobe64(AVB_DESCRIPTOR_TAG_KERNEL_CMDLINE);
+ h.kernel_cmdline_length = htobe32(40);
+
+ n32 = 0x11223344;
+ h.flags = htobe32(n32);
+ n32++;
+
+ EXPECT_NE(0, avb_kernel_cmdline_descriptor_validate_and_byteswap(&h, &s));
+
+ n32 = 0x11223344;
+ EXPECT_EQ(n32, s.flags);
+ n32++;
+
+ EXPECT_EQ(AVB_DESCRIPTOR_TAG_KERNEL_CMDLINE, s.parent_descriptor.tag);
+ EXPECT_EQ(nbf, s.parent_descriptor.num_bytes_following);
+ EXPECT_EQ(40UL, s.kernel_cmdline_length);
+
+ // Check for bad tag.
+ bad = h;
+ bad.parent_descriptor.tag = htobe64(0xf00dd00d);
+ EXPECT_EQ(0, avb_kernel_cmdline_descriptor_validate_and_byteswap(&bad, &s));
+
+ // Doesn't fit in 41 bytes.
+ bad = h;
+ bad.kernel_cmdline_length = htobe32(41);
+ EXPECT_EQ(0, avb_kernel_cmdline_descriptor_validate_and_byteswap(&bad, &s));
+}
+
+TEST(UtilTest, HashtreeDescriptorByteswap) {
+ AvbHashtreeDescriptor h;
+ AvbHashtreeDescriptor s;
+ AvbHashtreeDescriptor bad;
+ uint64_t nbf;
+ uint32_t n32;
+ uint64_t n64;
+
+ // Specify 44 bytes of data past the end of the descriptor struct.
+ nbf = 44 + sizeof(AvbHashtreeDescriptor) - sizeof(AvbDescriptor);
+ h.parent_descriptor.num_bytes_following = htobe64(nbf);
+ h.parent_descriptor.tag = htobe64(AVB_DESCRIPTOR_TAG_HASHTREE);
+ h.partition_name_len = htobe32(10);
+ h.salt_len = htobe32(10);
+ h.root_digest_len = htobe32(10);
+
+ n32 = 0x11223344;
+ n64 = 0x1122334455667788;
+
+ h.dm_verity_version = htobe32(n32);
+ n32++;
+ h.image_size = htobe64(n64);
+ n64++;
+ h.tree_offset = htobe64(n64);
+ n64++;
+ h.tree_size = htobe64(n64);
+ n64++;
+ h.data_block_size = htobe32(n32);
+ n32++;
+ h.hash_block_size = htobe32(n32);
+ n32++;
+ h.fec_num_roots = htobe32(n32);
+ n32++;
+ h.fec_offset = htobe64(n64);
+ n64++;
+ h.fec_size = htobe64(n64);
+ n64++;
+
+ EXPECT_TRUE(avb_hashtree_descriptor_validate_and_byteswap(&h, &s));
+
+ n32 = 0x11223344;
+ n64 = 0x1122334455667788;
+
+ EXPECT_EQ(n32, s.dm_verity_version);
+ n32++;
+ EXPECT_EQ(n64, s.image_size);
+ n64++;
+ EXPECT_EQ(n64, s.tree_offset);
+ n64++;
+ EXPECT_EQ(n64, s.tree_size);
+ n64++;
+ EXPECT_EQ(n32, s.data_block_size);
+ n32++;
+ EXPECT_EQ(n32, s.hash_block_size);
+ n32++;
+ EXPECT_EQ(n32, s.fec_num_roots);
+ n32++;
+ EXPECT_EQ(n64, s.fec_offset);
+ n64++;
+ EXPECT_EQ(n64, s.fec_size);
+ n64++;
+
+ EXPECT_EQ(AVB_DESCRIPTOR_TAG_HASHTREE, s.parent_descriptor.tag);
+ EXPECT_EQ(nbf, s.parent_descriptor.num_bytes_following);
+ EXPECT_EQ(10UL, s.partition_name_len);
+ EXPECT_EQ(10UL, s.salt_len);
+ EXPECT_EQ(10UL, s.root_digest_len);
+
+ // Check for bad tag.
+ bad = h;
+ bad.parent_descriptor.tag = htobe64(0xf00dd00d);
+ EXPECT_FALSE(avb_hashtree_descriptor_validate_and_byteswap(&bad, &s));
+
+ // Doesn't fit in 44 bytes (30 + 10 + 10 = 50).
+ bad = h;
+ bad.partition_name_len = htobe32(30);
+ EXPECT_FALSE(avb_hashtree_descriptor_validate_and_byteswap(&bad, &s));
+
+ // Doesn't fit in 44 bytes (10 + 30 + 10 = 50).
+ bad = h;
+ bad.salt_len = htobe32(30);
+ EXPECT_FALSE(avb_hashtree_descriptor_validate_and_byteswap(&bad, &s));
+
+ // Doesn't fit in 44 bytes (10 + 10 + 30 = 50).
+ bad = h;
+ bad.root_digest_len = htobe32(30);
+ EXPECT_FALSE(avb_hashtree_descriptor_validate_and_byteswap(&bad, &s));
+}
+
+TEST(UtilTest, HashDescriptorByteswap) {
+ AvbHashDescriptor h;
+ AvbHashDescriptor s;
+ AvbHashDescriptor bad;
+ uint64_t nbf;
+
+ // Specify 44 bytes of data past the end of the descriptor struct.
+ nbf = 44 + sizeof(AvbHashDescriptor) - sizeof(AvbDescriptor);
+ h.parent_descriptor.num_bytes_following = htobe64(nbf);
+ h.parent_descriptor.tag = htobe64(AVB_DESCRIPTOR_TAG_HASH);
+ h.partition_name_len = htobe32(10);
+ h.salt_len = htobe32(10);
+ h.digest_len = htobe32(10);
+
+ EXPECT_NE(0, avb_hash_descriptor_validate_and_byteswap(&h, &s));
+
+ EXPECT_EQ(AVB_DESCRIPTOR_TAG_HASH, s.parent_descriptor.tag);
+ EXPECT_EQ(nbf, s.parent_descriptor.num_bytes_following);
+ EXPECT_EQ(10UL, s.partition_name_len);
+ EXPECT_EQ(10UL, s.salt_len);
+ EXPECT_EQ(10UL, s.digest_len);
+
+ // Check for bad tag.
+ bad = h;
+ bad.parent_descriptor.tag = htobe64(0xf00dd00d);
+ EXPECT_EQ(0, avb_hash_descriptor_validate_and_byteswap(&bad, &s));
+
+ // Doesn't fit in 44 bytes (30 + 10 + 10 = 50).
+ bad = h;
+ bad.partition_name_len = htobe32(30);
+ EXPECT_EQ(0, avb_hash_descriptor_validate_and_byteswap(&bad, &s));
+
+ // Doesn't fit in 44 bytes (10 + 30 + 10 = 50).
+ bad = h;
+ bad.salt_len = htobe32(30);
+ EXPECT_EQ(0, avb_hash_descriptor_validate_and_byteswap(&bad, &s));
+
+ // Doesn't fit in 44 bytes (10 + 10 + 30 = 50).
+ bad = h;
+ bad.digest_len = htobe32(30);
+ EXPECT_EQ(0, avb_hash_descriptor_validate_and_byteswap(&bad, &s));
+}
+
+TEST(UtilTest, ChainPartitionDescriptorByteswap) {
+ AvbChainPartitionDescriptor h;
+ AvbChainPartitionDescriptor s;
+ AvbChainPartitionDescriptor bad;
+ uint64_t nbf;
+
+ // Specify 36 bytes of data past the end of the descriptor struct.
+ nbf = 36 + sizeof(AvbChainPartitionDescriptor) - sizeof(AvbDescriptor);
+ h.parent_descriptor.num_bytes_following = htobe64(nbf);
+ h.parent_descriptor.tag = htobe64(AVB_DESCRIPTOR_TAG_CHAIN_PARTITION);
+ h.rollback_index_location = htobe32(42);
+ h.partition_name_len = htobe32(16);
+ h.public_key_len = htobe32(17);
+
+ EXPECT_NE(0, avb_chain_partition_descriptor_validate_and_byteswap(&h, &s));
+
+ EXPECT_EQ(AVB_DESCRIPTOR_TAG_CHAIN_PARTITION, s.parent_descriptor.tag);
+ EXPECT_EQ(nbf, s.parent_descriptor.num_bytes_following);
+ EXPECT_EQ(42UL, s.rollback_index_location);
+ EXPECT_EQ(16UL, s.partition_name_len);
+ EXPECT_EQ(17UL, s.public_key_len);
+
+ // Check for bad tag.
+ bad = h;
+ bad.parent_descriptor.tag = htobe64(0xf00dd00d);
+ EXPECT_EQ(0, avb_chain_partition_descriptor_validate_and_byteswap(&bad, &s));
+
+ // Check for bad rollback index slot (must be at least 1).
+ bad = h;
+ bad.rollback_index_location = htobe32(0);
+ EXPECT_EQ(0, avb_chain_partition_descriptor_validate_and_byteswap(&bad, &s));
+
+ // Doesn't fit in 40 bytes (24 + 17 = 41).
+ bad = h;
+ bad.partition_name_len = htobe32(24);
+ EXPECT_EQ(0, avb_chain_partition_descriptor_validate_and_byteswap(&bad, &s));
+
+ // Doesn't fit in 40 bytes (16 + 25 = 41).
+ bad = h;
+ bad.public_key_len = htobe32(25);
+ EXPECT_EQ(0, avb_chain_partition_descriptor_validate_and_byteswap(&bad, &s));
+}
+
+TEST(UtilTest, PropertyDescriptorByteswap) {
+ AvbPropertyDescriptor h;
+ AvbPropertyDescriptor s;
+ AvbPropertyDescriptor bad;
+ uint64_t nbf;
+
+ // Specify 40 bytes of data past the end of the descriptor struct.
+ nbf = 40 + sizeof(AvbPropertyDescriptor) - sizeof(AvbDescriptor);
+ h.parent_descriptor.num_bytes_following = htobe64(nbf);
+ h.parent_descriptor.tag = htobe64(AVB_DESCRIPTOR_TAG_PROPERTY);
+ h.key_num_bytes = htobe64(16);
+ h.value_num_bytes = htobe64(17);
+
+ EXPECT_NE(0, avb_property_descriptor_validate_and_byteswap(&h, &s));
+
+ EXPECT_EQ(AVB_DESCRIPTOR_TAG_PROPERTY, s.parent_descriptor.tag);
+ EXPECT_EQ(nbf, s.parent_descriptor.num_bytes_following);
+ EXPECT_EQ(16UL, s.key_num_bytes);
+ EXPECT_EQ(17UL, s.value_num_bytes);
+
+ // Check for bad tag.
+ bad = h;
+ bad.parent_descriptor.tag = htobe64(0xf00dd00d);
+ EXPECT_EQ(0, avb_property_descriptor_validate_and_byteswap(&bad, &s));
+
+ // Doesn't fit in 40 bytes (22 + 17 + 2 = 41).
+ bad = h;
+ bad.key_num_bytes = htobe64(22);
+ EXPECT_EQ(0, avb_property_descriptor_validate_and_byteswap(&bad, &s));
+
+ // Doesn't fit in 40 bytes (16 + 23 + 2 = 41).
+ bad = h;
+ bad.value_num_bytes = htobe64(23);
+ EXPECT_EQ(0, avb_property_descriptor_validate_and_byteswap(&bad, &s));
+}
+
+TEST(UtilTest, DescriptorByteswap) {
+ AvbDescriptor h;
+ AvbDescriptor s;
+ uint64_t n64;
+
+ n64 = 0x1122334455667788;
+
+ h.num_bytes_following = htobe64(n64);
+ n64++;
+ h.tag = htobe64(n64);
+ n64++;
+
+ EXPECT_NE(0, avb_descriptor_validate_and_byteswap(&h, &s));
+
+ n64 = 0x1122334455667788;
+
+ EXPECT_EQ(n64, s.num_bytes_following);
+ n64++;
+ EXPECT_EQ(n64, s.tag);
+ n64++;
+
+ // Check that we catch if |num_bytes_following| isn't divisble by 8.
+ h.num_bytes_following = htobe64(7);
+ EXPECT_EQ(0, avb_descriptor_validate_and_byteswap(&h, &s));
+}
+
+TEST(UtilTest, SafeAddition) {
+ uint64_t value;
+ uint64_t pow2_60 = 1ULL << 60;
+
+ value = 2;
+ EXPECT_NE(0, avb_safe_add_to(&value, 5));
+ EXPECT_EQ(7UL, value);
+
+ /* These should not overflow */
+ value = 1 * pow2_60;
+ EXPECT_NE(0, avb_safe_add_to(&value, 2 * pow2_60));
+ EXPECT_EQ(3 * pow2_60, value);
+ value = 7 * pow2_60;
+ EXPECT_NE(0, avb_safe_add_to(&value, 8 * pow2_60));
+ EXPECT_EQ(15 * pow2_60, value);
+ value = 9 * pow2_60;
+ EXPECT_NE(0, avb_safe_add_to(&value, 3 * pow2_60));
+ EXPECT_EQ(12 * pow2_60, value);
+ value = 0xfffffffffffffffcUL;
+ EXPECT_NE(0, avb_safe_add_to(&value, 2));
+ EXPECT_EQ(0xfffffffffffffffeUL, value);
+
+ /* These should overflow. */
+ value = 8 * pow2_60;
+ EXPECT_EQ(0, avb_safe_add_to(&value, 8 * pow2_60));
+ value = 0xfffffffffffffffcUL;
+ EXPECT_EQ(0, avb_safe_add_to(&value, 4));
+}
+
+static int avb_validate_utf8z(const char* data) {
+ return avb_validate_utf8(reinterpret_cast<const uint8_t*>(data),
+ strlen(data));
+}
+
+TEST(UtilTest, UTF8Validation) {
+ // These should succeed.
+ EXPECT_NE(0, avb_validate_utf8z("foo bar"));
+ // Encoding of U+00E6 LATIN SMALL LETTER AE: æ
+ EXPECT_NE(0, avb_validate_utf8z("foo \xC3\xA6 bar"));
+ // Encoding of U+20AC EURO SIGN: €
+ EXPECT_NE(0, avb_validate_utf8z("foo \xE2\x82\xAC bar"));
+ // Encoding of U+1F466 BOY: 👦
+ EXPECT_NE(0, avb_validate_utf8z("foo \xF0\x9F\x91\xA6 bar"));
+ // All three runes following each other.
+ EXPECT_NE(0, avb_validate_utf8z("\xC3\xA6\xE2\x82\xAC\xF0\x9F\x91\xA6"));
+
+ // These should fail.
+ EXPECT_EQ(0, avb_validate_utf8z("foo \xF8 bar"));
+ EXPECT_EQ(0, avb_validate_utf8z("\xF8"));
+ // Stops in the middle of Unicode rune.
+ EXPECT_EQ(0, avb_validate_utf8z("foo \xC3"));
+}
+
+TEST(UtilTest, StrConcat) {
+ char buf[8];
+
+ // These should succeed.
+ EXPECT_NE(0, avb_str_concat(buf, sizeof buf, "foo", 3, "bar1", 4));
+
+ // This should fail: Insufficient space.
+ EXPECT_EQ(0, avb_str_concat(buf, sizeof buf, "foo0", 4, "bar1", 4));
+}
+
+TEST(UtilTest, StrStr) {
+ const char* haystack = "abc def abcabc";
+
+ EXPECT_EQ(nullptr, avb_strstr(haystack, "needle"));
+ EXPECT_EQ(haystack, avb_strstr(haystack, "abc"));
+ EXPECT_EQ(haystack + 4, avb_strstr(haystack, "def"));
+ EXPECT_EQ(haystack, avb_strstr(haystack, haystack));
+}
+
+TEST(UtilTest, StrvFindStr) {
+ const char* strings[] = {"abcabc", "abc", "def", nullptr};
+
+ EXPECT_EQ(nullptr, avb_strv_find_str(strings, "not there", 9));
+ EXPECT_EQ(strings[1], avb_strv_find_str(strings, "abc", 3));
+ EXPECT_EQ(strings[2], avb_strv_find_str(strings, "def", 3));
+ EXPECT_EQ(strings[0], avb_strv_find_str(strings, "abcabc", 6));
+}
+
+TEST(UtilTest, StrReplace) {
+ // We don't care about leaking strings from avb_replace().
+ EXPECT_EQ("OK blah bah $(FOO OK blah",
+ std::string(avb_replace(
+ "$(FOO) blah bah $(FOO $(FOO) blah", "$(FOO)", "OK")));
+ EXPECT_EQ("OK", std::string(avb_replace("$(FOO)", "$(FOO)", "OK")));
+ EXPECT_EQ(" OK", std::string(avb_replace(" $(FOO)", "$(FOO)", "OK")));
+ EXPECT_EQ("OK ", std::string(avb_replace("$(FOO) ", "$(FOO)", "OK")));
+ EXPECT_EQ("LONGSTRINGLONGSTRING",
+ std::string(avb_replace("$(FOO)$(FOO)", "$(FOO)", "LONGSTRING")));
+}
+
+TEST(UtilTest, Crc32) {
+ /* Compare with output of crc32(1):
+ *
+ * $ (echo -n foobar > /tmp/crc32_input); crc32 /tmp/crc32_input
+ * 9ef61f95
+ */
+ EXPECT_EQ(uint32_t(0x9ef61f95), avb_crc32((const uint8_t*)"foobar", 6));
+}
+
+TEST(UtilTest, htobe32) {
+ EXPECT_EQ(avb_htobe32(0x12345678), htobe32(0x12345678));
+}
+
+TEST(UtilTest, be32toh) {
+ EXPECT_EQ(avb_be32toh(0x12345678), be32toh(0x12345678));
+}
+
+TEST(UtilTest, htobe64) {
+ EXPECT_EQ(avb_htobe64(0x123456789abcdef0), htobe64(0x123456789abcdef0));
+}
+
+TEST(UtilTest, be64toh) {
+ EXPECT_EQ(avb_be64toh(0x123456789abcdef0), be64toh(0x123456789abcdef0));
+}
diff --git a/avb/test/avb_vbmeta_image_unittest.cc b/avb/test/avb_vbmeta_image_unittest.cc
new file mode 100644
index 0000000..560dd01
--- /dev/null
+++ b/avb/test/avb_vbmeta_image_unittest.cc
@@ -0,0 +1,567 @@
+/*
+ * 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 <libavb/libavb.h>
+
+#include "avb_unittest_util.h"
+
+namespace avb {
+
+class VerifyTest : public BaseAvbToolTest {
+ 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 avb_vbmeta_image_verify() returns
+ // |expected_result|.
+ bool test_modification(AvbVBMetaVerifyResult expected_result,
+ size_t offset,
+ size_t length);
+};
+
+TEST_F(VerifyTest, BootImageStructSize) {
+ EXPECT_EQ(256UL, sizeof(AvbVBMetaImageHeader));
+}
+
+TEST_F(VerifyTest, CheckSHA256RSA2048) {
+ GenerateVBMetaImage("vbmeta.img",
+ "SHA256_RSA2048",
+ 0,
+ base::FilePath("test/data/testkey_rsa2048.pem"));
+ EXPECT_EQ(AVB_VBMETA_VERIFY_RESULT_OK,
+ avb_vbmeta_image_verify(
+ vbmeta_image_.data(), vbmeta_image_.size(), NULL, NULL));
+}
+
+TEST_F(VerifyTest, CheckSHA256RSA4096) {
+ GenerateVBMetaImage("vbmeta.img",
+ "SHA256_RSA4096",
+ 0,
+ base::FilePath("test/data/testkey_rsa4096.pem"));
+ EXPECT_EQ(AVB_VBMETA_VERIFY_RESULT_OK,
+ avb_vbmeta_image_verify(
+ vbmeta_image_.data(), vbmeta_image_.size(), NULL, NULL));
+}
+
+TEST_F(VerifyTest, CheckSHA256RSA8192) {
+ GenerateVBMetaImage("vbmeta.img",
+ "SHA256_RSA8192",
+ 0,
+ base::FilePath("test/data/testkey_rsa8192.pem"));
+ EXPECT_EQ(AVB_VBMETA_VERIFY_RESULT_OK,
+ avb_vbmeta_image_verify(
+ vbmeta_image_.data(), vbmeta_image_.size(), NULL, NULL));
+}
+
+TEST_F(VerifyTest, CheckSHA512RSA2048) {
+ GenerateVBMetaImage("vbmeta.img",
+ "SHA512_RSA2048",
+ 0,
+ base::FilePath("test/data/testkey_rsa2048.pem"));
+ EXPECT_EQ(AVB_VBMETA_VERIFY_RESULT_OK,
+ avb_vbmeta_image_verify(
+ vbmeta_image_.data(), vbmeta_image_.size(), NULL, NULL));
+}
+
+TEST_F(VerifyTest, CheckSHA512RSA4096) {
+ GenerateVBMetaImage("vbmeta.img",
+ "SHA512_RSA4096",
+ 0,
+ base::FilePath("test/data/testkey_rsa4096.pem"));
+ EXPECT_EQ(AVB_VBMETA_VERIFY_RESULT_OK,
+ avb_vbmeta_image_verify(
+ vbmeta_image_.data(), vbmeta_image_.size(), NULL, NULL));
+}
+
+TEST_F(VerifyTest, CheckSHA512RSA8192) {
+ GenerateVBMetaImage("vbmeta.img",
+ "SHA512_RSA8192",
+ 0,
+ base::FilePath("test/data/testkey_rsa8192.pem"));
+ EXPECT_EQ(AVB_VBMETA_VERIFY_RESULT_OK,
+ avb_vbmeta_image_verify(
+ vbmeta_image_.data(), vbmeta_image_.size(), NULL, NULL));
+}
+
+TEST_F(VerifyTest, CheckUnsigned) {
+ GenerateVBMetaImage("vbmeta.img", "", 0, base::FilePath(""));
+ EXPECT_EQ(AVB_VBMETA_VERIFY_RESULT_OK_NOT_SIGNED,
+ avb_vbmeta_image_verify(
+ vbmeta_image_.data(), vbmeta_image_.size(), NULL, NULL));
+}
+
+TEST_F(VerifyTest, CheckBiggerLength) {
+ GenerateVBMetaImage("vbmeta.img",
+ "SHA256_RSA2048",
+ 0,
+ base::FilePath("test/data/testkey_rsa2048.pem"));
+ // Check that it's OK if we pass a bigger length than what the
+ // header indicates.
+ EXPECT_EQ(AVB_VBMETA_VERIFY_RESULT_OK,
+ avb_vbmeta_image_verify(
+ vbmeta_image_.data(), vbmeta_image_.size() + 8192, NULL, NULL));
+}
+
+TEST_F(VerifyTest, BadMagic) {
+ GenerateVBMetaImage("vbmeta.img",
+ "SHA256_RSA2048",
+ 0,
+ base::FilePath("test/data/testkey_rsa2048.pem"));
+ vbmeta_image_[0] = 'Z';
+ EXPECT_EQ(AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER,
+ avb_vbmeta_image_verify(
+ vbmeta_image_.data(), vbmeta_image_.size(), NULL, NULL));
+}
+
+TEST_F(VerifyTest, MajorVersionCheck) {
+ GenerateVBMetaImage("vbmeta.img",
+ "SHA256_RSA2048",
+ 0,
+ base::FilePath("test/data/testkey_rsa2048.pem"));
+
+ AvbVBMetaImageHeader* h =
+ reinterpret_cast<AvbVBMetaImageHeader*>(vbmeta_image_.data());
+ h->header_version_major = htobe32(1 + be32toh(h->header_version_major));
+ EXPECT_EQ(AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER,
+ avb_vbmeta_image_verify(
+ vbmeta_image_.data(), vbmeta_image_.size(), NULL, NULL));
+}
+
+TEST_F(VerifyTest, MinorVersionCheck) {
+ GenerateVBMetaImage("vbmeta.img", "", 0, base::FilePath(""));
+
+ AvbVBMetaImageHeader* h =
+ reinterpret_cast<AvbVBMetaImageHeader*>(vbmeta_image_.data());
+ h->header_version_minor = htobe32(1 + be32toh(h->header_version_minor));
+ EXPECT_EQ(AVB_VBMETA_VERIFY_RESULT_OK_NOT_SIGNED,
+ avb_vbmeta_image_verify(
+ vbmeta_image_.data(), vbmeta_image_.size(), NULL, NULL));
+}
+
+TEST_F(VerifyTest, BlockSizesAddUpToLessThanLength) {
+ GenerateVBMetaImage("vbmeta.img",
+ "SHA256_RSA2048",
+ 0,
+ base::FilePath("test/data/testkey_rsa2048.pem"));
+
+ AvbVBMetaImageHeader* h =
+ reinterpret_cast<AvbVBMetaImageHeader*>(vbmeta_image_.data());
+ AvbVBMetaImageHeader backup = *h;
+
+ // Check that the sum of the two 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 = vbmeta_image_.size() & (~0x3f);
+
+ h->authentication_data_block_size = htobe64(size);
+ EXPECT_EQ(AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER,
+ avb_vbmeta_image_verify(
+ vbmeta_image_.data(), vbmeta_image_.size(), NULL, NULL));
+ *h = backup;
+
+ h->auxiliary_data_block_size = htobe64(size);
+ EXPECT_EQ(AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER,
+ avb_vbmeta_image_verify(
+ vbmeta_image_.data(), vbmeta_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(AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER,
+ avb_vbmeta_image_verify(
+ vbmeta_image_.data(), vbmeta_image_.size(), NULL, NULL));
+ *h = backup;
+
+ h->auxiliary_data_block_size = htobe64(size);
+ EXPECT_EQ(AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER,
+ avb_vbmeta_image_verify(
+ vbmeta_image_.data(), vbmeta_image_.size(), NULL, NULL));
+ *h = backup;
+
+ EXPECT_EQ(AVB_VBMETA_VERIFY_RESULT_OK,
+ avb_vbmeta_image_verify(
+ vbmeta_image_.data(), vbmeta_image_.size(), NULL, NULL));
+}
+
+TEST_F(VerifyTest, BlockSizesMultipleOf64) {
+ GenerateVBMetaImage("vbmeta.img",
+ "SHA256_RSA2048",
+ 0,
+ base::FilePath("test/data/testkey_rsa2048.pem"));
+
+ AvbVBMetaImageHeader* h =
+ reinterpret_cast<AvbVBMetaImageHeader*>(vbmeta_image_.data());
+ AvbVBMetaImageHeader backup = *h;
+
+ h->authentication_data_block_size =
+ htobe32(be32toh(h->authentication_data_block_size) - 32);
+ EXPECT_EQ(AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER,
+ avb_vbmeta_image_verify(
+ vbmeta_image_.data(), vbmeta_image_.size() - 32, NULL, NULL));
+ *h = backup;
+
+ h->auxiliary_data_block_size =
+ htobe32(be32toh(h->auxiliary_data_block_size) - 32);
+ EXPECT_EQ(AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER,
+ avb_vbmeta_image_verify(
+ vbmeta_image_.data(), vbmeta_image_.size() - 32, NULL, NULL));
+ *h = backup;
+
+ EXPECT_EQ(AVB_VBMETA_VERIFY_RESULT_OK,
+ avb_vbmeta_image_verify(
+ vbmeta_image_.data(), vbmeta_image_.size(), NULL, NULL));
+}
+
+TEST_F(VerifyTest, HashOutOfBounds) {
+ GenerateVBMetaImage("vbmeta.img",
+ "SHA256_RSA2048",
+ 0,
+ base::FilePath("test/data/testkey_rsa2048.pem"));
+
+ AvbVBMetaImageHeader* h =
+ reinterpret_cast<AvbVBMetaImageHeader*>(vbmeta_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(AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER,
+ avb_vbmeta_image_verify(
+ vbmeta_image_.data(), vbmeta_image_.size(), NULL, NULL));
+
+ // Overflow checks.
+ h->hash_offset = htobe64(4);
+ h->hash_size = htobe64(0xfffffffffffffffeUL);
+ EXPECT_EQ(AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER,
+ avb_vbmeta_image_verify(
+ vbmeta_image_.data(), vbmeta_image_.size(), NULL, NULL));
+}
+
+TEST_F(VerifyTest, SignatureOutOfBounds) {
+ GenerateVBMetaImage("vbmeta.img",
+ "SHA256_RSA2048",
+ 0,
+ base::FilePath("test/data/testkey_rsa2048.pem"));
+
+ AvbVBMetaImageHeader* h =
+ reinterpret_cast<AvbVBMetaImageHeader*>(vbmeta_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(AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER,
+ avb_vbmeta_image_verify(
+ vbmeta_image_.data(), vbmeta_image_.size(), NULL, NULL));
+
+ // Overflow checks.
+ h->signature_offset = htobe64(4);
+ h->signature_size = htobe64(0xfffffffffffffffeUL);
+ EXPECT_EQ(AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER,
+ avb_vbmeta_image_verify(
+ vbmeta_image_.data(), vbmeta_image_.size(), NULL, NULL));
+}
+
+TEST_F(VerifyTest, PublicKeyOutOfBounds) {
+ GenerateVBMetaImage("vbmeta.img",
+ "SHA256_RSA2048",
+ 0,
+ base::FilePath("test/data/testkey_rsa2048.pem"));
+
+ AvbVBMetaImageHeader* h =
+ reinterpret_cast<AvbVBMetaImageHeader*>(vbmeta_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->auxiliary_data_block_size));
+ EXPECT_EQ(AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER,
+ avb_vbmeta_image_verify(
+ vbmeta_image_.data(), vbmeta_image_.size(), NULL, NULL));
+
+ // Overflow checks.
+ h->public_key_offset = htobe64(4);
+ h->public_key_size = htobe64(0xfffffffffffffffeUL);
+ EXPECT_EQ(AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER,
+ avb_vbmeta_image_verify(
+ vbmeta_image_.data(), vbmeta_image_.size(), NULL, NULL));
+}
+
+TEST_F(VerifyTest, PublicKeyMetadataOutOfBounds) {
+ GenerateVBMetaImage("vbmeta.img",
+ "SHA256_RSA2048",
+ 0,
+ base::FilePath("test/data/testkey_rsa2048.pem"));
+
+ AvbVBMetaImageHeader* h =
+ reinterpret_cast<AvbVBMetaImageHeader*>(vbmeta_image_.data());
+
+ // Check we catch when public key metadata data goes out of bounds.
+ h->public_key_metadata_offset = htobe64(4);
+ h->public_key_metadata_size = htobe64(be64toh(h->auxiliary_data_block_size));
+ EXPECT_EQ(AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER,
+ avb_vbmeta_image_verify(
+ vbmeta_image_.data(), vbmeta_image_.size(), NULL, NULL));
+
+ // Overflow checks.
+ h->public_key_metadata_offset = htobe64(4);
+ h->public_key_metadata_size = htobe64(0xfffffffffffffffeUL);
+ EXPECT_EQ(AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER,
+ avb_vbmeta_image_verify(
+ vbmeta_image_.data(), vbmeta_image_.size(), NULL, NULL));
+}
+
+TEST_F(VerifyTest, InvalidAlgorithmField) {
+ GenerateVBMetaImage("vbmeta.img",
+ "SHA256_RSA2048",
+ 0,
+ base::FilePath("test/data/testkey_rsa2048.pem"));
+
+ AvbVBMetaImageHeader* h =
+ reinterpret_cast<AvbVBMetaImageHeader*>(vbmeta_image_.data());
+ AvbVBMetaImageHeader backup = *h;
+
+ // Check we bail on unknown algorithm.
+ h->algorithm_type = htobe32(_AVB_ALGORITHM_NUM_TYPES);
+ EXPECT_EQ(AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER,
+ avb_vbmeta_image_verify(
+ vbmeta_image_.data(), vbmeta_image_.size(), NULL, NULL));
+ *h = backup;
+ EXPECT_EQ(AVB_VBMETA_VERIFY_RESULT_OK,
+ avb_vbmeta_image_verify(
+ vbmeta_image_.data(), vbmeta_image_.size(), NULL, NULL));
+}
+
+TEST_F(VerifyTest, PublicKeyBlockTooSmall) {
+ GenerateVBMetaImage("vbmeta.img",
+ "SHA256_RSA2048",
+ 0,
+ base::FilePath("test/data/testkey_rsa2048.pem"));
+
+ AvbVBMetaImageHeader* h =
+ reinterpret_cast<AvbVBMetaImageHeader*>(vbmeta_image_.data());
+ AvbVBMetaImageHeader backup = *h;
+
+ // Check we bail if the auxiliary data block is too small.
+ uint64_t change = be64toh(h->auxiliary_data_block_size) - 64;
+ h->auxiliary_data_block_size = htobe64(change);
+ EXPECT_EQ(
+ AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER,
+ avb_vbmeta_image_verify(
+ vbmeta_image_.data(), vbmeta_image_.size() - change, NULL, NULL));
+ *h = backup;
+ EXPECT_EQ(AVB_VBMETA_VERIFY_RESULT_OK,
+ avb_vbmeta_image_verify(
+ vbmeta_image_.data(), vbmeta_image_.size(), NULL, NULL));
+}
+
+bool VerifyTest::test_modification(AvbVBMetaVerifyResult expected_result,
+ size_t offset,
+ size_t length) {
+ uint8_t* d = reinterpret_cast<uint8_t*>(vbmeta_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;
+ AvbVBMetaVerifyResult result = avb_vbmeta_image_verify(
+ vbmeta_image_.data(), vbmeta_image_.size(), NULL, NULL);
+ d[o] ^= 0x80;
+ if (result != expected_result) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+TEST_F(VerifyTest, ModificationDetection) {
+ GenerateVBMetaImage("vbmeta.img",
+ "SHA256_RSA2048",
+ 0,
+ base::FilePath("test/data/testkey_rsa2048.pem"));
+
+ EXPECT_EQ(AVB_VBMETA_VERIFY_RESULT_OK,
+ avb_vbmeta_image_verify(
+ vbmeta_image_.data(), vbmeta_image_.size(), NULL, NULL));
+
+ AvbVBMetaImageHeader h;
+ avb_vbmeta_image_header_to_host_byte_order(
+ reinterpret_cast<AvbVBMetaImageHeader*>(vbmeta_image_.data()), &h);
+
+ size_t header_block_offset = 0;
+ size_t authentication_block_offset =
+ header_block_offset + sizeof(AvbVBMetaImageHeader);
+ size_t auxiliary_block_offset =
+ authentication_block_offset + h.authentication_data_block_size;
+
+ // Ensure we detect modification of the header data block. Do this
+ // in a field that's not validated so INVALID_VBMETA_HEADER
+ // isn't returned.
+ EXPECT_TRUE(test_modification(
+ AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH,
+ offsetof(AvbVBMetaImageHeader, reserved),
+ sizeof(AvbVBMetaImageHeader) - offsetof(AvbVBMetaImageHeader, reserved)));
+ // Also check the |reserved| field.
+ EXPECT_TRUE(test_modification(AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH,
+ offsetof(AvbVBMetaImageHeader, reserved),
+ sizeof(AvbVBMetaImageHeader().reserved)));
+
+ // Ensure we detect modifications in the auxiliary data block.
+ EXPECT_TRUE(test_modification(AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH,
+ auxiliary_block_offset,
+ h.auxiliary_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(AVB_VBMETA_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(AVB_VBMETA_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(AVB_VBMETA_VERIFY_RESULT_OK,
+ authentication_block_offset + offset,
+ h.authentication_data_block_size - offset));
+}
+
+TEST_F(VerifyTest, VBMetaHeaderByteswap) {
+ AvbVBMetaImageHeader h;
+ AvbVBMetaImageHeader 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.auxiliary_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.public_key_metadata_offset = htobe64(n64);
+ n64++;
+ h.public_key_metadata_size = htobe64(n64);
+ n64++;
+ h.descriptors_offset = htobe64(n64);
+ n64++;
+ h.descriptors_size = htobe64(n64);
+ n64++;
+ h.rollback_index = htobe64(n64);
+ n64++;
+ h.flags = htobe32(n32);
+ n32++;
+
+ avb_vbmeta_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.auxiliary_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.public_key_metadata_offset);
+ n64++;
+ EXPECT_EQ(n64, s.public_key_metadata_size);
+ n64++;
+ EXPECT_EQ(n64, s.descriptors_offset);
+ n64++;
+ EXPECT_EQ(n64, s.descriptors_size);
+ n64++;
+ EXPECT_EQ(n64, s.rollback_index);
+ n64++;
+ EXPECT_EQ(n32, s.flags);
+ n32++;
+
+ // If new fields are added, the following will fail. This is to
+ // remind that byteswapping code (in avb_util.c) and unittests for
+ // this should be updated.
+ static_assert(offsetof(AvbVBMetaImageHeader, reserved) == 124,
+ "Remember to unittest byteswapping of newly added fields");
+}
+
+} // namespace avb
diff --git a/avb/test/avbtool_signing_helper_test.py b/avb/test/avbtool_signing_helper_test.py
new file mode 100755
index 0000000..7291797
--- /dev/null
+++ b/avb/test/avbtool_signing_helper_test.py
@@ -0,0 +1,71 @@
+#!/usr/bin/env python
+
+#
+# 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 libavb_host.a and fails
+# if a reference not starting with avb_ is referenced. It's intended
+# to catch mistakes where the standard C library is inadvertently
+# used.
+
+import subprocess
+import sys
+import errno
+import os
+
+def rsa_signer(argv):
+ if len(argv) != 3:
+ sys.stderr.write("Wrong number of arguments: {} <alg> <pub key>\n".format(argv[0]))
+ return errno.EINVAL
+
+ data = sys.stdin.read()
+ if len(data) == 0:
+ sys.stderr.write("There is not input data\n")
+ return errno.EINVAL
+
+ if 'SIGNING_HELPER_TEST' not in os.environ or os.environ['SIGNING_HELPER_TEST'] == "":
+ sys.stderr.write("env SIGNING_HELPER_TEST is not set or empty\n")
+ return errno.EINVAL
+
+ test_file_name = os.environ['SIGNING_HELPER_TEST']
+ if os.path.isfile(test_file_name) and not os.access(test_file_name, os.W_OK):
+ sys.stderr.write("no permission to write into {} file\n".format(test_file_name))
+ return errno.EACCESS
+
+ p = subprocess.Popen(
+ ['openssl', 'rsautl', '-sign', '-inkey', argv[2], '-raw'],
+ stdin=subprocess.PIPE)
+
+ p.communicate(data)
+ retcode = p.wait()
+ if retcode != 0:
+ return retcode
+
+ with open(test_file_name, "w") as f:
+ f.write("DONE")
+
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(rsa_signer(sys.argv))
diff --git a/avb/test/avbtool_unittest.cc b/avb/test/avbtool_unittest.cc
new file mode 100644
index 0000000..b089e3c
--- /dev/null
+++ b/avb/test/avbtool_unittest.cc
@@ -0,0 +1,1323 @@
+/*
+ * 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_split.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+
+#include <libavb/avb_sha.h>
+#include <libavb/libavb.h>
+
+#include "avb_unittest_util.h"
+
+namespace avb {
+
+class AvbToolTest : public BaseAvbToolTest {
+ public:
+ AvbToolTest() {}
+
+ void AddHashFooterTest(bool sparse_image);
+ void AddHashtreeFooterTest(bool sparse_image);
+ void AddHashtreeFooterFECTest(bool sparse_image);
+};
+
+// This test ensure that the version is increased in both
+// avb_boot_image.h and the avb tool.
+TEST_F(AvbToolTest, AvbVersionInSync) {
+ base::FilePath path = testdir_.Append("version.txt");
+ EXPECT_COMMAND(0, "./avbtool 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", AVB_MAJOR_VERSION, AVB_MINOR_VERSION);
+ EXPECT_EQ(printed_version, expected_version);
+}
+
+TEST_F(AvbToolTest, ExtractPublicKey) {
+ GenerateVBMetaImage("vbmeta.img",
+ "SHA256_RSA2048",
+ 0,
+ base::FilePath("test/data/testkey_rsa2048.pem"));
+
+ std::string key_data =
+ PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem"));
+
+ AvbVBMetaImageHeader h;
+ avb_vbmeta_image_header_to_host_byte_order(
+ reinterpret_cast<AvbVBMetaImageHeader*>(vbmeta_image_.data()), &h);
+ uint8_t* d = reinterpret_cast<uint8_t*>(vbmeta_image_.data());
+ size_t auxiliary_data_block_offset =
+ sizeof(AvbVBMetaImageHeader) + h.authentication_data_block_size;
+ EXPECT_GT(h.auxiliary_data_block_size, key_data.size());
+ EXPECT_EQ(0,
+ memcmp(key_data.data(),
+ d + auxiliary_data_block_offset + h.public_key_offset,
+ key_data.size()));
+}
+
+TEST_F(AvbToolTest, CheckDescriptors) {
+ GenerateVBMetaImage("vbmeta.img",
+ "SHA256_RSA2048",
+ 0,
+ base::FilePath("test/data/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/data/small_blob.bin ");
+
+ AvbVBMetaImageHeader h;
+ avb_vbmeta_image_header_to_host_byte_order(
+ reinterpret_cast<AvbVBMetaImageHeader*>(vbmeta_image_.data()), &h);
+
+ EXPECT_EQ(AVB_VBMETA_VERIFY_RESULT_OK,
+ avb_vbmeta_image_verify(
+ vbmeta_image_.data(), vbmeta_image_.size(), nullptr, nullptr));
+
+ const char* s;
+ size_t len;
+ uint64_t val;
+
+ // Basic.
+ s = avb_property_lookup(
+ vbmeta_image_.data(), vbmeta_image_.size(), "foo", 0, &len);
+ EXPECT_EQ(0, strcmp(s, "brillo"));
+ EXPECT_EQ(6U, len);
+ s = avb_property_lookup(
+ vbmeta_image_.data(), vbmeta_image_.size(), "bar", 0, &len);
+ EXPECT_EQ(0, strcmp(s, "chromeos"));
+ EXPECT_EQ(8U, len);
+ s = avb_property_lookup(
+ vbmeta_image_.data(), vbmeta_image_.size(), "non-existant", 0, &len);
+ EXPECT_EQ(0U, len);
+ EXPECT_EQ(NULL, s);
+
+ // Numbers.
+ EXPECT_NE(
+ 0,
+ avb_property_lookup_uint64(
+ vbmeta_image_.data(), vbmeta_image_.size(), "prisoner", 0, &val));
+ EXPECT_EQ(24601U, val);
+
+ EXPECT_NE(
+ 0,
+ avb_property_lookup_uint64(
+ vbmeta_image_.data(), vbmeta_image_.size(), "hexnumber", 0, &val));
+ EXPECT_EQ(0xcafeU, val);
+
+ EXPECT_NE(0,
+ avb_property_lookup_uint64(vbmeta_image_.data(),
+ vbmeta_image_.size(),
+ "hexnumber_capital",
+ 0,
+ &val));
+ EXPECT_EQ(0xcafeU, val);
+
+ EXPECT_NE(0,
+ avb_property_lookup_uint64(vbmeta_image_.data(),
+ vbmeta_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,
+ avb_property_lookup_uint64(vbmeta_image_.data(),
+ vbmeta_image_.size(),
+ "larger_than_uint64",
+ 0,
+ &val));
+ EXPECT_EQ(0xedcba98765432101U, val);
+
+ // Number-parsing failures.
+ EXPECT_EQ(0,
+ avb_property_lookup_uint64(
+ vbmeta_image_.data(), vbmeta_image_.size(), "foo", 0, &val));
+
+ EXPECT_EQ(0,
+ avb_property_lookup_uint64(vbmeta_image_.data(),
+ vbmeta_image_.size(),
+ "almost_a_number",
+ 0,
+ &val));
+
+ // Blobs.
+ //
+ // test/data/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 = avb_property_lookup(
+ vbmeta_image_.data(), vbmeta_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(AvbToolTest, CheckRollbackIndex) {
+ uint64_t rollback_index = 42;
+ GenerateVBMetaImage("vbmeta.img",
+ "SHA256_RSA2048",
+ rollback_index,
+ base::FilePath("test/data/testkey_rsa2048.pem"));
+
+ AvbVBMetaImageHeader h;
+ avb_vbmeta_image_header_to_host_byte_order(
+ reinterpret_cast<AvbVBMetaImageHeader*>(vbmeta_image_.data()), &h);
+
+ EXPECT_EQ(rollback_index, h.rollback_index);
+}
+
+TEST_F(AvbToolTest, CheckPubkeyReturned) {
+ GenerateVBMetaImage("vbmeta.img",
+ "SHA256_RSA2048",
+ 0,
+ base::FilePath("test/data/testkey_rsa2048.pem"));
+
+ const uint8_t* pubkey = NULL;
+ size_t pubkey_length = 0;
+
+ EXPECT_EQ(
+ AVB_VBMETA_VERIFY_RESULT_OK,
+ avb_vbmeta_image_verify(
+ vbmeta_image_.data(), vbmeta_image_.size(), &pubkey, &pubkey_length));
+
+ AvbVBMetaImageHeader h;
+ avb_vbmeta_image_header_to_host_byte_order(
+ reinterpret_cast<AvbVBMetaImageHeader*>(vbmeta_image_.data()), &h);
+
+ EXPECT_EQ(pubkey_length, h.public_key_size);
+
+ const uint8_t* expected_pubkey =
+ vbmeta_image_.data() + sizeof(AvbVBMetaImageHeader) +
+ h.authentication_data_block_size + h.public_key_offset;
+ EXPECT_EQ(pubkey, expected_pubkey);
+}
+
+TEST_F(AvbToolTest, Info) {
+ GenerateVBMetaImage("vbmeta.img",
+ "SHA256_RSA2048",
+ 0,
+ base::FilePath("test/data/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/data/small_blob.bin "
+ "--prop_from_file large_blob:test/data/large_blob.bin");
+
+ ASSERT_EQ(
+ "VBMeta image version: 1.0\n"
+ "Header Block: 256 bytes\n"
+ "Authentication Block: 320 bytes\n"
+ "Auxiliary Block: 3200 bytes\n"
+ "Algorithm: SHA256_RSA2048\n"
+ "Rollback Index: 0\n"
+ "Flags: 0\n"
+ "Descriptors:\n"
+ " Prop: foo -> 'brillo'\n"
+ " Prop: bar -> 'chromeos'\n"
+ " Prop: prisoner -> '24601'\n"
+ " Prop: hexnumber -> '0xcafe'\n"
+ " Prop: hexnumber_capital -> '0xCAFE'\n"
+ " Prop: large_hexnumber -> '0xfedcba9876543210'\n"
+ " Prop: larger_than_uint64 -> '0xfedcba98765432101'\n"
+ " Prop: almost_a_number -> '423x'\n"
+ " Prop: blob -> '\\x00\\x00brillo "
+ "ftw!\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\n'\n"
+ " Prop: large_blob -> (2048 bytes)\n",
+ InfoImage(vbmeta_image_path_));
+}
+
+static bool collect_descriptors(const AvbDescriptor* descriptor,
+ void* user_data) {
+ std::vector<const AvbDescriptor*>* descriptors =
+ reinterpret_cast<std::vector<const AvbDescriptor*>*>(user_data);
+ descriptors->push_back(descriptor);
+ return true; // Keep iterating.
+}
+
+void AvbToolTest::AddHashFooterTest(bool sparse_image) {
+ const size_t rootfs_size = 1028 * 1024;
+ const size_t partition_size = 1536 * 1024;
+
+ // Generate a 1028 KiB file with known content. Some content have
+ // been arranged to ensure FILL_DATA segments in the sparse file.
+ std::vector<uint8_t> rootfs;
+ rootfs.resize(rootfs_size);
+ for (size_t n = 0; n < rootfs_size; n++) {
+ if ((n >= 5 * 1000 && n < 105 * 1000) ||
+ (n >= 205 * 1000 && n < 305 * 1000) ||
+ (n >= 505 * 1000 && n < 605 * 1000)) {
+ rootfs[n] = uint8_t(n) & 0x03;
+ } else {
+ rootfs[n] = uint8_t(n);
+ }
+ }
+ base::FilePath rootfs_path = testdir_.Append("rootfs.bin");
+ EXPECT_EQ(rootfs_size,
+ static_cast<const size_t>(
+ base::WriteFile(rootfs_path,
+ reinterpret_cast<const char*>(rootfs.data()),
+ rootfs.size())));
+
+ if (sparse_image) {
+ EXPECT_COMMAND(0,
+ "mv %s %s.unsparse",
+ rootfs_path.value().c_str(),
+ rootfs_path.value().c_str());
+ EXPECT_COMMAND(0,
+ "img2simg %s.unsparse %s",
+ rootfs_path.value().c_str(),
+ rootfs_path.value().c_str());
+ EXPECT_COMMAND(0, "rm -f %s.unsparse", rootfs_path.value().c_str());
+ }
+
+ /* Do this twice to check that 'add_hash_footer' is idempotent. */
+ for (int n = 0; n < 2; n++) {
+ EXPECT_COMMAND(0,
+ "./avbtool add_hash_footer --salt d00df00d "
+ "--hash_algorithm sha256 --image %s "
+ "--partition_size %d --partition_name foobar "
+ "--algorithm SHA256_RSA2048 "
+ "--key test/data/testkey_rsa2048.pem",
+ rootfs_path.value().c_str(),
+ (int)partition_size);
+
+ ASSERT_EQ(base::StringPrintf("Footer version: 1.0\n"
+ "Image size: 1572864 bytes\n"
+ "Original image size: 1052672 bytes\n"
+ "VBMeta offset: 1052672\n"
+ "VBMeta size: 1280 bytes\n"
+ "--\n"
+ "VBMeta image version: 1.0%s\n"
+ "Header Block: 256 bytes\n"
+ "Authentication Block: 320 bytes\n"
+ "Auxiliary Block: 704 bytes\n"
+ "Algorithm: SHA256_RSA2048\n"
+ "Rollback Index: 0\n"
+ "Flags: 0\n"
+ "Descriptors:\n"
+ " Hash descriptor:\n"
+ " Image Size: 1052672 bytes\n"
+ " Hash Algorithm: sha256\n"
+ " Partition Name: foobar\n"
+ " Salt: d00df00d\n"
+ " Digest: "
+ "9a58cc996d405e08a1e00f96dbfe9104fedf41cb83b1f"
+ "5e4ed357fbcf58d88d9\n",
+ sparse_image ? " (Sparse)" : ""),
+ InfoImage(rootfs_path));
+ }
+
+ if (sparse_image) {
+ EXPECT_COMMAND(0,
+ "mv %s %s.sparse",
+ rootfs_path.value().c_str(),
+ rootfs_path.value().c_str());
+ EXPECT_COMMAND(0,
+ "simg2img %s.sparse %s",
+ rootfs_path.value().c_str(),
+ rootfs_path.value().c_str());
+ EXPECT_COMMAND(0, "rm -f %s.sparse", rootfs_path.value().c_str());
+ }
+
+ // Manually calculate the hash to check that it agrees with avbtool.
+ AvbSHA256Ctx hasher_ctx;
+ const uint8_t hasher_salt[4] = {0xd0, 0x0d, 0xf0, 0x0d};
+ avb_sha256_init(&hasher_ctx);
+ avb_sha256_update(&hasher_ctx, hasher_salt, 4);
+ avb_sha256_update(&hasher_ctx, rootfs.data(), rootfs_size);
+ uint8_t* hasher_digest = avb_sha256_final(&hasher_ctx);
+ EXPECT_EQ("9a58cc996d405e08a1e00f96dbfe9104fedf41cb83b1f5e4ed357fbcf58d88d9",
+ mem_to_hexstring(hasher_digest, AVB_SHA256_DIGEST_SIZE));
+
+ // Now check that we can find the VBMeta block again from the footer.
+ std::string part_data;
+ ASSERT_TRUE(base::ReadFileToString(rootfs_path, &part_data));
+
+ // Check footer contains correct data.
+ AvbFooter f;
+ EXPECT_NE(0,
+ avb_footer_validate_and_byteswap(
+ reinterpret_cast<const AvbFooter*>(
+ part_data.data() + part_data.size() - AVB_FOOTER_SIZE),
+ &f));
+ EXPECT_EQ(
+ std::string(reinterpret_cast<const char*>(f.magic), AVB_FOOTER_MAGIC_LEN),
+ AVB_FOOTER_MAGIC);
+ EXPECT_EQ(AVB_FOOTER_MAJOR_VERSION, (int)f.version_major);
+ EXPECT_EQ(AVB_FOOTER_MINOR_VERSION, (int)f.version_minor);
+ EXPECT_EQ(1052672UL, f.original_image_size);
+ EXPECT_EQ(1052672UL, f.vbmeta_offset);
+ EXPECT_EQ(1280UL, f.vbmeta_size);
+
+ // Check that the vbmeta image at |f.vbmeta_offset| checks out.
+ const uint8_t* vbmeta_data =
+ reinterpret_cast<const uint8_t*>(part_data.data() + f.vbmeta_offset);
+ EXPECT_EQ(AVB_VBMETA_VERIFY_RESULT_OK,
+ avb_vbmeta_image_verify(vbmeta_data, f.vbmeta_size, NULL, NULL));
+
+ // Collect all descriptors.
+ std::vector<const AvbDescriptor*> descriptors;
+ avb_descriptor_foreach(
+ vbmeta_data, f.vbmeta_size, collect_descriptors, &descriptors);
+
+ // We should only have a single descriptor and it should be a
+ // hash descriptor.
+ EXPECT_EQ(1UL, descriptors.size());
+ EXPECT_EQ(AVB_DESCRIPTOR_TAG_HASH, avb_be64toh(descriptors[0]->tag));
+ AvbHashDescriptor d;
+ EXPECT_NE(
+ 0,
+ avb_hash_descriptor_validate_and_byteswap(
+ reinterpret_cast<const AvbHashDescriptor*>(descriptors[0]), &d));
+ EXPECT_EQ(1052672UL, d.image_size);
+ EXPECT_EQ(6UL, d.partition_name_len);
+ EXPECT_EQ(4UL, d.salt_len);
+ EXPECT_EQ(32UL, d.digest_len);
+ const uint8_t* desc_end = reinterpret_cast<const uint8_t*>(descriptors[0]) +
+ sizeof(AvbHashDescriptor);
+ uint64_t o = 0;
+ EXPECT_EQ("foobar",
+ std::string(reinterpret_cast<const char*>(desc_end + o),
+ d.partition_name_len));
+ o += d.partition_name_len;
+ EXPECT_EQ("d00df00d", mem_to_hexstring(desc_end + o, d.salt_len));
+ o += d.salt_len;
+ EXPECT_EQ("9a58cc996d405e08a1e00f96dbfe9104fedf41cb83b1f5e4ed357fbcf58d88d9",
+ mem_to_hexstring(desc_end + o, d.digest_len));
+
+ // Check that the footer is correctly erased.
+ EXPECT_COMMAND(
+ 0, "./avbtool erase_footer --image %s", rootfs_path.value().c_str());
+ int64_t erased_footer_file_size;
+ ASSERT_TRUE(base::GetFileSize(rootfs_path, &erased_footer_file_size));
+ EXPECT_EQ(static_cast<size_t>(erased_footer_file_size), rootfs_size);
+}
+
+TEST_F(AvbToolTest, AddHashFooter) {
+ AddHashFooterTest(false);
+}
+
+TEST_F(AvbToolTest, AddHashFooterSparse) {
+ AddHashFooterTest(true);
+}
+
+static std::string RemoveLinesStartingWith(const std::string& str,
+ const std::string& prefix) {
+ std::vector<std::string> lines;
+ std::string ret;
+
+ lines = base::SplitString(
+ str, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+ for (const std::string& line : lines) {
+ if (!base::StartsWith(line, prefix, base::CompareCase::SENSITIVE)) {
+ ret += line;
+ ret += '\n';
+ }
+ }
+ return ret;
+}
+
+TEST_F(AvbToolTest, AddHashFooterSparseWithHoleAtTheEnd) {
+ const size_t partition_size = 10 * 1024 * 1024;
+ const size_t metadata_size = 128 * 1024;
+
+ // It's not enough to run img2simg on a file with a lot of zeroes at
+ // the end since that will turn up as "Fill with value (for value =
+ // 0x00000000)" and not "Don't care". Instead, use make_ext4fs for
+ // this since it will put a big hole (e.g. "Don't care" chunk) at
+ // the end.
+ base::FilePath partition_path = testdir_.Append("partition.bin");
+ EXPECT_COMMAND(0,
+ "make_ext4fs -s -L test -l %zd %s",
+ partition_size - metadata_size,
+ partition_path.value().c_str());
+
+ EXPECT_COMMAND(0,
+ "./avbtool add_hash_footer --salt d00df00d "
+ "--hash_algorithm sha256 --image %s "
+ "--partition_size %d --partition_name foobar "
+ "--algorithm SHA256_RSA2048 "
+ "--key test/data/testkey_rsa2048.pem",
+ partition_path.value().c_str(),
+ (int)partition_size);
+
+ // Since we may be using an arbritary version of make_ext4fs
+ // (because of different branches) the contents of the resulting
+ // disk image may slightly change. It's enough to just remove the
+ // "Digest:" line from the output to work around this.
+ std::string info =
+ RemoveLinesStartingWith(InfoImage(partition_path), " Digest:");
+ ASSERT_EQ(
+ "Footer version: 1.0\n"
+ "Image size: 10485760 bytes\n"
+ "Original image size: 10354688 bytes\n"
+ "VBMeta offset: 10354688\n"
+ "VBMeta size: 1280 bytes\n"
+ "--\n"
+ "VBMeta image version: 1.0 (Sparse)\n"
+ "Header Block: 256 bytes\n"
+ "Authentication Block: 320 bytes\n"
+ "Auxiliary Block: 704 bytes\n"
+ "Algorithm: SHA256_RSA2048\n"
+ "Rollback Index: 0\n"
+ "Flags: 0\n"
+ "Descriptors:\n"
+ " Hash descriptor:\n"
+ " Image Size: 10354688 bytes\n"
+ " Hash Algorithm: sha256\n"
+ " Partition Name: foobar\n"
+ " Salt: d00df00d\n",
+ info);
+
+ EXPECT_COMMAND(0,
+ "mv %s %s.sparse",
+ partition_path.value().c_str(),
+ partition_path.value().c_str());
+ EXPECT_COMMAND(0,
+ "simg2img %s.sparse %s",
+ partition_path.value().c_str(),
+ partition_path.value().c_str());
+ EXPECT_COMMAND(0, "rm -f %s.sparse", partition_path.value().c_str());
+}
+
+void AvbToolTest::AddHashtreeFooterTest(bool sparse_image) {
+ const size_t rootfs_size = 1028 * 1024;
+ const size_t partition_size = 1536 * 1024;
+
+ // Generate a 1028 KiB file with known content.
+ std::vector<uint8_t> rootfs;
+ rootfs.resize(rootfs_size);
+ for (size_t n = 0; n < rootfs_size; n++)
+ rootfs[n] = uint8_t(n);
+ base::FilePath rootfs_path = testdir_.Append("rootfs.bin");
+ EXPECT_EQ(rootfs_size,
+ static_cast<const size_t>(
+ base::WriteFile(rootfs_path,
+ reinterpret_cast<const char*>(rootfs.data()),
+ rootfs.size())));
+
+ if (sparse_image) {
+ EXPECT_COMMAND(0,
+ "mv %s %s.unsparse",
+ rootfs_path.value().c_str(),
+ rootfs_path.value().c_str());
+ EXPECT_COMMAND(0,
+ "img2simg %s.unsparse %s",
+ rootfs_path.value().c_str(),
+ rootfs_path.value().c_str());
+ EXPECT_COMMAND(0, "rm -f %s.unsparse", rootfs_path.value().c_str());
+ }
+
+ /* Do this twice to check that 'add_hashtree_footer' is idempotent. */
+ for (int n = 0; n < 2; n++) {
+ EXPECT_COMMAND(0,
+ "./avbtool add_hashtree_footer --salt d00df00d --image %s "
+ "--partition_size %d --partition_name foobar "
+ "--algorithm SHA256_RSA2048 "
+ "--key test/data/testkey_rsa2048.pem",
+ rootfs_path.value().c_str(),
+ (int)partition_size);
+
+ ASSERT_EQ(base::StringPrintf("Footer version: 1.0\n"
+ "Image size: 1572864 bytes\n"
+ "Original image size: 1052672 bytes\n"
+ "VBMeta offset: 1069056\n"
+ "VBMeta size: 1344 bytes\n"
+ "--\n"
+ "VBMeta image version: 1.0%s\n"
+ "Header Block: 256 bytes\n"
+ "Authentication Block: 320 bytes\n"
+ "Auxiliary Block: 768 bytes\n"
+ "Algorithm: SHA256_RSA2048\n"
+ "Rollback Index: 0\n"
+ "Flags: 0\n"
+ "Descriptors:\n"
+ " Hashtree descriptor:\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"
+ " FEC num roots: 0\n"
+ " FEC offset: 0\n"
+ " FEC size: 0 bytes\n"
+ " Hash Algorithm: sha1\n"
+ " Partition Name: foobar\n"
+ " Salt: d00df00d\n"
+ " Root Digest: "
+ "e811611467dcd6e8dc4324e45f706c2bdd51db67\n",
+ sparse_image ? " (Sparse)" : ""),
+ InfoImage(rootfs_path));
+ }
+
+ if (sparse_image) {
+ EXPECT_COMMAND(0,
+ "mv %s %s.sparse",
+ rootfs_path.value().c_str(),
+ rootfs_path.value().c_str());
+ EXPECT_COMMAND(0,
+ "simg2img %s.sparse %s",
+ rootfs_path.value().c_str(),
+ rootfs_path.value().c_str());
+ EXPECT_COMMAND(0, "rm -f %s.sparse", rootfs_path.value().c_str());
+ }
+
+ // To check that we generate the correct hashtree we can use
+ // veritysetup(1) - another codebase for working with dm-verity
+ // hashtrees - to verify it.
+ //
+ // If we don't want to impose the requirement of having the
+ // veritysetup(1) command available on builders we can comment this
+ // out.
+ 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 %s "
+ "e811611467dcd6e8dc4324e45f706c2bdd51db67",
+ rootfs_path.value().c_str(),
+ rootfs_path.value().c_str());
+
+ // Now check that we can find the VBMeta block again from the footer.
+ std::string part_data;
+ ASSERT_TRUE(base::ReadFileToString(rootfs_path, &part_data));
+
+ // Check footer contains correct data.
+ AvbFooter f;
+ EXPECT_NE(0,
+ avb_footer_validate_and_byteswap(
+ reinterpret_cast<const AvbFooter*>(
+ part_data.data() + part_data.size() - AVB_FOOTER_SIZE),
+ &f));
+ EXPECT_EQ(
+ std::string(reinterpret_cast<const char*>(f.magic), AVB_FOOTER_MAGIC_LEN),
+ AVB_FOOTER_MAGIC);
+ EXPECT_EQ(AVB_FOOTER_MAJOR_VERSION, (int)f.version_major);
+ EXPECT_EQ(AVB_FOOTER_MINOR_VERSION, (int)f.version_minor);
+ EXPECT_EQ(1052672UL, f.original_image_size);
+ EXPECT_EQ(1069056UL, f.vbmeta_offset);
+ EXPECT_EQ(1344UL, f.vbmeta_size);
+
+ // Check that the vbmeta image at |f.vbmeta_offset| checks out.
+ const uint8_t* vbmeta_data =
+ reinterpret_cast<const uint8_t*>(part_data.data() + f.vbmeta_offset);
+ EXPECT_EQ(AVB_VBMETA_VERIFY_RESULT_OK,
+ avb_vbmeta_image_verify(vbmeta_data, f.vbmeta_size, NULL, NULL));
+
+ // Collect all descriptors.
+ std::vector<const AvbDescriptor*> descriptors;
+ avb_descriptor_foreach(
+ vbmeta_data, f.vbmeta_size, collect_descriptors, &descriptors);
+
+ // We should only have a single descriptor and it should be a
+ // hashtree descriptor.
+ EXPECT_EQ(1UL, descriptors.size());
+ EXPECT_EQ(AVB_DESCRIPTOR_TAG_HASHTREE, avb_be64toh(descriptors[0]->tag));
+ AvbHashtreeDescriptor d;
+ EXPECT_NE(
+ 0,
+ avb_hashtree_descriptor_validate_and_byteswap(
+ reinterpret_cast<const AvbHashtreeDescriptor*>(descriptors[0]), &d));
+ EXPECT_EQ(1UL, d.dm_verity_version);
+ EXPECT_EQ(1052672UL, d.image_size);
+ EXPECT_EQ(1052672UL, d.tree_offset);
+ EXPECT_EQ(16384UL, d.tree_size);
+ EXPECT_EQ(4096UL, d.data_block_size);
+ EXPECT_EQ(4096UL, d.hash_block_size);
+ EXPECT_EQ(6UL, d.partition_name_len);
+ EXPECT_EQ(4UL, d.salt_len);
+ EXPECT_EQ(20UL, d.root_digest_len);
+ const uint8_t* desc_end = reinterpret_cast<const uint8_t*>(descriptors[0]) +
+ sizeof(AvbHashtreeDescriptor);
+ uint64_t o = 0;
+ EXPECT_EQ("foobar",
+ std::string(reinterpret_cast<const char*>(desc_end + o),
+ d.partition_name_len));
+ o += d.partition_name_len;
+ EXPECT_EQ("d00df00d", mem_to_hexstring(desc_end + o, d.salt_len));
+ o += d.salt_len;
+ EXPECT_EQ("e811611467dcd6e8dc4324e45f706c2bdd51db67",
+ mem_to_hexstring(desc_end + o, d.root_digest_len));
+
+ // Check that we correctly generate dm-verity kernel cmdline
+ // snippets, if requested.
+ base::FilePath vbmeta_dmv_path = testdir_.Append("vbmeta_dm_verity_desc.bin");
+ EXPECT_COMMAND(0,
+ "./avbtool make_vbmeta_image "
+ "--output %s "
+ "--setup_rootfs_from_kernel %s "
+ "--algorithm SHA256_RSA2048 "
+ "--key test/data/testkey_rsa2048.pem",
+ vbmeta_dmv_path.value().c_str(),
+ rootfs_path.value().c_str());
+
+ ASSERT_EQ(
+ "VBMeta image version: 1.0\n"
+ "Header Block: 256 bytes\n"
+ "Authentication Block: 320 bytes\n"
+ "Auxiliary Block: 896 bytes\n"
+ "Algorithm: SHA256_RSA2048\n"
+ "Rollback Index: 0\n"
+ "Flags: 0\n"
+ "Descriptors:\n"
+ " Kernel Cmdline descriptor:\n"
+ " Flags: 1\n"
+ " Kernel Cmdline: 'dm=\"1 vroot none ro 1,0 2056 verity 1 "
+ "PARTUUID=$(ANDROID_SYSTEM_PARTUUID) PARTUUID=$(ANDROID_SYSTEM_PARTUUID) "
+ "4096 4096 257 257 sha1 e811611467dcd6e8dc4324e45f706c2bdd51db67 "
+ "d00df00d 2 restart_on_corruption ignore_zero_blocks\" root=0xfd00'\n"
+ " Kernel Cmdline descriptor:\n"
+ " Flags: 2\n"
+ " Kernel Cmdline: "
+ "'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'\n",
+ InfoImage(vbmeta_dmv_path));
+
+ // Check that the footer is correctly erased and the hashtree
+ // remains - see above for why the constant 1069056 is used.
+ EXPECT_COMMAND(0,
+ "./avbtool erase_footer --image %s --keep_hashtree",
+ rootfs_path.value().c_str());
+ int64_t erased_footer_file_size;
+ ASSERT_TRUE(base::GetFileSize(rootfs_path, &erased_footer_file_size));
+ EXPECT_EQ(static_cast<size_t>(erased_footer_file_size), 1069056UL);
+}
+
+TEST_F(AvbToolTest, AddHashtreeFooter) {
+ AddHashtreeFooterTest(false);
+}
+
+TEST_F(AvbToolTest, AddHashtreeFooterSparse) {
+ AddHashtreeFooterTest(true);
+}
+
+void AvbToolTest::AddHashtreeFooterFECTest(bool sparse_image) {
+ const size_t rootfs_size = 1028 * 1024;
+ const size_t partition_size = 1536 * 1024;
+
+ // Generate a 1028 KiB file with known content.
+ std::vector<uint8_t> rootfs;
+ rootfs.resize(rootfs_size);
+ for (size_t n = 0; n < rootfs_size; n++)
+ rootfs[n] = uint8_t(n);
+ base::FilePath rootfs_path = testdir_.Append("rootfs.bin");
+ EXPECT_EQ(rootfs_size,
+ static_cast<const size_t>(
+ base::WriteFile(rootfs_path,
+ reinterpret_cast<const char*>(rootfs.data()),
+ rootfs.size())));
+
+ if (sparse_image) {
+ EXPECT_COMMAND(0,
+ "mv %s %s.unsparse",
+ rootfs_path.value().c_str(),
+ rootfs_path.value().c_str());
+ EXPECT_COMMAND(0,
+ "img2simg %s.unsparse %s",
+ rootfs_path.value().c_str(),
+ rootfs_path.value().c_str());
+ EXPECT_COMMAND(0, "rm -f %s.unsparse", rootfs_path.value().c_str());
+ }
+
+ /* Do this twice to check that 'add_hashtree_footer' is idempotent. */
+ for (int n = 0; n < 2; n++) {
+ EXPECT_COMMAND(0,
+ "./avbtool add_hashtree_footer --salt d00df00d --image %s "
+ "--partition_size %d --partition_name foobar "
+ "--generate_fec "
+ "--algorithm SHA256_RSA2048 "
+ "--key test/data/testkey_rsa2048.pem",
+ rootfs_path.value().c_str(),
+ (int)partition_size);
+
+ ASSERT_EQ(base::StringPrintf("Footer version: 1.0\n"
+ "Image size: 1572864 bytes\n"
+ "Original image size: 1052672 bytes\n"
+ "VBMeta offset: 1085440\n"
+ "VBMeta size: 1344 bytes\n"
+ "--\n"
+ "VBMeta image version: 1.0%s\n"
+ "Header Block: 256 bytes\n"
+ "Authentication Block: 320 bytes\n"
+ "Auxiliary Block: 768 bytes\n"
+ "Algorithm: SHA256_RSA2048\n"
+ "Rollback Index: 0\n"
+ "Flags: 0\n"
+ "Descriptors:\n"
+ " Hashtree descriptor:\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"
+ " FEC num roots: 2\n"
+ " FEC offset: 1069056\n"
+ " FEC size: 16384 bytes\n"
+ " Hash Algorithm: sha1\n"
+ " Partition Name: foobar\n"
+ " Salt: d00df00d\n"
+ " Root Digest: "
+ "e811611467dcd6e8dc4324e45f706c2bdd51db67\n",
+ sparse_image ? " (Sparse)" : ""),
+ InfoImage(rootfs_path));
+ }
+
+ if (sparse_image) {
+ EXPECT_COMMAND(0,
+ "mv %s %s.sparse",
+ rootfs_path.value().c_str(),
+ rootfs_path.value().c_str());
+ EXPECT_COMMAND(0,
+ "simg2img %s.sparse %s",
+ rootfs_path.value().c_str(),
+ rootfs_path.value().c_str());
+ EXPECT_COMMAND(0, "rm -f %s.sparse", rootfs_path.value().c_str());
+ }
+
+ /* TODO: would be nice to verify that the FEC data is correct. */
+
+ // Now check that we can find the VBMeta block again from the footer.
+ std::string part_data;
+ ASSERT_TRUE(base::ReadFileToString(rootfs_path, &part_data));
+
+ // Check footer contains correct data.
+ AvbFooter f;
+ EXPECT_NE(0,
+ avb_footer_validate_and_byteswap(
+ reinterpret_cast<const AvbFooter*>(
+ part_data.data() + part_data.size() - AVB_FOOTER_SIZE),
+ &f));
+ EXPECT_EQ(
+ std::string(reinterpret_cast<const char*>(f.magic), AVB_FOOTER_MAGIC_LEN),
+ AVB_FOOTER_MAGIC);
+ EXPECT_EQ(AVB_FOOTER_MAJOR_VERSION, (int)f.version_major);
+ EXPECT_EQ(AVB_FOOTER_MINOR_VERSION, (int)f.version_minor);
+ EXPECT_EQ(1052672UL, f.original_image_size);
+ EXPECT_EQ(1085440UL, f.vbmeta_offset);
+ EXPECT_EQ(1344UL, f.vbmeta_size);
+
+ // Check that the vbmeta image at |f.vbmeta_offset| checks out.
+ const uint8_t* vbmeta_data =
+ reinterpret_cast<const uint8_t*>(part_data.data() + f.vbmeta_offset);
+ EXPECT_EQ(AVB_VBMETA_VERIFY_RESULT_OK,
+ avb_vbmeta_image_verify(vbmeta_data, f.vbmeta_size, NULL, NULL));
+
+ // Collect all descriptors.
+ std::vector<const AvbDescriptor*> descriptors;
+ avb_descriptor_foreach(
+ vbmeta_data, f.vbmeta_size, collect_descriptors, &descriptors);
+
+ // We should only have a single descriptor and it should be a
+ // hashtree descriptor.
+ EXPECT_EQ(1UL, descriptors.size());
+ EXPECT_EQ(AVB_DESCRIPTOR_TAG_HASHTREE, avb_be64toh(descriptors[0]->tag));
+ AvbHashtreeDescriptor d;
+ EXPECT_NE(
+ 0,
+ avb_hashtree_descriptor_validate_and_byteswap(
+ reinterpret_cast<const AvbHashtreeDescriptor*>(descriptors[0]), &d));
+ EXPECT_EQ(1UL, d.dm_verity_version);
+ EXPECT_EQ(1052672UL, d.image_size);
+ EXPECT_EQ(1052672UL, d.tree_offset);
+ EXPECT_EQ(16384UL, d.tree_size);
+ EXPECT_EQ(4096UL, d.data_block_size);
+ EXPECT_EQ(2UL, d.fec_num_roots);
+ EXPECT_EQ(1069056UL, d.fec_offset);
+ EXPECT_EQ(16384UL, d.fec_size);
+ EXPECT_EQ(6UL, d.partition_name_len);
+ EXPECT_EQ(4UL, d.salt_len);
+ EXPECT_EQ(20UL, d.root_digest_len);
+ const uint8_t* desc_end = reinterpret_cast<const uint8_t*>(descriptors[0]) +
+ sizeof(AvbHashtreeDescriptor);
+ uint64_t o = 0;
+ EXPECT_EQ("foobar",
+ std::string(reinterpret_cast<const char*>(desc_end + o),
+ d.partition_name_len));
+ o += d.partition_name_len;
+ EXPECT_EQ("d00df00d", mem_to_hexstring(desc_end + o, d.salt_len));
+ o += d.salt_len;
+ EXPECT_EQ("e811611467dcd6e8dc4324e45f706c2bdd51db67",
+ mem_to_hexstring(desc_end + o, d.root_digest_len));
+
+ // Check that we correctly generate dm-verity kernel cmdline
+ // snippets, if requested.
+ base::FilePath vbmeta_dmv_path = testdir_.Append("vbmeta_dm_verity_desc.bin");
+ EXPECT_COMMAND(0,
+ "./avbtool make_vbmeta_image "
+ "--output %s "
+ "--setup_rootfs_from_kernel %s "
+ "--algorithm SHA256_RSA2048 "
+ "--key test/data/testkey_rsa2048.pem",
+ vbmeta_dmv_path.value().c_str(),
+ rootfs_path.value().c_str());
+
+ ASSERT_EQ(
+ "VBMeta image version: 1.0\n"
+ "Header Block: 256 bytes\n"
+ "Authentication Block: 320 bytes\n"
+ "Auxiliary Block: 960 bytes\n"
+ "Algorithm: SHA256_RSA2048\n"
+ "Rollback Index: 0\n"
+ "Flags: 0\n"
+ "Descriptors:\n"
+ " Kernel Cmdline descriptor:\n"
+ " Flags: 1\n"
+ " Kernel Cmdline: 'dm=\"1 vroot none ro 1,0 2056 verity 1 "
+ "PARTUUID=$(ANDROID_SYSTEM_PARTUUID) PARTUUID=$(ANDROID_SYSTEM_PARTUUID) "
+ "4096 4096 257 257 sha1 e811611467dcd6e8dc4324e45f706c2bdd51db67 "
+ "d00df00d 10 restart_on_corruption ignore_zero_blocks "
+ "use_fec_from_device "
+ "PARTUUID=$(ANDROID_SYSTEM_PARTUUID) fec_roots 2 fec_blocks 261 "
+ "fec_start 261\" root=0xfd00'\n"
+ " Kernel Cmdline descriptor:\n"
+ " Flags: 2\n"
+ " Kernel Cmdline: "
+ "'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'\n",
+ InfoImage(vbmeta_dmv_path));
+
+ // Check that the footer is correctly erased and the hashtree and
+ // FEC data remains. The constant 1085440 is used because it's where
+ // the FEC data ends (it's at offset 1069056 and size 16384).
+ EXPECT_COMMAND(0,
+ "./avbtool erase_footer --image %s --keep_hashtree",
+ rootfs_path.value().c_str());
+ int64_t erased_footer_file_size;
+ ASSERT_TRUE(base::GetFileSize(rootfs_path, &erased_footer_file_size));
+ EXPECT_EQ(static_cast<size_t>(erased_footer_file_size), 1085440UL);
+}
+
+TEST_F(AvbToolTest, AddHashtreeFooterFEC) {
+ AddHashtreeFooterFECTest(false);
+}
+
+TEST_F(AvbToolTest, AddHashtreeFooterFECSparse) {
+ AddHashtreeFooterFECTest(true);
+}
+
+TEST_F(AvbToolTest, AddHashtreeFooterCalcMaxImageSize) {
+ const size_t partition_size = 10 * 1024 * 1024;
+ base::FilePath output_path = testdir_.Append("max_size.txt");
+
+ EXPECT_COMMAND(0,
+ "./avbtool add_hashtree_footer "
+ "--partition_size %zd --calc_max_image_size > %s",
+ partition_size,
+ output_path.value().c_str());
+ std::string max_image_size_data;
+ EXPECT_TRUE(base::ReadFileToString(output_path, &max_image_size_data));
+ EXPECT_EQ("10330112\n", max_image_size_data);
+ size_t max_image_size = atoll(max_image_size_data.c_str());
+
+ // Hashtree and metadata takes up 152 KiB - compare to below with
+ // FEC which is 244 KiB.
+ EXPECT_EQ(152 * 1024ULL, partition_size - max_image_size);
+
+ // Check that we can add a hashtree with an image this size for such
+ // a partition size.
+ base::FilePath system_path = GenerateImage("system", max_image_size);
+ EXPECT_COMMAND(0,
+ "./avbtool add_hashtree_footer"
+ " --image %s"
+ " --partition_name system"
+ " --partition_size %zd"
+ " --salt deadbeef"
+ " --algorithm SHA512_RSA4096 "
+ " --key test/data/testkey_rsa4096.pem",
+ system_path.value().c_str(),
+ partition_size);
+}
+
+TEST_F(AvbToolTest, AddHashtreeFooterCalcMaxImageSizeWithFEC) {
+ const size_t partition_size = 10 * 1024 * 1024;
+ base::FilePath output_path = testdir_.Append("max_size.txt");
+
+ EXPECT_COMMAND(0,
+ "./avbtool add_hashtree_footer "
+ "--partition_size %zd --generate_fec "
+ "--calc_max_image_size > %s",
+ partition_size,
+ output_path.value().c_str());
+ std::string max_image_size_data;
+ EXPECT_TRUE(base::ReadFileToString(output_path, &max_image_size_data));
+ EXPECT_EQ("10235904\n", max_image_size_data);
+ size_t max_image_size = atoll(max_image_size_data.c_str());
+
+ // Hashtree, FEC codes, and metadata takes up 244 KiB - compare to
+ // above wihtout FEC which is 152 KiB.
+ EXPECT_EQ(244 * 1024ULL, partition_size - max_image_size);
+
+ // Check that we can add a hashtree with an image this size for such
+ // a partition size.
+ base::FilePath system_path = GenerateImage("system", max_image_size);
+ EXPECT_COMMAND(0,
+ "./avbtool add_hashtree_footer"
+ " --image %s"
+ " --partition_name system"
+ " --partition_size %zd"
+ " --salt deadbeef"
+ " --generate_fec "
+ " --algorithm SHA512_RSA4096 "
+ " --key test/data/testkey_rsa4096.pem",
+ system_path.value().c_str(),
+ partition_size);
+}
+
+TEST_F(AvbToolTest, KernelCmdlineDescriptor) {
+ base::FilePath vbmeta_path =
+ testdir_.Append("vbmeta_kernel_cmdline_desc.bin");
+
+ EXPECT_COMMAND(0,
+ "./avbtool make_vbmeta_image "
+ "--output %s "
+ "--kernel_cmdline 'foo bar baz' "
+ "--kernel_cmdline 'second cmdline' "
+ "--algorithm SHA256_RSA2048 "
+ "--key test/data/testkey_rsa2048.pem",
+ vbmeta_path.value().c_str());
+
+ ASSERT_EQ(
+ "VBMeta image version: 1.0\n"
+ "Header Block: 256 bytes\n"
+ "Authentication Block: 320 bytes\n"
+ "Auxiliary Block: 640 bytes\n"
+ "Algorithm: SHA256_RSA2048\n"
+ "Rollback Index: 0\n"
+ "Flags: 0\n"
+ "Descriptors:\n"
+ " Kernel Cmdline descriptor:\n"
+ " Flags: 0\n"
+ " Kernel Cmdline: 'foo bar baz'\n"
+ " Kernel Cmdline descriptor:\n"
+ " Flags: 0\n"
+ " Kernel Cmdline: 'second cmdline'\n",
+ InfoImage(vbmeta_path));
+
+ // Now check the VBMeta image.
+ std::string image_data;
+ ASSERT_TRUE(base::ReadFileToString(vbmeta_path, &image_data));
+
+ const uint8_t* vbmeta_data =
+ reinterpret_cast<const uint8_t*>(image_data.data());
+ const size_t vbmeta_size = image_data.length();
+ EXPECT_EQ(AVB_VBMETA_VERIFY_RESULT_OK,
+ avb_vbmeta_image_verify(vbmeta_data, vbmeta_size, NULL, NULL));
+
+ // Collect all descriptors.
+ std::vector<const AvbDescriptor*> descriptors;
+ avb_descriptor_foreach(
+ vbmeta_data, vbmeta_size, collect_descriptors, &descriptors);
+
+ // We should have two descriptors - check them.
+ EXPECT_EQ(2UL, descriptors.size());
+ AvbKernelCmdlineDescriptor d;
+ EXPECT_EQ(AVB_DESCRIPTOR_TAG_KERNEL_CMDLINE,
+ avb_be64toh(descriptors[0]->tag));
+ EXPECT_NE(
+ 0,
+ avb_kernel_cmdline_descriptor_validate_and_byteswap(
+ reinterpret_cast<const AvbKernelCmdlineDescriptor*>(descriptors[0]),
+ &d));
+ EXPECT_EQ("foo bar baz",
+ std::string(reinterpret_cast<const char*>(descriptors[0]) +
+ sizeof(AvbKernelCmdlineDescriptor),
+ d.kernel_cmdline_length));
+ EXPECT_EQ(AVB_DESCRIPTOR_TAG_KERNEL_CMDLINE,
+ avb_be64toh(descriptors[1]->tag));
+ EXPECT_NE(
+ 0,
+ avb_kernel_cmdline_descriptor_validate_and_byteswap(
+ reinterpret_cast<const AvbKernelCmdlineDescriptor*>(descriptors[1]),
+ &d));
+ EXPECT_EQ("second cmdline",
+ std::string(reinterpret_cast<const char*>(descriptors[1]) +
+ sizeof(AvbKernelCmdlineDescriptor),
+ d.kernel_cmdline_length));
+}
+
+TEST_F(AvbToolTest, IncludeDescriptor) {
+ base::FilePath vbmeta1_path = testdir_.Append("vbmeta_id1.bin");
+ base::FilePath vbmeta2_path = testdir_.Append("vbmeta_id2.bin");
+ base::FilePath vbmeta3_path = testdir_.Append("vbmeta_id3.bin");
+
+ EXPECT_COMMAND(0,
+ "./avbtool make_vbmeta_image "
+ "--output %s "
+ "--kernel_cmdline 'something' "
+ "--prop name:value ",
+ vbmeta1_path.value().c_str());
+
+ EXPECT_COMMAND(0,
+ "./avbtool make_vbmeta_image "
+ "--output %s "
+ "--prop name2:value2 "
+ "--prop name3:value3 ",
+ vbmeta2_path.value().c_str());
+
+ EXPECT_COMMAND(0,
+ "./avbtool make_vbmeta_image "
+ "--output %s "
+ "--prop name4:value4 "
+ "--include_descriptors_from_image %s "
+ "--include_descriptors_from_image %s ",
+ vbmeta3_path.value().c_str(),
+ vbmeta1_path.value().c_str(),
+ vbmeta2_path.value().c_str());
+
+ ASSERT_EQ(
+ "VBMeta image version: 1.0\n"
+ "Header Block: 256 bytes\n"
+ "Authentication Block: 0 bytes\n"
+ "Auxiliary Block: 256 bytes\n"
+ "Algorithm: NONE\n"
+ "Rollback Index: 0\n"
+ "Flags: 0\n"
+ "Descriptors:\n"
+ " Prop: name4 -> 'value4'\n"
+ " Prop: name -> 'value'\n"
+ " Kernel Cmdline descriptor:\n"
+ " Flags: 0\n"
+ " Kernel Cmdline: 'something'\n"
+ " Prop: name2 -> 'value2'\n"
+ " Prop: name3 -> 'value3'\n",
+ InfoImage(vbmeta3_path));
+}
+
+TEST_F(AvbToolTest, ChainedPartition) {
+ base::FilePath vbmeta_path = testdir_.Append("vbmeta_cp.bin");
+
+ base::FilePath pk_path = testdir_.Append("testkey_rsa2048.avbpubkey");
+
+ EXPECT_COMMAND(
+ 0,
+ "./avbtool extract_public_key --key test/data/testkey_rsa2048.pem"
+ " --output %s",
+ pk_path.value().c_str());
+
+ EXPECT_COMMAND(
+ 0,
+ "./avbtool make_vbmeta_image "
+ "--output %s "
+ "--chain_partition system:1:%s "
+ "--algorithm SHA256_RSA2048 --key test/data/testkey_rsa2048.pem",
+ vbmeta_path.value().c_str(),
+ pk_path.value().c_str());
+
+ ASSERT_EQ(
+ "VBMeta image version: 1.0\n"
+ "Header Block: 256 bytes\n"
+ "Authentication Block: 320 bytes\n"
+ "Auxiliary Block: 1152 bytes\n"
+ "Algorithm: SHA256_RSA2048\n"
+ "Rollback Index: 0\n"
+ "Flags: 0\n"
+ "Descriptors:\n"
+ " Chain Partition descriptor:\n"
+ " Partition Name: system\n"
+ " Rollback Index Location: 1\n"
+ " Public key (sha1): "
+ "cdbb77177f731920bbe0a0f94f84d9038ae0617d\n",
+ InfoImage(vbmeta_path));
+
+ // Now check the VBMeta image.
+ std::string image_data;
+ ASSERT_TRUE(base::ReadFileToString(vbmeta_path, &image_data));
+
+ const uint8_t* vbmeta_data =
+ reinterpret_cast<const uint8_t*>(image_data.data());
+ const size_t vbmeta_size = image_data.length();
+ EXPECT_EQ(AVB_VBMETA_VERIFY_RESULT_OK,
+ avb_vbmeta_image_verify(vbmeta_data, vbmeta_size, NULL, NULL));
+
+ // Collect all descriptors.
+ std::vector<const AvbDescriptor*> descriptors;
+ avb_descriptor_foreach(
+ vbmeta_data, vbmeta_size, collect_descriptors, &descriptors);
+
+ // We should have one descriptor - check it.
+ EXPECT_EQ(1UL, descriptors.size());
+
+ std::string pk_data;
+ ASSERT_TRUE(base::ReadFileToString(pk_path, &pk_data));
+
+ AvbChainPartitionDescriptor d;
+ EXPECT_EQ(AVB_DESCRIPTOR_TAG_CHAIN_PARTITION,
+ avb_be64toh(descriptors[0]->tag));
+ EXPECT_NE(
+ 0,
+ avb_chain_partition_descriptor_validate_and_byteswap(
+ reinterpret_cast<const AvbChainPartitionDescriptor*>(descriptors[0]),
+ &d));
+ const uint8_t* desc_end = reinterpret_cast<const uint8_t*>(descriptors[0]) +
+ sizeof(AvbChainPartitionDescriptor);
+ uint64_t o = 0;
+ EXPECT_EQ("system",
+ std::string(reinterpret_cast<const char*>(desc_end + o),
+ d.partition_name_len));
+ o += d.partition_name_len;
+ EXPECT_EQ(pk_data,
+ std::string(reinterpret_cast<const char*>(descriptors[0]) +
+ sizeof(AvbChainPartitionDescriptor) + o,
+ d.public_key_len));
+}
+
+TEST_F(AvbToolTest, SigningHelperBasic) {
+ base::FilePath vbmeta_path = testdir_.Append("vbmeta.bin");
+ base::FilePath signing_helper_test_path =
+ testdir_.Append("signing_helper_test");
+ EXPECT_COMMAND(
+ 0,
+ "SIGNING_HELPER_TEST=\"%s\" ./avbtool make_vbmeta_image "
+ "--output %s "
+ "--algorithm SHA256_RSA2048 --key test/data/testkey_rsa2048.pem "
+ "--signing_helper test/avbtool_signing_helper_test.py",
+ signing_helper_test_path.value().c_str(),
+ vbmeta_path.value().c_str());
+
+ // Now check the value in test file.
+ std::string value;
+ ASSERT_TRUE(base::ReadFileToString(signing_helper_test_path, &value));
+ EXPECT_EQ("DONE", value);
+}
+
+TEST_F(AvbToolTest, SigningHelperReturnError) {
+ base::FilePath vbmeta_path = testdir_.Append("vbmeta.bin");
+ EXPECT_COMMAND(
+ 1,
+ "./avbtool make_vbmeta_image "
+ "--output %s "
+ "--algorithm SHA256_RSA2048 --key test/data/testkey_rsa2048.pem "
+ "--signing_helper test/avbtool_signing_helper_test.py",
+ vbmeta_path.value().c_str());
+}
+
+TEST_F(AvbToolTest, MakeAtxPikCertificate) {
+ base::FilePath pubkey_path = testdir_.Append("tmp_pubkey.pem");
+ EXPECT_COMMAND(
+ 0,
+ "openssl pkey -pubout -in test/data/testkey_rsa2048.pem -out %s",
+ pubkey_path.value().c_str());
+
+ base::FilePath output_path = testdir_.Append("tmp_certificate.bin");
+ EXPECT_COMMAND(0,
+ "./avbtool make_atx_certificate"
+ " --subject test/data/small_blob.bin"
+ " --subject_key %s"
+ " --subject_key_version 42"
+ " --subject_is_intermediate_authority"
+ " --authority_key test/data/testkey_rsa4096.pem"
+ " --output %s",
+ pubkey_path.value().c_str(),
+ output_path.value().c_str());
+
+ EXPECT_COMMAND(0,
+ "diff test/data/atx_pik_certificate.bin %s",
+ output_path.value().c_str());
+}
+
+TEST_F(AvbToolTest, MakeAtxPskCertificate) {
+ base::FilePath pubkey_path = testdir_.Append("tmp_pubkey.pem");
+ EXPECT_COMMAND(
+ 0,
+ "openssl pkey -pubout -in test/data/testkey_rsa2048.pem -out %s",
+ pubkey_path.value().c_str());
+
+ base::FilePath output_path = testdir_.Append("tmp_certificate.bin");
+ EXPECT_COMMAND(0,
+ "./avbtool make_atx_certificate"
+ " --subject test/data/atx_product_id.bin"
+ " --subject_key %s"
+ " --subject_key_version 42"
+ " --authority_key test/data/testkey_rsa2048.pem"
+ " --output %s",
+ pubkey_path.value().c_str(),
+ output_path.value().c_str());
+
+ EXPECT_COMMAND(0,
+ "diff test/data/atx_psk_certificate.bin %s",
+ output_path.value().c_str());
+}
+
+TEST_F(AvbToolTest, MakeAtxPermanentAttributes) {
+ base::FilePath pubkey_path = testdir_.Append("tmp_pubkey.pem");
+ EXPECT_COMMAND(
+ 0,
+ "openssl pkey -pubout -in test/data/testkey_rsa4096.pem -out %s",
+ pubkey_path.value().c_str());
+
+ base::FilePath output_path = testdir_.Append("tmp_attributes.bin");
+ EXPECT_COMMAND(0,
+ "./avbtool make_atx_permanent_attributes"
+ " --root_authority_key %s"
+ " --product_id test/data/atx_product_id.bin"
+ " --output %s",
+ pubkey_path.value().c_str(),
+ output_path.value().c_str());
+
+ EXPECT_COMMAND(0,
+ "diff test/data/atx_permanent_attributes.bin %s",
+ output_path.value().c_str());
+}
+
+TEST_F(AvbToolTest, MakeAtxMetadata) {
+ base::FilePath output_path = testdir_.Append("tmp_metadata.bin");
+
+ EXPECT_COMMAND(
+ 0,
+ "./avbtool make_atx_metadata"
+ " --intermediate_key_certificate test/data/atx_pik_certificate.bin"
+ " --product_key_certificate test/data/atx_psk_certificate.bin"
+ " --google_key_version 42"
+ " --output %s",
+ output_path.value().c_str());
+
+ EXPECT_COMMAND(
+ 0, "diff test/data/atx_metadata.bin %s", output_path.value().c_str());
+}
+
+} // namespace avb
diff --git a/avb/test/data/atx_metadata.bin b/avb/test/data/atx_metadata.bin
new file mode 100644
index 0000000..3b53bbc
--- /dev/null
+++ b/avb/test/data/atx_metadata.bin
Binary files differ
diff --git a/avb/test/data/atx_permanent_attributes.bin b/avb/test/data/atx_permanent_attributes.bin
new file mode 100644
index 0000000..be63c86
--- /dev/null
+++ b/avb/test/data/atx_permanent_attributes.bin
Binary files differ
diff --git a/avb/test/data/atx_pik_certificate.bin b/avb/test/data/atx_pik_certificate.bin
new file mode 100644
index 0000000..d0b4a7b
--- /dev/null
+++ b/avb/test/data/atx_pik_certificate.bin
Binary files differ
diff --git a/avb/test/data/atx_product_id.bin b/avb/test/data/atx_product_id.bin
new file mode 100644
index 0000000..d5648fa
--- /dev/null
+++ b/avb/test/data/atx_product_id.bin
@@ -0,0 +1 @@
+t‹ùAö®÷ ìí=±]
\ No newline at end of file
diff --git a/avb/test/data/atx_psk_certificate.bin b/avb/test/data/atx_psk_certificate.bin
new file mode 100644
index 0000000..abec3ce
--- /dev/null
+++ b/avb/test/data/atx_psk_certificate.bin
Binary files differ
diff --git a/avb/test/data/large_blob.bin b/avb/test/data/large_blob.bin
new file mode 100644
index 0000000..56feeb9
--- /dev/null
+++ b/avb/test/data/large_blob.bin
Binary files differ
diff --git a/avb/test/data/small_blob.bin b/avb/test/data/small_blob.bin
new file mode 100644
index 0000000..d712de6
--- /dev/null
+++ b/avb/test/data/small_blob.bin
Binary files differ
diff --git a/avb/test/data/test_file.bin b/avb/test/data/test_file.bin
new file mode 100644
index 0000000..a3fc7fb
--- /dev/null
+++ b/avb/test/data/test_file.bin
Binary files differ
diff --git a/avb/test/data/test_file.bin.sparse b/avb/test/data/test_file.bin.sparse
new file mode 100644
index 0000000..c4962b2
--- /dev/null
+++ b/avb/test/data/test_file.bin.sparse
Binary files differ
diff --git a/avb/test/data/testkey_rsa2048.pem b/avb/test/data/testkey_rsa2048.pem
new file mode 100644
index 0000000..867dcff
--- /dev/null
+++ b/avb/test/data/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/avb/test/data/testkey_rsa4096.pem b/avb/test/data/testkey_rsa4096.pem
new file mode 100644
index 0000000..26db5c3
--- /dev/null
+++ b/avb/test/data/testkey_rsa4096.pem
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKQIBAAKCAgEA2ASv49OEbH4NiT3CjNMSVeliyfEPXswWcqtEfCxlSpS1FisA
+uwbvEwdTTPlkuSh6G4SYiNhnpCP5p0vcSg/3OhiuVKgV/rCtrDXaO60nvK/o0y83
+NNZRK2xaJ9eWBq9ruIDK+jC0sYWzTaqqwxY0Grjnx/r5CXerl5PrRK7PILzwgBHb
+IwxHcblt1ntgR4cWVpO3wiqasEwBDDDYk4fw7W6LvjBb9qav3YB8RV6PkZNeRP64
+ggfuecq/MXNiWOPNxLzCER2hSr/+J32h9jWjXsrcVy8+8Mldhmr4r2an7c247aFf
+upuFGtUJrpROO8/LXMl5gPfMpkqoatjTMRH59gJjKhot0RpmGxZBvb33TcBK5SdJ
+X39Y4yct5clmDlI4Fjj7FutTP+b96aJeJVnYeUX/A0wmogBajsJRoRX5e/RcgZsY
+RzXYLQXprQ81dBWjjovMJ9p8XeT6BNMFC7o6sklFL0fHDUE/l4BNP8G1u3Bfpzev
+SCISRS71D4eS4oQB+RIPFBUkzomZ7rnEF3BwFeq+xmwfYrP0LRaH+1YeRauuMuRe
+ke1TZl697a3mEjkNg8noa2wtpe7EWmaujJfXDWxJx/XEkjGLCe4z2qk3tkkY+A5g
+Rcgzke8gVxC+eC2DJtbKYfkv4L8FMFJaEhwAp13MfC7FlYujO/BDLl7dANsCAwEA
+AQKCAgAWoL8P/WsktjuSwb5sY/vKtgzcHH1Ar942GsysuTXPDy686LpF3R8T/jNy
+n7k2UBAia8xSoWCR6BbRuHeV5oA+PLGeOpE7QaSfonB+yc+cy0x3Or3ssfqEsu/q
+toGHp75/8DXS6WE0K04x94u1rdC9b9sPrrGBlWCLGzqM0kbuJfyHXdd3n2SofAUO
+b5QRSgxD+2tHUpEroHqHnWJCaf4J0QegX45yktlfOYNK/PHLDQXV8ly/ejc32M4Y
+Tv7hUtOOJTuq8VCg9OWZm2Zo1QuM9XEJTPCp5l3+o5vzO6yhk2gotDvD32CdA+3k
+tLJRP54M1Sn+IXb1gGKN9rKAtGJbenWIPlNObhQgkbwG89Qd+5rfMXsiPv1Hl1tK
++tqwjD82/H3/ElaaMnwHCpeoGSp95OblAoBjzjMP2KsbvKSdL8O/rf1c3uOw9+DF
+cth0SA8y3ZzI11gJtb2QMGUrCny5n4sPGGbc3x38NdLhwbkPKZy60OiT4g2kNpdY
+dIitmAML2otttiF4AJM6AraPk8YVzkPLTksoL3azPBya5lIoDI2H3QvTtSvpXkXP
+yKchsDSWYbdqfplqC/X0Djp2/Zd8jpN5I6+1aSmpTmbwx/JTllY1N89FRZLIdxoh
+2k81LPiXhE6uRbjioJUlbnEWIpY2y2N2Clmxpjh0/IcXd1XImQKCAQEA7Zai+yjj
+8xit24aO9Tf3mZBXBjSaDodjC2KS1yCcAIXp6S7aH0wZipyZpQjys3zaBQyMRYFG
+bQqIfVAa6inWyDoofbAJHMu5BVcHFBPZvSS5YhDjc8XZ5dqSCxzIz9opIqAbm+b4
+aEV/3A3Jki5Dy8y/5j21GAK4Y4mqQOYzne7bDGi3Hyu041MGM4qfIcIkS5N1eHW4
+sDZJh6+K5tuxN5TX3nDZSpm9luNH8mLGgKAZ15b1LqXAtM5ycoBY9Hv082suPPom
+O+r0ybdRX6nDSH8+11y2KiP2kdVIUHCGkwlqgrux5YZyjCZPwOvEPhzSoOS+vBiF
+UVXA8idnxNLk1QKCAQEA6MIihDSXx+350fWqhQ/3Qc6gA/t2C15JwJ9+uFWA+gjd
+c/hn5HcmnmBJN4R04nLG/aU9SQur87a4mnC/Mp9JIARjHlZ/WNT4U0sJyPEVRg5U
+Z9VajAucWwi0JyJYCO1EMMy68Jp8qlTriK/L7nbD86JJ5ASxjojiN/0psK/Pk60F
+Rr+shKPi3jRQ1BDjDtAxOfo4ctf/nFbUM4bY0FNPQMP7WesoSKU0NBCRR6d0d2tq
+YflMjIQHx+N74P5jEdSCHTVGQm+dj47pUt3lLPLWc0bX1G/GekwXP4NUsR/70Hsi
+bwxkNnK2TSGzkt2rcOnutP125rJu6WpV7SNrq9rm7wKCAQAfMROcnbWviKHqnDPQ
+hdR/2K9UJTvEhInASOS2UZWpi+s1rez9BuSjigOx4wbaAZ4t44PW7C3uyt84dHfU
+HkIQb3I5bg8ENMrJpK9NN33ykwuzkDwMSwFcZ+Gci97hSubzoMl/IkeiiN1MapL4
+GhLUgsD+3UMVL+Y9SymK8637IgyoCGdiND6/SXsa8SwLJo3VTjqx4eKpX7cvlSBL
+RrRxc50TmwUsAhsd4CDl9YnSATLjVvJBeYlfM2tbFPaYwl1aR8v+PWkfnK0efm60
+fHki33HEnGteBPKuGq4vwVYpn6bYGwQz+f6335/A2DMfZHFSpjVURHPcRcHbCMla
+0cUxAoIBAQC25eYNkO478mo+bBbEXJlkoqLmvjAyGrNFo48F9lpVH6Y0vNuWkXJN
+PUgLUhAu6RYotjGENqG17rz8zt/PPY9Ok2P3sOx8t00y1mIn/hlDZXs55FM0fOMu
+PZaiscAPs7HDzvyOmDah+fzi+ZD8H2M3DS2W+YE0iaeJa2vZJS2t02W0BGXiDI33
+IZDqMyLYvwwPjOnShJydEzXID4xLl0tNjzLxo3GSNA7jYqlmbtV8CXIc7rMSL6WV
+ktIDKKJcnmpn3TcKeX6MEjaSIT82pNOS3fY3PmXuL+CMzfw8+u77Eecq78fHaTiL
+P5JGM93F6mzi19EY0tmInUBMCWtQLcENAoIBAQCg0KaOkb8T36qzPrtgbfou0E2D
+ufdpL1ugmD4edOFKQB5fDFQhLnSEVSJq3KUg4kWsXapQdsBd6kLdxS+K6MQrLBzr
+4tf0c7UCF1AzWk6wXMExZ8mRb2RkGZYQB2DdyhFB3TPmnq9CW8JCq+6kxg/wkU4s
+vM4JXzgcqVoSf42QJl+B9waeWhg0BTWx01lal4ds88HvEKmE0ik5GwiDbr7EvDDw
+E6UbZtQcIoSTIIZDgYqVFfR2DAho3wXJRsOXh433lEJ8X7cCDzrngFbQnlKrpwML
+Xgm0SIUc+Nf5poMM3rfLFK77t/ob4w+5PwRKcoSniyAxrHd6bwykYA8Vuydv
+-----END RSA PRIVATE KEY-----
diff --git a/avb/test/data/testkey_rsa8192.pem b/avb/test/data/testkey_rsa8192.pem
new file mode 100644
index 0000000..a383428
--- /dev/null
+++ b/avb/test/data/testkey_rsa8192.pem
@@ -0,0 +1,99 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIISKgIBAAKCBAEA0D3T+dISsmCHm797wsX0vVfqUWDJ/3mvDYozlCabDhnGLlSE
+pAQbf1Z8Ts+OM4pVRHOJUJL0WebNdmPPGjsyWQz6zZE96lQZL3avCEXqYVQR66V5
+3wdK/ohaMSRnGyEMBrqkVVbF3gCr+/irxD3YK+VowO2WKs/6GrMdqTA8Y5CTF/Je
+ptwsSg5MMjr6UaK4qDcrej3hkgBVGvRV3cj1snK6Br8HuYdFnpGGTS0d7UJlHFgl
+trGHU/CBO923hkHgJaWEjC0giSGjhKKtLzrVcpDV2y/lWQP9T/T4djEAIaHqQ++P
+SdOSR6psIGR6hVgSigt7HCnE7nW711/rfV5Ur9EiVpB040mDImKZcy8//TMnXydN
+1KYTVd/34fdpzMpSw5iblErbwOLXVTUmOztYnpl41feHSv/jPesHstPlfklIF2vo
+GZEohf9scQvcuM7wEBfC/aTA9K39zMmkBbcvSZjLyhmcSZWMPPOZyIcl3zY53QhW
+QC/abmIcBfI1S4+r7mC4i2Jn++oEvuGNVGr2SY2Z0ZZxXGL1HI/08D/3+Tcumrcn
+4YjPK/DMFi0F+e+1x41lipuf+cx/2qRNQX/m02STrLYdM6e0g33KvlnFdi2b752y
+/OIaMwxDaJvunMh6EMDWKM1AHbY/ioAoK7eS26HeJLEDllqO4+SWP37c8lMvSEWy
+1GiErR0HcsOj/QwWGPFseoVroMiA2sUQ0Ic/tgVjCTlXg+12XpUnouIweCi8KcL/
+ad2zJkju9hBhJLBQ/2GnivJi3lFgF4Gd//TSJ6rgWuXFfMKt/9z2Sz35ohEX4yA0
+flqlCeLInFEoevbz+XT9aRfDe65MZ79yw3TfP9CrV74hf1RRzveD4zpi3F+hcY2i
+JWsH7gROZeCm6fAX5Trecd3hOxJOfA4N4rvSSCq6BwCvebT8FY25Z/VF7cQrHYDS
+ij5w6lqhMzXHeUEY90Ga9AK4XzaWwGgezq+R7Zs00YSKqFv9qYNKdR7tz3cjijWf
+9q/3R1uh6EQKTMZKo4SEClJiGyjOBvmPK09jMFZTJv00hDxagDPZBl7XpLDJ5/Ln
+1uppvLCNWWY1zeJfaElMyq3/PqKZLidF9rVoA1SIwk2lpdUvPote2oFiwCZoXlwZ
+J2ncjmXgQNs76/8unDJA0rj4JPqccw4M5GxQ7okbgm3F4rmzriCuv8BeMSCkr2ry
+0mY3UhpohX4wCMq0G4x5sEUAz9FVVPZKjxnYBmLDzrJAR+4+G7gZsct01XDJYgDd
+JVYInFP22/cIre8VrFWYtHbgOFdNqUiVq58de6PdZG/E+uaWmEThSlRrgEjTxupi
+OXfgdKW/20j1qAtjOlqFwsY094Q5rqULQ6wPxQIDAQABAoIEAQChmkmlhrRBv42d
+fYUiyxK52b8ath0saJdDz6tlXmxYDgJxM9/XlORt9oTzeDknoEO5olu+rrx4BBgQ
+tzYiaiwRVXRREVTWQ7tjzRvaNL/GFkLt93XTccpuKwyrNE/bitLVagRbwcI+HZFa
+MknCOihHMHoRto8h3FKAY94xzSAgODMek1WG8jhgpCXXmVNnBPt+d4oDDIDAGAfz
+qgf03J5nhIb+80KgZOzPOKnbvJaL6EmlLHbgB3c42dzAw7hHtVmofYGWcvLb2MIY
+DVKO435/sQx1U/8NDH6JjVdACZjLgObXH9K3/Tt46DWPEcrPLmD8xhoc6gFM+Qr0
+AhkzKoBYDNk0CljbhdIBXjktXU6wRQFZ45uP2e4JZ4zrzGBLr/t4lTavZ0SQtLld
+A6kOsGh+dCWFDtnshxYnl/xad/yR+3a5zmDJbo/fJTBXrlf1B4rfQkFtK20etOPQ
+B++FC/rjh3Mm/Kb/p9Gz/2upZdArH97ZvD2LBFfj77lFmAhqAi3wCRlN+ekuYxaZ
+t1pBV9yXig8Dyldg1d7X8pOn2kyrF3rQUDDf4pa7x9vpnbkUlEUifoV9gnYsmdni
+qDzYBtTv2g6MKqwQySXaIUW0YOBPbOellWEwxJqGYQ7y4IfVHfM0iyHnehk2tZcr
++XazLnwGe+Bz4vcguFhJXLyIu//lAOhZtbk6r1QJEUuxaOOQX3wzyceE6nkDsgmr
+P5dj3Zpd7fS2VV2vyGHIFnBJ88LRxreVvgr6Q28UT27SB82zMb7mRZTVE2zeuubT
+5D2D1XbZ0wBo6WiK6eRRrDQ2Haeetkj/uoRy6PWXwnAaTmmIrrXwLqaoJh/U1e+D
+tfsDLWd6IxLjfXvGglrHsrtAz0oprpixUTeVhgTrGk9IQRd5rvxuGUYhFujVaYI6
++QUf+33AFdtncb8y9C9jZmgx8AKbJk+e73SLhB5JVos+WteU7b8d/Mim5mALjnO6
+Z1n/uimsT79sSDqy3XSymtKWXo/22UlrvGCpoEuELPMb6dSFWR7vwrsvhFngY4/K
+UnitnvxboEflQnaIQ4IfRLRzZsX+sC5Esqw9U5tHt4oI+91Dv3KbdbcERgV73K6B
+ZQgC4lkAQquFXiZ5AICkxjiMyZwTtU9KJ7xv17Xu6oywF/3AtbVGETW1D+3maHsD
+y3DASWojyqZdLj+WGzKQRa+swgCDAYKeek2fIAXFSdF63zxJ2RxOJ4GijSaoh+mr
+4HVvcpDaTj+A8T1+QdByM4s98gu4GD7kVtVQGBZdWjutyHvh0hWv1gtVmbhQ/413
+gDMFFDzHIjLTYGYes4hHL22169jVR9sZ1eQxwvTIg3N4pD5cFm0rRuZZTS+oJToF
+G27aBFihAoICAQDyVB62ZDnbxQthk+zITKIzRUrJbLoXrUcANcSHfaN7inF87Ova
+ze7ejT9DNSEhbtfZFJ1G6diOYoSw+2MzFXv0gEkLKY0dETydKgHEu6nVq5eivMgv
+D4hc9YkJMHDSlmv2FDkpL3AXCAmnW9rKp+ddttBZECnmlPEpHLoj6xgBw3pNa1Xs
+IcLVfdugH86Hexj6o0oKgYfcqrX8UUHtUI2/XQqgFrIj8ksjf1fFVWJRJFWmBXqp
+nMEsYarzATeM1kQ/kDeT1ZUpoGPQt02/XqXT4B5A3ATiEtpM2u+l48xtogWWg2Ry
+G9l938StAmhUiW1m7GnKE6EIFvQY85WvbzxOR0JYVUSr7MrasF6nnQlhYxFuIJoJ
+2h/KJQao5GCTvG4+GtbJJm4c2nyZgwyhizMsdgsdcls79aXiMkrZZkamLVUZWOtE
+3pA/oBuz2qnO9HwjbH1HGOccq0TXfmpFScEV3CQGYJdno6Fy7cbmupaL4U9agQ4e
+w+ygL18nq5HV++LStFnVrgs5YijjskfRdE9GUMVDh5pCsd9Y23Fymaad4O/2SRCC
+YkSsyH5OvyDOLpoyUJ6g6Q+45Hqm/3lG4YjNpzFUiMcnp7+3xU35qC0LK8xEfeei
+Ms1mTVEiHNIp6xH/TqRdX73WD7+YuKZSLIfRG7dgrirU6w+mhhvxD51uHQKCAgEA
+2/1mBCR5qm3/0Lt++RQbeyE3tiw40UeyQqucG/+VvY77sSLkI/Lx8iwRlywXcLBn
++A4TvgukmAdWzCs8ndgKNxPA+gfohvBsMOGN9KOB1Ug5vvg2J2kiI64vwYCwzhdZ
+NTUUmL+GMFHUqSsWYg6i7iBFcZmznr4W2T3bBxyTMZki7JStB86e35KXrzc2/W/b
++/p5U2HCSazDHI5mMyuClHc6GmUSVJ7f7LHjL94jviNqobp0Vj603tScHISmNrZw
+TBavkvZGYXsoWKvqavk7jBB9QzaBL+unaFRslg5jTaiKnISj44Us1fjFKu84xifL
+nJaEzjDPt7PBxko7LPgEY7wF39nM9VpoetI7bwR6NwDLSX8UU97MGd+HY+MO1Wi1
+pd2Lapwrx/EK7Oxz335VRK4Je0aZna4j2TyQdMJac9fsGPXv4ZsLfDLj/wD6l1j+
+lLLbBv3ImdSj32LBbhsgF4iCGeXO8HpPO+Q/h9XVsnY52Um2XdNMn03PCGm6ZvtM
+7DXiS+lPF90HjolJVHZTBNtdVRrLr53zLuWEfqT4FeKrDaxdtiXkxLjrB+5/VYu7
+ntyk01ZQ63VNfEwS1irmKl9+qZkTHk3HHV9jNV5RzWViwmJI7Wpr1YzBwmcKCB1O
+oGUADDs8QpnkCz0xkMVtYwHj9qKZlqfbHzrFDUUcF8kCggIAdYvUcgjf//ju8mA8
+5VQ3AcPE6TvycPW+kR2DvW12VcDsF/sc1UA7dHzziPhGn98SmNxlBjb8suSbFPZ8
+QhVT0WBBDkcTilwIGPx9ax7U3S6lGW2VdS6FqQH5fRmgQKZyrCVXLOEz8BgYBrSJ
+xu/3TQAWxH0QtibdbGHg8Pdi58gYlWFRhn9B8Slh1aRYHGPb1AhNLBd0/ddY+5G2
+9xSyDXdmZg1cUA+B3zAwNSqbzFxhp2zU+V1uXsbpk4KtnYV6CZM9QlrCRjTk9iNU
+dVXF/qaiRjfzrm4SsmEpCkEbsrp7F22Y1bkooORglMOsNAWNqfVXw4wN+syXj1ro
+6vZ8PERYrFyAOR1dsQMIhymnmTPjCpaJ4emKrhWTy20sY71thHakZWJc22YoNpbZ
+E6tgIVsJPTlxg/4+fyCCKj5wWr92nhsB1KBZPGO/zFhvMlJpvQ0tH8W2pbN2a0mI
+5x9FqALm/qjwCHfZItSwPM+ZozSht3cOkGHdcD5KXAXfcfsDJc4SHZKVIzq4NusN
+504R/jvD1GP8sglyG7omp75ckgzAmakLdxOP2HhQvIX9tcXpSirNJ6Sl2bwKuuMF
+wxo3r/o/9Y97e4LlfpEYp9eqMdcG+NpR993IwK0UhAWS9H5wdnWBSUHd5e4xtDUt
+iILNRuO46g7R/AIhz1cSSraWWQkCggIBAMhhPP5C9yt9PIm1b0eTwCBctnFSQIKo
+KsA9rll2ab+bMLk9jc8M6MLszy0CtWso09sHf4YY9tifvrkEHRethEh8zscwUuYu
+sm2n1fTixk0ul6LSVgl54uXbMJayENn4PIKRkew8cA8tSma43497w37hmD+MgCb1
+ALzqcco9hfmkgkI6fo1g8Ce3UEECKy2YKSmREdgYcK9JFQO61W6AkFWJcDxAmfzI
+JjFkKwsb7TSw79zWiEdSoM9jm7sCPKATd6Bm/ZAAkUUTuEFkfobn9Ax1rJN/Xxb2
+MKuAUtQv0NYY0gEVdG62jItuKLId6nncH8PG+rsRjPLIYpWqYdJpKx5pUnR+4AkQ
+S6CsRASwcF4PdBvDDBIFG6XpjFo4pPdQhDzL2sTF8b8SWSBLlJQbb7G6UNqgCSau
+SusCFpazvU5NfDmUMuctob2EYVaSXq9jGaj6bTUmDwXHwWilfIk9XfLxnYfXYrJ6
+xhdIpXGmHhuLQtAgK2O1JtLoPc9s9qP8/SkfP7xjjG6xHsP/WvL7QE1pPs9ZM/UI
+C01JNHFi9LKCn8o5mbZjN8jUowi7ffK+76wZUG1L7zM5ytWQOYwo0TQBfc8fpmFw
++RBRJX2kJyDO27ExczoGOKjwqEDaODIB9+9zcCK0BgSoRibSm4ZBvoxzWWD65Kls
+xdPhZUHcFGW5AoICAQC8iG27aD8aRUt94Oek66gFOJx84QVZehWPqtZjWyVenDuc
+T8dink8oejGjcK2UJuQDa83azv90ocVqE0n0ronYyszt9Ib1jlYC+CK1Ar9TYGFg
+WU5OWEDyCzCpqW/w/aG68U8qhKm0MvkLJR+G6evan9TwEhFEVAm3iWllNXs9x29s
+BucwyMMC23zsimxYlS7dA4DtyvVA+zL1omLpSWHbU/qtuI3HV1NeJzsy+gC4mwPh
+j52tdl669fyWLzHzBRLeq6dVOedjnCo+jlU3dL20DEk9SaW08D1CPuZekV1jVPMw
+JoaDcIRh4KLtQ0BYZ7UJeFUTsx1CS/+UqzqYSPOi57a5kvr0Y8YwRnSB8dHVFttX
+JTv83wTQXHPFSBgfnHNe7lsRTfIQfuIkr2bpiU7h85UQ7LsqcI6YHaC07URcsGFF
+FrLWGh91qzAd1diSHla2RnY3n8PPuMnCkguNhLUrYdmyMol7FfWFa9lwplsuTzBq
+B6yj8iaiE3LL+Q/eulJ7S6QPfAI2bU0UJO23Y4koeoIibEEDMSCQ6KYZ2NClRRRT
+ga5fS1YfkDFEcHUQ1/KIkdYHGBKBjoKGExzi8+CgiSySVSYDZl6wIOhLjH2OZ3ol
+ldPN7iNAHirrxg9v8QO6OQlpLUk5Lhp/1dSlZ6sy3UjFqvax3tw6ZjrL88YP5g==
+-----END RSA PRIVATE KEY-----
diff --git a/avb/test/fake_avb_ops.cc b/avb/test/fake_avb_ops.cc
new file mode 100644
index 0000000..cdd0850
--- /dev/null
+++ b/avb/test/fake_avb_ops.cc
@@ -0,0 +1,348 @@
+/*
+ * 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 <errno.h>
+#include <inttypes.h>
+#include <string.h>
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <base/files/file_util.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <openssl/sha.h>
+
+#include "fake_avb_ops.h"
+
+namespace avb {
+
+AvbIOResult FakeAvbOps::read_from_partition(const char* partition,
+ int64_t offset,
+ size_t num_bytes,
+ void* buffer,
+ size_t* out_num_read) {
+ base::FilePath path =
+ partition_dir_.Append(std::string(partition)).AddExtension("img");
+
+ if (offset < 0) {
+ int64_t file_size;
+ if (!base::GetFileSize(path, &file_size)) {
+ fprintf(
+ stderr, "Error getting size of file '%s'\n", path.value().c_str());
+ return AVB_IO_RESULT_ERROR_IO;
+ }
+ offset = file_size - (-offset);
+ }
+
+ int fd = open(path.value().c_str(), O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr,
+ "Error opening file '%s': %s\n",
+ path.value().c_str(),
+ strerror(errno));
+ return AVB_IO_RESULT_ERROR_IO;
+ }
+ if (lseek(fd, offset, SEEK_SET) != offset) {
+ fprintf(stderr,
+ "Error seeking to pos %zd in file %s: %s\n",
+ offset,
+ path.value().c_str(),
+ strerror(errno));
+ close(fd);
+ return AVB_IO_RESULT_ERROR_IO;
+ }
+ ssize_t num_read = read(fd, buffer, num_bytes);
+ if (num_read < 0) {
+ fprintf(stderr,
+ "Error reading %zd bytes from pos %" PRId64 " in file %s: %s\n",
+ num_bytes,
+ offset,
+ path.value().c_str(),
+ strerror(errno));
+ close(fd);
+ return AVB_IO_RESULT_ERROR_IO;
+ }
+ close(fd);
+
+ if (out_num_read != NULL) {
+ *out_num_read = num_read;
+ }
+
+ return AVB_IO_RESULT_OK;
+}
+
+AvbIOResult FakeAvbOps::write_to_partition(const char* partition,
+ int64_t offset,
+ size_t num_bytes,
+ const void* buffer) {
+ base::FilePath path =
+ partition_dir_.Append(std::string(partition)).AddExtension("img");
+
+ if (offset < 0) {
+ int64_t file_size;
+ if (!base::GetFileSize(path, &file_size)) {
+ fprintf(
+ stderr, "Error getting size of file '%s'\n", path.value().c_str());
+ return AVB_IO_RESULT_ERROR_IO;
+ }
+ offset = file_size - (-offset);
+ }
+
+ int fd = open(path.value().c_str(), O_WRONLY);
+ if (fd < 0) {
+ fprintf(stderr,
+ "Error opening file '%s': %s\n",
+ path.value().c_str(),
+ strerror(errno));
+ return AVB_IO_RESULT_ERROR_IO;
+ }
+ if (lseek(fd, offset, SEEK_SET) != offset) {
+ fprintf(stderr,
+ "Error seeking to pos %zd in file %s: %s\n",
+ offset,
+ path.value().c_str(),
+ strerror(errno));
+ close(fd);
+ return AVB_IO_RESULT_ERROR_IO;
+ }
+ ssize_t num_written = write(fd, buffer, num_bytes);
+ if (num_written < 0) {
+ fprintf(stderr,
+ "Error writing %zd bytes at pos %" PRId64 " in file %s: %s\n",
+ num_bytes,
+ offset,
+ path.value().c_str(),
+ strerror(errno));
+ close(fd);
+ return AVB_IO_RESULT_ERROR_IO;
+ }
+ close(fd);
+
+ return AVB_IO_RESULT_OK;
+}
+
+AvbIOResult FakeAvbOps::validate_vbmeta_public_key(
+ AvbOps* ops,
+ const uint8_t* public_key_data,
+ size_t public_key_length,
+ const uint8_t* public_key_metadata,
+ size_t public_key_metadata_length,
+ bool* out_key_is_trusted) {
+ if (out_key_is_trusted != NULL) {
+ bool pk_matches = (public_key_length == expected_public_key_.size() &&
+ (memcmp(expected_public_key_.c_str(),
+ public_key_data,
+ public_key_length) == 0));
+ bool pkmd_matches =
+ (public_key_metadata_length == expected_public_key_metadata_.size() &&
+ (memcmp(expected_public_key_metadata_.c_str(),
+ public_key_metadata,
+ public_key_metadata_length) == 0));
+ *out_key_is_trusted = pk_matches && pkmd_matches;
+ }
+ return AVB_IO_RESULT_OK;
+}
+
+AvbIOResult FakeAvbOps::read_rollback_index(AvbOps* ops,
+ size_t rollback_index_location,
+ uint64_t* out_rollback_index) {
+ if (stored_rollback_indexes_.count(rollback_index_location) == 0) {
+ fprintf(stderr,
+ "No rollback index for location %zd (has %zd locations).\n",
+ rollback_index_location,
+ stored_rollback_indexes_.size());
+ return AVB_IO_RESULT_ERROR_IO;
+ }
+ *out_rollback_index = stored_rollback_indexes_[rollback_index_location];
+ return AVB_IO_RESULT_OK;
+}
+
+AvbIOResult FakeAvbOps::write_rollback_index(AvbOps* ops,
+ size_t rollback_index_location,
+ uint64_t rollback_index) {
+ if (stored_rollback_indexes_.count(rollback_index_location) == 0) {
+ fprintf(stderr,
+ "No rollback index for location %zd (has %zd locations).\n",
+ rollback_index_location,
+ stored_rollback_indexes_.size());
+ return AVB_IO_RESULT_ERROR_IO;
+ }
+ stored_rollback_indexes_[rollback_index_location] = rollback_index;
+ return AVB_IO_RESULT_OK;
+}
+
+AvbIOResult FakeAvbOps::read_is_device_unlocked(AvbOps* ops,
+ bool* out_is_device_unlocked) {
+ *out_is_device_unlocked = stored_is_device_unlocked_ ? 1 : 0;
+ return AVB_IO_RESULT_OK;
+}
+
+AvbIOResult FakeAvbOps::get_unique_guid_for_partition(AvbOps* ops,
+ const char* partition,
+ char* guid_buf,
+ size_t guid_buf_size) {
+ // This is faking it a bit but makes testing easy. It works
+ // because avb_slot_verify.c doesn't check that the returned GUID
+ // is wellformed.
+ snprintf(guid_buf, guid_buf_size, "1234-fake-guid-for:%s", partition);
+ return AVB_IO_RESULT_OK;
+}
+
+AvbIOResult FakeAvbOps::read_permanent_attributes(
+ AvbAtxPermanentAttributes* attributes) {
+ *attributes = permanent_attributes_;
+ return AVB_IO_RESULT_OK;
+}
+
+AvbIOResult FakeAvbOps::read_permanent_attributes_hash(
+ uint8_t hash[AVB_SHA256_DIGEST_SIZE]) {
+ if (permanent_attributes_hash_.empty()) {
+ SHA256(reinterpret_cast<const unsigned char*>(&permanent_attributes_),
+ sizeof(AvbAtxPermanentAttributes),
+ hash);
+ return AVB_IO_RESULT_OK;
+ }
+ memset(hash, 0, AVB_SHA256_DIGEST_SIZE);
+ permanent_attributes_hash_.copy(reinterpret_cast<char*>(hash),
+ AVB_SHA256_DIGEST_SIZE);
+ return AVB_IO_RESULT_OK;
+}
+
+static AvbIOResult my_ops_read_from_partition(AvbOps* ops,
+ const char* partition,
+ int64_t offset,
+ size_t num_bytes,
+ void* buffer,
+ size_t* out_num_read) {
+ return FakeAvbOps::GetInstanceFromAvbOps(ops)
+ ->delegate()
+ ->read_from_partition(partition, offset, num_bytes, buffer, out_num_read);
+}
+
+static AvbIOResult my_ops_write_to_partition(AvbOps* ops,
+ const char* partition,
+ int64_t offset,
+ size_t num_bytes,
+ const void* buffer) {
+ return FakeAvbOps::GetInstanceFromAvbOps(ops)->delegate()->write_to_partition(
+ partition, offset, num_bytes, buffer);
+}
+
+static AvbIOResult my_ops_validate_vbmeta_public_key(
+ AvbOps* ops,
+ const uint8_t* public_key_data,
+ size_t public_key_length,
+ const uint8_t* public_key_metadata,
+ size_t public_key_metadata_length,
+ bool* out_key_is_trusted) {
+ return FakeAvbOps::GetInstanceFromAvbOps(ops)
+ ->delegate()
+ ->validate_vbmeta_public_key(ops,
+ public_key_data,
+ public_key_length,
+ public_key_metadata,
+ public_key_metadata_length,
+ out_key_is_trusted);
+}
+
+static AvbIOResult my_ops_read_rollback_index(AvbOps* ops,
+ size_t rollback_index_location,
+ uint64_t* out_rollback_index) {
+ return FakeAvbOps::GetInstanceFromAvbOps(ops)
+ ->delegate()
+ ->read_rollback_index(ops, rollback_index_location, out_rollback_index);
+}
+
+static AvbIOResult my_ops_write_rollback_index(AvbOps* ops,
+ size_t rollback_index_location,
+ uint64_t rollback_index) {
+ return FakeAvbOps::GetInstanceFromAvbOps(ops)
+ ->delegate()
+ ->write_rollback_index(ops, rollback_index_location, rollback_index);
+}
+
+static AvbIOResult my_ops_read_is_device_unlocked(
+ AvbOps* ops, bool* out_is_device_unlocked) {
+ return FakeAvbOps::GetInstanceFromAvbOps(ops)
+ ->delegate()
+ ->read_is_device_unlocked(ops, out_is_device_unlocked);
+}
+
+static AvbIOResult my_ops_get_unique_guid_for_partition(AvbOps* ops,
+ const char* partition,
+ char* guid_buf,
+ size_t guid_buf_size) {
+ return FakeAvbOps::GetInstanceFromAvbOps(ops)
+ ->delegate()
+ ->get_unique_guid_for_partition(ops, partition, guid_buf, guid_buf_size);
+}
+
+static AvbIOResult my_ops_read_permanent_attributes(
+ AvbAtxOps* atx_ops, AvbAtxPermanentAttributes* attributes) {
+ return FakeAvbOps::GetInstanceFromAvbOps(atx_ops->ops)
+ ->delegate()
+ ->read_permanent_attributes(attributes);
+}
+
+static AvbIOResult my_ops_read_permanent_attributes_hash(
+ AvbAtxOps* atx_ops, uint8_t hash[AVB_SHA256_DIGEST_SIZE]) {
+ return FakeAvbOps::GetInstanceFromAvbOps(atx_ops->ops)
+ ->delegate()
+ ->read_permanent_attributes_hash(hash);
+}
+
+FakeAvbOps::FakeAvbOps() {
+ avb_ops_.ab_ops = &avb_ab_ops_;
+ avb_ops_.atx_ops = &avb_atx_ops_;
+ avb_ops_.user_data = this;
+ avb_ops_.read_from_partition = my_ops_read_from_partition;
+ avb_ops_.write_to_partition = my_ops_write_to_partition;
+ avb_ops_.validate_vbmeta_public_key = my_ops_validate_vbmeta_public_key;
+ avb_ops_.read_rollback_index = my_ops_read_rollback_index;
+ avb_ops_.write_rollback_index = my_ops_write_rollback_index;
+ avb_ops_.read_is_device_unlocked = my_ops_read_is_device_unlocked;
+ avb_ops_.get_unique_guid_for_partition = my_ops_get_unique_guid_for_partition;
+
+ // Just use the built-in A/B metadata read/write routines.
+ avb_ab_ops_.ops = &avb_ops_;
+ avb_ab_ops_.read_ab_metadata = avb_ab_data_read;
+ avb_ab_ops_.write_ab_metadata = avb_ab_data_write;
+
+ avb_atx_ops_.ops = &avb_ops_;
+ avb_atx_ops_.read_permanent_attributes = my_ops_read_permanent_attributes;
+ avb_atx_ops_.read_permanent_attributes_hash =
+ my_ops_read_permanent_attributes_hash;
+
+ delegate_ = this;
+}
+
+FakeAvbOps::~FakeAvbOps() {}
+
+} // namespace avb
diff --git a/avb/test/fake_avb_ops.h b/avb/test/fake_avb_ops.h
new file mode 100644
index 0000000..0a7246d
--- /dev/null
+++ b/avb/test/fake_avb_ops.h
@@ -0,0 +1,216 @@
+/*
+ * 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 FAKE_AVB_OPS_H_
+#define FAKE_AVB_OPS_H_
+
+#include <base/files/file_util.h>
+#include <map>
+#include <string>
+
+#include <libavb_ab/libavb_ab.h>
+#include <libavb_atx/libavb_atx.h>
+
+namespace avb {
+
+// A delegate interface for ops callbacks. This allows tests to override default
+// fake implementations.
+class FakeAvbOpsDelegate {
+ public:
+ virtual ~FakeAvbOpsDelegate() {}
+ virtual AvbIOResult read_from_partition(const char* partition,
+ int64_t offset,
+ size_t num_bytes,
+ void* buffer,
+ size_t* out_num_read) = 0;
+
+ virtual AvbIOResult write_to_partition(const char* partition,
+ int64_t offset,
+ size_t num_bytes,
+ const void* buffer) = 0;
+
+ virtual AvbIOResult validate_vbmeta_public_key(
+ AvbOps* ops,
+ const uint8_t* public_key_data,
+ size_t public_key_length,
+ const uint8_t* public_key_metadata,
+ size_t public_key_metadata_length,
+ bool* out_key_is_trusted) = 0;
+
+ virtual AvbIOResult read_rollback_index(AvbOps* ops,
+ size_t rollback_index_slot,
+ uint64_t* out_rollback_index) = 0;
+
+ virtual AvbIOResult write_rollback_index(AvbOps* ops,
+ size_t rollback_index_slot,
+ uint64_t rollback_index) = 0;
+
+ virtual AvbIOResult read_is_device_unlocked(AvbOps* ops,
+ bool* out_is_device_unlocked) = 0;
+
+ virtual AvbIOResult get_unique_guid_for_partition(AvbOps* ops,
+ const char* partition,
+ char* guid_buf,
+ size_t guid_buf_size) = 0;
+
+ virtual AvbIOResult read_permanent_attributes(
+ AvbAtxPermanentAttributes* attributes) = 0;
+
+ virtual AvbIOResult read_permanent_attributes_hash(
+ uint8_t hash[AVB_SHA256_DIGEST_SIZE]) = 0;
+};
+
+// Provides fake implementations of AVB ops. All instances of this class must be
+// created on the same thread.
+class FakeAvbOps : public FakeAvbOpsDelegate {
+ public:
+ FakeAvbOps();
+ virtual ~FakeAvbOps();
+
+ static FakeAvbOps* GetInstanceFromAvbOps(AvbOps* ops) {
+ return reinterpret_cast<FakeAvbOps*>(ops->user_data);
+ }
+ static FakeAvbOps* GetInstanceFromAvbABOps(AvbABOps* ab_ops) {
+ return reinterpret_cast<FakeAvbOps*>(ab_ops->ops->user_data);
+ }
+
+ AvbOps* avb_ops() {
+ return &avb_ops_;
+ }
+
+ AvbABOps* avb_ab_ops() {
+ return &avb_ab_ops_;
+ }
+
+ AvbAtxOps* avb_atx_ops() {
+ return &avb_atx_ops_;
+ }
+
+ FakeAvbOpsDelegate* delegate() {
+ return delegate_;
+ }
+
+ // Does not take ownership of |delegate|.
+ void set_delegate(FakeAvbOpsDelegate* delegate) {
+ delegate_ = delegate;
+ }
+
+ void set_partition_dir(const base::FilePath& partition_dir) {
+ partition_dir_ = partition_dir;
+ }
+
+ void set_expected_public_key(const std::string& expected_public_key) {
+ expected_public_key_ = expected_public_key;
+ }
+
+ void set_expected_public_key_metadata(
+ const std::string& expected_public_key_metadata) {
+ expected_public_key_metadata_ = expected_public_key_metadata;
+ }
+
+ void set_stored_rollback_indexes(
+ const std::map<size_t, uint64_t>& stored_rollback_indexes) {
+ stored_rollback_indexes_ = stored_rollback_indexes;
+ }
+
+ std::map<size_t, uint64_t> get_stored_rollback_indexes() {
+ return stored_rollback_indexes_;
+ }
+
+ void set_stored_is_device_unlocked(bool stored_is_device_unlocked) {
+ stored_is_device_unlocked_ = stored_is_device_unlocked;
+ }
+
+ void set_permanent_attributes(const AvbAtxPermanentAttributes& attributes) {
+ permanent_attributes_ = attributes;
+ }
+
+ void set_permanent_attributes_hash(const std::string& hash) {
+ permanent_attributes_hash_ = hash;
+ }
+
+ // FakeAvbOpsDelegate methods.
+ AvbIOResult read_from_partition(const char* partition,
+ int64_t offset,
+ size_t num_bytes,
+ void* buffer,
+ size_t* out_num_read) override;
+
+ AvbIOResult write_to_partition(const char* partition,
+ int64_t offset,
+ size_t num_bytes,
+ const void* buffer) override;
+
+ AvbIOResult validate_vbmeta_public_key(AvbOps* ops,
+ const uint8_t* public_key_data,
+ size_t public_key_length,
+ const uint8_t* public_key_metadata,
+ size_t public_key_metadata_length,
+ bool* out_key_is_trusted) override;
+
+ AvbIOResult read_rollback_index(AvbOps* ops,
+ size_t rollback_index_location,
+ uint64_t* out_rollback_index) override;
+
+ AvbIOResult write_rollback_index(AvbOps* ops,
+ size_t rollback_index_location,
+ uint64_t rollback_index) override;
+
+ AvbIOResult read_is_device_unlocked(AvbOps* ops,
+ bool* out_is_device_unlocked) override;
+
+ AvbIOResult get_unique_guid_for_partition(AvbOps* ops,
+ const char* partition,
+ char* guid_buf,
+ size_t guid_buf_size) override;
+
+ AvbIOResult read_permanent_attributes(
+ AvbAtxPermanentAttributes* attributes) override;
+
+ AvbIOResult read_permanent_attributes_hash(
+ uint8_t hash[AVB_SHA256_DIGEST_SIZE]) override;
+
+ private:
+ AvbOps avb_ops_;
+ AvbABOps avb_ab_ops_;
+ AvbAtxOps avb_atx_ops_;
+
+ FakeAvbOpsDelegate* delegate_;
+
+ base::FilePath partition_dir_;
+
+ std::string expected_public_key_;
+ std::string expected_public_key_metadata_;
+
+ std::map<size_t, uint64_t> stored_rollback_indexes_;
+
+ bool stored_is_device_unlocked_;
+
+ AvbAtxPermanentAttributes permanent_attributes_;
+ std::string permanent_attributes_hash_;
+};
+
+} // namespace avb
+
+#endif /* FAKE_AVB_OPS_H_ */
diff --git a/avb/test/image_handler_unittest.py b/avb/test/image_handler_unittest.py
new file mode 100755
index 0000000..9eacac0
--- /dev/null
+++ b/avb/test/image_handler_unittest.py
@@ -0,0 +1,211 @@
+#!/usr/bin/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.
+
+
+"""Unit-test for ImageHandler."""
+
+
+import imp
+import os
+import sys
+import tempfile
+import unittest
+
+sys.dont_write_bytecode = True
+avbtool = imp.load_source('avbtool', './avbtool')
+
+# The file test_file.bin and test_file.bin.sparse are generated using
+# the following python code:
+#
+# with open('test_file.bin', 'w+b') as f:
+# f.write('Barfoo43'*128*12)
+# os.system('img2simg test_file.bin test_file.bin.sparse')
+# image = avbtool.ImageHandler('test_file.bin.sparse')
+# image.append_dont_care(12*1024)
+# image.append_fill('\x01\x02\x03\x04', 12*1024)
+# image.append_raw('Foobar42'*128*12)
+# image.append_dont_care(12*1024)
+# del image
+# os.system('rm -f test_file.bin')
+# os.system('simg2img test_file.bin.sparse test_file.bin')
+#
+# and manually verified to be correct. The content of the raw and
+# sparse files are as follows (the line with "Fill with 0x04030201" is
+# a simg_dump.py bug):
+#
+# $ hexdump -C test_file.bin
+# 00000000 42 61 72 66 6f 6f 34 33 42 61 72 66 6f 6f 34 33 |Barfoo43Barfoo43|
+# *
+# 00003000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+# *
+# 00006000 01 02 03 04 01 02 03 04 01 02 03 04 01 02 03 04 |................|
+# *
+# 00009000 46 6f 6f 62 61 72 34 32 46 6f 6f 62 61 72 34 32 |Foobar42Foobar42|
+# *
+# 0000c000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+# *
+# 0000f000
+#
+# $ system/core/libsparse/simg_dump.py -v test_file.bin.sparse
+# test_file.bin.sparse: Total of 15 4096-byte output blocks in 5 input chunks.
+# input_bytes output_blocks
+# chunk offset number offset number
+# 1 40 12288 0 3 Raw data
+# 2 12340 0 3 3 Don't care
+# 3 12352 4 6 3 Fill with 0x04030201
+# 4 12368 12288 9 3 Raw data
+# 5 24668 0 12 3 Don't care
+# 24668 15 End
+#
+
+
+class ImageHandler(unittest.TestCase):
+
+ TEST_FILE_SPARSE_PATH = 'test/data/test_file.bin.sparse'
+ TEST_FILE_PATH = 'test/data/test_file.bin'
+ TEST_FILE_SIZE = 61440
+ TEST_FILE_BLOCK_SIZE = 4096
+
+ def _file_contents_equal(self, path1, path2, size):
+ f1 = open(path1, 'r')
+ f2 = open(path2, 'r')
+ if f1.read(size) != f2.read(size):
+ return False
+ return True
+
+ def _file_size(self, f):
+ old_pos = f.tell()
+ f.seek(0, os.SEEK_END)
+ size = f.tell()
+ f.seek(old_pos)
+ return size
+
+ def _clone_sparse_file(self):
+ f = tempfile.NamedTemporaryFile()
+ f.write(open(self.TEST_FILE_SPARSE_PATH).read())
+ f.flush()
+ return f
+
+ def _unsparsify(self, path):
+ f = tempfile.NamedTemporaryFile()
+ os.system('simg2img {} {}'.format(path, f.name))
+ return f
+
+ def testRead(self):
+ """Checks that reading from a sparse file works as intended."""
+ ih = avbtool.ImageHandler(self.TEST_FILE_SPARSE_PATH)
+
+ # Check that we start at offset 0.
+ self.assertEqual(ih.tell(), 0)
+
+ # Check that reading advances the cursor.
+ self.assertEqual(ih.read(14), bytearray('Barfoo43Barfoo'))
+ self.assertEqual(ih.tell(), 14)
+ self.assertEqual(ih.read(2), bytearray('43'))
+ self.assertEqual(ih.tell(), 16)
+
+ # Check reading in the middle of a fill chunk gets the right data.
+ ih.seek(0x6000 + 1)
+ self.assertEqual(ih.read(4), bytearray('\x02\x03\x04\x01'))
+
+ # Check we can cross the chunk boundary correctly.
+ ih.seek(0x3000 - 10)
+ self.assertEqual(ih.read(12), bytearray('43Barfoo43\x00\x00'))
+ ih.seek(0x9000 - 3)
+ self.assertEqual(ih.read(5), bytearray('\x02\x03\x04Fo'))
+
+ # Check reading at end of file is a partial read.
+ ih.seek(0xf000 - 2)
+ self.assertEqual(ih.read(16), bytearray('\x00\x00'))
+
+ def testTruncate(self):
+ """Checks that we can truncate a sparse file correctly."""
+ # Check truncation at all possible boundaries (including start and end).
+ for size in range(0, self.TEST_FILE_SIZE + self.TEST_FILE_BLOCK_SIZE,
+ self.TEST_FILE_BLOCK_SIZE):
+ sparse_file = self._clone_sparse_file()
+ ih = avbtool.ImageHandler(sparse_file.name)
+ ih.truncate(size)
+ unsparse_file = self._unsparsify(sparse_file.name)
+ self.assertEqual(self._file_size(unsparse_file), size)
+ self.assertTrue(self._file_contents_equal(unsparse_file.name,
+ self.TEST_FILE_PATH,
+ size))
+
+ # Check truncation to grow the file.
+ grow_size = 8192
+ sparse_file = self._clone_sparse_file()
+ ih = avbtool.ImageHandler(sparse_file.name)
+ ih.truncate(self.TEST_FILE_SIZE + grow_size)
+ unsparse_file = self._unsparsify(sparse_file.name)
+ self.assertEqual(self._file_size(unsparse_file),
+ self.TEST_FILE_SIZE + grow_size)
+ self.assertTrue(self._file_contents_equal(unsparse_file.name,
+ self.TEST_FILE_PATH,
+ self.TEST_FILE_SIZE))
+ unsparse_file.seek(self.TEST_FILE_SIZE)
+ self.assertEqual(unsparse_file.read(), '\0'*grow_size)
+
+ def testAppendRaw(self):
+ """Checks that we can append raw data correctly."""
+ sparse_file = self._clone_sparse_file()
+ ih = avbtool.ImageHandler(sparse_file.name)
+ data = 'SomeData'*4096
+ ih.append_raw(data)
+ unsparse_file = self._unsparsify(sparse_file.name)
+ self.assertTrue(self._file_contents_equal(unsparse_file.name,
+ self.TEST_FILE_PATH,
+ self.TEST_FILE_SIZE))
+ unsparse_file.seek(self.TEST_FILE_SIZE)
+ self.assertEqual(unsparse_file.read(), data)
+
+ def testAppendFill(self):
+ """Checks that we can append fill data correctly."""
+ sparse_file = self._clone_sparse_file()
+ ih = avbtool.ImageHandler(sparse_file.name)
+ data = 'ABCD'*4096
+ ih.append_fill('ABCD', len(data))
+ unsparse_file = self._unsparsify(sparse_file.name)
+ self.assertTrue(self._file_contents_equal(unsparse_file.name,
+ self.TEST_FILE_PATH,
+ self.TEST_FILE_SIZE))
+ unsparse_file.seek(self.TEST_FILE_SIZE)
+ self.assertEqual(unsparse_file.read(), data)
+
+ def testDontCare(self):
+ """Checks that we can append DONT_CARE data correctly."""
+ sparse_file = self._clone_sparse_file()
+ ih = avbtool.ImageHandler(sparse_file.name)
+ data = '\0'*40960
+ ih.append_dont_care(len(data))
+ unsparse_file = self._unsparsify(sparse_file.name)
+ self.assertTrue(self._file_contents_equal(unsparse_file.name,
+ self.TEST_FILE_PATH,
+ self.TEST_FILE_SIZE))
+ unsparse_file.seek(self.TEST_FILE_SIZE)
+ self.assertEqual(unsparse_file.read(), data)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/avb/test/libavb_host_symbols_test b/avb/test/libavb_host_symbols_test
new file mode 100755
index 0000000..db0dcb0
--- /dev/null
+++ b/avb/test/libavb_host_symbols_test
@@ -0,0 +1,49 @@
+#!/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 libavb_host.a and fails
+# if a reference not starting with avb_ is referenced. It's intended
+# to catch mistakes where the standard C library is inadvertently
+# used.
+
+set -e
+
+SYMBOLS_FILE=$(mktemp /tmp/libavb_host_symbols.XXXXXXXXXX)
+
+trap "rm -f '${SYMBOLS_FILE}'" EXIT
+
+readelf --symbols --wide "${ANDROID_HOST_OUT}/obj/STATIC_LIBRARIES/libavb_host_intermediates/libavb_host.a" | \
+ awk '$7 == "UND" && $8 != "" {print $8}' | \
+ grep -v ^avb_ | \
+ sort -u > "${SYMBOLS_FILE}"
+
+# If this file is non-empty, it means that the library is using
+# symbols not starting with "avb_".
+if [ -s "${SYMBOLS_FILE}" ] ; then
+ echo "ERROR: $0: Unexpected symbols in libavb_host:" >&2
+ cat "${SYMBOLS_FILE}" >&2
+ exit 1
+fi
diff --git a/avb/test/user_code_test.cc b/avb/test/user_code_test.cc
new file mode 100644
index 0000000..fd851f3
--- /dev/null
+++ b/avb/test/user_code_test.cc
@@ -0,0 +1,29 @@
+/*
+ * 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 test ensures that user code can include libavb*.h and compile without
+// defining AVB_COMPILATION (which user code must not).
+#include "libavb/libavb.h"
+#include "libavb_ab/libavb_ab.h"
+#include "libavb_atx/libavb_atx.h"