| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1 | #!/usr/bin/env python | 
 | 2 |  | 
 | 3 | # Copyright 2016, The Android Open Source Project | 
 | 4 | # | 
| David Zeuthen | c612e2e | 2016-09-16 16:44:08 -0400 | [diff] [blame] | 5 | # Permission is hereby granted, free of charge, to any person | 
 | 6 | # obtaining a copy of this software and associated documentation | 
 | 7 | # files (the "Software"), to deal in the Software without | 
 | 8 | # restriction, including without limitation the rights to use, copy, | 
 | 9 | # modify, merge, publish, distribute, sublicense, and/or sell copies | 
 | 10 | # of the Software, and to permit persons to whom the Software is | 
 | 11 | # furnished to do so, subject to the following conditions: | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 12 | # | 
| David Zeuthen | c612e2e | 2016-09-16 16:44:08 -0400 | [diff] [blame] | 13 | # The above copyright notice and this permission notice shall be | 
 | 14 | # included in all copies or substantial portions of the Software. | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 15 | # | 
| David Zeuthen | c612e2e | 2016-09-16 16:44:08 -0400 | [diff] [blame] | 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | 
 | 17 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | 
 | 18 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | 
 | 19 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | 
 | 20 | # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | 
 | 21 | # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | 
 | 22 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | 
 | 23 | # SOFTWARE. | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 24 | # | 
| David Zeuthen | 8b6973b | 2016-09-20 12:39:49 -0400 | [diff] [blame] | 25 | """Command-line tool for working with Android Verified Boot images.""" | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 26 |  | 
 | 27 | import argparse | 
| David Zeuthen | 8b6973b | 2016-09-20 12:39:49 -0400 | [diff] [blame] | 28 | import binascii | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 29 | import bisect | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 30 | import hashlib | 
 | 31 | import os | 
 | 32 | import struct | 
 | 33 | import subprocess | 
 | 34 | import sys | 
| David Zeuthen | 0b7f1d3 | 2016-10-25 17:53:49 -0400 | [diff] [blame] | 35 | import tempfile | 
| Darren Krahn | 147b08d | 2016-12-20 16:38:29 -0800 | [diff] [blame] | 36 | import time | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 37 |  | 
 | 38 | import Crypto.PublicKey.RSA | 
 | 39 |  | 
 | 40 | # Keep in sync with avb_vbmeta_header.h. | 
 | 41 | AVB_VERSION_MAJOR = 1 | 
 | 42 | AVB_VERSION_MINOR = 0 | 
