blob: 7c18d46cff542d2dac9592912b236972279c966d [file] [log] [blame]
David Zeuthen21e95262016-07-27 17:58:40 -04001#!/usr/bin/env python
2
3# Copyright 2016, The Android Open Source Project
4#
David Zeuthenc612e2e2016-09-16 16:44:08 -04005# 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 Zeuthen21e95262016-07-27 17:58:40 -040012#
David Zeuthenc612e2e2016-09-16 16:44:08 -040013# The above copyright notice and this permission notice shall be
14# included in all copies or substantial portions of the Software.
David Zeuthen21e95262016-07-27 17:58:40 -040015#
David Zeuthenc612e2e2016-09-16 16:44:08 -040016# 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 Zeuthen21e95262016-07-27 17:58:40 -040024#
David Zeuthen8b6973b2016-09-20 12:39:49 -040025"""Command-line tool for working with Android Verified Boot images."""
David Zeuthen21e95262016-07-27 17:58:40 -040026
27import argparse
David Zeuthen8b6973b2016-09-20 12:39:49 -040028import binascii
David Zeuthena4fee8b2016-08-22 15:20:43 -040029import bisect
David Zeuthen21e95262016-07-27 17:58:40 -040030import hashlib
David Zeuthenc68f0822017-03-31 17:22:35 -040031import math
David Zeuthen21e95262016-07-27 17:58:40 -040032import os
33import struct
34import subprocess
35import sys
David Zeuthen0b7f1d32016-10-25 17:53:49 -040036import tempfile
Darren Krahn147b08d2016-12-20 16:38:29 -080037import time
David Zeuthen21e95262016-07-27 17:58:40 -040038
David Zeuthene3cadca2017-02-22 21:25:46 -050039# Keep in sync with libavb/avb_version.h.
David Zeuthen21e95262016-07-27 17:58:40 -040040AVB_VERSION_MAJOR = 1
41AVB_VERSION_MINOR = 0
David Zeuthene3cadca2017-02-22 21:25:46 -050042AVB_VERSION_SUB = 0
43
David Zeuthen58305522017-01-11 17:42:47 -050044AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED = 1
David Zeuthen21e95262016-07-27 17:58:40 -040045
David Zeuthene3cadca2017-02-22 21:25:46 -050046
David Zeuthen21e95262016-07-27 17:58:40 -040047class AvbError(Exception):
48 """Application-specific errors.
49
50 These errors represent issues for which a stack-trace should not be
51 presented.
52
53 Attributes:
54 message: Error message.
55 """
56
57 def __init__(self, message):
58 Exception.__init__(self, message)
59
60
61class Algorithm(object):
62 """Contains details about an algorithm.
63
64 See the avb_vbmeta_header.h file for more details about
65 algorithms.
66
67 The constant |ALGORITHMS| is a dictionary from human-readable
68 names (e.g 'SHA256_RSA2048') to instances of this class.
69
70 Attributes:
71 algorithm_type: Integer code corresponding to |AvbAlgorithmType|.
David Zeuthenb623d8b2017-04-04 16:05:53 -040072 hash_name: Empty or a name from |hashlib.algorithms|.
David Zeuthen21e95262016-07-27 17:58:40 -040073 hash_num_bytes: Number of bytes used to store the hash.
74 signature_num_bytes: Number of bytes used to store the signature.
75 public_key_num_bytes: Number of bytes used to store the public key.
76 padding: Padding used for signature, if any.
77 """
78
David Zeuthenb623d8b2017-04-04 16:05:53 -040079 def __init__(self, algorithm_type, hash_name, hash_num_bytes,
80 signature_num_bytes, public_key_num_bytes, padding):
David Zeuthen21e95262016-07-27 17:58:40 -040081 self.algorithm_type = algorithm_type
David Zeuthenb623d8b2017-04-04 16:05:53 -040082 self.hash_name = hash_name
David Zeuthen21e95262016-07-27 17:58:40 -040083 self.hash_num_bytes = hash_num_bytes
84 self.signature_num_bytes = signature_num_bytes
85 self.public_key_num_bytes = public_key_num_bytes
86 self.padding = padding
87
David Zeuthenb623d8b2017-04-04 16:05:53 -040088
David Zeuthen21e95262016-07-27 17:58:40 -040089# This must be kept in sync with the avb_crypto.h file.
90#
91# The PKC1-v1.5 padding is a blob of binary DER of ASN.1 and is
92# obtained from section 5.2.2 of RFC 4880.
93ALGORITHMS = {
94 'NONE': Algorithm(
95 algorithm_type=0, # AVB_ALGORITHM_TYPE_NONE
David Zeuthenb623d8b2017-04-04 16:05:53 -040096 hash_name='',
David Zeuthen21e95262016-07-27 17:58:40 -040097 hash_num_bytes=0,
98 signature_num_bytes=0,
99 public_key_num_bytes=0,
100 padding=[]),
101 'SHA256_RSA2048': Algorithm(
102 algorithm_type=1, # AVB_ALGORITHM_TYPE_SHA256_RSA2048
David Zeuthenb623d8b2017-04-04 16:05:53 -0400103 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400104 hash_num_bytes=32,
105 signature_num_bytes=256,
106 public_key_num_bytes=8 + 2*2048/8,
107 padding=[
108 # PKCS1-v1_5 padding
109 0x00, 0x01] + [0xff]*202 + [0x00] + [
110 # ASN.1 header
111 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
112 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
113 0x00, 0x04, 0x20,
114 ]),
115 'SHA256_RSA4096': Algorithm(
116 algorithm_type=2, # AVB_ALGORITHM_TYPE_SHA256_RSA4096
David Zeuthenb623d8b2017-04-04 16:05:53 -0400117 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400118 hash_num_bytes=32,
119 signature_num_bytes=512,
120 public_key_num_bytes=8 + 2*4096/8,
121 padding=[
122 # PKCS1-v1_5 padding
123 0x00, 0x01] + [0xff]*458 + [0x00] + [
124 # ASN.1 header
125 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
126 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
127 0x00, 0x04, 0x20,
128 ]),
129 'SHA256_RSA8192': Algorithm(
130 algorithm_type=3, # AVB_ALGORITHM_TYPE_SHA256_RSA8192
David Zeuthenb623d8b2017-04-04 16:05:53 -0400131 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400132 hash_num_bytes=32,
133 signature_num_bytes=1024,
134 public_key_num_bytes=8 + 2*8192/8,
135 padding=[
136 # PKCS1-v1_5 padding
137 0x00, 0x01] + [0xff]*970 + [0x00] + [
138 # ASN.1 header
139 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
140 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
141 0x00, 0x04, 0x20,
142 ]),
143 'SHA512_RSA2048': Algorithm(
144 algorithm_type=4, # AVB_ALGORITHM_TYPE_SHA512_RSA2048
David Zeuthenb623d8b2017-04-04 16:05:53 -0400145 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400146 hash_num_bytes=64,
147 signature_num_bytes=256,
148 public_key_num_bytes=8 + 2*2048/8,
149 padding=[
150 # PKCS1-v1_5 padding
151 0x00, 0x01] + [0xff]*170 + [0x00] + [
152 # ASN.1 header
153 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
154 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
155 0x00, 0x04, 0x40
156 ]),
157 'SHA512_RSA4096': Algorithm(
158 algorithm_type=5, # AVB_ALGORITHM_TYPE_SHA512_RSA4096
David Zeuthenb623d8b2017-04-04 16:05:53 -0400159 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400160 hash_num_bytes=64,
161 signature_num_bytes=512,
162 public_key_num_bytes=8 + 2*4096/8,
163 padding=[
164 # PKCS1-v1_5 padding
165 0x00, 0x01] + [0xff]*426 + [0x00] + [
166 # ASN.1 header
167 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
168 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
169 0x00, 0x04, 0x40
170 ]),
171 'SHA512_RSA8192': Algorithm(
172 algorithm_type=6, # AVB_ALGORITHM_TYPE_SHA512_RSA8192
David Zeuthenb623d8b2017-04-04 16:05:53 -0400173 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400174 hash_num_bytes=64,
175 signature_num_bytes=1024,
176 public_key_num_bytes=8 + 2*8192/8,
177 padding=[
178 # PKCS1-v1_5 padding
179 0x00, 0x01] + [0xff]*938 + [0x00] + [
180 # ASN.1 header
181 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
182 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
183 0x00, 0x04, 0x40
184 ]),
185}
186
187
David Zeuthene3cadca2017-02-22 21:25:46 -0500188def get_release_string():
189 """Calculates the release string to use in the VBMeta struct."""
190 # Keep in sync with libavb/avb_version.c:avb_version_string().
191 return 'avbtool {}.{}.{}'.format(AVB_VERSION_MAJOR,
192 AVB_VERSION_MINOR,
193 AVB_VERSION_SUB)
194
195
David Zeuthen21e95262016-07-27 17:58:40 -0400196def round_to_multiple(number, size):
197 """Rounds a number up to nearest multiple of another number.
198
199 Args:
200 number: The number to round up.
201 size: The multiple to round up to.
202
203 Returns:
204 If |number| is a multiple of |size|, returns |number|, otherwise
205 returns |number| + |size|.
206 """
207 remainder = number % size
208 if remainder == 0:
209 return number
210 return number + size - remainder
211
212
213def round_to_pow2(number):
214 """Rounds a number up to the next power of 2.
215
216 Args:
217 number: The number to round up.
218
219 Returns:
220 If |number| is already a power of 2 then |number| is
221 returned. Otherwise the smallest power of 2 greater than |number|
222 is returned.
223 """
224 return 2**((number - 1).bit_length())
225
226
David Zeuthen21e95262016-07-27 17:58:40 -0400227def encode_long(num_bits, value):
228 """Encodes a long to a bytearray() using a given amount of bits.
229
230 This number is written big-endian, e.g. with the most significant
231 bit first.
232
David Zeuthenb623d8b2017-04-04 16:05:53 -0400233 This is the reverse of decode_long().
234
David Zeuthen21e95262016-07-27 17:58:40 -0400235 Arguments:
236 num_bits: The number of bits to write, e.g. 2048.
237 value: The value to write.
238
239 Returns:
240 A bytearray() with the encoded long.
241 """
242 ret = bytearray()
243 for bit_pos in range(num_bits, 0, -8):
244 octet = (value >> (bit_pos - 8)) & 0xff
245 ret.extend(struct.pack('!B', octet))
246 return ret
247
248
David Zeuthenb623d8b2017-04-04 16:05:53 -0400249def decode_long(blob):
250 """Decodes a long from a bytearray() using a given amount of bits.
251
252 This number is expected to be in big-endian, e.g. with the most
253 significant bit first.
254
255 This is the reverse of encode_long().
256
257 Arguments:
258 value: A bytearray() with the encoded long.
259
260 Returns:
261 The decoded value.
262 """
263 ret = 0
264 for b in bytearray(blob):
265 ret *= 256
266 ret += b
267 return ret
268
269
David Zeuthen21e95262016-07-27 17:58:40 -0400270def egcd(a, b):
271 """Calculate greatest common divisor of two numbers.
272
273 This implementation uses a recursive version of the extended
274 Euclidian algorithm.
275
276 Arguments:
277 a: First number.
278 b: Second number.
279
280 Returns:
281 A tuple (gcd, x, y) that where |gcd| is the greatest common
282 divisor of |a| and |b| and |a|*|x| + |b|*|y| = |gcd|.
283 """
284 if a == 0:
285 return (b, 0, 1)
286 else:
287 g, y, x = egcd(b % a, a)
288 return (g, x - (b // a) * y, y)
289
290
291def modinv(a, m):
292 """Calculate modular multiplicative inverse of |a| modulo |m|.
293
294 This calculates the number |x| such that |a| * |x| == 1 (modulo
295 |m|). This number only exists if |a| and |m| are co-prime - |None|
296 is returned if this isn't true.
297
298 Arguments:
299 a: The number to calculate a modular inverse of.
300 m: The modulo to use.
301
302 Returns:
303 The modular multiplicative inverse of |a| and |m| or |None| if
304 these numbers are not co-prime.
305 """
306 gcd, x, _ = egcd(a, m)
307 if gcd != 1:
308 return None # modular inverse does not exist
309 else:
310 return x % m
311
312
313def parse_number(string):
314 """Parse a string as a number.
315
316 This is just a short-hand for int(string, 0) suitable for use in the
317 |type| parameter of |ArgumentParser|'s add_argument() function. An
318 improvement to just using type=int is that this function supports
319 numbers in other bases, e.g. "0x1234".
320
321 Arguments:
322 string: The string to parse.
323
324 Returns:
325 The parsed integer.
326
327 Raises:
328 ValueError: If the number could not be parsed.
329 """
330 return int(string, 0)
331
332
David Zeuthenc68f0822017-03-31 17:22:35 -0400333class RSAPublicKey(object):
334 """Data structure used for a RSA public key.
David Zeuthen21e95262016-07-27 17:58:40 -0400335
David Zeuthenc68f0822017-03-31 17:22:35 -0400336 Attributes:
337 exponent: The key exponent.
338 modulus: The key modulus.
339 num_bits: The key size.
David Zeuthen21e95262016-07-27 17:58:40 -0400340 """
David Zeuthenc68f0822017-03-31 17:22:35 -0400341
342 MODULUS_PREFIX = 'modulus='
343
344 def __init__(self, key_path):
345 """Loads and parses an RSA key from either a private or public key file.
346
347 Arguments:
348 key_path: The path to a key file.
349 """
350 # We used to have something as simple as this:
351 #
352 # key = Crypto.PublicKey.RSA.importKey(open(key_path).read())
353 # self.exponent = key.e
354 # self.modulus = key.n
355 # self.num_bits = key.size() + 1
356 #
357 # but unfortunately PyCrypto is not available in the builder. So
358 # instead just parse openssl(1) output to get this
359 # information. It's ugly but...
360 args = ['openssl', 'rsa', '-in', key_path, '-modulus', '-noout']
361 p = subprocess.Popen(args,
362 stdin=subprocess.PIPE,
363 stdout=subprocess.PIPE,
364 stderr=subprocess.PIPE)
365 (pout, perr) = p.communicate()
366 if p.wait() != 0:
367 # Could be just a public key is passed, try that.
368 args.append('-pubin')
369 p = subprocess.Popen(args,
370 stdin=subprocess.PIPE,
371 stdout=subprocess.PIPE,
372 stderr=subprocess.PIPE)
373 (pout, perr) = p.communicate()
374 if p.wait() != 0:
375 raise AvbError('Error getting public key: {}'.format(perr))
376
377 if not pout.lower().startswith(self.MODULUS_PREFIX):
378 raise AvbError('Unexpected modulus output')
379
380 modulus_hexstr = pout[len(self.MODULUS_PREFIX):]
381
382 # The exponent is assumed to always be 65537 and the number of
383 # bits can be derived from the modulus by rounding up to the
384 # nearest power of 2.
385 self.modulus = int(modulus_hexstr, 16)
386 self.num_bits = round_to_pow2(int(math.ceil(math.log(self.modulus, 2))))
387 self.exponent = 65537
David Zeuthen21e95262016-07-27 17:58:40 -0400388
389
David Zeuthenc68f0822017-03-31 17:22:35 -0400390def encode_rsa_key(key_path):
David Zeuthen21e95262016-07-27 17:58:40 -0400391 """Encodes a public RSA key in |AvbRSAPublicKeyHeader| format.
392
393 This creates a |AvbRSAPublicKeyHeader| as well as the two large
394 numbers (|key_num_bits| bits long) following it.
395
396 Arguments:
David Zeuthenc68f0822017-03-31 17:22:35 -0400397 key_path: The path to a key file.
David Zeuthen21e95262016-07-27 17:58:40 -0400398
399 Returns:
400 A bytearray() with the |AvbRSAPublicKeyHeader|.
401 """
David Zeuthenc68f0822017-03-31 17:22:35 -0400402 key = RSAPublicKey(key_path)
403 if key.exponent != 65537:
404 raise AvbError('Only RSA keys with exponent 65537 are supported.')
David Zeuthen21e95262016-07-27 17:58:40 -0400405 ret = bytearray()
David Zeuthen21e95262016-07-27 17:58:40 -0400406 # Calculate n0inv = -1/n[0] (mod 2^32)
407 b = 2L**32
David Zeuthenc68f0822017-03-31 17:22:35 -0400408 n0inv = b - modinv(key.modulus, b)
David Zeuthen21e95262016-07-27 17:58:40 -0400409 # Calculate rr = r^2 (mod N), where r = 2^(# of key bits)
David Zeuthenc68f0822017-03-31 17:22:35 -0400410 r = 2L**key.modulus.bit_length()
411 rrmodn = r * r % key.modulus
412 ret.extend(struct.pack('!II', key.num_bits, n0inv))
413 ret.extend(encode_long(key.num_bits, key.modulus))
414 ret.extend(encode_long(key.num_bits, rrmodn))
David Zeuthen21e95262016-07-27 17:58:40 -0400415 return ret
416
417
418def lookup_algorithm_by_type(alg_type):
419 """Looks up algorithm by type.
420
421 Arguments:
422 alg_type: The integer representing the type.
423
424 Returns:
425 A tuple with the algorithm name and an |Algorithm| instance.
426
427 Raises:
428 Exception: If the algorithm cannot be found
429 """
430 for alg_name in ALGORITHMS:
431 alg_data = ALGORITHMS[alg_name]
432 if alg_data.algorithm_type == alg_type:
433 return (alg_name, alg_data)
434 raise AvbError('Unknown algorithm type {}'.format(alg_type))
435
436
Esun Kimff44f232017-03-30 10:34:54 +0900437def raw_sign(signing_helper, algorithm_name, signature_num_bytes, key_path,
438 raw_data_to_sign):
Darren Krahn147b08d2016-12-20 16:38:29 -0800439 """Computes a raw RSA signature using |signing_helper| or openssl.
440
441 Arguments:
442 signing_helper: Program which signs a hash and returns the signature.
443 algorithm_name: The algorithm name as per the ALGORITHMS dict.
Esun Kimff44f232017-03-30 10:34:54 +0900444 signature_num_bytes: Number of bytes used to store the signature.
Darren Krahn147b08d2016-12-20 16:38:29 -0800445 key_path: Path to the private key file. Must be PEM format.
446 raw_data_to_sign: Data to sign (bytearray or str expected).
447
448 Returns:
449 A bytearray containing the signature.
450
451 Raises:
452 Exception: If an error occurs.
453 """
454 p = None
455 if signing_helper is not None:
David Zeuthene3cadca2017-02-22 21:25:46 -0500456 p = subprocess.Popen(
457 [signing_helper, algorithm_name, key_path],
458 stdin=subprocess.PIPE,
459 stdout=subprocess.PIPE,
460 stderr=subprocess.PIPE)
Darren Krahn147b08d2016-12-20 16:38:29 -0800461 else:
David Zeuthene3cadca2017-02-22 21:25:46 -0500462 p = subprocess.Popen(
463 ['openssl', 'rsautl', '-sign', '-inkey', key_path, '-raw'],
464 stdin=subprocess.PIPE,
465 stdout=subprocess.PIPE,
466 stderr=subprocess.PIPE)
Darren Krahn147b08d2016-12-20 16:38:29 -0800467 (pout, perr) = p.communicate(str(raw_data_to_sign))
468 retcode = p.wait()
469 if retcode != 0:
470 raise AvbError('Error signing: {}'.format(perr))
Esun Kimff44f232017-03-30 10:34:54 +0900471 signature = bytearray(pout)
472 if len(signature) != signature_num_bytes:
473 raise AvbError('Error signing: Invalid length of signature')
474 return signature
Darren Krahn147b08d2016-12-20 16:38:29 -0800475
476
David Zeuthenb623d8b2017-04-04 16:05:53 -0400477def verify_vbmeta_signature(vbmeta_header, vbmeta_blob):
478 """Checks that the signature in a vbmeta blob was made by
479 the embedded public key.
480
481 Arguments:
482 vbmeta_header: A AvbVBMetaHeader.
483 vbmeta_blob: The whole vbmeta blob, including the header.
484
485 Returns:
486 True if the signature is valid and corresponds to the embedded
487 public key. Also returns True if the vbmeta blob is not signed.
488 """
489 (_, alg) = lookup_algorithm_by_type(vbmeta_header.algorithm_type)
490 if alg.hash_name == '':
491 return True
492 header_blob = vbmeta_blob[0:256]
493 auth_offset = 256
494 aux_offset = auth_offset + vbmeta_header.authentication_data_block_size
495 aux_size = vbmeta_header.auxiliary_data_block_size
496 aux_blob = vbmeta_blob[aux_offset:aux_offset + aux_size]
497 pubkey_offset = aux_offset + vbmeta_header.public_key_offset
498 pubkey_size = vbmeta_header.public_key_size
499 pubkey_blob = vbmeta_blob[pubkey_offset:pubkey_offset + pubkey_size]
500
501 digest_offset = auth_offset + vbmeta_header.hash_offset
502 digest_size = vbmeta_header.hash_size
503 digest_blob = vbmeta_blob[digest_offset:digest_offset + digest_size]
504
505 sig_offset = auth_offset + vbmeta_header.signature_offset
506 sig_size = vbmeta_header.signature_size
507 sig_blob = vbmeta_blob[sig_offset:sig_offset + sig_size]
508
509 # Now that we've got the stored digest, public key, and signature
510 # all we need to do is to verify. This is the exactly the same
511 # steps as performed in the avb_vbmeta_image_verify() function in
512 # libavb/avb_vbmeta_image.c.
513
514 ha = hashlib.new(alg.hash_name)
515 ha.update(header_blob)
516 ha.update(aux_blob)
517 computed_digest = ha.digest()
518
519 if computed_digest != digest_blob:
520 return False
521
522 padding_and_digest = bytearray(alg.padding)
523 padding_and_digest.extend(computed_digest)
524
525 (num_bits,) = struct.unpack('!I', pubkey_blob[0:4])
526 modulus_blob = pubkey_blob[8:8 + num_bits/8]
527 modulus = decode_long(modulus_blob)
528 exponent = 65537
529
530 # For now, just use Crypto.PublicKey.RSA to verify the signature. This
531 # is OK since 'avbtool verify_image' is not expected to run on the
532 # Android builders (see bug #36809096).
533 import Crypto.PublicKey.RSA
534 key = Crypto.PublicKey.RSA.construct((modulus, long(exponent)))
535 if not key.verify(decode_long(padding_and_digest),
536 (decode_long(sig_blob), None)):
537 return False
538 return True
539
540
David Zeuthena4fee8b2016-08-22 15:20:43 -0400541class ImageChunk(object):
542 """Data structure used for representing chunks in Android sparse files.
543
544 Attributes:
545 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
546 chunk_offset: Offset in the sparse file where this chunk begins.
547 output_offset: Offset in de-sparsified file where output begins.
548 output_size: Number of bytes in output.
549 input_offset: Offset in sparse file for data if TYPE_RAW otherwise None.
550 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
551 """
552
553 FORMAT = '<2H2I'
554 TYPE_RAW = 0xcac1
555 TYPE_FILL = 0xcac2
556 TYPE_DONT_CARE = 0xcac3
557 TYPE_CRC32 = 0xcac4
558
559 def __init__(self, chunk_type, chunk_offset, output_offset, output_size,
560 input_offset, fill_data):
561 """Initializes an ImageChunk object.
562
563 Arguments:
564 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
565 chunk_offset: Offset in the sparse file where this chunk begins.
566 output_offset: Offset in de-sparsified file.
567 output_size: Number of bytes in output.
568 input_offset: Offset in sparse file if TYPE_RAW otherwise None.
569 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
570
571 Raises:
572 ValueError: If data is not well-formed.
573 """
574 self.chunk_type = chunk_type
575 self.chunk_offset = chunk_offset
576 self.output_offset = output_offset
577 self.output_size = output_size
578 self.input_offset = input_offset
579 self.fill_data = fill_data
580 # Check invariants.
581 if self.chunk_type == self.TYPE_RAW:
582 if self.fill_data is not None:
583 raise ValueError('RAW chunk cannot have fill_data set.')
584 if not self.input_offset:
585 raise ValueError('RAW chunk must have input_offset set.')
586 elif self.chunk_type == self.TYPE_FILL:
587 if self.fill_data is None:
588 raise ValueError('FILL chunk must have fill_data set.')
589 if self.input_offset:
590 raise ValueError('FILL chunk cannot have input_offset set.')
591 elif self.chunk_type == self.TYPE_DONT_CARE:
592 if self.fill_data is not None:
593 raise ValueError('DONT_CARE chunk cannot have fill_data set.')
594 if self.input_offset:
595 raise ValueError('DONT_CARE chunk cannot have input_offset set.')
596 else:
597 raise ValueError('Invalid chunk type')
598
599
600class ImageHandler(object):
601 """Abstraction for image I/O with support for Android sparse images.
602
603 This class provides an interface for working with image files that
604 may be using the Android Sparse Image format. When an instance is
605 constructed, we test whether it's an Android sparse file. If so,
606 operations will be on the sparse file by interpreting the sparse
607 format, otherwise they will be directly on the file. Either way the
608 operations do the same.
609
610 For reading, this interface mimics a file object - it has seek(),
611 tell(), and read() methods. For writing, only truncation
612 (truncate()) and appending is supported (append_raw() and
613 append_dont_care()). Additionally, data can only be written in units
614 of the block size.
615
616 Attributes:
617 is_sparse: Whether the file being operated on is sparse.
618 block_size: The block size, typically 4096.
619 image_size: The size of the unsparsified file.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400620 """
621 # See system/core/libsparse/sparse_format.h for details.
622 MAGIC = 0xed26ff3a
623 HEADER_FORMAT = '<I4H4I'
624
625 # These are formats and offset of just the |total_chunks| and
626 # |total_blocks| fields.
627 NUM_CHUNKS_AND_BLOCKS_FORMAT = '<II'
628 NUM_CHUNKS_AND_BLOCKS_OFFSET = 16
629
630 def __init__(self, image_filename):
631 """Initializes an image handler.
632
633 Arguments:
634 image_filename: The name of the file to operate on.
635
636 Raises:
637 ValueError: If data in the file is invalid.
638 """
639 self._image_filename = image_filename
640 self._read_header()
641
642 def _read_header(self):
643 """Initializes internal data structures used for reading file.
644
645 This may be called multiple times and is typically called after
646 modifying the file (e.g. appending, truncation).
647
648 Raises:
649 ValueError: If data in the file is invalid.
650 """
651 self.is_sparse = False
652 self.block_size = 4096
653 self._file_pos = 0
654 self._image = open(self._image_filename, 'r+b')
655 self._image.seek(0, os.SEEK_END)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400656 self.image_size = self._image.tell()
657
658 self._image.seek(0, os.SEEK_SET)
659 header_bin = self._image.read(struct.calcsize(self.HEADER_FORMAT))
660 (magic, major_version, minor_version, file_hdr_sz, chunk_hdr_sz,
661 block_size, self._num_total_blocks, self._num_total_chunks,
662 _) = struct.unpack(self.HEADER_FORMAT, header_bin)
663 if magic != self.MAGIC:
664 # Not a sparse image, our job here is done.
665 return
666 if not (major_version == 1 and minor_version == 0):
667 raise ValueError('Encountered sparse image format version {}.{} but '
668 'only 1.0 is supported'.format(major_version,
669 minor_version))
670 if file_hdr_sz != struct.calcsize(self.HEADER_FORMAT):
671 raise ValueError('Unexpected file_hdr_sz value {}.'.
672 format(file_hdr_sz))
673 if chunk_hdr_sz != struct.calcsize(ImageChunk.FORMAT):
674 raise ValueError('Unexpected chunk_hdr_sz value {}.'.
675 format(chunk_hdr_sz))
676
677 self.block_size = block_size
678
679 # Build an list of chunks by parsing the file.
680 self._chunks = []
681
682 # Find the smallest offset where only "Don't care" chunks
683 # follow. This will be the size of the content in the sparse
684 # image.
685 offset = 0
686 output_offset = 0
David Zeuthena4fee8b2016-08-22 15:20:43 -0400687 for _ in xrange(1, self._num_total_chunks + 1):
688 chunk_offset = self._image.tell()
689
690 header_bin = self._image.read(struct.calcsize(ImageChunk.FORMAT))
691 (chunk_type, _, chunk_sz, total_sz) = struct.unpack(ImageChunk.FORMAT,
692 header_bin)
693 data_sz = total_sz - struct.calcsize(ImageChunk.FORMAT)
694
David Zeuthena4fee8b2016-08-22 15:20:43 -0400695 if chunk_type == ImageChunk.TYPE_RAW:
696 if data_sz != (chunk_sz * self.block_size):
697 raise ValueError('Raw chunk input size ({}) does not match output '
698 'size ({})'.
699 format(data_sz, chunk_sz*self.block_size))
700 self._chunks.append(ImageChunk(ImageChunk.TYPE_RAW,
701 chunk_offset,
702 output_offset,
703 chunk_sz*self.block_size,
704 self._image.tell(),
705 None))
706 self._image.read(data_sz)
707
708 elif chunk_type == ImageChunk.TYPE_FILL:
709 if data_sz != 4:
710 raise ValueError('Fill chunk should have 4 bytes of fill, but this '
711 'has {}'.format(data_sz))
712 fill_data = self._image.read(4)
713 self._chunks.append(ImageChunk(ImageChunk.TYPE_FILL,
714 chunk_offset,
715 output_offset,
716 chunk_sz*self.block_size,
717 None,
718 fill_data))
719 elif chunk_type == ImageChunk.TYPE_DONT_CARE:
720 if data_sz != 0:
721 raise ValueError('Don\'t care chunk input size is non-zero ({})'.
722 format(data_sz))
David Zeuthena4fee8b2016-08-22 15:20:43 -0400723 self._chunks.append(ImageChunk(ImageChunk.TYPE_DONT_CARE,
724 chunk_offset,
725 output_offset,
726 chunk_sz*self.block_size,
727 None,
728 None))
729 elif chunk_type == ImageChunk.TYPE_CRC32:
730 if data_sz != 4:
731 raise ValueError('CRC32 chunk should have 4 bytes of CRC, but '
732 'this has {}'.format(data_sz))
733 self._image.read(4)
734 else:
735 raise ValueError('Unknown chunk type {}'.format(chunk_type))
736
737 offset += chunk_sz
738 output_offset += chunk_sz*self.block_size
739
740 # Record where sparse data end.
741 self._sparse_end = self._image.tell()
742
743 # Now that we've traversed all chunks, sanity check.
744 if self._num_total_blocks != offset:
745 raise ValueError('The header said we should have {} output blocks, '
746 'but we saw {}'.format(self._num_total_blocks, offset))
747 junk_len = len(self._image.read())
748 if junk_len > 0:
749 raise ValueError('There were {} bytes of extra data at the end of the '
750 'file.'.format(junk_len))
751
David Zeuthen09692692016-09-30 16:16:40 -0400752 # Assign |image_size|.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400753 self.image_size = output_offset
David Zeuthena4fee8b2016-08-22 15:20:43 -0400754
755 # This is used when bisecting in read() to find the initial slice.
756 self._chunk_output_offsets = [i.output_offset for i in self._chunks]
757
758 self.is_sparse = True
759
760 def _update_chunks_and_blocks(self):
761 """Helper function to update the image header.
762
763 The the |total_chunks| and |total_blocks| fields in the header
764 will be set to value of the |_num_total_blocks| and
765 |_num_total_chunks| attributes.
766
767 """
768 self._image.seek(self.NUM_CHUNKS_AND_BLOCKS_OFFSET, os.SEEK_SET)
769 self._image.write(struct.pack(self.NUM_CHUNKS_AND_BLOCKS_FORMAT,
770 self._num_total_blocks,
771 self._num_total_chunks))
772
773 def append_dont_care(self, num_bytes):
774 """Appends a DONT_CARE chunk to the sparse file.
775
776 The given number of bytes must be a multiple of the block size.
777
778 Arguments:
779 num_bytes: Size in number of bytes of the DONT_CARE chunk.
780 """
781 assert num_bytes % self.block_size == 0
782
783 if not self.is_sparse:
784 self._image.seek(0, os.SEEK_END)
785 # This is more efficient that writing NUL bytes since it'll add
786 # a hole on file systems that support sparse files (native
787 # sparse, not Android sparse).
788 self._image.truncate(self._image.tell() + num_bytes)
789 self._read_header()
790 return
791
792 self._num_total_chunks += 1
793 self._num_total_blocks += num_bytes / self.block_size
794 self._update_chunks_and_blocks()
795
796 self._image.seek(self._sparse_end, os.SEEK_SET)
797 self._image.write(struct.pack(ImageChunk.FORMAT,
798 ImageChunk.TYPE_DONT_CARE,
799 0, # Reserved
800 num_bytes / self.block_size,
801 struct.calcsize(ImageChunk.FORMAT)))
802 self._read_header()
803
804 def append_raw(self, data):
805 """Appends a RAW chunk to the sparse file.
806
807 The length of the given data must be a multiple of the block size.
808
809 Arguments:
810 data: Data to append.
811 """
812 assert len(data) % self.block_size == 0
813
814 if not self.is_sparse:
815 self._image.seek(0, os.SEEK_END)
816 self._image.write(data)
817 self._read_header()
818 return
819
820 self._num_total_chunks += 1
821 self._num_total_blocks += len(data) / self.block_size
822 self._update_chunks_and_blocks()
823
824 self._image.seek(self._sparse_end, os.SEEK_SET)
825 self._image.write(struct.pack(ImageChunk.FORMAT,
826 ImageChunk.TYPE_RAW,
827 0, # Reserved
828 len(data) / self.block_size,
829 len(data) +
830 struct.calcsize(ImageChunk.FORMAT)))
831 self._image.write(data)
832 self._read_header()
833
834 def append_fill(self, fill_data, size):
835 """Appends a fill chunk to the sparse file.
836
837 The total length of the fill data must be a multiple of the block size.
838
839 Arguments:
840 fill_data: Fill data to append - must be four bytes.
841 size: Number of chunk - must be a multiple of four and the block size.
842 """
843 assert len(fill_data) == 4
844 assert size % 4 == 0
845 assert size % self.block_size == 0
846
847 if not self.is_sparse:
848 self._image.seek(0, os.SEEK_END)
849 self._image.write(fill_data * (size/4))
850 self._read_header()
851 return
852
853 self._num_total_chunks += 1
854 self._num_total_blocks += size / self.block_size
855 self._update_chunks_and_blocks()
856
857 self._image.seek(self._sparse_end, os.SEEK_SET)
858 self._image.write(struct.pack(ImageChunk.FORMAT,
859 ImageChunk.TYPE_FILL,
860 0, # Reserved
861 size / self.block_size,
862 4 + struct.calcsize(ImageChunk.FORMAT)))
863 self._image.write(fill_data)
864 self._read_header()
865
866 def seek(self, offset):
867 """Sets the cursor position for reading from unsparsified file.
868
869 Arguments:
870 offset: Offset to seek to from the beginning of the file.
871 """
872 self._file_pos = offset
873
874 def read(self, size):
875 """Reads data from the unsparsified file.
876
877 This method may return fewer than |size| bytes of data if the end
878 of the file was encountered.
879
880 The file cursor for reading is advanced by the number of bytes
881 read.
882
883 Arguments:
884 size: Number of bytes to read.
885
886 Returns:
887 The data.
888
889 """
890 if not self.is_sparse:
891 self._image.seek(self._file_pos)
892 data = self._image.read(size)
893 self._file_pos += len(data)
894 return data
895
896 # Iterate over all chunks.
897 chunk_idx = bisect.bisect_right(self._chunk_output_offsets,
898 self._file_pos) - 1
899 data = bytearray()
900 to_go = size
901 while to_go > 0:
902 chunk = self._chunks[chunk_idx]
903 chunk_pos_offset = self._file_pos - chunk.output_offset
904 chunk_pos_to_go = min(chunk.output_size - chunk_pos_offset, to_go)
905
906 if chunk.chunk_type == ImageChunk.TYPE_RAW:
907 self._image.seek(chunk.input_offset + chunk_pos_offset)
908 data.extend(self._image.read(chunk_pos_to_go))
909 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
910 all_data = chunk.fill_data*(chunk_pos_to_go/len(chunk.fill_data) + 2)
911 offset_mod = chunk_pos_offset % len(chunk.fill_data)
912 data.extend(all_data[offset_mod:(offset_mod + chunk_pos_to_go)])
913 else:
914 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
915 data.extend('\0' * chunk_pos_to_go)
916
917 to_go -= chunk_pos_to_go
918 self._file_pos += chunk_pos_to_go
919 chunk_idx += 1
920 # Generate partial read in case of EOF.
921 if chunk_idx >= len(self._chunks):
922 break
923
924 return data
925
926 def tell(self):
927 """Returns the file cursor position for reading from unsparsified file.
928
929 Returns:
930 The file cursor position for reading.
931 """
932 return self._file_pos
933
934 def truncate(self, size):
935 """Truncates the unsparsified file.
936
937 Arguments:
938 size: Desired size of unsparsified file.
939
940 Raises:
941 ValueError: If desired size isn't a multiple of the block size.
942 """
943 if not self.is_sparse:
944 self._image.truncate(size)
945 self._read_header()
946 return
947
948 if size % self.block_size != 0:
949 raise ValueError('Cannot truncate to a size which is not a multiple '
950 'of the block size')
951
952 if size == self.image_size:
953 # Trivial where there's nothing to do.
954 return
955 elif size < self.image_size:
956 chunk_idx = bisect.bisect_right(self._chunk_output_offsets, size) - 1
957 chunk = self._chunks[chunk_idx]
958 if chunk.output_offset != size:
959 # Truncation in the middle of a trunk - need to keep the chunk
960 # and modify it.
961 chunk_idx_for_update = chunk_idx + 1
962 num_to_keep = size - chunk.output_offset
963 assert num_to_keep % self.block_size == 0
964 if chunk.chunk_type == ImageChunk.TYPE_RAW:
965 truncate_at = (chunk.chunk_offset +
966 struct.calcsize(ImageChunk.FORMAT) + num_to_keep)
967 data_sz = num_to_keep
968 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
969 truncate_at = (chunk.chunk_offset +
970 struct.calcsize(ImageChunk.FORMAT) + 4)
971 data_sz = 4
972 else:
973 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
974 truncate_at = chunk.chunk_offset + struct.calcsize(ImageChunk.FORMAT)
975 data_sz = 0
976 chunk_sz = num_to_keep/self.block_size
977 total_sz = data_sz + struct.calcsize(ImageChunk.FORMAT)
978 self._image.seek(chunk.chunk_offset)
979 self._image.write(struct.pack(ImageChunk.FORMAT,
980 chunk.chunk_type,
981 0, # Reserved
982 chunk_sz,
983 total_sz))
984 chunk.output_size = num_to_keep
985 else:
986 # Truncation at trunk boundary.
987 truncate_at = chunk.chunk_offset
988 chunk_idx_for_update = chunk_idx
989
990 self._num_total_chunks = chunk_idx_for_update
991 self._num_total_blocks = 0
992 for i in range(0, chunk_idx_for_update):
993 self._num_total_blocks += self._chunks[i].output_size / self.block_size
994 self._update_chunks_and_blocks()
995 self._image.truncate(truncate_at)
996
997 # We've modified the file so re-read all data.
998 self._read_header()
999 else:
1000 # Truncating to grow - just add a DONT_CARE section.
1001 self.append_dont_care(size - self.image_size)
1002
1003
David Zeuthen21e95262016-07-27 17:58:40 -04001004class AvbDescriptor(object):
1005 """Class for AVB descriptor.
1006
1007 See the |AvbDescriptor| C struct for more information.
1008
1009 Attributes:
1010 tag: The tag identifying what kind of descriptor this is.
1011 data: The data in the descriptor.
1012 """
1013
1014 SIZE = 16
1015 FORMAT_STRING = ('!QQ') # tag, num_bytes_following (descriptor header)
1016
1017 def __init__(self, data):
1018 """Initializes a new property descriptor.
1019
1020 Arguments:
1021 data: If not None, must be a bytearray().
1022
1023 Raises:
1024 LookupError: If the given descriptor is malformed.
1025 """
1026 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1027
1028 if data:
1029 (self.tag, num_bytes_following) = (
1030 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1031 self.data = data[self.SIZE:self.SIZE + num_bytes_following]
1032 else:
1033 self.tag = None
1034 self.data = None
1035
1036 def print_desc(self, o):
1037 """Print the descriptor.
1038
1039 Arguments:
1040 o: The object to write the output to.
1041 """
1042 o.write(' Unknown descriptor:\n')
1043 o.write(' Tag: {}\n'.format(self.tag))
1044 if len(self.data) < 256:
1045 o.write(' Data: {} ({} bytes)\n'.format(
1046 repr(str(self.data)), len(self.data)))
1047 else:
1048 o.write(' Data: {} bytes\n'.format(len(self.data)))
1049
1050 def encode(self):
1051 """Serializes the descriptor.
1052
1053 Returns:
1054 A bytearray() with the descriptor data.
1055 """
1056 num_bytes_following = len(self.data)
1057 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1058 padding_size = nbf_with_padding - num_bytes_following
1059 desc = struct.pack(self.FORMAT_STRING, self.tag, nbf_with_padding)
1060 padding = struct.pack(str(padding_size) + 'x')
1061 ret = desc + self.data + padding
1062 return bytearray(ret)
1063
1064
1065class AvbPropertyDescriptor(AvbDescriptor):
1066 """A class for property descriptors.
1067
1068 See the |AvbPropertyDescriptor| C struct for more information.
1069
1070 Attributes:
1071 key: The key.
1072 value: The key.
1073 """
1074
1075 TAG = 0
1076 SIZE = 32
1077 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1078 'Q' # key size (bytes)
1079 'Q') # value size (bytes)
1080
1081 def __init__(self, data=None):
1082 """Initializes a new property descriptor.
1083
1084 Arguments:
1085 data: If not None, must be a bytearray of size |SIZE|.
1086
1087 Raises:
1088 LookupError: If the given descriptor is malformed.
1089 """
1090 AvbDescriptor.__init__(self, None)
1091 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1092
1093 if data:
1094 (tag, num_bytes_following, key_size,
1095 value_size) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
1096 expected_size = round_to_multiple(
1097 self.SIZE - 16 + key_size + 1 + value_size + 1, 8)
1098 if tag != self.TAG or num_bytes_following != expected_size:
1099 raise LookupError('Given data does not look like a property '
1100 'descriptor.')
1101 self.key = data[self.SIZE:(self.SIZE + key_size)]
1102 self.value = data[(self.SIZE + key_size + 1):(self.SIZE + key_size + 1 +
1103 value_size)]
1104 else:
1105 self.key = ''
1106 self.value = ''
1107
1108 def print_desc(self, o):
1109 """Print the descriptor.
1110
1111 Arguments:
1112 o: The object to write the output to.
1113 """
1114 if len(self.value) < 256:
1115 o.write(' Prop: {} -> {}\n'.format(self.key, repr(str(self.value))))
1116 else:
1117 o.write(' Prop: {} -> ({} bytes)\n'.format(self.key, len(self.value)))
1118
1119 def encode(self):
1120 """Serializes the descriptor.
1121
1122 Returns:
1123 A bytearray() with the descriptor data.
1124 """
1125 num_bytes_following = self.SIZE + len(self.key) + len(self.value) + 2 - 16
1126 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1127 padding_size = nbf_with_padding - num_bytes_following
1128 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1129 len(self.key), len(self.value))
1130 padding = struct.pack(str(padding_size) + 'x')
1131 ret = desc + self.key + '\0' + self.value + '\0' + padding
1132 return bytearray(ret)
1133
1134
1135class AvbHashtreeDescriptor(AvbDescriptor):
1136 """A class for hashtree descriptors.
1137
1138 See the |AvbHashtreeDescriptor| C struct for more information.
1139
1140 Attributes:
1141 dm_verity_version: dm-verity version used.
1142 image_size: Size of the image, after rounding up to |block_size|.
1143 tree_offset: Offset of the hash tree in the file.
1144 tree_size: Size of the tree.
1145 data_block_size: Data block size
1146 hash_block_size: Hash block size
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001147 fec_num_roots: Number of roots used for FEC (0 if FEC is not used).
1148 fec_offset: Offset of FEC data (0 if FEC is not used).
1149 fec_size: Size of FEC data (0 if FEC is not used).
David Zeuthen21e95262016-07-27 17:58:40 -04001150 hash_algorithm: Hash algorithm used.
1151 partition_name: Partition name.
1152 salt: Salt used.
1153 root_digest: Root digest.
1154 """
1155
1156 TAG = 1
David Zeuthen5cb2db92016-10-27 15:14:14 -04001157 RESERVED = 64
1158 SIZE = 116 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001159 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1160 'L' # dm-verity version used
1161 'Q' # image size (bytes)
1162 'Q' # tree offset (bytes)
1163 'Q' # tree size (bytes)
1164 'L' # data block size (bytes)
1165 'L' # hash block size (bytes)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001166 'L' # FEC number of roots
1167 'Q' # FEC offset (bytes)
1168 'Q' # FEC size (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001169 '32s' # hash algorithm used
1170 'L' # partition name (bytes)
1171 'L' # salt length (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001172 'L' + # root digest length (bytes)
1173 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001174
1175 def __init__(self, data=None):
1176 """Initializes a new hashtree descriptor.
1177
1178 Arguments:
1179 data: If not None, must be a bytearray of size |SIZE|.
1180
1181 Raises:
1182 LookupError: If the given descriptor is malformed.
1183 """
1184 AvbDescriptor.__init__(self, None)
1185 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1186
1187 if data:
1188 (tag, num_bytes_following, self.dm_verity_version, self.image_size,
1189 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001190 self.hash_block_size, self.fec_num_roots, self.fec_offset, self.fec_size,
1191 self.hash_algorithm, partition_name_len, salt_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001192 root_digest_len, _) = struct.unpack(self.FORMAT_STRING,
1193 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001194 expected_size = round_to_multiple(
1195 self.SIZE - 16 + partition_name_len + salt_len + root_digest_len, 8)
1196 if tag != self.TAG or num_bytes_following != expected_size:
1197 raise LookupError('Given data does not look like a hashtree '
1198 'descriptor.')
1199 # Nuke NUL-bytes at the end.
1200 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1201 o = 0
1202 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1203 partition_name_len)])
1204 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1205 self.partition_name.decode('utf-8')
1206 o += partition_name_len
1207 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1208 o += salt_len
1209 self.root_digest = data[(self.SIZE + o):(self.SIZE + o + root_digest_len)]
1210 if root_digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
1211 raise LookupError('root_digest_len doesn\'t match hash algorithm')
1212
1213 else:
1214 self.dm_verity_version = 0
1215 self.image_size = 0
1216 self.tree_offset = 0
1217 self.tree_size = 0
1218 self.data_block_size = 0
1219 self.hash_block_size = 0
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001220 self.fec_num_roots = 0
1221 self.fec_offset = 0
1222 self.fec_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001223 self.hash_algorithm = ''
1224 self.partition_name = ''
1225 self.salt = bytearray()
1226 self.root_digest = bytearray()
1227
1228 def print_desc(self, o):
1229 """Print the descriptor.
1230
1231 Arguments:
1232 o: The object to write the output to.
1233 """
1234 o.write(' Hashtree descriptor:\n')
1235 o.write(' Version of dm-verity: {}\n'.format(self.dm_verity_version))
1236 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1237 o.write(' Tree Offset: {}\n'.format(self.tree_offset))
1238 o.write(' Tree Size: {} bytes\n'.format(self.tree_size))
1239 o.write(' Data Block Size: {} bytes\n'.format(
1240 self.data_block_size))
1241 o.write(' Hash Block Size: {} bytes\n'.format(
1242 self.hash_block_size))
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001243 o.write(' FEC num roots: {}\n'.format(self.fec_num_roots))
1244 o.write(' FEC offset: {}\n'.format(self.fec_offset))
1245 o.write(' FEC size: {} bytes\n'.format(self.fec_size))
David Zeuthen21e95262016-07-27 17:58:40 -04001246 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1247 o.write(' Partition Name: {}\n'.format(self.partition_name))
1248 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1249 'hex')))
1250 o.write(' Root Digest: {}\n'.format(str(
1251 self.root_digest).encode('hex')))
1252
1253 def encode(self):
1254 """Serializes the descriptor.
1255
1256 Returns:
1257 A bytearray() with the descriptor data.
1258 """
1259 encoded_name = self.partition_name.encode('utf-8')
1260 num_bytes_following = (self.SIZE + len(encoded_name) + len(self.salt) +
1261 len(self.root_digest) - 16)
1262 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1263 padding_size = nbf_with_padding - num_bytes_following
1264 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1265 self.dm_verity_version, self.image_size,
1266 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001267 self.hash_block_size, self.fec_num_roots,
1268 self.fec_offset, self.fec_size, self.hash_algorithm,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001269 len(encoded_name), len(self.salt), len(self.root_digest),
1270 self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001271 padding = struct.pack(str(padding_size) + 'x')
1272 ret = desc + encoded_name + self.salt + self.root_digest + padding
1273 return bytearray(ret)
1274
1275
1276class AvbHashDescriptor(AvbDescriptor):
1277 """A class for hash descriptors.
1278
1279 See the |AvbHashDescriptor| C struct for more information.
1280
1281 Attributes:
1282 image_size: Image size, in bytes.
1283 hash_algorithm: Hash algorithm used.
1284 partition_name: Partition name.
1285 salt: Salt used.
1286 digest: The hash value of salt and data combined.
1287 """
1288
1289 TAG = 2
David Zeuthen5cb2db92016-10-27 15:14:14 -04001290 RESERVED = 64
1291 SIZE = 68 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001292 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1293 'Q' # image size (bytes)
1294 '32s' # hash algorithm used
1295 'L' # partition name (bytes)
1296 'L' # salt length (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001297 'L' + # digest length (bytes)
1298 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001299
1300 def __init__(self, data=None):
1301 """Initializes a new hash descriptor.
1302
1303 Arguments:
1304 data: If not None, must be a bytearray of size |SIZE|.
1305
1306 Raises:
1307 LookupError: If the given descriptor is malformed.
1308 """
1309 AvbDescriptor.__init__(self, None)
1310 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1311
1312 if data:
1313 (tag, num_bytes_following, self.image_size, self.hash_algorithm,
1314 partition_name_len, salt_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001315 digest_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001316 expected_size = round_to_multiple(
1317 self.SIZE - 16 + partition_name_len + salt_len + digest_len, 8)
1318 if tag != self.TAG or num_bytes_following != expected_size:
1319 raise LookupError('Given data does not look like a hash ' 'descriptor.')
1320 # Nuke NUL-bytes at the end.
1321 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1322 o = 0
1323 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1324 partition_name_len)])
1325 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1326 self.partition_name.decode('utf-8')
1327 o += partition_name_len
1328 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1329 o += salt_len
1330 self.digest = data[(self.SIZE + o):(self.SIZE + o + digest_len)]
1331 if digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
1332 raise LookupError('digest_len doesn\'t match hash algorithm')
1333
1334 else:
1335 self.image_size = 0
1336 self.hash_algorithm = ''
1337 self.partition_name = ''
1338 self.salt = bytearray()
1339 self.digest = bytearray()
1340
1341 def print_desc(self, o):
1342 """Print the descriptor.
1343
1344 Arguments:
1345 o: The object to write the output to.
1346 """
1347 o.write(' Hash descriptor:\n')
1348 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1349 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1350 o.write(' Partition Name: {}\n'.format(self.partition_name))
1351 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1352 'hex')))
1353 o.write(' Digest: {}\n'.format(str(self.digest).encode(
1354 'hex')))
1355
1356 def encode(self):
1357 """Serializes the descriptor.
1358
1359 Returns:
1360 A bytearray() with the descriptor data.
1361 """
1362 encoded_name = self.partition_name.encode('utf-8')
1363 num_bytes_following = (
1364 self.SIZE + len(encoded_name) + len(self.salt) + len(self.digest) - 16)
1365 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1366 padding_size = nbf_with_padding - num_bytes_following
1367 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1368 self.image_size, self.hash_algorithm, len(encoded_name),
David Zeuthen5cb2db92016-10-27 15:14:14 -04001369 len(self.salt), len(self.digest), self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001370 padding = struct.pack(str(padding_size) + 'x')
1371 ret = desc + encoded_name + self.salt + self.digest + padding
1372 return bytearray(ret)
1373
1374
1375class AvbKernelCmdlineDescriptor(AvbDescriptor):
1376 """A class for kernel command-line descriptors.
1377
1378 See the |AvbKernelCmdlineDescriptor| C struct for more information.
1379
1380 Attributes:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001381 flags: Flags.
David Zeuthen21e95262016-07-27 17:58:40 -04001382 kernel_cmdline: The kernel command-line.
1383 """
1384
1385 TAG = 3
David Zeuthenfd41eb92016-11-17 12:24:47 -05001386 SIZE = 24
David Zeuthen21e95262016-07-27 17:58:40 -04001387 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001388 'L' # flags
David Zeuthen21e95262016-07-27 17:58:40 -04001389 'L') # cmdline length (bytes)
1390
David Zeuthenfd41eb92016-11-17 12:24:47 -05001391 FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED = (1 << 0)
1392 FLAGS_USE_ONLY_IF_HASHTREE_DISABLED = (1 << 1)
1393
David Zeuthen21e95262016-07-27 17:58:40 -04001394 def __init__(self, data=None):
1395 """Initializes a new kernel cmdline descriptor.
1396
1397 Arguments:
1398 data: If not None, must be a bytearray of size |SIZE|.
1399
1400 Raises:
1401 LookupError: If the given descriptor is malformed.
1402 """
1403 AvbDescriptor.__init__(self, None)
1404 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1405
1406 if data:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001407 (tag, num_bytes_following, self.flags, kernel_cmdline_length) = (
David Zeuthen21e95262016-07-27 17:58:40 -04001408 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1409 expected_size = round_to_multiple(self.SIZE - 16 + kernel_cmdline_length,
1410 8)
1411 if tag != self.TAG or num_bytes_following != expected_size:
1412 raise LookupError('Given data does not look like a kernel cmdline '
1413 'descriptor.')
1414 # Nuke NUL-bytes at the end.
1415 self.kernel_cmdline = str(data[self.SIZE:(self.SIZE +
1416 kernel_cmdline_length)])
1417 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1418 self.kernel_cmdline.decode('utf-8')
1419 else:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001420 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001421 self.kernel_cmdline = ''
1422
1423 def print_desc(self, o):
1424 """Print the descriptor.
1425
1426 Arguments:
1427 o: The object to write the output to.
1428 """
1429 o.write(' Kernel Cmdline descriptor:\n')
David Zeuthenfd41eb92016-11-17 12:24:47 -05001430 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001431 o.write(' Kernel Cmdline: {}\n'.format(repr(
1432 self.kernel_cmdline)))
1433
1434 def encode(self):
1435 """Serializes the descriptor.
1436
1437 Returns:
1438 A bytearray() with the descriptor data.
1439 """
1440 encoded_str = self.kernel_cmdline.encode('utf-8')
1441 num_bytes_following = (self.SIZE + len(encoded_str) - 16)
1442 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1443 padding_size = nbf_with_padding - num_bytes_following
1444 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001445 self.flags, len(encoded_str))
David Zeuthen21e95262016-07-27 17:58:40 -04001446 padding = struct.pack(str(padding_size) + 'x')
1447 ret = desc + encoded_str + padding
1448 return bytearray(ret)
1449
1450
1451class AvbChainPartitionDescriptor(AvbDescriptor):
1452 """A class for chained partition descriptors.
1453
1454 See the |AvbChainPartitionDescriptor| C struct for more information.
1455
1456 Attributes:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001457 rollback_index_location: The rollback index location to use.
David Zeuthen21e95262016-07-27 17:58:40 -04001458 partition_name: Partition name.
1459 public_key: Bytes for the public key.
1460 """
1461
1462 TAG = 4
David Zeuthen5cb2db92016-10-27 15:14:14 -04001463 RESERVED = 64
1464 SIZE = 28 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001465 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthen40ee1da2016-11-23 15:14:49 -05001466 'L' # rollback_index_location
David Zeuthen21e95262016-07-27 17:58:40 -04001467 'L' # partition_name_size (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001468 'L' + # public_key_size (bytes)
1469 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001470
1471 def __init__(self, data=None):
1472 """Initializes a new chain partition descriptor.
1473
1474 Arguments:
1475 data: If not None, must be a bytearray of size |SIZE|.
1476
1477 Raises:
1478 LookupError: If the given descriptor is malformed.
1479 """
1480 AvbDescriptor.__init__(self, None)
1481 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1482
1483 if data:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001484 (tag, num_bytes_following, self.rollback_index_location,
1485 partition_name_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001486 public_key_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001487 expected_size = round_to_multiple(
1488 self.SIZE - 16 + partition_name_len + public_key_len, 8)
1489 if tag != self.TAG or num_bytes_following != expected_size:
1490 raise LookupError('Given data does not look like a chain partition '
1491 'descriptor.')
1492 o = 0
1493 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1494 partition_name_len)])
1495 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1496 self.partition_name.decode('utf-8')
1497 o += partition_name_len
1498 self.public_key = data[(self.SIZE + o):(self.SIZE + o + public_key_len)]
1499
1500 else:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001501 self.rollback_index_location = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001502 self.partition_name = ''
1503 self.public_key = bytearray()
1504
1505 def print_desc(self, o):
1506 """Print the descriptor.
1507
1508 Arguments:
1509 o: The object to write the output to.
1510 """
1511 o.write(' Chain Partition descriptor:\n')
David Zeuthen40ee1da2016-11-23 15:14:49 -05001512 o.write(' Partition Name: {}\n'.format(self.partition_name))
1513 o.write(' Rollback Index Location: {}\n'.format(
1514 self.rollback_index_location))
David Zeuthen21e95262016-07-27 17:58:40 -04001515 # Just show the SHA1 of the key, for size reasons.
1516 hexdig = hashlib.sha1(self.public_key).hexdigest()
David Zeuthen40ee1da2016-11-23 15:14:49 -05001517 o.write(' Public key (sha1): {}\n'.format(hexdig))
David Zeuthen21e95262016-07-27 17:58:40 -04001518
1519 def encode(self):
1520 """Serializes the descriptor.
1521
1522 Returns:
1523 A bytearray() with the descriptor data.
1524 """
1525 encoded_name = self.partition_name.encode('utf-8')
1526 num_bytes_following = (
1527 self.SIZE + len(encoded_name) + len(self.public_key) - 16)
1528 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1529 padding_size = nbf_with_padding - num_bytes_following
1530 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthen40ee1da2016-11-23 15:14:49 -05001531 self.rollback_index_location, len(encoded_name),
David Zeuthen5cb2db92016-10-27 15:14:14 -04001532 len(self.public_key), self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001533 padding = struct.pack(str(padding_size) + 'x')
1534 ret = desc + encoded_name + self.public_key + padding
1535 return bytearray(ret)
1536
1537
1538DESCRIPTOR_CLASSES = [
1539 AvbPropertyDescriptor, AvbHashtreeDescriptor, AvbHashDescriptor,
1540 AvbKernelCmdlineDescriptor, AvbChainPartitionDescriptor
1541]
1542
1543
1544def parse_descriptors(data):
1545 """Parses a blob of data into descriptors.
1546
1547 Arguments:
1548 data: A bytearray() with encoded descriptors.
1549
1550 Returns:
1551 A list of instances of objects derived from AvbDescriptor. For
1552 unknown descriptors, the class AvbDescriptor is used.
1553 """
1554 o = 0
1555 ret = []
1556 while o < len(data):
1557 tag, nb_following = struct.unpack('!2Q', data[o:o + 16])
1558 if tag < len(DESCRIPTOR_CLASSES):
1559 c = DESCRIPTOR_CLASSES[tag]
1560 else:
1561 c = AvbDescriptor
1562 ret.append(c(bytearray(data[o:o + 16 + nb_following])))
1563 o += 16 + nb_following
1564 return ret
1565
1566
1567class AvbFooter(object):
1568 """A class for parsing and writing footers.
1569
1570 Footers are stored at the end of partitions and point to where the
1571 AvbVBMeta blob is located. They also contain the original size of
1572 the image before AVB information was added.
1573
1574 Attributes:
1575 magic: Magic for identifying the footer, see |MAGIC|.
1576 version_major: The major version of avbtool that wrote the footer.
1577 version_minor: The minor version of avbtool that wrote the footer.
1578 original_image_size: Original image size.
1579 vbmeta_offset: Offset of where the AvbVBMeta blob is stored.
1580 vbmeta_size: Size of the AvbVBMeta blob.
1581 """
1582
1583 MAGIC = 'AVBf'
1584 SIZE = 64
1585 RESERVED = 28
David Zeuthene3cadca2017-02-22 21:25:46 -05001586 FOOTER_VERSION_MAJOR = 1
1587 FOOTER_VERSION_MINOR = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001588 FORMAT_STRING = ('!4s2L' # magic, 2 x version.
1589 'Q' # Original image size.
1590 'Q' # Offset of VBMeta blob.
1591 'Q' + # Size of VBMeta blob.
1592 str(RESERVED) + 'x') # padding for reserved bytes
1593
1594 def __init__(self, data=None):
1595 """Initializes a new footer object.
1596
1597 Arguments:
1598 data: If not None, must be a bytearray of size 4096.
1599
1600 Raises:
1601 LookupError: If the given footer is malformed.
1602 struct.error: If the given data has no footer.
1603 """
1604 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1605
1606 if data:
1607 (self.magic, self.version_major, self.version_minor,
1608 self.original_image_size, self.vbmeta_offset,
1609 self.vbmeta_size) = struct.unpack(self.FORMAT_STRING, data)
1610 if self.magic != self.MAGIC:
David Zeuthen8b6973b2016-09-20 12:39:49 -04001611 raise LookupError('Given data does not look like a AVB footer.')
David Zeuthen21e95262016-07-27 17:58:40 -04001612 else:
1613 self.magic = self.MAGIC
David Zeuthene3cadca2017-02-22 21:25:46 -05001614 self.version_major = self.FOOTER_VERSION_MAJOR
1615 self.version_minor = self.FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001616 self.original_image_size = 0
1617 self.vbmeta_offset = 0
1618 self.vbmeta_size = 0
1619
David Zeuthena4fee8b2016-08-22 15:20:43 -04001620 def encode(self):
1621 """Gets a string representing the binary encoding of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001622
David Zeuthena4fee8b2016-08-22 15:20:43 -04001623 Returns:
1624 A bytearray() with a binary representation of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001625 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04001626 return struct.pack(self.FORMAT_STRING, self.magic, self.version_major,
1627 self.version_minor, self.original_image_size,
1628 self.vbmeta_offset, self.vbmeta_size)
David Zeuthen21e95262016-07-27 17:58:40 -04001629
1630
1631class AvbVBMetaHeader(object):
David Zeuthen8b6973b2016-09-20 12:39:49 -04001632 """A class for parsing and writing AVB vbmeta images.
David Zeuthen21e95262016-07-27 17:58:40 -04001633
1634 Attributes:
1635 The attributes correspond to the |AvbVBMetaHeader| struct
1636 defined in avb_vbmeta_header.h.
1637 """
1638
1639 SIZE = 256
1640
David Zeuthene3cadca2017-02-22 21:25:46 -05001641 # Keep in sync with |reserved0| and |reserved| field of
1642 # |AvbVBMetaImageHeader|.
1643 RESERVED0 = 4
1644 RESERVED = 80
David Zeuthen21e95262016-07-27 17:58:40 -04001645
1646 # Keep in sync with |AvbVBMetaImageHeader|.
1647 FORMAT_STRING = ('!4s2L' # magic, 2 x version
1648 '2Q' # 2 x block size
1649 'L' # algorithm type
1650 '2Q' # offset, size (hash)
1651 '2Q' # offset, size (signature)
1652 '2Q' # offset, size (public key)
David Zeuthen18666ab2016-11-15 11:18:05 -05001653 '2Q' # offset, size (public key metadata)
David Zeuthen21e95262016-07-27 17:58:40 -04001654 '2Q' # offset, size (descriptors)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001655 'Q' # rollback_index
1656 'L' + # flags
David Zeuthene3cadca2017-02-22 21:25:46 -05001657 str(RESERVED0) + 'x' + # padding for reserved bytes
1658 '47sx' + # NUL-terminated release string
David Zeuthen21e95262016-07-27 17:58:40 -04001659 str(RESERVED) + 'x') # padding for reserved bytes
1660
1661 def __init__(self, data=None):
1662 """Initializes a new header object.
1663
1664 Arguments:
1665 data: If not None, must be a bytearray of size 8192.
1666
1667 Raises:
1668 Exception: If the given data is malformed.
1669 """
1670 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1671
1672 if data:
David Zeuthene3cadca2017-02-22 21:25:46 -05001673 (self.magic, self.required_libavb_version_major,
1674 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04001675 self.authentication_data_block_size, self.auxiliary_data_block_size,
1676 self.algorithm_type, self.hash_offset, self.hash_size,
1677 self.signature_offset, self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05001678 self.public_key_size, self.public_key_metadata_offset,
1679 self.public_key_metadata_size, self.descriptors_offset,
1680 self.descriptors_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001681 self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05001682 self.flags,
1683 self.release_string) = struct.unpack(self.FORMAT_STRING, data)
David Zeuthen21e95262016-07-27 17:58:40 -04001684 # Nuke NUL-bytes at the end of the string.
1685 if self.magic != 'AVB0':
David Zeuthen8b6973b2016-09-20 12:39:49 -04001686 raise AvbError('Given image does not look like a vbmeta image.')
David Zeuthen21e95262016-07-27 17:58:40 -04001687 else:
1688 self.magic = 'AVB0'
David Zeuthene3cadca2017-02-22 21:25:46 -05001689 # Start by just requiring version 1.0. Code that adds features
1690 # in a future version can use bump_required_libavb_version_minor() to
1691 # bump the minor.
1692 self.required_libavb_version_major = AVB_VERSION_MAJOR
1693 self.required_libavb_version_minor = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001694 self.authentication_data_block_size = 0
1695 self.auxiliary_data_block_size = 0
1696 self.algorithm_type = 0
1697 self.hash_offset = 0
1698 self.hash_size = 0
1699 self.signature_offset = 0
1700 self.signature_size = 0
1701 self.public_key_offset = 0
1702 self.public_key_size = 0
David Zeuthen18666ab2016-11-15 11:18:05 -05001703 self.public_key_metadata_offset = 0
1704 self.public_key_metadata_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001705 self.descriptors_offset = 0
1706 self.descriptors_size = 0
1707 self.rollback_index = 0
David Zeuthenfd41eb92016-11-17 12:24:47 -05001708 self.flags = 0
David Zeuthene3cadca2017-02-22 21:25:46 -05001709 self.release_string = get_release_string()
1710
1711 def bump_required_libavb_version_minor(self, minor):
1712 """Function to bump required_libavb_version_minor.
1713
1714 Call this when writing data that requires a specific libavb
1715 version to parse it.
1716
1717 Arguments:
1718 minor: The minor version of libavb that has support for the feature.
1719 """
1720 self.required_libavb_version_minor = (
1721 min(self.required_libavb_version_minor, minor))
David Zeuthen21e95262016-07-27 17:58:40 -04001722
1723 def save(self, output):
1724 """Serializes the header (256 bytes) to disk.
1725
1726 Arguments:
1727 output: The object to write the output to.
1728 """
1729 output.write(struct.pack(
David Zeuthene3cadca2017-02-22 21:25:46 -05001730 self.FORMAT_STRING, self.magic, self.required_libavb_version_major,
1731 self.required_libavb_version_minor, self.authentication_data_block_size,
David Zeuthen21e95262016-07-27 17:58:40 -04001732 self.auxiliary_data_block_size, self.algorithm_type, self.hash_offset,
1733 self.hash_size, self.signature_offset, self.signature_size,
David Zeuthen18666ab2016-11-15 11:18:05 -05001734 self.public_key_offset, self.public_key_size,
1735 self.public_key_metadata_offset, self.public_key_metadata_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001736 self.descriptors_offset, self.descriptors_size, self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05001737 self.flags, self.release_string))
David Zeuthen21e95262016-07-27 17:58:40 -04001738
1739 def encode(self):
1740 """Serializes the header (256) to a bytearray().
1741
1742 Returns:
1743 A bytearray() with the encoded header.
1744 """
1745 return struct.pack(self.FORMAT_STRING, self.magic,
David Zeuthene3cadca2017-02-22 21:25:46 -05001746 self.required_libavb_version_major,
1747 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04001748 self.authentication_data_block_size,
1749 self.auxiliary_data_block_size, self.algorithm_type,
1750 self.hash_offset, self.hash_size, self.signature_offset,
1751 self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05001752 self.public_key_size, self.public_key_metadata_offset,
1753 self.public_key_metadata_size, self.descriptors_offset,
David Zeuthene3cadca2017-02-22 21:25:46 -05001754 self.descriptors_size, self.rollback_index, self.flags,
1755 self.release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04001756
1757
1758class Avb(object):
1759 """Business logic for avbtool command-line tool."""
1760
David Zeuthen8b6973b2016-09-20 12:39:49 -04001761 # Keep in sync with avb_ab_flow.h.
1762 AB_FORMAT_NO_CRC = '!4sBB2xBBBxBBBx12x'
1763 AB_MAGIC = '\0AB0'
1764 AB_MAJOR_VERSION = 1
1765 AB_MINOR_VERSION = 0
1766 AB_MISC_METADATA_OFFSET = 2048
1767
David Zeuthen09692692016-09-30 16:16:40 -04001768 # Constants for maximum metadata size. These are used to give
1769 # meaningful errors if the value passed in via --partition_size is
1770 # too small and when --calc_max_image_size is used. We use
1771 # conservative figures.
1772 MAX_VBMETA_SIZE = 64 * 1024
1773 MAX_FOOTER_SIZE = 4096
1774
David Zeuthena4fee8b2016-08-22 15:20:43 -04001775 def erase_footer(self, image_filename, keep_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04001776 """Implements the 'erase_footer' command.
1777
1778 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001779 image_filename: File to erase a footer from.
David Zeuthenfbb61fa2017-02-02 12:11:49 -05001780 keep_hashtree: If True, keep the hashtree and FEC around.
David Zeuthen21e95262016-07-27 17:58:40 -04001781
1782 Raises:
1783 AvbError: If there's no footer in the image.
1784 """
1785
David Zeuthena4fee8b2016-08-22 15:20:43 -04001786 image = ImageHandler(image_filename)
1787
David Zeuthen21e95262016-07-27 17:58:40 -04001788 (footer, _, descriptors, _) = self._parse_image(image)
1789
1790 if not footer:
1791 raise AvbError('Given image does not have a footer.')
1792
1793 new_image_size = None
1794 if not keep_hashtree:
1795 new_image_size = footer.original_image_size
1796 else:
1797 # If requested to keep the hashtree, search for a hashtree
David Zeuthenfbb61fa2017-02-02 12:11:49 -05001798 # descriptor to figure out the location and size of the hashtree
1799 # and FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04001800 for desc in descriptors:
1801 if isinstance(desc, AvbHashtreeDescriptor):
1802 # The hashtree is always just following the main data so the
1803 # new size is easily derived.
1804 new_image_size = desc.tree_offset + desc.tree_size
David Zeuthenfbb61fa2017-02-02 12:11:49 -05001805 # If the image has FEC codes, also keep those.
1806 if desc.fec_offset > 0:
1807 fec_end = desc.fec_offset + desc.fec_size
1808 new_image_size = max(new_image_size, fec_end)
David Zeuthen21e95262016-07-27 17:58:40 -04001809 break
1810 if not new_image_size:
1811 raise AvbError('Requested to keep hashtree but no hashtree '
1812 'descriptor was found.')
1813
1814 # And cut...
1815 image.truncate(new_image_size)
1816
David Zeuthen2bc232b2017-04-19 14:25:19 -04001817 def resize_image(self, image_filename, partition_size):
1818 """Implements the 'resize_image' command.
1819
1820 Arguments:
1821 image_filename: File with footer to resize.
1822 partition_size: The new size of the image.
1823
1824 Raises:
1825 AvbError: If there's no footer in the image.
1826 """
1827
1828 image = ImageHandler(image_filename)
1829
1830 if partition_size % image.block_size != 0:
1831 raise AvbError('Partition size of {} is not a multiple of the image '
1832 'block size {}.'.format(partition_size,
1833 image.block_size))
1834
1835 (footer, vbmeta_header, descriptors, _) = self._parse_image(image)
1836
1837 if not footer:
1838 raise AvbError('Given image does not have a footer.')
1839
1840 # The vbmeta blob is always at the end of the data so resizing an
1841 # image amounts to just moving the footer around.
1842
1843 vbmeta_end_offset = footer.vbmeta_offset + footer.vbmeta_size
1844 if vbmeta_end_offset % image.block_size != 0:
1845 vbmeta_end_offset += image.block_size - (vbmeta_end_offset % image.block_size)
1846
1847 if partition_size < vbmeta_end_offset + 1*image.block_size:
1848 raise AvbError('Requested size of {} is too small for an image '
1849 'of size {}.'
1850 .format(partition_size,
1851 vbmeta_end_offset + 1*image.block_size))
1852
1853 # Cut at the end of the vbmeta blob and insert a DONT_CARE chunk
1854 # with enough bytes such that the final Footer block is at the end
1855 # of partition_size.
1856 image.truncate(vbmeta_end_offset)
1857 image.append_dont_care(partition_size - vbmeta_end_offset -
1858 1*image.block_size)
1859
1860 # Just reuse the same footer - only difference is that we're
1861 # writing it in a different place.
1862 footer_blob = footer.encode()
1863 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
1864 footer_blob)
1865 image.append_raw(footer_blob_with_padding)
1866
David Zeuthen8b6973b2016-09-20 12:39:49 -04001867 def set_ab_metadata(self, misc_image, slot_data):
1868 """Implements the 'set_ab_metadata' command.
1869
1870 The |slot_data| argument must be of the form 'A_priority:A_tries_remaining:
1871 A_successful_boot:B_priority:B_tries_remaining:B_successful_boot'.
1872
1873 Arguments:
1874 misc_image: The misc image to write to.
1875 slot_data: Slot data as a string
1876
1877 Raises:
1878 AvbError: If slot data is malformed.
1879 """
1880 tokens = slot_data.split(':')
1881 if len(tokens) != 6:
1882 raise AvbError('Malformed slot data "{}".'.format(slot_data))
1883 a_priority = int(tokens[0])
1884 a_tries_remaining = int(tokens[1])
1885 a_success = True if int(tokens[2]) != 0 else False
1886 b_priority = int(tokens[3])
1887 b_tries_remaining = int(tokens[4])
1888 b_success = True if int(tokens[5]) != 0 else False
1889
1890 ab_data_no_crc = struct.pack(self.AB_FORMAT_NO_CRC,
1891 self.AB_MAGIC,
1892 self.AB_MAJOR_VERSION, self.AB_MINOR_VERSION,
1893 a_priority, a_tries_remaining, a_success,
1894 b_priority, b_tries_remaining, b_success)
1895 # Force CRC to be unsigned, see https://bugs.python.org/issue4903 for why.
1896 crc_value = binascii.crc32(ab_data_no_crc) & 0xffffffff
1897 ab_data = ab_data_no_crc + struct.pack('!I', crc_value)
1898 misc_image.seek(self.AB_MISC_METADATA_OFFSET)
1899 misc_image.write(ab_data)
1900
David Zeuthena4fee8b2016-08-22 15:20:43 -04001901 def info_image(self, image_filename, output):
David Zeuthen21e95262016-07-27 17:58:40 -04001902 """Implements the 'info_image' command.
1903
1904 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001905 image_filename: Image file to get information from (file object).
David Zeuthen21e95262016-07-27 17:58:40 -04001906 output: Output file to write human-readable information to (file object).
1907 """
1908
David Zeuthena4fee8b2016-08-22 15:20:43 -04001909 image = ImageHandler(image_filename)
1910
David Zeuthen21e95262016-07-27 17:58:40 -04001911 o = output
1912
1913 (footer, header, descriptors, image_size) = self._parse_image(image)
1914
1915 if footer:
1916 o.write('Footer version: {}.{}\n'.format(footer.version_major,
1917 footer.version_minor))
1918 o.write('Image size: {} bytes\n'.format(image_size))
1919 o.write('Original image size: {} bytes\n'.format(
1920 footer.original_image_size))
1921 o.write('VBMeta offset: {}\n'.format(footer.vbmeta_offset))
1922 o.write('VBMeta size: {} bytes\n'.format(footer.vbmeta_size))
1923 o.write('--\n')
1924
1925 (alg_name, _) = lookup_algorithm_by_type(header.algorithm_type)
1926
David Zeuthene3cadca2017-02-22 21:25:46 -05001927 o.write('Minimum libavb version: {}.{}{}\n'.format(
1928 header.required_libavb_version_major,
1929 header.required_libavb_version_minor,
David Zeuthena4fee8b2016-08-22 15:20:43 -04001930 ' (Sparse)' if image.is_sparse else ''))
David Zeuthen21e95262016-07-27 17:58:40 -04001931 o.write('Header Block: {} bytes\n'.format(AvbVBMetaHeader.SIZE))
1932 o.write('Authentication Block: {} bytes\n'.format(
1933 header.authentication_data_block_size))
1934 o.write('Auxiliary Block: {} bytes\n'.format(
1935 header.auxiliary_data_block_size))
1936 o.write('Algorithm: {}\n'.format(alg_name))
1937 o.write('Rollback Index: {}\n'.format(header.rollback_index))
David Zeuthenfd41eb92016-11-17 12:24:47 -05001938 o.write('Flags: {}\n'.format(header.flags))
David Zeuthene3cadca2017-02-22 21:25:46 -05001939 o.write('Release String: \'{}\'\n'.format(
1940 header.release_string.rstrip('\0')))
David Zeuthen21e95262016-07-27 17:58:40 -04001941
1942 # Print descriptors.
1943 num_printed = 0
1944 o.write('Descriptors:\n')
1945 for desc in descriptors:
1946 desc.print_desc(o)
1947 num_printed += 1
1948 if num_printed == 0:
1949 o.write(' (none)\n')
1950
David Zeuthenb623d8b2017-04-04 16:05:53 -04001951 def verify_image(self, image_filename):
1952 """Implements the 'verify_image' command.
1953
1954 Arguments:
1955 image_filename: Image file to get information from (file object).
1956 """
1957
1958 image = ImageHandler(image_filename)
1959 (footer, header, descriptors, image_size) = self._parse_image(image)
1960 offset = 0
1961 if footer:
1962 offset = footer.vbmeta_offset
1963 size = (header.SIZE + header.authentication_data_block_size +
1964 header.auxiliary_data_block_size)
1965 image.seek(offset)
1966 vbmeta_blob = image.read(size)
1967 if not verify_vbmeta_signature(header, vbmeta_blob):
1968 raise AvbError('Signature check failed.')
1969
David Zeuthen21e95262016-07-27 17:58:40 -04001970 def _parse_image(self, image):
1971 """Gets information about an image.
1972
1973 The image can either be a vbmeta or an image with a footer.
1974
1975 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001976 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04001977
1978 Returns:
1979 A tuple where the first argument is a AvbFooter (None if there
1980 is no footer on the image), the second argument is a
1981 AvbVBMetaHeader, the third argument is a list of
1982 AvbDescriptor-derived instances, and the fourth argument is the
1983 size of |image|.
1984 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04001985 assert isinstance(image, ImageHandler)
David Zeuthen21e95262016-07-27 17:58:40 -04001986 footer = None
David Zeuthen09692692016-09-30 16:16:40 -04001987 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04001988 try:
1989 footer = AvbFooter(image.read(AvbFooter.SIZE))
1990 except (LookupError, struct.error):
1991 # Nope, just seek back to the start.
1992 image.seek(0)
1993
1994 vbmeta_offset = 0
1995 if footer:
1996 vbmeta_offset = footer.vbmeta_offset
1997
1998 image.seek(vbmeta_offset)
1999 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2000
2001 auth_block_offset = vbmeta_offset + AvbVBMetaHeader.SIZE
2002 aux_block_offset = auth_block_offset + h.authentication_data_block_size
2003 desc_start_offset = aux_block_offset + h.descriptors_offset
2004 image.seek(desc_start_offset)
2005 descriptors = parse_descriptors(image.read(h.descriptors_size))
2006
David Zeuthen09692692016-09-30 16:16:40 -04002007 return footer, h, descriptors, image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002008
David Zeuthenb1b994d2017-03-06 18:01:31 -05002009 def _load_vbmeta_blob(self, image):
2010 """Gets the vbmeta struct and associated sections.
2011
2012 The image can either be a vbmeta.img or an image with a footer.
2013
2014 Arguments:
2015 image: An ImageHandler (vbmeta or footer).
2016
2017 Returns:
2018 A blob with the vbmeta struct and other sections.
2019 """
2020 assert isinstance(image, ImageHandler)
2021 footer = None
2022 image.seek(image.image_size - AvbFooter.SIZE)
2023 try:
2024 footer = AvbFooter(image.read(AvbFooter.SIZE))
2025 except (LookupError, struct.error):
2026 # Nope, just seek back to the start.
2027 image.seek(0)
2028
2029 vbmeta_offset = 0
2030 if footer:
2031 vbmeta_offset = footer.vbmeta_offset
2032
2033 image.seek(vbmeta_offset)
2034 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2035
2036 image.seek(vbmeta_offset)
2037 data_size = AvbVBMetaHeader.SIZE
2038 data_size += h.authentication_data_block_size
2039 data_size += h.auxiliary_data_block_size
2040 return image.read(data_size)
2041
David Zeuthenfd41eb92016-11-17 12:24:47 -05002042 def _get_cmdline_descriptors_for_dm_verity(self, image):
2043 """Generate kernel cmdline descriptors for dm-verity.
David Zeuthen21e95262016-07-27 17:58:40 -04002044
2045 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002046 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002047
2048 Returns:
David Zeuthenfd41eb92016-11-17 12:24:47 -05002049 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2050 instructions. There is one for when hashtree is not disabled and one for
2051 when it is.
David Zeuthen21e95262016-07-27 17:58:40 -04002052
2053 Raises:
2054 AvbError: If |image| doesn't have a hashtree descriptor.
2055
2056 """
2057
2058 (_, _, descriptors, _) = self._parse_image(image)
2059
2060 ht = None
2061 for desc in descriptors:
2062 if isinstance(desc, AvbHashtreeDescriptor):
2063 ht = desc
2064 break
2065
2066 if not ht:
2067 raise AvbError('No hashtree descriptor in given image')
2068
2069 c = 'dm="1 vroot none ro 1,'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002070 c += '0' # start
2071 c += ' {}'.format((ht.image_size / 512)) # size (# sectors)
2072 c += ' verity {}'.format(ht.dm_verity_version) # type and version
2073 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # data_dev
2074 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # hash_dev
2075 c += ' {}'.format(ht.data_block_size) # data_block
2076 c += ' {}'.format(ht.hash_block_size) # hash_block
2077 c += ' {}'.format(ht.image_size / ht.data_block_size) # #blocks
2078 c += ' {}'.format(ht.image_size / ht.data_block_size) # hash_offset
2079 c += ' {}'.format(ht.hash_algorithm) # hash_alg
2080 c += ' {}'.format(str(ht.root_digest).encode('hex')) # root_digest
2081 c += ' {}'.format(str(ht.salt).encode('hex')) # salt
2082 if ht.fec_num_roots > 0:
David Zeuthena01e32f2017-01-24 17:32:38 -05002083 c += ' 10' # number of optional args
David Zeuthen82218112017-05-08 18:30:41 -04002084 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002085 c += ' ignore_zero_blocks'
2086 c += ' use_fec_from_device PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2087 c += ' fec_roots {}'.format(ht.fec_num_roots)
2088 # Note that fec_blocks is the size that FEC covers, *not* the
2089 # size of the FEC data. Since we use FEC for everything up until
2090 # the FEC data, it's the same as the offset.
2091 c += ' fec_blocks {}'.format(ht.fec_offset/ht.data_block_size)
2092 c += ' fec_start {}'.format(ht.fec_offset/ht.data_block_size)
2093 else:
David Zeuthena01e32f2017-01-24 17:32:38 -05002094 c += ' 2' # number of optional args
David Zeuthen82218112017-05-08 18:30:41 -04002095 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002096 c += ' ignore_zero_blocks'
David Zeuthenfd9c18d2017-03-20 18:19:30 -04002097 c += '" root=/dev/dm-0'
David Zeuthen21e95262016-07-27 17:58:40 -04002098
David Zeuthenfd41eb92016-11-17 12:24:47 -05002099 # Now that we have the command-line, generate the descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002100 desc = AvbKernelCmdlineDescriptor()
2101 desc.kernel_cmdline = c
David Zeuthenfd41eb92016-11-17 12:24:47 -05002102 desc.flags = (
2103 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
2104
2105 # The descriptor for when hashtree verification is disabled is a lot
2106 # simpler - we just set the root to the partition.
2107 desc_no_ht = AvbKernelCmdlineDescriptor()
2108 desc_no_ht.kernel_cmdline = 'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2109 desc_no_ht.flags = (
2110 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED)
2111
2112 return [desc, desc_no_ht]
David Zeuthen21e95262016-07-27 17:58:40 -04002113
2114 def make_vbmeta_image(self, output, chain_partitions, algorithm_name,
David Zeuthen18666ab2016-11-15 11:18:05 -05002115 key_path, public_key_metadata_path, rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002116 flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002117 setup_rootfs_from_kernel,
David Zeuthene3cadca2017-02-22 21:25:46 -05002118 include_descriptors_from_image, signing_helper,
2119 release_string,
2120 append_to_release_string):
David Zeuthen21e95262016-07-27 17:58:40 -04002121 """Implements the 'make_vbmeta_image' command.
2122
2123 Arguments:
2124 output: File to write the image to.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002125 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002126 algorithm_name: Name of algorithm to use.
2127 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002128 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002129 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002130 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002131 props: Properties to insert (list of strings of the form 'key:value').
2132 props_from_file: Properties to insert (list of strings 'key:<path>').
2133 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002134 setup_rootfs_from_kernel: None or file to generate from.
David Zeuthen21e95262016-07-27 17:58:40 -04002135 include_descriptors_from_image: List of file objects with descriptors.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002136 signing_helper: Program which signs a hash and return signature.
David Zeuthene3cadca2017-02-22 21:25:46 -05002137 release_string: None or avbtool release string to use instead of default.
2138 append_to_release_string: None or string to append.
David Zeuthen21e95262016-07-27 17:58:40 -04002139
2140 Raises:
2141 AvbError: If a chained partition is malformed.
2142 """
2143
2144 descriptors = []
David Zeuthen21e95262016-07-27 17:58:40 -04002145 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002146 algorithm_name, key_path, public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002147 chain_partitions, rollback_index, flags, props, props_from_file,
2148 kernel_cmdlines, setup_rootfs_from_kernel,
David Zeuthene3cadca2017-02-22 21:25:46 -05002149 include_descriptors_from_image, signing_helper, release_string,
2150 append_to_release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04002151
2152 # Write entire vbmeta blob (header, authentication, auxiliary).
2153 output.seek(0)
2154 output.write(vbmeta_blob)
2155
David Zeuthen18666ab2016-11-15 11:18:05 -05002156 def _generate_vbmeta_blob(self, algorithm_name, key_path,
2157 public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002158 chain_partitions,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002159 rollback_index, flags, props, props_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04002160 kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002161 setup_rootfs_from_kernel,
David Zeuthene3cadca2017-02-22 21:25:46 -05002162 include_descriptors_from_image, signing_helper,
2163 release_string, append_to_release_string):
David Zeuthen21e95262016-07-27 17:58:40 -04002164 """Generates a VBMeta blob.
2165
2166 This blob contains the header (struct AvbVBMetaHeader), the
2167 authentication data block (which contains the hash and signature
2168 for the header and auxiliary block), and the auxiliary block
2169 (which contains descriptors, the public key used, and other data).
2170
2171 The |key| parameter can |None| only if the |algorithm_name| is
2172 'NONE'.
2173
2174 Arguments:
2175 algorithm_name: The algorithm name as per the ALGORITHMS dict.
2176 key_path: The path to the .pem file used to sign the blob.
David Zeuthen18666ab2016-11-15 11:18:05 -05002177 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002178 descriptors: A list of descriptors to insert or None.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002179 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002180 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002181 flags: Flags to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002182 props: Properties to insert (List of strings of the form 'key:value').
2183 props_from_file: Properties to insert (List of strings 'key:<path>').
2184 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002185 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002186 dm-verity kernel cmdline from.
2187 include_descriptors_from_image: List of file objects for which
2188 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002189 signing_helper: Program which signs a hash and return signature.
David Zeuthene3cadca2017-02-22 21:25:46 -05002190 release_string: None or avbtool release string.
2191 append_to_release_string: None or string to append.
David Zeuthen21e95262016-07-27 17:58:40 -04002192
2193 Returns:
2194 A bytearray() with the VBMeta blob.
2195
2196 Raises:
2197 Exception: If the |algorithm_name| is not found, if no key has
2198 been given and the given algorithm requires one, or the key is
2199 of the wrong size.
2200
2201 """
2202 try:
2203 alg = ALGORITHMS[algorithm_name]
2204 except KeyError:
2205 raise AvbError('Unknown algorithm with name {}'.format(algorithm_name))
2206
David Zeuthena5fd3a42017-02-27 16:38:54 -05002207 if not descriptors:
2208 descriptors = []
2209
2210 # Insert chained partition descriptors, if any
2211 if chain_partitions:
David Zeuthend8e48582017-04-21 11:31:51 -04002212 used_locations = {}
David Zeuthena5fd3a42017-02-27 16:38:54 -05002213 for cp in chain_partitions:
2214 cp_tokens = cp.split(':')
2215 if len(cp_tokens) != 3:
2216 raise AvbError('Malformed chained partition "{}".'.format(cp))
David Zeuthend8e48582017-04-21 11:31:51 -04002217 partition_name = cp_tokens[0]
2218 rollback_index_location = int(cp_tokens[1])
2219 file_path = cp_tokens[2]
2220 # Check that the same rollback location isn't being used by
2221 # multiple chained partitions.
2222 if used_locations.get(rollback_index_location):
2223 raise AvbError('Rollback Index Location {} is already in use.'.format(
2224 rollback_index_location))
2225 used_locations[rollback_index_location] = True
David Zeuthena5fd3a42017-02-27 16:38:54 -05002226 desc = AvbChainPartitionDescriptor()
David Zeuthend8e48582017-04-21 11:31:51 -04002227 desc.partition_name = partition_name
2228 desc.rollback_index_location = rollback_index_location
David Zeuthena5fd3a42017-02-27 16:38:54 -05002229 if desc.rollback_index_location < 1:
2230 raise AvbError('Rollback index location must be 1 or larger.')
David Zeuthena5fd3a42017-02-27 16:38:54 -05002231 desc.public_key = open(file_path, 'rb').read()
2232 descriptors.append(desc)
2233
David Zeuthen21e95262016-07-27 17:58:40 -04002234 # Descriptors.
2235 encoded_descriptors = bytearray()
David Zeuthena5fd3a42017-02-27 16:38:54 -05002236 for desc in descriptors:
2237 encoded_descriptors.extend(desc.encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002238
2239 # Add properties.
2240 if props:
2241 for prop in props:
2242 idx = prop.find(':')
2243 if idx == -1:
2244 raise AvbError('Malformed property "{}".'.format(prop))
2245 desc = AvbPropertyDescriptor()
2246 desc.key = prop[0:idx]
2247 desc.value = prop[(idx + 1):]
2248 encoded_descriptors.extend(desc.encode())
2249 if props_from_file:
2250 for prop in props_from_file:
2251 idx = prop.find(':')
2252 if idx == -1:
2253 raise AvbError('Malformed property "{}".'.format(prop))
2254 desc = AvbPropertyDescriptor()
2255 desc.key = prop[0:idx]
2256 desc.value = prop[(idx + 1):]
2257 file_path = prop[(idx + 1):]
2258 desc.value = open(file_path, 'rb').read()
2259 encoded_descriptors.extend(desc.encode())
2260
2261 # Add AvbKernelCmdline descriptor for dm-verity, if requested.
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002262 if setup_rootfs_from_kernel:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002263 image_handler = ImageHandler(
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002264 setup_rootfs_from_kernel.name)
David Zeuthenfd41eb92016-11-17 12:24:47 -05002265 cmdline_desc = self._get_cmdline_descriptors_for_dm_verity(image_handler)
2266 encoded_descriptors.extend(cmdline_desc[0].encode())
2267 encoded_descriptors.extend(cmdline_desc[1].encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002268
2269 # Add kernel command-lines.
2270 if kernel_cmdlines:
2271 for i in kernel_cmdlines:
2272 desc = AvbKernelCmdlineDescriptor()
2273 desc.kernel_cmdline = i
2274 encoded_descriptors.extend(desc.encode())
2275
2276 # Add descriptors from other images.
2277 if include_descriptors_from_image:
2278 for image in include_descriptors_from_image:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002279 image_handler = ImageHandler(image.name)
2280 (_, _, image_descriptors, _) = self._parse_image(image_handler)
David Zeuthen21e95262016-07-27 17:58:40 -04002281 for desc in image_descriptors:
2282 encoded_descriptors.extend(desc.encode())
2283
David Zeuthen18666ab2016-11-15 11:18:05 -05002284 # Load public key metadata blob, if requested.
2285 pkmd_blob = []
2286 if public_key_metadata_path:
2287 with open(public_key_metadata_path) as f:
2288 pkmd_blob = f.read()
2289
David Zeuthen21e95262016-07-27 17:58:40 -04002290 key = None
2291 encoded_key = bytearray()
2292 if alg.public_key_num_bytes > 0:
2293 if not key_path:
2294 raise AvbError('Key is required for algorithm {}'.format(
2295 algorithm_name))
David Zeuthenc68f0822017-03-31 17:22:35 -04002296 encoded_key = encode_rsa_key(key_path)
David Zeuthen21e95262016-07-27 17:58:40 -04002297 if len(encoded_key) != alg.public_key_num_bytes:
2298 raise AvbError('Key is wrong size for algorithm {}'.format(
2299 algorithm_name))
2300
2301 h = AvbVBMetaHeader()
2302
David Zeuthene3cadca2017-02-22 21:25:46 -05002303 # Override release string, if requested.
2304 if isinstance(release_string, (str, unicode)):
2305 h.release_string = release_string
2306
2307 # Append to release string, if requested. Also insert a space before.
2308 if isinstance(append_to_release_string, (str, unicode)):
2309 h.release_string += ' ' + append_to_release_string
2310
David Zeuthen18666ab2016-11-15 11:18:05 -05002311 # For the Auxiliary data block, descriptors are stored at offset 0,
2312 # followed by the public key, followed by the public key metadata blob.
David Zeuthen21e95262016-07-27 17:58:40 -04002313 h.auxiliary_data_block_size = round_to_multiple(
David Zeuthen18666ab2016-11-15 11:18:05 -05002314 len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64)
David Zeuthen21e95262016-07-27 17:58:40 -04002315 h.descriptors_offset = 0
2316 h.descriptors_size = len(encoded_descriptors)
2317 h.public_key_offset = h.descriptors_size
2318 h.public_key_size = len(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002319 h.public_key_metadata_offset = h.public_key_offset + h.public_key_size
2320 h.public_key_metadata_size = len(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002321
2322 # For the Authentication data block, the hash is first and then
2323 # the signature.
2324 h.authentication_data_block_size = round_to_multiple(
David Zeuthend5db21d2017-01-24 10:11:38 -05002325 alg.hash_num_bytes + alg.signature_num_bytes, 64)
David Zeuthen21e95262016-07-27 17:58:40 -04002326 h.algorithm_type = alg.algorithm_type
2327 h.hash_offset = 0
2328 h.hash_size = alg.hash_num_bytes
2329 # Signature offset and size - it's stored right after the hash
2330 # (in Authentication data block).
2331 h.signature_offset = alg.hash_num_bytes
2332 h.signature_size = alg.signature_num_bytes
2333
2334 h.rollback_index = rollback_index
David Zeuthenfd41eb92016-11-17 12:24:47 -05002335 h.flags = flags
David Zeuthen21e95262016-07-27 17:58:40 -04002336
2337 # Generate Header data block.
2338 header_data_blob = h.encode()
2339
2340 # Generate Auxiliary data block.
2341 aux_data_blob = bytearray()
2342 aux_data_blob.extend(encoded_descriptors)
2343 aux_data_blob.extend(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002344 aux_data_blob.extend(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002345 padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob)
2346 aux_data_blob.extend('\0' * padding_bytes)
2347
2348 # Calculate the hash.
2349 binary_hash = bytearray()
2350 binary_signature = bytearray()
2351 if algorithm_name != 'NONE':
David Zeuthenb623d8b2017-04-04 16:05:53 -04002352 ha = hashlib.new(alg.hash_name)
David Zeuthen21e95262016-07-27 17:58:40 -04002353 ha.update(header_data_blob)
2354 ha.update(aux_data_blob)
2355 binary_hash.extend(ha.digest())
2356
2357 # Calculate the signature.
David Zeuthen21e95262016-07-27 17:58:40 -04002358 padding_and_hash = str(bytearray(alg.padding)) + binary_hash
Esun Kimff44f232017-03-30 10:34:54 +09002359 binary_signature.extend(raw_sign(signing_helper, algorithm_name,
2360 alg.signature_num_bytes, key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08002361 padding_and_hash))
David Zeuthen21e95262016-07-27 17:58:40 -04002362
2363 # Generate Authentication data block.
2364 auth_data_blob = bytearray()
2365 auth_data_blob.extend(binary_hash)
2366 auth_data_blob.extend(binary_signature)
2367 padding_bytes = h.authentication_data_block_size - len(auth_data_blob)
2368 auth_data_blob.extend('\0' * padding_bytes)
2369
2370 return header_data_blob + auth_data_blob + aux_data_blob
2371
2372 def extract_public_key(self, key_path, output):
2373 """Implements the 'extract_public_key' command.
2374
2375 Arguments:
2376 key_path: The path to a RSA private key file.
2377 output: The file to write to.
2378 """
David Zeuthenc68f0822017-03-31 17:22:35 -04002379 output.write(encode_rsa_key(key_path))
David Zeuthen21e95262016-07-27 17:58:40 -04002380
David Zeuthenb1b994d2017-03-06 18:01:31 -05002381 def append_vbmeta_image(self, image_filename, vbmeta_image_filename,
2382 partition_size):
2383 """Implementation of the append_vbmeta_image command.
2384
2385 Arguments:
2386 image_filename: File to add the footer to.
2387 vbmeta_image_filename: File to get vbmeta struct from.
2388 partition_size: Size of partition.
2389
2390 Raises:
2391 AvbError: If an argument is incorrect.
2392 """
2393 image = ImageHandler(image_filename)
2394
2395 if partition_size % image.block_size != 0:
2396 raise AvbError('Partition size of {} is not a multiple of the image '
2397 'block size {}.'.format(partition_size,
2398 image.block_size))
2399
2400 # If there's already a footer, truncate the image to its original
2401 # size. This way 'avbtool append_vbmeta_image' is idempotent.
2402 image.seek(image.image_size - AvbFooter.SIZE)
2403 try:
2404 footer = AvbFooter(image.read(AvbFooter.SIZE))
2405 # Existing footer found. Just truncate.
2406 original_image_size = footer.original_image_size
2407 image.truncate(footer.original_image_size)
2408 except (LookupError, struct.error):
2409 original_image_size = image.image_size
2410
2411 # If anything goes wrong from here-on, restore the image back to
2412 # its original size.
2413 try:
2414 vbmeta_image_handler = ImageHandler(vbmeta_image_filename)
2415 vbmeta_blob = self._load_vbmeta_blob(vbmeta_image_handler)
2416
2417 # If the image isn't sparse, its size might not be a multiple of
2418 # the block size. This will screw up padding later so just grow it.
2419 if image.image_size % image.block_size != 0:
2420 assert not image.is_sparse
2421 padding_needed = image.block_size - (image.image_size%image.block_size)
2422 image.truncate(image.image_size + padding_needed)
2423
2424 # The append_raw() method requires content with size being a
2425 # multiple of |block_size| so add padding as needed. Also record
2426 # where this is written to since we'll need to put that in the
2427 # footer.
2428 vbmeta_offset = image.image_size
2429 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2430 len(vbmeta_blob))
2431 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
2432
2433 # Append vbmeta blob and footer
2434 image.append_raw(vbmeta_blob_with_padding)
2435 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
2436
2437 # Now insert a DONT_CARE chunk with enough bytes such that the
2438 # final Footer block is at the end of partition_size..
2439 image.append_dont_care(partition_size - vbmeta_end_offset -
2440 1*image.block_size)
2441
2442 # Generate the Footer that tells where the VBMeta footer
2443 # is. Also put enough padding in the front of the footer since
2444 # we'll write out an entire block.
2445 footer = AvbFooter()
2446 footer.original_image_size = original_image_size
2447 footer.vbmeta_offset = vbmeta_offset
2448 footer.vbmeta_size = len(vbmeta_blob)
2449 footer_blob = footer.encode()
2450 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2451 footer_blob)
2452 image.append_raw(footer_blob_with_padding)
2453
2454 except:
2455 # Truncate back to original size, then re-raise
2456 image.truncate(original_image_size)
2457 raise
2458
David Zeuthena4fee8b2016-08-22 15:20:43 -04002459 def add_hash_footer(self, image_filename, partition_size, partition_name,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002460 hash_algorithm, salt, chain_partitions, algorithm_name,
2461 key_path,
2462 public_key_metadata_path, rollback_index, flags, props,
David Zeuthen18666ab2016-11-15 11:18:05 -05002463 props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002464 setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05002465 include_descriptors_from_image, signing_helper,
David Zeuthene3cadca2017-02-22 21:25:46 -05002466 release_string, append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05002467 output_vbmeta_image, do_not_append_vbmeta_image):
David Zeuthena4fee8b2016-08-22 15:20:43 -04002468 """Implementation of the add_hash_footer on unsparse images.
David Zeuthen21e95262016-07-27 17:58:40 -04002469
2470 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002471 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04002472 partition_size: Size of partition.
2473 partition_name: Name of partition (without A/B suffix).
2474 hash_algorithm: Hash algorithm to use.
2475 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002476 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04002477 algorithm_name: Name of algorithm to use.
2478 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002479 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002480 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002481 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002482 props: Properties to insert (List of strings of the form 'key:value').
2483 props_from_file: Properties to insert (List of strings 'key:<path>').
2484 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002485 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002486 dm-verity kernel cmdline from.
2487 include_descriptors_from_image: List of file objects for which
2488 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002489 signing_helper: Program which signs a hash and return signature.
David Zeuthene3cadca2017-02-22 21:25:46 -05002490 release_string: None or avbtool release string.
2491 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05002492 output_vbmeta_image: If not None, also write vbmeta struct to this file.
2493 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthena4fee8b2016-08-22 15:20:43 -04002494
2495 Raises:
2496 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04002497 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002498 image = ImageHandler(image_filename)
2499
2500 if partition_size % image.block_size != 0:
2501 raise AvbError('Partition size of {} is not a multiple of the image '
2502 'block size {}.'.format(partition_size,
2503 image.block_size))
2504
David Zeuthen21e95262016-07-27 17:58:40 -04002505 # If there's already a footer, truncate the image to its original
2506 # size. This way 'avbtool add_hash_footer' is idempotent (modulo
2507 # salts).
David Zeuthen09692692016-09-30 16:16:40 -04002508 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002509 try:
2510 footer = AvbFooter(image.read(AvbFooter.SIZE))
2511 # Existing footer found. Just truncate.
2512 original_image_size = footer.original_image_size
David Zeuthen09692692016-09-30 16:16:40 -04002513 image.truncate(footer.original_image_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002514 except (LookupError, struct.error):
David Zeuthen09692692016-09-30 16:16:40 -04002515 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002516
2517 # If anything goes wrong from here-on, restore the image back to
2518 # its original size.
2519 try:
David Zeuthen09692692016-09-30 16:16:40 -04002520 # First, calculate the maximum image size such that an image
2521 # this size + metadata (footer + vbmeta struct) fits in
2522 # |partition_size|.
2523 max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE
2524 max_image_size = partition_size - max_metadata_size
2525
2526 # If image size exceeds the maximum image size, fail.
2527 if image.image_size > max_image_size:
2528 raise AvbError('Image size of {} exceeds maximum image '
2529 'size of {} in order to fit in a partition '
2530 'size of {}.'.format(image.image_size, max_image_size,
2531 partition_size))
2532
David Zeuthen21e95262016-07-27 17:58:40 -04002533 digest_size = len(hashlib.new(name=hash_algorithm).digest())
2534 if salt:
2535 salt = salt.decode('hex')
2536 else:
2537 if salt is None:
2538 # If salt is not explicitly specified, choose a hash
2539 # that's the same size as the hash size.
2540 hash_size = digest_size
2541 salt = open('/dev/urandom').read(hash_size)
2542 else:
2543 salt = ''
2544
2545 hasher = hashlib.new(name=hash_algorithm, string=salt)
2546 # TODO(zeuthen): might want to read this in chunks to avoid
2547 # memory pressure, then again, this is only supposed to be used
2548 # on kernel/initramfs partitions. Possible optimization.
2549 image.seek(0)
David Zeuthen09692692016-09-30 16:16:40 -04002550 hasher.update(image.read(image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002551 digest = hasher.digest()
2552
2553 h_desc = AvbHashDescriptor()
David Zeuthen09692692016-09-30 16:16:40 -04002554 h_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002555 h_desc.hash_algorithm = hash_algorithm
2556 h_desc.partition_name = partition_name
2557 h_desc.salt = salt
2558 h_desc.digest = digest
2559
2560 # Generate the VBMeta footer.
David Zeuthen21e95262016-07-27 17:58:40 -04002561 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002562 algorithm_name, key_path, public_key_metadata_path, [h_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05002563 chain_partitions, rollback_index, flags, props, props_from_file,
2564 kernel_cmdlines, setup_rootfs_from_kernel,
David Zeuthene3cadca2017-02-22 21:25:46 -05002565 include_descriptors_from_image, signing_helper, release_string,
2566 append_to_release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04002567
David Zeuthena4fee8b2016-08-22 15:20:43 -04002568 # If the image isn't sparse, its size might not be a multiple of
2569 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04002570 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002571 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04002572 padding_needed = image.block_size - (image.image_size%image.block_size)
2573 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04002574
David Zeuthena4fee8b2016-08-22 15:20:43 -04002575 # The append_raw() method requires content with size being a
2576 # multiple of |block_size| so add padding as needed. Also record
2577 # where this is written to since we'll need to put that in the
2578 # footer.
David Zeuthen09692692016-09-30 16:16:40 -04002579 vbmeta_offset = image.image_size
David Zeuthena4fee8b2016-08-22 15:20:43 -04002580 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2581 len(vbmeta_blob))
2582 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
David Zeuthena4fee8b2016-08-22 15:20:43 -04002583
David Zeuthend247fcb2017-02-16 12:09:27 -05002584 # Write vbmeta blob, if requested.
2585 if output_vbmeta_image:
2586 output_vbmeta_image.write(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002587
David Zeuthend247fcb2017-02-16 12:09:27 -05002588 # Append vbmeta blob and footer, unless requested not to.
2589 if not do_not_append_vbmeta_image:
2590 image.append_raw(vbmeta_blob_with_padding)
2591 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
2592
2593 # Now insert a DONT_CARE chunk with enough bytes such that the
2594 # final Footer block is at the end of partition_size..
2595 image.append_dont_care(partition_size - vbmeta_end_offset -
2596 1*image.block_size)
2597
2598 # Generate the Footer that tells where the VBMeta footer
2599 # is. Also put enough padding in the front of the footer since
2600 # we'll write out an entire block.
2601 footer = AvbFooter()
2602 footer.original_image_size = original_image_size
2603 footer.vbmeta_offset = vbmeta_offset
2604 footer.vbmeta_size = len(vbmeta_blob)
2605 footer_blob = footer.encode()
2606 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2607 footer_blob)
2608 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002609
David Zeuthen21e95262016-07-27 17:58:40 -04002610 except:
2611 # Truncate back to original size, then re-raise
2612 image.truncate(original_image_size)
2613 raise
2614
David Zeuthena4fee8b2016-08-22 15:20:43 -04002615 def add_hashtree_footer(self, image_filename, partition_size, partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002616 generate_fec, fec_num_roots, hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002617 block_size, salt, chain_partitions, algorithm_name,
2618 key_path,
2619 public_key_metadata_path, rollback_index, flags,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002620 props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002621 setup_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04002622 include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05002623 calc_max_image_size, signing_helper,
David Zeuthene3cadca2017-02-22 21:25:46 -05002624 release_string, append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05002625 output_vbmeta_image, do_not_append_vbmeta_image):
David Zeuthen21e95262016-07-27 17:58:40 -04002626 """Implements the 'add_hashtree_footer' command.
2627
2628 See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
2629 more information about dm-verity and these hashes.
2630
2631 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002632 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04002633 partition_size: Size of partition.
2634 partition_name: Name of partition (without A/B suffix).
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002635 generate_fec: If True, generate FEC codes.
2636 fec_num_roots: Number of roots for FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04002637 hash_algorithm: Hash algorithm to use.
2638 block_size: Block size to use.
2639 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002640 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04002641 algorithm_name: Name of algorithm to use.
2642 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002643 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002644 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002645 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002646 props: Properties to insert (List of strings of the form 'key:value').
2647 props_from_file: Properties to insert (List of strings 'key:<path>').
2648 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002649 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002650 dm-verity kernel cmdline from.
2651 include_descriptors_from_image: List of file objects for which
2652 to insert descriptors from.
David Zeuthen09692692016-09-30 16:16:40 -04002653 calc_max_image_size: Don't store the hashtree or footer - instead
2654 calculate the maximum image size leaving enough room for hashtree
2655 and metadata with the given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002656 signing_helper: Program which signs a hash and return signature.
David Zeuthene3cadca2017-02-22 21:25:46 -05002657 release_string: None or avbtool release string.
2658 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05002659 output_vbmeta_image: If not None, also write vbmeta struct to this file.
2660 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthena4fee8b2016-08-22 15:20:43 -04002661
2662 Raises:
2663 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04002664 """
David Zeuthen09692692016-09-30 16:16:40 -04002665 digest_size = len(hashlib.new(name=hash_algorithm).digest())
2666 digest_padding = round_to_pow2(digest_size) - digest_size
2667
2668 # First, calculate the maximum image size such that an image
2669 # this size + the hashtree + metadata (footer + vbmeta struct)
2670 # fits in |partition_size|. We use very conservative figures for
2671 # metadata.
2672 (_, max_tree_size) = calc_hash_level_offsets(
2673 partition_size, block_size, digest_size + digest_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002674 max_fec_size = 0
2675 if generate_fec:
2676 max_fec_size = calc_fec_data_size(partition_size, fec_num_roots)
2677 max_metadata_size = (max_fec_size + max_tree_size +
2678 self.MAX_VBMETA_SIZE +
David Zeuthen09692692016-09-30 16:16:40 -04002679 self.MAX_FOOTER_SIZE)
2680 max_image_size = partition_size - max_metadata_size
2681
2682 # If we're asked to only calculate the maximum image size, we're done.
2683 if calc_max_image_size:
2684 print '{}'.format(max_image_size)
2685 return
2686
David Zeuthena4fee8b2016-08-22 15:20:43 -04002687 image = ImageHandler(image_filename)
2688
2689 if partition_size % image.block_size != 0:
2690 raise AvbError('Partition size of {} is not a multiple of the image '
2691 'block size {}.'.format(partition_size,
2692 image.block_size))
2693
David Zeuthen21e95262016-07-27 17:58:40 -04002694 # If there's already a footer, truncate the image to its original
2695 # size. This way 'avbtool add_hashtree_footer' is idempotent
2696 # (modulo salts).
David Zeuthen09692692016-09-30 16:16:40 -04002697 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002698 try:
2699 footer = AvbFooter(image.read(AvbFooter.SIZE))
2700 # Existing footer found. Just truncate.
2701 original_image_size = footer.original_image_size
David Zeuthen09692692016-09-30 16:16:40 -04002702 image.truncate(footer.original_image_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002703 except (LookupError, struct.error):
David Zeuthen09692692016-09-30 16:16:40 -04002704 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002705
2706 # If anything goes wrong from here-on, restore the image back to
2707 # its original size.
2708 try:
2709 # Ensure image is multiple of block_size.
David Zeuthen09692692016-09-30 16:16:40 -04002710 rounded_image_size = round_to_multiple(image.image_size, block_size)
2711 if rounded_image_size > image.image_size:
2712 image.append_raw('\0' * (rounded_image_size - image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002713
David Zeuthen09692692016-09-30 16:16:40 -04002714 # If image size exceeds the maximum image size, fail.
2715 if image.image_size > max_image_size:
2716 raise AvbError('Image size of {} exceeds maximum image '
2717 'size of {} in order to fit in a partition '
2718 'size of {}.'.format(image.image_size, max_image_size,
2719 partition_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002720
2721 if salt:
2722 salt = salt.decode('hex')
2723 else:
2724 if salt is None:
2725 # If salt is not explicitly specified, choose a hash
2726 # that's the same size as the hash size.
2727 hash_size = digest_size
2728 salt = open('/dev/urandom').read(hash_size)
2729 else:
2730 salt = ''
2731
David Zeuthena4fee8b2016-08-22 15:20:43 -04002732 # Hashes are stored upside down so we need to calculate hash
David Zeuthen21e95262016-07-27 17:58:40 -04002733 # offsets in advance.
2734 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
David Zeuthen09692692016-09-30 16:16:40 -04002735 image.image_size, block_size, digest_size + digest_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04002736
David Zeuthena4fee8b2016-08-22 15:20:43 -04002737 # If the image isn't sparse, its size might not be a multiple of
2738 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04002739 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002740 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04002741 padding_needed = image.block_size - (image.image_size%image.block_size)
2742 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04002743
David Zeuthena4fee8b2016-08-22 15:20:43 -04002744 # Generate the tree and add padding as needed.
David Zeuthen09692692016-09-30 16:16:40 -04002745 tree_offset = image.image_size
2746 root_digest, hash_tree = generate_hash_tree(image, image.image_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002747 block_size,
2748 hash_algorithm, salt,
2749 digest_padding,
2750 hash_level_offsets,
2751 tree_size)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002752
2753 # Generate HashtreeDescriptor with details about the tree we
2754 # just generated.
David Zeuthen21e95262016-07-27 17:58:40 -04002755 ht_desc = AvbHashtreeDescriptor()
2756 ht_desc.dm_verity_version = 1
David Zeuthen09692692016-09-30 16:16:40 -04002757 ht_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002758 ht_desc.tree_offset = tree_offset
2759 ht_desc.tree_size = tree_size
2760 ht_desc.data_block_size = block_size
2761 ht_desc.hash_block_size = block_size
2762 ht_desc.hash_algorithm = hash_algorithm
2763 ht_desc.partition_name = partition_name
2764 ht_desc.salt = salt
2765 ht_desc.root_digest = root_digest
2766
David Zeuthen09692692016-09-30 16:16:40 -04002767 # Write the hash tree
2768 padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
2769 len(hash_tree))
2770 hash_tree_with_padding = hash_tree + '\0'*padding_needed
2771 image.append_raw(hash_tree_with_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002772 len_hashtree_and_fec = len(hash_tree_with_padding)
2773
2774 # Generate FEC codes, if requested.
2775 if generate_fec:
2776 fec_data = generate_fec_data(image_filename, fec_num_roots)
2777 padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
2778 len(fec_data))
2779 fec_data_with_padding = fec_data + '\0'*padding_needed
2780 fec_offset = image.image_size
2781 image.append_raw(fec_data_with_padding)
2782 len_hashtree_and_fec += len(fec_data_with_padding)
2783 # Update the hashtree descriptor.
2784 ht_desc.fec_num_roots = fec_num_roots
2785 ht_desc.fec_offset = fec_offset
2786 ht_desc.fec_size = len(fec_data)
David Zeuthen09692692016-09-30 16:16:40 -04002787
David Zeuthena4fee8b2016-08-22 15:20:43 -04002788 # Generate the VBMeta footer and add padding as needed.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002789 vbmeta_offset = tree_offset + len_hashtree_and_fec
David Zeuthen21e95262016-07-27 17:58:40 -04002790 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002791 algorithm_name, key_path, public_key_metadata_path, [ht_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05002792 chain_partitions, rollback_index, flags, props, props_from_file,
2793 kernel_cmdlines, setup_rootfs_from_kernel,
David Zeuthene3cadca2017-02-22 21:25:46 -05002794 include_descriptors_from_image, signing_helper, release_string,
2795 append_to_release_string)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002796 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2797 len(vbmeta_blob))
2798 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
David Zeuthen21e95262016-07-27 17:58:40 -04002799
David Zeuthend247fcb2017-02-16 12:09:27 -05002800 # Write vbmeta blob, if requested.
2801 if output_vbmeta_image:
2802 output_vbmeta_image.write(vbmeta_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002803
David Zeuthend247fcb2017-02-16 12:09:27 -05002804 # Append vbmeta blob and footer, unless requested not to.
2805 if not do_not_append_vbmeta_image:
2806 image.append_raw(vbmeta_blob_with_padding)
2807
2808 # Now insert a DONT_CARE chunk with enough bytes such that the
2809 # final Footer block is at the end of partition_size..
2810 image.append_dont_care(partition_size - image.image_size -
2811 1*image.block_size)
2812
2813 # Generate the Footer that tells where the VBMeta footer
2814 # is. Also put enough padding in the front of the footer since
2815 # we'll write out an entire block.
2816 footer = AvbFooter()
2817 footer.original_image_size = original_image_size
2818 footer.vbmeta_offset = vbmeta_offset
2819 footer.vbmeta_size = len(vbmeta_blob)
2820 footer_blob = footer.encode()
2821 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2822 footer_blob)
2823 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002824
David Zeuthen21e95262016-07-27 17:58:40 -04002825 except:
David Zeuthen09692692016-09-30 16:16:40 -04002826 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04002827 image.truncate(original_image_size)
2828 raise
2829
David Zeuthenc68f0822017-03-31 17:22:35 -04002830 def make_atx_certificate(self, output, authority_key_path, subject_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08002831 subject_key_version, subject,
2832 is_intermediate_authority, signing_helper):
2833 """Implements the 'make_atx_certificate' command.
2834
2835 Android Things certificates are required for Android Things public key
2836 metadata. They chain the vbmeta signing key for a particular product back to
2837 a fused, permanent root key. These certificates are fixed-length and fixed-
2838 format with the explicit goal of not parsing ASN.1 in bootloader code.
2839
2840 Arguments:
2841 output: Certificate will be written to this file on success.
2842 authority_key_path: A PEM file path with the authority private key.
2843 If None, then a certificate will be created without a
2844 signature. The signature can be created out-of-band
2845 and appended.
David Zeuthenc68f0822017-03-31 17:22:35 -04002846 subject_key_path: Path to a PEM or DER subject public key.
Darren Krahn147b08d2016-12-20 16:38:29 -08002847 subject_key_version: A 64-bit version value. If this is None, the number
2848 of seconds since the epoch is used.
2849 subject: A subject identifier. For Product Signing Key certificates this
2850 should be the same Product ID found in the permanent attributes.
2851 is_intermediate_authority: True if the certificate is for an intermediate
2852 authority.
2853 signing_helper: Program which signs a hash and returns the signature.
2854 """
2855 signed_data = bytearray()
2856 signed_data.extend(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04002857 signed_data.extend(encode_rsa_key(subject_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08002858 hasher = hashlib.sha256()
2859 hasher.update(subject)
2860 signed_data.extend(hasher.digest())
2861 usage = 'com.google.android.things.vboot'
2862 if is_intermediate_authority:
2863 usage += '.ca'
2864 hasher = hashlib.sha256()
2865 hasher.update(usage)
2866 signed_data.extend(hasher.digest())
2867 if not subject_key_version:
2868 subject_key_version = int(time.time())
2869 signed_data.extend(struct.pack('<Q', subject_key_version))
2870 signature = bytearray()
2871 if authority_key_path:
2872 padding_and_hash = bytearray()
Darren Krahn43e12d82017-02-24 16:26:31 -08002873 algorithm_name = 'SHA512_RSA4096'
Esun Kimff44f232017-03-30 10:34:54 +09002874 alg = ALGORITHMS[algorithm_name]
Darren Krahn43e12d82017-02-24 16:26:31 -08002875 hasher = hashlib.sha512()
Esun Kimff44f232017-03-30 10:34:54 +09002876 padding_and_hash.extend(alg.padding)
Darren Krahn147b08d2016-12-20 16:38:29 -08002877 hasher.update(signed_data)
2878 padding_and_hash.extend(hasher.digest())
2879 signature.extend(raw_sign(signing_helper, algorithm_name,
Esun Kimff44f232017-03-30 10:34:54 +09002880 alg.signature_num_bytes, authority_key_path,
2881 padding_and_hash))
Darren Krahn147b08d2016-12-20 16:38:29 -08002882 output.write(signed_data)
2883 output.write(signature)
2884
David Zeuthenc68f0822017-03-31 17:22:35 -04002885 def make_atx_permanent_attributes(self, output, root_authority_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08002886 product_id):
2887 """Implements the 'make_atx_permanent_attributes' command.
2888
2889 Android Things permanent attributes are designed to be permanent for a
2890 particular product and a hash of these attributes should be fused into
2891 hardware to enforce this.
2892
2893 Arguments:
2894 output: Attributes will be written to this file on success.
David Zeuthenc68f0822017-03-31 17:22:35 -04002895 root_authority_key_path: Path to a PEM or DER public key for
2896 the root authority.
Darren Krahn147b08d2016-12-20 16:38:29 -08002897 product_id: A 16-byte Product ID.
2898
2899 Raises:
2900 AvbError: If an argument is incorrect.
2901 """
Darren Krahn43e12d82017-02-24 16:26:31 -08002902 EXPECTED_PRODUCT_ID_SIZE = 16
2903 if len(product_id) != EXPECTED_PRODUCT_ID_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08002904 raise AvbError('Invalid Product ID length.')
2905 output.write(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04002906 output.write(encode_rsa_key(root_authority_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08002907 output.write(product_id)
2908
2909 def make_atx_metadata(self, output, intermediate_key_certificate,
Darren Krahn43e12d82017-02-24 16:26:31 -08002910 product_key_certificate):
Darren Krahn147b08d2016-12-20 16:38:29 -08002911 """Implements the 'make_atx_metadata' command.
2912
2913 Android Things metadata are included in vbmeta images to facilitate
2914 verification. The output of this command can be used as the
2915 public_key_metadata argument to other commands.
2916
2917 Arguments:
2918 output: Metadata will be written to this file on success.
2919 intermediate_key_certificate: A certificate file as output by
2920 make_atx_certificate with
2921 is_intermediate_authority set to true.
2922 product_key_certificate: A certificate file as output by
2923 make_atx_certificate with
2924 is_intermediate_authority set to false.
Darren Krahn147b08d2016-12-20 16:38:29 -08002925
2926 Raises:
2927 AvbError: If an argument is incorrect.
2928 """
Darren Krahn43e12d82017-02-24 16:26:31 -08002929 EXPECTED_CERTIFICATE_SIZE = 1620
2930 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08002931 raise AvbError('Invalid intermediate key certificate length.')
Darren Krahn43e12d82017-02-24 16:26:31 -08002932 if len(product_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08002933 raise AvbError('Invalid product key certificate length.')
2934 output.write(struct.pack('<I', 1)) # Format Version
2935 output.write(intermediate_key_certificate)
2936 output.write(product_key_certificate)
Darren Krahn147b08d2016-12-20 16:38:29 -08002937
David Zeuthen21e95262016-07-27 17:58:40 -04002938
2939def calc_hash_level_offsets(image_size, block_size, digest_size):
2940 """Calculate the offsets of all the hash-levels in a Merkle-tree.
2941
2942 Arguments:
2943 image_size: The size of the image to calculate a Merkle-tree for.
2944 block_size: The block size, e.g. 4096.
2945 digest_size: The size of each hash, e.g. 32 for SHA-256.
2946
2947 Returns:
2948 A tuple where the first argument is an array of offsets and the
2949 second is size of the tree, in bytes.
2950 """
2951 level_offsets = []
2952 level_sizes = []
2953 tree_size = 0
2954
2955 num_levels = 0
2956 size = image_size
2957 while size > block_size:
2958 num_blocks = (size + block_size - 1) / block_size
2959 level_size = round_to_multiple(num_blocks * digest_size, block_size)
2960
2961 level_sizes.append(level_size)
2962 tree_size += level_size
2963 num_levels += 1
2964
2965 size = level_size
2966
2967 for n in range(0, num_levels):
2968 offset = 0
2969 for m in range(n + 1, num_levels):
2970 offset += level_sizes[m]
2971 level_offsets.append(offset)
2972
David Zeuthena4fee8b2016-08-22 15:20:43 -04002973 return level_offsets, tree_size
David Zeuthen21e95262016-07-27 17:58:40 -04002974
2975
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002976# See system/extras/libfec/include/fec/io.h for these definitions.
2977FEC_FOOTER_FORMAT = '<LLLLLQ32s'
2978FEC_MAGIC = 0xfecfecfe
2979
2980
2981def calc_fec_data_size(image_size, num_roots):
2982 """Calculates how much space FEC data will take.
2983
2984 Args:
2985 image_size: The size of the image.
2986 num_roots: Number of roots.
2987
2988 Returns:
2989 The number of bytes needed for FEC for an image of the given size
2990 and with the requested number of FEC roots.
2991
2992 Raises:
2993 ValueError: If output from the 'fec' tool is invalid.
2994
2995 """
2996 p = subprocess.Popen(
2997 ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)],
2998 stdout=subprocess.PIPE,
2999 stderr=subprocess.PIPE)
3000 (pout, perr) = p.communicate()
3001 retcode = p.wait()
3002 if retcode != 0:
3003 raise ValueError('Error invoking fec: {}'.format(perr))
3004 return int(pout)
3005
3006
3007def generate_fec_data(image_filename, num_roots):
3008 """Generate FEC codes for an image.
3009
3010 Args:
3011 image_filename: The filename of the image.
3012 num_roots: Number of roots.
3013
3014 Returns:
3015 The FEC data blob.
3016
3017 Raises:
3018 ValueError: If output from the 'fec' tool is invalid.
3019 """
3020 fec_tmpfile = tempfile.NamedTemporaryFile()
3021 subprocess.check_call(
3022 ['fec', '--encode', '--roots', str(num_roots), image_filename,
3023 fec_tmpfile.name],
3024 stderr=open(os.devnull))
3025 fec_data = fec_tmpfile.read()
3026 footer_size = struct.calcsize(FEC_FOOTER_FORMAT)
3027 footer_data = fec_data[-footer_size:]
3028 (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT,
3029 footer_data)
3030 if magic != FEC_MAGIC:
3031 raise ValueError('Unexpected magic in FEC footer')
3032 return fec_data[0:fec_size]
3033
3034
David Zeuthen21e95262016-07-27 17:58:40 -04003035def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003036 digest_padding, hash_level_offsets, tree_size):
David Zeuthen21e95262016-07-27 17:58:40 -04003037 """Generates a Merkle-tree for a file.
3038
3039 Args:
3040 image: The image, as a file.
3041 image_size: The size of the image.
3042 block_size: The block size, e.g. 4096.
3043 hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'.
3044 salt: The salt to use.
3045 digest_padding: The padding for each digest.
David Zeuthen21e95262016-07-27 17:58:40 -04003046 hash_level_offsets: The offsets from calc_hash_level_offsets().
David Zeuthena4fee8b2016-08-22 15:20:43 -04003047 tree_size: The size of the tree, in number of bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04003048
3049 Returns:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003050 A tuple where the first element is the top-level hash and the
3051 second element is the hash-tree.
David Zeuthen21e95262016-07-27 17:58:40 -04003052 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04003053 hash_ret = bytearray(tree_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003054 hash_src_offset = 0
3055 hash_src_size = image_size
3056 level_num = 0
3057 while hash_src_size > block_size:
3058 level_output = ''
David Zeuthen21e95262016-07-27 17:58:40 -04003059 remaining = hash_src_size
3060 while remaining > 0:
3061 hasher = hashlib.new(name=hash_alg_name, string=salt)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003062 # Only read from the file for the first level - for subsequent
3063 # levels, access the array we're building.
3064 if level_num == 0:
3065 image.seek(hash_src_offset + hash_src_size - remaining)
3066 data = image.read(min(remaining, block_size))
3067 else:
3068 offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining
3069 data = hash_ret[offset:offset + block_size]
David Zeuthen21e95262016-07-27 17:58:40 -04003070 hasher.update(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003071
3072 remaining -= len(data)
David Zeuthen21e95262016-07-27 17:58:40 -04003073 if len(data) < block_size:
3074 hasher.update('\0' * (block_size - len(data)))
3075 level_output += hasher.digest()
3076 if digest_padding > 0:
3077 level_output += '\0' * digest_padding
3078
3079 padding_needed = (round_to_multiple(
3080 len(level_output), block_size) - len(level_output))
3081 level_output += '\0' * padding_needed
3082
David Zeuthena4fee8b2016-08-22 15:20:43 -04003083 # Copy level-output into resulting tree.
3084 offset = hash_level_offsets[level_num]
3085 hash_ret[offset:offset + len(level_output)] = level_output
David Zeuthen21e95262016-07-27 17:58:40 -04003086
David Zeuthena4fee8b2016-08-22 15:20:43 -04003087 # Continue on to the next level.
David Zeuthen21e95262016-07-27 17:58:40 -04003088 hash_src_size = len(level_output)
David Zeuthen21e95262016-07-27 17:58:40 -04003089 level_num += 1
3090
3091 hasher = hashlib.new(name=hash_alg_name, string=salt)
3092 hasher.update(level_output)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003093 return hasher.digest(), hash_ret
David Zeuthen21e95262016-07-27 17:58:40 -04003094
3095
3096class AvbTool(object):
3097 """Object for avbtool command-line tool."""
3098
3099 def __init__(self):
3100 """Initializer method."""
3101 self.avb = Avb()
3102
3103 def _add_common_args(self, sub_parser):
3104 """Adds arguments used by several sub-commands.
3105
3106 Arguments:
3107 sub_parser: The parser to add arguments to.
3108 """
3109 sub_parser.add_argument('--algorithm',
3110 help='Algorithm to use (default: NONE)',
3111 metavar='ALGORITHM',
3112 default='NONE')
3113 sub_parser.add_argument('--key',
3114 help='Path to RSA private key file',
3115 metavar='KEY',
3116 required=False)
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003117 sub_parser.add_argument('--signing_helper',
3118 help='Path to helper used for signing',
3119 metavar='APP',
3120 default=None,
3121 required=False)
David Zeuthen18666ab2016-11-15 11:18:05 -05003122 sub_parser.add_argument('--public_key_metadata',
3123 help='Path to public key metadata file',
3124 metavar='KEY_METADATA',
3125 required=False)
David Zeuthen21e95262016-07-27 17:58:40 -04003126 sub_parser.add_argument('--rollback_index',
3127 help='Rollback Index',
3128 type=parse_number,
3129 default=0)
David Zeuthene3cadca2017-02-22 21:25:46 -05003130 # This is used internally for unit tests. Do not include in --help output.
3131 sub_parser.add_argument('--internal_release_string',
3132 help=argparse.SUPPRESS)
3133 sub_parser.add_argument('--append_to_release_string',
3134 help='Text to append to release string',
3135 metavar='STR')
David Zeuthen21e95262016-07-27 17:58:40 -04003136 sub_parser.add_argument('--prop',
3137 help='Add property',
3138 metavar='KEY:VALUE',
3139 action='append')
3140 sub_parser.add_argument('--prop_from_file',
3141 help='Add property from file',
3142 metavar='KEY:PATH',
3143 action='append')
3144 sub_parser.add_argument('--kernel_cmdline',
3145 help='Add kernel cmdline',
3146 metavar='CMDLINE',
3147 action='append')
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003148 # TODO(zeuthen): the --setup_rootfs_from_kernel option used to be called
3149 # --generate_dm_verity_cmdline_from_hashtree. Remove support for the latter
3150 # at some future point.
3151 sub_parser.add_argument('--setup_rootfs_from_kernel',
3152 '--generate_dm_verity_cmdline_from_hashtree',
David Zeuthen21e95262016-07-27 17:58:40 -04003153 metavar='IMAGE',
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003154 help='Adds kernel cmdline to set up IMAGE',
David Zeuthen21e95262016-07-27 17:58:40 -04003155 type=argparse.FileType('rb'))
3156 sub_parser.add_argument('--include_descriptors_from_image',
3157 help='Include descriptors from image',
3158 metavar='IMAGE',
3159 action='append',
3160 type=argparse.FileType('rb'))
David Zeuthena5fd3a42017-02-27 16:38:54 -05003161 # These are only allowed from top-level vbmeta and boot-in-lieu-of-vbmeta.
3162 sub_parser.add_argument('--chain_partition',
3163 help='Allow signed integrity-data for partition',
3164 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
3165 action='append')
3166 sub_parser.add_argument('--flags',
3167 help='VBMeta flags',
3168 type=parse_number,
3169 default=0)
3170 sub_parser.add_argument('--set_hashtree_disabled_flag',
3171 help='Set the HASHTREE_DISABLED flag',
3172 action='store_true')
3173
3174 def _fixup_common_args(self, args):
3175 """Common fixups needed by subcommands.
3176
3177 Arguments:
3178 args: Arguments to modify.
3179
3180 Returns:
3181 The modified arguments.
3182 """
3183 if args.set_hashtree_disabled_flag:
3184 args.flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED
3185 return args
David Zeuthen21e95262016-07-27 17:58:40 -04003186
3187 def run(self, argv):
3188 """Command-line processor.
3189
3190 Arguments:
3191 argv: Pass sys.argv from main.
3192 """
3193 parser = argparse.ArgumentParser()
3194 subparsers = parser.add_subparsers(title='subcommands')
3195
3196 sub_parser = subparsers.add_parser('version',
3197 help='Prints version of avbtool.')
3198 sub_parser.set_defaults(func=self.version)
3199
3200 sub_parser = subparsers.add_parser('extract_public_key',
3201 help='Extract public key.')
3202 sub_parser.add_argument('--key',
3203 help='Path to RSA private key file',
3204 required=True)
3205 sub_parser.add_argument('--output',
3206 help='Output file name',
3207 type=argparse.FileType('wb'),
3208 required=True)
3209 sub_parser.set_defaults(func=self.extract_public_key)
3210
3211 sub_parser = subparsers.add_parser('make_vbmeta_image',
3212 help='Makes a vbmeta image.')
3213 sub_parser.add_argument('--output',
3214 help='Output file name',
3215 type=argparse.FileType('wb'),
3216 required=True)
3217 self._add_common_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04003218 sub_parser.set_defaults(func=self.make_vbmeta_image)
3219
3220 sub_parser = subparsers.add_parser('add_hash_footer',
3221 help='Add hashes and footer to image.')
3222 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003223 help='Image to add hashes to',
David Zeuthen21e95262016-07-27 17:58:40 -04003224 type=argparse.FileType('rab+'))
3225 sub_parser.add_argument('--partition_size',
3226 help='Partition size',
3227 type=parse_number,
3228 required=True)
3229 sub_parser.add_argument('--partition_name',
3230 help='Partition name',
3231 required=True)
3232 sub_parser.add_argument('--hash_algorithm',
3233 help='Hash algorithm to use (default: sha256)',
3234 default='sha256')
3235 sub_parser.add_argument('--salt',
3236 help='Salt in hex (default: /dev/urandom)')
David Zeuthend247fcb2017-02-16 12:09:27 -05003237 sub_parser.add_argument('--output_vbmeta_image',
3238 help='Also write vbmeta struct to file',
3239 type=argparse.FileType('wb'))
3240 sub_parser.add_argument('--do_not_append_vbmeta_image',
3241 help=('Do not append vbmeta struct or footer '
3242 'to the image'),
3243 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04003244 self._add_common_args(sub_parser)
3245 sub_parser.set_defaults(func=self.add_hash_footer)
3246
David Zeuthenb1b994d2017-03-06 18:01:31 -05003247 sub_parser = subparsers.add_parser('append_vbmeta_image',
3248 help='Append vbmeta image to image.')
3249 sub_parser.add_argument('--image',
3250 help='Image to append vbmeta blob to',
3251 type=argparse.FileType('rab+'))
3252 sub_parser.add_argument('--partition_size',
3253 help='Partition size',
3254 type=parse_number,
3255 required=True)
3256 sub_parser.add_argument('--vbmeta_image',
3257 help='Image with vbmeta blob to append',
3258 type=argparse.FileType('rb'))
3259 sub_parser.set_defaults(func=self.append_vbmeta_image)
3260
David Zeuthen21e95262016-07-27 17:58:40 -04003261 sub_parser = subparsers.add_parser('add_hashtree_footer',
3262 help='Add hashtree and footer to image.')
3263 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003264 help='Image to add hashtree to',
David Zeuthen21e95262016-07-27 17:58:40 -04003265 type=argparse.FileType('rab+'))
3266 sub_parser.add_argument('--partition_size',
3267 help='Partition size',
3268 type=parse_number,
3269 required=True)
3270 sub_parser.add_argument('--partition_name',
3271 help='Partition name',
David Zeuthen09692692016-09-30 16:16:40 -04003272 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04003273 sub_parser.add_argument('--hash_algorithm',
3274 help='Hash algorithm to use (default: sha1)',
3275 default='sha1')
3276 sub_parser.add_argument('--salt',
3277 help='Salt in hex (default: /dev/urandom)')
3278 sub_parser.add_argument('--block_size',
3279 help='Block size (default: 4096)',
3280 type=parse_number,
3281 default=4096)
David Zeuthen02c550f2017-05-10 17:18:04 -04003282 # TODO(zeuthen): The --generate_fec option was removed when we
3283 # moved to generating FEC by default. To avoid breaking existing
3284 # users needing to transition we simply just print a warning below
3285 # in add_hashtree_footer(). Remove this option and the warning at
3286 # some point in the future.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003287 sub_parser.add_argument('--generate_fec',
David Zeuthen02c550f2017-05-10 17:18:04 -04003288 help=argparse.SUPPRESS,
3289 action='store_true')
3290 sub_parser.add_argument('--do_not_generate_fec',
3291 help='Do not generate forward-error-correction codes',
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003292 action='store_true')
3293 sub_parser.add_argument('--fec_num_roots',
3294 help='Number of roots for FEC (default: 2)',
3295 type=parse_number,
3296 default=2)
David Zeuthen09692692016-09-30 16:16:40 -04003297 sub_parser.add_argument('--calc_max_image_size',
3298 help=('Don\'t store the hashtree or footer - '
3299 'instead calculate the maximum image size '
3300 'leaving enough room for hashtree '
3301 'and metadata with the given partition '
3302 'size.'),
3303 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05003304 sub_parser.add_argument('--output_vbmeta_image',
3305 help='Also write vbmeta struct to file',
3306 type=argparse.FileType('wb'))
3307 sub_parser.add_argument('--do_not_append_vbmeta_image',
3308 help=('Do not append vbmeta struct or footer '
3309 'to the image'),
3310 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04003311 self._add_common_args(sub_parser)
3312 sub_parser.set_defaults(func=self.add_hashtree_footer)
3313
3314 sub_parser = subparsers.add_parser('erase_footer',
3315 help='Erase footer from an image.')
3316 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003317 help='Image with a footer',
David Zeuthen21e95262016-07-27 17:58:40 -04003318 type=argparse.FileType('rwb+'),
3319 required=True)
3320 sub_parser.add_argument('--keep_hashtree',
David Zeuthenfbb61fa2017-02-02 12:11:49 -05003321 help='Keep the hashtree and FEC in the image',
David Zeuthen21e95262016-07-27 17:58:40 -04003322 action='store_true')
3323 sub_parser.set_defaults(func=self.erase_footer)
3324
David Zeuthen2bc232b2017-04-19 14:25:19 -04003325 sub_parser = subparsers.add_parser('resize_image',
3326 help='Resize image with a footer.')
3327 sub_parser.add_argument('--image',
3328 help='Image with a footer',
3329 type=argparse.FileType('rwb+'),
3330 required=True)
3331 sub_parser.add_argument('--partition_size',
3332 help='New partition size',
3333 type=parse_number)
3334 sub_parser.set_defaults(func=self.resize_image)
3335
David Zeuthen21e95262016-07-27 17:58:40 -04003336 sub_parser = subparsers.add_parser(
3337 'info_image',
3338 help='Show information about vbmeta or footer.')
3339 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003340 help='Image to show information about',
David Zeuthen21e95262016-07-27 17:58:40 -04003341 type=argparse.FileType('rb'),
3342 required=True)
3343 sub_parser.add_argument('--output',
3344 help='Write info to file',
3345 type=argparse.FileType('wt'),
3346 default=sys.stdout)
3347 sub_parser.set_defaults(func=self.info_image)
3348
David Zeuthenb623d8b2017-04-04 16:05:53 -04003349 sub_parser = subparsers.add_parser(
3350 'verify_image',
3351 help='Verify an image.')
3352 sub_parser.add_argument('--image',
3353 help='Image to verify',
3354 type=argparse.FileType('rb'),
3355 required=True)
3356 sub_parser.set_defaults(func=self.verify_image)
3357
David Zeuthen8b6973b2016-09-20 12:39:49 -04003358 sub_parser = subparsers.add_parser('set_ab_metadata',
3359 help='Set A/B metadata.')
3360 sub_parser.add_argument('--misc_image',
3361 help=('The misc image to modify. If the image does '
3362 'not exist, it will be created.'),
3363 type=argparse.FileType('r+b'),
3364 required=True)
3365 sub_parser.add_argument('--slot_data',
3366 help=('Slot data of the form "priority", '
3367 '"tries_remaining", "sucessful_boot" for '
3368 'slot A followed by the same for slot B, '
3369 'separated by colons. The default value '
3370 'is 15:7:0:14:7:0.'),
3371 default='15:7:0:14:7:0')
3372 sub_parser.set_defaults(func=self.set_ab_metadata)
3373
Darren Krahn147b08d2016-12-20 16:38:29 -08003374 sub_parser = subparsers.add_parser(
3375 'make_atx_certificate',
3376 help='Create an Android Things eXtension (ATX) certificate.')
3377 sub_parser.add_argument('--output',
3378 help='Write certificate to file',
3379 type=argparse.FileType('wb'),
3380 default=sys.stdout)
3381 sub_parser.add_argument('--subject',
3382 help=('Path to subject file'),
3383 type=argparse.FileType('rb'),
3384 required=True)
3385 sub_parser.add_argument('--subject_key',
3386 help=('Path to subject RSA public key file'),
3387 type=argparse.FileType('rb'),
3388 required=True)
3389 sub_parser.add_argument('--subject_key_version',
3390 help=('Version of the subject key'),
3391 type=parse_number,
3392 required=False)
3393 sub_parser.add_argument('--subject_is_intermediate_authority',
3394 help=('Generate an intermediate authority '
3395 'certificate'),
3396 action='store_true')
3397 sub_parser.add_argument('--authority_key',
3398 help='Path to authority RSA private key file',
3399 required=False)
3400 sub_parser.add_argument('--signing_helper',
3401 help='Path to helper used for signing',
3402 metavar='APP',
3403 default=None,
3404 required=False)
3405 sub_parser.set_defaults(func=self.make_atx_certificate)
3406
3407 sub_parser = subparsers.add_parser(
3408 'make_atx_permanent_attributes',
3409 help='Create Android Things eXtension (ATX) permanent attributes.')
3410 sub_parser.add_argument('--output',
3411 help='Write attributes to file',
3412 type=argparse.FileType('wb'),
3413 default=sys.stdout)
3414 sub_parser.add_argument('--root_authority_key',
3415 help='Path to authority RSA public key file',
3416 type=argparse.FileType('rb'),
3417 required=True)
3418 sub_parser.add_argument('--product_id',
3419 help=('Path to Product ID file'),
3420 type=argparse.FileType('rb'),
3421 required=True)
3422 sub_parser.set_defaults(func=self.make_atx_permanent_attributes)
3423
3424 sub_parser = subparsers.add_parser(
3425 'make_atx_metadata',
3426 help='Create Android Things eXtension (ATX) metadata.')
3427 sub_parser.add_argument('--output',
3428 help='Write metadata to file',
3429 type=argparse.FileType('wb'),
3430 default=sys.stdout)
3431 sub_parser.add_argument('--intermediate_key_certificate',
3432 help='Path to intermediate key certificate file',
3433 type=argparse.FileType('rb'),
3434 required=True)
3435 sub_parser.add_argument('--product_key_certificate',
3436 help='Path to product key certificate file',
3437 type=argparse.FileType('rb'),
3438 required=True)
Darren Krahn147b08d2016-12-20 16:38:29 -08003439 sub_parser.set_defaults(func=self.make_atx_metadata)
3440
David Zeuthen21e95262016-07-27 17:58:40 -04003441 args = parser.parse_args(argv[1:])
3442 try:
3443 args.func(args)
3444 except AvbError as e:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003445 sys.stderr.write('{}: {}\n'.format(argv[0], e.message))
David Zeuthen21e95262016-07-27 17:58:40 -04003446 sys.exit(1)
3447
3448 def version(self, _):
3449 """Implements the 'version' sub-command."""
David Zeuthene3cadca2017-02-22 21:25:46 -05003450 print get_release_string()
David Zeuthen21e95262016-07-27 17:58:40 -04003451
3452 def extract_public_key(self, args):
3453 """Implements the 'extract_public_key' sub-command."""
3454 self.avb.extract_public_key(args.key, args.output)
3455
3456 def make_vbmeta_image(self, args):
3457 """Implements the 'make_vbmeta_image' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05003458 args = self._fixup_common_args(args)
David Zeuthen21e95262016-07-27 17:58:40 -04003459 self.avb.make_vbmeta_image(args.output, args.chain_partition,
David Zeuthen18666ab2016-11-15 11:18:05 -05003460 args.algorithm, args.key,
3461 args.public_key_metadata, args.rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003462 args.flags, args.prop, args.prop_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04003463 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003464 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05003465 args.include_descriptors_from_image,
David Zeuthene3cadca2017-02-22 21:25:46 -05003466 args.signing_helper,
3467 args.internal_release_string,
3468 args.append_to_release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04003469
David Zeuthenb1b994d2017-03-06 18:01:31 -05003470 def append_vbmeta_image(self, args):
3471 """Implements the 'append_vbmeta_image' sub-command."""
3472 self.avb.append_vbmeta_image(args.image.name, args.vbmeta_image.name,
3473 args.partition_size)
3474
David Zeuthen21e95262016-07-27 17:58:40 -04003475 def add_hash_footer(self, args):
3476 """Implements the 'add_hash_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05003477 args = self._fixup_common_args(args)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003478 self.avb.add_hash_footer(args.image.name, args.partition_size,
David Zeuthen21e95262016-07-27 17:58:40 -04003479 args.partition_name, args.hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003480 args.salt, args.chain_partition, args.algorithm,
3481 args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05003482 args.public_key_metadata, args.rollback_index,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003483 args.flags, args.prop, args.prop_from_file,
David Zeuthen18666ab2016-11-15 11:18:05 -05003484 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003485 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05003486 args.include_descriptors_from_image,
3487 args.signing_helper,
David Zeuthene3cadca2017-02-22 21:25:46 -05003488 args.internal_release_string,
3489 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05003490 args.output_vbmeta_image,
3491 args.do_not_append_vbmeta_image)
David Zeuthen21e95262016-07-27 17:58:40 -04003492
3493 def add_hashtree_footer(self, args):
3494 """Implements the 'add_hashtree_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05003495 args = self._fixup_common_args(args)
David Zeuthen02c550f2017-05-10 17:18:04 -04003496 # TODO(zeuthen): Remove when removing support for the
3497 # '--generate_fec' option above.
3498 if args.generate_fec:
3499 sys.stderr.write('The --generate_fec option is deprecated since FEC '
3500 'is now generated by default. Use the option '
3501 '--do_not_generate_fec to not generate FEC.\n')
David Zeuthen09692692016-09-30 16:16:40 -04003502 self.avb.add_hashtree_footer(args.image.name if args.image else None,
3503 args.partition_size,
3504 args.partition_name,
David Zeuthen02c550f2017-05-10 17:18:04 -04003505 not args.do_not_generate_fec, args.fec_num_roots,
David Zeuthen09692692016-09-30 16:16:40 -04003506 args.hash_algorithm, args.block_size,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003507 args.salt, args.chain_partition, args.algorithm,
3508 args.key, args.public_key_metadata,
3509 args.rollback_index, args.flags, args.prop,
David Zeuthen09692692016-09-30 16:16:40 -04003510 args.prop_from_file,
3511 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003512 args.setup_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04003513 args.include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05003514 args.calc_max_image_size, args.signing_helper,
David Zeuthene3cadca2017-02-22 21:25:46 -05003515 args.internal_release_string,
3516 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05003517 args.output_vbmeta_image,
3518 args.do_not_append_vbmeta_image)
3519
David Zeuthen21e95262016-07-27 17:58:40 -04003520 def erase_footer(self, args):
3521 """Implements the 'erase_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04003522 self.avb.erase_footer(args.image.name, args.keep_hashtree)
David Zeuthen21e95262016-07-27 17:58:40 -04003523
David Zeuthen2bc232b2017-04-19 14:25:19 -04003524 def resize_image(self, args):
3525 """Implements the 'resize_image' sub-command."""
3526 self.avb.resize_image(args.image.name, args.partition_size)
3527
David Zeuthen8b6973b2016-09-20 12:39:49 -04003528 def set_ab_metadata(self, args):
3529 """Implements the 'set_ab_metadata' sub-command."""
3530 self.avb.set_ab_metadata(args.misc_image, args.slot_data)
3531
David Zeuthen21e95262016-07-27 17:58:40 -04003532 def info_image(self, args):
3533 """Implements the 'info_image' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04003534 self.avb.info_image(args.image.name, args.output)
David Zeuthen21e95262016-07-27 17:58:40 -04003535
David Zeuthenb623d8b2017-04-04 16:05:53 -04003536 def verify_image(self, args):
3537 """Implements the 'verify_image' sub-command."""
3538 self.avb.verify_image(args.image.name)
3539
Darren Krahn147b08d2016-12-20 16:38:29 -08003540 def make_atx_certificate(self, args):
3541 """Implements the 'make_atx_certificate' sub-command."""
3542 self.avb.make_atx_certificate(args.output, args.authority_key,
David Zeuthenc68f0822017-03-31 17:22:35 -04003543 args.subject_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08003544 args.subject_key_version,
3545 args.subject.read(),
3546 args.subject_is_intermediate_authority,
3547 args.signing_helper)
3548
3549 def make_atx_permanent_attributes(self, args):
3550 """Implements the 'make_atx_permanent_attributes' sub-command."""
3551 self.avb.make_atx_permanent_attributes(args.output,
David Zeuthenc68f0822017-03-31 17:22:35 -04003552 args.root_authority_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08003553 args.product_id.read())
3554
3555 def make_atx_metadata(self, args):
3556 """Implements the 'make_atx_metadata' sub-command."""
3557 self.avb.make_atx_metadata(args.output,
3558 args.intermediate_key_certificate.read(),
Darren Krahn43e12d82017-02-24 16:26:31 -08003559 args.product_key_certificate.read())
Darren Krahn147b08d2016-12-20 16:38:29 -08003560
David Zeuthen21e95262016-07-27 17:58:40 -04003561
3562if __name__ == '__main__':
3563 tool = AvbTool()
3564 tool.run(sys.argv)