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"