| David Zeuthen | 5830552 | 2017-01-11 17:42:47 -0500 | [diff] [blame] | 43 | AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED = 1 | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 44 |  | 
 | 45 | class AvbError(Exception): | 
 | 46 |   """Application-specific errors. | 
 | 47 |  | 
 | 48 |   These errors represent issues for which a stack-trace should not be | 
 | 49 |   presented. | 
 | 50 |  | 
 | 51 |   Attributes: | 
 | 52 |     message: Error message. | 
 | 53 |   """ | 
 | 54 |  | 
 | 55 |   def __init__(self, message): | 
 | 56 |     Exception.__init__(self, message) | 
 | 57 |  | 
 | 58 |  | 
 | 59 | class Algorithm(object): | 
 | 60 |   """Contains details about an algorithm. | 
 | 61 |  | 
 | 62 |   See the avb_vbmeta_header.h file for more details about | 
 | 63 |   algorithms. | 
 | 64 |  | 
 | 65 |   The constant |ALGORITHMS| is a dictionary from human-readable | 
 | 66 |   names (e.g 'SHA256_RSA2048') to instances of this class. | 
 | 67 |  | 
 | 68 |   Attributes: | 
 | 69 |     algorithm_type: Integer code corresponding to |AvbAlgorithmType|. | 
 | 70 |     hash_num_bytes: Number of bytes used to store the hash. | 
 | 71 |     signature_num_bytes: Number of bytes used to store the signature. | 
 | 72 |     public_key_num_bytes: Number of bytes used to store the public key. | 
 | 73 |     padding: Padding used for signature, if any. | 
 | 74 |   """ | 
 | 75 |  | 
 | 76 |   def __init__(self, algorithm_type, hash_num_bytes, signature_num_bytes, | 
 | 77 |                public_key_num_bytes, padding): | 
 | 78 |     self.algorithm_type = algorithm_type | 
 | 79 |     self.hash_num_bytes = hash_num_bytes | 
 | 80 |     self.signature_num_bytes = signature_num_bytes | 
 | 81 |     self.public_key_num_bytes = public_key_num_bytes | 
 | 82 |     self.padding = padding | 
 | 83 |  | 
 | 84 | # This must be kept in sync with the avb_crypto.h file. | 
 | 85 | # | 
 | 86 | # The PKC1-v1.5 padding is a blob of binary DER of ASN.1 and is | 
 | 87 | # obtained from section 5.2.2 of RFC 4880. | 
 | 88 | ALGORITHMS = { | 
 | 89 |     'NONE': Algorithm( | 
 | 90 |         algorithm_type=0,        # AVB_ALGORITHM_TYPE_NONE | 
 | 91 |         hash_num_bytes=0, | 
 | 92 |         signature_num_bytes=0, | 
 | 93 |         public_key_num_bytes=0, | 
 | 94 |         padding=[]), | 
 | 95 |     'SHA256_RSA2048': Algorithm( | 
 | 96 |         algorithm_type=1,        # AVB_ALGORITHM_TYPE_SHA256_RSA2048 | 
 | 97 |         hash_num_bytes=32, | 
 | 98 |         signature_num_bytes=256, | 
 | 99 |         public_key_num_bytes=8 + 2*2048/8, | 
 | 100 |         padding=[ | 
 | 101 |             # PKCS1-v1_5 padding | 
 | 102 |             0x00, 0x01] + [0xff]*202 + [0x00] + [ | 
 | 103 |                 # ASN.1 header | 
 | 104 |                 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, | 
 | 105 |                 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, | 
 | 106 |                 0x00, 0x04, 0x20, | 
 | 107 |             ]), | 
 | 108 |     'SHA256_RSA4096': Algorithm( | 
 | 109 |         algorithm_type=2,        # AVB_ALGORITHM_TYPE_SHA256_RSA4096 | 
 | 110 |         hash_num_bytes=32, | 
 | 111 |         signature_num_bytes=512, | 
 | 112 |         public_key_num_bytes=8 + 2*4096/8, | 
 | 113 |         padding=[ | 
 | 114 |             # PKCS1-v1_5 padding | 
 | 115 |             0x00, 0x01] + [0xff]*458 + [0x00] + [ | 
 | 116 |                 # ASN.1 header | 
 | 117 |                 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, | 
 | 118 |                 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, | 
 | 119 |                 0x00, 0x04, 0x20, | 
 | 120 |             ]), | 
 | 121 |     'SHA256_RSA8192': Algorithm( | 
 | 122 |         algorithm_type=3,        # AVB_ALGORITHM_TYPE_SHA256_RSA8192 | 
 | 123 |         hash_num_bytes=32, | 
 | 124 |         signature_num_bytes=1024, | 
 | 125 |         public_key_num_bytes=8 + 2*8192/8, | 
 | 126 |         padding=[ | 
 | 127 |             # PKCS1-v1_5 padding | 
 | 128 |             0x00, 0x01] + [0xff]*970 + [0x00] + [ | 
 | 129 |                 # ASN.1 header | 
 | 130 |                 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, | 
 | 131 |                 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, | 
 | 132 |                 0x00, 0x04, 0x20, | 
 | 133 |             ]), | 
 | 134 |     'SHA512_RSA2048': Algorithm( | 
 | 135 |         algorithm_type=4,        # AVB_ALGORITHM_TYPE_SHA512_RSA2048 | 
 | 136 |         hash_num_bytes=64, | 
 | 137 |         signature_num_bytes=256, | 
 | 138 |         public_key_num_bytes=8 + 2*2048/8, | 
 | 139 |         padding=[ | 
 | 140 |             # PKCS1-v1_5 padding | 
 | 141 |             0x00, 0x01] + [0xff]*170 + [0x00] + [ | 
 | 142 |                 # ASN.1 header | 
 | 143 |                 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, | 
 | 144 |                 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, | 
 | 145 |                 0x00, 0x04, 0x40 | 
 | 146 |             ]), | 
 | 147 |     'SHA512_RSA4096': Algorithm( | 
 | 148 |         algorithm_type=5,        # AVB_ALGORITHM_TYPE_SHA512_RSA4096 | 
 | 149 |         hash_num_bytes=64, | 
 | 150 |         signature_num_bytes=512, | 
 | 151 |         public_key_num_bytes=8 + 2*4096/8, | 
 | 152 |         padding=[ | 
 | 153 |             # PKCS1-v1_5 padding | 
 | 154 |             0x00, 0x01] + [0xff]*426 + [0x00] + [ | 
 | 155 |                 # ASN.1 header | 
 | 156 |                 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, | 
 | 157 |                 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, | 
 | 158 |                 0x00, 0x04, 0x40 | 
 | 159 |             ]), | 
 | 160 |     'SHA512_RSA8192': Algorithm( | 
 | 161 |         algorithm_type=6,        # AVB_ALGORITHM_TYPE_SHA512_RSA8192 | 
 | 162 |         hash_num_bytes=64, | 
 | 163 |         signature_num_bytes=1024, | 
 | 164 |         public_key_num_bytes=8 + 2*8192/8, | 
 | 165 |         padding=[ | 
 | 166 |             # PKCS1-v1_5 padding | 
 | 167 |             0x00, 0x01] + [0xff]*938 + [0x00] + [ | 
 | 168 |                 # ASN.1 header | 
 | 169 |                 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, | 
 | 170 |                 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, | 
 | 171 |                 0x00, 0x04, 0x40 | 
 | 172 |             ]), | 
 | 173 | } | 
 | 174 |  | 
 | 175 |  | 
 | 176 | def round_to_multiple(number, size): | 
 | 177 |   """Rounds a number up to nearest multiple of another number. | 
 | 178 |  | 
 | 179 |   Args: | 
 | 180 |     number: The number to round up. | 
 | 181 |     size: The multiple to round up to. | 
 | 182 |  | 
 | 183 |   Returns: | 
 | 184 |     If |number| is a multiple of |size|, returns |number|, otherwise | 
 | 185 |     returns |number| + |size|. | 
 | 186 |   """ | 
 | 187 |   remainder = number % size | 
 | 188 |   if remainder == 0: | 
 | 189 |     return number | 
 | 190 |   return number + size - remainder | 
 | 191 |  | 
 | 192 |  | 
 | 193 | def round_to_pow2(number): | 
 | 194 |   """Rounds a number up to the next power of 2. | 
 | 195 |  | 
 | 196 |   Args: | 
 | 197 |     number: The number to round up. | 
 | 198 |  | 
 | 199 |   Returns: | 
 | 200 |     If |number| is already a power of 2 then |number| is | 
 | 201 |     returned. Otherwise the smallest power of 2 greater than |number| | 
 | 202 |     is returned. | 
 | 203 |   """ | 
 | 204 |   return 2**((number - 1).bit_length()) | 
 | 205 |  | 
 | 206 |  | 
 | 207 | def write_long(output, num_bits, value): | 
 | 208 |   """Writes a long to an output stream using a given amount of bits. | 
 | 209 |  | 
 | 210 |   This number is written big-endian, e.g. with the most significant | 
 | 211 |   bit first. | 
 | 212 |  | 
 | 213 |   Arguments: | 
 | 214 |     output: The object to write the output to. | 
 | 215 |     num_bits: The number of bits to write, e.g. 2048. | 
 | 216 |     value: The value to write. | 
 | 217 |   """ | 
 | 218 |   for bit_pos in range(num_bits, 0, -8): | 
 | 219 |     octet = (value >> (bit_pos - 8)) & 0xff | 
 | 220 |     output.write(struct.pack('!B', octet)) | 
 | 221 |  | 
 | 222 |  | 
 | 223 | def encode_long(num_bits, value): | 
 | 224 |   """Encodes a long to a bytearray() using a given amount of bits. | 
 | 225 |  | 
 | 226 |   This number is written big-endian, e.g. with the most significant | 
 | 227 |   bit first. | 
 | 228 |  | 
 | 229 |   Arguments: | 
 | 230 |     num_bits: The number of bits to write, e.g. 2048. | 
 | 231 |     value: The value to write. | 
 | 232 |  | 
 | 233 |   Returns: | 
 | 234 |     A bytearray() with the encoded long. | 
 | 235 |   """ | 
 | 236 |   ret = bytearray() | 
 | 237 |   for bit_pos in range(num_bits, 0, -8): | 
 | 238 |     octet = (value >> (bit_pos - 8)) & 0xff | 
 | 239 |     ret.extend(struct.pack('!B', octet)) | 
 | 240 |   return ret | 
 | 241 |  | 
 | 242 |  | 
 | 243 | def egcd(a, b): | 
 | 244 |   """Calculate greatest common divisor of two numbers. | 
 | 245 |  | 
 | 246 |   This implementation uses a recursive version of the extended | 
 | 247 |   Euclidian algorithm. | 
 | 248 |  | 
 | 249 |   Arguments: | 
 | 250 |     a: First number. | 
 | 251 |     b: Second number. | 
 | 252 |  | 
 | 253 |   Returns: | 
 | 254 |     A tuple (gcd, x, y) that where |gcd| is the greatest common | 
 | 255 |     divisor of |a| and |b| and |a|*|x| + |b|*|y| = |gcd|. | 
 | 256 |   """ | 
 | 257 |   if a == 0: | 
 | 258 |     return (b, 0, 1) | 
 | 259 |   else: | 
 | 260 |     g, y, x = egcd(b % a, a) | 
 | 261 |     return (g, x - (b // a) * y, y) | 
 | 262 |  | 
 | 263 |  | 
 | 264 | def modinv(a, m): | 
 | 265 |   """Calculate modular multiplicative inverse of |a| modulo |m|. | 
 | 266 |  | 
 | 267 |   This calculates the number |x| such that |a| * |x| == 1 (modulo | 
 | 268 |   |m|). This number only exists if |a| and |m| are co-prime - |None| | 
 | 269 |   is returned if this isn't true. | 
 | 270 |  | 
 | 271 |   Arguments: | 
 | 272 |     a: The number to calculate a modular inverse of. | 
 | 273 |     m: The modulo to use. | 
 | 274 |  | 
 | 275 |   Returns: | 
 | 276 |     The modular multiplicative inverse of |a| and |m| or |None| if | 
 | 277 |     these numbers are not co-prime. | 
 | 278 |   """ | 
 | 279 |   gcd, x, _ = egcd(a, m) | 
 | 280 |   if gcd != 1: | 
 | 281 |     return None  # modular inverse does not exist | 
 | 282 |   else: | 
 | 283 |     return x % m | 
 | 284 |  | 
 | 285 |  | 
 | 286 | def parse_number(string): | 
 | 287 |   """Parse a string as a number. | 
 | 288 |  | 
 | 289 |   This is just a short-hand for int(string, 0) suitable for use in the | 
 | 290 |   |type| parameter of |ArgumentParser|'s add_argument() function. An | 
 | 291 |   improvement to just using type=int is that this function supports | 
 | 292 |   numbers in other bases, e.g. "0x1234". | 
 | 293 |  | 
 | 294 |   Arguments: | 
 | 295 |     string: The string to parse. | 
 | 296 |  | 
 | 297 |   Returns: | 
 | 298 |     The parsed integer. | 
 | 299 |  | 
 | 300 |   Raises: | 
 | 301 |     ValueError: If the number could not be parsed. | 
 | 302 |   """ | 
 | 303 |   return int(string, 0) | 
 | 304 |  | 
 | 305 |  | 
 | 306 | def write_rsa_key(output, key): | 
 | 307 |   """Writes a public RSA key in |AvbRSAPublicKeyHeader| format. | 
 | 308 |  | 
 | 309 |   This writes the |AvbRSAPublicKeyHeader| as well as the two large | 
 | 310 |   numbers (|key_num_bits| bits long) following it. | 
 | 311 |  | 
 | 312 |   Arguments: | 
 | 313 |     output: The object to write the output to. | 
 | 314 |     key: A Crypto.PublicKey.RSA object. | 
 | 315 |   """ | 
 | 316 |   # key.e is exponent | 
 | 317 |   # key.n is modulus | 
 | 318 |   key_num_bits = key.size() + 1 | 
 | 319 |   # Calculate n0inv = -1/n[0] (mod 2^32) | 
 | 320 |   b = 2L**32 | 
 | 321 |   n0inv = b - modinv(key.n, b) | 
 | 322 |   # Calculate rr = r^2 (mod N), where r = 2^(# of key bits) | 
 | 323 |   r = 2L**key.n.bit_length() | 
 | 324 |   rrmodn = r * r % key.n | 
 | 325 |   output.write(struct.pack('!II', key_num_bits, n0inv)) | 
 | 326 |   write_long(output, key_num_bits, key.n) | 
 | 327 |   write_long(output, key_num_bits, rrmodn) | 
 | 328 |  | 
 | 329 |  | 
 | 330 | def encode_rsa_key(key): | 
 | 331 |   """Encodes a public RSA key in |AvbRSAPublicKeyHeader| format. | 
 | 332 |  | 
 | 333 |   This creates a |AvbRSAPublicKeyHeader| as well as the two large | 
 | 334 |   numbers (|key_num_bits| bits long) following it. | 
 | 335 |  | 
 | 336 |   Arguments: | 
 | 337 |     key: A Crypto.PublicKey.RSA object. | 
 | 338 |  | 
 | 339 |   Returns: | 
 | 340 |     A bytearray() with the |AvbRSAPublicKeyHeader|. | 
 | 341 |   """ | 
 | 342 |   ret = bytearray() | 
 | 343 |   # key.e is exponent | 
 | 344 |   # key.n is modulus | 
 | 345 |   key_num_bits = key.size() + 1 | 
 | 346 |   # Calculate n0inv = -1/n[0] (mod 2^32) | 
 | 347 |   b = 2L**32 | 
 | 348 |   n0inv = b - modinv(key.n, b) | 
 | 349 |   # Calculate rr = r^2 (mod N), where r = 2^(# of key bits) | 
 | 350 |   r = 2L**key.n.bit_length() | 
 | 351 |   rrmodn = r * r % key.n | 
 | 352 |   ret.extend(struct.pack('!II', key_num_bits, n0inv)) | 
 | 353 |   ret.extend(encode_long(key_num_bits, key.n)) | 
 | 354 |   ret.extend(encode_long(key_num_bits, rrmodn)) | 
 | 355 |   return ret | 
 | 356 |  | 
 | 357 |  | 
 | 358 | def lookup_algorithm_by_type(alg_type): | 
 | 359 |   """Looks up algorithm by type. | 
 | 360 |  | 
 | 361 |   Arguments: | 
 | 362 |     alg_type: The integer representing the type. | 
 | 363 |  | 
 | 364 |   Returns: | 
 | 365 |     A tuple with the algorithm name and an |Algorithm| instance. | 
 | 366 |  | 
 | 367 |   Raises: | 
 | 368 |     Exception: If the algorithm cannot be found | 
 | 369 |   """ | 
 | 370 |   for alg_name in ALGORITHMS: | 
 | 371 |     alg_data = ALGORITHMS[alg_name] | 
 | 372 |     if alg_data.algorithm_type == alg_type: | 
 | 373 |       return (alg_name, alg_data) | 
 | 374 |   raise AvbError('Unknown algorithm type {}'.format(alg_type)) | 
 | 375 |  | 
 | 376 |  | 
| Darren Krahn | 147b08d | 2016-12-20 16:38:29 -0800 | [diff] [blame] | 377 | def raw_sign(signing_helper, algorithm_name, key_path, raw_data_to_sign): | 
 | 378 |   """Computes a raw RSA signature using |signing_helper| or openssl. | 
 | 379 |  | 
 | 380 |   Arguments: | 
 | 381 |     signing_helper: Program which signs a hash and returns the signature. | 
 | 382 |     algorithm_name: The algorithm name as per the ALGORITHMS dict. | 
 | 383 |     key_path: Path to the private key file. Must be PEM format. | 
 | 384 |     raw_data_to_sign: Data to sign (bytearray or str expected). | 
 | 385 |  | 
 | 386 |   Returns: | 
 | 387 |     A bytearray containing the signature. | 
 | 388 |  | 
 | 389 |   Raises: | 
 | 390 |     Exception: If an error occurs. | 
 | 391 |   """ | 
 | 392 |   p = None | 
 | 393 |   if signing_helper is not None: | 
 | 394 |       p = subprocess.Popen( | 
 | 395 |           [signing_helper, algorithm_name, key_path], | 
 | 396 |           stdin=subprocess.PIPE, | 
 | 397 |           stdout=subprocess.PIPE, | 
 | 398 |           stderr=subprocess.PIPE) | 
 | 399 |   else: | 
 | 400 |       p = subprocess.Popen( | 
 | 401 |           ['openssl', 'rsautl', '-sign', '-inkey', key_path, '-raw'], | 
 | 402 |           stdin=subprocess.PIPE, | 
 | 403 |           stdout=subprocess.PIPE, | 
 | 404 |           stderr=subprocess.PIPE) | 
 | 405 |   (pout, perr) = p.communicate(str(raw_data_to_sign)) | 
 | 406 |   retcode = p.wait() | 
 | 407 |   if retcode != 0: | 
 | 408 |     raise AvbError('Error signing: {}'.format(perr)) | 
 | 409 |   return bytearray(pout) | 
 | 410 |  | 
 | 411 |  | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 412 | class ImageChunk(object): | 
 | 413 |   """Data structure used for representing chunks in Android sparse files. | 
 | 414 |  | 
 | 415 |   Attributes: | 
 | 416 |     chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE. | 
 | 417 |     chunk_offset: Offset in the sparse file where this chunk begins. | 
 | 418 |     output_offset: Offset in de-sparsified file where output begins. | 
 | 419 |     output_size: Number of bytes in output. | 
 | 420 |     input_offset: Offset in sparse file for data if TYPE_RAW otherwise None. | 
 | 421 |     fill_data: Blob with data to fill if TYPE_FILL otherwise None. | 
 | 422 |   """ | 
 | 423 |  | 
 | 424 |   FORMAT = '<2H2I' | 
 | 425 |   TYPE_RAW = 0xcac1 | 
 | 426 |   TYPE_FILL = 0xcac2 | 
 | 427 |   TYPE_DONT_CARE = 0xcac3 | 
 | 428 |   TYPE_CRC32 = 0xcac4 | 
 | 429 |  | 
 | 430 |   def __init__(self, chunk_type, chunk_offset, output_offset, output_size, | 
 | 431 |                input_offset, fill_data): | 
 | 432 |     """Initializes an ImageChunk object. | 
 | 433 |  | 
 | 434 |     Arguments: | 
 | 435 |       chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE. | 
 | 436 |       chunk_offset: Offset in the sparse file where this chunk begins. | 
 | 437 |       output_offset: Offset in de-sparsified file. | 
 | 438 |       output_size: Number of bytes in output. | 
 | 439 |       input_offset: Offset in sparse file if TYPE_RAW otherwise None. | 
 | 440 |       fill_data: Blob with data to fill if TYPE_FILL otherwise None. | 
 | 441 |  | 
 | 442 |     Raises: | 
 | 443 |       ValueError: If data is not well-formed. | 
 | 444 |     """ | 
 | 445 |     self.chunk_type = chunk_type | 
 | 446 |     self.chunk_offset = chunk_offset | 
 | 447 |     self.output_offset = output_offset | 
 | 448 |     self.output_size = output_size | 
 | 449 |     self.input_offset = input_offset | 
 | 450 |     self.fill_data = fill_data | 
 | 451 |     # Check invariants. | 
 | 452 |     if self.chunk_type == self.TYPE_RAW: | 
 | 453 |       if self.fill_data is not None: | 
 | 454 |         raise ValueError('RAW chunk cannot have fill_data set.') | 
 | 455 |       if not self.input_offset: | 
 | 456 |         raise ValueError('RAW chunk must have input_offset set.') | 
 | 457 |     elif self.chunk_type == self.TYPE_FILL: | 
 | 458 |       if self.fill_data is None: | 
 | 459 |         raise ValueError('FILL chunk must have fill_data set.') | 
 | 460 |       if self.input_offset: | 
 | 461 |         raise ValueError('FILL chunk cannot have input_offset set.') | 
 | 462 |     elif self.chunk_type == self.TYPE_DONT_CARE: | 
 | 463 |       if self.fill_data is not None: | 
 | 464 |         raise ValueError('DONT_CARE chunk cannot have fill_data set.') | 
 | 465 |       if self.input_offset: | 
 | 466 |         raise ValueError('DONT_CARE chunk cannot have input_offset set.') | 
 | 467 |     else: | 
 | 468 |       raise ValueError('Invalid chunk type') | 
 | 469 |  | 
 | 470 |  | 
 | 471 | class ImageHandler(object): | 
 | 472 |   """Abstraction for image I/O with support for Android sparse images. | 
 | 473 |  | 
 | 474 |   This class provides an interface for working with image files that | 
 | 475 |   may be using the Android Sparse Image format. When an instance is | 
 | 476 |   constructed, we test whether it's an Android sparse file. If so, | 
 | 477 |   operations will be on the sparse file by interpreting the sparse | 
 | 478 |   format, otherwise they will be directly on the file. Either way the | 
 | 479 |   operations do the same. | 
 | 480 |  | 
 | 481 |   For reading, this interface mimics a file object - it has seek(), | 
 | 482 |   tell(), and read() methods. For writing, only truncation | 
 | 483 |   (truncate()) and appending is supported (append_raw() and | 
 | 484 |   append_dont_care()). Additionally, data can only be written in units | 
 | 485 |   of the block size. | 
 | 486 |  | 
 | 487 |   Attributes: | 
 | 488 |     is_sparse: Whether the file being operated on is sparse. | 
 | 489 |     block_size: The block size, typically 4096. | 
 | 490 |     image_size: The size of the unsparsified file. | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 491 |   """ | 
 | 492 |   # See system/core/libsparse/sparse_format.h for details. | 
 | 493 |   MAGIC = 0xed26ff3a | 
 | 494 |   HEADER_FORMAT = '<I4H4I' | 
 | 495 |  | 
 | 496 |   # These are formats and offset of just the |total_chunks| and | 
 | 497 |   # |total_blocks| fields. | 
 | 498 |   NUM_CHUNKS_AND_BLOCKS_FORMAT = '<II' | 
 | 499 |   NUM_CHUNKS_AND_BLOCKS_OFFSET = 16 | 
 | 500 |  | 
 | 501 |   def __init__(self, image_filename): | 
 | 502 |     """Initializes an image handler. | 
 | 503 |  | 
 | 504 |     Arguments: | 
 | 505 |       image_filename: The name of the file to operate on. | 
 | 506 |  | 
 | 507 |     Raises: | 
 | 508 |       ValueError: If data in the file is invalid. | 
 | 509 |     """ | 
 | 510 |     self._image_filename = image_filename | 
 | 511 |     self._read_header() | 
 | 512 |  | 
 | 513 |   def _read_header(self): | 
 | 514 |     """Initializes internal data structures used for reading file. | 
 | 515 |  | 
 | 516 |     This may be called multiple times and is typically called after | 
 | 517 |     modifying the file (e.g. appending, truncation). | 
 | 518 |  | 
 | 519 |     Raises: | 
 | 520 |       ValueError: If data in the file is invalid. | 
 | 521 |     """ | 
 | 522 |     self.is_sparse = False | 
 | 523 |     self.block_size = 4096 | 
 | 524 |     self._file_pos = 0 | 
 | 525 |     self._image = open(self._image_filename, 'r+b') | 
 | 526 |     self._image.seek(0, os.SEEK_END) | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 527 |     self.image_size = self._image.tell() | 
 | 528 |  | 
 | 529 |     self._image.seek(0, os.SEEK_SET) | 
 | 530 |     header_bin = self._image.read(struct.calcsize(self.HEADER_FORMAT)) | 
 | 531 |     (magic, major_version, minor_version, file_hdr_sz, chunk_hdr_sz, | 
 | 532 |      block_size, self._num_total_blocks, self._num_total_chunks, | 
 | 533 |      _) = struct.unpack(self.HEADER_FORMAT, header_bin) | 
 | 534 |     if magic != self.MAGIC: | 
 | 535 |       # Not a sparse image, our job here is done. | 
 | 536 |       return | 
 | 537 |     if not (major_version == 1 and minor_version == 0): | 
 | 538 |       raise ValueError('Encountered sparse image format version {}.{} but ' | 
 | 539 |                        'only 1.0 is supported'.format(major_version, | 
 | 540 |                                                       minor_version)) | 
 | 541 |     if file_hdr_sz != struct.calcsize(self.HEADER_FORMAT): | 
 | 542 |       raise ValueError('Unexpected file_hdr_sz value {}.'. | 
 | 543 |                        format(file_hdr_sz)) | 
 | 544 |     if chunk_hdr_sz != struct.calcsize(ImageChunk.FORMAT): | 
 | 545 |       raise ValueError('Unexpected chunk_hdr_sz value {}.'. | 
 | 546 |                        format(chunk_hdr_sz)) | 
 | 547 |  | 
 | 548 |     self.block_size = block_size | 
 | 549 |  | 
 | 550 |     # Build an list of chunks by parsing the file. | 
 | 551 |     self._chunks = [] | 
 | 552 |  | 
 | 553 |     # Find the smallest offset where only "Don't care" chunks | 
 | 554 |     # follow. This will be the size of the content in the sparse | 
 | 555 |     # image. | 
 | 556 |     offset = 0 | 
 | 557 |     output_offset = 0 | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 558 |     for _ in xrange(1, self._num_total_chunks + 1): | 
 | 559 |       chunk_offset = self._image.tell() | 
 | 560 |  | 
 | 561 |       header_bin = self._image.read(struct.calcsize(ImageChunk.FORMAT)) | 
 | 562 |       (chunk_type, _, chunk_sz, total_sz) = struct.unpack(ImageChunk.FORMAT, | 
 | 563 |                                                           header_bin) | 
 | 564 |       data_sz = total_sz - struct.calcsize(ImageChunk.FORMAT) | 
 | 565 |  | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 566 |       if chunk_type == ImageChunk.TYPE_RAW: | 
 | 567 |         if data_sz != (chunk_sz * self.block_size): | 
 | 568 |           raise ValueError('Raw chunk input size ({}) does not match output ' | 
 | 569 |                            'size ({})'. | 
 | 570 |                            format(data_sz, chunk_sz*self.block_size)) | 
 | 571 |         self._chunks.append(ImageChunk(ImageChunk.TYPE_RAW, | 
 | 572 |                                        chunk_offset, | 
 | 573 |                                        output_offset, | 
 | 574 |                                        chunk_sz*self.block_size, | 
 | 575 |                                        self._image.tell(), | 
 | 576 |                                        None)) | 
 | 577 |         self._image.read(data_sz) | 
 | 578 |  | 
 | 579 |       elif chunk_type == ImageChunk.TYPE_FILL: | 
 | 580 |         if data_sz != 4: | 
 | 581 |           raise ValueError('Fill chunk should have 4 bytes of fill, but this ' | 
 | 582 |                            'has {}'.format(data_sz)) | 
 | 583 |         fill_data = self._image.read(4) | 
 | 584 |         self._chunks.append(ImageChunk(ImageChunk.TYPE_FILL, | 
 | 585 |                                        chunk_offset, | 
 | 586 |                                        output_offset, | 
 | 587 |                                        chunk_sz*self.block_size, | 
 | 588 |                                        None, | 
 | 589 |                                        fill_data)) | 
 | 590 |       elif chunk_type == ImageChunk.TYPE_DONT_CARE: | 
 | 591 |         if data_sz != 0: | 
 | 592 |           raise ValueError('Don\'t care chunk input size is non-zero ({})'. | 
 | 593 |                            format(data_sz)) | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 594 |         self._chunks.append(ImageChunk(ImageChunk.TYPE_DONT_CARE, | 
 | 595 |                                        chunk_offset, | 
 | 596 |                                        output_offset, | 
 | 597 |                                        chunk_sz*self.block_size, | 
 | 598 |                                        None, | 
 | 599 |                                        None)) | 
 | 600 |       elif chunk_type == ImageChunk.TYPE_CRC32: | 
 | 601 |         if data_sz != 4: | 
 | 602 |           raise ValueError('CRC32 chunk should have 4 bytes of CRC, but ' | 
 | 603 |                            'this has {}'.format(data_sz)) | 
 | 604 |         self._image.read(4) | 
 | 605 |       else: | 
 | 606 |         raise ValueError('Unknown chunk type {}'.format(chunk_type)) | 
 | 607 |  | 
 | 608 |       offset += chunk_sz | 
 | 609 |       output_offset += chunk_sz*self.block_size | 
 | 610 |  | 
 | 611 |     # Record where sparse data end. | 
 | 612 |     self._sparse_end = self._image.tell() | 
 | 613 |  | 
 | 614 |     # Now that we've traversed all chunks, sanity check. | 
 | 615 |     if self._num_total_blocks != offset: | 
 | 616 |       raise ValueError('The header said we should have {} output blocks, ' | 
 | 617 |                        'but we saw {}'.format(self._num_total_blocks, offset)) | 
 | 618 |     junk_len = len(self._image.read()) | 
 | 619 |     if junk_len > 0: | 
 | 620 |       raise ValueError('There were {} bytes of extra data at the end of the ' | 
 | 621 |                        'file.'.format(junk_len)) | 
 | 622 |  | 
| David Zeuthen | 0969269 | 2016-09-30 16:16:40 -0400 | [diff] [blame] | 623 |     # Assign |image_size|. | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 624 |     self.image_size = output_offset | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 625 |  | 
 | 626 |     # This is used when bisecting in read() to find the initial slice. | 
 | 627 |     self._chunk_output_offsets = [i.output_offset for i in self._chunks] | 
 | 628 |  | 
 | 629 |     self.is_sparse = True | 
 | 630 |  | 
 | 631 |   def _update_chunks_and_blocks(self): | 
 | 632 |     """Helper function to update the image header. | 
 | 633 |  | 
 | 634 |     The the |total_chunks| and |total_blocks| fields in the header | 
 | 635 |     will be set to value of the |_num_total_blocks| and | 
 | 636 |     |_num_total_chunks| attributes. | 
 | 637 |  | 
 | 638 |     """ | 
 | 639 |     self._image.seek(self.NUM_CHUNKS_AND_BLOCKS_OFFSET, os.SEEK_SET) | 
 | 640 |     self._image.write(struct.pack(self.NUM_CHUNKS_AND_BLOCKS_FORMAT, | 
 | 641 |                                   self._num_total_blocks, | 
 | 642 |                                   self._num_total_chunks)) | 
 | 643 |  | 
 | 644 |   def append_dont_care(self, num_bytes): | 
 | 645 |     """Appends a DONT_CARE chunk to the sparse file. | 
 | 646 |  | 
 | 647 |     The given number of bytes must be a multiple of the block size. | 
 | 648 |  | 
 | 649 |     Arguments: | 
 | 650 |       num_bytes: Size in number of bytes of the DONT_CARE chunk. | 
 | 651 |     """ | 
 | 652 |     assert num_bytes % self.block_size == 0 | 
 | 653 |  | 
 | 654 |     if not self.is_sparse: | 
 | 655 |       self._image.seek(0, os.SEEK_END) | 
 | 656 |       # This is more efficient that writing NUL bytes since it'll add | 
 | 657 |       # a hole on file systems that support sparse files (native | 
 | 658 |       # sparse, not Android sparse). | 
 | 659 |       self._image.truncate(self._image.tell() + num_bytes) | 
 | 660 |       self._read_header() | 
 | 661 |       return | 
 | 662 |  | 
 | 663 |     self._num_total_chunks += 1 | 
 | 664 |     self._num_total_blocks += num_bytes / self.block_size | 
 | 665 |     self._update_chunks_and_blocks() | 
 | 666 |  | 
 | 667 |     self._image.seek(self._sparse_end, os.SEEK_SET) | 
 | 668 |     self._image.write(struct.pack(ImageChunk.FORMAT, | 
 | 669 |                                   ImageChunk.TYPE_DONT_CARE, | 
 | 670 |                                   0,  # Reserved | 
 | 671 |                                   num_bytes / self.block_size, | 
 | 672 |                                   struct.calcsize(ImageChunk.FORMAT))) | 
 | 673 |     self._read_header() | 
 | 674 |  | 
 | 675 |   def append_raw(self, data): | 
 | 676 |     """Appends a RAW chunk to the sparse file. | 
 | 677 |  | 
 | 678 |     The length of the given data must be a multiple of the block size. | 
 | 679 |  | 
 | 680 |     Arguments: | 
 | 681 |       data: Data to append. | 
 | 682 |     """ | 
 | 683 |     assert len(data) % self.block_size == 0 | 
 | 684 |  | 
 | 685 |     if not self.is_sparse: | 
 | 686 |       self._image.seek(0, os.SEEK_END) | 
 | 687 |       self._image.write(data) | 
 | 688 |       self._read_header() | 
 | 689 |       return | 
 | 690 |  | 
 | 691 |     self._num_total_chunks += 1 | 
 | 692 |     self._num_total_blocks += len(data) / self.block_size | 
 | 693 |     self._update_chunks_and_blocks() | 
 | 694 |  | 
 | 695 |     self._image.seek(self._sparse_end, os.SEEK_SET) | 
 | 696 |     self._image.write(struct.pack(ImageChunk.FORMAT, | 
 | 697 |                                   ImageChunk.TYPE_RAW, | 
 | 698 |                                   0,  # Reserved | 
 | 699 |                                   len(data) / self.block_size, | 
 | 700 |                                   len(data) + | 
 | 701 |                                   struct.calcsize(ImageChunk.FORMAT))) | 
 | 702 |     self._image.write(data) | 
 | 703 |     self._read_header() | 
 | 704 |  | 
 | 705 |   def append_fill(self, fill_data, size): | 
 | 706 |     """Appends a fill chunk to the sparse file. | 
 | 707 |  | 
 | 708 |     The total length of the fill data must be a multiple of the block size. | 
 | 709 |  | 
 | 710 |     Arguments: | 
 | 711 |       fill_data: Fill data to append - must be four bytes. | 
 | 712 |       size: Number of chunk - must be a multiple of four and the block size. | 
 | 713 |     """ | 
 | 714 |     assert len(fill_data) == 4 | 
 | 715 |     assert size % 4 == 0 | 
 | 716 |     assert size % self.block_size == 0 | 
 | 717 |  | 
 | 718 |     if not self.is_sparse: | 
 | 719 |       self._image.seek(0, os.SEEK_END) | 
 | 720 |       self._image.write(fill_data * (size/4)) | 
 | 721 |       self._read_header() | 
 | 722 |       return | 
 | 723 |  | 
 | 724 |     self._num_total_chunks += 1 | 
 | 725 |     self._num_total_blocks += size / self.block_size | 
 | 726 |     self._update_chunks_and_blocks() | 
 | 727 |  | 
 | 728 |     self._image.seek(self._sparse_end, os.SEEK_SET) | 
 | 729 |     self._image.write(struct.pack(ImageChunk.FORMAT, | 
 | 730 |                                   ImageChunk.TYPE_FILL, | 
 | 731 |                                   0,  # Reserved | 
 | 732 |                                   size / self.block_size, | 
 | 733 |                                   4 + struct.calcsize(ImageChunk.FORMAT))) | 
 | 734 |     self._image.write(fill_data) | 
 | 735 |     self._read_header() | 
 | 736 |  | 
 | 737 |   def seek(self, offset): | 
 | 738 |     """Sets the cursor position for reading from unsparsified file. | 
 | 739 |  | 
 | 740 |     Arguments: | 
 | 741 |       offset: Offset to seek to from the beginning of the file. | 
 | 742 |     """ | 
 | 743 |     self._file_pos = offset | 
 | 744 |  | 
 | 745 |   def read(self, size): | 
 | 746 |     """Reads data from the unsparsified file. | 
 | 747 |  | 
 | 748 |     This method may return fewer than |size| bytes of data if the end | 
 | 749 |     of the file was encountered. | 
 | 750 |  | 
 | 751 |     The file cursor for reading is advanced by the number of bytes | 
 | 752 |     read. | 
 | 753 |  | 
 | 754 |     Arguments: | 
 | 755 |       size: Number of bytes to read. | 
 | 756 |  | 
 | 757 |     Returns: | 
 | 758 |       The data. | 
 | 759 |  | 
 | 760 |     """ | 
 | 761 |     if not self.is_sparse: | 
 | 762 |       self._image.seek(self._file_pos) | 
 | 763 |       data = self._image.read(size) | 
 | 764 |       self._file_pos += len(data) | 
 | 765 |       return data | 
 | 766 |  | 
 | 767 |     # Iterate over all chunks. | 
 | 768 |     chunk_idx = bisect.bisect_right(self._chunk_output_offsets, | 
 | 769 |                                     self._file_pos) - 1 | 
 | 770 |     data = bytearray() | 
 | 771 |     to_go = size | 
 | 772 |     while to_go > 0: | 
 | 773 |       chunk = self._chunks[chunk_idx] | 
 | 774 |       chunk_pos_offset = self._file_pos - chunk.output_offset | 
 | 775 |       chunk_pos_to_go = min(chunk.output_size - chunk_pos_offset, to_go) | 
 | 776 |  | 
 | 777 |       if chunk.chunk_type == ImageChunk.TYPE_RAW: | 
 | 778 |         self._image.seek(chunk.input_offset + chunk_pos_offset) | 
 | 779 |         data.extend(self._image.read(chunk_pos_to_go)) | 
 | 780 |       elif chunk.chunk_type == ImageChunk.TYPE_FILL: | 
 | 781 |         all_data = chunk.fill_data*(chunk_pos_to_go/len(chunk.fill_data) + 2) | 
 | 782 |         offset_mod = chunk_pos_offset % len(chunk.fill_data) | 
 | 783 |         data.extend(all_data[offset_mod:(offset_mod + chunk_pos_to_go)]) | 
 | 784 |       else: | 
 | 785 |         assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE | 
 | 786 |         data.extend('\0' * chunk_pos_to_go) | 
 | 787 |  | 
 | 788 |       to_go -= chunk_pos_to_go | 
 | 789 |       self._file_pos += chunk_pos_to_go | 
 | 790 |       chunk_idx += 1 | 
 | 791 |       # Generate partial read in case of EOF. | 
 | 792 |       if chunk_idx >= len(self._chunks): | 
 | 793 |         break | 
 | 794 |  | 
 | 795 |     return data | 
 | 796 |  | 
 | 797 |   def tell(self): | 
 | 798 |     """Returns the file cursor position for reading from unsparsified file. | 
 | 799 |  | 
 | 800 |     Returns: | 
 | 801 |       The file cursor position for reading. | 
 | 802 |     """ | 
 | 803 |     return self._file_pos | 
 | 804 |  | 
 | 805 |   def truncate(self, size): | 
 | 806 |     """Truncates the unsparsified file. | 
 | 807 |  | 
 | 808 |     Arguments: | 
 | 809 |       size: Desired size of unsparsified file. | 
 | 810 |  | 
 | 811 |     Raises: | 
 | 812 |       ValueError: If desired size isn't a multiple of the block size. | 
 | 813 |     """ | 
 | 814 |     if not self.is_sparse: | 
 | 815 |       self._image.truncate(size) | 
 | 816 |       self._read_header() | 
 | 817 |       return | 
 | 818 |  | 
 | 819 |     if size % self.block_size != 0: | 
 | 820 |       raise ValueError('Cannot truncate to a size which is not a multiple ' | 
 | 821 |                        'of the block size') | 
 | 822 |  | 
 | 823 |     if size == self.image_size: | 
 | 824 |       # Trivial where there's nothing to do. | 
 | 825 |       return | 
 | 826 |     elif size < self.image_size: | 
 | 827 |       chunk_idx = bisect.bisect_right(self._chunk_output_offsets, size) - 1 | 
 | 828 |       chunk = self._chunks[chunk_idx] | 
 | 829 |       if chunk.output_offset != size: | 
 | 830 |         # Truncation in the middle of a trunk - need to keep the chunk | 
 | 831 |         # and modify it. | 
 | 832 |         chunk_idx_for_update = chunk_idx + 1 | 
 | 833 |         num_to_keep = size - chunk.output_offset | 
 | 834 |         assert num_to_keep % self.block_size == 0 | 
 | 835 |         if chunk.chunk_type == ImageChunk.TYPE_RAW: | 
 | 836 |           truncate_at = (chunk.chunk_offset + | 
 | 837 |                          struct.calcsize(ImageChunk.FORMAT) + num_to_keep) | 
 | 838 |           data_sz = num_to_keep | 
 | 839 |         elif chunk.chunk_type == ImageChunk.TYPE_FILL: | 
 | 840 |           truncate_at = (chunk.chunk_offset + | 
 | 841 |                          struct.calcsize(ImageChunk.FORMAT) + 4) | 
 | 842 |           data_sz = 4 | 
 | 843 |         else: | 
 | 844 |           assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE | 
 | 845 |           truncate_at = chunk.chunk_offset + struct.calcsize(ImageChunk.FORMAT) | 
 | 846 |           data_sz = 0 | 
 | 847 |         chunk_sz = num_to_keep/self.block_size | 
 | 848 |         total_sz = data_sz + struct.calcsize(ImageChunk.FORMAT) | 
 | 849 |         self._image.seek(chunk.chunk_offset) | 
 | 850 |         self._image.write(struct.pack(ImageChunk.FORMAT, | 
 | 851 |                                       chunk.chunk_type, | 
 | 852 |                                       0,  # Reserved | 
 | 853 |                                       chunk_sz, | 
 | 854 |                                       total_sz)) | 
 | 855 |         chunk.output_size = num_to_keep | 
 | 856 |       else: | 
 | 857 |         # Truncation at trunk boundary. | 
 | 858 |         truncate_at = chunk.chunk_offset | 
 | 859 |         chunk_idx_for_update = chunk_idx | 
 | 860 |  | 
 | 861 |       self._num_total_chunks = chunk_idx_for_update | 
 | 862 |       self._num_total_blocks = 0 | 
 | 863 |       for i in range(0, chunk_idx_for_update): | 
 | 864 |         self._num_total_blocks += self._chunks[i].output_size / self.block_size | 
 | 865 |       self._update_chunks_and_blocks() | 
 | 866 |       self._image.truncate(truncate_at) | 
 | 867 |  | 
 | 868 |       # We've modified the file so re-read all data. | 
 | 869 |       self._read_header() | 
 | 870 |     else: | 
 | 871 |       # Truncating to grow - just add a DONT_CARE section. | 
 | 872 |       self.append_dont_care(size - self.image_size) | 
 | 873 |  | 
 | 874 |  | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 875 | class AvbDescriptor(object): | 
 | 876 |   """Class for AVB descriptor. | 
 | 877 |  | 
 | 878 |   See the |AvbDescriptor| C struct for more information. | 
 | 879 |  | 
 | 880 |   Attributes: | 
 | 881 |     tag: The tag identifying what kind of descriptor this is. | 
 | 882 |     data: The data in the descriptor. | 
 | 883 |   """ | 
 | 884 |  | 
 | 885 |   SIZE = 16 | 
 | 886 |   FORMAT_STRING = ('!QQ')  # tag, num_bytes_following (descriptor header) | 
 | 887 |  | 
 | 888 |   def __init__(self, data): | 
 | 889 |     """Initializes a new property descriptor. | 
 | 890 |  | 
 | 891 |     Arguments: | 
 | 892 |       data: If not None, must be a bytearray(). | 
 | 893 |  | 
 | 894 |     Raises: | 
 | 895 |       LookupError: If the given descriptor is malformed. | 
 | 896 |     """ | 
 | 897 |     assert struct.calcsize(self.FORMAT_STRING) == self.SIZE | 
 | 898 |  | 
 | 899 |     if data: | 
 | 900 |       (self.tag, num_bytes_following) = ( | 
 | 901 |           struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])) | 
 | 902 |       self.data = data[self.SIZE:self.SIZE + num_bytes_following] | 
 | 903 |     else: | 
 | 904 |       self.tag = None | 
 | 905 |       self.data = None | 
 | 906 |  | 
 | 907 |   def print_desc(self, o): | 
 | 908 |     """Print the descriptor. | 
 | 909 |  | 
 | 910 |     Arguments: | 
 | 911 |       o: The object to write the output to. | 
 | 912 |     """ | 
 | 913 |     o.write('    Unknown descriptor:\n') | 
 | 914 |     o.write('      Tag:  {}\n'.format(self.tag)) | 
 | 915 |     if len(self.data) < 256: | 
 | 916 |       o.write('      Data: {} ({} bytes)\n'.format( | 
 | 917 |           repr(str(self.data)), len(self.data))) | 
 | 918 |     else: | 
 | 919 |       o.write('      Data: {} bytes\n'.format(len(self.data))) | 
 | 920 |  | 
 | 921 |   def encode(self): | 
 | 922 |     """Serializes the descriptor. | 
 | 923 |  | 
 | 924 |     Returns: | 
 | 925 |       A bytearray() with the descriptor data. | 
 | 926 |     """ | 
 | 927 |     num_bytes_following = len(self.data) | 
 | 928 |     nbf_with_padding = round_to_multiple(num_bytes_following, 8) | 
 | 929 |     padding_size = nbf_with_padding - num_bytes_following | 
 | 930 |     desc = struct.pack(self.FORMAT_STRING, self.tag, nbf_with_padding) | 
 | 931 |     padding = struct.pack(str(padding_size) + 'x') | 
 | 932 |     ret = desc + self.data + padding | 
 | 933 |     return bytearray(ret) | 
 | 934 |  | 
 | 935 |  | 
 | 936 | class AvbPropertyDescriptor(AvbDescriptor): | 
 | 937 |   """A class for property descriptors. | 
 | 938 |  | 
 | 939 |   See the |AvbPropertyDescriptor| C struct for more information. | 
 | 940 |  | 
 | 941 |   Attributes: | 
 | 942 |     key: The key. | 
 | 943 |     value: The key. | 
 | 944 |   """ | 
 | 945 |  | 
 | 946 |   TAG = 0 | 
 | 947 |   SIZE = 32 | 
 | 948 |   FORMAT_STRING = ('!QQ'  # tag, num_bytes_following (descriptor header) | 
 | 949 |                    'Q'  # key size (bytes) | 
 | 950 |                    'Q')  # value size (bytes) | 
 | 951 |  | 
 | 952 |   def __init__(self, data=None): | 
 | 953 |     """Initializes a new property descriptor. | 
 | 954 |  | 
 | 955 |     Arguments: | 
 | 956 |       data: If not None, must be a bytearray of size |SIZE|. | 
 | 957 |  | 
 | 958 |     Raises: | 
 | 959 |       LookupError: If the given descriptor is malformed. | 
 | 960 |     """ | 
 | 961 |     AvbDescriptor.__init__(self, None) | 
 | 962 |     assert struct.calcsize(self.FORMAT_STRING) == self.SIZE | 
 | 963 |  | 
 | 964 |     if data: | 
 | 965 |       (tag, num_bytes_following, key_size, | 
 | 966 |        value_size) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]) | 
 | 967 |       expected_size = round_to_multiple( | 
 | 968 |           self.SIZE - 16 + key_size + 1 + value_size + 1, 8) | 
 | 969 |       if tag != self.TAG or num_bytes_following != expected_size: | 
 | 970 |         raise LookupError('Given data does not look like a property ' | 
 | 971 |                           'descriptor.') | 
 | 972 |       self.key = data[self.SIZE:(self.SIZE + key_size)] | 
 | 973 |       self.value = data[(self.SIZE + key_size + 1):(self.SIZE + key_size + 1 + | 
 | 974 |                                                     value_size)] | 
 | 975 |     else: | 
 | 976 |       self.key = '' | 
 | 977 |       self.value = '' | 
 | 978 |  | 
 | 979 |   def print_desc(self, o): | 
 | 980 |     """Print the descriptor. | 
 | 981 |  | 
 | 982 |     Arguments: | 
 | 983 |       o: The object to write the output to. | 
 | 984 |     """ | 
 | 985 |     if len(self.value) < 256: | 
 | 986 |       o.write('    Prop: {} -> {}\n'.format(self.key, repr(str(self.value)))) | 
 | 987 |     else: | 
 | 988 |       o.write('    Prop: {} -> ({} bytes)\n'.format(self.key, len(self.value))) | 
 | 989 |  | 
 | 990 |   def encode(self): | 
 | 991 |     """Serializes the descriptor. | 
 | 992 |  | 
 | 993 |     Returns: | 
 | 994 |       A bytearray() with the descriptor data. | 
 | 995 |     """ | 
 | 996 |     num_bytes_following = self.SIZE + len(self.key) + len(self.value) + 2 - 16 | 
 | 997 |     nbf_with_padding = round_to_multiple(num_bytes_following, 8) | 
 | 998 |     padding_size = nbf_with_padding - num_bytes_following | 
 | 999 |     desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding, | 
 | 1000 |                        len(self.key), len(self.value)) | 
 | 1001 |     padding = struct.pack(str(padding_size) + 'x') | 
 | 1002 |     ret = desc + self.key + '\0' + self.value + '\0' + padding | 
 | 1003 |     return bytearray(ret) | 
 | 1004 |  | 
 | 1005 |  | 
 | 1006 | class AvbHashtreeDescriptor(AvbDescriptor): | 
 | 1007 |   """A class for hashtree descriptors. | 
 | 1008 |  | 
 | 1009 |   See the |AvbHashtreeDescriptor| C struct for more information. | 
 | 1010 |  | 
 | 1011 |   Attributes: | 
 | 1012 |     dm_verity_version: dm-verity version used. | 
 | 1013 |     image_size: Size of the image, after rounding up to |block_size|. | 
 | 1014 |     tree_offset: Offset of the hash tree in the file. | 
 | 1015 |     tree_size: Size of the tree. | 
 | 1016 |     data_block_size: Data block size | 
 | 1017 |     hash_block_size: Hash block size | 
| David Zeuthen | 0b7f1d3 | 2016-10-25 17:53:49 -0400 | [diff] [blame] | 1018 |     fec_num_roots: Number of roots used for FEC (0 if FEC is not used). | 
 | 1019 |     fec_offset: Offset of FEC data (0 if FEC is not used). | 
 | 1020 |     fec_size: Size of FEC data (0 if FEC is not used). | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1021 |     hash_algorithm: Hash algorithm used. | 
 | 1022 |     partition_name: Partition name. | 
 | 1023 |     salt: Salt used. | 
 | 1024 |     root_digest: Root digest. | 
 | 1025 |   """ | 
 | 1026 |  | 
 | 1027 |   TAG = 1 | 
