1*b6932704SJoseph Chen#!/usr/bin/env python3 2*b6932704SJoseph Chen 3*b6932704SJoseph Chen# Copyright 2016, The Android Open Source Project 4*b6932704SJoseph Chen# 5*b6932704SJoseph Chen# Permission is hereby granted, free of charge, to any person 6*b6932704SJoseph Chen# obtaining a copy of this software and associated documentation 7*b6932704SJoseph Chen# files (the "Software"), to deal in the Software without 8*b6932704SJoseph Chen# restriction, including without limitation the rights to use, copy, 9*b6932704SJoseph Chen# modify, merge, publish, distribute, sublicense, and/or sell copies 10*b6932704SJoseph Chen# of the Software, and to permit persons to whom the Software is 11*b6932704SJoseph Chen# furnished to do so, subject to the following conditions: 12*b6932704SJoseph Chen# 13*b6932704SJoseph Chen# The above copyright notice and this permission notice shall be 14*b6932704SJoseph Chen# included in all copies or substantial portions of the Software. 15*b6932704SJoseph Chen# 16*b6932704SJoseph Chen# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17*b6932704SJoseph Chen# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18*b6932704SJoseph Chen# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19*b6932704SJoseph Chen# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20*b6932704SJoseph Chen# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21*b6932704SJoseph Chen# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22*b6932704SJoseph Chen# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23*b6932704SJoseph Chen# SOFTWARE. 24*b6932704SJoseph Chen# 25*b6932704SJoseph Chen"""Command-line tool for working with Android Verified Boot images.""" 26*b6932704SJoseph Chen 27*b6932704SJoseph Chenimport argparse 28*b6932704SJoseph Chenimport binascii 29*b6932704SJoseph Chenimport bisect 30*b6932704SJoseph Chenimport hashlib 31*b6932704SJoseph Chenimport json 32*b6932704SJoseph Chenimport math 33*b6932704SJoseph Chenimport os 34*b6932704SJoseph Chenimport struct 35*b6932704SJoseph Chenimport subprocess 36*b6932704SJoseph Chenimport sys 37*b6932704SJoseph Chenimport tempfile 38*b6932704SJoseph Chenimport time 39*b6932704SJoseph Chen 40*b6932704SJoseph Chen# Keep in sync with libavb/avb_version.h. 41*b6932704SJoseph ChenAVB_VERSION_MAJOR = 1 42*b6932704SJoseph ChenAVB_VERSION_MINOR = 2 43*b6932704SJoseph ChenAVB_VERSION_SUB = 0 44*b6932704SJoseph Chen 45*b6932704SJoseph Chen# Keep in sync with libavb/avb_footer.h. 46*b6932704SJoseph ChenAVB_FOOTER_VERSION_MAJOR = 1 47*b6932704SJoseph ChenAVB_FOOTER_VERSION_MINOR = 0 48*b6932704SJoseph Chen 49*b6932704SJoseph ChenAVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED = 1 50*b6932704SJoseph Chen 51*b6932704SJoseph Chen# Configuration for enabling logging of calls to avbtool. 52*b6932704SJoseph ChenAVB_INVOCATION_LOGFILE = os.environ.get('AVB_INVOCATION_LOGFILE') 53*b6932704SJoseph Chen 54*b6932704SJoseph Chen 55*b6932704SJoseph Chenclass AvbError(Exception): 56*b6932704SJoseph Chen """Application-specific errors. 57*b6932704SJoseph Chen 58*b6932704SJoseph Chen These errors represent issues for which a stack-trace should not be 59*b6932704SJoseph Chen presented. 60*b6932704SJoseph Chen 61*b6932704SJoseph Chen Attributes: 62*b6932704SJoseph Chen message: Error message. 63*b6932704SJoseph Chen """ 64*b6932704SJoseph Chen 65*b6932704SJoseph Chen def __init__(self, message): 66*b6932704SJoseph Chen Exception.__init__(self, message) 67*b6932704SJoseph Chen 68*b6932704SJoseph Chen 69*b6932704SJoseph Chenclass Algorithm(object): 70*b6932704SJoseph Chen """Contains details about an algorithm. 71*b6932704SJoseph Chen 72*b6932704SJoseph Chen See the avb_vbmeta_image.h file for more details about algorithms. 73*b6932704SJoseph Chen 74*b6932704SJoseph Chen The constant |ALGORITHMS| is a dictionary from human-readable 75*b6932704SJoseph Chen names (e.g 'SHA256_RSA2048') to instances of this class. 76*b6932704SJoseph Chen 77*b6932704SJoseph Chen Attributes: 78*b6932704SJoseph Chen algorithm_type: Integer code corresponding to |AvbAlgorithmType|. 79*b6932704SJoseph Chen hash_name: Empty or a name from |hashlib.algorithms|. 80*b6932704SJoseph Chen hash_num_bytes: Number of bytes used to store the hash. 81*b6932704SJoseph Chen signature_num_bytes: Number of bytes used to store the signature. 82*b6932704SJoseph Chen public_key_num_bytes: Number of bytes used to store the public key. 83*b6932704SJoseph Chen padding: Padding used for signature as bytes, if any. 84*b6932704SJoseph Chen """ 85*b6932704SJoseph Chen 86*b6932704SJoseph Chen def __init__(self, algorithm_type, hash_name, hash_num_bytes, 87*b6932704SJoseph Chen signature_num_bytes, public_key_num_bytes, padding): 88*b6932704SJoseph Chen self.algorithm_type = algorithm_type 89*b6932704SJoseph Chen self.hash_name = hash_name 90*b6932704SJoseph Chen self.hash_num_bytes = hash_num_bytes 91*b6932704SJoseph Chen self.signature_num_bytes = signature_num_bytes 92*b6932704SJoseph Chen self.public_key_num_bytes = public_key_num_bytes 93*b6932704SJoseph Chen self.padding = padding 94*b6932704SJoseph Chen 95*b6932704SJoseph Chen 96*b6932704SJoseph Chen# This must be kept in sync with the avb_crypto.h file. 97*b6932704SJoseph Chen# 98*b6932704SJoseph Chen# The PKC1-v1.5 padding is a blob of binary DER of ASN.1 and is 99*b6932704SJoseph Chen# obtained from section 5.2.2 of RFC 4880. 100*b6932704SJoseph ChenALGORITHMS = { 101*b6932704SJoseph Chen 'NONE': Algorithm( 102*b6932704SJoseph Chen algorithm_type=0, # AVB_ALGORITHM_TYPE_NONE 103*b6932704SJoseph Chen hash_name='', 104*b6932704SJoseph Chen hash_num_bytes=0, 105*b6932704SJoseph Chen signature_num_bytes=0, 106*b6932704SJoseph Chen public_key_num_bytes=0, 107*b6932704SJoseph Chen padding=b''), 108*b6932704SJoseph Chen 'SHA256_RSA2048': Algorithm( 109*b6932704SJoseph Chen algorithm_type=1, # AVB_ALGORITHM_TYPE_SHA256_RSA2048 110*b6932704SJoseph Chen hash_name='sha256', 111*b6932704SJoseph Chen hash_num_bytes=32, 112*b6932704SJoseph Chen signature_num_bytes=256, 113*b6932704SJoseph Chen public_key_num_bytes=8 + 2*2048//8, 114*b6932704SJoseph Chen padding=bytes(bytearray([ 115*b6932704SJoseph Chen # PKCS1-v1_5 padding 116*b6932704SJoseph Chen 0x00, 0x01] + [0xff]*202 + [0x00] + [ 117*b6932704SJoseph Chen # ASN.1 header 118*b6932704SJoseph Chen 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 119*b6932704SJoseph Chen 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 120*b6932704SJoseph Chen 0x00, 0x04, 0x20, 121*b6932704SJoseph Chen ]))), 122*b6932704SJoseph Chen 'SHA256_RSA4096': Algorithm( 123*b6932704SJoseph Chen algorithm_type=2, # AVB_ALGORITHM_TYPE_SHA256_RSA4096 124*b6932704SJoseph Chen hash_name='sha256', 125*b6932704SJoseph Chen hash_num_bytes=32, 126*b6932704SJoseph Chen signature_num_bytes=512, 127*b6932704SJoseph Chen public_key_num_bytes=8 + 2*4096//8, 128*b6932704SJoseph Chen padding=bytes(bytearray([ 129*b6932704SJoseph Chen # PKCS1-v1_5 padding 130*b6932704SJoseph Chen 0x00, 0x01] + [0xff]*458 + [0x00] + [ 131*b6932704SJoseph Chen # ASN.1 header 132*b6932704SJoseph Chen 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 133*b6932704SJoseph Chen 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 134*b6932704SJoseph Chen 0x00, 0x04, 0x20, 135*b6932704SJoseph Chen ]))), 136*b6932704SJoseph Chen 'SHA256_RSA8192': Algorithm( 137*b6932704SJoseph Chen algorithm_type=3, # AVB_ALGORITHM_TYPE_SHA256_RSA8192 138*b6932704SJoseph Chen hash_name='sha256', 139*b6932704SJoseph Chen hash_num_bytes=32, 140*b6932704SJoseph Chen signature_num_bytes=1024, 141*b6932704SJoseph Chen public_key_num_bytes=8 + 2*8192//8, 142*b6932704SJoseph Chen padding=bytes(bytearray([ 143*b6932704SJoseph Chen # PKCS1-v1_5 padding 144*b6932704SJoseph Chen 0x00, 0x01] + [0xff]*970 + [0x00] + [ 145*b6932704SJoseph Chen # ASN.1 header 146*b6932704SJoseph Chen 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 147*b6932704SJoseph Chen 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 148*b6932704SJoseph Chen 0x00, 0x04, 0x20, 149*b6932704SJoseph Chen ]))), 150*b6932704SJoseph Chen 'SHA512_RSA2048': Algorithm( 151*b6932704SJoseph Chen algorithm_type=4, # AVB_ALGORITHM_TYPE_SHA512_RSA2048 152*b6932704SJoseph Chen hash_name='sha512', 153*b6932704SJoseph Chen hash_num_bytes=64, 154*b6932704SJoseph Chen signature_num_bytes=256, 155*b6932704SJoseph Chen public_key_num_bytes=8 + 2*2048//8, 156*b6932704SJoseph Chen padding=bytes(bytearray([ 157*b6932704SJoseph Chen # PKCS1-v1_5 padding 158*b6932704SJoseph Chen 0x00, 0x01] + [0xff]*170 + [0x00] + [ 159*b6932704SJoseph Chen # ASN.1 header 160*b6932704SJoseph Chen 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 161*b6932704SJoseph Chen 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 162*b6932704SJoseph Chen 0x00, 0x04, 0x40 163*b6932704SJoseph Chen ]))), 164*b6932704SJoseph Chen 'SHA512_RSA4096': Algorithm( 165*b6932704SJoseph Chen algorithm_type=5, # AVB_ALGORITHM_TYPE_SHA512_RSA4096 166*b6932704SJoseph Chen hash_name='sha512', 167*b6932704SJoseph Chen hash_num_bytes=64, 168*b6932704SJoseph Chen signature_num_bytes=512, 169*b6932704SJoseph Chen public_key_num_bytes=8 + 2*4096//8, 170*b6932704SJoseph Chen padding=bytes(bytearray([ 171*b6932704SJoseph Chen # PKCS1-v1_5 padding 172*b6932704SJoseph Chen 0x00, 0x01] + [0xff]*426 + [0x00] + [ 173*b6932704SJoseph Chen # ASN.1 header 174*b6932704SJoseph Chen 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 175*b6932704SJoseph Chen 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 176*b6932704SJoseph Chen 0x00, 0x04, 0x40 177*b6932704SJoseph Chen ]))), 178*b6932704SJoseph Chen 'SHA512_RSA8192': Algorithm( 179*b6932704SJoseph Chen algorithm_type=6, # AVB_ALGORITHM_TYPE_SHA512_RSA8192 180*b6932704SJoseph Chen hash_name='sha512', 181*b6932704SJoseph Chen hash_num_bytes=64, 182*b6932704SJoseph Chen signature_num_bytes=1024, 183*b6932704SJoseph Chen public_key_num_bytes=8 + 2*8192//8, 184*b6932704SJoseph Chen padding=bytes(bytearray([ 185*b6932704SJoseph Chen # PKCS1-v1_5 padding 186*b6932704SJoseph Chen 0x00, 0x01] + [0xff]*938 + [0x00] + [ 187*b6932704SJoseph Chen # ASN.1 header 188*b6932704SJoseph Chen 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 189*b6932704SJoseph Chen 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 190*b6932704SJoseph Chen 0x00, 0x04, 0x40 191*b6932704SJoseph Chen ]))), 192*b6932704SJoseph Chen} 193*b6932704SJoseph Chen 194*b6932704SJoseph Chen 195*b6932704SJoseph Chendef get_release_string(): 196*b6932704SJoseph Chen """Calculates the release string to use in the VBMeta struct.""" 197*b6932704SJoseph Chen # Keep in sync with libavb/avb_version.c:avb_version_string(). 198*b6932704SJoseph Chen return 'avbtool {}.{}.{}'.format(AVB_VERSION_MAJOR, 199*b6932704SJoseph Chen AVB_VERSION_MINOR, 200*b6932704SJoseph Chen AVB_VERSION_SUB) 201*b6932704SJoseph Chen 202*b6932704SJoseph Chen 203*b6932704SJoseph Chendef round_to_multiple(number, size): 204*b6932704SJoseph Chen """Rounds a number up to nearest multiple of another number. 205*b6932704SJoseph Chen 206*b6932704SJoseph Chen Arguments: 207*b6932704SJoseph Chen number: The number to round up. 208*b6932704SJoseph Chen size: The multiple to round up to. 209*b6932704SJoseph Chen 210*b6932704SJoseph Chen Returns: 211*b6932704SJoseph Chen If |number| is a multiple of |size|, returns |number|, otherwise 212*b6932704SJoseph Chen returns |number| + |size|. 213*b6932704SJoseph Chen """ 214*b6932704SJoseph Chen remainder = number % size 215*b6932704SJoseph Chen if remainder == 0: 216*b6932704SJoseph Chen return number 217*b6932704SJoseph Chen return number + size - remainder 218*b6932704SJoseph Chen 219*b6932704SJoseph Chen 220*b6932704SJoseph Chendef round_to_pow2(number): 221*b6932704SJoseph Chen """Rounds a number up to the next power of 2. 222*b6932704SJoseph Chen 223*b6932704SJoseph Chen Arguments: 224*b6932704SJoseph Chen number: The number to round up. 225*b6932704SJoseph Chen 226*b6932704SJoseph Chen Returns: 227*b6932704SJoseph Chen If |number| is already a power of 2 then |number| is 228*b6932704SJoseph Chen returned. Otherwise the smallest power of 2 greater than |number| 229*b6932704SJoseph Chen is returned. 230*b6932704SJoseph Chen """ 231*b6932704SJoseph Chen return 2**((number - 1).bit_length()) 232*b6932704SJoseph Chen 233*b6932704SJoseph Chen 234*b6932704SJoseph Chendef encode_long(num_bits, value): 235*b6932704SJoseph Chen """Encodes a long to a bytearray() using a given amount of bits. 236*b6932704SJoseph Chen 237*b6932704SJoseph Chen This number is written big-endian, e.g. with the most significant 238*b6932704SJoseph Chen bit first. 239*b6932704SJoseph Chen 240*b6932704SJoseph Chen This is the reverse of decode_long(). 241*b6932704SJoseph Chen 242*b6932704SJoseph Chen Arguments: 243*b6932704SJoseph Chen num_bits: The number of bits to write, e.g. 2048. 244*b6932704SJoseph Chen value: The value to write. 245*b6932704SJoseph Chen 246*b6932704SJoseph Chen Returns: 247*b6932704SJoseph Chen A bytearray() with the encoded long. 248*b6932704SJoseph Chen """ 249*b6932704SJoseph Chen ret = bytearray() 250*b6932704SJoseph Chen for bit_pos in range(num_bits, 0, -8): 251*b6932704SJoseph Chen octet = (value >> (bit_pos - 8)) & 0xff 252*b6932704SJoseph Chen ret.extend(struct.pack('!B', octet)) 253*b6932704SJoseph Chen return ret 254*b6932704SJoseph Chen 255*b6932704SJoseph Chen 256*b6932704SJoseph Chendef decode_long(blob): 257*b6932704SJoseph Chen """Decodes a long from a bytearray() using a given amount of bits. 258*b6932704SJoseph Chen 259*b6932704SJoseph Chen This number is expected to be in big-endian, e.g. with the most 260*b6932704SJoseph Chen significant bit first. 261*b6932704SJoseph Chen 262*b6932704SJoseph Chen This is the reverse of encode_long(). 263*b6932704SJoseph Chen 264*b6932704SJoseph Chen Arguments: 265*b6932704SJoseph Chen blob: A bytearray() with the encoded long. 266*b6932704SJoseph Chen 267*b6932704SJoseph Chen Returns: 268*b6932704SJoseph Chen The decoded value. 269*b6932704SJoseph Chen """ 270*b6932704SJoseph Chen ret = 0 271*b6932704SJoseph Chen for b in bytearray(blob): 272*b6932704SJoseph Chen ret *= 256 273*b6932704SJoseph Chen ret += b 274*b6932704SJoseph Chen return ret 275*b6932704SJoseph Chen 276*b6932704SJoseph Chen 277*b6932704SJoseph Chendef egcd(a, b): 278*b6932704SJoseph Chen """Calculate greatest common divisor of two numbers. 279*b6932704SJoseph Chen 280*b6932704SJoseph Chen This implementation uses a recursive version of the extended 281*b6932704SJoseph Chen Euclidian algorithm. 282*b6932704SJoseph Chen 283*b6932704SJoseph Chen Arguments: 284*b6932704SJoseph Chen a: First number. 285*b6932704SJoseph Chen b: Second number. 286*b6932704SJoseph Chen 287*b6932704SJoseph Chen Returns: 288*b6932704SJoseph Chen A tuple (gcd, x, y) that where |gcd| is the greatest common 289*b6932704SJoseph Chen divisor of |a| and |b| and |a|*|x| + |b|*|y| = |gcd|. 290*b6932704SJoseph Chen """ 291*b6932704SJoseph Chen if a == 0: 292*b6932704SJoseph Chen return (b, 0, 1) 293*b6932704SJoseph Chen g, y, x = egcd(b % a, a) 294*b6932704SJoseph Chen return (g, x - (b // a) * y, y) 295*b6932704SJoseph Chen 296*b6932704SJoseph Chen 297*b6932704SJoseph Chendef modinv(a, m): 298*b6932704SJoseph Chen """Calculate modular multiplicative inverse of |a| modulo |m|. 299*b6932704SJoseph Chen 300*b6932704SJoseph Chen This calculates the number |x| such that |a| * |x| == 1 (modulo 301*b6932704SJoseph Chen |m|). This number only exists if |a| and |m| are co-prime - |None| 302*b6932704SJoseph Chen is returned if this isn't true. 303*b6932704SJoseph Chen 304*b6932704SJoseph Chen Arguments: 305*b6932704SJoseph Chen a: The number to calculate a modular inverse of. 306*b6932704SJoseph Chen m: The modulo to use. 307*b6932704SJoseph Chen 308*b6932704SJoseph Chen Returns: 309*b6932704SJoseph Chen The modular multiplicative inverse of |a| and |m| or |None| if 310*b6932704SJoseph Chen these numbers are not co-prime. 311*b6932704SJoseph Chen """ 312*b6932704SJoseph Chen gcd, x, _ = egcd(a, m) 313*b6932704SJoseph Chen if gcd != 1: 314*b6932704SJoseph Chen return None # modular inverse does not exist 315*b6932704SJoseph Chen return x % m 316*b6932704SJoseph Chen 317*b6932704SJoseph Chen 318*b6932704SJoseph Chendef parse_number(string): 319*b6932704SJoseph Chen """Parse a string as a number. 320*b6932704SJoseph Chen 321*b6932704SJoseph Chen This is just a short-hand for int(string, 0) suitable for use in the 322*b6932704SJoseph Chen |type| parameter of |ArgumentParser|'s add_argument() function. An 323*b6932704SJoseph Chen improvement to just using type=int is that this function supports 324*b6932704SJoseph Chen numbers in other bases, e.g. "0x1234". 325*b6932704SJoseph Chen 326*b6932704SJoseph Chen Arguments: 327*b6932704SJoseph Chen string: The string to parse. 328*b6932704SJoseph Chen 329*b6932704SJoseph Chen Returns: 330*b6932704SJoseph Chen The parsed integer. 331*b6932704SJoseph Chen 332*b6932704SJoseph Chen Raises: 333*b6932704SJoseph Chen ValueError: If the number could not be parsed. 334*b6932704SJoseph Chen """ 335*b6932704SJoseph Chen return int(string, 0) 336*b6932704SJoseph Chen 337*b6932704SJoseph Chen 338*b6932704SJoseph Chenclass RSAPublicKey(object): 339*b6932704SJoseph Chen """Data structure used for a RSA public key. 340*b6932704SJoseph Chen 341*b6932704SJoseph Chen Attributes: 342*b6932704SJoseph Chen exponent: The key exponent. 343*b6932704SJoseph Chen modulus: The key modulus. 344*b6932704SJoseph Chen num_bits: The key size. 345*b6932704SJoseph Chen key_path: The path to a key file. 346*b6932704SJoseph Chen """ 347*b6932704SJoseph Chen 348*b6932704SJoseph Chen MODULUS_PREFIX = b'modulus=' 349*b6932704SJoseph Chen 350*b6932704SJoseph Chen def __init__(self, key_path): 351*b6932704SJoseph Chen """Loads and parses an RSA key from either a private or public key file. 352*b6932704SJoseph Chen 353*b6932704SJoseph Chen Arguments: 354*b6932704SJoseph Chen key_path: The path to a key file. 355*b6932704SJoseph Chen 356*b6932704SJoseph Chen Raises: 357*b6932704SJoseph Chen AvbError: If RSA key parameters could not be read from file. 358*b6932704SJoseph Chen """ 359*b6932704SJoseph Chen # We used to have something as simple as this: 360*b6932704SJoseph Chen # 361*b6932704SJoseph Chen # key = Crypto.PublicKey.RSA.importKey(open(key_path).read()) 362*b6932704SJoseph Chen # self.exponent = key.e 363*b6932704SJoseph Chen # self.modulus = key.n 364*b6932704SJoseph Chen # self.num_bits = key.size() + 1 365*b6932704SJoseph Chen # 366*b6932704SJoseph Chen # but unfortunately PyCrypto is not available in the builder. So 367*b6932704SJoseph Chen # instead just parse openssl(1) output to get this 368*b6932704SJoseph Chen # information. It's ugly but... 369*b6932704SJoseph Chen args = ['openssl', 'rsa', '-in', key_path, '-modulus', '-noout'] 370*b6932704SJoseph Chen p = subprocess.Popen(args, 371*b6932704SJoseph Chen stdin=subprocess.PIPE, 372*b6932704SJoseph Chen stdout=subprocess.PIPE, 373*b6932704SJoseph Chen stderr=subprocess.PIPE) 374*b6932704SJoseph Chen (pout, perr) = p.communicate() 375*b6932704SJoseph Chen if p.wait() != 0: 376*b6932704SJoseph Chen # Could be just a public key is passed, try that. 377*b6932704SJoseph Chen args.append('-pubin') 378*b6932704SJoseph Chen p = subprocess.Popen(args, 379*b6932704SJoseph Chen stdin=subprocess.PIPE, 380*b6932704SJoseph Chen stdout=subprocess.PIPE, 381*b6932704SJoseph Chen stderr=subprocess.PIPE) 382*b6932704SJoseph Chen (pout, perr) = p.communicate() 383*b6932704SJoseph Chen if p.wait() != 0: 384*b6932704SJoseph Chen raise AvbError('Error getting public key: {}'.format(perr)) 385*b6932704SJoseph Chen 386*b6932704SJoseph Chen if not pout.lower().startswith(self.MODULUS_PREFIX): 387*b6932704SJoseph Chen raise AvbError('Unexpected modulus output') 388*b6932704SJoseph Chen 389*b6932704SJoseph Chen modulus_hexstr = pout[len(self.MODULUS_PREFIX):] 390*b6932704SJoseph Chen 391*b6932704SJoseph Chen # The exponent is assumed to always be 65537 and the number of 392*b6932704SJoseph Chen # bits can be derived from the modulus by rounding up to the 393*b6932704SJoseph Chen # nearest power of 2. 394*b6932704SJoseph Chen self.key_path = key_path 395*b6932704SJoseph Chen self.modulus = int(modulus_hexstr, 16) 396*b6932704SJoseph Chen self.num_bits = round_to_pow2(int(math.ceil(math.log(self.modulus, 2)))) 397*b6932704SJoseph Chen self.exponent = 65537 398*b6932704SJoseph Chen 399*b6932704SJoseph Chen def encode(self): 400*b6932704SJoseph Chen """Encodes the public RSA key in |AvbRSAPublicKeyHeader| format. 401*b6932704SJoseph Chen 402*b6932704SJoseph Chen This creates a |AvbRSAPublicKeyHeader| as well as the two large 403*b6932704SJoseph Chen numbers (|key_num_bits| bits long) following it. 404*b6932704SJoseph Chen 405*b6932704SJoseph Chen Returns: 406*b6932704SJoseph Chen The |AvbRSAPublicKeyHeader| followed by two large numbers as bytes. 407*b6932704SJoseph Chen 408*b6932704SJoseph Chen Raises: 409*b6932704SJoseph Chen AvbError: If given RSA key exponent is not 65537. 410*b6932704SJoseph Chen """ 411*b6932704SJoseph Chen if self.exponent != 65537: 412*b6932704SJoseph Chen raise AvbError('Only RSA keys with exponent 65537 are supported.') 413*b6932704SJoseph Chen ret = bytearray() 414*b6932704SJoseph Chen # Calculate n0inv = -1/n[0] (mod 2^32) 415*b6932704SJoseph Chen b = 2 ** 32 416*b6932704SJoseph Chen n0inv = b - modinv(self.modulus, b) 417*b6932704SJoseph Chen # Calculate rr = r^2 (mod N), where r = 2^(# of key bits) 418*b6932704SJoseph Chen r = 2 ** self.modulus.bit_length() 419*b6932704SJoseph Chen rrmodn = r * r % self.modulus 420*b6932704SJoseph Chen ret.extend(struct.pack('!II', self.num_bits, n0inv)) 421*b6932704SJoseph Chen ret.extend(encode_long(self.num_bits, self.modulus)) 422*b6932704SJoseph Chen ret.extend(encode_long(self.num_bits, rrmodn)) 423*b6932704SJoseph Chen return bytes(ret) 424*b6932704SJoseph Chen 425*b6932704SJoseph Chen def sign(self, algorithm_name, data_to_sign, signing_helper=None, 426*b6932704SJoseph Chen signing_helper_with_files=None): 427*b6932704SJoseph Chen """Sign given data using |signing_helper| or openssl. 428*b6932704SJoseph Chen 429*b6932704SJoseph Chen openssl is used if neither the parameters signing_helper nor 430*b6932704SJoseph Chen signing_helper_with_files are given. 431*b6932704SJoseph Chen 432*b6932704SJoseph Chen Arguments: 433*b6932704SJoseph Chen algorithm_name: The algorithm name as per the ALGORITHMS dict. 434*b6932704SJoseph Chen data_to_sign: Data to sign as bytes or bytearray. 435*b6932704SJoseph Chen signing_helper: Program which signs a hash and returns the signature. 436*b6932704SJoseph Chen signing_helper_with_files: Same as signing_helper but uses files instead. 437*b6932704SJoseph Chen 438*b6932704SJoseph Chen Returns: 439*b6932704SJoseph Chen The signature as bytes. 440*b6932704SJoseph Chen 441*b6932704SJoseph Chen Raises: 442*b6932704SJoseph Chen AvbError: If an error occurred during signing. 443*b6932704SJoseph Chen """ 444*b6932704SJoseph Chen # Checks requested algorithm for validity. 445*b6932704SJoseph Chen algorithm = ALGORITHMS.get(algorithm_name) 446*b6932704SJoseph Chen if not algorithm: 447*b6932704SJoseph Chen raise AvbError('Algorithm with name {} is not supported.' 448*b6932704SJoseph Chen .format(algorithm_name)) 449*b6932704SJoseph Chen 450*b6932704SJoseph Chen if self.num_bits != (algorithm.signature_num_bytes * 8): 451*b6932704SJoseph Chen raise AvbError('Key size of key ({} bits) does not match key size ' 452*b6932704SJoseph Chen '({} bits) of given algorithm {}.' 453*b6932704SJoseph Chen .format(self.num_bits, algorithm.signature_num_bytes * 8, 454*b6932704SJoseph Chen algorithm_name)) 455*b6932704SJoseph Chen 456*b6932704SJoseph Chen # Hashes the data. 457*b6932704SJoseph Chen hasher = hashlib.new(algorithm.hash_name) 458*b6932704SJoseph Chen hasher.update(data_to_sign) 459*b6932704SJoseph Chen digest = hasher.digest() 460*b6932704SJoseph Chen 461*b6932704SJoseph Chen # Calculates the signature. 462*b6932704SJoseph Chen padding_and_hash = algorithm.padding + digest 463*b6932704SJoseph Chen p = None 464*b6932704SJoseph Chen if signing_helper_with_files is not None: 465*b6932704SJoseph Chen with tempfile.NamedTemporaryFile() as signing_file: 466*b6932704SJoseph Chen signing_file.write(padding_and_hash) 467*b6932704SJoseph Chen signing_file.flush() 468*b6932704SJoseph Chen p = subprocess.Popen([signing_helper_with_files, algorithm_name, 469*b6932704SJoseph Chen self.key_path, signing_file.name]) 470*b6932704SJoseph Chen retcode = p.wait() 471*b6932704SJoseph Chen if retcode != 0: 472*b6932704SJoseph Chen raise AvbError('Error signing') 473*b6932704SJoseph Chen signing_file.seek(0) 474*b6932704SJoseph Chen signature = signing_file.read() 475*b6932704SJoseph Chen else: 476*b6932704SJoseph Chen if signing_helper is not None: 477*b6932704SJoseph Chen p = subprocess.Popen( 478*b6932704SJoseph Chen [signing_helper, algorithm_name, self.key_path], 479*b6932704SJoseph Chen stdin=subprocess.PIPE, 480*b6932704SJoseph Chen stdout=subprocess.PIPE, 481*b6932704SJoseph Chen stderr=subprocess.PIPE) 482*b6932704SJoseph Chen else: 483*b6932704SJoseph Chen p = subprocess.Popen( 484*b6932704SJoseph Chen ['openssl', 'rsautl', '-sign', '-inkey', self.key_path, '-raw'], 485*b6932704SJoseph Chen stdin=subprocess.PIPE, 486*b6932704SJoseph Chen stdout=subprocess.PIPE, 487*b6932704SJoseph Chen stderr=subprocess.PIPE) 488*b6932704SJoseph Chen (pout, perr) = p.communicate(padding_and_hash) 489*b6932704SJoseph Chen retcode = p.wait() 490*b6932704SJoseph Chen if retcode != 0: 491*b6932704SJoseph Chen raise AvbError('Error signing: {}'.format(perr)) 492*b6932704SJoseph Chen signature = pout 493*b6932704SJoseph Chen if len(signature) != algorithm.signature_num_bytes: 494*b6932704SJoseph Chen raise AvbError('Error signing: Invalid length of signature') 495*b6932704SJoseph Chen return signature 496*b6932704SJoseph Chen 497*b6932704SJoseph Chen 498*b6932704SJoseph Chendef lookup_algorithm_by_type(alg_type): 499*b6932704SJoseph Chen """Looks up algorithm by type. 500*b6932704SJoseph Chen 501*b6932704SJoseph Chen Arguments: 502*b6932704SJoseph Chen alg_type: The integer representing the type. 503*b6932704SJoseph Chen 504*b6932704SJoseph Chen Returns: 505*b6932704SJoseph Chen A tuple with the algorithm name and an |Algorithm| instance. 506*b6932704SJoseph Chen 507*b6932704SJoseph Chen Raises: 508*b6932704SJoseph Chen Exception: If the algorithm cannot be found 509*b6932704SJoseph Chen """ 510*b6932704SJoseph Chen for alg_name in ALGORITHMS: 511*b6932704SJoseph Chen alg_data = ALGORITHMS[alg_name] 512*b6932704SJoseph Chen if alg_data.algorithm_type == alg_type: 513*b6932704SJoseph Chen return (alg_name, alg_data) 514*b6932704SJoseph Chen raise AvbError('Unknown algorithm type {}'.format(alg_type)) 515*b6932704SJoseph Chen 516*b6932704SJoseph Chen 517*b6932704SJoseph Chendef lookup_hash_size_by_type(alg_type): 518*b6932704SJoseph Chen """Looks up hash size by type. 519*b6932704SJoseph Chen 520*b6932704SJoseph Chen Arguments: 521*b6932704SJoseph Chen alg_type: The integer representing the type. 522*b6932704SJoseph Chen 523*b6932704SJoseph Chen Returns: 524*b6932704SJoseph Chen The corresponding hash size. 525*b6932704SJoseph Chen 526*b6932704SJoseph Chen Raises: 527*b6932704SJoseph Chen AvbError: If the algorithm cannot be found. 528*b6932704SJoseph Chen """ 529*b6932704SJoseph Chen for alg_name in ALGORITHMS: 530*b6932704SJoseph Chen alg_data = ALGORITHMS[alg_name] 531*b6932704SJoseph Chen if alg_data.algorithm_type == alg_type: 532*b6932704SJoseph Chen return alg_data.hash_num_bytes 533*b6932704SJoseph Chen raise AvbError('Unsupported algorithm type {}'.format(alg_type)) 534*b6932704SJoseph Chen 535*b6932704SJoseph Chen 536*b6932704SJoseph Chendef verify_vbmeta_signature(vbmeta_header, vbmeta_blob): 537*b6932704SJoseph Chen """Checks that signature in a vbmeta blob was made by the embedded public key. 538*b6932704SJoseph Chen 539*b6932704SJoseph Chen Arguments: 540*b6932704SJoseph Chen vbmeta_header: A AvbVBMetaHeader. 541*b6932704SJoseph Chen vbmeta_blob: The whole vbmeta blob, including the header as bytes or 542*b6932704SJoseph Chen bytearray. 543*b6932704SJoseph Chen 544*b6932704SJoseph Chen Returns: 545*b6932704SJoseph Chen True if the signature is valid and corresponds to the embedded 546*b6932704SJoseph Chen public key. Also returns True if the vbmeta blob is not signed. 547*b6932704SJoseph Chen 548*b6932704SJoseph Chen Raises: 549*b6932704SJoseph Chen AvbError: If there errors calling out to openssl command during 550*b6932704SJoseph Chen signature verification. 551*b6932704SJoseph Chen """ 552*b6932704SJoseph Chen (_, alg) = lookup_algorithm_by_type(vbmeta_header.algorithm_type) 553*b6932704SJoseph Chen if not alg.hash_name: 554*b6932704SJoseph Chen return True 555*b6932704SJoseph Chen header_blob = vbmeta_blob[0:256] 556*b6932704SJoseph Chen auth_offset = 256 557*b6932704SJoseph Chen aux_offset = auth_offset + vbmeta_header.authentication_data_block_size 558*b6932704SJoseph Chen aux_size = vbmeta_header.auxiliary_data_block_size 559*b6932704SJoseph Chen aux_blob = vbmeta_blob[aux_offset:aux_offset + aux_size] 560*b6932704SJoseph Chen pubkey_offset = aux_offset + vbmeta_header.public_key_offset 561*b6932704SJoseph Chen pubkey_size = vbmeta_header.public_key_size 562*b6932704SJoseph Chen pubkey_blob = vbmeta_blob[pubkey_offset:pubkey_offset + pubkey_size] 563*b6932704SJoseph Chen 564*b6932704SJoseph Chen digest_offset = auth_offset + vbmeta_header.hash_offset 565*b6932704SJoseph Chen digest_size = vbmeta_header.hash_size 566*b6932704SJoseph Chen digest_blob = vbmeta_blob[digest_offset:digest_offset + digest_size] 567*b6932704SJoseph Chen 568*b6932704SJoseph Chen sig_offset = auth_offset + vbmeta_header.signature_offset 569*b6932704SJoseph Chen sig_size = vbmeta_header.signature_size 570*b6932704SJoseph Chen sig_blob = vbmeta_blob[sig_offset:sig_offset + sig_size] 571*b6932704SJoseph Chen 572*b6932704SJoseph Chen # Now that we've got the stored digest, public key, and signature 573*b6932704SJoseph Chen # all we need to do is to verify. This is the exactly the same 574*b6932704SJoseph Chen # steps as performed in the avb_vbmeta_image_verify() function in 575*b6932704SJoseph Chen # libavb/avb_vbmeta_image.c. 576*b6932704SJoseph Chen 577*b6932704SJoseph Chen ha = hashlib.new(alg.hash_name) 578*b6932704SJoseph Chen ha.update(header_blob) 579*b6932704SJoseph Chen ha.update(aux_blob) 580*b6932704SJoseph Chen computed_digest = ha.digest() 581*b6932704SJoseph Chen 582*b6932704SJoseph Chen if computed_digest != digest_blob: 583*b6932704SJoseph Chen return False 584*b6932704SJoseph Chen 585*b6932704SJoseph Chen padding_and_digest = alg.padding + computed_digest 586*b6932704SJoseph Chen 587*b6932704SJoseph Chen (num_bits,) = struct.unpack('!I', pubkey_blob[0:4]) 588*b6932704SJoseph Chen modulus_blob = pubkey_blob[8:8 + num_bits//8] 589*b6932704SJoseph Chen modulus = decode_long(modulus_blob) 590*b6932704SJoseph Chen exponent = 65537 591*b6932704SJoseph Chen 592*b6932704SJoseph Chen # We used to have this: 593*b6932704SJoseph Chen # 594*b6932704SJoseph Chen # import Crypto.PublicKey.RSA 595*b6932704SJoseph Chen # key = Crypto.PublicKey.RSA.construct((modulus, long(exponent))) 596*b6932704SJoseph Chen # if not key.verify(decode_long(padding_and_digest), 597*b6932704SJoseph Chen # (decode_long(sig_blob), None)): 598*b6932704SJoseph Chen # return False 599*b6932704SJoseph Chen # return True 600*b6932704SJoseph Chen # 601*b6932704SJoseph Chen # but since 'avbtool verify_image' is used on the builders we don't want 602*b6932704SJoseph Chen # to rely on Crypto.PublicKey.RSA. Instead just use openssl(1) to verify. 603*b6932704SJoseph Chen asn1_str = ('asn1=SEQUENCE:pubkeyinfo\n' 604*b6932704SJoseph Chen '\n' 605*b6932704SJoseph Chen '[pubkeyinfo]\n' 606*b6932704SJoseph Chen 'algorithm=SEQUENCE:rsa_alg\n' 607*b6932704SJoseph Chen 'pubkey=BITWRAP,SEQUENCE:rsapubkey\n' 608*b6932704SJoseph Chen '\n' 609*b6932704SJoseph Chen '[rsa_alg]\n' 610*b6932704SJoseph Chen 'algorithm=OID:rsaEncryption\n' 611*b6932704SJoseph Chen 'parameter=NULL\n' 612*b6932704SJoseph Chen '\n' 613*b6932704SJoseph Chen '[rsapubkey]\n' 614*b6932704SJoseph Chen 'n=INTEGER:{}\n' 615*b6932704SJoseph Chen 'e=INTEGER:{}\n').format(hex(modulus).rstrip('L'), 616*b6932704SJoseph Chen hex(exponent).rstrip('L')) 617*b6932704SJoseph Chen 618*b6932704SJoseph Chen with tempfile.NamedTemporaryFile() as asn1_tmpfile: 619*b6932704SJoseph Chen asn1_tmpfile.write(asn1_str.encode('ascii')) 620*b6932704SJoseph Chen asn1_tmpfile.flush() 621*b6932704SJoseph Chen 622*b6932704SJoseph Chen with tempfile.NamedTemporaryFile() as der_tmpfile: 623*b6932704SJoseph Chen p = subprocess.Popen( 624*b6932704SJoseph Chen ['openssl', 'asn1parse', '-genconf', asn1_tmpfile.name, '-out', 625*b6932704SJoseph Chen der_tmpfile.name, '-noout']) 626*b6932704SJoseph Chen retcode = p.wait() 627*b6932704SJoseph Chen if retcode != 0: 628*b6932704SJoseph Chen raise AvbError('Error generating DER file') 629*b6932704SJoseph Chen 630*b6932704SJoseph Chen p = subprocess.Popen( 631*b6932704SJoseph Chen ['openssl', 'rsautl', '-verify', '-pubin', '-inkey', der_tmpfile.name, 632*b6932704SJoseph Chen '-keyform', 'DER', '-raw'], 633*b6932704SJoseph Chen stdin=subprocess.PIPE, 634*b6932704SJoseph Chen stdout=subprocess.PIPE, 635*b6932704SJoseph Chen stderr=subprocess.PIPE) 636*b6932704SJoseph Chen (pout, perr) = p.communicate(sig_blob) 637*b6932704SJoseph Chen retcode = p.wait() 638*b6932704SJoseph Chen if retcode != 0: 639*b6932704SJoseph Chen raise AvbError('Error verifying data: {}'.format(perr)) 640*b6932704SJoseph Chen if pout != padding_and_digest: 641*b6932704SJoseph Chen sys.stderr.write('Signature not correct\n') 642*b6932704SJoseph Chen return False 643*b6932704SJoseph Chen return True 644*b6932704SJoseph Chen 645*b6932704SJoseph Chen 646*b6932704SJoseph Chendef create_avb_hashtree_hasher(algorithm, salt): 647*b6932704SJoseph Chen """Create the hasher for AVB hashtree based on the input algorithm.""" 648*b6932704SJoseph Chen 649*b6932704SJoseph Chen if algorithm.lower() == 'blake2b-256': 650*b6932704SJoseph Chen return hashlib.new('blake2b', salt, digest_size=32) 651*b6932704SJoseph Chen 652*b6932704SJoseph Chen return hashlib.new(algorithm, salt) 653*b6932704SJoseph Chen 654*b6932704SJoseph Chen 655*b6932704SJoseph Chenclass ImageChunk(object): 656*b6932704SJoseph Chen """Data structure used for representing chunks in Android sparse files. 657*b6932704SJoseph Chen 658*b6932704SJoseph Chen Attributes: 659*b6932704SJoseph Chen chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE. 660*b6932704SJoseph Chen chunk_offset: Offset in the sparse file where this chunk begins. 661*b6932704SJoseph Chen output_offset: Offset in de-sparsified file where output begins. 662*b6932704SJoseph Chen output_size: Number of bytes in output. 663*b6932704SJoseph Chen input_offset: Offset in sparse file for data if TYPE_RAW otherwise None. 664*b6932704SJoseph Chen fill_data: Blob with data to fill if TYPE_FILL otherwise None. 665*b6932704SJoseph Chen """ 666*b6932704SJoseph Chen 667*b6932704SJoseph Chen FORMAT = '<2H2I' 668*b6932704SJoseph Chen TYPE_RAW = 0xcac1 669*b6932704SJoseph Chen TYPE_FILL = 0xcac2 670*b6932704SJoseph Chen TYPE_DONT_CARE = 0xcac3 671*b6932704SJoseph Chen TYPE_CRC32 = 0xcac4 672*b6932704SJoseph Chen 673*b6932704SJoseph Chen def __init__(self, chunk_type, chunk_offset, output_offset, output_size, 674*b6932704SJoseph Chen input_offset, fill_data): 675*b6932704SJoseph Chen """Initializes an ImageChunk object. 676*b6932704SJoseph Chen 677*b6932704SJoseph Chen Arguments: 678*b6932704SJoseph Chen chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE. 679*b6932704SJoseph Chen chunk_offset: Offset in the sparse file where this chunk begins. 680*b6932704SJoseph Chen output_offset: Offset in de-sparsified file. 681*b6932704SJoseph Chen output_size: Number of bytes in output. 682*b6932704SJoseph Chen input_offset: Offset in sparse file if TYPE_RAW otherwise None. 683*b6932704SJoseph Chen fill_data: Blob as bytes with data to fill if TYPE_FILL otherwise None. 684*b6932704SJoseph Chen 685*b6932704SJoseph Chen Raises: 686*b6932704SJoseph Chen ValueError: If given chunk parameters are invalid. 687*b6932704SJoseph Chen """ 688*b6932704SJoseph Chen self.chunk_type = chunk_type 689*b6932704SJoseph Chen self.chunk_offset = chunk_offset 690*b6932704SJoseph Chen self.output_offset = output_offset 691*b6932704SJoseph Chen self.output_size = output_size 692*b6932704SJoseph Chen self.input_offset = input_offset 693*b6932704SJoseph Chen self.fill_data = fill_data 694*b6932704SJoseph Chen # Check invariants. 695*b6932704SJoseph Chen if self.chunk_type == self.TYPE_RAW: 696*b6932704SJoseph Chen if self.fill_data is not None: 697*b6932704SJoseph Chen raise ValueError('RAW chunk cannot have fill_data set.') 698*b6932704SJoseph Chen if not self.input_offset: 699*b6932704SJoseph Chen raise ValueError('RAW chunk must have input_offset set.') 700*b6932704SJoseph Chen elif self.chunk_type == self.TYPE_FILL: 701*b6932704SJoseph Chen if self.fill_data is None: 702*b6932704SJoseph Chen raise ValueError('FILL chunk must have fill_data set.') 703*b6932704SJoseph Chen if self.input_offset: 704*b6932704SJoseph Chen raise ValueError('FILL chunk cannot have input_offset set.') 705*b6932704SJoseph Chen elif self.chunk_type == self.TYPE_DONT_CARE: 706*b6932704SJoseph Chen if self.fill_data is not None: 707*b6932704SJoseph Chen raise ValueError('DONT_CARE chunk cannot have fill_data set.') 708*b6932704SJoseph Chen if self.input_offset: 709*b6932704SJoseph Chen raise ValueError('DONT_CARE chunk cannot have input_offset set.') 710*b6932704SJoseph Chen else: 711*b6932704SJoseph Chen raise ValueError('Invalid chunk type') 712*b6932704SJoseph Chen 713*b6932704SJoseph Chen 714*b6932704SJoseph Chenclass ImageHandler(object): 715*b6932704SJoseph Chen """Abstraction for image I/O with support for Android sparse images. 716*b6932704SJoseph Chen 717*b6932704SJoseph Chen This class provides an interface for working with image files that 718*b6932704SJoseph Chen may be using the Android Sparse Image format. When an instance is 719*b6932704SJoseph Chen constructed, we test whether it's an Android sparse file. If so, 720*b6932704SJoseph Chen operations will be on the sparse file by interpreting the sparse 721*b6932704SJoseph Chen format, otherwise they will be directly on the file. Either way the 722*b6932704SJoseph Chen operations do the same. 723*b6932704SJoseph Chen 724*b6932704SJoseph Chen For reading, this interface mimics a file object - it has seek(), 725*b6932704SJoseph Chen tell(), and read() methods. For writing, only truncation 726*b6932704SJoseph Chen (truncate()) and appending is supported (append_raw() and 727*b6932704SJoseph Chen append_dont_care()). Additionally, data can only be written in units 728*b6932704SJoseph Chen of the block size. 729*b6932704SJoseph Chen 730*b6932704SJoseph Chen Attributes: 731*b6932704SJoseph Chen filename: Name of file. 732*b6932704SJoseph Chen is_sparse: Whether the file being operated on is sparse. 733*b6932704SJoseph Chen block_size: The block size, typically 4096. 734*b6932704SJoseph Chen image_size: The size of the unsparsified file. 735*b6932704SJoseph Chen """ 736*b6932704SJoseph Chen # See system/core/libsparse/sparse_format.h for details. 737*b6932704SJoseph Chen MAGIC = 0xed26ff3a 738*b6932704SJoseph Chen HEADER_FORMAT = '<I4H4I' 739*b6932704SJoseph Chen 740*b6932704SJoseph Chen # These are formats and offset of just the |total_chunks| and 741*b6932704SJoseph Chen # |total_blocks| fields. 742*b6932704SJoseph Chen NUM_CHUNKS_AND_BLOCKS_FORMAT = '<II' 743*b6932704SJoseph Chen NUM_CHUNKS_AND_BLOCKS_OFFSET = 16 744*b6932704SJoseph Chen 745*b6932704SJoseph Chen def __init__(self, image_filename, read_only=False): 746*b6932704SJoseph Chen """Initializes an image handler. 747*b6932704SJoseph Chen 748*b6932704SJoseph Chen Arguments: 749*b6932704SJoseph Chen image_filename: The name of the file to operate on. 750*b6932704SJoseph Chen read_only: True if file is only opened for read-only operations. 751*b6932704SJoseph Chen 752*b6932704SJoseph Chen Raises: 753*b6932704SJoseph Chen ValueError: If data in the file is invalid. 754*b6932704SJoseph Chen """ 755*b6932704SJoseph Chen self.filename = image_filename 756*b6932704SJoseph Chen self._num_total_blocks = 0 757*b6932704SJoseph Chen self._num_total_chunks = 0 758*b6932704SJoseph Chen self._file_pos = 0 759*b6932704SJoseph Chen self._read_only = read_only 760*b6932704SJoseph Chen self._read_header() 761*b6932704SJoseph Chen 762*b6932704SJoseph Chen def _read_header(self): 763*b6932704SJoseph Chen """Initializes internal data structures used for reading file. 764*b6932704SJoseph Chen 765*b6932704SJoseph Chen This may be called multiple times and is typically called after 766*b6932704SJoseph Chen modifying the file (e.g. appending, truncation). 767*b6932704SJoseph Chen 768*b6932704SJoseph Chen Raises: 769*b6932704SJoseph Chen ValueError: If data in the file is invalid. 770*b6932704SJoseph Chen """ 771*b6932704SJoseph Chen self.is_sparse = False 772*b6932704SJoseph Chen self.block_size = 4096 773*b6932704SJoseph Chen self._file_pos = 0 774*b6932704SJoseph Chen if self._read_only: 775*b6932704SJoseph Chen self._image = open(self.filename, 'rb') 776*b6932704SJoseph Chen else: 777*b6932704SJoseph Chen self._image = open(self.filename, 'r+b') 778*b6932704SJoseph Chen self._image.seek(0, os.SEEK_END) 779*b6932704SJoseph Chen self.image_size = self._image.tell() 780*b6932704SJoseph Chen 781*b6932704SJoseph Chen self._image.seek(0, os.SEEK_SET) 782*b6932704SJoseph Chen header_bin = self._image.read(struct.calcsize(self.HEADER_FORMAT)) 783*b6932704SJoseph Chen (magic, major_version, minor_version, file_hdr_sz, chunk_hdr_sz, 784*b6932704SJoseph Chen block_size, self._num_total_blocks, self._num_total_chunks, 785*b6932704SJoseph Chen _) = struct.unpack(self.HEADER_FORMAT, header_bin) 786*b6932704SJoseph Chen if magic != self.MAGIC: 787*b6932704SJoseph Chen # Not a sparse image, our job here is done. 788*b6932704SJoseph Chen return 789*b6932704SJoseph Chen if not (major_version == 1 and minor_version == 0): 790*b6932704SJoseph Chen raise ValueError('Encountered sparse image format version {}.{} but ' 791*b6932704SJoseph Chen 'only 1.0 is supported'.format(major_version, 792*b6932704SJoseph Chen minor_version)) 793*b6932704SJoseph Chen if file_hdr_sz != struct.calcsize(self.HEADER_FORMAT): 794*b6932704SJoseph Chen raise ValueError('Unexpected file_hdr_sz value {}.'. 795*b6932704SJoseph Chen format(file_hdr_sz)) 796*b6932704SJoseph Chen if chunk_hdr_sz != struct.calcsize(ImageChunk.FORMAT): 797*b6932704SJoseph Chen raise ValueError('Unexpected chunk_hdr_sz value {}.'. 798*b6932704SJoseph Chen format(chunk_hdr_sz)) 799*b6932704SJoseph Chen 800*b6932704SJoseph Chen self.block_size = block_size 801*b6932704SJoseph Chen 802*b6932704SJoseph Chen # Build an list of chunks by parsing the file. 803*b6932704SJoseph Chen self._chunks = [] 804*b6932704SJoseph Chen 805*b6932704SJoseph Chen # Find the smallest offset where only "Don't care" chunks 806*b6932704SJoseph Chen # follow. This will be the size of the content in the sparse 807*b6932704SJoseph Chen # image. 808*b6932704SJoseph Chen offset = 0 809*b6932704SJoseph Chen output_offset = 0 810*b6932704SJoseph Chen for _ in range(1, self._num_total_chunks + 1): 811*b6932704SJoseph Chen chunk_offset = self._image.tell() 812*b6932704SJoseph Chen 813*b6932704SJoseph Chen header_bin = self._image.read(struct.calcsize(ImageChunk.FORMAT)) 814*b6932704SJoseph Chen (chunk_type, _, chunk_sz, total_sz) = struct.unpack(ImageChunk.FORMAT, 815*b6932704SJoseph Chen header_bin) 816*b6932704SJoseph Chen data_sz = total_sz - struct.calcsize(ImageChunk.FORMAT) 817*b6932704SJoseph Chen 818*b6932704SJoseph Chen if chunk_type == ImageChunk.TYPE_RAW: 819*b6932704SJoseph Chen if data_sz != (chunk_sz * self.block_size): 820*b6932704SJoseph Chen raise ValueError('Raw chunk input size ({}) does not match output ' 821*b6932704SJoseph Chen 'size ({})'. 822*b6932704SJoseph Chen format(data_sz, chunk_sz*self.block_size)) 823*b6932704SJoseph Chen self._chunks.append(ImageChunk(ImageChunk.TYPE_RAW, 824*b6932704SJoseph Chen chunk_offset, 825*b6932704SJoseph Chen output_offset, 826*b6932704SJoseph Chen chunk_sz*self.block_size, 827*b6932704SJoseph Chen self._image.tell(), 828*b6932704SJoseph Chen None)) 829*b6932704SJoseph Chen self._image.seek(data_sz, os.SEEK_CUR) 830*b6932704SJoseph Chen 831*b6932704SJoseph Chen elif chunk_type == ImageChunk.TYPE_FILL: 832*b6932704SJoseph Chen if data_sz != 4: 833*b6932704SJoseph Chen raise ValueError('Fill chunk should have 4 bytes of fill, but this ' 834*b6932704SJoseph Chen 'has {}'.format(data_sz)) 835*b6932704SJoseph Chen fill_data = self._image.read(4) 836*b6932704SJoseph Chen self._chunks.append(ImageChunk(ImageChunk.TYPE_FILL, 837*b6932704SJoseph Chen chunk_offset, 838*b6932704SJoseph Chen output_offset, 839*b6932704SJoseph Chen chunk_sz*self.block_size, 840*b6932704SJoseph Chen None, 841*b6932704SJoseph Chen fill_data)) 842*b6932704SJoseph Chen elif chunk_type == ImageChunk.TYPE_DONT_CARE: 843*b6932704SJoseph Chen if data_sz != 0: 844*b6932704SJoseph Chen raise ValueError('Don\'t care chunk input size is non-zero ({})'. 845*b6932704SJoseph Chen format(data_sz)) 846*b6932704SJoseph Chen self._chunks.append(ImageChunk(ImageChunk.TYPE_DONT_CARE, 847*b6932704SJoseph Chen chunk_offset, 848*b6932704SJoseph Chen output_offset, 849*b6932704SJoseph Chen chunk_sz*self.block_size, 850*b6932704SJoseph Chen None, 851*b6932704SJoseph Chen None)) 852*b6932704SJoseph Chen elif chunk_type == ImageChunk.TYPE_CRC32: 853*b6932704SJoseph Chen if data_sz != 4: 854*b6932704SJoseph Chen raise ValueError('CRC32 chunk should have 4 bytes of CRC, but ' 855*b6932704SJoseph Chen 'this has {}'.format(data_sz)) 856*b6932704SJoseph Chen self._image.read(4) 857*b6932704SJoseph Chen else: 858*b6932704SJoseph Chen raise ValueError('Unknown chunk type {}'.format(chunk_type)) 859*b6932704SJoseph Chen 860*b6932704SJoseph Chen offset += chunk_sz 861*b6932704SJoseph Chen output_offset += chunk_sz*self.block_size 862*b6932704SJoseph Chen 863*b6932704SJoseph Chen # Record where sparse data end. 864*b6932704SJoseph Chen self._sparse_end = self._image.tell() 865*b6932704SJoseph Chen 866*b6932704SJoseph Chen # Now that we've traversed all chunks, sanity check. 867*b6932704SJoseph Chen if self._num_total_blocks != offset: 868*b6932704SJoseph Chen raise ValueError('The header said we should have {} output blocks, ' 869*b6932704SJoseph Chen 'but we saw {}'.format(self._num_total_blocks, offset)) 870*b6932704SJoseph Chen junk_len = len(self._image.read()) 871*b6932704SJoseph Chen if junk_len > 0: 872*b6932704SJoseph Chen raise ValueError('There were {} bytes of extra data at the end of the ' 873*b6932704SJoseph Chen 'file.'.format(junk_len)) 874*b6932704SJoseph Chen 875*b6932704SJoseph Chen # Assign |image_size|. 876*b6932704SJoseph Chen self.image_size = output_offset 877*b6932704SJoseph Chen 878*b6932704SJoseph Chen # This is used when bisecting in read() to find the initial slice. 879*b6932704SJoseph Chen self._chunk_output_offsets = [i.output_offset for i in self._chunks] 880*b6932704SJoseph Chen 881*b6932704SJoseph Chen self.is_sparse = True 882*b6932704SJoseph Chen 883*b6932704SJoseph Chen def _update_chunks_and_blocks(self): 884*b6932704SJoseph Chen """Helper function to update the image header. 885*b6932704SJoseph Chen 886*b6932704SJoseph Chen The the |total_chunks| and |total_blocks| fields in the header 887*b6932704SJoseph Chen will be set to value of the |_num_total_blocks| and 888*b6932704SJoseph Chen |_num_total_chunks| attributes. 889*b6932704SJoseph Chen 890*b6932704SJoseph Chen """ 891*b6932704SJoseph Chen self._image.seek(self.NUM_CHUNKS_AND_BLOCKS_OFFSET, os.SEEK_SET) 892*b6932704SJoseph Chen self._image.write(struct.pack(self.NUM_CHUNKS_AND_BLOCKS_FORMAT, 893*b6932704SJoseph Chen self._num_total_blocks, 894*b6932704SJoseph Chen self._num_total_chunks)) 895*b6932704SJoseph Chen 896*b6932704SJoseph Chen def append_dont_care(self, num_bytes): 897*b6932704SJoseph Chen """Appends a DONT_CARE chunk to the sparse file. 898*b6932704SJoseph Chen 899*b6932704SJoseph Chen The given number of bytes must be a multiple of the block size. 900*b6932704SJoseph Chen 901*b6932704SJoseph Chen Arguments: 902*b6932704SJoseph Chen num_bytes: Size in number of bytes of the DONT_CARE chunk. 903*b6932704SJoseph Chen 904*b6932704SJoseph Chen Raises: 905*b6932704SJoseph Chen OSError: If ImageHandler was initialized in read-only mode. 906*b6932704SJoseph Chen """ 907*b6932704SJoseph Chen assert num_bytes % self.block_size == 0 908*b6932704SJoseph Chen 909*b6932704SJoseph Chen if self._read_only: 910*b6932704SJoseph Chen raise OSError('ImageHandler is in read-only mode.') 911*b6932704SJoseph Chen 912*b6932704SJoseph Chen if not self.is_sparse: 913*b6932704SJoseph Chen self._image.seek(0, os.SEEK_END) 914*b6932704SJoseph Chen # This is more efficient that writing NUL bytes since it'll add 915*b6932704SJoseph Chen # a hole on file systems that support sparse files (native 916*b6932704SJoseph Chen # sparse, not Android sparse). 917*b6932704SJoseph Chen self._image.truncate(self._image.tell() + num_bytes) 918*b6932704SJoseph Chen self._read_header() 919*b6932704SJoseph Chen return 920*b6932704SJoseph Chen 921*b6932704SJoseph Chen self._num_total_chunks += 1 922*b6932704SJoseph Chen self._num_total_blocks += num_bytes // self.block_size 923*b6932704SJoseph Chen self._update_chunks_and_blocks() 924*b6932704SJoseph Chen 925*b6932704SJoseph Chen self._image.seek(self._sparse_end, os.SEEK_SET) 926*b6932704SJoseph Chen self._image.write(struct.pack(ImageChunk.FORMAT, 927*b6932704SJoseph Chen ImageChunk.TYPE_DONT_CARE, 928*b6932704SJoseph Chen 0, # Reserved 929*b6932704SJoseph Chen num_bytes // self.block_size, 930*b6932704SJoseph Chen struct.calcsize(ImageChunk.FORMAT))) 931*b6932704SJoseph Chen self._read_header() 932*b6932704SJoseph Chen 933*b6932704SJoseph Chen def append_raw(self, data): 934*b6932704SJoseph Chen """Appends a RAW chunk to the sparse file. 935*b6932704SJoseph Chen 936*b6932704SJoseph Chen The length of the given data must be a multiple of the block size. 937*b6932704SJoseph Chen 938*b6932704SJoseph Chen Arguments: 939*b6932704SJoseph Chen data: Data to append as bytes. 940*b6932704SJoseph Chen 941*b6932704SJoseph Chen Raises: 942*b6932704SJoseph Chen OSError: If ImageHandler was initialized in read-only mode. 943*b6932704SJoseph Chen """ 944*b6932704SJoseph Chen assert len(data) % self.block_size == 0 945*b6932704SJoseph Chen 946*b6932704SJoseph Chen if self._read_only: 947*b6932704SJoseph Chen raise OSError('ImageHandler is in read-only mode.') 948*b6932704SJoseph Chen 949*b6932704SJoseph Chen if not self.is_sparse: 950*b6932704SJoseph Chen self._image.seek(0, os.SEEK_END) 951*b6932704SJoseph Chen self._image.write(data) 952*b6932704SJoseph Chen self._read_header() 953*b6932704SJoseph Chen return 954*b6932704SJoseph Chen 955*b6932704SJoseph Chen self._num_total_chunks += 1 956*b6932704SJoseph Chen self._num_total_blocks += len(data) // self.block_size 957*b6932704SJoseph Chen self._update_chunks_and_blocks() 958*b6932704SJoseph Chen 959*b6932704SJoseph Chen self._image.seek(self._sparse_end, os.SEEK_SET) 960*b6932704SJoseph Chen self._image.write(struct.pack(ImageChunk.FORMAT, 961*b6932704SJoseph Chen ImageChunk.TYPE_RAW, 962*b6932704SJoseph Chen 0, # Reserved 963*b6932704SJoseph Chen len(data) // self.block_size, 964*b6932704SJoseph Chen len(data) + 965*b6932704SJoseph Chen struct.calcsize(ImageChunk.FORMAT))) 966*b6932704SJoseph Chen self._image.write(data) 967*b6932704SJoseph Chen self._read_header() 968*b6932704SJoseph Chen 969*b6932704SJoseph Chen def append_fill(self, fill_data, size): 970*b6932704SJoseph Chen """Appends a fill chunk to the sparse file. 971*b6932704SJoseph Chen 972*b6932704SJoseph Chen The total length of the fill data must be a multiple of the block size. 973*b6932704SJoseph Chen 974*b6932704SJoseph Chen Arguments: 975*b6932704SJoseph Chen fill_data: Fill data to append - must be four bytes. 976*b6932704SJoseph Chen size: Number of chunk - must be a multiple of four and the block size. 977*b6932704SJoseph Chen 978*b6932704SJoseph Chen Raises: 979*b6932704SJoseph Chen OSError: If ImageHandler was initialized in read-only mode. 980*b6932704SJoseph Chen """ 981*b6932704SJoseph Chen assert len(fill_data) == 4 982*b6932704SJoseph Chen assert size % 4 == 0 983*b6932704SJoseph Chen assert size % self.block_size == 0 984*b6932704SJoseph Chen 985*b6932704SJoseph Chen if self._read_only: 986*b6932704SJoseph Chen raise OSError('ImageHandler is in read-only mode.') 987*b6932704SJoseph Chen 988*b6932704SJoseph Chen if not self.is_sparse: 989*b6932704SJoseph Chen self._image.seek(0, os.SEEK_END) 990*b6932704SJoseph Chen self._image.write(fill_data * (size//4)) 991*b6932704SJoseph Chen self._read_header() 992*b6932704SJoseph Chen return 993*b6932704SJoseph Chen 994*b6932704SJoseph Chen self._num_total_chunks += 1 995*b6932704SJoseph Chen self._num_total_blocks += size // self.block_size 996*b6932704SJoseph Chen self._update_chunks_and_blocks() 997*b6932704SJoseph Chen 998*b6932704SJoseph Chen self._image.seek(self._sparse_end, os.SEEK_SET) 999*b6932704SJoseph Chen self._image.write(struct.pack(ImageChunk.FORMAT, 1000*b6932704SJoseph Chen ImageChunk.TYPE_FILL, 1001*b6932704SJoseph Chen 0, # Reserved 1002*b6932704SJoseph Chen size // self.block_size, 1003*b6932704SJoseph Chen 4 + struct.calcsize(ImageChunk.FORMAT))) 1004*b6932704SJoseph Chen self._image.write(fill_data) 1005*b6932704SJoseph Chen self._read_header() 1006*b6932704SJoseph Chen 1007*b6932704SJoseph Chen def seek(self, offset): 1008*b6932704SJoseph Chen """Sets the cursor position for reading from unsparsified file. 1009*b6932704SJoseph Chen 1010*b6932704SJoseph Chen Arguments: 1011*b6932704SJoseph Chen offset: Offset to seek to from the beginning of the file. 1012*b6932704SJoseph Chen 1013*b6932704SJoseph Chen Raises: 1014*b6932704SJoseph Chen RuntimeError: If the given offset is negative. 1015*b6932704SJoseph Chen """ 1016*b6932704SJoseph Chen if offset < 0: 1017*b6932704SJoseph Chen raise RuntimeError('Seeking with negative offset: {}'.format(offset)) 1018*b6932704SJoseph Chen self._file_pos = offset 1019*b6932704SJoseph Chen 1020*b6932704SJoseph Chen def read(self, size): 1021*b6932704SJoseph Chen """Reads data from the unsparsified file. 1022*b6932704SJoseph Chen 1023*b6932704SJoseph Chen This method may return fewer than |size| bytes of data if the end 1024*b6932704SJoseph Chen of the file was encountered. 1025*b6932704SJoseph Chen 1026*b6932704SJoseph Chen The file cursor for reading is advanced by the number of bytes 1027*b6932704SJoseph Chen read. 1028*b6932704SJoseph Chen 1029*b6932704SJoseph Chen Arguments: 1030*b6932704SJoseph Chen size: Number of bytes to read. 1031*b6932704SJoseph Chen 1032*b6932704SJoseph Chen Returns: 1033*b6932704SJoseph Chen The data as bytes. 1034*b6932704SJoseph Chen """ 1035*b6932704SJoseph Chen if not self.is_sparse: 1036*b6932704SJoseph Chen self._image.seek(self._file_pos) 1037*b6932704SJoseph Chen data = self._image.read(size) 1038*b6932704SJoseph Chen self._file_pos += len(data) 1039*b6932704SJoseph Chen return data 1040*b6932704SJoseph Chen 1041*b6932704SJoseph Chen # Iterate over all chunks. 1042*b6932704SJoseph Chen chunk_idx = bisect.bisect_right(self._chunk_output_offsets, 1043*b6932704SJoseph Chen self._file_pos) - 1 1044*b6932704SJoseph Chen data = bytearray() 1045*b6932704SJoseph Chen to_go = size 1046*b6932704SJoseph Chen while to_go > 0: 1047*b6932704SJoseph Chen chunk = self._chunks[chunk_idx] 1048*b6932704SJoseph Chen chunk_pos_offset = self._file_pos - chunk.output_offset 1049*b6932704SJoseph Chen chunk_pos_to_go = min(chunk.output_size - chunk_pos_offset, to_go) 1050*b6932704SJoseph Chen 1051*b6932704SJoseph Chen if chunk.chunk_type == ImageChunk.TYPE_RAW: 1052*b6932704SJoseph Chen self._image.seek(chunk.input_offset + chunk_pos_offset) 1053*b6932704SJoseph Chen data.extend(self._image.read(chunk_pos_to_go)) 1054*b6932704SJoseph Chen elif chunk.chunk_type == ImageChunk.TYPE_FILL: 1055*b6932704SJoseph Chen all_data = chunk.fill_data*(chunk_pos_to_go // len(chunk.fill_data) + 2) 1056*b6932704SJoseph Chen offset_mod = chunk_pos_offset % len(chunk.fill_data) 1057*b6932704SJoseph Chen data.extend(all_data[offset_mod:(offset_mod + chunk_pos_to_go)]) 1058*b6932704SJoseph Chen else: 1059*b6932704SJoseph Chen assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE 1060*b6932704SJoseph Chen data.extend(b'\0' * chunk_pos_to_go) 1061*b6932704SJoseph Chen 1062*b6932704SJoseph Chen to_go -= chunk_pos_to_go 1063*b6932704SJoseph Chen self._file_pos += chunk_pos_to_go 1064*b6932704SJoseph Chen chunk_idx += 1 1065*b6932704SJoseph Chen # Generate partial read in case of EOF. 1066*b6932704SJoseph Chen if chunk_idx >= len(self._chunks): 1067*b6932704SJoseph Chen break 1068*b6932704SJoseph Chen 1069*b6932704SJoseph Chen return bytes(data) 1070*b6932704SJoseph Chen 1071*b6932704SJoseph Chen def tell(self): 1072*b6932704SJoseph Chen """Returns the file cursor position for reading from unsparsified file. 1073*b6932704SJoseph Chen 1074*b6932704SJoseph Chen Returns: 1075*b6932704SJoseph Chen The file cursor position for reading. 1076*b6932704SJoseph Chen """ 1077*b6932704SJoseph Chen return self._file_pos 1078*b6932704SJoseph Chen 1079*b6932704SJoseph Chen def truncate(self, size): 1080*b6932704SJoseph Chen """Truncates the unsparsified file. 1081*b6932704SJoseph Chen 1082*b6932704SJoseph Chen Arguments: 1083*b6932704SJoseph Chen size: Desired size of unsparsified file. 1084*b6932704SJoseph Chen 1085*b6932704SJoseph Chen Raises: 1086*b6932704SJoseph Chen ValueError: If desired size isn't a multiple of the block size. 1087*b6932704SJoseph Chen OSError: If ImageHandler was initialized in read-only mode. 1088*b6932704SJoseph Chen """ 1089*b6932704SJoseph Chen if self._read_only: 1090*b6932704SJoseph Chen raise OSError('ImageHandler is in read-only mode.') 1091*b6932704SJoseph Chen 1092*b6932704SJoseph Chen if not self.is_sparse: 1093*b6932704SJoseph Chen self._image.truncate(size) 1094*b6932704SJoseph Chen self._read_header() 1095*b6932704SJoseph Chen return 1096*b6932704SJoseph Chen 1097*b6932704SJoseph Chen if size % self.block_size != 0: 1098*b6932704SJoseph Chen raise ValueError('Cannot truncate to a size which is not a multiple ' 1099*b6932704SJoseph Chen 'of the block size') 1100*b6932704SJoseph Chen 1101*b6932704SJoseph Chen if size == self.image_size: 1102*b6932704SJoseph Chen # Trivial where there's nothing to do. 1103*b6932704SJoseph Chen return 1104*b6932704SJoseph Chen 1105*b6932704SJoseph Chen if size < self.image_size: 1106*b6932704SJoseph Chen chunk_idx = bisect.bisect_right(self._chunk_output_offsets, size) - 1 1107*b6932704SJoseph Chen chunk = self._chunks[chunk_idx] 1108*b6932704SJoseph Chen if chunk.output_offset != size: 1109*b6932704SJoseph Chen # Truncation in the middle of a trunk - need to keep the chunk 1110*b6932704SJoseph Chen # and modify it. 1111*b6932704SJoseph Chen chunk_idx_for_update = chunk_idx + 1 1112*b6932704SJoseph Chen num_to_keep = size - chunk.output_offset 1113*b6932704SJoseph Chen assert num_to_keep % self.block_size == 0 1114*b6932704SJoseph Chen if chunk.chunk_type == ImageChunk.TYPE_RAW: 1115*b6932704SJoseph Chen truncate_at = (chunk.chunk_offset + 1116*b6932704SJoseph Chen struct.calcsize(ImageChunk.FORMAT) + num_to_keep) 1117*b6932704SJoseph Chen data_sz = num_to_keep 1118*b6932704SJoseph Chen elif chunk.chunk_type == ImageChunk.TYPE_FILL: 1119*b6932704SJoseph Chen truncate_at = (chunk.chunk_offset + 1120*b6932704SJoseph Chen struct.calcsize(ImageChunk.FORMAT) + 4) 1121*b6932704SJoseph Chen data_sz = 4 1122*b6932704SJoseph Chen else: 1123*b6932704SJoseph Chen assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE 1124*b6932704SJoseph Chen truncate_at = chunk.chunk_offset + struct.calcsize(ImageChunk.FORMAT) 1125*b6932704SJoseph Chen data_sz = 0 1126*b6932704SJoseph Chen chunk_sz = num_to_keep // self.block_size 1127*b6932704SJoseph Chen total_sz = data_sz + struct.calcsize(ImageChunk.FORMAT) 1128*b6932704SJoseph Chen self._image.seek(chunk.chunk_offset) 1129*b6932704SJoseph Chen self._image.write(struct.pack(ImageChunk.FORMAT, 1130*b6932704SJoseph Chen chunk.chunk_type, 1131*b6932704SJoseph Chen 0, # Reserved 1132*b6932704SJoseph Chen chunk_sz, 1133*b6932704SJoseph Chen total_sz)) 1134*b6932704SJoseph Chen chunk.output_size = num_to_keep 1135*b6932704SJoseph Chen else: 1136*b6932704SJoseph Chen # Truncation at trunk boundary. 1137*b6932704SJoseph Chen truncate_at = chunk.chunk_offset 1138*b6932704SJoseph Chen chunk_idx_for_update = chunk_idx 1139*b6932704SJoseph Chen 1140*b6932704SJoseph Chen self._num_total_chunks = chunk_idx_for_update 1141*b6932704SJoseph Chen self._num_total_blocks = 0 1142*b6932704SJoseph Chen for i in range(0, chunk_idx_for_update): 1143*b6932704SJoseph Chen self._num_total_blocks += self._chunks[i].output_size // self.block_size 1144*b6932704SJoseph Chen self._update_chunks_and_blocks() 1145*b6932704SJoseph Chen self._image.truncate(truncate_at) 1146*b6932704SJoseph Chen 1147*b6932704SJoseph Chen # We've modified the file so re-read all data. 1148*b6932704SJoseph Chen self._read_header() 1149*b6932704SJoseph Chen else: 1150*b6932704SJoseph Chen # Truncating to grow - just add a DONT_CARE section. 1151*b6932704SJoseph Chen self.append_dont_care(size - self.image_size) 1152*b6932704SJoseph Chen 1153*b6932704SJoseph Chen 1154*b6932704SJoseph Chenclass AvbDescriptor(object): 1155*b6932704SJoseph Chen """Class for AVB descriptor. 1156*b6932704SJoseph Chen 1157*b6932704SJoseph Chen See the |AvbDescriptor| C struct for more information. 1158*b6932704SJoseph Chen 1159*b6932704SJoseph Chen Attributes: 1160*b6932704SJoseph Chen tag: The tag identifying what kind of descriptor this is. 1161*b6932704SJoseph Chen data: The data in the descriptor. 1162*b6932704SJoseph Chen """ 1163*b6932704SJoseph Chen 1164*b6932704SJoseph Chen SIZE = 16 1165*b6932704SJoseph Chen FORMAT_STRING = ('!QQ') # tag, num_bytes_following (descriptor header) 1166*b6932704SJoseph Chen 1167*b6932704SJoseph Chen def __init__(self, data): 1168*b6932704SJoseph Chen """Initializes a new property descriptor. 1169*b6932704SJoseph Chen 1170*b6932704SJoseph Chen Arguments: 1171*b6932704SJoseph Chen data: If not None, must be a bytearray(). 1172*b6932704SJoseph Chen 1173*b6932704SJoseph Chen Raises: 1174*b6932704SJoseph Chen LookupError: If the given descriptor is malformed. 1175*b6932704SJoseph Chen """ 1176*b6932704SJoseph Chen assert struct.calcsize(self.FORMAT_STRING) == self.SIZE 1177*b6932704SJoseph Chen 1178*b6932704SJoseph Chen if data: 1179*b6932704SJoseph Chen (self.tag, num_bytes_following) = ( 1180*b6932704SJoseph Chen struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])) 1181*b6932704SJoseph Chen self.data = data[self.SIZE:self.SIZE + num_bytes_following] 1182*b6932704SJoseph Chen else: 1183*b6932704SJoseph Chen self.tag = None 1184*b6932704SJoseph Chen self.data = None 1185*b6932704SJoseph Chen 1186*b6932704SJoseph Chen def print_desc(self, o): 1187*b6932704SJoseph Chen """Print the descriptor. 1188*b6932704SJoseph Chen 1189*b6932704SJoseph Chen Arguments: 1190*b6932704SJoseph Chen o: The object to write the output to. 1191*b6932704SJoseph Chen """ 1192*b6932704SJoseph Chen o.write(' Unknown descriptor:\n') 1193*b6932704SJoseph Chen o.write(' Tag: {}\n'.format(self.tag)) 1194*b6932704SJoseph Chen if len(self.data) < 256: 1195*b6932704SJoseph Chen o.write(' Data: {} ({} bytes)\n'.format( 1196*b6932704SJoseph Chen repr(str(self.data)), len(self.data))) 1197*b6932704SJoseph Chen else: 1198*b6932704SJoseph Chen o.write(' Data: {} bytes\n'.format(len(self.data))) 1199*b6932704SJoseph Chen 1200*b6932704SJoseph Chen def encode(self): 1201*b6932704SJoseph Chen """Serializes the descriptor. 1202*b6932704SJoseph Chen 1203*b6932704SJoseph Chen Returns: 1204*b6932704SJoseph Chen A bytearray() with the descriptor data. 1205*b6932704SJoseph Chen """ 1206*b6932704SJoseph Chen num_bytes_following = len(self.data) 1207*b6932704SJoseph Chen nbf_with_padding = round_to_multiple(num_bytes_following, 8) 1208*b6932704SJoseph Chen padding_size = nbf_with_padding - num_bytes_following 1209*b6932704SJoseph Chen desc = struct.pack(self.FORMAT_STRING, self.tag, nbf_with_padding) 1210*b6932704SJoseph Chen padding = struct.pack(str(padding_size) + 'x') 1211*b6932704SJoseph Chen ret = desc + self.data + padding 1212*b6932704SJoseph Chen return bytearray(ret) 1213*b6932704SJoseph Chen 1214*b6932704SJoseph Chen def verify(self, image_dir, image_ext, expected_chain_partitions_map, 1215*b6932704SJoseph Chen image_containing_descriptor, accept_zeroed_hashtree): 1216*b6932704SJoseph Chen """Verifies contents of the descriptor - used in verify_image sub-command. 1217*b6932704SJoseph Chen 1218*b6932704SJoseph Chen Arguments: 1219*b6932704SJoseph Chen image_dir: The directory of the file being verified. 1220*b6932704SJoseph Chen image_ext: The extension of the file being verified (e.g. '.img'). 1221*b6932704SJoseph Chen expected_chain_partitions_map: A map from partition name to the 1222*b6932704SJoseph Chen tuple (rollback_index_location, key_blob). 1223*b6932704SJoseph Chen image_containing_descriptor: The image the descriptor is in. 1224*b6932704SJoseph Chen accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is 1225*b6932704SJoseph Chen zeroed out. 1226*b6932704SJoseph Chen 1227*b6932704SJoseph Chen Returns: 1228*b6932704SJoseph Chen True if the descriptor verifies, False otherwise. 1229*b6932704SJoseph Chen """ 1230*b6932704SJoseph Chen # Deletes unused parameters to prevent pylint warning unused-argument. 1231*b6932704SJoseph Chen del image_dir, image_ext, expected_chain_partitions_map 1232*b6932704SJoseph Chen del image_containing_descriptor, accept_zeroed_hashtree 1233*b6932704SJoseph Chen 1234*b6932704SJoseph Chen # Nothing to do. 1235*b6932704SJoseph Chen return True 1236*b6932704SJoseph Chen 1237*b6932704SJoseph Chen 1238*b6932704SJoseph Chenclass AvbPropertyDescriptor(AvbDescriptor): 1239*b6932704SJoseph Chen """A class for property descriptors. 1240*b6932704SJoseph Chen 1241*b6932704SJoseph Chen See the |AvbPropertyDescriptor| C struct for more information. 1242*b6932704SJoseph Chen 1243*b6932704SJoseph Chen Attributes: 1244*b6932704SJoseph Chen key: The key as string. 1245*b6932704SJoseph Chen value: The value as bytes. 1246*b6932704SJoseph Chen """ 1247*b6932704SJoseph Chen 1248*b6932704SJoseph Chen TAG = 0 1249*b6932704SJoseph Chen SIZE = 32 1250*b6932704SJoseph Chen FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header) 1251*b6932704SJoseph Chen 'Q' # key size (bytes) 1252*b6932704SJoseph Chen 'Q') # value size (bytes) 1253*b6932704SJoseph Chen 1254*b6932704SJoseph Chen def __init__(self, data=None): 1255*b6932704SJoseph Chen """Initializes a new property descriptor. 1256*b6932704SJoseph Chen 1257*b6932704SJoseph Chen Arguments: 1258*b6932704SJoseph Chen data: If not None, must be as bytes of size |SIZE|. 1259*b6932704SJoseph Chen 1260*b6932704SJoseph Chen Raises: 1261*b6932704SJoseph Chen LookupError: If the given descriptor is malformed. 1262*b6932704SJoseph Chen """ 1263*b6932704SJoseph Chen super().__init__(None) 1264*b6932704SJoseph Chen assert struct.calcsize(self.FORMAT_STRING) == self.SIZE 1265*b6932704SJoseph Chen 1266*b6932704SJoseph Chen if data: 1267*b6932704SJoseph Chen (tag, num_bytes_following, key_size, 1268*b6932704SJoseph Chen value_size) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]) 1269*b6932704SJoseph Chen expected_size = round_to_multiple( 1270*b6932704SJoseph Chen self.SIZE - 16 + key_size + 1 + value_size + 1, 8) 1271*b6932704SJoseph Chen if tag != self.TAG or num_bytes_following != expected_size: 1272*b6932704SJoseph Chen raise LookupError('Given data does not look like a property ' 1273*b6932704SJoseph Chen 'descriptor.') 1274*b6932704SJoseph Chen try: 1275*b6932704SJoseph Chen self.key = data[self.SIZE:(self.SIZE + key_size)].decode('utf-8') 1276*b6932704SJoseph Chen except UnicodeDecodeError as e: 1277*b6932704SJoseph Chen raise LookupError('Key cannot be decoded as UTF-8: {}.' 1278*b6932704SJoseph Chen .format(e)) from e 1279*b6932704SJoseph Chen self.value = data[(self.SIZE + key_size + 1):(self.SIZE + key_size + 1 + 1280*b6932704SJoseph Chen value_size)] 1281*b6932704SJoseph Chen else: 1282*b6932704SJoseph Chen self.key = '' 1283*b6932704SJoseph Chen self.value = b'' 1284*b6932704SJoseph Chen 1285*b6932704SJoseph Chen def print_desc(self, o): 1286*b6932704SJoseph Chen """Print the descriptor. 1287*b6932704SJoseph Chen 1288*b6932704SJoseph Chen Arguments: 1289*b6932704SJoseph Chen o: The object to write the output to. 1290*b6932704SJoseph Chen """ 1291*b6932704SJoseph Chen # Go forward with python 3, bytes are represented with the 'b' prefix, 1292*b6932704SJoseph Chen # e.g. b'foobar'. Thus, we trim off the 'b' to keep the print output 1293*b6932704SJoseph Chen # the same between python 2 and python 3. 1294*b6932704SJoseph Chen printable_value = repr(self.value) 1295*b6932704SJoseph Chen if printable_value.startswith('b\''): 1296*b6932704SJoseph Chen printable_value = printable_value[1:] 1297*b6932704SJoseph Chen 1298*b6932704SJoseph Chen if len(self.value) < 256: 1299*b6932704SJoseph Chen o.write(' Prop: {} -> {}\n'.format(self.key, printable_value)) 1300*b6932704SJoseph Chen else: 1301*b6932704SJoseph Chen o.write(' Prop: {} -> ({} bytes)\n'.format(self.key, len(self.value))) 1302*b6932704SJoseph Chen 1303*b6932704SJoseph Chen def encode(self): 1304*b6932704SJoseph Chen """Serializes the descriptor. 1305*b6932704SJoseph Chen 1306*b6932704SJoseph Chen Returns: 1307*b6932704SJoseph Chen The descriptor data as bytes. 1308*b6932704SJoseph Chen """ 1309*b6932704SJoseph Chen key_encoded = self.key.encode('utf-8') 1310*b6932704SJoseph Chen num_bytes_following = ( 1311*b6932704SJoseph Chen self.SIZE + len(key_encoded) + len(self.value) + 2 - 16) 1312*b6932704SJoseph Chen nbf_with_padding = round_to_multiple(num_bytes_following, 8) 1313*b6932704SJoseph Chen padding_size = nbf_with_padding - num_bytes_following 1314*b6932704SJoseph Chen desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding, 1315*b6932704SJoseph Chen len(key_encoded), len(self.value)) 1316*b6932704SJoseph Chen ret = (desc + key_encoded + b'\0' + self.value + b'\0' + 1317*b6932704SJoseph Chen padding_size * b'\0') 1318*b6932704SJoseph Chen return ret 1319*b6932704SJoseph Chen 1320*b6932704SJoseph Chen def verify(self, image_dir, image_ext, expected_chain_partitions_map, 1321*b6932704SJoseph Chen image_containing_descriptor, accept_zeroed_hashtree): 1322*b6932704SJoseph Chen """Verifies contents of the descriptor - used in verify_image sub-command. 1323*b6932704SJoseph Chen 1324*b6932704SJoseph Chen Arguments: 1325*b6932704SJoseph Chen image_dir: The directory of the file being verified. 1326*b6932704SJoseph Chen image_ext: The extension of the file being verified (e.g. '.img'). 1327*b6932704SJoseph Chen expected_chain_partitions_map: A map from partition name to the 1328*b6932704SJoseph Chen tuple (rollback_index_location, key_blob). 1329*b6932704SJoseph Chen image_containing_descriptor: The image the descriptor is in. 1330*b6932704SJoseph Chen accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is 1331*b6932704SJoseph Chen zeroed out. 1332*b6932704SJoseph Chen 1333*b6932704SJoseph Chen Returns: 1334*b6932704SJoseph Chen True if the descriptor verifies, False otherwise. 1335*b6932704SJoseph Chen """ 1336*b6932704SJoseph Chen # Nothing to do. 1337*b6932704SJoseph Chen return True 1338*b6932704SJoseph Chen 1339*b6932704SJoseph Chen 1340*b6932704SJoseph Chenclass AvbHashtreeDescriptor(AvbDescriptor): 1341*b6932704SJoseph Chen """A class for hashtree descriptors. 1342*b6932704SJoseph Chen 1343*b6932704SJoseph Chen See the |AvbHashtreeDescriptor| C struct for more information. 1344*b6932704SJoseph Chen 1345*b6932704SJoseph Chen Attributes: 1346*b6932704SJoseph Chen dm_verity_version: dm-verity version used. 1347*b6932704SJoseph Chen image_size: Size of the image, after rounding up to |block_size|. 1348*b6932704SJoseph Chen tree_offset: Offset of the hash tree in the file. 1349*b6932704SJoseph Chen tree_size: Size of the tree. 1350*b6932704SJoseph Chen data_block_size: Data block size. 1351*b6932704SJoseph Chen hash_block_size: Hash block size. 1352*b6932704SJoseph Chen fec_num_roots: Number of roots used for FEC (0 if FEC is not used). 1353*b6932704SJoseph Chen fec_offset: Offset of FEC data (0 if FEC is not used). 1354*b6932704SJoseph Chen fec_size: Size of FEC data (0 if FEC is not used). 1355*b6932704SJoseph Chen hash_algorithm: Hash algorithm used as string. 1356*b6932704SJoseph Chen partition_name: Partition name as string. 1357*b6932704SJoseph Chen salt: Salt used as bytes. 1358*b6932704SJoseph Chen root_digest: Root digest as bytes. 1359*b6932704SJoseph Chen flags: Descriptor flags (see avb_hashtree_descriptor.h). 1360*b6932704SJoseph Chen """ 1361*b6932704SJoseph Chen 1362*b6932704SJoseph Chen TAG = 1 1363*b6932704SJoseph Chen RESERVED = 60 1364*b6932704SJoseph Chen SIZE = 120 + RESERVED 1365*b6932704SJoseph Chen FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header) 1366*b6932704SJoseph Chen 'L' # dm-verity version used 1367*b6932704SJoseph Chen 'Q' # image size (bytes) 1368*b6932704SJoseph Chen 'Q' # tree offset (bytes) 1369*b6932704SJoseph Chen 'Q' # tree size (bytes) 1370*b6932704SJoseph Chen 'L' # data block size (bytes) 1371*b6932704SJoseph Chen 'L' # hash block size (bytes) 1372*b6932704SJoseph Chen 'L' # FEC number of roots 1373*b6932704SJoseph Chen 'Q' # FEC offset (bytes) 1374*b6932704SJoseph Chen 'Q' # FEC size (bytes) 1375*b6932704SJoseph Chen '32s' # hash algorithm used 1376*b6932704SJoseph Chen 'L' # partition name (bytes) 1377*b6932704SJoseph Chen 'L' # salt length (bytes) 1378*b6932704SJoseph Chen 'L' # root digest length (bytes) 1379*b6932704SJoseph Chen 'L' + # flags 1380*b6932704SJoseph Chen str(RESERVED) + 's') # reserved 1381*b6932704SJoseph Chen 1382*b6932704SJoseph Chen FLAGS_DO_NOT_USE_AB = (1 << 0) 1383*b6932704SJoseph Chen FLAGS_CHECK_AT_MOST_ONCE = (1 << 1) 1384*b6932704SJoseph Chen 1385*b6932704SJoseph Chen def __init__(self, data=None): 1386*b6932704SJoseph Chen """Initializes a new hashtree descriptor. 1387*b6932704SJoseph Chen 1388*b6932704SJoseph Chen Arguments: 1389*b6932704SJoseph Chen data: If not None, must be bytes of size |SIZE|. 1390*b6932704SJoseph Chen 1391*b6932704SJoseph Chen Raises: 1392*b6932704SJoseph Chen LookupError: If the given descriptor is malformed. 1393*b6932704SJoseph Chen """ 1394*b6932704SJoseph Chen super().__init__(None) 1395*b6932704SJoseph Chen assert struct.calcsize(self.FORMAT_STRING) == self.SIZE 1396*b6932704SJoseph Chen 1397*b6932704SJoseph Chen if data: 1398*b6932704SJoseph Chen (tag, num_bytes_following, self.dm_verity_version, self.image_size, 1399*b6932704SJoseph Chen self.tree_offset, self.tree_size, self.data_block_size, 1400*b6932704SJoseph Chen self.hash_block_size, self.fec_num_roots, self.fec_offset, self.fec_size, 1401*b6932704SJoseph Chen self.hash_algorithm, partition_name_len, salt_len, 1402*b6932704SJoseph Chen root_digest_len, self.flags, _) = struct.unpack(self.FORMAT_STRING, 1403*b6932704SJoseph Chen data[0:self.SIZE]) 1404*b6932704SJoseph Chen expected_size = round_to_multiple( 1405*b6932704SJoseph Chen self.SIZE - 16 + partition_name_len + salt_len + root_digest_len, 8) 1406*b6932704SJoseph Chen if tag != self.TAG or num_bytes_following != expected_size: 1407*b6932704SJoseph Chen raise LookupError('Given data does not look like a hashtree ' 1408*b6932704SJoseph Chen 'descriptor.') 1409*b6932704SJoseph Chen # Nuke NUL-bytes at the end. 1410*b6932704SJoseph Chen self.hash_algorithm = self.hash_algorithm.rstrip(b'\0').decode('ascii') 1411*b6932704SJoseph Chen o = 0 1412*b6932704SJoseph Chen try: 1413*b6932704SJoseph Chen self.partition_name = data[ 1414*b6932704SJoseph Chen (self.SIZE + o):(self.SIZE + o + partition_name_len) 1415*b6932704SJoseph Chen ].decode('utf-8') 1416*b6932704SJoseph Chen except UnicodeDecodeError as e: 1417*b6932704SJoseph Chen raise LookupError('Partition name cannot be decoded as UTF-8: {}.' 1418*b6932704SJoseph Chen .format(e)) from e 1419*b6932704SJoseph Chen o += partition_name_len 1420*b6932704SJoseph Chen self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)] 1421*b6932704SJoseph Chen o += salt_len 1422*b6932704SJoseph Chen self.root_digest = data[(self.SIZE + o):(self.SIZE + o + root_digest_len)] 1423*b6932704SJoseph Chen 1424*b6932704SJoseph Chen if root_digest_len != self._hashtree_digest_size(): 1425*b6932704SJoseph Chen if root_digest_len != 0: 1426*b6932704SJoseph Chen raise LookupError('root_digest_len doesn\'t match hash algorithm') 1427*b6932704SJoseph Chen 1428*b6932704SJoseph Chen else: 1429*b6932704SJoseph Chen self.dm_verity_version = 0 1430*b6932704SJoseph Chen self.image_size = 0 1431*b6932704SJoseph Chen self.tree_offset = 0 1432*b6932704SJoseph Chen self.tree_size = 0 1433*b6932704SJoseph Chen self.data_block_size = 0 1434*b6932704SJoseph Chen self.hash_block_size = 0 1435*b6932704SJoseph Chen self.fec_num_roots = 0 1436*b6932704SJoseph Chen self.fec_offset = 0 1437*b6932704SJoseph Chen self.fec_size = 0 1438*b6932704SJoseph Chen self.hash_algorithm = '' 1439*b6932704SJoseph Chen self.partition_name = '' 1440*b6932704SJoseph Chen self.salt = b'' 1441*b6932704SJoseph Chen self.root_digest = b'' 1442*b6932704SJoseph Chen self.flags = 0 1443*b6932704SJoseph Chen 1444*b6932704SJoseph Chen def _hashtree_digest_size(self): 1445*b6932704SJoseph Chen return len(create_avb_hashtree_hasher(self.hash_algorithm, b'').digest()) 1446*b6932704SJoseph Chen 1447*b6932704SJoseph Chen def print_desc(self, o): 1448*b6932704SJoseph Chen """Print the descriptor. 1449*b6932704SJoseph Chen 1450*b6932704SJoseph Chen Arguments: 1451*b6932704SJoseph Chen o: The object to write the output to. 1452*b6932704SJoseph Chen """ 1453*b6932704SJoseph Chen o.write(' Hashtree descriptor:\n') 1454*b6932704SJoseph Chen o.write(' Version of dm-verity: {}\n'.format(self.dm_verity_version)) 1455*b6932704SJoseph Chen o.write(' Image Size: {} bytes\n'.format(self.image_size)) 1456*b6932704SJoseph Chen o.write(' Tree Offset: {}\n'.format(self.tree_offset)) 1457*b6932704SJoseph Chen o.write(' Tree Size: {} bytes\n'.format(self.tree_size)) 1458*b6932704SJoseph Chen o.write(' Data Block Size: {} bytes\n'.format( 1459*b6932704SJoseph Chen self.data_block_size)) 1460*b6932704SJoseph Chen o.write(' Hash Block Size: {} bytes\n'.format( 1461*b6932704SJoseph Chen self.hash_block_size)) 1462*b6932704SJoseph Chen o.write(' FEC num roots: {}\n'.format(self.fec_num_roots)) 1463*b6932704SJoseph Chen o.write(' FEC offset: {}\n'.format(self.fec_offset)) 1464*b6932704SJoseph Chen o.write(' FEC size: {} bytes\n'.format(self.fec_size)) 1465*b6932704SJoseph Chen o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm)) 1466*b6932704SJoseph Chen o.write(' Partition Name: {}\n'.format(self.partition_name)) 1467*b6932704SJoseph Chen o.write(' Salt: {}\n'.format(self.salt.hex())) 1468*b6932704SJoseph Chen o.write(' Root Digest: {}\n'.format(self.root_digest.hex())) 1469*b6932704SJoseph Chen o.write(' Flags: {}\n'.format(self.flags)) 1470*b6932704SJoseph Chen 1471*b6932704SJoseph Chen def encode(self): 1472*b6932704SJoseph Chen """Serializes the descriptor. 1473*b6932704SJoseph Chen 1474*b6932704SJoseph Chen Returns: 1475*b6932704SJoseph Chen The descriptor data as bytes. 1476*b6932704SJoseph Chen """ 1477*b6932704SJoseph Chen hash_algorithm_encoded = self.hash_algorithm.encode('ascii') 1478*b6932704SJoseph Chen partition_name_encoded = self.partition_name.encode('utf-8') 1479*b6932704SJoseph Chen num_bytes_following = (self.SIZE + len(partition_name_encoded) 1480*b6932704SJoseph Chen + len(self.salt) + len(self.root_digest) - 16) 1481*b6932704SJoseph Chen nbf_with_padding = round_to_multiple(num_bytes_following, 8) 1482*b6932704SJoseph Chen padding_size = nbf_with_padding - num_bytes_following 1483*b6932704SJoseph Chen desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding, 1484*b6932704SJoseph Chen self.dm_verity_version, self.image_size, 1485*b6932704SJoseph Chen self.tree_offset, self.tree_size, self.data_block_size, 1486*b6932704SJoseph Chen self.hash_block_size, self.fec_num_roots, 1487*b6932704SJoseph Chen self.fec_offset, self.fec_size, hash_algorithm_encoded, 1488*b6932704SJoseph Chen len(partition_name_encoded), len(self.salt), 1489*b6932704SJoseph Chen len(self.root_digest), self.flags, self.RESERVED * b'\0') 1490*b6932704SJoseph Chen ret = (desc + partition_name_encoded + self.salt + self.root_digest + 1491*b6932704SJoseph Chen padding_size * b'\0') 1492*b6932704SJoseph Chen return ret 1493*b6932704SJoseph Chen 1494*b6932704SJoseph Chen def verify(self, image_dir, image_ext, expected_chain_partitions_map, 1495*b6932704SJoseph Chen image_containing_descriptor, accept_zeroed_hashtree): 1496*b6932704SJoseph Chen """Verifies contents of the descriptor - used in verify_image sub-command. 1497*b6932704SJoseph Chen 1498*b6932704SJoseph Chen Arguments: 1499*b6932704SJoseph Chen image_dir: The directory of the file being verified. 1500*b6932704SJoseph Chen image_ext: The extension of the file being verified (e.g. '.img'). 1501*b6932704SJoseph Chen expected_chain_partitions_map: A map from partition name to the 1502*b6932704SJoseph Chen tuple (rollback_index_location, key_blob). 1503*b6932704SJoseph Chen image_containing_descriptor: The image the descriptor is in. 1504*b6932704SJoseph Chen accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is 1505*b6932704SJoseph Chen zeroed out. 1506*b6932704SJoseph Chen 1507*b6932704SJoseph Chen Returns: 1508*b6932704SJoseph Chen True if the descriptor verifies, False otherwise. 1509*b6932704SJoseph Chen """ 1510*b6932704SJoseph Chen if not self.partition_name: 1511*b6932704SJoseph Chen image_filename = image_containing_descriptor.filename 1512*b6932704SJoseph Chen image = image_containing_descriptor 1513*b6932704SJoseph Chen else: 1514*b6932704SJoseph Chen image_filename = os.path.join(image_dir, self.partition_name + image_ext) 1515*b6932704SJoseph Chen image = ImageHandler(image_filename, read_only=True) 1516*b6932704SJoseph Chen # Generate the hashtree and checks that it matches what's in the file. 1517*b6932704SJoseph Chen digest_size = self._hashtree_digest_size() 1518*b6932704SJoseph Chen digest_padding = round_to_pow2(digest_size) - digest_size 1519*b6932704SJoseph Chen (hash_level_offsets, tree_size) = calc_hash_level_offsets( 1520*b6932704SJoseph Chen self.image_size, self.data_block_size, digest_size + digest_padding) 1521*b6932704SJoseph Chen root_digest, hash_tree = generate_hash_tree(image, self.image_size, 1522*b6932704SJoseph Chen self.data_block_size, 1523*b6932704SJoseph Chen self.hash_algorithm, self.salt, 1524*b6932704SJoseph Chen digest_padding, 1525*b6932704SJoseph Chen hash_level_offsets, 1526*b6932704SJoseph Chen tree_size) 1527*b6932704SJoseph Chen # The root digest must match unless it is not embedded in the descriptor. 1528*b6932704SJoseph Chen if self.root_digest and root_digest != self.root_digest: 1529*b6932704SJoseph Chen sys.stderr.write('hashtree of {} does not match descriptor\n'. 1530*b6932704SJoseph Chen format(image_filename)) 1531*b6932704SJoseph Chen return False 1532*b6932704SJoseph Chen # ... also check that the on-disk hashtree matches 1533*b6932704SJoseph Chen image.seek(self.tree_offset) 1534*b6932704SJoseph Chen hash_tree_ondisk = image.read(self.tree_size) 1535*b6932704SJoseph Chen is_zeroed = (self.tree_size == 0) or (hash_tree_ondisk[0:8] == b'ZeRoHaSH') 1536*b6932704SJoseph Chen if is_zeroed and accept_zeroed_hashtree: 1537*b6932704SJoseph Chen print('{}: skipping verification since hashtree is zeroed and ' 1538*b6932704SJoseph Chen '--accept_zeroed_hashtree was given' 1539*b6932704SJoseph Chen .format(self.partition_name)) 1540*b6932704SJoseph Chen else: 1541*b6932704SJoseph Chen if hash_tree != hash_tree_ondisk: 1542*b6932704SJoseph Chen sys.stderr.write('hashtree of {} contains invalid data\n'. 1543*b6932704SJoseph Chen format(image_filename)) 1544*b6932704SJoseph Chen return False 1545*b6932704SJoseph Chen print('{}: Successfully verified {} hashtree of {} for image of {} bytes' 1546*b6932704SJoseph Chen .format(self.partition_name, self.hash_algorithm, image.filename, 1547*b6932704SJoseph Chen self.image_size)) 1548*b6932704SJoseph Chen # TODO(zeuthen): we could also verify that the FEC stored in the image is 1549*b6932704SJoseph Chen # correct but this a) currently requires the 'fec' binary; and b) takes a 1550*b6932704SJoseph Chen # long time; and c) is not strictly needed for verification purposes as 1551*b6932704SJoseph Chen # we've already verified the root hash. 1552*b6932704SJoseph Chen return True 1553*b6932704SJoseph Chen 1554*b6932704SJoseph Chen 1555*b6932704SJoseph Chenclass AvbHashDescriptor(AvbDescriptor): 1556*b6932704SJoseph Chen """A class for hash descriptors. 1557*b6932704SJoseph Chen 1558*b6932704SJoseph Chen See the |AvbHashDescriptor| C struct for more information. 1559*b6932704SJoseph Chen 1560*b6932704SJoseph Chen Attributes: 1561*b6932704SJoseph Chen image_size: Image size, in bytes. 1562*b6932704SJoseph Chen hash_algorithm: Hash algorithm used as string. 1563*b6932704SJoseph Chen partition_name: Partition name as string. 1564*b6932704SJoseph Chen salt: Salt used as bytes. 1565*b6932704SJoseph Chen digest: The hash value of salt and data combined as bytes. 1566*b6932704SJoseph Chen flags: The descriptor flags (see avb_hash_descriptor.h). 1567*b6932704SJoseph Chen """ 1568*b6932704SJoseph Chen 1569*b6932704SJoseph Chen TAG = 2 1570*b6932704SJoseph Chen RESERVED = 60 1571*b6932704SJoseph Chen SIZE = 72 + RESERVED 1572*b6932704SJoseph Chen FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header) 1573*b6932704SJoseph Chen 'Q' # image size (bytes) 1574*b6932704SJoseph Chen '32s' # hash algorithm used 1575*b6932704SJoseph Chen 'L' # partition name (bytes) 1576*b6932704SJoseph Chen 'L' # salt length (bytes) 1577*b6932704SJoseph Chen 'L' # digest length (bytes) 1578*b6932704SJoseph Chen 'L' + # flags 1579*b6932704SJoseph Chen str(RESERVED) + 's') # reserved 1580*b6932704SJoseph Chen 1581*b6932704SJoseph Chen def __init__(self, data=None): 1582*b6932704SJoseph Chen """Initializes a new hash descriptor. 1583*b6932704SJoseph Chen 1584*b6932704SJoseph Chen Arguments: 1585*b6932704SJoseph Chen data: If not None, must be bytes of size |SIZE|. 1586*b6932704SJoseph Chen 1587*b6932704SJoseph Chen Raises: 1588*b6932704SJoseph Chen LookupError: If the given descriptor is malformed. 1589*b6932704SJoseph Chen """ 1590*b6932704SJoseph Chen super().__init__(None) 1591*b6932704SJoseph Chen assert struct.calcsize(self.FORMAT_STRING) == self.SIZE 1592*b6932704SJoseph Chen 1593*b6932704SJoseph Chen if data: 1594*b6932704SJoseph Chen (tag, num_bytes_following, self.image_size, self.hash_algorithm, 1595*b6932704SJoseph Chen partition_name_len, salt_len, 1596*b6932704SJoseph Chen digest_len, self.flags, _) = struct.unpack(self.FORMAT_STRING, 1597*b6932704SJoseph Chen data[0:self.SIZE]) 1598*b6932704SJoseph Chen expected_size = round_to_multiple( 1599*b6932704SJoseph Chen self.SIZE - 16 + partition_name_len + salt_len + digest_len, 8) 1600*b6932704SJoseph Chen if tag != self.TAG or num_bytes_following != expected_size: 1601*b6932704SJoseph Chen raise LookupError('Given data does not look like a hash descriptor.') 1602*b6932704SJoseph Chen # Nuke NUL-bytes at the end. 1603*b6932704SJoseph Chen self.hash_algorithm = self.hash_algorithm.rstrip(b'\0').decode('ascii') 1604*b6932704SJoseph Chen o = 0 1605*b6932704SJoseph Chen try: 1606*b6932704SJoseph Chen self.partition_name = data[ 1607*b6932704SJoseph Chen (self.SIZE + o):(self.SIZE + o + partition_name_len) 1608*b6932704SJoseph Chen ].decode('utf-8') 1609*b6932704SJoseph Chen except UnicodeDecodeError as e: 1610*b6932704SJoseph Chen raise LookupError('Partition name cannot be decoded as UTF-8: {}.' 1611*b6932704SJoseph Chen .format(e)) from e 1612*b6932704SJoseph Chen o += partition_name_len 1613*b6932704SJoseph Chen self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)] 1614*b6932704SJoseph Chen o += salt_len 1615*b6932704SJoseph Chen self.digest = data[(self.SIZE + o):(self.SIZE + o + digest_len)] 1616*b6932704SJoseph Chen if digest_len != len(hashlib.new(self.hash_algorithm).digest()): 1617*b6932704SJoseph Chen if digest_len != 0: 1618*b6932704SJoseph Chen raise LookupError('digest_len doesn\'t match hash algorithm') 1619*b6932704SJoseph Chen 1620*b6932704SJoseph Chen else: 1621*b6932704SJoseph Chen self.image_size = 0 1622*b6932704SJoseph Chen self.hash_algorithm = '' 1623*b6932704SJoseph Chen self.partition_name = '' 1624*b6932704SJoseph Chen self.salt = b'' 1625*b6932704SJoseph Chen self.digest = b'' 1626*b6932704SJoseph Chen self.flags = 0 1627*b6932704SJoseph Chen 1628*b6932704SJoseph Chen def print_desc(self, o): 1629*b6932704SJoseph Chen """Print the descriptor. 1630*b6932704SJoseph Chen 1631*b6932704SJoseph Chen Arguments: 1632*b6932704SJoseph Chen o: The object to write the output to. 1633*b6932704SJoseph Chen """ 1634*b6932704SJoseph Chen o.write(' Hash descriptor:\n') 1635*b6932704SJoseph Chen o.write(' Image Size: {} bytes\n'.format(self.image_size)) 1636*b6932704SJoseph Chen o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm)) 1637*b6932704SJoseph Chen o.write(' Partition Name: {}\n'.format(self.partition_name)) 1638*b6932704SJoseph Chen o.write(' Salt: {}\n'.format(self.salt.hex())) 1639*b6932704SJoseph Chen o.write(' Digest: {}\n'.format(self.digest.hex())) 1640*b6932704SJoseph Chen o.write(' Flags: {}\n'.format(self.flags)) 1641*b6932704SJoseph Chen 1642*b6932704SJoseph Chen def encode(self): 1643*b6932704SJoseph Chen """Serializes the descriptor. 1644*b6932704SJoseph Chen 1645*b6932704SJoseph Chen Returns: 1646*b6932704SJoseph Chen The descriptor data as bytes. 1647*b6932704SJoseph Chen """ 1648*b6932704SJoseph Chen hash_algorithm_encoded = self.hash_algorithm.encode('ascii') 1649*b6932704SJoseph Chen partition_name_encoded = self.partition_name.encode('utf-8') 1650*b6932704SJoseph Chen num_bytes_following = (self.SIZE + len(partition_name_encoded) + 1651*b6932704SJoseph Chen len(self.salt) + len(self.digest) - 16) 1652*b6932704SJoseph Chen nbf_with_padding = round_to_multiple(num_bytes_following, 8) 1653*b6932704SJoseph Chen padding_size = nbf_with_padding - num_bytes_following 1654*b6932704SJoseph Chen desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding, 1655*b6932704SJoseph Chen self.image_size, hash_algorithm_encoded, 1656*b6932704SJoseph Chen len(partition_name_encoded), len(self.salt), 1657*b6932704SJoseph Chen len(self.digest), self.flags, self.RESERVED * b'\0') 1658*b6932704SJoseph Chen ret = (desc + partition_name_encoded + self.salt + self.digest + 1659*b6932704SJoseph Chen padding_size * b'\0') 1660*b6932704SJoseph Chen return ret 1661*b6932704SJoseph Chen 1662*b6932704SJoseph Chen def verify(self, image_dir, image_ext, expected_chain_partitions_map, 1663*b6932704SJoseph Chen image_containing_descriptor, accept_zeroed_hashtree): 1664*b6932704SJoseph Chen """Verifies contents of the descriptor - used in verify_image sub-command. 1665*b6932704SJoseph Chen 1666*b6932704SJoseph Chen Arguments: 1667*b6932704SJoseph Chen image_dir: The directory of the file being verified. 1668*b6932704SJoseph Chen image_ext: The extension of the file being verified (e.g. '.img'). 1669*b6932704SJoseph Chen expected_chain_partitions_map: A map from partition name to the 1670*b6932704SJoseph Chen tuple (rollback_index_location, key_blob). 1671*b6932704SJoseph Chen image_containing_descriptor: The image the descriptor is in. 1672*b6932704SJoseph Chen accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is 1673*b6932704SJoseph Chen zeroed out. 1674*b6932704SJoseph Chen 1675*b6932704SJoseph Chen Returns: 1676*b6932704SJoseph Chen True if the descriptor verifies, False otherwise. 1677*b6932704SJoseph Chen """ 1678*b6932704SJoseph Chen if not self.partition_name: 1679*b6932704SJoseph Chen image_filename = image_containing_descriptor.filename 1680*b6932704SJoseph Chen image = image_containing_descriptor 1681*b6932704SJoseph Chen else: 1682*b6932704SJoseph Chen image_filename = os.path.join(image_dir, self.partition_name + image_ext) 1683*b6932704SJoseph Chen image = ImageHandler(image_filename, read_only=True) 1684*b6932704SJoseph Chen data = image.read(self.image_size) 1685*b6932704SJoseph Chen ha = hashlib.new(self.hash_algorithm) 1686*b6932704SJoseph Chen ha.update(self.salt) 1687*b6932704SJoseph Chen ha.update(data) 1688*b6932704SJoseph Chen digest = ha.digest() 1689*b6932704SJoseph Chen # The digest must match unless there is no digest in the descriptor. 1690*b6932704SJoseph Chen if self.digest and digest != self.digest: 1691*b6932704SJoseph Chen sys.stderr.write('{} digest of {} does not match digest in descriptor\n'. 1692*b6932704SJoseph Chen format(self.hash_algorithm, image_filename)) 1693*b6932704SJoseph Chen return False 1694*b6932704SJoseph Chen print('{}: Successfully verified {} hash of {} for image of {} bytes' 1695*b6932704SJoseph Chen .format(self.partition_name, self.hash_algorithm, image.filename, 1696*b6932704SJoseph Chen self.image_size)) 1697*b6932704SJoseph Chen return True 1698*b6932704SJoseph Chen 1699*b6932704SJoseph Chen 1700*b6932704SJoseph Chenclass AvbKernelCmdlineDescriptor(AvbDescriptor): 1701*b6932704SJoseph Chen """A class for kernel command-line descriptors. 1702*b6932704SJoseph Chen 1703*b6932704SJoseph Chen See the |AvbKernelCmdlineDescriptor| C struct for more information. 1704*b6932704SJoseph Chen 1705*b6932704SJoseph Chen Attributes: 1706*b6932704SJoseph Chen flags: Flags. 1707*b6932704SJoseph Chen kernel_cmdline: The kernel command-line as string. 1708*b6932704SJoseph Chen """ 1709*b6932704SJoseph Chen 1710*b6932704SJoseph Chen TAG = 3 1711*b6932704SJoseph Chen SIZE = 24 1712*b6932704SJoseph Chen FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header) 1713*b6932704SJoseph Chen 'L' # flags 1714*b6932704SJoseph Chen 'L') # cmdline length (bytes) 1715*b6932704SJoseph Chen 1716*b6932704SJoseph Chen FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED = (1 << 0) 1717*b6932704SJoseph Chen FLAGS_USE_ONLY_IF_HASHTREE_DISABLED = (1 << 1) 1718*b6932704SJoseph Chen 1719*b6932704SJoseph Chen def __init__(self, data=None): 1720*b6932704SJoseph Chen """Initializes a new kernel cmdline descriptor. 1721*b6932704SJoseph Chen 1722*b6932704SJoseph Chen Arguments: 1723*b6932704SJoseph Chen data: If not None, must be bytes of size |SIZE|. 1724*b6932704SJoseph Chen 1725*b6932704SJoseph Chen Raises: 1726*b6932704SJoseph Chen LookupError: If the given descriptor is malformed. 1727*b6932704SJoseph Chen """ 1728*b6932704SJoseph Chen super().__init__(None) 1729*b6932704SJoseph Chen assert struct.calcsize(self.FORMAT_STRING) == self.SIZE 1730*b6932704SJoseph Chen 1731*b6932704SJoseph Chen if data: 1732*b6932704SJoseph Chen (tag, num_bytes_following, self.flags, kernel_cmdline_length) = ( 1733*b6932704SJoseph Chen struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])) 1734*b6932704SJoseph Chen expected_size = round_to_multiple(self.SIZE - 16 + kernel_cmdline_length, 1735*b6932704SJoseph Chen 8) 1736*b6932704SJoseph Chen if tag != self.TAG or num_bytes_following != expected_size: 1737*b6932704SJoseph Chen raise LookupError('Given data does not look like a kernel cmdline ' 1738*b6932704SJoseph Chen 'descriptor.') 1739*b6932704SJoseph Chen # Nuke NUL-bytes at the end. 1740*b6932704SJoseph Chen try: 1741*b6932704SJoseph Chen self.kernel_cmdline = data[ 1742*b6932704SJoseph Chen self.SIZE:(self.SIZE + kernel_cmdline_length)].decode('utf-8') 1743*b6932704SJoseph Chen except UnicodeDecodeError as e: 1744*b6932704SJoseph Chen raise LookupError('Kernel command-line cannot be decoded as UTF-8: {}.' 1745*b6932704SJoseph Chen .format(e)) from e 1746*b6932704SJoseph Chen else: 1747*b6932704SJoseph Chen self.flags = 0 1748*b6932704SJoseph Chen self.kernel_cmdline = '' 1749*b6932704SJoseph Chen 1750*b6932704SJoseph Chen def print_desc(self, o): 1751*b6932704SJoseph Chen """Print the descriptor. 1752*b6932704SJoseph Chen 1753*b6932704SJoseph Chen Arguments: 1754*b6932704SJoseph Chen o: The object to write the output to. 1755*b6932704SJoseph Chen """ 1756*b6932704SJoseph Chen o.write(' Kernel Cmdline descriptor:\n') 1757*b6932704SJoseph Chen o.write(' Flags: {}\n'.format(self.flags)) 1758*b6932704SJoseph Chen o.write(' Kernel Cmdline: \'{}\'\n'.format(self.kernel_cmdline)) 1759*b6932704SJoseph Chen 1760*b6932704SJoseph Chen def encode(self): 1761*b6932704SJoseph Chen """Serializes the descriptor. 1762*b6932704SJoseph Chen 1763*b6932704SJoseph Chen Returns: 1764*b6932704SJoseph Chen The descriptor data as bytes. 1765*b6932704SJoseph Chen """ 1766*b6932704SJoseph Chen kernel_cmd_encoded = self.kernel_cmdline.encode('utf-8') 1767*b6932704SJoseph Chen num_bytes_following = (self.SIZE + len(kernel_cmd_encoded) - 16) 1768*b6932704SJoseph Chen nbf_with_padding = round_to_multiple(num_bytes_following, 8) 1769*b6932704SJoseph Chen padding_size = nbf_with_padding - num_bytes_following 1770*b6932704SJoseph Chen desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding, 1771*b6932704SJoseph Chen self.flags, len(kernel_cmd_encoded)) 1772*b6932704SJoseph Chen ret = desc + kernel_cmd_encoded + padding_size * b'\0' 1773*b6932704SJoseph Chen return ret 1774*b6932704SJoseph Chen 1775*b6932704SJoseph Chen def verify(self, image_dir, image_ext, expected_chain_partitions_map, 1776*b6932704SJoseph Chen image_containing_descriptor, accept_zeroed_hashtree): 1777*b6932704SJoseph Chen """Verifies contents of the descriptor - used in verify_image sub-command. 1778*b6932704SJoseph Chen 1779*b6932704SJoseph Chen Arguments: 1780*b6932704SJoseph Chen image_dir: The directory of the file being verified. 1781*b6932704SJoseph Chen image_ext: The extension of the file being verified (e.g. '.img'). 1782*b6932704SJoseph Chen expected_chain_partitions_map: A map from partition name to the 1783*b6932704SJoseph Chen tuple (rollback_index_location, key_blob). 1784*b6932704SJoseph Chen image_containing_descriptor: The image the descriptor is in. 1785*b6932704SJoseph Chen accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is 1786*b6932704SJoseph Chen zeroed out. 1787*b6932704SJoseph Chen 1788*b6932704SJoseph Chen Returns: 1789*b6932704SJoseph Chen True if the descriptor verifies, False otherwise. 1790*b6932704SJoseph Chen """ 1791*b6932704SJoseph Chen # Nothing to verify. 1792*b6932704SJoseph Chen return True 1793*b6932704SJoseph Chen 1794*b6932704SJoseph Chen 1795*b6932704SJoseph Chenclass AvbChainPartitionDescriptor(AvbDescriptor): 1796*b6932704SJoseph Chen """A class for chained partition descriptors. 1797*b6932704SJoseph Chen 1798*b6932704SJoseph Chen See the |AvbChainPartitionDescriptor| C struct for more information. 1799*b6932704SJoseph Chen 1800*b6932704SJoseph Chen Attributes: 1801*b6932704SJoseph Chen rollback_index_location: The rollback index location to use. 1802*b6932704SJoseph Chen partition_name: Partition name as string. 1803*b6932704SJoseph Chen public_key: The public key as bytes. 1804*b6932704SJoseph Chen """ 1805*b6932704SJoseph Chen 1806*b6932704SJoseph Chen TAG = 4 1807*b6932704SJoseph Chen RESERVED = 64 1808*b6932704SJoseph Chen SIZE = 28 + RESERVED 1809*b6932704SJoseph Chen FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header) 1810*b6932704SJoseph Chen 'L' # rollback_index_location 1811*b6932704SJoseph Chen 'L' # partition_name_size (bytes) 1812*b6932704SJoseph Chen 'L' + # public_key_size (bytes) 1813*b6932704SJoseph Chen str(RESERVED) + 's') # reserved 1814*b6932704SJoseph Chen 1815*b6932704SJoseph Chen def __init__(self, data=None): 1816*b6932704SJoseph Chen """Initializes a new chain partition descriptor. 1817*b6932704SJoseph Chen 1818*b6932704SJoseph Chen Arguments: 1819*b6932704SJoseph Chen data: If not None, must be a bytearray of size |SIZE|. 1820*b6932704SJoseph Chen 1821*b6932704SJoseph Chen Raises: 1822*b6932704SJoseph Chen LookupError: If the given descriptor is malformed. 1823*b6932704SJoseph Chen """ 1824*b6932704SJoseph Chen AvbDescriptor.__init__(self, None) 1825*b6932704SJoseph Chen assert struct.calcsize(self.FORMAT_STRING) == self.SIZE 1826*b6932704SJoseph Chen 1827*b6932704SJoseph Chen if data: 1828*b6932704SJoseph Chen (tag, num_bytes_following, self.rollback_index_location, 1829*b6932704SJoseph Chen partition_name_len, 1830*b6932704SJoseph Chen public_key_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]) 1831*b6932704SJoseph Chen expected_size = round_to_multiple( 1832*b6932704SJoseph Chen self.SIZE - 16 + partition_name_len + public_key_len, 8) 1833*b6932704SJoseph Chen if tag != self.TAG or num_bytes_following != expected_size: 1834*b6932704SJoseph Chen raise LookupError('Given data does not look like a chain partition ' 1835*b6932704SJoseph Chen 'descriptor.') 1836*b6932704SJoseph Chen o = 0 1837*b6932704SJoseph Chen try: 1838*b6932704SJoseph Chen self.partition_name = data[ 1839*b6932704SJoseph Chen (self.SIZE + o):(self.SIZE + o + partition_name_len) 1840*b6932704SJoseph Chen ].decode('utf-8') 1841*b6932704SJoseph Chen except UnicodeDecodeError as e: 1842*b6932704SJoseph Chen raise LookupError('Partition name cannot be decoded as UTF-8: {}.' 1843*b6932704SJoseph Chen .format(e)) from e 1844*b6932704SJoseph Chen o += partition_name_len 1845*b6932704SJoseph Chen self.public_key = data[(self.SIZE + o):(self.SIZE + o + public_key_len)] 1846*b6932704SJoseph Chen 1847*b6932704SJoseph Chen else: 1848*b6932704SJoseph Chen self.rollback_index_location = 0 1849*b6932704SJoseph Chen self.partition_name = '' 1850*b6932704SJoseph Chen self.public_key = b'' 1851*b6932704SJoseph Chen 1852*b6932704SJoseph Chen def print_desc(self, o): 1853*b6932704SJoseph Chen """Print the descriptor. 1854*b6932704SJoseph Chen 1855*b6932704SJoseph Chen Arguments: 1856*b6932704SJoseph Chen o: The object to write the output to. 1857*b6932704SJoseph Chen """ 1858*b6932704SJoseph Chen o.write(' Chain Partition descriptor:\n') 1859*b6932704SJoseph Chen o.write(' Partition Name: {}\n'.format(self.partition_name)) 1860*b6932704SJoseph Chen o.write(' Rollback Index Location: {}\n'.format( 1861*b6932704SJoseph Chen self.rollback_index_location)) 1862*b6932704SJoseph Chen # Just show the SHA1 of the key, for size reasons. 1863*b6932704SJoseph Chen pubkey_digest = hashlib.sha1(self.public_key).hexdigest() 1864*b6932704SJoseph Chen o.write(' Public key (sha1): {}\n'.format(pubkey_digest)) 1865*b6932704SJoseph Chen 1866*b6932704SJoseph Chen def encode(self): 1867*b6932704SJoseph Chen """Serializes the descriptor. 1868*b6932704SJoseph Chen 1869*b6932704SJoseph Chen Returns: 1870*b6932704SJoseph Chen The descriptor data as bytes. 1871*b6932704SJoseph Chen """ 1872*b6932704SJoseph Chen partition_name_encoded = self.partition_name.encode('utf-8') 1873*b6932704SJoseph Chen num_bytes_following = ( 1874*b6932704SJoseph Chen self.SIZE + len(partition_name_encoded) + len(self.public_key) - 16) 1875*b6932704SJoseph Chen nbf_with_padding = round_to_multiple(num_bytes_following, 8) 1876*b6932704SJoseph Chen padding_size = nbf_with_padding - num_bytes_following 1877*b6932704SJoseph Chen desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding, 1878*b6932704SJoseph Chen self.rollback_index_location, 1879*b6932704SJoseph Chen len(partition_name_encoded), len(self.public_key), 1880*b6932704SJoseph Chen self.RESERVED * b'\0') 1881*b6932704SJoseph Chen ret = desc + partition_name_encoded + self.public_key + padding_size * b'\0' 1882*b6932704SJoseph Chen return ret 1883*b6932704SJoseph Chen 1884*b6932704SJoseph Chen def verify(self, image_dir, image_ext, expected_chain_partitions_map, 1885*b6932704SJoseph Chen image_containing_descriptor, accept_zeroed_hashtree): 1886*b6932704SJoseph Chen """Verifies contents of the descriptor - used in verify_image sub-command. 1887*b6932704SJoseph Chen 1888*b6932704SJoseph Chen Arguments: 1889*b6932704SJoseph Chen image_dir: The directory of the file being verified. 1890*b6932704SJoseph Chen image_ext: The extension of the file being verified (e.g. '.img'). 1891*b6932704SJoseph Chen expected_chain_partitions_map: A map from partition name to the 1892*b6932704SJoseph Chen tuple (rollback_index_location, key_blob). 1893*b6932704SJoseph Chen image_containing_descriptor: The image the descriptor is in. 1894*b6932704SJoseph Chen accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is 1895*b6932704SJoseph Chen zeroed out. 1896*b6932704SJoseph Chen 1897*b6932704SJoseph Chen Returns: 1898*b6932704SJoseph Chen True if the descriptor verifies, False otherwise. 1899*b6932704SJoseph Chen """ 1900*b6932704SJoseph Chen value = expected_chain_partitions_map.get(self.partition_name) 1901*b6932704SJoseph Chen if not value: 1902*b6932704SJoseph Chen sys.stderr.write('No expected chain partition for partition {}. Use ' 1903*b6932704SJoseph Chen '--expected_chain_partition to specify expected ' 1904*b6932704SJoseph Chen 'contents or --follow_chain_partitions.\n'. 1905*b6932704SJoseph Chen format(self.partition_name)) 1906*b6932704SJoseph Chen return False 1907*b6932704SJoseph Chen rollback_index_location, pk_blob = value 1908*b6932704SJoseph Chen 1909*b6932704SJoseph Chen if self.rollback_index_location != rollback_index_location: 1910*b6932704SJoseph Chen sys.stderr.write('Expected rollback_index_location {} does not ' 1911*b6932704SJoseph Chen 'match {} in descriptor for partition {}\n'. 1912*b6932704SJoseph Chen format(rollback_index_location, 1913*b6932704SJoseph Chen self.rollback_index_location, 1914*b6932704SJoseph Chen self.partition_name)) 1915*b6932704SJoseph Chen return False 1916*b6932704SJoseph Chen 1917*b6932704SJoseph Chen if self.public_key != pk_blob: 1918*b6932704SJoseph Chen sys.stderr.write('Expected public key blob does not match public ' 1919*b6932704SJoseph Chen 'key blob in descriptor for partition {}\n'. 1920*b6932704SJoseph Chen format(self.partition_name)) 1921*b6932704SJoseph Chen return False 1922*b6932704SJoseph Chen 1923*b6932704SJoseph Chen print('{}: Successfully verified chain partition descriptor matches ' 1924*b6932704SJoseph Chen 'expected data'.format(self.partition_name)) 1925*b6932704SJoseph Chen 1926*b6932704SJoseph Chen return True 1927*b6932704SJoseph Chen 1928*b6932704SJoseph ChenDESCRIPTOR_CLASSES = [ 1929*b6932704SJoseph Chen AvbPropertyDescriptor, AvbHashtreeDescriptor, AvbHashDescriptor, 1930*b6932704SJoseph Chen AvbKernelCmdlineDescriptor, AvbChainPartitionDescriptor 1931*b6932704SJoseph Chen] 1932*b6932704SJoseph Chen 1933*b6932704SJoseph Chen 1934*b6932704SJoseph Chendef parse_descriptors(data): 1935*b6932704SJoseph Chen """Parses a blob of data into descriptors. 1936*b6932704SJoseph Chen 1937*b6932704SJoseph Chen Arguments: 1938*b6932704SJoseph Chen data: Encoded descriptors as bytes. 1939*b6932704SJoseph Chen 1940*b6932704SJoseph Chen Returns: 1941*b6932704SJoseph Chen A list of instances of objects derived from AvbDescriptor. For 1942*b6932704SJoseph Chen unknown descriptors, the class AvbDescriptor is used. 1943*b6932704SJoseph Chen """ 1944*b6932704SJoseph Chen o = 0 1945*b6932704SJoseph Chen ret = [] 1946*b6932704SJoseph Chen while o < len(data): 1947*b6932704SJoseph Chen tag, nb_following = struct.unpack('!2Q', data[o:o + 16]) 1948*b6932704SJoseph Chen if tag < len(DESCRIPTOR_CLASSES): 1949*b6932704SJoseph Chen clazz = DESCRIPTOR_CLASSES[tag] 1950*b6932704SJoseph Chen else: 1951*b6932704SJoseph Chen clazz = AvbDescriptor 1952*b6932704SJoseph Chen ret.append(clazz(data[o:o + 16 + nb_following])) 1953*b6932704SJoseph Chen o += 16 + nb_following 1954*b6932704SJoseph Chen return ret 1955*b6932704SJoseph Chen 1956*b6932704SJoseph Chen 1957*b6932704SJoseph Chenclass AvbFooter(object): 1958*b6932704SJoseph Chen """A class for parsing and writing footers. 1959*b6932704SJoseph Chen 1960*b6932704SJoseph Chen Footers are stored at the end of partitions and point to where the 1961*b6932704SJoseph Chen AvbVBMeta blob is located. They also contain the original size of 1962*b6932704SJoseph Chen the image before AVB information was added. 1963*b6932704SJoseph Chen 1964*b6932704SJoseph Chen Attributes: 1965*b6932704SJoseph Chen magic: Magic for identifying the footer, see |MAGIC|. 1966*b6932704SJoseph Chen version_major: The major version of avbtool that wrote the footer. 1967*b6932704SJoseph Chen version_minor: The minor version of avbtool that wrote the footer. 1968*b6932704SJoseph Chen original_image_size: Original image size. 1969*b6932704SJoseph Chen vbmeta_offset: Offset of where the AvbVBMeta blob is stored. 1970*b6932704SJoseph Chen vbmeta_size: Size of the AvbVBMeta blob. 1971*b6932704SJoseph Chen """ 1972*b6932704SJoseph Chen 1973*b6932704SJoseph Chen MAGIC = b'AVBf' 1974*b6932704SJoseph Chen SIZE = 64 1975*b6932704SJoseph Chen RESERVED = 28 1976*b6932704SJoseph Chen FOOTER_VERSION_MAJOR = AVB_FOOTER_VERSION_MAJOR 1977*b6932704SJoseph Chen FOOTER_VERSION_MINOR = AVB_FOOTER_VERSION_MINOR 1978*b6932704SJoseph Chen FORMAT_STRING = ('!4s2L' # magic, 2 x version. 1979*b6932704SJoseph Chen 'Q' # Original image size. 1980*b6932704SJoseph Chen 'Q' # Offset of VBMeta blob. 1981*b6932704SJoseph Chen 'Q' + # Size of VBMeta blob. 1982*b6932704SJoseph Chen str(RESERVED) + 'x') # padding for reserved bytes 1983*b6932704SJoseph Chen 1984*b6932704SJoseph Chen def __init__(self, data=None): 1985*b6932704SJoseph Chen """Initializes a new footer object. 1986*b6932704SJoseph Chen 1987*b6932704SJoseph Chen Arguments: 1988*b6932704SJoseph Chen data: If not None, must be bytes of size 4096. 1989*b6932704SJoseph Chen 1990*b6932704SJoseph Chen Raises: 1991*b6932704SJoseph Chen LookupError: If the given footer is malformed. 1992*b6932704SJoseph Chen struct.error: If the given data has no footer. 1993*b6932704SJoseph Chen """ 1994*b6932704SJoseph Chen assert struct.calcsize(self.FORMAT_STRING) == self.SIZE 1995*b6932704SJoseph Chen 1996*b6932704SJoseph Chen if data: 1997*b6932704SJoseph Chen (self.magic, self.version_major, self.version_minor, 1998*b6932704SJoseph Chen self.original_image_size, self.vbmeta_offset, 1999*b6932704SJoseph Chen self.vbmeta_size) = struct.unpack(self.FORMAT_STRING, data) 2000*b6932704SJoseph Chen if self.magic != self.MAGIC: 2001*b6932704SJoseph Chen raise LookupError('Given data does not look like a AVB footer.') 2002*b6932704SJoseph Chen else: 2003*b6932704SJoseph Chen self.magic = self.MAGIC 2004*b6932704SJoseph Chen self.version_major = self.FOOTER_VERSION_MAJOR 2005*b6932704SJoseph Chen self.version_minor = self.FOOTER_VERSION_MINOR 2006*b6932704SJoseph Chen self.original_image_size = 0 2007*b6932704SJoseph Chen self.vbmeta_offset = 0 2008*b6932704SJoseph Chen self.vbmeta_size = 0 2009*b6932704SJoseph Chen 2010*b6932704SJoseph Chen def encode(self): 2011*b6932704SJoseph Chen """Serializes the footer. 2012*b6932704SJoseph Chen 2013*b6932704SJoseph Chen Returns: 2014*b6932704SJoseph Chen The footer as bytes. 2015*b6932704SJoseph Chen """ 2016*b6932704SJoseph Chen return struct.pack(self.FORMAT_STRING, self.magic, self.version_major, 2017*b6932704SJoseph Chen self.version_minor, self.original_image_size, 2018*b6932704SJoseph Chen self.vbmeta_offset, self.vbmeta_size) 2019*b6932704SJoseph Chen 2020*b6932704SJoseph Chen 2021*b6932704SJoseph Chenclass AvbVBMetaHeader(object): 2022*b6932704SJoseph Chen """A class for parsing and writing AVB vbmeta images. 2023*b6932704SJoseph Chen 2024*b6932704SJoseph Chen The attributes correspond to the |AvbVBMetaImageHeader| struct defined in 2025*b6932704SJoseph Chen avb_vbmeta_image.h. 2026*b6932704SJoseph Chen 2027*b6932704SJoseph Chen Attributes: 2028*b6932704SJoseph Chen magic: Four bytes equal to "AVB0" (AVB_MAGIC). 2029*b6932704SJoseph Chen required_libavb_version_major: The major version of libavb required for this 2030*b6932704SJoseph Chen header. 2031*b6932704SJoseph Chen required_libavb_version_minor: The minor version of libavb required for this 2032*b6932704SJoseph Chen header. 2033*b6932704SJoseph Chen authentication_data_block_size: The size of the signature block. 2034*b6932704SJoseph Chen auxiliary_data_block_size: The size of the auxiliary data block. 2035*b6932704SJoseph Chen algorithm_type: The verification algorithm used, see |AvbAlgorithmType| 2036*b6932704SJoseph Chen enum. 2037*b6932704SJoseph Chen hash_offset: Offset into the "Authentication data" block of hash data. 2038*b6932704SJoseph Chen hash_size: Length of the hash data. 2039*b6932704SJoseph Chen signature_offset: Offset into the "Authentication data" block of signature 2040*b6932704SJoseph Chen data. 2041*b6932704SJoseph Chen signature_size: Length of the signature data. 2042*b6932704SJoseph Chen public_key_offset: Offset into the "Auxiliary data" block of public key 2043*b6932704SJoseph Chen data. 2044*b6932704SJoseph Chen public_key_size: Length of the public key data. 2045*b6932704SJoseph Chen public_key_metadata_offset: Offset into the "Auxiliary data" block of public 2046*b6932704SJoseph Chen key metadata. 2047*b6932704SJoseph Chen public_key_metadata_size: Length of the public key metadata. Must be set to 2048*b6932704SJoseph Chen zero if there is no public key metadata. 2049*b6932704SJoseph Chen descriptors_offset: Offset into the "Auxiliary data" block of descriptor 2050*b6932704SJoseph Chen data. 2051*b6932704SJoseph Chen descriptors_size: Length of descriptor data. 2052*b6932704SJoseph Chen rollback_index: The rollback index which can be used to prevent rollback to 2053*b6932704SJoseph Chen older versions. 2054*b6932704SJoseph Chen flags: Flags from the AvbVBMetaImageFlags enumeration. This must be set to 2055*b6932704SJoseph Chen zero if the vbmeta image is not a top-level image. 2056*b6932704SJoseph Chen rollback_index_location: The location of the rollback index defined in this 2057*b6932704SJoseph Chen header. Only valid for the main vbmeta. For chained partitions, the 2058*b6932704SJoseph Chen rollback index location must be specified in the 2059*b6932704SJoseph Chen AvbChainPartitionDescriptor and this value must be set to 0. 2060*b6932704SJoseph Chen release_string: The release string from avbtool, e.g. "avbtool 1.0.0" or 2061*b6932704SJoseph Chen "avbtool 1.0.0 xyz_board Git-234abde89". Is guaranteed to be NUL 2062*b6932704SJoseph Chen terminated. Applications must not make assumptions about how this 2063*b6932704SJoseph Chen string is formatted. 2064*b6932704SJoseph Chen """ 2065*b6932704SJoseph Chen MAGIC = b'AVB0' 2066*b6932704SJoseph Chen SIZE = 256 2067*b6932704SJoseph Chen 2068*b6932704SJoseph Chen # Keep in sync with |reserved| field of |AvbVBMetaImageHeader|. 2069*b6932704SJoseph Chen RESERVED = 80 2070*b6932704SJoseph Chen 2071*b6932704SJoseph Chen # Keep in sync with |AvbVBMetaImageHeader|. 2072*b6932704SJoseph Chen FORMAT_STRING = ('!4s2L' # magic, 2 x version 2073*b6932704SJoseph Chen '2Q' # 2 x block size 2074*b6932704SJoseph Chen 'L' # algorithm type 2075*b6932704SJoseph Chen '2Q' # offset, size (hash) 2076*b6932704SJoseph Chen '2Q' # offset, size (signature) 2077*b6932704SJoseph Chen '2Q' # offset, size (public key) 2078*b6932704SJoseph Chen '2Q' # offset, size (public key metadata) 2079*b6932704SJoseph Chen '2Q' # offset, size (descriptors) 2080*b6932704SJoseph Chen 'Q' # rollback_index 2081*b6932704SJoseph Chen 'L' # flags 2082*b6932704SJoseph Chen 'L' # rollback_index_location 2083*b6932704SJoseph Chen '47sx' + # NUL-terminated release string 2084*b6932704SJoseph Chen str(RESERVED) + 'x') # padding for reserved bytes 2085*b6932704SJoseph Chen 2086*b6932704SJoseph Chen def __init__(self, data=None): 2087*b6932704SJoseph Chen """Initializes a new header object. 2088*b6932704SJoseph Chen 2089*b6932704SJoseph Chen Arguments: 2090*b6932704SJoseph Chen data: If not None, must be a bytearray of size 8192. 2091*b6932704SJoseph Chen 2092*b6932704SJoseph Chen Raises: 2093*b6932704SJoseph Chen Exception: If the given data is malformed. 2094*b6932704SJoseph Chen """ 2095*b6932704SJoseph Chen assert struct.calcsize(self.FORMAT_STRING) == self.SIZE 2096*b6932704SJoseph Chen 2097*b6932704SJoseph Chen if data: 2098*b6932704SJoseph Chen (self.magic, self.required_libavb_version_major, 2099*b6932704SJoseph Chen self.required_libavb_version_minor, 2100*b6932704SJoseph Chen self.authentication_data_block_size, self.auxiliary_data_block_size, 2101*b6932704SJoseph Chen self.algorithm_type, self.hash_offset, self.hash_size, 2102*b6932704SJoseph Chen self.signature_offset, self.signature_size, self.public_key_offset, 2103*b6932704SJoseph Chen self.public_key_size, self.public_key_metadata_offset, 2104*b6932704SJoseph Chen self.public_key_metadata_size, self.descriptors_offset, 2105*b6932704SJoseph Chen self.descriptors_size, 2106*b6932704SJoseph Chen self.rollback_index, 2107*b6932704SJoseph Chen self.flags, 2108*b6932704SJoseph Chen self.rollback_index_location, 2109*b6932704SJoseph Chen release_string) = struct.unpack(self.FORMAT_STRING, data) 2110*b6932704SJoseph Chen # Nuke NUL-bytes at the end of the string. 2111*b6932704SJoseph Chen if self.magic != self.MAGIC: 2112*b6932704SJoseph Chen raise AvbError('Given image does not look like a vbmeta image.') 2113*b6932704SJoseph Chen self.release_string = release_string.rstrip(b'\0').decode('utf-8') 2114*b6932704SJoseph Chen else: 2115*b6932704SJoseph Chen self.magic = self.MAGIC 2116*b6932704SJoseph Chen # Start by just requiring version 1.0. Code that adds features 2117*b6932704SJoseph Chen # in a future version can use bump_required_libavb_version_minor() to 2118*b6932704SJoseph Chen # bump the minor. 2119*b6932704SJoseph Chen self.required_libavb_version_major = AVB_VERSION_MAJOR 2120*b6932704SJoseph Chen self.required_libavb_version_minor = 0 2121*b6932704SJoseph Chen self.authentication_data_block_size = 0 2122*b6932704SJoseph Chen self.auxiliary_data_block_size = 0 2123*b6932704SJoseph Chen self.algorithm_type = 0 2124*b6932704SJoseph Chen self.hash_offset = 0 2125*b6932704SJoseph Chen self.hash_size = 0 2126*b6932704SJoseph Chen self.signature_offset = 0 2127*b6932704SJoseph Chen self.signature_size = 0 2128*b6932704SJoseph Chen self.public_key_offset = 0 2129*b6932704SJoseph Chen self.public_key_size = 0 2130*b6932704SJoseph Chen self.public_key_metadata_offset = 0 2131*b6932704SJoseph Chen self.public_key_metadata_size = 0 2132*b6932704SJoseph Chen self.descriptors_offset = 0 2133*b6932704SJoseph Chen self.descriptors_size = 0 2134*b6932704SJoseph Chen self.rollback_index = 0 2135*b6932704SJoseph Chen self.flags = 0 2136*b6932704SJoseph Chen self.rollback_index_location = 0 2137*b6932704SJoseph Chen self.release_string = get_release_string() 2138*b6932704SJoseph Chen 2139*b6932704SJoseph Chen def bump_required_libavb_version_minor(self, minor): 2140*b6932704SJoseph Chen """Function to bump required_libavb_version_minor. 2141*b6932704SJoseph Chen 2142*b6932704SJoseph Chen Call this when writing data that requires a specific libavb 2143*b6932704SJoseph Chen version to parse it. 2144*b6932704SJoseph Chen 2145*b6932704SJoseph Chen Arguments: 2146*b6932704SJoseph Chen minor: The minor version of libavb that has support for the feature. 2147*b6932704SJoseph Chen """ 2148*b6932704SJoseph Chen self.required_libavb_version_minor = ( 2149*b6932704SJoseph Chen max(self.required_libavb_version_minor, minor)) 2150*b6932704SJoseph Chen 2151*b6932704SJoseph Chen def encode(self): 2152*b6932704SJoseph Chen """Serializes the header. 2153*b6932704SJoseph Chen 2154*b6932704SJoseph Chen Returns: 2155*b6932704SJoseph Chen The header as bytes. 2156*b6932704SJoseph Chen """ 2157*b6932704SJoseph Chen release_string_encoded = self.release_string.encode('utf-8') 2158*b6932704SJoseph Chen return struct.pack(self.FORMAT_STRING, self.magic, 2159*b6932704SJoseph Chen self.required_libavb_version_major, 2160*b6932704SJoseph Chen self.required_libavb_version_minor, 2161*b6932704SJoseph Chen self.authentication_data_block_size, 2162*b6932704SJoseph Chen self.auxiliary_data_block_size, self.algorithm_type, 2163*b6932704SJoseph Chen self.hash_offset, self.hash_size, self.signature_offset, 2164*b6932704SJoseph Chen self.signature_size, self.public_key_offset, 2165*b6932704SJoseph Chen self.public_key_size, self.public_key_metadata_offset, 2166*b6932704SJoseph Chen self.public_key_metadata_size, self.descriptors_offset, 2167*b6932704SJoseph Chen self.descriptors_size, self.rollback_index, self.flags, 2168*b6932704SJoseph Chen self.rollback_index_location, release_string_encoded) 2169*b6932704SJoseph Chen 2170*b6932704SJoseph Chen 2171*b6932704SJoseph Chenclass Avb(object): 2172*b6932704SJoseph Chen """Business logic for avbtool command-line tool.""" 2173*b6932704SJoseph Chen 2174*b6932704SJoseph Chen # Keep in sync with avb_ab_flow.h. 2175*b6932704SJoseph Chen AB_FORMAT_NO_CRC = '!4sBB2xBBBxBBBx12x' 2176*b6932704SJoseph Chen AB_MAGIC = b'\0AB0' 2177*b6932704SJoseph Chen AB_MAJOR_VERSION = 1 2178*b6932704SJoseph Chen AB_MINOR_VERSION = 0 2179*b6932704SJoseph Chen AB_MISC_METADATA_OFFSET = 2048 2180*b6932704SJoseph Chen 2181*b6932704SJoseph Chen # Constants for maximum metadata size. These are used to give 2182*b6932704SJoseph Chen # meaningful errors if the value passed in via --partition_size is 2183*b6932704SJoseph Chen # too small and when --calc_max_image_size is used. We use 2184*b6932704SJoseph Chen # conservative figures. 2185*b6932704SJoseph Chen MAX_VBMETA_SIZE = 64 * 1024 2186*b6932704SJoseph Chen MAX_FOOTER_SIZE = 4096 2187*b6932704SJoseph Chen 2188*b6932704SJoseph Chen def generate_test_image(self, output, image_size, start_byte): 2189*b6932704SJoseph Chen """Generates a test image for testing avbtool with known content. 2190*b6932704SJoseph Chen 2191*b6932704SJoseph Chen The content has following pattern: 0x00 0x01 0x02 .. 0xff 0x00 0x01 ..). 2192*b6932704SJoseph Chen 2193*b6932704SJoseph Chen Arguments: 2194*b6932704SJoseph Chen output: Write test image to this file. 2195*b6932704SJoseph Chen image_size: The size of the requested file in bytes. 2196*b6932704SJoseph Chen start_byte: The integer value of the start byte to use for pattern 2197*b6932704SJoseph Chen generation. 2198*b6932704SJoseph Chen """ 2199*b6932704SJoseph Chen pattern = bytearray([x & 0xFF for x in range(start_byte, start_byte + 256)]) 2200*b6932704SJoseph Chen buf = bytearray() 2201*b6932704SJoseph Chen c = int(math.ceil(image_size / 256.0)) 2202*b6932704SJoseph Chen for _ in range(0, c): 2203*b6932704SJoseph Chen buf.extend(pattern) 2204*b6932704SJoseph Chen output.write(buf[0:image_size]) 2205*b6932704SJoseph Chen 2206*b6932704SJoseph Chen def extract_vbmeta_image(self, output, image_filename, padding_size): 2207*b6932704SJoseph Chen """Implements the 'extract_vbmeta_image' command. 2208*b6932704SJoseph Chen 2209*b6932704SJoseph Chen Arguments: 2210*b6932704SJoseph Chen output: Write vbmeta struct to this file. 2211*b6932704SJoseph Chen image_filename: File to extract vbmeta data from (with a footer). 2212*b6932704SJoseph Chen padding_size: If not 0, pads output so size is a multiple of the number. 2213*b6932704SJoseph Chen 2214*b6932704SJoseph Chen Raises: 2215*b6932704SJoseph Chen AvbError: If there's no footer in the image. 2216*b6932704SJoseph Chen """ 2217*b6932704SJoseph Chen image = ImageHandler(image_filename, read_only=True) 2218*b6932704SJoseph Chen (footer, _, _, _) = self._parse_image(image) 2219*b6932704SJoseph Chen if not footer: 2220*b6932704SJoseph Chen raise AvbError('Given image does not have a footer.') 2221*b6932704SJoseph Chen 2222*b6932704SJoseph Chen image.seek(footer.vbmeta_offset) 2223*b6932704SJoseph Chen vbmeta_blob = image.read(footer.vbmeta_size) 2224*b6932704SJoseph Chen output.write(vbmeta_blob) 2225*b6932704SJoseph Chen 2226*b6932704SJoseph Chen if padding_size > 0: 2227*b6932704SJoseph Chen padded_size = round_to_multiple(len(vbmeta_blob), padding_size) 2228*b6932704SJoseph Chen padding_needed = padded_size - len(vbmeta_blob) 2229*b6932704SJoseph Chen output.write(b'\0' * padding_needed) 2230*b6932704SJoseph Chen 2231*b6932704SJoseph Chen def erase_footer(self, image_filename, keep_hashtree): 2232*b6932704SJoseph Chen """Implements the 'erase_footer' command. 2233*b6932704SJoseph Chen 2234*b6932704SJoseph Chen Arguments: 2235*b6932704SJoseph Chen image_filename: File to erase a footer from. 2236*b6932704SJoseph Chen keep_hashtree: If True, keep the hashtree and FEC around. 2237*b6932704SJoseph Chen 2238*b6932704SJoseph Chen Raises: 2239*b6932704SJoseph Chen AvbError: If there's no footer in the image. 2240*b6932704SJoseph Chen """ 2241*b6932704SJoseph Chen image = ImageHandler(image_filename) 2242*b6932704SJoseph Chen (footer, _, descriptors, _) = self._parse_image(image) 2243*b6932704SJoseph Chen if not footer: 2244*b6932704SJoseph Chen raise AvbError('Given image does not have a footer.') 2245*b6932704SJoseph Chen 2246*b6932704SJoseph Chen new_image_size = None 2247*b6932704SJoseph Chen if not keep_hashtree: 2248*b6932704SJoseph Chen new_image_size = footer.original_image_size 2249*b6932704SJoseph Chen else: 2250*b6932704SJoseph Chen # If requested to keep the hashtree, search for a hashtree 2251*b6932704SJoseph Chen # descriptor to figure out the location and size of the hashtree 2252*b6932704SJoseph Chen # and FEC. 2253*b6932704SJoseph Chen for desc in descriptors: 2254*b6932704SJoseph Chen if isinstance(desc, AvbHashtreeDescriptor): 2255*b6932704SJoseph Chen # The hashtree is always just following the main data so the 2256*b6932704SJoseph Chen # new size is easily derived. 2257*b6932704SJoseph Chen new_image_size = desc.tree_offset + desc.tree_size 2258*b6932704SJoseph Chen # If the image has FEC codes, also keep those. 2259*b6932704SJoseph Chen if desc.fec_offset > 0: 2260*b6932704SJoseph Chen fec_end = desc.fec_offset + desc.fec_size 2261*b6932704SJoseph Chen new_image_size = max(new_image_size, fec_end) 2262*b6932704SJoseph Chen break 2263*b6932704SJoseph Chen if not new_image_size: 2264*b6932704SJoseph Chen raise AvbError('Requested to keep hashtree but no hashtree ' 2265*b6932704SJoseph Chen 'descriptor was found.') 2266*b6932704SJoseph Chen 2267*b6932704SJoseph Chen # And cut... 2268*b6932704SJoseph Chen image.truncate(new_image_size) 2269*b6932704SJoseph Chen 2270*b6932704SJoseph Chen def zero_hashtree(self, image_filename): 2271*b6932704SJoseph Chen """Implements the 'zero_hashtree' command. 2272*b6932704SJoseph Chen 2273*b6932704SJoseph Chen Arguments: 2274*b6932704SJoseph Chen image_filename: File to zero hashtree and FEC data from. 2275*b6932704SJoseph Chen 2276*b6932704SJoseph Chen Raises: 2277*b6932704SJoseph Chen AvbError: If there's no footer in the image. 2278*b6932704SJoseph Chen """ 2279*b6932704SJoseph Chen image = ImageHandler(image_filename) 2280*b6932704SJoseph Chen (footer, _, descriptors, _) = self._parse_image(image) 2281*b6932704SJoseph Chen if not footer: 2282*b6932704SJoseph Chen raise AvbError('Given image does not have a footer.') 2283*b6932704SJoseph Chen 2284*b6932704SJoseph Chen # Search for a hashtree descriptor to figure out the location and 2285*b6932704SJoseph Chen # size of the hashtree and FEC. 2286*b6932704SJoseph Chen ht_desc = None 2287*b6932704SJoseph Chen for desc in descriptors: 2288*b6932704SJoseph Chen if isinstance(desc, AvbHashtreeDescriptor): 2289*b6932704SJoseph Chen ht_desc = desc 2290*b6932704SJoseph Chen break 2291*b6932704SJoseph Chen 2292*b6932704SJoseph Chen if not ht_desc: 2293*b6932704SJoseph Chen raise AvbError('No hashtree descriptor was found.') 2294*b6932704SJoseph Chen 2295*b6932704SJoseph Chen zero_ht_start_offset = ht_desc.tree_offset 2296*b6932704SJoseph Chen zero_ht_num_bytes = ht_desc.tree_size 2297*b6932704SJoseph Chen zero_fec_start_offset = None 2298*b6932704SJoseph Chen zero_fec_num_bytes = 0 2299*b6932704SJoseph Chen if ht_desc.fec_offset > 0: 2300*b6932704SJoseph Chen if ht_desc.fec_offset != ht_desc.tree_offset + ht_desc.tree_size: 2301*b6932704SJoseph Chen raise AvbError('Hash-tree and FEC data must be adjacent.') 2302*b6932704SJoseph Chen zero_fec_start_offset = ht_desc.fec_offset 2303*b6932704SJoseph Chen zero_fec_num_bytes = ht_desc.fec_size 2304*b6932704SJoseph Chen zero_end_offset = (zero_ht_start_offset + zero_ht_num_bytes 2305*b6932704SJoseph Chen + zero_fec_num_bytes) 2306*b6932704SJoseph Chen image.seek(zero_end_offset) 2307*b6932704SJoseph Chen data = image.read(image.image_size - zero_end_offset) 2308*b6932704SJoseph Chen 2309*b6932704SJoseph Chen # Write zeroes all over hashtree and FEC, except for the first eight bytes 2310*b6932704SJoseph Chen # where a magic marker - ZeroHaSH - is placed. Place these markers in the 2311*b6932704SJoseph Chen # beginning of both hashtree and FEC. (That way, in the future we can add 2312*b6932704SJoseph Chen # options to 'avbtool zero_hashtree' so as to zero out only either/or.) 2313*b6932704SJoseph Chen # 2314*b6932704SJoseph Chen # Applications can use these markers to detect that the hashtree and/or 2315*b6932704SJoseph Chen # FEC needs to be recomputed. 2316*b6932704SJoseph Chen image.truncate(zero_ht_start_offset) 2317*b6932704SJoseph Chen data_zeroed_firstblock = b'ZeRoHaSH' + b'\0' * (image.block_size - 8) 2318*b6932704SJoseph Chen image.append_raw(data_zeroed_firstblock) 2319*b6932704SJoseph Chen image.append_fill(b'\0\0\0\0', zero_ht_num_bytes - image.block_size) 2320*b6932704SJoseph Chen if zero_fec_start_offset: 2321*b6932704SJoseph Chen image.append_raw(data_zeroed_firstblock) 2322*b6932704SJoseph Chen image.append_fill(b'\0\0\0\0', zero_fec_num_bytes - image.block_size) 2323*b6932704SJoseph Chen image.append_raw(data) 2324*b6932704SJoseph Chen 2325*b6932704SJoseph Chen def resize_image(self, image_filename, partition_size): 2326*b6932704SJoseph Chen """Implements the 'resize_image' command. 2327*b6932704SJoseph Chen 2328*b6932704SJoseph Chen Arguments: 2329*b6932704SJoseph Chen image_filename: File with footer to resize. 2330*b6932704SJoseph Chen partition_size: The new size of the image. 2331*b6932704SJoseph Chen 2332*b6932704SJoseph Chen Raises: 2333*b6932704SJoseph Chen AvbError: If there's no footer in the image. 2334*b6932704SJoseph Chen """ 2335*b6932704SJoseph Chen 2336*b6932704SJoseph Chen image = ImageHandler(image_filename) 2337*b6932704SJoseph Chen if partition_size % image.block_size != 0: 2338*b6932704SJoseph Chen raise AvbError('Partition size of {} is not a multiple of the image ' 2339*b6932704SJoseph Chen 'block size {}.'.format(partition_size, 2340*b6932704SJoseph Chen image.block_size)) 2341*b6932704SJoseph Chen (footer, _, _, _) = self._parse_image(image) 2342*b6932704SJoseph Chen if not footer: 2343*b6932704SJoseph Chen raise AvbError('Given image does not have a footer.') 2344*b6932704SJoseph Chen 2345*b6932704SJoseph Chen # The vbmeta blob is always at the end of the data so resizing an 2346*b6932704SJoseph Chen # image amounts to just moving the footer around. 2347*b6932704SJoseph Chen vbmeta_end_offset = footer.vbmeta_offset + footer.vbmeta_size 2348*b6932704SJoseph Chen if vbmeta_end_offset % image.block_size != 0: 2349*b6932704SJoseph Chen vbmeta_end_offset += image.block_size - (vbmeta_end_offset 2350*b6932704SJoseph Chen % image.block_size) 2351*b6932704SJoseph Chen 2352*b6932704SJoseph Chen if partition_size < vbmeta_end_offset + 1 * image.block_size: 2353*b6932704SJoseph Chen raise AvbError('Requested size of {} is too small for an image ' 2354*b6932704SJoseph Chen 'of size {}.' 2355*b6932704SJoseph Chen .format(partition_size, 2356*b6932704SJoseph Chen vbmeta_end_offset + 1 * image.block_size)) 2357*b6932704SJoseph Chen 2358*b6932704SJoseph Chen # Cut at the end of the vbmeta blob and insert a DONT_CARE chunk 2359*b6932704SJoseph Chen # with enough bytes such that the final Footer block is at the end 2360*b6932704SJoseph Chen # of partition_size. 2361*b6932704SJoseph Chen image.truncate(vbmeta_end_offset) 2362*b6932704SJoseph Chen image.append_dont_care(partition_size - vbmeta_end_offset - 2363*b6932704SJoseph Chen 1 * image.block_size) 2364*b6932704SJoseph Chen 2365*b6932704SJoseph Chen # Just reuse the same footer - only difference is that we're 2366*b6932704SJoseph Chen # writing it in a different place. 2367*b6932704SJoseph Chen footer_blob = footer.encode() 2368*b6932704SJoseph Chen footer_blob_with_padding = (b'\0' * (image.block_size - AvbFooter.SIZE) + 2369*b6932704SJoseph Chen footer_blob) 2370*b6932704SJoseph Chen image.append_raw(footer_blob_with_padding) 2371*b6932704SJoseph Chen 2372*b6932704SJoseph Chen def set_ab_metadata(self, misc_image, slot_data): 2373*b6932704SJoseph Chen """Implements the 'set_ab_metadata' command. 2374*b6932704SJoseph Chen 2375*b6932704SJoseph Chen The |slot_data| argument must be of the form 'A_priority:A_tries_remaining: 2376*b6932704SJoseph Chen A_successful_boot:B_priority:B_tries_remaining:B_successful_boot'. 2377*b6932704SJoseph Chen 2378*b6932704SJoseph Chen Arguments: 2379*b6932704SJoseph Chen misc_image: The misc image to write to. 2380*b6932704SJoseph Chen slot_data: Slot data as a string 2381*b6932704SJoseph Chen 2382*b6932704SJoseph Chen Raises: 2383*b6932704SJoseph Chen AvbError: If slot data is malformed. 2384*b6932704SJoseph Chen """ 2385*b6932704SJoseph Chen tokens = slot_data.split(':') 2386*b6932704SJoseph Chen if len(tokens) != 6: 2387*b6932704SJoseph Chen raise AvbError('Malformed slot data "{}".'.format(slot_data)) 2388*b6932704SJoseph Chen a_priority = int(tokens[0]) 2389*b6932704SJoseph Chen a_tries_remaining = int(tokens[1]) 2390*b6932704SJoseph Chen a_success = int(tokens[2]) != 0 2391*b6932704SJoseph Chen b_priority = int(tokens[3]) 2392*b6932704SJoseph Chen b_tries_remaining = int(tokens[4]) 2393*b6932704SJoseph Chen b_success = int(tokens[5]) != 0 2394*b6932704SJoseph Chen 2395*b6932704SJoseph Chen ab_data_no_crc = struct.pack(self.AB_FORMAT_NO_CRC, 2396*b6932704SJoseph Chen self.AB_MAGIC, 2397*b6932704SJoseph Chen self.AB_MAJOR_VERSION, self.AB_MINOR_VERSION, 2398*b6932704SJoseph Chen a_priority, a_tries_remaining, a_success, 2399*b6932704SJoseph Chen b_priority, b_tries_remaining, b_success) 2400*b6932704SJoseph Chen # Force CRC to be unsigned, see https://bugs.python.org/issue4903 for why. 2401*b6932704SJoseph Chen crc_value = binascii.crc32(ab_data_no_crc) & 0xffffffff 2402*b6932704SJoseph Chen ab_data = ab_data_no_crc + struct.pack('!I', crc_value) 2403*b6932704SJoseph Chen misc_image.seek(self.AB_MISC_METADATA_OFFSET) 2404*b6932704SJoseph Chen misc_image.write(ab_data) 2405*b6932704SJoseph Chen 2406*b6932704SJoseph Chen def info_image(self, image_filename, output, atx): 2407*b6932704SJoseph Chen """Implements the 'info_image' command. 2408*b6932704SJoseph Chen 2409*b6932704SJoseph Chen Arguments: 2410*b6932704SJoseph Chen image_filename: Image file to get information from (file object). 2411*b6932704SJoseph Chen output: Output file to write human-readable information to (file object). 2412*b6932704SJoseph Chen atx: If True, show information about Android Things eXtension (ATX). 2413*b6932704SJoseph Chen """ 2414*b6932704SJoseph Chen image = ImageHandler(image_filename, read_only=True) 2415*b6932704SJoseph Chen o = output 2416*b6932704SJoseph Chen (footer, header, descriptors, image_size) = self._parse_image(image) 2417*b6932704SJoseph Chen 2418*b6932704SJoseph Chen # To show the SHA1 of the public key. 2419*b6932704SJoseph Chen vbmeta_blob = self._load_vbmeta_blob(image) 2420*b6932704SJoseph Chen key_offset = (header.SIZE + 2421*b6932704SJoseph Chen header.authentication_data_block_size + 2422*b6932704SJoseph Chen header.public_key_offset) 2423*b6932704SJoseph Chen key_blob = vbmeta_blob[key_offset:key_offset + header.public_key_size] 2424*b6932704SJoseph Chen 2425*b6932704SJoseph Chen if footer: 2426*b6932704SJoseph Chen o.write('Footer version: {}.{}\n'.format(footer.version_major, 2427*b6932704SJoseph Chen footer.version_minor)) 2428*b6932704SJoseph Chen o.write('Image size: {} bytes\n'.format(image_size)) 2429*b6932704SJoseph Chen o.write('Original image size: {} bytes\n'.format( 2430*b6932704SJoseph Chen footer.original_image_size)) 2431*b6932704SJoseph Chen o.write('VBMeta offset: {}\n'.format(footer.vbmeta_offset)) 2432*b6932704SJoseph Chen o.write('VBMeta size: {} bytes\n'.format(footer.vbmeta_size)) 2433*b6932704SJoseph Chen o.write('--\n') 2434*b6932704SJoseph Chen 2435*b6932704SJoseph Chen (alg_name, _) = lookup_algorithm_by_type(header.algorithm_type) 2436*b6932704SJoseph Chen 2437*b6932704SJoseph Chen o.write('Minimum libavb version: {}.{}{}\n'.format( 2438*b6932704SJoseph Chen header.required_libavb_version_major, 2439*b6932704SJoseph Chen header.required_libavb_version_minor, 2440*b6932704SJoseph Chen ' (Sparse)' if image.is_sparse else '')) 2441*b6932704SJoseph Chen o.write('Header Block: {} bytes\n'.format(AvbVBMetaHeader.SIZE)) 2442*b6932704SJoseph Chen o.write('Authentication Block: {} bytes\n'.format( 2443*b6932704SJoseph Chen header.authentication_data_block_size)) 2444*b6932704SJoseph Chen o.write('Auxiliary Block: {} bytes\n'.format( 2445*b6932704SJoseph Chen header.auxiliary_data_block_size)) 2446*b6932704SJoseph Chen if key_blob: 2447*b6932704SJoseph Chen hexdig = hashlib.sha1(key_blob).hexdigest() 2448*b6932704SJoseph Chen o.write('Public key (sha1): {}\n'.format(hexdig)) 2449*b6932704SJoseph Chen o.write('Algorithm: {}\n'.format(alg_name)) 2450*b6932704SJoseph Chen o.write('Rollback Index: {}\n'.format(header.rollback_index)) 2451*b6932704SJoseph Chen o.write('Flags: {}\n'.format(header.flags)) 2452*b6932704SJoseph Chen o.write('Rollback Index Location: {}\n'.format( 2453*b6932704SJoseph Chen header.rollback_index_location)) 2454*b6932704SJoseph Chen o.write('Release String: \'{}\'\n'.format(header.release_string)) 2455*b6932704SJoseph Chen 2456*b6932704SJoseph Chen # Print descriptors. 2457*b6932704SJoseph Chen num_printed = 0 2458*b6932704SJoseph Chen o.write('Descriptors:\n') 2459*b6932704SJoseph Chen for desc in descriptors: 2460*b6932704SJoseph Chen desc.print_desc(o) 2461*b6932704SJoseph Chen num_printed += 1 2462*b6932704SJoseph Chen if num_printed == 0: 2463*b6932704SJoseph Chen o.write(' (none)\n') 2464*b6932704SJoseph Chen 2465*b6932704SJoseph Chen if atx and header.public_key_metadata_size: 2466*b6932704SJoseph Chen o.write('Android Things eXtension (ATX):\n') 2467*b6932704SJoseph Chen key_metadata_offset = (header.SIZE + 2468*b6932704SJoseph Chen header.authentication_data_block_size + 2469*b6932704SJoseph Chen header.public_key_metadata_offset) 2470*b6932704SJoseph Chen key_metadata_blob = vbmeta_blob[key_metadata_offset: key_metadata_offset 2471*b6932704SJoseph Chen + header.public_key_metadata_size] 2472*b6932704SJoseph Chen version, pik, psk = struct.unpack('<I1620s1620s', key_metadata_blob) 2473*b6932704SJoseph Chen o.write(' Metadata version: {}\n'.format(version)) 2474*b6932704SJoseph Chen 2475*b6932704SJoseph Chen def print_atx_certificate(cert): 2476*b6932704SJoseph Chen version, public_key, subject, usage, key_version, _ = ( 2477*b6932704SJoseph Chen struct.unpack('<I1032s32s32sQ512s', cert)) 2478*b6932704SJoseph Chen o.write(' Version: {}\n'.format(version)) 2479*b6932704SJoseph Chen o.write(' Public key (sha1): {}\n'.format( 2480*b6932704SJoseph Chen hashlib.sha1(public_key).hexdigest())) 2481*b6932704SJoseph Chen o.write(' Subject: {}\n'.format(subject.hex())) 2482*b6932704SJoseph Chen o.write(' Usage: {}\n'.format(usage.hex())) 2483*b6932704SJoseph Chen o.write(' Key version: {}\n'.format(key_version)) 2484*b6932704SJoseph Chen 2485*b6932704SJoseph Chen o.write(' Product Intermediate Key:\n') 2486*b6932704SJoseph Chen print_atx_certificate(pik) 2487*b6932704SJoseph Chen o.write(' Product Signing Key:\n') 2488*b6932704SJoseph Chen print_atx_certificate(psk) 2489*b6932704SJoseph Chen 2490*b6932704SJoseph Chen def verify_image(self, image_filename, key_path, expected_chain_partitions, 2491*b6932704SJoseph Chen follow_chain_partitions, accept_zeroed_hashtree): 2492*b6932704SJoseph Chen """Implements the 'verify_image' command. 2493*b6932704SJoseph Chen 2494*b6932704SJoseph Chen Arguments: 2495*b6932704SJoseph Chen image_filename: Image file to get information from (file object). 2496*b6932704SJoseph Chen key_path: None or check that embedded public key matches key at given 2497*b6932704SJoseph Chen path. 2498*b6932704SJoseph Chen expected_chain_partitions: List of chain partitions to check or None. 2499*b6932704SJoseph Chen follow_chain_partitions: 2500*b6932704SJoseph Chen If True, will follows chain partitions even when not specified with 2501*b6932704SJoseph Chen the --expected_chain_partition option 2502*b6932704SJoseph Chen accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is 2503*b6932704SJoseph Chen zeroed out. 2504*b6932704SJoseph Chen 2505*b6932704SJoseph Chen Raises: 2506*b6932704SJoseph Chen AvbError: If verification of the image fails. 2507*b6932704SJoseph Chen """ 2508*b6932704SJoseph Chen expected_chain_partitions_map = {} 2509*b6932704SJoseph Chen if expected_chain_partitions: 2510*b6932704SJoseph Chen for cp in expected_chain_partitions: 2511*b6932704SJoseph Chen cp_tokens = cp.split(':') 2512*b6932704SJoseph Chen if len(cp_tokens) != 3: 2513*b6932704SJoseph Chen raise AvbError('Malformed chained partition "{}".'.format(cp)) 2514*b6932704SJoseph Chen partition_name = cp_tokens[0] 2515*b6932704SJoseph Chen rollback_index_location = int(cp_tokens[1]) 2516*b6932704SJoseph Chen file_path = cp_tokens[2] 2517*b6932704SJoseph Chen with open(file_path, 'rb') as f: 2518*b6932704SJoseph Chen pk_blob = f.read() 2519*b6932704SJoseph Chen expected_chain_partitions_map[partition_name] = ( 2520*b6932704SJoseph Chen rollback_index_location, pk_blob) 2521*b6932704SJoseph Chen 2522*b6932704SJoseph Chen image_dir = os.path.dirname(image_filename) 2523*b6932704SJoseph Chen image_ext = os.path.splitext(image_filename)[1] 2524*b6932704SJoseph Chen 2525*b6932704SJoseph Chen key_blob = None 2526*b6932704SJoseph Chen if key_path: 2527*b6932704SJoseph Chen print('Verifying image {} using key at {}'.format(image_filename, 2528*b6932704SJoseph Chen key_path)) 2529*b6932704SJoseph Chen key_blob = RSAPublicKey(key_path).encode() 2530*b6932704SJoseph Chen else: 2531*b6932704SJoseph Chen print('Verifying image {} using embedded public key'.format( 2532*b6932704SJoseph Chen image_filename)) 2533*b6932704SJoseph Chen 2534*b6932704SJoseph Chen image = ImageHandler(image_filename, read_only=True) 2535*b6932704SJoseph Chen (footer, header, descriptors, _) = self._parse_image(image) 2536*b6932704SJoseph Chen offset = 0 2537*b6932704SJoseph Chen if footer: 2538*b6932704SJoseph Chen offset = footer.vbmeta_offset 2539*b6932704SJoseph Chen 2540*b6932704SJoseph Chen image.seek(offset) 2541*b6932704SJoseph Chen vbmeta_blob = image.read(header.SIZE 2542*b6932704SJoseph Chen + header.authentication_data_block_size 2543*b6932704SJoseph Chen + header.auxiliary_data_block_size) 2544*b6932704SJoseph Chen 2545*b6932704SJoseph Chen alg_name, _ = lookup_algorithm_by_type(header.algorithm_type) 2546*b6932704SJoseph Chen if not verify_vbmeta_signature(header, vbmeta_blob): 2547*b6932704SJoseph Chen raise AvbError('Signature check failed for {} vbmeta struct {}' 2548*b6932704SJoseph Chen .format(alg_name, image_filename)) 2549*b6932704SJoseph Chen 2550*b6932704SJoseph Chen if key_blob: 2551*b6932704SJoseph Chen # The embedded public key is in the auxiliary block at an offset. 2552*b6932704SJoseph Chen key_offset = AvbVBMetaHeader.SIZE 2553*b6932704SJoseph Chen key_offset += header.authentication_data_block_size 2554*b6932704SJoseph Chen key_offset += header.public_key_offset 2555*b6932704SJoseph Chen key_blob_in_vbmeta = vbmeta_blob[key_offset:key_offset 2556*b6932704SJoseph Chen + header.public_key_size] 2557*b6932704SJoseph Chen if key_blob != key_blob_in_vbmeta: 2558*b6932704SJoseph Chen raise AvbError('Embedded public key does not match given key.') 2559*b6932704SJoseph Chen 2560*b6932704SJoseph Chen if footer: 2561*b6932704SJoseph Chen print('vbmeta: Successfully verified footer and {} vbmeta struct in {}' 2562*b6932704SJoseph Chen .format(alg_name, image.filename)) 2563*b6932704SJoseph Chen else: 2564*b6932704SJoseph Chen print('vbmeta: Successfully verified {} vbmeta struct in {}' 2565*b6932704SJoseph Chen .format(alg_name, image.filename)) 2566*b6932704SJoseph Chen 2567*b6932704SJoseph Chen for desc in descriptors: 2568*b6932704SJoseph Chen if (isinstance(desc, AvbChainPartitionDescriptor) 2569*b6932704SJoseph Chen and follow_chain_partitions 2570*b6932704SJoseph Chen and expected_chain_partitions_map.get(desc.partition_name) is None): 2571*b6932704SJoseph Chen # In this case we're processing a chain descriptor but don't have a 2572*b6932704SJoseph Chen # --expect_chain_partition ... however --follow_chain_partitions was 2573*b6932704SJoseph Chen # specified so we shouldn't error out in desc.verify(). 2574*b6932704SJoseph Chen print('{}: Chained but ROLLBACK_SLOT (which is {}) ' 2575*b6932704SJoseph Chen 'and KEY (which has sha1 {}) not specified' 2576*b6932704SJoseph Chen .format(desc.partition_name, desc.rollback_index_location, 2577*b6932704SJoseph Chen hashlib.sha1(desc.public_key).hexdigest())) 2578*b6932704SJoseph Chen elif not desc.verify(image_dir, image_ext, expected_chain_partitions_map, 2579*b6932704SJoseph Chen image, accept_zeroed_hashtree): 2580*b6932704SJoseph Chen raise AvbError('Error verifying descriptor.') 2581*b6932704SJoseph Chen # Honor --follow_chain_partitions - add '--' to make the output more 2582*b6932704SJoseph Chen # readable. 2583*b6932704SJoseph Chen if (isinstance(desc, AvbChainPartitionDescriptor) 2584*b6932704SJoseph Chen and follow_chain_partitions): 2585*b6932704SJoseph Chen print('--') 2586*b6932704SJoseph Chen chained_image_filename = os.path.join(image_dir, 2587*b6932704SJoseph Chen desc.partition_name + image_ext) 2588*b6932704SJoseph Chen self.verify_image(chained_image_filename, key_path, None, False, 2589*b6932704SJoseph Chen accept_zeroed_hashtree) 2590*b6932704SJoseph Chen 2591*b6932704SJoseph Chen def print_partition_digests(self, image_filename, output, as_json): 2592*b6932704SJoseph Chen """Implements the 'print_partition_digests' command. 2593*b6932704SJoseph Chen 2594*b6932704SJoseph Chen Arguments: 2595*b6932704SJoseph Chen image_filename: Image file to get information from (file object). 2596*b6932704SJoseph Chen output: Output file to write human-readable information to (file object). 2597*b6932704SJoseph Chen as_json: If True, print information as JSON 2598*b6932704SJoseph Chen 2599*b6932704SJoseph Chen Raises: 2600*b6932704SJoseph Chen AvbError: If getting the partition digests from the image fails. 2601*b6932704SJoseph Chen """ 2602*b6932704SJoseph Chen image_dir = os.path.dirname(image_filename) 2603*b6932704SJoseph Chen image_ext = os.path.splitext(image_filename)[1] 2604*b6932704SJoseph Chen json_partitions = None 2605*b6932704SJoseph Chen if as_json: 2606*b6932704SJoseph Chen json_partitions = [] 2607*b6932704SJoseph Chen self._print_partition_digests( 2608*b6932704SJoseph Chen image_filename, output, json_partitions, image_dir, image_ext) 2609*b6932704SJoseph Chen if as_json: 2610*b6932704SJoseph Chen output.write(json.dumps({'partitions': json_partitions}, indent=2)) 2611*b6932704SJoseph Chen 2612*b6932704SJoseph Chen def _print_partition_digests(self, image_filename, output, json_partitions, 2613*b6932704SJoseph Chen image_dir, image_ext): 2614*b6932704SJoseph Chen """Helper for printing partitions. 2615*b6932704SJoseph Chen 2616*b6932704SJoseph Chen Arguments: 2617*b6932704SJoseph Chen image_filename: Image file to get information from (file object). 2618*b6932704SJoseph Chen output: Output file to write human-readable information to (file object). 2619*b6932704SJoseph Chen json_partitions: If not None, don't print to output, instead add partition 2620*b6932704SJoseph Chen information to this list. 2621*b6932704SJoseph Chen image_dir: The directory to use when looking for chained partition files. 2622*b6932704SJoseph Chen image_ext: The extension to use for chained partition files. 2623*b6932704SJoseph Chen 2624*b6932704SJoseph Chen Raises: 2625*b6932704SJoseph Chen AvbError: If getting the partition digests from the image fails. 2626*b6932704SJoseph Chen """ 2627*b6932704SJoseph Chen image = ImageHandler(image_filename, read_only=True) 2628*b6932704SJoseph Chen (_, _, descriptors, _) = self._parse_image(image) 2629*b6932704SJoseph Chen 2630*b6932704SJoseph Chen for desc in descriptors: 2631*b6932704SJoseph Chen if isinstance(desc, AvbHashDescriptor): 2632*b6932704SJoseph Chen digest = desc.digest.hex() 2633*b6932704SJoseph Chen if json_partitions is not None: 2634*b6932704SJoseph Chen json_partitions.append({'name': desc.partition_name, 2635*b6932704SJoseph Chen 'digest': digest}) 2636*b6932704SJoseph Chen else: 2637*b6932704SJoseph Chen output.write('{}: {}\n'.format(desc.partition_name, digest)) 2638*b6932704SJoseph Chen elif isinstance(desc, AvbHashtreeDescriptor): 2639*b6932704SJoseph Chen digest = desc.root_digest.hex() 2640*b6932704SJoseph Chen if json_partitions is not None: 2641*b6932704SJoseph Chen json_partitions.append({'name': desc.partition_name, 2642*b6932704SJoseph Chen 'digest': digest}) 2643*b6932704SJoseph Chen else: 2644*b6932704SJoseph Chen output.write('{}: {}\n'.format(desc.partition_name, digest)) 2645*b6932704SJoseph Chen elif isinstance(desc, AvbChainPartitionDescriptor): 2646*b6932704SJoseph Chen chained_image_filename = os.path.join(image_dir, 2647*b6932704SJoseph Chen desc.partition_name + image_ext) 2648*b6932704SJoseph Chen self._print_partition_digests( 2649*b6932704SJoseph Chen chained_image_filename, output, json_partitions, image_dir, 2650*b6932704SJoseph Chen image_ext) 2651*b6932704SJoseph Chen 2652*b6932704SJoseph Chen def calculate_vbmeta_digest(self, image_filename, hash_algorithm, output): 2653*b6932704SJoseph Chen """Implements the 'calculate_vbmeta_digest' command. 2654*b6932704SJoseph Chen 2655*b6932704SJoseph Chen Arguments: 2656*b6932704SJoseph Chen image_filename: Image file to get information from (file object). 2657*b6932704SJoseph Chen hash_algorithm: Hash algorithm used. 2658*b6932704SJoseph Chen output: Output file to write human-readable information to (file object). 2659*b6932704SJoseph Chen """ 2660*b6932704SJoseph Chen 2661*b6932704SJoseph Chen image_dir = os.path.dirname(image_filename) 2662*b6932704SJoseph Chen image_ext = os.path.splitext(image_filename)[1] 2663*b6932704SJoseph Chen 2664*b6932704SJoseph Chen image = ImageHandler(image_filename, read_only=True) 2665*b6932704SJoseph Chen (footer, header, descriptors, _) = self._parse_image(image) 2666*b6932704SJoseph Chen offset = 0 2667*b6932704SJoseph Chen if footer: 2668*b6932704SJoseph Chen offset = footer.vbmeta_offset 2669*b6932704SJoseph Chen size = (header.SIZE + header.authentication_data_block_size + 2670*b6932704SJoseph Chen header.auxiliary_data_block_size) 2671*b6932704SJoseph Chen image.seek(offset) 2672*b6932704SJoseph Chen vbmeta_blob = image.read(size) 2673*b6932704SJoseph Chen 2674*b6932704SJoseph Chen hasher = hashlib.new(hash_algorithm) 2675*b6932704SJoseph Chen hasher.update(vbmeta_blob) 2676*b6932704SJoseph Chen 2677*b6932704SJoseph Chen for desc in descriptors: 2678*b6932704SJoseph Chen if isinstance(desc, AvbChainPartitionDescriptor): 2679*b6932704SJoseph Chen ch_image_filename = os.path.join(image_dir, 2680*b6932704SJoseph Chen desc.partition_name + image_ext) 2681*b6932704SJoseph Chen ch_image = ImageHandler(ch_image_filename, read_only=True) 2682*b6932704SJoseph Chen (ch_footer, ch_header, _, _) = self._parse_image(ch_image) 2683*b6932704SJoseph Chen ch_offset = 0 2684*b6932704SJoseph Chen ch_size = (ch_header.SIZE + ch_header.authentication_data_block_size + 2685*b6932704SJoseph Chen ch_header.auxiliary_data_block_size) 2686*b6932704SJoseph Chen if ch_footer: 2687*b6932704SJoseph Chen ch_offset = ch_footer.vbmeta_offset 2688*b6932704SJoseph Chen ch_image.seek(ch_offset) 2689*b6932704SJoseph Chen ch_vbmeta_blob = ch_image.read(ch_size) 2690*b6932704SJoseph Chen hasher.update(ch_vbmeta_blob) 2691*b6932704SJoseph Chen 2692*b6932704SJoseph Chen digest = hasher.digest() 2693*b6932704SJoseph Chen output.write('{}\n'.format(digest.hex())) 2694*b6932704SJoseph Chen 2695*b6932704SJoseph Chen def calculate_kernel_cmdline(self, image_filename, hashtree_disabled, output): 2696*b6932704SJoseph Chen """Implements the 'calculate_kernel_cmdline' command. 2697*b6932704SJoseph Chen 2698*b6932704SJoseph Chen Arguments: 2699*b6932704SJoseph Chen image_filename: Image file to get information from (file object). 2700*b6932704SJoseph Chen hashtree_disabled: If True, returns the cmdline for hashtree disabled. 2701*b6932704SJoseph Chen output: Output file to write human-readable information to (file object). 2702*b6932704SJoseph Chen """ 2703*b6932704SJoseph Chen 2704*b6932704SJoseph Chen image = ImageHandler(image_filename, read_only=True) 2705*b6932704SJoseph Chen _, _, descriptors, _ = self._parse_image(image) 2706*b6932704SJoseph Chen 2707*b6932704SJoseph Chen image_dir = os.path.dirname(image_filename) 2708*b6932704SJoseph Chen image_ext = os.path.splitext(image_filename)[1] 2709*b6932704SJoseph Chen 2710*b6932704SJoseph Chen cmdline_descriptors = [] 2711*b6932704SJoseph Chen for desc in descriptors: 2712*b6932704SJoseph Chen if isinstance(desc, AvbChainPartitionDescriptor): 2713*b6932704SJoseph Chen ch_image_filename = os.path.join(image_dir, 2714*b6932704SJoseph Chen desc.partition_name + image_ext) 2715*b6932704SJoseph Chen ch_image = ImageHandler(ch_image_filename, read_only=True) 2716*b6932704SJoseph Chen _, _, ch_descriptors, _ = self._parse_image(ch_image) 2717*b6932704SJoseph Chen for ch_desc in ch_descriptors: 2718*b6932704SJoseph Chen if isinstance(ch_desc, AvbKernelCmdlineDescriptor): 2719*b6932704SJoseph Chen cmdline_descriptors.append(ch_desc) 2720*b6932704SJoseph Chen elif isinstance(desc, AvbKernelCmdlineDescriptor): 2721*b6932704SJoseph Chen cmdline_descriptors.append(desc) 2722*b6932704SJoseph Chen 2723*b6932704SJoseph Chen kernel_cmdline_snippets = [] 2724*b6932704SJoseph Chen for desc in cmdline_descriptors: 2725*b6932704SJoseph Chen use_cmdline = True 2726*b6932704SJoseph Chen if ((desc.flags & 2727*b6932704SJoseph Chen AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED) 2728*b6932704SJoseph Chen != 0): 2729*b6932704SJoseph Chen if hashtree_disabled: 2730*b6932704SJoseph Chen use_cmdline = False 2731*b6932704SJoseph Chen if (desc.flags & 2732*b6932704SJoseph Chen AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED) != 0: 2733*b6932704SJoseph Chen if not hashtree_disabled: 2734*b6932704SJoseph Chen use_cmdline = False 2735*b6932704SJoseph Chen if use_cmdline: 2736*b6932704SJoseph Chen kernel_cmdline_snippets.append(desc.kernel_cmdline) 2737*b6932704SJoseph Chen output.write(' '.join(kernel_cmdline_snippets)) 2738*b6932704SJoseph Chen 2739*b6932704SJoseph Chen def _parse_image(self, image): 2740*b6932704SJoseph Chen """Gets information about an image. 2741*b6932704SJoseph Chen 2742*b6932704SJoseph Chen The image can either be a vbmeta or an image with a footer. 2743*b6932704SJoseph Chen 2744*b6932704SJoseph Chen Arguments: 2745*b6932704SJoseph Chen image: An ImageHandler (vbmeta or footer) with a hashtree descriptor. 2746*b6932704SJoseph Chen 2747*b6932704SJoseph Chen Returns: 2748*b6932704SJoseph Chen A tuple where the first argument is a AvbFooter (None if there 2749*b6932704SJoseph Chen is no footer on the image), the second argument is a 2750*b6932704SJoseph Chen AvbVBMetaHeader, the third argument is a list of 2751*b6932704SJoseph Chen AvbDescriptor-derived instances, and the fourth argument is the 2752*b6932704SJoseph Chen size of |image|. 2753*b6932704SJoseph Chen 2754*b6932704SJoseph Chen Raises: 2755*b6932704SJoseph Chen AvbError: In case the image cannot be parsed. 2756*b6932704SJoseph Chen """ 2757*b6932704SJoseph Chen assert isinstance(image, ImageHandler) 2758*b6932704SJoseph Chen footer = None 2759*b6932704SJoseph Chen image.seek(image.image_size - AvbFooter.SIZE) 2760*b6932704SJoseph Chen try: 2761*b6932704SJoseph Chen footer = AvbFooter(image.read(AvbFooter.SIZE)) 2762*b6932704SJoseph Chen except (LookupError, struct.error): 2763*b6932704SJoseph Chen # Nope, just seek back to the start. 2764*b6932704SJoseph Chen image.seek(0) 2765*b6932704SJoseph Chen 2766*b6932704SJoseph Chen vbmeta_offset = 0 2767*b6932704SJoseph Chen if footer: 2768*b6932704SJoseph Chen vbmeta_offset = footer.vbmeta_offset 2769*b6932704SJoseph Chen 2770*b6932704SJoseph Chen image.seek(vbmeta_offset) 2771*b6932704SJoseph Chen h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE)) 2772*b6932704SJoseph Chen 2773*b6932704SJoseph Chen auth_block_offset = vbmeta_offset + AvbVBMetaHeader.SIZE 2774*b6932704SJoseph Chen aux_block_offset = auth_block_offset + h.authentication_data_block_size 2775*b6932704SJoseph Chen desc_start_offset = aux_block_offset + h.descriptors_offset 2776*b6932704SJoseph Chen image.seek(desc_start_offset) 2777*b6932704SJoseph Chen descriptors = parse_descriptors(image.read(h.descriptors_size)) 2778*b6932704SJoseph Chen 2779*b6932704SJoseph Chen return footer, h, descriptors, image.image_size 2780*b6932704SJoseph Chen 2781*b6932704SJoseph Chen def _load_vbmeta_blob(self, image): 2782*b6932704SJoseph Chen """Gets the vbmeta struct and associated sections. 2783*b6932704SJoseph Chen 2784*b6932704SJoseph Chen The image can either be a vbmeta.img or an image with a footer. 2785*b6932704SJoseph Chen 2786*b6932704SJoseph Chen Arguments: 2787*b6932704SJoseph Chen image: An ImageHandler (vbmeta or footer). 2788*b6932704SJoseph Chen 2789*b6932704SJoseph Chen Returns: 2790*b6932704SJoseph Chen A blob with the vbmeta struct and other sections. 2791*b6932704SJoseph Chen """ 2792*b6932704SJoseph Chen assert isinstance(image, ImageHandler) 2793*b6932704SJoseph Chen footer = None 2794*b6932704SJoseph Chen image.seek(image.image_size - AvbFooter.SIZE) 2795*b6932704SJoseph Chen try: 2796*b6932704SJoseph Chen footer = AvbFooter(image.read(AvbFooter.SIZE)) 2797*b6932704SJoseph Chen except (LookupError, struct.error): 2798*b6932704SJoseph Chen # Nope, just seek back to the start. 2799*b6932704SJoseph Chen image.seek(0) 2800*b6932704SJoseph Chen 2801*b6932704SJoseph Chen vbmeta_offset = 0 2802*b6932704SJoseph Chen if footer: 2803*b6932704SJoseph Chen vbmeta_offset = footer.vbmeta_offset 2804*b6932704SJoseph Chen 2805*b6932704SJoseph Chen image.seek(vbmeta_offset) 2806*b6932704SJoseph Chen h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE)) 2807*b6932704SJoseph Chen 2808*b6932704SJoseph Chen image.seek(vbmeta_offset) 2809*b6932704SJoseph Chen data_size = AvbVBMetaHeader.SIZE 2810*b6932704SJoseph Chen data_size += h.authentication_data_block_size 2811*b6932704SJoseph Chen data_size += h.auxiliary_data_block_size 2812*b6932704SJoseph Chen return image.read(data_size) 2813*b6932704SJoseph Chen 2814*b6932704SJoseph Chen def _get_cmdline_descriptors_for_hashtree_descriptor(self, ht): 2815*b6932704SJoseph Chen """Generate kernel cmdline descriptors for dm-verity. 2816*b6932704SJoseph Chen 2817*b6932704SJoseph Chen Arguments: 2818*b6932704SJoseph Chen ht: A AvbHashtreeDescriptor 2819*b6932704SJoseph Chen 2820*b6932704SJoseph Chen Returns: 2821*b6932704SJoseph Chen A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline 2822*b6932704SJoseph Chen instructions. There is one for when hashtree is not disabled and one for 2823*b6932704SJoseph Chen when it is. 2824*b6932704SJoseph Chen 2825*b6932704SJoseph Chen """ 2826*b6932704SJoseph Chen c = 'dm="1 vroot none ro 1,' 2827*b6932704SJoseph Chen c += '0' # start 2828*b6932704SJoseph Chen c += ' {}'.format((ht.image_size // 512)) # size (# sectors) 2829*b6932704SJoseph Chen c += ' verity {}'.format(ht.dm_verity_version) # type and version 2830*b6932704SJoseph Chen c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # data_dev 2831*b6932704SJoseph Chen c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # hash_dev 2832*b6932704SJoseph Chen c += ' {}'.format(ht.data_block_size) # data_block 2833*b6932704SJoseph Chen c += ' {}'.format(ht.hash_block_size) # hash_block 2834*b6932704SJoseph Chen c += ' {}'.format(ht.image_size // ht.data_block_size) # #blocks 2835*b6932704SJoseph Chen c += ' {}'.format(ht.image_size // ht.data_block_size) # hash_offset 2836*b6932704SJoseph Chen c += ' {}'.format(ht.hash_algorithm) # hash_alg 2837*b6932704SJoseph Chen c += ' {}'.format(ht.root_digest.hex()) # root_digest 2838*b6932704SJoseph Chen c += ' {}'.format(ht.salt.hex()) # salt 2839*b6932704SJoseph Chen if ht.fec_num_roots > 0: 2840*b6932704SJoseph Chen if ht.flags & AvbHashtreeDescriptor.FLAGS_CHECK_AT_MOST_ONCE: 2841*b6932704SJoseph Chen c += ' 11' # number of optional args 2842*b6932704SJoseph Chen c += ' check_at_most_once' 2843*b6932704SJoseph Chen else: 2844*b6932704SJoseph Chen c += ' 10' # number of optional args 2845*b6932704SJoseph Chen c += ' $(ANDROID_VERITY_MODE)' 2846*b6932704SJoseph Chen c += ' ignore_zero_blocks' 2847*b6932704SJoseph Chen c += ' use_fec_from_device PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' 2848*b6932704SJoseph Chen c += ' fec_roots {}'.format(ht.fec_num_roots) 2849*b6932704SJoseph Chen # Note that fec_blocks is the size that FEC covers, *not* the 2850*b6932704SJoseph Chen # size of the FEC data. Since we use FEC for everything up until 2851*b6932704SJoseph Chen # the FEC data, it's the same as the offset. 2852*b6932704SJoseph Chen c += ' fec_blocks {}'.format(ht.fec_offset // ht.data_block_size) 2853*b6932704SJoseph Chen c += ' fec_start {}'.format(ht.fec_offset // ht.data_block_size) 2854*b6932704SJoseph Chen else: 2855*b6932704SJoseph Chen if ht.flags & AvbHashtreeDescriptor.FLAGS_CHECK_AT_MOST_ONCE: 2856*b6932704SJoseph Chen c += ' 3' # number of optional args 2857*b6932704SJoseph Chen c += ' check_at_most_once' 2858*b6932704SJoseph Chen else: 2859*b6932704SJoseph Chen c += ' 2' # number of optional args 2860*b6932704SJoseph Chen c += ' $(ANDROID_VERITY_MODE)' 2861*b6932704SJoseph Chen c += ' ignore_zero_blocks' 2862*b6932704SJoseph Chen c += '" root=/dev/dm-0' 2863*b6932704SJoseph Chen 2864*b6932704SJoseph Chen # Now that we have the command-line, generate the descriptor. 2865*b6932704SJoseph Chen desc = AvbKernelCmdlineDescriptor() 2866*b6932704SJoseph Chen desc.kernel_cmdline = c 2867*b6932704SJoseph Chen desc.flags = ( 2868*b6932704SJoseph Chen AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED) 2869*b6932704SJoseph Chen 2870*b6932704SJoseph Chen # The descriptor for when hashtree verification is disabled is a lot 2871*b6932704SJoseph Chen # simpler - we just set the root to the partition. 2872*b6932704SJoseph Chen desc_no_ht = AvbKernelCmdlineDescriptor() 2873*b6932704SJoseph Chen desc_no_ht.kernel_cmdline = 'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' 2874*b6932704SJoseph Chen desc_no_ht.flags = ( 2875*b6932704SJoseph Chen AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED) 2876*b6932704SJoseph Chen 2877*b6932704SJoseph Chen return [desc, desc_no_ht] 2878*b6932704SJoseph Chen 2879*b6932704SJoseph Chen def _get_cmdline_descriptors_for_dm_verity(self, image): 2880*b6932704SJoseph Chen """Generate kernel cmdline descriptors for dm-verity. 2881*b6932704SJoseph Chen 2882*b6932704SJoseph Chen Arguments: 2883*b6932704SJoseph Chen image: An ImageHandler (vbmeta or footer) with a hashtree descriptor. 2884*b6932704SJoseph Chen 2885*b6932704SJoseph Chen Returns: 2886*b6932704SJoseph Chen A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline 2887*b6932704SJoseph Chen instructions. There is one for when hashtree is not disabled and one for 2888*b6932704SJoseph Chen when it is. 2889*b6932704SJoseph Chen 2890*b6932704SJoseph Chen Raises: 2891*b6932704SJoseph Chen AvbError: If |image| doesn't have a hashtree descriptor. 2892*b6932704SJoseph Chen 2893*b6932704SJoseph Chen """ 2894*b6932704SJoseph Chen (_, _, descriptors, _) = self._parse_image(image) 2895*b6932704SJoseph Chen 2896*b6932704SJoseph Chen ht = None 2897*b6932704SJoseph Chen for desc in descriptors: 2898*b6932704SJoseph Chen if isinstance(desc, AvbHashtreeDescriptor): 2899*b6932704SJoseph Chen ht = desc 2900*b6932704SJoseph Chen break 2901*b6932704SJoseph Chen 2902*b6932704SJoseph Chen if not ht: 2903*b6932704SJoseph Chen raise AvbError('No hashtree descriptor in given image') 2904*b6932704SJoseph Chen 2905*b6932704SJoseph Chen return self._get_cmdline_descriptors_for_hashtree_descriptor(ht) 2906*b6932704SJoseph Chen 2907*b6932704SJoseph Chen def make_vbmeta_image(self, output, chain_partitions, algorithm_name, 2908*b6932704SJoseph Chen key_path, public_key_metadata_path, rollback_index, 2909*b6932704SJoseph Chen flags, rollback_index_location, 2910*b6932704SJoseph Chen props, props_from_file, kernel_cmdlines, 2911*b6932704SJoseph Chen setup_rootfs_from_kernel, 2912*b6932704SJoseph Chen include_descriptors_from_image, 2913*b6932704SJoseph Chen signing_helper, 2914*b6932704SJoseph Chen signing_helper_with_files, 2915*b6932704SJoseph Chen release_string, 2916*b6932704SJoseph Chen append_to_release_string, 2917*b6932704SJoseph Chen print_required_libavb_version, 2918*b6932704SJoseph Chen padding_size): 2919*b6932704SJoseph Chen """Implements the 'make_vbmeta_image' command. 2920*b6932704SJoseph Chen 2921*b6932704SJoseph Chen Arguments: 2922*b6932704SJoseph Chen output: File to write the image to. 2923*b6932704SJoseph Chen chain_partitions: List of partitions to chain or None. 2924*b6932704SJoseph Chen algorithm_name: Name of algorithm to use. 2925*b6932704SJoseph Chen key_path: Path to key to use or None. 2926*b6932704SJoseph Chen public_key_metadata_path: Path to public key metadata or None. 2927*b6932704SJoseph Chen rollback_index: The rollback index to use. 2928*b6932704SJoseph Chen flags: Flags value to use in the image. 2929*b6932704SJoseph Chen rollback_index_location: Location of the main vbmeta rollback index. 2930*b6932704SJoseph Chen props: Properties to insert (list of strings of the form 'key:value'). 2931*b6932704SJoseph Chen props_from_file: Properties to insert (list of strings 'key:<path>'). 2932*b6932704SJoseph Chen kernel_cmdlines: Kernel cmdlines to insert (list of strings). 2933*b6932704SJoseph Chen setup_rootfs_from_kernel: None or file to generate from. 2934*b6932704SJoseph Chen include_descriptors_from_image: List of file objects with descriptors. 2935*b6932704SJoseph Chen signing_helper: Program which signs a hash and return signature. 2936*b6932704SJoseph Chen signing_helper_with_files: Same as signing_helper but uses files instead. 2937*b6932704SJoseph Chen release_string: None or avbtool release string to use instead of default. 2938*b6932704SJoseph Chen append_to_release_string: None or string to append. 2939*b6932704SJoseph Chen print_required_libavb_version: True to only print required libavb version. 2940*b6932704SJoseph Chen padding_size: If not 0, pads output so size is a multiple of the number. 2941*b6932704SJoseph Chen 2942*b6932704SJoseph Chen Raises: 2943*b6932704SJoseph Chen AvbError: If a chained partition is malformed. 2944*b6932704SJoseph Chen """ 2945*b6932704SJoseph Chen # If we're asked to calculate minimum required libavb version, we're done. 2946*b6932704SJoseph Chen tmp_header = AvbVBMetaHeader() 2947*b6932704SJoseph Chen if rollback_index_location > 0: 2948*b6932704SJoseph Chen tmp_header.bump_required_libavb_version_minor(2) 2949*b6932704SJoseph Chen if include_descriptors_from_image: 2950*b6932704SJoseph Chen # Use the bump logic in AvbVBMetaHeader to calculate the max required 2951*b6932704SJoseph Chen # version of all included descriptors. 2952*b6932704SJoseph Chen for image in include_descriptors_from_image: 2953*b6932704SJoseph Chen (_, image_header, _, _) = self._parse_image(ImageHandler( 2954*b6932704SJoseph Chen image.name, read_only=True)) 2955*b6932704SJoseph Chen tmp_header.bump_required_libavb_version_minor( 2956*b6932704SJoseph Chen image_header.required_libavb_version_minor) 2957*b6932704SJoseph Chen 2958*b6932704SJoseph Chen if print_required_libavb_version: 2959*b6932704SJoseph Chen print('1.{}'.format(tmp_header.required_libavb_version_minor)) 2960*b6932704SJoseph Chen return 2961*b6932704SJoseph Chen 2962*b6932704SJoseph Chen if not output: 2963*b6932704SJoseph Chen raise AvbError('No output file given') 2964*b6932704SJoseph Chen 2965*b6932704SJoseph Chen descriptors = [] 2966*b6932704SJoseph Chen ht_desc_to_setup = None 2967*b6932704SJoseph Chen vbmeta_blob = self._generate_vbmeta_blob( 2968*b6932704SJoseph Chen algorithm_name, key_path, public_key_metadata_path, descriptors, 2969*b6932704SJoseph Chen chain_partitions, rollback_index, flags, rollback_index_location, 2970*b6932704SJoseph Chen props, props_from_file, 2971*b6932704SJoseph Chen kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup, 2972*b6932704SJoseph Chen include_descriptors_from_image, signing_helper, 2973*b6932704SJoseph Chen signing_helper_with_files, release_string, 2974*b6932704SJoseph Chen append_to_release_string, tmp_header.required_libavb_version_minor) 2975*b6932704SJoseph Chen 2976*b6932704SJoseph Chen # Write entire vbmeta blob (header, authentication, auxiliary). 2977*b6932704SJoseph Chen output.seek(0) 2978*b6932704SJoseph Chen output.write(vbmeta_blob) 2979*b6932704SJoseph Chen 2980*b6932704SJoseph Chen if padding_size > 0: 2981*b6932704SJoseph Chen padded_size = round_to_multiple(len(vbmeta_blob), padding_size) 2982*b6932704SJoseph Chen padding_needed = padded_size - len(vbmeta_blob) 2983*b6932704SJoseph Chen output.write(b'\0' * padding_needed) 2984*b6932704SJoseph Chen 2985*b6932704SJoseph Chen def _generate_vbmeta_blob(self, algorithm_name, key_path, 2986*b6932704SJoseph Chen public_key_metadata_path, descriptors, 2987*b6932704SJoseph Chen chain_partitions, 2988*b6932704SJoseph Chen rollback_index, flags, rollback_index_location, 2989*b6932704SJoseph Chen props, props_from_file, 2990*b6932704SJoseph Chen kernel_cmdlines, 2991*b6932704SJoseph Chen setup_rootfs_from_kernel, 2992*b6932704SJoseph Chen ht_desc_to_setup, 2993*b6932704SJoseph Chen include_descriptors_from_image, signing_helper, 2994*b6932704SJoseph Chen signing_helper_with_files, 2995*b6932704SJoseph Chen release_string, append_to_release_string, 2996*b6932704SJoseph Chen required_libavb_version_minor): 2997*b6932704SJoseph Chen """Generates a VBMeta blob. 2998*b6932704SJoseph Chen 2999*b6932704SJoseph Chen This blob contains the header (struct AvbVBMetaHeader), the 3000*b6932704SJoseph Chen authentication data block (which contains the hash and signature 3001*b6932704SJoseph Chen for the header and auxiliary block), and the auxiliary block 3002*b6932704SJoseph Chen (which contains descriptors, the public key used, and other data). 3003*b6932704SJoseph Chen 3004*b6932704SJoseph Chen The |key| parameter can |None| only if the |algorithm_name| is 3005*b6932704SJoseph Chen 'NONE'. 3006*b6932704SJoseph Chen 3007*b6932704SJoseph Chen Arguments: 3008*b6932704SJoseph Chen algorithm_name: The algorithm name as per the ALGORITHMS dict. 3009*b6932704SJoseph Chen key_path: The path to the .pem file used to sign the blob. 3010*b6932704SJoseph Chen public_key_metadata_path: Path to public key metadata or None. 3011*b6932704SJoseph Chen descriptors: A list of descriptors to insert or None. 3012*b6932704SJoseph Chen chain_partitions: List of partitions to chain or None. 3013*b6932704SJoseph Chen rollback_index: The rollback index to use. 3014*b6932704SJoseph Chen flags: Flags to use in the image. 3015*b6932704SJoseph Chen rollback_index_location: Location of the main vbmeta rollback index. 3016*b6932704SJoseph Chen props: Properties to insert (List of strings of the form 'key:value'). 3017*b6932704SJoseph Chen props_from_file: Properties to insert (List of strings 'key:<path>'). 3018*b6932704SJoseph Chen kernel_cmdlines: Kernel cmdlines to insert (list of strings). 3019*b6932704SJoseph Chen setup_rootfs_from_kernel: None or file to generate 3020*b6932704SJoseph Chen dm-verity kernel cmdline from. 3021*b6932704SJoseph Chen ht_desc_to_setup: If not None, an AvbHashtreeDescriptor to 3022*b6932704SJoseph Chen generate dm-verity kernel cmdline descriptors from. 3023*b6932704SJoseph Chen include_descriptors_from_image: List of file objects for which 3024*b6932704SJoseph Chen to insert descriptors from. 3025*b6932704SJoseph Chen signing_helper: Program which signs a hash and return signature. 3026*b6932704SJoseph Chen signing_helper_with_files: Same as signing_helper but uses files instead. 3027*b6932704SJoseph Chen release_string: None or avbtool release string. 3028*b6932704SJoseph Chen append_to_release_string: None or string to append. 3029*b6932704SJoseph Chen required_libavb_version_minor: Use at least this required minor version. 3030*b6932704SJoseph Chen 3031*b6932704SJoseph Chen Returns: 3032*b6932704SJoseph Chen The VBMeta blob as bytes. 3033*b6932704SJoseph Chen 3034*b6932704SJoseph Chen Raises: 3035*b6932704SJoseph Chen Exception: If the |algorithm_name| is not found, if no key has 3036*b6932704SJoseph Chen been given and the given algorithm requires one, or the key is 3037*b6932704SJoseph Chen of the wrong size. 3038*b6932704SJoseph Chen """ 3039*b6932704SJoseph Chen try: 3040*b6932704SJoseph Chen alg = ALGORITHMS[algorithm_name] 3041*b6932704SJoseph Chen except KeyError as e: 3042*b6932704SJoseph Chen raise AvbError('Unknown algorithm with name {}' 3043*b6932704SJoseph Chen .format(algorithm_name)) from e 3044*b6932704SJoseph Chen 3045*b6932704SJoseph Chen if not descriptors: 3046*b6932704SJoseph Chen descriptors = [] 3047*b6932704SJoseph Chen 3048*b6932704SJoseph Chen h = AvbVBMetaHeader() 3049*b6932704SJoseph Chen h.bump_required_libavb_version_minor(required_libavb_version_minor) 3050*b6932704SJoseph Chen 3051*b6932704SJoseph Chen # Insert chained partition descriptors, if any 3052*b6932704SJoseph Chen if chain_partitions: 3053*b6932704SJoseph Chen used_locations = {rollback_index_location: True} 3054*b6932704SJoseph Chen for cp in chain_partitions: 3055*b6932704SJoseph Chen cp_tokens = cp.split(':') 3056*b6932704SJoseph Chen if len(cp_tokens) != 3: 3057*b6932704SJoseph Chen raise AvbError('Malformed chained partition "{}".'.format(cp)) 3058*b6932704SJoseph Chen partition_name = cp_tokens[0] 3059*b6932704SJoseph Chen chained_rollback_index_location = int(cp_tokens[1]) 3060*b6932704SJoseph Chen file_path = cp_tokens[2] 3061*b6932704SJoseph Chen # Check that the same rollback location isn't being used by 3062*b6932704SJoseph Chen # multiple chained partitions. 3063*b6932704SJoseph Chen if used_locations.get(chained_rollback_index_location): 3064*b6932704SJoseph Chen raise AvbError('Rollback Index Location {} is already in use.'.format( 3065*b6932704SJoseph Chen chained_rollback_index_location)) 3066*b6932704SJoseph Chen used_locations[chained_rollback_index_location] = True 3067*b6932704SJoseph Chen desc = AvbChainPartitionDescriptor() 3068*b6932704SJoseph Chen desc.partition_name = partition_name 3069*b6932704SJoseph Chen desc.rollback_index_location = chained_rollback_index_location 3070*b6932704SJoseph Chen if desc.rollback_index_location < 1: 3071*b6932704SJoseph Chen raise AvbError('Rollback index location must be 1 or larger.') 3072*b6932704SJoseph Chen with open(file_path, 'rb') as f: 3073*b6932704SJoseph Chen desc.public_key = f.read() 3074*b6932704SJoseph Chen descriptors.append(desc) 3075*b6932704SJoseph Chen 3076*b6932704SJoseph Chen # Descriptors. 3077*b6932704SJoseph Chen encoded_descriptors = bytearray() 3078*b6932704SJoseph Chen for desc in descriptors: 3079*b6932704SJoseph Chen encoded_descriptors.extend(desc.encode()) 3080*b6932704SJoseph Chen 3081*b6932704SJoseph Chen # Add properties. 3082*b6932704SJoseph Chen if props: 3083*b6932704SJoseph Chen for prop in props: 3084*b6932704SJoseph Chen idx = prop.find(':') 3085*b6932704SJoseph Chen if idx == -1: 3086*b6932704SJoseph Chen raise AvbError('Malformed property "{}".'.format(prop)) 3087*b6932704SJoseph Chen # pylint: disable=redefined-variable-type 3088*b6932704SJoseph Chen desc = AvbPropertyDescriptor() 3089*b6932704SJoseph Chen desc.key = prop[0:idx] 3090*b6932704SJoseph Chen desc.value = prop[(idx + 1):].encode('utf-8') 3091*b6932704SJoseph Chen encoded_descriptors.extend(desc.encode()) 3092*b6932704SJoseph Chen if props_from_file: 3093*b6932704SJoseph Chen for prop in props_from_file: 3094*b6932704SJoseph Chen idx = prop.find(':') 3095*b6932704SJoseph Chen if idx == -1: 3096*b6932704SJoseph Chen raise AvbError('Malformed property "{}".'.format(prop)) 3097*b6932704SJoseph Chen desc = AvbPropertyDescriptor() 3098*b6932704SJoseph Chen desc.key = prop[0:idx] 3099*b6932704SJoseph Chen file_path = prop[(idx + 1):] 3100*b6932704SJoseph Chen with open(file_path, 'rb') as f: 3101*b6932704SJoseph Chen # pylint: disable=attribute-defined-outside-init 3102*b6932704SJoseph Chen desc.value = f.read() 3103*b6932704SJoseph Chen encoded_descriptors.extend(desc.encode()) 3104*b6932704SJoseph Chen 3105*b6932704SJoseph Chen # Add AvbKernelCmdline descriptor for dm-verity from an image, if requested. 3106*b6932704SJoseph Chen if setup_rootfs_from_kernel: 3107*b6932704SJoseph Chen image_handler = ImageHandler( 3108*b6932704SJoseph Chen setup_rootfs_from_kernel.name) 3109*b6932704SJoseph Chen cmdline_desc = self._get_cmdline_descriptors_for_dm_verity(image_handler) 3110*b6932704SJoseph Chen encoded_descriptors.extend(cmdline_desc[0].encode()) 3111*b6932704SJoseph Chen encoded_descriptors.extend(cmdline_desc[1].encode()) 3112*b6932704SJoseph Chen 3113*b6932704SJoseph Chen # Add AvbKernelCmdline descriptor for dm-verity from desc, if requested. 3114*b6932704SJoseph Chen if ht_desc_to_setup: 3115*b6932704SJoseph Chen cmdline_desc = self._get_cmdline_descriptors_for_hashtree_descriptor( 3116*b6932704SJoseph Chen ht_desc_to_setup) 3117*b6932704SJoseph Chen encoded_descriptors.extend(cmdline_desc[0].encode()) 3118*b6932704SJoseph Chen encoded_descriptors.extend(cmdline_desc[1].encode()) 3119*b6932704SJoseph Chen 3120*b6932704SJoseph Chen # Add kernel command-lines. 3121*b6932704SJoseph Chen if kernel_cmdlines: 3122*b6932704SJoseph Chen for i in kernel_cmdlines: 3123*b6932704SJoseph Chen desc = AvbKernelCmdlineDescriptor() 3124*b6932704SJoseph Chen desc.kernel_cmdline = i 3125*b6932704SJoseph Chen encoded_descriptors.extend(desc.encode()) 3126*b6932704SJoseph Chen 3127*b6932704SJoseph Chen # Add descriptors from other images. 3128*b6932704SJoseph Chen if include_descriptors_from_image: 3129*b6932704SJoseph Chen descriptors_dict = dict() 3130*b6932704SJoseph Chen for image in include_descriptors_from_image: 3131*b6932704SJoseph Chen image_handler = ImageHandler(image.name, read_only=True) 3132*b6932704SJoseph Chen (_, image_vbmeta_header, image_descriptors, _) = self._parse_image( 3133*b6932704SJoseph Chen image_handler) 3134*b6932704SJoseph Chen # Bump the required libavb version to support all included descriptors. 3135*b6932704SJoseph Chen h.bump_required_libavb_version_minor( 3136*b6932704SJoseph Chen image_vbmeta_header.required_libavb_version_minor) 3137*b6932704SJoseph Chen for desc in image_descriptors: 3138*b6932704SJoseph Chen # The --include_descriptors_from_image option is used in some setups 3139*b6932704SJoseph Chen # with images A and B where both A and B contain a descriptor 3140*b6932704SJoseph Chen # for a partition with the same name. Since it's not meaningful 3141*b6932704SJoseph Chen # to include both descriptors, only include the last seen descriptor. 3142*b6932704SJoseph Chen # See bug 76386656 for details. 3143*b6932704SJoseph Chen if hasattr(desc, 'partition_name'): 3144*b6932704SJoseph Chen key = type(desc).__name__ + '_' + desc.partition_name 3145*b6932704SJoseph Chen descriptors_dict[key] = desc.encode() 3146*b6932704SJoseph Chen else: 3147*b6932704SJoseph Chen encoded_descriptors.extend(desc.encode()) 3148*b6932704SJoseph Chen for key in sorted(descriptors_dict): 3149*b6932704SJoseph Chen encoded_descriptors.extend(descriptors_dict[key]) 3150*b6932704SJoseph Chen 3151*b6932704SJoseph Chen # Load public key metadata blob, if requested. 3152*b6932704SJoseph Chen pkmd_blob = b'' 3153*b6932704SJoseph Chen if public_key_metadata_path: 3154*b6932704SJoseph Chen with open(public_key_metadata_path, 'rb') as f: 3155*b6932704SJoseph Chen pkmd_blob = f.read() 3156*b6932704SJoseph Chen 3157*b6932704SJoseph Chen key = None 3158*b6932704SJoseph Chen encoded_key = b'' 3159*b6932704SJoseph Chen if alg.public_key_num_bytes > 0: 3160*b6932704SJoseph Chen if not key_path: 3161*b6932704SJoseph Chen raise AvbError('Key is required for algorithm {}'.format( 3162*b6932704SJoseph Chen algorithm_name)) 3163*b6932704SJoseph Chen encoded_key = RSAPublicKey(key_path).encode() 3164*b6932704SJoseph Chen if len(encoded_key) != alg.public_key_num_bytes: 3165*b6932704SJoseph Chen raise AvbError('Key is wrong size for algorithm {}'.format( 3166*b6932704SJoseph Chen algorithm_name)) 3167*b6932704SJoseph Chen 3168*b6932704SJoseph Chen # Override release string, if requested. 3169*b6932704SJoseph Chen if isinstance(release_string, str): 3170*b6932704SJoseph Chen h.release_string = release_string 3171*b6932704SJoseph Chen 3172*b6932704SJoseph Chen # Append to release string, if requested. Also insert a space before. 3173*b6932704SJoseph Chen if isinstance(append_to_release_string, str): 3174*b6932704SJoseph Chen h.release_string += ' ' + append_to_release_string 3175*b6932704SJoseph Chen 3176*b6932704SJoseph Chen # For the Auxiliary data block, descriptors are stored at offset 0, 3177*b6932704SJoseph Chen # followed by the public key, followed by the public key metadata blob. 3178*b6932704SJoseph Chen h.auxiliary_data_block_size = round_to_multiple( 3179*b6932704SJoseph Chen len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64) 3180*b6932704SJoseph Chen h.descriptors_offset = 0 3181*b6932704SJoseph Chen h.descriptors_size = len(encoded_descriptors) 3182*b6932704SJoseph Chen h.public_key_offset = h.descriptors_size 3183*b6932704SJoseph Chen h.public_key_size = len(encoded_key) 3184*b6932704SJoseph Chen h.public_key_metadata_offset = h.public_key_offset + h.public_key_size 3185*b6932704SJoseph Chen h.public_key_metadata_size = len(pkmd_blob) 3186*b6932704SJoseph Chen 3187*b6932704SJoseph Chen # For the Authentication data block, the hash is first and then 3188*b6932704SJoseph Chen # the signature. 3189*b6932704SJoseph Chen h.authentication_data_block_size = round_to_multiple( 3190*b6932704SJoseph Chen alg.hash_num_bytes + alg.signature_num_bytes, 64) 3191*b6932704SJoseph Chen h.algorithm_type = alg.algorithm_type 3192*b6932704SJoseph Chen h.hash_offset = 0 3193*b6932704SJoseph Chen h.hash_size = alg.hash_num_bytes 3194*b6932704SJoseph Chen # Signature offset and size - it's stored right after the hash 3195*b6932704SJoseph Chen # (in Authentication data block). 3196*b6932704SJoseph Chen h.signature_offset = alg.hash_num_bytes 3197*b6932704SJoseph Chen h.signature_size = alg.signature_num_bytes 3198*b6932704SJoseph Chen 3199*b6932704SJoseph Chen h.rollback_index = rollback_index 3200*b6932704SJoseph Chen h.flags = flags 3201*b6932704SJoseph Chen h.rollback_index_location = rollback_index_location 3202*b6932704SJoseph Chen 3203*b6932704SJoseph Chen # Generate Header data block. 3204*b6932704SJoseph Chen header_data_blob = h.encode() 3205*b6932704SJoseph Chen 3206*b6932704SJoseph Chen # Generate Auxiliary data block. 3207*b6932704SJoseph Chen aux_data_blob = bytearray() 3208*b6932704SJoseph Chen aux_data_blob.extend(encoded_descriptors) 3209*b6932704SJoseph Chen aux_data_blob.extend(encoded_key) 3210*b6932704SJoseph Chen aux_data_blob.extend(pkmd_blob) 3211*b6932704SJoseph Chen padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob) 3212*b6932704SJoseph Chen aux_data_blob.extend(b'\0' * padding_bytes) 3213*b6932704SJoseph Chen 3214*b6932704SJoseph Chen # Calculate the hash. 3215*b6932704SJoseph Chen binary_hash = b'' 3216*b6932704SJoseph Chen binary_signature = b'' 3217*b6932704SJoseph Chen if algorithm_name != 'NONE': 3218*b6932704SJoseph Chen ha = hashlib.new(alg.hash_name) 3219*b6932704SJoseph Chen ha.update(header_data_blob) 3220*b6932704SJoseph Chen ha.update(aux_data_blob) 3221*b6932704SJoseph Chen binary_hash = ha.digest() 3222*b6932704SJoseph Chen 3223*b6932704SJoseph Chen # Calculate the signature. 3224*b6932704SJoseph Chen rsa_key = RSAPublicKey(key_path) 3225*b6932704SJoseph Chen data_to_sign = header_data_blob + bytes(aux_data_blob) 3226*b6932704SJoseph Chen binary_signature = rsa_key.sign(algorithm_name, data_to_sign, 3227*b6932704SJoseph Chen signing_helper, signing_helper_with_files) 3228*b6932704SJoseph Chen 3229*b6932704SJoseph Chen # Generate Authentication data block. 3230*b6932704SJoseph Chen auth_data_blob = bytearray() 3231*b6932704SJoseph Chen auth_data_blob.extend(binary_hash) 3232*b6932704SJoseph Chen auth_data_blob.extend(binary_signature) 3233*b6932704SJoseph Chen padding_bytes = h.authentication_data_block_size - len(auth_data_blob) 3234*b6932704SJoseph Chen auth_data_blob.extend(b'\0' * padding_bytes) 3235*b6932704SJoseph Chen 3236*b6932704SJoseph Chen return header_data_blob + bytes(auth_data_blob) + bytes(aux_data_blob) 3237*b6932704SJoseph Chen 3238*b6932704SJoseph Chen def extract_public_key(self, key_path, output): 3239*b6932704SJoseph Chen """Implements the 'extract_public_key' command. 3240*b6932704SJoseph Chen 3241*b6932704SJoseph Chen Arguments: 3242*b6932704SJoseph Chen key_path: The path to a RSA private key file. 3243*b6932704SJoseph Chen output: The file to write to. 3244*b6932704SJoseph Chen 3245*b6932704SJoseph Chen Raises: 3246*b6932704SJoseph Chen AvbError: If the public key could not be extracted. 3247*b6932704SJoseph Chen """ 3248*b6932704SJoseph Chen output.write(RSAPublicKey(key_path).encode()) 3249*b6932704SJoseph Chen 3250*b6932704SJoseph Chen def append_vbmeta_image(self, image_filename, vbmeta_image_filename, 3251*b6932704SJoseph Chen partition_size): 3252*b6932704SJoseph Chen """Implementation of the append_vbmeta_image command. 3253*b6932704SJoseph Chen 3254*b6932704SJoseph Chen Arguments: 3255*b6932704SJoseph Chen image_filename: File to add the footer to. 3256*b6932704SJoseph Chen vbmeta_image_filename: File to get vbmeta struct from. 3257*b6932704SJoseph Chen partition_size: Size of partition. 3258*b6932704SJoseph Chen 3259*b6932704SJoseph Chen Raises: 3260*b6932704SJoseph Chen AvbError: If an argument is incorrect or if appending VBMeta image fialed. 3261*b6932704SJoseph Chen """ 3262*b6932704SJoseph Chen image = ImageHandler(image_filename) 3263*b6932704SJoseph Chen 3264*b6932704SJoseph Chen if partition_size % image.block_size != 0: 3265*b6932704SJoseph Chen raise AvbError('Partition size of {} is not a multiple of the image ' 3266*b6932704SJoseph Chen 'block size {}.'.format(partition_size, 3267*b6932704SJoseph Chen image.block_size)) 3268*b6932704SJoseph Chen 3269*b6932704SJoseph Chen # If there's already a footer, truncate the image to its original 3270*b6932704SJoseph Chen # size. This way 'avbtool append_vbmeta_image' is idempotent. 3271*b6932704SJoseph Chen if image.image_size >= AvbFooter.SIZE: 3272*b6932704SJoseph Chen image.seek(image.image_size - AvbFooter.SIZE) 3273*b6932704SJoseph Chen try: 3274*b6932704SJoseph Chen footer = AvbFooter(image.read(AvbFooter.SIZE)) 3275*b6932704SJoseph Chen # Existing footer found. Just truncate. 3276*b6932704SJoseph Chen original_image_size = footer.original_image_size 3277*b6932704SJoseph Chen image.truncate(footer.original_image_size) 3278*b6932704SJoseph Chen except (LookupError, struct.error): 3279*b6932704SJoseph Chen original_image_size = image.image_size 3280*b6932704SJoseph Chen else: 3281*b6932704SJoseph Chen # Image size is too small to possibly contain a footer. 3282*b6932704SJoseph Chen original_image_size = image.image_size 3283*b6932704SJoseph Chen 3284*b6932704SJoseph Chen # If anything goes wrong from here-on, restore the image back to 3285*b6932704SJoseph Chen # its original size. 3286*b6932704SJoseph Chen try: 3287*b6932704SJoseph Chen vbmeta_image_handler = ImageHandler(vbmeta_image_filename) 3288*b6932704SJoseph Chen vbmeta_blob = self._load_vbmeta_blob(vbmeta_image_handler) 3289*b6932704SJoseph Chen 3290*b6932704SJoseph Chen # If the image isn't sparse, its size might not be a multiple of 3291*b6932704SJoseph Chen # the block size. This will screw up padding later so just grow it. 3292*b6932704SJoseph Chen if image.image_size % image.block_size != 0: 3293*b6932704SJoseph Chen assert not image.is_sparse 3294*b6932704SJoseph Chen padding_needed = image.block_size - (image.image_size%image.block_size) 3295*b6932704SJoseph Chen image.truncate(image.image_size + padding_needed) 3296*b6932704SJoseph Chen 3297*b6932704SJoseph Chen # The append_raw() method requires content with size being a 3298*b6932704SJoseph Chen # multiple of |block_size| so add padding as needed. Also record 3299*b6932704SJoseph Chen # where this is written to since we'll need to put that in the 3300*b6932704SJoseph Chen # footer. 3301*b6932704SJoseph Chen vbmeta_offset = image.image_size 3302*b6932704SJoseph Chen padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) - 3303*b6932704SJoseph Chen len(vbmeta_blob)) 3304*b6932704SJoseph Chen vbmeta_blob_with_padding = vbmeta_blob + b'\0' * padding_needed 3305*b6932704SJoseph Chen 3306*b6932704SJoseph Chen # Append vbmeta blob and footer 3307*b6932704SJoseph Chen image.append_raw(vbmeta_blob_with_padding) 3308*b6932704SJoseph Chen vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding) 3309*b6932704SJoseph Chen 3310*b6932704SJoseph Chen # Now insert a DONT_CARE chunk with enough bytes such that the 3311*b6932704SJoseph Chen # final Footer block is at the end of partition_size.. 3312*b6932704SJoseph Chen image.append_dont_care(partition_size - vbmeta_end_offset - 3313*b6932704SJoseph Chen 1 * image.block_size) 3314*b6932704SJoseph Chen 3315*b6932704SJoseph Chen # Generate the Footer that tells where the VBMeta footer 3316*b6932704SJoseph Chen # is. Also put enough padding in the front of the footer since 3317*b6932704SJoseph Chen # we'll write out an entire block. 3318*b6932704SJoseph Chen footer = AvbFooter() 3319*b6932704SJoseph Chen footer.original_image_size = original_image_size 3320*b6932704SJoseph Chen footer.vbmeta_offset = vbmeta_offset 3321*b6932704SJoseph Chen footer.vbmeta_size = len(vbmeta_blob) 3322*b6932704SJoseph Chen footer_blob = footer.encode() 3323*b6932704SJoseph Chen footer_blob_with_padding = (b'\0' * (image.block_size - AvbFooter.SIZE) + 3324*b6932704SJoseph Chen footer_blob) 3325*b6932704SJoseph Chen image.append_raw(footer_blob_with_padding) 3326*b6932704SJoseph Chen 3327*b6932704SJoseph Chen except Exception as e: 3328*b6932704SJoseph Chen # Truncate back to original size, then re-raise. 3329*b6932704SJoseph Chen image.truncate(original_image_size) 3330*b6932704SJoseph Chen raise AvbError('Appending VBMeta image failed: {}.'.format(e)) from e 3331*b6932704SJoseph Chen 3332*b6932704SJoseph Chen def add_hash_footer(self, image_filename, partition_size, 3333*b6932704SJoseph Chen dynamic_partition_size, partition_name, 3334*b6932704SJoseph Chen hash_algorithm, salt, chain_partitions, algorithm_name, 3335*b6932704SJoseph Chen key_path, 3336*b6932704SJoseph Chen public_key_metadata_path, rollback_index, flags, 3337*b6932704SJoseph Chen rollback_index_location, props, 3338*b6932704SJoseph Chen props_from_file, kernel_cmdlines, 3339*b6932704SJoseph Chen setup_rootfs_from_kernel, 3340*b6932704SJoseph Chen include_descriptors_from_image, calc_max_image_size, 3341*b6932704SJoseph Chen signing_helper, signing_helper_with_files, 3342*b6932704SJoseph Chen release_string, append_to_release_string, 3343*b6932704SJoseph Chen output_vbmeta_image, do_not_append_vbmeta_image, 3344*b6932704SJoseph Chen print_required_libavb_version, use_persistent_digest, 3345*b6932704SJoseph Chen do_not_use_ab): 3346*b6932704SJoseph Chen """Implementation of the add_hash_footer on unsparse images. 3347*b6932704SJoseph Chen 3348*b6932704SJoseph Chen Arguments: 3349*b6932704SJoseph Chen image_filename: File to add the footer to. 3350*b6932704SJoseph Chen partition_size: Size of partition. 3351*b6932704SJoseph Chen dynamic_partition_size: Calculate partition size based on image size. 3352*b6932704SJoseph Chen partition_name: Name of partition (without A/B suffix). 3353*b6932704SJoseph Chen hash_algorithm: Hash algorithm to use. 3354*b6932704SJoseph Chen salt: Salt to use as a hexadecimal string or None to use /dev/urandom. 3355*b6932704SJoseph Chen chain_partitions: List of partitions to chain. 3356*b6932704SJoseph Chen algorithm_name: Name of algorithm to use. 3357*b6932704SJoseph Chen key_path: Path to key to use or None. 3358*b6932704SJoseph Chen public_key_metadata_path: Path to public key metadata or None. 3359*b6932704SJoseph Chen rollback_index: Rollback index. 3360*b6932704SJoseph Chen flags: Flags value to use in the image. 3361*b6932704SJoseph Chen rollback_index_location: Location of the main vbmeta rollback index. 3362*b6932704SJoseph Chen props: Properties to insert (List of strings of the form 'key:value'). 3363*b6932704SJoseph Chen props_from_file: Properties to insert (List of strings 'key:<path>'). 3364*b6932704SJoseph Chen kernel_cmdlines: Kernel cmdlines to insert (list of strings). 3365*b6932704SJoseph Chen setup_rootfs_from_kernel: None or file to generate 3366*b6932704SJoseph Chen dm-verity kernel cmdline from. 3367*b6932704SJoseph Chen include_descriptors_from_image: List of file objects for which 3368*b6932704SJoseph Chen to insert descriptors from. 3369*b6932704SJoseph Chen calc_max_image_size: Don't store the footer - instead calculate the 3370*b6932704SJoseph Chen maximum image size leaving enough room for metadata with the 3371*b6932704SJoseph Chen given |partition_size|. 3372*b6932704SJoseph Chen signing_helper: Program which signs a hash and return signature. 3373*b6932704SJoseph Chen signing_helper_with_files: Same as signing_helper but uses files instead. 3374*b6932704SJoseph Chen release_string: None or avbtool release string. 3375*b6932704SJoseph Chen append_to_release_string: None or string to append. 3376*b6932704SJoseph Chen output_vbmeta_image: If not None, also write vbmeta struct to this file. 3377*b6932704SJoseph Chen do_not_append_vbmeta_image: If True, don't append vbmeta struct. 3378*b6932704SJoseph Chen print_required_libavb_version: True to only print required libavb version. 3379*b6932704SJoseph Chen use_persistent_digest: Use a persistent digest on device. 3380*b6932704SJoseph Chen do_not_use_ab: This partition does not use A/B. 3381*b6932704SJoseph Chen 3382*b6932704SJoseph Chen Raises: 3383*b6932704SJoseph Chen AvbError: If an argument is incorrect of if adding of hash_footer failed. 3384*b6932704SJoseph Chen """ 3385*b6932704SJoseph Chen if not partition_size and not dynamic_partition_size: 3386*b6932704SJoseph Chen raise AvbError('--dynamic_partition_size required when not specifying a ' 3387*b6932704SJoseph Chen 'partition size') 3388*b6932704SJoseph Chen 3389*b6932704SJoseph Chen if dynamic_partition_size and calc_max_image_size: 3390*b6932704SJoseph Chen raise AvbError('--calc_max_image_size not supported with ' 3391*b6932704SJoseph Chen '--dynamic_partition_size') 3392*b6932704SJoseph Chen 3393*b6932704SJoseph Chen required_libavb_version_minor = 0 3394*b6932704SJoseph Chen if use_persistent_digest or do_not_use_ab: 3395*b6932704SJoseph Chen required_libavb_version_minor = 1 3396*b6932704SJoseph Chen if rollback_index_location > 0: 3397*b6932704SJoseph Chen required_libavb_version_minor = 2 3398*b6932704SJoseph Chen 3399*b6932704SJoseph Chen # If we're asked to calculate minimum required libavb version, we're done. 3400*b6932704SJoseph Chen if print_required_libavb_version: 3401*b6932704SJoseph Chen print('1.{}'.format(required_libavb_version_minor)) 3402*b6932704SJoseph Chen return 3403*b6932704SJoseph Chen 3404*b6932704SJoseph Chen # First, calculate the maximum image size such that an image 3405*b6932704SJoseph Chen # this size + metadata (footer + vbmeta struct) fits in 3406*b6932704SJoseph Chen # |partition_size|. 3407*b6932704SJoseph Chen max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE 3408*b6932704SJoseph Chen if not dynamic_partition_size and partition_size < max_metadata_size: 3409*b6932704SJoseph Chen raise AvbError('Parition size of {} is too small. ' 3410*b6932704SJoseph Chen 'Needs to be at least {}'.format( 3411*b6932704SJoseph Chen partition_size, max_metadata_size)) 3412*b6932704SJoseph Chen 3413*b6932704SJoseph Chen # If we're asked to only calculate the maximum image size, we're done. 3414*b6932704SJoseph Chen if calc_max_image_size: 3415*b6932704SJoseph Chen print('{}'.format(partition_size - max_metadata_size)) 3416*b6932704SJoseph Chen return 3417*b6932704SJoseph Chen 3418*b6932704SJoseph Chen image = ImageHandler(image_filename) 3419*b6932704SJoseph Chen 3420*b6932704SJoseph Chen # If there's already a footer, truncate the image to its original 3421*b6932704SJoseph Chen # size. This way 'avbtool add_hash_footer' is idempotent (modulo 3422*b6932704SJoseph Chen # salts). 3423*b6932704SJoseph Chen if image.image_size >= AvbFooter.SIZE: 3424*b6932704SJoseph Chen image.seek(image.image_size - AvbFooter.SIZE) 3425*b6932704SJoseph Chen try: 3426*b6932704SJoseph Chen footer = AvbFooter(image.read(AvbFooter.SIZE)) 3427*b6932704SJoseph Chen # Existing footer found. Just truncate. 3428*b6932704SJoseph Chen original_image_size = footer.original_image_size 3429*b6932704SJoseph Chen image.truncate(footer.original_image_size) 3430*b6932704SJoseph Chen except (LookupError, struct.error): 3431*b6932704SJoseph Chen original_image_size = image.image_size 3432*b6932704SJoseph Chen else: 3433*b6932704SJoseph Chen # Image size is too small to possibly contain a footer. 3434*b6932704SJoseph Chen original_image_size = image.image_size 3435*b6932704SJoseph Chen 3436*b6932704SJoseph Chen if dynamic_partition_size: 3437*b6932704SJoseph Chen partition_size = round_to_multiple( 3438*b6932704SJoseph Chen original_image_size + max_metadata_size, image.block_size) 3439*b6932704SJoseph Chen 3440*b6932704SJoseph Chen max_image_size = partition_size - max_metadata_size 3441*b6932704SJoseph Chen if partition_size % image.block_size != 0: 3442*b6932704SJoseph Chen raise AvbError('Partition size of {} is not a multiple of the image ' 3443*b6932704SJoseph Chen 'block size {}.'.format(partition_size, 3444*b6932704SJoseph Chen image.block_size)) 3445*b6932704SJoseph Chen 3446*b6932704SJoseph Chen # If anything goes wrong from here-on, restore the image back to 3447*b6932704SJoseph Chen # its original size. 3448*b6932704SJoseph Chen try: 3449*b6932704SJoseph Chen # If image size exceeds the maximum image size, fail. 3450*b6932704SJoseph Chen if image.image_size > max_image_size: 3451*b6932704SJoseph Chen raise AvbError('Image size of {} exceeds maximum image ' 3452*b6932704SJoseph Chen 'size of {} in order to fit in a partition ' 3453*b6932704SJoseph Chen 'size of {}.'.format(image.image_size, max_image_size, 3454*b6932704SJoseph Chen partition_size)) 3455*b6932704SJoseph Chen 3456*b6932704SJoseph Chen digest_size = len(hashlib.new(hash_algorithm).digest()) 3457*b6932704SJoseph Chen if salt: 3458*b6932704SJoseph Chen salt = binascii.unhexlify(salt) 3459*b6932704SJoseph Chen elif salt is None and not use_persistent_digest: 3460*b6932704SJoseph Chen # If salt is not explicitly specified, choose a hash that's the same 3461*b6932704SJoseph Chen # size as the hash size. Don't populate a random salt if this 3462*b6932704SJoseph Chen # descriptor is being created to use a persistent digest on device. 3463*b6932704SJoseph Chen hash_size = digest_size 3464*b6932704SJoseph Chen with open('/dev/urandom', 'rb') as f: 3465*b6932704SJoseph Chen salt = f.read(hash_size) 3466*b6932704SJoseph Chen else: 3467*b6932704SJoseph Chen salt = b'' 3468*b6932704SJoseph Chen 3469*b6932704SJoseph Chen hasher = hashlib.new(hash_algorithm, salt) 3470*b6932704SJoseph Chen # TODO(zeuthen): might want to read this in chunks to avoid 3471*b6932704SJoseph Chen # memory pressure, then again, this is only supposed to be used 3472*b6932704SJoseph Chen # on kernel/initramfs partitions. Possible optimization. 3473*b6932704SJoseph Chen image.seek(0) 3474*b6932704SJoseph Chen hasher.update(image.read(image.image_size)) 3475*b6932704SJoseph Chen digest = hasher.digest() 3476*b6932704SJoseph Chen 3477*b6932704SJoseph Chen h_desc = AvbHashDescriptor() 3478*b6932704SJoseph Chen h_desc.image_size = image.image_size 3479*b6932704SJoseph Chen h_desc.hash_algorithm = hash_algorithm 3480*b6932704SJoseph Chen h_desc.partition_name = partition_name 3481*b6932704SJoseph Chen h_desc.salt = salt 3482*b6932704SJoseph Chen h_desc.flags = 0 3483*b6932704SJoseph Chen if do_not_use_ab: 3484*b6932704SJoseph Chen h_desc.flags |= 1 # AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB 3485*b6932704SJoseph Chen if not use_persistent_digest: 3486*b6932704SJoseph Chen h_desc.digest = digest 3487*b6932704SJoseph Chen 3488*b6932704SJoseph Chen # Generate the VBMeta footer. 3489*b6932704SJoseph Chen ht_desc_to_setup = None 3490*b6932704SJoseph Chen vbmeta_blob = self._generate_vbmeta_blob( 3491*b6932704SJoseph Chen algorithm_name, key_path, public_key_metadata_path, [h_desc], 3492*b6932704SJoseph Chen chain_partitions, rollback_index, flags, rollback_index_location, 3493*b6932704SJoseph Chen props, props_from_file, 3494*b6932704SJoseph Chen kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup, 3495*b6932704SJoseph Chen include_descriptors_from_image, signing_helper, 3496*b6932704SJoseph Chen signing_helper_with_files, release_string, 3497*b6932704SJoseph Chen append_to_release_string, required_libavb_version_minor) 3498*b6932704SJoseph Chen 3499*b6932704SJoseph Chen # Write vbmeta blob, if requested. 3500*b6932704SJoseph Chen if output_vbmeta_image: 3501*b6932704SJoseph Chen output_vbmeta_image.write(vbmeta_blob) 3502*b6932704SJoseph Chen 3503*b6932704SJoseph Chen # Append vbmeta blob and footer, unless requested not to. 3504*b6932704SJoseph Chen if not do_not_append_vbmeta_image: 3505*b6932704SJoseph Chen # If the image isn't sparse, its size might not be a multiple of 3506*b6932704SJoseph Chen # the block size. This will screw up padding later so just grow it. 3507*b6932704SJoseph Chen if image.image_size % image.block_size != 0: 3508*b6932704SJoseph Chen assert not image.is_sparse 3509*b6932704SJoseph Chen padding_needed = image.block_size - ( 3510*b6932704SJoseph Chen image.image_size % image.block_size) 3511*b6932704SJoseph Chen image.truncate(image.image_size + padding_needed) 3512*b6932704SJoseph Chen 3513*b6932704SJoseph Chen # The append_raw() method requires content with size being a 3514*b6932704SJoseph Chen # multiple of |block_size| so add padding as needed. Also record 3515*b6932704SJoseph Chen # where this is written to since we'll need to put that in the 3516*b6932704SJoseph Chen # footer. 3517*b6932704SJoseph Chen vbmeta_offset = image.image_size 3518*b6932704SJoseph Chen padding_needed = ( 3519*b6932704SJoseph Chen round_to_multiple(len(vbmeta_blob), image.block_size) - 3520*b6932704SJoseph Chen len(vbmeta_blob)) 3521*b6932704SJoseph Chen vbmeta_blob_with_padding = vbmeta_blob + b'\0' * padding_needed 3522*b6932704SJoseph Chen 3523*b6932704SJoseph Chen image.append_raw(vbmeta_blob_with_padding) 3524*b6932704SJoseph Chen vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding) 3525*b6932704SJoseph Chen 3526*b6932704SJoseph Chen # Now insert a DONT_CARE chunk with enough bytes such that the 3527*b6932704SJoseph Chen # final Footer block is at the end of partition_size.. 3528*b6932704SJoseph Chen image.append_dont_care(partition_size - vbmeta_end_offset - 3529*b6932704SJoseph Chen 1 * image.block_size) 3530*b6932704SJoseph Chen 3531*b6932704SJoseph Chen # Generate the Footer that tells where the VBMeta footer 3532*b6932704SJoseph Chen # is. Also put enough padding in the front of the footer since 3533*b6932704SJoseph Chen # we'll write out an entire block. 3534*b6932704SJoseph Chen footer = AvbFooter() 3535*b6932704SJoseph Chen footer.original_image_size = original_image_size 3536*b6932704SJoseph Chen footer.vbmeta_offset = vbmeta_offset 3537*b6932704SJoseph Chen footer.vbmeta_size = len(vbmeta_blob) 3538*b6932704SJoseph Chen footer_blob = footer.encode() 3539*b6932704SJoseph Chen footer_blob_with_padding = ( 3540*b6932704SJoseph Chen b'\0' * (image.block_size - AvbFooter.SIZE) + footer_blob) 3541*b6932704SJoseph Chen image.append_raw(footer_blob_with_padding) 3542*b6932704SJoseph Chen except Exception as e: 3543*b6932704SJoseph Chen # Truncate back to original size, then re-raise. 3544*b6932704SJoseph Chen image.truncate(original_image_size) 3545*b6932704SJoseph Chen raise AvbError('Adding hash_footer failed: {}.'.format(e)) from e 3546*b6932704SJoseph Chen 3547*b6932704SJoseph Chen def add_hashtree_footer(self, image_filename, partition_size, partition_name, 3548*b6932704SJoseph Chen generate_fec, fec_num_roots, hash_algorithm, 3549*b6932704SJoseph Chen block_size, salt, chain_partitions, algorithm_name, 3550*b6932704SJoseph Chen key_path, 3551*b6932704SJoseph Chen public_key_metadata_path, rollback_index, flags, 3552*b6932704SJoseph Chen rollback_index_location, 3553*b6932704SJoseph Chen props, props_from_file, kernel_cmdlines, 3554*b6932704SJoseph Chen setup_rootfs_from_kernel, 3555*b6932704SJoseph Chen setup_as_rootfs_from_kernel, 3556*b6932704SJoseph Chen include_descriptors_from_image, 3557*b6932704SJoseph Chen calc_max_image_size, signing_helper, 3558*b6932704SJoseph Chen signing_helper_with_files, 3559*b6932704SJoseph Chen release_string, append_to_release_string, 3560*b6932704SJoseph Chen output_vbmeta_image, do_not_append_vbmeta_image, 3561*b6932704SJoseph Chen print_required_libavb_version, 3562*b6932704SJoseph Chen use_persistent_root_digest, do_not_use_ab, 3563*b6932704SJoseph Chen no_hashtree, check_at_most_once): 3564*b6932704SJoseph Chen """Implements the 'add_hashtree_footer' command. 3565*b6932704SJoseph Chen 3566*b6932704SJoseph Chen See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for 3567*b6932704SJoseph Chen more information about dm-verity and these hashes. 3568*b6932704SJoseph Chen 3569*b6932704SJoseph Chen Arguments: 3570*b6932704SJoseph Chen image_filename: File to add the footer to. 3571*b6932704SJoseph Chen partition_size: Size of partition or 0 to put it right at the end. 3572*b6932704SJoseph Chen partition_name: Name of partition (without A/B suffix). 3573*b6932704SJoseph Chen generate_fec: If True, generate FEC codes. 3574*b6932704SJoseph Chen fec_num_roots: Number of roots for FEC. 3575*b6932704SJoseph Chen hash_algorithm: Hash algorithm to use. 3576*b6932704SJoseph Chen block_size: Block size to use. 3577*b6932704SJoseph Chen salt: Salt to use as a hexadecimal string or None to use /dev/urandom. 3578*b6932704SJoseph Chen chain_partitions: List of partitions to chain. 3579*b6932704SJoseph Chen algorithm_name: Name of algorithm to use. 3580*b6932704SJoseph Chen key_path: Path to key to use or None. 3581*b6932704SJoseph Chen public_key_metadata_path: Path to public key metadata or None. 3582*b6932704SJoseph Chen rollback_index: Rollback index. 3583*b6932704SJoseph Chen flags: Flags value to use in the image. 3584*b6932704SJoseph Chen rollback_index_location: Location of the main vbmeta rollback index. 3585*b6932704SJoseph Chen props: Properties to insert (List of strings of the form 'key:value'). 3586*b6932704SJoseph Chen props_from_file: Properties to insert (List of strings 'key:<path>'). 3587*b6932704SJoseph Chen kernel_cmdlines: Kernel cmdlines to insert (list of strings). 3588*b6932704SJoseph Chen setup_rootfs_from_kernel: None or file to generate 3589*b6932704SJoseph Chen dm-verity kernel cmdline from. 3590*b6932704SJoseph Chen setup_as_rootfs_from_kernel: If True, generate dm-verity kernel 3591*b6932704SJoseph Chen cmdline to set up rootfs. 3592*b6932704SJoseph Chen include_descriptors_from_image: List of file objects for which 3593*b6932704SJoseph Chen to insert descriptors from. 3594*b6932704SJoseph Chen calc_max_image_size: Don't store the hashtree or footer - instead 3595*b6932704SJoseph Chen calculate the maximum image size leaving enough room for hashtree 3596*b6932704SJoseph Chen and metadata with the given |partition_size|. 3597*b6932704SJoseph Chen signing_helper: Program which signs a hash and return signature. 3598*b6932704SJoseph Chen signing_helper_with_files: Same as signing_helper but uses files instead. 3599*b6932704SJoseph Chen release_string: None or avbtool release string. 3600*b6932704SJoseph Chen append_to_release_string: None or string to append. 3601*b6932704SJoseph Chen output_vbmeta_image: If not None, also write vbmeta struct to this file. 3602*b6932704SJoseph Chen do_not_append_vbmeta_image: If True, don't append vbmeta struct. 3603*b6932704SJoseph Chen print_required_libavb_version: True to only print required libavb version. 3604*b6932704SJoseph Chen use_persistent_root_digest: Use a persistent root digest on device. 3605*b6932704SJoseph Chen do_not_use_ab: The partition does not use A/B. 3606*b6932704SJoseph Chen no_hashtree: Do not append hashtree. Set size in descriptor as zero. 3607*b6932704SJoseph Chen check_at_most_once: Set to verify data blocks only the first time they 3608*b6932704SJoseph Chen are read from the data device. 3609*b6932704SJoseph Chen 3610*b6932704SJoseph Chen Raises: 3611*b6932704SJoseph Chen AvbError: If an argument is incorrect or adding the hashtree footer 3612*b6932704SJoseph Chen failed. 3613*b6932704SJoseph Chen """ 3614*b6932704SJoseph Chen required_libavb_version_minor = 0 3615*b6932704SJoseph Chen if use_persistent_root_digest or do_not_use_ab or check_at_most_once: 3616*b6932704SJoseph Chen required_libavb_version_minor = 1 3617*b6932704SJoseph Chen if rollback_index_location > 0: 3618*b6932704SJoseph Chen required_libavb_version_minor = 2 3619*b6932704SJoseph Chen 3620*b6932704SJoseph Chen # If we're asked to calculate minimum required libavb version, we're done. 3621*b6932704SJoseph Chen if print_required_libavb_version: 3622*b6932704SJoseph Chen print('1.{}'.format(required_libavb_version_minor)) 3623*b6932704SJoseph Chen return 3624*b6932704SJoseph Chen 3625*b6932704SJoseph Chen digest_size = len(create_avb_hashtree_hasher(hash_algorithm, b'') 3626*b6932704SJoseph Chen .digest()) 3627*b6932704SJoseph Chen digest_padding = round_to_pow2(digest_size) - digest_size 3628*b6932704SJoseph Chen 3629*b6932704SJoseph Chen # If |partition_size| is given (e.g. not 0), calculate the maximum image 3630*b6932704SJoseph Chen # size such that an image this size + the hashtree + metadata (footer + 3631*b6932704SJoseph Chen # vbmeta struct) fits in |partition_size|. We use very conservative figures 3632*b6932704SJoseph Chen # for metadata. 3633*b6932704SJoseph Chen if partition_size > 0: 3634*b6932704SJoseph Chen max_tree_size = 0 3635*b6932704SJoseph Chen max_fec_size = 0 3636*b6932704SJoseph Chen if not no_hashtree: 3637*b6932704SJoseph Chen (_, max_tree_size) = calc_hash_level_offsets( 3638*b6932704SJoseph Chen partition_size, block_size, digest_size + digest_padding) 3639*b6932704SJoseph Chen if generate_fec: 3640*b6932704SJoseph Chen max_fec_size = calc_fec_data_size(partition_size, fec_num_roots) 3641*b6932704SJoseph Chen max_metadata_size = (max_fec_size + max_tree_size + 3642*b6932704SJoseph Chen self.MAX_VBMETA_SIZE + 3643*b6932704SJoseph Chen self.MAX_FOOTER_SIZE) 3644*b6932704SJoseph Chen max_image_size = partition_size - max_metadata_size 3645*b6932704SJoseph Chen else: 3646*b6932704SJoseph Chen max_image_size = 0 3647*b6932704SJoseph Chen 3648*b6932704SJoseph Chen # If we're asked to only calculate the maximum image size, we're done. 3649*b6932704SJoseph Chen if calc_max_image_size: 3650*b6932704SJoseph Chen print('{}'.format(max_image_size)) 3651*b6932704SJoseph Chen return 3652*b6932704SJoseph Chen 3653*b6932704SJoseph Chen image = ImageHandler(image_filename) 3654*b6932704SJoseph Chen 3655*b6932704SJoseph Chen if partition_size > 0: 3656*b6932704SJoseph Chen if partition_size % image.block_size != 0: 3657*b6932704SJoseph Chen raise AvbError('Partition size of {} is not a multiple of the image ' 3658*b6932704SJoseph Chen 'block size {}.'.format(partition_size, 3659*b6932704SJoseph Chen image.block_size)) 3660*b6932704SJoseph Chen elif image.image_size % image.block_size != 0: 3661*b6932704SJoseph Chen raise AvbError('File size of {} is not a multiple of the image ' 3662*b6932704SJoseph Chen 'block size {}.'.format(image.image_size, 3663*b6932704SJoseph Chen image.block_size)) 3664*b6932704SJoseph Chen 3665*b6932704SJoseph Chen # If there's already a footer, truncate the image to its original 3666*b6932704SJoseph Chen # size. This way 'avbtool add_hashtree_footer' is idempotent 3667*b6932704SJoseph Chen # (modulo salts). 3668*b6932704SJoseph Chen if image.image_size >= AvbFooter.SIZE: 3669*b6932704SJoseph Chen image.seek(image.image_size - AvbFooter.SIZE) 3670*b6932704SJoseph Chen try: 3671*b6932704SJoseph Chen footer = AvbFooter(image.read(AvbFooter.SIZE)) 3672*b6932704SJoseph Chen # Existing footer found. Just truncate. 3673*b6932704SJoseph Chen original_image_size = footer.original_image_size 3674*b6932704SJoseph Chen image.truncate(footer.original_image_size) 3675*b6932704SJoseph Chen except (LookupError, struct.error): 3676*b6932704SJoseph Chen original_image_size = image.image_size 3677*b6932704SJoseph Chen else: 3678*b6932704SJoseph Chen # Image size is too small to possibly contain a footer. 3679*b6932704SJoseph Chen original_image_size = image.image_size 3680*b6932704SJoseph Chen 3681*b6932704SJoseph Chen # If anything goes wrong from here-on, restore the image back to 3682*b6932704SJoseph Chen # its original size. 3683*b6932704SJoseph Chen try: 3684*b6932704SJoseph Chen # Ensure image is multiple of block_size. 3685*b6932704SJoseph Chen rounded_image_size = round_to_multiple(image.image_size, block_size) 3686*b6932704SJoseph Chen if rounded_image_size > image.image_size: 3687*b6932704SJoseph Chen image.append_raw('\0' * (rounded_image_size - image.image_size)) 3688*b6932704SJoseph Chen 3689*b6932704SJoseph Chen # If image size exceeds the maximum image size, fail. 3690*b6932704SJoseph Chen if partition_size > 0: 3691*b6932704SJoseph Chen if image.image_size > max_image_size: 3692*b6932704SJoseph Chen raise AvbError('Image size of {} exceeds maximum image ' 3693*b6932704SJoseph Chen 'size of {} in order to fit in a partition ' 3694*b6932704SJoseph Chen 'size of {}.'.format(image.image_size, max_image_size, 3695*b6932704SJoseph Chen partition_size)) 3696*b6932704SJoseph Chen 3697*b6932704SJoseph Chen if salt: 3698*b6932704SJoseph Chen salt = binascii.unhexlify(salt) 3699*b6932704SJoseph Chen elif salt is None and not use_persistent_root_digest: 3700*b6932704SJoseph Chen # If salt is not explicitly specified, choose a hash that's the same 3701*b6932704SJoseph Chen # size as the hash size. Don't populate a random salt if this 3702*b6932704SJoseph Chen # descriptor is being created to use a persistent digest on device. 3703*b6932704SJoseph Chen hash_size = digest_size 3704*b6932704SJoseph Chen with open('/dev/urandom', 'rb') as f: 3705*b6932704SJoseph Chen salt = f.read(hash_size) 3706*b6932704SJoseph Chen else: 3707*b6932704SJoseph Chen salt = b'' 3708*b6932704SJoseph Chen 3709*b6932704SJoseph Chen # Hashes are stored upside down so we need to calculate hash 3710*b6932704SJoseph Chen # offsets in advance. 3711*b6932704SJoseph Chen (hash_level_offsets, tree_size) = calc_hash_level_offsets( 3712*b6932704SJoseph Chen image.image_size, block_size, digest_size + digest_padding) 3713*b6932704SJoseph Chen 3714*b6932704SJoseph Chen # If the image isn't sparse, its size might not be a multiple of 3715*b6932704SJoseph Chen # the block size. This will screw up padding later so just grow it. 3716*b6932704SJoseph Chen if image.image_size % image.block_size != 0: 3717*b6932704SJoseph Chen assert not image.is_sparse 3718*b6932704SJoseph Chen padding_needed = image.block_size - (image.image_size%image.block_size) 3719*b6932704SJoseph Chen image.truncate(image.image_size + padding_needed) 3720*b6932704SJoseph Chen 3721*b6932704SJoseph Chen # Generate the tree and add padding as needed. 3722*b6932704SJoseph Chen tree_offset = image.image_size 3723*b6932704SJoseph Chen root_digest, hash_tree = generate_hash_tree(image, image.image_size, 3724*b6932704SJoseph Chen block_size, 3725*b6932704SJoseph Chen hash_algorithm, salt, 3726*b6932704SJoseph Chen digest_padding, 3727*b6932704SJoseph Chen hash_level_offsets, 3728*b6932704SJoseph Chen tree_size) 3729*b6932704SJoseph Chen 3730*b6932704SJoseph Chen # Generate HashtreeDescriptor with details about the tree we 3731*b6932704SJoseph Chen # just generated. 3732*b6932704SJoseph Chen if no_hashtree: 3733*b6932704SJoseph Chen tree_size = 0 3734*b6932704SJoseph Chen hash_tree = b'' 3735*b6932704SJoseph Chen ht_desc = AvbHashtreeDescriptor() 3736*b6932704SJoseph Chen ht_desc.dm_verity_version = 1 3737*b6932704SJoseph Chen ht_desc.image_size = image.image_size 3738*b6932704SJoseph Chen ht_desc.tree_offset = tree_offset 3739*b6932704SJoseph Chen ht_desc.tree_size = tree_size 3740*b6932704SJoseph Chen ht_desc.data_block_size = block_size 3741*b6932704SJoseph Chen ht_desc.hash_block_size = block_size 3742*b6932704SJoseph Chen ht_desc.hash_algorithm = hash_algorithm 3743*b6932704SJoseph Chen ht_desc.partition_name = partition_name 3744*b6932704SJoseph Chen ht_desc.salt = salt 3745*b6932704SJoseph Chen if do_not_use_ab: 3746*b6932704SJoseph Chen ht_desc.flags |= AvbHashtreeDescriptor.FLAGS_DO_NOT_USE_AB 3747*b6932704SJoseph Chen if not use_persistent_root_digest: 3748*b6932704SJoseph Chen ht_desc.root_digest = root_digest 3749*b6932704SJoseph Chen if check_at_most_once: 3750*b6932704SJoseph Chen ht_desc.flags |= AvbHashtreeDescriptor.FLAGS_CHECK_AT_MOST_ONCE 3751*b6932704SJoseph Chen 3752*b6932704SJoseph Chen # Write the hash tree 3753*b6932704SJoseph Chen padding_needed = (round_to_multiple(len(hash_tree), image.block_size) - 3754*b6932704SJoseph Chen len(hash_tree)) 3755*b6932704SJoseph Chen hash_tree_with_padding = hash_tree + b'\0' * padding_needed 3756*b6932704SJoseph Chen image.append_raw(hash_tree_with_padding) 3757*b6932704SJoseph Chen len_hashtree_and_fec = len(hash_tree_with_padding) 3758*b6932704SJoseph Chen 3759*b6932704SJoseph Chen # Generate FEC codes, if requested. 3760*b6932704SJoseph Chen if generate_fec: 3761*b6932704SJoseph Chen if no_hashtree: 3762*b6932704SJoseph Chen fec_data = b'' 3763*b6932704SJoseph Chen else: 3764*b6932704SJoseph Chen fec_data = generate_fec_data(image_filename, fec_num_roots) 3765*b6932704SJoseph Chen padding_needed = (round_to_multiple(len(fec_data), image.block_size) - 3766*b6932704SJoseph Chen len(fec_data)) 3767*b6932704SJoseph Chen fec_data_with_padding = fec_data + b'\0' * padding_needed 3768*b6932704SJoseph Chen fec_offset = image.image_size 3769*b6932704SJoseph Chen image.append_raw(fec_data_with_padding) 3770*b6932704SJoseph Chen len_hashtree_and_fec += len(fec_data_with_padding) 3771*b6932704SJoseph Chen # Update the hashtree descriptor. 3772*b6932704SJoseph Chen ht_desc.fec_num_roots = fec_num_roots 3773*b6932704SJoseph Chen ht_desc.fec_offset = fec_offset 3774*b6932704SJoseph Chen ht_desc.fec_size = len(fec_data) 3775*b6932704SJoseph Chen 3776*b6932704SJoseph Chen ht_desc_to_setup = None 3777*b6932704SJoseph Chen if setup_as_rootfs_from_kernel: 3778*b6932704SJoseph Chen ht_desc_to_setup = ht_desc 3779*b6932704SJoseph Chen 3780*b6932704SJoseph Chen # Generate the VBMeta footer and add padding as needed. 3781*b6932704SJoseph Chen vbmeta_offset = tree_offset + len_hashtree_and_fec 3782*b6932704SJoseph Chen vbmeta_blob = self._generate_vbmeta_blob( 3783*b6932704SJoseph Chen algorithm_name, key_path, public_key_metadata_path, [ht_desc], 3784*b6932704SJoseph Chen chain_partitions, rollback_index, flags, rollback_index_location, 3785*b6932704SJoseph Chen props, props_from_file, 3786*b6932704SJoseph Chen kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup, 3787*b6932704SJoseph Chen include_descriptors_from_image, signing_helper, 3788*b6932704SJoseph Chen signing_helper_with_files, release_string, 3789*b6932704SJoseph Chen append_to_release_string, required_libavb_version_minor) 3790*b6932704SJoseph Chen padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) - 3791*b6932704SJoseph Chen len(vbmeta_blob)) 3792*b6932704SJoseph Chen vbmeta_blob_with_padding = vbmeta_blob + b'\0' * padding_needed 3793*b6932704SJoseph Chen 3794*b6932704SJoseph Chen # Write vbmeta blob, if requested. 3795*b6932704SJoseph Chen if output_vbmeta_image: 3796*b6932704SJoseph Chen output_vbmeta_image.write(vbmeta_blob) 3797*b6932704SJoseph Chen 3798*b6932704SJoseph Chen # Append vbmeta blob and footer, unless requested not to. 3799*b6932704SJoseph Chen if not do_not_append_vbmeta_image: 3800*b6932704SJoseph Chen image.append_raw(vbmeta_blob_with_padding) 3801*b6932704SJoseph Chen 3802*b6932704SJoseph Chen # Now insert a DONT_CARE chunk with enough bytes such that the 3803*b6932704SJoseph Chen # final Footer block is at the end of partition_size.. 3804*b6932704SJoseph Chen if partition_size > 0: 3805*b6932704SJoseph Chen image.append_dont_care(partition_size - image.image_size - 3806*b6932704SJoseph Chen 1 * image.block_size) 3807*b6932704SJoseph Chen 3808*b6932704SJoseph Chen # Generate the Footer that tells where the VBMeta footer 3809*b6932704SJoseph Chen # is. Also put enough padding in the front of the footer since 3810*b6932704SJoseph Chen # we'll write out an entire block. 3811*b6932704SJoseph Chen footer = AvbFooter() 3812*b6932704SJoseph Chen footer.original_image_size = original_image_size 3813*b6932704SJoseph Chen footer.vbmeta_offset = vbmeta_offset 3814*b6932704SJoseph Chen footer.vbmeta_size = len(vbmeta_blob) 3815*b6932704SJoseph Chen footer_blob = footer.encode() 3816*b6932704SJoseph Chen footer_blob_with_padding = ( 3817*b6932704SJoseph Chen b'\0' * (image.block_size - AvbFooter.SIZE) + footer_blob) 3818*b6932704SJoseph Chen image.append_raw(footer_blob_with_padding) 3819*b6932704SJoseph Chen 3820*b6932704SJoseph Chen except Exception as e: 3821*b6932704SJoseph Chen # Truncate back to original size, then re-raise. 3822*b6932704SJoseph Chen image.truncate(original_image_size) 3823*b6932704SJoseph Chen raise AvbError('Adding hashtree_footer failed: {}.'.format(e)) from e 3824*b6932704SJoseph Chen 3825*b6932704SJoseph Chen def make_atx_certificate(self, output, authority_key_path, subject_key_path, 3826*b6932704SJoseph Chen subject_key_version, subject, 3827*b6932704SJoseph Chen is_intermediate_authority, usage, signing_helper, 3828*b6932704SJoseph Chen signing_helper_with_files): 3829*b6932704SJoseph Chen """Implements the 'make_atx_certificate' command. 3830*b6932704SJoseph Chen 3831*b6932704SJoseph Chen Android Things certificates are required for Android Things public key 3832*b6932704SJoseph Chen metadata. They chain the vbmeta signing key for a particular product back to 3833*b6932704SJoseph Chen a fused, permanent root key. These certificates are fixed-length and fixed- 3834*b6932704SJoseph Chen format with the explicit goal of not parsing ASN.1 in bootloader code. 3835*b6932704SJoseph Chen 3836*b6932704SJoseph Chen Arguments: 3837*b6932704SJoseph Chen output: Certificate will be written to this file on success. 3838*b6932704SJoseph Chen authority_key_path: A PEM file path with the authority private key. 3839*b6932704SJoseph Chen If None, then a certificate will be created without a 3840*b6932704SJoseph Chen signature. The signature can be created out-of-band 3841*b6932704SJoseph Chen and appended. 3842*b6932704SJoseph Chen subject_key_path: Path to a PEM or DER subject public key. 3843*b6932704SJoseph Chen subject_key_version: A 64-bit version value. If this is None, the number 3844*b6932704SJoseph Chen of seconds since the epoch is used. 3845*b6932704SJoseph Chen subject: A subject identifier. For Product Signing Key certificates this 3846*b6932704SJoseph Chen should be the same Product ID found in the permanent attributes. 3847*b6932704SJoseph Chen is_intermediate_authority: True if the certificate is for an intermediate 3848*b6932704SJoseph Chen authority. 3849*b6932704SJoseph Chen usage: If not empty, overrides the cert usage with a hash of this value. 3850*b6932704SJoseph Chen signing_helper: Program which signs a hash and returns the signature. 3851*b6932704SJoseph Chen signing_helper_with_files: Same as signing_helper but uses files instead. 3852*b6932704SJoseph Chen 3853*b6932704SJoseph Chen Raises: 3854*b6932704SJoseph Chen AvbError: If there an error during signing. 3855*b6932704SJoseph Chen """ 3856*b6932704SJoseph Chen signed_data = bytearray() 3857*b6932704SJoseph Chen signed_data.extend(struct.pack('<I', 1)) # Format Version 3858*b6932704SJoseph Chen signed_data.extend(RSAPublicKey(subject_key_path).encode()) 3859*b6932704SJoseph Chen hasher = hashlib.sha256() 3860*b6932704SJoseph Chen hasher.update(subject) 3861*b6932704SJoseph Chen signed_data.extend(hasher.digest()) 3862*b6932704SJoseph Chen if not usage: 3863*b6932704SJoseph Chen usage = 'com.google.android.things.vboot' 3864*b6932704SJoseph Chen if is_intermediate_authority: 3865*b6932704SJoseph Chen usage += '.ca' 3866*b6932704SJoseph Chen hasher = hashlib.sha256() 3867*b6932704SJoseph Chen hasher.update(usage.encode('ascii')) 3868*b6932704SJoseph Chen signed_data.extend(hasher.digest()) 3869*b6932704SJoseph Chen if subject_key_version is None: 3870*b6932704SJoseph Chen subject_key_version = int(time.time()) 3871*b6932704SJoseph Chen signed_data.extend(struct.pack('<Q', subject_key_version)) 3872*b6932704SJoseph Chen signature = b'' 3873*b6932704SJoseph Chen if authority_key_path: 3874*b6932704SJoseph Chen rsa_key = RSAPublicKey(authority_key_path) 3875*b6932704SJoseph Chen algorithm_name = 'SHA512_RSA4096' 3876*b6932704SJoseph Chen signature = rsa_key.sign(algorithm_name, signed_data, signing_helper, 3877*b6932704SJoseph Chen signing_helper_with_files) 3878*b6932704SJoseph Chen output.write(signed_data) 3879*b6932704SJoseph Chen output.write(signature) 3880*b6932704SJoseph Chen 3881*b6932704SJoseph Chen def make_atx_permanent_attributes(self, output, root_authority_key_path, 3882*b6932704SJoseph Chen product_id): 3883*b6932704SJoseph Chen """Implements the 'make_atx_permanent_attributes' command. 3884*b6932704SJoseph Chen 3885*b6932704SJoseph Chen Android Things permanent attributes are designed to be permanent for a 3886*b6932704SJoseph Chen particular product and a hash of these attributes should be fused into 3887*b6932704SJoseph Chen hardware to enforce this. 3888*b6932704SJoseph Chen 3889*b6932704SJoseph Chen Arguments: 3890*b6932704SJoseph Chen output: Attributes will be written to this file on success. 3891*b6932704SJoseph Chen root_authority_key_path: Path to a PEM or DER public key for 3892*b6932704SJoseph Chen the root authority. 3893*b6932704SJoseph Chen product_id: A 16-byte Product ID. 3894*b6932704SJoseph Chen 3895*b6932704SJoseph Chen Raises: 3896*b6932704SJoseph Chen AvbError: If an argument is incorrect. 3897*b6932704SJoseph Chen """ 3898*b6932704SJoseph Chen EXPECTED_PRODUCT_ID_SIZE = 16 # pylint: disable=invalid-name 3899*b6932704SJoseph Chen if len(product_id) != EXPECTED_PRODUCT_ID_SIZE: 3900*b6932704SJoseph Chen raise AvbError('Invalid Product ID length.') 3901*b6932704SJoseph Chen output.write(struct.pack('<I', 1)) # Format Version 3902*b6932704SJoseph Chen output.write(RSAPublicKey(root_authority_key_path).encode()) 3903*b6932704SJoseph Chen output.write(product_id) 3904*b6932704SJoseph Chen 3905*b6932704SJoseph Chen def make_atx_metadata(self, output, intermediate_key_certificate, 3906*b6932704SJoseph Chen product_key_certificate): 3907*b6932704SJoseph Chen """Implements the 'make_atx_metadata' command. 3908*b6932704SJoseph Chen 3909*b6932704SJoseph Chen Android Things metadata are included in vbmeta images to facilitate 3910*b6932704SJoseph Chen verification. The output of this command can be used as the 3911*b6932704SJoseph Chen public_key_metadata argument to other commands. 3912*b6932704SJoseph Chen 3913*b6932704SJoseph Chen Arguments: 3914*b6932704SJoseph Chen output: Metadata will be written to this file on success. 3915*b6932704SJoseph Chen intermediate_key_certificate: A certificate file as output by 3916*b6932704SJoseph Chen make_atx_certificate with 3917*b6932704SJoseph Chen is_intermediate_authority set to true. 3918*b6932704SJoseph Chen product_key_certificate: A certificate file as output by 3919*b6932704SJoseph Chen make_atx_certificate with 3920*b6932704SJoseph Chen is_intermediate_authority set to false. 3921*b6932704SJoseph Chen 3922*b6932704SJoseph Chen Raises: 3923*b6932704SJoseph Chen AvbError: If an argument is incorrect. 3924*b6932704SJoseph Chen """ 3925*b6932704SJoseph Chen EXPECTED_CERTIFICATE_SIZE = 1620 # pylint: disable=invalid-name 3926*b6932704SJoseph Chen if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE: 3927*b6932704SJoseph Chen raise AvbError('Invalid intermediate key certificate length.') 3928*b6932704SJoseph Chen if len(product_key_certificate) != EXPECTED_CERTIFICATE_SIZE: 3929*b6932704SJoseph Chen raise AvbError('Invalid product key certificate length.') 3930*b6932704SJoseph Chen output.write(struct.pack('<I', 1)) # Format Version 3931*b6932704SJoseph Chen output.write(intermediate_key_certificate) 3932*b6932704SJoseph Chen output.write(product_key_certificate) 3933*b6932704SJoseph Chen 3934*b6932704SJoseph Chen def make_atx_unlock_credential(self, output, intermediate_key_certificate, 3935*b6932704SJoseph Chen unlock_key_certificate, challenge_path, 3936*b6932704SJoseph Chen unlock_key_path, signing_helper, 3937*b6932704SJoseph Chen signing_helper_with_files): 3938*b6932704SJoseph Chen """Implements the 'make_atx_unlock_credential' command. 3939*b6932704SJoseph Chen 3940*b6932704SJoseph Chen Android Things unlock credentials can be used to authorize the unlock of AVB 3941*b6932704SJoseph Chen on a device. These credentials are presented to an Android Things bootloader 3942*b6932704SJoseph Chen via the fastboot interface in response to a 16-byte challenge. This method 3943*b6932704SJoseph Chen creates all fields of the credential except the challenge signature field 3944*b6932704SJoseph Chen (which is the last field) and can optionally create the challenge signature 3945*b6932704SJoseph Chen field as well if a challenge and the unlock_key_path is provided. 3946*b6932704SJoseph Chen 3947*b6932704SJoseph Chen Arguments: 3948*b6932704SJoseph Chen output: The credential will be written to this file on success. 3949*b6932704SJoseph Chen intermediate_key_certificate: A certificate file as output by 3950*b6932704SJoseph Chen make_atx_certificate with 3951*b6932704SJoseph Chen is_intermediate_authority set to true. 3952*b6932704SJoseph Chen unlock_key_certificate: A certificate file as output by 3953*b6932704SJoseph Chen make_atx_certificate with 3954*b6932704SJoseph Chen is_intermediate_authority set to false and the 3955*b6932704SJoseph Chen usage set to 3956*b6932704SJoseph Chen 'com.google.android.things.vboot.unlock'. 3957*b6932704SJoseph Chen challenge_path: [optional] A path to the challenge to sign. 3958*b6932704SJoseph Chen unlock_key_path: [optional] A PEM file path with the unlock private key. 3959*b6932704SJoseph Chen signing_helper: Program which signs a hash and returns the signature. 3960*b6932704SJoseph Chen signing_helper_with_files: Same as signing_helper but uses files instead. 3961*b6932704SJoseph Chen 3962*b6932704SJoseph Chen Raises: 3963*b6932704SJoseph Chen AvbError: If an argument is incorrect or an error occurs during signing. 3964*b6932704SJoseph Chen """ 3965*b6932704SJoseph Chen EXPECTED_CERTIFICATE_SIZE = 1620 # pylint: disable=invalid-name 3966*b6932704SJoseph Chen EXPECTED_CHALLENGE_SIZE = 16 # pylint: disable=invalid-name 3967*b6932704SJoseph Chen if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE: 3968*b6932704SJoseph Chen raise AvbError('Invalid intermediate key certificate length.') 3969*b6932704SJoseph Chen if len(unlock_key_certificate) != EXPECTED_CERTIFICATE_SIZE: 3970*b6932704SJoseph Chen raise AvbError('Invalid product key certificate length.') 3971*b6932704SJoseph Chen challenge = b'' 3972*b6932704SJoseph Chen if challenge_path: 3973*b6932704SJoseph Chen with open(challenge_path, 'rb') as f: 3974*b6932704SJoseph Chen challenge = f.read() 3975*b6932704SJoseph Chen if len(challenge) != EXPECTED_CHALLENGE_SIZE: 3976*b6932704SJoseph Chen raise AvbError('Invalid unlock challenge length.') 3977*b6932704SJoseph Chen output.write(struct.pack('<I', 1)) # Format Version 3978*b6932704SJoseph Chen output.write(intermediate_key_certificate) 3979*b6932704SJoseph Chen output.write(unlock_key_certificate) 3980*b6932704SJoseph Chen if challenge_path and unlock_key_path: 3981*b6932704SJoseph Chen rsa_key = RSAPublicKey(unlock_key_path) 3982*b6932704SJoseph Chen algorithm_name = 'SHA512_RSA4096' 3983*b6932704SJoseph Chen signature = rsa_key.sign(algorithm_name, challenge, signing_helper, 3984*b6932704SJoseph Chen signing_helper_with_files) 3985*b6932704SJoseph Chen output.write(signature) 3986*b6932704SJoseph Chen 3987*b6932704SJoseph Chen 3988*b6932704SJoseph Chendef calc_hash_level_offsets(image_size, block_size, digest_size): 3989*b6932704SJoseph Chen """Calculate the offsets of all the hash-levels in a Merkle-tree. 3990*b6932704SJoseph Chen 3991*b6932704SJoseph Chen Arguments: 3992*b6932704SJoseph Chen image_size: The size of the image to calculate a Merkle-tree for. 3993*b6932704SJoseph Chen block_size: The block size, e.g. 4096. 3994*b6932704SJoseph Chen digest_size: The size of each hash, e.g. 32 for SHA-256. 3995*b6932704SJoseph Chen 3996*b6932704SJoseph Chen Returns: 3997*b6932704SJoseph Chen A tuple where the first argument is an array of offsets and the 3998*b6932704SJoseph Chen second is size of the tree, in bytes. 3999*b6932704SJoseph Chen """ 4000*b6932704SJoseph Chen level_offsets = [] 4001*b6932704SJoseph Chen level_sizes = [] 4002*b6932704SJoseph Chen tree_size = 0 4003*b6932704SJoseph Chen 4004*b6932704SJoseph Chen num_levels = 0 4005*b6932704SJoseph Chen size = image_size 4006*b6932704SJoseph Chen while size > block_size: 4007*b6932704SJoseph Chen num_blocks = (size + block_size - 1) // block_size 4008*b6932704SJoseph Chen level_size = round_to_multiple(num_blocks * digest_size, block_size) 4009*b6932704SJoseph Chen 4010*b6932704SJoseph Chen level_sizes.append(level_size) 4011*b6932704SJoseph Chen tree_size += level_size 4012*b6932704SJoseph Chen num_levels += 1 4013*b6932704SJoseph Chen 4014*b6932704SJoseph Chen size = level_size 4015*b6932704SJoseph Chen 4016*b6932704SJoseph Chen for n in range(0, num_levels): 4017*b6932704SJoseph Chen offset = 0 4018*b6932704SJoseph Chen for m in range(n + 1, num_levels): 4019*b6932704SJoseph Chen offset += level_sizes[m] 4020*b6932704SJoseph Chen level_offsets.append(offset) 4021*b6932704SJoseph Chen 4022*b6932704SJoseph Chen return level_offsets, tree_size 4023*b6932704SJoseph Chen 4024*b6932704SJoseph Chen 4025*b6932704SJoseph Chen# See system/extras/libfec/include/fec/io.h for these definitions. 4026*b6932704SJoseph ChenFEC_FOOTER_FORMAT = '<LLLLLQ32s' 4027*b6932704SJoseph ChenFEC_MAGIC = 0xfecfecfe 4028*b6932704SJoseph Chen 4029*b6932704SJoseph Chen 4030*b6932704SJoseph Chendef calc_fec_data_size(image_size, num_roots): 4031*b6932704SJoseph Chen """Calculates how much space FEC data will take. 4032*b6932704SJoseph Chen 4033*b6932704SJoseph Chen Arguments: 4034*b6932704SJoseph Chen image_size: The size of the image. 4035*b6932704SJoseph Chen num_roots: Number of roots. 4036*b6932704SJoseph Chen 4037*b6932704SJoseph Chen Returns: 4038*b6932704SJoseph Chen The number of bytes needed for FEC for an image of the given size 4039*b6932704SJoseph Chen and with the requested number of FEC roots. 4040*b6932704SJoseph Chen 4041*b6932704SJoseph Chen Raises: 4042*b6932704SJoseph Chen ValueError: If output from the 'fec' tool is invalid. 4043*b6932704SJoseph Chen """ 4044*b6932704SJoseph Chen p = subprocess.Popen( 4045*b6932704SJoseph Chen ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)], 4046*b6932704SJoseph Chen stdout=subprocess.PIPE, 4047*b6932704SJoseph Chen stderr=subprocess.PIPE) 4048*b6932704SJoseph Chen (pout, perr) = p.communicate() 4049*b6932704SJoseph Chen retcode = p.wait() 4050*b6932704SJoseph Chen if retcode != 0: 4051*b6932704SJoseph Chen raise ValueError('Error invoking fec: {}'.format(perr)) 4052*b6932704SJoseph Chen return int(pout) 4053*b6932704SJoseph Chen 4054*b6932704SJoseph Chen 4055*b6932704SJoseph Chendef generate_fec_data(image_filename, num_roots): 4056*b6932704SJoseph Chen """Generate FEC codes for an image. 4057*b6932704SJoseph Chen 4058*b6932704SJoseph Chen Arguments: 4059*b6932704SJoseph Chen image_filename: The filename of the image. 4060*b6932704SJoseph Chen num_roots: Number of roots. 4061*b6932704SJoseph Chen 4062*b6932704SJoseph Chen Returns: 4063*b6932704SJoseph Chen The FEC data blob as bytes. 4064*b6932704SJoseph Chen 4065*b6932704SJoseph Chen Raises: 4066*b6932704SJoseph Chen ValueError: If calling the 'fec' tool failed or the output is invalid. 4067*b6932704SJoseph Chen """ 4068*b6932704SJoseph Chen with tempfile.NamedTemporaryFile() as fec_tmpfile: 4069*b6932704SJoseph Chen try: 4070*b6932704SJoseph Chen subprocess.check_call( 4071*b6932704SJoseph Chen ['fec', '--encode', '--roots', str(num_roots), image_filename, 4072*b6932704SJoseph Chen fec_tmpfile.name], 4073*b6932704SJoseph Chen stderr=open(os.devnull, 'wb')) 4074*b6932704SJoseph Chen except subprocess.CalledProcessError as e: 4075*b6932704SJoseph Chen raise ValueError('Execution of \'fec\' tool failed: {}.' 4076*b6932704SJoseph Chen .format(e)) from e 4077*b6932704SJoseph Chen fec_data = fec_tmpfile.read() 4078*b6932704SJoseph Chen 4079*b6932704SJoseph Chen footer_size = struct.calcsize(FEC_FOOTER_FORMAT) 4080*b6932704SJoseph Chen footer_data = fec_data[-footer_size:] 4081*b6932704SJoseph Chen (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT, 4082*b6932704SJoseph Chen footer_data) 4083*b6932704SJoseph Chen if magic != FEC_MAGIC: 4084*b6932704SJoseph Chen raise ValueError('Unexpected magic in FEC footer') 4085*b6932704SJoseph Chen return fec_data[0:fec_size] 4086*b6932704SJoseph Chen 4087*b6932704SJoseph Chen 4088*b6932704SJoseph Chendef generate_hash_tree(image, image_size, block_size, hash_alg_name, salt, 4089*b6932704SJoseph Chen digest_padding, hash_level_offsets, tree_size): 4090*b6932704SJoseph Chen """Generates a Merkle-tree for a file. 4091*b6932704SJoseph Chen 4092*b6932704SJoseph Chen Arguments: 4093*b6932704SJoseph Chen image: The image, as a file. 4094*b6932704SJoseph Chen image_size: The size of the image. 4095*b6932704SJoseph Chen block_size: The block size, e.g. 4096. 4096*b6932704SJoseph Chen hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'. 4097*b6932704SJoseph Chen salt: The salt to use. 4098*b6932704SJoseph Chen digest_padding: The padding for each digest. 4099*b6932704SJoseph Chen hash_level_offsets: The offsets from calc_hash_level_offsets(). 4100*b6932704SJoseph Chen tree_size: The size of the tree, in number of bytes. 4101*b6932704SJoseph Chen 4102*b6932704SJoseph Chen Returns: 4103*b6932704SJoseph Chen A tuple where the first element is the top-level hash as bytes and the 4104*b6932704SJoseph Chen second element is the hash-tree as bytes. 4105*b6932704SJoseph Chen """ 4106*b6932704SJoseph Chen hash_ret = bytearray(tree_size) 4107*b6932704SJoseph Chen hash_src_offset = 0 4108*b6932704SJoseph Chen hash_src_size = image_size 4109*b6932704SJoseph Chen level_num = 0 4110*b6932704SJoseph Chen 4111*b6932704SJoseph Chen # If there is only one block, returns the top-level hash directly. 4112*b6932704SJoseph Chen if hash_src_size == block_size: 4113*b6932704SJoseph Chen hasher = create_avb_hashtree_hasher(hash_alg_name, salt) 4114*b6932704SJoseph Chen image.seek(0) 4115*b6932704SJoseph Chen hasher.update(image.read(block_size)) 4116*b6932704SJoseph Chen return hasher.digest(), bytes(hash_ret) 4117*b6932704SJoseph Chen 4118*b6932704SJoseph Chen while hash_src_size > block_size: 4119*b6932704SJoseph Chen level_output_list = [] 4120*b6932704SJoseph Chen remaining = hash_src_size 4121*b6932704SJoseph Chen while remaining > 0: 4122*b6932704SJoseph Chen hasher = create_avb_hashtree_hasher(hash_alg_name, salt) 4123*b6932704SJoseph Chen # Only read from the file for the first level - for subsequent 4124*b6932704SJoseph Chen # levels, access the array we're building. 4125*b6932704SJoseph Chen if level_num == 0: 4126*b6932704SJoseph Chen image.seek(hash_src_offset + hash_src_size - remaining) 4127*b6932704SJoseph Chen data = image.read(min(remaining, block_size)) 4128*b6932704SJoseph Chen else: 4129*b6932704SJoseph Chen offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining 4130*b6932704SJoseph Chen data = hash_ret[offset:offset + block_size] 4131*b6932704SJoseph Chen hasher.update(data) 4132*b6932704SJoseph Chen 4133*b6932704SJoseph Chen remaining -= len(data) 4134*b6932704SJoseph Chen if len(data) < block_size: 4135*b6932704SJoseph Chen hasher.update(b'\0' * (block_size - len(data))) 4136*b6932704SJoseph Chen level_output_list.append(hasher.digest()) 4137*b6932704SJoseph Chen if digest_padding > 0: 4138*b6932704SJoseph Chen level_output_list.append(b'\0' * digest_padding) 4139*b6932704SJoseph Chen 4140*b6932704SJoseph Chen level_output = b''.join(level_output_list) 4141*b6932704SJoseph Chen 4142*b6932704SJoseph Chen padding_needed = (round_to_multiple( 4143*b6932704SJoseph Chen len(level_output), block_size) - len(level_output)) 4144*b6932704SJoseph Chen level_output += b'\0' * padding_needed 4145*b6932704SJoseph Chen 4146*b6932704SJoseph Chen # Copy level-output into resulting tree. 4147*b6932704SJoseph Chen offset = hash_level_offsets[level_num] 4148*b6932704SJoseph Chen hash_ret[offset:offset + len(level_output)] = level_output 4149*b6932704SJoseph Chen 4150*b6932704SJoseph Chen # Continue on to the next level. 4151*b6932704SJoseph Chen hash_src_size = len(level_output) 4152*b6932704SJoseph Chen level_num += 1 4153*b6932704SJoseph Chen 4154*b6932704SJoseph Chen hasher = create_avb_hashtree_hasher(hash_alg_name, salt) 4155*b6932704SJoseph Chen hasher.update(level_output) 4156*b6932704SJoseph Chen return hasher.digest(), bytes(hash_ret) 4157*b6932704SJoseph Chen 4158*b6932704SJoseph Chen 4159*b6932704SJoseph Chenclass AvbTool(object): 4160*b6932704SJoseph Chen """Object for avbtool command-line tool.""" 4161*b6932704SJoseph Chen 4162*b6932704SJoseph Chen def __init__(self): 4163*b6932704SJoseph Chen """Initializer method.""" 4164*b6932704SJoseph Chen self.avb = Avb() 4165*b6932704SJoseph Chen 4166*b6932704SJoseph Chen def _add_common_args(self, sub_parser): 4167*b6932704SJoseph Chen """Adds arguments used by several sub-commands. 4168*b6932704SJoseph Chen 4169*b6932704SJoseph Chen Arguments: 4170*b6932704SJoseph Chen sub_parser: The parser to add arguments to. 4171*b6932704SJoseph Chen """ 4172*b6932704SJoseph Chen sub_parser.add_argument('--algorithm', 4173*b6932704SJoseph Chen help='Algorithm to use (default: NONE)', 4174*b6932704SJoseph Chen metavar='ALGORITHM', 4175*b6932704SJoseph Chen default='NONE') 4176*b6932704SJoseph Chen sub_parser.add_argument('--key', 4177*b6932704SJoseph Chen help='Path to RSA private key file', 4178*b6932704SJoseph Chen metavar='KEY', 4179*b6932704SJoseph Chen required=False) 4180*b6932704SJoseph Chen sub_parser.add_argument('--signing_helper', 4181*b6932704SJoseph Chen help='Path to helper used for signing', 4182*b6932704SJoseph Chen metavar='APP', 4183*b6932704SJoseph Chen default=None, 4184*b6932704SJoseph Chen required=False) 4185*b6932704SJoseph Chen sub_parser.add_argument('--signing_helper_with_files', 4186*b6932704SJoseph Chen help='Path to helper used for signing using files', 4187*b6932704SJoseph Chen metavar='APP', 4188*b6932704SJoseph Chen default=None, 4189*b6932704SJoseph Chen required=False) 4190*b6932704SJoseph Chen sub_parser.add_argument('--public_key_metadata', 4191*b6932704SJoseph Chen help='Path to public key metadata file', 4192*b6932704SJoseph Chen metavar='KEY_METADATA', 4193*b6932704SJoseph Chen required=False) 4194*b6932704SJoseph Chen sub_parser.add_argument('--rollback_index', 4195*b6932704SJoseph Chen help='Rollback Index', 4196*b6932704SJoseph Chen type=parse_number, 4197*b6932704SJoseph Chen default=0) 4198*b6932704SJoseph Chen sub_parser.add_argument('--rollback_index_location', 4199*b6932704SJoseph Chen help='Location of main vbmeta Rollback Index', 4200*b6932704SJoseph Chen type=parse_number, 4201*b6932704SJoseph Chen default=0) 4202*b6932704SJoseph Chen # This is used internally for unit tests. Do not include in --help output. 4203*b6932704SJoseph Chen sub_parser.add_argument('--internal_release_string', 4204*b6932704SJoseph Chen help=argparse.SUPPRESS) 4205*b6932704SJoseph Chen sub_parser.add_argument('--append_to_release_string', 4206*b6932704SJoseph Chen help='Text to append to release string', 4207*b6932704SJoseph Chen metavar='STR') 4208*b6932704SJoseph Chen sub_parser.add_argument('--prop', 4209*b6932704SJoseph Chen help='Add property', 4210*b6932704SJoseph Chen metavar='KEY:VALUE', 4211*b6932704SJoseph Chen action='append') 4212*b6932704SJoseph Chen sub_parser.add_argument('--prop_from_file', 4213*b6932704SJoseph Chen help='Add property from file', 4214*b6932704SJoseph Chen metavar='KEY:PATH', 4215*b6932704SJoseph Chen action='append') 4216*b6932704SJoseph Chen sub_parser.add_argument('--kernel_cmdline', 4217*b6932704SJoseph Chen help='Add kernel cmdline', 4218*b6932704SJoseph Chen metavar='CMDLINE', 4219*b6932704SJoseph Chen action='append') 4220*b6932704SJoseph Chen # TODO(zeuthen): the --setup_rootfs_from_kernel option used to be called 4221*b6932704SJoseph Chen # --generate_dm_verity_cmdline_from_hashtree. Remove support for the latter 4222*b6932704SJoseph Chen # at some future point. 4223*b6932704SJoseph Chen sub_parser.add_argument('--setup_rootfs_from_kernel', 4224*b6932704SJoseph Chen '--generate_dm_verity_cmdline_from_hashtree', 4225*b6932704SJoseph Chen metavar='IMAGE', 4226*b6932704SJoseph Chen help='Adds kernel cmdline to set up IMAGE', 4227*b6932704SJoseph Chen type=argparse.FileType('rb')) 4228*b6932704SJoseph Chen sub_parser.add_argument('--include_descriptors_from_image', 4229*b6932704SJoseph Chen help='Include descriptors from image', 4230*b6932704SJoseph Chen metavar='IMAGE', 4231*b6932704SJoseph Chen action='append', 4232*b6932704SJoseph Chen type=argparse.FileType('rb')) 4233*b6932704SJoseph Chen sub_parser.add_argument('--print_required_libavb_version', 4234*b6932704SJoseph Chen help=('Don\'t store the footer - ' 4235*b6932704SJoseph Chen 'instead calculate the required libavb ' 4236*b6932704SJoseph Chen 'version for the given options.'), 4237*b6932704SJoseph Chen action='store_true') 4238*b6932704SJoseph Chen # These are only allowed from top-level vbmeta and boot-in-lieu-of-vbmeta. 4239*b6932704SJoseph Chen sub_parser.add_argument('--chain_partition', 4240*b6932704SJoseph Chen help='Allow signed integrity-data for partition', 4241*b6932704SJoseph Chen metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH', 4242*b6932704SJoseph Chen action='append') 4243*b6932704SJoseph Chen sub_parser.add_argument('--flags', 4244*b6932704SJoseph Chen help='VBMeta flags', 4245*b6932704SJoseph Chen type=parse_number, 4246*b6932704SJoseph Chen default=0) 4247*b6932704SJoseph Chen sub_parser.add_argument('--set_hashtree_disabled_flag', 4248*b6932704SJoseph Chen help='Set the HASHTREE_DISABLED flag', 4249*b6932704SJoseph Chen action='store_true') 4250*b6932704SJoseph Chen 4251*b6932704SJoseph Chen def _add_common_footer_args(self, sub_parser): 4252*b6932704SJoseph Chen """Adds arguments used by add_*_footer sub-commands. 4253*b6932704SJoseph Chen 4254*b6932704SJoseph Chen Arguments: 4255*b6932704SJoseph Chen sub_parser: The parser to add arguments to. 4256*b6932704SJoseph Chen """ 4257*b6932704SJoseph Chen sub_parser.add_argument('--use_persistent_digest', 4258*b6932704SJoseph Chen help='Use a persistent digest on device instead of ' 4259*b6932704SJoseph Chen 'storing the digest in the descriptor. This ' 4260*b6932704SJoseph Chen 'cannot be used with A/B so must be combined ' 4261*b6932704SJoseph Chen 'with --do_not_use_ab when an A/B suffix is ' 4262*b6932704SJoseph Chen 'expected at runtime.', 4263*b6932704SJoseph Chen action='store_true') 4264*b6932704SJoseph Chen sub_parser.add_argument('--do_not_use_ab', 4265*b6932704SJoseph Chen help='The partition does not use A/B even when an ' 4266*b6932704SJoseph Chen 'A/B suffix is present. This must not be used ' 4267*b6932704SJoseph Chen 'for vbmeta or chained partitions.', 4268*b6932704SJoseph Chen action='store_true') 4269*b6932704SJoseph Chen 4270*b6932704SJoseph Chen def _fixup_common_args(self, args): 4271*b6932704SJoseph Chen """Common fixups needed by subcommands. 4272*b6932704SJoseph Chen 4273*b6932704SJoseph Chen Arguments: 4274*b6932704SJoseph Chen args: Arguments to modify. 4275*b6932704SJoseph Chen 4276*b6932704SJoseph Chen Returns: 4277*b6932704SJoseph Chen The modified arguments. 4278*b6932704SJoseph Chen """ 4279*b6932704SJoseph Chen if args.set_hashtree_disabled_flag: 4280*b6932704SJoseph Chen args.flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED 4281*b6932704SJoseph Chen return args 4282*b6932704SJoseph Chen 4283*b6932704SJoseph Chen def run(self, argv): 4284*b6932704SJoseph Chen """Command-line processor. 4285*b6932704SJoseph Chen 4286*b6932704SJoseph Chen Arguments: 4287*b6932704SJoseph Chen argv: Pass sys.argv from main. 4288*b6932704SJoseph Chen """ 4289*b6932704SJoseph Chen parser = argparse.ArgumentParser() 4290*b6932704SJoseph Chen subparsers = parser.add_subparsers(title='subcommands') 4291*b6932704SJoseph Chen 4292*b6932704SJoseph Chen sub_parser = subparsers.add_parser( 4293*b6932704SJoseph Chen 'generate_test_image', 4294*b6932704SJoseph Chen help=('Generates a test image with a known pattern for testing: ' 4295*b6932704SJoseph Chen '0x00 0x01 0x02 ... 0xff 0x00 0x01 ...')) 4296*b6932704SJoseph Chen sub_parser.add_argument('--image_size', 4297*b6932704SJoseph Chen help='Size of image to generate.', 4298*b6932704SJoseph Chen type=parse_number, 4299*b6932704SJoseph Chen required=True) 4300*b6932704SJoseph Chen sub_parser.add_argument('--start_byte', 4301*b6932704SJoseph Chen help='Integer for the start byte of the pattern.', 4302*b6932704SJoseph Chen type=parse_number, 4303*b6932704SJoseph Chen default=0) 4304*b6932704SJoseph Chen sub_parser.add_argument('--output', 4305*b6932704SJoseph Chen help='Output file name.', 4306*b6932704SJoseph Chen type=argparse.FileType('wb'), 4307*b6932704SJoseph Chen default=sys.stdout) 4308*b6932704SJoseph Chen sub_parser.set_defaults(func=self.generate_test_image) 4309*b6932704SJoseph Chen 4310*b6932704SJoseph Chen sub_parser = subparsers.add_parser('version', 4311*b6932704SJoseph Chen help='Prints version of avbtool.') 4312*b6932704SJoseph Chen sub_parser.set_defaults(func=self.version) 4313*b6932704SJoseph Chen 4314*b6932704SJoseph Chen sub_parser = subparsers.add_parser('extract_public_key', 4315*b6932704SJoseph Chen help='Extract public key.') 4316*b6932704SJoseph Chen sub_parser.add_argument('--key', 4317*b6932704SJoseph Chen help='Path to RSA private key file', 4318*b6932704SJoseph Chen required=True) 4319*b6932704SJoseph Chen sub_parser.add_argument('--output', 4320*b6932704SJoseph Chen help='Output file name', 4321*b6932704SJoseph Chen type=argparse.FileType('wb'), 4322*b6932704SJoseph Chen required=True) 4323*b6932704SJoseph Chen sub_parser.set_defaults(func=self.extract_public_key) 4324*b6932704SJoseph Chen 4325*b6932704SJoseph Chen sub_parser = subparsers.add_parser('make_vbmeta_image', 4326*b6932704SJoseph Chen help='Makes a vbmeta image.') 4327*b6932704SJoseph Chen sub_parser.add_argument('--output', 4328*b6932704SJoseph Chen help='Output file name', 4329*b6932704SJoseph Chen type=argparse.FileType('wb')) 4330*b6932704SJoseph Chen sub_parser.add_argument('--padding_size', 4331*b6932704SJoseph Chen metavar='NUMBER', 4332*b6932704SJoseph Chen help='If non-zero, pads output with NUL bytes so ' 4333*b6932704SJoseph Chen 'its size is a multiple of NUMBER ' 4334*b6932704SJoseph Chen '(default: 0)', 4335*b6932704SJoseph Chen type=parse_number, 4336*b6932704SJoseph Chen default=0) 4337*b6932704SJoseph Chen self._add_common_args(sub_parser) 4338*b6932704SJoseph Chen sub_parser.set_defaults(func=self.make_vbmeta_image) 4339*b6932704SJoseph Chen 4340*b6932704SJoseph Chen sub_parser = subparsers.add_parser('add_hash_footer', 4341*b6932704SJoseph Chen help='Add hashes and footer to image.') 4342*b6932704SJoseph Chen sub_parser.add_argument('--image', 4343*b6932704SJoseph Chen help='Image to add hashes to', 4344*b6932704SJoseph Chen type=argparse.FileType('rb+')) 4345*b6932704SJoseph Chen sub_parser.add_argument('--partition_size', 4346*b6932704SJoseph Chen help='Partition size', 4347*b6932704SJoseph Chen type=parse_number) 4348*b6932704SJoseph Chen sub_parser.add_argument('--dynamic_partition_size', 4349*b6932704SJoseph Chen help='Calculate partition size based on image size', 4350*b6932704SJoseph Chen action='store_true') 4351*b6932704SJoseph Chen sub_parser.add_argument('--partition_name', 4352*b6932704SJoseph Chen help='Partition name', 4353*b6932704SJoseph Chen default=None) 4354*b6932704SJoseph Chen sub_parser.add_argument('--hash_algorithm', 4355*b6932704SJoseph Chen help='Hash algorithm to use (default: sha256)', 4356*b6932704SJoseph Chen default='sha256') 4357*b6932704SJoseph Chen sub_parser.add_argument('--salt', 4358*b6932704SJoseph Chen help='Salt in hex (default: /dev/urandom)') 4359*b6932704SJoseph Chen sub_parser.add_argument('--calc_max_image_size', 4360*b6932704SJoseph Chen help=('Don\'t store the footer - ' 4361*b6932704SJoseph Chen 'instead calculate the maximum image size ' 4362*b6932704SJoseph Chen 'leaving enough room for metadata with ' 4363*b6932704SJoseph Chen 'the given partition size.'), 4364*b6932704SJoseph Chen action='store_true') 4365*b6932704SJoseph Chen sub_parser.add_argument('--output_vbmeta_image', 4366*b6932704SJoseph Chen help='Also write vbmeta struct to file', 4367*b6932704SJoseph Chen type=argparse.FileType('wb')) 4368*b6932704SJoseph Chen sub_parser.add_argument('--do_not_append_vbmeta_image', 4369*b6932704SJoseph Chen help=('Do not append vbmeta struct or footer ' 4370*b6932704SJoseph Chen 'to the image'), 4371*b6932704SJoseph Chen action='store_true') 4372*b6932704SJoseph Chen self._add_common_args(sub_parser) 4373*b6932704SJoseph Chen self._add_common_footer_args(sub_parser) 4374*b6932704SJoseph Chen sub_parser.set_defaults(func=self.add_hash_footer) 4375*b6932704SJoseph Chen 4376*b6932704SJoseph Chen sub_parser = subparsers.add_parser('append_vbmeta_image', 4377*b6932704SJoseph Chen help='Append vbmeta image to image.') 4378*b6932704SJoseph Chen sub_parser.add_argument('--image', 4379*b6932704SJoseph Chen help='Image to append vbmeta blob to', 4380*b6932704SJoseph Chen type=argparse.FileType('rb+')) 4381*b6932704SJoseph Chen sub_parser.add_argument('--partition_size', 4382*b6932704SJoseph Chen help='Partition size', 4383*b6932704SJoseph Chen type=parse_number, 4384*b6932704SJoseph Chen required=True) 4385*b6932704SJoseph Chen sub_parser.add_argument('--vbmeta_image', 4386*b6932704SJoseph Chen help='Image with vbmeta blob to append', 4387*b6932704SJoseph Chen type=argparse.FileType('rb')) 4388*b6932704SJoseph Chen sub_parser.set_defaults(func=self.append_vbmeta_image) 4389*b6932704SJoseph Chen 4390*b6932704SJoseph Chen sub_parser = subparsers.add_parser( 4391*b6932704SJoseph Chen 'add_hashtree_footer', 4392*b6932704SJoseph Chen help='Add hashtree and footer to image.') 4393*b6932704SJoseph Chen sub_parser.add_argument('--image', 4394*b6932704SJoseph Chen help='Image to add hashtree to', 4395*b6932704SJoseph Chen type=argparse.FileType('rb+')) 4396*b6932704SJoseph Chen sub_parser.add_argument('--partition_size', 4397*b6932704SJoseph Chen help='Partition size', 4398*b6932704SJoseph Chen default=0, 4399*b6932704SJoseph Chen type=parse_number) 4400*b6932704SJoseph Chen sub_parser.add_argument('--partition_name', 4401*b6932704SJoseph Chen help='Partition name', 4402*b6932704SJoseph Chen default='') 4403*b6932704SJoseph Chen sub_parser.add_argument('--hash_algorithm', 4404*b6932704SJoseph Chen help='Hash algorithm to use (default: sha1)', 4405*b6932704SJoseph Chen default='sha1') 4406*b6932704SJoseph Chen sub_parser.add_argument('--salt', 4407*b6932704SJoseph Chen help='Salt in hex (default: /dev/urandom)') 4408*b6932704SJoseph Chen sub_parser.add_argument('--block_size', 4409*b6932704SJoseph Chen help='Block size (default: 4096)', 4410*b6932704SJoseph Chen type=parse_number, 4411*b6932704SJoseph Chen default=4096) 4412*b6932704SJoseph Chen # TODO(zeuthen): The --generate_fec option was removed when we 4413*b6932704SJoseph Chen # moved to generating FEC by default. To avoid breaking existing 4414*b6932704SJoseph Chen # users needing to transition we simply just print a warning below 4415*b6932704SJoseph Chen # in add_hashtree_footer(). Remove this option and the warning at 4416*b6932704SJoseph Chen # some point in the future. 4417*b6932704SJoseph Chen sub_parser.add_argument('--generate_fec', 4418*b6932704SJoseph Chen help=argparse.SUPPRESS, 4419*b6932704SJoseph Chen action='store_true') 4420*b6932704SJoseph Chen sub_parser.add_argument( 4421*b6932704SJoseph Chen '--do_not_generate_fec', 4422*b6932704SJoseph Chen help='Do not generate forward-error-correction codes', 4423*b6932704SJoseph Chen action='store_true') 4424*b6932704SJoseph Chen sub_parser.add_argument('--fec_num_roots', 4425*b6932704SJoseph Chen help='Number of roots for FEC (default: 2)', 4426*b6932704SJoseph Chen type=parse_number, 4427*b6932704SJoseph Chen default=2) 4428*b6932704SJoseph Chen sub_parser.add_argument('--calc_max_image_size', 4429*b6932704SJoseph Chen help=('Don\'t store the hashtree or footer - ' 4430*b6932704SJoseph Chen 'instead calculate the maximum image size ' 4431*b6932704SJoseph Chen 'leaving enough room for hashtree ' 4432*b6932704SJoseph Chen 'and metadata with the given partition ' 4433*b6932704SJoseph Chen 'size.'), 4434*b6932704SJoseph Chen action='store_true') 4435*b6932704SJoseph Chen sub_parser.add_argument('--output_vbmeta_image', 4436*b6932704SJoseph Chen help='Also write vbmeta struct to file', 4437*b6932704SJoseph Chen type=argparse.FileType('wb')) 4438*b6932704SJoseph Chen sub_parser.add_argument('--do_not_append_vbmeta_image', 4439*b6932704SJoseph Chen help=('Do not append vbmeta struct or footer ' 4440*b6932704SJoseph Chen 'to the image'), 4441*b6932704SJoseph Chen action='store_true') 4442*b6932704SJoseph Chen # This is different from --setup_rootfs_from_kernel insofar that 4443*b6932704SJoseph Chen # it doesn't take an IMAGE, the generated cmdline will be for the 4444*b6932704SJoseph Chen # hashtree we're adding. 4445*b6932704SJoseph Chen sub_parser.add_argument('--setup_as_rootfs_from_kernel', 4446*b6932704SJoseph Chen action='store_true', 4447*b6932704SJoseph Chen help='Adds kernel cmdline for setting up rootfs') 4448*b6932704SJoseph Chen sub_parser.add_argument('--no_hashtree', 4449*b6932704SJoseph Chen action='store_true', 4450*b6932704SJoseph Chen help='Do not append hashtree') 4451*b6932704SJoseph Chen sub_parser.add_argument('--check_at_most_once', 4452*b6932704SJoseph Chen action='store_true', 4453*b6932704SJoseph Chen help='Set to verify data block only once') 4454*b6932704SJoseph Chen self._add_common_args(sub_parser) 4455*b6932704SJoseph Chen self._add_common_footer_args(sub_parser) 4456*b6932704SJoseph Chen sub_parser.set_defaults(func=self.add_hashtree_footer) 4457*b6932704SJoseph Chen 4458*b6932704SJoseph Chen sub_parser = subparsers.add_parser('erase_footer', 4459*b6932704SJoseph Chen help='Erase footer from an image.') 4460*b6932704SJoseph Chen sub_parser.add_argument('--image', 4461*b6932704SJoseph Chen help='Image with a footer', 4462*b6932704SJoseph Chen type=argparse.FileType('rb+'), 4463*b6932704SJoseph Chen required=True) 4464*b6932704SJoseph Chen sub_parser.add_argument('--keep_hashtree', 4465*b6932704SJoseph Chen help='Keep the hashtree and FEC in the image', 4466*b6932704SJoseph Chen action='store_true') 4467*b6932704SJoseph Chen sub_parser.set_defaults(func=self.erase_footer) 4468*b6932704SJoseph Chen 4469*b6932704SJoseph Chen sub_parser = subparsers.add_parser('zero_hashtree', 4470*b6932704SJoseph Chen help='Zero out hashtree and FEC data.') 4471*b6932704SJoseph Chen sub_parser.add_argument('--image', 4472*b6932704SJoseph Chen help='Image with a footer', 4473*b6932704SJoseph Chen type=argparse.FileType('rb+'), 4474*b6932704SJoseph Chen required=True) 4475*b6932704SJoseph Chen sub_parser.set_defaults(func=self.zero_hashtree) 4476*b6932704SJoseph Chen 4477*b6932704SJoseph Chen sub_parser = subparsers.add_parser( 4478*b6932704SJoseph Chen 'extract_vbmeta_image', 4479*b6932704SJoseph Chen help='Extracts vbmeta from an image with a footer.') 4480*b6932704SJoseph Chen sub_parser.add_argument('--image', 4481*b6932704SJoseph Chen help='Image with footer', 4482*b6932704SJoseph Chen type=argparse.FileType('rb'), 4483*b6932704SJoseph Chen required=True) 4484*b6932704SJoseph Chen sub_parser.add_argument('--output', 4485*b6932704SJoseph Chen help='Output file name', 4486*b6932704SJoseph Chen type=argparse.FileType('wb')) 4487*b6932704SJoseph Chen sub_parser.add_argument('--padding_size', 4488*b6932704SJoseph Chen metavar='NUMBER', 4489*b6932704SJoseph Chen help='If non-zero, pads output with NUL bytes so ' 4490*b6932704SJoseph Chen 'its size is a multiple of NUMBER ' 4491*b6932704SJoseph Chen '(default: 0)', 4492*b6932704SJoseph Chen type=parse_number, 4493*b6932704SJoseph Chen default=0) 4494*b6932704SJoseph Chen sub_parser.set_defaults(func=self.extract_vbmeta_image) 4495*b6932704SJoseph Chen 4496*b6932704SJoseph Chen sub_parser = subparsers.add_parser('resize_image', 4497*b6932704SJoseph Chen help='Resize image with a footer.') 4498*b6932704SJoseph Chen sub_parser.add_argument('--image', 4499*b6932704SJoseph Chen help='Image with a footer', 4500*b6932704SJoseph Chen type=argparse.FileType('rb+'), 4501*b6932704SJoseph Chen required=True) 4502*b6932704SJoseph Chen sub_parser.add_argument('--partition_size', 4503*b6932704SJoseph Chen help='New partition size', 4504*b6932704SJoseph Chen type=parse_number) 4505*b6932704SJoseph Chen sub_parser.set_defaults(func=self.resize_image) 4506*b6932704SJoseph Chen 4507*b6932704SJoseph Chen sub_parser = subparsers.add_parser( 4508*b6932704SJoseph Chen 'info_image', 4509*b6932704SJoseph Chen help='Show information about vbmeta or footer.') 4510*b6932704SJoseph Chen sub_parser.add_argument('--image', 4511*b6932704SJoseph Chen help='Image to show information about', 4512*b6932704SJoseph Chen type=argparse.FileType('rb'), 4513*b6932704SJoseph Chen required=True) 4514*b6932704SJoseph Chen sub_parser.add_argument('--output', 4515*b6932704SJoseph Chen help='Write info to file', 4516*b6932704SJoseph Chen type=argparse.FileType('wt'), 4517*b6932704SJoseph Chen default=sys.stdout) 4518*b6932704SJoseph Chen sub_parser.add_argument('--atx', 4519*b6932704SJoseph Chen help=('Show information about Android Things ' 4520*b6932704SJoseph Chen 'eXtension (ATX).'), 4521*b6932704SJoseph Chen action='store_true') 4522*b6932704SJoseph Chen sub_parser.set_defaults(func=self.info_image) 4523*b6932704SJoseph Chen 4524*b6932704SJoseph Chen sub_parser = subparsers.add_parser( 4525*b6932704SJoseph Chen 'verify_image', 4526*b6932704SJoseph Chen help='Verify an image.') 4527*b6932704SJoseph Chen sub_parser.add_argument('--image', 4528*b6932704SJoseph Chen help='Image to verify', 4529*b6932704SJoseph Chen type=argparse.FileType('rb'), 4530*b6932704SJoseph Chen required=True) 4531*b6932704SJoseph Chen sub_parser.add_argument('--key', 4532*b6932704SJoseph Chen help='Check embedded public key matches KEY', 4533*b6932704SJoseph Chen metavar='KEY', 4534*b6932704SJoseph Chen required=False) 4535*b6932704SJoseph Chen sub_parser.add_argument('--expected_chain_partition', 4536*b6932704SJoseph Chen help='Expected chain partition', 4537*b6932704SJoseph Chen metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH', 4538*b6932704SJoseph Chen action='append') 4539*b6932704SJoseph Chen sub_parser.add_argument( 4540*b6932704SJoseph Chen '--follow_chain_partitions', 4541*b6932704SJoseph Chen help=('Follows chain partitions even when not ' 4542*b6932704SJoseph Chen 'specified with the --expected_chain_partition option'), 4543*b6932704SJoseph Chen action='store_true') 4544*b6932704SJoseph Chen sub_parser.add_argument( 4545*b6932704SJoseph Chen '--accept_zeroed_hashtree', 4546*b6932704SJoseph Chen help=('Accept images where the hashtree or FEC data is zeroed out'), 4547*b6932704SJoseph Chen action='store_true') 4548*b6932704SJoseph Chen sub_parser.set_defaults(func=self.verify_image) 4549*b6932704SJoseph Chen 4550*b6932704SJoseph Chen sub_parser = subparsers.add_parser( 4551*b6932704SJoseph Chen 'print_partition_digests', 4552*b6932704SJoseph Chen help='Prints partition digests.') 4553*b6932704SJoseph Chen sub_parser.add_argument('--image', 4554*b6932704SJoseph Chen help='Image to print partition digests from', 4555*b6932704SJoseph Chen type=argparse.FileType('rb'), 4556*b6932704SJoseph Chen required=True) 4557*b6932704SJoseph Chen sub_parser.add_argument('--output', 4558*b6932704SJoseph Chen help='Write info to file', 4559*b6932704SJoseph Chen type=argparse.FileType('wt'), 4560*b6932704SJoseph Chen default=sys.stdout) 4561*b6932704SJoseph Chen sub_parser.add_argument('--json', 4562*b6932704SJoseph Chen help=('Print output as JSON'), 4563*b6932704SJoseph Chen action='store_true') 4564*b6932704SJoseph Chen sub_parser.set_defaults(func=self.print_partition_digests) 4565*b6932704SJoseph Chen 4566*b6932704SJoseph Chen sub_parser = subparsers.add_parser( 4567*b6932704SJoseph Chen 'calculate_vbmeta_digest', 4568*b6932704SJoseph Chen help='Calculate vbmeta digest.') 4569*b6932704SJoseph Chen sub_parser.add_argument('--image', 4570*b6932704SJoseph Chen help='Image to calculate digest for', 4571*b6932704SJoseph Chen type=argparse.FileType('rb'), 4572*b6932704SJoseph Chen required=True) 4573*b6932704SJoseph Chen sub_parser.add_argument('--hash_algorithm', 4574*b6932704SJoseph Chen help='Hash algorithm to use (default: sha256)', 4575*b6932704SJoseph Chen default='sha256') 4576*b6932704SJoseph Chen sub_parser.add_argument('--output', 4577*b6932704SJoseph Chen help='Write hex digest to file (default: stdout)', 4578*b6932704SJoseph Chen type=argparse.FileType('wt'), 4579*b6932704SJoseph Chen default=sys.stdout) 4580*b6932704SJoseph Chen sub_parser.set_defaults(func=self.calculate_vbmeta_digest) 4581*b6932704SJoseph Chen 4582*b6932704SJoseph Chen sub_parser = subparsers.add_parser( 4583*b6932704SJoseph Chen 'calculate_kernel_cmdline', 4584*b6932704SJoseph Chen help='Calculate kernel cmdline.') 4585*b6932704SJoseph Chen sub_parser.add_argument('--image', 4586*b6932704SJoseph Chen help='Image to calculate kernel cmdline for', 4587*b6932704SJoseph Chen type=argparse.FileType('rb'), 4588*b6932704SJoseph Chen required=True) 4589*b6932704SJoseph Chen sub_parser.add_argument('--hashtree_disabled', 4590*b6932704SJoseph Chen help='Return the cmdline for hashtree disabled', 4591*b6932704SJoseph Chen action='store_true') 4592*b6932704SJoseph Chen sub_parser.add_argument('--output', 4593*b6932704SJoseph Chen help='Write cmdline to file (default: stdout)', 4594*b6932704SJoseph Chen type=argparse.FileType('wt'), 4595*b6932704SJoseph Chen default=sys.stdout) 4596*b6932704SJoseph Chen sub_parser.set_defaults(func=self.calculate_kernel_cmdline) 4597*b6932704SJoseph Chen 4598*b6932704SJoseph Chen sub_parser = subparsers.add_parser('set_ab_metadata', 4599*b6932704SJoseph Chen help='Set A/B metadata.') 4600*b6932704SJoseph Chen sub_parser.add_argument('--misc_image', 4601*b6932704SJoseph Chen help=('The misc image to modify. If the image does ' 4602*b6932704SJoseph Chen 'not exist, it will be created.'), 4603*b6932704SJoseph Chen type=argparse.FileType('r+b'), 4604*b6932704SJoseph Chen required=True) 4605*b6932704SJoseph Chen sub_parser.add_argument('--slot_data', 4606*b6932704SJoseph Chen help=('Slot data of the form "priority", ' 4607*b6932704SJoseph Chen '"tries_remaining", "sucessful_boot" for ' 4608*b6932704SJoseph Chen 'slot A followed by the same for slot B, ' 4609*b6932704SJoseph Chen 'separated by colons. The default value ' 4610*b6932704SJoseph Chen 'is 15:7:0:14:7:0.'), 4611*b6932704SJoseph Chen default='15:7:0:14:7:0') 4612*b6932704SJoseph Chen sub_parser.set_defaults(func=self.set_ab_metadata) 4613*b6932704SJoseph Chen 4614*b6932704SJoseph Chen sub_parser = subparsers.add_parser( 4615*b6932704SJoseph Chen 'make_atx_certificate', 4616*b6932704SJoseph Chen help='Create an Android Things eXtension (ATX) certificate.') 4617*b6932704SJoseph Chen sub_parser.add_argument('--output', 4618*b6932704SJoseph Chen help='Write certificate to file', 4619*b6932704SJoseph Chen type=argparse.FileType('wb'), 4620*b6932704SJoseph Chen default=sys.stdout) 4621*b6932704SJoseph Chen sub_parser.add_argument('--subject', 4622*b6932704SJoseph Chen help=('Path to subject file'), 4623*b6932704SJoseph Chen type=argparse.FileType('rb'), 4624*b6932704SJoseph Chen required=True) 4625*b6932704SJoseph Chen sub_parser.add_argument('--subject_key', 4626*b6932704SJoseph Chen help=('Path to subject RSA public key file'), 4627*b6932704SJoseph Chen type=argparse.FileType('rb'), 4628*b6932704SJoseph Chen required=True) 4629*b6932704SJoseph Chen sub_parser.add_argument('--subject_key_version', 4630*b6932704SJoseph Chen help=('Version of the subject key'), 4631*b6932704SJoseph Chen type=parse_number, 4632*b6932704SJoseph Chen required=False) 4633*b6932704SJoseph Chen sub_parser.add_argument('--subject_is_intermediate_authority', 4634*b6932704SJoseph Chen help=('Generate an intermediate authority ' 4635*b6932704SJoseph Chen 'certificate'), 4636*b6932704SJoseph Chen action='store_true') 4637*b6932704SJoseph Chen sub_parser.add_argument('--usage', 4638*b6932704SJoseph Chen help=('Override usage with a hash of the provided ' 4639*b6932704SJoseph Chen 'string'), 4640*b6932704SJoseph Chen required=False) 4641*b6932704SJoseph Chen sub_parser.add_argument('--authority_key', 4642*b6932704SJoseph Chen help='Path to authority RSA private key file', 4643*b6932704SJoseph Chen required=False) 4644*b6932704SJoseph Chen sub_parser.add_argument('--signing_helper', 4645*b6932704SJoseph Chen help='Path to helper used for signing', 4646*b6932704SJoseph Chen metavar='APP', 4647*b6932704SJoseph Chen default=None, 4648*b6932704SJoseph Chen required=False) 4649*b6932704SJoseph Chen sub_parser.add_argument('--signing_helper_with_files', 4650*b6932704SJoseph Chen help='Path to helper used for signing using files', 4651*b6932704SJoseph Chen metavar='APP', 4652*b6932704SJoseph Chen default=None, 4653*b6932704SJoseph Chen required=False) 4654*b6932704SJoseph Chen sub_parser.set_defaults(func=self.make_atx_certificate) 4655*b6932704SJoseph Chen 4656*b6932704SJoseph Chen sub_parser = subparsers.add_parser( 4657*b6932704SJoseph Chen 'make_atx_permanent_attributes', 4658*b6932704SJoseph Chen help='Create Android Things eXtension (ATX) permanent attributes.') 4659*b6932704SJoseph Chen sub_parser.add_argument('--output', 4660*b6932704SJoseph Chen help='Write attributes to file', 4661*b6932704SJoseph Chen type=argparse.FileType('wb'), 4662*b6932704SJoseph Chen default=sys.stdout) 4663*b6932704SJoseph Chen sub_parser.add_argument('--root_authority_key', 4664*b6932704SJoseph Chen help='Path to authority RSA public key file', 4665*b6932704SJoseph Chen type=argparse.FileType('rb'), 4666*b6932704SJoseph Chen required=True) 4667*b6932704SJoseph Chen sub_parser.add_argument('--product_id', 4668*b6932704SJoseph Chen help=('Path to Product ID file'), 4669*b6932704SJoseph Chen type=argparse.FileType('rb'), 4670*b6932704SJoseph Chen required=True) 4671*b6932704SJoseph Chen sub_parser.set_defaults(func=self.make_atx_permanent_attributes) 4672*b6932704SJoseph Chen 4673*b6932704SJoseph Chen sub_parser = subparsers.add_parser( 4674*b6932704SJoseph Chen 'make_atx_metadata', 4675*b6932704SJoseph Chen help='Create Android Things eXtension (ATX) metadata.') 4676*b6932704SJoseph Chen sub_parser.add_argument('--output', 4677*b6932704SJoseph Chen help='Write metadata to file', 4678*b6932704SJoseph Chen type=argparse.FileType('wb'), 4679*b6932704SJoseph Chen default=sys.stdout) 4680*b6932704SJoseph Chen sub_parser.add_argument('--intermediate_key_certificate', 4681*b6932704SJoseph Chen help='Path to intermediate key certificate file', 4682*b6932704SJoseph Chen type=argparse.FileType('rb'), 4683*b6932704SJoseph Chen required=True) 4684*b6932704SJoseph Chen sub_parser.add_argument('--product_key_certificate', 4685*b6932704SJoseph Chen help='Path to product key certificate file', 4686*b6932704SJoseph Chen type=argparse.FileType('rb'), 4687*b6932704SJoseph Chen required=True) 4688*b6932704SJoseph Chen sub_parser.set_defaults(func=self.make_atx_metadata) 4689*b6932704SJoseph Chen 4690*b6932704SJoseph Chen sub_parser = subparsers.add_parser( 4691*b6932704SJoseph Chen 'make_atx_unlock_credential', 4692*b6932704SJoseph Chen help='Create an Android Things eXtension (ATX) unlock credential.') 4693*b6932704SJoseph Chen sub_parser.add_argument('--output', 4694*b6932704SJoseph Chen help='Write credential to file', 4695*b6932704SJoseph Chen type=argparse.FileType('wb'), 4696*b6932704SJoseph Chen default=sys.stdout) 4697*b6932704SJoseph Chen sub_parser.add_argument('--intermediate_key_certificate', 4698*b6932704SJoseph Chen help='Path to intermediate key certificate file', 4699*b6932704SJoseph Chen type=argparse.FileType('rb'), 4700*b6932704SJoseph Chen required=True) 4701*b6932704SJoseph Chen sub_parser.add_argument('--unlock_key_certificate', 4702*b6932704SJoseph Chen help='Path to unlock key certificate file', 4703*b6932704SJoseph Chen type=argparse.FileType('rb'), 4704*b6932704SJoseph Chen required=True) 4705*b6932704SJoseph Chen sub_parser.add_argument('--challenge', 4706*b6932704SJoseph Chen help='Path to the challenge to sign (optional). If ' 4707*b6932704SJoseph Chen 'this is not provided the challenge signature ' 4708*b6932704SJoseph Chen 'field is omitted and can be concatenated ' 4709*b6932704SJoseph Chen 'later.', 4710*b6932704SJoseph Chen required=False) 4711*b6932704SJoseph Chen sub_parser.add_argument('--unlock_key', 4712*b6932704SJoseph Chen help='Path to unlock key (optional). Must be ' 4713*b6932704SJoseph Chen 'provided if using --challenge.', 4714*b6932704SJoseph Chen required=False) 4715*b6932704SJoseph Chen sub_parser.add_argument('--signing_helper', 4716*b6932704SJoseph Chen help='Path to helper used for signing', 4717*b6932704SJoseph Chen metavar='APP', 4718*b6932704SJoseph Chen default=None, 4719*b6932704SJoseph Chen required=False) 4720*b6932704SJoseph Chen sub_parser.add_argument('--signing_helper_with_files', 4721*b6932704SJoseph Chen help='Path to helper used for signing using files', 4722*b6932704SJoseph Chen metavar='APP', 4723*b6932704SJoseph Chen default=None, 4724*b6932704SJoseph Chen required=False) 4725*b6932704SJoseph Chen sub_parser.set_defaults(func=self.make_atx_unlock_credential) 4726*b6932704SJoseph Chen 4727*b6932704SJoseph Chen args = parser.parse_args(argv[1:]) 4728*b6932704SJoseph Chen try: 4729*b6932704SJoseph Chen args.func(args) 4730*b6932704SJoseph Chen except AttributeError: 4731*b6932704SJoseph Chen # This error gets raised when the command line tool is called without any 4732*b6932704SJoseph Chen # arguments. It mimics the original Python 2 behavior. 4733*b6932704SJoseph Chen parser.print_usage() 4734*b6932704SJoseph Chen print('avbtool: error: too few arguments') 4735*b6932704SJoseph Chen sys.exit(2) 4736*b6932704SJoseph Chen except AvbError as e: 4737*b6932704SJoseph Chen sys.stderr.write('{}: {}\n'.format(argv[0], str(e))) 4738*b6932704SJoseph Chen sys.exit(1) 4739*b6932704SJoseph Chen 4740*b6932704SJoseph Chen def version(self, _): 4741*b6932704SJoseph Chen """Implements the 'version' sub-command.""" 4742*b6932704SJoseph Chen print(get_release_string()) 4743*b6932704SJoseph Chen 4744*b6932704SJoseph Chen def generate_test_image(self, args): 4745*b6932704SJoseph Chen """Implements the 'generate_test_image' sub-command.""" 4746*b6932704SJoseph Chen self.avb.generate_test_image(args.output, args.image_size, args.start_byte) 4747*b6932704SJoseph Chen 4748*b6932704SJoseph Chen def extract_public_key(self, args): 4749*b6932704SJoseph Chen """Implements the 'extract_public_key' sub-command.""" 4750*b6932704SJoseph Chen self.avb.extract_public_key(args.key, args.output) 4751*b6932704SJoseph Chen 4752*b6932704SJoseph Chen def make_vbmeta_image(self, args): 4753*b6932704SJoseph Chen """Implements the 'make_vbmeta_image' sub-command.""" 4754*b6932704SJoseph Chen args = self._fixup_common_args(args) 4755*b6932704SJoseph Chen self.avb.make_vbmeta_image(args.output, args.chain_partition, 4756*b6932704SJoseph Chen args.algorithm, args.key, 4757*b6932704SJoseph Chen args.public_key_metadata, args.rollback_index, 4758*b6932704SJoseph Chen args.flags, args.rollback_index_location, 4759*b6932704SJoseph Chen args.prop, args.prop_from_file, 4760*b6932704SJoseph Chen args.kernel_cmdline, 4761*b6932704SJoseph Chen args.setup_rootfs_from_kernel, 4762*b6932704SJoseph Chen args.include_descriptors_from_image, 4763*b6932704SJoseph Chen args.signing_helper, 4764*b6932704SJoseph Chen args.signing_helper_with_files, 4765*b6932704SJoseph Chen args.internal_release_string, 4766*b6932704SJoseph Chen args.append_to_release_string, 4767*b6932704SJoseph Chen args.print_required_libavb_version, 4768*b6932704SJoseph Chen args.padding_size) 4769*b6932704SJoseph Chen 4770*b6932704SJoseph Chen def append_vbmeta_image(self, args): 4771*b6932704SJoseph Chen """Implements the 'append_vbmeta_image' sub-command.""" 4772*b6932704SJoseph Chen self.avb.append_vbmeta_image(args.image.name, args.vbmeta_image.name, 4773*b6932704SJoseph Chen args.partition_size) 4774*b6932704SJoseph Chen 4775*b6932704SJoseph Chen def add_hash_footer(self, args): 4776*b6932704SJoseph Chen """Implements the 'add_hash_footer' sub-command.""" 4777*b6932704SJoseph Chen args = self._fixup_common_args(args) 4778*b6932704SJoseph Chen self.avb.add_hash_footer(args.image.name if args.image else None, 4779*b6932704SJoseph Chen args.partition_size, args.dynamic_partition_size, 4780*b6932704SJoseph Chen args.partition_name, args.hash_algorithm, 4781*b6932704SJoseph Chen args.salt, args.chain_partition, args.algorithm, 4782*b6932704SJoseph Chen args.key, 4783*b6932704SJoseph Chen args.public_key_metadata, args.rollback_index, 4784*b6932704SJoseph Chen args.flags, args.rollback_index_location, 4785*b6932704SJoseph Chen args.prop, args.prop_from_file, 4786*b6932704SJoseph Chen args.kernel_cmdline, 4787*b6932704SJoseph Chen args.setup_rootfs_from_kernel, 4788*b6932704SJoseph Chen args.include_descriptors_from_image, 4789*b6932704SJoseph Chen args.calc_max_image_size, 4790*b6932704SJoseph Chen args.signing_helper, 4791*b6932704SJoseph Chen args.signing_helper_with_files, 4792*b6932704SJoseph Chen args.internal_release_string, 4793*b6932704SJoseph Chen args.append_to_release_string, 4794*b6932704SJoseph Chen args.output_vbmeta_image, 4795*b6932704SJoseph Chen args.do_not_append_vbmeta_image, 4796*b6932704SJoseph Chen args.print_required_libavb_version, 4797*b6932704SJoseph Chen args.use_persistent_digest, 4798*b6932704SJoseph Chen args.do_not_use_ab) 4799*b6932704SJoseph Chen 4800*b6932704SJoseph Chen def add_hashtree_footer(self, args): 4801*b6932704SJoseph Chen """Implements the 'add_hashtree_footer' sub-command.""" 4802*b6932704SJoseph Chen args = self._fixup_common_args(args) 4803*b6932704SJoseph Chen # TODO(zeuthen): Remove when removing support for the 4804*b6932704SJoseph Chen # '--generate_fec' option above. 4805*b6932704SJoseph Chen if args.generate_fec: 4806*b6932704SJoseph Chen sys.stderr.write('The --generate_fec option is deprecated since FEC ' 4807*b6932704SJoseph Chen 'is now generated by default. Use the option ' 4808*b6932704SJoseph Chen '--do_not_generate_fec to not generate FEC.\n') 4809*b6932704SJoseph Chen self.avb.add_hashtree_footer( 4810*b6932704SJoseph Chen args.image.name if args.image else None, 4811*b6932704SJoseph Chen args.partition_size, 4812*b6932704SJoseph Chen args.partition_name, 4813*b6932704SJoseph Chen not args.do_not_generate_fec, args.fec_num_roots, 4814*b6932704SJoseph Chen args.hash_algorithm, args.block_size, 4815*b6932704SJoseph Chen args.salt, args.chain_partition, args.algorithm, 4816*b6932704SJoseph Chen args.key, args.public_key_metadata, 4817*b6932704SJoseph Chen args.rollback_index, args.flags, 4818*b6932704SJoseph Chen args.rollback_index_location, args.prop, 4819*b6932704SJoseph Chen args.prop_from_file, 4820*b6932704SJoseph Chen args.kernel_cmdline, 4821*b6932704SJoseph Chen args.setup_rootfs_from_kernel, 4822*b6932704SJoseph Chen args.setup_as_rootfs_from_kernel, 4823*b6932704SJoseph Chen args.include_descriptors_from_image, 4824*b6932704SJoseph Chen args.calc_max_image_size, 4825*b6932704SJoseph Chen args.signing_helper, 4826*b6932704SJoseph Chen args.signing_helper_with_files, 4827*b6932704SJoseph Chen args.internal_release_string, 4828*b6932704SJoseph Chen args.append_to_release_string, 4829*b6932704SJoseph Chen args.output_vbmeta_image, 4830*b6932704SJoseph Chen args.do_not_append_vbmeta_image, 4831*b6932704SJoseph Chen args.print_required_libavb_version, 4832*b6932704SJoseph Chen args.use_persistent_digest, 4833*b6932704SJoseph Chen args.do_not_use_ab, 4834*b6932704SJoseph Chen args.no_hashtree, 4835*b6932704SJoseph Chen args.check_at_most_once) 4836*b6932704SJoseph Chen 4837*b6932704SJoseph Chen def erase_footer(self, args): 4838*b6932704SJoseph Chen """Implements the 'erase_footer' sub-command.""" 4839*b6932704SJoseph Chen self.avb.erase_footer(args.image.name, args.keep_hashtree) 4840*b6932704SJoseph Chen 4841*b6932704SJoseph Chen def zero_hashtree(self, args): 4842*b6932704SJoseph Chen """Implements the 'zero_hashtree' sub-command.""" 4843*b6932704SJoseph Chen self.avb.zero_hashtree(args.image.name) 4844*b6932704SJoseph Chen 4845*b6932704SJoseph Chen def extract_vbmeta_image(self, args): 4846*b6932704SJoseph Chen """Implements the 'extract_vbmeta_image' sub-command.""" 4847*b6932704SJoseph Chen self.avb.extract_vbmeta_image(args.output, args.image.name, 4848*b6932704SJoseph Chen args.padding_size) 4849*b6932704SJoseph Chen 4850*b6932704SJoseph Chen def resize_image(self, args): 4851*b6932704SJoseph Chen """Implements the 'resize_image' sub-command.""" 4852*b6932704SJoseph Chen self.avb.resize_image(args.image.name, args.partition_size) 4853*b6932704SJoseph Chen 4854*b6932704SJoseph Chen def set_ab_metadata(self, args): 4855*b6932704SJoseph Chen """Implements the 'set_ab_metadata' sub-command.""" 4856*b6932704SJoseph Chen self.avb.set_ab_metadata(args.misc_image, args.slot_data) 4857*b6932704SJoseph Chen 4858*b6932704SJoseph Chen def info_image(self, args): 4859*b6932704SJoseph Chen """Implements the 'info_image' sub-command.""" 4860*b6932704SJoseph Chen self.avb.info_image(args.image.name, args.output, args.atx) 4861*b6932704SJoseph Chen 4862*b6932704SJoseph Chen def verify_image(self, args): 4863*b6932704SJoseph Chen """Implements the 'verify_image' sub-command.""" 4864*b6932704SJoseph Chen self.avb.verify_image(args.image.name, args.key, 4865*b6932704SJoseph Chen args.expected_chain_partition, 4866*b6932704SJoseph Chen args.follow_chain_partitions, 4867*b6932704SJoseph Chen args.accept_zeroed_hashtree) 4868*b6932704SJoseph Chen 4869*b6932704SJoseph Chen def print_partition_digests(self, args): 4870*b6932704SJoseph Chen """Implements the 'print_partition_digests' sub-command.""" 4871*b6932704SJoseph Chen self.avb.print_partition_digests(args.image.name, args.output, args.json) 4872*b6932704SJoseph Chen 4873*b6932704SJoseph Chen def calculate_vbmeta_digest(self, args): 4874*b6932704SJoseph Chen """Implements the 'calculate_vbmeta_digest' sub-command.""" 4875*b6932704SJoseph Chen self.avb.calculate_vbmeta_digest(args.image.name, args.hash_algorithm, 4876*b6932704SJoseph Chen args.output) 4877*b6932704SJoseph Chen 4878*b6932704SJoseph Chen def calculate_kernel_cmdline(self, args): 4879*b6932704SJoseph Chen """Implements the 'calculate_kernel_cmdline' sub-command.""" 4880*b6932704SJoseph Chen self.avb.calculate_kernel_cmdline(args.image.name, args.hashtree_disabled, 4881*b6932704SJoseph Chen args.output) 4882*b6932704SJoseph Chen 4883*b6932704SJoseph Chen def make_atx_certificate(self, args): 4884*b6932704SJoseph Chen """Implements the 'make_atx_certificate' sub-command.""" 4885*b6932704SJoseph Chen self.avb.make_atx_certificate(args.output, args.authority_key, 4886*b6932704SJoseph Chen args.subject_key.name, 4887*b6932704SJoseph Chen args.subject_key_version, 4888*b6932704SJoseph Chen args.subject.read(), 4889*b6932704SJoseph Chen args.subject_is_intermediate_authority, 4890*b6932704SJoseph Chen args.usage, 4891*b6932704SJoseph Chen args.signing_helper, 4892*b6932704SJoseph Chen args.signing_helper_with_files) 4893*b6932704SJoseph Chen 4894*b6932704SJoseph Chen def make_atx_permanent_attributes(self, args): 4895*b6932704SJoseph Chen """Implements the 'make_atx_permanent_attributes' sub-command.""" 4896*b6932704SJoseph Chen self.avb.make_atx_permanent_attributes(args.output, 4897*b6932704SJoseph Chen args.root_authority_key.name, 4898*b6932704SJoseph Chen args.product_id.read()) 4899*b6932704SJoseph Chen 4900*b6932704SJoseph Chen def make_atx_metadata(self, args): 4901*b6932704SJoseph Chen """Implements the 'make_atx_metadata' sub-command.""" 4902*b6932704SJoseph Chen self.avb.make_atx_metadata(args.output, 4903*b6932704SJoseph Chen args.intermediate_key_certificate.read(), 4904*b6932704SJoseph Chen args.product_key_certificate.read()) 4905*b6932704SJoseph Chen 4906*b6932704SJoseph Chen def make_atx_unlock_credential(self, args): 4907*b6932704SJoseph Chen """Implements the 'make_atx_unlock_credential' sub-command.""" 4908*b6932704SJoseph Chen self.avb.make_atx_unlock_credential( 4909*b6932704SJoseph Chen args.output, 4910*b6932704SJoseph Chen args.intermediate_key_certificate.read(), 4911*b6932704SJoseph Chen args.unlock_key_certificate.read(), 4912*b6932704SJoseph Chen args.challenge, 4913*b6932704SJoseph Chen args.unlock_key, 4914*b6932704SJoseph Chen args.signing_helper, 4915*b6932704SJoseph Chen args.signing_helper_with_files) 4916*b6932704SJoseph Chen 4917*b6932704SJoseph Chen 4918*b6932704SJoseph Chenif __name__ == '__main__': 4919*b6932704SJoseph Chen if AVB_INVOCATION_LOGFILE: 4920*b6932704SJoseph Chen with open(AVB_INVOCATION_LOGFILE, 'a') as log: 4921*b6932704SJoseph Chen log.write(' '.join(sys.argv)) 4922*b6932704SJoseph Chen log.write('\n') 4923*b6932704SJoseph Chen 4924*b6932704SJoseph Chen tool = AvbTool() 4925*b6932704SJoseph Chen tool.run(sys.argv) 4926