blob: 6fd05cd560c47e4972871b2556cc7fa586fadf72 [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
Jan Monsch23e0c622019-12-11 11:23:58 +010027from __future__ import print_function
28
David Zeuthen21e95262016-07-27 17:58:40 -040029import argparse
David Zeuthen8b6973b2016-09-20 12:39:49 -040030import binascii
David Zeuthena4fee8b2016-08-22 15:20:43 -040031import bisect
David Zeuthen21e95262016-07-27 17:58:40 -040032import hashlib
David Zeuthenc68f0822017-03-31 17:22:35 -040033import math
David Zeuthen21e95262016-07-27 17:58:40 -040034import os
35import struct
36import subprocess
37import sys
David Zeuthen0b7f1d32016-10-25 17:53:49 -040038import tempfile
Darren Krahn147b08d2016-12-20 16:38:29 -080039import time
David Zeuthen21e95262016-07-27 17:58:40 -040040
David Zeuthene3cadca2017-02-22 21:25:46 -050041# Keep in sync with libavb/avb_version.h.
David Zeuthen21e95262016-07-27 17:58:40 -040042AVB_VERSION_MAJOR = 1
Darren Krahnfd0ba0d2018-02-01 18:06:34 -080043AVB_VERSION_MINOR = 1
David Zeuthene3cadca2017-02-22 21:25:46 -050044AVB_VERSION_SUB = 0
45
Darren Krahnfd0ba0d2018-02-01 18:06:34 -080046# Keep in sync with libavb/avb_footer.h.
47AVB_FOOTER_VERSION_MAJOR = 1
48AVB_FOOTER_VERSION_MINOR = 0
49
David Zeuthen58305522017-01-11 17:42:47 -050050AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED = 1
David Zeuthen21e95262016-07-27 17:58:40 -040051
Jan Monsch2c7be992020-04-03 14:37:13 +020052# Configuration for enabling logging of calls to avbtool.
53AVB_INVOCATION_LOGFILE = os.environ.get('AVB_INVOCATION_LOGFILE')
54
David Zeuthene3cadca2017-02-22 21:25:46 -050055
David Zeuthen21e95262016-07-27 17:58:40 -040056class AvbError(Exception):
57 """Application-specific errors.
58
59 These errors represent issues for which a stack-trace should not be
60 presented.
61
62 Attributes:
63 message: Error message.
64 """
65
66 def __init__(self, message):
67 Exception.__init__(self, message)
68
69
70class Algorithm(object):
71 """Contains details about an algorithm.
72
Tao Bao80418a52018-07-20 11:41:22 -070073 See the avb_vbmeta_image.h file for more details about algorithms.
David Zeuthen21e95262016-07-27 17:58:40 -040074
75 The constant |ALGORITHMS| is a dictionary from human-readable
76 names (e.g 'SHA256_RSA2048') to instances of this class.
77
78 Attributes:
79 algorithm_type: Integer code corresponding to |AvbAlgorithmType|.
David Zeuthenb623d8b2017-04-04 16:05:53 -040080 hash_name: Empty or a name from |hashlib.algorithms|.
David Zeuthen21e95262016-07-27 17:58:40 -040081 hash_num_bytes: Number of bytes used to store the hash.
82 signature_num_bytes: Number of bytes used to store the signature.
83 public_key_num_bytes: Number of bytes used to store the public key.
84 padding: Padding used for signature, if any.
85 """
86
David Zeuthenb623d8b2017-04-04 16:05:53 -040087 def __init__(self, algorithm_type, hash_name, hash_num_bytes,
88 signature_num_bytes, public_key_num_bytes, padding):
David Zeuthen21e95262016-07-27 17:58:40 -040089 self.algorithm_type = algorithm_type
David Zeuthenb623d8b2017-04-04 16:05:53 -040090 self.hash_name = hash_name
David Zeuthen21e95262016-07-27 17:58:40 -040091 self.hash_num_bytes = hash_num_bytes
92 self.signature_num_bytes = signature_num_bytes
93 self.public_key_num_bytes = public_key_num_bytes
94 self.padding = padding
95
David Zeuthenb623d8b2017-04-04 16:05:53 -040096
David Zeuthen21e95262016-07-27 17:58:40 -040097# This must be kept in sync with the avb_crypto.h file.
98#
99# The PKC1-v1.5 padding is a blob of binary DER of ASN.1 and is
100# obtained from section 5.2.2 of RFC 4880.
101ALGORITHMS = {
102 'NONE': Algorithm(
103 algorithm_type=0, # AVB_ALGORITHM_TYPE_NONE
David Zeuthenb623d8b2017-04-04 16:05:53 -0400104 hash_name='',
David Zeuthen21e95262016-07-27 17:58:40 -0400105 hash_num_bytes=0,
106 signature_num_bytes=0,
107 public_key_num_bytes=0,
108 padding=[]),
109 'SHA256_RSA2048': Algorithm(
110 algorithm_type=1, # AVB_ALGORITHM_TYPE_SHA256_RSA2048
David Zeuthenb623d8b2017-04-04 16:05:53 -0400111 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400112 hash_num_bytes=32,
113 signature_num_bytes=256,
Jan Monsch23e0c622019-12-11 11:23:58 +0100114 public_key_num_bytes=8 + 2*2048//8,
David Zeuthen21e95262016-07-27 17:58:40 -0400115 padding=[
116 # PKCS1-v1_5 padding
117 0x00, 0x01] + [0xff]*202 + [0x00] + [
118 # ASN.1 header
119 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
120 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
121 0x00, 0x04, 0x20,
122 ]),
123 'SHA256_RSA4096': Algorithm(
124 algorithm_type=2, # AVB_ALGORITHM_TYPE_SHA256_RSA4096
David Zeuthenb623d8b2017-04-04 16:05:53 -0400125 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400126 hash_num_bytes=32,
127 signature_num_bytes=512,
Jan Monsch23e0c622019-12-11 11:23:58 +0100128 public_key_num_bytes=8 + 2*4096//8,
David Zeuthen21e95262016-07-27 17:58:40 -0400129 padding=[
130 # PKCS1-v1_5 padding
131 0x00, 0x01] + [0xff]*458 + [0x00] + [
132 # ASN.1 header
133 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
134 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
135 0x00, 0x04, 0x20,
136 ]),
137 'SHA256_RSA8192': Algorithm(
138 algorithm_type=3, # AVB_ALGORITHM_TYPE_SHA256_RSA8192
David Zeuthenb623d8b2017-04-04 16:05:53 -0400139 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400140 hash_num_bytes=32,
141 signature_num_bytes=1024,
Jan Monsch23e0c622019-12-11 11:23:58 +0100142 public_key_num_bytes=8 + 2*8192//8,
David Zeuthen21e95262016-07-27 17:58:40 -0400143 padding=[
144 # PKCS1-v1_5 padding
145 0x00, 0x01] + [0xff]*970 + [0x00] + [
146 # ASN.1 header
147 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
148 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
149 0x00, 0x04, 0x20,
150 ]),
151 'SHA512_RSA2048': Algorithm(
152 algorithm_type=4, # AVB_ALGORITHM_TYPE_SHA512_RSA2048
David Zeuthenb623d8b2017-04-04 16:05:53 -0400153 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400154 hash_num_bytes=64,
155 signature_num_bytes=256,
Jan Monsch23e0c622019-12-11 11:23:58 +0100156 public_key_num_bytes=8 + 2*2048//8,
David Zeuthen21e95262016-07-27 17:58:40 -0400157 padding=[
158 # PKCS1-v1_5 padding
159 0x00, 0x01] + [0xff]*170 + [0x00] + [
160 # ASN.1 header
161 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
162 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
163 0x00, 0x04, 0x40
164 ]),
165 'SHA512_RSA4096': Algorithm(
166 algorithm_type=5, # AVB_ALGORITHM_TYPE_SHA512_RSA4096
David Zeuthenb623d8b2017-04-04 16:05:53 -0400167 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400168 hash_num_bytes=64,
169 signature_num_bytes=512,
Jan Monsch23e0c622019-12-11 11:23:58 +0100170 public_key_num_bytes=8 + 2*4096//8,
David Zeuthen21e95262016-07-27 17:58:40 -0400171 padding=[
172 # PKCS1-v1_5 padding
173 0x00, 0x01] + [0xff]*426 + [0x00] + [
174 # ASN.1 header
175 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
176 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
177 0x00, 0x04, 0x40
178 ]),
179 'SHA512_RSA8192': Algorithm(
180 algorithm_type=6, # AVB_ALGORITHM_TYPE_SHA512_RSA8192
David Zeuthenb623d8b2017-04-04 16:05:53 -0400181 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400182 hash_num_bytes=64,
183 signature_num_bytes=1024,
Jan Monsch23e0c622019-12-11 11:23:58 +0100184 public_key_num_bytes=8 + 2*8192//8,
David Zeuthen21e95262016-07-27 17:58:40 -0400185 padding=[
186 # PKCS1-v1_5 padding
187 0x00, 0x01] + [0xff]*938 + [0x00] + [
188 # ASN.1 header
189 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
190 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
191 0x00, 0x04, 0x40
192 ]),
193}
194
195
David Zeuthene3cadca2017-02-22 21:25:46 -0500196def get_release_string():
197 """Calculates the release string to use in the VBMeta struct."""
198 # Keep in sync with libavb/avb_version.c:avb_version_string().
199 return 'avbtool {}.{}.{}'.format(AVB_VERSION_MAJOR,
200 AVB_VERSION_MINOR,
201 AVB_VERSION_SUB)
202
203
David Zeuthen21e95262016-07-27 17:58:40 -0400204def round_to_multiple(number, size):
205 """Rounds a number up to nearest multiple of another number.
206
Jan Monschfe00c0a2019-12-11 11:19:40 +0100207 Arguments:
David Zeuthen21e95262016-07-27 17:58:40 -0400208 number: The number to round up.
209 size: The multiple to round up to.
210
211 Returns:
212 If |number| is a multiple of |size|, returns |number|, otherwise
213 returns |number| + |size|.
214 """
215 remainder = number % size
216 if remainder == 0:
217 return number
218 return number + size - remainder
219
220
221def round_to_pow2(number):
222 """Rounds a number up to the next power of 2.
223
Jan Monschfe00c0a2019-12-11 11:19:40 +0100224 Arguments:
David Zeuthen21e95262016-07-27 17:58:40 -0400225 number: The number to round up.
226
227 Returns:
228 If |number| is already a power of 2 then |number| is
229 returned. Otherwise the smallest power of 2 greater than |number|
230 is returned.
231 """
232 return 2**((number - 1).bit_length())
233
234
David Zeuthen21e95262016-07-27 17:58:40 -0400235def encode_long(num_bits, value):
236 """Encodes a long to a bytearray() using a given amount of bits.
237
238 This number is written big-endian, e.g. with the most significant
239 bit first.
240
David Zeuthenb623d8b2017-04-04 16:05:53 -0400241 This is the reverse of decode_long().
242
David Zeuthen21e95262016-07-27 17:58:40 -0400243 Arguments:
244 num_bits: The number of bits to write, e.g. 2048.
245 value: The value to write.
246
247 Returns:
248 A bytearray() with the encoded long.
249 """
250 ret = bytearray()
251 for bit_pos in range(num_bits, 0, -8):
252 octet = (value >> (bit_pos - 8)) & 0xff
253 ret.extend(struct.pack('!B', octet))
254 return ret
255
256
David Zeuthenb623d8b2017-04-04 16:05:53 -0400257def decode_long(blob):
258 """Decodes a long from a bytearray() using a given amount of bits.
259
260 This number is expected to be in big-endian, e.g. with the most
261 significant bit first.
262
263 This is the reverse of encode_long().
264
265 Arguments:
Jan Monscheeb28b62019-12-05 16:17:09 +0100266 blob: A bytearray() with the encoded long.
David Zeuthenb623d8b2017-04-04 16:05:53 -0400267
268 Returns:
269 The decoded value.
270 """
271 ret = 0
272 for b in bytearray(blob):
273 ret *= 256
274 ret += b
275 return ret
276
277
David Zeuthen21e95262016-07-27 17:58:40 -0400278def egcd(a, b):
279 """Calculate greatest common divisor of two numbers.
280
281 This implementation uses a recursive version of the extended
282 Euclidian algorithm.
283
284 Arguments:
285 a: First number.
286 b: Second number.
287
288 Returns:
289 A tuple (gcd, x, y) that where |gcd| is the greatest common
290 divisor of |a| and |b| and |a|*|x| + |b|*|y| = |gcd|.
291 """
292 if a == 0:
293 return (b, 0, 1)
Jan Monsch23e0c622019-12-11 11:23:58 +0100294 g, y, x = egcd(b % a, a)
295 return (g, x - (b // a) * y, y)
David Zeuthen21e95262016-07-27 17:58:40 -0400296
297
298def modinv(a, m):
299 """Calculate modular multiplicative inverse of |a| modulo |m|.
300
301 This calculates the number |x| such that |a| * |x| == 1 (modulo
302 |m|). This number only exists if |a| and |m| are co-prime - |None|
303 is returned if this isn't true.
304
305 Arguments:
306 a: The number to calculate a modular inverse of.
307 m: The modulo to use.
308
309 Returns:
310 The modular multiplicative inverse of |a| and |m| or |None| if
311 these numbers are not co-prime.
312 """
313 gcd, x, _ = egcd(a, m)
314 if gcd != 1:
315 return None # modular inverse does not exist
Jan Monsch23e0c622019-12-11 11:23:58 +0100316 return x % m
David Zeuthen21e95262016-07-27 17:58:40 -0400317
318
319def parse_number(string):
320 """Parse a string as a number.
321
322 This is just a short-hand for int(string, 0) suitable for use in the
323 |type| parameter of |ArgumentParser|'s add_argument() function. An
324 improvement to just using type=int is that this function supports
325 numbers in other bases, e.g. "0x1234".
326
327 Arguments:
328 string: The string to parse.
329
330 Returns:
331 The parsed integer.
332
333 Raises:
334 ValueError: If the number could not be parsed.
335 """
336 return int(string, 0)
337
338
David Zeuthenc68f0822017-03-31 17:22:35 -0400339class RSAPublicKey(object):
340 """Data structure used for a RSA public key.
David Zeuthen21e95262016-07-27 17:58:40 -0400341
David Zeuthenc68f0822017-03-31 17:22:35 -0400342 Attributes:
343 exponent: The key exponent.
344 modulus: The key modulus.
345 num_bits: The key size.
David Zeuthen21e95262016-07-27 17:58:40 -0400346 """
David Zeuthenc68f0822017-03-31 17:22:35 -0400347
Jan Monsch38865f22020-04-08 09:32:58 +0200348 MODULUS_PREFIX = b'modulus='
David Zeuthenc68f0822017-03-31 17:22:35 -0400349
350 def __init__(self, key_path):
351 """Loads and parses an RSA key from either a private or public key file.
352
353 Arguments:
354 key_path: The path to a key file.
Jan Monsch77cd2022019-12-10 17:18:04 +0100355
356 Raises:
357 AvbError: If RSA key parameters could not be read from file.
David Zeuthenc68f0822017-03-31 17:22:35 -0400358 """
359 # We used to have something as simple as this:
360 #
361 # key = Crypto.PublicKey.RSA.importKey(open(key_path).read())
362 # self.exponent = key.e
363 # self.modulus = key.n
364 # self.num_bits = key.size() + 1
365 #
366 # but unfortunately PyCrypto is not available in the builder. So
367 # instead just parse openssl(1) output to get this
368 # information. It's ugly but...
369 args = ['openssl', 'rsa', '-in', key_path, '-modulus', '-noout']
370 p = subprocess.Popen(args,
371 stdin=subprocess.PIPE,
372 stdout=subprocess.PIPE,
373 stderr=subprocess.PIPE)
374 (pout, perr) = p.communicate()
375 if p.wait() != 0:
376 # Could be just a public key is passed, try that.
377 args.append('-pubin')
378 p = subprocess.Popen(args,
379 stdin=subprocess.PIPE,
380 stdout=subprocess.PIPE,
381 stderr=subprocess.PIPE)
382 (pout, perr) = p.communicate()
383 if p.wait() != 0:
384 raise AvbError('Error getting public key: {}'.format(perr))
385
386 if not pout.lower().startswith(self.MODULUS_PREFIX):
387 raise AvbError('Unexpected modulus output')
388
389 modulus_hexstr = pout[len(self.MODULUS_PREFIX):]
390
391 # The exponent is assumed to always be 65537 and the number of
392 # bits can be derived from the modulus by rounding up to the
393 # nearest power of 2.
394 self.modulus = int(modulus_hexstr, 16)
395 self.num_bits = round_to_pow2(int(math.ceil(math.log(self.modulus, 2))))
396 self.exponent = 65537
David Zeuthen21e95262016-07-27 17:58:40 -0400397
398
David Zeuthenc68f0822017-03-31 17:22:35 -0400399def encode_rsa_key(key_path):
David Zeuthen21e95262016-07-27 17:58:40 -0400400 """Encodes a public RSA key in |AvbRSAPublicKeyHeader| format.
401
402 This creates a |AvbRSAPublicKeyHeader| as well as the two large
403 numbers (|key_num_bits| bits long) following it.
404
405 Arguments:
David Zeuthenc68f0822017-03-31 17:22:35 -0400406 key_path: The path to a key file.
David Zeuthen21e95262016-07-27 17:58:40 -0400407
408 Returns:
409 A bytearray() with the |AvbRSAPublicKeyHeader|.
Jan Monsch77cd2022019-12-10 17:18:04 +0100410
411 Raises:
412 AvbError: If given RSA key exponent is not 65537.
David Zeuthen21e95262016-07-27 17:58:40 -0400413 """
David Zeuthenc68f0822017-03-31 17:22:35 -0400414 key = RSAPublicKey(key_path)
415 if key.exponent != 65537:
416 raise AvbError('Only RSA keys with exponent 65537 are supported.')
David Zeuthen21e95262016-07-27 17:58:40 -0400417 ret = bytearray()
David Zeuthen21e95262016-07-27 17:58:40 -0400418 # Calculate n0inv = -1/n[0] (mod 2^32)
Jan Monschcc6a15c2020-04-09 02:06:54 +0200419 b = 2 ** 32
David Zeuthenc68f0822017-03-31 17:22:35 -0400420 n0inv = b - modinv(key.modulus, b)
David Zeuthen21e95262016-07-27 17:58:40 -0400421 # Calculate rr = r^2 (mod N), where r = 2^(# of key bits)
Jan Monschcc6a15c2020-04-09 02:06:54 +0200422 r = 2 ** key.modulus.bit_length()
David Zeuthenc68f0822017-03-31 17:22:35 -0400423 rrmodn = r * r % key.modulus
424 ret.extend(struct.pack('!II', key.num_bits, n0inv))
425 ret.extend(encode_long(key.num_bits, key.modulus))
426 ret.extend(encode_long(key.num_bits, rrmodn))
David Zeuthen21e95262016-07-27 17:58:40 -0400427 return ret
428
429
430def lookup_algorithm_by_type(alg_type):
431 """Looks up algorithm by type.
432
433 Arguments:
434 alg_type: The integer representing the type.
435
436 Returns:
437 A tuple with the algorithm name and an |Algorithm| instance.
438
439 Raises:
440 Exception: If the algorithm cannot be found
441 """
442 for alg_name in ALGORITHMS:
443 alg_data = ALGORITHMS[alg_name]
444 if alg_data.algorithm_type == alg_type:
445 return (alg_name, alg_data)
446 raise AvbError('Unknown algorithm type {}'.format(alg_type))
447
Jan Monsch77cd2022019-12-10 17:18:04 +0100448
Dan Austina7bc4962019-12-02 13:26:08 -0800449def lookup_hash_size_by_type(alg_type):
450 """Looks up hash size by type.
451
452 Arguments:
453 alg_type: The integer representing the type.
454
455 Returns:
456 The corresponding hash size.
457
458 Raises:
459 AvbError: If the algorithm cannot be found.
460 """
461 for alg_name in ALGORITHMS:
462 alg_data = ALGORITHMS[alg_name]
463 if alg_data.algorithm_type == alg_type:
464 return alg_data.hash_num_bytes
465 raise AvbError('Unsupported algorithm type {}'.format(alg_type))
David Zeuthen21e95262016-07-27 17:58:40 -0400466
Jan Monsch77cd2022019-12-10 17:18:04 +0100467
David Zeuthena156d3d2017-06-01 12:08:09 -0400468def raw_sign(signing_helper, signing_helper_with_files,
469 algorithm_name, signature_num_bytes, key_path,
Esun Kimff44f232017-03-30 10:34:54 +0900470 raw_data_to_sign):
Darren Krahn147b08d2016-12-20 16:38:29 -0800471 """Computes a raw RSA signature using |signing_helper| or openssl.
472
473 Arguments:
474 signing_helper: Program which signs a hash and returns the signature.
David Zeuthena156d3d2017-06-01 12:08:09 -0400475 signing_helper_with_files: Same as signing_helper but uses files instead.
Darren Krahn147b08d2016-12-20 16:38:29 -0800476 algorithm_name: The algorithm name as per the ALGORITHMS dict.
Esun Kimff44f232017-03-30 10:34:54 +0900477 signature_num_bytes: Number of bytes used to store the signature.
Darren Krahn147b08d2016-12-20 16:38:29 -0800478 key_path: Path to the private key file. Must be PEM format.
Jan Monsch38865f22020-04-08 09:32:58 +0200479 raw_data_to_sign: Data to sign as bytes or bytearray.
Darren Krahn147b08d2016-12-20 16:38:29 -0800480
481 Returns:
Jan Monsch38865f22020-04-08 09:32:58 +0200482 The signature as bytes.
Darren Krahn147b08d2016-12-20 16:38:29 -0800483
484 Raises:
Jan Monsch38865f22020-04-08 09:32:58 +0200485 AvbError: If an error occurred during signing.
Darren Krahn147b08d2016-12-20 16:38:29 -0800486 """
487 p = None
David Zeuthena156d3d2017-06-01 12:08:09 -0400488 if signing_helper_with_files is not None:
489 signing_file = tempfile.NamedTemporaryFile()
Jan Monsch38865f22020-04-08 09:32:58 +0200490 signing_file.write(raw_data_to_sign)
David Zeuthena156d3d2017-06-01 12:08:09 -0400491 signing_file.flush()
Jan Monscheeb28b62019-12-05 16:17:09 +0100492 p = subprocess.Popen([
493 signing_helper_with_files, algorithm_name, key_path, signing_file.name])
David Zeuthena156d3d2017-06-01 12:08:09 -0400494 retcode = p.wait()
495 if retcode != 0:
496 raise AvbError('Error signing')
497 signing_file.seek(0)
Jan Monsch38865f22020-04-08 09:32:58 +0200498 signature = signing_file.read()
Darren Krahn147b08d2016-12-20 16:38:29 -0800499 else:
David Zeuthena156d3d2017-06-01 12:08:09 -0400500 if signing_helper is not None:
501 p = subprocess.Popen(
502 [signing_helper, algorithm_name, key_path],
503 stdin=subprocess.PIPE,
504 stdout=subprocess.PIPE,
505 stderr=subprocess.PIPE)
506 else:
507 p = subprocess.Popen(
508 ['openssl', 'rsautl', '-sign', '-inkey', key_path, '-raw'],
509 stdin=subprocess.PIPE,
510 stdout=subprocess.PIPE,
511 stderr=subprocess.PIPE)
Jan Monsch38865f22020-04-08 09:32:58 +0200512 (pout, perr) = p.communicate(raw_data_to_sign)
David Zeuthena156d3d2017-06-01 12:08:09 -0400513 retcode = p.wait()
514 if retcode != 0:
515 raise AvbError('Error signing: {}'.format(perr))
Jan Monsch38865f22020-04-08 09:32:58 +0200516 signature = pout
Esun Kimff44f232017-03-30 10:34:54 +0900517 if len(signature) != signature_num_bytes:
518 raise AvbError('Error signing: Invalid length of signature')
519 return signature
Darren Krahn147b08d2016-12-20 16:38:29 -0800520
521
David Zeuthenb623d8b2017-04-04 16:05:53 -0400522def verify_vbmeta_signature(vbmeta_header, vbmeta_blob):
Jan Monsch77cd2022019-12-10 17:18:04 +0100523 """Checks that signature in a vbmeta blob was made by the embedded public key.
David Zeuthenb623d8b2017-04-04 16:05:53 -0400524
525 Arguments:
526 vbmeta_header: A AvbVBMetaHeader.
Jan Monsch38865f22020-04-08 09:32:58 +0200527 vbmeta_blob: The whole vbmeta blob, including the header as bytes or
528 bytearray.
David Zeuthenb623d8b2017-04-04 16:05:53 -0400529
530 Returns:
531 True if the signature is valid and corresponds to the embedded
532 public key. Also returns True if the vbmeta blob is not signed.
Jan Monsch77cd2022019-12-10 17:18:04 +0100533
534 Raises:
535 AvbError: If there errors calling out to openssl command during
536 signature verification.
David Zeuthenb623d8b2017-04-04 16:05:53 -0400537 """
538 (_, alg) = lookup_algorithm_by_type(vbmeta_header.algorithm_type)
Jan Monschfe00c0a2019-12-11 11:19:40 +0100539 if not alg.hash_name:
David Zeuthenb623d8b2017-04-04 16:05:53 -0400540 return True
541 header_blob = vbmeta_blob[0:256]
542 auth_offset = 256
543 aux_offset = auth_offset + vbmeta_header.authentication_data_block_size
544 aux_size = vbmeta_header.auxiliary_data_block_size
545 aux_blob = vbmeta_blob[aux_offset:aux_offset + aux_size]
546 pubkey_offset = aux_offset + vbmeta_header.public_key_offset
547 pubkey_size = vbmeta_header.public_key_size
548 pubkey_blob = vbmeta_blob[pubkey_offset:pubkey_offset + pubkey_size]
549
550 digest_offset = auth_offset + vbmeta_header.hash_offset
551 digest_size = vbmeta_header.hash_size
552 digest_blob = vbmeta_blob[digest_offset:digest_offset + digest_size]
553
554 sig_offset = auth_offset + vbmeta_header.signature_offset
555 sig_size = vbmeta_header.signature_size
556 sig_blob = vbmeta_blob[sig_offset:sig_offset + sig_size]
557
558 # Now that we've got the stored digest, public key, and signature
559 # all we need to do is to verify. This is the exactly the same
560 # steps as performed in the avb_vbmeta_image_verify() function in
561 # libavb/avb_vbmeta_image.c.
562
563 ha = hashlib.new(alg.hash_name)
564 ha.update(header_blob)
565 ha.update(aux_blob)
566 computed_digest = ha.digest()
567
568 if computed_digest != digest_blob:
569 return False
570
571 padding_and_digest = bytearray(alg.padding)
572 padding_and_digest.extend(computed_digest)
573
574 (num_bits,) = struct.unpack('!I', pubkey_blob[0:4])
Jan Monsch23e0c622019-12-11 11:23:58 +0100575 modulus_blob = pubkey_blob[8:8 + num_bits//8]
David Zeuthenb623d8b2017-04-04 16:05:53 -0400576 modulus = decode_long(modulus_blob)
577 exponent = 65537
578
David Zeuthenddd7d6f2018-11-26 17:28:38 -0500579 # We used to have this:
580 #
581 # import Crypto.PublicKey.RSA
582 # key = Crypto.PublicKey.RSA.construct((modulus, long(exponent)))
583 # if not key.verify(decode_long(padding_and_digest),
584 # (decode_long(sig_blob), None)):
585 # return False
586 # return True
587 #
588 # but since 'avbtool verify_image' is used on the builders we don't want
589 # to rely on Crypto.PublicKey.RSA. Instead just use openssl(1) to verify.
590 asn1_str = ('asn1=SEQUENCE:pubkeyinfo\n'
591 '\n'
592 '[pubkeyinfo]\n'
593 'algorithm=SEQUENCE:rsa_alg\n'
594 'pubkey=BITWRAP,SEQUENCE:rsapubkey\n'
595 '\n'
596 '[rsa_alg]\n'
597 'algorithm=OID:rsaEncryption\n'
598 'parameter=NULL\n'
599 '\n'
600 '[rsapubkey]\n'
Jan Monsch38865f22020-04-08 09:32:58 +0200601 'n=INTEGER:{}\n'
602 'e=INTEGER:{}\n').format(hex(modulus).rstrip('L'),
603 hex(exponent).rstrip('L'))
David Zeuthenddd7d6f2018-11-26 17:28:38 -0500604
Jan Monsch38865f22020-04-08 09:32:58 +0200605 with tempfile.NamedTemporaryFile() as asn1_tmpfile:
606 asn1_tmpfile.write(asn1_str.encode('ascii'))
607 asn1_tmpfile.flush()
608
609 with tempfile.NamedTemporaryFile() as der_tmpfile:
610 p = subprocess.Popen(
611 ['openssl', 'asn1parse', '-genconf', asn1_tmpfile.name, '-out',
612 der_tmpfile.name, '-noout'])
613 retcode = p.wait()
614 if retcode != 0:
615 raise AvbError('Error generating DER file')
616
617 p = subprocess.Popen(
618 ['openssl', 'rsautl', '-verify', '-pubin', '-inkey', der_tmpfile.name,
619 '-keyform', 'DER', '-raw'],
620 stdin=subprocess.PIPE,
621 stdout=subprocess.PIPE,
622 stderr=subprocess.PIPE)
623 (pout, perr) = p.communicate(sig_blob)
624 retcode = p.wait()
625 if retcode != 0:
626 raise AvbError('Error verifying data: {}'.format(perr))
627 if pout != padding_and_digest:
628 sys.stderr.write('Signature not correct\n')
629 return False
David Zeuthenb623d8b2017-04-04 16:05:53 -0400630 return True
631
632
David Zeuthena4fee8b2016-08-22 15:20:43 -0400633class ImageChunk(object):
634 """Data structure used for representing chunks in Android sparse files.
635
636 Attributes:
637 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
638 chunk_offset: Offset in the sparse file where this chunk begins.
639 output_offset: Offset in de-sparsified file where output begins.
640 output_size: Number of bytes in output.
641 input_offset: Offset in sparse file for data if TYPE_RAW otherwise None.
642 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
643 """
644
645 FORMAT = '<2H2I'
646 TYPE_RAW = 0xcac1
647 TYPE_FILL = 0xcac2
648 TYPE_DONT_CARE = 0xcac3
649 TYPE_CRC32 = 0xcac4
650
651 def __init__(self, chunk_type, chunk_offset, output_offset, output_size,
652 input_offset, fill_data):
653 """Initializes an ImageChunk object.
654
655 Arguments:
656 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
657 chunk_offset: Offset in the sparse file where this chunk begins.
658 output_offset: Offset in de-sparsified file.
659 output_size: Number of bytes in output.
660 input_offset: Offset in sparse file if TYPE_RAW otherwise None.
Jan Monsch8347da92020-04-08 12:41:49 +0200661 fill_data: Blob as bytes with data to fill if TYPE_FILL otherwise None.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400662
663 Raises:
Jan Monsch8347da92020-04-08 12:41:49 +0200664 ValueError: If given chunk parameters are invalid.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400665 """
666 self.chunk_type = chunk_type
667 self.chunk_offset = chunk_offset
668 self.output_offset = output_offset
669 self.output_size = output_size
670 self.input_offset = input_offset
671 self.fill_data = fill_data
672 # Check invariants.
673 if self.chunk_type == self.TYPE_RAW:
674 if self.fill_data is not None:
675 raise ValueError('RAW chunk cannot have fill_data set.')
676 if not self.input_offset:
677 raise ValueError('RAW chunk must have input_offset set.')
678 elif self.chunk_type == self.TYPE_FILL:
679 if self.fill_data is None:
680 raise ValueError('FILL chunk must have fill_data set.')
681 if self.input_offset:
682 raise ValueError('FILL chunk cannot have input_offset set.')
683 elif self.chunk_type == self.TYPE_DONT_CARE:
684 if self.fill_data is not None:
685 raise ValueError('DONT_CARE chunk cannot have fill_data set.')
686 if self.input_offset:
687 raise ValueError('DONT_CARE chunk cannot have input_offset set.')
688 else:
689 raise ValueError('Invalid chunk type')
690
691
692class ImageHandler(object):
693 """Abstraction for image I/O with support for Android sparse images.
694
695 This class provides an interface for working with image files that
696 may be using the Android Sparse Image format. When an instance is
697 constructed, we test whether it's an Android sparse file. If so,
698 operations will be on the sparse file by interpreting the sparse
699 format, otherwise they will be directly on the file. Either way the
700 operations do the same.
701
702 For reading, this interface mimics a file object - it has seek(),
703 tell(), and read() methods. For writing, only truncation
704 (truncate()) and appending is supported (append_raw() and
705 append_dont_care()). Additionally, data can only be written in units
706 of the block size.
707
708 Attributes:
David Zeuthen49936b42018-08-07 17:38:58 -0400709 filename: Name of file.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400710 is_sparse: Whether the file being operated on is sparse.
711 block_size: The block size, typically 4096.
712 image_size: The size of the unsparsified file.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400713 """
714 # See system/core/libsparse/sparse_format.h for details.
715 MAGIC = 0xed26ff3a
716 HEADER_FORMAT = '<I4H4I'
717
718 # These are formats and offset of just the |total_chunks| and
719 # |total_blocks| fields.
720 NUM_CHUNKS_AND_BLOCKS_FORMAT = '<II'
721 NUM_CHUNKS_AND_BLOCKS_OFFSET = 16
722
723 def __init__(self, image_filename):
724 """Initializes an image handler.
725
726 Arguments:
727 image_filename: The name of the file to operate on.
728
729 Raises:
730 ValueError: If data in the file is invalid.
731 """
David Zeuthen49936b42018-08-07 17:38:58 -0400732 self.filename = image_filename
Jan Monsch23e0c622019-12-11 11:23:58 +0100733 self._num_total_blocks = 0
734 self._num_total_chunks = 0
735 self._file_pos = 0
David Zeuthena4fee8b2016-08-22 15:20:43 -0400736 self._read_header()
737
738 def _read_header(self):
739 """Initializes internal data structures used for reading file.
740
741 This may be called multiple times and is typically called after
742 modifying the file (e.g. appending, truncation).
743
744 Raises:
745 ValueError: If data in the file is invalid.
746 """
747 self.is_sparse = False
748 self.block_size = 4096
749 self._file_pos = 0
David Zeuthen49936b42018-08-07 17:38:58 -0400750 self._image = open(self.filename, 'r+b')
David Zeuthena4fee8b2016-08-22 15:20:43 -0400751 self._image.seek(0, os.SEEK_END)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400752 self.image_size = self._image.tell()
753
754 self._image.seek(0, os.SEEK_SET)
755 header_bin = self._image.read(struct.calcsize(self.HEADER_FORMAT))
756 (magic, major_version, minor_version, file_hdr_sz, chunk_hdr_sz,
757 block_size, self._num_total_blocks, self._num_total_chunks,
758 _) = struct.unpack(self.HEADER_FORMAT, header_bin)
759 if magic != self.MAGIC:
760 # Not a sparse image, our job here is done.
761 return
762 if not (major_version == 1 and minor_version == 0):
763 raise ValueError('Encountered sparse image format version {}.{} but '
764 'only 1.0 is supported'.format(major_version,
765 minor_version))
766 if file_hdr_sz != struct.calcsize(self.HEADER_FORMAT):
767 raise ValueError('Unexpected file_hdr_sz value {}.'.
768 format(file_hdr_sz))
769 if chunk_hdr_sz != struct.calcsize(ImageChunk.FORMAT):
770 raise ValueError('Unexpected chunk_hdr_sz value {}.'.
771 format(chunk_hdr_sz))
772
773 self.block_size = block_size
774
775 # Build an list of chunks by parsing the file.
776 self._chunks = []
777
778 # Find the smallest offset where only "Don't care" chunks
779 # follow. This will be the size of the content in the sparse
780 # image.
781 offset = 0
782 output_offset = 0
Jan Monsch23e0c622019-12-11 11:23:58 +0100783 for _ in range(1, self._num_total_chunks + 1):
David Zeuthena4fee8b2016-08-22 15:20:43 -0400784 chunk_offset = self._image.tell()
785
786 header_bin = self._image.read(struct.calcsize(ImageChunk.FORMAT))
787 (chunk_type, _, chunk_sz, total_sz) = struct.unpack(ImageChunk.FORMAT,
788 header_bin)
789 data_sz = total_sz - struct.calcsize(ImageChunk.FORMAT)
790
David Zeuthena4fee8b2016-08-22 15:20:43 -0400791 if chunk_type == ImageChunk.TYPE_RAW:
792 if data_sz != (chunk_sz * self.block_size):
793 raise ValueError('Raw chunk input size ({}) does not match output '
794 'size ({})'.
795 format(data_sz, chunk_sz*self.block_size))
796 self._chunks.append(ImageChunk(ImageChunk.TYPE_RAW,
797 chunk_offset,
798 output_offset,
799 chunk_sz*self.block_size,
800 self._image.tell(),
801 None))
Dan Willemsen8e306ae2018-09-17 20:03:23 -0700802 self._image.seek(data_sz, os.SEEK_CUR)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400803
804 elif chunk_type == ImageChunk.TYPE_FILL:
805 if data_sz != 4:
806 raise ValueError('Fill chunk should have 4 bytes of fill, but this '
807 'has {}'.format(data_sz))
808 fill_data = self._image.read(4)
809 self._chunks.append(ImageChunk(ImageChunk.TYPE_FILL,
810 chunk_offset,
811 output_offset,
812 chunk_sz*self.block_size,
813 None,
814 fill_data))
815 elif chunk_type == ImageChunk.TYPE_DONT_CARE:
816 if data_sz != 0:
817 raise ValueError('Don\'t care chunk input size is non-zero ({})'.
818 format(data_sz))
David Zeuthena4fee8b2016-08-22 15:20:43 -0400819 self._chunks.append(ImageChunk(ImageChunk.TYPE_DONT_CARE,
820 chunk_offset,
821 output_offset,
822 chunk_sz*self.block_size,
823 None,
824 None))
825 elif chunk_type == ImageChunk.TYPE_CRC32:
826 if data_sz != 4:
827 raise ValueError('CRC32 chunk should have 4 bytes of CRC, but '
828 'this has {}'.format(data_sz))
829 self._image.read(4)
830 else:
831 raise ValueError('Unknown chunk type {}'.format(chunk_type))
832
833 offset += chunk_sz
834 output_offset += chunk_sz*self.block_size
835
836 # Record where sparse data end.
837 self._sparse_end = self._image.tell()
838
839 # Now that we've traversed all chunks, sanity check.
840 if self._num_total_blocks != offset:
841 raise ValueError('The header said we should have {} output blocks, '
842 'but we saw {}'.format(self._num_total_blocks, offset))
843 junk_len = len(self._image.read())
844 if junk_len > 0:
845 raise ValueError('There were {} bytes of extra data at the end of the '
846 'file.'.format(junk_len))
847
David Zeuthen09692692016-09-30 16:16:40 -0400848 # Assign |image_size|.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400849 self.image_size = output_offset
David Zeuthena4fee8b2016-08-22 15:20:43 -0400850
851 # This is used when bisecting in read() to find the initial slice.
852 self._chunk_output_offsets = [i.output_offset for i in self._chunks]
853
854 self.is_sparse = True
855
856 def _update_chunks_and_blocks(self):
857 """Helper function to update the image header.
858
859 The the |total_chunks| and |total_blocks| fields in the header
860 will be set to value of the |_num_total_blocks| and
861 |_num_total_chunks| attributes.
862
863 """
864 self._image.seek(self.NUM_CHUNKS_AND_BLOCKS_OFFSET, os.SEEK_SET)
865 self._image.write(struct.pack(self.NUM_CHUNKS_AND_BLOCKS_FORMAT,
866 self._num_total_blocks,
867 self._num_total_chunks))
868
869 def append_dont_care(self, num_bytes):
870 """Appends a DONT_CARE chunk to the sparse file.
871
872 The given number of bytes must be a multiple of the block size.
873
874 Arguments:
875 num_bytes: Size in number of bytes of the DONT_CARE chunk.
876 """
877 assert num_bytes % self.block_size == 0
878
879 if not self.is_sparse:
880 self._image.seek(0, os.SEEK_END)
881 # This is more efficient that writing NUL bytes since it'll add
882 # a hole on file systems that support sparse files (native
883 # sparse, not Android sparse).
884 self._image.truncate(self._image.tell() + num_bytes)
885 self._read_header()
886 return
887
888 self._num_total_chunks += 1
Jan Monsch23e0c622019-12-11 11:23:58 +0100889 self._num_total_blocks += num_bytes // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -0400890 self._update_chunks_and_blocks()
891
892 self._image.seek(self._sparse_end, os.SEEK_SET)
893 self._image.write(struct.pack(ImageChunk.FORMAT,
894 ImageChunk.TYPE_DONT_CARE,
895 0, # Reserved
Jan Monsch23e0c622019-12-11 11:23:58 +0100896 num_bytes // self.block_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -0400897 struct.calcsize(ImageChunk.FORMAT)))
898 self._read_header()
899
900 def append_raw(self, data):
901 """Appends a RAW chunk to the sparse file.
902
903 The length of the given data must be a multiple of the block size.
904
905 Arguments:
Jan Monsch8347da92020-04-08 12:41:49 +0200906 data: Data to append as bytes.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400907 """
908 assert len(data) % self.block_size == 0
909
910 if not self.is_sparse:
911 self._image.seek(0, os.SEEK_END)
912 self._image.write(data)
913 self._read_header()
914 return
915
916 self._num_total_chunks += 1
Jan Monsch23e0c622019-12-11 11:23:58 +0100917 self._num_total_blocks += len(data) // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -0400918 self._update_chunks_and_blocks()
919
920 self._image.seek(self._sparse_end, os.SEEK_SET)
921 self._image.write(struct.pack(ImageChunk.FORMAT,
922 ImageChunk.TYPE_RAW,
923 0, # Reserved
Jan Monsch23e0c622019-12-11 11:23:58 +0100924 len(data) // self.block_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -0400925 len(data) +
926 struct.calcsize(ImageChunk.FORMAT)))
927 self._image.write(data)
928 self._read_header()
929
930 def append_fill(self, fill_data, size):
931 """Appends a fill chunk to the sparse file.
932
933 The total length of the fill data must be a multiple of the block size.
934
935 Arguments:
936 fill_data: Fill data to append - must be four bytes.
937 size: Number of chunk - must be a multiple of four and the block size.
938 """
939 assert len(fill_data) == 4
940 assert size % 4 == 0
941 assert size % self.block_size == 0
942
943 if not self.is_sparse:
944 self._image.seek(0, os.SEEK_END)
Jan Monsch23e0c622019-12-11 11:23:58 +0100945 self._image.write(fill_data * (size//4))
David Zeuthena4fee8b2016-08-22 15:20:43 -0400946 self._read_header()
947 return
948
949 self._num_total_chunks += 1
Jan Monsch23e0c622019-12-11 11:23:58 +0100950 self._num_total_blocks += size // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -0400951 self._update_chunks_and_blocks()
952
953 self._image.seek(self._sparse_end, os.SEEK_SET)
954 self._image.write(struct.pack(ImageChunk.FORMAT,
955 ImageChunk.TYPE_FILL,
956 0, # Reserved
Jan Monsch23e0c622019-12-11 11:23:58 +0100957 size // self.block_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -0400958 4 + struct.calcsize(ImageChunk.FORMAT)))
959 self._image.write(fill_data)
960 self._read_header()
961
962 def seek(self, offset):
963 """Sets the cursor position for reading from unsparsified file.
964
965 Arguments:
966 offset: Offset to seek to from the beginning of the file.
Jan Monsch77cd2022019-12-10 17:18:04 +0100967
968 Raises:
969 RuntimeError: If the given offset is negative.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400970 """
Lonnie Liu6b5a33e2017-10-31 18:01:09 -0700971 if offset < 0:
Jan Monsch8347da92020-04-08 12:41:49 +0200972 raise RuntimeError('Seeking with negative offset: {}'.format(offset))
David Zeuthena4fee8b2016-08-22 15:20:43 -0400973 self._file_pos = offset
974
975 def read(self, size):
976 """Reads data from the unsparsified file.
977
978 This method may return fewer than |size| bytes of data if the end
979 of the file was encountered.
980
981 The file cursor for reading is advanced by the number of bytes
982 read.
983
984 Arguments:
985 size: Number of bytes to read.
986
987 Returns:
Jan Monsch8347da92020-04-08 12:41:49 +0200988 The data as bytes.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400989 """
990 if not self.is_sparse:
991 self._image.seek(self._file_pos)
992 data = self._image.read(size)
993 self._file_pos += len(data)
994 return data
995
996 # Iterate over all chunks.
997 chunk_idx = bisect.bisect_right(self._chunk_output_offsets,
998 self._file_pos) - 1
999 data = bytearray()
1000 to_go = size
1001 while to_go > 0:
1002 chunk = self._chunks[chunk_idx]
1003 chunk_pos_offset = self._file_pos - chunk.output_offset
1004 chunk_pos_to_go = min(chunk.output_size - chunk_pos_offset, to_go)
1005
1006 if chunk.chunk_type == ImageChunk.TYPE_RAW:
1007 self._image.seek(chunk.input_offset + chunk_pos_offset)
1008 data.extend(self._image.read(chunk_pos_to_go))
1009 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
Jan Monsch23e0c622019-12-11 11:23:58 +01001010 all_data = chunk.fill_data*(chunk_pos_to_go // len(chunk.fill_data) + 2)
David Zeuthena4fee8b2016-08-22 15:20:43 -04001011 offset_mod = chunk_pos_offset % len(chunk.fill_data)
1012 data.extend(all_data[offset_mod:(offset_mod + chunk_pos_to_go)])
1013 else:
1014 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
Jan Monsch8347da92020-04-08 12:41:49 +02001015 data.extend(b'\0' * chunk_pos_to_go)
David Zeuthena4fee8b2016-08-22 15:20:43 -04001016
1017 to_go -= chunk_pos_to_go
1018 self._file_pos += chunk_pos_to_go
1019 chunk_idx += 1
1020 # Generate partial read in case of EOF.
1021 if chunk_idx >= len(self._chunks):
1022 break
1023
Jan Monsch8347da92020-04-08 12:41:49 +02001024 return bytes(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04001025
1026 def tell(self):
1027 """Returns the file cursor position for reading from unsparsified file.
1028
1029 Returns:
1030 The file cursor position for reading.
1031 """
1032 return self._file_pos
1033
1034 def truncate(self, size):
1035 """Truncates the unsparsified file.
1036
1037 Arguments:
1038 size: Desired size of unsparsified file.
1039
1040 Raises:
1041 ValueError: If desired size isn't a multiple of the block size.
1042 """
1043 if not self.is_sparse:
1044 self._image.truncate(size)
1045 self._read_header()
1046 return
1047
1048 if size % self.block_size != 0:
1049 raise ValueError('Cannot truncate to a size which is not a multiple '
1050 'of the block size')
1051
1052 if size == self.image_size:
1053 # Trivial where there's nothing to do.
1054 return
1055 elif size < self.image_size:
1056 chunk_idx = bisect.bisect_right(self._chunk_output_offsets, size) - 1
1057 chunk = self._chunks[chunk_idx]
1058 if chunk.output_offset != size:
1059 # Truncation in the middle of a trunk - need to keep the chunk
1060 # and modify it.
1061 chunk_idx_for_update = chunk_idx + 1
1062 num_to_keep = size - chunk.output_offset
1063 assert num_to_keep % self.block_size == 0
1064 if chunk.chunk_type == ImageChunk.TYPE_RAW:
1065 truncate_at = (chunk.chunk_offset +
1066 struct.calcsize(ImageChunk.FORMAT) + num_to_keep)
1067 data_sz = num_to_keep
1068 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
1069 truncate_at = (chunk.chunk_offset +
1070 struct.calcsize(ImageChunk.FORMAT) + 4)
1071 data_sz = 4
1072 else:
1073 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
1074 truncate_at = chunk.chunk_offset + struct.calcsize(ImageChunk.FORMAT)
1075 data_sz = 0
Jan Monsch23e0c622019-12-11 11:23:58 +01001076 chunk_sz = num_to_keep // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -04001077 total_sz = data_sz + struct.calcsize(ImageChunk.FORMAT)
1078 self._image.seek(chunk.chunk_offset)
1079 self._image.write(struct.pack(ImageChunk.FORMAT,
1080 chunk.chunk_type,
1081 0, # Reserved
1082 chunk_sz,
1083 total_sz))
1084 chunk.output_size = num_to_keep
1085 else:
1086 # Truncation at trunk boundary.
1087 truncate_at = chunk.chunk_offset
1088 chunk_idx_for_update = chunk_idx
1089
1090 self._num_total_chunks = chunk_idx_for_update
1091 self._num_total_blocks = 0
1092 for i in range(0, chunk_idx_for_update):
Jan Monsch23e0c622019-12-11 11:23:58 +01001093 self._num_total_blocks += self._chunks[i].output_size // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -04001094 self._update_chunks_and_blocks()
1095 self._image.truncate(truncate_at)
1096
1097 # We've modified the file so re-read all data.
1098 self._read_header()
1099 else:
1100 # Truncating to grow - just add a DONT_CARE section.
1101 self.append_dont_care(size - self.image_size)
1102
1103
David Zeuthen21e95262016-07-27 17:58:40 -04001104class AvbDescriptor(object):
1105 """Class for AVB descriptor.
1106
1107 See the |AvbDescriptor| C struct for more information.
1108
1109 Attributes:
1110 tag: The tag identifying what kind of descriptor this is.
1111 data: The data in the descriptor.
1112 """
1113
1114 SIZE = 16
1115 FORMAT_STRING = ('!QQ') # tag, num_bytes_following (descriptor header)
1116
1117 def __init__(self, data):
1118 """Initializes a new property descriptor.
1119
1120 Arguments:
1121 data: If not None, must be a bytearray().
1122
1123 Raises:
1124 LookupError: If the given descriptor is malformed.
1125 """
1126 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1127
1128 if data:
1129 (self.tag, num_bytes_following) = (
1130 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1131 self.data = data[self.SIZE:self.SIZE + num_bytes_following]
1132 else:
1133 self.tag = None
1134 self.data = None
1135
1136 def print_desc(self, o):
1137 """Print the descriptor.
1138
1139 Arguments:
1140 o: The object to write the output to.
1141 """
1142 o.write(' Unknown descriptor:\n')
1143 o.write(' Tag: {}\n'.format(self.tag))
1144 if len(self.data) < 256:
1145 o.write(' Data: {} ({} bytes)\n'.format(
1146 repr(str(self.data)), len(self.data)))
1147 else:
1148 o.write(' Data: {} bytes\n'.format(len(self.data)))
1149
1150 def encode(self):
1151 """Serializes the descriptor.
1152
1153 Returns:
1154 A bytearray() with the descriptor data.
1155 """
1156 num_bytes_following = len(self.data)
1157 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1158 padding_size = nbf_with_padding - num_bytes_following
1159 desc = struct.pack(self.FORMAT_STRING, self.tag, nbf_with_padding)
1160 padding = struct.pack(str(padding_size) + 'x')
1161 ret = desc + self.data + padding
1162 return bytearray(ret)
1163
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001164 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001165 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001166 """Verifies contents of the descriptor - used in verify_image sub-command.
1167
1168 Arguments:
1169 image_dir: The directory of the file being verified.
1170 image_ext: The extension of the file being verified (e.g. '.img').
1171 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001172 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001173 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001174 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1175 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001176
1177 Returns:
1178 True if the descriptor verifies, False otherwise.
1179 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01001180 # Deletes unused parameters to prevent pylint warning unused-argument.
1181 del image_dir, image_ext, expected_chain_partitions_map
1182 del image_containing_descriptor, accept_zeroed_hashtree
1183
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001184 # Nothing to do.
1185 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001186
Jan Monscheeb28b62019-12-05 16:17:09 +01001187
David Zeuthen21e95262016-07-27 17:58:40 -04001188class AvbPropertyDescriptor(AvbDescriptor):
1189 """A class for property descriptors.
1190
1191 See the |AvbPropertyDescriptor| C struct for more information.
1192
1193 Attributes:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001194 key: The key as string.
1195 value: The value as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001196 """
1197
1198 TAG = 0
1199 SIZE = 32
1200 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001201 'Q' # key size (bytes)
1202 'Q') # value size (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001203
1204 def __init__(self, data=None):
1205 """Initializes a new property descriptor.
1206
1207 Arguments:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001208 data: If not None, must be as bytes of size |SIZE|.
David Zeuthen21e95262016-07-27 17:58:40 -04001209
1210 Raises:
1211 LookupError: If the given descriptor is malformed.
1212 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001213 super(AvbPropertyDescriptor, self).__init__(None)
David Zeuthen21e95262016-07-27 17:58:40 -04001214 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1215
1216 if data:
1217 (tag, num_bytes_following, key_size,
1218 value_size) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
1219 expected_size = round_to_multiple(
1220 self.SIZE - 16 + key_size + 1 + value_size + 1, 8)
1221 if tag != self.TAG or num_bytes_following != expected_size:
1222 raise LookupError('Given data does not look like a property '
1223 'descriptor.')
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001224 try:
1225 self.key = data[self.SIZE:(self.SIZE + key_size)].decode('utf-8')
1226 except UnicodeDecodeError as e:
1227 raise LookupError('Key cannot be decoded as UTF-8: {}.'.format(e))
David Zeuthen21e95262016-07-27 17:58:40 -04001228 self.value = data[(self.SIZE + key_size + 1):(self.SIZE + key_size + 1 +
1229 value_size)]
1230 else:
1231 self.key = ''
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001232 self.value = b''
David Zeuthen21e95262016-07-27 17:58:40 -04001233
1234 def print_desc(self, o):
1235 """Print the descriptor.
1236
1237 Arguments:
1238 o: The object to write the output to.
1239 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001240 # Go forward with python 3, bytes are represented with the 'b' prefix,
1241 # e.g. b'foobar'. Thus, we trim off the 'b' to keep the print output
1242 # the same between python 2 and python 3.
1243 printable_value = repr(self.value)
1244 if printable_value.startswith('b\''):
1245 printable_value = printable_value[1:]
1246
David Zeuthen21e95262016-07-27 17:58:40 -04001247 if len(self.value) < 256:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001248 o.write(' Prop: {} -> {}\n'.format(self.key, printable_value))
David Zeuthen21e95262016-07-27 17:58:40 -04001249 else:
1250 o.write(' Prop: {} -> ({} bytes)\n'.format(self.key, len(self.value)))
1251
1252 def encode(self):
1253 """Serializes the descriptor.
1254
1255 Returns:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001256 The descriptor data as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001257 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001258 key_encoded = self.key.encode('utf-8')
1259 num_bytes_following = (
1260 self.SIZE + len(key_encoded) + len(self.value) + 2 - 16)
David Zeuthen21e95262016-07-27 17:58:40 -04001261 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1262 padding_size = nbf_with_padding - num_bytes_following
1263 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001264 len(key_encoded), len(self.value))
1265 ret = (desc + key_encoded + b'\0' + self.value + b'\0' +
1266 padding_size * b'\0')
1267 return ret
David Zeuthen21e95262016-07-27 17:58:40 -04001268
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001269 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001270 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001271 """Verifies contents of the descriptor - used in verify_image sub-command.
1272
1273 Arguments:
1274 image_dir: The directory of the file being verified.
1275 image_ext: The extension of the file being verified (e.g. '.img').
1276 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001277 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001278 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001279 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1280 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001281
1282 Returns:
1283 True if the descriptor verifies, False otherwise.
1284 """
1285 # Nothing to do.
1286 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001287
Jan Monscheeb28b62019-12-05 16:17:09 +01001288
David Zeuthen21e95262016-07-27 17:58:40 -04001289class AvbHashtreeDescriptor(AvbDescriptor):
1290 """A class for hashtree descriptors.
1291
1292 See the |AvbHashtreeDescriptor| C struct for more information.
1293
1294 Attributes:
1295 dm_verity_version: dm-verity version used.
1296 image_size: Size of the image, after rounding up to |block_size|.
1297 tree_offset: Offset of the hash tree in the file.
1298 tree_size: Size of the tree.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001299 data_block_size: Data block size.
1300 hash_block_size: Hash block size.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001301 fec_num_roots: Number of roots used for FEC (0 if FEC is not used).
1302 fec_offset: Offset of FEC data (0 if FEC is not used).
1303 fec_size: Size of FEC data (0 if FEC is not used).
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001304 hash_algorithm: Hash algorithm used as string.
1305 partition_name: Partition name as string.
1306 salt: Salt used as bytes.
1307 root_digest: Root digest as bytes.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001308 flags: Descriptor flags (see avb_hashtree_descriptor.h).
David Zeuthen21e95262016-07-27 17:58:40 -04001309 """
1310
1311 TAG = 1
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001312 RESERVED = 60
1313 SIZE = 120 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001314 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001315 'L' # dm-verity version used
1316 'Q' # image size (bytes)
1317 'Q' # tree offset (bytes)
1318 'Q' # tree size (bytes)
1319 'L' # data block size (bytes)
1320 'L' # hash block size (bytes)
1321 'L' # FEC number of roots
1322 'Q' # FEC offset (bytes)
1323 'Q' # FEC size (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001324 '32s' # hash algorithm used
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001325 'L' # partition name (bytes)
1326 'L' # salt length (bytes)
1327 'L' # root digest length (bytes)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001328 'L' + # flags
David Zeuthen5cb2db92016-10-27 15:14:14 -04001329 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001330
1331 def __init__(self, data=None):
1332 """Initializes a new hashtree descriptor.
1333
1334 Arguments:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001335 data: If not None, must be bytes of size |SIZE|.
David Zeuthen21e95262016-07-27 17:58:40 -04001336
1337 Raises:
1338 LookupError: If the given descriptor is malformed.
1339 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001340 super(AvbHashtreeDescriptor, self).__init__(None)
David Zeuthen21e95262016-07-27 17:58:40 -04001341 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1342
1343 if data:
1344 (tag, num_bytes_following, self.dm_verity_version, self.image_size,
1345 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001346 self.hash_block_size, self.fec_num_roots, self.fec_offset, self.fec_size,
1347 self.hash_algorithm, partition_name_len, salt_len,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001348 root_digest_len, self.flags, _) = struct.unpack(self.FORMAT_STRING,
1349 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001350 expected_size = round_to_multiple(
1351 self.SIZE - 16 + partition_name_len + salt_len + root_digest_len, 8)
1352 if tag != self.TAG or num_bytes_following != expected_size:
1353 raise LookupError('Given data does not look like a hashtree '
1354 'descriptor.')
1355 # Nuke NUL-bytes at the end.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001356 self.hash_algorithm = self.hash_algorithm.rstrip(b'\0').decode('ascii')
David Zeuthen21e95262016-07-27 17:58:40 -04001357 o = 0
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001358 try:
1359 self.partition_name = data[
1360 (self.SIZE + o):(self.SIZE + o + partition_name_len)
1361 ].decode('utf-8')
1362 except UnicodeDecodeError as e:
1363 raise LookupError('Partition name cannot be decoded as UTF-8: {}.'
1364 .format(e))
David Zeuthen21e95262016-07-27 17:58:40 -04001365 o += partition_name_len
1366 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1367 o += salt_len
1368 self.root_digest = data[(self.SIZE + o):(self.SIZE + o + root_digest_len)]
Jan Monsch6f27bb12020-04-07 07:33:26 +02001369 if root_digest_len != len(hashlib.new(self.hash_algorithm).digest()):
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001370 if root_digest_len != 0:
1371 raise LookupError('root_digest_len doesn\'t match hash algorithm')
David Zeuthen21e95262016-07-27 17:58:40 -04001372
1373 else:
1374 self.dm_verity_version = 0
1375 self.image_size = 0
1376 self.tree_offset = 0
1377 self.tree_size = 0
1378 self.data_block_size = 0
1379 self.hash_block_size = 0
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001380 self.fec_num_roots = 0
1381 self.fec_offset = 0
1382 self.fec_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001383 self.hash_algorithm = ''
1384 self.partition_name = ''
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001385 self.salt = b''
1386 self.root_digest = b''
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001387 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001388
1389 def print_desc(self, o):
1390 """Print the descriptor.
1391
1392 Arguments:
1393 o: The object to write the output to.
1394 """
1395 o.write(' Hashtree descriptor:\n')
1396 o.write(' Version of dm-verity: {}\n'.format(self.dm_verity_version))
1397 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1398 o.write(' Tree Offset: {}\n'.format(self.tree_offset))
1399 o.write(' Tree Size: {} bytes\n'.format(self.tree_size))
1400 o.write(' Data Block Size: {} bytes\n'.format(
1401 self.data_block_size))
1402 o.write(' Hash Block Size: {} bytes\n'.format(
1403 self.hash_block_size))
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001404 o.write(' FEC num roots: {}\n'.format(self.fec_num_roots))
1405 o.write(' FEC offset: {}\n'.format(self.fec_offset))
1406 o.write(' FEC size: {} bytes\n'.format(self.fec_size))
David Zeuthen21e95262016-07-27 17:58:40 -04001407 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1408 o.write(' Partition Name: {}\n'.format(self.partition_name))
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001409 o.write(' Salt: {}\n'.format(
1410 binascii.hexlify(self.salt).decode('ascii')))
1411 o.write(' Root Digest: {}\n'.format(
1412 binascii.hexlify(self.root_digest).decode('ascii')))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001413 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001414
1415 def encode(self):
1416 """Serializes the descriptor.
1417
1418 Returns:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001419 The descriptor data as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001420 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001421 hash_algorithm_encoded = self.hash_algorithm.encode('ascii')
1422 partition_name_encoded = self.partition_name.encode('utf-8')
1423 num_bytes_following = (self.SIZE + len(partition_name_encoded)
1424 + len(self.salt) + len(self.root_digest) - 16)
David Zeuthen21e95262016-07-27 17:58:40 -04001425 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1426 padding_size = nbf_with_padding - num_bytes_following
1427 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1428 self.dm_verity_version, self.image_size,
1429 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001430 self.hash_block_size, self.fec_num_roots,
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001431 self.fec_offset, self.fec_size, hash_algorithm_encoded,
1432 len(partition_name_encoded), len(self.salt),
1433 len(self.root_digest), self.flags, self.RESERVED * b'\0')
1434 ret = (desc + partition_name_encoded + self.salt + self.root_digest +
1435 padding_size * b'\0')
1436 return ret
David Zeuthen21e95262016-07-27 17:58:40 -04001437
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001438 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001439 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001440 """Verifies contents of the descriptor - used in verify_image sub-command.
1441
1442 Arguments:
1443 image_dir: The directory of the file being verified.
1444 image_ext: The extension of the file being verified (e.g. '.img').
1445 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001446 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001447 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001448 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1449 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001450
1451 Returns:
1452 True if the descriptor verifies, False otherwise.
1453 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01001454 if not self.partition_name:
Tao Bao558bd752019-09-18 18:18:34 -07001455 image_filename = image_containing_descriptor.filename
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001456 image = image_containing_descriptor
1457 else:
1458 image_filename = os.path.join(image_dir, self.partition_name + image_ext)
1459 image = ImageHandler(image_filename)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001460 # Generate the hashtree and checks that it matches what's in the file.
Jan Monsch6f27bb12020-04-07 07:33:26 +02001461 digest_size = len(hashlib.new(self.hash_algorithm).digest())
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001462 digest_padding = round_to_pow2(digest_size) - digest_size
1463 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
Jan Monscheeb28b62019-12-05 16:17:09 +01001464 self.image_size, self.data_block_size, digest_size + digest_padding)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001465 root_digest, hash_tree = generate_hash_tree(image, self.image_size,
1466 self.data_block_size,
1467 self.hash_algorithm, self.salt,
1468 digest_padding,
1469 hash_level_offsets,
1470 tree_size)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001471 # The root digest must match unless it is not embedded in the descriptor.
Jan Monsch23e0c622019-12-11 11:23:58 +01001472 if self.root_digest and root_digest != self.root_digest:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001473 sys.stderr.write('hashtree of {} does not match descriptor\n'.
1474 format(image_filename))
1475 return False
1476 # ... also check that the on-disk hashtree matches
1477 image.seek(self.tree_offset)
1478 hash_tree_ondisk = image.read(self.tree_size)
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001479 is_zeroed = (self.tree_size == 0) or (hash_tree_ondisk[0:8] == b'ZeRoHaSH')
David Zeuthen1394f762019-04-30 10:20:11 -04001480 if is_zeroed and accept_zeroed_hashtree:
Jan Monsch23e0c622019-12-11 11:23:58 +01001481 print('{}: skipping verification since hashtree is zeroed and '
1482 '--accept_zeroed_hashtree was given'
1483 .format(self.partition_name))
David Zeuthen1394f762019-04-30 10:20:11 -04001484 else:
1485 if hash_tree != hash_tree_ondisk:
1486 sys.stderr.write('hashtree of {} contains invalid data\n'.
Tao Bao558bd752019-09-18 18:18:34 -07001487 format(image_filename))
David Zeuthen1394f762019-04-30 10:20:11 -04001488 return False
Jan Monsch23e0c622019-12-11 11:23:58 +01001489 print('{}: Successfully verified {} hashtree of {} for image of {} bytes'
1490 .format(self.partition_name, self.hash_algorithm, image.filename,
1491 self.image_size))
Jan Monschfe00c0a2019-12-11 11:19:40 +01001492 # TODO(zeuthen): we could also verify that the FEC stored in the image is
1493 # correct but this a) currently requires the 'fec' binary; and b) takes a
1494 # long time; and c) is not strictly needed for verification purposes as
1495 # we've already verified the root hash.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001496 return True
1497
David Zeuthen21e95262016-07-27 17:58:40 -04001498
1499class AvbHashDescriptor(AvbDescriptor):
1500 """A class for hash descriptors.
1501
1502 See the |AvbHashDescriptor| C struct for more information.
1503
1504 Attributes:
1505 image_size: Image size, in bytes.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001506 hash_algorithm: Hash algorithm used as string.
1507 partition_name: Partition name as string.
1508 salt: Salt used as bytes.
1509 digest: The hash value of salt and data combined as bytes.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001510 flags: The descriptor flags (see avb_hash_descriptor.h).
David Zeuthen21e95262016-07-27 17:58:40 -04001511 """
1512
1513 TAG = 2
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001514 RESERVED = 60
1515 SIZE = 72 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001516 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001517 'Q' # image size (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001518 '32s' # hash algorithm used
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001519 'L' # partition name (bytes)
1520 'L' # salt length (bytes)
1521 'L' # digest length (bytes)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001522 'L' + # flags
David Zeuthen5cb2db92016-10-27 15:14:14 -04001523 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001524
1525 def __init__(self, data=None):
1526 """Initializes a new hash descriptor.
1527
1528 Arguments:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001529 data: If not None, must be bytes of size |SIZE|.
David Zeuthen21e95262016-07-27 17:58:40 -04001530
1531 Raises:
1532 LookupError: If the given descriptor is malformed.
1533 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001534 super(AvbHashDescriptor, self).__init__(None)
David Zeuthen21e95262016-07-27 17:58:40 -04001535 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1536
1537 if data:
1538 (tag, num_bytes_following, self.image_size, self.hash_algorithm,
1539 partition_name_len, salt_len,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001540 digest_len, self.flags, _) = struct.unpack(self.FORMAT_STRING,
1541 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001542 expected_size = round_to_multiple(
1543 self.SIZE - 16 + partition_name_len + salt_len + digest_len, 8)
1544 if tag != self.TAG or num_bytes_following != expected_size:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001545 raise LookupError('Given data does not look like a hash descriptor.')
David Zeuthen21e95262016-07-27 17:58:40 -04001546 # Nuke NUL-bytes at the end.
1547 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1548 o = 0
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001549 try:
1550 self.partition_name = data[
1551 (self.SIZE + o):(self.SIZE + o + partition_name_len)
1552 ].decode('utf-8')
1553 except UnicodeDecodeError as e:
1554 raise LookupError('Partition name cannot be decoded as UTF-8: {}.'
1555 .format(e))
David Zeuthen21e95262016-07-27 17:58:40 -04001556 o += partition_name_len
1557 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1558 o += salt_len
1559 self.digest = data[(self.SIZE + o):(self.SIZE + o + digest_len)]
Jan Monsch6f27bb12020-04-07 07:33:26 +02001560 if digest_len != len(hashlib.new(self.hash_algorithm).digest()):
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001561 if digest_len != 0:
1562 raise LookupError('digest_len doesn\'t match hash algorithm')
David Zeuthen21e95262016-07-27 17:58:40 -04001563
1564 else:
1565 self.image_size = 0
1566 self.hash_algorithm = ''
1567 self.partition_name = ''
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001568 self.salt = b''
1569 self.digest = b''
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001570 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001571
1572 def print_desc(self, o):
1573 """Print the descriptor.
1574
1575 Arguments:
1576 o: The object to write the output to.
1577 """
1578 o.write(' Hash descriptor:\n')
1579 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1580 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1581 o.write(' Partition Name: {}\n'.format(self.partition_name))
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001582 o.write(' Salt: {}\n'.format(
1583 binascii.hexlify(self.salt).decode('ascii')))
1584 o.write(' Digest: {}\n'.format(
1585 binascii.hexlify(self.digest).decode('ascii')))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001586 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001587
1588 def encode(self):
1589 """Serializes the descriptor.
1590
1591 Returns:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001592 The descriptor data as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001593 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001594 hash_algorithm_encoded = self.hash_algorithm.encode('ascii')
1595 partition_name_encoded = self.partition_name.encode('utf-8')
1596 num_bytes_following = (self.SIZE + len(partition_name_encoded) +
1597 len(self.salt) + len(self.digest) - 16)
David Zeuthen21e95262016-07-27 17:58:40 -04001598 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1599 padding_size = nbf_with_padding - num_bytes_following
1600 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001601 self.image_size, hash_algorithm_encoded,
1602 len(partition_name_encoded), len(self.salt),
1603 len(self.digest), self.flags, self.RESERVED * b'\0')
1604 ret = (desc + partition_name_encoded + self.salt + self.digest +
1605 padding_size * b'\0')
1606 return ret
David Zeuthen21e95262016-07-27 17:58:40 -04001607
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001608 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001609 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001610 """Verifies contents of the descriptor - used in verify_image sub-command.
1611
1612 Arguments:
1613 image_dir: The directory of the file being verified.
1614 image_ext: The extension of the file being verified (e.g. '.img').
1615 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001616 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001617 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001618 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1619 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001620
1621 Returns:
1622 True if the descriptor verifies, False otherwise.
1623 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01001624 if not self.partition_name:
Tao Bao558bd752019-09-18 18:18:34 -07001625 image_filename = image_containing_descriptor.filename
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001626 image = image_containing_descriptor
1627 else:
1628 image_filename = os.path.join(image_dir, self.partition_name + image_ext)
1629 image = ImageHandler(image_filename)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001630 data = image.read(self.image_size)
1631 ha = hashlib.new(self.hash_algorithm)
1632 ha.update(self.salt)
1633 ha.update(data)
1634 digest = ha.digest()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001635 # The digest must match unless there is no digest in the descriptor.
Jan Monsch23e0c622019-12-11 11:23:58 +01001636 if self.digest and digest != self.digest:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001637 sys.stderr.write('{} digest of {} does not match digest in descriptor\n'.
1638 format(self.hash_algorithm, image_filename))
1639 return False
Jan Monsch23e0c622019-12-11 11:23:58 +01001640 print('{}: Successfully verified {} hash of {} for image of {} bytes'
1641 .format(self.partition_name, self.hash_algorithm, image.filename,
1642 self.image_size))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001643 return True
1644
David Zeuthen21e95262016-07-27 17:58:40 -04001645
1646class AvbKernelCmdlineDescriptor(AvbDescriptor):
1647 """A class for kernel command-line descriptors.
1648
1649 See the |AvbKernelCmdlineDescriptor| C struct for more information.
1650
1651 Attributes:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001652 flags: Flags.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001653 kernel_cmdline: The kernel command-line as string.
David Zeuthen21e95262016-07-27 17:58:40 -04001654 """
1655
1656 TAG = 3
David Zeuthenfd41eb92016-11-17 12:24:47 -05001657 SIZE = 24
David Zeuthen21e95262016-07-27 17:58:40 -04001658 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001659 'L' # flags
1660 'L') # cmdline length (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001661
David Zeuthenfd41eb92016-11-17 12:24:47 -05001662 FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED = (1 << 0)
1663 FLAGS_USE_ONLY_IF_HASHTREE_DISABLED = (1 << 1)
1664
David Zeuthen21e95262016-07-27 17:58:40 -04001665 def __init__(self, data=None):
1666 """Initializes a new kernel cmdline descriptor.
1667
1668 Arguments:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001669 data: If not None, must be bytes of size |SIZE|.
David Zeuthen21e95262016-07-27 17:58:40 -04001670
1671 Raises:
1672 LookupError: If the given descriptor is malformed.
1673 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001674 super(AvbKernelCmdlineDescriptor, self).__init__(None)
David Zeuthen21e95262016-07-27 17:58:40 -04001675 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1676
1677 if data:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001678 (tag, num_bytes_following, self.flags, kernel_cmdline_length) = (
David Zeuthen21e95262016-07-27 17:58:40 -04001679 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1680 expected_size = round_to_multiple(self.SIZE - 16 + kernel_cmdline_length,
1681 8)
1682 if tag != self.TAG or num_bytes_following != expected_size:
1683 raise LookupError('Given data does not look like a kernel cmdline '
1684 'descriptor.')
1685 # Nuke NUL-bytes at the end.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001686 try:
1687 self.kernel_cmdline = data[
1688 self.SIZE:(self.SIZE + kernel_cmdline_length)].decode('utf-8')
1689 except UnicodeDecodeError as e:
1690 raise LookupError('Kernel command-line cannot be decoded as UTF-8: {}.'
1691 .format(e))
David Zeuthen21e95262016-07-27 17:58:40 -04001692 else:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001693 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001694 self.kernel_cmdline = ''
1695
1696 def print_desc(self, o):
1697 """Print the descriptor.
1698
1699 Arguments:
1700 o: The object to write the output to.
1701 """
1702 o.write(' Kernel Cmdline descriptor:\n')
David Zeuthenfd41eb92016-11-17 12:24:47 -05001703 o.write(' Flags: {}\n'.format(self.flags))
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001704 o.write(' Kernel Cmdline: \'{}\'\n'.format(self.kernel_cmdline))
David Zeuthen21e95262016-07-27 17:58:40 -04001705
1706 def encode(self):
1707 """Serializes the descriptor.
1708
1709 Returns:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001710 The descriptor data as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001711 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001712 kernel_cmd_encoded = self.kernel_cmdline.encode('utf-8')
1713 num_bytes_following = (self.SIZE + len(kernel_cmd_encoded) - 16)
David Zeuthen21e95262016-07-27 17:58:40 -04001714 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1715 padding_size = nbf_with_padding - num_bytes_following
1716 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001717 self.flags, len(kernel_cmd_encoded))
1718 ret = desc + kernel_cmd_encoded + padding_size * b'\0'
1719 return ret
David Zeuthen21e95262016-07-27 17:58:40 -04001720
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001721 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001722 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001723 """Verifies contents of the descriptor - used in verify_image sub-command.
1724
1725 Arguments:
1726 image_dir: The directory of the file being verified.
1727 image_ext: The extension of the file being verified (e.g. '.img').
1728 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001729 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001730 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001731 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1732 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001733
1734 Returns:
1735 True if the descriptor verifies, False otherwise.
1736 """
1737 # Nothing to verify.
1738 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001739
Jan Monscheeb28b62019-12-05 16:17:09 +01001740
David Zeuthen21e95262016-07-27 17:58:40 -04001741class AvbChainPartitionDescriptor(AvbDescriptor):
1742 """A class for chained partition descriptors.
1743
1744 See the |AvbChainPartitionDescriptor| C struct for more information.
1745
1746 Attributes:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001747 rollback_index_location: The rollback index location to use.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001748 partition_name: Partition name as string.
1749 public_key: The public key as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001750 """
1751
1752 TAG = 4
David Zeuthen5cb2db92016-10-27 15:14:14 -04001753 RESERVED = 64
1754 SIZE = 28 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001755 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001756 'L' # rollback_index_location
1757 'L' # partition_name_size (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001758 'L' + # public_key_size (bytes)
1759 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001760
1761 def __init__(self, data=None):
1762 """Initializes a new chain partition descriptor.
1763
1764 Arguments:
1765 data: If not None, must be a bytearray of size |SIZE|.
1766
1767 Raises:
1768 LookupError: If the given descriptor is malformed.
1769 """
1770 AvbDescriptor.__init__(self, None)
1771 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1772
1773 if data:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001774 (tag, num_bytes_following, self.rollback_index_location,
1775 partition_name_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001776 public_key_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001777 expected_size = round_to_multiple(
1778 self.SIZE - 16 + partition_name_len + public_key_len, 8)
1779 if tag != self.TAG or num_bytes_following != expected_size:
1780 raise LookupError('Given data does not look like a chain partition '
1781 'descriptor.')
1782 o = 0
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001783 try:
1784 self.partition_name = data[
1785 (self.SIZE + o):(self.SIZE + o + partition_name_len)
1786 ].decode('utf-8')
1787 except UnicodeDecodeError as e:
1788 raise LookupError('Partition name cannot be decoded as UTF-8: {}.'
1789 .format(e))
David Zeuthen21e95262016-07-27 17:58:40 -04001790 o += partition_name_len
1791 self.public_key = data[(self.SIZE + o):(self.SIZE + o + public_key_len)]
1792
1793 else:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001794 self.rollback_index_location = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001795 self.partition_name = ''
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001796 self.public_key = b''
David Zeuthen21e95262016-07-27 17:58:40 -04001797
1798 def print_desc(self, o):
1799 """Print the descriptor.
1800
1801 Arguments:
1802 o: The object to write the output to.
1803 """
1804 o.write(' Chain Partition descriptor:\n')
David Zeuthen40ee1da2016-11-23 15:14:49 -05001805 o.write(' Partition Name: {}\n'.format(self.partition_name))
1806 o.write(' Rollback Index Location: {}\n'.format(
1807 self.rollback_index_location))
David Zeuthen21e95262016-07-27 17:58:40 -04001808 # Just show the SHA1 of the key, for size reasons.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001809 pubkey_digest = hashlib.sha1(self.public_key).hexdigest()
1810 o.write(' Public key (sha1): {}\n'.format(pubkey_digest))
David Zeuthen21e95262016-07-27 17:58:40 -04001811
1812 def encode(self):
1813 """Serializes the descriptor.
1814
1815 Returns:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001816 The descriptor data as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001817 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001818 partition_name_encoded = self.partition_name.encode('utf-8')
David Zeuthen21e95262016-07-27 17:58:40 -04001819 num_bytes_following = (
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001820 self.SIZE + len(partition_name_encoded) + len(self.public_key) - 16)
David Zeuthen21e95262016-07-27 17:58:40 -04001821 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1822 padding_size = nbf_with_padding - num_bytes_following
1823 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001824 self.rollback_index_location,
1825 len(partition_name_encoded), len(self.public_key),
1826 self.RESERVED * b'\0')
1827 ret = desc + partition_name_encoded + self.public_key + padding_size * b'\0'
1828 return ret
David Zeuthen21e95262016-07-27 17:58:40 -04001829
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001830 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001831 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001832 """Verifies contents of the descriptor - used in verify_image sub-command.
1833
1834 Arguments:
1835 image_dir: The directory of the file being verified.
1836 image_ext: The extension of the file being verified (e.g. '.img').
1837 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001838 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001839 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001840 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1841 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001842
1843 Returns:
1844 True if the descriptor verifies, False otherwise.
1845 """
1846 value = expected_chain_partitions_map.get(self.partition_name)
1847 if not value:
1848 sys.stderr.write('No expected chain partition for partition {}. Use '
1849 '--expected_chain_partition to specify expected '
David Zeuthene947cb62019-01-25 15:27:08 -05001850 'contents or --follow_chain_partitions.\n'.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001851 format(self.partition_name))
1852 return False
1853 rollback_index_location, pk_blob = value
1854
1855 if self.rollback_index_location != rollback_index_location:
1856 sys.stderr.write('Expected rollback_index_location {} does not '
1857 'match {} in descriptor for partition {}\n'.
1858 format(rollback_index_location,
1859 self.rollback_index_location,
1860 self.partition_name))
1861 return False
1862
1863 if self.public_key != pk_blob:
1864 sys.stderr.write('Expected public key blob does not match public '
1865 'key blob in descriptor for partition {}\n'.
1866 format(self.partition_name))
1867 return False
1868
Jan Monsch23e0c622019-12-11 11:23:58 +01001869 print('{}: Successfully verified chain partition descriptor matches '
1870 'expected data'.format(self.partition_name))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001871
1872 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001873
1874DESCRIPTOR_CLASSES = [
1875 AvbPropertyDescriptor, AvbHashtreeDescriptor, AvbHashDescriptor,
1876 AvbKernelCmdlineDescriptor, AvbChainPartitionDescriptor
1877]
1878
1879
1880def parse_descriptors(data):
1881 """Parses a blob of data into descriptors.
1882
1883 Arguments:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001884 data: Encoded descriptors as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001885
1886 Returns:
1887 A list of instances of objects derived from AvbDescriptor. For
1888 unknown descriptors, the class AvbDescriptor is used.
1889 """
1890 o = 0
1891 ret = []
1892 while o < len(data):
1893 tag, nb_following = struct.unpack('!2Q', data[o:o + 16])
1894 if tag < len(DESCRIPTOR_CLASSES):
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001895 clazz = DESCRIPTOR_CLASSES[tag]
David Zeuthen21e95262016-07-27 17:58:40 -04001896 else:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001897 clazz = AvbDescriptor
1898 ret.append(clazz(data[o:o + 16 + nb_following]))
David Zeuthen21e95262016-07-27 17:58:40 -04001899 o += 16 + nb_following
1900 return ret
1901
1902
1903class AvbFooter(object):
1904 """A class for parsing and writing footers.
1905
1906 Footers are stored at the end of partitions and point to where the
1907 AvbVBMeta blob is located. They also contain the original size of
1908 the image before AVB information was added.
1909
1910 Attributes:
1911 magic: Magic for identifying the footer, see |MAGIC|.
1912 version_major: The major version of avbtool that wrote the footer.
1913 version_minor: The minor version of avbtool that wrote the footer.
1914 original_image_size: Original image size.
1915 vbmeta_offset: Offset of where the AvbVBMeta blob is stored.
1916 vbmeta_size: Size of the AvbVBMeta blob.
1917 """
1918
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001919 MAGIC = b'AVBf'
David Zeuthen21e95262016-07-27 17:58:40 -04001920 SIZE = 64
1921 RESERVED = 28
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001922 FOOTER_VERSION_MAJOR = AVB_FOOTER_VERSION_MAJOR
1923 FOOTER_VERSION_MINOR = AVB_FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001924 FORMAT_STRING = ('!4s2L' # magic, 2 x version.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001925 'Q' # Original image size.
1926 'Q' # Offset of VBMeta blob.
1927 'Q' + # Size of VBMeta blob.
David Zeuthen21e95262016-07-27 17:58:40 -04001928 str(RESERVED) + 'x') # padding for reserved bytes
1929
1930 def __init__(self, data=None):
1931 """Initializes a new footer object.
1932
1933 Arguments:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001934 data: If not None, must be bytes of size 4096.
David Zeuthen21e95262016-07-27 17:58:40 -04001935
1936 Raises:
1937 LookupError: If the given footer is malformed.
1938 struct.error: If the given data has no footer.
1939 """
1940 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1941
1942 if data:
1943 (self.magic, self.version_major, self.version_minor,
1944 self.original_image_size, self.vbmeta_offset,
1945 self.vbmeta_size) = struct.unpack(self.FORMAT_STRING, data)
1946 if self.magic != self.MAGIC:
David Zeuthen8b6973b2016-09-20 12:39:49 -04001947 raise LookupError('Given data does not look like a AVB footer.')
David Zeuthen21e95262016-07-27 17:58:40 -04001948 else:
1949 self.magic = self.MAGIC
David Zeuthene3cadca2017-02-22 21:25:46 -05001950 self.version_major = self.FOOTER_VERSION_MAJOR
1951 self.version_minor = self.FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001952 self.original_image_size = 0
1953 self.vbmeta_offset = 0
1954 self.vbmeta_size = 0
1955
David Zeuthena4fee8b2016-08-22 15:20:43 -04001956 def encode(self):
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001957 """Serializes the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001958
David Zeuthena4fee8b2016-08-22 15:20:43 -04001959 Returns:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02001960 The footer as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04001961 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04001962 return struct.pack(self.FORMAT_STRING, self.magic, self.version_major,
1963 self.version_minor, self.original_image_size,
1964 self.vbmeta_offset, self.vbmeta_size)
David Zeuthen21e95262016-07-27 17:58:40 -04001965
1966
1967class AvbVBMetaHeader(object):
David Zeuthen8b6973b2016-09-20 12:39:49 -04001968 """A class for parsing and writing AVB vbmeta images.
David Zeuthen21e95262016-07-27 17:58:40 -04001969
Jan Monschfe00c0a2019-12-11 11:19:40 +01001970 The attributes correspond to the |AvbVBMetaImageHeader| struct defined in
1971 avb_vbmeta_image.h.
1972
David Zeuthen21e95262016-07-27 17:58:40 -04001973 Attributes:
Jan Monschfe00c0a2019-12-11 11:19:40 +01001974 magic: Four bytes equal to "AVB0" (AVB_MAGIC).
1975 required_libavb_version_major: The major version of libavb required for this
1976 header.
1977 required_libavb_version_minor: The minor version of libavb required for this
1978 header.
1979 authentication_data_block_size: The size of the signature block.
1980 auxiliary_data_block_size: The size of the auxiliary data block.
1981 algorithm_type: The verification algorithm used, see |AvbAlgorithmType|
1982 enum.
1983 hash_offset: Offset into the "Authentication data" block of hash data.
1984 hash_size: Length of the hash data.
1985 signature_offset: Offset into the "Authentication data" block of signature
1986 data.
1987 signature_size: Length of the signature data.
1988 public_key_offset: Offset into the "Auxiliary data" block of public key
1989 data.
1990 public_key_size: Length of the public key data.
1991 public_key_metadata_offset: Offset into the "Auxiliary data" block of public
1992 key metadata.
1993 public_key_metadata_size: Length of the public key metadata. Must be set to
1994 zero if there is no public key metadata.
1995 descriptors_offset: Offset into the "Auxiliary data" block of descriptor
1996 data.
1997 descriptors_size: Length of descriptor data.
1998 rollback_index: The rollback index which can be used to prevent rollback to
1999 older versions.
2000 flags: Flags from the AvbVBMetaImageFlags enumeration. This must be set to
2001 zero if the vbmeta image is not a top-level image.
2002 release_string: The release string from avbtool, e.g. "avbtool 1.0.0" or
2003 "avbtool 1.0.0 xyz_board Git-234abde89". Is guaranteed to be NUL
2004 terminated. Applications must not make assumptions about how this
2005 string is formatted.
David Zeuthen21e95262016-07-27 17:58:40 -04002006 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002007 MAGIC = b'AVB0'
David Zeuthen21e95262016-07-27 17:58:40 -04002008 SIZE = 256
2009
David Zeuthene3cadca2017-02-22 21:25:46 -05002010 # Keep in sync with |reserved0| and |reserved| field of
2011 # |AvbVBMetaImageHeader|.
2012 RESERVED0 = 4
2013 RESERVED = 80
David Zeuthen21e95262016-07-27 17:58:40 -04002014
2015 # Keep in sync with |AvbVBMetaImageHeader|.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002016 FORMAT_STRING = ('!4s2L' # magic, 2 x version
2017 '2Q' # 2 x block size
2018 'L' # algorithm type
2019 '2Q' # offset, size (hash)
2020 '2Q' # offset, size (signature)
2021 '2Q' # offset, size (public key)
2022 '2Q' # offset, size (public key metadata)
2023 '2Q' # offset, size (descriptors)
2024 'Q' # rollback_index
2025 'L' + # flags
David Zeuthene3cadca2017-02-22 21:25:46 -05002026 str(RESERVED0) + 'x' + # padding for reserved bytes
2027 '47sx' + # NUL-terminated release string
David Zeuthen21e95262016-07-27 17:58:40 -04002028 str(RESERVED) + 'x') # padding for reserved bytes
2029
2030 def __init__(self, data=None):
2031 """Initializes a new header object.
2032
2033 Arguments:
2034 data: If not None, must be a bytearray of size 8192.
2035
2036 Raises:
2037 Exception: If the given data is malformed.
2038 """
2039 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
2040
2041 if data:
David Zeuthene3cadca2017-02-22 21:25:46 -05002042 (self.magic, self.required_libavb_version_major,
2043 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04002044 self.authentication_data_block_size, self.auxiliary_data_block_size,
2045 self.algorithm_type, self.hash_offset, self.hash_size,
2046 self.signature_offset, self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05002047 self.public_key_size, self.public_key_metadata_offset,
2048 self.public_key_metadata_size, self.descriptors_offset,
2049 self.descriptors_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002050 self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05002051 self.flags,
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002052 release_string) = struct.unpack(self.FORMAT_STRING, data)
David Zeuthen21e95262016-07-27 17:58:40 -04002053 # Nuke NUL-bytes at the end of the string.
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002054 if self.magic != self.MAGIC:
David Zeuthen8b6973b2016-09-20 12:39:49 -04002055 raise AvbError('Given image does not look like a vbmeta image.')
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002056 self.release_string = release_string.rstrip(b'\0').decode('utf-8')
David Zeuthen21e95262016-07-27 17:58:40 -04002057 else:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002058 self.magic = self.MAGIC
David Zeuthene3cadca2017-02-22 21:25:46 -05002059 # Start by just requiring version 1.0. Code that adds features
2060 # in a future version can use bump_required_libavb_version_minor() to
2061 # bump the minor.
2062 self.required_libavb_version_major = AVB_VERSION_MAJOR
2063 self.required_libavb_version_minor = 0
David Zeuthen21e95262016-07-27 17:58:40 -04002064 self.authentication_data_block_size = 0
2065 self.auxiliary_data_block_size = 0
2066 self.algorithm_type = 0
2067 self.hash_offset = 0
2068 self.hash_size = 0
2069 self.signature_offset = 0
2070 self.signature_size = 0
2071 self.public_key_offset = 0
2072 self.public_key_size = 0
David Zeuthen18666ab2016-11-15 11:18:05 -05002073 self.public_key_metadata_offset = 0
2074 self.public_key_metadata_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04002075 self.descriptors_offset = 0
2076 self.descriptors_size = 0
2077 self.rollback_index = 0
David Zeuthenfd41eb92016-11-17 12:24:47 -05002078 self.flags = 0
David Zeuthene3cadca2017-02-22 21:25:46 -05002079 self.release_string = get_release_string()
2080
2081 def bump_required_libavb_version_minor(self, minor):
2082 """Function to bump required_libavb_version_minor.
2083
2084 Call this when writing data that requires a specific libavb
2085 version to parse it.
2086
2087 Arguments:
2088 minor: The minor version of libavb that has support for the feature.
2089 """
2090 self.required_libavb_version_minor = (
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002091 max(self.required_libavb_version_minor, minor))
David Zeuthen21e95262016-07-27 17:58:40 -04002092
David Zeuthen21e95262016-07-27 17:58:40 -04002093 def encode(self):
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002094 """Serializes the header.
David Zeuthen21e95262016-07-27 17:58:40 -04002095
2096 Returns:
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002097 The header as bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04002098 """
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002099 release_string_encoded = self.release_string.encode('utf-8')
David Zeuthen21e95262016-07-27 17:58:40 -04002100 return struct.pack(self.FORMAT_STRING, self.magic,
David Zeuthene3cadca2017-02-22 21:25:46 -05002101 self.required_libavb_version_major,
2102 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04002103 self.authentication_data_block_size,
2104 self.auxiliary_data_block_size, self.algorithm_type,
2105 self.hash_offset, self.hash_size, self.signature_offset,
2106 self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05002107 self.public_key_size, self.public_key_metadata_offset,
2108 self.public_key_metadata_size, self.descriptors_offset,
David Zeuthene3cadca2017-02-22 21:25:46 -05002109 self.descriptors_size, self.rollback_index, self.flags,
Jan Monschfbd9f0d2020-04-08 22:16:24 +02002110 release_string_encoded)
David Zeuthen21e95262016-07-27 17:58:40 -04002111
2112
2113class Avb(object):
2114 """Business logic for avbtool command-line tool."""
2115
David Zeuthen8b6973b2016-09-20 12:39:49 -04002116 # Keep in sync with avb_ab_flow.h.
2117 AB_FORMAT_NO_CRC = '!4sBB2xBBBxBBBx12x'
2118 AB_MAGIC = '\0AB0'
2119 AB_MAJOR_VERSION = 1
2120 AB_MINOR_VERSION = 0
2121 AB_MISC_METADATA_OFFSET = 2048
2122
David Zeuthen09692692016-09-30 16:16:40 -04002123 # Constants for maximum metadata size. These are used to give
2124 # meaningful errors if the value passed in via --partition_size is
2125 # too small and when --calc_max_image_size is used. We use
2126 # conservative figures.
2127 MAX_VBMETA_SIZE = 64 * 1024
2128 MAX_FOOTER_SIZE = 4096
2129
Jan Monsch2c7be992020-04-03 14:37:13 +02002130 def generate_test_image(self, output, image_size, start_byte):
2131 """Generates a test image for testing avbtool with known content.
2132
2133 The content has following pattern: 0x00 0x01 0x02 .. 0xff 0x00 0x01 ..).
2134
2135 Arguments:
2136 output: Write test image to this file.
2137 image_size: The size of the requested file in bytes.
2138 start_byte: The integer value of the start byte to use for pattern
2139 generation.
2140 """
2141 pattern = bytearray([x & 0xFF for x in range(start_byte, start_byte + 256)])
2142 buf = bytearray()
2143 c = int(math.ceil(image_size / 256.0))
2144 for _ in range(0, c):
2145 buf.extend(pattern)
2146 output.write(buf[0:image_size])
2147
David Zeuthen49936b42018-08-07 17:38:58 -04002148 def extract_vbmeta_image(self, output, image_filename, padding_size):
2149 """Implements the 'extract_vbmeta_image' command.
2150
2151 Arguments:
2152 output: Write vbmeta struct to this file.
2153 image_filename: File to extract vbmeta data from (with a footer).
2154 padding_size: If not 0, pads output so size is a multiple of the number.
2155
2156 Raises:
2157 AvbError: If there's no footer in the image.
2158 """
2159 image = ImageHandler(image_filename)
2160
2161 (footer, _, _, _) = self._parse_image(image)
2162
2163 if not footer:
2164 raise AvbError('Given image does not have a footer.')
2165
2166 image.seek(footer.vbmeta_offset)
2167 vbmeta_blob = image.read(footer.vbmeta_size)
2168 output.write(vbmeta_blob)
2169
2170 if padding_size > 0:
2171 padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
2172 padding_needed = padded_size - len(vbmeta_blob)
2173 output.write('\0' * padding_needed)
2174
David Zeuthena4fee8b2016-08-22 15:20:43 -04002175 def erase_footer(self, image_filename, keep_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04002176 """Implements the 'erase_footer' command.
2177
2178 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002179 image_filename: File to erase a footer from.
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002180 keep_hashtree: If True, keep the hashtree and FEC around.
David Zeuthen21e95262016-07-27 17:58:40 -04002181
2182 Raises:
2183 AvbError: If there's no footer in the image.
2184 """
2185
David Zeuthena4fee8b2016-08-22 15:20:43 -04002186 image = ImageHandler(image_filename)
2187
David Zeuthen21e95262016-07-27 17:58:40 -04002188 (footer, _, descriptors, _) = self._parse_image(image)
2189
2190 if not footer:
2191 raise AvbError('Given image does not have a footer.')
2192
2193 new_image_size = None
2194 if not keep_hashtree:
2195 new_image_size = footer.original_image_size
2196 else:
2197 # If requested to keep the hashtree, search for a hashtree
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002198 # descriptor to figure out the location and size of the hashtree
2199 # and FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04002200 for desc in descriptors:
2201 if isinstance(desc, AvbHashtreeDescriptor):
2202 # The hashtree is always just following the main data so the
2203 # new size is easily derived.
2204 new_image_size = desc.tree_offset + desc.tree_size
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002205 # If the image has FEC codes, also keep those.
2206 if desc.fec_offset > 0:
2207 fec_end = desc.fec_offset + desc.fec_size
2208 new_image_size = max(new_image_size, fec_end)
David Zeuthen21e95262016-07-27 17:58:40 -04002209 break
2210 if not new_image_size:
2211 raise AvbError('Requested to keep hashtree but no hashtree '
2212 'descriptor was found.')
2213
2214 # And cut...
2215 image.truncate(new_image_size)
2216
David Zeuthen1394f762019-04-30 10:20:11 -04002217 def zero_hashtree(self, image_filename):
2218 """Implements the 'zero_hashtree' command.
2219
2220 Arguments:
2221 image_filename: File to zero hashtree and FEC data from.
2222
2223 Raises:
2224 AvbError: If there's no footer in the image.
2225 """
2226
2227 image = ImageHandler(image_filename)
2228
2229 (footer, _, descriptors, _) = self._parse_image(image)
2230
2231 if not footer:
2232 raise AvbError('Given image does not have a footer.')
2233
2234 # Search for a hashtree descriptor to figure out the location and
2235 # size of the hashtree and FEC.
2236 ht_desc = None
2237 for desc in descriptors:
2238 if isinstance(desc, AvbHashtreeDescriptor):
2239 ht_desc = desc
2240 break
2241
2242 if not ht_desc:
2243 raise AvbError('No hashtree descriptor was found.')
2244
2245 zero_ht_start_offset = ht_desc.tree_offset
2246 zero_ht_num_bytes = ht_desc.tree_size
2247 zero_fec_start_offset = None
2248 zero_fec_num_bytes = 0
2249 if ht_desc.fec_offset > 0:
2250 if ht_desc.fec_offset != ht_desc.tree_offset + ht_desc.tree_size:
2251 raise AvbError('Hash-tree and FEC data must be adjacent.')
2252 zero_fec_start_offset = ht_desc.fec_offset
2253 zero_fec_num_bytes = ht_desc.fec_size
Jan Monsch23e0c622019-12-11 11:23:58 +01002254 zero_end_offset = (zero_ht_start_offset + zero_ht_num_bytes
2255 + zero_fec_num_bytes)
David Zeuthen1394f762019-04-30 10:20:11 -04002256 image.seek(zero_end_offset)
2257 data = image.read(image.image_size - zero_end_offset)
2258
2259 # Write zeroes all over hashtree and FEC, except for the first eight bytes
2260 # where a magic marker - ZeroHaSH - is placed. Place these markers in the
2261 # beginning of both hashtree and FEC. (That way, in the future we can add
2262 # options to 'avbtool zero_hashtree' so as to zero out only either/or.)
2263 #
2264 # Applications can use these markers to detect that the hashtree and/or
2265 # FEC needs to be recomputed.
2266 image.truncate(zero_ht_start_offset)
2267 data_zeroed_firstblock = 'ZeRoHaSH' + '\0'*(image.block_size - 8)
2268 image.append_raw(data_zeroed_firstblock)
2269 image.append_fill('\0\0\0\0', zero_ht_num_bytes - image.block_size)
2270 if zero_fec_start_offset:
2271 image.append_raw(data_zeroed_firstblock)
2272 image.append_fill('\0\0\0\0', zero_fec_num_bytes - image.block_size)
2273 image.append_raw(data)
2274
David Zeuthen2bc232b2017-04-19 14:25:19 -04002275 def resize_image(self, image_filename, partition_size):
2276 """Implements the 'resize_image' command.
2277
2278 Arguments:
2279 image_filename: File with footer to resize.
2280 partition_size: The new size of the image.
2281
2282 Raises:
2283 AvbError: If there's no footer in the image.
2284 """
2285
2286 image = ImageHandler(image_filename)
2287
2288 if partition_size % image.block_size != 0:
2289 raise AvbError('Partition size of {} is not a multiple of the image '
2290 'block size {}.'.format(partition_size,
2291 image.block_size))
2292
Jan Monsch77cd2022019-12-10 17:18:04 +01002293 (footer, _, _, _) = self._parse_image(image)
David Zeuthen2bc232b2017-04-19 14:25:19 -04002294
2295 if not footer:
2296 raise AvbError('Given image does not have a footer.')
2297
2298 # The vbmeta blob is always at the end of the data so resizing an
2299 # image amounts to just moving the footer around.
2300
2301 vbmeta_end_offset = footer.vbmeta_offset + footer.vbmeta_size
2302 if vbmeta_end_offset % image.block_size != 0:
Jan Monscheeb28b62019-12-05 16:17:09 +01002303 vbmeta_end_offset += image.block_size - (vbmeta_end_offset
2304 % image.block_size)
David Zeuthen2bc232b2017-04-19 14:25:19 -04002305
2306 if partition_size < vbmeta_end_offset + 1*image.block_size:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07002307 raise AvbError('Requested size of {} is too small for an image '
2308 'of size {}.'
2309 .format(partition_size,
2310 vbmeta_end_offset + 1*image.block_size))
David Zeuthen2bc232b2017-04-19 14:25:19 -04002311
2312 # Cut at the end of the vbmeta blob and insert a DONT_CARE chunk
2313 # with enough bytes such that the final Footer block is at the end
2314 # of partition_size.
2315 image.truncate(vbmeta_end_offset)
2316 image.append_dont_care(partition_size - vbmeta_end_offset -
2317 1*image.block_size)
2318
2319 # Just reuse the same footer - only difference is that we're
2320 # writing it in a different place.
2321 footer_blob = footer.encode()
2322 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2323 footer_blob)
2324 image.append_raw(footer_blob_with_padding)
2325
David Zeuthen8b6973b2016-09-20 12:39:49 -04002326 def set_ab_metadata(self, misc_image, slot_data):
2327 """Implements the 'set_ab_metadata' command.
2328
2329 The |slot_data| argument must be of the form 'A_priority:A_tries_remaining:
2330 A_successful_boot:B_priority:B_tries_remaining:B_successful_boot'.
2331
2332 Arguments:
2333 misc_image: The misc image to write to.
2334 slot_data: Slot data as a string
2335
2336 Raises:
2337 AvbError: If slot data is malformed.
2338 """
2339 tokens = slot_data.split(':')
2340 if len(tokens) != 6:
2341 raise AvbError('Malformed slot data "{}".'.format(slot_data))
2342 a_priority = int(tokens[0])
2343 a_tries_remaining = int(tokens[1])
2344 a_success = True if int(tokens[2]) != 0 else False
2345 b_priority = int(tokens[3])
2346 b_tries_remaining = int(tokens[4])
2347 b_success = True if int(tokens[5]) != 0 else False
2348
2349 ab_data_no_crc = struct.pack(self.AB_FORMAT_NO_CRC,
2350 self.AB_MAGIC,
2351 self.AB_MAJOR_VERSION, self.AB_MINOR_VERSION,
2352 a_priority, a_tries_remaining, a_success,
2353 b_priority, b_tries_remaining, b_success)
2354 # Force CRC to be unsigned, see https://bugs.python.org/issue4903 for why.
2355 crc_value = binascii.crc32(ab_data_no_crc) & 0xffffffff
2356 ab_data = ab_data_no_crc + struct.pack('!I', crc_value)
2357 misc_image.seek(self.AB_MISC_METADATA_OFFSET)
2358 misc_image.write(ab_data)
2359
David Zeuthena4fee8b2016-08-22 15:20:43 -04002360 def info_image(self, image_filename, output):
David Zeuthen21e95262016-07-27 17:58:40 -04002361 """Implements the 'info_image' command.
2362
2363 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002364 image_filename: Image file to get information from (file object).
David Zeuthen21e95262016-07-27 17:58:40 -04002365 output: Output file to write human-readable information to (file object).
2366 """
2367
David Zeuthena4fee8b2016-08-22 15:20:43 -04002368 image = ImageHandler(image_filename)
2369
David Zeuthen21e95262016-07-27 17:58:40 -04002370 o = output
2371
2372 (footer, header, descriptors, image_size) = self._parse_image(image)
2373
Bowgo Tsaid7145942020-03-20 17:03:51 +08002374 # To show the SHA1 of the public key.
2375 vbmeta_blob = self._load_vbmeta_blob(image)
2376 key_offset = (header.SIZE +
2377 header.authentication_data_block_size +
2378 header.public_key_offset)
2379 key_blob = vbmeta_blob[key_offset:key_offset + header.public_key_size]
2380
David Zeuthen21e95262016-07-27 17:58:40 -04002381 if footer:
2382 o.write('Footer version: {}.{}\n'.format(footer.version_major,
2383 footer.version_minor))
2384 o.write('Image size: {} bytes\n'.format(image_size))
2385 o.write('Original image size: {} bytes\n'.format(
2386 footer.original_image_size))
2387 o.write('VBMeta offset: {}\n'.format(footer.vbmeta_offset))
2388 o.write('VBMeta size: {} bytes\n'.format(footer.vbmeta_size))
2389 o.write('--\n')
2390
2391 (alg_name, _) = lookup_algorithm_by_type(header.algorithm_type)
2392
David Zeuthene3cadca2017-02-22 21:25:46 -05002393 o.write('Minimum libavb version: {}.{}{}\n'.format(
2394 header.required_libavb_version_major,
2395 header.required_libavb_version_minor,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002396 ' (Sparse)' if image.is_sparse else ''))
David Zeuthen21e95262016-07-27 17:58:40 -04002397 o.write('Header Block: {} bytes\n'.format(AvbVBMetaHeader.SIZE))
2398 o.write('Authentication Block: {} bytes\n'.format(
2399 header.authentication_data_block_size))
2400 o.write('Auxiliary Block: {} bytes\n'.format(
2401 header.auxiliary_data_block_size))
Bowgo Tsaid7145942020-03-20 17:03:51 +08002402 if key_blob:
2403 hexdig = hashlib.sha1(key_blob).hexdigest()
2404 o.write('Public key (sha1): {}\n'.format(hexdig))
David Zeuthen21e95262016-07-27 17:58:40 -04002405 o.write('Algorithm: {}\n'.format(alg_name))
2406 o.write('Rollback Index: {}\n'.format(header.rollback_index))
David Zeuthenfd41eb92016-11-17 12:24:47 -05002407 o.write('Flags: {}\n'.format(header.flags))
David Zeuthene3cadca2017-02-22 21:25:46 -05002408 o.write('Release String: \'{}\'\n'.format(
2409 header.release_string.rstrip('\0')))
David Zeuthen21e95262016-07-27 17:58:40 -04002410
2411 # Print descriptors.
2412 num_printed = 0
2413 o.write('Descriptors:\n')
2414 for desc in descriptors:
2415 desc.print_desc(o)
2416 num_printed += 1
2417 if num_printed == 0:
2418 o.write(' (none)\n')
2419
Jan Monscheeb28b62019-12-05 16:17:09 +01002420 def verify_image(self, image_filename, key_path, expected_chain_partitions,
2421 follow_chain_partitions, accept_zeroed_hashtree):
David Zeuthenb623d8b2017-04-04 16:05:53 -04002422 """Implements the 'verify_image' command.
2423
2424 Arguments:
2425 image_filename: Image file to get information from (file object).
Jan Monscheeb28b62019-12-05 16:17:09 +01002426 key_path: None or check that embedded public key matches key at given
2427 path.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002428 expected_chain_partitions: List of chain partitions to check or None.
Jan Monscheeb28b62019-12-05 16:17:09 +01002429 follow_chain_partitions:
2430 If True, will follows chain partitions even when not specified with
2431 the --expected_chain_partition option
2432 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
2433 zeroed out.
Jan Monsch77cd2022019-12-10 17:18:04 +01002434
2435 Raises:
2436 AvbError: If verification of the image fails.
David Zeuthenb623d8b2017-04-04 16:05:53 -04002437 """
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002438 expected_chain_partitions_map = {}
2439 if expected_chain_partitions:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002440 for cp in expected_chain_partitions:
2441 cp_tokens = cp.split(':')
2442 if len(cp_tokens) != 3:
2443 raise AvbError('Malformed chained partition "{}".'.format(cp))
2444 partition_name = cp_tokens[0]
2445 rollback_index_location = int(cp_tokens[1])
2446 file_path = cp_tokens[2]
2447 pk_blob = open(file_path).read()
Jan Monscheeb28b62019-12-05 16:17:09 +01002448 expected_chain_partitions_map[partition_name] = (
2449 rollback_index_location, pk_blob)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002450
2451 image_dir = os.path.dirname(image_filename)
2452 image_ext = os.path.splitext(image_filename)[1]
2453
2454 key_blob = None
2455 if key_path:
Jan Monsch23e0c622019-12-11 11:23:58 +01002456 print('Verifying image {} using key at {}'.format(image_filename,
2457 key_path))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002458 key_blob = encode_rsa_key(key_path)
2459 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01002460 print('Verifying image {} using embedded public key'.format(
2461 image_filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002462
David Zeuthenb623d8b2017-04-04 16:05:53 -04002463 image = ImageHandler(image_filename)
Jan Monsch77cd2022019-12-10 17:18:04 +01002464 (footer, header, descriptors, _) = self._parse_image(image)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002465 offset = 0
2466 if footer:
2467 offset = footer.vbmeta_offset
David Zeuthen49936b42018-08-07 17:38:58 -04002468
David Zeuthenb623d8b2017-04-04 16:05:53 -04002469 image.seek(offset)
Jan Monscheeb28b62019-12-05 16:17:09 +01002470 vbmeta_blob = image.read(header.SIZE
2471 + header.authentication_data_block_size
2472 + header.auxiliary_data_block_size)
David Zeuthen49936b42018-08-07 17:38:58 -04002473
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002474 alg_name, _ = lookup_algorithm_by_type(header.algorithm_type)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002475 if not verify_vbmeta_signature(header, vbmeta_blob):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002476 raise AvbError('Signature check failed for {} vbmeta struct {}'
2477 .format(alg_name, image_filename))
2478
2479 if key_blob:
2480 # The embedded public key is in the auxiliary block at an offset.
2481 key_offset = AvbVBMetaHeader.SIZE
David Zeuthen49936b42018-08-07 17:38:58 -04002482 key_offset += header.authentication_data_block_size
2483 key_offset += header.public_key_offset
Jan Monscheeb28b62019-12-05 16:17:09 +01002484 key_blob_in_vbmeta = vbmeta_blob[key_offset:key_offset
2485 + header.public_key_size]
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002486 if key_blob != key_blob_in_vbmeta:
2487 raise AvbError('Embedded public key does not match given key.')
2488
2489 if footer:
Jan Monsch23e0c622019-12-11 11:23:58 +01002490 print('vbmeta: Successfully verified footer and {} vbmeta struct in {}'
2491 .format(alg_name, image.filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002492 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01002493 print('vbmeta: Successfully verified {} vbmeta struct in {}'
2494 .format(alg_name, image.filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002495
2496 for desc in descriptors:
Jan Monscheeb28b62019-12-05 16:17:09 +01002497 if (isinstance(desc, AvbChainPartitionDescriptor)
2498 and follow_chain_partitions
Jan Monschfe00c0a2019-12-11 11:19:40 +01002499 and expected_chain_partitions_map.get(desc.partition_name) is None):
David Zeuthene947cb62019-01-25 15:27:08 -05002500 # In this case we're processing a chain descriptor but don't have a
2501 # --expect_chain_partition ... however --follow_chain_partitions was
2502 # specified so we shouldn't error out in desc.verify().
Jan Monsch23e0c622019-12-11 11:23:58 +01002503 print('{}: Chained but ROLLBACK_SLOT (which is {}) '
2504 'and KEY (which has sha1 {}) not specified'
2505 .format(desc.partition_name, desc.rollback_index_location,
2506 hashlib.sha1(desc.public_key).hexdigest()))
2507 elif not desc.verify(image_dir, image_ext, expected_chain_partitions_map,
Jan Monscheeb28b62019-12-05 16:17:09 +01002508 image, accept_zeroed_hashtree):
Jan Monsch23e0c622019-12-11 11:23:58 +01002509 raise AvbError('Error verifying descriptor.')
Jan Monscheeb28b62019-12-05 16:17:09 +01002510 # Honor --follow_chain_partitions - add '--' to make the output more
2511 # readable.
2512 if (isinstance(desc, AvbChainPartitionDescriptor)
2513 and follow_chain_partitions):
Jan Monsch23e0c622019-12-11 11:23:58 +01002514 print('--')
Jan Monscheeb28b62019-12-05 16:17:09 +01002515 chained_image_filename = os.path.join(image_dir,
2516 desc.partition_name + image_ext)
2517 self.verify_image(chained_image_filename, key_path, None, False,
2518 accept_zeroed_hashtree)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002519
David Zeuthenb8643c02018-05-17 17:21:18 -04002520 def calculate_vbmeta_digest(self, image_filename, hash_algorithm, output):
2521 """Implements the 'calculate_vbmeta_digest' command.
2522
2523 Arguments:
2524 image_filename: Image file to get information from (file object).
2525 hash_algorithm: Hash algorithm used.
2526 output: Output file to write human-readable information to (file object).
2527 """
2528
2529 image_dir = os.path.dirname(image_filename)
2530 image_ext = os.path.splitext(image_filename)[1]
2531
2532 image = ImageHandler(image_filename)
Jan Monsch77cd2022019-12-10 17:18:04 +01002533 (footer, header, descriptors, _) = self._parse_image(image)
David Zeuthenb8643c02018-05-17 17:21:18 -04002534 offset = 0
2535 if footer:
2536 offset = footer.vbmeta_offset
2537 size = (header.SIZE + header.authentication_data_block_size +
2538 header.auxiliary_data_block_size)
2539 image.seek(offset)
2540 vbmeta_blob = image.read(size)
2541
Jan Monsch6f27bb12020-04-07 07:33:26 +02002542 hasher = hashlib.new(hash_algorithm)
David Zeuthenb8643c02018-05-17 17:21:18 -04002543 hasher.update(vbmeta_blob)
2544
2545 for desc in descriptors:
2546 if isinstance(desc, AvbChainPartitionDescriptor):
Jan Monscheeb28b62019-12-05 16:17:09 +01002547 ch_image_filename = os.path.join(image_dir,
2548 desc.partition_name + image_ext)
David Zeuthenb8643c02018-05-17 17:21:18 -04002549 ch_image = ImageHandler(ch_image_filename)
Jan Monsch77cd2022019-12-10 17:18:04 +01002550 (ch_footer, ch_header, _, _) = self._parse_image(ch_image)
David Zeuthenb8643c02018-05-17 17:21:18 -04002551 ch_offset = 0
David Zeuthen49936b42018-08-07 17:38:58 -04002552 ch_size = (ch_header.SIZE + ch_header.authentication_data_block_size +
2553 ch_header.auxiliary_data_block_size)
David Zeuthenb8643c02018-05-17 17:21:18 -04002554 if ch_footer:
2555 ch_offset = ch_footer.vbmeta_offset
David Zeuthenb8643c02018-05-17 17:21:18 -04002556 ch_image.seek(ch_offset)
2557 ch_vbmeta_blob = ch_image.read(ch_size)
2558 hasher.update(ch_vbmeta_blob)
2559
2560 digest = hasher.digest()
Jan Monsch23e0c622019-12-11 11:23:58 +01002561 output.write('{}\n'.format(binascii.hexlify(digest)))
David Zeuthenb8643c02018-05-17 17:21:18 -04002562
David Zeuthenf7d2e752018-09-20 13:30:41 -04002563 def calculate_kernel_cmdline(self, image_filename, hashtree_disabled, output):
2564 """Implements the 'calculate_kernel_cmdline' command.
2565
2566 Arguments:
2567 image_filename: Image file to get information from (file object).
2568 hashtree_disabled: If True, returns the cmdline for hashtree disabled.
2569 output: Output file to write human-readable information to (file object).
2570 """
2571
2572 image = ImageHandler(image_filename)
2573 _, _, descriptors, _ = self._parse_image(image)
2574
2575 image_dir = os.path.dirname(image_filename)
2576 image_ext = os.path.splitext(image_filename)[1]
2577
2578 cmdline_descriptors = []
2579 for desc in descriptors:
2580 if isinstance(desc, AvbChainPartitionDescriptor):
Jan Monscheeb28b62019-12-05 16:17:09 +01002581 ch_image_filename = os.path.join(image_dir,
2582 desc.partition_name + image_ext)
David Zeuthenf7d2e752018-09-20 13:30:41 -04002583 ch_image = ImageHandler(ch_image_filename)
2584 _, _, ch_descriptors, _ = self._parse_image(ch_image)
2585 for ch_desc in ch_descriptors:
2586 if isinstance(ch_desc, AvbKernelCmdlineDescriptor):
2587 cmdline_descriptors.append(ch_desc)
2588 elif isinstance(desc, AvbKernelCmdlineDescriptor):
2589 cmdline_descriptors.append(desc)
2590
2591 kernel_cmdline_snippets = []
2592 for desc in cmdline_descriptors:
2593 use_cmdline = True
Jan Monscheeb28b62019-12-05 16:17:09 +01002594 if ((desc.flags &
2595 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
2596 != 0):
David Zeuthenf7d2e752018-09-20 13:30:41 -04002597 if hashtree_disabled:
2598 use_cmdline = False
Jan Monscheeb28b62019-12-05 16:17:09 +01002599 if (desc.flags &
2600 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED) != 0:
David Zeuthenf7d2e752018-09-20 13:30:41 -04002601 if not hashtree_disabled:
2602 use_cmdline = False
2603 if use_cmdline:
2604 kernel_cmdline_snippets.append(desc.kernel_cmdline)
2605 output.write(' '.join(kernel_cmdline_snippets))
2606
David Zeuthen21e95262016-07-27 17:58:40 -04002607 def _parse_image(self, image):
2608 """Gets information about an image.
2609
2610 The image can either be a vbmeta or an image with a footer.
2611
2612 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002613 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002614
2615 Returns:
2616 A tuple where the first argument is a AvbFooter (None if there
2617 is no footer on the image), the second argument is a
2618 AvbVBMetaHeader, the third argument is a list of
2619 AvbDescriptor-derived instances, and the fourth argument is the
2620 size of |image|.
Jan Monsch443bf322020-02-19 14:56:44 +01002621
2622 Raises:
2623 AvbError: In case the image cannot be parsed.
David Zeuthen21e95262016-07-27 17:58:40 -04002624 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002625 assert isinstance(image, ImageHandler)
David Zeuthen21e95262016-07-27 17:58:40 -04002626 footer = None
David Zeuthen09692692016-09-30 16:16:40 -04002627 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002628 try:
2629 footer = AvbFooter(image.read(AvbFooter.SIZE))
2630 except (LookupError, struct.error):
2631 # Nope, just seek back to the start.
2632 image.seek(0)
2633
2634 vbmeta_offset = 0
2635 if footer:
2636 vbmeta_offset = footer.vbmeta_offset
2637
2638 image.seek(vbmeta_offset)
2639 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2640
2641 auth_block_offset = vbmeta_offset + AvbVBMetaHeader.SIZE
2642 aux_block_offset = auth_block_offset + h.authentication_data_block_size
2643 desc_start_offset = aux_block_offset + h.descriptors_offset
2644 image.seek(desc_start_offset)
2645 descriptors = parse_descriptors(image.read(h.descriptors_size))
2646
David Zeuthen09692692016-09-30 16:16:40 -04002647 return footer, h, descriptors, image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002648
David Zeuthenb1b994d2017-03-06 18:01:31 -05002649 def _load_vbmeta_blob(self, image):
2650 """Gets the vbmeta struct and associated sections.
2651
2652 The image can either be a vbmeta.img or an image with a footer.
2653
2654 Arguments:
2655 image: An ImageHandler (vbmeta or footer).
2656
2657 Returns:
2658 A blob with the vbmeta struct and other sections.
2659 """
2660 assert isinstance(image, ImageHandler)
2661 footer = None
2662 image.seek(image.image_size - AvbFooter.SIZE)
2663 try:
2664 footer = AvbFooter(image.read(AvbFooter.SIZE))
2665 except (LookupError, struct.error):
2666 # Nope, just seek back to the start.
2667 image.seek(0)
2668
2669 vbmeta_offset = 0
2670 if footer:
2671 vbmeta_offset = footer.vbmeta_offset
2672
2673 image.seek(vbmeta_offset)
2674 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2675
2676 image.seek(vbmeta_offset)
2677 data_size = AvbVBMetaHeader.SIZE
2678 data_size += h.authentication_data_block_size
2679 data_size += h.auxiliary_data_block_size
2680 return image.read(data_size)
2681
David Zeuthen73f2afa2017-05-17 16:54:11 -04002682 def _get_cmdline_descriptors_for_hashtree_descriptor(self, ht):
David Zeuthenfd41eb92016-11-17 12:24:47 -05002683 """Generate kernel cmdline descriptors for dm-verity.
David Zeuthen21e95262016-07-27 17:58:40 -04002684
2685 Arguments:
David Zeuthen73f2afa2017-05-17 16:54:11 -04002686 ht: A AvbHashtreeDescriptor
David Zeuthen21e95262016-07-27 17:58:40 -04002687
2688 Returns:
David Zeuthenfd41eb92016-11-17 12:24:47 -05002689 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2690 instructions. There is one for when hashtree is not disabled and one for
2691 when it is.
David Zeuthen21e95262016-07-27 17:58:40 -04002692
David Zeuthen21e95262016-07-27 17:58:40 -04002693 """
2694
David Zeuthen21e95262016-07-27 17:58:40 -04002695 c = 'dm="1 vroot none ro 1,'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002696 c += '0' # start
Jan Monsch23e0c622019-12-11 11:23:58 +01002697 c += ' {}'.format((ht.image_size // 512)) # size (# sectors)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002698 c += ' verity {}'.format(ht.dm_verity_version) # type and version
2699 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # data_dev
2700 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # hash_dev
2701 c += ' {}'.format(ht.data_block_size) # data_block
2702 c += ' {}'.format(ht.hash_block_size) # hash_block
Jan Monsch23e0c622019-12-11 11:23:58 +01002703 c += ' {}'.format(ht.image_size // ht.data_block_size) # #blocks
2704 c += ' {}'.format(ht.image_size // ht.data_block_size) # hash_offset
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002705 c += ' {}'.format(ht.hash_algorithm) # hash_alg
2706 c += ' {}'.format(str(ht.root_digest).encode('hex')) # root_digest
2707 c += ' {}'.format(str(ht.salt).encode('hex')) # salt
2708 if ht.fec_num_roots > 0:
David Zeuthena01e32f2017-01-24 17:32:38 -05002709 c += ' 10' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04002710 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002711 c += ' ignore_zero_blocks'
2712 c += ' use_fec_from_device PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2713 c += ' fec_roots {}'.format(ht.fec_num_roots)
2714 # Note that fec_blocks is the size that FEC covers, *not* the
2715 # size of the FEC data. Since we use FEC for everything up until
2716 # the FEC data, it's the same as the offset.
Jan Monsch23e0c622019-12-11 11:23:58 +01002717 c += ' fec_blocks {}'.format(ht.fec_offset // ht.data_block_size)
2718 c += ' fec_start {}'.format(ht.fec_offset // ht.data_block_size)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002719 else:
David Zeuthena01e32f2017-01-24 17:32:38 -05002720 c += ' 2' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04002721 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002722 c += ' ignore_zero_blocks'
David Zeuthenfd9c18d2017-03-20 18:19:30 -04002723 c += '" root=/dev/dm-0'
David Zeuthen21e95262016-07-27 17:58:40 -04002724
David Zeuthenfd41eb92016-11-17 12:24:47 -05002725 # Now that we have the command-line, generate the descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002726 desc = AvbKernelCmdlineDescriptor()
2727 desc.kernel_cmdline = c
David Zeuthenfd41eb92016-11-17 12:24:47 -05002728 desc.flags = (
2729 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
2730
2731 # The descriptor for when hashtree verification is disabled is a lot
2732 # simpler - we just set the root to the partition.
2733 desc_no_ht = AvbKernelCmdlineDescriptor()
2734 desc_no_ht.kernel_cmdline = 'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2735 desc_no_ht.flags = (
2736 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED)
2737
2738 return [desc, desc_no_ht]
David Zeuthen21e95262016-07-27 17:58:40 -04002739
David Zeuthen73f2afa2017-05-17 16:54:11 -04002740 def _get_cmdline_descriptors_for_dm_verity(self, image):
2741 """Generate kernel cmdline descriptors for dm-verity.
2742
2743 Arguments:
2744 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
2745
2746 Returns:
2747 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2748 instructions. There is one for when hashtree is not disabled and one for
2749 when it is.
2750
2751 Raises:
2752 AvbError: If |image| doesn't have a hashtree descriptor.
2753
2754 """
2755
2756 (_, _, descriptors, _) = self._parse_image(image)
2757
2758 ht = None
2759 for desc in descriptors:
2760 if isinstance(desc, AvbHashtreeDescriptor):
2761 ht = desc
2762 break
2763
2764 if not ht:
2765 raise AvbError('No hashtree descriptor in given image')
2766
2767 return self._get_cmdline_descriptors_for_hashtree_descriptor(ht)
2768
David Zeuthen21e95262016-07-27 17:58:40 -04002769 def make_vbmeta_image(self, output, chain_partitions, algorithm_name,
David Zeuthen18666ab2016-11-15 11:18:05 -05002770 key_path, public_key_metadata_path, rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002771 flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002772 setup_rootfs_from_kernel,
David Zeuthena156d3d2017-06-01 12:08:09 -04002773 include_descriptors_from_image,
2774 signing_helper,
2775 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05002776 release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04002777 append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04002778 print_required_libavb_version,
2779 padding_size):
David Zeuthen21e95262016-07-27 17:58:40 -04002780 """Implements the 'make_vbmeta_image' command.
2781
2782 Arguments:
2783 output: File to write the image to.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002784 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002785 algorithm_name: Name of algorithm to use.
2786 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002787 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002788 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002789 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002790 props: Properties to insert (list of strings of the form 'key:value').
2791 props_from_file: Properties to insert (list of strings 'key:<path>').
2792 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002793 setup_rootfs_from_kernel: None or file to generate from.
David Zeuthen21e95262016-07-27 17:58:40 -04002794 include_descriptors_from_image: List of file objects with descriptors.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002795 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04002796 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05002797 release_string: None or avbtool release string to use instead of default.
2798 append_to_release_string: None or string to append.
David Zeuthen1097a782017-05-31 15:53:17 -04002799 print_required_libavb_version: True to only print required libavb version.
David Zeuthen97cb5802017-06-01 16:14:05 -04002800 padding_size: If not 0, pads output so size is a multiple of the number.
David Zeuthen21e95262016-07-27 17:58:40 -04002801
2802 Raises:
2803 AvbError: If a chained partition is malformed.
2804 """
2805
David Zeuthen1097a782017-05-31 15:53:17 -04002806 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04002807 if print_required_libavb_version:
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002808 if include_descriptors_from_image:
2809 # Use the bump logic in AvbVBMetaHeader to calculate the max required
2810 # version of all included descriptors.
2811 tmp_header = AvbVBMetaHeader()
2812 for image in include_descriptors_from_image:
2813 (_, image_header, _, _) = self._parse_image(ImageHandler(image.name))
2814 tmp_header.bump_required_libavb_version_minor(
2815 image_header.required_libavb_version_minor)
Jan Monsch23e0c622019-12-11 11:23:58 +01002816 print('1.{}'.format(tmp_header.required_libavb_version_minor))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002817 else:
2818 # Descriptors aside, all vbmeta features are supported in 1.0.
Jan Monsch23e0c622019-12-11 11:23:58 +01002819 print('1.0')
David Zeuthen1097a782017-05-31 15:53:17 -04002820 return
2821
2822 if not output:
2823 raise AvbError('No output file given')
2824
David Zeuthen21e95262016-07-27 17:58:40 -04002825 descriptors = []
David Zeuthen73f2afa2017-05-17 16:54:11 -04002826 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04002827 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002828 algorithm_name, key_path, public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002829 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002830 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04002831 include_descriptors_from_image, signing_helper,
2832 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002833 append_to_release_string, 0)
David Zeuthen21e95262016-07-27 17:58:40 -04002834
2835 # Write entire vbmeta blob (header, authentication, auxiliary).
2836 output.seek(0)
2837 output.write(vbmeta_blob)
2838
David Zeuthen97cb5802017-06-01 16:14:05 -04002839 if padding_size > 0:
2840 padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
2841 padding_needed = padded_size - len(vbmeta_blob)
2842 output.write('\0' * padding_needed)
2843
David Zeuthen18666ab2016-11-15 11:18:05 -05002844 def _generate_vbmeta_blob(self, algorithm_name, key_path,
2845 public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002846 chain_partitions,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002847 rollback_index, flags, props, props_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04002848 kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002849 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002850 ht_desc_to_setup,
David Zeuthene3cadca2017-02-22 21:25:46 -05002851 include_descriptors_from_image, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04002852 signing_helper_with_files,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002853 release_string, append_to_release_string,
2854 required_libavb_version_minor):
David Zeuthen21e95262016-07-27 17:58:40 -04002855 """Generates a VBMeta blob.
2856
2857 This blob contains the header (struct AvbVBMetaHeader), the
2858 authentication data block (which contains the hash and signature
2859 for the header and auxiliary block), and the auxiliary block
2860 (which contains descriptors, the public key used, and other data).
2861
2862 The |key| parameter can |None| only if the |algorithm_name| is
2863 'NONE'.
2864
2865 Arguments:
2866 algorithm_name: The algorithm name as per the ALGORITHMS dict.
2867 key_path: The path to the .pem file used to sign the blob.
David Zeuthen18666ab2016-11-15 11:18:05 -05002868 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002869 descriptors: A list of descriptors to insert or None.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002870 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002871 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002872 flags: Flags to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002873 props: Properties to insert (List of strings of the form 'key:value').
2874 props_from_file: Properties to insert (List of strings 'key:<path>').
2875 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002876 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002877 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04002878 ht_desc_to_setup: If not None, an AvbHashtreeDescriptor to
2879 generate dm-verity kernel cmdline descriptors from.
David Zeuthen21e95262016-07-27 17:58:40 -04002880 include_descriptors_from_image: List of file objects for which
2881 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002882 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04002883 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05002884 release_string: None or avbtool release string.
2885 append_to_release_string: None or string to append.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002886 required_libavb_version_minor: Use at least this required minor version.
David Zeuthen21e95262016-07-27 17:58:40 -04002887
2888 Returns:
2889 A bytearray() with the VBMeta blob.
2890
2891 Raises:
2892 Exception: If the |algorithm_name| is not found, if no key has
2893 been given and the given algorithm requires one, or the key is
2894 of the wrong size.
2895
2896 """
2897 try:
2898 alg = ALGORITHMS[algorithm_name]
2899 except KeyError:
2900 raise AvbError('Unknown algorithm with name {}'.format(algorithm_name))
2901
David Zeuthena5fd3a42017-02-27 16:38:54 -05002902 if not descriptors:
2903 descriptors = []
2904
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002905 h = AvbVBMetaHeader()
2906 h.bump_required_libavb_version_minor(required_libavb_version_minor)
2907
David Zeuthena5fd3a42017-02-27 16:38:54 -05002908 # Insert chained partition descriptors, if any
2909 if chain_partitions:
David Zeuthend8e48582017-04-21 11:31:51 -04002910 used_locations = {}
David Zeuthena5fd3a42017-02-27 16:38:54 -05002911 for cp in chain_partitions:
2912 cp_tokens = cp.split(':')
2913 if len(cp_tokens) != 3:
2914 raise AvbError('Malformed chained partition "{}".'.format(cp))
David Zeuthend8e48582017-04-21 11:31:51 -04002915 partition_name = cp_tokens[0]
2916 rollback_index_location = int(cp_tokens[1])
2917 file_path = cp_tokens[2]
2918 # Check that the same rollback location isn't being used by
2919 # multiple chained partitions.
2920 if used_locations.get(rollback_index_location):
2921 raise AvbError('Rollback Index Location {} is already in use.'.format(
2922 rollback_index_location))
2923 used_locations[rollback_index_location] = True
David Zeuthena5fd3a42017-02-27 16:38:54 -05002924 desc = AvbChainPartitionDescriptor()
David Zeuthend8e48582017-04-21 11:31:51 -04002925 desc.partition_name = partition_name
2926 desc.rollback_index_location = rollback_index_location
David Zeuthena5fd3a42017-02-27 16:38:54 -05002927 if desc.rollback_index_location < 1:
2928 raise AvbError('Rollback index location must be 1 or larger.')
David Zeuthena5fd3a42017-02-27 16:38:54 -05002929 desc.public_key = open(file_path, 'rb').read()
2930 descriptors.append(desc)
2931
David Zeuthen21e95262016-07-27 17:58:40 -04002932 # Descriptors.
2933 encoded_descriptors = bytearray()
David Zeuthena5fd3a42017-02-27 16:38:54 -05002934 for desc in descriptors:
2935 encoded_descriptors.extend(desc.encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002936
2937 # Add properties.
2938 if props:
2939 for prop in props:
2940 idx = prop.find(':')
2941 if idx == -1:
2942 raise AvbError('Malformed property "{}".'.format(prop))
Jan Monsch23e0c622019-12-11 11:23:58 +01002943 # pylint: disable=redefined-variable-type
David Zeuthen21e95262016-07-27 17:58:40 -04002944 desc = AvbPropertyDescriptor()
2945 desc.key = prop[0:idx]
2946 desc.value = prop[(idx + 1):]
2947 encoded_descriptors.extend(desc.encode())
2948 if props_from_file:
2949 for prop in props_from_file:
2950 idx = prop.find(':')
2951 if idx == -1:
2952 raise AvbError('Malformed property "{}".'.format(prop))
2953 desc = AvbPropertyDescriptor()
2954 desc.key = prop[0:idx]
2955 desc.value = prop[(idx + 1):]
2956 file_path = prop[(idx + 1):]
2957 desc.value = open(file_path, 'rb').read()
2958 encoded_descriptors.extend(desc.encode())
2959
David Zeuthen73f2afa2017-05-17 16:54:11 -04002960 # Add AvbKernelCmdline descriptor for dm-verity from an image, if requested.
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002961 if setup_rootfs_from_kernel:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002962 image_handler = ImageHandler(
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002963 setup_rootfs_from_kernel.name)
David Zeuthenfd41eb92016-11-17 12:24:47 -05002964 cmdline_desc = self._get_cmdline_descriptors_for_dm_verity(image_handler)
2965 encoded_descriptors.extend(cmdline_desc[0].encode())
2966 encoded_descriptors.extend(cmdline_desc[1].encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002967
David Zeuthen73f2afa2017-05-17 16:54:11 -04002968 # Add AvbKernelCmdline descriptor for dm-verity from desc, if requested.
2969 if ht_desc_to_setup:
2970 cmdline_desc = self._get_cmdline_descriptors_for_hashtree_descriptor(
2971 ht_desc_to_setup)
2972 encoded_descriptors.extend(cmdline_desc[0].encode())
2973 encoded_descriptors.extend(cmdline_desc[1].encode())
2974
David Zeuthen21e95262016-07-27 17:58:40 -04002975 # Add kernel command-lines.
2976 if kernel_cmdlines:
2977 for i in kernel_cmdlines:
2978 desc = AvbKernelCmdlineDescriptor()
2979 desc.kernel_cmdline = i
2980 encoded_descriptors.extend(desc.encode())
2981
2982 # Add descriptors from other images.
2983 if include_descriptors_from_image:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07002984 descriptors_dict = dict()
David Zeuthen21e95262016-07-27 17:58:40 -04002985 for image in include_descriptors_from_image:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002986 image_handler = ImageHandler(image.name)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002987 (_, image_vbmeta_header, image_descriptors, _) = self._parse_image(
2988 image_handler)
2989 # Bump the required libavb version to support all included descriptors.
2990 h.bump_required_libavb_version_minor(
2991 image_vbmeta_header.required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04002992 for desc in image_descriptors:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07002993 # The --include_descriptors_from_image option is used in some setups
2994 # with images A and B where both A and B contain a descriptor
2995 # for a partition with the same name. Since it's not meaningful
2996 # to include both descriptors, only include the last seen descriptor.
2997 # See bug 76386656 for details.
2998 if hasattr(desc, 'partition_name'):
2999 key = type(desc).__name__ + '_' + desc.partition_name
3000 descriptors_dict[key] = desc.encode()
3001 else:
3002 encoded_descriptors.extend(desc.encode())
Jan Monschfe00c0a2019-12-11 11:19:40 +01003003 for key in sorted(descriptors_dict):
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07003004 encoded_descriptors.extend(descriptors_dict[key])
David Zeuthen21e95262016-07-27 17:58:40 -04003005
David Zeuthen18666ab2016-11-15 11:18:05 -05003006 # Load public key metadata blob, if requested.
3007 pkmd_blob = []
3008 if public_key_metadata_path:
3009 with open(public_key_metadata_path) as f:
3010 pkmd_blob = f.read()
3011
David Zeuthen21e95262016-07-27 17:58:40 -04003012 key = None
3013 encoded_key = bytearray()
3014 if alg.public_key_num_bytes > 0:
3015 if not key_path:
3016 raise AvbError('Key is required for algorithm {}'.format(
3017 algorithm_name))
David Zeuthenc68f0822017-03-31 17:22:35 -04003018 encoded_key = encode_rsa_key(key_path)
David Zeuthen21e95262016-07-27 17:58:40 -04003019 if len(encoded_key) != alg.public_key_num_bytes:
3020 raise AvbError('Key is wrong size for algorithm {}'.format(
3021 algorithm_name))
3022
David Zeuthene3cadca2017-02-22 21:25:46 -05003023 # Override release string, if requested.
Jan Monsch23e0c622019-12-11 11:23:58 +01003024 # pylint: disable=unicode-builtin
David Zeuthene3cadca2017-02-22 21:25:46 -05003025 if isinstance(release_string, (str, unicode)):
3026 h.release_string = release_string
3027
3028 # Append to release string, if requested. Also insert a space before.
3029 if isinstance(append_to_release_string, (str, unicode)):
3030 h.release_string += ' ' + append_to_release_string
3031
David Zeuthen18666ab2016-11-15 11:18:05 -05003032 # For the Auxiliary data block, descriptors are stored at offset 0,
3033 # followed by the public key, followed by the public key metadata blob.
David Zeuthen21e95262016-07-27 17:58:40 -04003034 h.auxiliary_data_block_size = round_to_multiple(
David Zeuthen18666ab2016-11-15 11:18:05 -05003035 len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64)
David Zeuthen21e95262016-07-27 17:58:40 -04003036 h.descriptors_offset = 0
3037 h.descriptors_size = len(encoded_descriptors)
3038 h.public_key_offset = h.descriptors_size
3039 h.public_key_size = len(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05003040 h.public_key_metadata_offset = h.public_key_offset + h.public_key_size
3041 h.public_key_metadata_size = len(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003042
3043 # For the Authentication data block, the hash is first and then
3044 # the signature.
3045 h.authentication_data_block_size = round_to_multiple(
David Zeuthend5db21d2017-01-24 10:11:38 -05003046 alg.hash_num_bytes + alg.signature_num_bytes, 64)
David Zeuthen21e95262016-07-27 17:58:40 -04003047 h.algorithm_type = alg.algorithm_type
3048 h.hash_offset = 0
3049 h.hash_size = alg.hash_num_bytes
3050 # Signature offset and size - it's stored right after the hash
3051 # (in Authentication data block).
3052 h.signature_offset = alg.hash_num_bytes
3053 h.signature_size = alg.signature_num_bytes
3054
3055 h.rollback_index = rollback_index
David Zeuthenfd41eb92016-11-17 12:24:47 -05003056 h.flags = flags
David Zeuthen21e95262016-07-27 17:58:40 -04003057
3058 # Generate Header data block.
3059 header_data_blob = h.encode()
3060
3061 # Generate Auxiliary data block.
3062 aux_data_blob = bytearray()
3063 aux_data_blob.extend(encoded_descriptors)
3064 aux_data_blob.extend(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05003065 aux_data_blob.extend(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003066 padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob)
3067 aux_data_blob.extend('\0' * padding_bytes)
3068
3069 # Calculate the hash.
3070 binary_hash = bytearray()
3071 binary_signature = bytearray()
3072 if algorithm_name != 'NONE':
David Zeuthenb623d8b2017-04-04 16:05:53 -04003073 ha = hashlib.new(alg.hash_name)
David Zeuthen21e95262016-07-27 17:58:40 -04003074 ha.update(header_data_blob)
3075 ha.update(aux_data_blob)
3076 binary_hash.extend(ha.digest())
3077
3078 # Calculate the signature.
David Zeuthen21e95262016-07-27 17:58:40 -04003079 padding_and_hash = str(bytearray(alg.padding)) + binary_hash
David Zeuthena156d3d2017-06-01 12:08:09 -04003080 binary_signature.extend(raw_sign(signing_helper,
3081 signing_helper_with_files,
3082 algorithm_name,
Esun Kimff44f232017-03-30 10:34:54 +09003083 alg.signature_num_bytes, key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003084 padding_and_hash))
David Zeuthen21e95262016-07-27 17:58:40 -04003085
3086 # Generate Authentication data block.
3087 auth_data_blob = bytearray()
3088 auth_data_blob.extend(binary_hash)
3089 auth_data_blob.extend(binary_signature)
3090 padding_bytes = h.authentication_data_block_size - len(auth_data_blob)
3091 auth_data_blob.extend('\0' * padding_bytes)
3092
3093 return header_data_blob + auth_data_blob + aux_data_blob
3094
3095 def extract_public_key(self, key_path, output):
3096 """Implements the 'extract_public_key' command.
3097
3098 Arguments:
3099 key_path: The path to a RSA private key file.
3100 output: The file to write to.
3101 """
David Zeuthenc68f0822017-03-31 17:22:35 -04003102 output.write(encode_rsa_key(key_path))
David Zeuthen21e95262016-07-27 17:58:40 -04003103
David Zeuthenb1b994d2017-03-06 18:01:31 -05003104 def append_vbmeta_image(self, image_filename, vbmeta_image_filename,
3105 partition_size):
3106 """Implementation of the append_vbmeta_image command.
3107
3108 Arguments:
3109 image_filename: File to add the footer to.
3110 vbmeta_image_filename: File to get vbmeta struct from.
3111 partition_size: Size of partition.
3112
3113 Raises:
3114 AvbError: If an argument is incorrect.
3115 """
3116 image = ImageHandler(image_filename)
3117
3118 if partition_size % image.block_size != 0:
3119 raise AvbError('Partition size of {} is not a multiple of the image '
3120 'block size {}.'.format(partition_size,
3121 image.block_size))
3122
3123 # If there's already a footer, truncate the image to its original
3124 # size. This way 'avbtool append_vbmeta_image' is idempotent.
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003125 if image.image_size >= AvbFooter.SIZE:
3126 image.seek(image.image_size - AvbFooter.SIZE)
3127 try:
3128 footer = AvbFooter(image.read(AvbFooter.SIZE))
3129 # Existing footer found. Just truncate.
3130 original_image_size = footer.original_image_size
3131 image.truncate(footer.original_image_size)
3132 except (LookupError, struct.error):
3133 original_image_size = image.image_size
3134 else:
3135 # Image size is too small to possibly contain a footer.
David Zeuthenb1b994d2017-03-06 18:01:31 -05003136 original_image_size = image.image_size
3137
3138 # If anything goes wrong from here-on, restore the image back to
3139 # its original size.
3140 try:
3141 vbmeta_image_handler = ImageHandler(vbmeta_image_filename)
3142 vbmeta_blob = self._load_vbmeta_blob(vbmeta_image_handler)
3143
3144 # If the image isn't sparse, its size might not be a multiple of
3145 # the block size. This will screw up padding later so just grow it.
3146 if image.image_size % image.block_size != 0:
3147 assert not image.is_sparse
3148 padding_needed = image.block_size - (image.image_size%image.block_size)
3149 image.truncate(image.image_size + padding_needed)
3150
3151 # The append_raw() method requires content with size being a
3152 # multiple of |block_size| so add padding as needed. Also record
3153 # where this is written to since we'll need to put that in the
3154 # footer.
3155 vbmeta_offset = image.image_size
3156 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
3157 len(vbmeta_blob))
3158 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
3159
3160 # Append vbmeta blob and footer
3161 image.append_raw(vbmeta_blob_with_padding)
3162 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
3163
3164 # Now insert a DONT_CARE chunk with enough bytes such that the
3165 # final Footer block is at the end of partition_size..
3166 image.append_dont_care(partition_size - vbmeta_end_offset -
3167 1*image.block_size)
3168
3169 # Generate the Footer that tells where the VBMeta footer
3170 # is. Also put enough padding in the front of the footer since
3171 # we'll write out an entire block.
3172 footer = AvbFooter()
3173 footer.original_image_size = original_image_size
3174 footer.vbmeta_offset = vbmeta_offset
3175 footer.vbmeta_size = len(vbmeta_blob)
3176 footer_blob = footer.encode()
3177 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
3178 footer_blob)
3179 image.append_raw(footer_blob_with_padding)
3180
3181 except:
3182 # Truncate back to original size, then re-raise
3183 image.truncate(original_image_size)
3184 raise
3185
David Zeuthena4fee8b2016-08-22 15:20:43 -04003186 def add_hash_footer(self, image_filename, partition_size, partition_name,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003187 hash_algorithm, salt, chain_partitions, algorithm_name,
3188 key_path,
3189 public_key_metadata_path, rollback_index, flags, props,
David Zeuthen18666ab2016-11-15 11:18:05 -05003190 props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003191 setup_rootfs_from_kernel,
David Zeuthenbf562452017-05-17 18:04:43 -04003192 include_descriptors_from_image, calc_max_image_size,
David Zeuthena156d3d2017-06-01 12:08:09 -04003193 signing_helper, signing_helper_with_files,
3194 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003195 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003196 print_required_libavb_version, use_persistent_digest,
3197 do_not_use_ab):
David Zeuthena4fee8b2016-08-22 15:20:43 -04003198 """Implementation of the add_hash_footer on unsparse images.
David Zeuthen21e95262016-07-27 17:58:40 -04003199
3200 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003201 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04003202 partition_size: Size of partition.
3203 partition_name: Name of partition (without A/B suffix).
3204 hash_algorithm: Hash algorithm to use.
3205 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003206 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04003207 algorithm_name: Name of algorithm to use.
3208 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05003209 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003210 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003211 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04003212 props: Properties to insert (List of strings of the form 'key:value').
3213 props_from_file: Properties to insert (List of strings 'key:<path>').
3214 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003215 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003216 dm-verity kernel cmdline from.
3217 include_descriptors_from_image: List of file objects for which
3218 to insert descriptors from.
David Zeuthenbf562452017-05-17 18:04:43 -04003219 calc_max_image_size: Don't store the footer - instead calculate the
3220 maximum image size leaving enough room for metadata with the
3221 given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003222 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003223 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003224 release_string: None or avbtool release string.
3225 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05003226 output_vbmeta_image: If not None, also write vbmeta struct to this file.
3227 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04003228 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003229 use_persistent_digest: Use a persistent digest on device.
3230 do_not_use_ab: This partition does not use A/B.
David Zeuthena4fee8b2016-08-22 15:20:43 -04003231
3232 Raises:
3233 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04003234 """
David Zeuthen1097a782017-05-31 15:53:17 -04003235
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003236 required_libavb_version_minor = 0
3237 if use_persistent_digest or do_not_use_ab:
3238 required_libavb_version_minor = 1
3239
David Zeuthen1097a782017-05-31 15:53:17 -04003240 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003241 if print_required_libavb_version:
Jan Monsch23e0c622019-12-11 11:23:58 +01003242 print('1.{}'.format(required_libavb_version_minor))
David Zeuthen1097a782017-05-31 15:53:17 -04003243 return
3244
David Zeuthenbf562452017-05-17 18:04:43 -04003245 # First, calculate the maximum image size such that an image
3246 # this size + metadata (footer + vbmeta struct) fits in
3247 # |partition_size|.
3248 max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003249 if partition_size < max_metadata_size:
3250 raise AvbError('Parition size of {} is too small. '
3251 'Needs to be at least {}'.format(
3252 partition_size, max_metadata_size))
David Zeuthenbf562452017-05-17 18:04:43 -04003253 max_image_size = partition_size - max_metadata_size
3254
3255 # If we're asked to only calculate the maximum image size, we're done.
3256 if calc_max_image_size:
Jan Monsch23e0c622019-12-11 11:23:58 +01003257 print('{}'.format(max_image_size))
David Zeuthenbf562452017-05-17 18:04:43 -04003258 return
3259
David Zeuthena4fee8b2016-08-22 15:20:43 -04003260 image = ImageHandler(image_filename)
3261
3262 if partition_size % image.block_size != 0:
3263 raise AvbError('Partition size of {} is not a multiple of the image '
3264 'block size {}.'.format(partition_size,
3265 image.block_size))
3266
David Zeuthen21e95262016-07-27 17:58:40 -04003267 # If there's already a footer, truncate the image to its original
3268 # size. This way 'avbtool add_hash_footer' is idempotent (modulo
3269 # salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003270 if image.image_size >= AvbFooter.SIZE:
3271 image.seek(image.image_size - AvbFooter.SIZE)
3272 try:
3273 footer = AvbFooter(image.read(AvbFooter.SIZE))
3274 # Existing footer found. Just truncate.
3275 original_image_size = footer.original_image_size
3276 image.truncate(footer.original_image_size)
3277 except (LookupError, struct.error):
3278 original_image_size = image.image_size
3279 else:
3280 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04003281 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003282
3283 # If anything goes wrong from here-on, restore the image back to
3284 # its original size.
3285 try:
David Zeuthen09692692016-09-30 16:16:40 -04003286 # If image size exceeds the maximum image size, fail.
3287 if image.image_size > max_image_size:
3288 raise AvbError('Image size of {} exceeds maximum image '
3289 'size of {} in order to fit in a partition '
3290 'size of {}.'.format(image.image_size, max_image_size,
3291 partition_size))
3292
Jan Monsch6f27bb12020-04-07 07:33:26 +02003293 digest_size = len(hashlib.new(hash_algorithm).digest())
David Zeuthen21e95262016-07-27 17:58:40 -04003294 if salt:
Jan Monsch23e0c622019-12-11 11:23:58 +01003295 salt = binascii.unhexlify(salt)
3296 elif salt is None and not use_persistent_digest:
3297 # If salt is not explicitly specified, choose a hash that's the same
3298 # size as the hash size. Don't populate a random salt if this
3299 # descriptor is being created to use a persistent digest on device.
3300 hash_size = digest_size
3301 salt = open('/dev/urandom').read(hash_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003302 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01003303 salt = ''
David Zeuthen21e95262016-07-27 17:58:40 -04003304
Jan Monsch6f27bb12020-04-07 07:33:26 +02003305 hasher = hashlib.new(hash_algorithm, salt)
David Zeuthen21e95262016-07-27 17:58:40 -04003306 # TODO(zeuthen): might want to read this in chunks to avoid
3307 # memory pressure, then again, this is only supposed to be used
3308 # on kernel/initramfs partitions. Possible optimization.
3309 image.seek(0)
David Zeuthen09692692016-09-30 16:16:40 -04003310 hasher.update(image.read(image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003311 digest = hasher.digest()
3312
3313 h_desc = AvbHashDescriptor()
David Zeuthen09692692016-09-30 16:16:40 -04003314 h_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003315 h_desc.hash_algorithm = hash_algorithm
3316 h_desc.partition_name = partition_name
3317 h_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003318 h_desc.flags = 0
3319 if do_not_use_ab:
3320 h_desc.flags |= 1 # AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
3321 if not use_persistent_digest:
3322 h_desc.digest = digest
David Zeuthen21e95262016-07-27 17:58:40 -04003323
3324 # Generate the VBMeta footer.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003325 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04003326 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003327 algorithm_name, key_path, public_key_metadata_path, [h_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05003328 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003329 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003330 include_descriptors_from_image, signing_helper,
3331 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003332 append_to_release_string, required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04003333
David Zeuthend247fcb2017-02-16 12:09:27 -05003334 # Write vbmeta blob, if requested.
3335 if output_vbmeta_image:
3336 output_vbmeta_image.write(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003337
David Zeuthend247fcb2017-02-16 12:09:27 -05003338 # Append vbmeta blob and footer, unless requested not to.
3339 if not do_not_append_vbmeta_image:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003340 # If the image isn't sparse, its size might not be a multiple of
3341 # the block size. This will screw up padding later so just grow it.
3342 if image.image_size % image.block_size != 0:
3343 assert not image.is_sparse
3344 padding_needed = image.block_size - (
3345 image.image_size % image.block_size)
3346 image.truncate(image.image_size + padding_needed)
3347
3348 # The append_raw() method requires content with size being a
3349 # multiple of |block_size| so add padding as needed. Also record
3350 # where this is written to since we'll need to put that in the
3351 # footer.
3352 vbmeta_offset = image.image_size
3353 padding_needed = (
3354 round_to_multiple(len(vbmeta_blob), image.block_size) -
3355 len(vbmeta_blob))
3356 vbmeta_blob_with_padding = vbmeta_blob + '\0' * padding_needed
3357
David Zeuthend247fcb2017-02-16 12:09:27 -05003358 image.append_raw(vbmeta_blob_with_padding)
3359 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
3360
3361 # Now insert a DONT_CARE chunk with enough bytes such that the
3362 # final Footer block is at the end of partition_size..
3363 image.append_dont_care(partition_size - vbmeta_end_offset -
3364 1*image.block_size)
3365
3366 # Generate the Footer that tells where the VBMeta footer
3367 # is. Also put enough padding in the front of the footer since
3368 # we'll write out an entire block.
3369 footer = AvbFooter()
3370 footer.original_image_size = original_image_size
3371 footer.vbmeta_offset = vbmeta_offset
3372 footer.vbmeta_size = len(vbmeta_blob)
3373 footer_blob = footer.encode()
3374 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
3375 footer_blob)
3376 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003377
David Zeuthen21e95262016-07-27 17:58:40 -04003378 except:
3379 # Truncate back to original size, then re-raise
3380 image.truncate(original_image_size)
3381 raise
3382
David Zeuthena4fee8b2016-08-22 15:20:43 -04003383 def add_hashtree_footer(self, image_filename, partition_size, partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003384 generate_fec, fec_num_roots, hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003385 block_size, salt, chain_partitions, algorithm_name,
3386 key_path,
3387 public_key_metadata_path, rollback_index, flags,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003388 props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003389 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003390 setup_as_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04003391 include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05003392 calc_max_image_size, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003393 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05003394 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003395 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003396 print_required_libavb_version,
Jan Monscheeb28b62019-12-05 16:17:09 +01003397 use_persistent_root_digest, do_not_use_ab,
3398 no_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04003399 """Implements the 'add_hashtree_footer' command.
3400
3401 See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
3402 more information about dm-verity and these hashes.
3403
3404 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003405 image_filename: File to add the footer to.
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003406 partition_size: Size of partition or 0 to put it right at the end.
David Zeuthen21e95262016-07-27 17:58:40 -04003407 partition_name: Name of partition (without A/B suffix).
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003408 generate_fec: If True, generate FEC codes.
3409 fec_num_roots: Number of roots for FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04003410 hash_algorithm: Hash algorithm to use.
3411 block_size: Block size to use.
3412 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003413 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04003414 algorithm_name: Name of algorithm to use.
3415 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05003416 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003417 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003418 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04003419 props: Properties to insert (List of strings of the form 'key:value').
3420 props_from_file: Properties to insert (List of strings 'key:<path>').
3421 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003422 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003423 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003424 setup_as_rootfs_from_kernel: If True, generate dm-verity kernel
3425 cmdline to set up rootfs.
David Zeuthen21e95262016-07-27 17:58:40 -04003426 include_descriptors_from_image: List of file objects for which
3427 to insert descriptors from.
David Zeuthen09692692016-09-30 16:16:40 -04003428 calc_max_image_size: Don't store the hashtree or footer - instead
3429 calculate the maximum image size leaving enough room for hashtree
3430 and metadata with the given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003431 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003432 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003433 release_string: None or avbtool release string.
3434 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05003435 output_vbmeta_image: If not None, also write vbmeta struct to this file.
3436 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04003437 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003438 use_persistent_root_digest: Use a persistent root digest on device.
3439 do_not_use_ab: The partition does not use A/B.
Jooyung Hand7221942019-06-17 13:19:57 +09003440 no_hashtree: Do not append hashtree. Set size in descriptor as zero.
David Zeuthena4fee8b2016-08-22 15:20:43 -04003441
3442 Raises:
3443 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04003444 """
David Zeuthen1097a782017-05-31 15:53:17 -04003445
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003446 required_libavb_version_minor = 0
3447 if use_persistent_root_digest or do_not_use_ab:
3448 required_libavb_version_minor = 1
3449
David Zeuthen1097a782017-05-31 15:53:17 -04003450 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003451 if print_required_libavb_version:
Jan Monsch23e0c622019-12-11 11:23:58 +01003452 print('1.{}'.format(required_libavb_version_minor))
David Zeuthen1097a782017-05-31 15:53:17 -04003453 return
3454
Jan Monsch6f27bb12020-04-07 07:33:26 +02003455 digest_size = len(hashlib.new(hash_algorithm).digest())
David Zeuthen09692692016-09-30 16:16:40 -04003456 digest_padding = round_to_pow2(digest_size) - digest_size
3457
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003458 # If |partition_size| is given (e.g. not 0), calculate the maximum image
3459 # size such that an image this size + the hashtree + metadata (footer +
3460 # vbmeta struct) fits in |partition_size|. We use very conservative figures
3461 # for metadata.
3462 if partition_size > 0:
Jooyung Hand7221942019-06-17 13:19:57 +09003463 max_tree_size = 0
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003464 max_fec_size = 0
Jooyung Hand7221942019-06-17 13:19:57 +09003465 if not no_hashtree:
3466 (_, max_tree_size) = calc_hash_level_offsets(
3467 partition_size, block_size, digest_size + digest_padding)
3468 if generate_fec:
3469 max_fec_size = calc_fec_data_size(partition_size, fec_num_roots)
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003470 max_metadata_size = (max_fec_size + max_tree_size +
3471 self.MAX_VBMETA_SIZE +
3472 self.MAX_FOOTER_SIZE)
3473 max_image_size = partition_size - max_metadata_size
3474 else:
3475 max_image_size = 0
David Zeuthen09692692016-09-30 16:16:40 -04003476
3477 # If we're asked to only calculate the maximum image size, we're done.
3478 if calc_max_image_size:
Jan Monsch23e0c622019-12-11 11:23:58 +01003479 print('{}'.format(max_image_size))
David Zeuthen09692692016-09-30 16:16:40 -04003480 return
3481
David Zeuthena4fee8b2016-08-22 15:20:43 -04003482 image = ImageHandler(image_filename)
3483
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003484 if partition_size > 0:
3485 if partition_size % image.block_size != 0:
3486 raise AvbError('Partition size of {} is not a multiple of the image '
3487 'block size {}.'.format(partition_size,
3488 image.block_size))
Jan Monsch23e0c622019-12-11 11:23:58 +01003489 elif image.image_size % image.block_size != 0:
3490 raise AvbError('File size of {} is not a multiple of the image '
3491 'block size {}.'.format(image.image_size,
3492 image.block_size))
David Zeuthena4fee8b2016-08-22 15:20:43 -04003493
David Zeuthen21e95262016-07-27 17:58:40 -04003494 # If there's already a footer, truncate the image to its original
3495 # size. This way 'avbtool add_hashtree_footer' is idempotent
3496 # (modulo salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003497 if image.image_size >= AvbFooter.SIZE:
3498 image.seek(image.image_size - AvbFooter.SIZE)
3499 try:
3500 footer = AvbFooter(image.read(AvbFooter.SIZE))
3501 # Existing footer found. Just truncate.
3502 original_image_size = footer.original_image_size
3503 image.truncate(footer.original_image_size)
3504 except (LookupError, struct.error):
3505 original_image_size = image.image_size
3506 else:
3507 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04003508 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003509
3510 # If anything goes wrong from here-on, restore the image back to
3511 # its original size.
3512 try:
3513 # Ensure image is multiple of block_size.
David Zeuthen09692692016-09-30 16:16:40 -04003514 rounded_image_size = round_to_multiple(image.image_size, block_size)
3515 if rounded_image_size > image.image_size:
3516 image.append_raw('\0' * (rounded_image_size - image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003517
David Zeuthen09692692016-09-30 16:16:40 -04003518 # If image size exceeds the maximum image size, fail.
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003519 if partition_size > 0:
3520 if image.image_size > max_image_size:
3521 raise AvbError('Image size of {} exceeds maximum image '
3522 'size of {} in order to fit in a partition '
3523 'size of {}.'.format(image.image_size, max_image_size,
3524 partition_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003525
3526 if salt:
Jan Monsch23e0c622019-12-11 11:23:58 +01003527 salt = binascii.unhexlify(salt)
3528 elif salt is None and not use_persistent_root_digest:
3529 # If salt is not explicitly specified, choose a hash that's the same
3530 # size as the hash size. Don't populate a random salt if this
3531 # descriptor is being created to use a persistent digest on device.
3532 hash_size = digest_size
3533 salt = open('/dev/urandom').read(hash_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003534 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01003535 salt = ''
David Zeuthen21e95262016-07-27 17:58:40 -04003536
David Zeuthena4fee8b2016-08-22 15:20:43 -04003537 # Hashes are stored upside down so we need to calculate hash
David Zeuthen21e95262016-07-27 17:58:40 -04003538 # offsets in advance.
3539 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
David Zeuthen09692692016-09-30 16:16:40 -04003540 image.image_size, block_size, digest_size + digest_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04003541
David Zeuthena4fee8b2016-08-22 15:20:43 -04003542 # If the image isn't sparse, its size might not be a multiple of
3543 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04003544 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003545 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04003546 padding_needed = image.block_size - (image.image_size%image.block_size)
3547 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04003548
David Zeuthena4fee8b2016-08-22 15:20:43 -04003549 # Generate the tree and add padding as needed.
David Zeuthen09692692016-09-30 16:16:40 -04003550 tree_offset = image.image_size
3551 root_digest, hash_tree = generate_hash_tree(image, image.image_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003552 block_size,
3553 hash_algorithm, salt,
3554 digest_padding,
3555 hash_level_offsets,
3556 tree_size)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003557
3558 # Generate HashtreeDescriptor with details about the tree we
3559 # just generated.
Jooyung Hand7221942019-06-17 13:19:57 +09003560 if no_hashtree:
3561 tree_size = 0
3562 hash_tree = bytearray()
David Zeuthen21e95262016-07-27 17:58:40 -04003563 ht_desc = AvbHashtreeDescriptor()
3564 ht_desc.dm_verity_version = 1
David Zeuthen09692692016-09-30 16:16:40 -04003565 ht_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003566 ht_desc.tree_offset = tree_offset
3567 ht_desc.tree_size = tree_size
3568 ht_desc.data_block_size = block_size
3569 ht_desc.hash_block_size = block_size
3570 ht_desc.hash_algorithm = hash_algorithm
3571 ht_desc.partition_name = partition_name
3572 ht_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003573 if do_not_use_ab:
3574 ht_desc.flags |= 1 # AVB_HASHTREE_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
3575 if not use_persistent_root_digest:
3576 ht_desc.root_digest = root_digest
David Zeuthen21e95262016-07-27 17:58:40 -04003577
David Zeuthen09692692016-09-30 16:16:40 -04003578 # Write the hash tree
3579 padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
3580 len(hash_tree))
3581 hash_tree_with_padding = hash_tree + '\0'*padding_needed
3582 image.append_raw(hash_tree_with_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003583 len_hashtree_and_fec = len(hash_tree_with_padding)
3584
3585 # Generate FEC codes, if requested.
3586 if generate_fec:
Jooyung Hand7221942019-06-17 13:19:57 +09003587 if no_hashtree:
3588 fec_data = bytearray()
Tao Bao868db2a2019-09-09 13:35:05 -07003589 else:
3590 fec_data = generate_fec_data(image_filename, fec_num_roots)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003591 padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
3592 len(fec_data))
3593 fec_data_with_padding = fec_data + '\0'*padding_needed
3594 fec_offset = image.image_size
3595 image.append_raw(fec_data_with_padding)
3596 len_hashtree_and_fec += len(fec_data_with_padding)
3597 # Update the hashtree descriptor.
3598 ht_desc.fec_num_roots = fec_num_roots
3599 ht_desc.fec_offset = fec_offset
3600 ht_desc.fec_size = len(fec_data)
David Zeuthen09692692016-09-30 16:16:40 -04003601
David Zeuthen73f2afa2017-05-17 16:54:11 -04003602 ht_desc_to_setup = None
3603 if setup_as_rootfs_from_kernel:
3604 ht_desc_to_setup = ht_desc
3605
David Zeuthena4fee8b2016-08-22 15:20:43 -04003606 # Generate the VBMeta footer and add padding as needed.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003607 vbmeta_offset = tree_offset + len_hashtree_and_fec
David Zeuthen21e95262016-07-27 17:58:40 -04003608 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003609 algorithm_name, key_path, public_key_metadata_path, [ht_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05003610 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003611 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003612 include_descriptors_from_image, signing_helper,
3613 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003614 append_to_release_string, required_libavb_version_minor)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003615 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
3616 len(vbmeta_blob))
3617 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
David Zeuthen21e95262016-07-27 17:58:40 -04003618
David Zeuthend247fcb2017-02-16 12:09:27 -05003619 # Write vbmeta blob, if requested.
3620 if output_vbmeta_image:
3621 output_vbmeta_image.write(vbmeta_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003622
David Zeuthend247fcb2017-02-16 12:09:27 -05003623 # Append vbmeta blob and footer, unless requested not to.
3624 if not do_not_append_vbmeta_image:
3625 image.append_raw(vbmeta_blob_with_padding)
3626
3627 # Now insert a DONT_CARE chunk with enough bytes such that the
3628 # final Footer block is at the end of partition_size..
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003629 if partition_size > 0:
3630 image.append_dont_care(partition_size - image.image_size -
3631 1*image.block_size)
David Zeuthend247fcb2017-02-16 12:09:27 -05003632
3633 # Generate the Footer that tells where the VBMeta footer
3634 # is. Also put enough padding in the front of the footer since
3635 # we'll write out an entire block.
3636 footer = AvbFooter()
3637 footer.original_image_size = original_image_size
3638 footer.vbmeta_offset = vbmeta_offset
3639 footer.vbmeta_size = len(vbmeta_blob)
3640 footer_blob = footer.encode()
3641 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
3642 footer_blob)
3643 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003644
David Zeuthen21e95262016-07-27 17:58:40 -04003645 except:
David Zeuthen09692692016-09-30 16:16:40 -04003646 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04003647 image.truncate(original_image_size)
3648 raise
3649
David Zeuthenc68f0822017-03-31 17:22:35 -04003650 def make_atx_certificate(self, output, authority_key_path, subject_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003651 subject_key_version, subject,
Darren Krahnfccd64e2018-01-16 17:39:35 -08003652 is_intermediate_authority, usage, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003653 signing_helper_with_files):
Darren Krahn147b08d2016-12-20 16:38:29 -08003654 """Implements the 'make_atx_certificate' command.
3655
3656 Android Things certificates are required for Android Things public key
3657 metadata. They chain the vbmeta signing key for a particular product back to
3658 a fused, permanent root key. These certificates are fixed-length and fixed-
3659 format with the explicit goal of not parsing ASN.1 in bootloader code.
3660
3661 Arguments:
3662 output: Certificate will be written to this file on success.
3663 authority_key_path: A PEM file path with the authority private key.
3664 If None, then a certificate will be created without a
3665 signature. The signature can be created out-of-band
3666 and appended.
David Zeuthenc68f0822017-03-31 17:22:35 -04003667 subject_key_path: Path to a PEM or DER subject public key.
Darren Krahn147b08d2016-12-20 16:38:29 -08003668 subject_key_version: A 64-bit version value. If this is None, the number
3669 of seconds since the epoch is used.
3670 subject: A subject identifier. For Product Signing Key certificates this
3671 should be the same Product ID found in the permanent attributes.
3672 is_intermediate_authority: True if the certificate is for an intermediate
3673 authority.
Darren Krahnfccd64e2018-01-16 17:39:35 -08003674 usage: If not empty, overrides the cert usage with a hash of this value.
Darren Krahn147b08d2016-12-20 16:38:29 -08003675 signing_helper: Program which signs a hash and returns the signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003676 signing_helper_with_files: Same as signing_helper but uses files instead.
Darren Krahn147b08d2016-12-20 16:38:29 -08003677 """
3678 signed_data = bytearray()
3679 signed_data.extend(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04003680 signed_data.extend(encode_rsa_key(subject_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08003681 hasher = hashlib.sha256()
3682 hasher.update(subject)
3683 signed_data.extend(hasher.digest())
Darren Krahnfccd64e2018-01-16 17:39:35 -08003684 if not usage:
3685 usage = 'com.google.android.things.vboot'
3686 if is_intermediate_authority:
3687 usage += '.ca'
Darren Krahn147b08d2016-12-20 16:38:29 -08003688 hasher = hashlib.sha256()
3689 hasher.update(usage)
3690 signed_data.extend(hasher.digest())
Yu Shanc8540812019-07-01 16:54:46 -07003691 if subject_key_version is None:
Darren Krahn147b08d2016-12-20 16:38:29 -08003692 subject_key_version = int(time.time())
3693 signed_data.extend(struct.pack('<Q', subject_key_version))
3694 signature = bytearray()
3695 if authority_key_path:
3696 padding_and_hash = bytearray()
Darren Krahn43e12d82017-02-24 16:26:31 -08003697 algorithm_name = 'SHA512_RSA4096'
Esun Kimff44f232017-03-30 10:34:54 +09003698 alg = ALGORITHMS[algorithm_name]
Jan Monsch23e0c622019-12-11 11:23:58 +01003699 hasher = hashlib.sha512() # pylint: disable=redefined-variable-type
Esun Kimff44f232017-03-30 10:34:54 +09003700 padding_and_hash.extend(alg.padding)
Darren Krahn147b08d2016-12-20 16:38:29 -08003701 hasher.update(signed_data)
3702 padding_and_hash.extend(hasher.digest())
David Zeuthena156d3d2017-06-01 12:08:09 -04003703 signature.extend(raw_sign(signing_helper, signing_helper_with_files,
3704 algorithm_name,
Esun Kimff44f232017-03-30 10:34:54 +09003705 alg.signature_num_bytes, authority_key_path,
3706 padding_and_hash))
Darren Krahn147b08d2016-12-20 16:38:29 -08003707 output.write(signed_data)
3708 output.write(signature)
3709
David Zeuthenc68f0822017-03-31 17:22:35 -04003710 def make_atx_permanent_attributes(self, output, root_authority_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003711 product_id):
3712 """Implements the 'make_atx_permanent_attributes' command.
3713
3714 Android Things permanent attributes are designed to be permanent for a
3715 particular product and a hash of these attributes should be fused into
3716 hardware to enforce this.
3717
3718 Arguments:
3719 output: Attributes will be written to this file on success.
David Zeuthenc68f0822017-03-31 17:22:35 -04003720 root_authority_key_path: Path to a PEM or DER public key for
3721 the root authority.
Darren Krahn147b08d2016-12-20 16:38:29 -08003722 product_id: A 16-byte Product ID.
3723
3724 Raises:
3725 AvbError: If an argument is incorrect.
3726 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01003727 EXPECTED_PRODUCT_ID_SIZE = 16 # pylint: disable=invalid-name
Darren Krahn43e12d82017-02-24 16:26:31 -08003728 if len(product_id) != EXPECTED_PRODUCT_ID_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003729 raise AvbError('Invalid Product ID length.')
3730 output.write(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04003731 output.write(encode_rsa_key(root_authority_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08003732 output.write(product_id)
3733
3734 def make_atx_metadata(self, output, intermediate_key_certificate,
Darren Krahn43e12d82017-02-24 16:26:31 -08003735 product_key_certificate):
Darren Krahn147b08d2016-12-20 16:38:29 -08003736 """Implements the 'make_atx_metadata' command.
3737
3738 Android Things metadata are included in vbmeta images to facilitate
3739 verification. The output of this command can be used as the
3740 public_key_metadata argument to other commands.
3741
3742 Arguments:
3743 output: Metadata will be written to this file on success.
3744 intermediate_key_certificate: A certificate file as output by
3745 make_atx_certificate with
3746 is_intermediate_authority set to true.
3747 product_key_certificate: A certificate file as output by
3748 make_atx_certificate with
3749 is_intermediate_authority set to false.
Darren Krahn147b08d2016-12-20 16:38:29 -08003750
3751 Raises:
3752 AvbError: If an argument is incorrect.
3753 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01003754 EXPECTED_CERTIFICATE_SIZE = 1620 # pylint: disable=invalid-name
Darren Krahn43e12d82017-02-24 16:26:31 -08003755 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003756 raise AvbError('Invalid intermediate key certificate length.')
Darren Krahn43e12d82017-02-24 16:26:31 -08003757 if len(product_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003758 raise AvbError('Invalid product key certificate length.')
3759 output.write(struct.pack('<I', 1)) # Format Version
3760 output.write(intermediate_key_certificate)
3761 output.write(product_key_certificate)
Darren Krahn147b08d2016-12-20 16:38:29 -08003762
Darren Krahnfccd64e2018-01-16 17:39:35 -08003763 def make_atx_unlock_credential(self, output, intermediate_key_certificate,
3764 unlock_key_certificate, challenge_path,
3765 unlock_key_path, signing_helper,
3766 signing_helper_with_files):
3767 """Implements the 'make_atx_unlock_credential' command.
3768
3769 Android Things unlock credentials can be used to authorize the unlock of AVB
3770 on a device. These credentials are presented to an Android Things bootloader
3771 via the fastboot interface in response to a 16-byte challenge. This method
3772 creates all fields of the credential except the challenge signature field
3773 (which is the last field) and can optionally create the challenge signature
3774 field as well if a challenge and the unlock_key_path is provided.
3775
3776 Arguments:
3777 output: The credential will be written to this file on success.
3778 intermediate_key_certificate: A certificate file as output by
3779 make_atx_certificate with
3780 is_intermediate_authority set to true.
3781 unlock_key_certificate: A certificate file as output by
3782 make_atx_certificate with
3783 is_intermediate_authority set to false and the
3784 usage set to
3785 'com.google.android.things.vboot.unlock'.
3786 challenge_path: [optional] A path to the challenge to sign.
3787 unlock_key_path: [optional] A PEM file path with the unlock private key.
3788 signing_helper: Program which signs a hash and returns the signature.
3789 signing_helper_with_files: Same as signing_helper but uses files instead.
3790
3791 Raises:
3792 AvbError: If an argument is incorrect.
3793 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01003794 EXPECTED_CERTIFICATE_SIZE = 1620 # pylint: disable=invalid-name
3795 EXPECTED_CHALLENGE_SIZE = 16 # pylint: disable=invalid-name
Darren Krahnfccd64e2018-01-16 17:39:35 -08003796 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
3797 raise AvbError('Invalid intermediate key certificate length.')
3798 if len(unlock_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
3799 raise AvbError('Invalid product key certificate length.')
3800 challenge = bytearray()
3801 if challenge_path:
3802 with open(challenge_path, 'r') as f:
3803 challenge = f.read()
3804 if len(challenge) != EXPECTED_CHALLENGE_SIZE:
3805 raise AvbError('Invalid unlock challenge length.')
3806 output.write(struct.pack('<I', 1)) # Format Version
3807 output.write(intermediate_key_certificate)
3808 output.write(unlock_key_certificate)
3809 if challenge_path and unlock_key_path:
3810 signature = bytearray()
3811 padding_and_hash = bytearray()
3812 algorithm_name = 'SHA512_RSA4096'
3813 alg = ALGORITHMS[algorithm_name]
3814 hasher = hashlib.sha512()
3815 padding_and_hash.extend(alg.padding)
3816 hasher.update(challenge)
3817 padding_and_hash.extend(hasher.digest())
3818 signature.extend(raw_sign(signing_helper, signing_helper_with_files,
3819 algorithm_name,
3820 alg.signature_num_bytes, unlock_key_path,
3821 padding_and_hash))
3822 output.write(signature)
3823
David Zeuthen21e95262016-07-27 17:58:40 -04003824
3825def calc_hash_level_offsets(image_size, block_size, digest_size):
3826 """Calculate the offsets of all the hash-levels in a Merkle-tree.
3827
3828 Arguments:
3829 image_size: The size of the image to calculate a Merkle-tree for.
3830 block_size: The block size, e.g. 4096.
3831 digest_size: The size of each hash, e.g. 32 for SHA-256.
3832
3833 Returns:
3834 A tuple where the first argument is an array of offsets and the
3835 second is size of the tree, in bytes.
3836 """
3837 level_offsets = []
3838 level_sizes = []
3839 tree_size = 0
3840
3841 num_levels = 0
3842 size = image_size
3843 while size > block_size:
Jan Monsch23e0c622019-12-11 11:23:58 +01003844 num_blocks = (size + block_size - 1) // block_size
David Zeuthen21e95262016-07-27 17:58:40 -04003845 level_size = round_to_multiple(num_blocks * digest_size, block_size)
3846
3847 level_sizes.append(level_size)
3848 tree_size += level_size
3849 num_levels += 1
3850
3851 size = level_size
3852
3853 for n in range(0, num_levels):
3854 offset = 0
3855 for m in range(n + 1, num_levels):
3856 offset += level_sizes[m]
3857 level_offsets.append(offset)
3858
David Zeuthena4fee8b2016-08-22 15:20:43 -04003859 return level_offsets, tree_size
David Zeuthen21e95262016-07-27 17:58:40 -04003860
3861
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003862# See system/extras/libfec/include/fec/io.h for these definitions.
3863FEC_FOOTER_FORMAT = '<LLLLLQ32s'
3864FEC_MAGIC = 0xfecfecfe
3865
3866
3867def calc_fec_data_size(image_size, num_roots):
3868 """Calculates how much space FEC data will take.
3869
Jan Monschfe00c0a2019-12-11 11:19:40 +01003870 Arguments:
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003871 image_size: The size of the image.
3872 num_roots: Number of roots.
3873
3874 Returns:
3875 The number of bytes needed for FEC for an image of the given size
3876 and with the requested number of FEC roots.
3877
3878 Raises:
3879 ValueError: If output from the 'fec' tool is invalid.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003880 """
3881 p = subprocess.Popen(
3882 ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)],
3883 stdout=subprocess.PIPE,
3884 stderr=subprocess.PIPE)
3885 (pout, perr) = p.communicate()
3886 retcode = p.wait()
3887 if retcode != 0:
3888 raise ValueError('Error invoking fec: {}'.format(perr))
3889 return int(pout)
3890
3891
3892def generate_fec_data(image_filename, num_roots):
3893 """Generate FEC codes for an image.
3894
Jan Monschfe00c0a2019-12-11 11:19:40 +01003895 Arguments:
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003896 image_filename: The filename of the image.
3897 num_roots: Number of roots.
3898
3899 Returns:
3900 The FEC data blob.
3901
3902 Raises:
3903 ValueError: If output from the 'fec' tool is invalid.
3904 """
3905 fec_tmpfile = tempfile.NamedTemporaryFile()
3906 subprocess.check_call(
3907 ['fec', '--encode', '--roots', str(num_roots), image_filename,
3908 fec_tmpfile.name],
3909 stderr=open(os.devnull))
3910 fec_data = fec_tmpfile.read()
3911 footer_size = struct.calcsize(FEC_FOOTER_FORMAT)
3912 footer_data = fec_data[-footer_size:]
3913 (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT,
3914 footer_data)
3915 if magic != FEC_MAGIC:
3916 raise ValueError('Unexpected magic in FEC footer')
3917 return fec_data[0:fec_size]
3918
3919
David Zeuthen21e95262016-07-27 17:58:40 -04003920def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003921 digest_padding, hash_level_offsets, tree_size):
David Zeuthen21e95262016-07-27 17:58:40 -04003922 """Generates a Merkle-tree for a file.
3923
Jan Monschfe00c0a2019-12-11 11:19:40 +01003924 Arguments:
David Zeuthen21e95262016-07-27 17:58:40 -04003925 image: The image, as a file.
3926 image_size: The size of the image.
3927 block_size: The block size, e.g. 4096.
3928 hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'.
3929 salt: The salt to use.
3930 digest_padding: The padding for each digest.
David Zeuthen21e95262016-07-27 17:58:40 -04003931 hash_level_offsets: The offsets from calc_hash_level_offsets().
David Zeuthena4fee8b2016-08-22 15:20:43 -04003932 tree_size: The size of the tree, in number of bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04003933
3934 Returns:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003935 A tuple where the first element is the top-level hash and the
3936 second element is the hash-tree.
David Zeuthen21e95262016-07-27 17:58:40 -04003937 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04003938 hash_ret = bytearray(tree_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003939 hash_src_offset = 0
3940 hash_src_size = image_size
3941 level_num = 0
3942 while hash_src_size > block_size:
Colin Cross388338a2020-02-28 14:18:01 -08003943 level_output_list = []
David Zeuthen21e95262016-07-27 17:58:40 -04003944 remaining = hash_src_size
3945 while remaining > 0:
Jan Monsch6f27bb12020-04-07 07:33:26 +02003946 hasher = hashlib.new(hash_alg_name, salt)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003947 # Only read from the file for the first level - for subsequent
3948 # levels, access the array we're building.
3949 if level_num == 0:
3950 image.seek(hash_src_offset + hash_src_size - remaining)
3951 data = image.read(min(remaining, block_size))
3952 else:
3953 offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining
3954 data = hash_ret[offset:offset + block_size]
David Zeuthen21e95262016-07-27 17:58:40 -04003955 hasher.update(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003956
3957 remaining -= len(data)
David Zeuthen21e95262016-07-27 17:58:40 -04003958 if len(data) < block_size:
3959 hasher.update('\0' * (block_size - len(data)))
Colin Cross388338a2020-02-28 14:18:01 -08003960 level_output_list.append(hasher.digest())
David Zeuthen21e95262016-07-27 17:58:40 -04003961 if digest_padding > 0:
Colin Cross388338a2020-02-28 14:18:01 -08003962 level_output_list.append('\0' * digest_padding)
3963
3964 level_output = ''.join(level_output_list)
David Zeuthen21e95262016-07-27 17:58:40 -04003965
3966 padding_needed = (round_to_multiple(
3967 len(level_output), block_size) - len(level_output))
3968 level_output += '\0' * padding_needed
3969
David Zeuthena4fee8b2016-08-22 15:20:43 -04003970 # Copy level-output into resulting tree.
3971 offset = hash_level_offsets[level_num]
3972 hash_ret[offset:offset + len(level_output)] = level_output
David Zeuthen21e95262016-07-27 17:58:40 -04003973
David Zeuthena4fee8b2016-08-22 15:20:43 -04003974 # Continue on to the next level.
David Zeuthen21e95262016-07-27 17:58:40 -04003975 hash_src_size = len(level_output)
David Zeuthen21e95262016-07-27 17:58:40 -04003976 level_num += 1
3977
Jan Monsch6f27bb12020-04-07 07:33:26 +02003978 hasher = hashlib.new(hash_alg_name, salt)
David Zeuthen21e95262016-07-27 17:58:40 -04003979 hasher.update(level_output)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003980 return hasher.digest(), hash_ret
David Zeuthen21e95262016-07-27 17:58:40 -04003981
3982
3983class AvbTool(object):
3984 """Object for avbtool command-line tool."""
3985
3986 def __init__(self):
3987 """Initializer method."""
3988 self.avb = Avb()
3989
3990 def _add_common_args(self, sub_parser):
3991 """Adds arguments used by several sub-commands.
3992
3993 Arguments:
3994 sub_parser: The parser to add arguments to.
3995 """
3996 sub_parser.add_argument('--algorithm',
3997 help='Algorithm to use (default: NONE)',
3998 metavar='ALGORITHM',
3999 default='NONE')
4000 sub_parser.add_argument('--key',
4001 help='Path to RSA private key file',
4002 metavar='KEY',
4003 required=False)
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08004004 sub_parser.add_argument('--signing_helper',
4005 help='Path to helper used for signing',
4006 metavar='APP',
4007 default=None,
4008 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04004009 sub_parser.add_argument('--signing_helper_with_files',
4010 help='Path to helper used for signing using files',
4011 metavar='APP',
4012 default=None,
4013 required=False)
David Zeuthen18666ab2016-11-15 11:18:05 -05004014 sub_parser.add_argument('--public_key_metadata',
4015 help='Path to public key metadata file',
4016 metavar='KEY_METADATA',
4017 required=False)
David Zeuthen21e95262016-07-27 17:58:40 -04004018 sub_parser.add_argument('--rollback_index',
4019 help='Rollback Index',
4020 type=parse_number,
4021 default=0)
David Zeuthene3cadca2017-02-22 21:25:46 -05004022 # This is used internally for unit tests. Do not include in --help output.
4023 sub_parser.add_argument('--internal_release_string',
4024 help=argparse.SUPPRESS)
4025 sub_parser.add_argument('--append_to_release_string',
4026 help='Text to append to release string',
4027 metavar='STR')
David Zeuthen21e95262016-07-27 17:58:40 -04004028 sub_parser.add_argument('--prop',
4029 help='Add property',
4030 metavar='KEY:VALUE',
4031 action='append')
4032 sub_parser.add_argument('--prop_from_file',
4033 help='Add property from file',
4034 metavar='KEY:PATH',
4035 action='append')
4036 sub_parser.add_argument('--kernel_cmdline',
4037 help='Add kernel cmdline',
4038 metavar='CMDLINE',
4039 action='append')
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004040 # TODO(zeuthen): the --setup_rootfs_from_kernel option used to be called
4041 # --generate_dm_verity_cmdline_from_hashtree. Remove support for the latter
4042 # at some future point.
4043 sub_parser.add_argument('--setup_rootfs_from_kernel',
4044 '--generate_dm_verity_cmdline_from_hashtree',
David Zeuthen21e95262016-07-27 17:58:40 -04004045 metavar='IMAGE',
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004046 help='Adds kernel cmdline to set up IMAGE',
David Zeuthen21e95262016-07-27 17:58:40 -04004047 type=argparse.FileType('rb'))
4048 sub_parser.add_argument('--include_descriptors_from_image',
4049 help='Include descriptors from image',
4050 metavar='IMAGE',
4051 action='append',
4052 type=argparse.FileType('rb'))
David Zeuthen1097a782017-05-31 15:53:17 -04004053 sub_parser.add_argument('--print_required_libavb_version',
4054 help=('Don\'t store the footer - '
4055 'instead calculate the required libavb '
4056 'version for the given options.'),
4057 action='store_true')
David Zeuthena5fd3a42017-02-27 16:38:54 -05004058 # These are only allowed from top-level vbmeta and boot-in-lieu-of-vbmeta.
4059 sub_parser.add_argument('--chain_partition',
4060 help='Allow signed integrity-data for partition',
4061 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
4062 action='append')
4063 sub_parser.add_argument('--flags',
4064 help='VBMeta flags',
4065 type=parse_number,
4066 default=0)
4067 sub_parser.add_argument('--set_hashtree_disabled_flag',
4068 help='Set the HASHTREE_DISABLED flag',
4069 action='store_true')
4070
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004071 def _add_common_footer_args(self, sub_parser):
4072 """Adds arguments used by add_*_footer sub-commands.
4073
4074 Arguments:
4075 sub_parser: The parser to add arguments to.
4076 """
4077 sub_parser.add_argument('--use_persistent_digest',
4078 help='Use a persistent digest on device instead of '
4079 'storing the digest in the descriptor. This '
4080 'cannot be used with A/B so must be combined '
4081 'with --do_not_use_ab when an A/B suffix is '
4082 'expected at runtime.',
4083 action='store_true')
4084 sub_parser.add_argument('--do_not_use_ab',
4085 help='The partition does not use A/B even when an '
4086 'A/B suffix is present. This must not be used '
4087 'for vbmeta or chained partitions.',
4088 action='store_true')
4089
David Zeuthena5fd3a42017-02-27 16:38:54 -05004090 def _fixup_common_args(self, args):
4091 """Common fixups needed by subcommands.
4092
4093 Arguments:
4094 args: Arguments to modify.
4095
4096 Returns:
4097 The modified arguments.
4098 """
4099 if args.set_hashtree_disabled_flag:
4100 args.flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED
4101 return args
David Zeuthen21e95262016-07-27 17:58:40 -04004102
4103 def run(self, argv):
4104 """Command-line processor.
4105
4106 Arguments:
4107 argv: Pass sys.argv from main.
4108 """
4109 parser = argparse.ArgumentParser()
4110 subparsers = parser.add_subparsers(title='subcommands')
4111
Jan Monsch2c7be992020-04-03 14:37:13 +02004112 sub_parser = subparsers.add_parser(
4113 'generate_test_image',
4114 help=('Generates a test image with a known pattern for testing: '
4115 '0x00 0x01 0x02 ... 0xff 0x00 0x01 ...'))
4116 sub_parser.add_argument('--image_size',
4117 help='Size of image to generate.',
4118 type=parse_number,
4119 required=True)
4120 sub_parser.add_argument('--start_byte',
4121 help='Integer for the start byte of the pattern.',
4122 type=parse_number,
4123 default=0)
4124 sub_parser.add_argument('--output',
4125 help='Output file name.',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004126 type=argparse.FileType('wb'),
Jan Monsch2c7be992020-04-03 14:37:13 +02004127 default=sys.stdout)
4128 sub_parser.set_defaults(func=self.generate_test_image)
4129
David Zeuthen21e95262016-07-27 17:58:40 -04004130 sub_parser = subparsers.add_parser('version',
4131 help='Prints version of avbtool.')
4132 sub_parser.set_defaults(func=self.version)
4133
4134 sub_parser = subparsers.add_parser('extract_public_key',
4135 help='Extract public key.')
4136 sub_parser.add_argument('--key',
4137 help='Path to RSA private key file',
4138 required=True)
4139 sub_parser.add_argument('--output',
4140 help='Output file name',
4141 type=argparse.FileType('wb'),
4142 required=True)
4143 sub_parser.set_defaults(func=self.extract_public_key)
4144
4145 sub_parser = subparsers.add_parser('make_vbmeta_image',
4146 help='Makes a vbmeta image.')
4147 sub_parser.add_argument('--output',
4148 help='Output file name',
David Zeuthen1097a782017-05-31 15:53:17 -04004149 type=argparse.FileType('wb'))
David Zeuthen97cb5802017-06-01 16:14:05 -04004150 sub_parser.add_argument('--padding_size',
4151 metavar='NUMBER',
4152 help='If non-zero, pads output with NUL bytes so '
Jan Monscheeb28b62019-12-05 16:17:09 +01004153 'its size is a multiple of NUMBER '
4154 '(default: 0)',
David Zeuthen97cb5802017-06-01 16:14:05 -04004155 type=parse_number,
4156 default=0)
David Zeuthen21e95262016-07-27 17:58:40 -04004157 self._add_common_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004158 sub_parser.set_defaults(func=self.make_vbmeta_image)
4159
4160 sub_parser = subparsers.add_parser('add_hash_footer',
4161 help='Add hashes and footer to image.')
4162 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004163 help='Image to add hashes to',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004164 type=argparse.FileType('rb+'))
David Zeuthen21e95262016-07-27 17:58:40 -04004165 sub_parser.add_argument('--partition_size',
4166 help='Partition size',
David Zeuthen1097a782017-05-31 15:53:17 -04004167 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04004168 sub_parser.add_argument('--partition_name',
4169 help='Partition name',
David Zeuthenbf562452017-05-17 18:04:43 -04004170 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04004171 sub_parser.add_argument('--hash_algorithm',
4172 help='Hash algorithm to use (default: sha256)',
4173 default='sha256')
4174 sub_parser.add_argument('--salt',
4175 help='Salt in hex (default: /dev/urandom)')
David Zeuthenbf562452017-05-17 18:04:43 -04004176 sub_parser.add_argument('--calc_max_image_size',
4177 help=('Don\'t store the footer - '
4178 'instead calculate the maximum image size '
4179 'leaving enough room for metadata with '
4180 'the given partition size.'),
4181 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05004182 sub_parser.add_argument('--output_vbmeta_image',
4183 help='Also write vbmeta struct to file',
4184 type=argparse.FileType('wb'))
4185 sub_parser.add_argument('--do_not_append_vbmeta_image',
4186 help=('Do not append vbmeta struct or footer '
4187 'to the image'),
4188 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04004189 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004190 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004191 sub_parser.set_defaults(func=self.add_hash_footer)
4192
David Zeuthenb1b994d2017-03-06 18:01:31 -05004193 sub_parser = subparsers.add_parser('append_vbmeta_image',
4194 help='Append vbmeta image to image.')
4195 sub_parser.add_argument('--image',
4196 help='Image to append vbmeta blob to',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004197 type=argparse.FileType('rb+'))
David Zeuthenb1b994d2017-03-06 18:01:31 -05004198 sub_parser.add_argument('--partition_size',
4199 help='Partition size',
4200 type=parse_number,
4201 required=True)
4202 sub_parser.add_argument('--vbmeta_image',
4203 help='Image with vbmeta blob to append',
4204 type=argparse.FileType('rb'))
4205 sub_parser.set_defaults(func=self.append_vbmeta_image)
4206
Jan Monscheeb28b62019-12-05 16:17:09 +01004207 sub_parser = subparsers.add_parser(
4208 'add_hashtree_footer',
4209 help='Add hashtree and footer to image.')
David Zeuthen21e95262016-07-27 17:58:40 -04004210 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004211 help='Image to add hashtree to',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004212 type=argparse.FileType('rb+'))
David Zeuthen21e95262016-07-27 17:58:40 -04004213 sub_parser.add_argument('--partition_size',
4214 help='Partition size',
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004215 default=0,
David Zeuthen1097a782017-05-31 15:53:17 -04004216 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04004217 sub_parser.add_argument('--partition_name',
4218 help='Partition name',
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004219 default='')
David Zeuthen21e95262016-07-27 17:58:40 -04004220 sub_parser.add_argument('--hash_algorithm',
4221 help='Hash algorithm to use (default: sha1)',
4222 default='sha1')
4223 sub_parser.add_argument('--salt',
4224 help='Salt in hex (default: /dev/urandom)')
4225 sub_parser.add_argument('--block_size',
4226 help='Block size (default: 4096)',
4227 type=parse_number,
4228 default=4096)
David Zeuthenbce9a292017-05-10 17:18:04 -04004229 # TODO(zeuthen): The --generate_fec option was removed when we
4230 # moved to generating FEC by default. To avoid breaking existing
4231 # users needing to transition we simply just print a warning below
4232 # in add_hashtree_footer(). Remove this option and the warning at
4233 # some point in the future.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004234 sub_parser.add_argument('--generate_fec',
David Zeuthenbce9a292017-05-10 17:18:04 -04004235 help=argparse.SUPPRESS,
4236 action='store_true')
Jan Monscheeb28b62019-12-05 16:17:09 +01004237 sub_parser.add_argument(
4238 '--do_not_generate_fec',
4239 help='Do not generate forward-error-correction codes',
4240 action='store_true')
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004241 sub_parser.add_argument('--fec_num_roots',
4242 help='Number of roots for FEC (default: 2)',
4243 type=parse_number,
4244 default=2)
David Zeuthen09692692016-09-30 16:16:40 -04004245 sub_parser.add_argument('--calc_max_image_size',
4246 help=('Don\'t store the hashtree or footer - '
4247 'instead calculate the maximum image size '
4248 'leaving enough room for hashtree '
4249 'and metadata with the given partition '
4250 'size.'),
4251 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05004252 sub_parser.add_argument('--output_vbmeta_image',
4253 help='Also write vbmeta struct to file',
4254 type=argparse.FileType('wb'))
4255 sub_parser.add_argument('--do_not_append_vbmeta_image',
4256 help=('Do not append vbmeta struct or footer '
4257 'to the image'),
4258 action='store_true')
David Zeuthen73f2afa2017-05-17 16:54:11 -04004259 # This is different from --setup_rootfs_from_kernel insofar that
4260 # it doesn't take an IMAGE, the generated cmdline will be for the
4261 # hashtree we're adding.
4262 sub_parser.add_argument('--setup_as_rootfs_from_kernel',
4263 action='store_true',
4264 help='Adds kernel cmdline for setting up rootfs')
Jooyung Hand7221942019-06-17 13:19:57 +09004265 sub_parser.add_argument('--no_hashtree',
4266 action='store_true',
4267 help='Do not append hashtree')
David Zeuthen21e95262016-07-27 17:58:40 -04004268 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004269 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004270 sub_parser.set_defaults(func=self.add_hashtree_footer)
4271
4272 sub_parser = subparsers.add_parser('erase_footer',
4273 help='Erase footer from an image.')
4274 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004275 help='Image with a footer',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004276 type=argparse.FileType('rb+'),
David Zeuthen21e95262016-07-27 17:58:40 -04004277 required=True)
4278 sub_parser.add_argument('--keep_hashtree',
David Zeuthenfbb61fa2017-02-02 12:11:49 -05004279 help='Keep the hashtree and FEC in the image',
David Zeuthen21e95262016-07-27 17:58:40 -04004280 action='store_true')
4281 sub_parser.set_defaults(func=self.erase_footer)
4282
David Zeuthen1394f762019-04-30 10:20:11 -04004283 sub_parser = subparsers.add_parser('zero_hashtree',
4284 help='Zero out hashtree and FEC data.')
4285 sub_parser.add_argument('--image',
4286 help='Image with a footer',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004287 type=argparse.FileType('rb+'),
David Zeuthen1394f762019-04-30 10:20:11 -04004288 required=True)
4289 sub_parser.set_defaults(func=self.zero_hashtree)
4290
Jan Monscheeb28b62019-12-05 16:17:09 +01004291 sub_parser = subparsers.add_parser(
4292 'extract_vbmeta_image',
4293 help='Extracts vbmeta from an image with a footer.')
David Zeuthen49936b42018-08-07 17:38:58 -04004294 sub_parser.add_argument('--image',
4295 help='Image with footer',
4296 type=argparse.FileType('rb'),
4297 required=True)
4298 sub_parser.add_argument('--output',
4299 help='Output file name',
4300 type=argparse.FileType('wb'))
4301 sub_parser.add_argument('--padding_size',
4302 metavar='NUMBER',
4303 help='If non-zero, pads output with NUL bytes so '
Jan Monscheeb28b62019-12-05 16:17:09 +01004304 'its size is a multiple of NUMBER '
4305 '(default: 0)',
David Zeuthen49936b42018-08-07 17:38:58 -04004306 type=parse_number,
4307 default=0)
4308 sub_parser.set_defaults(func=self.extract_vbmeta_image)
4309
David Zeuthen2bc232b2017-04-19 14:25:19 -04004310 sub_parser = subparsers.add_parser('resize_image',
4311 help='Resize image with a footer.')
4312 sub_parser.add_argument('--image',
4313 help='Image with a footer',
Jan Monsch1fa4f592020-04-07 07:56:31 +02004314 type=argparse.FileType('rb+'),
David Zeuthen2bc232b2017-04-19 14:25:19 -04004315 required=True)
4316 sub_parser.add_argument('--partition_size',
4317 help='New partition size',
4318 type=parse_number)
4319 sub_parser.set_defaults(func=self.resize_image)
4320
David Zeuthen21e95262016-07-27 17:58:40 -04004321 sub_parser = subparsers.add_parser(
4322 'info_image',
4323 help='Show information about vbmeta or footer.')
4324 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004325 help='Image to show information about',
David Zeuthen21e95262016-07-27 17:58:40 -04004326 type=argparse.FileType('rb'),
4327 required=True)
4328 sub_parser.add_argument('--output',
4329 help='Write info to file',
4330 type=argparse.FileType('wt'),
4331 default=sys.stdout)
4332 sub_parser.set_defaults(func=self.info_image)
4333
David Zeuthenb623d8b2017-04-04 16:05:53 -04004334 sub_parser = subparsers.add_parser(
4335 'verify_image',
4336 help='Verify an image.')
4337 sub_parser.add_argument('--image',
4338 help='Image to verify',
4339 type=argparse.FileType('rb'),
4340 required=True)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04004341 sub_parser.add_argument('--key',
4342 help='Check embedded public key matches KEY',
4343 metavar='KEY',
4344 required=False)
4345 sub_parser.add_argument('--expected_chain_partition',
4346 help='Expected chain partition',
4347 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
4348 action='append')
Jan Monscheeb28b62019-12-05 16:17:09 +01004349 sub_parser.add_argument(
4350 '--follow_chain_partitions',
4351 help=('Follows chain partitions even when not '
4352 'specified with the --expected_chain_partition option'),
4353 action='store_true')
4354 sub_parser.add_argument(
4355 '--accept_zeroed_hashtree',
4356 help=('Accept images where the hashtree or FEC data is zeroed out'),
4357 action='store_true')
David Zeuthenb623d8b2017-04-04 16:05:53 -04004358 sub_parser.set_defaults(func=self.verify_image)
4359
David Zeuthenb8643c02018-05-17 17:21:18 -04004360 sub_parser = subparsers.add_parser(
4361 'calculate_vbmeta_digest',
4362 help='Calculate vbmeta digest.')
4363 sub_parser.add_argument('--image',
4364 help='Image to calculate digest for',
4365 type=argparse.FileType('rb'),
4366 required=True)
4367 sub_parser.add_argument('--hash_algorithm',
4368 help='Hash algorithm to use (default: sha256)',
4369 default='sha256')
4370 sub_parser.add_argument('--output',
4371 help='Write hex digest to file (default: stdout)',
4372 type=argparse.FileType('wt'),
4373 default=sys.stdout)
4374 sub_parser.set_defaults(func=self.calculate_vbmeta_digest)
4375
David Zeuthenf7d2e752018-09-20 13:30:41 -04004376 sub_parser = subparsers.add_parser(
4377 'calculate_kernel_cmdline',
4378 help='Calculate kernel cmdline.')
4379 sub_parser.add_argument('--image',
4380 help='Image to calculate kernel cmdline for',
4381 type=argparse.FileType('rb'),
4382 required=True)
4383 sub_parser.add_argument('--hashtree_disabled',
4384 help='Return the cmdline for hashtree disabled',
4385 action='store_true')
4386 sub_parser.add_argument('--output',
4387 help='Write cmdline to file (default: stdout)',
4388 type=argparse.FileType('wt'),
4389 default=sys.stdout)
4390 sub_parser.set_defaults(func=self.calculate_kernel_cmdline)
4391
David Zeuthen8b6973b2016-09-20 12:39:49 -04004392 sub_parser = subparsers.add_parser('set_ab_metadata',
4393 help='Set A/B metadata.')
4394 sub_parser.add_argument('--misc_image',
4395 help=('The misc image to modify. If the image does '
4396 'not exist, it will be created.'),
4397 type=argparse.FileType('r+b'),
4398 required=True)
4399 sub_parser.add_argument('--slot_data',
4400 help=('Slot data of the form "priority", '
4401 '"tries_remaining", "sucessful_boot" for '
4402 'slot A followed by the same for slot B, '
4403 'separated by colons. The default value '
4404 'is 15:7:0:14:7:0.'),
4405 default='15:7:0:14:7:0')
4406 sub_parser.set_defaults(func=self.set_ab_metadata)
4407
Darren Krahn147b08d2016-12-20 16:38:29 -08004408 sub_parser = subparsers.add_parser(
4409 'make_atx_certificate',
4410 help='Create an Android Things eXtension (ATX) certificate.')
4411 sub_parser.add_argument('--output',
4412 help='Write certificate to file',
4413 type=argparse.FileType('wb'),
4414 default=sys.stdout)
4415 sub_parser.add_argument('--subject',
4416 help=('Path to subject file'),
4417 type=argparse.FileType('rb'),
4418 required=True)
4419 sub_parser.add_argument('--subject_key',
4420 help=('Path to subject RSA public key file'),
4421 type=argparse.FileType('rb'),
4422 required=True)
4423 sub_parser.add_argument('--subject_key_version',
4424 help=('Version of the subject key'),
4425 type=parse_number,
4426 required=False)
4427 sub_parser.add_argument('--subject_is_intermediate_authority',
4428 help=('Generate an intermediate authority '
4429 'certificate'),
4430 action='store_true')
Darren Krahnfccd64e2018-01-16 17:39:35 -08004431 sub_parser.add_argument('--usage',
Darren Krahn2367b462018-06-19 00:53:32 -07004432 help=('Override usage with a hash of the provided '
Darren Krahnfccd64e2018-01-16 17:39:35 -08004433 'string'),
4434 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08004435 sub_parser.add_argument('--authority_key',
4436 help='Path to authority RSA private key file',
4437 required=False)
4438 sub_parser.add_argument('--signing_helper',
4439 help='Path to helper used for signing',
4440 metavar='APP',
4441 default=None,
4442 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04004443 sub_parser.add_argument('--signing_helper_with_files',
4444 help='Path to helper used for signing using files',
4445 metavar='APP',
4446 default=None,
4447 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08004448 sub_parser.set_defaults(func=self.make_atx_certificate)
4449
4450 sub_parser = subparsers.add_parser(
4451 'make_atx_permanent_attributes',
4452 help='Create Android Things eXtension (ATX) permanent attributes.')
4453 sub_parser.add_argument('--output',
4454 help='Write attributes to file',
4455 type=argparse.FileType('wb'),
4456 default=sys.stdout)
4457 sub_parser.add_argument('--root_authority_key',
4458 help='Path to authority RSA public key file',
4459 type=argparse.FileType('rb'),
4460 required=True)
4461 sub_parser.add_argument('--product_id',
4462 help=('Path to Product ID file'),
4463 type=argparse.FileType('rb'),
4464 required=True)
4465 sub_parser.set_defaults(func=self.make_atx_permanent_attributes)
4466
4467 sub_parser = subparsers.add_parser(
4468 'make_atx_metadata',
4469 help='Create Android Things eXtension (ATX) metadata.')
4470 sub_parser.add_argument('--output',
4471 help='Write metadata to file',
4472 type=argparse.FileType('wb'),
4473 default=sys.stdout)
4474 sub_parser.add_argument('--intermediate_key_certificate',
4475 help='Path to intermediate key certificate file',
4476 type=argparse.FileType('rb'),
4477 required=True)
4478 sub_parser.add_argument('--product_key_certificate',
4479 help='Path to product key certificate file',
4480 type=argparse.FileType('rb'),
4481 required=True)
Darren Krahn147b08d2016-12-20 16:38:29 -08004482 sub_parser.set_defaults(func=self.make_atx_metadata)
4483
Darren Krahnfccd64e2018-01-16 17:39:35 -08004484 sub_parser = subparsers.add_parser(
4485 'make_atx_unlock_credential',
4486 help='Create an Android Things eXtension (ATX) unlock credential.')
4487 sub_parser.add_argument('--output',
4488 help='Write credential to file',
4489 type=argparse.FileType('wb'),
4490 default=sys.stdout)
4491 sub_parser.add_argument('--intermediate_key_certificate',
4492 help='Path to intermediate key certificate file',
4493 type=argparse.FileType('rb'),
4494 required=True)
4495 sub_parser.add_argument('--unlock_key_certificate',
4496 help='Path to unlock key certificate file',
4497 type=argparse.FileType('rb'),
4498 required=True)
4499 sub_parser.add_argument('--challenge',
4500 help='Path to the challenge to sign (optional). If '
4501 'this is not provided the challenge signature '
4502 'field is omitted and can be concatenated '
4503 'later.',
4504 required=False)
4505 sub_parser.add_argument('--unlock_key',
4506 help='Path to unlock key (optional). Must be '
4507 'provided if using --challenge.',
4508 required=False)
4509 sub_parser.add_argument('--signing_helper',
4510 help='Path to helper used for signing',
4511 metavar='APP',
4512 default=None,
4513 required=False)
4514 sub_parser.add_argument('--signing_helper_with_files',
4515 help='Path to helper used for signing using files',
4516 metavar='APP',
4517 default=None,
4518 required=False)
4519 sub_parser.set_defaults(func=self.make_atx_unlock_credential)
4520
David Zeuthen21e95262016-07-27 17:58:40 -04004521 args = parser.parse_args(argv[1:])
4522 try:
4523 args.func(args)
4524 except AvbError as e:
Jan Monsch23e0c622019-12-11 11:23:58 +01004525 sys.stderr.write('{}: {}\n'.format(argv[0], str(e)))
David Zeuthen21e95262016-07-27 17:58:40 -04004526 sys.exit(1)
4527
4528 def version(self, _):
4529 """Implements the 'version' sub-command."""
Jan Monsch23e0c622019-12-11 11:23:58 +01004530 print(get_release_string())
David Zeuthen21e95262016-07-27 17:58:40 -04004531
Jan Monsch2c7be992020-04-03 14:37:13 +02004532 def generate_test_image(self, args):
4533 """Implements the 'generate_test_image' sub-command."""
4534 self.avb.generate_test_image(args.output, args.image_size, args.start_byte)
4535
David Zeuthen21e95262016-07-27 17:58:40 -04004536 def extract_public_key(self, args):
4537 """Implements the 'extract_public_key' sub-command."""
4538 self.avb.extract_public_key(args.key, args.output)
4539
4540 def make_vbmeta_image(self, args):
4541 """Implements the 'make_vbmeta_image' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004542 args = self._fixup_common_args(args)
David Zeuthen21e95262016-07-27 17:58:40 -04004543 self.avb.make_vbmeta_image(args.output, args.chain_partition,
David Zeuthen18666ab2016-11-15 11:18:05 -05004544 args.algorithm, args.key,
4545 args.public_key_metadata, args.rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05004546 args.flags, args.prop, args.prop_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04004547 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004548 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05004549 args.include_descriptors_from_image,
David Zeuthene3cadca2017-02-22 21:25:46 -05004550 args.signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04004551 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004552 args.internal_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04004553 args.append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04004554 args.print_required_libavb_version,
4555 args.padding_size)
David Zeuthen21e95262016-07-27 17:58:40 -04004556
David Zeuthenb1b994d2017-03-06 18:01:31 -05004557 def append_vbmeta_image(self, args):
4558 """Implements the 'append_vbmeta_image' sub-command."""
4559 self.avb.append_vbmeta_image(args.image.name, args.vbmeta_image.name,
4560 args.partition_size)
4561
David Zeuthen21e95262016-07-27 17:58:40 -04004562 def add_hash_footer(self, args):
4563 """Implements the 'add_hash_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004564 args = self._fixup_common_args(args)
David Zeuthenbf562452017-05-17 18:04:43 -04004565 self.avb.add_hash_footer(args.image.name if args.image else None,
4566 args.partition_size,
David Zeuthen21e95262016-07-27 17:58:40 -04004567 args.partition_name, args.hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004568 args.salt, args.chain_partition, args.algorithm,
4569 args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05004570 args.public_key_metadata, args.rollback_index,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004571 args.flags, args.prop, args.prop_from_file,
David Zeuthen18666ab2016-11-15 11:18:05 -05004572 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004573 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05004574 args.include_descriptors_from_image,
David Zeuthena156d3d2017-06-01 12:08:09 -04004575 args.calc_max_image_size,
4576 args.signing_helper,
4577 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004578 args.internal_release_string,
4579 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05004580 args.output_vbmeta_image,
David Zeuthen1097a782017-05-31 15:53:17 -04004581 args.do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004582 args.print_required_libavb_version,
4583 args.use_persistent_digest,
4584 args.do_not_use_ab)
David Zeuthen21e95262016-07-27 17:58:40 -04004585
4586 def add_hashtree_footer(self, args):
4587 """Implements the 'add_hashtree_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004588 args = self._fixup_common_args(args)
David Zeuthenbce9a292017-05-10 17:18:04 -04004589 # TODO(zeuthen): Remove when removing support for the
4590 # '--generate_fec' option above.
4591 if args.generate_fec:
4592 sys.stderr.write('The --generate_fec option is deprecated since FEC '
4593 'is now generated by default. Use the option '
4594 '--do_not_generate_fec to not generate FEC.\n')
Jan Monscheeb28b62019-12-05 16:17:09 +01004595 self.avb.add_hashtree_footer(
4596 args.image.name if args.image else None,
4597 args.partition_size,
4598 args.partition_name,
4599 not args.do_not_generate_fec, args.fec_num_roots,
4600 args.hash_algorithm, args.block_size,
4601 args.salt, args.chain_partition, args.algorithm,
4602 args.key, args.public_key_metadata,
4603 args.rollback_index, args.flags, args.prop,
4604 args.prop_from_file,
4605 args.kernel_cmdline,
4606 args.setup_rootfs_from_kernel,
4607 args.setup_as_rootfs_from_kernel,
4608 args.include_descriptors_from_image,
4609 args.calc_max_image_size,
4610 args.signing_helper,
4611 args.signing_helper_with_files,
4612 args.internal_release_string,
4613 args.append_to_release_string,
4614 args.output_vbmeta_image,
4615 args.do_not_append_vbmeta_image,
4616 args.print_required_libavb_version,
4617 args.use_persistent_digest,
4618 args.do_not_use_ab,
4619 args.no_hashtree)
David Zeuthend247fcb2017-02-16 12:09:27 -05004620
David Zeuthen21e95262016-07-27 17:58:40 -04004621 def erase_footer(self, args):
4622 """Implements the 'erase_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04004623 self.avb.erase_footer(args.image.name, args.keep_hashtree)
David Zeuthen21e95262016-07-27 17:58:40 -04004624
David Zeuthen1394f762019-04-30 10:20:11 -04004625 def zero_hashtree(self, args):
4626 """Implements the 'zero_hashtree' sub-command."""
4627 self.avb.zero_hashtree(args.image.name)
4628
David Zeuthen49936b42018-08-07 17:38:58 -04004629 def extract_vbmeta_image(self, args):
4630 """Implements the 'extract_vbmeta_image' sub-command."""
4631 self.avb.extract_vbmeta_image(args.output, args.image.name,
4632 args.padding_size)
4633
David Zeuthen2bc232b2017-04-19 14:25:19 -04004634 def resize_image(self, args):
4635 """Implements the 'resize_image' sub-command."""
4636 self.avb.resize_image(args.image.name, args.partition_size)
4637
David Zeuthen8b6973b2016-09-20 12:39:49 -04004638 def set_ab_metadata(self, args):
4639 """Implements the 'set_ab_metadata' sub-command."""
4640 self.avb.set_ab_metadata(args.misc_image, args.slot_data)
4641
David Zeuthen21e95262016-07-27 17:58:40 -04004642 def info_image(self, args):
4643 """Implements the 'info_image' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04004644 self.avb.info_image(args.image.name, args.output)
David Zeuthen21e95262016-07-27 17:58:40 -04004645
David Zeuthenb623d8b2017-04-04 16:05:53 -04004646 def verify_image(self, args):
4647 """Implements the 'verify_image' sub-command."""
David Zeuthen5dfb4e92017-05-24 14:49:32 -04004648 self.avb.verify_image(args.image.name, args.key,
David Zeuthene947cb62019-01-25 15:27:08 -05004649 args.expected_chain_partition,
David Zeuthen1394f762019-04-30 10:20:11 -04004650 args.follow_chain_partitions,
4651 args.accept_zeroed_hashtree)
David Zeuthenb623d8b2017-04-04 16:05:53 -04004652
David Zeuthenb8643c02018-05-17 17:21:18 -04004653 def calculate_vbmeta_digest(self, args):
4654 """Implements the 'calculate_vbmeta_digest' sub-command."""
4655 self.avb.calculate_vbmeta_digest(args.image.name, args.hash_algorithm,
4656 args.output)
4657
David Zeuthenf7d2e752018-09-20 13:30:41 -04004658 def calculate_kernel_cmdline(self, args):
4659 """Implements the 'calculate_kernel_cmdline' sub-command."""
Jan Monscheeb28b62019-12-05 16:17:09 +01004660 self.avb.calculate_kernel_cmdline(args.image.name, args.hashtree_disabled,
4661 args.output)
David Zeuthenf7d2e752018-09-20 13:30:41 -04004662
Darren Krahn147b08d2016-12-20 16:38:29 -08004663 def make_atx_certificate(self, args):
4664 """Implements the 'make_atx_certificate' sub-command."""
4665 self.avb.make_atx_certificate(args.output, args.authority_key,
David Zeuthenc68f0822017-03-31 17:22:35 -04004666 args.subject_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08004667 args.subject_key_version,
4668 args.subject.read(),
4669 args.subject_is_intermediate_authority,
Darren Krahnfccd64e2018-01-16 17:39:35 -08004670 args.usage,
David Zeuthena156d3d2017-06-01 12:08:09 -04004671 args.signing_helper,
4672 args.signing_helper_with_files)
Darren Krahn147b08d2016-12-20 16:38:29 -08004673
4674 def make_atx_permanent_attributes(self, args):
4675 """Implements the 'make_atx_permanent_attributes' sub-command."""
4676 self.avb.make_atx_permanent_attributes(args.output,
David Zeuthenc68f0822017-03-31 17:22:35 -04004677 args.root_authority_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08004678 args.product_id.read())
4679
4680 def make_atx_metadata(self, args):
4681 """Implements the 'make_atx_metadata' sub-command."""
4682 self.avb.make_atx_metadata(args.output,
4683 args.intermediate_key_certificate.read(),
Darren Krahn43e12d82017-02-24 16:26:31 -08004684 args.product_key_certificate.read())
Darren Krahn147b08d2016-12-20 16:38:29 -08004685
Darren Krahnfccd64e2018-01-16 17:39:35 -08004686 def make_atx_unlock_credential(self, args):
4687 """Implements the 'make_atx_unlock_credential' sub-command."""
4688 self.avb.make_atx_unlock_credential(
4689 args.output,
4690 args.intermediate_key_certificate.read(),
4691 args.unlock_key_certificate.read(),
4692 args.challenge,
4693 args.unlock_key,
4694 args.signing_helper,
4695 args.signing_helper_with_files)
4696
David Zeuthen21e95262016-07-27 17:58:40 -04004697
4698if __name__ == '__main__':
Jan Monsch2c7be992020-04-03 14:37:13 +02004699 if AVB_INVOCATION_LOGFILE:
4700 f = open(AVB_INVOCATION_LOGFILE, 'a')
4701 f.write(' '.join(sys.argv))
4702 f.write('\n')
4703 f.close()
4704
David Zeuthen21e95262016-07-27 17:58:40 -04004705 tool = AvbTool()
4706 tool.run(sys.argv)