| David Zeuthen | 5cb2db9 | 2016-10-27 15:14:14 -0400 | [diff] [blame] | 1028 |   RESERVED = 64 | 
 | 1029 |   SIZE = 116 + RESERVED | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1030 |   FORMAT_STRING = ('!QQ'  # tag, num_bytes_following (descriptor header) | 
 | 1031 |                    'L'  # dm-verity version used | 
 | 1032 |                    'Q'  # image size (bytes) | 
 | 1033 |                    'Q'  # tree offset (bytes) | 
 | 1034 |                    'Q'  # tree size (bytes) | 
 | 1035 |                    'L'  # data block size (bytes) | 
 | 1036 |                    'L'  # hash block size (bytes) | 
| David Zeuthen | 0b7f1d3 | 2016-10-25 17:53:49 -0400 | [diff] [blame] | 1037 |                    'L'  # FEC number of roots | 
 | 1038 |                    'Q'  # FEC offset (bytes) | 
 | 1039 |                    'Q'  # FEC size (bytes) | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1040 |                    '32s'  # hash algorithm used | 
 | 1041 |                    'L'  # partition name (bytes) | 
 | 1042 |                    'L'  # salt length (bytes) | 
| David Zeuthen | 5cb2db9 | 2016-10-27 15:14:14 -0400 | [diff] [blame] | 1043 |                    'L' +  # root digest length (bytes) | 
 | 1044 |                    str(RESERVED) + 's')  # reserved | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1045 |  | 
 | 1046 |   def __init__(self, data=None): | 
 | 1047 |     """Initializes a new hashtree descriptor. | 
 | 1048 |  | 
 | 1049 |     Arguments: | 
 | 1050 |       data: If not None, must be a bytearray of size |SIZE|. | 
 | 1051 |  | 
 | 1052 |     Raises: | 
 | 1053 |       LookupError: If the given descriptor is malformed. | 
 | 1054 |     """ | 
 | 1055 |     AvbDescriptor.__init__(self, None) | 
 | 1056 |     assert struct.calcsize(self.FORMAT_STRING) == self.SIZE | 
 | 1057 |  | 
 | 1058 |     if data: | 
 | 1059 |       (tag, num_bytes_following, self.dm_verity_version, self.image_size, | 
 | 1060 |        self.tree_offset, self.tree_size, self.data_block_size, | 
| David Zeuthen | 0b7f1d3 | 2016-10-25 17:53:49 -0400 | [diff] [blame] | 1061 |        self.hash_block_size, self.fec_num_roots, self.fec_offset, self.fec_size, | 
 | 1062 |        self.hash_algorithm, partition_name_len, salt_len, | 
| David Zeuthen | 5cb2db9 | 2016-10-27 15:14:14 -0400 | [diff] [blame] | 1063 |        root_digest_len, _) = struct.unpack(self.FORMAT_STRING, | 
 | 1064 |                                            data[0:self.SIZE]) | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1065 |       expected_size = round_to_multiple( | 
 | 1066 |           self.SIZE - 16 + partition_name_len + salt_len + root_digest_len, 8) | 
 | 1067 |       if tag != self.TAG or num_bytes_following != expected_size: | 
 | 1068 |         raise LookupError('Given data does not look like a hashtree ' | 
 | 1069 |                           'descriptor.') | 
 | 1070 |       # Nuke NUL-bytes at the end. | 
 | 1071 |       self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0] | 
 | 1072 |       o = 0 | 
 | 1073 |       self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o + | 
 | 1074 |                                                       partition_name_len)]) | 
 | 1075 |       # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8. | 
 | 1076 |       self.partition_name.decode('utf-8') | 
 | 1077 |       o += partition_name_len | 
 | 1078 |       self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)] | 
 | 1079 |       o += salt_len | 
 | 1080 |       self.root_digest = data[(self.SIZE + o):(self.SIZE + o + root_digest_len)] | 
 | 1081 |       if root_digest_len != len(hashlib.new(name=self.hash_algorithm).digest()): | 
 | 1082 |         raise LookupError('root_digest_len doesn\'t match hash algorithm') | 
 | 1083 |  | 
 | 1084 |     else: | 
 | 1085 |       self.dm_verity_version = 0 | 
 | 1086 |       self.image_size = 0 | 
 | 1087 |       self.tree_offset = 0 | 
 | 1088 |       self.tree_size = 0 | 
 | 1089 |       self.data_block_size = 0 | 
 | 1090 |       self.hash_block_size = 0 | 
| David Zeuthen | 0b7f1d3 | 2016-10-25 17:53:49 -0400 | [diff] [blame] | 1091 |       self.fec_num_roots = 0 | 
 | 1092 |       self.fec_offset = 0 | 
 | 1093 |       self.fec_size = 0 | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1094 |       self.hash_algorithm = '' | 
 | 1095 |       self.partition_name = '' | 
 | 1096 |       self.salt = bytearray() | 
 | 1097 |       self.root_digest = bytearray() | 
 | 1098 |  | 
 | 1099 |   def print_desc(self, o): | 
 | 1100 |     """Print the descriptor. | 
 | 1101 |  | 
 | 1102 |     Arguments: | 
 | 1103 |       o: The object to write the output to. | 
 | 1104 |     """ | 
 | 1105 |     o.write('    Hashtree descriptor:\n') | 
 | 1106 |     o.write('      Version of dm-verity:  {}\n'.format(self.dm_verity_version)) | 
 | 1107 |     o.write('      Image Size:            {} bytes\n'.format(self.image_size)) | 
 | 1108 |     o.write('      Tree Offset:           {}\n'.format(self.tree_offset)) | 
 | 1109 |     o.write('      Tree Size:             {} bytes\n'.format(self.tree_size)) | 
 | 1110 |     o.write('      Data Block Size:       {} bytes\n'.format( | 
 | 1111 |         self.data_block_size)) | 
 | 1112 |     o.write('      Hash Block Size:       {} bytes\n'.format( | 
 | 1113 |         self.hash_block_size)) | 
| David Zeuthen | 0b7f1d3 | 2016-10-25 17:53:49 -0400 | [diff] [blame] | 1114 |     o.write('      FEC num roots:         {}\n'.format(self.fec_num_roots)) | 
 | 1115 |     o.write('      FEC offset:            {}\n'.format(self.fec_offset)) | 
 | 1116 |     o.write('      FEC size:              {} bytes\n'.format(self.fec_size)) | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1117 |     o.write('      Hash Algorithm:        {}\n'.format(self.hash_algorithm)) | 
 | 1118 |     o.write('      Partition Name:        {}\n'.format(self.partition_name)) | 
 | 1119 |     o.write('      Salt:                  {}\n'.format(str(self.salt).encode( | 
 | 1120 |         'hex'))) | 
 | 1121 |     o.write('      Root Digest:           {}\n'.format(str( | 
 | 1122 |         self.root_digest).encode('hex'))) | 
 | 1123 |  | 
 | 1124 |   def encode(self): | 
 | 1125 |     """Serializes the descriptor. | 
 | 1126 |  | 
 | 1127 |     Returns: | 
 | 1128 |       A bytearray() with the descriptor data. | 
 | 1129 |     """ | 
 | 1130 |     encoded_name = self.partition_name.encode('utf-8') | 
 | 1131 |     num_bytes_following = (self.SIZE + len(encoded_name) + len(self.salt) + | 
 | 1132 |                            len(self.root_digest) - 16) | 
 | 1133 |     nbf_with_padding = round_to_multiple(num_bytes_following, 8) | 
 | 1134 |     padding_size = nbf_with_padding - num_bytes_following | 
 | 1135 |     desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding, | 
 | 1136 |                        self.dm_verity_version, self.image_size, | 
 | 1137 |                        self.tree_offset, self.tree_size, self.data_block_size, | 
| David Zeuthen | 0b7f1d3 | 2016-10-25 17:53:49 -0400 | [diff] [blame] | 1138 |                        self.hash_block_size, self.fec_num_roots, | 
 | 1139 |                        self.fec_offset, self.fec_size, self.hash_algorithm, | 
| David Zeuthen | 5cb2db9 | 2016-10-27 15:14:14 -0400 | [diff] [blame] | 1140 |                        len(encoded_name), len(self.salt), len(self.root_digest), | 
 | 1141 |                        self.RESERVED*'\0') | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1142 |     padding = struct.pack(str(padding_size) + 'x') | 
 | 1143 |     ret = desc + encoded_name + self.salt + self.root_digest + padding | 
 | 1144 |     return bytearray(ret) | 
 | 1145 |  | 
 | 1146 |  | 
 | 1147 | class AvbHashDescriptor(AvbDescriptor): | 
 | 1148 |   """A class for hash descriptors. | 
 | 1149 |  | 
 | 1150 |   See the |AvbHashDescriptor| C struct for more information. | 
 | 1151 |  | 
 | 1152 |   Attributes: | 
 | 1153 |     image_size: Image size, in bytes. | 
 | 1154 |     hash_algorithm: Hash algorithm used. | 
 | 1155 |     partition_name: Partition name. | 
 | 1156 |     salt: Salt used. | 
 | 1157 |     digest: The hash value of salt and data combined. | 
 | 1158 |   """ | 
 | 1159 |  | 
 | 1160 |   TAG = 2 | 
| David Zeuthen | 5cb2db9 | 2016-10-27 15:14:14 -0400 | [diff] [blame] | 1161 |   RESERVED = 64 | 
 | 1162 |   SIZE = 68 + RESERVED | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1163 |   FORMAT_STRING = ('!QQ'  # tag, num_bytes_following (descriptor header) | 
 | 1164 |                    'Q'  # image size (bytes) | 
 | 1165 |                    '32s'  # hash algorithm used | 
 | 1166 |                    'L'  # partition name (bytes) | 
 | 1167 |                    'L'  # salt length (bytes) | 
| David Zeuthen | 5cb2db9 | 2016-10-27 15:14:14 -0400 | [diff] [blame] | 1168 |                    'L' +  # digest length (bytes) | 
 | 1169 |                    str(RESERVED) + 's')  # reserved | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1170 |  | 
 | 1171 |   def __init__(self, data=None): | 
 | 1172 |     """Initializes a new hash descriptor. | 
 | 1173 |  | 
 | 1174 |     Arguments: | 
 | 1175 |       data: If not None, must be a bytearray of size |SIZE|. | 
 | 1176 |  | 
 | 1177 |     Raises: | 
 | 1178 |       LookupError: If the given descriptor is malformed. | 
 | 1179 |     """ | 
 | 1180 |     AvbDescriptor.__init__(self, None) | 
 | 1181 |     assert struct.calcsize(self.FORMAT_STRING) == self.SIZE | 
 | 1182 |  | 
 | 1183 |     if data: | 
 | 1184 |       (tag, num_bytes_following, self.image_size, self.hash_algorithm, | 
 | 1185 |        partition_name_len, salt_len, | 
| David Zeuthen | 5cb2db9 | 2016-10-27 15:14:14 -0400 | [diff] [blame] | 1186 |        digest_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]) | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1187 |       expected_size = round_to_multiple( | 
 | 1188 |           self.SIZE - 16 + partition_name_len + salt_len + digest_len, 8) | 
 | 1189 |       if tag != self.TAG or num_bytes_following != expected_size: | 
 | 1190 |         raise LookupError('Given data does not look like a hash ' 'descriptor.') | 
 | 1191 |       # Nuke NUL-bytes at the end. | 
 | 1192 |       self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0] | 
 | 1193 |       o = 0 | 
 | 1194 |       self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o + | 
 | 1195 |                                                       partition_name_len)]) | 
 | 1196 |       # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8. | 
 | 1197 |       self.partition_name.decode('utf-8') | 
 | 1198 |       o += partition_name_len | 
 | 1199 |       self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)] | 
 | 1200 |       o += salt_len | 
 | 1201 |       self.digest = data[(self.SIZE + o):(self.SIZE + o + digest_len)] | 
 | 1202 |       if digest_len != len(hashlib.new(name=self.hash_algorithm).digest()): | 
 | 1203 |         raise LookupError('digest_len doesn\'t match hash algorithm') | 
 | 1204 |  | 
 | 1205 |     else: | 
 | 1206 |       self.image_size = 0 | 
 | 1207 |       self.hash_algorithm = '' | 
 | 1208 |       self.partition_name = '' | 
 | 1209 |       self.salt = bytearray() | 
 | 1210 |       self.digest = bytearray() | 
 | 1211 |  | 
 | 1212 |   def print_desc(self, o): | 
 | 1213 |     """Print the descriptor. | 
 | 1214 |  | 
 | 1215 |     Arguments: | 
 | 1216 |       o: The object to write the output to. | 
 | 1217 |     """ | 
 | 1218 |     o.write('    Hash descriptor:\n') | 
 | 1219 |     o.write('      Image Size:            {} bytes\n'.format(self.image_size)) | 
 | 1220 |     o.write('      Hash Algorithm:        {}\n'.format(self.hash_algorithm)) | 
 | 1221 |     o.write('      Partition Name:        {}\n'.format(self.partition_name)) | 
 | 1222 |     o.write('      Salt:                  {}\n'.format(str(self.salt).encode( | 
 | 1223 |         'hex'))) | 
 | 1224 |     o.write('      Digest:                {}\n'.format(str(self.digest).encode( | 
 | 1225 |         'hex'))) | 
 | 1226 |  | 
 | 1227 |   def encode(self): | 
 | 1228 |     """Serializes the descriptor. | 
 | 1229 |  | 
 | 1230 |     Returns: | 
 | 1231 |       A bytearray() with the descriptor data. | 
 | 1232 |     """ | 
 | 1233 |     encoded_name = self.partition_name.encode('utf-8') | 
 | 1234 |     num_bytes_following = ( | 
 | 1235 |         self.SIZE + len(encoded_name) + len(self.salt) + len(self.digest) - 16) | 
 | 1236 |     nbf_with_padding = round_to_multiple(num_bytes_following, 8) | 
 | 1237 |     padding_size = nbf_with_padding - num_bytes_following | 
 | 1238 |     desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding, | 
 | 1239 |                        self.image_size, self.hash_algorithm, len(encoded_name), | 
| David Zeuthen | 5cb2db9 | 2016-10-27 15:14:14 -0400 | [diff] [blame] | 1240 |                        len(self.salt), len(self.digest), self.RESERVED*'\0') | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1241 |     padding = struct.pack(str(padding_size) + 'x') | 
 | 1242 |     ret = desc + encoded_name + self.salt + self.digest + padding | 
 | 1243 |     return bytearray(ret) | 
 | 1244 |  | 
 | 1245 |  | 
 | 1246 | class AvbKernelCmdlineDescriptor(AvbDescriptor): | 
 | 1247 |   """A class for kernel command-line descriptors. | 
 | 1248 |  | 
 | 1249 |   See the |AvbKernelCmdlineDescriptor| C struct for more information. | 
 | 1250 |  | 
 | 1251 |   Attributes: | 
| David Zeuthen | fd41eb9 | 2016-11-17 12:24:47 -0500 | [diff] [blame] | 1252 |     flags: Flags. | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1253 |     kernel_cmdline: The kernel command-line. | 
 | 1254 |   """ | 
 | 1255 |  | 
 | 1256 |   TAG = 3 | 
| David Zeuthen | fd41eb9 | 2016-11-17 12:24:47 -0500 | [diff] [blame] | 1257 |   SIZE = 24 | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1258 |   FORMAT_STRING = ('!QQ'  # tag, num_bytes_following (descriptor header) | 
| David Zeuthen | fd41eb9 | 2016-11-17 12:24:47 -0500 | [diff] [blame] | 1259 |                    'L'  # flags | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1260 |                    'L')  # cmdline length (bytes) | 
 | 1261 |  | 
| David Zeuthen | fd41eb9 | 2016-11-17 12:24:47 -0500 | [diff] [blame] | 1262 |   FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED = (1 << 0) | 
 | 1263 |   FLAGS_USE_ONLY_IF_HASHTREE_DISABLED = (1 << 1) | 
 | 1264 |  | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1265 |   def __init__(self, data=None): | 
 | 1266 |     """Initializes a new kernel cmdline descriptor. | 
 | 1267 |  | 
 | 1268 |     Arguments: | 
 | 1269 |       data: If not None, must be a bytearray of size |SIZE|. | 
 | 1270 |  | 
 | 1271 |     Raises: | 
 | 1272 |       LookupError: If the given descriptor is malformed. | 
 | 1273 |     """ | 
 | 1274 |     AvbDescriptor.__init__(self, None) | 
 | 1275 |     assert struct.calcsize(self.FORMAT_STRING) == self.SIZE | 
 | 1276 |  | 
 | 1277 |     if data: | 
| David Zeuthen | fd41eb9 | 2016-11-17 12:24:47 -0500 | [diff] [blame] | 1278 |       (tag, num_bytes_following, self.flags, kernel_cmdline_length) = ( | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1279 |           struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])) | 
 | 1280 |       expected_size = round_to_multiple(self.SIZE - 16 + kernel_cmdline_length, | 
 | 1281 |                                         8) | 
 | 1282 |       if tag != self.TAG or num_bytes_following != expected_size: | 
 | 1283 |         raise LookupError('Given data does not look like a kernel cmdline ' | 
 | 1284 |                           'descriptor.') | 
 | 1285 |       # Nuke NUL-bytes at the end. | 
 | 1286 |       self.kernel_cmdline = str(data[self.SIZE:(self.SIZE + | 
 | 1287 |                                                 kernel_cmdline_length)]) | 
 | 1288 |       # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8. | 
 | 1289 |       self.kernel_cmdline.decode('utf-8') | 
 | 1290 |     else: | 
| David Zeuthen | fd41eb9 | 2016-11-17 12:24:47 -0500 | [diff] [blame] | 1291 |       self.flags = 0 | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1292 |       self.kernel_cmdline = '' | 
 | 1293 |  | 
 | 1294 |   def print_desc(self, o): | 
 | 1295 |     """Print the descriptor. | 
 | 1296 |  | 
 | 1297 |     Arguments: | 
 | 1298 |       o: The object to write the output to. | 
 | 1299 |     """ | 
 | 1300 |     o.write('    Kernel Cmdline descriptor:\n') | 
| David Zeuthen | fd41eb9 | 2016-11-17 12:24:47 -0500 | [diff] [blame] | 1301 |     o.write('      Flags:                 {}\n'.format(self.flags)) | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1302 |     o.write('      Kernel Cmdline:        {}\n'.format(repr( | 
 | 1303 |         self.kernel_cmdline))) | 
 | 1304 |  | 
 | 1305 |   def encode(self): | 
 | 1306 |     """Serializes the descriptor. | 
 | 1307 |  | 
 | 1308 |     Returns: | 
 | 1309 |       A bytearray() with the descriptor data. | 
 | 1310 |     """ | 
 | 1311 |     encoded_str = self.kernel_cmdline.encode('utf-8') | 
 | 1312 |     num_bytes_following = (self.SIZE + len(encoded_str) - 16) | 
 | 1313 |     nbf_with_padding = round_to_multiple(num_bytes_following, 8) | 
 | 1314 |     padding_size = nbf_with_padding - num_bytes_following | 
 | 1315 |     desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding, | 
