blob: 574139c97717253563335d17c6e844473c7a1ec0 [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 Zeuthen8b6973b2016-09-20 12:39:49 -04001817 def set_ab_metadata(self, misc_image, slot_data):
1818 """Implements the 'set_ab_metadata' command.
1819
1820 The |slot_data| argument must be of the form 'A_priority:A_tries_remaining:
1821 A_successful_boot:B_priority:B_tries_remaining:B_successful_boot'.
1822
1823 Arguments:
1824 misc_image: The misc image to write to.
1825 slot_data: Slot data as a string
1826
1827 Raises:
1828 AvbError: If slot data is malformed.
1829 """
1830 tokens = slot_data.split(':')
1831 if len(tokens) != 6:
1832 raise AvbError('Malformed slot data "{}".'.format(slot_data))
1833 a_priority = int(tokens[0])
1834 a_tries_remaining = int(tokens[1])
1835 a_success = True if int(tokens[2]) != 0 else False
1836 b_priority = int(tokens[3])
1837 b_tries_remaining = int(tokens[4])
1838 b_success = True if int(tokens[5]) != 0 else False
1839
1840 ab_data_no_crc = struct.pack(self.AB_FORMAT_NO_CRC,
1841 self.AB_MAGIC,
1842 self.AB_MAJOR_VERSION, self.AB_MINOR_VERSION,
1843 a_priority, a_tries_remaining, a_success,
1844 b_priority, b_tries_remaining, b_success)
1845 # Force CRC to be unsigned, see https://bugs.python.org/issue4903 for why.
1846 crc_value = binascii.crc32(ab_data_no_crc) & 0xffffffff
1847 ab_data = ab_data_no_crc + struct.pack('!I', crc_value)
1848 misc_image.seek(self.AB_MISC_METADATA_OFFSET)
1849 misc_image.write(ab_data)
1850
David Zeuthena4fee8b2016-08-22 15:20:43 -04001851 def info_image(self, image_filename, output):
David Zeuthen21e95262016-07-27 17:58:40 -04001852 """Implements the 'info_image' command.
1853
1854 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001855 image_filename: Image file to get information from (file object).
David Zeuthen21e95262016-07-27 17:58:40 -04001856 output: Output file to write human-readable information to (file object).
1857 """
1858
David Zeuthena4fee8b2016-08-22 15:20:43 -04001859 image = ImageHandler(image_filename)
1860
David Zeuthen21e95262016-07-27 17:58:40 -04001861 o = output
1862
1863 (footer, header, descriptors, image_size) = self._parse_image(image)
1864
1865 if footer:
1866 o.write('Footer version: {}.{}\n'.format(footer.version_major,
1867 footer.version_minor))
1868 o.write('Image size: {} bytes\n'.format(image_size))
1869 o.write('Original image size: {} bytes\n'.format(
1870 footer.original_image_size))
1871 o.write('VBMeta offset: {}\n'.format(footer.vbmeta_offset))
1872 o.write('VBMeta size: {} bytes\n'.format(footer.vbmeta_size))
1873 o.write('--\n')
1874
1875 (alg_name, _) = lookup_algorithm_by_type(header.algorithm_type)
1876
David Zeuthene3cadca2017-02-22 21:25:46 -05001877 o.write('Minimum libavb version: {}.{}{}\n'.format(
1878 header.required_libavb_version_major,
1879 header.required_libavb_version_minor,
David Zeuthena4fee8b2016-08-22 15:20:43 -04001880 ' (Sparse)' if image.is_sparse else ''))
David Zeuthen21e95262016-07-27 17:58:40 -04001881 o.write('Header Block: {} bytes\n'.format(AvbVBMetaHeader.SIZE))
1882 o.write('Authentication Block: {} bytes\n'.format(
1883 header.authentication_data_block_size))
1884 o.write('Auxiliary Block: {} bytes\n'.format(
1885 header.auxiliary_data_block_size))
1886 o.write('Algorithm: {}\n'.format(alg_name))
1887 o.write('Rollback Index: {}\n'.format(header.rollback_index))
David Zeuthenfd41eb92016-11-17 12:24:47 -05001888 o.write('Flags: {}\n'.format(header.flags))
David Zeuthene3cadca2017-02-22 21:25:46 -05001889 o.write('Release String: \'{}\'\n'.format(
1890 header.release_string.rstrip('\0')))
David Zeuthen21e95262016-07-27 17:58:40 -04001891
1892 # Print descriptors.
1893 num_printed = 0
1894 o.write('Descriptors:\n')
1895 for desc in descriptors:
1896 desc.print_desc(o)
1897 num_printed += 1
1898 if num_printed == 0:
1899 o.write(' (none)\n')
1900
David Zeuthenb623d8b2017-04-04 16:05:53 -04001901 def verify_image(self, image_filename):
1902 """Implements the 'verify_image' command.
1903
1904 Arguments:
1905 image_filename: Image file to get information from (file object).
1906 """
1907
1908 image = ImageHandler(image_filename)
1909 (footer, header, descriptors, image_size) = self._parse_image(image)
1910 offset = 0
1911 if footer:
1912 offset = footer.vbmeta_offset
1913 size = (header.SIZE + header.authentication_data_block_size +
1914 header.auxiliary_data_block_size)
1915 image.seek(offset)
1916 vbmeta_blob = image.read(size)
1917 if not verify_vbmeta_signature(header, vbmeta_blob):
1918 raise AvbError('Signature check failed.')
1919
David Zeuthen21e95262016-07-27 17:58:40 -04001920 def _parse_image(self, image):
1921 """Gets information about an image.
1922
1923 The image can either be a vbmeta or an image with a footer.
1924
1925 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001926 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04001927
1928 Returns:
1929 A tuple where the first argument is a AvbFooter (None if there
1930 is no footer on the image), the second argument is a
1931 AvbVBMetaHeader, the third argument is a list of
1932 AvbDescriptor-derived instances, and the fourth argument is the
1933 size of |image|.
1934 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04001935 assert isinstance(image, ImageHandler)
David Zeuthen21e95262016-07-27 17:58:40 -04001936 footer = None
David Zeuthen09692692016-09-30 16:16:40 -04001937 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04001938 try:
1939 footer = AvbFooter(image.read(AvbFooter.SIZE))
1940 except (LookupError, struct.error):
1941 # Nope, just seek back to the start.
1942 image.seek(0)
1943
1944 vbmeta_offset = 0
1945 if footer:
1946 vbmeta_offset = footer.vbmeta_offset
1947
1948 image.seek(vbmeta_offset)
1949 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
1950
1951 auth_block_offset = vbmeta_offset + AvbVBMetaHeader.SIZE
1952 aux_block_offset = auth_block_offset + h.authentication_data_block_size
1953 desc_start_offset = aux_block_offset + h.descriptors_offset
1954 image.seek(desc_start_offset)
1955 descriptors = parse_descriptors(image.read(h.descriptors_size))
1956
David Zeuthen09692692016-09-30 16:16:40 -04001957 return footer, h, descriptors, image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04001958
David Zeuthenb1b994d2017-03-06 18:01:31 -05001959 def _load_vbmeta_blob(self, image):
1960 """Gets the vbmeta struct and associated sections.
1961
1962 The image can either be a vbmeta.img or an image with a footer.
1963
1964 Arguments:
1965 image: An ImageHandler (vbmeta or footer).
1966
1967 Returns:
1968 A blob with the vbmeta struct and other sections.
1969 """
1970 assert isinstance(image, ImageHandler)
1971 footer = None
1972 image.seek(image.image_size - AvbFooter.SIZE)
1973 try:
1974 footer = AvbFooter(image.read(AvbFooter.SIZE))
1975 except (LookupError, struct.error):
1976 # Nope, just seek back to the start.
1977 image.seek(0)
1978
1979 vbmeta_offset = 0
1980 if footer:
1981 vbmeta_offset = footer.vbmeta_offset
1982
1983 image.seek(vbmeta_offset)
1984 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
1985
1986 image.seek(vbmeta_offset)
1987 data_size = AvbVBMetaHeader.SIZE
1988 data_size += h.authentication_data_block_size
1989 data_size += h.auxiliary_data_block_size
1990 return image.read(data_size)
1991
David Zeuthenfd41eb92016-11-17 12:24:47 -05001992 def _get_cmdline_descriptors_for_dm_verity(self, image):
1993 """Generate kernel cmdline descriptors for dm-verity.
David Zeuthen21e95262016-07-27 17:58:40 -04001994
1995 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04001996 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04001997
1998 Returns:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001999 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2000 instructions. There is one for when hashtree is not disabled and one for
2001 when it is.
David Zeuthen21e95262016-07-27 17:58:40 -04002002
2003 Raises:
2004 AvbError: If |image| doesn't have a hashtree descriptor.
2005
2006 """
2007
2008 (_, _, descriptors, _) = self._parse_image(image)
2009
2010 ht = None
2011 for desc in descriptors:
2012 if isinstance(desc, AvbHashtreeDescriptor):
2013 ht = desc
2014 break
2015
2016 if not ht:
2017 raise AvbError('No hashtree descriptor in given image')
2018
2019 c = 'dm="1 vroot none ro 1,'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002020 c += '0' # start
2021 c += ' {}'.format((ht.image_size / 512)) # size (# sectors)
2022 c += ' verity {}'.format(ht.dm_verity_version) # type and version
2023 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # data_dev
2024 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # hash_dev
2025 c += ' {}'.format(ht.data_block_size) # data_block
2026 c += ' {}'.format(ht.hash_block_size) # hash_block
2027 c += ' {}'.format(ht.image_size / ht.data_block_size) # #blocks
2028 c += ' {}'.format(ht.image_size / ht.data_block_size) # hash_offset
2029 c += ' {}'.format(ht.hash_algorithm) # hash_alg
2030 c += ' {}'.format(str(ht.root_digest).encode('hex')) # root_digest
2031 c += ' {}'.format(str(ht.salt).encode('hex')) # salt
2032 if ht.fec_num_roots > 0:
David Zeuthena01e32f2017-01-24 17:32:38 -05002033 c += ' 10' # number of optional args
2034 c += ' restart_on_corruption'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002035 c += ' ignore_zero_blocks'
2036 c += ' use_fec_from_device PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2037 c += ' fec_roots {}'.format(ht.fec_num_roots)
2038 # Note that fec_blocks is the size that FEC covers, *not* the
2039 # size of the FEC data. Since we use FEC for everything up until
2040 # the FEC data, it's the same as the offset.
2041 c += ' fec_blocks {}'.format(ht.fec_offset/ht.data_block_size)
2042 c += ' fec_start {}'.format(ht.fec_offset/ht.data_block_size)
2043 else:
David Zeuthena01e32f2017-01-24 17:32:38 -05002044 c += ' 2' # number of optional args
2045 c += ' restart_on_corruption'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002046 c += ' ignore_zero_blocks'
David Zeuthenfd9c18d2017-03-20 18:19:30 -04002047 c += '" root=/dev/dm-0'
David Zeuthen21e95262016-07-27 17:58:40 -04002048
David Zeuthenfd41eb92016-11-17 12:24:47 -05002049 # Now that we have the command-line, generate the descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002050 desc = AvbKernelCmdlineDescriptor()
2051 desc.kernel_cmdline = c
David Zeuthenfd41eb92016-11-17 12:24:47 -05002052 desc.flags = (
2053 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
2054
2055 # The descriptor for when hashtree verification is disabled is a lot
2056 # simpler - we just set the root to the partition.
2057 desc_no_ht = AvbKernelCmdlineDescriptor()
2058 desc_no_ht.kernel_cmdline = 'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2059 desc_no_ht.flags = (
2060 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED)
2061
2062 return [desc, desc_no_ht]
David Zeuthen21e95262016-07-27 17:58:40 -04002063
2064 def make_vbmeta_image(self, output, chain_partitions, algorithm_name,
David Zeuthen18666ab2016-11-15 11:18:05 -05002065 key_path, public_key_metadata_path, rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002066 flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002067 setup_rootfs_from_kernel,
David Zeuthene3cadca2017-02-22 21:25:46 -05002068 include_descriptors_from_image, signing_helper,
2069 release_string,
2070 append_to_release_string):
David Zeuthen21e95262016-07-27 17:58:40 -04002071 """Implements the 'make_vbmeta_image' command.
2072
2073 Arguments:
2074 output: File to write the image to.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002075 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002076 algorithm_name: Name of algorithm to use.
2077 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002078 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002079 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002080 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002081 props: Properties to insert (list of strings of the form 'key:value').
2082 props_from_file: Properties to insert (list of strings 'key:<path>').
2083 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002084 setup_rootfs_from_kernel: None or file to generate from.
David Zeuthen21e95262016-07-27 17:58:40 -04002085 include_descriptors_from_image: List of file objects with descriptors.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002086 signing_helper: Program which signs a hash and return signature.
David Zeuthene3cadca2017-02-22 21:25:46 -05002087 release_string: None or avbtool release string to use instead of default.
2088 append_to_release_string: None or string to append.
David Zeuthen21e95262016-07-27 17:58:40 -04002089
2090 Raises:
2091 AvbError: If a chained partition is malformed.
2092 """
2093
2094 descriptors = []
David Zeuthen21e95262016-07-27 17:58:40 -04002095 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002096 algorithm_name, key_path, public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002097 chain_partitions, rollback_index, flags, props, props_from_file,
2098 kernel_cmdlines, setup_rootfs_from_kernel,
David Zeuthene3cadca2017-02-22 21:25:46 -05002099 include_descriptors_from_image, signing_helper, release_string,
2100 append_to_release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04002101
2102 # Write entire vbmeta blob (header, authentication, auxiliary).
2103 output.seek(0)
2104 output.write(vbmeta_blob)
2105
David Zeuthen18666ab2016-11-15 11:18:05 -05002106 def _generate_vbmeta_blob(self, algorithm_name, key_path,
2107 public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002108 chain_partitions,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002109 rollback_index, flags, props, props_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04002110 kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002111 setup_rootfs_from_kernel,
David Zeuthene3cadca2017-02-22 21:25:46 -05002112 include_descriptors_from_image, signing_helper,
2113 release_string, append_to_release_string):
David Zeuthen21e95262016-07-27 17:58:40 -04002114 """Generates a VBMeta blob.
2115
2116 This blob contains the header (struct AvbVBMetaHeader), the
2117 authentication data block (which contains the hash and signature
2118 for the header and auxiliary block), and the auxiliary block
2119 (which contains descriptors, the public key used, and other data).
2120
2121 The |key| parameter can |None| only if the |algorithm_name| is
2122 'NONE'.
2123
2124 Arguments:
2125 algorithm_name: The algorithm name as per the ALGORITHMS dict.
2126 key_path: The path to the .pem file used to sign the blob.
David Zeuthen18666ab2016-11-15 11:18:05 -05002127 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002128 descriptors: A list of descriptors to insert or None.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002129 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002130 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002131 flags: Flags to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002132 props: Properties to insert (List of strings of the form 'key:value').
2133 props_from_file: Properties to insert (List of strings 'key:<path>').
2134 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002135 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002136 dm-verity kernel cmdline from.
2137 include_descriptors_from_image: List of file objects for which
2138 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002139 signing_helper: Program which signs a hash and return signature.
David Zeuthene3cadca2017-02-22 21:25:46 -05002140 release_string: None or avbtool release string.
2141 append_to_release_string: None or string to append.
David Zeuthen21e95262016-07-27 17:58:40 -04002142
2143 Returns:
2144 A bytearray() with the VBMeta blob.
2145
2146 Raises:
2147 Exception: If the |algorithm_name| is not found, if no key has
2148 been given and the given algorithm requires one, or the key is
2149 of the wrong size.
2150
2151 """
2152 try:
2153 alg = ALGORITHMS[algorithm_name]
2154 except KeyError:
2155 raise AvbError('Unknown algorithm with name {}'.format(algorithm_name))
2156
David Zeuthena5fd3a42017-02-27 16:38:54 -05002157 if not descriptors:
2158 descriptors = []
2159
2160 # Insert chained partition descriptors, if any
2161 if chain_partitions:
2162 for cp in chain_partitions:
2163 cp_tokens = cp.split(':')
2164 if len(cp_tokens) != 3:
2165 raise AvbError('Malformed chained partition "{}".'.format(cp))
2166 desc = AvbChainPartitionDescriptor()
2167 desc.partition_name = cp_tokens[0]
2168 desc.rollback_index_location = int(cp_tokens[1])
2169 if desc.rollback_index_location < 1:
2170 raise AvbError('Rollback index location must be 1 or larger.')
2171 file_path = cp_tokens[2]
2172 desc.public_key = open(file_path, 'rb').read()
2173 descriptors.append(desc)
2174
David Zeuthen21e95262016-07-27 17:58:40 -04002175 # Descriptors.
2176 encoded_descriptors = bytearray()
David Zeuthena5fd3a42017-02-27 16:38:54 -05002177 for desc in descriptors:
2178 encoded_descriptors.extend(desc.encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002179
2180 # Add properties.
2181 if props:
2182 for prop in props:
2183 idx = prop.find(':')
2184 if idx == -1:
2185 raise AvbError('Malformed property "{}".'.format(prop))
2186 desc = AvbPropertyDescriptor()
2187 desc.key = prop[0:idx]
2188 desc.value = prop[(idx + 1):]
2189 encoded_descriptors.extend(desc.encode())
2190 if props_from_file:
2191 for prop in props_from_file:
2192 idx = prop.find(':')
2193 if idx == -1:
2194 raise AvbError('Malformed property "{}".'.format(prop))
2195 desc = AvbPropertyDescriptor()
2196 desc.key = prop[0:idx]
2197 desc.value = prop[(idx + 1):]
2198 file_path = prop[(idx + 1):]
2199 desc.value = open(file_path, 'rb').read()
2200 encoded_descriptors.extend(desc.encode())
2201
2202 # Add AvbKernelCmdline descriptor for dm-verity, if requested.
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002203 if setup_rootfs_from_kernel:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002204 image_handler = ImageHandler(
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002205 setup_rootfs_from_kernel.name)
David Zeuthenfd41eb92016-11-17 12:24:47 -05002206 cmdline_desc = self._get_cmdline_descriptors_for_dm_verity(image_handler)
2207 encoded_descriptors.extend(cmdline_desc[0].encode())
2208 encoded_descriptors.extend(cmdline_desc[1].encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002209
2210 # Add kernel command-lines.
2211 if kernel_cmdlines:
2212 for i in kernel_cmdlines:
2213 desc = AvbKernelCmdlineDescriptor()
2214 desc.kernel_cmdline = i
2215 encoded_descriptors.extend(desc.encode())
2216
2217 # Add descriptors from other images.
2218 if include_descriptors_from_image:
2219 for image in include_descriptors_from_image:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002220 image_handler = ImageHandler(image.name)
2221 (_, _, image_descriptors, _) = self._parse_image(image_handler)
David Zeuthen21e95262016-07-27 17:58:40 -04002222 for desc in image_descriptors:
2223 encoded_descriptors.extend(desc.encode())
2224
David Zeuthen18666ab2016-11-15 11:18:05 -05002225 # Load public key metadata blob, if requested.
2226 pkmd_blob = []
2227 if public_key_metadata_path:
2228 with open(public_key_metadata_path) as f:
2229 pkmd_blob = f.read()
2230
David Zeuthen21e95262016-07-27 17:58:40 -04002231 key = None
2232 encoded_key = bytearray()
2233 if alg.public_key_num_bytes > 0:
2234 if not key_path:
2235 raise AvbError('Key is required for algorithm {}'.format(
2236 algorithm_name))
David Zeuthenc68f0822017-03-31 17:22:35 -04002237 encoded_key = encode_rsa_key(key_path)
David Zeuthen21e95262016-07-27 17:58:40 -04002238 if len(encoded_key) != alg.public_key_num_bytes:
2239 raise AvbError('Key is wrong size for algorithm {}'.format(
2240 algorithm_name))
2241
2242 h = AvbVBMetaHeader()
2243
David Zeuthene3cadca2017-02-22 21:25:46 -05002244 # Override release string, if requested.
2245 if isinstance(release_string, (str, unicode)):
2246 h.release_string = release_string
2247
2248 # Append to release string, if requested. Also insert a space before.
2249 if isinstance(append_to_release_string, (str, unicode)):
2250 h.release_string += ' ' + append_to_release_string
2251
David Zeuthen18666ab2016-11-15 11:18:05 -05002252 # For the Auxiliary data block, descriptors are stored at offset 0,
2253 # followed by the public key, followed by the public key metadata blob.
David Zeuthen21e95262016-07-27 17:58:40 -04002254 h.auxiliary_data_block_size = round_to_multiple(
David Zeuthen18666ab2016-11-15 11:18:05 -05002255 len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64)
David Zeuthen21e95262016-07-27 17:58:40 -04002256 h.descriptors_offset = 0
2257 h.descriptors_size = len(encoded_descriptors)
2258 h.public_key_offset = h.descriptors_size
2259 h.public_key_size = len(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002260 h.public_key_metadata_offset = h.public_key_offset + h.public_key_size
2261 h.public_key_metadata_size = len(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002262
2263 # For the Authentication data block, the hash is first and then
2264 # the signature.
2265 h.authentication_data_block_size = round_to_multiple(
David Zeuthend5db21d2017-01-24 10:11:38 -05002266 alg.hash_num_bytes + alg.signature_num_bytes, 64)
David Zeuthen21e95262016-07-27 17:58:40 -04002267 h.algorithm_type = alg.algorithm_type
2268 h.hash_offset = 0
2269 h.hash_size = alg.hash_num_bytes
2270 # Signature offset and size - it's stored right after the hash
2271 # (in Authentication data block).
2272 h.signature_offset = alg.hash_num_bytes
2273 h.signature_size = alg.signature_num_bytes
2274
2275 h.rollback_index = rollback_index
David Zeuthenfd41eb92016-11-17 12:24:47 -05002276 h.flags = flags
David Zeuthen21e95262016-07-27 17:58:40 -04002277
2278 # Generate Header data block.
2279 header_data_blob = h.encode()
2280
2281 # Generate Auxiliary data block.
2282 aux_data_blob = bytearray()
2283 aux_data_blob.extend(encoded_descriptors)
2284 aux_data_blob.extend(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002285 aux_data_blob.extend(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002286 padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob)
2287 aux_data_blob.extend('\0' * padding_bytes)
2288
2289 # Calculate the hash.
2290 binary_hash = bytearray()
2291 binary_signature = bytearray()
2292 if algorithm_name != 'NONE':
David Zeuthenb623d8b2017-04-04 16:05:53 -04002293 ha = hashlib.new(alg.hash_name)
David Zeuthen21e95262016-07-27 17:58:40 -04002294 ha.update(header_data_blob)
2295 ha.update(aux_data_blob)
2296 binary_hash.extend(ha.digest())
2297
2298 # Calculate the signature.
David Zeuthen21e95262016-07-27 17:58:40 -04002299 padding_and_hash = str(bytearray(alg.padding)) + binary_hash
Esun Kimff44f232017-03-30 10:34:54 +09002300 binary_signature.extend(raw_sign(signing_helper, algorithm_name,
2301 alg.signature_num_bytes, key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08002302 padding_and_hash))
David Zeuthen21e95262016-07-27 17:58:40 -04002303
2304 # Generate Authentication data block.
2305 auth_data_blob = bytearray()
2306 auth_data_blob.extend(binary_hash)
2307 auth_data_blob.extend(binary_signature)
2308 padding_bytes = h.authentication_data_block_size - len(auth_data_blob)
2309 auth_data_blob.extend('\0' * padding_bytes)
2310
2311 return header_data_blob + auth_data_blob + aux_data_blob
2312
2313 def extract_public_key(self, key_path, output):
2314 """Implements the 'extract_public_key' command.
2315
2316 Arguments:
2317 key_path: The path to a RSA private key file.
2318 output: The file to write to.
2319 """
David Zeuthenc68f0822017-03-31 17:22:35 -04002320 output.write(encode_rsa_key(key_path))
David Zeuthen21e95262016-07-27 17:58:40 -04002321
David Zeuthenb1b994d2017-03-06 18:01:31 -05002322 def append_vbmeta_image(self, image_filename, vbmeta_image_filename,
2323 partition_size):
2324 """Implementation of the append_vbmeta_image command.
2325
2326 Arguments:
2327 image_filename: File to add the footer to.
2328 vbmeta_image_filename: File to get vbmeta struct from.
2329 partition_size: Size of partition.
2330
2331 Raises:
2332 AvbError: If an argument is incorrect.
2333 """
2334 image = ImageHandler(image_filename)
2335
2336 if partition_size % image.block_size != 0:
2337 raise AvbError('Partition size of {} is not a multiple of the image '
2338 'block size {}.'.format(partition_size,
2339 image.block_size))
2340
2341 # If there's already a footer, truncate the image to its original
2342 # size. This way 'avbtool append_vbmeta_image' is idempotent.
2343 image.seek(image.image_size - AvbFooter.SIZE)
2344 try:
2345 footer = AvbFooter(image.read(AvbFooter.SIZE))
2346 # Existing footer found. Just truncate.
2347 original_image_size = footer.original_image_size
2348 image.truncate(footer.original_image_size)
2349 except (LookupError, struct.error):
2350 original_image_size = image.image_size
2351
2352 # If anything goes wrong from here-on, restore the image back to
2353 # its original size.
2354 try:
2355 vbmeta_image_handler = ImageHandler(vbmeta_image_filename)
2356 vbmeta_blob = self._load_vbmeta_blob(vbmeta_image_handler)
2357
2358 # If the image isn't sparse, its size might not be a multiple of
2359 # the block size. This will screw up padding later so just grow it.
2360 if image.image_size % image.block_size != 0:
2361 assert not image.is_sparse
2362 padding_needed = image.block_size - (image.image_size%image.block_size)
2363 image.truncate(image.image_size + padding_needed)
2364
2365 # The append_raw() method requires content with size being a
2366 # multiple of |block_size| so add padding as needed. Also record
2367 # where this is written to since we'll need to put that in the
2368 # footer.
2369 vbmeta_offset = image.image_size
2370 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2371 len(vbmeta_blob))
2372 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
2373
2374 # Append vbmeta blob and footer
2375 image.append_raw(vbmeta_blob_with_padding)
2376 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
2377
2378 # Now insert a DONT_CARE chunk with enough bytes such that the
2379 # final Footer block is at the end of partition_size..
2380 image.append_dont_care(partition_size - vbmeta_end_offset -
2381 1*image.block_size)
2382
2383 # Generate the Footer that tells where the VBMeta footer
2384 # is. Also put enough padding in the front of the footer since
2385 # we'll write out an entire block.
2386 footer = AvbFooter()
2387 footer.original_image_size = original_image_size
2388 footer.vbmeta_offset = vbmeta_offset
2389 footer.vbmeta_size = len(vbmeta_blob)
2390 footer_blob = footer.encode()
2391 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2392 footer_blob)
2393 image.append_raw(footer_blob_with_padding)
2394
2395 except:
2396 # Truncate back to original size, then re-raise
2397 image.truncate(original_image_size)
2398 raise
2399
David Zeuthena4fee8b2016-08-22 15:20:43 -04002400 def add_hash_footer(self, image_filename, partition_size, partition_name,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002401 hash_algorithm, salt, chain_partitions, algorithm_name,
2402 key_path,
2403 public_key_metadata_path, rollback_index, flags, props,
David Zeuthen18666ab2016-11-15 11:18:05 -05002404 props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002405 setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05002406 include_descriptors_from_image, signing_helper,
David Zeuthene3cadca2017-02-22 21:25:46 -05002407 release_string, append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05002408 output_vbmeta_image, do_not_append_vbmeta_image):
David Zeuthena4fee8b2016-08-22 15:20:43 -04002409 """Implementation of the add_hash_footer on unsparse images.
David Zeuthen21e95262016-07-27 17:58:40 -04002410
2411 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002412 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04002413 partition_size: Size of partition.
2414 partition_name: Name of partition (without A/B suffix).
2415 hash_algorithm: Hash algorithm to use.
2416 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002417 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04002418 algorithm_name: Name of algorithm to use.
2419 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002420 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002421 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002422 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002423 props: Properties to insert (List of strings of the form 'key:value').
2424 props_from_file: Properties to insert (List of strings 'key:<path>').
2425 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002426 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002427 dm-verity kernel cmdline from.
2428 include_descriptors_from_image: List of file objects for which
2429 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002430 signing_helper: Program which signs a hash and return signature.
David Zeuthene3cadca2017-02-22 21:25:46 -05002431 release_string: None or avbtool release string.
2432 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05002433 output_vbmeta_image: If not None, also write vbmeta struct to this file.
2434 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthena4fee8b2016-08-22 15:20:43 -04002435
2436 Raises:
2437 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04002438 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002439 image = ImageHandler(image_filename)
2440
2441 if partition_size % image.block_size != 0:
2442 raise AvbError('Partition size of {} is not a multiple of the image '
2443 'block size {}.'.format(partition_size,
2444 image.block_size))
2445
David Zeuthen21e95262016-07-27 17:58:40 -04002446 # If there's already a footer, truncate the image to its original
2447 # size. This way 'avbtool add_hash_footer' is idempotent (modulo
2448 # salts).
David Zeuthen09692692016-09-30 16:16:40 -04002449 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002450 try:
2451 footer = AvbFooter(image.read(AvbFooter.SIZE))
2452 # Existing footer found. Just truncate.
2453 original_image_size = footer.original_image_size
David Zeuthen09692692016-09-30 16:16:40 -04002454 image.truncate(footer.original_image_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002455 except (LookupError, struct.error):
David Zeuthen09692692016-09-30 16:16:40 -04002456 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002457
2458 # If anything goes wrong from here-on, restore the image back to
2459 # its original size.
2460 try:
David Zeuthen09692692016-09-30 16:16:40 -04002461 # First, calculate the maximum image size such that an image
2462 # this size + metadata (footer + vbmeta struct) fits in
2463 # |partition_size|.
2464 max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE
2465 max_image_size = partition_size - max_metadata_size
2466
2467 # If image size exceeds the maximum image size, fail.
2468 if image.image_size > max_image_size:
2469 raise AvbError('Image size of {} exceeds maximum image '
2470 'size of {} in order to fit in a partition '
2471 'size of {}.'.format(image.image_size, max_image_size,
2472 partition_size))
2473
David Zeuthen21e95262016-07-27 17:58:40 -04002474 digest_size = len(hashlib.new(name=hash_algorithm).digest())
2475 if salt:
2476 salt = salt.decode('hex')
2477 else:
2478 if salt is None:
2479 # If salt is not explicitly specified, choose a hash
2480 # that's the same size as the hash size.
2481 hash_size = digest_size
2482 salt = open('/dev/urandom').read(hash_size)
2483 else:
2484 salt = ''
2485
2486 hasher = hashlib.new(name=hash_algorithm, string=salt)
2487 # TODO(zeuthen): might want to read this in chunks to avoid
2488 # memory pressure, then again, this is only supposed to be used
2489 # on kernel/initramfs partitions. Possible optimization.
2490 image.seek(0)
David Zeuthen09692692016-09-30 16:16:40 -04002491 hasher.update(image.read(image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002492 digest = hasher.digest()
2493
2494 h_desc = AvbHashDescriptor()
David Zeuthen09692692016-09-30 16:16:40 -04002495 h_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002496 h_desc.hash_algorithm = hash_algorithm
2497 h_desc.partition_name = partition_name
2498 h_desc.salt = salt
2499 h_desc.digest = digest
2500
2501 # Generate the VBMeta footer.
David Zeuthen21e95262016-07-27 17:58:40 -04002502 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002503 algorithm_name, key_path, public_key_metadata_path, [h_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05002504 chain_partitions, rollback_index, flags, props, props_from_file,
2505 kernel_cmdlines, setup_rootfs_from_kernel,
David Zeuthene3cadca2017-02-22 21:25:46 -05002506 include_descriptors_from_image, signing_helper, release_string,
2507 append_to_release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04002508
David Zeuthena4fee8b2016-08-22 15:20:43 -04002509 # If the image isn't sparse, its size might not be a multiple of
2510 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04002511 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002512 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04002513 padding_needed = image.block_size - (image.image_size%image.block_size)
2514 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04002515
David Zeuthena4fee8b2016-08-22 15:20:43 -04002516 # The append_raw() method requires content with size being a
2517 # multiple of |block_size| so add padding as needed. Also record
2518 # where this is written to since we'll need to put that in the
2519 # footer.
David Zeuthen09692692016-09-30 16:16:40 -04002520 vbmeta_offset = image.image_size
David Zeuthena4fee8b2016-08-22 15:20:43 -04002521 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2522 len(vbmeta_blob))
2523 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
David Zeuthena4fee8b2016-08-22 15:20:43 -04002524
David Zeuthend247fcb2017-02-16 12:09:27 -05002525 # Write vbmeta blob, if requested.
2526 if output_vbmeta_image:
2527 output_vbmeta_image.write(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002528
David Zeuthend247fcb2017-02-16 12:09:27 -05002529 # Append vbmeta blob and footer, unless requested not to.
2530 if not do_not_append_vbmeta_image:
2531 image.append_raw(vbmeta_blob_with_padding)
2532 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
2533
2534 # Now insert a DONT_CARE chunk with enough bytes such that the
2535 # final Footer block is at the end of partition_size..
2536 image.append_dont_care(partition_size - vbmeta_end_offset -
2537 1*image.block_size)
2538
2539 # Generate the Footer that tells where the VBMeta footer
2540 # is. Also put enough padding in the front of the footer since
2541 # we'll write out an entire block.
2542 footer = AvbFooter()
2543 footer.original_image_size = original_image_size
2544 footer.vbmeta_offset = vbmeta_offset
2545 footer.vbmeta_size = len(vbmeta_blob)
2546 footer_blob = footer.encode()
2547 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2548 footer_blob)
2549 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002550
David Zeuthen21e95262016-07-27 17:58:40 -04002551 except:
2552 # Truncate back to original size, then re-raise
2553 image.truncate(original_image_size)
2554 raise
2555
David Zeuthena4fee8b2016-08-22 15:20:43 -04002556 def add_hashtree_footer(self, image_filename, partition_size, partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002557 generate_fec, fec_num_roots, hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002558 block_size, salt, chain_partitions, algorithm_name,
2559 key_path,
2560 public_key_metadata_path, rollback_index, flags,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002561 props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002562 setup_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04002563 include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05002564 calc_max_image_size, signing_helper,
David Zeuthene3cadca2017-02-22 21:25:46 -05002565 release_string, append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05002566 output_vbmeta_image, do_not_append_vbmeta_image):
David Zeuthen21e95262016-07-27 17:58:40 -04002567 """Implements the 'add_hashtree_footer' command.
2568
2569 See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
2570 more information about dm-verity and these hashes.
2571
2572 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002573 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04002574 partition_size: Size of partition.
2575 partition_name: Name of partition (without A/B suffix).
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002576 generate_fec: If True, generate FEC codes.
2577 fec_num_roots: Number of roots for FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04002578 hash_algorithm: Hash algorithm to use.
2579 block_size: Block size to use.
2580 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002581 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04002582 algorithm_name: Name of algorithm to use.
2583 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002584 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002585 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002586 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002587 props: Properties to insert (List of strings of the form 'key:value').
2588 props_from_file: Properties to insert (List of strings 'key:<path>').
2589 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002590 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002591 dm-verity kernel cmdline from.
2592 include_descriptors_from_image: List of file objects for which
2593 to insert descriptors from.
David Zeuthen09692692016-09-30 16:16:40 -04002594 calc_max_image_size: Don't store the hashtree or footer - instead
2595 calculate the maximum image size leaving enough room for hashtree
2596 and metadata with the given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002597 signing_helper: Program which signs a hash and return signature.
David Zeuthene3cadca2017-02-22 21:25:46 -05002598 release_string: None or avbtool release string.
2599 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05002600 output_vbmeta_image: If not None, also write vbmeta struct to this file.
2601 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthena4fee8b2016-08-22 15:20:43 -04002602
2603 Raises:
2604 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04002605 """
David Zeuthen09692692016-09-30 16:16:40 -04002606 digest_size = len(hashlib.new(name=hash_algorithm).digest())
2607 digest_padding = round_to_pow2(digest_size) - digest_size
2608
2609 # First, calculate the maximum image size such that an image
2610 # this size + the hashtree + metadata (footer + vbmeta struct)
2611 # fits in |partition_size|. We use very conservative figures for
2612 # metadata.
2613 (_, max_tree_size) = calc_hash_level_offsets(
2614 partition_size, block_size, digest_size + digest_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002615 max_fec_size = 0
2616 if generate_fec:
2617 max_fec_size = calc_fec_data_size(partition_size, fec_num_roots)
2618 max_metadata_size = (max_fec_size + max_tree_size +
2619 self.MAX_VBMETA_SIZE +
David Zeuthen09692692016-09-30 16:16:40 -04002620 self.MAX_FOOTER_SIZE)
2621 max_image_size = partition_size - max_metadata_size
2622
2623 # If we're asked to only calculate the maximum image size, we're done.
2624 if calc_max_image_size:
2625 print '{}'.format(max_image_size)
2626 return
2627
David Zeuthena4fee8b2016-08-22 15:20:43 -04002628 image = ImageHandler(image_filename)
2629
2630 if partition_size % image.block_size != 0:
2631 raise AvbError('Partition size of {} is not a multiple of the image '
2632 'block size {}.'.format(partition_size,
2633 image.block_size))
2634
David Zeuthen21e95262016-07-27 17:58:40 -04002635 # If there's already a footer, truncate the image to its original
2636 # size. This way 'avbtool add_hashtree_footer' is idempotent
2637 # (modulo salts).
David Zeuthen09692692016-09-30 16:16:40 -04002638 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002639 try:
2640 footer = AvbFooter(image.read(AvbFooter.SIZE))
2641 # Existing footer found. Just truncate.
2642 original_image_size = footer.original_image_size
David Zeuthen09692692016-09-30 16:16:40 -04002643 image.truncate(footer.original_image_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002644 except (LookupError, struct.error):
David Zeuthen09692692016-09-30 16:16:40 -04002645 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002646
2647 # If anything goes wrong from here-on, restore the image back to
2648 # its original size.
2649 try:
2650 # Ensure image is multiple of block_size.
David Zeuthen09692692016-09-30 16:16:40 -04002651 rounded_image_size = round_to_multiple(image.image_size, block_size)
2652 if rounded_image_size > image.image_size:
2653 image.append_raw('\0' * (rounded_image_size - image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002654
David Zeuthen09692692016-09-30 16:16:40 -04002655 # If image size exceeds the maximum image size, fail.
2656 if image.image_size > max_image_size:
2657 raise AvbError('Image size of {} exceeds maximum image '
2658 'size of {} in order to fit in a partition '
2659 'size of {}.'.format(image.image_size, max_image_size,
2660 partition_size))
David Zeuthen21e95262016-07-27 17:58:40 -04002661
2662 if salt:
2663 salt = salt.decode('hex')
2664 else:
2665 if salt is None:
2666 # If salt is not explicitly specified, choose a hash
2667 # that's the same size as the hash size.
2668 hash_size = digest_size
2669 salt = open('/dev/urandom').read(hash_size)
2670 else:
2671 salt = ''
2672
David Zeuthena4fee8b2016-08-22 15:20:43 -04002673 # Hashes are stored upside down so we need to calculate hash
David Zeuthen21e95262016-07-27 17:58:40 -04002674 # offsets in advance.
2675 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
David Zeuthen09692692016-09-30 16:16:40 -04002676 image.image_size, block_size, digest_size + digest_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04002677
David Zeuthena4fee8b2016-08-22 15:20:43 -04002678 # If the image isn't sparse, its size might not be a multiple of
2679 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04002680 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002681 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04002682 padding_needed = image.block_size - (image.image_size%image.block_size)
2683 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04002684
David Zeuthena4fee8b2016-08-22 15:20:43 -04002685 # Generate the tree and add padding as needed.
David Zeuthen09692692016-09-30 16:16:40 -04002686 tree_offset = image.image_size
2687 root_digest, hash_tree = generate_hash_tree(image, image.image_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002688 block_size,
2689 hash_algorithm, salt,
2690 digest_padding,
2691 hash_level_offsets,
2692 tree_size)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002693
2694 # Generate HashtreeDescriptor with details about the tree we
2695 # just generated.
David Zeuthen21e95262016-07-27 17:58:40 -04002696 ht_desc = AvbHashtreeDescriptor()
2697 ht_desc.dm_verity_version = 1
David Zeuthen09692692016-09-30 16:16:40 -04002698 ht_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002699 ht_desc.tree_offset = tree_offset
2700 ht_desc.tree_size = tree_size
2701 ht_desc.data_block_size = block_size
2702 ht_desc.hash_block_size = block_size
2703 ht_desc.hash_algorithm = hash_algorithm
2704 ht_desc.partition_name = partition_name
2705 ht_desc.salt = salt
2706 ht_desc.root_digest = root_digest
2707
David Zeuthen09692692016-09-30 16:16:40 -04002708 # Write the hash tree
2709 padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
2710 len(hash_tree))
2711 hash_tree_with_padding = hash_tree + '\0'*padding_needed
2712 image.append_raw(hash_tree_with_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002713 len_hashtree_and_fec = len(hash_tree_with_padding)
2714
2715 # Generate FEC codes, if requested.
2716 if generate_fec:
2717 fec_data = generate_fec_data(image_filename, fec_num_roots)
2718 padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
2719 len(fec_data))
2720 fec_data_with_padding = fec_data + '\0'*padding_needed
2721 fec_offset = image.image_size
2722 image.append_raw(fec_data_with_padding)
2723 len_hashtree_and_fec += len(fec_data_with_padding)
2724 # Update the hashtree descriptor.
2725 ht_desc.fec_num_roots = fec_num_roots
2726 ht_desc.fec_offset = fec_offset
2727 ht_desc.fec_size = len(fec_data)
David Zeuthen09692692016-09-30 16:16:40 -04002728
David Zeuthena4fee8b2016-08-22 15:20:43 -04002729 # Generate the VBMeta footer and add padding as needed.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002730 vbmeta_offset = tree_offset + len_hashtree_and_fec
David Zeuthen21e95262016-07-27 17:58:40 -04002731 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002732 algorithm_name, key_path, public_key_metadata_path, [ht_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05002733 chain_partitions, rollback_index, flags, props, props_from_file,
2734 kernel_cmdlines, setup_rootfs_from_kernel,
David Zeuthene3cadca2017-02-22 21:25:46 -05002735 include_descriptors_from_image, signing_helper, release_string,
2736 append_to_release_string)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002737 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
2738 len(vbmeta_blob))
2739 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
David Zeuthen21e95262016-07-27 17:58:40 -04002740
David Zeuthend247fcb2017-02-16 12:09:27 -05002741 # Write vbmeta blob, if requested.
2742 if output_vbmeta_image:
2743 output_vbmeta_image.write(vbmeta_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002744
David Zeuthend247fcb2017-02-16 12:09:27 -05002745 # Append vbmeta blob and footer, unless requested not to.
2746 if not do_not_append_vbmeta_image:
2747 image.append_raw(vbmeta_blob_with_padding)
2748
2749 # Now insert a DONT_CARE chunk with enough bytes such that the
2750 # final Footer block is at the end of partition_size..
2751 image.append_dont_care(partition_size - image.image_size -
2752 1*image.block_size)
2753
2754 # Generate the Footer that tells where the VBMeta footer
2755 # is. Also put enough padding in the front of the footer since
2756 # we'll write out an entire block.
2757 footer = AvbFooter()
2758 footer.original_image_size = original_image_size
2759 footer.vbmeta_offset = vbmeta_offset
2760 footer.vbmeta_size = len(vbmeta_blob)
2761 footer_blob = footer.encode()
2762 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2763 footer_blob)
2764 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04002765
David Zeuthen21e95262016-07-27 17:58:40 -04002766 except:
David Zeuthen09692692016-09-30 16:16:40 -04002767 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04002768 image.truncate(original_image_size)
2769 raise
2770
David Zeuthenc68f0822017-03-31 17:22:35 -04002771 def make_atx_certificate(self, output, authority_key_path, subject_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08002772 subject_key_version, subject,
2773 is_intermediate_authority, signing_helper):
2774 """Implements the 'make_atx_certificate' command.
2775
2776 Android Things certificates are required for Android Things public key
2777 metadata. They chain the vbmeta signing key for a particular product back to
2778 a fused, permanent root key. These certificates are fixed-length and fixed-
2779 format with the explicit goal of not parsing ASN.1 in bootloader code.
2780
2781 Arguments:
2782 output: Certificate will be written to this file on success.
2783 authority_key_path: A PEM file path with the authority private key.
2784 If None, then a certificate will be created without a
2785 signature. The signature can be created out-of-band
2786 and appended.
David Zeuthenc68f0822017-03-31 17:22:35 -04002787 subject_key_path: Path to a PEM or DER subject public key.
Darren Krahn147b08d2016-12-20 16:38:29 -08002788 subject_key_version: A 64-bit version value. If this is None, the number
2789 of seconds since the epoch is used.
2790 subject: A subject identifier. For Product Signing Key certificates this
2791 should be the same Product ID found in the permanent attributes.
2792 is_intermediate_authority: True if the certificate is for an intermediate
2793 authority.
2794 signing_helper: Program which signs a hash and returns the signature.
2795 """
2796 signed_data = bytearray()
2797 signed_data.extend(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04002798 signed_data.extend(encode_rsa_key(subject_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08002799 hasher = hashlib.sha256()
2800 hasher.update(subject)
2801 signed_data.extend(hasher.digest())
2802 usage = 'com.google.android.things.vboot'
2803 if is_intermediate_authority:
2804 usage += '.ca'
2805 hasher = hashlib.sha256()
2806 hasher.update(usage)
2807 signed_data.extend(hasher.digest())
2808 if not subject_key_version:
2809 subject_key_version = int(time.time())
2810 signed_data.extend(struct.pack('<Q', subject_key_version))
2811 signature = bytearray()
2812 if authority_key_path:
2813 padding_and_hash = bytearray()
Darren Krahn43e12d82017-02-24 16:26:31 -08002814 algorithm_name = 'SHA512_RSA4096'
Esun Kimff44f232017-03-30 10:34:54 +09002815 alg = ALGORITHMS[algorithm_name]
Darren Krahn43e12d82017-02-24 16:26:31 -08002816 hasher = hashlib.sha512()
Esun Kimff44f232017-03-30 10:34:54 +09002817 padding_and_hash.extend(alg.padding)
Darren Krahn147b08d2016-12-20 16:38:29 -08002818 hasher.update(signed_data)
2819 padding_and_hash.extend(hasher.digest())
2820 signature.extend(raw_sign(signing_helper, algorithm_name,
Esun Kimff44f232017-03-30 10:34:54 +09002821 alg.signature_num_bytes, authority_key_path,
2822 padding_and_hash))
Darren Krahn147b08d2016-12-20 16:38:29 -08002823 output.write(signed_data)
2824 output.write(signature)
2825
David Zeuthenc68f0822017-03-31 17:22:35 -04002826 def make_atx_permanent_attributes(self, output, root_authority_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08002827 product_id):
2828 """Implements the 'make_atx_permanent_attributes' command.
2829
2830 Android Things permanent attributes are designed to be permanent for a
2831 particular product and a hash of these attributes should be fused into
2832 hardware to enforce this.
2833
2834 Arguments:
2835 output: Attributes will be written to this file on success.
David Zeuthenc68f0822017-03-31 17:22:35 -04002836 root_authority_key_path: Path to a PEM or DER public key for
2837 the root authority.
Darren Krahn147b08d2016-12-20 16:38:29 -08002838 product_id: A 16-byte Product ID.
2839
2840 Raises:
2841 AvbError: If an argument is incorrect.
2842 """
Darren Krahn43e12d82017-02-24 16:26:31 -08002843 EXPECTED_PRODUCT_ID_SIZE = 16
2844 if len(product_id) != EXPECTED_PRODUCT_ID_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08002845 raise AvbError('Invalid Product ID length.')
2846 output.write(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04002847 output.write(encode_rsa_key(root_authority_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08002848 output.write(product_id)
2849
2850 def make_atx_metadata(self, output, intermediate_key_certificate,
Darren Krahn43e12d82017-02-24 16:26:31 -08002851 product_key_certificate):
Darren Krahn147b08d2016-12-20 16:38:29 -08002852 """Implements the 'make_atx_metadata' command.
2853
2854 Android Things metadata are included in vbmeta images to facilitate
2855 verification. The output of this command can be used as the
2856 public_key_metadata argument to other commands.
2857
2858 Arguments:
2859 output: Metadata will be written to this file on success.
2860 intermediate_key_certificate: A certificate file as output by
2861 make_atx_certificate with
2862 is_intermediate_authority set to true.
2863 product_key_certificate: A certificate file as output by
2864 make_atx_certificate with
2865 is_intermediate_authority set to false.
Darren Krahn147b08d2016-12-20 16:38:29 -08002866
2867 Raises:
2868 AvbError: If an argument is incorrect.
2869 """
Darren Krahn43e12d82017-02-24 16:26:31 -08002870 EXPECTED_CERTIFICATE_SIZE = 1620
2871 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08002872 raise AvbError('Invalid intermediate key certificate length.')
Darren Krahn43e12d82017-02-24 16:26:31 -08002873 if len(product_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08002874 raise AvbError('Invalid product key certificate length.')
2875 output.write(struct.pack('<I', 1)) # Format Version
2876 output.write(intermediate_key_certificate)
2877 output.write(product_key_certificate)
Darren Krahn147b08d2016-12-20 16:38:29 -08002878
David Zeuthen21e95262016-07-27 17:58:40 -04002879
2880def calc_hash_level_offsets(image_size, block_size, digest_size):
2881 """Calculate the offsets of all the hash-levels in a Merkle-tree.
2882
2883 Arguments:
2884 image_size: The size of the image to calculate a Merkle-tree for.
2885 block_size: The block size, e.g. 4096.
2886 digest_size: The size of each hash, e.g. 32 for SHA-256.
2887
2888 Returns:
2889 A tuple where the first argument is an array of offsets and the
2890 second is size of the tree, in bytes.
2891 """
2892 level_offsets = []
2893 level_sizes = []
2894 tree_size = 0
2895
2896 num_levels = 0
2897 size = image_size
2898 while size > block_size:
2899 num_blocks = (size + block_size - 1) / block_size
2900 level_size = round_to_multiple(num_blocks * digest_size, block_size)
2901
2902 level_sizes.append(level_size)
2903 tree_size += level_size
2904 num_levels += 1
2905
2906 size = level_size
2907
2908 for n in range(0, num_levels):
2909 offset = 0
2910 for m in range(n + 1, num_levels):
2911 offset += level_sizes[m]
2912 level_offsets.append(offset)
2913
David Zeuthena4fee8b2016-08-22 15:20:43 -04002914 return level_offsets, tree_size
David Zeuthen21e95262016-07-27 17:58:40 -04002915
2916
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002917# See system/extras/libfec/include/fec/io.h for these definitions.
2918FEC_FOOTER_FORMAT = '<LLLLLQ32s'
2919FEC_MAGIC = 0xfecfecfe
2920
2921
2922def calc_fec_data_size(image_size, num_roots):
2923 """Calculates how much space FEC data will take.
2924
2925 Args:
2926 image_size: The size of the image.
2927 num_roots: Number of roots.
2928
2929 Returns:
2930 The number of bytes needed for FEC for an image of the given size
2931 and with the requested number of FEC roots.
2932
2933 Raises:
2934 ValueError: If output from the 'fec' tool is invalid.
2935
2936 """
2937 p = subprocess.Popen(
2938 ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)],
2939 stdout=subprocess.PIPE,
2940 stderr=subprocess.PIPE)
2941 (pout, perr) = p.communicate()
2942 retcode = p.wait()
2943 if retcode != 0:
2944 raise ValueError('Error invoking fec: {}'.format(perr))
2945 return int(pout)
2946
2947
2948def generate_fec_data(image_filename, num_roots):
2949 """Generate FEC codes for an image.
2950
2951 Args:
2952 image_filename: The filename of the image.
2953 num_roots: Number of roots.
2954
2955 Returns:
2956 The FEC data blob.
2957
2958 Raises:
2959 ValueError: If output from the 'fec' tool is invalid.
2960 """
2961 fec_tmpfile = tempfile.NamedTemporaryFile()
2962 subprocess.check_call(
2963 ['fec', '--encode', '--roots', str(num_roots), image_filename,
2964 fec_tmpfile.name],
2965 stderr=open(os.devnull))
2966 fec_data = fec_tmpfile.read()
2967 footer_size = struct.calcsize(FEC_FOOTER_FORMAT)
2968 footer_data = fec_data[-footer_size:]
2969 (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT,
2970 footer_data)
2971 if magic != FEC_MAGIC:
2972 raise ValueError('Unexpected magic in FEC footer')
2973 return fec_data[0:fec_size]
2974
2975
David Zeuthen21e95262016-07-27 17:58:40 -04002976def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002977 digest_padding, hash_level_offsets, tree_size):
David Zeuthen21e95262016-07-27 17:58:40 -04002978 """Generates a Merkle-tree for a file.
2979
2980 Args:
2981 image: The image, as a file.
2982 image_size: The size of the image.
2983 block_size: The block size, e.g. 4096.
2984 hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'.
2985 salt: The salt to use.
2986 digest_padding: The padding for each digest.
David Zeuthen21e95262016-07-27 17:58:40 -04002987 hash_level_offsets: The offsets from calc_hash_level_offsets().
David Zeuthena4fee8b2016-08-22 15:20:43 -04002988 tree_size: The size of the tree, in number of bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04002989
2990 Returns:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002991 A tuple where the first element is the top-level hash and the
2992 second element is the hash-tree.
David Zeuthen21e95262016-07-27 17:58:40 -04002993 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002994 hash_ret = bytearray(tree_size)
David Zeuthen21e95262016-07-27 17:58:40 -04002995 hash_src_offset = 0
2996 hash_src_size = image_size
2997 level_num = 0
2998 while hash_src_size > block_size:
2999 level_output = ''
David Zeuthen21e95262016-07-27 17:58:40 -04003000 remaining = hash_src_size
3001 while remaining > 0:
3002 hasher = hashlib.new(name=hash_alg_name, string=salt)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003003 # Only read from the file for the first level - for subsequent
3004 # levels, access the array we're building.
3005 if level_num == 0:
3006 image.seek(hash_src_offset + hash_src_size - remaining)
3007 data = image.read(min(remaining, block_size))
3008 else:
3009 offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining
3010 data = hash_ret[offset:offset + block_size]
David Zeuthen21e95262016-07-27 17:58:40 -04003011 hasher.update(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003012
3013 remaining -= len(data)
David Zeuthen21e95262016-07-27 17:58:40 -04003014 if len(data) < block_size:
3015 hasher.update('\0' * (block_size - len(data)))
3016 level_output += hasher.digest()
3017 if digest_padding > 0:
3018 level_output += '\0' * digest_padding
3019
3020 padding_needed = (round_to_multiple(
3021 len(level_output), block_size) - len(level_output))
3022 level_output += '\0' * padding_needed
3023
David Zeuthena4fee8b2016-08-22 15:20:43 -04003024 # Copy level-output into resulting tree.
3025 offset = hash_level_offsets[level_num]
3026 hash_ret[offset:offset + len(level_output)] = level_output
David Zeuthen21e95262016-07-27 17:58:40 -04003027
David Zeuthena4fee8b2016-08-22 15:20:43 -04003028 # Continue on to the next level.
David Zeuthen21e95262016-07-27 17:58:40 -04003029 hash_src_size = len(level_output)
David Zeuthen21e95262016-07-27 17:58:40 -04003030 level_num += 1
3031
3032 hasher = hashlib.new(name=hash_alg_name, string=salt)
3033 hasher.update(level_output)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003034 return hasher.digest(), hash_ret
David Zeuthen21e95262016-07-27 17:58:40 -04003035
3036
3037class AvbTool(object):
3038 """Object for avbtool command-line tool."""
3039
3040 def __init__(self):
3041 """Initializer method."""
3042 self.avb = Avb()
3043
3044 def _add_common_args(self, sub_parser):
3045 """Adds arguments used by several sub-commands.
3046
3047 Arguments:
3048 sub_parser: The parser to add arguments to.
3049 """
3050 sub_parser.add_argument('--algorithm',
3051 help='Algorithm to use (default: NONE)',
3052 metavar='ALGORITHM',
3053 default='NONE')
3054 sub_parser.add_argument('--key',
3055 help='Path to RSA private key file',
3056 metavar='KEY',
3057 required=False)
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003058 sub_parser.add_argument('--signing_helper',
3059 help='Path to helper used for signing',
3060 metavar='APP',
3061 default=None,
3062 required=False)
David Zeuthen18666ab2016-11-15 11:18:05 -05003063 sub_parser.add_argument('--public_key_metadata',
3064 help='Path to public key metadata file',
3065 metavar='KEY_METADATA',
3066 required=False)
David Zeuthen21e95262016-07-27 17:58:40 -04003067 sub_parser.add_argument('--rollback_index',
3068 help='Rollback Index',
3069 type=parse_number,
3070 default=0)
David Zeuthene3cadca2017-02-22 21:25:46 -05003071 # This is used internally for unit tests. Do not include in --help output.
3072 sub_parser.add_argument('--internal_release_string',
3073 help=argparse.SUPPRESS)
3074 sub_parser.add_argument('--append_to_release_string',
3075 help='Text to append to release string',
3076 metavar='STR')
David Zeuthen21e95262016-07-27 17:58:40 -04003077 sub_parser.add_argument('--prop',
3078 help='Add property',
3079 metavar='KEY:VALUE',
3080 action='append')
3081 sub_parser.add_argument('--prop_from_file',
3082 help='Add property from file',
3083 metavar='KEY:PATH',
3084 action='append')
3085 sub_parser.add_argument('--kernel_cmdline',
3086 help='Add kernel cmdline',
3087 metavar='CMDLINE',
3088 action='append')
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003089 # TODO(zeuthen): the --setup_rootfs_from_kernel option used to be called
3090 # --generate_dm_verity_cmdline_from_hashtree. Remove support for the latter
3091 # at some future point.
3092 sub_parser.add_argument('--setup_rootfs_from_kernel',
3093 '--generate_dm_verity_cmdline_from_hashtree',
David Zeuthen21e95262016-07-27 17:58:40 -04003094 metavar='IMAGE',
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003095 help='Adds kernel cmdline to set up IMAGE',
David Zeuthen21e95262016-07-27 17:58:40 -04003096 type=argparse.FileType('rb'))
3097 sub_parser.add_argument('--include_descriptors_from_image',
3098 help='Include descriptors from image',
3099 metavar='IMAGE',
3100 action='append',
3101 type=argparse.FileType('rb'))
David Zeuthena5fd3a42017-02-27 16:38:54 -05003102 # These are only allowed from top-level vbmeta and boot-in-lieu-of-vbmeta.
3103 sub_parser.add_argument('--chain_partition',
3104 help='Allow signed integrity-data for partition',
3105 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
3106 action='append')
3107 sub_parser.add_argument('--flags',
3108 help='VBMeta flags',
3109 type=parse_number,
3110 default=0)
3111 sub_parser.add_argument('--set_hashtree_disabled_flag',
3112 help='Set the HASHTREE_DISABLED flag',
3113 action='store_true')
3114
3115 def _fixup_common_args(self, args):
3116 """Common fixups needed by subcommands.
3117
3118 Arguments:
3119 args: Arguments to modify.
3120
3121 Returns:
3122 The modified arguments.
3123 """
3124 if args.set_hashtree_disabled_flag:
3125 args.flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED
3126 return args
David Zeuthen21e95262016-07-27 17:58:40 -04003127
3128 def run(self, argv):
3129 """Command-line processor.
3130
3131 Arguments:
3132 argv: Pass sys.argv from main.
3133 """
3134 parser = argparse.ArgumentParser()
3135 subparsers = parser.add_subparsers(title='subcommands')
3136
3137 sub_parser = subparsers.add_parser('version',
3138 help='Prints version of avbtool.')
3139 sub_parser.set_defaults(func=self.version)
3140
3141 sub_parser = subparsers.add_parser('extract_public_key',
3142 help='Extract public key.')
3143 sub_parser.add_argument('--key',
3144 help='Path to RSA private key file',
3145 required=True)
3146 sub_parser.add_argument('--output',
3147 help='Output file name',
3148 type=argparse.FileType('wb'),
3149 required=True)
3150 sub_parser.set_defaults(func=self.extract_public_key)
3151
3152 sub_parser = subparsers.add_parser('make_vbmeta_image',
3153 help='Makes a vbmeta image.')
3154 sub_parser.add_argument('--output',
3155 help='Output file name',
3156 type=argparse.FileType('wb'),
3157 required=True)
3158 self._add_common_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04003159 sub_parser.set_defaults(func=self.make_vbmeta_image)
3160
3161 sub_parser = subparsers.add_parser('add_hash_footer',
3162 help='Add hashes and footer to image.')
3163 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003164 help='Image to add hashes to',
David Zeuthen21e95262016-07-27 17:58:40 -04003165 type=argparse.FileType('rab+'))
3166 sub_parser.add_argument('--partition_size',
3167 help='Partition size',
3168 type=parse_number,
3169 required=True)
3170 sub_parser.add_argument('--partition_name',
3171 help='Partition name',
3172 required=True)
3173 sub_parser.add_argument('--hash_algorithm',
3174 help='Hash algorithm to use (default: sha256)',
3175 default='sha256')
3176 sub_parser.add_argument('--salt',
3177 help='Salt in hex (default: /dev/urandom)')
David Zeuthend247fcb2017-02-16 12:09:27 -05003178 sub_parser.add_argument('--output_vbmeta_image',
3179 help='Also write vbmeta struct to file',
3180 type=argparse.FileType('wb'))
3181 sub_parser.add_argument('--do_not_append_vbmeta_image',
3182 help=('Do not append vbmeta struct or footer '
3183 'to the image'),
3184 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04003185 self._add_common_args(sub_parser)
3186 sub_parser.set_defaults(func=self.add_hash_footer)
3187
David Zeuthenb1b994d2017-03-06 18:01:31 -05003188 sub_parser = subparsers.add_parser('append_vbmeta_image',
3189 help='Append vbmeta image to image.')
3190 sub_parser.add_argument('--image',
3191 help='Image to append vbmeta blob to',
3192 type=argparse.FileType('rab+'))
3193 sub_parser.add_argument('--partition_size',
3194 help='Partition size',
3195 type=parse_number,
3196 required=True)
3197 sub_parser.add_argument('--vbmeta_image',
3198 help='Image with vbmeta blob to append',
3199 type=argparse.FileType('rb'))
3200 sub_parser.set_defaults(func=self.append_vbmeta_image)
3201
David Zeuthen21e95262016-07-27 17:58:40 -04003202 sub_parser = subparsers.add_parser('add_hashtree_footer',
3203 help='Add hashtree and footer to image.')
3204 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003205 help='Image to add hashtree to',
David Zeuthen21e95262016-07-27 17:58:40 -04003206 type=argparse.FileType('rab+'))
3207 sub_parser.add_argument('--partition_size',
3208 help='Partition size',
3209 type=parse_number,
3210 required=True)
3211 sub_parser.add_argument('--partition_name',
3212 help='Partition name',
David Zeuthen09692692016-09-30 16:16:40 -04003213 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04003214 sub_parser.add_argument('--hash_algorithm',
3215 help='Hash algorithm to use (default: sha1)',
3216 default='sha1')
3217 sub_parser.add_argument('--salt',
3218 help='Salt in hex (default: /dev/urandom)')
3219 sub_parser.add_argument('--block_size',
3220 help='Block size (default: 4096)',
3221 type=parse_number,
3222 default=4096)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003223 sub_parser.add_argument('--generate_fec',
3224 help='Add forward-error-correction codes',
3225 action='store_true')
3226 sub_parser.add_argument('--fec_num_roots',
3227 help='Number of roots for FEC (default: 2)',
3228 type=parse_number,
3229 default=2)
David Zeuthen09692692016-09-30 16:16:40 -04003230 sub_parser.add_argument('--calc_max_image_size',
3231 help=('Don\'t store the hashtree or footer - '
3232 'instead calculate the maximum image size '
3233 'leaving enough room for hashtree '
3234 'and metadata with the given partition '
3235 'size.'),
3236 action='store_true')
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_hashtree_footer)
3246
3247 sub_parser = subparsers.add_parser('erase_footer',
3248 help='Erase footer from an image.')
3249 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003250 help='Image with a footer',
David Zeuthen21e95262016-07-27 17:58:40 -04003251 type=argparse.FileType('rwb+'),
3252 required=True)
3253 sub_parser.add_argument('--keep_hashtree',
David Zeuthenfbb61fa2017-02-02 12:11:49 -05003254 help='Keep the hashtree and FEC in the image',
David Zeuthen21e95262016-07-27 17:58:40 -04003255 action='store_true')
3256 sub_parser.set_defaults(func=self.erase_footer)
3257
3258 sub_parser = subparsers.add_parser(
3259 'info_image',
3260 help='Show information about vbmeta or footer.')
3261 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04003262 help='Image to show information about',
David Zeuthen21e95262016-07-27 17:58:40 -04003263 type=argparse.FileType('rb'),
3264 required=True)
3265 sub_parser.add_argument('--output',
3266 help='Write info to file',
3267 type=argparse.FileType('wt'),
3268 default=sys.stdout)
3269 sub_parser.set_defaults(func=self.info_image)
3270
David Zeuthenb623d8b2017-04-04 16:05:53 -04003271 sub_parser = subparsers.add_parser(
3272 'verify_image',
3273 help='Verify an image.')
3274 sub_parser.add_argument('--image',
3275 help='Image to verify',
3276 type=argparse.FileType('rb'),
3277 required=True)
3278 sub_parser.set_defaults(func=self.verify_image)
3279
David Zeuthen8b6973b2016-09-20 12:39:49 -04003280 sub_parser = subparsers.add_parser('set_ab_metadata',
3281 help='Set A/B metadata.')
3282 sub_parser.add_argument('--misc_image',
3283 help=('The misc image to modify. If the image does '
3284 'not exist, it will be created.'),
3285 type=argparse.FileType('r+b'),
3286 required=True)
3287 sub_parser.add_argument('--slot_data',
3288 help=('Slot data of the form "priority", '
3289 '"tries_remaining", "sucessful_boot" for '
3290 'slot A followed by the same for slot B, '
3291 'separated by colons. The default value '
3292 'is 15:7:0:14:7:0.'),
3293 default='15:7:0:14:7:0')
3294 sub_parser.set_defaults(func=self.set_ab_metadata)
3295
Darren Krahn147b08d2016-12-20 16:38:29 -08003296 sub_parser = subparsers.add_parser(
3297 'make_atx_certificate',
3298 help='Create an Android Things eXtension (ATX) certificate.')
3299 sub_parser.add_argument('--output',
3300 help='Write certificate to file',
3301 type=argparse.FileType('wb'),
3302 default=sys.stdout)
3303 sub_parser.add_argument('--subject',
3304 help=('Path to subject file'),
3305 type=argparse.FileType('rb'),
3306 required=True)
3307 sub_parser.add_argument('--subject_key',
3308 help=('Path to subject RSA public key file'),
3309 type=argparse.FileType('rb'),
3310 required=True)
3311 sub_parser.add_argument('--subject_key_version',
3312 help=('Version of the subject key'),
3313 type=parse_number,
3314 required=False)
3315 sub_parser.add_argument('--subject_is_intermediate_authority',
3316 help=('Generate an intermediate authority '
3317 'certificate'),
3318 action='store_true')
3319 sub_parser.add_argument('--authority_key',
3320 help='Path to authority RSA private key file',
3321 required=False)
3322 sub_parser.add_argument('--signing_helper',
3323 help='Path to helper used for signing',
3324 metavar='APP',
3325 default=None,
3326 required=False)
3327 sub_parser.set_defaults(func=self.make_atx_certificate)
3328
3329 sub_parser = subparsers.add_parser(
3330 'make_atx_permanent_attributes',
3331 help='Create Android Things eXtension (ATX) permanent attributes.')
3332 sub_parser.add_argument('--output',
3333 help='Write attributes to file',
3334 type=argparse.FileType('wb'),
3335 default=sys.stdout)
3336 sub_parser.add_argument('--root_authority_key',
3337 help='Path to authority RSA public key file',
3338 type=argparse.FileType('rb'),
3339 required=True)
3340 sub_parser.add_argument('--product_id',
3341 help=('Path to Product ID file'),
3342 type=argparse.FileType('rb'),
3343 required=True)
3344 sub_parser.set_defaults(func=self.make_atx_permanent_attributes)
3345
3346 sub_parser = subparsers.add_parser(
3347 'make_atx_metadata',
3348 help='Create Android Things eXtension (ATX) metadata.')
3349 sub_parser.add_argument('--output',
3350 help='Write metadata to file',
3351 type=argparse.FileType('wb'),
3352 default=sys.stdout)
3353 sub_parser.add_argument('--intermediate_key_certificate',
3354 help='Path to intermediate key certificate file',
3355 type=argparse.FileType('rb'),
3356 required=True)
3357 sub_parser.add_argument('--product_key_certificate',
3358 help='Path to product key certificate file',
3359 type=argparse.FileType('rb'),
3360 required=True)
Darren Krahn147b08d2016-12-20 16:38:29 -08003361 sub_parser.set_defaults(func=self.make_atx_metadata)
3362
David Zeuthen21e95262016-07-27 17:58:40 -04003363 args = parser.parse_args(argv[1:])
3364 try:
3365 args.func(args)
3366 except AvbError as e:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003367 sys.stderr.write('{}: {}\n'.format(argv[0], e.message))
David Zeuthen21e95262016-07-27 17:58:40 -04003368 sys.exit(1)
3369
3370 def version(self, _):
3371 """Implements the 'version' sub-command."""
David Zeuthene3cadca2017-02-22 21:25:46 -05003372 print get_release_string()
David Zeuthen21e95262016-07-27 17:58:40 -04003373
3374 def extract_public_key(self, args):
3375 """Implements the 'extract_public_key' sub-command."""
3376 self.avb.extract_public_key(args.key, args.output)
3377
3378 def make_vbmeta_image(self, args):
3379 """Implements the 'make_vbmeta_image' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05003380 args = self._fixup_common_args(args)
David Zeuthen21e95262016-07-27 17:58:40 -04003381 self.avb.make_vbmeta_image(args.output, args.chain_partition,
David Zeuthen18666ab2016-11-15 11:18:05 -05003382 args.algorithm, args.key,
3383 args.public_key_metadata, args.rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003384 args.flags, args.prop, args.prop_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04003385 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003386 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05003387 args.include_descriptors_from_image,
David Zeuthene3cadca2017-02-22 21:25:46 -05003388 args.signing_helper,
3389 args.internal_release_string,
3390 args.append_to_release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04003391
David Zeuthenb1b994d2017-03-06 18:01:31 -05003392 def append_vbmeta_image(self, args):
3393 """Implements the 'append_vbmeta_image' sub-command."""
3394 self.avb.append_vbmeta_image(args.image.name, args.vbmeta_image.name,
3395 args.partition_size)
3396
David Zeuthen21e95262016-07-27 17:58:40 -04003397 def add_hash_footer(self, args):
3398 """Implements the 'add_hash_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05003399 args = self._fixup_common_args(args)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003400 self.avb.add_hash_footer(args.image.name, args.partition_size,
David Zeuthen21e95262016-07-27 17:58:40 -04003401 args.partition_name, args.hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003402 args.salt, args.chain_partition, args.algorithm,
3403 args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05003404 args.public_key_metadata, args.rollback_index,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003405 args.flags, args.prop, args.prop_from_file,
David Zeuthen18666ab2016-11-15 11:18:05 -05003406 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003407 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05003408 args.include_descriptors_from_image,
3409 args.signing_helper,
David Zeuthene3cadca2017-02-22 21:25:46 -05003410 args.internal_release_string,
3411 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05003412 args.output_vbmeta_image,
3413 args.do_not_append_vbmeta_image)
David Zeuthen21e95262016-07-27 17:58:40 -04003414
3415 def add_hashtree_footer(self, args):
3416 """Implements the 'add_hashtree_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05003417 args = self._fixup_common_args(args)
David Zeuthen09692692016-09-30 16:16:40 -04003418 self.avb.add_hashtree_footer(args.image.name if args.image else None,
3419 args.partition_size,
3420 args.partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003421 args.generate_fec, args.fec_num_roots,
David Zeuthen09692692016-09-30 16:16:40 -04003422 args.hash_algorithm, args.block_size,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003423 args.salt, args.chain_partition, args.algorithm,
3424 args.key, args.public_key_metadata,
3425 args.rollback_index, args.flags, args.prop,
David Zeuthen09692692016-09-30 16:16:40 -04003426 args.prop_from_file,
3427 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003428 args.setup_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04003429 args.include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05003430 args.calc_max_image_size, args.signing_helper,
David Zeuthene3cadca2017-02-22 21:25:46 -05003431 args.internal_release_string,
3432 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05003433 args.output_vbmeta_image,
3434 args.do_not_append_vbmeta_image)
3435
David Zeuthen21e95262016-07-27 17:58:40 -04003436 def erase_footer(self, args):
3437 """Implements the 'erase_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04003438 self.avb.erase_footer(args.image.name, args.keep_hashtree)
David Zeuthen21e95262016-07-27 17:58:40 -04003439
David Zeuthen8b6973b2016-09-20 12:39:49 -04003440 def set_ab_metadata(self, args):
3441 """Implements the 'set_ab_metadata' sub-command."""
3442 self.avb.set_ab_metadata(args.misc_image, args.slot_data)
3443
David Zeuthen21e95262016-07-27 17:58:40 -04003444 def info_image(self, args):
3445 """Implements the 'info_image' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04003446 self.avb.info_image(args.image.name, args.output)
David Zeuthen21e95262016-07-27 17:58:40 -04003447
David Zeuthenb623d8b2017-04-04 16:05:53 -04003448 def verify_image(self, args):
3449 """Implements the 'verify_image' sub-command."""
3450 self.avb.verify_image(args.image.name)
3451
Darren Krahn147b08d2016-12-20 16:38:29 -08003452 def make_atx_certificate(self, args):
3453 """Implements the 'make_atx_certificate' sub-command."""
3454 self.avb.make_atx_certificate(args.output, args.authority_key,
David Zeuthenc68f0822017-03-31 17:22:35 -04003455 args.subject_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08003456 args.subject_key_version,
3457 args.subject.read(),
3458 args.subject_is_intermediate_authority,
3459 args.signing_helper)
3460
3461 def make_atx_permanent_attributes(self, args):
3462 """Implements the 'make_atx_permanent_attributes' sub-command."""
3463 self.avb.make_atx_permanent_attributes(args.output,
David Zeuthenc68f0822017-03-31 17:22:35 -04003464 args.root_authority_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08003465 args.product_id.read())
3466
3467 def make_atx_metadata(self, args):
3468 """Implements the 'make_atx_metadata' sub-command."""
3469 self.avb.make_atx_metadata(args.output,
3470 args.intermediate_key_certificate.read(),
Darren Krahn43e12d82017-02-24 16:26:31 -08003471 args.product_key_certificate.read())
Darren Krahn147b08d2016-12-20 16:38:29 -08003472
David Zeuthen21e95262016-07-27 17:58:40 -04003473
3474if __name__ == '__main__':
3475 tool = AvbTool()
3476 tool.run(sys.argv)