diff --git a/aosp/avb/avbtool.v1.2.py b/aosp/avb/avbtool.v1.2.py new file mode 100755 index 0000000..7dfbbc0 --- /dev/null +++ b/aosp/avb/avbtool.v1.2.py @@ -0,0 +1,4833 @@ +#!/usr/bin/env python3 + +# 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 json +import math +import os +import struct +import subprocess +import sys +import tempfile +import time + +# Keep in sync with libavb/avb_version.h. +AVB_VERSION_MAJOR = 1 +AVB_VERSION_MINOR = 2 +AVB_VERSION_SUB = 0 + +# Keep in sync with libavb/avb_footer.h. +AVB_FOOTER_VERSION_MAJOR = 1 +AVB_FOOTER_VERSION_MINOR = 0 + +AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED = 1 + +# Configuration for enabling logging of calls to avbtool. +AVB_INVOCATION_LOGFILE = os.environ.get('AVB_INVOCATION_LOGFILE') + + +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_image.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_name: Empty or a name from |hashlib.algorithms|. + 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 as bytes, if any. + """ + + def __init__(self, algorithm_type, hash_name, hash_num_bytes, + signature_num_bytes, public_key_num_bytes, padding): + self.algorithm_type = algorithm_type + self.hash_name = hash_name + 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_name='', + hash_num_bytes=0, + signature_num_bytes=0, + public_key_num_bytes=0, + padding=b''), + 'SHA256_RSA2048': Algorithm( + algorithm_type=1, # AVB_ALGORITHM_TYPE_SHA256_RSA2048 + hash_name='sha256', + hash_num_bytes=32, + signature_num_bytes=256, + public_key_num_bytes=8 + 2*2048//8, + padding=bytes(bytearray([ + # 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_name='sha256', + hash_num_bytes=32, + signature_num_bytes=512, + public_key_num_bytes=8 + 2*4096//8, + padding=bytes(bytearray([ + # 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_name='sha256', + hash_num_bytes=32, + signature_num_bytes=1024, + public_key_num_bytes=8 + 2*8192//8, + padding=bytes(bytearray([ + # 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_name='sha512', + hash_num_bytes=64, + signature_num_bytes=256, + public_key_num_bytes=8 + 2*2048//8, + padding=bytes(bytearray([ + # 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_name='sha512', + hash_num_bytes=64, + signature_num_bytes=512, + public_key_num_bytes=8 + 2*4096//8, + padding=bytes(bytearray([ + # 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_name='sha512', + hash_num_bytes=64, + signature_num_bytes=1024, + public_key_num_bytes=8 + 2*8192//8, + padding=bytes(bytearray([ + # 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 get_release_string(): + """Calculates the release string to use in the VBMeta struct.""" + # Keep in sync with libavb/avb_version.c:avb_version_string(). + return 'avbtool {}.{}.{}'.format(AVB_VERSION_MAJOR, + AVB_VERSION_MINOR, + AVB_VERSION_SUB) + + +def round_to_multiple(number, size): + """Rounds a number up to nearest multiple of another number. + + Arguments: + 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. + + Arguments: + 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 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. + + This is the reverse of decode_long(). + + 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 decode_long(blob): + """Decodes a long from a bytearray() using a given amount of bits. + + This number is expected to be in big-endian, e.g. with the most + significant bit first. + + This is the reverse of encode_long(). + + Arguments: + blob: A bytearray() with the encoded long. + + Returns: + The decoded value. + """ + ret = 0 + for b in bytearray(blob): + ret *= 256 + ret += b + 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) + 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 + 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) + + +class RSAPublicKey(object): + """Data structure used for a RSA public key. + + Attributes: + exponent: The key exponent. + modulus: The key modulus. + num_bits: The key size. + """ + + MODULUS_PREFIX = b'modulus=' + + def __init__(self, key_path): + """Loads and parses an RSA key from either a private or public key file. + + Arguments: + key_path: The path to a key file. + + Raises: + AvbError: If RSA key parameters could not be read from file. + """ + # We used to have something as simple as this: + # + # key = Crypto.PublicKey.RSA.importKey(open(key_path).read()) + # self.exponent = key.e + # self.modulus = key.n + # self.num_bits = key.size() + 1 + # + # but unfortunately PyCrypto is not available in the builder. So + # instead just parse openssl(1) output to get this + # information. It's ugly but... + args = ['openssl', 'rsa', '-in', key_path, '-modulus', '-noout'] + p = subprocess.Popen(args, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + (pout, perr) = p.communicate() + if p.wait() != 0: + # Could be just a public key is passed, try that. + args.append('-pubin') + p = subprocess.Popen(args, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + (pout, perr) = p.communicate() + if p.wait() != 0: + raise AvbError('Error getting public key: {}'.format(perr)) + + if not pout.lower().startswith(self.MODULUS_PREFIX): + raise AvbError('Unexpected modulus output') + + modulus_hexstr = pout[len(self.MODULUS_PREFIX):] + + # The exponent is assumed to always be 65537 and the number of + # bits can be derived from the modulus by rounding up to the + # nearest power of 2. + self.key_path = key_path + self.modulus = int(modulus_hexstr, 16) + self.num_bits = round_to_pow2(int(math.ceil(math.log(self.modulus, 2)))) + self.exponent = 65537 + + def encode(self): + """Encodes the public RSA key in |AvbRSAPublicKeyHeader| format. + + This creates a |AvbRSAPublicKeyHeader| as well as the two large + numbers (|key_num_bits| bits long) following it. + + Returns: + The |AvbRSAPublicKeyHeader| followed by two large numbers as bytes. + + Raises: + AvbError: If given RSA key exponent is not 65537. + """ + if self.exponent != 65537: + raise AvbError('Only RSA keys with exponent 65537 are supported.') + ret = bytearray() + # Calculate n0inv = -1/n[0] (mod 2^32) + b = 2 ** 32 + n0inv = b - modinv(self.modulus, b) + # Calculate rr = r^2 (mod N), where r = 2^(# of key bits) + r = 2 ** self.modulus.bit_length() + rrmodn = r * r % self.modulus + ret.extend(struct.pack('!II', self.num_bits, n0inv)) + ret.extend(encode_long(self.num_bits, self.modulus)) + ret.extend(encode_long(self.num_bits, rrmodn)) + return bytes(ret) + + def sign(self, algorithm_name, data_to_sign, signing_helper=None, + signing_helper_with_files=None): + """Sign given data using |signing_helper| or openssl. + + openssl is used if neither the parameters signing_helper nor + signing_helper_with_files are given. + + Arguments: + algorithm_name: The algorithm name as per the ALGORITHMS dict. + data_to_sign: Data to sign as bytes or bytearray. + signing_helper: Program which signs a hash and returns the signature. + signing_helper_with_files: Same as signing_helper but uses files instead. + + Returns: + The signature as bytes. + + Raises: + AvbError: If an error occurred during signing. + """ + # Checks requested algorithm for validity. + algorithm = ALGORITHMS.get(algorithm_name) + if not algorithm: + raise AvbError('Algorithm with name {} is not supported.' + .format(algorithm_name)) + + if self.num_bits != (algorithm.signature_num_bytes * 8): + raise AvbError('Key size of key ({} bits) does not match key size ' + '({} bits) of given algorithm {}.' + .format(self.num_bits, algorithm.signature_num_bytes * 8, + algorithm_name)) + + # Hashes the data. + hasher = hashlib.new(algorithm.hash_name) + hasher.update(data_to_sign) + digest = hasher.digest() + + # Calculates the signature. + padding_and_hash = algorithm.padding + digest + p = None + if signing_helper_with_files is not None: + with tempfile.NamedTemporaryFile() as signing_file: + signing_file.write(padding_and_hash) + signing_file.flush() + p = subprocess.Popen([signing_helper_with_files, algorithm_name, + self.key_path, signing_file.name]) + retcode = p.wait() + if retcode != 0: + raise AvbError('Error signing') + signing_file.seek(0) + signature = signing_file.read() + else: + if signing_helper is not None: + p = subprocess.Popen( + [signing_helper, algorithm_name, self.key_path], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + else: + p = subprocess.Popen( + ['openssl', 'rsautl', '-sign', '-inkey', self.key_path, '-raw'], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + (pout, perr) = p.communicate(padding_and_hash) + retcode = p.wait() + if retcode != 0: + raise AvbError('Error signing: {}'.format(perr)) + signature = pout + if len(signature) != algorithm.signature_num_bytes: + raise AvbError('Error signing: Invalid length of signature') + return signature + + +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 lookup_hash_size_by_type(alg_type): + """Looks up hash size by type. + + Arguments: + alg_type: The integer representing the type. + + Returns: + The corresponding hash size. + + Raises: + AvbError: 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_data.hash_num_bytes + raise AvbError('Unsupported algorithm type {}'.format(alg_type)) + + +def verify_vbmeta_signature(vbmeta_header, vbmeta_blob): + """Checks that signature in a vbmeta blob was made by the embedded public key. + + Arguments: + vbmeta_header: A AvbVBMetaHeader. + vbmeta_blob: The whole vbmeta blob, including the header as bytes or + bytearray. + + Returns: + True if the signature is valid and corresponds to the embedded + public key. Also returns True if the vbmeta blob is not signed. + + Raises: + AvbError: If there errors calling out to openssl command during + signature verification. + """ + (_, alg) = lookup_algorithm_by_type(vbmeta_header.algorithm_type) + if not alg.hash_name: + return True + header_blob = vbmeta_blob[0:256] + auth_offset = 256 + aux_offset = auth_offset + vbmeta_header.authentication_data_block_size + aux_size = vbmeta_header.auxiliary_data_block_size + aux_blob = vbmeta_blob[aux_offset:aux_offset + aux_size] + pubkey_offset = aux_offset + vbmeta_header.public_key_offset + pubkey_size = vbmeta_header.public_key_size + pubkey_blob = vbmeta_blob[pubkey_offset:pubkey_offset + pubkey_size] + + digest_offset = auth_offset + vbmeta_header.hash_offset + digest_size = vbmeta_header.hash_size + digest_blob = vbmeta_blob[digest_offset:digest_offset + digest_size] + + sig_offset = auth_offset + vbmeta_header.signature_offset + sig_size = vbmeta_header.signature_size + sig_blob = vbmeta_blob[sig_offset:sig_offset + sig_size] + + # Now that we've got the stored digest, public key, and signature + # all we need to do is to verify. This is the exactly the same + # steps as performed in the avb_vbmeta_image_verify() function in + # libavb/avb_vbmeta_image.c. + + ha = hashlib.new(alg.hash_name) + ha.update(header_blob) + ha.update(aux_blob) + computed_digest = ha.digest() + + if computed_digest != digest_blob: + return False + + padding_and_digest = alg.padding + computed_digest + + (num_bits,) = struct.unpack('!I', pubkey_blob[0:4]) + modulus_blob = pubkey_blob[8:8 + num_bits//8] + modulus = decode_long(modulus_blob) + exponent = 65537 + + # We used to have this: + # + # import Crypto.PublicKey.RSA + # key = Crypto.PublicKey.RSA.construct((modulus, long(exponent))) + # if not key.verify(decode_long(padding_and_digest), + # (decode_long(sig_blob), None)): + # return False + # return True + # + # but since 'avbtool verify_image' is used on the builders we don't want + # to rely on Crypto.PublicKey.RSA. Instead just use openssl(1) to verify. + asn1_str = ('asn1=SEQUENCE:pubkeyinfo\n' + '\n' + '[pubkeyinfo]\n' + 'algorithm=SEQUENCE:rsa_alg\n' + 'pubkey=BITWRAP,SEQUENCE:rsapubkey\n' + '\n' + '[rsa_alg]\n' + 'algorithm=OID:rsaEncryption\n' + 'parameter=NULL\n' + '\n' + '[rsapubkey]\n' + 'n=INTEGER:{}\n' + 'e=INTEGER:{}\n').format(hex(modulus).rstrip('L'), + hex(exponent).rstrip('L')) + + with tempfile.NamedTemporaryFile() as asn1_tmpfile: + asn1_tmpfile.write(asn1_str.encode('ascii')) + asn1_tmpfile.flush() + + with tempfile.NamedTemporaryFile() as der_tmpfile: + p = subprocess.Popen( + ['openssl', 'asn1parse', '-genconf', asn1_tmpfile.name, '-out', + der_tmpfile.name, '-noout']) + retcode = p.wait() + if retcode != 0: + raise AvbError('Error generating DER file') + + p = subprocess.Popen( + ['openssl', 'rsautl', '-verify', '-pubin', '-inkey', der_tmpfile.name, + '-keyform', 'DER', '-raw'], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + (pout, perr) = p.communicate(sig_blob) + retcode = p.wait() + if retcode != 0: + raise AvbError('Error verifying data: {}'.format(perr)) + if pout != padding_and_digest: + sys.stderr.write('Signature not correct\n') + return False + return True + + +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 as bytes with data to fill if TYPE_FILL otherwise None. + + Raises: + ValueError: If given chunk parameters are invalid. + """ + 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: + filename: Name of file. + 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 = ' 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. + + Raises + OSError: If ImageHandler was initialized in read-only mode. + """ + assert num_bytes % self.block_size == 0 + + if self._read_only: + raise OSError('ImageHandler is in read-only mode.') + + 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 as bytes. + + Raises + OSError: If ImageHandler was initialized in read-only mode. + """ + assert len(data) % self.block_size == 0 + + if self._read_only: + raise OSError('ImageHandler is in read-only mode.') + + 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. + + Raises + OSError: If ImageHandler was initialized in read-only mode. + """ + assert len(fill_data) == 4 + assert size % 4 == 0 + assert size % self.block_size == 0 + + if self._read_only: + raise OSError('ImageHandler is in read-only mode.') + + 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. + + Raises: + RuntimeError: If the given offset is negative. + """ + if offset < 0: + raise RuntimeError('Seeking with negative offset: {}'.format(offset)) + 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 as bytes. + """ + 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(b'\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 bytes(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. + OSError: If ImageHandler was initialized in read-only mode. + """ + if self._read_only: + raise OSError('ImageHandler is in read-only mode.') + + 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 + + if 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) + + def verify(self, image_dir, image_ext, expected_chain_partitions_map, + image_containing_descriptor, accept_zeroed_hashtree): + """Verifies contents of the descriptor - used in verify_image sub-command. + + Arguments: + image_dir: The directory of the file being verified. + image_ext: The extension of the file being verified (e.g. '.img'). + expected_chain_partitions_map: A map from partition name to the + tuple (rollback_index_location, key_blob). + image_containing_descriptor: The image the descriptor is in. + accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is + zeroed out. + + Returns: + True if the descriptor verifies, False otherwise. + """ + # Deletes unused parameters to prevent pylint warning unused-argument. + del image_dir, image_ext, expected_chain_partitions_map + del image_containing_descriptor, accept_zeroed_hashtree + + # Nothing to do. + return True + + +class AvbPropertyDescriptor(AvbDescriptor): + """A class for property descriptors. + + See the |AvbPropertyDescriptor| C struct for more information. + + Attributes: + key: The key as string. + value: The value as bytes. + """ + + 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 as bytes of size |SIZE|. + + Raises: + LookupError: If the given descriptor is malformed. + """ + super(AvbPropertyDescriptor, self).__init__(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.') + try: + self.key = data[self.SIZE:(self.SIZE + key_size)].decode('utf-8') + except UnicodeDecodeError as e: + raise LookupError('Key cannot be decoded as UTF-8: {}.'.format(e)) + self.value = data[(self.SIZE + key_size + 1):(self.SIZE + key_size + 1 + + value_size)] + else: + self.key = '' + self.value = b'' + + def print_desc(self, o): + """Print the descriptor. + + Arguments: + o: The object to write the output to. + """ + # Go forward with python 3, bytes are represented with the 'b' prefix, + # e.g. b'foobar'. Thus, we trim off the 'b' to keep the print output + # the same between python 2 and python 3. + printable_value = repr(self.value) + if printable_value.startswith('b\''): + printable_value = printable_value[1:] + + if len(self.value) < 256: + o.write(' Prop: {} -> {}\n'.format(self.key, printable_value)) + else: + o.write(' Prop: {} -> ({} bytes)\n'.format(self.key, len(self.value))) + + def encode(self): + """Serializes the descriptor. + + Returns: + The descriptor data as bytes. + """ + key_encoded = self.key.encode('utf-8') + num_bytes_following = ( + self.SIZE + len(key_encoded) + 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(key_encoded), len(self.value)) + ret = (desc + key_encoded + b'\0' + self.value + b'\0' + + padding_size * b'\0') + return ret + + def verify(self, image_dir, image_ext, expected_chain_partitions_map, + image_containing_descriptor, accept_zeroed_hashtree): + """Verifies contents of the descriptor - used in verify_image sub-command. + + Arguments: + image_dir: The directory of the file being verified. + image_ext: The extension of the file being verified (e.g. '.img'). + expected_chain_partitions_map: A map from partition name to the + tuple (rollback_index_location, key_blob). + image_containing_descriptor: The image the descriptor is in. + accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is + zeroed out. + + Returns: + True if the descriptor verifies, False otherwise. + """ + # Nothing to do. + return True + + +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 as string. + partition_name: Partition name as string. + salt: Salt used as bytes. + root_digest: Root digest as bytes. + flags: Descriptor flags (see avb_hashtree_descriptor.h). + """ + + TAG = 1 + RESERVED = 60 + SIZE = 120 + 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) + 'L' + # flags + str(RESERVED) + 's') # reserved + + def __init__(self, data=None): + """Initializes a new hashtree descriptor. + + Arguments: + data: If not None, must be bytes of size |SIZE|. + + Raises: + LookupError: If the given descriptor is malformed. + """ + super(AvbHashtreeDescriptor, self).__init__(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, self.flags, _) = 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.rstrip(b'\0').decode('ascii') + o = 0 + try: + self.partition_name = data[ + (self.SIZE + o):(self.SIZE + o + partition_name_len) + ].decode('utf-8') + except UnicodeDecodeError as e: + raise LookupError('Partition name cannot be decoded as UTF-8: {}.' + .format(e)) + 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(self.hash_algorithm).digest()): + if root_digest_len != 0: + 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 = b'' + self.root_digest = b'' + self.flags = 0 + + 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(self.salt.hex())) + o.write(' Root Digest: {}\n'.format(self.root_digest.hex())) + o.write(' Flags: {}\n'.format(self.flags)) + + def encode(self): + """Serializes the descriptor. + + Returns: + The descriptor data as bytes. + """ + hash_algorithm_encoded = self.hash_algorithm.encode('ascii') + partition_name_encoded = self.partition_name.encode('utf-8') + num_bytes_following = (self.SIZE + len(partition_name_encoded) + + 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, hash_algorithm_encoded, + len(partition_name_encoded), len(self.salt), + len(self.root_digest), self.flags, self.RESERVED * b'\0') + ret = (desc + partition_name_encoded + self.salt + self.root_digest + + padding_size * b'\0') + return ret + + def verify(self, image_dir, image_ext, expected_chain_partitions_map, + image_containing_descriptor, accept_zeroed_hashtree): + """Verifies contents of the descriptor - used in verify_image sub-command. + + Arguments: + image_dir: The directory of the file being verified. + image_ext: The extension of the file being verified (e.g. '.img'). + expected_chain_partitions_map: A map from partition name to the + tuple (rollback_index_location, key_blob). + image_containing_descriptor: The image the descriptor is in. + accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is + zeroed out. + + Returns: + True if the descriptor verifies, False otherwise. + """ + if not self.partition_name: + image_filename = image_containing_descriptor.filename + image = image_containing_descriptor + else: + image_filename = os.path.join(image_dir, self.partition_name + image_ext) + image = ImageHandler(image_filename, read_only=True) + # Generate the hashtree and checks that it matches what's in the file. + digest_size = len(hashlib.new(self.hash_algorithm).digest()) + digest_padding = round_to_pow2(digest_size) - digest_size + (hash_level_offsets, tree_size) = calc_hash_level_offsets( + self.image_size, self.data_block_size, digest_size + digest_padding) + root_digest, hash_tree = generate_hash_tree(image, self.image_size, + self.data_block_size, + self.hash_algorithm, self.salt, + digest_padding, + hash_level_offsets, + tree_size) + # The root digest must match unless it is not embedded in the descriptor. + if self.root_digest and root_digest != self.root_digest: + sys.stderr.write('hashtree of {} does not match descriptor\n'. + format(image_filename)) + return False + # ... also check that the on-disk hashtree matches + image.seek(self.tree_offset) + hash_tree_ondisk = image.read(self.tree_size) + is_zeroed = (self.tree_size == 0) or (hash_tree_ondisk[0:8] == b'ZeRoHaSH') + if is_zeroed and accept_zeroed_hashtree: + print('{}: skipping verification since hashtree is zeroed and ' + '--accept_zeroed_hashtree was given' + .format(self.partition_name)) + else: + if hash_tree != hash_tree_ondisk: + sys.stderr.write('hashtree of {} contains invalid data\n'. + format(image_filename)) + return False + print('{}: Successfully verified {} hashtree of {} for image of {} bytes' + .format(self.partition_name, self.hash_algorithm, image.filename, + self.image_size)) + # TODO(zeuthen): we could also verify that the FEC stored in the image is + # correct but this a) currently requires the 'fec' binary; and b) takes a + # long time; and c) is not strictly needed for verification purposes as + # we've already verified the root hash. + return True + + +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 as string. + partition_name: Partition name as string. + salt: Salt used as bytes. + digest: The hash value of salt and data combined as bytes. + flags: The descriptor flags (see avb_hash_descriptor.h). + """ + + TAG = 2 + RESERVED = 60 + SIZE = 72 + 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) + 'L' + # flags + str(RESERVED) + 's') # reserved + + def __init__(self, data=None): + """Initializes a new hash descriptor. + + Arguments: + data: If not None, must be bytes of size |SIZE|. + + Raises: + LookupError: If the given descriptor is malformed. + """ + super(AvbHashDescriptor, self).__init__(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, self.flags, _) = 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.rstrip(b'\0').decode('ascii') + o = 0 + try: + self.partition_name = data[ + (self.SIZE + o):(self.SIZE + o + partition_name_len) + ].decode('utf-8') + except UnicodeDecodeError as e: + raise LookupError('Partition name cannot be decoded as UTF-8: {}.' + .format(e)) + 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(self.hash_algorithm).digest()): + if digest_len != 0: + raise LookupError('digest_len doesn\'t match hash algorithm') + + else: + self.image_size = 0 + self.hash_algorithm = '' + self.partition_name = '' + self.salt = b'' + self.digest = b'' + self.flags = 0 + + 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(self.salt.hex())) + o.write(' Digest: {}\n'.format(self.digest.hex())) + o.write(' Flags: {}\n'.format(self.flags)) + + def encode(self): + """Serializes the descriptor. + + Returns: + The descriptor data as bytes. + """ + hash_algorithm_encoded = self.hash_algorithm.encode('ascii') + partition_name_encoded = self.partition_name.encode('utf-8') + num_bytes_following = (self.SIZE + len(partition_name_encoded) + + 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, hash_algorithm_encoded, + len(partition_name_encoded), len(self.salt), + len(self.digest), self.flags, self.RESERVED * b'\0') + ret = (desc + partition_name_encoded + self.salt + self.digest + + padding_size * b'\0') + return ret + + def verify(self, image_dir, image_ext, expected_chain_partitions_map, + image_containing_descriptor, accept_zeroed_hashtree): + """Verifies contents of the descriptor - used in verify_image sub-command. + + Arguments: + image_dir: The directory of the file being verified. + image_ext: The extension of the file being verified (e.g. '.img'). + expected_chain_partitions_map: A map from partition name to the + tuple (rollback_index_location, key_blob). + image_containing_descriptor: The image the descriptor is in. + accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is + zeroed out. + + Returns: + True if the descriptor verifies, False otherwise. + """ + if not self.partition_name: + image_filename = image_containing_descriptor.filename + image = image_containing_descriptor + else: + image_filename = os.path.join(image_dir, self.partition_name + image_ext) + image = ImageHandler(image_filename, read_only=True) + data = image.read(self.image_size) + ha = hashlib.new(self.hash_algorithm) + ha.update(self.salt) + ha.update(data) + digest = ha.digest() + # The digest must match unless there is no digest in the descriptor. + if self.digest and digest != self.digest: + sys.stderr.write('{} digest of {} does not match digest in descriptor\n'. + format(self.hash_algorithm, image_filename)) + return False + print('{}: Successfully verified {} hash of {} for image of {} bytes' + .format(self.partition_name, self.hash_algorithm, image.filename, + self.image_size)) + return True + + +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 as string. + """ + + 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 bytes of size |SIZE|. + + Raises: + LookupError: If the given descriptor is malformed. + """ + super(AvbKernelCmdlineDescriptor, self).__init__(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. + try: + self.kernel_cmdline = data[ + self.SIZE:(self.SIZE + kernel_cmdline_length)].decode('utf-8') + except UnicodeDecodeError as e: + raise LookupError('Kernel command-line cannot be decoded as UTF-8: {}.' + .format(e)) + 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(self.kernel_cmdline)) + + def encode(self): + """Serializes the descriptor. + + Returns: + The descriptor data as bytes. + """ + kernel_cmd_encoded = self.kernel_cmdline.encode('utf-8') + num_bytes_following = (self.SIZE + len(kernel_cmd_encoded) - 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(kernel_cmd_encoded)) + ret = desc + kernel_cmd_encoded + padding_size * b'\0' + return ret + + def verify(self, image_dir, image_ext, expected_chain_partitions_map, + image_containing_descriptor, accept_zeroed_hashtree): + """Verifies contents of the descriptor - used in verify_image sub-command. + + Arguments: + image_dir: The directory of the file being verified. + image_ext: The extension of the file being verified (e.g. '.img'). + expected_chain_partitions_map: A map from partition name to the + tuple (rollback_index_location, key_blob). + image_containing_descriptor: The image the descriptor is in. + accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is + zeroed out. + + Returns: + True if the descriptor verifies, False otherwise. + """ + # Nothing to verify. + return True + + +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 as string. + public_key: The public key as bytes. + """ + + 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 + try: + self.partition_name = data[ + (self.SIZE + o):(self.SIZE + o + partition_name_len) + ].decode('utf-8') + except UnicodeDecodeError as e: + raise LookupError('Partition name cannot be decoded as UTF-8: {}.' + .format(e)) + 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 = b'' + + 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. + pubkey_digest = hashlib.sha1(self.public_key).hexdigest() + o.write(' Public key (sha1): {}\n'.format(pubkey_digest)) + + def encode(self): + """Serializes the descriptor. + + Returns: + The descriptor data as bytes. + """ + partition_name_encoded = self.partition_name.encode('utf-8') + num_bytes_following = ( + self.SIZE + len(partition_name_encoded) + 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(partition_name_encoded), len(self.public_key), + self.RESERVED * b'\0') + ret = desc + partition_name_encoded + self.public_key + padding_size * b'\0' + return ret + + def verify(self, image_dir, image_ext, expected_chain_partitions_map, + image_containing_descriptor, accept_zeroed_hashtree): + """Verifies contents of the descriptor - used in verify_image sub-command. + + Arguments: + image_dir: The directory of the file being verified. + image_ext: The extension of the file being verified (e.g. '.img'). + expected_chain_partitions_map: A map from partition name to the + tuple (rollback_index_location, key_blob). + image_containing_descriptor: The image the descriptor is in. + accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is + zeroed out. + + Returns: + True if the descriptor verifies, False otherwise. + """ + value = expected_chain_partitions_map.get(self.partition_name) + if not value: + sys.stderr.write('No expected chain partition for partition {}. Use ' + '--expected_chain_partition to specify expected ' + 'contents or --follow_chain_partitions.\n'. + format(self.partition_name)) + return False + rollback_index_location, pk_blob = value + + if self.rollback_index_location != rollback_index_location: + sys.stderr.write('Expected rollback_index_location {} does not ' + 'match {} in descriptor for partition {}\n'. + format(rollback_index_location, + self.rollback_index_location, + self.partition_name)) + return False + + if self.public_key != pk_blob: + sys.stderr.write('Expected public key blob does not match public ' + 'key blob in descriptor for partition {}\n'. + format(self.partition_name)) + return False + + print('{}: Successfully verified chain partition descriptor matches ' + 'expected data'.format(self.partition_name)) + + return True + +DESCRIPTOR_CLASSES = [ + AvbPropertyDescriptor, AvbHashtreeDescriptor, AvbHashDescriptor, + AvbKernelCmdlineDescriptor, AvbChainPartitionDescriptor +] + + +def parse_descriptors(data): + """Parses a blob of data into descriptors. + + Arguments: + data: Encoded descriptors as bytes. + + 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): + clazz = DESCRIPTOR_CLASSES[tag] + else: + clazz = AvbDescriptor + ret.append(clazz(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 = b'AVBf' + SIZE = 64 + RESERVED = 28 + FOOTER_VERSION_MAJOR = AVB_FOOTER_VERSION_MAJOR + FOOTER_VERSION_MINOR = AVB_FOOTER_VERSION_MINOR + 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 bytes 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 = self.FOOTER_VERSION_MAJOR + self.version_minor = self.FOOTER_VERSION_MINOR + self.original_image_size = 0 + self.vbmeta_offset = 0 + self.vbmeta_size = 0 + + def encode(self): + """Serializes the footer. + + Returns: + The footer as bytes. + """ + 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. + + The attributes correspond to the |AvbVBMetaImageHeader| struct defined in + avb_vbmeta_image.h. + + Attributes: + magic: Four bytes equal to "AVB0" (AVB_MAGIC). + required_libavb_version_major: The major version of libavb required for this + header. + required_libavb_version_minor: The minor version of libavb required for this + header. + authentication_data_block_size: The size of the signature block. + auxiliary_data_block_size: The size of the auxiliary data block. + algorithm_type: The verification algorithm used, see |AvbAlgorithmType| + enum. + hash_offset: Offset into the "Authentication data" block of hash data. + hash_size: Length of the hash data. + signature_offset: Offset into the "Authentication data" block of signature + data. + signature_size: Length of the signature data. + public_key_offset: Offset into the "Auxiliary data" block of public key + data. + public_key_size: Length of the public key data. + public_key_metadata_offset: Offset into the "Auxiliary data" block of public + key metadata. + public_key_metadata_size: Length of the public key metadata. Must be set to + zero if there is no public key metadata. + descriptors_offset: Offset into the "Auxiliary data" block of descriptor + data. + descriptors_size: Length of descriptor data. + rollback_index: The rollback index which can be used to prevent rollback to + older versions. + flags: Flags from the AvbVBMetaImageFlags enumeration. This must be set to + zero if the vbmeta image is not a top-level image. + rollback_index_location: The location of the rollback index defined in this + header. Only valid for the main vbmeta. For chained partitions, the + rollback index location must be specified in the + AvbChainPartitionDescriptor and this value must be set to 0. + release_string: The release string from avbtool, e.g. "avbtool 1.0.0" or + "avbtool 1.0.0 xyz_board Git-234abde89". Is guaranteed to be NUL + terminated. Applications must not make assumptions about how this + string is formatted. + """ + MAGIC = b'AVB0' + SIZE = 256 + + # Keep in sync with |reserved| field of |AvbVBMetaImageHeader|. + RESERVED = 80 + + # 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 + 'L' # rollback_index_location + '47sx' + # NUL-terminated release string + 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.required_libavb_version_major, + self.required_libavb_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, + self.rollback_index_location, + release_string) = struct.unpack(self.FORMAT_STRING, data) + # Nuke NUL-bytes at the end of the string. + if self.magic != self.MAGIC: + raise AvbError('Given image does not look like a vbmeta image.') + self.release_string = release_string.rstrip(b'\0').decode('utf-8') + else: + self.magic = self.MAGIC + # Start by just requiring version 1.0. Code that adds features + # in a future version can use bump_required_libavb_version_minor() to + # bump the minor. + self.required_libavb_version_major = AVB_VERSION_MAJOR + self.required_libavb_version_minor = 0 + 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 + self.rollback_index_location = 0 + self.release_string = get_release_string() + + def bump_required_libavb_version_minor(self, minor): + """Function to bump required_libavb_version_minor. + + Call this when writing data that requires a specific libavb + version to parse it. + + Arguments: + minor: The minor version of libavb that has support for the feature. + """ + self.required_libavb_version_minor = ( + max(self.required_libavb_version_minor, minor)) + + def encode(self): + """Serializes the header. + + Returns: + The header as bytes. + """ + release_string_encoded = self.release_string.encode('utf-8') + return struct.pack(self.FORMAT_STRING, self.magic, + self.required_libavb_version_major, + self.required_libavb_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, + self.rollback_index_location, release_string_encoded) + + +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 = b'\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 generate_test_image(self, output, image_size, start_byte): + """Generates a test image for testing avbtool with known content. + + The content has following pattern: 0x00 0x01 0x02 .. 0xff 0x00 0x01 ..). + + Arguments: + output: Write test image to this file. + image_size: The size of the requested file in bytes. + start_byte: The integer value of the start byte to use for pattern + generation. + """ + pattern = bytearray([x & 0xFF for x in range(start_byte, start_byte + 256)]) + buf = bytearray() + c = int(math.ceil(image_size / 256.0)) + for _ in range(0, c): + buf.extend(pattern) + output.write(buf[0:image_size]) + + def extract_vbmeta_image(self, output, image_filename, padding_size): + """Implements the 'extract_vbmeta_image' command. + + Arguments: + output: Write vbmeta struct to this file. + image_filename: File to extract vbmeta data from (with a footer). + padding_size: If not 0, pads output so size is a multiple of the number. + + Raises: + AvbError: If there's no footer in the image. + """ + image = ImageHandler(image_filename, read_only=True) + (footer, _, _, _) = self._parse_image(image) + if not footer: + raise AvbError('Given image does not have a footer.') + + image.seek(footer.vbmeta_offset) + vbmeta_blob = image.read(footer.vbmeta_size) + output.write(vbmeta_blob) + + if padding_size > 0: + padded_size = round_to_multiple(len(vbmeta_blob), padding_size) + padding_needed = padded_size - len(vbmeta_blob) + output.write(b'\0' * padding_needed) + + 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 zero_hashtree(self, image_filename): + """Implements the 'zero_hashtree' command. + + Arguments: + image_filename: File to zero hashtree and FEC data from. + + 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.') + + # Search for a hashtree descriptor to figure out the location and + # size of the hashtree and FEC. + ht_desc = None + for desc in descriptors: + if isinstance(desc, AvbHashtreeDescriptor): + ht_desc = desc + break + + if not ht_desc: + raise AvbError('No hashtree descriptor was found.') + + zero_ht_start_offset = ht_desc.tree_offset + zero_ht_num_bytes = ht_desc.tree_size + zero_fec_start_offset = None + zero_fec_num_bytes = 0 + if ht_desc.fec_offset > 0: + if ht_desc.fec_offset != ht_desc.tree_offset + ht_desc.tree_size: + raise AvbError('Hash-tree and FEC data must be adjacent.') + zero_fec_start_offset = ht_desc.fec_offset + zero_fec_num_bytes = ht_desc.fec_size + zero_end_offset = (zero_ht_start_offset + zero_ht_num_bytes + + zero_fec_num_bytes) + image.seek(zero_end_offset) + data = image.read(image.image_size - zero_end_offset) + + # Write zeroes all over hashtree and FEC, except for the first eight bytes + # where a magic marker - ZeroHaSH - is placed. Place these markers in the + # beginning of both hashtree and FEC. (That way, in the future we can add + # options to 'avbtool zero_hashtree' so as to zero out only either/or.) + # + # Applications can use these markers to detect that the hashtree and/or + # FEC needs to be recomputed. + image.truncate(zero_ht_start_offset) + data_zeroed_firstblock = b'ZeRoHaSH' + b'\0' * (image.block_size - 8) + image.append_raw(data_zeroed_firstblock) + image.append_fill(b'\0\0\0\0', zero_ht_num_bytes - image.block_size) + if zero_fec_start_offset: + image.append_raw(data_zeroed_firstblock) + image.append_fill(b'\0\0\0\0', zero_fec_num_bytes - image.block_size) + image.append_raw(data) + + def resize_image(self, image_filename, partition_size): + """Implements the 'resize_image' command. + + Arguments: + image_filename: File with footer to resize. + partition_size: The new size of the image. + + Raises: + AvbError: If there's no footer in the image. + """ + + 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)) + (footer, _, _, _) = self._parse_image(image) + if not footer: + raise AvbError('Given image does not have a footer.') + + # The vbmeta blob is always at the end of the data so resizing an + # image amounts to just moving the footer around. + vbmeta_end_offset = footer.vbmeta_offset + footer.vbmeta_size + if vbmeta_end_offset % image.block_size != 0: + vbmeta_end_offset += image.block_size - (vbmeta_end_offset + % image.block_size) + + if partition_size < vbmeta_end_offset + 1 * image.block_size: + raise AvbError('Requested size of {} is too small for an image ' + 'of size {}.' + .format(partition_size, + vbmeta_end_offset + 1 * image.block_size)) + + # Cut at the end of the vbmeta blob and insert a DONT_CARE chunk + # with enough bytes such that the final Footer block is at the end + # of partition_size. + image.truncate(vbmeta_end_offset) + image.append_dont_care(partition_size - vbmeta_end_offset - + 1 * image.block_size) + + # Just reuse the same footer - only difference is that we're + # writing it in a different place. + footer_blob = footer.encode() + footer_blob_with_padding = (b'\0' * (image.block_size - AvbFooter.SIZE) + + footer_blob) + image.append_raw(footer_blob_with_padding) + + 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 = int(tokens[2]) != 0 + b_priority = int(tokens[3]) + b_tries_remaining = int(tokens[4]) + b_success = int(tokens[5]) != 0 + + 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, read_only=True) + o = output + (footer, header, descriptors, image_size) = self._parse_image(image) + + # To show the SHA1 of the public key. + vbmeta_blob = self._load_vbmeta_blob(image) + key_offset = (header.SIZE + + header.authentication_data_block_size + + header.public_key_offset) + key_blob = vbmeta_blob[key_offset:key_offset + header.public_key_size] + + 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('Minimum libavb version: {}.{}{}\n'.format( + header.required_libavb_version_major, + header.required_libavb_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)) + if key_blob: + hexdig = hashlib.sha1(key_blob).hexdigest() + o.write('Public key (sha1): {}\n'.format(hexdig)) + o.write('Algorithm: {}\n'.format(alg_name)) + o.write('Rollback Index: {}\n'.format(header.rollback_index)) + o.write('Flags: {}\n'.format(header.flags)) + o.write('Rollback Index Location: {}\n'.format( + header.rollback_index_location)) + o.write('Release String: \'{}\'\n'.format(header.release_string)) + + # 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 verify_image(self, image_filename, key_path, expected_chain_partitions, + follow_chain_partitions, accept_zeroed_hashtree): + """Implements the 'verify_image' command. + + Arguments: + image_filename: Image file to get information from (file object). + key_path: None or check that embedded public key matches key at given + path. + expected_chain_partitions: List of chain partitions to check or None. + follow_chain_partitions: + If True, will follows chain partitions even when not specified with + the --expected_chain_partition option + accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is + zeroed out. + + Raises: + AvbError: If verification of the image fails. + """ + expected_chain_partitions_map = {} + if expected_chain_partitions: + for cp in expected_chain_partitions: + cp_tokens = cp.split(':') + if len(cp_tokens) != 3: + raise AvbError('Malformed chained partition "{}".'.format(cp)) + partition_name = cp_tokens[0] + rollback_index_location = int(cp_tokens[1]) + file_path = cp_tokens[2] + with open(file_path, 'rb') as f: + pk_blob = f.read() + expected_chain_partitions_map[partition_name] = ( + rollback_index_location, pk_blob) + + image_dir = os.path.dirname(image_filename) + image_ext = os.path.splitext(image_filename)[1] + + key_blob = None + if key_path: + print('Verifying image {} using key at {}'.format(image_filename, + key_path)) + key_blob = RSAPublicKey(key_path).encode() + else: + print('Verifying image {} using embedded public key'.format( + image_filename)) + + image = ImageHandler(image_filename, read_only=True) + (footer, header, descriptors, _) = self._parse_image(image) + offset = 0 + if footer: + offset = footer.vbmeta_offset + + image.seek(offset) + vbmeta_blob = image.read(header.SIZE + + header.authentication_data_block_size + + header.auxiliary_data_block_size) + + alg_name, _ = lookup_algorithm_by_type(header.algorithm_type) + if not verify_vbmeta_signature(header, vbmeta_blob): + raise AvbError('Signature check failed for {} vbmeta struct {}' + .format(alg_name, image_filename)) + + if key_blob: + # The embedded public key is in the auxiliary block at an offset. + key_offset = AvbVBMetaHeader.SIZE + key_offset += header.authentication_data_block_size + key_offset += header.public_key_offset + key_blob_in_vbmeta = vbmeta_blob[key_offset:key_offset + + header.public_key_size] + if key_blob != key_blob_in_vbmeta: + raise AvbError('Embedded public key does not match given key.') + + if footer: + print('vbmeta: Successfully verified footer and {} vbmeta struct in {}' + .format(alg_name, image.filename)) + else: + print('vbmeta: Successfully verified {} vbmeta struct in {}' + .format(alg_name, image.filename)) + + for desc in descriptors: + if (isinstance(desc, AvbChainPartitionDescriptor) + and follow_chain_partitions + and expected_chain_partitions_map.get(desc.partition_name) is None): + # In this case we're processing a chain descriptor but don't have a + # --expect_chain_partition ... however --follow_chain_partitions was + # specified so we shouldn't error out in desc.verify(). + print('{}: Chained but ROLLBACK_SLOT (which is {}) ' + 'and KEY (which has sha1 {}) not specified' + .format(desc.partition_name, desc.rollback_index_location, + hashlib.sha1(desc.public_key).hexdigest())) + elif not desc.verify(image_dir, image_ext, expected_chain_partitions_map, + image, accept_zeroed_hashtree): + raise AvbError('Error verifying descriptor.') + # Honor --follow_chain_partitions - add '--' to make the output more + # readable. + if (isinstance(desc, AvbChainPartitionDescriptor) + and follow_chain_partitions): + print('--') + chained_image_filename = os.path.join(image_dir, + desc.partition_name + image_ext) + self.verify_image(chained_image_filename, key_path, None, False, + accept_zeroed_hashtree) + + def print_partition_digests(self, image_filename, output, as_json): + """Implements the 'print_partition_digests' command. + + Arguments: + image_filename: Image file to get information from (file object). + output: Output file to write human-readable information to (file object). + as_json: If True, print information as JSON + + Raises: + AvbError: If getting the partition digests from the image fails. + """ + image_dir = os.path.dirname(image_filename) + image_ext = os.path.splitext(image_filename)[1] + json_partitions = None + if as_json: + json_partitions = [] + self._print_partition_digests( + image_filename, output, json_partitions, image_dir, image_ext) + if as_json: + output.write(json.dumps({'partitions': json_partitions}, indent=2)) + + def _print_partition_digests(self, image_filename, output, json_partitions, + image_dir, image_ext): + """Helper for printing partitions. + + Arguments: + image_filename: Image file to get information from (file object). + output: Output file to write human-readable information to (file object). + json_partitions: If not None, don't print to output, instead add partition + information to this list. + image_dir: The directory to use when looking for chained partition files. + image_ext: The extension to use for chained partition files. + + Raises: + AvbError: If getting the partition digests from the image fails. + """ + image = ImageHandler(image_filename, read_only=True) + (_, _, descriptors, _) = self._parse_image(image) + + for desc in descriptors: + if isinstance(desc, AvbHashDescriptor): + digest = desc.digest.hex() + if json_partitions is not None: + json_partitions.append({'name': desc.partition_name, + 'digest': digest}) + else: + output.write('{}: {}\n'.format(desc.partition_name, digest)) + elif isinstance(desc, AvbHashtreeDescriptor): + digest = desc.root_digest.hex() + if json_partitions is not None: + json_partitions.append({'name': desc.partition_name, + 'digest': digest}) + else: + output.write('{}: {}\n'.format(desc.partition_name, digest)) + elif isinstance(desc, AvbChainPartitionDescriptor): + chained_image_filename = os.path.join(image_dir, + desc.partition_name + image_ext) + self._print_partition_digests( + chained_image_filename, output, json_partitions, image_dir, + image_ext) + + def calculate_vbmeta_digest(self, image_filename, hash_algorithm, output): + """Implements the 'calculate_vbmeta_digest' command. + + Arguments: + image_filename: Image file to get information from (file object). + hash_algorithm: Hash algorithm used. + output: Output file to write human-readable information to (file object). + """ + + image_dir = os.path.dirname(image_filename) + image_ext = os.path.splitext(image_filename)[1] + + image = ImageHandler(image_filename, read_only=True) + (footer, header, descriptors, _) = self._parse_image(image) + offset = 0 + if footer: + offset = footer.vbmeta_offset + size = (header.SIZE + header.authentication_data_block_size + + header.auxiliary_data_block_size) + image.seek(offset) + vbmeta_blob = image.read(size) + + hasher = hashlib.new(hash_algorithm) + hasher.update(vbmeta_blob) + + for desc in descriptors: + if isinstance(desc, AvbChainPartitionDescriptor): + ch_image_filename = os.path.join(image_dir, + desc.partition_name + image_ext) + ch_image = ImageHandler(ch_image_filename, read_only=True) + (ch_footer, ch_header, _, _) = self._parse_image(ch_image) + ch_offset = 0 + ch_size = (ch_header.SIZE + ch_header.authentication_data_block_size + + ch_header.auxiliary_data_block_size) + if ch_footer: + ch_offset = ch_footer.vbmeta_offset + ch_image.seek(ch_offset) + ch_vbmeta_blob = ch_image.read(ch_size) + hasher.update(ch_vbmeta_blob) + + digest = hasher.digest() + output.write('{}\n'.format(digest.hex())) + + def calculate_kernel_cmdline(self, image_filename, hashtree_disabled, output): + """Implements the 'calculate_kernel_cmdline' command. + + Arguments: + image_filename: Image file to get information from (file object). + hashtree_disabled: If True, returns the cmdline for hashtree disabled. + output: Output file to write human-readable information to (file object). + """ + + image = ImageHandler(image_filename, read_only=True) + _, _, descriptors, _ = self._parse_image(image) + + image_dir = os.path.dirname(image_filename) + image_ext = os.path.splitext(image_filename)[1] + + cmdline_descriptors = [] + for desc in descriptors: + if isinstance(desc, AvbChainPartitionDescriptor): + ch_image_filename = os.path.join(image_dir, + desc.partition_name + image_ext) + ch_image = ImageHandler(ch_image_filename, read_only=True) + _, _, ch_descriptors, _ = self._parse_image(ch_image) + for ch_desc in ch_descriptors: + if isinstance(ch_desc, AvbKernelCmdlineDescriptor): + cmdline_descriptors.append(ch_desc) + elif isinstance(desc, AvbKernelCmdlineDescriptor): + cmdline_descriptors.append(desc) + + kernel_cmdline_snippets = [] + for desc in cmdline_descriptors: + use_cmdline = True + if ((desc.flags & + AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED) + != 0): + if hashtree_disabled: + use_cmdline = False + if (desc.flags & + AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED) != 0: + if not hashtree_disabled: + use_cmdline = False + if use_cmdline: + kernel_cmdline_snippets.append(desc.kernel_cmdline) + output.write(' '.join(kernel_cmdline_snippets)) + + 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|. + + Raises: + AvbError: In case the image cannot be parsed. + """ + 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 _load_vbmeta_blob(self, image): + """Gets the vbmeta struct and associated sections. + + The image can either be a vbmeta.img or an image with a footer. + + Arguments: + image: An ImageHandler (vbmeta or footer). + + Returns: + A blob with the vbmeta struct and other sections. + """ + 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)) + + image.seek(vbmeta_offset) + data_size = AvbVBMetaHeader.SIZE + data_size += h.authentication_data_block_size + data_size += h.auxiliary_data_block_size + return image.read(data_size) + + def _get_cmdline_descriptors_for_hashtree_descriptor(self, ht): + """Generate kernel cmdline descriptors for dm-verity. + + Arguments: + ht: A AvbHashtreeDescriptor + + 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. + + """ + 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(ht.root_digest.hex()) # root_digest + c += ' {}'.format(ht.salt.hex()) # salt + if ht.fec_num_roots > 0: + c += ' 10' # number of optional args + c += ' $(ANDROID_VERITY_MODE)' + 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 += ' $(ANDROID_VERITY_MODE)' + c += ' ignore_zero_blocks' + c += '" root=/dev/dm-0' + + # 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 _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') + + return self._get_cmdline_descriptors_for_hashtree_descriptor(ht) + + def make_vbmeta_image(self, output, chain_partitions, algorithm_name, + key_path, public_key_metadata_path, rollback_index, + flags, rollback_index_location, + props, props_from_file, kernel_cmdlines, + setup_rootfs_from_kernel, + include_descriptors_from_image, + signing_helper, + signing_helper_with_files, + release_string, + append_to_release_string, + print_required_libavb_version, + padding_size): + """Implements the 'make_vbmeta_image' command. + + Arguments: + output: File to write the image to. + chain_partitions: List of partitions to chain or None. + 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. + rollback_index_location: Location of the main vbmeta rollback index. + props: Properties to insert (list of strings of the form 'key:value'). + props_from_file: Properties to insert (list of strings 'key:'). + 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. + signing_helper_with_files: Same as signing_helper but uses files instead. + release_string: None or avbtool release string to use instead of default. + append_to_release_string: None or string to append. + print_required_libavb_version: True to only print required libavb version. + padding_size: If not 0, pads output so size is a multiple of the number. + + Raises: + AvbError: If a chained partition is malformed. + """ + # If we're asked to calculate minimum required libavb version, we're done. + tmp_header = AvbVBMetaHeader() + if rollback_index_location > 0: + tmp_header.bump_required_libavb_version_minor(2) + if include_descriptors_from_image: + # Use the bump logic in AvbVBMetaHeader to calculate the max required + # version of all included descriptors. + for image in include_descriptors_from_image: + (_, image_header, _, _) = self._parse_image(ImageHandler( + image.name, read_only=True)) + tmp_header.bump_required_libavb_version_minor( + image_header.required_libavb_version_minor) + + if print_required_libavb_version: + print('1.{}'.format(tmp_header.required_libavb_version_minor)) + return + + if not output: + raise AvbError('No output file given') + + descriptors = [] + ht_desc_to_setup = None + vbmeta_blob = self._generate_vbmeta_blob( + algorithm_name, key_path, public_key_metadata_path, descriptors, + chain_partitions, rollback_index, flags, rollback_index_location, + props, props_from_file, + kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup, + include_descriptors_from_image, signing_helper, + signing_helper_with_files, release_string, + append_to_release_string, tmp_header.required_libavb_version_minor) + + # Write entire vbmeta blob (header, authentication, auxiliary). + output.seek(0) + output.write(vbmeta_blob) + + if padding_size > 0: + padded_size = round_to_multiple(len(vbmeta_blob), padding_size) + padding_needed = padded_size - len(vbmeta_blob) + output.write(b'\0' * padding_needed) + + def _generate_vbmeta_blob(self, algorithm_name, key_path, + public_key_metadata_path, descriptors, + chain_partitions, + rollback_index, flags, rollback_index_location, + props, props_from_file, + kernel_cmdlines, + setup_rootfs_from_kernel, + ht_desc_to_setup, + include_descriptors_from_image, signing_helper, + signing_helper_with_files, + release_string, append_to_release_string, + required_libavb_version_minor): + """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. + chain_partitions: List of partitions to chain or None. + rollback_index: The rollback index to use. + flags: Flags to use in the image. + rollback_index_location: Location of the main vbmeta rollback index. + props: Properties to insert (List of strings of the form 'key:value'). + props_from_file: Properties to insert (List of strings 'key:'). + kernel_cmdlines: Kernel cmdlines to insert (list of strings). + setup_rootfs_from_kernel: None or file to generate + dm-verity kernel cmdline from. + ht_desc_to_setup: If not None, an AvbHashtreeDescriptor to + generate dm-verity kernel cmdline descriptors 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. + signing_helper_with_files: Same as signing_helper but uses files instead. + release_string: None or avbtool release string. + append_to_release_string: None or string to append. + required_libavb_version_minor: Use at least this required minor version. + + Returns: + The VBMeta blob as bytes. + + 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)) + + if not descriptors: + descriptors = [] + + h = AvbVBMetaHeader() + h.bump_required_libavb_version_minor(required_libavb_version_minor) + + # Insert chained partition descriptors, if any + if chain_partitions: + used_locations = {rollback_index_location: True} + for cp in chain_partitions: + cp_tokens = cp.split(':') + if len(cp_tokens) != 3: + raise AvbError('Malformed chained partition "{}".'.format(cp)) + partition_name = cp_tokens[0] + chained_rollback_index_location = int(cp_tokens[1]) + file_path = cp_tokens[2] + # Check that the same rollback location isn't being used by + # multiple chained partitions. + if used_locations.get(chained_rollback_index_location): + raise AvbError('Rollback Index Location {} is already in use.'.format( + chained_rollback_index_location)) + used_locations[chained_rollback_index_location] = True + desc = AvbChainPartitionDescriptor() + desc.partition_name = partition_name + desc.rollback_index_location = chained_rollback_index_location + if desc.rollback_index_location < 1: + raise AvbError('Rollback index location must be 1 or larger.') + with open(file_path, 'rb') as f: + desc.public_key = f.read() + descriptors.append(desc) + + # Descriptors. + encoded_descriptors = bytearray() + 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)) + # pylint: disable=redefined-variable-type + desc = AvbPropertyDescriptor() + desc.key = prop[0:idx] + desc.value = prop[(idx + 1):].encode('utf-8') + 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] + file_path = prop[(idx + 1):] + with open(file_path, 'rb') as f: + # pylint: disable=attribute-defined-outside-init + desc.value = f.read() + encoded_descriptors.extend(desc.encode()) + + # Add AvbKernelCmdline descriptor for dm-verity from an image, 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 AvbKernelCmdline descriptor for dm-verity from desc, if requested. + if ht_desc_to_setup: + cmdline_desc = self._get_cmdline_descriptors_for_hashtree_descriptor( + ht_desc_to_setup) + 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: + descriptors_dict = dict() + for image in include_descriptors_from_image: + image_handler = ImageHandler(image.name, read_only=True) + (_, image_vbmeta_header, image_descriptors, _) = self._parse_image( + image_handler) + # Bump the required libavb version to support all included descriptors. + h.bump_required_libavb_version_minor( + image_vbmeta_header.required_libavb_version_minor) + for desc in image_descriptors: + # The --include_descriptors_from_image option is used in some setups + # with images A and B where both A and B contain a descriptor + # for a partition with the same name. Since it's not meaningful + # to include both descriptors, only include the last seen descriptor. + # See bug 76386656 for details. + if hasattr(desc, 'partition_name'): + key = type(desc).__name__ + '_' + desc.partition_name + descriptors_dict[key] = desc.encode() + else: + encoded_descriptors.extend(desc.encode()) + for key in sorted(descriptors_dict): + encoded_descriptors.extend(descriptors_dict[key]) + + # Load public key metadata blob, if requested. + pkmd_blob = b'' + if public_key_metadata_path: + with open(public_key_metadata_path, 'rb') as f: + pkmd_blob = f.read() + + key = None + encoded_key = b'' + if alg.public_key_num_bytes > 0: + if not key_path: + raise AvbError('Key is required for algorithm {}'.format( + algorithm_name)) + encoded_key = RSAPublicKey(key_path).encode() + if len(encoded_key) != alg.public_key_num_bytes: + raise AvbError('Key is wrong size for algorithm {}'.format( + algorithm_name)) + + # Override release string, if requested. + if isinstance(release_string, str): + h.release_string = release_string + + # Append to release string, if requested. Also insert a space before. + if isinstance(append_to_release_string, str): + h.release_string += ' ' + append_to_release_string + + # 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 + h.rollback_index_location = rollback_index_location + + # 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(b'\0' * padding_bytes) + + # Calculate the hash. + binary_hash = b'' + binary_signature = b'' + if algorithm_name != 'NONE': + ha = hashlib.new(alg.hash_name) + ha.update(header_data_blob) + ha.update(aux_data_blob) + binary_hash = ha.digest() + + # Calculate the signature. + rsa_key = RSAPublicKey(key_path) + data_to_sign = header_data_blob + bytes(aux_data_blob) + binary_signature = rsa_key.sign(algorithm_name, data_to_sign, + signing_helper, signing_helper_with_files) + + # 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(b'\0' * padding_bytes) + + return header_data_blob + bytes(auth_data_blob) + bytes(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. + + Raises: + AvbError: If the public key could not be extracted. + """ + output.write(RSAPublicKey(key_path).encode()) + + def append_vbmeta_image(self, image_filename, vbmeta_image_filename, + partition_size): + """Implementation of the append_vbmeta_image command. + + Arguments: + image_filename: File to add the footer to. + vbmeta_image_filename: File to get vbmeta struct from. + partition_size: Size of partition. + + Raises: + AvbError: If an argument is incorrect or if appending VBMeta image fialed. + """ + 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 append_vbmeta_image' is idempotent. + if image.image_size >= AvbFooter.SIZE: + 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 + else: + # Image size is too small to possibly contain a footer. + original_image_size = image.image_size + + # If anything goes wrong from here-on, restore the image back to + # its original size. + try: + vbmeta_image_handler = ImageHandler(vbmeta_image_filename) + vbmeta_blob = self._load_vbmeta_blob(vbmeta_image_handler) + + # 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 + b'\0' * padding_needed + + # Append vbmeta blob and footer + 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 = (b'\0' * (image.block_size - AvbFooter.SIZE) + + footer_blob) + image.append_raw(footer_blob_with_padding) + + except Exception as e: + # Truncate back to original size, then re-raise. + image.truncate(original_image_size) + raise AvbError('Appending VBMeta image failed: {}.'.format(e)) + + def add_hash_footer(self, image_filename, partition_size, partition_name, + hash_algorithm, salt, chain_partitions, algorithm_name, + key_path, + public_key_metadata_path, rollback_index, flags, + rollback_index_location, props, + props_from_file, kernel_cmdlines, + setup_rootfs_from_kernel, + include_descriptors_from_image, calc_max_image_size, + signing_helper, signing_helper_with_files, + release_string, append_to_release_string, + output_vbmeta_image, do_not_append_vbmeta_image, + print_required_libavb_version, use_persistent_digest, + do_not_use_ab): + """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. + 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: Rollback index. + flags: Flags value to use in the image. + rollback_index_location: Location of the main vbmeta rollback index. + props: Properties to insert (List of strings of the form 'key:value'). + props_from_file: Properties to insert (List of strings 'key:'). + 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 footer - instead calculate the + maximum image size leaving enough room for metadata with the + given |partition_size|. + signing_helper: Program which signs a hash and return signature. + signing_helper_with_files: Same as signing_helper but uses files instead. + release_string: None or avbtool release string. + append_to_release_string: None or string to append. + output_vbmeta_image: If not None, also write vbmeta struct to this file. + do_not_append_vbmeta_image: If True, don't append vbmeta struct. + print_required_libavb_version: True to only print required libavb version. + use_persistent_digest: Use a persistent digest on device. + do_not_use_ab: This partition does not use A/B. + + Raises: + AvbError: If an argument is incorrect of if adding of hash_footer failed. + """ + required_libavb_version_minor = 0 + if use_persistent_digest or do_not_use_ab: + required_libavb_version_minor = 1 + if rollback_index_location > 0: + required_libavb_version_minor = 2 + + # If we're asked to calculate minimum required libavb version, we're done. + if print_required_libavb_version: + print('1.{}'.format(required_libavb_version_minor)) + return + + # 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 + if partition_size < max_metadata_size: + raise AvbError('Parition size of {} is too small. ' + 'Needs to be at least {}'.format( + partition_size, max_metadata_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_hash_footer' is idempotent (modulo + # salts). + if image.image_size >= AvbFooter.SIZE: + 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 + else: + # Image size is too small to possibly contain a footer. + original_image_size = image.image_size + + # If anything goes wrong from here-on, restore the image back to + # its original size. + try: + # 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(hash_algorithm).digest()) + if salt: + salt = binascii.unhexlify(salt) + elif salt is None and not use_persistent_digest: + # If salt is not explicitly specified, choose a hash that's the same + # size as the hash size. Don't populate a random salt if this + # descriptor is being created to use a persistent digest on device. + hash_size = digest_size + with open('/dev/urandom', 'rb') as f: + salt = f.read(hash_size) + else: + salt = b'' + + hasher = hashlib.new(hash_algorithm, 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.flags = 0 + if do_not_use_ab: + h_desc.flags |= 1 # AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB + if not use_persistent_digest: + h_desc.digest = digest + + # Generate the VBMeta footer. + ht_desc_to_setup = None + vbmeta_blob = self._generate_vbmeta_blob( + algorithm_name, key_path, public_key_metadata_path, [h_desc], + chain_partitions, rollback_index, flags, rollback_index_location, + props, props_from_file, + kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup, + include_descriptors_from_image, signing_helper, + signing_helper_with_files, release_string, + append_to_release_string, required_libavb_version_minor) + + # Write vbmeta blob, if requested. + if output_vbmeta_image: + output_vbmeta_image.write(vbmeta_blob) + + # Append vbmeta blob and footer, unless requested not to. + if not do_not_append_vbmeta_image: + # 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 + b'\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 = ( + b'\0' * (image.block_size - AvbFooter.SIZE) + footer_blob) + image.append_raw(footer_blob_with_padding) + except Exception as e: + # Truncate back to original size, then re-raise. + image.truncate(original_image_size) + raise AvbError('Adding hash_footer failed: {}.'.format(e)) + + def add_hashtree_footer(self, image_filename, partition_size, partition_name, + generate_fec, fec_num_roots, hash_algorithm, + block_size, salt, chain_partitions, algorithm_name, + key_path, + public_key_metadata_path, rollback_index, flags, + rollback_index_location, + props, props_from_file, kernel_cmdlines, + setup_rootfs_from_kernel, + setup_as_rootfs_from_kernel, + include_descriptors_from_image, + calc_max_image_size, signing_helper, + signing_helper_with_files, + release_string, append_to_release_string, + output_vbmeta_image, do_not_append_vbmeta_image, + print_required_libavb_version, + use_persistent_root_digest, do_not_use_ab, + no_hashtree): + """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 or 0 to put it right at the end. + 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. + 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: Rollback index. + flags: Flags value to use in the image. + rollback_index_location: Location of the main vbmeta rollback index. + props: Properties to insert (List of strings of the form 'key:value'). + props_from_file: Properties to insert (List of strings 'key:'). + kernel_cmdlines: Kernel cmdlines to insert (list of strings). + setup_rootfs_from_kernel: None or file to generate + dm-verity kernel cmdline from. + setup_as_rootfs_from_kernel: If True, generate dm-verity kernel + cmdline to set up rootfs. + 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. + signing_helper_with_files: Same as signing_helper but uses files instead. + release_string: None or avbtool release string. + append_to_release_string: None or string to append. + output_vbmeta_image: If not None, also write vbmeta struct to this file. + do_not_append_vbmeta_image: If True, don't append vbmeta struct. + print_required_libavb_version: True to only print required libavb version. + use_persistent_root_digest: Use a persistent root digest on device. + do_not_use_ab: The partition does not use A/B. + no_hashtree: Do not append hashtree. Set size in descriptor as zero. + + Raises: + AvbError: If an argument is incorrect or adding the hashtree footer + failed. + """ + required_libavb_version_minor = 0 + if use_persistent_root_digest or do_not_use_ab: + required_libavb_version_minor = 1 + if rollback_index_location > 0: + required_libavb_version_minor = 2 + + # If we're asked to calculate minimum required libavb version, we're done. + if print_required_libavb_version: + print('1.{}'.format(required_libavb_version_minor)) + return + + digest_size = len(hashlib.new(hash_algorithm).digest()) + digest_padding = round_to_pow2(digest_size) - digest_size + + # If |partition_size| is given (e.g. not 0), 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. + if partition_size > 0: + max_tree_size = 0 + max_fec_size = 0 + if not no_hashtree: + (_, max_tree_size) = calc_hash_level_offsets( + partition_size, block_size, digest_size + digest_padding) + 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 + else: + max_image_size = 0 + + # 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 > 0: + 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)) + elif image.image_size % image.block_size != 0: + raise AvbError('File size of {} is not a multiple of the image ' + 'block size {}.'.format(image.image_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). + if image.image_size >= AvbFooter.SIZE: + 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 + else: + # Image size is too small to possibly contain a footer. + 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 partition_size > 0: + 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 = binascii.unhexlify(salt) + elif salt is None and not use_persistent_root_digest: + # If salt is not explicitly specified, choose a hash that's the same + # size as the hash size. Don't populate a random salt if this + # descriptor is being created to use a persistent digest on device. + hash_size = digest_size + with open('/dev/urandom', 'rb') as f: + salt = f.read(hash_size) + else: + salt = b'' + + # 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. + if no_hashtree: + tree_size = 0 + hash_tree = b'' + 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 + if do_not_use_ab: + ht_desc.flags |= 1 # AVB_HASHTREE_DESCRIPTOR_FLAGS_DO_NOT_USE_AB + if not use_persistent_root_digest: + 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 + b'\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: + if no_hashtree: + fec_data = b'' + else: + 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 + b'\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) + + ht_desc_to_setup = None + if setup_as_rootfs_from_kernel: + ht_desc_to_setup = ht_desc + + # 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], + chain_partitions, rollback_index, flags, rollback_index_location, + props, props_from_file, + kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup, + include_descriptors_from_image, signing_helper, + signing_helper_with_files, release_string, + append_to_release_string, required_libavb_version_minor) + padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) - + len(vbmeta_blob)) + vbmeta_blob_with_padding = vbmeta_blob + b'\0' * padding_needed + + # Write vbmeta blob, if requested. + if output_vbmeta_image: + output_vbmeta_image.write(vbmeta_blob) + + # Append vbmeta blob and footer, unless requested not to. + if not do_not_append_vbmeta_image: + 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.. + if partition_size > 0: + 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 = ( + b'\0' * (image.block_size - AvbFooter.SIZE) + footer_blob) + image.append_raw(footer_blob_with_padding) + + except Exception as e: + # Truncate back to original size, then re-raise. + image.truncate(original_image_size) + raise AvbError('Adding hashtree_footer failed: {}.'.format(e)) + + def make_atx_certificate(self, output, authority_key_path, subject_key_path, + subject_key_version, subject, + is_intermediate_authority, usage, signing_helper, + signing_helper_with_files): + """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_path: Path to 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. + usage: If not empty, overrides the cert usage with a hash of this value. + signing_helper: Program which signs a hash and returns the signature. + signing_helper_with_files: Same as signing_helper but uses files instead. + + Raises: + AvbError: If there an error during signing. + """ + signed_data = bytearray() + signed_data.extend(struct.pack(' 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 = ' block_size: + level_output_list = [] + remaining = hash_src_size + while remaining > 0: + hasher = hashlib.new(hash_alg_name, 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(b'\0' * (block_size - len(data))) + level_output_list.append(hasher.digest()) + if digest_padding > 0: + level_output_list.append(b'\0' * digest_padding) + + level_output = b''.join(level_output_list) + + padding_needed = (round_to_multiple( + len(level_output), block_size) - len(level_output)) + level_output += b'\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(hash_alg_name, salt) + hasher.update(level_output) + return hasher.digest(), bytes(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('--signing_helper_with_files', + help='Path to helper used for signing using files', + 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('--rollback_index_location', + help='Location of main vbmeta Rollback Index', + type=parse_number, + default=0) + # This is used internally for unit tests. Do not include in --help output. + sub_parser.add_argument('--internal_release_string', + help=argparse.SUPPRESS) + sub_parser.add_argument('--append_to_release_string', + help='Text to append to release string', + metavar='STR') + 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')) + sub_parser.add_argument('--print_required_libavb_version', + help=('Don\'t store the footer - ' + 'instead calculate the required libavb ' + 'version for the given options.'), + action='store_true') + # These are only allowed from top-level vbmeta and boot-in-lieu-of-vbmeta. + 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') + + def _add_common_footer_args(self, sub_parser): + """Adds arguments used by add_*_footer sub-commands. + + Arguments: + sub_parser: The parser to add arguments to. + """ + sub_parser.add_argument('--use_persistent_digest', + help='Use a persistent digest on device instead of ' + 'storing the digest in the descriptor. This ' + 'cannot be used with A/B so must be combined ' + 'with --do_not_use_ab when an A/B suffix is ' + 'expected at runtime.', + action='store_true') + sub_parser.add_argument('--do_not_use_ab', + help='The partition does not use A/B even when an ' + 'A/B suffix is present. This must not be used ' + 'for vbmeta or chained partitions.', + action='store_true') + + def _fixup_common_args(self, args): + """Common fixups needed by subcommands. + + Arguments: + args: Arguments to modify. + + Returns: + The modified arguments. + """ + if args.set_hashtree_disabled_flag: + args.flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED + return args + + 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( + 'generate_test_image', + help=('Generates a test image with a known pattern for testing: ' + '0x00 0x01 0x02 ... 0xff 0x00 0x01 ...')) + sub_parser.add_argument('--image_size', + help='Size of image to generate.', + type=parse_number, + required=True) + sub_parser.add_argument('--start_byte', + help='Integer for the start byte of the pattern.', + type=parse_number, + default=0) + sub_parser.add_argument('--output', + help='Output file name.', + type=argparse.FileType('wb'), + default=sys.stdout) + sub_parser.set_defaults(func=self.generate_test_image) + + 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')) + sub_parser.add_argument('--padding_size', + metavar='NUMBER', + help='If non-zero, pads output with NUL bytes so ' + 'its size is a multiple of NUMBER ' + '(default: 0)', + type=parse_number, + default=0) + self._add_common_args(sub_parser) + 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('rb+')) + sub_parser.add_argument('--partition_size', + help='Partition size', + type=parse_number) + sub_parser.add_argument('--partition_name', + help='Partition name', + default=None) + 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)') + sub_parser.add_argument('--calc_max_image_size', + help=('Don\'t store the footer - ' + 'instead calculate the maximum image size ' + 'leaving enough room for metadata with ' + 'the given partition size.'), + action='store_true') + sub_parser.add_argument('--output_vbmeta_image', + help='Also write vbmeta struct to file', + type=argparse.FileType('wb')) + sub_parser.add_argument('--do_not_append_vbmeta_image', + help=('Do not append vbmeta struct or footer ' + 'to the image'), + action='store_true') + self._add_common_args(sub_parser) + self._add_common_footer_args(sub_parser) + sub_parser.set_defaults(func=self.add_hash_footer) + + sub_parser = subparsers.add_parser('append_vbmeta_image', + help='Append vbmeta image to image.') + sub_parser.add_argument('--image', + help='Image to append vbmeta blob to', + type=argparse.FileType('rb+')) + sub_parser.add_argument('--partition_size', + help='Partition size', + type=parse_number, + required=True) + sub_parser.add_argument('--vbmeta_image', + help='Image with vbmeta blob to append', + type=argparse.FileType('rb')) + sub_parser.set_defaults(func=self.append_vbmeta_image) + + 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('rb+')) + sub_parser.add_argument('--partition_size', + help='Partition size', + default=0, + type=parse_number) + sub_parser.add_argument('--partition_name', + help='Partition name', + default='') + 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) + # TODO(zeuthen): The --generate_fec option was removed when we + # moved to generating FEC by default. To avoid breaking existing + # users needing to transition we simply just print a warning below + # in add_hashtree_footer(). Remove this option and the warning at + # some point in the future. + sub_parser.add_argument('--generate_fec', + help=argparse.SUPPRESS, + action='store_true') + sub_parser.add_argument( + '--do_not_generate_fec', + help='Do not generate 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') + sub_parser.add_argument('--output_vbmeta_image', + help='Also write vbmeta struct to file', + type=argparse.FileType('wb')) + sub_parser.add_argument('--do_not_append_vbmeta_image', + help=('Do not append vbmeta struct or footer ' + 'to the image'), + action='store_true') + # This is different from --setup_rootfs_from_kernel insofar that + # it doesn't take an IMAGE, the generated cmdline will be for the + # hashtree we're adding. + sub_parser.add_argument('--setup_as_rootfs_from_kernel', + action='store_true', + help='Adds kernel cmdline for setting up rootfs') + sub_parser.add_argument('--no_hashtree', + action='store_true', + help='Do not append hashtree') + self._add_common_args(sub_parser) + self._add_common_footer_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('rb+'), + 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('zero_hashtree', + help='Zero out hashtree and FEC data.') + sub_parser.add_argument('--image', + help='Image with a footer', + type=argparse.FileType('rb+'), + required=True) + sub_parser.set_defaults(func=self.zero_hashtree) + + sub_parser = subparsers.add_parser( + 'extract_vbmeta_image', + help='Extracts vbmeta from an image with a footer.') + sub_parser.add_argument('--image', + help='Image with footer', + type=argparse.FileType('rb'), + required=True) + sub_parser.add_argument('--output', + help='Output file name', + type=argparse.FileType('wb')) + sub_parser.add_argument('--padding_size', + metavar='NUMBER', + help='If non-zero, pads output with NUL bytes so ' + 'its size is a multiple of NUMBER ' + '(default: 0)', + type=parse_number, + default=0) + sub_parser.set_defaults(func=self.extract_vbmeta_image) + + sub_parser = subparsers.add_parser('resize_image', + help='Resize image with a footer.') + sub_parser.add_argument('--image', + help='Image with a footer', + type=argparse.FileType('rb+'), + required=True) + sub_parser.add_argument('--partition_size', + help='New partition size', + type=parse_number) + sub_parser.set_defaults(func=self.resize_image) + + 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( + 'verify_image', + help='Verify an image.') + sub_parser.add_argument('--image', + help='Image to verify', + type=argparse.FileType('rb'), + required=True) + sub_parser.add_argument('--key', + help='Check embedded public key matches KEY', + metavar='KEY', + required=False) + sub_parser.add_argument('--expected_chain_partition', + help='Expected chain partition', + metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH', + action='append') + sub_parser.add_argument( + '--follow_chain_partitions', + help=('Follows chain partitions even when not ' + 'specified with the --expected_chain_partition option'), + action='store_true') + sub_parser.add_argument( + '--accept_zeroed_hashtree', + help=('Accept images where the hashtree or FEC data is zeroed out'), + action='store_true') + sub_parser.set_defaults(func=self.verify_image) + + sub_parser = subparsers.add_parser( + 'print_partition_digests', + help='Prints partition digests.') + sub_parser.add_argument('--image', + help='Image to print partition digests from', + 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.add_argument('--json', + help=('Print output as JSON'), + action='store_true') + sub_parser.set_defaults(func=self.print_partition_digests) + + sub_parser = subparsers.add_parser( + 'calculate_vbmeta_digest', + help='Calculate vbmeta digest.') + sub_parser.add_argument('--image', + help='Image to calculate digest for', + type=argparse.FileType('rb'), + required=True) + sub_parser.add_argument('--hash_algorithm', + help='Hash algorithm to use (default: sha256)', + default='sha256') + sub_parser.add_argument('--output', + help='Write hex digest to file (default: stdout)', + type=argparse.FileType('wt'), + default=sys.stdout) + sub_parser.set_defaults(func=self.calculate_vbmeta_digest) + + sub_parser = subparsers.add_parser( + 'calculate_kernel_cmdline', + help='Calculate kernel cmdline.') + sub_parser.add_argument('--image', + help='Image to calculate kernel cmdline for', + type=argparse.FileType('rb'), + required=True) + sub_parser.add_argument('--hashtree_disabled', + help='Return the cmdline for hashtree disabled', + action='store_true') + sub_parser.add_argument('--output', + help='Write cmdline to file (default: stdout)', + type=argparse.FileType('wt'), + default=sys.stdout) + sub_parser.set_defaults(func=self.calculate_kernel_cmdline) + + 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('--usage', + help=('Override usage with a hash of the provided ' + 'string'), + required=False) + 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.add_argument('--signing_helper_with_files', + help='Path to helper used for signing using files', + 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.set_defaults(func=self.make_atx_metadata) + + sub_parser = subparsers.add_parser( + 'make_atx_unlock_credential', + help='Create an Android Things eXtension (ATX) unlock credential.') + sub_parser.add_argument('--output', + help='Write credential 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('--unlock_key_certificate', + help='Path to unlock key certificate file', + type=argparse.FileType('rb'), + required=True) + sub_parser.add_argument('--challenge', + help='Path to the challenge to sign (optional). If ' + 'this is not provided the challenge signature ' + 'field is omitted and can be concatenated ' + 'later.', + required=False) + sub_parser.add_argument('--unlock_key', + help='Path to unlock key (optional). Must be ' + 'provided if using --challenge.', + 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('--signing_helper_with_files', + help='Path to helper used for signing using files', + metavar='APP', + default=None, + required=False) + sub_parser.set_defaults(func=self.make_atx_unlock_credential) + + args = parser.parse_args(argv[1:]) + try: + args.func(args) + except AttributeError: + # This error gets raised when the command line tool is called without any + # arguments. It mimics the original Python 2 behavior. + parser.print_usage() + print('avbtool: error: too few arguments') + sys.exit(2) + except AvbError as e: + sys.stderr.write('{}: {}\n'.format(argv[0], str(e))) + sys.exit(1) + + def version(self, _): + """Implements the 'version' sub-command.""" + print(get_release_string()) + + def generate_test_image(self, args): + """Implements the 'generate_test_image' sub-command.""" + self.avb.generate_test_image(args.output, args.image_size, args.start_byte) + + 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.""" + args = self._fixup_common_args(args) + self.avb.make_vbmeta_image(args.output, args.chain_partition, + args.algorithm, args.key, + args.public_key_metadata, args.rollback_index, + args.flags, args.rollback_index_location, + args.prop, args.prop_from_file, + args.kernel_cmdline, + args.setup_rootfs_from_kernel, + args.include_descriptors_from_image, + args.signing_helper, + args.signing_helper_with_files, + args.internal_release_string, + args.append_to_release_string, + args.print_required_libavb_version, + args.padding_size) + + def append_vbmeta_image(self, args): + """Implements the 'append_vbmeta_image' sub-command.""" + self.avb.append_vbmeta_image(args.image.name, args.vbmeta_image.name, + args.partition_size) + + def add_hash_footer(self, args): + """Implements the 'add_hash_footer' sub-command.""" + args = self._fixup_common_args(args) + self.avb.add_hash_footer(args.image.name if args.image else None, + args.partition_size, + args.partition_name, args.hash_algorithm, + args.salt, args.chain_partition, args.algorithm, + args.key, + args.public_key_metadata, args.rollback_index, + args.flags, args.rollback_index_location, + 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, + args.signing_helper_with_files, + args.internal_release_string, + args.append_to_release_string, + args.output_vbmeta_image, + args.do_not_append_vbmeta_image, + args.print_required_libavb_version, + args.use_persistent_digest, + args.do_not_use_ab) + + def add_hashtree_footer(self, args): + """Implements the 'add_hashtree_footer' sub-command.""" + args = self._fixup_common_args(args) + # TODO(zeuthen): Remove when removing support for the + # '--generate_fec' option above. + if args.generate_fec: + sys.stderr.write('The --generate_fec option is deprecated since FEC ' + 'is now generated by default. Use the option ' + '--do_not_generate_fec to not generate FEC.\n') + self.avb.add_hashtree_footer( + args.image.name if args.image else None, + args.partition_size, + args.partition_name, + not args.do_not_generate_fec, args.fec_num_roots, + args.hash_algorithm, args.block_size, + args.salt, args.chain_partition, args.algorithm, + args.key, args.public_key_metadata, + args.rollback_index, args.flags, + args.rollback_index_location, args.prop, + args.prop_from_file, + args.kernel_cmdline, + args.setup_rootfs_from_kernel, + args.setup_as_rootfs_from_kernel, + args.include_descriptors_from_image, + args.calc_max_image_size, + args.signing_helper, + args.signing_helper_with_files, + args.internal_release_string, + args.append_to_release_string, + args.output_vbmeta_image, + args.do_not_append_vbmeta_image, + args.print_required_libavb_version, + args.use_persistent_digest, + args.do_not_use_ab, + args.no_hashtree) + + def erase_footer(self, args): + """Implements the 'erase_footer' sub-command.""" + self.avb.erase_footer(args.image.name, args.keep_hashtree) + + def zero_hashtree(self, args): + """Implements the 'zero_hashtree' sub-command.""" + self.avb.zero_hashtree(args.image.name) + + def extract_vbmeta_image(self, args): + """Implements the 'extract_vbmeta_image' sub-command.""" + self.avb.extract_vbmeta_image(args.output, args.image.name, + args.padding_size) + + def resize_image(self, args): + """Implements the 'resize_image' sub-command.""" + self.avb.resize_image(args.image.name, args.partition_size) + + 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 verify_image(self, args): + """Implements the 'verify_image' sub-command.""" + self.avb.verify_image(args.image.name, args.key, + args.expected_chain_partition, + args.follow_chain_partitions, + args.accept_zeroed_hashtree) + + def print_partition_digests(self, args): + """Implements the 'print_partition_digests' sub-command.""" + self.avb.print_partition_digests(args.image.name, args.output, args.json) + + def calculate_vbmeta_digest(self, args): + """Implements the 'calculate_vbmeta_digest' sub-command.""" + self.avb.calculate_vbmeta_digest(args.image.name, args.hash_algorithm, + args.output) + + def calculate_kernel_cmdline(self, args): + """Implements the 'calculate_kernel_cmdline' sub-command.""" + self.avb.calculate_kernel_cmdline(args.image.name, args.hashtree_disabled, + 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.name, + args.subject_key_version, + args.subject.read(), + args.subject_is_intermediate_authority, + args.usage, + args.signing_helper, + args.signing_helper_with_files) + + 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.name, + 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()) + + def make_atx_unlock_credential(self, args): + """Implements the 'make_atx_unlock_credential' sub-command.""" + self.avb.make_atx_unlock_credential( + args.output, + args.intermediate_key_certificate.read(), + args.unlock_key_certificate.read(), + args.challenge, + args.unlock_key, + args.signing_helper, + args.signing_helper_with_files) + + +if __name__ == '__main__': + if AVB_INVOCATION_LOGFILE: + with open(AVB_INVOCATION_LOGFILE, 'a') as log: + log.write(' '.join(sys.argv)) + log.write('\n') + + tool = AvbTool() + tool.run(sys.argv) diff --git a/aosp/libavb/Makefile b/aosp/libavb1.1/Makefile similarity index 100% rename from aosp/libavb/Makefile rename to aosp/libavb1.1/Makefile diff --git a/aosp/libavb/build.gradle b/aosp/libavb1.1/build.gradle similarity index 100% rename from aosp/libavb/build.gradle rename to aosp/libavb1.1/build.gradle diff --git a/aosp/libavb/src/avb/c/avb_chain_partition_descriptor.c b/aosp/libavb1.1/src/avb/c/avb_chain_partition_descriptor.c similarity index 100% rename from aosp/libavb/src/avb/c/avb_chain_partition_descriptor.c rename to aosp/libavb1.1/src/avb/c/avb_chain_partition_descriptor.c diff --git a/aosp/libavb/src/avb/c/avb_cmdline.c b/aosp/libavb1.1/src/avb/c/avb_cmdline.c similarity index 100% rename from aosp/libavb/src/avb/c/avb_cmdline.c rename to aosp/libavb1.1/src/avb/c/avb_cmdline.c diff --git a/aosp/libavb/src/avb/c/avb_crc32.c b/aosp/libavb1.1/src/avb/c/avb_crc32.c similarity index 100% rename from aosp/libavb/src/avb/c/avb_crc32.c rename to aosp/libavb1.1/src/avb/c/avb_crc32.c diff --git a/aosp/libavb/src/avb/c/avb_crypto.c b/aosp/libavb1.1/src/avb/c/avb_crypto.c similarity index 100% rename from aosp/libavb/src/avb/c/avb_crypto.c rename to aosp/libavb1.1/src/avb/c/avb_crypto.c diff --git a/aosp/libavb/src/avb/c/avb_descriptor.c b/aosp/libavb1.1/src/avb/c/avb_descriptor.c similarity index 100% rename from aosp/libavb/src/avb/c/avb_descriptor.c rename to aosp/libavb1.1/src/avb/c/avb_descriptor.c diff --git a/aosp/libavb/src/avb/c/avb_footer.c b/aosp/libavb1.1/src/avb/c/avb_footer.c similarity index 100% rename from aosp/libavb/src/avb/c/avb_footer.c rename to aosp/libavb1.1/src/avb/c/avb_footer.c diff --git a/aosp/libavb/src/avb/c/avb_hash_descriptor.c b/aosp/libavb1.1/src/avb/c/avb_hash_descriptor.c similarity index 100% rename from aosp/libavb/src/avb/c/avb_hash_descriptor.c rename to aosp/libavb1.1/src/avb/c/avb_hash_descriptor.c diff --git a/aosp/libavb/src/avb/c/avb_hashtree_descriptor.c b/aosp/libavb1.1/src/avb/c/avb_hashtree_descriptor.c similarity index 100% rename from aosp/libavb/src/avb/c/avb_hashtree_descriptor.c rename to aosp/libavb1.1/src/avb/c/avb_hashtree_descriptor.c diff --git a/aosp/libavb/src/avb/c/avb_kernel_cmdline_descriptor.c b/aosp/libavb1.1/src/avb/c/avb_kernel_cmdline_descriptor.c similarity index 100% rename from aosp/libavb/src/avb/c/avb_kernel_cmdline_descriptor.c rename to aosp/libavb1.1/src/avb/c/avb_kernel_cmdline_descriptor.c diff --git a/aosp/libavb/src/avb/c/avb_property_descriptor.c b/aosp/libavb1.1/src/avb/c/avb_property_descriptor.c similarity index 100% rename from aosp/libavb/src/avb/c/avb_property_descriptor.c rename to aosp/libavb1.1/src/avb/c/avb_property_descriptor.c diff --git a/aosp/libavb/src/avb/c/avb_rsa.c b/aosp/libavb1.1/src/avb/c/avb_rsa.c similarity index 100% rename from aosp/libavb/src/avb/c/avb_rsa.c rename to aosp/libavb1.1/src/avb/c/avb_rsa.c diff --git a/aosp/libavb/src/avb/c/avb_sha256.c b/aosp/libavb1.1/src/avb/c/avb_sha256.c similarity index 100% rename from aosp/libavb/src/avb/c/avb_sha256.c rename to aosp/libavb1.1/src/avb/c/avb_sha256.c diff --git a/aosp/libavb/src/avb/c/avb_sha512.c b/aosp/libavb1.1/src/avb/c/avb_sha512.c similarity index 100% rename from aosp/libavb/src/avb/c/avb_sha512.c rename to aosp/libavb1.1/src/avb/c/avb_sha512.c diff --git a/aosp/libavb/src/avb/c/avb_slot_verify.c b/aosp/libavb1.1/src/avb/c/avb_slot_verify.c similarity index 100% rename from aosp/libavb/src/avb/c/avb_slot_verify.c rename to aosp/libavb1.1/src/avb/c/avb_slot_verify.c diff --git a/aosp/libavb/src/avb/c/avb_sysdeps_posix.c b/aosp/libavb1.1/src/avb/c/avb_sysdeps_posix.c similarity index 100% rename from aosp/libavb/src/avb/c/avb_sysdeps_posix.c rename to aosp/libavb1.1/src/avb/c/avb_sysdeps_posix.c diff --git a/aosp/libavb/src/avb/c/avb_util.c b/aosp/libavb1.1/src/avb/c/avb_util.c similarity index 100% rename from aosp/libavb/src/avb/c/avb_util.c rename to aosp/libavb1.1/src/avb/c/avb_util.c diff --git a/aosp/libavb/src/avb/c/avb_vbmeta_image.c b/aosp/libavb1.1/src/avb/c/avb_vbmeta_image.c similarity index 100% rename from aosp/libavb/src/avb/c/avb_vbmeta_image.c rename to aosp/libavb1.1/src/avb/c/avb_vbmeta_image.c diff --git a/aosp/libavb/src/avb/c/avb_version.c b/aosp/libavb1.1/src/avb/c/avb_version.c similarity index 100% rename from aosp/libavb/src/avb/c/avb_version.c rename to aosp/libavb1.1/src/avb/c/avb_version.c diff --git a/aosp/libavb/src/avb/headers/avb_chain_partition_descriptor.h b/aosp/libavb1.1/src/avb/headers/avb_chain_partition_descriptor.h similarity index 100% rename from aosp/libavb/src/avb/headers/avb_chain_partition_descriptor.h rename to aosp/libavb1.1/src/avb/headers/avb_chain_partition_descriptor.h diff --git a/aosp/libavb/src/avb/headers/avb_cmdline.h b/aosp/libavb1.1/src/avb/headers/avb_cmdline.h similarity index 100% rename from aosp/libavb/src/avb/headers/avb_cmdline.h rename to aosp/libavb1.1/src/avb/headers/avb_cmdline.h diff --git a/aosp/libavb/src/avb/headers/avb_crypto.h b/aosp/libavb1.1/src/avb/headers/avb_crypto.h similarity index 100% rename from aosp/libavb/src/avb/headers/avb_crypto.h rename to aosp/libavb1.1/src/avb/headers/avb_crypto.h diff --git a/aosp/libavb/src/avb/headers/avb_descriptor.h b/aosp/libavb1.1/src/avb/headers/avb_descriptor.h similarity index 100% rename from aosp/libavb/src/avb/headers/avb_descriptor.h rename to aosp/libavb1.1/src/avb/headers/avb_descriptor.h diff --git a/aosp/libavb/src/avb/headers/avb_footer.h b/aosp/libavb1.1/src/avb/headers/avb_footer.h similarity index 100% rename from aosp/libavb/src/avb/headers/avb_footer.h rename to aosp/libavb1.1/src/avb/headers/avb_footer.h diff --git a/aosp/libavb/src/avb/headers/avb_hash_descriptor.h b/aosp/libavb1.1/src/avb/headers/avb_hash_descriptor.h similarity index 100% rename from aosp/libavb/src/avb/headers/avb_hash_descriptor.h rename to aosp/libavb1.1/src/avb/headers/avb_hash_descriptor.h diff --git a/aosp/libavb/src/avb/headers/avb_hashtree_descriptor.h b/aosp/libavb1.1/src/avb/headers/avb_hashtree_descriptor.h similarity index 100% rename from aosp/libavb/src/avb/headers/avb_hashtree_descriptor.h rename to aosp/libavb1.1/src/avb/headers/avb_hashtree_descriptor.h diff --git a/aosp/libavb/src/avb/headers/avb_kernel_cmdline_descriptor.h b/aosp/libavb1.1/src/avb/headers/avb_kernel_cmdline_descriptor.h similarity index 100% rename from aosp/libavb/src/avb/headers/avb_kernel_cmdline_descriptor.h rename to aosp/libavb1.1/src/avb/headers/avb_kernel_cmdline_descriptor.h diff --git a/aosp/libavb/src/avb/headers/avb_ops.h b/aosp/libavb1.1/src/avb/headers/avb_ops.h similarity index 100% rename from aosp/libavb/src/avb/headers/avb_ops.h rename to aosp/libavb1.1/src/avb/headers/avb_ops.h diff --git a/aosp/libavb/src/avb/headers/avb_property_descriptor.h b/aosp/libavb1.1/src/avb/headers/avb_property_descriptor.h similarity index 100% rename from aosp/libavb/src/avb/headers/avb_property_descriptor.h rename to aosp/libavb1.1/src/avb/headers/avb_property_descriptor.h diff --git a/aosp/libavb/src/avb/headers/avb_rsa.h b/aosp/libavb1.1/src/avb/headers/avb_rsa.h similarity index 100% rename from aosp/libavb/src/avb/headers/avb_rsa.h rename to aosp/libavb1.1/src/avb/headers/avb_rsa.h diff --git a/aosp/libavb/src/avb/headers/avb_sha.h b/aosp/libavb1.1/src/avb/headers/avb_sha.h similarity index 100% rename from aosp/libavb/src/avb/headers/avb_sha.h rename to aosp/libavb1.1/src/avb/headers/avb_sha.h diff --git a/aosp/libavb/src/avb/headers/avb_slot_verify.h b/aosp/libavb1.1/src/avb/headers/avb_slot_verify.h similarity index 100% rename from aosp/libavb/src/avb/headers/avb_slot_verify.h rename to aosp/libavb1.1/src/avb/headers/avb_slot_verify.h diff --git a/aosp/libavb/src/avb/headers/avb_sysdeps.h b/aosp/libavb1.1/src/avb/headers/avb_sysdeps.h similarity index 100% rename from aosp/libavb/src/avb/headers/avb_sysdeps.h rename to aosp/libavb1.1/src/avb/headers/avb_sysdeps.h diff --git a/aosp/libavb/src/avb/headers/avb_util.h b/aosp/libavb1.1/src/avb/headers/avb_util.h similarity index 100% rename from aosp/libavb/src/avb/headers/avb_util.h rename to aosp/libavb1.1/src/avb/headers/avb_util.h diff --git a/aosp/libavb/src/avb/headers/avb_vbmeta_image.h b/aosp/libavb1.1/src/avb/headers/avb_vbmeta_image.h similarity index 100% rename from aosp/libavb/src/avb/headers/avb_vbmeta_image.h rename to aosp/libavb1.1/src/avb/headers/avb_vbmeta_image.h diff --git a/aosp/libavb/src/avb/headers/avb_version.h b/aosp/libavb1.1/src/avb/headers/avb_version.h similarity index 100% rename from aosp/libavb/src/avb/headers/avb_version.h rename to aosp/libavb1.1/src/avb/headers/avb_version.h diff --git a/aosp/libavb/src/avb/headers/libavb.h b/aosp/libavb1.1/src/avb/headers/libavb.h similarity index 100% rename from aosp/libavb/src/avb/headers/libavb.h rename to aosp/libavb1.1/src/avb/headers/libavb.h diff --git a/aosp/libavb1.2/build.gradle b/aosp/libavb1.2/build.gradle new file mode 100644 index 0000000..3c5abcc --- /dev/null +++ b/aosp/libavb1.2/build.gradle @@ -0,0 +1,20 @@ +apply plugin: "c" +apply plugin: "cpp" + +model { + buildTypes { + release + } + + components { + avb(NativeLibrarySpec) { + binaries.all { + cCompiler.define "_FILE_OFFSET_BITS", "64" + cCompiler.define "_POSIX_C_SOURCE", "199309L" + cCompiler.define "AVB_ENABLE_DEBUG" + cCompiler.define "AVB_COMPILATION" + cCompiler.args << "-Wall" << "-g" + } + } + } +} diff --git a/aosp/libavb1.2/src/avb/c/avb_chain_partition_descriptor.c b/aosp/libavb1.2/src/avb/c/avb_chain_partition_descriptor.c new file mode 100644 index 0000000..3f14232 --- /dev/null +++ b/aosp/libavb1.2/src/avb/c/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/aosp/libavb1.2/src/avb/c/avb_cmdline.c b/aosp/libavb1.2/src/avb/c/avb_cmdline.c new file mode 100644 index 0000000..5773a20 --- /dev/null +++ b/aosp/libavb1.2/src/avb/c/avb_cmdline.c @@ -0,0 +1,450 @@ +/* + * 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_cmdline.h" +#include "avb_sha.h" +#include "avb_util.h" +#include "avb_version.h" + +#define NUM_GUIDS 3 + +/* Substitutes all variables (e.g. $(ANDROID_SYSTEM_PARTUUID)) with + * values. Returns NULL on OOM, otherwise the cmdline with values + * replaced. + */ +char* avb_sub_cmdline(AvbOps* ops, + const char* cmdline, + const char* ab_suffix, + bool using_boot_for_vbmeta, + const AvbCmdlineSubstList* additional_substitutions) { + 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; + size_t n; + + /* Special-case for when the top-level vbmeta struct is in the boot + * partition. + */ + if (using_boot_for_vbmeta) { + part_name_str[2] = "boot"; + } + + /* Replace unique partition GUIDs */ + for (n = 0; n < NUM_GUIDS; n++) { + char part_name[AVB_PART_NAME_MAX_SIZE]; + char guid_buf[37]; + + /* Don't attempt to query the partition guid unless its search string is + * present in the command line. Note: the original cmdline is used here, + * not the replaced one. See b/116010959. + */ + if (avb_strstr(cmdline, replace_str[n]) == NULL) { + continue; + } + + 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) { + goto fail; + } else if (io_ret != AVB_IO_RESULT_OK) { + avb_error("Error getting unique GUID for partition.\n"); + goto fail; + } + + if (ret == NULL) { + ret = avb_replace(cmdline, replace_str[n], guid_buf); + } else { + char* new_ret = avb_replace(ret, replace_str[n], guid_buf); + avb_free(ret); + ret = new_ret; + } + if (ret == NULL) { + goto fail; + } + } + + /* It's possible there is no _PARTUUID for replacement above. + * Duplicate cmdline to ret for additional substitutions below. + */ + if (ret == NULL) { + ret = avb_strdup(cmdline); + if (ret == NULL) { + goto fail; + } + } + + /* Replace any additional substitutions. */ + if (additional_substitutions != NULL) { + for (n = 0; n < additional_substitutions->size; ++n) { + char* new_ret = avb_replace(ret, + additional_substitutions->tokens[n], + additional_substitutions->values[n]); + avb_free(ret); + ret = new_ret; + if (ret == NULL) { + goto fail; + } + } + } + + 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_version(AvbSlotVerifyData* slot_data, + const char* key, + uint64_t major_version, + uint64_t minor_version) { + char major_digits[AVB_MAX_DIGITS_UINT64]; + char minor_digits[AVB_MAX_DIGITS_UINT64]; + char combined[AVB_MAX_DIGITS_UINT64 * 2 + 1]; + size_t num_major_digits, num_minor_digits; + + num_major_digits = avb_uint64_to_base10(major_version, major_digits); + num_minor_digits = avb_uint64_to_base10(minor_version, minor_digits); + avb_memcpy(combined, major_digits, num_major_digits); + combined[num_major_digits] = '.'; + avb_memcpy(combined + num_major_digits + 1, minor_digits, num_minor_digits); + combined[num_major_digits + 1 + num_minor_digits] = '\0'; + + return cmdline_append_option(slot_data, key, combined); +} + +static int cmdline_append_uint64_base10(AvbSlotVerifyData* slot_data, + const char* key, + uint64_t value) { + char digits[AVB_MAX_DIGITS_UINT64]; + avb_uint64_to_base10(value, digits); + 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) { + int ret; + char* hex_data = avb_bin2hex(data, data_len); + if (hex_data == NULL) { + return 0; + } + ret = cmdline_append_option(slot_data, key, hex_data); + avb_free(hex_data); + return ret; +} + +AvbSlotVerifyResult avb_append_options( + AvbOps* ops, + AvbSlotVerifyFlags flags, + AvbSlotVerifyData* slot_data, + AvbVBMetaImageHeader* toplevel_vbmeta, + AvbAlgorithmType algorithm_type, + AvbHashtreeErrorMode hashtree_error_mode, + AvbHashtreeErrorMode resolved_hashtree_error_mode) { + AvbSlotVerifyResult ret; + const char* verity_mode; + bool is_device_unlocked; + AvbIOResult io_ret; + + /* Add androidboot.vbmeta.device option... except if not using a vbmeta + * partition since it doesn't make sense in that case. + */ + if (!(flags & AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION)) { + if (!cmdline_append_option(slot_data, + "androidboot.vbmeta.device", + "PARTUUID=$(ANDROID_VBMETA_PARTUUID)")) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto out; + } + } + + /* Add androidboot.vbmeta.avb_version option. */ + if (!cmdline_append_version(slot_data, + "androidboot.vbmeta.avb_version", + AVB_VERSION_MAJOR, + AVB_VERSION_MINOR)) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto out; + } + + /* Set androidboot.avb.device_state to "locked" or "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 out; + } else if (io_ret != AVB_IO_RESULT_OK) { + avb_error("Error getting device state.\n"); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO; + goto out; + } + if (!cmdline_append_option(slot_data, + "androidboot.vbmeta.device_state", + is_device_unlocked ? "unlocked" : "locked")) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto out; + } + + /* 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: { + size_t n, total_size = 0; + uint8_t vbmeta_digest[AVB_SHA256_DIGEST_SIZE]; + avb_slot_verify_data_calculate_vbmeta_digest( + slot_data, AVB_DIGEST_TYPE_SHA256, vbmeta_digest); + for (n = 0; n < slot_data->num_vbmeta_images; n++) { + 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", + vbmeta_digest, + AVB_SHA256_DIGEST_SIZE)) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto out; + } + } break; + /* Explicit fallthrough. */ + case AVB_ALGORITHM_TYPE_SHA512_RSA2048: + case AVB_ALGORITHM_TYPE_SHA512_RSA4096: + case AVB_ALGORITHM_TYPE_SHA512_RSA8192: { + size_t n, total_size = 0; + uint8_t vbmeta_digest[AVB_SHA512_DIGEST_SIZE]; + avb_slot_verify_data_calculate_vbmeta_digest( + slot_data, AVB_DIGEST_TYPE_SHA512, vbmeta_digest); + for (n = 0; n < slot_data->num_vbmeta_images; n++) { + 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", + vbmeta_digest, + AVB_SHA512_DIGEST_SIZE)) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto out; + } + } break; + case _AVB_ALGORITHM_NUM_TYPES: + avb_assert_not_reached(); + break; + } + + /* Set androidboot.veritymode and androidboot.vbmeta.invalidate_on_error */ + if (toplevel_vbmeta->flags & AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED) { + verity_mode = "disabled"; + } else { + const char* dm_verity_mode; + char* new_ret; + + switch (resolved_hashtree_error_mode) { + case AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE: + if (!cmdline_append_option( + slot_data, "androidboot.vbmeta.invalidate_on_error", "yes")) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto out; + } + verity_mode = "enforcing"; + dm_verity_mode = "restart_on_corruption"; + break; + case AVB_HASHTREE_ERROR_MODE_RESTART: + verity_mode = "enforcing"; + dm_verity_mode = "restart_on_corruption"; + break; + case AVB_HASHTREE_ERROR_MODE_EIO: + verity_mode = "eio"; + /* For now there's no option to specify the EIO mode. So + * just use 'ignore_zero_blocks' since that's already set + * and dm-verity-target.c supports specifying this multiple + * times. + */ + dm_verity_mode = "ignore_zero_blocks"; + break; + case AVB_HASHTREE_ERROR_MODE_LOGGING: + verity_mode = "logging"; + dm_verity_mode = "ignore_corruption"; + break; + case AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO: + // Should never get here because MANAGED_RESTART_AND_EIO is + // remapped by avb_manage_hashtree_error_mode(). + avb_assert_not_reached(); + break; + } + new_ret = avb_replace( + slot_data->cmdline, "$(ANDROID_VERITY_MODE)", dm_verity_mode); + avb_free(slot_data->cmdline); + slot_data->cmdline = new_ret; + if (slot_data->cmdline == NULL) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto out; + } + } + if (!cmdline_append_option( + slot_data, "androidboot.veritymode", verity_mode)) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto out; + } + if (hashtree_error_mode == AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO) { + if (!cmdline_append_option( + slot_data, "androidboot.veritymode.managed", "yes")) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto out; + } + } + + ret = AVB_SLOT_VERIFY_RESULT_OK; + +out: + + return ret; +} + +AvbCmdlineSubstList* avb_new_cmdline_subst_list() { + return (AvbCmdlineSubstList*)avb_calloc(sizeof(AvbCmdlineSubstList)); +} + +void avb_free_cmdline_subst_list(AvbCmdlineSubstList* cmdline_subst) { + size_t i; + for (i = 0; i < cmdline_subst->size; ++i) { + avb_free(cmdline_subst->tokens[i]); + avb_free(cmdline_subst->values[i]); + } + cmdline_subst->size = 0; + avb_free(cmdline_subst); +} + +AvbSlotVerifyResult avb_add_root_digest_substitution( + const char* part_name, + const uint8_t* digest, + size_t digest_size, + AvbCmdlineSubstList* out_cmdline_subst) { + const char* kDigestSubPrefix = "$(AVB_"; + const char* kDigestSubSuffix = "_ROOT_DIGEST)"; + size_t part_name_len = avb_strlen(part_name); + size_t list_index = out_cmdline_subst->size; + + avb_assert(part_name_len < AVB_PART_NAME_MAX_SIZE); + avb_assert(digest_size <= AVB_SHA512_DIGEST_SIZE); + if (part_name_len >= AVB_PART_NAME_MAX_SIZE || + digest_size > AVB_SHA512_DIGEST_SIZE) { + return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; + } + + if (out_cmdline_subst->size >= AVB_MAX_NUM_CMDLINE_SUBST) { + /* The list is full. Currently dynamic growth of this list is not supported. + */ + return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; + } + + /* Construct the token to replace in the command line based on the partition + * name. For partition 'foo', this will be '$(AVB_FOO_ROOT_DIGEST)'. + */ + out_cmdline_subst->tokens[list_index] = + avb_strdupv(kDigestSubPrefix, part_name, kDigestSubSuffix, NULL); + if (out_cmdline_subst->tokens[list_index] == NULL) { + goto fail; + } + avb_uppercase(out_cmdline_subst->tokens[list_index]); + + /* The digest value is hex encoded when inserted in the command line. */ + out_cmdline_subst->values[list_index] = avb_bin2hex(digest, digest_size); + if (out_cmdline_subst->values[list_index] == NULL) { + goto fail; + } + + out_cmdline_subst->size++; + return AVB_SLOT_VERIFY_RESULT_OK; + +fail: + if (out_cmdline_subst->tokens[list_index]) { + avb_free(out_cmdline_subst->tokens[list_index]); + } + if (out_cmdline_subst->values[list_index]) { + avb_free(out_cmdline_subst->values[list_index]); + } + return AVB_SLOT_VERIFY_RESULT_ERROR_OOM; +} diff --git a/aosp/libavb1.2/src/avb/c/avb_crc32.c b/aosp/libavb1.2/src/avb/c/avb_crc32.c new file mode 100644 index 0000000..7d4cb09 --- /dev/null +++ b/aosp/libavb1.2/src/avb/c/avb_crc32.c @@ -0,0 +1,114 @@ +/*- + * 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" +#include "avb_util.h" + +/* Code taken from FreeBSD 8 */ + +static uint32_t iavb_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 iavb_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 = iavb_crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8); + return crc ^ ~0U; +} + +uint32_t avb_crc32(const uint8_t* buf, size_t size) { + return iavb_crc32(0, buf, size); +} diff --git a/aosp/libavb1.2/src/avb/c/avb_crypto.c b/aosp/libavb1.2/src/avb/c/avb_crypto.c new file mode 100644 index 0000000..a99ff80 --- /dev/null +++ b/aosp/libavb1.2/src/avb/c/avb_crypto.c @@ -0,0 +1,372 @@ +/* + * 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 ((size_t)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/aosp/libavb1.2/src/avb/c/avb_descriptor.c b/aosp/libavb1.2/src/avb/c/avb_descriptor.c new file mode 100644 index 0000000..cfc2aac --- /dev/null +++ b/aosp/libavb1.2/src/avb/c/avb_descriptor.c @@ -0,0 +1,167 @@ +/* + * 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 = 0; + if (!avb_safe_add(&nb_total, sizeof(AvbDescriptor), nb_following)) { + avb_error("Invalid descriptor length.\n"); + goto out; + } + + 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; + } + + if (!avb_safe_add_to((uint64_t*)(&p), nb_total)) { + avb_error("Invalid descriptor length.\n"); + goto out; + } + } + + 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/aosp/libavb1.2/src/avb/c/avb_footer.c b/aosp/libavb1.2/src/avb/c/avb_footer.c new file mode 100644 index 0000000..b8b8211 --- /dev/null +++ b/aosp/libavb1.2/src/avb/c/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_VERSION_MAJOR) { + avb_error("No support for footer version.\n"); + return false; + } + + return true; +} diff --git a/aosp/libavb1.2/src/avb/c/avb_hash_descriptor.c b/aosp/libavb1.2/src/avb/c/avb_hash_descriptor.c new file mode 100644 index 0000000..3a6b8c8 --- /dev/null +++ b/aosp/libavb1.2/src/avb/c/avb_hash_descriptor.c @@ -0,0 +1,62 @@ +/* + * 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); + dest->flags = avb_be32toh(dest->flags); + + /* 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/aosp/libavb1.2/src/avb/c/avb_hashtree_descriptor.c b/aosp/libavb1.2/src/avb/c/avb_hashtree_descriptor.c new file mode 100644 index 0000000..0822458 --- /dev/null +++ b/aosp/libavb1.2/src/avb/c/avb_hashtree_descriptor.c @@ -0,0 +1,70 @@ +/* + * 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); + dest->flags = avb_be32toh(dest->flags); + + /* 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/aosp/libavb1.2/src/avb/c/avb_kernel_cmdline_descriptor.c b/aosp/libavb1.2/src/avb/c/avb_kernel_cmdline_descriptor.c new file mode 100644 index 0000000..67521f2 --- /dev/null +++ b/aosp/libavb1.2/src/avb/c/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/aosp/libavb1.2/src/avb/c/avb_property_descriptor.c b/aosp/libavb1.2/src/avb/c/avb_property_descriptor.c new file mode 100644 index 0000000..7eba2c0 --- /dev/null +++ b/aosp/libavb1.2/src/avb/c/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/aosp/libavb1.2/src/avb/c/avb_rsa.c b/aosp/libavb1.2/src/avb/c/avb_rsa.c new file mode 100644 index 0000000..f4cb322 --- /dev/null +++ b/aosp/libavb1.2/src/avb/c/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 IAvbKey { + 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) */ +} IAvbKey; + +static IAvbKey* iavb_parse_key_data(const uint8_t* data, size_t length) { + AvbRSAPublicKeyHeader h; + IAvbKey* 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 = (IAvbKey*)(avb_malloc(sizeof(IAvbKey) + 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(IAvbKey) 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; +} + +static void iavb_free_parsed_key(IAvbKey* key) { + avb_free(key); +} + +/* a[] -= mod */ +static void subM(const IAvbKey* 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 IAvbKey* 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 IAvbKey* 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 IAvbKey* 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 IAvbKey* 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; + IAvbKey* parsed_key = NULL; + bool success = false; + + if (key == NULL || sig == NULL || hash == NULL || padding == NULL) { + avb_error("Invalid input.\n"); + goto out; + } + + parsed_key = iavb_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) { + iavb_free_parsed_key(parsed_key); + } + if (buf != NULL) { + avb_free(buf); + } + return success; +} diff --git a/aosp/libavb1.2/src/avb/c/avb_sha256.c b/aosp/libavb1.2/src/avb/c/avb_sha256.c new file mode 100644 index 0000000..8a1c3ae --- /dev/null +++ b/aosp/libavb1.2/src/avb/c/avb_sha256.c @@ -0,0 +1,402 @@ +/* SHA-256 and SHA-512 implementation based on code by Oliver Gay + * 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 + * 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 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 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, + size_t block_nb) { + uint32_t w[64]; + uint32_t wv[8]; + uint32_t t1, t2; + const unsigned char* sub_block; + size_t i; + +#ifndef UNROLL_LOOPS + size_t j; +#endif + + for (i = 0; i < 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, size_t len) { + size_t block_nb; + size_t 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) { + size_t block_nb; + size_t pm_len; + uint64_t len_b; +#ifndef UNROLL_LOOPS + size_t 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; + UNPACK64(len_b, ctx->block + pm_len - 8); + + 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/aosp/libavb1.2/src/avb/c/avb_sha512.c b/aosp/libavb1.2/src/avb/c/avb_sha512.c new file mode 100644 index 0000000..6cdc494 --- /dev/null +++ b/aosp/libavb1.2/src/avb/c/avb_sha512.c @@ -0,0 +1,388 @@ +/* SHA-256 and SHA-512 implementation based on code by Oliver Gay + * 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 + * 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, + size_t block_nb) { + uint64_t w[80]; + uint64_t wv[8]; + uint64_t t1, t2; + const uint8_t* sub_block; + size_t i, j; + + for (i = 0; i < 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, size_t len) { + size_t block_nb; + size_t 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) { + size_t block_nb; + size_t pm_len; + uint64_t len_b; + +#ifndef UNROLL_LOOPS_SHA512 + size_t 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; + UNPACK64(len_b, ctx->block + pm_len - 8); + + 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/aosp/libavb1.2/src/avb/c/avb_slot_verify.c b/aosp/libavb1.2/src/avb/c/avb_slot_verify.c new file mode 100644 index 0000000..fc4c564 --- /dev/null +++ b/aosp/libavb1.2/src/avb/c/avb_slot_verify.c @@ -0,0 +1,1746 @@ +/* + * 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_cmdline.h" +#include "avb_footer.h" +#include "avb_hash_descriptor.h" +#include "avb_hashtree_descriptor.h" +#include "avb_kernel_cmdline_descriptor.h" +#include "avb_sha.h" +#include "avb_util.h" +#include "avb_vbmeta_image.h" +#include "avb_version.h" + +/* 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) + +static AvbSlotVerifyResult initialize_persistent_digest( + AvbOps* ops, + const char* part_name, + const char* persistent_value_name, + size_t digest_size, + const uint8_t* initial_digest, + uint8_t* out_digest); + +/* 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: + case AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION: + case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT: + 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_full_partition(AvbOps* ops, + const char* part_name, + uint64_t image_size, + uint8_t** out_image_buf, + bool* out_image_preloaded) { + size_t part_num_read; + AvbIOResult io_ret; + + /* Make sure that we do not overwrite existing data. */ + avb_assert(*out_image_buf == NULL); + avb_assert(!*out_image_preloaded); + + /* We are going to implicitly cast image_size from uint64_t to size_t in the + * following code, so we need to make sure that the cast is safe. */ + if (image_size != (size_t)(image_size)) { + avb_errorv(part_name, ": Partition size too large to load.\n", NULL); + return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; + } + + /* Try use a preloaded one. */ + if (ops->get_preloaded_partition != NULL) { + io_ret = ops->get_preloaded_partition( + ops, part_name, image_size, out_image_buf, &part_num_read); + if (io_ret == AVB_IO_RESULT_ERROR_OOM) { + return AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + } else if (io_ret != AVB_IO_RESULT_OK) { + avb_errorv(part_name, ": Error loading data from partition.\n", NULL); + return AVB_SLOT_VERIFY_RESULT_ERROR_IO; + } + + if (*out_image_buf != NULL) { + *out_image_preloaded = true; + if (part_num_read != image_size) { + avb_errorv(part_name, ": Read incorrect number of bytes.\n", NULL); + return AVB_SLOT_VERIFY_RESULT_ERROR_IO; + } + } + } + + /* Allocate and copy the partition. */ + if (!*out_image_preloaded) { + *out_image_buf = avb_malloc(image_size); + if (*out_image_buf == NULL) { + return AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + } + + io_ret = ops->read_from_partition(ops, + part_name, + 0 /* offset */, + image_size, + *out_image_buf, + &part_num_read); + if (io_ret == AVB_IO_RESULT_ERROR_OOM) { + return AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + } else if (io_ret != AVB_IO_RESULT_OK) { + avb_errorv(part_name, ": Error loading data from partition.\n", NULL); + return AVB_SLOT_VERIFY_RESULT_ERROR_IO; + } + if (part_num_read != image_size) { + avb_errorv(part_name, ": Read incorrect number of bytes.\n", NULL); + return AVB_SLOT_VERIFY_RESULT_ERROR_IO; + } + } + + return AVB_SLOT_VERIFY_RESULT_OK; +} + +/* Reads a persistent digest stored as a named persistent value corresponding to + * the given |part_name|. The value is returned in |out_digest| which must point + * to |expected_digest_size| bytes. If there is no digest stored for |part_name| + * it can be initialized by providing a non-NULL |initial_digest| of length + * |expected_digest_size|. This automatic initialization will only occur if the + * device is currently locked. The |initial_digest| may be NULL. + * + * Returns AVB_SLOT_VERIFY_RESULT_OK on success, otherwise returns an + * AVB_SLOT_VERIFY_RESULT_ERROR_* error code. + * + * If the value does not exist, is not supported, or is not populated, and + * |initial_digest| is NULL, returns + * AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA. If |expected_digest_size| does + * not match the stored digest size, also returns + * AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA. + */ +static AvbSlotVerifyResult read_persistent_digest(AvbOps* ops, + const char* part_name, + size_t expected_digest_size, + const uint8_t* initial_digest, + uint8_t* out_digest) { + char* persistent_value_name = NULL; + AvbIOResult io_ret = AVB_IO_RESULT_OK; + size_t stored_digest_size = 0; + + if (ops->read_persistent_value == NULL) { + avb_errorv(part_name, ": Persistent values are not implemented.\n", NULL); + return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; + } + persistent_value_name = + avb_strdupv(AVB_NPV_PERSISTENT_DIGEST_PREFIX, part_name, NULL); + if (persistent_value_name == NULL) { + return AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + } + + io_ret = ops->read_persistent_value(ops, + persistent_value_name, + expected_digest_size, + out_digest, + &stored_digest_size); + + // If no such named persistent value exists and an initial digest value was + // given, initialize the named persistent value with the given digest. If + // initialized successfully, this will recurse into this function but with a + // NULL initial_digest. + if (io_ret == AVB_IO_RESULT_ERROR_NO_SUCH_VALUE && initial_digest) { + AvbSlotVerifyResult ret = + initialize_persistent_digest(ops, + part_name, + persistent_value_name, + expected_digest_size, + initial_digest, + out_digest); + avb_free(persistent_value_name); + return ret; + } + avb_free(persistent_value_name); + + if (io_ret == AVB_IO_RESULT_ERROR_OOM) { + return AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + } else if (io_ret == AVB_IO_RESULT_ERROR_NO_SUCH_VALUE) { + // Treat a missing persistent value as a verification error, which is + // ignoreable, rather than a metadata error which is not. + avb_errorv(part_name, ": Persistent digest does not exist.\n", NULL); + return AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION; + } else if (io_ret == AVB_IO_RESULT_ERROR_INVALID_VALUE_SIZE || + io_ret == AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE) { + avb_errorv( + part_name, ": Persistent digest is not of expected size.\n", NULL); + return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; + } else if (io_ret != AVB_IO_RESULT_OK) { + avb_errorv(part_name, ": Error reading persistent digest.\n", NULL); + return AVB_SLOT_VERIFY_RESULT_ERROR_IO; + } else if (expected_digest_size != stored_digest_size) { + avb_errorv( + part_name, ": Persistent digest is not of expected size.\n", NULL); + return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; + } + return AVB_SLOT_VERIFY_RESULT_OK; +} + +static AvbSlotVerifyResult initialize_persistent_digest( + AvbOps* ops, + const char* part_name, + const char* persistent_value_name, + size_t digest_size, + const uint8_t* initial_digest, + uint8_t* out_digest) { + AvbSlotVerifyResult ret; + AvbIOResult io_ret = AVB_IO_RESULT_OK; + bool is_device_unlocked = true; + + io_ret = ops->read_is_device_unlocked(ops, &is_device_unlocked); + if (io_ret == AVB_IO_RESULT_ERROR_OOM) { + return AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + } else if (io_ret != AVB_IO_RESULT_OK) { + avb_error("Error getting device lock state.\n"); + return AVB_SLOT_VERIFY_RESULT_ERROR_IO; + } + + if (is_device_unlocked) { + avb_debugv(part_name, + ": Digest does not exist, device unlocked so not initializing " + "digest.\n", + NULL); + return AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION; + } + + // Device locked; initialize digest with given initial value. + avb_debugv(part_name, + ": Digest does not exist, initializing persistent digest.\n", + NULL); + io_ret = ops->write_persistent_value( + ops, persistent_value_name, digest_size, initial_digest); + if (io_ret == AVB_IO_RESULT_ERROR_OOM) { + return AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + } else if (io_ret != AVB_IO_RESULT_OK) { + avb_errorv(part_name, ": Error initializing persistent digest.\n", NULL); + return AVB_SLOT_VERIFY_RESULT_ERROR_IO; + } + + // To ensure that the digest value was written successfully - and avoid a + // scenario where the digest is simply 'initialized' on every verify - recurse + // into read_persistent_digest to read back the written value. The NULL + // initial_digest ensures that this will not recurse again. + ret = read_persistent_digest(ops, part_name, digest_size, NULL, out_digest); + if (ret != AVB_SLOT_VERIFY_RESULT_OK) { + avb_errorv(part_name, + ": Reading back initialized persistent digest failed!\n", + NULL); + } + return ret; +} + +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[AVB_PART_NAME_MAX_SIZE]; + AvbSlotVerifyResult ret; + AvbIOResult io_ret; + uint8_t* image_buf = NULL; + bool image_preloaded = false; + uint8_t* digest; + size_t digest_len; + const char* found; + uint64_t image_size; + size_t expected_digest_len = 0; + uint8_t expected_digest_buf[AVB_SHA512_DIGEST_SIZE]; + const uint8_t* expected_digest = NULL; + + 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; + } + + /* Don't bother loading or validating unless the partition was + * requested in the first place. + */ + found = avb_strv_find_str(requested_partitions, + (const char*)desc_partition_name, + hash_desc.partition_name_len); + if (found == NULL) { + ret = AVB_SLOT_VERIFY_RESULT_OK; + goto out; + } + + if ((hash_desc.flags & AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB) != 0) { + /* No ab_suffix, just copy the partition name as is. */ + if (hash_desc.partition_name_len >= AVB_PART_NAME_MAX_SIZE) { + avb_error("Partition name does not fit.\n"); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; + goto out; + } + avb_memcpy(part_name, desc_partition_name, hash_desc.partition_name_len); + part_name[hash_desc.partition_name_len] = '\0'; + } else if (hash_desc.digest_len == 0 && avb_strlen(ab_suffix) != 0) { + /* No ab_suffix allowed for partitions without a digest in the descriptor + * because these partitions hold data unique to this device and are not + * updated using an A/B scheme. + */ + avb_error("Cannot use A/B with a persistent digest.\n"); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; + goto out; + } else { + /* Add ab_suffix to the partition name. */ + 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; + } + } + + /* If we're allowing verification errors then hash_desc.image_size + * may no longer match what's in the partition... so in this case + * just load the entire partition. + * + * For example, this can happen if a developer does 'fastboot flash + * boot /path/to/new/and/bigger/boot.img'. We want this to work + * since it's such a common workflow. + */ + image_size = hash_desc.image_size; + if (allow_verification_error) { + io_ret = ops->get_size_of_partition(ops, part_name, &image_size); + 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 determining partition size.\n", NULL); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO; + goto out; + } + avb_debugv(part_name, ": Loading entire partition.\n", NULL); + } + + ret = load_full_partition( + ops, part_name, image_size, &image_buf, &image_preloaded); + if (ret != AVB_SLOT_VERIFY_RESULT_OK) { + goto out; + } + // Although only one of the type might be used, we have to defined the + // structure here so that they would live outside the 'if/else' scope to be + // used later. + AvbSHA256Ctx sha256_ctx; + AvbSHA512Ctx sha512_ctx; + size_t image_size_to_hash = hash_desc.image_size; + // If we allow verification error and the whole partition is smaller than + // image size in hash descriptor, we just hash the whole partition. + if (image_size_to_hash > image_size) { + image_size_to_hash = image_size; + } + if (avb_strcmp((const char*)hash_desc.hash_algorithm, "sha256") == 0) { + avb_sha256_init(&sha256_ctx); + avb_sha256_update(&sha256_ctx, desc_salt, hash_desc.salt_len); + avb_sha256_update(&sha256_ctx, image_buf, image_size_to_hash); + digest = avb_sha256_final(&sha256_ctx); + digest_len = AVB_SHA256_DIGEST_SIZE; + } else if (avb_strcmp((const char*)hash_desc.hash_algorithm, "sha512") == 0) { + avb_sha512_init(&sha512_ctx); + avb_sha512_update(&sha512_ctx, desc_salt, hash_desc.salt_len); + avb_sha512_update(&sha512_ctx, image_buf, image_size_to_hash); + 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 (hash_desc.digest_len == 0) { + /* Expect a match to a persistent digest. */ + avb_debugv(part_name, ": No digest, using persistent digest.\n", NULL); + expected_digest_len = digest_len; + expected_digest = expected_digest_buf; + avb_assert(expected_digest_len <= sizeof(expected_digest_buf)); + /* Pass |digest| as the |initial_digest| so devices not yet initialized get + * initialized to the current partition digest. + */ + ret = read_persistent_digest( + ops, part_name, digest_len, digest, expected_digest_buf); + if (ret != AVB_SLOT_VERIFY_RESULT_OK) { + goto out; + } + } else { + /* Expect a match to the digest in the descriptor. */ + expected_digest_len = hash_desc.digest_len; + expected_digest = desc_digest; + } + + if (digest_len != expected_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, expected_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 it worked and something was loaded, copy to slot_data. */ + if ((ret == AVB_SLOT_VERIFY_RESULT_OK || result_should_continue(ret)) && + image_buf != 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 = image_size; + loaded_partition->data = image_buf; + loaded_partition->preloaded = image_preloaded; + image_buf = NULL; + } + +fail: + if (image_buf != NULL && !image_preloaded) { + avb_free(image_buf); + } + return ret; +} + +static AvbSlotVerifyResult load_requested_partitions( + AvbOps* ops, + const char* const* requested_partitions, + const char* ab_suffix, + AvbSlotVerifyData* slot_data) { + AvbSlotVerifyResult ret; + uint8_t* image_buf = NULL; + bool image_preloaded = false; + size_t n; + + for (n = 0; requested_partitions[n] != NULL; n++) { + char part_name[AVB_PART_NAME_MAX_SIZE]; + AvbIOResult io_ret; + uint64_t image_size; + AvbPartitionData* loaded_partition; + + if (!avb_str_concat(part_name, + sizeof part_name, + requested_partitions[n], + avb_strlen(requested_partitions[n]), + 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; + } + + io_ret = ops->get_size_of_partition(ops, part_name, &image_size); + 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 determining partition size.\n", NULL); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO; + goto out; + } + avb_debugv(part_name, ": Loading entire partition.\n", NULL); + + ret = load_full_partition( + ops, part_name, image_size, &image_buf, &image_preloaded); + if (ret != AVB_SLOT_VERIFY_RESULT_OK) { + goto out; + } + + /* Move to slot_data. */ + 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 out; + } + loaded_partition = + &slot_data->loaded_partitions[slot_data->num_loaded_partitions++]; + loaded_partition->partition_name = avb_strdup(requested_partitions[n]); + if (loaded_partition->partition_name == NULL) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto out; + } + loaded_partition->data_size = image_size; + loaded_partition->data = image_buf; /* Transferring the owner. */ + loaded_partition->preloaded = image_preloaded; + image_buf = NULL; + image_preloaded = false; + } + + ret = AVB_SLOT_VERIFY_RESULT_OK; + +out: + /* Free the current buffer if any. */ + if (image_buf != NULL && !image_preloaded) { + avb_free(image_buf); + } + /* Buffers that are already saved in slot_data will be handled by the caller + * even on failure. */ + return ret; +} + +static AvbSlotVerifyResult load_and_verify_vbmeta( + AvbOps* ops, + const char* const* requested_partitions, + const char* ab_suffix, + AvbSlotVerifyFlags flags, + bool allow_verification_error, + AvbVBMetaImageFlags toplevel_vbmeta_flags, + uint32_t 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, + AvbCmdlineSubstList* out_additional_cmdline_subst) { + char full_partition_name[AVB_PART_NAME_MAX_SIZE]; + AvbSlotVerifyResult ret; + AvbIOResult io_ret; + uint64_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; + bool is_main_vbmeta; + bool look_for_vbmeta_footer; + AvbVBMetaData* vbmeta_image_data = NULL; + + ret = AVB_SLOT_VERIFY_RESULT_OK; + + avb_assert(slot_data != NULL); + + /* Since we allow top-level vbmeta in 'boot', use + * rollback_index_location to determine whether we're the main + * vbmeta struct. + */ + is_main_vbmeta = false; + if (rollback_index_location == 0) { + if ((flags & AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION) == 0) { + is_main_vbmeta = true; + } + } + + /* Don't use footers for vbmeta partitions ('vbmeta' or + * 'vbmeta_'). + */ + look_for_vbmeta_footer = true; + if (avb_strncmp(partition_name, "vbmeta", avb_strlen("vbmeta")) == 0) { + look_for_vbmeta_footer = false; + } + + 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 e.g. system_a. */ + 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; + } + + /* If we're loading from the main vbmeta partition, the vbmeta struct is in + * the beginning. Otherwise we may have to locate it via a footer... if no + * footer is found, we look in the beginning to support e.g. vbmeta_ + * partitions holding data for e.g. super partitions (b/80195851 for + * rationale). + */ + vbmeta_offset = 0; + vbmeta_size = VBMETA_MAX_SIZE; + if (look_for_vbmeta_footer) { + 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_debugv(full_partition_name, ": No footer detected.\n", NULL); + } else { + /* 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); + } else { + 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; + } + + if (vbmeta_offset != 0) { + avb_debugv("Loading vbmeta struct in footer from partition '", + full_partition_name, + "'.\n", + NULL); + } else { + avb_debugv("Loading vbmeta struct from partition '", + full_partition_name, + "'.\n", + NULL); + } + + 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) { + /* If we're looking for 'vbmeta' but there is no such partition, + * go try to get it from the boot partition instead. + */ + if (is_main_vbmeta && io_ret == AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION && + !look_for_vbmeta_footer) { + avb_debugv(full_partition_name, + ": No such partition. Trying 'boot' instead.\n", + NULL); + ret = load_and_verify_vbmeta(ops, + requested_partitions, + ab_suffix, + flags, + allow_verification_error, + 0 /* toplevel_vbmeta_flags */, + 0 /* rollback_index_location */, + "boot", + avb_strlen("boot"), + NULL /* expected_public_key */, + 0 /* expected_public_key_length */, + slot_data, + out_algorithm_type, + out_additional_cmdline_subst); + goto out; + } else { + 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; + + case AVB_VBMETA_VERIFY_RESULT_UNSUPPORTED_VERSION: + /* No way to continue this case. */ + ret = AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION; + avb_errorv(full_partition_name, + ": Error verifying vbmeta image: unsupported AVB version\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; + } + } + + uint32_t rollback_index_location_to_use = rollback_index_location; + if (is_main_vbmeta) { + rollback_index_location_to_use = vbmeta_header.rollback_index_location; + } + + /* 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; + } + + // If we're not using a vbmeta partition, need to use another AvbOps... + if (flags & AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION) { + io_ret = ops->validate_public_key_for_partition( + ops, + full_partition_name, + pk_data, + pk_len, + pk_metadata, + pk_metadata_len, + &key_is_trusted, + &rollback_index_location_to_use); + } else { + 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_to_use, &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 { + if (!(flags & AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION)) { + 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; + + /* If verification has been disabled by setting a bit in the image, + * we're done... except that we need to load the entirety of the + * requested partitions. + */ + if (vbmeta_header.flags & AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED) { + AvbSlotVerifyResult sub_ret; + avb_debugv( + full_partition_name, ": VERIFICATION_DISABLED bit is set.\n", NULL); + /* If load_requested_partitions() fail it is always a fatal + * failure (e.g. ERROR_INVALID_ARGUMENT, ERROR_OOM, etc.) rather + * than recoverable (e.g. one where result_should_continue() + * returns true) and we want to convey that error. + */ + sub_ret = load_requested_partitions( + ops, requested_partitions, ab_suffix, slot_data); + if (sub_ret != AVB_SLOT_VERIFY_RESULT_OK) { + ret = sub_ret; + } + goto out; + } + + /* 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. (Unless the descriptor uses a + * persistent digest, in which case we need to find it). + * + * - 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; + } + + if (chain_desc.rollback_index_location == 0) { + avb_errorv(full_partition_name, + ": Chain partition has invalid " + "rollback_index_location field.\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, + flags, + 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 */ + NULL /* out_additional_cmdline_subst */); + 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: + ' ' + + '\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; + + case AVB_DESCRIPTOR_TAG_HASHTREE: { + AvbHashtreeDescriptor hashtree_desc; + + if (!avb_hashtree_descriptor_validate_and_byteswap( + (AvbHashtreeDescriptor*)descriptors[n], &hashtree_desc)) { + avb_errorv( + full_partition_name, ": Hashtree descriptor is invalid.\n", NULL); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; + goto out; + } + + /* We only need to continue when there is no digest in the descriptor. + * This is because the only processing here is to find the digest and + * make it available on the kernel command line. + */ + if (hashtree_desc.root_digest_len == 0) { + char part_name[AVB_PART_NAME_MAX_SIZE]; + size_t digest_len = 0; + uint8_t digest_buf[AVB_SHA512_DIGEST_SIZE]; + const uint8_t* desc_partition_name = + ((const uint8_t*)descriptors[n]) + sizeof(AvbHashtreeDescriptor); + + if (!avb_validate_utf8(desc_partition_name, + hashtree_desc.partition_name_len)) { + avb_error("Partition name is not valid UTF-8.\n"); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; + goto out; + } + + /* No ab_suffix for partitions without a digest in the descriptor + * because these partitions hold data unique to this device and are + * not updated using an A/B scheme. + */ + if ((hashtree_desc.flags & + AVB_HASHTREE_DESCRIPTOR_FLAGS_DO_NOT_USE_AB) == 0 && + avb_strlen(ab_suffix) != 0) { + avb_error("Cannot use A/B with a persistent root digest.\n"); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; + goto out; + } + if (hashtree_desc.partition_name_len >= AVB_PART_NAME_MAX_SIZE) { + avb_error("Partition name does not fit.\n"); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA; + goto out; + } + avb_memcpy( + part_name, desc_partition_name, hashtree_desc.partition_name_len); + part_name[hashtree_desc.partition_name_len] = '\0'; + + /* Determine the expected digest size from the hash algorithm. */ + if (avb_strcmp((const char*)hashtree_desc.hash_algorithm, "sha1") == + 0) { + digest_len = AVB_SHA1_DIGEST_SIZE; + } else if (avb_strcmp((const char*)hashtree_desc.hash_algorithm, + "sha256") == 0) { + digest_len = AVB_SHA256_DIGEST_SIZE; + } else if (avb_strcmp((const char*)hashtree_desc.hash_algorithm, + "sha512") == 0) { + 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; + } + + ret = read_persistent_digest(ops, + part_name, + digest_len, + NULL /* initial_digest */, + digest_buf); + if (ret != AVB_SLOT_VERIFY_RESULT_OK) { + goto out; + } + + if (out_additional_cmdline_subst) { + ret = + avb_add_root_digest_substitution(part_name, + digest_buf, + digest_len, + out_additional_cmdline_subst); + if (ret != AVB_SLOT_VERIFY_RESULT_OK) { + goto out; + } + } + } + } break; + + case AVB_DESCRIPTOR_TAG_PROPERTY: + /* Do nothing. */ + break; + } + } + + if (rollback_index_location_to_use >= + 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_to_use] = + 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; +} + +static AvbIOResult avb_manage_hashtree_error_mode( + AvbOps* ops, + AvbSlotVerifyFlags flags, + AvbSlotVerifyData* data, + AvbHashtreeErrorMode* out_hashtree_error_mode) { + AvbHashtreeErrorMode ret = AVB_HASHTREE_ERROR_MODE_RESTART; + AvbIOResult io_ret = AVB_IO_RESULT_OK; + uint8_t vbmeta_digest_sha256[AVB_SHA256_DIGEST_SIZE]; + uint8_t stored_vbmeta_digest_sha256[AVB_SHA256_DIGEST_SIZE]; + size_t num_bytes_read; + + avb_assert(out_hashtree_error_mode != NULL); + avb_assert(ops->read_persistent_value != NULL); + avb_assert(ops->write_persistent_value != NULL); + + // If we're rebooting because of dm-verity corruption, make a note of + // the vbmeta hash so we can stay in 'eio' mode until things change. + if (flags & AVB_SLOT_VERIFY_FLAGS_RESTART_CAUSED_BY_HASHTREE_CORRUPTION) { + avb_debug( + "Rebooting because of dm-verity corruption - " + "recording OS instance and using 'eio' mode.\n"); + avb_slot_verify_data_calculate_vbmeta_digest( + data, AVB_DIGEST_TYPE_SHA256, vbmeta_digest_sha256); + io_ret = ops->write_persistent_value(ops, + AVB_NPV_MANAGED_VERITY_MODE, + AVB_SHA256_DIGEST_SIZE, + vbmeta_digest_sha256); + if (io_ret != AVB_IO_RESULT_OK) { + avb_error("Error writing to " AVB_NPV_MANAGED_VERITY_MODE ".\n"); + goto out; + } + ret = AVB_HASHTREE_ERROR_MODE_EIO; + io_ret = AVB_IO_RESULT_OK; + goto out; + } + + // See if we're in 'eio' mode. + io_ret = ops->read_persistent_value(ops, + AVB_NPV_MANAGED_VERITY_MODE, + AVB_SHA256_DIGEST_SIZE, + stored_vbmeta_digest_sha256, + &num_bytes_read); + if (io_ret == AVB_IO_RESULT_ERROR_NO_SUCH_VALUE || + (io_ret == AVB_IO_RESULT_OK && num_bytes_read == 0)) { + // This is the usual case ('eio' mode not set). + avb_debug("No dm-verity corruption - using in 'restart' mode.\n"); + ret = AVB_HASHTREE_ERROR_MODE_RESTART; + io_ret = AVB_IO_RESULT_OK; + goto out; + } else if (io_ret != AVB_IO_RESULT_OK) { + avb_error("Error reading from " AVB_NPV_MANAGED_VERITY_MODE ".\n"); + goto out; + } + if (num_bytes_read != AVB_SHA256_DIGEST_SIZE) { + avb_error( + "Unexpected number of bytes read from " AVB_NPV_MANAGED_VERITY_MODE + ".\n"); + io_ret = AVB_IO_RESULT_ERROR_IO; + goto out; + } + + // OK, so we're currently in 'eio' mode and the vbmeta digest of the OS + // that caused this is in |stored_vbmeta_digest_sha256| ... now see if + // the OS we're dealing with now is the same. + avb_slot_verify_data_calculate_vbmeta_digest( + data, AVB_DIGEST_TYPE_SHA256, vbmeta_digest_sha256); + if (avb_memcmp(vbmeta_digest_sha256, + stored_vbmeta_digest_sha256, + AVB_SHA256_DIGEST_SIZE) == 0) { + // It's the same so we're still in 'eio' mode. + avb_debug("Same OS instance detected - staying in 'eio' mode.\n"); + ret = AVB_HASHTREE_ERROR_MODE_EIO; + io_ret = AVB_IO_RESULT_OK; + } else { + // It did change! + avb_debug( + "New OS instance detected - changing from 'eio' to 'restart' mode.\n"); + io_ret = + ops->write_persistent_value(ops, + AVB_NPV_MANAGED_VERITY_MODE, + 0, // This clears the persistent property. + vbmeta_digest_sha256); + if (io_ret != AVB_IO_RESULT_OK) { + avb_error("Error clearing " AVB_NPV_MANAGED_VERITY_MODE ".\n"); + goto out; + } + ret = AVB_HASHTREE_ERROR_MODE_RESTART; + io_ret = AVB_IO_RESULT_OK; + } + +out: + *out_hashtree_error_mode = ret; + return io_ret; +} + +static bool has_system_partition(AvbOps* ops, const char* ab_suffix) { + char part_name[AVB_PART_NAME_MAX_SIZE]; + char* system_part_name = "system"; + char guid_buf[37]; + AvbIOResult io_ret; + + if (!avb_str_concat(part_name, + sizeof part_name, + system_part_name, + avb_strlen(system_part_name), + ab_suffix, + avb_strlen(ab_suffix))) { + avb_error("System partition name and suffix does not fit.\n"); + return false; + } + + io_ret = ops->get_unique_guid_for_partition( + ops, part_name, guid_buf, sizeof guid_buf); + if (io_ret == AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION) { + avb_debug("No system partition.\n"); + return false; + } else if (io_ret != AVB_IO_RESULT_OK) { + avb_error("Error getting unique GUID for system partition.\n"); + return false; + } + + return true; +} + +AvbSlotVerifyResult avb_slot_verify(AvbOps* ops, + const char* const* requested_partitions, + const char* ab_suffix, + AvbSlotVerifyFlags flags, + AvbHashtreeErrorMode hashtree_error_mode, + AvbSlotVerifyData** out_data) { + AvbSlotVerifyResult ret; + AvbSlotVerifyData* slot_data = NULL; + AvbAlgorithmType algorithm_type = AVB_ALGORITHM_TYPE_NONE; + bool using_boot_for_vbmeta = false; + AvbVBMetaImageHeader toplevel_vbmeta; + bool allow_verification_error = + (flags & AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR); + AvbCmdlineSubstList* additional_cmdline_subst = NULL; + + /* Fail early if we're missing the AvbOps needed for slot verification. */ + avb_assert(ops->read_is_device_unlocked != NULL); + avb_assert(ops->read_from_partition != NULL); + avb_assert(ops->get_size_of_partition != NULL); + avb_assert(ops->read_rollback_index != NULL); + avb_assert(ops->get_unique_guid_for_partition != NULL); + + if (out_data != NULL) { + *out_data = NULL; + } + + /* Allowing dm-verity errors defeats the purpose of verified boot so + * only allow this if set up to allow verification errors + * (e.g. typically only UNLOCKED mode). + */ + if (hashtree_error_mode == AVB_HASHTREE_ERROR_MODE_LOGGING && + !allow_verification_error) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT; + goto fail; + } + + /* Make sure passed-in AvbOps support persistent values if + * asking for libavb to manage verity state. + */ + if (hashtree_error_mode == AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO) { + if (ops->read_persistent_value == NULL || + ops->write_persistent_value == NULL) { + avb_error( + "Persistent values required for " + "AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO " + "but are not implemented in given AvbOps.\n"); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT; + goto fail; + } + } + + /* Make sure passed-in AvbOps support verifying public keys and getting + * rollback index location if not using a vbmeta partition. + */ + if (flags & AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION) { + if (ops->validate_public_key_for_partition == NULL) { + avb_error( + "AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION was passed but the " + "validate_public_key_for_partition() operation isn't implemented.\n"); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT; + goto fail; + } + } else { + avb_assert(ops->validate_vbmeta_public_key != 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; + } + + additional_cmdline_subst = avb_new_cmdline_subst_list(); + if (additional_cmdline_subst == NULL) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto fail; + } + + if (flags & AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION) { + if (requested_partitions == NULL || requested_partitions[0] == NULL) { + avb_fatal( + "Requested partitions cannot be empty when using " + "AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION"); + ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT; + goto fail; + } + + /* No vbmeta partition, go through each of the requested partitions... */ + for (size_t n = 0; requested_partitions[n] != NULL; n++) { + ret = load_and_verify_vbmeta(ops, + requested_partitions, + ab_suffix, + flags, + allow_verification_error, + 0 /* toplevel_vbmeta_flags */, + 0 /* rollback_index_location */, + requested_partitions[n], + avb_strlen(requested_partitions[n]), + NULL /* expected_public_key */, + 0 /* expected_public_key_length */, + slot_data, + &algorithm_type, + additional_cmdline_subst); + if (!allow_verification_error && ret != AVB_SLOT_VERIFY_RESULT_OK) { + goto fail; + } + } + + } else { + /* Usual path, load "vbmeta"... */ + ret = load_and_verify_vbmeta(ops, + requested_partitions, + ab_suffix, + flags, + 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, + additional_cmdline_subst); + if (!allow_verification_error && ret != AVB_SLOT_VERIFY_RESULT_OK) { + goto fail; + } + } + + if (!result_should_continue(ret)) { + goto fail; + } + + /* If things check out, mangle the kernel command-line as needed. */ + if (!(flags & AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION)) { + if (avb_strcmp(slot_data->vbmeta_images[0].partition_name, "vbmeta") != 0) { + avb_assert( + avb_strcmp(slot_data->vbmeta_images[0].partition_name, "boot") == 0); + using_boot_for_vbmeta = true; + } + } + + /* Byteswap top-level vbmeta header since we'll need it below. */ + avb_vbmeta_image_header_to_host_byte_order( + (const AvbVBMetaImageHeader*)slot_data->vbmeta_images[0].vbmeta_data, + &toplevel_vbmeta); + + /* 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; + } + + /* If verification is disabled, we are done ... we specifically + * don't want to add any androidboot.* options since verification + * is disabled. + */ + if (toplevel_vbmeta.flags & AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED) { + /* Since verification is disabled we didn't process any + * descriptors and thus there's no cmdline... so set root= such + * that the system partition is mounted. + */ + avb_assert(slot_data->cmdline == NULL); + // Devices with dynamic partitions won't have system partition. + // Instead, it has a large super partition to accommodate *.img files. + // See b/119551429 for details. + if (has_system_partition(ops, ab_suffix)) { + slot_data->cmdline = + avb_strdup("root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)"); + } else { + // The |cmdline| field should be a NUL-terminated string. + slot_data->cmdline = avb_strdup(""); + } + if (slot_data->cmdline == NULL) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto fail; + } + } else { + /* If requested, manage dm-verity mode... */ + AvbHashtreeErrorMode resolved_hashtree_error_mode = hashtree_error_mode; + if (hashtree_error_mode == + AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO) { + AvbIOResult io_ret; + io_ret = avb_manage_hashtree_error_mode( + ops, flags, slot_data, &resolved_hashtree_error_mode); + if (io_ret != AVB_IO_RESULT_OK) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO; + if (io_ret == AVB_IO_RESULT_ERROR_OOM) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + } + goto fail; + } + } + slot_data->resolved_hashtree_error_mode = resolved_hashtree_error_mode; + + /* Add options... */ + AvbSlotVerifyResult sub_ret; + sub_ret = avb_append_options(ops, + flags, + slot_data, + &toplevel_vbmeta, + algorithm_type, + hashtree_error_mode, + resolved_hashtree_error_mode); + if (sub_ret != AVB_SLOT_VERIFY_RESULT_OK) { + ret = sub_ret; + goto fail; + } + } + + /* Substitute $(ANDROID_SYSTEM_PARTUUID) and friends. */ + if (slot_data->cmdline != NULL && avb_strlen(slot_data->cmdline) != 0) { + char* new_cmdline; + new_cmdline = avb_sub_cmdline(ops, + slot_data->cmdline, + ab_suffix, + using_boot_for_vbmeta, + additional_cmdline_subst); + if (new_cmdline != slot_data->cmdline) { + if (new_cmdline == NULL) { + ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM; + goto fail; + } + avb_free(slot_data->cmdline); + slot_data->cmdline = new_cmdline; + } + } + + if (out_data != NULL) { + *out_data = slot_data; + } else { + avb_slot_verify_data_free(slot_data); + } + + avb_free_cmdline_subst_list(additional_cmdline_subst); + additional_cmdline_subst = NULL; + + 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); + } + if (additional_cmdline_subst != NULL) { + avb_free_cmdline_subst_list(additional_cmdline_subst); + } + 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 && !loaded_partition->preloaded) { + 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; + case AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION: + ret = "ERROR_UNSUPPORTED_VERSION"; + break; + case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT: + ret = "ERROR_INVALID_ARGUMENT"; + break; + /* Do not add a 'default:' case here because of -Wswitch. */ + } + + if (ret == NULL) { + avb_error("Unknown AvbSlotVerifyResult value.\n"); + ret = "(unknown)"; + } + + return ret; +} + +void avb_slot_verify_data_calculate_vbmeta_digest(AvbSlotVerifyData* data, + AvbDigestType digest_type, + uint8_t* out_digest) { + bool ret = false; + size_t n; + + switch (digest_type) { + case AVB_DIGEST_TYPE_SHA256: { + AvbSHA256Ctx ctx; + avb_sha256_init(&ctx); + for (n = 0; n < data->num_vbmeta_images; n++) { + avb_sha256_update(&ctx, + data->vbmeta_images[n].vbmeta_data, + data->vbmeta_images[n].vbmeta_size); + } + avb_memcpy(out_digest, avb_sha256_final(&ctx), AVB_SHA256_DIGEST_SIZE); + ret = true; + } break; + + case AVB_DIGEST_TYPE_SHA512: { + AvbSHA512Ctx ctx; + avb_sha512_init(&ctx); + for (n = 0; n < data->num_vbmeta_images; n++) { + avb_sha512_update(&ctx, + data->vbmeta_images[n].vbmeta_data, + data->vbmeta_images[n].vbmeta_size); + } + avb_memcpy(out_digest, avb_sha512_final(&ctx), AVB_SHA512_DIGEST_SIZE); + ret = true; + } break; + + /* Do not add a 'default:' case here because of -Wswitch. */ + } + + if (!ret) { + avb_fatal("Unknown digest type"); + } +} diff --git a/aosp/libavb1.2/src/avb/c/avb_sysdeps_posix.c b/aosp/libavb1.2/src/avb/c/avb_sysdeps_posix.c new file mode 100644 index 0000000..e26c3ef --- /dev/null +++ b/aosp/libavb1.2/src/avb/c/avb_sysdeps_posix.c @@ -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. + */ + +#include +#include +#include +#include +#include + +#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); +} + +int avb_strncmp(const char* s1, const char* s2, size_t n) { + return strncmp(s1, s2, n); +} + +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); +} + +uint32_t avb_div_by_10(uint64_t* dividend) { + uint32_t rem = (uint32_t)(*dividend % 10); + *dividend /= 10; + return rem; +} diff --git a/aosp/libavb1.2/src/avb/c/avb_util.c b/aosp/libavb1.2/src/avb/c/avb_util.c new file mode 100644 index 0000000..83d526a --- /dev/null +++ b/aosp/libavb1.2/src/avb/c/avb_util.c @@ -0,0 +1,474 @@ +/* + * 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" + +#include + +uint16_t avb_be16toh(uint16_t in) { + uint8_t* d = (uint8_t*)∈ + uint16_t ret; + ret = ((uint16_t)d[0]) << 8; + ret |= ((uint16_t)d[1]); + return ret; +} + +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 16-bit unsigned integer from host to big-endian byte order. */ +uint16_t avb_htobe16(uint16_t in) { + union { + uint16_t word; + uint8_t bytes[2]; + } ret; + ret.bytes[0] = (in >> 8) & 0xff; + ret.bytes[1] = in & 0xff; + return ret.word; +} + +/* 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; + + // Doesn't make sense to pass 0 for buf_size since there's + // no room for the terminating NUL byte. + if (buf_size == 0) { + return false; + } + + 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 (new_str == 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 (new_str == 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; +} + +/* We only support a limited amount of strings in avb_strdupv(). */ +#define AVB_STRDUPV_MAX_NUM_STRINGS 32 + +char* avb_strdupv(const char* str, ...) { + va_list ap; + const char* strings[AVB_STRDUPV_MAX_NUM_STRINGS]; + size_t lengths[AVB_STRDUPV_MAX_NUM_STRINGS]; + size_t num_strings, n; + uint64_t total_length; + char *ret = NULL, *dest; + + num_strings = 0; + total_length = 0; + va_start(ap, str); + do { + size_t str_len = avb_strlen(str); + strings[num_strings] = str; + lengths[num_strings] = str_len; + if (!avb_safe_add_to(&total_length, str_len)) { + avb_fatal("Overflow while determining total length.\n"); + break; + } + num_strings++; + if (num_strings == AVB_STRDUPV_MAX_NUM_STRINGS) { + avb_fatal("Too many strings passed.\n"); + break; + } + str = va_arg(ap, const char*); + } while (str != NULL); + va_end(ap); + + ret = avb_malloc(total_length + 1); + if (ret == NULL) { + goto out; + } + + dest = ret; + for (n = 0; n < num_strings; n++) { + avb_memcpy(dest, strings[n], lengths[n]); + dest += lengths[n]; + } + *dest = '\0'; + avb_assert(dest == ret + total_length); + +out: + return ret; +} + +const char* avb_basename(const char* str) { + int64_t n; + size_t len; + + len = avb_strlen(str); + if (len >= 2) { + for (n = len - 2; n >= 0; n--) { + if (str[n] == '/') { + return str + n + 1; + } + } + } + return str; +} + +void avb_uppercase(char* str) { + size_t i; + for (i = 0; str[i] != '\0'; ++i) { + if (str[i] <= 0x7A && str[i] >= 0x61) { + str[i] -= 0x20; + } + } +} + +char* avb_bin2hex(const uint8_t* data, size_t data_len) { + const char hex_digits[17] = "0123456789abcdef"; + char* hex_data; + size_t n; + + hex_data = avb_malloc(data_len * 2 + 1); + if (hex_data == NULL) { + return NULL; + } + + 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'; + return hex_data; +} + +size_t avb_uint64_to_base10(uint64_t value, + char digits[AVB_MAX_DIGITS_UINT64]) { + char rev_digits[AVB_MAX_DIGITS_UINT64]; + size_t n, num_digits; + + for (num_digits = 0; num_digits < AVB_MAX_DIGITS_UINT64 - 1;) { + rev_digits[num_digits++] = avb_div_by_10(&value) + '0'; + if (value == 0) { + break; + } + } + + for (n = 0; n < num_digits; n++) { + digits[n] = rev_digits[num_digits - 1 - n]; + } + digits[n] = '\0'; + return n; +} diff --git a/aosp/libavb1.2/src/avb/c/avb_vbmeta_image.c b/aosp/libavb1.2/src/avb/c/avb_vbmeta_image.c new file mode 100644 index 0000000..8a6d898 --- /dev/null +++ b/aosp/libavb1.2/src/avb/c/avb_vbmeta_image.c @@ -0,0 +1,310 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "avb_vbmeta_image.h" +#include "avb_crypto.h" +#include "avb_rsa.h" +#include "avb_sha.h" +#include "avb_util.h" +#include "avb_version.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; + } + + /* Before we byteswap or compare Magic, ensure length is long enough. */ + if (length < sizeof(AvbVBMetaImageHeader)) { + avb_error("Length is smaller than header.\n"); + goto out; + } + + /* Ensure magic is correct. */ + if (avb_safe_memcmp(data, AVB_MAGIC, AVB_MAGIC_LEN) != 0) { + avb_error("Magic is incorrect.\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 we do not meet + * the specified minimum version of libavb. + */ + if ((h.required_libavb_version_major != AVB_VERSION_MAJOR) || + (h.required_libavb_version_minor > AVB_VERSION_MINOR)) { + avb_error("Mismatch between image version and libavb version.\n"); + ret = AVB_VBMETA_VERIFY_RESULT_UNSUPPORTED_VERSION; + goto out; + } + + /* Ensure |release_string| ends with a NUL byte. */ + if (h.release_string[AVB_RELEASE_STRING_SIZE - 1] != '\0') { + avb_error("Release string does not end with a NUL byte.\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->required_libavb_version_major = + avb_be32toh(dest->required_libavb_version_major); + dest->required_libavb_version_minor = + avb_be32toh(dest->required_libavb_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); + dest->rollback_index_location = avb_be32toh(dest->rollback_index_location); +} + +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_UNSUPPORTED_VERSION: + ret = "UNSUPPORTED_VERSION"; + 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/aosp/libavb1.2/src/avb/c/avb_version.c b/aosp/libavb1.2/src/avb/c/avb_version.c new file mode 100644 index 0000000..31f5fa6 --- /dev/null +++ b/aosp/libavb1.2/src/avb/c/avb_version.c @@ -0,0 +1,34 @@ +/* + * 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 "avb_version.h" + +#define AVB_QUOTE(str) #str +#define AVB_EXPAND_AND_QUOTE(str) AVB_QUOTE(str) + +/* Keep in sync with get_release_string() in avbtool. */ +const char* avb_version_string(void) { + return AVB_EXPAND_AND_QUOTE(AVB_VERSION_MAJOR) "." AVB_EXPAND_AND_QUOTE( + AVB_VERSION_MINOR) "." AVB_EXPAND_AND_QUOTE(AVB_VERSION_SUB); +} diff --git a/aosp/libavb1.2/src/avb/headers/avb_chain_partition_descriptor.h b/aosp/libavb1.2/src/avb/headers/avb_chain_partition_descriptor.h new file mode 100644 index 0000000..f2c9250 --- /dev/null +++ b/aosp/libavb1.2/src/avb/headers/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/aosp/libavb1.2/src/avb/headers/avb_cmdline.h b/aosp/libavb1.2/src/avb/headers/avb_cmdline.h new file mode 100644 index 0000000..377783f --- /dev/null +++ b/aosp/libavb1.2/src/avb/headers/avb_cmdline.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#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_CMDLINE_H_ +#define AVB_CMDLINE_H_ + +#include "avb_ops.h" +#include "avb_slot_verify.h" + +/* Maximum allow length (in bytes) of a partition name, including + * ab_suffix. + */ +#define AVB_PART_NAME_MAX_SIZE 32 + +#define AVB_MAX_NUM_CMDLINE_SUBST 10 + +/* Holds information about command-line substitutions. */ +typedef struct AvbCmdlineSubstList { + size_t size; + char* tokens[AVB_MAX_NUM_CMDLINE_SUBST]; + char* values[AVB_MAX_NUM_CMDLINE_SUBST]; +} AvbCmdlineSubstList; + +/* Substitutes all variables (e.g. $(ANDROID_SYSTEM_PARTUUID)) with + * values. Returns NULL on OOM, otherwise the cmdline with values + * replaced. + */ +char* avb_sub_cmdline(AvbOps* ops, + const char* cmdline, + const char* ab_suffix, + bool using_boot_for_vbmeta, + const AvbCmdlineSubstList* additional_substitutions); + +AvbSlotVerifyResult avb_append_options( + AvbOps* ops, + AvbSlotVerifyFlags flags, + AvbSlotVerifyData* slot_data, + AvbVBMetaImageHeader* toplevel_vbmeta, + AvbAlgorithmType algorithm_type, + AvbHashtreeErrorMode hashtree_error_mode, + AvbHashtreeErrorMode resolved_hashtree_error_mode); + +/* Allocates and initializes a new command line substitution list. Free with + * |avb_free_cmdline_subst_list|. + */ +AvbCmdlineSubstList* avb_new_cmdline_subst_list(void); + +/* Use this instead of |avb_free| to deallocate a AvbCmdlineSubstList. */ +void avb_free_cmdline_subst_list(AvbCmdlineSubstList* cmdline_subst); + +/* Adds a hashtree root digest to be substituted in $(AVB_*_ROOT_DIGEST) + * variables. The partition name differentiates the variable. For example, if + * |part_name| is "foo" then $(AVB_FOO_ROOT_DIGEST) will be substituted with the + * hex encoding of the digest. The substitution will be added to + * |out_cmdline_subst|. Returns AVB_SLOT_VERIFY_RESULT_OK on success. + */ +AvbSlotVerifyResult avb_add_root_digest_substitution( + const char* part_name, + const uint8_t* digest, + size_t digest_size, + AvbCmdlineSubstList* out_cmdline_subst); + +#endif diff --git a/aosp/libavb1.2/src/avb/headers/avb_crypto.h b/aosp/libavb1.2/src/avb/headers/avb_crypto.h new file mode 100644 index 0000000..30c1e4b --- /dev/null +++ b/aosp/libavb1.2/src/avb/headers/avb_crypto.h @@ -0,0 +1,174 @@ +/* + * 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-1 digest. */ +#define AVB_SHA1_DIGEST_SIZE 20 + +/* 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 + +/* Possible digest types supported by libavb routines. */ +typedef enum { + AVB_DIGEST_TYPE_SHA256, + AVB_DIGEST_TYPE_SHA512, +} AvbDigestType; + +/* 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/aosp/libavb1.2/src/avb/headers/avb_descriptor.h b/aosp/libavb1.2/src/avb/headers/avb_descriptor.h new file mode 100644 index 0000000..5d0f0c6 --- /dev/null +++ b/aosp/libavb1.2/src/avb/headers/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/aosp/libavb1.2/src/avb/headers/avb_footer.h b/aosp/libavb1.2/src/avb/headers/avb_footer.h new file mode 100644 index 0000000..e84826f --- /dev/null +++ b/aosp/libavb1.2/src/avb/headers/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 footer version used - keep in sync with avbtool. */ +#define AVB_FOOTER_VERSION_MAJOR 1 +#define AVB_FOOTER_VERSION_MINOR 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/aosp/libavb1.2/src/avb/headers/avb_hash_descriptor.h b/aosp/libavb1.2/src/avb/headers/avb_hash_descriptor.h new file mode 100644 index 0000000..9ee8997 --- /dev/null +++ b/aosp/libavb1.2/src/avb/headers/avb_hash_descriptor.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_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 + +/* Flags for hash descriptors. + * + * AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB: Do not apply the default A/B + * partition logic to this partition. This is intentionally a negative boolean + * because A/B should be both the default and most used in practice. + */ +typedef enum { + AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB = (1 << 0), +} AvbHashDescriptorFlags; + +/* 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. + * + * Changes in v1.1: + * - flags field is added which supports AVB_HASH_DESCRIPTOR_FLAGS_USE_AB + * - digest_len may be zero, which indicates the use of a persistent digest + */ +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; + uint32_t flags; + uint8_t reserved[60]; +} 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/aosp/libavb1.2/src/avb/headers/avb_hashtree_descriptor.h b/aosp/libavb1.2/src/avb/headers/avb_hashtree_descriptor.h new file mode 100644 index 0000000..d0f7e2c --- /dev/null +++ b/aosp/libavb1.2/src/avb/headers/avb_hashtree_descriptor.h @@ -0,0 +1,98 @@ +/* + * 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 + +/* Flags for hashtree descriptors. + * + * AVB_HASHTREE_DESCRIPTOR_FLAGS_DO_NOT_USE_AB: Do not apply the default A/B + * partition logic to this partition. This is intentionally a negative boolean + * because A/B should be both the default and most used in practice. + */ +typedef enum { + AVB_HASHTREE_DESCRIPTOR_FLAGS_DO_NOT_USE_AB = (1 << 0), +} AvbHashtreeDescriptorFlags; + +/* 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. + * + * Changes in v1.1: + * - flags field is added which supports AVB_HASHTREE_DESCRIPTOR_FLAGS_USE_AB + * - digest_len may be zero, which indicates the use of a persistent digest + */ +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; + uint32_t flags; + uint8_t reserved[60]; +} 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/aosp/libavb1.2/src/avb/headers/avb_kernel_cmdline_descriptor.h b/aosp/libavb1.2/src/avb/headers/avb_kernel_cmdline_descriptor.h new file mode 100644 index 0000000..6908b3b --- /dev/null +++ b/aosp/libavb1.2/src/avb/headers/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/aosp/libavb1.2/src/avb/headers/avb_ops.h b/aosp/libavb1.2/src/avb/headers/avb_ops.h new file mode 100644 index 0000000..dec163d --- /dev/null +++ b/aosp/libavb1.2/src/avb/headers/avb_ops.h @@ -0,0 +1,340 @@ +/* + * 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 + +/* Well-known names of named persistent values. */ +#define AVB_NPV_PERSISTENT_DIGEST_PREFIX "avb.persistent_digest." +#define AVB_NPV_MANAGED_VERITY_MODE "avb.managed_verity_mode" + +/* 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. + * + * AVB_IO_RESULT_ERROR_NO_SUCH_VALUE is returned if a named persistent value + * does not exist. + * + * AVB_IO_RESULT_ERROR_INVALID_VALUE_SIZE is returned if a named persistent + * value size is not supported or does not match the expected size. + * + * AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE is returned if a buffer is too small + * for the requested operation. + */ +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, + AVB_IO_RESULT_ERROR_NO_SUCH_VALUE, + AVB_IO_RESULT_ERROR_INVALID_VALUE_SIZE, + AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE, +} 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. + * + * Operations may be added in the future so when implementing it + * always make sure to zero out sizeof(AvbOps) bytes of the struct to + * ensure that unimplemented operations are set to NULL. + */ +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); + + /* Gets the starting pointer of a partition that is pre-loaded in memory, and + * save it to |out_pointer|. The preloaded partition is expected to be + * |num_bytes|, where the actual preloaded byte count is returned in + * |out_num_bytes_preloaded|. |out_num_bytes_preloaded| must be no larger than + * |num_bytes|. + * + * This provides an alternative way to access a partition that is preloaded + * into memory without a full memory copy. When this function pointer is not + * set (has value NULL), or when the |out_pointer| is set to NULL as a result, + * |read_from_partition| will be used as the fallback. This function is mainly + * used for accessing the entire partition content to calculate its hash. + * + * Preloaded partition data must outlive the lifespan of the + * |AvbSlotVerifyData| structure that |avb_slot_verify| outputs. + */ + AvbIOResult (*get_preloaded_partition)(AvbOps* ops, + const char* partition, + size_t num_bytes, + uint8_t** out_pointer, + size_t* out_num_bytes_preloaded); + + /* 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. + * + * NOTE: If AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION is passed to + * avb_slot_verify() then this operation is never used. Instead, the + * validate_public_key_for_partition() operation is used + */ + 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); + + /* Gets the size of a partition with the name in |partition| + * (NUL-terminated UTF-8 string). Returns the value in + * |out_size_num_bytes|. + * + * If the partition doesn't exist the AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION + * error code should be returned. + * + * Returns AVB_IO_RESULT_OK on success, otherwise an error code. + */ + AvbIOResult (*get_size_of_partition)(AvbOps* ops, + const char* partition, + uint64_t* out_size_num_bytes); + + /* Reads a persistent value corresponding to the given |name|. The value is + * returned in |out_buffer| which must point to |buffer_size| bytes. On + * success |out_num_bytes_read| contains the number of bytes read into + * |out_buffer|. If AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE is returned, + * |out_num_bytes_read| contains the number of bytes that would have been read + * which can be used to allocate a buffer. + * + * The |buffer_size| may be zero and the |out_buffer| may be NULL, but if + * |out_buffer| is NULL then |buffer_size| *must* be zero. + * + * Returns AVB_IO_RESULT_OK on success, otherwise an error code. + * + * If the value does not exist, is not supported, or is not populated, returns + * AVB_IO_RESULT_ERROR_NO_SUCH_VALUE. If |buffer_size| is smaller than the + * size of the stored value, returns AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE. + * + * This operation is currently only used to support persistent digests or the + * AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO hashtree error mode. If a + * device does not use one of these features this function pointer can be set + * to NULL. + */ + AvbIOResult (*read_persistent_value)(AvbOps* ops, + const char* name, + size_t buffer_size, + uint8_t* out_buffer, + size_t* out_num_bytes_read); + + /* Writes a persistent value corresponding to the given |name|. The value is + * supplied in |value| which must point to |value_size| bytes. Any existing + * value with the same name is overwritten. If |value_size| is zero, future + * calls to |read_persistent_value| will return + * AVB_IO_RESULT_ERROR_NO_SUCH_VALUE. + * + * Returns AVB_IO_RESULT_OK on success, otherwise an error code. + * + * If the value |name| is not supported, returns + * AVB_IO_RESULT_ERROR_NO_SUCH_VALUE. If the |value_size| is not supported, + * returns AVB_IO_RESULT_ERROR_INVALID_VALUE_SIZE. + * + * This operation is currently only used to support persistent digests or the + * AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO hashtree error mode. If a + * device does not use one of these features this function pointer can be set + * to NULL. + */ + AvbIOResult (*write_persistent_value)(AvbOps* ops, + const char* name, + size_t value_size, + const uint8_t* value); + + /* Like validate_vbmeta_public_key() but for when the flag + * AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION is being used. The name of the + * partition to get the public key for is passed in |partition_name|. + * + * Also returns the rollback index location to use for the partition, in + * |out_rollback_index_location|. + * + * Returns AVB_IO_RESULT_OK on success, otherwise an error code. + */ + AvbIOResult (*validate_public_key_for_partition)( + AvbOps* ops, + const char* partition, + 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, + uint32_t* out_rollback_index_location); +}; + +#ifdef __cplusplus +} +#endif + +#endif /* AVB_OPS_H_ */ diff --git a/aosp/libavb1.2/src/avb/headers/avb_property_descriptor.h b/aosp/libavb1.2/src/avb/headers/avb_property_descriptor.h new file mode 100644 index 0000000..85b0163 --- /dev/null +++ b/aosp/libavb1.2/src/avb/headers/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 encoded + * as UTF-8, 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/aosp/libavb1.2/src/avb/headers/avb_rsa.h b/aosp/libavb1.2/src/avb/headers/avb_rsa.h new file mode 100644 index 0000000..c2dcf47 --- /dev/null +++ b/aosp/libavb1.2/src/avb/headers/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/aosp/libavb1.2/src/avb/headers/avb_sha.h b/aosp/libavb1.2/src/avb/headers/avb_sha.h new file mode 100644 index 0000000..82ac9a5 --- /dev/null +++ b/aosp/libavb1.2/src/avb/headers/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]; + uint64_t tot_len; + size_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]; + uint64_t tot_len; + size_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, size_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, size_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/aosp/libavb1.2/src/avb/headers/avb_slot_verify.h b/aosp/libavb1.2/src/avb/headers/avb_slot_verify.h new file mode 100644 index 0000000..a2c98f4 --- /dev/null +++ b/aosp/libavb1.2/src/avb/headers/avb_slot_verify.h @@ -0,0 +1,400 @@ +/* + * 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, + AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION, + AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT +} AvbSlotVerifyResult; + +/* Various error handling modes for when verification fails using a + * hashtree at runtime inside the HLOS. + * + * AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE means that the OS + * will invalidate the current slot and restart. + * + * AVB_HASHTREE_ERROR_MODE_RESTART means that the OS will restart. + * + * AVB_HASHTREE_ERROR_MODE_EIO means that an EIO error will be + * returned to applications. + * + * AVB_HASHTREE_ERROR_MODE_LOGGING means that errors will be logged + * and corrupt data may be returned to applications. This mode should + * be used ONLY for diagnostics and debugging. It cannot be used + * unless AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR is also + * used. + * + * AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO means that either + * AVB_HASHTREE_ERROR_MODE_RESTART or AVB_HASHTREE_ERROR_MODE_EIO is used + * depending on state. This mode implements a state machine whereby + * AVB_HASHTREE_ERROR_MODE_RESTART is used by default and when + * AVB_SLOT_VERIFY_FLAGS_RESTART_CAUSED_BY_HASHTREE_CORRUPTION is passed the + * mode transitions to AVB_HASHTREE_ERROR_MODE_EIO. When a new OS has been + * detected the device transitions back to the AVB_HASHTREE_ERROR_MODE_RESTART + * mode. To do this persistent storage is needed - specifically this means that + * the passed in AvbOps will need to have the read_persistent_value() and + * write_persistent_value() operations implemented. The name of the persistent + * value used is "avb.managed_verity_mode" and 32 bytes of storage is needed. + */ +typedef enum { + AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, + AVB_HASHTREE_ERROR_MODE_RESTART, + AVB_HASHTREE_ERROR_MODE_EIO, + AVB_HASHTREE_ERROR_MODE_LOGGING, + AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO +} AvbHashtreeErrorMode; + +/* Flags that influence how avb_slot_verify() works. + * + * If AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR is NOT set then + * avb_slot_verify() 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 AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR is set + * avb_slot_verify() 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, if AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR is set the + * contents loaded from |requested_partition| will be the contents of + * the entire partition instead of just the size specified in the hash + * descriptor. + * + * The AVB_SLOT_VERIFY_FLAGS_RESTART_CAUSED_BY_HASHTREE_CORRUPTION flag + * should be set if using AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO + * and the reason the boot loader is running is because the device + * was restarted by the dm-verity driver. + * + * If the AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION flag is set then + * data won't be loaded from the "vbmeta" partition and the + * |validate_vbmeta_public_key| operation is never called. Instead, the + * vbmeta structs in |requested_partitions| are loaded and processed and the + * |validate_public_key_for_partition| operation is called for each of these + * vbmeta structs. This flag is useful when booting into recovery on a device + * not using A/B - see section "Booting into recovery" in README.md for + * more information. + */ +typedef enum { + AVB_SLOT_VERIFY_FLAGS_NONE = 0, + AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR = (1 << 0), + AVB_SLOT_VERIFY_FLAGS_RESTART_CAUSED_BY_HASHTREE_CORRUPTION = (1 << 1), + AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION = (1 << 2), +} AvbSlotVerifyFlags; + +/* 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. If |preloaded| is set to true, + * this structure dose not own |data|. The caller of |avb_slot_verify| + * needs to make sure that the preloaded data outlives this + * |AvbPartitionData| structure. + * + * 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; + bool preloaded; +} 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. This + * value includes the leading underscore - typical values are "" (if + * no slots are in use), "_a" (for the first slot), and "_b" (for the + * second 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 partition with the + * top-level vbmeta struct. This is usually the "vbmeta" partition in + * the requested slot but if there is no "vbmeta" partition it can + * also be the "boot" partition. + * + * 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 + * $(ANDROID_VERITY_MODE) will be replaced with the proper dm-verity + * option depending on the value of |hashtree_error_mode|. + * + * Additionally, the |cmdline| field will have the following kernel + * command-line options set (unless verification is disabled, see + * below): + * + * androidboot.veritymode: This is set to 'disabled' if the + * AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED flag is set in top-level + * vbmeta struct. Otherwise it is set to 'enforcing' if the + * passed-in hashtree error mode is AVB_HASHTREE_ERROR_MODE_RESTART + * or AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, 'eio' if it's + * set to AVB_HASHTREE_ERROR_MODE_EIO, and 'logging' if it's set to + * AVB_HASHTREE_ERROR_MODE_LOGGING. + * + * androidboot.veritymode.managed: This is set to 'yes' only + * if hashtree validation isn't disabled and the passed-in hashtree + * error mode is AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO. + * + * androidboot.vbmeta.invalidate_on_error: This is set to 'yes' only + * if hashtree validation isn't disabled and the passed-in hashtree + * error mode is AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE. + * + * androidboot.vbmeta.device_state: set to "locked" or "unlocked" + * depending on the result of the result of AvbOps's + * read_is_unlocked() function. + * + * 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. If there is no vbmeta partition it will point to the boot + * partition of the verified slot. If the flag + * AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION is used, this is not + * set. + * + * androidboot.vbmeta.avb_version: This is set to the decimal value + * of AVB_VERSION_MAJOR followed by a dot followed by the decimal + * value of AVB_VERSION_MINOR, for example "1.0" or "1.4". This + * version number represents the vbmeta file format version + * supported by libavb copy used in the boot loader. This is not + * necessarily the same version number of the on-disk metadata for + * the slot that was verified. + * + * Note that androidboot.slot_suffix is not set in the |cmdline| field + * in |AvbSlotVerifyData| - you will have to set this yourself. + * + * If the |AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED| flag is set + * in the top-level vbmeta struct then only the top-level vbmeta + * struct is verified and descriptors will not processed. The return + * value will be set accordingly (if this flag is set via 'avbctl + * disable-verification' then the return value will be + * |AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION|) and + * |AvbSlotVerifyData| is returned. Additionally all partitions in the + * |requested_partitions| are loaded and the |cmdline| field is set to + * "root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)" and the GUID for the + * appropriate system partition is substituted in. Note that none of + * the androidboot.* options mentioned above will be set. + * + * The |resolved_hashtree_error_mode| is the the value of the passed + * avb_slot_verify()'s |hashtree_error_mode| parameter except that it never has + * the value AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO. If this value was + * passed in, then the restart/eio state machine is used resulting in + * |resolved_hashtree_error_mode| being set to either + * AVB_HASHTREE_ERROR_MODE_RESTART or AVB_HASHTREE_ERROR_MODE_EIO. If set to + * AVB_HASHTREE_ERROR_MODE_EIO the boot loader should present a RED warning + * screen for the user to click through before continuing to boot. + * + * 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]; + AvbHashtreeErrorMode resolved_hashtree_error_mode; +} AvbSlotVerifyData; + +/* Calculates a digest of all vbmeta images in |data| using + * the digest indicated by |digest_type|. Stores the result + * in |out_digest| which must be large enough to hold a digest + * of the requested type. + */ +void avb_slot_verify_data_calculate_vbmeta_digest(AvbSlotVerifyData* data, + AvbDigestType digest_type, + uint8_t* out_digest); + +/* 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 and verify 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|. This parameter + * must include the leading underscore, for example "_a" should be + * used to refer to the first slot. + * + * Typically the |requested_partitions| array only contains a single + * item for the boot partition, 'boot'. + * + * Verification includes loading and verifying data from the 'vbmeta', + * the requested 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. + * + * The |flags| parameter is used to influence the semantics of + * avb_slot_verify() - for example the + * AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR flag can be used to + * ignore verification errors which is something needed in the + * UNLOCKED state. See the AvbSlotVerifyFlags enumeration for details. + * + * The |hashtree_error_mode| parameter should be set to the desired error + * handling mode. See the AvbHashtreeErrorMode enumeration for details. + * + * 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. + * + * AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION is returned if + * some of the metadata requires a newer version of libavb than what + * is in use. + * + * AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT is returned if the + * caller passed invalid parameters, for example trying to use + * AVB_HASHTREE_ERROR_MODE_LOGGING without + * AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR. + */ +AvbSlotVerifyResult avb_slot_verify(AvbOps* ops, + const char* const* requested_partitions, + const char* ab_suffix, + AvbSlotVerifyFlags flags, + AvbHashtreeErrorMode hashtree_error_mode, + AvbSlotVerifyData** out_data); + +#ifdef __cplusplus +} +#endif + +#endif /* AVB_SLOT_VERIFY_H_ */ diff --git a/aosp/libavb1.2/src/avb/headers/avb_sysdeps.h b/aosp/libavb1.2/src/avb/headers/avb_sysdeps.h new file mode 100644 index 0000000..e511a8a --- /dev/null +++ b/aosp/libavb1.2/src/avb/headers/avb_sysdeps.h @@ -0,0 +1,130 @@ +/* + * 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 +#include +#include +#include + +/* 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); + +/* Compare |n| bytes in two strings. + * + * Return an integer less than, equal to, or greater than zero if the + * first |n| bytes of |s1| is found, respectively, to be less than, + * to match, or be greater than the first |n| bytes of |s2|. + */ +int avb_strncmp(const char* s1, const char* s2, size_t n); + +/* 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; + +/* Divide the |dividend| by 10 and saves back to the pointer. Return the + * remainder. */ +uint32_t avb_div_by_10(uint64_t* dividend); + +#ifdef __cplusplus +} +#endif + +#endif /* AVB_SYSDEPS_H_ */ diff --git a/aosp/libavb1.2/src/avb/headers/avb_util.h b/aosp/libavb1.2/src/avb/headers/avb_util.h new file mode 100644 index 0000000..b6b036e --- /dev/null +++ b/aosp/libavb1.2/src/avb/headers/avb_util.h @@ -0,0 +1,298 @@ +/* + * 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_printv(avb_basename(__FILE__), \ + ":", \ + AVB_TO_STRING(__LINE__), \ + ": DEBUG: ", \ + message, \ + NULL); \ + } while (0) +#define avb_debugv(message, ...) \ + do { \ + avb_printv(avb_basename(__FILE__), \ + ":", \ + AVB_TO_STRING(__LINE__), \ + ": DEBUG: ", \ + 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_printv(avb_basename(__FILE__), \ + ":", \ + AVB_TO_STRING(__LINE__), \ + ": ERROR: ", \ + message, \ + NULL); \ + } while (0) +#define avb_errorv(message, ...) \ + do { \ + avb_printv(avb_basename(__FILE__), \ + ":", \ + AVB_TO_STRING(__LINE__), \ + ": ERROR: ", \ + message, \ + ##__VA_ARGS__); \ + } while (0) + +/* Prints out a message and calls avb_abort(). + */ +#define avb_fatal(message) \ + do { \ + avb_printv(avb_basename(__FILE__), \ + ":", \ + AVB_TO_STRING(__LINE__), \ + ": FATAL: ", \ + message, \ + NULL); \ + avb_abort(); \ + } while (0) +#define avb_fatalv(message, ...) \ + do { \ + avb_printv(avb_basename(__FILE__), \ + ":", \ + AVB_TO_STRING(__LINE__), \ + ": FATAL: ", \ + message, \ + ##__VA_ARGS__); \ + avb_abort(); \ + } while (0) + +/* Converts a 16-bit unsigned integer from big-endian to host byte order. */ +uint16_t avb_be16toh(uint16_t in) AVB_ATTR_WARN_UNUSED_RESULT; + +/* 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 16-bit unsigned integer from host to big-endian byte order. */ +uint16_t avb_htobe16(uint16_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; + +/* Duplicates a NULL-terminated array of NUL-terminated strings by + * concatenating them. The returned string will be + * NUL-terminated. Returns NULL on OOM. + */ +char* avb_strdupv(const char* str, + ...) AVB_ATTR_WARN_UNUSED_RESULT AVB_ATTR_SENTINEL; + +/* 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); + +/* Returns the basename of |str|. This is defined as the last path + * component, assuming the normal POSIX separator '/'. If there are no + * separators, returns |str|. + */ +const char* avb_basename(const char* str); + +/* Converts any ascii lowercase characters in |str| to uppercase in-place. + * |str| must be NUL-terminated and valid UTF-8. + */ +void avb_uppercase(char* str); + +/* Converts |data_len| bytes of |data| to hex and returns the result. Returns + * NULL on OOM. Caller must free the returned string with avb_free. + */ +char* avb_bin2hex(const uint8_t* data, size_t data_len); + +/* Writes |value| to |digits| in base 10 followed by a NUL byte. + * Returns number of characters written excluding the NUL byte. + */ +#define AVB_MAX_DIGITS_UINT64 32 +size_t avb_uint64_to_base10(uint64_t value, char digits[AVB_MAX_DIGITS_UINT64]); +#ifdef __cplusplus +} +#endif + +#endif /* AVB_UTIL_H_ */ diff --git a/aosp/libavb1.2/src/avb/headers/avb_vbmeta_image.h b/aosp/libavb1.2/src/avb/headers/avb_vbmeta_image.h new file mode 100644 index 0000000..f9cbac4 --- /dev/null +++ b/aosp/libavb1.2/src/avb/headers/avb_vbmeta_image.h @@ -0,0 +1,296 @@ +/* + * 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 + +/* Maximum size of the release string including the terminating NUL byte. */ +#define AVB_RELEASE_STRING_SIZE 48 + +/* Flags for the vbmeta image. + * + * AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED: If this flag is set, + * hashtree image verification will be disabled. + * + * AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED: If this flag is set, + * verification will be disabled and descriptors will not be parsed. + */ +typedef enum { + AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED = (1 << 0), + AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED = (1 << 1) +} 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 |required_libavb_version_major| + * and |required_libavb_version_minor| fields. This represents the + * minimum version of libavb required to verify the header and depends + * on the features (e.g. algorithms, descriptors) used. Note that this + * may be 1.0 even if generated by an avbtool from 1.4 but where no + * features introduced after 1.0 has been used. See the "Versioning + * and compatibility" section in the README.md file for more details. + * + * 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 libavb required for this header. */ + uint32_t required_libavb_version_major; + /* 8: The minor version of libavb required for this header. */ + uint32_t required_libavb_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: The location of the rollback index defined in this header. + * Only valid for the main vbmeta. For chained partitions, the rollback + * index location must be specified in the AvbChainPartitionDescriptor + * and this value must be set to 0. + */ + uint32_t rollback_index_location; + + /* 128: The release string from avbtool, e.g. "avbtool 1.0.0" or + * "avbtool 1.0.0 xyz_board Git-234abde89". Is guaranteed to be NUL + * terminated. Applications must not make assumptions about how this + * string is formatted. + */ + uint8_t release_string[AVB_RELEASE_STRING_SIZE]; + + /* 176: Padding to ensure struct is size AVB_VBMETA_IMAGE_HEADER_SIZE + * bytes. This must be set to zeroes. + */ + uint8_t reserved[80]; +} 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_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER is returned if the + * header of the vbmeta image is invalid, for example, invalid magic + * or inconsistent data. + * + * AVB_VBMETA_VERIFY_RESULT_UNSUPPORTED_VERSION is returned if a) the + * vbmeta image requires a minimum version of libavb which exceeds the + * version of libavb used; or b) the vbmeta image major version + * differs from the major version of libavb in use. + * + * AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH is returned if the hash + * stored in the "Authentication data" block does not match the + * calculated hash. + * + * AVB_VBMETA_VERIFY_RESULT_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_UNSUPPORTED_VERSION, + 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/aosp/libavb1.2/src/avb/headers/avb_version.h b/aosp/libavb1.2/src/avb/headers/avb_version.h new file mode 100644 index 0000000..e7b7f53 --- /dev/null +++ b/aosp/libavb1.2/src/avb/headers/avb_version.h @@ -0,0 +1,59 @@ +/* + * 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. + */ + +#if !defined(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION) +#error "Never include this file directly, include libavb.h instead." +#endif + +#ifndef AVB_VERSION_H_ +#define AVB_VERSION_H_ + +#include "avb_sysdeps.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* The version number of AVB - keep in sync with avbtool. */ +#define AVB_VERSION_MAJOR 1 +#define AVB_VERSION_MINOR 2 +#define AVB_VERSION_SUB 0 + +/* Returns a NUL-terminated string for the libavb version in use. The + * returned string usually looks like "%d.%d.%d". Applications must + * not make assumptions about the content of this string. + * + * Boot loaders should display this string in debug/diagnostics output + * to aid with debugging. + * + * This is similar to the string put in the |release_string| string + * field in the VBMeta struct by avbtool. + */ +const char* avb_version_string(void); + +#ifdef __cplusplus +} +#endif + +#endif /* AVB_VERSION_H_ */ diff --git a/aosp/libavb1.2/src/avb/headers/libavb.h b/aosp/libavb1.2/src/avb/headers/libavb.h new file mode 100644 index 0000000..d511584 --- /dev/null +++ b/aosp/libavb1.2/src/avb/headers/libavb.h @@ -0,0 +1,50 @@ +/* + * 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" +#include "avb_version.h" +#undef AVB_INSIDE_LIBAVB_H + +#endif /* LIBAVB_H_ */ diff --git a/aosp/libsparse/base/src/main/cpp/mapped_file.cpp b/aosp/libsparse/base/src/main/cpp/mapped_file.cpp new file mode 100644 index 0000000..fff3453 --- /dev/null +++ b/aosp/libsparse/base/src/main/cpp/mapped_file.cpp @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "android-base/mapped_file.h" + +#include + +#include + +namespace android { +namespace base { + +static constexpr char kEmptyBuffer[] = {'0'}; + +static off64_t InitPageSize() { +#if defined(_WIN32) + SYSTEM_INFO si; + GetSystemInfo(&si); + return si.dwAllocationGranularity; +#else + return sysconf(_SC_PAGE_SIZE); +#endif +} + +std::unique_ptr MappedFile::FromFd(borrowed_fd fd, off64_t offset, size_t length, + int prot) { +#if defined(_WIN32) + return FromOsHandle(reinterpret_cast(_get_osfhandle(fd.get())), offset, length, prot); +#else + return FromOsHandle(fd.get(), offset, length, prot); +#endif +} + +std::unique_ptr MappedFile::FromOsHandle(os_handle h, off64_t offset, size_t length, + int prot) { + static const off64_t page_size = InitPageSize(); + size_t slop = offset % page_size; + off64_t file_offset = offset - slop; + off64_t file_length = length + slop; + +#if defined(_WIN32) + HANDLE handle = CreateFileMappingW( + h, nullptr, (prot & PROT_WRITE) ? PAGE_READWRITE : PAGE_READONLY, 0, 0, nullptr); + if (handle == nullptr) { + // http://b/119818070 "app crashes when reading asset of zero length". + // Return a MappedFile that's only valid for reading the size. + if (length == 0 && ::GetLastError() == ERROR_FILE_INVALID) { + return std::unique_ptr( + new MappedFile(const_cast(kEmptyBuffer), 0, 0, nullptr)); + } + return nullptr; + } + void* base = MapViewOfFile(handle, (prot & PROT_WRITE) ? FILE_MAP_ALL_ACCESS : FILE_MAP_READ, 0, + file_offset, file_length); + if (base == nullptr) { + CloseHandle(handle); + return nullptr; + } + return std::unique_ptr( + new MappedFile(static_cast(base), length, slop, handle)); +#else + void* base = mmap(nullptr, file_length, prot, MAP_SHARED, h, file_offset); + if (base == MAP_FAILED) { + // http://b/119818070 "app crashes when reading asset of zero length". + // mmap fails with EINVAL for a zero length region. + if (errno == EINVAL && length == 0) { + return std::unique_ptr(new MappedFile(const_cast(kEmptyBuffer), 0, 0)); + } + return nullptr; + } + return std::unique_ptr(new MappedFile(static_cast(base), length, slop)); +#endif +} + +MappedFile::MappedFile(MappedFile&& other) + : base_(std::exchange(other.base_, nullptr)), + size_(std::exchange(other.size_, 0)), + offset_(std::exchange(other.offset_, 0)) +#ifdef _WIN32 + , + handle_(std::exchange(other.handle_, nullptr)) +#endif +{ +} + +MappedFile& MappedFile::operator=(MappedFile&& other) { + Close(); + base_ = std::exchange(other.base_, nullptr); + size_ = std::exchange(other.size_, 0); + offset_ = std::exchange(other.offset_, 0); +#ifdef _WIN32 + handle_ = std::exchange(other.handle_, nullptr); +#endif + return *this; +} + +MappedFile::~MappedFile() { + Close(); +} + +void MappedFile::Close() { +#if defined(_WIN32) + if (base_ != nullptr && size_ != 0) UnmapViewOfFile(base_); + if (handle_ != nullptr) CloseHandle(handle_); + handle_ = nullptr; +#else + if (base_ != nullptr && size_ != 0) munmap(base_, size_ + offset_); +#endif + + base_ = nullptr; + offset_ = size_ = 0; +} + +} // namespace base +} // namespace android diff --git a/aosp/libsparse/base/src/main/public/android-base/macros.h b/aosp/libsparse/base/src/main/public/android-base/macros.h new file mode 100644 index 0000000..546b2ec --- /dev/null +++ b/aosp/libsparse/base/src/main/public/android-base/macros.h @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2015 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. + */ + +#pragma once + +#include // for size_t +#include // for TEMP_FAILURE_RETRY + +#include + +// bionic and glibc both have TEMP_FAILURE_RETRY, but eg Mac OS' libc doesn't. +#ifndef TEMP_FAILURE_RETRY +#define TEMP_FAILURE_RETRY(exp) \ + ({ \ + decltype(exp) _rc; \ + do { \ + _rc = (exp); \ + } while (_rc == -1 && errno == EINTR); \ + _rc; \ + }) +#endif + +// A macro to disallow the copy constructor and operator= functions +// This must be placed in the private: declarations for a class. +// +// For disallowing only assign or copy, delete the relevant operator or +// constructor, for example: +// void operator=(const TypeName&) = delete; +// Note, that most uses of DISALLOW_ASSIGN and DISALLOW_COPY are broken +// semantically, one should either use disallow both or neither. Try to +// avoid these in new code. +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&) = delete; \ + void operator=(const TypeName&) = delete + +// A macro to disallow all the implicit constructors, namely the +// default constructor, copy constructor and operator= functions. +// +// This should be used in the private: declarations for a class +// that wants to prevent anyone from instantiating it. This is +// especially useful for classes containing only static methods. +#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \ + TypeName() = delete; \ + DISALLOW_COPY_AND_ASSIGN(TypeName) + +// The arraysize(arr) macro returns the # of elements in an array arr. +// The expression is a compile-time constant, and therefore can be +// used in defining new arrays, for example. If you use arraysize on +// a pointer by mistake, you will get a compile-time error. +// +// One caveat is that arraysize() doesn't accept any array of an +// anonymous type or a type defined inside a function. In these rare +// cases, you have to use the unsafe ARRAYSIZE_UNSAFE() macro below. This is +// due to a limitation in C++'s template system. The limitation might +// eventually be removed, but it hasn't happened yet. + +// This template function declaration is used in defining arraysize. +// Note that the function doesn't need an implementation, as we only +// use its type. +template +char(&ArraySizeHelper(T(&array)[N]))[N]; // NOLINT(readability/casting) + +#define arraysize(array) (sizeof(ArraySizeHelper(array))) + +#define SIZEOF_MEMBER(t, f) sizeof(std::declval().f) + +// Changing this definition will cause you a lot of pain. A majority of +// vendor code defines LIKELY and UNLIKELY this way, and includes +// this header through an indirect path. +#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) +#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) + +#define WARN_UNUSED __attribute__((warn_unused_result)) + +// A deprecated function to call to create a false use of the parameter, for +// example: +// int foo(int x) { UNUSED(x); return 10; } +// to avoid compiler warnings. Going forward we prefer ATTRIBUTE_UNUSED. +template +void UNUSED(const T&...) { +} + +// An attribute to place on a parameter to a function, for example: +// int foo(int x ATTRIBUTE_UNUSED) { return 10; } +// to avoid compiler warnings. +#define ATTRIBUTE_UNUSED __attribute__((__unused__)) + +// The FALLTHROUGH_INTENDED macro can be used to annotate implicit fall-through +// between switch labels: +// switch (x) { +// case 40: +// case 41: +// if (truth_is_out_there) { +// ++x; +// FALLTHROUGH_INTENDED; // Use instead of/along with annotations in +// // comments. +// } else { +// return x; +// } +// case 42: +// ... +// +// As shown in the example above, the FALLTHROUGH_INTENDED macro should be +// followed by a semicolon. It is designed to mimic control-flow statements +// like 'break;', so it can be placed in most places where 'break;' can, but +// only if there are no statements on the execution path between it and the +// next switch label. +// +// When compiled with clang, the FALLTHROUGH_INTENDED macro is expanded to +// [[clang::fallthrough]] attribute, which is analysed when performing switch +// labels fall-through diagnostic ('-Wimplicit-fallthrough'). See clang +// documentation on language extensions for details: +// http://clang.llvm.org/docs/LanguageExtensions.html#clang__fallthrough +// +// When used with unsupported compilers, the FALLTHROUGH_INTENDED macro has no +// effect on diagnostics. +// +// In either case this macro has no effect on runtime behavior and performance +// of code. +#ifndef FALLTHROUGH_INTENDED +#define FALLTHROUGH_INTENDED [[clang::fallthrough]] // NOLINT +#endif + +// Current ABI string +#if defined(__arm__) +#define ABI_STRING "arm" +#elif defined(__aarch64__) +#define ABI_STRING "arm64" +#elif defined(__i386__) +#define ABI_STRING "x86" +#elif defined(__x86_64__) +#define ABI_STRING "x86_64" +#endif diff --git a/aosp/libsparse/base/src/main/public/android-base/mapped_file.h b/aosp/libsparse/base/src/main/public/android-base/mapped_file.h new file mode 100644 index 0000000..8c37f43 --- /dev/null +++ b/aosp/libsparse/base/src/main/public/android-base/mapped_file.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2018 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. + */ + +#pragma once + +#include + +#include + +#include "android-base/macros.h" +#include "android-base/off64_t.h" +#include "android-base/unique_fd.h" + +#if defined(_WIN32) +#include +#define PROT_READ 1 +#define PROT_WRITE 2 +using os_handle = HANDLE; +#else +#include +using os_handle = int; +#endif + +namespace android { +namespace base { + +/** + * A region of a file mapped into memory (for grepping: also known as MmapFile or file mapping). + */ +class MappedFile { + public: + /** + * Creates a new mapping of the file pointed to by `fd`. Unlike the underlying OS primitives, + * `offset` does not need to be page-aligned. If `PROT_WRITE` is set in `prot`, the mapping + * will be writable, otherwise it will be read-only. Mappings are always `MAP_SHARED`. + */ + static std::unique_ptr FromFd(borrowed_fd fd, off64_t offset, size_t length, + int prot); + + /** + * Same thing, but using the raw OS file handle instead of a CRT wrapper. + */ + static std::unique_ptr FromOsHandle(os_handle h, off64_t offset, size_t length, + int prot); + + /** + * Removes the mapping. + */ + ~MappedFile(); + + /** + * Not copyable but movable. + */ + MappedFile(MappedFile&& other); + MappedFile& operator=(MappedFile&& other); + + char* data() const { return base_ + offset_; } + size_t size() const { return size_; } + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(MappedFile); + + void Close(); + + char* base_; + size_t size_; + + size_t offset_; + +#if defined(_WIN32) + MappedFile(char* base, size_t size, size_t offset, HANDLE handle) + : base_(base), size_(size), offset_(offset), handle_(handle) {} + HANDLE handle_; +#else + MappedFile(char* base, size_t size, size_t offset) : base_(base), size_(size), offset_(offset) {} +#endif +}; + +} // namespace base +} // namespace android diff --git a/aosp/libsparse/base/src/main/public/android-base/off64_t.h b/aosp/libsparse/base/src/main/public/android-base/off64_t.h new file mode 100644 index 0000000..e6b71b8 --- /dev/null +++ b/aosp/libsparse/base/src/main/public/android-base/off64_t.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2018 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. + */ + +#pragma once + +#if defined(__APPLE__) +/** Mac OS has always had a 64-bit off_t, so it doesn't have off64_t. */ +typedef off_t off64_t; +#endif diff --git a/aosp/libsparse/base/src/main/public/android-base/unique_fd.h b/aosp/libsparse/base/src/main/public/android-base/unique_fd.h new file mode 100644 index 0000000..9ceb5db --- /dev/null +++ b/aosp/libsparse/base/src/main/public/android-base/unique_fd.h @@ -0,0 +1,293 @@ +/* + * Copyright (C) 2015 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. + */ + +#pragma once + +#include +#include +#include + +#if !defined(_WIN32) +#include +#endif + +#include +#include +#include + +// DO NOT INCLUDE OTHER LIBBASE HEADERS! +// This file gets used in libbinder, and libbinder is used everywhere. +// Including other headers from libbase frequently results in inclusion of +// android-base/macros.h, which causes macro collisions. + +// Container for a file descriptor that automatically closes the descriptor as +// it goes out of scope. +// +// unique_fd ufd(open("/some/path", "r")); +// if (ufd.get() == -1) return error; +// +// // Do something useful, possibly including 'return'. +// +// return 0; // Descriptor is closed for you. +// +// unique_fd is also known as ScopedFd/ScopedFD/scoped_fd; mentioned here to help +// you find this class if you're searching for one of those names. + +#if defined(__BIONIC__) +#include +#endif + +namespace android { +namespace base { + +struct DefaultCloser { +#if defined(__BIONIC__) + static void Tag(int fd, void* old_addr, void* new_addr) { + if (android_fdsan_exchange_owner_tag) { + uint64_t old_tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_UNIQUE_FD, + reinterpret_cast(old_addr)); + uint64_t new_tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_UNIQUE_FD, + reinterpret_cast(new_addr)); + android_fdsan_exchange_owner_tag(fd, old_tag, new_tag); + } + } + static void Close(int fd, void* addr) { + if (android_fdsan_close_with_tag) { + uint64_t tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_UNIQUE_FD, + reinterpret_cast(addr)); + android_fdsan_close_with_tag(fd, tag); + } else { + close(fd); + } + } +#else + static void Close(int fd) { + // Even if close(2) fails with EINTR, the fd will have been closed. + // Using TEMP_FAILURE_RETRY will either lead to EBADF or closing someone + // else's fd. + // http://lkml.indiana.edu/hypermail/linux/kernel/0509.1/0877.html + ::close(fd); + } +#endif +}; + +template +class unique_fd_impl final { + public: + unique_fd_impl() {} + + explicit unique_fd_impl(int fd) { reset(fd); } + ~unique_fd_impl() { reset(); } + + unique_fd_impl(const unique_fd_impl&) = delete; + void operator=(const unique_fd_impl&) = delete; + unique_fd_impl(unique_fd_impl&& other) noexcept { reset(other.release()); } + unique_fd_impl& operator=(unique_fd_impl&& s) noexcept { + int fd = s.fd_; + s.fd_ = -1; + reset(fd, &s); + return *this; + } + + [[clang::reinitializes]] void reset(int new_value = -1) { reset(new_value, nullptr); } + + int get() const { return fd_; } + +#if !defined(ANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION) + // unique_fd's operator int is dangerous, but we have way too much code that + // depends on it, so make this opt-in at first. + operator int() const { return get(); } // NOLINT +#endif + + bool operator>=(int rhs) const { return get() >= rhs; } + bool operator<(int rhs) const { return get() < rhs; } + bool operator==(int rhs) const { return get() == rhs; } + bool operator!=(int rhs) const { return get() != rhs; } + bool operator==(const unique_fd_impl& rhs) const { return get() == rhs.get(); } + bool operator!=(const unique_fd_impl& rhs) const { return get() != rhs.get(); } + + // Catch bogus error checks (i.e.: "!fd" instead of "fd != -1"). + bool operator!() const = delete; + + bool ok() const { return get() >= 0; } + + int release() __attribute__((warn_unused_result)) { + tag(fd_, this, nullptr); + int ret = fd_; + fd_ = -1; + return ret; + } + + private: + void reset(int new_value, void* previous_tag) { + int previous_errno = errno; + + if (fd_ != -1) { + close(fd_, this); + } + + fd_ = new_value; + if (new_value != -1) { + tag(new_value, previous_tag, this); + } + + errno = previous_errno; + } + + int fd_ = -1; + + // Template magic to use Closer::Tag if available, and do nothing if not. + // If Closer::Tag exists, this implementation is preferred, because int is a better match. + // If not, this implementation is SFINAEd away, and the no-op below is the only one that exists. + template + static auto tag(int fd, void* old_tag, void* new_tag) + -> decltype(T::Tag(fd, old_tag, new_tag), void()) { + T::Tag(fd, old_tag, new_tag); + } + + template + static void tag(long, void*, void*) { + // No-op. + } + + // Same as above, to select between Closer::Close(int) and Closer::Close(int, void*). + template + static auto close(int fd, void* tag_value) -> decltype(T::Close(fd, tag_value), void()) { + T::Close(fd, tag_value); + } + + template + static auto close(int fd, void*) -> decltype(T::Close(fd), void()) { + T::Close(fd); + } +}; + +using unique_fd = unique_fd_impl; + +#if !defined(_WIN32) + +// Inline functions, so that they can be used header-only. +template +inline bool Pipe(unique_fd_impl* read, unique_fd_impl* write, + int flags = O_CLOEXEC) { + int pipefd[2]; + +#if defined(__linux__) + if (pipe2(pipefd, flags) != 0) { + return false; + } +#else // defined(__APPLE__) + if (flags & ~(O_CLOEXEC | O_NONBLOCK)) { + return false; + } + if (pipe(pipefd) != 0) { + return false; + } + + if (flags & O_CLOEXEC) { + if (fcntl(pipefd[0], F_SETFD, FD_CLOEXEC) != 0 || fcntl(pipefd[1], F_SETFD, FD_CLOEXEC) != 0) { + close(pipefd[0]); + close(pipefd[1]); + return false; + } + } + if (flags & O_NONBLOCK) { + if (fcntl(pipefd[0], F_SETFL, O_NONBLOCK) != 0 || fcntl(pipefd[1], F_SETFL, O_NONBLOCK) != 0) { + close(pipefd[0]); + close(pipefd[1]); + return false; + } + } +#endif + + read->reset(pipefd[0]); + write->reset(pipefd[1]); + return true; +} + +template +inline bool Socketpair(int domain, int type, int protocol, unique_fd_impl* left, + unique_fd_impl* right) { + int sockfd[2]; + if (socketpair(domain, type, protocol, sockfd) != 0) { + return false; + } + left->reset(sockfd[0]); + right->reset(sockfd[1]); + return true; +} + +template +inline bool Socketpair(int type, unique_fd_impl* left, unique_fd_impl* right) { + return Socketpair(AF_UNIX, type, 0, left, right); +} + +// Using fdopen with unique_fd correctly is more annoying than it should be, +// because fdopen doesn't close the file descriptor received upon failure. +inline FILE* Fdopen(unique_fd&& ufd, const char* mode) { + int fd = ufd.release(); + FILE* file = fdopen(fd, mode); + if (!file) { + close(fd); + } + return file; +} + +// Using fdopendir with unique_fd correctly is more annoying than it should be, +// because fdopen doesn't close the file descriptor received upon failure. +inline DIR* Fdopendir(unique_fd&& ufd) { + int fd = ufd.release(); + DIR* dir = fdopendir(fd); + if (dir == nullptr) { + close(fd); + } + return dir; +} + +#endif // !defined(_WIN32) + +// A wrapper type that can be implicitly constructed from either int or unique_fd. +struct borrowed_fd { + /* implicit */ borrowed_fd(int fd) : fd_(fd) {} // NOLINT + template + /* implicit */ borrowed_fd(const unique_fd_impl& ufd) : fd_(ufd.get()) {} // NOLINT + + int get() const { return fd_; } + + bool operator>=(int rhs) const { return get() >= rhs; } + bool operator<(int rhs) const { return get() < rhs; } + bool operator==(int rhs) const { return get() == rhs; } + bool operator!=(int rhs) const { return get() != rhs; } + + private: + int fd_ = -1; +}; +} // namespace base +} // namespace android + +template +int close(const android::base::unique_fd_impl&) + __attribute__((__unavailable__("close called on unique_fd"))); + +template +FILE* fdopen(const android::base::unique_fd_impl&, const char* mode) + __attribute__((__unavailable__("fdopen takes ownership of the fd passed in; either dup the " + "unique_fd, or use android::base::Fdopen to pass ownership"))); + +template +DIR* fdopendir(const android::base::unique_fd_impl&) __attribute__(( + __unavailable__("fdopendir takes ownership of the fd passed in; either dup the " + "unique_fd, or use android::base::Fdopendir to pass ownership"))); diff --git a/aosp/libsparse/sparse/src/main/cpp/output_file.cpp b/aosp/libsparse/sparse/src/main/cpp/output_file.cpp index c5c4960..b883c13 100644 --- a/aosp/libsparse/sparse/src/main/cpp/output_file.cpp +++ b/aosp/libsparse/sparse/src/main/cpp/output_file.cpp @@ -35,8 +35,9 @@ #include "sparse_crc32.h" #include "sparse_format.h" +#include + #ifndef _WIN32 -#include #define O_BINARY 0 #else #define ftruncate64 ftruncate @@ -45,7 +46,6 @@ #if defined(__APPLE__) && defined(__MACH__) #define lseek64 lseek #define ftruncate64 ftruncate -#define mmap64 mmap #define off64_t off_t #endif @@ -493,6 +493,10 @@ static struct sparse_file_ops normal_file_ops = { void output_file_close(struct output_file* out) { out->sparse_ops->write_end_chunk(out); + free(out->zero_buf); + free(out->fill_buf); + out->zero_buf = nullptr; + out->fill_buf = nullptr; out->ops->close(out); } @@ -645,52 +649,10 @@ int write_fill_chunk(struct output_file* out, unsigned int len, uint32_t fill_va } int write_fd_chunk(struct output_file* out, unsigned int len, int fd, int64_t offset) { - int ret; - int64_t aligned_offset; - int aligned_diff; - uint64_t buffer_size; - char* ptr; - - aligned_offset = offset & ~(4096 - 1); - aligned_diff = offset - aligned_offset; - buffer_size = (uint64_t)len + (uint64_t)aligned_diff; - -#ifndef _WIN32 - if (buffer_size > SIZE_MAX) return -E2BIG; - char* data = - reinterpret_cast(mmap64(nullptr, buffer_size, PROT_READ, MAP_SHARED, fd, aligned_offset)); - if (data == MAP_FAILED) { - return -errno; - } - ptr = data + aligned_diff; -#else - off64_t pos; - char* data = reinterpret_cast(malloc(len)); - if (!data) { - return -errno; - } - pos = lseek64(fd, offset, SEEK_SET); - if (pos < 0) { - free(data); - return -errno; - } - ret = read_all(fd, data, len); - if (ret < 0) { - free(data); - return ret; - } - ptr = data; -#endif + auto m = android::base::MappedFile::FromFd(fd, offset, len, PROT_READ); + if (!m) return -errno; - ret = out->sparse_ops->write_data_chunk(out, len, ptr); - -#ifndef _WIN32 - munmap(data, buffer_size); -#else - free(data); -#endif - - return ret; + return out->sparse_ops->write_data_chunk(out, m->size(), m->data()); } /* Write a contiguous region of data blocks from a file */ diff --git a/aosp/libsparse/sparse/src/main/cpp/sparse.cpp b/aosp/libsparse/sparse/src/main/cpp/sparse.cpp index cb288c5..8622b4c 100644 --- a/aosp/libsparse/sparse/src/main/cpp/sparse.cpp +++ b/aosp/libsparse/sparse/src/main/cpp/sparse.cpp @@ -136,11 +136,23 @@ static int write_all_blocks(struct sparse_file* s, struct output_file* out) { return 0; } +/* + * This is a workaround for 32-bit Windows: Limit the block size to 64 MB before + * fastboot executable binary for windows 64-bit is released (b/156057250). + */ +#define MAX_BACKED_BLOCK_SIZE ((unsigned int) (64UL << 20)) + int sparse_file_write(struct sparse_file* s, int fd, bool gz, bool sparse, bool crc) { + struct backed_block* bb; int ret; int chunks; struct output_file* out; + for (bb = backed_block_iter_new(s->backed_block_list); bb; bb = backed_block_iter_next(bb)) { + ret = backed_block_split(s->backed_block_list, bb, MAX_BACKED_BLOCK_SIZE); + if (ret) return ret; + } + chunks = sparse_count_chunks(s); out = output_file_open_fd(fd, s->block_size, s->len, gz, sparse, chunks, crc); @@ -188,7 +200,7 @@ int sparse_file_foreach_chunk(struct sparse_file* s, bool sparse, bool crc, int (*write)(void* priv, const void* data, size_t len, unsigned int block, unsigned int nr_blocks), void* priv) { - int ret; + int ret = 0; int chunks; struct chunk_data chk; struct output_file* out; diff --git a/aosp/mkbootfs/Makefile b/aosp/mkbootfs.10/Makefile similarity index 100% rename from aosp/mkbootfs/Makefile rename to aosp/mkbootfs.10/Makefile diff --git a/aosp/mkbootfs/build.gradle b/aosp/mkbootfs.10/build.gradle similarity index 100% rename from aosp/mkbootfs/build.gradle rename to aosp/mkbootfs.10/build.gradle diff --git a/aosp/mkbootfs/src/mkbootfs/cpp/fs_config.cpp b/aosp/mkbootfs.10/src/mkbootfs/cpp/fs_config.cpp similarity index 100% rename from aosp/mkbootfs/src/mkbootfs/cpp/fs_config.cpp rename to aosp/mkbootfs.10/src/mkbootfs/cpp/fs_config.cpp diff --git a/aosp/mkbootfs/src/mkbootfs/cpp/mkbootfs.c b/aosp/mkbootfs.10/src/mkbootfs/cpp/mkbootfs.c similarity index 100% rename from aosp/mkbootfs/src/mkbootfs/cpp/mkbootfs.c rename to aosp/mkbootfs.10/src/mkbootfs/cpp/mkbootfs.c diff --git a/aosp/mkbootfs/src/mkbootfs/headers/log/log.h b/aosp/mkbootfs.10/src/mkbootfs/headers/log/log.h similarity index 100% rename from aosp/mkbootfs/src/mkbootfs/headers/log/log.h rename to aosp/mkbootfs.10/src/mkbootfs/headers/log/log.h diff --git a/aosp/mkbootfs/src/mkbootfs/headers/private/android_filesystem_capability.h b/aosp/mkbootfs.10/src/mkbootfs/headers/private/android_filesystem_capability.h similarity index 100% rename from aosp/mkbootfs/src/mkbootfs/headers/private/android_filesystem_capability.h rename to aosp/mkbootfs.10/src/mkbootfs/headers/private/android_filesystem_capability.h diff --git a/aosp/mkbootfs/src/mkbootfs/headers/private/android_filesystem_config.h b/aosp/mkbootfs.10/src/mkbootfs/headers/private/android_filesystem_config.h similarity index 100% rename from aosp/mkbootfs/src/mkbootfs/headers/private/android_filesystem_config.h rename to aosp/mkbootfs.10/src/mkbootfs/headers/private/android_filesystem_config.h diff --git a/aosp/mkbootfs/src/mkbootfs/headers/private/fs_config.h b/aosp/mkbootfs.10/src/mkbootfs/headers/private/fs_config.h similarity index 100% rename from aosp/mkbootfs/src/mkbootfs/headers/private/fs_config.h rename to aosp/mkbootfs.10/src/mkbootfs/headers/private/fs_config.h diff --git a/aosp/mkbootfs/src/mkbootfs/headers/utils/Compat.h b/aosp/mkbootfs.10/src/mkbootfs/headers/utils/Compat.h similarity index 100% rename from aosp/mkbootfs/src/mkbootfs/headers/utils/Compat.h rename to aosp/mkbootfs.10/src/mkbootfs/headers/utils/Compat.h diff --git a/aosp/mkbootfs.11/build.gradle b/aosp/mkbootfs.11/build.gradle new file mode 100644 index 0000000..636dd65 --- /dev/null +++ b/aosp/mkbootfs.11/build.gradle @@ -0,0 +1,19 @@ +apply plugin: 'cpp' +apply plugin: 'c' + +model { + buildTypes { + release + } + + components { + mkbootfs(NativeExecutableSpec) { + binaries.all { + //cppCompiler.define "ANDROID" + //cppCompiler.define "ANDROID", "__ANDROID_VNDK__" + //cppCompiler.define 'CFIG_NO_FIX_STAT' + cppCompiler.args << '-std=c++17' << "-Wno-write-strings" + } + } + } +} diff --git a/aosp/mkbootfs.11/src/mkbootfs/cpp/fs_config.cpp b/aosp/mkbootfs.11/src/mkbootfs/cpp/fs_config.cpp new file mode 100644 index 0000000..5805a4d --- /dev/null +++ b/aosp/mkbootfs.11/src/mkbootfs/cpp/fs_config.cpp @@ -0,0 +1,373 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +// This file is used to define the properties of the filesystem +// images generated by build tools (mkbootfs and mkyaffs2image) and +// by the device side of adb. + +#define LOG_TAG "fs_config" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "fs_config.h" + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +using android::base::EndsWith; +using android::base::StartsWith; + +#define ALIGN(x, alignment) (((x) + ((alignment)-1)) & ~((alignment)-1)) +#define CAP_MASK_LONG(cap_name) (1ULL << (cap_name)) + +// Rules for directories. +// These rules are applied based on "first match", so they +// should start with the most specific path and work their +// way up to the root. + +static const struct fs_path_config android_dirs[] = { + // clang-format off + { 00770, AID_SYSTEM, AID_CACHE, 0, "cache" }, + { 00555, AID_ROOT, AID_ROOT, 0, "config" }, + { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app" }, + { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app-private" }, + { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app-ephemeral" }, + { 00771, AID_ROOT, AID_ROOT, 0, "data/dalvik-cache" }, + { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/data" }, + { 00771, AID_SHELL, AID_SHELL, 0, "data/local/tmp" }, + { 00771, AID_SHELL, AID_SHELL, 0, "data/local" }, + { 00770, AID_DHCP, AID_DHCP, 0, "data/misc/dhcp" }, + { 00771, AID_SHARED_RELRO, AID_SHARED_RELRO, 0, "data/misc/shared_relro" }, + { 01771, AID_SYSTEM, AID_MISC, 0, "data/misc" }, + { 00775, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media/Music" }, + { 00775, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media" }, + { 00750, AID_ROOT, AID_SHELL, 0, "data/nativetest" }, + { 00750, AID_ROOT, AID_SHELL, 0, "data/nativetest64" }, + { 00750, AID_ROOT, AID_SHELL, 0, "data/benchmarktest" }, + { 00750, AID_ROOT, AID_SHELL, 0, "data/benchmarktest64" }, + { 00775, AID_ROOT, AID_ROOT, 0, "data/preloads" }, + { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data" }, + { 00755, AID_ROOT, AID_SYSTEM, 0, "mnt" }, + { 00751, AID_ROOT, AID_SHELL, 0, "product/bin" }, + { 00777, AID_ROOT, AID_ROOT, 0, "sdcard" }, + { 00751, AID_ROOT, AID_SDCARD_R, 0, "storage" }, + { 00751, AID_ROOT, AID_SHELL, 0, "system/bin" }, + { 00755, AID_ROOT, AID_ROOT, 0, "system/etc/ppp" }, + { 00755, AID_ROOT, AID_SHELL, 0, "system/vendor" }, + { 00751, AID_ROOT, AID_SHELL, 0, "system/xbin" }, + { 00751, AID_ROOT, AID_SHELL, 0, "system/apex/*/bin" }, + { 00751, AID_ROOT, AID_SHELL, 0, "system_ext/bin" }, + { 00751, AID_ROOT, AID_SHELL, 0, "system_ext/apex/*/bin" }, + { 00751, AID_ROOT, AID_SHELL, 0, "vendor/bin" }, + { 00755, AID_ROOT, AID_SHELL, 0, "vendor" }, + { 00755, AID_ROOT, AID_ROOT, 0, 0 }, + // clang-format on +}; +#ifndef __ANDROID_VNDK__ +auto __for_testing_only__android_dirs = android_dirs; +#endif + +// Rules for files. +// These rules are applied based on "first match", so they +// should start with the most specific path and work their +// way up to the root. Prefixes ending in * denotes wildcard +// and will allow partial matches. +static const char sys_conf_dir[] = "/system/etc/fs_config_dirs"; +static const char sys_conf_file[] = "/system/etc/fs_config_files"; +// No restrictions are placed on the vendor and oem file-system config files, +// although the developer is advised to restrict the scope to the /vendor or +// oem/ file-system since the intent is to provide support for customized +// portions of a separate vendor.img or oem.img. Has to remain open so that +// customization can also land on /system/vendor, /system/oem, /system/odm, +// /system/product or /system/system_ext. +// +// We expect build-time checking or filtering when constructing the associated +// fs_config_* files (see build/tools/fs_config/fs_config_generate.c) +static const char ven_conf_dir[] = "/vendor/etc/fs_config_dirs"; +static const char ven_conf_file[] = "/vendor/etc/fs_config_files"; +static const char oem_conf_dir[] = "/oem/etc/fs_config_dirs"; +static const char oem_conf_file[] = "/oem/etc/fs_config_files"; +static const char odm_conf_dir[] = "/odm/etc/fs_config_dirs"; +static const char odm_conf_file[] = "/odm/etc/fs_config_files"; +static const char product_conf_dir[] = "/product/etc/fs_config_dirs"; +static const char product_conf_file[] = "/product/etc/fs_config_files"; +static const char system_ext_conf_dir[] = "/system_ext/etc/fs_config_dirs"; +static const char system_ext_conf_file[] = "/system_ext/etc/fs_config_files"; +static const char* conf[][2] = { + {sys_conf_file, sys_conf_dir}, {ven_conf_file, ven_conf_dir}, + {oem_conf_file, oem_conf_dir}, {odm_conf_file, odm_conf_dir}, + {product_conf_file, product_conf_dir}, {system_ext_conf_file, system_ext_conf_dir}, +}; + +// Do not use android_files to grant Linux capabilities. Use ambient capabilities in their +// associated init.rc file instead. See https://source.android.com/devices/tech/config/ambient. + +// Do not place any new vendor/, data/vendor/, etc entries in android_files. +// Vendor entries should be done via a vendor or device specific config.fs. +// See https://source.android.com/devices/tech/config/filesystem#using-file-system-capabilities +static const struct fs_path_config android_files[] = { + // clang-format off + { 00644, AID_SYSTEM, AID_SYSTEM, 0, "data/app/*" }, + { 00644, AID_SYSTEM, AID_SYSTEM, 0, "data/app-ephemeral/*" }, + { 00644, AID_SYSTEM, AID_SYSTEM, 0, "data/app-private/*" }, + { 00644, AID_APP, AID_APP, 0, "data/data/*" }, + { 00644, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media/*" }, + { 00640, AID_ROOT, AID_SHELL, 0, "data/nativetest/tests.txt" }, + { 00640, AID_ROOT, AID_SHELL, 0, "data/nativetest64/tests.txt" }, + { 00750, AID_ROOT, AID_SHELL, 0, "data/nativetest/*" }, + { 00750, AID_ROOT, AID_SHELL, 0, "data/nativetest64/*" }, + { 00750, AID_ROOT, AID_SHELL, 0, "data/benchmarktest/*" }, + { 00750, AID_ROOT, AID_SHELL, 0, "data/benchmarktest64/*" }, + { 00600, AID_ROOT, AID_ROOT, 0, "default.prop" }, // legacy + { 00600, AID_ROOT, AID_ROOT, 0, "system/etc/prop.default" }, + { 00600, AID_ROOT, AID_ROOT, 0, "odm/build.prop" }, // legacy; only for P release + { 00600, AID_ROOT, AID_ROOT, 0, "odm/default.prop" }, // legacy; only for P release + { 00600, AID_ROOT, AID_ROOT, 0, "odm/etc/build.prop" }, + { 00444, AID_ROOT, AID_ROOT, 0, odm_conf_dir + 1 }, + { 00444, AID_ROOT, AID_ROOT, 0, odm_conf_file + 1 }, + { 00444, AID_ROOT, AID_ROOT, 0, oem_conf_dir + 1 }, + { 00444, AID_ROOT, AID_ROOT, 0, oem_conf_file + 1 }, + { 00600, AID_ROOT, AID_ROOT, 0, "product/build.prop" }, + { 00444, AID_ROOT, AID_ROOT, 0, product_conf_dir + 1 }, + { 00444, AID_ROOT, AID_ROOT, 0, product_conf_file + 1 }, + { 00600, AID_ROOT, AID_ROOT, 0, "system_ext/build.prop" }, + { 00444, AID_ROOT, AID_ROOT, 0, system_ext_conf_dir + 1 }, + { 00444, AID_ROOT, AID_ROOT, 0, system_ext_conf_file + 1 }, + { 00755, AID_ROOT, AID_SHELL, 0, "system/bin/crash_dump32" }, + { 00755, AID_ROOT, AID_SHELL, 0, "system/bin/crash_dump64" }, + { 00755, AID_ROOT, AID_SHELL, 0, "system/bin/debuggerd" }, + { 00550, AID_LOGD, AID_LOGD, 0, "system/bin/logd" }, + { 00700, AID_ROOT, AID_ROOT, 0, "system/bin/secilc" }, + { 00750, AID_ROOT, AID_ROOT, 0, "system/bin/uncrypt" }, + { 00600, AID_ROOT, AID_ROOT, 0, "system/build.prop" }, + { 00444, AID_ROOT, AID_ROOT, 0, sys_conf_dir + 1 }, + { 00444, AID_ROOT, AID_ROOT, 0, sys_conf_file + 1 }, + { 00440, AID_ROOT, AID_SHELL, 0, "system/etc/init.goldfish.rc" }, + { 00550, AID_ROOT, AID_SHELL, 0, "system/etc/init.goldfish.sh" }, + { 00550, AID_ROOT, AID_SHELL, 0, "system/etc/init.ril" }, + { 00555, AID_ROOT, AID_ROOT, 0, "system/etc/ppp/*" }, + { 00555, AID_ROOT, AID_ROOT, 0, "system/etc/rc.*" }, + { 00750, AID_ROOT, AID_ROOT, 0, "vendor/bin/install-recovery.sh" }, + { 00600, AID_ROOT, AID_ROOT, 0, "vendor/build.prop" }, + { 00600, AID_ROOT, AID_ROOT, 0, "vendor/default.prop" }, + { 00440, AID_ROOT, AID_ROOT, 0, "vendor/etc/recovery.img" }, + { 00444, AID_ROOT, AID_ROOT, 0, ven_conf_dir + 1 }, + { 00444, AID_ROOT, AID_ROOT, 0, ven_conf_file + 1 }, + + // the following two files are INTENTIONALLY set-uid, but they + // are NOT included on user builds. + { 06755, AID_ROOT, AID_ROOT, 0, "system/xbin/procmem" }, + { 04750, AID_ROOT, AID_SHELL, 0, "system/xbin/su" }, + + // the following files have enhanced capabilities and ARE included + // in user builds. + { 00700, AID_SYSTEM, AID_SHELL, CAP_MASK_LONG(CAP_BLOCK_SUSPEND), + "system/bin/inputflinger" }, + { 00750, AID_ROOT, AID_SHELL, CAP_MASK_LONG(CAP_SETUID) | + CAP_MASK_LONG(CAP_SETGID), + "system/bin/run-as" }, + { 00750, AID_ROOT, AID_SHELL, CAP_MASK_LONG(CAP_SETUID) | + CAP_MASK_LONG(CAP_SETGID), + "system/bin/simpleperf_app_runner" }, + { 00755, AID_ROOT, AID_ROOT, 0, "first_stage_ramdisk/system/bin/e2fsck" }, + { 00755, AID_ROOT, AID_ROOT, 0, "first_stage_ramdisk/system/bin/tune2fs" }, + { 00755, AID_ROOT, AID_ROOT, 0, "first_stage_ramdisk/system/bin/resize2fs" }, + // generic defaults + { 00755, AID_ROOT, AID_ROOT, 0, "bin/*" }, + { 00640, AID_ROOT, AID_SHELL, 0, "fstab.*" }, + { 00750, AID_ROOT, AID_SHELL, 0, "init*" }, + { 00755, AID_ROOT, AID_SHELL, 0, "odm/bin/*" }, + { 00755, AID_ROOT, AID_SHELL, 0, "product/bin/*" }, + { 00755, AID_ROOT, AID_SHELL, 0, "system/bin/*" }, + { 00755, AID_ROOT, AID_SHELL, 0, "system/xbin/*" }, + { 00755, AID_ROOT, AID_SHELL, 0, "system/apex/*/bin/*" }, + { 00755, AID_ROOT, AID_SHELL, 0, "system_ext/bin/*" }, + { 00755, AID_ROOT, AID_SHELL, 0, "system_ext/apex/*/bin/*" }, + { 00755, AID_ROOT, AID_SHELL, 0, "vendor/bin/*" }, + { 00755, AID_ROOT, AID_SHELL, 0, "vendor/xbin/*" }, + { 00644, AID_ROOT, AID_ROOT, 0, 0 }, + // clang-format on +}; +#ifndef __ANDROID_VNDK__ +auto __for_testing_only__android_files = android_files; +#endif + +static size_t strip(const char* path, size_t len, const char suffix[]) { + if (len < strlen(suffix)) return len; + if (strncmp(path + len - strlen(suffix), suffix, strlen(suffix))) return len; + return len - strlen(suffix); +} + +static int fs_config_open(int dir, int which, const char* target_out_path) { + int fd = -1; + + if (target_out_path && *target_out_path) { + // target_out_path is the path to the directory holding content of + // system partition but as we cannot guarantee it ends with '/system' + // or with or without a trailing slash, need to strip them carefully. + char* name = NULL; + size_t len = strlen(target_out_path); + len = strip(target_out_path, len, "/"); + len = strip(target_out_path, len, "/system"); + if (asprintf(&name, "%.*s%s", (int)len, target_out_path, conf[which][dir]) != -1) { + fd = TEMP_FAILURE_RETRY(open(name, O_RDONLY | O_BINARY)); + free(name); + } + } + if (fd < 0) { + fd = TEMP_FAILURE_RETRY(open(conf[which][dir], O_RDONLY | O_BINARY)); + } + return fd; +} + +// if path is "odm/", "oem/", "product/", +// "system_ext/" or "vendor/" +static bool is_partition(const std::string& path) { + static const char* partitions[] = {"odm/", "oem/", "product/", "system_ext/", "vendor/"}; + for (size_t i = 0; i < (sizeof(partitions) / sizeof(partitions[0])); ++i) { + if (StartsWith(path, partitions[i])) return true; + } + return false; +} + +// alias prefixes of "/" to "system//" or +// "system//" to "/" +static bool fs_config_cmp(bool dir, const char* prefix, size_t len, const char* path, size_t plen) { + std::string pattern(prefix, len); + std::string input(path, plen); + + // Massage pattern and input so that they can be used by fnmatch where + // directories have to end with /. + if (dir) { + if (!EndsWith(input, "/")) { + input.append("/"); + } + + if (!EndsWith(pattern, "/*")) { + if (EndsWith(pattern, "/")) { + pattern.append("*"); + } else { + pattern.append("/*"); + } + } + } + + // no FNM_PATHNAME is set in order to match a/b/c/d with a/* + // FNM_ESCAPE is set in order to prevent using \\? and \\* and maintenance issues. + const int fnm_flags = FNM_NOESCAPE; + if (fnmatch(pattern.c_str(), input.c_str(), fnm_flags) == 0) return true; + + // Check match between logical partition's files and patterns. + static constexpr const char* kLogicalPartitions[] = {"system/product/", "system/system_ext/", + "system/vendor/", "vendor/odm/"}; + for (auto& logical_partition : kLogicalPartitions) { + if (StartsWith(input, logical_partition)) { + std::string input_in_partition = input.substr(input.find('/') + 1); + if (!is_partition(input_in_partition)) continue; + if (fnmatch(pattern.c_str(), input_in_partition.c_str(), fnm_flags) == 0) { + return true; + } + } + } + return false; +} +#ifndef __ANDROID_VNDK__ +auto __for_testing_only__fs_config_cmp = fs_config_cmp; +#endif + +void fs_config(const char* path, int dir, const char* target_out_path, unsigned* uid, unsigned* gid, + unsigned* mode, uint64_t* capabilities) { + const struct fs_path_config* pc; + size_t which, plen; + + if (path[0] == '/') { + path++; + } + + plen = strlen(path); + + for (which = 0; which < (sizeof(conf) / sizeof(conf[0])); ++which) { + struct fs_path_config_from_file header; + + int fd = fs_config_open(dir, which, target_out_path); + if (fd < 0) continue; + + while (TEMP_FAILURE_RETRY(read(fd, &header, sizeof(header))) == sizeof(header)) { + char* prefix; + uint16_t host_len = header.len; + ssize_t len, remainder = host_len - sizeof(header); + if (remainder <= 0) { + ALOGE("%s len is corrupted", conf[which][dir]); + break; + } + prefix = static_cast(calloc(1, remainder)); + if (!prefix) { + ALOGE("%s out of memory", conf[which][dir]); + break; + } + if (TEMP_FAILURE_RETRY(read(fd, prefix, remainder)) != remainder) { + free(prefix); + ALOGE("%s prefix is truncated", conf[which][dir]); + break; + } + len = strnlen(prefix, remainder); + if (len >= remainder) { // missing a terminating null + free(prefix); + ALOGE("%s is corrupted", conf[which][dir]); + break; + } + if (fs_config_cmp(dir, prefix, len, path, plen)) { + free(prefix); + close(fd); + *uid = header.uid; + *gid = header.gid; + *mode = (*mode & (~07777)) | header.mode; + *capabilities = header.capabilities; + return; + } + free(prefix); + } + close(fd); + } + + for (pc = dir ? android_dirs : android_files; pc->prefix; pc++) { + if (fs_config_cmp(dir, pc->prefix, strlen(pc->prefix), path, plen)) { + break; + } + } + *uid = pc->uid; + *gid = pc->gid; + *mode = (*mode & (~07777)) | pc->mode; + *capabilities = pc->capabilities; +} diff --git a/aosp/mkbootfs.11/src/mkbootfs/cpp/mkbootfs.c b/aosp/mkbootfs.11/src/mkbootfs/cpp/mkbootfs.c new file mode 100644 index 0000000..3240a84 --- /dev/null +++ b/aosp/mkbootfs.11/src/mkbootfs/cpp/mkbootfs.c @@ -0,0 +1,372 @@ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include + +/* NOTES +** +** - see buffer-format.txt from the linux kernel docs for +** an explanation of this file format +** - dotfiles are ignored +** - directories named 'root' are ignored +** - device notes, pipes, etc are not supported (error) +*/ + +void die(const char *why, ...) +{ + va_list ap; + + va_start(ap, why); + fprintf(stderr,"error: "); + vfprintf(stderr, why, ap); + fprintf(stderr,"\n"); + va_end(ap); + exit(1); +} + +struct fs_config_entry { + char* name; + int uid, gid, mode; +}; + +static struct fs_config_entry* canned_config = NULL; +static char *target_out_path = NULL; + +/* Each line in the canned file should be a path plus three ints (uid, + * gid, mode). */ +#ifdef PATH_MAX +#define CANNED_LINE_LENGTH (PATH_MAX+100) +#else +#define CANNED_LINE_LENGTH (1024) +#endif + +#define TRAILER "TRAILER!!!" + +static int verbose = 0; +static int total_size = 0; + +static void fix_stat(const char *path, struct stat *s) +{ + uint64_t capabilities; + if (canned_config) { + // Use the list of file uid/gid/modes loaded from the file + // given with -f. + + struct fs_config_entry* empty_path_config = NULL; + struct fs_config_entry* p; + for (p = canned_config; p->name; ++p) { + if (!p->name[0]) { + empty_path_config = p; + } + if (strcmp(p->name, path) == 0) { + s->st_uid = p->uid; + s->st_gid = p->gid; + s->st_mode = p->mode | (s->st_mode & ~07777); + return; + } + } + s->st_uid = empty_path_config->uid; + s->st_gid = empty_path_config->gid; + s->st_mode = empty_path_config->mode | (s->st_mode & ~07777); + } else { + // Use the compiled-in fs_config() function. + unsigned st_mode = s->st_mode; + int is_dir = S_ISDIR(s->st_mode) || strcmp(path, TRAILER) == 0; + fs_config(path, is_dir, target_out_path, &s->st_uid, &s->st_gid, &st_mode, &capabilities); + s->st_mode = (decltype(s->st_mode)) st_mode; + } +} + +static void _eject(struct stat *s, char *out, int olen, char *data, unsigned datasize) +{ + // Nothing is special about this value, just picked something in the + // approximate range that was being used already, and avoiding small + // values which may be special. + static unsigned next_inode = 300000; + + while(total_size & 3) { + total_size++; + putchar(0); + } + +#ifdef CFIG_NO_FIX_STAT +#warning CFIG_NO_FIX_STAT defined, will not fix_stat() defined in Android +#else + fix_stat(out, s); +#endif +// fprintf(stderr, "_eject %s: mode=0%o\n", out, s->st_mode); + + printf("%06x%08x%08x%08x%08x%08x%08x" + "%08x%08x%08x%08x%08x%08x%08x%s%c", + 0x070701, + next_inode++, // s.st_ino, + s->st_mode, + 0, // s.st_uid, + 0, // s.st_gid, + 1, // s.st_nlink, + 0, // s.st_mtime, + datasize, + 0, // volmajor + 0, // volminor + 0, // devmajor + 0, // devminor, + olen + 1, + 0, + out, + 0 + ); + + total_size += 6 + 8*13 + olen + 1; + + if(strlen(out) != (unsigned int)olen) die("ACK!"); + + while(total_size & 3) { + total_size++; + putchar(0); + } + + if(datasize) { + fwrite(data, datasize, 1, stdout); + total_size += datasize; + } +} + +static void _eject_trailer() +{ + struct stat s; + memset(&s, 0, sizeof(s)); + _eject(&s, TRAILER, 10, 0, 0); + + while(total_size & 0xff) { + total_size++; + putchar(0); + } +} + +static void _archive(char *in, char *out, int ilen, int olen); + +static int compare(const void* a, const void* b) { + return strcmp(*(const char**)a, *(const char**)b); +} + +static void _archive_dir(char *in, char *out, int ilen, int olen) +{ + int i, t; + DIR *d; + struct dirent *de; + + if(verbose) { + fprintf(stderr,"_archive_dir('%s','%s',%d,%d)\n", + in, out, ilen, olen); + } + + d = opendir(in); + if(d == 0) die("cannot open directory '%s'", in); + + int size = 32; + int entries = 0; + char** names = (char**) malloc(size * sizeof(char*)); + if (names == NULL) { + fprintf(stderr, "failed to allocate dir names array (size %d)\n", size); + exit(1); + } + + while((de = readdir(d)) != 0){ + /* xxx: feature? maybe some dotfiles are okay */ + if(de->d_name[0] == '.') continue; + + /* xxx: hack. use a real exclude list */ + if(!strcmp(de->d_name, "root")) continue; + + if (entries >= size) { + size *= 2; + names = (char**) realloc(names, size * sizeof(char*)); + if (names == NULL) { + fprintf(stderr, "failed to reallocate dir names array (size %d)\n", + size); + exit(1); + } + } + names[entries] = strdup(de->d_name); + if (names[entries] == NULL) { + fprintf(stderr, "failed to strdup name \"%s\"\n", + de->d_name); + exit(1); + } + ++entries; + } + + qsort(names, entries, sizeof(char*), compare); + + for (i = 0; i < entries; ++i) { + t = strlen(names[i]); + in[ilen] = '/'; + memcpy(in + ilen + 1, names[i], t + 1); + + if(olen > 0) { + out[olen] = '/'; + memcpy(out + olen + 1, names[i], t + 1); + _archive(in, out, ilen + t + 1, olen + t + 1); + } else { + memcpy(out, names[i], t + 1); + _archive(in, out, ilen + t + 1, t); + } + + in[ilen] = 0; + out[olen] = 0; + + free(names[i]); + } + free(names); + + closedir(d); +} + +static void _archive(char *in, char *out, int ilen, int olen) +{ + struct stat s; + + if(verbose) { + fprintf(stderr,"_archive('%s','%s',%d,%d)\n", + in, out, ilen, olen); + } + + if(lstat(in, &s)) die("could not stat '%s'\n", in); + + if(S_ISREG(s.st_mode)){ + char *tmp; + int fd; + + fd = open(in, O_RDONLY); + if(fd < 0) die("cannot open '%s' for read", in); + + tmp = (char*) malloc(s.st_size); + if(tmp == 0) die("cannot allocate %d bytes", s.st_size); + + if(read(fd, tmp, s.st_size) != s.st_size) { + die("cannot read %d bytes", s.st_size); + } + + _eject(&s, out, olen, tmp, s.st_size); + + free(tmp); + close(fd); + } else if(S_ISDIR(s.st_mode)) { + _eject(&s, out, olen, 0, 0); + _archive_dir(in, out, ilen, olen); + } else if(S_ISLNK(s.st_mode)) { + char buf[1024]; + int size; + size = readlink(in, buf, 1024); + if(size < 0) die("cannot read symlink '%s'", in); + _eject(&s, out, olen, buf, size); + } else { + die("Unknown '%s' (mode %d)?\n", in, s.st_mode); + } +} + +void archive(const char *start, const char *prefix) +{ + char in[8192]; + char out[8192]; + + strcpy(in, start); + strcpy(out, prefix); + + _archive_dir(in, out, strlen(in), strlen(out)); +} + +static void read_canned_config(char* filename) +{ + int allocated = 8; + int used = 0; + + canned_config = + (struct fs_config_entry*)malloc(allocated * sizeof(struct fs_config_entry)); + + char line[CANNED_LINE_LENGTH]; + FILE* f = fopen(filename, "r"); + if (f == NULL) die("failed to open canned file"); + + while (fgets(line, CANNED_LINE_LENGTH, f) != NULL) { + if (!line[0]) break; + if (used >= allocated) { + allocated *= 2; + canned_config = (struct fs_config_entry*)realloc( + canned_config, allocated * sizeof(struct fs_config_entry)); + if (canned_config == NULL) die("failed to reallocate memory"); + } + + struct fs_config_entry* cc = canned_config + used; + + if (isspace(line[0])) { + cc->name = strdup(""); + cc->uid = atoi(strtok(line, " \n")); + } else { + cc->name = strdup(strtok(line, " \n")); + cc->uid = atoi(strtok(NULL, " \n")); + } + cc->gid = atoi(strtok(NULL, " \n")); + cc->mode = strtol(strtok(NULL, " \n"), NULL, 8); + ++used; + } + if (used >= allocated) { + ++allocated; + canned_config = (struct fs_config_entry*)realloc( + canned_config, allocated * sizeof(struct fs_config_entry)); + if (canned_config == NULL) die("failed to reallocate memory"); + } + canned_config[used].name = NULL; + + fclose(f); +} + + +int main(int argc, char *argv[]) +{ + argc--; + argv++; + + if (argc > 1 && strcmp(argv[0], "-d") == 0) { + target_out_path = argv[1]; + argc -= 2; + argv += 2; + } + + if (argc > 1 && strcmp(argv[0], "-f") == 0) { + read_canned_config(argv[1]); + argc -= 2; + argv += 2; + } + + if(argc == 0) die("no directories to process?!"); + + while(argc-- > 0){ + char *x = strchr(*argv, '='); + if(x != 0) { + *x++ = 0; + } else { + x = ""; + } + + archive(*argv, x); + + argv++; + } + + _eject_trailer(); + + return 0; +} diff --git a/aosp/mkbootfs.11/src/mkbootfs/cpp/strings.cpp b/aosp/mkbootfs.11/src/mkbootfs/cpp/strings.cpp new file mode 100644 index 0000000..05d09e1 --- /dev/null +++ b/aosp/mkbootfs.11/src/mkbootfs/cpp/strings.cpp @@ -0,0 +1,26 @@ +#include "android-base/strings.h" +#include +#include + +namespace android { +namespace base { + +bool StartsWith(std::string_view s, std::string_view prefix) { + return s.substr(0, prefix.size()) == prefix; +} + +bool StartsWith(std::string_view s, char prefix) { + return !s.empty() && s.front() == prefix; +} + + +bool EndsWith(std::string_view s, std::string_view suffix) { + return s.size() >= suffix.size() && s.substr(s.size() - suffix.size(), suffix.size()) == suffix; +} + +bool EndsWith(std::string_view s, char suffix) { + return !s.empty() && s.back() == suffix; +} + +} // namespace base +} // namespace android diff --git a/aosp/mkbootfs.11/src/mkbootfs/headers/android-base/strings.h b/aosp/mkbootfs.11/src/mkbootfs/headers/android-base/strings.h new file mode 100644 index 0000000..fe48e94 --- /dev/null +++ b/aosp/mkbootfs.11/src/mkbootfs/headers/android-base/strings.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include +#include + +namespace android { +namespace base { + +// Tests whether 's' starts with 'prefix'. +bool StartsWith(std::string_view s, std::string_view prefix); +bool StartsWith(std::string_view s, char prefix); + +// Tests whether 's' ends with 'suffix'. +bool EndsWith(std::string_view s, std::string_view suffix); +bool EndsWith(std::string_view s, char suffix); + +} // namespace base +} // namespace android diff --git a/aosp/mkbootfs.11/src/mkbootfs/headers/fs_config.h b/aosp/mkbootfs.11/src/mkbootfs/headers/fs_config.h new file mode 100644 index 0000000..66ad48b --- /dev/null +++ b/aosp/mkbootfs.11/src/mkbootfs/headers/fs_config.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2019 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. + */ + +#pragma once + +#include + +// Binary format for the runtime /etc/fs_config_(dirs|files) filesystem override files. +struct fs_path_config_from_file { + uint16_t len; + uint16_t mode; + uint16_t uid; + uint16_t gid; + uint64_t capabilities; + char prefix[]; +} __attribute__((__aligned__(sizeof(uint64_t)))); + +struct fs_path_config { + unsigned mode; + unsigned uid; + unsigned gid; + uint64_t capabilities; + const char* prefix; +}; diff --git a/aosp/mkbootfs.11/src/mkbootfs/headers/log/log.h b/aosp/mkbootfs.11/src/mkbootfs/headers/log/log.h new file mode 100644 index 0000000..74001a2 --- /dev/null +++ b/aosp/mkbootfs.11/src/mkbootfs/headers/log/log.h @@ -0,0 +1,6 @@ +#ifndef _CFIG_LOG_H +#define _CFIG_LOG_H + +#define ALOGE printf + +#endif diff --git a/aosp/mkbootfs.11/src/mkbootfs/headers/private/android_filesystem_capability.h b/aosp/mkbootfs.11/src/mkbootfs/headers/private/android_filesystem_capability.h new file mode 100644 index 0000000..0227b1d --- /dev/null +++ b/aosp/mkbootfs.11/src/mkbootfs/headers/private/android_filesystem_capability.h @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2013 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. + */ + +/* + * Taken from linux/capability.h, with minor modifications + */ + +#ifndef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_FILESYSTEM_CAPABILITY_H +#define _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_FILESYSTEM_CAPABILITY_H + +#include + +#define __user +#define __u32 uint32_t +#define __le32 uint32_t + +#define _LINUX_CAPABILITY_VERSION_1 0x19980330 +#define _LINUX_CAPABILITY_U32S_1 1 +#define _LINUX_CAPABILITY_VERSION_2 0x20071026 +#define _LINUX_CAPABILITY_U32S_2 2 +#define _LINUX_CAPABILITY_VERSION_3 0x20080522 +#define _LINUX_CAPABILITY_U32S_3 2 + +typedef struct __user_cap_header_struct { + __u32 version; + int pid; +} __user* cap_user_header_t; + +typedef struct __user_cap_data_struct { + __u32 effective; + __u32 permitted; + __u32 inheritable; +} __user* cap_user_data_t; + +#define VFS_CAP_REVISION_MASK 0xFF000000 +#define VFS_CAP_REVISION_SHIFT 24 +#define VFS_CAP_FLAGS_MASK ~VFS_CAP_REVISION_MASK +#define VFS_CAP_FLAGS_EFFECTIVE 0x000001 +#define VFS_CAP_REVISION_1 0x01000000 +#define VFS_CAP_U32_1 1 +#define XATTR_CAPS_SZ_1 (sizeof(__le32) * (1 + 2 * VFS_CAP_U32_1)) +#define VFS_CAP_REVISION_2 0x02000000 +#define VFS_CAP_U32_2 2 +#define XATTR_CAPS_SZ_2 (sizeof(__le32) * (1 + 2 * VFS_CAP_U32_2)) +#define XATTR_CAPS_SZ XATTR_CAPS_SZ_2 +#define VFS_CAP_U32 VFS_CAP_U32_2 +#define VFS_CAP_REVISION VFS_CAP_REVISION_2 + +struct vfs_cap_data { + __le32 magic_etc; + struct { + __le32 permitted; + __le32 inheritable; + } data[VFS_CAP_U32]; +}; + +#define _LINUX_CAPABILITY_VERSION _LINUX_CAPABILITY_VERSION_1 +#define _LINUX_CAPABILITY_U32S _LINUX_CAPABILITY_U32S_1 +#define CAP_CHOWN 0 +#define CAP_DAC_OVERRIDE 1 +#define CAP_DAC_READ_SEARCH 2 +#define CAP_FOWNER 3 +#define CAP_FSETID 4 +#define CAP_KILL 5 +#define CAP_SETGID 6 +#define CAP_SETUID 7 +#define CAP_SETPCAP 8 +#define CAP_LINUX_IMMUTABLE 9 +#define CAP_NET_BIND_SERVICE 10 +#define CAP_NET_BROADCAST 11 +#define CAP_NET_ADMIN 12 +#define CAP_NET_RAW 13 +#define CAP_IPC_LOCK 14 +#define CAP_IPC_OWNER 15 +#define CAP_SYS_MODULE 16 +#define CAP_SYS_RAWIO 17 +#define CAP_SYS_CHROOT 18 +#define CAP_SYS_PTRACE 19 +#define CAP_SYS_PACCT 20 +#define CAP_SYS_ADMIN 21 +#define CAP_SYS_BOOT 22 +#define CAP_SYS_NICE 23 +#define CAP_SYS_RESOURCE 24 +#define CAP_SYS_TIME 25 +#define CAP_SYS_TTY_CONFIG 26 +#define CAP_MKNOD 27 +#define CAP_LEASE 28 +#define CAP_AUDIT_WRITE 29 +#define CAP_AUDIT_CONTROL 30 +#define CAP_SETFCAP 31 +#define CAP_MAC_OVERRIDE 32 +#define CAP_MAC_ADMIN 33 +#define CAP_SYSLOG 34 +#define CAP_WAKE_ALARM 35 +#define CAP_BLOCK_SUSPEND 36 +#define CAP_AUDIT_READ 37 +#define CAP_LAST_CAP CAP_AUDIT_READ +#define cap_valid(x) ((x) >= 0 && (x) <= CAP_LAST_CAP) +#define CAP_TO_INDEX(x) ((x) >> 5) +#define CAP_TO_MASK(x) (1 << ((x)&31)) + +#undef __user +#undef __u32 +#undef __le32 + +#endif diff --git a/aosp/mkbootfs.11/src/mkbootfs/headers/private/android_filesystem_config.h b/aosp/mkbootfs.11/src/mkbootfs/headers/private/android_filesystem_config.h new file mode 100644 index 0000000..e4f45a8 --- /dev/null +++ b/aosp/mkbootfs.11/src/mkbootfs/headers/private/android_filesystem_config.h @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2007 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. + */ + +/* + * This file is consumed by build/tools/fs_config and is used + * for generating various files. Anything #define AID_ + * becomes the mapping for getpwnam/getpwuid, etc. The + * field is lowercased. + * For example: + * #define AID_FOO_BAR 6666 becomes a friendly name of "foo_bar" + * + * The above holds true with the exception of: + * mediacodec + * mediaex + * mediadrm + * Whose friendly names do not match the #define statements. + * + * This file must only be used for platform (Google managed, and submitted through AOSP), AIDs. 3rd + * party AIDs must be added via config.fs, which will place them in the corresponding partition's + * passwd and group files. There are ranges in this file reserved for AIDs for each 3rd party + * partition, from which the system reads passwd and group files. + */ + +#pragma once + +/* This is the master Users and Groups config for the platform. + * DO NOT EVER RENUMBER + */ + +#define AID_ROOT 0 /* traditional unix root user */ +/* The following are for LTP and should only be used for testing */ +#define AID_DAEMON 1 /* traditional unix daemon owner */ +#define AID_BIN 2 /* traditional unix binaries owner */ + +#define AID_SYSTEM 1000 /* system server */ + +#define AID_RADIO 1001 /* telephony subsystem, RIL */ +#define AID_BLUETOOTH 1002 /* bluetooth subsystem */ +#define AID_GRAPHICS 1003 /* graphics devices */ +#define AID_INPUT 1004 /* input devices */ +#define AID_AUDIO 1005 /* audio devices */ +#define AID_CAMERA 1006 /* camera devices */ +#define AID_LOG 1007 /* log devices */ +#define AID_COMPASS 1008 /* compass device */ +#define AID_MOUNT 1009 /* mountd socket */ +#define AID_WIFI 1010 /* wifi subsystem */ +#define AID_ADB 1011 /* android debug bridge (adbd) */ +#define AID_INSTALL 1012 /* group for installing packages */ +#define AID_MEDIA 1013 /* mediaserver process */ +#define AID_DHCP 1014 /* dhcp client */ +#define AID_SDCARD_RW 1015 /* external storage write access */ +#define AID_VPN 1016 /* vpn system */ +#define AID_KEYSTORE 1017 /* keystore subsystem */ +#define AID_USB 1018 /* USB devices */ +#define AID_DRM 1019 /* DRM server */ +#define AID_MDNSR 1020 /* MulticastDNSResponder (service discovery) */ +#define AID_GPS 1021 /* GPS daemon */ +#define AID_UNUSED1 1022 /* deprecated, DO NOT USE */ +#define AID_MEDIA_RW 1023 /* internal media storage write access */ +#define AID_MTP 1024 /* MTP USB driver access */ +#define AID_UNUSED2 1025 /* deprecated, DO NOT USE */ +#define AID_DRMRPC 1026 /* group for drm rpc */ +#define AID_NFC 1027 /* nfc subsystem */ +#define AID_SDCARD_R 1028 /* external storage read access */ +#define AID_CLAT 1029 /* clat part of nat464 */ +#define AID_LOOP_RADIO 1030 /* loop radio devices */ +#define AID_MEDIA_DRM 1031 /* MediaDrm plugins */ +#define AID_PACKAGE_INFO 1032 /* access to installed package details */ +#define AID_SDCARD_PICS 1033 /* external storage photos access */ +#define AID_SDCARD_AV 1034 /* external storage audio/video access */ +#define AID_SDCARD_ALL 1035 /* access all users external storage */ +#define AID_LOGD 1036 /* log daemon */ +#define AID_SHARED_RELRO 1037 /* creator of shared GNU RELRO files */ +#define AID_DBUS 1038 /* dbus-daemon IPC broker process */ +#define AID_TLSDATE 1039 /* tlsdate unprivileged user */ +#define AID_MEDIA_EX 1040 /* mediaextractor process */ +#define AID_AUDIOSERVER 1041 /* audioserver process */ +#define AID_METRICS_COLL 1042 /* metrics_collector process */ +#define AID_METRICSD 1043 /* metricsd process */ +#define AID_WEBSERV 1044 /* webservd process */ +#define AID_DEBUGGERD 1045 /* debuggerd unprivileged user */ +#define AID_MEDIA_CODEC 1046 /* mediacodec process */ +#define AID_CAMERASERVER 1047 /* cameraserver process */ +#define AID_FIREWALL 1048 /* firewalld process */ +#define AID_TRUNKS 1049 /* trunksd process (TPM daemon) */ +#define AID_NVRAM 1050 /* Access-controlled NVRAM */ +#define AID_DNS 1051 /* DNS resolution daemon (system: netd) */ +#define AID_DNS_TETHER 1052 /* DNS resolution daemon (tether: dnsmasq) */ +#define AID_WEBVIEW_ZYGOTE 1053 /* WebView zygote process */ +#define AID_VEHICLE_NETWORK 1054 /* Vehicle network service */ +#define AID_MEDIA_AUDIO 1055 /* GID for audio files on internal media storage */ +#define AID_MEDIA_VIDEO 1056 /* GID for video files on internal media storage */ +#define AID_MEDIA_IMAGE 1057 /* GID for image files on internal media storage */ +#define AID_TOMBSTONED 1058 /* tombstoned user */ +#define AID_MEDIA_OBB 1059 /* GID for OBB files on internal media storage */ +#define AID_ESE 1060 /* embedded secure element (eSE) subsystem */ +#define AID_OTA_UPDATE 1061 /* resource tracking UID for OTA updates */ +#define AID_AUTOMOTIVE_EVS 1062 /* Automotive rear and surround view system */ +#define AID_LOWPAN 1063 /* LoWPAN subsystem */ +#define AID_HSM 1064 /* hardware security module subsystem */ +#define AID_RESERVED_DISK 1065 /* GID that has access to reserved disk space */ +#define AID_STATSD 1066 /* statsd daemon */ +#define AID_INCIDENTD 1067 /* incidentd daemon */ +#define AID_SECURE_ELEMENT 1068 /* secure element subsystem */ +#define AID_LMKD 1069 /* low memory killer daemon */ +#define AID_LLKD 1070 /* live lock daemon */ +#define AID_IORAPD 1071 /* input/output readahead and pin daemon */ +#define AID_GPU_SERVICE 1072 /* GPU service daemon */ +#define AID_NETWORK_STACK 1073 /* network stack service */ +#define AID_GSID 1074 /* GSI service daemon */ +#define AID_FSVERITY_CERT 1075 /* fs-verity key ownership in keystore */ +#define AID_CREDSTORE 1076 /* identity credential manager service */ +#define AID_EXTERNAL_STORAGE 1077 /* Full external storage access including USB OTG volumes */ +#define AID_EXT_DATA_RW 1078 /* GID for app-private data directories on external storage */ +#define AID_EXT_OBB_RW 1079 /* GID for OBB directories on external storage */ +#define AID_CONTEXT_HUB 1080 /* GID for access to the Context Hub */ +/* Changes to this file must be made in AOSP, *not* in internal branches. */ + +#define AID_SHELL 2000 /* adb and debug shell user */ +#define AID_CACHE 2001 /* cache access */ +#define AID_DIAG 2002 /* access to diagnostic resources */ + +/* The range 2900-2999 is reserved for the vendor partition */ +/* Note that the two 'OEM' ranges pre-dated the vendor partition, so they take the legacy 'OEM' + * name. Additionally, they pre-dated passwd/group files, so there are users and groups named oem_# + * created automatically for all values in these ranges. If there is a user/group in a passwd/group + * file corresponding to this range, both the oem_# and user/group names will resolve to the same + * value. */ +#define AID_OEM_RESERVED_START 2900 +#define AID_OEM_RESERVED_END 2999 + +/* The 3000 series are intended for use as supplemental group id's only. + * They indicate special Android capabilities that the kernel is aware of. */ +#define AID_NET_BT_ADMIN 3001 /* bluetooth: create any socket */ +#define AID_NET_BT 3002 /* bluetooth: create sco, rfcomm or l2cap sockets */ +#define AID_INET 3003 /* can create AF_INET and AF_INET6 sockets */ +#define AID_NET_RAW 3004 /* can create raw INET sockets */ +#define AID_NET_ADMIN 3005 /* can configure interfaces and routing tables. */ +#define AID_NET_BW_STATS 3006 /* read bandwidth statistics */ +#define AID_NET_BW_ACCT 3007 /* change bandwidth statistics accounting */ +#define AID_READPROC 3009 /* Allow /proc read access */ +#define AID_WAKELOCK 3010 /* Allow system wakelock read/write access */ +#define AID_UHID 3011 /* Allow read/write to /dev/uhid node */ + +/* The range 5000-5999 is also reserved for vendor partition. */ +#define AID_OEM_RESERVED_2_START 5000 +#define AID_OEM_RESERVED_2_END 5999 + +/* The range 6000-6499 is reserved for the system partition. */ +#define AID_SYSTEM_RESERVED_START 6000 +#define AID_SYSTEM_RESERVED_END 6499 + +/* The range 6500-6999 is reserved for the odm partition. */ +#define AID_ODM_RESERVED_START 6500 +#define AID_ODM_RESERVED_END 6999 + +/* The range 7000-7499 is reserved for the product partition. */ +#define AID_PRODUCT_RESERVED_START 7000 +#define AID_PRODUCT_RESERVED_END 7499 + +/* The range 7500-7999 is reserved for the system_ext partition. */ +#define AID_SYSTEM_EXT_RESERVED_START 7500 +#define AID_SYSTEM_EXT_RESERVED_END 7999 + +#define AID_EVERYBODY 9997 /* shared between all apps in the same profile */ +#define AID_MISC 9998 /* access to misc storage */ +#define AID_NOBODY 9999 + +#define AID_APP 10000 /* TODO: switch users over to AID_APP_START */ +#define AID_APP_START 10000 /* first app user */ +#define AID_APP_END 19999 /* last app user */ + +#define AID_CACHE_GID_START 20000 /* start of gids for apps to mark cached data */ +#define AID_CACHE_GID_END 29999 /* end of gids for apps to mark cached data */ + +#define AID_EXT_GID_START 30000 /* start of gids for apps to mark external data */ +#define AID_EXT_GID_END 39999 /* end of gids for apps to mark external data */ + +#define AID_EXT_CACHE_GID_START 40000 /* start of gids for apps to mark external cached data */ +#define AID_EXT_CACHE_GID_END 49999 /* end of gids for apps to mark external cached data */ + +#define AID_SHARED_GID_START 50000 /* start of gids for apps in each user to share */ +#define AID_SHARED_GID_END 59999 /* end of gids for apps in each user to share */ + +/* + * This is a magic number in the kernel and not something that was picked + * arbitrarily. This value is returned whenever a uid that has no mapping in the + * user namespace is returned to userspace: + * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/linux/highuid.h?h=v4.4#n40 + */ +#define AID_OVERFLOWUID 65534 /* unmapped user in the user namespace */ + +/* use the ranges below to determine whether a process is isolated */ +#define AID_ISOLATED_START 90000 /* start of uids for fully isolated sandboxed processes */ +#define AID_ISOLATED_END 99999 /* end of uids for fully isolated sandboxed processes */ + +#define AID_USER 100000 /* TODO: switch users over to AID_USER_OFFSET */ +#define AID_USER_OFFSET 100000 /* offset for uid ranges for each user */ + +/* + * android_ids has moved to pwd/grp functionality. + * If you need to add one, the structure is now + * auto-generated based on the AID_ constraints + * documented at the top of this header file. + * Also see build/tools/fs_config for more details. + */ diff --git a/aosp/mkbootfs.11/src/mkbootfs/headers/private/fs_config.h b/aosp/mkbootfs.11/src/mkbootfs/headers/private/fs_config.h new file mode 100644 index 0000000..8a9a1ff --- /dev/null +++ b/aosp/mkbootfs.11/src/mkbootfs/headers/private/fs_config.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2007 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. + */ + +/* This file is used to define the properties of the filesystem +** images generated by build tools (mkbootfs and mkyaffs2image) and +** by the device side of adb. +*/ + +#pragma once + +#include +#include + +#if defined(__BIONIC__) +#include +#else // defined(__BIONIC__) +#include +#endif // defined(__BIONIC__) + +/* Rules for directories and files has moved to system/code/libcutils/fs_config.c */ + +__BEGIN_DECLS + +/* + * Used in: + * build/tools/fs_config/fs_config.c + * build/tools/fs_get_stats/fs_get_stats.c + * system/extras/ext4_utils/make_ext4fs_main.c + * external/squashfs-tools/squashfs-tools/android.c + * system/core/cpio/mkbootfs.c + * system/core/adb/file_sync_service.cpp + * system/extras/ext4_utils/canned_fs_config.c + */ +void fs_config(const char* path, int dir, const char* target_out_path, unsigned* uid, unsigned* gid, + unsigned* mode, uint64_t* capabilities); + +__END_DECLS diff --git a/aosp/mkbootfs.11/src/mkbootfs/headers/utils/Compat.h b/aosp/mkbootfs.11/src/mkbootfs/headers/utils/Compat.h new file mode 100644 index 0000000..6002567 --- /dev/null +++ b/aosp/mkbootfs.11/src/mkbootfs/headers/utils/Compat.h @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LIB_UTILS_COMPAT_H +#define __LIB_UTILS_COMPAT_H + +#include + +#if !defined(__MINGW32__) +#include +#endif + +#if defined(__APPLE__) + +/* Mac OS has always had a 64-bit off_t, so it doesn't have off64_t. */ +static_assert(sizeof(off_t) >= 8, "This code requires that Mac OS have at least a 64-bit off_t."); +typedef off_t off64_t; + +static inline void* mmap64(void* addr, size_t length, int prot, int flags, int fd, off64_t offset) { + return mmap(addr, length, prot, flags, fd, offset); +} + +static inline off64_t lseek64(int fd, off64_t offset, int whence) { + return lseek(fd, offset, whence); +} + +static inline ssize_t pread64(int fd, void* buf, size_t nbytes, off64_t offset) { + return pread(fd, buf, nbytes, offset); +} + +static inline ssize_t pwrite64(int fd, const void* buf, size_t nbytes, off64_t offset) { + return pwrite(fd, buf, nbytes, offset); +} + +static inline int ftruncate64(int fd, off64_t length) { + return ftruncate(fd, length); +} + +#endif /* __APPLE__ */ + +#if defined(_WIN32) +#define O_CLOEXEC O_NOINHERIT +#define O_NOFOLLOW 0 +#define DEFFILEMODE 0666 +#endif /* _WIN32 */ + +#define ZD "%zd" +#define ZD_TYPE ssize_t + +/* + * Needed for cases where something should be constexpr if possible, but not + * being constexpr is fine if in pre-C++11 code (such as a const static float + * member variable). + */ +#if __cplusplus >= 201103L +#define CONSTEXPR constexpr +#else +#define CONSTEXPR +#endif + +/* + * TEMP_FAILURE_RETRY is defined by some, but not all, versions of + * . (Alas, it is not as standard as we'd hoped!) So, if it's + * not already defined, then define it here. + */ +#ifndef TEMP_FAILURE_RETRY +/* Used to retry syscalls that can return EINTR. */ +#define TEMP_FAILURE_RETRY(exp) ({ \ + typeof (exp) _rc; \ + do { \ + _rc = (exp); \ + } while (_rc == -1 && errno == EINTR); \ + _rc; }) +#endif + +#if defined(_WIN32) +#define OS_PATH_SEPARATOR '\\' +#else +#define OS_PATH_SEPARATOR '/' +#endif + +#endif /* __LIB_UTILS_COMPAT_H */ diff --git a/avbImpl/build.gradle b/avbImpl/build.gradle index 84bdb2e..2d8616f 100644 --- a/avbImpl/build.gradle +++ b/avbImpl/build.gradle @@ -11,7 +11,7 @@ model { avbx(NativeLibrarySpec) { sources { cpp { - lib project: ":aosp:libavb", library: "avb" + lib project: ":aosp:libavb1.2", library: "avb" } } binaries.all { @@ -23,7 +23,7 @@ model { avbVerifier(NativeExecutableSpec) { sources { cpp { - lib project: ":aosp:libavb", library: "avb", linkage: "static" + lib project: ":aosp:libavb1.2", library: "avb", linkage: "static" lib project: ":avbImpl", library: "avbx", linkage: "static" } } diff --git a/bbootimg/src/main/kotlin/bootimg/Common.kt b/bbootimg/src/main/kotlin/bootimg/Common.kt index b78e5c8..980d578 100644 --- a/bbootimg/src/main/kotlin/bootimg/Common.kt +++ b/bbootimg/src/main/kotlin/bootimg/Common.kt @@ -175,14 +175,14 @@ class Common { } } - fun packRootfs(rootDir: String, ramdiskGz: String) { - val mkbootfs = Helper.prop("mkbootfsBin") + fun packRootfs(rootDir: String, ramdiskGz: String, osMajor: Int = 10) { + val mkbootfs = String.format(Helper.prop("mkbootfsBin"), osMajor) log.info("Packing rootfs $rootDir ...") val outputStream = ByteArrayOutputStream() DefaultExecutor().let { exec -> exec.streamHandler = PumpStreamHandler(outputStream) val cmdline = "$mkbootfs $rootDir" - log.info(cmdline) + log.info("CMD: $cmdline -> PIPE -> $ramdiskGz") exec.execute(CommandLine.parse(cmdline)) } Helper.gnuZipFile2(ramdiskGz, ByteArrayInputStream(outputStream.toByteArray())) diff --git a/bbootimg/src/main/kotlin/bootimg/v2/BootV2.kt b/bbootimg/src/main/kotlin/bootimg/v2/BootV2.kt index 9ec43b9..e3eb12f 100644 --- a/bbootimg/src/main/kotlin/bootimg/v2/BootV2.kt +++ b/bbootimg/src/main/kotlin/bootimg/v2/BootV2.kt @@ -302,6 +302,16 @@ data class BootV2( ) } + fun parseOsMajor(): Int { + return try { + log.info("OS Major: " + (info.osVersion?.split(".")?.get(0) ?: "null")) + val ret = Integer.parseInt(info.osVersion!!.split(".")[0]) + if (ret <= 10) 10 else ret + } catch (e: Exception) { + 10 + } + } + fun pack(): BootV2 { //refresh kernel size this.kernel.size = File(this.kernel.file!!).length().toInt() @@ -316,7 +326,7 @@ data class BootV2( } else { File(this.ramdisk.file!!).deleleIfExists() File(this.ramdisk.file!!.removeSuffix(".gz")).deleleIfExists() - Common.packRootfs("${workDir}/root", this.ramdisk.file!!) + Common.packRootfs("${workDir}/root", this.ramdisk.file!!, parseOsMajor()) } this.ramdisk.size = File(this.ramdisk.file!!).length().toInt() } diff --git a/bbootimg/src/main/kotlin/bootimg/v3/BootV3.kt b/bbootimg/src/main/kotlin/bootimg/v3/BootV3.kt index f1fab21..43fd6d7 100644 --- a/bbootimg/src/main/kotlin/bootimg/v3/BootV3.kt +++ b/bbootimg/src/main/kotlin/bootimg/v3/BootV3.kt @@ -72,6 +72,15 @@ data class BootV3(var info: MiscInfo = MiscInfo(), var position: Int = 0, var size: Int = 0) + fun parseOsMajor(): Int { + return try { + log.info("OS Major: " + info.osVersion.split(".")[0]) + Integer.parseInt(info.osVersion.split(".")[0]) + } catch (e: Exception) { + 11 + } + } + fun pack(): BootV3 { if (File( this.ramdisk.file).exists() && !File(workDir + "root").exists()) { //do nothing if we have ramdisk.img.gz but no /root @@ -79,7 +88,7 @@ data class BootV3(var info: MiscInfo = MiscInfo(), } else { File(this.ramdisk.file).deleleIfExists() File(this.ramdisk.file.removeSuffix(".gz")).deleleIfExists() - C.packRootfs("$workDir/root", this.ramdisk.file) + C.packRootfs("$workDir/root", this.ramdisk.file, parseOsMajor()) } this.kernel.size = File(this.kernel.file).length().toInt() this.ramdisk.size = File( this.ramdisk.file).length().toInt() diff --git a/bbootimg/src/main/kotlin/bootimg/v3/VendorBoot.kt b/bbootimg/src/main/kotlin/bootimg/v3/VendorBoot.kt index cfe413e..bb3678f 100644 --- a/bbootimg/src/main/kotlin/bootimg/v3/VendorBoot.kt +++ b/bbootimg/src/main/kotlin/bootimg/v3/VendorBoot.kt @@ -75,6 +75,10 @@ data class VendorBoot(var info: MiscInfo = MiscInfo(), } } + private fun parseOsMajor(): Int { + return 11 + } + fun pack(): VendorBoot { val workDir = Helper.prop("workDir") if (File(workDir + this.ramdisk.file).exists() && !File(workDir + "root").exists()) { @@ -83,7 +87,7 @@ data class VendorBoot(var info: MiscInfo = MiscInfo(), } else { File(this.ramdisk.file).deleleIfExists() File(this.ramdisk.file.removeSuffix(".gz")).deleleIfExists() - C.packRootfs("$workDir/root", this.ramdisk.file) + C.packRootfs("$workDir/root", this.ramdisk.file, parseOsMajor()) } this.ramdisk.size = File(this.ramdisk.file).length().toUInt() this.dtb.size = File(this.dtb.file).length().toUInt() @@ -152,7 +156,11 @@ data class VendorBoot(var info: MiscInfo = MiscInfo(), } fun extractVBMeta(): VendorBoot { - Avb().parseVbMeta(info.output) + try { + Avb().parseVbMeta(info.output) + } catch (e: Exception) { + log.error("extraceVBMeta(): $e") + } if (File("vbmeta.img").exists()) { log.warn("Found vbmeta.img, parsing ...") VBMetaParser().unpack("vbmeta.img") @@ -200,14 +208,15 @@ data class VendorBoot(var info: MiscInfo = MiscInfo(), private fun toCommandLine(): CommandLine { return CommandLine(Helper.prop("mkbootimg")) - .addArgument("--vendor_ramdisk") - .addArgument(this.ramdisk.file) - .addArgument("--dtb") - .addArgument(this.dtb.file) - .addArgument("--vendor_cmdline") - .addArgument(info.cmdline, false) - .addArgument("--header_version") - .addArgument(info.headerVersion.toString()) + .addArgument("--vendor_ramdisk").addArgument(this.ramdisk.file) + .addArgument("--dtb").addArgument(this.dtb.file) + .addArgument("--vendor_cmdline").addArgument(info.cmdline, false) + .addArgument("--header_version").addArgument(info.headerVersion.toString()) + .addArgument("--base").addArgument("0") + .addArgument("--tags_offset").addArgument(info.tagsLoadAddr.toString()) + .addArgument("--kernel_offset").addArgument(info.kernelLoadAddr.toString()) + .addArgument("--ramdisk_offset").addArgument(ramdisk.loadAddr.toString()) + .addArgument("--dtb_offset").addArgument(dtb.loadAddr.toString()) .addArgument("--vendor_boot") } } diff --git a/bbootimg/src/main/resources/general.cfg b/bbootimg/src/main/resources/general.cfg index 458aaf3..af40d48 100644 --- a/bbootimg/src/main/resources/general.cfg +++ b/bbootimg/src/main/resources/general.cfg @@ -1,5 +1,5 @@ workDir = build/unzip_boot/ -mkbootfsBin = aosp/mkbootfs/build/exe/mkbootfs/mkbootfs +mkbootfsBin = aosp/mkbootfs.%d/build/exe/mkbootfs/mkbootfs avbtool = aosp/avb/avbtool kernelExtracter = aosp/build/tools/extract_kernel.py bootSigner = aosp/boot_signer/build/libs/boot_signer.jar diff --git a/build.gradle.kts b/build.gradle.kts index 0ef61f0..ac77985 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -62,8 +62,10 @@ tasks { flashTask.dependsOn("bbootimg:jar") //sparse image dependencies - packTask.dependsOn("aosp:mkbootfs:mkbootfsExecutable") - unpackTask.dependsOn("aosp:mkbootfs:mkbootfsExecutable") + packTask.dependsOn("aosp:mkbootfs.10:mkbootfsExecutable") + packTask.dependsOn("aosp:mkbootfs.11:mkbootfsExecutable") + unpackTask.dependsOn("aosp:mkbootfs.10:mkbootfsExecutable") + unpackTask.dependsOn("aosp:mkbootfs.11:mkbootfsExecutable") if (System.getProperty("os.name").contains("Mac")) { unpackTask.dependsOn("aosp:libsparse:simg2img:installReleaseMacos") packTask.dependsOn("aosp:libsparse:img2simg:installReleaseMacos") diff --git a/integrationTest.py b/integrationTest.py index 84ecf24..1e579ea 100755 --- a/integrationTest.py +++ b/integrationTest.py @@ -42,6 +42,11 @@ def cleanUp(): deleteIfExists("recovery.img.signed2") deleteIfExists("vbmeta.img") deleteIfExists("vbmeta.img.signed") + deleteIfExists("vendor_boot.img") + deleteIfExists("vendor_boot.img.clear") + deleteIfExists("vendor_boot.img.google") + deleteIfExists("vendor_boot.img.signed") + deleteIfExists("vendor_boot.img.signed2") def verifySingleJson(jsonFile): log.info(jsonFile) diff --git a/settings.gradle.kts b/settings.gradle.kts index 1485b37..4c48ece 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,11 +1,13 @@ include("bbootimg") include("aosp:boot_signer") -include("aosp:mkbootfs") +include("aosp:mkbootfs.10") +include("aosp:mkbootfs.11") include("aosp:libsparse:base") include("aosp:libsparse:sparse") include("aosp:libsparse:img2simg") include("aosp:libsparse:simg2img") include("aosp:libsparse:simg2simg") include("aosp:libsparse:append2simg") -include("aosp:libavb") +include("aosp:libavb1.1") +include("aosp:libavb1.2") include("avbImpl")