| David Zeuthen | fd41eb9 | 2016-11-17 12:24:47 -0500 | [diff] [blame] | 1316 |                        self.flags, len(encoded_str)) | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1317 |     padding = struct.pack(str(padding_size) + 'x') | 
 | 1318 |     ret = desc + encoded_str + padding | 
 | 1319 |     return bytearray(ret) | 
 | 1320 |  | 
 | 1321 |  | 
 | 1322 | class AvbChainPartitionDescriptor(AvbDescriptor): | 
 | 1323 |   """A class for chained partition descriptors. | 
 | 1324 |  | 
 | 1325 |   See the |AvbChainPartitionDescriptor| C struct for more information. | 
 | 1326 |  | 
 | 1327 |   Attributes: | 
| David Zeuthen | 40ee1da | 2016-11-23 15:14:49 -0500 | [diff] [blame] | 1328 |     rollback_index_location: The rollback index location to use. | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1329 |     partition_name: Partition name. | 
 | 1330 |     public_key: Bytes for the public key. | 
 | 1331 |   """ | 
 | 1332 |  | 
 | 1333 |   TAG = 4 | 
| David Zeuthen | 5cb2db9 | 2016-10-27 15:14:14 -0400 | [diff] [blame] | 1334 |   RESERVED = 64 | 
 | 1335 |   SIZE = 28 + RESERVED | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1336 |   FORMAT_STRING = ('!QQ'  # tag, num_bytes_following (descriptor header) | 
| David Zeuthen | 40ee1da | 2016-11-23 15:14:49 -0500 | [diff] [blame] | 1337 |                    'L'  # rollback_index_location | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1338 |                    'L'  # partition_name_size (bytes) | 
| David Zeuthen | 5cb2db9 | 2016-10-27 15:14:14 -0400 | [diff] [blame] | 1339 |                    'L' +  # public_key_size (bytes) | 
 | 1340 |                    str(RESERVED) + 's')  # reserved | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1341 |  | 
 | 1342 |   def __init__(self, data=None): | 
 | 1343 |     """Initializes a new chain partition descriptor. | 
 | 1344 |  | 
 | 1345 |     Arguments: | 
 | 1346 |       data: If not None, must be a bytearray of size |SIZE|. | 
 | 1347 |  | 
 | 1348 |     Raises: | 
 | 1349 |       LookupError: If the given descriptor is malformed. | 
 | 1350 |     """ | 
 | 1351 |     AvbDescriptor.__init__(self, None) | 
 | 1352 |     assert struct.calcsize(self.FORMAT_STRING) == self.SIZE | 
 | 1353 |  | 
 | 1354 |     if data: | 
| David Zeuthen | 40ee1da | 2016-11-23 15:14:49 -0500 | [diff] [blame] | 1355 |       (tag, num_bytes_following, self.rollback_index_location, | 
 | 1356 |        partition_name_len, | 
| David Zeuthen | 5cb2db9 | 2016-10-27 15:14:14 -0400 | [diff] [blame] | 1357 |        public_key_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]) | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1358 |       expected_size = round_to_multiple( | 
 | 1359 |           self.SIZE - 16 + partition_name_len + public_key_len, 8) | 
 | 1360 |       if tag != self.TAG or num_bytes_following != expected_size: | 
 | 1361 |         raise LookupError('Given data does not look like a chain partition ' | 
 | 1362 |                           'descriptor.') | 
 | 1363 |       o = 0 | 
 | 1364 |       self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o + | 
 | 1365 |                                                       partition_name_len)]) | 
 | 1366 |       # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8. | 
 | 1367 |       self.partition_name.decode('utf-8') | 
 | 1368 |       o += partition_name_len | 
 | 1369 |       self.public_key = data[(self.SIZE + o):(self.SIZE + o + public_key_len)] | 
 | 1370 |  | 
 | 1371 |     else: | 
| David Zeuthen | 40ee1da | 2016-11-23 15:14:49 -0500 | [diff] [blame] | 1372 |       self.rollback_index_location = 0 | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1373 |       self.partition_name = '' | 
 | 1374 |       self.public_key = bytearray() | 
 | 1375 |  | 
 | 1376 |   def print_desc(self, o): | 
 | 1377 |     """Print the descriptor. | 
 | 1378 |  | 
 | 1379 |     Arguments: | 
 | 1380 |       o: The object to write the output to. | 
 | 1381 |     """ | 
 | 1382 |     o.write('    Chain Partition descriptor:\n') | 
| David Zeuthen | 40ee1da | 2016-11-23 15:14:49 -0500 | [diff] [blame] | 1383 |     o.write('      Partition Name:          {}\n'.format(self.partition_name)) | 
 | 1384 |     o.write('      Rollback Index Location: {}\n'.format( | 
 | 1385 |         self.rollback_index_location)) | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1386 |     # Just show the SHA1 of the key, for size reasons. | 
 | 1387 |     hexdig = hashlib.sha1(self.public_key).hexdigest() | 
| David Zeuthen | 40ee1da | 2016-11-23 15:14:49 -0500 | [diff] [blame] | 1388 |     o.write('      Public key (sha1):       {}\n'.format(hexdig)) | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1389 |  | 
 | 1390 |   def encode(self): | 
 | 1391 |     """Serializes the descriptor. | 
 | 1392 |  | 
 | 1393 |     Returns: | 
 | 1394 |       A bytearray() with the descriptor data. | 
 | 1395 |     """ | 
 | 1396 |     encoded_name = self.partition_name.encode('utf-8') | 
 | 1397 |     num_bytes_following = ( | 
 | 1398 |         self.SIZE + len(encoded_name) + len(self.public_key) - 16) | 
 | 1399 |     nbf_with_padding = round_to_multiple(num_bytes_following, 8) | 
 | 1400 |     padding_size = nbf_with_padding - num_bytes_following | 
 | 1401 |     desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding, | 
| David Zeuthen | 40ee1da | 2016-11-23 15:14:49 -0500 | [diff] [blame] | 1402 |                        self.rollback_index_location, len(encoded_name), | 
| David Zeuthen | 5cb2db9 | 2016-10-27 15:14:14 -0400 | [diff] [blame] | 1403 |                        len(self.public_key), self.RESERVED*'\0') | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1404 |     padding = struct.pack(str(padding_size) + 'x') | 
 | 1405 |     ret = desc + encoded_name + self.public_key + padding | 
 | 1406 |     return bytearray(ret) | 
 | 1407 |  | 
 | 1408 |  | 
 | 1409 | DESCRIPTOR_CLASSES = [ | 
 | 1410 |     AvbPropertyDescriptor, AvbHashtreeDescriptor, AvbHashDescriptor, | 
 | 1411 |     AvbKernelCmdlineDescriptor, AvbChainPartitionDescriptor | 
 | 1412 | ] | 
 | 1413 |  | 
 | 1414 |  | 
 | 1415 | def parse_descriptors(data): | 
 | 1416 |   """Parses a blob of data into descriptors. | 
 | 1417 |  | 
 | 1418 |   Arguments: | 
 | 1419 |     data: A bytearray() with encoded descriptors. | 
 | 1420 |  | 
 | 1421 |   Returns: | 
 | 1422 |     A list of instances of objects derived from AvbDescriptor. For | 
 | 1423 |     unknown descriptors, the class AvbDescriptor is used. | 
 | 1424 |   """ | 
 | 1425 |   o = 0 | 
 | 1426 |   ret = [] | 
 | 1427 |   while o < len(data): | 
 | 1428 |     tag, nb_following = struct.unpack('!2Q', data[o:o + 16]) | 
 | 1429 |     if tag < len(DESCRIPTOR_CLASSES): | 
 | 1430 |       c = DESCRIPTOR_CLASSES[tag] | 
 | 1431 |     else: | 
 | 1432 |       c = AvbDescriptor | 
 | 1433 |     ret.append(c(bytearray(data[o:o + 16 + nb_following]))) | 
 | 1434 |     o += 16 + nb_following | 
 | 1435 |   return ret | 
 | 1436 |  | 
 | 1437 |  | 
 | 1438 | class AvbFooter(object): | 
 | 1439 |   """A class for parsing and writing footers. | 
 | 1440 |  | 
 | 1441 |   Footers are stored at the end of partitions and point to where the | 
 | 1442 |   AvbVBMeta blob is located. They also contain the original size of | 
 | 1443 |   the image before AVB information was added. | 
 | 1444 |  | 
 | 1445 |   Attributes: | 
 | 1446 |     magic: Magic for identifying the footer, see |MAGIC|. | 
 | 1447 |     version_major: The major version of avbtool that wrote the footer. | 
 | 1448 |     version_minor: The minor version of avbtool that wrote the footer. | 
 | 1449 |     original_image_size: Original image size. | 
 | 1450 |     vbmeta_offset: Offset of where the AvbVBMeta blob is stored. | 
 | 1451 |     vbmeta_size: Size of the AvbVBMeta blob. | 
 | 1452 |   """ | 
 | 1453 |  | 
 | 1454 |   MAGIC = 'AVBf' | 
 | 1455 |   SIZE = 64 | 
 | 1456 |   RESERVED = 28 | 
 | 1457 |   FORMAT_STRING = ('!4s2L'  # magic, 2 x version. | 
 | 1458 |                    'Q'  # Original image size. | 
 | 1459 |                    'Q'  # Offset of VBMeta blob. | 
 | 1460 |                    'Q' +  # Size of VBMeta blob. | 
 | 1461 |                    str(RESERVED) + 'x')  # padding for reserved bytes | 
 | 1462 |  | 
 | 1463 |   def __init__(self, data=None): | 
 | 1464 |     """Initializes a new footer object. | 
 | 1465 |  | 
 | 1466 |     Arguments: | 
 | 1467 |       data: If not None, must be a bytearray of size 4096. | 
 | 1468 |  | 
 | 1469 |     Raises: | 
 | 1470 |       LookupError: If the given footer is malformed. | 
 | 1471 |       struct.error: If the given data has no footer. | 
 | 1472 |     """ | 
 | 1473 |     assert struct.calcsize(self.FORMAT_STRING) == self.SIZE | 
 | 1474 |  | 
 | 1475 |     if data: | 
 | 1476 |       (self.magic, self.version_major, self.version_minor, | 
 | 1477 |        self.original_image_size, self.vbmeta_offset, | 
 | 1478 |        self.vbmeta_size) = struct.unpack(self.FORMAT_STRING, data) | 
 | 1479 |       if self.magic != self.MAGIC: | 
| David Zeuthen | 8b6973b | 2016-09-20 12:39:49 -0400 | [diff] [blame] | 1480 |         raise LookupError('Given data does not look like a AVB footer.') | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1481 |     else: | 
 | 1482 |       self.magic = self.MAGIC | 
 | 1483 |       self.version_major = AVB_VERSION_MAJOR | 
 | 1484 |       self.version_minor = AVB_VERSION_MINOR | 
 | 1485 |       self.original_image_size = 0 | 
 | 1486 |       self.vbmeta_offset = 0 | 
 | 1487 |       self.vbmeta_size = 0 | 
 | 1488 |  | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 1489 |   def encode(self): | 
 | 1490 |     """Gets a string representing the binary encoding of the footer. | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1491 |  | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 1492 |     Returns: | 
 | 1493 |       A bytearray() with a binary representation of the footer. | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1494 |     """ | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 1495 |     return struct.pack(self.FORMAT_STRING, self.magic, self.version_major, | 
 | 1496 |                        self.version_minor, self.original_image_size, | 
 | 1497 |                        self.vbmeta_offset, self.vbmeta_size) | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1498 |  | 
 | 1499 |  | 
 | 1500 | class AvbVBMetaHeader(object): | 
| David Zeuthen | 8b6973b | 2016-09-20 12:39:49 -0400 | [diff] [blame] | 1501 |   """A class for parsing and writing AVB vbmeta images. | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1502 |  | 
 | 1503 |   Attributes: | 
 | 1504 |     The attributes correspond to the |AvbVBMetaHeader| struct | 
 | 1505 |     defined in avb_vbmeta_header.h. | 
 | 1506 |   """ | 
 | 1507 |  | 
 | 1508 |   SIZE = 256 | 
 | 1509 |  | 
 | 1510 |   # Keep in sync with |reserved| field of |AvbVBMetaImageHeader|. | 
| David Zeuthen | fd41eb9 | 2016-11-17 12:24:47 -0500 | [diff] [blame] | 1511 |   RESERVED = 132 | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1512 |  | 
 | 1513 |   # Keep in sync with |AvbVBMetaImageHeader|. | 
 | 1514 |   FORMAT_STRING = ('!4s2L'  # magic, 2 x version | 
 | 1515 |                    '2Q'  # 2 x block size | 
 | 1516 |                    'L'  # algorithm type | 
 | 1517 |                    '2Q'  # offset, size (hash) | 
 | 1518 |                    '2Q'  # offset, size (signature) | 
 | 1519 |                    '2Q'  # offset, size (public key) | 
| David Zeuthen | 18666ab | 2016-11-15 11:18:05 -0500 | [diff] [blame] | 1520 |                    '2Q'  # offset, size (public key metadata) | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1521 |                    '2Q'  # offset, size (descriptors) | 
| David Zeuthen | fd41eb9 | 2016-11-17 12:24:47 -0500 | [diff] [blame] | 1522 |                    'Q'  # rollback_index | 
 | 1523 |                    'L' +  # flags | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1524 |                    str(RESERVED) + 'x')  # padding for reserved bytes | 
 | 1525 |  | 
 | 1526 |   def __init__(self, data=None): | 
 | 1527 |     """Initializes a new header object. | 
 | 1528 |  | 
 | 1529 |     Arguments: | 
 | 1530 |       data: If not None, must be a bytearray of size 8192. | 
 | 1531 |  | 
 | 1532 |     Raises: | 
 | 1533 |       Exception: If the given data is malformed. | 
 | 1534 |     """ | 
 | 1535 |     assert struct.calcsize(self.FORMAT_STRING) == self.SIZE | 
 | 1536 |  | 
 | 1537 |     if data: | 
 | 1538 |       (self.magic, self.header_version_major, self.header_version_minor, | 
 | 1539 |        self.authentication_data_block_size, self.auxiliary_data_block_size, | 
 | 1540 |        self.algorithm_type, self.hash_offset, self.hash_size, | 
 | 1541 |        self.signature_offset, self.signature_size, self.public_key_offset, | 
| David Zeuthen | 18666ab | 2016-11-15 11:18:05 -0500 | [diff] [blame] | 1542 |        self.public_key_size, self.public_key_metadata_offset, | 
 | 1543 |        self.public_key_metadata_size, self.descriptors_offset, | 
 | 1544 |        self.descriptors_size, | 
| David Zeuthen | fd41eb9 | 2016-11-17 12:24:47 -0500 | [diff] [blame] | 1545 |        self.rollback_index, | 
 | 1546 |        self.flags) = struct.unpack(self.FORMAT_STRING, data) | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1547 |       # Nuke NUL-bytes at the end of the string. | 
 | 1548 |       if self.magic != 'AVB0': | 
| David Zeuthen | 8b6973b | 2016-09-20 12:39:49 -0400 | [diff] [blame] | 1549 |         raise AvbError('Given image does not look like a vbmeta image.') | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1550 |     else: | 
 | 1551 |       self.magic = 'AVB0' | 
 | 1552 |       self.header_version_major = AVB_VERSION_MAJOR | 
 | 1553 |       self.header_version_minor = AVB_VERSION_MINOR | 
 | 1554 |       self.authentication_data_block_size = 0 | 
 | 1555 |       self.auxiliary_data_block_size = 0 | 
 | 1556 |       self.algorithm_type = 0 | 
 | 1557 |       self.hash_offset = 0 | 
 | 1558 |       self.hash_size = 0 | 
 | 1559 |       self.signature_offset = 0 | 
 | 1560 |       self.signature_size = 0 | 
 | 1561 |       self.public_key_offset = 0 | 
 | 1562 |       self.public_key_size = 0 | 
| David Zeuthen | 18666ab | 2016-11-15 11:18:05 -0500 | [diff] [blame] | 1563 |       self.public_key_metadata_offset = 0 | 
 | 1564 |       self.public_key_metadata_size = 0 | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1565 |       self.descriptors_offset = 0 | 
 | 1566 |       self.descriptors_size = 0 | 
 | 1567 |       self.rollback_index = 0 | 
| David Zeuthen | fd41eb9 | 2016-11-17 12:24:47 -0500 | [diff] [blame] | 1568 |       self.flags = 0 | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1569 |  | 
 | 1570 |   def save(self, output): | 
 | 1571 |     """Serializes the header (256 bytes) to disk. | 
 | 1572 |  | 
 | 1573 |     Arguments: | 
 | 1574 |       output: The object to write the output to. | 
 | 1575 |     """ | 
 | 1576 |     output.write(struct.pack( | 
 | 1577 |         self.FORMAT_STRING, self.magic, self.header_version_major, | 
 | 1578 |         self.header_version_minor, self.authentication_data_block_size, | 
 | 1579 |         self.auxiliary_data_block_size, self.algorithm_type, self.hash_offset, | 
 | 1580 |         self.hash_size, self.signature_offset, self.signature_size, | 
| David Zeuthen | 18666ab | 2016-11-15 11:18:05 -0500 | [diff] [blame] | 1581 |         self.public_key_offset, self.public_key_size, | 
 | 1582 |         self.public_key_metadata_offset, self.public_key_metadata_size, | 
| David Zeuthen | fd41eb9 | 2016-11-17 12:24:47 -0500 | [diff] [blame] | 1583 |         self.descriptors_offset, self.descriptors_size, self.rollback_index, | 
 | 1584 |         self.flags)) | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1585 |  | 
 | 1586 |   def encode(self): | 
 | 1587 |     """Serializes the header (256) to a bytearray(). | 
 | 1588 |  | 
 | 1589 |     Returns: | 
 | 1590 |       A bytearray() with the encoded header. | 
 | 1591 |     """ | 
 | 1592 |     return struct.pack(self.FORMAT_STRING, self.magic, | 
 | 1593 |                        self.header_version_major, self.header_version_minor, | 
 | 1594 |                        self.authentication_data_block_size, | 
 | 1595 |                        self.auxiliary_data_block_size, self.algorithm_type, | 
 | 1596 |                        self.hash_offset, self.hash_size, self.signature_offset, | 
 | 1597 |                        self.signature_size, self.public_key_offset, | 
| David Zeuthen | 18666ab | 2016-11-15 11:18:05 -0500 | [diff] [blame] | 1598 |                        self.public_key_size, self.public_key_metadata_offset, | 
 | 1599 |                        self.public_key_metadata_size, self.descriptors_offset, | 
| David Zeuthen | fd41eb9 | 2016-11-17 12:24:47 -0500 | [diff] [blame] | 1600 |                        self.descriptors_size, self.rollback_index, self.flags) | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1601 |  | 
 | 1602 |  | 
 | 1603 | class Avb(object): | 
 | 1604 |   """Business logic for avbtool command-line tool.""" | 
 | 1605 |  | 
| David Zeuthen | 8b6973b | 2016-09-20 12:39:49 -0400 | [diff] [blame] | 1606 |   # Keep in sync with avb_ab_flow.h. | 
 | 1607 |   AB_FORMAT_NO_CRC = '!4sBB2xBBBxBBBx12x' | 
 | 1608 |   AB_MAGIC = '\0AB0' | 
 | 1609 |   AB_MAJOR_VERSION = 1 | 
 | 1610 |   AB_MINOR_VERSION = 0 | 
 | 1611 |   AB_MISC_METADATA_OFFSET = 2048 | 
 | 1612 |  | 
| David Zeuthen | 0969269 | 2016-09-30 16:16:40 -0400 | [diff] [blame] | 1613 |   # Constants for maximum metadata size. These are used to give | 
 | 1614 |   # meaningful errors if the value passed in via --partition_size is | 
 | 1615 |   # too small and when --calc_max_image_size is used. We use | 
 | 1616 |   # conservative figures. | 
 | 1617 |   MAX_VBMETA_SIZE = 64 * 1024 | 
 | 1618 |   MAX_FOOTER_SIZE = 4096 | 
 | 1619 |  | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 1620 |   def erase_footer(self, image_filename, keep_hashtree): | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1621 |     """Implements the 'erase_footer' command. | 
 | 1622 |  | 
 | 1623 |     Arguments: | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 1624 |       image_filename: File to erase a footer from. | 
| David Zeuthen | fbb61fa | 2017-02-02 12:11:49 -0500 | [diff] [blame] | 1625 |       keep_hashtree: If True, keep the hashtree and FEC around. | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1626 |  | 
 | 1627 |     Raises: | 
 | 1628 |       AvbError: If there's no footer in the image. | 
 | 1629 |     """ | 
 | 1630 |  | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 1631 |     image = ImageHandler(image_filename) | 
 | 1632 |  | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1633 |     (footer, _, descriptors, _) = self._parse_image(image) | 
 | 1634 |  | 
 | 1635 |     if not footer: | 
 | 1636 |       raise AvbError('Given image does not have a footer.') | 
 | 1637 |  | 
 | 1638 |     new_image_size = None | 
 | 1639 |     if not keep_hashtree: | 
 | 1640 |       new_image_size = footer.original_image_size | 
 | 1641 |     else: | 
 | 1642 |       # If requested to keep the hashtree, search for a hashtree | 
| David Zeuthen | fbb61fa | 2017-02-02 12:11:49 -0500 | [diff] [blame] | 1643 |       # descriptor to figure out the location and size of the hashtree | 
 | 1644 |       # and FEC. | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1645 |       for desc in descriptors: | 
 | 1646 |         if isinstance(desc, AvbHashtreeDescriptor): | 
 | 1647 |           # The hashtree is always just following the main data so the | 
 | 1648 |           # new size is easily derived. | 
 | 1649 |           new_image_size = desc.tree_offset + desc.tree_size | 
| David Zeuthen | fbb61fa | 2017-02-02 12:11:49 -0500 | [diff] [blame] | 1650 |           # If the image has FEC codes, also keep those. | 
 | 1651 |           if desc.fec_offset > 0: | 
 | 1652 |             fec_end = desc.fec_offset + desc.fec_size | 
 | 1653 |             new_image_size = max(new_image_size, fec_end) | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1654 |           break | 
 | 1655 |       if not new_image_size: | 
 | 1656 |         raise AvbError('Requested to keep hashtree but no hashtree ' | 
 | 1657 |                        'descriptor was found.') | 
 | 1658 |  | 
 | 1659 |     # And cut... | 
 | 1660 |     image.truncate(new_image_size) | 
 | 1661 |  | 
| David Zeuthen | 8b6973b | 2016-09-20 12:39:49 -0400 | [diff] [blame] | 1662 |   def set_ab_metadata(self, misc_image, slot_data): | 
 | 1663 |     """Implements the 'set_ab_metadata' command. | 
 | 1664 |  | 
 | 1665 |     The |slot_data| argument must be of the form 'A_priority:A_tries_remaining: | 
 | 1666 |     A_successful_boot:B_priority:B_tries_remaining:B_successful_boot'. | 
 | 1667 |  | 
 | 1668 |     Arguments: | 
 | 1669 |       misc_image: The misc image to write to. | 
 | 1670 |       slot_data: Slot data as a string | 
 | 1671 |  | 
 | 1672 |     Raises: | 
 | 1673 |       AvbError: If slot data is malformed. | 
 | 1674 |     """ | 
 | 1675 |     tokens = slot_data.split(':') | 
 | 1676 |     if len(tokens) != 6: | 
 | 1677 |       raise AvbError('Malformed slot data "{}".'.format(slot_data)) | 
 | 1678 |     a_priority = int(tokens[0]) | 
 | 1679 |     a_tries_remaining = int(tokens[1]) | 
 | 1680 |     a_success = True if int(tokens[2]) != 0 else False | 
 | 1681 |     b_priority = int(tokens[3]) | 
 | 1682 |     b_tries_remaining = int(tokens[4]) | 
 | 1683 |     b_success = True if int(tokens[5]) != 0 else False | 
 | 1684 |  | 
 | 1685 |     ab_data_no_crc = struct.pack(self.AB_FORMAT_NO_CRC, | 
 | 1686 |                                  self.AB_MAGIC, | 
 | 1687 |                                  self.AB_MAJOR_VERSION, self.AB_MINOR_VERSION, | 
 | 1688 |                                  a_priority, a_tries_remaining, a_success, | 
 | 1689 |                                  b_priority, b_tries_remaining, b_success) | 
 | 1690 |     # Force CRC to be unsigned, see https://bugs.python.org/issue4903 for why. | 
 | 1691 |     crc_value = binascii.crc32(ab_data_no_crc) & 0xffffffff | 
 | 1692 |     ab_data = ab_data_no_crc + struct.pack('!I', crc_value) | 
 | 1693 |     misc_image.seek(self.AB_MISC_METADATA_OFFSET) | 
 | 1694 |     misc_image.write(ab_data) | 
 | 1695 |  | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 1696 |   def info_image(self, image_filename, output): | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1697 |     """Implements the 'info_image' command. | 
 | 1698 |  | 
 | 1699 |     Arguments: | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 1700 |       image_filename: Image file to get information from (file object). | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1701 |       output: Output file to write human-readable information to (file object). | 
 | 1702 |     """ | 
 | 1703 |  | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 1704 |     image = ImageHandler(image_filename) | 
 | 1705 |  | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1706 |     o = output | 
 | 1707 |  | 
 | 1708 |     (footer, header, descriptors, image_size) = self._parse_image(image) | 
 | 1709 |  | 
 | 1710 |     if footer: | 
 | 1711 |       o.write('Footer version:           {}.{}\n'.format(footer.version_major, | 
 | 1712 |                                                          footer.version_minor)) | 
 | 1713 |       o.write('Image size:               {} bytes\n'.format(image_size)) | 
 | 1714 |       o.write('Original image size:      {} bytes\n'.format( | 
 | 1715 |           footer.original_image_size)) | 
 | 1716 |       o.write('VBMeta offset:            {}\n'.format(footer.vbmeta_offset)) | 
 | 1717 |       o.write('VBMeta size:              {} bytes\n'.format(footer.vbmeta_size)) | 
 | 1718 |       o.write('--\n') | 
 | 1719 |  | 
 | 1720 |     (alg_name, _) = lookup_algorithm_by_type(header.algorithm_type) | 
 | 1721 |  | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 1722 |     o.write('VBMeta image version:     {}.{}{}\n'.format( | 
 | 1723 |         header.header_version_major, header.header_version_minor, | 
 | 1724 |         ' (Sparse)' if image.is_sparse else '')) | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1725 |     o.write('Header Block:             {} bytes\n'.format(AvbVBMetaHeader.SIZE)) | 
 | 1726 |     o.write('Authentication Block:     {} bytes\n'.format( | 
 | 1727 |         header.authentication_data_block_size)) | 
 | 1728 |     o.write('Auxiliary Block:          {} bytes\n'.format( | 
 | 1729 |         header.auxiliary_data_block_size)) | 
 | 1730 |     o.write('Algorithm:                {}\n'.format(alg_name)) | 
 | 1731 |     o.write('Rollback Index:           {}\n'.format(header.rollback_index)) | 
| David Zeuthen | fd41eb9 | 2016-11-17 12:24:47 -0500 | [diff] [blame] | 1732 |     o.write('Flags:                    {}\n'.format(header.flags)) | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1733 |  | 
 | 1734 |     # Print descriptors. | 
 | 1735 |     num_printed = 0 | 
 | 1736 |     o.write('Descriptors:\n') | 
 | 1737 |     for desc in descriptors: | 
 | 1738 |       desc.print_desc(o) | 
 | 1739 |       num_printed += 1 | 
 | 1740 |     if num_printed == 0: | 
 | 1741 |       o.write('    (none)\n') | 
 | 1742 |  | 
 | 1743 |   def _parse_image(self, image): | 
 | 1744 |     """Gets information about an image. | 
 | 1745 |  | 
 | 1746 |     The image can either be a vbmeta or an image with a footer. | 
 | 1747 |  | 
 | 1748 |     Arguments: | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 1749 |       image: An ImageHandler (vbmeta or footer) with a hashtree descriptor. | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1750 |  | 
 | 1751 |     Returns: | 
 | 1752 |       A tuple where the first argument is a AvbFooter (None if there | 
 | 1753 |       is no footer on the image), the second argument is a | 
 | 1754 |       AvbVBMetaHeader, the third argument is a list of | 
 | 1755 |       AvbDescriptor-derived instances, and the fourth argument is the | 
 | 1756 |       size of |image|. | 
 | 1757 |     """ | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 1758 |     assert isinstance(image, ImageHandler) | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1759 |     footer = None | 
| David Zeuthen | 0969269 | 2016-09-30 16:16:40 -0400 | [diff] [blame] | 1760 |     image.seek(image.image_size - AvbFooter.SIZE) | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1761 |     try: | 
 | 1762 |       footer = AvbFooter(image.read(AvbFooter.SIZE)) | 
 | 1763 |     except (LookupError, struct.error): | 
 | 1764 |       # Nope, just seek back to the start. | 
 | 1765 |       image.seek(0) | 
 | 1766 |  | 
 | 1767 |     vbmeta_offset = 0 | 
 | 1768 |     if footer: | 
 | 1769 |       vbmeta_offset = footer.vbmeta_offset | 
 | 1770 |  | 
 | 1771 |     image.seek(vbmeta_offset) | 
 | 1772 |     h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE)) | 
 | 1773 |  | 
 | 1774 |     auth_block_offset = vbmeta_offset + AvbVBMetaHeader.SIZE | 
 | 1775 |     aux_block_offset = auth_block_offset + h.authentication_data_block_size | 
 | 1776 |     desc_start_offset = aux_block_offset + h.descriptors_offset | 
 | 1777 |     image.seek(desc_start_offset) | 
 | 1778 |     descriptors = parse_descriptors(image.read(h.descriptors_size)) | 
 | 1779 |  | 
