blob: c263b9044927b51c0608c04774a083918101da35 [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
David Zeuthene3cadca2017-02-22 21:25:46 -050052
David Zeuthen21e95262016-07-27 17:58:40 -040053class AvbError(Exception):
54 """Application-specific errors.
55
56 These errors represent issues for which a stack-trace should not be
57 presented.
58
59 Attributes:
60 message: Error message.
61 """
62
63 def __init__(self, message):
64 Exception.__init__(self, message)
65
66
67class Algorithm(object):
68 """Contains details about an algorithm.
69
Tao Bao80418a52018-07-20 11:41:22 -070070 See the avb_vbmeta_image.h file for more details about algorithms.
David Zeuthen21e95262016-07-27 17:58:40 -040071
72 The constant |ALGORITHMS| is a dictionary from human-readable
73 names (e.g 'SHA256_RSA2048') to instances of this class.
74
75 Attributes:
76 algorithm_type: Integer code corresponding to |AvbAlgorithmType|.
David Zeuthenb623d8b2017-04-04 16:05:53 -040077 hash_name: Empty or a name from |hashlib.algorithms|.
David Zeuthen21e95262016-07-27 17:58:40 -040078 hash_num_bytes: Number of bytes used to store the hash.
79 signature_num_bytes: Number of bytes used to store the signature.
80 public_key_num_bytes: Number of bytes used to store the public key.
81 padding: Padding used for signature, if any.
82 """
83
David Zeuthenb623d8b2017-04-04 16:05:53 -040084 def __init__(self, algorithm_type, hash_name, hash_num_bytes,
85 signature_num_bytes, public_key_num_bytes, padding):
David Zeuthen21e95262016-07-27 17:58:40 -040086 self.algorithm_type = algorithm_type
David Zeuthenb623d8b2017-04-04 16:05:53 -040087 self.hash_name = hash_name
David Zeuthen21e95262016-07-27 17:58:40 -040088 self.hash_num_bytes = hash_num_bytes
89 self.signature_num_bytes = signature_num_bytes
90 self.public_key_num_bytes = public_key_num_bytes
91 self.padding = padding
92
David Zeuthenb623d8b2017-04-04 16:05:53 -040093
David Zeuthen21e95262016-07-27 17:58:40 -040094# This must be kept in sync with the avb_crypto.h file.
95#
96# The PKC1-v1.5 padding is a blob of binary DER of ASN.1 and is
97# obtained from section 5.2.2 of RFC 4880.
98ALGORITHMS = {
99 'NONE': Algorithm(
100 algorithm_type=0, # AVB_ALGORITHM_TYPE_NONE
David Zeuthenb623d8b2017-04-04 16:05:53 -0400101 hash_name='',
David Zeuthen21e95262016-07-27 17:58:40 -0400102 hash_num_bytes=0,
103 signature_num_bytes=0,
104 public_key_num_bytes=0,
105 padding=[]),
106 'SHA256_RSA2048': Algorithm(
107 algorithm_type=1, # AVB_ALGORITHM_TYPE_SHA256_RSA2048
David Zeuthenb623d8b2017-04-04 16:05:53 -0400108 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400109 hash_num_bytes=32,
110 signature_num_bytes=256,
Jan Monsch23e0c622019-12-11 11:23:58 +0100111 public_key_num_bytes=8 + 2*2048//8,
David Zeuthen21e95262016-07-27 17:58:40 -0400112 padding=[
113 # PKCS1-v1_5 padding
114 0x00, 0x01] + [0xff]*202 + [0x00] + [
115 # ASN.1 header
116 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
117 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
118 0x00, 0x04, 0x20,
119 ]),
120 'SHA256_RSA4096': Algorithm(
121 algorithm_type=2, # AVB_ALGORITHM_TYPE_SHA256_RSA4096
David Zeuthenb623d8b2017-04-04 16:05:53 -0400122 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400123 hash_num_bytes=32,
124 signature_num_bytes=512,
Jan Monsch23e0c622019-12-11 11:23:58 +0100125 public_key_num_bytes=8 + 2*4096//8,
David Zeuthen21e95262016-07-27 17:58:40 -0400126 padding=[
127 # PKCS1-v1_5 padding
128 0x00, 0x01] + [0xff]*458 + [0x00] + [
129 # ASN.1 header
130 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
131 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
132 0x00, 0x04, 0x20,
133 ]),
134 'SHA256_RSA8192': Algorithm(
135 algorithm_type=3, # AVB_ALGORITHM_TYPE_SHA256_RSA8192
David Zeuthenb623d8b2017-04-04 16:05:53 -0400136 hash_name='sha256',
David Zeuthen21e95262016-07-27 17:58:40 -0400137 hash_num_bytes=32,
138 signature_num_bytes=1024,
Jan Monsch23e0c622019-12-11 11:23:58 +0100139 public_key_num_bytes=8 + 2*8192//8,
David Zeuthen21e95262016-07-27 17:58:40 -0400140 padding=[
141 # PKCS1-v1_5 padding
142 0x00, 0x01] + [0xff]*970 + [0x00] + [
143 # ASN.1 header
144 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
145 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
146 0x00, 0x04, 0x20,
147 ]),
148 'SHA512_RSA2048': Algorithm(
149 algorithm_type=4, # AVB_ALGORITHM_TYPE_SHA512_RSA2048
David Zeuthenb623d8b2017-04-04 16:05:53 -0400150 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400151 hash_num_bytes=64,
152 signature_num_bytes=256,
Jan Monsch23e0c622019-12-11 11:23:58 +0100153 public_key_num_bytes=8 + 2*2048//8,
David Zeuthen21e95262016-07-27 17:58:40 -0400154 padding=[
155 # PKCS1-v1_5 padding
156 0x00, 0x01] + [0xff]*170 + [0x00] + [
157 # ASN.1 header
158 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
159 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
160 0x00, 0x04, 0x40
161 ]),
162 'SHA512_RSA4096': Algorithm(
163 algorithm_type=5, # AVB_ALGORITHM_TYPE_SHA512_RSA4096
David Zeuthenb623d8b2017-04-04 16:05:53 -0400164 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400165 hash_num_bytes=64,
166 signature_num_bytes=512,
Jan Monsch23e0c622019-12-11 11:23:58 +0100167 public_key_num_bytes=8 + 2*4096//8,
David Zeuthen21e95262016-07-27 17:58:40 -0400168 padding=[
169 # PKCS1-v1_5 padding
170 0x00, 0x01] + [0xff]*426 + [0x00] + [
171 # ASN.1 header
172 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
173 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
174 0x00, 0x04, 0x40
175 ]),
176 'SHA512_RSA8192': Algorithm(
177 algorithm_type=6, # AVB_ALGORITHM_TYPE_SHA512_RSA8192
David Zeuthenb623d8b2017-04-04 16:05:53 -0400178 hash_name='sha512',
David Zeuthen21e95262016-07-27 17:58:40 -0400179 hash_num_bytes=64,
180 signature_num_bytes=1024,
Jan Monsch23e0c622019-12-11 11:23:58 +0100181 public_key_num_bytes=8 + 2*8192//8,
David Zeuthen21e95262016-07-27 17:58:40 -0400182 padding=[
183 # PKCS1-v1_5 padding
184 0x00, 0x01] + [0xff]*938 + [0x00] + [
185 # ASN.1 header
186 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
187 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
188 0x00, 0x04, 0x40
189 ]),
190}
191
192
David Zeuthene3cadca2017-02-22 21:25:46 -0500193def get_release_string():
194 """Calculates the release string to use in the VBMeta struct."""
195 # Keep in sync with libavb/avb_version.c:avb_version_string().
196 return 'avbtool {}.{}.{}'.format(AVB_VERSION_MAJOR,
197 AVB_VERSION_MINOR,
198 AVB_VERSION_SUB)
199
200
David Zeuthen21e95262016-07-27 17:58:40 -0400201def round_to_multiple(number, size):
202 """Rounds a number up to nearest multiple of another number.
203
Jan Monschfe00c0a2019-12-11 11:19:40 +0100204 Arguments:
David Zeuthen21e95262016-07-27 17:58:40 -0400205 number: The number to round up.
206 size: The multiple to round up to.
207
208 Returns:
209 If |number| is a multiple of |size|, returns |number|, otherwise
210 returns |number| + |size|.
211 """
212 remainder = number % size
213 if remainder == 0:
214 return number
215 return number + size - remainder
216
217
218def round_to_pow2(number):
219 """Rounds a number up to the next power of 2.
220
Jan Monschfe00c0a2019-12-11 11:19:40 +0100221 Arguments:
David Zeuthen21e95262016-07-27 17:58:40 -0400222 number: The number to round up.
223
224 Returns:
225 If |number| is already a power of 2 then |number| is
226 returned. Otherwise the smallest power of 2 greater than |number|
227 is returned.
228 """
229 return 2**((number - 1).bit_length())
230
231
David Zeuthen21e95262016-07-27 17:58:40 -0400232def encode_long(num_bits, value):
233 """Encodes a long to a bytearray() using a given amount of bits.
234
235 This number is written big-endian, e.g. with the most significant
236 bit first.
237
David Zeuthenb623d8b2017-04-04 16:05:53 -0400238 This is the reverse of decode_long().
239
David Zeuthen21e95262016-07-27 17:58:40 -0400240 Arguments:
241 num_bits: The number of bits to write, e.g. 2048.
242 value: The value to write.
243
244 Returns:
245 A bytearray() with the encoded long.
246 """
247 ret = bytearray()
248 for bit_pos in range(num_bits, 0, -8):
249 octet = (value >> (bit_pos - 8)) & 0xff
250 ret.extend(struct.pack('!B', octet))
251 return ret
252
253
David Zeuthenb623d8b2017-04-04 16:05:53 -0400254def decode_long(blob):
255 """Decodes a long from a bytearray() using a given amount of bits.
256
257 This number is expected to be in big-endian, e.g. with the most
258 significant bit first.
259
260 This is the reverse of encode_long().
261
262 Arguments:
Jan Monscheeb28b62019-12-05 16:17:09 +0100263 blob: A bytearray() with the encoded long.
David Zeuthenb623d8b2017-04-04 16:05:53 -0400264
265 Returns:
266 The decoded value.
267 """
268 ret = 0
269 for b in bytearray(blob):
270 ret *= 256
271 ret += b
272 return ret
273
274
David Zeuthen21e95262016-07-27 17:58:40 -0400275def egcd(a, b):
276 """Calculate greatest common divisor of two numbers.
277
278 This implementation uses a recursive version of the extended
279 Euclidian algorithm.
280
281 Arguments:
282 a: First number.
283 b: Second number.
284
285 Returns:
286 A tuple (gcd, x, y) that where |gcd| is the greatest common
287 divisor of |a| and |b| and |a|*|x| + |b|*|y| = |gcd|.
288 """
289 if a == 0:
290 return (b, 0, 1)
Jan Monsch23e0c622019-12-11 11:23:58 +0100291 g, y, x = egcd(b % a, a)
292 return (g, x - (b // a) * y, y)
David Zeuthen21e95262016-07-27 17:58:40 -0400293
294
295def modinv(a, m):
296 """Calculate modular multiplicative inverse of |a| modulo |m|.
297
298 This calculates the number |x| such that |a| * |x| == 1 (modulo
299 |m|). This number only exists if |a| and |m| are co-prime - |None|
300 is returned if this isn't true.
301
302 Arguments:
303 a: The number to calculate a modular inverse of.
304 m: The modulo to use.
305
306 Returns:
307 The modular multiplicative inverse of |a| and |m| or |None| if
308 these numbers are not co-prime.
309 """
310 gcd, x, _ = egcd(a, m)
311 if gcd != 1:
312 return None # modular inverse does not exist
Jan Monsch23e0c622019-12-11 11:23:58 +0100313 return x % m
David Zeuthen21e95262016-07-27 17:58:40 -0400314
315
316def parse_number(string):
317 """Parse a string as a number.
318
319 This is just a short-hand for int(string, 0) suitable for use in the
320 |type| parameter of |ArgumentParser|'s add_argument() function. An
321 improvement to just using type=int is that this function supports
322 numbers in other bases, e.g. "0x1234".
323
324 Arguments:
325 string: The string to parse.
326
327 Returns:
328 The parsed integer.
329
330 Raises:
331 ValueError: If the number could not be parsed.
332 """
333 return int(string, 0)
334
335
David Zeuthenc68f0822017-03-31 17:22:35 -0400336class RSAPublicKey(object):
337 """Data structure used for a RSA public key.
David Zeuthen21e95262016-07-27 17:58:40 -0400338
David Zeuthenc68f0822017-03-31 17:22:35 -0400339 Attributes:
340 exponent: The key exponent.
341 modulus: The key modulus.
342 num_bits: The key size.
David Zeuthen21e95262016-07-27 17:58:40 -0400343 """
David Zeuthenc68f0822017-03-31 17:22:35 -0400344
345 MODULUS_PREFIX = 'modulus='
346
347 def __init__(self, key_path):
348 """Loads and parses an RSA key from either a private or public key file.
349
350 Arguments:
351 key_path: The path to a key file.
Jan Monsch77cd2022019-12-10 17:18:04 +0100352
353 Raises:
354 AvbError: If RSA key parameters could not be read from file.
David Zeuthenc68f0822017-03-31 17:22:35 -0400355 """
356 # We used to have something as simple as this:
357 #
358 # key = Crypto.PublicKey.RSA.importKey(open(key_path).read())
359 # self.exponent = key.e
360 # self.modulus = key.n
361 # self.num_bits = key.size() + 1
362 #
363 # but unfortunately PyCrypto is not available in the builder. So
364 # instead just parse openssl(1) output to get this
365 # information. It's ugly but...
366 args = ['openssl', 'rsa', '-in', key_path, '-modulus', '-noout']
367 p = subprocess.Popen(args,
368 stdin=subprocess.PIPE,
369 stdout=subprocess.PIPE,
370 stderr=subprocess.PIPE)
371 (pout, perr) = p.communicate()
372 if p.wait() != 0:
373 # Could be just a public key is passed, try that.
374 args.append('-pubin')
375 p = subprocess.Popen(args,
376 stdin=subprocess.PIPE,
377 stdout=subprocess.PIPE,
378 stderr=subprocess.PIPE)
379 (pout, perr) = p.communicate()
380 if p.wait() != 0:
381 raise AvbError('Error getting public key: {}'.format(perr))
382
383 if not pout.lower().startswith(self.MODULUS_PREFIX):
384 raise AvbError('Unexpected modulus output')
385
386 modulus_hexstr = pout[len(self.MODULUS_PREFIX):]
387
388 # The exponent is assumed to always be 65537 and the number of
389 # bits can be derived from the modulus by rounding up to the
390 # nearest power of 2.
391 self.modulus = int(modulus_hexstr, 16)
392 self.num_bits = round_to_pow2(int(math.ceil(math.log(self.modulus, 2))))
393 self.exponent = 65537
David Zeuthen21e95262016-07-27 17:58:40 -0400394
395
David Zeuthenc68f0822017-03-31 17:22:35 -0400396def encode_rsa_key(key_path):
David Zeuthen21e95262016-07-27 17:58:40 -0400397 """Encodes a public RSA key in |AvbRSAPublicKeyHeader| format.
398
399 This creates a |AvbRSAPublicKeyHeader| as well as the two large
400 numbers (|key_num_bits| bits long) following it.
401
402 Arguments:
David Zeuthenc68f0822017-03-31 17:22:35 -0400403 key_path: The path to a key file.
David Zeuthen21e95262016-07-27 17:58:40 -0400404
405 Returns:
406 A bytearray() with the |AvbRSAPublicKeyHeader|.
Jan Monsch77cd2022019-12-10 17:18:04 +0100407
408 Raises:
409 AvbError: If given RSA key exponent is not 65537.
David Zeuthen21e95262016-07-27 17:58:40 -0400410 """
David Zeuthenc68f0822017-03-31 17:22:35 -0400411 key = RSAPublicKey(key_path)
412 if key.exponent != 65537:
413 raise AvbError('Only RSA keys with exponent 65537 are supported.')
David Zeuthen21e95262016-07-27 17:58:40 -0400414 ret = bytearray()
David Zeuthen21e95262016-07-27 17:58:40 -0400415 # Calculate n0inv = -1/n[0] (mod 2^32)
Jan Monsch23e0c622019-12-11 11:23:58 +0100416 b = 2L**32 # pylint: disable=long-suffix
David Zeuthenc68f0822017-03-31 17:22:35 -0400417 n0inv = b - modinv(key.modulus, b)
David Zeuthen21e95262016-07-27 17:58:40 -0400418 # Calculate rr = r^2 (mod N), where r = 2^(# of key bits)
Jan Monsch23e0c622019-12-11 11:23:58 +0100419 r = 2L**key.modulus.bit_length() # pylint: disable=long-suffix
David Zeuthenc68f0822017-03-31 17:22:35 -0400420 rrmodn = r * r % key.modulus
421 ret.extend(struct.pack('!II', key.num_bits, n0inv))
422 ret.extend(encode_long(key.num_bits, key.modulus))
423 ret.extend(encode_long(key.num_bits, rrmodn))
David Zeuthen21e95262016-07-27 17:58:40 -0400424 return ret
425
426
427def lookup_algorithm_by_type(alg_type):
428 """Looks up algorithm by type.
429
430 Arguments:
431 alg_type: The integer representing the type.
432
433 Returns:
434 A tuple with the algorithm name and an |Algorithm| instance.
435
436 Raises:
437 Exception: If the algorithm cannot be found
438 """
439 for alg_name in ALGORITHMS:
440 alg_data = ALGORITHMS[alg_name]
441 if alg_data.algorithm_type == alg_type:
442 return (alg_name, alg_data)
443 raise AvbError('Unknown algorithm type {}'.format(alg_type))
444
Jan Monsch77cd2022019-12-10 17:18:04 +0100445
Dan Austina7bc4962019-12-02 13:26:08 -0800446def lookup_hash_size_by_type(alg_type):
447 """Looks up hash size by type.
448
449 Arguments:
450 alg_type: The integer representing the type.
451
452 Returns:
453 The corresponding hash size.
454
455 Raises:
456 AvbError: If the algorithm cannot be found.
457 """
458 for alg_name in ALGORITHMS:
459 alg_data = ALGORITHMS[alg_name]
460 if alg_data.algorithm_type == alg_type:
461 return alg_data.hash_num_bytes
462 raise AvbError('Unsupported algorithm type {}'.format(alg_type))
David Zeuthen21e95262016-07-27 17:58:40 -0400463
Jan Monsch77cd2022019-12-10 17:18:04 +0100464
David Zeuthena156d3d2017-06-01 12:08:09 -0400465def raw_sign(signing_helper, signing_helper_with_files,
466 algorithm_name, signature_num_bytes, key_path,
Esun Kimff44f232017-03-30 10:34:54 +0900467 raw_data_to_sign):
Darren Krahn147b08d2016-12-20 16:38:29 -0800468 """Computes a raw RSA signature using |signing_helper| or openssl.
469
470 Arguments:
471 signing_helper: Program which signs a hash and returns the signature.
David Zeuthena156d3d2017-06-01 12:08:09 -0400472 signing_helper_with_files: Same as signing_helper but uses files instead.
Darren Krahn147b08d2016-12-20 16:38:29 -0800473 algorithm_name: The algorithm name as per the ALGORITHMS dict.
Esun Kimff44f232017-03-30 10:34:54 +0900474 signature_num_bytes: Number of bytes used to store the signature.
Darren Krahn147b08d2016-12-20 16:38:29 -0800475 key_path: Path to the private key file. Must be PEM format.
476 raw_data_to_sign: Data to sign (bytearray or str expected).
477
478 Returns:
479 A bytearray containing the signature.
480
481 Raises:
482 Exception: If an error occurs.
483 """
484 p = None
David Zeuthena156d3d2017-06-01 12:08:09 -0400485 if signing_helper_with_files is not None:
486 signing_file = tempfile.NamedTemporaryFile()
487 signing_file.write(str(raw_data_to_sign))
488 signing_file.flush()
Jan Monscheeb28b62019-12-05 16:17:09 +0100489 p = subprocess.Popen([
490 signing_helper_with_files, algorithm_name, key_path, signing_file.name])
David Zeuthena156d3d2017-06-01 12:08:09 -0400491 retcode = p.wait()
492 if retcode != 0:
493 raise AvbError('Error signing')
494 signing_file.seek(0)
495 signature = bytearray(signing_file.read())
Darren Krahn147b08d2016-12-20 16:38:29 -0800496 else:
David Zeuthena156d3d2017-06-01 12:08:09 -0400497 if signing_helper is not None:
498 p = subprocess.Popen(
499 [signing_helper, algorithm_name, key_path],
500 stdin=subprocess.PIPE,
501 stdout=subprocess.PIPE,
502 stderr=subprocess.PIPE)
503 else:
504 p = subprocess.Popen(
505 ['openssl', 'rsautl', '-sign', '-inkey', key_path, '-raw'],
506 stdin=subprocess.PIPE,
507 stdout=subprocess.PIPE,
508 stderr=subprocess.PIPE)
509 (pout, perr) = p.communicate(str(raw_data_to_sign))
510 retcode = p.wait()
511 if retcode != 0:
512 raise AvbError('Error signing: {}'.format(perr))
513 signature = bytearray(pout)
Esun Kimff44f232017-03-30 10:34:54 +0900514 if len(signature) != signature_num_bytes:
515 raise AvbError('Error signing: Invalid length of signature')
516 return signature
Darren Krahn147b08d2016-12-20 16:38:29 -0800517
518
David Zeuthenb623d8b2017-04-04 16:05:53 -0400519def verify_vbmeta_signature(vbmeta_header, vbmeta_blob):
Jan Monsch77cd2022019-12-10 17:18:04 +0100520 """Checks that signature in a vbmeta blob was made by the embedded public key.
David Zeuthenb623d8b2017-04-04 16:05:53 -0400521
522 Arguments:
523 vbmeta_header: A AvbVBMetaHeader.
524 vbmeta_blob: The whole vbmeta blob, including the header.
525
526 Returns:
527 True if the signature is valid and corresponds to the embedded
528 public key. Also returns True if the vbmeta blob is not signed.
Jan Monsch77cd2022019-12-10 17:18:04 +0100529
530 Raises:
531 AvbError: If there errors calling out to openssl command during
532 signature verification.
David Zeuthenb623d8b2017-04-04 16:05:53 -0400533 """
534 (_, alg) = lookup_algorithm_by_type(vbmeta_header.algorithm_type)
Jan Monschfe00c0a2019-12-11 11:19:40 +0100535 if not alg.hash_name:
David Zeuthenb623d8b2017-04-04 16:05:53 -0400536 return True
537 header_blob = vbmeta_blob[0:256]
538 auth_offset = 256
539 aux_offset = auth_offset + vbmeta_header.authentication_data_block_size
540 aux_size = vbmeta_header.auxiliary_data_block_size
541 aux_blob = vbmeta_blob[aux_offset:aux_offset + aux_size]
542 pubkey_offset = aux_offset + vbmeta_header.public_key_offset
543 pubkey_size = vbmeta_header.public_key_size
544 pubkey_blob = vbmeta_blob[pubkey_offset:pubkey_offset + pubkey_size]
545
546 digest_offset = auth_offset + vbmeta_header.hash_offset
547 digest_size = vbmeta_header.hash_size
548 digest_blob = vbmeta_blob[digest_offset:digest_offset + digest_size]
549
550 sig_offset = auth_offset + vbmeta_header.signature_offset
551 sig_size = vbmeta_header.signature_size
552 sig_blob = vbmeta_blob[sig_offset:sig_offset + sig_size]
553
554 # Now that we've got the stored digest, public key, and signature
555 # all we need to do is to verify. This is the exactly the same
556 # steps as performed in the avb_vbmeta_image_verify() function in
557 # libavb/avb_vbmeta_image.c.
558
559 ha = hashlib.new(alg.hash_name)
560 ha.update(header_blob)
561 ha.update(aux_blob)
562 computed_digest = ha.digest()
563
564 if computed_digest != digest_blob:
565 return False
566
567 padding_and_digest = bytearray(alg.padding)
568 padding_and_digest.extend(computed_digest)
569
570 (num_bits,) = struct.unpack('!I', pubkey_blob[0:4])
Jan Monsch23e0c622019-12-11 11:23:58 +0100571 modulus_blob = pubkey_blob[8:8 + num_bits//8]
David Zeuthenb623d8b2017-04-04 16:05:53 -0400572 modulus = decode_long(modulus_blob)
573 exponent = 65537
574
David Zeuthenddd7d6f2018-11-26 17:28:38 -0500575 # We used to have this:
576 #
577 # import Crypto.PublicKey.RSA
578 # key = Crypto.PublicKey.RSA.construct((modulus, long(exponent)))
579 # if not key.verify(decode_long(padding_and_digest),
580 # (decode_long(sig_blob), None)):
581 # return False
582 # return True
583 #
584 # but since 'avbtool verify_image' is used on the builders we don't want
585 # to rely on Crypto.PublicKey.RSA. Instead just use openssl(1) to verify.
586 asn1_str = ('asn1=SEQUENCE:pubkeyinfo\n'
587 '\n'
588 '[pubkeyinfo]\n'
589 'algorithm=SEQUENCE:rsa_alg\n'
590 'pubkey=BITWRAP,SEQUENCE:rsapubkey\n'
591 '\n'
592 '[rsa_alg]\n'
593 'algorithm=OID:rsaEncryption\n'
594 'parameter=NULL\n'
595 '\n'
596 '[rsapubkey]\n'
597 'n=INTEGER:%s\n'
Jan Monscheeb28b62019-12-05 16:17:09 +0100598 'e=INTEGER:%s\n' % (hex(modulus).rstrip('L'),
599 hex(exponent).rstrip('L')))
David Zeuthenddd7d6f2018-11-26 17:28:38 -0500600 asn1_tmpfile = tempfile.NamedTemporaryFile()
601 asn1_tmpfile.write(asn1_str)
602 asn1_tmpfile.flush()
603 der_tmpfile = tempfile.NamedTemporaryFile()
604 p = subprocess.Popen(
Jan Monscheeb28b62019-12-05 16:17:09 +0100605 ['openssl', 'asn1parse', '-genconf', asn1_tmpfile.name, '-out',
606 der_tmpfile.name, '-noout'])
David Zeuthenddd7d6f2018-11-26 17:28:38 -0500607 retcode = p.wait()
608 if retcode != 0:
609 raise AvbError('Error generating DER file')
610
611 p = subprocess.Popen(
Jan Monscheeb28b62019-12-05 16:17:09 +0100612 ['openssl', 'rsautl', '-verify', '-pubin', '-inkey', der_tmpfile.name,
613 '-keyform', 'DER', '-raw'],
David Zeuthenddd7d6f2018-11-26 17:28:38 -0500614 stdin=subprocess.PIPE,
615 stdout=subprocess.PIPE,
616 stderr=subprocess.PIPE)
617 (pout, perr) = p.communicate(str(sig_blob))
618 retcode = p.wait()
619 if retcode != 0:
620 raise AvbError('Error verifying data: {}'.format(perr))
621 recovered_data = bytearray(pout)
622 if recovered_data != padding_and_digest:
623 sys.stderr.write('Signature not correct\n')
David Zeuthenb623d8b2017-04-04 16:05:53 -0400624 return False
625 return True
626
627
David Zeuthena4fee8b2016-08-22 15:20:43 -0400628class ImageChunk(object):
629 """Data structure used for representing chunks in Android sparse files.
630
631 Attributes:
632 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
633 chunk_offset: Offset in the sparse file where this chunk begins.
634 output_offset: Offset in de-sparsified file where output begins.
635 output_size: Number of bytes in output.
636 input_offset: Offset in sparse file for data if TYPE_RAW otherwise None.
637 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
638 """
639
640 FORMAT = '<2H2I'
641 TYPE_RAW = 0xcac1
642 TYPE_FILL = 0xcac2
643 TYPE_DONT_CARE = 0xcac3
644 TYPE_CRC32 = 0xcac4
645
646 def __init__(self, chunk_type, chunk_offset, output_offset, output_size,
647 input_offset, fill_data):
648 """Initializes an ImageChunk object.
649
650 Arguments:
651 chunk_type: One of TYPE_RAW, TYPE_FILL, or TYPE_DONT_CARE.
652 chunk_offset: Offset in the sparse file where this chunk begins.
653 output_offset: Offset in de-sparsified file.
654 output_size: Number of bytes in output.
655 input_offset: Offset in sparse file if TYPE_RAW otherwise None.
656 fill_data: Blob with data to fill if TYPE_FILL otherwise None.
657
658 Raises:
659 ValueError: If data is not well-formed.
660 """
661 self.chunk_type = chunk_type
662 self.chunk_offset = chunk_offset
663 self.output_offset = output_offset
664 self.output_size = output_size
665 self.input_offset = input_offset
666 self.fill_data = fill_data
667 # Check invariants.
668 if self.chunk_type == self.TYPE_RAW:
669 if self.fill_data is not None:
670 raise ValueError('RAW chunk cannot have fill_data set.')
671 if not self.input_offset:
672 raise ValueError('RAW chunk must have input_offset set.')
673 elif self.chunk_type == self.TYPE_FILL:
674 if self.fill_data is None:
675 raise ValueError('FILL chunk must have fill_data set.')
676 if self.input_offset:
677 raise ValueError('FILL chunk cannot have input_offset set.')
678 elif self.chunk_type == self.TYPE_DONT_CARE:
679 if self.fill_data is not None:
680 raise ValueError('DONT_CARE chunk cannot have fill_data set.')
681 if self.input_offset:
682 raise ValueError('DONT_CARE chunk cannot have input_offset set.')
683 else:
684 raise ValueError('Invalid chunk type')
685
686
687class ImageHandler(object):
688 """Abstraction for image I/O with support for Android sparse images.
689
690 This class provides an interface for working with image files that
691 may be using the Android Sparse Image format. When an instance is
692 constructed, we test whether it's an Android sparse file. If so,
693 operations will be on the sparse file by interpreting the sparse
694 format, otherwise they will be directly on the file. Either way the
695 operations do the same.
696
697 For reading, this interface mimics a file object - it has seek(),
698 tell(), and read() methods. For writing, only truncation
699 (truncate()) and appending is supported (append_raw() and
700 append_dont_care()). Additionally, data can only be written in units
701 of the block size.
702
703 Attributes:
David Zeuthen49936b42018-08-07 17:38:58 -0400704 filename: Name of file.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400705 is_sparse: Whether the file being operated on is sparse.
706 block_size: The block size, typically 4096.
707 image_size: The size of the unsparsified file.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400708 """
709 # See system/core/libsparse/sparse_format.h for details.
710 MAGIC = 0xed26ff3a
711 HEADER_FORMAT = '<I4H4I'
712
713 # These are formats and offset of just the |total_chunks| and
714 # |total_blocks| fields.
715 NUM_CHUNKS_AND_BLOCKS_FORMAT = '<II'
716 NUM_CHUNKS_AND_BLOCKS_OFFSET = 16
717
718 def __init__(self, image_filename):
719 """Initializes an image handler.
720
721 Arguments:
722 image_filename: The name of the file to operate on.
723
724 Raises:
725 ValueError: If data in the file is invalid.
726 """
David Zeuthen49936b42018-08-07 17:38:58 -0400727 self.filename = image_filename
Jan Monsch23e0c622019-12-11 11:23:58 +0100728 self._num_total_blocks = 0
729 self._num_total_chunks = 0
730 self._file_pos = 0
David Zeuthena4fee8b2016-08-22 15:20:43 -0400731 self._read_header()
732
733 def _read_header(self):
734 """Initializes internal data structures used for reading file.
735
736 This may be called multiple times and is typically called after
737 modifying the file (e.g. appending, truncation).
738
739 Raises:
740 ValueError: If data in the file is invalid.
741 """
742 self.is_sparse = False
743 self.block_size = 4096
744 self._file_pos = 0
David Zeuthen49936b42018-08-07 17:38:58 -0400745 self._image = open(self.filename, 'r+b')
David Zeuthena4fee8b2016-08-22 15:20:43 -0400746 self._image.seek(0, os.SEEK_END)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400747 self.image_size = self._image.tell()
748
749 self._image.seek(0, os.SEEK_SET)
750 header_bin = self._image.read(struct.calcsize(self.HEADER_FORMAT))
751 (magic, major_version, minor_version, file_hdr_sz, chunk_hdr_sz,
752 block_size, self._num_total_blocks, self._num_total_chunks,
753 _) = struct.unpack(self.HEADER_FORMAT, header_bin)
754 if magic != self.MAGIC:
755 # Not a sparse image, our job here is done.
756 return
757 if not (major_version == 1 and minor_version == 0):
758 raise ValueError('Encountered sparse image format version {}.{} but '
759 'only 1.0 is supported'.format(major_version,
760 minor_version))
761 if file_hdr_sz != struct.calcsize(self.HEADER_FORMAT):
762 raise ValueError('Unexpected file_hdr_sz value {}.'.
763 format(file_hdr_sz))
764 if chunk_hdr_sz != struct.calcsize(ImageChunk.FORMAT):
765 raise ValueError('Unexpected chunk_hdr_sz value {}.'.
766 format(chunk_hdr_sz))
767
768 self.block_size = block_size
769
770 # Build an list of chunks by parsing the file.
771 self._chunks = []
772
773 # Find the smallest offset where only "Don't care" chunks
774 # follow. This will be the size of the content in the sparse
775 # image.
776 offset = 0
777 output_offset = 0
Jan Monsch23e0c622019-12-11 11:23:58 +0100778 for _ in range(1, self._num_total_chunks + 1):
David Zeuthena4fee8b2016-08-22 15:20:43 -0400779 chunk_offset = self._image.tell()
780
781 header_bin = self._image.read(struct.calcsize(ImageChunk.FORMAT))
782 (chunk_type, _, chunk_sz, total_sz) = struct.unpack(ImageChunk.FORMAT,
783 header_bin)
784 data_sz = total_sz - struct.calcsize(ImageChunk.FORMAT)
785
David Zeuthena4fee8b2016-08-22 15:20:43 -0400786 if chunk_type == ImageChunk.TYPE_RAW:
787 if data_sz != (chunk_sz * self.block_size):
788 raise ValueError('Raw chunk input size ({}) does not match output '
789 'size ({})'.
790 format(data_sz, chunk_sz*self.block_size))
791 self._chunks.append(ImageChunk(ImageChunk.TYPE_RAW,
792 chunk_offset,
793 output_offset,
794 chunk_sz*self.block_size,
795 self._image.tell(),
796 None))
Dan Willemsen8e306ae2018-09-17 20:03:23 -0700797 self._image.seek(data_sz, os.SEEK_CUR)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400798
799 elif chunk_type == ImageChunk.TYPE_FILL:
800 if data_sz != 4:
801 raise ValueError('Fill chunk should have 4 bytes of fill, but this '
802 'has {}'.format(data_sz))
803 fill_data = self._image.read(4)
804 self._chunks.append(ImageChunk(ImageChunk.TYPE_FILL,
805 chunk_offset,
806 output_offset,
807 chunk_sz*self.block_size,
808 None,
809 fill_data))
810 elif chunk_type == ImageChunk.TYPE_DONT_CARE:
811 if data_sz != 0:
812 raise ValueError('Don\'t care chunk input size is non-zero ({})'.
813 format(data_sz))
David Zeuthena4fee8b2016-08-22 15:20:43 -0400814 self._chunks.append(ImageChunk(ImageChunk.TYPE_DONT_CARE,
815 chunk_offset,
816 output_offset,
817 chunk_sz*self.block_size,
818 None,
819 None))
820 elif chunk_type == ImageChunk.TYPE_CRC32:
821 if data_sz != 4:
822 raise ValueError('CRC32 chunk should have 4 bytes of CRC, but '
823 'this has {}'.format(data_sz))
824 self._image.read(4)
825 else:
826 raise ValueError('Unknown chunk type {}'.format(chunk_type))
827
828 offset += chunk_sz
829 output_offset += chunk_sz*self.block_size
830
831 # Record where sparse data end.
832 self._sparse_end = self._image.tell()
833
834 # Now that we've traversed all chunks, sanity check.
835 if self._num_total_blocks != offset:
836 raise ValueError('The header said we should have {} output blocks, '
837 'but we saw {}'.format(self._num_total_blocks, offset))
838 junk_len = len(self._image.read())
839 if junk_len > 0:
840 raise ValueError('There were {} bytes of extra data at the end of the '
841 'file.'.format(junk_len))
842
David Zeuthen09692692016-09-30 16:16:40 -0400843 # Assign |image_size|.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400844 self.image_size = output_offset
David Zeuthena4fee8b2016-08-22 15:20:43 -0400845
846 # This is used when bisecting in read() to find the initial slice.
847 self._chunk_output_offsets = [i.output_offset for i in self._chunks]
848
849 self.is_sparse = True
850
851 def _update_chunks_and_blocks(self):
852 """Helper function to update the image header.
853
854 The the |total_chunks| and |total_blocks| fields in the header
855 will be set to value of the |_num_total_blocks| and
856 |_num_total_chunks| attributes.
857
858 """
859 self._image.seek(self.NUM_CHUNKS_AND_BLOCKS_OFFSET, os.SEEK_SET)
860 self._image.write(struct.pack(self.NUM_CHUNKS_AND_BLOCKS_FORMAT,
861 self._num_total_blocks,
862 self._num_total_chunks))
863
864 def append_dont_care(self, num_bytes):
865 """Appends a DONT_CARE chunk to the sparse file.
866
867 The given number of bytes must be a multiple of the block size.
868
869 Arguments:
870 num_bytes: Size in number of bytes of the DONT_CARE chunk.
871 """
872 assert num_bytes % self.block_size == 0
873
874 if not self.is_sparse:
875 self._image.seek(0, os.SEEK_END)
876 # This is more efficient that writing NUL bytes since it'll add
877 # a hole on file systems that support sparse files (native
878 # sparse, not Android sparse).
879 self._image.truncate(self._image.tell() + num_bytes)
880 self._read_header()
881 return
882
883 self._num_total_chunks += 1
Jan Monsch23e0c622019-12-11 11:23:58 +0100884 self._num_total_blocks += num_bytes // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -0400885 self._update_chunks_and_blocks()
886
887 self._image.seek(self._sparse_end, os.SEEK_SET)
888 self._image.write(struct.pack(ImageChunk.FORMAT,
889 ImageChunk.TYPE_DONT_CARE,
890 0, # Reserved
Jan Monsch23e0c622019-12-11 11:23:58 +0100891 num_bytes // self.block_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -0400892 struct.calcsize(ImageChunk.FORMAT)))
893 self._read_header()
894
895 def append_raw(self, data):
896 """Appends a RAW chunk to the sparse file.
897
898 The length of the given data must be a multiple of the block size.
899
900 Arguments:
901 data: Data to append.
902 """
903 assert len(data) % self.block_size == 0
904
905 if not self.is_sparse:
906 self._image.seek(0, os.SEEK_END)
907 self._image.write(data)
908 self._read_header()
909 return
910
911 self._num_total_chunks += 1
Jan Monsch23e0c622019-12-11 11:23:58 +0100912 self._num_total_blocks += len(data) // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -0400913 self._update_chunks_and_blocks()
914
915 self._image.seek(self._sparse_end, os.SEEK_SET)
916 self._image.write(struct.pack(ImageChunk.FORMAT,
917 ImageChunk.TYPE_RAW,
918 0, # Reserved
Jan Monsch23e0c622019-12-11 11:23:58 +0100919 len(data) // self.block_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -0400920 len(data) +
921 struct.calcsize(ImageChunk.FORMAT)))
922 self._image.write(data)
923 self._read_header()
924
925 def append_fill(self, fill_data, size):
926 """Appends a fill chunk to the sparse file.
927
928 The total length of the fill data must be a multiple of the block size.
929
930 Arguments:
931 fill_data: Fill data to append - must be four bytes.
932 size: Number of chunk - must be a multiple of four and the block size.
933 """
934 assert len(fill_data) == 4
935 assert size % 4 == 0
936 assert size % self.block_size == 0
937
938 if not self.is_sparse:
939 self._image.seek(0, os.SEEK_END)
Jan Monsch23e0c622019-12-11 11:23:58 +0100940 self._image.write(fill_data * (size//4))
David Zeuthena4fee8b2016-08-22 15:20:43 -0400941 self._read_header()
942 return
943
944 self._num_total_chunks += 1
Jan Monsch23e0c622019-12-11 11:23:58 +0100945 self._num_total_blocks += size // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -0400946 self._update_chunks_and_blocks()
947
948 self._image.seek(self._sparse_end, os.SEEK_SET)
949 self._image.write(struct.pack(ImageChunk.FORMAT,
950 ImageChunk.TYPE_FILL,
951 0, # Reserved
Jan Monsch23e0c622019-12-11 11:23:58 +0100952 size // self.block_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -0400953 4 + struct.calcsize(ImageChunk.FORMAT)))
954 self._image.write(fill_data)
955 self._read_header()
956
957 def seek(self, offset):
958 """Sets the cursor position for reading from unsparsified file.
959
960 Arguments:
961 offset: Offset to seek to from the beginning of the file.
Jan Monsch77cd2022019-12-10 17:18:04 +0100962
963 Raises:
964 RuntimeError: If the given offset is negative.
David Zeuthena4fee8b2016-08-22 15:20:43 -0400965 """
Lonnie Liu6b5a33e2017-10-31 18:01:09 -0700966 if offset < 0:
Jan Monscheeb28b62019-12-05 16:17:09 +0100967 raise RuntimeError('Seeking with negative offset: %d' % offset)
David Zeuthena4fee8b2016-08-22 15:20:43 -0400968 self._file_pos = offset
969
970 def read(self, size):
971 """Reads data from the unsparsified file.
972
973 This method may return fewer than |size| bytes of data if the end
974 of the file was encountered.
975
976 The file cursor for reading is advanced by the number of bytes
977 read.
978
979 Arguments:
980 size: Number of bytes to read.
981
982 Returns:
983 The data.
984
985 """
986 if not self.is_sparse:
987 self._image.seek(self._file_pos)
988 data = self._image.read(size)
989 self._file_pos += len(data)
990 return data
991
992 # Iterate over all chunks.
993 chunk_idx = bisect.bisect_right(self._chunk_output_offsets,
994 self._file_pos) - 1
995 data = bytearray()
996 to_go = size
997 while to_go > 0:
998 chunk = self._chunks[chunk_idx]
999 chunk_pos_offset = self._file_pos - chunk.output_offset
1000 chunk_pos_to_go = min(chunk.output_size - chunk_pos_offset, to_go)
1001
1002 if chunk.chunk_type == ImageChunk.TYPE_RAW:
1003 self._image.seek(chunk.input_offset + chunk_pos_offset)
1004 data.extend(self._image.read(chunk_pos_to_go))
1005 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
Jan Monsch23e0c622019-12-11 11:23:58 +01001006 all_data = chunk.fill_data*(chunk_pos_to_go // len(chunk.fill_data) + 2)
David Zeuthena4fee8b2016-08-22 15:20:43 -04001007 offset_mod = chunk_pos_offset % len(chunk.fill_data)
1008 data.extend(all_data[offset_mod:(offset_mod + chunk_pos_to_go)])
1009 else:
1010 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
1011 data.extend('\0' * chunk_pos_to_go)
1012
1013 to_go -= chunk_pos_to_go
1014 self._file_pos += chunk_pos_to_go
1015 chunk_idx += 1
1016 # Generate partial read in case of EOF.
1017 if chunk_idx >= len(self._chunks):
1018 break
1019
1020 return data
1021
1022 def tell(self):
1023 """Returns the file cursor position for reading from unsparsified file.
1024
1025 Returns:
1026 The file cursor position for reading.
1027 """
1028 return self._file_pos
1029
1030 def truncate(self, size):
1031 """Truncates the unsparsified file.
1032
1033 Arguments:
1034 size: Desired size of unsparsified file.
1035
1036 Raises:
1037 ValueError: If desired size isn't a multiple of the block size.
1038 """
1039 if not self.is_sparse:
1040 self._image.truncate(size)
1041 self._read_header()
1042 return
1043
1044 if size % self.block_size != 0:
1045 raise ValueError('Cannot truncate to a size which is not a multiple '
1046 'of the block size')
1047
1048 if size == self.image_size:
1049 # Trivial where there's nothing to do.
1050 return
1051 elif size < self.image_size:
1052 chunk_idx = bisect.bisect_right(self._chunk_output_offsets, size) - 1
1053 chunk = self._chunks[chunk_idx]
1054 if chunk.output_offset != size:
1055 # Truncation in the middle of a trunk - need to keep the chunk
1056 # and modify it.
1057 chunk_idx_for_update = chunk_idx + 1
1058 num_to_keep = size - chunk.output_offset
1059 assert num_to_keep % self.block_size == 0
1060 if chunk.chunk_type == ImageChunk.TYPE_RAW:
1061 truncate_at = (chunk.chunk_offset +
1062 struct.calcsize(ImageChunk.FORMAT) + num_to_keep)
1063 data_sz = num_to_keep
1064 elif chunk.chunk_type == ImageChunk.TYPE_FILL:
1065 truncate_at = (chunk.chunk_offset +
1066 struct.calcsize(ImageChunk.FORMAT) + 4)
1067 data_sz = 4
1068 else:
1069 assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
1070 truncate_at = chunk.chunk_offset + struct.calcsize(ImageChunk.FORMAT)
1071 data_sz = 0
Jan Monsch23e0c622019-12-11 11:23:58 +01001072 chunk_sz = num_to_keep // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -04001073 total_sz = data_sz + struct.calcsize(ImageChunk.FORMAT)
1074 self._image.seek(chunk.chunk_offset)
1075 self._image.write(struct.pack(ImageChunk.FORMAT,
1076 chunk.chunk_type,
1077 0, # Reserved
1078 chunk_sz,
1079 total_sz))
1080 chunk.output_size = num_to_keep
1081 else:
1082 # Truncation at trunk boundary.
1083 truncate_at = chunk.chunk_offset
1084 chunk_idx_for_update = chunk_idx
1085
1086 self._num_total_chunks = chunk_idx_for_update
1087 self._num_total_blocks = 0
1088 for i in range(0, chunk_idx_for_update):
Jan Monsch23e0c622019-12-11 11:23:58 +01001089 self._num_total_blocks += self._chunks[i].output_size // self.block_size
David Zeuthena4fee8b2016-08-22 15:20:43 -04001090 self._update_chunks_and_blocks()
1091 self._image.truncate(truncate_at)
1092
1093 # We've modified the file so re-read all data.
1094 self._read_header()
1095 else:
1096 # Truncating to grow - just add a DONT_CARE section.
1097 self.append_dont_care(size - self.image_size)
1098
1099
David Zeuthen21e95262016-07-27 17:58:40 -04001100class AvbDescriptor(object):
1101 """Class for AVB descriptor.
1102
1103 See the |AvbDescriptor| C struct for more information.
1104
1105 Attributes:
1106 tag: The tag identifying what kind of descriptor this is.
1107 data: The data in the descriptor.
1108 """
1109
1110 SIZE = 16
1111 FORMAT_STRING = ('!QQ') # tag, num_bytes_following (descriptor header)
1112
1113 def __init__(self, data):
1114 """Initializes a new property descriptor.
1115
1116 Arguments:
1117 data: If not None, must be a bytearray().
1118
1119 Raises:
1120 LookupError: If the given descriptor is malformed.
1121 """
1122 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1123
1124 if data:
1125 (self.tag, num_bytes_following) = (
1126 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1127 self.data = data[self.SIZE:self.SIZE + num_bytes_following]
1128 else:
1129 self.tag = None
1130 self.data = None
1131
1132 def print_desc(self, o):
1133 """Print the descriptor.
1134
1135 Arguments:
1136 o: The object to write the output to.
1137 """
1138 o.write(' Unknown descriptor:\n')
1139 o.write(' Tag: {}\n'.format(self.tag))
1140 if len(self.data) < 256:
1141 o.write(' Data: {} ({} bytes)\n'.format(
1142 repr(str(self.data)), len(self.data)))
1143 else:
1144 o.write(' Data: {} bytes\n'.format(len(self.data)))
1145
1146 def encode(self):
1147 """Serializes the descriptor.
1148
1149 Returns:
1150 A bytearray() with the descriptor data.
1151 """
1152 num_bytes_following = len(self.data)
1153 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1154 padding_size = nbf_with_padding - num_bytes_following
1155 desc = struct.pack(self.FORMAT_STRING, self.tag, nbf_with_padding)
1156 padding = struct.pack(str(padding_size) + 'x')
1157 ret = desc + self.data + padding
1158 return bytearray(ret)
1159
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001160 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001161 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001162 """Verifies contents of the descriptor - used in verify_image sub-command.
1163
1164 Arguments:
1165 image_dir: The directory of the file being verified.
1166 image_ext: The extension of the file being verified (e.g. '.img').
1167 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001168 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001169 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001170 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1171 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001172
1173 Returns:
1174 True if the descriptor verifies, False otherwise.
1175 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01001176 # Deletes unused parameters to prevent pylint warning unused-argument.
1177 del image_dir, image_ext, expected_chain_partitions_map
1178 del image_containing_descriptor, accept_zeroed_hashtree
1179
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001180 # Nothing to do.
1181 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001182
Jan Monscheeb28b62019-12-05 16:17:09 +01001183
David Zeuthen21e95262016-07-27 17:58:40 -04001184class AvbPropertyDescriptor(AvbDescriptor):
1185 """A class for property descriptors.
1186
1187 See the |AvbPropertyDescriptor| C struct for more information.
1188
1189 Attributes:
1190 key: The key.
1191 value: The key.
1192 """
1193
1194 TAG = 0
1195 SIZE = 32
1196 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1197 'Q' # key size (bytes)
1198 'Q') # value size (bytes)
1199
1200 def __init__(self, data=None):
1201 """Initializes a new property descriptor.
1202
1203 Arguments:
1204 data: If not None, must be a bytearray of size |SIZE|.
1205
1206 Raises:
1207 LookupError: If the given descriptor is malformed.
1208 """
1209 AvbDescriptor.__init__(self, None)
1210 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1211
1212 if data:
1213 (tag, num_bytes_following, key_size,
1214 value_size) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
1215 expected_size = round_to_multiple(
1216 self.SIZE - 16 + key_size + 1 + value_size + 1, 8)
1217 if tag != self.TAG or num_bytes_following != expected_size:
1218 raise LookupError('Given data does not look like a property '
1219 'descriptor.')
1220 self.key = data[self.SIZE:(self.SIZE + key_size)]
1221 self.value = data[(self.SIZE + key_size + 1):(self.SIZE + key_size + 1 +
1222 value_size)]
1223 else:
1224 self.key = ''
1225 self.value = ''
1226
1227 def print_desc(self, o):
1228 """Print the descriptor.
1229
1230 Arguments:
1231 o: The object to write the output to.
1232 """
1233 if len(self.value) < 256:
1234 o.write(' Prop: {} -> {}\n'.format(self.key, repr(str(self.value))))
1235 else:
1236 o.write(' Prop: {} -> ({} bytes)\n'.format(self.key, len(self.value)))
1237
1238 def encode(self):
1239 """Serializes the descriptor.
1240
1241 Returns:
1242 A bytearray() with the descriptor data.
1243 """
1244 num_bytes_following = self.SIZE + len(self.key) + len(self.value) + 2 - 16
1245 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1246 padding_size = nbf_with_padding - num_bytes_following
1247 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1248 len(self.key), len(self.value))
1249 padding = struct.pack(str(padding_size) + 'x')
1250 ret = desc + self.key + '\0' + self.value + '\0' + padding
1251 return bytearray(ret)
1252
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001253 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001254 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001255 """Verifies contents of the descriptor - used in verify_image sub-command.
1256
1257 Arguments:
1258 image_dir: The directory of the file being verified.
1259 image_ext: The extension of the file being verified (e.g. '.img').
1260 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001261 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001262 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001263 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1264 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001265
1266 Returns:
1267 True if the descriptor verifies, False otherwise.
1268 """
1269 # Nothing to do.
1270 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001271
Jan Monscheeb28b62019-12-05 16:17:09 +01001272
David Zeuthen21e95262016-07-27 17:58:40 -04001273class AvbHashtreeDescriptor(AvbDescriptor):
1274 """A class for hashtree descriptors.
1275
1276 See the |AvbHashtreeDescriptor| C struct for more information.
1277
1278 Attributes:
1279 dm_verity_version: dm-verity version used.
1280 image_size: Size of the image, after rounding up to |block_size|.
1281 tree_offset: Offset of the hash tree in the file.
1282 tree_size: Size of the tree.
1283 data_block_size: Data block size
1284 hash_block_size: Hash block size
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001285 fec_num_roots: Number of roots used for FEC (0 if FEC is not used).
1286 fec_offset: Offset of FEC data (0 if FEC is not used).
1287 fec_size: Size of FEC data (0 if FEC is not used).
David Zeuthen21e95262016-07-27 17:58:40 -04001288 hash_algorithm: Hash algorithm used.
1289 partition_name: Partition name.
1290 salt: Salt used.
1291 root_digest: Root digest.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001292 flags: Descriptor flags (see avb_hashtree_descriptor.h).
David Zeuthen21e95262016-07-27 17:58:40 -04001293 """
1294
1295 TAG = 1
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001296 RESERVED = 60
1297 SIZE = 120 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001298 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1299 'L' # dm-verity version used
1300 'Q' # image size (bytes)
1301 'Q' # tree offset (bytes)
1302 'Q' # tree size (bytes)
1303 'L' # data block size (bytes)
1304 'L' # hash block size (bytes)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001305 'L' # FEC number of roots
1306 'Q' # FEC offset (bytes)
1307 'Q' # FEC size (bytes)
David Zeuthen21e95262016-07-27 17:58:40 -04001308 '32s' # hash algorithm used
1309 'L' # partition name (bytes)
1310 'L' # salt length (bytes)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001311 'L' # root digest length (bytes)
1312 'L' + # flags
David Zeuthen5cb2db92016-10-27 15:14:14 -04001313 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001314
1315 def __init__(self, data=None):
1316 """Initializes a new hashtree descriptor.
1317
1318 Arguments:
1319 data: If not None, must be a bytearray of size |SIZE|.
1320
1321 Raises:
1322 LookupError: If the given descriptor is malformed.
1323 """
1324 AvbDescriptor.__init__(self, None)
1325 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1326
1327 if data:
1328 (tag, num_bytes_following, self.dm_verity_version, self.image_size,
1329 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001330 self.hash_block_size, self.fec_num_roots, self.fec_offset, self.fec_size,
1331 self.hash_algorithm, partition_name_len, salt_len,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001332 root_digest_len, self.flags, _) = struct.unpack(self.FORMAT_STRING,
1333 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001334 expected_size = round_to_multiple(
1335 self.SIZE - 16 + partition_name_len + salt_len + root_digest_len, 8)
1336 if tag != self.TAG or num_bytes_following != expected_size:
1337 raise LookupError('Given data does not look like a hashtree '
1338 'descriptor.')
1339 # Nuke NUL-bytes at the end.
1340 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1341 o = 0
1342 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1343 partition_name_len)])
1344 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1345 self.partition_name.decode('utf-8')
1346 o += partition_name_len
1347 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1348 o += salt_len
1349 self.root_digest = data[(self.SIZE + o):(self.SIZE + o + root_digest_len)]
1350 if root_digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001351 if root_digest_len != 0:
1352 raise LookupError('root_digest_len doesn\'t match hash algorithm')
David Zeuthen21e95262016-07-27 17:58:40 -04001353
1354 else:
1355 self.dm_verity_version = 0
1356 self.image_size = 0
1357 self.tree_offset = 0
1358 self.tree_size = 0
1359 self.data_block_size = 0
1360 self.hash_block_size = 0
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001361 self.fec_num_roots = 0
1362 self.fec_offset = 0
1363 self.fec_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001364 self.hash_algorithm = ''
1365 self.partition_name = ''
1366 self.salt = bytearray()
1367 self.root_digest = bytearray()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001368 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001369
1370 def print_desc(self, o):
1371 """Print the descriptor.
1372
1373 Arguments:
1374 o: The object to write the output to.
1375 """
1376 o.write(' Hashtree descriptor:\n')
1377 o.write(' Version of dm-verity: {}\n'.format(self.dm_verity_version))
1378 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1379 o.write(' Tree Offset: {}\n'.format(self.tree_offset))
1380 o.write(' Tree Size: {} bytes\n'.format(self.tree_size))
1381 o.write(' Data Block Size: {} bytes\n'.format(
1382 self.data_block_size))
1383 o.write(' Hash Block Size: {} bytes\n'.format(
1384 self.hash_block_size))
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001385 o.write(' FEC num roots: {}\n'.format(self.fec_num_roots))
1386 o.write(' FEC offset: {}\n'.format(self.fec_offset))
1387 o.write(' FEC size: {} bytes\n'.format(self.fec_size))
David Zeuthen21e95262016-07-27 17:58:40 -04001388 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1389 o.write(' Partition Name: {}\n'.format(self.partition_name))
1390 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1391 'hex')))
1392 o.write(' Root Digest: {}\n'.format(str(
1393 self.root_digest).encode('hex')))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001394 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001395
1396 def encode(self):
1397 """Serializes the descriptor.
1398
1399 Returns:
1400 A bytearray() with the descriptor data.
1401 """
1402 encoded_name = self.partition_name.encode('utf-8')
1403 num_bytes_following = (self.SIZE + len(encoded_name) + len(self.salt) +
1404 len(self.root_digest) - 16)
1405 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1406 padding_size = nbf_with_padding - num_bytes_following
1407 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1408 self.dm_verity_version, self.image_size,
1409 self.tree_offset, self.tree_size, self.data_block_size,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04001410 self.hash_block_size, self.fec_num_roots,
1411 self.fec_offset, self.fec_size, self.hash_algorithm,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001412 len(encoded_name), len(self.salt), len(self.root_digest),
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001413 self.flags, self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001414 padding = struct.pack(str(padding_size) + 'x')
1415 ret = desc + encoded_name + self.salt + self.root_digest + padding
1416 return bytearray(ret)
1417
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001418 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001419 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001420 """Verifies contents of the descriptor - used in verify_image sub-command.
1421
1422 Arguments:
1423 image_dir: The directory of the file being verified.
1424 image_ext: The extension of the file being verified (e.g. '.img').
1425 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001426 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001427 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001428 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1429 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001430
1431 Returns:
1432 True if the descriptor verifies, False otherwise.
1433 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01001434 if not self.partition_name:
Tao Bao558bd752019-09-18 18:18:34 -07001435 image_filename = image_containing_descriptor.filename
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001436 image = image_containing_descriptor
1437 else:
1438 image_filename = os.path.join(image_dir, self.partition_name + image_ext)
1439 image = ImageHandler(image_filename)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001440 # Generate the hashtree and checks that it matches what's in the file.
1441 digest_size = len(hashlib.new(name=self.hash_algorithm).digest())
1442 digest_padding = round_to_pow2(digest_size) - digest_size
1443 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
Jan Monscheeb28b62019-12-05 16:17:09 +01001444 self.image_size, self.data_block_size, digest_size + digest_padding)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001445 root_digest, hash_tree = generate_hash_tree(image, self.image_size,
1446 self.data_block_size,
1447 self.hash_algorithm, self.salt,
1448 digest_padding,
1449 hash_level_offsets,
1450 tree_size)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001451 # The root digest must match unless it is not embedded in the descriptor.
Jan Monsch23e0c622019-12-11 11:23:58 +01001452 if self.root_digest and root_digest != self.root_digest:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001453 sys.stderr.write('hashtree of {} does not match descriptor\n'.
1454 format(image_filename))
1455 return False
1456 # ... also check that the on-disk hashtree matches
1457 image.seek(self.tree_offset)
1458 hash_tree_ondisk = image.read(self.tree_size)
Jooyung Hand7221942019-06-17 13:19:57 +09001459 is_zeroed = (self.tree_size == 0) or (hash_tree_ondisk[0:8] == 'ZeRoHaSH')
David Zeuthen1394f762019-04-30 10:20:11 -04001460 if is_zeroed and accept_zeroed_hashtree:
Jan Monsch23e0c622019-12-11 11:23:58 +01001461 print('{}: skipping verification since hashtree is zeroed and '
1462 '--accept_zeroed_hashtree was given'
1463 .format(self.partition_name))
David Zeuthen1394f762019-04-30 10:20:11 -04001464 else:
1465 if hash_tree != hash_tree_ondisk:
1466 sys.stderr.write('hashtree of {} contains invalid data\n'.
Tao Bao558bd752019-09-18 18:18:34 -07001467 format(image_filename))
David Zeuthen1394f762019-04-30 10:20:11 -04001468 return False
Jan Monsch23e0c622019-12-11 11:23:58 +01001469 print('{}: Successfully verified {} hashtree of {} for image of {} bytes'
1470 .format(self.partition_name, self.hash_algorithm, image.filename,
1471 self.image_size))
Jan Monschfe00c0a2019-12-11 11:19:40 +01001472 # TODO(zeuthen): we could also verify that the FEC stored in the image is
1473 # correct but this a) currently requires the 'fec' binary; and b) takes a
1474 # long time; and c) is not strictly needed for verification purposes as
1475 # we've already verified the root hash.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001476 return True
1477
David Zeuthen21e95262016-07-27 17:58:40 -04001478
1479class AvbHashDescriptor(AvbDescriptor):
1480 """A class for hash descriptors.
1481
1482 See the |AvbHashDescriptor| C struct for more information.
1483
1484 Attributes:
1485 image_size: Image size, in bytes.
1486 hash_algorithm: Hash algorithm used.
1487 partition_name: Partition name.
1488 salt: Salt used.
1489 digest: The hash value of salt and data combined.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001490 flags: The descriptor flags (see avb_hash_descriptor.h).
David Zeuthen21e95262016-07-27 17:58:40 -04001491 """
1492
1493 TAG = 2
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001494 RESERVED = 60
1495 SIZE = 72 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001496 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
1497 'Q' # image size (bytes)
1498 '32s' # hash algorithm used
1499 'L' # partition name (bytes)
1500 'L' # salt length (bytes)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001501 'L' # digest length (bytes)
1502 'L' + # flags
David Zeuthen5cb2db92016-10-27 15:14:14 -04001503 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001504
1505 def __init__(self, data=None):
1506 """Initializes a new hash descriptor.
1507
1508 Arguments:
1509 data: If not None, must be a bytearray of size |SIZE|.
1510
1511 Raises:
1512 LookupError: If the given descriptor is malformed.
1513 """
1514 AvbDescriptor.__init__(self, None)
1515 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1516
1517 if data:
1518 (tag, num_bytes_following, self.image_size, self.hash_algorithm,
1519 partition_name_len, salt_len,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001520 digest_len, self.flags, _) = struct.unpack(self.FORMAT_STRING,
1521 data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001522 expected_size = round_to_multiple(
1523 self.SIZE - 16 + partition_name_len + salt_len + digest_len, 8)
1524 if tag != self.TAG or num_bytes_following != expected_size:
1525 raise LookupError('Given data does not look like a hash ' 'descriptor.')
1526 # Nuke NUL-bytes at the end.
1527 self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
1528 o = 0
1529 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1530 partition_name_len)])
1531 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1532 self.partition_name.decode('utf-8')
1533 o += partition_name_len
1534 self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
1535 o += salt_len
1536 self.digest = data[(self.SIZE + o):(self.SIZE + o + digest_len)]
1537 if digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001538 if digest_len != 0:
1539 raise LookupError('digest_len doesn\'t match hash algorithm')
David Zeuthen21e95262016-07-27 17:58:40 -04001540
1541 else:
1542 self.image_size = 0
1543 self.hash_algorithm = ''
1544 self.partition_name = ''
1545 self.salt = bytearray()
1546 self.digest = bytearray()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001547 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001548
1549 def print_desc(self, o):
1550 """Print the descriptor.
1551
1552 Arguments:
1553 o: The object to write the output to.
1554 """
1555 o.write(' Hash descriptor:\n')
1556 o.write(' Image Size: {} bytes\n'.format(self.image_size))
1557 o.write(' Hash Algorithm: {}\n'.format(self.hash_algorithm))
1558 o.write(' Partition Name: {}\n'.format(self.partition_name))
1559 o.write(' Salt: {}\n'.format(str(self.salt).encode(
1560 'hex')))
1561 o.write(' Digest: {}\n'.format(str(self.digest).encode(
1562 'hex')))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001563 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001564
1565 def encode(self):
1566 """Serializes the descriptor.
1567
1568 Returns:
1569 A bytearray() with the descriptor data.
1570 """
1571 encoded_name = self.partition_name.encode('utf-8')
1572 num_bytes_following = (
1573 self.SIZE + len(encoded_name) + len(self.salt) + len(self.digest) - 16)
1574 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1575 padding_size = nbf_with_padding - num_bytes_following
1576 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
1577 self.image_size, self.hash_algorithm, len(encoded_name),
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001578 len(self.salt), len(self.digest), self.flags,
1579 self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001580 padding = struct.pack(str(padding_size) + 'x')
1581 ret = desc + encoded_name + self.salt + self.digest + padding
1582 return bytearray(ret)
1583
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001584 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001585 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001586 """Verifies contents of the descriptor - used in verify_image sub-command.
1587
1588 Arguments:
1589 image_dir: The directory of the file being verified.
1590 image_ext: The extension of the file being verified (e.g. '.img').
1591 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001592 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001593 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001594 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1595 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001596
1597 Returns:
1598 True if the descriptor verifies, False otherwise.
1599 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01001600 if not self.partition_name:
Tao Bao558bd752019-09-18 18:18:34 -07001601 image_filename = image_containing_descriptor.filename
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001602 image = image_containing_descriptor
1603 else:
1604 image_filename = os.path.join(image_dir, self.partition_name + image_ext)
1605 image = ImageHandler(image_filename)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001606 data = image.read(self.image_size)
1607 ha = hashlib.new(self.hash_algorithm)
1608 ha.update(self.salt)
1609 ha.update(data)
1610 digest = ha.digest()
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001611 # The digest must match unless there is no digest in the descriptor.
Jan Monsch23e0c622019-12-11 11:23:58 +01001612 if self.digest and digest != self.digest:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001613 sys.stderr.write('{} digest of {} does not match digest in descriptor\n'.
1614 format(self.hash_algorithm, image_filename))
1615 return False
Jan Monsch23e0c622019-12-11 11:23:58 +01001616 print('{}: Successfully verified {} hash of {} for image of {} bytes'
1617 .format(self.partition_name, self.hash_algorithm, image.filename,
1618 self.image_size))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001619 return True
1620
David Zeuthen21e95262016-07-27 17:58:40 -04001621
1622class AvbKernelCmdlineDescriptor(AvbDescriptor):
1623 """A class for kernel command-line descriptors.
1624
1625 See the |AvbKernelCmdlineDescriptor| C struct for more information.
1626
1627 Attributes:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001628 flags: Flags.
David Zeuthen21e95262016-07-27 17:58:40 -04001629 kernel_cmdline: The kernel command-line.
1630 """
1631
1632 TAG = 3
David Zeuthenfd41eb92016-11-17 12:24:47 -05001633 SIZE = 24
David Zeuthen21e95262016-07-27 17:58:40 -04001634 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001635 'L' # flags
David Zeuthen21e95262016-07-27 17:58:40 -04001636 'L') # cmdline length (bytes)
1637
David Zeuthenfd41eb92016-11-17 12:24:47 -05001638 FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED = (1 << 0)
1639 FLAGS_USE_ONLY_IF_HASHTREE_DISABLED = (1 << 1)
1640
David Zeuthen21e95262016-07-27 17:58:40 -04001641 def __init__(self, data=None):
1642 """Initializes a new kernel cmdline descriptor.
1643
1644 Arguments:
1645 data: If not None, must be a bytearray of size |SIZE|.
1646
1647 Raises:
1648 LookupError: If the given descriptor is malformed.
1649 """
1650 AvbDescriptor.__init__(self, None)
1651 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1652
1653 if data:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001654 (tag, num_bytes_following, self.flags, kernel_cmdline_length) = (
David Zeuthen21e95262016-07-27 17:58:40 -04001655 struct.unpack(self.FORMAT_STRING, data[0:self.SIZE]))
1656 expected_size = round_to_multiple(self.SIZE - 16 + kernel_cmdline_length,
1657 8)
1658 if tag != self.TAG or num_bytes_following != expected_size:
1659 raise LookupError('Given data does not look like a kernel cmdline '
1660 'descriptor.')
1661 # Nuke NUL-bytes at the end.
1662 self.kernel_cmdline = str(data[self.SIZE:(self.SIZE +
1663 kernel_cmdline_length)])
1664 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1665 self.kernel_cmdline.decode('utf-8')
1666 else:
David Zeuthenfd41eb92016-11-17 12:24:47 -05001667 self.flags = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001668 self.kernel_cmdline = ''
1669
1670 def print_desc(self, o):
1671 """Print the descriptor.
1672
1673 Arguments:
1674 o: The object to write the output to.
1675 """
1676 o.write(' Kernel Cmdline descriptor:\n')
David Zeuthenfd41eb92016-11-17 12:24:47 -05001677 o.write(' Flags: {}\n'.format(self.flags))
David Zeuthen21e95262016-07-27 17:58:40 -04001678 o.write(' Kernel Cmdline: {}\n'.format(repr(
1679 self.kernel_cmdline)))
1680
1681 def encode(self):
1682 """Serializes the descriptor.
1683
1684 Returns:
1685 A bytearray() with the descriptor data.
1686 """
1687 encoded_str = self.kernel_cmdline.encode('utf-8')
1688 num_bytes_following = (self.SIZE + len(encoded_str) - 16)
1689 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1690 padding_size = nbf_with_padding - num_bytes_following
1691 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthenfd41eb92016-11-17 12:24:47 -05001692 self.flags, len(encoded_str))
David Zeuthen21e95262016-07-27 17:58:40 -04001693 padding = struct.pack(str(padding_size) + 'x')
1694 ret = desc + encoded_str + padding
1695 return bytearray(ret)
1696
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001697 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001698 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001699 """Verifies contents of the descriptor - used in verify_image sub-command.
1700
1701 Arguments:
1702 image_dir: The directory of the file being verified.
1703 image_ext: The extension of the file being verified (e.g. '.img').
1704 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001705 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001706 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001707 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1708 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001709
1710 Returns:
1711 True if the descriptor verifies, False otherwise.
1712 """
1713 # Nothing to verify.
1714 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001715
Jan Monscheeb28b62019-12-05 16:17:09 +01001716
David Zeuthen21e95262016-07-27 17:58:40 -04001717class AvbChainPartitionDescriptor(AvbDescriptor):
1718 """A class for chained partition descriptors.
1719
1720 See the |AvbChainPartitionDescriptor| C struct for more information.
1721
1722 Attributes:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001723 rollback_index_location: The rollback index location to use.
David Zeuthen21e95262016-07-27 17:58:40 -04001724 partition_name: Partition name.
1725 public_key: Bytes for the public key.
1726 """
1727
1728 TAG = 4
David Zeuthen5cb2db92016-10-27 15:14:14 -04001729 RESERVED = 64
1730 SIZE = 28 + RESERVED
David Zeuthen21e95262016-07-27 17:58:40 -04001731 FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
David Zeuthen40ee1da2016-11-23 15:14:49 -05001732 'L' # rollback_index_location
David Zeuthen21e95262016-07-27 17:58:40 -04001733 'L' # partition_name_size (bytes)
David Zeuthen5cb2db92016-10-27 15:14:14 -04001734 'L' + # public_key_size (bytes)
1735 str(RESERVED) + 's') # reserved
David Zeuthen21e95262016-07-27 17:58:40 -04001736
1737 def __init__(self, data=None):
1738 """Initializes a new chain partition descriptor.
1739
1740 Arguments:
1741 data: If not None, must be a bytearray of size |SIZE|.
1742
1743 Raises:
1744 LookupError: If the given descriptor is malformed.
1745 """
1746 AvbDescriptor.__init__(self, None)
1747 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1748
1749 if data:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001750 (tag, num_bytes_following, self.rollback_index_location,
1751 partition_name_len,
David Zeuthen5cb2db92016-10-27 15:14:14 -04001752 public_key_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
David Zeuthen21e95262016-07-27 17:58:40 -04001753 expected_size = round_to_multiple(
1754 self.SIZE - 16 + partition_name_len + public_key_len, 8)
1755 if tag != self.TAG or num_bytes_following != expected_size:
1756 raise LookupError('Given data does not look like a chain partition '
1757 'descriptor.')
1758 o = 0
1759 self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
1760 partition_name_len)])
1761 # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
1762 self.partition_name.decode('utf-8')
1763 o += partition_name_len
1764 self.public_key = data[(self.SIZE + o):(self.SIZE + o + public_key_len)]
1765
1766 else:
David Zeuthen40ee1da2016-11-23 15:14:49 -05001767 self.rollback_index_location = 0
David Zeuthen21e95262016-07-27 17:58:40 -04001768 self.partition_name = ''
1769 self.public_key = bytearray()
1770
1771 def print_desc(self, o):
1772 """Print the descriptor.
1773
1774 Arguments:
1775 o: The object to write the output to.
1776 """
1777 o.write(' Chain Partition descriptor:\n')
David Zeuthen40ee1da2016-11-23 15:14:49 -05001778 o.write(' Partition Name: {}\n'.format(self.partition_name))
1779 o.write(' Rollback Index Location: {}\n'.format(
1780 self.rollback_index_location))
David Zeuthen21e95262016-07-27 17:58:40 -04001781 # Just show the SHA1 of the key, for size reasons.
1782 hexdig = hashlib.sha1(self.public_key).hexdigest()
David Zeuthen40ee1da2016-11-23 15:14:49 -05001783 o.write(' Public key (sha1): {}\n'.format(hexdig))
David Zeuthen21e95262016-07-27 17:58:40 -04001784
1785 def encode(self):
1786 """Serializes the descriptor.
1787
1788 Returns:
1789 A bytearray() with the descriptor data.
1790 """
1791 encoded_name = self.partition_name.encode('utf-8')
1792 num_bytes_following = (
1793 self.SIZE + len(encoded_name) + len(self.public_key) - 16)
1794 nbf_with_padding = round_to_multiple(num_bytes_following, 8)
1795 padding_size = nbf_with_padding - num_bytes_following
1796 desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
David Zeuthen40ee1da2016-11-23 15:14:49 -05001797 self.rollback_index_location, len(encoded_name),
David Zeuthen5cb2db92016-10-27 15:14:14 -04001798 len(self.public_key), self.RESERVED*'\0')
David Zeuthen21e95262016-07-27 17:58:40 -04001799 padding = struct.pack(str(padding_size) + 'x')
1800 ret = desc + encoded_name + self.public_key + padding
1801 return bytearray(ret)
1802
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001803 def verify(self, image_dir, image_ext, expected_chain_partitions_map,
David Zeuthen1394f762019-04-30 10:20:11 -04001804 image_containing_descriptor, accept_zeroed_hashtree):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001805 """Verifies contents of the descriptor - used in verify_image sub-command.
1806
1807 Arguments:
1808 image_dir: The directory of the file being verified.
1809 image_ext: The extension of the file being verified (e.g. '.img').
1810 expected_chain_partitions_map: A map from partition name to the
Jan Monscheeb28b62019-12-05 16:17:09 +01001811 tuple (rollback_index_location, key_blob).
David Zeuthenf4f51eb2018-09-20 14:56:46 -04001812 image_containing_descriptor: The image the descriptor is in.
Jan Monscheeb28b62019-12-05 16:17:09 +01001813 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
1814 zeroed out.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001815
1816 Returns:
1817 True if the descriptor verifies, False otherwise.
1818 """
1819 value = expected_chain_partitions_map.get(self.partition_name)
1820 if not value:
1821 sys.stderr.write('No expected chain partition for partition {}. Use '
1822 '--expected_chain_partition to specify expected '
David Zeuthene947cb62019-01-25 15:27:08 -05001823 'contents or --follow_chain_partitions.\n'.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001824 format(self.partition_name))
1825 return False
1826 rollback_index_location, pk_blob = value
1827
1828 if self.rollback_index_location != rollback_index_location:
1829 sys.stderr.write('Expected rollback_index_location {} does not '
1830 'match {} in descriptor for partition {}\n'.
1831 format(rollback_index_location,
1832 self.rollback_index_location,
1833 self.partition_name))
1834 return False
1835
1836 if self.public_key != pk_blob:
1837 sys.stderr.write('Expected public key blob does not match public '
1838 'key blob in descriptor for partition {}\n'.
1839 format(self.partition_name))
1840 return False
1841
Jan Monsch23e0c622019-12-11 11:23:58 +01001842 print('{}: Successfully verified chain partition descriptor matches '
1843 'expected data'.format(self.partition_name))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04001844
1845 return True
David Zeuthen21e95262016-07-27 17:58:40 -04001846
1847DESCRIPTOR_CLASSES = [
1848 AvbPropertyDescriptor, AvbHashtreeDescriptor, AvbHashDescriptor,
1849 AvbKernelCmdlineDescriptor, AvbChainPartitionDescriptor
1850]
1851
1852
1853def parse_descriptors(data):
1854 """Parses a blob of data into descriptors.
1855
1856 Arguments:
1857 data: A bytearray() with encoded descriptors.
1858
1859 Returns:
1860 A list of instances of objects derived from AvbDescriptor. For
1861 unknown descriptors, the class AvbDescriptor is used.
1862 """
1863 o = 0
1864 ret = []
1865 while o < len(data):
1866 tag, nb_following = struct.unpack('!2Q', data[o:o + 16])
1867 if tag < len(DESCRIPTOR_CLASSES):
1868 c = DESCRIPTOR_CLASSES[tag]
1869 else:
1870 c = AvbDescriptor
1871 ret.append(c(bytearray(data[o:o + 16 + nb_following])))
1872 o += 16 + nb_following
1873 return ret
1874
1875
1876class AvbFooter(object):
1877 """A class for parsing and writing footers.
1878
1879 Footers are stored at the end of partitions and point to where the
1880 AvbVBMeta blob is located. They also contain the original size of
1881 the image before AVB information was added.
1882
1883 Attributes:
1884 magic: Magic for identifying the footer, see |MAGIC|.
1885 version_major: The major version of avbtool that wrote the footer.
1886 version_minor: The minor version of avbtool that wrote the footer.
1887 original_image_size: Original image size.
1888 vbmeta_offset: Offset of where the AvbVBMeta blob is stored.
1889 vbmeta_size: Size of the AvbVBMeta blob.
1890 """
1891
1892 MAGIC = 'AVBf'
1893 SIZE = 64
1894 RESERVED = 28
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08001895 FOOTER_VERSION_MAJOR = AVB_FOOTER_VERSION_MAJOR
1896 FOOTER_VERSION_MINOR = AVB_FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001897 FORMAT_STRING = ('!4s2L' # magic, 2 x version.
1898 'Q' # Original image size.
1899 'Q' # Offset of VBMeta blob.
1900 'Q' + # Size of VBMeta blob.
1901 str(RESERVED) + 'x') # padding for reserved bytes
1902
1903 def __init__(self, data=None):
1904 """Initializes a new footer object.
1905
1906 Arguments:
1907 data: If not None, must be a bytearray of size 4096.
1908
1909 Raises:
1910 LookupError: If the given footer is malformed.
1911 struct.error: If the given data has no footer.
1912 """
1913 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
1914
1915 if data:
1916 (self.magic, self.version_major, self.version_minor,
1917 self.original_image_size, self.vbmeta_offset,
1918 self.vbmeta_size) = struct.unpack(self.FORMAT_STRING, data)
1919 if self.magic != self.MAGIC:
David Zeuthen8b6973b2016-09-20 12:39:49 -04001920 raise LookupError('Given data does not look like a AVB footer.')
David Zeuthen21e95262016-07-27 17:58:40 -04001921 else:
1922 self.magic = self.MAGIC
David Zeuthene3cadca2017-02-22 21:25:46 -05001923 self.version_major = self.FOOTER_VERSION_MAJOR
1924 self.version_minor = self.FOOTER_VERSION_MINOR
David Zeuthen21e95262016-07-27 17:58:40 -04001925 self.original_image_size = 0
1926 self.vbmeta_offset = 0
1927 self.vbmeta_size = 0
1928
David Zeuthena4fee8b2016-08-22 15:20:43 -04001929 def encode(self):
1930 """Gets a string representing the binary encoding of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001931
David Zeuthena4fee8b2016-08-22 15:20:43 -04001932 Returns:
1933 A bytearray() with a binary representation of the footer.
David Zeuthen21e95262016-07-27 17:58:40 -04001934 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04001935 return struct.pack(self.FORMAT_STRING, self.magic, self.version_major,
1936 self.version_minor, self.original_image_size,
1937 self.vbmeta_offset, self.vbmeta_size)
David Zeuthen21e95262016-07-27 17:58:40 -04001938
1939
1940class AvbVBMetaHeader(object):
David Zeuthen8b6973b2016-09-20 12:39:49 -04001941 """A class for parsing and writing AVB vbmeta images.
David Zeuthen21e95262016-07-27 17:58:40 -04001942
Jan Monschfe00c0a2019-12-11 11:19:40 +01001943 The attributes correspond to the |AvbVBMetaImageHeader| struct defined in
1944 avb_vbmeta_image.h.
1945
David Zeuthen21e95262016-07-27 17:58:40 -04001946 Attributes:
Jan Monschfe00c0a2019-12-11 11:19:40 +01001947 magic: Four bytes equal to "AVB0" (AVB_MAGIC).
1948 required_libavb_version_major: The major version of libavb required for this
1949 header.
1950 required_libavb_version_minor: The minor version of libavb required for this
1951 header.
1952 authentication_data_block_size: The size of the signature block.
1953 auxiliary_data_block_size: The size of the auxiliary data block.
1954 algorithm_type: The verification algorithm used, see |AvbAlgorithmType|
1955 enum.
1956 hash_offset: Offset into the "Authentication data" block of hash data.
1957 hash_size: Length of the hash data.
1958 signature_offset: Offset into the "Authentication data" block of signature
1959 data.
1960 signature_size: Length of the signature data.
1961 public_key_offset: Offset into the "Auxiliary data" block of public key
1962 data.
1963 public_key_size: Length of the public key data.
1964 public_key_metadata_offset: Offset into the "Auxiliary data" block of public
1965 key metadata.
1966 public_key_metadata_size: Length of the public key metadata. Must be set to
1967 zero if there is no public key metadata.
1968 descriptors_offset: Offset into the "Auxiliary data" block of descriptor
1969 data.
1970 descriptors_size: Length of descriptor data.
1971 rollback_index: The rollback index which can be used to prevent rollback to
1972 older versions.
1973 flags: Flags from the AvbVBMetaImageFlags enumeration. This must be set to
1974 zero if the vbmeta image is not a top-level image.
1975 release_string: The release string from avbtool, e.g. "avbtool 1.0.0" or
1976 "avbtool 1.0.0 xyz_board Git-234abde89". Is guaranteed to be NUL
1977 terminated. Applications must not make assumptions about how this
1978 string is formatted.
David Zeuthen21e95262016-07-27 17:58:40 -04001979 """
1980
1981 SIZE = 256
1982
David Zeuthene3cadca2017-02-22 21:25:46 -05001983 # Keep in sync with |reserved0| and |reserved| field of
1984 # |AvbVBMetaImageHeader|.
1985 RESERVED0 = 4
1986 RESERVED = 80
David Zeuthen21e95262016-07-27 17:58:40 -04001987
1988 # Keep in sync with |AvbVBMetaImageHeader|.
1989 FORMAT_STRING = ('!4s2L' # magic, 2 x version
1990 '2Q' # 2 x block size
1991 'L' # algorithm type
1992 '2Q' # offset, size (hash)
1993 '2Q' # offset, size (signature)
1994 '2Q' # offset, size (public key)
David Zeuthen18666ab2016-11-15 11:18:05 -05001995 '2Q' # offset, size (public key metadata)
David Zeuthen21e95262016-07-27 17:58:40 -04001996 '2Q' # offset, size (descriptors)
David Zeuthenfd41eb92016-11-17 12:24:47 -05001997 'Q' # rollback_index
1998 'L' + # flags
David Zeuthene3cadca2017-02-22 21:25:46 -05001999 str(RESERVED0) + 'x' + # padding for reserved bytes
2000 '47sx' + # NUL-terminated release string
David Zeuthen21e95262016-07-27 17:58:40 -04002001 str(RESERVED) + 'x') # padding for reserved bytes
2002
2003 def __init__(self, data=None):
2004 """Initializes a new header object.
2005
2006 Arguments:
2007 data: If not None, must be a bytearray of size 8192.
2008
2009 Raises:
2010 Exception: If the given data is malformed.
2011 """
2012 assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
2013
2014 if data:
David Zeuthene3cadca2017-02-22 21:25:46 -05002015 (self.magic, self.required_libavb_version_major,
2016 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04002017 self.authentication_data_block_size, self.auxiliary_data_block_size,
2018 self.algorithm_type, self.hash_offset, self.hash_size,
2019 self.signature_offset, self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05002020 self.public_key_size, self.public_key_metadata_offset,
2021 self.public_key_metadata_size, self.descriptors_offset,
2022 self.descriptors_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002023 self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05002024 self.flags,
2025 self.release_string) = struct.unpack(self.FORMAT_STRING, data)
David Zeuthen21e95262016-07-27 17:58:40 -04002026 # Nuke NUL-bytes at the end of the string.
2027 if self.magic != 'AVB0':
David Zeuthen8b6973b2016-09-20 12:39:49 -04002028 raise AvbError('Given image does not look like a vbmeta image.')
David Zeuthen21e95262016-07-27 17:58:40 -04002029 else:
2030 self.magic = 'AVB0'
David Zeuthene3cadca2017-02-22 21:25:46 -05002031 # Start by just requiring version 1.0. Code that adds features
2032 # in a future version can use bump_required_libavb_version_minor() to
2033 # bump the minor.
2034 self.required_libavb_version_major = AVB_VERSION_MAJOR
2035 self.required_libavb_version_minor = 0
David Zeuthen21e95262016-07-27 17:58:40 -04002036 self.authentication_data_block_size = 0
2037 self.auxiliary_data_block_size = 0
2038 self.algorithm_type = 0
2039 self.hash_offset = 0
2040 self.hash_size = 0
2041 self.signature_offset = 0
2042 self.signature_size = 0
2043 self.public_key_offset = 0
2044 self.public_key_size = 0
David Zeuthen18666ab2016-11-15 11:18:05 -05002045 self.public_key_metadata_offset = 0
2046 self.public_key_metadata_size = 0
David Zeuthen21e95262016-07-27 17:58:40 -04002047 self.descriptors_offset = 0
2048 self.descriptors_size = 0
2049 self.rollback_index = 0
David Zeuthenfd41eb92016-11-17 12:24:47 -05002050 self.flags = 0
David Zeuthene3cadca2017-02-22 21:25:46 -05002051 self.release_string = get_release_string()
2052
2053 def bump_required_libavb_version_minor(self, minor):
2054 """Function to bump required_libavb_version_minor.
2055
2056 Call this when writing data that requires a specific libavb
2057 version to parse it.
2058
2059 Arguments:
2060 minor: The minor version of libavb that has support for the feature.
2061 """
2062 self.required_libavb_version_minor = (
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002063 max(self.required_libavb_version_minor, minor))
David Zeuthen21e95262016-07-27 17:58:40 -04002064
2065 def save(self, output):
2066 """Serializes the header (256 bytes) to disk.
2067
2068 Arguments:
2069 output: The object to write the output to.
2070 """
2071 output.write(struct.pack(
David Zeuthene3cadca2017-02-22 21:25:46 -05002072 self.FORMAT_STRING, self.magic, self.required_libavb_version_major,
2073 self.required_libavb_version_minor, self.authentication_data_block_size,
David Zeuthen21e95262016-07-27 17:58:40 -04002074 self.auxiliary_data_block_size, self.algorithm_type, self.hash_offset,
2075 self.hash_size, self.signature_offset, self.signature_size,
David Zeuthen18666ab2016-11-15 11:18:05 -05002076 self.public_key_offset, self.public_key_size,
2077 self.public_key_metadata_offset, self.public_key_metadata_size,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002078 self.descriptors_offset, self.descriptors_size, self.rollback_index,
David Zeuthene3cadca2017-02-22 21:25:46 -05002079 self.flags, self.release_string))
David Zeuthen21e95262016-07-27 17:58:40 -04002080
2081 def encode(self):
2082 """Serializes the header (256) to a bytearray().
2083
2084 Returns:
2085 A bytearray() with the encoded header.
2086 """
2087 return struct.pack(self.FORMAT_STRING, self.magic,
David Zeuthene3cadca2017-02-22 21:25:46 -05002088 self.required_libavb_version_major,
2089 self.required_libavb_version_minor,
David Zeuthen21e95262016-07-27 17:58:40 -04002090 self.authentication_data_block_size,
2091 self.auxiliary_data_block_size, self.algorithm_type,
2092 self.hash_offset, self.hash_size, self.signature_offset,
2093 self.signature_size, self.public_key_offset,
David Zeuthen18666ab2016-11-15 11:18:05 -05002094 self.public_key_size, self.public_key_metadata_offset,
2095 self.public_key_metadata_size, self.descriptors_offset,
David Zeuthene3cadca2017-02-22 21:25:46 -05002096 self.descriptors_size, self.rollback_index, self.flags,
2097 self.release_string)
David Zeuthen21e95262016-07-27 17:58:40 -04002098
2099
2100class Avb(object):
2101 """Business logic for avbtool command-line tool."""
2102
David Zeuthen8b6973b2016-09-20 12:39:49 -04002103 # Keep in sync with avb_ab_flow.h.
2104 AB_FORMAT_NO_CRC = '!4sBB2xBBBxBBBx12x'
2105 AB_MAGIC = '\0AB0'
2106 AB_MAJOR_VERSION = 1
2107 AB_MINOR_VERSION = 0
2108 AB_MISC_METADATA_OFFSET = 2048
2109
David Zeuthen09692692016-09-30 16:16:40 -04002110 # Constants for maximum metadata size. These are used to give
2111 # meaningful errors if the value passed in via --partition_size is
2112 # too small and when --calc_max_image_size is used. We use
2113 # conservative figures.
2114 MAX_VBMETA_SIZE = 64 * 1024
2115 MAX_FOOTER_SIZE = 4096
2116
David Zeuthen49936b42018-08-07 17:38:58 -04002117 def extract_vbmeta_image(self, output, image_filename, padding_size):
2118 """Implements the 'extract_vbmeta_image' command.
2119
2120 Arguments:
2121 output: Write vbmeta struct to this file.
2122 image_filename: File to extract vbmeta data from (with a footer).
2123 padding_size: If not 0, pads output so size is a multiple of the number.
2124
2125 Raises:
2126 AvbError: If there's no footer in the image.
2127 """
2128 image = ImageHandler(image_filename)
2129
2130 (footer, _, _, _) = self._parse_image(image)
2131
2132 if not footer:
2133 raise AvbError('Given image does not have a footer.')
2134
2135 image.seek(footer.vbmeta_offset)
2136 vbmeta_blob = image.read(footer.vbmeta_size)
2137 output.write(vbmeta_blob)
2138
2139 if padding_size > 0:
2140 padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
2141 padding_needed = padded_size - len(vbmeta_blob)
2142 output.write('\0' * padding_needed)
2143
David Zeuthena4fee8b2016-08-22 15:20:43 -04002144 def erase_footer(self, image_filename, keep_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04002145 """Implements the 'erase_footer' command.
2146
2147 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002148 image_filename: File to erase a footer from.
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002149 keep_hashtree: If True, keep the hashtree and FEC around.
David Zeuthen21e95262016-07-27 17:58:40 -04002150
2151 Raises:
2152 AvbError: If there's no footer in the image.
2153 """
2154
David Zeuthena4fee8b2016-08-22 15:20:43 -04002155 image = ImageHandler(image_filename)
2156
David Zeuthen21e95262016-07-27 17:58:40 -04002157 (footer, _, descriptors, _) = self._parse_image(image)
2158
2159 if not footer:
2160 raise AvbError('Given image does not have a footer.')
2161
2162 new_image_size = None
2163 if not keep_hashtree:
2164 new_image_size = footer.original_image_size
2165 else:
2166 # If requested to keep the hashtree, search for a hashtree
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002167 # descriptor to figure out the location and size of the hashtree
2168 # and FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04002169 for desc in descriptors:
2170 if isinstance(desc, AvbHashtreeDescriptor):
2171 # The hashtree is always just following the main data so the
2172 # new size is easily derived.
2173 new_image_size = desc.tree_offset + desc.tree_size
David Zeuthenfbb61fa2017-02-02 12:11:49 -05002174 # If the image has FEC codes, also keep those.
2175 if desc.fec_offset > 0:
2176 fec_end = desc.fec_offset + desc.fec_size
2177 new_image_size = max(new_image_size, fec_end)
David Zeuthen21e95262016-07-27 17:58:40 -04002178 break
2179 if not new_image_size:
2180 raise AvbError('Requested to keep hashtree but no hashtree '
2181 'descriptor was found.')
2182
2183 # And cut...
2184 image.truncate(new_image_size)
2185
David Zeuthen1394f762019-04-30 10:20:11 -04002186 def zero_hashtree(self, image_filename):
2187 """Implements the 'zero_hashtree' command.
2188
2189 Arguments:
2190 image_filename: File to zero hashtree and FEC data from.
2191
2192 Raises:
2193 AvbError: If there's no footer in the image.
2194 """
2195
2196 image = ImageHandler(image_filename)
2197
2198 (footer, _, descriptors, _) = self._parse_image(image)
2199
2200 if not footer:
2201 raise AvbError('Given image does not have a footer.')
2202
2203 # Search for a hashtree descriptor to figure out the location and
2204 # size of the hashtree and FEC.
2205 ht_desc = None
2206 for desc in descriptors:
2207 if isinstance(desc, AvbHashtreeDescriptor):
2208 ht_desc = desc
2209 break
2210
2211 if not ht_desc:
2212 raise AvbError('No hashtree descriptor was found.')
2213
2214 zero_ht_start_offset = ht_desc.tree_offset
2215 zero_ht_num_bytes = ht_desc.tree_size
2216 zero_fec_start_offset = None
2217 zero_fec_num_bytes = 0
2218 if ht_desc.fec_offset > 0:
2219 if ht_desc.fec_offset != ht_desc.tree_offset + ht_desc.tree_size:
2220 raise AvbError('Hash-tree and FEC data must be adjacent.')
2221 zero_fec_start_offset = ht_desc.fec_offset
2222 zero_fec_num_bytes = ht_desc.fec_size
Jan Monsch23e0c622019-12-11 11:23:58 +01002223 zero_end_offset = (zero_ht_start_offset + zero_ht_num_bytes
2224 + zero_fec_num_bytes)
David Zeuthen1394f762019-04-30 10:20:11 -04002225 image.seek(zero_end_offset)
2226 data = image.read(image.image_size - zero_end_offset)
2227
2228 # Write zeroes all over hashtree and FEC, except for the first eight bytes
2229 # where a magic marker - ZeroHaSH - is placed. Place these markers in the
2230 # beginning of both hashtree and FEC. (That way, in the future we can add
2231 # options to 'avbtool zero_hashtree' so as to zero out only either/or.)
2232 #
2233 # Applications can use these markers to detect that the hashtree and/or
2234 # FEC needs to be recomputed.
2235 image.truncate(zero_ht_start_offset)
2236 data_zeroed_firstblock = 'ZeRoHaSH' + '\0'*(image.block_size - 8)
2237 image.append_raw(data_zeroed_firstblock)
2238 image.append_fill('\0\0\0\0', zero_ht_num_bytes - image.block_size)
2239 if zero_fec_start_offset:
2240 image.append_raw(data_zeroed_firstblock)
2241 image.append_fill('\0\0\0\0', zero_fec_num_bytes - image.block_size)
2242 image.append_raw(data)
2243
David Zeuthen2bc232b2017-04-19 14:25:19 -04002244 def resize_image(self, image_filename, partition_size):
2245 """Implements the 'resize_image' command.
2246
2247 Arguments:
2248 image_filename: File with footer to resize.
2249 partition_size: The new size of the image.
2250
2251 Raises:
2252 AvbError: If there's no footer in the image.
2253 """
2254
2255 image = ImageHandler(image_filename)
2256
2257 if partition_size % image.block_size != 0:
2258 raise AvbError('Partition size of {} is not a multiple of the image '
2259 'block size {}.'.format(partition_size,
2260 image.block_size))
2261
Jan Monsch77cd2022019-12-10 17:18:04 +01002262 (footer, _, _, _) = self._parse_image(image)
David Zeuthen2bc232b2017-04-19 14:25:19 -04002263
2264 if not footer:
2265 raise AvbError('Given image does not have a footer.')
2266
2267 # The vbmeta blob is always at the end of the data so resizing an
2268 # image amounts to just moving the footer around.
2269
2270 vbmeta_end_offset = footer.vbmeta_offset + footer.vbmeta_size
2271 if vbmeta_end_offset % image.block_size != 0:
Jan Monscheeb28b62019-12-05 16:17:09 +01002272 vbmeta_end_offset += image.block_size - (vbmeta_end_offset
2273 % image.block_size)
David Zeuthen2bc232b2017-04-19 14:25:19 -04002274
2275 if partition_size < vbmeta_end_offset + 1*image.block_size:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07002276 raise AvbError('Requested size of {} is too small for an image '
2277 'of size {}.'
2278 .format(partition_size,
2279 vbmeta_end_offset + 1*image.block_size))
David Zeuthen2bc232b2017-04-19 14:25:19 -04002280
2281 # Cut at the end of the vbmeta blob and insert a DONT_CARE chunk
2282 # with enough bytes such that the final Footer block is at the end
2283 # of partition_size.
2284 image.truncate(vbmeta_end_offset)
2285 image.append_dont_care(partition_size - vbmeta_end_offset -
2286 1*image.block_size)
2287
2288 # Just reuse the same footer - only difference is that we're
2289 # writing it in a different place.
2290 footer_blob = footer.encode()
2291 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
2292 footer_blob)
2293 image.append_raw(footer_blob_with_padding)
2294
David Zeuthen8b6973b2016-09-20 12:39:49 -04002295 def set_ab_metadata(self, misc_image, slot_data):
2296 """Implements the 'set_ab_metadata' command.
2297
2298 The |slot_data| argument must be of the form 'A_priority:A_tries_remaining:
2299 A_successful_boot:B_priority:B_tries_remaining:B_successful_boot'.
2300
2301 Arguments:
2302 misc_image: The misc image to write to.
2303 slot_data: Slot data as a string
2304
2305 Raises:
2306 AvbError: If slot data is malformed.
2307 """
2308 tokens = slot_data.split(':')
2309 if len(tokens) != 6:
2310 raise AvbError('Malformed slot data "{}".'.format(slot_data))
2311 a_priority = int(tokens[0])
2312 a_tries_remaining = int(tokens[1])
2313 a_success = True if int(tokens[2]) != 0 else False
2314 b_priority = int(tokens[3])
2315 b_tries_remaining = int(tokens[4])
2316 b_success = True if int(tokens[5]) != 0 else False
2317
2318 ab_data_no_crc = struct.pack(self.AB_FORMAT_NO_CRC,
2319 self.AB_MAGIC,
2320 self.AB_MAJOR_VERSION, self.AB_MINOR_VERSION,
2321 a_priority, a_tries_remaining, a_success,
2322 b_priority, b_tries_remaining, b_success)
2323 # Force CRC to be unsigned, see https://bugs.python.org/issue4903 for why.
2324 crc_value = binascii.crc32(ab_data_no_crc) & 0xffffffff
2325 ab_data = ab_data_no_crc + struct.pack('!I', crc_value)
2326 misc_image.seek(self.AB_MISC_METADATA_OFFSET)
2327 misc_image.write(ab_data)
2328
David Zeuthena4fee8b2016-08-22 15:20:43 -04002329 def info_image(self, image_filename, output):
David Zeuthen21e95262016-07-27 17:58:40 -04002330 """Implements the 'info_image' command.
2331
2332 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002333 image_filename: Image file to get information from (file object).
David Zeuthen21e95262016-07-27 17:58:40 -04002334 output: Output file to write human-readable information to (file object).
2335 """
2336
David Zeuthena4fee8b2016-08-22 15:20:43 -04002337 image = ImageHandler(image_filename)
2338
David Zeuthen21e95262016-07-27 17:58:40 -04002339 o = output
2340
2341 (footer, header, descriptors, image_size) = self._parse_image(image)
2342
Bowgo Tsaid7145942020-03-20 17:03:51 +08002343 # To show the SHA1 of the public key.
2344 vbmeta_blob = self._load_vbmeta_blob(image)
2345 key_offset = (header.SIZE +
2346 header.authentication_data_block_size +
2347 header.public_key_offset)
2348 key_blob = vbmeta_blob[key_offset:key_offset + header.public_key_size]
2349
David Zeuthen21e95262016-07-27 17:58:40 -04002350 if footer:
2351 o.write('Footer version: {}.{}\n'.format(footer.version_major,
2352 footer.version_minor))
2353 o.write('Image size: {} bytes\n'.format(image_size))
2354 o.write('Original image size: {} bytes\n'.format(
2355 footer.original_image_size))
2356 o.write('VBMeta offset: {}\n'.format(footer.vbmeta_offset))
2357 o.write('VBMeta size: {} bytes\n'.format(footer.vbmeta_size))
2358 o.write('--\n')
2359
2360 (alg_name, _) = lookup_algorithm_by_type(header.algorithm_type)
2361
David Zeuthene3cadca2017-02-22 21:25:46 -05002362 o.write('Minimum libavb version: {}.{}{}\n'.format(
2363 header.required_libavb_version_major,
2364 header.required_libavb_version_minor,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002365 ' (Sparse)' if image.is_sparse else ''))
David Zeuthen21e95262016-07-27 17:58:40 -04002366 o.write('Header Block: {} bytes\n'.format(AvbVBMetaHeader.SIZE))
2367 o.write('Authentication Block: {} bytes\n'.format(
2368 header.authentication_data_block_size))
2369 o.write('Auxiliary Block: {} bytes\n'.format(
2370 header.auxiliary_data_block_size))
Bowgo Tsaid7145942020-03-20 17:03:51 +08002371 if key_blob:
2372 hexdig = hashlib.sha1(key_blob).hexdigest()
2373 o.write('Public key (sha1): {}\n'.format(hexdig))
David Zeuthen21e95262016-07-27 17:58:40 -04002374 o.write('Algorithm: {}\n'.format(alg_name))
2375 o.write('Rollback Index: {}\n'.format(header.rollback_index))
David Zeuthenfd41eb92016-11-17 12:24:47 -05002376 o.write('Flags: {}\n'.format(header.flags))
David Zeuthene3cadca2017-02-22 21:25:46 -05002377 o.write('Release String: \'{}\'\n'.format(
2378 header.release_string.rstrip('\0')))
David Zeuthen21e95262016-07-27 17:58:40 -04002379
2380 # Print descriptors.
2381 num_printed = 0
2382 o.write('Descriptors:\n')
2383 for desc in descriptors:
2384 desc.print_desc(o)
2385 num_printed += 1
2386 if num_printed == 0:
2387 o.write(' (none)\n')
2388
Jan Monscheeb28b62019-12-05 16:17:09 +01002389 def verify_image(self, image_filename, key_path, expected_chain_partitions,
2390 follow_chain_partitions, accept_zeroed_hashtree):
David Zeuthenb623d8b2017-04-04 16:05:53 -04002391 """Implements the 'verify_image' command.
2392
2393 Arguments:
2394 image_filename: Image file to get information from (file object).
Jan Monscheeb28b62019-12-05 16:17:09 +01002395 key_path: None or check that embedded public key matches key at given
2396 path.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002397 expected_chain_partitions: List of chain partitions to check or None.
Jan Monscheeb28b62019-12-05 16:17:09 +01002398 follow_chain_partitions:
2399 If True, will follows chain partitions even when not specified with
2400 the --expected_chain_partition option
2401 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
2402 zeroed out.
Jan Monsch77cd2022019-12-10 17:18:04 +01002403
2404 Raises:
2405 AvbError: If verification of the image fails.
David Zeuthenb623d8b2017-04-04 16:05:53 -04002406 """
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002407 expected_chain_partitions_map = {}
2408 if expected_chain_partitions:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002409 for cp in expected_chain_partitions:
2410 cp_tokens = cp.split(':')
2411 if len(cp_tokens) != 3:
2412 raise AvbError('Malformed chained partition "{}".'.format(cp))
2413 partition_name = cp_tokens[0]
2414 rollback_index_location = int(cp_tokens[1])
2415 file_path = cp_tokens[2]
2416 pk_blob = open(file_path).read()
Jan Monscheeb28b62019-12-05 16:17:09 +01002417 expected_chain_partitions_map[partition_name] = (
2418 rollback_index_location, pk_blob)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002419
2420 image_dir = os.path.dirname(image_filename)
2421 image_ext = os.path.splitext(image_filename)[1]
2422
2423 key_blob = None
2424 if key_path:
Jan Monsch23e0c622019-12-11 11:23:58 +01002425 print('Verifying image {} using key at {}'.format(image_filename,
2426 key_path))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002427 key_blob = encode_rsa_key(key_path)
2428 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01002429 print('Verifying image {} using embedded public key'.format(
2430 image_filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002431
David Zeuthenb623d8b2017-04-04 16:05:53 -04002432 image = ImageHandler(image_filename)
Jan Monsch77cd2022019-12-10 17:18:04 +01002433 (footer, header, descriptors, _) = self._parse_image(image)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002434 offset = 0
2435 if footer:
2436 offset = footer.vbmeta_offset
David Zeuthen49936b42018-08-07 17:38:58 -04002437
David Zeuthenb623d8b2017-04-04 16:05:53 -04002438 image.seek(offset)
Jan Monscheeb28b62019-12-05 16:17:09 +01002439 vbmeta_blob = image.read(header.SIZE
2440 + header.authentication_data_block_size
2441 + header.auxiliary_data_block_size)
David Zeuthen49936b42018-08-07 17:38:58 -04002442
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002443 alg_name, _ = lookup_algorithm_by_type(header.algorithm_type)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002444 if not verify_vbmeta_signature(header, vbmeta_blob):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002445 raise AvbError('Signature check failed for {} vbmeta struct {}'
2446 .format(alg_name, image_filename))
2447
2448 if key_blob:
2449 # The embedded public key is in the auxiliary block at an offset.
2450 key_offset = AvbVBMetaHeader.SIZE
David Zeuthen49936b42018-08-07 17:38:58 -04002451 key_offset += header.authentication_data_block_size
2452 key_offset += header.public_key_offset
Jan Monscheeb28b62019-12-05 16:17:09 +01002453 key_blob_in_vbmeta = vbmeta_blob[key_offset:key_offset
2454 + header.public_key_size]
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002455 if key_blob != key_blob_in_vbmeta:
2456 raise AvbError('Embedded public key does not match given key.')
2457
2458 if footer:
Jan Monsch23e0c622019-12-11 11:23:58 +01002459 print('vbmeta: Successfully verified footer and {} vbmeta struct in {}'
2460 .format(alg_name, image.filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002461 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01002462 print('vbmeta: Successfully verified {} vbmeta struct in {}'
2463 .format(alg_name, image.filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002464
2465 for desc in descriptors:
Jan Monscheeb28b62019-12-05 16:17:09 +01002466 if (isinstance(desc, AvbChainPartitionDescriptor)
2467 and follow_chain_partitions
Jan Monschfe00c0a2019-12-11 11:19:40 +01002468 and expected_chain_partitions_map.get(desc.partition_name) is None):
David Zeuthene947cb62019-01-25 15:27:08 -05002469 # In this case we're processing a chain descriptor but don't have a
2470 # --expect_chain_partition ... however --follow_chain_partitions was
2471 # specified so we shouldn't error out in desc.verify().
Jan Monsch23e0c622019-12-11 11:23:58 +01002472 print('{}: Chained but ROLLBACK_SLOT (which is {}) '
2473 'and KEY (which has sha1 {}) not specified'
2474 .format(desc.partition_name, desc.rollback_index_location,
2475 hashlib.sha1(desc.public_key).hexdigest()))
2476 elif not desc.verify(image_dir, image_ext, expected_chain_partitions_map,
Jan Monscheeb28b62019-12-05 16:17:09 +01002477 image, accept_zeroed_hashtree):
Jan Monsch23e0c622019-12-11 11:23:58 +01002478 raise AvbError('Error verifying descriptor.')
Jan Monscheeb28b62019-12-05 16:17:09 +01002479 # Honor --follow_chain_partitions - add '--' to make the output more
2480 # readable.
2481 if (isinstance(desc, AvbChainPartitionDescriptor)
2482 and follow_chain_partitions):
Jan Monsch23e0c622019-12-11 11:23:58 +01002483 print('--')
Jan Monscheeb28b62019-12-05 16:17:09 +01002484 chained_image_filename = os.path.join(image_dir,
2485 desc.partition_name + image_ext)
2486 self.verify_image(chained_image_filename, key_path, None, False,
2487 accept_zeroed_hashtree)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002488
David Zeuthenb8643c02018-05-17 17:21:18 -04002489 def calculate_vbmeta_digest(self, image_filename, hash_algorithm, output):
2490 """Implements the 'calculate_vbmeta_digest' command.
2491
2492 Arguments:
2493 image_filename: Image file to get information from (file object).
2494 hash_algorithm: Hash algorithm used.
2495 output: Output file to write human-readable information to (file object).
2496 """
2497
2498 image_dir = os.path.dirname(image_filename)
2499 image_ext = os.path.splitext(image_filename)[1]
2500
2501 image = ImageHandler(image_filename)
Jan Monsch77cd2022019-12-10 17:18:04 +01002502 (footer, header, descriptors, _) = self._parse_image(image)
David Zeuthenb8643c02018-05-17 17:21:18 -04002503 offset = 0
2504 if footer:
2505 offset = footer.vbmeta_offset
2506 size = (header.SIZE + header.authentication_data_block_size +
2507 header.auxiliary_data_block_size)
2508 image.seek(offset)
2509 vbmeta_blob = image.read(size)
2510
2511 hasher = hashlib.new(name=hash_algorithm)
2512 hasher.update(vbmeta_blob)
2513
2514 for desc in descriptors:
2515 if isinstance(desc, AvbChainPartitionDescriptor):
Jan Monscheeb28b62019-12-05 16:17:09 +01002516 ch_image_filename = os.path.join(image_dir,
2517 desc.partition_name + image_ext)
David Zeuthenb8643c02018-05-17 17:21:18 -04002518 ch_image = ImageHandler(ch_image_filename)
Jan Monsch77cd2022019-12-10 17:18:04 +01002519 (ch_footer, ch_header, _, _) = self._parse_image(ch_image)
David Zeuthenb8643c02018-05-17 17:21:18 -04002520 ch_offset = 0
David Zeuthen49936b42018-08-07 17:38:58 -04002521 ch_size = (ch_header.SIZE + ch_header.authentication_data_block_size +
2522 ch_header.auxiliary_data_block_size)
David Zeuthenb8643c02018-05-17 17:21:18 -04002523 if ch_footer:
2524 ch_offset = ch_footer.vbmeta_offset
David Zeuthenb8643c02018-05-17 17:21:18 -04002525 ch_image.seek(ch_offset)
2526 ch_vbmeta_blob = ch_image.read(ch_size)
2527 hasher.update(ch_vbmeta_blob)
2528
2529 digest = hasher.digest()
Jan Monsch23e0c622019-12-11 11:23:58 +01002530 output.write('{}\n'.format(binascii.hexlify(digest)))
David Zeuthenb8643c02018-05-17 17:21:18 -04002531
David Zeuthenf7d2e752018-09-20 13:30:41 -04002532 def calculate_kernel_cmdline(self, image_filename, hashtree_disabled, output):
2533 """Implements the 'calculate_kernel_cmdline' command.
2534
2535 Arguments:
2536 image_filename: Image file to get information from (file object).
2537 hashtree_disabled: If True, returns the cmdline for hashtree disabled.
2538 output: Output file to write human-readable information to (file object).
2539 """
2540
2541 image = ImageHandler(image_filename)
2542 _, _, descriptors, _ = self._parse_image(image)
2543
2544 image_dir = os.path.dirname(image_filename)
2545 image_ext = os.path.splitext(image_filename)[1]
2546
2547 cmdline_descriptors = []
2548 for desc in descriptors:
2549 if isinstance(desc, AvbChainPartitionDescriptor):
Jan Monscheeb28b62019-12-05 16:17:09 +01002550 ch_image_filename = os.path.join(image_dir,
2551 desc.partition_name + image_ext)
David Zeuthenf7d2e752018-09-20 13:30:41 -04002552 ch_image = ImageHandler(ch_image_filename)
2553 _, _, ch_descriptors, _ = self._parse_image(ch_image)
2554 for ch_desc in ch_descriptors:
2555 if isinstance(ch_desc, AvbKernelCmdlineDescriptor):
2556 cmdline_descriptors.append(ch_desc)
2557 elif isinstance(desc, AvbKernelCmdlineDescriptor):
2558 cmdline_descriptors.append(desc)
2559
2560 kernel_cmdline_snippets = []
2561 for desc in cmdline_descriptors:
2562 use_cmdline = True
Jan Monscheeb28b62019-12-05 16:17:09 +01002563 if ((desc.flags &
2564 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
2565 != 0):
David Zeuthenf7d2e752018-09-20 13:30:41 -04002566 if hashtree_disabled:
2567 use_cmdline = False
Jan Monscheeb28b62019-12-05 16:17:09 +01002568 if (desc.flags &
2569 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED) != 0:
David Zeuthenf7d2e752018-09-20 13:30:41 -04002570 if not hashtree_disabled:
2571 use_cmdline = False
2572 if use_cmdline:
2573 kernel_cmdline_snippets.append(desc.kernel_cmdline)
2574 output.write(' '.join(kernel_cmdline_snippets))
2575
David Zeuthen21e95262016-07-27 17:58:40 -04002576 def _parse_image(self, image):
2577 """Gets information about an image.
2578
2579 The image can either be a vbmeta or an image with a footer.
2580
2581 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002582 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002583
2584 Returns:
2585 A tuple where the first argument is a AvbFooter (None if there
2586 is no footer on the image), the second argument is a
2587 AvbVBMetaHeader, the third argument is a list of
2588 AvbDescriptor-derived instances, and the fourth argument is the
2589 size of |image|.
Jan Monsch443bf322020-02-19 14:56:44 +01002590
2591 Raises:
2592 AvbError: In case the image cannot be parsed.
David Zeuthen21e95262016-07-27 17:58:40 -04002593 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002594 assert isinstance(image, ImageHandler)
David Zeuthen21e95262016-07-27 17:58:40 -04002595 footer = None
David Zeuthen09692692016-09-30 16:16:40 -04002596 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002597 try:
2598 footer = AvbFooter(image.read(AvbFooter.SIZE))
2599 except (LookupError, struct.error):
2600 # Nope, just seek back to the start.
2601 image.seek(0)
2602
2603 vbmeta_offset = 0
2604 if footer:
2605 vbmeta_offset = footer.vbmeta_offset
2606
2607 image.seek(vbmeta_offset)
2608 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2609
2610 auth_block_offset = vbmeta_offset + AvbVBMetaHeader.SIZE
2611 aux_block_offset = auth_block_offset + h.authentication_data_block_size
2612 desc_start_offset = aux_block_offset + h.descriptors_offset
2613 image.seek(desc_start_offset)
2614 descriptors = parse_descriptors(image.read(h.descriptors_size))
2615
David Zeuthen09692692016-09-30 16:16:40 -04002616 return footer, h, descriptors, image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002617
David Zeuthenb1b994d2017-03-06 18:01:31 -05002618 def _load_vbmeta_blob(self, image):
2619 """Gets the vbmeta struct and associated sections.
2620
2621 The image can either be a vbmeta.img or an image with a footer.
2622
2623 Arguments:
2624 image: An ImageHandler (vbmeta or footer).
2625
2626 Returns:
2627 A blob with the vbmeta struct and other sections.
2628 """
2629 assert isinstance(image, ImageHandler)
2630 footer = None
2631 image.seek(image.image_size - AvbFooter.SIZE)
2632 try:
2633 footer = AvbFooter(image.read(AvbFooter.SIZE))
2634 except (LookupError, struct.error):
2635 # Nope, just seek back to the start.
2636 image.seek(0)
2637
2638 vbmeta_offset = 0
2639 if footer:
2640 vbmeta_offset = footer.vbmeta_offset
2641
2642 image.seek(vbmeta_offset)
2643 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2644
2645 image.seek(vbmeta_offset)
2646 data_size = AvbVBMetaHeader.SIZE
2647 data_size += h.authentication_data_block_size
2648 data_size += h.auxiliary_data_block_size
2649 return image.read(data_size)
2650
David Zeuthen73f2afa2017-05-17 16:54:11 -04002651 def _get_cmdline_descriptors_for_hashtree_descriptor(self, ht):
David Zeuthenfd41eb92016-11-17 12:24:47 -05002652 """Generate kernel cmdline descriptors for dm-verity.
David Zeuthen21e95262016-07-27 17:58:40 -04002653
2654 Arguments:
David Zeuthen73f2afa2017-05-17 16:54:11 -04002655 ht: A AvbHashtreeDescriptor
David Zeuthen21e95262016-07-27 17:58:40 -04002656
2657 Returns:
David Zeuthenfd41eb92016-11-17 12:24:47 -05002658 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2659 instructions. There is one for when hashtree is not disabled and one for
2660 when it is.
David Zeuthen21e95262016-07-27 17:58:40 -04002661
David Zeuthen21e95262016-07-27 17:58:40 -04002662 """
2663
David Zeuthen21e95262016-07-27 17:58:40 -04002664 c = 'dm="1 vroot none ro 1,'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002665 c += '0' # start
Jan Monsch23e0c622019-12-11 11:23:58 +01002666 c += ' {}'.format((ht.image_size // 512)) # size (# sectors)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002667 c += ' verity {}'.format(ht.dm_verity_version) # type and version
2668 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # data_dev
2669 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # hash_dev
2670 c += ' {}'.format(ht.data_block_size) # data_block
2671 c += ' {}'.format(ht.hash_block_size) # hash_block
Jan Monsch23e0c622019-12-11 11:23:58 +01002672 c += ' {}'.format(ht.image_size // ht.data_block_size) # #blocks
2673 c += ' {}'.format(ht.image_size // ht.data_block_size) # hash_offset
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002674 c += ' {}'.format(ht.hash_algorithm) # hash_alg
2675 c += ' {}'.format(str(ht.root_digest).encode('hex')) # root_digest
2676 c += ' {}'.format(str(ht.salt).encode('hex')) # salt
2677 if ht.fec_num_roots > 0:
David Zeuthena01e32f2017-01-24 17:32:38 -05002678 c += ' 10' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04002679 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002680 c += ' ignore_zero_blocks'
2681 c += ' use_fec_from_device PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2682 c += ' fec_roots {}'.format(ht.fec_num_roots)
2683 # Note that fec_blocks is the size that FEC covers, *not* the
2684 # size of the FEC data. Since we use FEC for everything up until
2685 # the FEC data, it's the same as the offset.
Jan Monsch23e0c622019-12-11 11:23:58 +01002686 c += ' fec_blocks {}'.format(ht.fec_offset // ht.data_block_size)
2687 c += ' fec_start {}'.format(ht.fec_offset // ht.data_block_size)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002688 else:
David Zeuthena01e32f2017-01-24 17:32:38 -05002689 c += ' 2' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04002690 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002691 c += ' ignore_zero_blocks'
David Zeuthenfd9c18d2017-03-20 18:19:30 -04002692 c += '" root=/dev/dm-0'
David Zeuthen21e95262016-07-27 17:58:40 -04002693
David Zeuthenfd41eb92016-11-17 12:24:47 -05002694 # Now that we have the command-line, generate the descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002695 desc = AvbKernelCmdlineDescriptor()
2696 desc.kernel_cmdline = c
David Zeuthenfd41eb92016-11-17 12:24:47 -05002697 desc.flags = (
2698 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
2699
2700 # The descriptor for when hashtree verification is disabled is a lot
2701 # simpler - we just set the root to the partition.
2702 desc_no_ht = AvbKernelCmdlineDescriptor()
2703 desc_no_ht.kernel_cmdline = 'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2704 desc_no_ht.flags = (
2705 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED)
2706
2707 return [desc, desc_no_ht]
David Zeuthen21e95262016-07-27 17:58:40 -04002708
David Zeuthen73f2afa2017-05-17 16:54:11 -04002709 def _get_cmdline_descriptors_for_dm_verity(self, image):
2710 """Generate kernel cmdline descriptors for dm-verity.
2711
2712 Arguments:
2713 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
2714
2715 Returns:
2716 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2717 instructions. There is one for when hashtree is not disabled and one for
2718 when it is.
2719
2720 Raises:
2721 AvbError: If |image| doesn't have a hashtree descriptor.
2722
2723 """
2724
2725 (_, _, descriptors, _) = self._parse_image(image)
2726
2727 ht = None
2728 for desc in descriptors:
2729 if isinstance(desc, AvbHashtreeDescriptor):
2730 ht = desc
2731 break
2732
2733 if not ht:
2734 raise AvbError('No hashtree descriptor in given image')
2735
2736 return self._get_cmdline_descriptors_for_hashtree_descriptor(ht)
2737
David Zeuthen21e95262016-07-27 17:58:40 -04002738 def make_vbmeta_image(self, output, chain_partitions, algorithm_name,
David Zeuthen18666ab2016-11-15 11:18:05 -05002739 key_path, public_key_metadata_path, rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002740 flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002741 setup_rootfs_from_kernel,
David Zeuthena156d3d2017-06-01 12:08:09 -04002742 include_descriptors_from_image,
2743 signing_helper,
2744 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05002745 release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04002746 append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04002747 print_required_libavb_version,
2748 padding_size):
David Zeuthen21e95262016-07-27 17:58:40 -04002749 """Implements the 'make_vbmeta_image' command.
2750
2751 Arguments:
2752 output: File to write the image to.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002753 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002754 algorithm_name: Name of algorithm to use.
2755 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002756 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002757 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002758 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002759 props: Properties to insert (list of strings of the form 'key:value').
2760 props_from_file: Properties to insert (list of strings 'key:<path>').
2761 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002762 setup_rootfs_from_kernel: None or file to generate from.
David Zeuthen21e95262016-07-27 17:58:40 -04002763 include_descriptors_from_image: List of file objects with descriptors.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002764 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04002765 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05002766 release_string: None or avbtool release string to use instead of default.
2767 append_to_release_string: None or string to append.
David Zeuthen1097a782017-05-31 15:53:17 -04002768 print_required_libavb_version: True to only print required libavb version.
David Zeuthen97cb5802017-06-01 16:14:05 -04002769 padding_size: If not 0, pads output so size is a multiple of the number.
David Zeuthen21e95262016-07-27 17:58:40 -04002770
2771 Raises:
2772 AvbError: If a chained partition is malformed.
2773 """
2774
David Zeuthen1097a782017-05-31 15:53:17 -04002775 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04002776 if print_required_libavb_version:
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002777 if include_descriptors_from_image:
2778 # Use the bump logic in AvbVBMetaHeader to calculate the max required
2779 # version of all included descriptors.
2780 tmp_header = AvbVBMetaHeader()
2781 for image in include_descriptors_from_image:
2782 (_, image_header, _, _) = self._parse_image(ImageHandler(image.name))
2783 tmp_header.bump_required_libavb_version_minor(
2784 image_header.required_libavb_version_minor)
Jan Monsch23e0c622019-12-11 11:23:58 +01002785 print('1.{}'.format(tmp_header.required_libavb_version_minor))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002786 else:
2787 # Descriptors aside, all vbmeta features are supported in 1.0.
Jan Monsch23e0c622019-12-11 11:23:58 +01002788 print('1.0')
David Zeuthen1097a782017-05-31 15:53:17 -04002789 return
2790
2791 if not output:
2792 raise AvbError('No output file given')
2793
David Zeuthen21e95262016-07-27 17:58:40 -04002794 descriptors = []
David Zeuthen73f2afa2017-05-17 16:54:11 -04002795 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04002796 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002797 algorithm_name, key_path, public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002798 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002799 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04002800 include_descriptors_from_image, signing_helper,
2801 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002802 append_to_release_string, 0)
David Zeuthen21e95262016-07-27 17:58:40 -04002803
2804 # Write entire vbmeta blob (header, authentication, auxiliary).
2805 output.seek(0)
2806 output.write(vbmeta_blob)
2807
David Zeuthen97cb5802017-06-01 16:14:05 -04002808 if padding_size > 0:
2809 padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
2810 padding_needed = padded_size - len(vbmeta_blob)
2811 output.write('\0' * padding_needed)
2812
David Zeuthen18666ab2016-11-15 11:18:05 -05002813 def _generate_vbmeta_blob(self, algorithm_name, key_path,
2814 public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002815 chain_partitions,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002816 rollback_index, flags, props, props_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04002817 kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002818 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002819 ht_desc_to_setup,
David Zeuthene3cadca2017-02-22 21:25:46 -05002820 include_descriptors_from_image, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04002821 signing_helper_with_files,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002822 release_string, append_to_release_string,
2823 required_libavb_version_minor):
David Zeuthen21e95262016-07-27 17:58:40 -04002824 """Generates a VBMeta blob.
2825
2826 This blob contains the header (struct AvbVBMetaHeader), the
2827 authentication data block (which contains the hash and signature
2828 for the header and auxiliary block), and the auxiliary block
2829 (which contains descriptors, the public key used, and other data).
2830
2831 The |key| parameter can |None| only if the |algorithm_name| is
2832 'NONE'.
2833
2834 Arguments:
2835 algorithm_name: The algorithm name as per the ALGORITHMS dict.
2836 key_path: The path to the .pem file used to sign the blob.
David Zeuthen18666ab2016-11-15 11:18:05 -05002837 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002838 descriptors: A list of descriptors to insert or None.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002839 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002840 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002841 flags: Flags to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002842 props: Properties to insert (List of strings of the form 'key:value').
2843 props_from_file: Properties to insert (List of strings 'key:<path>').
2844 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002845 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002846 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04002847 ht_desc_to_setup: If not None, an AvbHashtreeDescriptor to
2848 generate dm-verity kernel cmdline descriptors from.
David Zeuthen21e95262016-07-27 17:58:40 -04002849 include_descriptors_from_image: List of file objects for which
2850 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002851 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04002852 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05002853 release_string: None or avbtool release string.
2854 append_to_release_string: None or string to append.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002855 required_libavb_version_minor: Use at least this required minor version.
David Zeuthen21e95262016-07-27 17:58:40 -04002856
2857 Returns:
2858 A bytearray() with the VBMeta blob.
2859
2860 Raises:
2861 Exception: If the |algorithm_name| is not found, if no key has
2862 been given and the given algorithm requires one, or the key is
2863 of the wrong size.
2864
2865 """
2866 try:
2867 alg = ALGORITHMS[algorithm_name]
2868 except KeyError:
2869 raise AvbError('Unknown algorithm with name {}'.format(algorithm_name))
2870
David Zeuthena5fd3a42017-02-27 16:38:54 -05002871 if not descriptors:
2872 descriptors = []
2873
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002874 h = AvbVBMetaHeader()
2875 h.bump_required_libavb_version_minor(required_libavb_version_minor)
2876
David Zeuthena5fd3a42017-02-27 16:38:54 -05002877 # Insert chained partition descriptors, if any
2878 if chain_partitions:
David Zeuthend8e48582017-04-21 11:31:51 -04002879 used_locations = {}
David Zeuthena5fd3a42017-02-27 16:38:54 -05002880 for cp in chain_partitions:
2881 cp_tokens = cp.split(':')
2882 if len(cp_tokens) != 3:
2883 raise AvbError('Malformed chained partition "{}".'.format(cp))
David Zeuthend8e48582017-04-21 11:31:51 -04002884 partition_name = cp_tokens[0]
2885 rollback_index_location = int(cp_tokens[1])
2886 file_path = cp_tokens[2]
2887 # Check that the same rollback location isn't being used by
2888 # multiple chained partitions.
2889 if used_locations.get(rollback_index_location):
2890 raise AvbError('Rollback Index Location {} is already in use.'.format(
2891 rollback_index_location))
2892 used_locations[rollback_index_location] = True
David Zeuthena5fd3a42017-02-27 16:38:54 -05002893 desc = AvbChainPartitionDescriptor()
David Zeuthend8e48582017-04-21 11:31:51 -04002894 desc.partition_name = partition_name
2895 desc.rollback_index_location = rollback_index_location
David Zeuthena5fd3a42017-02-27 16:38:54 -05002896 if desc.rollback_index_location < 1:
2897 raise AvbError('Rollback index location must be 1 or larger.')
David Zeuthena5fd3a42017-02-27 16:38:54 -05002898 desc.public_key = open(file_path, 'rb').read()
2899 descriptors.append(desc)
2900
David Zeuthen21e95262016-07-27 17:58:40 -04002901 # Descriptors.
2902 encoded_descriptors = bytearray()
David Zeuthena5fd3a42017-02-27 16:38:54 -05002903 for desc in descriptors:
2904 encoded_descriptors.extend(desc.encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002905
2906 # Add properties.
2907 if props:
2908 for prop in props:
2909 idx = prop.find(':')
2910 if idx == -1:
2911 raise AvbError('Malformed property "{}".'.format(prop))
Jan Monsch23e0c622019-12-11 11:23:58 +01002912 # pylint: disable=redefined-variable-type
David Zeuthen21e95262016-07-27 17:58:40 -04002913 desc = AvbPropertyDescriptor()
2914 desc.key = prop[0:idx]
2915 desc.value = prop[(idx + 1):]
2916 encoded_descriptors.extend(desc.encode())
2917 if props_from_file:
2918 for prop in props_from_file:
2919 idx = prop.find(':')
2920 if idx == -1:
2921 raise AvbError('Malformed property "{}".'.format(prop))
2922 desc = AvbPropertyDescriptor()
2923 desc.key = prop[0:idx]
2924 desc.value = prop[(idx + 1):]
2925 file_path = prop[(idx + 1):]
2926 desc.value = open(file_path, 'rb').read()
2927 encoded_descriptors.extend(desc.encode())
2928
David Zeuthen73f2afa2017-05-17 16:54:11 -04002929 # Add AvbKernelCmdline descriptor for dm-verity from an image, if requested.
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002930 if setup_rootfs_from_kernel:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002931 image_handler = ImageHandler(
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002932 setup_rootfs_from_kernel.name)
David Zeuthenfd41eb92016-11-17 12:24:47 -05002933 cmdline_desc = self._get_cmdline_descriptors_for_dm_verity(image_handler)
2934 encoded_descriptors.extend(cmdline_desc[0].encode())
2935 encoded_descriptors.extend(cmdline_desc[1].encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002936
David Zeuthen73f2afa2017-05-17 16:54:11 -04002937 # Add AvbKernelCmdline descriptor for dm-verity from desc, if requested.
2938 if ht_desc_to_setup:
2939 cmdline_desc = self._get_cmdline_descriptors_for_hashtree_descriptor(
2940 ht_desc_to_setup)
2941 encoded_descriptors.extend(cmdline_desc[0].encode())
2942 encoded_descriptors.extend(cmdline_desc[1].encode())
2943
David Zeuthen21e95262016-07-27 17:58:40 -04002944 # Add kernel command-lines.
2945 if kernel_cmdlines:
2946 for i in kernel_cmdlines:
2947 desc = AvbKernelCmdlineDescriptor()
2948 desc.kernel_cmdline = i
2949 encoded_descriptors.extend(desc.encode())
2950
2951 # Add descriptors from other images.
2952 if include_descriptors_from_image:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07002953 descriptors_dict = dict()
David Zeuthen21e95262016-07-27 17:58:40 -04002954 for image in include_descriptors_from_image:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002955 image_handler = ImageHandler(image.name)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002956 (_, image_vbmeta_header, image_descriptors, _) = self._parse_image(
2957 image_handler)
2958 # Bump the required libavb version to support all included descriptors.
2959 h.bump_required_libavb_version_minor(
2960 image_vbmeta_header.required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04002961 for desc in image_descriptors:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07002962 # The --include_descriptors_from_image option is used in some setups
2963 # with images A and B where both A and B contain a descriptor
2964 # for a partition with the same name. Since it's not meaningful
2965 # to include both descriptors, only include the last seen descriptor.
2966 # See bug 76386656 for details.
2967 if hasattr(desc, 'partition_name'):
2968 key = type(desc).__name__ + '_' + desc.partition_name
2969 descriptors_dict[key] = desc.encode()
2970 else:
2971 encoded_descriptors.extend(desc.encode())
Jan Monschfe00c0a2019-12-11 11:19:40 +01002972 for key in sorted(descriptors_dict):
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07002973 encoded_descriptors.extend(descriptors_dict[key])
David Zeuthen21e95262016-07-27 17:58:40 -04002974
David Zeuthen18666ab2016-11-15 11:18:05 -05002975 # Load public key metadata blob, if requested.
2976 pkmd_blob = []
2977 if public_key_metadata_path:
2978 with open(public_key_metadata_path) as f:
2979 pkmd_blob = f.read()
2980
David Zeuthen21e95262016-07-27 17:58:40 -04002981 key = None
2982 encoded_key = bytearray()
2983 if alg.public_key_num_bytes > 0:
2984 if not key_path:
2985 raise AvbError('Key is required for algorithm {}'.format(
2986 algorithm_name))
David Zeuthenc68f0822017-03-31 17:22:35 -04002987 encoded_key = encode_rsa_key(key_path)
David Zeuthen21e95262016-07-27 17:58:40 -04002988 if len(encoded_key) != alg.public_key_num_bytes:
2989 raise AvbError('Key is wrong size for algorithm {}'.format(
2990 algorithm_name))
2991
David Zeuthene3cadca2017-02-22 21:25:46 -05002992 # Override release string, if requested.
Jan Monsch23e0c622019-12-11 11:23:58 +01002993 # pylint: disable=unicode-builtin
David Zeuthene3cadca2017-02-22 21:25:46 -05002994 if isinstance(release_string, (str, unicode)):
2995 h.release_string = release_string
2996
2997 # Append to release string, if requested. Also insert a space before.
2998 if isinstance(append_to_release_string, (str, unicode)):
2999 h.release_string += ' ' + append_to_release_string
3000
David Zeuthen18666ab2016-11-15 11:18:05 -05003001 # For the Auxiliary data block, descriptors are stored at offset 0,
3002 # followed by the public key, followed by the public key metadata blob.
David Zeuthen21e95262016-07-27 17:58:40 -04003003 h.auxiliary_data_block_size = round_to_multiple(
David Zeuthen18666ab2016-11-15 11:18:05 -05003004 len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64)
David Zeuthen21e95262016-07-27 17:58:40 -04003005 h.descriptors_offset = 0
3006 h.descriptors_size = len(encoded_descriptors)
3007 h.public_key_offset = h.descriptors_size
3008 h.public_key_size = len(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05003009 h.public_key_metadata_offset = h.public_key_offset + h.public_key_size
3010 h.public_key_metadata_size = len(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003011
3012 # For the Authentication data block, the hash is first and then
3013 # the signature.
3014 h.authentication_data_block_size = round_to_multiple(
David Zeuthend5db21d2017-01-24 10:11:38 -05003015 alg.hash_num_bytes + alg.signature_num_bytes, 64)
David Zeuthen21e95262016-07-27 17:58:40 -04003016 h.algorithm_type = alg.algorithm_type
3017 h.hash_offset = 0
3018 h.hash_size = alg.hash_num_bytes
3019 # Signature offset and size - it's stored right after the hash
3020 # (in Authentication data block).
3021 h.signature_offset = alg.hash_num_bytes
3022 h.signature_size = alg.signature_num_bytes
3023
3024 h.rollback_index = rollback_index
David Zeuthenfd41eb92016-11-17 12:24:47 -05003025 h.flags = flags
David Zeuthen21e95262016-07-27 17:58:40 -04003026
3027 # Generate Header data block.
3028 header_data_blob = h.encode()
3029
3030 # Generate Auxiliary data block.
3031 aux_data_blob = bytearray()
3032 aux_data_blob.extend(encoded_descriptors)
3033 aux_data_blob.extend(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05003034 aux_data_blob.extend(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003035 padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob)
3036 aux_data_blob.extend('\0' * padding_bytes)
3037
3038 # Calculate the hash.
3039 binary_hash = bytearray()
3040 binary_signature = bytearray()
3041 if algorithm_name != 'NONE':
David Zeuthenb623d8b2017-04-04 16:05:53 -04003042 ha = hashlib.new(alg.hash_name)
David Zeuthen21e95262016-07-27 17:58:40 -04003043 ha.update(header_data_blob)
3044 ha.update(aux_data_blob)
3045 binary_hash.extend(ha.digest())
3046
3047 # Calculate the signature.
David Zeuthen21e95262016-07-27 17:58:40 -04003048 padding_and_hash = str(bytearray(alg.padding)) + binary_hash
David Zeuthena156d3d2017-06-01 12:08:09 -04003049 binary_signature.extend(raw_sign(signing_helper,
3050 signing_helper_with_files,
3051 algorithm_name,
Esun Kimff44f232017-03-30 10:34:54 +09003052 alg.signature_num_bytes, key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003053 padding_and_hash))
David Zeuthen21e95262016-07-27 17:58:40 -04003054
3055 # Generate Authentication data block.
3056 auth_data_blob = bytearray()
3057 auth_data_blob.extend(binary_hash)
3058 auth_data_blob.extend(binary_signature)
3059 padding_bytes = h.authentication_data_block_size - len(auth_data_blob)
3060 auth_data_blob.extend('\0' * padding_bytes)
3061
3062 return header_data_blob + auth_data_blob + aux_data_blob
3063
3064 def extract_public_key(self, key_path, output):
3065 """Implements the 'extract_public_key' command.
3066
3067 Arguments:
3068 key_path: The path to a RSA private key file.
3069 output: The file to write to.
3070 """
David Zeuthenc68f0822017-03-31 17:22:35 -04003071 output.write(encode_rsa_key(key_path))
David Zeuthen21e95262016-07-27 17:58:40 -04003072
David Zeuthenb1b994d2017-03-06 18:01:31 -05003073 def append_vbmeta_image(self, image_filename, vbmeta_image_filename,
3074 partition_size):
3075 """Implementation of the append_vbmeta_image command.
3076
3077 Arguments:
3078 image_filename: File to add the footer to.
3079 vbmeta_image_filename: File to get vbmeta struct from.
3080 partition_size: Size of partition.
3081
3082 Raises:
3083 AvbError: If an argument is incorrect.
3084 """
3085 image = ImageHandler(image_filename)
3086
3087 if partition_size % image.block_size != 0:
3088 raise AvbError('Partition size of {} is not a multiple of the image '
3089 'block size {}.'.format(partition_size,
3090 image.block_size))
3091
3092 # If there's already a footer, truncate the image to its original
3093 # size. This way 'avbtool append_vbmeta_image' is idempotent.
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003094 if image.image_size >= AvbFooter.SIZE:
3095 image.seek(image.image_size - AvbFooter.SIZE)
3096 try:
3097 footer = AvbFooter(image.read(AvbFooter.SIZE))
3098 # Existing footer found. Just truncate.
3099 original_image_size = footer.original_image_size
3100 image.truncate(footer.original_image_size)
3101 except (LookupError, struct.error):
3102 original_image_size = image.image_size
3103 else:
3104 # Image size is too small to possibly contain a footer.
David Zeuthenb1b994d2017-03-06 18:01:31 -05003105 original_image_size = image.image_size
3106
3107 # If anything goes wrong from here-on, restore the image back to
3108 # its original size.
3109 try:
3110 vbmeta_image_handler = ImageHandler(vbmeta_image_filename)
3111 vbmeta_blob = self._load_vbmeta_blob(vbmeta_image_handler)
3112
3113 # If the image isn't sparse, its size might not be a multiple of
3114 # the block size. This will screw up padding later so just grow it.
3115 if image.image_size % image.block_size != 0:
3116 assert not image.is_sparse
3117 padding_needed = image.block_size - (image.image_size%image.block_size)
3118 image.truncate(image.image_size + padding_needed)
3119
3120 # The append_raw() method requires content with size being a
3121 # multiple of |block_size| so add padding as needed. Also record
3122 # where this is written to since we'll need to put that in the
3123 # footer.
3124 vbmeta_offset = image.image_size
3125 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
3126 len(vbmeta_blob))
3127 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
3128
3129 # Append vbmeta blob and footer
3130 image.append_raw(vbmeta_blob_with_padding)
3131 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
3132
3133 # Now insert a DONT_CARE chunk with enough bytes such that the
3134 # final Footer block is at the end of partition_size..
3135 image.append_dont_care(partition_size - vbmeta_end_offset -
3136 1*image.block_size)
3137
3138 # Generate the Footer that tells where the VBMeta footer
3139 # is. Also put enough padding in the front of the footer since
3140 # we'll write out an entire block.
3141 footer = AvbFooter()
3142 footer.original_image_size = original_image_size
3143 footer.vbmeta_offset = vbmeta_offset
3144 footer.vbmeta_size = len(vbmeta_blob)
3145 footer_blob = footer.encode()
3146 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
3147 footer_blob)
3148 image.append_raw(footer_blob_with_padding)
3149
3150 except:
3151 # Truncate back to original size, then re-raise
3152 image.truncate(original_image_size)
3153 raise
3154
David Zeuthena4fee8b2016-08-22 15:20:43 -04003155 def add_hash_footer(self, image_filename, partition_size, partition_name,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003156 hash_algorithm, salt, chain_partitions, algorithm_name,
3157 key_path,
3158 public_key_metadata_path, rollback_index, flags, props,
David Zeuthen18666ab2016-11-15 11:18:05 -05003159 props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003160 setup_rootfs_from_kernel,
David Zeuthenbf562452017-05-17 18:04:43 -04003161 include_descriptors_from_image, calc_max_image_size,
David Zeuthena156d3d2017-06-01 12:08:09 -04003162 signing_helper, signing_helper_with_files,
3163 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003164 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003165 print_required_libavb_version, use_persistent_digest,
3166 do_not_use_ab):
David Zeuthena4fee8b2016-08-22 15:20:43 -04003167 """Implementation of the add_hash_footer on unsparse images.
David Zeuthen21e95262016-07-27 17:58:40 -04003168
3169 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003170 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04003171 partition_size: Size of partition.
3172 partition_name: Name of partition (without A/B suffix).
3173 hash_algorithm: Hash algorithm to use.
3174 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003175 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04003176 algorithm_name: Name of algorithm to use.
3177 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05003178 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003179 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003180 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04003181 props: Properties to insert (List of strings of the form 'key:value').
3182 props_from_file: Properties to insert (List of strings 'key:<path>').
3183 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003184 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003185 dm-verity kernel cmdline from.
3186 include_descriptors_from_image: List of file objects for which
3187 to insert descriptors from.
David Zeuthenbf562452017-05-17 18:04:43 -04003188 calc_max_image_size: Don't store the footer - instead calculate the
3189 maximum image size leaving enough room for metadata with the
3190 given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003191 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003192 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003193 release_string: None or avbtool release string.
3194 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05003195 output_vbmeta_image: If not None, also write vbmeta struct to this file.
3196 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04003197 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003198 use_persistent_digest: Use a persistent digest on device.
3199 do_not_use_ab: This partition does not use A/B.
David Zeuthena4fee8b2016-08-22 15:20:43 -04003200
3201 Raises:
3202 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04003203 """
David Zeuthen1097a782017-05-31 15:53:17 -04003204
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003205 required_libavb_version_minor = 0
3206 if use_persistent_digest or do_not_use_ab:
3207 required_libavb_version_minor = 1
3208
David Zeuthen1097a782017-05-31 15:53:17 -04003209 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003210 if print_required_libavb_version:
Jan Monsch23e0c622019-12-11 11:23:58 +01003211 print('1.{}'.format(required_libavb_version_minor))
David Zeuthen1097a782017-05-31 15:53:17 -04003212 return
3213
David Zeuthenbf562452017-05-17 18:04:43 -04003214 # First, calculate the maximum image size such that an image
3215 # this size + metadata (footer + vbmeta struct) fits in
3216 # |partition_size|.
3217 max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003218 if partition_size < max_metadata_size:
3219 raise AvbError('Parition size of {} is too small. '
3220 'Needs to be at least {}'.format(
3221 partition_size, max_metadata_size))
David Zeuthenbf562452017-05-17 18:04:43 -04003222 max_image_size = partition_size - max_metadata_size
3223
3224 # If we're asked to only calculate the maximum image size, we're done.
3225 if calc_max_image_size:
Jan Monsch23e0c622019-12-11 11:23:58 +01003226 print('{}'.format(max_image_size))
David Zeuthenbf562452017-05-17 18:04:43 -04003227 return
3228
David Zeuthena4fee8b2016-08-22 15:20:43 -04003229 image = ImageHandler(image_filename)
3230
3231 if partition_size % image.block_size != 0:
3232 raise AvbError('Partition size of {} is not a multiple of the image '
3233 'block size {}.'.format(partition_size,
3234 image.block_size))
3235
David Zeuthen21e95262016-07-27 17:58:40 -04003236 # If there's already a footer, truncate the image to its original
3237 # size. This way 'avbtool add_hash_footer' is idempotent (modulo
3238 # salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003239 if image.image_size >= AvbFooter.SIZE:
3240 image.seek(image.image_size - AvbFooter.SIZE)
3241 try:
3242 footer = AvbFooter(image.read(AvbFooter.SIZE))
3243 # Existing footer found. Just truncate.
3244 original_image_size = footer.original_image_size
3245 image.truncate(footer.original_image_size)
3246 except (LookupError, struct.error):
3247 original_image_size = image.image_size
3248 else:
3249 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04003250 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003251
3252 # If anything goes wrong from here-on, restore the image back to
3253 # its original size.
3254 try:
David Zeuthen09692692016-09-30 16:16:40 -04003255 # If image size exceeds the maximum image size, fail.
3256 if image.image_size > max_image_size:
3257 raise AvbError('Image size of {} exceeds maximum image '
3258 'size of {} in order to fit in a partition '
3259 'size of {}.'.format(image.image_size, max_image_size,
3260 partition_size))
3261
David Zeuthen21e95262016-07-27 17:58:40 -04003262 digest_size = len(hashlib.new(name=hash_algorithm).digest())
3263 if salt:
Jan Monsch23e0c622019-12-11 11:23:58 +01003264 salt = binascii.unhexlify(salt)
3265 elif salt is None and not use_persistent_digest:
3266 # If salt is not explicitly specified, choose a hash that's the same
3267 # size as the hash size. Don't populate a random salt if this
3268 # descriptor is being created to use a persistent digest on device.
3269 hash_size = digest_size
3270 salt = open('/dev/urandom').read(hash_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003271 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01003272 salt = ''
David Zeuthen21e95262016-07-27 17:58:40 -04003273
3274 hasher = hashlib.new(name=hash_algorithm, string=salt)
3275 # TODO(zeuthen): might want to read this in chunks to avoid
3276 # memory pressure, then again, this is only supposed to be used
3277 # on kernel/initramfs partitions. Possible optimization.
3278 image.seek(0)
David Zeuthen09692692016-09-30 16:16:40 -04003279 hasher.update(image.read(image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003280 digest = hasher.digest()
3281
3282 h_desc = AvbHashDescriptor()
David Zeuthen09692692016-09-30 16:16:40 -04003283 h_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003284 h_desc.hash_algorithm = hash_algorithm
3285 h_desc.partition_name = partition_name
3286 h_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003287 h_desc.flags = 0
3288 if do_not_use_ab:
3289 h_desc.flags |= 1 # AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
3290 if not use_persistent_digest:
3291 h_desc.digest = digest
David Zeuthen21e95262016-07-27 17:58:40 -04003292
3293 # Generate the VBMeta footer.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003294 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04003295 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003296 algorithm_name, key_path, public_key_metadata_path, [h_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05003297 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003298 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003299 include_descriptors_from_image, signing_helper,
3300 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003301 append_to_release_string, required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04003302
David Zeuthend247fcb2017-02-16 12:09:27 -05003303 # Write vbmeta blob, if requested.
3304 if output_vbmeta_image:
3305 output_vbmeta_image.write(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003306
David Zeuthend247fcb2017-02-16 12:09:27 -05003307 # Append vbmeta blob and footer, unless requested not to.
3308 if not do_not_append_vbmeta_image:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003309 # If the image isn't sparse, its size might not be a multiple of
3310 # the block size. This will screw up padding later so just grow it.
3311 if image.image_size % image.block_size != 0:
3312 assert not image.is_sparse
3313 padding_needed = image.block_size - (
3314 image.image_size % image.block_size)
3315 image.truncate(image.image_size + padding_needed)
3316
3317 # The append_raw() method requires content with size being a
3318 # multiple of |block_size| so add padding as needed. Also record
3319 # where this is written to since we'll need to put that in the
3320 # footer.
3321 vbmeta_offset = image.image_size
3322 padding_needed = (
3323 round_to_multiple(len(vbmeta_blob), image.block_size) -
3324 len(vbmeta_blob))
3325 vbmeta_blob_with_padding = vbmeta_blob + '\0' * padding_needed
3326
David Zeuthend247fcb2017-02-16 12:09:27 -05003327 image.append_raw(vbmeta_blob_with_padding)
3328 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
3329
3330 # Now insert a DONT_CARE chunk with enough bytes such that the
3331 # final Footer block is at the end of partition_size..
3332 image.append_dont_care(partition_size - vbmeta_end_offset -
3333 1*image.block_size)
3334
3335 # Generate the Footer that tells where the VBMeta footer
3336 # is. Also put enough padding in the front of the footer since
3337 # we'll write out an entire block.
3338 footer = AvbFooter()
3339 footer.original_image_size = original_image_size
3340 footer.vbmeta_offset = vbmeta_offset
3341 footer.vbmeta_size = len(vbmeta_blob)
3342 footer_blob = footer.encode()
3343 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
3344 footer_blob)
3345 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003346
David Zeuthen21e95262016-07-27 17:58:40 -04003347 except:
3348 # Truncate back to original size, then re-raise
3349 image.truncate(original_image_size)
3350 raise
3351
David Zeuthena4fee8b2016-08-22 15:20:43 -04003352 def add_hashtree_footer(self, image_filename, partition_size, partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003353 generate_fec, fec_num_roots, hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003354 block_size, salt, chain_partitions, algorithm_name,
3355 key_path,
3356 public_key_metadata_path, rollback_index, flags,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003357 props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003358 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003359 setup_as_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04003360 include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05003361 calc_max_image_size, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003362 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05003363 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003364 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003365 print_required_libavb_version,
Jan Monscheeb28b62019-12-05 16:17:09 +01003366 use_persistent_root_digest, do_not_use_ab,
3367 no_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04003368 """Implements the 'add_hashtree_footer' command.
3369
3370 See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
3371 more information about dm-verity and these hashes.
3372
3373 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003374 image_filename: File to add the footer to.
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003375 partition_size: Size of partition or 0 to put it right at the end.
David Zeuthen21e95262016-07-27 17:58:40 -04003376 partition_name: Name of partition (without A/B suffix).
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003377 generate_fec: If True, generate FEC codes.
3378 fec_num_roots: Number of roots for FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04003379 hash_algorithm: Hash algorithm to use.
3380 block_size: Block size to use.
3381 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003382 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04003383 algorithm_name: Name of algorithm to use.
3384 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05003385 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003386 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003387 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04003388 props: Properties to insert (List of strings of the form 'key:value').
3389 props_from_file: Properties to insert (List of strings 'key:<path>').
3390 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003391 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003392 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003393 setup_as_rootfs_from_kernel: If True, generate dm-verity kernel
3394 cmdline to set up rootfs.
David Zeuthen21e95262016-07-27 17:58:40 -04003395 include_descriptors_from_image: List of file objects for which
3396 to insert descriptors from.
David Zeuthen09692692016-09-30 16:16:40 -04003397 calc_max_image_size: Don't store the hashtree or footer - instead
3398 calculate the maximum image size leaving enough room for hashtree
3399 and metadata with the given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003400 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003401 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003402 release_string: None or avbtool release string.
3403 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05003404 output_vbmeta_image: If not None, also write vbmeta struct to this file.
3405 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04003406 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003407 use_persistent_root_digest: Use a persistent root digest on device.
3408 do_not_use_ab: The partition does not use A/B.
Jooyung Hand7221942019-06-17 13:19:57 +09003409 no_hashtree: Do not append hashtree. Set size in descriptor as zero.
David Zeuthena4fee8b2016-08-22 15:20:43 -04003410
3411 Raises:
3412 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04003413 """
David Zeuthen1097a782017-05-31 15:53:17 -04003414
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003415 required_libavb_version_minor = 0
3416 if use_persistent_root_digest or do_not_use_ab:
3417 required_libavb_version_minor = 1
3418
David Zeuthen1097a782017-05-31 15:53:17 -04003419 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003420 if print_required_libavb_version:
Jan Monsch23e0c622019-12-11 11:23:58 +01003421 print('1.{}'.format(required_libavb_version_minor))
David Zeuthen1097a782017-05-31 15:53:17 -04003422 return
3423
David Zeuthen09692692016-09-30 16:16:40 -04003424 digest_size = len(hashlib.new(name=hash_algorithm).digest())
3425 digest_padding = round_to_pow2(digest_size) - digest_size
3426
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003427 # If |partition_size| is given (e.g. not 0), calculate the maximum image
3428 # size such that an image this size + the hashtree + metadata (footer +
3429 # vbmeta struct) fits in |partition_size|. We use very conservative figures
3430 # for metadata.
3431 if partition_size > 0:
Jooyung Hand7221942019-06-17 13:19:57 +09003432 max_tree_size = 0
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003433 max_fec_size = 0
Jooyung Hand7221942019-06-17 13:19:57 +09003434 if not no_hashtree:
3435 (_, max_tree_size) = calc_hash_level_offsets(
3436 partition_size, block_size, digest_size + digest_padding)
3437 if generate_fec:
3438 max_fec_size = calc_fec_data_size(partition_size, fec_num_roots)
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003439 max_metadata_size = (max_fec_size + max_tree_size +
3440 self.MAX_VBMETA_SIZE +
3441 self.MAX_FOOTER_SIZE)
3442 max_image_size = partition_size - max_metadata_size
3443 else:
3444 max_image_size = 0
David Zeuthen09692692016-09-30 16:16:40 -04003445
3446 # If we're asked to only calculate the maximum image size, we're done.
3447 if calc_max_image_size:
Jan Monsch23e0c622019-12-11 11:23:58 +01003448 print('{}'.format(max_image_size))
David Zeuthen09692692016-09-30 16:16:40 -04003449 return
3450
David Zeuthena4fee8b2016-08-22 15:20:43 -04003451 image = ImageHandler(image_filename)
3452
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003453 if partition_size > 0:
3454 if partition_size % image.block_size != 0:
3455 raise AvbError('Partition size of {} is not a multiple of the image '
3456 'block size {}.'.format(partition_size,
3457 image.block_size))
Jan Monsch23e0c622019-12-11 11:23:58 +01003458 elif image.image_size % image.block_size != 0:
3459 raise AvbError('File size of {} is not a multiple of the image '
3460 'block size {}.'.format(image.image_size,
3461 image.block_size))
David Zeuthena4fee8b2016-08-22 15:20:43 -04003462
David Zeuthen21e95262016-07-27 17:58:40 -04003463 # If there's already a footer, truncate the image to its original
3464 # size. This way 'avbtool add_hashtree_footer' is idempotent
3465 # (modulo salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003466 if image.image_size >= AvbFooter.SIZE:
3467 image.seek(image.image_size - AvbFooter.SIZE)
3468 try:
3469 footer = AvbFooter(image.read(AvbFooter.SIZE))
3470 # Existing footer found. Just truncate.
3471 original_image_size = footer.original_image_size
3472 image.truncate(footer.original_image_size)
3473 except (LookupError, struct.error):
3474 original_image_size = image.image_size
3475 else:
3476 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04003477 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003478
3479 # If anything goes wrong from here-on, restore the image back to
3480 # its original size.
3481 try:
3482 # Ensure image is multiple of block_size.
David Zeuthen09692692016-09-30 16:16:40 -04003483 rounded_image_size = round_to_multiple(image.image_size, block_size)
3484 if rounded_image_size > image.image_size:
3485 image.append_raw('\0' * (rounded_image_size - image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003486
David Zeuthen09692692016-09-30 16:16:40 -04003487 # If image size exceeds the maximum image size, fail.
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003488 if partition_size > 0:
3489 if image.image_size > max_image_size:
3490 raise AvbError('Image size of {} exceeds maximum image '
3491 'size of {} in order to fit in a partition '
3492 'size of {}.'.format(image.image_size, max_image_size,
3493 partition_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003494
3495 if salt:
Jan Monsch23e0c622019-12-11 11:23:58 +01003496 salt = binascii.unhexlify(salt)
3497 elif salt is None and not use_persistent_root_digest:
3498 # If salt is not explicitly specified, choose a hash that's the same
3499 # size as the hash size. Don't populate a random salt if this
3500 # descriptor is being created to use a persistent digest on device.
3501 hash_size = digest_size
3502 salt = open('/dev/urandom').read(hash_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003503 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01003504 salt = ''
David Zeuthen21e95262016-07-27 17:58:40 -04003505
David Zeuthena4fee8b2016-08-22 15:20:43 -04003506 # Hashes are stored upside down so we need to calculate hash
David Zeuthen21e95262016-07-27 17:58:40 -04003507 # offsets in advance.
3508 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
David Zeuthen09692692016-09-30 16:16:40 -04003509 image.image_size, block_size, digest_size + digest_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04003510
David Zeuthena4fee8b2016-08-22 15:20:43 -04003511 # If the image isn't sparse, its size might not be a multiple of
3512 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04003513 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003514 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04003515 padding_needed = image.block_size - (image.image_size%image.block_size)
3516 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04003517
David Zeuthena4fee8b2016-08-22 15:20:43 -04003518 # Generate the tree and add padding as needed.
David Zeuthen09692692016-09-30 16:16:40 -04003519 tree_offset = image.image_size
3520 root_digest, hash_tree = generate_hash_tree(image, image.image_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003521 block_size,
3522 hash_algorithm, salt,
3523 digest_padding,
3524 hash_level_offsets,
3525 tree_size)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003526
3527 # Generate HashtreeDescriptor with details about the tree we
3528 # just generated.
Jooyung Hand7221942019-06-17 13:19:57 +09003529 if no_hashtree:
3530 tree_size = 0
3531 hash_tree = bytearray()
David Zeuthen21e95262016-07-27 17:58:40 -04003532 ht_desc = AvbHashtreeDescriptor()
3533 ht_desc.dm_verity_version = 1
David Zeuthen09692692016-09-30 16:16:40 -04003534 ht_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003535 ht_desc.tree_offset = tree_offset
3536 ht_desc.tree_size = tree_size
3537 ht_desc.data_block_size = block_size
3538 ht_desc.hash_block_size = block_size
3539 ht_desc.hash_algorithm = hash_algorithm
3540 ht_desc.partition_name = partition_name
3541 ht_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003542 if do_not_use_ab:
3543 ht_desc.flags |= 1 # AVB_HASHTREE_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
3544 if not use_persistent_root_digest:
3545 ht_desc.root_digest = root_digest
David Zeuthen21e95262016-07-27 17:58:40 -04003546
David Zeuthen09692692016-09-30 16:16:40 -04003547 # Write the hash tree
3548 padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
3549 len(hash_tree))
3550 hash_tree_with_padding = hash_tree + '\0'*padding_needed
3551 image.append_raw(hash_tree_with_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003552 len_hashtree_and_fec = len(hash_tree_with_padding)
3553
3554 # Generate FEC codes, if requested.
3555 if generate_fec:
Jooyung Hand7221942019-06-17 13:19:57 +09003556 if no_hashtree:
3557 fec_data = bytearray()
Tao Bao868db2a2019-09-09 13:35:05 -07003558 else:
3559 fec_data = generate_fec_data(image_filename, fec_num_roots)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003560 padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
3561 len(fec_data))
3562 fec_data_with_padding = fec_data + '\0'*padding_needed
3563 fec_offset = image.image_size
3564 image.append_raw(fec_data_with_padding)
3565 len_hashtree_and_fec += len(fec_data_with_padding)
3566 # Update the hashtree descriptor.
3567 ht_desc.fec_num_roots = fec_num_roots
3568 ht_desc.fec_offset = fec_offset
3569 ht_desc.fec_size = len(fec_data)
David Zeuthen09692692016-09-30 16:16:40 -04003570
David Zeuthen73f2afa2017-05-17 16:54:11 -04003571 ht_desc_to_setup = None
3572 if setup_as_rootfs_from_kernel:
3573 ht_desc_to_setup = ht_desc
3574
David Zeuthena4fee8b2016-08-22 15:20:43 -04003575 # Generate the VBMeta footer and add padding as needed.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003576 vbmeta_offset = tree_offset + len_hashtree_and_fec
David Zeuthen21e95262016-07-27 17:58:40 -04003577 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003578 algorithm_name, key_path, public_key_metadata_path, [ht_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05003579 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003580 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003581 include_descriptors_from_image, signing_helper,
3582 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003583 append_to_release_string, required_libavb_version_minor)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003584 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
3585 len(vbmeta_blob))
3586 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
David Zeuthen21e95262016-07-27 17:58:40 -04003587
David Zeuthend247fcb2017-02-16 12:09:27 -05003588 # Write vbmeta blob, if requested.
3589 if output_vbmeta_image:
3590 output_vbmeta_image.write(vbmeta_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003591
David Zeuthend247fcb2017-02-16 12:09:27 -05003592 # Append vbmeta blob and footer, unless requested not to.
3593 if not do_not_append_vbmeta_image:
3594 image.append_raw(vbmeta_blob_with_padding)
3595
3596 # Now insert a DONT_CARE chunk with enough bytes such that the
3597 # final Footer block is at the end of partition_size..
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003598 if partition_size > 0:
3599 image.append_dont_care(partition_size - image.image_size -
3600 1*image.block_size)
David Zeuthend247fcb2017-02-16 12:09:27 -05003601
3602 # Generate the Footer that tells where the VBMeta footer
3603 # is. Also put enough padding in the front of the footer since
3604 # we'll write out an entire block.
3605 footer = AvbFooter()
3606 footer.original_image_size = original_image_size
3607 footer.vbmeta_offset = vbmeta_offset
3608 footer.vbmeta_size = len(vbmeta_blob)
3609 footer_blob = footer.encode()
3610 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
3611 footer_blob)
3612 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003613
David Zeuthen21e95262016-07-27 17:58:40 -04003614 except:
David Zeuthen09692692016-09-30 16:16:40 -04003615 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04003616 image.truncate(original_image_size)
3617 raise
3618
David Zeuthenc68f0822017-03-31 17:22:35 -04003619 def make_atx_certificate(self, output, authority_key_path, subject_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003620 subject_key_version, subject,
Darren Krahnfccd64e2018-01-16 17:39:35 -08003621 is_intermediate_authority, usage, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003622 signing_helper_with_files):
Darren Krahn147b08d2016-12-20 16:38:29 -08003623 """Implements the 'make_atx_certificate' command.
3624
3625 Android Things certificates are required for Android Things public key
3626 metadata. They chain the vbmeta signing key for a particular product back to
3627 a fused, permanent root key. These certificates are fixed-length and fixed-
3628 format with the explicit goal of not parsing ASN.1 in bootloader code.
3629
3630 Arguments:
3631 output: Certificate will be written to this file on success.
3632 authority_key_path: A PEM file path with the authority private key.
3633 If None, then a certificate will be created without a
3634 signature. The signature can be created out-of-band
3635 and appended.
David Zeuthenc68f0822017-03-31 17:22:35 -04003636 subject_key_path: Path to a PEM or DER subject public key.
Darren Krahn147b08d2016-12-20 16:38:29 -08003637 subject_key_version: A 64-bit version value. If this is None, the number
3638 of seconds since the epoch is used.
3639 subject: A subject identifier. For Product Signing Key certificates this
3640 should be the same Product ID found in the permanent attributes.
3641 is_intermediate_authority: True if the certificate is for an intermediate
3642 authority.
Darren Krahnfccd64e2018-01-16 17:39:35 -08003643 usage: If not empty, overrides the cert usage with a hash of this value.
Darren Krahn147b08d2016-12-20 16:38:29 -08003644 signing_helper: Program which signs a hash and returns the signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003645 signing_helper_with_files: Same as signing_helper but uses files instead.
Darren Krahn147b08d2016-12-20 16:38:29 -08003646 """
3647 signed_data = bytearray()
3648 signed_data.extend(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04003649 signed_data.extend(encode_rsa_key(subject_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08003650 hasher = hashlib.sha256()
3651 hasher.update(subject)
3652 signed_data.extend(hasher.digest())
Darren Krahnfccd64e2018-01-16 17:39:35 -08003653 if not usage:
3654 usage = 'com.google.android.things.vboot'
3655 if is_intermediate_authority:
3656 usage += '.ca'
Darren Krahn147b08d2016-12-20 16:38:29 -08003657 hasher = hashlib.sha256()
3658 hasher.update(usage)
3659 signed_data.extend(hasher.digest())
Yu Shanc8540812019-07-01 16:54:46 -07003660 if subject_key_version is None:
Darren Krahn147b08d2016-12-20 16:38:29 -08003661 subject_key_version = int(time.time())
3662 signed_data.extend(struct.pack('<Q', subject_key_version))
3663 signature = bytearray()
3664 if authority_key_path:
3665 padding_and_hash = bytearray()
Darren Krahn43e12d82017-02-24 16:26:31 -08003666 algorithm_name = 'SHA512_RSA4096'
Esun Kimff44f232017-03-30 10:34:54 +09003667 alg = ALGORITHMS[algorithm_name]
Jan Monsch23e0c622019-12-11 11:23:58 +01003668 hasher = hashlib.sha512() # pylint: disable=redefined-variable-type
Esun Kimff44f232017-03-30 10:34:54 +09003669 padding_and_hash.extend(alg.padding)
Darren Krahn147b08d2016-12-20 16:38:29 -08003670 hasher.update(signed_data)
3671 padding_and_hash.extend(hasher.digest())
David Zeuthena156d3d2017-06-01 12:08:09 -04003672 signature.extend(raw_sign(signing_helper, signing_helper_with_files,
3673 algorithm_name,
Esun Kimff44f232017-03-30 10:34:54 +09003674 alg.signature_num_bytes, authority_key_path,
3675 padding_and_hash))
Darren Krahn147b08d2016-12-20 16:38:29 -08003676 output.write(signed_data)
3677 output.write(signature)
3678
David Zeuthenc68f0822017-03-31 17:22:35 -04003679 def make_atx_permanent_attributes(self, output, root_authority_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003680 product_id):
3681 """Implements the 'make_atx_permanent_attributes' command.
3682
3683 Android Things permanent attributes are designed to be permanent for a
3684 particular product and a hash of these attributes should be fused into
3685 hardware to enforce this.
3686
3687 Arguments:
3688 output: Attributes will be written to this file on success.
David Zeuthenc68f0822017-03-31 17:22:35 -04003689 root_authority_key_path: Path to a PEM or DER public key for
3690 the root authority.
Darren Krahn147b08d2016-12-20 16:38:29 -08003691 product_id: A 16-byte Product ID.
3692
3693 Raises:
3694 AvbError: If an argument is incorrect.
3695 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01003696 EXPECTED_PRODUCT_ID_SIZE = 16 # pylint: disable=invalid-name
Darren Krahn43e12d82017-02-24 16:26:31 -08003697 if len(product_id) != EXPECTED_PRODUCT_ID_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003698 raise AvbError('Invalid Product ID length.')
3699 output.write(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04003700 output.write(encode_rsa_key(root_authority_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08003701 output.write(product_id)
3702
3703 def make_atx_metadata(self, output, intermediate_key_certificate,
Darren Krahn43e12d82017-02-24 16:26:31 -08003704 product_key_certificate):
Darren Krahn147b08d2016-12-20 16:38:29 -08003705 """Implements the 'make_atx_metadata' command.
3706
3707 Android Things metadata are included in vbmeta images to facilitate
3708 verification. The output of this command can be used as the
3709 public_key_metadata argument to other commands.
3710
3711 Arguments:
3712 output: Metadata will be written to this file on success.
3713 intermediate_key_certificate: A certificate file as output by
3714 make_atx_certificate with
3715 is_intermediate_authority set to true.
3716 product_key_certificate: A certificate file as output by
3717 make_atx_certificate with
3718 is_intermediate_authority set to false.
Darren Krahn147b08d2016-12-20 16:38:29 -08003719
3720 Raises:
3721 AvbError: If an argument is incorrect.
3722 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01003723 EXPECTED_CERTIFICATE_SIZE = 1620 # pylint: disable=invalid-name
Darren Krahn43e12d82017-02-24 16:26:31 -08003724 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003725 raise AvbError('Invalid intermediate key certificate length.')
Darren Krahn43e12d82017-02-24 16:26:31 -08003726 if len(product_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003727 raise AvbError('Invalid product key certificate length.')
3728 output.write(struct.pack('<I', 1)) # Format Version
3729 output.write(intermediate_key_certificate)
3730 output.write(product_key_certificate)
Darren Krahn147b08d2016-12-20 16:38:29 -08003731
Darren Krahnfccd64e2018-01-16 17:39:35 -08003732 def make_atx_unlock_credential(self, output, intermediate_key_certificate,
3733 unlock_key_certificate, challenge_path,
3734 unlock_key_path, signing_helper,
3735 signing_helper_with_files):
3736 """Implements the 'make_atx_unlock_credential' command.
3737
3738 Android Things unlock credentials can be used to authorize the unlock of AVB
3739 on a device. These credentials are presented to an Android Things bootloader
3740 via the fastboot interface in response to a 16-byte challenge. This method
3741 creates all fields of the credential except the challenge signature field
3742 (which is the last field) and can optionally create the challenge signature
3743 field as well if a challenge and the unlock_key_path is provided.
3744
3745 Arguments:
3746 output: The credential will be written to this file on success.
3747 intermediate_key_certificate: A certificate file as output by
3748 make_atx_certificate with
3749 is_intermediate_authority set to true.
3750 unlock_key_certificate: A certificate file as output by
3751 make_atx_certificate with
3752 is_intermediate_authority set to false and the
3753 usage set to
3754 'com.google.android.things.vboot.unlock'.
3755 challenge_path: [optional] A path to the challenge to sign.
3756 unlock_key_path: [optional] A PEM file path with the unlock private key.
3757 signing_helper: Program which signs a hash and returns the signature.
3758 signing_helper_with_files: Same as signing_helper but uses files instead.
3759
3760 Raises:
3761 AvbError: If an argument is incorrect.
3762 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01003763 EXPECTED_CERTIFICATE_SIZE = 1620 # pylint: disable=invalid-name
3764 EXPECTED_CHALLENGE_SIZE = 16 # pylint: disable=invalid-name
Darren Krahnfccd64e2018-01-16 17:39:35 -08003765 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
3766 raise AvbError('Invalid intermediate key certificate length.')
3767 if len(unlock_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
3768 raise AvbError('Invalid product key certificate length.')
3769 challenge = bytearray()
3770 if challenge_path:
3771 with open(challenge_path, 'r') as f:
3772 challenge = f.read()
3773 if len(challenge) != EXPECTED_CHALLENGE_SIZE:
3774 raise AvbError('Invalid unlock challenge length.')
3775 output.write(struct.pack('<I', 1)) # Format Version
3776 output.write(intermediate_key_certificate)
3777 output.write(unlock_key_certificate)
3778 if challenge_path and unlock_key_path:
3779 signature = bytearray()
3780 padding_and_hash = bytearray()
3781 algorithm_name = 'SHA512_RSA4096'
3782 alg = ALGORITHMS[algorithm_name]
3783 hasher = hashlib.sha512()
3784 padding_and_hash.extend(alg.padding)
3785 hasher.update(challenge)
3786 padding_and_hash.extend(hasher.digest())
3787 signature.extend(raw_sign(signing_helper, signing_helper_with_files,
3788 algorithm_name,
3789 alg.signature_num_bytes, unlock_key_path,
3790 padding_and_hash))
3791 output.write(signature)
3792
David Zeuthen21e95262016-07-27 17:58:40 -04003793
3794def calc_hash_level_offsets(image_size, block_size, digest_size):
3795 """Calculate the offsets of all the hash-levels in a Merkle-tree.
3796
3797 Arguments:
3798 image_size: The size of the image to calculate a Merkle-tree for.
3799 block_size: The block size, e.g. 4096.
3800 digest_size: The size of each hash, e.g. 32 for SHA-256.
3801
3802 Returns:
3803 A tuple where the first argument is an array of offsets and the
3804 second is size of the tree, in bytes.
3805 """
3806 level_offsets = []
3807 level_sizes = []
3808 tree_size = 0
3809
3810 num_levels = 0
3811 size = image_size
3812 while size > block_size:
Jan Monsch23e0c622019-12-11 11:23:58 +01003813 num_blocks = (size + block_size - 1) // block_size
David Zeuthen21e95262016-07-27 17:58:40 -04003814 level_size = round_to_multiple(num_blocks * digest_size, block_size)
3815
3816 level_sizes.append(level_size)
3817 tree_size += level_size
3818 num_levels += 1
3819
3820 size = level_size
3821
3822 for n in range(0, num_levels):
3823 offset = 0
3824 for m in range(n + 1, num_levels):
3825 offset += level_sizes[m]
3826 level_offsets.append(offset)
3827
David Zeuthena4fee8b2016-08-22 15:20:43 -04003828 return level_offsets, tree_size
David Zeuthen21e95262016-07-27 17:58:40 -04003829
3830
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003831# See system/extras/libfec/include/fec/io.h for these definitions.
3832FEC_FOOTER_FORMAT = '<LLLLLQ32s'
3833FEC_MAGIC = 0xfecfecfe
3834
3835
3836def calc_fec_data_size(image_size, num_roots):
3837 """Calculates how much space FEC data will take.
3838
Jan Monschfe00c0a2019-12-11 11:19:40 +01003839 Arguments:
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003840 image_size: The size of the image.
3841 num_roots: Number of roots.
3842
3843 Returns:
3844 The number of bytes needed for FEC for an image of the given size
3845 and with the requested number of FEC roots.
3846
3847 Raises:
3848 ValueError: If output from the 'fec' tool is invalid.
3849
3850 """
3851 p = subprocess.Popen(
3852 ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)],
3853 stdout=subprocess.PIPE,
3854 stderr=subprocess.PIPE)
3855 (pout, perr) = p.communicate()
3856 retcode = p.wait()
3857 if retcode != 0:
3858 raise ValueError('Error invoking fec: {}'.format(perr))
3859 return int(pout)
3860
3861
3862def generate_fec_data(image_filename, num_roots):
3863 """Generate FEC codes for an image.
3864
Jan Monschfe00c0a2019-12-11 11:19:40 +01003865 Arguments:
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003866 image_filename: The filename of the image.
3867 num_roots: Number of roots.
3868
3869 Returns:
3870 The FEC data blob.
3871
3872 Raises:
3873 ValueError: If output from the 'fec' tool is invalid.
3874 """
3875 fec_tmpfile = tempfile.NamedTemporaryFile()
3876 subprocess.check_call(
3877 ['fec', '--encode', '--roots', str(num_roots), image_filename,
3878 fec_tmpfile.name],
3879 stderr=open(os.devnull))
3880 fec_data = fec_tmpfile.read()
3881 footer_size = struct.calcsize(FEC_FOOTER_FORMAT)
3882 footer_data = fec_data[-footer_size:]
3883 (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT,
3884 footer_data)
3885 if magic != FEC_MAGIC:
3886 raise ValueError('Unexpected magic in FEC footer')
3887 return fec_data[0:fec_size]
3888
3889
David Zeuthen21e95262016-07-27 17:58:40 -04003890def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003891 digest_padding, hash_level_offsets, tree_size):
David Zeuthen21e95262016-07-27 17:58:40 -04003892 """Generates a Merkle-tree for a file.
3893
Jan Monschfe00c0a2019-12-11 11:19:40 +01003894 Arguments:
David Zeuthen21e95262016-07-27 17:58:40 -04003895 image: The image, as a file.
3896 image_size: The size of the image.
3897 block_size: The block size, e.g. 4096.
3898 hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'.
3899 salt: The salt to use.
3900 digest_padding: The padding for each digest.
David Zeuthen21e95262016-07-27 17:58:40 -04003901 hash_level_offsets: The offsets from calc_hash_level_offsets().
David Zeuthena4fee8b2016-08-22 15:20:43 -04003902 tree_size: The size of the tree, in number of bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04003903
3904 Returns:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003905 A tuple where the first element is the top-level hash and the
3906 second element is the hash-tree.
David Zeuthen21e95262016-07-27 17:58:40 -04003907 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04003908 hash_ret = bytearray(tree_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003909 hash_src_offset = 0
3910 hash_src_size = image_size
3911 level_num = 0
3912 while hash_src_size > block_size:
Colin Cross388338a2020-02-28 14:18:01 -08003913 level_output_list = []
David Zeuthen21e95262016-07-27 17:58:40 -04003914 remaining = hash_src_size
3915 while remaining > 0:
3916 hasher = hashlib.new(name=hash_alg_name, string=salt)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003917 # Only read from the file for the first level - for subsequent
3918 # levels, access the array we're building.
3919 if level_num == 0:
3920 image.seek(hash_src_offset + hash_src_size - remaining)
3921 data = image.read(min(remaining, block_size))
3922 else:
3923 offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining
3924 data = hash_ret[offset:offset + block_size]
David Zeuthen21e95262016-07-27 17:58:40 -04003925 hasher.update(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003926
3927 remaining -= len(data)
David Zeuthen21e95262016-07-27 17:58:40 -04003928 if len(data) < block_size:
3929 hasher.update('\0' * (block_size - len(data)))
Colin Cross388338a2020-02-28 14:18:01 -08003930 level_output_list.append(hasher.digest())
David Zeuthen21e95262016-07-27 17:58:40 -04003931 if digest_padding > 0:
Colin Cross388338a2020-02-28 14:18:01 -08003932 level_output_list.append('\0' * digest_padding)
3933
3934 level_output = ''.join(level_output_list)
David Zeuthen21e95262016-07-27 17:58:40 -04003935
3936 padding_needed = (round_to_multiple(
3937 len(level_output), block_size) - len(level_output))
3938 level_output += '\0' * padding_needed
3939
David Zeuthena4fee8b2016-08-22 15:20:43 -04003940 # Copy level-output into resulting tree.
3941 offset = hash_level_offsets[level_num]
3942 hash_ret[offset:offset + len(level_output)] = level_output
David Zeuthen21e95262016-07-27 17:58:40 -04003943
David Zeuthena4fee8b2016-08-22 15:20:43 -04003944 # Continue on to the next level.
David Zeuthen21e95262016-07-27 17:58:40 -04003945 hash_src_size = len(level_output)
David Zeuthen21e95262016-07-27 17:58:40 -04003946 level_num += 1
3947
3948 hasher = hashlib.new(name=hash_alg_name, string=salt)
3949 hasher.update(level_output)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003950 return hasher.digest(), hash_ret
David Zeuthen21e95262016-07-27 17:58:40 -04003951
3952
3953class AvbTool(object):
3954 """Object for avbtool command-line tool."""
3955
3956 def __init__(self):
3957 """Initializer method."""
3958 self.avb = Avb()
3959
3960 def _add_common_args(self, sub_parser):
3961 """Adds arguments used by several sub-commands.
3962
3963 Arguments:
3964 sub_parser: The parser to add arguments to.
3965 """
3966 sub_parser.add_argument('--algorithm',
3967 help='Algorithm to use (default: NONE)',
3968 metavar='ALGORITHM',
3969 default='NONE')
3970 sub_parser.add_argument('--key',
3971 help='Path to RSA private key file',
3972 metavar='KEY',
3973 required=False)
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003974 sub_parser.add_argument('--signing_helper',
3975 help='Path to helper used for signing',
3976 metavar='APP',
3977 default=None,
3978 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04003979 sub_parser.add_argument('--signing_helper_with_files',
3980 help='Path to helper used for signing using files',
3981 metavar='APP',
3982 default=None,
3983 required=False)
David Zeuthen18666ab2016-11-15 11:18:05 -05003984 sub_parser.add_argument('--public_key_metadata',
3985 help='Path to public key metadata file',
3986 metavar='KEY_METADATA',
3987 required=False)
David Zeuthen21e95262016-07-27 17:58:40 -04003988 sub_parser.add_argument('--rollback_index',
3989 help='Rollback Index',
3990 type=parse_number,
3991 default=0)
David Zeuthene3cadca2017-02-22 21:25:46 -05003992 # This is used internally for unit tests. Do not include in --help output.
3993 sub_parser.add_argument('--internal_release_string',
3994 help=argparse.SUPPRESS)
3995 sub_parser.add_argument('--append_to_release_string',
3996 help='Text to append to release string',
3997 metavar='STR')
David Zeuthen21e95262016-07-27 17:58:40 -04003998 sub_parser.add_argument('--prop',
3999 help='Add property',
4000 metavar='KEY:VALUE',
4001 action='append')
4002 sub_parser.add_argument('--prop_from_file',
4003 help='Add property from file',
4004 metavar='KEY:PATH',
4005 action='append')
4006 sub_parser.add_argument('--kernel_cmdline',
4007 help='Add kernel cmdline',
4008 metavar='CMDLINE',
4009 action='append')
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004010 # TODO(zeuthen): the --setup_rootfs_from_kernel option used to be called
4011 # --generate_dm_verity_cmdline_from_hashtree. Remove support for the latter
4012 # at some future point.
4013 sub_parser.add_argument('--setup_rootfs_from_kernel',
4014 '--generate_dm_verity_cmdline_from_hashtree',
David Zeuthen21e95262016-07-27 17:58:40 -04004015 metavar='IMAGE',
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004016 help='Adds kernel cmdline to set up IMAGE',
David Zeuthen21e95262016-07-27 17:58:40 -04004017 type=argparse.FileType('rb'))
4018 sub_parser.add_argument('--include_descriptors_from_image',
4019 help='Include descriptors from image',
4020 metavar='IMAGE',
4021 action='append',
4022 type=argparse.FileType('rb'))
David Zeuthen1097a782017-05-31 15:53:17 -04004023 sub_parser.add_argument('--print_required_libavb_version',
4024 help=('Don\'t store the footer - '
4025 'instead calculate the required libavb '
4026 'version for the given options.'),
4027 action='store_true')
David Zeuthena5fd3a42017-02-27 16:38:54 -05004028 # These are only allowed from top-level vbmeta and boot-in-lieu-of-vbmeta.
4029 sub_parser.add_argument('--chain_partition',
4030 help='Allow signed integrity-data for partition',
4031 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
4032 action='append')
4033 sub_parser.add_argument('--flags',
4034 help='VBMeta flags',
4035 type=parse_number,
4036 default=0)
4037 sub_parser.add_argument('--set_hashtree_disabled_flag',
4038 help='Set the HASHTREE_DISABLED flag',
4039 action='store_true')
4040
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004041 def _add_common_footer_args(self, sub_parser):
4042 """Adds arguments used by add_*_footer sub-commands.
4043
4044 Arguments:
4045 sub_parser: The parser to add arguments to.
4046 """
4047 sub_parser.add_argument('--use_persistent_digest',
4048 help='Use a persistent digest on device instead of '
4049 'storing the digest in the descriptor. This '
4050 'cannot be used with A/B so must be combined '
4051 'with --do_not_use_ab when an A/B suffix is '
4052 'expected at runtime.',
4053 action='store_true')
4054 sub_parser.add_argument('--do_not_use_ab',
4055 help='The partition does not use A/B even when an '
4056 'A/B suffix is present. This must not be used '
4057 'for vbmeta or chained partitions.',
4058 action='store_true')
4059
David Zeuthena5fd3a42017-02-27 16:38:54 -05004060 def _fixup_common_args(self, args):
4061 """Common fixups needed by subcommands.
4062
4063 Arguments:
4064 args: Arguments to modify.
4065
4066 Returns:
4067 The modified arguments.
4068 """
4069 if args.set_hashtree_disabled_flag:
4070 args.flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED
4071 return args
David Zeuthen21e95262016-07-27 17:58:40 -04004072
4073 def run(self, argv):
4074 """Command-line processor.
4075
4076 Arguments:
4077 argv: Pass sys.argv from main.
4078 """
4079 parser = argparse.ArgumentParser()
4080 subparsers = parser.add_subparsers(title='subcommands')
4081
4082 sub_parser = subparsers.add_parser('version',
4083 help='Prints version of avbtool.')
4084 sub_parser.set_defaults(func=self.version)
4085
4086 sub_parser = subparsers.add_parser('extract_public_key',
4087 help='Extract public key.')
4088 sub_parser.add_argument('--key',
4089 help='Path to RSA private key file',
4090 required=True)
4091 sub_parser.add_argument('--output',
4092 help='Output file name',
4093 type=argparse.FileType('wb'),
4094 required=True)
4095 sub_parser.set_defaults(func=self.extract_public_key)
4096
4097 sub_parser = subparsers.add_parser('make_vbmeta_image',
4098 help='Makes a vbmeta image.')
4099 sub_parser.add_argument('--output',
4100 help='Output file name',
David Zeuthen1097a782017-05-31 15:53:17 -04004101 type=argparse.FileType('wb'))
David Zeuthen97cb5802017-06-01 16:14:05 -04004102 sub_parser.add_argument('--padding_size',
4103 metavar='NUMBER',
4104 help='If non-zero, pads output with NUL bytes so '
Jan Monscheeb28b62019-12-05 16:17:09 +01004105 'its size is a multiple of NUMBER '
4106 '(default: 0)',
David Zeuthen97cb5802017-06-01 16:14:05 -04004107 type=parse_number,
4108 default=0)
David Zeuthen21e95262016-07-27 17:58:40 -04004109 self._add_common_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004110 sub_parser.set_defaults(func=self.make_vbmeta_image)
4111
4112 sub_parser = subparsers.add_parser('add_hash_footer',
4113 help='Add hashes and footer to image.')
4114 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004115 help='Image to add hashes to',
David Zeuthen21e95262016-07-27 17:58:40 -04004116 type=argparse.FileType('rab+'))
4117 sub_parser.add_argument('--partition_size',
4118 help='Partition size',
David Zeuthen1097a782017-05-31 15:53:17 -04004119 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04004120 sub_parser.add_argument('--partition_name',
4121 help='Partition name',
David Zeuthenbf562452017-05-17 18:04:43 -04004122 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04004123 sub_parser.add_argument('--hash_algorithm',
4124 help='Hash algorithm to use (default: sha256)',
4125 default='sha256')
4126 sub_parser.add_argument('--salt',
4127 help='Salt in hex (default: /dev/urandom)')
David Zeuthenbf562452017-05-17 18:04:43 -04004128 sub_parser.add_argument('--calc_max_image_size',
4129 help=('Don\'t store the footer - '
4130 'instead calculate the maximum image size '
4131 'leaving enough room for metadata with '
4132 'the given partition size.'),
4133 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05004134 sub_parser.add_argument('--output_vbmeta_image',
4135 help='Also write vbmeta struct to file',
4136 type=argparse.FileType('wb'))
4137 sub_parser.add_argument('--do_not_append_vbmeta_image',
4138 help=('Do not append vbmeta struct or footer '
4139 'to the image'),
4140 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04004141 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004142 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004143 sub_parser.set_defaults(func=self.add_hash_footer)
4144
David Zeuthenb1b994d2017-03-06 18:01:31 -05004145 sub_parser = subparsers.add_parser('append_vbmeta_image',
4146 help='Append vbmeta image to image.')
4147 sub_parser.add_argument('--image',
4148 help='Image to append vbmeta blob to',
4149 type=argparse.FileType('rab+'))
4150 sub_parser.add_argument('--partition_size',
4151 help='Partition size',
4152 type=parse_number,
4153 required=True)
4154 sub_parser.add_argument('--vbmeta_image',
4155 help='Image with vbmeta blob to append',
4156 type=argparse.FileType('rb'))
4157 sub_parser.set_defaults(func=self.append_vbmeta_image)
4158
Jan Monscheeb28b62019-12-05 16:17:09 +01004159 sub_parser = subparsers.add_parser(
4160 'add_hashtree_footer',
4161 help='Add hashtree and footer to image.')
David Zeuthen21e95262016-07-27 17:58:40 -04004162 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004163 help='Image to add hashtree to',
David Zeuthen21e95262016-07-27 17:58:40 -04004164 type=argparse.FileType('rab+'))
4165 sub_parser.add_argument('--partition_size',
4166 help='Partition size',
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004167 default=0,
David Zeuthen1097a782017-05-31 15:53:17 -04004168 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04004169 sub_parser.add_argument('--partition_name',
4170 help='Partition name',
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004171 default='')
David Zeuthen21e95262016-07-27 17:58:40 -04004172 sub_parser.add_argument('--hash_algorithm',
4173 help='Hash algorithm to use (default: sha1)',
4174 default='sha1')
4175 sub_parser.add_argument('--salt',
4176 help='Salt in hex (default: /dev/urandom)')
4177 sub_parser.add_argument('--block_size',
4178 help='Block size (default: 4096)',
4179 type=parse_number,
4180 default=4096)
David Zeuthenbce9a292017-05-10 17:18:04 -04004181 # TODO(zeuthen): The --generate_fec option was removed when we
4182 # moved to generating FEC by default. To avoid breaking existing
4183 # users needing to transition we simply just print a warning below
4184 # in add_hashtree_footer(). Remove this option and the warning at
4185 # some point in the future.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004186 sub_parser.add_argument('--generate_fec',
David Zeuthenbce9a292017-05-10 17:18:04 -04004187 help=argparse.SUPPRESS,
4188 action='store_true')
Jan Monscheeb28b62019-12-05 16:17:09 +01004189 sub_parser.add_argument(
4190 '--do_not_generate_fec',
4191 help='Do not generate forward-error-correction codes',
4192 action='store_true')
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004193 sub_parser.add_argument('--fec_num_roots',
4194 help='Number of roots for FEC (default: 2)',
4195 type=parse_number,
4196 default=2)
David Zeuthen09692692016-09-30 16:16:40 -04004197 sub_parser.add_argument('--calc_max_image_size',
4198 help=('Don\'t store the hashtree or footer - '
4199 'instead calculate the maximum image size '
4200 'leaving enough room for hashtree '
4201 'and metadata with the given partition '
4202 'size.'),
4203 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05004204 sub_parser.add_argument('--output_vbmeta_image',
4205 help='Also write vbmeta struct to file',
4206 type=argparse.FileType('wb'))
4207 sub_parser.add_argument('--do_not_append_vbmeta_image',
4208 help=('Do not append vbmeta struct or footer '
4209 'to the image'),
4210 action='store_true')
David Zeuthen73f2afa2017-05-17 16:54:11 -04004211 # This is different from --setup_rootfs_from_kernel insofar that
4212 # it doesn't take an IMAGE, the generated cmdline will be for the
4213 # hashtree we're adding.
4214 sub_parser.add_argument('--setup_as_rootfs_from_kernel',
4215 action='store_true',
4216 help='Adds kernel cmdline for setting up rootfs')
Jooyung Hand7221942019-06-17 13:19:57 +09004217 sub_parser.add_argument('--no_hashtree',
4218 action='store_true',
4219 help='Do not append hashtree')
David Zeuthen21e95262016-07-27 17:58:40 -04004220 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004221 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004222 sub_parser.set_defaults(func=self.add_hashtree_footer)
4223
4224 sub_parser = subparsers.add_parser('erase_footer',
4225 help='Erase footer from an image.')
4226 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004227 help='Image with a footer',
David Zeuthen21e95262016-07-27 17:58:40 -04004228 type=argparse.FileType('rwb+'),
4229 required=True)
4230 sub_parser.add_argument('--keep_hashtree',
David Zeuthenfbb61fa2017-02-02 12:11:49 -05004231 help='Keep the hashtree and FEC in the image',
David Zeuthen21e95262016-07-27 17:58:40 -04004232 action='store_true')
4233 sub_parser.set_defaults(func=self.erase_footer)
4234
David Zeuthen1394f762019-04-30 10:20:11 -04004235 sub_parser = subparsers.add_parser('zero_hashtree',
4236 help='Zero out hashtree and FEC data.')
4237 sub_parser.add_argument('--image',
4238 help='Image with a footer',
4239 type=argparse.FileType('rwb+'),
4240 required=True)
4241 sub_parser.set_defaults(func=self.zero_hashtree)
4242
Jan Monscheeb28b62019-12-05 16:17:09 +01004243 sub_parser = subparsers.add_parser(
4244 'extract_vbmeta_image',
4245 help='Extracts vbmeta from an image with a footer.')
David Zeuthen49936b42018-08-07 17:38:58 -04004246 sub_parser.add_argument('--image',
4247 help='Image with footer',
4248 type=argparse.FileType('rb'),
4249 required=True)
4250 sub_parser.add_argument('--output',
4251 help='Output file name',
4252 type=argparse.FileType('wb'))
4253 sub_parser.add_argument('--padding_size',
4254 metavar='NUMBER',
4255 help='If non-zero, pads output with NUL bytes so '
Jan Monscheeb28b62019-12-05 16:17:09 +01004256 'its size is a multiple of NUMBER '
4257 '(default: 0)',
David Zeuthen49936b42018-08-07 17:38:58 -04004258 type=parse_number,
4259 default=0)
4260 sub_parser.set_defaults(func=self.extract_vbmeta_image)
4261
David Zeuthen2bc232b2017-04-19 14:25:19 -04004262 sub_parser = subparsers.add_parser('resize_image',
4263 help='Resize image with a footer.')
4264 sub_parser.add_argument('--image',
4265 help='Image with a footer',
4266 type=argparse.FileType('rwb+'),
4267 required=True)
4268 sub_parser.add_argument('--partition_size',
4269 help='New partition size',
4270 type=parse_number)
4271 sub_parser.set_defaults(func=self.resize_image)
4272
David Zeuthen21e95262016-07-27 17:58:40 -04004273 sub_parser = subparsers.add_parser(
4274 'info_image',
4275 help='Show information about vbmeta or footer.')
4276 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004277 help='Image to show information about',
David Zeuthen21e95262016-07-27 17:58:40 -04004278 type=argparse.FileType('rb'),
4279 required=True)
4280 sub_parser.add_argument('--output',
4281 help='Write info to file',
4282 type=argparse.FileType('wt'),
4283 default=sys.stdout)
4284 sub_parser.set_defaults(func=self.info_image)
4285
David Zeuthenb623d8b2017-04-04 16:05:53 -04004286 sub_parser = subparsers.add_parser(
4287 'verify_image',
4288 help='Verify an image.')
4289 sub_parser.add_argument('--image',
4290 help='Image to verify',
4291 type=argparse.FileType('rb'),
4292 required=True)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04004293 sub_parser.add_argument('--key',
4294 help='Check embedded public key matches KEY',
4295 metavar='KEY',
4296 required=False)
4297 sub_parser.add_argument('--expected_chain_partition',
4298 help='Expected chain partition',
4299 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
4300 action='append')
Jan Monscheeb28b62019-12-05 16:17:09 +01004301 sub_parser.add_argument(
4302 '--follow_chain_partitions',
4303 help=('Follows chain partitions even when not '
4304 'specified with the --expected_chain_partition option'),
4305 action='store_true')
4306 sub_parser.add_argument(
4307 '--accept_zeroed_hashtree',
4308 help=('Accept images where the hashtree or FEC data is zeroed out'),
4309 action='store_true')
David Zeuthenb623d8b2017-04-04 16:05:53 -04004310 sub_parser.set_defaults(func=self.verify_image)
4311
David Zeuthenb8643c02018-05-17 17:21:18 -04004312 sub_parser = subparsers.add_parser(
4313 'calculate_vbmeta_digest',
4314 help='Calculate vbmeta digest.')
4315 sub_parser.add_argument('--image',
4316 help='Image to calculate digest for',
4317 type=argparse.FileType('rb'),
4318 required=True)
4319 sub_parser.add_argument('--hash_algorithm',
4320 help='Hash algorithm to use (default: sha256)',
4321 default='sha256')
4322 sub_parser.add_argument('--output',
4323 help='Write hex digest to file (default: stdout)',
4324 type=argparse.FileType('wt'),
4325 default=sys.stdout)
4326 sub_parser.set_defaults(func=self.calculate_vbmeta_digest)
4327
David Zeuthenf7d2e752018-09-20 13:30:41 -04004328 sub_parser = subparsers.add_parser(
4329 'calculate_kernel_cmdline',
4330 help='Calculate kernel cmdline.')
4331 sub_parser.add_argument('--image',
4332 help='Image to calculate kernel cmdline for',
4333 type=argparse.FileType('rb'),
4334 required=True)
4335 sub_parser.add_argument('--hashtree_disabled',
4336 help='Return the cmdline for hashtree disabled',
4337 action='store_true')
4338 sub_parser.add_argument('--output',
4339 help='Write cmdline to file (default: stdout)',
4340 type=argparse.FileType('wt'),
4341 default=sys.stdout)
4342 sub_parser.set_defaults(func=self.calculate_kernel_cmdline)
4343
David Zeuthen8b6973b2016-09-20 12:39:49 -04004344 sub_parser = subparsers.add_parser('set_ab_metadata',
4345 help='Set A/B metadata.')
4346 sub_parser.add_argument('--misc_image',
4347 help=('The misc image to modify. If the image does '
4348 'not exist, it will be created.'),
4349 type=argparse.FileType('r+b'),
4350 required=True)
4351 sub_parser.add_argument('--slot_data',
4352 help=('Slot data of the form "priority", '
4353 '"tries_remaining", "sucessful_boot" for '
4354 'slot A followed by the same for slot B, '
4355 'separated by colons. The default value '
4356 'is 15:7:0:14:7:0.'),
4357 default='15:7:0:14:7:0')
4358 sub_parser.set_defaults(func=self.set_ab_metadata)
4359
Darren Krahn147b08d2016-12-20 16:38:29 -08004360 sub_parser = subparsers.add_parser(
4361 'make_atx_certificate',
4362 help='Create an Android Things eXtension (ATX) certificate.')
4363 sub_parser.add_argument('--output',
4364 help='Write certificate to file',
4365 type=argparse.FileType('wb'),
4366 default=sys.stdout)
4367 sub_parser.add_argument('--subject',
4368 help=('Path to subject file'),
4369 type=argparse.FileType('rb'),
4370 required=True)
4371 sub_parser.add_argument('--subject_key',
4372 help=('Path to subject RSA public key file'),
4373 type=argparse.FileType('rb'),
4374 required=True)
4375 sub_parser.add_argument('--subject_key_version',
4376 help=('Version of the subject key'),
4377 type=parse_number,
4378 required=False)
4379 sub_parser.add_argument('--subject_is_intermediate_authority',
4380 help=('Generate an intermediate authority '
4381 'certificate'),
4382 action='store_true')
Darren Krahnfccd64e2018-01-16 17:39:35 -08004383 sub_parser.add_argument('--usage',
Darren Krahn2367b462018-06-19 00:53:32 -07004384 help=('Override usage with a hash of the provided '
Darren Krahnfccd64e2018-01-16 17:39:35 -08004385 'string'),
4386 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08004387 sub_parser.add_argument('--authority_key',
4388 help='Path to authority RSA private key file',
4389 required=False)
4390 sub_parser.add_argument('--signing_helper',
4391 help='Path to helper used for signing',
4392 metavar='APP',
4393 default=None,
4394 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04004395 sub_parser.add_argument('--signing_helper_with_files',
4396 help='Path to helper used for signing using files',
4397 metavar='APP',
4398 default=None,
4399 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08004400 sub_parser.set_defaults(func=self.make_atx_certificate)
4401
4402 sub_parser = subparsers.add_parser(
4403 'make_atx_permanent_attributes',
4404 help='Create Android Things eXtension (ATX) permanent attributes.')
4405 sub_parser.add_argument('--output',
4406 help='Write attributes to file',
4407 type=argparse.FileType('wb'),
4408 default=sys.stdout)
4409 sub_parser.add_argument('--root_authority_key',
4410 help='Path to authority RSA public key file',
4411 type=argparse.FileType('rb'),
4412 required=True)
4413 sub_parser.add_argument('--product_id',
4414 help=('Path to Product ID file'),
4415 type=argparse.FileType('rb'),
4416 required=True)
4417 sub_parser.set_defaults(func=self.make_atx_permanent_attributes)
4418
4419 sub_parser = subparsers.add_parser(
4420 'make_atx_metadata',
4421 help='Create Android Things eXtension (ATX) metadata.')
4422 sub_parser.add_argument('--output',
4423 help='Write metadata to file',
4424 type=argparse.FileType('wb'),
4425 default=sys.stdout)
4426 sub_parser.add_argument('--intermediate_key_certificate',
4427 help='Path to intermediate key certificate file',
4428 type=argparse.FileType('rb'),
4429 required=True)
4430 sub_parser.add_argument('--product_key_certificate',
4431 help='Path to product key certificate file',
4432 type=argparse.FileType('rb'),
4433 required=True)
Darren Krahn147b08d2016-12-20 16:38:29 -08004434 sub_parser.set_defaults(func=self.make_atx_metadata)
4435
Darren Krahnfccd64e2018-01-16 17:39:35 -08004436 sub_parser = subparsers.add_parser(
4437 'make_atx_unlock_credential',
4438 help='Create an Android Things eXtension (ATX) unlock credential.')
4439 sub_parser.add_argument('--output',
4440 help='Write credential to file',
4441 type=argparse.FileType('wb'),
4442 default=sys.stdout)
4443 sub_parser.add_argument('--intermediate_key_certificate',
4444 help='Path to intermediate key certificate file',
4445 type=argparse.FileType('rb'),
4446 required=True)
4447 sub_parser.add_argument('--unlock_key_certificate',
4448 help='Path to unlock key certificate file',
4449 type=argparse.FileType('rb'),
4450 required=True)
4451 sub_parser.add_argument('--challenge',
4452 help='Path to the challenge to sign (optional). If '
4453 'this is not provided the challenge signature '
4454 'field is omitted and can be concatenated '
4455 'later.',
4456 required=False)
4457 sub_parser.add_argument('--unlock_key',
4458 help='Path to unlock key (optional). Must be '
4459 'provided if using --challenge.',
4460 required=False)
4461 sub_parser.add_argument('--signing_helper',
4462 help='Path to helper used for signing',
4463 metavar='APP',
4464 default=None,
4465 required=False)
4466 sub_parser.add_argument('--signing_helper_with_files',
4467 help='Path to helper used for signing using files',
4468 metavar='APP',
4469 default=None,
4470 required=False)
4471 sub_parser.set_defaults(func=self.make_atx_unlock_credential)
4472
David Zeuthen21e95262016-07-27 17:58:40 -04004473 args = parser.parse_args(argv[1:])
4474 try:
4475 args.func(args)
4476 except AvbError as e:
Jan Monsch23e0c622019-12-11 11:23:58 +01004477 sys.stderr.write('{}: {}\n'.format(argv[0], str(e)))
David Zeuthen21e95262016-07-27 17:58:40 -04004478 sys.exit(1)
4479
4480 def version(self, _):
4481 """Implements the 'version' sub-command."""
Jan Monsch23e0c622019-12-11 11:23:58 +01004482 print(get_release_string())
David Zeuthen21e95262016-07-27 17:58:40 -04004483
4484 def extract_public_key(self, args):
4485 """Implements the 'extract_public_key' sub-command."""
4486 self.avb.extract_public_key(args.key, args.output)
4487
4488 def make_vbmeta_image(self, args):
4489 """Implements the 'make_vbmeta_image' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004490 args = self._fixup_common_args(args)
David Zeuthen21e95262016-07-27 17:58:40 -04004491 self.avb.make_vbmeta_image(args.output, args.chain_partition,
David Zeuthen18666ab2016-11-15 11:18:05 -05004492 args.algorithm, args.key,
4493 args.public_key_metadata, args.rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05004494 args.flags, args.prop, args.prop_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04004495 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004496 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05004497 args.include_descriptors_from_image,
David Zeuthene3cadca2017-02-22 21:25:46 -05004498 args.signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04004499 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004500 args.internal_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04004501 args.append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04004502 args.print_required_libavb_version,
4503 args.padding_size)
David Zeuthen21e95262016-07-27 17:58:40 -04004504
David Zeuthenb1b994d2017-03-06 18:01:31 -05004505 def append_vbmeta_image(self, args):
4506 """Implements the 'append_vbmeta_image' sub-command."""
4507 self.avb.append_vbmeta_image(args.image.name, args.vbmeta_image.name,
4508 args.partition_size)
4509
David Zeuthen21e95262016-07-27 17:58:40 -04004510 def add_hash_footer(self, args):
4511 """Implements the 'add_hash_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004512 args = self._fixup_common_args(args)
David Zeuthenbf562452017-05-17 18:04:43 -04004513 self.avb.add_hash_footer(args.image.name if args.image else None,
4514 args.partition_size,
David Zeuthen21e95262016-07-27 17:58:40 -04004515 args.partition_name, args.hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004516 args.salt, args.chain_partition, args.algorithm,
4517 args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05004518 args.public_key_metadata, args.rollback_index,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004519 args.flags, args.prop, args.prop_from_file,
David Zeuthen18666ab2016-11-15 11:18:05 -05004520 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004521 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05004522 args.include_descriptors_from_image,
David Zeuthena156d3d2017-06-01 12:08:09 -04004523 args.calc_max_image_size,
4524 args.signing_helper,
4525 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004526 args.internal_release_string,
4527 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05004528 args.output_vbmeta_image,
David Zeuthen1097a782017-05-31 15:53:17 -04004529 args.do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004530 args.print_required_libavb_version,
4531 args.use_persistent_digest,
4532 args.do_not_use_ab)
David Zeuthen21e95262016-07-27 17:58:40 -04004533
4534 def add_hashtree_footer(self, args):
4535 """Implements the 'add_hashtree_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004536 args = self._fixup_common_args(args)
David Zeuthenbce9a292017-05-10 17:18:04 -04004537 # TODO(zeuthen): Remove when removing support for the
4538 # '--generate_fec' option above.
4539 if args.generate_fec:
4540 sys.stderr.write('The --generate_fec option is deprecated since FEC '
4541 'is now generated by default. Use the option '
4542 '--do_not_generate_fec to not generate FEC.\n')
Jan Monscheeb28b62019-12-05 16:17:09 +01004543 self.avb.add_hashtree_footer(
4544 args.image.name if args.image else None,
4545 args.partition_size,
4546 args.partition_name,
4547 not args.do_not_generate_fec, args.fec_num_roots,
4548 args.hash_algorithm, args.block_size,
4549 args.salt, args.chain_partition, args.algorithm,
4550 args.key, args.public_key_metadata,
4551 args.rollback_index, args.flags, args.prop,
4552 args.prop_from_file,
4553 args.kernel_cmdline,
4554 args.setup_rootfs_from_kernel,
4555 args.setup_as_rootfs_from_kernel,
4556 args.include_descriptors_from_image,
4557 args.calc_max_image_size,
4558 args.signing_helper,
4559 args.signing_helper_with_files,
4560 args.internal_release_string,
4561 args.append_to_release_string,
4562 args.output_vbmeta_image,
4563 args.do_not_append_vbmeta_image,
4564 args.print_required_libavb_version,
4565 args.use_persistent_digest,
4566 args.do_not_use_ab,
4567 args.no_hashtree)
David Zeuthend247fcb2017-02-16 12:09:27 -05004568
David Zeuthen21e95262016-07-27 17:58:40 -04004569 def erase_footer(self, args):
4570 """Implements the 'erase_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04004571 self.avb.erase_footer(args.image.name, args.keep_hashtree)
David Zeuthen21e95262016-07-27 17:58:40 -04004572
David Zeuthen1394f762019-04-30 10:20:11 -04004573 def zero_hashtree(self, args):
4574 """Implements the 'zero_hashtree' sub-command."""
4575 self.avb.zero_hashtree(args.image.name)
4576
David Zeuthen49936b42018-08-07 17:38:58 -04004577 def extract_vbmeta_image(self, args):
4578 """Implements the 'extract_vbmeta_image' sub-command."""
4579 self.avb.extract_vbmeta_image(args.output, args.image.name,
4580 args.padding_size)
4581
David Zeuthen2bc232b2017-04-19 14:25:19 -04004582 def resize_image(self, args):
4583 """Implements the 'resize_image' sub-command."""
4584 self.avb.resize_image(args.image.name, args.partition_size)
4585
David Zeuthen8b6973b2016-09-20 12:39:49 -04004586 def set_ab_metadata(self, args):
4587 """Implements the 'set_ab_metadata' sub-command."""
4588 self.avb.set_ab_metadata(args.misc_image, args.slot_data)
4589
David Zeuthen21e95262016-07-27 17:58:40 -04004590 def info_image(self, args):
4591 """Implements the 'info_image' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04004592 self.avb.info_image(args.image.name, args.output)
David Zeuthen21e95262016-07-27 17:58:40 -04004593
David Zeuthenb623d8b2017-04-04 16:05:53 -04004594 def verify_image(self, args):
4595 """Implements the 'verify_image' sub-command."""
David Zeuthen5dfb4e92017-05-24 14:49:32 -04004596 self.avb.verify_image(args.image.name, args.key,
David Zeuthene947cb62019-01-25 15:27:08 -05004597 args.expected_chain_partition,
David Zeuthen1394f762019-04-30 10:20:11 -04004598 args.follow_chain_partitions,
4599 args.accept_zeroed_hashtree)
David Zeuthenb623d8b2017-04-04 16:05:53 -04004600
David Zeuthenb8643c02018-05-17 17:21:18 -04004601 def calculate_vbmeta_digest(self, args):
4602 """Implements the 'calculate_vbmeta_digest' sub-command."""
4603 self.avb.calculate_vbmeta_digest(args.image.name, args.hash_algorithm,
4604 args.output)
4605
David Zeuthenf7d2e752018-09-20 13:30:41 -04004606 def calculate_kernel_cmdline(self, args):
4607 """Implements the 'calculate_kernel_cmdline' sub-command."""
Jan Monscheeb28b62019-12-05 16:17:09 +01004608 self.avb.calculate_kernel_cmdline(args.image.name, args.hashtree_disabled,
4609 args.output)
David Zeuthenf7d2e752018-09-20 13:30:41 -04004610
Darren Krahn147b08d2016-12-20 16:38:29 -08004611 def make_atx_certificate(self, args):
4612 """Implements the 'make_atx_certificate' sub-command."""
4613 self.avb.make_atx_certificate(args.output, args.authority_key,
David Zeuthenc68f0822017-03-31 17:22:35 -04004614 args.subject_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08004615 args.subject_key_version,
4616 args.subject.read(),
4617 args.subject_is_intermediate_authority,
Darren Krahnfccd64e2018-01-16 17:39:35 -08004618 args.usage,
David Zeuthena156d3d2017-06-01 12:08:09 -04004619 args.signing_helper,
4620 args.signing_helper_with_files)
Darren Krahn147b08d2016-12-20 16:38:29 -08004621
4622 def make_atx_permanent_attributes(self, args):
4623 """Implements the 'make_atx_permanent_attributes' sub-command."""
4624 self.avb.make_atx_permanent_attributes(args.output,
David Zeuthenc68f0822017-03-31 17:22:35 -04004625 args.root_authority_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08004626 args.product_id.read())
4627
4628 def make_atx_metadata(self, args):
4629 """Implements the 'make_atx_metadata' sub-command."""
4630 self.avb.make_atx_metadata(args.output,
4631 args.intermediate_key_certificate.read(),
Darren Krahn43e12d82017-02-24 16:26:31 -08004632 args.product_key_certificate.read())
Darren Krahn147b08d2016-12-20 16:38:29 -08004633
Darren Krahnfccd64e2018-01-16 17:39:35 -08004634 def make_atx_unlock_credential(self, args):
4635 """Implements the 'make_atx_unlock_credential' sub-command."""
4636 self.avb.make_atx_unlock_credential(
4637 args.output,
4638 args.intermediate_key_certificate.read(),
4639 args.unlock_key_certificate.read(),
4640 args.challenge,
4641 args.unlock_key,
4642 args.signing_helper,
4643 args.signing_helper_with_files)
4644
David Zeuthen21e95262016-07-27 17:58:40 -04004645
4646if __name__ == '__main__':
4647 tool = AvbTool()
4648 tool.run(sys.argv)