blob: bdac52d8baba93129590ce20d7965c3efde185dc [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|.
Jan Monsch443bf322020-02-19 14:56:44 +01002580
2581 Raises:
2582 AvbError: In case the image cannot be parsed.
David Zeuthen21e95262016-07-27 17:58:40 -04002583 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04002584 assert isinstance(image, ImageHandler)
David Zeuthen21e95262016-07-27 17:58:40 -04002585 footer = None
David Zeuthen09692692016-09-30 16:16:40 -04002586 image.seek(image.image_size - AvbFooter.SIZE)
David Zeuthen21e95262016-07-27 17:58:40 -04002587 try:
2588 footer = AvbFooter(image.read(AvbFooter.SIZE))
2589 except (LookupError, struct.error):
2590 # Nope, just seek back to the start.
2591 image.seek(0)
2592
2593 vbmeta_offset = 0
2594 if footer:
2595 vbmeta_offset = footer.vbmeta_offset
2596
2597 image.seek(vbmeta_offset)
2598 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2599
2600 auth_block_offset = vbmeta_offset + AvbVBMetaHeader.SIZE
2601 aux_block_offset = auth_block_offset + h.authentication_data_block_size
2602 desc_start_offset = aux_block_offset + h.descriptors_offset
2603 image.seek(desc_start_offset)
2604 descriptors = parse_descriptors(image.read(h.descriptors_size))
2605
David Zeuthen09692692016-09-30 16:16:40 -04002606 return footer, h, descriptors, image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04002607
David Zeuthenb1b994d2017-03-06 18:01:31 -05002608 def _load_vbmeta_blob(self, image):
2609 """Gets the vbmeta struct and associated sections.
2610
2611 The image can either be a vbmeta.img or an image with a footer.
2612
2613 Arguments:
2614 image: An ImageHandler (vbmeta or footer).
2615
2616 Returns:
2617 A blob with the vbmeta struct and other sections.
2618 """
2619 assert isinstance(image, ImageHandler)
2620 footer = None
2621 image.seek(image.image_size - AvbFooter.SIZE)
2622 try:
2623 footer = AvbFooter(image.read(AvbFooter.SIZE))
2624 except (LookupError, struct.error):
2625 # Nope, just seek back to the start.
2626 image.seek(0)
2627
2628 vbmeta_offset = 0
2629 if footer:
2630 vbmeta_offset = footer.vbmeta_offset
2631
2632 image.seek(vbmeta_offset)
2633 h = AvbVBMetaHeader(image.read(AvbVBMetaHeader.SIZE))
2634
2635 image.seek(vbmeta_offset)
2636 data_size = AvbVBMetaHeader.SIZE
2637 data_size += h.authentication_data_block_size
2638 data_size += h.auxiliary_data_block_size
2639 return image.read(data_size)
2640
David Zeuthen73f2afa2017-05-17 16:54:11 -04002641 def _get_cmdline_descriptors_for_hashtree_descriptor(self, ht):
David Zeuthenfd41eb92016-11-17 12:24:47 -05002642 """Generate kernel cmdline descriptors for dm-verity.
David Zeuthen21e95262016-07-27 17:58:40 -04002643
2644 Arguments:
David Zeuthen73f2afa2017-05-17 16:54:11 -04002645 ht: A AvbHashtreeDescriptor
David Zeuthen21e95262016-07-27 17:58:40 -04002646
2647 Returns:
David Zeuthenfd41eb92016-11-17 12:24:47 -05002648 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2649 instructions. There is one for when hashtree is not disabled and one for
2650 when it is.
David Zeuthen21e95262016-07-27 17:58:40 -04002651
David Zeuthen21e95262016-07-27 17:58:40 -04002652 """
2653
David Zeuthen21e95262016-07-27 17:58:40 -04002654 c = 'dm="1 vroot none ro 1,'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002655 c += '0' # start
Jan Monsch23e0c622019-12-11 11:23:58 +01002656 c += ' {}'.format((ht.image_size // 512)) # size (# sectors)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002657 c += ' verity {}'.format(ht.dm_verity_version) # type and version
2658 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # data_dev
2659 c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)' # hash_dev
2660 c += ' {}'.format(ht.data_block_size) # data_block
2661 c += ' {}'.format(ht.hash_block_size) # hash_block
Jan Monsch23e0c622019-12-11 11:23:58 +01002662 c += ' {}'.format(ht.image_size // ht.data_block_size) # #blocks
2663 c += ' {}'.format(ht.image_size // ht.data_block_size) # hash_offset
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002664 c += ' {}'.format(ht.hash_algorithm) # hash_alg
2665 c += ' {}'.format(str(ht.root_digest).encode('hex')) # root_digest
2666 c += ' {}'.format(str(ht.salt).encode('hex')) # salt
2667 if ht.fec_num_roots > 0:
David Zeuthena01e32f2017-01-24 17:32:38 -05002668 c += ' 10' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04002669 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002670 c += ' ignore_zero_blocks'
2671 c += ' use_fec_from_device PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2672 c += ' fec_roots {}'.format(ht.fec_num_roots)
2673 # Note that fec_blocks is the size that FEC covers, *not* the
2674 # size of the FEC data. Since we use FEC for everything up until
2675 # the FEC data, it's the same as the offset.
Jan Monsch23e0c622019-12-11 11:23:58 +01002676 c += ' fec_blocks {}'.format(ht.fec_offset // ht.data_block_size)
2677 c += ' fec_start {}'.format(ht.fec_offset // ht.data_block_size)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002678 else:
David Zeuthena01e32f2017-01-24 17:32:38 -05002679 c += ' 2' # number of optional args
David Zeuthen1b2f7a62017-06-23 13:20:54 -04002680 c += ' $(ANDROID_VERITY_MODE)'
David Zeuthen0b7f1d32016-10-25 17:53:49 -04002681 c += ' ignore_zero_blocks'
David Zeuthenfd9c18d2017-03-20 18:19:30 -04002682 c += '" root=/dev/dm-0'
David Zeuthen21e95262016-07-27 17:58:40 -04002683
David Zeuthenfd41eb92016-11-17 12:24:47 -05002684 # Now that we have the command-line, generate the descriptor.
David Zeuthen21e95262016-07-27 17:58:40 -04002685 desc = AvbKernelCmdlineDescriptor()
2686 desc.kernel_cmdline = c
David Zeuthenfd41eb92016-11-17 12:24:47 -05002687 desc.flags = (
2688 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED)
2689
2690 # The descriptor for when hashtree verification is disabled is a lot
2691 # simpler - we just set the root to the partition.
2692 desc_no_ht = AvbKernelCmdlineDescriptor()
2693 desc_no_ht.kernel_cmdline = 'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'
2694 desc_no_ht.flags = (
2695 AvbKernelCmdlineDescriptor.FLAGS_USE_ONLY_IF_HASHTREE_DISABLED)
2696
2697 return [desc, desc_no_ht]
David Zeuthen21e95262016-07-27 17:58:40 -04002698
David Zeuthen73f2afa2017-05-17 16:54:11 -04002699 def _get_cmdline_descriptors_for_dm_verity(self, image):
2700 """Generate kernel cmdline descriptors for dm-verity.
2701
2702 Arguments:
2703 image: An ImageHandler (vbmeta or footer) with a hashtree descriptor.
2704
2705 Returns:
2706 A list with two AvbKernelCmdlineDescriptor with dm-verity kernel cmdline
2707 instructions. There is one for when hashtree is not disabled and one for
2708 when it is.
2709
2710 Raises:
2711 AvbError: If |image| doesn't have a hashtree descriptor.
2712
2713 """
2714
2715 (_, _, descriptors, _) = self._parse_image(image)
2716
2717 ht = None
2718 for desc in descriptors:
2719 if isinstance(desc, AvbHashtreeDescriptor):
2720 ht = desc
2721 break
2722
2723 if not ht:
2724 raise AvbError('No hashtree descriptor in given image')
2725
2726 return self._get_cmdline_descriptors_for_hashtree_descriptor(ht)
2727
David Zeuthen21e95262016-07-27 17:58:40 -04002728 def make_vbmeta_image(self, output, chain_partitions, algorithm_name,
David Zeuthen18666ab2016-11-15 11:18:05 -05002729 key_path, public_key_metadata_path, rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002730 flags, props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002731 setup_rootfs_from_kernel,
David Zeuthena156d3d2017-06-01 12:08:09 -04002732 include_descriptors_from_image,
2733 signing_helper,
2734 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05002735 release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04002736 append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04002737 print_required_libavb_version,
2738 padding_size):
David Zeuthen21e95262016-07-27 17:58:40 -04002739 """Implements the 'make_vbmeta_image' command.
2740
2741 Arguments:
2742 output: File to write the image to.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002743 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002744 algorithm_name: Name of algorithm to use.
2745 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05002746 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002747 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002748 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002749 props: Properties to insert (list of strings of the form 'key:value').
2750 props_from_file: Properties to insert (list of strings 'key:<path>').
2751 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002752 setup_rootfs_from_kernel: None or file to generate from.
David Zeuthen21e95262016-07-27 17:58:40 -04002753 include_descriptors_from_image: List of file objects with descriptors.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002754 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04002755 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05002756 release_string: None or avbtool release string to use instead of default.
2757 append_to_release_string: None or string to append.
David Zeuthen1097a782017-05-31 15:53:17 -04002758 print_required_libavb_version: True to only print required libavb version.
David Zeuthen97cb5802017-06-01 16:14:05 -04002759 padding_size: If not 0, pads output so size is a multiple of the number.
David Zeuthen21e95262016-07-27 17:58:40 -04002760
2761 Raises:
2762 AvbError: If a chained partition is malformed.
2763 """
2764
David Zeuthen1097a782017-05-31 15:53:17 -04002765 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04002766 if print_required_libavb_version:
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002767 if include_descriptors_from_image:
2768 # Use the bump logic in AvbVBMetaHeader to calculate the max required
2769 # version of all included descriptors.
2770 tmp_header = AvbVBMetaHeader()
2771 for image in include_descriptors_from_image:
2772 (_, image_header, _, _) = self._parse_image(ImageHandler(image.name))
2773 tmp_header.bump_required_libavb_version_minor(
2774 image_header.required_libavb_version_minor)
Jan Monsch23e0c622019-12-11 11:23:58 +01002775 print('1.{}'.format(tmp_header.required_libavb_version_minor))
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002776 else:
2777 # Descriptors aside, all vbmeta features are supported in 1.0.
Jan Monsch23e0c622019-12-11 11:23:58 +01002778 print('1.0')
David Zeuthen1097a782017-05-31 15:53:17 -04002779 return
2780
2781 if not output:
2782 raise AvbError('No output file given')
2783
David Zeuthen21e95262016-07-27 17:58:40 -04002784 descriptors = []
David Zeuthen73f2afa2017-05-17 16:54:11 -04002785 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04002786 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05002787 algorithm_name, key_path, public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002788 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002789 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04002790 include_descriptors_from_image, signing_helper,
2791 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002792 append_to_release_string, 0)
David Zeuthen21e95262016-07-27 17:58:40 -04002793
2794 # Write entire vbmeta blob (header, authentication, auxiliary).
2795 output.seek(0)
2796 output.write(vbmeta_blob)
2797
David Zeuthen97cb5802017-06-01 16:14:05 -04002798 if padding_size > 0:
2799 padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
2800 padding_needed = padded_size - len(vbmeta_blob)
2801 output.write('\0' * padding_needed)
2802
David Zeuthen18666ab2016-11-15 11:18:05 -05002803 def _generate_vbmeta_blob(self, algorithm_name, key_path,
2804 public_key_metadata_path, descriptors,
David Zeuthena5fd3a42017-02-27 16:38:54 -05002805 chain_partitions,
David Zeuthenfd41eb92016-11-17 12:24:47 -05002806 rollback_index, flags, props, props_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04002807 kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002808 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04002809 ht_desc_to_setup,
David Zeuthene3cadca2017-02-22 21:25:46 -05002810 include_descriptors_from_image, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04002811 signing_helper_with_files,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002812 release_string, append_to_release_string,
2813 required_libavb_version_minor):
David Zeuthen21e95262016-07-27 17:58:40 -04002814 """Generates a VBMeta blob.
2815
2816 This blob contains the header (struct AvbVBMetaHeader), the
2817 authentication data block (which contains the hash and signature
2818 for the header and auxiliary block), and the auxiliary block
2819 (which contains descriptors, the public key used, and other data).
2820
2821 The |key| parameter can |None| only if the |algorithm_name| is
2822 'NONE'.
2823
2824 Arguments:
2825 algorithm_name: The algorithm name as per the ALGORITHMS dict.
2826 key_path: The path to the .pem file used to sign the blob.
David Zeuthen18666ab2016-11-15 11:18:05 -05002827 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002828 descriptors: A list of descriptors to insert or None.
David Zeuthena5fd3a42017-02-27 16:38:54 -05002829 chain_partitions: List of partitions to chain or None.
David Zeuthen21e95262016-07-27 17:58:40 -04002830 rollback_index: The rollback index to use.
David Zeuthenfd41eb92016-11-17 12:24:47 -05002831 flags: Flags to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04002832 props: Properties to insert (List of strings of the form 'key:value').
2833 props_from_file: Properties to insert (List of strings 'key:<path>').
2834 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002835 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04002836 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04002837 ht_desc_to_setup: If not None, an AvbHashtreeDescriptor to
2838 generate dm-verity kernel cmdline descriptors from.
David Zeuthen21e95262016-07-27 17:58:40 -04002839 include_descriptors_from_image: List of file objects for which
2840 to insert descriptors from.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08002841 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04002842 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05002843 release_string: None or avbtool release string.
2844 append_to_release_string: None or string to append.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002845 required_libavb_version_minor: Use at least this required minor version.
David Zeuthen21e95262016-07-27 17:58:40 -04002846
2847 Returns:
2848 A bytearray() with the VBMeta blob.
2849
2850 Raises:
2851 Exception: If the |algorithm_name| is not found, if no key has
2852 been given and the given algorithm requires one, or the key is
2853 of the wrong size.
2854
2855 """
2856 try:
2857 alg = ALGORITHMS[algorithm_name]
2858 except KeyError:
2859 raise AvbError('Unknown algorithm with name {}'.format(algorithm_name))
2860
David Zeuthena5fd3a42017-02-27 16:38:54 -05002861 if not descriptors:
2862 descriptors = []
2863
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002864 h = AvbVBMetaHeader()
2865 h.bump_required_libavb_version_minor(required_libavb_version_minor)
2866
David Zeuthena5fd3a42017-02-27 16:38:54 -05002867 # Insert chained partition descriptors, if any
2868 if chain_partitions:
David Zeuthend8e48582017-04-21 11:31:51 -04002869 used_locations = {}
David Zeuthena5fd3a42017-02-27 16:38:54 -05002870 for cp in chain_partitions:
2871 cp_tokens = cp.split(':')
2872 if len(cp_tokens) != 3:
2873 raise AvbError('Malformed chained partition "{}".'.format(cp))
David Zeuthend8e48582017-04-21 11:31:51 -04002874 partition_name = cp_tokens[0]
2875 rollback_index_location = int(cp_tokens[1])
2876 file_path = cp_tokens[2]
2877 # Check that the same rollback location isn't being used by
2878 # multiple chained partitions.
2879 if used_locations.get(rollback_index_location):
2880 raise AvbError('Rollback Index Location {} is already in use.'.format(
2881 rollback_index_location))
2882 used_locations[rollback_index_location] = True
David Zeuthena5fd3a42017-02-27 16:38:54 -05002883 desc = AvbChainPartitionDescriptor()
David Zeuthend8e48582017-04-21 11:31:51 -04002884 desc.partition_name = partition_name
2885 desc.rollback_index_location = rollback_index_location
David Zeuthena5fd3a42017-02-27 16:38:54 -05002886 if desc.rollback_index_location < 1:
2887 raise AvbError('Rollback index location must be 1 or larger.')
David Zeuthena5fd3a42017-02-27 16:38:54 -05002888 desc.public_key = open(file_path, 'rb').read()
2889 descriptors.append(desc)
2890
David Zeuthen21e95262016-07-27 17:58:40 -04002891 # Descriptors.
2892 encoded_descriptors = bytearray()
David Zeuthena5fd3a42017-02-27 16:38:54 -05002893 for desc in descriptors:
2894 encoded_descriptors.extend(desc.encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002895
2896 # Add properties.
2897 if props:
2898 for prop in props:
2899 idx = prop.find(':')
2900 if idx == -1:
2901 raise AvbError('Malformed property "{}".'.format(prop))
Jan Monsch23e0c622019-12-11 11:23:58 +01002902 # pylint: disable=redefined-variable-type
David Zeuthen21e95262016-07-27 17:58:40 -04002903 desc = AvbPropertyDescriptor()
2904 desc.key = prop[0:idx]
2905 desc.value = prop[(idx + 1):]
2906 encoded_descriptors.extend(desc.encode())
2907 if props_from_file:
2908 for prop in props_from_file:
2909 idx = prop.find(':')
2910 if idx == -1:
2911 raise AvbError('Malformed property "{}".'.format(prop))
2912 desc = AvbPropertyDescriptor()
2913 desc.key = prop[0:idx]
2914 desc.value = prop[(idx + 1):]
2915 file_path = prop[(idx + 1):]
2916 desc.value = open(file_path, 'rb').read()
2917 encoded_descriptors.extend(desc.encode())
2918
David Zeuthen73f2afa2017-05-17 16:54:11 -04002919 # Add AvbKernelCmdline descriptor for dm-verity from an image, if requested.
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002920 if setup_rootfs_from_kernel:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002921 image_handler = ImageHandler(
David Zeuthen5d4f4f22017-01-11 11:37:34 -05002922 setup_rootfs_from_kernel.name)
David Zeuthenfd41eb92016-11-17 12:24:47 -05002923 cmdline_desc = self._get_cmdline_descriptors_for_dm_verity(image_handler)
2924 encoded_descriptors.extend(cmdline_desc[0].encode())
2925 encoded_descriptors.extend(cmdline_desc[1].encode())
David Zeuthen21e95262016-07-27 17:58:40 -04002926
David Zeuthen73f2afa2017-05-17 16:54:11 -04002927 # Add AvbKernelCmdline descriptor for dm-verity from desc, if requested.
2928 if ht_desc_to_setup:
2929 cmdline_desc = self._get_cmdline_descriptors_for_hashtree_descriptor(
2930 ht_desc_to_setup)
2931 encoded_descriptors.extend(cmdline_desc[0].encode())
2932 encoded_descriptors.extend(cmdline_desc[1].encode())
2933
David Zeuthen21e95262016-07-27 17:58:40 -04002934 # Add kernel command-lines.
2935 if kernel_cmdlines:
2936 for i in kernel_cmdlines:
2937 desc = AvbKernelCmdlineDescriptor()
2938 desc.kernel_cmdline = i
2939 encoded_descriptors.extend(desc.encode())
2940
2941 # Add descriptors from other images.
2942 if include_descriptors_from_image:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07002943 descriptors_dict = dict()
David Zeuthen21e95262016-07-27 17:58:40 -04002944 for image in include_descriptors_from_image:
David Zeuthena4fee8b2016-08-22 15:20:43 -04002945 image_handler = ImageHandler(image.name)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08002946 (_, image_vbmeta_header, image_descriptors, _) = self._parse_image(
2947 image_handler)
2948 # Bump the required libavb version to support all included descriptors.
2949 h.bump_required_libavb_version_minor(
2950 image_vbmeta_header.required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04002951 for desc in image_descriptors:
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07002952 # The --include_descriptors_from_image option is used in some setups
2953 # with images A and B where both A and B contain a descriptor
2954 # for a partition with the same name. Since it's not meaningful
2955 # to include both descriptors, only include the last seen descriptor.
2956 # See bug 76386656 for details.
2957 if hasattr(desc, 'partition_name'):
2958 key = type(desc).__name__ + '_' + desc.partition_name
2959 descriptors_dict[key] = desc.encode()
2960 else:
2961 encoded_descriptors.extend(desc.encode())
Jan Monschfe00c0a2019-12-11 11:19:40 +01002962 for key in sorted(descriptors_dict):
Sen Jiang2a3d6bc2018-04-04 14:13:28 -07002963 encoded_descriptors.extend(descriptors_dict[key])
David Zeuthen21e95262016-07-27 17:58:40 -04002964
David Zeuthen18666ab2016-11-15 11:18:05 -05002965 # Load public key metadata blob, if requested.
2966 pkmd_blob = []
2967 if public_key_metadata_path:
2968 with open(public_key_metadata_path) as f:
2969 pkmd_blob = f.read()
2970
David Zeuthen21e95262016-07-27 17:58:40 -04002971 key = None
2972 encoded_key = bytearray()
2973 if alg.public_key_num_bytes > 0:
2974 if not key_path:
2975 raise AvbError('Key is required for algorithm {}'.format(
2976 algorithm_name))
David Zeuthenc68f0822017-03-31 17:22:35 -04002977 encoded_key = encode_rsa_key(key_path)
David Zeuthen21e95262016-07-27 17:58:40 -04002978 if len(encoded_key) != alg.public_key_num_bytes:
2979 raise AvbError('Key is wrong size for algorithm {}'.format(
2980 algorithm_name))
2981
David Zeuthene3cadca2017-02-22 21:25:46 -05002982 # Override release string, if requested.
Jan Monsch23e0c622019-12-11 11:23:58 +01002983 # pylint: disable=unicode-builtin
David Zeuthene3cadca2017-02-22 21:25:46 -05002984 if isinstance(release_string, (str, unicode)):
2985 h.release_string = release_string
2986
2987 # Append to release string, if requested. Also insert a space before.
2988 if isinstance(append_to_release_string, (str, unicode)):
2989 h.release_string += ' ' + append_to_release_string
2990
David Zeuthen18666ab2016-11-15 11:18:05 -05002991 # For the Auxiliary data block, descriptors are stored at offset 0,
2992 # followed by the public key, followed by the public key metadata blob.
David Zeuthen21e95262016-07-27 17:58:40 -04002993 h.auxiliary_data_block_size = round_to_multiple(
David Zeuthen18666ab2016-11-15 11:18:05 -05002994 len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64)
David Zeuthen21e95262016-07-27 17:58:40 -04002995 h.descriptors_offset = 0
2996 h.descriptors_size = len(encoded_descriptors)
2997 h.public_key_offset = h.descriptors_size
2998 h.public_key_size = len(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05002999 h.public_key_metadata_offset = h.public_key_offset + h.public_key_size
3000 h.public_key_metadata_size = len(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003001
3002 # For the Authentication data block, the hash is first and then
3003 # the signature.
3004 h.authentication_data_block_size = round_to_multiple(
David Zeuthend5db21d2017-01-24 10:11:38 -05003005 alg.hash_num_bytes + alg.signature_num_bytes, 64)
David Zeuthen21e95262016-07-27 17:58:40 -04003006 h.algorithm_type = alg.algorithm_type
3007 h.hash_offset = 0
3008 h.hash_size = alg.hash_num_bytes
3009 # Signature offset and size - it's stored right after the hash
3010 # (in Authentication data block).
3011 h.signature_offset = alg.hash_num_bytes
3012 h.signature_size = alg.signature_num_bytes
3013
3014 h.rollback_index = rollback_index
David Zeuthenfd41eb92016-11-17 12:24:47 -05003015 h.flags = flags
David Zeuthen21e95262016-07-27 17:58:40 -04003016
3017 # Generate Header data block.
3018 header_data_blob = h.encode()
3019
3020 # Generate Auxiliary data block.
3021 aux_data_blob = bytearray()
3022 aux_data_blob.extend(encoded_descriptors)
3023 aux_data_blob.extend(encoded_key)
David Zeuthen18666ab2016-11-15 11:18:05 -05003024 aux_data_blob.extend(pkmd_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003025 padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob)
3026 aux_data_blob.extend('\0' * padding_bytes)
3027
3028 # Calculate the hash.
3029 binary_hash = bytearray()
3030 binary_signature = bytearray()
3031 if algorithm_name != 'NONE':
David Zeuthenb623d8b2017-04-04 16:05:53 -04003032 ha = hashlib.new(alg.hash_name)
David Zeuthen21e95262016-07-27 17:58:40 -04003033 ha.update(header_data_blob)
3034 ha.update(aux_data_blob)
3035 binary_hash.extend(ha.digest())
3036
3037 # Calculate the signature.
David Zeuthen21e95262016-07-27 17:58:40 -04003038 padding_and_hash = str(bytearray(alg.padding)) + binary_hash
David Zeuthena156d3d2017-06-01 12:08:09 -04003039 binary_signature.extend(raw_sign(signing_helper,
3040 signing_helper_with_files,
3041 algorithm_name,
Esun Kimff44f232017-03-30 10:34:54 +09003042 alg.signature_num_bytes, key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003043 padding_and_hash))
David Zeuthen21e95262016-07-27 17:58:40 -04003044
3045 # Generate Authentication data block.
3046 auth_data_blob = bytearray()
3047 auth_data_blob.extend(binary_hash)
3048 auth_data_blob.extend(binary_signature)
3049 padding_bytes = h.authentication_data_block_size - len(auth_data_blob)
3050 auth_data_blob.extend('\0' * padding_bytes)
3051
3052 return header_data_blob + auth_data_blob + aux_data_blob
3053
3054 def extract_public_key(self, key_path, output):
3055 """Implements the 'extract_public_key' command.
3056
3057 Arguments:
3058 key_path: The path to a RSA private key file.
3059 output: The file to write to.
3060 """
David Zeuthenc68f0822017-03-31 17:22:35 -04003061 output.write(encode_rsa_key(key_path))
David Zeuthen21e95262016-07-27 17:58:40 -04003062
David Zeuthenb1b994d2017-03-06 18:01:31 -05003063 def append_vbmeta_image(self, image_filename, vbmeta_image_filename,
3064 partition_size):
3065 """Implementation of the append_vbmeta_image command.
3066
3067 Arguments:
3068 image_filename: File to add the footer to.
3069 vbmeta_image_filename: File to get vbmeta struct from.
3070 partition_size: Size of partition.
3071
3072 Raises:
3073 AvbError: If an argument is incorrect.
3074 """
3075 image = ImageHandler(image_filename)
3076
3077 if partition_size % image.block_size != 0:
3078 raise AvbError('Partition size of {} is not a multiple of the image '
3079 'block size {}.'.format(partition_size,
3080 image.block_size))
3081
3082 # If there's already a footer, truncate the image to its original
3083 # size. This way 'avbtool append_vbmeta_image' is idempotent.
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003084 if image.image_size >= AvbFooter.SIZE:
3085 image.seek(image.image_size - AvbFooter.SIZE)
3086 try:
3087 footer = AvbFooter(image.read(AvbFooter.SIZE))
3088 # Existing footer found. Just truncate.
3089 original_image_size = footer.original_image_size
3090 image.truncate(footer.original_image_size)
3091 except (LookupError, struct.error):
3092 original_image_size = image.image_size
3093 else:
3094 # Image size is too small to possibly contain a footer.
David Zeuthenb1b994d2017-03-06 18:01:31 -05003095 original_image_size = image.image_size
3096
3097 # If anything goes wrong from here-on, restore the image back to
3098 # its original size.
3099 try:
3100 vbmeta_image_handler = ImageHandler(vbmeta_image_filename)
3101 vbmeta_blob = self._load_vbmeta_blob(vbmeta_image_handler)
3102
3103 # If the image isn't sparse, its size might not be a multiple of
3104 # the block size. This will screw up padding later so just grow it.
3105 if image.image_size % image.block_size != 0:
3106 assert not image.is_sparse
3107 padding_needed = image.block_size - (image.image_size%image.block_size)
3108 image.truncate(image.image_size + padding_needed)
3109
3110 # The append_raw() method requires content with size being a
3111 # multiple of |block_size| so add padding as needed. Also record
3112 # where this is written to since we'll need to put that in the
3113 # footer.
3114 vbmeta_offset = image.image_size
3115 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
3116 len(vbmeta_blob))
3117 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
3118
3119 # Append vbmeta blob and footer
3120 image.append_raw(vbmeta_blob_with_padding)
3121 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
3122
3123 # Now insert a DONT_CARE chunk with enough bytes such that the
3124 # final Footer block is at the end of partition_size..
3125 image.append_dont_care(partition_size - vbmeta_end_offset -
3126 1*image.block_size)
3127
3128 # Generate the Footer that tells where the VBMeta footer
3129 # is. Also put enough padding in the front of the footer since
3130 # we'll write out an entire block.
3131 footer = AvbFooter()
3132 footer.original_image_size = original_image_size
3133 footer.vbmeta_offset = vbmeta_offset
3134 footer.vbmeta_size = len(vbmeta_blob)
3135 footer_blob = footer.encode()
3136 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
3137 footer_blob)
3138 image.append_raw(footer_blob_with_padding)
3139
3140 except:
3141 # Truncate back to original size, then re-raise
3142 image.truncate(original_image_size)
3143 raise
3144
David Zeuthena4fee8b2016-08-22 15:20:43 -04003145 def add_hash_footer(self, image_filename, partition_size, partition_name,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003146 hash_algorithm, salt, chain_partitions, algorithm_name,
3147 key_path,
3148 public_key_metadata_path, rollback_index, flags, props,
David Zeuthen18666ab2016-11-15 11:18:05 -05003149 props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003150 setup_rootfs_from_kernel,
David Zeuthenbf562452017-05-17 18:04:43 -04003151 include_descriptors_from_image, calc_max_image_size,
David Zeuthena156d3d2017-06-01 12:08:09 -04003152 signing_helper, signing_helper_with_files,
3153 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003154 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003155 print_required_libavb_version, use_persistent_digest,
3156 do_not_use_ab):
David Zeuthena4fee8b2016-08-22 15:20:43 -04003157 """Implementation of the add_hash_footer on unsparse images.
David Zeuthen21e95262016-07-27 17:58:40 -04003158
3159 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003160 image_filename: File to add the footer to.
David Zeuthen21e95262016-07-27 17:58:40 -04003161 partition_size: Size of partition.
3162 partition_name: Name of partition (without A/B suffix).
3163 hash_algorithm: Hash algorithm to use.
3164 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003165 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04003166 algorithm_name: Name of algorithm to use.
3167 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05003168 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003169 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003170 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04003171 props: Properties to insert (List of strings of the form 'key:value').
3172 props_from_file: Properties to insert (List of strings 'key:<path>').
3173 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003174 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003175 dm-verity kernel cmdline from.
3176 include_descriptors_from_image: List of file objects for which
3177 to insert descriptors from.
David Zeuthenbf562452017-05-17 18:04:43 -04003178 calc_max_image_size: Don't store the footer - instead calculate the
3179 maximum image size leaving enough room for metadata with the
3180 given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003181 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003182 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003183 release_string: None or avbtool release string.
3184 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05003185 output_vbmeta_image: If not None, also write vbmeta struct to this file.
3186 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04003187 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003188 use_persistent_digest: Use a persistent digest on device.
3189 do_not_use_ab: This partition does not use A/B.
David Zeuthena4fee8b2016-08-22 15:20:43 -04003190
3191 Raises:
3192 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04003193 """
David Zeuthen1097a782017-05-31 15:53:17 -04003194
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003195 required_libavb_version_minor = 0
3196 if use_persistent_digest or do_not_use_ab:
3197 required_libavb_version_minor = 1
3198
David Zeuthen1097a782017-05-31 15:53:17 -04003199 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003200 if print_required_libavb_version:
Jan Monsch23e0c622019-12-11 11:23:58 +01003201 print('1.{}'.format(required_libavb_version_minor))
David Zeuthen1097a782017-05-31 15:53:17 -04003202 return
3203
David Zeuthenbf562452017-05-17 18:04:43 -04003204 # First, calculate the maximum image size such that an image
3205 # this size + metadata (footer + vbmeta struct) fits in
3206 # |partition_size|.
3207 max_metadata_size = self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003208 if partition_size < max_metadata_size:
3209 raise AvbError('Parition size of {} is too small. '
3210 'Needs to be at least {}'.format(
3211 partition_size, max_metadata_size))
David Zeuthenbf562452017-05-17 18:04:43 -04003212 max_image_size = partition_size - max_metadata_size
3213
3214 # If we're asked to only calculate the maximum image size, we're done.
3215 if calc_max_image_size:
Jan Monsch23e0c622019-12-11 11:23:58 +01003216 print('{}'.format(max_image_size))
David Zeuthenbf562452017-05-17 18:04:43 -04003217 return
3218
David Zeuthena4fee8b2016-08-22 15:20:43 -04003219 image = ImageHandler(image_filename)
3220
3221 if partition_size % image.block_size != 0:
3222 raise AvbError('Partition size of {} is not a multiple of the image '
3223 'block size {}.'.format(partition_size,
3224 image.block_size))
3225
David Zeuthen21e95262016-07-27 17:58:40 -04003226 # If there's already a footer, truncate the image to its original
3227 # size. This way 'avbtool add_hash_footer' is idempotent (modulo
3228 # salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003229 if image.image_size >= AvbFooter.SIZE:
3230 image.seek(image.image_size - AvbFooter.SIZE)
3231 try:
3232 footer = AvbFooter(image.read(AvbFooter.SIZE))
3233 # Existing footer found. Just truncate.
3234 original_image_size = footer.original_image_size
3235 image.truncate(footer.original_image_size)
3236 except (LookupError, struct.error):
3237 original_image_size = image.image_size
3238 else:
3239 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04003240 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003241
3242 # If anything goes wrong from here-on, restore the image back to
3243 # its original size.
3244 try:
David Zeuthen09692692016-09-30 16:16:40 -04003245 # If image size exceeds the maximum image size, fail.
3246 if image.image_size > max_image_size:
3247 raise AvbError('Image size of {} exceeds maximum image '
3248 'size of {} in order to fit in a partition '
3249 'size of {}.'.format(image.image_size, max_image_size,
3250 partition_size))
3251
David Zeuthen21e95262016-07-27 17:58:40 -04003252 digest_size = len(hashlib.new(name=hash_algorithm).digest())
3253 if salt:
Jan Monsch23e0c622019-12-11 11:23:58 +01003254 salt = binascii.unhexlify(salt)
3255 elif salt is None and not use_persistent_digest:
3256 # If salt is not explicitly specified, choose a hash that's the same
3257 # size as the hash size. Don't populate a random salt if this
3258 # descriptor is being created to use a persistent digest on device.
3259 hash_size = digest_size
3260 salt = open('/dev/urandom').read(hash_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003261 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01003262 salt = ''
David Zeuthen21e95262016-07-27 17:58:40 -04003263
3264 hasher = hashlib.new(name=hash_algorithm, string=salt)
3265 # TODO(zeuthen): might want to read this in chunks to avoid
3266 # memory pressure, then again, this is only supposed to be used
3267 # on kernel/initramfs partitions. Possible optimization.
3268 image.seek(0)
David Zeuthen09692692016-09-30 16:16:40 -04003269 hasher.update(image.read(image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003270 digest = hasher.digest()
3271
3272 h_desc = AvbHashDescriptor()
David Zeuthen09692692016-09-30 16:16:40 -04003273 h_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003274 h_desc.hash_algorithm = hash_algorithm
3275 h_desc.partition_name = partition_name
3276 h_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003277 h_desc.flags = 0
3278 if do_not_use_ab:
3279 h_desc.flags |= 1 # AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
3280 if not use_persistent_digest:
3281 h_desc.digest = digest
David Zeuthen21e95262016-07-27 17:58:40 -04003282
3283 # Generate the VBMeta footer.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003284 ht_desc_to_setup = None
David Zeuthen21e95262016-07-27 17:58:40 -04003285 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003286 algorithm_name, key_path, public_key_metadata_path, [h_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05003287 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003288 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003289 include_descriptors_from_image, signing_helper,
3290 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003291 append_to_release_string, required_libavb_version_minor)
David Zeuthen21e95262016-07-27 17:58:40 -04003292
David Zeuthend247fcb2017-02-16 12:09:27 -05003293 # Write vbmeta blob, if requested.
3294 if output_vbmeta_image:
3295 output_vbmeta_image.write(vbmeta_blob)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003296
David Zeuthend247fcb2017-02-16 12:09:27 -05003297 # Append vbmeta blob and footer, unless requested not to.
3298 if not do_not_append_vbmeta_image:
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003299 # If the image isn't sparse, its size might not be a multiple of
3300 # the block size. This will screw up padding later so just grow it.
3301 if image.image_size % image.block_size != 0:
3302 assert not image.is_sparse
3303 padding_needed = image.block_size - (
3304 image.image_size % image.block_size)
3305 image.truncate(image.image_size + padding_needed)
3306
3307 # The append_raw() method requires content with size being a
3308 # multiple of |block_size| so add padding as needed. Also record
3309 # where this is written to since we'll need to put that in the
3310 # footer.
3311 vbmeta_offset = image.image_size
3312 padding_needed = (
3313 round_to_multiple(len(vbmeta_blob), image.block_size) -
3314 len(vbmeta_blob))
3315 vbmeta_blob_with_padding = vbmeta_blob + '\0' * padding_needed
3316
David Zeuthend247fcb2017-02-16 12:09:27 -05003317 image.append_raw(vbmeta_blob_with_padding)
3318 vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
3319
3320 # Now insert a DONT_CARE chunk with enough bytes such that the
3321 # final Footer block is at the end of partition_size..
3322 image.append_dont_care(partition_size - vbmeta_end_offset -
3323 1*image.block_size)
3324
3325 # Generate the Footer that tells where the VBMeta footer
3326 # is. Also put enough padding in the front of the footer since
3327 # we'll write out an entire block.
3328 footer = AvbFooter()
3329 footer.original_image_size = original_image_size
3330 footer.vbmeta_offset = vbmeta_offset
3331 footer.vbmeta_size = len(vbmeta_blob)
3332 footer_blob = footer.encode()
3333 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
3334 footer_blob)
3335 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003336
David Zeuthen21e95262016-07-27 17:58:40 -04003337 except:
3338 # Truncate back to original size, then re-raise
3339 image.truncate(original_image_size)
3340 raise
3341
David Zeuthena4fee8b2016-08-22 15:20:43 -04003342 def add_hashtree_footer(self, image_filename, partition_size, partition_name,
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003343 generate_fec, fec_num_roots, hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05003344 block_size, salt, chain_partitions, algorithm_name,
3345 key_path,
3346 public_key_metadata_path, rollback_index, flags,
David Zeuthenfd41eb92016-11-17 12:24:47 -05003347 props, props_from_file, kernel_cmdlines,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003348 setup_rootfs_from_kernel,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003349 setup_as_rootfs_from_kernel,
David Zeuthen09692692016-09-30 16:16:40 -04003350 include_descriptors_from_image,
David Zeuthend247fcb2017-02-16 12:09:27 -05003351 calc_max_image_size, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003352 signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05003353 release_string, append_to_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04003354 output_vbmeta_image, do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003355 print_required_libavb_version,
Jan Monscheeb28b62019-12-05 16:17:09 +01003356 use_persistent_root_digest, do_not_use_ab,
3357 no_hashtree):
David Zeuthen21e95262016-07-27 17:58:40 -04003358 """Implements the 'add_hashtree_footer' command.
3359
3360 See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
3361 more information about dm-verity and these hashes.
3362
3363 Arguments:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003364 image_filename: File to add the footer to.
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003365 partition_size: Size of partition or 0 to put it right at the end.
David Zeuthen21e95262016-07-27 17:58:40 -04003366 partition_name: Name of partition (without A/B suffix).
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003367 generate_fec: If True, generate FEC codes.
3368 fec_num_roots: Number of roots for FEC.
David Zeuthen21e95262016-07-27 17:58:40 -04003369 hash_algorithm: Hash algorithm to use.
3370 block_size: Block size to use.
3371 salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003372 chain_partitions: List of partitions to chain.
David Zeuthen21e95262016-07-27 17:58:40 -04003373 algorithm_name: Name of algorithm to use.
3374 key_path: Path to key to use or None.
David Zeuthen18666ab2016-11-15 11:18:05 -05003375 public_key_metadata_path: Path to public key metadata or None.
David Zeuthen21e95262016-07-27 17:58:40 -04003376 rollback_index: Rollback index.
David Zeuthena5fd3a42017-02-27 16:38:54 -05003377 flags: Flags value to use in the image.
David Zeuthen21e95262016-07-27 17:58:40 -04003378 props: Properties to insert (List of strings of the form 'key:value').
3379 props_from_file: Properties to insert (List of strings 'key:<path>').
3380 kernel_cmdlines: Kernel cmdlines to insert (list of strings).
David Zeuthen5d4f4f22017-01-11 11:37:34 -05003381 setup_rootfs_from_kernel: None or file to generate
David Zeuthen21e95262016-07-27 17:58:40 -04003382 dm-verity kernel cmdline from.
David Zeuthen73f2afa2017-05-17 16:54:11 -04003383 setup_as_rootfs_from_kernel: If True, generate dm-verity kernel
3384 cmdline to set up rootfs.
David Zeuthen21e95262016-07-27 17:58:40 -04003385 include_descriptors_from_image: List of file objects for which
3386 to insert descriptors from.
David Zeuthen09692692016-09-30 16:16:40 -04003387 calc_max_image_size: Don't store the hashtree or footer - instead
3388 calculate the maximum image size leaving enough room for hashtree
3389 and metadata with the given |partition_size|.
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003390 signing_helper: Program which signs a hash and return signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003391 signing_helper_with_files: Same as signing_helper but uses files instead.
David Zeuthene3cadca2017-02-22 21:25:46 -05003392 release_string: None or avbtool release string.
3393 append_to_release_string: None or string to append.
David Zeuthend247fcb2017-02-16 12:09:27 -05003394 output_vbmeta_image: If not None, also write vbmeta struct to this file.
3395 do_not_append_vbmeta_image: If True, don't append vbmeta struct.
David Zeuthen1097a782017-05-31 15:53:17 -04003396 print_required_libavb_version: True to only print required libavb version.
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003397 use_persistent_root_digest: Use a persistent root digest on device.
3398 do_not_use_ab: The partition does not use A/B.
Jooyung Hand7221942019-06-17 13:19:57 +09003399 no_hashtree: Do not append hashtree. Set size in descriptor as zero.
David Zeuthena4fee8b2016-08-22 15:20:43 -04003400
3401 Raises:
3402 AvbError: If an argument is incorrect.
David Zeuthen21e95262016-07-27 17:58:40 -04003403 """
David Zeuthen1097a782017-05-31 15:53:17 -04003404
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003405 required_libavb_version_minor = 0
3406 if use_persistent_root_digest or do_not_use_ab:
3407 required_libavb_version_minor = 1
3408
David Zeuthen1097a782017-05-31 15:53:17 -04003409 # If we're asked to calculate minimum required libavb version, we're done.
David Zeuthen1097a782017-05-31 15:53:17 -04003410 if print_required_libavb_version:
Jan Monsch23e0c622019-12-11 11:23:58 +01003411 print('1.{}'.format(required_libavb_version_minor))
David Zeuthen1097a782017-05-31 15:53:17 -04003412 return
3413
David Zeuthen09692692016-09-30 16:16:40 -04003414 digest_size = len(hashlib.new(name=hash_algorithm).digest())
3415 digest_padding = round_to_pow2(digest_size) - digest_size
3416
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003417 # If |partition_size| is given (e.g. not 0), calculate the maximum image
3418 # size such that an image this size + the hashtree + metadata (footer +
3419 # vbmeta struct) fits in |partition_size|. We use very conservative figures
3420 # for metadata.
3421 if partition_size > 0:
Jooyung Hand7221942019-06-17 13:19:57 +09003422 max_tree_size = 0
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003423 max_fec_size = 0
Jooyung Hand7221942019-06-17 13:19:57 +09003424 if not no_hashtree:
3425 (_, max_tree_size) = calc_hash_level_offsets(
3426 partition_size, block_size, digest_size + digest_padding)
3427 if generate_fec:
3428 max_fec_size = calc_fec_data_size(partition_size, fec_num_roots)
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003429 max_metadata_size = (max_fec_size + max_tree_size +
3430 self.MAX_VBMETA_SIZE +
3431 self.MAX_FOOTER_SIZE)
3432 max_image_size = partition_size - max_metadata_size
3433 else:
3434 max_image_size = 0
David Zeuthen09692692016-09-30 16:16:40 -04003435
3436 # If we're asked to only calculate the maximum image size, we're done.
3437 if calc_max_image_size:
Jan Monsch23e0c622019-12-11 11:23:58 +01003438 print('{}'.format(max_image_size))
David Zeuthen09692692016-09-30 16:16:40 -04003439 return
3440
David Zeuthena4fee8b2016-08-22 15:20:43 -04003441 image = ImageHandler(image_filename)
3442
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003443 if partition_size > 0:
3444 if partition_size % image.block_size != 0:
3445 raise AvbError('Partition size of {} is not a multiple of the image '
3446 'block size {}.'.format(partition_size,
3447 image.block_size))
Jan Monsch23e0c622019-12-11 11:23:58 +01003448 elif image.image_size % image.block_size != 0:
3449 raise AvbError('File size of {} is not a multiple of the image '
3450 'block size {}.'.format(image.image_size,
3451 image.block_size))
David Zeuthena4fee8b2016-08-22 15:20:43 -04003452
David Zeuthen21e95262016-07-27 17:58:40 -04003453 # If there's already a footer, truncate the image to its original
3454 # size. This way 'avbtool add_hashtree_footer' is idempotent
3455 # (modulo salts).
Lonnie Liu6b5a33e2017-10-31 18:01:09 -07003456 if image.image_size >= AvbFooter.SIZE:
3457 image.seek(image.image_size - AvbFooter.SIZE)
3458 try:
3459 footer = AvbFooter(image.read(AvbFooter.SIZE))
3460 # Existing footer found. Just truncate.
3461 original_image_size = footer.original_image_size
3462 image.truncate(footer.original_image_size)
3463 except (LookupError, struct.error):
3464 original_image_size = image.image_size
3465 else:
3466 # Image size is too small to possibly contain a footer.
David Zeuthen09692692016-09-30 16:16:40 -04003467 original_image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003468
3469 # If anything goes wrong from here-on, restore the image back to
3470 # its original size.
3471 try:
3472 # Ensure image is multiple of block_size.
David Zeuthen09692692016-09-30 16:16:40 -04003473 rounded_image_size = round_to_multiple(image.image_size, block_size)
3474 if rounded_image_size > image.image_size:
3475 image.append_raw('\0' * (rounded_image_size - image.image_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003476
David Zeuthen09692692016-09-30 16:16:40 -04003477 # If image size exceeds the maximum image size, fail.
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003478 if partition_size > 0:
3479 if image.image_size > max_image_size:
3480 raise AvbError('Image size of {} exceeds maximum image '
3481 'size of {} in order to fit in a partition '
3482 'size of {}.'.format(image.image_size, max_image_size,
3483 partition_size))
David Zeuthen21e95262016-07-27 17:58:40 -04003484
3485 if salt:
Jan Monsch23e0c622019-12-11 11:23:58 +01003486 salt = binascii.unhexlify(salt)
3487 elif salt is None and not use_persistent_root_digest:
3488 # If salt is not explicitly specified, choose a hash that's the same
3489 # size as the hash size. Don't populate a random salt if this
3490 # descriptor is being created to use a persistent digest on device.
3491 hash_size = digest_size
3492 salt = open('/dev/urandom').read(hash_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003493 else:
Jan Monsch23e0c622019-12-11 11:23:58 +01003494 salt = ''
David Zeuthen21e95262016-07-27 17:58:40 -04003495
David Zeuthena4fee8b2016-08-22 15:20:43 -04003496 # Hashes are stored upside down so we need to calculate hash
David Zeuthen21e95262016-07-27 17:58:40 -04003497 # offsets in advance.
3498 (hash_level_offsets, tree_size) = calc_hash_level_offsets(
David Zeuthen09692692016-09-30 16:16:40 -04003499 image.image_size, block_size, digest_size + digest_padding)
David Zeuthen21e95262016-07-27 17:58:40 -04003500
David Zeuthena4fee8b2016-08-22 15:20:43 -04003501 # If the image isn't sparse, its size might not be a multiple of
3502 # the block size. This will screw up padding later so just grow it.
David Zeuthen09692692016-09-30 16:16:40 -04003503 if image.image_size % image.block_size != 0:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003504 assert not image.is_sparse
David Zeuthen09692692016-09-30 16:16:40 -04003505 padding_needed = image.block_size - (image.image_size%image.block_size)
3506 image.truncate(image.image_size + padding_needed)
David Zeuthen21e95262016-07-27 17:58:40 -04003507
David Zeuthena4fee8b2016-08-22 15:20:43 -04003508 # Generate the tree and add padding as needed.
David Zeuthen09692692016-09-30 16:16:40 -04003509 tree_offset = image.image_size
3510 root_digest, hash_tree = generate_hash_tree(image, image.image_size,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003511 block_size,
3512 hash_algorithm, salt,
3513 digest_padding,
3514 hash_level_offsets,
3515 tree_size)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003516
3517 # Generate HashtreeDescriptor with details about the tree we
3518 # just generated.
Jooyung Hand7221942019-06-17 13:19:57 +09003519 if no_hashtree:
3520 tree_size = 0
3521 hash_tree = bytearray()
David Zeuthen21e95262016-07-27 17:58:40 -04003522 ht_desc = AvbHashtreeDescriptor()
3523 ht_desc.dm_verity_version = 1
David Zeuthen09692692016-09-30 16:16:40 -04003524 ht_desc.image_size = image.image_size
David Zeuthen21e95262016-07-27 17:58:40 -04003525 ht_desc.tree_offset = tree_offset
3526 ht_desc.tree_size = tree_size
3527 ht_desc.data_block_size = block_size
3528 ht_desc.hash_block_size = block_size
3529 ht_desc.hash_algorithm = hash_algorithm
3530 ht_desc.partition_name = partition_name
3531 ht_desc.salt = salt
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003532 if do_not_use_ab:
3533 ht_desc.flags |= 1 # AVB_HASHTREE_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
3534 if not use_persistent_root_digest:
3535 ht_desc.root_digest = root_digest
David Zeuthen21e95262016-07-27 17:58:40 -04003536
David Zeuthen09692692016-09-30 16:16:40 -04003537 # Write the hash tree
3538 padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
3539 len(hash_tree))
3540 hash_tree_with_padding = hash_tree + '\0'*padding_needed
3541 image.append_raw(hash_tree_with_padding)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003542 len_hashtree_and_fec = len(hash_tree_with_padding)
3543
3544 # Generate FEC codes, if requested.
3545 if generate_fec:
Jooyung Hand7221942019-06-17 13:19:57 +09003546 if no_hashtree:
3547 fec_data = bytearray()
Tao Bao868db2a2019-09-09 13:35:05 -07003548 else:
3549 fec_data = generate_fec_data(image_filename, fec_num_roots)
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003550 padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
3551 len(fec_data))
3552 fec_data_with_padding = fec_data + '\0'*padding_needed
3553 fec_offset = image.image_size
3554 image.append_raw(fec_data_with_padding)
3555 len_hashtree_and_fec += len(fec_data_with_padding)
3556 # Update the hashtree descriptor.
3557 ht_desc.fec_num_roots = fec_num_roots
3558 ht_desc.fec_offset = fec_offset
3559 ht_desc.fec_size = len(fec_data)
David Zeuthen09692692016-09-30 16:16:40 -04003560
David Zeuthen73f2afa2017-05-17 16:54:11 -04003561 ht_desc_to_setup = None
3562 if setup_as_rootfs_from_kernel:
3563 ht_desc_to_setup = ht_desc
3564
David Zeuthena4fee8b2016-08-22 15:20:43 -04003565 # Generate the VBMeta footer and add padding as needed.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003566 vbmeta_offset = tree_offset + len_hashtree_and_fec
David Zeuthen21e95262016-07-27 17:58:40 -04003567 vbmeta_blob = self._generate_vbmeta_blob(
David Zeuthen18666ab2016-11-15 11:18:05 -05003568 algorithm_name, key_path, public_key_metadata_path, [ht_desc],
David Zeuthena5fd3a42017-02-27 16:38:54 -05003569 chain_partitions, rollback_index, flags, props, props_from_file,
David Zeuthen73f2afa2017-05-17 16:54:11 -04003570 kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
David Zeuthena156d3d2017-06-01 12:08:09 -04003571 include_descriptors_from_image, signing_helper,
3572 signing_helper_with_files, release_string,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08003573 append_to_release_string, required_libavb_version_minor)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003574 padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
3575 len(vbmeta_blob))
3576 vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
David Zeuthen21e95262016-07-27 17:58:40 -04003577
David Zeuthend247fcb2017-02-16 12:09:27 -05003578 # Write vbmeta blob, if requested.
3579 if output_vbmeta_image:
3580 output_vbmeta_image.write(vbmeta_blob)
David Zeuthen21e95262016-07-27 17:58:40 -04003581
David Zeuthend247fcb2017-02-16 12:09:27 -05003582 # Append vbmeta blob and footer, unless requested not to.
3583 if not do_not_append_vbmeta_image:
3584 image.append_raw(vbmeta_blob_with_padding)
3585
3586 # Now insert a DONT_CARE chunk with enough bytes such that the
3587 # final Footer block is at the end of partition_size..
David Zeuthenf4f51eb2018-09-20 14:56:46 -04003588 if partition_size > 0:
3589 image.append_dont_care(partition_size - image.image_size -
3590 1*image.block_size)
David Zeuthend247fcb2017-02-16 12:09:27 -05003591
3592 # Generate the Footer that tells where the VBMeta footer
3593 # is. Also put enough padding in the front of the footer since
3594 # we'll write out an entire block.
3595 footer = AvbFooter()
3596 footer.original_image_size = original_image_size
3597 footer.vbmeta_offset = vbmeta_offset
3598 footer.vbmeta_size = len(vbmeta_blob)
3599 footer_blob = footer.encode()
3600 footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
3601 footer_blob)
3602 image.append_raw(footer_blob_with_padding)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003603
David Zeuthen21e95262016-07-27 17:58:40 -04003604 except:
David Zeuthen09692692016-09-30 16:16:40 -04003605 # Truncate back to original size, then re-raise.
David Zeuthen21e95262016-07-27 17:58:40 -04003606 image.truncate(original_image_size)
3607 raise
3608
David Zeuthenc68f0822017-03-31 17:22:35 -04003609 def make_atx_certificate(self, output, authority_key_path, subject_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003610 subject_key_version, subject,
Darren Krahnfccd64e2018-01-16 17:39:35 -08003611 is_intermediate_authority, usage, signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04003612 signing_helper_with_files):
Darren Krahn147b08d2016-12-20 16:38:29 -08003613 """Implements the 'make_atx_certificate' command.
3614
3615 Android Things certificates are required for Android Things public key
3616 metadata. They chain the vbmeta signing key for a particular product back to
3617 a fused, permanent root key. These certificates are fixed-length and fixed-
3618 format with the explicit goal of not parsing ASN.1 in bootloader code.
3619
3620 Arguments:
3621 output: Certificate will be written to this file on success.
3622 authority_key_path: A PEM file path with the authority private key.
3623 If None, then a certificate will be created without a
3624 signature. The signature can be created out-of-band
3625 and appended.
David Zeuthenc68f0822017-03-31 17:22:35 -04003626 subject_key_path: Path to a PEM or DER subject public key.
Darren Krahn147b08d2016-12-20 16:38:29 -08003627 subject_key_version: A 64-bit version value. If this is None, the number
3628 of seconds since the epoch is used.
3629 subject: A subject identifier. For Product Signing Key certificates this
3630 should be the same Product ID found in the permanent attributes.
3631 is_intermediate_authority: True if the certificate is for an intermediate
3632 authority.
Darren Krahnfccd64e2018-01-16 17:39:35 -08003633 usage: If not empty, overrides the cert usage with a hash of this value.
Darren Krahn147b08d2016-12-20 16:38:29 -08003634 signing_helper: Program which signs a hash and returns the signature.
David Zeuthena156d3d2017-06-01 12:08:09 -04003635 signing_helper_with_files: Same as signing_helper but uses files instead.
Darren Krahn147b08d2016-12-20 16:38:29 -08003636 """
3637 signed_data = bytearray()
3638 signed_data.extend(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04003639 signed_data.extend(encode_rsa_key(subject_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08003640 hasher = hashlib.sha256()
3641 hasher.update(subject)
3642 signed_data.extend(hasher.digest())
Darren Krahnfccd64e2018-01-16 17:39:35 -08003643 if not usage:
3644 usage = 'com.google.android.things.vboot'
3645 if is_intermediate_authority:
3646 usage += '.ca'
Darren Krahn147b08d2016-12-20 16:38:29 -08003647 hasher = hashlib.sha256()
3648 hasher.update(usage)
3649 signed_data.extend(hasher.digest())
Yu Shanc8540812019-07-01 16:54:46 -07003650 if subject_key_version is None:
Darren Krahn147b08d2016-12-20 16:38:29 -08003651 subject_key_version = int(time.time())
3652 signed_data.extend(struct.pack('<Q', subject_key_version))
3653 signature = bytearray()
3654 if authority_key_path:
3655 padding_and_hash = bytearray()
Darren Krahn43e12d82017-02-24 16:26:31 -08003656 algorithm_name = 'SHA512_RSA4096'
Esun Kimff44f232017-03-30 10:34:54 +09003657 alg = ALGORITHMS[algorithm_name]
Jan Monsch23e0c622019-12-11 11:23:58 +01003658 hasher = hashlib.sha512() # pylint: disable=redefined-variable-type
Esun Kimff44f232017-03-30 10:34:54 +09003659 padding_and_hash.extend(alg.padding)
Darren Krahn147b08d2016-12-20 16:38:29 -08003660 hasher.update(signed_data)
3661 padding_and_hash.extend(hasher.digest())
David Zeuthena156d3d2017-06-01 12:08:09 -04003662 signature.extend(raw_sign(signing_helper, signing_helper_with_files,
3663 algorithm_name,
Esun Kimff44f232017-03-30 10:34:54 +09003664 alg.signature_num_bytes, authority_key_path,
3665 padding_and_hash))
Darren Krahn147b08d2016-12-20 16:38:29 -08003666 output.write(signed_data)
3667 output.write(signature)
3668
David Zeuthenc68f0822017-03-31 17:22:35 -04003669 def make_atx_permanent_attributes(self, output, root_authority_key_path,
Darren Krahn147b08d2016-12-20 16:38:29 -08003670 product_id):
3671 """Implements the 'make_atx_permanent_attributes' command.
3672
3673 Android Things permanent attributes are designed to be permanent for a
3674 particular product and a hash of these attributes should be fused into
3675 hardware to enforce this.
3676
3677 Arguments:
3678 output: Attributes will be written to this file on success.
David Zeuthenc68f0822017-03-31 17:22:35 -04003679 root_authority_key_path: Path to a PEM or DER public key for
3680 the root authority.
Darren Krahn147b08d2016-12-20 16:38:29 -08003681 product_id: A 16-byte Product ID.
3682
3683 Raises:
3684 AvbError: If an argument is incorrect.
3685 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01003686 EXPECTED_PRODUCT_ID_SIZE = 16 # pylint: disable=invalid-name
Darren Krahn43e12d82017-02-24 16:26:31 -08003687 if len(product_id) != EXPECTED_PRODUCT_ID_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003688 raise AvbError('Invalid Product ID length.')
3689 output.write(struct.pack('<I', 1)) # Format Version
David Zeuthenc68f0822017-03-31 17:22:35 -04003690 output.write(encode_rsa_key(root_authority_key_path))
Darren Krahn147b08d2016-12-20 16:38:29 -08003691 output.write(product_id)
3692
3693 def make_atx_metadata(self, output, intermediate_key_certificate,
Darren Krahn43e12d82017-02-24 16:26:31 -08003694 product_key_certificate):
Darren Krahn147b08d2016-12-20 16:38:29 -08003695 """Implements the 'make_atx_metadata' command.
3696
3697 Android Things metadata are included in vbmeta images to facilitate
3698 verification. The output of this command can be used as the
3699 public_key_metadata argument to other commands.
3700
3701 Arguments:
3702 output: Metadata will be written to this file on success.
3703 intermediate_key_certificate: A certificate file as output by
3704 make_atx_certificate with
3705 is_intermediate_authority set to true.
3706 product_key_certificate: A certificate file as output by
3707 make_atx_certificate with
3708 is_intermediate_authority set to false.
Darren Krahn147b08d2016-12-20 16:38:29 -08003709
3710 Raises:
3711 AvbError: If an argument is incorrect.
3712 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01003713 EXPECTED_CERTIFICATE_SIZE = 1620 # pylint: disable=invalid-name
Darren Krahn43e12d82017-02-24 16:26:31 -08003714 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003715 raise AvbError('Invalid intermediate key certificate length.')
Darren Krahn43e12d82017-02-24 16:26:31 -08003716 if len(product_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
Darren Krahn147b08d2016-12-20 16:38:29 -08003717 raise AvbError('Invalid product key certificate length.')
3718 output.write(struct.pack('<I', 1)) # Format Version
3719 output.write(intermediate_key_certificate)
3720 output.write(product_key_certificate)
Darren Krahn147b08d2016-12-20 16:38:29 -08003721
Darren Krahnfccd64e2018-01-16 17:39:35 -08003722 def make_atx_unlock_credential(self, output, intermediate_key_certificate,
3723 unlock_key_certificate, challenge_path,
3724 unlock_key_path, signing_helper,
3725 signing_helper_with_files):
3726 """Implements the 'make_atx_unlock_credential' command.
3727
3728 Android Things unlock credentials can be used to authorize the unlock of AVB
3729 on a device. These credentials are presented to an Android Things bootloader
3730 via the fastboot interface in response to a 16-byte challenge. This method
3731 creates all fields of the credential except the challenge signature field
3732 (which is the last field) and can optionally create the challenge signature
3733 field as well if a challenge and the unlock_key_path is provided.
3734
3735 Arguments:
3736 output: The credential will be written to this file on success.
3737 intermediate_key_certificate: A certificate file as output by
3738 make_atx_certificate with
3739 is_intermediate_authority set to true.
3740 unlock_key_certificate: A certificate file as output by
3741 make_atx_certificate with
3742 is_intermediate_authority set to false and the
3743 usage set to
3744 'com.google.android.things.vboot.unlock'.
3745 challenge_path: [optional] A path to the challenge to sign.
3746 unlock_key_path: [optional] A PEM file path with the unlock private key.
3747 signing_helper: Program which signs a hash and returns the signature.
3748 signing_helper_with_files: Same as signing_helper but uses files instead.
3749
3750 Raises:
3751 AvbError: If an argument is incorrect.
3752 """
Jan Monschfe00c0a2019-12-11 11:19:40 +01003753 EXPECTED_CERTIFICATE_SIZE = 1620 # pylint: disable=invalid-name
3754 EXPECTED_CHALLENGE_SIZE = 16 # pylint: disable=invalid-name
Darren Krahnfccd64e2018-01-16 17:39:35 -08003755 if len(intermediate_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
3756 raise AvbError('Invalid intermediate key certificate length.')
3757 if len(unlock_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
3758 raise AvbError('Invalid product key certificate length.')
3759 challenge = bytearray()
3760 if challenge_path:
3761 with open(challenge_path, 'r') as f:
3762 challenge = f.read()
3763 if len(challenge) != EXPECTED_CHALLENGE_SIZE:
3764 raise AvbError('Invalid unlock challenge length.')
3765 output.write(struct.pack('<I', 1)) # Format Version
3766 output.write(intermediate_key_certificate)
3767 output.write(unlock_key_certificate)
3768 if challenge_path and unlock_key_path:
3769 signature = bytearray()
3770 padding_and_hash = bytearray()
3771 algorithm_name = 'SHA512_RSA4096'
3772 alg = ALGORITHMS[algorithm_name]
3773 hasher = hashlib.sha512()
3774 padding_and_hash.extend(alg.padding)
3775 hasher.update(challenge)
3776 padding_and_hash.extend(hasher.digest())
3777 signature.extend(raw_sign(signing_helper, signing_helper_with_files,
3778 algorithm_name,
3779 alg.signature_num_bytes, unlock_key_path,
3780 padding_and_hash))
3781 output.write(signature)
3782
David Zeuthen21e95262016-07-27 17:58:40 -04003783
3784def calc_hash_level_offsets(image_size, block_size, digest_size):
3785 """Calculate the offsets of all the hash-levels in a Merkle-tree.
3786
3787 Arguments:
3788 image_size: The size of the image to calculate a Merkle-tree for.
3789 block_size: The block size, e.g. 4096.
3790 digest_size: The size of each hash, e.g. 32 for SHA-256.
3791
3792 Returns:
3793 A tuple where the first argument is an array of offsets and the
3794 second is size of the tree, in bytes.
3795 """
3796 level_offsets = []
3797 level_sizes = []
3798 tree_size = 0
3799
3800 num_levels = 0
3801 size = image_size
3802 while size > block_size:
Jan Monsch23e0c622019-12-11 11:23:58 +01003803 num_blocks = (size + block_size - 1) // block_size
David Zeuthen21e95262016-07-27 17:58:40 -04003804 level_size = round_to_multiple(num_blocks * digest_size, block_size)
3805
3806 level_sizes.append(level_size)
3807 tree_size += level_size
3808 num_levels += 1
3809
3810 size = level_size
3811
3812 for n in range(0, num_levels):
3813 offset = 0
3814 for m in range(n + 1, num_levels):
3815 offset += level_sizes[m]
3816 level_offsets.append(offset)
3817
David Zeuthena4fee8b2016-08-22 15:20:43 -04003818 return level_offsets, tree_size
David Zeuthen21e95262016-07-27 17:58:40 -04003819
3820
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003821# See system/extras/libfec/include/fec/io.h for these definitions.
3822FEC_FOOTER_FORMAT = '<LLLLLQ32s'
3823FEC_MAGIC = 0xfecfecfe
3824
3825
3826def calc_fec_data_size(image_size, num_roots):
3827 """Calculates how much space FEC data will take.
3828
Jan Monschfe00c0a2019-12-11 11:19:40 +01003829 Arguments:
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003830 image_size: The size of the image.
3831 num_roots: Number of roots.
3832
3833 Returns:
3834 The number of bytes needed for FEC for an image of the given size
3835 and with the requested number of FEC roots.
3836
3837 Raises:
3838 ValueError: If output from the 'fec' tool is invalid.
3839
3840 """
3841 p = subprocess.Popen(
3842 ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)],
3843 stdout=subprocess.PIPE,
3844 stderr=subprocess.PIPE)
3845 (pout, perr) = p.communicate()
3846 retcode = p.wait()
3847 if retcode != 0:
3848 raise ValueError('Error invoking fec: {}'.format(perr))
3849 return int(pout)
3850
3851
3852def generate_fec_data(image_filename, num_roots):
3853 """Generate FEC codes for an image.
3854
Jan Monschfe00c0a2019-12-11 11:19:40 +01003855 Arguments:
David Zeuthen0b7f1d32016-10-25 17:53:49 -04003856 image_filename: The filename of the image.
3857 num_roots: Number of roots.
3858
3859 Returns:
3860 The FEC data blob.
3861
3862 Raises:
3863 ValueError: If output from the 'fec' tool is invalid.
3864 """
3865 fec_tmpfile = tempfile.NamedTemporaryFile()
3866 subprocess.check_call(
3867 ['fec', '--encode', '--roots', str(num_roots), image_filename,
3868 fec_tmpfile.name],
3869 stderr=open(os.devnull))
3870 fec_data = fec_tmpfile.read()
3871 footer_size = struct.calcsize(FEC_FOOTER_FORMAT)
3872 footer_data = fec_data[-footer_size:]
3873 (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT,
3874 footer_data)
3875 if magic != FEC_MAGIC:
3876 raise ValueError('Unexpected magic in FEC footer')
3877 return fec_data[0:fec_size]
3878
3879
David Zeuthen21e95262016-07-27 17:58:40 -04003880def generate_hash_tree(image, image_size, block_size, hash_alg_name, salt,
David Zeuthena4fee8b2016-08-22 15:20:43 -04003881 digest_padding, hash_level_offsets, tree_size):
David Zeuthen21e95262016-07-27 17:58:40 -04003882 """Generates a Merkle-tree for a file.
3883
Jan Monschfe00c0a2019-12-11 11:19:40 +01003884 Arguments:
David Zeuthen21e95262016-07-27 17:58:40 -04003885 image: The image, as a file.
3886 image_size: The size of the image.
3887 block_size: The block size, e.g. 4096.
3888 hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'.
3889 salt: The salt to use.
3890 digest_padding: The padding for each digest.
David Zeuthen21e95262016-07-27 17:58:40 -04003891 hash_level_offsets: The offsets from calc_hash_level_offsets().
David Zeuthena4fee8b2016-08-22 15:20:43 -04003892 tree_size: The size of the tree, in number of bytes.
David Zeuthen21e95262016-07-27 17:58:40 -04003893
3894 Returns:
David Zeuthena4fee8b2016-08-22 15:20:43 -04003895 A tuple where the first element is the top-level hash and the
3896 second element is the hash-tree.
David Zeuthen21e95262016-07-27 17:58:40 -04003897 """
David Zeuthena4fee8b2016-08-22 15:20:43 -04003898 hash_ret = bytearray(tree_size)
David Zeuthen21e95262016-07-27 17:58:40 -04003899 hash_src_offset = 0
3900 hash_src_size = image_size
3901 level_num = 0
3902 while hash_src_size > block_size:
Colin Cross388338a2020-02-28 14:18:01 -08003903 level_output_list = []
David Zeuthen21e95262016-07-27 17:58:40 -04003904 remaining = hash_src_size
3905 while remaining > 0:
3906 hasher = hashlib.new(name=hash_alg_name, string=salt)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003907 # Only read from the file for the first level - for subsequent
3908 # levels, access the array we're building.
3909 if level_num == 0:
3910 image.seek(hash_src_offset + hash_src_size - remaining)
3911 data = image.read(min(remaining, block_size))
3912 else:
3913 offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining
3914 data = hash_ret[offset:offset + block_size]
David Zeuthen21e95262016-07-27 17:58:40 -04003915 hasher.update(data)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003916
3917 remaining -= len(data)
David Zeuthen21e95262016-07-27 17:58:40 -04003918 if len(data) < block_size:
3919 hasher.update('\0' * (block_size - len(data)))
Colin Cross388338a2020-02-28 14:18:01 -08003920 level_output_list.append(hasher.digest())
David Zeuthen21e95262016-07-27 17:58:40 -04003921 if digest_padding > 0:
Colin Cross388338a2020-02-28 14:18:01 -08003922 level_output_list.append('\0' * digest_padding)
3923
3924 level_output = ''.join(level_output_list)
David Zeuthen21e95262016-07-27 17:58:40 -04003925
3926 padding_needed = (round_to_multiple(
3927 len(level_output), block_size) - len(level_output))
3928 level_output += '\0' * padding_needed
3929
David Zeuthena4fee8b2016-08-22 15:20:43 -04003930 # Copy level-output into resulting tree.
3931 offset = hash_level_offsets[level_num]
3932 hash_ret[offset:offset + len(level_output)] = level_output
David Zeuthen21e95262016-07-27 17:58:40 -04003933
David Zeuthena4fee8b2016-08-22 15:20:43 -04003934 # Continue on to the next level.
David Zeuthen21e95262016-07-27 17:58:40 -04003935 hash_src_size = len(level_output)
David Zeuthen21e95262016-07-27 17:58:40 -04003936 level_num += 1
3937
3938 hasher = hashlib.new(name=hash_alg_name, string=salt)
3939 hasher.update(level_output)
David Zeuthena4fee8b2016-08-22 15:20:43 -04003940 return hasher.digest(), hash_ret
David Zeuthen21e95262016-07-27 17:58:40 -04003941
3942
3943class AvbTool(object):
3944 """Object for avbtool command-line tool."""
3945
3946 def __init__(self):
3947 """Initializer method."""
3948 self.avb = Avb()
3949
3950 def _add_common_args(self, sub_parser):
3951 """Adds arguments used by several sub-commands.
3952
3953 Arguments:
3954 sub_parser: The parser to add arguments to.
3955 """
3956 sub_parser.add_argument('--algorithm',
3957 help='Algorithm to use (default: NONE)',
3958 metavar='ALGORITHM',
3959 default='NONE')
3960 sub_parser.add_argument('--key',
3961 help='Path to RSA private key file',
3962 metavar='KEY',
3963 required=False)
Dzmitry Yatsushkevich4e552792016-12-15 10:27:48 -08003964 sub_parser.add_argument('--signing_helper',
3965 help='Path to helper used for signing',
3966 metavar='APP',
3967 default=None,
3968 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04003969 sub_parser.add_argument('--signing_helper_with_files',
3970 help='Path to helper used for signing using files',
3971 metavar='APP',
3972 default=None,
3973 required=False)
David Zeuthen18666ab2016-11-15 11:18:05 -05003974 sub_parser.add_argument('--public_key_metadata',
3975 help='Path to public key metadata file',
3976 metavar='KEY_METADATA',
3977 required=False)
David Zeuthen21e95262016-07-27 17:58:40 -04003978 sub_parser.add_argument('--rollback_index',
3979 help='Rollback Index',
3980 type=parse_number,
3981 default=0)
David Zeuthene3cadca2017-02-22 21:25:46 -05003982 # This is used internally for unit tests. Do not include in --help output.
3983 sub_parser.add_argument('--internal_release_string',
3984 help=argparse.SUPPRESS)
3985 sub_parser.add_argument('--append_to_release_string',
3986 help='Text to append to release string',
3987 metavar='STR')
David Zeuthen21e95262016-07-27 17:58:40 -04003988 sub_parser.add_argument('--prop',
3989 help='Add property',
3990 metavar='KEY:VALUE',
3991 action='append')
3992 sub_parser.add_argument('--prop_from_file',
3993 help='Add property from file',
3994 metavar='KEY:PATH',
3995 action='append')
3996 sub_parser.add_argument('--kernel_cmdline',
3997 help='Add kernel cmdline',
3998 metavar='CMDLINE',
3999 action='append')
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004000 # TODO(zeuthen): the --setup_rootfs_from_kernel option used to be called
4001 # --generate_dm_verity_cmdline_from_hashtree. Remove support for the latter
4002 # at some future point.
4003 sub_parser.add_argument('--setup_rootfs_from_kernel',
4004 '--generate_dm_verity_cmdline_from_hashtree',
David Zeuthen21e95262016-07-27 17:58:40 -04004005 metavar='IMAGE',
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004006 help='Adds kernel cmdline to set up IMAGE',
David Zeuthen21e95262016-07-27 17:58:40 -04004007 type=argparse.FileType('rb'))
4008 sub_parser.add_argument('--include_descriptors_from_image',
4009 help='Include descriptors from image',
4010 metavar='IMAGE',
4011 action='append',
4012 type=argparse.FileType('rb'))
David Zeuthen1097a782017-05-31 15:53:17 -04004013 sub_parser.add_argument('--print_required_libavb_version',
4014 help=('Don\'t store the footer - '
4015 'instead calculate the required libavb '
4016 'version for the given options.'),
4017 action='store_true')
David Zeuthena5fd3a42017-02-27 16:38:54 -05004018 # These are only allowed from top-level vbmeta and boot-in-lieu-of-vbmeta.
4019 sub_parser.add_argument('--chain_partition',
4020 help='Allow signed integrity-data for partition',
4021 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
4022 action='append')
4023 sub_parser.add_argument('--flags',
4024 help='VBMeta flags',
4025 type=parse_number,
4026 default=0)
4027 sub_parser.add_argument('--set_hashtree_disabled_flag',
4028 help='Set the HASHTREE_DISABLED flag',
4029 action='store_true')
4030
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004031 def _add_common_footer_args(self, sub_parser):
4032 """Adds arguments used by add_*_footer sub-commands.
4033
4034 Arguments:
4035 sub_parser: The parser to add arguments to.
4036 """
4037 sub_parser.add_argument('--use_persistent_digest',
4038 help='Use a persistent digest on device instead of '
4039 'storing the digest in the descriptor. This '
4040 'cannot be used with A/B so must be combined '
4041 'with --do_not_use_ab when an A/B suffix is '
4042 'expected at runtime.',
4043 action='store_true')
4044 sub_parser.add_argument('--do_not_use_ab',
4045 help='The partition does not use A/B even when an '
4046 'A/B suffix is present. This must not be used '
4047 'for vbmeta or chained partitions.',
4048 action='store_true')
4049
David Zeuthena5fd3a42017-02-27 16:38:54 -05004050 def _fixup_common_args(self, args):
4051 """Common fixups needed by subcommands.
4052
4053 Arguments:
4054 args: Arguments to modify.
4055
4056 Returns:
4057 The modified arguments.
4058 """
4059 if args.set_hashtree_disabled_flag:
4060 args.flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED
4061 return args
David Zeuthen21e95262016-07-27 17:58:40 -04004062
4063 def run(self, argv):
4064 """Command-line processor.
4065
4066 Arguments:
4067 argv: Pass sys.argv from main.
4068 """
4069 parser = argparse.ArgumentParser()
4070 subparsers = parser.add_subparsers(title='subcommands')
4071
4072 sub_parser = subparsers.add_parser('version',
4073 help='Prints version of avbtool.')
4074 sub_parser.set_defaults(func=self.version)
4075
4076 sub_parser = subparsers.add_parser('extract_public_key',
4077 help='Extract public key.')
4078 sub_parser.add_argument('--key',
4079 help='Path to RSA private key file',
4080 required=True)
4081 sub_parser.add_argument('--output',
4082 help='Output file name',
4083 type=argparse.FileType('wb'),
4084 required=True)
4085 sub_parser.set_defaults(func=self.extract_public_key)
4086
4087 sub_parser = subparsers.add_parser('make_vbmeta_image',
4088 help='Makes a vbmeta image.')
4089 sub_parser.add_argument('--output',
4090 help='Output file name',
David Zeuthen1097a782017-05-31 15:53:17 -04004091 type=argparse.FileType('wb'))
David Zeuthen97cb5802017-06-01 16:14:05 -04004092 sub_parser.add_argument('--padding_size',
4093 metavar='NUMBER',
4094 help='If non-zero, pads output with NUL bytes so '
Jan Monscheeb28b62019-12-05 16:17:09 +01004095 'its size is a multiple of NUMBER '
4096 '(default: 0)',
David Zeuthen97cb5802017-06-01 16:14:05 -04004097 type=parse_number,
4098 default=0)
David Zeuthen21e95262016-07-27 17:58:40 -04004099 self._add_common_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004100 sub_parser.set_defaults(func=self.make_vbmeta_image)
4101
4102 sub_parser = subparsers.add_parser('add_hash_footer',
4103 help='Add hashes and footer to image.')
4104 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004105 help='Image to add hashes to',
David Zeuthen21e95262016-07-27 17:58:40 -04004106 type=argparse.FileType('rab+'))
4107 sub_parser.add_argument('--partition_size',
4108 help='Partition size',
David Zeuthen1097a782017-05-31 15:53:17 -04004109 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04004110 sub_parser.add_argument('--partition_name',
4111 help='Partition name',
David Zeuthenbf562452017-05-17 18:04:43 -04004112 default=None)
David Zeuthen21e95262016-07-27 17:58:40 -04004113 sub_parser.add_argument('--hash_algorithm',
4114 help='Hash algorithm to use (default: sha256)',
4115 default='sha256')
4116 sub_parser.add_argument('--salt',
4117 help='Salt in hex (default: /dev/urandom)')
David Zeuthenbf562452017-05-17 18:04:43 -04004118 sub_parser.add_argument('--calc_max_image_size',
4119 help=('Don\'t store the footer - '
4120 'instead calculate the maximum image size '
4121 'leaving enough room for metadata with '
4122 'the given partition size.'),
4123 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05004124 sub_parser.add_argument('--output_vbmeta_image',
4125 help='Also write vbmeta struct to file',
4126 type=argparse.FileType('wb'))
4127 sub_parser.add_argument('--do_not_append_vbmeta_image',
4128 help=('Do not append vbmeta struct or footer '
4129 'to the image'),
4130 action='store_true')
David Zeuthen21e95262016-07-27 17:58:40 -04004131 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004132 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004133 sub_parser.set_defaults(func=self.add_hash_footer)
4134
David Zeuthenb1b994d2017-03-06 18:01:31 -05004135 sub_parser = subparsers.add_parser('append_vbmeta_image',
4136 help='Append vbmeta image to image.')
4137 sub_parser.add_argument('--image',
4138 help='Image to append vbmeta blob to',
4139 type=argparse.FileType('rab+'))
4140 sub_parser.add_argument('--partition_size',
4141 help='Partition size',
4142 type=parse_number,
4143 required=True)
4144 sub_parser.add_argument('--vbmeta_image',
4145 help='Image with vbmeta blob to append',
4146 type=argparse.FileType('rb'))
4147 sub_parser.set_defaults(func=self.append_vbmeta_image)
4148
Jan Monscheeb28b62019-12-05 16:17:09 +01004149 sub_parser = subparsers.add_parser(
4150 'add_hashtree_footer',
4151 help='Add hashtree and footer to image.')
David Zeuthen21e95262016-07-27 17:58:40 -04004152 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004153 help='Image to add hashtree to',
David Zeuthen21e95262016-07-27 17:58:40 -04004154 type=argparse.FileType('rab+'))
4155 sub_parser.add_argument('--partition_size',
4156 help='Partition size',
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004157 default=0,
David Zeuthen1097a782017-05-31 15:53:17 -04004158 type=parse_number)
David Zeuthen21e95262016-07-27 17:58:40 -04004159 sub_parser.add_argument('--partition_name',
4160 help='Partition name',
David Zeuthenf4f51eb2018-09-20 14:56:46 -04004161 default='')
David Zeuthen21e95262016-07-27 17:58:40 -04004162 sub_parser.add_argument('--hash_algorithm',
4163 help='Hash algorithm to use (default: sha1)',
4164 default='sha1')
4165 sub_parser.add_argument('--salt',
4166 help='Salt in hex (default: /dev/urandom)')
4167 sub_parser.add_argument('--block_size',
4168 help='Block size (default: 4096)',
4169 type=parse_number,
4170 default=4096)
David Zeuthenbce9a292017-05-10 17:18:04 -04004171 # TODO(zeuthen): The --generate_fec option was removed when we
4172 # moved to generating FEC by default. To avoid breaking existing
4173 # users needing to transition we simply just print a warning below
4174 # in add_hashtree_footer(). Remove this option and the warning at
4175 # some point in the future.
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004176 sub_parser.add_argument('--generate_fec',
David Zeuthenbce9a292017-05-10 17:18:04 -04004177 help=argparse.SUPPRESS,
4178 action='store_true')
Jan Monscheeb28b62019-12-05 16:17:09 +01004179 sub_parser.add_argument(
4180 '--do_not_generate_fec',
4181 help='Do not generate forward-error-correction codes',
4182 action='store_true')
David Zeuthen0b7f1d32016-10-25 17:53:49 -04004183 sub_parser.add_argument('--fec_num_roots',
4184 help='Number of roots for FEC (default: 2)',
4185 type=parse_number,
4186 default=2)
David Zeuthen09692692016-09-30 16:16:40 -04004187 sub_parser.add_argument('--calc_max_image_size',
4188 help=('Don\'t store the hashtree or footer - '
4189 'instead calculate the maximum image size '
4190 'leaving enough room for hashtree '
4191 'and metadata with the given partition '
4192 'size.'),
4193 action='store_true')
David Zeuthend247fcb2017-02-16 12:09:27 -05004194 sub_parser.add_argument('--output_vbmeta_image',
4195 help='Also write vbmeta struct to file',
4196 type=argparse.FileType('wb'))
4197 sub_parser.add_argument('--do_not_append_vbmeta_image',
4198 help=('Do not append vbmeta struct or footer '
4199 'to the image'),
4200 action='store_true')
David Zeuthen73f2afa2017-05-17 16:54:11 -04004201 # This is different from --setup_rootfs_from_kernel insofar that
4202 # it doesn't take an IMAGE, the generated cmdline will be for the
4203 # hashtree we're adding.
4204 sub_parser.add_argument('--setup_as_rootfs_from_kernel',
4205 action='store_true',
4206 help='Adds kernel cmdline for setting up rootfs')
Jooyung Hand7221942019-06-17 13:19:57 +09004207 sub_parser.add_argument('--no_hashtree',
4208 action='store_true',
4209 help='Do not append hashtree')
David Zeuthen21e95262016-07-27 17:58:40 -04004210 self._add_common_args(sub_parser)
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004211 self._add_common_footer_args(sub_parser)
David Zeuthen21e95262016-07-27 17:58:40 -04004212 sub_parser.set_defaults(func=self.add_hashtree_footer)
4213
4214 sub_parser = subparsers.add_parser('erase_footer',
4215 help='Erase footer from an image.')
4216 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004217 help='Image with a footer',
David Zeuthen21e95262016-07-27 17:58:40 -04004218 type=argparse.FileType('rwb+'),
4219 required=True)
4220 sub_parser.add_argument('--keep_hashtree',
David Zeuthenfbb61fa2017-02-02 12:11:49 -05004221 help='Keep the hashtree and FEC in the image',
David Zeuthen21e95262016-07-27 17:58:40 -04004222 action='store_true')
4223 sub_parser.set_defaults(func=self.erase_footer)
4224
David Zeuthen1394f762019-04-30 10:20:11 -04004225 sub_parser = subparsers.add_parser('zero_hashtree',
4226 help='Zero out hashtree and FEC data.')
4227 sub_parser.add_argument('--image',
4228 help='Image with a footer',
4229 type=argparse.FileType('rwb+'),
4230 required=True)
4231 sub_parser.set_defaults(func=self.zero_hashtree)
4232
Jan Monscheeb28b62019-12-05 16:17:09 +01004233 sub_parser = subparsers.add_parser(
4234 'extract_vbmeta_image',
4235 help='Extracts vbmeta from an image with a footer.')
David Zeuthen49936b42018-08-07 17:38:58 -04004236 sub_parser.add_argument('--image',
4237 help='Image with footer',
4238 type=argparse.FileType('rb'),
4239 required=True)
4240 sub_parser.add_argument('--output',
4241 help='Output file name',
4242 type=argparse.FileType('wb'))
4243 sub_parser.add_argument('--padding_size',
4244 metavar='NUMBER',
4245 help='If non-zero, pads output with NUL bytes so '
Jan Monscheeb28b62019-12-05 16:17:09 +01004246 'its size is a multiple of NUMBER '
4247 '(default: 0)',
David Zeuthen49936b42018-08-07 17:38:58 -04004248 type=parse_number,
4249 default=0)
4250 sub_parser.set_defaults(func=self.extract_vbmeta_image)
4251
David Zeuthen2bc232b2017-04-19 14:25:19 -04004252 sub_parser = subparsers.add_parser('resize_image',
4253 help='Resize image with a footer.')
4254 sub_parser.add_argument('--image',
4255 help='Image with a footer',
4256 type=argparse.FileType('rwb+'),
4257 required=True)
4258 sub_parser.add_argument('--partition_size',
4259 help='New partition size',
4260 type=parse_number)
4261 sub_parser.set_defaults(func=self.resize_image)
4262
David Zeuthen21e95262016-07-27 17:58:40 -04004263 sub_parser = subparsers.add_parser(
4264 'info_image',
4265 help='Show information about vbmeta or footer.')
4266 sub_parser.add_argument('--image',
David Zeuthen8b6973b2016-09-20 12:39:49 -04004267 help='Image to show information about',
David Zeuthen21e95262016-07-27 17:58:40 -04004268 type=argparse.FileType('rb'),
4269 required=True)
4270 sub_parser.add_argument('--output',
4271 help='Write info to file',
4272 type=argparse.FileType('wt'),
4273 default=sys.stdout)
4274 sub_parser.set_defaults(func=self.info_image)
4275
David Zeuthenb623d8b2017-04-04 16:05:53 -04004276 sub_parser = subparsers.add_parser(
4277 'verify_image',
4278 help='Verify an image.')
4279 sub_parser.add_argument('--image',
4280 help='Image to verify',
4281 type=argparse.FileType('rb'),
4282 required=True)
David Zeuthen5dfb4e92017-05-24 14:49:32 -04004283 sub_parser.add_argument('--key',
4284 help='Check embedded public key matches KEY',
4285 metavar='KEY',
4286 required=False)
4287 sub_parser.add_argument('--expected_chain_partition',
4288 help='Expected chain partition',
4289 metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
4290 action='append')
Jan Monscheeb28b62019-12-05 16:17:09 +01004291 sub_parser.add_argument(
4292 '--follow_chain_partitions',
4293 help=('Follows chain partitions even when not '
4294 'specified with the --expected_chain_partition option'),
4295 action='store_true')
4296 sub_parser.add_argument(
4297 '--accept_zeroed_hashtree',
4298 help=('Accept images where the hashtree or FEC data is zeroed out'),
4299 action='store_true')
David Zeuthenb623d8b2017-04-04 16:05:53 -04004300 sub_parser.set_defaults(func=self.verify_image)
4301
David Zeuthenb8643c02018-05-17 17:21:18 -04004302 sub_parser = subparsers.add_parser(
4303 'calculate_vbmeta_digest',
4304 help='Calculate vbmeta digest.')
4305 sub_parser.add_argument('--image',
4306 help='Image to calculate digest for',
4307 type=argparse.FileType('rb'),
4308 required=True)
4309 sub_parser.add_argument('--hash_algorithm',
4310 help='Hash algorithm to use (default: sha256)',
4311 default='sha256')
4312 sub_parser.add_argument('--output',
4313 help='Write hex digest to file (default: stdout)',
4314 type=argparse.FileType('wt'),
4315 default=sys.stdout)
4316 sub_parser.set_defaults(func=self.calculate_vbmeta_digest)
4317
David Zeuthenf7d2e752018-09-20 13:30:41 -04004318 sub_parser = subparsers.add_parser(
4319 'calculate_kernel_cmdline',
4320 help='Calculate kernel cmdline.')
4321 sub_parser.add_argument('--image',
4322 help='Image to calculate kernel cmdline for',
4323 type=argparse.FileType('rb'),
4324 required=True)
4325 sub_parser.add_argument('--hashtree_disabled',
4326 help='Return the cmdline for hashtree disabled',
4327 action='store_true')
4328 sub_parser.add_argument('--output',
4329 help='Write cmdline to file (default: stdout)',
4330 type=argparse.FileType('wt'),
4331 default=sys.stdout)
4332 sub_parser.set_defaults(func=self.calculate_kernel_cmdline)
4333
David Zeuthen8b6973b2016-09-20 12:39:49 -04004334 sub_parser = subparsers.add_parser('set_ab_metadata',
4335 help='Set A/B metadata.')
4336 sub_parser.add_argument('--misc_image',
4337 help=('The misc image to modify. If the image does '
4338 'not exist, it will be created.'),
4339 type=argparse.FileType('r+b'),
4340 required=True)
4341 sub_parser.add_argument('--slot_data',
4342 help=('Slot data of the form "priority", '
4343 '"tries_remaining", "sucessful_boot" for '
4344 'slot A followed by the same for slot B, '
4345 'separated by colons. The default value '
4346 'is 15:7:0:14:7:0.'),
4347 default='15:7:0:14:7:0')
4348 sub_parser.set_defaults(func=self.set_ab_metadata)
4349
Darren Krahn147b08d2016-12-20 16:38:29 -08004350 sub_parser = subparsers.add_parser(
4351 'make_atx_certificate',
4352 help='Create an Android Things eXtension (ATX) certificate.')
4353 sub_parser.add_argument('--output',
4354 help='Write certificate to file',
4355 type=argparse.FileType('wb'),
4356 default=sys.stdout)
4357 sub_parser.add_argument('--subject',
4358 help=('Path to subject file'),
4359 type=argparse.FileType('rb'),
4360 required=True)
4361 sub_parser.add_argument('--subject_key',
4362 help=('Path to subject RSA public key file'),
4363 type=argparse.FileType('rb'),
4364 required=True)
4365 sub_parser.add_argument('--subject_key_version',
4366 help=('Version of the subject key'),
4367 type=parse_number,
4368 required=False)
4369 sub_parser.add_argument('--subject_is_intermediate_authority',
4370 help=('Generate an intermediate authority '
4371 'certificate'),
4372 action='store_true')
Darren Krahnfccd64e2018-01-16 17:39:35 -08004373 sub_parser.add_argument('--usage',
Darren Krahn2367b462018-06-19 00:53:32 -07004374 help=('Override usage with a hash of the provided '
Darren Krahnfccd64e2018-01-16 17:39:35 -08004375 'string'),
4376 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08004377 sub_parser.add_argument('--authority_key',
4378 help='Path to authority RSA private key file',
4379 required=False)
4380 sub_parser.add_argument('--signing_helper',
4381 help='Path to helper used for signing',
4382 metavar='APP',
4383 default=None,
4384 required=False)
David Zeuthena156d3d2017-06-01 12:08:09 -04004385 sub_parser.add_argument('--signing_helper_with_files',
4386 help='Path to helper used for signing using files',
4387 metavar='APP',
4388 default=None,
4389 required=False)
Darren Krahn147b08d2016-12-20 16:38:29 -08004390 sub_parser.set_defaults(func=self.make_atx_certificate)
4391
4392 sub_parser = subparsers.add_parser(
4393 'make_atx_permanent_attributes',
4394 help='Create Android Things eXtension (ATX) permanent attributes.')
4395 sub_parser.add_argument('--output',
4396 help='Write attributes to file',
4397 type=argparse.FileType('wb'),
4398 default=sys.stdout)
4399 sub_parser.add_argument('--root_authority_key',
4400 help='Path to authority RSA public key file',
4401 type=argparse.FileType('rb'),
4402 required=True)
4403 sub_parser.add_argument('--product_id',
4404 help=('Path to Product ID file'),
4405 type=argparse.FileType('rb'),
4406 required=True)
4407 sub_parser.set_defaults(func=self.make_atx_permanent_attributes)
4408
4409 sub_parser = subparsers.add_parser(
4410 'make_atx_metadata',
4411 help='Create Android Things eXtension (ATX) metadata.')
4412 sub_parser.add_argument('--output',
4413 help='Write metadata to file',
4414 type=argparse.FileType('wb'),
4415 default=sys.stdout)
4416 sub_parser.add_argument('--intermediate_key_certificate',
4417 help='Path to intermediate key certificate file',
4418 type=argparse.FileType('rb'),
4419 required=True)
4420 sub_parser.add_argument('--product_key_certificate',
4421 help='Path to product key certificate file',
4422 type=argparse.FileType('rb'),
4423 required=True)
Darren Krahn147b08d2016-12-20 16:38:29 -08004424 sub_parser.set_defaults(func=self.make_atx_metadata)
4425
Darren Krahnfccd64e2018-01-16 17:39:35 -08004426 sub_parser = subparsers.add_parser(
4427 'make_atx_unlock_credential',
4428 help='Create an Android Things eXtension (ATX) unlock credential.')
4429 sub_parser.add_argument('--output',
4430 help='Write credential to file',
4431 type=argparse.FileType('wb'),
4432 default=sys.stdout)
4433 sub_parser.add_argument('--intermediate_key_certificate',
4434 help='Path to intermediate key certificate file',
4435 type=argparse.FileType('rb'),
4436 required=True)
4437 sub_parser.add_argument('--unlock_key_certificate',
4438 help='Path to unlock key certificate file',
4439 type=argparse.FileType('rb'),
4440 required=True)
4441 sub_parser.add_argument('--challenge',
4442 help='Path to the challenge to sign (optional). If '
4443 'this is not provided the challenge signature '
4444 'field is omitted and can be concatenated '
4445 'later.',
4446 required=False)
4447 sub_parser.add_argument('--unlock_key',
4448 help='Path to unlock key (optional). Must be '
4449 'provided if using --challenge.',
4450 required=False)
4451 sub_parser.add_argument('--signing_helper',
4452 help='Path to helper used for signing',
4453 metavar='APP',
4454 default=None,
4455 required=False)
4456 sub_parser.add_argument('--signing_helper_with_files',
4457 help='Path to helper used for signing using files',
4458 metavar='APP',
4459 default=None,
4460 required=False)
4461 sub_parser.set_defaults(func=self.make_atx_unlock_credential)
4462
David Zeuthen21e95262016-07-27 17:58:40 -04004463 args = parser.parse_args(argv[1:])
4464 try:
4465 args.func(args)
4466 except AvbError as e:
Jan Monsch23e0c622019-12-11 11:23:58 +01004467 sys.stderr.write('{}: {}\n'.format(argv[0], str(e)))
David Zeuthen21e95262016-07-27 17:58:40 -04004468 sys.exit(1)
4469
4470 def version(self, _):
4471 """Implements the 'version' sub-command."""
Jan Monsch23e0c622019-12-11 11:23:58 +01004472 print(get_release_string())
David Zeuthen21e95262016-07-27 17:58:40 -04004473
4474 def extract_public_key(self, args):
4475 """Implements the 'extract_public_key' sub-command."""
4476 self.avb.extract_public_key(args.key, args.output)
4477
4478 def make_vbmeta_image(self, args):
4479 """Implements the 'make_vbmeta_image' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004480 args = self._fixup_common_args(args)
David Zeuthen21e95262016-07-27 17:58:40 -04004481 self.avb.make_vbmeta_image(args.output, args.chain_partition,
David Zeuthen18666ab2016-11-15 11:18:05 -05004482 args.algorithm, args.key,
4483 args.public_key_metadata, args.rollback_index,
David Zeuthenfd41eb92016-11-17 12:24:47 -05004484 args.flags, args.prop, args.prop_from_file,
David Zeuthen21e95262016-07-27 17:58:40 -04004485 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004486 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05004487 args.include_descriptors_from_image,
David Zeuthene3cadca2017-02-22 21:25:46 -05004488 args.signing_helper,
David Zeuthena156d3d2017-06-01 12:08:09 -04004489 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004490 args.internal_release_string,
David Zeuthen1097a782017-05-31 15:53:17 -04004491 args.append_to_release_string,
David Zeuthen97cb5802017-06-01 16:14:05 -04004492 args.print_required_libavb_version,
4493 args.padding_size)
David Zeuthen21e95262016-07-27 17:58:40 -04004494
David Zeuthenb1b994d2017-03-06 18:01:31 -05004495 def append_vbmeta_image(self, args):
4496 """Implements the 'append_vbmeta_image' sub-command."""
4497 self.avb.append_vbmeta_image(args.image.name, args.vbmeta_image.name,
4498 args.partition_size)
4499
David Zeuthen21e95262016-07-27 17:58:40 -04004500 def add_hash_footer(self, args):
4501 """Implements the 'add_hash_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004502 args = self._fixup_common_args(args)
David Zeuthenbf562452017-05-17 18:04:43 -04004503 self.avb.add_hash_footer(args.image.name if args.image else None,
4504 args.partition_size,
David Zeuthen21e95262016-07-27 17:58:40 -04004505 args.partition_name, args.hash_algorithm,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004506 args.salt, args.chain_partition, args.algorithm,
4507 args.key,
David Zeuthen18666ab2016-11-15 11:18:05 -05004508 args.public_key_metadata, args.rollback_index,
David Zeuthena5fd3a42017-02-27 16:38:54 -05004509 args.flags, args.prop, args.prop_from_file,
David Zeuthen18666ab2016-11-15 11:18:05 -05004510 args.kernel_cmdline,
David Zeuthen5d4f4f22017-01-11 11:37:34 -05004511 args.setup_rootfs_from_kernel,
David Zeuthend247fcb2017-02-16 12:09:27 -05004512 args.include_descriptors_from_image,
David Zeuthena156d3d2017-06-01 12:08:09 -04004513 args.calc_max_image_size,
4514 args.signing_helper,
4515 args.signing_helper_with_files,
David Zeuthene3cadca2017-02-22 21:25:46 -05004516 args.internal_release_string,
4517 args.append_to_release_string,
David Zeuthend247fcb2017-02-16 12:09:27 -05004518 args.output_vbmeta_image,
David Zeuthen1097a782017-05-31 15:53:17 -04004519 args.do_not_append_vbmeta_image,
Darren Krahnfd0ba0d2018-02-01 18:06:34 -08004520 args.print_required_libavb_version,
4521 args.use_persistent_digest,
4522 args.do_not_use_ab)
David Zeuthen21e95262016-07-27 17:58:40 -04004523
4524 def add_hashtree_footer(self, args):
4525 """Implements the 'add_hashtree_footer' sub-command."""
David Zeuthena5fd3a42017-02-27 16:38:54 -05004526 args = self._fixup_common_args(args)
David Zeuthenbce9a292017-05-10 17:18:04 -04004527 # TODO(zeuthen): Remove when removing support for the
4528 # '--generate_fec' option above.
4529 if args.generate_fec:
4530 sys.stderr.write('The --generate_fec option is deprecated since FEC '
4531 'is now generated by default. Use the option '
4532 '--do_not_generate_fec to not generate FEC.\n')
Jan Monscheeb28b62019-12-05 16:17:09 +01004533 self.avb.add_hashtree_footer(
4534 args.image.name if args.image else None,
4535 args.partition_size,
4536 args.partition_name,
4537 not args.do_not_generate_fec, args.fec_num_roots,
4538 args.hash_algorithm, args.block_size,
4539 args.salt, args.chain_partition, args.algorithm,
4540 args.key, args.public_key_metadata,
4541 args.rollback_index, args.flags, args.prop,
4542 args.prop_from_file,
4543 args.kernel_cmdline,
4544 args.setup_rootfs_from_kernel,
4545 args.setup_as_rootfs_from_kernel,
4546 args.include_descriptors_from_image,
4547 args.calc_max_image_size,
4548 args.signing_helper,
4549 args.signing_helper_with_files,
4550 args.internal_release_string,
4551 args.append_to_release_string,
4552 args.output_vbmeta_image,
4553 args.do_not_append_vbmeta_image,
4554 args.print_required_libavb_version,
4555 args.use_persistent_digest,
4556 args.do_not_use_ab,
4557 args.no_hashtree)
David Zeuthend247fcb2017-02-16 12:09:27 -05004558
David Zeuthen21e95262016-07-27 17:58:40 -04004559 def erase_footer(self, args):
4560 """Implements the 'erase_footer' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04004561 self.avb.erase_footer(args.image.name, args.keep_hashtree)
David Zeuthen21e95262016-07-27 17:58:40 -04004562
David Zeuthen1394f762019-04-30 10:20:11 -04004563 def zero_hashtree(self, args):
4564 """Implements the 'zero_hashtree' sub-command."""
4565 self.avb.zero_hashtree(args.image.name)
4566
David Zeuthen49936b42018-08-07 17:38:58 -04004567 def extract_vbmeta_image(self, args):
4568 """Implements the 'extract_vbmeta_image' sub-command."""
4569 self.avb.extract_vbmeta_image(args.output, args.image.name,
4570 args.padding_size)
4571
David Zeuthen2bc232b2017-04-19 14:25:19 -04004572 def resize_image(self, args):
4573 """Implements the 'resize_image' sub-command."""
4574 self.avb.resize_image(args.image.name, args.partition_size)
4575
David Zeuthen8b6973b2016-09-20 12:39:49 -04004576 def set_ab_metadata(self, args):
4577 """Implements the 'set_ab_metadata' sub-command."""
4578 self.avb.set_ab_metadata(args.misc_image, args.slot_data)
4579
David Zeuthen21e95262016-07-27 17:58:40 -04004580 def info_image(self, args):
4581 """Implements the 'info_image' sub-command."""
David Zeuthena4fee8b2016-08-22 15:20:43 -04004582 self.avb.info_image(args.image.name, args.output)
David Zeuthen21e95262016-07-27 17:58:40 -04004583
David Zeuthenb623d8b2017-04-04 16:05:53 -04004584 def verify_image(self, args):
4585 """Implements the 'verify_image' sub-command."""
David Zeuthen5dfb4e92017-05-24 14:49:32 -04004586 self.avb.verify_image(args.image.name, args.key,
David Zeuthene947cb62019-01-25 15:27:08 -05004587 args.expected_chain_partition,
David Zeuthen1394f762019-04-30 10:20:11 -04004588 args.follow_chain_partitions,
4589 args.accept_zeroed_hashtree)
David Zeuthenb623d8b2017-04-04 16:05:53 -04004590
David Zeuthenb8643c02018-05-17 17:21:18 -04004591 def calculate_vbmeta_digest(self, args):
4592 """Implements the 'calculate_vbmeta_digest' sub-command."""
4593 self.avb.calculate_vbmeta_digest(args.image.name, args.hash_algorithm,
4594 args.output)
4595
David Zeuthenf7d2e752018-09-20 13:30:41 -04004596 def calculate_kernel_cmdline(self, args):
4597 """Implements the 'calculate_kernel_cmdline' sub-command."""
Jan Monscheeb28b62019-12-05 16:17:09 +01004598 self.avb.calculate_kernel_cmdline(args.image.name, args.hashtree_disabled,
4599 args.output)
David Zeuthenf7d2e752018-09-20 13:30:41 -04004600
Darren Krahn147b08d2016-12-20 16:38:29 -08004601 def make_atx_certificate(self, args):
4602 """Implements the 'make_atx_certificate' sub-command."""
4603 self.avb.make_atx_certificate(args.output, args.authority_key,
David Zeuthenc68f0822017-03-31 17:22:35 -04004604 args.subject_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08004605 args.subject_key_version,
4606 args.subject.read(),
4607 args.subject_is_intermediate_authority,
Darren Krahnfccd64e2018-01-16 17:39:35 -08004608 args.usage,
David Zeuthena156d3d2017-06-01 12:08:09 -04004609 args.signing_helper,
4610 args.signing_helper_with_files)
Darren Krahn147b08d2016-12-20 16:38:29 -08004611
4612 def make_atx_permanent_attributes(self, args):
4613 """Implements the 'make_atx_permanent_attributes' sub-command."""
4614 self.avb.make_atx_permanent_attributes(args.output,
David Zeuthenc68f0822017-03-31 17:22:35 -04004615 args.root_authority_key.name,
Darren Krahn147b08d2016-12-20 16:38:29 -08004616 args.product_id.read())
4617
4618 def make_atx_metadata(self, args):
4619 """Implements the 'make_atx_metadata' sub-command."""
4620 self.avb.make_atx_metadata(args.output,
4621 args.intermediate_key_certificate.read(),
Darren Krahn43e12d82017-02-24 16:26:31 -08004622 args.product_key_certificate.read())
Darren Krahn147b08d2016-12-20 16:38:29 -08004623
Darren Krahnfccd64e2018-01-16 17:39:35 -08004624 def make_atx_unlock_credential(self, args):
4625 """Implements the 'make_atx_unlock_credential' sub-command."""
4626 self.avb.make_atx_unlock_credential(
4627 args.output,
4628 args.intermediate_key_certificate.read(),
4629 args.unlock_key_certificate.read(),
4630 args.challenge,
4631 args.unlock_key,
4632 args.signing_helper,
4633 args.signing_helper_with_files)
4634
David Zeuthen21e95262016-07-27 17:58:40 -04004635
4636if __name__ == '__main__':
4637 tool = AvbTool()
4638 tool.run(sys.argv)