| David Zeuthen | 0969269 | 2016-09-30 16:16:40 -0400 | [diff] [blame] | 1780 |     return footer, h, descriptors, image.image_size | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1781 |  | 
| David Zeuthen | fd41eb9 | 2016-11-17 12:24:47 -0500 | [diff] [blame] | 1782 |   def _get_cmdline_descriptors_for_dm_verity(self, image): | 
 | 1783 |     """Generate kernel cmdline descriptors for dm-verity. | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1784 |  | 
 | 1785 |     Arguments: | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 1786 |       image: An ImageHandler (vbmeta or footer) with a hashtree descriptor. | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1787 |  | 
 | 1788 |     Returns: | 
| David Zeuthen | fd41eb9 | 2016-11-17 12:24:47 -0500 | [diff] [blame] | 1789 |       A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline | 
 | 1790 |       instructions. There is one for when hashtree is not disabled and one for | 
 | 1791 |       when it is. | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1792 |  | 
 | 1793 |     Raises: | 
 | 1794 |       AvbError: If  |image| doesn't have a hashtree descriptor. | 
 | 1795 |  | 
 | 1796 |     """ | 
 | 1797 |  | 
 | 1798 |     (_, _, descriptors, _) = self._parse_image(image) | 
 | 1799 |  | 
 | 1800 |     ht = None | 
 | 1801 |     for desc in descriptors: | 
 | 1802 |       if isinstance(desc, AvbHashtreeDescriptor): | 
 | 1803 |         ht = desc | 
 | 1804 |         break | 
 | 1805 |  | 
 | 1806 |     if not ht: | 
 | 1807 |       raise AvbError('No hashtree descriptor in given image') | 
 | 1808 |  | 
 | 1809 |     c = 'dm="1 vroot none ro 1,' | 
| David Zeuthen | 0b7f1d3 | 2016-10-25 17:53:49 -0400 | [diff] [blame] | 1810 |     c += '0'  # start | 
 | 1811 |     c += ' {}'.format((ht.image_size / 512))  # size (# sectors) | 
 | 1812 |     c += ' verity {}'.format(ht.dm_verity_version)  # type and version | 
 | 1813 |     c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'  # data_dev | 
 | 1814 |     c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'  # hash_dev | 
 | 1815 |     c += ' {}'.format(ht.data_block_size)  # data_block | 
 | 1816 |     c += ' {}'.format(ht.hash_block_size)  # hash_block | 
 | 1817 |     c += ' {}'.format(ht.image_size / ht.data_block_size)  # #blocks | 
 | 1818 |     c += ' {}'.format(ht.image_size / ht.data_block_size)  # hash_offset | 
 | 1819 |     c += ' {}'.format(ht.hash_algorithm)  # hash_alg | 
 | 1820 |     c += ' {}'.format(str(ht.root_digest).encode('hex'))  # root_digest | 
 | 1821 |     c += ' {}'.format(str(ht.salt).encode('hex'))  # salt | 
 | 1822 |     if ht.fec_num_roots > 0: | 
| David Zeuthen | a01e32f | 2017-01-24 17:32:38 -0500 | [diff] [blame] | 1823 |       c += ' 10'  # number of optional args | 
 | 1824 |       c += ' restart_on_corruption' | 
| David Zeuthen | 0b7f1d3 | 2016-10-25 17:53:49 -0400 | [diff] [blame] | 1825 |       c += ' ignore_zero_blocks' | 
 | 1826 |       c += ' use_fec_from_device PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' | 
 | 1827 |       c += ' fec_roots {}'.format(ht.fec_num_roots) | 
 | 1828 |       # Note that fec_blocks is the size that FEC covers, *not* the | 
 | 1829 |       # size of the FEC data. Since we use FEC for everything up until | 
 | 1830 |       # the FEC data, it's the same as the offset. | 
 | 1831 |       c += ' fec_blocks {}'.format(ht.fec_offset/ht.data_block_size) | 
 | 1832 |       c += ' fec_start {}'.format(ht.fec_offset/ht.data_block_size) | 
 | 1833 |     else: | 
| David Zeuthen | a01e32f | 2017-01-24 17:32:38 -0500 | [diff] [blame] | 1834 |       c += ' 2'  # number of optional args | 
 | 1835 |       c += ' restart_on_corruption' | 
| David Zeuthen | 0b7f1d3 | 2016-10-25 17:53:49 -0400 | [diff] [blame] | 1836 |       c += ' ignore_zero_blocks' | 
| David Zeuthen | fd41eb9 | 2016-11-17 12:24:47 -0500 | [diff] [blame] | 1837 |     c += '" root=0xfd00' | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1838 |  | 
| David Zeuthen | fd41eb9 | 2016-11-17 12:24:47 -0500 | [diff] [blame] | 1839 |     # Now that we have the command-line, generate the descriptor. | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1840 |     desc = AvbKernelCmdlineDescriptor() | 
 | 1841 |     desc.kernel_cmdline = c | 
| David Zeuthen | fd41eb9 | 2016-11-17 12:24:47 -0500 | [diff] [blame] | 1842 |     desc.flags = ( | 
 | 1843 |         AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED) | 
 | 1844 |  | 
 | 1845 |     # The descriptor for when hashtree verification is disabled is a lot | 
 | 1846 |     # simpler - we just set the root to the partition. | 
 | 1847 |     desc_no_ht = AvbKernelCmdlineDescriptor() | 
 | 1848 |     desc_no_ht.kernel_cmdline = 'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' | 
 | 1849 |     desc_no_ht.flags = ( | 
 | 1850 |         AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED) | 
 | 1851 |  | 
 | 1852 |     return [desc, desc_no_ht] | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1853 |  | 
 | 1854 |   def make_vbmeta_image(self, output, chain_partitions, algorithm_name, | 
| David Zeuthen | 18666ab | 2016-11-15 11:18:05 -0500 | [diff] [blame] | 1855 |                         key_path, public_key_metadata_path, rollback_index, | 
| David Zeuthen | fd41eb9 | 2016-11-17 12:24:47 -0500 | [diff] [blame] | 1856 |                         flags, props, props_from_file, kernel_cmdlines, | 
| David Zeuthen | 5d4f4f2 | 2017-01-11 11:37:34 -0500 | [diff] [blame] | 1857 |                         setup_rootfs_from_kernel, | 
| Dzmitry Yatsushkevich | 4e55279 | 2016-12-15 10:27:48 -0800 | [diff] [blame] | 1858 |                         include_descriptors_from_image, signing_helper): | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1859 |     """Implements the 'make_vbmeta_image' command. | 
 | 1860 |  | 
 | 1861 |     Arguments: | 
 | 1862 |       output: File to write the image to. | 
 | 1863 |       chain_partitions: List of partitions to chain. | 
 | 1864 |       algorithm_name: Name of algorithm to use. | 
 | 1865 |       key_path: Path to key to use or None. | 
| David Zeuthen | 18666ab | 2016-11-15 11:18:05 -0500 | [diff] [blame] | 1866 |       public_key_metadata_path: Path to public key metadata or None. | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1867 |       rollback_index: The rollback index to use. | 
| David Zeuthen | fd41eb9 | 2016-11-17 12:24:47 -0500 | [diff] [blame] | 1868 |       flags: Flags value to use in the image. | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1869 |       props: Properties to insert (list of strings of the form 'key:value'). | 
 | 1870 |       props_from_file: Properties to insert (list of strings 'key:<path>'). | 
 | 1871 |       kernel_cmdlines: Kernel cmdlines to insert (list of strings). | 
| David Zeuthen | 5d4f4f2 | 2017-01-11 11:37:34 -0500 | [diff] [blame] | 1872 |       setup_rootfs_from_kernel: None or file to generate from. | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1873 |       include_descriptors_from_image: List of file objects with descriptors. | 
| Dzmitry Yatsushkevich | 4e55279 | 2016-12-15 10:27:48 -0800 | [diff] [blame] | 1874 |       signing_helper: Program which signs a hash and return signature. | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1875 |  | 
 | 1876 |     Raises: | 
 | 1877 |       AvbError: If a chained partition is malformed. | 
 | 1878 |     """ | 
 | 1879 |  | 
 | 1880 |     descriptors = [] | 
 | 1881 |  | 
 | 1882 |     # Insert chained partition descriptors. | 
 | 1883 |     if chain_partitions: | 
 | 1884 |       for cp in chain_partitions: | 
 | 1885 |         cp_tokens = cp.split(':') | 
 | 1886 |         if len(cp_tokens) != 3: | 
 | 1887 |           raise AvbError('Malformed chained partition "{}".'.format(cp)) | 
 | 1888 |         desc = AvbChainPartitionDescriptor() | 
 | 1889 |         desc.partition_name = cp_tokens[0] | 
| David Zeuthen | 40ee1da | 2016-11-23 15:14:49 -0500 | [diff] [blame] | 1890 |         desc.rollback_index_location = int(cp_tokens[1]) | 
 | 1891 |         if desc.rollback_index_location < 1: | 
 | 1892 |           raise AvbError('Rollback index location must be 1 or larger.') | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1893 |         file_path = cp_tokens[2] | 
 | 1894 |         desc.public_key = open(file_path, 'rb').read() | 
 | 1895 |         descriptors.append(desc) | 
 | 1896 |  | 
 | 1897 |     vbmeta_blob = self._generate_vbmeta_blob( | 
| David Zeuthen | 18666ab | 2016-11-15 11:18:05 -0500 | [diff] [blame] | 1898 |         algorithm_name, key_path, public_key_metadata_path, descriptors, | 
| David Zeuthen | fd41eb9 | 2016-11-17 12:24:47 -0500 | [diff] [blame] | 1899 |         rollback_index, flags, props, props_from_file, kernel_cmdlines, | 
| David Zeuthen | 5d4f4f2 | 2017-01-11 11:37:34 -0500 | [diff] [blame] | 1900 |         setup_rootfs_from_kernel, | 
| Dzmitry Yatsushkevich | 4e55279 | 2016-12-15 10:27:48 -0800 | [diff] [blame] | 1901 |         include_descriptors_from_image, signing_helper) | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1902 |  | 
 | 1903 |     # Write entire vbmeta blob (header, authentication, auxiliary). | 
 | 1904 |     output.seek(0) | 
 | 1905 |     output.write(vbmeta_blob) | 
 | 1906 |  | 
| David Zeuthen | 18666ab | 2016-11-15 11:18:05 -0500 | [diff] [blame] | 1907 |   def _generate_vbmeta_blob(self, algorithm_name, key_path, | 
 | 1908 |                             public_key_metadata_path, descriptors, | 
| David Zeuthen | fd41eb9 | 2016-11-17 12:24:47 -0500 | [diff] [blame] | 1909 |                             rollback_index, flags, props, props_from_file, | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1910 |                             kernel_cmdlines, | 
| David Zeuthen | 5d4f4f2 | 2017-01-11 11:37:34 -0500 | [diff] [blame] | 1911 |                             setup_rootfs_from_kernel, | 
| Dzmitry Yatsushkevich | 4e55279 | 2016-12-15 10:27:48 -0800 | [diff] [blame] | 1912 |                             include_descriptors_from_image, signing_helper): | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1913 |     """Generates a VBMeta blob. | 
 | 1914 |  | 
 | 1915 |     This blob contains the header (struct AvbVBMetaHeader), the | 
 | 1916 |     authentication data block (which contains the hash and signature | 
 | 1917 |     for the header and auxiliary block), and the auxiliary block | 
 | 1918 |     (which contains descriptors, the public key used, and other data). | 
 | 1919 |  | 
 | 1920 |     The |key| parameter can |None| only if the |algorithm_name| is | 
 | 1921 |     'NONE'. | 
 | 1922 |  | 
 | 1923 |     Arguments: | 
 | 1924 |       algorithm_name: The algorithm name as per the ALGORITHMS dict. | 
 | 1925 |       key_path: The path to the .pem file used to sign the blob. | 
| David Zeuthen | 18666ab | 2016-11-15 11:18:05 -0500 | [diff] [blame] | 1926 |       public_key_metadata_path: Path to public key metadata or None. | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1927 |       descriptors: A list of descriptors to insert or None. | 
 | 1928 |       rollback_index: The rollback index to use. | 
| David Zeuthen | fd41eb9 | 2016-11-17 12:24:47 -0500 | [diff] [blame] | 1929 |       flags: Flags to use in the image. | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1930 |       props: Properties to insert (List of strings of the form 'key:value'). | 
 | 1931 |       props_from_file: Properties to insert (List of strings 'key:<path>'). | 
 | 1932 |       kernel_cmdlines: Kernel cmdlines to insert (list of strings). | 
| David Zeuthen | 5d4f4f2 | 2017-01-11 11:37:34 -0500 | [diff] [blame] | 1933 |       setup_rootfs_from_kernel: None or file to generate | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1934 |         dm-verity kernel cmdline from. | 
 | 1935 |       include_descriptors_from_image: List of file objects for which | 
 | 1936 |         to insert descriptors from. | 
| Dzmitry Yatsushkevich | 4e55279 | 2016-12-15 10:27:48 -0800 | [diff] [blame] | 1937 |       signing_helper: Program which signs a hash and return signature. | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1938 |  | 
 | 1939 |     Returns: | 
 | 1940 |       A bytearray() with the VBMeta blob. | 
 | 1941 |  | 
 | 1942 |     Raises: | 
 | 1943 |       Exception: If the |algorithm_name| is not found, if no key has | 
 | 1944 |         been given and the given algorithm requires one, or the key is | 
 | 1945 |         of the wrong size. | 
 | 1946 |  | 
 | 1947 |     """ | 
 | 1948 |     try: | 
 | 1949 |       alg = ALGORITHMS[algorithm_name] | 
 | 1950 |     except KeyError: | 
 | 1951 |       raise AvbError('Unknown algorithm with name {}'.format(algorithm_name)) | 
 | 1952 |  | 
 | 1953 |     # Descriptors. | 
 | 1954 |     encoded_descriptors = bytearray() | 
 | 1955 |     if descriptors: | 
 | 1956 |       for desc in descriptors: | 
 | 1957 |         encoded_descriptors.extend(desc.encode()) | 
 | 1958 |  | 
 | 1959 |     # Add properties. | 
 | 1960 |     if props: | 
 | 1961 |       for prop in props: | 
 | 1962 |         idx = prop.find(':') | 
 | 1963 |         if idx == -1: | 
 | 1964 |           raise AvbError('Malformed property "{}".'.format(prop)) | 
 | 1965 |         desc = AvbPropertyDescriptor() | 
 | 1966 |         desc.key = prop[0:idx] | 
 | 1967 |         desc.value = prop[(idx + 1):] | 
 | 1968 |         encoded_descriptors.extend(desc.encode()) | 
 | 1969 |     if props_from_file: | 
 | 1970 |       for prop in props_from_file: | 
 | 1971 |         idx = prop.find(':') | 
 | 1972 |         if idx == -1: | 
 | 1973 |           raise AvbError('Malformed property "{}".'.format(prop)) | 
 | 1974 |         desc = AvbPropertyDescriptor() | 
 | 1975 |         desc.key = prop[0:idx] | 
 | 1976 |         desc.value = prop[(idx + 1):] | 
 | 1977 |         file_path = prop[(idx + 1):] | 
 | 1978 |         desc.value = open(file_path, 'rb').read() | 
 | 1979 |         encoded_descriptors.extend(desc.encode()) | 
 | 1980 |  | 
 | 1981 |     # Add AvbKernelCmdline descriptor for dm-verity, if requested. | 
| David Zeuthen | 5d4f4f2 | 2017-01-11 11:37:34 -0500 | [diff] [blame] | 1982 |     if setup_rootfs_from_kernel: | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 1983 |       image_handler = ImageHandler( | 
| David Zeuthen | 5d4f4f2 | 2017-01-11 11:37:34 -0500 | [diff] [blame] | 1984 |           setup_rootfs_from_kernel.name) | 
| David Zeuthen | fd41eb9 | 2016-11-17 12:24:47 -0500 | [diff] [blame] | 1985 |       cmdline_desc = self._get_cmdline_descriptors_for_dm_verity(image_handler) | 
 | 1986 |       encoded_descriptors.extend(cmdline_desc[0].encode()) | 
 | 1987 |       encoded_descriptors.extend(cmdline_desc[1].encode()) | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 1988 |  | 
 | 1989 |     # Add kernel command-lines. | 
 | 1990 |     if kernel_cmdlines: | 
 | 1991 |       for i in kernel_cmdlines: | 
 | 1992 |         desc = AvbKernelCmdlineDescriptor() | 
 | 1993 |         desc.kernel_cmdline = i | 
 | 1994 |         encoded_descriptors.extend(desc.encode()) | 
 | 1995 |  | 
 | 1996 |     # Add descriptors from other images. | 
 | 1997 |     if include_descriptors_from_image: | 
 | 1998 |       for image in include_descriptors_from_image: | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 1999 |         image_handler = ImageHandler(image.name) | 
 | 2000 |         (_, _, image_descriptors, _) = self._parse_image(image_handler) | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2001 |         for desc in image_descriptors: | 
 | 2002 |           encoded_descriptors.extend(desc.encode()) | 
 | 2003 |  | 
| David Zeuthen | 18666ab | 2016-11-15 11:18:05 -0500 | [diff] [blame] | 2004 |     # Load public key metadata blob, if requested. | 
 | 2005 |     pkmd_blob = [] | 
 | 2006 |     if public_key_metadata_path: | 
 | 2007 |       with open(public_key_metadata_path) as f: | 
 | 2008 |         pkmd_blob = f.read() | 
 | 2009 |  | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2010 |     key = None | 
 | 2011 |     encoded_key = bytearray() | 
 | 2012 |     if alg.public_key_num_bytes > 0: | 
 | 2013 |       if not key_path: | 
 | 2014 |         raise AvbError('Key is required for algorithm {}'.format( | 
 | 2015 |             algorithm_name)) | 
 | 2016 |       key = Crypto.PublicKey.RSA.importKey(open(key_path).read()) | 
 | 2017 |       encoded_key = encode_rsa_key(key) | 
 | 2018 |       if len(encoded_key) != alg.public_key_num_bytes: | 
 | 2019 |         raise AvbError('Key is wrong size for algorithm {}'.format( | 
 | 2020 |             algorithm_name)) | 
 | 2021 |  | 
 | 2022 |     h = AvbVBMetaHeader() | 
 | 2023 |  | 
| David Zeuthen | 18666ab | 2016-11-15 11:18:05 -0500 | [diff] [blame] | 2024 |     # For the Auxiliary data block, descriptors are stored at offset 0, | 
 | 2025 |     # followed by the public key, followed by the public key metadata blob. | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2026 |     h.auxiliary_data_block_size = round_to_multiple( | 
