blob: 2523449a3f4f579c27668b710aab01807641f082 [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
2343 if footer:
2344 o.write('Footer version: {}.{}\n'.format(footer.version_major,
2345 footer.version_minor))
2346 o.write('Image size: {} bytes\n'.format(image_size))
2347 o.write('Original image size: {} bytes\n'.format(
2348 footer.original_image_size))
2349 o.write('VBMeta offset: {}\n'.format(footer.vbmeta_offset))
2350 o.write('VBMeta size: {} bytes\n'.format(footer.vbmeta_size))
2351 o.write('--\n')
2352
2353 (alg_name, _) = lookup_algorithm_by_type(header.algorithm_type)
2354
David Zeuthene3cadca2017-02-22 21:25:46 -05002355 o.write('Minimum libavb version: {}.{}{}\n'.format(
2356 header.required_libavb_version_major,
2357 header.required_libavb_version_minor,
David Zeuthena4fee8b2016-08-22 15:20:43 -04002358 ' (Sparse)' if image.is_sparse else ''))
David Zeuthen21e95262016-07-27 17:58:40 -04002359 o.write('Header Block: {} bytes\n'.format(AvbVBMetaHeader.SIZE))
2360 o.write('Authentication Block: {} bytes\n'.format(
2361 header.authentication_data_block_size))
2362 o.write('Auxiliary Block: {} bytes\n'.format(
2363 header.auxiliary_data_block_size))
2364 o.write('Algorithm: {}\n'.format(alg_name))
2365 o.write('Rollback Index: {}\n'.format(header.rollback_index))
David Zeuthenfd41eb92016-11-17 12:24:47 -05002366 o.write('Flags: {}\n'.format(header.flags))
David Zeuthene3cadca2017-02-22 21:25:46 -05002367 o.write('Release String: \'{}\'\n'.format(
2368 header.release_string.rstrip('\0')))
David Zeuthen21e95262016-07-27 17:58:40 -04002369
2370 # Print descriptors.
2371 num_printed = 0
2372 o.write('Descriptors:\n')
2373 for desc in descriptors:
2374 desc.print_desc(o)
2375 num_printed += 1
2376 if num_printed == 0:
2377 o.write(' (none)\n')
2378
Jan Monscheeb28b62019-12-05 16:17:09 +01002379 def verify_image(self, image_filename, key_path, expected_chain_partitions,
2380 follow_chain_partitions, accept_zeroed_hashtree):
David Zeuthenb623d8b2017-04-04 16:05:53 -04002381 """Implements the 'verify_image' command.
2382
2383 Arguments:
2384 image_filename: Image file to get information from (file object).
Jan Monscheeb28b62019-12-05 16:17:09 +01002385 key_path: None or check that embedded public key matches key at given
2386 path.
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002387 expected_chain_partitions: List of chain partitions to check or None.
Jan Monscheeb28b62019-12-05 16:17:09 +01002388 follow_chain_partitions:
2389 If True, will follows chain partitions even when not specified with
2390 the --expected_chain_partition option
2391 accept_zeroed_hashtree: If True, don't fail if hashtree or FEC data is
2392 zeroed out.
Jan Monsch77cd2022019-12-10 17:18:04 +01002393
2394 Raises:
2395 AvbError: If verification of the image fails.
David Zeuthenb623d8b2017-04-04 16:05:53 -04002396 """
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002397 expected_chain_partitions_map = {}
2398 if expected_chain_partitions:
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002399 for cp in expected_chain_partitions:
2400 cp_tokens = cp.split(':')
2401 if len(cp_tokens) != 3:
2402 raise AvbError('Malformed chained partition "{}".'.format(cp))
2403 partition_name = cp_tokens[0]
2404 rollback_index_location = int(cp_tokens[1])
2405 file_path = cp_tokens[2]
2406 pk_blob = open(file_path).read()
Jan Monscheeb28b62019-12-05 16:17:09 +01002407 expected_chain_partitions_map[partition_name] = (
2408 rollback_index_location, pk_blob)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002409
2410 image_dir = os.path.dirname(image_filename)
2411 image_ext = os.path.splitext(image_filename)[1]
2412
2413 key_blob = None
2414 if key_path:
Jan Monsch23e0c622019-12-11 11:23:58 +01002415 print('Verifying image {} using key at {}'.format(image_filename,
2416 key_path))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002417 key_blob = encode_rsa_key(key_path)
2418 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01002419 print('Verifying image {} using embedded public key'.format(
2420 image_filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002421
David Zeuthenb623d8b2017-04-04 16:05:53 -04002422 image = ImageHandler(image_filename)
Jan Monsch77cd2022019-12-10 17:18:04 +01002423 (footer, header, descriptors, _) = self._parse_image(image)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002424 offset = 0
2425 if footer:
2426 offset = footer.vbmeta_offset
David Zeuthen49936b42018-08-07 17:38:58 -04002427
David Zeuthenb623d8b2017-04-04 16:05:53 -04002428 image.seek(offset)
Jan Monscheeb28b62019-12-05 16:17:09 +01002429 vbmeta_blob = image.read(header.SIZE
2430 + header.authentication_data_block_size
2431 + header.auxiliary_data_block_size)
David Zeuthen49936b42018-08-07 17:38:58 -04002432
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002433 alg_name, _ = lookup_algorithm_by_type(header.algorithm_type)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002434 if not verify_vbmeta_signature(header, vbmeta_blob):
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002435 raise AvbError('Signature check failed for {} vbmeta struct {}'
2436 .format(alg_name, image_filename))
2437
2438 if key_blob:
2439 # The embedded public key is in the auxiliary block at an offset.
2440 key_offset = AvbVBMetaHeader.SIZE
David Zeuthen49936b42018-08-07 17:38:58 -04002441 key_offset += header.authentication_data_block_size
2442 key_offset += header.public_key_offset
Jan Monscheeb28b62019-12-05 16:17:09 +01002443 key_blob_in_vbmeta = vbmeta_blob[key_offset:key_offset
2444 + header.public_key_size]
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002445 if key_blob != key_blob_in_vbmeta:
2446 raise AvbError('Embedded public key does not match given key.')
2447
2448 if footer:
Jan Monsch23e0c622019-12-11 11:23:58 +01002449 print('vbmeta: Successfully verified footer and {} vbmeta struct in {}'
2450 .format(alg_name, image.filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002451 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01002452 print('vbmeta: Successfully verified {} vbmeta struct in {}'
2453 .format(alg_name, image.filename))
David Zeuthen5dfb4e92017-05-24 14:49:32 -04002454
2455 for desc in descriptors:
Jan Monscheeb28b62019-12-05 16:17:09 +01002456 if (isinstance(desc, AvbChainPartitionDescriptor)
2457 and follow_chain_partitions
Jan Monschfe00c0a2019-12-11 11:19:40 +01002458 and expected_chain_partitions_map.get(desc.partition_name) is None):
David Zeuthene947cb62019-01-25 15:27:08 -05002459 # In this case we're processing a chain descriptor but don't have a
2460 # --expect_chain_partition ... however --follow_chain_partitions was
2461 # specified so we shouldn't error out in desc.verify().
Jan Monsch23e0c622019-12-11 11:23:58 +01002462 print('{}: Chained but ROLLBACK_SLOT (which is {}) '
2463 'and KEY (which has sha1 {}) not specified'
2464 .format(desc.partition_name, desc.rollback_index_location,
2465 hashlib.sha1(desc.public_key).hexdigest()))
2466 elif not desc.verify(image_dir, image_ext, expected_chain_partitions_map,
Jan Monscheeb28b62019-12-05 16:17:09 +01002467 image, accept_zeroed_hashtree):
Jan Monsch23e0c622019-12-11 11:23:58 +01002468 raise AvbError('Error verifying descriptor.')
Jan Monscheeb28b62019-12-05 16:17:09 +01002469 # Honor --follow_chain_partitions - add '--' to make the output more
2470 # readable.
2471 if (isinstance(desc, AvbChainPartitionDescriptor)
2472 and follow_chain_partitions):
Jan Monsch23e0c622019-12-11 11:23:58 +01002473 print('--')
Jan Monscheeb28b62019-12-05 16:17:09 +01002474 chained_image_filename = os.path.join(image_dir,
2475 desc.partition_name + image_ext)
2476 self.verify_image(chained_image_filename, key_path, None, False,
2477 accept_zeroed_hashtree)
David Zeuthenb623d8b2017-04-04 16:05:53 -04002478
David Zeuthenb8643c02018-05-17 17:21:18 -04002479 def calculate_vbmeta_digest(self, image_filename, hash_algorithm, output):
2480 """Implements the 'calculate_vbmeta_digest' command.
2481
2482 Arguments:
2483 image_filename: Image file to get information from (file object).
2484 hash_algorithm: Hash algorithm used.
2485 output: Output file to write human-readable information to (file object).
2486 """
2487
2488 image_dir = os.path.dirname(image_filename)
2489 image_ext = os.path.splitext(image_filename)[1]
2490
2491 image = ImageHandler(image_filename)
Jan Monsch77cd2022019-12-10 17:18:04 +01002492 (footer, header, descriptors, _) = self._parse_image(image)
David Zeuthenb8643c02018-05-17 17:21:18 -04002493 offset = 0
2494 if footer:
2495 offset = footer.vbmeta_offset
2496 size = (header.SIZE + header.authentication_data_block_size +
2497 header.auxiliary_data_block_size)
2498 image.seek(offset)
2499 vbmeta_blob = image.read(size)
2500
2501 hasher = hashlib.new(name=hash_algorithm)
2502 hasher.update(vbmeta_blob)
2503
2504 for desc in descriptors:
2505 if isinstance(desc, AvbChainPartitionDescriptor):
Jan Monscheeb28b62019-12-05 16:17:09 +01002506 ch_image_filename = os.path.join(image_dir,
2507 desc.partition_name + image_ext)
David Zeuthenb8643c02018-05-17 17:21:18 -04002508 ch_image = ImageHandler(ch_image_filename)
Jan Monsch77cd2022019-12-10 17:18:04 +01002509 (ch_footer, ch_header, _, _) = self._parse_image(ch_image)
David Zeuthenb8643c02018-05-17 17:21:18 -04002510 ch_offset = 0
David Zeuthen49936b42018-08-07 17:38:58 -04002511 ch_size = (ch_header.SIZE + ch_header.authentication_data_block_size +
2512 ch_header.auxiliary_data_block_size)
David Zeuthenb8643c02018-05-17 17:21:18 -04002513 if ch_footer:
2514 ch_offset = ch_footer.vbmeta_offset
David Zeuthenb8643c02018-05-17 17:21:18 -04002515 ch_image.seek(ch_offset)
2516 ch_vbmeta_blob = ch_image.read(ch_size)
2517 hasher.update(ch_vbmeta_blob)
2518
2519 digest = hasher.digest()
Jan Monsch23e0c622019-12-11 11:23:58 +01002520 output.write('{}\n'.format(binascii.hexlify(digest)))
David Zeuthenb8643c02018-05-17 17:21:18 -04002521
David Zeuthenf7d2e752018-09-20 13:30:41 -04002522 def calculate_kernel_cmdline(self, image_filename, hashtree_disabled, output):
2523 """Implements the 'calculate_kernel_cmdline' command.
2524
2525 Arguments:
2526 image_filename: Image file to get information from (file object).
2527 hashtree_disabled: If True, returns the cmdline for hashtree disabled.
2528 output: Output file to write human-readable information to (file object).
2529 """
2530
2531 image = ImageHandler(image_filename)
2532 _, _, descriptors, _ = self._parse_image(image)
2533
2534 image_dir = os.path.dirname(image_filename)
2535 image_ext = os.path.splitext(image_filename)[1]
2536
2537 cmdline_descriptors = []
2538 for desc in descriptors:
2539 if isinstance(desc, AvbChainPartitionDescriptor):
Jan Monscheeb28b62019-12-05 16:17:09 +01002540 ch_image_filename = os.path.join(image_dir,
2541 desc.partition_name + image_ext)
David Zeuthenf7d2e752018-09-20 13:30:41 -04002542 ch_image = ImageHandler(ch_image_filename)
2543 _, _, ch_descriptors, _ = self._parse_image(ch_image)
2544 for ch_desc in ch_descriptors:
2545 if isinstance(ch_desc, AvbKernelCmdlineDescriptor):
2546 cmdline_descriptors.append(ch_desc)
2547 elif isinstance(desc, AvbKernelCmdlineDescriptor):
2548 cmdline_descriptors.append(desc)
2549
2550 kernel_cmdline_snippets = []
2551 for desc in cmdline_descriptors:
2552 use_cmdline = True
Jan Monscheeb28b62019-12-05 16:17:09 +01002553 if ((desc.flags &
2554 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
2555 != 0):
David Zeuthenf7d2e752018-09-20 13:30:41 -04002556 if hashtree_disabled:
2557 use_cmdline = False
Jan Monscheeb28b62019-12-05 16:17:09 +01002558 if (desc.flags &
2559 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED) != 0:
David Zeuthenf7d2e752018-09-20 13:30:41 -04002560 if not hashtree_disabled:
2561 use_cmdline = False
2562 if use_cmdline:
2563 kernel_cmdline_snippets.append(desc.kernel_cmdline)
2564 output.write(' '.join(kernel_cmdline_snippets))
2565
David Zeuthen21e95262016-07-27 17:58:40 -04002566 def _parse_image(self, image):
2567 """Gets information about an image.
2568
2569 The image can either be a vbmeta or an image with a footer.
2570
2571 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002572 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002573
2574 Returns:
2575 A tuple where the first argument is a AvbFooter (None if there
2576 is no footer on the image), the second argument is a
2577 AvbVBMetaHeader, the third argument is a list of
2578 AvbDescriptor-derived instances, and the fourth argument is the
2579 size of |image|.
2580 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002581 assert isinstance(image, ImageHandler)
David Zeuthen21e95262016-07-27 17:58:40 -04002582 footer = None
David Zeuthen09692692016-09-30 16:16:40 -04002583 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002584 try:
2585 footer = AvbFooter(image.read(AvbFooter.SIZE))
2586 except (LookupError, struct.error):
2587 # Nope, just seek back to the start.
2588 image.seek(0)
2589
2590 vbmeta_offset = 0
2591 if footer:
2592 vbmeta_offset = footer.vbmeta_offset
2593
2594 image.seek(vbmeta_offset)
2595 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2596
2597 auth_block_offset = vbmeta_offset + AvbVBMetaHeader.SIZE
2598 aux_block_offset = auth_block_offset + h.authentication_data_block_size
2599 desc_start_offset = aux_block_offset + h.descriptors_offset
2600 image.seek(desc_start_offset)
2601 descriptors = parse_descriptors(image.read(h.descriptors_size))
2602
David Zeuthen09692692016-09-30 16:16:40 -04002603 return footer, h, descriptors, image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002604
David Zeuthenb1b994d2017-03-06 18:01:31 -05002605 def _load_vbmeta_blob(self, image):
2606 """Gets the vbmeta struct and associated sections.
2607
2608 The image can either be a vbmeta.img or an image with a footer.
2609
2610 Arguments:
2611 image: An ImageHandler (vbmeta or footer).
2612
2613 Returns:
2614 A blob with the vbmeta struct and other sections.
2615 """
2616 assert isinstance(image, ImageHandler)
2617 footer = None
2618 image.seek(image.image_size - AvbFooter.SIZE)
2619 try:
2620 footer = AvbFooter(image.read(AvbFooter.SIZE))
2621 except (LookupError, struct.error):
2622 # Nope, just seek back to the start.
2623 image.seek(0)
2624
2625 vbmeta_offset = 0
2626 if footer:
2627 vbmeta_offset = footer.vbmeta_offset
2628
2629 image.seek(vbmeta_offset)
2630 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2631
2632 image.seek(vbmeta_offset)
2633 data_size = AvbVBMetaHeader.SIZE
2634 data_size += h.authentication_data_block_size
2635 data_size += h.auxiliary_data_block_size
2636 return image.read(data_size)
2637
David Zeuthen73f2afa2017-05-17 16:54:11 -04002638 def _get_cmdline_descriptors_for_hashtree_descriptor(self, ht):
David Zeuthenfd41eb92016-11-17 12:24:47 -05002639 """Generate kernel cmdline descriptors for dm-verity.
David Zeuthen21e95262016-07-27 17:58:40 -04002640
2641 Arguments:
David Zeuthen73f2afa2017-05-17 16:54:11 -04002642 ht: A AvbHashtreeDescriptor
David Zeuthen21e95262016-07-27 17:58:40 -04002643
2644 Returns:
David Zeuthenfd41eb92016-11-17 12:24:47 -05002645 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2646 instructions. There is one for when hashtree is not disabled and one for
2647 when it is.
David Zeuthen21e95262016-07-27 17:58:40 -04002648
David Zeuthen21e95262016-07-27 17:58:40 -04002649 """
2650
David Zeuthen21e95262016-07-27 17:58:40 -04002651 c = 'dm="1 vroot none ro 1,'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002652 c += '0' # start
Jan Monsch23e0c622019-12-11 11:23:58 +01002653 c += ' {}'.format((ht.image_size // 512)) # size (# sectors)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002654 c += ' verity {}'.format(ht.dm_verity_version) # type and version
2655 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # data_dev
2656 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # hash_dev
2657 c += ' {}'.format(ht.data_block_size) # data_block
2658 c += ' {}'.format(ht.hash_block_size) # hash_block
Jan Monsch23e0c622019-12-11 11:23:58 +01002659 c += ' {}'.format(ht.image_size // ht.data_block_size) # #blocks
2660 c += ' {}'.format(ht.image_size // ht.data_block_size) # hash_offset
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002661 c += ' {}'.format(ht.hash_algorithm) # hash_alg
2662 c += ' {}'.format(str(ht.root_digest).encode('hex')) # root_digest
2663 c += ' {}'.format(str(ht.salt).encode('hex')) # salt
2664 if ht.fec_num_roots > 0:
David Zeuthena01e32f2017-01-24 17:32:38 -05002665 c += ' 10' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04002666 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002667 c += ' ignore_zero_blocks'
2668 c += ' use_fec_from_device PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2669 c += ' fec_roots {}'.format(ht.fec_num_roots)
2670 # Note that fec_blocks is the size that FEC covers, *not* the
2671 # size of the FEC data. Since we use FEC for everything up until
2672 # the FEC data, it's the same as the offset.
Jan Monsch23e0c622019-12-11 11:23:58 +01002673 c += ' fec_blocks {}'.format(ht.fec_offset // ht.data_block_size)
2674 c += ' fec_start {}'.format(ht.fec_offset // ht.data_block_size)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002675 else:
David Zeuthena01e32f2017-01-24 17:32:38 -05002676 c += ' 2' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04002677 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002678 c += ' ignore_zero_blocks'
David Zeuthenfd9c18d2017-03-20 18:19:30 -04002679 c += '" root=/dev/dm-0'
David Zeuthen21e95262016-07-27 17:58:40 -04002680
David Zeuthenfd41eb92016-11-17 12:24:47 -05002681 # Now that we have the command-line, generate the descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002682 desc = AvbKernelCmdlineDescriptor()
2683 desc.kernel_cmdline = c
David Zeuthenfd41eb92016-11-17 12:24:47 -05002684 desc.flags = (
2685 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
2686
2687 # The descriptor for when hashtree verification is disabled is a lot
2688 # simpler - we just set the root to the partition.
2689 desc_no_ht = AvbKernelCmdlineDescriptor()
2690 desc_no_ht.kernel_cmdline = 'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2691 desc_no_ht.flags = (
2692 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED)
2693
2694 return [desc, desc_no_ht]
David Zeuthen21e95262016-07-27 17:58:40 -04002695
David Zeuthen73f2afa2017-05-17 16:54:11 -04002696 def _get_cmdline_descriptors_for_dm_verity(self, image):
2697 """Generate kernel cmdline descriptors for dm-verity.
2698
2699 Arguments:
2700 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
2701
2702 Returns:
2703 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2704 instructions. There is one for when hashtree is not disabled and one for
2705 when it is.
2706
2707 Raises:
2708 AvbError: If |image| doesn't have a hashtree descriptor.
2709
2710 """
2711
2712 (_, _, descriptors, _) = self._parse_image(image)
2713
2714 ht = None
2715 for desc in descriptors:
2716 if isinstance(desc, AvbHashtreeDescriptor):
2717 ht = desc
2718 break
2719
2720 if not ht:
2721 raise AvbError('No hashtree descriptor in given image')
2722
2723 return self._get_cmdline_descriptors_for_hashtree_descriptor(ht)
2724
David Zeuthen21e95262016-07-27 17:58:40 -04002725 def make_vbmeta_image(self, output, chain_partitions, algorithm_name,
David Zeuthen18666ab2016-11-15 11:18:05 -05002726 key_path, public_key_metadata_path, rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002727 flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002728 setup_rootfs_from_kernel,
David Zeuthena156d3d2017-06-01 12:08:09 -04002729 include_descriptors_from_image,
2730 signing_helper,
2731 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05002732 release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04002733 append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04002734 print_required_libavb_version,
2735 padding_size):
David Zeuthen21e95262016-07-27 17:58:40 -04002736 """Implements the 'make_vbmeta_image' command.
2737
2738 Arguments:
2739 output: File to write the image to.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002740 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002741 algorithm_name: Name of algorithm to use.
2742 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002743 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002744 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002745 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002746 props: Properties to insert (list of strings of the form 'key:value').
2747 props_from_file: Properties to insert (list of strings 'key:<path>').
2748 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002749 setup_rootfs_from_kernel: None or file to generate from.
David Zeuthen21e95262016-07-27 17:58:40 -04002750 include_descriptors_from_image: List of file objects with descriptors.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002751 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04002752 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05002753 release_string: None or avbtool release string to use instead of default.
2754 append_to_release_string: None or string to append.
David Zeuthen1097a782017-05-31 15:53:17 -04002755 print_required_libavb_version: True to only print required libavb version.
David Zeuthen97cb5802017-06-01 16:14:05 -04002756 padding_size: If not 0, pads output so size is a multiple of the number.
David Zeuthen21e95262016-07-27 17:58:40 -04002757
2758 Raises:
2759 AvbError: If a chained partition is malformed.
2760 """
2761
David Zeuthen1097a782017-05-31 15:53:17 -04002762 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04002763 if print_required_libavb_version:
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002764 if include_descriptors_from_image:
2765 # Use the bump logic in AvbVBMetaHeader to calculate the max required
2766 # version of all included descriptors.
2767 tmp_header = AvbVBMetaHeader()
2768 for image in include_descriptors_from_image:
2769 (_, image_header, _, _) = self._parse_image(ImageHandler(image.name))
2770 tmp_header.bump_required_libavb_version_minor(
2771 image_header.required_libavb_version_minor)
Jan Monsch23e0c622019-12-11 11:23:58 +01002772 print('1.{}'.format(tmp_header.required_libavb_version_minor))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002773 else:
2774 # Descriptors aside, all vbmeta features are supported in 1.0.
Jan Monsch23e0c622019-12-11 11:23:58 +01002775 print('1.0')
David Zeuthen1097a782017-05-31 15:53:17 -04002776 return
2777
2778 if not output:
2779 raise AvbError('No output file given')
2780
David Zeuthen21e95262016-07-27 17:58:40 -04002781 descriptors = []
David Zeuthen73f2afa2017-05-17 16:54:11 -04002782 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04002783 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002784 algorithm_name, key_path, public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002785 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002786 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04002787 include_descriptors_from_image, signing_helper,
2788 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002789 append_to_release_string, 0)
David Zeuthen21e95262016-07-27 17:58:40 -04002790
2791 # Write entire vbmeta blob (header, authentication, auxiliary).
2792 output.seek(0)
2793 output.write(vbmeta_blob)
2794
David Zeuthen97cb5802017-06-01 16:14:05 -04002795 if padding_size > 0:
2796 padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
2797 padding_needed = padded_size - len(vbmeta_blob)
2798 output.write('\0' * padding_needed)
2799
Dan Austinb12b2c12019-12-15 20:28:02 -08002800
David Zeuthen18666ab2016-11-15 11:18:05 -05002801 def _generate_vbmeta_blob(self, algorithm_name, key_path,
2802 public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002803 chain_partitions,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002804 rollback_index, flags, props, props_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04002805 kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002806 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002807 ht_desc_to_setup,
David Zeuthene3cadca2017-02-22 21:25:46 -05002808 include_descriptors_from_image, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04002809 signing_helper_with_files,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002810 release_string, append_to_release_string,
2811 required_libavb_version_minor):
David Zeuthen21e95262016-07-27 17:58:40 -04002812 """Generates a VBMeta blob.
2813
2814 This blob contains the header (struct AvbVBMetaHeader), the
2815 authentication data block (which contains the hash and signature
2816 for the header and auxiliary block), and the auxiliary block
2817 (which contains descriptors, the public key used, and other data).
2818
2819 The |key| parameter can |None| only if the |algorithm_name| is
2820 'NONE'.
2821
2822 Arguments:
2823 algorithm_name: The algorithm name as per the ALGORITHMS dict.
2824 key_path: The path to the .pem file used to sign the blob.
David Zeuthen18666ab2016-11-15 11:18:05 -05002825 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002826 descriptors: A list of descriptors to insert or None.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002827 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002828 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002829 flags: Flags to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002830 props: Properties to insert (List of strings of the form 'key:value').
2831 props_from_file: Properties to insert (List of strings 'key:<path>').
2832 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002833 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002834 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04002835 ht_desc_to_setup: If not None, an AvbHashtreeDescriptor to
2836 generate dm-verity kernel cmdline descriptors from.
David Zeuthen21e95262016-07-27 17:58:40 -04002837 include_descriptors_from_image: List of file objects for which
2838 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002839 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04002840 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05002841 release_string: None or avbtool release string.
2842 append_to_release_string: None or string to append.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002843 required_libavb_version_minor: Use at least this required minor version.
David Zeuthen21e95262016-07-27 17:58:40 -04002844
2845 Returns:
2846 A bytearray() with the VBMeta blob.
2847
2848 Raises:
2849 Exception: If the |algorithm_name| is not found, if no key has
2850 been given and the given algorithm requires one, or the key is
2851 of the wrong size.
2852
2853 """
2854 try:
2855 alg = ALGORITHMS[algorithm_name]
2856 except KeyError:
2857 raise AvbError('Unknown algorithm with name {}'.format(algorithm_name))
2858
David Zeuthena5fd3a42017-02-27 16:38:54 -05002859 if not descriptors:
2860 descriptors = []
2861
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002862 h = AvbVBMetaHeader()
2863 h.bump_required_libavb_version_minor(required_libavb_version_minor)
2864
David Zeuthena5fd3a42017-02-27 16:38:54 -05002865 # Insert chained partition descriptors, if any
2866 if chain_partitions:
David Zeuthend8e48582017-04-21 11:31:51 -04002867 used_locations = {}
David Zeuthena5fd3a42017-02-27 16:38:54 -05002868 for cp in chain_partitions:
2869 cp_tokens = cp.split(':')
2870 if len(cp_tokens) != 3:
2871 raise AvbError('Malformed chained partition "{}".'.format(cp))
David Zeuthend8e48582017-04-21 11:31:51 -04002872 partition_name = cp_tokens[0]
2873 rollback_index_location = int(cp_tokens[1])
2874 file_path = cp_tokens[2]
2875 # Check that the same rollback location isn't being used by
2876 # multiple chained partitions.
2877 if used_locations.get(rollback_index_location):
2878 raise AvbError('Rollback Index Location {} is already in use.'.format(
2879 rollback_index_location))
2880 used_locations[rollback_index_location] = True
David Zeuthena5fd3a42017-02-27 16:38:54 -05002881 desc = AvbChainPartitionDescriptor()
David Zeuthend8e48582017-04-21 11:31:51 -04002882 desc.partition_name = partition_name
2883 desc.rollback_index_location = rollback_index_location
David Zeuthena5fd3a42017-02-27 16:38:54 -05002884 if desc.rollback_index_location < 1:
2885 raise AvbError('Rollback index location must be 1 or larger.')
David Zeuthena5fd3a42017-02-27 16:38:54 -05002886 desc.public_key = open(file_path, 'rb').read()
2887 descriptors.append(desc)
2888
David Zeuthen21e95262016-07-27 17:58:40 -04002889 # Descriptors.
2890 encoded_descriptors = bytearray()
David Zeuthena5fd3a42017-02-27 16:38:54 -05002891 for desc in descriptors:
2892 encoded_descriptors.extend(desc.encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002893
2894 # Add properties.
2895 if props:
2896 for prop in props:
2897 idx = prop.find(':')
2898 if idx == -1:
2899 raise AvbError('Malformed property "{}".'.format(prop))
Jan Monsch23e0c622019-12-11 11:23:58 +01002900 # pylint: disable=redefined-variable-type
David Zeuthen21e95262016-07-27 17:58:40 -04002901 desc = AvbPropertyDescriptor()
2902 desc.key = prop[0:idx]
2903 desc.value = prop[(idx + 1):]
2904 encoded_descriptors.extend(desc.encode())
2905 if props_from_file:
2906 for prop in props_from_file:
2907 idx = prop.find(':')
2908 if idx == -1:
2909 raise AvbError('Malformed property "{}".'.format(prop))
2910 desc = AvbPropertyDescriptor()
2911 desc.key = prop[0:idx]
2912 desc.value = prop[(idx + 1):]
2913 file_path = prop[(idx + 1):]
2914 desc.value = open(file_path, 'rb').read()
2915 encoded_descriptors.extend(desc.encode())
2916
David Zeuthen73f2afa2017-05-17 16:54:11 -04002917 # Add AvbKernelCmdline descriptor for dm-verity from an image, if requested.
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002918 if setup_rootfs_from_kernel:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002919 image_handler = ImageHandler(
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002920 setup_rootfs_from_kernel.name)
David Zeuthenfd41eb92016-11-17 12:24:47 -05002921 cmdline_desc = self._get_cmdline_descriptors_for_dm_verity(image_handler)
2922 encoded_descriptors.extend(cmdline_desc[0].encode())
2923 encoded_descriptors.extend(cmdline_desc[1].encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002924
David Zeuthen73f2afa2017-05-17 16:54:11 -04002925 # Add AvbKernelCmdline descriptor for dm-verity from desc, if requested.
2926 if ht_desc_to_setup:
2927 cmdline_desc = self._get_cmdline_descriptors_for_hashtree_descriptor(
2928 ht_desc_to_setup)
2929 encoded_descriptors.extend(cmdline_desc[0].encode())
2930 encoded_descriptors.extend(cmdline_desc[1].encode())
2931
David Zeuthen21e95262016-07-27 17:58:40 -04002932 # Add kernel command-lines.
2933 if kernel_cmdlines:
2934 for i in kernel_cmdlines:
2935 desc = AvbKernelCmdlineDescriptor()
2936 desc.kernel_cmdline = i
2937 encoded_descriptors.extend(desc.encode())
2938
2939 # Add descriptors from other images.
2940 if include_descriptors_from_image:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07002941 descriptors_dict = dict()
David Zeuthen21e95262016-07-27 17:58:40 -04002942 for image in include_descriptors_from_image:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002943 image_handler = ImageHandler(image.name)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002944 (_, image_vbmeta_header, image_descriptors, _) = self._parse_image(
2945 image_handler)
2946 # Bump the required libavb version to support all included descriptors.
2947 h.bump_required_libavb_version_minor(
2948 image_vbmeta_header.required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04002949 for desc in image_descriptors:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07002950 # The --include_descriptors_from_image option is used in some setups
2951 # with images A and B where both A and B contain a descriptor
2952 # for a partition with the same name. Since it's not meaningful
2953 # to include both descriptors, only include the last seen descriptor.
2954 # See bug 76386656 for details.
2955 if hasattr(desc, 'partition_name'):
2956 key = type(desc).__name__ + '_' + desc.partition_name
2957 descriptors_dict[key] = desc.encode()
2958 else:
2959 encoded_descriptors.extend(desc.encode())
Jan Monschfe00c0a2019-12-11 11:19:40 +01002960 for key in sorted(descriptors_dict):
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07002961 encoded_descriptors.extend(descriptors_dict[key])
David Zeuthen21e95262016-07-27 17:58:40 -04002962
David Zeuthen18666ab2016-11-15 11:18:05 -05002963 # Load public key metadata blob, if requested.
2964 pkmd_blob = []
2965 if public_key_metadata_path:
2966 with open(public_key_metadata_path) as f:
2967 pkmd_blob = f.read()
2968
David Zeuthen21e95262016-07-27 17:58:40 -04002969 key = None
2970 encoded_key = bytearray()
2971 if alg.public_key_num_bytes > 0:
2972 if not key_path:
2973 raise AvbError('Key is required for algorithm {}'.format(
2974 algorithm_name))
David Zeuthenc68f0822017-03-31 17:22:35 -04002975 encoded_key = encode_rsa_key(key_path)
David Zeuthen21e95262016-07-27 17:58:40 -04002976 if len(encoded_key) != alg.public_key_num_bytes:
2977 raise AvbError('Key is wrong size for algorithm {}'.format(
2978 algorithm_name))
2979
David Zeuthene3cadca2017-02-22 21:25:46 -05002980 # Override release string, if requested.
Jan Monsch23e0c622019-12-11 11:23:58 +01002981 # pylint: disable=unicode-builtin
David Zeuthene3cadca2017-02-22 21:25:46 -05002982 if isinstance(release_string, (str, unicode)):
2983 h.release_string = release_string
2984
2985 # Append to release string, if requested. Also insert a space before.
2986 if isinstance(append_to_release_string, (str, unicode)):
2987 h.release_string += ' ' + append_to_release_string
2988
David Zeuthen18666ab2016-11-15 11:18:05 -05002989 # For the Auxiliary data block, descriptors are stored at offset 0,
2990 # followed by the public key, followed by the public key metadata blob.
David Zeuthen21e95262016-07-27 17:58:40 -04002991 h.auxiliary_data_block_size = round_to_multiple(
David Zeuthen18666ab2016-11-15 11:18:05 -05002992 len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64)
David Zeuthen21e95262016-07-27 17:58:40 -04002993 h.descriptors_offset = 0
2994 h.descriptors_size = len(encoded_descriptors)
2995 h.public_key_offset = h.descriptors_size
2996 h.public_key_size = len(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002997 h.public_key_metadata_offset = h.public_key_offset + h.public_key_size
2998 h.public_key_metadata_size = len(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04002999
3000 # For the Authentication data block, the hash is first and then
3001 # the signature.
3002 h.authentication_data_block_size = round_to_multiple(
David Zeuthend5db21d2017-01-24 10:11:38 -05003003 alg.hash_num_bytes + alg.signature_num_bytes, 64)
David Zeuthen21e95262016-07-27 17:58:40 -04003004 h.algorithm_type = alg.algorithm_type
3005 h.hash_offset = 0
3006 h.hash_size = alg.hash_num_bytes
3007 # Signature offset and size - it's stored right after the hash
3008 # (in Authentication data block).
3009 h.signature_offset = alg.hash_num_bytes
3010 h.signature_size = alg.signature_num_bytes
3011
3012 h.rollback_index = rollback_index
David Zeuthenfd41eb92016-11-17 12:24:47 -05003013 h.flags = flags
David Zeuthen21e95262016-07-27 17:58:40 -04003014
3015 # Generate Header data block.
3016 header_data_blob = h.encode()
3017
3018 # Generate Auxiliary data block.
3019 aux_data_blob = bytearray()
3020 aux_data_blob.extend(encoded_descriptors)
3021 aux_data_blob.extend(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05003022 aux_data_blob.extend(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003023 padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob)
3024 aux_data_blob.extend('\0' * padding_bytes)
3025
3026 # Calculate the hash.
3027 binary_hash = bytearray()
3028 binary_signature = bytearray()
3029 if algorithm_name != 'NONE':
David Zeuthenb623d8b2017-04-04 16:05:53 -04003030 ha = hashlib.new(alg.hash_name)
David Zeuthen21e95262016-07-27 17:58:40 -04003031 ha.update(header_data_blob)
3032 ha.update(aux_data_blob)
3033 binary_hash.extend(ha.digest())
3034
3035 # Calculate the signature.
David Zeuthen21e95262016-07-27 17:58:40 -04003036 padding_and_hash = str(bytearray(alg.padding)) + binary_hash
David Zeuthena156d3d2017-06-01 12:08:09 -04003037 binary_signature.extend(raw_sign(signing_helper,
3038 signing_helper_with_files,
3039 algorithm_name,
Esun Kimff44f232017-03-30 10:34:54 +09003040 alg.signature_num_bytes, key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003041 padding_and_hash))
David Zeuthen21e95262016-07-27 17:58:40 -04003042
3043 # Generate Authentication data block.
3044 auth_data_blob = bytearray()
3045 auth_data_blob.extend(binary_hash)
3046 auth_data_blob.extend(binary_signature)
3047 padding_bytes = h.authentication_data_block_size - len(auth_data_blob)
3048 auth_data_blob.extend('\0' * padding_bytes)
3049
3050 return header_data_blob + auth_data_blob + aux_data_blob
3051
3052 def extract_public_key(self, key_path, output):
3053 """Implements the 'extract_public_key' command.
3054
3055 Arguments:
3056 key_path: The path to a RSA private key file.
3057 output: The file to write to.
3058 """
David Zeuthenc68f0822017-03-31 17:22:35 -04003059 output.write(encode_rsa_key(key_path))
David Zeuthen21e95262016-07-27 17:58:40 -04003060
David Zeuthenb1b994d2017-03-06 18:01:31 -05003061 def append_vbmeta_image(self, image_filename, vbmeta_image_filename,
3062 partition_size):
3063 """Implementation of the append_vbmeta_image command.
3064
3065 Arguments:
3066 image_filename: File to add the footer to.
3067 vbmeta_image_filename: File to get vbmeta struct from.
3068 partition_size: Size of partition.
3069
3070 Raises:
3071 AvbError: If an argument is incorrect.
3072 """
3073 image = ImageHandler(image_filename)
3074
3075 if partition_size % image.block_size != 0:
3076 raise AvbError('Partition size of {} is not a multiple of the image '
3077 'block size {}.'.format(partition_size,
3078 image.block_size))
3079
3080 # If there's already a footer, truncate the image to its original
3081 # size. This way 'avbtool append_vbmeta_image' is idempotent.
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003082 if image.image_size >= AvbFooter.SIZE:
3083 image.seek(image.image_size - AvbFooter.SIZE)
3084 try:
3085 footer = AvbFooter(image.read(AvbFooter.SIZE))
3086 # Existing footer found. Just truncate.
3087 original_image_size = footer.original_image_size
3088 image.truncate(footer.original_image_size)
3089 except (LookupError, struct.error):
3090 original_image_size = image.image_size
3091 else:
3092 # Image size is too small to possibly contain a footer.
David Zeuthenb1b994d2017-03-06 18:01:31 -05003093 original_image_size = image.image_size
3094
3095 # If anything goes wrong from here-on, restore the image back to
3096 # its original size.
3097 try:
3098 vbmeta_image_handler = ImageHandler(vbmeta_image_filename)
3099 vbmeta_blob = self._load_vbmeta_blob(vbmeta_image_handler)
3100
3101 # If the image isn't sparse, its size might not be a multiple of
3102 # the block size. This will screw up padding later so just grow it.
3103 if image.image_size % image.block_size != 0:
3104 assert not image.is_sparse
3105 padding_needed = image.block_size - (image.image_size%image.block_size)
3106 image.truncate(image.image_size + padding_needed)
3107
3108 # The append_raw() method requires content with size being a
3109 # multiple of |block_size| so add padding as needed. Also record
3110 # where this is written to since we'll need to put that in the
3111 # footer.
3112 vbmeta_offset = image.image_size
3113 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
3114 len(vbmeta_blob))
3115 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
3116
3117 # Append vbmeta blob and footer
3118 image.append_raw(vbmeta_blob_with_padding)
3119 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
3120
3121 # Now insert a DONT_CARE chunk with enough bytes such that the
3122 # final Footer block is at the end of partition_size..
3123 image.append_dont_care(partition_size - vbmeta_end_offset -
3124 1*image.block_size)
3125
3126 # Generate the Footer that tells where the VBMeta footer
3127 # is. Also put enough padding in the front of the footer since
3128 # we'll write out an entire block.
3129 footer = AvbFooter()
3130 footer.original_image_size = original_image_size
3131 footer.vbmeta_offset = vbmeta_offset
3132 footer.vbmeta_size = len(vbmeta_blob)
3133 footer_blob = footer.encode()
3134 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
3135 footer_blob)
3136 image.append_raw(footer_blob_with_padding)
3137
3138 except:
3139 # Truncate back to original size, then re-raise
3140 image.truncate(original_image_size)
3141 raise
3142
David Zeuthena4fee8b2016-08-22 15:20:43 -04003143 def add_hash_footer(self, image_filename, partition_size, partition_name,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003144 hash_algorithm, salt, chain_partitions, algorithm_name,
3145 key_path,
3146 public_key_metadata_path, rollback_index, flags, props,
David Zeuthen18666ab2016-11-15 11:18:05 -05003147 props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003148 setup_rootfs_from_kernel,
David Zeuthenbf562452017-05-17 18:04:43 -04003149 include_descriptors_from_image, calc_max_image_size,
David Zeuthena156d3d2017-06-01 12:08:09 -04003150 signing_helper, signing_helper_with_files,
3151 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003152 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003153 print_required_libavb_version, use_persistent_digest,
3154 do_not_use_ab):
David Zeuthena4fee8b2016-08-22 15:20:43 -04003155 """Implementation of the add_hash_footer on unsparse images.
David Zeuthen21e95262016-07-27 17:58:40 -04003156
3157 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003158 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04003159 partition_size: Size of partition.
3160 partition_name: Name of partition (without A/B suffix).
3161 hash_algorithm: Hash algorithm to use.
3162 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003163 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04003164 algorithm_name: Name of algorithm to use.
3165 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05003166 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003167 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003168 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04003169 props: Properties to insert (List of strings of the form 'key:value').
3170 props_from_file: Properties to insert (List of strings 'key:<path>').
3171 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003172 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003173 dm-verity kernel cmdline from.
3174 include_descriptors_from_image: List of file objects for which
3175 to insert descriptors from.
David Zeuthenbf562452017-05-17 18:04:43 -04003176 calc_max_image_size: Don't store the footer - instead calculate the
3177 maximum image size leaving enough room for metadata with the
3178 given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003179 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003180 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003181 release_string: None or avbtool release string.
3182 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05003183 output_vbmeta_image: If not None, also write vbmeta struct to this file.
3184 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04003185 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003186 use_persistent_digest: Use a persistent digest on device.
3187 do_not_use_ab: This partition does not use A/B.
David Zeuthena4fee8b2016-08-22 15:20:43 -04003188
3189 Raises:
3190 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04003191 """
David Zeuthen1097a782017-05-31 15:53:17 -04003192
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003193 required_libavb_version_minor = 0
3194 if use_persistent_digest or do_not_use_ab:
3195 required_libavb_version_minor = 1
3196
David Zeuthen1097a782017-05-31 15:53:17 -04003197 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003198 if print_required_libavb_version:
Jan Monsch23e0c622019-12-11 11:23:58 +01003199 print('1.{}'.format(required_libavb_version_minor))
David Zeuthen1097a782017-05-31 15:53:17 -04003200 return
3201
David Zeuthenbf562452017-05-17 18:04:43 -04003202 # First, calculate the maximum image size such that an image
3203 # this size + metadata (footer + vbmeta struct) fits in
3204 # |partition_size|.
3205 max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003206 if partition_size < max_metadata_size:
3207 raise AvbError('Parition size of {} is too small. '
3208 'Needs to be at least {}'.format(
3209 partition_size, max_metadata_size))
David Zeuthenbf562452017-05-17 18:04:43 -04003210 max_image_size = partition_size - max_metadata_size
3211
3212 # If we're asked to only calculate the maximum image size, we're done.
3213 if calc_max_image_size:
Jan Monsch23e0c622019-12-11 11:23:58 +01003214 print('{}'.format(max_image_size))
David Zeuthenbf562452017-05-17 18:04:43 -04003215 return
3216
David Zeuthena4fee8b2016-08-22 15:20:43 -04003217 image = ImageHandler(image_filename)
3218
3219 if partition_size % image.block_size != 0:
3220 raise AvbError('Partition size of {} is not a multiple of the image '
3221 'block size {}.'.format(partition_size,
3222 image.block_size))
3223
David Zeuthen21e95262016-07-27 17:58:40 -04003224 # If there's already a footer, truncate the image to its original
3225 # size. This way 'avbtool add_hash_footer' is idempotent (modulo
3226 # salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003227 if image.image_size >= AvbFooter.SIZE:
3228 image.seek(image.image_size - AvbFooter.SIZE)
3229 try:
3230 footer = AvbFooter(image.read(AvbFooter.SIZE))
3231 # Existing footer found. Just truncate.
3232 original_image_size = footer.original_image_size
3233 image.truncate(footer.original_image_size)
3234 except (LookupError, struct.error):
3235 original_image_size = image.image_size
3236 else:
3237 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04003238 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003239
3240 # If anything goes wrong from here-on, restore the image back to
3241 # its original size.
3242 try:
David Zeuthen09692692016-09-30 16:16:40 -04003243 # If image size exceeds the maximum image size, fail.
3244 if image.image_size > max_image_size:
3245 raise AvbError('Image size of {} exceeds maximum image '
3246 'size of {} in order to fit in a partition '
3247 'size of {}.'.format(image.image_size, max_image_size,
3248 partition_size))
3249
David Zeuthen21e95262016-07-27 17:58:40 -04003250 digest_size = len(hashlib.new(name=hash_algorithm).digest())
3251 if salt:
Jan Monsch23e0c622019-12-11 11:23:58 +01003252 salt = binascii.unhexlify(salt)
3253 elif salt is None and not use_persistent_digest:
3254 # If salt is not explicitly specified, choose a hash that's the same
3255 # size as the hash size. Don't populate a random salt if this
3256 # descriptor is being created to use a persistent digest on device.
3257 hash_size = digest_size
3258 salt = open('/dev/urandom').read(hash_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003259 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01003260 salt = ''
David Zeuthen21e95262016-07-27 17:58:40 -04003261
3262 hasher = hashlib.new(name=hash_algorithm, string=salt)
3263 # TODO(zeuthen): might want to read this in chunks to avoid
3264 # memory pressure, then again, this is only supposed to be used
3265 # on kernel/initramfs partitions. Possible optimization.
3266 image.seek(0)
David Zeuthen09692692016-09-30 16:16:40 -04003267 hasher.update(image.read(image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003268 digest = hasher.digest()
3269
3270 h_desc = AvbHashDescriptor()
David Zeuthen09692692016-09-30 16:16:40 -04003271 h_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003272 h_desc.hash_algorithm = hash_algorithm
3273 h_desc.partition_name = partition_name
3274 h_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003275 h_desc.flags = 0
3276 if do_not_use_ab:
3277 h_desc.flags |= 1 # AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
3278 if not use_persistent_digest:
3279 h_desc.digest = digest
David Zeuthen21e95262016-07-27 17:58:40 -04003280
3281 # Generate the VBMeta footer.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003282 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04003283 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003284 algorithm_name, key_path, public_key_metadata_path, [h_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05003285 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003286 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003287 include_descriptors_from_image, signing_helper,
3288 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003289 append_to_release_string, required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04003290
David Zeuthend247fcb2017-02-16 12:09:27 -05003291 # Write vbmeta blob, if requested.
3292 if output_vbmeta_image:
3293 output_vbmeta_image.write(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003294
David Zeuthend247fcb2017-02-16 12:09:27 -05003295 # Append vbmeta blob and footer, unless requested not to.
3296 if not do_not_append_vbmeta_image:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003297 # If the image isn't sparse, its size might not be a multiple of
3298 # the block size. This will screw up padding later so just grow it.
3299 if image.image_size % image.block_size != 0:
3300 assert not image.is_sparse
3301 padding_needed = image.block_size - (
3302 image.image_size % image.block_size)
3303 image.truncate(image.image_size + padding_needed)
3304
3305 # The append_raw() method requires content with size being a
3306 # multiple of |block_size| so add padding as needed. Also record
3307 # where this is written to since we'll need to put that in the
3308 # footer.
3309 vbmeta_offset = image.image_size
3310 padding_needed = (
3311 round_to_multiple(len(vbmeta_blob), image.block_size) -
3312 len(vbmeta_blob))
3313 vbmeta_blob_with_padding = vbmeta_blob + '\0' * padding_needed
3314
David Zeuthend247fcb2017-02-16 12:09:27 -05003315 image.append_raw(vbmeta_blob_with_padding)
3316 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
3317
3318 # Now insert a DONT_CARE chunk with enough bytes such that the
3319 # final Footer block is at the end of partition_size..
3320 image.append_dont_care(partition_size - vbmeta_end_offset -
3321 1*image.block_size)
3322
3323 # Generate the Footer that tells where the VBMeta footer
3324 # is. Also put enough padding in the front of the footer since
3325 # we'll write out an entire block.
3326 footer = AvbFooter()
3327 footer.original_image_size = original_image_size
3328 footer.vbmeta_offset = vbmeta_offset
3329 footer.vbmeta_size = len(vbmeta_blob)
3330 footer_blob = footer.encode()
3331 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
3332 footer_blob)
3333 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003334
David Zeuthen21e95262016-07-27 17:58:40 -04003335 except:
3336 # Truncate back to original size, then re-raise
3337 image.truncate(original_image_size)
3338 raise
3339
David Zeuthena4fee8b2016-08-22 15:20:43 -04003340 def add_hashtree_footer(self, image_filename, partition_size, partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003341 generate_fec, fec_num_roots, hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003342 block_size, salt, chain_partitions, algorithm_name,
3343 key_path,
3344 public_key_metadata_path, rollback_index, flags,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003345 props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003346 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003347 setup_as_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04003348 include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05003349 calc_max_image_size, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003350 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05003351 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003352 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003353 print_required_libavb_version,
Jan Monscheeb28b62019-12-05 16:17:09 +01003354 use_persistent_root_digest, do_not_use_ab,
3355 no_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04003356 """Implements the 'add_hashtree_footer' command.
3357
3358 See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
3359 more information about dm-verity and these hashes.
3360
3361 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003362 image_filename: File to add the footer to.
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003363 partition_size: Size of partition or 0 to put it right at the end.
David Zeuthen21e95262016-07-27 17:58:40 -04003364 partition_name: Name of partition (without A/B suffix).
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003365 generate_fec: If True, generate FEC codes.
3366 fec_num_roots: Number of roots for FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04003367 hash_algorithm: Hash algorithm to use.
3368 block_size: Block size to use.
3369 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003370 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04003371 algorithm_name: Name of algorithm to use.
3372 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05003373 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003374 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003375 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04003376 props: Properties to insert (List of strings of the form 'key:value').
3377 props_from_file: Properties to insert (List of strings 'key:<path>').
3378 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003379 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003380 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003381 setup_as_rootfs_from_kernel: If True, generate dm-verity kernel
3382 cmdline to set up rootfs.
David Zeuthen21e95262016-07-27 17:58:40 -04003383 include_descriptors_from_image: List of file objects for which
3384 to insert descriptors from.
David Zeuthen09692692016-09-30 16:16:40 -04003385 calc_max_image_size: Don't store the hashtree or footer - instead
3386 calculate the maximum image size leaving enough room for hashtree
3387 and metadata with the given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003388 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003389 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003390 release_string: None or avbtool release string.
3391 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05003392 output_vbmeta_image: If not None, also write vbmeta struct to this file.
3393 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04003394 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003395 use_persistent_root_digest: Use a persistent root digest on device.
3396 do_not_use_ab: The partition does not use A/B.
Jooyung Hand7221942019-06-17 13:19:57 +09003397 no_hashtree: Do not append hashtree. Set size in descriptor as zero.
David Zeuthena4fee8b2016-08-22 15:20:43 -04003398
3399 Raises:
3400 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04003401 """
David Zeuthen1097a782017-05-31 15:53:17 -04003402
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003403 required_libavb_version_minor = 0
3404 if use_persistent_root_digest or do_not_use_ab:
3405 required_libavb_version_minor = 1
3406
David Zeuthen1097a782017-05-31 15:53:17 -04003407 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003408 if print_required_libavb_version:
Jan Monsch23e0c622019-12-11 11:23:58 +01003409 print('1.{}'.format(required_libavb_version_minor))
David Zeuthen1097a782017-05-31 15:53:17 -04003410 return
3411
David Zeuthen09692692016-09-30 16:16:40 -04003412 digest_size = len(hashlib.new(name=hash_algorithm).digest())
3413 digest_padding = round_to_pow2(digest_size) - digest_size
3414
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003415 # If |partition_size| is given (e.g. not 0), calculate the maximum image
3416 # size such that an image this size + the hashtree + metadata (footer +
3417 # vbmeta struct) fits in |partition_size|. We use very conservative figures
3418 # for metadata.
3419 if partition_size > 0:
Jooyung Hand7221942019-06-17 13:19:57 +09003420 max_tree_size = 0
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003421 max_fec_size = 0
Jooyung Hand7221942019-06-17 13:19:57 +09003422 if not no_hashtree:
3423 (_, max_tree_size) = calc_hash_level_offsets(
3424 partition_size, block_size, digest_size + digest_padding)
3425 if generate_fec:
3426 max_fec_size = calc_fec_data_size(partition_size, fec_num_roots)
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003427 max_metadata_size = (max_fec_size + max_tree_size +
3428 self.MAX_VBMETA_SIZE +
3429 self.MAX_FOOTER_SIZE)
3430 max_image_size = partition_size - max_metadata_size
3431 else:
3432 max_image_size = 0
David Zeuthen09692692016-09-30 16:16:40 -04003433
3434 # If we're asked to only calculate the maximum image size, we're done.
3435 if calc_max_image_size:
Jan Monsch23e0c622019-12-11 11:23:58 +01003436 print('{}'.format(max_image_size))
David Zeuthen09692692016-09-30 16:16:40 -04003437 return
3438
David Zeuthena4fee8b2016-08-22 15:20:43 -04003439 image = ImageHandler(image_filename)
3440
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003441 if partition_size > 0:
3442 if partition_size % image.block_size != 0:
3443 raise AvbError('Partition size of {} is not a multiple of the image '
3444 'block size {}.'.format(partition_size,
3445 image.block_size))
Jan Monsch23e0c622019-12-11 11:23:58 +01003446 elif image.image_size % image.block_size != 0:
3447 raise AvbError('File size of {} is not a multiple of the image '
3448 'block size {}.'.format(image.image_size,
3449 image.block_size))
David Zeuthena4fee8b2016-08-22 15:20:43 -04003450
David Zeuthen21e95262016-07-27 17:58:40 -04003451 # If there's already a footer, truncate the image to its original
3452 # size. This way 'avbtool add_hashtree_footer' is idempotent
3453 # (modulo salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003454 if image.image_size >= AvbFooter.SIZE:
3455 image.seek(image.image_size - AvbFooter.SIZE)
3456 try:
3457 footer = AvbFooter(image.read(AvbFooter.SIZE))
3458 # Existing footer found. Just truncate.
3459 original_image_size = footer.original_image_size
3460 image.truncate(footer.original_image_size)
3461 except (LookupError, struct.error):
3462 original_image_size = image.image_size
3463 else:
3464 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04003465 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003466
3467 # If anything goes wrong from here-on, restore the image back to
3468 # its original size.
3469 try:
3470 # Ensure image is multiple of block_size.
David Zeuthen09692692016-09-30 16:16:40 -04003471 rounded_image_size = round_to_multiple(image.image_size, block_size)
3472 if rounded_image_size > image.image_size:
3473 image.append_raw('\0' * (rounded_image_size - image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003474
David Zeuthen09692692016-09-30 16:16:40 -04003475 # If image size exceeds the maximum image size, fail.
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003476 if partition_size > 0:
3477 if image.image_size > max_image_size:
3478 raise AvbError('Image size of {} exceeds maximum image '
3479 'size of {} in order to fit in a partition '
3480 'size of {}.'.format(image.image_size, max_image_size,
3481 partition_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003482
3483 if salt:
Jan Monsch23e0c622019-12-11 11:23:58 +01003484 salt = binascii.unhexlify(salt)
3485 elif salt is None and not use_persistent_root_digest:
3486 # If salt is not explicitly specified, choose a hash that's the same
3487 # size as the hash size. Don't populate a random salt if this
3488 # descriptor is being created to use a persistent digest on device.
3489 hash_size = digest_size
3490 salt = open('/dev/urandom').read(hash_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003491 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01003492 salt = ''
David Zeuthen21e95262016-07-27 17:58:40 -04003493
David Zeuthena4fee8b2016-08-22 15:20:43 -04003494 # Hashes are stored upside down so we need to calculate hash
David Zeuthen21e95262016-07-27 17:58:40 -04003495 # offsets in advance.
3496 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
David Zeuthen09692692016-09-30 16:16:40 -04003497 image.image_size, block_size, digest_size + digest_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04003498
David Zeuthena4fee8b2016-08-22 15:20:43 -04003499 # If the image isn't sparse, its size might not be a multiple of
3500 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04003501 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003502 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04003503 padding_needed = image.block_size - (image.image_size%image.block_size)
3504 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04003505
David Zeuthena4fee8b2016-08-22 15:20:43 -04003506 # Generate the tree and add padding as needed.
David Zeuthen09692692016-09-30 16:16:40 -04003507 tree_offset = image.image_size
3508 root_digest, hash_tree = generate_hash_tree(image, image.image_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003509 block_size,
3510 hash_algorithm, salt,
3511 digest_padding,
3512 hash_level_offsets,
3513 tree_size)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003514
3515 # Generate HashtreeDescriptor with details about the tree we
3516 # just generated.
Jooyung Hand7221942019-06-17 13:19:57 +09003517 if no_hashtree:
3518 tree_size = 0
3519 hash_tree = bytearray()
David Zeuthen21e95262016-07-27 17:58:40 -04003520 ht_desc = AvbHashtreeDescriptor()
3521 ht_desc.dm_verity_version = 1
David Zeuthen09692692016-09-30 16:16:40 -04003522 ht_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003523 ht_desc.tree_offset = tree_offset
3524 ht_desc.tree_size = tree_size
3525 ht_desc.data_block_size = block_size
3526 ht_desc.hash_block_size = block_size
3527 ht_desc.hash_algorithm = hash_algorithm
3528 ht_desc.partition_name = partition_name
3529 ht_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003530 if do_not_use_ab:
3531 ht_desc.flags |= 1 # AVB_HASHTREE_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
3532 if not use_persistent_root_digest:
3533 ht_desc.root_digest = root_digest
David Zeuthen21e95262016-07-27 17:58:40 -04003534
David Zeuthen09692692016-09-30 16:16:40 -04003535 # Write the hash tree
3536 padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
3537 len(hash_tree))
3538 hash_tree_with_padding = hash_tree + '\0'*padding_needed
3539 image.append_raw(hash_tree_with_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003540 len_hashtree_and_fec = len(hash_tree_with_padding)
3541
3542 # Generate FEC codes, if requested.
3543 if generate_fec:
Jooyung Hand7221942019-06-17 13:19:57 +09003544 if no_hashtree:
3545 fec_data = bytearray()
Tao Bao868db2a2019-09-09 13:35:05 -07003546 else:
3547 fec_data = generate_fec_data(image_filename, fec_num_roots)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003548 padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
3549 len(fec_data))
3550 fec_data_with_padding = fec_data + '\0'*padding_needed
3551 fec_offset = image.image_size
3552 image.append_raw(fec_data_with_padding)
3553 len_hashtree_and_fec += len(fec_data_with_padding)
3554 # Update the hashtree descriptor.
3555 ht_desc.fec_num_roots = fec_num_roots
3556 ht_desc.fec_offset = fec_offset
3557 ht_desc.fec_size = len(fec_data)
David Zeuthen09692692016-09-30 16:16:40 -04003558
David Zeuthen73f2afa2017-05-17 16:54:11 -04003559 ht_desc_to_setup = None
3560 if setup_as_rootfs_from_kernel:
3561 ht_desc_to_setup = ht_desc
3562
David Zeuthena4fee8b2016-08-22 15:20:43 -04003563 # Generate the VBMeta footer and add padding as needed.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003564 vbmeta_offset = tree_offset + len_hashtree_and_fec
David Zeuthen21e95262016-07-27 17:58:40 -04003565 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003566 algorithm_name, key_path, public_key_metadata_path, [ht_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05003567 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003568 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003569 include_descriptors_from_image, signing_helper,
3570 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003571 append_to_release_string, required_libavb_version_minor)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003572 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
3573 len(vbmeta_blob))
3574 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
David Zeuthen21e95262016-07-27 17:58:40 -04003575
David Zeuthend247fcb2017-02-16 12:09:27 -05003576 # Write vbmeta blob, if requested.
3577 if output_vbmeta_image:
3578 output_vbmeta_image.write(vbmeta_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003579
David Zeuthend247fcb2017-02-16 12:09:27 -05003580 # Append vbmeta blob and footer, unless requested not to.
3581 if not do_not_append_vbmeta_image:
3582 image.append_raw(vbmeta_blob_with_padding)
3583
3584 # Now insert a DONT_CARE chunk with enough bytes such that the
3585 # final Footer block is at the end of partition_size..
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003586 if partition_size > 0:
3587 image.append_dont_care(partition_size - image.image_size -
3588 1*image.block_size)
David Zeuthend247fcb2017-02-16 12:09:27 -05003589
3590 # Generate the Footer that tells where the VBMeta footer
3591 # is. Also put enough padding in the front of the footer since
3592 # we'll write out an entire block.
3593 footer = AvbFooter()
3594 footer.original_image_size = original_image_size
3595 footer.vbmeta_offset = vbmeta_offset
3596 footer.vbmeta_size = len(vbmeta_blob)
3597 footer_blob = footer.encode()
3598 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
3599 footer_blob)
3600 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003601
David Zeuthen21e95262016-07-27 17:58:40 -04003602 except:
David Zeuthen09692692016-09-30 16:16:40 -04003603 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04003604 image.truncate(original_image_size)
3605 raise
3606
David Zeuthenc68f0822017-03-31 17:22:35 -04003607 def make_atx_certificate(self, output, authority_key_path, subject_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003608 subject_key_version, subject,
Darren Krahnfccd64e2018-01-16 17:39:35 -08003609 is_intermediate_authority, usage, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003610 signing_helper_with_files):
Darren Krahn147b08d2016-12-20 16:38:29 -08003611 """Implements the 'make_atx_certificate' command.
3612
3613 Android Things certificates are required for Android Things public key
3614 metadata. They chain the vbmeta signing key for a particular product back to
3615 a fused, permanent root key. These certificates are fixed-length and fixed-
3616 format with the explicit goal of not parsing ASN.1 in bootloader code.
3617
3618 Arguments:
3619 output: Certificate will be written to this file on success.
3620 authority_key_path: A PEM file path with the authority private key.
3621 If None, then a certificate will be created without a
3622 signature. The signature can be created out-of-band
3623 and appended.
David Zeuthenc68f0822017-03-31 17:22:35 -04003624 subject_key_path: Path to a PEM or DER subject public key.
Darren Krahn147b08d2016-12-20 16:38:29 -08003625 subject_key_version: A 64-bit version value. If this is None, the number
3626 of seconds since the epoch is used.
3627 subject: A subject identifier. For Product Signing Key certificates this
3628 should be the same Product ID found in the permanent attributes.
3629 is_intermediate_authority: True if the certificate is for an intermediate
3630 authority.
Darren Krahnfccd64e2018-01-16 17:39:35 -08003631 usage: If not empty, overrides the cert usage with a hash of this value.
Darren Krahn147b08d2016-12-20 16:38:29 -08003632 signing_helper: Program which signs a hash and returns the signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003633 signing_helper_with_files: Same as signing_helper but uses files instead.
Darren Krahn147b08d2016-12-20 16:38:29 -08003634 """
3635 signed_data = bytearray()
3636 signed_data.extend(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04003637 signed_data.extend(encode_rsa_key(subject_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08003638 hasher = hashlib.sha256()
3639 hasher.update(subject)
3640 signed_data.extend(hasher.digest())
Darren Krahnfccd64e2018-01-16 17:39:35 -08003641 if not usage:
3642 usage = 'com.google.android.things.vboot'
3643 if is_intermediate_authority:
3644 usage += '.ca'
Darren Krahn147b08d2016-12-20 16:38:29 -08003645 hasher = hashlib.sha256()
3646 hasher.update(usage)
3647 signed_data.extend(hasher.digest())
Yu Shanc8540812019-07-01 16:54:46 -07003648 if subject_key_version is None:
Darren Krahn147b08d2016-12-20 16:38:29 -08003649 subject_key_version = int(time.time())
3650 signed_data.extend(struct.pack('<Q', subject_key_version))
3651 signature = bytearray()
3652 if authority_key_path:
3653 padding_and_hash = bytearray()
Darren Krahn43e12d82017-02-24 16:26:31 -08003654 algorithm_name = 'SHA512_RSA4096'
Esun Kimff44f232017-03-30 10:34:54 +09003655 alg = ALGORITHMS[algorithm_name]
Jan Monsch23e0c622019-12-11 11:23:58 +01003656 hasher = hashlib.sha512() # pylint: disable=redefined-variable-type
Esun Kimff44f232017-03-30 10:34:54 +09003657 padding_and_hash.extend(alg.padding)
Darren Krahn147b08d2016-12-20 16:38:29 -08003658 hasher.update(signed_data)
3659 padding_and_hash.extend(hasher.digest())
David Zeuthena156d3d2017-06-01 12:08:09 -04003660 signature.extend(raw_sign(signing_helper, signing_helper_with_files,
3661 algorithm_name,
Esun Kimff44f232017-03-30 10:34:54 +09003662 alg.signature_num_bytes, authority_key_path,
3663 padding_and_hash))
Darren Krahn147b08d2016-12-20 16:38:29 -08003664 output.write(signed_data)
3665 output.write(signature)
3666
David Zeuthenc68f0822017-03-31 17:22:35 -04003667 def make_atx_permanent_attributes(self, output, root_authority_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003668 product_id):
3669 """Implements the 'make_atx_permanent_attributes' command.
3670
3671 Android Things permanent attributes are designed to be permanent for a
3672 particular product and a hash of these attributes should be fused into
3673 hardware to enforce this.
3674
3675 Arguments:
3676 output: Attributes will be written to this file on success.
David Zeuthenc68f0822017-03-31 17:22:35 -04003677 root_authority_key_path: Path to a PEM or DER public key for
3678 the root authority.
Darren Krahn147b08d2016-12-20 16:38:29 -08003679 product_id: A 16-byte Product ID.
3680
3681 Raises:
3682 AvbError: If an argument is incorrect.
3683 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01003684 EXPECTED_PRODUCT_ID_SIZE = 16 # pylint: disable=invalid-name
Darren Krahn43e12d82017-02-24 16:26:31 -08003685 if len(product_id) != EXPECTED_PRODUCT_ID_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003686 raise AvbError('Invalid Product ID length.')
3687 output.write(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04003688 output.write(encode_rsa_key(root_authority_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08003689 output.write(product_id)
3690
3691 def make_atx_metadata(self, output, intermediate_key_certificate,
Darren Krahn43e12d82017-02-24 16:26:31 -08003692 product_key_certificate):
Darren Krahn147b08d2016-12-20 16:38:29 -08003693 """Implements the 'make_atx_metadata' command.
3694
3695 Android Things metadata are included in vbmeta images to facilitate
3696 verification. The output of this command can be used as the
3697 public_key_metadata argument to other commands.
3698
3699 Arguments:
3700 output: Metadata will be written to this file on success.
3701 intermediate_key_certificate: A certificate file as output by
3702 make_atx_certificate with
3703 is_intermediate_authority set to true.
3704 product_key_certificate: A certificate file as output by
3705 make_atx_certificate with
3706 is_intermediate_authority set to false.
Darren Krahn147b08d2016-12-20 16:38:29 -08003707
3708 Raises:
3709 AvbError: If an argument is incorrect.
3710 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01003711 EXPECTED_CERTIFICATE_SIZE = 1620 # pylint: disable=invalid-name
Darren Krahn43e12d82017-02-24 16:26:31 -08003712 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003713 raise AvbError('Invalid intermediate key certificate length.')
Darren Krahn43e12d82017-02-24 16:26:31 -08003714 if len(product_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003715 raise AvbError('Invalid product key certificate length.')
3716 output.write(struct.pack('<I', 1)) # Format Version
3717 output.write(intermediate_key_certificate)
3718 output.write(product_key_certificate)
Darren Krahn147b08d2016-12-20 16:38:29 -08003719
Darren Krahnfccd64e2018-01-16 17:39:35 -08003720 def make_atx_unlock_credential(self, output, intermediate_key_certificate,
3721 unlock_key_certificate, challenge_path,
3722 unlock_key_path, signing_helper,
3723 signing_helper_with_files):
3724 """Implements the 'make_atx_unlock_credential' command.
3725
3726 Android Things unlock credentials can be used to authorize the unlock of AVB
3727 on a device. These credentials are presented to an Android Things bootloader
3728 via the fastboot interface in response to a 16-byte challenge. This method
3729 creates all fields of the credential except the challenge signature field
3730 (which is the last field) and can optionally create the challenge signature
3731 field as well if a challenge and the unlock_key_path is provided.
3732
3733 Arguments:
3734 output: The credential will be written to this file on success.
3735 intermediate_key_certificate: A certificate file as output by
3736 make_atx_certificate with
3737 is_intermediate_authority set to true.
3738 unlock_key_certificate: A certificate file as output by
3739 make_atx_certificate with
3740 is_intermediate_authority set to false and the
3741 usage set to
3742 'com.google.android.things.vboot.unlock'.
3743 challenge_path: [optional] A path to the challenge to sign.
3744 unlock_key_path: [optional] A PEM file path with the unlock private key.
3745 signing_helper: Program which signs a hash and returns the signature.
3746 signing_helper_with_files: Same as signing_helper but uses files instead.
3747
3748 Raises:
3749 AvbError: If an argument is incorrect.
3750 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01003751 EXPECTED_CERTIFICATE_SIZE = 1620 # pylint: disable=invalid-name
3752 EXPECTED_CHALLENGE_SIZE = 16 # pylint: disable=invalid-name
Darren Krahnfccd64e2018-01-16 17:39:35 -08003753 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
3754 raise AvbError('Invalid intermediate key certificate length.')
3755 if len(unlock_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
3756 raise AvbError('Invalid product key certificate length.')
3757 challenge = bytearray()
3758 if challenge_path:
3759 with open(challenge_path, 'r') as f:
3760 challenge = f.read()
3761 if len(challenge) != EXPECTED_CHALLENGE_SIZE:
3762 raise AvbError('Invalid unlock challenge length.')
3763 output.write(struct.pack('<I', 1)) # Format Version
3764 output.write(intermediate_key_certificate)
3765 output.write(unlock_key_certificate)
3766 if challenge_path and unlock_key_path:
3767 signature = bytearray()
3768 padding_and_hash = bytearray()
3769 algorithm_name = 'SHA512_RSA4096'
3770 alg = ALGORITHMS[algorithm_name]
3771 hasher = hashlib.sha512()
3772 padding_and_hash.extend(alg.padding)
3773 hasher.update(challenge)
3774 padding_and_hash.extend(hasher.digest())
3775 signature.extend(raw_sign(signing_helper, signing_helper_with_files,
3776 algorithm_name,
3777 alg.signature_num_bytes, unlock_key_path,
3778 padding_and_hash))
3779 output.write(signature)
3780
David Zeuthen21e95262016-07-27 17:58:40 -04003781
3782def calc_hash_level_offsets(image_size, block_size, digest_size):
3783 """Calculate the offsets of all the hash-levels in a Merkle-tree.
3784
3785 Arguments:
3786 image_size: The size of the image to calculate a Merkle-tree for.
3787 block_size: The block size, e.g. 4096.
3788 digest_size: The size of each hash, e.g. 32 for SHA-256.
3789
3790 Returns:
3791 A tuple where the first argument is an array of offsets and the
3792 second is size of the tree, in bytes.
3793 """
3794 level_offsets = []
3795 level_sizes = []
3796 tree_size = 0
3797
3798 num_levels = 0
3799 size = image_size
3800 while size > block_size:
Jan Monsch23e0c622019-12-11 11:23:58 +01003801 num_blocks = (size + block_size - 1) // block_size
David Zeuthen21e95262016-07-27 17:58:40 -04003802 level_size = round_to_multiple(num_blocks * digest_size, block_size)
3803
3804 level_sizes.append(level_size)
3805 tree_size += level_size
3806 num_levels += 1
3807
3808 size = level_size
3809
3810 for n in range(0, num_levels):
3811 offset = 0
3812 for m in range(n + 1, num_levels):
3813 offset += level_sizes[m]
3814 level_offsets.append(offset)
3815
David Zeuthena4fee8b2016-08-22 15:20:43 -04003816 return level_offsets, tree_size
David Zeuthen21e95262016-07-27 17:58:40 -04003817
3818
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003819# See system/extras/libfec/include/fec/io.h for these definitions.
3820FEC_FOOTER_FORMAT = '<LLLLLQ32s'
3821FEC_MAGIC = 0xfecfecfe
3822
3823
3824def calc_fec_data_size(image_size, num_roots):
3825 """Calculates how much space FEC data will take.
3826
Jan Monschfe00c0a2019-12-11 11:19:40 +01003827 Arguments:
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003828 image_size: The size of the image.
3829 num_roots: Number of roots.
3830
3831 Returns:
3832 The number of bytes needed for FEC for an image of the given size
3833 and with the requested number of FEC roots.
3834
3835 Raises:
3836 ValueError: If output from the 'fec' tool is invalid.
3837
3838 """
3839 p = subprocess.Popen(
3840 ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)],
3841 stdout=subprocess.PIPE,
3842 stderr=subprocess.PIPE)
3843 (pout, perr) = p.communicate()
3844 retcode = p.wait()
3845 if retcode != 0:
3846 raise ValueError('Error invoking fec: {}'.format(perr))
3847 return int(pout)
3848
3849
3850def generate_fec_data(image_filename, num_roots):
3851 """Generate FEC codes for an image.
3852
Jan Monschfe00c0a2019-12-11 11:19:40 +01003853 Arguments:
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003854 image_filename: The filename of the image.
3855 num_roots: Number of roots.
3856
3857 Returns:
3858 The FEC data blob.
3859
3860 Raises:
3861 ValueError: If output from the 'fec' tool is invalid.
3862 """
3863 fec_tmpfile = tempfile.NamedTemporaryFile()
3864 subprocess.check_call(
3865 ['fec', '--encode', '--roots', str(num_roots), image_filename,
3866 fec_tmpfile.name],
3867 stderr=open(os.devnull))
3868 fec_data = fec_tmpfile.read()
3869 footer_size = struct.calcsize(FEC_FOOTER_FORMAT)
3870 footer_data = fec_data[-footer_size:]
3871 (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT,
3872 footer_data)
3873 if magic != FEC_MAGIC:
3874 raise ValueError('Unexpected magic in FEC footer')
3875 return fec_data[0:fec_size]
3876
3877
David Zeuthen21e95262016-07-27 17:58:40 -04003878def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003879 digest_padding, hash_level_offsets, tree_size):
David Zeuthen21e95262016-07-27 17:58:40 -04003880 """Generates a Merkle-tree for a file.
3881
Jan Monschfe00c0a2019-12-11 11:19:40 +01003882 Arguments:
David Zeuthen21e95262016-07-27 17:58:40 -04003883 image: The image, as a file.
3884 image_size: The size of the image.
3885 block_size: The block size, e.g. 4096.
3886 hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'.
3887 salt: The salt to use.
3888 digest_padding: The padding for each digest.
David Zeuthen21e95262016-07-27 17:58:40 -04003889 hash_level_offsets: The offsets from calc_hash_level_offsets().
David Zeuthena4fee8b2016-08-22 15:20:43 -04003890 tree_size: The size of the tree, in number of bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04003891
3892 Returns:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003893 A tuple where the first element is the top-level hash and the
3894 second element is the hash-tree.
David Zeuthen21e95262016-07-27 17:58:40 -04003895 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04003896 hash_ret = bytearray(tree_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003897 hash_src_offset = 0
3898 hash_src_size = image_size
3899 level_num = 0
3900 while hash_src_size > block_size:
3901 level_output = ''
David Zeuthen21e95262016-07-27 17:58:40 -04003902 remaining = hash_src_size
3903 while remaining > 0:
3904 hasher = hashlib.new(name=hash_alg_name, string=salt)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003905 # Only read from the file for the first level - for subsequent
3906 # levels, access the array we're building.
3907 if level_num == 0:
3908 image.seek(hash_src_offset + hash_src_size - remaining)
3909 data = image.read(min(remaining, block_size))
3910 else:
3911 offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining
3912 data = hash_ret[offset:offset + block_size]
David Zeuthen21e95262016-07-27 17:58:40 -04003913 hasher.update(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003914
3915 remaining -= len(data)
David Zeuthen21e95262016-07-27 17:58:40 -04003916 if len(data) < block_size:
3917 hasher.update('\0' * (block_size - len(data)))
3918 level_output += hasher.digest()
3919 if digest_padding > 0:
3920 level_output += '\0' * digest_padding
3921
3922 padding_needed = (round_to_multiple(
3923 len(level_output), block_size) - len(level_output))
3924 level_output += '\0' * padding_needed
3925
David Zeuthena4fee8b2016-08-22 15:20:43 -04003926 # Copy level-output into resulting tree.
3927 offset = hash_level_offsets[level_num]
3928 hash_ret[offset:offset + len(level_output)] = level_output
David Zeuthen21e95262016-07-27 17:58:40 -04003929
David Zeuthena4fee8b2016-08-22 15:20:43 -04003930 # Continue on to the next level.
David Zeuthen21e95262016-07-27 17:58:40 -04003931 hash_src_size = len(level_output)
David Zeuthen21e95262016-07-27 17:58:40 -04003932 level_num += 1
3933
3934 hasher = hashlib.new(name=hash_alg_name, string=salt)
3935 hasher.update(level_output)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003936 return hasher.digest(), hash_ret
David Zeuthen21e95262016-07-27 17:58:40 -04003937
3938
3939class AvbTool(object):
3940 """Object for avbtool command-line tool."""
3941
3942 def __init__(self):
3943 """Initializer method."""
3944 self.avb = Avb()
3945
3946 def _add_common_args(self, sub_parser):
3947 """Adds arguments used by several sub-commands.
3948
3949 Arguments:
3950 sub_parser: The parser to add arguments to.
3951 """
3952 sub_parser.add_argument('--algorithm',
3953 help='Algorithm to use (default: NONE)',
3954 metavar='ALGORITHM',
3955 default='NONE')
3956 sub_parser.add_argument('--key',
3957 help='Path to RSA private key file',
3958 metavar='KEY',
3959 required=False)
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003960 sub_parser.add_argument('--signing_helper',
3961 help='Path to helper used for signing',
3962 metavar='APP',
3963 default=None,
3964 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04003965 sub_parser.add_argument('--signing_helper_with_files',
3966 help='Path to helper used for signing using files',
3967 metavar='APP',
3968 default=None,
3969 required=False)
David Zeuthen18666ab2016-11-15 11:18:05 -05003970 sub_parser.add_argument('--public_key_metadata',
3971 help='Path to public key metadata file',
3972 metavar='KEY_METADATA',
3973 required=False)
David Zeuthen21e95262016-07-27 17:58:40 -04003974 sub_parser.add_argument('--rollback_index',
3975 help='Rollback Index',
3976 type=parse_number,
3977 default=0)
David Zeuthene3cadca2017-02-22 21:25:46 -05003978 # This is used internally for unit tests. Do not include in --help output.
3979 sub_parser.add_argument('--internal_release_string',
3980 help=argparse.SUPPRESS)
3981 sub_parser.add_argument('--append_to_release_string',
3982 help='Text to append to release string',
3983 metavar='STR')
David Zeuthen21e95262016-07-27 17:58:40 -04003984 sub_parser.add_argument('--prop',
3985 help='Add property',
3986 metavar='KEY:VALUE',
3987 action='append')
3988 sub_parser.add_argument('--prop_from_file',
3989 help='Add property from file',
3990 metavar='KEY:PATH',
3991 action='append')
3992 sub_parser.add_argument('--kernel_cmdline',
3993 help='Add kernel cmdline',
3994 metavar='CMDLINE',
3995 action='append')
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003996 # TODO(zeuthen): the --setup_rootfs_from_kernel option used to be called
3997 # --generate_dm_verity_cmdline_from_hashtree. Remove support for the latter
3998 # at some future point.
3999 sub_parser.add_argument('--setup_rootfs_from_kernel',
4000 '--generate_dm_verity_cmdline_from_hashtree',
David Zeuthen21e95262016-07-27 17:58:40 -04004001 metavar='IMAGE',
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004002 help='Adds kernel cmdline to set up IMAGE',
David Zeuthen21e95262016-07-27 17:58:40 -04004003 type=argparse.FileType('rb'))
4004 sub_parser.add_argument('--include_descriptors_from_image',
4005 help='Include descriptors from image',
4006 metavar='IMAGE',
4007 action='append',
4008 type=argparse.FileType('rb'))
David Zeuthen1097a782017-05-31 15:53:17 -04004009 sub_parser.add_argument('--print_required_libavb_version',
4010 help=('Don\'t store the footer - '
4011 'instead calculate the required libavb '
4012 'version for the given options.'),
4013 action='store_true')
David Zeuthena5fd3a42017-02-27 16:38:54 -05004014 # These are only allowed from top-level vbmeta and boot-in-lieu-of-vbmeta.
4015 sub_parser.add_argument('--chain_partition',
4016 help='Allow signed integrity-data for partition',
4017 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
4018 action='append')
4019 sub_parser.add_argument('--flags',
4020 help='VBMeta flags',
4021 type=parse_number,
4022 default=0)
4023 sub_parser.add_argument('--set_hashtree_disabled_flag',
4024 help='Set the HASHTREE_DISABLED flag',
4025 action='store_true')
4026
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004027 def _add_common_footer_args(self, sub_parser):
4028 """Adds arguments used by add_*_footer sub-commands.
4029
4030 Arguments:
4031 sub_parser: The parser to add arguments to.
4032 """
4033 sub_parser.add_argument('--use_persistent_digest',
4034 help='Use a persistent digest on device instead of '
4035 'storing the digest in the descriptor. This '
4036 'cannot be used with A/B so must be combined '
4037 'with --do_not_use_ab when an A/B suffix is '
4038 'expected at runtime.',
4039 action='store_true')
4040 sub_parser.add_argument('--do_not_use_ab',
4041 help='The partition does not use A/B even when an '
4042 'A/B suffix is present. This must not be used '
4043 'for vbmeta or chained partitions.',
4044 action='store_true')
4045
David Zeuthena5fd3a42017-02-27 16:38:54 -05004046 def _fixup_common_args(self, args):
4047 """Common fixups needed by subcommands.
4048
4049 Arguments:
4050 args: Arguments to modify.
4051
4052 Returns:
4053 The modified arguments.
4054 """
4055 if args.set_hashtree_disabled_flag:
4056 args.flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED
4057 return args
David Zeuthen21e95262016-07-27 17:58:40 -04004058
4059 def run(self, argv):
4060 """Command-line processor.
4061
4062 Arguments:
4063 argv: Pass sys.argv from main.
4064 """
4065 parser = argparse.ArgumentParser()
4066 subparsers = parser.add_subparsers(title='subcommands')
4067
4068 sub_parser = subparsers.add_parser('version',
4069 help='Prints version of avbtool.')
4070 sub_parser.set_defaults(func=self.version)
4071
4072 sub_parser = subparsers.add_parser('extract_public_key',
4073 help='Extract public key.')
4074 sub_parser.add_argument('--key',
4075 help='Path to RSA private key file',
4076 required=True)
4077 sub_parser.add_argument('--output',
4078 help='Output file name',
4079 type=argparse.FileType('wb'),
4080 required=True)
4081 sub_parser.set_defaults(func=self.extract_public_key)
4082
4083 sub_parser = subparsers.add_parser('make_vbmeta_image',
4084 help='Makes a vbmeta image.')
4085 sub_parser.add_argument('--output',
4086 help='Output file name',
David Zeuthen1097a782017-05-31 15:53:17 -04004087 type=argparse.FileType('wb'))
David Zeuthen97cb5802017-06-01 16:14:05 -04004088 sub_parser.add_argument('--padding_size',
4089 metavar='NUMBER',
4090 help='If non-zero, pads output with NUL bytes so '
Jan Monscheeb28b62019-12-05 16:17:09 +01004091 'its size is a multiple of NUMBER '
4092 '(default: 0)',
David Zeuthen97cb5802017-06-01 16:14:05 -04004093 type=parse_number,
4094 default=0)
David Zeuthen21e95262016-07-27 17:58:40 -04004095 self._add_common_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004096 sub_parser.set_defaults(func=self.make_vbmeta_image)
4097
Dan Austinb12b2c12019-12-15 20:28:02 -08004098
David Zeuthen21e95262016-07-27 17:58:40 -04004099 sub_parser = subparsers.add_parser('add_hash_footer',
4100 help='Add hashes and footer to image.')
4101 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004102 help='Image to add hashes to',
David Zeuthen21e95262016-07-27 17:58:40 -04004103 type=argparse.FileType('rab+'))
4104 sub_parser.add_argument('--partition_size',
4105 help='Partition size',
David Zeuthen1097a782017-05-31 15:53:17 -04004106 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04004107 sub_parser.add_argument('--partition_name',
4108 help='Partition name',
David Zeuthenbf562452017-05-17 18:04:43 -04004109 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04004110 sub_parser.add_argument('--hash_algorithm',
4111 help='Hash algorithm to use (default: sha256)',
4112 default='sha256')
4113 sub_parser.add_argument('--salt',
4114 help='Salt in hex (default: /dev/urandom)')
David Zeuthenbf562452017-05-17 18:04:43 -04004115 sub_parser.add_argument('--calc_max_image_size',
4116 help=('Don\'t store the footer - '
4117 'instead calculate the maximum image size '
4118 'leaving enough room for metadata with '
4119 'the given partition size.'),
4120 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05004121 sub_parser.add_argument('--output_vbmeta_image',
4122 help='Also write vbmeta struct to file',
4123 type=argparse.FileType('wb'))
4124 sub_parser.add_argument('--do_not_append_vbmeta_image',
4125 help=('Do not append vbmeta struct or footer '
4126 'to the image'),
4127 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04004128 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004129 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004130 sub_parser.set_defaults(func=self.add_hash_footer)
4131
David Zeuthenb1b994d2017-03-06 18:01:31 -05004132 sub_parser = subparsers.add_parser('append_vbmeta_image',
4133 help='Append vbmeta image to image.')
4134 sub_parser.add_argument('--image',
4135 help='Image to append vbmeta blob to',
4136 type=argparse.FileType('rab+'))
4137 sub_parser.add_argument('--partition_size',
4138 help='Partition size',
4139 type=parse_number,
4140 required=True)
4141 sub_parser.add_argument('--vbmeta_image',
4142 help='Image with vbmeta blob to append',
4143 type=argparse.FileType('rb'))
4144 sub_parser.set_defaults(func=self.append_vbmeta_image)
4145
Jan Monscheeb28b62019-12-05 16:17:09 +01004146 sub_parser = subparsers.add_parser(
4147 'add_hashtree_footer',
4148 help='Add hashtree and footer to image.')
David Zeuthen21e95262016-07-27 17:58:40 -04004149 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004150 help='Image to add hashtree to',
David Zeuthen21e95262016-07-27 17:58:40 -04004151 type=argparse.FileType('rab+'))
4152 sub_parser.add_argument('--partition_size',
4153 help='Partition size',
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004154 default=0,
David Zeuthen1097a782017-05-31 15:53:17 -04004155 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04004156 sub_parser.add_argument('--partition_name',
4157 help='Partition name',
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004158 default='')
David Zeuthen21e95262016-07-27 17:58:40 -04004159 sub_parser.add_argument('--hash_algorithm',
4160 help='Hash algorithm to use (default: sha1)',
4161 default='sha1')
4162 sub_parser.add_argument('--salt',
4163 help='Salt in hex (default: /dev/urandom)')
4164 sub_parser.add_argument('--block_size',
4165 help='Block size (default: 4096)',
4166 type=parse_number,
4167 default=4096)
David Zeuthenbce9a292017-05-10 17:18:04 -04004168 # TODO(zeuthen): The --generate_fec option was removed when we
4169 # moved to generating FEC by default. To avoid breaking existing
4170 # users needing to transition we simply just print a warning below
4171 # in add_hashtree_footer(). Remove this option and the warning at
4172 # some point in the future.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004173 sub_parser.add_argument('--generate_fec',
David Zeuthenbce9a292017-05-10 17:18:04 -04004174 help=argparse.SUPPRESS,
4175 action='store_true')
Jan Monscheeb28b62019-12-05 16:17:09 +01004176 sub_parser.add_argument(
4177 '--do_not_generate_fec',
4178 help='Do not generate forward-error-correction codes',
4179 action='store_true')
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004180 sub_parser.add_argument('--fec_num_roots',
4181 help='Number of roots for FEC (default: 2)',
4182 type=parse_number,
4183 default=2)
David Zeuthen09692692016-09-30 16:16:40 -04004184 sub_parser.add_argument('--calc_max_image_size',
4185 help=('Don\'t store the hashtree or footer - '
4186 'instead calculate the maximum image size '
4187 'leaving enough room for hashtree '
4188 'and metadata with the given partition '
4189 'size.'),
4190 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05004191 sub_parser.add_argument('--output_vbmeta_image',
4192 help='Also write vbmeta struct to file',
4193 type=argparse.FileType('wb'))
4194 sub_parser.add_argument('--do_not_append_vbmeta_image',
4195 help=('Do not append vbmeta struct or footer '
4196 'to the image'),
4197 action='store_true')
David Zeuthen73f2afa2017-05-17 16:54:11 -04004198 # This is different from --setup_rootfs_from_kernel insofar that
4199 # it doesn't take an IMAGE, the generated cmdline will be for the
4200 # hashtree we're adding.
4201 sub_parser.add_argument('--setup_as_rootfs_from_kernel',
4202 action='store_true',
4203 help='Adds kernel cmdline for setting up rootfs')
Jooyung Hand7221942019-06-17 13:19:57 +09004204 sub_parser.add_argument('--no_hashtree',
4205 action='store_true',
4206 help='Do not append hashtree')
David Zeuthen21e95262016-07-27 17:58:40 -04004207 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004208 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004209 sub_parser.set_defaults(func=self.add_hashtree_footer)
4210
4211 sub_parser = subparsers.add_parser('erase_footer',
4212 help='Erase footer from an image.')
4213 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004214 help='Image with a footer',
David Zeuthen21e95262016-07-27 17:58:40 -04004215 type=argparse.FileType('rwb+'),
4216 required=True)
4217 sub_parser.add_argument('--keep_hashtree',
David Zeuthenfbb61fa2017-02-02 12:11:49 -05004218 help='Keep the hashtree and FEC in the image',
David Zeuthen21e95262016-07-27 17:58:40 -04004219 action='store_true')
4220 sub_parser.set_defaults(func=self.erase_footer)
4221
David Zeuthen1394f762019-04-30 10:20:11 -04004222 sub_parser = subparsers.add_parser('zero_hashtree',
4223 help='Zero out hashtree and FEC data.')
4224 sub_parser.add_argument('--image',
4225 help='Image with a footer',
4226 type=argparse.FileType('rwb+'),
4227 required=True)
4228 sub_parser.set_defaults(func=self.zero_hashtree)
4229
Jan Monscheeb28b62019-12-05 16:17:09 +01004230 sub_parser = subparsers.add_parser(
4231 'extract_vbmeta_image',
4232 help='Extracts vbmeta from an image with a footer.')
David Zeuthen49936b42018-08-07 17:38:58 -04004233 sub_parser.add_argument('--image',
4234 help='Image with footer',
4235 type=argparse.FileType('rb'),
4236 required=True)
4237 sub_parser.add_argument('--output',
4238 help='Output file name',
4239 type=argparse.FileType('wb'))
4240 sub_parser.add_argument('--padding_size',
4241 metavar='NUMBER',
4242 help='If non-zero, pads output with NUL bytes so '
Jan Monscheeb28b62019-12-05 16:17:09 +01004243 'its size is a multiple of NUMBER '
4244 '(default: 0)',
David Zeuthen49936b42018-08-07 17:38:58 -04004245 type=parse_number,
4246 default=0)
4247 sub_parser.set_defaults(func=self.extract_vbmeta_image)
4248
David Zeuthen2bc232b2017-04-19 14:25:19 -04004249 sub_parser = subparsers.add_parser('resize_image',
4250 help='Resize image with a footer.')
4251 sub_parser.add_argument('--image',
4252 help='Image with a footer',
4253 type=argparse.FileType('rwb+'),
4254 required=True)
4255 sub_parser.add_argument('--partition_size',
4256 help='New partition size',
4257 type=parse_number)
4258 sub_parser.set_defaults(func=self.resize_image)
4259
David Zeuthen21e95262016-07-27 17:58:40 -04004260 sub_parser = subparsers.add_parser(
4261 'info_image',
4262 help='Show information about vbmeta or footer.')
4263 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004264 help='Image to show information about',
David Zeuthen21e95262016-07-27 17:58:40 -04004265 type=argparse.FileType('rb'),
4266 required=True)
4267 sub_parser.add_argument('--output',
4268 help='Write info to file',
4269 type=argparse.FileType('wt'),
4270 default=sys.stdout)
4271 sub_parser.set_defaults(func=self.info_image)
4272
David Zeuthenb623d8b2017-04-04 16:05:53 -04004273 sub_parser = subparsers.add_parser(
4274 'verify_image',
4275 help='Verify an image.')
4276 sub_parser.add_argument('--image',
4277 help='Image to verify',
4278 type=argparse.FileType('rb'),
4279 required=True)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04004280 sub_parser.add_argument('--key',
4281 help='Check embedded public key matches KEY',
4282 metavar='KEY',
4283 required=False)
4284 sub_parser.add_argument('--expected_chain_partition',
4285 help='Expected chain partition',
4286 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
4287 action='append')
Jan Monscheeb28b62019-12-05 16:17:09 +01004288 sub_parser.add_argument(
4289 '--follow_chain_partitions',
4290 help=('Follows chain partitions even when not '
4291 'specified with the --expected_chain_partition option'),
4292 action='store_true')
4293 sub_parser.add_argument(
4294 '--accept_zeroed_hashtree',
4295 help=('Accept images where the hashtree or FEC data is zeroed out'),
4296 action='store_true')
David Zeuthenb623d8b2017-04-04 16:05:53 -04004297 sub_parser.set_defaults(func=self.verify_image)
4298
David Zeuthenb8643c02018-05-17 17:21:18 -04004299 sub_parser = subparsers.add_parser(
4300 'calculate_vbmeta_digest',
4301 help='Calculate vbmeta digest.')
4302 sub_parser.add_argument('--image',
4303 help='Image to calculate digest for',
4304 type=argparse.FileType('rb'),
4305 required=True)
4306 sub_parser.add_argument('--hash_algorithm',
4307 help='Hash algorithm to use (default: sha256)',
4308 default='sha256')
4309 sub_parser.add_argument('--output',
4310 help='Write hex digest to file (default: stdout)',
4311 type=argparse.FileType('wt'),
4312 default=sys.stdout)
4313 sub_parser.set_defaults(func=self.calculate_vbmeta_digest)
4314
David Zeuthenf7d2e752018-09-20 13:30:41 -04004315 sub_parser = subparsers.add_parser(
4316 'calculate_kernel_cmdline',
4317 help='Calculate kernel cmdline.')
4318 sub_parser.add_argument('--image',
4319 help='Image to calculate kernel cmdline for',
4320 type=argparse.FileType('rb'),
4321 required=True)
4322 sub_parser.add_argument('--hashtree_disabled',
4323 help='Return the cmdline for hashtree disabled',
4324 action='store_true')
4325 sub_parser.add_argument('--output',
4326 help='Write cmdline to file (default: stdout)',
4327 type=argparse.FileType('wt'),
4328 default=sys.stdout)
4329 sub_parser.set_defaults(func=self.calculate_kernel_cmdline)
4330
David Zeuthen8b6973b2016-09-20 12:39:49 -04004331 sub_parser = subparsers.add_parser('set_ab_metadata',
4332 help='Set A/B metadata.')
4333 sub_parser.add_argument('--misc_image',
4334 help=('The misc image to modify. If the image does '
4335 'not exist, it will be created.'),
4336 type=argparse.FileType('r+b'),
4337 required=True)
4338 sub_parser.add_argument('--slot_data',
4339 help=('Slot data of the form "priority", '
4340 '"tries_remaining", "sucessful_boot" for '
4341 'slot A followed by the same for slot B, '
4342 'separated by colons. The default value '
4343 'is 15:7:0:14:7:0.'),
4344 default='15:7:0:14:7:0')
4345 sub_parser.set_defaults(func=self.set_ab_metadata)
4346
Darren Krahn147b08d2016-12-20 16:38:29 -08004347 sub_parser = subparsers.add_parser(
4348 'make_atx_certificate',
4349 help='Create an Android Things eXtension (ATX) certificate.')
4350 sub_parser.add_argument('--output',
4351 help='Write certificate to file',
4352 type=argparse.FileType('wb'),
4353 default=sys.stdout)
4354 sub_parser.add_argument('--subject',
4355 help=('Path to subject file'),
4356 type=argparse.FileType('rb'),
4357 required=True)
4358 sub_parser.add_argument('--subject_key',
4359 help=('Path to subject RSA public key file'),
4360 type=argparse.FileType('rb'),
4361 required=True)
4362 sub_parser.add_argument('--subject_key_version',
4363 help=('Version of the subject key'),
4364 type=parse_number,
4365 required=False)
4366 sub_parser.add_argument('--subject_is_intermediate_authority',
4367 help=('Generate an intermediate authority '
4368 'certificate'),
4369 action='store_true')
Darren Krahnfccd64e2018-01-16 17:39:35 -08004370 sub_parser.add_argument('--usage',
Darren Krahn2367b462018-06-19 00:53:32 -07004371 help=('Override usage with a hash of the provided '
Darren Krahnfccd64e2018-01-16 17:39:35 -08004372 'string'),
4373 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08004374 sub_parser.add_argument('--authority_key',
4375 help='Path to authority RSA private key file',
4376 required=False)
4377 sub_parser.add_argument('--signing_helper',
4378 help='Path to helper used for signing',
4379 metavar='APP',
4380 default=None,
4381 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04004382 sub_parser.add_argument('--signing_helper_with_files',
4383 help='Path to helper used for signing using files',
4384 metavar='APP',
4385 default=None,
4386 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08004387 sub_parser.set_defaults(func=self.make_atx_certificate)
4388
4389 sub_parser = subparsers.add_parser(
4390 'make_atx_permanent_attributes',
4391 help='Create Android Things eXtension (ATX) permanent attributes.')
4392 sub_parser.add_argument('--output',
4393 help='Write attributes to file',
4394 type=argparse.FileType('wb'),
4395 default=sys.stdout)
4396 sub_parser.add_argument('--root_authority_key',
4397 help='Path to authority RSA public key file',
4398 type=argparse.FileType('rb'),
4399 required=True)
4400 sub_parser.add_argument('--product_id',
4401 help=('Path to Product ID file'),
4402 type=argparse.FileType('rb'),
4403 required=True)
4404 sub_parser.set_defaults(func=self.make_atx_permanent_attributes)
4405
4406 sub_parser = subparsers.add_parser(
4407 'make_atx_metadata',
4408 help='Create Android Things eXtension (ATX) metadata.')
4409 sub_parser.add_argument('--output',
4410 help='Write metadata to file',
4411 type=argparse.FileType('wb'),
4412 default=sys.stdout)
4413 sub_parser.add_argument('--intermediate_key_certificate',
4414 help='Path to intermediate key certificate file',
4415 type=argparse.FileType('rb'),
4416 required=True)
4417 sub_parser.add_argument('--product_key_certificate',
4418 help='Path to product key certificate file',
4419 type=argparse.FileType('rb'),
4420 required=True)
Darren Krahn147b08d2016-12-20 16:38:29 -08004421 sub_parser.set_defaults(func=self.make_atx_metadata)
4422
Darren Krahnfccd64e2018-01-16 17:39:35 -08004423 sub_parser = subparsers.add_parser(
4424 'make_atx_unlock_credential',
4425 help='Create an Android Things eXtension (ATX) unlock credential.')
4426 sub_parser.add_argument('--output',
4427 help='Write credential to file',
4428 type=argparse.FileType('wb'),
4429 default=sys.stdout)
4430 sub_parser.add_argument('--intermediate_key_certificate',
4431 help='Path to intermediate key certificate file',
4432 type=argparse.FileType('rb'),
4433 required=True)
4434 sub_parser.add_argument('--unlock_key_certificate',
4435 help='Path to unlock key certificate file',
4436 type=argparse.FileType('rb'),
4437 required=True)
4438 sub_parser.add_argument('--challenge',
4439 help='Path to the challenge to sign (optional). If '
4440 'this is not provided the challenge signature '
4441 'field is omitted and can be concatenated '
4442 'later.',
4443 required=False)
4444 sub_parser.add_argument('--unlock_key',
4445 help='Path to unlock key (optional). Must be '
4446 'provided if using --challenge.',
4447 required=False)
4448 sub_parser.add_argument('--signing_helper',
4449 help='Path to helper used for signing',
4450 metavar='APP',
4451 default=None,
4452 required=False)
4453 sub_parser.add_argument('--signing_helper_with_files',
4454 help='Path to helper used for signing using files',
4455 metavar='APP',
4456 default=None,
4457 required=False)
4458 sub_parser.set_defaults(func=self.make_atx_unlock_credential)
4459
David Zeuthen21e95262016-07-27 17:58:40 -04004460 args = parser.parse_args(argv[1:])
4461 try:
4462 args.func(args)
4463 except AvbError as e:
Jan Monsch23e0c622019-12-11 11:23:58 +01004464 sys.stderr.write('{}: {}\n'.format(argv[0], str(e)))
David Zeuthen21e95262016-07-27 17:58:40 -04004465 sys.exit(1)
4466
4467 def version(self, _):
4468 """Implements the 'version' sub-command."""
Jan Monsch23e0c622019-12-11 11:23:58 +01004469 print(get_release_string())
David Zeuthen21e95262016-07-27 17:58:40 -04004470
4471 def extract_public_key(self, args):
4472 """Implements the 'extract_public_key' sub-command."""
4473 self.avb.extract_public_key(args.key, args.output)
4474
4475 def make_vbmeta_image(self, args):
4476 """Implements the 'make_vbmeta_image' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004477 args = self._fixup_common_args(args)
David Zeuthen21e95262016-07-27 17:58:40 -04004478 self.avb.make_vbmeta_image(args.output, args.chain_partition,
David Zeuthen18666ab2016-11-15 11:18:05 -05004479 args.algorithm, args.key,
4480 args.public_key_metadata, args.rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05004481 args.flags, args.prop, args.prop_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04004482 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004483 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05004484 args.include_descriptors_from_image,
David Zeuthene3cadca2017-02-22 21:25:46 -05004485 args.signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04004486 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004487 args.internal_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04004488 args.append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04004489 args.print_required_libavb_version,
4490 args.padding_size)
David Zeuthen21e95262016-07-27 17:58:40 -04004491
David Zeuthenb1b994d2017-03-06 18:01:31 -05004492 def append_vbmeta_image(self, args):
4493 """Implements the 'append_vbmeta_image' sub-command."""
4494 self.avb.append_vbmeta_image(args.image.name, args.vbmeta_image.name,
4495 args.partition_size)
4496
David Zeuthen21e95262016-07-27 17:58:40 -04004497 def add_hash_footer(self, args):
4498 """Implements the 'add_hash_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004499 args = self._fixup_common_args(args)
David Zeuthenbf562452017-05-17 18:04:43 -04004500 self.avb.add_hash_footer(args.image.name if args.image else None,
4501 args.partition_size,
David Zeuthen21e95262016-07-27 17:58:40 -04004502 args.partition_name, args.hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004503 args.salt, args.chain_partition, args.algorithm,
4504 args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05004505 args.public_key_metadata, args.rollback_index,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004506 args.flags, args.prop, args.prop_from_file,
David Zeuthen18666ab2016-11-15 11:18:05 -05004507 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004508 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05004509 args.include_descriptors_from_image,
David Zeuthena156d3d2017-06-01 12:08:09 -04004510 args.calc_max_image_size,
4511 args.signing_helper,
4512 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004513 args.internal_release_string,
4514 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05004515 args.output_vbmeta_image,
David Zeuthen1097a782017-05-31 15:53:17 -04004516 args.do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004517 args.print_required_libavb_version,
4518 args.use_persistent_digest,
4519 args.do_not_use_ab)
David Zeuthen21e95262016-07-27 17:58:40 -04004520
4521 def add_hashtree_footer(self, args):
4522 """Implements the 'add_hashtree_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004523 args = self._fixup_common_args(args)
David Zeuthenbce9a292017-05-10 17:18:04 -04004524 # TODO(zeuthen): Remove when removing support for the
4525 # '--generate_fec' option above.
4526 if args.generate_fec:
4527 sys.stderr.write('The --generate_fec option is deprecated since FEC '
4528 'is now generated by default. Use the option '
4529 '--do_not_generate_fec to not generate FEC.\n')
Jan Monscheeb28b62019-12-05 16:17:09 +01004530 self.avb.add_hashtree_footer(
4531 args.image.name if args.image else None,
4532 args.partition_size,
4533 args.partition_name,
4534 not args.do_not_generate_fec, args.fec_num_roots,
4535 args.hash_algorithm, args.block_size,
4536 args.salt, args.chain_partition, args.algorithm,
4537 args.key, args.public_key_metadata,
4538 args.rollback_index, args.flags, args.prop,
4539 args.prop_from_file,
4540 args.kernel_cmdline,
4541 args.setup_rootfs_from_kernel,
4542 args.setup_as_rootfs_from_kernel,
4543 args.include_descriptors_from_image,
4544 args.calc_max_image_size,
4545 args.signing_helper,
4546 args.signing_helper_with_files,
4547 args.internal_release_string,
4548 args.append_to_release_string,
4549 args.output_vbmeta_image,
4550 args.do_not_append_vbmeta_image,
4551 args.print_required_libavb_version,
4552 args.use_persistent_digest,
4553 args.do_not_use_ab,
4554 args.no_hashtree)
David Zeuthend247fcb2017-02-16 12:09:27 -05004555
David Zeuthen21e95262016-07-27 17:58:40 -04004556 def erase_footer(self, args):
4557 """Implements the 'erase_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04004558 self.avb.erase_footer(args.image.name, args.keep_hashtree)
David Zeuthen21e95262016-07-27 17:58:40 -04004559
David Zeuthen1394f762019-04-30 10:20:11 -04004560 def zero_hashtree(self, args):
4561 """Implements the 'zero_hashtree' sub-command."""
4562 self.avb.zero_hashtree(args.image.name)
4563
David Zeuthen49936b42018-08-07 17:38:58 -04004564 def extract_vbmeta_image(self, args):
4565 """Implements the 'extract_vbmeta_image' sub-command."""
4566 self.avb.extract_vbmeta_image(args.output, args.image.name,
4567 args.padding_size)
4568
David Zeuthen2bc232b2017-04-19 14:25:19 -04004569 def resize_image(self, args):
4570 """Implements the 'resize_image' sub-command."""
4571 self.avb.resize_image(args.image.name, args.partition_size)
4572
David Zeuthen8b6973b2016-09-20 12:39:49 -04004573 def set_ab_metadata(self, args):
4574 """Implements the 'set_ab_metadata' sub-command."""
4575 self.avb.set_ab_metadata(args.misc_image, args.slot_data)
4576
David Zeuthen21e95262016-07-27 17:58:40 -04004577 def info_image(self, args):
4578 """Implements the 'info_image' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04004579 self.avb.info_image(args.image.name, args.output)
David Zeuthen21e95262016-07-27 17:58:40 -04004580
David Zeuthenb623d8b2017-04-04 16:05:53 -04004581 def verify_image(self, args):
4582 """Implements the 'verify_image' sub-command."""
David Zeuthen5dfb4e92017-05-24 14:49:32 -04004583 self.avb.verify_image(args.image.name, args.key,
David Zeuthene947cb62019-01-25 15:27:08 -05004584 args.expected_chain_partition,
David Zeuthen1394f762019-04-30 10:20:11 -04004585 args.follow_chain_partitions,
4586 args.accept_zeroed_hashtree)
David Zeuthenb623d8b2017-04-04 16:05:53 -04004587
David Zeuthenb8643c02018-05-17 17:21:18 -04004588 def calculate_vbmeta_digest(self, args):
4589 """Implements the 'calculate_vbmeta_digest' sub-command."""
4590 self.avb.calculate_vbmeta_digest(args.image.name, args.hash_algorithm,
4591 args.output)
4592
David Zeuthenf7d2e752018-09-20 13:30:41 -04004593 def calculate_kernel_cmdline(self, args):
4594 """Implements the 'calculate_kernel_cmdline' sub-command."""
Jan Monscheeb28b62019-12-05 16:17:09 +01004595 self.avb.calculate_kernel_cmdline(args.image.name, args.hashtree_disabled,
4596 args.output)
David Zeuthenf7d2e752018-09-20 13:30:41 -04004597
Darren Krahn147b08d2016-12-20 16:38:29 -08004598 def make_atx_certificate(self, args):
4599 """Implements the 'make_atx_certificate' sub-command."""
4600 self.avb.make_atx_certificate(args.output, args.authority_key,
David Zeuthenc68f0822017-03-31 17:22:35 -04004601 args.subject_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08004602 args.subject_key_version,
4603 args.subject.read(),
4604 args.subject_is_intermediate_authority,
Darren Krahnfccd64e2018-01-16 17:39:35 -08004605 args.usage,
David Zeuthena156d3d2017-06-01 12:08:09 -04004606 args.signing_helper,
4607 args.signing_helper_with_files)
Darren Krahn147b08d2016-12-20 16:38:29 -08004608
4609 def make_atx_permanent_attributes(self, args):
4610 """Implements the 'make_atx_permanent_attributes' sub-command."""
4611 self.avb.make_atx_permanent_attributes(args.output,
David Zeuthenc68f0822017-03-31 17:22:35 -04004612 args.root_authority_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08004613 args.product_id.read())
4614
4615 def make_atx_metadata(self, args):
4616 """Implements the 'make_atx_metadata' sub-command."""
4617 self.avb.make_atx_metadata(args.output,
4618 args.intermediate_key_certificate.read(),
Darren Krahn43e12d82017-02-24 16:26:31 -08004619 args.product_key_certificate.read())
Darren Krahn147b08d2016-12-20 16:38:29 -08004620
Darren Krahnfccd64e2018-01-16 17:39:35 -08004621 def make_atx_unlock_credential(self, args):
4622 """Implements the 'make_atx_unlock_credential' sub-command."""
4623 self.avb.make_atx_unlock_credential(
4624 args.output,
4625 args.intermediate_key_certificate.read(),
4626 args.unlock_key_certificate.read(),
4627 args.challenge,
4628 args.unlock_key,
4629 args.signing_helper,
4630 args.signing_helper_with_files)
4631
David Zeuthen21e95262016-07-27 17:58:40 -04004632
4633if __name__ == '__main__':
4634 tool = AvbTool()
4635 tool.run(sys.argv)