| David Zeuthen | 18666ab | 2016-11-15 11:18:05 -0500 | [diff] [blame] | 2027 |         len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64) | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2028 |     h.descriptors_offset = 0 | 
 | 2029 |     h.descriptors_size = len(encoded_descriptors) | 
 | 2030 |     h.public_key_offset = h.descriptors_size | 
 | 2031 |     h.public_key_size = len(encoded_key) | 
| David Zeuthen | 18666ab | 2016-11-15 11:18:05 -0500 | [diff] [blame] | 2032 |     h.public_key_metadata_offset = h.public_key_offset + h.public_key_size | 
 | 2033 |     h.public_key_metadata_size = len(pkmd_blob) | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2034 |  | 
 | 2035 |     # For the Authentication data block, the hash is first and then | 
 | 2036 |     # the signature. | 
 | 2037 |     h.authentication_data_block_size = round_to_multiple( | 
| David Zeuthen | d5db21d | 2017-01-24 10:11:38 -0500 | [diff] [blame] | 2038 |         alg.hash_num_bytes + alg.signature_num_bytes, 64) | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2039 |     h.algorithm_type = alg.algorithm_type | 
 | 2040 |     h.hash_offset = 0 | 
 | 2041 |     h.hash_size = alg.hash_num_bytes | 
 | 2042 |     # Signature offset and size - it's stored right after the hash | 
 | 2043 |     # (in Authentication data block). | 
 | 2044 |     h.signature_offset = alg.hash_num_bytes | 
 | 2045 |     h.signature_size = alg.signature_num_bytes | 
 | 2046 |  | 
 | 2047 |     h.rollback_index = rollback_index | 
| David Zeuthen | fd41eb9 | 2016-11-17 12:24:47 -0500 | [diff] [blame] | 2048 |     h.flags = flags | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2049 |  | 
 | 2050 |     # Generate Header data block. | 
 | 2051 |     header_data_blob = h.encode() | 
 | 2052 |  | 
 | 2053 |     # Generate Auxiliary data block. | 
 | 2054 |     aux_data_blob = bytearray() | 
 | 2055 |     aux_data_blob.extend(encoded_descriptors) | 
 | 2056 |     aux_data_blob.extend(encoded_key) | 
| David Zeuthen | 18666ab | 2016-11-15 11:18:05 -0500 | [diff] [blame] | 2057 |     aux_data_blob.extend(pkmd_blob) | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2058 |     padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob) | 
 | 2059 |     aux_data_blob.extend('\0' * padding_bytes) | 
 | 2060 |  | 
 | 2061 |     # Calculate the hash. | 
 | 2062 |     binary_hash = bytearray() | 
 | 2063 |     binary_signature = bytearray() | 
 | 2064 |     if algorithm_name != 'NONE': | 
 | 2065 |       if algorithm_name[0:6] == 'SHA256': | 
 | 2066 |         ha = hashlib.sha256() | 
 | 2067 |       elif algorithm_name[0:6] == 'SHA512': | 
 | 2068 |         ha = hashlib.sha512() | 
 | 2069 |       else: | 
 | 2070 |         raise AvbError('Unsupported algorithm {}.'.format(algorithm_name)) | 
 | 2071 |       ha.update(header_data_blob) | 
 | 2072 |       ha.update(aux_data_blob) | 
 | 2073 |       binary_hash.extend(ha.digest()) | 
 | 2074 |  | 
 | 2075 |       # Calculate the signature. | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2076 |       padding_and_hash = str(bytearray(alg.padding)) + binary_hash | 
| Darren Krahn | 147b08d | 2016-12-20 16:38:29 -0800 | [diff] [blame] | 2077 |       binary_signature.extend(raw_sign(signing_helper, algorithm_name, key_path, | 
 | 2078 |                                        padding_and_hash)) | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2079 |  | 
 | 2080 |     # Generate Authentication data block. | 
 | 2081 |     auth_data_blob = bytearray() | 
 | 2082 |     auth_data_blob.extend(binary_hash) | 
 | 2083 |     auth_data_blob.extend(binary_signature) | 
 | 2084 |     padding_bytes = h.authentication_data_block_size - len(auth_data_blob) | 
 | 2085 |     auth_data_blob.extend('\0' * padding_bytes) | 
 | 2086 |  | 
 | 2087 |     return header_data_blob + auth_data_blob + aux_data_blob | 
 | 2088 |  | 
 | 2089 |   def extract_public_key(self, key_path, output): | 
 | 2090 |     """Implements the 'extract_public_key' command. | 
 | 2091 |  | 
 | 2092 |     Arguments: | 
 | 2093 |       key_path: The path to a RSA private key file. | 
 | 2094 |       output: The file to write to. | 
 | 2095 |     """ | 
 | 2096 |     key = Crypto.PublicKey.RSA.importKey(open(key_path).read()) | 
 | 2097 |     write_rsa_key(output, key) | 
 | 2098 |  | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 2099 |   def add_hash_footer(self, image_filename, partition_size, partition_name, | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2100 |                       hash_algorithm, salt, algorithm_name, key_path, | 
| David Zeuthen | 18666ab | 2016-11-15 11:18:05 -0500 | [diff] [blame] | 2101 |                       public_key_metadata_path, rollback_index, props, | 
 | 2102 |                       props_from_file, kernel_cmdlines, | 
| David Zeuthen | 5d4f4f2 | 2017-01-11 11:37:34 -0500 | [diff] [blame] | 2103 |                       setup_rootfs_from_kernel, | 
| David Zeuthen | d247fcb | 2017-02-16 12:09:27 -0500 | [diff] [blame^] | 2104 |                       include_descriptors_from_image, signing_helper, | 
 | 2105 |                       output_vbmeta_image, do_not_append_vbmeta_image): | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 2106 |     """Implementation of the add_hash_footer on unsparse images. | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2107 |  | 
 | 2108 |     Arguments: | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 2109 |       image_filename: File to add the footer to. | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2110 |       partition_size: Size of partition. | 
 | 2111 |       partition_name: Name of partition (without A/B suffix). | 
 | 2112 |       hash_algorithm: Hash algorithm to use. | 
 | 2113 |       salt: Salt to use as a hexadecimal string or None to use /dev/urandom. | 
 | 2114 |       algorithm_name: Name of algorithm to use. | 
 | 2115 |       key_path: Path to key to use or None. | 
| David Zeuthen | 18666ab | 2016-11-15 11:18:05 -0500 | [diff] [blame] | 2116 |       public_key_metadata_path: Path to public key metadata or None. | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2117 |       rollback_index: Rollback index. | 
 | 2118 |       props: Properties to insert (List of strings of the form 'key:value'). | 
 | 2119 |       props_from_file: Properties to insert (List of strings 'key:<path>'). | 
 | 2120 |       kernel_cmdlines: Kernel cmdlines to insert (list of strings). | 
| David Zeuthen | 5d4f4f2 | 2017-01-11 11:37:34 -0500 | [diff] [blame] | 2121 |       setup_rootfs_from_kernel: None or file to generate | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2122 |         dm-verity kernel cmdline from. | 
 | 2123 |       include_descriptors_from_image: List of file objects for which | 
 | 2124 |         to insert descriptors from. | 
| Dzmitry Yatsushkevich | 4e55279 | 2016-12-15 10:27:48 -0800 | [diff] [blame] | 2125 |       signing_helper: Program which signs a hash and return signature. | 
| David Zeuthen | d247fcb | 2017-02-16 12:09:27 -0500 | [diff] [blame^] | 2126 |       output_vbmeta_image: If not None, also write vbmeta struct to this file. | 
 | 2127 |       do_not_append_vbmeta_image: If True, don't append vbmeta struct. | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 2128 |  | 
 | 2129 |     Raises: | 
 | 2130 |       AvbError: If an argument is incorrect. | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2131 |     """ | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 2132 |     image = ImageHandler(image_filename) | 
 | 2133 |  | 
 | 2134 |     if partition_size % image.block_size != 0: | 
 | 2135 |       raise AvbError('Partition size of {} is not a multiple of the image ' | 
 | 2136 |                      'block size {}.'.format(partition_size, | 
 | 2137 |                                              image.block_size)) | 
 | 2138 |  | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2139 |     # If there's already a footer, truncate the image to its original | 
 | 2140 |     # size. This way 'avbtool add_hash_footer' is idempotent (modulo | 
 | 2141 |     # salts). | 
| David Zeuthen | 0969269 | 2016-09-30 16:16:40 -0400 | [diff] [blame] | 2142 |     image.seek(image.image_size - AvbFooter.SIZE) | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2143 |     try: | 
 | 2144 |       footer = AvbFooter(image.read(AvbFooter.SIZE)) | 
 | 2145 |       # Existing footer found. Just truncate. | 
 | 2146 |       original_image_size = footer.original_image_size | 
| David Zeuthen | 0969269 | 2016-09-30 16:16:40 -0400 | [diff] [blame] | 2147 |       image.truncate(footer.original_image_size) | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2148 |     except (LookupError, struct.error): | 
| David Zeuthen | 0969269 | 2016-09-30 16:16:40 -0400 | [diff] [blame] | 2149 |       original_image_size = image.image_size | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2150 |  | 
 | 2151 |     # If anything goes wrong from here-on, restore the image back to | 
 | 2152 |     # its original size. | 
 | 2153 |     try: | 
| David Zeuthen | 0969269 | 2016-09-30 16:16:40 -0400 | [diff] [blame] | 2154 |       # First, calculate the maximum image size such that an image | 
 | 2155 |       # this size + metadata (footer + vbmeta struct) fits in | 
 | 2156 |       # |partition_size|. | 
 | 2157 |       max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE | 
 | 2158 |       max_image_size = partition_size - max_metadata_size | 
 | 2159 |  | 
 | 2160 |       # If image size exceeds the maximum image size, fail. | 
 | 2161 |       if image.image_size > max_image_size: | 
 | 2162 |         raise AvbError('Image size of {} exceeds maximum image ' | 
 | 2163 |                        'size of {} in order to fit in a partition ' | 
 | 2164 |                        'size of {}.'.format(image.image_size, max_image_size, | 
 | 2165 |                                             partition_size)) | 
 | 2166 |  | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2167 |       digest_size = len(hashlib.new(name=hash_algorithm).digest()) | 
 | 2168 |       if salt: | 
 | 2169 |         salt = salt.decode('hex') | 
 | 2170 |       else: | 
 | 2171 |         if salt is None: | 
 | 2172 |           # If salt is not explicitly specified, choose a hash | 
 | 2173 |           # that's the same size as the hash size. | 
 | 2174 |           hash_size = digest_size | 
 | 2175 |           salt = open('/dev/urandom').read(hash_size) | 
 | 2176 |         else: | 
 | 2177 |           salt = '' | 
 | 2178 |  | 
 | 2179 |       hasher = hashlib.new(name=hash_algorithm, string=salt) | 
 | 2180 |       # TODO(zeuthen): might want to read this in chunks to avoid | 
 | 2181 |       # memory pressure, then again, this is only supposed to be used | 
 | 2182 |       # on kernel/initramfs partitions. Possible optimization. | 
 | 2183 |       image.seek(0) | 
| David Zeuthen | 0969269 | 2016-09-30 16:16:40 -0400 | [diff] [blame] | 2184 |       hasher.update(image.read(image.image_size)) | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2185 |       digest = hasher.digest() | 
 | 2186 |  | 
 | 2187 |       h_desc = AvbHashDescriptor() | 
| David Zeuthen | 0969269 | 2016-09-30 16:16:40 -0400 | [diff] [blame] | 2188 |       h_desc.image_size = image.image_size | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2189 |       h_desc.hash_algorithm = hash_algorithm | 
 | 2190 |       h_desc.partition_name = partition_name | 
 | 2191 |       h_desc.salt = salt | 
 | 2192 |       h_desc.digest = digest | 
 | 2193 |  | 
| David Zeuthen | fd41eb9 | 2016-11-17 12:24:47 -0500 | [diff] [blame] | 2194 |       # Flags are only allowed on top-level vbmeta struct. | 
 | 2195 |       flags = 0 | 
 | 2196 |  | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2197 |       # Generate the VBMeta footer. | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2198 |       vbmeta_blob = self._generate_vbmeta_blob( | 
| David Zeuthen | 18666ab | 2016-11-15 11:18:05 -0500 | [diff] [blame] | 2199 |           algorithm_name, key_path, public_key_metadata_path, [h_desc], | 
| David Zeuthen | fd41eb9 | 2016-11-17 12:24:47 -0500 | [diff] [blame] | 2200 |           rollback_index, flags, props, props_from_file, kernel_cmdlines, | 
| David Zeuthen | 5d4f4f2 | 2017-01-11 11:37:34 -0500 | [diff] [blame] | 2201 |           setup_rootfs_from_kernel, | 
| Dzmitry Yatsushkevich | 4e55279 | 2016-12-15 10:27:48 -0800 | [diff] [blame] | 2202 |           include_descriptors_from_image, signing_helper) | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2203 |  | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 2204 |       # If the image isn't sparse, its size might not be a multiple of | 
 | 2205 |       # the block size. This will screw up padding later so just grow it. | 
| David Zeuthen | 0969269 | 2016-09-30 16:16:40 -0400 | [diff] [blame] | 2206 |       if image.image_size % image.block_size != 0: | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 2207 |         assert not image.is_sparse | 
| David Zeuthen | 0969269 | 2016-09-30 16:16:40 -0400 | [diff] [blame] | 2208 |         padding_needed = image.block_size - (image.image_size%image.block_size) | 
 | 2209 |         image.truncate(image.image_size + padding_needed) | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2210 |  | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 2211 |       # The append_raw() method requires content with size being a | 
 | 2212 |       # multiple of |block_size| so add padding as needed. Also record | 
 | 2213 |       # where this is written to since we'll need to put that in the | 
 | 2214 |       # footer. | 
| David Zeuthen | 0969269 | 2016-09-30 16:16:40 -0400 | [diff] [blame] | 2215 |       vbmeta_offset = image.image_size | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 2216 |       padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) - | 
 | 2217 |                         len(vbmeta_blob)) | 
 | 2218 |       vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 2219 |  | 
| David Zeuthen | d247fcb | 2017-02-16 12:09:27 -0500 | [diff] [blame^] | 2220 |       # Write vbmeta blob, if requested. | 
 | 2221 |       if output_vbmeta_image: | 
 | 2222 |         output_vbmeta_image.write(vbmeta_blob) | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 2223 |  | 
| David Zeuthen | d247fcb | 2017-02-16 12:09:27 -0500 | [diff] [blame^] | 2224 |       # Append vbmeta blob and footer, unless requested not to. | 
 | 2225 |       if not do_not_append_vbmeta_image: | 
 | 2226 |         image.append_raw(vbmeta_blob_with_padding) | 
 | 2227 |         vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding) | 
 | 2228 |  | 
 | 2229 |         # Now insert a DONT_CARE chunk with enough bytes such that the | 
 | 2230 |         # final Footer block is at the end of partition_size.. | 
 | 2231 |         image.append_dont_care(partition_size - vbmeta_end_offset - | 
 | 2232 |                                1*image.block_size) | 
 | 2233 |  | 
 | 2234 |         # Generate the Footer that tells where the VBMeta footer | 
 | 2235 |         # is. Also put enough padding in the front of the footer since | 
 | 2236 |         # we'll write out an entire block. | 
 | 2237 |         footer = AvbFooter() | 
 | 2238 |         footer.original_image_size = original_image_size | 
 | 2239 |         footer.vbmeta_offset = vbmeta_offset | 
 | 2240 |         footer.vbmeta_size = len(vbmeta_blob) | 
 | 2241 |         footer_blob = footer.encode() | 
 | 2242 |         footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) + | 
 | 2243 |                                     footer_blob) | 
 | 2244 |         image.append_raw(footer_blob_with_padding) | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 2245 |  | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2246 |     except: | 
 | 2247 |       # Truncate back to original size, then re-raise | 
 | 2248 |       image.truncate(original_image_size) | 
 | 2249 |       raise | 
 | 2250 |  | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 2251 |   def add_hashtree_footer(self, image_filename, partition_size, partition_name, | 
| David Zeuthen | 0b7f1d3 | 2016-10-25 17:53:49 -0400 | [diff] [blame] | 2252 |                           generate_fec, fec_num_roots, hash_algorithm, | 
 | 2253 |                           block_size, salt, algorithm_name, key_path, | 
| David Zeuthen | fd41eb9 | 2016-11-17 12:24:47 -0500 | [diff] [blame] | 2254 |                           public_key_metadata_path, rollback_index, | 
 | 2255 |                           props, props_from_file, kernel_cmdlines, | 
| David Zeuthen | 5d4f4f2 | 2017-01-11 11:37:34 -0500 | [diff] [blame] | 2256 |                           setup_rootfs_from_kernel, | 
| David Zeuthen | 0969269 | 2016-09-30 16:16:40 -0400 | [diff] [blame] | 2257 |                           include_descriptors_from_image, | 
| David Zeuthen | d247fcb | 2017-02-16 12:09:27 -0500 | [diff] [blame^] | 2258 |                           calc_max_image_size, signing_helper, | 
 | 2259 |                           output_vbmeta_image, do_not_append_vbmeta_image): | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2260 |     """Implements the 'add_hashtree_footer' command. | 
 | 2261 |  | 
 | 2262 |     See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for | 
 | 2263 |     more information about dm-verity and these hashes. | 
 | 2264 |  | 
 | 2265 |     Arguments: | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 2266 |       image_filename: File to add the footer to. | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2267 |       partition_size: Size of partition. | 
 | 2268 |       partition_name: Name of partition (without A/B suffix). | 
| David Zeuthen | 0b7f1d3 | 2016-10-25 17:53:49 -0400 | [diff] [blame] | 2269 |       generate_fec: If True, generate FEC codes. | 
 | 2270 |       fec_num_roots: Number of roots for FEC. | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2271 |       hash_algorithm: Hash algorithm to use. | 
 | 2272 |       block_size: Block size to use. | 
 | 2273 |       salt: Salt to use as a hexadecimal string or None to use /dev/urandom. | 
 | 2274 |       algorithm_name: Name of algorithm to use. | 
 | 2275 |       key_path: Path to key to use or None. | 
| David Zeuthen | 18666ab | 2016-11-15 11:18:05 -0500 | [diff] [blame] | 2276 |       public_key_metadata_path: Path to public key metadata or None. | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2277 |       rollback_index: Rollback index. | 
 | 2278 |       props: Properties to insert (List of strings of the form 'key:value'). | 
 | 2279 |       props_from_file: Properties to insert (List of strings 'key:<path>'). | 
 | 2280 |       kernel_cmdlines: Kernel cmdlines to insert (list of strings). | 
| David Zeuthen | 5d4f4f2 | 2017-01-11 11:37:34 -0500 | [diff] [blame] | 2281 |       setup_rootfs_from_kernel: None or file to generate | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2282 |         dm-verity kernel cmdline from. | 
 | 2283 |       include_descriptors_from_image: List of file objects for which | 
 | 2284 |         to insert descriptors from. | 
| David Zeuthen | 0969269 | 2016-09-30 16:16:40 -0400 | [diff] [blame] | 2285 |       calc_max_image_size: Don't store the hashtree or footer - instead | 
 | 2286 |         calculate the maximum image size leaving enough room for hashtree | 
 | 2287 |         and metadata with the given |partition_size|. | 
| Dzmitry Yatsushkevich | 4e55279 | 2016-12-15 10:27:48 -0800 | [diff] [blame] | 2288 |       signing_helper: Program which signs a hash and return signature. | 
| David Zeuthen | d247fcb | 2017-02-16 12:09:27 -0500 | [diff] [blame^] | 2289 |       output_vbmeta_image: If not None, also write vbmeta struct to this file. | 
 | 2290 |       do_not_append_vbmeta_image: If True, don't append vbmeta struct. | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 2291 |  | 
 | 2292 |     Raises: | 
 | 2293 |       AvbError: If an argument is incorrect. | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2294 |     """ | 
| David Zeuthen | 0969269 | 2016-09-30 16:16:40 -0400 | [diff] [blame] | 2295 |     digest_size = len(hashlib.new(name=hash_algorithm).digest()) | 
 | 2296 |     digest_padding = round_to_pow2(digest_size) - digest_size | 
 | 2297 |  | 
 | 2298 |     # First, calculate the maximum image size such that an image | 
 | 2299 |     # this size + the hashtree + metadata (footer + vbmeta struct) | 
 | 2300 |     # fits in |partition_size|. We use very conservative figures for | 
 | 2301 |     # metadata. | 
 | 2302 |     (_, max_tree_size) = calc_hash_level_offsets( | 
 | 2303 |         partition_size, block_size, digest_size + digest_padding) | 
| David Zeuthen | 0b7f1d3 | 2016-10-25 17:53:49 -0400 | [diff] [blame] | 2304 |     max_fec_size = 0 | 
 | 2305 |     if generate_fec: | 
 | 2306 |       max_fec_size = calc_fec_data_size(partition_size, fec_num_roots) | 
 | 2307 |     max_metadata_size = (max_fec_size + max_tree_size + | 
 | 2308 |                          self.MAX_VBMETA_SIZE + | 
| David Zeuthen | 0969269 | 2016-09-30 16:16:40 -0400 | [diff] [blame] | 2309 |                          self.MAX_FOOTER_SIZE) | 
 | 2310 |     max_image_size = partition_size - max_metadata_size | 
 | 2311 |  | 
 | 2312 |     # If we're asked to only calculate the maximum image size, we're done. | 
 | 2313 |     if calc_max_image_size: | 
 | 2314 |       print '{}'.format(max_image_size) | 
 | 2315 |       return | 
 | 2316 |  | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 2317 |     image = ImageHandler(image_filename) | 
 | 2318 |  | 
 | 2319 |     if partition_size % image.block_size != 0: | 
 | 2320 |       raise AvbError('Partition size of {} is not a multiple of the image ' | 
 | 2321 |                      'block size {}.'.format(partition_size, | 
 | 2322 |                                              image.block_size)) | 
 | 2323 |  | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2324 |     # If there's already a footer, truncate the image to its original | 
 | 2325 |     # size. This way 'avbtool add_hashtree_footer' is idempotent | 
 | 2326 |     # (modulo salts). | 
| David Zeuthen | 0969269 | 2016-09-30 16:16:40 -0400 | [diff] [blame] | 2327 |     image.seek(image.image_size - AvbFooter.SIZE) | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2328 |     try: | 
 | 2329 |       footer = AvbFooter(image.read(AvbFooter.SIZE)) | 
 | 2330 |       # Existing footer found. Just truncate. | 
 | 2331 |       original_image_size = footer.original_image_size | 
| David Zeuthen | 0969269 | 2016-09-30 16:16:40 -0400 | [diff] [blame] | 2332 |       image.truncate(footer.original_image_size) | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2333 |     except (LookupError, struct.error): | 
| David Zeuthen | 0969269 | 2016-09-30 16:16:40 -0400 | [diff] [blame] | 2334 |       original_image_size = image.image_size | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2335 |  | 
 | 2336 |     # If anything goes wrong from here-on, restore the image back to | 
 | 2337 |     # its original size. | 
 | 2338 |     try: | 
 | 2339 |       # Ensure image is multiple of block_size. | 
| David Zeuthen | 0969269 | 2016-09-30 16:16:40 -0400 | [diff] [blame] | 2340 |       rounded_image_size = round_to_multiple(image.image_size, block_size) | 
 | 2341 |       if rounded_image_size > image.image_size: | 
 | 2342 |         image.append_raw('\0' * (rounded_image_size - image.image_size)) | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2343 |  | 
| David Zeuthen | 0969269 | 2016-09-30 16:16:40 -0400 | [diff] [blame] | 2344 |       # If image size exceeds the maximum image size, fail. | 
 | 2345 |       if image.image_size > max_image_size: | 
 | 2346 |         raise AvbError('Image size of {} exceeds maximum image ' | 
 | 2347 |                        'size of {} in order to fit in a partition ' | 
 | 2348 |                        'size of {}.'.format(image.image_size, max_image_size, | 
 | 2349 |                                             partition_size)) | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2350 |  | 
 | 2351 |       if salt: | 
 | 2352 |         salt = salt.decode('hex') | 
 | 2353 |       else: | 
 | 2354 |         if salt is None: | 
 | 2355 |           # If salt is not explicitly specified, choose a hash | 
 | 2356 |           # that's the same size as the hash size. | 
 | 2357 |           hash_size = digest_size | 
 | 2358 |           salt = open('/dev/urandom').read(hash_size) | 
 | 2359 |         else: | 
 | 2360 |           salt = '' | 
 | 2361 |  | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 2362 |       # Hashes are stored upside down so we need to calculate hash | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2363 |       # offsets in advance. | 
 | 2364 |       (hash_level_offsets, tree_size) = calc_hash_level_offsets( | 
| David Zeuthen | 0969269 | 2016-09-30 16:16:40 -0400 | [diff] [blame] | 2365 |           image.image_size, block_size, digest_size + digest_padding) | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2366 |  | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 2367 |       # If the image isn't sparse, its size might not be a multiple of | 
 | 2368 |       # the block size. This will screw up padding later so just grow it. | 
| David Zeuthen | 0969269 | 2016-09-30 16:16:40 -0400 | [diff] [blame] | 2369 |       if image.image_size % image.block_size != 0: | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 2370 |         assert not image.is_sparse | 
| David Zeuthen | 0969269 | 2016-09-30 16:16:40 -0400 | [diff] [blame] | 2371 |         padding_needed = image.block_size - (image.image_size%image.block_size) | 
 | 2372 |         image.truncate(image.image_size + padding_needed) | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2373 |  | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 2374 |       # Generate the tree and add padding as needed. | 
| David Zeuthen | 0969269 | 2016-09-30 16:16:40 -0400 | [diff] [blame] | 2375 |       tree_offset = image.image_size | 
 | 2376 |       root_digest, hash_tree = generate_hash_tree(image, image.image_size, | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 2377 |                                                   block_size, | 
 | 2378 |                                                   hash_algorithm, salt, | 
 | 2379 |                                                   digest_padding, | 
 | 2380 |                                                   hash_level_offsets, | 
 | 2381 |                                                   tree_size) | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 2382 |  | 
 | 2383 |       # Generate HashtreeDescriptor with details about the tree we | 
 | 2384 |       # just generated. | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2385 |       ht_desc = AvbHashtreeDescriptor() | 
 | 2386 |       ht_desc.dm_verity_version = 1 | 
| David Zeuthen | 0969269 | 2016-09-30 16:16:40 -0400 | [diff] [blame] | 2387 |       ht_desc.image_size = image.image_size | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2388 |       ht_desc.tree_offset = tree_offset | 
 | 2389 |       ht_desc.tree_size = tree_size | 
 | 2390 |       ht_desc.data_block_size = block_size | 
 | 2391 |       ht_desc.hash_block_size = block_size | 
 | 2392 |       ht_desc.hash_algorithm = hash_algorithm | 
 | 2393 |       ht_desc.partition_name = partition_name | 
 | 2394 |       ht_desc.salt = salt | 
 | 2395 |       ht_desc.root_digest = root_digest | 
 | 2396 |  | 
| David Zeuthen | 0969269 | 2016-09-30 16:16:40 -0400 | [diff] [blame] | 2397 |       # Write the hash tree | 
 | 2398 |       padding_needed = (round_to_multiple(len(hash_tree), image.block_size) - | 
 | 2399 |                         len(hash_tree)) | 
 | 2400 |       hash_tree_with_padding = hash_tree + '\0'*padding_needed | 
 | 2401 |       image.append_raw(hash_tree_with_padding) | 
| David Zeuthen | 0b7f1d3 | 2016-10-25 17:53:49 -0400 | [diff] [blame] | 2402 |       len_hashtree_and_fec = len(hash_tree_with_padding) | 
 | 2403 |  | 
 | 2404 |       # Generate FEC codes, if requested. | 
 | 2405 |       if generate_fec: | 
 | 2406 |         fec_data = generate_fec_data(image_filename, fec_num_roots) | 
 | 2407 |         padding_needed = (round_to_multiple(len(fec_data), image.block_size) - | 
 | 2408 |                           len(fec_data)) | 
 | 2409 |         fec_data_with_padding = fec_data + '\0'*padding_needed | 
 | 2410 |         fec_offset = image.image_size | 
 | 2411 |         image.append_raw(fec_data_with_padding) | 
 | 2412 |         len_hashtree_and_fec += len(fec_data_with_padding) | 
 | 2413 |         # Update the hashtree descriptor. | 
 | 2414 |         ht_desc.fec_num_roots = fec_num_roots | 
 | 2415 |         ht_desc.fec_offset = fec_offset | 
 | 2416 |         ht_desc.fec_size = len(fec_data) | 
| David Zeuthen | 0969269 | 2016-09-30 16:16:40 -0400 | [diff] [blame] | 2417 |  | 
| David Zeuthen | fd41eb9 | 2016-11-17 12:24:47 -0500 | [diff] [blame] | 2418 |       # Flags are only allowed on top-level vbmeta struct. | 
 | 2419 |       flags = 0 | 
 | 2420 |  | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 2421 |       # Generate the VBMeta footer and add padding as needed. | 
| David Zeuthen | 0b7f1d3 | 2016-10-25 17:53:49 -0400 | [diff] [blame] | 2422 |       vbmeta_offset = tree_offset + len_hashtree_and_fec | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2423 |       vbmeta_blob = self._generate_vbmeta_blob( | 
| David Zeuthen | 18666ab | 2016-11-15 11:18:05 -0500 | [diff] [blame] | 2424 |           algorithm_name, key_path, public_key_metadata_path, [ht_desc], | 
| David Zeuthen | fd41eb9 | 2016-11-17 12:24:47 -0500 | [diff] [blame] | 2425 |           rollback_index, flags, props, props_from_file, kernel_cmdlines, | 
| David Zeuthen | 5d4f4f2 | 2017-01-11 11:37:34 -0500 | [diff] [blame] | 2426 |           setup_rootfs_from_kernel, | 
| Dzmitry Yatsushkevich | 4e55279 | 2016-12-15 10:27:48 -0800 | [diff] [blame] | 2427 |           include_descriptors_from_image, signing_helper) | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 2428 |       padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) - | 
 | 2429 |                         len(vbmeta_blob)) | 
 | 2430 |       vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2431 |  | 
| David Zeuthen | d247fcb | 2017-02-16 12:09:27 -0500 | [diff] [blame^] | 2432 |       # Write vbmeta blob, if requested. | 
 | 2433 |       if output_vbmeta_image: | 
 | 2434 |         output_vbmeta_image.write(vbmeta_blob) | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2435 |  | 
| David Zeuthen | d247fcb | 2017-02-16 12:09:27 -0500 | [diff] [blame^] | 2436 |       # Append vbmeta blob and footer, unless requested not to. | 
 | 2437 |       if not do_not_append_vbmeta_image: | 
 | 2438 |         image.append_raw(vbmeta_blob_with_padding) | 
 | 2439 |  | 
 | 2440 |         # Now insert a DONT_CARE chunk with enough bytes such that the | 
 | 2441 |         # final Footer block is at the end of partition_size.. | 
 | 2442 |         image.append_dont_care(partition_size - image.image_size - | 
 | 2443 |                                1*image.block_size) | 
 | 2444 |  | 
 | 2445 |         # Generate the Footer that tells where the VBMeta footer | 
 | 2446 |         # is. Also put enough padding in the front of the footer since | 
 | 2447 |         # we'll write out an entire block. | 
 | 2448 |         footer = AvbFooter() | 
 | 2449 |         footer.original_image_size = original_image_size | 
 | 2450 |         footer.vbmeta_offset = vbmeta_offset | 
 | 2451 |         footer.vbmeta_size = len(vbmeta_blob) | 
 | 2452 |         footer_blob = footer.encode() | 
 | 2453 |         footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) + | 
 | 2454 |                                     footer_blob) | 
 | 2455 |         image.append_raw(footer_blob_with_padding) | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 2456 |  | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2457 |     except: | 
| David Zeuthen | 0969269 | 2016-09-30 16:16:40 -0400 | [diff] [blame] | 2458 |       # Truncate back to original size, then re-raise. | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2459 |       image.truncate(original_image_size) | 
 | 2460 |       raise | 
 | 2461 |  | 
| Darren Krahn | 147b08d | 2016-12-20 16:38:29 -0800 | [diff] [blame] | 2462 |   def make_atx_certificate(self, output, authority_key_path, subject_key, | 
 | 2463 |                            subject_key_version, subject, | 
 | 2464 |                            is_intermediate_authority, signing_helper): | 
 | 2465 |     """Implements the 'make_atx_certificate' command. | 
 | 2466 |  | 
 | 2467 |     Android Things certificates are required for Android Things public key | 
 | 2468 |     metadata. They chain the vbmeta signing key for a particular product back to | 
 | 2469 |     a fused, permanent root key. These certificates are fixed-length and fixed- | 
 | 2470 |     format with the explicit goal of not parsing ASN.1 in bootloader code. | 
 | 2471 |  | 
 | 2472 |     Arguments: | 
 | 2473 |       output: Certificate will be written to this file on success. | 
 | 2474 |       authority_key_path: A PEM file path with the authority private key. | 
 | 2475 |                           If None, then a certificate will be created without a | 
 | 2476 |                           signature. The signature can be created out-of-band | 
 | 2477 |                           and appended. | 
 | 2478 |       subject_key: A PEM or DER subject public key. | 
 | 2479 |       subject_key_version: A 64-bit version value. If this is None, the number | 
 | 2480 |                            of seconds since the epoch is used. | 
 | 2481 |       subject: A subject identifier. For Product Signing Key certificates this | 
 | 2482 |                should be the same Product ID found in the permanent attributes. | 
 | 2483 |       is_intermediate_authority: True if the certificate is for an intermediate | 
 | 2484 |                                  authority. | 
 | 2485 |       signing_helper: Program which signs a hash and returns the signature. | 
 | 2486 |     """ | 
 | 2487 |     signed_data = bytearray() | 
 | 2488 |     signed_data.extend(struct.pack('<I', 1))  # Format Version | 
 | 2489 |     signed_data.extend( | 
 | 2490 |         encode_rsa_key(Crypto.PublicKey.RSA.importKey(subject_key))) | 
 | 2491 |     hasher = hashlib.sha256() | 
 | 2492 |     hasher.update(subject) | 
 | 2493 |     signed_data.extend(hasher.digest()) | 
 | 2494 |     usage = 'com.google.android.things.vboot' | 
 | 2495 |     if is_intermediate_authority: | 
 | 2496 |       usage += '.ca' | 
 | 2497 |     hasher = hashlib.sha256() | 
 | 2498 |     hasher.update(usage) | 
 | 2499 |     signed_data.extend(hasher.digest()) | 
 | 2500 |     if not subject_key_version: | 
 | 2501 |       subject_key_version = int(time.time()) | 
 | 2502 |     signed_data.extend(struct.pack('<Q', subject_key_version)) | 
 | 2503 |     signature = bytearray() | 
 | 2504 |     if authority_key_path: | 
 | 2505 |       padding_and_hash = bytearray() | 
 | 2506 |       algorithm_name = None | 
 | 2507 |       hasher = None | 
 | 2508 |       if is_intermediate_authority: | 
 | 2509 |         hasher = hashlib.sha512() | 
 | 2510 |         algorithm_name = 'SHA512_RSA4096' | 
 | 2511 |       else: | 
 | 2512 |         hasher = hashlib.sha256() | 
 | 2513 |         algorithm_name = 'SHA256_RSA2048' | 
 | 2514 |       padding_and_hash.extend(ALGORITHMS[algorithm_name].padding) | 
 | 2515 |       hasher.update(signed_data) | 
 | 2516 |       padding_and_hash.extend(hasher.digest()) | 
 | 2517 |       signature.extend(raw_sign(signing_helper, algorithm_name, | 
 | 2518 |                                 authority_key_path, padding_and_hash)) | 
 | 2519 |     output.write(signed_data) | 
 | 2520 |     output.write(signature) | 
 | 2521 |  | 
 | 2522 |   def make_atx_permanent_attributes(self, output, root_authority_key, | 
 | 2523 |                                     product_id): | 
 | 2524 |     """Implements the 'make_atx_permanent_attributes' command. | 
 | 2525 |  | 
 | 2526 |     Android Things permanent attributes are designed to be permanent for a | 
 | 2527 |     particular product and a hash of these attributes should be fused into | 
 | 2528 |     hardware to enforce this. | 
 | 2529 |  | 
 | 2530 |     Arguments: | 
 | 2531 |       output: Attributes will be written to this file on success. | 
 | 2532 |       root_authority_key: A PEM or DER public key for the root authority. | 
 | 2533 |       product_id: A 16-byte Product ID. | 
 | 2534 |  | 
 | 2535 |     Raises: | 
 | 2536 |       AvbError: If an argument is incorrect. | 
 | 2537 |     """ | 
 | 2538 |     if len(product_id) != 16: | 
 | 2539 |       raise AvbError('Invalid Product ID length.') | 
 | 2540 |     output.write(struct.pack('<I', 1))  # Format Version | 
 | 2541 |     write_rsa_key(output, Crypto.PublicKey.RSA.importKey(root_authority_key)) | 
 | 2542 |     output.write(product_id) | 
 | 2543 |  | 
 | 2544 |   def make_atx_metadata(self, output, intermediate_key_certificate, | 
 | 2545 |                         product_key_certificate, google_key_version): | 
 | 2546 |     """Implements the 'make_atx_metadata' command. | 
 | 2547 |  | 
 | 2548 |     Android Things metadata are included in vbmeta images to facilitate | 
 | 2549 |     verification. The output of this command can be used as the | 
 | 2550 |     public_key_metadata argument to other commands. | 
 | 2551 |  | 
 | 2552 |     Arguments: | 
 | 2553 |       output: Metadata will be written to this file on success. | 
 | 2554 |       intermediate_key_certificate: A certificate file as output by | 
 | 2555 |                                     make_atx_certificate with | 
 | 2556 |                                     is_intermediate_authority set to true. | 
 | 2557 |       product_key_certificate: A certificate file as output by | 
 | 2558 |                                make_atx_certificate with | 
 | 2559 |                                is_intermediate_authority set to false. | 
 | 2560 |       google_key_version: The version of the Google Signing Key used in the | 
 | 2561 |                           associated vbmeta image. | 
 | 2562 |  | 
 | 2563 |     Raises: | 
 | 2564 |       AvbError: If an argument is incorrect. | 
 | 2565 |     """ | 
 | 2566 |     if len(intermediate_key_certificate) != 1108: | 
 | 2567 |       raise AvbError('Invalid intermediate key certificate length.') | 
 | 2568 |     if len(product_key_certificate) != 852: | 
 | 2569 |       raise AvbError('Invalid product key certificate length.') | 
 | 2570 |     output.write(struct.pack('<I', 1))  # Format Version | 
 | 2571 |     output.write(intermediate_key_certificate) | 
 | 2572 |     output.write(product_key_certificate) | 
 | 2573 |     output.write(struct.pack('<Q', google_key_version)) | 
 | 2574 |  | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2575 |  | 
 | 2576 | def calc_hash_level_offsets(image_size, block_size, digest_size): | 
 | 2577 |   """Calculate the offsets of all the hash-levels in a Merkle-tree. | 
 | 2578 |  | 
 | 2579 |   Arguments: | 
 | 2580 |     image_size: The size of the image to calculate a Merkle-tree for. | 
 | 2581 |     block_size: The block size, e.g. 4096. | 
 | 2582 |     digest_size: The size of each hash, e.g. 32 for SHA-256. | 
 | 2583 |  | 
 | 2584 |   Returns: | 
 | 2585 |     A tuple where the first argument is an array of offsets and the | 
 | 2586 |     second is size of the tree, in bytes. | 
 | 2587 |   """ | 
 | 2588 |   level_offsets = [] | 
 | 2589 |   level_sizes = [] | 
 | 2590 |   tree_size = 0 | 
 | 2591 |  | 
 | 2592 |   num_levels = 0 | 
 | 2593 |   size = image_size | 
 | 2594 |   while size > block_size: | 
 | 2595 |     num_blocks = (size + block_size - 1) / block_size | 
 | 2596 |     level_size = round_to_multiple(num_blocks * digest_size, block_size) | 
 | 2597 |  | 
 | 2598 |     level_sizes.append(level_size) | 
 | 2599 |     tree_size += level_size | 
 | 2600 |     num_levels += 1 | 
 | 2601 |  | 
 | 2602 |     size = level_size | 
 | 2603 |  | 
 | 2604 |   for n in range(0, num_levels): | 
 | 2605 |     offset = 0 | 
 | 2606 |     for m in range(n + 1, num_levels): | 
 | 2607 |       offset += level_sizes[m] | 
 | 2608 |     level_offsets.append(offset) | 
 | 2609 |  | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 2610 |   return level_offsets, tree_size | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2611 |  | 
 | 2612 |  | 
| David Zeuthen | 0b7f1d3 | 2016-10-25 17:53:49 -0400 | [diff] [blame] | 2613 | # See system/extras/libfec/include/fec/io.h for these definitions. | 
 | 2614 | FEC_FOOTER_FORMAT = '<LLLLLQ32s' | 
 | 2615 | FEC_MAGIC = 0xfecfecfe | 
 | 2616 |  | 
 | 2617 |  | 
 | 2618 | def calc_fec_data_size(image_size, num_roots): | 
 | 2619 |   """Calculates how much space FEC data will take. | 
 | 2620 |  | 
 | 2621 |   Args: | 
 | 2622 |     image_size: The size of the image. | 
 | 2623 |     num_roots: Number of roots. | 
 | 2624 |  | 
 | 2625 |   Returns: | 
 | 2626 |     The number of bytes needed for FEC for an image of the given size | 
 | 2627 |     and with the requested number of FEC roots. | 
 | 2628 |  | 
 | 2629 |   Raises: | 
 | 2630 |     ValueError: If output from the 'fec' tool is invalid. | 
 | 2631 |  | 
 | 2632 |   """ | 
 | 2633 |   p = subprocess.Popen( | 
 | 2634 |       ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)], | 
 | 2635 |       stdout=subprocess.PIPE, | 
 | 2636 |       stderr=subprocess.PIPE) | 
 | 2637 |   (pout, perr) = p.communicate() | 
 | 2638 |   retcode = p.wait() | 
 | 2639 |   if retcode != 0: | 
 | 2640 |     raise ValueError('Error invoking fec: {}'.format(perr)) | 
 | 2641 |   return int(pout) | 
 | 2642 |  | 
 | 2643 |  | 
 | 2644 | def generate_fec_data(image_filename, num_roots): | 
 | 2645 |   """Generate FEC codes for an image. | 
 | 2646 |  | 
 | 2647 |   Args: | 
 | 2648 |     image_filename: The filename of the image. | 
 | 2649 |     num_roots: Number of roots. | 
 | 2650 |  | 
 | 2651 |   Returns: | 
 | 2652 |     The FEC data blob. | 
 | 2653 |  | 
 | 2654 |   Raises: | 
 | 2655 |     ValueError: If output from the 'fec' tool is invalid. | 
 | 2656 |   """ | 
 | 2657 |   fec_tmpfile = tempfile.NamedTemporaryFile() | 
 | 2658 |   subprocess.check_call( | 
 | 2659 |       ['fec', '--encode', '--roots', str(num_roots), image_filename, | 
 | 2660 |        fec_tmpfile.name], | 
 | 2661 |       stderr=open(os.devnull)) | 
 | 2662 |   fec_data = fec_tmpfile.read() | 
 | 2663 |   footer_size = struct.calcsize(FEC_FOOTER_FORMAT) | 
 | 2664 |   footer_data = fec_data[-footer_size:] | 
 | 2665 |   (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT, | 
 | 2666 |                                                            footer_data) | 
 | 2667 |   if magic != FEC_MAGIC: | 
 | 2668 |     raise ValueError('Unexpected magic in FEC footer') | 
 | 2669 |   return fec_data[0:fec_size] | 
 | 2670 |  | 
 | 2671 |  | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2672 | def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt, | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 2673 |                        digest_padding, hash_level_offsets, tree_size): | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2674 |   """Generates a Merkle-tree for a file. | 
 | 2675 |  | 
 | 2676 |   Args: | 
 | 2677 |     image: The image, as a file. | 
 | 2678 |     image_size: The size of the image. | 
 | 2679 |     block_size: The block size, e.g. 4096. | 
 | 2680 |     hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'. | 
 | 2681 |     salt: The salt to use. | 
 | 2682 |     digest_padding: The padding for each digest. | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2683 |     hash_level_offsets: The offsets from calc_hash_level_offsets(). | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 2684 |     tree_size: The size of the tree, in number of bytes. | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2685 |  | 
 | 2686 |   Returns: | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 2687 |     A tuple where the first element is the top-level hash and the | 
 | 2688 |     second element is the hash-tree. | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2689 |   """ | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 2690 |   hash_ret = bytearray(tree_size) | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2691 |   hash_src_offset = 0 | 
 | 2692 |   hash_src_size = image_size | 
 | 2693 |   level_num = 0 | 
 | 2694 |   while hash_src_size > block_size: | 
 | 2695 |     level_output = '' | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2696 |     remaining = hash_src_size | 
 | 2697 |     while remaining > 0: | 
 | 2698 |       hasher = hashlib.new(name=hash_alg_name, string=salt) | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 2699 |       # Only read from the file for the first level - for subsequent | 
 | 2700 |       # levels, access the array we're building. | 
 | 2701 |       if level_num == 0: | 
 | 2702 |         image.seek(hash_src_offset + hash_src_size - remaining) | 
 | 2703 |         data = image.read(min(remaining, block_size)) | 
 | 2704 |       else: | 
 | 2705 |         offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining | 
 | 2706 |         data = hash_ret[offset:offset + block_size] | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2707 |       hasher.update(data) | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 2708 |  | 
 | 2709 |       remaining -= len(data) | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2710 |       if len(data) < block_size: | 
 | 2711 |         hasher.update('\0' * (block_size - len(data))) | 
 | 2712 |       level_output += hasher.digest() | 
 | 2713 |       if digest_padding > 0: | 
 | 2714 |         level_output += '\0' * digest_padding | 
 | 2715 |  | 
 | 2716 |     padding_needed = (round_to_multiple( | 
 | 2717 |         len(level_output), block_size) - len(level_output)) | 
 | 2718 |     level_output += '\0' * padding_needed | 
 | 2719 |  | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 2720 |     # Copy level-output into resulting tree. | 
 | 2721 |     offset = hash_level_offsets[level_num] | 
 | 2722 |     hash_ret[offset:offset + len(level_output)] = level_output | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2723 |  | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 2724 |     # Continue on to the next level. | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2725 |     hash_src_size = len(level_output) | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2726 |     level_num += 1 | 
 | 2727 |  | 
 | 2728 |   hasher = hashlib.new(name=hash_alg_name, string=salt) | 
 | 2729 |   hasher.update(level_output) | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 2730 |   return hasher.digest(), hash_ret | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2731 |  | 
 | 2732 |  | 
 | 2733 | class AvbTool(object): | 
 | 2734 |   """Object for avbtool command-line tool.""" | 
 | 2735 |  | 
 | 2736 |   def __init__(self): | 
 | 2737 |     """Initializer method.""" | 
 | 2738 |     self.avb = Avb() | 
 | 2739 |  | 
 | 2740 |   def _add_common_args(self, sub_parser): | 
 | 2741 |     """Adds arguments used by several sub-commands. | 
 | 2742 |  | 
 | 2743 |     Arguments: | 
 | 2744 |       sub_parser: The parser to add arguments to. | 
 | 2745 |     """ | 
 | 2746 |     sub_parser.add_argument('--algorithm', | 
 | 2747 |                             help='Algorithm to use (default: NONE)', | 
 | 2748 |                             metavar='ALGORITHM', | 
 | 2749 |                             default='NONE') | 
 | 2750 |     sub_parser.add_argument('--key', | 
 | 2751 |                             help='Path to RSA private key file', | 
 | 2752 |                             metavar='KEY', | 
 | 2753 |                             required=False) | 
| Dzmitry Yatsushkevich | 4e55279 | 2016-12-15 10:27:48 -0800 | [diff] [blame] | 2754 |     sub_parser.add_argument('--signing_helper', | 
 | 2755 |                             help='Path to helper used for signing', | 
 | 2756 |                             metavar='APP', | 
 | 2757 |                             default=None, | 
 | 2758 |                             required=False) | 
| David Zeuthen | 18666ab | 2016-11-15 11:18:05 -0500 | [diff] [blame] | 2759 |     sub_parser.add_argument('--public_key_metadata', | 
 | 2760 |                             help='Path to public key metadata file', | 
 | 2761 |                             metavar='KEY_METADATA', | 
 | 2762 |                             required=False) | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2763 |     sub_parser.add_argument('--rollback_index', | 
 | 2764 |                             help='Rollback Index', | 
 | 2765 |                             type=parse_number, | 
 | 2766 |                             default=0) | 
 | 2767 |     sub_parser.add_argument('--prop', | 
 | 2768 |                             help='Add property', | 
 | 2769 |                             metavar='KEY:VALUE', | 
 | 2770 |                             action='append') | 
 | 2771 |     sub_parser.add_argument('--prop_from_file', | 
 | 2772 |                             help='Add property from file', | 
 | 2773 |                             metavar='KEY:PATH', | 
 | 2774 |                             action='append') | 
 | 2775 |     sub_parser.add_argument('--kernel_cmdline', | 
 | 2776 |                             help='Add kernel cmdline', | 
 | 2777 |                             metavar='CMDLINE', | 
 | 2778 |                             action='append') | 
| David Zeuthen | 5d4f4f2 | 2017-01-11 11:37:34 -0500 | [diff] [blame] | 2779 |     # TODO(zeuthen): the --setup_rootfs_from_kernel option used to be called | 
 | 2780 |     # --generate_dm_verity_cmdline_from_hashtree. Remove support for the latter | 
 | 2781 |     # at some future point. | 
 | 2782 |     sub_parser.add_argument('--setup_rootfs_from_kernel', | 
 | 2783 |                             '--generate_dm_verity_cmdline_from_hashtree', | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2784 |                             metavar='IMAGE', | 
| David Zeuthen | 5d4f4f2 | 2017-01-11 11:37:34 -0500 | [diff] [blame] | 2785 |                             help='Adds kernel cmdline to set up IMAGE', | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2786 |                             type=argparse.FileType('rb')) | 
 | 2787 |     sub_parser.add_argument('--include_descriptors_from_image', | 
 | 2788 |                             help='Include descriptors from image', | 
 | 2789 |                             metavar='IMAGE', | 
 | 2790 |                             action='append', | 
 | 2791 |                             type=argparse.FileType('rb')) | 
 | 2792 |  | 
 | 2793 |   def run(self, argv): | 
 | 2794 |     """Command-line processor. | 
 | 2795 |  | 
 | 2796 |     Arguments: | 
 | 2797 |       argv: Pass sys.argv from main. | 
 | 2798 |     """ | 
 | 2799 |     parser = argparse.ArgumentParser() | 
 | 2800 |     subparsers = parser.add_subparsers(title='subcommands') | 
 | 2801 |  | 
 | 2802 |     sub_parser = subparsers.add_parser('version', | 
 | 2803 |                                        help='Prints version of avbtool.') | 
 | 2804 |     sub_parser.set_defaults(func=self.version) | 
 | 2805 |  | 
 | 2806 |     sub_parser = subparsers.add_parser('extract_public_key', | 
 | 2807 |                                        help='Extract public key.') | 
 | 2808 |     sub_parser.add_argument('--key', | 
 | 2809 |                             help='Path to RSA private key file', | 
 | 2810 |                             required=True) | 
 | 2811 |     sub_parser.add_argument('--output', | 
 | 2812 |                             help='Output file name', | 
 | 2813 |                             type=argparse.FileType('wb'), | 
 | 2814 |                             required=True) | 
 | 2815 |     sub_parser.set_defaults(func=self.extract_public_key) | 
 | 2816 |  | 
 | 2817 |     sub_parser = subparsers.add_parser('make_vbmeta_image', | 
 | 2818 |                                        help='Makes a vbmeta image.') | 
 | 2819 |     sub_parser.add_argument('--output', | 
 | 2820 |                             help='Output file name', | 
 | 2821 |                             type=argparse.FileType('wb'), | 
 | 2822 |                             required=True) | 
 | 2823 |     self._add_common_args(sub_parser) | 
 | 2824 |     sub_parser.add_argument('--chain_partition', | 
 | 2825 |                             help='Allow signed integrity-data for partition', | 
 | 2826 |                             metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH', | 
 | 2827 |                             action='append') | 
| David Zeuthen | fd41eb9 | 2016-11-17 12:24:47 -0500 | [diff] [blame] | 2828 |     sub_parser.add_argument('--flags', | 
 | 2829 |                             help='VBMeta flags', | 
 | 2830 |                             type=parse_number, | 
 | 2831 |                             default=0) | 
| David Zeuthen | 5830552 | 2017-01-11 17:42:47 -0500 | [diff] [blame] | 2832 |     sub_parser.add_argument('--set_hashtree_disabled_flag', | 
 | 2833 |                             help='Set the HASHTREE_DISABLED flag', | 
 | 2834 |                             action='store_true') | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2835 |     sub_parser.set_defaults(func=self.make_vbmeta_image) | 
 | 2836 |  | 
 | 2837 |     sub_parser = subparsers.add_parser('add_hash_footer', | 
 | 2838 |                                        help='Add hashes and footer to image.') | 
 | 2839 |     sub_parser.add_argument('--image', | 
| David Zeuthen | 8b6973b | 2016-09-20 12:39:49 -0400 | [diff] [blame] | 2840 |                             help='Image to add hashes to', | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2841 |                             type=argparse.FileType('rab+')) | 
 | 2842 |     sub_parser.add_argument('--partition_size', | 
 | 2843 |                             help='Partition size', | 
 | 2844 |                             type=parse_number, | 
 | 2845 |                             required=True) | 
 | 2846 |     sub_parser.add_argument('--partition_name', | 
 | 2847 |                             help='Partition name', | 
 | 2848 |                             required=True) | 
 | 2849 |     sub_parser.add_argument('--hash_algorithm', | 
 | 2850 |                             help='Hash algorithm to use (default: sha256)', | 
 | 2851 |                             default='sha256') | 
 | 2852 |     sub_parser.add_argument('--salt', | 
 | 2853 |                             help='Salt in hex (default: /dev/urandom)') | 
| David Zeuthen | d247fcb | 2017-02-16 12:09:27 -0500 | [diff] [blame^] | 2854 |     sub_parser.add_argument('--output_vbmeta_image', | 
 | 2855 |                             help='Also write vbmeta struct to file', | 
 | 2856 |                             type=argparse.FileType('wb')) | 
 | 2857 |     sub_parser.add_argument('--do_not_append_vbmeta_image', | 
 | 2858 |                             help=('Do not append vbmeta struct or footer ' | 
 | 2859 |                                   'to the image'), | 
 | 2860 |                             action='store_true') | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2861 |     self._add_common_args(sub_parser) | 
 | 2862 |     sub_parser.set_defaults(func=self.add_hash_footer) | 
 | 2863 |  | 
 | 2864 |     sub_parser = subparsers.add_parser('add_hashtree_footer', | 
 | 2865 |                                        help='Add hashtree and footer to image.') | 
 | 2866 |     sub_parser.add_argument('--image', | 
| David Zeuthen | 8b6973b | 2016-09-20 12:39:49 -0400 | [diff] [blame] | 2867 |                             help='Image to add hashtree to', | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2868 |                             type=argparse.FileType('rab+')) | 
 | 2869 |     sub_parser.add_argument('--partition_size', | 
 | 2870 |                             help='Partition size', | 
 | 2871 |                             type=parse_number, | 
 | 2872 |                             required=True) | 
 | 2873 |     sub_parser.add_argument('--partition_name', | 
 | 2874 |                             help='Partition name', | 
| David Zeuthen | 0969269 | 2016-09-30 16:16:40 -0400 | [diff] [blame] | 2875 |                             default=None) | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2876 |     sub_parser.add_argument('--hash_algorithm', | 
 | 2877 |                             help='Hash algorithm to use (default: sha1)', | 
 | 2878 |                             default='sha1') | 
 | 2879 |     sub_parser.add_argument('--salt', | 
 | 2880 |                             help='Salt in hex (default: /dev/urandom)') | 
 | 2881 |     sub_parser.add_argument('--block_size', | 
 | 2882 |                             help='Block size (default: 4096)', | 
 | 2883 |                             type=parse_number, | 
 | 2884 |                             default=4096) | 
| David Zeuthen | 0b7f1d3 | 2016-10-25 17:53:49 -0400 | [diff] [blame] | 2885 |     sub_parser.add_argument('--generate_fec', | 
 | 2886 |                             help='Add forward-error-correction codes', | 
 | 2887 |                             action='store_true') | 
 | 2888 |     sub_parser.add_argument('--fec_num_roots', | 
 | 2889 |                             help='Number of roots for FEC (default: 2)', | 
 | 2890 |                             type=parse_number, | 
 | 2891 |                             default=2) | 
| David Zeuthen | 0969269 | 2016-09-30 16:16:40 -0400 | [diff] [blame] | 2892 |     sub_parser.add_argument('--calc_max_image_size', | 
 | 2893 |                             help=('Don\'t store the hashtree or footer - ' | 
 | 2894 |                                   'instead calculate the maximum image size ' | 
 | 2895 |                                   'leaving enough room for hashtree ' | 
 | 2896 |                                   'and metadata with the given partition ' | 
 | 2897 |                                   'size.'), | 
 | 2898 |                             action='store_true') | 
| David Zeuthen | d247fcb | 2017-02-16 12:09:27 -0500 | [diff] [blame^] | 2899 |     sub_parser.add_argument('--output_vbmeta_image', | 
 | 2900 |                             help='Also write vbmeta struct to file', | 
 | 2901 |                             type=argparse.FileType('wb')) | 
 | 2902 |     sub_parser.add_argument('--do_not_append_vbmeta_image', | 
 | 2903 |                             help=('Do not append vbmeta struct or footer ' | 
 | 2904 |                                   'to the image'), | 
 | 2905 |                             action='store_true') | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2906 |     self._add_common_args(sub_parser) | 
 | 2907 |     sub_parser.set_defaults(func=self.add_hashtree_footer) | 
 | 2908 |  | 
 | 2909 |     sub_parser = subparsers.add_parser('erase_footer', | 
 | 2910 |                                        help='Erase footer from an image.') | 
 | 2911 |     sub_parser.add_argument('--image', | 
| David Zeuthen | 8b6973b | 2016-09-20 12:39:49 -0400 | [diff] [blame] | 2912 |                             help='Image with a footer', | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2913 |                             type=argparse.FileType('rwb+'), | 
 | 2914 |                             required=True) | 
 | 2915 |     sub_parser.add_argument('--keep_hashtree', | 
| David Zeuthen | fbb61fa | 2017-02-02 12:11:49 -0500 | [diff] [blame] | 2916 |                             help='Keep the hashtree and FEC in the image', | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2917 |                             action='store_true') | 
 | 2918 |     sub_parser.set_defaults(func=self.erase_footer) | 
 | 2919 |  | 
 | 2920 |     sub_parser = subparsers.add_parser( | 
 | 2921 |         'info_image', | 
 | 2922 |         help='Show information about vbmeta or footer.') | 
 | 2923 |     sub_parser.add_argument('--image', | 
| David Zeuthen | 8b6973b | 2016-09-20 12:39:49 -0400 | [diff] [blame] | 2924 |                             help='Image to show information about', | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 2925 |                             type=argparse.FileType('rb'), | 
 | 2926 |                             required=True) | 
 | 2927 |     sub_parser.add_argument('--output', | 
 | 2928 |                             help='Write info to file', | 
 | 2929 |                             type=argparse.FileType('wt'), | 
 | 2930 |                             default=sys.stdout) | 
 | 2931 |     sub_parser.set_defaults(func=self.info_image) | 
 | 2932 |  | 
| David Zeuthen | 8b6973b | 2016-09-20 12:39:49 -0400 | [diff] [blame] | 2933 |     sub_parser = subparsers.add_parser('set_ab_metadata', | 
 | 2934 |                                        help='Set A/B metadata.') | 
 | 2935 |     sub_parser.add_argument('--misc_image', | 
 | 2936 |                             help=('The misc image to modify. If the image does ' | 
 | 2937 |                                   'not exist, it will be created.'), | 
 | 2938 |                             type=argparse.FileType('r+b'), | 
 | 2939 |                             required=True) | 
 | 2940 |     sub_parser.add_argument('--slot_data', | 
 | 2941 |                             help=('Slot data of the form "priority", ' | 
 | 2942 |                                   '"tries_remaining", "sucessful_boot" for ' | 
 | 2943 |                                   'slot A followed by the same for slot B, ' | 
 | 2944 |                                   'separated by colons. The default value ' | 
 | 2945 |                                   'is 15:7:0:14:7:0.'), | 
 | 2946 |                             default='15:7:0:14:7:0') | 
 | 2947 |     sub_parser.set_defaults(func=self.set_ab_metadata) | 
 | 2948 |  | 
| Darren Krahn | 147b08d | 2016-12-20 16:38:29 -0800 | [diff] [blame] | 2949 |     sub_parser = subparsers.add_parser( | 
 | 2950 |         'make_atx_certificate', | 
 | 2951 |         help='Create an Android Things eXtension (ATX) certificate.') | 
 | 2952 |     sub_parser.add_argument('--output', | 
 | 2953 |                             help='Write certificate to file', | 
 | 2954 |                             type=argparse.FileType('wb'), | 
 | 2955 |                             default=sys.stdout) | 
 | 2956 |     sub_parser.add_argument('--subject', | 
 | 2957 |                             help=('Path to subject file'), | 
 | 2958 |                             type=argparse.FileType('rb'), | 
 | 2959 |                             required=True) | 
 | 2960 |     sub_parser.add_argument('--subject_key', | 
 | 2961 |                             help=('Path to subject RSA public key file'), | 
 | 2962 |                             type=argparse.FileType('rb'), | 
 | 2963 |                             required=True) | 
 | 2964 |     sub_parser.add_argument('--subject_key_version', | 
 | 2965 |                             help=('Version of the subject key'), | 
 | 2966 |                             type=parse_number, | 
 | 2967 |                             required=False) | 
 | 2968 |     sub_parser.add_argument('--subject_is_intermediate_authority', | 
 | 2969 |                             help=('Generate an intermediate authority ' | 
 | 2970 |                                   'certificate'), | 
 | 2971 |                             action='store_true') | 
 | 2972 |     sub_parser.add_argument('--authority_key', | 
 | 2973 |                             help='Path to authority RSA private key file', | 
 | 2974 |                             required=False) | 
 | 2975 |     sub_parser.add_argument('--signing_helper', | 
 | 2976 |                             help='Path to helper used for signing', | 
 | 2977 |                             metavar='APP', | 
 | 2978 |                             default=None, | 
 | 2979 |                             required=False) | 
 | 2980 |     sub_parser.set_defaults(func=self.make_atx_certificate) | 
 | 2981 |  | 
 | 2982 |     sub_parser = subparsers.add_parser( | 
 | 2983 |         'make_atx_permanent_attributes', | 
 | 2984 |         help='Create Android Things eXtension (ATX) permanent attributes.') | 
 | 2985 |     sub_parser.add_argument('--output', | 
 | 2986 |                             help='Write attributes to file', | 
 | 2987 |                             type=argparse.FileType('wb'), | 
 | 2988 |                             default=sys.stdout) | 
 | 2989 |     sub_parser.add_argument('--root_authority_key', | 
 | 2990 |                             help='Path to authority RSA public key file', | 
 | 2991 |                             type=argparse.FileType('rb'), | 
 | 2992 |                             required=True) | 
 | 2993 |     sub_parser.add_argument('--product_id', | 
 | 2994 |                             help=('Path to Product ID file'), | 
 | 2995 |                             type=argparse.FileType('rb'), | 
 | 2996 |                             required=True) | 
 | 2997 |     sub_parser.set_defaults(func=self.make_atx_permanent_attributes) | 
 | 2998 |  | 
 | 2999 |     sub_parser = subparsers.add_parser( | 
 | 3000 |         'make_atx_metadata', | 
 | 3001 |         help='Create Android Things eXtension (ATX) metadata.') | 
 | 3002 |     sub_parser.add_argument('--output', | 
 | 3003 |                             help='Write metadata to file', | 
 | 3004 |                             type=argparse.FileType('wb'), | 
 | 3005 |                             default=sys.stdout) | 
 | 3006 |     sub_parser.add_argument('--intermediate_key_certificate', | 
 | 3007 |                             help='Path to intermediate key certificate file', | 
 | 3008 |                             type=argparse.FileType('rb'), | 
 | 3009 |                             required=True) | 
 | 3010 |     sub_parser.add_argument('--product_key_certificate', | 
 | 3011 |                             help='Path to product key certificate file', | 
 | 3012 |                             type=argparse.FileType('rb'), | 
 | 3013 |                             required=True) | 
 | 3014 |     sub_parser.add_argument('--google_key_version', | 
 | 3015 |                             help=('Version of the Google signing key'), | 
 | 3016 |                             type=parse_number, | 
 | 3017 |                             default=0) | 
 | 3018 |     sub_parser.set_defaults(func=self.make_atx_metadata) | 
 | 3019 |  | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 3020 |     args = parser.parse_args(argv[1:]) | 
 | 3021 |     try: | 
 | 3022 |       args.func(args) | 
 | 3023 |     except AvbError as e: | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 3024 |       sys.stderr.write('{}: {}\n'.format(argv[0], e.message)) | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 3025 |       sys.exit(1) | 
 | 3026 |  | 
 | 3027 |   def version(self, _): | 
 | 3028 |     """Implements the 'version' sub-command.""" | 
 | 3029 |     print '{}.{}'.format(AVB_VERSION_MAJOR, AVB_VERSION_MINOR) | 
 | 3030 |  | 
 | 3031 |   def extract_public_key(self, args): | 
 | 3032 |     """Implements the 'extract_public_key' sub-command.""" | 
 | 3033 |     self.avb.extract_public_key(args.key, args.output) | 
 | 3034 |  | 
 | 3035 |   def make_vbmeta_image(self, args): | 
 | 3036 |     """Implements the 'make_vbmeta_image' sub-command.""" | 
| David Zeuthen | 5830552 | 2017-01-11 17:42:47 -0500 | [diff] [blame] | 3037 |     if args.set_hashtree_disabled_flag: | 
 | 3038 |       args.flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 3039 |     self.avb.make_vbmeta_image(args.output, args.chain_partition, | 
| David Zeuthen | 18666ab | 2016-11-15 11:18:05 -0500 | [diff] [blame] | 3040 |                                args.algorithm, args.key, | 
 | 3041 |                                args.public_key_metadata, args.rollback_index, | 
| David Zeuthen | fd41eb9 | 2016-11-17 12:24:47 -0500 | [diff] [blame] | 3042 |                                args.flags, args.prop, args.prop_from_file, | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 3043 |                                args.kernel_cmdline, | 
| David Zeuthen | 5d4f4f2 | 2017-01-11 11:37:34 -0500 | [diff] [blame] | 3044 |                                args.setup_rootfs_from_kernel, | 
| David Zeuthen | d247fcb | 2017-02-16 12:09:27 -0500 | [diff] [blame^] | 3045 |                                args.include_descriptors_from_image, | 
 | 3046 |                                args.signing_helper) | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 3047 |  | 
 | 3048 |   def add_hash_footer(self, args): | 
 | 3049 |     """Implements the 'add_hash_footer' sub-command.""" | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 3050 |     self.avb.add_hash_footer(args.image.name, args.partition_size, | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 3051 |                              args.partition_name, args.hash_algorithm, | 
 | 3052 |                              args.salt, args.algorithm, args.key, | 
| David Zeuthen | 18666ab | 2016-11-15 11:18:05 -0500 | [diff] [blame] | 3053 |                              args.public_key_metadata, args.rollback_index, | 
 | 3054 |                              args.prop, args.prop_from_file, | 
 | 3055 |                              args.kernel_cmdline, | 
| David Zeuthen | 5d4f4f2 | 2017-01-11 11:37:34 -0500 | [diff] [blame] | 3056 |                              args.setup_rootfs_from_kernel, | 
| David Zeuthen | d247fcb | 2017-02-16 12:09:27 -0500 | [diff] [blame^] | 3057 |                              args.include_descriptors_from_image, | 
 | 3058 |                              args.signing_helper, | 
 | 3059 |                              args.output_vbmeta_image, | 
 | 3060 |                              args.do_not_append_vbmeta_image) | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 3061 |  | 
 | 3062 |   def add_hashtree_footer(self, args): | 
 | 3063 |     """Implements the 'add_hashtree_footer' sub-command.""" | 
| David Zeuthen | 0969269 | 2016-09-30 16:16:40 -0400 | [diff] [blame] | 3064 |     self.avb.add_hashtree_footer(args.image.name if args.image else None, | 
 | 3065 |                                  args.partition_size, | 
 | 3066 |                                  args.partition_name, | 
| David Zeuthen | 0b7f1d3 | 2016-10-25 17:53:49 -0400 | [diff] [blame] | 3067 |                                  args.generate_fec, args.fec_num_roots, | 
| David Zeuthen | 0969269 | 2016-09-30 16:16:40 -0400 | [diff] [blame] | 3068 |                                  args.hash_algorithm, args.block_size, | 
 | 3069 |                                  args.salt, args.algorithm, args.key, | 
| David Zeuthen | 18666ab | 2016-11-15 11:18:05 -0500 | [diff] [blame] | 3070 |                                  args.public_key_metadata, | 
| David Zeuthen | 0969269 | 2016-09-30 16:16:40 -0400 | [diff] [blame] | 3071 |                                  args.rollback_index, args.prop, | 
 | 3072 |                                  args.prop_from_file, | 
 | 3073 |                                  args.kernel_cmdline, | 
| David Zeuthen | 5d4f4f2 | 2017-01-11 11:37:34 -0500 | [diff] [blame] | 3074 |                                  args.setup_rootfs_from_kernel, | 
| David Zeuthen | 0969269 | 2016-09-30 16:16:40 -0400 | [diff] [blame] | 3075 |                                  args.include_descriptors_from_image, | 
| David Zeuthen | d247fcb | 2017-02-16 12:09:27 -0500 | [diff] [blame^] | 3076 |                                  args.calc_max_image_size, args.signing_helper, | 
 | 3077 |                                  args.output_vbmeta_image, | 
 | 3078 |                                  args.do_not_append_vbmeta_image) | 
 | 3079 |  | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 3080 |  | 
 | 3081 |   def erase_footer(self, args): | 
 | 3082 |     """Implements the 'erase_footer' sub-command.""" | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 3083 |     self.avb.erase_footer(args.image.name, args.keep_hashtree) | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 3084 |  | 
| David Zeuthen | 8b6973b | 2016-09-20 12:39:49 -0400 | [diff] [blame] | 3085 |   def set_ab_metadata(self, args): | 
 | 3086 |     """Implements the 'set_ab_metadata' sub-command.""" | 
 | 3087 |     self.avb.set_ab_metadata(args.misc_image, args.slot_data) | 
 | 3088 |  | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 3089 |   def info_image(self, args): | 
 | 3090 |     """Implements the 'info_image' sub-command.""" | 
| David Zeuthen | a4fee8b | 2016-08-22 15:20:43 -0400 | [diff] [blame] | 3091 |     self.avb.info_image(args.image.name, args.output) | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 3092 |  | 
| Darren Krahn | 147b08d | 2016-12-20 16:38:29 -0800 | [diff] [blame] | 3093 |   def make_atx_certificate(self, args): | 
 | 3094 |     """Implements the 'make_atx_certificate' sub-command.""" | 
 | 3095 |     self.avb.make_atx_certificate(args.output, args.authority_key, | 
 | 3096 |                                   args.subject_key.read(), | 
 | 3097 |                                   args.subject_key_version, | 
 | 3098 |                                   args.subject.read(), | 
 | 3099 |                                   args.subject_is_intermediate_authority, | 
 | 3100 |                                   args.signing_helper) | 
 | 3101 |  | 
 | 3102 |   def make_atx_permanent_attributes(self, args): | 
 | 3103 |     """Implements the 'make_atx_permanent_attributes' sub-command.""" | 
 | 3104 |     self.avb.make_atx_permanent_attributes(args.output, | 
 | 3105 |                                            args.root_authority_key.read(), | 
 | 3106 |                                            args.product_id.read()) | 
 | 3107 |  | 
 | 3108 |   def make_atx_metadata(self, args): | 
 | 3109 |     """Implements the 'make_atx_metadata' sub-command.""" | 
 | 3110 |     self.avb.make_atx_metadata(args.output, | 
 | 3111 |                                args.intermediate_key_certificate.read(), | 
 | 3112 |                                args.product_key_certificate.read(), | 
 | 3113 |                                args.google_key_version) | 
 | 3114 |  | 
| David Zeuthen | 21e9526 | 2016-07-27 17:58:40 -0400 | [diff] [blame] | 3115 |  | 
 | 3116 | if __name__ == '__main__': | 
 | 3117 |   tool = AvbTool() | 
 | 3118 |   tool.run(sys.argv) |